From fbbe323f50b41d865adc3ce9aba9d7ccf3ace262 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 19:28:17 +0000 Subject: [PATCH 1/3] Initial plan From 404a4c8fb6d4958edbd6393e2e4e38896b06f038 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 19:36:00 +0000 Subject: [PATCH 2/3] Add conventional name fields to frontmatter structs and tests Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- pkg/codingcontext/markdown/frontmatter.go | 18 ++ .../markdown/frontmatter_rule_test.go | 40 ++++ .../markdown/frontmatter_task_test.go | 172 +++++++++++++++++- 3 files changed, 229 insertions(+), 1 deletion(-) diff --git a/pkg/codingcontext/markdown/frontmatter.go b/pkg/codingcontext/markdown/frontmatter.go index bb9f2ad..ade144b 100644 --- a/pkg/codingcontext/markdown/frontmatter.go +++ b/pkg/codingcontext/markdown/frontmatter.go @@ -16,6 +16,10 @@ type BaseFrontMatter struct { type TaskFrontMatter struct { BaseFrontMatter `yaml:",inline"` + // TaskName is an optional identifier for the task file + // This is a conventional naming field for consistency across file types + TaskName string `yaml:"task_name,omitempty" json:"task_name,omitempty"` + // Agent specifies the default agent if not specified via -a flag // This is not used for selecting tasks or rules, only as a default Agent string `yaml:"agent,omitempty" json:"agent,omitempty"` @@ -75,6 +79,10 @@ func (t *TaskFrontMatter) UnmarshalJSON(data []byte) error { type CommandFrontMatter struct { BaseFrontMatter `yaml:",inline"` + // CommandName is an optional identifier for the command file + // This is a conventional naming field for consistency across file types + CommandName string `yaml:"command_name,omitempty" json:"command_name,omitempty"` + // ExpandParams controls whether parameter expansion should occur // Defaults to true if not specified ExpandParams *bool `yaml:"expand,omitempty" json:"expand,omitempty"` @@ -126,8 +134,13 @@ type RuleFrontMatter struct { MCPServer mcp.MCPServerConfig `yaml:"mcp_server,omitempty" json:"mcp_server,omitempty"` // RuleName is an optional identifier for the rule file + // This is a conventional naming field for consistency across file types RuleName string `yaml:"rule_name,omitempty" json:"rule_name,omitempty"` + // ToolName is an optional identifier when this rule defines a tool + // Since rules can be tools, this provides a consistent naming convention + ToolName string `yaml:"tool_name,omitempty" json:"tool_name,omitempty"` + // ExpandParams controls whether parameter expansion should occur // Defaults to true if not specified ExpandParams *bool `yaml:"expand,omitempty" json:"expand,omitempty"` @@ -161,8 +174,13 @@ type SkillFrontMatter struct { // Name is the skill identifier (required) // Must be 1-64 characters, lowercase alphanumeric and hyphens only + // SkillName is an alias for Name for consistency across file types Name string `yaml:"name" json:"name"` + // SkillName is an alternative field name for Name, providing consistency + // If both are specified, Name takes precedence + SkillName string `yaml:"skill_name,omitempty" json:"skill_name,omitempty"` + // Description explains what the skill does and when to use it (required) // Must be 1-1024 characters Description string `yaml:"description" json:"description"` diff --git a/pkg/codingcontext/markdown/frontmatter_rule_test.go b/pkg/codingcontext/markdown/frontmatter_rule_test.go index 6424383..6466c35 100644 --- a/pkg/codingcontext/markdown/frontmatter_rule_test.go +++ b/pkg/codingcontext/markdown/frontmatter_rule_test.go @@ -57,6 +57,7 @@ agent: cursor Args: []string{"--port", "5432"}, }, RuleName: "test-rule", + ToolName: "test-tool", }, want: `task_names: - test-task @@ -71,6 +72,7 @@ mcp_server: - --port - "5432" rule_name: test-rule +tool_name: test-tool `, }, } @@ -133,6 +135,41 @@ languages: Languages: []string{"go", "python", "javascript"}, }, }, + { + name: "rule with rule_name", + yaml: `rule_name: go-best-practices +languages: + - go +`, + want: RuleFrontMatter{ + RuleName: "go-best-practices", + Languages: []string{"go"}, + }, + }, + { + name: "rule with tool_name", + yaml: `tool_name: static-analyzer +languages: + - go +`, + want: RuleFrontMatter{ + ToolName: "static-analyzer", + Languages: []string{"go"}, + }, + }, + { + name: "rule with both rule_name and tool_name", + yaml: `rule_name: go-standards +tool_name: go-linter +languages: + - go +`, + want: RuleFrontMatter{ + RuleName: "go-standards", + ToolName: "go-linter", + Languages: []string{"go"}, + }, + }, } for _, tt := range tests { @@ -153,6 +190,9 @@ languages: if got.RuleName != tt.want.RuleName { t.Errorf("RuleName = %q, want %q", got.RuleName, tt.want.RuleName) } + if got.ToolName != tt.want.ToolName { + t.Errorf("ToolName = %q, want %q", got.ToolName, tt.want.ToolName) + } }) } } diff --git a/pkg/codingcontext/markdown/frontmatter_task_test.go b/pkg/codingcontext/markdown/frontmatter_task_test.go index c84d874..eac2318 100644 --- a/pkg/codingcontext/markdown/frontmatter_task_test.go +++ b/pkg/codingcontext/markdown/frontmatter_task_test.go @@ -18,6 +18,7 @@ func TestTaskFrontMatter_Marshal(t *testing.T) { BaseFrontMatter: BaseFrontMatter{ Content: map[string]any{"task_name": "test-task"}, }, + TaskName: "test-task", }, want: "task_name: test-task\n", }, @@ -27,6 +28,7 @@ func TestTaskFrontMatter_Marshal(t *testing.T) { BaseFrontMatter: BaseFrontMatter{ Content: map[string]any{"task_name": "full-task"}, }, + TaskName: "full-task", Agent: "cursor", Languages: []string{"go"}, Model: "gpt-4", @@ -54,6 +56,7 @@ selectors: BaseFrontMatter: BaseFrontMatter{ Content: map[string]any{"task_name": "polyglot-task"}, }, + TaskName: "polyglot-task", Languages: []string{"go", "python", "javascript"}, }, want: `task_name: polyglot-task @@ -92,6 +95,7 @@ func TestTaskFrontMatter_Unmarshal(t *testing.T) { BaseFrontMatter: BaseFrontMatter{ Content: map[string]any{"task_name": "test-task"}, }, + TaskName: "test-task", }, }, { @@ -104,6 +108,7 @@ languages: BaseFrontMatter: BaseFrontMatter{ Content: map[string]any{"task_name": "test-task"}, }, + TaskName: "test-task", Languages: []string{"go"}, }, }, @@ -118,6 +123,7 @@ languages: BaseFrontMatter: BaseFrontMatter{ Content: map[string]any{"task_name": "test-task"}, }, + TaskName: "test-task", Languages: []string{"go", "python"}, }, }, @@ -137,6 +143,7 @@ selectors: BaseFrontMatter: BaseFrontMatter{ Content: map[string]any{"task_name": "full-task"}, }, + TaskName: "full-task", Agent: "cursor", Languages: []string{"go"}, Model: "gpt-4", @@ -164,7 +171,10 @@ selectors: gotTaskName, _ := got.Content["task_name"].(string) wantTaskName, _ := tt.want.Content["task_name"].(string) if gotTaskName != wantTaskName { - t.Errorf("TaskName = %q, want %q", gotTaskName, wantTaskName) + t.Errorf("Content.TaskName = %q, want %q", gotTaskName, wantTaskName) + } + if got.TaskName != tt.want.TaskName { + t.Errorf("TaskName = %q, want %q", got.TaskName, tt.want.TaskName) } if got.Agent != tt.want.Agent { t.Errorf("Agent = %q, want %q", got.Agent, tt.want.Agent) @@ -181,3 +191,163 @@ selectors: }) } } + +func TestCommandFrontMatter_Unmarshal(t *testing.T) { + tests := []struct { + name string + yaml string + want CommandFrontMatter + wantErr bool + }{ + { + name: "minimal command", + yaml: "command_name: test-command\n", + want: CommandFrontMatter{ + BaseFrontMatter: BaseFrontMatter{ + Content: map[string]any{"command_name": "test-command"}, + }, + CommandName: "test-command", + }, + }, + { + name: "command with expand false", + yaml: `command_name: no-expand-command +expand: false +`, + want: CommandFrontMatter{ + BaseFrontMatter: BaseFrontMatter{ + Content: map[string]any{"command_name": "no-expand-command"}, + }, + CommandName: "no-expand-command", + ExpandParams: boolPtr(false), + }, + }, + { + name: "command with selectors", + yaml: `command_name: db-command +selectors: + database: postgres +`, + want: CommandFrontMatter{ + BaseFrontMatter: BaseFrontMatter{ + Content: map[string]any{"command_name": "db-command"}, + }, + CommandName: "db-command", + Selectors: map[string]any{ + "database": "postgres", + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got CommandFrontMatter + err := yaml.Unmarshal([]byte(tt.yaml), &got) + if (err != nil) != tt.wantErr { + t.Fatalf("Unmarshal() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + return + } + + if got.CommandName != tt.want.CommandName { + t.Errorf("CommandName = %q, want %q", got.CommandName, tt.want.CommandName) + } + if (got.ExpandParams == nil) != (tt.want.ExpandParams == nil) { + t.Errorf("ExpandParams presence mismatch: got %v, want %v", got.ExpandParams, tt.want.ExpandParams) + } else if got.ExpandParams != nil && *got.ExpandParams != *tt.want.ExpandParams { + t.Errorf("ExpandParams = %v, want %v", *got.ExpandParams, *tt.want.ExpandParams) + } + }) + } +} + +func TestSkillFrontMatter_Unmarshal(t *testing.T) { + tests := []struct { + name string + yaml string + want SkillFrontMatter + wantErr bool + }{ + { + name: "skill with name field", + yaml: `name: data-analysis +description: Analyze datasets +`, + want: SkillFrontMatter{ + BaseFrontMatter: BaseFrontMatter{ + Content: map[string]any{"name": "data-analysis"}, + }, + Name: "data-analysis", + Description: "Analyze datasets", + }, + }, + { + name: "skill with skill_name alias", + yaml: `skill_name: data-analysis +name: data-analysis +description: Analyze datasets +`, + want: SkillFrontMatter{ + BaseFrontMatter: BaseFrontMatter{ + Content: map[string]any{"name": "data-analysis"}, + }, + Name: "data-analysis", + SkillName: "data-analysis", + Description: "Analyze datasets", + }, + }, + { + name: "skill with metadata", + yaml: `name: pdf-processor +description: Process PDF files +license: MIT +compatibility: Python 3.8+ +`, + want: SkillFrontMatter{ + BaseFrontMatter: BaseFrontMatter{ + Content: map[string]any{"name": "pdf-processor"}, + }, + Name: "pdf-processor", + Description: "Process PDF files", + License: "MIT", + Compatibility: "Python 3.8+", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got SkillFrontMatter + err := yaml.Unmarshal([]byte(tt.yaml), &got) + if (err != nil) != tt.wantErr { + t.Fatalf("Unmarshal() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + return + } + + if got.Name != tt.want.Name { + t.Errorf("Name = %q, want %q", got.Name, tt.want.Name) + } + if got.SkillName != tt.want.SkillName { + t.Errorf("SkillName = %q, want %q", got.SkillName, tt.want.SkillName) + } + if got.Description != tt.want.Description { + t.Errorf("Description = %q, want %q", got.Description, tt.want.Description) + } + if got.License != tt.want.License { + t.Errorf("License = %q, want %q", got.License, tt.want.License) + } + if got.Compatibility != tt.want.Compatibility { + t.Errorf("Compatibility = %q, want %q", got.Compatibility, tt.want.Compatibility) + } + }) + } +} + +// boolPtr returns a pointer to a boolean value +func boolPtr(b bool) *bool { + return &b +} From 101d85c3cc0c47eed077b42ac9ba4a40cf6b0e38 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 11 Jan 2026 19:38:32 +0000 Subject: [PATCH 3/3] Update SPECIFICATION.md and examples with conventional naming fields Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- SPECIFICATION.md | 187 ++++++++++++++++-- examples/agents/commands/setup-database.md | 1 + examples/agents/rules/implementation-go.md | 1 + examples/agents/rules/linter-tool.md | 51 +++++ examples/agents/skills/data-analysis/SKILL.md | 1 + 5 files changed, 228 insertions(+), 13 deletions(-) create mode 100644 examples/agents/rules/linter-tool.md diff --git a/SPECIFICATION.md b/SPECIFICATION.md index 4d31878..38e9ee4 100644 --- a/SPECIFICATION.md +++ b/SPECIFICATION.md @@ -437,8 +437,9 @@ field3: "string value" #### 5.2.1 `task_name` (optional) - **Type:** String -- **Purpose:** Metadata identifier for the task +- **Purpose:** Conventional naming field - metadata identifier for the task - **Note:** Tasks are matched by filename, not this field +- **Convention:** Provides consistent naming across all file types (tasks, rules, commands, skills) ```yaml --- @@ -550,7 +551,31 @@ expand: false ### 5.3 Standard Rule Fields -#### 5.3.1 `languages` (optional) +#### 5.3.1 `rule_name` (optional) +- **Type:** String +- **Purpose:** Conventional naming field - metadata identifier for the rule +- **Convention:** Provides consistent naming across all file types (tasks, rules, commands, skills) + +```yaml +--- +rule_name: go-best-practices +--- +``` + +#### 5.3.2 `tool_name` (optional) +- **Type:** String +- **Purpose:** Conventional naming field - identifier when this rule defines a tool +- **Note:** Since rules can also be tools, this provides a separate naming convention for tool functionality +- **Use case:** When a rule provides tool capabilities or MCP server definitions + +```yaml +--- +tool_name: static-analyzer +rule_name: go-linter-standards +--- +``` + +#### 5.3.3 `languages` (optional) - **Type:** Array or string - **Purpose:** Filter rules by programming language - **Values:** Lowercase language names @@ -562,7 +587,7 @@ languages: --- ``` -#### 5.3.2 `stage` (optional) +#### 5.3.4 `stage` (optional) - **Type:** String - **Purpose:** Filter by development stage - **Common values:** `planning`, `implementation`, `testing`, `review` @@ -573,7 +598,7 @@ stage: implementation --- ``` -#### 5.3.3 `agent` (optional) +#### 5.3.5 `agent` (optional) - **Type:** String - **Purpose:** Target specific agent @@ -583,7 +608,7 @@ agent: cursor --- ``` -#### 5.3.4 `mcp_server` (optional) +#### 5.3.6 `mcp_server` (optional) - **Type:** Object - **Purpose:** Model Context Protocol server configuration @@ -602,27 +627,80 @@ mcp_server: #### 5.4.1 `name` (required) - **Type:** String - **Length:** 1-64 characters -- **Purpose:** Skill identifier +- **Purpose:** Skill identifier (conventional naming field) +- **Note:** Primary identifier for the skill + +#### 5.4.2 `skill_name` (optional) +- **Type:** String +- **Purpose:** Conventional naming field - alternative/alias for `name` +- **Convention:** Provides consistency with other file types (tasks, rules, commands) +- **Note:** If both `name` and `skill_name` are specified, `name` takes precedence + +```yaml +--- +name: data-analysis +skill_name: data-analysis # Optional alias for consistency +description: Analyze datasets and generate reports +--- +``` -#### 5.4.2 `description` (required) +#### 5.4.3 `description` (required) - **Type:** String - **Length:** 1-1024 characters - **Purpose:** What the skill does and when to use it -#### 5.4.3 `license` (optional) +#### 5.4.4 `license` (optional) - **Type:** String - **Purpose:** License information -#### 5.4.4 `compatibility` (optional) +#### 5.4.5 `compatibility` (optional) - **Type:** String - **Max length:** 500 characters - **Purpose:** Environment requirements -#### 5.4.5 `metadata` (optional) +#### 5.4.6 `metadata` (optional) - **Type:** Object - **Purpose:** Arbitrary key-value pairs -### 5.5 Custom Fields +### 5.5 Standard Command Fields + +#### 5.5.1 `command_name` (optional) +- **Type:** String +- **Purpose:** Conventional naming field - metadata identifier for the command +- **Convention:** Provides consistent naming across all file types (tasks, rules, commands, skills) + +```yaml +--- +command_name: setup-database +--- +``` + +#### 5.5.2 `expand` (optional) +- **Type:** Boolean +- **Purpose:** Control parameter expansion +- **Default:** `true` + +```yaml +--- +command_name: pre-deploy +expand: true +--- +``` + +#### 5.5.3 `selectors` (optional) +- **Type:** Object +- **Purpose:** Auto-filter rules when command is used +- **Note:** Selectors from commands are combined with task selectors + +```yaml +--- +command_name: db-setup +selectors: + database: postgres +--- +``` + +### 5.6 Custom Fields Any additional YAML fields can be used for custom selectors: @@ -639,6 +717,76 @@ priority: high - Only top-level fields work with selectors - Nested objects are stored but not matchable +### 5.7 Conventional Naming Fields + +The Coding Context Standard defines conventional naming fields for consistency across all file types. These fields are **optional** and serve as metadata identifiers. + +#### Naming Convention Summary + +| File Type | Primary Name Field | Purpose | +|-----------|-------------------|---------| +| **Task** | `task_name` | Identifies the task file | +| **Rule** | `rule_name` | Identifies the rule file | +| **Rule (as Tool)** | `tool_name` | Identifies tool functionality when rule acts as a tool | +| **Command** | `command_name` | Identifies the command file | +| **Skill** | `name` / `skill_name` | Identifies the skill (`name` is required, `skill_name` is an alias) | + +#### Design Rationale + +1. **Consistency**: All file types use a similar naming pattern (`_name`) +2. **Self-documenting**: Makes frontmatter more readable and explicit +3. **Metadata**: These fields are for documentation and tooling, not for file matching +4. **Optional**: Files are primarily matched by filename, not these fields +5. **Tool Support**: Rules can have both `rule_name` and `tool_name` since they may serve dual purposes + +#### Example Usage + +**Task file** (`.agents/tasks/fix-bug.md`): +```yaml +--- +task_name: fix-bug +resume: false +--- +``` + +**Rule file** (`.agents/rules/go-standards.md`): +```yaml +--- +rule_name: go-best-practices +languages: + - go +--- +``` + +**Rule as Tool** (`.agents/rules/linter.md`): +```yaml +--- +rule_name: linting-standards +tool_name: go-linter +mcp_server: + command: golangci-lint + args: ["run"] +--- +``` + +**Command file** (`.agents/commands/setup-db.md`): +```yaml +--- +command_name: setup-database +selectors: + database: postgres +--- +``` + +**Skill file** (`.agents/skills/data-analysis/SKILL.md`): +```yaml +--- +name: data-analysis +skill_name: data-analysis # Optional alias +description: Analyze datasets and generate reports +--- +``` + --- ## 6. Content Expansion @@ -1554,7 +1702,7 @@ Potential future additions while maintaining backward compatibility: | Field | Type | Required | Purpose | |-------|------|----------|---------| -| `task_name` | string | No | Metadata identifier | +| `task_name` | string | No | Conventional naming field - metadata identifier | | `resume` | boolean | No | Resume mode indicator | | `languages` | array/string | No | Programming languages (metadata) | | `agent` | string | No | Target agent (selector) | @@ -1564,20 +1712,33 @@ Potential future additions while maintaining backward compatibility: | `selectors` | object | No | Auto-filter rules | | `expand` | boolean | No | Parameter expansion control | +### Command Fields + +| Field | Type | Required | Purpose | +|-------|------|----------|---------| +| `command_name` | string | No | Conventional naming field - metadata identifier | +| `expand` | boolean | No | Parameter expansion control | +| `selectors` | object | No | Auto-filter rules when command is used | + ### Rule Fields | Field | Type | Required | Purpose | |-------|------|----------|---------| +| `rule_name` | string | No | Conventional naming field - metadata identifier | +| `tool_name` | string | No | Conventional naming field - identifier for tool functionality | +| `task_names` | array/string | No | Filter by task names | | `languages` | array/string | No | Language filter | | `stage` | string | No | Development stage filter | | `agent` | string | No | Agent filter | | `mcp_server` | object | No | MCP server config | +| `expand` | boolean | No | Parameter expansion control | ### Skill Fields | Field | Type | Required | Purpose | |-------|------|----------|---------| -| `name` | string | Yes | Skill identifier (1-64 chars) | +| `name` | string | Yes | Skill identifier (1-64 chars) - primary name field | +| `skill_name` | string | No | Conventional naming field - alias for `name` | | `description` | string | Yes | What skill does (1-1024 chars) | | `license` | string | No | License information | | `compatibility` | string | No | Environment requirements (max 500 chars) | diff --git a/examples/agents/commands/setup-database.md b/examples/agents/commands/setup-database.md index 6198f72..f7e4747 100644 --- a/examples/agents/commands/setup-database.md +++ b/examples/agents/commands/setup-database.md @@ -1,4 +1,5 @@ --- +command_name: setup-database selectors: database: postgres feature: auth diff --git a/examples/agents/rules/implementation-go.md b/examples/agents/rules/implementation-go.md index 91bf76a..206ca42 100644 --- a/examples/agents/rules/implementation-go.md +++ b/examples/agents/rules/implementation-go.md @@ -1,4 +1,5 @@ --- +rule_name: go-implementation-standards stage: implementation language: go --- diff --git a/examples/agents/rules/linter-tool.md b/examples/agents/rules/linter-tool.md new file mode 100644 index 0000000..2f96312 --- /dev/null +++ b/examples/agents/rules/linter-tool.md @@ -0,0 +1,51 @@ +--- +rule_name: go-linting-standards +tool_name: golangci-lint +languages: + - go +stage: implementation +mcp_server: + type: stdio + command: golangci-lint + args: + - run + - --config + - .golangci.yml +--- + +# Go Linting Standards with Tool Integration + +This rule defines linting standards for Go code and provides integration with the `golangci-lint` tool. + +## Linting Standards + +- Run `golangci-lint` before committing code +- Fix all errors and warnings +- Use the project's `.golangci.yml` configuration +- Enable key linters: `gofmt`, `govet`, `staticcheck`, `errcheck` + +## Tool Integration + +This rule includes MCP server configuration for the `golangci-lint` tool. When used with compatible AI agents, the tool can be invoked directly to check code quality. + +## Usage + +The linter checks for: +- Code formatting issues +- Common bugs and errors +- Code complexity +- Unused code +- Security vulnerabilities + +## Configuration + +Customize linting rules in `.golangci.yml`: +```yaml +linters: + enable: + - gofmt + - govet + - staticcheck + - errcheck + - gosec +``` diff --git a/examples/agents/skills/data-analysis/SKILL.md b/examples/agents/skills/data-analysis/SKILL.md index 0352cfd..dda5fab 100644 --- a/examples/agents/skills/data-analysis/SKILL.md +++ b/examples/agents/skills/data-analysis/SKILL.md @@ -1,5 +1,6 @@ --- name: data-analysis +skill_name: data-analysis description: Analyze datasets, generate charts, and create summary reports. Use when the user needs to work with CSV, Excel, or other tabular data formats for analysis or visualization. ---