From 59bc2173d8304ad60d4d5fe608456fd0a39c9dc0 Mon Sep 17 00:00:00 2001 From: Andrew Thal <467872+athal7@users.noreply.github.com> Date: Sat, 7 Nov 2026 19:38:18 -0600 Subject: [PATCH] Accept an initial prompt as Freebuff's first argument freebuff "fix the flaky login test" fails with "command-argument value ... is invalid for argument 'command'", because the Freebuff branch declares its positional with choices(['login', 'doctor']). That check runs before the prompt is used, so the initialPrompt the TUI already accepts can never be set from argv, and a launcher has no way to start a session with its task except by typing into the TUI. Take bare words as the initial prompt the way the Codebuff branch already does, while keeping login and doctor as commands. --- cli/src/__tests__/cli-args.test.ts | 43 ++++++++++++++++++++++++++++++ cli/src/cli-args.ts | 22 ++++++++++++--- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/cli/src/__tests__/cli-args.test.ts b/cli/src/__tests__/cli-args.test.ts index c8c3db485b..e3829fd150 100644 --- a/cli/src/__tests__/cli-args.test.ts +++ b/cli/src/__tests__/cli-args.test.ts @@ -208,3 +208,46 @@ describe('Codebuff --trust-agents', () => { expect(result.initialPrompt).toBe('fix it') }) }) + +describe('Freebuff initial prompt', () => { + test('takes bare words as the initial prompt', () => { + const result = parseArgs({ + argv: ['node', 'freebuff', 'fix the flaky login test'], + isFreebuff: true, + version: '1.0.0', + }) + expect(result.initialPrompt).toBe('fix the flaky login test') + expect(result.command).toBeUndefined() + }) + + test('keeps login and doctor as commands, not prompts', () => { + for (const command of ['login', 'doctor']) { + const result = parseArgs({ + argv: ['node', 'freebuff', command], + isFreebuff: true, + version: '1.0.0', + }) + expect(result.command).toBe(command) + expect(result.initialPrompt).toBeNull() + } + }) + + test('joins a multi-word prompt that follows flags', () => { + const result = parseArgs({ + argv: ['node', 'freebuff', '--trust-agents', 'fix', 'it'], + isFreebuff: true, + version: '1.0.0', + }) + expect(result.trustAgents).toBe(true) + expect(result.initialPrompt).toBe('fix it') + }) + + test('leaves the codebuff prompt path alone', () => { + const result = parseArgs({ + argv: ['node', 'codebuff', 'fix', 'it'], + isFreebuff: false, + version: '1.0.0', + }) + expect(result.initialPrompt).toBe('fix it') + }) +}) diff --git a/cli/src/cli-args.ts b/cli/src/cli-args.ts index c338ac837a..959752fe0d 100644 --- a/cli/src/cli-args.ts +++ b/cli/src/cli-args.ts @@ -68,8 +68,12 @@ export function parseArgs({ "Load this repository's .agents files and mcp.json without asking (for CI)", ) .addArgument( - new Argument('[command]', 'Command to run: login, or doctor to check this computer for common problems').choices(['login', 'doctor']), + new Argument( + '[prompt...]', + 'Command to run (login, or doctor), or an initial prompt to send to the agent', + ), ) + .allowExcessArguments(true) .helpOption('-h, --help', 'Show this help message') } else { // Codebuff: full CLI with all options @@ -128,9 +132,21 @@ export function parseArgs({ if (options.plan) initialMode = 'PLAN' } + // Freebuff accepts a first task the way Codebuff does: bare words that are not + // one of its commands become the session's initial prompt, so a launcher can + // start the session with its task instead of typing it into the TUI. + const freebuffCommands: readonly string[] = ['login', 'doctor'] + const freebuffCommand = + isFreebuff && args[0] !== undefined && freebuffCommands.includes(args[0]) + ? args[0] + : undefined + return { - initialPrompt: !isFreebuff && args.length > 0 ? args.join(' ') : null, - command: args[0], + initialPrompt: + args.length > 0 && (!isFreebuff || freebuffCommand === undefined) + ? args.join(' ') + : null, + command: isFreebuff ? freebuffCommand : args[0], agent: options.agent, clearLogs: options.clearLogs || false, continue: Boolean(continueFlag),