Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ Options:
-s value
Include rules with matching frontmatter. Can be specified multiple times as key=value.
Note: Only matches top-level YAML fields in frontmatter.
-t Print task frontmatter at the beginning of output.
-a value
Target agent to use (excludes rules from other agents). Supported agents: cursor, opencode, copilot, claude, gemini, augment, windsurf, codex.
```
Expand Down Expand Up @@ -538,13 +537,13 @@ echo "Fetching issue information..." >&2
# Fetch and prepare issue data
```

### Emitting Task Frontmatter
### Task Frontmatter

The `-t` flag allows you to include the task's YAML frontmatter at the beginning of the output. This is useful when the AI agent or downstream tool needs access to metadata about the task being executed.
Task frontmatter is automatically included at the beginning of the output. This allows the AI agent or downstream tool to access metadata about the task being executed.

**Example usage:**
```bash
coding-context -t -p issue_number=123 fix-bug
coding-context -p issue_number=123 fix-bug
```

**Output format:**
Expand All @@ -565,7 +564,7 @@ This can be useful for:

**Example with selectors in frontmatter:**
```bash
coding-context -t implement-feature
coding-context implement-feature
```

If the task has `selectors` in its frontmatter, they will be visible in the output:
Expand Down
6 changes: 3 additions & 3 deletions docs/how-to/create-tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,13 @@ This matches rules where `(language=Go OR language=Python) AND stage=testing`.
coding-context -s priority=high implement-feature
```

## Emitting Task Frontmatter
## Task Frontmatter

Use the `-t` flag to include the task frontmatter in the output. This is useful when downstream tools need access to task metadata.
Task frontmatter is automatically included in the output. This is useful when downstream tools need access to task metadata.

**Example:**
```bash
coding-context -t implement-feature
coding-context implement-feature
```

**Output:**
Expand Down
4 changes: 2 additions & 2 deletions docs/how-to/use-selectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,10 @@ coding-context -s environment=production deploy

### Viewing Task Frontmatter

Use the `-t` flag to see which selectors are embedded in a task:
Task frontmatter (including selectors) is automatically included in the output:

```bash
coding-context -t implement-feature
coding-context implement-feature
```

**Output:**
Expand Down
23 changes: 3 additions & 20 deletions integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -823,27 +823,10 @@ This is a test task.
t.Fatalf("failed to write task file: %v", err)
}

// Test without -t flag (should not print frontmatter)
// Test that frontmatter is always printed
output := runTool(t, "-C", dirs.tmpDir, "test-task")

// Should not contain frontmatter delimiters in the main output
lines := strings.Split(output, "\n")
if len(lines) > 0 && lines[0] == "---" {
t.Errorf("frontmatter should not be printed without -t flag")
}
// Task content should be present
if !strings.Contains(output, "# Test Task") {
t.Errorf("task content not found in output without -t flag")
}
// Rule content should be present
if !strings.Contains(output, "# Test Rule") {
t.Errorf("rule content not found in output without -t flag")
}

// Test with -t flag (should print frontmatter)
output = runTool(t, "-C", dirs.tmpDir, "-t", "test-task")

lines = strings.Split(output, "\n")

// Find the first non-log line (skip lines starting with "time=")
var firstContentLine string
Expand Down Expand Up @@ -884,12 +867,12 @@ This is a test task.

// Rule content should appear after frontmatter
if !strings.Contains(output, "# Test Rule") {
t.Errorf("rule content not found in output with -t flag")
t.Errorf("rule content not found in output")
}

// Task content should appear after rules
if !strings.Contains(output, "# Test Task") {
t.Errorf("task content not found in output with -t flag")
t.Errorf("task content not found in output")
}

// Verify order: frontmatter should come before rules, rules before task content
Expand Down
7 changes: 2 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ func main() {

var workDir string
var resume bool
var emitTaskFrontmatter bool
params := make(codingcontext.Params)
includes := make(codingcontext.Selectors)
var targetAgent codingcontext.TargetAgent
Expand All @@ -33,7 +32,6 @@ func main() {
flag.BoolVar(&resume, "r", false, "Resume mode: skip outputting rules and select task with 'resume: true' in frontmatter.")
flag.Var(&includes, "s", "Include rules with matching frontmatter. Can be specified multiple times as key=value.")
flag.Var(&targetAgent, "a", "Target agent to use (excludes rules from other agents). Supported agents: cursor, opencode, copilot, claude, gemini, augment, windsurf, codex.")
flag.BoolVar(&emitTaskFrontmatter, "t", false, "Print task frontmatter at the beginning of output.")
flag.Func("d", "Remote directory containing rules and tasks. Can be specified multiple times. Supports various protocols via go-getter (http://, https://, git::, s3::, etc.).", func(s string) error {
remotePaths = append(remotePaths, s)
return nil
Expand Down Expand Up @@ -62,7 +60,6 @@ func main() {
codingcontext.WithSelectors(includes),
codingcontext.WithAgent(targetAgent),
codingcontext.WithRemotePaths(remotePaths),
codingcontext.WithEmitTaskFrontmatter(emitTaskFrontmatter),
codingcontext.WithLogger(logger),
)

Expand All @@ -73,8 +70,8 @@ func main() {
os.Exit(1)
}

// Output task frontmatter if requested
if emitTaskFrontmatter && result.Task.FrontMatter != nil {
// Output task frontmatter (always enabled)
if result.Task.FrontMatter != nil {
fmt.Println("---")
if err := yaml.NewEncoder(os.Stdout).Encode(result.Task.FrontMatter); err != nil {
logger.Error("Failed to encode task frontmatter", "error", err)
Expand Down
20 changes: 6 additions & 14 deletions pkg/codingcontext/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@ import (

// Context holds the configuration and state for assembling coding context
type Context struct {
workDir string
resume bool
params Params
includes Selectors
targetAgent TargetAgent
remotePaths []string
emitTaskFrontmatter bool
workDir string
resume bool
params Params
includes Selectors
targetAgent TargetAgent
remotePaths []string

downloadedDirs []string
matchingTaskFile string
Expand Down Expand Up @@ -68,13 +67,6 @@ func WithRemotePaths(paths []string) Option {
}
}

// WithEmitTaskFrontmatter enables task frontmatter emission
func WithEmitTaskFrontmatter(emit bool) Option {
return func(c *Context) {
c.emitTaskFrontmatter = emit
}
}

// WithLogger sets the logger
func WithLogger(logger *slog.Logger) Option {
return func(c *Context) {
Expand Down
51 changes: 21 additions & 30 deletions pkg/codingcontext/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -751,13 +751,12 @@ func TestRunBootstrapScript(t *testing.T) {

func TestWriteTaskFileContent(t *testing.T) {
tests := []struct {
name string
taskFile string
params Params
emitTaskFrontmatter bool
setupFiles func(t *testing.T, tmpDir string) string // returns task file path
expectInOutput string
wantErr bool
name string
taskFile string
params Params
setupFiles func(t *testing.T, tmpDir string) string // returns task file path
expectInOutput string
wantErr bool
}{
{
name: "simple task",
Expand Down Expand Up @@ -821,10 +820,9 @@ func TestWriteTaskFileContent(t *testing.T) {
wantErr: false,
},
{
name: "task with frontmatter emission enabled",
taskFile: "task.md",
params: Params{},
emitTaskFrontmatter: true,
name: "task with frontmatter (always emitted)",
taskFile: "task.md",
params: Params{},
setupFiles: func(t *testing.T, tmpDir string) string {
taskPath := filepath.Join(tmpDir, "task.md")
createMarkdownFile(t, taskPath,
Expand All @@ -851,14 +849,13 @@ func TestWriteTaskFileContent(t *testing.T) {

var logOut bytes.Buffer
cc := &Context{
workDir: tmpDir,
matchingTaskFile: taskPath,
params: tt.params,
emitTaskFrontmatter: tt.emitTaskFrontmatter,
rules: make([]Markdown, 0),
logger: slog.New(slog.NewTextHandler(&logOut, nil)),
includes: make(Selectors),
taskFrontmatter: make(FrontMatter),
workDir: tmpDir,
matchingTaskFile: taskPath,
params: tt.params,
rules: make([]Markdown, 0),
logger: slog.New(slog.NewTextHandler(&logOut, nil)),
includes: make(Selectors),
taskFrontmatter: make(FrontMatter),
}

// Parse task file first
Expand Down Expand Up @@ -891,17 +888,11 @@ func TestWriteTaskFileContent(t *testing.T) {
}
}

// Additional checks for frontmatter emission
if tt.emitTaskFrontmatter {
// Verify frontmatter contains expected fields
if cc.taskFrontmatter != nil {
if _, ok := cc.taskFrontmatter["task_name"]; !ok {
t.Errorf("taskFrontmatter should contain 'task_name' field, got: %v", cc.taskFrontmatter)
}
}
// Verify task content is still present
if !strings.Contains(expandedTask, "# Task with Frontmatter") {
t.Errorf("task content should contain task content, got:\n%s", expandedTask)
// Verify frontmatter is always parsed when present
if cc.taskFrontmatter != nil && len(cc.taskFrontmatter) > 0 {
// Just verify frontmatter was parsed - the Context doesn't emit it, main.go does
if _, ok := cc.taskFrontmatter["task_name"]; !ok {
// This is OK - not all tasks have task_name in frontmatter
}
}

Expand Down
Loading