11import test from 'ava' ;
2+ import { filterCliFlags } from '@/utils/cli-flags' ;
23
34// Test CLI argument parsing for non-interactive mode
45// These tests verify that the CLI correctly parses the 'run' command
@@ -13,47 +14,8 @@ function parsePrompt(args: string[]): string | undefined {
1314 if ( afterRunArgs . length === 0 ) {
1415 return undefined ;
1516 }
16- // Filter out known flags after 'run' when constructing the prompt
17- const promptArgs : string [ ] = [ ] ;
18- for ( let i = 0 ; i < afterRunArgs . length ; i ++ ) {
19- const arg = afterRunArgs [ i ] ;
20- if ( arg === '--vscode' ) {
21- continue ; // skip this flag
22- } else if ( arg === '--vscode-port' ) {
23- i ++ ; // skip this flag and its value
24- continue ;
25- } else if ( arg === '--provider' ) {
26- i ++ ; // skip this flag and its value
27- continue ;
28- } else if ( arg === '--model' ) {
29- i ++ ; // skip this flag and its value
30- continue ;
31- } else if ( arg === '--context-max' ) {
32- i ++ ; // skip this flag and its value
33- continue ;
34- } else if ( arg === '--mode' ) {
35- i ++ ; // skip this flag and its value
36- continue ;
37- } else if ( arg . startsWith ( '--mode=' ) ) {
38- continue ; // skip fused form
39- } else if ( arg === '--json' ) {
40- continue ; // skip this flag
41- } else if ( arg === '--output-format' ) {
42- i ++ ; // skip this flag and its value
43- continue ;
44- } else if ( arg . startsWith ( '--output-format=' ) ) {
45- continue ; // skip fused form
46- } else if ( arg === '--trust-directory' ) {
47- continue ; // skip this flag
48- } else if ( arg === '--plain' || arg === '--no-plain' ) {
49- continue ; // skip this flag
50- } else if ( arg === '--no-alt-screen' || arg === '--alt-screen' ) {
51- continue ; // skip this flag
52- } else {
53- promptArgs . push ( arg ) ;
54- }
55- }
56- return promptArgs . length > 0 ? promptArgs . join ( ' ' ) : undefined ;
17+ const positionals = filterCliFlags ( afterRunArgs ) ;
18+ return positionals . length > 0 ? positionals . join ( ' ' ) : undefined ;
5719}
5820
5921test ( 'CLI parsing: detects run command with single word prompt' , t => {
@@ -593,3 +555,120 @@ test('CLI parsing: handles --mode before run command', t => {
593555 const prompt = parsePrompt ( args ) ;
594556 t . is ( prompt , 'audit module' ) ;
595557} ) ;
558+
559+ // Review guard tests — mirrors the guards in cli.tsx
560+ function resolveReviewGuards ( opts : {
561+ args : string [ ] ;
562+ stdoutIsTTY : boolean ;
563+ outputFormat : string ;
564+ } ) : { ttyError : boolean ; jsonError : boolean ; collisionError : boolean } {
565+ const { args, stdoutIsTTY, outputFormat} = opts ;
566+ const isRunCommand = args . findIndex ( arg => arg === 'run' ) !== - 1 ;
567+ const isReviewCommand = args [ 0 ] === 'review' ;
568+ const ttyError = isReviewCommand && ! stdoutIsTTY ;
569+ const jsonError = isReviewCommand && outputFormat === 'json' ;
570+ const collisionError = isRunCommand && isReviewCommand ;
571+ return { ttyError, jsonError, collisionError} ;
572+ }
573+
574+ test ( 'review guard: errors when stdout is not a TTY' , t => {
575+ const { ttyError} = resolveReviewGuards ( {
576+ args : [ 'review' , 'main' ] ,
577+ stdoutIsTTY : false ,
578+ outputFormat : 'text' ,
579+ } ) ;
580+ t . true ( ttyError ) ;
581+ } ) ;
582+
583+ test ( 'review guard: passes on a TTY' , t => {
584+ const { ttyError} = resolveReviewGuards ( {
585+ args : [ 'review' , 'main' ] ,
586+ stdoutIsTTY : true ,
587+ outputFormat : 'text' ,
588+ } ) ;
589+ t . false ( ttyError ) ;
590+ } ) ;
591+
592+ test ( 'review guard: --json is rejected with review' , t => {
593+ const { jsonError} = resolveReviewGuards ( {
594+ args : [ 'review' , 'main' ] ,
595+ stdoutIsTTY : true ,
596+ outputFormat : 'json' ,
597+ } ) ;
598+ t . true ( jsonError ) ;
599+ } ) ;
600+
601+ test ( 'review guard: --json is not rejected with run' , t => {
602+ const { jsonError} = resolveReviewGuards ( {
603+ args : [ 'run' , 'hello' ] ,
604+ stdoutIsTTY : true ,
605+ outputFormat : 'json' ,
606+ } ) ;
607+ t . false ( jsonError ) ;
608+ } ) ;
609+
610+ test ( 'review guard: run and review collision is detected' , t => {
611+ const { collisionError} = resolveReviewGuards ( {
612+ args : [ 'review' , 'run' ] ,
613+ stdoutIsTTY : true ,
614+ outputFormat : 'text' ,
615+ } ) ;
616+ t . true ( collisionError ) ;
617+ } ) ;
618+
619+ test ( 'review guard: review alone has no collision' , t => {
620+ const { collisionError} = resolveReviewGuards ( {
621+ args : [ 'review' , 'main' ] ,
622+ stdoutIsTTY : true ,
623+ outputFormat : 'text' ,
624+ } ) ;
625+ t . false ( collisionError ) ;
626+ } ) ;
627+
628+ test ( 'review guard: run alone has no collision' , t => {
629+ const { collisionError} = resolveReviewGuards ( {
630+ args : [ 'run' , 'hello' ] ,
631+ stdoutIsTTY : true ,
632+ outputFormat : 'text' ,
633+ } ) ;
634+ t . false ( collisionError ) ;
635+ } ) ;
636+
637+ // filterCliFlags: shared flag filter produces single source of truth
638+ test ( 'filterCliFlags: filters all known flags' , t => {
639+ const result = filterCliFlags ( [
640+ '--vscode' ,
641+ '--json' ,
642+ '--trust-directory' ,
643+ '--plain' ,
644+ '--no-plain' ,
645+ '--no-alt-screen' ,
646+ '--alt-screen' ,
647+ '--vscode-port' ,
648+ '3000' ,
649+ '--provider' ,
650+ 'ollama' ,
651+ '--model' ,
652+ 'llama3' ,
653+ '--context-max' ,
654+ '128k' ,
655+ '--output-format' ,
656+ 'json' ,
657+ '--output-format=json' ,
658+ '--mode' ,
659+ 'plan' ,
660+ '--mode=plan' ,
661+ 'my-prompt' ,
662+ ] ) ;
663+ t . deepEqual ( result , [ 'my-prompt' ] ) ;
664+ } ) ;
665+
666+ test ( 'filterCliFlags: returns all args when no flags present' , t => {
667+ const result = filterCliFlags ( [ 'hello' , 'world' ] ) ;
668+ t . deepEqual ( result , [ 'hello' , 'world' ] ) ;
669+ } ) ;
670+
671+ test ( 'filterCliFlags: returns empty array for empty input' , t => {
672+ const result = filterCliFlags ( [ ] ) ;
673+ t . deepEqual ( result , [ ] ) ;
674+ } ) ;
0 commit comments