From bb1cfd510c807e949d67177ef31377c2f5a59fae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Dec 2025 18:05:44 +0000 Subject: [PATCH 1/2] Initial plan From c8c97af45e82bcf8701c36754d998cac59e9b489 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Dec 2025 18:11:11 +0000 Subject: [PATCH 2/2] Add .cursor/skills to skill search paths Co-authored-by: alexec <1142830+alexec@users.noreply.github.com> --- docs/reference/search-paths.md | 7 +++ pkg/codingcontext/context_test.go | 98 +++++++++++++++++++++++++++++++ pkg/codingcontext/paths.go | 1 + 3 files changed, 106 insertions(+) diff --git a/docs/reference/search-paths.md b/docs/reference/search-paths.md index 79f71d5..4a351ea 100644 --- a/docs/reference/search-paths.md +++ b/docs/reference/search-paths.md @@ -36,6 +36,7 @@ Command files are referenced via slash commands inside task content. Within each Skill files provide specialized capabilities with progressive disclosure. Within each directory, skill files are searched in: 1. `.agents/skills/*/SKILL.md` (each subdirectory in `.agents/skills/` can contain a `SKILL.md` file) +2. `.cursor/skills/*/SKILL.md` (each subdirectory in `.cursor/skills/` can contain a `SKILL.md` file) **Example:** ``` @@ -46,6 +47,12 @@ Skill files provide specialized capabilities with progressive disclosure. Within │ └── SKILL.md └── api-testing/ └── SKILL.md + +.cursor/skills/ +├── code-review/ +│ └── SKILL.md +└── refactoring/ + └── SKILL.md ``` ### Discovery Rules diff --git a/pkg/codingcontext/context_test.go b/pkg/codingcontext/context_test.go index a0c5063..f4aea0e 100644 --- a/pkg/codingcontext/context_test.go +++ b/pkg/codingcontext/context_test.go @@ -2279,6 +2279,104 @@ This is a test skill. } }, }, + { + name: "discover skills from .cursor/skills directory", + setup: func(t *testing.T, dir string) { + // Create task + createTask(t, dir, "test-task", "", "Test task content") + + // Create skill in .cursor/skills directory + skillDir := filepath.Join(dir, ".cursor", "skills", "cursor-skill") + if err := os.MkdirAll(skillDir, 0o755); err != nil { + t.Fatalf("failed to create skill directory: %v", err) + } + + skillContent := `--- +name: cursor-skill +description: A skill for Cursor IDE +--- + +# Cursor Skill + +This is a skill for Cursor. +` + skillPath := filepath.Join(skillDir, "SKILL.md") + if err := os.WriteFile(skillPath, []byte(skillContent), 0o644); err != nil { + t.Fatalf("failed to create skill file: %v", err) + } + }, + taskName: "test-task", + wantErr: false, + checkFunc: func(t *testing.T, result *Result) { + if len(result.Skills.Skills) != 1 { + t.Fatalf("expected 1 skill, got %d", len(result.Skills.Skills)) + } + skill := result.Skills.Skills[0] + if skill.Name != "cursor-skill" { + t.Errorf("expected skill name 'cursor-skill', got %q", skill.Name) + } + if skill.Description != "A skill for Cursor IDE" { + t.Errorf("expected skill description 'A skill for Cursor IDE', got %q", skill.Description) + } + if skill.Location == "" { + t.Error("expected skill Location to be set") + } + }, + }, + { + name: "discover skills from both .agents/skills and .cursor/skills", + setup: func(t *testing.T, dir string) { + // Create task + createTask(t, dir, "test-task", "", "Test task content") + + // Create skill in .agents/skills directory + skillDir1 := filepath.Join(dir, ".agents", "skills", "agents-skill") + if err := os.MkdirAll(skillDir1, 0o755); err != nil { + t.Fatalf("failed to create skill directory: %v", err) + } + skillContent1 := `--- +name: agents-skill +description: A generic agents skill +--- + +# Agents Skill +` + skillPath1 := filepath.Join(skillDir1, "SKILL.md") + if err := os.WriteFile(skillPath1, []byte(skillContent1), 0o644); err != nil { + t.Fatalf("failed to create skill file: %v", err) + } + + // Create skill in .cursor/skills directory + skillDir2 := filepath.Join(dir, ".cursor", "skills", "cursor-skill") + if err := os.MkdirAll(skillDir2, 0o755); err != nil { + t.Fatalf("failed to create skill directory: %v", err) + } + skillContent2 := `--- +name: cursor-skill +description: A Cursor IDE skill +--- + +# Cursor Skill +` + skillPath2 := filepath.Join(skillDir2, "SKILL.md") + if err := os.WriteFile(skillPath2, []byte(skillContent2), 0o644); err != nil { + t.Fatalf("failed to create skill file: %v", err) + } + }, + taskName: "test-task", + wantErr: false, + checkFunc: func(t *testing.T, result *Result) { + if len(result.Skills.Skills) != 2 { + t.Fatalf("expected 2 skills, got %d", len(result.Skills.Skills)) + } + names := []string{result.Skills.Skills[0].Name, result.Skills.Skills[1].Name} + // Verify both skills are present (order doesn't matter) + if (names[0] != "agents-skill" && names[0] != "cursor-skill") || + (names[1] != "agents-skill" && names[1] != "cursor-skill") { + t.Errorf("expected skills 'agents-skill' and 'cursor-skill', got %v", names) + } + }, + }, } for _, tt := range tests { diff --git a/pkg/codingcontext/paths.go b/pkg/codingcontext/paths.go index 5488ab5..c9386f2 100644 --- a/pkg/codingcontext/paths.go +++ b/pkg/codingcontext/paths.go @@ -53,5 +53,6 @@ func commandSearchPaths(dir string) []string { func skillSearchPaths(dir string) []string { return []string{ filepath.Join(dir, ".agents", "skills"), + filepath.Join(dir, ".cursor", "skills"), } }