gsd-pi 2.66.1-dev.ed243f2 → 2.67.0-dev.43b0159

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 (287) hide show
  1. package/dist/claude-cli-check.d.ts +8 -0
  2. package/dist/claude-cli-check.js +36 -0
  3. package/dist/cli.js +40 -0
  4. package/dist/onboarding.js +19 -2
  5. package/dist/resources/extensions/ask-user-questions.js +79 -11
  6. package/dist/resources/extensions/claude-code-cli/partial-builder.js +4 -3
  7. package/dist/resources/extensions/claude-code-cli/readiness.js +63 -12
  8. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +10 -3
  9. package/dist/resources/extensions/gsd/auto/loop.js +13 -1
  10. package/dist/resources/extensions/gsd/auto/phases.js +22 -3
  11. package/dist/resources/extensions/gsd/auto/run-unit.js +10 -2
  12. package/dist/resources/extensions/gsd/auto/session.js +1 -1
  13. package/dist/resources/extensions/gsd/auto-dashboard.js +65 -15
  14. package/dist/resources/extensions/gsd/auto-dispatch.js +30 -28
  15. package/dist/resources/extensions/gsd/auto-model-selection.js +12 -3
  16. package/dist/resources/extensions/gsd/auto-prompts.js +173 -25
  17. package/dist/resources/extensions/gsd/auto-recovery.js +11 -12
  18. package/dist/resources/extensions/gsd/auto.js +13 -1
  19. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +32 -1
  20. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +18 -6
  21. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +5 -0
  22. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +59 -5
  23. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +8 -5
  24. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +186 -14
  25. package/dist/resources/extensions/gsd/codebase-generator.js +4 -0
  26. package/dist/resources/extensions/gsd/commands/catalog.js +2 -1
  27. package/dist/resources/extensions/gsd/commands/dispatcher.js +1 -1
  28. package/dist/resources/extensions/gsd/commands/handlers/core.js +94 -4
  29. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +49 -9
  30. package/dist/resources/extensions/gsd/context-store.js +134 -2
  31. package/dist/resources/extensions/gsd/custom-workflow-engine.js +3 -1
  32. package/dist/resources/extensions/gsd/detection.js +6 -0
  33. package/dist/resources/extensions/gsd/files.js +19 -2
  34. package/dist/resources/extensions/gsd/guided-flow.js +12 -8
  35. package/dist/resources/extensions/gsd/index.js +1 -1
  36. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +2 -0
  37. package/dist/resources/extensions/gsd/parsers-legacy.js +3 -1
  38. package/dist/resources/extensions/gsd/preferences.js +6 -1
  39. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  40. package/dist/resources/extensions/gsd/prompts/complete-slice.md +3 -3
  41. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +7 -7
  42. package/dist/resources/extensions/gsd/prompts/discuss.md +3 -3
  43. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -3
  44. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -3
  45. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
  46. package/dist/resources/extensions/gsd/prompts/rethink.md +6 -2
  47. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  48. package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  49. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +11 -9
  50. package/dist/resources/extensions/gsd/prompts/worktree-merge.md +3 -1
  51. package/dist/resources/extensions/gsd/safety/file-change-validator.js +2 -1
  52. package/dist/resources/extensions/gsd/state.js +2 -1
  53. package/dist/resources/extensions/gsd/visualizer-overlay.js +27 -26
  54. package/dist/resources/extensions/gsd/workflow-reconcile.js +46 -7
  55. package/dist/resources/extensions/remote-questions/manager.js +8 -0
  56. package/dist/resources/extensions/shared/interview-ui.js +10 -0
  57. package/dist/web/standalone/.next/BUILD_ID +1 -1
  58. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  59. package/dist/web/standalone/.next/build-manifest.json +3 -3
  60. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  61. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  63. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/index.html +1 -1
  80. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  87. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  90. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  91. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  92. package/dist/web/standalone/.next/static/chunks/{6502.8874bcae249c02e1.js → 6502.b804e48b7919f55e.js} +3 -3
  93. package/dist/web/standalone/.next/static/chunks/{webpack-9fed74684e1c5bb1.js → webpack-65f0501b197d1c49.js} +1 -1
  94. package/package.json +1 -1
  95. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  96. package/packages/pi-ai/dist/providers/anthropic-shared.js +4 -3
  97. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  98. package/packages/pi-ai/dist/utils/json-parse.d.ts.map +1 -1
  99. package/packages/pi-ai/dist/utils/json-parse.js +11 -1
  100. package/packages/pi-ai/dist/utils/json-parse.js.map +1 -1
  101. package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
  102. package/packages/pi-ai/dist/utils/repair-tool-json.js +60 -1
  103. package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
  104. package/packages/pi-ai/dist/utils/tests/json-parse.test.d.ts +2 -0
  105. package/packages/pi-ai/dist/utils/tests/json-parse.test.d.ts.map +1 -0
  106. package/packages/pi-ai/dist/utils/tests/json-parse.test.js +14 -0
  107. package/packages/pi-ai/dist/utils/tests/json-parse.test.js.map +1 -0
  108. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +10 -0
  109. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
  110. package/packages/pi-ai/src/providers/anthropic-shared.ts +4 -3
  111. package/packages/pi-ai/src/utils/json-parse.ts +11 -1
  112. package/packages/pi-ai/src/utils/repair-tool-json.ts +69 -1
  113. package/packages/pi-ai/src/utils/tests/json-parse.test.ts +17 -0
  114. package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +13 -0
  115. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +3 -0
  116. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  117. package/packages/pi-coding-agent/dist/core/agent-session.js +1 -0
  118. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  119. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +16 -0
  120. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/retry-handler.js +58 -1
  122. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +58 -0
  124. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/core/sdk.d.ts +3 -0
  126. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/sdk.js +1 -0
  128. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.d.ts +2 -0
  130. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.d.ts.map +1 -0
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js +17 -0
  132. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js.map +1 -0
  133. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +2 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  136. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts +1 -0
  137. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
  139. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  141. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +2 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  143. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  144. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js +2 -1
  145. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +2 -2
  147. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  148. package/packages/pi-coding-agent/package.json +1 -1
  149. package/packages/pi-coding-agent/src/core/agent-session.ts +4 -0
  150. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +69 -0
  151. package/packages/pi-coding-agent/src/core/retry-handler.ts +66 -1
  152. package/packages/pi-coding-agent/src/core/sdk.ts +5 -0
  153. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/provider-display-name.test.ts +18 -0
  154. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +2 -1
  155. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +11 -2
  156. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +2 -1
  157. package/packages/pi-coding-agent/src/modes/interactive/components/scoped-models-selector.ts +2 -1
  158. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +2 -2
  159. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +13 -0
  160. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  161. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.d.ts +2 -0
  162. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.d.ts.map +1 -0
  163. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +35 -0
  164. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -0
  165. package/packages/pi-tui/dist/__tests__/tui.test.d.ts +2 -0
  166. package/packages/pi-tui/dist/__tests__/tui.test.d.ts.map +1 -0
  167. package/packages/pi-tui/dist/__tests__/tui.test.js +43 -0
  168. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -0
  169. package/packages/pi-tui/dist/autocomplete.d.ts.map +1 -1
  170. package/packages/pi-tui/dist/autocomplete.js +9 -7
  171. package/packages/pi-tui/dist/autocomplete.js.map +1 -1
  172. package/packages/pi-tui/dist/components/__tests__/editor.test.d.ts +2 -0
  173. package/packages/pi-tui/dist/components/__tests__/editor.test.d.ts.map +1 -0
  174. package/packages/pi-tui/dist/components/__tests__/editor.test.js +54 -0
  175. package/packages/pi-tui/dist/components/__tests__/editor.test.js.map +1 -0
  176. package/packages/pi-tui/dist/components/editor.d.ts +3 -1
  177. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  178. package/packages/pi-tui/dist/components/editor.js +14 -3
  179. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  180. package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  181. package/packages/pi-tui/dist/stdin-buffer.js +6 -0
  182. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
  183. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  184. package/packages/pi-tui/dist/tui.js +8 -0
  185. package/packages/pi-tui/dist/tui.js.map +1 -1
  186. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +15 -0
  187. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +43 -0
  188. package/packages/pi-tui/src/__tests__/tui.test.ts +50 -0
  189. package/packages/pi-tui/src/autocomplete.ts +9 -7
  190. package/packages/pi-tui/src/components/__tests__/editor.test.ts +64 -0
  191. package/packages/pi-tui/src/components/editor.ts +14 -3
  192. package/packages/pi-tui/src/stdin-buffer.ts +7 -0
  193. package/packages/pi-tui/src/tui.ts +9 -0
  194. package/pkg/package.json +1 -1
  195. package/src/resources/extensions/ask-user-questions.ts +103 -11
  196. package/src/resources/extensions/claude-code-cli/partial-builder.ts +4 -3
  197. package/src/resources/extensions/claude-code-cli/readiness.ts +67 -12
  198. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +12 -3
  199. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +17 -0
  200. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +18 -0
  201. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -1
  202. package/src/resources/extensions/gsd/auto/loop.ts +14 -1
  203. package/src/resources/extensions/gsd/auto/phases.ts +27 -4
  204. package/src/resources/extensions/gsd/auto/run-unit.ts +14 -2
  205. package/src/resources/extensions/gsd/auto/session.ts +1 -1
  206. package/src/resources/extensions/gsd/auto-dashboard.ts +76 -16
  207. package/src/resources/extensions/gsd/auto-dispatch.ts +36 -35
  208. package/src/resources/extensions/gsd/auto-model-selection.ts +12 -3
  209. package/src/resources/extensions/gsd/auto-prompts.ts +195 -25
  210. package/src/resources/extensions/gsd/auto-recovery.ts +15 -15
  211. package/src/resources/extensions/gsd/auto.ts +12 -1
  212. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +34 -1
  213. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +27 -6
  214. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +6 -0
  215. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +67 -6
  216. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +11 -8
  217. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +209 -16
  218. package/src/resources/extensions/gsd/codebase-generator.ts +4 -0
  219. package/src/resources/extensions/gsd/commands/catalog.ts +2 -1
  220. package/src/resources/extensions/gsd/commands/dispatcher.ts +1 -2
  221. package/src/resources/extensions/gsd/commands/handlers/core.ts +113 -8
  222. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +49 -11
  223. package/src/resources/extensions/gsd/context-store.ts +167 -2
  224. package/src/resources/extensions/gsd/custom-workflow-engine.ts +3 -1
  225. package/src/resources/extensions/gsd/detection.ts +6 -0
  226. package/src/resources/extensions/gsd/files.ts +21 -2
  227. package/src/resources/extensions/gsd/guided-flow.ts +15 -8
  228. package/src/resources/extensions/gsd/index.ts +6 -0
  229. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +2 -0
  230. package/src/resources/extensions/gsd/parsers-legacy.ts +3 -1
  231. package/src/resources/extensions/gsd/preferences.ts +6 -1
  232. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  233. package/src/resources/extensions/gsd/prompts/complete-slice.md +3 -3
  234. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +7 -7
  235. package/src/resources/extensions/gsd/prompts/discuss.md +3 -3
  236. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -3
  237. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -3
  238. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
  239. package/src/resources/extensions/gsd/prompts/rethink.md +6 -2
  240. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  241. package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  242. package/src/resources/extensions/gsd/prompts/validate-milestone.md +11 -9
  243. package/src/resources/extensions/gsd/prompts/worktree-merge.md +3 -1
  244. package/src/resources/extensions/gsd/safety/file-change-validator.ts +4 -1
  245. package/src/resources/extensions/gsd/state.ts +2 -1
  246. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +52 -1
  247. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +50 -2
  248. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +21 -7
  249. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +48 -0
  250. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  251. package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +12 -0
  252. package/src/resources/extensions/gsd/tests/context-store.test.ts +176 -0
  253. package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +76 -0
  254. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +7 -1
  255. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +31 -0
  256. package/src/resources/extensions/gsd/tests/decision-scope-cascade.test.ts +370 -0
  257. package/src/resources/extensions/gsd/tests/detection.test.ts +37 -0
  258. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +50 -0
  259. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +35 -0
  260. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +34 -0
  261. package/src/resources/extensions/gsd/tests/health-widget.test.ts +45 -0
  262. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +53 -13
  263. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -2
  264. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +3 -3
  265. package/src/resources/extensions/gsd/tests/measurement.test.ts +531 -0
  266. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +3 -4
  267. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +21 -0
  268. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +71 -2
  269. package/src/resources/extensions/gsd/tests/parsers.test.ts +25 -0
  270. package/src/resources/extensions/gsd/tests/preferences.test.ts +37 -0
  271. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +26 -4
  272. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +60 -0
  273. package/src/resources/extensions/gsd/tests/queue-execution-guard.test.ts +9 -0
  274. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +19 -0
  275. package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +73 -0
  276. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +98 -0
  277. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +2 -2
  278. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +26 -0
  279. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +59 -0
  280. package/src/resources/extensions/gsd/tests/workflow-reconcile.test.ts +91 -0
  281. package/src/resources/extensions/gsd/tests/write-gate.test.ts +210 -35
  282. package/src/resources/extensions/gsd/visualizer-overlay.ts +31 -27
  283. package/src/resources/extensions/gsd/workflow-reconcile.ts +59 -8
  284. package/src/resources/extensions/remote-questions/manager.ts +9 -0
  285. package/src/resources/extensions/shared/interview-ui.ts +13 -0
  286. /package/dist/web/standalone/.next/static/{HAq0VE4k68rhRvJbQL1VW → CrKrzIIxk55witDF1eS0L}/_buildManifest.js +0 -0
  287. /package/dist/web/standalone/.next/static/{HAq0VE4k68rhRvJbQL1VW → CrKrzIIxk55witDF1eS0L}/_ssgManifest.js +0 -0
@@ -44,6 +44,7 @@ export function showHelp(ctx) {
44
44
  "SETUP & CONFIGURATION",
45
45
  " /gsd init Project init wizard — detect, configure, bootstrap .gsd/",
46
46
  " /gsd setup Global setup status [llm|search|remote|keys|prefs]",
47
+ " /gsd model Switch active session model [provider/model|model-id]",
47
48
  " /gsd mode Set workflow mode (solo/team) [global|project]",
48
49
  " /gsd prefs Manage preferences [global|project|status|wizard|setup|import-claude]",
49
50
  " /gsd cmux Manage cmux integration [status|on|off|notifications|sidebar|splits|browser]",
@@ -77,7 +78,7 @@ export async function handleStatus(ctx) {
77
78
  return;
78
79
  }
79
80
  const { GSDDashboardOverlay } = await import("../../dashboard-overlay.js");
80
- const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDDashboardOverlay(tui, theme, () => done()), {
81
+ const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDDashboardOverlay(tui, theme, () => done(true)), {
81
82
  overlay: true,
82
83
  overlayOptions: {
83
84
  width: "70%",
@@ -99,7 +100,7 @@ export async function handleVisualize(ctx) {
99
100
  return;
100
101
  }
101
102
  const { GSDVisualizerOverlay } = await import("../../visualizer-overlay.js");
102
- const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDVisualizerOverlay(tui, theme, () => done()), {
103
+ const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDVisualizerOverlay(tui, theme, () => done(true)), {
103
104
  overlay: true,
104
105
  overlayOptions: {
105
106
  width: "80%",
@@ -152,7 +153,92 @@ export async function handleSetup(args, ctx) {
152
153
  " /gsd setup keys — Tool API keys\n" +
153
154
  " /gsd setup prefs — Global preferences wizard", "info");
154
155
  }
155
- export async function handleCoreCommand(trimmed, ctx) {
156
+ function sortModelsForSelection(models, currentModel) {
157
+ return [...models].sort((a, b) => {
158
+ const aCurrent = currentModel && a.provider === currentModel.provider && a.id === currentModel.id;
159
+ const bCurrent = currentModel && b.provider === currentModel.provider && b.id === currentModel.id;
160
+ if (aCurrent && !bCurrent)
161
+ return -1;
162
+ if (!aCurrent && bCurrent)
163
+ return 1;
164
+ const providerCmp = a.provider.localeCompare(b.provider);
165
+ if (providerCmp !== 0)
166
+ return providerCmp;
167
+ return a.id.localeCompare(b.id);
168
+ });
169
+ }
170
+ async function resolveRequestedModel(query, ctx) {
171
+ const { resolveModelId } = await import("../../auto-model-selection.js");
172
+ const models = ctx.modelRegistry.getAvailable();
173
+ const exact = resolveModelId(query, models, ctx.model?.provider);
174
+ if (exact)
175
+ return exact;
176
+ const lowerQuery = query.toLowerCase();
177
+ const partialMatches = models.filter((model) => model.id.toLowerCase().includes(lowerQuery)
178
+ || `${model.provider}/${model.id}`.toLowerCase().includes(lowerQuery));
179
+ if (partialMatches.length === 1)
180
+ return partialMatches[0];
181
+ if (partialMatches.length === 0 || !ctx.hasUI)
182
+ return undefined;
183
+ const sorted = sortModelsForSelection(partialMatches, ctx.model);
184
+ const optionToModel = new Map();
185
+ const options = sorted.map((model) => {
186
+ const label = `${model.provider}/${model.id}`;
187
+ optionToModel.set(label, model);
188
+ return label;
189
+ });
190
+ options.push("(cancel)");
191
+ const choice = await ctx.ui.select(`Multiple models match "${query}" — choose one:`, options);
192
+ if (!choice || typeof choice !== "string" || choice === "(cancel)")
193
+ return undefined;
194
+ return optionToModel.get(choice);
195
+ }
196
+ async function handleModel(trimmedArgs, ctx, pi) {
197
+ const availableModels = ctx.modelRegistry.getAvailable();
198
+ if (availableModels.length === 0) {
199
+ ctx.ui.notify("No available models found. Check provider auth and model discovery.", "warning");
200
+ return;
201
+ }
202
+ if (!pi) {
203
+ ctx.ui.notify("Model switching is unavailable in this context.", "warning");
204
+ return;
205
+ }
206
+ const trimmed = trimmedArgs.trim();
207
+ let targetModel;
208
+ if (!trimmed) {
209
+ if (!ctx.hasUI) {
210
+ const current = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "(none)";
211
+ ctx.ui.notify(`Current model: ${current}\nUsage: /gsd model <provider/model|model-id>`, "info");
212
+ return;
213
+ }
214
+ const optionToModel = new Map();
215
+ const options = sortModelsForSelection(availableModels, ctx.model).map((model) => {
216
+ const isCurrent = ctx.model && model.provider === ctx.model.provider && model.id === ctx.model.id;
217
+ const label = `${isCurrent ? "* " : ""}${model.provider}/${model.id}`;
218
+ optionToModel.set(label, model);
219
+ return label;
220
+ });
221
+ options.push("(cancel)");
222
+ const choice = await ctx.ui.select("Select session model:", options);
223
+ if (!choice || typeof choice !== "string" || choice === "(cancel)")
224
+ return;
225
+ targetModel = optionToModel.get(choice);
226
+ }
227
+ else {
228
+ targetModel = await resolveRequestedModel(trimmed, ctx);
229
+ }
230
+ if (!targetModel) {
231
+ ctx.ui.notify(`Model "${trimmed}" not found. Use /gsd model with an exact provider/model or a unique model ID.`, "warning");
232
+ return;
233
+ }
234
+ const ok = await pi.setModel(targetModel);
235
+ if (!ok) {
236
+ ctx.ui.notify(`No API key for ${targetModel.provider}/${targetModel.id}`, "warning");
237
+ return;
238
+ }
239
+ ctx.ui.notify(`Model: ${targetModel.provider}/${targetModel.id}`, "info");
240
+ }
241
+ export async function handleCoreCommand(trimmed, ctx, pi) {
156
242
  if (trimmed === "help" || trimmed === "h" || trimmed === "?") {
157
243
  showHelp(ctx);
158
244
  return true;
@@ -177,6 +263,10 @@ export async function handleCoreCommand(trimmed, ctx) {
177
263
  ctx.ui.notify(`Widget: ${getWidgetMode()}`, "info");
178
264
  return true;
179
265
  }
266
+ if (trimmed === "model" || trimmed.startsWith("model ")) {
267
+ await handleModel(trimmed.replace(/^model\s*/, "").trim(), ctx, pi);
268
+ return true;
269
+ }
180
270
  if (trimmed === "mode" || trimmed.startsWith("mode ")) {
181
271
  const modeArgs = trimmed.replace(/^mode\s*/, "").trim();
182
272
  const scope = modeArgs === "project" ? "project" : "global";
@@ -195,7 +285,7 @@ export async function handleCoreCommand(trimmed, ctx) {
195
285
  }
196
286
  if (trimmed === "show-config") {
197
287
  const { GSDConfigOverlay, formatConfigText } = await import("../../config-overlay.js");
198
- const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDConfigOverlay(tui, theme, () => done()), {
288
+ const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDConfigOverlay(tui, theme, () => done(true)), {
199
289
  overlay: true,
200
290
  overlayOptions: {
201
291
  width: "65%",
@@ -137,7 +137,7 @@ export function buildCategorySummaries(prefs) {
137
137
  const models = prefs.models;
138
138
  let modelsSummary = "(not configured)";
139
139
  if (models && Object.keys(models).length > 0) {
140
- const parts = Object.entries(models).map(([phase, model]) => `${phase}: ${model}`);
140
+ const parts = Object.entries(models).map(([phase, model]) => `${phase}: ${formatConfiguredModel(model)}`);
141
141
  modelsSummary = parts.join(", ");
142
142
  }
143
143
  // Timeouts
@@ -220,8 +220,39 @@ export function buildCategorySummaries(prefs) {
220
220
  };
221
221
  }
222
222
  // ─── Category configuration functions ────────────────────────────────────────
223
+ export function formatConfiguredModel(config) {
224
+ if (typeof config === "string")
225
+ return config;
226
+ if (!config || typeof config !== "object")
227
+ return "(invalid)";
228
+ const maybeConfig = config;
229
+ if (typeof maybeConfig.model !== "string" || maybeConfig.model.trim() === "")
230
+ return "(invalid)";
231
+ if (typeof maybeConfig.provider === "string" && maybeConfig.provider && !maybeConfig.model.includes("/")) {
232
+ return `${maybeConfig.provider}/${maybeConfig.model}`;
233
+ }
234
+ return maybeConfig.model;
235
+ }
236
+ export function toPersistedModelId(provider, modelId) {
237
+ if (!provider.trim())
238
+ return modelId;
239
+ const normalizedProvider = provider.trim();
240
+ const normalizedModelId = modelId.trim();
241
+ return normalizedModelId.startsWith(`${normalizedProvider}/`)
242
+ ? normalizedModelId
243
+ : `${normalizedProvider}/${normalizedModelId}`;
244
+ }
223
245
  async function configureModels(ctx, prefs) {
224
- const modelPhases = ["research", "planning", "execution", "completion"];
246
+ const modelPhases = [
247
+ "research",
248
+ "planning",
249
+ "discuss",
250
+ "execution",
251
+ "execution_simple",
252
+ "completion",
253
+ "validation",
254
+ "subagent",
255
+ ];
225
256
  const models = prefs.models ?? {};
226
257
  const availableModels = ctx.modelRegistry.getAvailable();
227
258
  if (availableModels.length > 0) {
@@ -240,14 +271,20 @@ async function configureModels(ctx, prefs) {
240
271
  for (const group of byProvider.values()) {
241
272
  group.sort((a, b) => a.id.localeCompare(b.id));
242
273
  }
243
- // Build provider menu with model counts
274
+ // Display names for providers in the preferences wizard UI.
275
+ const PROVIDER_DISPLAY_NAMES = { anthropic: "anthropic-api" };
276
+ const displayName = (p) => PROVIDER_DISPLAY_NAMES[p] ?? p;
277
+ // Build provider menu with model counts (display name → real name lookup)
278
+ const displayToReal = new Map();
244
279
  const providerOptions = providers.map(p => {
245
280
  const count = byProvider.get(p).length;
246
- return `${p} (${count} models)`;
281
+ const label = `${displayName(p)} (${count} models)`;
282
+ displayToReal.set(label, p);
283
+ return label;
247
284
  });
248
285
  providerOptions.push("(keep current)", "(clear)", "(type manually)");
249
286
  for (const phase of modelPhases) {
250
- const current = models[phase] ?? "";
287
+ const current = formatConfiguredModel(models[phase]);
251
288
  const phaseLabel = `Model for ${phase} phase${current ? ` (current: ${current})` : ""}`;
252
289
  // Step 1: pick provider
253
290
  const providerChoice = await ctx.ui.select(`${phaseLabel} — choose provider:`, providerOptions);
@@ -267,26 +304,26 @@ async function configureModels(ctx, prefs) {
267
304
  continue;
268
305
  }
269
306
  // Step 2: pick model within provider
270
- const providerName = providerChoice.replace(/ \(\d+ models?\)$/, "");
307
+ const providerName = displayToReal.get(providerChoice) ?? providerChoice.replace(/ \(\d+ models?\)$/, "");
271
308
  const group = byProvider.get(providerName);
272
309
  if (!group)
273
310
  continue;
274
311
  const modelOptions = group.map(m => m.id);
275
312
  modelOptions.push("(keep current)", "(clear)");
276
- const modelChoice = await ctx.ui.select(`${phaseLabel} — ${providerName}:`, modelOptions);
313
+ const modelChoice = await ctx.ui.select(`${phaseLabel} — ${displayName(providerName)}:`, modelOptions);
277
314
  if (modelChoice && typeof modelChoice === "string" && modelChoice !== "(keep current)") {
278
315
  if (modelChoice === "(clear)") {
279
316
  delete models[phase];
280
317
  }
281
318
  else {
282
- models[phase] = modelChoice;
319
+ models[phase] = toPersistedModelId(providerName, modelChoice);
283
320
  }
284
321
  }
285
322
  }
286
323
  }
287
324
  else {
288
325
  for (const phase of modelPhases) {
289
- const current = models[phase] ?? "";
326
+ const current = formatConfiguredModel(models[phase]);
290
327
  const input = await ctx.ui.input(`Model for ${phase} phase${current ? ` (current: ${current})` : ""}:`, current || "e.g. claude-sonnet-4-20250514");
291
328
  if (input !== null && input !== undefined) {
292
329
  const val = input.trim();
@@ -302,6 +339,9 @@ async function configureModels(ctx, prefs) {
302
339
  if (Object.keys(models).length > 0) {
303
340
  prefs.models = models;
304
341
  }
342
+ else {
343
+ delete prefs.models;
344
+ }
305
345
  }
306
346
  async function configureTimeouts(ctx, prefs) {
307
347
  const autoSup = prefs.auto_supervisor ?? {};
@@ -49,7 +49,8 @@ export function queryDecisions(opts) {
49
49
  }
50
50
  /**
51
51
  * Query active (non-superseded) requirements with optional filters.
52
- * - sliceId: filters where primary_owner LIKE '%sliceId%' OR supporting_slices LIKE '%sliceId%'
52
+ * - milestoneId: combined with sliceId for precise filtering (e.g. %M005/S01%)
53
+ * - sliceId: filters where primary_owner LIKE '%pattern%' OR supporting_slices LIKE '%pattern%'
53
54
  * - status: filters where status = :status (exact match)
54
55
  *
55
56
  * Returns [] if DB is not available. Never throws.
@@ -63,10 +64,22 @@ export function queryRequirements(opts) {
63
64
  try {
64
65
  const clauses = ['superseded_by IS NULL'];
65
66
  const params = {};
66
- if (opts?.sliceId) {
67
+ // Combined milestone+slice filtering for precise scoping
68
+ if (opts?.milestoneId && opts?.sliceId) {
69
+ // Use combined pattern like %M005/S01% to avoid cross-milestone contamination
70
+ clauses.push('(primary_owner LIKE :combined_pattern OR supporting_slices LIKE :combined_pattern)');
71
+ params[':combined_pattern'] = `%${opts.milestoneId}/${opts.sliceId}%`;
72
+ }
73
+ else if (opts?.sliceId) {
74
+ // Slice-only filtering (legacy behavior)
67
75
  clauses.push('(primary_owner LIKE :slice_pattern OR supporting_slices LIKE :slice_pattern)');
68
76
  params[':slice_pattern'] = `%${opts.sliceId}%`;
69
77
  }
78
+ else if (opts?.milestoneId) {
79
+ // Milestone-only filtering
80
+ clauses.push('(primary_owner LIKE :milestone_pattern OR supporting_slices LIKE :milestone_pattern)');
81
+ params[':milestone_pattern'] = `%${opts.milestoneId}%`;
82
+ }
70
83
  if (opts?.status) {
71
84
  clauses.push('status = :status');
72
85
  params[':status'] = opts.status;
@@ -164,3 +177,122 @@ export function queryArtifact(path) {
164
177
  export function queryProject() {
165
178
  return queryArtifact('PROJECT.md');
166
179
  }
180
+ // ─── Knowledge Query ───────────────────────────────────────────────────────
181
+ /**
182
+ * Filter KNOWLEDGE.md sections by keyword matching.
183
+ * Uses H2 sections, matches keywords case-insensitively against:
184
+ * 1. Section header text
185
+ * 2. First paragraph of section content (up to first blank line or next heading)
186
+ *
187
+ * Per D020, returns empty string (not null) when no matches found.
188
+ * This signals "no relevant knowledge" vs "file not found".
189
+ *
190
+ * @param content - Full KNOWLEDGE.md content
191
+ * @param keywords - Keywords to match (case-insensitive)
192
+ * @returns Concatenated matching sections with H2 headers, or empty string
193
+ */
194
+ export async function queryKnowledge(content, keywords) {
195
+ if (!content || keywords.length === 0)
196
+ return '';
197
+ // Lazy import to avoid circular dependency
198
+ const { extractAllSections } = await import('./files.js');
199
+ const sections = extractAllSections(content, 2);
200
+ if (sections.size === 0)
201
+ return '';
202
+ // Normalize keywords for case-insensitive matching
203
+ const normalizedKeywords = keywords.map(k => k.toLowerCase());
204
+ const matchingSections = [];
205
+ for (const [header, body] of sections) {
206
+ // Extract first paragraph: everything up to first blank line or next heading
207
+ const firstParagraph = body.split(/\n\s*\n|\n#/)[0] || '';
208
+ // Check if any keyword matches header or first paragraph
209
+ const headerLower = header.toLowerCase();
210
+ const paragraphLower = firstParagraph.toLowerCase();
211
+ const matches = normalizedKeywords.some(kw => headerLower.includes(kw) || paragraphLower.includes(kw));
212
+ if (matches) {
213
+ matchingSections.push(`## ${header}\n\n${body}`);
214
+ }
215
+ }
216
+ return matchingSections.join('\n\n');
217
+ }
218
+ // ─── Roadmap Excerpt Formatter ─────────────────────────────────────────────
219
+ /**
220
+ * Format a minimal roadmap excerpt for prompt injection.
221
+ * Parses the slice table from roadmap content, extracts:
222
+ * 1. Header row + separator
223
+ * 2. Predecessor row (if sliceId depends on one via the Depends column)
224
+ * 3. Target slice row
225
+ * 4. Reference directive pointing to full roadmap path
226
+ *
227
+ * Per D021, this minimizes injected content while preserving dependency awareness.
228
+ * Returns empty string if sliceId is not found in the table.
229
+ * Never throws.
230
+ *
231
+ * @param roadmapContent - Full content of the M###-ROADMAP.md file
232
+ * @param sliceId - Target slice ID (e.g. 'S02')
233
+ * @param roadmapPath - Optional path for reference directive (defaults to generic)
234
+ */
235
+ export function formatRoadmapExcerpt(roadmapContent, sliceId, roadmapPath = 'ROADMAP.md') {
236
+ if (!roadmapContent || !sliceId)
237
+ return '';
238
+ const lines = roadmapContent.split('\n');
239
+ // Find the slice table header: | ID | Slice | ... (case insensitive)
240
+ let headerIndex = -1;
241
+ for (let i = 0; i < lines.length; i++) {
242
+ const line = lines[i];
243
+ if (line && /^\s*\|\s*ID\s*\|\s*Slice\s*\|/i.test(line)) {
244
+ headerIndex = i;
245
+ break;
246
+ }
247
+ }
248
+ if (headerIndex === -1)
249
+ return '';
250
+ // The separator should be the next line (|---|---|...)
251
+ const separatorIndex = headerIndex + 1;
252
+ if (separatorIndex >= lines.length)
253
+ return '';
254
+ const headerLine = lines[headerIndex];
255
+ const separatorLine = lines[separatorIndex];
256
+ // Validate separator line looks like |---|---|... (may include : for alignment)
257
+ if (!separatorLine || !/^\s*\|[\s:\-|]+\|/.test(separatorLine))
258
+ return '';
259
+ const sliceRows = [];
260
+ for (let i = separatorIndex + 1; i < lines.length; i++) {
261
+ const line = lines[i];
262
+ if (!line || !line.trim().startsWith('|'))
263
+ break; // End of table
264
+ // Parse row: | ID | Slice | Risk | Depends | Done | After this |
265
+ const cells = line.split('|').map(c => c.trim());
266
+ // cells[0] is empty (before first |), cells[1] is ID, etc.
267
+ if (cells.length < 5)
268
+ continue;
269
+ const id = cells[1] || '';
270
+ const depends = cells[4] || ''; // Depends column (0-indexed: empty, ID, Slice, Risk, Depends, ...)
271
+ sliceRows.push({ line, id, depends });
272
+ }
273
+ // Find target slice row
274
+ const targetRow = sliceRows.find(r => r.id === sliceId);
275
+ if (!targetRow)
276
+ return '';
277
+ // Find predecessor if target depends on one
278
+ // Depends column may contain: '—', 'S01', 'S01, S02', etc.
279
+ let predecessorRow;
280
+ const dependsRaw = targetRow.depends;
281
+ if (dependsRaw && dependsRaw !== '—' && dependsRaw !== '-') {
282
+ // Extract first dependency (e.g. 'S01' from 'S01, S02')
283
+ const depMatch = dependsRaw.match(/S\d+/);
284
+ if (depMatch) {
285
+ predecessorRow = sliceRows.find(r => r.id === depMatch[0]);
286
+ }
287
+ }
288
+ // Build excerpt
289
+ const excerptLines = [headerLine, separatorLine];
290
+ if (predecessorRow) {
291
+ excerptLines.push(predecessorRow.line);
292
+ }
293
+ excerptLines.push(targetRow.line);
294
+ // Add reference directive
295
+ excerptLines.push('');
296
+ excerptLines.push(`> See full roadmap: ${roadmapPath}`);
297
+ return excerptLines.join('\n');
298
+ }
@@ -135,7 +135,9 @@ export class CustomWorkflowEngine {
135
135
  * Returns "milestone-complete" when all steps are now done, "continue" otherwise.
136
136
  */
137
137
  async reconcile(state, completedStep) {
138
- const graph = state.raw;
138
+ // Re-read the graph from disk so we do not overwrite concurrent
139
+ // workflow edits with a stale in-memory snapshot from deriveState().
140
+ const graph = readGraph(this.runDir);
139
141
  // Extract stepId from "<workflowName>/<stepId>"
140
142
  const { milestone, slice, task } = parseUnitId(completedStep.unitId);
141
143
  const stepId = task ?? slice ?? milestone;
@@ -170,6 +170,12 @@ const TEST_MARKERS = [
170
170
  /** Directories skipped during bounded recursive project scans. */
171
171
  const RECURSIVE_SCAN_IGNORED_DIRS = new Set([
172
172
  ".git",
173
+ ".gsd",
174
+ ".planning",
175
+ ".plans",
176
+ ".claude",
177
+ ".cursor",
178
+ ".vscode",
173
179
  "node_modules",
174
180
  ".venv",
175
181
  "venv",
@@ -104,6 +104,23 @@ export function extractAllSections(body, level = 2) {
104
104
  function escapeRegex(s) {
105
105
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
106
106
  }
107
+ /**
108
+ * Normalize a task-plan file reference that may include inline description text
109
+ * after the path, for example:
110
+ * "docs/file.md — explanation"
111
+ * "docs/file.md - explanation"
112
+ */
113
+ export function normalizePlannedFileReference(value) {
114
+ const trimmed = value.trim().replace(/`/g, "");
115
+ const match = /^(.*?)(?:\s+(?:—|-)\s+)(.+)$/.exec(trimmed);
116
+ if (!match)
117
+ return trimmed;
118
+ const pathCandidate = match[1].trim();
119
+ if (pathCandidate.includes("/") || pathCandidate.includes("\\") || pathCandidate.includes(".")) {
120
+ return pathCandidate;
121
+ }
122
+ return trimmed;
123
+ }
107
124
  /** Parse bullet list items from a text block. */
108
125
  export function parseBullets(text) {
109
126
  return text.split('\n')
@@ -540,11 +557,11 @@ export function parseTaskPlanIO(content) {
540
557
  let match;
541
558
  backtickPathRegex.lastIndex = 0;
542
559
  while ((match = backtickPathRegex.exec(trimmed)) !== null) {
543
- const candidate = match[1];
560
+ const candidate = normalizePlannedFileReference(match[1]);
544
561
  // Filter out things that look like code tokens rather than file paths
545
562
  // (e.g. `true`, `false`, `npm run test`). A file path has at least one
546
563
  // dot or slash.
547
- if (candidate.includes("/") || candidate.includes(".")) {
564
+ if (candidate.includes("/") || candidate.includes("\\") || candidate.includes(".")) {
548
565
  paths.push(candidate);
549
566
  }
550
567
  }
@@ -142,12 +142,13 @@ export function checkAutoStartAfterDiscuss() {
142
142
  // Parse PROJECT.md for milestone sequence, warn if any are missing context.
143
143
  // Don't block — milestones can be intentionally queued without context.
144
144
  const projectFile = resolveGsdRootFile(basePath, "PROJECT");
145
+ let projectIds = [];
145
146
  if (projectFile) {
146
147
  try {
147
148
  const projectContent = readFileSync(projectFile, "utf-8");
148
- const milestoneIds = parseMilestoneSequenceFromProject(projectContent);
149
- if (milestoneIds.length > 1) {
150
- const missing = milestoneIds.filter(id => {
149
+ projectIds = parseMilestoneSequenceFromProject(projectContent);
150
+ if (projectIds.length > 1) {
151
+ const missing = projectIds.filter(id => {
151
152
  const hasContext = !!resolveMilestoneFile(basePath, id, "CONTEXT");
152
153
  const hasDraft = !!resolveMilestoneFile(basePath, id, "CONTEXT-DRAFT");
153
154
  const hasDir = existsSync(join(gsdRoot(basePath), "milestones", id));
@@ -165,9 +166,14 @@ export function checkAutoStartAfterDiscuss() {
165
166
  }
166
167
  // Gate 4: Discussion manifest process verification (multi-milestone only)
167
168
  // The LLM writes DISCUSSION-MANIFEST.json after each Phase 3 gate decision.
168
- // If the manifest exists but gates_completed < total, the LLM hasn't finished
169
- // presenting all readiness gates to the user — block auto-start.
169
+ // If the project is multi-milestone, the manifest is required. When it is
170
+ // missing, fail closed instead of assuming the discussion finished.
170
171
  const manifestPath = join(gsdRoot(basePath), "DISCUSSION-MANIFEST.json");
172
+ const requiresManifest = projectIds.length > 1 || findMilestoneIds(basePath).length > 1;
173
+ if (requiresManifest && !existsSync(manifestPath)) {
174
+ ctx.ui.notify("Multi-milestone discussion manifest is missing. Auto-start will remain paused until the manifest is written.", "warning");
175
+ return false;
176
+ }
171
177
  if (existsSync(manifestPath)) {
172
178
  try {
173
179
  const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
@@ -178,9 +184,7 @@ export function checkAutoStartAfterDiscuss() {
178
184
  return false;
179
185
  }
180
186
  // Cross-check manifest milestones against PROJECT.md if available
181
- if (projectFile) {
182
- const projectContent = readFileSync(projectFile, "utf-8");
183
- const projectIds = parseMilestoneSequenceFromProject(projectContent);
187
+ if (projectIds.length > 0) {
184
188
  const manifestIds = Object.keys(manifest.milestones ?? {});
185
189
  const untracked = projectIds.filter(id => !manifestIds.includes(id));
186
190
  if (untracked.length > 0) {
@@ -1,4 +1,4 @@
1
- export { isDepthConfirmationAnswer, isDepthVerified, isQueuePhaseActive, setQueuePhaseActive, shouldBlockContextWrite, shouldBlockQueueExecution, } from "./bootstrap/write-gate.js";
1
+ export { isDepthConfirmationAnswer, isDepthVerified, isGateQuestionId, isQueuePhaseActive, setQueuePhaseActive, shouldBlockContextWrite, shouldBlockPendingGate, shouldBlockPendingGateBash, shouldBlockQueueExecution, setPendingGate, clearPendingGate, getPendingGate, } from "./bootstrap/write-gate.js";
2
2
  export default async function registerExtension(pi) {
3
3
  const { registerGsdExtension } = await import("./bootstrap/register-extension.js");
4
4
  registerGsdExtension(pi);
@@ -406,6 +406,8 @@ export class ParallelMonitorOverlay {
406
406
  lines.push(t.fg("muted", " ESC/q to close │ ↑↓ scroll"));
407
407
  // Apply scroll — use terminal rows as height estimate
408
408
  const termHeight = process.stdout.rows || 40;
409
+ const maxScroll = Math.max(0, lines.length - termHeight);
410
+ this.scrollOffset = Math.min(Math.max(this.scrollOffset, 0), maxScroll);
409
411
  const visible = lines.slice(this.scrollOffset, this.scrollOffset + termHeight);
410
412
  this.cachedLines = visible;
411
413
  return visible;
@@ -177,7 +177,9 @@ function _parsePlanImpl(content) {
177
177
  for (const line of lines) {
178
178
  const cbMatch = line.match(/^-\s+\[([ xX])\]\s+\*\*([\w.]+):\s+(.+?)\*\*\s*(.*)/);
179
179
  // Heading-style: ### T01 -- Title, ### T01: Title, ### T01 — Title
180
- const hdMatch = !cbMatch ? line.match(/^#{2,4}\s+([\w.]+)\s*(?:--|—|:)\s*(.+)/) : null;
180
+ const hdMatch = !cbMatch
181
+ ? line.match(/^#{2,4}\s+([A-Z]+\d+(?:\.[A-Z]+\d+)*)\s*(?:--|—|:)\s*(.+)/)
182
+ : null;
181
183
  if (cbMatch || hdMatch) {
182
184
  const taskId = cbMatch ? cbMatch[2] : hdMatch[1];
183
185
  // Skip tasks already found in the Tasks section
@@ -127,10 +127,12 @@ function loadPreferencesFile(path, scope) {
127
127
  };
128
128
  }
129
129
  let _warnedUnrecognizedFormat = false;
130
+ let _warnedSectionParse = false;
130
131
  /** @internal Reset the warn-once flags — exported for testing only. */
131
132
  export function _resetParseWarningFlag() {
132
133
  _warnedUnrecognizedFormat = false;
133
134
  _warnedFrontmatterParse = false;
135
+ _warnedSectionParse = false;
134
136
  }
135
137
  /** @internal Exported for testing only */
136
138
  export function parsePreferencesMarkdown(content) {
@@ -227,7 +229,10 @@ function parseHeadingListFormat(content) {
227
229
  typed[targetSection] = value;
228
230
  }
229
231
  catch (e) {
230
- logWarning("guided", `preferences section parse failed: ${e.message}`);
232
+ if (!_warnedSectionParse) {
233
+ _warnedSectionParse = true;
234
+ logWarning("guided", `preferences section parse failed: ${e.message}`);
235
+ }
231
236
  }
232
237
  }
233
238
  return typed;
@@ -63,6 +63,6 @@ Then:
63
63
  13. Do not commit manually — the system auto-commits your changes after this unit completes.
64
64
  - Say: "Milestone {{milestoneId}} complete."
65
65
 
66
- **Important:** Do NOT skip the code change verification, success criteria, or definition of done verification (steps 3-5). The milestone summary must reflect actual verified outcomes, not assumed success. Verification failures BLOCK completion — there is no override. The milestone stays in its current state until issues are resolved and verification is re-run.
66
+ **Important:** Do NOT skip the code change verification, success criteria, or definition of done verification (steps 3-5). The milestone summary must reflect actual verified outcomes, not assumed success. Verification failures BLOCK completion — there is no override. The milestone stays in its current state until issues are resolved and verification is re-run. **If a verification tool itself fails, errors, or returns unexpected output, treat it as a verification failure** — never rationalize past a tool error ("tool didn't respond, assuming success" is forbidden). A tool that cannot verify is a tool that did not verify.
67
67
 
68
68
  **File system safety:** When scanning milestone directories for evidence, use `ls` or `find` to list directory contents first — never pass a directory path (e.g. `tasks/`, `slices/`) directly to the `read` tool. The `read` tool only accepts file paths, not directories.
@@ -25,11 +25,11 @@ Then:
25
25
  4. If the slice plan includes observability/diagnostic surfaces, confirm they work. Skip this for simple slices that don't have observability sections.
26
26
  5. If the slice involved runtime behavior, fill the **Operational Readiness** section (Q8) in the slice summary: health signal, failure signal, recovery procedure, and monitoring gaps. Omit entirely for simple slices with no runtime concerns.
27
27
  6. If this slice produced evidence that a requirement changed status (Active → Validated, Active → Deferred, etc.), call `gsd_requirement_update` with the requirement ID, updated `status`, and `validation` evidence. Do NOT write `.gsd/REQUIREMENTS.md` directly — the engine renders it from the database.
28
- 7. Write `{{sliceSummaryPath}}` (compress all task summaries).
29
- 8. Write `{{sliceUatPath}}` — a concrete UAT script with real test cases derived from the slice plan and task summaries. Include preconditions, numbered steps with expected outcomes, and edge cases. This must NOT be a placeholder or generic template — tailor every test case to what this slice actually built.
28
+ 7. Prepare the slice completion content you will pass to `gsd_complete_slice` using the camelCase fields `milestoneId`, `sliceId`, `sliceTitle`, `oneLiner`, `narrative`, `verification`, and `uatContent`. Do **not** manually write `{{sliceSummaryPath}}`. Do **not** manually write `{{sliceUatPath}}` the DB-backed tool is the canonical write path for both artifacts.
29
+ 8. Draft the UAT content you will pass as `uatContent` — a concrete UAT script with real test cases derived from the slice plan and task summaries. Include preconditions, numbered steps with expected outcomes, and edge cases. This must NOT be a placeholder or generic template — tailor every test case to what this slice actually built.
30
30
  9. Review task summaries for `key_decisions`. Append any significant decisions to `.gsd/DECISIONS.md` if missing.
31
31
  10. Review task summaries for patterns, gotchas, or non-obvious lessons learned. If any would save future agents from repeating investigation or hitting the same issues, append them to `.gsd/KNOWLEDGE.md`. Only add entries that are genuinely useful — don't pad with obvious observations.
32
- 11. Call `gsd_complete_slice` with milestoneId, sliceId, the slice summary, and the UAT result. Do NOT manually mark the roadmap checkbox — the tool writes to the DB and renders the ROADMAP.md projection automatically.
32
+ 11. Call `gsd_complete_slice` with the camelCase fields `milestoneId`, `sliceId`, `sliceTitle`, `oneLiner`, `narrative`, `verification`, and `uatContent`, plus any optional enrichment fields you have. Do NOT manually mark the roadmap checkbox — the tool writes to the DB, renders `{{sliceSummaryPath}}` and `{{sliceUatPath}}`, and updates the ROADMAP.md projection automatically.
33
33
  12. Do not run git commands — the system commits your changes and handles any merge after this unit succeeds.
34
34
  13. Update `.gsd/PROJECT.md` if it exists — refresh current state if needed: use the `write` tool with `path: ".gsd/PROJECT.md"` and `content` containing the full updated document reflecting current project state. Do NOT use the `edit` tool for this — PROJECT.md is a full-document refresh.
35
35