fix: name claude skills after their folder, not their frontmatter name
d95259a parent: ebef0e9 modified
packages/core/src/scanners/claude/content.ts +2 -1 | @@ -62,7 +62,8 @@ export async function addSkillDir(acc: Acc, dir: string, opts: SkillOptions = {} | ||
| 62 | 62 | if (text === undefined) return false |
| 63 | 63 | const { frontmatter: fm } = parseFrontmatter(text) |
| 64 | 64 | const folder = path.basename(dir) |
| 65 | - const name = str(fm.name) ?? folder | |
| 65 | + // Claude Code names a skill after its folder; the frontmatter `name` does not rename it | |
| 66 | + const name = folder | |
| 66 | 67 | const shown = opts.displayName ?? name |
| 67 | 68 | const details: SkillDetails = { |
| 68 | 69 | type: 'skill', |
| @@ -62,7 +62,8 @@ export async function addSkillDir(acc: Acc, dir: string, opts: SkillOptions = {} | |||
| 62 | if (text === undefined) return false | 62 | if (text === undefined) return false |
| 63 | const { frontmatter: fm } = parseFrontmatter(text) | 63 | const { frontmatter: fm } = parseFrontmatter(text) |
| 64 | const folder = path.basename(dir) | 64 | const folder = path.basename(dir) |
| 65 | - const name = str(fm.name) ?? folder | 65 | + // Claude Code names a skill after its folder; the frontmatter `name` does not rename it |
| 66 | + const name = folder | ||
| 66 | const shown = opts.displayName ?? name | 67 | const shown = opts.displayName ?? name |
| 67 | const details: SkillDetails = { | 68 | const details: SkillDetails = { |
| 68 | type: 'skill', | 69 | type: 'skill', |
modified
packages/core/test/claude.test.ts +6 -0 | @@ -47,6 +47,7 @@ beforeAll(async () => { | ||
| 47 | 47 | await put(path.join(c, 'commands', 'team', 'deploy.md'), '---\ndescription: Deploy\n---\nrun') |
| 48 | 48 | // A broken skill (bad frontmatter) must not throw |
| 49 | 49 | await put(path.join(c, 'skills', 'broken', 'SKILL.md'), '---\nname: [unclosed\n---\n') |
| 50 | + await put(path.join(c, 'skills', 'marketing-launch', 'SKILL.md'), '---\nname: launch\ndescription: Renamed\n---\n') | |
| 50 | 51 | |
| 51 | 52 | // Real skill linked through a junction |
| 52 | 53 | const outside = path.join(tmp, 'outside', 'linked-skill') |
| @@ -195,6 +196,11 @@ describe('claude scanner: home', () => { | ||
| 195 | 196 | expect(r.items.find((i) => i.name === 'broken')).toBeDefined() |
| 196 | 197 | }) |
| 197 | 198 | |
| 199 | + it('names skills after their folder, not their frontmatter name', () => { | |
| 200 | + expect(find(r, 'skill', 'marketing-launch').description).toBe('Renamed') | |
| 201 | + expect(r.items.find((i) => i.type === 'skill' && i.name === 'launch')).toBeUndefined() | |
| 202 | + }) | |
| 203 | + | |
| 198 | 204 | it.runIf(process.platform === 'win32' || true)('reports junctions as symlinks', () => { |
| 199 | 205 | if (!linked) return |
| 200 | 206 | const s = find(r, 'skill', 'linked-skill') |
| @@ -47,6 +47,7 @@ beforeAll(async () => { | |||
| 47 | await put(path.join(c, 'commands', 'team', 'deploy.md'), '---\ndescription: Deploy\n---\nrun') | 47 | await put(path.join(c, 'commands', 'team', 'deploy.md'), '---\ndescription: Deploy\n---\nrun') |
| 48 | // A broken skill (bad frontmatter) must not throw | 48 | // A broken skill (bad frontmatter) must not throw |
| 49 | await put(path.join(c, 'skills', 'broken', 'SKILL.md'), '---\nname: [unclosed\n---\n') | 49 | await put(path.join(c, 'skills', 'broken', 'SKILL.md'), '---\nname: [unclosed\n---\n') |
| 50 | + await put(path.join(c, 'skills', 'marketing-launch', 'SKILL.md'), '---\nname: launch\ndescription: Renamed\n---\n') | ||
| 50 | 51 | ||
| 51 | // Real skill linked through a junction | 52 | // Real skill linked through a junction |
| 52 | const outside = path.join(tmp, 'outside', 'linked-skill') | 53 | const outside = path.join(tmp, 'outside', 'linked-skill') |
| @@ -195,6 +196,11 @@ describe('claude scanner: home', () => { | |||
| 195 | expect(r.items.find((i) => i.name === 'broken')).toBeDefined() | 196 | expect(r.items.find((i) => i.name === 'broken')).toBeDefined() |
| 196 | }) | 197 | }) |
| 197 | 198 | ||
| 199 | + it('names skills after their folder, not their frontmatter name', () => { | ||
| 200 | + expect(find(r, 'skill', 'marketing-launch').description).toBe('Renamed') | ||
| 201 | + expect(r.items.find((i) => i.type === 'skill' && i.name === 'launch')).toBeUndefined() | ||
| 202 | + }) | ||
| 203 | + | ||
| 198 | it.runIf(process.platform === 'win32' || true)('reports junctions as symlinks', () => { | 204 | it.runIf(process.platform === 'win32' || true)('reports junctions as symlinks', () => { |
| 199 | if (!linked) return | 205 | if (!linked) return |
| 200 | const s = find(r, 'skill', 'linked-skill') | 206 | const s = find(r, 'skill', 'linked-skill') |