Seamlessly integrate the opencode AI assistant with Neovim.
demo.mp4
Note
Uses opencode's currently undocumented, likely unstable API.
Latest tested opencode version: v0.4.1
- Auto-find any
opencodeprocess running inside Neovim's CWD or open in an embedded terminal - Select and input customizable prompts
- Inject customizable editor context
- Auto-reload edited buffers
- Write and refine prompts quickly with completion, highlight, and normal-mode support
When your prompt contains placeholders, opencode.nvim will replace them with context before sending:
| Placeholder | Context |
|---|---|
@buffer |
Current buffer |
@buffers |
Open buffers |
@cursor |
Cursor position |
@selection |
Selected text |
@visible |
Visible text |
@diagnostic |
Current line diagnostics |
@diagnostics |
Current buffer diagnostics |
@quickfix |
Quickfix list |
@diff |
Git diff |
lazy.nvim
{
'NickvanDyke/opencode.nvim',
dependencies = { 'folke/snacks.nvim', },
---@type opencode.Config
opts = {
-- Your configuration, if any
},
-- stylua: ignore
keys = {
{ '<leader>ot', function() require('opencode').toggle() end, desc = 'Toggle embedded opencode', },
{ '<leader>oa', function() require('opencode').ask() end, desc = 'Ask opencode', mode = 'n', },
{ '<leader>oa', function() require('opencode').ask('@selection: ') end, desc = 'Ask opencode about selection', mode = 'v', },
{ '<leader>op', function() require('opencode').select_prompt() end, desc = 'Select prompt', mode = { 'n', 'v', }, },
{ '<leader>on', function() require('opencode').command('session_new') end, desc = 'New session', },
{ '<leader>oy', function() require('opencode').command('messages_copy') end, desc = 'Copy last message', },
{ '<S-C-u>', function() require('opencode').command('messages_half_page_up') end, desc = 'Scroll messages up', },
{ '<S-C-d>', function() require('opencode').command('messages_half_page_down') end, desc = 'Scroll messages down', },
},
}nixvim
programs.nixvim = { extraPlugins = [ pkgs.vimPlugins.opencode-nvim ]; keymaps = [ { key = "<leader>ot"; action = "<cmd>lua require('opencode').toggle()<CR>"; } { key = "<leader>oa"; action = "<cmd>lua require('opencode').ask()<CR>"; mode = "n"; } { key = "<leader>oa"; action = "<cmd>lua require('opencode').ask('@selection: ')<CR>"; mode = "v"; } { key = "<leader>oe"; action = "<cmd>lua require('opencode').select_prompt()<CR>"; mode = ["n" "v"]; } { key = "<leader>on"; action = "<cmd>lua require('opencode').command('session_new')<CR>"; } ]; };
See all the available options and their defaults here.
Tip
opencode.nvim offers a flexible API β customize prompts, contexts, and keymaps to fit your workflow!
Add custom selectable prompts to opts.prompts:
{
prompts = {
joke = {
description = 'Tell me a cat joke',
prompt = 'Tell me a joke about cats. Make it funny, but not too funny.',
},
},
}Add keymaps for your favorite built-in or custom prompts:
local prompts = require('opencode.config').options.prompts or {} vim.keymap.set('n', '<leader>oj', function() require('opencode').prompt(prompts.joke.prompt) end, { desc = prompts.joke.description }) vim.keymap.set('v', '<leader>os', function() require('opencode').prompt("Tell me a story about cats and @selection") end, { desc = "Tell me a story" })
Add custom contexts to opts.contexts. The below replaces @grapple with files tagged by grapple.nvim:
{
contexts = {
---@type opencode.Context
['@grapple'] = {
description = 'Files tagged by grapple',
value = function()
local tags = require('grapple').tags()
if not tags or #tags == 0 then
return nil
end
local paths = {}
for _, tag in ipairs(tags) do
table.insert(paths, tag.path)
end
return table.concat(paths, ', ')
end,
},
}
}opencode.nvim offers context placeholder completions in the ask input.
blink.cmp
opencode.nvim automatically registers opts.auto_register_cmp_sources (default: { "opencode", "buffer" }) with blink.cmp (if loaded) at runtime.
Built-in
Press <Tab> to trigger Neovim's built-in completion.
opencode.nvim forwards opencode's Server-Sent-Events as an 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), vim.log.levels.DEBUG) -- Do something interesting, like show a notification when opencode finishes responding if args.data.type == "session.idle" then vim.notify("opencode finished responding", vim.log.levels.INFO) end end, })
| Name | Description |
|---|---|
OpencodePlaceholder |
Placeholders in ask input |
- Inspired by (and partially based on) nvim-aider and later neopencode.nvim.
opencode.nvimuses 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.