@opengsd/gsd-core 1.17.0 → 1.19.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 (369) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -1
  3. package/.opencode/plugins/gsd-core.js +2 -3
  4. package/agents/gsd-advisor-researcher.compact.md +1 -1
  5. package/agents/gsd-advisor-researcher.md +1 -1
  6. package/agents/gsd-assumptions-analyzer.compact.md +1 -1
  7. package/agents/gsd-assumptions-analyzer.md +1 -1
  8. package/agents/gsd-code-fixer.compact.md +3 -3
  9. package/agents/gsd-code-fixer.md +5 -4
  10. package/agents/gsd-code-reviewer.compact.md +1 -1
  11. package/agents/gsd-code-reviewer.md +1 -1
  12. package/agents/gsd-codebase-mapper.compact.md +1 -1
  13. package/agents/gsd-codebase-mapper.md +1 -1
  14. package/agents/gsd-debugger.md +1 -1
  15. package/agents/gsd-doc-verifier.compact.md +4 -4
  16. package/agents/gsd-doc-verifier.md +4 -3
  17. package/agents/gsd-doc-writer.compact.md +23 -5
  18. package/agents/gsd-doc-writer.md +19 -6
  19. package/agents/gsd-eval-auditor.compact.md +1 -1
  20. package/agents/gsd-eval-auditor.md +1 -1
  21. package/agents/gsd-executor.md +7 -3
  22. package/agents/gsd-integration-checker.compact.md +1 -1
  23. package/agents/gsd-integration-checker.md +1 -1
  24. package/agents/gsd-nyquist-auditor.compact.md +1 -1
  25. package/agents/gsd-nyquist-auditor.md +1 -1
  26. package/agents/gsd-phase-researcher.md +1 -1
  27. package/agents/gsd-plan-checker.md +2 -1
  28. package/agents/gsd-planner.md +5 -4
  29. package/agents/gsd-project-researcher.compact.md +1 -1
  30. package/agents/gsd-project-researcher.md +1 -1
  31. package/agents/gsd-research-synthesizer.compact.md +5 -5
  32. package/agents/gsd-research-synthesizer.md +5 -5
  33. package/agents/gsd-roadmapper.compact.md +1 -1
  34. package/agents/gsd-roadmapper.md +1 -1
  35. package/agents/gsd-security-auditor.compact.md +1 -1
  36. package/agents/gsd-security-auditor.md +1 -1
  37. package/agents/gsd-ui-auditor.compact.md +1 -1
  38. package/agents/gsd-ui-auditor.md +1 -1
  39. package/agents/gsd-ui-checker.compact.md +1 -1
  40. package/agents/gsd-ui-checker.md +1 -1
  41. package/agents/gsd-ui-researcher.compact.md +13 -1
  42. package/agents/gsd-ui-researcher.md +28 -1
  43. package/agents/gsd-verifier.md +2 -2
  44. package/bin/install.js +460 -105
  45. package/commands/gsd/code-review.md +8 -0
  46. package/commands/gsd/pause-work.md +1 -1
  47. package/gsd-core/bin/gsd-tools.cjs +124 -18
  48. package/gsd-core/bin/lib/agent-install-check.cjs +29 -3
  49. package/gsd-core/bin/lib/args-terminator.cjs +114 -0
  50. package/gsd-core/bin/lib/broken-windows.cjs +59 -1
  51. package/gsd-core/bin/lib/capability-ledger.cjs +25 -0
  52. package/gsd-core/bin/lib/capability-lock.cjs +41 -8
  53. package/gsd-core/bin/lib/capability-registry.cjs +162 -74
  54. package/gsd-core/bin/lib/capability-source.cjs +3 -1
  55. package/gsd-core/bin/lib/capability-state.cjs +20 -11
  56. package/gsd-core/bin/lib/capability-validator.cjs +23 -4
  57. package/gsd-core/bin/lib/commands.cjs +5 -1
  58. package/gsd-core/bin/lib/config-loader.cjs +13 -3
  59. package/gsd-core/bin/lib/config.cjs +256 -58
  60. package/gsd-core/bin/lib/core-utils.cjs +34 -0
  61. package/gsd-core/bin/lib/decision-coverage-support.cjs +93 -0
  62. package/gsd-core/bin/lib/gate-context-drift.cjs +8 -3
  63. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +21 -1
  64. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +19 -1
  65. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +134 -13
  66. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +23 -3
  67. package/gsd-core/bin/lib/gate-predicate.cjs +8 -12
  68. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +114 -18
  69. package/gsd-core/bin/lib/gate-ui-safety.cjs +23 -5
  70. package/gsd-core/bin/lib/git-info-exclude.cjs +57 -0
  71. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +18 -3
  72. package/gsd-core/bin/lib/health-diagnostic-types.cjs +4 -0
  73. package/gsd-core/bin/lib/health-diagnostic.cjs +52 -1
  74. package/gsd-core/bin/lib/host-integration.cjs +13 -6
  75. package/gsd-core/bin/lib/init.cjs +76 -5
  76. package/gsd-core/bin/lib/install-effort-resolver.cjs +2 -2
  77. package/gsd-core/bin/lib/install-engine.cjs +246 -32
  78. package/gsd-core/bin/lib/install-model-override-resolver.cjs +4 -3
  79. package/gsd-core/bin/lib/install-profiles.cjs +14 -7
  80. package/gsd-core/bin/lib/installer-migration-report.cjs +2 -0
  81. package/gsd-core/bin/lib/markdown-line-diff.cjs +147 -0
  82. package/gsd-core/bin/lib/markdown-sectionizer.cjs +113 -0
  83. package/gsd-core/bin/lib/milestone-lock.cjs +8 -1
  84. package/gsd-core/bin/lib/milestone-release-map.cjs +244 -0
  85. package/gsd-core/bin/lib/milestone-release-tags.cjs +440 -0
  86. package/gsd-core/bin/lib/milestone.cjs +43 -1
  87. package/gsd-core/bin/lib/model-catalog.cjs +8 -0
  88. package/gsd-core/bin/lib/normalize-test-command.cjs +67 -0
  89. package/gsd-core/bin/lib/phase-id.cjs +21 -4
  90. package/gsd-core/bin/lib/phase-lifecycle.cjs +37 -4
  91. package/gsd-core/bin/lib/phase.cjs +235 -37
  92. package/gsd-core/bin/lib/planning-snapshot.cjs +3 -1
  93. package/gsd-core/bin/lib/planning-workspace.cjs +18 -0
  94. package/gsd-core/bin/lib/pristine-baseline.cjs +11 -3
  95. package/gsd-core/bin/lib/read-only-git-env.cjs +58 -0
  96. package/gsd-core/bin/lib/report-parser.cjs +57 -1
  97. package/gsd-core/bin/lib/review-lane-descriptor.cjs +35 -2
  98. package/gsd-core/bin/lib/review-lane-invocation.cjs +40 -0
  99. package/gsd-core/bin/lib/review-lane-runner.cjs +33 -4
  100. package/gsd-core/bin/lib/review-reviewer-selection.cjs +20 -1
  101. package/gsd-core/bin/lib/roadmap-parser.cjs +12 -0
  102. package/gsd-core/bin/lib/roadmap-upgrade.cjs +39 -2
  103. package/gsd-core/bin/lib/roadmap.cjs +89 -30
  104. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +428 -16
  105. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +8 -4
  106. package/gsd-core/bin/lib/runtime-homes.cjs +18 -0
  107. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +60 -16
  108. package/gsd-core/bin/lib/shell-command-projection.cjs +123 -16
  109. package/gsd-core/bin/lib/smart-entry.cjs +37 -16
  110. package/gsd-core/bin/lib/state-contract.cjs +29 -9
  111. package/gsd-core/bin/lib/state-document.cjs +47 -5
  112. package/gsd-core/bin/lib/state-transition.cjs +150 -34
  113. package/gsd-core/bin/lib/state.cjs +140 -88
  114. package/gsd-core/bin/lib/surface.cjs +9 -0
  115. package/gsd-core/bin/lib/task-command-router.cjs +7 -28
  116. package/gsd-core/bin/lib/task-content-resolution.cjs +46 -1
  117. package/gsd-core/bin/lib/tdd-red-evidence.cjs +5 -0
  118. package/gsd-core/bin/lib/tdd-test-files.cjs +42 -0
  119. package/gsd-core/bin/lib/verification.cjs +96 -9
  120. package/gsd-core/bin/lib/verify.cjs +38 -1
  121. package/gsd-core/bin/lib/worktree-safety.cjs +22 -1
  122. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  123. package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
  124. package/gsd-core/references/agent-contracts.md +1 -1
  125. package/gsd-core/references/agent-skills-bootstrap.md +26 -7
  126. package/gsd-core/references/git-integration.md +14 -3
  127. package/gsd-core/references/loop-hook-dispatch.md +13 -6
  128. package/gsd-core/references/planner-reversibility.md +18 -4
  129. package/gsd-core/references/planner-verify-command-grounding.md +18 -0
  130. package/gsd-core/references/planning-config.md +2 -0
  131. package/gsd-core/references/tdd.md +5 -4
  132. package/gsd-core/references/verifier-phase-gates.md +8 -1
  133. package/gsd-core/templates/DEBUG.md +16 -0
  134. package/gsd-core/templates/config.json +2 -1
  135. package/gsd-core/templates/verification-report.md +1 -1
  136. package/gsd-core/workflows/add-backlog.md +10 -2
  137. package/gsd-core/workflows/add-phase.md +5 -1
  138. package/gsd-core/workflows/add-tests.md +5 -1
  139. package/gsd-core/workflows/add-todo.md +5 -1
  140. package/gsd-core/workflows/ai-integration-phase.md +5 -1
  141. package/gsd-core/workflows/audit-fix.md +2 -1
  142. package/gsd-core/workflows/audit-milestone.md +21 -9
  143. package/gsd-core/workflows/autonomous/templates/minimal-context.md +41 -0
  144. package/gsd-core/workflows/autonomous.md +52 -64
  145. package/gsd-core/workflows/check-todos.md +5 -1
  146. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +10 -1
  147. package/gsd-core/workflows/code-review-fix.md +5 -1
  148. package/gsd-core/workflows/code-review.md +66 -8
  149. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +5 -1
  150. package/gsd-core/workflows/complete-milestone/steps/project-md-evolution-review.md +145 -0
  151. package/gsd-core/workflows/complete-milestone/steps/release-map.md +80 -0
  152. package/gsd-core/workflows/complete-milestone.md +64 -157
  153. package/gsd-core/workflows/debug.md +5 -1
  154. package/gsd-core/workflows/diagnose-issues.md +34 -6
  155. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -1
  156. package/gsd-core/workflows/discuss-phase/modes/power.md +1 -1
  157. package/gsd-core/workflows/discuss-phase-assumptions.md +44 -5
  158. package/gsd-core/workflows/discuss-phase-power.md +19 -2
  159. package/gsd-core/workflows/discuss-phase.md +11 -11
  160. package/gsd-core/workflows/docs-update/detail/elaboration.md +7 -6
  161. package/gsd-core/workflows/docs-update.md +27 -5
  162. package/gsd-core/workflows/edit-phase.md +5 -1
  163. package/gsd-core/workflows/eval-review.md +11 -3
  164. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +5 -1
  165. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +13 -7
  166. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +19 -8
  167. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +21 -3
  168. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +18 -1
  169. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +7 -3
  170. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +11 -2
  171. package/gsd-core/workflows/execute-phase.md +46 -11
  172. package/gsd-core/workflows/execute-plan.md +8 -4
  173. package/gsd-core/workflows/extract-learnings.md +6 -2
  174. package/gsd-core/workflows/forensics.md +5 -1
  175. package/gsd-core/workflows/health.md +2 -1
  176. package/gsd-core/workflows/import.md +6 -2
  177. package/gsd-core/workflows/insert-phase.md +5 -1
  178. package/gsd-core/workflows/manager.md +10 -2
  179. package/gsd-core/workflows/map-codebase.md +5 -1
  180. package/gsd-core/workflows/milestone-summary.md +10 -2
  181. package/gsd-core/workflows/new-milestone.md +13 -5
  182. package/gsd-core/workflows/new-project.md +9 -5
  183. package/gsd-core/workflows/next.md +8 -4
  184. package/gsd-core/workflows/pause-work.md +3 -3
  185. package/gsd-core/workflows/plan-phase/steps/auto-advance-check.md +70 -0
  186. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +7 -3
  187. package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +6 -2
  188. package/gsd-core/workflows/plan-phase.md +54 -86
  189. package/gsd-core/workflows/plan-review-convergence.md +17 -5
  190. package/gsd-core/workflows/plant-seed.md +8 -4
  191. package/gsd-core/workflows/progress.md +6 -2
  192. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -1
  193. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +8 -1
  194. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +2 -1
  195. package/gsd-core/workflows/quick-batch.md +5 -1
  196. package/gsd-core/workflows/quick.md +23 -8
  197. package/gsd-core/workflows/remove-phase.md +5 -1
  198. package/gsd-core/workflows/resume-project.md +5 -1
  199. package/gsd-core/workflows/review.md +22 -15
  200. package/gsd-core/workflows/scan.md +5 -1
  201. package/gsd-core/workflows/section-manifest.json +5 -0
  202. package/gsd-core/workflows/secure-phase.md +6 -2
  203. package/gsd-core/workflows/settings.md +4 -3
  204. package/gsd-core/workflows/ship.md +16 -14
  205. package/gsd-core/workflows/spec-phase.md +3 -6
  206. package/gsd-core/workflows/thread.md +10 -2
  207. package/gsd-core/workflows/transition.md +5 -1
  208. package/gsd-core/workflows/ui-phase.md +5 -1
  209. package/gsd-core/workflows/ui-review.md +6 -2
  210. package/gsd-core/workflows/ultraplan-phase.md +5 -1
  211. package/gsd-core/workflows/update.md +5 -1
  212. package/gsd-core/workflows/validate-phase.md +6 -2
  213. package/gsd-core/workflows/verify-work/detail/elaboration.md +1 -1
  214. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -1
  215. package/gsd-core/workflows/verify-work.md +15 -6
  216. package/hooks/dist/gsd-agent-isolation-guard.js +14 -2
  217. package/hooks/dist/gsd-cursor-subagent-start.js +8 -3
  218. package/hooks/dist/gsd-path-export.js +95 -0
  219. package/hooks/dist/gsd-secret-read-guard.js +244 -53
  220. package/hooks/dist/gsd-statusline.js +35 -2
  221. package/hooks/dist/gsd-validate-commit.sh +27 -5
  222. package/hooks/dist/gsd-windsurf-pre-write.js +28 -1
  223. package/hooks/dist/gsd-workflow-guard.js +1 -1
  224. package/hooks/dist/gsd-worktree-path-guard.js +28 -1
  225. package/hooks/dist/lib/isolation-sentinel.js +44 -0
  226. package/hooks/dist/managed-hooks-registry.cjs +1 -0
  227. package/hooks/gsd-agent-isolation-guard.js +14 -2
  228. package/hooks/gsd-cursor-subagent-start.js +8 -3
  229. package/hooks/gsd-path-export.js +95 -0
  230. package/hooks/gsd-secret-read-guard.js +244 -53
  231. package/hooks/gsd-statusline.js +35 -2
  232. package/hooks/gsd-validate-commit.sh +27 -5
  233. package/hooks/gsd-windsurf-pre-write.js +28 -1
  234. package/hooks/gsd-workflow-guard.js +1 -1
  235. package/hooks/gsd-worktree-path-guard.js +28 -1
  236. package/hooks/hooks.json +1 -0
  237. package/hooks/lib/isolation-sentinel.js +44 -0
  238. package/hooks/managed-hooks-registry.cjs +1 -0
  239. package/package.json +13 -6
  240. package/pi/gsd.cjs +1047 -10
  241. package/pi/package-agents/gsd-advisor-researcher.md +113 -0
  242. package/pi/package-agents/gsd-ai-researcher.md +111 -0
  243. package/pi/package-agents/gsd-assumptions-analyzer.md +110 -0
  244. package/pi/package-agents/gsd-code-fixer.md +769 -0
  245. package/pi/package-agents/gsd-code-reviewer.md +402 -0
  246. package/pi/package-agents/gsd-codebase-mapper.md +850 -0
  247. package/pi/package-agents/gsd-debug-session-manager.md +416 -0
  248. package/pi/package-agents/gsd-debugger.md +1274 -0
  249. package/pi/package-agents/gsd-doc-classifier.md +269 -0
  250. package/pi/package-agents/gsd-doc-synthesizer.md +259 -0
  251. package/pi/package-agents/gsd-doc-verifier.md +212 -0
  252. package/pi/package-agents/gsd-doc-writer.md +626 -0
  253. package/pi/package-agents/gsd-dom-verifier.md +164 -0
  254. package/pi/package-agents/gsd-domain-researcher.md +144 -0
  255. package/pi/package-agents/gsd-eval-auditor.md +185 -0
  256. package/pi/package-agents/gsd-eval-planner.md +149 -0
  257. package/pi/package-agents/gsd-executor.md +894 -0
  258. package/pi/package-agents/gsd-framework-selector.md +159 -0
  259. package/pi/package-agents/gsd-integration-checker.md +476 -0
  260. package/pi/package-agents/gsd-intel-updater.md +338 -0
  261. package/pi/package-agents/gsd-mempalace-curator.md +49 -0
  262. package/pi/package-agents/gsd-nyquist-auditor.md +200 -0
  263. package/pi/package-agents/gsd-pattern-mapper.md +340 -0
  264. package/pi/package-agents/gsd-phase-researcher.md +897 -0
  265. package/pi/package-agents/gsd-plan-checker.md +1077 -0
  266. package/pi/package-agents/gsd-planner.md +1003 -0
  267. package/pi/package-agents/gsd-project-researcher.md +611 -0
  268. package/pi/package-agents/gsd-research-synthesizer.md +259 -0
  269. package/pi/package-agents/gsd-roadmapper.md +779 -0
  270. package/pi/package-agents/gsd-security-auditor.md +171 -0
  271. package/pi/package-agents/gsd-ui-auditor.md +581 -0
  272. package/pi/package-agents/gsd-ui-checker.md +417 -0
  273. package/pi/package-agents/gsd-ui-researcher.md +469 -0
  274. package/pi/package-agents/gsd-user-profiler.md +174 -0
  275. package/pi/package-agents/gsd-verifier.md +963 -0
  276. package/pi/package-agents/manifest.json +39 -0
  277. package/pi/package-skills/gsd-add-tests/SKILL.md +39 -0
  278. package/pi/package-skills/gsd-ai-integration-phase/SKILL.md +36 -0
  279. package/pi/package-skills/gsd-audit-fix/SKILL.md +34 -0
  280. package/pi/package-skills/gsd-audit-milestone/SKILL.md +40 -0
  281. package/pi/package-skills/gsd-audit-uat/SKILL.md +26 -0
  282. package/pi/package-skills/gsd-autonomous/SKILL.md +52 -0
  283. package/pi/package-skills/gsd-capture/SKILL.md +68 -0
  284. package/pi/package-skills/gsd-cleanup/SKILL.md +25 -0
  285. package/pi/package-skills/gsd-code-review/SKILL.md +71 -0
  286. package/pi/package-skills/gsd-complete-milestone/SKILL.md +147 -0
  287. package/pi/package-skills/gsd-config/SKILL.md +60 -0
  288. package/pi/package-skills/gsd-debug/SKILL.md +64 -0
  289. package/pi/package-skills/gsd-discuss-phase/SKILL.md +74 -0
  290. package/pi/package-skills/gsd-docs-update/SKILL.md +50 -0
  291. package/pi/package-skills/gsd-eval-review/SKILL.md +35 -0
  292. package/pi/package-skills/gsd-execute-phase/SKILL.md +62 -0
  293. package/pi/package-skills/gsd-explore/SKILL.md +26 -0
  294. package/pi/package-skills/gsd-extract-learnings/SKILL.md +25 -0
  295. package/pi/package-skills/gsd-fast/SKILL.md +33 -0
  296. package/pi/package-skills/gsd-forensics/SKILL.md +59 -0
  297. package/pi/package-skills/gsd-graphify/SKILL.md +210 -0
  298. package/pi/package-skills/gsd-health/SKILL.md +36 -0
  299. package/pi/package-skills/gsd-help/SKILL.md +37 -0
  300. package/pi/package-skills/gsd-import/SKILL.md +46 -0
  301. package/pi/package-skills/gsd-inbox/SKILL.md +42 -0
  302. package/pi/package-skills/gsd-ingest-docs/SKILL.md +43 -0
  303. package/pi/package-skills/gsd-manager/SKILL.md +46 -0
  304. package/pi/package-skills/gsd-map-codebase/SKILL.md +85 -0
  305. package/pi/package-skills/gsd-mempalace-capture/SKILL.md +111 -0
  306. package/pi/package-skills/gsd-mempalace-recall/SKILL.md +111 -0
  307. package/pi/package-skills/gsd-milestone-summary/SKILL.md +54 -0
  308. package/pi/package-skills/gsd-mvp-phase/SKILL.md +45 -0
  309. package/pi/package-skills/gsd-new-milestone/SKILL.md +49 -0
  310. package/pi/package-skills/gsd-new-project/SKILL.md +49 -0
  311. package/pi/package-skills/gsd-next/SKILL.md +34 -0
  312. package/pi/package-skills/gsd-ns-context/SKILL.md +31 -0
  313. package/pi/package-skills/gsd-ns-ideate/SKILL.md +30 -0
  314. package/pi/package-skills/gsd-ns-manage/SKILL.md +42 -0
  315. package/pi/package-skills/gsd-ns-project/SKILL.md +34 -0
  316. package/pi/package-skills/gsd-ns-review/SKILL.md +35 -0
  317. package/pi/package-skills/gsd-ns-workflow/SKILL.md +44 -0
  318. package/pi/package-skills/gsd-onboard/SKILL.md +46 -0
  319. package/pi/package-skills/gsd-pause-work/SKILL.md +48 -0
  320. package/pi/package-skills/gsd-phase/SKILL.md +61 -0
  321. package/pi/package-skills/gsd-plan-phase/SKILL.md +62 -0
  322. package/pi/package-skills/gsd-plan-review-convergence/SKILL.md +63 -0
  323. package/pi/package-skills/gsd-pr-branch/SKILL.md +31 -0
  324. package/pi/package-skills/gsd-profile-user/SKILL.md +48 -0
  325. package/pi/package-skills/gsd-progress/SKILL.md +51 -0
  326. package/pi/package-skills/gsd-quick/SKILL.md +179 -0
  327. package/pi/package-skills/gsd-quick-batch/SKILL.md +114 -0
  328. package/pi/package-skills/gsd-resume-work/SKILL.md +31 -0
  329. package/pi/package-skills/gsd-review/SKILL.md +50 -0
  330. package/pi/package-skills/gsd-review-backlog/SKILL.md +65 -0
  331. package/pi/package-skills/gsd-secure-phase/SKILL.md +37 -0
  332. package/pi/package-skills/gsd-settings/SKILL.md +30 -0
  333. package/pi/package-skills/gsd-ship/SKILL.md +27 -0
  334. package/pi/package-skills/gsd-sketch/SKILL.md +56 -0
  335. package/pi/package-skills/gsd-spec-phase/SKILL.md +63 -0
  336. package/pi/package-skills/gsd-spike/SKILL.md +53 -0
  337. package/pi/package-skills/gsd-stats/SKILL.md +22 -0
  338. package/pi/package-skills/gsd-surface/SKILL.md +177 -0
  339. package/pi/package-skills/gsd-thread/SKILL.md +29 -0
  340. package/pi/package-skills/gsd-ui-phase/SKILL.md +35 -0
  341. package/pi/package-skills/gsd-ui-review/SKILL.md +35 -0
  342. package/pi/package-skills/gsd-ultraplan-phase/SKILL.md +38 -0
  343. package/pi/package-skills/gsd-undo/SKILL.md +38 -0
  344. package/pi/package-skills/gsd-update/SKILL.md +51 -0
  345. package/pi/package-skills/gsd-validate-phase/SKILL.md +37 -0
  346. package/pi/package-skills/gsd-verify-work/SKILL.md +41 -0
  347. package/pi/package-skills/gsd-workspace/SKILL.md +57 -0
  348. package/pi/package-skills/gsd-workstreams/SKILL.md +73 -0
  349. package/scripts/audit-workflow-script-paths.cjs +1 -1
  350. package/scripts/build-hooks.js +4 -0
  351. package/scripts/command-contract-helpers.cjs +1 -1
  352. package/scripts/diagnose-windows-spawn-watcher.cjs +317 -0
  353. package/scripts/docs-guard-registry.cjs +22 -7
  354. package/scripts/gen-health-docs.cjs +13 -1
  355. package/scripts/gen-pi-package-skills.cjs +217 -0
  356. package/scripts/lib/macos-conformance-tier.generated.cjs +19 -0
  357. package/scripts/lib/platform-conformance-tier.generated.cjs +41 -0
  358. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +3 -3
  359. package/scripts/lint-phase-arg-assignment.cjs +2 -2
  360. package/scripts/lint-retired-runtime-name.cjs +5 -0
  361. package/scripts/lint-workflow-shellcheck-baseline.json +16 -16
  362. package/scripts/npm-audit-baseline.cjs +41 -7
  363. package/scripts/release-tarball-smoke.cjs +1 -0
  364. package/scripts/reviewed-or-maintainer-policy.cjs +181 -0
  365. package/scripts/select-docs-guards.cjs +1 -1
  366. package/scripts/self-test-report-policy.cjs +350 -0
  367. package/skills/gsd-code-review/SKILL.md +8 -0
  368. package/skills/gsd-pause-work/SKILL.md +1 -1
  369. package/vscode/package.json +1 -1
@@ -48,6 +48,14 @@ Optional flags parsed from the `<arguments>` block:
48
48
  - `--files=file1,file2,...` — Explicit file list override. Has highest precedence for file scoping per D-08. When provided, workflow skips SUMMARY.md extraction and git diff fallback entirely.
49
49
 
50
50
  Context files (CLAUDE.md, SUMMARY.md, phase state) are resolved inside the workflow via `gsd-tools query init.phase-op` and delegated to agent via `<required_reading>` blocks.
51
+
52
+ ## Outside a phase
53
+
54
+ Reviewer lanes run without a phase (`gsd_run` is defined by the runtime-launcher preamble inside workflow fences; in a plain shell call `node <install>/gsd-core/bin/gsd-tools.cjs` instead): pipe canonical repo-relative file paths (one per line) to stdin of
55
+ `gsd_run review-lane dispatch-step --repo-root <root> --depth <quick|standard|deep> --base-sha <sha> --run-dir "$(mktemp -d)" --cap-id code-review --point execute:post --explicit <slugs> --raw`.
56
+ `review-lane` flags are space-separated (`--depth quick`), never `--flag=value`. `--explicit` (omitted, nothing dispatches yet the call exits ok), `--depth`, `--base-sha` (lane reviews changes relative to it; commit them first) and stdin paths are required; `--run-dir` should be a fresh temp dir (default `.` writes the prompt file into the current directory); read each lane's output path (`results[].reviewPath`) from the JSON before removing it. `--cap-id`/`--point` select the `code-review` capability, which must be enabled (`workflow.code_review`) and active at `--point` — `execute:post`, or `execute:wave:post` when `workflow.code_review_point` selects it; otherwise nothing dispatches.
57
+ `--explicit` takes comma-separated canonical slugs, no spaces (`codex,antigravity`), not flag aliases (`antigravity`, not `agy`); slugs: first column of `gsd_run review-lane sections` (`review-lane flags` lists flags/aliases).
58
+ Each lane declares its own model key, which is not always its slug (antigravity's is `review.models.agy`); see the `review.models.*` table in `docs/CONFIGURATION.md`. Unset = lane default model.
51
59
  </context>
52
60
 
53
61
  <process>
@@ -21,7 +21,7 @@ Routes to the pause-work workflow which handles:
21
21
  - Current phase detection from recent files
22
22
  - Complete state gathering (position, completed work, remaining work, decisions, blockers)
23
23
  - Handoff file creation with all context sections
24
- - Git commit as WIP
24
+ - Git commit handoff
25
25
  - Resume instructions
26
26
  </objective>
27
27
 
@@ -85,6 +85,14 @@
85
85
  * [--no-archive-phases] Skip moving phase dirs to milestones/vX.Y-phases/ (archived by default)
86
86
  * [--archive-quick] Move .planning/quick/* dirs to milestones/vX.Y-quick/ + reset the
87
87
  * Quick Tasks Completed table (#2142; opt-in, default OFF)
88
+ * [--release-map <file>] Archive a per-repository release map (JSON array of
89
+ * {repo, release, reference, environment, status, tag}) as
90
+ * milestones/vX.Y-RELEASE-MAP.json and record it in MILESTONES.md
91
+ * (#5706; validated before any write, absent = unchanged)
92
+ *
93
+ * milestone release-tags --map <file.json> [--dry-run]
94
+ * Preflight, then create annotated tags per sub-repo from a JSON array of
95
+ * {repo, tag, ref?, message?, skip?}; never pushes, never overwrites (#5705)
88
96
  *
89
97
  * milestone archive-quick <version> Move .planning/quick/* dirs to milestones/vX.Y-quick/ + reset the
90
98
  * Quick Tasks Completed table, WITHOUT the milestone complete close-out
@@ -312,6 +320,7 @@ try {
312
320
  } catch { /* advisory — never block */ }
313
321
 
314
322
  const { resolveActiveWorkstream, applyResolvedWorkstreamEnv, peekActiveWorkstream } = require('./lib/active-workstream-store.cjs');
323
+ const { planningDir, planningRoot } = require('./lib/planning-workspace.cjs');
315
324
  const state = require('./lib/state.cjs');
316
325
  const phase = require('./lib/phase.cjs');
317
326
  const roadmap = require('./lib/roadmap.cjs');
@@ -325,6 +334,7 @@ const config = require('./lib/config.cjs');
325
334
  const estimateCli = require('./lib/estimate-cli.cjs');
326
335
  const template = require('./lib/template.cjs');
327
336
  const milestone = require('./lib/milestone.cjs');
337
+ const milestoneReleaseTags = require('./lib/milestone-release-tags.cjs');
328
338
  const commands = require('./lib/commands.cjs');
329
339
  const runtimeIdentity = require('./lib/runtime-identity.cjs');
330
340
  const init = require('./lib/init.cjs');
@@ -847,7 +857,31 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
847
857
  error,
848
858
  output: output,
849
859
  });
850
- if (!handled) config.cmdConfigNewProject(cwd, args[1], raw);
860
+ if (handled) return;
861
+ // #5268: this verb's flag surface is closed, as `todo complete`'s is
862
+ // (#4096). `--dry-run` is parsed and plumbed, and any OTHER flag fails loudly
863
+ // instead of being silently ignored: an accepted-but-ignored `--dry-run`
864
+ // wrote the config it was meant to preview. (`--raw` is spliced by the
865
+ // dispatcher before routing; it stays in the set as a guard against that
866
+ // splice ever moving.) The choices JSON is the one positional argument,
867
+ // wherever the flag sits, and a second one is refused for the same reason.
868
+ // A flag is a dash followed by a letter: `-1` is a (negative-number) JSON
869
+ // argument, refused below as choices that are not an object, not a flag.
870
+ const CONFIG_NEW_PROJECT_KNOWN_FLAGS = new Set(['--dry-run', '--raw']);
871
+ const positionals = [];
872
+ for (const a of args.slice(1)) {
873
+ if (typeof a === 'string' && /^--?[A-Za-z]/.test(a)) {
874
+ if (!CONFIG_NEW_PROJECT_KNOWN_FLAGS.has(a)) {
875
+ error(`Unknown flag for config-new-project: ${a}`, ERROR_REASON.USAGE);
876
+ }
877
+ continue;
878
+ }
879
+ positionals.push(a);
880
+ }
881
+ if (positionals.length > 1) {
882
+ error(`Unexpected argument for config-new-project: ${positionals[1]}. Usage: config-new-project [choices-json] [--dry-run]`, ERROR_REASON.USAGE);
883
+ }
884
+ config.cmdConfigNewProject(cwd, positionals[0], raw, { dryRun: args.includes('--dry-run') });
851
885
  }
852
886
 
853
887
  function routeConfigPath({ cwd, raw, workstreamContext }) {
@@ -1445,8 +1479,12 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
1445
1479
  // Silent no-op when the section is absent or the table is already canonical
1446
1480
  // — the quick/fast workflows call this before their first append, so the
1447
1481
  // migration runs exactly once, on the first quick run, unprompted otherwise.
1448
- const statePath = path.join(cwd, '.planning', 'STATE.md');
1482
+ const planningPath = planningDir(cwd);
1483
+ const statePath = path.join(planningPath, 'STATE.md');
1449
1484
  if (!fs.existsSync(statePath)) {
1485
+ if (planningPath !== planningRoot(cwd)) {
1486
+ throw new ExitError(1, `quick-tasks-migrate: STATE.md not found at ${statePath}`);
1487
+ }
1450
1488
  output({ ok: true, migrated: false, reason: `STATE.md not found at ${statePath}` }, raw);
1451
1489
  return;
1452
1490
  }
@@ -1519,7 +1557,7 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
1519
1557
  || (qtaQuickId && qtaSlug ? `[${qtaQuickId}-${qtaSlug}](./quick/${qtaQuickId}-${qtaSlug}/)` : undefined);
1520
1558
  const qtaStatus = qtaParsed['status'] || undefined;
1521
1559
 
1522
- const statePath = path.join(cwd, '.planning', 'STATE.md');
1560
+ const statePath = path.join(planningDir(cwd), 'STATE.md');
1523
1561
  if (!fs.existsSync(statePath)) {
1524
1562
  error(`quick-tasks-append: STATE.md not found at ${statePath}`, ERROR_REASON.USAGE);
1525
1563
  }
@@ -1593,7 +1631,7 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
1593
1631
  const fsx = require('node:fs');
1594
1632
  const os = require('node:os');
1595
1633
  const { REVIEWER_LANES, mergeReviewerLanes } = require('./lib/review-lane-descriptor.cjs');
1596
- const { resolveLanePlan: resolveLanePlanBase, resolveLaneEffort, resolveLaneBudget } = require('./lib/review-lane-invocation.cjs');
1634
+ const { resolveLanePlan: resolveLanePlanBase, resolveLaneEffort, resolveLaneBudget, applyPromptOverride } = require('./lib/review-lane-invocation.cjs');
1597
1635
  // OpenCode 2.x argv differs from 1.x (no --variant; effort rides on the model as `#variant`;
1598
1636
  // ADR-5331 D5/D8). The generation is probed lazily, only when the opencode lane is resolved.
1599
1637
  const resolveLanePlan = (laneInput) => {
@@ -1627,8 +1665,9 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
1627
1665
  // `plan`/`invoke` are the only subs that need the expensive plan-building path
1628
1666
  // below; `sections`/`flags` return earlier still. Anything else errors here, before
1629
1667
  // any of that work starts.
1630
- if (!['plan', 'invoke', 'sections', 'flags', 'dispatch-step', 'explicit-from-argv'].includes(sub)) {
1631
- error("Usage: review-lane <plan|invoke|sections|flags|dispatch-step|explicit-from-argv> [--selected a,b] [--run-dir D] [--repo-root R]");
1668
+ const REVIEW_LANE_USAGE = 'Usage: review-lane <plan|invoke|sections|flags|availability|dispatch-step|explicit-from-argv> [--selected a,b] [--run-dir D] [--repo-root R]';
1669
+ if (!['plan', 'invoke', 'sections', 'flags', 'availability', 'dispatch-step', 'explicit-from-argv'].includes(sub)) {
1670
+ error(REVIEW_LANE_USAGE);
1632
1671
  return;
1633
1672
  }
1634
1673
  const runDir = flag('--run-dir') || '.';
@@ -1704,8 +1743,34 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
1704
1743
  method: opts.method,
1705
1744
  headers: opts.body ? { 'Content-Type': 'application/json' } : undefined,
1706
1745
  body: opts.body,
1746
+ // Never follow a redirect: the egress check compares the CONFIGURED host, so a
1747
+ // followed redirect would send the review prompt to a host the user never named (#5433).
1748
+ redirect: 'manual',
1707
1749
  signal: AbortSignal.timeout(opts.timeoutMs),
1708
1750
  });
1751
+ // probe:true marks a reachability probe (returns status only, body ''): the status line is the answer, so do not
1752
+ // let a stalled body turn a replying server into `missing`.
1753
+ if (opts.probe) {
1754
+ res.body?.cancel().catch(() => {});
1755
+ return { ok: res.ok, status: res.status, body: '' };
1756
+ }
1757
+ if (res.status >= 300 && res.status < 400) {
1758
+ res.body?.cancel().catch(() => {});
1759
+ const location = res.headers.get('location');
1760
+ let target = 'without a Location header';
1761
+ if (location) {
1762
+ target = 'to an unparseable Location';
1763
+ try { target = `to ${new URL(location, url).host}`; } catch { /* keep the unparseable wording */ }
1764
+ }
1765
+ // Host and path only: a configured URL may carry credentials that must not reach a review file.
1766
+ const asked = (() => { try { const u = new URL(url); return `${u.host}${u.pathname}`; } catch { return 'the configured URL'; } })();
1767
+ return {
1768
+ ok: false,
1769
+ status: res.status,
1770
+ body: '',
1771
+ error: `${asked} answered HTTP ${res.status}, redirecting ${target}. The lane does not follow redirects; configure the final URL as the lane's host.`,
1772
+ };
1773
+ }
1709
1774
  return { ok: res.ok, status: res.status, body: await res.text() };
1710
1775
  } catch (e) {
1711
1776
  return { ok: false, status: 0, body: '', error: e && e.message ? e.message : String(e) };
@@ -1732,6 +1797,23 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
1732
1797
 
1733
1798
  const selected = (flag('--selected') || '')
1734
1799
  .split(',').map((s) => s.trim()).filter(Boolean);
1800
+ // #5266: `<slug>:available|missing` per openai-http lane. Host + default come from the lane
1801
+ // descriptor (resolveLanePlan), probing is runner.probeLane — review.md keeps no host table.
1802
+ // Prerequisite binaries (jq) are reported separately by the workflow, not folded in here.
1803
+ // First-party lanes only: ADR-2782 D5 requires egress/consent before a third-party lane's host is
1804
+ // contacted; detection has no such check, so installed third-party lanes are never probed here.
1805
+ if (sub === 'availability') {
1806
+ const { rows, warnings } = await runner.probeHttpLanes(
1807
+ REVIEWER_LANES,
1808
+ selected,
1809
+ (lane) => resolveLanePlan({ lane, configGet, runDir, repoRoot }),
1810
+ buildLaneRunnerDeps(),
1811
+ );
1812
+ for (const w of warnings) process.stderr.write(`review-lane availability: ${w}\n`);
1813
+ process.stdout.write(rows.join('\n') + (rows.length ? '\n' : ''));
1814
+ return;
1815
+ }
1816
+
1735
1817
  // ADR-2782 D8 (#2927): the lane map is first-party ∪ INSTALLED overlay
1736
1818
  // `reviewer` bodies, first-party winning on slug collision. Before this merge
1737
1819
  // the map was built from the frozen REVIEWER_LANES array alone, so an installed,
@@ -1945,7 +2027,7 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
1945
2027
  const dispatchResult = await dispatchReviewerLanes(
1946
2028
  {
1947
2029
  trait,
1948
- selection: { explicitFlags, detected: rosterSlugs },
2030
+ selection: { explicitFlags, detected: rosterSlugs, lanes: mergedLanes },
1949
2031
  repoRoot,
1950
2032
  paths,
1951
2033
  depth,
@@ -1996,7 +2078,7 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
1996
2078
  }
1997
2079
 
1998
2080
  if (sub !== 'invoke') {
1999
- error("Usage: review-lane <plan|invoke|sections|flags|dispatch-step|explicit-from-argv> [--selected a,b] [--run-dir D] [--repo-root R]");
2081
+ error(REVIEW_LANE_USAGE);
2000
2082
  return;
2001
2083
  }
2002
2084
 
@@ -2097,14 +2179,11 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
2097
2179
  }
2098
2180
 
2099
2181
  // `--prompt-file` lets the caller substitute a budget-trimmed prompt. Applied to whichever
2100
- // channel this lane actually reads from, so one flag serves both transports.
2182
+ // channel this lane actually reads from (stdin, argv, an argv-embedded file reference, or the
2183
+ // http promptPath), so one flag serves every lane (#5049).
2101
2184
  const promptOverride = flag('--prompt-file');
2102
2185
  if (promptOverride) {
2103
- if (entry.plan.transport === 'spawn') {
2104
- if (entry.plan.stdin) entry.plan.stdin = promptOverride;
2105
- } else {
2106
- entry.plan.promptPath = promptOverride;
2107
- }
2186
+ entry.plan = applyPromptOverride(entry.plan, promptOverride, repoRoot);
2108
2187
  }
2109
2188
 
2110
2189
  // ADR-2782 D5 rule 4: re-resolve the consented destination at INVOCATION. `undefined` (no
@@ -2139,6 +2218,13 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
2139
2218
  testCommandNormalizer.cmdNormalizeTestCommand(cwd, args[1]);
2140
2219
  }
2141
2220
 
2221
+ function routeResolvePackageManager({ args, cwd }) {
2222
+ // #5346: the gates' no-configured-command fallback asks here for the
2223
+ // project's package manager (packageManager field, then lockfile, else npm).
2224
+ // args[1] is the script name; --cwd (already parsed into `cwd`) locates package.json.
2225
+ require('./lib/normalize-test-command.cjs').cmdResolvePackageManager(cwd, args[1]);
2226
+ }
2227
+
2142
2228
  function routeDispatchShouldFlatten({ args, cwd, raw, error }) {
2143
2229
  // #1708 / #853: typed query replacing the `RUNTIME === 'codex'` prose rule.
2144
2230
  //
@@ -3022,7 +3108,12 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
3022
3108
  const jsonIdx = args.indexOf('--json');
3023
3109
  const agentSkillsJsonMode = jsonIdx !== -1;
3024
3110
  if (agentSkillsJsonMode) args.splice(jsonIdx, 1);
3025
- init.cmdAgentSkills(cwd, args[1], raw, agentSkillsJsonMode);
3111
+ // #5122: the agent's own self-load asks for configured skills only,
3112
+ // never the persona fallback served to orchestrator-side injection.
3113
+ const skillsOnlyIdx = args.indexOf('--skills-only');
3114
+ const agentSkillsOnly = skillsOnlyIdx !== -1;
3115
+ if (agentSkillsOnly) args.splice(skillsOnlyIdx, 1);
3116
+ init.cmdAgentSkills(cwd, args[1], raw, agentSkillsJsonMode, agentSkillsOnly);
3026
3117
  }
3027
3118
 
3028
3119
  function routeSkillManifest({ args, cwd, raw, error }) {
@@ -3135,7 +3226,18 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
3135
3226
  // --dry-run) the command refuses before touching anything. Distinct
3136
3227
  // from --force, which bypasses the narrow scope guards only.
3137
3228
  const confirm = args.includes('--confirm');
3138
- milestone.cmdMilestoneComplete(cwd, args[2], { name: milestoneName, archivePhases, force, dryRun, archiveQuick, confirm }, raw);
3229
+ // #5706 (epic #3628): optional caller-supplied release map file.
3230
+ // A flag with no usable value fails loud rather than being dropped.
3231
+ let releaseMap = null;
3232
+ const releaseMapIdx = args.indexOf('--release-map');
3233
+ if (releaseMapIdx !== -1) {
3234
+ const releaseMapValue = args[releaseMapIdx + 1];
3235
+ if (typeof releaseMapValue !== 'string' || releaseMapValue.startsWith('--') || releaseMapValue.trim() === '') {
3236
+ error('--release-map requires a file path (a JSON array of release-map entries)');
3237
+ }
3238
+ releaseMap = releaseMapValue;
3239
+ }
3240
+ milestone.cmdMilestoneComplete(cwd, args[2], { name: milestoneName, archivePhases, force, dryRun, archiveQuick, confirm, releaseMap }, raw);
3139
3241
  } else if (subcommand === 'archive-quick') {
3140
3242
  // #2142 escalation: narrow archival-only entry point (does NOT
3141
3243
  // touch ROADMAP/REQUIREMENTS/MILESTONES.md, runs no completion
@@ -3143,8 +3245,11 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
3143
3245
  // unlike `milestone complete --archive-quick`.
3144
3246
  const dryRun = args.includes('--dry-run');
3145
3247
  milestone.cmdQuickArchive(cwd, args[2], { dryRun }, raw);
3248
+ } else if (subcommand === 'release-tags') {
3249
+ // #5705 (epic #3628 Phase 1): repository-scoped annotated release tags.
3250
+ milestoneReleaseTags.cmdMilestoneReleaseTags(cwd, args, raw);
3146
3251
  } else {
3147
- error('Unknown milestone subcommand. Available: complete, archive-quick', ERROR_REASON.SDK_UNKNOWN_COMMAND);
3252
+ error('Unknown milestone subcommand. Available: complete, archive-quick, release-tags', ERROR_REASON.SDK_UNKNOWN_COMMAND);
3148
3253
  }
3149
3254
  }
3150
3255
 
@@ -4802,6 +4907,7 @@ const HOST_COMMAND_ROUTERS = {
4802
4907
  // #3676 (Phase 4, epic #3344): quick-batch coordination verbs.
4803
4908
  'quick-batch': routeQuickBatchCommand,
4804
4909
  'normalize-test-command': routeNormalizeTestCommand,
4910
+ 'resolve-package-manager': routeResolvePackageManager,
4805
4911
  'dispatch-should-flatten': routeDispatchShouldFlatten,
4806
4912
  'dispatch-isolation': routeDispatchIsolation,
4807
4913
  'dispatch-capacity': routeDispatchCapacity,
@@ -5107,7 +5213,7 @@ function runWithTimeout(argv) {
5107
5213
  // drifting apart when a query command was added to only one or two.
5108
5214
  const TOP_LEVEL_USAGE = 'Usage: gsd-tools <command> [args] [--raw] [--pick <field>] [--cwd <path>] [--project-dir <path>] [--ws <name>] [--json-errors] [--exit-contract=<v>]\n' +
5109
5215
  'Commands: agent, agent-skills, assumption-delta, audit-open, audit-uat, check, check-commit, commit, commit-docs-guard, commit-to-subrepo, pr-subrepo, ' +
5110
- 'config-ensure-section, config-get, config-new-project, config-path, config-set, migrate-config, normalize-test-command, ' +
5216
+ 'config-ensure-section, config-get, config-new-project, config-path, config-set, migrate-config, normalize-test-command, resolve-package-manager, ' +
5111
5217
  'context-predicates, current-timestamp, detect-custom-files, docs-init, drift-guard, effort, extract-messages, find-phase, ' +
5112
5218
  'from-gsd2, frontmatter, gap-analysis, generate-claude-md, generate-claude-profile, ' +
5113
5219
  'generate-dev-preferences, generate-slug, graphify, history-digest, init, intel, ' +
@@ -328,8 +328,33 @@ function checkCodexModelPosture(runtime, projectRoot) {
328
328
  // derives its sandbox_mode from actually lives, in every install shape
329
329
  // (repo-dev tree or npm-global package, where the package still ships its
330
330
  // own agents/ alongside bin/ and gsd-core/).
331
- function _canonicalAgentSourceDir() {
332
- return node_path_1.default.join(__dirname, '..', '..', '..', 'agents');
331
+ //
332
+ // #5496: "install-relative" has three shapes. This module compiles to
333
+ // `<root>/gsd-core/bin/lib/`, so `<root>` is three parents up. In the repository
334
+ // and the npm package the canonical sources are `<root>/agents`. In an INSTALLED
335
+ // config dir `<root>/agents` holds the CONVERTED runtime copies (no `tools:`
336
+ // declaration — every role then derived `read-only`, 26 false violations on a
337
+ // clean Codex install) and the canonical sources are `<root>/gsd-core/agents`.
338
+ // Prefer the nested directory when it actually holds agent sources; fall back to
339
+ // `<root>/agents`. `root` is a parameter so each layout is testable.
340
+ //
341
+ // #5636: "holds agent sources" is a recognized agent file, `gsd-<name>.md` (every file in the shipped
342
+ // roster) that is not a `.compact.md` variant. Any markdown would let a stray README in the nested
343
+ // directory hide the real sources, so the posture check found no role's canonical file and skipped all.
344
+ const CANONICAL_AGENT_SOURCE_FILE = /^gsd-.+\.md$/;
345
+ function isCanonicalAgentSourceFile(entry) {
346
+ return CANONICAL_AGENT_SOURCE_FILE.test(entry) && !entry.endsWith('.compact.md');
347
+ }
348
+ function canonicalAgentSourceDir(root = node_path_1.default.join(__dirname, '..', '..', '..')) {
349
+ const nested = node_path_1.default.join(root, 'gsd-core', 'agents');
350
+ try {
351
+ if (node_fs_1.default.readdirSync(nested).some(isCanonicalAgentSourceFile))
352
+ return nested;
353
+ }
354
+ catch {
355
+ // Absent or unreadable: not this layout.
356
+ }
357
+ return node_path_1.default.join(root, 'agents');
333
358
  }
334
359
  /**
335
360
  * Validate installed Codex `.toml` agents' `sandbox_mode` against what each
@@ -384,7 +409,7 @@ function checkCodexSandboxPosture(runtime, projectRoot) {
384
409
  reason: POSTURE_REASON.AGENTS_DIR_MISSING,
385
410
  };
386
411
  }
387
- const canonicalAgentsDir = _canonicalAgentSourceDir();
412
+ const canonicalAgentsDir = canonicalAgentSourceDir();
388
413
  // Same symlink guard as checkCodexModelPosture, same reason (never follow
389
414
  // an agents-dir symlink into an arbitrary file's content).
390
415
  const tomlFiles = node_fs_1.default
@@ -533,6 +558,7 @@ module.exports = {
533
558
  checkAgentsInstalled,
534
559
  checkCodexModelPosture,
535
560
  checkCodexSandboxPosture,
561
+ canonicalAgentSourceDir,
536
562
  POSTURE_REASON,
537
563
  resolveAgentHint,
538
564
  };
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ /**
3
+ * Per-install terminators for the typed-text capture heredocs (#5637).
4
+ *
5
+ * Every workflow that reads the typed text opens with
6
+ *
7
+ * IFS= read -rd '' GSD_ARGS <<'GSD_ARGS_END' || true
8
+ * $ARGUMENTS
9
+ * GSD_ARGS_END
10
+ *
11
+ * and `/gsd-quick-batch` feeds `parse-args` through the same shape with `GSD_QUICK_BATCH_ARGS_END`.
12
+ * The harness replaces `$ARGUMENTS` with the typed text before the shell parses the fence, so typed
13
+ * text with a terminator line of its own closes the heredoc early and the lines after it run as
14
+ * shell. A literal in a shipped file cannot be made unforgeable, so each install replaces it with
15
+ * `<TERMINATOR>_<nonce>`, a nonce the typed text has no way to know. The shipped source keeps the
16
+ * canonical tokens; only the files an install writes carry the nonce.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.ARGS_NONCE_RE = exports.ARGS_TERMINATOR = exports.ARGS_TERMINATORS = void 0;
20
+ exports.generateArgsNonce = generateArgsNonce;
21
+ exports.stampArgsTerminator = stampArgsTerminator;
22
+ exports.readInstalledNonce = readInstalledNonce;
23
+ exports.armArgsNonce = armArgsNonce;
24
+ exports.disarmArgsNonce = disarmArgsNonce;
25
+ exports.stampInstalledContent = stampInstalledContent;
26
+ const node_crypto_1 = require("node:crypto");
27
+ const node_fs_1 = require("node:fs");
28
+ const node_path_1 = require("node:path");
29
+ /** The canonical terminators the shipped workflows and commands carry. */
30
+ exports.ARGS_TERMINATORS = Object.freeze(['GSD_ARGS_END', 'GSD_QUICK_BATCH_ARGS_END']);
31
+ /** The terminator every workflow carries. */
32
+ exports.ARGS_TERMINATOR = 'GSD_ARGS_END';
33
+ /** A nonce is 128 random bits as 32 lowercase hex characters. */
34
+ exports.ARGS_NONCE_RE = /^[0-9a-f]{32}$/;
35
+ const OPENER_RE = new RegExp(`<<'(${exports.ARGS_TERMINATORS.join('|')})'`, 'g');
36
+ const CLOSER_RE = new RegExp(`^(${exports.ARGS_TERMINATORS.join('|')})(?=\\r?$)`, 'gm');
37
+ /** A fresh nonce. `randomBytes` is a seam for tests. */
38
+ function generateArgsNonce(randomBytes = node_crypto_1.randomBytes) {
39
+ return randomBytes(16).toString('hex');
40
+ }
41
+ /**
42
+ * `content` with each canonical terminator replaced by the stamped one: the heredoc opener tag and
43
+ * the closing line. A terminator that is already stamped does not match and is left alone, and so
44
+ * is prose that names a token inside a line.
45
+ *
46
+ * @throws {TypeError} when `nonce` is not 32 lowercase hex characters
47
+ */
48
+ function stampArgsTerminator(content, nonce) {
49
+ if (typeof nonce !== 'string' || !exports.ARGS_NONCE_RE.test(nonce)) {
50
+ throw new TypeError('stampArgsTerminator: nonce must be 32 lowercase hex characters');
51
+ }
52
+ return content
53
+ .replace(OPENER_RE, (_m, token) => `<<'${token}_${nonce}'`)
54
+ .replace(CLOSER_RE, (_m, token) => `${token}_${nonce}`);
55
+ }
56
+ let armedNonce = null;
57
+ const INSTALLED_NONCE_RE = /^GSD_ARGS_END_([0-9a-f]{32})\r?$/m;
58
+ /** A workflow is tens of KiB; anything near this is not one, and is not read. */
59
+ const MAX_WORKFLOW_BYTES = 2 * 1024 * 1024;
60
+ /**
61
+ * The nonce a previous install left in `configDir`, read from the first installed workflow that
62
+ * carries one; null when there is none. Entries that are not regular files (a directory, a FIFO,
63
+ * a symlink) and files over MAX_WORKFLOW_BYTES are skipped, and one unreadable entry does not end
64
+ * the scan.
65
+ */
66
+ function readInstalledNonce(configDir) {
67
+ let dir;
68
+ let names;
69
+ try {
70
+ dir = (0, node_path_1.join)(configDir, 'gsd-core', 'workflows');
71
+ names = (0, node_fs_1.readdirSync)(dir).sort();
72
+ }
73
+ catch {
74
+ return null;
75
+ }
76
+ for (const name of names) {
77
+ if (!name.endsWith('.md'))
78
+ continue;
79
+ try {
80
+ const full = (0, node_path_1.join)(dir, name);
81
+ const stat = (0, node_fs_1.lstatSync)(full);
82
+ if (!stat.isFile() || stat.size > MAX_WORKFLOW_BYTES)
83
+ continue;
84
+ const match = INSTALLED_NONCE_RE.exec((0, node_fs_1.readFileSync)(full, 'utf8'));
85
+ if (match)
86
+ return match[1];
87
+ }
88
+ catch {
89
+ // This entry is unreadable; the next one may still carry the nonce.
90
+ }
91
+ }
92
+ return null;
93
+ }
94
+ /**
95
+ * Arm stamping for the install into `configDir`, before it writes anything. When `reuse` is true
96
+ * the directory's installed nonce is kept, so a reinstall changes nothing; otherwise, or when
97
+ * there is none, a fresh one is minted. Only a global install (a directory under the user's home,
98
+ * which a cloned repository cannot seed) may reuse: a project-local directory is often committed,
99
+ * and a nonce planted there would be adopted. Generators never arm it, so the committed generated
100
+ * skills stay canonical and deterministic.
101
+ */
102
+ function armArgsNonce(configDir, reuse = false) {
103
+ const prior = reuse && configDir !== undefined ? readInstalledNonce(configDir) : null;
104
+ armedNonce = prior ?? generateArgsNonce();
105
+ return armedNonce;
106
+ }
107
+ /** Stop stamping: the process is no longer writing for an install that has a nonce. */
108
+ function disarmArgsNonce() {
109
+ armedNonce = null;
110
+ }
111
+ /** `content` stamped with the armed nonce, or untouched when this process is not an installer. */
112
+ function stampInstalledContent(content) {
113
+ return armedNonce === null ? content : stampArgsTerminator(content, armedNonce);
114
+ }
@@ -504,6 +504,63 @@ function validateEntryShape(e, i) {
504
504
  ...('milestone' in o ? { milestone: typeof o.milestone === 'string' ? o.milestone : null } : {}),
505
505
  };
506
506
  }
507
+ /**
508
+ * #5418: when the frontmatter counts disagree with the parsed entries, the usual
509
+ * cause is a hand-edited table: a row added to the rendered table (and the counts
510
+ * bumped to match) while the fenced JSON block, the sole source of entries, was left
511
+ * behind. Names the row ids the table and the JSON block disagree about so the
512
+ * counts message does not point at the (correct) frontmatter. Returns '' when there
513
+ * is nothing to add. Best-effort by design: it runs while a WindowsError is already
514
+ * on its way out, so it must never throw and replace that error.
515
+ */
516
+ /** Row ids spelled out in the divergence note; the rest are counted. */
517
+ const ROW_ID_LIST_LIMIT = 20;
518
+ function describeTableJsonDivergence(raw, entries) {
519
+ try {
520
+ // The hint is the PARSED entry count, not frontmatter total_count: the latter
521
+ // is the number that is wrong here.
522
+ const table = extractTableRegion(raw, entries.length);
523
+ if (table === null)
524
+ return '';
525
+ const rowId = (line) => (line.split('|')[1] ?? '').trim();
526
+ // Ids are positive integers (the JSON side enforces it), so a first cell that is
527
+ // not one — the `_(none)_` empty-ledger placeholder, a continuation line of a
528
+ // multi-line description, control characters — is not a row id and is never
529
+ // echoed into the message.
530
+ const tableIds = new Set();
531
+ const tableLines = table.split('\n');
532
+ for (let i = 2; i < tableLines.length; i++) {
533
+ if (!tableLines[i].startsWith('|'))
534
+ continue;
535
+ const id = rowId(tableLines[i]);
536
+ if (/^\d{1,15}$/.test(id))
537
+ tableIds.add(id);
538
+ }
539
+ const jsonIds = new Set(entries.map((e) => String(e.id)));
540
+ const byNumber = (a, b) => Number(a) - Number(b);
541
+ const list = (ids) => {
542
+ const shown = ids.slice(0, ROW_ID_LIST_LIMIT).join(', ');
543
+ return ids.length > ROW_ID_LIST_LIMIT ? `${shown} and ${ids.length - ROW_ID_LIST_LIMIT} more` : shown;
544
+ };
545
+ const tableOnly = [...tableIds].filter((id) => !jsonIds.has(id)).sort(byNumber);
546
+ const jsonOnly = [...jsonIds].filter((id) => !tableIds.has(id)).sort(byNumber);
547
+ const parts = [];
548
+ if (tableOnly.length > 0) {
549
+ parts.push(`row id(s) in the table but not in the fenced JSON block: ${list(tableOnly)}`);
550
+ }
551
+ if (jsonOnly.length > 0) {
552
+ parts.push(`row id(s) in the fenced JSON block but not in the table: ${list(jsonOnly)}`);
553
+ }
554
+ if (parts.length === 0)
555
+ return '';
556
+ return ` The table and the fenced JSON block disagree (${parts.join('; ')}). The JSON block is the ` +
557
+ 'authoritative entries source: add the missing entries there (or remove the stray table rows) ' +
558
+ 'and keep the frontmatter counts in step with it.';
559
+ }
560
+ catch {
561
+ return '';
562
+ }
563
+ }
507
564
  function parseLedger(raw) {
508
565
  const fm = parseFrontmatterStrict(raw);
509
566
  if (fm.schema_version !== exports.SCHEMA_VERSION) {
@@ -537,7 +594,8 @@ function parseLedger(raw) {
537
594
  recomputed.total_count !== ledger.total_count) {
538
595
  throw new WindowsError(exports.REASON.WINDOWS_LEDGER_MALFORMED, `Ledger counts disagree with entries: frontmatter open/waived/fixed/total=` +
539
596
  `${ledger.open_count}/${ledger.waived_count}/${ledger.fixed_count}/${ledger.total_count}` +
540
- ` but entries yield ${recomputed.open_count}/${recomputed.waived_count}/${recomputed.fixed_count}/${recomputed.total_count}.`);
597
+ ` but entries yield ${recomputed.open_count}/${recomputed.waived_count}/${recomputed.fixed_count}/${recomputed.total_count}.` +
598
+ describeTableJsonDivergence(raw, entries));
541
599
  }
542
600
  return ledger;
543
601
  }
@@ -807,6 +807,28 @@ function reconcile(runtimeDir) {
807
807
  }
808
808
  return result;
809
809
  }
810
+ /**
811
+ * #5121: the one bounded read for a SKILL.md found by walking a skill root. A skill file is untrusted
812
+ * repository (or installed-runtime) content: a symlink to /dev/zero exhausts memory and a FIFO blocks
813
+ * the process, because a bare readFileSync does neither a type nor a size check. Reuses the fd protocol
814
+ * of readSmallRegularFile and maps its fail-closed "unreadable" conditions (not a regular file after
815
+ * following symlinks, over the cap) to null, so a caller treats the file as it treats a missing one.
816
+ * Any other IO error still throws, as the unbounded read did.
817
+ *
818
+ * 256 KiB: the largest shipped SKILL.md is ~21 KB, so the cap is >10x headroom while still bounding the
819
+ * allocation a hostile repository can force.
820
+ */
821
+ const SKILL_FILE_MAX_BYTES = 256 * 1024;
822
+ function readSkillFile(filePath) {
823
+ try {
824
+ return readSmallRegularFile(filePath, SKILL_FILE_MAX_BYTES);
825
+ }
826
+ catch (err) {
827
+ if (err instanceof LedgerIOError && (err.code === 'ENXIO' || err.code === 'EISDIR' || err.code === 'EFBIG'))
828
+ return null;
829
+ throw err;
830
+ }
831
+ }
810
832
  module.exports = {
811
833
  readLedger,
812
834
  readLedgerStrict,
@@ -822,6 +844,9 @@ module.exports = {
822
844
  // #1459 finding 1 (HIGH): the RAW-BYTES variant — the SOLE correct reader for the byte-exact,
823
845
  // injective consent content-hash binding (a utf8 decode is lossy and could collide binary artifacts).
824
846
  readSmallRegularFileBuffer,
847
+ // #5121: bounded SKILL.md read shared by skill discovery, the installed-runtime manifest and capability skills.
848
+ readSkillFile,
849
+ SKILL_FILE_MAX_BYTES,
825
850
  // Exported for testing / introspection
826
851
  LEDGER_FILE_NAME,
827
852
  CorruptLedgerError,