@@ -19,12 +19,34 @@ const TAIL_PATTERNS = [
1919 / ^ \s * t a i l \s + / ,
2020] ;
2121
22+ const GIT_VERBOSE_PATTERNS = [
23+ / ^ \s * g i t \s + l o g \b (? ! .* - - o n e l i n e ) (? ! .* - n \s * [ 1 - 9 ] ) / ,
24+ / ^ \s * g i t \s + d i f f \b (? ! .* - - s t a t ) (? ! .* - - n a m e - o n l y ) / ,
25+ / ^ \s * g i t \s + s h o w \b (? ! .* - - s t a t ) (? ! .* - - n a m e - o n l y ) / ,
26+ ] ;
27+
28+ const LS_TREE_PATTERNS = [
29+ / ^ \s * l s \s + - [ a - z ] * R / ,
30+ / ^ \s * l s \s + - [ a - z ] * l / ,
31+ / ^ \s * t r e e \b / ,
32+ ] ;
33+
34+ const WRITE_VIA_BASH_PATTERNS = [
35+ / < < \s * [ ' " ] ? E O F / i,
36+ / > \s * \S + \. ( j s | t s | p y | r b | g o | r s | j a v a | c s | c p p | c | h | s h | j s o n | y a m l | y m l | t o m l | m d ) / ,
37+ ] ;
38+
39+ const SED_AWK_EDIT_PATTERNS = [
40+ / ^ \s * s e d \s + - i / ,
41+ / ^ \s * a w k \s + .* > \s * \S + / ,
42+ ] ;
43+
2244const NON_CODE_EXTS = new Set ( [
23- 'md' , 'txt' , 'json' , 'yaml' , 'yml' , 'toml' , 'xml' , 'csv' ,
24- 'lock' , 'svg' , 'html' , 'css' , 'log' , 'env' ,
45+ 'md' , 'txt' , 'toml' , 'xml' , 'csv' , 'lock' , 'svg' , 'html' , 'css' , 'log' , 'env' ,
2546] ) ;
2647
27- const LARGE_FILE_BYTES = 12000 ; // ~300 lines of code
48+ const LARGE_FILE_BYTES = 12000 ; // ~300 lines of code
49+ const LARGE_JSON_BYTES = 50000 ; // 50KB JSON/YAML worth flagging
2850
2951function normalizeToolCall ( data = { } ) {
3052 const toolName = data . tool_name || data . tool || data . name || '' ;
@@ -45,11 +67,23 @@ export function evaluateToolCall(data = {}, options = {}) {
4567 if ( ! filePath ) return null ;
4668
4769 const ext = filePath . split ( '.' ) . pop ( ) . toLowerCase ( ) ;
48- if ( NON_CODE_EXTS . has ( ext ) ) return null ;
4970
5071 let size ;
5172 try { size = stat ( filePath ) . size ; } catch { return null ; }
5273
74+ if ( size > LARGE_JSON_BYTES && ( ext === 'json' || ext === 'yaml' || ext === 'yml' ) ) {
75+ return {
76+ id : 'read-large-json' ,
77+ severity : 'nudge' ,
78+ action : 'suggest' ,
79+ message : `[tl] ${ Math . round ( size / 1024 ) } KB ${ ext } — use tl-snippet to extract a specific path` ,
80+ alternative : 'tl snippet <key> <file>' ,
81+ toolName,
82+ } ;
83+ }
84+
85+ if ( NON_CODE_EXTS . has ( ext ) ) return null ;
86+
5387 if ( size > LARGE_FILE_BYTES ) {
5488 return {
5589 id : 'read-large' ,
@@ -111,6 +145,72 @@ export function evaluateToolCall(data = {}, options = {}) {
111145 } ;
112146 }
113147
148+ if ( / ^ \s * ( g r e p | r g | a g ) \s / . test ( cmd ) ) {
149+ return {
150+ id : 'bash-grep' ,
151+ severity : 'nudge' ,
152+ action : 'replace' ,
153+ message : '[tl] use Grep tool, not bash' ,
154+ alternative : 'Grep tool' ,
155+ toolName,
156+ } ;
157+ }
158+
159+ if ( / ^ \s * ( f i n d | f d ) \s / . test ( cmd ) ) {
160+ return {
161+ id : 'bash-find' ,
162+ severity : 'nudge' ,
163+ action : 'replace' ,
164+ message : '[tl] use Glob tool, not bash' ,
165+ alternative : 'Glob tool' ,
166+ toolName,
167+ } ;
168+ }
169+
170+ if ( LS_TREE_PATTERNS . some ( p => p . test ( cmd ) ) ) {
171+ return {
172+ id : 'bash-ls-tree' ,
173+ severity : 'nudge' ,
174+ action : 'replace' ,
175+ message : '[tl] use tl-structure or Glob for directory exploration' ,
176+ alternative : 'tl structure' ,
177+ toolName,
178+ } ;
179+ }
180+
181+ if ( GIT_VERBOSE_PATTERNS . some ( p => p . test ( cmd ) ) ) {
182+ return {
183+ id : 'bash-git-verbose' ,
184+ severity : 'nudge' ,
185+ action : 'replace' ,
186+ message : '[tl] use tl-diff for token-efficient git output' ,
187+ alternative : 'tl diff' ,
188+ toolName,
189+ } ;
190+ }
191+
192+ if ( WRITE_VIA_BASH_PATTERNS . some ( p => p . test ( cmd ) ) ) {
193+ return {
194+ id : 'bash-write' ,
195+ severity : 'nudge' ,
196+ action : 'replace' ,
197+ message : '[tl] use Write tool instead of writing via bash' ,
198+ alternative : 'Write tool' ,
199+ toolName,
200+ } ;
201+ }
202+
203+ if ( SED_AWK_EDIT_PATTERNS . some ( p => p . test ( cmd ) ) ) {
204+ return {
205+ id : 'bash-sed-awk' ,
206+ severity : 'nudge' ,
207+ action : 'replace' ,
208+ message : '[tl] use Edit tool for targeted edits' ,
209+ alternative : 'Edit tool' ,
210+ toolName,
211+ } ;
212+ }
213+
114214 if ( / ^ \s * c u r l \s / . test ( cmd ) && ! / ( - X \s | - - d a t a | - - h e a d e r .* a u t h | - d \s ) / i. test ( cmd ) ) {
115215 const url = cmd . match ( / h t t p s ? : \/ \/ \S + / ) ?. [ 0 ] ;
116216 return {
0 commit comments