From ede5187f21d9e55b110d8c87bf2bcc5f9f92e4cb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 16:19:23 +0000 Subject: [PATCH 1/5] Initial plan From 8200954805c8f8046f4b319d0fbf018f886e177d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 16:23:44 +0000 Subject: [PATCH 2/5] Rename memories to rules and remove deduplication check in code Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- integration_test.go | 376 ++++++++++++++++++++++---------------------- main.go | 56 +++---- memory_name_test.go | 81 ---------- 3 files changed, 211 insertions(+), 302 deletions(-) delete mode 100644 memory_name_test.go diff --git a/integration_test.go b/integration_test.go index 5b2c1643..7b7dee11 100644 --- a/integration_test.go +++ b/integration_test.go @@ -20,31 +20,31 @@ func TestBootstrapFromFile(t *testing.T) { // Create a temporary directory structure tmpDir := t.TempDir() contextDir := filepath.Join(tmpDir, ".prompts") - memoriesDir := filepath.Join(contextDir, "memories") + rulesDir := filepath.Join(contextDir, "rules") tasksDir := filepath.Join(contextDir, "tasks") outputDir := filepath.Join(tmpDir, "output") - if err := os.MkdirAll(memoriesDir, 0755); err != nil { - t.Fatalf("failed to create memories dir: %v", err) + if err := os.MkdirAll(rulesDir, 0755); err != nil { + t.Fatalf("failed to create rules dir: %v", err) } if err := os.MkdirAll(tasksDir, 0755); err != nil { t.Fatalf("failed to create tasks dir: %v", err) } - // Create a memory file - memoryFile := filepath.Join(memoriesDir, "setup.md") - memoryContent := `--- + // Create a rule file + ruleFile := filepath.Join(rulesDir, "setup.md") + ruleContent := `--- --- # Development Setup This is a setup guide. ` - if err := os.WriteFile(memoryFile, []byte(memoryContent), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(ruleFile, []byte(ruleContent), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } - // Create a bootstrap file for the memory (setup.md -> setup-bootstrap) - bootstrapFile := filepath.Join(memoriesDir, "setup-bootstrap") + // Create a bootstrap file for the rule (setup.md -> setup-bootstrap) + bootstrapFile := filepath.Join(rulesDir, "setup-bootstrap") bootstrapContent := `#!/bin/bash echo "Running bootstrap" npm install @@ -66,7 +66,7 @@ Please help with this task. } // Run the binary - cmd = exec.Command(binaryPath, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "test-task") + cmd = exec.Command(binaryPath, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "test-task") cmd.Dir = tmpDir if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary: %v\n%s", err, output) @@ -101,14 +101,14 @@ Please help with this task. // Check that the three output files were created personaOutput := filepath.Join(outputDir, "persona.md") - memoriesOutput := filepath.Join(outputDir, "memories.md") + rulesOutput := filepath.Join(outputDir, "rules.md") taskOutput := filepath.Join(outputDir, "task.md") if _, err := os.Stat(personaOutput); os.IsNotExist(err) { t.Errorf("persona.md file was not created") } - if _, err := os.Stat(memoriesOutput); os.IsNotExist(err) { - t.Errorf("memories.md file was not created") + if _, err := os.Stat(rulesOutput); os.IsNotExist(err) { + t.Errorf("rules.md file was not created") } if _, err := os.Stat(taskOutput); os.IsNotExist(err) { t.Errorf("task.md file was not created") @@ -126,29 +126,29 @@ func TestBootstrapFileNaming(t *testing.T) { // Create a temporary directory structure tmpDir := t.TempDir() contextDir := filepath.Join(tmpDir, ".prompts") - memoriesDir := filepath.Join(contextDir, "memories") + rulesDir := filepath.Join(contextDir, "rules") tasksDir := filepath.Join(contextDir, "tasks") outputDir := filepath.Join(tmpDir, "output") - if err := os.MkdirAll(memoriesDir, 0755); err != nil { - t.Fatalf("failed to create memories dir: %v", err) + if err := os.MkdirAll(rulesDir, 0755); err != nil { + t.Fatalf("failed to create rules dir: %v", err) } if err := os.MkdirAll(tasksDir, 0755); err != nil { t.Fatalf("failed to create tasks dir: %v", err) } - // Create a memory file - memoryFile := filepath.Join(memoriesDir, "jira.md") - memoryContent := `--- + // Create a rule file + ruleFile := filepath.Join(rulesDir, "jira.md") + ruleContent := `--- --- # Jira Integration ` - if err := os.WriteFile(memoryFile, []byte(memoryContent), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(ruleFile, []byte(ruleContent), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } - // Create a bootstrap file for the memory (jira.md -> jira-bootstrap) - bootstrapFile := filepath.Join(memoriesDir, "jira-bootstrap") + // Create a bootstrap file for the rule (jira.md -> jira-bootstrap) + bootstrapFile := filepath.Join(rulesDir, "jira-bootstrap") bootstrapContent := `#!/bin/bash echo "Setting up Jira" ` @@ -167,7 +167,7 @@ echo "Setting up Jira" } // Run the binary - cmd = exec.Command(binaryPath, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "test-task") + cmd = exec.Command(binaryPath, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "test-task") cmd.Dir = tmpDir if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary: %v\n%s", err, output) @@ -216,27 +216,27 @@ func TestBootstrapFileNotRequired(t *testing.T) { // Create a temporary directory structure tmpDir := t.TempDir() contextDir := filepath.Join(tmpDir, ".prompts") - memoriesDir := filepath.Join(contextDir, "memories") + rulesDir := filepath.Join(contextDir, "rules") tasksDir := filepath.Join(contextDir, "tasks") outputDir := filepath.Join(tmpDir, "output") - if err := os.MkdirAll(memoriesDir, 0755); err != nil { - t.Fatalf("failed to create memories dir: %v", err) + if err := os.MkdirAll(rulesDir, 0755); err != nil { + t.Fatalf("failed to create rules dir: %v", err) } if err := os.MkdirAll(tasksDir, 0755); err != nil { t.Fatalf("failed to create tasks dir: %v", err) } - // Create a memory file WITHOUT a bootstrap - memoryFile := filepath.Join(memoriesDir, "info.md") - memoryContent := `--- + // Create a rule file WITHOUT a bootstrap + ruleFile := filepath.Join(rulesDir, "info.md") + ruleContent := `--- --- # Project Info Just some information. ` - if err := os.WriteFile(memoryFile, []byte(memoryContent), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(ruleFile, []byte(ruleContent), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } // Create a prompt file @@ -252,7 +252,7 @@ Please help with this task. } // Run the binary - cmd = exec.Command(binaryPath, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "test-task") + cmd = exec.Command(binaryPath, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "test-task") cmd.Dir = tmpDir if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary: %v\n%s", err, output) @@ -269,11 +269,11 @@ Please help with this task. } // Check that the three output files were still created - memoriesOutput := filepath.Join(outputDir, "memories.md") + rulesOutput := filepath.Join(outputDir, "rules.md") taskOutput := filepath.Join(outputDir, "task.md") - if _, err := os.Stat(memoriesOutput); os.IsNotExist(err) { - t.Errorf("memories.md file was not created") + if _, err := os.Stat(rulesOutput); os.IsNotExist(err) { + t.Errorf("rules.md file was not created") } if _, err := os.Stat(taskOutput); os.IsNotExist(err) { t.Errorf("task.md file was not created") @@ -291,30 +291,30 @@ func TestMultipleBootstrapFiles(t *testing.T) { // Create a temporary directory structure tmpDir := t.TempDir() contextDir := filepath.Join(tmpDir, ".prompts") - memoriesDir := filepath.Join(contextDir, "memories") + rulesDir := filepath.Join(contextDir, "rules") tasksDir := filepath.Join(contextDir, "tasks") outputDir := filepath.Join(tmpDir, "output") - if err := os.MkdirAll(memoriesDir, 0755); err != nil { - t.Fatalf("failed to create memories dir: %v", err) + if err := os.MkdirAll(rulesDir, 0755); err != nil { + t.Fatalf("failed to create rules dir: %v", err) } if err := os.MkdirAll(tasksDir, 0755); err != nil { t.Fatalf("failed to create tasks dir: %v", err) } - // Create first memory file with bootstrap - if err := os.WriteFile(filepath.Join(memoriesDir, "setup.md"), []byte("---\n---\n# Setup\n"), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + // Create first rule file with bootstrap + if err := os.WriteFile(filepath.Join(rulesDir, "setup.md"), []byte("---\n---\n# Setup\n"), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } - if err := os.WriteFile(filepath.Join(memoriesDir, "setup-bootstrap"), []byte("#!/bin/bash\necho setup\n"), 0755); err != nil { + if err := os.WriteFile(filepath.Join(rulesDir, "setup-bootstrap"), []byte("#!/bin/bash\necho setup\n"), 0755); err != nil { t.Fatalf("failed to write bootstrap file: %v", err) } - // Create second memory file with bootstrap - if err := os.WriteFile(filepath.Join(memoriesDir, "deps.md"), []byte("---\n---\n# Dependencies\n"), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + // Create second rule file with bootstrap + if err := os.WriteFile(filepath.Join(rulesDir, "deps.md"), []byte("---\n---\n# Dependencies\n"), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } - if err := os.WriteFile(filepath.Join(memoriesDir, "deps-bootstrap"), []byte("#!/bin/bash\necho deps\n"), 0755); err != nil { + if err := os.WriteFile(filepath.Join(rulesDir, "deps-bootstrap"), []byte("#!/bin/bash\necho deps\n"), 0755); err != nil { t.Fatalf("failed to write bootstrap file: %v", err) } @@ -324,7 +324,7 @@ func TestMultipleBootstrapFiles(t *testing.T) { } // Run the binary - cmd = exec.Command(binaryPath, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "test-task") + cmd = exec.Command(binaryPath, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "test-task") cmd.Dir = tmpDir if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary: %v\n%s", err, output) @@ -352,30 +352,30 @@ func TestSelectorFiltering(t *testing.T) { // Create a temporary directory structure tmpDir := t.TempDir() contextDir := filepath.Join(tmpDir, ".prompts") - memoriesDir := filepath.Join(contextDir, "memories") + rulesDir := filepath.Join(contextDir, "rules") tasksDir := filepath.Join(contextDir, "tasks") outputDir := filepath.Join(tmpDir, "output") - if err := os.MkdirAll(memoriesDir, 0755); err != nil { - t.Fatalf("failed to create memories dir: %v", err) + if err := os.MkdirAll(rulesDir, 0755); err != nil { + t.Fatalf("failed to create rules dir: %v", err) } if err := os.MkdirAll(tasksDir, 0755); err != nil { t.Fatalf("failed to create tasks dir: %v", err) } - // Create memory files with different frontmatter - if err := os.WriteFile(filepath.Join(memoriesDir, "prod.md"), []byte("---\nenv: production\nlanguage: go\n---\n# Production\nProd content\n"), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + // Create rule files with different frontmatter + if err := os.WriteFile(filepath.Join(rulesDir, "prod.md"), []byte("---\nenv: production\nlanguage: go\n---\n# Production\nProd content\n"), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } - if err := os.WriteFile(filepath.Join(memoriesDir, "dev.md"), []byte("---\nenv: development\nlanguage: python\n---\n# Development\nDev content\n"), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(filepath.Join(rulesDir, "dev.md"), []byte("---\nenv: development\nlanguage: python\n---\n# Development\nDev content\n"), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } - if err := os.WriteFile(filepath.Join(memoriesDir, "test.md"), []byte("---\nenv: test\nlanguage: go\n---\n# Test\nTest content\n"), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(filepath.Join(rulesDir, "test.md"), []byte("---\nenv: test\nlanguage: go\n---\n# Test\nTest content\n"), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } // Create a file without frontmatter (should be included by default) - if err := os.WriteFile(filepath.Join(memoriesDir, "nofm.md"), []byte("---\n---\n# No Frontmatter\nNo FM content\n"), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(filepath.Join(rulesDir, "nofm.md"), []byte("---\n---\n# No Frontmatter\nNo FM content\n"), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } // Create a prompt file @@ -384,16 +384,16 @@ func TestSelectorFiltering(t *testing.T) { } // Test 1: Include by env=production - cmd = exec.Command(binaryPath, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "-s", "env=production", "test-task") + cmd = exec.Command(binaryPath, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "-s", "env=production", "test-task") cmd.Dir = tmpDir if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary: %v\n%s", err, output) } - memoriesOutput := filepath.Join(outputDir, "memories.md") - content, err := os.ReadFile(memoriesOutput) + rulesOutput := filepath.Join(outputDir, "rules.md") + content, err := os.ReadFile(rulesOutput) if err != nil { - t.Fatalf("failed to read memories output: %v", err) + t.Fatalf("failed to read rules output: %v", err) } contentStr := string(content) if !strings.Contains(contentStr, "Prod content") { @@ -414,15 +414,15 @@ func TestSelectorFiltering(t *testing.T) { os.RemoveAll(outputDir) // Test 2: Include by language=go (should include prod and test, and nofm) - cmd = exec.Command(binaryPath, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "-s", "language=go", "test-task") + cmd = exec.Command(binaryPath, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "-s", "language=go", "test-task") cmd.Dir = tmpDir if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary: %v\n%s", err, output) } - content, err = os.ReadFile(memoriesOutput) + content, err = os.ReadFile(rulesOutput) if err != nil { - t.Fatalf("failed to read memories output: %v", err) + t.Fatalf("failed to read rules output: %v", err) } contentStr = string(content) if !strings.Contains(contentStr, "Prod content") { @@ -442,15 +442,15 @@ func TestSelectorFiltering(t *testing.T) { os.RemoveAll(outputDir) // Test 3: Exclude by env=production (should include dev and test, and nofm) - cmd = exec.Command(binaryPath, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "-S", "env=production", "test-task") + cmd = exec.Command(binaryPath, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "-S", "env=production", "test-task") cmd.Dir = tmpDir if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary: %v\n%s", err, output) } - content, err = os.ReadFile(memoriesOutput) + content, err = os.ReadFile(rulesOutput) if err != nil { - t.Fatalf("failed to read memories output: %v", err) + t.Fatalf("failed to read rules output: %v", err) } contentStr = string(content) if strings.Contains(contentStr, "Prod content") { @@ -470,15 +470,15 @@ func TestSelectorFiltering(t *testing.T) { os.RemoveAll(outputDir) // Test 4: Multiple includes env=production language=go (should include only prod and nofm) - cmd = exec.Command(binaryPath, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "-s", "env=production", "-s", "language=go", "test-task") + cmd = exec.Command(binaryPath, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "-s", "env=production", "-s", "language=go", "test-task") cmd.Dir = tmpDir if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary: %v\n%s", err, output) } - content, err = os.ReadFile(memoriesOutput) + content, err = os.ReadFile(rulesOutput) if err != nil { - t.Fatalf("failed to read memories output: %v", err) + t.Fatalf("failed to read rules output: %v", err) } contentStr = string(content) if !strings.Contains(contentStr, "Prod content") { @@ -498,15 +498,15 @@ func TestSelectorFiltering(t *testing.T) { os.RemoveAll(outputDir) // Test 5: Mix of include and exclude -s env=production -S language=python (should include only prod with go) - cmd = exec.Command(binaryPath, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "-s", "env=production", "-S", "language=python", "test-task") + cmd = exec.Command(binaryPath, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "-s", "env=production", "-S", "language=python", "test-task") cmd.Dir = tmpDir if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary: %v\n%s", err, output) } - content, err = os.ReadFile(memoriesOutput) + content, err = os.ReadFile(rulesOutput) if err != nil { - t.Fatalf("failed to read memories output: %v", err) + t.Fatalf("failed to read rules output: %v", err) } contentStr = string(content) if !strings.Contains(contentStr, "Prod content") { @@ -662,31 +662,31 @@ func TestBootstrapFlag(t *testing.T) { // Create a temporary directory structure tmpDir := t.TempDir() contextDir := filepath.Join(tmpDir, ".prompts") - memoriesDir := filepath.Join(contextDir, "memories") + rulesDir := filepath.Join(contextDir, "rules") tasksDir := filepath.Join(contextDir, "tasks") outputDir := filepath.Join(tmpDir, "output") - if err := os.MkdirAll(memoriesDir, 0755); err != nil { - t.Fatalf("failed to create memories dir: %v", err) + if err := os.MkdirAll(rulesDir, 0755); err != nil { + t.Fatalf("failed to create rules dir: %v", err) } if err := os.MkdirAll(tasksDir, 0755); err != nil { t.Fatalf("failed to create tasks dir: %v", err) } - // Create a memory file - memoryFile := filepath.Join(memoriesDir, "setup.md") - memoryContent := `--- + // Create a rule file + ruleFile := filepath.Join(rulesDir, "setup.md") + ruleContent := `--- --- # Setup This is a setup guide. ` - if err := os.WriteFile(memoryFile, []byte(memoryContent), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(ruleFile, []byte(ruleContent), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } // Create a bootstrap file that creates a marker file - bootstrapFile := filepath.Join(memoriesDir, "setup-bootstrap") + bootstrapFile := filepath.Join(rulesDir, "setup-bootstrap") markerFile := filepath.Join(outputDir, "bootstrap-ran.txt") bootstrapContent := `#!/bin/bash echo "Bootstrap executed" > ` + markerFile + ` @@ -708,7 +708,7 @@ Please help with this task. } // Run the binary WITH the -b flag - cmd = exec.Command(binaryPath, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "-b", "test-task") + cmd = exec.Command(binaryPath, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "-b", "test-task") cmd.Dir = tmpDir if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary: %v\n%s", err, output) @@ -741,31 +741,31 @@ func TestBootstrapFlagNotSet(t *testing.T) { // Create a temporary directory structure tmpDir := t.TempDir() contextDir := filepath.Join(tmpDir, ".prompts") - memoriesDir := filepath.Join(contextDir, "memories") + rulesDir := filepath.Join(contextDir, "rules") tasksDir := filepath.Join(contextDir, "tasks") outputDir := filepath.Join(tmpDir, "output") - if err := os.MkdirAll(memoriesDir, 0755); err != nil { - t.Fatalf("failed to create memories dir: %v", err) + if err := os.MkdirAll(rulesDir, 0755); err != nil { + t.Fatalf("failed to create rules dir: %v", err) } if err := os.MkdirAll(tasksDir, 0755); err != nil { t.Fatalf("failed to create tasks dir: %v", err) } - // Create a memory file - memoryFile := filepath.Join(memoriesDir, "setup.md") - memoryContent := `--- + // Create a rule file + ruleFile := filepath.Join(rulesDir, "setup.md") + ruleContent := `--- --- # Setup This is a setup guide. ` - if err := os.WriteFile(memoryFile, []byte(memoryContent), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(ruleFile, []byte(ruleContent), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } // Create a bootstrap file that creates a marker file - bootstrapFile := filepath.Join(memoriesDir, "setup-bootstrap") + bootstrapFile := filepath.Join(rulesDir, "setup-bootstrap") markerFile := filepath.Join(outputDir, "bootstrap-ran.txt") bootstrapContent := `#!/bin/bash echo "Bootstrap executed" > ` + markerFile + ` @@ -787,7 +787,7 @@ Please help with this task. } // Run the binary WITHOUT the -b flag - cmd = exec.Command(binaryPath, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "test-task") + cmd = exec.Command(binaryPath, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "test-task") cmd.Dir = tmpDir if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary: %v\n%s", err, output) @@ -810,31 +810,31 @@ func TestBootstrapCancellation(t *testing.T) { // Create a temporary directory structure tmpDir := t.TempDir() contextDir := filepath.Join(tmpDir, ".prompts") - memoriesDir := filepath.Join(contextDir, "memories") + rulesDir := filepath.Join(contextDir, "rules") tasksDir := filepath.Join(contextDir, "tasks") outputDir := filepath.Join(tmpDir, "output") - if err := os.MkdirAll(memoriesDir, 0755); err != nil { - t.Fatalf("failed to create memories dir: %v", err) + if err := os.MkdirAll(rulesDir, 0755); err != nil { + t.Fatalf("failed to create rules dir: %v", err) } if err := os.MkdirAll(tasksDir, 0755); err != nil { t.Fatalf("failed to create tasks dir: %v", err) } - // Create a memory file - memoryFile := filepath.Join(memoriesDir, "setup.md") - memoryContent := `--- + // Create a rule file + ruleFile := filepath.Join(rulesDir, "setup.md") + ruleContent := `--- --- # Setup Long running setup. ` - if err := os.WriteFile(memoryFile, []byte(memoryContent), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(ruleFile, []byte(ruleContent), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } // Create a bootstrap file that runs for a while - bootstrapFile := filepath.Join(memoriesDir, "setup-bootstrap") + bootstrapFile := filepath.Join(rulesDir, "setup-bootstrap") bootstrapContent := `#!/bin/bash for i in {1..30}; do echo "Running $i" @@ -856,7 +856,7 @@ done } // Run the binary WITH the -b flag and send interrupt signal - cmd = exec.Command(binaryPath, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "-b", "test-task") + cmd = exec.Command(binaryPath, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "-b", "test-task") cmd.Dir = tmpDir // Start the command @@ -883,7 +883,7 @@ done } // TestTaskNameBuiltinFilter verifies that the task_name built-in filter -// automatically includes/excludes memory files based on the task being run +// automatically includes/excludes rule files based on the task being run func TestTaskNameBuiltinFilter(t *testing.T) { // Build the binary binaryPath := filepath.Join(t.TempDir(), "coding-context") @@ -895,27 +895,27 @@ func TestTaskNameBuiltinFilter(t *testing.T) { // Create a temporary directory structure tmpDir := t.TempDir() contextDir := filepath.Join(tmpDir, ".prompts") - memoriesDir := filepath.Join(contextDir, "memories") + rulesDir := filepath.Join(contextDir, "rules") tasksDir := filepath.Join(contextDir, "tasks") outputDir := filepath.Join(tmpDir, "output") - if err := os.MkdirAll(memoriesDir, 0755); err != nil { - t.Fatalf("failed to create memories dir: %v", err) + if err := os.MkdirAll(rulesDir, 0755); err != nil { + t.Fatalf("failed to create rules dir: %v", err) } if err := os.MkdirAll(tasksDir, 0755); err != nil { t.Fatalf("failed to create tasks dir: %v", err) } - // Create memory files with task_name frontmatter - if err := os.WriteFile(filepath.Join(memoriesDir, "deploy-specific.md"), []byte("---\ntask_name: deploy\n---\n# Deploy Memory\nDeploy-specific content\n"), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + // Create rule files with task_name frontmatter + if err := os.WriteFile(filepath.Join(rulesDir, "deploy-specific.md"), []byte("---\ntask_name: deploy\n---\n# Deploy Rule\nDeploy-specific content\n"), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } - if err := os.WriteFile(filepath.Join(memoriesDir, "test-specific.md"), []byte("---\ntask_name: test\n---\n# Test Memory\nTest-specific content\n"), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(filepath.Join(rulesDir, "test-specific.md"), []byte("---\ntask_name: test\n---\n# Test Rule\nTest-specific content\n"), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } // Create a file without task_name (should be included for all tasks) - if err := os.WriteFile(filepath.Join(memoriesDir, "general.md"), []byte("---\n---\n# General Memory\nGeneral content\n"), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(filepath.Join(rulesDir, "general.md"), []byte("---\n---\n# General Rule\nGeneral content\n"), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } // Create prompt files for both tasks @@ -927,16 +927,16 @@ func TestTaskNameBuiltinFilter(t *testing.T) { } // Test 1: Run with "deploy" task - should include deploy-specific and general, but not test-specific - cmd = exec.Command(binaryPath, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "deploy") + cmd = exec.Command(binaryPath, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "deploy") cmd.Dir = tmpDir if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary: %v\n%s", err, output) } - memoriesOutput := filepath.Join(outputDir, "memories.md") - content, err := os.ReadFile(memoriesOutput) + rulesOutput := filepath.Join(outputDir, "rules.md") + content, err := os.ReadFile(rulesOutput) if err != nil { - t.Fatalf("failed to read memories output: %v", err) + t.Fatalf("failed to read rules output: %v", err) } contentStr := string(content) if !strings.Contains(contentStr, "Deploy-specific content") { @@ -953,15 +953,15 @@ func TestTaskNameBuiltinFilter(t *testing.T) { os.RemoveAll(outputDir) // Test 2: Run with "test" task - should include test-specific and general, but not deploy-specific - cmd = exec.Command(binaryPath, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "test") + cmd = exec.Command(binaryPath, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "test") cmd.Dir = tmpDir if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary: %v\n%s", err, output) } - content, err = os.ReadFile(memoriesOutput) + content, err = os.ReadFile(rulesOutput) if err != nil { - t.Fatalf("failed to read memories output: %v", err) + t.Fatalf("failed to read rules output: %v", err) } contentStr = string(content) if strings.Contains(contentStr, "Deploy-specific content") { @@ -986,13 +986,13 @@ func TestPersonaBasic(t *testing.T) { // Create a temporary directory structure tmpDir := t.TempDir() contextDir := filepath.Join(tmpDir, ".prompts") - memoriesDir := filepath.Join(contextDir, "memories") + rulesDir := filepath.Join(contextDir, "rules") personasDir := filepath.Join(contextDir, "personas") tasksDir := filepath.Join(contextDir, "tasks") outputDir := filepath.Join(tmpDir, "output") - if err := os.MkdirAll(memoriesDir, 0755); err != nil { - t.Fatalf("failed to create memories dir: %v", err) + if err := os.MkdirAll(rulesDir, 0755); err != nil { + t.Fatalf("failed to create rules dir: %v", err) } if err := os.MkdirAll(personasDir, 0755); err != nil { t.Fatalf("failed to create personas dir: %v", err) @@ -1013,16 +1013,16 @@ You are an expert in Go. t.Fatalf("failed to write persona file: %v", err) } - // Create a memory file - memoryFile := filepath.Join(memoriesDir, "context.md") - memoryContent := `--- + // Create a rule file + ruleFile := filepath.Join(rulesDir, "context.md") + ruleContent := `--- --- # Context This is context. ` - if err := os.WriteFile(memoryFile, []byte(memoryContent), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(ruleFile, []byte(ruleContent), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } // Create a task file @@ -1038,7 +1038,7 @@ Please help with ${feature}. } // Run with persona (persona is now a positional argument after task name) - cmd = exec.Command(binaryPath, "-r", personasDir, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "-p", "feature=auth", "test-task", "expert") + cmd = exec.Command(binaryPath, "-r", personasDir, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "-p", "feature=auth", "test-task", "expert") cmd.Dir = tmpDir if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary: %v\n%s", err, output) @@ -1051,10 +1051,10 @@ Please help with ${feature}. t.Fatalf("failed to read persona output: %v", err) } - memoriesOutput := filepath.Join(outputDir, "memories.md") - memoriesBytes, err2 := os.ReadFile(memoriesOutput) + rulesOutput := filepath.Join(outputDir, "rules.md") + rulesBytes, err2 := os.ReadFile(rulesOutput) if err2 != nil { - t.Fatalf("failed to read memories output: %v", err2) + t.Fatalf("failed to read rules output: %v", err2) } taskOutput := filepath.Join(outputDir, "task.md") @@ -1072,10 +1072,10 @@ Please help with ${feature}. t.Errorf("Expected persona content to remain as-is without template expansion") } - // Verify memories content - memoriesStr := string(memoriesBytes) - if !strings.Contains(memoriesStr, "# Context") { - t.Errorf("Expected to find '# Context' in memories.md") + // Verify rules content + rulesStr := string(rulesBytes) + if !strings.Contains(rulesStr, "# Context") { + t.Errorf("Expected to find '# Context' in rules.md") } // Verify task content @@ -1099,27 +1099,27 @@ func TestPersonaOptional(t *testing.T) { // Create a temporary directory structure tmpDir := t.TempDir() contextDir := filepath.Join(tmpDir, ".prompts") - memoriesDir := filepath.Join(contextDir, "memories") + rulesDir := filepath.Join(contextDir, "rules") tasksDir := filepath.Join(contextDir, "tasks") outputDir := filepath.Join(tmpDir, "output") - if err := os.MkdirAll(memoriesDir, 0755); err != nil { - t.Fatalf("failed to create memories dir: %v", err) + if err := os.MkdirAll(rulesDir, 0755); err != nil { + t.Fatalf("failed to create rules dir: %v", err) } if err := os.MkdirAll(tasksDir, 0755); err != nil { t.Fatalf("failed to create tasks dir: %v", err) } - // Create a memory file - memoryFile := filepath.Join(memoriesDir, "context.md") - memoryContent := `--- + // Create a rule file + ruleFile := filepath.Join(rulesDir, "context.md") + ruleContent := `--- --- # Context This is context. ` - if err := os.WriteFile(memoryFile, []byte(memoryContent), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(ruleFile, []byte(ruleContent), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } // Create a task file @@ -1135,17 +1135,17 @@ Please help. } // Run WITHOUT persona (should still work) - cmd = exec.Command(binaryPath, "-m", memoriesDir, "-t", tasksDir, "-o", outputDir, "test-task") + cmd = exec.Command(binaryPath, "-m", rulesDir, "-t", tasksDir, "-o", outputDir, "test-task") cmd.Dir = tmpDir if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary without persona: %v\n%s", err, output) } - // Check the memories and task outputs - memoriesOutput := filepath.Join(outputDir, "memories.md") - memoriesBytes, err := os.ReadFile(memoriesOutput) + // Check the rules and task outputs + rulesOutput := filepath.Join(outputDir, "rules.md") + rulesBytes, err := os.ReadFile(rulesOutput) if err != nil { - t.Fatalf("failed to read memories output: %v", err) + t.Fatalf("failed to read rules output: %v", err) } taskOutput := filepath.Join(outputDir, "task.md") @@ -1155,8 +1155,8 @@ Please help. } // Verify context and task are present - if !strings.Contains(string(memoriesBytes), "# Context") { - t.Errorf("Expected to find '# Context' in memories.md") + if !strings.Contains(string(rulesBytes), "# Context") { + t.Errorf("Expected to find '# Context' in rules.md") } if !strings.Contains(string(taskBytes), "# Task") { t.Errorf("Expected to find '# Task' in task.md") @@ -1224,25 +1224,25 @@ func TestWorkDirOption(t *testing.T) { // Create a temporary directory structure tmpDir := t.TempDir() workDir := filepath.Join(tmpDir, "work") - memoriesDir := filepath.Join(workDir, ".prompts", "memories") + rulesDir := filepath.Join(workDir, ".prompts", "rules") tasksDir := filepath.Join(workDir, ".prompts", "tasks") outputDir := filepath.Join(tmpDir, "output") - if err := os.MkdirAll(memoriesDir, 0755); err != nil { - t.Fatalf("failed to create memories dir: %v", err) + if err := os.MkdirAll(rulesDir, 0755); err != nil { + t.Fatalf("failed to create rules dir: %v", err) } if err := os.MkdirAll(tasksDir, 0755); err != nil { t.Fatalf("failed to create tasks dir: %v", err) } - // Create a memory file in the work directory - memoryFile := filepath.Join(memoriesDir, "test.md") - memoryContent := `--- + // Create a rule file in the work directory + ruleFile := filepath.Join(rulesDir, "test.md") + ruleContent := `--- --- -# Test Memory +# Test Rule ` - if err := os.WriteFile(memoryFile, []byte(memoryContent), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(ruleFile, []byte(ruleContent), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } // Create a task file @@ -1256,19 +1256,19 @@ func TestWorkDirOption(t *testing.T) { } // Run the binary with -C option to change to work directory - cmd = exec.Command(binaryPath, "-C", workDir, "-m", ".prompts/memories", "-t", ".prompts/tasks", "-o", outputDir, "task") + cmd = exec.Command(binaryPath, "-C", workDir, "-m", ".prompts/rules", "-t", ".prompts/tasks", "-o", outputDir, "task") if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to run binary with -C option: %v\n%s", err, output) } // Verify that the three output files were created in the output directory - memoriesOutFile := filepath.Join(outputDir, "memories.md") + rulesOutFile := filepath.Join(outputDir, "rules.md") taskOutFile := filepath.Join(outputDir, "task.md") personaOutFile := filepath.Join(outputDir, "persona.md") var statErr error - if _, statErr = os.Stat(memoriesOutFile); os.IsNotExist(statErr) { - t.Errorf("memories.md was not created in output directory") + if _, statErr = os.Stat(rulesOutFile); os.IsNotExist(statErr) { + t.Errorf("rules.md was not created in output directory") } if _, statErr = os.Stat(taskOutFile); os.IsNotExist(statErr) { t.Errorf("task.md was not created in output directory") @@ -1277,13 +1277,13 @@ func TestWorkDirOption(t *testing.T) { t.Errorf("persona.md was not created in output directory") } - // Verify the content includes the memory - content, err := os.ReadFile(memoriesOutFile) + // Verify the content includes the rule + content, err := os.ReadFile(rulesOutFile) if err != nil { - t.Fatalf("failed to read memories.md: %v", err) + t.Fatalf("failed to read rules.md: %v", err) } - if !strings.Contains(string(content), "Test Memory") { - t.Errorf("memories.md does not contain expected memory content") + if !strings.Contains(string(content), "Test Rule") { + t.Errorf("rules.md does not contain expected rule content") } } @@ -1298,13 +1298,13 @@ func TestTokenCounting(t *testing.T) { // Create a temporary directory structure tmpDir := t.TempDir() contextDir := filepath.Join(tmpDir, ".prompts") - memoriesDir := filepath.Join(contextDir, "memories") + rulesDir := filepath.Join(contextDir, "rules") tasksDir := filepath.Join(contextDir, "tasks") personasDir := filepath.Join(contextDir, "personas") outputDir := filepath.Join(tmpDir, "output") - if err := os.MkdirAll(memoriesDir, 0755); err != nil { - t.Fatalf("failed to create memories dir: %v", err) + if err := os.MkdirAll(rulesDir, 0755); err != nil { + t.Fatalf("failed to create rules dir: %v", err) } if err := os.MkdirAll(tasksDir, 0755); err != nil { t.Fatalf("failed to create tasks dir: %v", err) @@ -1322,21 +1322,21 @@ You are an expert developer.` t.Fatalf("failed to write persona file: %v", err) } - // Create memory files - memoryFile1 := filepath.Join(memoriesDir, "setup.md") - memoryContent1 := `# Development Setup + // Create rule files + ruleFile1 := filepath.Join(rulesDir, "setup.md") + ruleContent1 := `# Development Setup This is a setup guide with detailed instructions.` - if err := os.WriteFile(memoryFile1, []byte(memoryContent1), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(ruleFile1, []byte(ruleContent1), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } - memoryFile2 := filepath.Join(memoriesDir, "conventions.md") - memoryContent2 := `# Coding Conventions + ruleFile2 := filepath.Join(rulesDir, "conventions.md") + ruleContent2 := `# Coding Conventions Follow best practices and write clean code.` - if err := os.WriteFile(memoryFile2, []byte(memoryContent2), 0644); err != nil { - t.Fatalf("failed to write memory file: %v", err) + if err := os.WriteFile(ruleFile2, []byte(ruleContent2), 0644); err != nil { + t.Fatalf("failed to write rule file: %v", err) } // Create a task file @@ -1365,8 +1365,8 @@ Complete this task with high quality.` if !strings.Contains(outputStr, "tokens)") { t.Errorf("Expected token count in output") } - if !strings.Contains(outputStr, "Including memory file:") { - t.Errorf("Expected memory file message in output") + if !strings.Contains(outputStr, "Including rule file:") { + t.Errorf("Expected rule file message in output") } if !strings.Contains(outputStr, "Using task file:") { t.Errorf("Expected task file message in output") diff --git a/main.go b/main.go index 293f4445..235141b7 100644 --- a/main.go +++ b/main.go @@ -19,7 +19,7 @@ var bootstrap string var ( workDir string - memories stringSlice + rules stringSlice personas stringSlice tasks stringSlice outputDir = "." @@ -39,7 +39,7 @@ func main() { os.Exit(1) } - memories = []string{ + rules = []string{ "AGENTS.md", ".github/copilot-instructions.md", "CLAUDE.md", @@ -47,9 +47,9 @@ func main() { ".cursor/rules/", ".instructions.md", ".continuerules", - ".prompts/memories", - filepath.Join(userConfigDir, "prompts", "memories"), - "/var/local/prompts/memories", + ".prompts/rules", + filepath.Join(userConfigDir, "prompts", "rules"), + "/var/local/prompts/rules", } personas = []string{ @@ -65,13 +65,13 @@ func main() { } flag.StringVar(&workDir, "C", ".", "Change to directory before doing anything.") - flag.Var(&memories, "m", "Directory containing memories, or a single memory file. Can be specified multiple times.") + flag.Var(&rules, "m", "Directory containing rules, or a single rule file. Can be specified multiple times.") flag.Var(&personas, "r", "Directory containing personas, or a single persona file. Can be specified multiple times.") flag.Var(&tasks, "t", "Directory containing tasks, or a single task file. Can be specified multiple times.") flag.StringVar(&outputDir, "o", ".", "Directory to write the context files to.") flag.Var(¶ms, "p", "Parameter to substitute in the prompt. Can be specified multiple times as key=value.") - flag.Var(&includes, "s", "Include memories with matching frontmatter. Can be specified multiple times as key=value.") - flag.Var(&excludes, "S", "Exclude memories with matching frontmatter. Can be specified multiple times as key=value.") + flag.Var(&includes, "s", "Include rules with matching frontmatter. Can be specified multiple times as key=value.") + flag.Var(&excludes, "S", "Exclude rules with matching frontmatter. Can be specified multiple times as key=value.") flag.BoolVar(&runBootstrap, "b", false, "Automatically run the bootstrap script after generating it.") flag.Usage = func() { @@ -101,7 +101,7 @@ func run(ctx context.Context, args []string) error { return fmt.Errorf("failed to chdir to %s: %w", workDir, err) } - // Add task name to includes so memories can be filtered by task + // Add task name to includes so rules can be filtered by task taskName := args[0] includes["task_name"] = taskName @@ -173,23 +173,21 @@ func run(ctx context.Context, args []string) error { } } - // Create memories.md file - memoriesOutput, err := os.Create(filepath.Join(outputDir, "memories.md")) + // Create rules.md file + rulesOutput, err := os.Create(filepath.Join(outputDir, "rules.md")) if err != nil { - return fmt.Errorf("failed to create memories file: %w", err) + return fmt.Errorf("failed to create rules file: %w", err) } - defer memoriesOutput.Close() + defer rulesOutput.Close() - memoryBasenames := make(map[string]bool) - - for _, memory := range memories { + for _, rule := range rules { // Skip if the path doesn't exist - if _, err := os.Stat(memory); os.IsNotExist(err) { + if _, err := os.Stat(rule); os.IsNotExist(err) { continue } - err := filepath.Walk(memory, func(path string, info os.FileInfo, err error) error { + err := filepath.Walk(rule, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -197,7 +195,7 @@ func run(ctx context.Context, args []string) error { return nil } - // Only process .md files as memory files + // Only process .md files as rule files if filepath.Ext(path) != ".md" { return nil } @@ -211,26 +209,18 @@ func run(ctx context.Context, args []string) error { // Check if file matches include and exclude selectors if !includes.matchesIncludes(frontmatter) { - fmt.Fprintf(os.Stdout, "Excluding memory file (does not match include selectors): %s\n", path) + fmt.Fprintf(os.Stdout, "Excluding rule file (does not match include selectors): %s\n", path) return nil } if !excludes.matchesExcludes(frontmatter) { - fmt.Fprintf(os.Stdout, "Excluding memory file (matches exclude selectors): %s\n", path) - return nil - } - - // Check for duplicate basenames - basename := filepath.Base(path) - if memoryBasenames[basename] { - fmt.Fprintf(os.Stdout, "Excluding memory file (other memory with same basename found): %s\n", path) + fmt.Fprintf(os.Stdout, "Excluding rule file (matches exclude selectors): %s\n", path) return nil } - memoryBasenames[basename] = true // Estimate tokens for this file tokens := estimateTokens(content) totalTokens += tokens - fmt.Fprintf(os.Stdout, "Including memory file: %s (~%d tokens)\n", path, tokens) + fmt.Fprintf(os.Stdout, "Including rule file: %s (~%d tokens)\n", path, tokens) // Check for a bootstrap file named -bootstrap // For example, setup.md -> setup-bootstrap @@ -249,15 +239,15 @@ func run(ctx context.Context, args []string) error { } } - if _, err := memoriesOutput.WriteString(content + "\n\n"); err != nil { - return fmt.Errorf("failed to write to memories file: %w", err) + if _, err := rulesOutput.WriteString(content + "\n\n"); err != nil { + return fmt.Errorf("failed to write to rules file: %w", err) } return nil }) if err != nil { - return fmt.Errorf("failed to walk memory dir: %w", err) + return fmt.Errorf("failed to walk rule dir: %w", err) } } diff --git a/memory_name_test.go b/memory_name_test.go deleted file mode 100644 index a86ce6b0..00000000 --- a/memory_name_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package main - -import ( - "context" - "os" - "path/filepath" - "strings" - "testing" -) - -func TestMemoryNameExclusion(t *testing.T) { - // Create a temporary directory for our test files - tmpDir, err := os.MkdirTemp("", "memory-name-test") - if err != nil { - t.Fatalf("Failed to create temp dir: %v", err) - } - defer os.RemoveAll(tmpDir) - - // Create two subdirectories with memory files with the same basename - dir1 := filepath.Join(tmpDir, "dir1") - dir2 := filepath.Join(tmpDir, "dir2") - if err := os.MkdirAll(dir1, 0755); err != nil { - t.Fatalf("Failed to create dir1: %v", err) - } - if err := os.MkdirAll(dir2, 0755); err != nil { - t.Fatalf("Failed to create dir2: %v", err) - } - - memory1Content := `--- ---- -This is the first memory.` - memory1Path := filepath.Join(dir1, "memory.md") - if err := os.WriteFile(memory1Path, []byte(memory1Content), 0644); err != nil { - t.Fatalf("Failed to write memory.md in dir1: %v", err) - } - - memory2Content := `--- ---- -This is the second memory.` - memory2Path := filepath.Join(dir2, "memory.md") - if err := os.WriteFile(memory2Path, []byte(memory2Content), 0644); err != nil { - t.Fatalf("Failed to write memory.md in dir2: %v", err) - } - - // Create a dummy task file - taskContent := "This is the task." - taskPath := filepath.Join(tmpDir, "task.md") - if err := os.WriteFile(taskPath, []byte(taskContent), 0644); err != nil { - t.Fatalf("Failed to write task.md: %v", err) - } - - // Set up the arguments for the run function - args := []string{"task"} - memories = []string{dir1, dir2} - tasks = []string{tmpDir} - outputDir = tmpDir - params = make(paramMap) - includes = make(selectorMap) - excludes = make(selectorMap) - runBootstrap = false - workDir = tmpDir - - // Run the application - if err := run(context.Background(), args); err != nil { - t.Fatalf("run() failed: %v", err) - } - - // Check the output - memoriesBytes, err := os.ReadFile(filepath.Join(tmpDir, "memories.md")) - if err != nil { - t.Fatalf("Failed to read memories.md: %v", err) - } - memoriesContent := string(memoriesBytes) - - // We expect only one of the memories to be included - hasFirst := strings.Contains(memoriesContent, "This is the first memory.") - hasSecond := strings.Contains(memoriesContent, "This is the second memory.") - if hasFirst == hasSecond { - t.Errorf("Expected only one memory to be included, but got: %s", memoriesContent) - } -} From 5b3e1766172b65139060d37a7bbee8301da04015 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 16:26:48 +0000 Subject: [PATCH 3/5] Update README.md to reflect memories->rules rename and remove deduplication docs Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- README.md | 152 +++--- README.md.backup | 1183 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1247 insertions(+), 88 deletions(-) create mode 100644 README.md.backup diff --git a/README.md b/README.md index 40dc0967..d8577ece 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Coding Context CLI -A CLI tool for managing context files for coding agents. It helps you organize prompts, memories (reusable context), and bootstrap scripts that can be assembled into a single context file for AI coding agents. +A CLI tool for managing context files for coding agents. It helps you organize prompts, rules (reusable context), and bootstrap scripts that can be assembled into a single context file for AI coding agents. ## Why Use This? @@ -13,11 +13,11 @@ When working with AI coding agents (like GitHub Copilot, ChatGPT, Claude, etc.), **This tool solves these problems by:** -1. **Centralizing reusable context** - Store project conventions, coding standards, and setup instructions once in "memory" files +1. **Centralizing reusable context** - Store project conventions, coding standards, and setup instructions once in "rule" files 2. **Creating task-specific prompts** - Define templated prompts for common tasks (e.g., "add feature", "fix bug", "refactor") 3. **Automating environment setup** - Package bootstrap scripts that prepare the environment before an agent starts work 4. **Filtering context dynamically** - Use selectors to include only relevant context (e.g., production vs. development, Python vs. Go) -5. **Composing everything together** - Generate three separate markdown files: `persona.md`, `memories.md`, and `task.md` +5. **Composing everything together** - Generate three separate markdown files: `persona.md`, `rules.md`, and `task.md` ## When to Use @@ -33,27 +33,27 @@ This tool is ideal for: The basic workflow is: -1. **Organize your context** - Create persona files (optional), memory files (shared context), and task files (task-specific instructions) +1. **Organize your context** - Create persona files (optional), rule files (shared context), and task files (task-specific instructions) 2. **Run the CLI** - Execute `coding-context [options] [persona-name]` 3. **Get assembled output** - The tool generates: - `persona.md` - Persona content (always created, can be empty if no persona is specified) - - `memories.md` - All included memory files combined + - `rules.md` - All included rule files combined - `task.md` - Task prompt with template variables filled in - `bootstrap` - Executable script to set up the environment - - `bootstrap.d/` - Individual bootstrap scripts from your memory files + - `bootstrap.d/` - Individual bootstrap scripts from your rule files 4. **Use with AI agents** - Share the generated markdown files with your AI coding agent, or run `./bootstrap` to prepare the environment first **Visual flow:** ``` +----------------------+ +---------------------+ +--------------------------+ -| Persona File (*.md) | | Memory Files (*.md) | | Task Template | +| Persona File (*.md) | | Rule Files (*.md) | | Task Template | | (optional) | | | | (task-name.md) | +----------+-----------+ +----------+----------+ +------------+-------------+ | | | | No expansion | Filter by selectors | Apply template params v v v +----------------------+ +---------------------+ +--------------------------+ -| persona.md | | memories.md | | task.md | +| persona.md | | rules.md | | task.md | +----------------------+ +---------------------+ +--------------------------+ ``` @@ -74,18 +74,18 @@ coding-context [options] [persona-name] Options: -b Automatically run the bootstrap script after generating it -C Change to directory before doing anything (default: .) - -m Directory containing memories, or a single memory file (can be used multiple times) + -m Directory containing rules, or a single rule file (can be used multiple times) Defaults: AGENTS.md, .github/copilot-instructions.md, CLAUDE.md, .cursorrules, - .cursor/rules/, .instructions.md, .continuerules, .prompts/memories, - ~/.config/prompts/memories, /var/local/prompts/memories + .cursor/rules/, .instructions.md, .continuerules, .prompts/rules, + ~/.config/prompts/rules, /var/local/prompts/rules -r Directory containing personas, or a single persona file (can be used multiple times) Defaults: .prompts/personas, ~/.config/prompts/personas, /var/local/prompts/personas -t Directory containing tasks, or a single task file (can be used multiple times) Defaults: .prompts/tasks, ~/.config/prompts/tasks, /var/local/prompts/tasks -o Output directory for generated files (default: .) -p Template parameter for prompt substitution (can be used multiple times) - -s Include memories with matching frontmatter (can be used multiple times) - -S Exclude memories with matching frontmatter (can be used multiple times) + -s Include rules with matching frontmatter (can be used multiple times) + -S Exclude rules with matching frontmatter (can be used multiple times) ``` **Important:** The task file name **MUST** match the task name you provide on the command line. For example, if you run `coding-context my-task`, the tool will look for `my-task.md` in the task directories. @@ -101,9 +101,9 @@ coding-context -p feature="Authentication" -p language=Go add-feature coding-context add-feature expert ``` -**Example with custom memory and task paths:** +**Example with custom rule and task paths:** ```bash -# Specify explicit memory files or directories +# Specify explicit rule files or directories coding-context -m .github/copilot-instructions.md -m CLAUDE.md my-task # Specify custom task directory @@ -112,10 +112,10 @@ coding-context -t ./custom-tasks my-task **Example with selectors:** ```bash -# Include only production memories +# Include only production rules coding-context -s env=production deploy -# Exclude test memories +# Exclude test rules coding-context -S env=test deploy # Combine include and exclude selectors @@ -128,12 +128,12 @@ This guide shows how to set up and generate your first context: **Step 1: Create a context directory structure** ```bash -mkdir -p .prompts/{tasks,memories,personas} +mkdir -p .prompts/{tasks,rules,personas} ``` -**Step 2: Create a memory file** (`.prompts/memories/project-info.md`) +**Step 2: Create a rule file** (`.prompts/rules/project-info.md`) -Memory files are included in every generated context. They contain reusable information like project conventions, architecture notes, or coding standards. +Rule files are included in every generated context. They contain reusable information like project conventions, architecture notes, or coding standards. ```markdown # Project Context @@ -174,7 +174,7 @@ coding-context -p taskName="Fix Bug" -p language=Go my-task coding-context -p taskName="Fix Bug" -p language=Go my-task expert ``` -**Result:** This generates three files: `./persona.md` (if persona is specified), `./memories.md`, and `./task.md` with template variables filled in. You can now share these files with your AI coding agent! +**Result:** This generates three files: `./persona.md` (if persona is specified), `./rules.md`, and `./task.md` with template variables filled in. You can now share these files with your AI coding agent! **What you'll see in the generated files (with persona):** @@ -185,7 +185,7 @@ coding-context -p taskName="Fix Bug" -p language=Go my-task expert You are an expert developer with deep knowledge of best practices. ``` -`memories.md`: +`rules.md`: ```markdown # Project Context @@ -215,7 +215,7 @@ Each directory should contain: │ └── .md ├── tasks/ # Task-specific prompt templates │ └── .md -└── memories/ # Reusable context files (included in all outputs) +└── rules/ # Reusable context files (included in all outputs) └── *.md ``` @@ -241,7 +241,7 @@ Run with: coding-context add-feature expert ``` -This will look for `expert.md` in the persona directories and output it to `persona.md`. The persona is optional – if you don't specify a persona name as the second argument, `persona.md` will still be generated but will be empty, alongside `memories.md` and `task.md`. +This will look for `expert.md` in the persona directories and output it to `persona.md`. The persona is optional – if you don't specify a persona name as the second argument, `persona.md` will still be generated but will be empty, alongside `rules.md` and `task.md`. ### Prompt Files @@ -265,11 +265,11 @@ coding-context -p feature="User Login" -p language=Go add-feature This will look for `add-feature.md` in the task directories. -### Memory Files +### Rule Files Markdown files included in every generated context. Bootstrap scripts can be provided in separate files. -**Example** (`.prompts/memories/setup.md`): +**Example** (`.prompts/rules/setup.md`): ```markdown --- env: development @@ -280,23 +280,23 @@ language: go This project requires Node.js dependencies. ``` -**Bootstrap file** (`.prompts/memories/setup-bootstrap`): +**Bootstrap file** (`.prompts/rules/setup-bootstrap`): ```bash #!/bin/bash npm install ``` -For each memory file `.md`, you can optionally create a corresponding `-bootstrap` file that will be executed during setup. +For each rule file `.md`, you can optionally create a corresponding `-bootstrap` file that will be executed during setup. -### Supported Memory File Formats +### Supported Rule File Formats -This tool can work with various memory file formats used by popular AI coding assistants. By default, it looks for `AGENTS.md` in the current directory. You can also specify additional memory files or directories using the `-m` flag. +This tool can work with various rule file formats used by popular AI coding assistants. By default, it looks for `AGENTS.md` in the current directory. You can also specify additional rule files or directories using the `-m` flag. -#### Common Memory File Names +#### Common Rule File Names -The following memory file formats are commonly used by AI coding assistants and can be used with this tool: +The following rule file formats are commonly used by AI coding assistants and can be used with this tool: -- **`AGENTS.md`** - Default memory file (automatically included) +- **`AGENTS.md`** - Default rule file (automatically included) - **`.github/copilot-instructions.md`** - GitHub Copilot instructions file - **`CLAUDE.md`** - Claude-specific instructions - **`.cursorrules`** - Cursor editor rules (if in Markdown format) @@ -304,7 +304,7 @@ The following memory file formats are commonly used by AI coding assistants and - **`.instructions.md`** - General instructions file - **`.continuerules`** - Continue.dev rules (if in Markdown format) -**Example:** Using multiple memory sources +**Example:** Using multiple rule sources ```bash # Include GitHub Copilot instructions and CLAUDE.md coding-context -m .github/copilot-instructions.md -m CLAUDE.md my-task @@ -312,27 +312,27 @@ coding-context -m .github/copilot-instructions.md -m CLAUDE.md my-task # Include all rules from Cursor directory coding-context -m .cursor/rules/ my-task -# Combine default AGENTS.md with additional memories +# Combine default AGENTS.md with additional rules coding-context -m .instructions.md my-task ``` -**Note:** All memory files should be in Markdown format (`.md` extension) or contain Markdown-compatible content. The tool will automatically process frontmatter in YAML format if present. +**Note:** All rule files should be in Markdown format (`.md` extension) or contain Markdown-compatible content. The tool will automatically process frontmatter in YAML format if present. -## Filtering Memories with Selectors +## Filtering Rules with Selectors -Use the `-s` and `-S` flags to filter which memory files are included based on their frontmatter metadata. +Use the `-s` and `-S` flags to filter which rule files are included based on their frontmatter metadata. ### Selector Syntax -- **`-s key=value`** - Include memories where the frontmatter key matches the value -- **`-S key=value`** - Exclude memories where the frontmatter key matches the value -- If a key doesn't exist in a memory's frontmatter, the memory is allowed (not filtered out) +- **`-s key=value`** - Include rules where the frontmatter key matches the value +- **`-S key=value`** - Exclude rules where the frontmatter key matches the value +- If a key doesn't exist in a rule's frontmatter, the rule is allowed (not filtered out) - Multiple selectors of the same type use AND logic (all must match) ### Examples -**Include only production memories:** +**Include only production rules:** ```bash coding-context -s env=production deploy ``` @@ -350,7 +350,7 @@ coding-context -s env=production -S language=python deploy **Multiple includes:** ```bash -# Only production Go backend memories +# Only production Go backend rules coding-context -s env=production -s language=go -s tier=backend deploy ``` @@ -359,46 +359,22 @@ coding-context -s env=production -s language=go -s tier=backend deploy When you run with selectors, the tool logs which files are included or excluded: ``` -INFO Including memory file path=.prompts/memories/production.md -INFO Excluding memory file (does not match include selectors) path=.prompts/memories/development.md -INFO Including memory file path=.prompts/memories/nofrontmatter.md +INFO Including rule file path=.prompts/rules/production.md +INFO Excluding rule file (does not match include selectors) path=.prompts/rules/development.md +INFO Including rule file path=.prompts/rules/nofrontmatter.md ``` -**Important:** Files without the specified frontmatter keys are still included. This allows you to have generic memories that apply to all scenarios. +**Important:** Files without the specified frontmatter keys are still included. This allows you to have generic rules that apply to all scenarios. -If no selectors are specified, all memory files are included. - -### Deduplicating Memories - -When you have multiple memory files with the same filename (basename), only the first one encountered will be included. This allows you to override default memories with project-specific ones by using the same filename. - -**Example:** - -If you have two directories with memory files: - -`~/.coding-context/memories/general/setup.md`: -```markdown ---- ---- -This is the default setup memory. -``` - -`./memories/setup.md`: -```markdown ---- ---- -This is a project-specific setup memory. -``` - -When the tool processes these two files, it will include only one of them based on which is encountered first during filesystem traversal. **The order depends on the order of memory paths specified and filesystem traversal order, which is not guaranteed to be alphabetical or consistent.** This mechanism is useful for overriding default memories with project-specific ones when you use the same filename. +If no selectors are specified, all rule files are included. ## Output Files - **`persona.md`** - Persona content (always created, can be empty if no persona is specified) -- **`memories.md`** - Combined output with all filtered memory files +- **`rules.md`** - Combined output with all filtered rule files - **`task.md`** - Task prompt with template variables expanded -- **`bootstrap`** - Executable script that runs all bootstrap scripts from memories +- **`bootstrap`** - Executable script that runs all bootstrap scripts from rules - **`bootstrap.d/`** - Individual bootstrap scripts (SHA256 named) Run the bootstrap script to set up your environment: @@ -418,10 +394,10 @@ coding-context -b my-task ```bash # Create structure -mkdir -p .prompts/{tasks,memories} +mkdir -p .prompts/{tasks,rules} -# Add a memory -cat > .prompts/memories/conventions.md << 'EOF' +# Add a rule +cat > .prompts/rules/conventions.md << 'EOF' # Coding Conventions - Use tabs for indentation @@ -454,17 +430,17 @@ coding-context -p featureName="Authentication" -p language=Go add-feature ### With Bootstrap Scripts ```bash -cat > .prompts/memories/setup.md << 'EOF' +cat > .prompts/rules/setup.md << 'EOF' # Project Setup This Go project uses modules. EOF -cat > .prompts/memories/setup-bootstrap << 'EOF' +cat > .prompts/rules/setup-bootstrap << 'EOF' #!/bin/bash go mod download EOF -chmod +x .prompts/memories/setup-bootstrap +chmod +x .prompts/rules/setup-bootstrap coding-context -o ./output my-task cd output && ./bootstrap @@ -483,7 +459,7 @@ The bootstrap script mechanism is especially useful for integrating external CLI The `kitproj/jira-cli` tool allows agents to interact with Jira issues programmatically. Here's how to set it up: -**Step 1: Create a memory file with Jira context** (`.prompts/memories/jira.md`) +**Step 1: Create a rule file with Jira context** (`.prompts/rules/jira.md`) ```markdown # Jira Integration @@ -505,7 +481,7 @@ The Jira CLI is configured with: - Authentication: Token-based (set via JIRA_API_TOKEN environment variable) ``` -**Step 2: Create a bootstrap script** (`.prompts/memories/jira-bootstrap`) +**Step 2: Create a bootstrap script** (`.prompts/rules/jira-bootstrap`) ```bash #!/bin/bash @@ -521,7 +497,7 @@ sudo chmod +x /usr/local/bin/jira **Step 3: Make the bootstrap script executable** ```bash -chmod +x .prompts/memories/jira-bootstrap +chmod +x .prompts/rules/jira-bootstrap ``` **Step 4: Use with a task that needs Jira** @@ -537,7 +513,7 @@ Now when an agent starts work, the bootstrap script will ensure `jira-cli` is in The `kitproj/slack-cli` tool allows agents to send notifications and interact with Slack channels. Here's the setup: -**Step 1: Create a memory file with Slack context** (`.prompts/memories/slack.md`) +**Step 1: Create a rule file with Slack context** (`.prompts/rules/slack.md`) ```markdown # Slack Integration @@ -566,7 +542,7 @@ The Slack CLI requires: - Alert on failures: `slack send-message "#alerts" "Test suite failed on main branch"` ``` -**Step 2: Create a bootstrap script** (`.prompts/memories/slack-bootstrap`) +**Step 2: Create a bootstrap script** (`.prompts/rules/slack-bootstrap`) ```bash #!/bin/bash @@ -582,7 +558,7 @@ sudo chmod +x /usr/local/bin/slack **Step 3: Make the bootstrap script executable** ```bash -chmod +x .prompts/memories/slack-bootstrap +chmod +x .prompts/rules/slack-bootstrap ``` **Step 4: Create a task that uses Slack** (`.prompts/tasks/slack-deploy-alert.md`) @@ -1167,9 +1143,9 @@ When the same task exists in multiple directories, the first match wins: **"prompt file not found for task"** - Ensure `.md` exists in a `tasks/` subdirectory -**"failed to walk memory dir"** +**"failed to walk rule dir"** ```bash -mkdir -p .prompts/memories +mkdir -p .prompts/rules ``` **Template parameter not replaced (shows as `${variableName}`)** diff --git a/README.md.backup b/README.md.backup new file mode 100644 index 00000000..40dc0967 --- /dev/null +++ b/README.md.backup @@ -0,0 +1,1183 @@ +# Coding Context CLI + +A CLI tool for managing context files for coding agents. It helps you organize prompts, memories (reusable context), and bootstrap scripts that can be assembled into a single context file for AI coding agents. + +## Why Use This? + +When working with AI coding agents (like GitHub Copilot, ChatGPT, Claude, etc.), providing the right context is crucial for getting quality results. However, managing this context becomes challenging when: + +- **Context is scattered**: Project conventions, coding standards, and setup instructions are spread across multiple documents +- **Repetition is tedious**: You find yourself copy-pasting the same information into every AI chat session +- **Context size is limited**: AI models have token limits, so you need to efficiently select what's relevant +- **Onboarding is manual**: New team members or agents need step-by-step setup instructions + +**This tool solves these problems by:** + +1. **Centralizing reusable context** - Store project conventions, coding standards, and setup instructions once in "memory" files +2. **Creating task-specific prompts** - Define templated prompts for common tasks (e.g., "add feature", "fix bug", "refactor") +3. **Automating environment setup** - Package bootstrap scripts that prepare the environment before an agent starts work +4. **Filtering context dynamically** - Use selectors to include only relevant context (e.g., production vs. development, Python vs. Go) +5. **Composing everything together** - Generate three separate markdown files: `persona.md`, `memories.md`, and `task.md` + +## When to Use + +This tool is ideal for: + +- **Working with AI coding agents** - Prepare comprehensive context before starting a coding session +- **Team standardization** - Share common prompts and conventions across your team +- **Complex projects** - Manage large amounts of project-specific context efficiently +- **Onboarding automation** - New developers or agents can run bootstrap scripts to set up their environment +- **Multi-environment projects** - Filter context based on environment (dev/staging/prod) or technology stack + +## How It Works + +The basic workflow is: + +1. **Organize your context** - Create persona files (optional), memory files (shared context), and task files (task-specific instructions) +2. **Run the CLI** - Execute `coding-context [options] [persona-name]` +3. **Get assembled output** - The tool generates: + - `persona.md` - Persona content (always created, can be empty if no persona is specified) + - `memories.md` - All included memory files combined + - `task.md` - Task prompt with template variables filled in + - `bootstrap` - Executable script to set up the environment + - `bootstrap.d/` - Individual bootstrap scripts from your memory files +4. **Use with AI agents** - Share the generated markdown files with your AI coding agent, or run `./bootstrap` to prepare the environment first + +**Visual flow:** +``` ++----------------------+ +---------------------+ +--------------------------+ +| Persona File (*.md) | | Memory Files (*.md) | | Task Template | +| (optional) | | | | (task-name.md) | ++----------+-----------+ +----------+----------+ +------------+-------------+ + | | | + | No expansion | Filter by selectors | Apply template params + v v v ++----------------------+ +---------------------+ +--------------------------+ +| persona.md | | memories.md | | task.md | ++----------------------+ +---------------------+ +--------------------------+ +``` + +## Installation + +Download the binary for your platform from the release page: + +```bash +sudo curl -fsL -o /usr/local/bin/coding-context https://github.com/kitproj/coding-agent-context-cli/releases/download/v0.0.1/coding-context_v0.0.1_linux_arm64 +sudo chmod +x /usr/local/bin/coding-context +``` + +## Usage + +``` +coding-context [options] [persona-name] + +Options: + -b Automatically run the bootstrap script after generating it + -C Change to directory before doing anything (default: .) + -m Directory containing memories, or a single memory file (can be used multiple times) + Defaults: AGENTS.md, .github/copilot-instructions.md, CLAUDE.md, .cursorrules, + .cursor/rules/, .instructions.md, .continuerules, .prompts/memories, + ~/.config/prompts/memories, /var/local/prompts/memories + -r Directory containing personas, or a single persona file (can be used multiple times) + Defaults: .prompts/personas, ~/.config/prompts/personas, /var/local/prompts/personas + -t Directory containing tasks, or a single task file (can be used multiple times) + Defaults: .prompts/tasks, ~/.config/prompts/tasks, /var/local/prompts/tasks + -o Output directory for generated files (default: .) + -p Template parameter for prompt substitution (can be used multiple times) + -s Include memories with matching frontmatter (can be used multiple times) + -S Exclude memories with matching frontmatter (can be used multiple times) +``` + +**Important:** The task file name **MUST** match the task name you provide on the command line. For example, if you run `coding-context my-task`, the tool will look for `my-task.md` in the task directories. + +**Example:** +```bash +coding-context -p feature="Authentication" -p language=Go add-feature +``` + +**Example with persona:** +```bash +# Use a persona to set the context for the AI agent (persona is an optional positional argument) +coding-context add-feature expert +``` + +**Example with custom memory and task paths:** +```bash +# Specify explicit memory files or directories +coding-context -m .github/copilot-instructions.md -m CLAUDE.md my-task + +# Specify custom task directory +coding-context -t ./custom-tasks my-task +``` + +**Example with selectors:** +```bash +# Include only production memories +coding-context -s env=production deploy + +# Exclude test memories +coding-context -S env=test deploy + +# Combine include and exclude selectors +coding-context -s env=production -S language=python deploy +``` + +## Quick Start + +This guide shows how to set up and generate your first context: + +**Step 1: Create a context directory structure** +```bash +mkdir -p .prompts/{tasks,memories,personas} +``` + +**Step 2: Create a memory file** (`.prompts/memories/project-info.md`) + +Memory files are included in every generated context. They contain reusable information like project conventions, architecture notes, or coding standards. + +```markdown +# Project Context + +- Framework: Go CLI +- Purpose: Manage AI agent context +``` + +**Step 3: (Optional) Create a persona file** (`.prompts/personas/expert.md`) + +Persona files define the role or character the AI agent should assume. They appear first in the output and do NOT support template variable expansion. + +```markdown +# Expert Developer + +You are an expert developer with deep knowledge of best practices. +``` + +**Step 4: Create a prompt file** (`.prompts/tasks/my-task.md`) + +Prompt files define specific tasks. They can use template variables (like `${taskName}` or `$taskName`) that you provide via command-line parameters. + +**IMPORTANT:** The file name **MUST** match the task name you'll use on the command line. For example, a file named `my-task.md` is invoked with `coding-context my-task`. + +```markdown +# Task: ${taskName} + +Please help me with this task. The project uses ${language}. +``` + +**Step 5: Generate your context file** + +```bash +# Without persona +coding-context -p taskName="Fix Bug" -p language=Go my-task + +# With persona (as optional positional argument after task name) +coding-context -p taskName="Fix Bug" -p language=Go my-task expert +``` + +**Result:** This generates three files: `./persona.md` (if persona is specified), `./memories.md`, and `./task.md` with template variables filled in. You can now share these files with your AI coding agent! + +**What you'll see in the generated files (with persona):** + +`persona.md`: +```markdown +# Expert Developer + +You are an expert developer with deep knowledge of best practices. +``` + +`memories.md`: +```markdown +# Project Context + +- Framework: Go CLI +- Purpose: Manage AI agent context +``` + +`task.md`: +```markdown +# Task: Fix Bug + +Please help me with this task. The project uses Go. +``` + + +## Directory Structure + +The tool searches these directories for context files (in priority order): +1. `.prompts/` (project-local) +2. `~/.config/prompts/` (user-specific) +3. `/var/local/prompts/` (system-wide) + +Each directory should contain: +``` +.prompts/ +├── personas/ # Optional persona files (output first when specified) +│ └── .md +├── tasks/ # Task-specific prompt templates +│ └── .md +└── memories/ # Reusable context files (included in all outputs) + └── *.md +``` + + +## File Formats + +### Persona Files + +Optional persona files define the role or character the AI agent should assume. Personas are output to `persona.md` when specified. + +**Important:** Persona files do NOT support template variable expansion. They are included as-is in the output. + +**Example** (`.prompts/personas/expert.md`): +```markdown +# Expert Software Engineer + +You are an expert software engineer with deep knowledge of best practices. +You are known for writing clean, maintainable code and following industry standards. +``` + +Run with: +```bash +coding-context add-feature expert +``` + +This will look for `expert.md` in the persona directories and output it to `persona.md`. The persona is optional – if you don't specify a persona name as the second argument, `persona.md` will still be generated but will be empty, alongside `memories.md` and `task.md`. + +### Prompt Files + +Markdown files with YAML frontmatter and Go template support. + +**CRITICAL:** The prompt file name (without the `.md` extension) **MUST** exactly match the task name you provide on the command line. For example: +- To run `coding-context add-feature`, you need a file named `add-feature.md` +- To run `coding-context my-custom-task`, you need a file named `my-custom-task.md` + +**Example** (`.prompts/tasks/add-feature.md`): +```markdown +# Task: ${feature} + +Implement ${feature} in ${language}. +``` + +Run with: +```bash +coding-context -p feature="User Login" -p language=Go add-feature +``` + +This will look for `add-feature.md` in the task directories. + +### Memory Files + +Markdown files included in every generated context. Bootstrap scripts can be provided in separate files. + +**Example** (`.prompts/memories/setup.md`): +```markdown +--- +env: development +language: go +--- +# Development Setup + +This project requires Node.js dependencies. +``` + +**Bootstrap file** (`.prompts/memories/setup-bootstrap`): +```bash +#!/bin/bash +npm install +``` + +For each memory file `.md`, you can optionally create a corresponding `-bootstrap` file that will be executed during setup. + +### Supported Memory File Formats + +This tool can work with various memory file formats used by popular AI coding assistants. By default, it looks for `AGENTS.md` in the current directory. You can also specify additional memory files or directories using the `-m` flag. + +#### Common Memory File Names + +The following memory file formats are commonly used by AI coding assistants and can be used with this tool: + +- **`AGENTS.md`** - Default memory file (automatically included) +- **`.github/copilot-instructions.md`** - GitHub Copilot instructions file +- **`CLAUDE.md`** - Claude-specific instructions +- **`.cursorrules`** - Cursor editor rules (if in Markdown format) +- **`.cursor/rules/`** - Directory containing Cursor-specific rule files +- **`.instructions.md`** - General instructions file +- **`.continuerules`** - Continue.dev rules (if in Markdown format) + +**Example:** Using multiple memory sources +```bash +# Include GitHub Copilot instructions and CLAUDE.md +coding-context -m .github/copilot-instructions.md -m CLAUDE.md my-task + +# Include all rules from Cursor directory +coding-context -m .cursor/rules/ my-task + +# Combine default AGENTS.md with additional memories +coding-context -m .instructions.md my-task +``` + +**Note:** All memory files should be in Markdown format (`.md` extension) or contain Markdown-compatible content. The tool will automatically process frontmatter in YAML format if present. + + +## Filtering Memories with Selectors + +Use the `-s` and `-S` flags to filter which memory files are included based on their frontmatter metadata. + +### Selector Syntax + +- **`-s key=value`** - Include memories where the frontmatter key matches the value +- **`-S key=value`** - Exclude memories where the frontmatter key matches the value +- If a key doesn't exist in a memory's frontmatter, the memory is allowed (not filtered out) +- Multiple selectors of the same type use AND logic (all must match) + +### Examples + +**Include only production memories:** +```bash +coding-context -s env=production deploy +``` + +**Exclude test environment:** +```bash +coding-context -S env=test deploy +``` + +**Combine include and exclude:** +```bash +# Include production but exclude python +coding-context -s env=production -S language=python deploy +``` + +**Multiple includes:** +```bash +# Only production Go backend memories +coding-context -s env=production -s language=go -s tier=backend deploy +``` + +### How It Works + +When you run with selectors, the tool logs which files are included or excluded: + +``` +INFO Including memory file path=.prompts/memories/production.md +INFO Excluding memory file (does not match include selectors) path=.prompts/memories/development.md +INFO Including memory file path=.prompts/memories/nofrontmatter.md +``` + +**Important:** Files without the specified frontmatter keys are still included. This allows you to have generic memories that apply to all scenarios. + +If no selectors are specified, all memory files are included. + +### Deduplicating Memories + +When you have multiple memory files with the same filename (basename), only the first one encountered will be included. This allows you to override default memories with project-specific ones by using the same filename. + +**Example:** + +If you have two directories with memory files: + +`~/.coding-context/memories/general/setup.md`: +```markdown +--- +--- +This is the default setup memory. +``` + +`./memories/setup.md`: +```markdown +--- +--- +This is a project-specific setup memory. +``` + +When the tool processes these two files, it will include only one of them based on which is encountered first during filesystem traversal. **The order depends on the order of memory paths specified and filesystem traversal order, which is not guaranteed to be alphabetical or consistent.** This mechanism is useful for overriding default memories with project-specific ones when you use the same filename. + + +## Output Files + +- **`persona.md`** - Persona content (always created, can be empty if no persona is specified) +- **`memories.md`** - Combined output with all filtered memory files +- **`task.md`** - Task prompt with template variables expanded +- **`bootstrap`** - Executable script that runs all bootstrap scripts from memories +- **`bootstrap.d/`** - Individual bootstrap scripts (SHA256 named) + +Run the bootstrap script to set up your environment: +```bash +./bootstrap +``` + +Or use the `-b` flag to automatically run the bootstrap script after generating it: +```bash +coding-context -b my-task +``` + + +## Examples + +### Basic Usage + +```bash +# Create structure +mkdir -p .prompts/{tasks,memories} + +# Add a memory +cat > .prompts/memories/conventions.md << 'EOF' +# Coding Conventions + +- Use tabs for indentation +- Write tests for all functions +EOF + +# Create a task prompt +cat > .prompts/tasks/refactor.md << 'EOF' +# Refactoring Task + +Please refactor the codebase to improve code quality. +EOF + +# Generate context +coding-context refactor +``` + +### With Template Parameters + +```bash +cat > .prompts/tasks/add-feature.md << 'EOF' +# Add Feature: ${featureName} + +Implement ${featureName} in ${language}. +EOF + +coding-context -p featureName="Authentication" -p language=Go add-feature +``` + +### With Bootstrap Scripts + +```bash +cat > .prompts/memories/setup.md << 'EOF' +# Project Setup + +This Go project uses modules. +EOF + +cat > .prompts/memories/setup-bootstrap << 'EOF' +#!/bin/bash +go mod download +EOF +chmod +x .prompts/memories/setup-bootstrap + +coding-context -o ./output my-task +cd output && ./bootstrap +``` + +Alternatively, use the `-b` flag to automatically run the bootstrap script: +```bash +coding-context -o ./output -b my-task +``` + +### Integrating External CLI Tools + +The bootstrap script mechanism is especially useful for integrating external CLI tools like `kitproj/jira-cli` and `kitproj/slack-cli`. These tools can be installed automatically when an agent starts working on a task. + +#### Example: Using kitproj/jira-cli + +The `kitproj/jira-cli` tool allows agents to interact with Jira issues programmatically. Here's how to set it up: + +**Step 1: Create a memory file with Jira context** (`.prompts/memories/jira.md`) + +```markdown +# Jira Integration + +This project uses Jira for issue tracking. The `jira` CLI tool is available for interacting with issues. + +## Available Commands + +- `jira get-issue ` - Get details of a Jira issue +- `jira get-comments ` - Get all comments on an issue +- `jira add-comment ` - Add a comment to an issue +- `jira update-issue-status ` - Update the status of an issue +- `jira create-issue ` - Create a new issue + +## Configuration + +The Jira CLI is configured with: +- Server URL: https://your-company.atlassian.net +- Authentication: Token-based (set via JIRA_API_TOKEN environment variable) +``` + +**Step 2: Create a bootstrap script** (`.prompts/memories/jira-bootstrap`) + +```bash +#!/bin/bash +set -euo pipefail + +VERSION="v0.1.0" # Update to the latest version +BINARY_URL="https://github.com/kitproj/jira-cli/releases/download/${VERSION}/jira-cli_${VERSION}_linux_amd64" + +sudo curl -fsSL -o /usr/local/bin/jira "$BINARY_URL" +sudo chmod +x /usr/local/bin/jira +``` + +**Step 3: Make the bootstrap script executable** + +```bash +chmod +x .prompts/memories/jira-bootstrap +``` + +**Step 4: Use with a task that needs Jira** + +```bash +# The bootstrap will automatically run when you generate context +coding-context -b -p storyId="PROJ-123" implement-jira-story +``` + +Now when an agent starts work, the bootstrap script will ensure `jira-cli` is installed and ready to use! + +#### Example: Using kitproj/slack-cli + +The `kitproj/slack-cli` tool allows agents to send notifications and interact with Slack channels. Here's the setup: + +**Step 1: Create a memory file with Slack context** (`.prompts/memories/slack.md`) + +```markdown +# Slack Integration + +This project uses Slack for team communication. The `slack` CLI tool is available for sending messages and notifications. + +## Available Commands + +- `slack send-message ` - Send a message to a channel +- `slack send-thread-reply ` - Reply to a thread +- `slack upload-file ` - Upload a file to a channel +- `slack set-status ` - Set your Slack status +- `slack get-channel-history ` - Get recent messages from a channel + +## Configuration + +The Slack CLI requires: +- Workspace: your-workspace.slack.com +- Authentication: Bot token (set via SLACK_BOT_TOKEN environment variable) +- Channels: Use channel IDs or names (e.g., #engineering, #alerts) + +## Common Use Cases + +- Send build notifications: `slack send-message "#builds" "Build completed successfully"` +- Report deployment status: `slack send-message "#deployments" "Production deployment started"` +- Alert on failures: `slack send-message "#alerts" "Test suite failed on main branch"` +``` + +**Step 2: Create a bootstrap script** (`.prompts/memories/slack-bootstrap`) + +```bash +#!/bin/bash +set -euo pipefail + +VERSION="v0.1.0" # Update to the latest version +BINARY_URL="https://github.com/kitproj/slack-cli/releases/download/${VERSION}/slack-cli_${VERSION}_linux_amd64" + +sudo curl -fsSL -o /usr/local/bin/slack "$BINARY_URL" +sudo chmod +x /usr/local/bin/slack +``` + +**Step 3: Make the bootstrap script executable** + +```bash +chmod +x .prompts/memories/slack-bootstrap +``` + +**Step 4: Create a task that uses Slack** (`.prompts/tasks/slack-deploy-alert.md`) + +```markdown +# Slack Deployment Alert: ${environment} + +## Task + +Send a deployment notification to the team via Slack. + +## Steps + +1. **Prepare the notification message** + - Include environment: ${environment} + - Include deployment status + - Include relevant details (version, commit, etc.) + +2. **Send to appropriate channels** + ```bash + slack send-message "#deployments" "🚀 Deployment to ${environment} started" + ``` + +3. **Update on completion** + ```bash + slack send-message "#deployments" "✅ Deployment to ${environment} completed successfully" + ``` + +4. **Alert on failures** (if needed) + ```bash + slack send-message "#alerts" "❌ Deployment to ${environment} failed. Check logs for details." + ``` + +## Success Criteria +- Team is notified of deployment status +- Appropriate channels receive updates +- Messages are clear and actionable +``` + +**Step 5: Use the task** + +```bash +coding-context -p environment="production" slack-deploy-alert +./bootstrap # Installs slack-cli if needed +``` + +#### Writing Bootstrap Scripts - Best Practices + +When writing bootstrap scripts for external CLI tools: + +1. **Check if already installed** - Avoid reinstalling if the tool exists + ```bash + if ! command -v toolname &> /dev/null; then + # Install logic here + fi + ``` + +2. **Use specific versions** - Pin to a specific version for reproducibility + ```bash + VERSION="v0.1.0" + ``` + +3. **Set error handling** - Use `set -euo pipefail` to catch errors early + ```bash + #!/bin/bash + set -euo pipefail + ``` + +4. **Verify installation** - Check that the tool works after installation + ```bash + toolname --version + ``` + +5. **Provide clear output** - Echo messages to show progress + ```bash + echo "Installing toolname..." + echo "Installation complete" + ``` + +### Real-World Task Examples + +Here are some practical task templates for common development workflows: + +#### Implement Jira Story + +**Note:** This example assumes you've set up the Jira CLI integration as shown in the [Using kitproj/jira-cli](#example-using-kitprojjira-cli) section above. The bootstrap script will automatically install the `jira` command. + +```bash +cat > .prompts/tasks/implement-jira-story.md << 'EOF' +# Implement Jira Story: ${storyId} + +## Story Details + +First, get the full story details from Jira: + + jira get-issue ${storyId} + +## Requirements + +Please implement the feature described in the Jira story. Follow these steps: + +1. **Review the Story** + - Read the story details, acceptance criteria, and comments + - Get all comments: `jira get-comments ${storyId}` + - Clarify any uncertainties by adding comments: `jira add-comment ${storyId} "Your question"` + +2. **Start Development** + - Create a feature branch with the story ID in the name (e.g., `feature/${storyId}-implement-auth`) + - Move the story to "In Progress": `jira update-issue-status ${storyId} "In Progress"` + +3. **Implementation** + - Design the solution following project conventions + - Implement the feature with proper error handling + - Add comprehensive unit tests (aim for >80% coverage) + - Update documentation if needed + - Ensure all tests pass and code is lint-free + +4. **Update Jira Throughout** + - Add progress updates: `jira add-comment ${storyId} "Completed implementation, working on tests"` + - Keep stakeholders informed of any blockers or changes + +5. **Complete the Story** + - Ensure all acceptance criteria are met + - Create a pull request + - Move to review: `jira update-issue-status ${storyId} "In Review"` + - Once merged, close: `jira update-issue-status ${storyId} "Done"` + +## Success Criteria +- All acceptance criteria are met +- Code follows project coding standards +- Tests are passing +- Documentation is updated +- Jira story is properly tracked through workflow +EOF + +# Usage +coding-context -p storyId="PROJ-123" implement-jira-story +``` + +#### Triage Jira Bug + +**Note:** This example requires the Jira CLI integration. See [Using kitproj/jira-cli](#example-using-kitprojjira-cli) for setup instructions. + +```bash +cat > .prompts/tasks/triage-jira-bug.md << 'EOF' +# Triage Jira Bug: ${bugId} + +## Get Bug Details + +First, retrieve the full bug report from Jira: + + jira get-issue ${bugId} + jira get-comments ${bugId} + +## Triage Steps + +1. **Acknowledge and Take Ownership** + - Add initial comment: `jira add-comment ${bugId} "Triaging this bug now"` + - Move to investigation: `jira update-issue-status ${bugId} "In Progress"` + +2. **Reproduce the Issue** + - Follow the steps to reproduce in the bug report + - Verify the issue exists in the reported environment + - Document actual vs. expected behavior + - Update Jira: `jira add-comment ${bugId} "Reproduced on [environment]. Actual: [X], Expected: [Y]"` + +3. **Investigate Root Cause** + - Review relevant code and logs + - Identify the component/module causing the issue + - Determine if this is a regression (check git history) + - Document findings: `jira add-comment ${bugId} "Root cause: [description]"` + +4. **Assess Impact** + - How many users are affected? + - Is there a workaround available? + - What is the risk if left unfixed? + - Add assessment: `jira add-comment ${bugId} "Impact: [severity]. Workaround: [yes/no]. Affected users: [estimate]"` + +5. **Provide Triage Report** + - Root cause analysis + - Recommended priority level + - Estimated effort to fix + - Suggested assignee/team + - Final summary: `jira add-comment ${bugId} "Triage complete. Priority: [level]. Effort: [estimate]. Recommended assignee: [name]"` + +## Output +Provide a detailed triage report with your findings and recommendations, and post it as a comment to the Jira issue. +EOF + +# Usage +coding-context -p bugId="PROJ-456" triage-jira-bug +``` + +#### Respond to Jira Comment + +**Note:** This example requires the Jira CLI integration. See [Using kitproj/jira-cli](#example-using-kitprojjira-cli) for setup instructions. + +```bash +cat > .prompts/tasks/respond-to-jira-comment.md << 'EOF' +# Respond to Jira Comment: ${issueId} + +## Get Issue and Comments + +First, retrieve the issue details and all comments: + + jira get-issue ${issueId} + jira get-comments ${issueId} + +Review the latest comment and the full context of the issue. + +## Instructions + +Please analyze the comment and provide a professional response: + +1. **Acknowledge** the comment and any concerns raised +2. **Address** each question or point made +3. **Provide** technical details or clarifications as needed +4. **Suggest** next steps or actions if appropriate +5. **Maintain** a collaborative and helpful tone + +## Response Guidelines +- Be clear and concise +- Provide code examples if relevant +- Link to documentation when helpful +- Offer to discuss further if needed + +## Post Your Response + +Once you've formulated your response, add it to the Jira issue: + + jira add-comment ${issueId} "Your detailed response here" + +If the comment requires action on your part, update the issue status accordingly: + + jira update-issue-status ${issueId} "In Progress" + +EOF + +# Usage +coding-context -p issueId="PROJ-789" respond-to-jira-comment +``` + +#### Send Slack Notification on Build Completion + +**Note:** This example requires the Slack CLI integration. See [Using kitproj/slack-cli](#example-using-kitprojslack-cli) for setup instructions. + +```bash +cat > .prompts/tasks/notify-build-status.md << 'EOF' +# Notify Build Status: ${buildStatus} + +## Task + +Send a build status notification to the team via Slack. + +## Build Information +- Status: ${buildStatus} +- Branch: ${branch} +- Commit: ${commit} +- Build Time: ${buildTime} + +## Steps + +1. **Prepare the notification message** + - Determine the appropriate emoji based on status + - Include all relevant build details + - Add links to build logs or artifacts + +2. **Send notification to #builds channel** + + For successful builds: + + slack send-message "#builds" "✅ Build succeeded on ${branch} + Commit: ${commit} + Time: ${buildTime} + Status: ${buildStatus}" + + For failed builds: + + slack send-message "#builds" "❌ Build failed on ${branch} + Commit: ${commit} + Time: ${buildTime} + Status: ${buildStatus} + Please check the build logs for details." + +3. **Alert in #alerts channel for failures** (if build failed) + + slack send-message "#alerts" "🚨 Build failure detected on ${branch}. Immediate attention needed." + +4. **Update thread if this is a rebuild** + If responding to a previous build notification: + + slack send-thread-reply "#builds" "" "Rebuild completed: ${buildStatus}" + +## Success Criteria +- Appropriate channels are notified +- Message includes all relevant details +- Team can quickly assess build status +- Failed builds trigger alerts +EOF + +# Usage +coding-context -p buildStatus="SUCCESS" -p branch="main" -p commit="abc123" -p buildTime="2m 30s" notify-build-status +``` + +#### Post Deployment Notification to Slack + +**Note:** This example requires the Slack CLI integration. See [Using kitproj/slack-cli](#example-using-kitprojslack-cli) for setup instructions. + +```bash +cat > .prompts/tasks/notify-deployment.md << 'EOF' +# Notify Deployment: ${environment} + +## Task + +Communicate deployment status to stakeholders via Slack. + +## Deployment Details +- Environment: ${environment} +- Version: ${version} +- Deployer: ${deployer} + +## Instructions + +1. **Announce deployment start** + + slack send-message "#deployments" "🚀 Deployment to ${environment} started + Version: ${version} + Deployer: ${deployer} + Started at: $(date)" + +2. **Monitor deployment progress** + - Track deployment steps + - Note any issues or delays + +3. **Send completion notification** + + For successful deployments: + + slack send-message "#deployments" "✅ Deployment to ${environment} completed successfully + Version: ${version} + Completed at: $(date) + All services are healthy and running." + + For failed deployments: + + slack send-message "#deployments" "❌ Deployment to ${environment} failed + Version: ${version} + Failed at: $(date) + Rolling back to previous version..." + +4. **Alert stakeholders for production deployments** + + slack send-message "#general" "📢 Production deployment completed: version ${version} is now live!" + +5. **Update status thread** + - Reply to the initial announcement with final status + - Include any post-deployment tasks or notes + +## Success Criteria +- Deployment timeline is clearly communicated +- All stakeholders are informed +- Status updates are timely and accurate +- Issues are escalated appropriately +EOF + +# Usage +coding-context -p environment="production" -p version="v2.1.0" -p deployer="deploy-bot" notify-deployment +``` + +#### Review Pull Request + +```bash +cat > .prompts/tasks/review-pull-request.md << 'EOF' +# Review Pull Request: ${prNumber} + +## PR Details +- PR #${prNumber} +- Author: ${author} +- Title: ${title} + +## Review Checklist + +### Code Quality +- [ ] Code follows project style guidelines +- [ ] No obvious bugs or logic errors +- [ ] Error handling is appropriate +- [ ] No security vulnerabilities introduced +- [ ] Performance considerations addressed + +### Testing +- [ ] Tests are included for new functionality +- [ ] Tests cover edge cases +- [ ] All tests pass +- [ ] Test quality is high (clear, maintainable) + +### Documentation +- [ ] Public APIs are documented +- [ ] Complex logic has explanatory comments +- [ ] README updated if needed +- [ ] Breaking changes are noted + +### Architecture +- [ ] Changes align with project architecture +- [ ] No unnecessary dependencies added +- [ ] Code is modular and reusable +- [ ] Separation of concerns maintained + +## Instructions +Please review the pull request thoroughly and provide: +1. Constructive feedback on any issues found +2. Suggestions for improvements +3. Approval or request for changes +4. Specific line-by-line comments where helpful + +Be thorough but encouraging. Focus on learning and improvement. +EOF + +# Usage +coding-context -p prNumber="42" -p author="Jane" -p title="Add feature X" review-pull-request +``` + +#### Respond to Pull Request Comment + +```bash +cat > .prompts/tasks/respond-to-pull-request-comment.md << 'EOF' +# Respond to Pull Request Comment + +## PR Details +- PR #${prNumber} +- Reviewer: ${reviewer} +- File: ${file} + +## Comment +${comment} + +## Instructions + +Please address the pull request review comment: + +1. **Analyze** the feedback carefully +2. **Determine** if the comment is valid +3. **Respond** professionally: + - If you agree: Acknowledge and describe your fix + - If you disagree: Respectfully explain your reasoning + - If unclear: Ask clarifying questions + +4. **Make changes** if needed: + - Fix the issue raised + - Add tests if applicable + - Update documentation + - Ensure code still works + +5. **Reply** with: + - What you changed (with commit reference) + - Why you made that choice + - Any additional context needed + +## Tone +Be collaborative, open to feedback, and focused on code quality. +EOF + +# Usage +coding-context -p prNumber="42" -p reviewer="Bob" -p file="main.go" -p comment="Consider using a switch here" respond-to-pull-request-comment +``` + +#### Fix Failing Check + +```bash +cat > .prompts/tasks/fix-failing-check.md << 'EOF' +# Fix Failing Check: ${checkName} + +## Check Details +- Check Name: ${checkName} +- Branch: ${branch} +- Status: FAILED + +## Debugging Steps + +1. **Identify the Failure** + - Review the check logs + - Identify the specific error message + - Determine which component is failing + +2. **Reproduce Locally** + - Pull the latest code from ${branch} + - Run the same check locally + - Verify you can reproduce the failure + +3. **Root Cause Analysis** + - Is this a new failure or regression? + - What recent changes might have caused it? + - Is it environment-specific? + +4. **Fix the Issue** + - Implement the fix + - Verify the check passes locally + - Ensure no other checks are broken + - Add tests to prevent regression + +5. **Validate** + - Run all relevant checks locally + - Push changes and verify CI passes + - Update any related documentation + +## Common Check Types +- **Tests**: Fix failing unit/integration tests +- **Linter**: Address code style issues +- **Build**: Resolve compilation errors +- **Security**: Fix vulnerability scans +- **Coverage**: Improve test coverage + +Please fix the failing check and ensure all CI checks pass. +EOF + +# Usage +coding-context -p checkName="Unit Tests" -p branch="main" fix-failing-check +``` + +## Advanced Usage + +### Template Variables + +Prompts use shell-style variable expansion via `os.Expand`: + +```markdown +${variableName} # Braced variable substitution +$variableName # Simple variable substitution (works with alphanumeric names) +``` + +Variables that are not provided via `-p` flag are left as-is (e.g., `${missingVar}` remains `${missingVar}`). + +### Determining Common Parameters + +You can automate the detection of common parameters like `language` using external tools. Here's an example using the GitHub CLI (`gh`) to determine the primary programming language via GitHub Linguist: + +**Example: Automatically detect language using GitHub Linguist** + +```bash +# Get the primary language from the current repository +LANGUAGE=$(gh repo view --json primaryLanguage --jq .primaryLanguage.name) + +# Use the detected language with coding-context +coding-context -p language="$LANGUAGE" my-task +``` + +This works because GitHub uses Linguist to analyze repository languages, and `gh repo view` provides direct access to the primary language detected for the current repository. + +**Example with error handling:** + +```bash +# Get primary language with error handling +LANGUAGE=$(gh repo view --json primaryLanguage --jq .primaryLanguage.name 2>/dev/null) + +# Check if we successfully detected a language +if [ -z "$LANGUAGE" ] || [ "$LANGUAGE" = "null" ]; then + echo "Warning: Could not detect language, using default" + LANGUAGE="Go" # or your preferred default +fi + +coding-context -p language="$LANGUAGE" my-task +``` + +**One-liner version:** + +```bash +coding-context -p language="$(gh repo view --json primaryLanguage --jq .primaryLanguage.name)" my-task +``` + +**Prerequisites:** +- Install GitHub CLI: `brew install gh` (macOS) or `sudo apt install gh` (Ubuntu) +- Authenticate: `gh auth login` + +### Directory Priority + +When the same task exists in multiple directories, the first match wins: +1. `.prompts/` (highest priority) +2. `~/.config/prompts/` +3. `/var/local/prompts/` (lowest priority) + +## Troubleshooting + +**"prompt file not found for task"** +- Ensure `.md` exists in a `tasks/` subdirectory + +**"failed to walk memory dir"** +```bash +mkdir -p .prompts/memories +``` + +**Template parameter not replaced (shows as `${variableName}`)** +```bash +coding-context -p variableName="value" my-task +``` + +**Bootstrap script not executing** +```bash +chmod +x bootstrap +``` From f000f8be865c1901e3af4716adfcd9fd34942e53 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 16:27:10 +0000 Subject: [PATCH 4/5] Remove accidental backup file Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- README.md.backup | 1183 ---------------------------------------------- 1 file changed, 1183 deletions(-) delete mode 100644 README.md.backup diff --git a/README.md.backup b/README.md.backup deleted file mode 100644 index 40dc0967..00000000 --- a/README.md.backup +++ /dev/null @@ -1,1183 +0,0 @@ -# Coding Context CLI - -A CLI tool for managing context files for coding agents. It helps you organize prompts, memories (reusable context), and bootstrap scripts that can be assembled into a single context file for AI coding agents. - -## Why Use This? - -When working with AI coding agents (like GitHub Copilot, ChatGPT, Claude, etc.), providing the right context is crucial for getting quality results. However, managing this context becomes challenging when: - -- **Context is scattered**: Project conventions, coding standards, and setup instructions are spread across multiple documents -- **Repetition is tedious**: You find yourself copy-pasting the same information into every AI chat session -- **Context size is limited**: AI models have token limits, so you need to efficiently select what's relevant -- **Onboarding is manual**: New team members or agents need step-by-step setup instructions - -**This tool solves these problems by:** - -1. **Centralizing reusable context** - Store project conventions, coding standards, and setup instructions once in "memory" files -2. **Creating task-specific prompts** - Define templated prompts for common tasks (e.g., "add feature", "fix bug", "refactor") -3. **Automating environment setup** - Package bootstrap scripts that prepare the environment before an agent starts work -4. **Filtering context dynamically** - Use selectors to include only relevant context (e.g., production vs. development, Python vs. Go) -5. **Composing everything together** - Generate three separate markdown files: `persona.md`, `memories.md`, and `task.md` - -## When to Use - -This tool is ideal for: - -- **Working with AI coding agents** - Prepare comprehensive context before starting a coding session -- **Team standardization** - Share common prompts and conventions across your team -- **Complex projects** - Manage large amounts of project-specific context efficiently -- **Onboarding automation** - New developers or agents can run bootstrap scripts to set up their environment -- **Multi-environment projects** - Filter context based on environment (dev/staging/prod) or technology stack - -## How It Works - -The basic workflow is: - -1. **Organize your context** - Create persona files (optional), memory files (shared context), and task files (task-specific instructions) -2. **Run the CLI** - Execute `coding-context [options] [persona-name]` -3. **Get assembled output** - The tool generates: - - `persona.md` - Persona content (always created, can be empty if no persona is specified) - - `memories.md` - All included memory files combined - - `task.md` - Task prompt with template variables filled in - - `bootstrap` - Executable script to set up the environment - - `bootstrap.d/` - Individual bootstrap scripts from your memory files -4. **Use with AI agents** - Share the generated markdown files with your AI coding agent, or run `./bootstrap` to prepare the environment first - -**Visual flow:** -``` -+----------------------+ +---------------------+ +--------------------------+ -| Persona File (*.md) | | Memory Files (*.md) | | Task Template | -| (optional) | | | | (task-name.md) | -+----------+-----------+ +----------+----------+ +------------+-------------+ - | | | - | No expansion | Filter by selectors | Apply template params - v v v -+----------------------+ +---------------------+ +--------------------------+ -| persona.md | | memories.md | | task.md | -+----------------------+ +---------------------+ +--------------------------+ -``` - -## Installation - -Download the binary for your platform from the release page: - -```bash -sudo curl -fsL -o /usr/local/bin/coding-context https://github.com/kitproj/coding-agent-context-cli/releases/download/v0.0.1/coding-context_v0.0.1_linux_arm64 -sudo chmod +x /usr/local/bin/coding-context -``` - -## Usage - -``` -coding-context [options] [persona-name] - -Options: - -b Automatically run the bootstrap script after generating it - -C Change to directory before doing anything (default: .) - -m Directory containing memories, or a single memory file (can be used multiple times) - Defaults: AGENTS.md, .github/copilot-instructions.md, CLAUDE.md, .cursorrules, - .cursor/rules/, .instructions.md, .continuerules, .prompts/memories, - ~/.config/prompts/memories, /var/local/prompts/memories - -r Directory containing personas, or a single persona file (can be used multiple times) - Defaults: .prompts/personas, ~/.config/prompts/personas, /var/local/prompts/personas - -t Directory containing tasks, or a single task file (can be used multiple times) - Defaults: .prompts/tasks, ~/.config/prompts/tasks, /var/local/prompts/tasks - -o Output directory for generated files (default: .) - -p Template parameter for prompt substitution (can be used multiple times) - -s Include memories with matching frontmatter (can be used multiple times) - -S Exclude memories with matching frontmatter (can be used multiple times) -``` - -**Important:** The task file name **MUST** match the task name you provide on the command line. For example, if you run `coding-context my-task`, the tool will look for `my-task.md` in the task directories. - -**Example:** -```bash -coding-context -p feature="Authentication" -p language=Go add-feature -``` - -**Example with persona:** -```bash -# Use a persona to set the context for the AI agent (persona is an optional positional argument) -coding-context add-feature expert -``` - -**Example with custom memory and task paths:** -```bash -# Specify explicit memory files or directories -coding-context -m .github/copilot-instructions.md -m CLAUDE.md my-task - -# Specify custom task directory -coding-context -t ./custom-tasks my-task -``` - -**Example with selectors:** -```bash -# Include only production memories -coding-context -s env=production deploy - -# Exclude test memories -coding-context -S env=test deploy - -# Combine include and exclude selectors -coding-context -s env=production -S language=python deploy -``` - -## Quick Start - -This guide shows how to set up and generate your first context: - -**Step 1: Create a context directory structure** -```bash -mkdir -p .prompts/{tasks,memories,personas} -``` - -**Step 2: Create a memory file** (`.prompts/memories/project-info.md`) - -Memory files are included in every generated context. They contain reusable information like project conventions, architecture notes, or coding standards. - -```markdown -# Project Context - -- Framework: Go CLI -- Purpose: Manage AI agent context -``` - -**Step 3: (Optional) Create a persona file** (`.prompts/personas/expert.md`) - -Persona files define the role or character the AI agent should assume. They appear first in the output and do NOT support template variable expansion. - -```markdown -# Expert Developer - -You are an expert developer with deep knowledge of best practices. -``` - -**Step 4: Create a prompt file** (`.prompts/tasks/my-task.md`) - -Prompt files define specific tasks. They can use template variables (like `${taskName}` or `$taskName`) that you provide via command-line parameters. - -**IMPORTANT:** The file name **MUST** match the task name you'll use on the command line. For example, a file named `my-task.md` is invoked with `coding-context my-task`. - -```markdown -# Task: ${taskName} - -Please help me with this task. The project uses ${language}. -``` - -**Step 5: Generate your context file** - -```bash -# Without persona -coding-context -p taskName="Fix Bug" -p language=Go my-task - -# With persona (as optional positional argument after task name) -coding-context -p taskName="Fix Bug" -p language=Go my-task expert -``` - -**Result:** This generates three files: `./persona.md` (if persona is specified), `./memories.md`, and `./task.md` with template variables filled in. You can now share these files with your AI coding agent! - -**What you'll see in the generated files (with persona):** - -`persona.md`: -```markdown -# Expert Developer - -You are an expert developer with deep knowledge of best practices. -``` - -`memories.md`: -```markdown -# Project Context - -- Framework: Go CLI -- Purpose: Manage AI agent context -``` - -`task.md`: -```markdown -# Task: Fix Bug - -Please help me with this task. The project uses Go. -``` - - -## Directory Structure - -The tool searches these directories for context files (in priority order): -1. `.prompts/` (project-local) -2. `~/.config/prompts/` (user-specific) -3. `/var/local/prompts/` (system-wide) - -Each directory should contain: -``` -.prompts/ -├── personas/ # Optional persona files (output first when specified) -│ └── .md -├── tasks/ # Task-specific prompt templates -│ └── .md -└── memories/ # Reusable context files (included in all outputs) - └── *.md -``` - - -## File Formats - -### Persona Files - -Optional persona files define the role or character the AI agent should assume. Personas are output to `persona.md` when specified. - -**Important:** Persona files do NOT support template variable expansion. They are included as-is in the output. - -**Example** (`.prompts/personas/expert.md`): -```markdown -# Expert Software Engineer - -You are an expert software engineer with deep knowledge of best practices. -You are known for writing clean, maintainable code and following industry standards. -``` - -Run with: -```bash -coding-context add-feature expert -``` - -This will look for `expert.md` in the persona directories and output it to `persona.md`. The persona is optional – if you don't specify a persona name as the second argument, `persona.md` will still be generated but will be empty, alongside `memories.md` and `task.md`. - -### Prompt Files - -Markdown files with YAML frontmatter and Go template support. - -**CRITICAL:** The prompt file name (without the `.md` extension) **MUST** exactly match the task name you provide on the command line. For example: -- To run `coding-context add-feature`, you need a file named `add-feature.md` -- To run `coding-context my-custom-task`, you need a file named `my-custom-task.md` - -**Example** (`.prompts/tasks/add-feature.md`): -```markdown -# Task: ${feature} - -Implement ${feature} in ${language}. -``` - -Run with: -```bash -coding-context -p feature="User Login" -p language=Go add-feature -``` - -This will look for `add-feature.md` in the task directories. - -### Memory Files - -Markdown files included in every generated context. Bootstrap scripts can be provided in separate files. - -**Example** (`.prompts/memories/setup.md`): -```markdown ---- -env: development -language: go ---- -# Development Setup - -This project requires Node.js dependencies. -``` - -**Bootstrap file** (`.prompts/memories/setup-bootstrap`): -```bash -#!/bin/bash -npm install -``` - -For each memory file `.md`, you can optionally create a corresponding `-bootstrap` file that will be executed during setup. - -### Supported Memory File Formats - -This tool can work with various memory file formats used by popular AI coding assistants. By default, it looks for `AGENTS.md` in the current directory. You can also specify additional memory files or directories using the `-m` flag. - -#### Common Memory File Names - -The following memory file formats are commonly used by AI coding assistants and can be used with this tool: - -- **`AGENTS.md`** - Default memory file (automatically included) -- **`.github/copilot-instructions.md`** - GitHub Copilot instructions file -- **`CLAUDE.md`** - Claude-specific instructions -- **`.cursorrules`** - Cursor editor rules (if in Markdown format) -- **`.cursor/rules/`** - Directory containing Cursor-specific rule files -- **`.instructions.md`** - General instructions file -- **`.continuerules`** - Continue.dev rules (if in Markdown format) - -**Example:** Using multiple memory sources -```bash -# Include GitHub Copilot instructions and CLAUDE.md -coding-context -m .github/copilot-instructions.md -m CLAUDE.md my-task - -# Include all rules from Cursor directory -coding-context -m .cursor/rules/ my-task - -# Combine default AGENTS.md with additional memories -coding-context -m .instructions.md my-task -``` - -**Note:** All memory files should be in Markdown format (`.md` extension) or contain Markdown-compatible content. The tool will automatically process frontmatter in YAML format if present. - - -## Filtering Memories with Selectors - -Use the `-s` and `-S` flags to filter which memory files are included based on their frontmatter metadata. - -### Selector Syntax - -- **`-s key=value`** - Include memories where the frontmatter key matches the value -- **`-S key=value`** - Exclude memories where the frontmatter key matches the value -- If a key doesn't exist in a memory's frontmatter, the memory is allowed (not filtered out) -- Multiple selectors of the same type use AND logic (all must match) - -### Examples - -**Include only production memories:** -```bash -coding-context -s env=production deploy -``` - -**Exclude test environment:** -```bash -coding-context -S env=test deploy -``` - -**Combine include and exclude:** -```bash -# Include production but exclude python -coding-context -s env=production -S language=python deploy -``` - -**Multiple includes:** -```bash -# Only production Go backend memories -coding-context -s env=production -s language=go -s tier=backend deploy -``` - -### How It Works - -When you run with selectors, the tool logs which files are included or excluded: - -``` -INFO Including memory file path=.prompts/memories/production.md -INFO Excluding memory file (does not match include selectors) path=.prompts/memories/development.md -INFO Including memory file path=.prompts/memories/nofrontmatter.md -``` - -**Important:** Files without the specified frontmatter keys are still included. This allows you to have generic memories that apply to all scenarios. - -If no selectors are specified, all memory files are included. - -### Deduplicating Memories - -When you have multiple memory files with the same filename (basename), only the first one encountered will be included. This allows you to override default memories with project-specific ones by using the same filename. - -**Example:** - -If you have two directories with memory files: - -`~/.coding-context/memories/general/setup.md`: -```markdown ---- ---- -This is the default setup memory. -``` - -`./memories/setup.md`: -```markdown ---- ---- -This is a project-specific setup memory. -``` - -When the tool processes these two files, it will include only one of them based on which is encountered first during filesystem traversal. **The order depends on the order of memory paths specified and filesystem traversal order, which is not guaranteed to be alphabetical or consistent.** This mechanism is useful for overriding default memories with project-specific ones when you use the same filename. - - -## Output Files - -- **`persona.md`** - Persona content (always created, can be empty if no persona is specified) -- **`memories.md`** - Combined output with all filtered memory files -- **`task.md`** - Task prompt with template variables expanded -- **`bootstrap`** - Executable script that runs all bootstrap scripts from memories -- **`bootstrap.d/`** - Individual bootstrap scripts (SHA256 named) - -Run the bootstrap script to set up your environment: -```bash -./bootstrap -``` - -Or use the `-b` flag to automatically run the bootstrap script after generating it: -```bash -coding-context -b my-task -``` - - -## Examples - -### Basic Usage - -```bash -# Create structure -mkdir -p .prompts/{tasks,memories} - -# Add a memory -cat > .prompts/memories/conventions.md << 'EOF' -# Coding Conventions - -- Use tabs for indentation -- Write tests for all functions -EOF - -# Create a task prompt -cat > .prompts/tasks/refactor.md << 'EOF' -# Refactoring Task - -Please refactor the codebase to improve code quality. -EOF - -# Generate context -coding-context refactor -``` - -### With Template Parameters - -```bash -cat > .prompts/tasks/add-feature.md << 'EOF' -# Add Feature: ${featureName} - -Implement ${featureName} in ${language}. -EOF - -coding-context -p featureName="Authentication" -p language=Go add-feature -``` - -### With Bootstrap Scripts - -```bash -cat > .prompts/memories/setup.md << 'EOF' -# Project Setup - -This Go project uses modules. -EOF - -cat > .prompts/memories/setup-bootstrap << 'EOF' -#!/bin/bash -go mod download -EOF -chmod +x .prompts/memories/setup-bootstrap - -coding-context -o ./output my-task -cd output && ./bootstrap -``` - -Alternatively, use the `-b` flag to automatically run the bootstrap script: -```bash -coding-context -o ./output -b my-task -``` - -### Integrating External CLI Tools - -The bootstrap script mechanism is especially useful for integrating external CLI tools like `kitproj/jira-cli` and `kitproj/slack-cli`. These tools can be installed automatically when an agent starts working on a task. - -#### Example: Using kitproj/jira-cli - -The `kitproj/jira-cli` tool allows agents to interact with Jira issues programmatically. Here's how to set it up: - -**Step 1: Create a memory file with Jira context** (`.prompts/memories/jira.md`) - -```markdown -# Jira Integration - -This project uses Jira for issue tracking. The `jira` CLI tool is available for interacting with issues. - -## Available Commands - -- `jira get-issue ` - Get details of a Jira issue -- `jira get-comments ` - Get all comments on an issue -- `jira add-comment ` - Add a comment to an issue -- `jira update-issue-status ` - Update the status of an issue -- `jira create-issue ` - Create a new issue - -## Configuration - -The Jira CLI is configured with: -- Server URL: https://your-company.atlassian.net -- Authentication: Token-based (set via JIRA_API_TOKEN environment variable) -``` - -**Step 2: Create a bootstrap script** (`.prompts/memories/jira-bootstrap`) - -```bash -#!/bin/bash -set -euo pipefail - -VERSION="v0.1.0" # Update to the latest version -BINARY_URL="https://github.com/kitproj/jira-cli/releases/download/${VERSION}/jira-cli_${VERSION}_linux_amd64" - -sudo curl -fsSL -o /usr/local/bin/jira "$BINARY_URL" -sudo chmod +x /usr/local/bin/jira -``` - -**Step 3: Make the bootstrap script executable** - -```bash -chmod +x .prompts/memories/jira-bootstrap -``` - -**Step 4: Use with a task that needs Jira** - -```bash -# The bootstrap will automatically run when you generate context -coding-context -b -p storyId="PROJ-123" implement-jira-story -``` - -Now when an agent starts work, the bootstrap script will ensure `jira-cli` is installed and ready to use! - -#### Example: Using kitproj/slack-cli - -The `kitproj/slack-cli` tool allows agents to send notifications and interact with Slack channels. Here's the setup: - -**Step 1: Create a memory file with Slack context** (`.prompts/memories/slack.md`) - -```markdown -# Slack Integration - -This project uses Slack for team communication. The `slack` CLI tool is available for sending messages and notifications. - -## Available Commands - -- `slack send-message ` - Send a message to a channel -- `slack send-thread-reply ` - Reply to a thread -- `slack upload-file ` - Upload a file to a channel -- `slack set-status ` - Set your Slack status -- `slack get-channel-history ` - Get recent messages from a channel - -## Configuration - -The Slack CLI requires: -- Workspace: your-workspace.slack.com -- Authentication: Bot token (set via SLACK_BOT_TOKEN environment variable) -- Channels: Use channel IDs or names (e.g., #engineering, #alerts) - -## Common Use Cases - -- Send build notifications: `slack send-message "#builds" "Build completed successfully"` -- Report deployment status: `slack send-message "#deployments" "Production deployment started"` -- Alert on failures: `slack send-message "#alerts" "Test suite failed on main branch"` -``` - -**Step 2: Create a bootstrap script** (`.prompts/memories/slack-bootstrap`) - -```bash -#!/bin/bash -set -euo pipefail - -VERSION="v0.1.0" # Update to the latest version -BINARY_URL="https://github.com/kitproj/slack-cli/releases/download/${VERSION}/slack-cli_${VERSION}_linux_amd64" - -sudo curl -fsSL -o /usr/local/bin/slack "$BINARY_URL" -sudo chmod +x /usr/local/bin/slack -``` - -**Step 3: Make the bootstrap script executable** - -```bash -chmod +x .prompts/memories/slack-bootstrap -``` - -**Step 4: Create a task that uses Slack** (`.prompts/tasks/slack-deploy-alert.md`) - -```markdown -# Slack Deployment Alert: ${environment} - -## Task - -Send a deployment notification to the team via Slack. - -## Steps - -1. **Prepare the notification message** - - Include environment: ${environment} - - Include deployment status - - Include relevant details (version, commit, etc.) - -2. **Send to appropriate channels** - ```bash - slack send-message "#deployments" "🚀 Deployment to ${environment} started" - ``` - -3. **Update on completion** - ```bash - slack send-message "#deployments" "✅ Deployment to ${environment} completed successfully" - ``` - -4. **Alert on failures** (if needed) - ```bash - slack send-message "#alerts" "❌ Deployment to ${environment} failed. Check logs for details." - ``` - -## Success Criteria -- Team is notified of deployment status -- Appropriate channels receive updates -- Messages are clear and actionable -``` - -**Step 5: Use the task** - -```bash -coding-context -p environment="production" slack-deploy-alert -./bootstrap # Installs slack-cli if needed -``` - -#### Writing Bootstrap Scripts - Best Practices - -When writing bootstrap scripts for external CLI tools: - -1. **Check if already installed** - Avoid reinstalling if the tool exists - ```bash - if ! command -v toolname &> /dev/null; then - # Install logic here - fi - ``` - -2. **Use specific versions** - Pin to a specific version for reproducibility - ```bash - VERSION="v0.1.0" - ``` - -3. **Set error handling** - Use `set -euo pipefail` to catch errors early - ```bash - #!/bin/bash - set -euo pipefail - ``` - -4. **Verify installation** - Check that the tool works after installation - ```bash - toolname --version - ``` - -5. **Provide clear output** - Echo messages to show progress - ```bash - echo "Installing toolname..." - echo "Installation complete" - ``` - -### Real-World Task Examples - -Here are some practical task templates for common development workflows: - -#### Implement Jira Story - -**Note:** This example assumes you've set up the Jira CLI integration as shown in the [Using kitproj/jira-cli](#example-using-kitprojjira-cli) section above. The bootstrap script will automatically install the `jira` command. - -```bash -cat > .prompts/tasks/implement-jira-story.md << 'EOF' -# Implement Jira Story: ${storyId} - -## Story Details - -First, get the full story details from Jira: - - jira get-issue ${storyId} - -## Requirements - -Please implement the feature described in the Jira story. Follow these steps: - -1. **Review the Story** - - Read the story details, acceptance criteria, and comments - - Get all comments: `jira get-comments ${storyId}` - - Clarify any uncertainties by adding comments: `jira add-comment ${storyId} "Your question"` - -2. **Start Development** - - Create a feature branch with the story ID in the name (e.g., `feature/${storyId}-implement-auth`) - - Move the story to "In Progress": `jira update-issue-status ${storyId} "In Progress"` - -3. **Implementation** - - Design the solution following project conventions - - Implement the feature with proper error handling - - Add comprehensive unit tests (aim for >80% coverage) - - Update documentation if needed - - Ensure all tests pass and code is lint-free - -4. **Update Jira Throughout** - - Add progress updates: `jira add-comment ${storyId} "Completed implementation, working on tests"` - - Keep stakeholders informed of any blockers or changes - -5. **Complete the Story** - - Ensure all acceptance criteria are met - - Create a pull request - - Move to review: `jira update-issue-status ${storyId} "In Review"` - - Once merged, close: `jira update-issue-status ${storyId} "Done"` - -## Success Criteria -- All acceptance criteria are met -- Code follows project coding standards -- Tests are passing -- Documentation is updated -- Jira story is properly tracked through workflow -EOF - -# Usage -coding-context -p storyId="PROJ-123" implement-jira-story -``` - -#### Triage Jira Bug - -**Note:** This example requires the Jira CLI integration. See [Using kitproj/jira-cli](#example-using-kitprojjira-cli) for setup instructions. - -```bash -cat > .prompts/tasks/triage-jira-bug.md << 'EOF' -# Triage Jira Bug: ${bugId} - -## Get Bug Details - -First, retrieve the full bug report from Jira: - - jira get-issue ${bugId} - jira get-comments ${bugId} - -## Triage Steps - -1. **Acknowledge and Take Ownership** - - Add initial comment: `jira add-comment ${bugId} "Triaging this bug now"` - - Move to investigation: `jira update-issue-status ${bugId} "In Progress"` - -2. **Reproduce the Issue** - - Follow the steps to reproduce in the bug report - - Verify the issue exists in the reported environment - - Document actual vs. expected behavior - - Update Jira: `jira add-comment ${bugId} "Reproduced on [environment]. Actual: [X], Expected: [Y]"` - -3. **Investigate Root Cause** - - Review relevant code and logs - - Identify the component/module causing the issue - - Determine if this is a regression (check git history) - - Document findings: `jira add-comment ${bugId} "Root cause: [description]"` - -4. **Assess Impact** - - How many users are affected? - - Is there a workaround available? - - What is the risk if left unfixed? - - Add assessment: `jira add-comment ${bugId} "Impact: [severity]. Workaround: [yes/no]. Affected users: [estimate]"` - -5. **Provide Triage Report** - - Root cause analysis - - Recommended priority level - - Estimated effort to fix - - Suggested assignee/team - - Final summary: `jira add-comment ${bugId} "Triage complete. Priority: [level]. Effort: [estimate]. Recommended assignee: [name]"` - -## Output -Provide a detailed triage report with your findings and recommendations, and post it as a comment to the Jira issue. -EOF - -# Usage -coding-context -p bugId="PROJ-456" triage-jira-bug -``` - -#### Respond to Jira Comment - -**Note:** This example requires the Jira CLI integration. See [Using kitproj/jira-cli](#example-using-kitprojjira-cli) for setup instructions. - -```bash -cat > .prompts/tasks/respond-to-jira-comment.md << 'EOF' -# Respond to Jira Comment: ${issueId} - -## Get Issue and Comments - -First, retrieve the issue details and all comments: - - jira get-issue ${issueId} - jira get-comments ${issueId} - -Review the latest comment and the full context of the issue. - -## Instructions - -Please analyze the comment and provide a professional response: - -1. **Acknowledge** the comment and any concerns raised -2. **Address** each question or point made -3. **Provide** technical details or clarifications as needed -4. **Suggest** next steps or actions if appropriate -5. **Maintain** a collaborative and helpful tone - -## Response Guidelines -- Be clear and concise -- Provide code examples if relevant -- Link to documentation when helpful -- Offer to discuss further if needed - -## Post Your Response - -Once you've formulated your response, add it to the Jira issue: - - jira add-comment ${issueId} "Your detailed response here" - -If the comment requires action on your part, update the issue status accordingly: - - jira update-issue-status ${issueId} "In Progress" - -EOF - -# Usage -coding-context -p issueId="PROJ-789" respond-to-jira-comment -``` - -#### Send Slack Notification on Build Completion - -**Note:** This example requires the Slack CLI integration. See [Using kitproj/slack-cli](#example-using-kitprojslack-cli) for setup instructions. - -```bash -cat > .prompts/tasks/notify-build-status.md << 'EOF' -# Notify Build Status: ${buildStatus} - -## Task - -Send a build status notification to the team via Slack. - -## Build Information -- Status: ${buildStatus} -- Branch: ${branch} -- Commit: ${commit} -- Build Time: ${buildTime} - -## Steps - -1. **Prepare the notification message** - - Determine the appropriate emoji based on status - - Include all relevant build details - - Add links to build logs or artifacts - -2. **Send notification to #builds channel** - - For successful builds: - - slack send-message "#builds" "✅ Build succeeded on ${branch} - Commit: ${commit} - Time: ${buildTime} - Status: ${buildStatus}" - - For failed builds: - - slack send-message "#builds" "❌ Build failed on ${branch} - Commit: ${commit} - Time: ${buildTime} - Status: ${buildStatus} - Please check the build logs for details." - -3. **Alert in #alerts channel for failures** (if build failed) - - slack send-message "#alerts" "🚨 Build failure detected on ${branch}. Immediate attention needed." - -4. **Update thread if this is a rebuild** - If responding to a previous build notification: - - slack send-thread-reply "#builds" "" "Rebuild completed: ${buildStatus}" - -## Success Criteria -- Appropriate channels are notified -- Message includes all relevant details -- Team can quickly assess build status -- Failed builds trigger alerts -EOF - -# Usage -coding-context -p buildStatus="SUCCESS" -p branch="main" -p commit="abc123" -p buildTime="2m 30s" notify-build-status -``` - -#### Post Deployment Notification to Slack - -**Note:** This example requires the Slack CLI integration. See [Using kitproj/slack-cli](#example-using-kitprojslack-cli) for setup instructions. - -```bash -cat > .prompts/tasks/notify-deployment.md << 'EOF' -# Notify Deployment: ${environment} - -## Task - -Communicate deployment status to stakeholders via Slack. - -## Deployment Details -- Environment: ${environment} -- Version: ${version} -- Deployer: ${deployer} - -## Instructions - -1. **Announce deployment start** - - slack send-message "#deployments" "🚀 Deployment to ${environment} started - Version: ${version} - Deployer: ${deployer} - Started at: $(date)" - -2. **Monitor deployment progress** - - Track deployment steps - - Note any issues or delays - -3. **Send completion notification** - - For successful deployments: - - slack send-message "#deployments" "✅ Deployment to ${environment} completed successfully - Version: ${version} - Completed at: $(date) - All services are healthy and running." - - For failed deployments: - - slack send-message "#deployments" "❌ Deployment to ${environment} failed - Version: ${version} - Failed at: $(date) - Rolling back to previous version..." - -4. **Alert stakeholders for production deployments** - - slack send-message "#general" "📢 Production deployment completed: version ${version} is now live!" - -5. **Update status thread** - - Reply to the initial announcement with final status - - Include any post-deployment tasks or notes - -## Success Criteria -- Deployment timeline is clearly communicated -- All stakeholders are informed -- Status updates are timely and accurate -- Issues are escalated appropriately -EOF - -# Usage -coding-context -p environment="production" -p version="v2.1.0" -p deployer="deploy-bot" notify-deployment -``` - -#### Review Pull Request - -```bash -cat > .prompts/tasks/review-pull-request.md << 'EOF' -# Review Pull Request: ${prNumber} - -## PR Details -- PR #${prNumber} -- Author: ${author} -- Title: ${title} - -## Review Checklist - -### Code Quality -- [ ] Code follows project style guidelines -- [ ] No obvious bugs or logic errors -- [ ] Error handling is appropriate -- [ ] No security vulnerabilities introduced -- [ ] Performance considerations addressed - -### Testing -- [ ] Tests are included for new functionality -- [ ] Tests cover edge cases -- [ ] All tests pass -- [ ] Test quality is high (clear, maintainable) - -### Documentation -- [ ] Public APIs are documented -- [ ] Complex logic has explanatory comments -- [ ] README updated if needed -- [ ] Breaking changes are noted - -### Architecture -- [ ] Changes align with project architecture -- [ ] No unnecessary dependencies added -- [ ] Code is modular and reusable -- [ ] Separation of concerns maintained - -## Instructions -Please review the pull request thoroughly and provide: -1. Constructive feedback on any issues found -2. Suggestions for improvements -3. Approval or request for changes -4. Specific line-by-line comments where helpful - -Be thorough but encouraging. Focus on learning and improvement. -EOF - -# Usage -coding-context -p prNumber="42" -p author="Jane" -p title="Add feature X" review-pull-request -``` - -#### Respond to Pull Request Comment - -```bash -cat > .prompts/tasks/respond-to-pull-request-comment.md << 'EOF' -# Respond to Pull Request Comment - -## PR Details -- PR #${prNumber} -- Reviewer: ${reviewer} -- File: ${file} - -## Comment -${comment} - -## Instructions - -Please address the pull request review comment: - -1. **Analyze** the feedback carefully -2. **Determine** if the comment is valid -3. **Respond** professionally: - - If you agree: Acknowledge and describe your fix - - If you disagree: Respectfully explain your reasoning - - If unclear: Ask clarifying questions - -4. **Make changes** if needed: - - Fix the issue raised - - Add tests if applicable - - Update documentation - - Ensure code still works - -5. **Reply** with: - - What you changed (with commit reference) - - Why you made that choice - - Any additional context needed - -## Tone -Be collaborative, open to feedback, and focused on code quality. -EOF - -# Usage -coding-context -p prNumber="42" -p reviewer="Bob" -p file="main.go" -p comment="Consider using a switch here" respond-to-pull-request-comment -``` - -#### Fix Failing Check - -```bash -cat > .prompts/tasks/fix-failing-check.md << 'EOF' -# Fix Failing Check: ${checkName} - -## Check Details -- Check Name: ${checkName} -- Branch: ${branch} -- Status: FAILED - -## Debugging Steps - -1. **Identify the Failure** - - Review the check logs - - Identify the specific error message - - Determine which component is failing - -2. **Reproduce Locally** - - Pull the latest code from ${branch} - - Run the same check locally - - Verify you can reproduce the failure - -3. **Root Cause Analysis** - - Is this a new failure or regression? - - What recent changes might have caused it? - - Is it environment-specific? - -4. **Fix the Issue** - - Implement the fix - - Verify the check passes locally - - Ensure no other checks are broken - - Add tests to prevent regression - -5. **Validate** - - Run all relevant checks locally - - Push changes and verify CI passes - - Update any related documentation - -## Common Check Types -- **Tests**: Fix failing unit/integration tests -- **Linter**: Address code style issues -- **Build**: Resolve compilation errors -- **Security**: Fix vulnerability scans -- **Coverage**: Improve test coverage - -Please fix the failing check and ensure all CI checks pass. -EOF - -# Usage -coding-context -p checkName="Unit Tests" -p branch="main" fix-failing-check -``` - -## Advanced Usage - -### Template Variables - -Prompts use shell-style variable expansion via `os.Expand`: - -```markdown -${variableName} # Braced variable substitution -$variableName # Simple variable substitution (works with alphanumeric names) -``` - -Variables that are not provided via `-p` flag are left as-is (e.g., `${missingVar}` remains `${missingVar}`). - -### Determining Common Parameters - -You can automate the detection of common parameters like `language` using external tools. Here's an example using the GitHub CLI (`gh`) to determine the primary programming language via GitHub Linguist: - -**Example: Automatically detect language using GitHub Linguist** - -```bash -# Get the primary language from the current repository -LANGUAGE=$(gh repo view --json primaryLanguage --jq .primaryLanguage.name) - -# Use the detected language with coding-context -coding-context -p language="$LANGUAGE" my-task -``` - -This works because GitHub uses Linguist to analyze repository languages, and `gh repo view` provides direct access to the primary language detected for the current repository. - -**Example with error handling:** - -```bash -# Get primary language with error handling -LANGUAGE=$(gh repo view --json primaryLanguage --jq .primaryLanguage.name 2>/dev/null) - -# Check if we successfully detected a language -if [ -z "$LANGUAGE" ] || [ "$LANGUAGE" = "null" ]; then - echo "Warning: Could not detect language, using default" - LANGUAGE="Go" # or your preferred default -fi - -coding-context -p language="$LANGUAGE" my-task -``` - -**One-liner version:** - -```bash -coding-context -p language="$(gh repo view --json primaryLanguage --jq .primaryLanguage.name)" my-task -``` - -**Prerequisites:** -- Install GitHub CLI: `brew install gh` (macOS) or `sudo apt install gh` (Ubuntu) -- Authenticate: `gh auth login` - -### Directory Priority - -When the same task exists in multiple directories, the first match wins: -1. `.prompts/` (highest priority) -2. `~/.config/prompts/` -3. `/var/local/prompts/` (lowest priority) - -## Troubleshooting - -**"prompt file not found for task"** -- Ensure `.md` exists in a `tasks/` subdirectory - -**"failed to walk memory dir"** -```bash -mkdir -p .prompts/memories -``` - -**Template parameter not replaced (shows as `${variableName}`)** -```bash -coding-context -p variableName="value" my-task -``` - -**Bootstrap script not executing** -```bash -chmod +x bootstrap -``` From 2d25ea157056cfa9a66a66adfe0bb7faa2a0d043 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 2 Nov 2025 16:28:35 +0000 Subject: [PATCH 5/5] Add clarifying comment about duplicate basenames being allowed Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 235141b7..ef25c42b 100644 --- a/main.go +++ b/main.go @@ -207,7 +207,8 @@ func run(ctx context.Context, args []string) error { return fmt.Errorf("failed to parse markdown file: %w", err) } - // Check if file matches include and exclude selectors + // Check if file matches include and exclude selectors. + // Note: Files with duplicate basenames will both be included. if !includes.matchesIncludes(frontmatter) { fmt.Fprintf(os.Stdout, "Excluding rule file (does not match include selectors): %s\n", path) return nil