PR #883 nested Claude skills 3 levels deep under gsd-ns-*/skills/<stem>/SKILL.md. Claude Code's Skill tool scans only one level under ~/.claude/skills/ — nested concrete skills were never listed and Skill(skill="gsd-plan-phase") calls failed. Revert to flat layout: all ~61 concrete skills at ~/.claude/skills/gsd-<name>/SKILL.md. The 6 other runtimes confirmed as non-recursive scanners (cline, qwen, hermes, augment, trae, antigravity) retain their nested layout — only Claude changes. Tradeoff: ~61 top-level skill dirs return to the flat install, but they are discoverable and invokable. Nested concretes were invisible to the Skill tool entirely. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -417,7 +417,7 @@ describe('stage — skills kind (claude global)', () => {
|
||||
assert.ok(entries.length >= 1, 'at least one skill dir should be staged');
|
||||
});
|
||||
|
||||
test('stage with skills="*" nests all commands/gsd/*.md under 6 routers (claude)', () => {
|
||||
test('stage with skills="*" produces flat layout for claude (#924: reverted from nested)', () => {
|
||||
const layout = resolveRuntimeArtifactLayout('claude', FAKE_STAGE_DIR, 'global');
|
||||
const skillsKind = layout.kinds.find(k => k.kind === 'skills');
|
||||
assert.ok(skillsKind, 'should have a skills kind');
|
||||
@@ -425,32 +425,22 @@ describe('stage — skills kind (claude global)', () => {
|
||||
const stagedDir = skillsKind.stage(PROFILE_FULL);
|
||||
assert.ok(fs.existsSync(stagedDir), 'stagedDir must exist');
|
||||
|
||||
// Claude is a NESTING runtime: full profile produces exactly 6 gsd-ns-* router dirs.
|
||||
// #924: Claude is reverted to FLAT. Full profile produces >= 60 top-level gsd-* dirs.
|
||||
// (Previously nested: exactly 6 gsd-ns-* router dirs. That broke Skill-tool discovery.)
|
||||
const topEntries = fs.readdirSync(stagedDir);
|
||||
assert.strictEqual(topEntries.length, 6, `full profile should have exactly 6 router dirs, got ${topEntries.length}`);
|
||||
assert.ok(
|
||||
topEntries.length >= 60,
|
||||
`full profile should have >= 60 top-level skill dirs (flat layout, #924), got ${topEntries.length}`,
|
||||
);
|
||||
for (const entry of topEntries) {
|
||||
assert.ok(entry.startsWith('gsd-ns-'), `top-level entry should be a gsd-ns-* router: ${entry}`);
|
||||
// Each router has its own SKILL.md.
|
||||
const routerSkillMd = path.join(stagedDir, entry, 'SKILL.md');
|
||||
assert.ok(fs.existsSync(routerSkillMd), `router SKILL.md must exist in ${entry}`);
|
||||
// Each router has a skills/ subdirectory with nested children.
|
||||
assert.ok(entry.startsWith('gsd-'), `entry should start with gsd-: ${entry}`);
|
||||
// Each skill dir has its own SKILL.md at the top level.
|
||||
const skillMd = path.join(stagedDir, entry, 'SKILL.md');
|
||||
assert.ok(fs.existsSync(skillMd), `SKILL.md must exist at top level in ${entry}`);
|
||||
// No nested skills/ subdirectory: flat layout means no nesting.
|
||||
const skillsSubdir = path.join(stagedDir, entry, 'skills');
|
||||
assert.ok(fs.existsSync(skillsSubdir), `skills/ subdir must exist in ${entry}`);
|
||||
assert.ok(fs.statSync(skillsSubdir).isDirectory(), `${entry}/skills must be a directory`);
|
||||
assert.ok(!fs.existsSync(skillsSubdir), `skills/ subdir must NOT exist in ${entry} (flat layout, #924)`);
|
||||
}
|
||||
|
||||
// Total SKILL.md files across all routers + nested children must be large (proves no skill was dropped).
|
||||
function countSkillMdFiles(dir) {
|
||||
let count = 0;
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) count += countSkillMdFiles(fullPath);
|
||||
else if (entry.name === 'SKILL.md') count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
const totalSkillMd = countSkillMdFiles(stagedDir);
|
||||
assert.ok(totalSkillMd >= 60, `full profile should have >= 60 total SKILL.md files (routers + children), got ${totalSkillMd}`);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user