diff --git a/packages/cli/src/config/config.test.ts b/packages/cli/src/config/config.test.ts index 47e9a4f03c..33299375ba 100644 --- a/packages/cli/src/config/config.test.ts +++ b/packages/cli/src/config/config.test.ts @@ -1971,3 +1971,55 @@ describe('loadCliConfig fileFiltering', () => { }, ); }); + +describe('parseArguments with positional prompt', () => { + const originalArgv = process.argv; + + afterEach(() => { + process.argv = originalArgv; + }); + + it('should throw an error when both a positional prompt and the --prompt flag are used', async () => { + process.argv = [ + 'node', + 'script.js', + 'positional', + 'prompt', + '--prompt', + 'test prompt', + ]; + + const mockExit = vi.spyOn(process, 'exit').mockImplementation(() => { + throw new Error('process.exit called'); + }); + + const mockConsoleError = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + await expect(parseArguments({} as Settings)).rejects.toThrow( + 'process.exit called', + ); + + expect(mockConsoleError).toHaveBeenCalledWith( + expect.stringContaining( + 'Cannot use both a positional prompt and the --prompt (-p) flag together', + ), + ); + + mockExit.mockRestore(); + mockConsoleError.mockRestore(); + }); + + it('should correctly parse a positional prompt', async () => { + process.argv = ['node', 'script.js', 'positional', 'prompt']; + const argv = await parseArguments({} as Settings); + expect(argv.promptWords).toEqual(['positional', 'prompt']); + }); + + it('should correctly parse a prompt from the --prompt flag', async () => { + process.argv = ['node', 'script.js', '--prompt', 'test prompt']; + const argv = await parseArguments({} as Settings); + expect(argv.prompt).toBe('test prompt'); + }); +}); diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index 8e047682d1..c127ebc98b 100755 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -80,6 +80,7 @@ export interface CliArgs { screenReader: boolean | undefined; useSmartEdit: boolean | undefined; sessionSummary: string | undefined; + promptWords: string[] | undefined; } export async function parseArguments(settings: Settings): Promise { @@ -89,7 +90,7 @@ export async function parseArguments(settings: Settings): Promise { .usage( 'Usage: gemini [options] [command]\n\nGemini CLI - Launch an interactive CLI, use -p/--prompt for non-interactive mode', ) - .command('$0', 'Launch Gemini CLI', (yargsInstance) => + .command('$0 [promptWords...]', 'Launch Gemini CLI', (yargsInstance) => yargsInstance .option('model', { alias: 'm', @@ -277,8 +278,18 @@ export async function parseArguments(settings: Settings): Promise { 'all-files', 'Use @ includes in the application instead. This flag will be removed in a future version.', ) + .deprecateOption( + 'prompt', + 'Use the positional prompt instead. This flag will be removed in a future version.', + ) .check((argv) => { - if (argv.prompt && argv['promptInteractive']) { + const promptWords = argv['promptWords'] as string[] | undefined; + if (argv['prompt'] && promptWords && promptWords.length > 0) { + throw new Error( + 'Cannot use both a positional prompt and the --prompt (-p) flag together', + ); + } + if (argv['prompt'] && argv['promptInteractive']) { throw new Error( 'Cannot use both --prompt (-p) and --prompt-interactive (-i) together', ); @@ -439,7 +450,8 @@ export async function loadCliConfig( ); let mcpServers = mergeMcpServers(settings, activeExtensions); - const question = argv.promptInteractive || argv.prompt || ''; + const question = + argv.promptInteractive || argv.prompt || (argv.promptWords || []).join(' '); // Determine approval mode with backward compatibility let approvalMode: ApprovalMode;