@opengsd/gsd-core 1.13.0 → 1.15.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 (441) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.ja-JP.md +3 -3
  4. package/README.ko-KR.md +3 -3
  5. package/README.pt-BR.md +3 -3
  6. package/README.zh-CN.md +3 -3
  7. package/agents/gsd-advisor-researcher.compact.md +85 -0
  8. package/agents/gsd-ai-researcher.compact.md +96 -0
  9. package/agents/gsd-assumptions-analyzer.compact.md +81 -0
  10. package/agents/gsd-code-fixer.compact.md +459 -0
  11. package/agents/gsd-code-fixer.md +9 -8
  12. package/agents/gsd-code-reviewer.compact.md +269 -0
  13. package/agents/gsd-code-reviewer.md +15 -3
  14. package/agents/gsd-codebase-mapper.compact.md +760 -0
  15. package/agents/gsd-debug-session-manager.compact.md +360 -0
  16. package/agents/gsd-debug-session-manager.md +17 -2
  17. package/agents/gsd-debugger.md +2 -2
  18. package/agents/gsd-doc-classifier.compact.md +192 -0
  19. package/agents/gsd-doc-synthesizer.compact.md +200 -0
  20. package/agents/gsd-doc-verifier.compact.md +143 -0
  21. package/agents/gsd-doc-writer.compact.md +440 -0
  22. package/agents/gsd-dom-verifier.compact.md +138 -0
  23. package/agents/gsd-domain-researcher.compact.md +141 -0
  24. package/agents/gsd-eval-auditor.compact.md +160 -0
  25. package/agents/gsd-eval-auditor.md +1 -1
  26. package/agents/gsd-eval-planner.compact.md +137 -0
  27. package/agents/gsd-executor.md +13 -8
  28. package/agents/gsd-framework-selector.compact.md +82 -0
  29. package/agents/gsd-integration-checker.compact.md +245 -0
  30. package/agents/gsd-intel-updater.compact.md +226 -0
  31. package/agents/gsd-intel-updater.md +1 -1
  32. package/agents/gsd-mempalace-curator.compact.md +45 -0
  33. package/agents/gsd-nyquist-auditor.compact.md +179 -0
  34. package/agents/gsd-pattern-mapper.compact.md +275 -0
  35. package/agents/gsd-phase-researcher.md +19 -11
  36. package/agents/gsd-plan-checker.md +8 -7
  37. package/agents/gsd-planner.md +12 -8
  38. package/agents/gsd-project-researcher.compact.md +587 -0
  39. package/agents/gsd-project-researcher.md +1 -1
  40. package/agents/gsd-research-synthesizer.compact.md +212 -0
  41. package/agents/gsd-research-synthesizer.md +1 -1
  42. package/agents/gsd-roadmapper.compact.md +454 -0
  43. package/agents/gsd-roadmapper.md +13 -0
  44. package/agents/gsd-security-auditor.compact.md +162 -0
  45. package/agents/gsd-ui-auditor.compact.md +404 -0
  46. package/agents/gsd-ui-auditor.md +155 -17
  47. package/agents/gsd-ui-checker.compact.md +277 -0
  48. package/agents/gsd-ui-researcher.compact.md +282 -0
  49. package/agents/gsd-ui-researcher.md +1 -1
  50. package/agents/gsd-user-profiler.compact.md +108 -0
  51. package/agents/gsd-verifier.md +10 -9
  52. package/bin/install.js +848 -163
  53. package/commands/gsd/autonomous.md +2 -2
  54. package/commands/gsd/capture.md +1 -1
  55. package/commands/gsd/cleanup.md +1 -0
  56. package/commands/gsd/code-review.md +2 -1
  57. package/commands/gsd/complete-milestone.md +1 -0
  58. package/commands/gsd/config.md +1 -0
  59. package/commands/gsd/debug.md +1 -0
  60. package/commands/gsd/graphify.md +1 -0
  61. package/commands/gsd/health.md +1 -0
  62. package/commands/gsd/mempalace-capture.md +8 -3
  63. package/commands/gsd/mempalace-recall.md +1 -0
  64. package/commands/gsd/new-milestone.md +1 -0
  65. package/commands/gsd/new-project.md +1 -0
  66. package/commands/gsd/next.md +1 -0
  67. package/commands/gsd/pause-work.md +1 -0
  68. package/commands/gsd/phase.md +1 -0
  69. package/commands/gsd/plan-review-convergence.md +6 -6
  70. package/commands/gsd/pr-branch.md +1 -0
  71. package/commands/gsd/progress.md +1 -1
  72. package/commands/gsd/quick-batch.md +1 -1
  73. package/commands/gsd/resume-work.md +1 -0
  74. package/commands/gsd/review-backlog.md +1 -0
  75. package/commands/gsd/review.md +2 -3
  76. package/commands/gsd/settings.md +2 -1
  77. package/commands/gsd/stats.md +1 -0
  78. package/commands/gsd/thread.md +1 -0
  79. package/commands/gsd/workspace.md +1 -0
  80. package/commands/gsd/workstreams.md +1 -0
  81. package/gsd-core/bin/check-latest-version.cjs +8 -3
  82. package/gsd-core/bin/gsd-tools.cjs +672 -146
  83. package/gsd-core/bin/lib/adr-parser.cjs +4 -2
  84. package/gsd-core/bin/lib/artifacts.cjs +2 -1
  85. package/gsd-core/bin/lib/audit.cjs +119 -34
  86. package/gsd-core/bin/lib/broken-windows.cjs +168 -49
  87. package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
  88. package/gsd-core/bin/lib/capability-loader.cjs +135 -1
  89. package/gsd-core/bin/lib/capability-registry.cjs +96 -189
  90. package/gsd-core/bin/lib/capability-source.cjs +19 -2
  91. package/gsd-core/bin/lib/capability-validator.cjs +14 -2
  92. package/gsd-core/bin/lib/check-command-router.cjs +213 -49
  93. package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
  94. package/gsd-core/bin/lib/codex-agent-toml.cjs +21 -25
  95. package/gsd-core/bin/lib/commands.cjs +823 -112
  96. package/gsd-core/bin/lib/config-loader.cjs +66 -4
  97. package/gsd-core/bin/lib/config.cjs +186 -45
  98. package/gsd-core/bin/lib/coverage.cjs +1 -1
  99. package/gsd-core/bin/lib/decisions.cjs +164 -45
  100. package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
  101. package/gsd-core/bin/lib/frontmatter.cjs +13 -0
  102. package/gsd-core/bin/lib/graphify.cjs +10 -2
  103. package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
  104. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +12 -1
  105. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
  106. package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
  107. package/gsd-core/bin/lib/init.cjs +614 -86
  108. package/gsd-core/bin/lib/install-engine.cjs +29 -3
  109. package/gsd-core/bin/lib/install-profiles.cjs +14 -0
  110. package/gsd-core/bin/lib/installer-migrations.cjs +41 -5
  111. package/gsd-core/bin/lib/loop-resolver.cjs +50 -31
  112. package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
  113. package/gsd-core/bin/lib/milestone.cjs +37 -13
  114. package/gsd-core/bin/lib/model-resolver.cjs +253 -53
  115. package/gsd-core/bin/lib/phase-command-router.cjs +16 -2
  116. package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
  117. package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
  118. package/gsd-core/bin/lib/phase-id.cjs +268 -27
  119. package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
  120. package/gsd-core/bin/lib/phase-locator.cjs +29 -10
  121. package/gsd-core/bin/lib/phase.cjs +393 -88
  122. package/gsd-core/bin/lib/plan-document.cjs +49 -1
  123. package/gsd-core/bin/lib/planning-document.cjs +459 -0
  124. package/gsd-core/bin/lib/planning-inspect.cjs +52 -19
  125. package/gsd-core/bin/lib/planning-snapshot.cjs +61 -12
  126. package/gsd-core/bin/lib/planning-workspace.cjs +57 -3
  127. package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
  128. package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
  129. package/gsd-core/bin/lib/probe-core.cjs +7 -1
  130. package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
  131. package/gsd-core/bin/lib/project-root.cjs +41 -2
  132. package/gsd-core/bin/lib/quick-batch.cjs +1 -1
  133. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
  134. package/gsd-core/bin/lib/research-store.cjs +11 -12
  135. package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
  136. package/gsd-core/bin/lib/review-lane-invocation.cjs +23 -0
  137. package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
  138. package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
  139. package/gsd-core/bin/lib/roadmap-command-router.cjs +12 -4
  140. package/gsd-core/bin/lib/roadmap-parser.cjs +219 -18
  141. package/gsd-core/bin/lib/roadmap-upgrade.cjs +1539 -13
  142. package/gsd-core/bin/lib/roadmap.cjs +356 -42
  143. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +310 -41
  144. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +15 -4
  145. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +13 -5
  146. package/gsd-core/bin/lib/runtime-homes.cjs +4 -0
  147. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -37
  148. package/gsd-core/bin/lib/runtime-name-policy.cjs +111 -1
  149. package/gsd-core/bin/lib/security.cjs +126 -7
  150. package/gsd-core/bin/lib/shell-command-projection.cjs +10 -6
  151. package/gsd-core/bin/lib/state-document.cjs +130 -28
  152. package/gsd-core/bin/lib/state-md-schema.cjs +21 -14
  153. package/gsd-core/bin/lib/state-transition.cjs +181 -30
  154. package/gsd-core/bin/lib/state.cjs +265 -27
  155. package/gsd-core/bin/lib/surface.cjs +77 -3
  156. package/gsd-core/bin/lib/task-command-router.cjs +12 -6
  157. package/gsd-core/bin/lib/tdd-red-evidence.cjs +78 -5
  158. package/gsd-core/bin/lib/uat-predicate.cjs +47 -4
  159. package/gsd-core/bin/lib/uat.cjs +9 -1
  160. package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
  161. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +100 -9
  162. package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
  163. package/gsd-core/bin/lib/update-context.cjs +30 -24
  164. package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
  165. package/gsd-core/bin/lib/verification.cjs +315 -30
  166. package/gsd-core/bin/lib/verify-command-grounding.cjs +47 -3
  167. package/gsd-core/bin/lib/verify.cjs +320 -48
  168. package/gsd-core/bin/lib/workstream-inventory.cjs +1 -0
  169. package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
  170. package/gsd-core/bin/lib/worktree-safety.cjs +797 -58
  171. package/gsd-core/bin/shared/config-defaults.manifest.json +4 -0
  172. package/gsd-core/bin/shared/config-schema.manifest.json +6 -0
  173. package/gsd-core/bin/verify-reapply-patches.cjs +439 -80
  174. package/gsd-core/references/checkpoints.md +5 -3
  175. package/gsd-core/references/compact-content-gate.md +66 -0
  176. package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
  177. package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
  178. package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
  179. package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
  180. package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
  181. package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
  182. package/gsd-core/references/edge-probe.md +195 -21
  183. package/gsd-core/references/execute-phase-between-wave-reset.md +7 -6
  184. package/gsd-core/references/execute-phase-wave-guard.md +22 -11
  185. package/gsd-core/references/gsd-run-resolver.md +1 -1
  186. package/gsd-core/references/loop-hook-dispatch.md +18 -0
  187. package/gsd-core/references/model-profiles.md +13 -4
  188. package/gsd-core/references/phase-argument-parsing.md +9 -7
  189. package/gsd-core/references/phase-id-convention.md +28 -0
  190. package/gsd-core/references/planner-gap-closure.md +2 -0
  191. package/gsd-core/references/planner-load-graph-context.md +24 -13
  192. package/gsd-core/references/planner-verify-command-grounding.md +14 -0
  193. package/gsd-core/references/planning-config.md +14 -2
  194. package/gsd-core/references/tdd.md +30 -4
  195. package/gsd-core/references/thinking-models-planning.md +18 -2
  196. package/gsd-core/references/ui-consideration-probe.md +10 -5
  197. package/gsd-core/references/verification-patterns.md +17 -4
  198. package/gsd-core/references/verify-command-path-resolvability.md +10 -2
  199. package/gsd-core/references/worktree-path-safety.md +433 -2
  200. package/gsd-core/templates/README.md +7 -1
  201. package/gsd-core/templates/state.md +6 -3
  202. package/gsd-core/templates/summary.compact.md +212 -0
  203. package/gsd-core/templates/user-setup.compact.md +199 -0
  204. package/gsd-core/templates/user-setup.md +0 -9
  205. package/gsd-core/templates/verification-report.md +1 -1
  206. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  207. package/gsd-core/workflows/add-backlog.md +1 -1
  208. package/gsd-core/workflows/add-phase.md +1 -1
  209. package/gsd-core/workflows/add-tests.md +2 -2
  210. package/gsd-core/workflows/add-todo.md +6 -5
  211. package/gsd-core/workflows/ai-integration-phase.md +11 -3
  212. package/gsd-core/workflows/audit-fix.md +1 -1
  213. package/gsd-core/workflows/audit-milestone.md +1 -1
  214. package/gsd-core/workflows/audit-uat.md +1 -1
  215. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
  216. package/gsd-core/workflows/autonomous.md +29 -16
  217. package/gsd-core/workflows/check-todos.md +6 -4
  218. package/gsd-core/workflows/cleanup.md +5 -3
  219. package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
  220. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +8 -1
  221. package/gsd-core/workflows/code-review-fix.md +108 -22
  222. package/gsd-core/workflows/code-review.md +216 -73
  223. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
  224. package/gsd-core/workflows/complete-milestone.md +41 -264
  225. package/gsd-core/workflows/debug.md +3 -3
  226. package/gsd-core/workflows/diagnose-issues.md +1 -1
  227. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  228. package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
  229. package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
  230. package/gsd-core/workflows/discuss-phase.md +1 -1
  231. package/gsd-core/workflows/do.md +2 -2
  232. package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
  233. package/gsd-core/workflows/docs-update.md +17 -158
  234. package/gsd-core/workflows/edit-phase.md +1 -1
  235. package/gsd-core/workflows/eval-review.md +10 -3
  236. package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
  237. package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1017 -0
  238. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +19 -4
  239. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
  240. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +43 -4
  241. package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
  242. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  243. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  244. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  245. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
  246. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
  247. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
  248. package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
  249. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
  250. package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
  251. package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +24 -0
  252. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
  253. package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
  254. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +1 -1
  255. package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
  256. package/gsd-core/workflows/execute-phase.md +83 -172
  257. package/gsd-core/workflows/execute-plan.md +24 -10
  258. package/gsd-core/workflows/explore.md +3 -3
  259. package/gsd-core/workflows/extract-learnings.md +2 -1
  260. package/gsd-core/workflows/fast.md +1 -1
  261. package/gsd-core/workflows/forensics.md +1 -1
  262. package/gsd-core/workflows/graduation.md +1 -1
  263. package/gsd-core/workflows/health.md +2 -2
  264. package/gsd-core/workflows/help/modes/full.compact.md +398 -0
  265. package/gsd-core/workflows/help/modes/full.md +5 -5
  266. package/gsd-core/workflows/help/modes/topic.md +15 -5
  267. package/gsd-core/workflows/help.md +1 -1
  268. package/gsd-core/workflows/import.md +2 -2
  269. package/gsd-core/workflows/inbox.md +2 -2
  270. package/gsd-core/workflows/ingest-docs.md +3 -3
  271. package/gsd-core/workflows/insert-phase.md +1 -1
  272. package/gsd-core/workflows/list-seeds.md +1 -1
  273. package/gsd-core/workflows/list-workspaces.md +1 -1
  274. package/gsd-core/workflows/manager.md +2 -2
  275. package/gsd-core/workflows/map-codebase.md +52 -5
  276. package/gsd-core/workflows/milestone-summary.md +1 -1
  277. package/gsd-core/workflows/mvp-phase.md +1 -1
  278. package/gsd-core/workflows/new-milestone.md +56 -14
  279. package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
  280. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
  281. package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
  282. package/gsd-core/workflows/new-project.md +39 -209
  283. package/gsd-core/workflows/new-workspace.md +2 -2
  284. package/gsd-core/workflows/next.md +1 -1
  285. package/gsd-core/workflows/note.md +1 -1
  286. package/gsd-core/workflows/onboard.md +1 -1
  287. package/gsd-core/workflows/pause-work.md +1 -1
  288. package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
  289. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
  290. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
  291. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
  292. package/gsd-core/workflows/plan-phase.md +45 -187
  293. package/gsd-core/workflows/plan-review-convergence.md +21 -5
  294. package/gsd-core/workflows/plant-seed.md +62 -20
  295. package/gsd-core/workflows/pr-branch.md +132 -20
  296. package/gsd-core/workflows/profile-user.md +2 -2
  297. package/gsd-core/workflows/progress.md +1 -1
  298. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +25 -0
  299. package/gsd-core/workflows/quick/steps/quick-verification.md +1 -1
  300. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +1 -1
  301. package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
  302. package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
  303. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
  304. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
  305. package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
  306. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
  307. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +1 -1
  308. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
  309. package/gsd-core/workflows/quick-batch.md +1 -1
  310. package/gsd-core/workflows/quick.md +29 -10
  311. package/gsd-core/workflows/reapply-patches.md +86 -6
  312. package/gsd-core/workflows/remove-phase.md +1 -1
  313. package/gsd-core/workflows/remove-workspace.md +2 -2
  314. package/gsd-core/workflows/resume-project.md +1 -1
  315. package/gsd-core/workflows/review.md +31 -16
  316. package/gsd-core/workflows/scan.md +1 -1
  317. package/gsd-core/workflows/secure-phase.md +3 -2
  318. package/gsd-core/workflows/settings-advanced.md +30 -10
  319. package/gsd-core/workflows/settings-integrations.md +2 -3
  320. package/gsd-core/workflows/settings.md +22 -9
  321. package/gsd-core/workflows/ship.md +3 -2
  322. package/gsd-core/workflows/sketch-wrap-up.md +1 -1
  323. package/gsd-core/workflows/sketch.md +1 -1
  324. package/gsd-core/workflows/smart-entry.md +2 -2
  325. package/gsd-core/workflows/spec-phase.md +15 -5
  326. package/gsd-core/workflows/spike-wrap-up.md +1 -1
  327. package/gsd-core/workflows/spike.md +1 -1
  328. package/gsd-core/workflows/stats.md +1 -1
  329. package/gsd-core/workflows/sync-skills.md +5 -5
  330. package/gsd-core/workflows/thread.md +1 -1
  331. package/gsd-core/workflows/transition.md +1 -1
  332. package/gsd-core/workflows/ui-phase.md +44 -8
  333. package/gsd-core/workflows/ui-review.md +18 -4
  334. package/gsd-core/workflows/ultraplan-phase.md +1 -1
  335. package/gsd-core/workflows/undo.md +339 -20
  336. package/gsd-core/workflows/update.md +14 -12
  337. package/gsd-core/workflows/validate-phase.md +3 -2
  338. package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
  339. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
  340. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  341. package/gsd-core/workflows/verify-work.md +101 -196
  342. package/hooks/dist/gsd-agent-isolation-guard.js +66 -16
  343. package/hooks/dist/gsd-context-monitor.js +88 -15
  344. package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
  345. package/hooks/dist/gsd-secret-read-guard.js +71 -19
  346. package/hooks/dist/gsd-statusline.js +81 -20
  347. package/hooks/dist/gsd-validate-commit.sh +97 -8
  348. package/hooks/dist/gsd-worktree-path-guard.js +25 -14
  349. package/hooks/dist/gsd-write-guard.js +46 -1
  350. package/hooks/dist/lib/dispatch-identity.js +187 -0
  351. package/hooks/dist/lib/filename-classification.js +64 -0
  352. package/hooks/dist/lib/isolation-deny-reason.js +53 -1
  353. package/hooks/dist/lib/isolation-sentinel.js +58 -19
  354. package/hooks/gsd-agent-isolation-guard.js +66 -16
  355. package/hooks/gsd-context-monitor.js +88 -15
  356. package/hooks/gsd-cursor-subagent-start.js +34 -14
  357. package/hooks/gsd-secret-read-guard.js +71 -19
  358. package/hooks/gsd-statusline.js +81 -20
  359. package/hooks/gsd-validate-commit.sh +97 -8
  360. package/hooks/gsd-worktree-path-guard.js +25 -14
  361. package/hooks/gsd-write-guard.js +46 -1
  362. package/hooks/lib/dispatch-identity.js +187 -0
  363. package/hooks/lib/filename-classification.js +64 -0
  364. package/hooks/lib/isolation-deny-reason.js +53 -1
  365. package/hooks/lib/isolation-sentinel.js +58 -19
  366. package/package.json +11 -6
  367. package/scripts/benchmark-compact-content-variants.cjs +298 -0
  368. package/scripts/benchmark-compact-content.cjs +368 -0
  369. package/scripts/build-hooks.js +15 -6
  370. package/scripts/check-contract-drift.cjs +131 -12
  371. package/scripts/check-env.cjs +36 -8
  372. package/scripts/check-glossary-refs.cjs +25 -21
  373. package/scripts/ci-next-health.cjs +271 -0
  374. package/scripts/ci-prepare-test-scope.cjs +7 -7
  375. package/scripts/ci-test-scope.cjs +126 -20
  376. package/scripts/ci-timeout-report.cjs +1 -1
  377. package/scripts/command-contract-helpers.cjs +3 -0
  378. package/scripts/diff-touches-shipped-paths.cjs +1 -1
  379. package/scripts/docs-guard-registry.cjs +35 -2
  380. package/scripts/gen-adr-index.cjs +8 -2
  381. package/scripts/gen-inventory-manifest.cjs +12 -0
  382. package/scripts/gen-loop-host-contract.cjs +69 -0
  383. package/scripts/gen-platform-conformance-tier.cjs +557 -0
  384. package/scripts/lib/drift-scan.cjs +1 -1
  385. package/scripts/lib/macos-conformance-tier.generated.cjs +224 -0
  386. package/scripts/lib/ndjson-reporter.cjs +3 -2
  387. package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
  388. package/scripts/lib/platform-conformance-tier.generated.cjs +287 -0
  389. package/scripts/lib/suite-detection.cjs +32 -0
  390. package/scripts/lint-allowed-tools-parity.cjs +221 -0
  391. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +47 -3
  392. package/scripts/lint-phase-arg-assignment.cjs +257 -0
  393. package/scripts/lint-phase-id-drift.cjs +623 -13
  394. package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
  395. package/scripts/lint-response-language-coverage.cjs +9 -3
  396. package/scripts/lint-retired-runtime-name.cjs +619 -0
  397. package/scripts/lint-source-test-name-collision.cjs +1 -1
  398. package/scripts/lint-state-write-path-drift.cjs +93 -0
  399. package/scripts/lint-test-file-count.allowlist.json +29 -9
  400. package/scripts/lint-vendored-deps.cjs +128 -17
  401. package/scripts/lint-workflow-shellcheck-baseline.json +100 -0
  402. package/scripts/prompt-injection-scan.sh +18 -0
  403. package/scripts/release-tarball-smoke.cjs +194 -1
  404. package/scripts/workflow-size.cjs +139 -0
  405. package/skills/gsd-autonomous/SKILL.md +2 -2
  406. package/skills/gsd-capture/SKILL.md +1 -1
  407. package/skills/gsd-cleanup/SKILL.md +1 -0
  408. package/skills/gsd-code-review/SKILL.md +2 -1
  409. package/skills/gsd-complete-milestone/SKILL.md +1 -0
  410. package/skills/gsd-config/SKILL.md +1 -0
  411. package/skills/gsd-debug/SKILL.md +1 -0
  412. package/skills/gsd-graphify/SKILL.md +1 -0
  413. package/skills/gsd-health/SKILL.md +1 -0
  414. package/skills/gsd-mempalace-capture/SKILL.md +8 -3
  415. package/skills/gsd-mempalace-recall/SKILL.md +1 -0
  416. package/skills/gsd-new-milestone/SKILL.md +1 -0
  417. package/skills/gsd-new-project/SKILL.md +1 -0
  418. package/skills/gsd-next/SKILL.md +1 -0
  419. package/skills/gsd-pause-work/SKILL.md +1 -0
  420. package/skills/gsd-phase/SKILL.md +1 -0
  421. package/skills/gsd-plan-review-convergence/SKILL.md +5 -5
  422. package/skills/gsd-pr-branch/SKILL.md +1 -0
  423. package/skills/gsd-progress/SKILL.md +1 -1
  424. package/skills/gsd-quick-batch/SKILL.md +1 -1
  425. package/skills/gsd-resume-work/SKILL.md +1 -0
  426. package/skills/gsd-review/SKILL.md +2 -3
  427. package/skills/gsd-review-backlog/SKILL.md +1 -0
  428. package/skills/gsd-settings/SKILL.md +2 -1
  429. package/skills/gsd-stats/SKILL.md +1 -0
  430. package/skills/gsd-thread/SKILL.md +1 -0
  431. package/skills/gsd-workspace/SKILL.md +1 -0
  432. package/skills/gsd-workstreams/SKILL.md +1 -0
  433. package/vscode/package.json +1 -1
  434. package/gsd-core/templates/claude-md.md +0 -145
  435. package/gsd-core/templates/codebase/concerns.md +0 -310
  436. package/gsd-core/templates/codebase/conventions.md +0 -307
  437. package/gsd-core/templates/codebase/integrations.md +0 -280
  438. package/gsd-core/templates/codebase/structure.md +0 -285
  439. package/gsd-core/templates/codebase/testing.md +0 -480
  440. package/gsd-core/templates/debug-subagent-prompt.md +0 -91
  441. package/gsd-core/templates/discovery.md +0 -146
@@ -38,7 +38,7 @@ const commonjs_marker_cjs_1 = require("./commonjs-marker.cjs");
38
38
  const imperative_hook_bus_cjs_1 = require("./host-integration-adapters/imperative-hook-bus.cjs");
39
39
  // eslint-disable-next-line @typescript-eslint/no-require-imports
40
40
  const shellCmdProjection = require("./shell-command-projection.cjs");
41
- const { isManagedHookBasename, isManagedHookCommand, projectLegacySettingsHookCommand, projectManagedHookCommand, projectPortableHookBaseDir, projectCodexHookTomlCommand, shellHookOmitsBashRunner, escapeTomlDoubleQuotedString, escapePosixDoubleQuoted, } = shellCmdProjection;
41
+ const { isManagedHookBasename, isManagedHookCommand, projectLegacySettingsHookCommand, projectManagedHookCommand, projectPortableHookBaseDir, projectCodexHookTomlCommand, shellHookOmitsBashRunner, escapeTomlDoubleQuotedString, escapePosixDoubleQuoted, resolveExecutableBinary, } = shellCmdProjection;
42
42
  // ---------------------------------------------------------------------------
43
43
  // Terminal color constants (mirrors install.js for console output parity)
44
44
  // ---------------------------------------------------------------------------
@@ -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
@@ -567,7 +576,7 @@ function buildBakedNodeToken(opts) {
567
576
  * guarantee), ahead of `command -v node` and the well-known fallback list.
568
577
  */
569
578
  const NODE_RUNNER_RESOLVER_HOOK = 'gsd-node-runner.sh';
570
- function resolveBashRunner(opts) {
579
+ function resolveBashExecutable(opts) {
571
580
  const platform = (opts && opts.platform) || process.platform;
572
581
  if (platform !== 'win32')
573
582
  return 'bash';
@@ -585,12 +594,19 @@ function resolveBashRunner(opts) {
585
594
  candidates.push(node_path_1.default.win32.join(env.SystemDrive, 'Program Files (x86)', 'Git', 'bin', 'bash.exe'));
586
595
  }
587
596
  for (const candidate of candidates) {
588
- if (candidate && exists(candidate)) {
589
- return JSON.stringify(shellCmdProjection.posixNormalize(candidate));
590
- }
597
+ if (candidate && exists(candidate))
598
+ return shellCmdProjection.posixNormalize(candidate);
591
599
  }
592
600
  return null;
593
601
  }
602
+ function resolveBashRunner(opts) {
603
+ const executable = resolveBashExecutable(opts);
604
+ if (executable === null)
605
+ return null;
606
+ return ((opts && opts.platform) || process.platform) === 'win32'
607
+ ? JSON.stringify(executable)
608
+ : executable;
609
+ }
594
610
  // #3662 — recognize the two runtime-resolving command shapes the installer
595
611
  // emits, so the rewriter never churns (or un-does) an entry that already
596
612
  // works in every environment sharing the config root.
@@ -802,16 +818,52 @@ function rewriteLegacyCodexHookBlock(content, absoluteRunner, opts) {
802
818
  });
803
819
  return { content: updated, changed };
804
820
  }
821
+ function _installEngineSymlinkGuard() {
822
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
823
+ const mod = require('./install-engine.cjs');
824
+ return mod;
825
+ }
805
826
  function reconcileCodexHooksJsonEvent(targetDir, eventName, opts = {}) {
806
827
  const hooksJsonPath = node_path_1.default.join(targetDir, 'hooks.json');
807
828
  const managedCommand = typeof opts.managedCommand === 'string' ? opts.managedCommand : null;
808
829
  const commandWindows = typeof opts.commandWindows === 'string' ? opts.commandWindows : null;
809
830
  const matcher = typeof opts.matcher === 'string' ? opts.matcher : undefined;
810
831
  const timeout = typeof opts.timeout === 'number' ? opts.timeout : undefined;
832
+ // #2586 Major 2: every Codex hooks.json writer funnels through this one
833
+ // function, and atomicWriteFileSync's final step is a rename(2) onto
834
+ // `hooksJsonPath` — which, when that path is a symlink, REPLACES the
835
+ // symlink with a plain file rather than writing through it. Refuse (with
836
+ // the same GSD_ALLOW_SYMLINKED_DEST opt-in every other install call site
837
+ // honors) before reading or writing, so a symlinked hooks.json is neither
838
+ // silently destroyed nor left the caller no escape hatch.
839
+ const symlinkGuard = _installEngineSymlinkGuard();
840
+ // The path this function actually reads/writes. Defaults to the nominal
841
+ // hooks.json path; reassigned below to the symlink's real target when the
842
+ // opt-in is active, so the write lands on the file the user's symlink
843
+ // points at instead of clobbering the symlink itself (see note below).
844
+ let effectiveHooksJsonPath = hooksJsonPath;
845
+ if (node_fs_1.default.existsSync(hooksJsonPath) && node_fs_1.default.lstatSync(hooksJsonPath).isSymbolicLink()) {
846
+ if (symlinkGuard.hasExistingSymlinkBetween(targetDir, hooksJsonPath, {
847
+ allowOptInFollow: symlinkGuard.isSymlinkedDestOptIn(),
848
+ })) {
849
+ throw new Error(`hooks.json at "${hooksJsonPath}" contains a symlink the install root "${targetDir}" does not trust — ` +
850
+ 'refusing to read or write it. If this is an intentional user-owned symlink layout, re-run with ' +
851
+ 'GSD_ALLOW_SYMLINKED_DEST=1.');
852
+ }
853
+ // hasExistingSymlinkBetween returned false only because the opt-in is
854
+ // active (a symlinked leaf always trips it otherwise) — so this IS a
855
+ // symlink and we are cleared to follow it. atomicWriteFileSync's final
856
+ // step is a rename(2) onto its target, which REPLACES an existing
857
+ // symlink at that path rather than writing through it; resolving to the
858
+ // real path here makes the read AND the write operate on the symlink's
859
+ // target, leaving the symlink itself untouched, matching what "follow"
860
+ // is supposed to mean.
861
+ effectiveHooksJsonPath = node_fs_1.default.realpathSync(hooksJsonPath);
862
+ }
811
863
  let parsed = {};
812
864
  let currentContent = null;
813
- if (node_fs_1.default.existsSync(hooksJsonPath)) {
814
- const raw = node_fs_1.default.readFileSync(hooksJsonPath, 'utf8');
865
+ if (node_fs_1.default.existsSync(effectiveHooksJsonPath)) {
866
+ const raw = node_fs_1.default.readFileSync(effectiveHooksJsonPath, 'utf8');
815
867
  currentContent = raw;
816
868
  if (raw.trim()) {
817
869
  try {
@@ -845,6 +897,12 @@ function reconcileCodexHooksJsonEvent(targetDir, eventName, opts = {}) {
845
897
  }
846
898
  parsed['hooks'] = hookTable;
847
899
  const eventEntries = Array.isArray(hookTable[eventName]) ? hookTable[eventName] : [];
900
+ // Minor 5 (#2586 review): an event key the user already had, already
901
+ // holding an empty array, must survive removal as an empty array — not be
902
+ // deleted outright. Deleting is only correct when OUR removal is what
903
+ // emptied a previously non-empty array. Tracked before the loop below can
904
+ // mutate anything.
905
+ const wasArrayEmpty = Array.isArray(hookTable[eventName]) && eventEntries.length === 0;
848
906
  let removedLegacy = false;
849
907
  const sanitizedEntries = [];
850
908
  for (const entry of eventEntries) {
@@ -886,6 +944,11 @@ function reconcileCodexHooksJsonEvent(targetDir, eventName, opts = {}) {
886
944
  if (sanitizedEntries.length > 0) {
887
945
  hookTable[eventName] = sanitizedEntries;
888
946
  }
947
+ else if (wasArrayEmpty) {
948
+ // Nothing of ours was ever here to remove — preserve the user's own
949
+ // empty array exactly as found (Minor 5).
950
+ hookTable[eventName] = [];
951
+ }
889
952
  else {
890
953
  delete hookTable[eventName];
891
954
  }
@@ -898,23 +961,25 @@ function reconcileCodexHooksJsonEvent(targetDir, eventName, opts = {}) {
898
961
  const changed = currentContent !== nextContent;
899
962
  const shouldWrite = changed && (currentContent !== null || Object.keys(parsed).length > 0);
900
963
  if (shouldWrite) {
901
- atomicWriteFileSync(hooksJsonPath, nextContent, 'utf8');
964
+ atomicWriteFileSync(effectiveHooksJsonPath, nextContent, 'utf8');
902
965
  }
903
966
  return { changed: changed || removedLegacy, wrote: shouldWrite, path: hooksJsonPath };
904
967
  }
905
968
  function reconcileCodexHooksJsonSessionStart(targetDir, opts = {}) {
906
969
  return reconcileCodexHooksJsonEvent(targetDir, 'SessionStart', opts);
907
970
  }
908
- function buildCodexHookWindowsShimIR(scriptAbsPath, absoluteRunnerToken) {
909
- if (!absoluteRunnerToken)
910
- return null;
911
- let interpreter;
971
+ function parseAbsoluteRunnerToken(absoluteRunnerToken) {
912
972
  try {
913
- interpreter = JSON.parse(absoluteRunnerToken);
973
+ return JSON.parse(absoluteRunnerToken);
914
974
  }
915
975
  catch {
916
- interpreter = absoluteRunnerToken;
976
+ return absoluteRunnerToken;
917
977
  }
978
+ }
979
+ function buildCodexHookWindowsShimIR(scriptAbsPath, absoluteRunnerToken) {
980
+ if (!absoluteRunnerToken)
981
+ return null;
982
+ const interpreter = parseAbsoluteRunnerToken(absoluteRunnerToken);
918
983
  const targetAbs = shellCmdProjection.posixNormalize(scriptAbsPath);
919
984
  const scriptQuoted = JSON.stringify(targetAbs);
920
985
  const cmdPath = scriptAbsPath.replace(/\.js$/, '.cmd');
@@ -939,6 +1004,7 @@ function ensureCodexHooksJsonSessionStart(targetDir, opts = {}) {
939
1004
  return { changed: false, wrote: false, path: hooksJsonPath };
940
1005
  const scriptPath = shellCmdProjection.posixNormalize(node_path_1.default.resolve(targetDir, 'hooks', 'gsd-check-update.js'));
941
1006
  const cmdShimPath = scriptPath.replace(/\.js$/, '.cmd');
1007
+ const configuredEntrypoints = [];
942
1008
  let managedCommand;
943
1009
  if (platform === 'win32') {
944
1010
  const shimIR = buildCodexHookWindowsShimIR(scriptPath, absoluteRunner);
@@ -955,6 +1021,7 @@ function ensureCodexHooksJsonSessionStart(targetDir, opts = {}) {
955
1021
  return { changed: false, wrote: false, path: hooksJsonPath };
956
1022
  }
957
1023
  managedCommand = shimIR.hookCommand;
1024
+ configuredEntrypoints.push({ runtime: 'codex', configPath: hooksJsonPath, scriptPath: shimIR.cmdPath, platform, selfExecutable: true }, { runtime: 'codex', configPath: hooksJsonPath, scriptPath, interpreterCandidates: [parseAbsoluteRunnerToken(absoluteRunner)], platform });
958
1025
  }
959
1026
  else {
960
1027
  managedCommand = projectManagedHookCommand({
@@ -963,13 +1030,23 @@ function ensureCodexHooksJsonSessionStart(targetDir, opts = {}) {
963
1030
  runtime: 'codex',
964
1031
  platform,
965
1032
  }) ?? undefined;
1033
+ if (managedCommand) {
1034
+ configuredEntrypoints.push({
1035
+ runtime: 'codex',
1036
+ configPath: hooksJsonPath,
1037
+ scriptPath,
1038
+ interpreterCandidates: [parseAbsoluteRunnerToken(absoluteRunner)],
1039
+ platform,
1040
+ });
1041
+ }
966
1042
  }
967
1043
  if (!managedCommand)
968
1044
  return { changed: false, wrote: false, path: hooksJsonPath };
969
1045
  const commandWindows = platform === 'win32'
970
1046
  ? JSON.stringify(shellCmdProjection.posixNormalize(cmdShimPath))
971
1047
  : undefined;
972
- return reconcileCodexHooksJsonSessionStart(targetDir, { managedCommand, commandWindows });
1048
+ const result = reconcileCodexHooksJsonSessionStart(targetDir, { managedCommand, commandWindows });
1049
+ return { ...result, configuredEntrypoints };
973
1050
  }
974
1051
  function ensureCodexHooksJsonEvent(targetDir, eventName, opts = {}) {
975
1052
  const platform = opts.platform || process.platform;
@@ -1015,6 +1092,186 @@ function removeCodexHooksJsonEvent(targetDir, eventName) {
1015
1092
  function removeCodexHooksJsonSessionStart(targetDir) {
1016
1093
  return reconcileCodexHooksJsonSessionStart(targetDir, { managedCommand: null });
1017
1094
  }
1095
+ // Literal, version-stable markers every shipped gsd-context-monitor.js
1096
+ // carries. Stable across the {{GSD_VERSION}} and runtime-path substitutions
1097
+ // the Codex copy step applies (#2586 design doc "Ownership check" — a raw
1098
+ // content hash would differ per runtime/version by construction, so a marker
1099
+ // check is used instead of manifest-membership, which has a bootstrap gap on
1100
+ // the exact case that matters most: a pre-#2586 install's manifest never
1101
+ // recorded this file at all).
1102
+ const CODEX_CONTEXT_MONITOR_OWNERSHIP_MARKERS = [
1103
+ '#!/usr/bin/env node',
1104
+ '// gsd-hook-version:',
1105
+ '// Context Monitor - PostToolUse/AfterTool hook',
1106
+ ];
1107
+ function isGsdOwnedCodexContextMonitorScript(filePath) {
1108
+ let content;
1109
+ try {
1110
+ content = node_fs_1.default.readFileSync(filePath, 'utf8');
1111
+ }
1112
+ catch {
1113
+ return false;
1114
+ }
1115
+ // The .cmd shim (buildCodexHookWindowsShimIR) is a tiny generated batch
1116
+ // wrapper, not the JS file itself — it never carries the JS markers above,
1117
+ // so it gets its own narrower, still-specific signature: the exact
1118
+ // "@ECHO OFF" / "@SETLOCAL" preamble the shim generator emits, invoking a
1119
+ // script path that ends in gsd-context-monitor.js.
1120
+ if (filePath.endsWith('.cmd')) {
1121
+ return content.startsWith('@ECHO OFF') && content.includes('@SETLOCAL')
1122
+ && /gsd-context-monitor\.js/.test(content);
1123
+ }
1124
+ return CODEX_CONTEXT_MONITOR_OWNERSHIP_MARKERS.every((marker) => content.includes(marker));
1125
+ }
1126
+ /**
1127
+ * Scan every event in hooks.json for a surviving reference to the
1128
+ * context-monitor script or its Windows .cmd shim, by basename — not scoped
1129
+ * to CODEX_EXTENDED_HOOK_EVENTS, so a user who hand-registered it under an
1130
+ * unrelated event key is still detected as "referenced" and the script is
1131
+ * preserved.
1132
+ */
1133
+ function hooksJsonReferencesCodexContextMonitor(targetDir) {
1134
+ const hooksJsonPath = node_path_1.default.join(targetDir, 'hooks.json');
1135
+ if (!node_fs_1.default.existsSync(hooksJsonPath))
1136
+ return false;
1137
+ let raw;
1138
+ try {
1139
+ raw = node_fs_1.default.readFileSync(hooksJsonPath, 'utf8');
1140
+ }
1141
+ catch {
1142
+ return true; // unreadable — conservatively assume referenced, never delete
1143
+ }
1144
+ if (!raw.trim())
1145
+ return false;
1146
+ let parsed;
1147
+ try {
1148
+ parsed = JSON.parse(raw);
1149
+ }
1150
+ catch {
1151
+ return true; // unparseable — conservatively assume referenced
1152
+ }
1153
+ if (!parsed || typeof parsed !== 'object')
1154
+ return false;
1155
+ const hooks = parsed['hooks'];
1156
+ const table = hooks && typeof hooks === 'object' && !Array.isArray(hooks)
1157
+ ? hooks
1158
+ : parsed;
1159
+ for (const key of Object.keys(table)) {
1160
+ const entries = table[key];
1161
+ if (!Array.isArray(entries))
1162
+ continue;
1163
+ for (const entry of entries) {
1164
+ if (!entry || typeof entry !== 'object')
1165
+ continue;
1166
+ const entryHooks = entry['hooks'];
1167
+ const hookList = Array.isArray(entryHooks) ? entryHooks : [entry];
1168
+ for (const hook of hookList) {
1169
+ if (!hook || typeof hook !== 'object')
1170
+ continue;
1171
+ const values = [
1172
+ hook['command'],
1173
+ hook['commandWindows'],
1174
+ ];
1175
+ for (const value of values) {
1176
+ if (typeof value === 'string' && /gsd-context-monitor(\.js|\.cmd)?/.test(value)) {
1177
+ return true;
1178
+ }
1179
+ }
1180
+ }
1181
+ }
1182
+ }
1183
+ return false;
1184
+ }
1185
+ /**
1186
+ * #2586 must-have #4/#8: after hooks.json registrations for
1187
+ * CODEX_EXTENDED_HOOK_EVENTS have been reconciled away (by the caller, via
1188
+ * removeCodexHooksJsonEvent), delete `hooks/gsd-context-monitor.js` and its
1189
+ * `.cmd` shim ONLY when (a) no surviving hooks.json registration under ANY
1190
+ * event still references either basename, and (b) the on-disk file carries
1191
+ * GSD's own ownership markers (a user's hand-edited or unrelated file at that
1192
+ * path is left alone). Each file is deleted independently — a failure
1193
+ * deleting one is reported as a warning and never rolls back the (already
1194
+ * safe, already-written) hooks.json deregistration the caller performed
1195
+ * first.
1196
+ */
1197
+ function cleanupOrphanedCodexContextMonitorScript(targetDir) {
1198
+ const result = { deleted: [], warnings: [], stillReferenced: false };
1199
+ if (hooksJsonReferencesCodexContextMonitor(targetDir)) {
1200
+ result.stillReferenced = true;
1201
+ return result;
1202
+ }
1203
+ const candidates = [
1204
+ node_path_1.default.join(targetDir, 'hooks', 'gsd-context-monitor.js'),
1205
+ node_path_1.default.join(targetDir, 'hooks', 'gsd-context-monitor.cmd'),
1206
+ ];
1207
+ for (const candidate of candidates) {
1208
+ if (!node_fs_1.default.existsSync(candidate))
1209
+ continue;
1210
+ if (!isGsdOwnedCodexContextMonitorScript(candidate))
1211
+ continue;
1212
+ try {
1213
+ node_fs_1.default.unlinkSync(candidate);
1214
+ result.deleted.push(candidate);
1215
+ }
1216
+ catch (err) {
1217
+ result.warnings.push({
1218
+ path: candidate,
1219
+ reason: err && err.message ? err.message : String(err),
1220
+ });
1221
+ }
1222
+ }
1223
+ return result;
1224
+ }
1225
+ function configuredEntrypointsForHook(configDir, hookName, opts) {
1226
+ const platform = opts.platform || process.platform;
1227
+ const runtime = opts.runtime || 'generic';
1228
+ const configPath = opts.configPath || configDir;
1229
+ const target = {
1230
+ runtime,
1231
+ configPath,
1232
+ scriptPath: node_path_1.default.join(configDir, 'hooks', hookName),
1233
+ platform,
1234
+ };
1235
+ const isShellHook = hookName.endsWith('.sh');
1236
+ if (shellHookOmitsBashRunner({ platform, runtime, isShellHook }))
1237
+ return [{ ...target, selfExecutable: true }];
1238
+ const bash = resolveBashExecutable(opts);
1239
+ if (isShellHook) {
1240
+ // An unresolved bash must still surface as an interpreterCandidates entry
1241
+ // (the literal token, same as the portableHooks runner below) so
1242
+ // validateConfiguredEntrypoints reports 'unresolved-interpreter' instead
1243
+ // of silently skipping the check because the field is absent.
1244
+ return [{ ...target, interpreterCandidates: [bash === null ? 'bash' : bash] }];
1245
+ }
1246
+ // #4249: check the SAME stable alias buildNodeRunnerChainToken bakes as its
1247
+ // first candidate (normalizeNodePath rewrites a version-manager shim like
1248
+ // fnm/nvm/mise/volta into its persistent path), not the raw, currently-
1249
+ // running process.execPath — which always trivially resolves regardless of
1250
+ // whether the alias actually baked into the persisted command still does.
1251
+ const nodeCandidates = [
1252
+ normalizeNodePath(opts.execPath || process.execPath, opts),
1253
+ 'node',
1254
+ '/usr/local/bin/node',
1255
+ '/usr/bin/node',
1256
+ ].filter((candidate) => Boolean(candidate));
1257
+ if (!opts.portableHooks) {
1258
+ return [{ ...target, interpreterCandidates: nodeCandidates }];
1259
+ }
1260
+ const runner = {
1261
+ runtime,
1262
+ configPath,
1263
+ scriptPath: node_path_1.default.join(configDir, 'hooks', NODE_RUNNER_RESOLVER_HOOK),
1264
+ interpreterCandidates: bash === null ? ['bash'] : [bash],
1265
+ platform,
1266
+ };
1267
+ return [runner, { ...target, interpreterCandidates: nodeCandidates }];
1268
+ }
1269
+ function recordConfiguredHookCommand(command, configDir, hookName, opts) {
1270
+ if (command && opts.configuredEntrypoints) {
1271
+ opts.configuredEntrypoints.push(...configuredEntrypointsForHook(configDir, hookName, opts).map(entry => ({ ...entry, command })));
1272
+ }
1273
+ return command;
1274
+ }
1018
1275
  function buildHookCommand(configDir, hookName, opts) {
1019
1276
  if (!opts)
1020
1277
  opts = {};
@@ -1022,44 +1279,56 @@ function buildHookCommand(configDir, hookName, opts) {
1022
1279
  const runtime = opts.runtime || 'generic';
1023
1280
  const hookShell = opts.hookShell;
1024
1281
  const isShellHook = hookName.endsWith('.sh');
1282
+ const track = (command) => recordConfiguredHookCommand(command, configDir, hookName, opts);
1025
1283
  if (shellHookOmitsBashRunner({ platform, runtime, isShellHook })) {
1026
1284
  if (opts.portableHooks) {
1027
1285
  const portableBaseDir = projectPortableHookBaseDir({
1028
1286
  configDir,
1029
1287
  homeDir: node_os_1.default.homedir(),
1030
1288
  });
1031
- return JSON.stringify(`${portableBaseDir}/hooks/${hookName}`);
1289
+ return track(JSON.stringify(`${portableBaseDir}/hooks/${hookName}`));
1032
1290
  }
1033
- return JSON.stringify(shellCmdProjection.posixNormalize(configDir) + '/hooks/' + hookName);
1291
+ return track(JSON.stringify(shellCmdProjection.posixNormalize(configDir) + '/hooks/' + hookName));
1034
1292
  }
1035
1293
  // .sh hooks keep the pre-#3662 shape everywhere: the bash runner resolves
1036
1294
  // at install time like today, and `bash` itself is a PATH-stable binary
1037
1295
  // (the absolute Git-Bash discovery covers win32 — #580/#3393).
1038
1296
  if (isShellHook) {
1039
1297
  const runner = resolveBashRunner(opts);
1040
- if (runner === null)
1298
+ if (runner === null) {
1299
+ // #4249 (antigravity review): this early return skips `track()` below,
1300
+ // so an unresolved bash on win32 (no Git Bash found) previously left
1301
+ // this hook silently unregistered with nothing for
1302
+ // validateConfiguredEntrypoints to reject — configuredEntrypointsForHook's
1303
+ // own 'unresolved bash must still surface' comment describes intent this
1304
+ // return never reached. Push the entry directly (no `command`, since
1305
+ // none was ever built) so the gate actually sees it.
1306
+ if (opts.configuredEntrypoints) {
1307
+ opts.configuredEntrypoints.push(...configuredEntrypointsForHook(configDir, hookName, opts));
1308
+ }
1041
1309
  return null;
1310
+ }
1042
1311
  if (opts.portableHooks) {
1043
1312
  const portableBaseDir = projectPortableHookBaseDir({
1044
1313
  configDir,
1045
1314
  homeDir: node_os_1.default.homedir(),
1046
1315
  });
1047
- return projectManagedHookCommand({
1316
+ return track(projectManagedHookCommand({
1048
1317
  absoluteRunner: runner,
1049
1318
  scriptPath: `${portableBaseDir}/hooks/${hookName}`,
1050
1319
  runtime: opts.runtime || 'generic',
1051
1320
  platform,
1052
1321
  hookShell,
1053
- });
1322
+ }));
1054
1323
  }
1055
1324
  const hooksPath = shellCmdProjection.posixNormalize(configDir) + '/hooks/' + hookName;
1056
- return projectManagedHookCommand({
1325
+ return track(projectManagedHookCommand({
1057
1326
  absoluteRunner: runner,
1058
1327
  scriptPath: hooksPath,
1059
1328
  runtime,
1060
1329
  platform,
1061
1330
  hookShell,
1062
- });
1331
+ }));
1063
1332
  }
1064
1333
  // JS hooks (#3662): the node runner is resolved at hook-fire time, never
1065
1334
  // baked as a bare absolute path — an install-environment absolute path is
@@ -1082,7 +1351,7 @@ function buildHookCommand(configDir, hookName, opts) {
1082
1351
  // Absolute Git-Bash discovery on win32 when available (#580); `bash` on
1083
1352
  // PATH otherwise — the same assumption .sh hooks already make.
1084
1353
  const resolverRunner = resolveBashRunner(opts) || 'bash';
1085
- return shellCmdProjection.projectShellCommandText({
1354
+ return track(shellCmdProjection.projectShellCommandText({
1086
1355
  runnerToken: resolverRunner,
1087
1356
  argTokens: [
1088
1357
  JSON.stringify(`${portableBaseDir}/hooks/${NODE_RUNNER_RESOLVER_HOOK}`),
@@ -1092,19 +1361,19 @@ function buildHookCommand(configDir, hookName, opts) {
1092
1361
  runtime,
1093
1362
  platform,
1094
1363
  hookShell,
1095
- });
1364
+ }));
1096
1365
  }
1097
1366
  const chainRunner = buildNodeRunnerChainToken(opts);
1098
1367
  if (chainRunner === null)
1099
1368
  return null;
1100
1369
  const hooksPath = shellCmdProjection.posixNormalize(configDir) + '/hooks/' + hookName;
1101
- return shellCmdProjection.projectShellCommandText({
1370
+ return track(shellCmdProjection.projectShellCommandText({
1102
1371
  runnerToken: chainRunner,
1103
1372
  argTokens: [JSON.stringify(hooksPath)],
1104
1373
  runtime,
1105
1374
  platform,
1106
1375
  hookShell,
1107
- });
1376
+ }));
1108
1377
  }
1109
1378
  // ---------------------------------------------------------------------------
1110
1379
  // Cline helpers
@@ -1205,6 +1474,7 @@ function mergeGsdAgentsMd(filePath, gsdContent) {
1205
1474
  // ---------------------------------------------------------------------------
1206
1475
  function writeClineArtifacts(targetDir, isGlobalInstall) {
1207
1476
  const written = [];
1477
+ const configuredEntrypoints = [];
1208
1478
  const clinerulesDir = node_path_1.default.join(targetDir, '.clinerules');
1209
1479
  try {
1210
1480
  if (node_fs_1.default.existsSync(clinerulesDir)) {
@@ -1230,6 +1500,20 @@ function writeClineArtifacts(targetDir, isGlobalInstall) {
1230
1500
  catch { /* Windows: hooks unsupported anyway */ }
1231
1501
  written.push('.clinerules/hooks/PreToolUse');
1232
1502
  console.log(` ${green}✓${reset} Wrote .clinerules/hooks/PreToolUse`);
1503
+ // #4249 (CodeRabbit): Cline invokes this file directly via its own
1504
+ // `#!/usr/bin/env node` shebang — a hybrid case. The script itself still
1505
+ // needs the execute bit (selfExecutable), but unlike GSD's other JS hooks
1506
+ // (which bake an absolute, install-time-resolved node path specifically to
1507
+ // avoid this) its interpreter is looked up on PATH by `env` at hook-fire
1508
+ // time, so `node` must also resolve or the hook can never run.
1509
+ configuredEntrypoints.push({
1510
+ runtime: 'cline',
1511
+ configPath: hookPath,
1512
+ scriptPath: hookPath,
1513
+ interpreterCandidates: ['node'],
1514
+ selfExecutable: true,
1515
+ platform: process.platform,
1516
+ });
1233
1517
  if (isGlobalInstall) {
1234
1518
  try {
1235
1519
  const agentsPath = node_path_1.default.join(node_os_1.default.homedir(), '.agents', 'AGENTS.md');
@@ -1240,7 +1524,7 @@ function writeClineArtifacts(targetDir, isGlobalInstall) {
1240
1524
  console.warn(` ${yellow}⚠${reset} Could not write ~/.agents/AGENTS.md: ${err.message}`);
1241
1525
  }
1242
1526
  }
1243
- return written;
1527
+ return { written, configuredEntrypoints };
1244
1528
  }
1245
1529
  // ---------------------------------------------------------------------------
1246
1530
  // Cursor hook functions
@@ -1491,7 +1775,13 @@ function writeCursorHooksJson(targetDir, src, opts) {
1491
1775
  if (installedScripts.size > 0) {
1492
1776
  ensureCommonJsMarker(hooksDir);
1493
1777
  }
1494
- const hookOpts = { runtime: 'cursor', platform: opts.platform || process.platform };
1778
+ const configuredEntrypoints = [];
1779
+ const hookOpts = {
1780
+ runtime: 'cursor',
1781
+ platform: opts.platform || process.platform,
1782
+ configPath: node_path_1.default.join(targetDir, 'hooks.json'),
1783
+ configuredEntrypoints,
1784
+ };
1495
1785
  const commands = {};
1496
1786
  for (const ev of events) {
1497
1787
  const script = imperative_hook_bus_cjs_1.CURSOR_EVENT_SCRIPT_MAP[ev];
@@ -1505,7 +1795,7 @@ function writeCursorHooksJson(targetDir, src, opts) {
1505
1795
  const managedEntries = (0, imperative_hook_bus_cjs_1.buildHookBusEntries)(events, commands);
1506
1796
  const hooksJsonPath = node_path_1.default.join(targetDir, 'hooks.json');
1507
1797
  const result = reconcileCursorHooksJson(hooksJsonPath, managedEntries);
1508
- return { hooksJsonPath, changed: result.changed };
1798
+ return { hooksJsonPath, changed: result.changed, configuredEntrypoints };
1509
1799
  }
1510
1800
  function removeCursorHooksJson(targetDir) {
1511
1801
  const hooksJsonPath = node_path_1.default.join(targetDir, 'hooks.json');
@@ -1710,7 +2000,13 @@ function writeWindsurfHooksJson(targetDir, src, opts) {
1710
2000
  if (installedScripts.size > 0) {
1711
2001
  ensureCommonJsMarker(hooksDir);
1712
2002
  }
1713
- const hookOpts = { runtime: 'windsurf', platform: opts.platform || process.platform };
2003
+ const configuredEntrypoints = [];
2004
+ const hookOpts = {
2005
+ runtime: 'windsurf',
2006
+ platform: opts.platform || process.platform,
2007
+ configPath: node_path_1.default.join(targetDir, 'hooks.json'),
2008
+ configuredEntrypoints,
2009
+ };
1714
2010
  const commands = {};
1715
2011
  for (const ev of WINDSURF_HOOK_EVENTS) {
1716
2012
  const script = WINDSURF_EVENT_SCRIPT_MAP[ev];
@@ -1724,7 +2020,7 @@ function writeWindsurfHooksJson(targetDir, src, opts) {
1724
2020
  }
1725
2021
  const hooksJsonPath = node_path_1.default.join(targetDir, 'hooks.json');
1726
2022
  const result = reconcileWindsurfHooksJson(hooksJsonPath, managedEntries);
1727
- return { hooksJsonPath, changed: result.changed };
2023
+ return { hooksJsonPath, changed: result.changed, configuredEntrypoints };
1728
2024
  }
1729
2025
  /**
1730
2026
  * Remove all GSD-managed Cascade hook entries from hooks.json. User-owned
@@ -2575,13 +2871,21 @@ function stripKimiHooksTomlBlock(content) {
2575
2871
  * touch the file's mtime for no reason.
2576
2872
  */
2577
2873
  function writeKimiHooksToml(configPath, targetDir, opts) {
2874
+ const configuredEntrypoints = [];
2875
+ const trackedOpts = {
2876
+ hookOpts: {
2877
+ ...opts.hookOpts,
2878
+ configPath,
2879
+ configuredEntrypoints,
2880
+ },
2881
+ };
2578
2882
  const existing = node_fs_1.default.existsSync(configPath) ? node_fs_1.default.readFileSync(configPath, 'utf8') : '';
2579
2883
  const stripped = stripKimiHooksTomlBlock(existing) ?? '';
2580
- const block = buildKimiHooksTomlBlock(targetDir, opts);
2884
+ const block = buildKimiHooksTomlBlock(targetDir, trackedOpts);
2581
2885
  const entryCount = block ? (block.match(/\[\[hooks\]\]/g) || []).length : 0;
2582
2886
  if (!block) {
2583
2887
  if (stripped === existing)
2584
- return { changed: false, path: configPath, entryCount: 0 };
2888
+ return { changed: false, path: configPath, entryCount: 0, configuredEntrypoints };
2585
2889
  if (stripped.trim() === '') {
2586
2890
  if (node_fs_1.default.existsSync(configPath))
2587
2891
  node_fs_1.default.unlinkSync(configPath);
@@ -2590,15 +2894,15 @@ function writeKimiHooksToml(configPath, targetDir, opts) {
2590
2894
  node_fs_1.default.mkdirSync(node_path_1.default.dirname(configPath), { recursive: true });
2591
2895
  atomicWriteFileSync(configPath, stripped, 'utf8');
2592
2896
  }
2593
- return { changed: true, path: configPath, entryCount: 0 };
2897
+ return { changed: true, path: configPath, entryCount: 0, configuredEntrypoints };
2594
2898
  }
2595
2899
  const separator = stripped.trim() === '' ? '' : (stripped.endsWith('\n') ? '\n' : '\n\n');
2596
2900
  const next = stripped.trim() === '' ? `${block}\n` : `${stripped}${separator}${block}\n`;
2597
2901
  if (next === existing)
2598
- return { changed: false, path: configPath, entryCount };
2902
+ return { changed: false, path: configPath, entryCount, configuredEntrypoints };
2599
2903
  node_fs_1.default.mkdirSync(node_path_1.default.dirname(configPath), { recursive: true });
2600
2904
  atomicWriteFileSync(configPath, next, 'utf8');
2601
- return { changed: true, path: configPath, entryCount };
2905
+ return { changed: true, path: configPath, entryCount, configuredEntrypoints };
2602
2906
  }
2603
2907
  /**
2604
2908
  * Uninstall-time counterpart to writeKimiHooksToml: strips the GSD block and
@@ -2644,6 +2948,68 @@ function referencesHook(h, hookName) {
2644
2948
  (Array.isArray(args) && args.some(a => typeof a === 'string' && a.includes(hookName))) ||
2645
2949
  (typeof url === 'string' && url.includes(hookName));
2646
2950
  }
2951
+ function validateConfiguredEntrypoints(entries, deps = {}) {
2952
+ const statSync = deps.statSync ?? node_fs_1.default.statSync;
2953
+ const accessSync = deps.accessSync ?? node_fs_1.default.accessSync;
2954
+ const resolve = deps.resolveExecutableBinary ?? resolveExecutableBinary;
2955
+ const invalid = [];
2956
+ for (const entry of entries) {
2957
+ let scriptOk = false;
2958
+ try {
2959
+ scriptOk = statSync(entry.scriptPath).isFile();
2960
+ if (!scriptOk) {
2961
+ invalid.push({ runtime: entry.runtime, configPath: entry.configPath, role: 'script', path: entry.scriptPath, reason: 'wrong-file-type' });
2962
+ }
2963
+ else {
2964
+ // #4249: statSync only needs search permission on the parent dirs, so
2965
+ // it succeeds even for a chmod-000 file — the EACCES catch below
2966
+ // never fires for that case. Read permission on the file itself must
2967
+ // be checked explicitly: an interpreter opens the script directly,
2968
+ // and even a self-executable shebang script is opened and read by
2969
+ // its kernel-invoked interpreter, not just exec'd — X_OK alone does
2970
+ // not prove it's readable.
2971
+ try {
2972
+ accessSync(entry.scriptPath, node_fs_1.default.constants.R_OK);
2973
+ }
2974
+ catch {
2975
+ scriptOk = false;
2976
+ invalid.push({ runtime: entry.runtime, configPath: entry.configPath, role: 'script', path: entry.scriptPath, reason: 'unreadable' });
2977
+ }
2978
+ }
2979
+ }
2980
+ catch (statErr) {
2981
+ // #4249 Nit: EACCES means a parent directory couldn't be searched —
2982
+ // a real (if rare) permission problem, distinct from ENOENT's "missing".
2983
+ // EPERM: Windows' equivalent permission-denied code for a directory a
2984
+ // parent path couldn't be traversed into.
2985
+ const code = statErr?.code;
2986
+ const reason = code === 'EACCES' || code === 'EPERM' ? 'unreadable' : 'missing';
2987
+ invalid.push({ runtime: entry.runtime, configPath: entry.configPath, role: 'script', path: entry.scriptPath, reason });
2988
+ }
2989
+ // #4249: selfExecutable is the sole source of truth for whether the OS
2990
+ // execs scriptPath directly via its own shebang (set explicitly by every
2991
+ // producer that needs it — a Windows-Claude .sh hook, Codex's Windows
2992
+ // .cmd shim, Cline's hybrid `env node` hook — rather than inferred from
2993
+ // the absence of interpreterCandidates, which Cline's hybrid case also
2994
+ // carries). Skip on win32 like resolveExecutableBinary's own X_OK
2995
+ // carve-out does: POSIX mode bits don't mean executable on Windows, and a
2996
+ // real accessSync(X_OK) there would fail a .cmd shim under a test that
2997
+ // simulates win32 on a POSIX runner (Node's own no-op only protects an
2998
+ // actual Windows machine). Cline is the only producer where this runs.
2999
+ if (scriptOk && entry.selfExecutable && (entry.platform ?? process.platform) !== 'win32') {
3000
+ try {
3001
+ accessSync(entry.scriptPath, node_fs_1.default.constants.X_OK);
3002
+ }
3003
+ catch {
3004
+ invalid.push({ runtime: entry.runtime, configPath: entry.configPath, role: 'script', path: entry.scriptPath, reason: 'not-executable' });
3005
+ }
3006
+ }
3007
+ if (entry.interpreterCandidates && !entry.interpreterCandidates.some(candidate => resolve(candidate, { platform: entry.platform, requireExecutable: true }) !== null)) {
3008
+ invalid.push({ runtime: entry.runtime, configPath: entry.configPath, role: 'interpreter', path: entry.interpreterCandidates.join(' | '), reason: 'unresolved-interpreter' });
3009
+ }
3010
+ }
3011
+ return invalid.length === 0 ? { ok: true } : { ok: false, invalid };
3012
+ }
2647
3013
  module.exports = {
2648
3014
  // Cline
2649
3015
  buildClineRulesBody,
@@ -2692,6 +3058,9 @@ module.exports = {
2692
3058
  removeCodexHooksJsonEvent,
2693
3059
  removeCodexHooksJsonSessionStart,
2694
3060
  buildCodexHookWindowsShimIR,
3061
+ cleanupOrphanedCodexContextMonitorScript,
3062
+ isGsdOwnedCodexContextMonitorScript,
3063
+ hooksJsonReferencesCodexContextMonitor,
2695
3064
  // Codex TOML
2696
3065
  buildCodexHookBlock,
2697
3066
  rewriteLegacyCodexHookBlock,
@@ -2705,7 +3074,9 @@ module.exports = {
2705
3074
  // Shared
2706
3075
  stageTransitiveHookLibs,
2707
3076
  buildHookCommand,
3077
+ recordConfiguredHookCommand,
2708
3078
  applySettingsJsonHooks,
3079
+ validateConfiguredEntrypoints,
2709
3080
  referencesHook,
2710
3081
  rewriteLegacyManagedNodeHookCommands,
2711
3082
  reconcileManagedShellHookCommands,