Skip to content

Commit 46c8a15

Browse files
authored
Merge pull request #344 from rumpl/fix-exclude-patterns
Fix the exclude patterns
2 parents be62817 + f9a3463 commit 46c8a15

2 files changed

Lines changed: 149 additions & 8 deletions

File tree

pkg/tools/builtin/filesystem.go

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -926,14 +926,20 @@ func (t *FilesystemTool) handleSearchFiles(_ context.Context, toolCall tools.Too
926926
return nil // Skip disallowed paths
927927
}
928928

929-
// Check exclude patterns
929+
// Check exclude patterns against relative path from search root
930+
relPath, err := filepath.Rel(args.Path, path)
931+
if err != nil {
932+
return nil
933+
}
934+
930935
for _, exclude := range args.ExcludePatterns {
931-
if match(exclude, filepath.Base(path)) {
936+
if matchExcludePattern(exclude, relPath) {
937+
if d.IsDir() {
938+
return fs.SkipDir
939+
}
932940
return nil
933941
}
934942
}
935-
936-
// Case-insensitive match
937943
if match(pattern, filepath.Base(path)) {
938944
matches = append(matches, path)
939945
}
@@ -980,21 +986,34 @@ func (t *FilesystemTool) handleSearchFilesContent(_ context.Context, toolCall to
980986
var results []string
981987

982988
err := filepath.WalkDir(args.Path, func(path string, d fs.DirEntry, err error) error {
983-
if err != nil || d.IsDir() {
989+
if err != nil {
984990
return nil
985991
}
986992

987993
if err := t.isPathAllowed(path); err != nil {
988994
return nil
989995
}
990996

991-
// Check exclude patterns
997+
// Check exclude patterns against relative path from search root
998+
relPath, err := filepath.Rel(args.Path, path)
999+
if err != nil {
1000+
return nil
1001+
}
1002+
9921003
for _, exclude := range args.ExcludePatterns {
993-
if match(exclude, filepath.Base(path)) {
994-
return nil
1004+
if matchExcludePattern(exclude, relPath) {
1005+
if d.IsDir() {
1006+
return fs.SkipDir // Skip entire directory
1007+
}
1008+
return nil // Skip this file
9951009
}
9961010
}
9971011

1012+
// Only process files, not directories
1013+
if d.IsDir() {
1014+
return nil
1015+
}
1016+
9981017
content, err := os.ReadFile(path)
9991018
if err != nil {
10001019
return nil
@@ -1077,6 +1096,47 @@ func (t *FilesystemTool) Stop() error {
10771096
return nil
10781097
}
10791098

1099+
// matchExcludePattern checks if a path should be excluded based on the exclude pattern
1100+
// It supports glob patterns and directory wildcards like .git/*
1101+
func matchExcludePattern(pattern, relPath string) bool {
1102+
// Normalize path separators to forward slashes for consistent matching
1103+
normalizedPath := filepath.ToSlash(relPath)
1104+
normalizedPattern := filepath.ToSlash(pattern)
1105+
1106+
// Handle directory patterns ending with /*
1107+
if strings.HasSuffix(normalizedPattern, "/*") {
1108+
dirPattern := strings.TrimSuffix(normalizedPattern, "/*")
1109+
// Check if path starts with the directory pattern
1110+
if strings.HasPrefix(normalizedPath, dirPattern+"/") || normalizedPath == dirPattern {
1111+
return true
1112+
}
1113+
}
1114+
1115+
// Try glob pattern matching on the full relative path
1116+
matched, _ := filepath.Match(normalizedPattern, normalizedPath)
1117+
if matched {
1118+
return true
1119+
}
1120+
1121+
// Try glob pattern matching on just the base name for backwards compatibility
1122+
matched, _ = filepath.Match(normalizedPattern, filepath.Base(normalizedPath))
1123+
if matched {
1124+
return true
1125+
}
1126+
1127+
// Check if pattern matches any parent directory path
1128+
pathParts := strings.Split(normalizedPath, "/")
1129+
for i := range pathParts {
1130+
subPath := strings.Join(pathParts[:i+1], "/")
1131+
matched, _ := filepath.Match(normalizedPattern, subPath)
1132+
if matched {
1133+
return true
1134+
}
1135+
}
1136+
1137+
return false
1138+
}
1139+
10801140
func match(pattern, name string) bool {
10811141
matched, _ := filepath.Match(pattern, name)
10821142
if matched {

pkg/tools/builtin/filesystem_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,3 +841,84 @@ func TestFilesystemTool_FilterTools(t *testing.T) {
841841
require.Equal(t, "list_allowed_directories", tools[0].Function.Name)
842842
require.NotNil(t, tools[0].Handler)
843843
}
844+
845+
func TestMatchExcludePattern(t *testing.T) {
846+
tests := []struct {
847+
name string
848+
pattern string
849+
relPath string
850+
expected bool
851+
}{
852+
// Directory wildcard patterns
853+
{
854+
name: "matches directory with wildcard",
855+
pattern: ".git/*",
856+
relPath: ".git/config",
857+
expected: true,
858+
},
859+
{
860+
name: "matches directory itself with wildcard",
861+
pattern: ".git/*",
862+
relPath: ".git",
863+
expected: true,
864+
},
865+
{
866+
name: "matches nested file with directory wildcard",
867+
pattern: ".git/*",
868+
relPath: ".git/hooks/pre-commit",
869+
expected: true,
870+
},
871+
{
872+
name: "does not match different directory",
873+
pattern: ".git/*",
874+
relPath: "src/main.go",
875+
expected: false,
876+
},
877+
// Glob patterns on full path
878+
{
879+
name: "matches full path glob",
880+
pattern: "*.log",
881+
relPath: "debug.log",
882+
expected: true,
883+
},
884+
{
885+
name: "matches nested file glob",
886+
pattern: "*.log",
887+
relPath: "logs/debug.log",
888+
expected: true,
889+
},
890+
{
891+
name: "does not match different extension",
892+
pattern: "*.log",
893+
relPath: "main.go",
894+
expected: false,
895+
},
896+
// Base name matching for backwards compatibility
897+
{
898+
name: "matches base name glob",
899+
pattern: "*.tmp",
900+
relPath: "cache/temp.tmp",
901+
expected: true,
902+
},
903+
{
904+
name: "matches base name exact",
905+
pattern: "README.md",
906+
relPath: "docs/README.md",
907+
expected: true,
908+
},
909+
// Parent directory matching
910+
{
911+
name: "matches parent directory",
912+
pattern: "node_modules",
913+
relPath: "node_modules/package/file.js",
914+
expected: true,
915+
},
916+
}
917+
918+
for _, tc := range tests {
919+
t.Run(tc.name, func(t *testing.T) {
920+
result := matchExcludePattern(tc.pattern, tc.relPath)
921+
assert.Equal(t, tc.expected, result, "Pattern: %s, Path: %s, IsDir: %v", tc.pattern, tc.relPath)
922+
})
923+
}
924+
}

0 commit comments

Comments
 (0)