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
12 changes: 11 additions & 1 deletion lua/opencode/cli/server.lua
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,15 @@ local function find_server_inside_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 inside Neovim's CWD", 0)
end
Expand Down Expand Up @@ -200,7 +209,8 @@ end
---Attempt to get the `opencode` server's port. Tries, in order:
---1. A process responding on `opts.port`.
---2. Any `opencode` process running inside Neovim's CWD. Prioritizes embedded.
---3. Calling `opts.provider.start` and polling for the port.
---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.
function M.get_port(launch)
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
36 changes: 36 additions & 0 deletions lua/opencode/provider/tmux.lua
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,40 @@ function Tmux:stop()
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