Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions lua/opencode/cli/server.lua
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,15 @@ local function find_server_in_nvim_cwd()
end
end
end

-- Fallback: try provider-specific discovery
if not found_server then
local provider = require("opencode.config").provider
if provider and provider.find_server then
found_server = provider:find_server()
end
end

if not found_server then
error("No `opencode` servers in Neovim's CWD", 0)
end
Expand Down Expand Up @@ -198,8 +207,9 @@ end

---Attempt to get the `opencode` server's port. Tries, in order:
---1. A process responding on `opts.port`.
---2. Any `opencode` process running in Neovim's CWD. Prioritizes embedded.
---3. Calling `opts.provider.start` and polling for the port.
---2. Any `opencode` process running inside Neovim's CWD. Prioritizes embedded.
---3. Provider-specific discovery (e.g., tmux sibling panes).
---4. Calling `opts.provider.start` and polling for the port.
---
---@param launch boolean? Whether to launch a new server if none found. Defaults to true.
---@return Promise<number>
Expand Down
1 change: 1 addition & 0 deletions lua/opencode/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ local defaults = {
-- Disables allow-passthrough in the tmux split
-- preventing OSC escape sequences from leaking into the nvim buffer
allow_passthrough = false,
auto_close = true, -- Auto-close the tmux pane when opencode exits
},
},
}
Expand Down
9 changes: 7 additions & 2 deletions lua/opencode/provider/init.lua
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---@module 'snacks.terminal'

---Provide an integrated `opencode`.
---Providers should ignore manually-started `opencode` instances,
---operating only on those they start themselves.
---`start`/`stop`/`toggle` should only operate on provider-managed instances.
---`find_server` may attach to any existing instance for connection purposes.
---@class opencode.Provider
---
---The name of the provider.
Expand Down Expand Up @@ -33,6 +33,11 @@
---Should return `true` if the provider is available,
---else a reason string and optional advice (for `vim.health.warn`).
---@field health? fun(): boolean|string, ...string|string[]
---
---Find an existing `opencode` server via provider-specific discovery.
---Unlike other methods, may return servers not started by the provider.
---Called as a fallback when CWD-based discovery fails.
---@field find_server? fun(self: opencode.Provider): opencode.cli.server.Server|nil

---Configure and enable built-in providers.
---@class opencode.provider.Opts
Expand Down
41 changes: 40 additions & 1 deletion lua/opencode/provider/tmux.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ Tmux.name = "tmux"
---
---Focus the opencode pane when created. Default: `false`
---@field focus? boolean
---
---Auto-close the tmux pane when opencode exits. Default: `true`
---@field auto_close? boolean
--
---Allow `allow-passthrough` on the opencode pane.
-- When enabled, opencode.nvim will use your configured tmux `allow-passthrough` option on its pane.
Expand Down Expand Up @@ -107,10 +110,46 @@ end
---Kill the `opencode` pane.
function Tmux:stop()
local pane_id = self:get_pane_id()
if pane_id then
if pane_id and self.opts.auto_close ~= false then
vim.fn.system("tmux kill-pane -t " .. pane_id)
self.pane_id = nil
end
end

---Find an `opencode` server running in a sibling pane of the current tmux window.
---@return opencode.cli.server.Server|nil
function Tmux:find_server()
if self.health() ~= true then
return nil
end

local session_window = vim.fn.system("tmux display-message -p '#{session_name}:#{window_index}'"):gsub("\n", "")
local panes_output = vim.fn.system(string.format("tmux list-panes -t '%s' -F '#{pane_tty}'", session_window))

for tty in panes_output:gmatch("[^\r\n]+") do
local tty_short = tty:gsub("^/dev/", "")
local ps_output = vim.fn.system(string.format("ps -t %s -o pid,command", tty_short))

for line in ps_output:gmatch("[^\r\n]+") do
local pid = line:match("^%s*(%d+).*opencode")
if pid then
local lsof = vim.fn.system(string.format("lsof -w -iTCP -sTCP:LISTEN -P -n -a -p %s", pid))
local port = lsof:match(":(%d+) %(LISTEN%)")
if port then
local ok, path = pcall(require("opencode.cli.client").get_path, tonumber(port))
if ok then
return {
pid = tonumber(pid),
port = tonumber(port),
cwd = path.directory or path.worktree,
}
end
end
end
end
end

return nil
end

return Tmux