From d0d70a3783721669d4c8535896fd959065f7640d Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Tue, 8 Jul 2025 15:03:30 -0400 Subject: [PATCH] wip --- internal/fsext/fileutil.go | 24 ++++++++++++++++++------ internal/llm/tools/glob.go | 11 +++++++---- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/internal/fsext/fileutil.go b/internal/fsext/fileutil.go index 2793c11116d1f3574b9ce051afd76e6d056c7030..ac5994c15bf970390693a828577749470237aff7 100644 --- a/internal/fsext/fileutil.go +++ b/internal/fsext/fileutil.go @@ -8,6 +8,7 @@ import ( "path/filepath" "sort" "strings" + "sync" "time" "github.com/bmatcuk/doublestar/v4" @@ -95,14 +96,14 @@ func NewFastGlobWalker(searchPath string) *FastGlobWalker { return walker } -func (w *FastGlobWalker) shouldSkip(path string) bool { +func shouldSkip(path, rootPath string, gitignore *ignore.GitIgnore) bool { if SkipHidden(path) { return true } - if w.gitignore != nil { - relPath, err := filepath.Rel(w.rootPath, path) - if err == nil && w.gitignore.MatchesPath(relPath) { + if gitignore != nil { + relPath, err := filepath.Rel(rootPath, path) + if err == nil && gitignore.MatchesPath(relPath) { return true } } @@ -111,6 +112,7 @@ func (w *FastGlobWalker) shouldSkip(path string) bool { } func GlobWithDoubleStar(pattern, searchPath string, limit int) ([]string, bool, error) { + var mu sync.Mutex walker := NewFastGlobWalker(searchPath) var matches []FileInfo conf := fastwalk.Config{ @@ -119,21 +121,28 @@ func GlobWithDoubleStar(pattern, searchPath string, limit int) ([]string, bool, ToSlash: fastwalk.DefaultToSlash(), Sort: fastwalk.SortFilesFirst, } + rootPath, gitignore := walker.rootPath, walker.gitignore err := fastwalk.Walk(&conf, searchPath, func(path string, d os.DirEntry, err error) error { if err != nil { return nil // Skip files we can't access } if d.IsDir() { - if walker.shouldSkip(path) { + mu.Lock() + if shouldSkip(path, rootPath, gitignore) { + mu.Unlock() return filepath.SkipDir } + mu.Unlock() return nil } - if walker.shouldSkip(path) { + mu.Lock() + if shouldSkip(path, rootPath, gitignore) { + mu.Unlock() return nil } + mu.Unlock() // Check if path matches the pattern relPath, err := filepath.Rel(searchPath, path) @@ -151,6 +160,9 @@ func GlobWithDoubleStar(pattern, searchPath string, limit int) ([]string, bool, return nil } + mu.Lock() + defer mu.Unlock() + matches = append(matches, FileInfo{Path: path, ModTime: info.ModTime()}) if limit > 0 && len(matches) >= limit*2 { return filepath.SkipAll diff --git a/internal/llm/tools/glob.go b/internal/llm/tools/glob.go index eaf3707d0d1cd67b29e477e9b28990544ddfae05..1c8027755495615fca8c5f005afe950b324df55a 100644 --- a/internal/llm/tools/glob.go +++ b/internal/llm/tools/glob.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "strings" + "sync" "github.com/charmbracelet/crush/internal/fsext" ) @@ -64,6 +65,7 @@ type GlobResponseMetadata struct { type globTool struct { workingDir string + mu sync.Mutex } func NewGlobTool(workingDir string) BaseTool { @@ -109,20 +111,21 @@ func (g *globTool) Run(ctx context.Context, call ToolCall) (ToolResponse, error) searchPath = g.workingDir } + g.mu.Lock() files, truncated, err := globFiles(params.Pattern, searchPath, 100) if err != nil { + g.mu.Unlock() return ToolResponse{}, fmt.Errorf("error finding files: %w", err) } - var output string - if len(files) == 0 { - output = "No files found" - } else { + output := "No files found" + if len(files) > 0 { output = strings.Join(files, "\n") if truncated { output += "\n\n(Results are truncated. Consider using a more specific path or pattern.)" } } + g.mu.Unlock() return WithResponseMetadata( NewTextResponse(output),