Skip to content

Commit c1b728a

Browse files
committed
feat(api): add /commands to show user commands
1 parent f6a2ed3 commit c1b728a

File tree

3 files changed

+93
-2
lines changed

3 files changed

+93
-2
lines changed

lua/opencode/api.lua

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ function M.select_agent()
395395
end
396396

397397
function M.switch_mode()
398-
local modes = require('opencode.config_file').get_opencode_agents()
398+
local modes = require('opencode.config_file').get_opencode_agents() --[[@as string[] ]]
399399

400400
local current_index = util.index_of(modes, state.current_mode)
401401

@@ -519,6 +519,33 @@ function M.mcp()
519519
ui.render_lines(msg)
520520
end
521521

522+
function M.commands_list()
523+
local info = require('opencode.config_file')
524+
local commands = info.get_user_commands()
525+
if not commands then
526+
vim.notify('No user commands found. Please check your opencode config file.', vim.log.levels.WARN)
527+
return
528+
end
529+
530+
state.display_route = '/commands'
531+
M.open_input()
532+
533+
local msg = M.with_header({
534+
'### Available User Commands',
535+
'',
536+
'| Name | Description |',
537+
'|------|-------------|',
538+
})
539+
540+
for name, def in pairs(commands) do
541+
local desc = def.description or ''
542+
table.insert(msg, string.format('| %s | %s |', name, desc))
543+
end
544+
545+
table.insert(msg, '')
546+
ui.render_lines(msg)
547+
end
548+
522549
--- Runs a user-defined command by name.
523550
--- @param name string The name of the user command to run.
524551
--- @param args? string[] Additional arguments to pass to the command.
@@ -1015,6 +1042,11 @@ M.commands = {
10151042
fn = M.mcp,
10161043
},
10171044

1045+
commands_list = {
1046+
desc = 'Show user-defined commands',
1047+
fn = M.commands_list,
1048+
},
1049+
10181050
permission = {
10191051
desc = 'Respond to permissions (accept/accept_all/deny)',
10201052
completions = { 'accept', 'accept_all', 'deny' },
@@ -1039,6 +1071,7 @@ M.slash_commands_map = {
10391071
['/agent'] = { fn = M.select_agent, desc = 'Select agent mode' },
10401072
['/agents_init'] = { fn = M.initialize, desc = 'Initialize AGENTS.md session' },
10411073
['/child-sessions'] = { fn = M.select_child_session, desc = 'Select child session' },
1074+
['/commands'] = { fn = M.commands_list, desc = 'Show user-defined commands' },
10421075
['/compact'] = { fn = M.compact_session, desc = 'Compact current session' },
10431076
['/mcp'] = { fn = M.mcp, desc = 'Show MCP server configuration' },
10441077
['/models'] = { fn = M.configure_provider, desc = 'Switch provider/model' },

lua/opencode/core.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ local function on_opencode_server()
277277
end
278278

279279
--- Switches the current mode to the specified agent.
280-
--- @param mode string The agent/mode to switch to
280+
--- @param mode string|nil The agent/mode to switch to
281281
--- @return boolean success Returns true if the mode was switched successfully, false otherwise
282282
function M.switch_to_mode(mode)
283283
if not mode or mode == '' then

tests/unit/api_spec.lua

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,4 +276,62 @@ describe('opencode.api', function()
276276
notify_stub:revert()
277277
end)
278278
end)
279+
280+
describe('/commands command', function()
281+
it('displays user commands when available', function()
282+
local config_file = require('opencode.config_file')
283+
local original_get_user_commands = config_file.get_user_commands
284+
285+
config_file.get_user_commands = function()
286+
return {
287+
['build'] = { description = 'Build the project' },
288+
['test'] = { description = 'Run tests' },
289+
['deploy'] = { description = 'Deploy to production' },
290+
}
291+
end
292+
293+
stub(ui, 'render_lines')
294+
stub(api, 'open_input')
295+
296+
api.commands_list()
297+
298+
assert.stub(api.open_input).was_called()
299+
assert.stub(ui.render_lines).was_called()
300+
301+
local render_args = ui.render_lines.calls[1].refs[1]
302+
local rendered_text = table.concat(render_args, '\n')
303+
304+
assert.truthy(rendered_text:match('Available User Commands'))
305+
assert.truthy(rendered_text:match('Description'))
306+
assert.truthy(rendered_text:match('build'))
307+
assert.truthy(rendered_text:match('Build the project'))
308+
assert.truthy(rendered_text:match('test'))
309+
assert.truthy(rendered_text:match('Run tests'))
310+
assert.truthy(rendered_text:match('deploy'))
311+
assert.truthy(rendered_text:match('Deploy to production'))
312+
313+
config_file.get_user_commands = original_get_user_commands
314+
end)
315+
316+
it('shows warning when no user commands exist', function()
317+
local config_file = require('opencode.config_file')
318+
local original_get_user_commands = config_file.get_user_commands
319+
320+
config_file.get_user_commands = function()
321+
return nil
322+
end
323+
324+
local notify_stub = stub(vim, 'notify')
325+
326+
api.commands_list()
327+
328+
assert.stub(notify_stub).was_called_with(
329+
'No user commands found. Please check your opencode config file.',
330+
vim.log.levels.WARN
331+
)
332+
333+
config_file.get_user_commands = original_get_user_commands
334+
notify_stub:revert()
335+
end)
336+
end)
279337
end)

0 commit comments

Comments
 (0)