From 2df3b0f0ca484998640c8d85d217ea8e0685d739 Mon Sep 17 00:00:00 2001 From: horaoen Date: Fri, 26 Dec 2025 13:57:43 +0800 Subject: [PATCH 1/3] feat: implement project root detection for terminal providers --- lua/opencode/provider/init.lua | 34 ++++++++++++++++++++++++++++++ lua/opencode/provider/kitty.lua | 3 ++- lua/opencode/provider/snacks.lua | 8 +++++-- lua/opencode/provider/terminal.lua | 1 + lua/opencode/provider/tmux.lua | 5 +++-- lua/opencode/provider/wezterm.lua | 3 +++ 6 files changed, 49 insertions(+), 5 deletions(-) diff --git a/lua/opencode/provider/init.lua b/lua/opencode/provider/init.lua index 1a13a759..7d5e5152 100644 --- a/lua/opencode/provider/init.lua +++ b/lua/opencode/provider/init.lua @@ -52,6 +52,40 @@ local M = {} +---Get the project root directory using smart detection. +---Priority: 1) nvim directory argument, 2) git root, 3) current buffer dir, 4) LSP workspace, 5) cwd +---@return string +function M.get_project_root() + local arg = vim.fn.argv(0) + ---@cast arg string + if arg and arg ~= "" and vim.fn.isdirectory(arg) == 1 then + local path = vim.fn.fnamemodify(arg, ":p"):gsub("/$", "") + return path + end + + local git_root = vim.fn.systemlist("git rev-parse --show-toplevel 2>/dev/null")[1] + if vim.v.shell_error == 0 and git_root and git_root ~= "" then + return git_root + end + + local buf_name = vim.api.nvim_buf_get_name(0) + if buf_name and buf_name ~= "" then + local buf_dir = vim.fn.fnamemodify(buf_name, ":p:h") + if vim.fn.isdirectory(buf_dir) == 1 then + return buf_dir + end + end + + local clients = vim.lsp.get_clients({ bufnr = 0 }) + for _, client in ipairs(clients) do + if client.config.root_dir then + return client.config.root_dir + end + end + + return vim.fn.getcwd() +end + local function subscribe_to_sse() if not require("opencode.config").opts.events.enabled then return diff --git a/lua/opencode/provider/kitty.lua b/lua/opencode/provider/kitty.lua index 7a818d24..ff9d8e98 100644 --- a/lua/opencode/provider/kitty.lua +++ b/lua/opencode/provider/kitty.lua @@ -103,7 +103,8 @@ function Kitty:start() end local location = self.opts.location - local launch_cmd = { "launch", "--cwd=current", "--hold", "--dont-take-focus" } + local cwd = require("opencode.provider").get_project_root() + local launch_cmd = { "launch", "--cwd=" .. cwd, "--hold", "--dont-take-focus" } -- Input validation for `location` option local VALID_LOCATIONS = { diff --git a/lua/opencode/provider/snacks.lua b/lua/opencode/provider/snacks.lua index ee3fc9fe..3f7b9688 100644 --- a/lua/opencode/provider/snacks.lua +++ b/lua/opencode/provider/snacks.lua @@ -43,12 +43,16 @@ function Snacks:get() end function Snacks:toggle() - require("snacks.terminal").toggle(self.cmd, self.opts) + local cwd = require("opencode.provider").get_project_root() + local opts = vim.tbl_deep_extend("force", self.opts, { cwd = cwd }) + require("snacks.terminal").toggle(self.cmd, opts) end function Snacks:start() if not self:get() then - require("snacks.terminal").open(self.cmd, self.opts) + local cwd = require("opencode.provider").get_project_root() + local opts = vim.tbl_deep_extend("force", self.opts, { cwd = cwd }) + require("snacks.terminal").open(self.cmd, opts) end end diff --git a/lua/opencode/provider/terminal.lua b/lua/opencode/provider/terminal.lua index 240a54b1..86d40d84 100644 --- a/lua/opencode/provider/terminal.lua +++ b/lua/opencode/provider/terminal.lua @@ -51,6 +51,7 @@ function Terminal:start() -- FIX: There's a few empty columns on the right side of the terminal until it's redrawn, at least for me. vim.fn.jobstart(self.cmd, { term = true, + cwd = require("opencode.provider").get_project_root(), on_exit = function() self.winid = nil self.bufnr = nil diff --git a/lua/opencode/provider/tmux.lua b/lua/opencode/provider/tmux.lua index 2895c8cf..cb521d04 100644 --- a/lua/opencode/provider/tmux.lua +++ b/lua/opencode/provider/tmux.lua @@ -73,9 +73,10 @@ end function Tmux:start() local pane_id = self:get_pane_id() if not pane_id then - -- Create new pane + local cwd = require("opencode.provider").get_project_root() + local options = self.opts.options or "" self.pane_id = - vim.fn.system(string.format("tmux split-window -d -P -F '#{pane_id}' %s '%s'", self.opts.options, self.cmd)) + vim.fn.system(string.format("tmux split-window -d -P -F '#{pane_id}' -c '%s' %s '%s'", cwd, options, self.cmd)) end end diff --git a/lua/opencode/provider/wezterm.lua b/lua/opencode/provider/wezterm.lua index 9eb3db6d..d58e46d4 100644 --- a/lua/opencode/provider/wezterm.lua +++ b/lua/opencode/provider/wezterm.lua @@ -108,6 +108,9 @@ function Wezterm:start() if not pane_id then local cmd_parts = { "wezterm", "cli", "split-pane" } + table.insert(cmd_parts, "--cwd") + table.insert(cmd_parts, require("opencode.provider").get_project_root()) + if self.opts.direction then table.insert(cmd_parts, "--" .. self.opts.direction) end From c1bea8a4a0e2e1ec1293a4eaf43a5e52169d11c3 Mon Sep 17 00:00:00 2001 From: horaoen Date: Fri, 26 Dec 2025 16:20:07 +0800 Subject: [PATCH 2/3] fix: improve project root detection and enhance terminal visibility management --- lua/opencode/provider/init.lua | 30 ++++++++++++++++-------------- lua/opencode/provider/snacks.lua | 24 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/lua/opencode/provider/init.lua b/lua/opencode/provider/init.lua index 7d5e5152..97888cc9 100644 --- a/lua/opencode/provider/init.lua +++ b/lua/opencode/provider/init.lua @@ -53,9 +53,23 @@ local M = {} ---Get the project root directory using smart detection. ----Priority: 1) nvim directory argument, 2) git root, 3) current buffer dir, 4) LSP workspace, 5) cwd +---Priority: 1) git root, 2) LSP workspace, 3) nvim directory argument, 4) current buffer dir, 5) cwd ---@return string function M.get_project_root() + local cwd = vim.fn.getcwd() + + local git_root = vim.fn.systemlist("git -C " .. vim.fn.shellescape(cwd) .. " rev-parse --show-toplevel 2>/dev/null")[1] + if vim.v.shell_error == 0 and git_root and git_root ~= "" then + return git_root + end + + local clients = vim.lsp.get_clients({ bufnr = 0 }) + for _, client in ipairs(clients) do + if client.config.root_dir then + return client.config.root_dir + end + end + local arg = vim.fn.argv(0) ---@cast arg string if arg and arg ~= "" and vim.fn.isdirectory(arg) == 1 then @@ -63,11 +77,6 @@ function M.get_project_root() return path end - local git_root = vim.fn.systemlist("git rev-parse --show-toplevel 2>/dev/null")[1] - if vim.v.shell_error == 0 and git_root and git_root ~= "" then - return git_root - end - local buf_name = vim.api.nvim_buf_get_name(0) if buf_name and buf_name ~= "" then local buf_dir = vim.fn.fnamemodify(buf_name, ":p:h") @@ -76,14 +85,7 @@ function M.get_project_root() end end - local clients = vim.lsp.get_clients({ bufnr = 0 }) - for _, client in ipairs(clients) do - if client.config.root_dir then - return client.config.root_dir - end - end - - return vim.fn.getcwd() + return cwd end local function subscribe_to_sse() diff --git a/lua/opencode/provider/snacks.lua b/lua/opencode/provider/snacks.lua index 3f7b9688..0f9c345a 100644 --- a/lua/opencode/provider/snacks.lua +++ b/lua/opencode/provider/snacks.lua @@ -42,8 +42,32 @@ function Snacks:get() return win end +---@param current_cwd string +---@return boolean +---@private +function Snacks:hide_other_visible_terminals(current_cwd) + local terminals = require("snacks.terminal").list() + local did_hide = false + for _, term in ipairs(terminals) do + if term.buf and vim.api.nvim_buf_is_valid(term.buf) then + local term_info = vim.b[term.buf].snacks_terminal + local is_opencode = term_info and term_info.cmd == self.cmd + local is_other_cwd = term_info and term_info.cwd ~= current_cwd + local is_visible = term.win and vim.api.nvim_win_is_valid(term.win) + if is_opencode and is_other_cwd and is_visible then + term:hide() + did_hide = true + end + end + end + return did_hide +end + function Snacks:toggle() local cwd = require("opencode.provider").get_project_root() + if self:hide_other_visible_terminals(cwd) then + return + end local opts = vim.tbl_deep_extend("force", self.opts, { cwd = cwd }) require("snacks.terminal").toggle(self.cmd, opts) end From e1f652d1d11fcc5e880598ca6d25e42dc8bdc26f Mon Sep 17 00:00:00 2001 From: horaoen Date: Fri, 26 Dec 2025 16:20:07 +0800 Subject: [PATCH 3/3] fix: improve project root detection and enhance terminal visibility management --- lua/opencode/provider/init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lua/opencode/provider/init.lua b/lua/opencode/provider/init.lua index 97888cc9..340ac99a 100644 --- a/lua/opencode/provider/init.lua +++ b/lua/opencode/provider/init.lua @@ -58,7 +58,8 @@ local M = {} function M.get_project_root() local cwd = vim.fn.getcwd() - local git_root = vim.fn.systemlist("git -C " .. vim.fn.shellescape(cwd) .. " rev-parse --show-toplevel 2>/dev/null")[1] + local git_cmd = "git -C " .. vim.fn.shellescape(cwd) .. " rev-parse --show-toplevel 2>/dev/null" + local git_root = vim.fn.systemlist(git_cmd)[1] if vim.v.shell_error == 0 and git_root and git_root ~= "" then return git_root end