Integrate the opencode AI assistant with Neovim — streamline editor-aware research, reviews, and requests.
demo.mp4
- Auto-connect to any
opencoderunning inside Neovim's CWD, or manage an embedded instance. - Input prompts with completions, highlights, and normal-mode support.
- Select prompts from a library and define your own.
- Inject relevant editor context (buffer, cursor, selection, diagnostics, ...).
- Control
opencodewith commands. - Auto-reload buffers edited by
opencodein real-time. - Respond to
opencodepermission requests. - Monitor state via statusline component.
- Forward
opencode's Server-Sent-Events as Neovim autocmds for automation. - Sensible defaults with well-documented, flexible configuration and API to fit your workflow.
{
"NickvanDyke/opencode.nvim",
dependencies = {
-- Recommended for `ask()` and `select()`.
-- Required for default `toggle()` implementation.
{ "folke/snacks.nvim", opts = { input = {}, picker = {}, terminal = {} } },
},
config = function()
---@type opencode.Opts
vim.g.opencode_opts = {
-- Your configuration, if any — see `lua/opencode/config.lua`, or "goto definition".
}
-- Required for `opts.auto_reload`.
vim.o.autoread = true
-- Recommended/example keymaps.
vim.keymap.set({ "n", "x" }, "<C-a>", function() require("opencode").ask("@this: ", { submit = true }) end, { desc = "Ask opencode" })
vim.keymap.set({ "n", "x" }, "<C-x>", function() require("opencode").select() end, { desc = "Execute opencode action…" })
vim.keymap.set({ "n", "x" }, "ga", function() require("opencode").prompt("@this") end, { desc = "Add to opencode" })
vim.keymap.set({ "n", "t" }, "<C-.>", function() require("opencode").toggle() end, { desc = "Toggle opencode" })
vim.keymap.set("n", "<S-C-u>", function() require("opencode").command("session.half.page.up") end, { desc = "opencode half page up" })
vim.keymap.set("n", "<S-C-d>", function() require("opencode").command("session.half.page.down") end, { desc = "opencode half page down" })
-- You may want these if you stick with the opinionated "<C-a>" and "<C-x>" above — otherwise consider "<leader>o".
vim.keymap.set('n', '+', '<C-a>', { desc = 'Increment', noremap = true })
vim.keymap.set('n', '-', '<C-x>', { desc = 'Decrement', noremap = true })
end,
}nixvim
programs.nixvim = {
extraPlugins = [
pkgs.vimPlugins.opencode-nvim
];
};Tip
Run :checkhealth opencode after installation.
opencode.nvim provides a rich and reliable default experience — see all available options and their defaults here.
By default, opencode.nvim will use snacks.terminal (if available) to run opencode when it isn't already running:
vim.g.opencode_opts = {
provider = {
enabled = "snacks",
---@type opencode.provider.Snacks
snacks = {
-- Customize `snacks.terminal` to your liking.
}
}
}You can manually run opencode inside Neovim's CWD however you like (terminal plugin, multiplexer, app, ...) and opencode.nvim will find it! But consider configuring opencode.nvim to manage it on your behalf:
vim.g.opencode_opts = {
---@type opencode.Provider
provider = {
toggle = function(self)
-- Called by `require("opencode").toggle()`.
end,
start = function(self)
-- Called when sending a prompt or command to `opencode` but no process was found.
-- `opencode.nvim` will poll for a couple seconds waiting for one to appear.
end,
show = function(self)
-- Called when a prompt or command is sent to `opencode`,
-- *and* this provider's `toggle` or `start` has previously been called
-- (so as to not interfere when `opencode` was started externally).
end
}
}Tip
I only use snacks.terminal, but welcome PRs adding your custom method as a built-in provider 🙂
Input a prompt to send to opencode.
- Press
<Up>to browse recent asks. - Highlights placeholders.
- Completes placeholders.
- Press
<Tab>to trigger built-in completion. - When using
blink.cmpandsnacks.input, registersopts.auto_register_cmp_sources.
- Press
Select from all opencode.nvim functionality.
Send a prompt to opencode.
Replaces placeholders in the prompt with the corresponding context:
| Placeholder | Context |
|---|---|
@buffer |
Current buffer |
@buffers |
Open buffers |
@cursor |
Cursor position |
@selection |
Visual selection |
@this |
Visual selection if any, else cursor position |
@visible |
Visible text |
@diagnostics |
Current buffer diagnostics |
@quickfix |
Quickfix list |
@diff |
Git diff |
@grapple |
grapple.nvim tags |
Reference a prompt by name to review, explain, and improve your code:
| Name | Prompt |
|---|---|
ask |
... |
explain |
Explain @this and its context |
optimize |
Optimize @this for performance and readability |
document |
Add comments documenting @this |
test |
Add tests for @this |
review |
Review @this for correctness and readability |
diagnostics |
Explain @diagnostics |
fix |
Fix @diagnostics |
diff |
Review the following git diff for correctness and readability: @diff |
buffer |
@buffer |
this |
@this |
Send a command to opencode:
| Command | Description |
|---|---|
session.list |
List sessions |
session.new |
Start a new session |
session.share |
Share the current session |
session.interrupt |
Interrupt the current session |
session.compact |
Compact the current session (reduce context size) |
session.page.up |
Scroll messages up by one page |
session.page.down |
Scroll messages down by one page |
session.half.page.up |
Scroll messages up by half a page |
session.half.page.down |
Scroll messages down by half a page |
session.first |
Jump to the first message in the session |
session.last |
Jump to the last message in the session |
session.undo |
Undo the last action in the current session |
session.redo |
Redo the last undone action in the current session |
prompt.submit |
Submit the TUI input |
prompt.clear |
Clear the TUI input |
agent.cycle |
Cycle the selected agent |
opencode.nvim forwards opencode's Server-Sent-Events as an OpencodeEvent autocmd:
-- Listen for `opencode` events
vim.api.nvim_create_autocmd("User", {
pattern = "OpencodeEvent",
callback = function(args)
-- See the available event types and their properties
vim.notify(vim.inspect(args.data.event))
-- Do something useful
if args.data.event.type == "session.idle" then
vim.notify("`opencode` finished responding")
end
end,
})When opencode edits a file, opencode.nvim automatically reloads the corresponding buffer.
When opencode requests a permission, opencode.nvim waits for idle to ask you to approve or deny it.
require("lualine").setup({
sections = {
lualine_z = {
{
require("opencode").statusline,
},
}
}
})- Inspired by nvim-aider, neopencode.nvim, and sidekick.nvim.
- Uses
opencode's TUI for simplicity — see sudo-tee/opencode.nvim for a Neovim frontend. - mcp-neovim-server may better suit you, but it lacks customization and tool calls are slow and unreliable.