Skip to content

Commit e1a3066

Browse files
committed
feat: replace "id" by "urn"
1 parent 8617256 commit e1a3066

12 files changed

Lines changed: 150 additions & 224 deletions

go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ go 1.24.5
44

55
require (
66
github.com/alecthomas/participle/v2 v2.1.4
7-
github.com/goccy/go-yaml v1.18.0
87
github.com/hashicorp/go-getter/v2 v2.2.3
98
github.com/stretchr/testify v1.10.0
9+
gopkg.in/yaml.v3 v3.0.1
1010
)
1111

1212
require (
@@ -22,5 +22,4 @@ require (
2222
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
2323
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
2424
github.com/ulikunitz/xz v0.5.8 // indirect
25-
gopkg.in/yaml.v3 v3.0.1 // indirect
2625
)

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1U
88
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
99
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
1010
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
11-
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
12-
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
1311
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
1412
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
1513
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=

pkg/codingcontext/context.go

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,6 @@ func New(opts ...Option) *Context {
5858
return c
5959
}
6060

61-
// generateIDFromPath generates an ID from a file path by extracting the filename without extension.
62-
// Used to auto-set ID fields in frontmatter when not explicitly provided.
63-
func generateIDFromPath(path string) string {
64-
baseName := filepath.Base(path)
65-
ext := filepath.Ext(baseName)
66-
return strings.TrimSuffix(baseName, ext)
67-
}
68-
6961
type markdownVisitor func(path string, fm *markdown.BaseFrontMatter) error
7062

7163
// findMarkdownFile searches for a markdown file by name in the given directories.
@@ -139,11 +131,6 @@ func (cc *Context) findTask(taskName string) error {
139131
return fmt.Errorf("failed to parse task file %s: %w", path, err)
140132
}
141133

142-
// Automatically set ID to filename (without extension) if not set in frontmatter
143-
if frontMatter.ID == "" {
144-
frontMatter.ID = generateIDFromPath(path)
145-
}
146-
147134
// Extract selector labels from task frontmatter and add them to cc.includes.
148135
// This combines CLI selectors (from -s flag) with task selectors using OR logic:
149136
// rules match if their frontmatter value matches ANY selector value for a given key.
@@ -244,11 +231,6 @@ func (cc *Context) findCommand(commandName string, params taskparser.Params) (st
244231
return fmt.Errorf("failed to parse command file %s: %w", path, err)
245232
}
246233

247-
// Automatically set ID to filename (without extension) if not set in frontmatter
248-
if frontMatter.ID == "" {
249-
frontMatter.ID = generateIDFromPath(path)
250-
}
251-
252234
// Extract selector labels from command frontmatter and add them to cc.includes.
253235
// This combines CLI selectors, task selectors, and command selectors using OR logic:
254236
// rules match if their frontmatter value matches ANY selector value for a given key.
@@ -536,11 +518,6 @@ func (cc *Context) findExecuteRuleFiles(ctx context.Context, homeDir string) err
536518
return fmt.Errorf("failed to parse markdown file %s: %w", path, err)
537519
}
538520

539-
// Automatically set ID to filename (without extension) if not set in frontmatter
540-
if frontmatter.ID == "" {
541-
frontmatter.ID = generateIDFromPath(path)
542-
}
543-
544521
// Expand parameters only if expand is not explicitly set to false
545522
var processedContent string
546523
if shouldExpandParams(frontmatter.ExpandParams) {

pkg/codingcontext/context_test.go

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -385,28 +385,15 @@ func TestContext_Run_Basic(t *testing.T) {
385385
},
386386
},
387387
{
388-
name: "task ID automatically set from filename",
388+
name: "task with explicit URN in frontmatter",
389389
setup: func(t *testing.T, dir string) {
390-
createTask(t, dir, "my-task", "", "Task content")
391-
},
392-
taskName: "my-task",
393-
wantErr: false,
394-
check: func(t *testing.T, result *Result) {
395-
if result.Task.FrontMatter.ID != "my-task" {
396-
t.Errorf("expected task ID 'my-task', got %q", result.Task.FrontMatter.ID)
397-
}
398-
},
399-
},
400-
{
401-
name: "task with explicit ID in frontmatter",
402-
setup: func(t *testing.T, dir string) {
403-
createTask(t, dir, "file-name", "id: explicit-task-id", "Task content")
390+
createTask(t, dir, "file-name", "id: urn:agents:task:file-name", "Task content")
404391
},
405392
taskName: "file-name",
406393
wantErr: false,
407394
check: func(t *testing.T, result *Result) {
408-
if result.Task.FrontMatter.ID != "explicit-task-id" {
409-
t.Errorf("expected task ID 'explicit-task-id', got %q", result.Task.FrontMatter.ID)
395+
if result.Task.FrontMatter.URN != "urn:agents:task:file-name" {
396+
t.Errorf("expected task URN 'urn:agents:task:file-name', got %q", result.Task.FrontMatter.URN)
410397
}
411398
},
412399
},
@@ -772,11 +759,11 @@ func TestContext_Run_Rules(t *testing.T) {
772759
},
773760
},
774761
{
775-
name: "rule IDs automatically set from filename",
762+
name: "rule URN set from frontmatter",
776763
setup: func(t *testing.T, dir string) {
777764
createTask(t, dir, "id-task", "", "Task")
778-
createRule(t, dir, ".agents/rules/my-rule.md", "", "Rule without ID in frontmatter")
779-
createRule(t, dir, ".agents/rules/another-rule.md", "id: explicit-id", "Rule with explicit ID")
765+
createRule(t, dir, ".agents/rules/my-rule.md", "id: urn:agents:rule:my-rule", "Rule with URN")
766+
createRule(t, dir, ".agents/rules/another-rule.md", "id: urn:agents:rule:another", "Rule with another URN")
780767
},
781768
taskName: "id-task",
782769
wantErr: false,
@@ -785,29 +772,28 @@ func TestContext_Run_Rules(t *testing.T) {
785772
t.Fatalf("expected 2 rules, got %d", len(result.Rules))
786773
}
787774

788-
// Check that one rule has auto-generated ID from filename
789775
foundMyRule := false
790776
foundAnotherRule := false
791777
for _, rule := range result.Rules {
792-
if rule.FrontMatter.ID == "my-rule" {
778+
if rule.FrontMatter.URN == "urn:agents:rule:my-rule" {
793779
foundMyRule = true
794-
if !strings.Contains(rule.Content, "Rule without ID") {
795-
t.Error("my-rule should contain 'Rule without ID'")
780+
if !strings.Contains(rule.Content, "Rule with URN") {
781+
t.Error("my-rule should contain 'Rule with URN'")
796782
}
797783
}
798-
if rule.FrontMatter.ID == "explicit-id" {
784+
if rule.FrontMatter.URN == "urn:agents:rule:another" {
799785
foundAnotherRule = true
800-
if !strings.Contains(rule.Content, "Rule with explicit ID") {
801-
t.Error("explicit-id should contain 'Rule with explicit ID'")
786+
if !strings.Contains(rule.Content, "Rule with another URN") {
787+
t.Error("another should contain 'Rule with another URN'")
802788
}
803789
}
804790
}
805791

806792
if !foundMyRule {
807-
t.Error("expected to find rule with auto-generated ID 'my-rule'")
793+
t.Error("expected to find rule with URN 'urn:agents:rule:my-rule'")
808794
}
809795
if !foundAnotherRule {
810-
t.Error("expected to find rule with explicit ID 'explicit-id'")
796+
t.Error("expected to find rule with URN 'urn:agents:rule:another'")
811797
}
812798
},
813799
},

pkg/codingcontext/markdown/frontmatter.go

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,23 @@ import (
99

1010
// BaseFrontMatter represents parsed YAML frontmatter from markdown files
1111
type BaseFrontMatter struct {
12-
Content map[string]any `json:"-" yaml:",inline"`
13-
}
14-
15-
// TaskFrontMatter represents the standard frontmatter fields for task files
16-
type TaskFrontMatter struct {
17-
BaseFrontMatter `yaml:",inline"`
18-
19-
// ID is an optional unique identifier for the task
20-
// Metadata only, does not affect task matching or filtering
21-
ID string `yaml:"id,omitempty" json:"id,omitempty"`
12+
// URN is an optional unique identifier for the prompt in URN format (e.g. urn:agents:task:<name>)
13+
// Automatically inferred from filename if not specified in frontmatter
14+
URN string `yaml:"id,omitempty" json:"id,omitempty"`
2215

2316
// Name is an optional human-readable name for the task
2417
// Metadata only, does not affect task matching or filtering
2518
Name string `yaml:"name,omitempty" json:"name,omitempty"`
2619

2720
// Description is an optional description of what the task does
2821
// Metadata only, does not affect task matching or filtering
29-
Description string `yaml:"description,omitempty" json:"description,omitempty"`
22+
Description string `yaml:"description,omitempty" json:"description,omitempty"`
23+
Content map[string]any `json:"-" yaml:",inline"`
24+
}
25+
26+
// TaskFrontMatter represents the standard frontmatter fields for task files
27+
type TaskFrontMatter struct {
28+
BaseFrontMatter `yaml:",inline"`
3029

3130
// Agent specifies the default agent if not specified via -a flag
3231
// This is not used for selecting tasks or rules, only as a default
@@ -87,18 +86,6 @@ func (t *TaskFrontMatter) UnmarshalJSON(data []byte) error {
8786
type CommandFrontMatter struct {
8887
BaseFrontMatter `yaml:",inline"`
8988

90-
// ID is an optional unique identifier for the command
91-
// Metadata only, does not affect command matching or filtering
92-
ID string `yaml:"id,omitempty" json:"id,omitempty"`
93-
94-
// Name is an optional human-readable name for the command
95-
// Metadata only, does not affect command matching or filtering
96-
Name string `yaml:"name,omitempty" json:"name,omitempty"`
97-
98-
// Description is an optional description of what the command does
99-
// Metadata only, does not affect command matching or filtering
100-
Description string `yaml:"description,omitempty" json:"description,omitempty"`
101-
10289
// ExpandParams controls whether parameter expansion should occur
10390
// Defaults to true if not specified
10491
ExpandParams *bool `yaml:"expand,omitempty" json:"expand,omitempty"`
@@ -134,18 +121,6 @@ func (c *CommandFrontMatter) UnmarshalJSON(data []byte) error {
134121
type RuleFrontMatter struct {
135122
BaseFrontMatter `yaml:",inline"`
136123

137-
// ID is an optional unique identifier for the rule
138-
// Metadata only, does not affect rule matching or filtering
139-
ID string `yaml:"id,omitempty" json:"id,omitempty"`
140-
141-
// Name is an optional human-readable name for the rule
142-
// Metadata only, does not affect rule matching or filtering
143-
Name string `yaml:"name,omitempty" json:"name,omitempty"`
144-
145-
// Description is an optional description of what the rule provides
146-
// Metadata only, does not affect rule matching or filtering
147-
Description string `yaml:"description,omitempty" json:"description,omitempty"`
148-
149124
// TaskNames specifies which task(s) this rule applies to
150125
// Array of task names for OR logic
151126
TaskNames []string `yaml:"task_names,omitempty" json:"task_names,omitempty"`

pkg/codingcontext/markdown/frontmatter_command_test.go

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package markdown
33
import (
44
"testing"
55

6-
"github.com/goccy/go-yaml"
6+
"gopkg.in/yaml.v3"
77
)
88

99
func TestCommandFrontMatter_Marshal(t *testing.T) {
@@ -20,27 +20,31 @@ func TestCommandFrontMatter_Marshal(t *testing.T) {
2020
{
2121
name: "command with standard id, name, description",
2222
command: CommandFrontMatter{
23-
ID: "cmd-123",
24-
Name: "Standard Command",
25-
Description: "This is a standard command with metadata",
23+
BaseFrontMatter: BaseFrontMatter{
24+
URN: "urn:agents:command:standard",
25+
Name: "Standard Command",
26+
Description: "This is a standard command with metadata",
27+
},
2628
},
27-
want: `id: cmd-123
29+
want: `id: urn:agents:command:standard
2830
name: Standard Command
2931
description: This is a standard command with metadata
3032
`,
3133
},
3234
{
3335
name: "command with expand false",
3436
command: CommandFrontMatter{
35-
ID: "cmd-456",
36-
Name: "No Expand Command",
37-
Description: "Command with expansion disabled",
37+
BaseFrontMatter: BaseFrontMatter{
38+
URN: "urn:agents:command:no-expand",
39+
Name: "No Expand Command",
40+
Description: "Command with expansion disabled",
41+
},
3842
ExpandParams: func() *bool {
3943
b := false
4044
return &b
4145
}(),
4246
},
43-
want: `id: cmd-456
47+
want: `id: urn:agents:command:no-expand
4448
name: No Expand Command
4549
description: Command with expansion disabled
4650
expand: false
@@ -49,15 +53,17 @@ expand: false
4953
{
5054
name: "command with selectors",
5155
command: CommandFrontMatter{
52-
ID: "cmd-789",
53-
Name: "Selector Command",
54-
Description: "Command with selectors",
56+
BaseFrontMatter: BaseFrontMatter{
57+
URN: "urn:agents:command:selector",
58+
Name: "Selector Command",
59+
Description: "Command with selectors",
60+
},
5561
Selectors: map[string]any{
5662
"database": "postgres",
5763
"feature": "auth",
5864
},
5965
},
60-
want: `id: cmd-789
66+
want: `id: urn:agents:command:selector
6167
name: Selector Command
6268
description: Command with selectors
6369
selectors:
@@ -89,27 +95,31 @@ func TestCommandFrontMatter_Unmarshal(t *testing.T) {
8995
}{
9096
{
9197
name: "command with standard id, name, description",
92-
yaml: `id: cmd-abc
98+
yaml: `id: urn:agents:command:named
9399
name: Named Command
94100
description: A command with standard fields
95101
`,
96102
want: CommandFrontMatter{
97-
ID: "cmd-abc",
98-
Name: "Named Command",
99-
Description: "A command with standard fields",
103+
BaseFrontMatter: BaseFrontMatter{
104+
URN: "urn:agents:command:named",
105+
Name: "Named Command",
106+
Description: "A command with standard fields",
107+
},
100108
},
101109
},
102110
{
103111
name: "command with expand false",
104-
yaml: `id: cmd-def
112+
yaml: `id: urn:agents:command:no-expand
105113
name: No Expand
106114
description: No expansion
107115
expand: false
108116
`,
109117
want: CommandFrontMatter{
110-
ID: "cmd-def",
111-
Name: "No Expand",
112-
Description: "No expansion",
118+
BaseFrontMatter: BaseFrontMatter{
119+
URN: "urn:agents:command:no-expand",
120+
Name: "No Expand",
121+
Description: "No expansion",
122+
},
113123
ExpandParams: func() *bool {
114124
b := false
115125
return &b
@@ -118,17 +128,19 @@ expand: false
118128
},
119129
{
120130
name: "command with selectors",
121-
yaml: `id: cmd-ghi
131+
yaml: `id: urn:agents:command:selector
122132
name: Selector Command
123133
description: Has selectors
124134
selectors:
125135
database: postgres
126136
feature: auth
127137
`,
128138
want: CommandFrontMatter{
129-
ID: "cmd-ghi",
130-
Name: "Selector Command",
131-
Description: "Has selectors",
139+
BaseFrontMatter: BaseFrontMatter{
140+
URN: "urn:agents:command:selector",
141+
Name: "Selector Command",
142+
Description: "Has selectors",
143+
},
132144
Selectors: map[string]any{
133145
"database": "postgres",
134146
"feature": "auth",
@@ -149,8 +161,8 @@ selectors:
149161
}
150162

151163
// Compare fields individually
152-
if got.ID != tt.want.ID {
153-
t.Errorf("ID = %q, want %q", got.ID, tt.want.ID)
164+
if got.URN != tt.want.URN {
165+
t.Errorf("URN = %q, want %q", got.URN, tt.want.URN)
154166
}
155167
if got.Name != tt.want.Name {
156168
t.Errorf("Name = %q, want %q", got.Name, tt.want.Name)

0 commit comments

Comments
 (0)