gsd-pi 2.66.1-dev.ed243f2 → 2.67.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (384) 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/handlers/core.js +3 -3
  27. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +10 -4
  28. package/dist/resources/extensions/gsd/context-store.js +134 -2
  29. package/dist/resources/extensions/gsd/custom-workflow-engine.js +3 -1
  30. package/dist/resources/extensions/gsd/detection.js +6 -0
  31. package/dist/resources/extensions/gsd/files.js +19 -2
  32. package/dist/resources/extensions/gsd/guided-flow.js +12 -8
  33. package/dist/resources/extensions/gsd/index.js +1 -1
  34. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +2 -0
  35. package/dist/resources/extensions/gsd/parsers-legacy.js +3 -1
  36. package/dist/resources/extensions/gsd/preferences.js +6 -1
  37. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  38. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +7 -7
  39. package/dist/resources/extensions/gsd/prompts/discuss.md +3 -3
  40. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -3
  41. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
  42. package/dist/resources/extensions/gsd/prompts/rethink.md +6 -2
  43. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  44. package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +4 -4
  46. package/dist/resources/extensions/gsd/prompts/worktree-merge.md +3 -1
  47. package/dist/resources/extensions/gsd/safety/file-change-validator.js +2 -1
  48. package/dist/resources/extensions/gsd/state.js +2 -1
  49. package/dist/resources/extensions/gsd/visualizer-overlay.js +27 -26
  50. package/dist/resources/extensions/gsd/workflow-reconcile.js +46 -7
  51. package/dist/resources/extensions/remote-questions/manager.js +8 -0
  52. package/dist/resources/extensions/shared/interview-ui.js +10 -0
  53. package/dist/web/standalone/.next/BUILD_ID +1 -1
  54. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  55. package/dist/web/standalone/.next/build-manifest.json +3 -3
  56. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  57. package/dist/web/standalone/.next/required-server-files.json +3 -3
  58. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  59. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  60. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  61. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  69. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +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 +3 -3
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  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 +3 -3
  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/boot/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  85. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  97. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  117. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  127. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  133. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  147. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  149. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  151. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  153. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/index.html +1 -1
  163. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  164. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  165. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  166. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  167. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  168. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  169. package/dist/web/standalone/.next/server/app/page.js +2 -2
  170. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  172. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  173. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  174. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/middleware.js +2 -2
  176. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  178. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  179. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  180. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  181. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
  182. package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
  183. package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +1 -0
  184. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
  185. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
  186. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  187. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  188. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  189. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  190. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  191. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  192. package/dist/web/standalone/server.js +1 -1
  193. package/package.json +1 -1
  194. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  195. package/packages/pi-ai/dist/providers/anthropic-shared.js +4 -3
  196. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  197. package/packages/pi-ai/dist/utils/json-parse.d.ts.map +1 -1
  198. package/packages/pi-ai/dist/utils/json-parse.js +11 -1
  199. package/packages/pi-ai/dist/utils/json-parse.js.map +1 -1
  200. package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
  201. package/packages/pi-ai/dist/utils/repair-tool-json.js +60 -1
  202. package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
  203. package/packages/pi-ai/dist/utils/tests/json-parse.test.d.ts +2 -0
  204. package/packages/pi-ai/dist/utils/tests/json-parse.test.d.ts.map +1 -0
  205. package/packages/pi-ai/dist/utils/tests/json-parse.test.js +14 -0
  206. package/packages/pi-ai/dist/utils/tests/json-parse.test.js.map +1 -0
  207. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +10 -0
  208. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
  209. package/packages/pi-ai/src/providers/anthropic-shared.ts +4 -3
  210. package/packages/pi-ai/src/utils/json-parse.ts +11 -1
  211. package/packages/pi-ai/src/utils/repair-tool-json.ts +69 -1
  212. package/packages/pi-ai/src/utils/tests/json-parse.test.ts +17 -0
  213. package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +13 -0
  214. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +3 -0
  215. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  216. package/packages/pi-coding-agent/dist/core/agent-session.js +1 -0
  217. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  218. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +16 -0
  219. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  220. package/packages/pi-coding-agent/dist/core/retry-handler.js +58 -1
  221. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  222. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +58 -0
  223. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  224. package/packages/pi-coding-agent/dist/core/sdk.d.ts +3 -0
  225. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  226. package/packages/pi-coding-agent/dist/core/sdk.js +1 -0
  227. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.d.ts +2 -0
  229. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.d.ts.map +1 -0
  230. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js +17 -0
  231. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js.map +1 -0
  232. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  233. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +2 -1
  234. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  235. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts +1 -0
  236. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  237. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
  238. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  239. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  240. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +2 -1
  241. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  242. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  243. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js +2 -1
  244. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  245. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +2 -2
  246. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  247. package/packages/pi-coding-agent/package.json +1 -1
  248. package/packages/pi-coding-agent/src/core/agent-session.ts +4 -0
  249. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +69 -0
  250. package/packages/pi-coding-agent/src/core/retry-handler.ts +66 -1
  251. package/packages/pi-coding-agent/src/core/sdk.ts +5 -0
  252. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/provider-display-name.test.ts +18 -0
  253. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +2 -1
  254. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +11 -2
  255. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +2 -1
  256. package/packages/pi-coding-agent/src/modes/interactive/components/scoped-models-selector.ts +2 -1
  257. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +2 -2
  258. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +13 -0
  259. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  260. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.d.ts +2 -0
  261. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.d.ts.map +1 -0
  262. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +35 -0
  263. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -0
  264. package/packages/pi-tui/dist/__tests__/tui.test.d.ts +2 -0
  265. package/packages/pi-tui/dist/__tests__/tui.test.d.ts.map +1 -0
  266. package/packages/pi-tui/dist/__tests__/tui.test.js +43 -0
  267. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -0
  268. package/packages/pi-tui/dist/autocomplete.d.ts.map +1 -1
  269. package/packages/pi-tui/dist/autocomplete.js +9 -7
  270. package/packages/pi-tui/dist/autocomplete.js.map +1 -1
  271. package/packages/pi-tui/dist/components/__tests__/editor.test.d.ts +2 -0
  272. package/packages/pi-tui/dist/components/__tests__/editor.test.d.ts.map +1 -0
  273. package/packages/pi-tui/dist/components/__tests__/editor.test.js +54 -0
  274. package/packages/pi-tui/dist/components/__tests__/editor.test.js.map +1 -0
  275. package/packages/pi-tui/dist/components/editor.d.ts +3 -1
  276. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  277. package/packages/pi-tui/dist/components/editor.js +14 -3
  278. package/packages/pi-tui/dist/components/editor.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 +6 -0
  281. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
  282. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  283. package/packages/pi-tui/dist/tui.js +8 -0
  284. package/packages/pi-tui/dist/tui.js.map +1 -1
  285. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +15 -0
  286. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +43 -0
  287. package/packages/pi-tui/src/__tests__/tui.test.ts +50 -0
  288. package/packages/pi-tui/src/autocomplete.ts +9 -7
  289. package/packages/pi-tui/src/components/__tests__/editor.test.ts +64 -0
  290. package/packages/pi-tui/src/components/editor.ts +14 -3
  291. package/packages/pi-tui/src/stdin-buffer.ts +7 -0
  292. package/packages/pi-tui/src/tui.ts +9 -0
  293. package/pkg/package.json +1 -1
  294. package/src/resources/extensions/ask-user-questions.ts +103 -11
  295. package/src/resources/extensions/claude-code-cli/partial-builder.ts +4 -3
  296. package/src/resources/extensions/claude-code-cli/readiness.ts +67 -12
  297. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +12 -3
  298. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +17 -0
  299. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +18 -0
  300. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -1
  301. package/src/resources/extensions/gsd/auto/loop.ts +14 -1
  302. package/src/resources/extensions/gsd/auto/phases.ts +27 -4
  303. package/src/resources/extensions/gsd/auto/run-unit.ts +14 -2
  304. package/src/resources/extensions/gsd/auto/session.ts +1 -1
  305. package/src/resources/extensions/gsd/auto-dashboard.ts +76 -16
  306. package/src/resources/extensions/gsd/auto-dispatch.ts +36 -35
  307. package/src/resources/extensions/gsd/auto-model-selection.ts +12 -3
  308. package/src/resources/extensions/gsd/auto-prompts.ts +195 -25
  309. package/src/resources/extensions/gsd/auto-recovery.ts +15 -15
  310. package/src/resources/extensions/gsd/auto.ts +12 -1
  311. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +34 -1
  312. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +27 -6
  313. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +6 -0
  314. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +67 -6
  315. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +11 -8
  316. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +209 -16
  317. package/src/resources/extensions/gsd/codebase-generator.ts +4 -0
  318. package/src/resources/extensions/gsd/commands/handlers/core.ts +6 -6
  319. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +11 -4
  320. package/src/resources/extensions/gsd/context-store.ts +167 -2
  321. package/src/resources/extensions/gsd/custom-workflow-engine.ts +3 -1
  322. package/src/resources/extensions/gsd/detection.ts +6 -0
  323. package/src/resources/extensions/gsd/files.ts +21 -2
  324. package/src/resources/extensions/gsd/guided-flow.ts +15 -8
  325. package/src/resources/extensions/gsd/index.ts +6 -0
  326. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +2 -0
  327. package/src/resources/extensions/gsd/parsers-legacy.ts +3 -1
  328. package/src/resources/extensions/gsd/preferences.ts +6 -1
  329. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  330. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +7 -7
  331. package/src/resources/extensions/gsd/prompts/discuss.md +3 -3
  332. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -3
  333. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
  334. package/src/resources/extensions/gsd/prompts/rethink.md +6 -2
  335. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  336. package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  337. package/src/resources/extensions/gsd/prompts/validate-milestone.md +4 -4
  338. package/src/resources/extensions/gsd/prompts/worktree-merge.md +3 -1
  339. package/src/resources/extensions/gsd/safety/file-change-validator.ts +4 -1
  340. package/src/resources/extensions/gsd/state.ts +2 -1
  341. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +52 -1
  342. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +50 -2
  343. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +21 -7
  344. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +48 -0
  345. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  346. package/src/resources/extensions/gsd/tests/context-store.test.ts +176 -0
  347. package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +44 -0
  348. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +7 -1
  349. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +31 -0
  350. package/src/resources/extensions/gsd/tests/decision-scope-cascade.test.ts +370 -0
  351. package/src/resources/extensions/gsd/tests/detection.test.ts +37 -0
  352. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +50 -0
  353. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +35 -0
  354. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +34 -0
  355. package/src/resources/extensions/gsd/tests/health-widget.test.ts +45 -0
  356. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +53 -13
  357. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -2
  358. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +3 -3
  359. package/src/resources/extensions/gsd/tests/measurement.test.ts +531 -0
  360. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +3 -4
  361. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +21 -0
  362. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +71 -2
  363. package/src/resources/extensions/gsd/tests/parsers.test.ts +25 -0
  364. package/src/resources/extensions/gsd/tests/preferences.test.ts +20 -0
  365. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +8 -1
  366. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +60 -0
  367. package/src/resources/extensions/gsd/tests/queue-execution-guard.test.ts +9 -0
  368. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +19 -0
  369. package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +73 -0
  370. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +98 -0
  371. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +2 -2
  372. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +26 -0
  373. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +59 -0
  374. package/src/resources/extensions/gsd/tests/workflow-reconcile.test.ts +91 -0
  375. package/src/resources/extensions/gsd/tests/write-gate.test.ts +210 -35
  376. package/src/resources/extensions/gsd/visualizer-overlay.ts +31 -27
  377. package/src/resources/extensions/gsd/workflow-reconcile.ts +59 -8
  378. package/src/resources/extensions/remote-questions/manager.ts +9 -0
  379. package/src/resources/extensions/shared/interview-ui.ts +13 -0
  380. package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +0 -1
  381. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
  382. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
  383. /package/dist/web/standalone/.next/static/{HAq0VE4k68rhRvJbQL1VW → DFZllMYDbO0OwyS6FSvm5}/_buildManifest.js +0 -0
  384. /package/dist/web/standalone/.next/static/{HAq0VE4k68rhRvJbQL1VW → DFZllMYDbO0OwyS6FSvm5}/_ssgManifest.js +0 -0
@@ -10,7 +10,16 @@ import { resumeAutoAfterProviderDelay } from "./provider-error-resume.js";
10
10
  import { classifyError, createRetryState, resetRetryState, isTransient, } from "../error-classifier.js";
11
11
  const retryState = createRetryState();
12
12
  const MAX_NETWORK_RETRIES = 2;
13
- const MAX_TRANSIENT_AUTO_RESUMES = 3;
13
+ const MAX_TRANSIENT_AUTO_RESUMES = 8;
14
+ /**
15
+ * Reset the module-level retry state so a resumed auto-session starts fresh.
16
+ * Called by provider-error-resume.ts before startAuto() — without this, the
17
+ * consecutiveTransientCount accumulates across pause/resume cycles and locks
18
+ * out auto-resume after MAX_TRANSIENT_AUTO_RESUMES total (not consecutive) errors.
19
+ */
20
+ export function resetTransientRetryState() {
21
+ resetRetryState(retryState);
22
+ }
14
23
  async function pauseTransientWithBackoff(cls, pi, ctx, errorDetail, isRateLimit) {
15
24
  retryState.consecutiveTransientCount += 1;
16
25
  const baseRetryAfterMs = "retryAfterMs" in cls ? cls.retryAfterMs : 15_000;
@@ -97,6 +106,28 @@ export async function handleAgentEnd(pi, event, ctx) {
97
106
  const explicitRetryAfterMs = ("retryAfterMs" in lastMsg && typeof lastMsg.retryAfterMs === "number") ? lastMsg.retryAfterMs : undefined;
98
107
  // ── 1. Classify using rawErrorMsg to avoid prose false-positives ────
99
108
  const cls = classifyError(rawErrorMsg, explicitRetryAfterMs);
109
+ // ── 1b. Defer to Core RetryHandler for transient errors ─────────────
110
+ // The Core RetryHandler (agent-session.ts) processes retryable errors
111
+ // AFTER this extension handler, in the same _processAgentEvent() call.
112
+ // For transient errors (overloaded, rate limit, server), the Core will
113
+ // retry in-context — same session, same conversation — which is strictly
114
+ // better than our Layer 2 pause+resume (which creates a new session).
115
+ //
116
+ // If we react here AND the Core also retries, we race: pauseAuto tears
117
+ // down the session while agent.continue() starts a new turn.
118
+ //
119
+ // Solution: Do nothing for transient errors. The Core RetryHandler
120
+ // runs next in _processAgentEvent and will either:
121
+ // a) Retry successfully → new agent_end (success) → we see it next time
122
+ // b) Exhaust retries → the agent stays idle, autoLoop's unit timeout
123
+ // or stuck detection handles it
124
+ //
125
+ // We do NOT call resolveAgentEnd here — that would unblock autoLoop
126
+ // prematurely while the Core is still retrying in the same session.
127
+ // We do NOT call pauseAuto — that would tear down the session.
128
+ if (isTransient(cls)) {
129
+ return;
130
+ }
100
131
  // Cap rate-limit backoff for CLI-style providers (openai-codex, google-gemini-cli)
101
132
  // which use per-user quotas with shorter windows (#2922).
102
133
  if (cls.kind === "rate-limit") {
@@ -5,6 +5,11 @@ import { loadEffectiveGSDPreferences } from "../preferences.js";
5
5
  import { ensureDbOpen } from "./dynamic-tools.js";
6
6
  import { StringEnum } from "@gsd/pi-ai";
7
7
  import { logError } from "../workflow-logger.js";
8
+ import { shouldBlockContextArtifactSave } from "./write-gate.js";
9
+ const SUPPORTED_SUMMARY_ARTIFACT_TYPES = ["SUMMARY", "RESEARCH", "CONTEXT", "ASSESSMENT", "CONTEXT-DRAFT"];
10
+ export function isSupportedSummaryArtifactType(artifactType) {
11
+ return SUPPORTED_SUMMARY_ARTIFACT_TYPES.includes(artifactType);
12
+ }
8
13
  /**
9
14
  * Register an alias tool that shares the same execute function as its canonical counterpart.
10
15
  * The alias description and promptGuidelines direct the LLM to prefer the canonical name.
@@ -274,13 +279,19 @@ export function registerDbTools(pi) {
274
279
  details: { operation: "save_summary", error: "db_unavailable" },
275
280
  };
276
281
  }
277
- const validTypes = ["SUMMARY", "RESEARCH", "CONTEXT", "ASSESSMENT"];
278
- if (!validTypes.includes(params.artifact_type)) {
282
+ if (!isSupportedSummaryArtifactType(params.artifact_type)) {
279
283
  return {
280
- content: [{ type: "text", text: `Error: Invalid artifact_type "${params.artifact_type}". Must be one of: ${validTypes.join(", ")}` }],
284
+ content: [{ type: "text", text: `Error: Invalid artifact_type "${params.artifact_type}". Must be one of: ${SUPPORTED_SUMMARY_ARTIFACT_TYPES.join(", ")}` }],
281
285
  details: { operation: "save_summary", error: "invalid_artifact_type" },
282
286
  };
283
287
  }
288
+ const contextGuard = shouldBlockContextArtifactSave(params.artifact_type, params.milestone_id ?? null, params.slice_id ?? null);
289
+ if (contextGuard.block) {
290
+ return {
291
+ content: [{ type: "text", text: `Error saving artifact: ${contextGuard.reason ?? "context write blocked"}` }],
292
+ details: { operation: "save_summary", error: "context_write_blocked" },
293
+ };
294
+ }
284
295
  try {
285
296
  let relativePath;
286
297
  if (params.task_id && params.slice_id) {
@@ -322,16 +333,17 @@ export function registerDbTools(pi) {
322
333
  "Computes the file path from milestone/slice/task IDs automatically.",
323
334
  promptSnippet: "Save a GSD artifact (summary/research/context/assessment) to DB and disk",
324
335
  promptGuidelines: [
325
- "Use gsd_summary_save to persist structured artifacts (SUMMARY, RESEARCH, CONTEXT, ASSESSMENT).",
336
+ "Use gsd_summary_save to persist structured artifacts (SUMMARY, RESEARCH, CONTEXT, ASSESSMENT, CONTEXT-DRAFT).",
326
337
  "milestone_id is required. slice_id and task_id are optional — they determine the file path.",
327
338
  "The tool computes the relative path automatically: milestones/M001/M001-SUMMARY.md, milestones/M001/slices/S01/S01-SUMMARY.md, etc.",
328
- "artifact_type must be one of: SUMMARY, RESEARCH, CONTEXT, ASSESSMENT.",
339
+ "artifact_type must be one of: SUMMARY, RESEARCH, CONTEXT, ASSESSMENT, CONTEXT-DRAFT.",
340
+ "Use CONTEXT-DRAFT for incremental draft persistence; use CONTEXT for the final milestone context after depth verification.",
329
341
  ],
330
342
  parameters: Type.Object({
331
343
  milestone_id: Type.String({ description: "Milestone ID (e.g. M001)" }),
332
344
  slice_id: Type.Optional(Type.String({ description: "Slice ID (e.g. S01)" })),
333
345
  task_id: Type.Optional(Type.String({ description: "Task ID (e.g. T01)" })),
334
- artifact_type: Type.String({ description: "One of: SUMMARY, RESEARCH, CONTEXT, ASSESSMENT" }),
346
+ artifact_type: Type.String({ description: "One of: SUMMARY, RESEARCH, CONTEXT, ASSESSMENT, CONTEXT-DRAFT" }),
335
347
  content: Type.String({ description: "The full markdown content of the artifact" }),
336
348
  }),
337
349
  execute: summarySaveExecute,
@@ -1,4 +1,5 @@
1
1
  import { getAutoDashboardData, startAuto } from "../auto.js";
2
+ import { resetTransientRetryState } from "./agent-end-recovery.js";
2
3
  const defaultDeps = {
3
4
  getSnapshot: () => getAutoDashboardData(),
4
5
  startAuto,
@@ -13,6 +14,10 @@ export async function resumeAutoAfterProviderDelay(pi, ctx, deps = defaultDeps)
13
14
  ctx.ui.notify("Provider error recovery delay elapsed, but no paused auto-mode base path was available. Leaving auto-mode paused.", "warning");
14
15
  return "missing-base";
15
16
  }
17
+ // Reset the transient retry counter before restarting — without this,
18
+ // consecutiveTransientCount accumulates across pause/resume cycles and
19
+ // permanently locks out auto-resume after MAX_TRANSIENT_AUTO_RESUMES errors.
20
+ resetTransientRetryState();
16
21
  await deps.startAuto(ctx, pi, snapshot.basePath, false, { step: snapshot.stepMode });
17
22
  return "resumed";
18
23
  }
@@ -3,7 +3,7 @@ import { isToolCallEventType } from "@gsd/pi-coding-agent";
3
3
  import { buildMilestoneFileName, resolveMilestonePath, resolveSliceFile, resolveSlicePath } from "../paths.js";
4
4
  import { buildBeforeAgentStartResult } from "./system-context.js";
5
5
  import { handleAgentEnd } from "./agent-end-recovery.js";
6
- import { clearDiscussionFlowState, isDepthVerified, isDepthConfirmationAnswer, isQueuePhaseActive, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockQueueExecution } from "./write-gate.js";
6
+ import { clearDiscussionFlowState, isDepthConfirmationAnswer, isQueuePhaseActive, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockQueueExecution, isGateQuestionId, setPendingGate, clearPendingGate, getPendingGate, shouldBlockPendingGate, shouldBlockPendingGateBash, extractDepthVerificationMilestoneId } from "./write-gate.js";
7
7
  import { isBlockedStateFile, isBashWriteToStateFile, BLOCKED_WRITE_ERROR } from "../write-intercept.js";
8
8
  import { cleanupQuickBranch } from "../quick.js";
9
9
  import { getDiscussionMilestoneId } from "../guided-flow.js";
@@ -21,6 +21,7 @@ import { logWarning as safetyLogWarning } from "../workflow-logger.js";
21
21
  import { installNotifyInterceptor } from "./notify-interceptor.js";
22
22
  import { initNotificationStore } from "../notification-store.js";
23
23
  import { initNotificationWidget } from "../notification-widget.js";
24
+ import { initHealthWidget } from "../health-widget.js";
24
25
  // Skip the welcome screen on the very first session_start — cli.ts already
25
26
  // printed it before the TUI launched. Only re-print on /clear (subsequent sessions).
26
27
  let isFirstSession = true;
@@ -33,6 +34,7 @@ export function registerHooks(pi) {
33
34
  initNotificationStore(process.cwd());
34
35
  installNotifyInterceptor(ctx);
35
36
  initNotificationWidget(ctx);
37
+ initHealthWidget(ctx);
36
38
  resetWriteGateState();
37
39
  resetToolCallLoopGuard();
38
40
  resetAskUserQuestionsCache();
@@ -154,11 +156,42 @@ export function registerHooks(pi) {
154
156
  }
155
157
  });
156
158
  pi.on("tool_call", async (event) => {
159
+ const discussionBasePath = process.cwd();
157
160
  // ── Loop guard: block repeated identical tool calls ──
158
161
  const loopCheck = checkToolCallLoop(event.toolName, event.input);
159
162
  if (loopCheck.block) {
160
163
  return { block: true, reason: loopCheck.reason };
161
164
  }
165
+ // ── Discussion gate enforcement: track pending gate questions ─────────
166
+ // Only gate-shaped ask_user_questions calls should block execution.
167
+ // The gate stays pending until the user selects the approval option.
168
+ if (event.toolName === "ask_user_questions") {
169
+ const milestoneId = getDiscussionMilestoneId(discussionBasePath);
170
+ const inDiscussion = milestoneId !== null || isQueuePhaseActive();
171
+ if (inDiscussion) {
172
+ const questions = event.input?.questions ?? [];
173
+ const questionId = questions.find((question) => typeof question?.id === "string" && isGateQuestionId(question.id))?.id;
174
+ if (typeof questionId === "string") {
175
+ setPendingGate(questionId);
176
+ }
177
+ }
178
+ }
179
+ // ── Discussion gate enforcement: block tool calls while gate is pending ──
180
+ // If ask_user_questions was called with a gate ID but hasn't been confirmed,
181
+ // block all non-read-only tool calls to prevent the model from skipping gates.
182
+ if (getPendingGate()) {
183
+ const milestoneId = getDiscussionMilestoneId(discussionBasePath);
184
+ if (isToolCallEventType("bash", event)) {
185
+ const bashGuard = shouldBlockPendingGateBash(event.input.command, milestoneId, isQueuePhaseActive());
186
+ if (bashGuard.block)
187
+ return bashGuard;
188
+ }
189
+ else {
190
+ const gateGuard = shouldBlockPendingGate(event.toolName, milestoneId, isQueuePhaseActive());
191
+ if (gateGuard.block)
192
+ return gateGuard;
193
+ }
194
+ }
162
195
  // ── Queue-mode execution guard (#2545): block source-code mutations ──
163
196
  // When /gsd queue is active, the agent should only create milestones,
164
197
  // not execute work. Block write/edit to non-.gsd/ paths and bash commands
@@ -197,7 +230,7 @@ export function registerHooks(pi) {
197
230
  }
198
231
  if (!isToolCallEventType("write", event))
199
232
  return;
200
- const result = shouldBlockContextWrite(event.toolName, event.input.path, getDiscussionMilestoneId(), isDepthVerified(), isQueuePhaseActive());
233
+ const result = shouldBlockContextWrite(event.toolName, event.input.path, getDiscussionMilestoneId(discussionBasePath), isQueuePhaseActive());
201
234
  if (result.block)
202
235
  return result;
203
236
  });
@@ -220,21 +253,42 @@ export function registerHooks(pi) {
220
253
  pi.on("tool_result", async (event) => {
221
254
  if (event.toolName !== "ask_user_questions")
222
255
  return;
223
- const milestoneId = getDiscussionMilestoneId();
256
+ const milestoneId = getDiscussionMilestoneId(process.cwd());
224
257
  const queueActive = isQueuePhaseActive();
225
258
  if (!milestoneId && !queueActive)
226
259
  return;
227
260
  const details = event.details;
261
+ // ── Discussion gate enforcement: handle gate question responses ──
262
+ // If the result is cancelled or has no response, the pending gate stays active
263
+ // so the model is blocked from non-read-only tools until it re-asks.
264
+ // If the user responded at all (even "needs adjustment"), clear the pending gate
265
+ // because the user engaged — the prompt handles the re-ask-after-adjustment flow.
266
+ const questions = event.input?.questions ?? [];
267
+ const currentPendingGate = getPendingGate();
268
+ if (currentPendingGate) {
269
+ if (details?.cancelled || !details?.response) {
270
+ // Gate stays pending — model will be blocked from non-read-only tools
271
+ // until it re-asks and gets a valid response
272
+ }
273
+ else {
274
+ const pendingQuestion = questions.find((question) => question?.id === currentPendingGate);
275
+ if (pendingQuestion) {
276
+ const answer = details.response?.answers?.[currentPendingGate];
277
+ if (isDepthConfirmationAnswer(answer?.selected, pendingQuestion.options)) {
278
+ clearPendingGate();
279
+ }
280
+ }
281
+ }
282
+ }
228
283
  if (details?.cancelled || !details?.response)
229
284
  return;
230
- const questions = event.input?.questions ?? [];
231
285
  for (const question of questions) {
232
286
  if (typeof question.id === "string" && question.id.includes("depth_verification")) {
233
287
  // Only unlock the gate if the user selected the first option (confirmation).
234
288
  // Cross-references against the question's defined options to reject free-form "Other" text.
235
289
  const answer = details.response?.answers?.[question.id];
236
290
  if (isDepthConfirmationAnswer(answer?.selected, question.options)) {
237
- markDepthVerified();
291
+ markDepthVerified(extractDepthVerificationMilestoneId(question.id) ?? milestoneId);
238
292
  }
239
293
  break;
240
294
  }
@@ -4,16 +4,18 @@ import { Key } from "@gsd/pi-tui";
4
4
  import { GSDDashboardOverlay } from "../dashboard-overlay.js";
5
5
  import { GSDNotificationOverlay } from "../notification-overlay.js";
6
6
  import { ParallelMonitorOverlay } from "../parallel-monitor-overlay.js";
7
+ import { projectRoot } from "../commands/context.js";
7
8
  import { shortcutDesc } from "../../shared/mod.js";
8
9
  export function registerShortcuts(pi) {
9
10
  pi.registerShortcut(Key.ctrlAlt("g"), {
10
11
  description: shortcutDesc("Open GSD dashboard", "/gsd status"),
11
12
  handler: async (ctx) => {
12
- if (!existsSync(join(process.cwd(), ".gsd"))) {
13
+ const basePath = projectRoot();
14
+ if (!existsSync(join(basePath, ".gsd"))) {
13
15
  ctx.ui.notify("No .gsd/ directory found. Run /gsd to start.", "info");
14
16
  return;
15
17
  }
16
- await ctx.ui.custom((tui, theme, _kb, done) => new GSDDashboardOverlay(tui, theme, () => done()), {
18
+ await ctx.ui.custom((tui, theme, _kb, done) => new GSDDashboardOverlay(tui, theme, () => done(true)), {
17
19
  overlay: true,
18
20
  overlayOptions: {
19
21
  width: "90%",
@@ -27,7 +29,7 @@ export function registerShortcuts(pi) {
27
29
  pi.registerShortcut(Key.ctrlAlt("n"), {
28
30
  description: shortcutDesc("Open notification history", "/gsd notifications"),
29
31
  handler: async (ctx) => {
30
- await ctx.ui.custom((tui, theme, _kb, done) => new GSDNotificationOverlay(tui, theme, () => done()), {
32
+ await ctx.ui.custom((tui, theme, _kb, done) => new GSDNotificationOverlay(tui, theme, () => done(true)), {
31
33
  overlay: true,
32
34
  overlayOptions: {
33
35
  width: "80%",
@@ -42,12 +44,13 @@ export function registerShortcuts(pi) {
42
44
  pi.registerShortcut(Key.ctrlAlt("p"), {
43
45
  description: shortcutDesc("Open parallel worker monitor", "/gsd parallel watch"),
44
46
  handler: async (ctx) => {
45
- const parallelDir = join(process.cwd(), ".gsd", "parallel");
47
+ const basePath = projectRoot();
48
+ const parallelDir = join(basePath, ".gsd", "parallel");
46
49
  if (!existsSync(parallelDir)) {
47
50
  ctx.ui.notify("No parallel workers found. Run /gsd parallel start first.", "info");
48
51
  return;
49
52
  }
50
- await ctx.ui.custom((tui, theme, _kb, done) => new ParallelMonitorOverlay(tui, theme, () => done()), {
53
+ await ctx.ui.custom((tui, theme, _kb, done) => new ParallelMonitorOverlay(tui, theme, () => done(true)), {
51
54
  overlay: true,
52
55
  overlayOptions: {
53
56
  width: "90%",
@@ -1,4 +1,6 @@
1
1
  const MILESTONE_CONTEXT_RE = /M\d+(?:-[a-z0-9]{6})?-CONTEXT\.md$/;
2
+ const CONTEXT_MILESTONE_RE = /(?:^|[/\\])(M\d+(?:-[a-z0-9]{6})?)-CONTEXT\.md$/i;
3
+ const DEPTH_VERIFICATION_MILESTONE_RE = /depth_verification[_-](M\d+(?:-[a-z0-9]{6})?)/i;
2
4
  /**
3
5
  * Path segment that identifies .gsd/ planning artifacts.
4
6
  * Writes to these paths are allowed during queue mode.
@@ -22,10 +24,49 @@ const QUEUE_SAFE_TOOLS = new Set([
22
24
  * Matches the leading command in a bash invocation.
23
25
  */
24
26
  const BASH_READ_ONLY_RE = /^\s*(cat|head|tail|less|more|wc|file|stat|du|df|which|type|echo|printf|ls|find|grep|rg|awk|sed\b(?!.*-i)|sort|uniq|diff|comm|tr|cut|tee\s+-a\s+\/dev\/null|git\s+(log|show|diff|status|branch|tag|remote|rev-parse|ls-files|blame|shortlog|describe|stash\s+list|config\s+--get|cat-file)|gh\s+(issue|pr|api|repo|release)\s+(view|list|diff|status|checks)|mkdir\s+-p\s+\.gsd|rtk\s)/;
25
- let depthVerificationDone = false;
27
+ const verifiedDepthMilestones = new Set();
26
28
  let activeQueuePhase = false;
29
+ /**
30
+ * Discussion gate enforcement state.
31
+ *
32
+ * When ask_user_questions is called with a recognized gate question ID,
33
+ * we track the pending gate. Until the gate is confirmed (user selects the
34
+ * first/recommended option), all non-read-only tool calls are blocked.
35
+ * This mechanically prevents the model from rationalizing past failed or
36
+ * cancelled gate questions.
37
+ */
38
+ let pendingGateId = null;
39
+ /**
40
+ * Recognized gate question ID patterns.
41
+ * These appear in both discuss-prepared.md (4-layer) and discuss.md (depth/requirements/roadmap).
42
+ */
43
+ const GATE_QUESTION_PATTERNS = [
44
+ "layer1_scope_gate",
45
+ "layer2_architecture_gate",
46
+ "layer3_error_gate",
47
+ "layer4_quality_gate",
48
+ "depth_verification",
49
+ ];
50
+ /**
51
+ * Tools that are safe to call while a gate is pending.
52
+ * Includes read-only tools and ask_user_questions itself (so the model can re-ask).
53
+ */
54
+ const GATE_SAFE_TOOLS = new Set([
55
+ "ask_user_questions",
56
+ "read", "grep", "find", "ls", "glob",
57
+ "search-the-web", "resolve_library", "get_library_docs", "fetch_page",
58
+ "search_and_read",
59
+ ]);
27
60
  export function isDepthVerified() {
28
- return depthVerificationDone;
61
+ return verifiedDepthMilestones.size > 0;
62
+ }
63
+ /**
64
+ * Check whether a specific milestone has passed depth verification.
65
+ */
66
+ export function isMilestoneDepthVerified(milestoneId) {
67
+ if (!milestoneId)
68
+ return false;
69
+ return verifiedDepthMilestones.has(milestoneId);
29
70
  }
30
71
  export function isQueuePhaseActive() {
31
72
  return activeQueuePhase;
@@ -34,14 +75,103 @@ export function setQueuePhaseActive(active) {
34
75
  activeQueuePhase = active;
35
76
  }
36
77
  export function resetWriteGateState() {
37
- depthVerificationDone = false;
78
+ verifiedDepthMilestones.clear();
79
+ pendingGateId = null;
38
80
  }
39
81
  export function clearDiscussionFlowState() {
40
- depthVerificationDone = false;
82
+ verifiedDepthMilestones.clear();
41
83
  activeQueuePhase = false;
84
+ pendingGateId = null;
85
+ }
86
+ export function markDepthVerified(milestoneId) {
87
+ if (!milestoneId)
88
+ return;
89
+ verifiedDepthMilestones.add(milestoneId);
90
+ }
91
+ /**
92
+ * Check whether a question ID matches a recognized gate pattern.
93
+ */
94
+ export function isGateQuestionId(questionId) {
95
+ return GATE_QUESTION_PATTERNS.some(pattern => questionId.includes(pattern));
96
+ }
97
+ /**
98
+ * Extract the milestone ID embedded in a depth-verification question id.
99
+ * Prompts are expected to use ids like `depth_verification_M001_confirm`.
100
+ */
101
+ export function extractDepthVerificationMilestoneId(questionId) {
102
+ const match = questionId.match(DEPTH_VERIFICATION_MILESTONE_RE);
103
+ return match?.[1] ?? null;
104
+ }
105
+ /**
106
+ * Extract the milestone ID from a milestone CONTEXT file path.
107
+ */
108
+ function extractContextMilestoneId(inputPath) {
109
+ const match = inputPath.match(CONTEXT_MILESTONE_RE);
110
+ return match?.[1] ?? null;
111
+ }
112
+ /**
113
+ * Mark a gate as pending (called when ask_user_questions is invoked with a gate ID).
114
+ */
115
+ export function setPendingGate(gateId) {
116
+ pendingGateId = gateId;
117
+ }
118
+ /**
119
+ * Clear the pending gate (called when the user confirms).
120
+ */
121
+ export function clearPendingGate() {
122
+ pendingGateId = null;
123
+ }
124
+ /**
125
+ * Get the currently pending gate, if any.
126
+ */
127
+ export function getPendingGate() {
128
+ return pendingGateId;
129
+ }
130
+ /**
131
+ * Check whether a tool call should be blocked because a discussion gate
132
+ * is pending (ask_user_questions was called but not confirmed).
133
+ *
134
+ * Returns { block: true, reason } if the tool should be blocked.
135
+ * Read-only tools and ask_user_questions itself are always allowed.
136
+ */
137
+ export function shouldBlockPendingGate(toolName, _milestoneId, _queuePhaseActive) {
138
+ if (!pendingGateId)
139
+ return { block: false };
140
+ if (GATE_SAFE_TOOLS.has(toolName))
141
+ return { block: false };
142
+ // Bash read-only commands are also safe
143
+ if (toolName === "bash")
144
+ return { block: false }; // bash is checked separately below
145
+ return {
146
+ block: true,
147
+ reason: [
148
+ `HARD BLOCK: Discussion gate "${pendingGateId}" has not been confirmed by the user.`,
149
+ `You MUST re-call ask_user_questions with the gate question before making any other tool calls.`,
150
+ `If the previous ask_user_questions call failed, errored, was cancelled, or the user's response`,
151
+ `did not match a provided option, you MUST re-ask — never rationalize past the block.`,
152
+ `Do NOT proceed, do NOT use alternative approaches, do NOT skip the gate.`,
153
+ ].join(" "),
154
+ };
42
155
  }
43
- export function markDepthVerified() {
44
- depthVerificationDone = true;
156
+ /**
157
+ * Check whether a bash command should be blocked because a discussion gate is pending.
158
+ * Read-only bash commands are allowed; mutating commands are blocked.
159
+ */
160
+ export function shouldBlockPendingGateBash(command, _milestoneId, _queuePhaseActive) {
161
+ if (!pendingGateId)
162
+ return { block: false };
163
+ // Allow read-only bash commands
164
+ if (BASH_READ_ONLY_RE.test(command))
165
+ return { block: false };
166
+ return {
167
+ block: true,
168
+ reason: [
169
+ `HARD BLOCK: Discussion gate "${pendingGateId}" has not been confirmed by the user.`,
170
+ `You MUST re-call ask_user_questions with the gate question before running mutating commands.`,
171
+ `If the previous ask_user_questions call failed, errored, was cancelled, or the user's response`,
172
+ `did not match a provided option, you MUST re-ask — never rationalize past the block.`,
173
+ ].join(" "),
174
+ };
45
175
  }
46
176
  /**
47
177
  * Check whether a depth_verification answer confirms the discussion is complete.
@@ -67,16 +197,23 @@ export function isDepthConfirmationAnswer(selected, options) {
67
197
  // accept only if it contains "(Recommended)" — the prompt convention suffix.
68
198
  return value.includes("(Recommended)");
69
199
  }
70
- export function shouldBlockContextWrite(toolName, inputPath, milestoneId, depthVerified, queuePhaseActive) {
200
+ export function shouldBlockContextWrite(toolName, inputPath, milestoneId, _queuePhaseActive) {
71
201
  if (toolName !== "write")
72
202
  return { block: false };
73
- const inDiscussion = milestoneId !== null;
74
- const inQueue = queuePhaseActive ?? false;
75
- if (!inDiscussion && !inQueue)
76
- return { block: false };
77
203
  if (!MILESTONE_CONTEXT_RE.test(inputPath))
78
204
  return { block: false };
79
- if (depthVerified)
205
+ const targetMilestoneId = extractContextMilestoneId(inputPath) ?? milestoneId;
206
+ if (!targetMilestoneId) {
207
+ return {
208
+ block: true,
209
+ reason: [
210
+ `HARD BLOCK: Cannot write milestone CONTEXT.md without knowing which milestone it belongs to.`,
211
+ `This is a mechanical gate — you MUST NOT proceed, retry, or rationalize past this block.`,
212
+ `Required action: call ask_user_questions with question id containing "depth_verification" and the milestone id.`,
213
+ ].join(" "),
214
+ };
215
+ }
216
+ if (isMilestoneDepthVerified(targetMilestoneId))
80
217
  return { block: false };
81
218
  return {
82
219
  block: true,
@@ -89,6 +226,37 @@ export function shouldBlockContextWrite(toolName, inputPath, milestoneId, depthV
89
226
  ].join(" "),
90
227
  };
91
228
  }
229
+ /**
230
+ * Check whether a gsd_summary_save CONTEXT artifact should be blocked.
231
+ * Slice-level CONTEXT artifacts are allowed; milestone-level CONTEXT writes
232
+ * require the milestone to be depth-verified first.
233
+ */
234
+ export function shouldBlockContextArtifactSave(artifactType, milestoneId, sliceId) {
235
+ if (artifactType !== "CONTEXT")
236
+ return { block: false };
237
+ if (sliceId)
238
+ return { block: false };
239
+ if (!milestoneId) {
240
+ return {
241
+ block: true,
242
+ reason: [
243
+ `HARD BLOCK: Cannot save milestone CONTEXT without a milestone_id.`,
244
+ `This is a mechanical gate — you MUST NOT proceed, retry, or rationalize past this block.`,
245
+ ].join(" "),
246
+ };
247
+ }
248
+ if (isMilestoneDepthVerified(milestoneId))
249
+ return { block: false };
250
+ return {
251
+ block: true,
252
+ reason: [
253
+ `HARD BLOCK: Cannot save milestone CONTEXT without depth verification for ${milestoneId}.`,
254
+ `This is a mechanical gate — you MUST NOT proceed, retry, or rationalize past this block.`,
255
+ `Required action: call ask_user_questions with question id containing "depth_verification_${milestoneId}".`,
256
+ `The user MUST select the "(Recommended)" confirmation option to unlock this gate.`,
257
+ ].join(" "),
258
+ };
259
+ }
92
260
  /**
93
261
  * Queue-mode execution guard (#2545).
94
262
  *
@@ -130,6 +298,10 @@ export function shouldBlockQueueExecution(toolName, input, queuePhaseActive) {
130
298
  `Use read-only commands (cat, grep, git log, etc.) to investigate, then write planning artifacts.`,
131
299
  };
132
300
  }
133
- // Unknown tools — allow by default (custom extension tools, etc.)
134
- return { block: false };
301
+ // Unknown tools — block by default in queue mode so custom tools cannot
302
+ // bypass execution restrictions.
303
+ return {
304
+ block: true,
305
+ reason: `Blocked: /gsd queue is a planning tool — it creates milestones, not executes work. Unknown tools are not permitted during queue mode.`,
306
+ };
135
307
  }
@@ -15,6 +15,10 @@ import { gsdRoot } from "./paths.js";
15
15
  const DEFAULT_EXCLUDES = [
16
16
  ".gsd/",
17
17
  ".planning/",
18
+ ".plans/",
19
+ ".claude/",
20
+ ".cursor/",
21
+ ".vscode/",
18
22
  ".git/",
19
23
  "node_modules/",
20
24
  "dist/",
@@ -77,7 +77,7 @@ export async function handleStatus(ctx) {
77
77
  return;
78
78
  }
79
79
  const { GSDDashboardOverlay } = await import("../../dashboard-overlay.js");
80
- const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDDashboardOverlay(tui, theme, () => done()), {
80
+ const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDDashboardOverlay(tui, theme, () => done(true)), {
81
81
  overlay: true,
82
82
  overlayOptions: {
83
83
  width: "70%",
@@ -99,7 +99,7 @@ export async function handleVisualize(ctx) {
99
99
  return;
100
100
  }
101
101
  const { GSDVisualizerOverlay } = await import("../../visualizer-overlay.js");
102
- const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDVisualizerOverlay(tui, theme, () => done()), {
102
+ const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDVisualizerOverlay(tui, theme, () => done(true)), {
103
103
  overlay: true,
104
104
  overlayOptions: {
105
105
  width: "80%",
@@ -195,7 +195,7 @@ export async function handleCoreCommand(trimmed, ctx) {
195
195
  }
196
196
  if (trimmed === "show-config") {
197
197
  const { GSDConfigOverlay, formatConfigText } = await import("../../config-overlay.js");
198
- const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDConfigOverlay(tui, theme, () => done()), {
198
+ const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDConfigOverlay(tui, theme, () => done(true)), {
199
199
  overlay: true,
200
200
  overlayOptions: {
201
201
  width: "65%",
@@ -240,10 +240,16 @@ async function configureModels(ctx, prefs) {
240
240
  for (const group of byProvider.values()) {
241
241
  group.sort((a, b) => a.id.localeCompare(b.id));
242
242
  }
243
- // Build provider menu with model counts
243
+ // Display names for providers in the preferences wizard UI.
244
+ const PROVIDER_DISPLAY_NAMES = { anthropic: "anthropic-api" };
245
+ const displayName = (p) => PROVIDER_DISPLAY_NAMES[p] ?? p;
246
+ // Build provider menu with model counts (display name → real name lookup)
247
+ const displayToReal = new Map();
244
248
  const providerOptions = providers.map(p => {
245
249
  const count = byProvider.get(p).length;
246
- return `${p} (${count} models)`;
250
+ const label = `${displayName(p)} (${count} models)`;
251
+ displayToReal.set(label, p);
252
+ return label;
247
253
  });
248
254
  providerOptions.push("(keep current)", "(clear)", "(type manually)");
249
255
  for (const phase of modelPhases) {
@@ -267,13 +273,13 @@ async function configureModels(ctx, prefs) {
267
273
  continue;
268
274
  }
269
275
  // Step 2: pick model within provider
270
- const providerName = providerChoice.replace(/ \(\d+ models?\)$/, "");
276
+ const providerName = displayToReal.get(providerChoice) ?? providerChoice.replace(/ \(\d+ models?\)$/, "");
271
277
  const group = byProvider.get(providerName);
272
278
  if (!group)
273
279
  continue;
274
280
  const modelOptions = group.map(m => m.id);
275
281
  modelOptions.push("(keep current)", "(clear)");
276
- const modelChoice = await ctx.ui.select(`${phaseLabel} — ${providerName}:`, modelOptions);
282
+ const modelChoice = await ctx.ui.select(`${phaseLabel} — ${displayName(providerName)}:`, modelOptions);
277
283
  if (modelChoice && typeof modelChoice === "string" && modelChoice !== "(keep current)") {
278
284
  if (modelChoice === "(clear)") {
279
285
  delete models[phase];