@opengsd/gsd-core 1.15.0 → 1.17.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 (505) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -2
  3. package/.opencode/plugins/gsd-core.js +336 -6
  4. package/.opencode/plugins/gsd-statusline/tui.tsx +55 -0
  5. package/GEMINI.md +1 -1
  6. package/agents/gsd-code-fixer.compact.md +1 -1
  7. package/agents/gsd-code-fixer.md +1 -1
  8. package/agents/gsd-code-reviewer.compact.md +5 -3
  9. package/agents/gsd-code-reviewer.md +8 -6
  10. package/agents/gsd-debug-session-manager.compact.md +7 -1
  11. package/agents/gsd-debug-session-manager.md +7 -1
  12. package/agents/gsd-debugger.md +2 -2
  13. package/agents/gsd-eval-auditor.compact.md +1 -1
  14. package/agents/gsd-eval-auditor.md +1 -1
  15. package/agents/gsd-executor.md +4 -4
  16. package/agents/gsd-intel-updater.compact.md +1 -1
  17. package/agents/gsd-intel-updater.md +1 -1
  18. package/agents/gsd-mempalace-curator.md +2 -2
  19. package/agents/gsd-phase-researcher.md +1 -1
  20. package/agents/gsd-plan-checker.md +7 -2
  21. package/agents/gsd-planner.md +3 -3
  22. package/agents/gsd-project-researcher.compact.md +1 -1
  23. package/agents/gsd-project-researcher.md +1 -1
  24. package/agents/gsd-research-synthesizer.compact.md +1 -1
  25. package/agents/gsd-research-synthesizer.md +1 -1
  26. package/agents/gsd-ui-auditor.compact.md +21 -30
  27. package/agents/gsd-ui-auditor.md +42 -51
  28. package/agents/gsd-ui-researcher.compact.md +1 -1
  29. package/agents/gsd-ui-researcher.md +1 -1
  30. package/agents/gsd-verifier.md +31 -9
  31. package/bin/gsd-mcp-server.js +3 -1
  32. package/bin/install.js +487 -290
  33. package/commands/gsd/add-tests.md +6 -1
  34. package/commands/gsd/ai-integration-phase.md +6 -1
  35. package/commands/gsd/audit-fix.md +5 -0
  36. package/commands/gsd/audit-milestone.md +6 -1
  37. package/commands/gsd/autonomous.md +5 -0
  38. package/commands/gsd/capture.md +8 -4
  39. package/commands/gsd/code-review.md +7 -2
  40. package/commands/gsd/complete-milestone.md +4 -0
  41. package/commands/gsd/config.md +7 -3
  42. package/commands/gsd/debug.md +11 -7
  43. package/commands/gsd/discuss-phase.md +7 -3
  44. package/commands/gsd/docs-update.md +12 -7
  45. package/commands/gsd/eval-review.md +6 -1
  46. package/commands/gsd/execute-phase.md +12 -7
  47. package/commands/gsd/extract-learnings.md +5 -0
  48. package/commands/gsd/fast.md +4 -0
  49. package/commands/gsd/forensics.md +5 -1
  50. package/commands/gsd/graphify.md +10 -6
  51. package/commands/gsd/health.md +5 -0
  52. package/commands/gsd/help.md +7 -2
  53. package/commands/gsd/import.md +7 -3
  54. package/commands/gsd/inbox.md +5 -0
  55. package/commands/gsd/ingest-docs.md +5 -1
  56. package/commands/gsd/manager.md +6 -1
  57. package/commands/gsd/map-codebase.md +7 -3
  58. package/commands/gsd/mempalace-capture.md +5 -1
  59. package/commands/gsd/mempalace-recall.md +5 -1
  60. package/commands/gsd/milestone-summary.md +5 -1
  61. package/commands/gsd/mvp-phase.md +8 -3
  62. package/commands/gsd/new-milestone.md +6 -1
  63. package/commands/gsd/new-project.md +5 -0
  64. package/commands/gsd/next.md +6 -1
  65. package/commands/gsd/ns-context.md +4 -0
  66. package/commands/gsd/ns-ideate.md +4 -0
  67. package/commands/gsd/ns-manage.md +4 -0
  68. package/commands/gsd/ns-project.md +4 -0
  69. package/commands/gsd/ns-review.md +4 -0
  70. package/commands/gsd/ns-workflow.md +4 -0
  71. package/commands/gsd/onboard.md +6 -1
  72. package/commands/gsd/pause-work.md +5 -1
  73. package/commands/gsd/phase.md +8 -4
  74. package/commands/gsd/plan-phase.md +6 -1
  75. package/commands/gsd/plan-review-convergence.md +5 -1
  76. package/commands/gsd/pr-branch.md +4 -0
  77. package/commands/gsd/profile-user.md +5 -1
  78. package/commands/gsd/progress.md +6 -1
  79. package/commands/gsd/quick-batch.md +20 -8
  80. package/commands/gsd/quick.md +12 -7
  81. package/commands/gsd/review.md +5 -1
  82. package/commands/gsd/secure-phase.md +6 -1
  83. package/commands/gsd/ship.md +5 -0
  84. package/commands/gsd/sketch.md +7 -2
  85. package/commands/gsd/spec-phase.md +5 -1
  86. package/commands/gsd/spike.md +8 -3
  87. package/commands/gsd/surface.md +5 -1
  88. package/commands/gsd/thread.md +4 -0
  89. package/commands/gsd/ui-phase.md +6 -1
  90. package/commands/gsd/ui-review.md +6 -1
  91. package/commands/gsd/ultraplan-phase.md +5 -1
  92. package/commands/gsd/undo.md +5 -1
  93. package/commands/gsd/update.md +6 -2
  94. package/commands/gsd/validate-phase.md +6 -1
  95. package/commands/gsd/verify-work.md +6 -1
  96. package/commands/gsd/workspace.md +7 -3
  97. package/gsd-core/bin/gsd-tools.cjs +159 -58
  98. package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
  99. package/gsd-core/bin/lib/adr-parser.cjs +37 -5
  100. package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
  101. package/gsd-core/bin/lib/audit.cjs +78 -44
  102. package/gsd-core/bin/lib/broken-windows.cjs +13 -13
  103. package/gsd-core/bin/lib/capability-activation.cjs +9 -4
  104. package/gsd-core/bin/lib/capability-registry.cjs +249 -117
  105. package/gsd-core/bin/lib/capability-validator.cjs +18 -1
  106. package/gsd-core/bin/lib/capability-writer.cjs +2 -1
  107. package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
  108. package/gsd-core/bin/lib/check-command-router.cjs +164 -1712
  109. package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
  110. package/gsd-core/bin/lib/cli-exit.cjs +12 -0
  111. package/gsd-core/bin/lib/codex-agent-toml.cjs +11 -8
  112. package/gsd-core/bin/lib/command-aliases.cjs +7 -0
  113. package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
  114. package/gsd-core/bin/lib/commands.cjs +201 -178
  115. package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
  116. package/gsd-core/bin/lib/config.cjs +43 -12
  117. package/gsd-core/bin/lib/core-utils.cjs +6 -1
  118. package/gsd-core/bin/lib/coverage.cjs +4 -8
  119. package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
  120. package/gsd-core/bin/lib/drift.cjs +177 -42
  121. package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
  122. package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
  123. package/gsd-core/bin/lib/frontmatter.cjs +413 -234
  124. package/gsd-core/bin/lib/gap-checker.cjs +72 -29
  125. package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
  126. package/gsd-core/bin/lib/gate-args.cjs +53 -0
  127. package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
  128. package/gsd-core/bin/lib/gate-config.cjs +46 -0
  129. package/gsd-core/bin/lib/gate-context-drift.cjs +142 -0
  130. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
  131. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
  132. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
  133. package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
  134. package/gsd-core/bin/lib/gate-exit.cjs +27 -0
  135. package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
  136. package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
  137. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
  138. package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
  139. package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
  140. package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
  141. package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
  142. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
  143. package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
  144. package/gsd-core/bin/lib/gate-ui-safety.cjs +81 -0
  145. package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
  146. package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
  147. package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
  148. package/gsd-core/bin/lib/graphify.cjs +3 -2
  149. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
  150. package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
  151. package/gsd-core/bin/lib/init.cjs +194 -108
  152. package/gsd-core/bin/lib/install-engine.cjs +30 -35
  153. package/gsd-core/bin/lib/install-profiles.cjs +7 -4
  154. package/gsd-core/bin/lib/install-shadow-report.cjs +12 -0
  155. package/gsd-core/bin/lib/installer-migrations/011-opencode-mcp-no-prompts.cjs +99 -0
  156. package/gsd-core/bin/lib/installer-migrations.cjs +54 -1
  157. package/gsd-core/bin/lib/io.cjs +122 -3
  158. package/gsd-core/bin/lib/jsonc-edit.cjs +418 -0
  159. package/gsd-core/bin/lib/loop-host-contract.cjs +2 -1
  160. package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
  161. package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
  162. package/gsd-core/bin/lib/mcp-server.cjs +5 -1
  163. package/gsd-core/bin/lib/milestone.cjs +19 -1
  164. package/gsd-core/bin/lib/model-resolver.cjs +18 -18
  165. package/gsd-core/bin/lib/observability/event.cjs +1 -1
  166. package/gsd-core/bin/lib/observability/logger.cjs +46 -1
  167. package/gsd-core/bin/lib/opencode-mcp-entry.cjs +39 -0
  168. package/gsd-core/bin/lib/opencode-version.cjs +486 -0
  169. package/gsd-core/bin/lib/pattern.cjs +10 -0
  170. package/gsd-core/bin/lib/phase-command-router.cjs +11 -4
  171. package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
  172. package/gsd-core/bin/lib/phase-id.cjs +1 -1
  173. package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
  174. package/gsd-core/bin/lib/phase-status.cjs +360 -0
  175. package/gsd-core/bin/lib/phase.cjs +301 -80
  176. package/gsd-core/bin/lib/plan-document.cjs +93 -19
  177. package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
  178. package/gsd-core/bin/lib/planning-document.cjs +273 -40
  179. package/gsd-core/bin/lib/planning-inspect.cjs +42 -8
  180. package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
  181. package/gsd-core/bin/lib/planning-workspace.cjs +76 -53
  182. package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
  183. package/gsd-core/bin/lib/profile-output.cjs +6 -3
  184. package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
  185. package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
  186. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +13 -9
  187. package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
  188. package/gsd-core/bin/lib/report-parser.cjs +269 -0
  189. package/gsd-core/bin/lib/roadmap-command-router.cjs +13 -15
  190. package/gsd-core/bin/lib/roadmap-parser.cjs +82 -15
  191. package/gsd-core/bin/lib/roadmap-upgrade.cjs +127 -65
  192. package/gsd-core/bin/lib/roadmap.cjs +169 -72
  193. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +186 -161
  194. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +2 -1
  195. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
  196. package/gsd-core/bin/lib/runtime-homes.cjs +13 -10
  197. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +365 -594
  198. package/gsd-core/bin/lib/runtime-name-policy.cjs +142 -27
  199. package/gsd-core/bin/lib/runtime-slash.cjs +60 -30
  200. package/gsd-core/bin/lib/shell-command-projection.cjs +37 -2
  201. package/gsd-core/bin/lib/smart-entry.cjs +111 -56
  202. package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
  203. package/gsd-core/bin/lib/state-contract.cjs +15 -18
  204. package/gsd-core/bin/lib/state-document.cjs +100 -22
  205. package/gsd-core/bin/lib/state-transition.cjs +67 -9
  206. package/gsd-core/bin/lib/state.cjs +668 -171
  207. package/gsd-core/bin/lib/surface.cjs +2 -1
  208. package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -152
  209. package/gsd-core/bin/lib/uat-predicate.cjs +313 -35
  210. package/gsd-core/bin/lib/uat.cjs +424 -7
  211. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +94 -58
  212. package/gsd-core/bin/lib/update-context.cjs +18 -1
  213. package/gsd-core/bin/lib/vendor/README.md +31 -9
  214. package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
  215. package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
  216. package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
  217. package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
  218. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  219. package/gsd-core/bin/lib/verification.cjs +1265 -324
  220. package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
  221. package/gsd-core/bin/lib/verify.cjs +286 -567
  222. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
  223. package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
  224. package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
  225. package/gsd-core/bin/lib/workstream.cjs +11 -2
  226. package/gsd-core/bin/shared/config-defaults.manifest.json +5 -0
  227. package/gsd-core/bin/shared/config-schema.manifest.json +4 -0
  228. package/gsd-core/references/autonomous-smart-discuss.md +2 -1
  229. package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
  230. package/gsd-core/references/execute-mvp-tdd.md +5 -10
  231. package/gsd-core/references/execute-phase-between-wave-reset.md +3 -0
  232. package/gsd-core/references/execute-phase-response-language.md +1 -1
  233. package/gsd-core/references/gsd-run-resolver.md +1 -1
  234. package/gsd-core/references/loop-hook-dispatch.md +7 -1
  235. package/gsd-core/references/offer-next.md +1 -1
  236. package/gsd-core/references/planning-config.md +1 -1
  237. package/gsd-core/references/spidr-splitting.md +1 -1
  238. package/gsd-core/references/tdd.md +10 -4
  239. package/gsd-core/references/verifier-phase-gates.md +5 -2
  240. package/gsd-core/references/verify-mvp-mode.md +2 -2
  241. package/gsd-core/references/workstream-flag.md +33 -3
  242. package/gsd-core/templates/README.md +1 -1
  243. package/gsd-core/templates/UAT.md +17 -1
  244. package/gsd-core/templates/config.json +2 -11
  245. package/gsd-core/templates/verification-report.md +1 -1
  246. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  247. package/gsd-core/workflows/add-backlog.md +1 -1
  248. package/gsd-core/workflows/add-phase.md +8 -7
  249. package/gsd-core/workflows/add-tests.md +3 -2
  250. package/gsd-core/workflows/add-todo.md +4 -3
  251. package/gsd-core/workflows/ai-integration-phase.md +3 -2
  252. package/gsd-core/workflows/audit-fix.md +1 -1
  253. package/gsd-core/workflows/audit-milestone.md +4 -3
  254. package/gsd-core/workflows/audit-uat.md +1 -1
  255. package/gsd-core/workflows/autonomous.md +28 -18
  256. package/gsd-core/workflows/check-todos.md +6 -5
  257. package/gsd-core/workflows/cleanup.md +1 -1
  258. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
  259. package/gsd-core/workflows/code-review-fix.md +5 -4
  260. package/gsd-core/workflows/code-review.md +100 -91
  261. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
  262. package/gsd-core/workflows/complete-milestone.md +12 -7
  263. package/gsd-core/workflows/debug.md +30 -5
  264. package/gsd-core/workflows/diagnose-issues.md +3 -2
  265. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  266. package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
  267. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
  268. package/gsd-core/workflows/discuss-phase.md +4 -3
  269. package/gsd-core/workflows/do.md +1 -1
  270. package/gsd-core/workflows/docs-update.md +4 -3
  271. package/gsd-core/workflows/edit-phase.md +4 -3
  272. package/gsd-core/workflows/eval-review.md +5 -3
  273. package/gsd-core/workflows/execute-phase/detail/elaboration.md +1 -1
  274. package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +4 -2
  275. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
  276. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
  277. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +2 -2
  278. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
  279. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
  280. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  281. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
  282. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +1 -1
  283. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
  284. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
  285. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
  286. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
  287. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
  288. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
  289. package/gsd-core/workflows/execute-phase.md +81 -171
  290. package/gsd-core/workflows/execute-plan.md +13 -19
  291. package/gsd-core/workflows/explore.md +2 -2
  292. package/gsd-core/workflows/extract-learnings.md +3 -2
  293. package/gsd-core/workflows/fast.md +1 -1
  294. package/gsd-core/workflows/forensics.md +1 -1
  295. package/gsd-core/workflows/graduation.md +1 -1
  296. package/gsd-core/workflows/health.md +2 -1
  297. package/gsd-core/workflows/import.md +3 -2
  298. package/gsd-core/workflows/inbox.md +1 -1
  299. package/gsd-core/workflows/ingest-docs.md +1 -1
  300. package/gsd-core/workflows/insert-phase.md +4 -3
  301. package/gsd-core/workflows/list-seeds.md +1 -1
  302. package/gsd-core/workflows/list-workspaces.md +1 -1
  303. package/gsd-core/workflows/manager.md +5 -3
  304. package/gsd-core/workflows/map-codebase.md +4 -3
  305. package/gsd-core/workflows/milestone-summary.md +3 -2
  306. package/gsd-core/workflows/mvp-phase.md +14 -14
  307. package/gsd-core/workflows/new-milestone.md +10 -10
  308. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  309. package/gsd-core/workflows/new-project.md +1 -1
  310. package/gsd-core/workflows/new-workspace.md +1 -1
  311. package/gsd-core/workflows/next.md +1 -1
  312. package/gsd-core/workflows/pause-work.md +2 -2
  313. package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
  314. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  315. package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
  316. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
  317. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +1 -1
  318. package/gsd-core/workflows/plan-phase.md +27 -18
  319. package/gsd-core/workflows/plan-review-convergence.md +1 -1
  320. package/gsd-core/workflows/plant-seed.md +1 -1
  321. package/gsd-core/workflows/pr-branch.md +1 -1
  322. package/gsd-core/workflows/profile-user.md +1 -1
  323. package/gsd-core/workflows/progress.md +19 -49
  324. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -2
  325. package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
  326. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
  327. package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
  328. package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
  329. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
  330. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
  331. package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
  332. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
  333. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
  334. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
  335. package/gsd-core/workflows/quick-batch.md +15 -10
  336. package/gsd-core/workflows/quick.md +42 -31
  337. package/gsd-core/workflows/remove-phase.md +3 -2
  338. package/gsd-core/workflows/remove-workspace.md +1 -1
  339. package/gsd-core/workflows/resume-project.md +17 -12
  340. package/gsd-core/workflows/review.md +3 -2
  341. package/gsd-core/workflows/scan.md +3 -2
  342. package/gsd-core/workflows/secure-phase.md +11 -12
  343. package/gsd-core/workflows/settings-advanced.md +1 -1
  344. package/gsd-core/workflows/settings-integrations.md +1 -1
  345. package/gsd-core/workflows/settings.md +1 -1
  346. package/gsd-core/workflows/ship.md +12 -12
  347. package/gsd-core/workflows/sketch-wrap-up.md +1 -1
  348. package/gsd-core/workflows/sketch.md +1 -1
  349. package/gsd-core/workflows/smart-entry.md +1 -1
  350. package/gsd-core/workflows/spec-phase.md +1 -1
  351. package/gsd-core/workflows/spike-wrap-up.md +1 -1
  352. package/gsd-core/workflows/spike.md +1 -1
  353. package/gsd-core/workflows/stats.md +1 -1
  354. package/gsd-core/workflows/sync-skills.md +1 -1
  355. package/gsd-core/workflows/thread.md +2 -2
  356. package/gsd-core/workflows/transition.md +13 -23
  357. package/gsd-core/workflows/ui-phase.md +5 -4
  358. package/gsd-core/workflows/ui-review.md +4 -3
  359. package/gsd-core/workflows/ultraplan-phase.md +3 -2
  360. package/gsd-core/workflows/undo.md +1 -1
  361. package/gsd-core/workflows/validate-phase.md +10 -12
  362. package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
  363. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
  364. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
  365. package/gsd-core/workflows/verify-work.md +68 -59
  366. package/hooks/dist/gsd-agent-isolation-guard.js +8 -0
  367. package/hooks/dist/gsd-check-update-worker.js +8 -0
  368. package/hooks/dist/gsd-check-update.js +34 -20
  369. package/hooks/dist/gsd-context-monitor.js +45 -13
  370. package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
  371. package/hooks/dist/gsd-secret-read-guard.js +263 -16
  372. package/hooks/dist/gsd-statusline.js +336 -29
  373. package/hooks/dist/gsd-update-banner.js +8 -0
  374. package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
  375. package/hooks/dist/gsd-workflow-guard.js +5 -4
  376. package/hooks/dist/gsd-worktree-path-guard.js +6 -2
  377. package/hooks/dist/lib/cli-exit.js +12 -0
  378. package/hooks/dist/lib/git-probe.js +17 -1
  379. package/hooks/dist/lib/isolation-sentinel.js +2 -2
  380. package/hooks/gsd-agent-isolation-guard.js +8 -0
  381. package/hooks/gsd-check-update-worker.js +8 -0
  382. package/hooks/gsd-check-update.js +34 -20
  383. package/hooks/gsd-context-monitor.js +45 -13
  384. package/hooks/gsd-cursor-subagent-start.js +8 -0
  385. package/hooks/gsd-secret-read-guard.js +263 -16
  386. package/hooks/gsd-statusline.js +336 -29
  387. package/hooks/gsd-update-banner.js +8 -0
  388. package/hooks/gsd-windsurf-pre-write.js +11 -2
  389. package/hooks/gsd-workflow-guard.js +5 -4
  390. package/hooks/gsd-worktree-path-guard.js +6 -2
  391. package/hooks/hooks.json +5 -5
  392. package/hooks/lib/cli-exit.js +12 -0
  393. package/hooks/lib/git-probe.js +17 -1
  394. package/hooks/lib/isolation-sentinel.js +2 -2
  395. package/package.json +21 -4
  396. package/scripts/changeset/parse.cjs +52 -4
  397. package/scripts/ci-timeout-report.cjs +770 -4
  398. package/scripts/command-contract-helpers.cjs +12 -8
  399. package/scripts/docs-guard-registry.cjs +6 -0
  400. package/scripts/gen-capability-registry.cjs +7 -1
  401. package/scripts/gen-features.cjs +13 -8
  402. package/scripts/gen-hooks-cli-exit.cjs +12 -28
  403. package/scripts/gen-loop-host-contract.cjs +14 -2
  404. package/scripts/gen-platform-conformance-tier.cjs +187 -1
  405. package/scripts/gen-plugin-skills.cjs +87 -1
  406. package/scripts/gen-research-agents.cjs +24 -31
  407. package/scripts/gen-scripts-cli-exit.cjs +30 -3
  408. package/scripts/gen-test-timings.cjs +32 -7
  409. package/scripts/lib/cli-exit.cjs +12 -0
  410. package/scripts/lib/macos-conformance-tier.generated.cjs +24 -2
  411. package/scripts/lib/ndjson-reporter.cjs +28 -3
  412. package/scripts/lib/platform-conformance-tier.generated.cjs +44 -5
  413. package/scripts/lib/registration-ledger-preload.cjs +155 -0
  414. package/scripts/lib/vendor-bundle.cjs +59 -0
  415. package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
  416. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  417. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  418. package/scripts/lint-completion-predicate-drift.cjs +18 -19
  419. package/scripts/lint-descriptions.cjs +7 -3
  420. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +10 -0
  421. package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
  422. package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
  423. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
  424. package/scripts/lint-phase-enumeration-drift.cjs +12 -8
  425. package/scripts/lint-phase-id-drift.cjs +29 -0
  426. package/scripts/lint-planning-document-positive-control.cjs +329 -0
  427. package/scripts/lint-response-language-coverage.cjs +3 -0
  428. package/scripts/lint-skill-deps.cjs +7 -3
  429. package/scripts/lint-test-file-count.allowlist.json +11 -1
  430. package/scripts/lint-test-file-count.cjs +34 -1
  431. package/scripts/lint-vendored-deps.cjs +41 -5
  432. package/scripts/lint-workflow-shellcheck-baseline.json +10 -10
  433. package/scripts/mutation-matrix.cjs +53 -6
  434. package/scripts/release-tarball-smoke.cjs +14 -7
  435. package/scripts/require-issue-link-policy.cjs +6 -2
  436. package/scripts/sync-runtime-launcher.cjs +184 -2
  437. package/scripts/verify-npm-publish.cjs +76 -20
  438. package/skills/gsd-add-tests/SKILL.md +6 -1
  439. package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
  440. package/skills/gsd-audit-fix/SKILL.md +5 -0
  441. package/skills/gsd-audit-milestone/SKILL.md +6 -1
  442. package/skills/gsd-autonomous/SKILL.md +5 -0
  443. package/skills/gsd-capture/SKILL.md +8 -4
  444. package/skills/gsd-code-review/SKILL.md +7 -2
  445. package/skills/gsd-complete-milestone/SKILL.md +4 -0
  446. package/skills/gsd-config/SKILL.md +8 -4
  447. package/skills/gsd-debug/SKILL.md +11 -7
  448. package/skills/gsd-discuss-phase/SKILL.md +7 -3
  449. package/skills/gsd-docs-update/SKILL.md +12 -7
  450. package/skills/gsd-eval-review/SKILL.md +6 -1
  451. package/skills/gsd-execute-phase/SKILL.md +12 -7
  452. package/skills/gsd-extract-learnings/SKILL.md +5 -0
  453. package/skills/gsd-fast/SKILL.md +4 -0
  454. package/skills/gsd-forensics/SKILL.md +5 -1
  455. package/skills/gsd-graphify/SKILL.md +10 -6
  456. package/skills/gsd-health/SKILL.md +5 -0
  457. package/skills/gsd-help/SKILL.md +7 -2
  458. package/skills/gsd-import/SKILL.md +7 -3
  459. package/skills/gsd-inbox/SKILL.md +5 -0
  460. package/skills/gsd-ingest-docs/SKILL.md +5 -1
  461. package/skills/gsd-manager/SKILL.md +6 -1
  462. package/skills/gsd-map-codebase/SKILL.md +7 -3
  463. package/skills/gsd-mempalace-capture/SKILL.md +5 -1
  464. package/skills/gsd-mempalace-recall/SKILL.md +5 -1
  465. package/skills/gsd-milestone-summary/SKILL.md +5 -1
  466. package/skills/gsd-mvp-phase/SKILL.md +8 -3
  467. package/skills/gsd-new-milestone/SKILL.md +6 -1
  468. package/skills/gsd-new-project/SKILL.md +5 -0
  469. package/skills/gsd-next/SKILL.md +6 -1
  470. package/skills/gsd-ns-context/SKILL.md +4 -0
  471. package/skills/gsd-ns-ideate/SKILL.md +4 -0
  472. package/skills/gsd-ns-manage/SKILL.md +4 -0
  473. package/skills/gsd-ns-project/SKILL.md +4 -0
  474. package/skills/gsd-ns-review/SKILL.md +4 -0
  475. package/skills/gsd-ns-workflow/SKILL.md +4 -0
  476. package/skills/gsd-onboard/SKILL.md +6 -1
  477. package/skills/gsd-pause-work/SKILL.md +5 -1
  478. package/skills/gsd-phase/SKILL.md +8 -4
  479. package/skills/gsd-plan-phase/SKILL.md +6 -1
  480. package/skills/gsd-plan-review-convergence/SKILL.md +5 -1
  481. package/skills/gsd-pr-branch/SKILL.md +4 -0
  482. package/skills/gsd-profile-user/SKILL.md +5 -1
  483. package/skills/gsd-progress/SKILL.md +6 -1
  484. package/skills/gsd-quick/SKILL.md +16 -10
  485. package/skills/gsd-quick-batch/SKILL.md +20 -8
  486. package/skills/gsd-review/SKILL.md +5 -1
  487. package/skills/gsd-review-backlog/SKILL.md +3 -2
  488. package/skills/gsd-secure-phase/SKILL.md +6 -1
  489. package/skills/gsd-ship/SKILL.md +5 -0
  490. package/skills/gsd-sketch/SKILL.md +7 -2
  491. package/skills/gsd-spec-phase/SKILL.md +5 -1
  492. package/skills/gsd-spike/SKILL.md +8 -3
  493. package/skills/gsd-surface/SKILL.md +5 -1
  494. package/skills/gsd-thread/SKILL.md +4 -0
  495. package/skills/gsd-ui-phase/SKILL.md +6 -1
  496. package/skills/gsd-ui-review/SKILL.md +6 -1
  497. package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
  498. package/skills/gsd-undo/SKILL.md +5 -1
  499. package/skills/gsd-update/SKILL.md +6 -2
  500. package/skills/gsd-validate-phase/SKILL.md +6 -1
  501. package/skills/gsd-verify-work/SKILL.md +6 -1
  502. package/skills/gsd-workspace/SKILL.md +7 -3
  503. package/skills/gsd-workstreams/SKILL.md +6 -6
  504. package/vscode/package.json +1 -1
  505. package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +0 -24
@@ -0,0 +1,418 @@
1
+ "use strict";
2
+ /**
3
+ * Comment-preserving JSONC editing (issue #5334; epic #5331, Phase 2 #5338).
4
+ *
5
+ * OpenCode (1.x and 2.x) and Kilo read `opencode.jsonc`/`kilo.jsonc`, so users annotate them. The
6
+ * installer used to parse such a file and write it back with `JSON.stringify`, which dropped every
7
+ * comment, trailing comma and the user's layout. This module edits the TEXT instead: it locates the
8
+ * property GSD owns and splices only that, leaving every other byte as the user wrote it.
9
+ *
10
+ * Scope is deliberately small — set a value at a key path, remove a key path (pruning objects the
11
+ * removal emptied). It is not a general JSONC formatter. A document it cannot parse or whose root is
12
+ * not an object is reported as `null` so the caller can keep its existing behavior.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.hasJsoncFeatures = hasJsoncFeatures;
16
+ exports.setJsoncValue = setJsoncValue;
17
+ exports.removeJsoncValue = removeJsoncValue;
18
+ exports.isEditableJsonc = isEditableJsonc;
19
+ exports.parseJsonc = parseJsonc;
20
+ class JsoncSyntaxError extends Error {
21
+ }
22
+ function skipTrivia(text, from) {
23
+ let i = from;
24
+ while (i < text.length) {
25
+ const c = text[i];
26
+ if (c === ' ' || c === '\t' || c === '\n' || c === '\r') {
27
+ i++;
28
+ }
29
+ else if (c === '/' && text[i + 1] === '/') {
30
+ while (i < text.length && text[i] !== '\n')
31
+ i++;
32
+ }
33
+ else if (c === '/' && text[i + 1] === '*') {
34
+ const close = text.indexOf('*/', i + 2);
35
+ if (close === -1)
36
+ throw new JsoncSyntaxError('unterminated block comment');
37
+ i = close + 2;
38
+ }
39
+ else {
40
+ break;
41
+ }
42
+ }
43
+ return i;
44
+ }
45
+ function scanString(text, from) {
46
+ let i = from + 1;
47
+ while (i < text.length) {
48
+ const c = text[i];
49
+ if (c === '\\') {
50
+ i += 2;
51
+ }
52
+ else if (c === '"') {
53
+ const raw = text.slice(from, i + 1);
54
+ return { value: JSON.parse(raw), end: i + 1 };
55
+ }
56
+ else if (c === '\n') {
57
+ throw new JsoncSyntaxError('unterminated string');
58
+ }
59
+ else {
60
+ i++;
61
+ }
62
+ }
63
+ throw new JsoncSyntaxError('unterminated string');
64
+ }
65
+ function parseValue(text, from) {
66
+ const i = skipTrivia(text, from);
67
+ const c = text[i];
68
+ if (c === '{')
69
+ return parseObject(text, i);
70
+ if (c === '[')
71
+ return parseArray(text, i);
72
+ if (c === '"') {
73
+ const s = scanString(text, i);
74
+ return { kind: 'string', start: i, end: s.end, props: [] };
75
+ }
76
+ const m = /^(?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)/.exec(text.slice(i, i + 64));
77
+ if (!m)
78
+ throw new JsoncSyntaxError(`unexpected token at ${i}`);
79
+ return { kind: m[0] === 'true' || m[0] === 'false' || m[0] === 'null' ? 'literal' : 'number', start: i, end: i + m[0].length, props: [] };
80
+ }
81
+ function parseArray(text, from) {
82
+ let i = from + 1;
83
+ for (;;) {
84
+ i = skipTrivia(text, i);
85
+ if (text[i] === ']')
86
+ return { kind: 'array', start: from, end: i + 1, props: [] };
87
+ const v = parseValue(text, i);
88
+ i = skipTrivia(text, v.end);
89
+ if (text[i] === ',')
90
+ i++;
91
+ else if (text[i] !== ']')
92
+ throw new JsoncSyntaxError(`expected , or ] at ${i}`);
93
+ }
94
+ }
95
+ function parseObject(text, from) {
96
+ const props = [];
97
+ let i = from + 1;
98
+ for (;;) {
99
+ i = skipTrivia(text, i);
100
+ if (text[i] === '}')
101
+ return { kind: 'object', start: from, end: i + 1, props };
102
+ if (text[i] !== '"')
103
+ throw new JsoncSyntaxError(`expected key at ${i}`);
104
+ const k = scanString(text, i);
105
+ let j = skipTrivia(text, k.end);
106
+ if (text[j] !== ':')
107
+ throw new JsoncSyntaxError(`expected : at ${j}`);
108
+ const value = parseValue(text, j + 1);
109
+ const prop = { key: k.value, start: i, end: value.end, value, commaAfter: -1 };
110
+ props.push(prop);
111
+ j = skipTrivia(text, value.end);
112
+ if (text[j] === ',') {
113
+ prop.commaAfter = j;
114
+ i = j + 1;
115
+ }
116
+ else if (text[j] === '}') {
117
+ i = j;
118
+ }
119
+ else {
120
+ throw new JsoncSyntaxError(`expected , or } at ${j}`);
121
+ }
122
+ }
123
+ }
124
+ function parseRoot(text) {
125
+ try {
126
+ const bom = text.charCodeAt(0) === 0xfeff ? 1 : 0;
127
+ const i = skipTrivia(text, bom);
128
+ if (text[i] !== '{')
129
+ return null;
130
+ const root = parseObject(text, i);
131
+ if (skipTrivia(text, root.end) !== text.length)
132
+ return null;
133
+ return root;
134
+ }
135
+ catch (e) {
136
+ if (e instanceof JsoncSyntaxError || e instanceof SyntaxError)
137
+ return null;
138
+ throw e;
139
+ }
140
+ }
141
+ /** True when `text` carries JSONC-only syntax (comments or a trailing comma) that a rewrite would lose. */
142
+ function hasJsoncFeatures(text) {
143
+ let i = text.charCodeAt(0) === 0xfeff ? 1 : 0;
144
+ while (i < text.length) {
145
+ const c = text[i];
146
+ if (c === '"') {
147
+ try {
148
+ i = scanString(text, i).end;
149
+ }
150
+ catch {
151
+ return false;
152
+ }
153
+ }
154
+ else if (c === '/' && (text[i + 1] === '/' || text[i + 1] === '*')) {
155
+ return true;
156
+ }
157
+ else if (c === ',') {
158
+ const j = skipTrivia(text, i + 1);
159
+ if (text[j] === '}' || text[j] === ']')
160
+ return true;
161
+ i++;
162
+ }
163
+ else {
164
+ i++;
165
+ }
166
+ }
167
+ return false;
168
+ }
169
+ // ─── Layout helpers ──────────────────────────────────────────────────────────
170
+ function lineStart(text, pos) {
171
+ const nl = text.lastIndexOf('\n', pos - 1);
172
+ return nl === -1 ? 0 : nl + 1;
173
+ }
174
+ function indentOf(text, pos) {
175
+ const ls = lineStart(text, pos);
176
+ const lead = text.slice(ls, pos);
177
+ return /^[ \t]*$/.test(lead) ? lead : null;
178
+ }
179
+ function detectUnit(text, root) {
180
+ const first = root.props[0];
181
+ const lead = first ? indentOf(text, first.start) : null;
182
+ if (lead && lead.length > 0)
183
+ return lead;
184
+ return ' ';
185
+ }
186
+ /** Indentation for children of `node`, given the indentation of the line that opens it. */
187
+ function childIndent(text, node, openerIndent, unit) {
188
+ const first = node.props[0];
189
+ const lead = first ? indentOf(text, first.start) : null;
190
+ return lead !== null && lead.length > 0 ? lead : openerIndent + unit;
191
+ }
192
+ function serialize(value, baseIndent, unit) {
193
+ // A short array of primitives stays on one line, the way people write command arrays by hand.
194
+ if (Array.isArray(value) && value.every((v) => v === null || ['string', 'number', 'boolean'].includes(typeof v))) {
195
+ const inline = `[${value.map((v) => JSON.stringify(v)).join(', ')}]`;
196
+ if (inline.length <= 100)
197
+ return inline;
198
+ }
199
+ return JSON.stringify(value, null, unit).split('\n').join('\n' + baseIndent);
200
+ }
201
+ function nest(path, value) {
202
+ let v = value;
203
+ for (let i = path.length - 1; i >= 0; i--)
204
+ v = { [path[i]]: v };
205
+ return v;
206
+ }
207
+ function endOfSameLineTrivia(text, from) {
208
+ let i = from;
209
+ for (;;) {
210
+ while (text[i] === ' ' || text[i] === '\t')
211
+ i++;
212
+ if (text[i] === '/' && text[i + 1] === '/') {
213
+ while (i < text.length && text[i] !== '\n')
214
+ i++;
215
+ return i;
216
+ }
217
+ if (text[i] === '/' && text[i + 1] === '*') {
218
+ const close = text.indexOf('*/', i + 2);
219
+ if (close === -1)
220
+ return i;
221
+ if (text.slice(i, close).includes('\n'))
222
+ return i;
223
+ i = close + 2;
224
+ continue;
225
+ }
226
+ return i;
227
+ }
228
+ }
229
+ function splice(text, start, end, insert) {
230
+ return text.slice(0, start) + insert + text.slice(end);
231
+ }
232
+ // ─── Operations ──────────────────────────────────────────────────────────────
233
+ function insertProperty(text, node, openerIndent, unit, key, value) {
234
+ const indent = childIndent(text, node, openerIndent, unit);
235
+ const rendered = `${JSON.stringify(key)}: ${serialize(value, indent, unit)}`;
236
+ if (node.props.length === 0) {
237
+ const interior = text.slice(node.start + 1, node.end - 1);
238
+ if (interior.trim() === '') {
239
+ return splice(text, node.start + 1, node.end - 1, `\n${indent}${rendered}\n${openerIndent}`);
240
+ }
241
+ // Only comments inside: keep them, add the property after the last one.
242
+ let at = node.end - 1;
243
+ while (at > node.start + 1 && /\s/.test(text[at - 1]))
244
+ at--;
245
+ return splice(text, at, at, `\n${indent}${rendered}`);
246
+ }
247
+ const last = node.props[node.props.length - 1];
248
+ if (last.commaAfter >= 0) {
249
+ // Trailing-comma style: keep it for the new property.
250
+ const at = endOfSameLineTrivia(text, last.commaAfter + 1);
251
+ return splice(text, at, at, `\n${indent}${rendered},`);
252
+ }
253
+ const at = endOfSameLineTrivia(text, last.end);
254
+ const withProp = splice(text, at, at, `\n${indent}${rendered}`);
255
+ return splice(withProp, last.end, last.end, ',');
256
+ }
257
+ function removeProperty(text, node, index) {
258
+ const prop = node.props[index];
259
+ let end = prop.commaAfter >= 0 ? prop.commaAfter + 1 : prop.end;
260
+ end = endOfSameLineTrivia(text, end);
261
+ if (text[end] === '\r')
262
+ end++;
263
+ if (text[end] === '\n')
264
+ end++;
265
+ const lead = indentOf(text, prop.start);
266
+ const start = lead !== null ? lineStart(text, prop.start) : prop.start;
267
+ if (lead === null) {
268
+ // Sharing a line with other content: do not swallow the line break.
269
+ end = prop.commaAfter >= 0 ? prop.commaAfter + 1 : prop.end;
270
+ while (text[end] === ' ')
271
+ end++;
272
+ }
273
+ let out = splice(text, start, end, '');
274
+ // The removed property was last with no trailing comma: drop the comma that preceded it.
275
+ if (prop.commaAfter < 0 && index > 0) {
276
+ const prev = node.props[index - 1];
277
+ if (prev.commaAfter >= 0 && prev.commaAfter < start) {
278
+ out = splice(out, prev.commaAfter, prev.commaAfter + 1, '');
279
+ }
280
+ }
281
+ return out;
282
+ }
283
+ function descend(root, path) {
284
+ const chain = [root];
285
+ let node = root;
286
+ for (let i = 0; i < path.length; i++) {
287
+ const prop = node.props.find((p) => p.key === path[i]);
288
+ if (!prop)
289
+ return { chain, hit: null, depth: i };
290
+ if (i === path.length - 1)
291
+ return { chain, hit: prop, depth: i };
292
+ if (prop.value.kind !== 'object')
293
+ return { chain, hit: prop, depth: i };
294
+ node = prop.value;
295
+ chain.push(node);
296
+ }
297
+ return { chain, hit: null, depth: path.length };
298
+ }
299
+ function openerIndentFor(text, chain, depth, unit) {
300
+ // Indentation of the line holding the opener of chain[depth]; the root opens at column 0.
301
+ if (depth === 0)
302
+ return '';
303
+ const node = chain[depth];
304
+ const lead = indentOf(text, node.start);
305
+ if (lead !== null)
306
+ return lead;
307
+ // `"key": {` on a line of its own: use the key's line indentation.
308
+ const parentProps = (chain[depth - 1]).props;
309
+ const owner = parentProps.find((p) => p.value === node);
310
+ const keyLead = owner ? indentOf(text, owner.start) : null;
311
+ return keyLead ?? unit.repeat(depth);
312
+ }
313
+ /**
314
+ * Set `path` to `value`, creating missing objects. Returns the edited text, or `null` when the
315
+ * document is not an object-rooted JSONC document.
316
+ */
317
+ function setJsoncValue(text, path, value) {
318
+ const root = parseRoot(text);
319
+ if (!root || path.length === 0)
320
+ return null;
321
+ const unit = detectUnit(text, root);
322
+ const { chain, hit, depth } = descend(root, path);
323
+ const atLeaf = hit !== null && depth === path.length - 1;
324
+ if (atLeaf && (hit).value.kind !== undefined) {
325
+ const prop = hit;
326
+ const base = indentOf(text, prop.start) ?? '';
327
+ return splice(text, prop.value.start, prop.value.end, serialize(value, base, unit));
328
+ }
329
+ if (hit !== null) {
330
+ // A non-object sits where an object is needed: replace it with the nested structure.
331
+ const base = indentOf(text, hit.start) ?? '';
332
+ return splice(text, hit.value.start, hit.value.end, serialize(nest(path.slice(depth + 1), value), base, unit));
333
+ }
334
+ const node = chain[chain.length - 1];
335
+ const parentDepth = chain.length - 1;
336
+ const opener = openerIndentFor(text, chain, parentDepth, unit);
337
+ const key = path[parentDepth];
338
+ return insertProperty(text, node, opener, unit, key, nest(path.slice(parentDepth + 1), value));
339
+ }
340
+ /**
341
+ * Remove the key at `path`, then remove any ancestor object (never the root) the removal emptied.
342
+ * Returns the edited text, or `null` when the document is not an object-rooted JSONC document.
343
+ * A path that does not exist leaves the text unchanged.
344
+ */
345
+ function removeJsoncValue(text, path) {
346
+ let root = parseRoot(text);
347
+ if (!root || path.length === 0)
348
+ return null;
349
+ let current = text;
350
+ let target = path;
351
+ for (;;) {
352
+ const { chain, hit, depth } = descend(root, target);
353
+ if (hit === null || depth !== target.length - 1)
354
+ return current;
355
+ const parent = chain[chain.length - 1];
356
+ current = removeProperty(current, parent, parent.props.indexOf(hit));
357
+ root = parseRoot(current);
358
+ if (!root || target.length === 1)
359
+ return current;
360
+ target = target.slice(0, -1);
361
+ const again = descend(root, target);
362
+ const holder = again.hit;
363
+ if (holder === null || again.depth !== target.length - 1 || holder.value.kind !== 'object' || holder.value.props.length > 0) {
364
+ return current;
365
+ }
366
+ }
367
+ }
368
+ /** Parse tolerantly for callers that only need to know whether the text is edit-able. */
369
+ function isEditableJsonc(text) {
370
+ return parseRoot(text) !== null;
371
+ }
372
+ /**
373
+ * Parse JSONC (JSON with comments and trailing commas) into a value. Throws a `SyntaxError` (from
374
+ * `JSON.parse`) on malformed input, like `JSON.parse` itself — callers keep their existing
375
+ * "could not parse, do not touch the file" handling.
376
+ */
377
+ function parseJsonc(content) {
378
+ let text = content.charCodeAt(0) === 0xfeff ? content.slice(1) : content;
379
+ let stripped = '';
380
+ let inString = false;
381
+ let i = 0;
382
+ while (i < text.length) {
383
+ const c = text[i];
384
+ const next = text[i + 1];
385
+ if (inString) {
386
+ stripped += c;
387
+ if (c === '\\' && i + 1 < text.length) {
388
+ stripped += next;
389
+ i += 2;
390
+ continue;
391
+ }
392
+ if (c === '"')
393
+ inString = false;
394
+ i++;
395
+ }
396
+ else if (c === '"') {
397
+ inString = true;
398
+ stripped += c;
399
+ i++;
400
+ }
401
+ else if (c === '/' && next === '/') {
402
+ while (i < text.length && text[i] !== '\n')
403
+ i++;
404
+ }
405
+ else if (c === '/' && next === '*') {
406
+ i += 2;
407
+ while (i < text.length - 1 && !(text[i] === '*' && text[i + 1] === '/'))
408
+ i++;
409
+ i += 2;
410
+ }
411
+ else {
412
+ stripped += c;
413
+ i++;
414
+ }
415
+ }
416
+ text = stripped.replace(/,(\s*[}\]])/g, '$1');
417
+ return JSON.parse(text);
418
+ }
@@ -55,7 +55,8 @@ const LOOP_HOST_CONTRACT = [
55
55
  ],
56
56
  "agentRoles": [
57
57
  "executor",
58
- "verifier"
58
+ "verifier",
59
+ "code-reviewer"
59
60
  ],
60
61
  "coreArtifacts": {
61
62
  "produces": [
@@ -28,10 +28,19 @@
28
28
  * - capability-registry.cjs (byLoopPoint, consumed at call time)
29
29
  * - capability-state.cjs (resolveCapabilityRuntimeState — for capabilities list)
30
30
  */
31
+ var __importDefault = (this && this.__importDefault) || function (mod) {
32
+ return (mod && mod.__esModule) ? mod : { "default": mod };
33
+ };
34
+ const node_fs_1 = __importDefault(require("node:fs"));
35
+ const node_path_1 = __importDefault(require("node:path"));
31
36
  // eslint-disable-next-line @typescript-eslint/no-require-imports
32
37
  const ioMod = require("./io.cjs");
33
38
  const { output: coreOutput, error: coreError } = ioMod;
34
39
  // eslint-disable-next-line @typescript-eslint/no-require-imports
40
+ const verificationMod = require("./verification.cjs");
41
+ const { resolvePhaseArtifactFile } = verificationMod;
42
+ const security_cjs_1 = require("./security.cjs");
43
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
35
44
  const configLoaderModule = require("./config-loader.cjs");
36
45
  const { loadConfig } = configLoaderModule;
37
46
  // eslint-disable-next-line @typescript-eslint/no-require-imports
@@ -528,6 +537,56 @@ function resolveActiveHooksForPoint(cwd, point, options = {}) {
528
537
  const combinedWarnings = [...(state.warnings || []), ...loadFailWarnings];
529
538
  return { point: resolved.point, activeHooks: resolved.activeHooks, warnings: combinedWarnings };
530
539
  }
540
+ /**
541
+ * #5105 R2: a `produces` entry `p` "exists" in `phaseDir` when it resolves via
542
+ * the SAME phase-artifact selection core `resolveVerificationFile`/
543
+ * `resolveUatFile` delegate to (`resolvePhaseArtifactFile`, `verification.cts`)
544
+ * — no second derivation of "which file counts as this phase's artifact"
545
+ * (#3473 F2's generative-divergence class). That core is pure and takes an
546
+ * already-read directory listing of REGULAR-file names only, so a directory
547
+ * of the same name, or a suffixed near-miss (`p.bak`, `p.tmp`), never counts,
548
+ * and `phaseDirName` scoping rejects a stray cross-phase file (`02-SECURITY.md`
549
+ * inside a `01-foo` phase dir) exactly as the aggregate scans do.
550
+ */
551
+ function producesEntryPresent(fileNames, phaseDirName, p) {
552
+ return resolvePhaseArtifactFile([...fileNames], p, { phaseDirName, allowBare: true }) !== null;
553
+ }
554
+ /**
555
+ * #5105 R2: partition `activeHooks` by whether every one of a `kind:"step"`
556
+ * hook's declared `produces` artifacts already exists (as a regular file)
557
+ * directly in `phaseDir`. A hook with `produces: []` (e.g. mempalace-capture),
558
+ * and every gate/contribution, passes through unchanged in `activeHooks`.
559
+ *
560
+ * Throws when `phaseDir` cannot be read (fail closed — the caller converts
561
+ * this to a `coreError` exit, never a silent pass-everything-through).
562
+ */
563
+ function partitionHooksByFingerprint(activeHooks, phaseDir) {
564
+ const entries = node_fs_1.default.readdirSync(phaseDir, { withFileTypes: true });
565
+ const fileNames = entries.filter((e) => e.isFile()).map((e) => e.name);
566
+ const phaseDirName = node_path_1.default.basename(phaseDir);
567
+ const kept = [];
568
+ const skipped = [];
569
+ for (const hook of activeHooks) {
570
+ if (hook.kind !== 'step' || !hook.produces || hook.produces.length === 0) {
571
+ kept.push(hook);
572
+ continue;
573
+ }
574
+ const everyProduced = hook.produces.every((p) => producesEntryPresent(fileNames, phaseDirName, p));
575
+ if (everyProduced) {
576
+ skipped.push({
577
+ capId: hook.capId,
578
+ kind: 'step',
579
+ ref: hook.ref,
580
+ reason: 'produces-present',
581
+ artifacts: [...hook.produces],
582
+ });
583
+ }
584
+ else {
585
+ kept.push(hook);
586
+ }
587
+ }
588
+ return { activeHooks: kept, skippedHooks: skipped };
589
+ }
531
590
  function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
532
591
  if (!point) {
533
592
  coreError('loop render-hooks requires a <point> argument. Valid points: ' + CANONICAL_POINTS.join(', '));
@@ -548,6 +607,38 @@ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
548
607
  coreError(msg);
549
608
  return;
550
609
  }
610
+ // #5105 R2: --after-fingerprint <phaseDir> — gate out verify:post steps
611
+ // whose declared artifact(s) already exist in phaseDir (execute-phase
612
+ // already dispatched them before its own fingerprint; a re-dispatch here
613
+ // would write a post-fingerprint covered path for no reason, #4981/#4887).
614
+ const afterFingerprintDir = typeof options['afterFingerprint'] === 'string' ? options['afterFingerprint'] : undefined;
615
+ let skippedHooks;
616
+ if (afterFingerprintDir !== undefined) {
617
+ // #5105 S10: resolve relative to the handler's own cwd (never the
618
+ // process cwd) and fail closed if it escapes the project root — the same
619
+ // `requireSafePath` seam `uat.cts`'s `cmdUatCompleteSession` uses for its
620
+ // own path argument.
621
+ let safePhaseDir;
622
+ try {
623
+ safePhaseDir = (0, security_cjs_1.requireSafePath)(afterFingerprintDir, cwd, '--after-fingerprint directory', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
624
+ }
625
+ catch (err) {
626
+ const msg = (err instanceof Error) ? err.message : String(err);
627
+ coreError(`--after-fingerprint directory is unsafe: ${msg}`);
628
+ return;
629
+ }
630
+ let partition;
631
+ try {
632
+ partition = partitionHooksByFingerprint(result.activeHooks, safePhaseDir);
633
+ }
634
+ catch (err) {
635
+ const msg = (err instanceof Error) ? err.message : String(err);
636
+ coreError(`--after-fingerprint phase directory not found or unreadable: ${afterFingerprintDir} (${msg})`);
637
+ return;
638
+ }
639
+ result = { point: result.point, activeHooks: partition.activeHooks, warnings: result.warnings };
640
+ skippedHooks = partition.skippedHooks;
641
+ }
551
642
  if (activeCapId !== undefined) {
552
643
  const isActive = result.activeHooks.some((h) => h.capId === activeCapId);
553
644
  process.stdout.write(isActive ? 'true\n' : 'false\n');
@@ -562,6 +653,9 @@ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
562
653
  if (result.warnings.length > 0) {
563
654
  envelope.warnings = result.warnings;
564
655
  }
656
+ if (skippedHooks !== undefined) {
657
+ envelope.skippedHooks = skippedHooks;
658
+ }
565
659
  coreOutput(envelope, raw);
566
660
  }
567
661
  module.exports = {
@@ -569,6 +663,7 @@ module.exports = {
569
663
  renderLoopHooks,
570
664
  cmdLoopRenderHooks,
571
665
  resolveActiveHooksForPoint,
666
+ partitionHooksByFingerprint,
572
667
  // Exported for tests
573
668
  _getNestedConfigValue,
574
669
  _resolveActivationValue,
@@ -11,6 +11,7 @@
11
11
  * ADR-457 build-at-publish: compiled by tsc to gsd-core/bin/lib/markdown-sectionizer.cjs.
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.XML_DECISION_TAG_NAMES = void 0;
14
15
  exports.stripFencedCode = stripFencedCode;
15
16
  exports.stripInlineCode = stripInlineCode;
16
17
  exports.scanInlineCodeSpans = scanInlineCodeSpans;
@@ -21,11 +22,13 @@ exports.collectSections = collectSections;
21
22
  exports.collectSection = collectSection;
22
23
  exports.iterateBullets = iterateBullets;
23
24
  exports.updateBullet = updateBullet;
25
+ exports.updateHeading = updateHeading;
24
26
  exports.extractTaggedBlocks = extractTaggedBlocks;
25
27
  exports.stripTaggedBlocks = stripTaggedBlocks;
26
28
  exports.replaceSection = replaceSection;
27
29
  exports.withSection = withSection;
28
30
  exports.deleteSection = deleteSection;
31
+ exports.extractXmlTagBodies = extractXmlTagBodies;
29
32
  const pattern_cjs_1 = require("./pattern.cjs");
30
33
  // ─── stripFencedCode ──────────────────────────────────────────────────────────
31
34
  /**
@@ -722,7 +725,7 @@ function updateBullet(content, match, transform) {
722
725
  bulletText = nm[2];
723
726
  }
724
727
  }
725
- if (bulletText !== null && match(bulletText, rawLine)) {
728
+ if (bulletText !== null && match(bulletText, rawLine, i)) {
726
729
  const newLine = transform(rawLine);
727
730
  if (typeof newLine !== 'string')
728
731
  return content;
@@ -732,6 +735,40 @@ function updateBullet(content, match, transform) {
732
735
  }
733
736
  return content;
734
737
  }
738
+ // ─── updateHeading ────────────────────────────────────────────────────────────
739
+ /**
740
+ * Rewrite the physical line of the FIRST ATX heading (outside fenced code)
741
+ * accepted by `match` — the heading analogue of `updateBullet` (ADR-5057 §6,
742
+ * Phase 13, #5217). Heading discovery is `tokenizeHeadings`' (the one fence
743
+ * state machine for headings); this function adds only the splice.
744
+ *
745
+ * `rawLine` (second argument to `match`, the only argument to `transform`) is
746
+ * the UNMODIFIED physical line between `\n` separators, so on a CRLF document
747
+ * its trailing `\r` is included and every untouched byte — including every
748
+ * other line's own terminator — is copied from `content` verbatim. `lineIndex`
749
+ * is the 0-based physical line index, the same indexing as
750
+ * `content.split('\n')`, so a caller holding a pre-computed line-indexed plan
751
+ * can disambiguate two headings with identical text.
752
+ *
753
+ * Bounded no-op: no accepted heading, or `transform` returning a non-string,
754
+ * returns `content` unchanged.
755
+ */
756
+ function updateHeading(content, match, transform) {
757
+ if (typeof content !== 'string' || content.length === 0)
758
+ return content;
759
+ for (const heading of tokenizeHeadings(content)) {
760
+ const newlineAt = content.indexOf('\n', heading.offset);
761
+ const lineEnd = newlineAt === -1 ? content.length : newlineAt;
762
+ const rawLine = content.slice(heading.offset, lineEnd);
763
+ if (!match(heading, rawLine, heading.line - 1))
764
+ continue;
765
+ const newLine = transform(rawLine);
766
+ if (typeof newLine !== 'string')
767
+ return content;
768
+ return content.slice(0, heading.offset) + newLine + content.slice(lineEnd);
769
+ }
770
+ return content;
771
+ }
735
772
  // ─── extractTaggedBlocks ──────────────────────────────────────────────────────
736
773
  /**
737
774
  * Return the inner text of every `<tagName>…</tagName>` block in `content`,
@@ -962,5 +999,42 @@ function deleteSection(content, headingPredicate, opts = {}) {
962
999
  const collapsedBefore = before.replace(/(?:\r\n|\n){3,}$/, (m) => (m.includes('\r\n') ? '\r\n\r\n' : '\n\n'));
963
1000
  return collapsedBefore + after;
964
1001
  }
1002
+ // ─── extractXmlTagBodies ──────────────────────────────────────────────────────
1003
+ // #2372: scanned-tag set must match the planner-canonical surfaces where a D-NN citation
1004
+ // is meaningful. `<objective>`/`<tasks>`/`<task>`/`<action>` are the historical core. The
1005
+ // planner is also explicitly told (plan-phase.md) to cite decisions in `<read_first>`,
1006
+ // `<behavior>`, `<verify>`, `<acceptance_criteria>`, and `<done>` — those are scanned too,
1007
+ // so the decision-coverage gate does not report a false gap when a decision is cited in any of them.
1008
+ //
1009
+ // Implementation: per-tag matching, NOT a single wide alternation. A single alternation
1010
+ // like `<(?:a|b|c)>...<\/(?:a|b|c)>` halts the outer tag's body capture at any inner tag
1011
+ // in the set, dropping any citation in the outer tag's prefix prose — e.g.
1012
+ // `<action>per D-05 <verify>npm test</verify></action>` would lose D-05 because `<verify>`
1013
+ // halts the `<action>` body before the citation. Per-tag matching avoids this: each tag's
1014
+ // body terminates only at its OWN closing tag, so `<verify>` inside `<action>` is absorbed
1015
+ // into `<action>`'s body (D-05 caught) AND `<verify>` is matched separately on its own pass.
1016
+ // Each per-tag regex keeps the ReDoS-safe negative-lookahead tempering (#2128).
1017
+ exports.XML_DECISION_TAG_NAMES = ['objective', 'tasks', 'task', 'action', 'read_first', 'behavior', 'verify', 'acceptance_criteria', 'done'];
1018
+ function buildXmlDecisionTagRegex(tagName) {
1019
+ // Per-tag: body tempering stops only at the SAME tag's reopening or closing — other
1020
+ // scanned tags pass through as text into this body. Non-greedy `*?` to first close.
1021
+ return new RegExp(`<${tagName}(?:\\s[^>]{0,1000})?>((?:(?!<${tagName}[\\s>])[\\s\\S])*?)<\\/${tagName}>`, 'gi');
1022
+ }
1023
+ /**
1024
+ * The bodies of every planner-canonical XML tag (`XML_DECISION_TAG_NAMES`) in `text`,
1025
+ * joined by `\n`. Each tag's body ends only at its OWN closing tag, so an inner scanned tag
1026
+ * is absorbed into the outer body AND matched on its own pass.
1027
+ */
1028
+ function extractXmlTagBodies(text) {
1029
+ const parts = [];
1030
+ for (const tagName of exports.XML_DECISION_TAG_NAMES) {
1031
+ const re = buildXmlDecisionTagRegex(tagName);
1032
+ for (const match of text.matchAll(re)) {
1033
+ if (match[1])
1034
+ parts.push(match[1]);
1035
+ }
1036
+ }
1037
+ return parts.join('\n');
1038
+ }
965
1039
  // Consumers: require('../gsd-core/bin/lib/markdown-sectionizer.cjs')
966
1040
  // Named CJS exports are the canonical surface (ADR-457 .cts → .cjs build-at-publish).