Skip to content
Merged
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
8 changes: 4 additions & 4 deletions lua/opencode/api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -357,11 +357,11 @@ function M.initialize()
end

function M.agent_plan()
state.current_mode = 'plan'
require('opencode.core').switch_to_mode('plan')
end

function M.agent_build()
state.current_mode = 'build'
require('opencode.core').switch_to_mode('build')
end

function M.select_agent()
Expand All @@ -373,7 +373,7 @@ function M.select_agent()
return
end

state.current_mode = selection
require('opencode.core').switch_to_mode(selection)
end)
end

Expand All @@ -389,7 +389,7 @@ function M.switch_mode()
-- Calculate next index, wrapping around if necessary
local next_index = (current_index % #modes) + 1

state.current_mode = modes[next_index]
require('opencode.core').switch_to_mode(modes[next_index])
end

function M.with_header(lines, show_welcome)
Expand Down
16 changes: 13 additions & 3 deletions lua/opencode/config_file.lua
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,23 @@ function M.get_opencode_agents()
end
local agents = {}
for agent, opts in pairs(cfg.agent or {}) do
if opts.mode == 'primary' or opts.mode == 'all' then
-- Only include agents that are enabled and have the right mode
if opts.disable ~= true and (opts.mode == 'primary' or opts.mode == 'all') then
table.insert(agents, agent)
end
end
for _, mode in ipairs({ 'build', 'plan' }) do

-- Sort the agents before prepending the default agents
table.sort(agents)

-- Only add build/plan as fallbacks if they're not explicitly disabled in config
for _, mode in ipairs({ 'plan', 'build' }) do
if not vim.tbl_contains(agents, mode) then
table.insert(agents, mode)
local mode_config = cfg.agent and cfg.agent[mode]
-- Only add if not explicitly disabled or if no config exists (default behavior)
if mode_config == nil or (mode_config.disable ~= true) then
table.insert(agents, 1, mode)
end
end
end
return agents
Expand Down
52 changes: 52 additions & 0 deletions lua/opencode/core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ function M.open(opts)
state.opencode_server = server_job.ensure_server() --[[@as OpencodeServer]]
end

M.ensure_current_mode()

local are_windows_closed = state.windows == nil

if are_windows_closed then
Expand Down Expand Up @@ -254,6 +256,56 @@ local function on_opencode_server()
state.current_permission = nil
end

--- Switches the current mode to the specified agent.
--- @param mode string The agent/mode to switch to
--- @return boolean success Returns true if the mode was switched successfully, false otherwise
function M.switch_to_mode(mode)
if not mode or mode == '' then
vim.notify('Mode cannot be empty', vim.log.levels.ERROR)
return false
end

local config_file = require('opencode.config_file')
local available_agents = config_file.get_opencode_agents()

if not vim.tbl_contains(available_agents, mode) then
vim.notify(
string.format('Invalid mode "%s". Available modes: %s', mode, table.concat(available_agents, ', ')),
vim.log.levels.ERROR
)
return false
end

state.current_mode = mode
ui.render_output()
return true
end

--- Ensure the current_mode is set using the config.default_mode or falling back to the first available agent.
--- @return boolean success Returns true if current_mode is set
function M.ensure_current_mode()
if state.current_mode == nil then
local config_file = require('opencode.config_file')
local available_agents = config_file.get_opencode_agents()

if not available_agents or #available_agents == 0 then
vim.notify('No available agents found', vim.log.levels.ERROR)
return false
end

local default_mode = config.default_mode

-- Try to use the configured default mode if it's available
if default_mode and vim.tbl_contains(available_agents, default_mode) then
state.current_mode = default_mode
else
-- Fallback to first available agent
state.current_mode = available_agents[1]
end
end
return true
end

function M.setup()
state.subscribe('opencode_server', on_opencode_server)

Expand Down
2 changes: 1 addition & 1 deletion lua/opencode/state.lua
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ local _state = {
last_output_window_position = nil,
last_code_win_before_opencode = nil,
display_route = nil,
current_mode = config.default_mode,
current_mode = nil,
last_output = 0,
-- context
last_sent_context = nil,
Expand Down
4 changes: 2 additions & 2 deletions lua/opencode/ui/topbar.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ local function format_model_info()
end

local function format_mode_info()
return ' ' .. state.current_mode:upper() .. ' '
return ' ' .. (state.current_mode or ''):upper() .. ' '
end

local function get_mode_highlight()
local mode = state.current_mode:lower()
local mode = (state.current_mode or ''):lower()
if mode == 'build' then
return '%#OpencodeAgentBuild#'
elseif mode == 'plan' then
Expand Down
1 change: 1 addition & 0 deletions tests/helpers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ function M.replay_setup()
return nil
end

state.current_mode = 'build' -- default mode for tests
state.windows = ui.create_windows()

-- we use the event manager to dispatch events
Expand Down
15 changes: 15 additions & 0 deletions tests/unit/config_file_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@ describe('config_file.setup', function()
assert.True(vim.tbl_contains(agents, 'plan'))
end)

it('get_opencode_agents respects disabled defaults', function()
state.api_client = {
get_config = function()
return Promise.new():resolve({ agent = { ['custom'] = { mode = 'primary' }, ['build'] = { disable = true }, ['plan'] = { disable = false } } })
end,
get_current_project = function()
return Promise.new():resolve({ id = 'p1' })
end,
}
local agents = config_file.get_opencode_agents()
assert.True(vim.tbl_contains(agents, 'custom'))
assert.False(vim.tbl_contains(agents, 'build'))
assert.True(vim.tbl_contains(agents, 'plan'))
end)

it('get_opencode_project returns project', function()
local project = { id = 'p1', name = 'X' }
state.api_client = {
Expand Down
3 changes: 3 additions & 0 deletions tests/unit/core_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ local function mock_api_client()
get_current_project = function()
return Promise.new():resolve({ id = 'test-project-id' })
end,
get_config = function()
return Promise.new():resolve({ model = 'gpt-4' })
end,
}
end

Expand Down