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:
@@ -8,6 +8,10 @@
|
||||
//
|
||||
// Fix (install-profiles.cts): gate nesting on full OR full-equivalent (all routerStems
|
||||
// present in the concrete Set) so that the surface path preserves nesting.
|
||||
//
|
||||
// NOTE: As of #924 Claude has been REVERTED to FLAT. This test now uses Cline as the
|
||||
// representative nested runtime. The original claude-global test below is updated to
|
||||
// assert the flat layout (>= 60 top-level gsd-* entries, concrete skills discoverable).
|
||||
|
||||
'use strict';
|
||||
|
||||
@@ -29,18 +33,19 @@ const { resolveRuntimeArtifactLayout } = require('../gsd-core/bin/lib/runtime-ar
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
describe('issue-69: applySurface preserves nested skill layout (no re-flatten)', () => {
|
||||
test('claude global full: applySurface keeps 6 router dirs and nested gsd-ns-workflow/skills/plan-phase/SKILL.md', (t) => {
|
||||
// #924: Claude is now flat; use Cline as the representative nested runtime.
|
||||
test('cline global full: applySurface keeps 6 router dirs and nested gsd-ns-manage/skills/help/SKILL.md', (t) => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-69-surface-'));
|
||||
t.after(() => { try { cleanup(tmpDir); } catch { /* best-effort */ } });
|
||||
|
||||
// Step 1: full install
|
||||
const manifest = loadSkillsManifest(COMMANDS_GSD);
|
||||
const resolved = resolveProfile({ modes: ['full'], manifest });
|
||||
installRuntimeArtifacts('claude', tmpDir, 'global', resolved);
|
||||
installRuntimeArtifacts('cline', tmpDir, 'global', resolved);
|
||||
|
||||
const skillsDir = path.join(tmpDir, 'skills');
|
||||
|
||||
// Sanity: install must produce nested layout
|
||||
// Sanity: install must produce nested layout (6 top-level router dirs)
|
||||
const topLevelAfterInstall = fs.readdirSync(skillsDir).filter((n) => n.startsWith('gsd-'));
|
||||
assert.strictEqual(
|
||||
topLevelAfterInstall.length,
|
||||
@@ -53,7 +58,7 @@ describe('issue-69: applySurface preserves nested skill layout (no re-flatten)',
|
||||
);
|
||||
|
||||
// Step 2: applySurface (full surface, no surface state file → resolves to full)
|
||||
const layout = resolveRuntimeArtifactLayout('claude', tmpDir, 'global');
|
||||
const layout = resolveRuntimeArtifactLayout('cline', tmpDir, 'global');
|
||||
applySurface(tmpDir, layout, manifest);
|
||||
|
||||
// Step 3: assert nested layout is preserved after applySurface
|
||||
@@ -77,4 +82,50 @@ describe('issue-69: applySurface preserves nested skill layout (no re-flatten)',
|
||||
'After applySurface: gsd-plan-phase/ must NOT exist at top level (#69 re-flatten regression guard)',
|
||||
);
|
||||
});
|
||||
|
||||
// #924 companion: Claude must use FLAT layout and applySurface must NOT re-nest it.
|
||||
test('claude global full: install produces flat layout and applySurface preserves it (#924)', (t) => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-924-69-'));
|
||||
t.after(() => { try { cleanup(tmpDir); } catch { /* best-effort */ } });
|
||||
|
||||
const manifest = loadSkillsManifest(COMMANDS_GSD);
|
||||
const resolved = resolveProfile({ modes: ['full'], manifest });
|
||||
installRuntimeArtifacts('claude', tmpDir, 'global', resolved);
|
||||
|
||||
const skillsDir = path.join(tmpDir, 'skills');
|
||||
|
||||
// Install must produce FLAT layout (>= 60 gsd-* dirs)
|
||||
const topLevelAfterInstall = fs.readdirSync(skillsDir).filter((n) => n.startsWith('gsd-'));
|
||||
assert.ok(
|
||||
topLevelAfterInstall.length >= 60,
|
||||
`Claude install must produce >= 60 gsd-* top-level dirs (flat, #924). Got ${topLevelAfterInstall.length}.`,
|
||||
);
|
||||
|
||||
// gsd-plan-phase must be directly at top level
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(skillsDir, 'gsd-plan-phase', 'SKILL.md')),
|
||||
'After claude install: gsd-plan-phase/SKILL.md must be at top level (flat layout, #924)',
|
||||
);
|
||||
|
||||
// No nested skills/ subdirs under gsd-ns-* in Claude
|
||||
assert.ok(
|
||||
!fs.existsSync(path.join(skillsDir, 'gsd-ns-workflow', 'skills')),
|
||||
'After claude install: gsd-ns-workflow/skills/ must NOT exist (flat layout, no nesting, #924)',
|
||||
);
|
||||
|
||||
// applySurface must preserve flat layout
|
||||
const layout = resolveRuntimeArtifactLayout('claude', tmpDir, 'global');
|
||||
applySurface(tmpDir, layout, manifest);
|
||||
|
||||
const topLevelAfterSurface = fs.readdirSync(skillsDir).filter((n) => n.startsWith('gsd-'));
|
||||
assert.ok(
|
||||
topLevelAfterSurface.length >= 60,
|
||||
`After applySurface: claude must still have >= 60 gsd-* dirs (flat preserved). Got ${topLevelAfterSurface.length}.`,
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
fs.existsSync(path.join(skillsDir, 'gsd-plan-phase', 'SKILL.md')),
|
||||
'After applySurface: gsd-plan-phase/SKILL.md must remain at top level (#924)',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user