@opengsd/gsd-core 1.11.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (395) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-code-fixer.md +1 -1
  4. package/agents/gsd-debug-session-manager.md +1 -1
  5. package/agents/gsd-debugger.md +1 -1
  6. package/agents/gsd-dom-verifier.md +169 -0
  7. package/agents/gsd-eval-auditor.md +1 -1
  8. package/agents/gsd-executor.md +17 -9
  9. package/agents/gsd-framework-selector.md +1 -3
  10. package/agents/gsd-intel-updater.md +1 -1
  11. package/agents/gsd-mempalace-curator.md +0 -1
  12. package/agents/gsd-pattern-mapper.md +11 -0
  13. package/agents/gsd-phase-researcher.md +3 -1
  14. package/agents/gsd-plan-checker.md +15 -55
  15. package/agents/gsd-planner.md +6 -4
  16. package/agents/gsd-project-researcher.md +1 -1
  17. package/agents/gsd-research-synthesizer.md +2 -2
  18. package/agents/gsd-roadmapper.md +15 -11
  19. package/agents/gsd-ui-checker.md +63 -4
  20. package/agents/gsd-ui-researcher.md +41 -3
  21. package/agents/gsd-verifier.md +1 -1
  22. package/bin/install.js +609 -134
  23. package/commands/gsd/discuss-phase.md +1 -1
  24. package/commands/gsd/import.md +1 -1
  25. package/commands/gsd/quick.md +8 -4
  26. package/gsd-core/bin/gsd-tools.cjs +567 -51
  27. package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
  28. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  29. package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
  30. package/gsd-core/bin/lib/api-coverage.cjs +30 -9
  31. package/gsd-core/bin/lib/artifacts.cjs +2 -0
  32. package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
  33. package/gsd-core/bin/lib/audit.cjs +163 -41
  34. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  35. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  36. package/gsd-core/bin/lib/capability-registry.cjs +336 -95
  37. package/gsd-core/bin/lib/capability-state.cjs +18 -3
  38. package/gsd-core/bin/lib/capability-validator.cjs +205 -18
  39. package/gsd-core/bin/lib/check-command-router.cjs +145 -5
  40. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  41. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  42. package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
  43. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  44. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  45. package/gsd-core/bin/lib/commands.cjs +543 -44
  46. package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
  47. package/gsd-core/bin/lib/config-loader.cjs +118 -29
  48. package/gsd-core/bin/lib/config.cjs +92 -2
  49. package/gsd-core/bin/lib/configuration.cjs +129 -37
  50. package/gsd-core/bin/lib/core-utils.cjs +84 -7
  51. package/gsd-core/bin/lib/edge-probe.cjs +9 -1
  52. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  53. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  54. package/gsd-core/bin/lib/frontmatter.cjs +840 -305
  55. package/gsd-core/bin/lib/gap-checker.cjs +27 -3
  56. package/gsd-core/bin/lib/git-base-branch.cjs +174 -39
  57. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
  58. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +6 -3
  59. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
  60. package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
  61. package/gsd-core/bin/lib/host-integration.cjs +39 -6
  62. package/gsd-core/bin/lib/init-command-router.cjs +118 -21
  63. package/gsd-core/bin/lib/init.cjs +120 -41
  64. package/gsd-core/bin/lib/install-engine.cjs +68 -3
  65. package/gsd-core/bin/lib/install-model-override-resolver.cjs +33 -1
  66. package/gsd-core/bin/lib/install-profiles.cjs +78 -4
  67. package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
  68. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  69. package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
  70. package/gsd-core/bin/lib/intel.cjs +101 -26
  71. package/gsd-core/bin/lib/io.cjs +160 -15
  72. package/gsd-core/bin/lib/learnings.cjs +85 -14
  73. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  74. package/gsd-core/bin/lib/markdown-table.cjs +52 -4
  75. package/gsd-core/bin/lib/milestone.cjs +90 -5
  76. package/gsd-core/bin/lib/model-catalog.cjs +177 -19
  77. package/gsd-core/bin/lib/model-resolver.cjs +10 -28
  78. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  79. package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
  80. package/gsd-core/bin/lib/phase-id.cjs +70 -4
  81. package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
  82. package/gsd-core/bin/lib/phase-locator.cjs +138 -17
  83. package/gsd-core/bin/lib/phase.cjs +405 -84
  84. package/gsd-core/bin/lib/plan-document.cjs +263 -0
  85. package/gsd-core/bin/lib/plan-scan.cjs +13 -2
  86. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  87. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  88. package/gsd-core/bin/lib/planning-snapshot.cjs +18 -14
  89. package/gsd-core/bin/lib/planning-workspace.cjs +56 -0
  90. package/gsd-core/bin/lib/probe-core.cjs +4 -1
  91. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  92. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  93. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  94. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
  95. package/gsd-core/bin/lib/review-lane-descriptor.cjs +9 -9
  96. package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
  97. package/gsd-core/bin/lib/roadmap-parser.cjs +79 -16
  98. package/gsd-core/bin/lib/roadmap.cjs +74 -19
  99. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +96 -8
  100. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +34 -1
  101. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +287 -55
  102. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  103. package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
  104. package/gsd-core/bin/lib/shell-command-projection.cjs +71 -8
  105. package/gsd-core/bin/lib/smart-entry.cjs +12 -22
  106. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  107. package/gsd-core/bin/lib/state-command-router.cjs +47 -18
  108. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  109. package/gsd-core/bin/lib/state-document.cjs +186 -0
  110. package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
  111. package/gsd-core/bin/lib/state-transition.cjs +517 -101
  112. package/gsd-core/bin/lib/state.cjs +946 -163
  113. package/gsd-core/bin/lib/surface.cjs +10 -2
  114. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  115. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  116. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  117. package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
  118. package/gsd-core/bin/lib/uat.cjs +1376 -125
  119. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  120. package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
  121. package/gsd-core/bin/lib/unusable-input.cjs +13 -0
  122. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  123. package/gsd-core/bin/lib/vendor/README.md +43 -5
  124. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  125. package/gsd-core/bin/lib/verification.cjs +14 -1
  126. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  127. package/gsd-core/bin/lib/verify.cjs +95 -40
  128. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  129. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  130. package/gsd-core/bin/lib/worktree-safety.cjs +177 -21
  131. package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
  132. package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
  133. package/gsd-core/bin/shared/exit-codes.json +8 -0
  134. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  135. package/gsd-core/bin/shared/model-catalog.json +8 -1
  136. package/gsd-core/references/agent-contracts.md +3 -2
  137. package/gsd-core/references/api-coverage.md +24 -2
  138. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  139. package/gsd-core/references/checkpoints.md +37 -19
  140. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  141. package/gsd-core/references/edge-probe.md +8 -0
  142. package/gsd-core/references/execute-mvp-tdd.md +1 -3
  143. package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
  144. package/gsd-core/references/execute-phase-wave-guard.md +11 -9
  145. package/gsd-core/references/failing-direction.md +78 -0
  146. package/gsd-core/references/gate-prompts.md +1 -1
  147. package/gsd-core/references/git-integration.md +5 -5
  148. package/gsd-core/references/git-planning-commit.md +3 -3
  149. package/gsd-core/references/gsd-run-resolver.md +1 -1
  150. package/gsd-core/references/loop-hook-dispatch.md +22 -0
  151. package/gsd-core/references/model-profiles.md +1 -1
  152. package/gsd-core/references/nyquist-compliance.md +74 -0
  153. package/gsd-core/references/offer-next.md +3 -5
  154. package/gsd-core/references/phase-argument-parsing.md +3 -3
  155. package/gsd-core/references/planner-failing-direction.md +53 -0
  156. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  157. package/gsd-core/references/planner-revision.md +1 -1
  158. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  159. package/gsd-core/references/planning-config.md +37 -8
  160. package/gsd-core/references/reviewer-instances.md +31 -0
  161. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  162. package/gsd-core/references/tdd.md +1 -3
  163. package/gsd-core/references/ui-brand.md +65 -21
  164. package/gsd-core/references/ui-consideration-probe.md +1 -1
  165. package/gsd-core/references/universal-anti-patterns.md +2 -2
  166. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  167. package/gsd-core/references/verify-mvp-mode.md +1 -1
  168. package/gsd-core/references/workstream-flag.md +11 -11
  169. package/gsd-core/templates/README.md +1 -1
  170. package/gsd-core/templates/SECURITY.md +3 -3
  171. package/gsd-core/templates/UI-SPEC.md +25 -3
  172. package/gsd-core/templates/VALIDATION.md +3 -3
  173. package/gsd-core/templates/phase-prompt.md +3 -0
  174. package/gsd-core/templates/state.md +7 -0
  175. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  176. package/gsd-core/workflows/add-backlog.md +1 -1
  177. package/gsd-core/workflows/add-phase.md +3 -3
  178. package/gsd-core/workflows/add-tests.md +3 -8
  179. package/gsd-core/workflows/add-todo.md +1 -1
  180. package/gsd-core/workflows/ai-integration-phase.md +4 -9
  181. package/gsd-core/workflows/audit-fix.md +12 -3
  182. package/gsd-core/workflows/audit-milestone.md +9 -9
  183. package/gsd-core/workflows/audit-uat.md +17 -2
  184. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  185. package/gsd-core/workflows/autonomous.md +10 -26
  186. package/gsd-core/workflows/check-todos.md +1 -1
  187. package/gsd-core/workflows/cleanup.md +2 -2
  188. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +1 -1
  189. package/gsd-core/workflows/code-review-fix.md +1 -1
  190. package/gsd-core/workflows/code-review.md +121 -40
  191. package/gsd-core/workflows/complete-milestone.md +15 -10
  192. package/gsd-core/workflows/debug.md +5 -3
  193. package/gsd-core/workflows/diagnose-issues.md +12 -6
  194. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  195. package/gsd-core/workflows/discuss-phase/modes/chain.md +3 -7
  196. package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
  197. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  198. package/gsd-core/workflows/discuss-phase-assumptions.md +2 -2
  199. package/gsd-core/workflows/discuss-phase.md +1 -1
  200. package/gsd-core/workflows/do.md +3 -6
  201. package/gsd-core/workflows/docs-update.md +5 -4
  202. package/gsd-core/workflows/edit-phase.md +1 -1
  203. package/gsd-core/workflows/eval-review.md +4 -9
  204. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
  205. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +113 -11
  206. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  207. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  208. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  209. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +22 -4
  210. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  211. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  212. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
  213. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  214. package/gsd-core/workflows/execute-phase.md +38 -54
  215. package/gsd-core/workflows/execute-plan.md +17 -12
  216. package/gsd-core/workflows/explore.md +1 -1
  217. package/gsd-core/workflows/extract-learnings.md +1 -1
  218. package/gsd-core/workflows/fast.md +2 -2
  219. package/gsd-core/workflows/forensics.md +1 -1
  220. package/gsd-core/workflows/graduation.md +5 -5
  221. package/gsd-core/workflows/health.md +3 -6
  222. package/gsd-core/workflows/import.md +14 -11
  223. package/gsd-core/workflows/inbox.md +4 -5
  224. package/gsd-core/workflows/ingest-docs.md +44 -11
  225. package/gsd-core/workflows/insert-phase.md +5 -5
  226. package/gsd-core/workflows/list-seeds.md +5 -3
  227. package/gsd-core/workflows/list-workspaces.md +1 -1
  228. package/gsd-core/workflows/manager.md +12 -23
  229. package/gsd-core/workflows/map-codebase.md +1 -1
  230. package/gsd-core/workflows/milestone-summary.md +1 -1
  231. package/gsd-core/workflows/mvp-phase.md +2 -2
  232. package/gsd-core/workflows/new-milestone.md +9 -21
  233. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  234. package/gsd-core/workflows/new-project.md +12 -26
  235. package/gsd-core/workflows/new-workspace.md +1 -1
  236. package/gsd-core/workflows/next.md +2 -2
  237. package/gsd-core/workflows/pause-work.md +1 -1
  238. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  239. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  240. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
  241. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
  242. package/gsd-core/workflows/plan-phase.md +121 -42
  243. package/gsd-core/workflows/plan-review-convergence.md +46 -9
  244. package/gsd-core/workflows/plant-seed.md +2 -2
  245. package/gsd-core/workflows/pr-branch.md +187 -51
  246. package/gsd-core/workflows/profile-user.md +16 -14
  247. package/gsd-core/workflows/progress.md +27 -12
  248. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  249. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -3
  250. package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
  251. package/gsd-core/workflows/quick/steps/research-phase.md +2 -4
  252. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  253. package/gsd-core/workflows/quick.md +20 -29
  254. package/gsd-core/workflows/remove-phase.md +4 -4
  255. package/gsd-core/workflows/remove-workspace.md +2 -2
  256. package/gsd-core/workflows/resume-project.md +8 -12
  257. package/gsd-core/workflows/review.md +193 -15
  258. package/gsd-core/workflows/scan.md +1 -1
  259. package/gsd-core/workflows/secure-phase.md +2 -2
  260. package/gsd-core/workflows/settings-advanced.md +7 -9
  261. package/gsd-core/workflows/settings-integrations.md +64 -31
  262. package/gsd-core/workflows/settings.md +3 -5
  263. package/gsd-core/workflows/ship.md +12 -6
  264. package/gsd-core/workflows/sketch-wrap-up.md +11 -17
  265. package/gsd-core/workflows/sketch.md +12 -18
  266. package/gsd-core/workflows/smart-entry.md +3 -5
  267. package/gsd-core/workflows/spec-phase.md +23 -1
  268. package/gsd-core/workflows/spike-wrap-up.md +7 -11
  269. package/gsd-core/workflows/spike.md +20 -31
  270. package/gsd-core/workflows/stats.md +2 -2
  271. package/gsd-core/workflows/sync-skills.md +1 -1
  272. package/gsd-core/workflows/thread.md +11 -7
  273. package/gsd-core/workflows/transition.md +5 -5
  274. package/gsd-core/workflows/ui-phase.md +10 -16
  275. package/gsd-core/workflows/ui-review.md +6 -10
  276. package/gsd-core/workflows/ultraplan-phase.md +5 -13
  277. package/gsd-core/workflows/undo.md +8 -16
  278. package/gsd-core/workflows/update.md +6 -10
  279. package/gsd-core/workflows/validate-phase.md +2 -2
  280. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  281. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  282. package/gsd-core/workflows/verify-work.md +57 -18
  283. package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
  284. package/hooks/dist/gsd-config-reload.js +18 -12
  285. package/hooks/dist/gsd-context-monitor.js +19 -10
  286. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  287. package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
  288. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  289. package/hooks/dist/gsd-cursor-stop.js +2 -1
  290. package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
  291. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
  292. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  293. package/hooks/dist/gsd-graphify-update.sh +22 -18
  294. package/hooks/dist/gsd-node-runner.sh +76 -0
  295. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  296. package/hooks/dist/gsd-prompt-guard.js +16 -7
  297. package/hooks/dist/gsd-read-guard.js +16 -7
  298. package/hooks/dist/gsd-read-injection-scanner.js +17 -8
  299. package/hooks/dist/gsd-session-state.sh +1 -0
  300. package/hooks/dist/gsd-statusline.js +215 -26
  301. package/hooks/dist/gsd-validate-commit.sh +80 -6
  302. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  303. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  304. package/hooks/dist/gsd-workflow-guard.js +34 -16
  305. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  306. package/hooks/dist/gsd-write-guard.js +35 -25
  307. package/hooks/dist/lib/cli-exit.js +560 -0
  308. package/hooks/dist/lib/exit-code-registry.js +98 -0
  309. package/hooks/dist/lib/git-probe.js +84 -0
  310. package/hooks/dist/lib/hook-exit.js +81 -0
  311. package/hooks/dist/managed-hooks-registry.cjs +3 -0
  312. package/hooks/gsd-agent-isolation-guard.js +77 -38
  313. package/hooks/gsd-config-reload.js +18 -12
  314. package/hooks/gsd-context-monitor.js +19 -10
  315. package/hooks/gsd-cursor-post-tool.js +3 -1
  316. package/hooks/gsd-cursor-pre-tool.js +3 -1
  317. package/hooks/gsd-cursor-session-start.js +2 -1
  318. package/hooks/gsd-cursor-stop.js +2 -1
  319. package/hooks/gsd-cursor-subagent-start.js +28 -23
  320. package/hooks/gsd-cursor-subagent-stop.js +3 -1
  321. package/hooks/gsd-ensure-canonical-path.js +2 -1
  322. package/hooks/gsd-graphify-update.sh +22 -18
  323. package/hooks/gsd-node-runner.sh +76 -0
  324. package/hooks/gsd-phase-boundary.sh +1 -0
  325. package/hooks/gsd-prompt-guard.js +16 -7
  326. package/hooks/gsd-read-guard.js +16 -7
  327. package/hooks/gsd-read-injection-scanner.js +17 -8
  328. package/hooks/gsd-session-state.sh +1 -0
  329. package/hooks/gsd-statusline.js +215 -26
  330. package/hooks/gsd-validate-commit.sh +80 -6
  331. package/hooks/gsd-windsurf-pre-command.js +16 -11
  332. package/hooks/gsd-windsurf-pre-write.js +22 -13
  333. package/hooks/gsd-workflow-guard.js +34 -16
  334. package/hooks/gsd-worktree-path-guard.js +36 -21
  335. package/hooks/gsd-write-guard.js +35 -25
  336. package/hooks/lib/cli-exit.js +560 -0
  337. package/hooks/lib/exit-code-registry.js +98 -0
  338. package/hooks/lib/git-probe.js +84 -0
  339. package/hooks/lib/hook-exit.js +81 -0
  340. package/hooks/managed-hooks-registry.cjs +3 -0
  341. package/package.json +12 -7
  342. package/scripts/base64-scan.sh +74 -12
  343. package/scripts/build-hooks.js +5 -0
  344. package/scripts/check-glossary-refs.cjs +77 -15
  345. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  346. package/scripts/ci-check-job-near-cap.cjs +49 -0
  347. package/scripts/ci-pr-mergeability.cjs +262 -0
  348. package/scripts/ci-test-scope.cjs +45 -12
  349. package/scripts/ci-timeout-report.cjs +230 -0
  350. package/scripts/docs-guard-registry.cjs +396 -0
  351. package/scripts/gen-capability-registry.cjs +8 -6
  352. package/scripts/gen-exit-code-docs.cjs +318 -0
  353. package/scripts/gen-exit-code-registry.cjs +891 -0
  354. package/scripts/gen-features.cjs +836 -0
  355. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  356. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  357. package/scripts/gen-loop-host-contract.cjs +134 -1
  358. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  359. package/scripts/gen-state-md-docs.cjs +727 -0
  360. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  361. package/scripts/lib/ci-job-timing.cjs +72 -0
  362. package/scripts/lib/cli-exit.cjs +546 -44
  363. package/scripts/lib/drift-scan.cjs +32 -2
  364. package/scripts/lib/exit-code-registry.cjs +98 -0
  365. package/scripts/lib/ndjson-reporter.cjs +119 -0
  366. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  367. package/scripts/lint-docs-guard-registration.cjs +495 -0
  368. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
  369. package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
  370. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  371. package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
  372. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  373. package/scripts/lint-phase-enumeration-drift.cjs +21 -8
  374. package/scripts/lint-planning-prompt-drift.cjs +38 -1
  375. package/scripts/lint-removed-but-needed.cjs +184 -16
  376. package/scripts/lint-seam-enforcement.cjs +182 -0
  377. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  378. package/scripts/lint-source-test-name-collision.cjs +241 -0
  379. package/scripts/lint-state-write-path-drift.cjs +337 -432
  380. package/scripts/lint-test-file-count.allowlist.json +122 -4
  381. package/scripts/lint-test-file-count.cjs +25 -3
  382. package/scripts/lint-unreachable-guard-drift.cjs +51 -64
  383. package/scripts/lint-vendored-deps.cjs +208 -35
  384. package/scripts/mutation-matrix.cjs +599 -50
  385. package/scripts/prompt-injection-scan.sh +75 -14
  386. package/scripts/secret-scan.sh +75 -13
  387. package/scripts/select-docs-guards.cjs +56 -0
  388. package/scripts/sync-runtime-launcher.cjs +22 -3
  389. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  390. package/skills/gsd-import/SKILL.md +1 -1
  391. package/skills/gsd-quick/SKILL.md +8 -4
  392. package/vscode/package.json +1 -1
  393. package/bin/lib/ui-safety-gate.cjs +0 -109
  394. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  395. package/scripts/state-write-path-drift-baseline.json +0 -19
@@ -6,12 +6,29 @@
6
6
  # scripts/prompt-injection-scan.sh --file path/to/file # Scan a single file
7
7
  # scripts/prompt-injection-scan.sh --dir agents/ # Scan all files in a directory
8
8
  #
9
- # Exit codes:
10
- # 0 = clean
11
- # 1 = findings detected
12
- # 2 = usage error
9
+ # Exit codes (ADR-3889, #3908 — registered in gsd-core/bin/shared/exit-codes.json):
10
+ # 0 = clean
11
+ # 1 = findings detected
12
+ # $EXIT_USAGE (64) = usage error (bad argv, missing --file/--dir target)
13
+ # $EXIT_NO_INPUT (66) = ran; scope established; zero files in scope (genuinely empty)
14
+ # $EXIT_UNAVAILABLE (69) = could not establish scope (bad ref, not a repo, unreadable dir)
13
15
  set -euo pipefail
14
16
 
17
+ # ─── Exit-code registry (ADR-3889, #3908) ────────────────────────────────────
18
+ # Resolved relative to THIS script's location, not the caller's cwd. Loud,
19
+ # non-zero failure if the fragment is missing — never fall back to a guessed
20
+ # literal integer, and never let a missing registry silently degrade to
21
+ # exit 0.
22
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
23
+ EXIT_CODES_SH="$SCRIPT_DIR/../gsd-core/bin/shared/exit-codes.sh"
24
+ if [[ ! -f "$EXIT_CODES_SH" ]]; then
25
+ echo "prompt-injection-scan: FATAL: exit-code registry not found at $EXIT_CODES_SH" >&2
26
+ echo " Regenerate with: node scripts/gen-exit-code-registry.cjs --write" >&2
27
+ exit 1
28
+ fi
29
+ # shellcheck disable=SC1090
30
+ . "$EXIT_CODES_SH"
31
+
15
32
  # ─── Patterns ────────────────────────────────────────────────────────────────
16
33
  # Each pattern is a POSIX extended regex. Keep alphabetized by category.
17
34
  #
@@ -175,33 +192,73 @@ collect_files() {
175
192
  case "$mode" in
176
193
  --diff)
177
194
  local base="${1:-origin/main}"
178
- # Get changed files in the diff, filter to scannable extensions
179
- git diff --name-only --diff-filter=ACMR "$base"...HEAD 2>/dev/null \
180
- | grep -E '\.(md|cjs|js|json|yml|yaml|sh)$' || true
195
+ # Run git separately from the filter pipe so its OWN exit status (not
196
+ # grep's) decides whether the diff could be established. stdout and
197
+ # stderr are captured SEPARATELY (never merged with `2>&1`) so that a
198
+ # warning git writes to stderr on an otherwise successful diff can
199
+ # never be mistaken for a filename in the file list. On failure the
200
+ # captured stderr is emitted as the diagnostic; on success it is
201
+ # forwarded as a warning, never folded into the file list.
202
+ # `|| true` on the filter below is CORRECT (not gratuitous): `grep -E`
203
+ # exits 1 when nothing matches the scannable extensions (e.g. a diff
204
+ # touching only non-scannable file types), which is a legitimate empty
205
+ # result, not a failure to run.
206
+ local raw status err_file
207
+ err_file=$(mktemp)
208
+ raw=$(git diff --name-only --diff-filter=ACMR "$base"...HEAD 2>"$err_file")
209
+ status=$?
210
+ if (( status != 0 )); then
211
+ cat "$err_file" >&2
212
+ rm -f "$err_file"
213
+ exit "$EXIT_UNAVAILABLE"
214
+ fi
215
+ if [[ -s "$err_file" ]]; then
216
+ echo "Warning: git diff emitted stderr output:" >&2
217
+ cat "$err_file" >&2
218
+ fi
219
+ rm -f "$err_file"
220
+ printf '%s\n' "$raw" | grep -E '\.(md|cjs|js|json|yml|yaml|sh)$' || true
181
221
  ;;
182
222
  --file)
183
223
  if [[ -f "$1" ]]; then
184
224
  echo "$1"
185
225
  else
186
226
  echo "Error: file not found: $1" >&2
187
- exit 2
227
+ exit "$EXIT_USAGE"
188
228
  fi
189
229
  ;;
190
230
  --dir)
191
231
  local dir="$1"
192
232
  if [[ ! -d "$dir" ]]; then
193
233
  echo "Error: directory not found: $dir" >&2
194
- exit 2
234
+ exit "$EXIT_USAGE"
235
+ fi
236
+ # Same treatment as --diff: a `find` that fails (e.g. permission
237
+ # denied) must not be reported as an empty directory, and stdout/stderr
238
+ # are captured separately so a stderr warning never enters the file list.
239
+ local raw status err_file
240
+ err_file=$(mktemp)
241
+ raw=$(find "$dir" -type f \( -name '*.md' -o -name '*.cjs' -o -name '*.js' -o -name '*.json' -o -name '*.yml' -o -name '*.yaml' -o -name '*.sh' \) \
242
+ ! -path '*/node_modules/*' ! -path '*/.git/*' ! -path '*/dist/*' 2>"$err_file")
243
+ status=$?
244
+ if (( status != 0 )); then
245
+ cat "$err_file" >&2
246
+ rm -f "$err_file"
247
+ exit "$EXIT_UNAVAILABLE"
248
+ fi
249
+ if [[ -s "$err_file" ]]; then
250
+ echo "Warning: find emitted stderr output:" >&2
251
+ cat "$err_file" >&2
195
252
  fi
196
- find "$dir" -type f \( -name '*.md' -o -name '*.cjs' -o -name '*.js' -o -name '*.json' -o -name '*.yml' -o -name '*.yaml' -o -name '*.sh' \) \
197
- ! -path '*/node_modules/*' ! -path '*/.git/*' ! -path '*/dist/*' 2>/dev/null || true
253
+ rm -f "$err_file"
254
+ printf '%s\n' "$raw"
198
255
  ;;
199
256
  --stdin)
200
257
  cat
201
258
  ;;
202
259
  *)
203
260
  echo "Usage: $0 --diff [base] | --file <path> | --dir <path> | --stdin" >&2
204
- exit 2
261
+ exit "$EXIT_USAGE"
205
262
  ;;
206
263
  esac
207
264
  }
@@ -240,7 +297,7 @@ scan_file() {
240
297
  main() {
241
298
  if [[ $# -eq 0 ]]; then
242
299
  echo "Usage: $0 --diff [base] | --file <path> | --dir <path>" >&2
243
- exit 2
300
+ exit "$EXIT_USAGE"
244
301
  fi
245
302
 
246
303
  local mode="$1"
@@ -250,8 +307,12 @@ main() {
250
307
  files=$(collect_files "$mode" "$@")
251
308
 
252
309
  if [[ -z "$files" ]]; then
310
+ # collect_files already exited (UNAVAILABLE/USAGE) for anything that
311
+ # could not establish scope. Reaching here with an empty result means
312
+ # scope WAS established and is genuinely empty — that is NO_INPUT, not a
313
+ # silent clean pass.
253
314
  echo "prompt-injection-scan: no files to scan"
254
- exit 0
315
+ exit "$EXIT_NO_INPUT"
255
316
  fi
256
317
 
257
318
  local total=0
@@ -17,10 +17,12 @@
17
17
  # This flag does not change secret-detection logic — only which
18
18
  # exclusions are applied.
19
19
  #
20
- # Exit codes:
21
- # 0 = clean
22
- # 1 = findings detected
23
- # 2 = usage error
20
+ # Exit codes (ADR-3889, #3908 — registered in gsd-core/bin/shared/exit-codes.json):
21
+ # 0 = clean
22
+ # 1 = findings detected
23
+ # $EXIT_USAGE (64) = usage error (bad argv, missing --file/--dir target)
24
+ # $EXIT_NO_INPUT (66) = ran; scope established; zero files in scope (genuinely empty)
25
+ # $EXIT_UNAVAILABLE (69) = could not establish scope (bad ref, not a repo, unreadable dir)
24
26
  #
25
27
  # Annotation format for .secretscanignore (required for --strict compliance):
26
28
  # # allow: <pattern> reason="..." owner="..." expires="YYYY-MM-DD" [rule-id="..."]
@@ -39,6 +41,21 @@
39
41
  # cannot permanently hide secrets. See SECURITY.md for the audit runbook.
40
42
  set -euo pipefail
41
43
 
44
+ # ─── Exit-code registry (ADR-3889, #3908) ────────────────────────────────────
45
+ # Resolved relative to THIS script's location, not the caller's cwd, so this
46
+ # works regardless of where the scanner is invoked from. Loud, non-zero
47
+ # failure if the fragment is missing — never fall back to a guessed literal
48
+ # integer, and never let a missing registry silently degrade to exit 0.
49
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
50
+ EXIT_CODES_SH="$SCRIPT_DIR/../gsd-core/bin/shared/exit-codes.sh"
51
+ if [[ ! -f "$EXIT_CODES_SH" ]]; then
52
+ echo "secret-scan: FATAL: exit-code registry not found at $EXIT_CODES_SH" >&2
53
+ echo " Regenerate with: node scripts/gen-exit-code-registry.cjs --write" >&2
54
+ exit 1
55
+ fi
56
+ # shellcheck disable=SC1090
57
+ . "$EXIT_CODES_SH"
58
+
42
59
  # ─── Global mode flag ─────────────────────────────────────────────────────────
43
60
  STRICT_MODE=false
44
61
 
@@ -235,7 +252,31 @@ collect_files() {
235
252
  case "$mode" in
236
253
  --diff)
237
254
  local base="${1:-origin/main}"
238
- git diff --name-only --diff-filter=ACMR "$base"...HEAD 2>/dev/null \
255
+ # Run git separately from the filter pipe so its OWN exit status (not
256
+ # grep's) decides whether the diff could be established. stdout and
257
+ # stderr are captured SEPARATELY (never merged with `2>&1`) so that a
258
+ # warning git writes to stderr on an otherwise successful diff can
259
+ # never be mistaken for a filename in the file list. On failure the
260
+ # captured stderr is emitted as the diagnostic; on success it is
261
+ # forwarded as a warning, never folded into the file list.
262
+ # `|| true` on the filter below is CORRECT (not gratuitous): `grep -v`
263
+ # exits 1 when every line is filtered out (e.g. an all-images diff),
264
+ # which is a legitimate empty result, not a failure to run.
265
+ local raw status err_file
266
+ err_file=$(mktemp)
267
+ raw=$(git diff --name-only --diff-filter=ACMR "$base"...HEAD 2>"$err_file")
268
+ status=$?
269
+ if (( status != 0 )); then
270
+ cat "$err_file" >&2
271
+ rm -f "$err_file"
272
+ exit "$EXIT_UNAVAILABLE"
273
+ fi
274
+ if [[ -s "$err_file" ]]; then
275
+ echo "Warning: git diff emitted stderr output:" >&2
276
+ cat "$err_file" >&2
277
+ fi
278
+ rm -f "$err_file"
279
+ printf '%s\n' "$raw" \
239
280
  | grep -vE '\.(png|jpg|jpeg|gif|ico|woff|woff2|ttf|eot|otf|zip|tar|gz|pdf)$' || true
240
281
  ;;
241
282
  --file)
@@ -243,24 +284,41 @@ collect_files() {
243
284
  echo "$1"
244
285
  else
245
286
  echo "Error: file not found: $1" >&2
246
- exit 2
287
+ exit "$EXIT_USAGE"
247
288
  fi
248
289
  ;;
249
290
  --dir)
250
291
  local dir="$1"
251
292
  if [[ ! -d "$dir" ]]; then
252
293
  echo "Error: directory not found: $dir" >&2
253
- exit 2
294
+ exit "$EXIT_USAGE"
295
+ fi
296
+ # Same treatment as --diff: a `find` that fails (e.g. permission
297
+ # denied) must not be reported as an empty directory, and stdout/stderr
298
+ # are captured separately so a stderr warning never enters the file list.
299
+ local raw status err_file
300
+ err_file=$(mktemp)
301
+ raw=$(find "$dir" -type f ! -path '*/node_modules/*' ! -path '*/.git/*' ! -path '*/dist/*' \
302
+ ! -name '*.png' ! -name '*.jpg' ! -name '*.gif' ! -name '*.woff*' 2>"$err_file")
303
+ status=$?
304
+ if (( status != 0 )); then
305
+ cat "$err_file" >&2
306
+ rm -f "$err_file"
307
+ exit "$EXIT_UNAVAILABLE"
308
+ fi
309
+ if [[ -s "$err_file" ]]; then
310
+ echo "Warning: find emitted stderr output:" >&2
311
+ cat "$err_file" >&2
254
312
  fi
255
- find "$dir" -type f ! -path '*/node_modules/*' ! -path '*/.git/*' ! -path '*/dist/*' \
256
- ! -name '*.png' ! -name '*.jpg' ! -name '*.gif' ! -name '*.woff*' 2>/dev/null || true
313
+ rm -f "$err_file"
314
+ printf '%s\n' "$raw"
257
315
  ;;
258
316
  --stdin)
259
317
  cat
260
318
  ;;
261
319
  *)
262
320
  echo "Usage: $0 --diff [base] | --file <path> | --dir <path> | --stdin" >&2
263
- exit 2
321
+ exit "$EXIT_USAGE"
264
322
  ;;
265
323
  esac
266
324
  }
@@ -300,7 +358,7 @@ scan_file() {
300
358
  main() {
301
359
  if [[ $# -eq 0 ]]; then
302
360
  echo "Usage: $0 --diff [base] | --file <path> | --dir <path> [--strict]" >&2
303
- exit 2
361
+ exit "$EXIT_USAGE"
304
362
  fi
305
363
 
306
364
  # Parse --strict flag first (may appear anywhere in argv)
@@ -316,7 +374,7 @@ main() {
316
374
 
317
375
  if [[ $# -eq 0 ]]; then
318
376
  echo "Usage: $0 --diff [base] | --file <path> | --dir <path> [--strict]" >&2
319
- exit 2
377
+ exit "$EXIT_USAGE"
320
378
  fi
321
379
 
322
380
  load_ignorelist
@@ -328,8 +386,12 @@ main() {
328
386
  files=$(collect_files "$mode" "$@")
329
387
 
330
388
  if [[ -z "$files" ]]; then
389
+ # collect_files already exited (UNAVAILABLE/USAGE) for anything that
390
+ # could not establish scope. Reaching here with an empty result means
391
+ # scope WAS established and is genuinely empty (e.g. an all-images
392
+ # diff) — that is NO_INPUT, not a silent clean pass.
331
393
  echo "secret-scan: no files to scan"
332
- exit 0
394
+ exit "$EXIT_NO_INPUT"
333
395
  fi
334
396
 
335
397
  local total=0
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * select-docs-guards.cjs — pure selector mapping a PR's changed docs/ paths
6
+ * to the subset of scripts/docs-guard-registry.cjs's DOCS_GUARD_TESTS that
7
+ * actually reads any of them (#3753 follow-up).
8
+ *
9
+ * Deliberately dependency-free: no fs, no git, no process. Callers (the
10
+ * docs-required.yml workflow, tests) are responsible for producing the
11
+ * changed-paths list and reading the registry; this module only implements
12
+ * the matching semantics so they are independently unit-testable.
13
+ *
14
+ * Pattern semantics (mirrors scripts/docs-guard-registry.cjs's header doc):
15
+ * - a plain path matches an exact changed path;
16
+ * - a trailing-slash path is a DIRECTORY PREFIX match — 'docs/adr/'
17
+ * matches 'docs/adr/README.md' but must NOT match 'docs/adrenaline.md'
18
+ * (a naive `startsWith('docs/adr')` without the trailing slash would
19
+ * wrongly match the latter; matching against the full prefix INCLUDING
20
+ * the trailing slash is what keeps this boundary correct);
21
+ * - the sentinel '*' matches any non-empty changedDocsPaths.
22
+ */
23
+
24
+ /**
25
+ * @param {string} changedPath - a single changed docs/ path (e.g. 'docs/AGENTS.md').
26
+ * @param {string} pattern - one entry from a registry value array.
27
+ * @returns {boolean}
28
+ */
29
+ function patternMatches(changedPath, pattern) {
30
+ if (pattern === '*') return true;
31
+ if (pattern.endsWith('/')) return changedPath.startsWith(pattern);
32
+ return changedPath === pattern;
33
+ }
34
+
35
+ /**
36
+ * @param {string[]} changedDocsPaths - repo-relative paths under docs/ that
37
+ * changed in this PR. An empty array always yields an empty selection.
38
+ * @param {Record<string, string[]>} registry - DOCS_GUARD_TESTS shape: test
39
+ * file -> array of patterns it reads.
40
+ * @returns {string[]} sorted, deduped list of selected test file paths.
41
+ */
42
+ function selectDocsGuards(changedDocsPaths, registry) {
43
+ if (!Array.isArray(changedDocsPaths) || changedDocsPaths.length === 0) return [];
44
+
45
+ const selected = new Set();
46
+ for (const [testFile, patterns] of Object.entries(registry)) {
47
+ const hit = patterns.some((pattern) =>
48
+ changedDocsPaths.some((changedPath) => patternMatches(changedPath, pattern)),
49
+ );
50
+ if (hit) selected.add(testFile);
51
+ }
52
+
53
+ return [...selected].sort();
54
+ }
55
+
56
+ module.exports = { selectDocsGuards, patternMatches };
@@ -330,6 +330,16 @@ function transformFile(content, preamble) {
330
330
  let changed = false;
331
331
  let firstGsdRunBlockIdx = -1; // index into shellBlockRanges of the first gsd_run block (after strip)
332
332
 
333
+ // A file may place the preamble in a bootstrap-only block that defines
334
+ // gsd_run without calling it (gsd-core/workflows/explore.md Step 1, which
335
+ // documents exactly why). Stripping empties that block of gsd_run calls, so
336
+ // a pure "first block that CALLS gsd_run" target silently relocates the
337
+ // preamble forward and breaks define-before-use. Honour where it already is.
338
+ const existingPreambleBlockIdx = shellBlockRanges.findIndex((range) =>
339
+ allLines
340
+ .slice(range.contentStart, range.contentEnd)
341
+ .some((l) => /^\s*_GSD_SHIM_NAME=/.test(l)));
342
+
333
343
  // First: strip + replace in all blocks, find first gsd_run block
334
344
  const strippedBlocks = shellBlockRanges.map((range) => {
335
345
  const blockLines = outputLines.slice(range.contentStart, range.contentEnd);
@@ -345,12 +355,17 @@ function transformFile(content, preamble) {
345
355
  }
346
356
  }
347
357
 
348
- // Insert preamble into the first gsd_run block only — UNLESS this file
358
+ // If the preamble already existed in a specific block before stripping,
359
+ // keep it there (even if that block no longer calls gsd_run after
360
+ // stripping). Otherwise fall back to the first gsd_run-calling block.
361
+ const preambleTargetIdx = existingPreambleBlockIdx >= 0 ? existingPreambleBlockIdx : firstGsdRunBlockIdx;
362
+
363
+ // Insert preamble into the target block only — UNLESS this file
349
364
  // delegates to the shared resolver reference (@-include). Delegating files keep
350
365
  // the stripped blocks (any inline preamble removed) but never get one inserted.
351
366
  const delegates = delegatesToResolverReference(content);
352
367
  const finalBlocks = strippedBlocks.map((stripped, bi) => {
353
- if (!delegates && bi === firstGsdRunBlockIdx) {
368
+ if (!delegates && bi === preambleTargetIdx) {
354
369
  return insertPreamble(stripped, preamble);
355
370
  }
356
371
  return stripped;
@@ -428,4 +443,8 @@ function main() {
428
443
  console.log(`\nDone. ${transformedCount} files transformed, ${unchangedCount} unchanged.`);
429
444
  }
430
445
 
431
- main();
446
+ if (require.main === module) {
447
+ main();
448
+ }
449
+
450
+ module.exports = { transformFile };
@@ -48,7 +48,7 @@ Context files are resolved in-workflow using `init phase-op` and roadmap/state t
48
48
  **Mode routing:**
49
49
  ```bash
50
50
  _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi
51
- DISCUSS_MODE=$(gsd_run query config-get workflow.discuss_mode 2>/dev/null || echo "discuss")
51
+ DISCUSS_MODE=$(gsd_run query config-get workflow.discuss_mode --raw 2>/dev/null || echo "discuss")
52
52
  ```
53
53
 
54
54
  If `--assumptions` is in $ARGUMENTS:
@@ -18,7 +18,7 @@ allowed-tools:
18
18
  Import external plan files into the GSD planning system with conflict detection against PROJECT.md decisions.
19
19
 
20
20
  - **--from**: Import an external plan file, detect conflicts, write as GSD PLAN.md, validate via gsd-plan-checker.
21
- - **--from-gsd2**: Reverse-migrate a GSD-2 project (`.gsd/` directory) back to GSD v1 (`.planning/`) format. Runs `gsd-tools.cjs from-gsd2`. Pass `--path <dir>` to migrate a project at a different path.
21
+ - **--from-gsd2**: Reverse-migrate a GSD-2 project (`.gsd/` directory) back to GSD v1 (`.planning/`) format. Runs `gsd_run from-gsd2`. Pass `--path <dir>` to migrate a project at a different path.
22
22
  </objective>
23
23
 
24
24
  <execution_context>
@@ -86,12 +86,14 @@ For each directory found:
86
86
  Display format:
87
87
  ```
88
88
  Quick Tasks
89
- ────────────────────────────────────────────────────────────
89
+
90
+ ---
90
91
  slug date status
91
92
  backup-s3-policy 2026-04-10 in-progress
92
93
  auth-token-refresh-fix 2026-04-09 complete ✓
93
94
  update-node-deps 2026-04-08 abandoned? (>7 days, no summary)
94
- ────────────────────────────────────────────────────────────
95
+
96
+ ---
95
97
  3 tasks (1 complete, 2 incomplete/in-progress)
96
98
  ```
97
99
 
@@ -113,12 +115,14 @@ If no directory found, print `No quick task found with slug: {SLUG}` and stop.
113
115
  Read PLAN.md and SUMMARY.md (if exists) for the given slug. Display:
114
116
  ```
115
117
  Quick Task: {slug}
116
- ─────────────────────────────────────
118
+
119
+ ---
117
120
  Plan file: .planning/quick/{dir}/PLAN.md
118
121
  Status: {status from SUMMARY.md frontmatter, or "no summary yet"}
119
122
  Description: {first non-empty line from PLAN.md after frontmatter}
120
123
  Last action: {last meaningful line of SUMMARY.md, or "none"}
121
- ─────────────────────────────────────
124
+
125
+ ---
122
126
  Resume with: /gsd-quick resume {slug}
123
127
  ```
124
128
 
@@ -2,7 +2,7 @@
2
2
  "name": "gsd-core-vscode",
3
3
  "displayName": "GSD Core",
4
4
  "description": "GSD orchestration engine embedded in VS Code (ADR-1239 IDE profile).",
5
- "version": "1.11.0",
5
+ "version": "1.12.0",
6
6
  "publisher": "opengsd",
7
7
  "engines": {
8
8
  "vscode": "^1.105.0"
@@ -1,109 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * UI Safety Gate — shell-free implementation (#3706, #3718)
5
- *
6
- * Replaces the bash shell-based one-liner that silently degraded on Windows
7
- * PowerShell / cmd.exe because the locale env-var prefix was not recognised.
8
- * This module runs inside Node.js — no shell dependency, works identically
9
- * on bash, Git-Bash, PowerShell, and cmd.exe.
10
- *
11
- * Word-boundary anchoring:
12
- * (^|[^a-zA-Z0-9])(TOKEN)([^a-zA-Z0-9]|$)
13
- * Equivalent to POSIX ERE [^[:alnum:]] — matches tokens only when they are not
14
- * interior substrings of alphanumeric compound words (e.g. "microfrontend" is NOT
15
- * matched; "micro-frontend" and "micro frontend" ARE matched).
16
- *
17
- * Public API:
18
- * checkUiPresence(text: string): { hasUI: boolean, tokens: string[] }
19
- *
20
- * CLI usage — reads phase-section text from STDIN to avoid ARG_MAX limits:
21
- * echo "$PHASE_SECTION" | node bin/lib/ui-safety-gate.cjs
22
- * echo $? → 0 if UI tokens found, 1 if not, 2 on usage error
23
- *
24
- * Exit codes mirror grep: 0 = match found, 1 = no match, 2 = usage error.
25
- */
26
-
27
- const UI_TOKENS = [
28
- 'UI',
29
- 'interface',
30
- 'frontend',
31
- 'component',
32
- 'layout',
33
- 'page',
34
- 'screen',
35
- 'view',
36
- 'form',
37
- 'dashboard',
38
- 'widget',
39
- ];
40
-
41
- /**
42
- * Built once at module load — no per-call compilation overhead.
43
- * ASCII word boundaries — matches the original ASCII-grep intent of #3706.
44
- * Note: JS [a-zA-Z0-9] is ASCII-only and NOT equivalent to POSIX [[:alnum:]],
45
- * which is locale-sensitive and includes accented characters.
46
- */
47
- const UI_GATE_PATTERN = new RegExp(
48
- '(^|[^a-zA-Z0-9])(' + UI_TOKENS.join('|') + ')([^a-zA-Z0-9]|$)',
49
- 'i'
50
- );
51
-
52
- // Global-flagged variant for extracting ALL matches per line (matchAll).
53
- const UI_GATE_PATTERN_GLOBAL = new RegExp(UI_GATE_PATTERN.source, 'gi');
54
-
55
- /**
56
- * Check a roadmap phase section string for frontend UI indicators.
57
- *
58
- * @param {string} text - The roadmap phase section content (may be multi-line, CRLF or LF).
59
- * @returns {{ hasUI: boolean, tokens: string[] }}
60
- * hasUI — true if any UI token was matched as a standalone word.
61
- * tokens — matched token strings (lowercased), deduplicated.
62
- */
63
- function checkUiPresence(text) {
64
- if (typeof text !== 'string') {
65
- return { hasUI: false, tokens: [] };
66
- }
67
-
68
- // Normalise CRLF so the pattern sees consistent line boundaries.
69
- const normalised = text.replace(/\r\n/g, '\n');
70
-
71
- const found = new Set();
72
- for (const line of normalised.split('\n')) {
73
- // Reset lastIndex before each line so the global pattern restarts from 0.
74
- UI_GATE_PATTERN_GLOBAL.lastIndex = 0;
75
- for (const m of line.matchAll(UI_GATE_PATTERN_GLOBAL)) {
76
- found.add(m[2].toLowerCase());
77
- }
78
- }
79
-
80
- return { hasUI: found.size > 0, tokens: [...found] };
81
- }
82
-
83
- module.exports = { checkUiPresence, UI_TOKENS };
84
-
85
- // ── CLI entry point ─────────────────────────────────────────────────────────
86
- // Reads phase-section text from STDIN (not argv) to avoid OS ARG_MAX limits.
87
- // Invoked by workflow .md bash blocks as: echo "$PHASE_SECTION" | node bin/lib/ui-safety-gate.cjs
88
- // Exit 0 = UI found, 1 = no UI, 2 = startup error.
89
-
90
- if (require.main === module) {
91
- // Collect stdin chunks asynchronously.
92
- const chunks = [];
93
- process.stdin.setEncoding('utf-8');
94
-
95
- process.stdin.on('data', (chunk) => chunks.push(chunk));
96
-
97
- process.stdin.on('end', () => {
98
- const input = chunks.join('');
99
- const result = checkUiPresence(input);
100
- // eslint-disable-next-line n/no-process-exit -- CLI entry point (require.main===module): async stdin handler, nothing else terminates the process (#3059)
101
- process.exit(result.hasUI ? 0 : 1);
102
- });
103
-
104
- process.stdin.on('error', (err) => {
105
- process.stderr.write(`ERROR: ui-safety-gate.cjs stdin read failed: ${err.message}\n`);
106
- // eslint-disable-next-line n/no-process-exit -- CLI entry point (require.main===module): async stdin handler, nothing else terminates the process (#3059)
107
- process.exit(2);
108
- });
109
- }