fix(3610): unblock fresh Codex install when leftover bundled hooks present
`npx get-shit-done-cc@latest --codex` aborted with "installer migration blocked pending user choice" listing 12 hooks/gsd-* files. Those files are part of the GSD npm distribution (hooks/gsd-prompt-guard.js, hooks/gsd-context-monitor.js, etc.), not user-owned content, so asking the user to choose between keep/remove for them was a UX bug, not a real choice. The installer is about to write the fresh bundled versions in their place. Root cause: `classifyPromptUserAction` in get-shit-done/bin/lib/installer-migration-report.cjs knew two unambiguous categories (`stale-sdk-build-artifact`, `user-facing-skill`) but had no rule for the bundled GSD hooks. The first-time-baseline scan classified them as `stale-gsd-looking` prompt-user blockers, and `assertInstallerMigrationsUnblocked` threw. A second gate compounded the bug: the safe-default resolver in bin/install.js was wrapped in `if (!_migrationIsTty)`, so even with a correct classification rule, TTY runs (every `npx get-shit-done-cc` invocation) skipped the resolver and went straight to the hard throw. Fix: 1) Add `hooks/gsd-<name>.(js|sh|cjs|mjs)` to `classifyPromptUserAction` as `bundled-gsd-hook` → `remove`. The regex is anchored at the top-level `hooks/` directory so nested paths like `hooks/gsd-helpers/index.js` (or any user-owned helper directory) do NOT auto-classify. 2) Remove the `!_migrationIsTty` gate from the resolver call in bin/install.js. The classifier-based path is unambiguous and must apply regardless of TTY; the env-override branch (GSD_INSTALLER_MIGRATION_RESOLVE) still applies only when isTty=false inside the resolver, preserving the #3541 semantic. Regression test added (tests/bug-3610-installer-migration-bundled-hooks-classification.test.cjs): - Positive: hooks/gsd-*.{js,sh} → category=bundled-gsd-hook, choice=remove. - Counter-test: hooks/my-custom-hook.js → classifier returns null (user files are preserved). - Boundary: hooks/gsd-helpers/index.js → classifier returns null (nested directories don't auto-classify). - End-to-end: 12 reporter-exact bundled hooks + empty manifest → resolver clears every blocker, assertInstallerMigrationsUnblocked does not throw. Test exercises the real installer-migration code path (`runInstallerMigrations` + `resolveInstallerMigrationPromptsForNonTty` + `assertInstallerMigrationsUnblocked`) — no source-grep, no raw text matching on outputs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -115,6 +115,17 @@ function classifyPromptUserAction(action) {
|
||||
if (/^skills\/gsd-[^/]+\/SKILL\.md$/.test(relPath)) {
|
||||
return { category: 'user-facing-skill', choice: 'keep' };
|
||||
}
|
||||
// #3610: bundled GSD hooks at hooks/gsd-<name>.<ext>. These are part of
|
||||
// the npm distribution (`hooks/gsd-*.{js,sh,cjs,mjs}` shipped in the
|
||||
// package), NOT user-owned files. When a first-time-baseline scan finds
|
||||
// them on disk without manifest entries — the case for any upgrade from
|
||||
// a pre-manifest-baseline release — the safe default is to remove them
|
||||
// so the installer can write the fresh bundled versions in their place.
|
||||
// Restricted to top-level files (`hooks/gsd-X.ext`) so nested user
|
||||
// directories like `hooks/gsd-helpers/...` do NOT auto-classify.
|
||||
if (/^hooks\/gsd-[^/]+\.(?:js|sh|cjs|mjs)$/.test(relPath)) {
|
||||
return { category: 'bundled-gsd-hook', choice: 'remove' };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user