Skip to content

Commit ae0aa96

Browse files
authored
Add .cursor/skills to skill search paths (#185)
1 parent cf03e46 commit ae0aa96

3 files changed

Lines changed: 106 additions & 0 deletions

File tree

docs/reference/search-paths.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Command files are referenced via slash commands inside task content. Within each
3636
Skill files provide specialized capabilities with progressive disclosure. Within each directory, skill files are searched in:
3737

3838
1. `.agents/skills/*/SKILL.md` (each subdirectory in `.agents/skills/` can contain a `SKILL.md` file)
39+
2. `.cursor/skills/*/SKILL.md` (each subdirectory in `.cursor/skills/` can contain a `SKILL.md` file)
3940

4041
**Example:**
4142
```
@@ -46,6 +47,12 @@ Skill files provide specialized capabilities with progressive disclosure. Within
4647
│ └── SKILL.md
4748
└── api-testing/
4849
└── SKILL.md
50+
51+
.cursor/skills/
52+
├── code-review/
53+
│ └── SKILL.md
54+
└── refactoring/
55+
└── SKILL.md
4956
```
5057

5158
### Discovery Rules

pkg/codingcontext/context_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2279,6 +2279,104 @@ This is a test skill.
22792279
}
22802280
},
22812281
},
2282+
{
2283+
name: "discover skills from .cursor/skills directory",
2284+
setup: func(t *testing.T, dir string) {
2285+
// Create task
2286+
createTask(t, dir, "test-task", "", "Test task content")
2287+
2288+
// Create skill in .cursor/skills directory
2289+
skillDir := filepath.Join(dir, ".cursor", "skills", "cursor-skill")
2290+
if err := os.MkdirAll(skillDir, 0o755); err != nil {
2291+
t.Fatalf("failed to create skill directory: %v", err)
2292+
}
2293+
2294+
skillContent := `---
2295+
name: cursor-skill
2296+
description: A skill for Cursor IDE
2297+
---
2298+
2299+
# Cursor Skill
2300+
2301+
This is a skill for Cursor.
2302+
`
2303+
skillPath := filepath.Join(skillDir, "SKILL.md")
2304+
if err := os.WriteFile(skillPath, []byte(skillContent), 0o644); err != nil {
2305+
t.Fatalf("failed to create skill file: %v", err)
2306+
}
2307+
},
2308+
taskName: "test-task",
2309+
wantErr: false,
2310+
checkFunc: func(t *testing.T, result *Result) {
2311+
if len(result.Skills.Skills) != 1 {
2312+
t.Fatalf("expected 1 skill, got %d", len(result.Skills.Skills))
2313+
}
2314+
skill := result.Skills.Skills[0]
2315+
if skill.Name != "cursor-skill" {
2316+
t.Errorf("expected skill name 'cursor-skill', got %q", skill.Name)
2317+
}
2318+
if skill.Description != "A skill for Cursor IDE" {
2319+
t.Errorf("expected skill description 'A skill for Cursor IDE', got %q", skill.Description)
2320+
}
2321+
if skill.Location == "" {
2322+
t.Error("expected skill Location to be set")
2323+
}
2324+
},
2325+
},
2326+
{
2327+
name: "discover skills from both .agents/skills and .cursor/skills",
2328+
setup: func(t *testing.T, dir string) {
2329+
// Create task
2330+
createTask(t, dir, "test-task", "", "Test task content")
2331+
2332+
// Create skill in .agents/skills directory
2333+
skillDir1 := filepath.Join(dir, ".agents", "skills", "agents-skill")
2334+
if err := os.MkdirAll(skillDir1, 0o755); err != nil {
2335+
t.Fatalf("failed to create skill directory: %v", err)
2336+
}
2337+
skillContent1 := `---
2338+
name: agents-skill
2339+
description: A generic agents skill
2340+
---
2341+
2342+
# Agents Skill
2343+
`
2344+
skillPath1 := filepath.Join(skillDir1, "SKILL.md")
2345+
if err := os.WriteFile(skillPath1, []byte(skillContent1), 0o644); err != nil {
2346+
t.Fatalf("failed to create skill file: %v", err)
2347+
}
2348+
2349+
// Create skill in .cursor/skills directory
2350+
skillDir2 := filepath.Join(dir, ".cursor", "skills", "cursor-skill")
2351+
if err := os.MkdirAll(skillDir2, 0o755); err != nil {
2352+
t.Fatalf("failed to create skill directory: %v", err)
2353+
}
2354+
skillContent2 := `---
2355+
name: cursor-skill
2356+
description: A Cursor IDE skill
2357+
---
2358+
2359+
# Cursor Skill
2360+
`
2361+
skillPath2 := filepath.Join(skillDir2, "SKILL.md")
2362+
if err := os.WriteFile(skillPath2, []byte(skillContent2), 0o644); err != nil {
2363+
t.Fatalf("failed to create skill file: %v", err)
2364+
}
2365+
},
2366+
taskName: "test-task",
2367+
wantErr: false,
2368+
checkFunc: func(t *testing.T, result *Result) {
2369+
if len(result.Skills.Skills) != 2 {
2370+
t.Fatalf("expected 2 skills, got %d", len(result.Skills.Skills))
2371+
}
2372+
names := []string{result.Skills.Skills[0].Name, result.Skills.Skills[1].Name}
2373+
// Verify both skills are present (order doesn't matter)
2374+
if (names[0] != "agents-skill" && names[0] != "cursor-skill") ||
2375+
(names[1] != "agents-skill" && names[1] != "cursor-skill") {
2376+
t.Errorf("expected skills 'agents-skill' and 'cursor-skill', got %v", names)
2377+
}
2378+
},
2379+
},
22822380
}
22832381

22842382
for _, tt := range tests {

pkg/codingcontext/paths.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,6 @@ func commandSearchPaths(dir string) []string {
5353
func skillSearchPaths(dir string) []string {
5454
return []string{
5555
filepath.Join(dir, ".agents", "skills"),
56+
filepath.Join(dir, ".cursor", "skills"),
5657
}
5758
}

0 commit comments

Comments
 (0)