diff --git a/src/utils/claude-subcommand-detector.ts b/src/utils/claude-subcommand-detector.ts index 9f425e82..4ecdaf8d 100644 --- a/src/utils/claude-subcommand-detector.ts +++ b/src/utils/claude-subcommand-detector.ts @@ -119,9 +119,11 @@ const SUBCOMMAND_ALLOWED_SESSION_FLAGS: Record> = { * * Heuristic: * 1. Walk args until the `--` terminator or end. - * 2. Skip known value-taking flags together with their next token. - * 3. Skip unknown `--flag=value` forms and bare `--flag` / `-x` tokens. - * 4. The first remaining positional token is the candidate. + * 2. If `--print` is present before the first positional token, treat as + * prompt/headless session mode (never a subcommand launch). + * 3. Skip known value-taking flags together with their next token. + * 4. Skip unknown `--flag=value` forms and bare `--flag` / `-x` tokens. + * 5. The first remaining positional token is the candidate. * 5. Match against CLAUDE_SUBCOMMANDS. * * Anything after the candidate is irrelevant — once a subcommand is in play, @@ -142,6 +144,8 @@ export function getClaudeSubcommandName(args: readonly string[]): string | null const arg = args[i]; if (arg === '--') return null; + if (arg === '--print') return null; + if (arg.startsWith('-')) { if (VALUE_TAKING_FLAGS.has(arg)) { // Skip the next token as the flag's value (when present and not another flag). diff --git a/tests/unit/utils/claude-subcommand-detector.test.ts b/tests/unit/utils/claude-subcommand-detector.test.ts index ae6f0488..27492e0c 100644 --- a/tests/unit/utils/claude-subcommand-detector.test.ts +++ b/tests/unit/utils/claude-subcommand-detector.test.ts @@ -56,6 +56,12 @@ describe('isClaudeSubcommandInvocation', () => { expect(isClaudeSubcommandInvocation(['--name', 'auth'])).toBe(false); }); + + it('treats --print prompt mode as non-subcommand even with subcommand-like prompt text', () => { + expect(isClaudeSubcommandInvocation(['--print', 'agents'])).toBe(false); + expect(isClaudeSubcommandInvocation(['--print', 'doctor'])).toBe(false); + }); + it('handles --flag=value forms', () => { expect(isClaudeSubcommandInvocation(['--model=sonnet', 'agents'])).toBe(true); }); @@ -201,6 +207,13 @@ describe('subcommand passthrough — injectors short-circuit', () => { expect(appendBrowserToolArgs(['remote-control'])).toEqual(['remote-control']); }); + + it('injectors still inject in --print prompt mode with subcommand-like prompt text', () => { + const out = appendThirdPartyWebSearchToolArgs(['--print', 'agents']); + expect(out).toContain('--append-system-prompt'); + expect(out).toContain('--disallowedTools'); + }); + it('injectors still inject for non-subcommand interactive launches', () => { const out = appendThirdPartyWebSearchToolArgs(['fix the bug']); expect(out).toContain('--append-system-prompt');