@opengsd/gsd-core 1.12.0 → 1.14.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 (455) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +12 -0
  4. package/agents/gsd-advisor-researcher.compact.md +85 -0
  5. package/agents/gsd-ai-researcher.compact.md +96 -0
  6. package/agents/gsd-assumptions-analyzer.compact.md +81 -0
  7. package/agents/gsd-code-fixer.compact.md +458 -0
  8. package/agents/gsd-code-fixer.md +5 -5
  9. package/agents/gsd-code-reviewer.compact.md +269 -0
  10. package/agents/gsd-code-reviewer.md +15 -3
  11. package/agents/gsd-codebase-mapper.compact.md +760 -0
  12. package/agents/gsd-debug-session-manager.compact.md +345 -0
  13. package/agents/gsd-doc-classifier.compact.md +192 -0
  14. package/agents/gsd-doc-synthesizer.compact.md +200 -0
  15. package/agents/gsd-doc-verifier.compact.md +143 -0
  16. package/agents/gsd-doc-writer.compact.md +440 -0
  17. package/agents/gsd-dom-verifier.compact.md +138 -0
  18. package/agents/gsd-domain-researcher.compact.md +141 -0
  19. package/agents/gsd-eval-auditor.compact.md +160 -0
  20. package/agents/gsd-eval-planner.compact.md +137 -0
  21. package/agents/gsd-executor.md +63 -35
  22. package/agents/gsd-framework-selector.compact.md +82 -0
  23. package/agents/gsd-integration-checker.compact.md +245 -0
  24. package/agents/gsd-intel-updater.compact.md +226 -0
  25. package/agents/gsd-mempalace-curator.compact.md +45 -0
  26. package/agents/gsd-nyquist-auditor.compact.md +179 -0
  27. package/agents/gsd-pattern-mapper.compact.md +275 -0
  28. package/agents/gsd-plan-checker.md +76 -57
  29. package/agents/gsd-planner.md +14 -0
  30. package/agents/gsd-project-researcher.compact.md +587 -0
  31. package/agents/gsd-research-synthesizer.compact.md +212 -0
  32. package/agents/gsd-roadmapper.compact.md +454 -0
  33. package/agents/gsd-roadmapper.md +13 -0
  34. package/agents/gsd-security-auditor.compact.md +162 -0
  35. package/agents/gsd-ui-auditor.compact.md +404 -0
  36. package/agents/gsd-ui-checker.compact.md +277 -0
  37. package/agents/gsd-ui-checker.md +19 -3
  38. package/agents/gsd-ui-researcher.compact.md +282 -0
  39. package/agents/gsd-ui-researcher.md +29 -0
  40. package/agents/gsd-user-profiler.compact.md +108 -0
  41. package/agents/gsd-verifier.md +23 -1
  42. package/bin/install.js +444 -134
  43. package/commands/gsd/cleanup.md +1 -0
  44. package/commands/gsd/code-review.md +2 -1
  45. package/commands/gsd/complete-milestone.md +1 -0
  46. package/commands/gsd/config.md +1 -0
  47. package/commands/gsd/debug.md +1 -0
  48. package/commands/gsd/execute-phase.md +1 -1
  49. package/commands/gsd/graphify.md +1 -0
  50. package/commands/gsd/health.md +1 -0
  51. package/commands/gsd/mempalace-capture.md +1 -0
  52. package/commands/gsd/mempalace-recall.md +1 -0
  53. package/commands/gsd/new-milestone.md +1 -0
  54. package/commands/gsd/new-project.md +1 -0
  55. package/commands/gsd/next.md +1 -0
  56. package/commands/gsd/ns-workflow.md +2 -1
  57. package/commands/gsd/pause-work.md +1 -0
  58. package/commands/gsd/phase.md +2 -1
  59. package/commands/gsd/pr-branch.md +1 -0
  60. package/commands/gsd/quick-batch.md +105 -0
  61. package/commands/gsd/resume-work.md +1 -0
  62. package/commands/gsd/review-backlog.md +1 -0
  63. package/commands/gsd/settings.md +2 -1
  64. package/commands/gsd/stats.md +1 -0
  65. package/commands/gsd/surface.md +18 -8
  66. package/commands/gsd/thread.md +1 -0
  67. package/commands/gsd/workspace.md +1 -0
  68. package/commands/gsd/workstreams.md +1 -0
  69. package/gsd-core/bin/check-latest-version.cjs +8 -3
  70. package/gsd-core/bin/gsd-tools.cjs +532 -174
  71. package/gsd-core/bin/lib/adr-parser.cjs +1 -1
  72. package/gsd-core/bin/lib/artifacts.cjs +2 -1
  73. package/gsd-core/bin/lib/audit.cjs +39 -22
  74. package/gsd-core/bin/lib/broken-windows.cjs +168 -49
  75. package/gsd-core/bin/lib/capability-activation.cjs +27 -0
  76. package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
  77. package/gsd-core/bin/lib/capability-loader.cjs +135 -1
  78. package/gsd-core/bin/lib/capability-registry.cjs +528 -116
  79. package/gsd-core/bin/lib/capability-source.cjs +19 -2
  80. package/gsd-core/bin/lib/capability-state.cjs +7 -1
  81. package/gsd-core/bin/lib/capability-validator.cjs +134 -5
  82. package/gsd-core/bin/lib/capability-writer.cjs +14 -4
  83. package/gsd-core/bin/lib/check-command-router.cjs +198 -38
  84. package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
  85. package/gsd-core/bin/lib/clusters.cjs +1 -0
  86. package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
  87. package/gsd-core/bin/lib/command-aliases.cjs +16 -0
  88. package/gsd-core/bin/lib/commands.cjs +981 -79
  89. package/gsd-core/bin/lib/config-loader.cjs +4 -0
  90. package/gsd-core/bin/lib/config.cjs +153 -38
  91. package/gsd-core/bin/lib/core-utils.cjs +34 -7
  92. package/gsd-core/bin/lib/coverage.cjs +1 -1
  93. package/gsd-core/bin/lib/decisions.cjs +343 -28
  94. package/gsd-core/bin/lib/edge-probe.cjs +14 -1
  95. package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
  96. package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
  97. package/gsd-core/bin/lib/frontmatter.cjs +137 -23
  98. package/gsd-core/bin/lib/gap-checker.cjs +22 -13
  99. package/gsd-core/bin/lib/git-base-branch.cjs +10 -2
  100. package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
  101. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
  102. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +54 -11
  103. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +87 -23
  104. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
  105. package/gsd-core/bin/lib/host-integration.cjs +57 -5
  106. package/gsd-core/bin/lib/init-command-router.cjs +14 -0
  107. package/gsd-core/bin/lib/init.cjs +539 -60
  108. package/gsd-core/bin/lib/install-engine.cjs +199 -14
  109. package/gsd-core/bin/lib/install-model-override-resolver.cjs +45 -0
  110. package/gsd-core/bin/lib/install-profiles.cjs +36 -14
  111. package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
  112. package/gsd-core/bin/lib/installer-migrations.cjs +33 -4
  113. package/gsd-core/bin/lib/io.cjs +35 -0
  114. package/gsd-core/bin/lib/loop-resolver.cjs +64 -39
  115. package/gsd-core/bin/lib/markdown-table.cjs +123 -0
  116. package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
  117. package/gsd-core/bin/lib/milestone.cjs +41 -10
  118. package/gsd-core/bin/lib/model-resolver.cjs +101 -10
  119. package/gsd-core/bin/lib/phase-command-router.cjs +20 -7
  120. package/gsd-core/bin/lib/phase-id.cjs +412 -31
  121. package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
  122. package/gsd-core/bin/lib/phase.cjs +941 -98
  123. package/gsd-core/bin/lib/plan-document.cjs +10 -0
  124. package/gsd-core/bin/lib/planning-inspect.cjs +34 -18
  125. package/gsd-core/bin/lib/planning-snapshot.cjs +206 -30
  126. package/gsd-core/bin/lib/planning-workspace.cjs +153 -29
  127. package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
  128. package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
  129. package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
  130. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
  131. package/gsd-core/bin/lib/quick-batch.cjs +840 -0
  132. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
  133. package/gsd-core/bin/lib/research-store.cjs +11 -12
  134. package/gsd-core/bin/lib/review-lane-descriptor.cjs +53 -5
  135. package/gsd-core/bin/lib/review-lane-invocation.cjs +96 -1
  136. package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
  137. package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
  138. package/gsd-core/bin/lib/roadmap-parser.cjs +555 -41
  139. package/gsd-core/bin/lib/roadmap.cjs +292 -69
  140. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +260 -43
  141. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +28 -20
  142. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +294 -108
  143. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -47
  144. package/gsd-core/bin/lib/security.cjs +126 -7
  145. package/gsd-core/bin/lib/shell-command-projection.cjs +4 -0
  146. package/gsd-core/bin/lib/smart-entry.cjs +7 -9
  147. package/gsd-core/bin/lib/state-document.cjs +159 -32
  148. package/gsd-core/bin/lib/state-md-schema.cjs +44 -27
  149. package/gsd-core/bin/lib/state-transition.cjs +465 -62
  150. package/gsd-core/bin/lib/state.cjs +906 -151
  151. package/gsd-core/bin/lib/surface.cjs +83 -10
  152. package/gsd-core/bin/lib/task-command-router.cjs +12 -6
  153. package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
  154. package/gsd-core/bin/lib/uat.cjs +1420 -516
  155. package/gsd-core/bin/lib/update-context.cjs +36 -26
  156. package/gsd-core/bin/lib/validate.cjs +230 -12
  157. package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
  158. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  159. package/gsd-core/bin/lib/verification.cjs +316 -23
  160. package/gsd-core/bin/lib/verify-command-grounding.cjs +1 -1
  161. package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
  162. package/gsd-core/bin/lib/verify.cjs +531 -36
  163. package/gsd-core/bin/lib/workstream-inventory.cjs +21 -2
  164. package/gsd-core/bin/lib/worktree-safety.cjs +21 -7
  165. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  166. package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
  167. package/gsd-core/bin/verify-reapply-patches.cjs +507 -81
  168. package/gsd-core/references/agent-contracts.md +3 -3
  169. package/gsd-core/references/compact-content-gate.md +66 -0
  170. package/gsd-core/references/edge-probe.md +17 -13
  171. package/gsd-core/references/execute-mvp-tdd.md +18 -16
  172. package/gsd-core/references/execute-phase-response-language.md +6 -0
  173. package/gsd-core/references/executor-examples.md +42 -0
  174. package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
  175. package/gsd-core/references/loop-hook-dispatch.md +18 -0
  176. package/gsd-core/references/model-profiles.md +12 -3
  177. package/gsd-core/references/mvp-concepts.md +2 -2
  178. package/gsd-core/references/plan-checker-examples.md +41 -0
  179. package/gsd-core/references/planner-antipatterns.md +25 -0
  180. package/gsd-core/references/planner-chunked.md +5 -1
  181. package/gsd-core/references/planner-coupling.md +42 -0
  182. package/gsd-core/references/planner-quick-batch.md +71 -0
  183. package/gsd-core/references/planner-reviews.md +47 -0
  184. package/gsd-core/references/planner-revision.md +75 -2
  185. package/gsd-core/references/planning-config.md +5 -1
  186. package/gsd-core/references/response-language-directive.md +9 -0
  187. package/gsd-core/references/revision-loop.md +118 -11
  188. package/gsd-core/references/tdd.md +17 -9
  189. package/gsd-core/references/thinking-models-planning.md +18 -2
  190. package/gsd-core/references/verification-patterns.md +17 -4
  191. package/gsd-core/references/verifier-evidence-gate.md +160 -0
  192. package/gsd-core/references/worktree-path-safety.md +112 -2
  193. package/gsd-core/templates/README.md +7 -1
  194. package/gsd-core/templates/phase-prompt.md +4 -0
  195. package/gsd-core/templates/state.md +6 -3
  196. package/gsd-core/templates/summary.compact.md +212 -0
  197. package/gsd-core/templates/user-setup.compact.md +199 -0
  198. package/gsd-core/templates/user-setup.md +0 -9
  199. package/gsd-core/templates/verification-report.md +5 -0
  200. package/gsd-core/workflows/add-backlog.md +2 -0
  201. package/gsd-core/workflows/add-phase.md +2 -0
  202. package/gsd-core/workflows/add-tests.md +1 -1
  203. package/gsd-core/workflows/add-todo.md +4 -3
  204. package/gsd-core/workflows/ai-integration-phase.md +1 -1
  205. package/gsd-core/workflows/analyze-dependencies.md +2 -0
  206. package/gsd-core/workflows/audit-fix.md +2 -0
  207. package/gsd-core/workflows/audit-milestone.md +2 -0
  208. package/gsd-core/workflows/audit-uat.md +2 -0
  209. package/gsd-core/workflows/autonomous.md +15 -10
  210. package/gsd-core/workflows/check-todos.md +5 -3
  211. package/gsd-core/workflows/cleanup.md +4 -2
  212. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +22 -13
  213. package/gsd-core/workflows/code-review-fix.md +5 -3
  214. package/gsd-core/workflows/code-review.md +211 -43
  215. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
  216. package/gsd-core/workflows/complete-milestone.md +40 -254
  217. package/gsd-core/workflows/debug.md +1 -1
  218. package/gsd-core/workflows/diagnose-issues.md +5 -1
  219. package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -0
  220. package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
  221. package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
  222. package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
  223. package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
  224. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -0
  225. package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
  226. package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
  227. package/gsd-core/workflows/discuss-phase/modes/text.md +2 -0
  228. package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
  229. package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
  230. package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
  231. package/gsd-core/workflows/discuss-phase-power.md +2 -0
  232. package/gsd-core/workflows/discuss-phase.md +1 -1
  233. package/gsd-core/workflows/do.md +43 -13
  234. package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
  235. package/gsd-core/workflows/docs-update.md +15 -156
  236. package/gsd-core/workflows/edit-phase.md +2 -0
  237. package/gsd-core/workflows/eval-review.md +1 -1
  238. package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
  239. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +20 -3
  240. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
  241. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +24 -3
  242. package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
  243. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +8 -2
  244. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -0
  245. package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
  246. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
  247. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
  248. package/gsd-core/workflows/execute-phase.md +78 -159
  249. package/gsd-core/workflows/execute-plan.md +28 -15
  250. package/gsd-core/workflows/explore.md +2 -0
  251. package/gsd-core/workflows/extract-learnings.md +2 -0
  252. package/gsd-core/workflows/fast.md +6 -0
  253. package/gsd-core/workflows/forensics.md +2 -0
  254. package/gsd-core/workflows/graduation.md +1 -1
  255. package/gsd-core/workflows/health.md +1 -1
  256. package/gsd-core/workflows/help/modes/brief.md +2 -0
  257. package/gsd-core/workflows/help/modes/default.md +2 -0
  258. package/gsd-core/workflows/help/modes/full.compact.md +398 -0
  259. package/gsd-core/workflows/help/modes/full.md +12 -0
  260. package/gsd-core/workflows/help/modes/topic.md +2 -0
  261. package/gsd-core/workflows/help.md +3 -1
  262. package/gsd-core/workflows/import.md +3 -3
  263. package/gsd-core/workflows/inbox.md +1 -1
  264. package/gsd-core/workflows/ingest-docs.md +1 -1
  265. package/gsd-core/workflows/insert-phase.md +2 -0
  266. package/gsd-core/workflows/list-phase-assumptions.md +2 -0
  267. package/gsd-core/workflows/list-seeds.md +2 -0
  268. package/gsd-core/workflows/list-workspaces.md +2 -0
  269. package/gsd-core/workflows/manager.md +3 -3
  270. package/gsd-core/workflows/map-codebase.md +52 -3
  271. package/gsd-core/workflows/milestone-summary.md +2 -0
  272. package/gsd-core/workflows/mvp-phase.md +1 -1
  273. package/gsd-core/workflows/new-milestone.md +55 -13
  274. package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
  275. package/gsd-core/workflows/new-project.md +37 -205
  276. package/gsd-core/workflows/new-workspace.md +1 -1
  277. package/gsd-core/workflows/next.md +2 -0
  278. package/gsd-core/workflows/node-repair.md +2 -0
  279. package/gsd-core/workflows/note.md +2 -0
  280. package/gsd-core/workflows/onboard.md +1 -1
  281. package/gsd-core/workflows/pause-work.md +19 -4
  282. package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
  283. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
  284. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -0
  285. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +9 -0
  286. package/gsd-core/workflows/plan-phase.md +144 -185
  287. package/gsd-core/workflows/plan-review-convergence.md +102 -10
  288. package/gsd-core/workflows/plant-seed.md +1 -1
  289. package/gsd-core/workflows/pr-branch.md +30 -10
  290. package/gsd-core/workflows/profile-user.md +1 -1
  291. package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
  292. package/gsd-core/workflows/progress.md +25 -3
  293. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +37 -2
  294. package/gsd-core/workflows/quick/steps/research-phase.md +3 -3
  295. package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
  296. package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
  297. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
  298. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
  299. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
  300. package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
  301. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
  302. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
  303. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
  304. package/gsd-core/workflows/quick-batch.md +203 -0
  305. package/gsd-core/workflows/quick.md +21 -4
  306. package/gsd-core/workflows/reapply-patches.md +79 -3
  307. package/gsd-core/workflows/remove-phase.md +2 -0
  308. package/gsd-core/workflows/remove-workspace.md +1 -1
  309. package/gsd-core/workflows/resume-project.md +6 -2
  310. package/gsd-core/workflows/review.md +215 -10
  311. package/gsd-core/workflows/scan.md +2 -0
  312. package/gsd-core/workflows/section-manifest.json +12 -0
  313. package/gsd-core/workflows/secure-phase.md +1 -1
  314. package/gsd-core/workflows/session-report.md +2 -0
  315. package/gsd-core/workflows/settings-advanced.md +2 -0
  316. package/gsd-core/workflows/settings-integrations.md +9 -8
  317. package/gsd-core/workflows/settings.md +19 -6
  318. package/gsd-core/workflows/ship.md +10 -10
  319. package/gsd-core/workflows/sketch-wrap-up.md +2 -0
  320. package/gsd-core/workflows/sketch.md +1 -1
  321. package/gsd-core/workflows/smart-entry.md +1 -1
  322. package/gsd-core/workflows/spec-phase.md +24 -19
  323. package/gsd-core/workflows/spike-wrap-up.md +2 -0
  324. package/gsd-core/workflows/spike.md +1 -1
  325. package/gsd-core/workflows/stats.md +2 -0
  326. package/gsd-core/workflows/sync-skills.md +12 -4
  327. package/gsd-core/workflows/thread.md +2 -0
  328. package/gsd-core/workflows/transition.md +2 -0
  329. package/gsd-core/workflows/ui-phase.md +26 -5
  330. package/gsd-core/workflows/ui-review.md +1 -1
  331. package/gsd-core/workflows/ultraplan-phase.md +2 -0
  332. package/gsd-core/workflows/undo.md +1 -1
  333. package/gsd-core/workflows/update.md +48 -43
  334. package/gsd-core/workflows/validate-phase.md +1 -1
  335. package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
  336. package/gsd-core/workflows/verify-work.md +68 -182
  337. package/hooks/dist/gsd-agent-isolation-guard.js +42 -16
  338. package/hooks/dist/gsd-check-update-worker.js +19 -2
  339. package/hooks/dist/gsd-context-monitor.js +371 -27
  340. package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
  341. package/hooks/dist/gsd-node-runner.sh +1 -0
  342. package/hooks/dist/gsd-prompt-guard.js +30 -5
  343. package/hooks/dist/gsd-read-guard.js +2 -0
  344. package/hooks/dist/gsd-read-injection-scanner.js +5 -5
  345. package/hooks/dist/gsd-secret-read-guard.js +1105 -0
  346. package/hooks/dist/gsd-statusline.js +18 -10
  347. package/hooks/dist/gsd-validate-commit.sh +474 -7
  348. package/hooks/dist/gsd-workflow-guard.js +2 -1
  349. package/hooks/dist/gsd-worktree-path-guard.js +25 -14
  350. package/hooks/dist/gsd-write-guard.js +46 -1
  351. package/hooks/dist/lib/dispatch-identity.js +187 -0
  352. package/hooks/dist/lib/filename-classification.js +64 -0
  353. package/hooks/dist/lib/git-cmd.js +210 -1
  354. package/hooks/dist/lib/injection-patterns.js +36 -6
  355. package/hooks/dist/lib/isolation-deny-reason.js +53 -1
  356. package/hooks/dist/lib/isolation-sentinel.js +58 -19
  357. package/hooks/dist/managed-hooks-registry.cjs +1 -0
  358. package/hooks/gsd-agent-isolation-guard.js +42 -16
  359. package/hooks/gsd-check-update-worker.js +19 -2
  360. package/hooks/gsd-context-monitor.js +371 -27
  361. package/hooks/gsd-cursor-subagent-start.js +34 -14
  362. package/hooks/gsd-node-runner.sh +1 -0
  363. package/hooks/gsd-prompt-guard.js +30 -5
  364. package/hooks/gsd-read-guard.js +2 -0
  365. package/hooks/gsd-read-injection-scanner.js +5 -5
  366. package/hooks/gsd-secret-read-guard.js +1105 -0
  367. package/hooks/gsd-statusline.js +18 -10
  368. package/hooks/gsd-validate-commit.sh +474 -7
  369. package/hooks/gsd-workflow-guard.js +2 -1
  370. package/hooks/gsd-worktree-path-guard.js +25 -14
  371. package/hooks/gsd-write-guard.js +46 -1
  372. package/hooks/hooks.json +6 -0
  373. package/hooks/lib/dispatch-identity.js +187 -0
  374. package/hooks/lib/filename-classification.js +64 -0
  375. package/hooks/lib/git-cmd.js +210 -1
  376. package/hooks/lib/injection-patterns.js +36 -6
  377. package/hooks/lib/isolation-deny-reason.js +53 -1
  378. package/hooks/lib/isolation-sentinel.js +58 -19
  379. package/hooks/managed-hooks-registry.cjs +1 -0
  380. package/package.json +13 -9
  381. package/scripts/benchmark-compact-content-variants.cjs +298 -0
  382. package/scripts/benchmark-compact-content.cjs +368 -0
  383. package/scripts/build-hooks.js +11 -4
  384. package/scripts/check-contract-drift.cjs +4 -1
  385. package/scripts/check-env.cjs +36 -8
  386. package/scripts/check-glossary-refs.cjs +25 -21
  387. package/scripts/ci-next-health.cjs +271 -0
  388. package/scripts/ci-prepare-test-scope.cjs +7 -7
  389. package/scripts/ci-test-scope.cjs +133 -20
  390. package/scripts/ci-timeout-report.cjs +1 -1
  391. package/scripts/diff-touches-shipped-paths.cjs +1 -1
  392. package/scripts/docs-guard-registry.cjs +17 -2
  393. package/scripts/gen-adr-index.cjs +8 -2
  394. package/scripts/gen-inventory-manifest.cjs +12 -0
  395. package/scripts/gen-loop-host-contract.cjs +67 -15
  396. package/scripts/gen-platform-conformance-tier.cjs +557 -0
  397. package/scripts/lib/drift-scan.cjs +1 -1
  398. package/scripts/lib/macos-conformance-tier.generated.cjs +210 -0
  399. package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
  400. package/scripts/lib/platform-conformance-tier.generated.cjs +276 -0
  401. package/scripts/lib/shellcheck-fetch.cjs +247 -0
  402. package/scripts/lib/suite-detection.cjs +32 -0
  403. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
  404. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  405. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  406. package/scripts/lint-allowed-tools-parity.cjs +221 -0
  407. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +24 -2
  408. package/scripts/lint-phase-enumeration-drift.cjs +24 -6
  409. package/scripts/lint-phase-id-drift.cjs +465 -15
  410. package/scripts/lint-portable-grep.cjs +176 -0
  411. package/scripts/lint-response-language-coverage.cjs +530 -0
  412. package/scripts/lint-source-test-name-collision.cjs +1 -1
  413. package/scripts/lint-test-file-count.allowlist.json +4 -1
  414. package/scripts/lint-vendored-deps.cjs +128 -17
  415. package/scripts/lint-workflow-shellcheck-baseline.json +1112 -0
  416. package/scripts/lint-workflow-shellcheck.cjs +614 -0
  417. package/scripts/npm-audit-baseline.cjs +376 -0
  418. package/scripts/prompt-injection-scan.sh +22 -0
  419. package/scripts/require-issue-link-policy.cjs +16 -1
  420. package/scripts/workflow-size.cjs +139 -0
  421. package/skills/gsd-cleanup/SKILL.md +1 -0
  422. package/skills/gsd-code-review/SKILL.md +2 -1
  423. package/skills/gsd-complete-milestone/SKILL.md +1 -0
  424. package/skills/gsd-config/SKILL.md +1 -0
  425. package/skills/gsd-debug/SKILL.md +1 -0
  426. package/skills/gsd-execute-phase/SKILL.md +1 -1
  427. package/skills/gsd-graphify/SKILL.md +1 -0
  428. package/skills/gsd-health/SKILL.md +1 -0
  429. package/skills/gsd-mempalace-capture/SKILL.md +1 -0
  430. package/skills/gsd-mempalace-recall/SKILL.md +1 -0
  431. package/skills/gsd-new-milestone/SKILL.md +1 -0
  432. package/skills/gsd-new-project/SKILL.md +1 -0
  433. package/skills/gsd-next/SKILL.md +1 -0
  434. package/skills/gsd-ns-workflow/SKILL.md +1 -0
  435. package/skills/gsd-pause-work/SKILL.md +1 -0
  436. package/skills/gsd-phase/SKILL.md +2 -1
  437. package/skills/gsd-pr-branch/SKILL.md +1 -0
  438. package/skills/gsd-quick-batch/SKILL.md +105 -0
  439. package/skills/gsd-resume-work/SKILL.md +1 -0
  440. package/skills/gsd-review-backlog/SKILL.md +1 -0
  441. package/skills/gsd-settings/SKILL.md +2 -1
  442. package/skills/gsd-stats/SKILL.md +1 -0
  443. package/skills/gsd-surface/SKILL.md +18 -8
  444. package/skills/gsd-thread/SKILL.md +1 -0
  445. package/skills/gsd-workspace/SKILL.md +1 -0
  446. package/skills/gsd-workstreams/SKILL.md +1 -0
  447. package/vscode/package.json +1 -1
  448. package/gsd-core/templates/claude-md.md +0 -145
  449. package/gsd-core/templates/codebase/concerns.md +0 -310
  450. package/gsd-core/templates/codebase/conventions.md +0 -307
  451. package/gsd-core/templates/codebase/integrations.md +0 -280
  452. package/gsd-core/templates/codebase/structure.md +0 -285
  453. package/gsd-core/templates/codebase/testing.md +0 -480
  454. package/gsd-core/templates/debug-subagent-prompt.md +0 -91
  455. package/gsd-core/templates/discovery.md +0 -146
@@ -459,9 +459,18 @@ function normalizeNodePath(execPath, opts) {
459
459
  // survives the upgrade. Derive <prefix> from the path itself (more reliable
460
460
  // than HOMEBREW_PREFIX env — the path IS the install location) so every layout
461
461
  // is covered by one branch instead of one per known prefix (#2185).
462
+ //
463
+ // #4137: rewrite only when the symlink exists; otherwise fall through to the
464
+ // raw execPath, exactly like the mise and volta branches. A keg-only/versioned
465
+ // formula (node@24 installed but never `brew link`ed) has no <prefix>/bin/node
466
+ // at all, so the unconditional rewrite handed every managed hook a path that
467
+ // fails at invocation — a rewrite must never turn a working keg path into an
468
+ // immediately broken one.
462
469
  const homebrewMatch = normalizedForMatch.match(/^(.+)\/Cellar\/node(@\d+)?\/[^/]+\/bin\/node(\.exe)?$/i);
463
470
  if (homebrewMatch) {
464
- return `${homebrewMatch[1]}/bin/node${homebrewMatch[3] || ''}`;
471
+ const homebrewStable = `${homebrewMatch[1]}/bin/node${homebrewMatch[3] || ''}`;
472
+ if (existsSync(homebrewStable))
473
+ return homebrewStable;
465
474
  }
466
475
  // mise pins a concrete node version at <data>/installs/node/<ver>/bin/node
467
476
  // (Windows: <data>/installs/node/<ver>/node.exe). Node realpaths
@@ -802,16 +811,52 @@ function rewriteLegacyCodexHookBlock(content, absoluteRunner, opts) {
802
811
  });
803
812
  return { content: updated, changed };
804
813
  }
814
+ function _installEngineSymlinkGuard() {
815
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
816
+ const mod = require('./install-engine.cjs');
817
+ return mod;
818
+ }
805
819
  function reconcileCodexHooksJsonEvent(targetDir, eventName, opts = {}) {
806
820
  const hooksJsonPath = node_path_1.default.join(targetDir, 'hooks.json');
807
821
  const managedCommand = typeof opts.managedCommand === 'string' ? opts.managedCommand : null;
808
822
  const commandWindows = typeof opts.commandWindows === 'string' ? opts.commandWindows : null;
809
823
  const matcher = typeof opts.matcher === 'string' ? opts.matcher : undefined;
810
824
  const timeout = typeof opts.timeout === 'number' ? opts.timeout : undefined;
825
+ // #2586 Major 2: every Codex hooks.json writer funnels through this one
826
+ // function, and atomicWriteFileSync's final step is a rename(2) onto
827
+ // `hooksJsonPath` — which, when that path is a symlink, REPLACES the
828
+ // symlink with a plain file rather than writing through it. Refuse (with
829
+ // the same GSD_ALLOW_SYMLINKED_DEST opt-in every other install call site
830
+ // honors) before reading or writing, so a symlinked hooks.json is neither
831
+ // silently destroyed nor left the caller no escape hatch.
832
+ const symlinkGuard = _installEngineSymlinkGuard();
833
+ // The path this function actually reads/writes. Defaults to the nominal
834
+ // hooks.json path; reassigned below to the symlink's real target when the
835
+ // opt-in is active, so the write lands on the file the user's symlink
836
+ // points at instead of clobbering the symlink itself (see note below).
837
+ let effectiveHooksJsonPath = hooksJsonPath;
838
+ if (node_fs_1.default.existsSync(hooksJsonPath) && node_fs_1.default.lstatSync(hooksJsonPath).isSymbolicLink()) {
839
+ if (symlinkGuard.hasExistingSymlinkBetween(targetDir, hooksJsonPath, {
840
+ allowOptInFollow: symlinkGuard.isSymlinkedDestOptIn(),
841
+ })) {
842
+ throw new Error(`hooks.json at "${hooksJsonPath}" contains a symlink the install root "${targetDir}" does not trust — ` +
843
+ 'refusing to read or write it. If this is an intentional user-owned symlink layout, re-run with ' +
844
+ 'GSD_ALLOW_SYMLINKED_DEST=1.');
845
+ }
846
+ // hasExistingSymlinkBetween returned false only because the opt-in is
847
+ // active (a symlinked leaf always trips it otherwise) — so this IS a
848
+ // symlink and we are cleared to follow it. atomicWriteFileSync's final
849
+ // step is a rename(2) onto its target, which REPLACES an existing
850
+ // symlink at that path rather than writing through it; resolving to the
851
+ // real path here makes the read AND the write operate on the symlink's
852
+ // target, leaving the symlink itself untouched, matching what "follow"
853
+ // is supposed to mean.
854
+ effectiveHooksJsonPath = node_fs_1.default.realpathSync(hooksJsonPath);
855
+ }
811
856
  let parsed = {};
812
857
  let currentContent = null;
813
- if (node_fs_1.default.existsSync(hooksJsonPath)) {
814
- const raw = node_fs_1.default.readFileSync(hooksJsonPath, 'utf8');
858
+ if (node_fs_1.default.existsSync(effectiveHooksJsonPath)) {
859
+ const raw = node_fs_1.default.readFileSync(effectiveHooksJsonPath, 'utf8');
815
860
  currentContent = raw;
816
861
  if (raw.trim()) {
817
862
  try {
@@ -845,6 +890,12 @@ function reconcileCodexHooksJsonEvent(targetDir, eventName, opts = {}) {
845
890
  }
846
891
  parsed['hooks'] = hookTable;
847
892
  const eventEntries = Array.isArray(hookTable[eventName]) ? hookTable[eventName] : [];
893
+ // Minor 5 (#2586 review): an event key the user already had, already
894
+ // holding an empty array, must survive removal as an empty array — not be
895
+ // deleted outright. Deleting is only correct when OUR removal is what
896
+ // emptied a previously non-empty array. Tracked before the loop below can
897
+ // mutate anything.
898
+ const wasArrayEmpty = Array.isArray(hookTable[eventName]) && eventEntries.length === 0;
848
899
  let removedLegacy = false;
849
900
  const sanitizedEntries = [];
850
901
  for (const entry of eventEntries) {
@@ -886,6 +937,11 @@ function reconcileCodexHooksJsonEvent(targetDir, eventName, opts = {}) {
886
937
  if (sanitizedEntries.length > 0) {
887
938
  hookTable[eventName] = sanitizedEntries;
888
939
  }
940
+ else if (wasArrayEmpty) {
941
+ // Nothing of ours was ever here to remove — preserve the user's own
942
+ // empty array exactly as found (Minor 5).
943
+ hookTable[eventName] = [];
944
+ }
889
945
  else {
890
946
  delete hookTable[eventName];
891
947
  }
@@ -898,7 +954,7 @@ function reconcileCodexHooksJsonEvent(targetDir, eventName, opts = {}) {
898
954
  const changed = currentContent !== nextContent;
899
955
  const shouldWrite = changed && (currentContent !== null || Object.keys(parsed).length > 0);
900
956
  if (shouldWrite) {
901
- atomicWriteFileSync(hooksJsonPath, nextContent, 'utf8');
957
+ atomicWriteFileSync(effectiveHooksJsonPath, nextContent, 'utf8');
902
958
  }
903
959
  return { changed: changed || removedLegacy, wrote: shouldWrite, path: hooksJsonPath };
904
960
  }
@@ -1015,6 +1071,136 @@ function removeCodexHooksJsonEvent(targetDir, eventName) {
1015
1071
  function removeCodexHooksJsonSessionStart(targetDir) {
1016
1072
  return reconcileCodexHooksJsonSessionStart(targetDir, { managedCommand: null });
1017
1073
  }
1074
+ // Literal, version-stable markers every shipped gsd-context-monitor.js
1075
+ // carries. Stable across the {{GSD_VERSION}} and runtime-path substitutions
1076
+ // the Codex copy step applies (#2586 design doc "Ownership check" — a raw
1077
+ // content hash would differ per runtime/version by construction, so a marker
1078
+ // check is used instead of manifest-membership, which has a bootstrap gap on
1079
+ // the exact case that matters most: a pre-#2586 install's manifest never
1080
+ // recorded this file at all).
1081
+ const CODEX_CONTEXT_MONITOR_OWNERSHIP_MARKERS = [
1082
+ '#!/usr/bin/env node',
1083
+ '// gsd-hook-version:',
1084
+ '// Context Monitor - PostToolUse/AfterTool hook',
1085
+ ];
1086
+ function isGsdOwnedCodexContextMonitorScript(filePath) {
1087
+ let content;
1088
+ try {
1089
+ content = node_fs_1.default.readFileSync(filePath, 'utf8');
1090
+ }
1091
+ catch {
1092
+ return false;
1093
+ }
1094
+ // The .cmd shim (buildCodexHookWindowsShimIR) is a tiny generated batch
1095
+ // wrapper, not the JS file itself — it never carries the JS markers above,
1096
+ // so it gets its own narrower, still-specific signature: the exact
1097
+ // "@ECHO OFF" / "@SETLOCAL" preamble the shim generator emits, invoking a
1098
+ // script path that ends in gsd-context-monitor.js.
1099
+ if (filePath.endsWith('.cmd')) {
1100
+ return content.startsWith('@ECHO OFF') && content.includes('@SETLOCAL')
1101
+ && /gsd-context-monitor\.js/.test(content);
1102
+ }
1103
+ return CODEX_CONTEXT_MONITOR_OWNERSHIP_MARKERS.every((marker) => content.includes(marker));
1104
+ }
1105
+ /**
1106
+ * Scan every event in hooks.json for a surviving reference to the
1107
+ * context-monitor script or its Windows .cmd shim, by basename — not scoped
1108
+ * to CODEX_EXTENDED_HOOK_EVENTS, so a user who hand-registered it under an
1109
+ * unrelated event key is still detected as "referenced" and the script is
1110
+ * preserved.
1111
+ */
1112
+ function hooksJsonReferencesCodexContextMonitor(targetDir) {
1113
+ const hooksJsonPath = node_path_1.default.join(targetDir, 'hooks.json');
1114
+ if (!node_fs_1.default.existsSync(hooksJsonPath))
1115
+ return false;
1116
+ let raw;
1117
+ try {
1118
+ raw = node_fs_1.default.readFileSync(hooksJsonPath, 'utf8');
1119
+ }
1120
+ catch {
1121
+ return true; // unreadable — conservatively assume referenced, never delete
1122
+ }
1123
+ if (!raw.trim())
1124
+ return false;
1125
+ let parsed;
1126
+ try {
1127
+ parsed = JSON.parse(raw);
1128
+ }
1129
+ catch {
1130
+ return true; // unparseable — conservatively assume referenced
1131
+ }
1132
+ if (!parsed || typeof parsed !== 'object')
1133
+ return false;
1134
+ const hooks = parsed['hooks'];
1135
+ const table = hooks && typeof hooks === 'object' && !Array.isArray(hooks)
1136
+ ? hooks
1137
+ : parsed;
1138
+ for (const key of Object.keys(table)) {
1139
+ const entries = table[key];
1140
+ if (!Array.isArray(entries))
1141
+ continue;
1142
+ for (const entry of entries) {
1143
+ if (!entry || typeof entry !== 'object')
1144
+ continue;
1145
+ const entryHooks = entry['hooks'];
1146
+ const hookList = Array.isArray(entryHooks) ? entryHooks : [entry];
1147
+ for (const hook of hookList) {
1148
+ if (!hook || typeof hook !== 'object')
1149
+ continue;
1150
+ const values = [
1151
+ hook['command'],
1152
+ hook['commandWindows'],
1153
+ ];
1154
+ for (const value of values) {
1155
+ if (typeof value === 'string' && /gsd-context-monitor(\.js|\.cmd)?/.test(value)) {
1156
+ return true;
1157
+ }
1158
+ }
1159
+ }
1160
+ }
1161
+ }
1162
+ return false;
1163
+ }
1164
+ /**
1165
+ * #2586 must-have #4/#8: after hooks.json registrations for
1166
+ * CODEX_EXTENDED_HOOK_EVENTS have been reconciled away (by the caller, via
1167
+ * removeCodexHooksJsonEvent), delete `hooks/gsd-context-monitor.js` and its
1168
+ * `.cmd` shim ONLY when (a) no surviving hooks.json registration under ANY
1169
+ * event still references either basename, and (b) the on-disk file carries
1170
+ * GSD's own ownership markers (a user's hand-edited or unrelated file at that
1171
+ * path is left alone). Each file is deleted independently — a failure
1172
+ * deleting one is reported as a warning and never rolls back the (already
1173
+ * safe, already-written) hooks.json deregistration the caller performed
1174
+ * first.
1175
+ */
1176
+ function cleanupOrphanedCodexContextMonitorScript(targetDir) {
1177
+ const result = { deleted: [], warnings: [], stillReferenced: false };
1178
+ if (hooksJsonReferencesCodexContextMonitor(targetDir)) {
1179
+ result.stillReferenced = true;
1180
+ return result;
1181
+ }
1182
+ const candidates = [
1183
+ node_path_1.default.join(targetDir, 'hooks', 'gsd-context-monitor.js'),
1184
+ node_path_1.default.join(targetDir, 'hooks', 'gsd-context-monitor.cmd'),
1185
+ ];
1186
+ for (const candidate of candidates) {
1187
+ if (!node_fs_1.default.existsSync(candidate))
1188
+ continue;
1189
+ if (!isGsdOwnedCodexContextMonitorScript(candidate))
1190
+ continue;
1191
+ try {
1192
+ node_fs_1.default.unlinkSync(candidate);
1193
+ result.deleted.push(candidate);
1194
+ }
1195
+ catch (err) {
1196
+ result.warnings.push({
1197
+ path: candidate,
1198
+ reason: err && err.message ? err.message : String(err),
1199
+ });
1200
+ }
1201
+ }
1202
+ return result;
1203
+ }
1018
1204
  function buildHookCommand(configDir, hookName, opts) {
1019
1205
  if (!opts)
1020
1206
  opts = {};
@@ -1311,6 +1497,128 @@ function reconcileCursorHooksJson(hooksJsonPath, managedEntries) {
1311
1497
  }
1312
1498
  return { changed: changed, wrote: shouldWrite, path: hooksJsonPath };
1313
1499
  }
1500
+ /**
1501
+ * Stage the `hooks/lib/` helpers a set of staged hook scripts require, walking
1502
+ * the require graph TRANSITIVELY to a fixed point.
1503
+ *
1504
+ * Extracted from writeCursorHooksJson (#3911 review, commit 704859e9c) so the
1505
+ * reduced Codex hook bundle can share one implementation instead of growing a
1506
+ * second, divergent copy (#4087 / #4098). Both reduced bundles hand-pick which
1507
+ * hook SCRIPTS they ship, and neither can hand-pick their helpers correctly for
1508
+ * long: `hooks/lib/hook-exit.js` requires `./cli-exit.js`, which requires
1509
+ * `./exit-code-registry.js` — with NO `./lib/` prefix, because from inside
1510
+ * `lib/` the sibling is already local. A single-pass scan for the `./lib/…`
1511
+ * spelling used FROM a hook script stages hook-exit.js and stops, and the
1512
+ * installed hook then dies on MODULE_NOT_FOUND at load, before its own
1513
+ * try/catch, on every event it is registered for.
1514
+ *
1515
+ * Scans CONTENT rather than paths so a caller can seed from whatever it staged,
1516
+ * transformed or not, without this helper knowing the caller's layout.
1517
+ *
1518
+ * @returns the lib filenames actually staged, in staging order.
1519
+ */
1520
+ function stageTransitiveHookLibs(opts) {
1521
+ const { seedSources, srcLibDir, destLibDir, runtimeLabel, transform } = opts;
1522
+ const requiredLibFiles = new Set();
1523
+ const scannedLibFiles = new Set();
1524
+ const staged = [];
1525
+ // `./X` means DIFFERENT things depending on where the scanned file lives, and
1526
+ // conflating them stages the wrong file. From a hook SCRIPT in hooks/, a bare
1527
+ // `./X` is a sibling hook-level artifact — Codex's gsd-check-update-worker.js
1528
+ // requires `./managed-hooks-registry.cjs`, which lives in hooks/, not
1529
+ // hooks/lib/ — so only the explicit `./lib/X` spelling is a lib requirement.
1530
+ // From inside a LIB file, the sibling is already local, so `./X` IS a lib
1531
+ // requirement (hook-exit.js -> ./cli-exit.js -> ./exit-code-registry.js); that
1532
+ // is the case 704859e9c added and it must keep working. Cursor never exposed
1533
+ // the difference because none of its staged scripts has a bare sibling
1534
+ // require; Codex's does, and the fail-loud guard below caught it immediately
1535
+ // by demanding managed-hooks-registry.cjs out of hooks/dist/lib.
1536
+ // Fresh per call: a module-level /g regex carries lastIndex across calls and
1537
+ // would silently skip matches on the second install in one process.
1538
+ const seedRequireRe = /require\(\s*['"]\.\/lib\/([A-Za-z0-9._-]+)['"]\s*\)/g;
1539
+ const libRequireRe = /require\(\s*['"]\.\/(?:lib\/)?([A-Za-z0-9._-]+)['"]\s*\)/g;
1540
+ // A NESTED helper path is outside the flat layout hooks/lib/ has and the
1541
+ // build emits, and the character classes above cannot express it — so it
1542
+ // would be a SILENT miss, staging nothing and shipping a hook that dies at
1543
+ // load. Detected separately and refused loudly instead: a silent miss is the
1544
+ // failure mode this whole function exists to remove (review of #4087).
1545
+ const nestedRequireRe = /require\(\s*['"]\.\/lib\/[A-Za-z0-9._-]+\/[^'"]*['"]\s*\)/;
1546
+ const scanForLibRequires = (source, fromLib) => {
1547
+ if (nestedRequireRe.test(source)) {
1548
+ throw new Error(`A staged ${runtimeLabel} hook requires a NESTED hooks/lib path. hooks/lib/ is flat and `
1549
+ + 'this stager only resolves flat helper names, so the nested helper would never be '
1550
+ + 'staged and the hook would throw MODULE_NOT_FOUND at load. Flatten the helper or '
1551
+ + 'extend this stager deliberately.');
1552
+ }
1553
+ const re = fromLib ? libRequireRe : seedRequireRe;
1554
+ re.lastIndex = 0;
1555
+ let m;
1556
+ while ((m = re.exec(source)) !== null) {
1557
+ const candidate = m[1];
1558
+ // A capture with no alphanumeric character is not a module name — it is
1559
+ // prose. This scan reads whole file text, comments included, and
1560
+ // hooks/lib/injection-patterns.js's own header documents this mechanism
1561
+ // with the literal string `require('./lib/...')`, which captures `...`
1562
+ // and would send the resolver hunting for `hooks/lib/...` and fail the
1563
+ // install (measured; that helper is not staged for either reduced bundle
1564
+ // today, so it is latent rather than live).
1565
+ //
1566
+ // KNOWN LIMIT, disclosed rather than papered over: this does NOT make the
1567
+ // scan comment-aware. A comment naming a REAL helper — `require(
1568
+ // './lib/git-cmd.js')` in prose — still registers it and would over-stage
1569
+ // that helper. Closing that needs a comment-stripping pass; the
1570
+ // line-based stripper in scripts/lint-hooks-runtime-build-seam.cjs is the
1571
+ // precedent (its header explains why the naive two-regex strip corrupts
1572
+ // these very files), but promoting a lint-script helper into installer
1573
+ // runtime code is a larger change than this fix.
1574
+ if (!/[A-Za-z0-9]/.test(candidate))
1575
+ continue;
1576
+ requiredLibFiles.add(candidate);
1577
+ }
1578
+ };
1579
+ for (const source of seedSources)
1580
+ scanForLibRequires(source, false);
1581
+ if (requiredLibFiles.size === 0)
1582
+ return staged;
1583
+ node_fs_1.default.mkdirSync(destLibDir, { recursive: true });
1584
+ // Iterate to a fixed point: staging a lib file can add MORE required lib
1585
+ // files (its own requires), which must themselves be staged and scanned.
1586
+ let libFile = [...requiredLibFiles].find((f) => !scannedLibFiles.has(f));
1587
+ while (libFile !== undefined) {
1588
+ scannedLibFiles.add(libFile);
1589
+ // Node's own extension resolution: `require('./lib/x')` is a valid, working
1590
+ // CommonJS spelling today, and matching only the extension-bearing form
1591
+ // resolved `x` literally, found nothing, and failed the install on a
1592
+ // legitimate require (review of #4087). Try the bare name first so an
1593
+ // extension-bearing capture still wins, then .js/.cjs.
1594
+ let resolvedName;
1595
+ for (const candidate of [libFile, `${libFile}.js`, `${libFile}.cjs`]) {
1596
+ if (node_fs_1.default.existsSync(node_path_1.default.join(srcLibDir, candidate))) {
1597
+ resolvedName = candidate;
1598
+ break;
1599
+ }
1600
+ }
1601
+ const libSrc = node_path_1.default.join(srcLibDir, resolvedName ?? libFile);
1602
+ if (resolvedName === undefined) {
1603
+ // FAIL LOUD. Skipping here would ship hook scripts whose top-level
1604
+ // require() throws before their own try/catch, wedging every session —
1605
+ // and the install would still exit 0, so nobody would know until a user
1606
+ // hit it. A missing helper source is a packaging bug; surface it.
1607
+ throw new Error(`hooks/lib/${libFile} is required by a staged ${runtimeLabel} hook but is missing from ${srcLibDir}. `
1608
+ + 'Installing would ship a hook that throws MODULE_NOT_FOUND at load.');
1609
+ }
1610
+ let libContent = node_fs_1.default.readFileSync(libSrc, 'utf8');
1611
+ if (transform)
1612
+ libContent = transform(libContent);
1613
+ // Written under its RESOLVED name so an extensionless require still lands a
1614
+ // file Node can resolve at the destination.
1615
+ node_fs_1.default.writeFileSync(node_path_1.default.join(destLibDir, resolvedName), libContent);
1616
+ staged.push(resolvedName);
1617
+ scanForLibRequires(libContent, true);
1618
+ libFile = [...requiredLibFiles].find((f) => !scannedLibFiles.has(f));
1619
+ }
1620
+ return staged;
1621
+ }
1314
1622
  function writeCursorHooksJson(targetDir, src, opts) {
1315
1623
  opts = opts || {};
1316
1624
  const hooksDir = node_path_1.default.join(targetDir, 'hooks');
@@ -1348,43 +1656,13 @@ function writeCursorHooksJson(targetDir, src, opts) {
1348
1656
  // "./lib/X" requires, and every lib file staged is itself scanned for further
1349
1657
  // "./lib/X" OR bare "./X" (sibling-within-lib) requires, so the requirement
1350
1658
  // graph is derived to a fixed point instead of one hand-tuned level deep.
1351
- const requiredLibFiles = new Set();
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;
1356
- let m;
1357
- while ((m = libRequireRe.exec(source)) !== null)
1358
- requiredLibFiles.add(m[1]);
1359
- }
1360
- for (const script of installedScripts) {
1361
- scanForLibRequires(node_fs_1.default.readFileSync(node_path_1.default.join(hooksDir, script), 'utf8'));
1362
- }
1363
- if (requiredLibFiles.size > 0) {
1364
- const srcLibDir = node_path_1.default.join(srcHooksDir, 'lib');
1365
- const destLibDir = node_path_1.default.join(hooksDir, 'lib');
1366
- node_fs_1.default.mkdirSync(destLibDir, { recursive: true });
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);
1372
- const libSrc = node_path_1.default.join(srcLibDir, libFile);
1373
- if (!node_fs_1.default.existsSync(libSrc)) {
1374
- // FAIL LOUD. Skipping here would ship hook scripts whose top-level
1375
- // require() throws before their own try/catch, wedging every session —
1376
- // and the install would still exit 0, so nobody would know until a user
1377
- // hit it. A missing helper source is a packaging bug; surface it.
1378
- throw new Error(`hooks/lib/${libFile} is required by a staged Cursor hook but is missing from ${srcLibDir}. `
1379
- + 'Installing would ship a hook that throws MODULE_NOT_FOUND at load.');
1380
- }
1381
- let libContent = node_fs_1.default.readFileSync(libSrc, 'utf8');
1382
- libContent = libContent.replace(/gsd:/gi, 'gsd-');
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));
1386
- }
1387
- }
1659
+ stageTransitiveHookLibs({
1660
+ seedSources: [...installedScripts].map((script) => node_fs_1.default.readFileSync(node_path_1.default.join(hooksDir, script), 'utf8')),
1661
+ srcLibDir: node_path_1.default.join(srcHooksDir, 'lib'),
1662
+ destLibDir: node_path_1.default.join(hooksDir, 'lib'),
1663
+ runtimeLabel: 'Cursor',
1664
+ transform: (content) => content.replace(/gsd:/gi, 'gsd-'),
1665
+ });
1388
1666
  // #2717: write the CommonJS marker into hooks/ alongside the staged .js
1389
1667
  // scripts. Cursor sets skipSharedHooksInstall, so it never reaches
1390
1668
  // installSharedHooksBundle (the only other writer of this marker); without
@@ -1587,6 +1865,26 @@ function writeWindsurfHooksJson(targetDir, src, opts) {
1587
1865
  installedScripts.add(script);
1588
1866
  }
1589
1867
  }
1868
+ // Stage the hooks/lib/ helpers these scripts require (#4087 review). Windsurf
1869
+ // sets hostBehaviors.skipSharedHooksInstall, so like Cursor it never reaches
1870
+ // installSharedHooksBundle — the only other stager of hooks/lib — and it was
1871
+ // staging neither. Both Cascade guards require helpers at module load:
1872
+ // gsd-windsurf-pre-write.js requires ./lib/hook-exit.js and ./lib/git-probe.js,
1873
+ // gsd-windsurf-pre-command.js requires ./lib/hook-exit.js. Measured against a
1874
+ // real `--windsurf --global` install before this call existed: the installer
1875
+ // exited 0, hooks/ held only the two scripts, and running either one exited 1
1876
+ // with "Cannot find module './lib/hook-exit.js'" — the same failure #4087
1877
+ // reports for Codex, on every pre_write_code / pre_run_command event.
1878
+ //
1879
+ // The transform matches the one applied to the scripts above: a helper must be
1880
+ // rewritten the same way as its caller or the two disagree on the spelling.
1881
+ stageTransitiveHookLibs({
1882
+ seedSources: [...installedScripts].map((script) => node_fs_1.default.readFileSync(node_path_1.default.join(hooksDir, script), 'utf8')),
1883
+ srcLibDir: node_path_1.default.join(srcHooksDir, 'lib'),
1884
+ destLibDir: node_path_1.default.join(hooksDir, 'lib'),
1885
+ runtimeLabel: 'Windsurf',
1886
+ transform: (content) => content.replace(/gsd:/gi, 'gsd-'),
1887
+ });
1590
1888
  // #2717: write the CommonJS marker into hooks/ alongside the staged .js
1591
1889
  // scripts. Windsurf sets skipSharedHooksInstall, so it never reaches
1592
1890
  // installSharedHooksBundle (the only other writer of this marker); without
@@ -1737,6 +2035,37 @@ function applySettingsJsonHooks(settings, opts) {
1737
2035
  if (!settings.hooks.SessionStart) {
1738
2036
  settings.hooks.SessionStart = [];
1739
2037
  }
2038
+ // #3981: Claude Code treats a timed-out hook as NON-blocking — the tool
2039
+ // call continues through the normal permission flow. The blocking
2040
+ // PreToolUse guards therefore need a budget a host stall cannot exceed,
2041
+ // not one sized to the hook's own ~0.1 s runtime. Observed stalls reached
2042
+ // 84.3 s; 120 s is the top of the issue's prescribed 60–120 range and
2043
+ // returns every observed verdict. Registration below uses this constant,
2044
+ // and the migration pass right here raises existing managed entries.
2045
+ const BLOCKING_GUARD_TIMEOUT_S = 120;
2046
+ const blockingGuardNames = [
2047
+ 'gsd-prompt-guard',
2048
+ 'gsd-workflow-guard',
2049
+ 'gsd-worktree-path-guard',
2050
+ 'gsd-agent-isolation-guard',
2051
+ 'gsd-write-guard',
2052
+ 'gsd-secret-read-guard',
2053
+ 'gsd-validate-commit',
2054
+ ];
2055
+ for (const entries of Object.values(settings.hooks)) {
2056
+ if (!Array.isArray(entries))
2057
+ continue;
2058
+ for (const entry of entries) {
2059
+ if (!entry || !Array.isArray(entry.hooks))
2060
+ continue;
2061
+ for (const h of entry.hooks) {
2062
+ if (blockingGuardNames.some((name) => referencesHook(h, name)) &&
2063
+ h.timeout === 5) {
2064
+ h.timeout = BLOCKING_GUARD_TIMEOUT_S;
2065
+ }
2066
+ }
2067
+ }
2068
+ }
1740
2069
  const hasGsdUpdateHook = settings.hooks.SessionStart.some((entry) => entry.hooks && entry.hooks.some((h) => referencesHook(h, 'gsd-check-update')));
1741
2070
  // Guard: only register if the hook file was actually installed (#1754).
1742
2071
  // When hooks/dist/ is missing from the npm package (as in v1.32.0), the
@@ -1817,7 +2146,7 @@ function applySettingsJsonHooks(settings, opts) {
1817
2146
  {
1818
2147
  type: 'command',
1819
2148
  command: promptGuardCommand,
1820
- timeout: 5
2149
+ timeout: BLOCKING_GUARD_TIMEOUT_S
1821
2150
  }
1822
2151
  ]
1823
2152
  });
@@ -1894,7 +2223,7 @@ function applySettingsJsonHooks(settings, opts) {
1894
2223
  {
1895
2224
  type: 'command',
1896
2225
  command: workflowGuardCommand,
1897
- timeout: 5
2226
+ timeout: BLOCKING_GUARD_TIMEOUT_S
1898
2227
  }
1899
2228
  ]
1900
2229
  });
@@ -1919,7 +2248,7 @@ function applySettingsJsonHooks(settings, opts) {
1919
2248
  {
1920
2249
  type: 'command',
1921
2250
  command: worktreePathGuardCommand,
1922
- timeout: 5
2251
+ timeout: BLOCKING_GUARD_TIMEOUT_S
1923
2252
  }
1924
2253
  ]
1925
2254
  });
@@ -1949,7 +2278,7 @@ function applySettingsJsonHooks(settings, opts) {
1949
2278
  {
1950
2279
  type: 'command',
1951
2280
  command: agentIsolationGuardCommand,
1952
- timeout: 5
2281
+ timeout: BLOCKING_GUARD_TIMEOUT_S
1953
2282
  }
1954
2283
  ]
1955
2284
  });
@@ -1976,7 +2305,7 @@ function applySettingsJsonHooks(settings, opts) {
1976
2305
  {
1977
2306
  type: 'command',
1978
2307
  command: writeGuardCommand,
1979
- timeout: 5
2308
+ timeout: BLOCKING_GUARD_TIMEOUT_S
1980
2309
  }
1981
2310
  ]
1982
2311
  });
@@ -1985,6 +2314,33 @@ function applySettingsJsonHooks(settings, opts) {
1985
2314
  else if (!hasWriteGuardHook && !node_fs_1.default.existsSync(writeGuardFile)) {
1986
2315
  console.warn(` ${yellow}⚠${reset} Skipped write guard hook — gsd-write-guard.js not found at target`);
1987
2316
  }
2317
+ // Configure PreToolUse hook for secret-file read protection (#4221).
2318
+ // Hard-blocks Read/Grep/Bash reads of .env, .env.<suffix> and .secrets.
2319
+ // Replaces the Read(.env*) permission deny rules the installer used to
2320
+ // write (#768): on Claude Code >= 2.1.259 ANY Read() deny rule makes every
2321
+ // `cd DIR && grep …` compound prompt for approval, even in auto mode; a
2322
+ // hook denial is not a permission rule and never arms that check.
2323
+ const secretReadGuardCommand = isGlobal
2324
+ ? buildHookCommand(targetDir, 'gsd-secret-read-guard.js', hookOpts)
2325
+ : localCmd('gsd-secret-read-guard.js');
2326
+ const hasSecretReadGuardHook = settings.hooks[preToolEvent].some((entry) => entry.hooks && entry.hooks.some((h) => referencesHook(h, 'gsd-secret-read-guard')));
2327
+ const secretReadGuardFile = node_path_1.default.join(targetDir, 'hooks', 'gsd-secret-read-guard.js');
2328
+ if (!hasSecretReadGuardHook && node_fs_1.default.existsSync(secretReadGuardFile) && secretReadGuardCommand) {
2329
+ settings.hooks[preToolEvent].push({
2330
+ matcher: 'Read|Grep|Bash',
2331
+ hooks: [
2332
+ {
2333
+ type: 'command',
2334
+ command: secretReadGuardCommand,
2335
+ timeout: BLOCKING_GUARD_TIMEOUT_S
2336
+ }
2337
+ ]
2338
+ });
2339
+ console.log(` ${green}✓${reset} Configured secret read guard hook (.env / .secrets read protection)`);
2340
+ }
2341
+ else if (!hasSecretReadGuardHook && !node_fs_1.default.existsSync(secretReadGuardFile)) {
2342
+ console.warn(` ${yellow}⚠${reset} Skipped secret read guard hook — gsd-secret-read-guard.js not found at target`);
2343
+ }
1988
2344
  // Configure commit validation hook (Conventional Commits enforcement, opt-in)
1989
2345
  const validateCommitCommand = isGlobal
1990
2346
  ? buildHookCommand(targetDir, 'gsd-validate-commit.sh', hookOpts)
@@ -2001,7 +2357,7 @@ function applySettingsJsonHooks(settings, opts) {
2001
2357
  {
2002
2358
  type: 'command',
2003
2359
  command: validateCommitCommand,
2004
- timeout: 5
2360
+ timeout: BLOCKING_GUARD_TIMEOUT_S
2005
2361
  }
2006
2362
  ]
2007
2363
  });
@@ -2330,6 +2686,7 @@ function buildKimiHooksTomlBlock(targetDir, opts) {
2330
2686
  { event: 'PreToolUse', command: cmd('gsd-read-guard.js'), matcher: 'WriteFile|StrReplaceFile', timeout: 5 },
2331
2687
  { event: 'PreToolUse', command: cmd('gsd-worktree-path-guard.js'), matcher: 'WriteFile|StrReplaceFile', timeout: 5 },
2332
2688
  { event: 'PreToolUse', command: cmd('gsd-write-guard.js'), matcher: 'WriteFile', timeout: 5 },
2689
+ { event: 'PreToolUse', command: cmd('gsd-secret-read-guard.js'), matcher: 'ReadFile|Grep|Shell', timeout: 5 },
2333
2690
  { event: 'PreToolUse', command: cmd('gsd-workflow-guard.js'), matcher: 'Shell|WriteFile|StrReplaceFile', timeout: 5 },
2334
2691
  { event: 'PreToolUse', command: cmd('gsd-validate-commit.sh'), matcher: 'Shell', timeout: 5 },
2335
2692
  // PostToolUse
@@ -2521,6 +2878,9 @@ module.exports = {
2521
2878
  removeCodexHooksJsonEvent,
2522
2879
  removeCodexHooksJsonSessionStart,
2523
2880
  buildCodexHookWindowsShimIR,
2881
+ cleanupOrphanedCodexContextMonitorScript,
2882
+ isGsdOwnedCodexContextMonitorScript,
2883
+ hooksJsonReferencesCodexContextMonitor,
2524
2884
  // Codex TOML
2525
2885
  buildCodexHookBlock,
2526
2886
  rewriteLegacyCodexHookBlock,
@@ -2532,6 +2892,7 @@ module.exports = {
2532
2892
  KIMI_HOOKS_TOML_MARKER_BEGIN,
2533
2893
  KIMI_HOOKS_TOML_MARKER_END,
2534
2894
  // Shared
2895
+ stageTransitiveHookLibs,
2535
2896
  buildHookCommand,
2536
2897
  applySettingsJsonHooks,
2537
2898
  referencesHook,