@@ -384,7 +384,7 @@ suite('RunInTerminalTool', () => {
384384 strictEqual ( customActions . length , 4 ) ;
385385
386386 ok ( ! isSeparator ( customActions [ 0 ] ) ) ;
387- strictEqual ( customActions [ 0 ] . label , 'Always Allow Command: npm' ) ;
387+ strictEqual ( customActions [ 0 ] . label , 'Always Allow Command: npm install, npm run build ' ) ;
388388 strictEqual ( customActions [ 0 ] . data . type , 'newRule' ) ;
389389
390390 ok ( ! isSeparator ( customActions [ 1 ] ) ) ;
@@ -475,6 +475,207 @@ suite('RunInTerminalTool', () => {
475475 strictEqual ( customActions [ 3 ] . data . type , 'configure' ) ;
476476 } ) ;
477477
478+ test ( 'should suggest subcommand for git commands' , async ( ) => {
479+ const result = await executeToolTest ( {
480+ command : 'git status' ,
481+ explanation : 'Check git status'
482+ } ) ;
483+
484+ assertConfirmationRequired ( result ) ;
485+ ok ( result ! . confirmationMessages ! . terminalCustomActions , 'Expected custom actions to be defined' ) ;
486+
487+ const customActions = result ! . confirmationMessages ! . terminalCustomActions ! ;
488+ strictEqual ( customActions . length , 4 ) ;
489+
490+ ok ( ! isSeparator ( customActions [ 0 ] ) ) ;
491+ strictEqual ( customActions [ 0 ] . label , 'Always Allow Command: git status' ) ;
492+ strictEqual ( customActions [ 0 ] . data . type , 'newRule' ) ;
493+ ok ( Array . isArray ( customActions [ 0 ] . data . rule ) , 'Expected rule to be an array' ) ;
494+ strictEqual ( ( customActions [ 0 ] . data . rule as any ) [ 0 ] . key , 'git status' ) ;
495+ } ) ;
496+
497+ test ( 'should suggest subcommand for npm commands' , async ( ) => {
498+ const result = await executeToolTest ( {
499+ command : 'npm test' ,
500+ explanation : 'Run npm tests'
501+ } ) ;
502+
503+ assertConfirmationRequired ( result ) ;
504+ ok ( result ! . confirmationMessages ! . terminalCustomActions , 'Expected custom actions to be defined' ) ;
505+
506+ const customActions = result ! . confirmationMessages ! . terminalCustomActions ! ;
507+ strictEqual ( customActions . length , 4 ) ;
508+
509+ ok ( ! isSeparator ( customActions [ 0 ] ) ) ;
510+ strictEqual ( customActions [ 0 ] . label , 'Always Allow Command: npm test' ) ;
511+ strictEqual ( customActions [ 0 ] . data . type , 'newRule' ) ;
512+ ok ( Array . isArray ( customActions [ 0 ] . data . rule ) , 'Expected rule to be an array' ) ;
513+ strictEqual ( ( customActions [ 0 ] . data . rule as any ) [ 0 ] . key , 'npm test' ) ;
514+ } ) ;
515+
516+ test ( 'should suggest 3-part subcommand for npm run commands' , async ( ) => {
517+ const result = await executeToolTest ( {
518+ command : 'npm run build' ,
519+ explanation : 'Run build script'
520+ } ) ;
521+
522+ assertConfirmationRequired ( result ) ;
523+ ok ( result ! . confirmationMessages ! . terminalCustomActions , 'Expected custom actions to be defined' ) ;
524+
525+ const customActions = result ! . confirmationMessages ! . terminalCustomActions ! ;
526+ strictEqual ( customActions . length , 4 ) ;
527+
528+ ok ( ! isSeparator ( customActions [ 0 ] ) ) ;
529+ strictEqual ( customActions [ 0 ] . label , 'Always Allow Command: npm run build' ) ;
530+ strictEqual ( customActions [ 0 ] . data . type , 'newRule' ) ;
531+ ok ( Array . isArray ( customActions [ 0 ] . data . rule ) , 'Expected rule to be an array' ) ;
532+ strictEqual ( ( customActions [ 0 ] . data . rule as any ) [ 0 ] . key , 'npm run build' ) ;
533+ } ) ;
534+
535+ test ( 'should suggest 3-part subcommand for yarn run commands' , async ( ) => {
536+ const result = await executeToolTest ( {
537+ command : 'yarn run test' ,
538+ explanation : 'Run test script'
539+ } ) ;
540+
541+ assertConfirmationRequired ( result ) ;
542+ ok ( result ! . confirmationMessages ! . terminalCustomActions , 'Expected custom actions to be defined' ) ;
543+
544+ const customActions = result ! . confirmationMessages ! . terminalCustomActions ! ;
545+ strictEqual ( customActions . length , 4 ) ;
546+
547+ ok ( ! isSeparator ( customActions [ 0 ] ) ) ;
548+ strictEqual ( customActions [ 0 ] . label , 'Always Allow Command: yarn run test' ) ;
549+ strictEqual ( customActions [ 0 ] . data . type , 'newRule' ) ;
550+ ok ( Array . isArray ( customActions [ 0 ] . data . rule ) , 'Expected rule to be an array' ) ;
551+ strictEqual ( ( customActions [ 0 ] . data . rule as any ) [ 0 ] . key , 'yarn run test' ) ;
552+ } ) ;
553+
554+ test ( 'should handle mixed npm run and other commands' , async ( ) => {
555+ const result = await executeToolTest ( {
556+ command : 'npm run build && git status' ,
557+ explanation : 'Build and check status'
558+ } ) ;
559+
560+ assertConfirmationRequired ( result ) ;
561+ ok ( result ! . confirmationMessages ! . terminalCustomActions , 'Expected custom actions to be defined' ) ;
562+
563+ const customActions = result ! . confirmationMessages ! . terminalCustomActions ! ;
564+ strictEqual ( customActions . length , 4 ) ;
565+
566+ ok ( ! isSeparator ( customActions [ 0 ] ) ) ;
567+ strictEqual ( customActions [ 0 ] . label , 'Always Allow Commands: npm run build, git status' ) ;
568+ strictEqual ( customActions [ 0 ] . data . type , 'newRule' ) ;
569+ ok ( Array . isArray ( customActions [ 0 ] . data . rule ) , 'Expected rule to be an array' ) ;
570+ const rules = customActions [ 0 ] . data . rule as any ;
571+ strictEqual ( rules . length , 2 ) ;
572+ strictEqual ( rules [ 0 ] . key , 'npm run build' ) ;
573+ strictEqual ( rules [ 1 ] . key , 'git status' ) ;
574+ } ) ;
575+
576+ test ( 'should suggest mixed subcommands and base commands' , async ( ) => {
577+ const result = await executeToolTest ( {
578+ command : 'git push && echo "done"' ,
579+ explanation : 'Push and print done'
580+ } ) ;
581+
582+ assertConfirmationRequired ( result ) ;
583+ ok ( result ! . confirmationMessages ! . terminalCustomActions , 'Expected custom actions to be defined' ) ;
584+
585+ const customActions = result ! . confirmationMessages ! . terminalCustomActions ! ;
586+ strictEqual ( customActions . length , 4 ) ;
587+
588+ ok ( ! isSeparator ( customActions [ 0 ] ) ) ;
589+ strictEqual ( customActions [ 0 ] . label , 'Always Allow Commands: git push, echo' ) ;
590+ strictEqual ( customActions [ 0 ] . data . type , 'newRule' ) ;
591+ ok ( Array . isArray ( customActions [ 0 ] . data . rule ) , 'Expected rule to be an array' ) ;
592+ const rules = customActions [ 0 ] . data . rule as any ;
593+ strictEqual ( rules . length , 2 ) ;
594+ strictEqual ( rules [ 0 ] . key , 'git push' ) ;
595+ strictEqual ( rules [ 1 ] . key , 'echo' ) ;
596+ } ) ;
597+
598+ test ( 'should suggest subcommands for multiple git commands' , async ( ) => {
599+ const result = await executeToolTest ( {
600+ command : 'git status && git log --oneline' ,
601+ explanation : 'Check status and log'
602+ } ) ;
603+
604+ assertConfirmationRequired ( result ) ;
605+ ok ( result ! . confirmationMessages ! . terminalCustomActions , 'Expected custom actions to be defined' ) ;
606+
607+ const customActions = result ! . confirmationMessages ! . terminalCustomActions ! ;
608+ strictEqual ( customActions . length , 4 ) ;
609+
610+ ok ( ! isSeparator ( customActions [ 0 ] ) ) ;
611+ strictEqual ( customActions [ 0 ] . label , 'Always Allow Commands: git status, git log' ) ;
612+ strictEqual ( customActions [ 0 ] . data . type , 'newRule' ) ;
613+ ok ( Array . isArray ( customActions [ 0 ] . data . rule ) , 'Expected rule to be an array' ) ;
614+ const rules = customActions [ 0 ] . data . rule as any ;
615+ strictEqual ( rules . length , 2 ) ;
616+ strictEqual ( rules [ 0 ] . key , 'git status' ) ;
617+ strictEqual ( rules [ 1 ] . key , 'git log' ) ;
618+ } ) ;
619+
620+ test ( 'should suggest base command for non-subcommand tools' , async ( ) => {
621+ const result = await executeToolTest ( {
622+ command : 'curl https://example.com' ,
623+ explanation : 'Download from example.com'
624+ } ) ;
625+
626+ assertConfirmationRequired ( result ) ;
627+ ok ( result ! . confirmationMessages ! . terminalCustomActions , 'Expected custom actions to be defined' ) ;
628+
629+ const customActions = result ! . confirmationMessages ! . terminalCustomActions ! ;
630+ strictEqual ( customActions . length , 4 ) ;
631+
632+ ok ( ! isSeparator ( customActions [ 0 ] ) ) ;
633+ strictEqual ( customActions [ 0 ] . label , 'Always Allow Command: curl' ) ;
634+ strictEqual ( customActions [ 0 ] . data . type , 'newRule' ) ;
635+ ok ( Array . isArray ( customActions [ 0 ] . data . rule ) , 'Expected rule to be an array' ) ;
636+ strictEqual ( ( customActions [ 0 ] . data . rule as any ) [ 0 ] . key , 'curl' ) ;
637+ } ) ;
638+
639+ test ( 'should handle single word commands from subcommand-aware tools' , async ( ) => {
640+ const result = await executeToolTest ( {
641+ command : 'git' ,
642+ explanation : 'Run git command'
643+ } ) ;
644+
645+ assertConfirmationRequired ( result ) ;
646+ ok ( result ! . confirmationMessages ! . terminalCustomActions , 'Expected custom actions to be defined' ) ;
647+
648+ const customActions = result ! . confirmationMessages ! . terminalCustomActions ! ;
649+ strictEqual ( customActions . length , 3 ) ; // No full command line suggestion for single word
650+
651+ ok ( ! isSeparator ( customActions [ 0 ] ) ) ;
652+ strictEqual ( customActions [ 0 ] . label , 'Always Allow Command: git' ) ;
653+ strictEqual ( customActions [ 0 ] . data . type , 'newRule' ) ;
654+ ok ( Array . isArray ( customActions [ 0 ] . data . rule ) , 'Expected rule to be an array' ) ;
655+ strictEqual ( ( customActions [ 0 ] . data . rule as any ) [ 0 ] . key , 'git' ) ;
656+ } ) ;
657+
658+ test ( 'should deduplicate identical subcommand suggestions' , async ( ) => {
659+ const result = await executeToolTest ( {
660+ command : 'npm test && npm test --verbose' ,
661+ explanation : 'Run tests twice'
662+ } ) ;
663+
664+ assertConfirmationRequired ( result ) ;
665+ ok ( result ! . confirmationMessages ! . terminalCustomActions , 'Expected custom actions to be defined' ) ;
666+
667+ const customActions = result ! . confirmationMessages ! . terminalCustomActions ! ;
668+ strictEqual ( customActions . length , 4 ) ;
669+
670+ ok ( ! isSeparator ( customActions [ 0 ] ) ) ;
671+ strictEqual ( customActions [ 0 ] . label , 'Always Allow Command: npm test' ) ;
672+ strictEqual ( customActions [ 0 ] . data . type , 'newRule' ) ;
673+ ok ( Array . isArray ( customActions [ 0 ] . data . rule ) , 'Expected rule to be an array' ) ;
674+ const rules = customActions [ 0 ] . data . rule as any ;
675+ strictEqual ( rules . length , 1 ) ; // Should be deduplicated
676+ strictEqual ( rules [ 0 ] . key , 'npm test' ) ;
677+ } ) ;
678+
478679 } ) ;
479680
480681 suite ( 'command re-writing' , ( ) => {
0 commit comments