@opengsd/get-shit-done-redux 1.0.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 (1466) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja-JP.md +870 -0
  3. package/README.ko-KR.md +861 -0
  4. package/README.md +300 -0
  5. package/README.pt-BR.md +492 -0
  6. package/README.zh-CN.md +842 -0
  7. package/agents/gsd-advisor-researcher.md +127 -0
  8. package/agents/gsd-ai-researcher.md +133 -0
  9. package/agents/gsd-assumptions-analyzer.md +105 -0
  10. package/agents/gsd-code-fixer.md +668 -0
  11. package/agents/gsd-code-reviewer.md +387 -0
  12. package/agents/gsd-codebase-mapper.md +853 -0
  13. package/agents/gsd-debug-session-manager.md +314 -0
  14. package/agents/gsd-debugger.md +1452 -0
  15. package/agents/gsd-doc-classifier.md +168 -0
  16. package/agents/gsd-doc-synthesizer.md +204 -0
  17. package/agents/gsd-doc-verifier.md +217 -0
  18. package/agents/gsd-doc-writer.md +615 -0
  19. package/agents/gsd-domain-researcher.md +153 -0
  20. package/agents/gsd-eval-auditor.md +191 -0
  21. package/agents/gsd-eval-planner.md +154 -0
  22. package/agents/gsd-executor.md +774 -0
  23. package/agents/gsd-framework-selector.md +160 -0
  24. package/agents/gsd-integration-checker.md +470 -0
  25. package/agents/gsd-intel-updater.md +342 -0
  26. package/agents/gsd-nyquist-auditor.md +203 -0
  27. package/agents/gsd-pattern-mapper.md +335 -0
  28. package/agents/gsd-phase-researcher.md +928 -0
  29. package/agents/gsd-plan-checker.md +978 -0
  30. package/agents/gsd-planner.md +1278 -0
  31. package/agents/gsd-project-researcher.md +677 -0
  32. package/agents/gsd-research-synthesizer.md +247 -0
  33. package/agents/gsd-roadmapper.md +688 -0
  34. package/agents/gsd-security-auditor.md +155 -0
  35. package/agents/gsd-ui-auditor.md +495 -0
  36. package/agents/gsd-ui-checker.md +309 -0
  37. package/agents/gsd-ui-researcher.md +380 -0
  38. package/agents/gsd-user-profiler.md +171 -0
  39. package/agents/gsd-verifier.md +917 -0
  40. package/bin/gsd-sdk.js +37 -0
  41. package/bin/install.js +11468 -0
  42. package/bin/lib/ui-safety-gate.cjs +107 -0
  43. package/commands/gsd/add-tests.md +42 -0
  44. package/commands/gsd/ai-integration-phase.md +37 -0
  45. package/commands/gsd/audit-fix.md +34 -0
  46. package/commands/gsd/audit-milestone.md +37 -0
  47. package/commands/gsd/audit-uat.md +24 -0
  48. package/commands/gsd/autonomous.md +46 -0
  49. package/commands/gsd/capture.md +62 -0
  50. package/commands/gsd/cleanup.md +24 -0
  51. package/commands/gsd/code-review.md +59 -0
  52. package/commands/gsd/complete-milestone.md +143 -0
  53. package/commands/gsd/config.md +58 -0
  54. package/commands/gsd/debug.md +52 -0
  55. package/commands/gsd/discuss-phase.md +76 -0
  56. package/commands/gsd/docs-update.md +49 -0
  57. package/commands/gsd/eval-review.md +33 -0
  58. package/commands/gsd/execute-phase.md +64 -0
  59. package/commands/gsd/explore.md +27 -0
  60. package/commands/gsd/extract-learnings.md +23 -0
  61. package/commands/gsd/fast.md +31 -0
  62. package/commands/gsd/forensics.md +57 -0
  63. package/commands/gsd/graphify.md +199 -0
  64. package/commands/gsd/health.md +31 -0
  65. package/commands/gsd/help.md +28 -0
  66. package/commands/gsd/import.md +41 -0
  67. package/commands/gsd/inbox.md +39 -0
  68. package/commands/gsd/ingest-docs.md +42 -0
  69. package/commands/gsd/manager.md +45 -0
  70. package/commands/gsd/map-codebase.md +83 -0
  71. package/commands/gsd/milestone-summary.md +51 -0
  72. package/commands/gsd/mvp-phase.md +45 -0
  73. package/commands/gsd/new-milestone.md +45 -0
  74. package/commands/gsd/new-project.md +47 -0
  75. package/commands/gsd/ns-context.md +23 -0
  76. package/commands/gsd/ns-ideate.md +24 -0
  77. package/commands/gsd/ns-manage.md +29 -0
  78. package/commands/gsd/ns-project.md +22 -0
  79. package/commands/gsd/ns-review.md +26 -0
  80. package/commands/gsd/ns-workflow.md +28 -0
  81. package/commands/gsd/pause-work.md +43 -0
  82. package/commands/gsd/phase.md +56 -0
  83. package/commands/gsd/plan-phase.md +62 -0
  84. package/commands/gsd/plan-review-convergence.md +59 -0
  85. package/commands/gsd/pr-branch.md +26 -0
  86. package/commands/gsd/profile-user.md +46 -0
  87. package/commands/gsd/progress.md +46 -0
  88. package/commands/gsd/quick.md +174 -0
  89. package/commands/gsd/resume-work.md +30 -0
  90. package/commands/gsd/review-backlog.md +63 -0
  91. package/commands/gsd/review.md +41 -0
  92. package/commands/gsd/secure-phase.md +36 -0
  93. package/commands/gsd/settings.md +29 -0
  94. package/commands/gsd/ship.md +24 -0
  95. package/commands/gsd/sketch.md +60 -0
  96. package/commands/gsd/spec-phase.md +63 -0
  97. package/commands/gsd/spike.md +57 -0
  98. package/commands/gsd/stats.md +19 -0
  99. package/commands/gsd/surface.md +155 -0
  100. package/commands/gsd/thread.md +24 -0
  101. package/commands/gsd/ui-phase.md +35 -0
  102. package/commands/gsd/ui-review.md +33 -0
  103. package/commands/gsd/ultraplan-phase.md +34 -0
  104. package/commands/gsd/undo.md +35 -0
  105. package/commands/gsd/update.md +48 -0
  106. package/commands/gsd/validate-phase.md +36 -0
  107. package/commands/gsd/verify-work.md +39 -0
  108. package/commands/gsd/workspace.md +52 -0
  109. package/commands/gsd/workstreams.md +70 -0
  110. package/get-shit-done/bin/check-latest-version.cjs +104 -0
  111. package/get-shit-done/bin/gsd-tools.cjs +1630 -0
  112. package/get-shit-done/bin/lib/active-workstream-store.cjs +85 -0
  113. package/get-shit-done/bin/lib/adr-parser.cjs +394 -0
  114. package/get-shit-done/bin/lib/artifacts.cjs +53 -0
  115. package/get-shit-done/bin/lib/audit.cjs +755 -0
  116. package/get-shit-done/bin/lib/cjs-command-router-adapter.cjs +39 -0
  117. package/get-shit-done/bin/lib/cjs-sdk-bridge.cjs +136 -0
  118. package/get-shit-done/bin/lib/clusters.cjs +135 -0
  119. package/get-shit-done/bin/lib/code-review-flags.cjs +74 -0
  120. package/get-shit-done/bin/lib/command-aliases.generated.cjs +824 -0
  121. package/get-shit-done/bin/lib/command-routing-hub.cjs +239 -0
  122. package/get-shit-done/bin/lib/commands.cjs +1035 -0
  123. package/get-shit-done/bin/lib/config-schema.cjs +31 -0
  124. package/get-shit-done/bin/lib/config.cjs +704 -0
  125. package/get-shit-done/bin/lib/configuration.generated.cjs +253 -0
  126. package/get-shit-done/bin/lib/context-utilization.cjs +47 -0
  127. package/get-shit-done/bin/lib/core.cjs +1922 -0
  128. package/get-shit-done/bin/lib/decisions.cjs +19 -0
  129. package/get-shit-done/bin/lib/decisions.generated.cjs +121 -0
  130. package/get-shit-done/bin/lib/docs.cjs +270 -0
  131. package/get-shit-done/bin/lib/drift.cjs +388 -0
  132. package/get-shit-done/bin/lib/fallow-runner.cjs +109 -0
  133. package/get-shit-done/bin/lib/frontmatter.cjs +389 -0
  134. package/get-shit-done/bin/lib/gap-checker.cjs +205 -0
  135. package/get-shit-done/bin/lib/graphify.cjs +592 -0
  136. package/get-shit-done/bin/lib/gsd2-import.cjs +514 -0
  137. package/get-shit-done/bin/lib/init-command-router.cjs +174 -0
  138. package/get-shit-done/bin/lib/init.cjs +2096 -0
  139. package/get-shit-done/bin/lib/install-profiles.cjs +603 -0
  140. package/get-shit-done/bin/lib/installer-migration-authoring.cjs +117 -0
  141. package/get-shit-done/bin/lib/installer-migration-report.cjs +354 -0
  142. package/get-shit-done/bin/lib/installer-migrations/000-first-time-baseline.cjs +220 -0
  143. package/get-shit-done/bin/lib/installer-migrations/001-legacy-orphan-files.cjs +41 -0
  144. package/get-shit-done/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs +80 -0
  145. package/get-shit-done/bin/lib/installer-migrations.cjs +776 -0
  146. package/get-shit-done/bin/lib/intel.cjs +643 -0
  147. package/get-shit-done/bin/lib/learnings.cjs +379 -0
  148. package/get-shit-done/bin/lib/milestone.cjs +314 -0
  149. package/get-shit-done/bin/lib/model-catalog.cjs +136 -0
  150. package/get-shit-done/bin/lib/model-profiles.cjs +25 -0
  151. package/get-shit-done/bin/lib/phase-command-router.cjs +226 -0
  152. package/get-shit-done/bin/lib/phase.cjs +1490 -0
  153. package/get-shit-done/bin/lib/phases-command-router.cjs +97 -0
  154. package/get-shit-done/bin/lib/plan-scan.cjs +26 -0
  155. package/get-shit-done/bin/lib/plan-scan.generated.cjs +97 -0
  156. package/get-shit-done/bin/lib/planning-workspace.cjs +415 -0
  157. package/get-shit-done/bin/lib/profile-output.cjs +1130 -0
  158. package/get-shit-done/bin/lib/profile-pipeline.cjs +539 -0
  159. package/get-shit-done/bin/lib/project-root.generated.cjs +117 -0
  160. package/get-shit-done/bin/lib/prompt-budget.cjs +399 -0
  161. package/get-shit-done/bin/lib/review-reviewer-selection.cjs +125 -0
  162. package/get-shit-done/bin/lib/roadmap-command-router.cjs +99 -0
  163. package/get-shit-done/bin/lib/roadmap.cjs +642 -0
  164. package/get-shit-done/bin/lib/runtime-artifact-layout.cjs +301 -0
  165. package/get-shit-done/bin/lib/runtime-homes.cjs +185 -0
  166. package/get-shit-done/bin/lib/runtime-slash.cjs +109 -0
  167. package/get-shit-done/bin/lib/schema-detect.cjs +21 -0
  168. package/get-shit-done/bin/lib/schema-detect.generated.cjs +170 -0
  169. package/get-shit-done/bin/lib/secrets.cjs +20 -0
  170. package/get-shit-done/bin/lib/secrets.generated.cjs +37 -0
  171. package/get-shit-done/bin/lib/security.cjs +504 -0
  172. package/get-shit-done/bin/lib/shell-command-projection.cjs +552 -0
  173. package/get-shit-done/bin/lib/state-command-router.cjs +346 -0
  174. package/get-shit-done/bin/lib/state-document.cjs +12 -0
  175. package/get-shit-done/bin/lib/state-document.generated.cjs +127 -0
  176. package/get-shit-done/bin/lib/state.cjs +1940 -0
  177. package/get-shit-done/bin/lib/surface.cjs +430 -0
  178. package/get-shit-done/bin/lib/template.cjs +228 -0
  179. package/get-shit-done/bin/lib/uat.cjs +289 -0
  180. package/get-shit-done/bin/lib/validate-command-router.cjs +129 -0
  181. package/get-shit-done/bin/lib/verify-command-router.cjs +122 -0
  182. package/get-shit-done/bin/lib/verify.cjs +1458 -0
  183. package/get-shit-done/bin/lib/workstream-inventory-builder.generated.cjs +79 -0
  184. package/get-shit-done/bin/lib/workstream-inventory.cjs +132 -0
  185. package/get-shit-done/bin/lib/workstream-name-policy.cjs +19 -0
  186. package/get-shit-done/bin/lib/workstream-name-policy.generated.cjs +61 -0
  187. package/get-shit-done/bin/lib/workstream.cjs +374 -0
  188. package/get-shit-done/bin/lib/worktree-safety.cjs +985 -0
  189. package/get-shit-done/bin/verify-reapply-patches.cjs +336 -0
  190. package/get-shit-done/contexts/dev.md +21 -0
  191. package/get-shit-done/contexts/research.md +22 -0
  192. package/get-shit-done/contexts/review.md +23 -0
  193. package/get-shit-done/references/agent-contracts.md +79 -0
  194. package/get-shit-done/references/ai-evals.md +156 -0
  195. package/get-shit-done/references/ai-frameworks.md +186 -0
  196. package/get-shit-done/references/artifact-types.md +131 -0
  197. package/get-shit-done/references/autonomous-smart-discuss.md +277 -0
  198. package/get-shit-done/references/checkpoints.md +814 -0
  199. package/get-shit-done/references/common-bug-patterns.md +114 -0
  200. package/get-shit-done/references/context-budget.md +85 -0
  201. package/get-shit-done/references/continuation-format.md +253 -0
  202. package/get-shit-done/references/debugger-philosophy.md +76 -0
  203. package/get-shit-done/references/decimal-phase-calculation.md +64 -0
  204. package/get-shit-done/references/doc-conflict-engine.md +91 -0
  205. package/get-shit-done/references/domain-probes.md +125 -0
  206. package/get-shit-done/references/execute-mvp-tdd.md +81 -0
  207. package/get-shit-done/references/executor-examples.md +110 -0
  208. package/get-shit-done/references/few-shot-examples/plan-checker.md +73 -0
  209. package/get-shit-done/references/few-shot-examples/verifier.md +109 -0
  210. package/get-shit-done/references/gate-prompts.md +100 -0
  211. package/get-shit-done/references/gates.md +70 -0
  212. package/get-shit-done/references/git-integration.md +298 -0
  213. package/get-shit-done/references/git-planning-commit.md +40 -0
  214. package/get-shit-done/references/ios-scaffold.md +123 -0
  215. package/get-shit-done/references/mandatory-initial-read.md +2 -0
  216. package/get-shit-done/references/model-profile-resolution.md +38 -0
  217. package/get-shit-done/references/model-profiles.md +245 -0
  218. package/get-shit-done/references/mvp-concepts.md +49 -0
  219. package/get-shit-done/references/phase-argument-parsing.md +61 -0
  220. package/get-shit-done/references/planner-antipatterns.md +89 -0
  221. package/get-shit-done/references/planner-chunked.md +49 -0
  222. package/get-shit-done/references/planner-gap-closure.md +62 -0
  223. package/get-shit-done/references/planner-graphify-auto-update.md +67 -0
  224. package/get-shit-done/references/planner-human-verify-mode.md +57 -0
  225. package/get-shit-done/references/planner-mvp-mode.md +53 -0
  226. package/get-shit-done/references/planner-reviews.md +39 -0
  227. package/get-shit-done/references/planner-revision.md +87 -0
  228. package/get-shit-done/references/planner-source-audit.md +73 -0
  229. package/get-shit-done/references/planning-config.md +471 -0
  230. package/get-shit-done/references/project-skills-discovery.md +19 -0
  231. package/get-shit-done/references/questioning.md +162 -0
  232. package/get-shit-done/references/revision-loop.md +97 -0
  233. package/get-shit-done/references/scout-codebase.md +51 -0
  234. package/get-shit-done/references/skeleton-template.md +48 -0
  235. package/get-shit-done/references/sketch-interactivity.md +41 -0
  236. package/get-shit-done/references/sketch-theme-system.md +94 -0
  237. package/get-shit-done/references/sketch-tooling.md +45 -0
  238. package/get-shit-done/references/sketch-variant-patterns.md +81 -0
  239. package/get-shit-done/references/spidr-splitting.md +69 -0
  240. package/get-shit-done/references/tdd.md +330 -0
  241. package/get-shit-done/references/thinking-models-debug.md +44 -0
  242. package/get-shit-done/references/thinking-models-execution.md +50 -0
  243. package/get-shit-done/references/thinking-models-planning.md +62 -0
  244. package/get-shit-done/references/thinking-models-research.md +50 -0
  245. package/get-shit-done/references/thinking-models-verification.md +55 -0
  246. package/get-shit-done/references/thinking-partner.md +96 -0
  247. package/get-shit-done/references/ui-brand.md +160 -0
  248. package/get-shit-done/references/universal-anti-patterns.md +63 -0
  249. package/get-shit-done/references/user-profiling.md +681 -0
  250. package/get-shit-done/references/user-story-template.md +58 -0
  251. package/get-shit-done/references/verification-overrides.md +227 -0
  252. package/get-shit-done/references/verification-patterns.md +612 -0
  253. package/get-shit-done/references/verify-mvp-mode.md +85 -0
  254. package/get-shit-done/references/workstream-flag.md +111 -0
  255. package/get-shit-done/references/worktree-path-safety.md +89 -0
  256. package/get-shit-done/templates/AI-SPEC.md +246 -0
  257. package/get-shit-done/templates/DEBUG.md +169 -0
  258. package/get-shit-done/templates/README.md +77 -0
  259. package/get-shit-done/templates/SECURITY.md +61 -0
  260. package/get-shit-done/templates/UAT.md +265 -0
  261. package/get-shit-done/templates/UI-SPEC.md +100 -0
  262. package/get-shit-done/templates/VALIDATION.md +76 -0
  263. package/get-shit-done/templates/claude-md.md +145 -0
  264. package/get-shit-done/templates/codebase/architecture.md +255 -0
  265. package/get-shit-done/templates/codebase/concerns.md +310 -0
  266. package/get-shit-done/templates/codebase/conventions.md +307 -0
  267. package/get-shit-done/templates/codebase/integrations.md +280 -0
  268. package/get-shit-done/templates/codebase/stack.md +186 -0
  269. package/get-shit-done/templates/codebase/structure.md +285 -0
  270. package/get-shit-done/templates/codebase/testing.md +480 -0
  271. package/get-shit-done/templates/config.json +62 -0
  272. package/get-shit-done/templates/context.md +352 -0
  273. package/get-shit-done/templates/continue-here.md +78 -0
  274. package/get-shit-done/templates/copilot-instructions.md +7 -0
  275. package/get-shit-done/templates/debug-subagent-prompt.md +91 -0
  276. package/get-shit-done/templates/dev-preferences.md +21 -0
  277. package/get-shit-done/templates/discovery.md +146 -0
  278. package/get-shit-done/templates/discussion-log.md +63 -0
  279. package/get-shit-done/templates/milestone-archive.md +123 -0
  280. package/get-shit-done/templates/milestone.md +115 -0
  281. package/get-shit-done/templates/phase-prompt.md +610 -0
  282. package/get-shit-done/templates/planner-subagent-prompt.md +117 -0
  283. package/get-shit-done/templates/project.md +186 -0
  284. package/get-shit-done/templates/requirements.md +231 -0
  285. package/get-shit-done/templates/research-project/ARCHITECTURE.md +204 -0
  286. package/get-shit-done/templates/research-project/FEATURES.md +147 -0
  287. package/get-shit-done/templates/research-project/PITFALLS.md +200 -0
  288. package/get-shit-done/templates/research-project/STACK.md +120 -0
  289. package/get-shit-done/templates/research-project/SUMMARY.md +170 -0
  290. package/get-shit-done/templates/research.md +592 -0
  291. package/get-shit-done/templates/retrospective.md +54 -0
  292. package/get-shit-done/templates/roadmap.md +202 -0
  293. package/get-shit-done/templates/spec.md +307 -0
  294. package/get-shit-done/templates/state.md +184 -0
  295. package/get-shit-done/templates/summary-complex.md +59 -0
  296. package/get-shit-done/templates/summary-minimal.md +41 -0
  297. package/get-shit-done/templates/summary-standard.md +48 -0
  298. package/get-shit-done/templates/summary.md +248 -0
  299. package/get-shit-done/templates/user-profile.md +146 -0
  300. package/get-shit-done/templates/user-setup.md +311 -0
  301. package/get-shit-done/templates/verification-report.md +322 -0
  302. package/get-shit-done/workflows/add-backlog.md +101 -0
  303. package/get-shit-done/workflows/add-phase.md +123 -0
  304. package/get-shit-done/workflows/add-tests.md +365 -0
  305. package/get-shit-done/workflows/add-todo.md +171 -0
  306. package/get-shit-done/workflows/ai-integration-phase.md +305 -0
  307. package/get-shit-done/workflows/analyze-dependencies.md +96 -0
  308. package/get-shit-done/workflows/audit-fix.md +188 -0
  309. package/get-shit-done/workflows/audit-milestone.md +368 -0
  310. package/get-shit-done/workflows/audit-uat.md +120 -0
  311. package/get-shit-done/workflows/autonomous.md +805 -0
  312. package/get-shit-done/workflows/check-todos.md +190 -0
  313. package/get-shit-done/workflows/cleanup.md +165 -0
  314. package/get-shit-done/workflows/code-review-fix.md +512 -0
  315. package/get-shit-done/workflows/code-review.md +666 -0
  316. package/get-shit-done/workflows/complete-milestone.md +865 -0
  317. package/get-shit-done/workflows/debug.md +242 -0
  318. package/get-shit-done/workflows/diagnose-issues.md +251 -0
  319. package/get-shit-done/workflows/discovery-phase.md +291 -0
  320. package/get-shit-done/workflows/discuss-phase/modes/advisor.md +175 -0
  321. package/get-shit-done/workflows/discuss-phase/modes/all.md +28 -0
  322. package/get-shit-done/workflows/discuss-phase/modes/analyze.md +44 -0
  323. package/get-shit-done/workflows/discuss-phase/modes/auto.md +56 -0
  324. package/get-shit-done/workflows/discuss-phase/modes/batch.md +52 -0
  325. package/get-shit-done/workflows/discuss-phase/modes/chain.md +97 -0
  326. package/get-shit-done/workflows/discuss-phase/modes/default.md +141 -0
  327. package/get-shit-done/workflows/discuss-phase/modes/power.md +44 -0
  328. package/get-shit-done/workflows/discuss-phase/modes/text.md +55 -0
  329. package/get-shit-done/workflows/discuss-phase/templates/checkpoint.json +18 -0
  330. package/get-shit-done/workflows/discuss-phase/templates/context.md +136 -0
  331. package/get-shit-done/workflows/discuss-phase/templates/discussion-log.md +50 -0
  332. package/get-shit-done/workflows/discuss-phase-assumptions.md +685 -0
  333. package/get-shit-done/workflows/discuss-phase-power.md +291 -0
  334. package/get-shit-done/workflows/discuss-phase.md +499 -0
  335. package/get-shit-done/workflows/do.md +122 -0
  336. package/get-shit-done/workflows/docs-update.md +1172 -0
  337. package/get-shit-done/workflows/edit-phase.md +305 -0
  338. package/get-shit-done/workflows/eval-review.md +166 -0
  339. package/get-shit-done/workflows/execute-phase/steps/codebase-drift-gate.md +81 -0
  340. package/get-shit-done/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
  341. package/get-shit-done/workflows/execute-phase/steps/post-merge-gate.md +116 -0
  342. package/get-shit-done/workflows/execute-phase.md +1717 -0
  343. package/get-shit-done/workflows/execute-plan.md +536 -0
  344. package/get-shit-done/workflows/explore.md +154 -0
  345. package/get-shit-done/workflows/extract-learnings.md +253 -0
  346. package/get-shit-done/workflows/fast.md +124 -0
  347. package/get-shit-done/workflows/forensics.md +289 -0
  348. package/get-shit-done/workflows/graduation.md +206 -0
  349. package/get-shit-done/workflows/health.md +234 -0
  350. package/get-shit-done/workflows/help/modes/brief.md +22 -0
  351. package/get-shit-done/workflows/help/modes/default.md +50 -0
  352. package/get-shit-done/workflows/help/modes/full.md +784 -0
  353. package/get-shit-done/workflows/help/modes/topic.md +74 -0
  354. package/get-shit-done/workflows/help.md +24 -0
  355. package/get-shit-done/workflows/import.md +264 -0
  356. package/get-shit-done/workflows/inbox.md +387 -0
  357. package/get-shit-done/workflows/ingest-docs.md +339 -0
  358. package/get-shit-done/workflows/insert-phase.md +162 -0
  359. package/get-shit-done/workflows/list-phase-assumptions.md +178 -0
  360. package/get-shit-done/workflows/list-workspaces.md +67 -0
  361. package/get-shit-done/workflows/manager.md +403 -0
  362. package/get-shit-done/workflows/map-codebase.md +454 -0
  363. package/get-shit-done/workflows/milestone-summary.md +234 -0
  364. package/get-shit-done/workflows/mvp-phase.md +232 -0
  365. package/get-shit-done/workflows/new-milestone.md +645 -0
  366. package/get-shit-done/workflows/new-project.md +1487 -0
  367. package/get-shit-done/workflows/new-workspace.md +250 -0
  368. package/get-shit-done/workflows/next.md +231 -0
  369. package/get-shit-done/workflows/node-repair.md +92 -0
  370. package/get-shit-done/workflows/note.md +158 -0
  371. package/get-shit-done/workflows/pause-work.md +254 -0
  372. package/get-shit-done/workflows/plan-milestone-gaps.md +291 -0
  373. package/get-shit-done/workflows/plan-phase.md +1800 -0
  374. package/get-shit-done/workflows/plan-review-convergence.md +340 -0
  375. package/get-shit-done/workflows/plant-seed.md +240 -0
  376. package/get-shit-done/workflows/pr-branch.md +157 -0
  377. package/get-shit-done/workflows/profile-user.md +463 -0
  378. package/get-shit-done/workflows/progress.md +660 -0
  379. package/get-shit-done/workflows/quick.md +1049 -0
  380. package/get-shit-done/workflows/reapply-patches.md +426 -0
  381. package/get-shit-done/workflows/remove-phase.md +166 -0
  382. package/get-shit-done/workflows/remove-workspace.md +118 -0
  383. package/get-shit-done/workflows/resume-project.md +342 -0
  384. package/get-shit-done/workflows/review.md +633 -0
  385. package/get-shit-done/workflows/scan.md +115 -0
  386. package/get-shit-done/workflows/secure-phase.md +190 -0
  387. package/get-shit-done/workflows/session-report.md +146 -0
  388. package/get-shit-done/workflows/settings-advanced.md +590 -0
  389. package/get-shit-done/workflows/settings-integrations.md +292 -0
  390. package/get-shit-done/workflows/settings.md +545 -0
  391. package/get-shit-done/workflows/ship.md +366 -0
  392. package/get-shit-done/workflows/sketch-wrap-up.md +296 -0
  393. package/get-shit-done/workflows/sketch.md +371 -0
  394. package/get-shit-done/workflows/spec-phase.md +262 -0
  395. package/get-shit-done/workflows/spike-wrap-up.md +317 -0
  396. package/get-shit-done/workflows/spike.md +463 -0
  397. package/get-shit-done/workflows/stats.md +90 -0
  398. package/get-shit-done/workflows/sync-skills.md +182 -0
  399. package/get-shit-done/workflows/thread.md +232 -0
  400. package/get-shit-done/workflows/transition.md +704 -0
  401. package/get-shit-done/workflows/ui-phase.md +338 -0
  402. package/get-shit-done/workflows/ui-review.md +203 -0
  403. package/get-shit-done/workflows/ultraplan-phase.md +209 -0
  404. package/get-shit-done/workflows/undo.md +314 -0
  405. package/get-shit-done/workflows/update.md +664 -0
  406. package/get-shit-done/workflows/validate-phase.md +189 -0
  407. package/get-shit-done/workflows/verify-phase.md +554 -0
  408. package/get-shit-done/workflows/verify-work.md +791 -0
  409. package/hooks/dist/gsd-check-update-worker.js +117 -0
  410. package/hooks/dist/gsd-check-update.js +64 -0
  411. package/hooks/dist/gsd-context-monitor.js +192 -0
  412. package/hooks/dist/gsd-graphify-update.sh +158 -0
  413. package/hooks/dist/gsd-phase-boundary.sh +47 -0
  414. package/hooks/dist/gsd-prompt-guard.js +97 -0
  415. package/hooks/dist/gsd-read-guard.js +101 -0
  416. package/hooks/dist/gsd-read-injection-scanner.js +152 -0
  417. package/hooks/dist/gsd-session-state.sh +59 -0
  418. package/hooks/dist/gsd-statusline.js +537 -0
  419. package/hooks/dist/gsd-update-banner.js +134 -0
  420. package/hooks/dist/gsd-validate-commit.sh +57 -0
  421. package/hooks/dist/gsd-workflow-guard.js +94 -0
  422. package/hooks/dist/lib/git-cmd.js +150 -0
  423. package/hooks/dist/lib/gsd-graphify-rebuild.sh +65 -0
  424. package/hooks/gsd-check-update-worker.js +117 -0
  425. package/hooks/gsd-check-update.js +64 -0
  426. package/hooks/gsd-context-monitor.js +192 -0
  427. package/hooks/gsd-graphify-update.sh +158 -0
  428. package/hooks/gsd-phase-boundary.sh +47 -0
  429. package/hooks/gsd-prompt-guard.js +97 -0
  430. package/hooks/gsd-read-guard.js +101 -0
  431. package/hooks/gsd-read-injection-scanner.js +152 -0
  432. package/hooks/gsd-session-state.sh +59 -0
  433. package/hooks/gsd-statusline.js +537 -0
  434. package/hooks/gsd-update-banner.js +134 -0
  435. package/hooks/gsd-validate-commit.sh +57 -0
  436. package/hooks/gsd-workflow-guard.js +94 -0
  437. package/hooks/lib/git-cmd.js +150 -0
  438. package/hooks/lib/gsd-graphify-rebuild.sh +65 -0
  439. package/package.json +98 -0
  440. package/scripts/audit-workflow-script-paths.cjs +73 -0
  441. package/scripts/base64-scan.sh +262 -0
  442. package/scripts/build-hooks.js +227 -0
  443. package/scripts/changeset/cli.cjs +408 -0
  444. package/scripts/changeset/github-release-notes.cjs +198 -0
  445. package/scripts/changeset/lint.cjs +110 -0
  446. package/scripts/changeset/new.cjs +137 -0
  447. package/scripts/changeset/parse.cjs +114 -0
  448. package/scripts/changeset/render.cjs +34 -0
  449. package/scripts/changeset/serialize.cjs +130 -0
  450. package/scripts/command-contract-helpers.cjs +64 -0
  451. package/scripts/diff-touches-shipped-paths.cjs +147 -0
  452. package/scripts/fix-slash-commands.cjs +147 -0
  453. package/scripts/gen-inventory-manifest.cjs +109 -0
  454. package/scripts/lint-command-contract.cjs +108 -0
  455. package/scripts/lint-descriptions.cjs +83 -0
  456. package/scripts/lint-docs-required.cjs +222 -0
  457. package/scripts/lint-no-source-grep-extras.cjs +81 -0
  458. package/scripts/lint-no-source-grep.cjs +174 -0
  459. package/scripts/lint-pr-check-project-dir.cjs +98 -0
  460. package/scripts/lint-shared-module-handsync.cjs +331 -0
  461. package/scripts/lint-shell-command-projection-drift.cjs +57 -0
  462. package/scripts/lint-skill-deps.cjs +180 -0
  463. package/scripts/lint-test-file-count.allowlist.json +35 -0
  464. package/scripts/lint-test-file-count.cjs +190 -0
  465. package/scripts/pr-template-policy.cjs +268 -0
  466. package/scripts/prompt-injection-scan.sh +203 -0
  467. package/scripts/release-tarball-smoke.cjs +677 -0
  468. package/scripts/run-tests.cjs +178 -0
  469. package/scripts/secret-scan.sh +229 -0
  470. package/scripts/shared-module-handsync-allowlist.json +145 -0
  471. package/scripts/strip-prose-atrefs.cjs +106 -0
  472. package/scripts/sync-rulesets.sh +34 -0
  473. package/scripts/verify-tarball-sdk-dist.sh +69 -0
  474. package/sdk/dist/cli-transport.d.ts +19 -0
  475. package/sdk/dist/cli-transport.d.ts.map +1 -0
  476. package/sdk/dist/cli-transport.js +104 -0
  477. package/sdk/dist/cli-transport.js.map +1 -0
  478. package/sdk/dist/cli.d.ts +46 -0
  479. package/sdk/dist/cli.d.ts.map +1 -0
  480. package/sdk/dist/cli.js +511 -0
  481. package/sdk/dist/cli.js.map +1 -0
  482. package/sdk/dist/config.d.ts +108 -0
  483. package/sdk/dist/config.d.ts.map +1 -0
  484. package/sdk/dist/config.js +116 -0
  485. package/sdk/dist/config.js.map +1 -0
  486. package/sdk/dist/configuration/index.d.ts +85 -0
  487. package/sdk/dist/configuration/index.d.ts.map +1 -0
  488. package/sdk/dist/configuration/index.js +257 -0
  489. package/sdk/dist/configuration/index.js.map +1 -0
  490. package/sdk/dist/context-engine.d.ts +49 -0
  491. package/sdk/dist/context-engine.d.ts.map +1 -0
  492. package/sdk/dist/context-engine.js +142 -0
  493. package/sdk/dist/context-engine.js.map +1 -0
  494. package/sdk/dist/context-truncation.d.ts +33 -0
  495. package/sdk/dist/context-truncation.d.ts.map +1 -0
  496. package/sdk/dist/context-truncation.js +197 -0
  497. package/sdk/dist/context-truncation.js.map +1 -0
  498. package/sdk/dist/errors.d.ts +46 -0
  499. package/sdk/dist/errors.d.ts.map +1 -0
  500. package/sdk/dist/errors.js +64 -0
  501. package/sdk/dist/errors.js.map +1 -0
  502. package/sdk/dist/event-stream.d.ts +53 -0
  503. package/sdk/dist/event-stream.d.ts.map +1 -0
  504. package/sdk/dist/event-stream.js +321 -0
  505. package/sdk/dist/event-stream.js.map +1 -0
  506. package/sdk/dist/golden/capture.d.ts +15 -0
  507. package/sdk/dist/golden/capture.d.ts.map +1 -0
  508. package/sdk/dist/golden/capture.js +67 -0
  509. package/sdk/dist/golden/capture.js.map +1 -0
  510. package/sdk/dist/golden/golden-integration-covered.d.ts +6 -0
  511. package/sdk/dist/golden/golden-integration-covered.d.ts.map +1 -0
  512. package/sdk/dist/golden/golden-integration-covered.js +30 -0
  513. package/sdk/dist/golden/golden-integration-covered.js.map +1 -0
  514. package/sdk/dist/golden/golden-mutation-covered.d.ts +7 -0
  515. package/sdk/dist/golden/golden-mutation-covered.d.ts.map +1 -0
  516. package/sdk/dist/golden/golden-mutation-covered.js +17 -0
  517. package/sdk/dist/golden/golden-mutation-covered.js.map +1 -0
  518. package/sdk/dist/golden/golden-policy.d.ts +10 -0
  519. package/sdk/dist/golden/golden-policy.d.ts.map +1 -0
  520. package/sdk/dist/golden/golden-policy.js +98 -0
  521. package/sdk/dist/golden/golden-policy.js.map +1 -0
  522. package/sdk/dist/golden/init-golden-normalize.d.ts +8 -0
  523. package/sdk/dist/golden/init-golden-normalize.d.ts.map +1 -0
  524. package/sdk/dist/golden/init-golden-normalize.js +14 -0
  525. package/sdk/dist/golden/init-golden-normalize.js.map +1 -0
  526. package/sdk/dist/golden/read-only-golden-rows.d.ts +20 -0
  527. package/sdk/dist/golden/read-only-golden-rows.d.ts.map +1 -0
  528. package/sdk/dist/golden/read-only-golden-rows.js +67 -0
  529. package/sdk/dist/golden/read-only-golden-rows.js.map +1 -0
  530. package/sdk/dist/golden/registry-canonical-commands.d.ts +6 -0
  531. package/sdk/dist/golden/registry-canonical-commands.d.ts.map +1 -0
  532. package/sdk/dist/golden/registry-canonical-commands.js +30 -0
  533. package/sdk/dist/golden/registry-canonical-commands.js.map +1 -0
  534. package/sdk/dist/gsd-tools-error.d.ts +23 -0
  535. package/sdk/dist/gsd-tools-error.d.ts.map +1 -0
  536. package/sdk/dist/gsd-tools-error.js +29 -0
  537. package/sdk/dist/gsd-tools-error.js.map +1 -0
  538. package/sdk/dist/gsd-tools.d.ts +97 -0
  539. package/sdk/dist/gsd-tools.d.ts.map +1 -0
  540. package/sdk/dist/gsd-tools.js +168 -0
  541. package/sdk/dist/gsd-tools.js.map +1 -0
  542. package/sdk/dist/gsd-transport-policy.d.ts +10 -0
  543. package/sdk/dist/gsd-transport-policy.d.ts.map +1 -0
  544. package/sdk/dist/gsd-transport-policy.js +32 -0
  545. package/sdk/dist/gsd-transport-policy.js.map +1 -0
  546. package/sdk/dist/gsd-transport.d.ts +39 -0
  547. package/sdk/dist/gsd-transport.d.ts.map +1 -0
  548. package/sdk/dist/gsd-transport.js +78 -0
  549. package/sdk/dist/gsd-transport.js.map +1 -0
  550. package/sdk/dist/index.d.ts +127 -0
  551. package/sdk/dist/index.d.ts.map +1 -0
  552. package/sdk/dist/index.js +300 -0
  553. package/sdk/dist/index.js.map +1 -0
  554. package/sdk/dist/init-runner.d.ts +90 -0
  555. package/sdk/dist/init-runner.d.ts.map +1 -0
  556. package/sdk/dist/init-runner.js +613 -0
  557. package/sdk/dist/init-runner.js.map +1 -0
  558. package/sdk/dist/logger.d.ts +50 -0
  559. package/sdk/dist/logger.d.ts.map +1 -0
  560. package/sdk/dist/logger.js +70 -0
  561. package/sdk/dist/logger.js.map +1 -0
  562. package/sdk/dist/model-catalog.d.ts +31 -0
  563. package/sdk/dist/model-catalog.d.ts.map +1 -0
  564. package/sdk/dist/model-catalog.js +31 -0
  565. package/sdk/dist/model-catalog.js.map +1 -0
  566. package/sdk/dist/phase-prompt.d.ts +72 -0
  567. package/sdk/dist/phase-prompt.d.ts.map +1 -0
  568. package/sdk/dist/phase-prompt.js +213 -0
  569. package/sdk/dist/phase-prompt.js.map +1 -0
  570. package/sdk/dist/phase-runner.d.ts +145 -0
  571. package/sdk/dist/phase-runner.d.ts.map +1 -0
  572. package/sdk/dist/phase-runner.js +1206 -0
  573. package/sdk/dist/phase-runner.js.map +1 -0
  574. package/sdk/dist/plan-parser.d.ts +55 -0
  575. package/sdk/dist/plan-parser.d.ts.map +1 -0
  576. package/sdk/dist/plan-parser.js +389 -0
  577. package/sdk/dist/plan-parser.js.map +1 -0
  578. package/sdk/dist/planning-journal.d.ts +64 -0
  579. package/sdk/dist/planning-journal.d.ts.map +1 -0
  580. package/sdk/dist/planning-journal.js +88 -0
  581. package/sdk/dist/planning-journal.js.map +1 -0
  582. package/sdk/dist/planning-runtime.d.ts +67 -0
  583. package/sdk/dist/planning-runtime.d.ts.map +1 -0
  584. package/sdk/dist/planning-runtime.js +58 -0
  585. package/sdk/dist/planning-runtime.js.map +1 -0
  586. package/sdk/dist/project-root/index.d.ts +46 -0
  587. package/sdk/dist/project-root/index.d.ts.map +1 -0
  588. package/sdk/dist/project-root/index.js +138 -0
  589. package/sdk/dist/project-root/index.js.map +1 -0
  590. package/sdk/dist/prompt-builder.d.ts +44 -0
  591. package/sdk/dist/prompt-builder.d.ts.map +1 -0
  592. package/sdk/dist/prompt-builder.js +180 -0
  593. package/sdk/dist/prompt-builder.js.map +1 -0
  594. package/sdk/dist/prompt-sanitizer.d.ts +35 -0
  595. package/sdk/dist/prompt-sanitizer.d.ts.map +1 -0
  596. package/sdk/dist/prompt-sanitizer.js +101 -0
  597. package/sdk/dist/prompt-sanitizer.js.map +1 -0
  598. package/sdk/dist/query/active-workstream-store.d.ts +7 -0
  599. package/sdk/dist/query/active-workstream-store.d.ts.map +1 -0
  600. package/sdk/dist/query/active-workstream-store.js +56 -0
  601. package/sdk/dist/query/active-workstream-store.js.map +1 -0
  602. package/sdk/dist/query/agent-failure-classifier.d.ts +38 -0
  603. package/sdk/dist/query/agent-failure-classifier.d.ts.map +1 -0
  604. package/sdk/dist/query/agent-failure-classifier.js +83 -0
  605. package/sdk/dist/query/agent-failure-classifier.js.map +1 -0
  606. package/sdk/dist/query/audit-open.d.ts +46 -0
  607. package/sdk/dist/query/audit-open.d.ts.map +1 -0
  608. package/sdk/dist/query/audit-open.js +662 -0
  609. package/sdk/dist/query/audit-open.js.map +1 -0
  610. package/sdk/dist/query/check-auto-mode.d.ts +13 -0
  611. package/sdk/dist/query/check-auto-mode.d.ts.map +1 -0
  612. package/sdk/dist/query/check-auto-mode.js +40 -0
  613. package/sdk/dist/query/check-auto-mode.js.map +1 -0
  614. package/sdk/dist/query/check-completion.d.ts +10 -0
  615. package/sdk/dist/query/check-completion.d.ts.map +1 -0
  616. package/sdk/dist/query/check-completion.js +157 -0
  617. package/sdk/dist/query/check-completion.js.map +1 -0
  618. package/sdk/dist/query/check-decision-coverage.d.ts +33 -0
  619. package/sdk/dist/query/check-decision-coverage.d.ts.map +1 -0
  620. package/sdk/dist/query/check-decision-coverage.js +472 -0
  621. package/sdk/dist/query/check-decision-coverage.js.map +1 -0
  622. package/sdk/dist/query/check-gates.d.ts +10 -0
  623. package/sdk/dist/query/check-gates.d.ts.map +1 -0
  624. package/sdk/dist/query/check-gates.js +89 -0
  625. package/sdk/dist/query/check-gates.js.map +1 -0
  626. package/sdk/dist/query/check-ship-ready.d.ts +17 -0
  627. package/sdk/dist/query/check-ship-ready.d.ts.map +1 -0
  628. package/sdk/dist/query/check-ship-ready.js +121 -0
  629. package/sdk/dist/query/check-ship-ready.js.map +1 -0
  630. package/sdk/dist/query/check-verification-status.d.ts +10 -0
  631. package/sdk/dist/query/check-verification-status.d.ts.map +1 -0
  632. package/sdk/dist/query/check-verification-status.js +142 -0
  633. package/sdk/dist/query/check-verification-status.js.map +1 -0
  634. package/sdk/dist/query/command-aliases.generated.d.ts +31 -0
  635. package/sdk/dist/query/command-aliases.generated.d.ts.map +1 -0
  636. package/sdk/dist/query/command-aliases.generated.js +133 -0
  637. package/sdk/dist/query/command-aliases.generated.js.map +1 -0
  638. package/sdk/dist/query/command-catalog.d.ts +9 -0
  639. package/sdk/dist/query/command-catalog.d.ts.map +1 -0
  640. package/sdk/dist/query/command-catalog.js +17 -0
  641. package/sdk/dist/query/command-catalog.js.map +1 -0
  642. package/sdk/dist/query/command-definition.d.ts +19 -0
  643. package/sdk/dist/query/command-definition.d.ts.map +1 -0
  644. package/sdk/dist/query/command-definition.js +44 -0
  645. package/sdk/dist/query/command-definition.js.map +1 -0
  646. package/sdk/dist/query/command-family-handlers.d.ts +3 -0
  647. package/sdk/dist/query/command-family-handlers.d.ts.map +1 -0
  648. package/sdk/dist/query/command-family-handlers.js +101 -0
  649. package/sdk/dist/query/command-family-handlers.js.map +1 -0
  650. package/sdk/dist/query/command-manifest.d.ts +2 -0
  651. package/sdk/dist/query/command-manifest.d.ts.map +1 -0
  652. package/sdk/dist/query/command-manifest.init.d.ts +6 -0
  653. package/sdk/dist/query/command-manifest.init.d.ts.map +1 -0
  654. package/sdk/dist/query/command-manifest.init.js +23 -0
  655. package/sdk/dist/query/command-manifest.init.js.map +1 -0
  656. package/sdk/dist/query/command-manifest.js +17 -0
  657. package/sdk/dist/query/command-manifest.js.map +1 -0
  658. package/sdk/dist/query/command-manifest.non-family.d.ts +9 -0
  659. package/sdk/dist/query/command-manifest.non-family.d.ts.map +1 -0
  660. package/sdk/dist/query/command-manifest.non-family.js +60 -0
  661. package/sdk/dist/query/command-manifest.non-family.js.map +1 -0
  662. package/sdk/dist/query/command-manifest.phase.d.ts +6 -0
  663. package/sdk/dist/query/command-manifest.phase.d.ts.map +1 -0
  664. package/sdk/dist/query/command-manifest.phase.js +16 -0
  665. package/sdk/dist/query/command-manifest.phase.js.map +1 -0
  666. package/sdk/dist/query/command-manifest.phases.d.ts +7 -0
  667. package/sdk/dist/query/command-manifest.phases.d.ts.map +1 -0
  668. package/sdk/dist/query/command-manifest.phases.js +10 -0
  669. package/sdk/dist/query/command-manifest.phases.js.map +1 -0
  670. package/sdk/dist/query/command-manifest.roadmap.d.ts +6 -0
  671. package/sdk/dist/query/command-manifest.roadmap.d.ts.map +1 -0
  672. package/sdk/dist/query/command-manifest.roadmap.js +10 -0
  673. package/sdk/dist/query/command-manifest.roadmap.js.map +1 -0
  674. package/sdk/dist/query/command-manifest.state.d.ts +9 -0
  675. package/sdk/dist/query/command-manifest.state.d.ts.map +1 -0
  676. package/sdk/dist/query/command-manifest.state.js +30 -0
  677. package/sdk/dist/query/command-manifest.state.js.map +1 -0
  678. package/sdk/dist/query/command-manifest.types.d.ts +12 -0
  679. package/sdk/dist/query/command-manifest.types.d.ts.map +1 -0
  680. package/sdk/dist/query/command-manifest.types.js +2 -0
  681. package/sdk/dist/query/command-manifest.types.js.map +1 -0
  682. package/sdk/dist/query/command-manifest.validate.d.ts +6 -0
  683. package/sdk/dist/query/command-manifest.validate.d.ts.map +1 -0
  684. package/sdk/dist/query/command-manifest.validate.js +10 -0
  685. package/sdk/dist/query/command-manifest.validate.js.map +1 -0
  686. package/sdk/dist/query/command-manifest.verify.d.ts +6 -0
  687. package/sdk/dist/query/command-manifest.verify.d.ts.map +1 -0
  688. package/sdk/dist/query/command-manifest.verify.js +16 -0
  689. package/sdk/dist/query/command-manifest.verify.js.map +1 -0
  690. package/sdk/dist/query/command-static-catalog-domain.d.ts +3 -0
  691. package/sdk/dist/query/command-static-catalog-domain.d.ts.map +1 -0
  692. package/sdk/dist/query/command-static-catalog-domain.js +110 -0
  693. package/sdk/dist/query/command-static-catalog-domain.js.map +1 -0
  694. package/sdk/dist/query/command-static-catalog-foundation.d.ts +7 -0
  695. package/sdk/dist/query/command-static-catalog-foundation.d.ts.map +1 -0
  696. package/sdk/dist/query/command-static-catalog-foundation.js +106 -0
  697. package/sdk/dist/query/command-static-catalog-foundation.js.map +1 -0
  698. package/sdk/dist/query/command-topology.d.ts +32 -0
  699. package/sdk/dist/query/command-topology.d.ts.map +1 -0
  700. package/sdk/dist/query/command-topology.js +66 -0
  701. package/sdk/dist/query/command-topology.js.map +1 -0
  702. package/sdk/dist/query/commands-list.d.ts +14 -0
  703. package/sdk/dist/query/commands-list.d.ts.map +1 -0
  704. package/sdk/dist/query/commands-list.js +18 -0
  705. package/sdk/dist/query/commands-list.js.map +1 -0
  706. package/sdk/dist/query/commit.d.ts +179 -0
  707. package/sdk/dist/query/commit.d.ts.map +1 -0
  708. package/sdk/dist/query/commit.js +632 -0
  709. package/sdk/dist/query/commit.js.map +1 -0
  710. package/sdk/dist/query/config-gates.d.ts +12 -0
  711. package/sdk/dist/query/config-gates.d.ts.map +1 -0
  712. package/sdk/dist/query/config-gates.js +66 -0
  713. package/sdk/dist/query/config-gates.js.map +1 -0
  714. package/sdk/dist/query/config-mutation.d.ts +86 -0
  715. package/sdk/dist/query/config-mutation.d.ts.map +1 -0
  716. package/sdk/dist/query/config-mutation.js +602 -0
  717. package/sdk/dist/query/config-mutation.js.map +1 -0
  718. package/sdk/dist/query/config-query.d.ts +57 -0
  719. package/sdk/dist/query/config-query.d.ts.map +1 -0
  720. package/sdk/dist/query/config-query.js +277 -0
  721. package/sdk/dist/query/config-query.js.map +1 -0
  722. package/sdk/dist/query/config-schema.d.ts +19 -0
  723. package/sdk/dist/query/config-schema.d.ts.map +1 -0
  724. package/sdk/dist/query/config-schema.js +26 -0
  725. package/sdk/dist/query/config-schema.js.map +1 -0
  726. package/sdk/dist/query/decisions.d.ts +58 -0
  727. package/sdk/dist/query/decisions.d.ts.map +1 -0
  728. package/sdk/dist/query/decisions.js +165 -0
  729. package/sdk/dist/query/decisions.js.map +1 -0
  730. package/sdk/dist/query/detect-custom-files.d.ts +11 -0
  731. package/sdk/dist/query/detect-custom-files.d.ts.map +1 -0
  732. package/sdk/dist/query/detect-custom-files.js +89 -0
  733. package/sdk/dist/query/detect-custom-files.js.map +1 -0
  734. package/sdk/dist/query/detect-phase-type.d.ts +9 -0
  735. package/sdk/dist/query/detect-phase-type.d.ts.map +1 -0
  736. package/sdk/dist/query/detect-phase-type.js +124 -0
  737. package/sdk/dist/query/detect-phase-type.js.map +1 -0
  738. package/sdk/dist/query/docs-init.d.ts +26 -0
  739. package/sdk/dist/query/docs-init.d.ts.map +1 -0
  740. package/sdk/dist/query/docs-init.js +231 -0
  741. package/sdk/dist/query/docs-init.js.map +1 -0
  742. package/sdk/dist/query/fallow-audit.d.ts +44 -0
  743. package/sdk/dist/query/fallow-audit.d.ts.map +1 -0
  744. package/sdk/dist/query/fallow-audit.js +44 -0
  745. package/sdk/dist/query/fallow-audit.js.map +1 -0
  746. package/sdk/dist/query/frontmatter-mutation.d.ts +77 -0
  747. package/sdk/dist/query/frontmatter-mutation.d.ts.map +1 -0
  748. package/sdk/dist/query/frontmatter-mutation.js +299 -0
  749. package/sdk/dist/query/frontmatter-mutation.js.map +1 -0
  750. package/sdk/dist/query/frontmatter.d.ts +93 -0
  751. package/sdk/dist/query/frontmatter.d.ts.map +1 -0
  752. package/sdk/dist/query/frontmatter.js +364 -0
  753. package/sdk/dist/query/frontmatter.js.map +1 -0
  754. package/sdk/dist/query/helpers.d.ts +194 -0
  755. package/sdk/dist/query/helpers.d.ts.map +1 -0
  756. package/sdk/dist/query/helpers.js +540 -0
  757. package/sdk/dist/query/helpers.js.map +1 -0
  758. package/sdk/dist/query/index.d.ts +8 -0
  759. package/sdk/dist/query/index.d.ts.map +1 -0
  760. package/sdk/dist/query/index.js +6 -0
  761. package/sdk/dist/query/index.js.map +1 -0
  762. package/sdk/dist/query/init-complex.d.ts +47 -0
  763. package/sdk/dist/query/init-complex.d.ts.map +1 -0
  764. package/sdk/dist/query/init-complex.js +735 -0
  765. package/sdk/dist/query/init-complex.js.map +1 -0
  766. package/sdk/dist/query/init.d.ts +106 -0
  767. package/sdk/dist/query/init.d.ts.map +1 -0
  768. package/sdk/dist/query/init.js +1228 -0
  769. package/sdk/dist/query/init.js.map +1 -0
  770. package/sdk/dist/query/intel.d.ts +43 -0
  771. package/sdk/dist/query/intel.d.ts.map +1 -0
  772. package/sdk/dist/query/intel.js +416 -0
  773. package/sdk/dist/query/intel.js.map +1 -0
  774. package/sdk/dist/query/mutation-event-decorator.d.ts +5 -0
  775. package/sdk/dist/query/mutation-event-decorator.d.ts.map +1 -0
  776. package/sdk/dist/query/mutation-event-decorator.js +28 -0
  777. package/sdk/dist/query/mutation-event-decorator.js.map +1 -0
  778. package/sdk/dist/query/mutation-event-mapper.d.ts +4 -0
  779. package/sdk/dist/query/mutation-event-mapper.d.ts.map +1 -0
  780. package/sdk/dist/query/mutation-event-mapper.js +70 -0
  781. package/sdk/dist/query/mutation-event-mapper.js.map +1 -0
  782. package/sdk/dist/query/mvp.d.ts +113 -0
  783. package/sdk/dist/query/mvp.d.ts.map +1 -0
  784. package/sdk/dist/query/mvp.js +225 -0
  785. package/sdk/dist/query/mvp.js.map +1 -0
  786. package/sdk/dist/query/phase-filesystem-adapter.d.ts +4 -0
  787. package/sdk/dist/query/phase-filesystem-adapter.d.ts.map +1 -0
  788. package/sdk/dist/query/phase-filesystem-adapter.js +33 -0
  789. package/sdk/dist/query/phase-filesystem-adapter.js.map +1 -0
  790. package/sdk/dist/query/phase-lifecycle-policy.d.ts +34 -0
  791. package/sdk/dist/query/phase-lifecycle-policy.d.ts.map +1 -0
  792. package/sdk/dist/query/phase-lifecycle-policy.js +138 -0
  793. package/sdk/dist/query/phase-lifecycle-policy.js.map +1 -0
  794. package/sdk/dist/query/phase-lifecycle.d.ts +116 -0
  795. package/sdk/dist/query/phase-lifecycle.d.ts.map +1 -0
  796. package/sdk/dist/query/phase-lifecycle.js +1823 -0
  797. package/sdk/dist/query/phase-lifecycle.js.map +1 -0
  798. package/sdk/dist/query/phase-list-queries.d.ts +20 -0
  799. package/sdk/dist/query/phase-list-queries.d.ts.map +1 -0
  800. package/sdk/dist/query/phase-list-queries.js +129 -0
  801. package/sdk/dist/query/phase-list-queries.js.map +1 -0
  802. package/sdk/dist/query/phase-ready.d.ts +9 -0
  803. package/sdk/dist/query/phase-ready.d.ts.map +1 -0
  804. package/sdk/dist/query/phase-ready.js +132 -0
  805. package/sdk/dist/query/phase-ready.js.map +1 -0
  806. package/sdk/dist/query/phase-roadmap-mutation.d.ts +25 -0
  807. package/sdk/dist/query/phase-roadmap-mutation.d.ts.map +1 -0
  808. package/sdk/dist/query/phase-roadmap-mutation.js +76 -0
  809. package/sdk/dist/query/phase-roadmap-mutation.js.map +1 -0
  810. package/sdk/dist/query/phase-uat-passed.d.ts +46 -0
  811. package/sdk/dist/query/phase-uat-passed.d.ts.map +1 -0
  812. package/sdk/dist/query/phase-uat-passed.js +238 -0
  813. package/sdk/dist/query/phase-uat-passed.js.map +1 -0
  814. package/sdk/dist/query/phase.d.ts +104 -0
  815. package/sdk/dist/query/phase.d.ts.map +1 -0
  816. package/sdk/dist/query/phase.js +617 -0
  817. package/sdk/dist/query/phase.js.map +1 -0
  818. package/sdk/dist/query/pipeline.d.ts +53 -0
  819. package/sdk/dist/query/pipeline.d.ts.map +1 -0
  820. package/sdk/dist/query/pipeline.js +198 -0
  821. package/sdk/dist/query/pipeline.js.map +1 -0
  822. package/sdk/dist/query/plan-scan.d.ts +14 -0
  823. package/sdk/dist/query/plan-scan.d.ts.map +1 -0
  824. package/sdk/dist/query/plan-scan.js +70 -0
  825. package/sdk/dist/query/plan-scan.js.map +1 -0
  826. package/sdk/dist/query/plan-task-structure.d.ts +9 -0
  827. package/sdk/dist/query/plan-task-structure.d.ts.map +1 -0
  828. package/sdk/dist/query/plan-task-structure.js +59 -0
  829. package/sdk/dist/query/plan-task-structure.js.map +1 -0
  830. package/sdk/dist/query/profile-extract-messages.d.ts +40 -0
  831. package/sdk/dist/query/profile-extract-messages.d.ts.map +1 -0
  832. package/sdk/dist/query/profile-extract-messages.js +195 -0
  833. package/sdk/dist/query/profile-extract-messages.js.map +1 -0
  834. package/sdk/dist/query/profile-output.d.ts +11 -0
  835. package/sdk/dist/query/profile-output.d.ts.map +1 -0
  836. package/sdk/dist/query/profile-output.js +873 -0
  837. package/sdk/dist/query/profile-output.js.map +1 -0
  838. package/sdk/dist/query/profile-questionnaire-data.d.ts +21 -0
  839. package/sdk/dist/query/profile-questionnaire-data.d.ts.map +1 -0
  840. package/sdk/dist/query/profile-questionnaire-data.js +171 -0
  841. package/sdk/dist/query/profile-questionnaire-data.js.map +1 -0
  842. package/sdk/dist/query/profile-sample.d.ts +22 -0
  843. package/sdk/dist/query/profile-sample.d.ts.map +1 -0
  844. package/sdk/dist/query/profile-sample.js +136 -0
  845. package/sdk/dist/query/profile-sample.js.map +1 -0
  846. package/sdk/dist/query/profile-scan-sessions.d.ts +49 -0
  847. package/sdk/dist/query/profile-scan-sessions.d.ts.map +1 -0
  848. package/sdk/dist/query/profile-scan-sessions.js +137 -0
  849. package/sdk/dist/query/profile-scan-sessions.js.map +1 -0
  850. package/sdk/dist/query/profile.d.ts +61 -0
  851. package/sdk/dist/query/profile.d.ts.map +1 -0
  852. package/sdk/dist/query/profile.js +307 -0
  853. package/sdk/dist/query/profile.js.map +1 -0
  854. package/sdk/dist/query/progress.d.ts +77 -0
  855. package/sdk/dist/query/progress.d.ts.map +1 -0
  856. package/sdk/dist/query/progress.js +481 -0
  857. package/sdk/dist/query/progress.js.map +1 -0
  858. package/sdk/dist/query/prompt-budget.d.ts +14 -0
  859. package/sdk/dist/query/prompt-budget.d.ts.map +1 -0
  860. package/sdk/dist/query/prompt-budget.js +417 -0
  861. package/sdk/dist/query/prompt-budget.js.map +1 -0
  862. package/sdk/dist/query/query-cli-adapter.d.ts +8 -0
  863. package/sdk/dist/query/query-cli-adapter.d.ts.map +1 -0
  864. package/sdk/dist/query/query-cli-adapter.js +32 -0
  865. package/sdk/dist/query/query-cli-adapter.js.map +1 -0
  866. package/sdk/dist/query/query-cli-output.d.ts +9 -0
  867. package/sdk/dist/query/query-cli-output.d.ts.map +1 -0
  868. package/sdk/dist/query/query-cli-output.js +28 -0
  869. package/sdk/dist/query/query-cli-output.js.map +1 -0
  870. package/sdk/dist/query/query-command-diagnosis.d.ts +6 -0
  871. package/sdk/dist/query/query-command-diagnosis.d.ts.map +1 -0
  872. package/sdk/dist/query/query-command-diagnosis.js +6 -0
  873. package/sdk/dist/query/query-command-diagnosis.js.map +1 -0
  874. package/sdk/dist/query/query-command-resolution-strategy.d.ts +29 -0
  875. package/sdk/dist/query/query-command-resolution-strategy.d.ts.map +1 -0
  876. package/sdk/dist/query/query-command-resolution-strategy.js +103 -0
  877. package/sdk/dist/query/query-command-resolution-strategy.js.map +1 -0
  878. package/sdk/dist/query/query-command-semantics.d.ts +7 -0
  879. package/sdk/dist/query/query-command-semantics.d.ts.map +1 -0
  880. package/sdk/dist/query/query-command-semantics.js +7 -0
  881. package/sdk/dist/query/query-command-semantics.js.map +1 -0
  882. package/sdk/dist/query/query-dispatch-contract.d.ts +21 -0
  883. package/sdk/dist/query/query-dispatch-contract.d.ts.map +1 -0
  884. package/sdk/dist/query/query-dispatch-contract.js +2 -0
  885. package/sdk/dist/query/query-dispatch-contract.js.map +1 -0
  886. package/sdk/dist/query/query-dispatch-error-mapper.d.ts +6 -0
  887. package/sdk/dist/query/query-dispatch-error-mapper.d.ts.map +1 -0
  888. package/sdk/dist/query/query-dispatch-error-mapper.js +6 -0
  889. package/sdk/dist/query/query-dispatch-error-mapper.js.map +1 -0
  890. package/sdk/dist/query/query-dispatch-formatting.d.ts +6 -0
  891. package/sdk/dist/query/query-dispatch-formatting.d.ts.map +1 -0
  892. package/sdk/dist/query/query-dispatch-formatting.js +6 -0
  893. package/sdk/dist/query/query-dispatch-formatting.js.map +1 -0
  894. package/sdk/dist/query/query-dispatch-observability.d.ts +2 -0
  895. package/sdk/dist/query/query-dispatch-observability.d.ts.map +1 -0
  896. package/sdk/dist/query/query-dispatch-observability.js +7 -0
  897. package/sdk/dist/query/query-dispatch-observability.js.map +1 -0
  898. package/sdk/dist/query/query-dispatch.d.ts +48 -0
  899. package/sdk/dist/query/query-dispatch.d.ts.map +1 -0
  900. package/sdk/dist/query/query-dispatch.js +175 -0
  901. package/sdk/dist/query/query-dispatch.js.map +1 -0
  902. package/sdk/dist/query/query-error-details-schema.d.ts +19 -0
  903. package/sdk/dist/query/query-error-details-schema.d.ts.map +1 -0
  904. package/sdk/dist/query/query-error-details-schema.js +10 -0
  905. package/sdk/dist/query/query-error-details-schema.js.map +1 -0
  906. package/sdk/dist/query/query-error-taxonomy.d.ts +38 -0
  907. package/sdk/dist/query/query-error-taxonomy.d.ts.map +1 -0
  908. package/sdk/dist/query/query-error-taxonomy.js +74 -0
  909. package/sdk/dist/query/query-error-taxonomy.js.map +1 -0
  910. package/sdk/dist/query/query-fallback-bridge-adapter.d.ts +14 -0
  911. package/sdk/dist/query/query-fallback-bridge-adapter.d.ts.map +1 -0
  912. package/sdk/dist/query/query-fallback-bridge-adapter.js +33 -0
  913. package/sdk/dist/query/query-fallback-bridge-adapter.js.map +1 -0
  914. package/sdk/dist/query/query-fallback-executor.d.ts +11 -0
  915. package/sdk/dist/query/query-fallback-executor.d.ts.map +1 -0
  916. package/sdk/dist/query/query-fallback-executor.js +31 -0
  917. package/sdk/dist/query/query-fallback-executor.js.map +1 -0
  918. package/sdk/dist/query/query-fallback-output-classifier.d.ts +6 -0
  919. package/sdk/dist/query/query-fallback-output-classifier.d.ts.map +1 -0
  920. package/sdk/dist/query/query-fallback-output-classifier.js +27 -0
  921. package/sdk/dist/query/query-fallback-output-classifier.js.map +1 -0
  922. package/sdk/dist/query/query-fallback-policy.d.ts +6 -0
  923. package/sdk/dist/query/query-fallback-policy.d.ts.map +1 -0
  924. package/sdk/dist/query/query-fallback-policy.js +7 -0
  925. package/sdk/dist/query/query-fallback-policy.js.map +1 -0
  926. package/sdk/dist/query/query-native-dispatch-adapter.d.ts +7 -0
  927. package/sdk/dist/query/query-native-dispatch-adapter.d.ts.map +1 -0
  928. package/sdk/dist/query/query-native-dispatch-adapter.js +6 -0
  929. package/sdk/dist/query/query-native-dispatch-adapter.js.map +1 -0
  930. package/sdk/dist/query/query-policy-capability.d.ts +10 -0
  931. package/sdk/dist/query/query-policy-capability.d.ts.map +1 -0
  932. package/sdk/dist/query/query-policy-capability.js +17 -0
  933. package/sdk/dist/query/query-policy-capability.js.map +1 -0
  934. package/sdk/dist/query/query-runtime-context.d.ts +19 -0
  935. package/sdk/dist/query/query-runtime-context.d.ts.map +1 -0
  936. package/sdk/dist/query/query-runtime-context.js +31 -0
  937. package/sdk/dist/query/query-runtime-context.js.map +1 -0
  938. package/sdk/dist/query/query-unknown-command-hints.d.ts +2 -0
  939. package/sdk/dist/query/query-unknown-command-hints.d.ts.map +1 -0
  940. package/sdk/dist/query/query-unknown-command-hints.js +6 -0
  941. package/sdk/dist/query/query-unknown-command-hints.js.map +1 -0
  942. package/sdk/dist/query/registry-assembly-descriptor.d.ts +12 -0
  943. package/sdk/dist/query/registry-assembly-descriptor.d.ts.map +1 -0
  944. package/sdk/dist/query/registry-assembly-descriptor.js +61 -0
  945. package/sdk/dist/query/registry-assembly-descriptor.js.map +1 -0
  946. package/sdk/dist/query/registry-assembly-invariants.d.ts +30 -0
  947. package/sdk/dist/query/registry-assembly-invariants.d.ts.map +1 -0
  948. package/sdk/dist/query/registry-assembly-invariants.js +77 -0
  949. package/sdk/dist/query/registry-assembly-invariants.js.map +1 -0
  950. package/sdk/dist/query/registry-assembly.d.ts +10 -0
  951. package/sdk/dist/query/registry-assembly.d.ts.map +1 -0
  952. package/sdk/dist/query/registry-assembly.js +53 -0
  953. package/sdk/dist/query/registry-assembly.js.map +1 -0
  954. package/sdk/dist/query/registry.d.ts +90 -0
  955. package/sdk/dist/query/registry.d.ts.map +1 -0
  956. package/sdk/dist/query/registry.js +129 -0
  957. package/sdk/dist/query/registry.js.map +1 -0
  958. package/sdk/dist/query/requirements-extract-from-plans.d.ts +9 -0
  959. package/sdk/dist/query/requirements-extract-from-plans.d.ts.map +1 -0
  960. package/sdk/dist/query/requirements-extract-from-plans.js +76 -0
  961. package/sdk/dist/query/requirements-extract-from-plans.js.map +1 -0
  962. package/sdk/dist/query/roadmap-update-plan-progress.d.ts +11 -0
  963. package/sdk/dist/query/roadmap-update-plan-progress.d.ts.map +1 -0
  964. package/sdk/dist/query/roadmap-update-plan-progress.js +124 -0
  965. package/sdk/dist/query/roadmap-update-plan-progress.js.map +1 -0
  966. package/sdk/dist/query/roadmap.d.ts +160 -0
  967. package/sdk/dist/query/roadmap.d.ts.map +1 -0
  968. package/sdk/dist/query/roadmap.js +982 -0
  969. package/sdk/dist/query/roadmap.js.map +1 -0
  970. package/sdk/dist/query/route-next-action.d.ts +9 -0
  971. package/sdk/dist/query/route-next-action.d.ts.map +1 -0
  972. package/sdk/dist/query/route-next-action.js +318 -0
  973. package/sdk/dist/query/route-next-action.js.map +1 -0
  974. package/sdk/dist/query/schema-detect.d.ts +21 -0
  975. package/sdk/dist/query/schema-detect.d.ts.map +1 -0
  976. package/sdk/dist/query/schema-detect.js +146 -0
  977. package/sdk/dist/query/schema-detect.js.map +1 -0
  978. package/sdk/dist/query/secrets.d.ts +27 -0
  979. package/sdk/dist/query/secrets.d.ts.map +1 -0
  980. package/sdk/dist/query/secrets.js +42 -0
  981. package/sdk/dist/query/secrets.js.map +1 -0
  982. package/sdk/dist/query/skill-manifest.d.ts +50 -0
  983. package/sdk/dist/query/skill-manifest.d.ts.map +1 -0
  984. package/sdk/dist/query/skill-manifest.js +171 -0
  985. package/sdk/dist/query/skill-manifest.js.map +1 -0
  986. package/sdk/dist/query/skills.d.ts +27 -0
  987. package/sdk/dist/query/skills.d.ts.map +1 -0
  988. package/sdk/dist/query/skills.js +137 -0
  989. package/sdk/dist/query/skills.js.map +1 -0
  990. package/sdk/dist/query/state-document.d.ts +14 -0
  991. package/sdk/dist/query/state-document.d.ts.map +1 -0
  992. package/sdk/dist/query/state-document.js +110 -0
  993. package/sdk/dist/query/state-document.js.map +1 -0
  994. package/sdk/dist/query/state-mutation.d.ts +224 -0
  995. package/sdk/dist/query/state-mutation.d.ts.map +1 -0
  996. package/sdk/dist/query/state-mutation.js +1635 -0
  997. package/sdk/dist/query/state-mutation.js.map +1 -0
  998. package/sdk/dist/query/state-project-load.d.ts +23 -0
  999. package/sdk/dist/query/state-project-load.d.ts.map +1 -0
  1000. package/sdk/dist/query/state-project-load.js +75 -0
  1001. package/sdk/dist/query/state-project-load.js.map +1 -0
  1002. package/sdk/dist/query/state.d.ts +78 -0
  1003. package/sdk/dist/query/state.d.ts.map +1 -0
  1004. package/sdk/dist/query/state.js +443 -0
  1005. package/sdk/dist/query/state.js.map +1 -0
  1006. package/sdk/dist/query/summary.d.ts +18 -0
  1007. package/sdk/dist/query/summary.d.ts.map +1 -0
  1008. package/sdk/dist/query/summary.js +249 -0
  1009. package/sdk/dist/query/summary.js.map +1 -0
  1010. package/sdk/dist/query/template.d.ts +46 -0
  1011. package/sdk/dist/query/template.d.ts.map +1 -0
  1012. package/sdk/dist/query/template.js +210 -0
  1013. package/sdk/dist/query/template.js.map +1 -0
  1014. package/sdk/dist/query/uat.d.ts +42 -0
  1015. package/sdk/dist/query/uat.d.ts.map +1 -0
  1016. package/sdk/dist/query/uat.js +339 -0
  1017. package/sdk/dist/query/uat.js.map +1 -0
  1018. package/sdk/dist/query/utils.d.ts +59 -0
  1019. package/sdk/dist/query/utils.d.ts.map +1 -0
  1020. package/sdk/dist/query/utils.js +74 -0
  1021. package/sdk/dist/query/utils.js.map +1 -0
  1022. package/sdk/dist/query/validate.d.ts +67 -0
  1023. package/sdk/dist/query/validate.d.ts.map +1 -0
  1024. package/sdk/dist/query/validate.js +1001 -0
  1025. package/sdk/dist/query/validate.js.map +1 -0
  1026. package/sdk/dist/query/verify.d.ts +98 -0
  1027. package/sdk/dist/query/verify.d.ts.map +1 -0
  1028. package/sdk/dist/query/verify.js +593 -0
  1029. package/sdk/dist/query/verify.js.map +1 -0
  1030. package/sdk/dist/query/websearch.d.ts +24 -0
  1031. package/sdk/dist/query/websearch.d.ts.map +1 -0
  1032. package/sdk/dist/query/websearch.js +68 -0
  1033. package/sdk/dist/query/websearch.js.map +1 -0
  1034. package/sdk/dist/query/workspace.d.ts +62 -0
  1035. package/sdk/dist/query/workspace.d.ts.map +1 -0
  1036. package/sdk/dist/query/workspace.js +104 -0
  1037. package/sdk/dist/query/workspace.js.map +1 -0
  1038. package/sdk/dist/query/workstream-inventory.d.ts +24 -0
  1039. package/sdk/dist/query/workstream-inventory.d.ts.map +1 -0
  1040. package/sdk/dist/query/workstream-inventory.js +120 -0
  1041. package/sdk/dist/query/workstream-inventory.js.map +1 -0
  1042. package/sdk/dist/query/workstream.d.ts +35 -0
  1043. package/sdk/dist/query/workstream.d.ts.map +1 -0
  1044. package/sdk/dist/query/workstream.js +298 -0
  1045. package/sdk/dist/query/workstream.js.map +1 -0
  1046. package/sdk/dist/query/worktree.d.ts +9 -0
  1047. package/sdk/dist/query/worktree.d.ts.map +1 -0
  1048. package/sdk/dist/query/worktree.js +79 -0
  1049. package/sdk/dist/query/worktree.js.map +1 -0
  1050. package/sdk/dist/query-command-executor.d.ts +22 -0
  1051. package/sdk/dist/query-command-executor.d.ts.map +1 -0
  1052. package/sdk/dist/query-command-executor.js +22 -0
  1053. package/sdk/dist/query-command-executor.js.map +1 -0
  1054. package/sdk/dist/query-execution-policy.d.ts +24 -0
  1055. package/sdk/dist/query-execution-policy.d.ts.map +1 -0
  1056. package/sdk/dist/query-execution-policy.js +27 -0
  1057. package/sdk/dist/query-execution-policy.js.map +1 -0
  1058. package/sdk/dist/query-failure-classification.d.ts +9 -0
  1059. package/sdk/dist/query-failure-classification.d.ts.map +1 -0
  1060. package/sdk/dist/query-failure-classification.js +32 -0
  1061. package/sdk/dist/query-failure-classification.js.map +1 -0
  1062. package/sdk/dist/query-gsd-tools-path.d.ts +2 -0
  1063. package/sdk/dist/query-gsd-tools-path.d.ts.map +1 -0
  1064. package/sdk/dist/query-gsd-tools-path.js +2 -0
  1065. package/sdk/dist/query-gsd-tools-path.js.map +1 -0
  1066. package/sdk/dist/query-gsd-tools-runtime.d.ts +20 -0
  1067. package/sdk/dist/query-gsd-tools-runtime.d.ts.map +1 -0
  1068. package/sdk/dist/query-gsd-tools-runtime.js +47 -0
  1069. package/sdk/dist/query-gsd-tools-runtime.js.map +1 -0
  1070. package/sdk/dist/query-hotpath-methods.d.ts +19 -0
  1071. package/sdk/dist/query-hotpath-methods.d.ts.map +1 -0
  1072. package/sdk/dist/query-hotpath-methods.js +34 -0
  1073. package/sdk/dist/query-hotpath-methods.js.map +1 -0
  1074. package/sdk/dist/query-native-direct-adapter.d.ts +20 -0
  1075. package/sdk/dist/query-native-direct-adapter.d.ts.map +1 -0
  1076. package/sdk/dist/query-native-direct-adapter.js +52 -0
  1077. package/sdk/dist/query-native-direct-adapter.js.map +1 -0
  1078. package/sdk/dist/query-native-hotpath-adapter.d.ts +15 -0
  1079. package/sdk/dist/query-native-hotpath-adapter.d.ts.map +1 -0
  1080. package/sdk/dist/query-native-hotpath-adapter.js +32 -0
  1081. package/sdk/dist/query-native-hotpath-adapter.js.map +1 -0
  1082. package/sdk/dist/query-raw-output-projection.d.ts +6 -0
  1083. package/sdk/dist/query-raw-output-projection.d.ts.map +1 -0
  1084. package/sdk/dist/query-raw-output-projection.js +86 -0
  1085. package/sdk/dist/query-raw-output-projection.js.map +1 -0
  1086. package/sdk/dist/query-runtime-bridge.d.ts +61 -0
  1087. package/sdk/dist/query-runtime-bridge.d.ts.map +1 -0
  1088. package/sdk/dist/query-runtime-bridge.js +144 -0
  1089. package/sdk/dist/query-runtime-bridge.js.map +1 -0
  1090. package/sdk/dist/query-subprocess-adapter.d.ts +18 -0
  1091. package/sdk/dist/query-subprocess-adapter.d.ts.map +1 -0
  1092. package/sdk/dist/query-subprocess-adapter.js +92 -0
  1093. package/sdk/dist/query-subprocess-adapter.js.map +1 -0
  1094. package/sdk/dist/query-tools-error-factory.d.ts +16 -0
  1095. package/sdk/dist/query-tools-error-factory.d.ts.map +1 -0
  1096. package/sdk/dist/query-tools-error-factory.js +33 -0
  1097. package/sdk/dist/query-tools-error-factory.js.map +1 -0
  1098. package/sdk/dist/research-gate.d.ts +24 -0
  1099. package/sdk/dist/research-gate.d.ts.map +1 -0
  1100. package/sdk/dist/research-gate.js +70 -0
  1101. package/sdk/dist/research-gate.js.map +1 -0
  1102. package/sdk/dist/runtime-bridge-sync/index.d.ts +96 -0
  1103. package/sdk/dist/runtime-bridge-sync/index.d.ts.map +1 -0
  1104. package/sdk/dist/runtime-bridge-sync/index.js +109 -0
  1105. package/sdk/dist/runtime-bridge-sync/index.js.map +1 -0
  1106. package/sdk/dist/runtime-bridge-sync/worker.d.ts +2 -0
  1107. package/sdk/dist/runtime-bridge-sync/worker.d.ts.map +1 -0
  1108. package/sdk/dist/runtime-bridge-sync/worker.js +180 -0
  1109. package/sdk/dist/runtime-bridge-sync/worker.js.map +1 -0
  1110. package/sdk/dist/runtime-gate.d.ts +14 -0
  1111. package/sdk/dist/runtime-gate.d.ts.map +1 -0
  1112. package/sdk/dist/runtime-gate.js +48 -0
  1113. package/sdk/dist/runtime-gate.js.map +1 -0
  1114. package/sdk/dist/sdk-package-compatibility.d.ts +38 -0
  1115. package/sdk/dist/sdk-package-compatibility.d.ts.map +1 -0
  1116. package/sdk/dist/sdk-package-compatibility.js +90 -0
  1117. package/sdk/dist/sdk-package-compatibility.js.map +1 -0
  1118. package/sdk/dist/session-runner.d.ts +40 -0
  1119. package/sdk/dist/session-runner.d.ts.map +1 -0
  1120. package/sdk/dist/session-runner.js +274 -0
  1121. package/sdk/dist/session-runner.js.map +1 -0
  1122. package/sdk/dist/tool-scoping.d.ts +31 -0
  1123. package/sdk/dist/tool-scoping.d.ts.map +1 -0
  1124. package/sdk/dist/tool-scoping.js +54 -0
  1125. package/sdk/dist/tool-scoping.js.map +1 -0
  1126. package/sdk/dist/types.d.ts +794 -0
  1127. package/sdk/dist/types.d.ts.map +1 -0
  1128. package/sdk/dist/types.js +77 -0
  1129. package/sdk/dist/types.js.map +1 -0
  1130. package/sdk/dist/workstream-inventory/builder.d.ts +88 -0
  1131. package/sdk/dist/workstream-inventory/builder.d.ts.map +1 -0
  1132. package/sdk/dist/workstream-inventory/builder.js +84 -0
  1133. package/sdk/dist/workstream-inventory/builder.js.map +1 -0
  1134. package/sdk/dist/workstream-name-policy.d.ts +37 -0
  1135. package/sdk/dist/workstream-name-policy.d.ts.map +1 -0
  1136. package/sdk/dist/workstream-name-policy.js +53 -0
  1137. package/sdk/dist/workstream-name-policy.js.map +1 -0
  1138. package/sdk/dist/workstream-utils.d.ts +23 -0
  1139. package/sdk/dist/workstream-utils.d.ts.map +1 -0
  1140. package/sdk/dist/workstream-utils.js +34 -0
  1141. package/sdk/dist/workstream-utils.js.map +1 -0
  1142. package/sdk/dist/ws-transport.d.ts +32 -0
  1143. package/sdk/dist/ws-transport.d.ts.map +1 -0
  1144. package/sdk/dist/ws-transport.js +84 -0
  1145. package/sdk/dist/ws-transport.js.map +1 -0
  1146. package/sdk/package-lock.json +2530 -0
  1147. package/sdk/package.json +77 -0
  1148. package/sdk/prompts/templates/project.md +186 -0
  1149. package/sdk/prompts/templates/requirements.md +231 -0
  1150. package/sdk/prompts/templates/research-project/ARCHITECTURE.md +204 -0
  1151. package/sdk/prompts/templates/research-project/FEATURES.md +147 -0
  1152. package/sdk/prompts/templates/research-project/PITFALLS.md +200 -0
  1153. package/sdk/prompts/templates/research-project/STACK.md +120 -0
  1154. package/sdk/prompts/templates/research-project/SUMMARY.md +170 -0
  1155. package/sdk/prompts/templates/roadmap.md +202 -0
  1156. package/sdk/prompts/templates/state.md +175 -0
  1157. package/sdk/shared/config-defaults.manifest.json +75 -0
  1158. package/sdk/shared/config-schema.manifest.json +151 -0
  1159. package/sdk/shared/model-catalog.json +122 -0
  1160. package/sdk/src/assembled-prompts.test.ts +349 -0
  1161. package/sdk/src/bug-3589-planning-paths-validation.test.ts +89 -0
  1162. package/sdk/src/bug-3591-gsdtools-runtime-workstream.test.ts +179 -0
  1163. package/sdk/src/cli-transport.test.ts +388 -0
  1164. package/sdk/src/cli-transport.ts +130 -0
  1165. package/sdk/src/cli.test.ts +426 -0
  1166. package/sdk/src/cli.ts +589 -0
  1167. package/sdk/src/config.test.ts +277 -0
  1168. package/sdk/src/config.ts +202 -0
  1169. package/sdk/src/configuration/index.test.ts +318 -0
  1170. package/sdk/src/configuration/index.ts +325 -0
  1171. package/sdk/src/context-engine.test.ts +295 -0
  1172. package/sdk/src/context-engine.ts +170 -0
  1173. package/sdk/src/context-truncation.test.ts +163 -0
  1174. package/sdk/src/context-truncation.ts +233 -0
  1175. package/sdk/src/e2e.integration.test.ts +181 -0
  1176. package/sdk/src/errors.ts +72 -0
  1177. package/sdk/src/event-stream.test.ts +661 -0
  1178. package/sdk/src/event-stream.ts +441 -0
  1179. package/sdk/src/golden/capture.ts +95 -0
  1180. package/sdk/src/golden/fixtures/generate-slug.golden.json +1 -0
  1181. package/sdk/src/golden/fixtures/profile-sample-sessions/demo-project/sample.jsonl +3 -0
  1182. package/sdk/src/golden/fixtures/summary-extract-sample.md +26 -0
  1183. package/sdk/src/golden/fixtures/uat-render-checkpoint-sample.md +15 -0
  1184. package/sdk/src/golden/golden-integration-covered.ts +30 -0
  1185. package/sdk/src/golden/golden-mutation-covered.ts +17 -0
  1186. package/sdk/src/golden/golden-policy.test.ts +8 -0
  1187. package/sdk/src/golden/golden-policy.ts +120 -0
  1188. package/sdk/src/golden/golden.integration.test.ts +1031 -0
  1189. package/sdk/src/golden/init-golden-normalize.ts +15 -0
  1190. package/sdk/src/golden/read-only-golden-rows.ts +77 -0
  1191. package/sdk/src/golden/read-only-parity.integration.test.ts +133 -0
  1192. package/sdk/src/golden/registry-canonical-commands.ts +31 -0
  1193. package/sdk/src/gsd-tools-error.test.ts +21 -0
  1194. package/sdk/src/gsd-tools-error.ts +65 -0
  1195. package/sdk/src/gsd-tools.test.ts +472 -0
  1196. package/sdk/src/gsd-tools.ts +237 -0
  1197. package/sdk/src/gsd-transport-policy.test.ts +34 -0
  1198. package/sdk/src/gsd-transport-policy.ts +48 -0
  1199. package/sdk/src/gsd-transport.test.ts +299 -0
  1200. package/sdk/src/gsd-transport.ts +118 -0
  1201. package/sdk/src/index.ts +366 -0
  1202. package/sdk/src/init-e2e.integration.test.ts +138 -0
  1203. package/sdk/src/init-runner.test.ts +740 -0
  1204. package/sdk/src/init-runner.ts +734 -0
  1205. package/sdk/src/lifecycle-e2e.integration.test.ts +258 -0
  1206. package/sdk/src/logger.test.ts +149 -0
  1207. package/sdk/src/logger.ts +113 -0
  1208. package/sdk/src/milestone-runner.test.ts +421 -0
  1209. package/sdk/src/model-catalog.ts +70 -0
  1210. package/sdk/src/phase-prompt.ts +259 -0
  1211. package/sdk/src/phase-runner.integration.test.ts +377 -0
  1212. package/sdk/src/phase-runner.test.ts +3660 -0
  1213. package/sdk/src/phase-runner.ts +1442 -0
  1214. package/sdk/src/plan-parser.test.ts +579 -0
  1215. package/sdk/src/plan-parser.ts +431 -0
  1216. package/sdk/src/planning-journal.test.ts +70 -0
  1217. package/sdk/src/planning-journal.ts +153 -0
  1218. package/sdk/src/planning-runtime.test.ts +29 -0
  1219. package/sdk/src/planning-runtime.ts +100 -0
  1220. package/sdk/src/project-root/index.test.ts +186 -0
  1221. package/sdk/src/project-root/index.ts +144 -0
  1222. package/sdk/src/prompt-builder.test.ts +318 -0
  1223. package/sdk/src/prompt-builder.ts +218 -0
  1224. package/sdk/src/prompt-sanitizer.test.ts +260 -0
  1225. package/sdk/src/prompt-sanitizer.ts +116 -0
  1226. package/sdk/src/query/QUERY-HANDLERS.md +349 -0
  1227. package/sdk/src/query/active-workstream-store.ts +50 -0
  1228. package/sdk/src/query/agent-failure-classifier.test.ts +157 -0
  1229. package/sdk/src/query/agent-failure-classifier.ts +105 -0
  1230. package/sdk/src/query/audit-open.ts +722 -0
  1231. package/sdk/src/query/check-auto-mode.test.ts +77 -0
  1232. package/sdk/src/query/check-auto-mode.ts +49 -0
  1233. package/sdk/src/query/check-completion.test.ts +113 -0
  1234. package/sdk/src/query/check-completion.ts +182 -0
  1235. package/sdk/src/query/check-decision-coverage.test.ts +519 -0
  1236. package/sdk/src/query/check-decision-coverage.ts +554 -0
  1237. package/sdk/src/query/check-gates.test.ts +103 -0
  1238. package/sdk/src/query/check-gates.ts +112 -0
  1239. package/sdk/src/query/check-ship-ready.test.ts +303 -0
  1240. package/sdk/src/query/check-ship-ready.ts +136 -0
  1241. package/sdk/src/query/check-verification-status.test.ts +143 -0
  1242. package/sdk/src/query/check-verification-status.ts +160 -0
  1243. package/sdk/src/query/command-aliases.generated.ts +154 -0
  1244. package/sdk/src/query/command-catalog.ts +31 -0
  1245. package/sdk/src/query/command-definition.test.ts +47 -0
  1246. package/sdk/src/query/command-definition.ts +70 -0
  1247. package/sdk/src/query/command-family-handlers.ts +123 -0
  1248. package/sdk/src/query/command-manifest.init.ts +24 -0
  1249. package/sdk/src/query/command-manifest.non-family.ts +86 -0
  1250. package/sdk/src/query/command-manifest.phase.ts +17 -0
  1251. package/sdk/src/query/command-manifest.phases.ts +11 -0
  1252. package/sdk/src/query/command-manifest.roadmap.ts +11 -0
  1253. package/sdk/src/query/command-manifest.state.ts +31 -0
  1254. package/sdk/src/query/command-manifest.ts +17 -0
  1255. package/sdk/src/query/command-manifest.types.ts +13 -0
  1256. package/sdk/src/query/command-manifest.validate.ts +11 -0
  1257. package/sdk/src/query/command-manifest.verify.ts +17 -0
  1258. package/sdk/src/query/command-resolution.test.ts +70 -0
  1259. package/sdk/src/query/command-seam-coverage.test.ts +118 -0
  1260. package/sdk/src/query/command-static-catalog-domain.ts +111 -0
  1261. package/sdk/src/query/command-static-catalog-foundation.ts +111 -0
  1262. package/sdk/src/query/command-topology.test.ts +28 -0
  1263. package/sdk/src/query/command-topology.ts +114 -0
  1264. package/sdk/src/query/commands-list.test.ts +36 -0
  1265. package/sdk/src/query/commands-list.ts +19 -0
  1266. package/sdk/src/query/commit.test.ts +485 -0
  1267. package/sdk/src/query/commit.ts +717 -0
  1268. package/sdk/src/query/config-gates.test.ts +89 -0
  1269. package/sdk/src/query/config-gates.ts +69 -0
  1270. package/sdk/src/query/config-mutation.test.ts +598 -0
  1271. package/sdk/src/query/config-mutation.ts +705 -0
  1272. package/sdk/src/query/config-query.test.ts +472 -0
  1273. package/sdk/src/query/config-query.ts +314 -0
  1274. package/sdk/src/query/config-schema.ts +35 -0
  1275. package/sdk/src/query/decisions.test.ts +221 -0
  1276. package/sdk/src/query/decisions.ts +196 -0
  1277. package/sdk/src/query/decomposed-handlers.test.ts +431 -0
  1278. package/sdk/src/query/detect-custom-files.test.ts +115 -0
  1279. package/sdk/src/query/detect-custom-files.ts +96 -0
  1280. package/sdk/src/query/detect-phase-type.test.ts +105 -0
  1281. package/sdk/src/query/detect-phase-type.ts +141 -0
  1282. package/sdk/src/query/docs-init.ts +258 -0
  1283. package/sdk/src/query/fallow-audit.ts +88 -0
  1284. package/sdk/src/query/frontmatter-array.test.ts +14 -0
  1285. package/sdk/src/query/frontmatter-mutation.test.ts +259 -0
  1286. package/sdk/src/query/frontmatter-mutation.ts +328 -0
  1287. package/sdk/src/query/frontmatter.test.ts +326 -0
  1288. package/sdk/src/query/frontmatter.ts +395 -0
  1289. package/sdk/src/query/helpers.test.ts +615 -0
  1290. package/sdk/src/query/helpers.ts +566 -0
  1291. package/sdk/src/query/index-thin-seam.test.ts +16 -0
  1292. package/sdk/src/query/index.ts +9 -0
  1293. package/sdk/src/query/init-complex.test.ts +788 -0
  1294. package/sdk/src/query/init-complex.ts +815 -0
  1295. package/sdk/src/query/init-workstream-milestone-op.test.ts +321 -0
  1296. package/sdk/src/query/init.test.ts +791 -0
  1297. package/sdk/src/query/init.ts +1335 -0
  1298. package/sdk/src/query/intel.test.ts +90 -0
  1299. package/sdk/src/query/intel.ts +404 -0
  1300. package/sdk/src/query/mutation-event-decorator.test.ts +45 -0
  1301. package/sdk/src/query/mutation-event-decorator.ts +37 -0
  1302. package/sdk/src/query/mutation-event-mapper.test.ts +33 -0
  1303. package/sdk/src/query/mutation-event-mapper.ts +102 -0
  1304. package/sdk/src/query/mvp.test.ts +335 -0
  1305. package/sdk/src/query/mvp.ts +292 -0
  1306. package/sdk/src/query/normalize-query-command.test.ts +102 -0
  1307. package/sdk/src/query/phase-filesystem-adapter.ts +35 -0
  1308. package/sdk/src/query/phase-lifecycle-policy.ts +171 -0
  1309. package/sdk/src/query/phase-lifecycle.test.ts +1971 -0
  1310. package/sdk/src/query/phase-lifecycle.ts +2210 -0
  1311. package/sdk/src/query/phase-list-queries.test.ts +88 -0
  1312. package/sdk/src/query/phase-list-queries.ts +152 -0
  1313. package/sdk/src/query/phase-ready.test.ts +65 -0
  1314. package/sdk/src/query/phase-ready.ts +159 -0
  1315. package/sdk/src/query/phase-roadmap-mutation.ts +82 -0
  1316. package/sdk/src/query/phase-uat-passed.test.ts +593 -0
  1317. package/sdk/src/query/phase-uat-passed.ts +297 -0
  1318. package/sdk/src/query/phase.test.ts +693 -0
  1319. package/sdk/src/query/phase.ts +741 -0
  1320. package/sdk/src/query/pipeline.test.ts +169 -0
  1321. package/sdk/src/query/pipeline.ts +243 -0
  1322. package/sdk/src/query/plan-scan.test.ts +35 -0
  1323. package/sdk/src/query/plan-scan.ts +82 -0
  1324. package/sdk/src/query/plan-task-structure.test.ts +65 -0
  1325. package/sdk/src/query/plan-task-structure.ts +63 -0
  1326. package/sdk/src/query/policy-convergence.test.ts +28 -0
  1327. package/sdk/src/query/profile-extract-messages.ts +247 -0
  1328. package/sdk/src/query/profile-output.ts +929 -0
  1329. package/sdk/src/query/profile-questionnaire-data.ts +181 -0
  1330. package/sdk/src/query/profile-sample.ts +184 -0
  1331. package/sdk/src/query/profile-scan-sessions.ts +174 -0
  1332. package/sdk/src/query/profile.test.ts +136 -0
  1333. package/sdk/src/query/profile.ts +337 -0
  1334. package/sdk/src/query/progress.test.ts +156 -0
  1335. package/sdk/src/query/progress.ts +566 -0
  1336. package/sdk/src/query/prompt-budget.ts +556 -0
  1337. package/sdk/src/query/query-cli-adapter.test.ts +79 -0
  1338. package/sdk/src/query/query-cli-adapter.ts +39 -0
  1339. package/sdk/src/query/query-cli-output.test.ts +33 -0
  1340. package/sdk/src/query/query-cli-output.ts +35 -0
  1341. package/sdk/src/query/query-command-diagnosis.test.ts +22 -0
  1342. package/sdk/src/query/query-command-diagnosis.ts +5 -0
  1343. package/sdk/src/query/query-command-resolution-strategy.test.ts +34 -0
  1344. package/sdk/src/query/query-command-resolution-strategy.ts +121 -0
  1345. package/sdk/src/query/query-command-semantics.test.ts +22 -0
  1346. package/sdk/src/query/query-command-semantics.ts +22 -0
  1347. package/sdk/src/query/query-dispatch-contract.ts +30 -0
  1348. package/sdk/src/query/query-dispatch-error-mapper.ts +5 -0
  1349. package/sdk/src/query/query-dispatch-formatting.ts +5 -0
  1350. package/sdk/src/query/query-dispatch-observability.ts +6 -0
  1351. package/sdk/src/query/query-dispatch.test.ts +699 -0
  1352. package/sdk/src/query/query-dispatch.ts +243 -0
  1353. package/sdk/src/query/query-error-details-schema.ts +29 -0
  1354. package/sdk/src/query/query-error-taxonomy.test.ts +39 -0
  1355. package/sdk/src/query/query-error-taxonomy.ts +117 -0
  1356. package/sdk/src/query/query-fallback-bridge-adapter.test.ts +32 -0
  1357. package/sdk/src/query/query-fallback-bridge-adapter.ts +54 -0
  1358. package/sdk/src/query/query-fallback-executor.test.ts +82 -0
  1359. package/sdk/src/query/query-fallback-executor.ts +44 -0
  1360. package/sdk/src/query/query-fallback-output-classifier.test.ts +36 -0
  1361. package/sdk/src/query/query-fallback-output-classifier.ts +31 -0
  1362. package/sdk/src/query/query-fallback-policy.test.ts +13 -0
  1363. package/sdk/src/query/query-fallback-policy.ts +11 -0
  1364. package/sdk/src/query/query-native-dispatch-adapter.ts +16 -0
  1365. package/sdk/src/query/query-policy-capability.test.ts +10 -0
  1366. package/sdk/src/query/query-policy-capability.ts +26 -0
  1367. package/sdk/src/query/query-policy-snapshot.test.ts +9 -0
  1368. package/sdk/src/query/query-registry-capability.test.ts +14 -0
  1369. package/sdk/src/query/query-runtime-context.ts +44 -0
  1370. package/sdk/src/query/query-unknown-command-hints.test.ts +9 -0
  1371. package/sdk/src/query/query-unknown-command-hints.ts +5 -0
  1372. package/sdk/src/query/registry-assembly-descriptor.ts +87 -0
  1373. package/sdk/src/query/registry-assembly-invariants.ts +127 -0
  1374. package/sdk/src/query/registry-assembly.test.ts +138 -0
  1375. package/sdk/src/query/registry-assembly.ts +78 -0
  1376. package/sdk/src/query/registry.test.ts +208 -0
  1377. package/sdk/src/query/registry.ts +142 -0
  1378. package/sdk/src/query/requirements-extract-from-plans.test.ts +58 -0
  1379. package/sdk/src/query/requirements-extract-from-plans.ts +86 -0
  1380. package/sdk/src/query/roadmap-update-plan-progress.test.ts +233 -0
  1381. package/sdk/src/query/roadmap-update-plan-progress.ts +159 -0
  1382. package/sdk/src/query/roadmap.test.ts +1250 -0
  1383. package/sdk/src/query/roadmap.ts +1131 -0
  1384. package/sdk/src/query/route-next-action.test.ts +61 -0
  1385. package/sdk/src/query/route-next-action.ts +345 -0
  1386. package/sdk/src/query/schema-detect.ts +189 -0
  1387. package/sdk/src/query/secrets.test.ts +66 -0
  1388. package/sdk/src/query/secrets.ts +43 -0
  1389. package/sdk/src/query/skill-manifest.test.ts +62 -0
  1390. package/sdk/src/query/skill-manifest.ts +216 -0
  1391. package/sdk/src/query/skills.test.ts +234 -0
  1392. package/sdk/src/query/skills.ts +143 -0
  1393. package/sdk/src/query/state-document.test.ts +197 -0
  1394. package/sdk/src/query/state-document.ts +129 -0
  1395. package/sdk/src/query/state-mutation.test.ts +1210 -0
  1396. package/sdk/src/query/state-mutation.ts +1814 -0
  1397. package/sdk/src/query/state-project-load.ts +80 -0
  1398. package/sdk/src/query/state.test.ts +616 -0
  1399. package/sdk/src/query/state.ts +476 -0
  1400. package/sdk/src/query/sub-repos-root.integration.test.ts +79 -0
  1401. package/sdk/src/query/summary.test.ts +95 -0
  1402. package/sdk/src/query/summary.ts +296 -0
  1403. package/sdk/src/query/template.test.ts +180 -0
  1404. package/sdk/src/query/template.ts +242 -0
  1405. package/sdk/src/query/uat.test.ts +77 -0
  1406. package/sdk/src/query/uat.ts +365 -0
  1407. package/sdk/src/query/utils.test.ts +82 -0
  1408. package/sdk/src/query/utils.ts +106 -0
  1409. package/sdk/src/query/validate.test.ts +924 -0
  1410. package/sdk/src/query/validate.ts +1054 -0
  1411. package/sdk/src/query/verify.test.ts +414 -0
  1412. package/sdk/src/query/verify.ts +656 -0
  1413. package/sdk/src/query/websearch.test.ts +31 -0
  1414. package/sdk/src/query/websearch.ts +82 -0
  1415. package/sdk/src/query/workspace.test.ts +120 -0
  1416. package/sdk/src/query/workspace.ts +145 -0
  1417. package/sdk/src/query/workstream-inventory.ts +143 -0
  1418. package/sdk/src/query/workstream.test.ts +153 -0
  1419. package/sdk/src/query/workstream.ts +324 -0
  1420. package/sdk/src/query/worktree.ts +84 -0
  1421. package/sdk/src/query-command-executor.ts +31 -0
  1422. package/sdk/src/query-execution-policy.test.ts +52 -0
  1423. package/sdk/src/query-execution-policy.ts +46 -0
  1424. package/sdk/src/query-failure-classification.test.ts +23 -0
  1425. package/sdk/src/query-failure-classification.ts +42 -0
  1426. package/sdk/src/query-gsd-tools-path.ts +1 -0
  1427. package/sdk/src/query-gsd-tools-runtime.ts +89 -0
  1428. package/sdk/src/query-hotpath-methods.ts +48 -0
  1429. package/sdk/src/query-native-direct-adapter.test.ts +35 -0
  1430. package/sdk/src/query-native-direct-adapter.ts +70 -0
  1431. package/sdk/src/query-native-hotpath-adapter.test.ts +43 -0
  1432. package/sdk/src/query-native-hotpath-adapter.ts +45 -0
  1433. package/sdk/src/query-raw-output-projection.test.ts +39 -0
  1434. package/sdk/src/query-raw-output-projection.ts +93 -0
  1435. package/sdk/src/query-runtime-bridge.test.ts +150 -0
  1436. package/sdk/src/query-runtime-bridge.ts +215 -0
  1437. package/sdk/src/query-runtime-seam-coverage.test.ts +20 -0
  1438. package/sdk/src/query-subprocess-adapter.test.ts +84 -0
  1439. package/sdk/src/query-subprocess-adapter.ts +146 -0
  1440. package/sdk/src/query-tools-error-factory.test.ts +35 -0
  1441. package/sdk/src/query-tools-error-factory.ts +76 -0
  1442. package/sdk/src/research-gate.test.ts +190 -0
  1443. package/sdk/src/research-gate.ts +94 -0
  1444. package/sdk/src/runtime-bridge-options.test.ts +33 -0
  1445. package/sdk/src/runtime-bridge-sync/index.test.ts +164 -0
  1446. package/sdk/src/runtime-bridge-sync/index.ts +154 -0
  1447. package/sdk/src/runtime-bridge-sync/projectdir-regression.test.ts +150 -0
  1448. package/sdk/src/runtime-bridge-sync/worker.ts +224 -0
  1449. package/sdk/src/runtime-gate.test.ts +84 -0
  1450. package/sdk/src/runtime-gate.ts +52 -0
  1451. package/sdk/src/sdk-package-compatibility.test.ts +97 -0
  1452. package/sdk/src/sdk-package-compatibility.ts +141 -0
  1453. package/sdk/src/session-runner.test.ts +164 -0
  1454. package/sdk/src/session-runner.ts +327 -0
  1455. package/sdk/src/tool-scoping.test.ts +160 -0
  1456. package/sdk/src/tool-scoping.ts +61 -0
  1457. package/sdk/src/types.ts +927 -0
  1458. package/sdk/src/workflow-agent-skills-consistency.test.ts +98 -0
  1459. package/sdk/src/workstream-inventory/builder.test.ts +241 -0
  1460. package/sdk/src/workstream-inventory/builder.ts +170 -0
  1461. package/sdk/src/workstream-name-policy.ts +57 -0
  1462. package/sdk/src/workstream-utils.ts +36 -0
  1463. package/sdk/src/ws-flag.test.ts +285 -0
  1464. package/sdk/src/ws-transport.test.ts +161 -0
  1465. package/sdk/src/ws-transport.ts +93 -0
  1466. package/sdk/tsconfig.json +20 -0
@@ -0,0 +1,1922 @@
1
+ /**
2
+ * Core — Shared utilities, constants, and internal helpers
3
+ */
4
+
5
+ const fs = require('fs');
6
+ const os = require('os');
7
+ const path = require('path');
8
+ const { execGit, platformWriteSync, platformReadSync, platformEnsureDir } = require('./shell-command-projection.cjs');
9
+ const { MODEL_PROFILES, AGENT_TO_PHASE_TYPE, VALID_PHASE_TYPES, AGENT_DEFAULT_TIERS, VALID_AGENT_TIERS, nextTier } = require('./model-profiles.cjs');
10
+ const { MODEL_ALIAS_MAP, RUNTIME_PROFILE_MAP, KNOWN_RUNTIMES, RUNTIMES_WITH_REASONING_EFFORT } = require('./model-catalog.cjs');
11
+ const {
12
+ resolveWorktreeContext,
13
+ parseWorktreePorcelain: parseWorktreePorcelainPolicy,
14
+ planWorktreePrune,
15
+ executeWorktreePrunePlan,
16
+ inspectWorktreeHealth,
17
+ } = require('./worktree-safety.cjs');
18
+ // Compatibility shim: new imports should use planning-workspace.cjs directly.
19
+ const {
20
+ planningDir,
21
+ planningRoot,
22
+ planningPaths,
23
+ withPlanningLock,
24
+ getActiveWorkstream,
25
+ setActiveWorkstream,
26
+ findContextMdIn,
27
+ } = require('./planning-workspace.cjs');
28
+ const { findProjectRoot } = require('./project-root.generated.cjs');
29
+
30
+ // ─── Configuration Module (generated CJS mirror) ────────────────────────────
31
+ // Cycle 4: import canonical defaults + normalization primitives from the
32
+ // generated module; core.cjs no longer carries its own inline literal or its
33
+ // own migration logic. The exported CONFIG_DEFAULTS remains a flat-key object
34
+ // (shape unchanged) so legacy consumers (config.cjs, verify.cjs, tests) require
35
+ // no changes. Values are sourced from the canonical nested manifest.
36
+ const {
37
+ CONFIG_DEFAULTS: CANONICAL_CONFIG_DEFAULTS,
38
+ normalizeLegacyKeys,
39
+ } = require('./configuration.generated.cjs');
40
+
41
+ // ─── Path helpers ────────────────────────────────────────────────────────────
42
+
43
+ /** Normalize a relative path to always use forward slashes (cross-platform). */
44
+ function toPosixPath(p) {
45
+ return p.split(path.sep).join('/');
46
+ }
47
+
48
+ /**
49
+ * Scan immediate child directories for separate git repos.
50
+ * Returns a sorted array of directory names that have their own `.git`.
51
+ * Excludes hidden directories and node_modules.
52
+ */
53
+ function detectSubRepos(cwd) {
54
+ const results = [];
55
+ try {
56
+ const entries = fs.readdirSync(cwd, { withFileTypes: true });
57
+ for (const entry of entries) {
58
+ if (!entry.isDirectory()) continue;
59
+ if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
60
+ const gitPath = path.join(cwd, entry.name, '.git');
61
+ try {
62
+ if (fs.existsSync(gitPath)) {
63
+ results.push(entry.name);
64
+ }
65
+ } catch {}
66
+ }
67
+ } catch {}
68
+ return results.sort();
69
+ }
70
+
71
+ // findProjectRoot is now re-exported from the generated CJS module above.
72
+
73
+ // ─── Output helpers ───────────────────────────────────────────────────────────
74
+
75
+ /**
76
+ * Remove stale gsd-* temp files/dirs older than maxAgeMs (default: 5 minutes).
77
+ * Runs opportunistically before each new temp file write to prevent unbounded accumulation.
78
+ * @param {string} prefix - filename prefix to match (e.g., 'gsd-')
79
+ * @param {object} opts
80
+ * @param {number} opts.maxAgeMs - max age in ms before removal (default: 5 min)
81
+ * @param {boolean} opts.dirsOnly - if true, only remove directories (default: false)
82
+ */
83
+ /**
84
+ * Dedicated GSD temp directory: path.join(os.tmpdir(), 'gsd').
85
+ * Created on first use. Keeps GSD temp files isolated from the system
86
+ * temp directory so reap scans only GSD files (#1975).
87
+ */
88
+ const GSD_TEMP_DIR = path.join(require('os').tmpdir(), 'gsd');
89
+
90
+ function ensureGsdTempDir() {
91
+ platformEnsureDir(GSD_TEMP_DIR);
92
+ }
93
+
94
+ function reapStaleTempFiles(prefix = 'gsd-', { maxAgeMs = 5 * 60 * 1000, dirsOnly = false } = {}) {
95
+ try {
96
+ ensureGsdTempDir();
97
+ const now = Date.now();
98
+ const entries = fs.readdirSync(GSD_TEMP_DIR);
99
+ for (const entry of entries) {
100
+ if (!entry.startsWith(prefix)) continue;
101
+ const fullPath = path.join(GSD_TEMP_DIR, entry);
102
+ try {
103
+ const stat = fs.statSync(fullPath);
104
+ if (now - stat.mtimeMs > maxAgeMs) {
105
+ if (stat.isDirectory()) {
106
+ fs.rmSync(fullPath, { recursive: true, force: true });
107
+ } else if (!dirsOnly) {
108
+ fs.unlinkSync(fullPath);
109
+ }
110
+ }
111
+ } catch {
112
+ // File may have been removed between readdir and stat — ignore
113
+ }
114
+ }
115
+ } catch {
116
+ // Non-critical — don't let cleanup failures break output
117
+ }
118
+ }
119
+
120
+ function output(result, raw, rawValue) {
121
+ let data;
122
+ if (raw && rawValue !== undefined) {
123
+ data = String(rawValue);
124
+ } else {
125
+ const json = JSON.stringify(result, null, 2);
126
+ // Large payloads exceed Claude Code's Bash tool buffer (~50KB).
127
+ // Write to tmpfile and output the path prefixed with @file: so callers can detect it.
128
+ if (json.length > 50000) {
129
+ reapStaleTempFiles();
130
+ ensureGsdTempDir();
131
+ const tmpPath = path.join(GSD_TEMP_DIR, `gsd-${Date.now()}.json`);
132
+ platformWriteSync(tmpPath, json);
133
+ data = '@file:' + tmpPath;
134
+ } else {
135
+ data = json;
136
+ }
137
+ }
138
+ // process.stdout.write() is async when stdout is a pipe — process.exit()
139
+ // can tear down the process before the reader consumes the buffer.
140
+ // fs.writeSync(1, ...) blocks until the kernel accepts the bytes, and
141
+ // skipping process.exit() lets the event loop drain naturally.
142
+ fs.writeSync(1, data);
143
+ }
144
+
145
+ /**
146
+ * Frozen enum of typed reason codes used by error() for structured errors.
147
+ * Each subcommand contributes its own codes; the enum exists so tests can
148
+ * assert against typed values instead of grepping stderr (#2974).
149
+ *
150
+ * Adding a new code:
151
+ * - Pick a snake_case lowercase value (the JSON wire form)
152
+ * - Group by subsystem prefix (CONFIG_*, SDK_*, etc)
153
+ * - Pass it to error(msg, ERROR_REASON.NEW_CODE) at the call site
154
+ */
155
+ const ERROR_REASON = Object.freeze({
156
+ // config-get / config-set
157
+ CONFIG_KEY_NOT_FOUND: 'config_key_not_found',
158
+ CONFIG_NO_FILE: 'config_no_file',
159
+ CONFIG_PARSE_FAILED: 'config_parse_failed',
160
+ CONFIG_INVALID_KEY: 'config_invalid_key',
161
+ // SDK / gsd-tools dispatch
162
+ SDK_FAIL_FAST: 'sdk_fail_fast',
163
+ SDK_UNKNOWN_COMMAND: 'sdk_unknown_command',
164
+ SDK_MISSING_ARG: 'sdk_missing_arg',
165
+ // workflow / phase
166
+ PHASE_NOT_FOUND: 'phase_not_found',
167
+ SUMMARY_NO_PLANNING: 'summary_no_planning',
168
+ // graphify
169
+ GRAPHIFY_NO_GRAPH: 'graphify_no_graph',
170
+ GRAPHIFY_INVALID_QUERY: 'graphify_invalid_query',
171
+ // hooks
172
+ HOOKS_OPT_OUT: 'hooks_opt_out',
173
+ // security-scan
174
+ SECURITY_SCAN_FAILED: 'security_scan_failed',
175
+ // generic
176
+ USAGE: 'usage',
177
+ UNKNOWN: 'unknown',
178
+ });
179
+
180
+ /**
181
+ * Process-level flag: when true, error() emits structured JSON to stderr
182
+ * instead of plain "Error: <message>" text. Set by gsd-tools.cjs when the
183
+ * CLI is invoked with `--json-errors`. Tests opt in to typed-IR error
184
+ * assertions by passing that flag and parsing the JSON.
185
+ *
186
+ * Default off so existing callers and human operators keep their plain-text
187
+ * diagnostics. The structured form is opt-in for tooling and tests (#2974).
188
+ */
189
+ let _jsonErrorMode = false;
190
+ function setJsonErrorMode(v) { _jsonErrorMode = !!v; }
191
+ function getJsonErrorMode() { return _jsonErrorMode; }
192
+
193
+ /**
194
+ * Emit an error and exit. When the second argument is provided it must be
195
+ * a value from ERROR_REASON; tests can assert on `result.reason`. When the
196
+ * process is in JSON-error mode, stderr receives `{ ok: false, reason,
197
+ * message }` so callers can parse it; otherwise stderr keeps the plain
198
+ * text form for human operators.
199
+ */
200
+ function error(message, reason = ERROR_REASON.UNKNOWN) {
201
+ if (_jsonErrorMode) {
202
+ const payload = JSON.stringify({ ok: false, reason, message }) + '\n';
203
+ fs.writeSync(2, payload);
204
+ } else {
205
+ fs.writeSync(2, 'Error: ' + message + '\n');
206
+ }
207
+ process.exit(1);
208
+ }
209
+
210
+ // ─── File & Config utilities ──────────────────────────────────────────────────
211
+
212
+ /**
213
+ * Canonical config defaults — flat-key projection for CJS consumers.
214
+ *
215
+ * Cycle 4: Values are sourced from CANONICAL_CONFIG_DEFAULTS (the nested
216
+ * manifest loaded by configuration.generated.cjs). The flat shape is
217
+ * preserved here so legacy consumers (config.cjs, verify.cjs, tests that
218
+ * regex-parse this source) continue to work without changes. The key names
219
+ * and the `const CONFIG_DEFAULTS = {` pattern are intentionally kept.
220
+ *
221
+ * Mapping notes:
222
+ * - workflow.plan_check → plan_checker (CJS flat name; verify.cjs uses this)
223
+ * - git.* → flat git keys (branching_strategy, templates)
224
+ * - workflow.* → flat names (research, verifier, …)
225
+ * - planning.sub_repos → sub_repos
226
+ * - planning.commit_docs / search_gitignored → top-level flat keys
227
+ */
228
+ const CONFIG_DEFAULTS = {
229
+ model_profile: CANONICAL_CONFIG_DEFAULTS.model_profile,
230
+ commit_docs: CANONICAL_CONFIG_DEFAULTS.commit_docs,
231
+ search_gitignored: CANONICAL_CONFIG_DEFAULTS.search_gitignored,
232
+ branching_strategy: CANONICAL_CONFIG_DEFAULTS.git.branching_strategy,
233
+ phase_branch_template: CANONICAL_CONFIG_DEFAULTS.git.phase_branch_template,
234
+ milestone_branch_template: CANONICAL_CONFIG_DEFAULTS.git.milestone_branch_template,
235
+ quick_branch_template: CANONICAL_CONFIG_DEFAULTS.git.quick_branch_template,
236
+ research: CANONICAL_CONFIG_DEFAULTS.workflow.research,
237
+ plan_checker: CANONICAL_CONFIG_DEFAULTS.workflow.plan_check, // flat CJS name maps to workflow.plan_check
238
+ verifier: CANONICAL_CONFIG_DEFAULTS.workflow.verifier,
239
+ nyquist_validation: CANONICAL_CONFIG_DEFAULTS.workflow.nyquist_validation,
240
+ ai_integration_phase: CANONICAL_CONFIG_DEFAULTS.workflow.ai_integration_phase,
241
+ parallelization: CANONICAL_CONFIG_DEFAULTS.parallelization,
242
+ brave_search: CANONICAL_CONFIG_DEFAULTS.brave_search,
243
+ firecrawl: CANONICAL_CONFIG_DEFAULTS.firecrawl,
244
+ exa_search: CANONICAL_CONFIG_DEFAULTS.exa_search,
245
+ text_mode: CANONICAL_CONFIG_DEFAULTS.workflow.text_mode,
246
+ sub_repos: CANONICAL_CONFIG_DEFAULTS.planning.sub_repos,
247
+ resolve_model_ids: CANONICAL_CONFIG_DEFAULTS.resolve_model_ids,
248
+ context_window: CANONICAL_CONFIG_DEFAULTS.context_window,
249
+ phase_naming: CANONICAL_CONFIG_DEFAULTS.phase_naming,
250
+ project_code: CANONICAL_CONFIG_DEFAULTS.project_code,
251
+ subagent_timeout: CANONICAL_CONFIG_DEFAULTS.workflow.subagent_timeout,
252
+ security_enforcement: CANONICAL_CONFIG_DEFAULTS.workflow.security_enforcement,
253
+ security_asvs_level: CANONICAL_CONFIG_DEFAULTS.workflow.security_asvs_level,
254
+ security_block_on: CANONICAL_CONFIG_DEFAULTS.workflow.security_block_on,
255
+ post_planning_gaps: CANONICAL_CONFIG_DEFAULTS.workflow.post_planning_gaps,
256
+ };
257
+
258
+ /**
259
+ * Deep-merge two plain config objects. `overlay` wins on key conflict.
260
+ * Explicit `null` in overlay overrides base (null means "unset this key").
261
+ * Arrays are replaced, not merged. Non-object primitives use overlay value.
262
+ *
263
+ * Note: `undefined` in overlay is treated as "no value provided" and falls
264
+ * back to base (preserves inheritance). Explicit `null` overrides base.
265
+ */
266
+ function _deepMergeConfig(base, overlay) {
267
+ if (overlay === null || overlay === undefined) return overlay;
268
+ if (typeof base !== 'object' || typeof overlay !== 'object') return overlay;
269
+ const result = { ...base };
270
+ for (const key of Object.keys(overlay)) {
271
+ if (overlay[key] !== null && typeof overlay[key] === 'object' && !Array.isArray(overlay[key])) {
272
+ result[key] = _deepMergeConfig(base[key] ?? {}, overlay[key]);
273
+ } else {
274
+ result[key] = overlay[key];
275
+ }
276
+ }
277
+ return result;
278
+ }
279
+
280
+ function loadConfig(cwd, options = {}) {
281
+ const activeWorkstream = Object.prototype.hasOwnProperty.call(options, 'workstream')
282
+ ? options.workstream
283
+ : (process.env.GSD_WORKSTREAM || null);
284
+ // When GSD_WORKSTREAM is set, load root config first so workstream config
285
+ // can inherit from it. This prevents users from duplicating model_overrides,
286
+ // workflow.*, etc. across every workstream config (#2714).
287
+ const ws = activeWorkstream;
288
+ let rootParsed = null;
289
+ if (ws) {
290
+ const rootConfigPath = path.join(planningRoot(cwd), 'config.json');
291
+ try {
292
+ const raw = platformReadSync(rootConfigPath);
293
+ if (raw === null) throw new Error('missing');
294
+ rootParsed = JSON.parse(raw);
295
+ // Cycle 4: delegate all legacy-key normalization to the Configuration Module.
296
+ // normalizeLegacyKeys handles branching_strategy → git.branching_strategy,
297
+ // sub_repos → planning.sub_repos, multiRepo, and depth → granularity.
298
+ const { parsed: rootNormalized, normalizations: rootNorms } = normalizeLegacyKeys(rootParsed);
299
+ if (rootNorms.length > 0) {
300
+ // Resolve filesystem-dependent normalizations (multiRepo → planning.sub_repos)
301
+ for (const norm of rootNorms) {
302
+ if (norm.requiresFilesystem && !rootNormalized.planning?.sub_repos) {
303
+ const detected = detectSubRepos(cwd);
304
+ if (detected.length > 0) {
305
+ if (!rootNormalized.planning) rootNormalized.planning = {};
306
+ rootNormalized.planning.sub_repos = detected;
307
+ rootNormalized.planning.commit_docs = false;
308
+ }
309
+ }
310
+ }
311
+ rootParsed = rootNormalized;
312
+ try { platformWriteSync(rootConfigPath, JSON.stringify(rootParsed, null, 2)); } catch {}
313
+ } else {
314
+ rootParsed = rootNormalized;
315
+ }
316
+ } catch {
317
+ // Root config missing or unparseable — workstream config stands alone
318
+ }
319
+ }
320
+
321
+ const configPath = path.join(planningDir(cwd, ws), 'config.json');
322
+ const defaults = CONFIG_DEFAULTS;
323
+
324
+ try {
325
+ const raw = platformReadSync(configPath);
326
+ if (raw === null) throw new Error('missing');
327
+ // `fileData` is the parsed content of the config.json file on disk — used
328
+ // for migrations and writes so we never persist merged values back to disk.
329
+ const fileData = JSON.parse(raw);
330
+
331
+ // Cycle 4: Single normalizeLegacyKeys call replaces all four inline migration
332
+ // blocks (depth→granularity, multiRepo→planning.sub_repos, sub_repos→planning.sub_repos,
333
+ // branching_strategy→git.branching_strategy). The Module is pure (no I/O); disk
334
+ // writeback is handled below with the existing platformWriteSync pattern.
335
+ // Note: migrateOnDisk from the Module is async; loadConfig is sync — so we
336
+ // call normalizeLegacyKeys inline and do the writeback at the call site.
337
+ // Per brief §4.3: "use normalizeLegacyKeys directly and do writeback inline."
338
+ let configDirty = false;
339
+ {
340
+ const { parsed: normalized, normalizations } = normalizeLegacyKeys(fileData);
341
+ if (normalizations.length > 0) {
342
+ // Merge normalized values back into fileData (mutation-in-place for legacy code below)
343
+ Object.keys(fileData).forEach(k => delete fileData[k]);
344
+ Object.assign(fileData, normalized);
345
+ configDirty = true;
346
+ // Resolve filesystem-dependent normalizations (multiRepo → planning.sub_repos).
347
+ // Guard: only populate sub_repos from filesystem if not already set by normalization
348
+ // AND the original file didn't have sub_repos already (preserve existing intent).
349
+ for (const norm of normalizations) {
350
+ if (norm.requiresFilesystem && !fileData.planning?.sub_repos) {
351
+ const detected = detectSubRepos(cwd);
352
+ if (detected.length > 0) {
353
+ if (!fileData.planning) fileData.planning = {};
354
+ fileData.planning.sub_repos = detected;
355
+ fileData.planning.commit_docs = false;
356
+ }
357
+ }
358
+ }
359
+ }
360
+ }
361
+
362
+ // Keep planning.sub_repos in sync with actual filesystem
363
+ const currentSubRepos = fileData.planning?.sub_repos || [];
364
+ if (Array.isArray(currentSubRepos) && currentSubRepos.length > 0) {
365
+ const detected = detectSubRepos(cwd);
366
+ if (detected.length > 0) {
367
+ const sorted = [...currentSubRepos].sort();
368
+ if (JSON.stringify(sorted) !== JSON.stringify(detected)) {
369
+ if (!fileData.planning) fileData.planning = {};
370
+ fileData.planning.sub_repos = detected;
371
+ configDirty = true;
372
+ }
373
+ }
374
+ }
375
+
376
+ // Persist sub_repos changes (migration or sync) — write only the on-disk
377
+ // file contents, never the merged result, to avoid polluting workstream configs.
378
+ if (configDirty) {
379
+ try { platformWriteSync(configPath, JSON.stringify(fileData, null, 2)); } catch {}
380
+ }
381
+
382
+ // Now apply root→workstream inheritance. `parsed` is the effective config
383
+ // used for value extraction below; fileData is kept for disk writes only.
384
+ const parsed = rootParsed ? _deepMergeConfig(rootParsed, fileData) : fileData;
385
+
386
+ // Warn about unrecognized top-level keys so users don't silently lose config.
387
+ // Derived from config-set's VALID_CONFIG_KEYS (canonical source) plus internal-only
388
+ // keys that loadConfig handles but config-set doesn't expose. This avoids maintaining
389
+ // a hardcoded duplicate that drifts when new config keys are added.
390
+ // DYNAMIC_KEY_PATTERNS supplies topLevel for each pattern so adding a new
391
+ // dynamic-pattern namespace to config-schema.cjs automatically updates this set
392
+ // — no more drift between the read side and the write side (#2687).
393
+ const { VALID_CONFIG_KEYS, DYNAMIC_KEY_PATTERNS } = require('./config-schema.cjs');
394
+ const KNOWN_TOP_LEVEL = new Set([
395
+ // Extract top-level key names from dot-notation paths (e.g., 'workflow.research' → 'workflow')
396
+ ...[...VALID_CONFIG_KEYS].map(k => k.split('.')[0]),
397
+ // Dynamic-pattern top-level containers (e.g. review, model_profile_overrides)
398
+ ...DYNAMIC_KEY_PATTERNS.map(p => p.topLevel),
399
+ // Internal keys loadConfig reads but config-set doesn't expose
400
+ 'model_overrides', 'context_window', 'resolve_model_ids', 'claude_md_path',
401
+ // Deprecated keys (still accepted for migration, not in config-set)
402
+ // 'branching_strategy' is kept here as a safety net: it is migrated to
403
+ // git.branching_strategy above (#3523), but on the first read of a root
404
+ // config that feeds into a workstream merge, `parsed` may still surface it.
405
+ 'depth', 'multiRepo', 'branching_strategy',
406
+ ]);
407
+ const unknownKeys = Object.keys(parsed).filter(k => !KNOWN_TOP_LEVEL.has(k));
408
+ if (unknownKeys.length > 0) {
409
+ // Deduplicate: a single `init phase-op N` invocation calls loadConfig twice
410
+ // (once for the sub-command setup, once for git-config resolution). Guard with
411
+ // a module-level Set so the same message never fires more than once per process.
412
+ const warnKey = unknownKeys.join(',');
413
+ if (!_warnedUnknownConfigKeys.has(warnKey)) {
414
+ _warnedUnknownConfigKeys.add(warnKey);
415
+ process.stderr.write(
416
+ `gsd-tools: warning: unknown config key(s) in .planning/config.json: ${unknownKeys.join(', ')} — these will be ignored\n`
417
+ );
418
+ }
419
+ }
420
+
421
+ // #2517 — Validate runtime/tier values for keys that loadConfig handles but
422
+ // can be edited directly into config.json (bypassing config-set's enum check).
423
+ // This catches typos like `runtime: "codx"` and `model_profile_overrides.codex.banana`
424
+ // at read time without rejecting back-compat values from new runtimes
425
+ // (review findings #10, #13).
426
+ _warnUnknownProfileOverrides(parsed, '.planning/config.json');
427
+
428
+ const get = (key, nested) => {
429
+ if (parsed[key] !== undefined) return parsed[key];
430
+ if (nested && parsed[nested.section] && parsed[nested.section][nested.field] !== undefined) {
431
+ return parsed[nested.section][nested.field];
432
+ }
433
+ return undefined;
434
+ };
435
+
436
+ const parallelization = (() => {
437
+ const val = get('parallelization');
438
+ if (typeof val === 'boolean') return val;
439
+ if (typeof val === 'object' && val !== null && 'enabled' in val) return val.enabled;
440
+ return defaults.parallelization;
441
+ })();
442
+
443
+ return {
444
+ model_profile: get('model_profile') ?? defaults.model_profile,
445
+ commit_docs: (() => {
446
+ const explicit = get('commit_docs', { section: 'planning', field: 'commit_docs' });
447
+ // If explicitly set in config, respect the user's choice
448
+ if (explicit !== undefined) return explicit;
449
+ // Auto-detection: when no explicit value and .planning/ is gitignored,
450
+ // default to false instead of true
451
+ if (isGitIgnored(cwd, '.planning/')) return false;
452
+ return defaults.commit_docs;
453
+ })(),
454
+ search_gitignored: get('search_gitignored', { section: 'planning', field: 'search_gitignored' }) ?? defaults.search_gitignored,
455
+ branching_strategy: get('branching_strategy', { section: 'git', field: 'branching_strategy' }) ?? defaults.branching_strategy,
456
+ phase_branch_template: get('phase_branch_template', { section: 'git', field: 'phase_branch_template' }) ?? defaults.phase_branch_template,
457
+ milestone_branch_template: get('milestone_branch_template', { section: 'git', field: 'milestone_branch_template' }) ?? defaults.milestone_branch_template,
458
+ quick_branch_template: get('quick_branch_template', { section: 'git', field: 'quick_branch_template' }) ?? defaults.quick_branch_template,
459
+ research: get('research', { section: 'workflow', field: 'research' }) ?? defaults.research,
460
+ plan_checker: get('plan_checker', { section: 'workflow', field: 'plan_check' }) ?? defaults.plan_checker,
461
+ verifier: get('verifier', { section: 'workflow', field: 'verifier' }) ?? defaults.verifier,
462
+ nyquist_validation: get('nyquist_validation', { section: 'workflow', field: 'nyquist_validation' }) ?? defaults.nyquist_validation,
463
+ post_planning_gaps: get('post_planning_gaps', { section: 'workflow', field: 'post_planning_gaps' }) ?? defaults.post_planning_gaps,
464
+ parallelization,
465
+ brave_search: get('brave_search') ?? defaults.brave_search,
466
+ firecrawl: get('firecrawl') ?? defaults.firecrawl,
467
+ exa_search: get('exa_search') ?? defaults.exa_search,
468
+ tdd_mode: get('tdd_mode', { section: 'workflow', field: 'tdd_mode' }) ?? false,
469
+ mvp_mode: get('mvp_mode', { section: 'workflow', field: 'mvp_mode' }) ?? false,
470
+ text_mode: get('text_mode', { section: 'workflow', field: 'text_mode' }) ?? defaults.text_mode,
471
+ auto_advance: get('auto_advance', { section: 'workflow', field: 'auto_advance' }) ?? false,
472
+ _auto_chain_active: get('_auto_chain_active', { section: 'workflow', field: '_auto_chain_active' }) ?? false,
473
+ mode: get('mode') ?? 'interactive',
474
+ sub_repos: get('sub_repos', { section: 'planning', field: 'sub_repos' }) ?? defaults.sub_repos,
475
+ resolve_model_ids: get('resolve_model_ids') ?? defaults.resolve_model_ids,
476
+ context_window: get('context_window') ?? defaults.context_window,
477
+ phase_naming: get('phase_naming') ?? defaults.phase_naming,
478
+ project_code: get('project_code') ?? defaults.project_code,
479
+ subagent_timeout: get('subagent_timeout', { section: 'workflow', field: 'subagent_timeout' }) ?? defaults.subagent_timeout,
480
+ model_overrides: parsed.model_overrides || null,
481
+ // #3023 — per-phase-type model map. Six named slots
482
+ // (planning/discuss/research/execution/verification/completion).
483
+ // Resolves between per-agent override and profile-derived tier in
484
+ // resolveModelInternal. Defaults to null so configs without it
485
+ // behave exactly as today.
486
+ models: parsed.models || null,
487
+ // #3024 — dynamic routing block. When `enabled: true`, the
488
+ // resolveModelForTier() resolver picks tier_models[default_tier]
489
+ // for the agent and escalates one tier per attempt up to
490
+ // max_escalations. Disabled by default for backward compat.
491
+ dynamic_routing: parsed.dynamic_routing || null,
492
+ // #2517 — runtime-aware profiles. `runtime` defaults to null (back-compat).
493
+ // When null, resolveModelInternal preserves today's Claude-native behavior.
494
+ // NOTE: `runtime` and `model_profile_overrides` are intentionally read
495
+ // flat-only (not via `get()` with a workflow.X fallback) — they are
496
+ // top-level keys per docs/CONFIGURATION.md. The lighter-touch decision
497
+ // here was to document the constraint rather than introduce nested
498
+ // resolution edge cases for two new keys (review finding #9). The
499
+ // schema validation in `_warnUnknownProfileOverrides` runs against the
500
+ // raw `parsed` blob, so direct `.planning/config.json` edits surface
501
+ // unknown runtime/tier names at load time, not silently (review finding #10).
502
+ runtime: parsed.runtime || null,
503
+ model_profile_overrides: parsed.model_profile_overrides || null,
504
+ agent_skills: parsed.agent_skills || {},
505
+ manager: parsed.manager || {},
506
+ response_language: get('response_language') || null,
507
+ claude_md_path: get('claude_md_path') || null,
508
+ claude_md_assembly: parsed.claude_md_assembly || null,
509
+ };
510
+ } catch {
511
+ // Fall back to ~/.gsd/defaults.json only for truly pre-project contexts (#1683)
512
+ // If .planning/ exists, the project is initialized — just missing config.json.
513
+ // When GSD_WORKSTREAM is set and root config was loaded, the workstream config
514
+ // doesn't exist — treat root config as the effective config for this workstream.
515
+ if (fs.existsSync(planningDir(cwd, ws))) {
516
+ if (rootParsed) {
517
+ // Workstream has no config.json: re-parse using root config as the sole source.
518
+ // Keep env immutable by explicitly reloading with workstream context cleared.
519
+ return loadConfig(cwd, { workstream: null });
520
+ }
521
+ return defaults;
522
+ }
523
+ try {
524
+ const home = process.env.GSD_HOME || os.homedir();
525
+ const globalDefaultsPath = path.join(home, '.gsd', 'defaults.json');
526
+ const raw = platformReadSync(globalDefaultsPath);
527
+ if (raw === null) throw new Error('missing');
528
+ const globalDefaults = JSON.parse(raw);
529
+ return {
530
+ ...defaults,
531
+ model_profile: globalDefaults.model_profile ?? defaults.model_profile,
532
+ commit_docs: globalDefaults.commit_docs ?? defaults.commit_docs,
533
+ research: globalDefaults.research ?? defaults.research,
534
+ plan_checker: globalDefaults.plan_checker ?? defaults.plan_checker,
535
+ verifier: globalDefaults.verifier ?? defaults.verifier,
536
+ nyquist_validation: globalDefaults.nyquist_validation ?? defaults.nyquist_validation,
537
+ post_planning_gaps: globalDefaults.post_planning_gaps
538
+ ?? globalDefaults.workflow?.post_planning_gaps
539
+ ?? defaults.post_planning_gaps,
540
+ parallelization: globalDefaults.parallelization ?? defaults.parallelization,
541
+ text_mode: globalDefaults.text_mode ?? defaults.text_mode,
542
+ resolve_model_ids: globalDefaults.resolve_model_ids ?? defaults.resolve_model_ids,
543
+ context_window: globalDefaults.context_window ?? defaults.context_window,
544
+ subagent_timeout: globalDefaults.subagent_timeout ?? defaults.subagent_timeout,
545
+ model_overrides: globalDefaults.model_overrides || null,
546
+ models: globalDefaults.models || null,
547
+ dynamic_routing: globalDefaults.dynamic_routing || null,
548
+ agent_skills: globalDefaults.agent_skills || {},
549
+ response_language: globalDefaults.response_language || null,
550
+ };
551
+ } catch {
552
+ return defaults;
553
+ }
554
+ }
555
+ }
556
+
557
+ // ─── Git utilities ────────────────────────────────────────────────────────────
558
+
559
+ // Module-level deduplication for unknown-key warnings (#3523).
560
+ // A single `init phase-op N` call invokes loadConfig more than once; this Set
561
+ // prevents the same warning from being echoed on each invocation.
562
+ const _warnedUnknownConfigKeys = new Set();
563
+
564
+ const _gitIgnoredCache = new Map();
565
+
566
+ function isGitIgnored(cwd, targetPath) {
567
+ const key = cwd + '::' + targetPath;
568
+ if (_gitIgnoredCache.has(key)) return _gitIgnoredCache.get(key);
569
+ // --no-index checks .gitignore rules regardless of whether the file is tracked.
570
+ // Without it, git check-ignore returns "not ignored" for tracked files even when
571
+ // .gitignore explicitly lists them — a common source of confusion when .planning/
572
+ // was committed before being added to .gitignore.
573
+ // Array args (via the seam) prevent shell interpretation of special characters in
574
+ // file paths — avoids command injection via crafted path names.
575
+ const result = execGit(['check-ignore', '-q', '--no-index', '--', targetPath], { cwd });
576
+ const ignored = result.exitCode === 0;
577
+ _gitIgnoredCache.set(key, ignored);
578
+ return ignored;
579
+ }
580
+
581
+ // ─── Common path helpers ──────────────────────────────────────────────────────
582
+
583
+ /**
584
+ * Resolve the main worktree root when running inside a git worktree.
585
+ * In a linked worktree, .planning/ lives in the main worktree, not in the linked one.
586
+ * Returns the main worktree path, or cwd if not in a worktree.
587
+ */
588
+ function resolveWorktreeRoot(cwd) {
589
+ // Omit execGit so worktree-safety uses its own execGitDefault — that wrapper
590
+ // delegates to the seam and derives the `timedOut` field that pruneResult
591
+ // branches on below.
592
+ const context = resolveWorktreeContext(cwd, {
593
+ existsSync: fs.existsSync,
594
+ });
595
+ return context.effectiveRoot;
596
+ }
597
+
598
+ /**
599
+ * Parse `git worktree list --porcelain` output into an array of
600
+ * { path, branch } objects. Entries with a detached HEAD (no branch line)
601
+ * are skipped because we cannot safely reason about their merge status.
602
+ *
603
+ * @param {string} porcelain - raw output from git worktree list --porcelain
604
+ * @returns {{ path: string, branch: string }[]}
605
+ */
606
+ function parseWorktreePorcelain(porcelain) {
607
+ return parseWorktreePorcelainPolicy(porcelain);
608
+ }
609
+
610
+ /**
611
+ * Clear stale worktree metadata references via `git worktree prune`.
612
+ *
613
+ * Destructive linked-worktree removal is disabled by default for safety.
614
+ *
615
+ * @param {string} repoRoot - absolute path to the main (or any) worktree of
616
+ * the repository; used as `cwd` for git commands.
617
+ * @returns {string[]} list of worktree paths that were removed (always empty)
618
+ */
619
+ function pruneOrphanedWorktrees(repoRoot) {
620
+ try {
621
+ const plan = planWorktreePrune(
622
+ repoRoot,
623
+ { allowDestructive: false },
624
+ { parseWorktreePorcelain }
625
+ );
626
+ const pruneResult = executeWorktreePrunePlan(plan);
627
+ if (pruneResult && pruneResult.timedOut) {
628
+ // AC2: surface structured warning instead of silently swallowing the timeout.
629
+ // Uses process.stderr.write to match the [gsd-tools] WARNING prefix style.
630
+ process.stderr.write(
631
+ '[gsd-tools] WARNING: worktree health check degraded' +
632
+ ' — git worktree prune timed out after 10s.' +
633
+ ' Orphaned worktree metadata may remain until the next successful run.\n'
634
+ );
635
+ }
636
+ } catch { /* never crash the caller */ }
637
+ return [];
638
+ }
639
+
640
+ // ─── Planning workspace (pathing + active workstream + lock) moved to planning-workspace.cjs ───
641
+
642
+ // ─── Phase utilities ──────────────────────────────────────────────────────────
643
+
644
+ function escapeRegex(value) {
645
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
646
+ }
647
+
648
+ function normalizePhaseName(phase) {
649
+ const str = String(phase);
650
+ // Strip optional project_code prefix (e.g., 'CK-01' → '01')
651
+ const stripped = str.replace(/^[A-Z]{1,6}-(?=\d)/, '');
652
+ // Standard numeric phases: 1, 01, 12A, 12.1
653
+ const match = stripped.match(/^(\d+)([A-Z])?((?:\.\d+)*)/i);
654
+ if (match) {
655
+ const padded = match[1].padStart(2, '0');
656
+ // Preserve original case of letter suffix (#1962).
657
+ // Uppercasing causes directory/roadmap mismatches on case-sensitive filesystems
658
+ // (e.g., "16c" in ROADMAP.md → directory "16C-name" → progress can't match).
659
+ const letter = match[2] || '';
660
+ const decimal = match[3] || '';
661
+ return padded + letter + decimal;
662
+ }
663
+ // Custom phase IDs (e.g. PROJ-42, AUTH-101): return as-is
664
+ return str;
665
+ }
666
+
667
+ /**
668
+ * Render a regex source fragment matching a phase number against ROADMAP/STATE
669
+ * prose regardless of zero-padding on either side. Skills pass the resolved
670
+ * padded form (`02.7`), but human-authored ROADMAP prose is conventionally
671
+ * un-padded (`### Phase 2.7:`); a naive `escapeRegex(phaseNum)` fragment never
672
+ * matches when the two diverge. Strips leading zeros from the integer part
673
+ * before re-emitting with a `0*` prefix, so the fragment matches both `2.7`
674
+ * and `02.7` (and `002.7`).
675
+ *
676
+ * Falls back to `escapeRegex(phaseNum)` for non-numeric IDs (custom project
677
+ * codes like `PROJ-42`) so callers can substitute it unconditionally.
678
+ *
679
+ * See #3537 — wired into every ROADMAP-prose regex builder.
680
+ */
681
+ function phaseMarkdownRegexSource(phaseNum) {
682
+ const stripped = String(phaseNum).replace(/^[A-Z]{1,6}-(?=\d)/i, '');
683
+ const match = stripped.match(/^0*(\d+)([A-Z])?((?:\.\d+)*)$/i);
684
+ if (!match) return escapeRegex(phaseNum);
685
+
686
+ const integer = match[1].replace(/^0+/, '') || '0';
687
+ const letter = match[2] ? escapeRegex(match[2]) : '';
688
+ const decimal = match[3] ? escapeRegex(match[3]) : '';
689
+ return `0*${escapeRegex(integer)}${letter}${decimal}`;
690
+ }
691
+
692
+ /**
693
+ * #3599: when the caller passed a project-code-prefixed ID like `PROJ-42`,
694
+ * return the exact-escaped form so the caller can search the ROADMAP for
695
+ * `### Phase PROJ-42:` BEFORE falling back to the padding-tolerant numeric
696
+ * form. Returns null when the input has no project-code prefix — in that
697
+ * case the numeric form (`phaseMarkdownRegexSource`) is the only thing the
698
+ * caller needs.
699
+ *
700
+ * Two-pass at the call site preserves the #3537 contract (`CK-01` directory
701
+ * names mapping to `Phase 1:` prose) while letting `PROJ-42` resolve to its
702
+ * own prefixed heading without cross-matching a bare `### Phase 42:` that
703
+ * happens to share the trailing integer.
704
+ */
705
+ function phaseMarkdownRegexSourceExact(phaseNum) {
706
+ const raw = String(phaseNum);
707
+ if (!/^[A-Z]{1,6}-(?=\d)/i.test(raw)) return null;
708
+ return escapeRegex(raw);
709
+ }
710
+
711
+ function comparePhaseNum(a, b) {
712
+ // Strip optional project_code prefix before comparing (e.g., 'CK-01-name' → '01-name')
713
+ const sa = String(a).replace(/^[A-Z]{1,6}-/, '');
714
+ const sb = String(b).replace(/^[A-Z]{1,6}-/, '');
715
+ const pa = sa.match(/^(\d+)([A-Z])?((?:\.\d+)*)/i);
716
+ const pb = sb.match(/^(\d+)([A-Z])?((?:\.\d+)*)/i);
717
+ // If either is non-numeric (custom ID), fall back to string comparison
718
+ if (!pa || !pb) return String(a).localeCompare(String(b));
719
+ const intDiff = parseInt(pa[1], 10) - parseInt(pb[1], 10);
720
+ if (intDiff !== 0) return intDiff;
721
+ // No letter sorts before letter: 12 < 12A < 12B
722
+ const la = (pa[2] || '').toUpperCase();
723
+ const lb = (pb[2] || '').toUpperCase();
724
+ if (la !== lb) {
725
+ if (!la) return -1;
726
+ if (!lb) return 1;
727
+ return la < lb ? -1 : 1;
728
+ }
729
+ // Segment-by-segment decimal comparison: 12A < 12A.1 < 12A.1.2 < 12A.2
730
+ const aDecParts = pa[3] ? pa[3].slice(1).split('.').map(p => parseInt(p, 10)) : [];
731
+ const bDecParts = pb[3] ? pb[3].slice(1).split('.').map(p => parseInt(p, 10)) : [];
732
+ const maxLen = Math.max(aDecParts.length, bDecParts.length);
733
+ if (aDecParts.length === 0 && bDecParts.length > 0) return -1;
734
+ if (bDecParts.length === 0 && aDecParts.length > 0) return 1;
735
+ for (let i = 0; i < maxLen; i++) {
736
+ const av = Number.isFinite(aDecParts[i]) ? aDecParts[i] : 0;
737
+ const bv = Number.isFinite(bDecParts[i]) ? bDecParts[i] : 0;
738
+ if (av !== bv) return av - bv;
739
+ }
740
+ return 0;
741
+ }
742
+
743
+ /**
744
+ * Extract the phase token from a directory name.
745
+ * Supports: '01-name', '1009A-name', '999.6-name', 'CK-01-name', 'PROJ-42-name'.
746
+ * Returns the token portion (e.g. '01', '1009A', '999.6', 'PROJ-42') or the full name if no separator.
747
+ */
748
+ function extractPhaseToken(dirName) {
749
+ // Try project-code-prefixed numeric: CK-01-name → CK-01, CK-01A.2-name → CK-01A.2
750
+ const codePrefixed = dirName.match(/^([A-Z]{1,6}-\d+[A-Z]?(?:\.\d+)*)(?:-|$)/i);
751
+ if (codePrefixed) return codePrefixed[1];
752
+ // Try plain numeric: 01-name, 1009A-name, 999.6-name
753
+ const numeric = dirName.match(/^(\d+[A-Z]?(?:\.\d+)*)(?:-|$)/i);
754
+ if (numeric) return numeric[1];
755
+ // Custom IDs: PROJ-42-name → everything before the last segment that looks like a name
756
+ const custom = dirName.match(/^([A-Z][A-Z0-9]*(?:-[A-Z0-9]+)*)(?:-[a-z]|$)/i);
757
+ if (custom) return custom[1];
758
+ return dirName;
759
+ }
760
+
761
+ /**
762
+ * Check if a directory name's phase token matches the normalized phase exactly.
763
+ * Case-insensitive comparison for the token portion.
764
+ */
765
+ function phaseTokenMatches(dirName, normalized) {
766
+ const token = extractPhaseToken(dirName);
767
+ if (token.toUpperCase() === normalized.toUpperCase()) return true;
768
+ // Strip optional project_code prefix from dir and retry
769
+ const stripped = dirName.replace(/^[A-Z]{1,6}-(?=\d)/i, '');
770
+ if (stripped !== dirName) {
771
+ const strippedToken = extractPhaseToken(stripped);
772
+ if (strippedToken.toUpperCase() === normalized.toUpperCase()) return true;
773
+ }
774
+ return false;
775
+ }
776
+
777
+ function extractCanonicalPlanId(filename) {
778
+ const base = filename.replace(/-PLAN\.md$/i, '').replace(/-SUMMARY\.md$/i, '').replace(/\.md$/i, '');
779
+ const parts = base.split('-').filter(Boolean);
780
+ const tokenRe = /^\d+[A-Z]?(?:\.\d+)*$/i;
781
+ const phaseIdx = parts.findIndex(p => tokenRe.test(p));
782
+ if (phaseIdx >= 0 && phaseIdx + 1 < parts.length && tokenRe.test(parts[phaseIdx + 1])) {
783
+ return `${parts[phaseIdx]}-${parts[phaseIdx + 1]}`;
784
+ }
785
+ return base;
786
+ }
787
+
788
+ function searchPhaseInDir(baseDir, relBase, normalized) {
789
+ try {
790
+ const dirs = readSubdirectories(baseDir, true);
791
+ // Match: exact phase token comparison (not prefix matching)
792
+ const match = dirs.find(d => phaseTokenMatches(d, normalized));
793
+ if (!match) return null;
794
+
795
+ // Extract phase number and name — supports numeric (01-name), project-code-prefixed (CK-01-name), and custom (PROJ-42-name)
796
+ const dirMatch = match.match(/^(?:[A-Z]{1,6}-)(\d+[A-Z]?(?:\.\d+)*)-?(.*)/i)
797
+ || match.match(/^(\d+[A-Z]?(?:\.\d+)*)-?(.*)/i)
798
+ || match.match(/^([A-Z][A-Z0-9]*(?:-[A-Z0-9]+)*)-(.+)/i)
799
+ || [null, match, null];
800
+ const phaseNumber = dirMatch ? dirMatch[1] : normalized;
801
+ const phaseName = dirMatch && dirMatch[2] ? dirMatch[2] : null;
802
+ const phaseDir = path.join(baseDir, match);
803
+ const { plans: unsortedPlans, summaries: unsortedSummaries, hasResearch, hasContext, hasVerification, hasReviews } = getPhaseFileStats(phaseDir);
804
+ const plans = unsortedPlans.sort();
805
+ const summaries = unsortedSummaries.sort();
806
+
807
+ const completedPlanIds = new Set(
808
+ summaries.flatMap(s => {
809
+ const exact = s.replace('-SUMMARY.md', '').replace('SUMMARY.md', '');
810
+ const canonical = extractCanonicalPlanId(s);
811
+ return canonical === exact ? [exact] : [exact, canonical];
812
+ })
813
+ );
814
+ const incompletePlans = plans.filter(p => {
815
+ const planId = p.replace('-PLAN.md', '').replace('PLAN.md', '');
816
+ const canonical = extractCanonicalPlanId(p);
817
+ return !completedPlanIds.has(planId) && !completedPlanIds.has(canonical);
818
+ });
819
+
820
+ return {
821
+ found: true,
822
+ directory: toPosixPath(path.join(relBase, match)),
823
+ phase_number: phaseNumber,
824
+ phase_name: phaseName,
825
+ phase_slug: phaseName ? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') : null,
826
+ plans,
827
+ summaries,
828
+ incomplete_plans: incompletePlans,
829
+ has_research: hasResearch,
830
+ has_context: hasContext,
831
+ has_verification: hasVerification,
832
+ has_reviews: hasReviews,
833
+ };
834
+ } catch {
835
+ return null;
836
+ }
837
+ }
838
+
839
+ function findPhaseInternal(cwd, phase) {
840
+ if (!phase) return null;
841
+
842
+ const phasesDir = path.join(planningDir(cwd), 'phases');
843
+ const normalized = normalizePhaseName(phase);
844
+
845
+ // Search current phases first
846
+ const relPhasesDir = toPosixPath(path.relative(cwd, phasesDir));
847
+ const current = searchPhaseInDir(phasesDir, relPhasesDir, normalized);
848
+ if (current) return current;
849
+
850
+ // Search archived milestone phases (newest first)
851
+ const milestonesDir = path.join(cwd, '.planning', 'milestones');
852
+ if (!fs.existsSync(milestonesDir)) return null;
853
+
854
+ try {
855
+ const milestoneEntries = fs.readdirSync(milestonesDir, { withFileTypes: true });
856
+ const archiveDirs = milestoneEntries
857
+ .filter(e => e.isDirectory() && /^v[\d.]+-phases$/.test(e.name))
858
+ .map(e => e.name)
859
+ .sort()
860
+ .reverse();
861
+
862
+ for (const archiveName of archiveDirs) {
863
+ const version = archiveName.match(/^(v[\d.]+)-phases$/)[1];
864
+ const archivePath = path.join(milestonesDir, archiveName);
865
+ const relBase = '.planning/milestones/' + archiveName;
866
+ const result = searchPhaseInDir(archivePath, relBase, normalized);
867
+ if (result) {
868
+ result.archived = version;
869
+ return result;
870
+ }
871
+ }
872
+ } catch { /* intentionally empty */ }
873
+
874
+ return null;
875
+ }
876
+
877
+ function getArchivedPhaseDirs(cwd) {
878
+ const milestonesDir = path.join(cwd, '.planning', 'milestones');
879
+ const results = [];
880
+
881
+ if (!fs.existsSync(milestonesDir)) return results;
882
+
883
+ try {
884
+ const milestoneEntries = fs.readdirSync(milestonesDir, { withFileTypes: true });
885
+ // Find v*-phases directories, sort newest first
886
+ const phaseDirs = milestoneEntries
887
+ .filter(e => e.isDirectory() && /^v[\d.]+-phases$/.test(e.name))
888
+ .map(e => e.name)
889
+ .sort()
890
+ .reverse();
891
+
892
+ for (const archiveName of phaseDirs) {
893
+ const version = archiveName.match(/^(v[\d.]+)-phases$/)[1];
894
+ const archivePath = path.join(milestonesDir, archiveName);
895
+ const dirs = readSubdirectories(archivePath, true);
896
+
897
+ for (const dir of dirs) {
898
+ results.push({
899
+ name: dir,
900
+ milestone: version,
901
+ basePath: path.join('.planning', 'milestones', archiveName),
902
+ fullPath: path.join(archivePath, dir),
903
+ });
904
+ }
905
+ }
906
+ } catch { /* intentionally empty */ }
907
+
908
+ return results;
909
+ }
910
+
911
+ // ─── Roadmap milestone scoping ───────────────────────────────────────────────
912
+
913
+ /**
914
+ * Strip shipped milestone content wrapped in <details> blocks.
915
+ * Used to isolate current milestone phases when searching ROADMAP.md
916
+ * for phase headings or checkboxes — prevents matching archived milestone
917
+ * phases that share the same numbers as current milestone phases.
918
+ */
919
+ function stripShippedMilestones(content) {
920
+ return content.replace(/<details>[\s\S]*?<\/details>/gi, '');
921
+ }
922
+
923
+ /**
924
+ * Extract the current milestone section from ROADMAP.md by positive lookup.
925
+ *
926
+ * Instead of stripping <details> blocks (negative heuristic that breaks if
927
+ * agents wrap the current milestone in <details>), this finds the section
928
+ * matching the current milestone version and returns only that content.
929
+ *
930
+ * Falls back to stripShippedMilestones() if:
931
+ * - cwd is not provided
932
+ * - STATE.md doesn't exist or has no milestone field
933
+ * - Version can't be found in ROADMAP.md
934
+ *
935
+ * @param {string} content - Full ROADMAP.md content
936
+ * @param {string} [cwd] - Working directory for reading STATE.md
937
+ * @returns {string} Content scoped to current milestone
938
+ */
939
+ function extractCurrentMilestone(content, cwd) {
940
+ if (!cwd) return stripShippedMilestones(content);
941
+
942
+ // 1. Get current milestone version from STATE.md frontmatter
943
+ let version = null;
944
+ try {
945
+ const statePath = path.join(planningDir(cwd), 'STATE.md');
946
+ const stateRaw = platformReadSync(statePath);
947
+ if (stateRaw !== null) {
948
+ const milestoneMatch = stateRaw.match(/^milestone:\s*(.+)/m);
949
+ if (milestoneMatch) {
950
+ version = milestoneMatch[1].trim();
951
+ }
952
+ }
953
+ } catch {}
954
+
955
+ // 2. Fallback: derive version from getMilestoneInfo pattern in ROADMAP.md itself
956
+ if (!version) {
957
+ // Check for 🚧 in-progress marker
958
+ const inProgressMatch = content.match(/🚧\s*\*\*v(\d+\.\d+)\s/);
959
+ if (inProgressMatch) {
960
+ version = 'v' + inProgressMatch[1];
961
+ }
962
+ }
963
+
964
+ if (!version) return stripShippedMilestones(content);
965
+
966
+ // 3. Find the section matching this version
967
+ // Match headings like: ## Roadmap v3.0: Name, ## v3.0 Name, etc.
968
+ const escapedVersion = escapeRegex(version);
969
+ const sectionPattern = new RegExp(
970
+ `(^#{1,3}\\s+.*${escapedVersion}[^\\n]*)`,
971
+ 'mi'
972
+ );
973
+ const sectionMatch = content.match(sectionPattern);
974
+
975
+ if (!sectionMatch) return stripShippedMilestones(content);
976
+
977
+ const sectionStart = sectionMatch.index;
978
+
979
+ // Find the end: next milestone heading at same or higher level, or EOF.
980
+ // Milestone headings look like: ## v2.0, ## Roadmap v2.0, ## ✅ v1.0, etc.
981
+ // Scan line-by-line so that heading-like lines inside fenced code blocks
982
+ // (``` or ~~~) are not mistaken for milestone boundaries. See #2787.
983
+ const headingLevel = sectionMatch[1].match(/^(#{1,3})\s/)[1].length;
984
+ const restContent = content.slice(sectionStart + sectionMatch[0].length);
985
+ // Exclude phase headings (e.g. "### Phase 12: v1.0 Tech-Debt Closure") from
986
+ // being treated as milestone boundaries just because they mention vX.Y in
987
+ // the title. Phase headings always start with the literal `Phase `. See #2619.
988
+ const nextMilestonePattern = new RegExp(
989
+ `^#{1,${headingLevel}}\\s+(?!Phase\\s+\\S)(?:.*v\\d+\\.\\d+|✅|📋|🚧)`,
990
+ 'i'
991
+ );
992
+
993
+ let sectionEnd = content.length;
994
+ let fenceChar = null;
995
+ let fenceLen = 0;
996
+ let charOffset = 0;
997
+ for (const line of restContent.split('\n')) {
998
+ const fenceMatch = line.match(/^\s{0,3}((?:`{3,}|~{3,}))(.*)/);
999
+ if (fenceMatch) {
1000
+ const char = fenceMatch[1][0];
1001
+ const len = fenceMatch[1].length;
1002
+ const trailing = fenceMatch[2] || '';
1003
+ if (!fenceChar) {
1004
+ fenceChar = char;
1005
+ fenceLen = len;
1006
+ } else if (char === fenceChar && len >= fenceLen && /^\s*$/.test(trailing)) {
1007
+ fenceChar = null;
1008
+ fenceLen = 0;
1009
+ }
1010
+ } else if (!fenceChar && nextMilestonePattern.test(line)) {
1011
+ sectionEnd = sectionStart + sectionMatch[0].length + charOffset;
1012
+ break;
1013
+ }
1014
+ charOffset += line.length + 1;
1015
+ }
1016
+
1017
+ // Return everything before the current milestone section (non-milestone content
1018
+ // like title, overview) plus the current milestone section
1019
+ const beforeMilestones = content.slice(0, sectionStart);
1020
+ const currentSection = content.slice(sectionStart, sectionEnd);
1021
+
1022
+ // Also include any content before the first milestone heading (title, overview, etc.)
1023
+ // but strip any <details> blocks in it (these are definitely shipped)
1024
+ const preamble = beforeMilestones.replace(/<details>[\s\S]*?<\/details>/gi, '');
1025
+
1026
+ return preamble + currentSection;
1027
+ }
1028
+
1029
+ /**
1030
+ * Replace a pattern only in the current milestone section of ROADMAP.md
1031
+ * (everything after the last </details> close tag). Used for write operations
1032
+ * that must not accidentally modify archived milestone checkboxes/tables.
1033
+ */
1034
+ function replaceInCurrentMilestone(content, pattern, replacement) {
1035
+ const lastDetailsClose = content.lastIndexOf('</details>');
1036
+ if (lastDetailsClose === -1) {
1037
+ return content.replace(pattern, replacement);
1038
+ }
1039
+ const offset = lastDetailsClose + '</details>'.length;
1040
+ const before = content.slice(0, offset);
1041
+ const after = content.slice(offset);
1042
+ return before + after.replace(pattern, replacement);
1043
+ }
1044
+
1045
+ // ─── Roadmap & model utilities ────────────────────────────────────────────────
1046
+
1047
+ function getRoadmapPhaseInternal(cwd, phaseNum) {
1048
+ if (!phaseNum) return null;
1049
+ const roadmapPath = path.join(planningDir(cwd), 'ROADMAP.md');
1050
+ if (!fs.existsSync(roadmapPath)) return null;
1051
+
1052
+ try {
1053
+ const roadmapRaw = platformReadSync(roadmapPath);
1054
+ if (roadmapRaw === null) throw new Error('missing');
1055
+ const content = extractCurrentMilestone(roadmapRaw, cwd);
1056
+ // #3537: route through canonical padding-tolerant fragment. The prior
1057
+ // hand-rolled `isNumeric` branch only stripped padding on integer-only
1058
+ // ids and missed decimal padding (`02.7` against `Phase 2.7:` headings).
1059
+ const phasePattern = new RegExp(
1060
+ `#{2,4}\\s*Phase\\s+${phaseMarkdownRegexSource(phaseNum)}:\\s*([^\\n]+)`,
1061
+ 'i'
1062
+ );
1063
+ const headerMatch = content.match(phasePattern);
1064
+ if (!headerMatch) return null;
1065
+
1066
+ const phaseName = headerMatch[1].trim();
1067
+ const headerIndex = headerMatch.index;
1068
+ const restOfContent = content.slice(headerIndex);
1069
+ const nextHeaderMatch = restOfContent.match(/\n#{2,4}\s+Phase\s+[\w]/i);
1070
+ const sectionEnd = nextHeaderMatch ? headerIndex + nextHeaderMatch.index : content.length;
1071
+ const section = content.slice(headerIndex, sectionEnd).trim();
1072
+
1073
+ const goalMatch = section.match(/\*\*Goal(?:\*\*:|\*?\*?:\*\*)\s*([^\n]+)/i);
1074
+ const goal = goalMatch ? goalMatch[1].trim() : null;
1075
+
1076
+ return {
1077
+ found: true,
1078
+ phase_number: phaseNum.toString(),
1079
+ phase_name: phaseName,
1080
+ goal,
1081
+ section,
1082
+ };
1083
+ } catch {
1084
+ return null;
1085
+ }
1086
+ }
1087
+
1088
+ // ─── Agent installation validation (#1371) ───────────────────────────────────
1089
+
1090
+ /**
1091
+ * Resolve the agents directory from the GSD install location.
1092
+ * gsd-tools.cjs lives at <configDir>/get-shit-done/bin/gsd-tools.cjs,
1093
+ * so agents/ is at <configDir>/agents/.
1094
+ *
1095
+ * GSD_AGENTS_DIR env var overrides the default path. Used in tests and for
1096
+ * installs where the agents directory is not co-located with gsd-tools.cjs.
1097
+ *
1098
+ * @returns {string} Absolute path to the agents directory
1099
+ */
1100
+ function getAgentsDir() {
1101
+ if (process.env.GSD_AGENTS_DIR) {
1102
+ return process.env.GSD_AGENTS_DIR;
1103
+ }
1104
+ // __dirname is get-shit-done/bin/lib/ → go up 3 levels to configDir
1105
+ return path.join(__dirname, '..', '..', '..', 'agents');
1106
+ }
1107
+
1108
+ /**
1109
+ * Check which GSD agents are installed on disk.
1110
+ * Returns an object with installation status and details.
1111
+ *
1112
+ * Recognises both standard format (gsd-planner.md) and Copilot format
1113
+ * (gsd-planner.agent.md). Copilot renames agent files during install (#1512).
1114
+ *
1115
+ * @returns {{ agents_installed: boolean, missing_agents: string[], installed_agents: string[], agents_dir: string }}
1116
+ */
1117
+ function checkAgentsInstalled() {
1118
+ const agentsDir = getAgentsDir();
1119
+ const expectedAgents = Object.keys(MODEL_PROFILES);
1120
+ const installed = [];
1121
+ const missing = [];
1122
+
1123
+ if (!fs.existsSync(agentsDir)) {
1124
+ return {
1125
+ agents_installed: false,
1126
+ missing_agents: expectedAgents,
1127
+ installed_agents: [],
1128
+ agents_dir: agentsDir,
1129
+ };
1130
+ }
1131
+
1132
+ for (const agent of expectedAgents) {
1133
+ // Check both .md (standard) and .agent.md (Copilot) file formats.
1134
+ const agentFile = path.join(agentsDir, `${agent}.md`);
1135
+ const agentFileCopilot = path.join(agentsDir, `${agent}.agent.md`);
1136
+ if (fs.existsSync(agentFile) || fs.existsSync(agentFileCopilot)) {
1137
+ installed.push(agent);
1138
+ } else {
1139
+ missing.push(agent);
1140
+ }
1141
+ }
1142
+
1143
+ return {
1144
+ agents_installed: installed.length > 0 && missing.length === 0,
1145
+ missing_agents: missing,
1146
+ installed_agents: installed,
1147
+ agents_dir: agentsDir,
1148
+ };
1149
+ }
1150
+
1151
+ // ─── Model alias resolution ───────────────────────────────────────────────────
1152
+
1153
+ const RUNTIME_OVERRIDE_TIERS = new Set(['opus', 'sonnet', 'haiku']);
1154
+ const _warnedConfigKeys = new Set();
1155
+
1156
+ function _warnUnknownProfileOverrides(parsed, configLabel) {
1157
+ if (!parsed || typeof parsed !== 'object') return;
1158
+
1159
+ const runtime = parsed.runtime;
1160
+ if (runtime && typeof runtime === 'string' && !KNOWN_RUNTIMES.has(runtime)) {
1161
+ const key = `${configLabel}::runtime::${runtime}`;
1162
+ if (!_warnedConfigKeys.has(key)) {
1163
+ _warnedConfigKeys.add(key);
1164
+ try {
1165
+ process.stderr.write(
1166
+ `gsd: warning — config key "runtime" has unknown value "${runtime}". ` +
1167
+ `Known runtimes: ${[...KNOWN_RUNTIMES].sort().join(', ')}. ` +
1168
+ `Resolution will fall back to safe defaults. (#2517)\n`
1169
+ );
1170
+ } catch { /* stderr might be closed in some test harnesses */ }
1171
+ }
1172
+ }
1173
+
1174
+ const overrides = parsed.model_profile_overrides;
1175
+ if (!overrides || typeof overrides !== 'object') return;
1176
+ for (const [overrideRuntime, tierMap] of Object.entries(overrides)) {
1177
+ if (!KNOWN_RUNTIMES.has(overrideRuntime)) {
1178
+ const key = `${configLabel}::override-runtime::${overrideRuntime}`;
1179
+ if (!_warnedConfigKeys.has(key)) {
1180
+ _warnedConfigKeys.add(key);
1181
+ try {
1182
+ process.stderr.write(
1183
+ `gsd: warning — model_profile_overrides.${overrideRuntime}.* uses ` +
1184
+ `unknown runtime "${overrideRuntime}". Known runtimes: ` +
1185
+ `${[...KNOWN_RUNTIMES].sort().join(', ')}. (#2517)\n`
1186
+ );
1187
+ } catch { /* ok */ }
1188
+ }
1189
+ }
1190
+ if (!tierMap || typeof tierMap !== 'object') continue;
1191
+ for (const tierName of Object.keys(tierMap)) {
1192
+ if (!RUNTIME_OVERRIDE_TIERS.has(tierName)) {
1193
+ const key = `${configLabel}::override-tier::${overrideRuntime}.${tierName}`;
1194
+ if (!_warnedConfigKeys.has(key)) {
1195
+ _warnedConfigKeys.add(key);
1196
+ try {
1197
+ process.stderr.write(
1198
+ `gsd: warning — model_profile_overrides.${overrideRuntime}.${tierName} ` +
1199
+ `uses unknown tier "${tierName}". Allowed tiers: opus, sonnet, haiku. (#2517)\n`
1200
+ );
1201
+ } catch { /* ok */ }
1202
+ }
1203
+ }
1204
+ }
1205
+ }
1206
+ }
1207
+
1208
+ // Internal helper exposed for tests so per-process warning state can be reset
1209
+ // between cases that intentionally exercise the warning path repeatedly.
1210
+ function _resetRuntimeWarningCacheForTests() {
1211
+ _warnedConfigKeys.clear();
1212
+ }
1213
+
1214
+ /**
1215
+ * #2517 — Resolve the runtime-aware tier entry for (runtime, tier).
1216
+ *
1217
+ * Single source of truth shared by core.cjs (resolveModelInternal /
1218
+ * resolveReasoningEffortInternal) and bin/install.js (Codex/OpenCode TOML emit
1219
+ * paths). Always merges built-in defaults with user overrides at the field
1220
+ * level so partial overrides keep the unspecified fields:
1221
+ *
1222
+ * `{ codex: { opus: "gpt-5-pro" } }` keeps reasoning_effort: 'xhigh'
1223
+ * `{ codex: { opus: { reasoning_effort: 'low' } } }` keeps model: 'gpt-5.4'
1224
+ *
1225
+ * Without this field-merge, the documented string-shorthand example silently
1226
+ * dropped reasoning_effort and a partial-object override silently dropped the
1227
+ * model — both reported as critical findings in the #2609 review.
1228
+ *
1229
+ * Inputs:
1230
+ * - runtime: string (e.g. 'codex', 'claude', 'opencode')
1231
+ * - tier: 'opus' | 'sonnet' | 'haiku'
1232
+ * - overrides: optional `model_profile_overrides` blob (may be null/undefined)
1233
+ *
1234
+ * Returns `{ model: string, reasoning_effort?: string } | null`.
1235
+ */
1236
+ function resolveTierEntry({ runtime, tier, overrides }) {
1237
+ if (!runtime || !tier) return null;
1238
+
1239
+ const builtin = RUNTIME_PROFILE_MAP[runtime]?.[tier] || null;
1240
+ const userRaw = overrides?.[runtime]?.[tier];
1241
+
1242
+ // String shorthand from CONFIGURATION.md examples — `{ codex: { opus: "gpt-5-pro" } }`.
1243
+ // Treat as `{ model: "gpt-5-pro" }` so the field-merge below still preserves
1244
+ // reasoning_effort from the built-in defaults.
1245
+ let userEntry = null;
1246
+ if (userRaw) {
1247
+ userEntry = typeof userRaw === 'string' ? { model: userRaw } : userRaw;
1248
+ }
1249
+
1250
+ if (!builtin && !userEntry) return null;
1251
+ // Field-merge: user fields win, built-in fills the gaps.
1252
+ return { ...(builtin || {}), ...(userEntry || {}) };
1253
+ }
1254
+
1255
+ /**
1256
+ * Convenience wrapper used by resolveModelInternal / resolveReasoningEffortInternal.
1257
+ * Pulls runtime + overrides out of a loaded config and delegates to resolveTierEntry.
1258
+ */
1259
+ function _resolveRuntimeTier(config, tier) {
1260
+ return resolveTierEntry({
1261
+ runtime: config.runtime,
1262
+ tier,
1263
+ overrides: config.model_profile_overrides,
1264
+ });
1265
+ }
1266
+
1267
+ function resolveModelInternal(cwd, agentType) {
1268
+ const config = loadConfig(cwd);
1269
+
1270
+ // 1. Per-agent override — always respected; highest precedence.
1271
+ // Users who set fully-qualified model IDs (e.g., "openai/gpt-5.4") get exactly that.
1272
+ const override = config.model_overrides?.[agentType];
1273
+ if (override) {
1274
+ return override;
1275
+ }
1276
+
1277
+ // 2. Compute the tier (opus/sonnet/haiku/inherit) for this agent.
1278
+ //
1279
+ // #3023: phase-type slot can override the profile-derived tier.
1280
+ // Precedence: per-agent override (above) > phase-type slot > profile.
1281
+ // Phase-type values are tier aliases (opus/sonnet/haiku/inherit) — same
1282
+ // shape as model_profile output — so the runtime-resolution chain
1283
+ // (step 3), resolve_model_ids handling (step 4), and profile lookup
1284
+ // (step 5) all stay correct without further branching.
1285
+ const profile = String(config.model_profile || 'balanced').toLowerCase();
1286
+ const agentModels = MODEL_PROFILES[agentType];
1287
+ const phaseType = AGENT_TO_PHASE_TYPE[agentType];
1288
+ const phaseTypeTier = (phaseType && config.models && typeof config.models === 'object')
1289
+ ? config.models[phaseType]
1290
+ : undefined;
1291
+ // Only honor phase-type tier if it's one of the recognized aliases.
1292
+ // Anything else falls through to profile lookup so a typo doesn't
1293
+ // silently break tier resolution.
1294
+ const VALID_TIERS = new Set(['opus', 'sonnet', 'haiku', 'inherit']);
1295
+ // Resolve tier: phase-type wins when valid; else profile-derived; else
1296
+ // (when profile === 'inherit') propagate inherit so the later short-
1297
+ // circuit fires. CR Major (#3030): a config like
1298
+ // { model_profile: 'inherit', models: { execution: 'opus' } }
1299
+ // must honor the phase-type opus, not return 'inherit'. Synthesizing
1300
+ // tier='inherit' only when there's no phase-type override keeps the
1301
+ // original inherit semantics intact while letting a valid phase-type
1302
+ // tier win.
1303
+ const tier = (phaseTypeTier && VALID_TIERS.has(phaseTypeTier))
1304
+ ? phaseTypeTier
1305
+ : (profile === 'inherit'
1306
+ ? 'inherit'
1307
+ : (agentModels ? (agentModels[profile] || agentModels['balanced']) : null));
1308
+
1309
+ // 3. Runtime-aware resolution (#2517) — only when `runtime` is explicitly set
1310
+ // to a non-Claude runtime. `runtime: "claude"` is the implicit default and is
1311
+ // treated as a no-op here so it does not silently override `resolve_model_ids:
1312
+ // "omit"` (review finding #4). Deliberate ordering for non-Claude runtimes:
1313
+ // explicit opt-in beats `resolve_model_ids: "omit"` so users on Codex installs
1314
+ // that auto-set "omit" can still flip on tiered behavior by setting runtime
1315
+ // alone. Gate on tier !== 'inherit' (not profile !== 'inherit') so a
1316
+ // valid phase-type tier flips runtime resolution on even when the
1317
+ // profile is inherit.
1318
+ if (config.runtime && config.runtime !== 'claude' && tier && tier !== 'inherit') {
1319
+ const entry = _resolveRuntimeTier(config, tier);
1320
+ if (entry?.model) return entry.model;
1321
+ // Unknown runtime with no user-supplied overrides — fall through to Claude-safe
1322
+ // default rather than emit an ID the runtime can't accept.
1323
+ }
1324
+
1325
+ // 4. resolve_model_ids: "omit" — return empty string so the runtime uses its
1326
+ // configured default model. For non-Claude runtimes (OpenCode, Codex, etc.) that
1327
+ // don't recognize Claude aliases. Set automatically during install. See #1156.
1328
+ if (config.resolve_model_ids === 'omit') {
1329
+ return '';
1330
+ }
1331
+
1332
+ // 5. Profile lookup (Claude-native default).
1333
+ if (!agentModels) {
1334
+ return profile === 'quality' ? 'opus'
1335
+ : profile === 'budget' ? 'haiku'
1336
+ : profile === 'inherit' ? 'inherit'
1337
+ : 'sonnet';
1338
+ }
1339
+ // Gate on tier (not profile) so a valid phase-type override beats
1340
+ // profile=inherit (#3030 CR Major).
1341
+ if (tier === 'inherit') return 'inherit';
1342
+ // `tier` is guaranteed truthy here: agentModels exists, and MODEL_PROFILES
1343
+ // entries always define `balanced`, so `agentModels[profile] || agentModels.balanced`
1344
+ // resolves to a string. Keep the local for readability — no defensive fallback.
1345
+ const alias = tier;
1346
+
1347
+ // resolve_model_ids: true — map alias to full Claude model ID.
1348
+ // Prevents 404s when the Task tool passes aliases directly to the API.
1349
+ if (config.resolve_model_ids) {
1350
+ return MODEL_ALIAS_MAP[alias] || alias;
1351
+ }
1352
+
1353
+ return alias;
1354
+ }
1355
+
1356
+ /**
1357
+ * #3024 — Resolve a model for a specific dynamic-routing attempt.
1358
+ *
1359
+ * The orchestrator (workflow agent) tracks the attempt counter. On
1360
+ * the first spawn, it calls with attempt=0. If the orchestrator detects
1361
+ * a soft failure (verification inconclusive, plan-check FLAG, etc.),
1362
+ * it re-spawns with attempt=1, which escalates the agent's tier one
1363
+ * step up. `max_escalations` caps how many escalations are allowed.
1364
+ *
1365
+ * Resolution precedence (highest → lowest):
1366
+ * 1. config.model_overrides[agent] (full IDs accepted)
1367
+ * 2. dynamic_routing.tier_models[escalated_tier] (when enabled)
1368
+ * 3. models[phase_type] / model_profile (existing chain via
1369
+ * resolveModelInternal)
1370
+ *
1371
+ * When dynamic_routing is null/disabled, this function is identical
1372
+ * to resolveModelInternal — orchestrators can call it unconditionally
1373
+ * without breaking back-compat.
1374
+ *
1375
+ * @param {string} cwd - Project directory.
1376
+ * @param {string} agentType - Agent name (e.g. 'gsd-verifier').
1377
+ * @param {number} [attempt=0] - 0 for first spawn; 1+ for escalation.
1378
+ * Capped internally at max_escalations.
1379
+ * @returns {string} Model alias (opus/sonnet/haiku) or full ID.
1380
+ */
1381
+ function resolveModelForTier(cwd, agentType, attempt) {
1382
+ const config = loadConfig(cwd);
1383
+ const attemptN = Number.isInteger(attempt) && attempt > 0 ? attempt : 0;
1384
+
1385
+ // Per-agent override always wins — same as resolveModelInternal step 1.
1386
+ // User-supplied full IDs bypass the entire tier mechanism.
1387
+ const override = config.model_overrides?.[agentType];
1388
+ if (override) return override;
1389
+
1390
+ const dr = config.dynamic_routing;
1391
+ // Disabled / missing / non-object → fall back to the existing resolver.
1392
+ if (!dr || typeof dr !== 'object' || dr.enabled !== true) {
1393
+ return resolveModelInternal(cwd, agentType);
1394
+ }
1395
+
1396
+ const tierModels = dr.tier_models;
1397
+ if (!tierModels || typeof tierModels !== 'object') {
1398
+ // tier_models missing — can't dynamic-route; fall back.
1399
+ return resolveModelInternal(cwd, agentType);
1400
+ }
1401
+
1402
+ const defaultTier = AGENT_DEFAULT_TIERS[agentType];
1403
+ if (!defaultTier || !VALID_AGENT_TIERS.has(defaultTier)) {
1404
+ // Unmapped agent — no default tier; fall back so we don't silently
1405
+ // pick the wrong model.
1406
+ return resolveModelInternal(cwd, agentType);
1407
+ }
1408
+
1409
+ // Cap effective escalation at max_escalations (default 1). Beyond
1410
+ // the cap, the resolver returns the model for the cap level so the
1411
+ // orchestrator can log "max escalations reached" without burning
1412
+ // further budget.
1413
+ //
1414
+ // CR Major (#3031): `escalate_on_failure: false` is the kill-switch
1415
+ // for escalation — when false, every attempt resolves to the default
1416
+ // tier regardless of the attempt counter. Without this guard, an
1417
+ // orchestrator that blindly bumps the counter on retry would silently
1418
+ // escalate even though the user opted out.
1419
+ const maxEscalations = Number.isInteger(dr.max_escalations) && dr.max_escalations >= 0
1420
+ ? dr.max_escalations
1421
+ : 1;
1422
+ const escalationEnabled = dr.escalate_on_failure !== false;
1423
+ const effectiveAttempt = escalationEnabled
1424
+ ? Math.min(attemptN, maxEscalations)
1425
+ : 0;
1426
+
1427
+ // Walk the escalation chain N times from the default tier.
1428
+ let tier = defaultTier;
1429
+ for (let i = 0; i < effectiveAttempt; i += 1) {
1430
+ const next = nextTier(tier);
1431
+ if (!next || next === tier) break; // already at top
1432
+ tier = next;
1433
+ }
1434
+
1435
+ const alias = tierModels[tier];
1436
+ if (typeof alias !== 'string' || alias.length === 0) {
1437
+ // Misconfigured tier_models — missing slot. Fall back rather
1438
+ // than emit an empty model id.
1439
+ return resolveModelInternal(cwd, agentType);
1440
+ }
1441
+ return alias;
1442
+ }
1443
+
1444
+ /**
1445
+ * #2517 — Resolve runtime-specific reasoning_effort for an agent.
1446
+ * Returns null unless:
1447
+ * - `runtime` is explicitly set in config,
1448
+ * - the runtime supports reasoning_effort (currently: codex),
1449
+ * - profile is not 'inherit',
1450
+ * - the resolved tier entry has a `reasoning_effort` value.
1451
+ *
1452
+ * Never returns a value for Claude — keeps reasoning_effort out of Claude spawn paths.
1453
+ */
1454
+ function resolveReasoningEffortInternal(cwd, agentType) {
1455
+ const config = loadConfig(cwd);
1456
+ if (!config.runtime) return null;
1457
+ // Strict allowlist: reasoning_effort only propagates for runtimes whose
1458
+ // install path actually accepts it. Adding a new runtime here is the only
1459
+ // way to enable effort propagation — overrides cannot bypass the gate.
1460
+ // Without this, a typo in `runtime` (e.g. `"codx"`) plus a user override
1461
+ // for that typo would leak `xhigh` into a Claude or unknown install
1462
+ // (review finding #3).
1463
+ if (!RUNTIMES_WITH_REASONING_EFFORT.has(config.runtime)) return null;
1464
+ // Per-agent override means user supplied a fully-qualified ID; reasoning_effort
1465
+ // for that case must be set via per-agent mechanism, not tier inference.
1466
+ if (config.model_overrides?.[agentType]) return null;
1467
+
1468
+ const profile = String(config.model_profile || 'balanced').toLowerCase();
1469
+ const agentModels = MODEL_PROFILES[agentType];
1470
+ if (!agentModels) return null;
1471
+
1472
+ // #3023 (CR Major): mirror the phase-type tier lookup from
1473
+ // resolveModelInternal. Without this, `model` and `reasoning_effort`
1474
+ // derive from different tier sources on Codex when models.<phase_type>
1475
+ // overrides the profile.
1476
+ //
1477
+ // #3030 CR follow-up: do NOT short-circuit on profile === 'inherit'
1478
+ // before reading the phase-type tier. A config like
1479
+ // { model_profile: 'inherit', models: { execution: 'opus' } }
1480
+ // must produce the opus runtime effort, not null. Compute tier from
1481
+ // phase-type first; only fall back to profile when there's no valid
1482
+ // phase-type override; only return null when the resolved tier is
1483
+ // 'inherit' or unknown.
1484
+ const phaseType = AGENT_TO_PHASE_TYPE[agentType];
1485
+ const phaseTypeTier = (phaseType && config.models && typeof config.models === 'object')
1486
+ ? config.models[phaseType]
1487
+ : undefined;
1488
+ // Explicit phase-type 'inherit' is the user opting out of tier-based
1489
+ // effort for this phase — return null instead of falling through to
1490
+ // profile (which would silently emit the profile's effort and
1491
+ // contradict the user's choice).
1492
+ if (phaseTypeTier === 'inherit') return null;
1493
+ const VALID_TIERS = new Set(['opus', 'sonnet', 'haiku']);
1494
+ const tier = (phaseTypeTier && VALID_TIERS.has(phaseTypeTier))
1495
+ ? phaseTypeTier
1496
+ : (profile === 'inherit'
1497
+ ? 'inherit'
1498
+ : (agentModels[profile] || agentModels['balanced']));
1499
+ // 'inherit' (from profile fallback) yields no runtime effort.
1500
+ if (!tier || tier === 'inherit') return null;
1501
+
1502
+ const entry = _resolveRuntimeTier(config, tier);
1503
+ return entry?.reasoning_effort || null;
1504
+ }
1505
+
1506
+ // ─── Summary body helpers ─────────────────────────────────────────────────
1507
+
1508
+ /**
1509
+ * Extract a one-liner from the summary body when it's not in frontmatter.
1510
+ * The summary template defines one-liner as a bold markdown line after the heading:
1511
+ * # Phase X: Name Summary
1512
+ * **[substantive one-liner text]**
1513
+ */
1514
+ function extractOneLinerFromBody(content) {
1515
+ if (!content) return null;
1516
+ // Normalize EOLs so matching works for LF and CRLF files.
1517
+ const normalized = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
1518
+ // Strip frontmatter first
1519
+ const body = normalized.replace(/^---\n[\s\S]*?\n---\n*/, '');
1520
+ // Find the first **...** span on a line after a # heading.
1521
+ // Two supported template forms:
1522
+ // 1) Labeled: **One-liner:** Real prose here. (bug #2660 — new template)
1523
+ // 2) Bare: **Real prose here.** (legacy template)
1524
+ // For (1), the first bold span ends in a colon and the prose that follows
1525
+ // on the same line is the one-liner. For (2), the bold span itself is the
1526
+ // one-liner.
1527
+ const match = body.match(/^#[^\n]*\n+\*\*([^*\n]+)\*\*([^\n]*)/m);
1528
+ if (!match) return null;
1529
+ const boldInner = match[1].trim();
1530
+ const afterBold = match[2];
1531
+ // Labeled form: bold span is a "Label:" prefix — capture prose after it.
1532
+ if (/:\s*$/.test(boldInner)) {
1533
+ const prose = afterBold.trim();
1534
+ return prose.length > 0 ? prose : null;
1535
+ }
1536
+ // Bare form: the bold content itself is the one-liner.
1537
+ return boldInner.length > 0 ? boldInner : null;
1538
+ }
1539
+
1540
+ // ─── Misc utilities ───────────────────────────────────────────────────────────
1541
+
1542
+ function pathExistsInternal(cwd, targetPath) {
1543
+ const fullPath = path.isAbsolute(targetPath) ? targetPath : path.join(cwd, targetPath);
1544
+ try {
1545
+ fs.statSync(fullPath);
1546
+ return true;
1547
+ } catch {
1548
+ return false;
1549
+ }
1550
+ }
1551
+
1552
+ /**
1553
+ * Detect whether `cwd` sits inside a git worktree, and if so, return the
1554
+ * absolute path of the worktree root.
1555
+ *
1556
+ * Bug #3491: the previous shallow `pathExistsInternal(cwd, '.git')` check
1557
+ * only saw a `.git` entry directly in cwd, so subdirectories of an existing
1558
+ * repo reported `has_git: false` and the new-project workflow then ran
1559
+ * `git init` — creating a nested `.git` inside the outer repo's worktree.
1560
+ *
1561
+ * Mirrors `git rev-parse --is-inside-work-tree` semantics. Uses the existing
1562
+ * `execGit` seam so behaviour is consistent with the rest of the toolchain
1563
+ * (non-interactive env, 10s timeout, mockable in tests).
1564
+ *
1565
+ * Returns: { inside: boolean, worktreeRoot: string | null }
1566
+ * - inside=true → cwd is somewhere inside a git worktree
1567
+ * - inside=false → cwd is not inside any git worktree (or git is unavailable)
1568
+ *
1569
+ * Failure modes (git not installed, command times out, non-zero exit) all
1570
+ * collapse to `{ inside: false, worktreeRoot: null }` — the conservative
1571
+ * default that preserves pre-fix behaviour for environments without git.
1572
+ */
1573
+ function gitWorktreeInfoInternal(cwd) {
1574
+ try {
1575
+ const insideResult = execGit(['rev-parse', '--is-inside-work-tree'], { cwd, timeout: 5000 });
1576
+ if (insideResult.exitCode !== 0) {
1577
+ return { inside: false, worktreeRoot: null };
1578
+ }
1579
+ const insideStdout = String(insideResult.stdout || '').trim();
1580
+ if (insideStdout !== 'true') {
1581
+ return { inside: false, worktreeRoot: null };
1582
+ }
1583
+ const rootResult = execGit(['rev-parse', '--show-toplevel'], { cwd, timeout: 5000 });
1584
+ if (rootResult.exitCode !== 0) {
1585
+ return { inside: true, worktreeRoot: null };
1586
+ }
1587
+ const root = String(rootResult.stdout || '').trim();
1588
+ return { inside: true, worktreeRoot: root || null };
1589
+ } catch {
1590
+ return { inside: false, worktreeRoot: null };
1591
+ }
1592
+ }
1593
+
1594
+ function generateSlugInternal(text) {
1595
+ if (!text) return null;
1596
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').substring(0, 60);
1597
+ }
1598
+
1599
+ function getMilestoneInfo(cwd) {
1600
+ try {
1601
+ const roadmap = platformReadSync(path.join(planningDir(cwd), 'ROADMAP.md'));
1602
+ if (roadmap === null) throw new Error('missing');
1603
+
1604
+ // 0. Prefer STATE.md milestone: frontmatter as the authoritative source.
1605
+ // This prevents falling through to a regex that may match an old heading
1606
+ // when the active milestone's 🚧 marker is inside a <summary> tag without
1607
+ // **bold** formatting (bug #2409).
1608
+ let stateVersion = null;
1609
+ if (cwd) {
1610
+ try {
1611
+ const statePath = path.join(planningDir(cwd), 'STATE.md');
1612
+ const stateRaw = platformReadSync(statePath);
1613
+ if (stateRaw !== null) {
1614
+ const m = stateRaw.match(/^milestone:\s*(.+)/m);
1615
+ if (m) stateVersion = m[1].trim();
1616
+ }
1617
+ } catch { /* intentionally empty */ }
1618
+ }
1619
+
1620
+ if (stateVersion) {
1621
+ // Look up the name for this version in ROADMAP.md
1622
+ const escapedVer = escapeRegex(stateVersion);
1623
+ // Match heading-format: ## Roadmap v2.9: Name or ## v2.9 Name
1624
+ const headingMatch = roadmap.match(
1625
+ new RegExp(`##[^\\n]*${escapedVer}[:\\s]+([^\\n(]+)`, 'i')
1626
+ );
1627
+ if (headingMatch) {
1628
+ // If the heading line contains ✅ the milestone is already shipped.
1629
+ // Fall through to normal detection so the NEW active milestone is returned
1630
+ // instead of the stale shipped one still recorded in STATE.md.
1631
+ if (!headingMatch[0].includes('✅')) {
1632
+ return { version: stateVersion, name: headingMatch[1].trim() };
1633
+ }
1634
+ // Shipped milestone — do not early-return; fall through to normal detection below.
1635
+ } else {
1636
+ // Match list-format: 🚧 **v2.9 Name** or 🚧 v2.9 Name
1637
+ const listMatch = roadmap.match(
1638
+ new RegExp(`🚧\\s*\\*?\\*?${escapedVer}\\s+([^*\\n]+)`, 'i')
1639
+ );
1640
+ if (listMatch) {
1641
+ return { version: stateVersion, name: listMatch[1].trim() };
1642
+ }
1643
+ // Version found in STATE.md but no name match in ROADMAP — return bare version
1644
+ return { version: stateVersion, name: 'milestone' };
1645
+ }
1646
+ }
1647
+
1648
+ // First: check for list-format roadmaps using 🚧 (in-progress) marker
1649
+ // e.g. "- 🚧 **v2.1 Belgium** — Phases 24-28 (in progress)"
1650
+ // e.g. "- 🚧 **v1.2.1 Tech Debt** — Phases 1-8 (in progress)"
1651
+ const inProgressMatch = roadmap.match(/🚧\s*\*\*v(\d+(?:\.\d+)+)\s+([^*]+)\*\*/);
1652
+ if (inProgressMatch) {
1653
+ return {
1654
+ version: 'v' + inProgressMatch[1],
1655
+ name: inProgressMatch[2].trim(),
1656
+ };
1657
+ }
1658
+
1659
+ // Second: heading-format roadmaps — strip shipped milestones.
1660
+ // <details> blocks are stripped by stripShippedMilestones; heading-format ✅ markers
1661
+ // are excluded by the negative lookahead below so a stale STATE.md version (or any
1662
+ // shipped ✅ heading) never wins over the first non-shipped milestone heading.
1663
+ const cleaned = stripShippedMilestones(roadmap);
1664
+ // Negative lookahead skips headings that contain ✅ (shipped milestone marker).
1665
+ // Supports 2+ segment versions: v1.2, v1.2.1, v2.0.1, etc.
1666
+ const headingMatch = cleaned.match(/## (?!.*✅).*v(\d+(?:\.\d+)+)[:\s]+([^\n(]+)/);
1667
+ if (headingMatch) {
1668
+ return {
1669
+ version: 'v' + headingMatch[1],
1670
+ name: headingMatch[2].trim(),
1671
+ };
1672
+ }
1673
+ // Fallback: try bare version match (greedy — capture longest version string)
1674
+ const versionMatch = cleaned.match(/v(\d+(?:\.\d+)+)/);
1675
+ return {
1676
+ version: versionMatch ? versionMatch[0] : 'v1.0',
1677
+ name: 'milestone',
1678
+ };
1679
+ } catch {
1680
+ return { version: 'v1.0', name: 'milestone' };
1681
+ }
1682
+ }
1683
+
1684
+ /**
1685
+ * Returns a filter function that checks whether a phase directory belongs
1686
+ * to the current milestone based on ROADMAP.md phase headings.
1687
+ * If no ROADMAP exists or no phases are listed, returns a pass-all filter.
1688
+ */
1689
+ function getMilestonePhaseFilter(cwd, versionOverride) {
1690
+ const milestonePhaseNums = new Set();
1691
+ let missingExplicitVersion = false;
1692
+ try {
1693
+ const roadmapPath = path.join(planningDir(cwd), 'ROADMAP.md');
1694
+ const roadmapContent = platformReadSync(roadmapPath);
1695
+ if (roadmapContent === null) throw new Error('missing');
1696
+ let roadmap = extractCurrentMilestone(roadmapContent, cwd);
1697
+
1698
+ if (versionOverride) {
1699
+ const escapedVersion = escapeRegex(versionOverride);
1700
+ const sectionPattern = new RegExp(`(^#{1,3}\\s+.*${escapedVersion}[^\\n]*)`, 'mi');
1701
+ const sectionMatch = roadmapContent.match(sectionPattern);
1702
+ if (!sectionMatch) {
1703
+ // Only treat this as an error case when the roadmap is milestone-versioned.
1704
+ // Older/flat roadmap formats without vX.Y milestone headings should keep
1705
+ // legacy pass-through behavior for milestone.complete.
1706
+ const hasVersionedMilestones = /^#{1,3}\s+.*v\d+\.\d+/mi.test(roadmapContent);
1707
+ if (hasVersionedMilestones) {
1708
+ roadmap = '';
1709
+ missingExplicitVersion = true;
1710
+ }
1711
+ } else {
1712
+ const sectionStart = sectionMatch.index;
1713
+ const headingLevel = sectionMatch[1].match(/^(#{1,3})\s/)[1].length;
1714
+ const restContent = roadmapContent.slice(sectionStart + sectionMatch[0].length);
1715
+ const nextMilestonePattern = new RegExp(`^#{1,${headingLevel}}\\s+(?!Phase\\s+\\S)(?:.*v\\d+\\.\\d+|✅|📋|🚧)`, 'i');
1716
+
1717
+ let sectionEnd = roadmapContent.length;
1718
+ let fenceChar = null;
1719
+ let fenceLen = 0;
1720
+ let charOffset = 0;
1721
+ for (const line of restContent.split('\n')) {
1722
+ const fenceMatch = line.match(/^\s{0,3}((?:`{3,}|~{3,}))(.*)/);
1723
+ if (fenceMatch) {
1724
+ const char = fenceMatch[1][0];
1725
+ const len = fenceMatch[1].length;
1726
+ const trailing = fenceMatch[2] || '';
1727
+ if (!fenceChar) {
1728
+ fenceChar = char;
1729
+ fenceLen = len;
1730
+ } else if (char === fenceChar && len >= fenceLen && /^\s*$/.test(trailing)) {
1731
+ fenceChar = null;
1732
+ fenceLen = 0;
1733
+ }
1734
+ } else if (!fenceChar && nextMilestonePattern.test(line)) {
1735
+ sectionEnd = sectionStart + sectionMatch[0].length + charOffset;
1736
+ break;
1737
+ }
1738
+ charOffset += line.length + 1;
1739
+ }
1740
+
1741
+ const currentSection = roadmapContent.slice(sectionStart, sectionEnd);
1742
+ roadmap = currentSection;
1743
+ }
1744
+ }
1745
+
1746
+ // Match both numeric phases (Phase 1:) and custom IDs (Phase PROJ-42:)
1747
+ const phasePattern = /#{2,4}\s*Phase\s+([\w][\w.-]*)\s*:/gi;
1748
+ let m;
1749
+ while ((m = phasePattern.exec(roadmap)) !== null) {
1750
+ milestonePhaseNums.add(m[1]);
1751
+ }
1752
+ } catch { /* intentionally empty */ }
1753
+
1754
+ if (milestonePhaseNums.size === 0) {
1755
+ const passAll = () => true;
1756
+ passAll.phaseCount = 0;
1757
+ passAll.missingExplicitVersion = missingExplicitVersion;
1758
+ return passAll;
1759
+ }
1760
+
1761
+ const normalized = new Set(
1762
+ [...milestonePhaseNums].map(n => (n.replace(/^0+(?=\d)/, '') || '0').toLowerCase())
1763
+ );
1764
+
1765
+ function isDirInMilestone(dirName) {
1766
+ // Try numeric match first
1767
+ const m = dirName.match(/^0*(\d+[A-Za-z]?(?:\.\d+)*)/);
1768
+ if (m && normalized.has(m[1].toLowerCase())) return true;
1769
+ // Try custom ID match (e.g. PROJ-42-description → PROJ-42)
1770
+ const customMatch = dirName.match(/^([A-Za-z][A-Za-z0-9]*(?:-[A-Za-z0-9]+)*)/);
1771
+ if (customMatch && normalized.has(customMatch[1].toLowerCase())) return true;
1772
+ // #3600: project-code-prefixed directory (`CK-01-name`) against a
1773
+ // numeric ROADMAP heading (`### Phase 1:`). Strip the same prefix
1774
+ // shape `normalizePhaseName` recognises (`^[A-Z]{1,6}-(?=\d)`) and
1775
+ // retry the numeric match. This runs AFTER the custom-ID match so
1776
+ // a roadmap that uses `Phase PROJ-42:` continues to win via the
1777
+ // existing custom-ID path; the strip-and-retry only fires when the
1778
+ // milestone is keyed on the bare numeric form.
1779
+ const stripped = dirName.replace(/^[A-Z]{1,6}-(?=\d)/i, '');
1780
+ if (stripped !== dirName) {
1781
+ const sm = stripped.match(/^0*(\d+[A-Za-z]?(?:\.\d+)*)/);
1782
+ if (sm && normalized.has(sm[1].toLowerCase())) return true;
1783
+ }
1784
+ return false;
1785
+ }
1786
+ isDirInMilestone.phaseCount = milestonePhaseNums.size;
1787
+ isDirInMilestone.missingExplicitVersion = missingExplicitVersion;
1788
+ return isDirInMilestone;
1789
+ }
1790
+
1791
+ // ─── Phase file helpers ──────────────────────────────────────────────────────
1792
+
1793
+ /** Filter a file list to just PLAN.md / *-PLAN.md entries. */
1794
+ function filterPlanFiles(files) {
1795
+ return files.filter(f => f.endsWith('-PLAN.md') || f === 'PLAN.md');
1796
+ }
1797
+
1798
+ /** Filter a file list to just SUMMARY.md / *-SUMMARY.md entries. */
1799
+ function filterSummaryFiles(files) {
1800
+ return files.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
1801
+ }
1802
+
1803
+ /**
1804
+ * Read a phase directory and return counts/flags for common file types.
1805
+ * Returns an object with plans[], summaries[], and boolean flags for
1806
+ * research/context/verification files.
1807
+ */
1808
+ function getPhaseFileStats(phaseDir) {
1809
+ const files = fs.readdirSync(phaseDir);
1810
+ return {
1811
+ plans: filterPlanFiles(files),
1812
+ summaries: filterSummaryFiles(files),
1813
+ hasResearch: files.some(f => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md'),
1814
+ hasContext: findContextMdIn(files) !== null,
1815
+ hasVerification: files.some(f => f.endsWith('-VERIFICATION.md') || f === 'VERIFICATION.md'),
1816
+ hasReviews: files.some(f => f.endsWith('-REVIEWS.md') || f === 'REVIEWS.md'),
1817
+ };
1818
+ }
1819
+
1820
+ /**
1821
+ * Read immediate child directories from a path.
1822
+ * Returns [] if the path doesn't exist or can't be read.
1823
+ * Pass sort=true to apply comparePhaseNum ordering.
1824
+ */
1825
+ function readSubdirectories(dirPath, sort = false) {
1826
+ try {
1827
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
1828
+ const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
1829
+ return sort ? dirs.sort((a, b) => comparePhaseNum(a, b)) : dirs;
1830
+ } catch {
1831
+ return [];
1832
+ }
1833
+ }
1834
+
1835
+ /**
1836
+ * Format a Date as a fuzzy relative time string (e.g. "5 minutes ago").
1837
+ * @param {Date} date
1838
+ * @returns {string}
1839
+ */
1840
+ function timeAgo(date) {
1841
+ const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
1842
+ if (seconds < 5) return 'just now';
1843
+ if (seconds < 60) return `${seconds} seconds ago`;
1844
+ const minutes = Math.floor(seconds / 60);
1845
+ if (minutes === 1) return '1 minute ago';
1846
+ if (minutes < 60) return `${minutes} minutes ago`;
1847
+ const hours = Math.floor(minutes / 60);
1848
+ if (hours === 1) return '1 hour ago';
1849
+ if (hours < 24) return `${hours} hours ago`;
1850
+ const days = Math.floor(hours / 24);
1851
+ if (days === 1) return '1 day ago';
1852
+ if (days < 30) return `${days} days ago`;
1853
+ const months = Math.floor(days / 30);
1854
+ if (months === 1) return '1 month ago';
1855
+ if (months < 12) return `${months} months ago`;
1856
+ const years = Math.floor(days / 365);
1857
+ if (years === 1) return '1 year ago';
1858
+ return `${years} years ago`;
1859
+ }
1860
+
1861
+ module.exports = {
1862
+ output,
1863
+ error,
1864
+ ERROR_REASON,
1865
+ setJsonErrorMode,
1866
+ getJsonErrorMode,
1867
+ loadConfig,
1868
+ isGitIgnored,
1869
+ escapeRegex,
1870
+ normalizePhaseName,
1871
+ phaseMarkdownRegexSource,
1872
+ phaseMarkdownRegexSourceExact,
1873
+ comparePhaseNum,
1874
+ searchPhaseInDir,
1875
+ extractPhaseToken,
1876
+ phaseTokenMatches,
1877
+ findPhaseInternal,
1878
+ getArchivedPhaseDirs,
1879
+ getRoadmapPhaseInternal,
1880
+ resolveModelInternal,
1881
+ resolveModelForTier,
1882
+ resolveReasoningEffortInternal,
1883
+ RUNTIME_PROFILE_MAP,
1884
+ RUNTIMES_WITH_REASONING_EFFORT,
1885
+ KNOWN_RUNTIMES,
1886
+ RUNTIME_OVERRIDE_TIERS,
1887
+ resolveTierEntry,
1888
+ _resetRuntimeWarningCacheForTests,
1889
+ pathExistsInternal,
1890
+ gitWorktreeInfoInternal,
1891
+ generateSlugInternal,
1892
+ getMilestoneInfo,
1893
+ getMilestonePhaseFilter,
1894
+ stripShippedMilestones,
1895
+ extractCurrentMilestone,
1896
+ replaceInCurrentMilestone,
1897
+ toPosixPath,
1898
+ extractOneLinerFromBody,
1899
+ resolveWorktreeRoot,
1900
+ // Deprecated re-exports — prefer direct import from planning-workspace.cjs
1901
+ withPlanningLock,
1902
+ findProjectRoot,
1903
+ detectSubRepos,
1904
+ reapStaleTempFiles,
1905
+ GSD_TEMP_DIR,
1906
+ MODEL_ALIAS_MAP,
1907
+ CONFIG_DEFAULTS,
1908
+ planningDir,
1909
+ planningRoot,
1910
+ planningPaths,
1911
+ getActiveWorkstream,
1912
+ setActiveWorkstream,
1913
+ filterPlanFiles,
1914
+ filterSummaryFiles,
1915
+ getPhaseFileStats,
1916
+ readSubdirectories,
1917
+ getAgentsDir,
1918
+ checkAgentsInstalled,
1919
+ timeAgo,
1920
+ pruneOrphanedWorktrees,
1921
+ inspectWorktreeHealth,
1922
+ };