summaryrefslogtreecommitdiffhomepage
path: root/internal/llm/tools
diff options
context:
space:
mode:
Diffstat (limited to 'internal/llm/tools')
-rw-r--r--internal/llm/tools/grep.go91
1 files changed, 58 insertions, 33 deletions
diff --git a/internal/llm/tools/grep.go b/internal/llm/tools/grep.go
index 086a5e686..475370ffb 100644
--- a/internal/llm/tools/grep.go
+++ b/internal/llm/tools/grep.go
@@ -10,6 +10,7 @@ import (
"path/filepath"
"regexp"
"sort"
+ "strconv"
"strings"
"time"
@@ -24,8 +25,10 @@ type GrepParams struct {
}
type grepMatch struct {
- path string
- modTime time.Time
+ path string
+ modTime time.Time
+ lineNum int
+ lineText string
}
type GrepResponseMetadata struct {
@@ -147,13 +150,26 @@ func (g *grepTool) Run(ctx context.Context, call ToolCall) (ToolResponse, error)
if len(matches) == 0 {
output = "No files found"
} else {
- output = fmt.Sprintf("Found %d file%s\n%s",
- len(matches),
- pluralize(len(matches)),
- strings.Join(matches, "\n"))
+ output = fmt.Sprintf("Found %d matches\n", len(matches))
+
+ currentFile := ""
+ for _, match := range matches {
+ if currentFile != match.path {
+ if currentFile != "" {
+ output += "\n"
+ }
+ currentFile = match.path
+ output += fmt.Sprintf("%s:\n", match.path)
+ }
+ if match.lineNum > 0 {
+ output += fmt.Sprintf(" Line %d: %s\n", match.lineNum, match.lineText)
+ } else {
+ output += fmt.Sprintf(" %s\n", match.path)
+ }
+ }
if truncated {
- output += "\n\n(Results are truncated. Consider using a more specific path or pattern.)"
+ output += "\n(Results are truncated. Consider using a more specific path or pattern.)"
}
}
@@ -166,14 +182,7 @@ func (g *grepTool) Run(ctx context.Context, call ToolCall) (ToolResponse, error)
), nil
}
-func pluralize(count int) string {
- if count == 1 {
- return ""
- }
- return "s"
-}
-
-func searchFiles(pattern, rootPath, include string, limit int) ([]string, bool, error) {
+func searchFiles(pattern, rootPath, include string, limit int) ([]grepMatch, bool, error) {
matches, err := searchWithRipgrep(pattern, rootPath, include)
if err != nil {
matches, err = searchFilesWithRegex(pattern, rootPath, include)
@@ -191,12 +200,7 @@ func searchFiles(pattern, rootPath, include string, limit int) ([]string, bool,
matches = matches[:limit]
}
- results := make([]string, len(matches))
- for i, m := range matches {
- results[i] = m.path
- }
-
- return results, truncated, nil
+ return matches, truncated, nil
}
func searchWithRipgrep(pattern, path, include string) ([]grepMatch, error) {
@@ -205,7 +209,8 @@ func searchWithRipgrep(pattern, path, include string) ([]grepMatch, error) {
return nil, fmt.Errorf("ripgrep not found: %w", err)
}
- args := []string{"-l", pattern}
+ // Use -n to show line numbers and include the matched line
+ args := []string{"-n", pattern}
if include != "" {
args = append(args, "--glob", include)
}
@@ -228,14 +233,29 @@ func searchWithRipgrep(pattern, path, include string) ([]grepMatch, error) {
continue
}
- fileInfo, err := os.Stat(line)
+ // Parse ripgrep output format: file:line:content
+ parts := strings.SplitN(line, ":", 3)
+ if len(parts) < 3 {
+ continue
+ }
+
+ filePath := parts[0]
+ lineNum, err := strconv.Atoi(parts[1])
+ if err != nil {
+ continue
+ }
+ lineText := parts[2]
+
+ fileInfo, err := os.Stat(filePath)
if err != nil {
continue // Skip files we can't access
}
matches = append(matches, grepMatch{
- path: line,
- modTime: fileInfo.ModTime(),
+ path: filePath,
+ modTime: fileInfo.ModTime(),
+ lineNum: lineNum,
+ lineText: lineText,
})
}
@@ -276,15 +296,17 @@ func searchFilesWithRegex(pattern, rootPath, include string) ([]grepMatch, error
return nil
}
- match, err := fileContainsPattern(path, regex)
+ match, lineNum, lineText, err := fileContainsPattern(path, regex)
if err != nil {
return nil // Skip files we can't read
}
if match {
matches = append(matches, grepMatch{
- path: path,
- modTime: info.ModTime(),
+ path: path,
+ modTime: info.ModTime(),
+ lineNum: lineNum,
+ lineText: lineText,
})
if len(matches) >= 200 {
@@ -301,21 +323,24 @@ func searchFilesWithRegex(pattern, rootPath, include string) ([]grepMatch, error
return matches, nil
}
-func fileContainsPattern(filePath string, pattern *regexp.Regexp) (bool, error) {
+func fileContainsPattern(filePath string, pattern *regexp.Regexp) (bool, int, string, error) {
file, err := os.Open(filePath)
if err != nil {
- return false, err
+ return false, 0, "", err
}
defer file.Close()
scanner := bufio.NewScanner(file)
+ lineNum := 0
for scanner.Scan() {
- if pattern.MatchString(scanner.Text()) {
- return true, nil
+ lineNum++
+ line := scanner.Text()
+ if pattern.MatchString(line) {
+ return true, lineNum, line, nil
}
}
- return false, scanner.Err()
+ return false, 0, "", scanner.Err()
}
func globToRegex(glob string) string {