diff --git a/.gitignore b/.gitignore index 0514c63..38befa5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,11 +6,7 @@ dist/ # Agent outputs (generated at runtime) -outputs/logs/*.log -outputs/logs/*.md -outputs/logs/*.jsonl -outputs/reports/*.md -outputs/reports/*.jsonl +outputs/ # Environment .env diff --git a/internal/tools/tools.go b/internal/tools/tools.go index a053763..42c82aa 100644 --- a/internal/tools/tools.go +++ b/internal/tools/tools.go @@ -198,7 +198,8 @@ if err != nil { return nil } name := d.Name() -if name == "node_modules" || name == ".git" || strings.HasPrefix(name, ".") { +// Skip hidden files/directories and special directories, but not the root directory +if path != dir && (name == "node_modules" || name == ".git" || strings.HasPrefix(name, ".")) { if d.IsDir() { return filepath.SkipDir } @@ -210,7 +211,11 @@ return fmt.Errorf("limit reached") if ext != "" && filepath.Ext(name) != ext { return nil } -rel, _ := filepath.Rel(".", path) +rel, _ := filepath.Rel(dir, path) +// Skip the root directory itself (it will be ".") +if rel == "." { +return nil +} if d.IsDir() { files = append(files, rel+"/") } else { diff --git a/internal/tools/tools_test.go b/internal/tools/tools_test.go index 667155b..bdef31a 100644 --- a/internal/tools/tools_test.go +++ b/internal/tools/tools_test.go @@ -473,3 +473,172 @@ func TestExecuteDirect_Grep(t *testing.T) { t.Fatalf("expected match, got: %s", r.Output) } } + +// ── list_files tests ── + +func TestListFiles_Basic(t *testing.T) { + dir := t.TempDir() + os.WriteFile(filepath.Join(dir, "a.txt"), []byte(""), 0o644) + os.WriteFile(filepath.Join(dir, "b.go"), []byte(""), 0o644) + sub := filepath.Join(dir, "sub") + os.MkdirAll(sub, 0o755) + os.WriteFile(filepath.Join(sub, "c.md"), []byte(""), 0o644) + + // Save original working directory + originalWd, _ := os.Getwd() + defer os.Chdir(originalWd) + + // Change to a different directory to test relative paths + os.Chdir("/tmp") + + r := listFiles(map[string]string{ + "directory": dir, + }, 0) + + if !r.Success { + t.Fatalf("expected success, got error: %s", r.Error) + } + + // Check that paths are relative to 'dir', not to cwd + lines := strings.Split(strings.TrimSpace(r.Output), "\n") + expected := []string{"a.txt", "b.go", "sub/", "sub/c.md"} + if len(lines) != len(expected) { + t.Fatalf("expected %d lines, got %d: %s", len(expected), len(lines), r.Output) + } + + for i, line := range lines { + if line != expected[i] { + t.Errorf("line %d: got %q, want %q", i, line, expected[i]) + } + } +} + +func TestListFiles_FromWithinDirectory(t *testing.T) { + dir := t.TempDir() + os.WriteFile(filepath.Join(dir, "test.txt"), []byte(""), 0o644) + + // Save original working directory + originalWd, _ := os.Getwd() + defer os.Chdir(originalWd) + + // Change to the test directory + os.Chdir(dir) + + r := listFiles(map[string]string{ + "directory": ".", + }, 0) + + if !r.Success { + t.Fatalf("expected success, got error: %s", r.Error) + } + + // Should list files relative to current directory (.) + lines := strings.Split(strings.TrimSpace(r.Output), "\n") + if len(lines) != 1 || lines[0] != "test.txt" { + t.Fatalf("expected ['test.txt'], got %v", lines) + } +} + +func TestListFiles_ExtensionFilter(t *testing.T) { + dir := t.TempDir() + os.WriteFile(filepath.Join(dir, "a.txt"), []byte(""), 0o644) + os.WriteFile(filepath.Join(dir, "b.go"), []byte(""), 0o644) + os.WriteFile(filepath.Join(dir, "c.go"), []byte(""), 0o644) + + r := listFiles(map[string]string{ + "directory": dir, + "extension": ".go", + }, 0) + + if !r.Success { + t.Fatalf("expected success, got error: %s", r.Error) + } + + lines := strings.Split(strings.TrimSpace(r.Output), "\n") + if len(lines) != 2 { + t.Fatalf("expected 2 .go files, got %d: %s", len(lines), r.Output) + } + for _, line := range lines { + if !strings.HasSuffix(line, ".go") { + t.Errorf("expected .go file, got %q", line) + } + } +} + +func TestListFiles_EmptyDirectory(t *testing.T) { + dir := t.TempDir() + + r := listFiles(map[string]string{ + "directory": dir, + }, 0) + + if !r.Success { + t.Fatalf("expected success, got error: %s", r.Error) + } + if r.Output != "(empty directory)" { + t.Fatalf("expected '(empty directory)', got %q", r.Output) + } +} + +func TestListFiles_SkipsHiddenAndSpecialDirs(t *testing.T) { + dir := t.TempDir() + os.WriteFile(filepath.Join(dir, "normal.txt"), []byte(""), 0o644) + os.WriteFile(filepath.Join(dir, ".hidden"), []byte(""), 0o644) + gitDir := filepath.Join(dir, ".git") + os.MkdirAll(gitDir, 0o755) + os.WriteFile(filepath.Join(gitDir, "config"), []byte(""), 0o644) + nodeModules := filepath.Join(dir, "node_modules") + os.MkdirAll(nodeModules, 0o755) + os.WriteFile(filepath.Join(nodeModules, "package.json"), []byte(""), 0o644) + + r := listFiles(map[string]string{ + "directory": dir, + }, 0) + + if !r.Success { + t.Fatalf("expected success, got error: %s", r.Error) + } + + // Should only show normal.txt + lines := strings.Split(strings.TrimSpace(r.Output), "\n") + if len(lines) != 1 || lines[0] != "normal.txt" { + t.Fatalf("expected only 'normal.txt', got %v", lines) + } +} + +func TestListFiles_SubdirectoryListing(t *testing.T) { + dir := t.TempDir() + sub := filepath.Join(dir, "subdir") + os.MkdirAll(sub, 0o755) + os.WriteFile(filepath.Join(sub, "file.txt"), []byte(""), 0o644) + os.WriteFile(filepath.Join(sub, "another.md"), []byte(""), 0o644) + + r := listFiles(map[string]string{ + "directory": sub, + }, 0) + + if !r.Success { + t.Fatalf("expected success, got error: %s", r.Error) + } + + // Should list files relative to subdir + lines := strings.Split(strings.TrimSpace(r.Output), "\n") + expected := []string{"file.txt", "another.md"} + if len(lines) != len(expected) { + t.Fatalf("expected %d lines, got %d: %s", len(expected), len(lines), r.Output) + } + // Check that we have both files (order may vary) + hasFileTxt := false + hasAnotherMd := false + for _, line := range lines { + if line == "file.txt" { + hasFileTxt = true + } + if line == "another.md" { + hasAnotherMd = true + } + } + if !hasFileTxt || !hasAnotherMd { + t.Errorf("missing expected files: got %v", lines) + } +} diff --git a/outputs/logs/.gitkeep b/outputs/logs/.gitkeep deleted file mode 100644 index 8b13789..0000000 --- a/outputs/logs/.gitkeep +++ /dev/null @@ -1 +0,0 @@ - diff --git a/outputs/reports/.gitkeep b/outputs/reports/.gitkeep deleted file mode 100644 index 8b13789..0000000 --- a/outputs/reports/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -