From 09b6f37903c10d0449c0bfa4f296965f5fa1510e Mon Sep 17 00:00:00 2001 From: Francis Belanger Date: Thu, 12 Feb 2026 13:14:04 -0500 Subject: [PATCH 1/5] feat(server): handle working dir change This should fix #246 --- lua/opencode/opencode_server.lua | 1 - lua/opencode/ui/autocmds.lua | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lua/opencode/opencode_server.lua b/lua/opencode/opencode_server.lua index e63a1b6d..5db72fcc 100644 --- a/lua/opencode/opencode_server.lua +++ b/lua/opencode/opencode_server.lua @@ -34,7 +34,6 @@ end --- Create a new ServerJob instance --- @return OpencodeServer function OpencodeServer.new() - local log = require('opencode.log') ensure_vim_leave_autocmd() return setmetatable({ diff --git a/lua/opencode/ui/autocmds.lua b/lua/opencode/ui/autocmds.lua index 7c780eef..37d7f5b8 100644 --- a/lua/opencode/ui/autocmds.lua +++ b/lua/opencode/ui/autocmds.lua @@ -1,3 +1,4 @@ +local Promise = require('opencode.promise') local input_window = require('opencode.ui.input_window') local output_window = require('opencode.ui.output_window') local M = {} @@ -48,6 +49,30 @@ function M.setup_autocmds(windows) end, }) + vim.api.nvim_create_autocmd('DirChanged', { + group = group, + callback = Promise.async(function(event) + local log = require('opencode.log') + local state = require('opencode.state') + local server_job = require('opencode.server_job') + local session = require('opencode.session') + local core = require('opencode.core') + + if state.opencode_server then + vim.notify('Directory changed, restarting Opencode server...', vim.log.levels.INFO) + log.info('Shutting down Opencode server due to directory change...') + state.opencode_server:shutdown():await() + server_job.ensure_server():await() + state.active_session = nil + vim.notify('Loading last session for new working dir', vim.log.levels.INFO) + state.active_session = session.get_last_workspace_session():await() + if not state.active_session then + state.active_session = core.create_new_session():await() + end + end + end), + }) + if require('opencode.config').ui.position == 'current' then vim.api.nvim_create_autocmd('BufEnter', { group = group, From 73b4241dc7dbf998b8a8cd3acdca2f74333e24d2 Mon Sep 17 00:00:00 2001 From: Francis Belanger Date: Thu, 12 Feb 2026 15:26:39 -0500 Subject: [PATCH 2/5] refactor: extract directory change handler to core module --- lua/opencode/core.lua | 27 ++++++++ lua/opencode/ui/autocmds.lua | 18 +---- tests/unit/core_spec.lua | 128 +++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+), 17 deletions(-) diff --git a/lua/opencode/core.lua b/lua/opencode/core.lua index aea4b478..1e34148b 100644 --- a/lua/opencode/core.lua +++ b/lua/opencode/core.lua @@ -543,6 +543,33 @@ function M.paste_image_from_clipboard() return image_handler.paste_image_from_clipboard() end +--- Handle working directory changes by restarting the server and loading the appropriate session. +--- This function performs the following steps: +--- 1. Shuts down the existing opencode server +--- 2. Starts a new server instance +--- 3. Clears the active session and context +--- 4. Loads the last workspace session for the new directory, or creates a new one if none exists +--- @return Promise +M.handle_directory_change = Promise.async(function() + local log = require('opencode.log') + + if state.opencode_server then + vim.notify('Directory changed, restarting Opencode server...', vim.log.levels.INFO) + log.info('Shutting down Opencode server due to directory change...') + state.opencode_server:shutdown():await() + server_job.ensure_server():await() + state.active_session = nil + vim.notify('Loading last session for new working dir', vim.log.levels.INFO) + state.last_sent_context = nil + context.unload_attachments() + + state.active_session = session.get_last_workspace_session():await() + if not state.active_session then + state.active_session = M.create_new_session():await() + end + end +end) + function M.setup() state.subscribe('opencode_server', on_opencode_server) state.subscribe('user_message_count', M._on_user_message_count_change) diff --git a/lua/opencode/ui/autocmds.lua b/lua/opencode/ui/autocmds.lua index 37d7f5b8..5cc14480 100644 --- a/lua/opencode/ui/autocmds.lua +++ b/lua/opencode/ui/autocmds.lua @@ -52,24 +52,8 @@ function M.setup_autocmds(windows) vim.api.nvim_create_autocmd('DirChanged', { group = group, callback = Promise.async(function(event) - local log = require('opencode.log') - local state = require('opencode.state') - local server_job = require('opencode.server_job') - local session = require('opencode.session') local core = require('opencode.core') - - if state.opencode_server then - vim.notify('Directory changed, restarting Opencode server...', vim.log.levels.INFO) - log.info('Shutting down Opencode server due to directory change...') - state.opencode_server:shutdown():await() - server_job.ensure_server():await() - state.active_session = nil - vim.notify('Loading last session for new working dir', vim.log.levels.INFO) - state.active_session = session.get_last_workspace_session():await() - if not state.active_session then - state.active_session = core.create_new_session():await() - end - end + core.handle_directory_change():await() end), }) diff --git a/tests/unit/core_spec.lua b/tests/unit/core_spec.lua index 94e5f843..9eeea5a5 100644 --- a/tests/unit/core_spec.lua +++ b/tests/unit/core_spec.lua @@ -469,6 +469,134 @@ describe('opencode.core', function() end) end) + describe('handle_directory_change', function() + local server_job + local context + + before_each(function() + server_job = require('opencode.server_job') + context = require('opencode.context') + stub(server_job, 'ensure_server').invokes(function() + local p = Promise.new() + p:resolve({ + is_running = function() + return true + end, + shutdown = function() + return Promise.new():resolve() + end, + url = 'http://127.0.0.1:4000', + }) + return p + end) + stub(context, 'unload_attachments') + end) + + after_each(function() + if server_job.ensure_server.revert then + server_job.ensure_server:revert() + end + if context.unload_attachments.revert then + context.unload_attachments:revert() + end + end) + + it('does nothing when no server is running', function() + state.opencode_server = nil + state.active_session = { id = 'sess1' } + + core.handle_directory_change():wait() + + assert.is_nil(state.opencode_server) + assert.equal('sess1', state.active_session.id) + assert.stub(server_job.ensure_server).was_not_called() + end) + + it('shuts down existing server and starts new one', function() + local shutdown_called = false + state.opencode_server = { + is_running = function() + return true + end, + shutdown = function() + shutdown_called = true + return Promise.new():resolve() + end, + url = 'http://127.0.0.1:4000', + } + + core.handle_directory_change():wait() + + assert.is_true(shutdown_called) + assert.stub(server_job.ensure_server).was_called() + end) + + it('clears active session and context', function() + state.opencode_server = { + is_running = function() + return true + end, + shutdown = function() + return Promise.new():resolve() + end, + url = 'http://127.0.0.1:4000', + } + state.active_session = { id = 'old-session' } + state.last_sent_context = { some = 'context' } + + core.handle_directory_change():wait() + + -- Should be set to the new session from get_last_workspace_session stub + assert.truthy(state.active_session) + assert.equal('test-session', state.active_session.id) + assert.is_nil(state.last_sent_context) + assert.stub(context.unload_attachments).was_called() + end) + + it('loads last workspace session for new directory', function() + state.opencode_server = { + is_running = function() + return true + end, + shutdown = function() + return Promise.new():resolve() + end, + url = 'http://127.0.0.1:4000', + } + + core.handle_directory_change():wait() + + assert.truthy(state.active_session) + assert.equal('test-session', state.active_session.id) + assert.stub(session.get_last_workspace_session).was_called() + end) + + it('creates new session when no last session exists', function() + state.opencode_server = { + is_running = function() + return true + end, + shutdown = function() + return Promise.new():resolve() + end, + url = 'http://127.0.0.1:4000', + } + + -- Override stub to return nil (no last session) + session.get_last_workspace_session:revert() + stub(session, 'get_last_workspace_session').invokes(function() + local p = Promise.new() + p:resolve(nil) + return p + end) + + core.handle_directory_change():wait() + + assert.truthy(state.active_session) + assert.truthy(state.active_session.id) + end) + end) + describe('switch_to_mode', function() it('sets current model from config file when mode has a model configured', function() local Promise = require('opencode.promise') From 9fdb565ceba2f61616cc8592464ff23533284aa0 Mon Sep 17 00:00:00 2001 From: Francis Belanger Date: Fri, 13 Feb 2026 08:12:18 -0500 Subject: [PATCH 3/5] fix(cwd): multiple cwd changes --- lua/opencode/core.lua | 41 ++++++++++++++++++++++++------------ lua/opencode/ui/autocmds.lua | 8 +++---- tests/unit/core_spec.lua | 9 ++++++++ 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/lua/opencode/core.lua b/lua/opencode/core.lua index 1e34148b..ef53361a 100644 --- a/lua/opencode/core.lua +++ b/lua/opencode/core.lua @@ -545,10 +545,6 @@ end --- Handle working directory changes by restarting the server and loading the appropriate session. --- This function performs the following steps: ---- 1. Shuts down the existing opencode server ---- 2. Starts a new server instance ---- 3. Clears the active session and context ---- 4. Loads the last workspace session for the new directory, or creates a new one if none exists --- @return Promise M.handle_directory_change = Promise.async(function() local log = require('opencode.log') @@ -556,17 +552,34 @@ M.handle_directory_change = Promise.async(function() if state.opencode_server then vim.notify('Directory changed, restarting Opencode server...', vim.log.levels.INFO) log.info('Shutting down Opencode server due to directory change...') + state.opencode_server:shutdown():await() - server_job.ensure_server():await() - state.active_session = nil - vim.notify('Loading last session for new working dir', vim.log.levels.INFO) - state.last_sent_context = nil - context.unload_attachments() - - state.active_session = session.get_last_workspace_session():await() - if not state.active_session then - state.active_session = M.create_new_session():await() - end + + vim.defer_fn( + Promise.async(function() + state.opencode_server = nil + server_job.ensure_server():await() + + vim.notify('Loading last session for new working dir [' .. vim.fn.getcwd() .. ']', vim.log.levels.INFO) + + state.active_session = nil + state.last_sent_context = nil + context.unload_attachments() + + local is_new = false + state.active_session = session.get_last_workspace_session():await() + + if not state.active_session then + is_new = true + state.active_session = M.create_new_session():await() + end + + log.debug( + 'Loaded session for new working dir' .. vim.inspect({ session = state.active_session, is_new = is_new }) + ) + end), + 200 + ) end end) diff --git a/lua/opencode/ui/autocmds.lua b/lua/opencode/ui/autocmds.lua index 5cc14480..9db3996a 100644 --- a/lua/opencode/ui/autocmds.lua +++ b/lua/opencode/ui/autocmds.lua @@ -49,12 +49,12 @@ function M.setup_autocmds(windows) end, }) - vim.api.nvim_create_autocmd('DirChanged', { + vim.api.nvim_create_autocmd('DirChangedPre', { group = group, - callback = Promise.async(function(event) + callback = function(event) local core = require('opencode.core') - core.handle_directory_change():await() - end), + core.handle_directory_change() + end, }) if require('opencode.config').ui.position == 'current' then diff --git a/tests/unit/core_spec.lua b/tests/unit/core_spec.lua index 9eeea5a5..f15f9bd2 100644 --- a/tests/unit/core_spec.lua +++ b/tests/unit/core_spec.lua @@ -472,10 +472,18 @@ describe('opencode.core', function() describe('handle_directory_change', function() local server_job local context + local original_defer_fn before_each(function() server_job = require('opencode.server_job') context = require('opencode.context') + original_defer_fn = vim.defer_fn + + -- Mock vim.defer_fn to execute immediately in tests + vim.defer_fn = function(fn, delay) + fn() + end + stub(server_job, 'ensure_server').invokes(function() local p = Promise.new() p:resolve({ @@ -493,6 +501,7 @@ describe('opencode.core', function() end) after_each(function() + vim.defer_fn = original_defer_fn if server_job.ensure_server.revert then server_job.ensure_server:revert() end From 0f3b34c3ae2d2b4c941fc812d987a81c22a6de46 Mon Sep 17 00:00:00 2001 From: Francis Belanger Date: Tue, 17 Feb 2026 08:26:01 -0500 Subject: [PATCH 4/5] fix: auto-set cwd in API queries - Remove server restart logic on directory change - Set default directory to current working directory in API client queries - Change DirChangedPre to DirChanged autocmd for proper timing --- lua/opencode/api_client.lua | 4 ++ lua/opencode/core.lua | 20 +++------ lua/opencode/ui/autocmds.lua | 2 +- tests/unit/api_client_spec.lua | 7 ++- tests/unit/core_spec.lua | 78 ---------------------------------- 5 files changed, 18 insertions(+), 93 deletions(-) diff --git a/lua/opencode/api_client.lua b/lua/opencode/api_client.lua index f0362cc8..9da85172 100644 --- a/lua/opencode/api_client.lua +++ b/lua/opencode/api_client.lua @@ -62,6 +62,10 @@ function OpencodeApiClient:_call(endpoint, method, body, query) local url = self.base_url .. endpoint if query then + if not query.directory then + query.directory = vim.fn.getcwd() + end + local params = {} for k, v in pairs(query) do diff --git a/lua/opencode/core.lua b/lua/opencode/core.lua index ef53361a..e63799e1 100644 --- a/lua/opencode/core.lua +++ b/lua/opencode/core.lua @@ -548,18 +548,17 @@ end --- @return Promise M.handle_directory_change = Promise.async(function() local log = require('opencode.log') + if not state.active_session then + is_new = true + state.active_session = M.create_new_session():await() + end if state.opencode_server then - vim.notify('Directory changed, restarting Opencode server...', vim.log.levels.INFO) - log.info('Shutting down Opencode server due to directory change...') - - state.opencode_server:shutdown():await() + vim.notify('Working directory changed.', vim.log.levels.INFO) + log.debug('Working directory change %s', vim.inspect({ cwd = vim.fn.getcwd() })) vim.defer_fn( Promise.async(function() - state.opencode_server = nil - server_job.ensure_server():await() - vim.notify('Loading last session for new working dir [' .. vim.fn.getcwd() .. ']', vim.log.levels.INFO) state.active_session = nil @@ -567,12 +566,7 @@ M.handle_directory_change = Promise.async(function() context.unload_attachments() local is_new = false - state.active_session = session.get_last_workspace_session():await() - - if not state.active_session then - is_new = true - state.active_session = M.create_new_session():await() - end + state.active_session = session.get_last_workspace_session():await() or M.create_new_session():await() log.debug( 'Loaded session for new working dir' .. vim.inspect({ session = state.active_session, is_new = is_new }) diff --git a/lua/opencode/ui/autocmds.lua b/lua/opencode/ui/autocmds.lua index 9db3996a..e90d5408 100644 --- a/lua/opencode/ui/autocmds.lua +++ b/lua/opencode/ui/autocmds.lua @@ -49,7 +49,7 @@ function M.setup_autocmds(windows) end, }) - vim.api.nvim_create_autocmd('DirChangedPre', { + vim.api.nvim_create_autocmd('DirChanged', { group = group, callback = function(event) local core = require('opencode.core') diff --git a/tests/unit/api_client_spec.lua b/tests/unit/api_client_spec.lua index ce7445d1..ff214c98 100644 --- a/tests/unit/api_client_spec.lua +++ b/tests/unit/api_client_spec.lua @@ -62,6 +62,10 @@ describe('api_client', function() local server_job = require('opencode.server_job') local original_call_api = server_job.call_api local captured_calls = {} + local original_cwd = vim.fn.getcwd + vim.fn.getcwd = function() + return '/current/directory' + end server_job.call_api = function(url, method, body) table.insert(captured_calls, { url = url, method = method, body = body }) @@ -74,7 +78,7 @@ describe('api_client', function() -- Test without query params client:list_projects() - assert.are.equal('http://localhost:8080/project', captured_calls[1].url) + assert.are.equal('http://localhost:8080/project?directory=/current/directory', captured_calls[1].url) assert.are.equal('GET', captured_calls[1].method) -- Test with query params @@ -95,5 +99,6 @@ describe('api_client', function() -- Restore original function server_job.call_api = original_call_api + vim.fn.getcwd = original_cwd end) end) diff --git a/tests/unit/core_spec.lua b/tests/unit/core_spec.lua index f15f9bd2..b8a2efb4 100644 --- a/tests/unit/core_spec.lua +++ b/tests/unit/core_spec.lua @@ -484,72 +484,14 @@ describe('opencode.core', function() fn() end - stub(server_job, 'ensure_server').invokes(function() - local p = Promise.new() - p:resolve({ - is_running = function() - return true - end, - shutdown = function() - return Promise.new():resolve() - end, - url = 'http://127.0.0.1:4000', - }) - return p - end) stub(context, 'unload_attachments') end) after_each(function() vim.defer_fn = original_defer_fn - if server_job.ensure_server.revert then - server_job.ensure_server:revert() - end - if context.unload_attachments.revert then - context.unload_attachments:revert() - end - end) - - it('does nothing when no server is running', function() - state.opencode_server = nil - state.active_session = { id = 'sess1' } - - core.handle_directory_change():wait() - - assert.is_nil(state.opencode_server) - assert.equal('sess1', state.active_session.id) - assert.stub(server_job.ensure_server).was_not_called() - end) - - it('shuts down existing server and starts new one', function() - local shutdown_called = false - state.opencode_server = { - is_running = function() - return true - end, - shutdown = function() - shutdown_called = true - return Promise.new():resolve() - end, - url = 'http://127.0.0.1:4000', - } - - core.handle_directory_change():wait() - - assert.is_true(shutdown_called) - assert.stub(server_job.ensure_server).was_called() end) it('clears active session and context', function() - state.opencode_server = { - is_running = function() - return true - end, - shutdown = function() - return Promise.new():resolve() - end, - url = 'http://127.0.0.1:4000', - } state.active_session = { id = 'old-session' } state.last_sent_context = { some = 'context' } @@ -563,16 +505,6 @@ describe('opencode.core', function() end) it('loads last workspace session for new directory', function() - state.opencode_server = { - is_running = function() - return true - end, - shutdown = function() - return Promise.new():resolve() - end, - url = 'http://127.0.0.1:4000', - } - core.handle_directory_change():wait() assert.truthy(state.active_session) @@ -581,16 +513,6 @@ describe('opencode.core', function() end) it('creates new session when no last session exists', function() - state.opencode_server = { - is_running = function() - return true - end, - shutdown = function() - return Promise.new():resolve() - end, - url = 'http://127.0.0.1:4000', - } - -- Override stub to return nil (no last session) session.get_last_workspace_session:revert() stub(session, 'get_last_workspace_session').invokes(function() From 5f81a87107114453131dfef91420b1c742b5adad Mon Sep 17 00:00:00 2001 From: Francis Belanger Date: Wed, 18 Feb 2026 08:12:35 -0500 Subject: [PATCH 5/5] feat(core): ensure cwd is correct when opening the panel --- lua/opencode/api_client.lua | 3 +- lua/opencode/core.lua | 79 ++++++++++++++++------------------ lua/opencode/server_job.lua | 3 ++ lua/opencode/state.lua | 2 + lua/opencode/ui/autocmds.lua | 3 +- tests/unit/api_client_spec.lua | 3 ++ tests/unit/core_spec.lua | 40 +++++++++++++---- 7 files changed, 81 insertions(+), 52 deletions(-) diff --git a/lua/opencode/api_client.lua b/lua/opencode/api_client.lua index 9da85172..b2b0862e 100644 --- a/lua/opencode/api_client.lua +++ b/lua/opencode/api_client.lua @@ -1,4 +1,5 @@ local server_job = require('opencode.server_job') +local state = require('opencode.state') --- @class OpencodeApiClient --- @field base_url string The base URL of the opencode server @@ -63,7 +64,7 @@ function OpencodeApiClient:_call(endpoint, method, body, query) if query then if not query.directory then - query.directory = vim.fn.getcwd() + query.directory = state.current_cwd or vim.fn.getcwd() end local params = {} diff --git a/lua/opencode/core.lua b/lua/opencode/core.lua index e63799e1..30eac5b5 100644 --- a/lua/opencode/core.lua +++ b/lua/opencode/core.lua @@ -10,6 +10,7 @@ local config = require('opencode.config') local image_handler = require('opencode.image_handler') local Promise = require('opencode.promise') local permission_window = require('opencode.ui.permission_window') +local log = require('opencode.log') local M = {} M._abort_count = 0 @@ -57,6 +58,27 @@ M.open_if_closed = Promise.async(function(opts) end end) +M.is_prompting_allowed = function() + local mentioned_files = context.get_context().mentioned_files or {} + local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files) + if not allowed then + vim.notify(err_msg or 'Prompt denied by prompt_guard', vim.log.levels.ERROR) + end + return allowed +end + +M.check_cwd = function() + if state.current_cwd ~= vim.fn.getcwd() then + log.debug( + 'CWD changed since last check, resetting session and context', + { current_cwd = state.current_cwd, new_cwd = vim.fn.getcwd() } + ) + state.current_cwd = vim.fn.getcwd() + state.active_session = nil + context.unload_attachments() + end +end + ---@param opts? OpenOpts M.open = Promise.async(function(opts) opts = opts or { focus = 'input', new_session = false } @@ -69,13 +91,7 @@ M.open = Promise.async(function(opts) local are_windows_closed = state.windows == nil if are_windows_closed then - -- Check if whether prompting will be allowed - local mentioned_files = context.get_context().mentioned_files or {} - local allowed, err_msg = util.check_prompt_allowed(config.prompt_guard, mentioned_files) - if not allowed then - vim.notify(err_msg or 'Prompts will be denied by prompt_guard', vim.log.levels.WARN) - end - + M.is_prompting_allowed() state.windows = ui.create_windows() end @@ -85,22 +101,16 @@ M.open = Promise.async(function(opts) ui.focus_output({ restore_position = are_windows_closed }) end - local server - local server_ok, server_err = pcall(function() - server = server_job.ensure_server():await() - end) + local server = server_job.ensure_server():await() - if not server_ok or not server then + if not server then state.is_opening = false - vim.notify('Failed to start opencode server: ' .. tostring(server_err or 'Unknown error'), vim.log.levels.ERROR) - return Promise.new():reject(server_err or 'Server failed to start') + return Promise.new():reject('Server failed to start') end - state.opencode_server = server + M.check_cwd() local ok, err = pcall(function() - state.opencode_server = server - if opts.new_session then state.active_session = nil state.last_sent_context = nil @@ -109,6 +119,7 @@ M.open = Promise.async(function(opts) M.ensure_current_mode():await() state.active_session = M.create_new_session():await() + log.debug('Created new session on open', { session = state.active_session.id }) else M.ensure_current_mode():await() if not state.active_session then @@ -543,38 +554,22 @@ function M.paste_image_from_clipboard() return image_handler.paste_image_from_clipboard() end ---- Handle working directory changes by restarting the server and loading the appropriate session. ---- This function performs the following steps: +--- Handle working directory changes loading the appropriate session. --- @return Promise M.handle_directory_change = Promise.async(function() local log = require('opencode.log') - if not state.active_session then - is_new = true - state.active_session = M.create_new_session():await() - end - - if state.opencode_server then - vim.notify('Working directory changed.', vim.log.levels.INFO) - log.debug('Working directory change %s', vim.inspect({ cwd = vim.fn.getcwd() })) - vim.defer_fn( - Promise.async(function() - vim.notify('Loading last session for new working dir [' .. vim.fn.getcwd() .. ']', vim.log.levels.INFO) + local cwd = vim.fn.getcwd() + log.debug('Working directory change %s', vim.inspect({ cwd = cwd })) + vim.notify('Loading last session for new working dir [' .. cwd .. ']', vim.log.levels.INFO) - state.active_session = nil - state.last_sent_context = nil - context.unload_attachments() + state.active_session = nil + state.last_sent_context = nil + context.unload_attachments() - local is_new = false - state.active_session = session.get_last_workspace_session():await() or M.create_new_session():await() + state.active_session = session.get_last_workspace_session():await() or M.create_new_session():await() - log.debug( - 'Loaded session for new working dir' .. vim.inspect({ session = state.active_session, is_new = is_new }) - ) - end), - 200 - ) - end + log.debug('Loaded session for new working dir ' .. vim.inspect({ session = state.active_session })) end) function M.setup() diff --git a/lua/opencode/server_job.lua b/lua/opencode/server_job.lua index 789ab19b..c571c83e 100644 --- a/lua/opencode/server_job.lua +++ b/lua/opencode/server_job.lua @@ -2,6 +2,7 @@ local state = require('opencode.state') local curl = require('opencode.curl') local Promise = require('opencode.promise') local opencode_server = require('opencode.opencode_server') +local log = require('opencode.log') local M = {} M.requests = {} @@ -146,6 +147,8 @@ function M.ensure_server() promise:resolve(state.opencode_server) end, on_error = function(err) + log.error('Error starting opencode server: ' .. vim.inspect(err)) + vim.notify('Failed to start opencode server', vim.log.levels.ERROR) promise:reject(err) end, on_exit = function(exit_opts) diff --git a/lua/opencode/state.lua b/lua/opencode/state.lua index 0f43666f..c8644db2 100644 --- a/lua/opencode/state.lua +++ b/lua/opencode/state.lua @@ -43,6 +43,7 @@ ---@field pre_zoom_width integer|nil ---@field required_version string ---@field opencode_cli_version string|nil +---@field current_cwd string|nil ---@field append fun( key:string, value:any) ---@field remove fun( key:string, idx:number) ---@field subscribe fun( key:string|nil, cb:fun(key:string, new_val:any, old_val:any)) @@ -97,6 +98,7 @@ local _state = { -- versions required_version = '0.6.3', opencode_cli_version = nil, + current_cwd = vim.fn.getcwd(), } -- Listener registry: { [key] = {cb1, cb2, ...}, ['*'] = {cb1, ...} } diff --git a/lua/opencode/ui/autocmds.lua b/lua/opencode/ui/autocmds.lua index e90d5408..9b3fef2d 100644 --- a/lua/opencode/ui/autocmds.lua +++ b/lua/opencode/ui/autocmds.lua @@ -1,4 +1,3 @@ -local Promise = require('opencode.promise') local input_window = require('opencode.ui.input_window') local output_window = require('opencode.ui.output_window') local M = {} @@ -52,6 +51,8 @@ function M.setup_autocmds(windows) vim.api.nvim_create_autocmd('DirChanged', { group = group, callback = function(event) + local state = require('opencode.state') + state.current_cwd = event.file local core = require('opencode.core') core.handle_directory_change() end, diff --git a/tests/unit/api_client_spec.lua b/tests/unit/api_client_spec.lua index ff214c98..130d1bc7 100644 --- a/tests/unit/api_client_spec.lua +++ b/tests/unit/api_client_spec.lua @@ -63,6 +63,9 @@ describe('api_client', function() local original_call_api = server_job.call_api local captured_calls = {} local original_cwd = vim.fn.getcwd + local state = require('opencode.state') + state.current_cwd = '/current/directory' + vim.fn.getcwd = function() return '/current/directory' end diff --git a/tests/unit/core_spec.lua b/tests/unit/core_spec.lua index b8a2efb4..623d48fb 100644 --- a/tests/unit/core_spec.lua +++ b/tests/unit/core_spec.lua @@ -152,6 +152,37 @@ describe('opencode.core', function() }, state.windows) end) + it('ensure the current cwd is correct when opening', function() + local cwd = vim.fn.getcwd() + state.current_cwd = nil + core.open({ new_session = false, focus = 'input' }):wait() + assert.equal(cwd, state.current_cwd) + end) + + it('reload the active_session if cwd has changed since last session', function() + local original_getcwd = vim.fn.getcwd + + state.windows = nil + state.active_session = { id = 'old-session' } + state.current_cwd = '/some/old/path' + vim.fn.getcwd = function() + return '/some/new/path' + end + session.get_last_workspace_session:revert() + stub(session, 'get_last_workspace_session').invokes(function() + local p = Promise.new() + p:resolve({ id = 'new_cwd-test-session' }) + return p + end) + + core.open({ new_session = false, focus = 'input' }):wait() + + assert.truthy(state.active_session) + assert.equal('new_cwd-test-session', state.active_session.id) + -- Restore original cwd function + vim.fn.getcwd = original_getcwd + end) + it('handles new session properly', function() state.windows = nil state.active_session = { id = 'old-session' } @@ -472,23 +503,16 @@ describe('opencode.core', function() describe('handle_directory_change', function() local server_job local context - local original_defer_fn before_each(function() server_job = require('opencode.server_job') context = require('opencode.context') - original_defer_fn = vim.defer_fn - - -- Mock vim.defer_fn to execute immediately in tests - vim.defer_fn = function(fn, delay) - fn() - end stub(context, 'unload_attachments') end) after_each(function() - vim.defer_fn = original_defer_fn + context.unload_attachments:revert() end) it('clears active session and context', function()