refactor(3664): migrate bin/install.js install/uninstall to Runtime Artifact Layout Module (#3674)
Phase 2 of #3660 / ADR-3660. Routes both lifecycle verbs through the Runtime Artifact Layout Module landed in Phase 1 (#3663): - Add installRuntimeArtifacts(runtime, configDir, scope, resolvedProfile) and uninstallRuntimeArtifacts(runtime, configDir, scope) as the public orchestrators. Both pre-prune stale gsd-* entries before staged copy; installRuntimeArtifacts brackets the prune+copy with preserveUserArtifacts / restoreUserArtifacts so user-owned content (e.g. gsd-dev-preferences) survives wipe-and-replace for claude/qwen/hermes runtimes. - Add applyRuntimeContentRewritesInPlace as the per-runtime path/branding post-stage step (preserves byte-output equivalence with the legacy copyCommandsAs* pipeline, including Qwen/Hermes branding rewrites). - Add _copyStaged, _removeGsdEntries kind-aware filesystem helpers. - Add _runLegacyInstallMigrations, _runLegacyUninstallCleanup as thin dispatchers over existing ADR-0008 legacy migrations (Hermes flat->nested per #2841, dev-preferences-as-skill per #2973). For Hermes, also clean up the intermediate skills/gsd/gsd-*/ layout that pre-Phase-2 installs left on disk. - Delete the 9 copyCommandsAs*Skills functions (Codex / Cursor / Windsurf / Trae / CodeBuddy / Copilot / Claude / Antigravity / Augment) and the _copyCommandsAsSkillsViaConverter helper. All test entry points migrated to call installRuntimeArtifacts directly through the unified seam. - Collapse the 9-branch uninstall ladder to one uninstallRuntimeArtifacts call plus preserved non-layout side-effects (Codex TOML, Copilot instructions, hooks). - Unify install dispatcher: a single _isSkillsRuntime gate routes all 11 skills runtimes through installRuntimeArtifacts for both full and core/ minimal profiles. Removes 11 per-runtime if-else branches (3 minimal-mode shim branches + 8 dead after-the-gate branches). Net delta on bin/install.js: 11,495 -> 11,174 (-321 LOC). New tests: - tests/install-uninstall-layout-loop.test.cjs (34 tests) - per-runtime fixture assertions on install/uninstall/legacy-migration ordering. - tests/install-hermes-regressions.test.cjs (6 tests) - covers the six defects surfaced by iterative review: Hermes upgrade leaves stale dirs, --hermes --profile=core fall-through, --qwen --profile=core fall-through, minimal-mode dev-preferences migration skipped (Hermes/Qwen/Claude-global), and ordering bug in _runLegacyInstallMigrations. Existing tests (10,038 prior + 40 new) all green: 10,078/10,078 pass. Refs #3664 Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -170,21 +170,24 @@ describe('#1924: dev-preferences.md preserved across re-install (global Claude)'
|
||||
const originalContent = '# Dev Preferences\n\nI prefer TDD. I like short functions.\n';
|
||||
fs.writeFileSync(devPrefsPath, originalContent);
|
||||
|
||||
// Re-run installer (simulating gsd-update)
|
||||
// Bug: this triggers legacy cleanup that rmSync's commands/gsd/ entirely,
|
||||
// deleting dev-preferences.md
|
||||
// Re-run installer (simulating gsd-update).
|
||||
// In the layout-driven path (B2), legacy commands/gsd/ is removed and
|
||||
// dev-preferences.md is migrated to skills/gsd-dev-preferences/SKILL.md (#2973).
|
||||
runInstaller(tmpDir);
|
||||
|
||||
// Content is migrated to the new canonical skills location (#2973).
|
||||
// The old commands/gsd/ path is cleaned up; the skill file carries the content.
|
||||
const devPrefSkillPath = path.join(tmpDir, 'skills', 'gsd-dev-preferences', 'SKILL.md');
|
||||
assert.ok(
|
||||
fs.existsSync(devPrefsPath),
|
||||
'dev-preferences.md must survive re-install — gsd-update legacy cleanup must not delete user-generated files'
|
||||
fs.existsSync(devPrefSkillPath),
|
||||
'dev-preferences.md must be migrated to skills/gsd-dev-preferences/SKILL.md — gsd-update legacy cleanup must not silently drop user-generated content'
|
||||
);
|
||||
|
||||
const afterContent = fs.readFileSync(devPrefsPath, 'utf8');
|
||||
const afterContent = fs.readFileSync(devPrefSkillPath, 'utf8');
|
||||
assert.strictEqual(
|
||||
afterContent,
|
||||
originalContent,
|
||||
'dev-preferences.md content must be identical after re-install'
|
||||
'migrated dev-preferences content must be identical to the original'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -199,19 +202,22 @@ describe('#1924: dev-preferences.md preserved across re-install (global Claude)'
|
||||
fs.writeFileSync(legacyFile, '---\nname: gsd:next\n---\n\nLegacy content.');
|
||||
|
||||
// But dev-preferences.md is also there (user-generated)
|
||||
const devPrefsContent = '# Dev Preferences\n\nMy preferences.\n';
|
||||
const devPrefsPath = path.join(commandsGsdDir, 'dev-preferences.md');
|
||||
fs.writeFileSync(devPrefsPath, '# Dev Preferences\n\nMy preferences.\n');
|
||||
fs.writeFileSync(devPrefsPath, devPrefsContent);
|
||||
|
||||
// Re-install
|
||||
runInstaller(tmpDir);
|
||||
|
||||
// dev-preferences.md must be preserved
|
||||
// In the layout-driven path (B2), commands/gsd/ is fully removed but
|
||||
// dev-preferences.md content is migrated to the new canonical skill location.
|
||||
const devPrefSkillPath = path.join(tmpDir, 'skills', 'gsd-dev-preferences', 'SKILL.md');
|
||||
assert.ok(
|
||||
fs.existsSync(devPrefsPath),
|
||||
'dev-preferences.md must be preserved while legacy commands/gsd/ is cleaned up'
|
||||
fs.existsSync(devPrefSkillPath),
|
||||
'dev-preferences.md content must be migrated to skills/gsd-dev-preferences/SKILL.md'
|
||||
);
|
||||
|
||||
// The legacy GSD command (next.md) is NOT user-generated, should be removed
|
||||
// The legacy GSD command (next.md) is NOT user-generated, must be removed
|
||||
// (it would exist only as a skill now in skills/gsd-next/SKILL.md)
|
||||
assert.ok(
|
||||
!fs.existsSync(legacyFile),
|
||||
|
||||
Reference in New Issue
Block a user