diff --git a/README.md b/README.md index bb34257..fb11ebc 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,11 @@ The tool assembles context into a structured prompt with the following component ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌───────────────────────────────────────────────────────┐ │ -│ │ 1. Task Frontmatter (YAML) │ │ -│ │ • Task metadata (selectors, agent, etc.) │ │ -│ │ • Always included when task has frontmatter │ │ +│ │ 1. Rules Content (Markdown) │ │ +│ │ • Coding standards and guidelines │ │ +│ │ • Team conventions and best practices │ │ +│ │ • Filtered by selectors (-s flag) │ │ +│ │ • Skipped in resume mode (-r flag) │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ ┌───────────────────────────────────────────────────────┐ │ @@ -31,15 +33,7 @@ The tool assembles context into a structured prompt with the following component │ └───────────────────────────────────────────────────────┘ │ │ │ │ ┌───────────────────────────────────────────────────────┐ │ -│ │ 3. Rules Content (Markdown) │ │ -│ │ • Coding standards and guidelines │ │ -│ │ • Team conventions and best practices │ │ -│ │ • Filtered by selectors (-s flag) │ │ -│ │ • Skipped in resume mode (-r flag) │ │ -│ └───────────────────────────────────────────────────────┘ │ -│ │ -│ ┌───────────────────────────────────────────────────────┐ │ -│ │ 4. Task Content (Markdown) │ │ +│ │ 3. Task Content (Markdown) │ │ │ │ • Task-specific instructions │ │ │ │ • Parameter substitutions (${param}) │ │ │ │ • Command expansions (!`command`) │ │ @@ -50,10 +44,10 @@ The tool assembles context into a structured prompt with the following component ``` **Key Points:** -- **Task Frontmatter**: Provides metadata for agent decision-making and workflow automation -- **Skills**: Enable progressive disclosure of specialized capabilities - **Rules**: Reusable context that applies across multiple tasks +- **Skills**: Enable progressive disclosure of specialized capabilities - **Task Content**: Specific instructions for the current task with dynamic content expansion +- **Note**: Task frontmatter is used for filtering and metadata but is not included in the output ## Features @@ -767,40 +761,49 @@ The AI agent can then read the full skill content from the provided location whe ### Task Frontmatter -Task frontmatter is **always** automatically included at the beginning of the output when a task file has frontmatter. This allows the AI agent or downstream tool to access metadata about the task being executed. There is no flag needed to enable this - it happens automatically. +Task frontmatter contains metadata used for filtering, agent selection, and workflow automation. While the frontmatter itself is not included in the generated output, it serves important purposes: + +**What frontmatter is used for:** +- **Task selection**: Filtering between multiple task files using selectors +- **Agent specification**: The `agent` field can override the `-a` command-line flag +- **Rule filtering**: The `selectors` field automatically filters rules without requiring explicit `-s` flags +- **Workflow control**: Fields like `resume` control task behavior in different modes **Example usage:** ```bash coding-context -p issue_number=123 fix-bug ``` -**Output format:** -```yaml +**Example task file:** +```markdown --- resume: false +selectors: + languages: go + stage: implementation --- # Fix Bug Task +Fix the bug in issue #${issue_number}... +``` + +**Output format:** +The task frontmatter is NOT included in the output. Only the task content (below the frontmatter delimiters) is included: +```markdown +# Fix Bug Task + Fix the bug in issue #123... ``` -This can be useful for: -- **Agent decision making**: The AI can see metadata like priority, environment, or stage -- **Workflow automation**: Downstream tools can parse the frontmatter to make decisions -- **Debugging**: You can verify which task variant was selected and what selectors were applied +This design keeps the output focused on actionable content while still allowing frontmatter to control behavior and filtering. **Example with selectors in frontmatter:** ```bash coding-context implement-feature ``` -If the task has `selectors` in its frontmatter, they will be visible in the output: -```yaml ---- -selectors: - languages: go - stage: implementation ---- +If the task has `selectors` in its frontmatter, they will automatically filter rules, but the frontmatter itself won't appear in the output: +```markdown # Implementation Task ... ``` diff --git a/SPECIFICATION.md b/SPECIFICATION.md index ef191e4..ddb4dd2 100644 --- a/SPECIFICATION.md +++ b/SPECIFICATION.md @@ -230,7 +230,7 @@ selectors: **Rules:** - `task_name` field is optional metadata - Tasks matched by filename (e.g., `fix-bug.md` → task name `fix-bug`) -- Frontmatter is automatically output with task +- Frontmatter is used for filtering and metadata but not included in output - Can specify `selectors` to auto-filter rules - Supports parameter expansion: `${param_name}` @@ -1096,20 +1096,28 @@ Remote directories support (via go-getter): ### 11.1 Assembly Order -1. **Task frontmatter** (YAML format, automatically included if present) +1. **Rule content** (all included rules, content only) 2. **Skill metadata** (XML format, if skills found) -3. **Rule content** (all included rules, content only) -4. **Task content** (with expansions applied) -5. **User prompt** (if provided, after `---` delimiter) +3. **Task content** (with expansions applied) +4. **User prompt** (if provided, after `---` delimiter) + +**Note**: Task frontmatter is used for filtering and metadata purposes but is not included in the output. ### 11.2 Output Format **To stdout (the context):** -```yaml ---- -task_name: fix-bug -resume: false ---- +```markdown +# Rule 1 Content + +Rule 1 text... + +# Rule 2 Content + +Rule 2 text... + +# Skills + +You have access to the following skills. Skills are specialized capabilities that provide domain expertise, workflows, and procedural knowledge. When a task matches a skill's description, you can load the full skill content by reading the SKILL.md file at the location provided. @@ -1119,14 +1127,6 @@ resume: false -# Rule 1 Content - -Rule 1 text... - -# Rule 2 Content - -Rule 2 text... - # Task Content Fix bug #123... @@ -1148,8 +1148,7 @@ INFO: Estimated tokens: 2,345 With `-r` flag: 1. All rules are **skipped** 2. Implicit selector: `-s resume=true` added -3. Task frontmatter still included -4. Useful for continuing work with established context +3. Useful for continuing work with established context ### 11.4 Token Estimation @@ -1396,12 +1395,14 @@ Bootstrap scripts output to stderr (not context) because: - Allows logging without polluting context - Enables verification without affecting AI input -#### 14.2.5 Frontmatter in Task Output -Task frontmatter is automatically included because: -- Enables agent decision-making -- Supports downstream tooling -- Aids debugging and verification -- No overhead for simple cases +#### 14.2.5 Task Frontmatter Usage +Task frontmatter serves metadata purposes: +- Enables task selection and filtering via selectors +- Specifies agent preferences +- Controls workflow behavior (e.g., resume mode) +- Defines rule filtering via `selectors` field + +The frontmatter is not included in the output to keep the generated context focused on actionable content. ### 14.3 Limitations diff --git a/examples/.agents b/examples/.agents new file mode 120000 index 0000000..93aa575 --- /dev/null +++ b/examples/.agents @@ -0,0 +1 @@ +agents \ No newline at end of file diff --git a/integration_test.go b/integration_test.go index 60fe9d1..e4633fb 100644 --- a/integration_test.go +++ b/integration_test.go @@ -379,15 +379,12 @@ Command with param: !`+"`echo '${secret}'`"+` output := runTool(t, "-C", tmpDir, "-p", "evil=!`echo INJECTED`", "-p", "secret=TOPSECRET", "test-security") // Split output into lines to separate stderr logs from stdout prompt + // Since task frontmatter is no longer printed, we identify stdout by filtering out stderr log lines lines := strings.Split(output, "\n") var promptLines []string - inPrompt := false for _, line := range lines { - // Look for the start of the frontmatter (which marks beginning of stdout) - if strings.HasPrefix(line, "---") { - inPrompt = true - } - if inPrompt { + // Stderr log lines start with "time=", stdout lines don't + if !strings.HasPrefix(line, "time=") { promptLines = append(promptLines, line) } } @@ -974,78 +971,46 @@ This is a test task. t.Fatalf("failed to write task file: %v", err) } - // Test that frontmatter is always printed + // Test that task frontmatter is NOT printed output := runTool(t, "-C", dirs.tmpDir, "test-task") - lines := strings.Split(output, "\n") - - // Find the first non-log line (skip lines starting with "time=") - var firstContentLine string - for _, line := range lines { - if !strings.HasPrefix(line, "time=") { - firstContentLine = line - break - } - } - - // First content line should be frontmatter delimiter - if firstContentLine != "---" { - t.Errorf("expected first content line to be '---', got %q", firstContentLine) - } - - // Should contain task frontmatter fields - if !strings.Contains(output, "task_name: test-task") { - t.Errorf("task frontmatter field 'task_name' not found in output") + // Task frontmatter fields should NOT be in the output + if strings.Contains(output, "task_name: test-task") { + t.Errorf("task frontmatter field 'task_name' should not be in output") } - if !strings.Contains(output, "author: tester") { - t.Errorf("task frontmatter field 'author' not found in output") + if strings.Contains(output, "author: tester") { + t.Errorf("task frontmatter field 'author' should not be in output") } - if !strings.Contains(output, "version: 1.0") { - t.Errorf("task frontmatter field 'version' not found in output") + if strings.Contains(output, "version: 1.0") { + t.Errorf("task frontmatter field 'version' should not be in output") } - // Find the second --- (end of frontmatter) - secondDelimiterIdx := -1 - for i := 1; i < len(lines); i++ { - if lines[i] == "---" { - secondDelimiterIdx = i - break - } - } - if secondDelimiterIdx == -1 { - t.Errorf("expected to find closing frontmatter delimiter '---'") + // Rule frontmatter should NOT be printed + if strings.Contains(output, "language: go") { + t.Errorf("rule frontmatter should not be printed in output") } - // Rule content should appear after frontmatter + // Rule content should be in the output if !strings.Contains(output, "# Test Rule") { t.Errorf("rule content not found in output") } - // Task content should appear after rules + // Task content should be in the output if !strings.Contains(output, "# Test Task") { t.Errorf("task content not found in output") } - // Verify order: frontmatter should come before rules, rules before task content - frontmatterIdx := strings.Index(output, "task_name: test-task") + // Verify order: rules should appear before task content ruleIdx := strings.Index(output, "# Test Rule") taskIdx := strings.Index(output, "# Test Task") - if frontmatterIdx == -1 || ruleIdx == -1 || taskIdx == -1 { + if ruleIdx == -1 || taskIdx == -1 { t.Fatalf("could not find all required sections in output") } - if frontmatterIdx > ruleIdx { - t.Errorf("frontmatter should appear before rules") - } if ruleIdx > taskIdx { t.Errorf("rules should appear before task content") } - - // Rule frontmatter should NOT be printed - if strings.Contains(output, "language: go") { - t.Errorf("rule frontmatter should not be printed in output") - } } func TestTaskBootstrapFromFile(t *testing.T) { diff --git a/main.go b/main.go index 9ff7384..af9cde4 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,6 @@ import ( "strings" "syscall" - yaml "github.com/goccy/go-yaml" "github.com/kitproj/coding-context-cli/pkg/codingcontext" "github.com/kitproj/coding-context-cli/pkg/codingcontext/selectors" "github.com/kitproj/coding-context-cli/pkg/codingcontext/taskparser" @@ -144,40 +143,12 @@ func main() { logger.Info("Rules written", "path", rulesFile) } - // Output only task frontmatter and content - if taskContent := result.Task.FrontMatter.Content; taskContent != nil { - fmt.Println("---") - if err := yaml.NewEncoder(os.Stdout).Encode(taskContent); err != nil { - logger.Error("Failed to encode task frontmatter", "error", err) - os.Exit(1) - } - fmt.Println("---") - } + // Output only task content (task frontmatter is not included) fmt.Println(result.Task.Content) } else { // Normal mode: output everything - // Output task frontmatter (always enabled) - if taskContent := result.Task.FrontMatter.Content; taskContent != nil { - fmt.Println("---") - if err := yaml.NewEncoder(os.Stdout).Encode(taskContent); err != nil { - logger.Error("Failed to encode task frontmatter", "error", err) - os.Exit(1) - } - fmt.Println("---") - } - - // Output available skills metadata (progressive disclosure) - if len(result.Skills.Skills) > 0 { - skillsXML, err := result.Skills.AsXML() - if err != nil { - logger.Error("Failed to encode skills as XML", "error", err) - os.Exit(1) - } - fmt.Println(skillsXML) - fmt.Println() - } - - // Output the combined prompt (rules + task) + // Output the combined prompt (rules + skills + task) + // Note: Task frontmatter is not included in the output fmt.Println(result.Prompt) } }