@opengsd/gsd-pi 1.1.1-dev.b2556262 → 1.2.0-dev.4813ead6

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 (500) hide show
  1. package/dist/cli-web-branch.d.ts +2 -0
  2. package/dist/cli-web-branch.js +9 -2
  3. package/dist/help-text.js +5 -0
  4. package/dist/project-sessions.js +4 -2
  5. package/dist/resources/.managed-resources-content-hash +1 -1
  6. package/dist/resources/extensions/ask-user-questions.js +78 -23
  7. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +101 -237
  8. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +224 -0
  9. package/dist/resources/extensions/github-sync/templates.js +3 -3
  10. package/dist/resources/extensions/gsd/artifact-projection.js +14 -0
  11. package/dist/resources/extensions/gsd/auto/contracts.js +8 -1
  12. package/dist/resources/extensions/gsd/auto/loop.js +74 -56
  13. package/dist/resources/extensions/gsd/auto/orchestrator.js +763 -63
  14. package/dist/resources/extensions/gsd/auto/phases.js +28 -3
  15. package/dist/resources/extensions/gsd/auto/run-unit.js +2 -1
  16. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  17. package/dist/resources/extensions/gsd/auto-dashboard.js +16 -4
  18. package/dist/resources/extensions/gsd/auto-dispatch.js +6 -5
  19. package/dist/resources/extensions/gsd/auto-model-selection.js +8 -0
  20. package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
  21. package/dist/resources/extensions/gsd/auto-prompts.js +191 -9
  22. package/dist/resources/extensions/gsd/auto-recovery.js +48 -49
  23. package/dist/resources/extensions/gsd/auto-runtime-state.js +17 -0
  24. package/dist/resources/extensions/gsd/auto-start.js +12 -23
  25. package/dist/resources/extensions/gsd/auto-timers.js +16 -2
  26. package/dist/resources/extensions/gsd/auto-tool-tracking.js +37 -0
  27. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +33 -29
  28. package/dist/resources/extensions/gsd/auto-verification.js +7 -7
  29. package/dist/resources/extensions/gsd/auto-worktree.js +45 -36
  30. package/dist/resources/extensions/gsd/auto.js +73 -471
  31. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +28 -37
  32. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +11 -37
  33. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
  34. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +103 -138
  35. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +63 -4
  36. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +21 -4
  37. package/dist/resources/extensions/gsd/codebase-generator.js +8 -4
  38. package/dist/resources/extensions/gsd/commands/handlers/auto.js +3 -0
  39. package/dist/resources/extensions/gsd/commands-handlers.js +20 -0
  40. package/dist/resources/extensions/gsd/commands-inspect.js +4 -8
  41. package/dist/resources/extensions/gsd/commands-maintenance.js +61 -41
  42. package/dist/resources/extensions/gsd/commands-ship.js +2 -2
  43. package/dist/resources/extensions/gsd/commands-verdict.js +12 -2
  44. package/dist/resources/extensions/gsd/db-workspace.js +103 -0
  45. package/dist/resources/extensions/gsd/debug-logger.js +10 -0
  46. package/dist/resources/extensions/gsd/delegation-policy.js +2 -10
  47. package/dist/resources/extensions/gsd/discussion-handoff.js +218 -0
  48. package/dist/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  49. package/dist/resources/extensions/gsd/doctor-proactive.js +7 -2
  50. package/dist/resources/extensions/gsd/doctor.js +16 -9
  51. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  52. package/dist/resources/extensions/gsd/git-conflict-state.js +16 -1
  53. package/dist/resources/extensions/gsd/gsd-db.js +12 -0
  54. package/dist/resources/extensions/gsd/guided-flow.js +36 -470
  55. package/dist/resources/extensions/gsd/guided-unit-completion.js +225 -0
  56. package/dist/resources/extensions/gsd/markdown-renderer.js +33 -33
  57. package/dist/resources/extensions/gsd/mcp-filter.js +8 -1
  58. package/dist/resources/extensions/gsd/mcp-tool-name.js +26 -0
  59. package/dist/resources/extensions/gsd/md-importer.js +4 -3
  60. package/dist/resources/extensions/gsd/migrate/safety.js +2 -2
  61. package/dist/resources/extensions/gsd/migration-auto-check.js +3 -2
  62. package/dist/resources/extensions/gsd/milestone-closeout-proof.js +72 -0
  63. package/dist/resources/extensions/gsd/milestone-closeout.js +12 -4
  64. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +10 -0
  65. package/dist/resources/extensions/gsd/milestone-planning-persistence.js +156 -0
  66. package/dist/resources/extensions/gsd/milestone-readiness.js +77 -0
  67. package/dist/resources/extensions/gsd/milestone-settlement.js +50 -0
  68. package/dist/resources/extensions/gsd/milestone-validation-evidence.js +73 -0
  69. package/dist/resources/extensions/gsd/milestone-validation-verdict.js +57 -0
  70. package/dist/resources/extensions/gsd/native-git-bridge.js +45 -0
  71. package/dist/resources/extensions/gsd/parallel-eligibility.js +3 -6
  72. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -2
  73. package/dist/resources/extensions/gsd/preferences-diagnostics.js +67 -0
  74. package/dist/resources/extensions/gsd/preferences.js +147 -29
  75. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  76. package/dist/resources/extensions/gsd/prompts/discuss.md +6 -7
  77. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
  78. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +5 -7
  79. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +6 -6
  80. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +1 -2
  81. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -6
  82. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  83. package/dist/resources/extensions/gsd/prompts/plan-slice.md +2 -1
  84. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  85. package/dist/resources/extensions/gsd/prompts/research-milestone.md +2 -2
  86. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  87. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +5 -3
  88. package/dist/resources/extensions/gsd/provider-payload-policy.js +83 -0
  89. package/dist/resources/extensions/gsd/pull-request-process.js +13 -0
  90. package/dist/resources/extensions/gsd/quality-gate-closure.js +109 -0
  91. package/dist/resources/extensions/gsd/question-transport.js +86 -0
  92. package/dist/resources/extensions/gsd/roadmap-slices.js +8 -2
  93. package/dist/resources/extensions/gsd/schemas/parsers.js +6 -1
  94. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +3 -2
  95. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +21 -1
  96. package/dist/resources/extensions/gsd/state.js +13 -5
  97. package/dist/resources/extensions/gsd/templates/plan.md +7 -0
  98. package/dist/resources/extensions/gsd/templates/project.md +1 -0
  99. package/dist/resources/extensions/gsd/templates/roadmap.md +1 -1
  100. package/dist/resources/extensions/gsd/templates/uat.md +5 -1
  101. package/dist/resources/extensions/gsd/tool-contract.js +52 -8
  102. package/dist/resources/extensions/gsd/tool-presentation-plan.js +15 -34
  103. package/dist/resources/extensions/gsd/tool-surface-snapshot.js +17 -0
  104. package/dist/resources/extensions/gsd/tools/plan-milestone.js +15 -143
  105. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +39 -0
  106. package/dist/resources/extensions/gsd/tools/validate-milestone.js +15 -78
  107. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +169 -20
  108. package/dist/resources/extensions/gsd/uat-policy.js +16 -10
  109. package/dist/resources/extensions/gsd/uat-run.js +9 -14
  110. package/dist/resources/extensions/gsd/unit-context-composer.js +40 -20
  111. package/dist/resources/extensions/gsd/unit-runtime.js +3 -2
  112. package/dist/resources/extensions/gsd/unit-tool-contracts.js +2 -1
  113. package/dist/resources/extensions/gsd/user-input-boundary.js +65 -4
  114. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
  115. package/dist/resources/extensions/gsd/web-app-uat.js +80 -0
  116. package/dist/resources/extensions/gsd/workflow-mcp.js +15 -102
  117. package/dist/resources/extensions/gsd/workflow-reconcile.js +4 -3
  118. package/dist/resources/extensions/gsd/workflow-tool-surface.js +46 -0
  119. package/dist/resources/extensions/gsd/workspace-git-guard.js +2 -0
  120. package/dist/resources/extensions/gsd/worktree-state-projection.js +33 -4
  121. package/dist/resources/extensions/gsd/worktree-telemetry.js +12 -0
  122. package/dist/resources/extensions/shared/interview-ui.js +2 -2
  123. package/dist/resources/shared/claude-runtime-floor.js +182 -0
  124. package/dist/tsconfig.extensions.tsbuildinfo +1 -0
  125. package/dist/update-cmd.js +20 -0
  126. package/dist/web/standalone/.next/BUILD_ID +1 -1
  127. package/dist/web/standalone/.next/app-path-routes-manifest.json +5 -5
  128. package/dist/web/standalone/.next/build-manifest.json +3 -3
  129. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  130. package/dist/web/standalone/.next/react-loadable-manifest.json +8 -8
  131. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  132. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  140. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  141. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  144. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  145. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  146. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  147. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  148. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  150. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  151. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  152. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  153. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  154. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  156. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  157. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  159. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  160. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  161. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  162. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  163. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  164. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  165. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  166. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  167. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  168. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  169. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  170. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  171. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  172. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  173. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  174. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  175. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  176. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  177. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  178. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  179. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  180. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  181. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  182. package/dist/web/standalone/.next/server/app/index.html +1 -1
  183. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  184. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  185. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  186. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  187. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  188. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  189. package/dist/web/standalone/.next/server/app-paths-manifest.json +5 -5
  190. package/dist/web/standalone/.next/server/chunks/5047.js +2 -0
  191. package/dist/web/standalone/.next/server/chunks/5124.js +1 -0
  192. package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
  193. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  196. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  197. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  198. package/dist/web/standalone/.next/static/chunks/2659.b7b129ee6a769448.js +1 -0
  199. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +1 -0
  200. package/dist/web/standalone/.next/static/chunks/{3616.4113d484a994e411.js → 3616.3c60753b8ffcbd2e.js} +1 -1
  201. package/dist/web/standalone/.next/static/chunks/4283.e4873b058df143a1.js +2 -0
  202. package/dist/web/standalone/.next/static/chunks/5826.a46ecdd1cfe8dabc.js +1 -0
  203. package/dist/web/standalone/.next/static/chunks/796.cf859a427a2cb2ac.js +10 -0
  204. package/dist/web/standalone/.next/static/chunks/8785.2e5a118797fb2dd2.js +1 -0
  205. package/dist/web/standalone/.next/static/chunks/{webpack-dda80a1ef5587410.js → webpack-fbea77b5f9953368.js} +1 -1
  206. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  207. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  208. package/dist/web/standalone/node_modules/postcss/lib/container.js +26 -18
  209. package/dist/web/standalone/node_modules/postcss/lib/css-syntax-error.js +47 -14
  210. package/dist/web/standalone/node_modules/postcss/lib/declaration.js +4 -4
  211. package/dist/web/standalone/node_modules/postcss/lib/fromJSON.js +3 -3
  212. package/dist/web/standalone/node_modules/postcss/lib/input.js +54 -29
  213. package/dist/web/standalone/node_modules/postcss/lib/lazy-result.js +47 -37
  214. package/dist/web/standalone/node_modules/postcss/lib/map-generator.js +26 -9
  215. package/dist/web/standalone/node_modules/postcss/lib/no-work-result.js +57 -55
  216. package/dist/web/standalone/node_modules/postcss/lib/node.js +99 -31
  217. package/dist/web/standalone/node_modules/postcss/lib/parse.js +1 -1
  218. package/dist/web/standalone/node_modules/postcss/lib/parser.js +10 -9
  219. package/dist/web/standalone/node_modules/postcss/lib/postcss.js +12 -12
  220. package/dist/web/standalone/node_modules/postcss/lib/previous-map.js +30 -11
  221. package/dist/web/standalone/node_modules/postcss/lib/processor.js +7 -7
  222. package/dist/web/standalone/node_modules/postcss/lib/result.js +5 -5
  223. package/dist/web/standalone/node_modules/postcss/lib/rule.js +6 -6
  224. package/dist/web/standalone/node_modules/postcss/lib/stringifier.js +69 -28
  225. package/dist/web/standalone/node_modules/postcss/lib/tokenize.js +6 -2
  226. package/dist/web/standalone/node_modules/postcss/package.json +48 -48
  227. package/dist/web-mode.d.ts +2 -0
  228. package/dist/web-mode.js +20 -8
  229. package/package.json +17 -11
  230. package/packages/cloud-mcp-gateway/package.json +2 -2
  231. package/packages/contracts/package.json +1 -1
  232. package/packages/daemon/package.json +4 -4
  233. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +2 -0
  234. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  235. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +14 -0
  236. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  237. package/packages/gsd-agent-core/package.json +5 -5
  238. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  239. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +3 -0
  240. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  241. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +1 -1
  242. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  243. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  244. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +106 -40
  245. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  246. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.d.ts.map +1 -1
  247. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js +6 -0
  248. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js.map +1 -1
  249. package/packages/gsd-agent-modes/package.json +7 -7
  250. package/packages/mcp-server/dist/server.d.ts +10 -0
  251. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  252. package/packages/mcp-server/dist/server.js +8 -0
  253. package/packages/mcp-server/dist/server.js.map +1 -1
  254. package/packages/mcp-server/dist/workflow-tools.d.ts +41 -0
  255. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  256. package/packages/mcp-server/dist/workflow-tools.js +2 -1
  257. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  258. package/packages/mcp-server/package.json +3 -3
  259. package/packages/native/package.json +1 -1
  260. package/packages/pi-agent-core/package.json +1 -1
  261. package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
  262. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  263. package/packages/pi-ai/dist/image-models.generated.js +30 -0
  264. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  265. package/packages/pi-ai/dist/models.generated.d.ts +8 -127
  266. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  267. package/packages/pi-ai/dist/models.generated.js +47 -166
  268. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  269. package/packages/pi-ai/package.json +1 -1
  270. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  271. package/packages/pi-coding-agent/dist/core/auth-storage.js +11 -3
  272. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  273. package/packages/pi-coding-agent/package.json +7 -7
  274. package/packages/pi-tui/dist/components/input.js +1 -1
  275. package/packages/pi-tui/dist/components/input.js.map +1 -1
  276. package/packages/pi-tui/dist/keys.d.ts.map +1 -1
  277. package/packages/pi-tui/dist/keys.js +39 -30
  278. package/packages/pi-tui/dist/keys.js.map +1 -1
  279. package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  280. package/packages/pi-tui/dist/stdin-buffer.js +22 -0
  281. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
  282. package/packages/pi-tui/package.json +2 -2
  283. package/packages/rpc-client/package.json +2 -2
  284. package/pkg/package.json +1 -1
  285. package/scripts/install/deps.js +10 -0
  286. package/scripts/link-workspace-packages.cjs +7 -40
  287. package/src/resources/extensions/ask-user-questions.ts +87 -24
  288. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +126 -289
  289. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +242 -2
  290. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +287 -0
  291. package/src/resources/extensions/github-sync/templates.ts +3 -3
  292. package/src/resources/extensions/github-sync/tests/templates.test.ts +2 -2
  293. package/src/resources/extensions/gsd/artifact-projection.ts +31 -0
  294. package/src/resources/extensions/gsd/auto/contracts.ts +40 -121
  295. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
  296. package/src/resources/extensions/gsd/auto/loop.ts +83 -61
  297. package/src/resources/extensions/gsd/auto/orchestrator.ts +913 -64
  298. package/src/resources/extensions/gsd/auto/phases.ts +35 -3
  299. package/src/resources/extensions/gsd/auto/run-unit.ts +2 -1
  300. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  301. package/src/resources/extensions/gsd/auto-dashboard.ts +18 -4
  302. package/src/resources/extensions/gsd/auto-dispatch.ts +20 -7
  303. package/src/resources/extensions/gsd/auto-model-selection.ts +8 -0
  304. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -3
  305. package/src/resources/extensions/gsd/auto-prompts.ts +220 -9
  306. package/src/resources/extensions/gsd/auto-recovery.ts +50 -50
  307. package/src/resources/extensions/gsd/auto-runtime-state.ts +30 -0
  308. package/src/resources/extensions/gsd/auto-start.ts +17 -20
  309. package/src/resources/extensions/gsd/auto-timers.ts +16 -2
  310. package/src/resources/extensions/gsd/auto-tool-tracking.ts +40 -0
  311. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +42 -30
  312. package/src/resources/extensions/gsd/auto-verification.ts +7 -8
  313. package/src/resources/extensions/gsd/auto-worktree.ts +57 -42
  314. package/src/resources/extensions/gsd/auto.ts +96 -508
  315. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +29 -37
  316. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +10 -37
  317. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
  318. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +120 -151
  319. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +107 -3
  320. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +27 -5
  321. package/src/resources/extensions/gsd/codebase-generator.ts +9 -5
  322. package/src/resources/extensions/gsd/commands/handlers/auto.ts +3 -0
  323. package/src/resources/extensions/gsd/commands-handlers.ts +18 -0
  324. package/src/resources/extensions/gsd/commands-inspect.ts +7 -8
  325. package/src/resources/extensions/gsd/commands-maintenance.ts +74 -40
  326. package/src/resources/extensions/gsd/commands-ship.ts +2 -2
  327. package/src/resources/extensions/gsd/commands-verdict.ts +19 -2
  328. package/src/resources/extensions/gsd/db-workspace.ts +170 -0
  329. package/src/resources/extensions/gsd/debug-logger.ts +11 -0
  330. package/src/resources/extensions/gsd/delegation-policy.ts +3 -11
  331. package/src/resources/extensions/gsd/discussion-handoff.ts +276 -0
  332. package/src/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  333. package/src/resources/extensions/gsd/doctor-proactive.ts +8 -2
  334. package/src/resources/extensions/gsd/doctor.ts +15 -5
  335. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  336. package/src/resources/extensions/gsd/git-conflict-state.ts +17 -1
  337. package/src/resources/extensions/gsd/gsd-db.ts +12 -0
  338. package/src/resources/extensions/gsd/guided-flow.ts +49 -560
  339. package/src/resources/extensions/gsd/guided-unit-completion.ts +275 -0
  340. package/src/resources/extensions/gsd/markdown-renderer.ts +40 -20
  341. package/src/resources/extensions/gsd/mcp-filter.ts +9 -1
  342. package/src/resources/extensions/gsd/mcp-tool-name.ts +35 -0
  343. package/src/resources/extensions/gsd/md-importer.ts +3 -3
  344. package/src/resources/extensions/gsd/migrate/safety.ts +2 -2
  345. package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
  346. package/src/resources/extensions/gsd/milestone-closeout-proof.ts +131 -0
  347. package/src/resources/extensions/gsd/milestone-closeout.ts +12 -4
  348. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +47 -0
  349. package/src/resources/extensions/gsd/milestone-planning-persistence.ts +224 -0
  350. package/src/resources/extensions/gsd/milestone-readiness.ts +125 -0
  351. package/src/resources/extensions/gsd/milestone-settlement.ts +81 -0
  352. package/src/resources/extensions/gsd/milestone-validation-evidence.ts +95 -0
  353. package/src/resources/extensions/gsd/milestone-validation-verdict.ts +80 -0
  354. package/src/resources/extensions/gsd/native-git-bridge.ts +48 -0
  355. package/src/resources/extensions/gsd/parallel-eligibility.ts +4 -5
  356. package/src/resources/extensions/gsd/parallel-orchestrator.ts +6 -2
  357. package/src/resources/extensions/gsd/preferences-diagnostics.ts +98 -0
  358. package/src/resources/extensions/gsd/preferences-types.ts +16 -0
  359. package/src/resources/extensions/gsd/preferences.ts +173 -28
  360. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  361. package/src/resources/extensions/gsd/prompts/discuss.md +6 -7
  362. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
  363. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +5 -7
  364. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +6 -6
  365. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +1 -2
  366. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -6
  367. package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  368. package/src/resources/extensions/gsd/prompts/plan-slice.md +2 -1
  369. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  370. package/src/resources/extensions/gsd/prompts/research-milestone.md +2 -2
  371. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  372. package/src/resources/extensions/gsd/prompts/validate-milestone.md +5 -3
  373. package/src/resources/extensions/gsd/provider-payload-policy.ts +140 -0
  374. package/src/resources/extensions/gsd/pull-request-process.ts +41 -0
  375. package/src/resources/extensions/gsd/quality-gate-closure.ts +140 -0
  376. package/src/resources/extensions/gsd/question-transport.ts +138 -0
  377. package/src/resources/extensions/gsd/roadmap-slices.ts +8 -2
  378. package/src/resources/extensions/gsd/schemas/parsers.ts +6 -1
  379. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +6 -2
  380. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +31 -10
  381. package/src/resources/extensions/gsd/state.ts +15 -5
  382. package/src/resources/extensions/gsd/templates/plan.md +7 -0
  383. package/src/resources/extensions/gsd/templates/project.md +1 -0
  384. package/src/resources/extensions/gsd/templates/roadmap.md +1 -1
  385. package/src/resources/extensions/gsd/templates/uat.md +5 -1
  386. package/src/resources/extensions/gsd/tests/artifact-db-drift-memo.test.ts +66 -0
  387. package/src/resources/extensions/gsd/tests/ask-user-questions-render.test.ts +92 -0
  388. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +29 -1
  389. package/src/resources/extensions/gsd/tests/auto-dispatch-baseline-harness.test.ts +53 -0
  390. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +321 -5
  391. package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +23 -0
  392. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +18 -0
  393. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +709 -845
  394. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +38 -10
  395. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +34 -0
  396. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +20 -0
  397. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  398. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +11 -0
  399. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +38 -1
  400. package/src/resources/extensions/gsd/tests/debug-logger.test.ts +15 -0
  401. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +34 -3
  402. package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +88 -0
  403. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +18 -0
  404. package/src/resources/extensions/gsd/tests/execute-summary-save-empty-project.test.ts +64 -1
  405. package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +1 -0
  406. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +1 -5
  407. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +1 -5
  408. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +48 -1
  409. package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +157 -0
  410. package/src/resources/extensions/gsd/tests/markdown-renderer-parse-cache.test.ts +75 -0
  411. package/src/resources/extensions/gsd/tests/mcp-tool-name.test.ts +34 -0
  412. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +58 -0
  413. package/src/resources/extensions/gsd/tests/milestone-closeout-proof.test.ts +99 -0
  414. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +25 -0
  415. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +46 -0
  416. package/src/resources/extensions/gsd/tests/milestone-readiness.test.ts +65 -0
  417. package/src/resources/extensions/gsd/tests/milestone-validation-evidence.test.ts +41 -0
  418. package/src/resources/extensions/gsd/tests/milestone-validation-verdict.test.ts +55 -0
  419. package/src/resources/extensions/gsd/tests/native-merge-regular.test.ts +139 -0
  420. package/src/resources/extensions/gsd/tests/orchestrator-legacy-parity.test.ts +127 -0
  421. package/src/resources/extensions/gsd/tests/parse-project-milestone-bridge.test.ts +77 -0
  422. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +45 -0
  423. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +6 -2
  424. package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +45 -0
  425. package/src/resources/extensions/gsd/tests/preferences-diagnostics.test.ts +67 -0
  426. package/src/resources/extensions/gsd/tests/preferences.test.ts +183 -0
  427. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +75 -2
  428. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +9 -0
  429. package/src/resources/extensions/gsd/tests/provider-payload-policy.test.ts +165 -0
  430. package/src/resources/extensions/gsd/tests/pull-request-process.test.ts +47 -0
  431. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +94 -0
  432. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +65 -0
  433. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +40 -0
  434. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +25 -1
  435. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +80 -0
  436. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +101 -1
  437. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +27 -0
  438. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +21 -6
  439. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +38 -0
  440. package/src/resources/extensions/gsd/tests/tool-availability-audit.test.ts +35 -0
  441. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +35 -42
  442. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +23 -0
  443. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +47 -0
  444. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +147 -0
  445. package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +39 -0
  446. package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +150 -0
  447. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +126 -9
  448. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +15 -0
  449. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +21 -0
  450. package/src/resources/extensions/gsd/tests/worktree-projection-writers.test.ts +1 -1
  451. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +24 -0
  452. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +22 -0
  453. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -3
  454. package/src/resources/extensions/gsd/tests/write-gate.test.ts +79 -0
  455. package/src/resources/extensions/gsd/tool-contract.ts +86 -8
  456. package/src/resources/extensions/gsd/tool-presentation-plan.ts +16 -33
  457. package/src/resources/extensions/gsd/tool-surface-snapshot.ts +47 -0
  458. package/src/resources/extensions/gsd/tools/plan-milestone.ts +19 -160
  459. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +43 -0
  460. package/src/resources/extensions/gsd/tools/validate-milestone.ts +25 -84
  461. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +183 -21
  462. package/src/resources/extensions/gsd/uat-policy.ts +19 -10
  463. package/src/resources/extensions/gsd/uat-run.ts +10 -14
  464. package/src/resources/extensions/gsd/unit-context-composer.ts +85 -20
  465. package/src/resources/extensions/gsd/unit-runtime.ts +3 -2
  466. package/src/resources/extensions/gsd/unit-tool-contracts.ts +2 -1
  467. package/src/resources/extensions/gsd/user-input-boundary.ts +55 -5
  468. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
  469. package/src/resources/extensions/gsd/web-app-uat.ts +101 -0
  470. package/src/resources/extensions/gsd/workflow-mcp.ts +22 -110
  471. package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
  472. package/src/resources/extensions/gsd/workflow-tool-surface.ts +73 -0
  473. package/src/resources/extensions/gsd/workspace-git-guard.ts +1 -0
  474. package/src/resources/extensions/gsd/worktree-lifecycle.ts +7 -16
  475. package/src/resources/extensions/gsd/worktree-state-projection.ts +55 -7
  476. package/src/resources/extensions/gsd/worktree-telemetry.ts +16 -0
  477. package/src/resources/extensions/shared/interview-ui.ts +15 -2
  478. package/src/resources/shared/claude-runtime-floor.ts +248 -0
  479. package/dist/web/standalone/.next/server/chunks/678.js +0 -2
  480. package/dist/web/standalone/.next/static/chunks/2659.feb6499ca863ebfc.js +0 -1
  481. package/dist/web/standalone/.next/static/chunks/2772.151789db0edea835.js +0 -1
  482. package/dist/web/standalone/.next/static/chunks/4283.10a065467b5340d8.js +0 -2
  483. package/dist/web/standalone/.next/static/chunks/5826.960dc4634cc9b0d3.js +0 -1
  484. package/dist/web/standalone/.next/static/chunks/796.46f811c0fac23aab.js +0 -10
  485. package/dist/web/standalone/.next/static/chunks/8785.d32f7a61f55c1600.js +0 -1
  486. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts +0 -21
  487. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts.map +0 -1
  488. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js +0 -213
  489. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js.map +0 -1
  490. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.d.ts +0 -28
  491. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.d.ts.map +0 -1
  492. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.js +0 -249
  493. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-density-prototype.js.map +0 -1
  494. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.d.ts +0 -19
  495. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.d.ts.map +0 -1
  496. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.js +0 -797
  497. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/transcript-design-prototype.js.map +0 -1
  498. package/scripts/ensure-workspace-builds.cjs +0 -129
  499. /package/dist/web/standalone/.next/static/{tJOKQbQRO-9MiFDO8DIDS → tkLHUSzPA2kMmWz4DmGwI}/_buildManifest.js +0 -0
  500. /package/dist/web/standalone/.next/static/{tJOKQbQRO-9MiFDO8DIDS → tkLHUSzPA2kMmWz4DmGwI}/_ssgManifest.js +0 -0
@@ -6,10 +6,11 @@ import { sanitizeCompleteMilestoneParams } from "../bootstrap/sanitize-complete-
6
6
  import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot, shouldBlockRootArtifactSaveInSnapshot } from "../bootstrap/write-gate.js";
7
7
  import {
8
8
  getActiveRequirements,
9
- insertMilestone,
9
+ getAllMilestones,
10
10
  getMilestone,
11
11
  getSliceStatusSummary,
12
12
  getSliceTaskCounts,
13
+ insertMilestone,
13
14
  insertAssessment,
14
15
  insertGateRun,
15
16
  readTransaction,
@@ -126,6 +127,111 @@ function registerProjectMilestoneSequence(content: string): string[] {
126
127
  return registered;
127
128
  }
128
129
 
130
+ /** Minimal shape of a DB milestone row needed to re-render the sequence section. */
131
+ interface MilestoneSeqRow {
132
+ id: string;
133
+ title: string;
134
+ status: string;
135
+ vision: string;
136
+ }
137
+
138
+ /**
139
+ * Best-effort recovery of the human one-liner for each milestone id from a
140
+ * (possibly malformed) Milestone Sequence body. Deliberately lenient: tolerates
141
+ * any separator the canonical MILESTONE_LINE_RE rejects (en-dash, " : ", a
142
+ * missing checkbox, etc.) so a model formatting slip does not discard the prose.
143
+ */
144
+ function recoverMilestoneTails(sequenceBody: string): Map<string, string> {
145
+ const out = new Map<string, string>();
146
+ const lenient = /^\s*(?:-\s*)?(?:\[[ xX]\]\s*)?(M\d{3})\b\s*[:.\-–—]*\s*(.*)$/;
147
+ for (const rawLine of sequenceBody.split("\n")) {
148
+ const m = rawLine.match(lenient);
149
+ if (m) out.set(m[1], m[2].trim());
150
+ }
151
+ return out;
152
+ }
153
+
154
+ function firstSentence(text: string): string {
155
+ const trimmed = text.trim();
156
+ if (!trimmed) return "";
157
+ const idx = trimmed.search(/[.!?](\s|$)/);
158
+ return (idx >= 0 ? trimmed.slice(0, idx + 1) : trimmed).trim();
159
+ }
160
+
161
+ /** Render one canonical, parseable milestone line for the given DB row. */
162
+ function renderMilestoneLine(m: MilestoneSeqRow, recoveredTail: string): string {
163
+ const done = m.status === "complete";
164
+ let oneLiner = recoveredTail;
165
+ // The recovered tail often still carries the title (e.g. "Foo — bar" or
166
+ // "Foo : bar"). Strip a leading repetition of the title, then any separator.
167
+ if (oneLiner.toLowerCase().startsWith(m.title.toLowerCase())) {
168
+ oneLiner = oneLiner.slice(m.title.length).replace(/^\s*[:.\-–—]+\s*/, "").trim();
169
+ } else {
170
+ const sep = oneLiner.match(/\s+(?:—|–|--|-|:)\s+/);
171
+ if (sep && sep.index !== undefined) oneLiner = oneLiner.slice(sep.index + sep[0].length).trim();
172
+ }
173
+ // MILESTONE_LINE_RE requires non-empty prose after the separator.
174
+ if (!oneLiner) oneLiner = firstSentence(m.vision) || (done ? "Completed." : "Planned.");
175
+ return `- [${done ? "x" : " "}] ${m.id}: ${m.title} — ${oneLiner}`;
176
+ }
177
+
178
+ /**
179
+ * Rebuild the "## Milestone Sequence" section from authoritative DB rows when a
180
+ * model-authored PROJECT.md projection parsed to zero milestone lines but the DB
181
+ * already holds milestones. The DB is the source of truth (markdown is a
182
+ * projection), so this repairs the projection rather than failing the save.
183
+ * Preserves a leading HTML comment in the section and recovers one-liners
184
+ * best-effort. The returned content parses cleanly under MILESTONE_LINE_RE.
185
+ */
186
+ function rebuildMilestoneSequenceSection(content: string, milestones: MilestoneSeqRow[]): string {
187
+ const lines = content.split("\n");
188
+ const headerIdx = lines.findIndex(l => /^##\s+Milestone Sequence\s*$/.test(l));
189
+
190
+ const canonicalLines = (() => {
191
+ // Recover tails from the existing (malformed) body when the section exists.
192
+ let body = "";
193
+ if (headerIdx !== -1) {
194
+ let end = headerIdx + 1;
195
+ while (end < lines.length && !/^##\s+/.test(lines[end])) end++;
196
+ body = lines.slice(headerIdx + 1, end).join("\n");
197
+ }
198
+ const tails = recoverMilestoneTails(body);
199
+ return milestones.map(m => renderMilestoneLine(m, tails.get(m.id) ?? ""));
200
+ })();
201
+
202
+ if (headerIdx === -1) {
203
+ // No section at all — append a fresh, canonical one.
204
+ const sep = content.endsWith("\n") ? "" : "\n";
205
+ return `${content}${sep}\n## Milestone Sequence\n\n${canonicalLines.join("\n")}\n`;
206
+ }
207
+
208
+ let bodyEnd = headerIdx + 1;
209
+ while (bodyEnd < lines.length && !/^##\s+/.test(lines[bodyEnd])) bodyEnd++;
210
+ const existingBody = lines.slice(headerIdx + 1, bodyEnd);
211
+
212
+ // Preserve a contiguous leading HTML comment block (the "Check off…" hint).
213
+ let i = 0;
214
+ while (i < existingBody.length && existingBody[i].trim() === "") i++;
215
+ const preserved: string[] = [];
216
+ if (i < existingBody.length && existingBody[i].trim().startsWith("<!--")) {
217
+ while (i < existingBody.length) {
218
+ preserved.push(existingBody[i]);
219
+ const closed = existingBody[i].includes("-->");
220
+ i++;
221
+ if (closed) break;
222
+ }
223
+ }
224
+
225
+ return [
226
+ ...lines.slice(0, headerIdx + 1),
227
+ "",
228
+ ...(preserved.length ? [...preserved, ""] : []),
229
+ ...canonicalLines,
230
+ "",
231
+ ...lines.slice(bodyEnd),
232
+ ].join("\n");
233
+ }
234
+
129
235
  async function mirrorArtifactToActiveWorktreeProjection(
130
236
  basePath: string,
131
237
  relativePath: string,
@@ -260,6 +366,7 @@ export async function executeSummarySave(
260
366
  await mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, contentToSave);
261
367
 
262
368
  let registeredMilestones: string[] = [];
369
+ let milestoneSequenceSelfHealed = false;
263
370
  if (params.artifact_type === "PROJECT") {
264
371
  try {
265
372
  registeredMilestones = registerProjectMilestoneSequence(contentToSave);
@@ -294,29 +401,83 @@ export async function executeSummarySave(
294
401
  };
295
402
  }
296
403
  if (registeredMilestones.length === 0) {
297
- logError("tool", `gsd_summary_save: PROJECT.md saved to ${relativePath} but parsed zero milestones — registration produced no DB rows`, {
404
+ const existingMilestones = getAllMilestones();
405
+ if (existingMilestones.length === 0) {
406
+ // Genuine first-save failure: no milestones parsed AND none in the DB.
407
+ // /gsd really would report "No Active Milestone" — hard-fail so the
408
+ // caller rewrites the sequence before proceeding.
409
+ logError("tool", `gsd_summary_save: PROJECT.md saved to ${relativePath} but parsed zero milestones — registration produced no DB rows`, {
410
+ tool: "gsd_summary_save",
411
+ });
412
+ // PROJECT.md was persisted; invalidate so subsequent reads see the new
413
+ // artifacts row even though no milestones registered.
414
+ invalidateStateCache();
415
+ return {
416
+ content: [{
417
+ type: "text",
418
+ text:
419
+ `Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
420
+ `so no milestones were registered in the DB. /gsd will report "No Active Milestone". ` +
421
+ `Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
422
+ `\`- [ ] M001: <Title> — <One-liner>\` (em-dash, double-dash \`--\`, or single-dash \`-\` separator), then re-call gsd_summary_save(PROJECT).`,
423
+ }],
424
+ details: {
425
+ operation: "save_summary",
426
+ path: relativePath,
427
+ artifact_type: params.artifact_type,
428
+ error: "milestone_registration_empty_parse",
429
+ },
430
+ isError: true,
431
+ };
432
+ }
433
+
434
+ // Existing DB rows mean this is projection drift, not data loss. Rebuild
435
+ // the section from DB state and re-persist a parseable projection.
436
+ logWarning("tool", `gsd_summary_save: PROJECT.md parsed zero milestone lines but DB has ${existingMilestones.length} — rebuilding Milestone Sequence from DB (projection self-heal)`, {
298
437
  tool: "gsd_summary_save",
438
+ path: relativePath,
299
439
  });
300
- // PROJECT.md was persisted; invalidate so subsequent reads see the new
301
- // artifacts row even though no milestones registered.
302
- invalidateStateCache();
303
- return {
304
- content: [{
305
- type: "text",
306
- text:
307
- `Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
308
- `so no milestones were registered in the DB. /gsd will report "No Active Milestone". ` +
309
- `Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
310
- `\`- [ ] M001: <Title> — <One-liner>\` (em-dash, double-dash \`--\`, or single-dash \`-\` separator), then re-call gsd_summary_save(PROJECT).`,
311
- }],
312
- details: {
313
- operation: "save_summary",
440
+ try {
441
+ const healed = rebuildMilestoneSequenceSection(contentToSave, existingMilestones);
442
+ await saveArtifactToDb(
443
+ { path: relativePath, artifact_type: params.artifact_type, content: healed },
444
+ basePath,
445
+ );
446
+ await mirrorArtifactToActiveWorktreeProjection(basePath, relativePath, healed);
447
+ const healedRegisteredMilestones = registerProjectMilestoneSequence(healed);
448
+ if (healedRegisteredMilestones.length === 0) {
449
+ throw new Error("self-healed PROJECT.md still parsed zero milestone lines");
450
+ }
451
+ registeredMilestones = healedRegisteredMilestones;
452
+ milestoneSequenceSelfHealed = true;
453
+ } catch (healErr) {
454
+ const msg = healErr instanceof Error ? healErr.message : String(healErr);
455
+ logError("tool", `gsd_summary_save: Milestone Sequence self-heal failed: ${msg}`, {
456
+ tool: "gsd_summary_save",
314
457
  path: relativePath,
315
- artifact_type: params.artifact_type,
316
- error: "milestone_registration_empty_parse",
317
- },
318
- isError: true,
319
- };
458
+ error: msg,
459
+ });
460
+ invalidateStateCache();
461
+ return {
462
+ content: [{
463
+ type: "text",
464
+ text:
465
+ `Error: PROJECT.md was saved to ${relativePath} but contains zero parseable milestone lines, ` +
466
+ `and automatic DB-backed Milestone Sequence repair failed: ${msg}. ` +
467
+ `Rewrite PROJECT.md so the "Milestone Sequence" section uses canonical lines: ` +
468
+ `\`- [ ] M001: <Title> — <One-liner>\`, then re-call gsd_summary_save(PROJECT).`,
469
+ }],
470
+ details: {
471
+ operation: "save_summary",
472
+ path: relativePath,
473
+ artifact_type: params.artifact_type,
474
+ error: "milestone_sequence_self_heal_failed",
475
+ self_heal_error: msg,
476
+ },
477
+ isError: true,
478
+ };
479
+ }
480
+ invalidateStateCache();
320
481
  }
321
482
  }
322
483
 
@@ -339,6 +500,7 @@ export async function executeSummarySave(
339
500
  artifact_type: params.artifact_type,
340
501
  content_source: contentSource,
341
502
  ...(registeredMilestones.length > 0 ? { registeredMilestones } : {}),
503
+ ...(milestoneSequenceSelfHealed ? { milestoneSequenceSelfHealed: true } : {}),
342
504
  },
343
505
  };
344
506
  } catch (err) {
@@ -4,6 +4,7 @@
4
4
  import { extractUatType } from "./files.js";
5
5
  import type { UatType } from "./files.js";
6
6
  import { hasBrowserRequiredText } from "./browser-evidence.js";
7
+ import { parseMcpToolName } from "./mcp-tool-name.js";
7
8
 
8
9
  export type { UatType } from "./files.js";
9
10
 
@@ -122,31 +123,39 @@ export function uatTypeIncludesBrowser(uatType: string | undefined): boolean {
122
123
  return isUatType(uatType) && UAT_MODE_POLICIES[uatType].browserTools;
123
124
  }
124
125
 
125
- function canonicalPresentedToolName(toolName: string): string {
126
- if (!toolName.startsWith("mcp__")) return toolName;
127
- const toolSeparator = toolName.indexOf("__", "mcp__".length);
128
- return toolSeparator >= 0 ? toolName.slice(toolSeparator + 2) : toolName;
129
- }
130
-
131
126
  export function isUatBrowserToolName(toolName: string): boolean {
132
- return canonicalPresentedToolName(toolName).startsWith("browser_");
127
+ const parsed = parseMcpToolName(toolName);
128
+ const canonicalName = parsed?.toolName ?? toolName;
129
+ if (canonicalName.startsWith("browser_")) return true;
130
+ return parsed?.toolName === "*" && parsed.serverName.toLowerCase().includes("browser");
133
131
  }
134
132
 
135
133
  export function hasUatBrowserToolSurface(activeTools: readonly string[] | undefined): boolean {
136
134
  return Array.isArray(activeTools) && activeTools.some(isUatBrowserToolName);
137
135
  }
138
136
 
137
+ export function resolveUatBrowserToolSurface(options: {
138
+ activeTools: readonly string[] | undefined;
139
+ registeredTools?: readonly string[] | undefined;
140
+ }): readonly string[] | undefined {
141
+ const surfaces = [options.activeTools, options.registeredTools].filter(Array.isArray);
142
+ if (surfaces.length === 0) return undefined;
143
+ return [...new Set(surfaces.flat())];
144
+ }
145
+
139
146
  export function getUatBrowserToolSupportError(options: {
140
147
  uatType: UatType;
141
148
  activeTools: readonly string[] | undefined;
149
+ registeredTools?: readonly string[] | undefined;
142
150
  milestoneId: string;
143
151
  sliceId: string;
144
152
  }): string | null {
145
153
  if (!uatTypeIncludesBrowser(options.uatType)) return null;
146
- if (!Array.isArray(options.activeTools)) return null;
147
- if (hasUatBrowserToolSurface(options.activeTools)) return null;
154
+ const toolSurface = resolveUatBrowserToolSurface(options);
155
+ if (!toolSurface) return null;
156
+ if (hasUatBrowserToolSurface(toolSurface)) return null;
148
157
 
149
- return `Cannot dispatch browser-backed run-uat for ${options.milestoneId}/${options.sliceId}: UAT mode "${options.uatType}" requires browser tools, but the active tool surface has none. Enable browser tools or change the UAT to a runtime-executable Playwright command, then rerun /gsd auto.`;
158
+ return `Cannot dispatch browser-backed run-uat for ${options.milestoneId}/${options.sliceId}: UAT mode "${options.uatType}" requires browser tools, but the run-uat tool surface has none. Enable browser tools or change the UAT to a runtime-executable Playwright command, then rerun /gsd auto.`;
150
159
  }
151
160
 
152
161
  export function isPartialEligibleUatType(uatType: UatType | undefined): boolean {
@@ -103,7 +103,7 @@ function mergeBlockedTools(
103
103
  ): UatPresentationInput["blockedTools"] {
104
104
  const merged = new Map<string, { name: string; reason: string }>();
105
105
  for (const entry of [...(current ?? []), ...canonical]) {
106
- merged.set(canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name), entry);
106
+ merged.set(canonicalWorkflowToolName(parseMcpToolName(entry.name)?.toolName ?? entry.name), entry);
107
107
  }
108
108
  return [...merged.values()];
109
109
  }
@@ -299,22 +299,18 @@ function quoteToolNames(toolNames: readonly string[]): string {
299
299
  }
300
300
 
301
301
  function validateCanonicalPresentation(params: UatResultSaveParams): string | null {
302
- const aliasHints: Record<string, string> = {
303
- gsd_save_summary: "gsd_summary_save",
304
- gsd_complete_task: "gsd_task_complete",
305
- gsd_complete_slice: "gsd_slice_complete",
306
- gsd_milestone_complete: "gsd_complete_milestone",
307
- };
308
302
  const errors: string[] = [];
309
303
  for (const toolName of params.presentation.presentedTools) {
310
- const baseName = parseMcpToolName(toolName)?.tool ?? toolName;
311
- const canonical = aliasHints[baseName];
312
- if (canonical) errors.push(`presentation tool "${toolName}" uses an alias; use canonical "${canonical}"`);
304
+ const baseName = parseMcpToolName(toolName)?.toolName ?? toolName;
305
+ const canonical = canonicalWorkflowToolName(baseName);
306
+ if (canonical !== baseName) {
307
+ errors.push(`presentation tool "${toolName}" uses an alias; use canonical "${canonical}"`);
308
+ }
313
309
  }
314
310
 
315
311
  const presentedCanonical = new Set(
316
312
  params.presentation.presentedTools.map((toolName) =>
317
- canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)
313
+ canonicalWorkflowToolName(parseMcpToolName(toolName)?.toolName ?? toolName)
318
314
  ),
319
315
  );
320
316
  const missingRequiredTools = RUN_UAT_WORKFLOW_TOOL_NAMES.filter(
@@ -329,11 +325,11 @@ function validateCanonicalPresentation(params: UatResultSaveParams): string | nu
329
325
  const forbiddenCanonical = new Set(
330
326
  RUN_UAT_FORBIDDEN_TOOL_NAMES
331
327
  .filter((toolName) => !toolName.includes("*"))
332
- .map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName)),
328
+ .map((toolName) => canonicalWorkflowToolName(parseMcpToolName(toolName)?.toolName ?? toolName)),
333
329
  );
334
330
  const forbiddenPresentedTools: string[] = [];
335
331
  for (const toolName of params.presentation.presentedTools) {
336
- const canonical = canonicalWorkflowToolName(parseMcpToolName(toolName)?.tool ?? toolName);
332
+ const canonical = canonicalWorkflowToolName(parseMcpToolName(toolName)?.toolName ?? toolName);
337
333
  if (toolName === "mcp__gsd-workflow__*" || forbiddenCanonical.has(canonical)) {
338
334
  forbiddenPresentedTools.push(toolName);
339
335
  }
@@ -346,7 +342,7 @@ function validateCanonicalPresentation(params: UatResultSaveParams): string | nu
346
342
 
347
343
  const blockedCanonical = new Set(
348
344
  params.presentation.blockedTools.map((entry) =>
349
- canonicalWorkflowToolName(parseMcpToolName(entry.name)?.tool ?? entry.name)
345
+ canonicalWorkflowToolName(parseMcpToolName(entry.name)?.toolName ?? entry.name)
350
346
  ),
351
347
  );
352
348
  const missingBlockedTools = ["gsd_exec", "gsd_summary_save", "gsd_save_gate_result"].filter(
@@ -45,6 +45,7 @@ import {
45
45
  type ContextModePolicy,
46
46
  type UnitContextManifest,
47
47
  } from "./unit-context-manifest.js";
48
+ import type { UnitPromptContextContract } from "./tool-contract.js";
48
49
 
49
50
  /**
50
51
  * Async function mapping an artifact key to its inlined-content string,
@@ -195,8 +196,32 @@ export interface ComposedUnitContext {
195
196
  readonly inline: string;
196
197
  }
197
198
 
199
+ export type UnitContextBlockMode = "prepend" | "inline" | "excerpt" | "computed";
200
+
201
+ export interface ComposedUnitContextBlock {
202
+ readonly key: string;
203
+ readonly mode: UnitContextBlockMode;
204
+ readonly body: string;
205
+ }
206
+
207
+ export interface ComposedContractedUnitContext extends ComposedUnitContext {
208
+ readonly blocks: readonly ComposedUnitContextBlock[];
209
+ readonly onDemand: readonly ArtifactKey[];
210
+ }
211
+
198
212
  const SECTION_SEPARATOR = "\n\n---\n\n";
199
213
 
214
+ interface UnitContextCompositionContract {
215
+ readonly unitType: string;
216
+ readonly artifacts: {
217
+ readonly inline: readonly ArtifactKey[];
218
+ readonly excerpt: readonly ArtifactKey[];
219
+ readonly onDemand: readonly ArtifactKey[];
220
+ readonly computed: readonly ComputedArtifactId[];
221
+ readonly prepend: readonly ComputedArtifactId[];
222
+ };
223
+ }
224
+
200
225
  /**
201
226
  * Compose all manifest-declared context for a unit type using the v2
202
227
  * surface. Walks `prepend` first (computed-only), then the `inline` list
@@ -220,36 +245,73 @@ export async function composeUnitContext(
220
245
  const manifest: UnitContextManifest | null = resolveManifest(unitType);
221
246
  if (!manifest) return { prepend: "", inline: "" };
222
247
 
223
- // Single-source `unitType`: the manifest is resolved against the
248
+ const composed = await composeDeclaredUnitContext({
249
+ unitType,
250
+ artifacts: {
251
+ inline: manifest.artifacts.inline,
252
+ excerpt: manifest.artifacts.excerpt,
253
+ onDemand: manifest.artifacts.onDemand,
254
+ computed: manifest.artifacts.computed ?? [],
255
+ prepend: manifest.prepend ?? [],
256
+ },
257
+ }, opts);
258
+ return {
259
+ prepend: composed.prepend,
260
+ inline: composed.inline,
261
+ };
262
+ }
263
+
264
+ export async function composeContractedUnitContext(
265
+ contract: UnitPromptContextContract,
266
+ opts: ComposeUnitContextOptions,
267
+ ): Promise<ComposedContractedUnitContext> {
268
+ return composeDeclaredUnitContext(contract, opts);
269
+ }
270
+
271
+ async function composeDeclaredUnitContext(
272
+ contract: UnitContextCompositionContract,
273
+ opts: ComposeUnitContextOptions,
274
+ ): Promise<ComposedContractedUnitContext> {
275
+ // Single-source `unitType`: contract/manifest selection comes from the
224
276
  // function arg, but computed builders read it from `base.unitType`.
225
- // If those ever diverge (caller passes one type to composeUnitContext
226
- // but a different one in opts.base), the composer would silently
227
- // mix one unit's manifest with another unit's computed context.
228
- // Normalize here so the composer dispatches a consistent identity
229
- // through to every builder.
277
+ // Normalize here so every builder sees the same Unit identity.
230
278
  const normalizedOpts: ComposeUnitContextOptions = {
231
279
  ...opts,
232
- base: { ...opts.base, unitType },
280
+ base: { ...opts.base, unitType: contract.unitType },
233
281
  };
234
282
 
235
- const prependBlocks = await runComputed(manifest.prepend ?? [], normalizedOpts);
236
- const inlineBlocks: string[] = [];
283
+ const prependBlocks = await runComputedBlocks(
284
+ contract.artifacts.prepend,
285
+ normalizedOpts,
286
+ "prepend",
287
+ );
288
+ const inlineBlocks: ComposedUnitContextBlock[] = [];
237
289
 
238
- for (const key of manifest.artifacts.inline) {
290
+ for (const key of contract.artifacts.inline) {
239
291
  if (!normalizedOpts.resolveArtifact) break;
240
292
  const body = await normalizedOpts.resolveArtifact(key);
241
- if (body && body.length > 0) inlineBlocks.push(body);
293
+ if (body && body.length > 0) {
294
+ inlineBlocks.push({ key, mode: "inline", body });
295
+ }
242
296
  }
243
- for (const key of manifest.artifacts.excerpt) {
297
+ for (const key of contract.artifacts.excerpt) {
244
298
  if (!normalizedOpts.resolveExcerpt) break;
245
299
  const body = await normalizedOpts.resolveExcerpt(key);
246
- if (body && body.length > 0) inlineBlocks.push(body);
300
+ if (body && body.length > 0) {
301
+ inlineBlocks.push({ key, mode: "excerpt", body });
302
+ }
247
303
  }
248
- inlineBlocks.push(...await runComputed(manifest.artifacts.computed ?? [], normalizedOpts));
304
+ inlineBlocks.push(...await runComputedBlocks(
305
+ contract.artifacts.computed,
306
+ normalizedOpts,
307
+ "computed",
308
+ ));
249
309
 
250
310
  return {
251
- prepend: prependBlocks.join(SECTION_SEPARATOR),
252
- inline: inlineBlocks.join(SECTION_SEPARATOR),
311
+ prepend: prependBlocks.map((block) => block.body).join(SECTION_SEPARATOR),
312
+ inline: inlineBlocks.map((block) => block.body).join(SECTION_SEPARATOR),
313
+ blocks: [...prependBlocks, ...inlineBlocks],
314
+ onDemand: contract.artifacts.onDemand,
253
315
  };
254
316
  }
255
317
 
@@ -258,10 +320,11 @@ export async function composeUnitContext(
258
320
  * Missing registry entries (manifest declares the id but caller didn't
259
321
  * register it) are skipped silently — see composeUnitContext rationale.
260
322
  */
261
- async function runComputed(
323
+ async function runComputedBlocks(
262
324
  ids: readonly ComputedArtifactId[],
263
325
  opts: ComposeUnitContextOptions,
264
- ): Promise<string[]> {
326
+ mode: Extract<UnitContextBlockMode, "prepend" | "computed">,
327
+ ): Promise<ComposedUnitContextBlock[]> {
265
328
  if (ids.length === 0 || !opts.computed) return [];
266
329
  // Type safety lives at the registration boundary (caller-supplied
267
330
  // `computed` is typed against ComputedArtifactInputs[K] per id). Inside
@@ -274,12 +337,14 @@ async function runComputed(
274
337
  inputs: unknown;
275
338
  };
276
339
  const registry = opts.computed as Record<string, AnyEntry | undefined>;
277
- const out: string[] = [];
340
+ const out: ComposedUnitContextBlock[] = [];
278
341
  for (const id of ids) {
279
342
  const entry = registry[id];
280
343
  if (!entry) continue;
281
344
  const body = await entry.build(entry.inputs, opts.base);
282
- if (body && body.length > 0) out.push(body);
345
+ if (body && body.length > 0) {
346
+ out.push({ key: id, mode, body });
347
+ }
283
348
  }
284
349
  return out;
285
350
  }
@@ -10,7 +10,8 @@ import {
10
10
  } from "./paths.js";
11
11
  import { loadFile, parseTaskPlanMustHaves, countMustHavesMentionedInSummary } from "./files.js";
12
12
  import { parseUnitId } from "./unit-id.js";
13
- import { getTask, isDbAvailable, refreshOpenDatabaseFromDisk } from "./gsd-db.js";
13
+ import { getTask, isDbAvailable } from "./gsd-db.js";
14
+ import { refreshWorkflowDatabaseFromDisk } from "./db-workspace.js";
14
15
  import { isClosedStatus } from "./status-guards.js";
15
16
 
16
17
  // Per-record advisory lock — prevents read-modify-write races between
@@ -218,7 +219,7 @@ export async function inspectExecuteTaskDurability(
218
219
  const nextActionAdvanced = !new RegExp(`Execute ${tid}\\b`).test(stateContent);
219
220
  let dbComplete = false;
220
221
  if (isDbAvailable()) {
221
- refreshOpenDatabaseFromDisk();
222
+ refreshWorkflowDatabaseFromDisk();
222
223
  const task = getTask(mid, sid, tid);
223
224
  dbComplete = !!task && isClosedStatus(task.status);
224
225
  }
@@ -71,6 +71,7 @@ export const UNIT_TOOL_CONTRACTS: Record<string, UnitToolSurfaceContract> = {
71
71
  "gsd_milestone_generate_id",
72
72
  ],
73
73
  requiredWorkflowTools: [
74
+ "ask_user_questions",
74
75
  "gsd_summary_save",
75
76
  "gsd_requirement_save",
76
77
  "gsd_requirement_update",
@@ -80,7 +81,7 @@ export const UNIT_TOOL_CONTRACTS: Record<string, UnitToolSurfaceContract> = {
80
81
  },
81
82
  "discuss-slice": {
82
83
  allowedGsdTools: ["gsd_summary_save", "gsd_decision_save"],
83
- requiredWorkflowTools: ["gsd_summary_save"],
84
+ requiredWorkflowTools: ["ask_user_questions", "gsd_summary_save"],
84
85
  },
85
86
  "validate-milestone": {
86
87
  allowedGsdTools: ["gsd_milestone_status", "gsd_validate_milestone", "gsd_reassess_roadmap", "subagent"],
@@ -23,7 +23,7 @@ const APPROVAL_CHANGE_QUESTION_RE =
23
23
  const RESEARCH_DECISION_QUESTION_RE =
24
24
  /\b(?:research|skip)\b/i;
25
25
 
26
- function extractTextFromMessage(msg: unknown): string {
26
+ function extractVisibleTextFromMessage(msg: unknown): string {
27
27
  if (!msg || typeof msg !== "object") return "";
28
28
  const content = (msg as { content?: unknown }).content;
29
29
  if (typeof content === "string") return content;
@@ -35,6 +35,26 @@ function extractTextFromMessage(msg: unknown): string {
35
35
  if (typed.type === "text" && typeof typed.text === "string") {
36
36
  parts.push(typed.text);
37
37
  }
38
+ // thinking blocks intentionally excluded — they are internal reasoning, not user-visible
39
+ }
40
+ return parts.join("\n");
41
+ }
42
+
43
+ function extractTextFromMessage(msg: unknown): string {
44
+ if (!msg || typeof msg !== "object") return "";
45
+ const content = (msg as { content?: unknown }).content;
46
+ if (typeof content === "string") return content;
47
+ if (!Array.isArray(content)) return "";
48
+ const parts: string[] = [];
49
+ for (const block of content) {
50
+ if (!block || typeof block !== "object") continue;
51
+ const typed = block as { type?: unknown; text?: unknown; thinking?: unknown };
52
+ if (typed.type === "text" && typeof typed.text === "string") {
53
+ parts.push(typed.text);
54
+ }
55
+ if (typed.type === "thinking" && typeof typed.thinking === "string") {
56
+ parts.push(typed.thinking);
57
+ }
38
58
  }
39
59
  return parts.join("\n");
40
60
  }
@@ -51,12 +71,24 @@ export function lastAssistantText(messages: unknown[] | null | undefined): strin
51
71
  return "";
52
72
  }
53
73
 
74
+ function lastAssistantVisibleText(messages: unknown[] | null | undefined): string {
75
+ if (!Array.isArray(messages)) return "";
76
+ for (let i = messages.length - 1; i >= 0; i--) {
77
+ const msg = messages[i];
78
+ if (!msg || typeof msg !== "object") continue;
79
+ if ((msg as { role?: unknown }).role !== "assistant") continue;
80
+ const text = extractVisibleTextFromMessage(msg).trim();
81
+ if (text) return text;
82
+ }
83
+ return "";
84
+ }
85
+
54
86
  function anyMessageMatches(messages: unknown[] | undefined, pattern: RegExp): boolean {
55
87
  if (!Array.isArray(messages)) return false;
56
88
  return messages.some((msg) => {
57
89
  if (!msg || typeof msg !== "object") return false;
58
90
  if ((msg as { role?: unknown }).role === "user") return false;
59
- return pattern.test(extractTextFromMessage(msg));
91
+ return pattern.test(extractVisibleTextFromMessage(msg));
60
92
  });
61
93
  }
62
94
 
@@ -134,7 +166,7 @@ export function isExplicitApprovalResponse(
134
166
  export function isAwaitingUserInput(messages: unknown[] | undefined): boolean {
135
167
  if (anyMessageMatches(messages, /ask_user_questions was cancelled before receiving a response/i)) return true;
136
168
  if (anyMessageMatches(messages, REMOTE_QUESTION_FAILURE_RE)) return true;
137
- const text = lastAssistantText(messages);
169
+ const text = lastAssistantVisibleText(messages);
138
170
  if (!text) return false;
139
171
  if (APPROVAL_WAIT_RE.test(text)) return true;
140
172
  const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
@@ -145,12 +177,30 @@ export function isAwaitingUserInput(messages: unknown[] | undefined): boolean {
145
177
  export function isAwaitingApprovalBoundary(messages: unknown[] | undefined): boolean {
146
178
  if (anyMessageMatches(messages, /ask_user_questions was cancelled before receiving a response/i)) return true;
147
179
  if (anyMessageMatches(messages, REMOTE_QUESTION_FAILURE_RE)) return true;
148
- const text = lastAssistantText(messages);
180
+ const text = lastAssistantVisibleText(messages);
149
181
  if (!text) return false;
150
182
  if (APPROVAL_WAIT_RE.test(text)) return true;
151
183
  return hasApprovalQuestion(text);
152
184
  }
153
185
 
186
+ /** True when an assistant message already has an in-flight ask_user_questions tool call. */
187
+ export function messageHasPendingAskUserQuestionsTool(message: unknown): boolean {
188
+ if (!message || typeof message !== "object") return false;
189
+ const content = (message as { content?: unknown }).content;
190
+ if (!Array.isArray(content)) return false;
191
+ return content.some((block) => {
192
+ if (!block || typeof block !== "object") return false;
193
+ // Claude Code marks completion by attaching externalResult, not by setting state.
194
+ // Streaming blocks often carry no state; serverToolUse is the claude-code-cli MCP path.
195
+ const tool = block as { type?: string; name?: string; state?: string; externalResult?: unknown };
196
+ if (tool.type !== "toolCall" && tool.type !== "serverToolUse") return false;
197
+ const name = String(tool.name ?? "").toLowerCase();
198
+ if (!name.includes("ask_user_questions")) return false;
199
+ if (tool.externalResult !== undefined) return false;
200
+ return tool.state !== "completed" && tool.state !== "done";
201
+ });
202
+ }
203
+
154
204
  export function shouldPauseForUserApprovalQuestion(
155
205
  unitType: string | undefined,
156
206
  messages: unknown[] | undefined,
@@ -158,7 +208,7 @@ export function shouldPauseForUserApprovalQuestion(
158
208
  if (!unitType || !USER_APPROVAL_UNIT_TYPES.has(unitType)) return false;
159
209
  if (anyMessageMatches(messages, /ask_user_questions was cancelled before receiving a response/i)) return true;
160
210
  if (anyMessageMatches(messages, REMOTE_QUESTION_FAILURE_RE)) return true;
161
- const text = lastAssistantText(messages);
211
+ const text = lastAssistantVisibleText(messages);
162
212
  if (!text) return false;
163
213
  if (APPROVAL_WAIT_RE.test(text)) return true;
164
214
  if (unitType === "research-decision") return hasResearchDecisionQuestion(text);