Load nested SKILL.md files from .claude/skills and namespace them with colons so category-based skill layouts work in Claude Code clients. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
65 lines
2.1 KiB
TypeScript
65 lines
2.1 KiB
TypeScript
import assert from 'node:assert/strict'
|
|
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import test from 'node:test'
|
|
|
|
import { getSkillDirCommands, clearSkillCaches } from './loadSkillsDir.ts'
|
|
|
|
function writeSkill(rootDir: string, skillPath: string): void {
|
|
const skillDir = join(rootDir, '.claude', 'skills', ...skillPath.split('/'))
|
|
mkdirSync(skillDir, { recursive: true })
|
|
writeFileSync(
|
|
join(skillDir, 'SKILL.md'),
|
|
`---\ndescription: ${skillPath}\n---\n# ${skillPath}\n`,
|
|
'utf8',
|
|
)
|
|
}
|
|
|
|
test('loads flat and nested skills with colon namespaces', async () => {
|
|
const configDir = mkdtempSync(join(tmpdir(), 'openclaude-skills-'))
|
|
const cwd = join(configDir, 'workspace')
|
|
const originalConfigDir = process.env.CLAUDE_CONFIG_DIR
|
|
|
|
try {
|
|
mkdirSync(cwd, { recursive: true })
|
|
writeSkill(configDir, 'flat-skill')
|
|
writeSkill(configDir, 'git/commit')
|
|
writeSkill(configDir, 'frontend/react/form')
|
|
|
|
process.env.CLAUDE_CONFIG_DIR = configDir
|
|
clearSkillCaches()
|
|
|
|
const skills = await getSkillDirCommands(cwd)
|
|
const promptSkills = skills.filter(skill => skill.type === 'prompt')
|
|
const skillNames = promptSkills.map(skill => skill.name).sort()
|
|
|
|
assert.deepEqual(skillNames, [
|
|
'flat-skill',
|
|
'frontend:react:form',
|
|
'git:commit',
|
|
])
|
|
|
|
const nestedSkill = promptSkills.find(skill => skill.name === 'git:commit')
|
|
assert.ok(nestedSkill)
|
|
assert.equal(nestedSkill.skillRoot, join(configDir, '.claude', 'skills', 'git', 'commit'))
|
|
|
|
const deepSkill = promptSkills.find(
|
|
skill => skill.name === 'frontend:react:form',
|
|
)
|
|
assert.ok(deepSkill)
|
|
assert.equal(
|
|
deepSkill.skillRoot,
|
|
join(configDir, '.claude', 'skills', 'frontend', 'react', 'form'),
|
|
)
|
|
} finally {
|
|
if (originalConfigDir === undefined) {
|
|
delete process.env.CLAUDE_CONFIG_DIR
|
|
} else {
|
|
process.env.CLAUDE_CONFIG_DIR = originalConfigDir
|
|
}
|
|
clearSkillCaches()
|
|
rmSync(configDir, { recursive: true, force: true })
|
|
}
|
|
})
|