@opengsd/gsd-core 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +12 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-doc-synthesizer.md +2 -4
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +22 -14
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +5 -3
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +23 -2
- package/agents/gsd-plan-checker.md +50 -53
- package/agents/gsd-planner.md +50 -50
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +2 -2
- package/agents/gsd-roadmapper.md +15 -11
- package/agents/gsd-ui-checker.md +63 -4
- package/agents/gsd-ui-researcher.md +41 -3
- package/agents/gsd-user-profiler.md +3 -0
- package/agents/gsd-verifier.md +13 -4
- package/bin/install.js +1448 -1103
- package/commands/gsd/code-review.md +1 -1
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/map-codebase.md +1 -1
- package/commands/gsd/mempalace-capture.md +1 -1
- package/commands/gsd/mempalace-recall.md +1 -1
- package/commands/gsd/new-milestone.md +1 -1
- package/commands/gsd/quick.md +9 -5
- package/commands/gsd/review-backlog.md +2 -1
- package/commands/gsd/verify-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +1035 -138
- package/gsd-core/bin/lib/active-workstream-store.cjs +146 -22
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +392 -32
- package/gsd-core/bin/lib/api-coverage.cjs +33 -14
- package/gsd-core/bin/lib/artifacts.cjs +5 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +32 -15
- package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
- package/gsd-core/bin/lib/audit.cjs +1026 -268
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- package/gsd-core/bin/lib/capability-consent.cjs +149 -15
- package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +845 -130
- package/gsd-core/bin/lib/capability-source.cjs +92 -0
- package/gsd-core/bin/lib/capability-state.cjs +18 -3
- package/gsd-core/bin/lib/capability-trust.cjs +444 -25
- package/gsd-core/bin/lib/capability-validator.cjs +700 -40
- package/gsd-core/bin/lib/capability-writer.cjs +3 -2
- package/gsd-core/bin/lib/check-command-router.cjs +216 -42
- package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +735 -0
- package/gsd-core/bin/lib/command-aliases.cjs +22 -0
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-roster.cjs +44 -1
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +1172 -108
- package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
- package/gsd-core/bin/lib/complexity-trigger.cjs +1192 -0
- package/gsd-core/bin/lib/config-loader.cjs +187 -23
- package/gsd-core/bin/lib/config.cjs +102 -3
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +208 -33
- package/gsd-core/bin/lib/decisions.cjs +23 -0
- package/gsd-core/bin/lib/edge-probe.cjs +9 -1
- package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
- package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
- package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
- package/gsd-core/bin/lib/frontmatter.cjs +899 -229
- package/gsd-core/bin/lib/gap-checker.cjs +95 -10
- package/gsd-core/bin/lib/git-base-branch.cjs +276 -39
- package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
- package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +149 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +268 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +187 -0
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +451 -0
- package/gsd-core/bin/lib/host-integration.cjs +39 -6
- package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
- package/gsd-core/bin/lib/init-command-router.cjs +118 -21
- package/gsd-core/bin/lib/init.cjs +439 -168
- package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
- package/gsd-core/bin/lib/install-engine.cjs +811 -259
- package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +235 -0
- package/gsd-core/bin/lib/install-profiles.cjs +212 -61
- package/gsd-core/bin/lib/install-scope.cjs +270 -0
- package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
- package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
- package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
- package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +148 -38
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +170 -15
- package/gsd-core/bin/lib/learnings.cjs +85 -14
- package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
- package/gsd-core/bin/lib/markdown-table.cjs +183 -22
- package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
- package/gsd-core/bin/lib/milestone.cjs +842 -73
- package/gsd-core/bin/lib/model-catalog.cjs +232 -16
- package/gsd-core/bin/lib/model-resolver.cjs +193 -68
- package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/pattern.cjs +122 -0
- package/gsd-core/bin/lib/phase-estimation.cjs +18 -9
- package/gsd-core/bin/lib/phase-id.cjs +514 -40
- package/gsd-core/bin/lib/phase-lifecycle.cjs +52 -19
- package/gsd-core/bin/lib/phase-locator.cjs +262 -34
- package/gsd-core/bin/lib/phase.cjs +1038 -214
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
- package/gsd-core/bin/lib/plan-document.cjs +263 -0
- package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
- package/gsd-core/bin/lib/plan-scan.cjs +98 -3
- package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
- package/gsd-core/bin/lib/planning-scope.cjs +31 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +894 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +112 -6
- package/gsd-core/bin/lib/probe-core.cjs +5 -2
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
- package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +766 -0
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -13
- package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
- package/gsd-core/bin/lib/roadmap-command-router.cjs +59 -11
- package/gsd-core/bin/lib/roadmap-parser.cjs +1006 -184
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
- package/gsd-core/bin/lib/roadmap.cjs +442 -96
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +702 -52
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +459 -55
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
- package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +402 -58
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
- package/gsd-core/bin/lib/runtime-slash.cjs +96 -8
- package/gsd-core/bin/lib/security.cjs +104 -5
- package/gsd-core/bin/lib/shell-command-projection.cjs +342 -7
- package/gsd-core/bin/lib/smart-entry.cjs +133 -23
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +52 -19
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +338 -8
- package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
- package/gsd-core/bin/lib/state-transition.cjs +846 -176
- package/gsd-core/bin/lib/state.cjs +2589 -369
- package/gsd-core/bin/lib/surface.cjs +33 -11
- package/gsd-core/bin/lib/task-command-router.cjs +111 -1
- package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
- package/gsd-core/bin/lib/teams-status.cjs +4 -1
- package/gsd-core/bin/lib/text-lines.cjs +80 -0
- package/gsd-core/bin/lib/token-scanner.cjs +76 -0
- package/gsd-core/bin/lib/uat-predicate.cjs +67 -23
- package/gsd-core/bin/lib/uat.cjs +1761 -167
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +51 -12
- package/gsd-core/bin/lib/unusable-input.cjs +37 -0
- package/gsd-core/bin/lib/update-context.cjs +8 -2
- package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +20 -6
- package/gsd-core/bin/lib/vendor/README.md +75 -0
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
- package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
- package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +272 -9
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify.cjs +453 -918
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
- package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
- package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/workstream.cjs +2 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +341 -18
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +12 -1
- package/gsd-core/bin/shared/exit-codes.json +8 -0
- package/gsd-core/bin/shared/exit-codes.sh +20 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -1
- package/gsd-core/references/agent-contracts.md +44 -26
- package/gsd-core/references/api-coverage.md +24 -2
- package/gsd-core/references/autonomous-smart-discuss.md +3 -3
- package/gsd-core/references/checkpoints.md +39 -21
- package/gsd-core/references/context-budget.md +1 -1
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/dispatch-isolation-gate.md +138 -0
- package/gsd-core/references/doc-conflict-engine.md +1 -1
- package/gsd-core/references/edge-probe.md +8 -0
- package/gsd-core/references/execute-mvp-tdd.md +4 -6
- package/gsd-core/references/execute-phase-between-wave-reset.md +15 -14
- package/gsd-core/references/execute-phase-context-guard.md +1 -1
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +17 -11
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-integration.md +5 -5
- package/gsd-core/references/git-planning-commit.md +5 -4
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +61 -2
- package/gsd-core/references/model-profiles.md +12 -4
- package/gsd-core/references/mvp-concepts.md +9 -9
- package/gsd-core/references/nyquist-compliance.md +74 -0
- package/gsd-core/references/offer-next.md +3 -5
- package/gsd-core/references/phase-argument-parsing.md +3 -3
- package/gsd-core/references/planner-failing-direction.md +53 -0
- package/gsd-core/references/planner-guidance.md +3 -9
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- package/gsd-core/references/planner-preconditions.md +1 -1
- package/gsd-core/references/planner-reviews.md +1 -1
- package/gsd-core/references/planner-revision.md +1 -1
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +44 -13
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/revision-loop.md +1 -1
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/specless-probe-fallback.md +1 -1
- package/gsd-core/references/tdd.md +1 -3
- package/gsd-core/references/ui-brand.md +65 -21
- package/gsd-core/references/ui-consideration-probe.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +5 -5
- package/gsd-core/references/verifier-phase-gates.md +192 -0
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -17
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/SECURITY.md +3 -3
- package/gsd-core/templates/UI-SPEC.md +25 -3
- package/gsd-core/templates/VALIDATION.md +3 -3
- package/gsd-core/templates/discussion-log.md +1 -1
- package/gsd-core/templates/phase-prompt.md +5 -4
- package/gsd-core/templates/state.md +11 -4
- package/gsd-core/templates/verification-report.md +9 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +3 -3
- package/gsd-core/workflows/add-tests.md +3 -8
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +13 -20
- package/gsd-core/workflows/audit-fix.md +12 -3
- package/gsd-core/workflows/audit-milestone.md +9 -9
- package/gsd-core/workflows/audit-uat.md +17 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +11 -27
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +64 -5
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +14 -4
- package/gsd-core/workflows/code-review-fix.md +38 -11
- package/gsd-core/workflows/code-review.md +159 -52
- package/gsd-core/workflows/complete-milestone.md +151 -23
- package/gsd-core/workflows/debug.md +12 -8
- package/gsd-core/workflows/diagnose-issues.md +47 -15
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -8
- package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +3 -6
- package/gsd-core/workflows/docs-update.md +5 -4
- package/gsd-core/workflows/edit-phase.md +27 -2
- package/gsd-core/workflows/eval-review.md +7 -14
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +142 -15
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +24 -4
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase.md +72 -100
- package/gsd-core/workflows/execute-plan.md +52 -15
- package/gsd-core/workflows/explore.md +131 -4
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/fast.md +10 -2
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +5 -5
- package/gsd-core/workflows/health.md +76 -10
- package/gsd-core/workflows/import.md +18 -15
- package/gsd-core/workflows/inbox.md +4 -5
- package/gsd-core/workflows/ingest-docs.md +49 -16
- package/gsd-core/workflows/insert-phase.md +5 -5
- package/gsd-core/workflows/list-seeds.md +5 -3
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +12 -23
- package/gsd-core/workflows/map-codebase.md +1 -1
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +8 -5
- package/gsd-core/workflows/new-milestone.md +22 -29
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +26 -40
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +14 -2
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
- package/gsd-core/workflows/plan-phase.md +162 -59
- package/gsd-core/workflows/plan-review-convergence.md +96 -11
- package/gsd-core/workflows/plant-seed.md +2 -2
- package/gsd-core/workflows/pr-branch.md +187 -51
- package/gsd-core/workflows/profile-user.md +16 -14
- package/gsd-core/workflows/progress.md +61 -18
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +5 -7
- package/gsd-core/workflows/quick/steps/quick-verification.md +28 -9
- package/gsd-core/workflows/quick/steps/research-phase.md +4 -6
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick.md +55 -44
- package/gsd-core/workflows/remove-phase.md +4 -4
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +8 -12
- package/gsd-core/workflows/review.md +219 -20
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +3 -3
- package/gsd-core/workflows/session-report.md +2 -1
- package/gsd-core/workflows/settings-advanced.md +7 -9
- package/gsd-core/workflows/settings-integrations.md +64 -31
- package/gsd-core/workflows/settings.md +69 -7
- package/gsd-core/workflows/ship.md +116 -50
- package/gsd-core/workflows/sketch-wrap-up.md +11 -17
- package/gsd-core/workflows/sketch.md +12 -18
- package/gsd-core/workflows/smart-entry.md +3 -5
- package/gsd-core/workflows/spec-phase.md +53 -13
- package/gsd-core/workflows/spike-wrap-up.md +7 -11
- package/gsd-core/workflows/spike.md +20 -31
- package/gsd-core/workflows/stats.md +2 -2
- package/gsd-core/workflows/sync-skills.md +64 -9
- package/gsd-core/workflows/thread.md +11 -7
- package/gsd-core/workflows/transition.md +49 -14
- package/gsd-core/workflows/ui-phase.md +15 -21
- package/gsd-core/workflows/ui-review.md +8 -12
- package/gsd-core/workflows/ultraplan-phase.md +5 -13
- package/gsd-core/workflows/undo.md +8 -16
- package/gsd-core/workflows/update.md +7 -11
- package/gsd-core/workflows/validate-phase.md +3 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +66 -25
- package/hooks/dist/gsd-agent-isolation-guard.js +158 -30
- package/hooks/dist/gsd-check-update-worker.js +56 -13
- package/hooks/dist/gsd-check-update.js +19 -1
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +19 -10
- package/hooks/dist/gsd-cursor-post-tool.js +3 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +2 -3
- package/hooks/dist/gsd-cursor-session-start.js +2 -1
- package/hooks/dist/gsd-cursor-stop.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +83 -3
- package/hooks/dist/gsd-cursor-subagent-stop.js +6 -3
- package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
- package/hooks/dist/gsd-graphify-update.sh +22 -18
- package/hooks/dist/gsd-node-runner.sh +76 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +37 -27
- package/hooks/dist/gsd-read-guard.js +16 -7
- package/hooks/dist/gsd-read-injection-scanner.js +55 -32
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +231 -24
- package/hooks/dist/gsd-update-banner.js +22 -1
- package/hooks/dist/gsd-validate-commit.sh +80 -6
- package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
- package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
- package/hooks/dist/gsd-workflow-guard.js +162 -46
- package/hooks/dist/gsd-worktree-path-guard.js +36 -21
- package/hooks/dist/gsd-write-guard.js +35 -25
- package/hooks/dist/lib/cli-exit.js +560 -0
- package/hooks/dist/lib/exit-code-registry.js +98 -0
- package/hooks/dist/lib/git-cmd.js +92 -59
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/lib/injection-patterns.js +45 -0
- package/hooks/dist/lib/isolation-deny-reason.js +39 -0
- package/hooks/dist/lib/isolation-sentinel.js +9 -0
- package/hooks/dist/managed-hooks-registry.cjs +3 -0
- package/hooks/gsd-agent-isolation-guard.js +158 -30
- package/hooks/gsd-check-update-worker.js +56 -13
- package/hooks/gsd-check-update.js +19 -1
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +19 -10
- package/hooks/gsd-cursor-post-tool.js +3 -1
- package/hooks/gsd-cursor-pre-tool.js +2 -3
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +83 -3
- package/hooks/gsd-cursor-subagent-stop.js +6 -3
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +76 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +37 -27
- package/hooks/gsd-read-guard.js +16 -7
- package/hooks/gsd-read-injection-scanner.js +55 -32
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +231 -24
- package/hooks/gsd-update-banner.js +22 -1
- package/hooks/gsd-validate-commit.sh +80 -6
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +162 -46
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-cmd.js +92 -59
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/lib/injection-patterns.js +45 -0
- package/hooks/lib/isolation-deny-reason.js +39 -0
- package/hooks/lib/isolation-sentinel.js +9 -0
- package/hooks/managed-hooks-registry.cjs +3 -0
- package/package.json +28 -11
- package/pi/gsd.cjs +19 -5
- package/scripts/base64-scan.sh +74 -12
- package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
- package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
- package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
- package/scripts/build-hooks.js +5 -0
- package/scripts/changeset/lint.cjs +60 -5
- package/scripts/check-alias-drift.cjs +7 -43
- package/scripts/check-contract-drift.cjs +297 -0
- package/scripts/check-glossary-refs.cjs +77 -15
- package/scripts/check-mutation-score-ratchet.cjs +156 -0
- package/scripts/ci-check-job-near-cap.cjs +49 -0
- package/scripts/ci-pr-mergeability.cjs +262 -0
- package/scripts/ci-test-scope.cjs +64 -14
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/command-contract-helpers.cjs +903 -1
- package/scripts/docs-guard-registry.cjs +396 -0
- package/scripts/gen-adr-index.cjs +728 -38
- package/scripts/gen-capability-registry.cjs +11 -21
- package/scripts/gen-context-index.cjs +2 -11
- package/scripts/gen-exit-code-docs.cjs +318 -0
- package/scripts/gen-exit-code-registry.cjs +891 -0
- package/scripts/gen-features.cjs +836 -0
- package/scripts/gen-health-docs.cjs +390 -0
- package/scripts/gen-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-inventory-manifest.cjs +50 -4
- package/scripts/gen-loop-host-contract.cjs +138 -25
- package/scripts/gen-registry.cjs +3 -14
- package/scripts/gen-scripts-cli-exit.cjs +185 -0
- package/scripts/gen-state-md-docs.cjs +727 -0
- package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
- package/scripts/lib/alias-drift-families.cjs +46 -0
- package/scripts/lib/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +308 -0
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
- package/scripts/lint-canary-version-leak.cjs +73 -0
- package/scripts/lint-command-contract.cjs +96 -13
- package/scripts/lint-completion-predicate-drift.cjs +933 -0
- package/scripts/lint-completion-ratio-drift.cjs +214 -0
- package/scripts/lint-default-flip-documentation.cjs +193 -0
- package/scripts/lint-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +38 -0
- package/scripts/lint-eslint-glob-coverage.cjs +340 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
- package/scripts/lint-health-diagnostic-rule-table.cjs +461 -0
- package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
- package/scripts/lint-milestone-window-drift.cjs +468 -0
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +492 -0
- package/scripts/lint-plan-count-drift.cjs +318 -0
- package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
- package/scripts/lint-planning-prompt-drift.cjs +471 -0
- package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
- package/scripts/lint-regression-test-names.cjs +15 -13
- package/scripts/lint-removed-but-needed.cjs +488 -0
- package/scripts/lint-seam-enforcement.cjs +182 -0
- package/scripts/lint-slug-derivation-drift.cjs +921 -0
- package/scripts/lint-source-test-name-collision.cjs +241 -0
- package/scripts/lint-state-field-drift.cjs +805 -0
- package/scripts/lint-state-write-path-drift.cjs +950 -0
- package/scripts/lint-test-file-count.allowlist.json +137 -8
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +830 -0
- package/scripts/lint-vendored-deps.cjs +297 -0
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/pr-changed-files.cjs +63 -0
- package/scripts/pr-template-policy.cjs +14 -4
- package/scripts/prompt-injection-scan.sh +100 -14
- package/scripts/require-issue-link-policy.cjs +192 -0
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +24 -7
- package/skills/gsd-autonomous/SKILL.md +0 -1
- package/skills/gsd-code-review/SKILL.md +1 -1
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -2
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-map-codebase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +1 -1
- package/skills/gsd-mempalace-recall/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +1 -1
- package/skills/gsd-next/SKILL.md +0 -1
- package/skills/gsd-plan-phase/SKILL.md +0 -1
- package/skills/gsd-progress/SKILL.md +0 -1
- package/skills/gsd-quick/SKILL.md +9 -5
- package/skills/gsd-review-backlog/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +0 -1
- package/skills/gsd-verify-work/SKILL.md +1 -1
- package/vscode/package.json +1 -1
- package/bin/lib/ui-safety-gate.cjs +0 -107
- package/gsd-core/workflows/discovery-phase.md +0 -298
- package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
- package/gsd-core/workflows/verify-phase.md +0 -574
- package/scripts/affected-tests-lib.cjs +0 -554
- package/scripts/lint-allow-test-rule-refs.cjs +0 -162
- package/scripts/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/run-affected-tests.cjs +0 -7
- package/scripts/run-tests.cjs +0 -1051
|
@@ -21,9 +21,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
21
21
|
* BEHAVIOR-PRESERVING RELOCATION: all logic is copied verbatim from
|
|
22
22
|
* bin/install.js. No behavior change, no descriptor reads, no new IO.
|
|
23
23
|
*
|
|
24
|
-
* bin/install.js re-
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* #2876 (epic #2866 Phase 7): bin/install.js previously re-exported every
|
|
25
|
+
* symbol from this module, but a repo-wide audit found zero production
|
|
26
|
+
* consumers of those re-exports — no `require('../bin/install.js').
|
|
27
|
+
* writeCursorHooksJson` (or any sibling) exists outside a doc comment
|
|
28
|
+
* anywhere in the tree. The re-exports were test-suite-only pass-throughs;
|
|
29
|
+
* tests now require this module directly instead.
|
|
27
30
|
*/
|
|
28
31
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
29
32
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -35,7 +38,7 @@ const commonjs_marker_cjs_1 = require("./commonjs-marker.cjs");
|
|
|
35
38
|
const imperative_hook_bus_cjs_1 = require("./host-integration-adapters/imperative-hook-bus.cjs");
|
|
36
39
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
37
40
|
const shellCmdProjection = require("./shell-command-projection.cjs");
|
|
38
|
-
const { isManagedHookBasename, isManagedHookCommand, projectLegacySettingsHookCommand, projectManagedHookCommand, projectPortableHookBaseDir, projectCodexHookTomlCommand, shellHookOmitsBashRunner, escapeTomlDoubleQuotedString, } = shellCmdProjection;
|
|
41
|
+
const { isManagedHookBasename, isManagedHookCommand, projectLegacySettingsHookCommand, projectManagedHookCommand, projectPortableHookBaseDir, projectCodexHookTomlCommand, shellHookOmitsBashRunner, escapeTomlDoubleQuotedString, escapePosixDoubleQuoted, } = shellCmdProjection;
|
|
39
42
|
// ---------------------------------------------------------------------------
|
|
40
43
|
// Terminal color constants (mirrors install.js for console output parity)
|
|
41
44
|
// ---------------------------------------------------------------------------
|
|
@@ -133,23 +136,64 @@ function _capabilityTitle(runtime) {
|
|
|
133
136
|
let __atomicWriteCounter = 0;
|
|
134
137
|
// Set<string> — absolute paths of .tmp-<pid>-<n> files this process created.
|
|
135
138
|
const __atomicWrittenTmps = new Set();
|
|
139
|
+
// Retry budget for the EEXIST (squatted temp path) branch in atomicWriteFileSync.
|
|
140
|
+
const MAX_TEMP_FILE_ATTEMPTS = 4;
|
|
136
141
|
function atomicWriteFileSync(target, data, options) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
142
|
+
// A pre-existing target's permission bits must survive the rewrite:
|
|
143
|
+
// rename() swaps the temp file's inode into place, so without an explicit
|
|
144
|
+
// carry a user-hardened chmod (e.g. 600 on a secrets-bearing settings.json)
|
|
145
|
+
// would silently reset to the umask default.
|
|
146
|
+
let priorMode;
|
|
140
147
|
try {
|
|
141
|
-
node_fs_1.default.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
+
const st = node_fs_1.default.statSync(target);
|
|
149
|
+
if (st.isFile())
|
|
150
|
+
priorMode = st.mode & 0o7777;
|
|
151
|
+
}
|
|
152
|
+
catch { /* no pre-existing target: default creation mode applies */ }
|
|
153
|
+
// 'wx' (O_EXCL) refuses to follow a symlink pre-planted at the predictable
|
|
154
|
+
// temp path and refuses to reuse a foreign file already sitting there; on
|
|
155
|
+
// EEXIST the write retries under a fresh counter value.
|
|
156
|
+
const exclusiveOptions = typeof options === 'string' || options == null
|
|
157
|
+
? { encoding: options ?? null, flag: 'wx' }
|
|
158
|
+
: { ...options, flag: 'wx' };
|
|
159
|
+
for (let attempt = 0;; attempt++) {
|
|
160
|
+
__atomicWriteCounter += 1;
|
|
161
|
+
const tmp = `${target}.tmp-${process.pid}-${__atomicWriteCounter}`;
|
|
162
|
+
__atomicWrittenTmps.add(tmp);
|
|
148
163
|
try {
|
|
149
|
-
node_fs_1.default.
|
|
164
|
+
node_fs_1.default.writeFileSync(tmp, data, exclusiveOptions);
|
|
150
165
|
}
|
|
151
|
-
catch {
|
|
152
|
-
|
|
166
|
+
catch (e) {
|
|
167
|
+
if (e.code === 'EEXIST') {
|
|
168
|
+
// The file at tmp is not ours — never rmSync it.
|
|
169
|
+
if (attempt < MAX_TEMP_FILE_ATTEMPTS)
|
|
170
|
+
continue;
|
|
171
|
+
throw e;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
node_fs_1.default.rmSync(tmp, { force: true });
|
|
175
|
+
}
|
|
176
|
+
catch { /* ignore */ }
|
|
177
|
+
throw e;
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
// chmod rather than options.mode: open(2) masks mode with the process
|
|
181
|
+
// umask, chmod applies the preserved bits exactly.
|
|
182
|
+
if (priorMode !== undefined)
|
|
183
|
+
node_fs_1.default.chmodSync(tmp, priorMode);
|
|
184
|
+
shellCmdProjection.retryRenameSync(tmp, target);
|
|
185
|
+
// Successful rename: the tmp path no longer exists, but leave it in the
|
|
186
|
+
// Set so _cleanTmpFiles can recognise it as installer-owned if it somehow
|
|
187
|
+
// lingers (e.g. a rename succeeded but left a stale entry on some FS).
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
try {
|
|
191
|
+
node_fs_1.default.rmSync(tmp, { force: true });
|
|
192
|
+
}
|
|
193
|
+
catch { /* ignore */ }
|
|
194
|
+
throw e;
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
153
197
|
}
|
|
154
198
|
}
|
|
155
199
|
// ---------------------------------------------------------------------------
|
|
@@ -330,20 +374,41 @@ function parseTomlValue(text, i) {
|
|
|
330
374
|
}
|
|
331
375
|
throw new Error(`parseTomlValue: unexpected character '${ch}' at position ${i}`);
|
|
332
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* Normalize a directory that will be joined with `/…` — posix separators, no
|
|
379
|
+
* trailing slash. `FNM_DIR=/custom/fnm/` would otherwise bake
|
|
380
|
+
* `/custom/fnm//aliases/default/bin/node` (#3704 review). Cosmetic — `existsSync`
|
|
381
|
+
* resolves the doubled separator and every shell collapses it — but the value is
|
|
382
|
+
* written into a user's settings.json and read by humans.
|
|
383
|
+
*
|
|
384
|
+
* Shared by both fnm branches deliberately: they build the same alias paths from
|
|
385
|
+
* different roots, and a trim applied to only one is a difference with no reason
|
|
386
|
+
* behind it.
|
|
387
|
+
*/
|
|
388
|
+
function normalizeRootDir(dir) {
|
|
389
|
+
return shellCmdProjection.posixNormalize(dir).replace(/\/+$/, '');
|
|
390
|
+
}
|
|
333
391
|
function normalizeNodePath(execPath, opts) {
|
|
334
392
|
if (!execPath)
|
|
335
393
|
return execPath;
|
|
336
394
|
const env = (opts && opts.env) || process.env;
|
|
337
395
|
const existsSync = (opts && opts.existsSync) || node_fs_1.default.existsSync;
|
|
338
396
|
const normalizedForMatch = shellCmdProjection.posixNormalize(execPath);
|
|
339
|
-
|
|
397
|
+
// #977: fnm's Windows shim IS `process.execPath` there — Windows does not
|
|
398
|
+
// realpath through it — so this branch stays. #3704 adds `(?:bin\/)?`: fnm's
|
|
399
|
+
// POSIX shim is `<multishell>/bin/node`, so the original pattern could not match
|
|
400
|
+
// it even when a caller hands one in explicitly (#3662's `execPath` option
|
|
401
|
+
// exists to do exactly that, normalizing a path from another environment).
|
|
402
|
+
if (/\/fnm_multishells\/[0-9]+_[0-9]+\/(?:bin\/)?node(\.exe)?$/i.test(normalizedForMatch)) {
|
|
340
403
|
const candidates = [];
|
|
341
404
|
if (env.FNM_DIR) {
|
|
342
|
-
|
|
343
|
-
candidates.push(`${
|
|
405
|
+
const fnmRoot = normalizeRootDir(env.FNM_DIR);
|
|
406
|
+
candidates.push(`${fnmRoot}/aliases/default/node.exe`);
|
|
407
|
+
candidates.push(`${fnmRoot}/aliases/default/bin/node`);
|
|
344
408
|
}
|
|
345
|
-
|
|
346
|
-
|
|
409
|
+
const appdata = shellCmdProjection.envGet(env, 'APPDATA');
|
|
410
|
+
if (appdata) {
|
|
411
|
+
candidates.push(`${normalizeRootDir(appdata)}/fnm/aliases/default/node.exe`);
|
|
347
412
|
}
|
|
348
413
|
for (const candidate of candidates) {
|
|
349
414
|
if (candidate && existsSync(candidate))
|
|
@@ -351,6 +416,42 @@ function normalizeNodePath(execPath, opts) {
|
|
|
351
416
|
}
|
|
352
417
|
return execPath;
|
|
353
418
|
}
|
|
419
|
+
// #3704: fnm pins a concrete version at
|
|
420
|
+
// <FNM_DIR>/node-versions/<ver>/installation/bin/node (Windows:
|
|
421
|
+
// .../installation/node.exe). On macOS/Linux this — not the shim above — is what
|
|
422
|
+
// `process.execPath` reports, because Node realpaths through
|
|
423
|
+
// `fnm_multishells`. So the shim branch above is unreachable on POSIX and the
|
|
424
|
+
// raw versioned path was baked into every managed hook: `fnm uninstall <ver>`,
|
|
425
|
+
// or fnm's own pruning, then 404s every hook — the ephemeral-path failure #977
|
|
426
|
+
// exists to prevent, and the same one #1619 (mise) and #2185 (Homebrew) fixed
|
|
427
|
+
// for their managers by matching the VERSIONED path rather than a shim.
|
|
428
|
+
//
|
|
429
|
+
// The stable alias is <FNM_DIR>/aliases/default/..., which fnm repoints on
|
|
430
|
+
// `fnm default`. Derive <FNM_DIR> from execPath first (#2185's rule — the path
|
|
431
|
+
// IS the install location, and FNM_DIR may be unset or point at a different
|
|
432
|
+
// install), keeping the env as a secondary candidate. Rewrite only when the
|
|
433
|
+
// alias exists; otherwise fall through to the raw execPath, exactly like the
|
|
434
|
+
// mise and volta branches, so a rewrite never turns a stale-but-working pin
|
|
435
|
+
// into an immediately broken one.
|
|
436
|
+
const fnmVersioned = normalizedForMatch.match(/^(.*)\/node-versions\/[^/]+\/installation\/(?:bin\/)?node(\.exe)?$/i);
|
|
437
|
+
if (fnmVersioned) {
|
|
438
|
+
const isExe = Boolean(fnmVersioned[2]);
|
|
439
|
+
const roots = [fnmVersioned[1]];
|
|
440
|
+
if (env.FNM_DIR)
|
|
441
|
+
roots.push(normalizeRootDir(env.FNM_DIR));
|
|
442
|
+
const fnmAliasCandidates = [];
|
|
443
|
+
for (const root of roots) {
|
|
444
|
+
// Probe the spelling matching the input first, then the other — a layout
|
|
445
|
+
// is one or the other, and guessing wrong would skip a real alias.
|
|
446
|
+
const leaf = isExe ? ['node.exe', 'bin/node'] : ['bin/node', 'node.exe'];
|
|
447
|
+
for (const tail of leaf)
|
|
448
|
+
fnmAliasCandidates.push(`${root}/aliases/default/${tail}`);
|
|
449
|
+
}
|
|
450
|
+
for (const candidate of fnmAliasCandidates) {
|
|
451
|
+
if (existsSync(candidate))
|
|
452
|
+
return candidate;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
354
455
|
// Homebrew (macOS Intel /usr/local, Apple Silicon /opt/homebrew, Linuxbrew
|
|
355
456
|
// /home/linuxbrew/.linuxbrew, and any custom HOMEBREW_PREFIX) pins node at
|
|
356
457
|
// <prefix>/Cellar/node(<@ver>)?/<ver>/bin/node, then deletes prior versions on
|
|
@@ -396,12 +497,76 @@ function normalizeNodePath(execPath, opts) {
|
|
|
396
497
|
return execPath;
|
|
397
498
|
}
|
|
398
499
|
function resolveNodeRunner(opts) {
|
|
399
|
-
const execPath = typeof process.execPath === 'string' ? process.execPath : '';
|
|
500
|
+
const execPath = (opts && opts.execPath) || (typeof process.execPath === 'string' ? process.execPath : '');
|
|
400
501
|
if (!execPath)
|
|
401
502
|
return null;
|
|
402
503
|
const stablePath = normalizeNodePath(execPath, opts);
|
|
403
504
|
return JSON.stringify(shellCmdProjection.posixNormalize(stablePath));
|
|
404
505
|
}
|
|
506
|
+
/**
|
|
507
|
+
* #3662 — the runtime-resolving node runner token for managed JS hooks.
|
|
508
|
+
*
|
|
509
|
+
* A bake-time absolute runner (`resolveNodeRunner`) only works in the
|
|
510
|
+
* environment that ran the installer; a config root shared across
|
|
511
|
+
* environments (the `--portable-hooks` scenario, or any settings.json under a
|
|
512
|
+
* mounted `$HOME`) carries a path that 404s with exit 127 everywhere else.
|
|
513
|
+
* This token is a POSIX `sh` command substitution that resolves node at
|
|
514
|
+
* hook-fire time, trying IN ORDER:
|
|
515
|
+
*
|
|
516
|
+
* 1. the baked installer path (absolute — keeps the #2979/#3002/#3017/#3022
|
|
517
|
+
* minimal-PATH guarantee: where the baked path exists it still wins,
|
|
518
|
+
* under any PATH, GUI launch included);
|
|
519
|
+
* 2. `command -v node` (quoted — one word even with spaces in the result);
|
|
520
|
+
* 3. the well-known stable layouts (`/usr/local/bin/node`, `/usr/bin/node`).
|
|
521
|
+
*
|
|
522
|
+
* The FIRST executable candidate wins; if none resolves the substitution
|
|
523
|
+
* yields an empty word and the hook fails exactly as a stale absolute path
|
|
524
|
+
* does today — no bare `node` token is ever emitted or depended on.
|
|
525
|
+
*
|
|
526
|
+
* One shape for every platform: emitted hook commands execute via POSIX `sh`
|
|
527
|
+
* (Claude-on-win32 runs Git Bash per #166/#580; `hookCommandNeedsPowerShellCallOperator`
|
|
528
|
+
* is an unused opt-in), and the baked path is posixNormalize'd before escaping
|
|
529
|
+
* (escapePosixDoubleQuoted — the Shell Command Projection seam owns quoting).
|
|
530
|
+
* The portable resolver script (hooks/gsd-node-runner.sh) resolves through a
|
|
531
|
+
* SUPERSET of this candidate list — keep the two lists consistent.
|
|
532
|
+
*/
|
|
533
|
+
function buildNodeRunnerChainToken(opts) {
|
|
534
|
+
const execPath = (opts && opts.execPath) || (typeof process.execPath === 'string' ? process.execPath : '');
|
|
535
|
+
if (!execPath)
|
|
536
|
+
return null;
|
|
537
|
+
const stablePath = shellCmdProjection.posixNormalize(normalizeNodePath(execPath, opts));
|
|
538
|
+
const baked = escapePosixDoubleQuoted(stablePath);
|
|
539
|
+
// Absolute candidates only (leading / or a win32 drive letter): a relative
|
|
540
|
+
// `command -v node` hit (legal under a relative PATH entry) must never
|
|
541
|
+
// promote repo-cwd content into the runner slot. The gate uses parameter
|
|
542
|
+
// expansion + [ ] — deliberately NO `case` (its `)` terminates the command
|
|
543
|
+
// substitution under macOS's stock bash 3.2 /bin/sh, breaking the hook).
|
|
544
|
+
// \${…} below stays a literal shell parameter expansion, not TS interpolation.
|
|
545
|
+
return `"$(for n in "${baked}" "$(command -v node)" /usr/local/bin/node /usr/bin/node; do [ -x "$n" ] && { [ "\${n#/}" != "$n" ] || [ "\${n#?:}" != "$n" ]; } && printf '%s' "$n" && break; done)"`;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* #3662 — the install-time node path as a shell-safe QUOTED token, carrying
|
|
549
|
+
* the same double-quote escaping as the chain token (`escapePosixDoubleQuoted`
|
|
550
|
+
* — $ ` " \), NOT bare JSON quoting. The portable resolver's first argument
|
|
551
|
+
* is executed by the host shell before the resolver sees argv, so a path
|
|
552
|
+
* containing shell metacharacters must arrive escaped.
|
|
553
|
+
*/
|
|
554
|
+
function buildBakedNodeToken(opts) {
|
|
555
|
+
const execPath = (opts && opts.execPath) || (typeof process.execPath === 'string' ? process.execPath : '');
|
|
556
|
+
if (!execPath)
|
|
557
|
+
return null;
|
|
558
|
+
const stablePath = shellCmdProjection.posixNormalize(normalizeNodePath(execPath, opts));
|
|
559
|
+
return `"${escapePosixDoubleQuoted(stablePath)}"`;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* #3662 — basename of the portable node resolver staged into the install's
|
|
563
|
+
* hooks/ directory. Under `--portable-hooks`, managed JS hook commands route
|
|
564
|
+
* through it (`bash "<hooks>/gsd-node-runner.sh" "<baked-node>" "<script>.js"`)
|
|
565
|
+
* so the SAME staged file works for every install: the install-time node path
|
|
566
|
+
* travels as the resolver's first argument (tried first — the minimal-PATH
|
|
567
|
+
* guarantee), ahead of `command -v node` and the well-known fallback list.
|
|
568
|
+
*/
|
|
569
|
+
const NODE_RUNNER_RESOLVER_HOOK = 'gsd-node-runner.sh';
|
|
405
570
|
function resolveBashRunner(opts) {
|
|
406
571
|
const platform = (opts && opts.platform) || process.platform;
|
|
407
572
|
if (platform !== 'win32')
|
|
@@ -426,8 +591,13 @@ function resolveBashRunner(opts) {
|
|
|
426
591
|
}
|
|
427
592
|
return null;
|
|
428
593
|
}
|
|
429
|
-
|
|
430
|
-
|
|
594
|
+
// #3662 — recognize the two runtime-resolving command shapes the installer
|
|
595
|
+
// emits, so the rewriter never churns (or un-does) an entry that already
|
|
596
|
+
// works in every environment sharing the config root.
|
|
597
|
+
const CHAIN_RUNNER_COMMAND = /^"\$\(for n in [\s\S]*?printf '%s' "\$n" && break; done\)"\s+\S/;
|
|
598
|
+
const RESOLVER_RUNNER_COMMAND = /^(?:"[^"]*bash(\.exe)?"|bash)\s+"[^"]*gsd-node-runner\.sh"\s+"[^"]*"\s+\S/;
|
|
599
|
+
function rewriteLegacyManagedNodeHookCommands(settings, runnerToken, opts) {
|
|
600
|
+
if (!settings || !settings.hooks || !runnerToken)
|
|
431
601
|
return false;
|
|
432
602
|
if (!opts)
|
|
433
603
|
opts = {};
|
|
@@ -449,29 +619,31 @@ function rewriteLegacyManagedNodeHookCommands(settings, absoluteRunner, opts) {
|
|
|
449
619
|
if (hadPowerShellCallOperator) {
|
|
450
620
|
trimmed = trimmed.replace(/^&\s+/, '').trim();
|
|
451
621
|
}
|
|
622
|
+
if (CHAIN_RUNNER_COMMAND.test(trimmed) || RESOLVER_RUNNER_COMMAND.test(trimmed))
|
|
623
|
+
continue;
|
|
452
624
|
const m = trimmed.match(/^node\s+("([^"]+)"|'([^']+)'|(\S+))\s*$/) ||
|
|
453
625
|
trimmed.match(/^("([^"]+)"|'([^']+)'|(\S+))\s+("([^"]+)"|'([^']+)'|(\S+))\s*$/);
|
|
454
626
|
if (!m)
|
|
455
627
|
continue;
|
|
456
|
-
let
|
|
628
|
+
let scriptToken, scriptPath;
|
|
457
629
|
if (/^node\s+/.test(trimmed)) {
|
|
458
|
-
_runnerToken = 'node';
|
|
459
630
|
scriptToken = m[1];
|
|
460
631
|
scriptPath = m[2] || m[3] || m[4] || '';
|
|
461
632
|
}
|
|
462
633
|
else {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
634
|
+
// #3662: the pre-fix two-token shape baked an absolute node path at
|
|
635
|
+
// install time. A foreign-but-stable runner (valid in the
|
|
636
|
+
// environment that wrote it, absent here) used to be SKIPPED — the
|
|
637
|
+
// exact mechanism behind the mixed state where no environment can
|
|
638
|
+
// run all hooks. Every two-token managed entry now re-projects onto
|
|
639
|
+
// the runtime-resolving runner, whatever environment baked it.
|
|
468
640
|
scriptToken = m[5];
|
|
469
641
|
scriptPath = m[6] || m[7] || m[8] || '';
|
|
470
642
|
}
|
|
471
643
|
if (!isManagedHookBasename(scriptPath, { surface: 'settings-json' }))
|
|
472
644
|
continue;
|
|
473
645
|
const projectedCommand = projectLegacySettingsHookCommand({
|
|
474
|
-
|
|
646
|
+
runnerToken,
|
|
475
647
|
scriptPath,
|
|
476
648
|
scriptToken,
|
|
477
649
|
runtime: opts.runtime || 'generic',
|
|
@@ -488,6 +660,98 @@ function rewriteLegacyManagedNodeHookCommands(settings, absoluteRunner, opts) {
|
|
|
488
660
|
}
|
|
489
661
|
return changed;
|
|
490
662
|
}
|
|
663
|
+
// ---------------------------------------------------------------------------
|
|
664
|
+
// Shared: reconcileManagedShellHookCommands (#3329)
|
|
665
|
+
// ---------------------------------------------------------------------------
|
|
666
|
+
/**
|
|
667
|
+
* Rewrite already-registered managed `.sh` hook `command` strings to the shape
|
|
668
|
+
* the current installer would generate (#3329).
|
|
669
|
+
*
|
|
670
|
+
* applySettingsJsonHooks registers the four `.sh` managed hooks only-if-absent,
|
|
671
|
+
* so an entry registered by an older installer keeps its old command forever —
|
|
672
|
+
* `/gsd-update` (which re-invokes the installer) never re-derived it. On
|
|
673
|
+
* Claude/win32 that left the pre-#580/#3393 bash-runner-prefixed commands
|
|
674
|
+
* (`bash "<script>.sh"`, `"<git>/bash.exe" "<script>.sh"`) in place, spawning a
|
|
675
|
+
* nested bash on every hook fire. The #2979 rewriter above cannot help: it is
|
|
676
|
+
* Node-only by design (its basename gate contains only `.js` filenames).
|
|
677
|
+
*
|
|
678
|
+
* Scoping / safety:
|
|
679
|
+
* - Inert unless `shellHookOmitsBashRunner({ platform, runtime, isShellHook:
|
|
680
|
+
* true })` — the exact combination whose correct command shape changed. Where
|
|
681
|
+
* the bash runner is still correct (non-Windows, non-claude), nothing is
|
|
682
|
+
* rewritten, so the reconcile cannot churn unrelated installs.
|
|
683
|
+
* - Only entries whose parsed script token's basename exactly equals one of the
|
|
684
|
+
* expected managed `.sh` filenames are touched. A user hook would have to
|
|
685
|
+
* live at a path ending in exactly `gsd-session-state.sh` etc. — i.e. the
|
|
686
|
+
* GSD-installed file — to match. Extra-token commands (env prefixes, extra
|
|
687
|
+
* args) and args-form launcher entries (#976) never match the strict
|
|
688
|
+
* `[runner ]<script>` two-token shape and are left alone.
|
|
689
|
+
* - A null/empty expected command disables rewriting for that hook (a
|
|
690
|
+
* bash-runner-unavailable install must not have its entry nulled).
|
|
691
|
+
*
|
|
692
|
+
* @param settings settings.json-shaped object; mutated in place
|
|
693
|
+
* @param expected map of managed `.sh` filename → the command this install
|
|
694
|
+
* would register today (from buildHookCommand / buildLocalShellHookCommand)
|
|
695
|
+
* @param opts platform/runtime override (default: current process)
|
|
696
|
+
* @returns true when any command was rewritten
|
|
697
|
+
*/
|
|
698
|
+
function reconcileManagedShellHookCommands(settings, expected, opts) {
|
|
699
|
+
if (!settings || !settings.hooks || !expected)
|
|
700
|
+
return false;
|
|
701
|
+
if (!opts)
|
|
702
|
+
opts = {};
|
|
703
|
+
const platform = opts.platform || process.platform;
|
|
704
|
+
const runtime = opts.runtime || 'generic';
|
|
705
|
+
if (!shellHookOmitsBashRunner({ platform, runtime, isShellHook: true }))
|
|
706
|
+
return false;
|
|
707
|
+
const expectedByBasename = new Map();
|
|
708
|
+
for (const [hookFile, command] of Object.entries(expected)) {
|
|
709
|
+
if (typeof command === 'string' && command.length > 0) {
|
|
710
|
+
expectedByBasename.set(hookFile, command);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
if (expectedByBasename.size === 0)
|
|
714
|
+
return false;
|
|
715
|
+
let changed = false;
|
|
716
|
+
for (const entries of Object.values(settings.hooks)) {
|
|
717
|
+
if (!Array.isArray(entries))
|
|
718
|
+
continue;
|
|
719
|
+
for (const entry of entries) {
|
|
720
|
+
if (!entry || !Array.isArray(entry.hooks))
|
|
721
|
+
continue;
|
|
722
|
+
for (const h of entry.hooks) {
|
|
723
|
+
if (!h || typeof h.command !== 'string')
|
|
724
|
+
continue;
|
|
725
|
+
if (Array.isArray(h.args) && h.args.length > 0)
|
|
726
|
+
continue;
|
|
727
|
+
let trimmed = h.command.trim();
|
|
728
|
+
const hadPowerShellCallOperator = platform === 'win32' && /^&\s+/.test(trimmed);
|
|
729
|
+
if (hadPowerShellCallOperator) {
|
|
730
|
+
trimmed = trimmed.replace(/^&\s+/, '').trim();
|
|
731
|
+
}
|
|
732
|
+
// Strict `[runner ]<script>` shape: an optional single runner token
|
|
733
|
+
// (bare, 'single-quoted', or "double-quoted") followed by the script
|
|
734
|
+
// token. Anything else (env prefixes, extra flags, pipelines) does not
|
|
735
|
+
// match and is left untouched.
|
|
736
|
+
const m = trimmed.match(/^(?:(?:"([^"]+)"|'([^']+)'|(\S+))\s+)?(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/);
|
|
737
|
+
if (!m)
|
|
738
|
+
continue;
|
|
739
|
+
const scriptToken = m[4] || m[5] || m[6] || '';
|
|
740
|
+
if (!scriptToken)
|
|
741
|
+
continue;
|
|
742
|
+
const basename = shellCmdProjection.posixNormalize(scriptToken).split('/').pop() || '';
|
|
743
|
+
const expectedCommand = expectedByBasename.get(basename);
|
|
744
|
+
if (!expectedCommand)
|
|
745
|
+
continue;
|
|
746
|
+
if (h.command === expectedCommand)
|
|
747
|
+
continue;
|
|
748
|
+
h.command = expectedCommand;
|
|
749
|
+
changed = true;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
return changed;
|
|
754
|
+
}
|
|
491
755
|
function buildCodexHookBlock(targetDir, opts) {
|
|
492
756
|
const absoluteRunner = opts && opts.absoluteRunner;
|
|
493
757
|
if (!absoluteRunner)
|
|
@@ -768,27 +1032,75 @@ function buildHookCommand(configDir, hookName, opts) {
|
|
|
768
1032
|
}
|
|
769
1033
|
return JSON.stringify(shellCmdProjection.posixNormalize(configDir) + '/hooks/' + hookName);
|
|
770
1034
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
1035
|
+
// .sh hooks keep the pre-#3662 shape everywhere: the bash runner resolves
|
|
1036
|
+
// at install time like today, and `bash` itself is a PATH-stable binary
|
|
1037
|
+
// (the absolute Git-Bash discovery covers win32 — #580/#3393).
|
|
1038
|
+
if (isShellHook) {
|
|
1039
|
+
const runner = resolveBashRunner(opts);
|
|
1040
|
+
if (runner === null)
|
|
1041
|
+
return null;
|
|
1042
|
+
if (opts.portableHooks) {
|
|
1043
|
+
const portableBaseDir = projectPortableHookBaseDir({
|
|
1044
|
+
configDir,
|
|
1045
|
+
homeDir: node_os_1.default.homedir(),
|
|
1046
|
+
});
|
|
1047
|
+
return projectManagedHookCommand({
|
|
1048
|
+
absoluteRunner: runner,
|
|
1049
|
+
scriptPath: `${portableBaseDir}/hooks/${hookName}`,
|
|
1050
|
+
runtime: opts.runtime || 'generic',
|
|
1051
|
+
platform,
|
|
1052
|
+
hookShell,
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
const hooksPath = shellCmdProjection.posixNormalize(configDir) + '/hooks/' + hookName;
|
|
1056
|
+
return projectManagedHookCommand({
|
|
1057
|
+
absoluteRunner: runner,
|
|
1058
|
+
scriptPath: hooksPath,
|
|
1059
|
+
runtime,
|
|
1060
|
+
platform,
|
|
1061
|
+
hookShell,
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
// JS hooks (#3662): the node runner is resolved at hook-fire time, never
|
|
1065
|
+
// baked as a bare absolute path — an install-environment absolute path is
|
|
1066
|
+
// exactly what breaks with exit 127 when the config root is shared across
|
|
1067
|
+
// environments with different node layouts.
|
|
775
1068
|
if (opts.portableHooks) {
|
|
1069
|
+
// Portable installs route through the staged resolver: the baked absolute
|
|
1070
|
+
// path travels as the resolver's FIRST argument (tried first, so the
|
|
1071
|
+
// minimal-PATH guarantee holds), then `command -v node`, then the
|
|
1072
|
+
// well-known list — one staged file, no per-install templating. The
|
|
1073
|
+
// token is shell-escaped like the chain (the host shell expands the
|
|
1074
|
+
// argument before bash sees argv), not merely JSON-quoted.
|
|
1075
|
+
const bakedToken = buildBakedNodeToken(opts);
|
|
1076
|
+
if (bakedToken === null)
|
|
1077
|
+
return null;
|
|
776
1078
|
const portableBaseDir = projectPortableHookBaseDir({
|
|
777
1079
|
configDir,
|
|
778
1080
|
homeDir: node_os_1.default.homedir(),
|
|
779
1081
|
});
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
1082
|
+
// Absolute Git-Bash discovery on win32 when available (#580); `bash` on
|
|
1083
|
+
// PATH otherwise — the same assumption .sh hooks already make.
|
|
1084
|
+
const resolverRunner = resolveBashRunner(opts) || 'bash';
|
|
1085
|
+
return shellCmdProjection.projectShellCommandText({
|
|
1086
|
+
runnerToken: resolverRunner,
|
|
1087
|
+
argTokens: [
|
|
1088
|
+
JSON.stringify(`${portableBaseDir}/hooks/${NODE_RUNNER_RESOLVER_HOOK}`),
|
|
1089
|
+
bakedToken,
|
|
1090
|
+
JSON.stringify(`${portableBaseDir}/hooks/${hookName}`),
|
|
1091
|
+
],
|
|
1092
|
+
runtime,
|
|
784
1093
|
platform,
|
|
785
1094
|
hookShell,
|
|
786
1095
|
});
|
|
787
1096
|
}
|
|
1097
|
+
const chainRunner = buildNodeRunnerChainToken(opts);
|
|
1098
|
+
if (chainRunner === null)
|
|
1099
|
+
return null;
|
|
788
1100
|
const hooksPath = shellCmdProjection.posixNormalize(configDir) + '/hooks/' + hookName;
|
|
789
|
-
return
|
|
790
|
-
|
|
791
|
-
|
|
1101
|
+
return shellCmdProjection.projectShellCommandText({
|
|
1102
|
+
runnerToken: chainRunner,
|
|
1103
|
+
argTokens: [JSON.stringify(hooksPath)],
|
|
792
1104
|
runtime,
|
|
793
1105
|
platform,
|
|
794
1106
|
hookShell,
|
|
@@ -1024,28 +1336,39 @@ function writeCursorHooksJson(targetDir, src, opts) {
|
|
|
1024
1336
|
installedScripts.add(script);
|
|
1025
1337
|
}
|
|
1026
1338
|
}
|
|
1027
|
-
// Stage the hooks/lib/ helpers the staged scripts require (#2587)
|
|
1028
|
-
//
|
|
1029
|
-
//
|
|
1030
|
-
//
|
|
1031
|
-
//
|
|
1032
|
-
//
|
|
1339
|
+
// Stage the hooks/lib/ helpers the staged scripts require (#2587), TRANSITIVELY
|
|
1340
|
+
// (#3911 review): a lib helper can itself require a sibling under lib/ (e.g.
|
|
1341
|
+
// hooks/lib/hook-exit.js requires './cli-exit.js', which requires
|
|
1342
|
+
// './exit-code-registry.js') — a require with NO './lib/' prefix, because from
|
|
1343
|
+
// inside lib/ the sibling is already local. The original single-pass scan only
|
|
1344
|
+
// ever matched the "./lib/…" spelling used FROM a hook script, so it staged
|
|
1345
|
+
// hook-exit.js but never walked hook-exit.js's own requires, and an installed
|
|
1346
|
+
// Cursor hook wedged on MODULE_NOT_FOUND for './cli-exit.js' at load — before
|
|
1347
|
+
// its own try/catch. This walks a worklist: hook scripts seed it with their
|
|
1348
|
+
// "./lib/X" requires, and every lib file staged is itself scanned for further
|
|
1349
|
+
// "./lib/X" OR bare "./X" (sibling-within-lib) requires, so the requirement
|
|
1350
|
+
// graph is derived to a fixed point instead of one hand-tuned level deep.
|
|
1033
1351
|
const requiredLibFiles = new Set();
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
// a miss here surfaces as MODULE_NOT_FOUND at hook load, not at install.
|
|
1039
|
-
const re = /require\(\s*['"]\.\/lib\/([A-Za-z0-9._-]+)['"]\s*\)/g;
|
|
1352
|
+
const scannedLibFiles = new Set();
|
|
1353
|
+
const libRequireRe = /require\(\s*['"]\.\/(?:lib\/)?([A-Za-z0-9._-]+)['"]\s*\)/g;
|
|
1354
|
+
function scanForLibRequires(source) {
|
|
1355
|
+
libRequireRe.lastIndex = 0;
|
|
1040
1356
|
let m;
|
|
1041
|
-
while ((m =
|
|
1357
|
+
while ((m = libRequireRe.exec(source)) !== null)
|
|
1042
1358
|
requiredLibFiles.add(m[1]);
|
|
1043
1359
|
}
|
|
1360
|
+
for (const script of installedScripts) {
|
|
1361
|
+
scanForLibRequires(node_fs_1.default.readFileSync(node_path_1.default.join(hooksDir, script), 'utf8'));
|
|
1362
|
+
}
|
|
1044
1363
|
if (requiredLibFiles.size > 0) {
|
|
1045
1364
|
const srcLibDir = node_path_1.default.join(srcHooksDir, 'lib');
|
|
1046
1365
|
const destLibDir = node_path_1.default.join(hooksDir, 'lib');
|
|
1047
1366
|
node_fs_1.default.mkdirSync(destLibDir, { recursive: true });
|
|
1048
|
-
|
|
1367
|
+
// Iterate to a fixed point: staging a lib file can add MORE required lib
|
|
1368
|
+
// files (its own requires), which must themselves be staged and scanned.
|
|
1369
|
+
let libFile = [...requiredLibFiles].find((f) => !scannedLibFiles.has(f));
|
|
1370
|
+
while (libFile !== undefined) {
|
|
1371
|
+
scannedLibFiles.add(libFile);
|
|
1049
1372
|
const libSrc = node_path_1.default.join(srcLibDir, libFile);
|
|
1050
1373
|
if (!node_fs_1.default.existsSync(libSrc)) {
|
|
1051
1374
|
// FAIL LOUD. Skipping here would ship hook scripts whose top-level
|
|
@@ -1058,6 +1381,8 @@ function writeCursorHooksJson(targetDir, src, opts) {
|
|
|
1058
1381
|
let libContent = node_fs_1.default.readFileSync(libSrc, 'utf8');
|
|
1059
1382
|
libContent = libContent.replace(/gsd:/gi, 'gsd-');
|
|
1060
1383
|
node_fs_1.default.writeFileSync(node_path_1.default.join(destLibDir, libFile), libContent);
|
|
1384
|
+
scanForLibRequires(libContent);
|
|
1385
|
+
libFile = [...requiredLibFiles].find((f) => !scannedLibFiles.has(f));
|
|
1061
1386
|
}
|
|
1062
1387
|
}
|
|
1063
1388
|
// #2717: write the CommonJS marker into hooks/ alongside the staged .js
|
|
@@ -1764,6 +2089,22 @@ function applySettingsJsonHooks(settings, opts) {
|
|
|
1764
2089
|
else if (!hasPhaseBoundaryHook && !phaseBoundaryCommand) {
|
|
1765
2090
|
console.warn(` ${yellow}⚠${reset} Skipped phase boundary hook — Bash executable path unavailable (#3393)`);
|
|
1766
2091
|
}
|
|
2092
|
+
// #3329: the four `.sh` sites above register only-if-absent, so an entry
|
|
2093
|
+
// registered by an older installer keeps its old command forever —
|
|
2094
|
+
// /gsd-update (which re-invokes the installer) never re-derived it. On
|
|
2095
|
+
// Claude/win32 that left the pre-#580/#3393 bash-runner-prefixed commands
|
|
2096
|
+
// in settings.json indefinitely. Reconcile existing managed `.sh` entries
|
|
2097
|
+
// to the command this install would generate today. Inert wherever the
|
|
2098
|
+
// bash runner is still the correct shape; scoped to exact managed
|
|
2099
|
+
// basenames so user-authored hooks are never touched.
|
|
2100
|
+
if (reconcileManagedShellHookCommands(settings, {
|
|
2101
|
+
'gsd-validate-commit.sh': validateCommitCommand,
|
|
2102
|
+
'gsd-graphify-update.sh': graphifyUpdateCommand,
|
|
2103
|
+
'gsd-session-state.sh': sessionStateCommand,
|
|
2104
|
+
'gsd-phase-boundary.sh': phaseBoundaryCommand,
|
|
2105
|
+
}, { platform: hookOpts.platform, runtime })) {
|
|
2106
|
+
console.log(` ${green}✓${reset} Reconciled managed .sh hook commands to current format (#3329)`);
|
|
2107
|
+
}
|
|
1767
2108
|
// ── Extended hook events: SubagentStop / Stop / PreCompact / SubagentStart
|
|
1768
2109
|
// (#788 + #770 + #2092) ────────────────────────────────────────────────
|
|
1769
2110
|
// Claude Code (since #770) and Qwen Code (since #788) both support the
|
|
@@ -2195,9 +2536,12 @@ module.exports = {
|
|
|
2195
2536
|
applySettingsJsonHooks,
|
|
2196
2537
|
referencesHook,
|
|
2197
2538
|
rewriteLegacyManagedNodeHookCommands,
|
|
2539
|
+
reconcileManagedShellHookCommands,
|
|
2198
2540
|
normalizeNodePath,
|
|
2199
2541
|
resolveNodeRunner,
|
|
2542
|
+
buildNodeRunnerChainToken,
|
|
2200
2543
|
resolveBashRunner,
|
|
2544
|
+
NODE_RUNNER_RESOLVER_HOOK,
|
|
2201
2545
|
// Atomic write seam (shared with bin/install.js so all writes participate
|
|
2202
2546
|
// in install.js's _cleanTmpFiles() scoped temp-cleanup).
|
|
2203
2547
|
atomicWriteFileSync,
|