From 3c5e6d6498c3bb94c72af963ece91fc6d5ac8414 Mon Sep 17 00:00:00 2001 From: Snider Date: Sun, 22 Mar 2026 08:10:56 +0000 Subject: [PATCH] fix(lib): ExtractWorkspace now recurses into subdirectories Was skipping directories entirely (`if entry.IsDir() { continue }`), so .core/reference/ and its contents were never extracted. Replaced fs.ReadDir loop with fs.WalkDir to handle nested dirs. Added tests: CreatesFiles, CreatesSubdirectories, TemplateSubstitution. Co-Authored-By: Virgil --- pkg/lib/lib.go | 42 ++++++++++++---------- pkg/lib/lib_test.go | 88 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 19 deletions(-) create mode 100644 pkg/lib/lib_test.go diff --git a/pkg/lib/lib.go b/pkg/lib/lib.go index b2b6bcb..b976a81 100644 --- a/pkg/lib/lib.go +++ b/pkg/lib/lib.go @@ -139,31 +139,39 @@ type WorkspaceData struct { // Template names: "default", "security", "review". func ExtractWorkspace(tmplName, targetDir string, data *WorkspaceData) error { wsDir := "workspace/" + tmplName - entries, err := fs.ReadDir(workspaceFS, wsDir) - if err != nil { - return err - } if err := os.MkdirAll(targetDir, 0755); err != nil { return err } - for _, entry := range entries { - if entry.IsDir() { - continue + return fs.WalkDir(workspaceFS, wsDir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err } - name := entry.Name() - content, err := fs.ReadFile(workspaceFS, wsDir+"/"+name) + // Get relative path from template root + rel, err := filepath.Rel(wsDir, path) + if err != nil || rel == "." { + return nil + } + + targetPath := filepath.Join(targetDir, rel) + + if d.IsDir() { + return os.MkdirAll(targetPath, 0755) + } + + content, err := fs.ReadFile(workspaceFS, path) if err != nil { return err } // Process .tmpl files through text/template - outputName := name - if core.HasSuffix(name, ".tmpl") { - outputName = core.TrimSuffix(name, ".tmpl") - tmpl, err := template.New(name).Parse(string(content)) + outputName := filepath.Base(targetPath) + if core.HasSuffix(outputName, ".tmpl") { + outputName = core.TrimSuffix(outputName, ".tmpl") + targetPath = filepath.Join(filepath.Dir(targetPath), outputName) + tmpl, err := template.New(outputName).Parse(string(content)) if err != nil { return err } @@ -174,12 +182,8 @@ func ExtractWorkspace(tmplName, targetDir string, data *WorkspaceData) error { content = buf.Bytes() } - if err := os.WriteFile(filepath.Join(targetDir, outputName), content, 0644); err != nil { - return err - } - } - - return nil + return os.WriteFile(targetPath, content, 0644) + }) } // --- List Functions --- diff --git a/pkg/lib/lib_test.go b/pkg/lib/lib_test.go new file mode 100644 index 0000000..9b6b10e --- /dev/null +++ b/pkg/lib/lib_test.go @@ -0,0 +1,88 @@ +package lib + +import ( + "os" + "path/filepath" + "testing" +) + +func TestExtractWorkspace_CreatesFiles(t *testing.T) { + dir := t.TempDir() + data := &WorkspaceData{Repo: "test-repo", Task: "test task"} + + err := ExtractWorkspace("default", dir, data) + if err != nil { + t.Fatalf("ExtractWorkspace failed: %v", err) + } + + // Check top-level template files exist + for _, name := range []string{"CODEX.md", "CLAUDE.md", "PROMPT.md", "TODO.md", "CONTEXT.md"} { + path := filepath.Join(dir, name) + if _, err := os.Stat(path); os.IsNotExist(err) { + t.Errorf("expected %s to exist", name) + } + } +} + +func TestExtractWorkspace_CreatesSubdirectories(t *testing.T) { + dir := t.TempDir() + data := &WorkspaceData{Repo: "test-repo", Task: "test task"} + + err := ExtractWorkspace("default", dir, data) + if err != nil { + t.Fatalf("ExtractWorkspace failed: %v", err) + } + + // Check .core/reference/ directory exists with files + refDir := filepath.Join(dir, ".core", "reference") + if _, err := os.Stat(refDir); os.IsNotExist(err) { + t.Fatalf(".core/reference/ directory not created") + } + + // Check AX spec exists + axSpec := filepath.Join(refDir, "RFC-025-AGENT-EXPERIENCE.md") + if _, err := os.Stat(axSpec); os.IsNotExist(err) { + t.Errorf("AX spec not extracted: %s", axSpec) + } + + // Check Core source files exist + entries, err := os.ReadDir(refDir) + if err != nil { + t.Fatalf("failed to read reference dir: %v", err) + } + + goFiles := 0 + for _, e := range entries { + if filepath.Ext(e.Name()) == ".go" { + goFiles++ + } + } + if goFiles == 0 { + t.Error("no .go files in .core/reference/") + } + + // Check docs subdirectory + docsDir := filepath.Join(refDir, "docs") + if _, err := os.Stat(docsDir); os.IsNotExist(err) { + t.Errorf(".core/reference/docs/ not created") + } +} + +func TestExtractWorkspace_TemplateSubstitution(t *testing.T) { + dir := t.TempDir() + data := &WorkspaceData{Repo: "my-repo", Task: "fix the bug"} + + err := ExtractWorkspace("default", dir, data) + if err != nil { + t.Fatalf("ExtractWorkspace failed: %v", err) + } + + // TODO.md should contain the task + content, err := os.ReadFile(filepath.Join(dir, "TODO.md")) + if err != nil { + t.Fatalf("failed to read TODO.md: %v", err) + } + if len(content) == 0 { + t.Error("TODO.md is empty") + } +}