diff --git a/src/cli.ts b/src/cli.ts index a7f7aab..1148cf1 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -297,5 +297,5 @@ function helpText() { ` lobster 'exec --json "echo [1,2,3]" | json'\n` + ` lobster run --mode tool 'exec --json "echo [1]" | approve --prompt "ok?"'\n\n` + `Commands:\n` + - ` exec, head, json, pick, table, where, approve, clawd.invoke, state.get, state.set, diff.last, workflows.list, workflows.run\n`; + ` exec, head, json, pick, table, where, approve, clawd.invoke, state.get, state.set, diff.last, commands.list, workflows.list, workflows.run\n`; } diff --git a/src/commands/commands_list.ts b/src/commands/commands_list.ts new file mode 100644 index 0000000..519e338 --- /dev/null +++ b/src/commands/commands_list.ts @@ -0,0 +1,40 @@ +export const commandsListCommand = { + name: 'commands.list', + help() { + return ( + `commands.list — list available Lobster pipeline commands\n\n` + + `Usage:\n` + + ` commands.list\n\n` + + `Notes:\n` + + ` - Intended for agents (e.g. Clawdbot) to discover available pipeline stages dynamically.\n` + + ` - Output includes the command name and a short description extracted from help().\n` + ); + }, + async run({ input, ctx }) { + // Drain input + for await (const _ of input) { + // no-op + } + + const names = ctx.registry.list(); + const output = names.map((name) => { + const cmd = ctx.registry.get(name); + const help = typeof cmd?.help === 'function' ? String(cmd.help()) : ''; + const firstLine = help.split('\n').find((l) => l.trim().length > 0) ?? ''; + + // Expected pattern: "name — description" but fall back to the line as-is. + const desc = firstLine.includes('—') ? firstLine.split('—').slice(1).join('—').trim() : firstLine.trim(); + + return { + name, + description: desc, + }; + }); + + return { + output: (async function* () { + for (const item of output) yield item; + })(), + }; + }, +}; diff --git a/src/commands/registry.ts b/src/commands/registry.ts index a2f3477..5afb11f 100644 --- a/src/commands/registry.ts +++ b/src/commands/registry.ts @@ -10,6 +10,7 @@ import { stateGetCommand, stateSetCommand } from "./stdlib/state.js"; import { diffLastCommand } from "./stdlib/diff_last.js"; import { workflowsListCommand } from "./workflows/workflows_list.js"; import { workflowsRunCommand } from "./workflows/workflows_run.js"; +import { commandsListCommand } from "./commands_list.js"; import { gogGmailSearchCommand } from "./stdlib/gog_gmail_search.js"; import { gogGmailSendCommand } from "./stdlib/gog_gmail_send.js"; import { emailTriageCommand } from "./stdlib/email_triage.js"; @@ -31,6 +32,7 @@ export function createDefaultRegistry() { diffLastCommand, workflowsListCommand, workflowsRunCommand, + commandsListCommand, gogGmailSearchCommand, gogGmailSendCommand, emailTriageCommand,