@@ -187,6 +187,10 @@ var toolDefs = []ToolDef{
187187 "type" : "string" ,
188188 "description" : "Glob to filter which files to search (e.g. \" *.ts\" , \" *.{js,jsx}\" )." ,
189189 },
190+ "context_lines" : map [string ]interface {}{
191+ "type" : "number" ,
192+ "description" : "Number of context lines before and after each match (like grep -C). Default 0." ,
193+ },
190194 },
191195 "required" : []string {"pattern" },
192196 },
@@ -482,16 +486,30 @@ func toolWriteFile(args map[string]interface{}, workDir string) (string, bool) {
482486 return fmt .Sprintf ("Error creating directory: %v" , err ), false
483487 }
484488
489+ // Read old content for diff summary before overwriting
490+ var oldLineCount int
491+ if existed {
492+ if oldData , readErr := os .ReadFile (absPath ); readErr == nil {
493+ oldLineCount = strings .Count (string (oldData ), "\n " ) + 1
494+ }
495+ }
496+
485497 if err := os .WriteFile (absPath , []byte (content ), perm ); err != nil {
486498 return fmt .Sprintf ("Error: %v" , err ), false
487499 }
488500
489501 lines := strings .Count (content , "\n " ) + 1
490- action := "Created"
491502 if existed {
492- action = "Updated"
503+ lineDiff := lines - oldLineCount
504+ diffNote := ""
505+ if lineDiff > 0 {
506+ diffNote = fmt .Sprintf (", +%d" , lineDiff )
507+ } else if lineDiff < 0 {
508+ diffNote = fmt .Sprintf (", %d" , lineDiff )
509+ }
510+ return fmt .Sprintf ("Updated %s (%d→%d lines, %d bytes%s)" , relPath , oldLineCount , lines , len (content ), diffNote ), true
493511 }
494- return fmt .Sprintf ("%s %s (%d lines, %d bytes)" , action , relPath , lines , len (content )), true
512+ return fmt .Sprintf ("Created %s (%d lines, %d bytes)" , relPath , lines , len (content )), true
495513}
496514
497515// ── edit_file ────────────────────────────────────────────────
@@ -881,17 +899,24 @@ func toolSearchFiles(args map[string]interface{}, workDir string) (string, bool)
881899 dirPath = "."
882900 }
883901 include := getString (args , "include" )
902+ contextLines := 0
903+ if cl , ok := getFloat (args , "context_lines" ); ok && cl > 0 {
904+ contextLines = int (cl )
905+ if contextLines > 10 {
906+ contextLines = 10
907+ }
908+ }
884909
885910 fullPath , err := safePath (workDir , dirPath )
886911 if err != nil {
887912 return fmt .Sprintf ("Error: %v" , err ), false
888913 }
889914
890915 // Try ripgrep first, fall back to grep
891- output , err := searchWithRg (pattern , fullPath , include , workDir )
916+ output , err := searchWithRg (pattern , fullPath , include , workDir , contextLines )
892917 if err != nil {
893918 // rg not found — try grep
894- output , err = searchWithGrep (pattern , fullPath , include , workDir )
919+ output , err = searchWithGrep (pattern , fullPath , include , workDir , contextLines )
895920 if err != nil {
896921 return fmt .Sprintf ("Error: %v" , err ), false
897922 }
@@ -910,7 +935,7 @@ func toolSearchFiles(args map[string]interface{}, workDir string) (string, bool)
910935 return truncateOutput (fmt .Sprintf ("%d matches for %q in %s:\n \n %s" , len (lines ), pattern , dirPath , output )), true
911936}
912937
913- func searchWithRg (pattern , searchPath , include , workDir string ) (string , error ) {
938+ func searchWithRg (pattern , searchPath , include , workDir string , contextLines int ) (string , error ) {
914939 args := []string {
915940 "rg" ,
916941 "--line-number" ,
@@ -934,6 +959,10 @@ func searchWithRg(pattern, searchPath, include, workDir string) (string, error)
934959 args = append (args , "--glob" , include )
935960 }
936961
962+ if contextLines > 0 {
963+ args = append (args , fmt .Sprintf ("-C%d" , contextLines ))
964+ }
965+
937966 args = append (args , "--" , pattern , searchPath )
938967
939968 cmd := exec .Command (args [0 ], args [1 :]... )
@@ -963,13 +992,18 @@ func searchWithRg(pattern, searchPath, include, workDir string) (string, error)
963992 return strings .TrimRight (output , "\n " ), nil
964993}
965994
966- func searchWithGrep (pattern , searchPath , include , workDir string ) (string , error ) {
995+ func searchWithGrep (pattern , searchPath , include , workDir string , contextLines int ) (string , error ) {
967996 incFlag := "*"
968997 if include != "" {
969998 incFlag = include
970999 }
9711000
972- cmd := exec .Command ("grep" , "-rn" , "--include=" + incFlag , pattern , searchPath )
1001+ grepArgs := []string {"-rn" , "--include=" + incFlag }
1002+ if contextLines > 0 {
1003+ grepArgs = append (grepArgs , fmt .Sprintf ("-C%d" , contextLines ))
1004+ }
1005+ grepArgs = append (grepArgs , pattern , searchPath )
1006+ cmd := exec .Command ("grep" , grepArgs ... )
9731007 cmd .Dir = workDir
9741008 out , err := cmd .CombinedOutput ()
9751009
0 commit comments