Skip to content
This repository was archived by the owner on Apr 19, 2026. It is now read-only.

Commit 7f52cba

Browse files
jpleva91claude
andauthored
Fix issue #24: listFiles() returns paths relative to cwd, not the listed directory (#105)
* feat: brain-24-1775264868 * chore: remove debug outputs/ artifacts that break go build The outputs/ directory contained debug Go files with conflicting package declarations that caused CI build failures. Simplified .gitignore to exclude the entire outputs/ directory. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 54f996e commit 7f52cba

File tree

5 files changed

+177
-9
lines changed

5 files changed

+177
-9
lines changed

.gitignore

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@
66
dist/
77

88
# Agent outputs (generated at runtime)
9-
outputs/logs/*.log
10-
outputs/logs/*.md
11-
outputs/logs/*.jsonl
12-
outputs/reports/*.md
13-
outputs/reports/*.jsonl
9+
outputs/
1410

1511
# Environment
1612
.env

internal/tools/tools.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@ if err != nil {
198198
return nil
199199
}
200200
name := d.Name()
201-
if name == "node_modules" || name == ".git" || strings.HasPrefix(name, ".") {
201+
// Skip hidden files/directories and special directories, but not the root directory
202+
if path != dir && (name == "node_modules" || name == ".git" || strings.HasPrefix(name, ".")) {
202203
if d.IsDir() {
203204
return filepath.SkipDir
204205
}
@@ -210,7 +211,11 @@ return fmt.Errorf("limit reached")
210211
if ext != "" && filepath.Ext(name) != ext {
211212
return nil
212213
}
213-
rel, _ := filepath.Rel(".", path)
214+
rel, _ := filepath.Rel(dir, path)
215+
// Skip the root directory itself (it will be ".")
216+
if rel == "." {
217+
return nil
218+
}
214219
if d.IsDir() {
215220
files = append(files, rel+"/")
216221
} else {

internal/tools/tools_test.go

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,3 +473,172 @@ func TestExecuteDirect_Grep(t *testing.T) {
473473
t.Fatalf("expected match, got: %s", r.Output)
474474
}
475475
}
476+
477+
// ── list_files tests ──
478+
479+
func TestListFiles_Basic(t *testing.T) {
480+
dir := t.TempDir()
481+
os.WriteFile(filepath.Join(dir, "a.txt"), []byte(""), 0o644)
482+
os.WriteFile(filepath.Join(dir, "b.go"), []byte(""), 0o644)
483+
sub := filepath.Join(dir, "sub")
484+
os.MkdirAll(sub, 0o755)
485+
os.WriteFile(filepath.Join(sub, "c.md"), []byte(""), 0o644)
486+
487+
// Save original working directory
488+
originalWd, _ := os.Getwd()
489+
defer os.Chdir(originalWd)
490+
491+
// Change to a different directory to test relative paths
492+
os.Chdir("/tmp")
493+
494+
r := listFiles(map[string]string{
495+
"directory": dir,
496+
}, 0)
497+
498+
if !r.Success {
499+
t.Fatalf("expected success, got error: %s", r.Error)
500+
}
501+
502+
// Check that paths are relative to 'dir', not to cwd
503+
lines := strings.Split(strings.TrimSpace(r.Output), "\n")
504+
expected := []string{"a.txt", "b.go", "sub/", "sub/c.md"}
505+
if len(lines) != len(expected) {
506+
t.Fatalf("expected %d lines, got %d: %s", len(expected), len(lines), r.Output)
507+
}
508+
509+
for i, line := range lines {
510+
if line != expected[i] {
511+
t.Errorf("line %d: got %q, want %q", i, line, expected[i])
512+
}
513+
}
514+
}
515+
516+
func TestListFiles_FromWithinDirectory(t *testing.T) {
517+
dir := t.TempDir()
518+
os.WriteFile(filepath.Join(dir, "test.txt"), []byte(""), 0o644)
519+
520+
// Save original working directory
521+
originalWd, _ := os.Getwd()
522+
defer os.Chdir(originalWd)
523+
524+
// Change to the test directory
525+
os.Chdir(dir)
526+
527+
r := listFiles(map[string]string{
528+
"directory": ".",
529+
}, 0)
530+
531+
if !r.Success {
532+
t.Fatalf("expected success, got error: %s", r.Error)
533+
}
534+
535+
// Should list files relative to current directory (.)
536+
lines := strings.Split(strings.TrimSpace(r.Output), "\n")
537+
if len(lines) != 1 || lines[0] != "test.txt" {
538+
t.Fatalf("expected ['test.txt'], got %v", lines)
539+
}
540+
}
541+
542+
func TestListFiles_ExtensionFilter(t *testing.T) {
543+
dir := t.TempDir()
544+
os.WriteFile(filepath.Join(dir, "a.txt"), []byte(""), 0o644)
545+
os.WriteFile(filepath.Join(dir, "b.go"), []byte(""), 0o644)
546+
os.WriteFile(filepath.Join(dir, "c.go"), []byte(""), 0o644)
547+
548+
r := listFiles(map[string]string{
549+
"directory": dir,
550+
"extension": ".go",
551+
}, 0)
552+
553+
if !r.Success {
554+
t.Fatalf("expected success, got error: %s", r.Error)
555+
}
556+
557+
lines := strings.Split(strings.TrimSpace(r.Output), "\n")
558+
if len(lines) != 2 {
559+
t.Fatalf("expected 2 .go files, got %d: %s", len(lines), r.Output)
560+
}
561+
for _, line := range lines {
562+
if !strings.HasSuffix(line, ".go") {
563+
t.Errorf("expected .go file, got %q", line)
564+
}
565+
}
566+
}
567+
568+
func TestListFiles_EmptyDirectory(t *testing.T) {
569+
dir := t.TempDir()
570+
571+
r := listFiles(map[string]string{
572+
"directory": dir,
573+
}, 0)
574+
575+
if !r.Success {
576+
t.Fatalf("expected success, got error: %s", r.Error)
577+
}
578+
if r.Output != "(empty directory)" {
579+
t.Fatalf("expected '(empty directory)', got %q", r.Output)
580+
}
581+
}
582+
583+
func TestListFiles_SkipsHiddenAndSpecialDirs(t *testing.T) {
584+
dir := t.TempDir()
585+
os.WriteFile(filepath.Join(dir, "normal.txt"), []byte(""), 0o644)
586+
os.WriteFile(filepath.Join(dir, ".hidden"), []byte(""), 0o644)
587+
gitDir := filepath.Join(dir, ".git")
588+
os.MkdirAll(gitDir, 0o755)
589+
os.WriteFile(filepath.Join(gitDir, "config"), []byte(""), 0o644)
590+
nodeModules := filepath.Join(dir, "node_modules")
591+
os.MkdirAll(nodeModules, 0o755)
592+
os.WriteFile(filepath.Join(nodeModules, "package.json"), []byte(""), 0o644)
593+
594+
r := listFiles(map[string]string{
595+
"directory": dir,
596+
}, 0)
597+
598+
if !r.Success {
599+
t.Fatalf("expected success, got error: %s", r.Error)
600+
}
601+
602+
// Should only show normal.txt
603+
lines := strings.Split(strings.TrimSpace(r.Output), "\n")
604+
if len(lines) != 1 || lines[0] != "normal.txt" {
605+
t.Fatalf("expected only 'normal.txt', got %v", lines)
606+
}
607+
}
608+
609+
func TestListFiles_SubdirectoryListing(t *testing.T) {
610+
dir := t.TempDir()
611+
sub := filepath.Join(dir, "subdir")
612+
os.MkdirAll(sub, 0o755)
613+
os.WriteFile(filepath.Join(sub, "file.txt"), []byte(""), 0o644)
614+
os.WriteFile(filepath.Join(sub, "another.md"), []byte(""), 0o644)
615+
616+
r := listFiles(map[string]string{
617+
"directory": sub,
618+
}, 0)
619+
620+
if !r.Success {
621+
t.Fatalf("expected success, got error: %s", r.Error)
622+
}
623+
624+
// Should list files relative to subdir
625+
lines := strings.Split(strings.TrimSpace(r.Output), "\n")
626+
expected := []string{"file.txt", "another.md"}
627+
if len(lines) != len(expected) {
628+
t.Fatalf("expected %d lines, got %d: %s", len(expected), len(lines), r.Output)
629+
}
630+
// Check that we have both files (order may vary)
631+
hasFileTxt := false
632+
hasAnotherMd := false
633+
for _, line := range lines {
634+
if line == "file.txt" {
635+
hasFileTxt = true
636+
}
637+
if line == "another.md" {
638+
hasAnotherMd = true
639+
}
640+
}
641+
if !hasFileTxt || !hasAnotherMd {
642+
t.Errorf("missing expected files: got %v", lines)
643+
}
644+
}

outputs/logs/.gitkeep

Lines changed: 0 additions & 1 deletion
This file was deleted.

outputs/reports/.gitkeep

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)