From affa4dbb96b9339e79915b4583acd2c6103bc3a3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Dec 2025 18:37:16 +0000 Subject: [PATCH 1/6] Initial plan From 5bd47303b5aba15719dfd7caa531334670f7f42b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Dec 2025 18:42:30 +0000 Subject: [PATCH 2/6] Initial plan for fixing task frontmatter printing issue Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- examples/.agents | 1 + 1 file changed, 1 insertion(+) create mode 120000 examples/.agents 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 From 4a4cf311cb431c14a7baf508a2ea905532e75b9e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Dec 2025 18:47:28 +0000 Subject: [PATCH 3/6] Remove task frontmatter from output and update documentation Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- README.md | 51 +++++++++++++++++++------------------ SPECIFICATION.md | 36 +++++++++++++------------- integration_test.go | 62 +++++++++++---------------------------------- main.go | 22 ++-------------- 4 files changed, 61 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index bb34257..20867c8 100644 --- a/README.md +++ b/README.md @@ -18,20 +18,14 @@ 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 │ │ -│ └───────────────────────────────────────────────────────┘ │ -│ │ -│ ┌───────────────────────────────────────────────────────┐ │ -│ │ 2. Skills Metadata (XML) - Optional │ │ +│ │ 1. Skills Metadata (XML) - Optional │ │ │ │ • Available skills with names & descriptions │ │ │ │ • Progressive disclosure - full content on demand │ │ │ │ • Only included if skills are discovered │ │ │ └───────────────────────────────────────────────────────┘ │ │ │ │ ┌───────────────────────────────────────────────────────┐ │ -│ │ 3. Rules Content (Markdown) │ │ +│ │ 2. Rules Content (Markdown) │ │ │ │ • Coding standards and guidelines │ │ │ │ • Team conventions and best practices │ │ │ │ • Filtered by selectors (-s flag) │ │ @@ -39,7 +33,7 @@ The tool assembles context into a structured prompt with the following component │ └───────────────────────────────────────────────────────┘ │ │ │ │ ┌───────────────────────────────────────────────────────┐ │ -│ │ 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 - **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..07547be 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,17 @@ Remote directories support (via go-getter): ### 11.1 Assembly Order -1. **Task frontmatter** (YAML format, automatically included if present) -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) +1. **Skill metadata** (XML format, if skills found) +2. **Rule content** (all included rules, content only) +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 @@ -1148,8 +1145,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 +1392,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/integration_test.go b/integration_test.go index 60fe9d1..792f9ba 100644 --- a/integration_test.go +++ b/integration_test.go @@ -974,78 +974,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..a159515 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,28 +143,10 @@ 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() @@ -178,6 +159,7 @@ func main() { } // Output the combined prompt (rules + task) + // Note: Task frontmatter is not included in the output fmt.Println(result.Prompt) } } From b1c10e6becd6c250266c0a9764f6f6ffb2a31214 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Dec 2025 18:56:45 +0000 Subject: [PATCH 4/6] Fix skills ordering: remove duplicate skills from Prompt Skills were being printed twice (once in main.go and once in the Prompt). Removed skills from the Prompt so they are only printed once in main.go, maintaining the correct order: skills -> rules -> task. Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- pkg/codingcontext/context.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/pkg/codingcontext/context.go b/pkg/codingcontext/context.go index 49d3973..d4158ac 100644 --- a/pkg/codingcontext/context.go +++ b/pkg/codingcontext/context.go @@ -348,22 +348,6 @@ func (cc *Context) Run(ctx context.Context, taskName string) (*Result, error) { promptBuilder.WriteString("\n") } - // Add skills section if there are any skills - if len(cc.skills.Skills) > 0 { - promptBuilder.WriteString("\n# Skills\n\n") - promptBuilder.WriteString("You have access to the following skills. Skills are specialized capabilities that provide ") - promptBuilder.WriteString("domain expertise, workflows, and procedural knowledge. When a task matches a skill's ") - promptBuilder.WriteString("description, you can load the full skill content by reading the SKILL.md file at the ") - promptBuilder.WriteString("location provided.\n\n") - - skillsXML, err := cc.skills.AsXML() - if err != nil { - return nil, fmt.Errorf("failed to encode skills as XML: %w", err) - } - promptBuilder.WriteString(skillsXML) - promptBuilder.WriteString("\n\n") - } - promptBuilder.WriteString(cc.task.Content) // Build and return the result From 3fcddbee6dfd795173f4b1b62e5fb35141a19313 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Dec 2025 19:02:59 +0000 Subject: [PATCH 5/6] Fix skills ordering: rules -> skills -> task Corrected the output order to be rules, then skills, then task. Skills are now part of the Prompt (result.Prompt field) as intended, appearing between rules and task content. Updated documentation to reflect the correct order. Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- README.md | 18 +++++++++--------- SPECIFICATION.md | 23 +++++++++++++---------- main.go | 13 +------------ pkg/codingcontext/context.go | 16 ++++++++++++++++ 4 files changed, 39 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 20867c8..fb11ebc 100644 --- a/README.md +++ b/README.md @@ -18,14 +18,7 @@ The tool assembles context into a structured prompt with the following component ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌───────────────────────────────────────────────────────┐ │ -│ │ 1. Skills Metadata (XML) - Optional │ │ -│ │ • Available skills with names & descriptions │ │ -│ │ • Progressive disclosure - full content on demand │ │ -│ │ • Only included if skills are discovered │ │ -│ └───────────────────────────────────────────────────────┘ │ -│ │ -│ ┌───────────────────────────────────────────────────────┐ │ -│ │ 2. Rules Content (Markdown) │ │ +│ │ 1. Rules Content (Markdown) │ │ │ │ • Coding standards and guidelines │ │ │ │ • Team conventions and best practices │ │ │ │ • Filtered by selectors (-s flag) │ │ @@ -33,6 +26,13 @@ The tool assembles context into a structured prompt with the following component │ └───────────────────────────────────────────────────────┘ │ │ │ │ ┌───────────────────────────────────────────────────────┐ │ +│ │ 2. Skills Metadata (XML) - Optional │ │ +│ │ • Available skills with names & descriptions │ │ +│ │ • Progressive disclosure - full content on demand │ │ +│ │ • Only included if skills are discovered │ │ +│ └───────────────────────────────────────────────────────┘ │ +│ │ +│ ┌───────────────────────────────────────────────────────┐ │ │ │ 3. Task Content (Markdown) │ │ │ │ • Task-specific instructions │ │ │ │ • Parameter substitutions (${param}) │ │ @@ -44,8 +44,8 @@ The tool assembles context into a structured prompt with the following component ``` **Key Points:** -- **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 diff --git a/SPECIFICATION.md b/SPECIFICATION.md index 07547be..ddb4dd2 100644 --- a/SPECIFICATION.md +++ b/SPECIFICATION.md @@ -1096,8 +1096,8 @@ Remote directories support (via go-getter): ### 11.1 Assembly Order -1. **Skill metadata** (XML format, if skills found) -2. **Rule content** (all included rules, content only) +1. **Rule content** (all included rules, content only) +2. **Skill metadata** (XML format, if skills found) 3. **Task content** (with expansions applied) 4. **User prompt** (if provided, after `---` delimiter) @@ -1107,6 +1107,17 @@ Remote directories support (via go-getter): **To stdout (the context):** ```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. @@ -1116,14 +1127,6 @@ Remote directories support (via go-getter): -# Rule 1 Content - -Rule 1 text... - -# Rule 2 Content - -Rule 2 text... - # Task Content Fix bug #123... diff --git a/main.go b/main.go index a159515..af9cde4 100644 --- a/main.go +++ b/main.go @@ -147,18 +147,7 @@ func main() { fmt.Println(result.Task.Content) } else { // Normal mode: output everything - // 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) } diff --git a/pkg/codingcontext/context.go b/pkg/codingcontext/context.go index d4158ac..49d3973 100644 --- a/pkg/codingcontext/context.go +++ b/pkg/codingcontext/context.go @@ -348,6 +348,22 @@ func (cc *Context) Run(ctx context.Context, taskName string) (*Result, error) { promptBuilder.WriteString("\n") } + // Add skills section if there are any skills + if len(cc.skills.Skills) > 0 { + promptBuilder.WriteString("\n# Skills\n\n") + promptBuilder.WriteString("You have access to the following skills. Skills are specialized capabilities that provide ") + promptBuilder.WriteString("domain expertise, workflows, and procedural knowledge. When a task matches a skill's ") + promptBuilder.WriteString("description, you can load the full skill content by reading the SKILL.md file at the ") + promptBuilder.WriteString("location provided.\n\n") + + skillsXML, err := cc.skills.AsXML() + if err != nil { + return nil, fmt.Errorf("failed to encode skills as XML: %w", err) + } + promptBuilder.WriteString(skillsXML) + promptBuilder.WriteString("\n\n") + } + promptBuilder.WriteString(cc.task.Content) // Build and return the result From fcdf06ea5d115af88a0d5e8f9184572acdf34a68 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Dec 2025 19:35:50 +0000 Subject: [PATCH 6/6] Fix TestExpanderSecurityIntegration to work without task frontmatter Updated the test to identify stdout by filtering out stderr log lines (lines starting with "time=") instead of looking for the frontmatter delimiter "---" which is no longer printed. Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- integration_test.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/integration_test.go b/integration_test.go index 792f9ba..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) } }