OPENCODE.NVIM(1)

NAME

opencode.nvimIntegrate the opencode AI assistant with Neovim — streamline editor-aware research, reviews, and requests.

SYNOPSIS

INFO

3.1k stars
106 forks
0 views

DESCRIPTION

Integrate the opencode AI assistant with Neovim — streamline editor-aware research, reviews, and requests.

README

opencode.nvim

Integrate the opencode AI assistant with Neovim — streamline editor-aware research, reviews, and requests.

https://github.com/user-attachments/assets/077daa78-d401-4b8b-98d1-9ba9f94c2330

✨ Features

  • Connect to any opencode, or provide an integrated instance
  • Share editor context (buffer, selection, diagnostics, etc.)
  • Input prompts with completions, highlights, and normal-mode support
  • Select prompts from a library and define your own
  • Execute commands
  • Monitor and respond to events (edits, permissions, etc.) in real-time
  • Interact with opencode via an in-process LSP
  • Vim-y — supports ranges and dot-repeat
  • Simple, sensible defaults to get you started quickly

📦 Setup

lazy.nvim

{
  "nickjvandyke/opencode.nvim",
  version = "*", -- Latest stable release
  dependencies = {
    {
      -- `snacks.nvim` integration is recommended, but optional
      ---@module "snacks" <- Loads `snacks.nvim` types for configuration intellisense
      "folke/snacks.nvim",
      optional = true,
      opts = {
        input = {}, -- Enhances `ask()`
        picker = { -- Enhances `select()`
          actions = {
            opencode_send = function(...) return require("opencode").snacks_picker_send(...) end,
          },
          win = {
            input = {
              keys = {
                ["<a-a>"] = { "opencode_send", mode = { "n", "i" } },
              },
            },
          },
        },
      },
    },
  },
  config = function()
    ---@type opencode.Opts
    vim.g.opencode_opts = {
      -- Your configuration, if any; goto definition on the type or field for details
    }
vim.o.autoread = true -- Required for `opts.events.reload`

-- Recommended/example keymaps
vim.keymap.set({ &quot;n&quot;, &quot;x&quot; }, &quot;&lt;C-a&gt;&quot;, function() require(&quot;opencode&quot;).ask(&quot;@this: &quot;, { submit = true }) end, { desc = &quot;Ask opencode…&quot; })
vim.keymap.set({ &quot;n&quot;, &quot;x&quot; }, &quot;&lt;C-x&gt;&quot;, function() require(&quot;opencode&quot;).select() end,                          { desc = &quot;Execute opencode action…&quot; })
vim.keymap.set({ &quot;n&quot;, &quot;t&quot; }, &quot;&lt;C-.&gt;&quot;, function() require(&quot;opencode&quot;).toggle() end,                          { desc = &quot;Toggle opencode&quot; })

vim.keymap.set({ &quot;n&quot;, &quot;x&quot; }, &quot;go&quot;,  function() return require(&quot;opencode&quot;).operator(&quot;@this &quot;) end,        { desc = &quot;Add range to opencode&quot;, expr = true })
vim.keymap.set(&quot;n&quot;,          &quot;goo&quot;, function() return require(&quot;opencode&quot;).operator(&quot;@this &quot;) .. &quot;_&quot; end, { desc = &quot;Add line to opencode&quot;, expr = true })

vim.keymap.set(&quot;n&quot;, &quot;&lt;S-C-u&gt;&quot;, function() require(&quot;opencode&quot;).command(&quot;session.half.page.up&quot;) end,   { desc = &quot;Scroll opencode up&quot; })
vim.keymap.set(&quot;n&quot;, &quot;&lt;S-C-d&gt;&quot;, function() require(&quot;opencode&quot;).command(&quot;session.half.page.down&quot;) end, { desc = &quot;Scroll opencode down&quot; })

-- You may want these if you use the opinionated `&lt;C-a&gt;` and `&lt;C-x&gt;` keymaps above — otherwise consider `&lt;leader&gt;o…` (and remove terminal mode from the `toggle` keymap)
vim.keymap.set(&quot;n&quot;, &quot;+&quot;, &quot;&lt;C-a&gt;&quot;, { desc = &quot;Increment under cursor&quot;, noremap = true })
vim.keymap.set(&quot;n&quot;, &quot;-&quot;, &quot;&lt;C-x&gt;&quot;, { desc = &quot;Decrement under cursor&quot;, noremap = true })

end, }

nixvim

programs.nixvim = {
  extraPlugins = [
    pkgs.vimPlugins.opencode-nvim
  ];
};

[!TIP] Run :checkhealth opencode after setup.

⚙️ Configuration

opencode.nvim provides a rich and reliable default experience — see all available options and their defaults here.

Contexts

opencode.nvim replaces placeholders in prompts with the corresponding context:

PlaceholderContext
@thisOperator range or visual selection if any, else cursor position
@bufferCurrent buffer
@buffersOpen buffers
@visibleVisible text
@diagnosticsCurrent buffer diagnostics
@quickfixQuickfix list
@diffGit diff
@marksGlobal marks
@grapplegrapple.nvim tags

Prompts

Select or reference prompts to review, explain, and improve your code:

NamePrompt
diagnosticsExplain @diagnostics
diffReview the following git diff for correctness and readability: @diff
documentAdd comments documenting @this
explainExplain @this and its context
fixFix @diagnostics
implementImplement @this
optimizeOptimize @this for performance and readability
reviewReview @this for correctness and readability
testAdd tests for @this

Server

You can manually run opencodes however you like and opencode.nvim will find them!

[!IMPORTANT] You must run opencode with the --port flag to expose its server.

If opencode.nvim can't find an existing opencode, it uses the configured server to start one for you, defaulting to an embedded terminal.

Keymaps

opencode.nvim sets these normal-mode keymaps in the embedded terminal for Neovim-like message navigation:

KeymapCommandDescription
<C-u>session.half.page.upScroll up half page
<C-d>session.half.page.downScroll down half page
ggsession.firstGo to first message
Gsession.lastGo to last message
<Esc>session.interruptInterrupt

Customization

Example using snacks.terminal instead:

local opencode_cmd = 'opencode --port'
---@type snacks.terminal.Opts
local snacks_terminal_opts = {
  win = {
    position = 'right',
    enter = false,
    on_win = function(win)
      -- Set up keymaps and cleanup for an arbitrary terminal
      require('opencode.terminal').setup(win.win)
    end,
  },
}
---@type opencode.Opts
vim.g.opencode_opts = {
  server = {
    start = function()
      require('snacks.terminal').open(opencode_cmd, snacks_terminal_opts)
    end,
    stop = function()
      require('snacks.terminal').get(opencode_cmd, snacks_terminal_opts):close()
    end,
    toggle = function()
      require('snacks.terminal').toggle(opencode_cmd, snacks_terminal_opts)
    end,
  },
}

🚀 Usage

Ask — require("opencode").ask()

Input a prompt for opencode.

  • Press <Up> to browse recent asks.
  • Highlights and completes contexts and opencode subagents.
    • Press <Tab> to trigger built-in completion.
  • End the prompt with \n to append instead of submit.
  • Additionally, when using snacks.input:
    • Press <S-CR> to append instead of submit.
    • Offers completions via in-process LSP.

Select — require("opencode").select()

Select from all opencode.nvim functionality.

  • Prompts
  • Commands
    • Fetches custom commands from opencode
  • Server controls

Highlights and previews items when using snacks.picker.

Prompt — require("opencode").prompt()

Prompt opencode.

  • Resolves named references to configured prompts.
  • Injects configured contexts.
  • opencode will interpret @ references to files or subagents.

Operator — require("opencode").operator()

Wraps prompt as an operator, supporting ranges and dot-repeat.

Command — require("opencode").command()

Command opencode:

CommandDescription
session.listList sessions
session.newStart a new session
session.selectSelect a session
session.shareShare the current session
session.interruptInterrupt the current session
session.compactCompact the current session (reduce context size)
session.page.upScroll messages up by one page
session.page.downScroll messages down by one page
session.half.page.upScroll messages up by half a page
session.half.page.downScroll messages down by half a page
session.firstJump to the first message in the session
session.lastJump to the last message in the session
session.undoUndo the last action in the current session
session.redoRedo the last undone action in the current session
prompt.submitSubmit the TUI input
prompt.clearClear the TUI input
agent.cycleCycle the selected agent

LSP

[!WARNING] This feature is experimental! Try it out with vim.g.opencode_opts.lsp.enabled = true.

opencode.nvim provides an in-process LSP to interact with opencode via the LSP functions you're used to!

LSP Functionopencode.nvim Handler
HoverAsks opencode for a brief explanation of the symbol under the cursor.
Code ActionsAsks opencode to explain or fix diagnostics under the cursor.

👀 Events

opencode.nvim forwards opencode's Server-Sent-Events as an OpencodeEvent autocmd:

-- Handle `opencode` events
vim.api.nvim_create_autocmd("User", {
  pattern = "OpencodeEvent:*", -- Optionally filter event types
  callback = function(args)
    ---@type opencode.cli.client.Event
    local event = args.data.event
    ---@type number
    local port = args.data.port
-- See the available event types and their properties
vim.notify(vim.inspect(event))
-- Do something useful
if event.type == &quot;session.idle&quot; then
  vim.notify(&quot;`opencode` finished responding&quot;)
end

end, })

Edits

When opencode edits a file, opencode.nvim automatically reloads the corresponding buffer.

Permissions

When opencode requests a permission, opencode.nvim waits for idle to ask you to approve or deny it.

Statusline

require("lualine").setup({
  sections = {
    lualine_z = {
      {
        require("opencode").statusline,
      },
    }
  }
})

🙏 Acknowledgments

SEE ALSO

clihub3/4/2026OPENCODE.NVIM(1)