diff --git a/integration_test.go b/integration_test.go index 75b0652e..bb1a04c1 100644 --- a/integration_test.go +++ b/integration_test.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "fmt" "os" "os/exec" "path/filepath" @@ -1145,3 +1146,85 @@ Please fix issue number ${1} with priority ${2}. t.Errorf("positional arguments not substituted in content. Output:\n%s", output) } } + +func TestManifestFile(t *testing.T) { + // Create main project directory + mainDir := t.TempDir() + mainRulesDir := filepath.Join(mainDir, ".agents", "rules") + mainTasksDir := filepath.Join(mainDir, ".agents", "tasks") + + if err := os.MkdirAll(mainRulesDir, 0o755); err != nil { + t.Fatalf("failed to create main rules dir: %v", err) + } + if err := os.MkdirAll(mainTasksDir, 0o755); err != nil { + t.Fatalf("failed to create main tasks dir: %v", err) + } + + // Create a task file in the main directory + taskFile := filepath.Join(mainTasksDir, "test-task.md") + taskContent := `--- +task_name: test-task +--- +# Test Task + +This is a test task. +` + if err := os.WriteFile(taskFile, []byte(taskContent), 0o644); err != nil { + t.Fatalf("failed to write task file: %v", err) + } + + // Create a rule file in the main directory (should be included) + mainRuleFile := filepath.Join(mainRulesDir, "main-rule.md") + mainRuleContent := `--- +--- +# Main Rule + +This rule is in the main project. +` + if err := os.WriteFile(mainRuleFile, []byte(mainRuleContent), 0o644); err != nil { + t.Fatalf("failed to write main rule file: %v", err) + } + + // Create a remote directory with rules + remoteDir := t.TempDir() + remoteRulesDir := filepath.Join(remoteDir, ".agents", "rules") + if err := os.MkdirAll(remoteRulesDir, 0o755); err != nil { + t.Fatalf("failed to create remote rules dir: %v", err) + } + + remoteRuleFile := filepath.Join(remoteRulesDir, "remote-rule.md") + remoteRuleContent := `--- +--- +# Remote Rule + +This rule is from a remote directory. +` + if err := os.WriteFile(remoteRuleFile, []byte(remoteRuleContent), 0o644); err != nil { + t.Fatalf("failed to write remote rule file: %v", err) + } + + // Create a manifest file that references the remote directory + manifestFile := filepath.Join(t.TempDir(), "manifest.txt") + manifestContent := fmt.Sprintf("file://%s\n", remoteDir) + if err := os.WriteFile(manifestFile, []byte(manifestContent), 0o644); err != nil { + t.Fatalf("failed to write manifest file: %v", err) + } + + // Run the tool with the manifest file + output := runTool(t, "-C", mainDir, "-m", "file://"+manifestFile, "/test-task") + + // Check that the main rule is included + if !strings.Contains(output, "# Main Rule") { + t.Errorf("main rule not found in stdout. Output:\n%s", output) + } + + // Check that the remote rule from the manifest is included + if !strings.Contains(output, "# Remote Rule") { + t.Errorf("remote rule from manifest not found in stdout. Output:\n%s", output) + } + + // Check that the task is included + if !strings.Contains(output, "# Test Task") { + t.Errorf("task not found in stdout. Output:\n%s", output) + } +} diff --git a/pkg/codingcontext/context.go b/pkg/codingcontext/context.go index c0365fa9..f585d529 100644 --- a/pkg/codingcontext/context.go +++ b/pkg/codingcontext/context.go @@ -115,6 +115,14 @@ func (cc *Context) expandParams(content string) string { // - A free-text prompt (used directly as task content) // - A prompt containing a slash command (e.g., "/fix-bug 123") which triggers task lookup func (cc *Context) Run(ctx context.Context, taskPrompt string) (*Result, error) { + // Parse manifest file first to get additional search paths + manifestPaths, err := cc.parseManifestFile(ctx) + if err != nil { + return nil, fmt.Errorf("failed to parse manifest file: %w", err) + } + cc.searchPaths = append(cc.searchPaths, manifestPaths...) + + // Download all remote directories (including those from manifest) if err := cc.downloadRemoteDirectories(ctx); err != nil { return nil, fmt.Errorf("failed to download remote directories: %w", err) } @@ -130,12 +138,6 @@ func (cc *Context) Run(ctx context.Context, taskPrompt string) (*Result, error) return nil, fmt.Errorf("failed to get user home directory: %w", err) } - searchPaths, err := cc.parseManifestFile(ctx) - if err != nil { - return nil, fmt.Errorf("failed to parse manifest file: %w", err) - } - cc.searchPaths = append(cc.searchPaths, searchPaths...) - // Expand parameters in taskPrompt to allow slash commands in parameters expandedTaskPrompt := cc.expandParams(taskPrompt) @@ -226,8 +228,9 @@ func (cc *Context) parseManifestFile(ctx context.Context) ([]string, error) { manifestFile := downloadDir(cc.manifestURL) - // Download the manifest file using go-getter - if _, err := getter.Get(ctx, manifestFile, cc.manifestURL); err != nil { + // Download the manifest file using go-getter's GetFile function + // GetFile is specifically for downloading single files (not directories) + if _, err := getter.GetFile(ctx, manifestFile, cc.manifestURL); err != nil { return nil, fmt.Errorf("failed to download manifest file %s: %w", cc.manifestURL, err) } defer os.RemoveAll(manifestFile)