11use regex:: Regex ;
22
33use crate :: command_split;
4- use crate :: config:: { Config , Step } ;
4+ use crate :: config:: { BUILTIN_EXCLUDED_COMMANDS , Config , Step } ;
55use crate :: primitives:: { dedup, filter, group, match_output, prose, replace, truncate} ;
66
77pub struct CompressionResult {
@@ -33,6 +33,20 @@ impl CompressionResult {
3333 }
3434}
3535
36+ fn first_command_token ( segment : & str ) -> Option < & str > {
37+ segment
38+ . split_whitespace ( )
39+ . find ( |token| !token. contains ( '=' ) )
40+ }
41+
42+ fn command_basename ( token : & str ) -> & str {
43+ token
44+ . trim_matches ( |c| c == '\'' || c == '"' )
45+ . rsplit ( [ '/' , '\\' ] )
46+ . next ( )
47+ . unwrap_or ( token)
48+ }
49+
3650struct CompiledPipeline {
3751 name : String ,
3852 regex : Regex ,
@@ -82,26 +96,36 @@ impl Compressor {
8296 }
8397 }
8498
99+ pub fn command_is_excluded ( & self , command : & str ) -> bool {
100+ command_split:: split_compound ( command)
101+ . iter ( )
102+ . filter_map ( |segment| first_command_token ( segment) )
103+ . map ( command_basename)
104+ . any ( |name| BUILTIN_EXCLUDED_COMMANDS . contains ( & name) )
105+ || self . excluded . iter ( ) . any ( |r| r. is_match ( command) )
106+ }
107+
85108 pub fn compress ( & self , command : & str , output : & str ) -> CompressionResult {
86109 let original_chars = output. len ( ) ;
87110
88- // Skip if too short
89- if original_chars < self . min_length {
111+ // Built-in and configured exclusions are raw passthrough. Check before
112+ // length so excluded commands are always identified as `excluded`.
113+ if self . command_is_excluded ( command) {
90114 return CompressionResult {
91115 compressed : output. to_string ( ) ,
92116 original_chars,
93117 compressed_chars : original_chars,
94- strategy_name : "passthrough " . into ( ) ,
118+ strategy_name : "excluded " . into ( ) ,
95119 } ;
96120 }
97121
98- // Skip excluded commands
99- if self . excluded . iter ( ) . any ( |r| r . is_match ( command ) ) {
122+ // Skip if too short
123+ if original_chars < self . min_length {
100124 return CompressionResult {
101125 compressed : output. to_string ( ) ,
102126 original_chars,
103127 compressed_chars : original_chars,
104- strategy_name : "excluded " . into ( ) ,
128+ strategy_name : "passthrough " . into ( ) ,
105129 } ;
106130 }
107131
@@ -315,7 +339,7 @@ mod tests {
315339 }
316340
317341 #[ test]
318- fn test_git_status_pipeline ( ) {
342+ fn test_git_status_is_excluded ( ) {
319343 let compressor = Compressor :: new ( & test_config ( ) ) ;
320344 let mut lines = Vec :: new ( ) ;
321345 for i in 0 ..100 {
@@ -326,9 +350,64 @@ mod tests {
326350 }
327351 let output = lines. join ( "" ) ;
328352 let result = compressor. compress ( "git status" , & output) ;
329- assert_eq ! ( result. strategy_name, "git-status" ) ;
330- assert ! ( result. compressed. contains( "Modified" ) ) ;
331- assert ! ( result. compressed. contains( "Untracked" ) ) ;
353+ assert_eq ! ( result. strategy_name, "excluded" ) ;
354+ assert_eq ! ( result. compressed, output) ;
355+ assert_eq ! ( result. compressed_chars, result. original_chars) ;
356+ assert ! ( result. is_passthrough( ) ) ;
357+ assert ! ( !result. compressed. contains( "[gsqz:" ) ) ;
358+ }
359+
360+ #[ test]
361+ fn test_builtin_excluded_commands_are_raw_passthrough ( ) {
362+ let compressor = Compressor :: new ( & test_config ( ) ) ;
363+ let output = ( 0 ..120 )
364+ . map ( |i| format ! ( "structured output line {}\n " , i) )
365+ . collect :: < String > ( ) ;
366+
367+ for command in [
368+ "gobby status" ,
369+ "gobby-cli status" ,
370+ "gcode search symbol" ,
371+ "ghook --diagnose" ,
372+ "gloc prompt" ,
373+ "gsqz --stats -- cargo test" ,
374+ "git diff" ,
375+ ] {
376+ let result = compressor. compress ( command, & output) ;
377+ assert_eq ! ( result. strategy_name, "excluded" , "{command}" ) ;
378+ assert_eq ! ( result. compressed, output, "{command}" ) ;
379+ assert_eq ! ( result. compressed_chars, result. original_chars, "{command}" ) ;
380+ assert ! ( result. is_passthrough( ) , "{command}" ) ;
381+ }
382+ }
383+
384+ #[ test]
385+ fn test_builtin_exclusion_runs_before_min_length ( ) {
386+ let compressor = Compressor :: new ( & test_config ( ) ) ;
387+ let result = compressor. compress ( "gcode search symbol" , "ok" ) ;
388+ assert_eq ! ( result. strategy_name, "excluded" ) ;
389+ assert_eq ! ( result. compressed, "ok" ) ;
390+ }
391+
392+ #[ test]
393+ fn test_builtin_exclusion_matches_compound_segments ( ) {
394+ let compressor = Compressor :: new ( & test_config ( ) ) ;
395+ let output = ( 0 ..120 )
396+ . map ( |i| format ! ( "line {}\n " , i) )
397+ . collect :: < String > ( ) ;
398+
399+ let result = compressor. compress ( "cargo test && git status" , & output) ;
400+ assert_eq ! ( result. strategy_name, "excluded" ) ;
401+
402+ let result = compressor. compress ( "rg git README.md" , & output) ;
403+ assert_ne ! ( result. strategy_name, "excluded" ) ;
404+ }
405+
406+ #[ test]
407+ fn test_builtin_exclusion_matches_binary_paths ( ) {
408+ let compressor = Compressor :: new ( & test_config ( ) ) ;
409+ let result = compressor. compress ( "/Users/josh/.gobby/bin/gcode search" , "ok" ) ;
410+ assert_eq ! ( result. strategy_name, "excluded" ) ;
332411 }
333412
334413 #[ test]
@@ -507,10 +586,10 @@ mod tests {
507586 // Pure passthrough cases — main.rs surfaces output verbatim.
508587 assert ! ( mk( "passthrough" ) . is_passthrough( ) ) ;
509588 assert ! ( mk( "excluded" ) . is_passthrough( ) ) ;
510- assert ! ( mk( "git-mutation /no-op" ) . is_passthrough( ) ) ;
589+ assert ! ( mk( "pytest /no-op" ) . is_passthrough( ) ) ;
511590 assert ! ( mk( "cargo-test/no-op" ) . is_passthrough( ) ) ;
512591 // Real compression — main.rs prepends the header and reports to daemon.
513- assert ! ( !mk( "git-status " ) . is_passthrough( ) ) ;
592+ assert ! ( !mk( "pytest " ) . is_passthrough( ) ) ;
514593 assert ! ( !mk( "cargo-test/low-savings" ) . is_passthrough( ) ) ;
515594 assert ! ( !mk( "pytest/on_empty" ) . is_passthrough( ) ) ;
516595 assert ! ( !mk( "fallback" ) . is_passthrough( ) ) ;
@@ -556,10 +635,12 @@ mod tests {
556635 fn test_compound_falls_back_to_earlier_segment ( ) {
557636 let compressor = Compressor :: new ( & test_config ( ) ) ;
558637 // Last segment doesn't match any pipeline, first does
559- let output = ( 0 ..200 )
560- . map ( |i| format ! ( " M src/file_{}.rs\n " , i) )
561- . collect :: < String > ( ) ;
562- let result = compressor. compress ( "git status && unknown-cmd" , & output) ;
563- assert_eq ! ( result. strategy_name, "git-status" ) ;
638+ let mut lines: Vec < String > = ( 0 ..100 )
639+ . map ( |i| format ! ( "test test_{} ... ok\n " , i) )
640+ . collect ( ) ;
641+ lines. push ( "test result: ok. 100 passed; 0 failed\n " . into ( ) ) ;
642+ let output = lines. join ( "" ) ;
643+ let result = compressor. compress ( "cargo test && unknown-cmd" , & output) ;
644+ assert_eq ! ( result. strategy_name, "cargo-test" ) ;
564645 }
565646}
0 commit comments