opencode.nvim: Wiring OpenCode's TUI and API into Neovim's Native Interfaces
Neovim 🤝 OpenCode in the flow that you already know.
At a glance
- What is it?
- opencode.nvim is a Lua plugin that connects Neovim to an OpenCode server, injecting editor context into prompts and translating OpenCode events into autocmds. It is a thin bridge, not a reimplementation, and its value depends entirely on whether you already run OpenCode.
- Who is it for?
- Adopt opencode.nvim if you already run an OpenCode server and want its TUI reachable from Neovim without leaving your keymaps and registers. Skip it if you have not installed OpenCode, since the plugin has nothing to talk to and no fallback path.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Lua, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The gap opencode.nvim fills between Neovim and an OpenCode server
OpenCode ships a terminal user interface and an API. If you work in Neovim, reaching that TUI means leaving the editor, and copying a selection into a prompt means losing the file path, the cursor position and the diagnostics attached to that range. opencode.nvim targets that specific friction. Its README frames the intent as keeping you in the flow you already know, and the mechanism is deliberately narrow: the plugin does not build a second chat interface, it connects to OpenCode and hands it context drawn from the editor.
The intended user is someone who already runs OpenCode and treats it as a pair programmer at small scope. The README's motivation section is explicit that the human stays in control and that AI works best at focused ranges rather than whole repositories. That positioning matters for adoption. This is not a tool for someone who wants an agent to own a task end to end. It is for someone who wants to point at a function, attach the surrounding diagnostics, and send that to a model without retyping any of it.
The plugin is MIT licensed, written in Lua, and its latest release is v1.0.0, published 2026-08-20, with v0.14.0 and v0.13.4 preceding it. The jump to 1.0 after a run of 0.x releases is the kind of signal worth reading alongside the changelog rather than instead of it.
How context injection actually works: placeholders, not clipboard plumbing
The core mechanism is placeholder substitution. The README documents a table of tokens that opencode.nvim replaces in prompts with the corresponding context. @this resolves to the range or selection if there is one, otherwise the cursor position. @buffer is the current buffer, @buffers is every open buffer. @diagnostics resolves to diagnostics within the range or selection if any, else diagnostics for the whole current buffer. @marks is global marks, @quickfix is the quickfix list, @visible is visible text.
That table is the most concrete thing in the repository, and it is also where the design shows its shape. The fallback behaviour is hierarchical: a range wins over a cursor, a selection-scoped diagnostic set wins over a buffer-wide one. So the same token produces different payloads depending on visual mode and cursor placement. That is convenient in practice and slightly opaque when a prompt comes back with more context than you expected. Reading the table before you bind keys is worth the two minutes.
The operator functions build on the same idea. The README's example keymaps use require("opencode").operator("@this ") with expr = true for a motion, and the same operator with an appended underscore for a linewise variant. That is a Vim-native construction: the operator composes with existing motions rather than defining a new selection grammar. The README states the goal directly, that the plugin leverages OpenCode's existing TUI and API via standard Neovim interfaces rather than introducing another interaction model. Whether that holds up depends on how much you rely on custom operators, but the choice to express context as an operator argument rather than a bespoke picker is the reason the keymaps look like ordinary Neovim keymaps.
Installation paths and the configuration surface you actually touch
Three installation routes appear in the README. The recommended one is vim.pack, Neovim's built-in package manager, with the plugin added via vim.pack.add and a version constraint of vim.version.range("*") for the latest stable release. lazy.nvim is documented in a collapsible block with version = "*" and a config function. nixvim users get a three-line snippet adding pkgs.vimPlugins.opencode-nvim to extraPlugins.
Configuration runs through a single global, vim.g.opencode_opts, typed as opencode.Opts. The README does not enumerate the options inline. It points to lua/opencode/config.lua for the full list and defaults, and suggests going to definition on the type. That is a reasonable convention for a Neovim plugin, but it means you cannot evaluate the configuration surface from the README alone. You have to open the file. Treat that as a documentation gap rather than a design flaw, and treat the file as the source of truth for your pinned version.
The example keymaps are the part most readers will copy. Ctrl-a calls require("opencode").ask("@this: ") in normal and visual mode. Ctrl-x calls require("opencode").select(). The go motion appends a range, goo appends a line. Shift-Ctrl-u and Shift-Ctrl-d call require("opencode").command with session.half.page.up and session.half.page.down, which scrolls the OpenCode session rather than the Neovim buffer. That last pair is the clearest sign of the integration's intent: the OpenCode TUI is treated as a pane you drive from Neovim keymaps.
After setup the README advises running :checkhealth opencode. Given that the plugin depends on reaching a server, that command is the first thing to run when nothing appears to work.
The integrations that reveal the plugin's real architecture
The README documents four integrations, and they are more informative than the feature list. snacks.nvim is the most revealing. Its input module, when enabled, enhances the Ask flow, and its picker module enhances Select. The README notes that blink.cmp completions in Ask work through opencode.nvim's in-process LSP, and that this is only applicable when using snacks.input. An in-process LSP is a specific architectural fact: the plugin stands up a language server inside Neovim to serve completions for the prompt buffer, with a filetype of opencode_ask. blink.cmp is configured to enable the lsp and buffer sources, either globally or per_filetype for opencode_ask.
The snacks picker integration shows the data flow end to end. A custom action named opencode_send maps to Alt-o in the picker's input window. When invoked, it maps over the selected items, and for each item with a file it calls require("opencode").format with path, from and to fields taken from the item's position and end position. Items without a file fall back to item.text. The results are joined with commas and passed to require("opencode").prompt with a trailing space. That is a concrete pipeline: picker selection to formatted context string to prompt. It also tells you the format function takes a table with path, from and to, which is not spelled out anywhere else in the README.
lualine gets a statusline component, require("opencode").statusline, which the README says shows the currently connected server and its status. That phrasing is worth pausing on. The plugin can connect to any OpenCode server or start an integrated instance, so there is a notion of a current connection that can change. Surfacing it in the statusline suggests the connection is not always obvious from the editor state, which is a mild admission that the integration has state you may need to watch.
Where opencode.nvim is the wrong tool
The plugin's dependency is absolute. It integrates with OpenCode, and if OpenCode is not installed and running somewhere reachable, there is no degraded mode described in the README. There is no local model fallback, no offline path, no alternative backend. That is not a defect in a plugin whose stated purpose is integration, but it does mean the adoption decision is really a decision about OpenCode, and opencode.nvim is downstream of it.
The second limitation is scope discipline. The README's motivation argues for small, focused scopes with the human driving. The plugin's context tokens support that: @this, @diagnostics, @quickfix. Nothing in the documented surface suggests a mechanism for long autonomous runs or multi-step task delegation. If your workflow is to describe a feature and walk away, this plugin's affordances point the other way. You would be paying the integration cost for a bridge you rarely cross.
The third is version drift. The README shows a version constraint of vim.version.range("*") for the latest stable release, and the release history moves quickly, with v1.0.0 following v0.14.0 by roughly three weeks. Configuration lives in a Lua file the README does not reproduce, so an option you rely on can change shape between releases without the README reflecting it. Pinning to a tag rather than a range is the obvious mitigation, and the README's own examples give you the syntax to do it.
Finally, the README is truncated in the material available here. The context table ends mid-row at @visible, and the configuration section defers to a file rather than listing options. Anything about keybinding conflicts, server discovery, or authentication is not visible in what I can see. Do not assume those questions are unanswered in the repository, but do not assume they are answered either.
How this differs from a general-purpose AI completion plugin
The obvious comparison is a completion plugin such as blink.cmp or nvim-cmp paired with an inline suggestion source. Those tools insert text at the cursor and are evaluated on latency and acceptance rate. opencode.nvim does something structurally different: it composes a prompt containing editor state and sends it to a separate process, then handles the response as edits you accept or reject. The README lists accept/reject and reload of OpenCode edits as a feature, which confirms the response is treated as a reviewable change rather than an inline insertion.
That difference has consequences. A completion plugin keeps you in insert mode and never asks you to think about what context was attached. opencode.nvim makes context the explicit subject of the interaction through placeholders, and the snacks picker example shows you can batch several files into one prompt. The trade is ceremony for control. You press Ctrl-a, you see the token, you know what is being sent.
There is a second comparison worth drawing, against driving the OpenCode TUI in a separate terminal pane. The README's stated goal is to avoid introducing another interaction model, and the scrolling keymaps for session.half.page.up and session.half.page.down show that the TUI is still there, just reachable from Neovim. So the plugin is not replacing the terminal workflow, it is relocating it. If you are comfortable with a split terminal and tmux, the gain is mostly the context injection and the autocmd events, not the interface.
Maintenance cost, licence, and what to verify before adopting
The plugin is MIT licensed, which permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained. That is a permissive, low-friction licence with no copyleft obligation on your own configuration. This is a description of the licence text, not legal advice; if your organisation has a policy on third-party dependencies, route it through whoever owns that policy.
Maintenance cost has three components. The first is OpenCode itself, since the plugin is useless without a reachable server. The second is the plugin's own release cadence, which the release list shows as active, with v1.0.0 in August 2026. The third is your keymap surface. The README's examples bind Ctrl-a, Ctrl-x, go, goo, Shift-Ctrl-u and Shift-Ctrl-d. Several of those collide with common defaults or with other plugins, particularly go as an operator prefix and the Ctrl-a pair if you use them for anything else. Budget a pass through your existing keymaps before copying the block.
The autocmd feature deserves a mention in a maintenance context. The README lists handling OpenCode events as autocmds as a capability, which means you can attach editor behaviour to agent state. That is also a place where a plugin upgrade can change event names or payloads and break your configuration silently. If you build on it, the events you subscribe to are worth writing down somewhere outside your config.
What to verify first, concretely: run :checkhealth opencode as the README instructs, and open lua/opencode/config.lua to read the option names and defaults for the version you installed rather than the version the README was written against. Then decide whether vim.g.opencode_opts needs anything set at all. The README describes the defaults as a rich and reliable default experience, so the honest starting point is an empty table and a health check, not a copied configuration you do not understand.
Editorial conclusion
Adopt opencode.nvim if you already run an OpenCode server and want its TUI reachable from Neovim without leaving your keymaps and registers. Skip it if you have not installed OpenCode, since the plugin has nothing to talk to and no fallback path. Before wiring the example keymaps, run :checkhealth opencode and read lua/opencode/config.lua to see which vim.g.opencode_opts keys exist in your pinned version.
Community notes