fix(headless): drop /sf prefix so typed commands route through extension dispatch

headless.ts was sending `/sf {subcommand} {args}` to the RPC session, but
commands are registered without the sf namespace (e.g. 'todo', 'autonomous').
_tryExecuteExtensionCommand parsed commandName='sf', found no match, and the
LLM handled the request instead of the typed backend.

Fix: send `/{subcommand} {args}` directly — matches what registerSFCommands
registers and what the TUI already uses.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Mikael Hugo 2026-05-12 15:55:46 +02:00
parent 2bb9cdbeef
commit 0426aafad2

View file

@ -855,6 +855,27 @@ async function runHeadlessOnce(
process.exit(exitCode);
}
// import-backlog: deterministic text→DB transform, no LLM or RPC child needed.
if (options.command === "import-backlog") {
const { runImportBacklog } = await import(
"./headless-import-backlog.js"
);
const filePath = options.commandArgs[0] ?? options.context;
if (!filePath) {
process.stderr.write(
"[headless] Error: import-backlog requires a file path as the first argument\n" +
" Usage: sf headless import-backlog <file.md>\n",
);
process.exit(1);
}
const exitCode = await runImportBacklog(
resolve(filePath),
process.cwd(),
{ json: options.json },
);
process.exit(exitCode);
}
// Resolve CLI path for the child process
const cliPath = process.env.SF_BIN_PATH || process.argv[1];
if (!cliPath) {
@ -1918,8 +1939,9 @@ async function runHeadlessOnce(
);
}
// Send the command
const command = `/sf ${options.command}${options.commandArgs.length > 0 ? " " + options.commandArgs.join(" ") : ""}`;
// Send the command — use bare /{subcommand} form so _tryExecuteExtensionCommand
// can look up the registered command directly without an "sf" namespace wrapper.
const command = `/${options.command}${options.commandArgs.length > 0 ? " " + options.commandArgs.join(" ") : ""}`;
try {
await waitForHeadlessExtensionCommands(client);
await client.prompt(command);
@ -1962,7 +1984,7 @@ async function runHeadlessOnce(
try {
await waitForHeadlessExtensionCommands(client);
await client.prompt("/sf autonomous");
await client.prompt("/autonomous");
} catch (err) {
process.stderr.write(
`[headless] Error: Failed to start autonomous mode: ${err instanceof Error ? err.message : String(err)}\n`,