diff --git a/README.md b/README.md index f51bf333be..c7862a1aa0 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,12 @@ freebuff Then describe what you want. Freebuff finds the relevant files, makes changes, and runs the checks that matter for your project. +You can also pass the first prompt on the command line: + +```bash +freebuff "add a dark mode toggle" +``` + ## Models Freebuff includes a curated model catalog. The regular picker currently offers: diff --git a/cli/src/__tests__/cli-args.test.ts b/cli/src/__tests__/cli-args.test.ts index c8c3db485b..a982eebc3b 100644 --- a/cli/src/__tests__/cli-args.test.ts +++ b/cli/src/__tests__/cli-args.test.ts @@ -179,6 +179,51 @@ describe('Freebuff CLI Argument Parsing', () => { expect(result.initialPrompt).toBeNull() }) + test('accepts an initial prompt and joins its words', () => { + const result = parseArgs({ + argv: ['node', 'freebuff', 'add', 'a', 'dark', 'mode', 'toggle'], + isFreebuff: true, + version: '1.0.0', + }) + + expect(result.initialPrompt).toBe('add a dark mode toggle') + expect(result.command).toBeUndefined() + expect(result.initialMode).toBe('LITE') + }) + + test('accepts an initial prompt alongside --cwd and --continue', () => { + // `--continue [id]` takes an optional value, so a prompt that follows it + // would be read as the conversation id (as in codebuff); keep it last. + const result = parseArgs({ + argv: [ + 'node', + 'freebuff', + '--cwd', + '/tmp', + 'fix the tests', + '--continue', + ], + isFreebuff: true, + version: '1.0.0', + }) + + expect(result.cwd).toBe('/tmp') + expect(result.continue).toBe(true) + expect(result.continueId).toBeNull() + expect(result.initialPrompt).toBe('fix the tests') + }) + + test('treats a prompt that starts with a codebuff command word as a prompt', () => { + const result = parseArgs({ + argv: ['node', 'freebuff', 'publish', 'the', 'release', 'notes'], + isFreebuff: true, + version: '1.0.0', + }) + + expect(result.command).toBeUndefined() + expect(result.initialPrompt).toBe('publish the release notes') + }) + test('parses --trust-agents and defaults it to false', () => { expect( parseArgs({ diff --git a/cli/src/cli-args.ts b/cli/src/cli-args.ts index 1ce73347cc..f69f3a097a 100644 --- a/cli/src/cli-args.ts +++ b/cli/src/cli-args.ts @@ -1,6 +1,6 @@ import { createRequire } from 'module' -import { Argument, Command } from 'commander' +import { Command } from 'commander' import { IS_FREEBUFF, type AgentMode } from './utils/constants' import { getCliEnv } from './utils/env' @@ -50,7 +50,10 @@ export function parseArgs({ const program = new Command() if (isFreebuff) { - // Freebuff: simplified CLI - no prompt args, no agent override, no clear-logs + // Freebuff: simplified CLI - no agent override, no clear-logs, no mode + // flags. The initial prompt is accepted like codebuff's so launchers can + // hand a task straight to the TUI; it is sent through the normal chat + // path, so session admission, the landing screen and ads are unchanged. program .name('freebuff') .description('Freebuff - Free AI coding assistant') @@ -67,10 +70,12 @@ export function parseArgs({ '--trust-agents', "Load this repository's .agents files and mcp.json without asking (for CI)", ) - .addArgument( - new Argument('[command]', 'Command to run').choices(['login']), + .addHelpText( + 'after', + '\nCommands:\n login Log in to your account', ) .helpOption('-h, --help', 'Show this help message') + .argument('[prompt...]', 'Initial prompt to send to the agent') } else { // Codebuff: full CLI with all options program @@ -115,6 +120,14 @@ export function parseArgs({ const options = program.opts() const args = program.args + // Freebuff has one command, `login`; any other first word is the start of + // a prompt. Codebuff keeps its wider command set (login, publish). + const command = isFreebuff + ? args[0] === 'login' + ? 'login' + : undefined + : args[0] + const continueFlag = options.continue // Determine initial mode from flags (last flag wins if multiple specified) @@ -129,8 +142,9 @@ export function parseArgs({ } return { - initialPrompt: !isFreebuff && args.length > 0 ? args.join(' ') : null, - command: args[0], + initialPrompt: + args.length > 0 && command !== 'login' ? args.join(' ') : null, + command, agent: options.agent, clearLogs: options.clearLogs || false, continue: Boolean(continueFlag),