fix(ai): restore opencode.nvim integration with OpenCode V1 CLI #10

Merged
sam_oneal merged 3 commits from fix/opencode-ai into main 2026-09-21 13:09:26 +00:00
5 changed files with 68 additions and 26 deletions
+2 -2
View File
@@ -142,7 +142,7 @@
"filename": "README.md", "filename": "README.md",
"hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684", "hashed_secret": "9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684",
"is_verified": false, "is_verified": false,
"line_number": 85 "line_number": 110
} }
], ],
"lazy-lock.json": [ "lazy-lock.json": [
@@ -582,5 +582,5 @@
} }
] ]
}, },
"generated_at": "2026-06-06T21:52:58Z" "generated_at": "2026-09-21T13:00:27Z"
} }
+28 -4
View File
@@ -77,6 +77,31 @@ AI is intentionally **environment-agnostic**: opencode.nvim drives the external
OpenAI, local models, etc.) is chosen in opencode's own config rather than here. OpenAI, local models, etc.) is chosen in opencode's own config rather than here.
Install the `opencode` CLI separately and configure your provider there. Install the `opencode` CLI separately and configure your provider there.
> [!IMPORTANT]
> This integration requires the OpenCode **V1** CLI. opencode.nvim connects to
> the API server that `opencode --port` exposes; OpenCode **v2 removed that
> flag** and replaced the server API (v2 support in opencode.nvim is tracked
> upstream in [nickjvandyke/opencode.nvim#322](https://github.com/nickjvandyke/opencode.nvim/issues/322)).
>
> **Binary split:** Neovim explicitly launches the pinned V1 binary at
> `~/.opencode/bin/opencode` (see `opencode_cmd` in `lua/plugins/ai.lua` and the
> `<leader>at` toggle in `lua/config/keymaps.lua`), so your terminal is free to
> keep using the latest `opencode` (v2) from its normal PATH.
>
> Pin V1 with the install script (replace `1.18.31` with the latest V1 release):
>
> ```sh
> curl -fsSL https://opencode.ai/install | bash -s -- --version 1.18.31
> ```
>
> Do **not** put `~/.opencode/bin` on your shell PATH or set
> `OPENCODE_DISABLE_AUTOUPDATE=1` globally: installing the pinned binary via the
> script above prepends `~/.opencode/bin` to `PATH` in `~/.zshrc` (remove that
> line) and a global env var would stop the terminal's v2 from auto-updating.
> Instead, the config sets `OPENCODE_DISABLE_AUTOUPDATE=1` on the spawned V1
> process only, so the pinned binary can't replace itself with v2 while your
> terminal's opencode keeps updating.
### Database (vim-dadbod) ### Database (vim-dadbod)
Open the UI with `<leader>Du`. Add a connection with `<leader>Da`. Examples: Open the UI with `<leader>Du`. Add a connection with `<leader>Da`. Examples:
@@ -353,7 +378,6 @@ project, branch, and worktree count when inside a git repo.
## Known issues (in current repo) ## Known issues (in current repo)
Two unrelated typos exist in `lua/config/keymaps.lua`'s opencode block (present One quirk remains in `lua/config/keymaps.lua`'s opencode block (present before
before these docs): the scroll-up command string reads `sesion.half.page.up` these docs): `<leader>at` is also mapped in `t` (terminal) mode, not just `n` —
(should be `session.…`), and `<leader>at` is mapped in `t` (terminal) mode where intentional, so the TUI terminal window itself can toggle the split.
`n`/`x` is likely intended. Documented here for accuracy; fix when convenient.
+1
View File
@@ -31,6 +31,7 @@
"neotest-golang": { "branch": "main", "commit": "53f06063780b0b244583d3d32b138fc2df21e162" }, "neotest-golang": { "branch": "main", "commit": "53f06063780b0b244583d3d32b138fc2df21e162" },
"neotest-python": { "branch": "master", "commit": "b0d3a861bd85689d8ed73f0590c47963a7eb1bf9" }, "neotest-python": { "branch": "master", "commit": "b0d3a861bd85689d8ed73f0590c47963a7eb1bf9" },
"noice.nvim": { "branch": "main", "commit": "7bfd942445fb63089b59f97ca487d605e715f155" }, "noice.nvim": { "branch": "main", "commit": "7bfd942445fb63089b59f97ca487d605e715f155" },
"opencode.nvim": { "branch": "main", "commit": "84eabfbe65a44f452ee302fe360cea9fd9939f31" },
"nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" }, "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" },
"nvim-colorizer.lua": { "branch": "master", "commit": "81e676d3203c9eb6e4c0ccf1eba1679296ef923f" }, "nvim-colorizer.lua": { "branch": "master", "commit": "81e676d3203c9eb6e4c0ccf1eba1679296ef923f" },
"nvim-coverage": { "branch": "main", "commit": "a939e425e363319d952a6c35fb3f38b34041ded2" }, "nvim-coverage": { "branch": "main", "commit": "a939e425e363319d952a6c35fb3f38b34041ded2" },
+9 -2
View File
@@ -69,7 +69,14 @@ map({ "n", "x" }, "<leader>ax", function()
require("opencode").select() require("opencode").select()
end, { desc = "AI Ask (selection)" }) end, { desc = "AI Ask (selection)" })
map({ "n", "t" }, "<leader>at", function() map({ "n", "t" }, "<leader>at", function()
require("opencode").toggle() -- opencode.nvim v1.0.2 removed `opencode.toggle()`; toggle the TUI terminal
-- directly. Command, `env` and window opts must match `opencode_cmd` and
-- `snacks_terminal_opts` in lua/plugins/ai.lua so `snacks.terminal.tid`
-- identifies the same terminal.
require("snacks.terminal").toggle(vim.fn.expand("$HOME/.opencode/bin/opencode") .. " --port", {
env = { OPENCODE_DISABLE_AUTOUPDATE = "1" },
win = { position = "right", enter = false },
})
end, { desc = "AI Toggle" }) end, { desc = "AI Toggle" })
map({ "n", "x" }, "go", function() map({ "n", "x" }, "go", function()
@@ -80,7 +87,7 @@ map("n", "goo", function()
end, { desc = "Add line to AI chat", expr = true }) end, { desc = "Add line to AI chat", expr = true })
map("n", "<S-C-u>", function() map("n", "<S-C-u>", function()
require("opencode").command("sesion.half.page.up") require("opencode").command("session.half.page.up")
end, { desc = "Scroll AI chat up" }) end, { desc = "Scroll AI chat up" })
map("n", "<S-C-d>", function() map("n", "<S-C-d>", function()
require("opencode").command("session.half.page.down") require("opencode").command("session.half.page.down")
+28 -18
View File
@@ -1,7 +1,13 @@
return { return {
{ {
-- Drive the OpenCode TUI from Neovim.
--
-- IMPORTANT: this integration requires the OpenCode **V1** CLI. opencode.nvim
-- connects to the HTTP API that `opencode --port` exposes; OpenCode v2 removed
-- that flag and replaced the server API, which opencode.nvim does not support
-- yet (https://github.com/nickjvandyke/opencode.nvim/issues/322).
"nickjvandyke/opencode.nvim", "nickjvandyke/opencode.nvim",
version = "v0.10.1", version = "v1.0.2",
dependencies = { dependencies = {
{ {
---@module "snacks" ---@module "snacks"
@@ -11,8 +17,14 @@ return {
input = {}, -- Enhances `ask()` input = {}, -- Enhances `ask()`
picker = { -- Enhances `select()` picker = { -- Enhances `select()`
actions = { actions = {
opencode_send = function(...) opencode_send = function(picker) ---@param picker snacks.Picker
return require("opencode").snacks_picker_send(...) local items = vim.tbl_map(function(item) ---@param item snacks.picker.Item
return item.file
and require("opencode").format({ path = item.file, from = item.pos, to = item.end_pos })
or item.text
end, picker:selected({ fallback = true }))
require("opencode").prompt(table.concat(items, ", ") .. " ")
end, end,
}, },
win = { win = {
@@ -27,38 +39,36 @@ return {
}, },
}, },
config = function() config = function()
local opencode_cmd = "opencode --port" -- `--port` exposes OpenCode's HTTP API, which opencode.nvim discovers and
-- connects to. V1 CLI only; v2 removed this flag.
--
-- Binary split: nvim explicitly uses the pinned V1 binary at
-- ~/.opencode/bin/opencode while the interactive shell keeps using the
-- latest `opencode` (v2) from its PATH. Keep this in sync with the
-- `<leader>at` toggle in lua/config/keymaps.lua.
local opencode_cmd = vim.fn.expand("$HOME/.opencode/bin/opencode") .. " --port"
---@type snacks.terminal.Opts ---@type snacks.terminal.Opts
local snacks_terminal_opts = { local snacks_terminal_opts = {
-- V1 checks for updates on startup and would replace itself with V2;
-- disable autoupdate for this pinned binary only (does not affect the
-- terminal's v2, which keeps updating).
env = { OPENCODE_DISABLE_AUTOUPDATE = "1" },
win = { win = {
position = "right", position = "right",
enter = false, enter = false,
on_win = function(win)
-- Set up keymaps and cleanup for an arbitary terminal
require("opencode.terminal").setup(win.win)
end,
}, },
} }
---@type opencode.Opts ---@type opencode.Opts
vim.g.opencode_opts = { vim.g.opencode_opts = {
-- Specific configuration around opencode
server = { server = {
start = function() start = function()
require("snacks.terminal").open(opencode_cmd, snacks_terminal_opts) require("snacks.terminal").open(opencode_cmd, snacks_terminal_opts)
end, 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,
},
lsp = {
enabled = true,
}, },
} }
-- Reload buffers that OpenCode edits (the plugin also sets this itself)
vim.o.autoread = true -- Required for `opts.events.reload` vim.o.autoread = true -- Required for `opts.events.reload`
end, end,
}, },