@opengsd/gsd-core 1.9.1 → 1.11.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.
Files changed (426) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -3
  3. package/.opencode/plugins/gsd-core.js +8 -1
  4. package/agents/gsd-code-fixer.md +27 -3
  5. package/agents/gsd-debug-session-manager.md +11 -0
  6. package/agents/gsd-debugger.md +12 -246
  7. package/agents/gsd-doc-synthesizer.md +2 -4
  8. package/agents/gsd-executor.md +12 -10
  9. package/agents/gsd-integration-checker.md +3 -0
  10. package/agents/gsd-mempalace-curator.md +5 -2
  11. package/agents/gsd-phase-researcher.md +20 -1
  12. package/agents/gsd-plan-checker.md +46 -0
  13. package/agents/gsd-planner.md +49 -54
  14. package/agents/gsd-roadmapper.md +21 -3
  15. package/agents/gsd-user-profiler.md +3 -0
  16. package/agents/gsd-verifier.md +26 -73
  17. package/bin/install.js +1272 -1238
  18. package/bin/lib/ui-safety-gate.cjs +2 -0
  19. package/commands/gsd/code-review.md +1 -1
  20. package/commands/gsd/execute-phase.md +1 -1
  21. package/commands/gsd/map-codebase.md +1 -1
  22. package/commands/gsd/mempalace-capture.md +2 -2
  23. package/commands/gsd/mempalace-recall.md +1 -1
  24. package/commands/gsd/new-milestone.md +2 -2
  25. package/commands/gsd/plan-phase.md +1 -1
  26. package/commands/gsd/quick.md +1 -1
  27. package/commands/gsd/review-backlog.md +2 -1
  28. package/commands/gsd/verify-work.md +1 -1
  29. package/gsd-core/bin/gsd-tools.cjs +1009 -115
  30. package/gsd-core/bin/lib/active-workstream-store.cjs +153 -12
  31. package/gsd-core/bin/lib/agent-install-check.cjs +268 -38
  32. package/gsd-core/bin/lib/api-coverage.cjs +123 -5
  33. package/gsd-core/bin/lib/artifacts.cjs +3 -0
  34. package/gsd-core/bin/lib/assumption-delta.cjs +2 -4
  35. package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
  36. package/gsd-core/bin/lib/audit.cjs +926 -202
  37. package/gsd-core/bin/lib/broken-windows.cjs +36 -6
  38. package/gsd-core/bin/lib/capability-consent.cjs +149 -15
  39. package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
  40. package/gsd-core/bin/lib/capability-registry.cjs +608 -148
  41. package/gsd-core/bin/lib/capability-source.cjs +92 -0
  42. package/gsd-core/bin/lib/capability-trust.cjs +444 -25
  43. package/gsd-core/bin/lib/capability-validator.cjs +507 -24
  44. package/gsd-core/bin/lib/capability-writer.cjs +3 -2
  45. package/gsd-core/bin/lib/check-command-router.cjs +114 -38
  46. package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
  47. package/gsd-core/bin/lib/codex-agent-toml.cjs +329 -0
  48. package/gsd-core/bin/lib/command-aliases.cjs +94 -0
  49. package/gsd-core/bin/lib/command-roster.cjs +44 -1
  50. package/gsd-core/bin/lib/commands.cjs +665 -99
  51. package/gsd-core/bin/lib/commonjs-marker.cjs +142 -0
  52. package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
  53. package/gsd-core/bin/lib/config-loader.cjs +76 -0
  54. package/gsd-core/bin/lib/config.cjs +22 -2
  55. package/gsd-core/bin/lib/context-composer.cjs +278 -0
  56. package/gsd-core/bin/lib/context-predicates.cjs +506 -0
  57. package/gsd-core/bin/lib/core-utils.cjs +217 -40
  58. package/gsd-core/bin/lib/decisions.cjs +23 -0
  59. package/gsd-core/bin/lib/docs.cjs +3 -2
  60. package/gsd-core/bin/lib/external-job.cjs +19 -4
  61. package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
  62. package/gsd-core/bin/lib/frontmatter.cjs +239 -32
  63. package/gsd-core/bin/lib/gap-checker.cjs +68 -7
  64. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +57 -6
  65. package/gsd-core/bin/lib/git-base-branch.cjs +160 -15
  66. package/gsd-core/bin/lib/graphify.cjs +142 -27
  67. package/gsd-core/bin/lib/gsd2-import.cjs +37 -5
  68. package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
  69. package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
  70. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +145 -0
  71. package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
  72. package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
  73. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
  74. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +265 -0
  75. package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
  76. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
  77. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +173 -0
  78. package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
  79. package/gsd-core/bin/lib/health-diagnostic.cjs +431 -0
  80. package/gsd-core/bin/lib/host-integration.cjs +13 -1
  81. package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
  82. package/gsd-core/bin/lib/init-command-router.cjs +83 -8
  83. package/gsd-core/bin/lib/init.cjs +1325 -169
  84. package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
  85. package/gsd-core/bin/lib/install-engine.cjs +805 -264
  86. package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
  87. package/gsd-core/bin/lib/install-model-override-resolver.cjs +203 -0
  88. package/gsd-core/bin/lib/install-profiles.cjs +160 -57
  89. package/gsd-core/bin/lib/install-scope.cjs +270 -0
  90. package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
  91. package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
  92. package/gsd-core/bin/lib/installer-migration-authoring.cjs +3 -1
  93. package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
  94. package/gsd-core/bin/lib/installer-migrations/007-retire-config-root-commonjs-marker.cjs +149 -0
  95. package/gsd-core/bin/lib/installer-migrations/008-cursor-retire-commands-surface.cjs +55 -0
  96. package/gsd-core/bin/lib/installer-migrations/009-pi-retire-reserved-hooks-dir.cjs +199 -0
  97. package/gsd-core/bin/lib/installer-migrations.cjs +206 -13
  98. package/gsd-core/bin/lib/io.cjs +38 -3
  99. package/gsd-core/bin/lib/markdown-sectionizer.cjs +8 -1
  100. package/gsd-core/bin/lib/markdown-table.cjs +133 -20
  101. package/gsd-core/bin/lib/mcp-catalog.cjs +518 -0
  102. package/gsd-core/bin/lib/mcp-server.cjs +135 -3
  103. package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
  104. package/gsd-core/bin/lib/milestone.cjs +821 -109
  105. package/gsd-core/bin/lib/model-catalog.cjs +59 -1
  106. package/gsd-core/bin/lib/model-resolver.cjs +183 -40
  107. package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
  108. package/gsd-core/bin/lib/pattern.cjs +122 -0
  109. package/gsd-core/bin/lib/phase-estimation.cjs +1 -1
  110. package/gsd-core/bin/lib/phase-id.cjs +507 -36
  111. package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
  112. package/gsd-core/bin/lib/phase-locator.cjs +258 -58
  113. package/gsd-core/bin/lib/phase.cjs +891 -156
  114. package/gsd-core/bin/lib/plan-dependency-graph.cjs +303 -0
  115. package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
  116. package/gsd-core/bin/lib/plan-scan.cjs +86 -2
  117. package/gsd-core/bin/lib/planning-scope.cjs +31 -0
  118. package/gsd-core/bin/lib/planning-snapshot.cjs +890 -0
  119. package/gsd-core/bin/lib/planning-workspace.cjs +60 -6
  120. package/gsd-core/bin/lib/probe-core.cjs +1 -1
  121. package/gsd-core/bin/lib/profile-output.cjs +1 -1
  122. package/gsd-core/bin/lib/prompt-budget.cjs +128 -165
  123. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +740 -0
  124. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +85 -0
  125. package/gsd-core/bin/lib/review-lane-descriptor.cjs +108 -0
  126. package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
  127. package/gsd-core/bin/lib/review-lane-runner.cjs +447 -68
  128. package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
  129. package/gsd-core/bin/lib/roadmap-command-router.cjs +76 -9
  130. package/gsd-core/bin/lib/roadmap-parser.cjs +1035 -194
  131. package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
  132. package/gsd-core/bin/lib/roadmap.cjs +405 -84
  133. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +795 -100
  134. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
  135. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +440 -57
  136. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
  137. package/gsd-core/bin/lib/runtime-homes.cjs +220 -41
  138. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +220 -44
  139. package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
  140. package/gsd-core/bin/lib/runtime-slash.cjs +27 -9
  141. package/gsd-core/bin/lib/section-manifest.cjs +209 -0
  142. package/gsd-core/bin/lib/security.cjs +104 -5
  143. package/gsd-core/bin/lib/shell-command-projection.cjs +388 -30
  144. package/gsd-core/bin/lib/smart-entry.cjs +154 -22
  145. package/gsd-core/bin/lib/state-command-router.cjs +5 -1
  146. package/gsd-core/bin/lib/state-document.cjs +152 -8
  147. package/gsd-core/bin/lib/state-transition.cjs +424 -105
  148. package/gsd-core/bin/lib/state.cjs +1927 -401
  149. package/gsd-core/bin/lib/surface.cjs +35 -10
  150. package/gsd-core/bin/lib/text-lines.cjs +80 -0
  151. package/gsd-core/bin/lib/token-scanner.cjs +76 -0
  152. package/gsd-core/bin/lib/uat-predicate.cjs +20 -4
  153. package/gsd-core/bin/lib/uat.cjs +706 -64
  154. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
  155. package/gsd-core/bin/lib/ui-safety-gate.cjs +14 -5
  156. package/gsd-core/bin/lib/unusable-input.cjs +33 -0
  157. package/gsd-core/bin/lib/update-context.cjs +8 -2
  158. package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
  159. package/gsd-core/bin/lib/validate.cjs +20 -6
  160. package/gsd-core/bin/lib/vendor/README.md +37 -0
  161. package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
  162. package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
  163. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  164. package/gsd-core/bin/lib/verification.cjs +287 -20
  165. package/gsd-core/bin/lib/verify.cjs +368 -880
  166. package/gsd-core/bin/lib/workflow-fragments.cjs +557 -0
  167. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +203 -19
  168. package/gsd-core/bin/lib/workstream-inventory.cjs +576 -31
  169. package/gsd-core/bin/lib/workstream.cjs +8 -2
  170. package/gsd-core/bin/lib/worktree-base-ref.cjs +50 -6
  171. package/gsd-core/bin/lib/worktree-safety.cjs +450 -125
  172. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  173. package/gsd-core/bin/shared/config-schema.manifest.json +9 -1
  174. package/gsd-core/references/agent-contracts.md +43 -26
  175. package/gsd-core/references/artifact-types.md +10 -3
  176. package/gsd-core/references/autonomous-ui-design-contract.md +42 -0
  177. package/gsd-core/references/checkpoints.md +2 -2
  178. package/gsd-core/references/context-budget.md +1 -1
  179. package/gsd-core/references/debugger-techniques.md +255 -0
  180. package/gsd-core/references/dispatch-isolation-gate.md +138 -0
  181. package/gsd-core/references/doc-conflict-engine.md +1 -1
  182. package/gsd-core/references/execute-mvp-tdd.md +3 -3
  183. package/gsd-core/references/execute-phase-between-wave-reset.md +6 -2
  184. package/gsd-core/references/execute-phase-context-guard.md +1 -1
  185. package/gsd-core/references/execute-phase-response-language.md +1 -1
  186. package/gsd-core/references/execute-phase-wave-guard.md +6 -2
  187. package/gsd-core/references/gate-prompts.md +1 -1
  188. package/gsd-core/references/git-planning-commit.md +2 -1
  189. package/gsd-core/references/loop-hook-dispatch.md +39 -2
  190. package/gsd-core/references/model-profiles.md +12 -4
  191. package/gsd-core/references/mvp-concepts.md +9 -9
  192. package/gsd-core/references/planner-guidance.md +3 -9
  193. package/gsd-core/references/planner-preconditions.md +1 -1
  194. package/gsd-core/references/planner-reviews.md +1 -1
  195. package/gsd-core/references/planning-config.md +8 -6
  196. package/gsd-core/references/research-documentation-lookup.md +5 -3
  197. package/gsd-core/references/revision-loop.md +1 -1
  198. package/gsd-core/references/specless-probe-fallback.md +8 -7
  199. package/gsd-core/references/universal-anti-patterns.md +3 -3
  200. package/gsd-core/references/verifier-phase-gates.md +192 -0
  201. package/gsd-core/references/verifier-wiring-patterns.md +100 -0
  202. package/gsd-core/references/verify-mvp-mode.md +1 -1
  203. package/gsd-core/references/workstream-flag.md +22 -6
  204. package/gsd-core/references/worktree-branch-check.md +2 -2
  205. package/gsd-core/templates/discussion-log.md +1 -1
  206. package/gsd-core/templates/phase-prompt.md +2 -4
  207. package/gsd-core/templates/state.md +4 -4
  208. package/gsd-core/templates/summary-complex.md +2 -0
  209. package/gsd-core/templates/summary-minimal.md +2 -0
  210. package/gsd-core/templates/summary-standard.md +2 -0
  211. package/gsd-core/templates/summary.md +2 -0
  212. package/gsd-core/templates/verification-report.md +9 -1
  213. package/gsd-core/workflows/ai-integration-phase.md +9 -11
  214. package/gsd-core/workflows/audit-milestone.md +3 -0
  215. package/gsd-core/workflows/autonomous/steps/converge-banner.md +1 -0
  216. package/gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md +11 -0
  217. package/gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md +7 -0
  218. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +21 -0
  219. package/gsd-core/workflows/autonomous/steps/converge-loop.md +7 -0
  220. package/gsd-core/workflows/autonomous.md +33 -70
  221. package/gsd-core/workflows/cleanup.md +62 -3
  222. package/gsd-core/workflows/code-review/steps/dispatch-fix.md +39 -0
  223. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +93 -0
  224. package/gsd-core/workflows/code-review-fix.md +37 -10
  225. package/gsd-core/workflows/code-review.md +74 -166
  226. package/gsd-core/workflows/complete-milestone/steps/git-tag.md +29 -0
  227. package/gsd-core/workflows/complete-milestone.md +160 -95
  228. package/gsd-core/workflows/debug.md +16 -17
  229. package/gsd-core/workflows/diagnose-issues.md +56 -8
  230. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -1
  231. package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
  232. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +15 -0
  233. package/gsd-core/workflows/discuss-phase-assumptions.md +7 -17
  234. package/gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md +51 -0
  235. package/gsd-core/workflows/docs-update.md +8 -51
  236. package/gsd-core/workflows/edit-phase.md +26 -1
  237. package/gsd-core/workflows/eval-review.md +3 -5
  238. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +64 -7
  239. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +50 -0
  240. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +31 -0
  241. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
  242. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +21 -0
  243. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +42 -0
  244. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +43 -37
  245. package/gsd-core/workflows/execute-phase.md +103 -187
  246. package/gsd-core/workflows/execute-plan.md +36 -4
  247. package/gsd-core/workflows/explore.md +131 -4
  248. package/gsd-core/workflows/fast.md +10 -2
  249. package/gsd-core/workflows/health.md +73 -4
  250. package/gsd-core/workflows/help/modes/full.md +6 -1
  251. package/gsd-core/workflows/import.md +4 -4
  252. package/gsd-core/workflows/ingest-docs.md +7 -6
  253. package/gsd-core/workflows/mvp-phase.md +6 -3
  254. package/gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md +16 -0
  255. package/gsd-core/workflows/new-milestone/steps/reset-phase-safety.md +19 -0
  256. package/gsd-core/workflows/new-milestone.md +35 -47
  257. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +176 -0
  258. package/gsd-core/workflows/new-project/steps/auto-mode-detection.md +32 -0
  259. package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +18 -0
  260. package/gsd-core/workflows/new-project.md +27 -240
  261. package/gsd-core/workflows/next.md +12 -0
  262. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +15 -0
  263. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +110 -0
  264. package/gsd-core/workflows/plan-phase/steps/prd-express-gate.md +8 -0
  265. package/gsd-core/workflows/plan-phase/steps/research-only-early-exit.md +17 -0
  266. package/gsd-core/workflows/plan-phase/steps/research-only-modifiers.md +16 -0
  267. package/gsd-core/workflows/plan-phase/steps/reviews-prerequisite.md +17 -0
  268. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +149 -0
  269. package/gsd-core/workflows/plan-phase.md +89 -209
  270. package/gsd-core/workflows/plan-review-convergence.md +50 -2
  271. package/gsd-core/workflows/progress/steps/forensic-audit.md +125 -0
  272. package/gsd-core/workflows/progress/steps/mvp-display.md +18 -0
  273. package/gsd-core/workflows/progress.md +45 -159
  274. package/gsd-core/workflows/quick/steps/discussion-phase.md +124 -0
  275. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +111 -0
  276. package/gsd-core/workflows/quick/steps/quick-verification.md +67 -0
  277. package/gsd-core/workflows/quick/steps/research-phase.md +72 -0
  278. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +37 -0
  279. package/gsd-core/workflows/quick.md +55 -405
  280. package/gsd-core/workflows/resume-project.md +3 -0
  281. package/gsd-core/workflows/review/steps/reviewer-instances-note-1.md +4 -0
  282. package/gsd-core/workflows/review/steps/reviewer-instances-note-2.md +3 -0
  283. package/gsd-core/workflows/review.md +41 -13
  284. package/gsd-core/workflows/section-manifest.json +219 -0
  285. package/gsd-core/workflows/secure-phase.md +1 -1
  286. package/gsd-core/workflows/session-report.md +2 -1
  287. package/gsd-core/workflows/settings.md +66 -2
  288. package/gsd-core/workflows/ship.md +104 -44
  289. package/gsd-core/workflows/sketch.md +1 -1
  290. package/gsd-core/workflows/spec-phase.md +41 -20
  291. package/gsd-core/workflows/spike-wrap-up.md +20 -5
  292. package/gsd-core/workflows/spike.md +50 -16
  293. package/gsd-core/workflows/sync-skills.md +106 -13
  294. package/gsd-core/workflows/transition/steps/workstream-collision-check.md +17 -0
  295. package/gsd-core/workflows/transition.md +53 -31
  296. package/gsd-core/workflows/ui-phase.md +13 -12
  297. package/gsd-core/workflows/ui-review.md +2 -2
  298. package/gsd-core/workflows/update/steps/channel-banner.md +7 -0
  299. package/gsd-core/workflows/update.md +19 -8
  300. package/gsd-core/workflows/validate-phase.md +1 -1
  301. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +36 -0
  302. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +21 -0
  303. package/gsd-core/workflows/verify-work.md +17 -65
  304. package/hooks/dist/gsd-agent-isolation-guard.js +517 -0
  305. package/hooks/dist/gsd-check-update-worker.js +64 -12
  306. package/hooks/dist/gsd-check-update.js +19 -1
  307. package/hooks/dist/gsd-cursor-pre-tool.js +0 -3
  308. package/hooks/dist/gsd-cursor-subagent-start.js +607 -26
  309. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -2
  310. package/hooks/dist/gsd-prompt-guard.js +21 -20
  311. package/hooks/dist/gsd-read-injection-scanner.js +45 -24
  312. package/hooks/dist/gsd-statusline.js +90 -6
  313. package/hooks/dist/gsd-update-banner.js +22 -1
  314. package/hooks/dist/gsd-workflow-guard.js +134 -36
  315. package/hooks/dist/gsd-worktree-path-guard.js +2 -1
  316. package/hooks/dist/gsd-write-guard.js +359 -0
  317. package/hooks/dist/lib/git-cmd.js +92 -59
  318. package/hooks/dist/lib/injection-patterns.js +45 -0
  319. package/hooks/dist/lib/isolation-deny-reason.js +39 -0
  320. package/hooks/dist/lib/isolation-sentinel.js +277 -0
  321. package/hooks/dist/managed-hooks-registry.cjs +2 -0
  322. package/hooks/gsd-agent-isolation-guard.js +517 -0
  323. package/hooks/gsd-check-update-worker.js +64 -12
  324. package/hooks/gsd-check-update.js +19 -1
  325. package/hooks/gsd-cursor-pre-tool.js +0 -3
  326. package/hooks/gsd-cursor-subagent-start.js +607 -26
  327. package/hooks/gsd-cursor-subagent-stop.js +3 -2
  328. package/hooks/gsd-prompt-guard.js +21 -20
  329. package/hooks/gsd-read-injection-scanner.js +45 -24
  330. package/hooks/gsd-statusline.js +90 -6
  331. package/hooks/gsd-update-banner.js +22 -1
  332. package/hooks/gsd-workflow-guard.js +134 -36
  333. package/hooks/gsd-worktree-path-guard.js +2 -1
  334. package/hooks/gsd-write-guard.js +359 -0
  335. package/hooks/hooks.json +12 -0
  336. package/hooks/lib/git-cmd.js +92 -59
  337. package/hooks/lib/injection-patterns.js +45 -0
  338. package/hooks/lib/isolation-deny-reason.js +39 -0
  339. package/hooks/lib/isolation-sentinel.js +277 -0
  340. package/hooks/managed-hooks-registry.cjs +2 -0
  341. package/package.json +31 -10
  342. package/pi/gsd.cjs +71 -12
  343. package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
  344. package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
  345. package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
  346. package/scripts/build-hooks.js +9 -0
  347. package/scripts/changeset/lint.cjs +68 -6
  348. package/scripts/changeset/serialize.cjs +5 -1
  349. package/scripts/check-alias-drift.cjs +7 -43
  350. package/scripts/check-contract-drift.cjs +297 -0
  351. package/scripts/ci-test-scope.cjs +19 -2
  352. package/scripts/command-contract-helpers.cjs +903 -1
  353. package/scripts/gen-adr-index.cjs +728 -38
  354. package/scripts/gen-capability-matrix.cjs +1 -1
  355. package/scripts/gen-capability-registry.cjs +3 -15
  356. package/scripts/gen-context-index.cjs +439 -0
  357. package/scripts/gen-health-docs.cjs +390 -0
  358. package/scripts/gen-inventory-manifest.cjs +150 -4
  359. package/scripts/gen-loop-host-contract.cjs +4 -24
  360. package/scripts/gen-prompt-budget-parity-corpus.cjs +645 -0
  361. package/scripts/gen-registry.cjs +3 -14
  362. package/scripts/gen-section-manifest.cjs +638 -0
  363. package/scripts/generate-package-identity.cjs +4 -2
  364. package/scripts/lib/alias-drift-families.cjs +46 -0
  365. package/scripts/lib/drift-scan.cjs +278 -0
  366. package/scripts/lint-allow-test-rule-refs.allowlist.json +15 -54
  367. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
  368. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
  369. package/scripts/lint-canary-version-leak.cjs +73 -0
  370. package/scripts/lint-command-contract.cjs +96 -13
  371. package/scripts/lint-compiled-artifact-sync.cjs +6 -1
  372. package/scripts/lint-completion-predicate-drift.cjs +933 -0
  373. package/scripts/lint-completion-ratio-drift.cjs +214 -0
  374. package/scripts/lint-default-flip-documentation.cjs +193 -0
  375. package/scripts/lint-docs-command-form.cjs +195 -0
  376. package/scripts/lint-docs-required.cjs +9 -1
  377. package/scripts/lint-emitted-drift-ack.cjs +215 -20
  378. package/scripts/lint-eslint-glob-coverage.allowlist.json +34 -0
  379. package/scripts/lint-eslint-glob-coverage.cjs +340 -0
  380. package/scripts/lint-example-parser-parity.cjs +395 -0
  381. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
  382. package/scripts/lint-health-diagnostic-rule-table.cjs +404 -0
  383. package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
  384. package/scripts/lint-milestone-window-drift.cjs +468 -0
  385. package/scripts/lint-phase-enumeration-drift.cjs +479 -0
  386. package/scripts/lint-plan-count-drift.cjs +318 -0
  387. package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
  388. package/scripts/lint-planning-prompt-drift.cjs +434 -0
  389. package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
  390. package/scripts/lint-regression-test-names.cjs +15 -13
  391. package/scripts/lint-removed-but-needed.cjs +320 -0
  392. package/scripts/lint-state-field-drift.cjs +805 -0
  393. package/scripts/lint-state-write-path-drift.cjs +1045 -0
  394. package/scripts/lint-test-file-count.allowlist.json +40 -3
  395. package/scripts/lint-unreachable-guard-drift.cjs +843 -0
  396. package/scripts/lint-vendored-deps.cjs +124 -0
  397. package/scripts/mutation-matrix.cjs +13 -0
  398. package/scripts/pr-changed-files.cjs +63 -0
  399. package/scripts/pr-template-policy.cjs +14 -4
  400. package/scripts/prompt-injection-scan.sh +52 -6
  401. package/scripts/require-issue-link-policy.cjs +192 -0
  402. package/scripts/state-write-path-drift-baseline.json +19 -0
  403. package/scripts/sync-runtime-launcher.cjs +2 -4
  404. package/skills/gsd-autonomous/SKILL.md +0 -1
  405. package/skills/gsd-code-review/SKILL.md +1 -1
  406. package/skills/gsd-execute-phase/SKILL.md +1 -2
  407. package/skills/gsd-map-codebase/SKILL.md +1 -1
  408. package/skills/gsd-mempalace-capture/SKILL.md +2 -2
  409. package/skills/gsd-mempalace-recall/SKILL.md +1 -1
  410. package/skills/gsd-new-milestone/SKILL.md +2 -2
  411. package/skills/gsd-next/SKILL.md +0 -1
  412. package/skills/gsd-plan-phase/SKILL.md +1 -2
  413. package/skills/gsd-progress/SKILL.md +0 -1
  414. package/skills/gsd-quick/SKILL.md +1 -1
  415. package/skills/gsd-review-backlog/SKILL.md +2 -1
  416. package/skills/gsd-stats/SKILL.md +0 -1
  417. package/skills/gsd-verify-work/SKILL.md +1 -1
  418. package/vscode/package.json +1 -1
  419. package/gsd-core/workflows/discovery-phase.md +0 -298
  420. package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
  421. package/gsd-core/workflows/verify-phase.md +0 -577
  422. package/scripts/affected-tests-lib.cjs +0 -554
  423. package/scripts/gen-emitted-baseline.cjs +0 -145
  424. package/scripts/lint-allow-test-rule-refs.cjs +0 -162
  425. package/scripts/run-affected-tests.cjs +0 -7
  426. package/scripts/run-tests.cjs +0 -1050
@@ -21,13 +21,20 @@ 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-exports every symbol from this module so existing
25
- * consumers that do require('../bin/install.js').writeCursorHooksJson
26
- * (etc.) continue to work unchanged.
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"));
30
33
  const node_os_1 = __importDefault(require("node:os"));
34
+ // #2544: the single source of truth for the CommonJS module-type marker. The
35
+ // two helpers below are thin boolean-returning shims over these — see the
36
+ // marker section for why this file no longer carries its own copy.
37
+ const commonjs_marker_cjs_1 = require("./commonjs-marker.cjs");
31
38
  const imperative_hook_bus_cjs_1 = require("./host-integration-adapters/imperative-hook-bus.cjs");
32
39
  // eslint-disable-next-line @typescript-eslint/no-require-imports
33
40
  const shellCmdProjection = require("./shell-command-projection.cjs");
@@ -166,62 +173,51 @@ function atomicWriteFileSync(target, data, options) {
166
173
  //
167
174
  // The marker content is byte-identical to installSharedHooksBundle's
168
175
  // (bin/install.js installSharedHooksBundle): {"type":"commonjs"}\n.
176
+ //
177
+ // #2544: the two helpers below no longer carry their own copy of the write and
178
+ // remove rules — they DELEGATE to src/commonjs-marker.cts, which #2544 makes the
179
+ // single place both rules are enforced. Keeping a second copy here was not
180
+ // merely redundant; the copies had drifted apart on exactly the two properties
181
+ // that matter:
182
+ //
183
+ // - ownership probe: `fs.existsSync` FOLLOWS symlinks and reports `false` for
184
+ // a DANGLING one, so a dangling `package.json` symlink classified as absent
185
+ // and the write below followed the link outside the directory GSD owns.
186
+ // `classifyMarker` uses `lstat` + `isFile()`, so a symlink or a directory at
187
+ // the marker path is classified `foreign` and left strictly alone.
188
+ // - create: a plain `writeFileSync` leaves the classify->write window open.
189
+ // `ensureCommonJsMarker` creates with `flag: 'wx'` (O_EXCL), so anything
190
+ // that appears at the path in between fails with EEXIST instead of being
191
+ // followed or overwritten.
192
+ //
193
+ // The exported signatures are unchanged (both still return a boolean), so every
194
+ // caller and the #2717 tests are unaffected.
169
195
  // ---------------------------------------------------------------------------
170
- /** The exact marker content GSD writes, matching installSharedHooksBundle. */
171
- const COMMONJS_MARKER_CONTENT = '{"type":"commonjs"}\n';
172
196
  /**
173
197
  * Ensure a `package.json` forcing CommonJS mode exists in `dir` (the directory
174
198
  * holding GSD-staged `.js` hook scripts). Idempotent: a no-op if the marker is
175
- * already present with GSD's content. Overwrites only when the file is absent
176
- * or already carries GSD's exact marker — it never clobbers a distinct
177
- * user-authored package.json (it leaves such a file in place; the user owns it).
199
+ * already present with GSD's content. Never clobbers a distinct user-authored
200
+ * package.json (it leaves such a file in place; the user owns it).
178
201
  *
179
202
  * @param dir - absolute path to the directory holding the staged .js hooks
180
- * @returns `true` if the marker is present after the call (written or already there)
203
+ * @returns `true` if GSD's marker is present after the call (written or already there)
181
204
  */
182
205
  function ensureCommonJsMarker(dir) {
183
- const markerPath = node_path_1.default.join(dir, 'package.json');
184
- try {
185
- if (node_fs_1.default.existsSync(markerPath)) {
186
- const existing = node_fs_1.default.readFileSync(markerPath, 'utf8');
187
- // Already GSD's marker (tolerant of trailing-whitespace variants) — done.
188
- if (existing.trim() === '{"type":"commonjs"}')
189
- return true;
190
- // A distinct package.json the user owns — do NOT clobber. The hook will
191
- // load as whatever type the user declared; that is the user's choice.
192
- return false;
193
- }
194
- node_fs_1.default.writeFileSync(markerPath, COMMONJS_MARKER_CONTENT);
195
- return true;
196
- }
197
- catch {
198
- // Best-effort: a marker write failure must not fail the whole install.
199
- return false;
200
- }
206
+ // 'written' | 'unchanged' -> the marker is ours and present.
207
+ // 'preserved-foreign' -> a file GSD does not own is there; left untouched.
208
+ // 'failed' -> environmental (EACCES/EROFS/ENOSPC); best-effort.
209
+ const outcome = (0, commonjs_marker_cjs_1.ensureCommonJsMarker)(dir);
210
+ return outcome === 'written' || outcome === 'unchanged';
201
211
  }
202
212
  /**
203
213
  * Remove the CommonJS marker from `dir` on uninstall — but ONLY if it carries
204
214
  * GSD's exact marker content. A user-authored package.json is never deleted.
205
- * Mirrors the kimi uninstall guard in bin/install.js.
206
215
  *
207
216
  * @param dir - absolute path to the directory that held the staged .js hooks
208
217
  * @returns `true` if a GSD-owned marker was removed
209
218
  */
210
219
  function removeCommonJsMarkerIfGsdOwned(dir) {
211
- const markerPath = node_path_1.default.join(dir, 'package.json');
212
- try {
213
- if (!node_fs_1.default.existsSync(markerPath))
214
- return false;
215
- const content = node_fs_1.default.readFileSync(markerPath, 'utf8').trim();
216
- if (content === '{"type":"commonjs"}') {
217
- node_fs_1.default.unlinkSync(markerPath);
218
- return true;
219
- }
220
- return false;
221
- }
222
- catch {
223
- return false;
224
- }
220
+ return (0, commonjs_marker_cjs_1.removeCommonJsMarker)(dir);
225
221
  }
226
222
  // ---------------------------------------------------------------------------
227
223
  // parseTomlValue + findMultilineBasicStringClose
@@ -495,6 +491,98 @@ function rewriteLegacyManagedNodeHookCommands(settings, absoluteRunner, opts) {
495
491
  }
496
492
  return changed;
497
493
  }
494
+ // ---------------------------------------------------------------------------
495
+ // Shared: reconcileManagedShellHookCommands (#3329)
496
+ // ---------------------------------------------------------------------------
497
+ /**
498
+ * Rewrite already-registered managed `.sh` hook `command` strings to the shape
499
+ * the current installer would generate (#3329).
500
+ *
501
+ * applySettingsJsonHooks registers the four `.sh` managed hooks only-if-absent,
502
+ * so an entry registered by an older installer keeps its old command forever —
503
+ * `/gsd-update` (which re-invokes the installer) never re-derived it. On
504
+ * Claude/win32 that left the pre-#580/#3393 bash-runner-prefixed commands
505
+ * (`bash "<script>.sh"`, `"<git>/bash.exe" "<script>.sh"`) in place, spawning a
506
+ * nested bash on every hook fire. The #2979 rewriter above cannot help: it is
507
+ * Node-only by design (its basename gate contains only `.js` filenames).
508
+ *
509
+ * Scoping / safety:
510
+ * - Inert unless `shellHookOmitsBashRunner({ platform, runtime, isShellHook:
511
+ * true })` — the exact combination whose correct command shape changed. Where
512
+ * the bash runner is still correct (non-Windows, non-claude), nothing is
513
+ * rewritten, so the reconcile cannot churn unrelated installs.
514
+ * - Only entries whose parsed script token's basename exactly equals one of the
515
+ * expected managed `.sh` filenames are touched. A user hook would have to
516
+ * live at a path ending in exactly `gsd-session-state.sh` etc. — i.e. the
517
+ * GSD-installed file — to match. Extra-token commands (env prefixes, extra
518
+ * args) and args-form launcher entries (#976) never match the strict
519
+ * `[runner ]<script>` two-token shape and are left alone.
520
+ * - A null/empty expected command disables rewriting for that hook (a
521
+ * bash-runner-unavailable install must not have its entry nulled).
522
+ *
523
+ * @param settings settings.json-shaped object; mutated in place
524
+ * @param expected map of managed `.sh` filename → the command this install
525
+ * would register today (from buildHookCommand / buildLocalShellHookCommand)
526
+ * @param opts platform/runtime override (default: current process)
527
+ * @returns true when any command was rewritten
528
+ */
529
+ function reconcileManagedShellHookCommands(settings, expected, opts) {
530
+ if (!settings || !settings.hooks || !expected)
531
+ return false;
532
+ if (!opts)
533
+ opts = {};
534
+ const platform = opts.platform || process.platform;
535
+ const runtime = opts.runtime || 'generic';
536
+ if (!shellHookOmitsBashRunner({ platform, runtime, isShellHook: true }))
537
+ return false;
538
+ const expectedByBasename = new Map();
539
+ for (const [hookFile, command] of Object.entries(expected)) {
540
+ if (typeof command === 'string' && command.length > 0) {
541
+ expectedByBasename.set(hookFile, command);
542
+ }
543
+ }
544
+ if (expectedByBasename.size === 0)
545
+ return false;
546
+ let changed = false;
547
+ for (const entries of Object.values(settings.hooks)) {
548
+ if (!Array.isArray(entries))
549
+ continue;
550
+ for (const entry of entries) {
551
+ if (!entry || !Array.isArray(entry.hooks))
552
+ continue;
553
+ for (const h of entry.hooks) {
554
+ if (!h || typeof h.command !== 'string')
555
+ continue;
556
+ if (Array.isArray(h.args) && h.args.length > 0)
557
+ continue;
558
+ let trimmed = h.command.trim();
559
+ const hadPowerShellCallOperator = platform === 'win32' && /^&\s+/.test(trimmed);
560
+ if (hadPowerShellCallOperator) {
561
+ trimmed = trimmed.replace(/^&\s+/, '').trim();
562
+ }
563
+ // Strict `[runner ]<script>` shape: an optional single runner token
564
+ // (bare, 'single-quoted', or "double-quoted") followed by the script
565
+ // token. Anything else (env prefixes, extra flags, pipelines) does not
566
+ // match and is left untouched.
567
+ const m = trimmed.match(/^(?:(?:"([^"]+)"|'([^']+)'|(\S+))\s+)?(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/);
568
+ if (!m)
569
+ continue;
570
+ const scriptToken = m[4] || m[5] || m[6] || '';
571
+ if (!scriptToken)
572
+ continue;
573
+ const basename = shellCmdProjection.posixNormalize(scriptToken).split('/').pop() || '';
574
+ const expectedCommand = expectedByBasename.get(basename);
575
+ if (!expectedCommand)
576
+ continue;
577
+ if (h.command === expectedCommand)
578
+ continue;
579
+ h.command = expectedCommand;
580
+ changed = true;
581
+ }
582
+ }
583
+ }
584
+ return changed;
585
+ }
498
586
  function buildCodexHookBlock(targetDir, opts) {
499
587
  const absoluteRunner = opts && opts.absoluteRunner;
500
588
  if (!absoluteRunner)
@@ -1072,7 +1160,15 @@ function writeCursorHooksJson(targetDir, src, opts) {
1072
1160
  // installSharedHooksBundle (the only other writer of this marker); without
1073
1161
  // it, a ~/.cursor/package.json declaring {"type":"module"} makes Node load
1074
1162
  // these require()-using scripts as ESM and every Cursor hook fails silently.
1075
- ensureCommonJsMarker(hooksDir);
1163
+ //
1164
+ // #2544: gated on having actually staged a script, mirroring
1165
+ // installSharedHooksBundle's `stagedHooks` gate. hooks/ is shared space, and
1166
+ // this function mkdirs it unconditionally — so an ungated write drops a GSD
1167
+ // marker into a directory GSD created but did not fill, which is the same
1168
+ // write-into-someone-else's-territory this issue is about.
1169
+ if (installedScripts.size > 0) {
1170
+ ensureCommonJsMarker(hooksDir);
1171
+ }
1076
1172
  const hookOpts = { runtime: 'cursor', platform: opts.platform || process.platform };
1077
1173
  const commands = {};
1078
1174
  for (const ev of events) {
@@ -1266,7 +1362,12 @@ function writeWindsurfHooksJson(targetDir, src, opts) {
1266
1362
  // installSharedHooksBundle (the only other writer of this marker); without
1267
1363
  // it, a config-root package.json declaring {"type":"module"} makes Node load
1268
1364
  // these require()-using scripts as ESM and the Windsurf hooks fail silently.
1269
- ensureCommonJsMarker(hooksDir);
1365
+ //
1366
+ // #2544: gated on having actually staged a script — see the identical gate in
1367
+ // the Cursor writer above and `stagedHooks` in installSharedHooksBundle.
1368
+ if (installedScripts.size > 0) {
1369
+ ensureCommonJsMarker(hooksDir);
1370
+ }
1270
1371
  const hookOpts = { runtime: 'windsurf', platform: opts.platform || process.platform };
1271
1372
  const commands = {};
1272
1373
  for (const ev of WINDSURF_HOOK_EVENTS) {
@@ -1597,6 +1698,63 @@ function applySettingsJsonHooks(settings, opts) {
1597
1698
  else if (!hasWorktreePathGuardHook && !node_fs_1.default.existsSync(worktreePathGuardFile)) {
1598
1699
  console.warn(` ${yellow}⚠${reset} Skipped worktree path guard hook — gsd-worktree-path-guard.js not found at target`);
1599
1700
  }
1701
+ // Configure PreToolUse hook for Agent-dispatch isolation (#3045)
1702
+ // Hard-blocks an executor Agent() dispatch (subagent_type="gsd-executor")
1703
+ // missing its harness isolation parameter when this project's resolved
1704
+ // dispatch isolation is harness-worktree. Prevents the executor from
1705
+ // silently running and committing in the primary checkout when the
1706
+ // model-authored dispatch omits isolation="worktree".
1707
+ const agentIsolationGuardCommand = isGlobal
1708
+ ? buildHookCommand(targetDir, 'gsd-agent-isolation-guard.js', hookOpts)
1709
+ : localCmd('gsd-agent-isolation-guard.js');
1710
+ const hasAgentIsolationGuardHook = settings.hooks[preToolEvent].some((entry) => entry.hooks && entry.hooks.some((h) => referencesHook(h, 'gsd-agent-isolation-guard')));
1711
+ const agentIsolationGuardFile = node_path_1.default.join(targetDir, 'hooks', 'gsd-agent-isolation-guard.js');
1712
+ if (!hasAgentIsolationGuardHook && node_fs_1.default.existsSync(agentIsolationGuardFile) && agentIsolationGuardCommand) {
1713
+ settings.hooks[preToolEvent].push({
1714
+ // #3045 MAJOR 1: widened from "Agent"-only — hooks.json's own
1715
+ // PostToolUse precedent (context-monitor) already hedges both names,
1716
+ // and the hook itself now accepts tool_name "Task" too.
1717
+ matcher: 'Agent|Task',
1718
+ hooks: [
1719
+ {
1720
+ type: 'command',
1721
+ command: agentIsolationGuardCommand,
1722
+ timeout: 5
1723
+ }
1724
+ ]
1725
+ });
1726
+ console.log(` ${green}✓${reset} Configured agent isolation dispatch guard hook`);
1727
+ }
1728
+ else if (!hasAgentIsolationGuardHook && !node_fs_1.default.existsSync(agentIsolationGuardFile)) {
1729
+ console.warn(` ${yellow}⚠${reset} Skipped agent isolation guard hook — gsd-agent-isolation-guard.js not found at target`);
1730
+ }
1731
+ // Configure PreToolUse hook for catastrophic-shrink protection (#2255, fix 3 of #973)
1732
+ // Hard-blocks a whole-file Write that collapses a curated .planning/ artifact
1733
+ // (ROADMAP.md, milestone roadmaps, STATE.md) far below its on-disk size.
1734
+ // Escape hatches (both named in the block message): the single-use
1735
+ // sentinel .planning/.gsd-allow-shrink (workflow steps — a per-step env
1736
+ // cannot reach a hook) and GSD_ALLOW_PLANNING_SHRINK=1 (interactive).
1737
+ const writeGuardCommand = isGlobal
1738
+ ? buildHookCommand(targetDir, 'gsd-write-guard.js', hookOpts)
1739
+ : localCmd('gsd-write-guard.js');
1740
+ const hasWriteGuardHook = settings.hooks[preToolEvent].some((entry) => entry.hooks && entry.hooks.some((h) => referencesHook(h, 'gsd-write-guard')));
1741
+ const writeGuardFile = node_path_1.default.join(targetDir, 'hooks', 'gsd-write-guard.js');
1742
+ if (!hasWriteGuardHook && node_fs_1.default.existsSync(writeGuardFile) && writeGuardCommand) {
1743
+ settings.hooks[preToolEvent].push({
1744
+ matcher: 'Write',
1745
+ hooks: [
1746
+ {
1747
+ type: 'command',
1748
+ command: writeGuardCommand,
1749
+ timeout: 5
1750
+ }
1751
+ ]
1752
+ });
1753
+ console.log(` ${green}✓${reset} Configured write guard hook (catastrophic-shrink protection)`);
1754
+ }
1755
+ else if (!hasWriteGuardHook && !node_fs_1.default.existsSync(writeGuardFile)) {
1756
+ console.warn(` ${yellow}⚠${reset} Skipped write guard hook — gsd-write-guard.js not found at target`);
1757
+ }
1600
1758
  // Configure commit validation hook (Conventional Commits enforcement, opt-in)
1601
1759
  const validateCommitCommand = isGlobal
1602
1760
  ? buildHookCommand(targetDir, 'gsd-validate-commit.sh', hookOpts)
@@ -1701,6 +1859,22 @@ function applySettingsJsonHooks(settings, opts) {
1701
1859
  else if (!hasPhaseBoundaryHook && !phaseBoundaryCommand) {
1702
1860
  console.warn(` ${yellow}⚠${reset} Skipped phase boundary hook — Bash executable path unavailable (#3393)`);
1703
1861
  }
1862
+ // #3329: the four `.sh` sites above register only-if-absent, so an entry
1863
+ // registered by an older installer keeps its old command forever —
1864
+ // /gsd-update (which re-invokes the installer) never re-derived it. On
1865
+ // Claude/win32 that left the pre-#580/#3393 bash-runner-prefixed commands
1866
+ // in settings.json indefinitely. Reconcile existing managed `.sh` entries
1867
+ // to the command this install would generate today. Inert wherever the
1868
+ // bash runner is still the correct shape; scoped to exact managed
1869
+ // basenames so user-authored hooks are never touched.
1870
+ if (reconcileManagedShellHookCommands(settings, {
1871
+ 'gsd-validate-commit.sh': validateCommitCommand,
1872
+ 'gsd-graphify-update.sh': graphifyUpdateCommand,
1873
+ 'gsd-session-state.sh': sessionStateCommand,
1874
+ 'gsd-phase-boundary.sh': phaseBoundaryCommand,
1875
+ }, { platform: hookOpts.platform, runtime })) {
1876
+ console.log(` ${green}✓${reset} Reconciled managed .sh hook commands to current format (#3329)`);
1877
+ }
1704
1878
  // ── Extended hook events: SubagentStop / Stop / PreCompact / SubagentStart
1705
1879
  // (#788 + #770 + #2092) ────────────────────────────────────────────────
1706
1880
  // Claude Code (since #770) and Qwen Code (since #788) both support the
@@ -1925,6 +2099,7 @@ function buildKimiHooksTomlBlock(targetDir, opts) {
1925
2099
  { event: 'PreToolUse', command: cmd('gsd-prompt-guard.js'), matcher: 'WriteFile|StrReplaceFile', timeout: 5 },
1926
2100
  { event: 'PreToolUse', command: cmd('gsd-read-guard.js'), matcher: 'WriteFile|StrReplaceFile', timeout: 5 },
1927
2101
  { event: 'PreToolUse', command: cmd('gsd-worktree-path-guard.js'), matcher: 'WriteFile|StrReplaceFile', timeout: 5 },
2102
+ { event: 'PreToolUse', command: cmd('gsd-write-guard.js'), matcher: 'WriteFile', timeout: 5 },
1928
2103
  { event: 'PreToolUse', command: cmd('gsd-workflow-guard.js'), matcher: 'Shell|WriteFile|StrReplaceFile', timeout: 5 },
1929
2104
  { event: 'PreToolUse', command: cmd('gsd-validate-commit.sh'), matcher: 'Shell', timeout: 5 },
1930
2105
  // PostToolUse
@@ -2131,6 +2306,7 @@ module.exports = {
2131
2306
  applySettingsJsonHooks,
2132
2307
  referencesHook,
2133
2308
  rewriteLegacyManagedNodeHookCommands,
2309
+ reconcileManagedShellHookCommands,
2134
2310
  normalizeNodePath,
2135
2311
  resolveNodeRunner,
2136
2312
  resolveBashRunner,
@@ -193,7 +193,9 @@ exports.NO_LOCAL_CONFIG_DIR_SENTINEL = '(no-local-config-dir)';
193
193
  * Pure runtime-identity projection. Relocated from `bin/install.js` per
194
194
  * ADR-1508 (epic #1507, #1510 Phase 1) so the Runtime Artifact Conversion
195
195
  * Module's rewrite engine can consume it without importing the installer.
196
- * `bin/install.js` re-exports this same function for back-compat.
196
+ * Consumers import it from here. `bin/install.js` used to re-export it for
197
+ * back-compat; that re-export was retired in #2876, once an audit found the
198
+ * compatibility spine had no production consumer at all.
197
199
  */
198
200
  function getDirName(runtime) {
199
201
  if (!runtime)
@@ -29,6 +29,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
29
29
  };
30
30
  Object.defineProperty(exports, "__esModule", { value: true });
31
31
  exports.formatGsdSlash = formatGsdSlash;
32
+ exports.resolveExplicitRuntime = resolveExplicitRuntime;
32
33
  exports.resolveRuntime = resolveRuntime;
33
34
  exports.formatGsdSlashFor = formatGsdSlashFor;
34
35
  const node_fs_1 = __importDefault(require("node:fs"));
@@ -75,19 +76,21 @@ function formatGsdSlash(commandName, runtime) {
75
76
  return `/gsd-${token}${tail}`;
76
77
  }
77
78
  /**
78
- * Resolve the effective runtime for a project directory.
79
+ * Resolve the explicit runtime for a project directory, from the two
80
+ * explicit sources only — no default is applied.
79
81
  *
80
- * process.env.GSD_RUNTIME > config.runtime > 'claude'
82
+ * env.GSD_RUNTIME > config.runtime > null
81
83
  *
82
- * Mirrors the precedence already used by profile-output.cjs and the rest of
83
- * the runtime resolution chain. Returns a lowercased string so downstream
84
- * comparisons can be case-blind.
84
+ * Returns null when neither explicit source is set, so a caller can
85
+ * distinguish "config said claude" from "nothing was set" (needed by
86
+ * #3245's host-detection rung, which must only run when this returns null).
85
87
  *
86
88
  * @param projectDir - path to the project directory, or null/undefined
87
- * @returns the resolved runtime name
89
+ * @param env - environment variables to read GSD_RUNTIME from; defaults to process.env
90
+ * @returns the resolved runtime name, or null if neither source is set
88
91
  */
89
- function resolveRuntime(projectDir) {
90
- const envRuntime = (0, runtime_name_policy_cjs_1.resolveRuntimeNameFromCandidates)(process.env['GSD_RUNTIME']);
92
+ function resolveExplicitRuntime(projectDir, env = process.env) {
93
+ const envRuntime = (0, runtime_name_policy_cjs_1.resolveRuntimeNameFromCandidates)(env['GSD_RUNTIME']);
91
94
  if (envRuntime)
92
95
  return envRuntime;
93
96
  if (projectDir) {
@@ -113,7 +116,22 @@ function resolveRuntime(projectDir) {
113
116
  // runtime output formatting.
114
117
  }
115
118
  }
116
- return 'claude';
119
+ return null;
120
+ }
121
+ /**
122
+ * Resolve the effective runtime for a project directory.
123
+ *
124
+ * process.env.GSD_RUNTIME > config.runtime > 'claude'
125
+ *
126
+ * Mirrors the precedence already used by profile-output.cjs and the rest of
127
+ * the runtime resolution chain. Returns a lowercased string so downstream
128
+ * comparisons can be case-blind.
129
+ *
130
+ * @param projectDir - path to the project directory, or null/undefined
131
+ * @returns the resolved runtime name
132
+ */
133
+ function resolveRuntime(projectDir) {
134
+ return resolveExplicitRuntime(projectDir) ?? 'claude';
117
135
  }
118
136
  /**
119
137
  * Convenience: format using the runtime resolved from a project directory.
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ /**
3
+ * Section Manifest — pure `when=` evaluator over `InvocationFacts`, mapping
4
+ * a document-order list of parsed `<!-- gsd:section -->` sections (Phase 3,
5
+ * `src/workflow-fragments.cts`) to an included/excluded partition for one
6
+ * concrete invocation (ADR-1671 epic #1671, Phase 5 / issue #2932,
7
+ * `.gsd/phase/chore-2932-init-section-manifest/40-design.md`).
8
+ *
9
+ * Pure module: no I/O, no dependency beyond node built-ins and the sibling
10
+ * compiled module `workflow-fragments.cjs`, whose {@link
11
+ * workflowFragments.WHEN_VOCABULARY} is imported and never redeclared here
12
+ * (DEFECT.GENERATIVE-FIX — a second frozen copy of the same 4 strings would
13
+ * silently desync from the source of truth the moment either side is edited
14
+ * without the other).
15
+ *
16
+ * ## The evaluator is a LOOKUP, not a parser
17
+ *
18
+ * Derived from Greenspun's Tenth Rule (ADR-1671:69 cites it by name) and
19
+ * binding on this implementation: `when=` is a closed vocabulary, widened
20
+ * from 4 to 14 entries via the ADR-1671 amendment for #2992 (epic #1671
21
+ * Phase 6.1; see `.gsd/phase/chore-2992-widen-when-vocabulary/
22
+ * 40-design.md`), then from 14 to 19 via the ADR-1671 amendment for #2993
23
+ * (epic #1671 Phase 6.2; see `.gsd/phase/chore-2993-fragmentize-plan-phase/
24
+ * 40-design.md`), then from 19 to 20 via the ADR-1671 amendment for #2994
25
+ * (epic #1671 Phase 6.3), then from 20 to 23 via a further #2994 amendment
26
+ * fragmentizing `code-review.md` and `complete-milestone.md`, then from 23
27
+ * to 24 via a still further #2994 amendment fragmentizing `autonomous.md`,
28
+ * then from 24 to 26 via a still further #2994 amendment fragmentizing
29
+ * `review.md` and `discuss-phase-assumptions.md`, then from 26 to 30 — and
30
+ * finally to 29, `flag:--full` having been retired as dead vocabulary — via the
31
+ * final #2994 slice fragmentizing `docs-update.md`, `update.md`,
32
+ * `transition.md`, and `new-milestone.md`.
33
+ * {@link WHEN_PREDICATES} is a total map from each frozen
34
+ * vocabulary entry to exactly one predicate over {@link InvocationFacts}.
35
+ * It MUST NOT tokenize, split on operators, or interpret structure in the
36
+ * `when=` string — the moment it parses, the ad-hoc language has begun.
37
+ * Every entry below is therefore a HAND-WRITTEN LITERAL: deriving a
38
+ * predicate's flag/state name from its atom string (e.g. slicing `--fix`
39
+ * out of `'flag:--fix'`) is tokenization relocated into this map and is
40
+ * forbidden even though it would be shorter — the redundancy between each
41
+ * key and its literal token is deliberate, and the bidirectional parity
42
+ * test below catches any desync a hand-written entry could introduce. An
43
+ * unrecognized `when=` value fails closed via {@link selectSections}
44
+ * throwing a `TypeError` carrying `.reason = REASON.UNKNOWN_WHEN`; it is
45
+ * never silently excluded (Postel's Law: liberal on FORMAT elsewhere in the
46
+ * pipeline, strict on this SEMANTIC boundary — matching the discipline
47
+ * Phase 3 already established for the same vocabulary at parse time).
48
+ *
49
+ * ## Totality over facts
50
+ *
51
+ * Every predicate treats an absent/missing fact key as falsy WITHOUT
52
+ * throwing — {@link InvocationFacts} is a plain data object handed in by a
53
+ * caller (the init CLI seam) that may not always populate every field, and
54
+ * this module must never surprise that caller with an exception for an
55
+ * omission rather than a malformed `when=` value.
56
+ *
57
+ * ## Partition invariant
58
+ *
59
+ * {@link selectSections} returns `included` and `excluded` id arrays that
60
+ * together contain every input section's `id` exactly once, in the SAME
61
+ * relative document order they appeared in the input — never mutating the
62
+ * input array or its elements.
63
+ *
64
+ * ADR-457 build-at-publish: compiled by tsc to
65
+ * gsd-core/bin/lib/section-manifest.cjs (gitignored).
66
+ */
67
+ Object.defineProperty(exports, "__esModule", { value: true });
68
+ exports.WHEN_PREDICATES = exports.REASON = void 0;
69
+ exports.selectSections = selectSections;
70
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- workflow-fragments.cjs is a CommonJS module compiled from a sibling .cts source; `import x = require()` reads its module.exports namespace directly.
71
+ const workflowFragments = require("./workflow-fragments.cjs");
72
+ /**
73
+ * Frozen, stable reason codes for every `fail()` throw site in this module.
74
+ * Tests assert via `assert.equal(err.reason, REASON.X)` rather than
75
+ * regex-/substring-matching the human-readable message (CONTRIBUTING.md
76
+ * "Prohibited: Raw Text Matching on Test Outputs"; shape copied from
77
+ * `src/workflow-fragments.cts`'s own `REASON` export) — a message reword
78
+ * must never silently pass a test that exists to catch a behavior
79
+ * regression.
80
+ *
81
+ * Adding a new reason requires updating this map AND the test that locks
82
+ * `Object.keys(REASON).sort()` as a coordinated change.
83
+ */
84
+ exports.REASON = Object.freeze({
85
+ UNKNOWN_WHEN: 'unknown_when',
86
+ });
87
+ /**
88
+ * Throws a `TypeError` naming the offending `when` value, carrying `reason`
89
+ * (one of {@link REASON}) as a typed property so callers/tests never need
90
+ * to pattern-match the message prose.
91
+ */
92
+ function fail(reason, message) {
93
+ const err = new TypeError(`section-manifest: ${message}`);
94
+ err.reason = reason;
95
+ throw err;
96
+ }
97
+ /**
98
+ * Safely tests whether `facts.flags` contains `flag`, tolerating an absent,
99
+ * `null`, or non-`Set` (e.g. array) `flags` value without throwing —
100
+ * `.has` is checked to be callable before it is called, rather than
101
+ * assuming every {@link InvocationFacts.flags} is a real `Set` (totality
102
+ * over facts; not duck-typed — an array `flags` degrades to "not present",
103
+ * it is never iterated or `.includes`-checked).
104
+ */
105
+ function hasFlag(facts, flag) {
106
+ return typeof facts.flags?.has === 'function' && facts.flags.has(flag) === true;
107
+ }
108
+ /**
109
+ * Total map from each frozen {@link workflowFragments.WHEN_VOCABULARY}
110
+ * entry to exactly one predicate over {@link InvocationFacts}. This is a
111
+ * LOOKUP, never a parser — see the module doc comment's "The evaluator is a
112
+ * LOOKUP, not a parser" section. Every entry is a hand-written literal; see
113
+ * the module doc comment for why deriving a predicate from its atom string
114
+ * is forbidden. Semantics confirmed against the section bodies themselves
115
+ * (design doc "Semantics confirmed against the section bodies themselves,
116
+ * not inferred from the id"):
117
+ *
118
+ * - `gap-closure-artifacts` — "For decimal/polish phases only (X.Y
119
+ * pattern) … Skip if phase number has no decimal" -> `state:gap-closure-phase`.
120
+ * - `regression-gate` — "Skip if: this is the first phase (no prior
121
+ * phases)" -> `state:has-prior-phases`.
122
+ * - `partial-wave` — "If `WAVE_FILTER` was used" -> `flag:--wave`.
123
+ */
124
+ exports.WHEN_PREDICATES = Object.freeze(Object.assign(Object.create(null), {
125
+ always: () => true,
126
+ 'flag:--wave': (facts) => hasFlag(facts, '--wave'),
127
+ 'state:gap-closure-phase': (facts) => typeof facts.phaseNumber === 'string' && facts.phaseNumber.includes('.'),
128
+ 'state:has-prior-phases': (facts) => facts.hasPriorPhases === true,
129
+ 'flag:--auto': (facts) => hasFlag(facts, '--auto'),
130
+ 'flag:--discuss': (facts) => hasFlag(facts, '--discuss'),
131
+ 'flag:--fix': (facts) => hasFlag(facts, '--fix'),
132
+ 'flag:--forensic': (facts) => hasFlag(facts, '--forensic'),
133
+ 'flag:--ingest': (facts) => hasFlag(facts, '--ingest'),
134
+ 'flag:--prd': (facts) => hasFlag(facts, '--prd'),
135
+ 'flag:--research': (facts) => hasFlag(facts, '--research'),
136
+ 'flag:--research-phase': (facts) => hasFlag(facts, '--research-phase'),
137
+ 'flag:--reset-phase-numbers': (facts) => hasFlag(facts, '--reset-phase-numbers'),
138
+ 'flag:--reviews': (facts) => hasFlag(facts, '--reviews'),
139
+ 'flag:--validate': (facts) => hasFlag(facts, '--validate'),
140
+ 'state:auto-advance-active': (facts) => facts.autoAdvanceActive === true,
141
+ 'state:chunked-mode': (facts) => facts.chunkedMode === true,
142
+ 'state:fallow-enabled': (facts) => facts.fallowEnabled === true,
143
+ 'state:flat-mode': (facts) => facts.flatMode === true,
144
+ 'state:git-create-tag': (facts) => facts.gitCreateTag === true,
145
+ 'state:is-monorepo': (facts) => facts.isMonorepo === true,
146
+ 'state:needs-codebase-map': (facts) => facts.needsCodebaseMap === true,
147
+ 'state:next-channel': (facts) => facts.nextChannel === true,
148
+ 'state:phase-mvp-mode': (facts) => facts.phaseMvpMode === true,
149
+ 'state:plan-strategy-converge': (facts) => facts.planStrategyConverge === true,
150
+ 'state:reviewer-instances-configured': (facts) => facts.reviewerInstancesConfigured === true,
151
+ 'state:ui-phase-active': (facts) => facts.uiPhaseActive === true,
152
+ 'state:workstream-active': (facts) => facts.workstreamActive === true,
153
+ 'state:worktrees-enabled': (facts) => facts.worktreesEnabled === true,
154
+ }));
155
+ // Coordinated-change guard, checked at module load: every entry of the
156
+ // frozen WHEN_VOCABULARY (imported, never redeclared — see module doc
157
+ // comment) must have exactly one predicate here, and vice versa. This is
158
+ // the load-bearing half of the DEFECT.GENERATIVE-FIX parity contract; the
159
+ // test-level half (50-test-matrix.md rows 21-23) additionally asserts it
160
+ // from the vocabulary's own export so a 5th vocabulary entry added without
161
+ // a predicate fails loudly rather than silently falling through to
162
+ // REASON.UNKNOWN_WHEN only at run time.
163
+ for (const when of workflowFragments.WHEN_VOCABULARY) {
164
+ if (!Object.hasOwn(exports.WHEN_PREDICATES, when)) {
165
+ throw new Error(`section-manifest: WHEN_VOCABULARY entry "${when}" has no predicate in WHEN_PREDICATES`);
166
+ }
167
+ }
168
+ // Reverse half of the same coordinated-change guard: every own key of
169
+ // WHEN_PREDICATES must also appear in WHEN_VOCABULARY. Without this, a
170
+ // predicate key with no vocabulary entry would let the evaluator accept an
171
+ // atom that the parser (classifyMarker) rejects — a real divergence between
172
+ // the two shared-constant halves (DEFECT.GENERATIVE-FIX; B9/B10 in
173
+ // `.gsd/phase/chore-2992-widen-when-vocabulary/50-test-matrix.md`).
174
+ const VOCABULARY_SET = new Set(workflowFragments.WHEN_VOCABULARY);
175
+ for (const predicateKey of Object.keys(exports.WHEN_PREDICATES)) {
176
+ if (!VOCABULARY_SET.has(predicateKey)) {
177
+ throw new Error(`section-manifest: WHEN_PREDICATES entry "${predicateKey}" has no matching WHEN_VOCABULARY entry`);
178
+ }
179
+ }
180
+ /**
181
+ * Partition `sections` (document order) into `included`/`excluded` id
182
+ * arrays for one set of `facts`, per {@link WHEN_PREDICATES}. Exact
183
+ * partition: every input id appears in exactly one of the two output
184
+ * arrays, in the same relative order it appeared in `sections`. Never
185
+ * mutates `sections` or its elements.
186
+ *
187
+ * @param sections - document-order sections carrying at least `{id, when}`
188
+ * @param facts - the concrete invocation's resolved facts
189
+ * @throws {SectionManifestError} with `.reason = REASON.UNKNOWN_WHEN` when a
190
+ * section's `when` value has no entry in {@link WHEN_PREDICATES} (fail
191
+ * closed — never silently excluded).
192
+ */
193
+ function selectSections(sections, facts) {
194
+ const included = [];
195
+ const excluded = [];
196
+ for (const section of sections) {
197
+ if (!Object.hasOwn(exports.WHEN_PREDICATES, section.when)) {
198
+ fail(exports.REASON.UNKNOWN_WHEN, `section "${section.id}" has unrecognized when= value "${section.when}"`);
199
+ }
200
+ const predicate = exports.WHEN_PREDICATES[section.when];
201
+ if (predicate(facts)) {
202
+ included.push(section.id);
203
+ }
204
+ else {
205
+ excluded.push(section.id);
206
+ }
207
+ }
208
+ return { included, excluded };
209
+ }