From f77bbb9aba4f6905edf70d1303706fc1ef89826b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 18:48:20 +0000 Subject: [PATCH 1/5] Initial plan From e388f807a8d9e72f37ff344e8b31e4184f152424 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 18:54:21 +0000 Subject: [PATCH 2/5] Change MCPServers() to return map from rule ID to MCPServerConfig - Modified Result.MCPServers() to return map[string]mcp.MCPServerConfig - Use rule ID as map key, with fallback to "rule-" for rules without IDs - Updated all tests to verify map-based behavior - Updated documentation to reflect new API Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- pkg/codingcontext/README.md | 6 +- pkg/codingcontext/result.go | 20 ++++-- pkg/codingcontext/result_test.go | 113 ++++++++++++++++++++++++++----- 3 files changed, 113 insertions(+), 26 deletions(-) diff --git a/pkg/codingcontext/README.md b/pkg/codingcontext/README.md index 649b0d1..de359d4 100644 --- a/pkg/codingcontext/README.md +++ b/pkg/codingcontext/README.md @@ -116,8 +116,8 @@ func main() { // Access MCP server configurations mcpServers := result.MCPServers() - for i, config := range mcpServers { - fmt.Printf("MCP Server %d: %s\n", i, config.Command) + for id, config := range mcpServers { + fmt.Printf("MCP Server %s: %s\n", id, config.Command) } } ``` @@ -139,7 +139,7 @@ Result holds the assembled context from running a task: - `Agent Agent` - The agent used (from task frontmatter or option) **Methods:** -- `MCPServers() []MCPServerConfig` - Returns all MCP server configurations from rules as a slice +- `MCPServers() map[string]MCPServerConfig` - Returns all MCP server configurations from rules as a map from rule ID to configuration #### `Markdown[T]` diff --git a/pkg/codingcontext/result.go b/pkg/codingcontext/result.go index e737302..5f1ef15 100644 --- a/pkg/codingcontext/result.go +++ b/pkg/codingcontext/result.go @@ -1,6 +1,8 @@ package codingcontext import ( + "fmt" + "github.com/kitproj/coding-context-cli/pkg/codingcontext/markdown" "github.com/kitproj/coding-context-cli/pkg/codingcontext/mcp" "github.com/kitproj/coding-context-cli/pkg/codingcontext/skills" @@ -16,19 +18,25 @@ type Result struct { Prompt string // Combined prompt: all rules and task content } -// MCPServers returns all MCP server configurations from rules. +// MCPServers returns all MCP server configurations from rules as a map. // Each rule can specify one MCP server configuration. -// Returns a slice of all configured MCP servers from rules only. +// Returns a map from rule ID to MCP server configuration. // Empty/zero-value MCP server configurations are filtered out. -func (r *Result) MCPServers() []mcp.MCPServerConfig { - var servers []mcp.MCPServerConfig +// Rules without an ID field are included with a generated key "rule-". +func (r *Result) MCPServers() map[string]mcp.MCPServerConfig { + servers := make(map[string]mcp.MCPServerConfig) // Add server from each rule, filtering out empty configs - for _, rule := range r.Rules { + for i, rule := range r.Rules { server := rule.FrontMatter.MCPServer // Skip empty MCP server configs (no command and no URL means empty) if server.Command != "" || server.URL != "" { - servers = append(servers, server) + // Use the rule's ID as the key, or generate one if not present + key := rule.FrontMatter.ID + if key == "" { + key = fmt.Sprintf("rule-%d", i) + } + servers[key] = server } } diff --git a/pkg/codingcontext/result_test.go b/pkg/codingcontext/result_test.go index 88a0041..d5242e5 100644 --- a/pkg/codingcontext/result_test.go +++ b/pkg/codingcontext/result_test.go @@ -72,7 +72,7 @@ func TestResult_MCPServers(t *testing.T) { tests := []struct { name string result Result - want []mcp.MCPServerConfig + want map[string]mcp.MCPServerConfig }{ { name: "no MCP servers", @@ -82,19 +82,21 @@ func TestResult_MCPServers(t *testing.T) { FrontMatter: markdown.TaskFrontMatter{}, }, }, - want: []mcp.MCPServerConfig{}, + want: map[string]mcp.MCPServerConfig{}, }, { - name: "MCP servers from rules only", + name: "MCP servers from rules with IDs", result: Result{ Rules: []markdown.Markdown[markdown.RuleFrontMatter]{ { FrontMatter: markdown.RuleFrontMatter{ + ID: "jira-server", MCPServer: mcp.MCPServerConfig{Type: mcp.TransportTypeStdio, Command: "jira"}, }, }, { FrontMatter: markdown.RuleFrontMatter{ + ID: "api-server", MCPServer: mcp.MCPServerConfig{Type: mcp.TransportTypeHTTP, URL: "https://api.example.com"}, }, }, @@ -103,9 +105,33 @@ func TestResult_MCPServers(t *testing.T) { FrontMatter: markdown.TaskFrontMatter{}, }, }, - want: []mcp.MCPServerConfig{ - {Type: mcp.TransportTypeStdio, Command: "jira"}, - {Type: mcp.TransportTypeHTTP, URL: "https://api.example.com"}, + want: map[string]mcp.MCPServerConfig{ + "jira-server": {Type: mcp.TransportTypeStdio, Command: "jira"}, + "api-server": {Type: mcp.TransportTypeHTTP, URL: "https://api.example.com"}, + }, + }, + { + name: "MCP servers from rules without IDs", + result: Result{ + Rules: []markdown.Markdown[markdown.RuleFrontMatter]{ + { + FrontMatter: markdown.RuleFrontMatter{ + MCPServer: mcp.MCPServerConfig{Type: mcp.TransportTypeStdio, Command: "server1"}, + }, + }, + { + FrontMatter: markdown.RuleFrontMatter{ + MCPServer: mcp.MCPServerConfig{Type: mcp.TransportTypeStdio, Command: "server2"}, + }, + }, + }, + Task: markdown.Markdown[markdown.TaskFrontMatter]{ + FrontMatter: markdown.TaskFrontMatter{}, + }, + }, + want: map[string]mcp.MCPServerConfig{ + "rule-0": {Type: mcp.TransportTypeStdio, Command: "server1"}, + "rule-1": {Type: mcp.TransportTypeStdio, Command: "server2"}, }, }, { @@ -114,25 +140,29 @@ func TestResult_MCPServers(t *testing.T) { Rules: []markdown.Markdown[markdown.RuleFrontMatter]{ { FrontMatter: markdown.RuleFrontMatter{ + ID: "server1-id", MCPServer: mcp.MCPServerConfig{Type: mcp.TransportTypeStdio, Command: "server1"}, }, }, { FrontMatter: markdown.RuleFrontMatter{ + ID: "server2-id", MCPServer: mcp.MCPServerConfig{Type: mcp.TransportTypeStdio, Command: "server2"}, }, }, { - FrontMatter: markdown.RuleFrontMatter{}, + FrontMatter: markdown.RuleFrontMatter{ + ID: "empty-rule", + }, }, }, Task: markdown.Markdown[markdown.TaskFrontMatter]{ FrontMatter: markdown.TaskFrontMatter{}, }, }, - want: []mcp.MCPServerConfig{ - {Type: mcp.TransportTypeStdio, Command: "server1"}, - {Type: mcp.TransportTypeStdio, Command: "server2"}, + want: map[string]mcp.MCPServerConfig{ + "server1-id": {Type: mcp.TransportTypeStdio, Command: "server1"}, + "server2-id": {Type: mcp.TransportTypeStdio, Command: "server2"}, // Empty rule MCP server is filtered out }, }, @@ -141,17 +171,51 @@ func TestResult_MCPServers(t *testing.T) { result: Result{ Rules: []markdown.Markdown[markdown.RuleFrontMatter]{ { - FrontMatter: markdown.RuleFrontMatter{}, + FrontMatter: markdown.RuleFrontMatter{ + ID: "no-server-rule", + }, }, }, Task: markdown.Markdown[markdown.TaskFrontMatter]{ FrontMatter: markdown.TaskFrontMatter{}, }, }, - want: []mcp.MCPServerConfig{ + want: map[string]mcp.MCPServerConfig{ // Empty rule MCP server is filtered out }, }, + { + name: "mixed rules with and without IDs", + result: Result{ + Rules: []markdown.Markdown[markdown.RuleFrontMatter]{ + { + FrontMatter: markdown.RuleFrontMatter{ + ID: "explicit-id", + MCPServer: mcp.MCPServerConfig{Type: mcp.TransportTypeStdio, Command: "server1"}, + }, + }, + { + FrontMatter: markdown.RuleFrontMatter{ + MCPServer: mcp.MCPServerConfig{Type: mcp.TransportTypeStdio, Command: "server2"}, + }, + }, + { + FrontMatter: markdown.RuleFrontMatter{ + ID: "another-id", + MCPServer: mcp.MCPServerConfig{Type: mcp.TransportTypeHTTP, URL: "https://example.com"}, + }, + }, + }, + Task: markdown.Markdown[markdown.TaskFrontMatter]{ + FrontMatter: markdown.TaskFrontMatter{}, + }, + }, + want: map[string]mcp.MCPServerConfig{ + "explicit-id": {Type: mcp.TransportTypeStdio, Command: "server1"}, + "rule-1": {Type: mcp.TransportTypeStdio, Command: "server2"}, + "another-id": {Type: mcp.TransportTypeHTTP, URL: "https://example.com"}, + }, + }, } for _, tt := range tests { @@ -160,22 +224,37 @@ func TestResult_MCPServers(t *testing.T) { if len(got) != len(tt.want) { t.Errorf("MCPServers() returned %d servers, want %d", len(got), len(tt.want)) + t.Logf("Got keys: %v", mapKeys(got)) + t.Logf("Want keys: %v", mapKeys(tt.want)) return } - for i, wantServer := range tt.want { - gotServer := got[i] + for key, wantServer := range tt.want { + gotServer, ok := got[key] + if !ok { + t.Errorf("MCPServers() missing key %q", key) + continue + } if gotServer.Type != wantServer.Type { - t.Errorf("MCPServers()[%d].Type = %v, want %v", i, gotServer.Type, wantServer.Type) + t.Errorf("MCPServers()[%q].Type = %v, want %v", key, gotServer.Type, wantServer.Type) } if gotServer.Command != wantServer.Command { - t.Errorf("MCPServers()[%d].Command = %q, want %q", i, gotServer.Command, wantServer.Command) + t.Errorf("MCPServers()[%q].Command = %q, want %q", key, gotServer.Command, wantServer.Command) } if gotServer.URL != wantServer.URL { - t.Errorf("MCPServers()[%d].URL = %q, want %q", i, gotServer.URL, wantServer.URL) + t.Errorf("MCPServers()[%q].URL = %q, want %q", key, gotServer.URL, wantServer.URL) } } }) } } + +// Helper function to get map keys for debugging +func mapKeys(m map[string]mcp.MCPServerConfig) []string { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + return keys +} From 4e76fdfe0704461c848ee1d6cb5547e59877c3db Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 19:13:45 +0000 Subject: [PATCH 3/5] Automatically set rule ID to filename if not in frontmatter - Modified rule loading to auto-set ID from filename (without extension) - Simplified MCPServers() since ID is now always set - Updated tests to reflect auto-generated IDs - Added integration test to verify ID auto-generation behavior Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- pkg/codingcontext/context.go | 7 ++++++ pkg/codingcontext/context_test.go | 40 +++++++++++++++++++++++++++++++ pkg/codingcontext/result.go | 14 ++++------- pkg/codingcontext/result_test.go | 17 +++++++------ 4 files changed, 61 insertions(+), 17 deletions(-) diff --git a/pkg/codingcontext/context.go b/pkg/codingcontext/context.go index 35c9704..283ad48 100644 --- a/pkg/codingcontext/context.go +++ b/pkg/codingcontext/context.go @@ -516,6 +516,13 @@ func (cc *Context) findExecuteRuleFiles(ctx context.Context, homeDir string) err return fmt.Errorf("failed to parse markdown file %s: %w", path, err) } + // Automatically set ID to filename (without extension) if not set in frontmatter + if frontmatter.ID == "" { + baseName := filepath.Base(path) + ext := filepath.Ext(baseName) + frontmatter.ID = strings.TrimSuffix(baseName, ext) + } + // Expand parameters only if expand is not explicitly set to false var processedContent string if shouldExpandParams(frontmatter.ExpandParams) { diff --git a/pkg/codingcontext/context_test.go b/pkg/codingcontext/context_test.go index e11025d..417f4f9 100644 --- a/pkg/codingcontext/context_test.go +++ b/pkg/codingcontext/context_test.go @@ -699,6 +699,46 @@ func TestContext_Run_Rules(t *testing.T) { } }, }, + { + name: "rule IDs automatically set from filename", + setup: func(t *testing.T, dir string) { + createTask(t, dir, "id-task", "", "Task") + createRule(t, dir, ".agents/rules/my-rule.md", "", "Rule without ID in frontmatter") + createRule(t, dir, ".agents/rules/another-rule.md", "id: explicit-id", "Rule with explicit ID") + }, + taskName: "id-task", + wantErr: false, + check: func(t *testing.T, result *Result) { + if len(result.Rules) != 2 { + t.Fatalf("expected 2 rules, got %d", len(result.Rules)) + } + + // Check that one rule has auto-generated ID from filename + foundMyRule := false + foundAnotherRule := false + for _, rule := range result.Rules { + if rule.FrontMatter.ID == "my-rule" { + foundMyRule = true + if !strings.Contains(rule.Content, "Rule without ID") { + t.Error("my-rule should contain 'Rule without ID'") + } + } + if rule.FrontMatter.ID == "explicit-id" { + foundAnotherRule = true + if !strings.Contains(rule.Content, "Rule with explicit ID") { + t.Error("explicit-id should contain 'Rule with explicit ID'") + } + } + } + + if !foundMyRule { + t.Error("expected to find rule with auto-generated ID 'my-rule'") + } + if !foundAnotherRule { + t.Error("expected to find rule with explicit ID 'explicit-id'") + } + }, + }, } for _, tt := range tests { diff --git a/pkg/codingcontext/result.go b/pkg/codingcontext/result.go index 5f1ef15..794f11e 100644 --- a/pkg/codingcontext/result.go +++ b/pkg/codingcontext/result.go @@ -1,8 +1,6 @@ package codingcontext import ( - "fmt" - "github.com/kitproj/coding-context-cli/pkg/codingcontext/markdown" "github.com/kitproj/coding-context-cli/pkg/codingcontext/mcp" "github.com/kitproj/coding-context-cli/pkg/codingcontext/skills" @@ -22,21 +20,17 @@ type Result struct { // Each rule can specify one MCP server configuration. // Returns a map from rule ID to MCP server configuration. // Empty/zero-value MCP server configurations are filtered out. -// Rules without an ID field are included with a generated key "rule-". +// The rule ID is automatically set to the filename (without extension) if not +// explicitly provided in the frontmatter. func (r *Result) MCPServers() map[string]mcp.MCPServerConfig { servers := make(map[string]mcp.MCPServerConfig) // Add server from each rule, filtering out empty configs - for i, rule := range r.Rules { + for _, rule := range r.Rules { server := rule.FrontMatter.MCPServer // Skip empty MCP server configs (no command and no URL means empty) if server.Command != "" || server.URL != "" { - // Use the rule's ID as the key, or generate one if not present - key := rule.FrontMatter.ID - if key == "" { - key = fmt.Sprintf("rule-%d", i) - } - servers[key] = server + servers[rule.FrontMatter.ID] = server } } diff --git a/pkg/codingcontext/result_test.go b/pkg/codingcontext/result_test.go index d5242e5..54689b5 100644 --- a/pkg/codingcontext/result_test.go +++ b/pkg/codingcontext/result_test.go @@ -111,16 +111,18 @@ func TestResult_MCPServers(t *testing.T) { }, }, { - name: "MCP servers from rules without IDs", + name: "MCP servers from rules without explicit IDs in frontmatter", result: Result{ Rules: []markdown.Markdown[markdown.RuleFrontMatter]{ { FrontMatter: markdown.RuleFrontMatter{ + ID: "rule-file-1", // ID is auto-set to filename during loading MCPServer: mcp.MCPServerConfig{Type: mcp.TransportTypeStdio, Command: "server1"}, }, }, { FrontMatter: markdown.RuleFrontMatter{ + ID: "rule-file-2", // ID is auto-set to filename during loading MCPServer: mcp.MCPServerConfig{Type: mcp.TransportTypeStdio, Command: "server2"}, }, }, @@ -130,8 +132,8 @@ func TestResult_MCPServers(t *testing.T) { }, }, want: map[string]mcp.MCPServerConfig{ - "rule-0": {Type: mcp.TransportTypeStdio, Command: "server1"}, - "rule-1": {Type: mcp.TransportTypeStdio, Command: "server2"}, + "rule-file-1": {Type: mcp.TransportTypeStdio, Command: "server1"}, + "rule-file-2": {Type: mcp.TransportTypeStdio, Command: "server2"}, }, }, { @@ -185,23 +187,24 @@ func TestResult_MCPServers(t *testing.T) { }, }, { - name: "mixed rules with and without IDs", + name: "mixed rules with explicit and auto-generated IDs", result: Result{ Rules: []markdown.Markdown[markdown.RuleFrontMatter]{ { FrontMatter: markdown.RuleFrontMatter{ - ID: "explicit-id", + ID: "explicit-id", // Explicit ID in frontmatter MCPServer: mcp.MCPServerConfig{Type: mcp.TransportTypeStdio, Command: "server1"}, }, }, { FrontMatter: markdown.RuleFrontMatter{ + ID: "some-rule", // ID auto-set to filename during loading MCPServer: mcp.MCPServerConfig{Type: mcp.TransportTypeStdio, Command: "server2"}, }, }, { FrontMatter: markdown.RuleFrontMatter{ - ID: "another-id", + ID: "another-id", // Explicit ID in frontmatter MCPServer: mcp.MCPServerConfig{Type: mcp.TransportTypeHTTP, URL: "https://example.com"}, }, }, @@ -212,7 +215,7 @@ func TestResult_MCPServers(t *testing.T) { }, want: map[string]mcp.MCPServerConfig{ "explicit-id": {Type: mcp.TransportTypeStdio, Command: "server1"}, - "rule-1": {Type: mcp.TransportTypeStdio, Command: "server2"}, + "some-rule": {Type: mcp.TransportTypeStdio, Command: "server2"}, "another-id": {Type: mcp.TransportTypeHTTP, URL: "https://example.com"}, }, }, From 0572a0c372f559435cd6956512e325e07b480d4a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 19:33:46 +0000 Subject: [PATCH 4/5] Automatically set ID for tasks and commands from filename - Added ID auto-setting for TaskFrontMatter and CommandFrontMatter - Ensures consistent behavior: all types with ID field get auto-set from filename - Added tests to verify task ID auto-setting (both explicit and implicit) Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- pkg/codingcontext/context.go | 14 ++++++++++++++ pkg/codingcontext/context_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/pkg/codingcontext/context.go b/pkg/codingcontext/context.go index 283ad48..5143224 100644 --- a/pkg/codingcontext/context.go +++ b/pkg/codingcontext/context.go @@ -129,6 +129,13 @@ func (cc *Context) findTask(taskName string) error { return fmt.Errorf("failed to parse task file %s: %w", path, err) } + // Automatically set ID to filename (without extension) if not set in frontmatter + if frontMatter.ID == "" { + baseName := filepath.Base(path) + ext := filepath.Ext(baseName) + frontMatter.ID = strings.TrimSuffix(baseName, ext) + } + // Extract selector labels from task frontmatter and add them to cc.includes. // This combines CLI selectors (from -s flag) with task selectors using OR logic: // rules match if their frontmatter value matches ANY selector value for a given key. @@ -229,6 +236,13 @@ func (cc *Context) findCommand(commandName string, params taskparser.Params) (st return fmt.Errorf("failed to parse command file %s: %w", path, err) } + // Automatically set ID to filename (without extension) if not set in frontmatter + if frontMatter.ID == "" { + baseName := filepath.Base(path) + ext := filepath.Ext(baseName) + frontMatter.ID = strings.TrimSuffix(baseName, ext) + } + // Extract selector labels from command frontmatter and add them to cc.includes. // This combines CLI selectors, task selectors, and command selectors using OR logic: // rules match if their frontmatter value matches ANY selector value for a given key. diff --git a/pkg/codingcontext/context_test.go b/pkg/codingcontext/context_test.go index 417f4f9..f33ed7d 100644 --- a/pkg/codingcontext/context_test.go +++ b/pkg/codingcontext/context_test.go @@ -355,6 +355,32 @@ func TestContext_Run_Basic(t *testing.T) { } }, }, + { + name: "task ID automatically set from filename", + setup: func(t *testing.T, dir string) { + createTask(t, dir, "my-task", "", "Task content") + }, + taskName: "my-task", + wantErr: false, + check: func(t *testing.T, result *Result) { + if result.Task.FrontMatter.ID != "my-task" { + t.Errorf("expected task ID 'my-task', got %q", result.Task.FrontMatter.ID) + } + }, + }, + { + name: "task with explicit ID in frontmatter", + setup: func(t *testing.T, dir string) { + createTask(t, dir, "file-name", "id: explicit-task-id", "Task content") + }, + taskName: "file-name", + wantErr: false, + check: func(t *testing.T, result *Result) { + if result.Task.FrontMatter.ID != "explicit-task-id" { + t.Errorf("expected task ID 'explicit-task-id', got %q", result.Task.FrontMatter.ID) + } + }, + }, } for _, tt := range tests { From 3449617b432c39b201e99810e75ea7ac6995a7ba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 19:36:34 +0000 Subject: [PATCH 5/5] Refactor: Extract ID generation into helper function - Created generateIDFromPath() helper to reduce code duplication - Updated all three locations (tasks, commands, rules) to use helper - Improves maintainability and consistency Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- pkg/codingcontext/context.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/pkg/codingcontext/context.go b/pkg/codingcontext/context.go index 5143224..09509f9 100644 --- a/pkg/codingcontext/context.go +++ b/pkg/codingcontext/context.go @@ -56,6 +56,14 @@ func New(opts ...Option) *Context { return c } +// generateIDFromPath generates an ID from a file path by extracting the filename without extension. +// Used to auto-set ID fields in frontmatter when not explicitly provided. +func generateIDFromPath(path string) string { + baseName := filepath.Base(path) + ext := filepath.Ext(baseName) + return strings.TrimSuffix(baseName, ext) +} + type markdownVisitor func(path string, fm *markdown.BaseFrontMatter) error // findMarkdownFile searches for a markdown file by name in the given directories. @@ -131,9 +139,7 @@ func (cc *Context) findTask(taskName string) error { // Automatically set ID to filename (without extension) if not set in frontmatter if frontMatter.ID == "" { - baseName := filepath.Base(path) - ext := filepath.Ext(baseName) - frontMatter.ID = strings.TrimSuffix(baseName, ext) + frontMatter.ID = generateIDFromPath(path) } // Extract selector labels from task frontmatter and add them to cc.includes. @@ -238,9 +244,7 @@ func (cc *Context) findCommand(commandName string, params taskparser.Params) (st // Automatically set ID to filename (without extension) if not set in frontmatter if frontMatter.ID == "" { - baseName := filepath.Base(path) - ext := filepath.Ext(baseName) - frontMatter.ID = strings.TrimSuffix(baseName, ext) + frontMatter.ID = generateIDFromPath(path) } // Extract selector labels from command frontmatter and add them to cc.includes. @@ -532,9 +536,7 @@ func (cc *Context) findExecuteRuleFiles(ctx context.Context, homeDir string) err // Automatically set ID to filename (without extension) if not set in frontmatter if frontmatter.ID == "" { - baseName := filepath.Base(path) - ext := filepath.Ext(baseName) - frontmatter.ID = strings.TrimSuffix(baseName, ext) + frontmatter.ID = generateIDFromPath(path) } // Expand parameters only if expand is not explicitly set to false