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
@@ -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
  /**
4
6
  * Path segment that identifies .gsd/ planning artifacts.
@@ -26,11 +28,53 @@ const QUEUE_SAFE_TOOLS = new Set([
26
28
  */
27
29
  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)/;
28
30
 
29
- let depthVerificationDone = false;
31
+ const verifiedDepthMilestones = new Set<string>();
30
32
  let activeQueuePhase = false;
31
33
 
34
+ /**
35
+ * Discussion gate enforcement state.
36
+ *
37
+ * When ask_user_questions is called with a recognized gate question ID,
38
+ * we track the pending gate. Until the gate is confirmed (user selects the
39
+ * first/recommended option), all non-read-only tool calls are blocked.
40
+ * This mechanically prevents the model from rationalizing past failed or
41
+ * cancelled gate questions.
42
+ */
43
+ let pendingGateId: string | null = null;
44
+
45
+ /**
46
+ * Recognized gate question ID patterns.
47
+ * These appear in both discuss-prepared.md (4-layer) and discuss.md (depth/requirements/roadmap).
48
+ */
49
+ const GATE_QUESTION_PATTERNS = [
50
+ "layer1_scope_gate",
51
+ "layer2_architecture_gate",
52
+ "layer3_error_gate",
53
+ "layer4_quality_gate",
54
+ "depth_verification",
55
+ ] as const;
56
+
57
+ /**
58
+ * Tools that are safe to call while a gate is pending.
59
+ * Includes read-only tools and ask_user_questions itself (so the model can re-ask).
60
+ */
61
+ const GATE_SAFE_TOOLS = new Set([
62
+ "ask_user_questions",
63
+ "read", "grep", "find", "ls", "glob",
64
+ "search-the-web", "resolve_library", "get_library_docs", "fetch_page",
65
+ "search_and_read",
66
+ ]);
67
+
32
68
  export function isDepthVerified(): boolean {
33
- return depthVerificationDone;
69
+ return verifiedDepthMilestones.size > 0;
70
+ }
71
+
72
+ /**
73
+ * Check whether a specific milestone has passed depth verification.
74
+ */
75
+ export function isMilestoneDepthVerified(milestoneId: string | null | undefined): boolean {
76
+ if (!milestoneId) return false;
77
+ return verifiedDepthMilestones.has(milestoneId);
34
78
  }
35
79
 
36
80
  export function isQueuePhaseActive(): boolean {
@@ -42,16 +86,120 @@ export function setQueuePhaseActive(active: boolean): void {
42
86
  }
43
87
 
44
88
  export function resetWriteGateState(): void {
45
- depthVerificationDone = false;
89
+ verifiedDepthMilestones.clear();
90
+ pendingGateId = null;
46
91
  }
47
92
 
48
93
  export function clearDiscussionFlowState(): void {
49
- depthVerificationDone = false;
94
+ verifiedDepthMilestones.clear();
50
95
  activeQueuePhase = false;
96
+ pendingGateId = null;
97
+ }
98
+
99
+ export function markDepthVerified(milestoneId?: string | null): void {
100
+ if (!milestoneId) return;
101
+ verifiedDepthMilestones.add(milestoneId);
102
+ }
103
+
104
+ /**
105
+ * Check whether a question ID matches a recognized gate pattern.
106
+ */
107
+ export function isGateQuestionId(questionId: string): boolean {
108
+ return GATE_QUESTION_PATTERNS.some(pattern => questionId.includes(pattern));
51
109
  }
52
110
 
53
- export function markDepthVerified(): void {
54
- depthVerificationDone = true;
111
+ /**
112
+ * Extract the milestone ID embedded in a depth-verification question id.
113
+ * Prompts are expected to use ids like `depth_verification_M001_confirm`.
114
+ */
115
+ export function extractDepthVerificationMilestoneId(questionId: string): string | null {
116
+ const match = questionId.match(DEPTH_VERIFICATION_MILESTONE_RE);
117
+ return match?.[1] ?? null;
118
+ }
119
+
120
+ /**
121
+ * Extract the milestone ID from a milestone CONTEXT file path.
122
+ */
123
+ function extractContextMilestoneId(inputPath: string): string | null {
124
+ const match = inputPath.match(CONTEXT_MILESTONE_RE);
125
+ return match?.[1] ?? null;
126
+ }
127
+
128
+ /**
129
+ * Mark a gate as pending (called when ask_user_questions is invoked with a gate ID).
130
+ */
131
+ export function setPendingGate(gateId: string): void {
132
+ pendingGateId = gateId;
133
+ }
134
+
135
+ /**
136
+ * Clear the pending gate (called when the user confirms).
137
+ */
138
+ export function clearPendingGate(): void {
139
+ pendingGateId = null;
140
+ }
141
+
142
+ /**
143
+ * Get the currently pending gate, if any.
144
+ */
145
+ export function getPendingGate(): string | null {
146
+ return pendingGateId;
147
+ }
148
+
149
+ /**
150
+ * Check whether a tool call should be blocked because a discussion gate
151
+ * is pending (ask_user_questions was called but not confirmed).
152
+ *
153
+ * Returns { block: true, reason } if the tool should be blocked.
154
+ * Read-only tools and ask_user_questions itself are always allowed.
155
+ */
156
+ export function shouldBlockPendingGate(
157
+ toolName: string,
158
+ _milestoneId: string | null,
159
+ _queuePhaseActive?: boolean,
160
+ ): { block: boolean; reason?: string } {
161
+ if (!pendingGateId) return { block: false };
162
+
163
+ if (GATE_SAFE_TOOLS.has(toolName)) return { block: false };
164
+
165
+ // Bash read-only commands are also safe
166
+ if (toolName === "bash") return { block: false }; // bash is checked separately below
167
+
168
+ return {
169
+ block: true,
170
+ reason: [
171
+ `HARD BLOCK: Discussion gate "${pendingGateId}" has not been confirmed by the user.`,
172
+ `You MUST re-call ask_user_questions with the gate question before making any other tool calls.`,
173
+ `If the previous ask_user_questions call failed, errored, was cancelled, or the user's response`,
174
+ `did not match a provided option, you MUST re-ask — never rationalize past the block.`,
175
+ `Do NOT proceed, do NOT use alternative approaches, do NOT skip the gate.`,
176
+ ].join(" "),
177
+ };
178
+ }
179
+
180
+ /**
181
+ * Check whether a bash command should be blocked because a discussion gate is pending.
182
+ * Read-only bash commands are allowed; mutating commands are blocked.
183
+ */
184
+ export function shouldBlockPendingGateBash(
185
+ command: string,
186
+ _milestoneId: string | null,
187
+ _queuePhaseActive?: boolean,
188
+ ): { block: boolean; reason?: string } {
189
+ if (!pendingGateId) return { block: false };
190
+
191
+ // Allow read-only bash commands
192
+ if (BASH_READ_ONLY_RE.test(command)) return { block: false };
193
+
194
+ return {
195
+ block: true,
196
+ reason: [
197
+ `HARD BLOCK: Discussion gate "${pendingGateId}" has not been confirmed by the user.`,
198
+ `You MUST re-call ask_user_questions with the gate question before running mutating commands.`,
199
+ `If the previous ask_user_questions call failed, errored, was cancelled, or the user's response`,
200
+ `did not match a provided option, you MUST re-ask — never rationalize past the block.`,
201
+ ].join(" "),
202
+ };
55
203
  }
56
204
 
57
205
  /**
@@ -87,16 +235,24 @@ export function shouldBlockContextWrite(
87
235
  toolName: string,
88
236
  inputPath: string,
89
237
  milestoneId: string | null,
90
- depthVerified: boolean,
91
- queuePhaseActive?: boolean,
238
+ _queuePhaseActive?: boolean,
92
239
  ): { block: boolean; reason?: string } {
93
240
  if (toolName !== "write") return { block: false };
94
-
95
- const inDiscussion = milestoneId !== null;
96
- const inQueue = queuePhaseActive ?? false;
97
- if (!inDiscussion && !inQueue) return { block: false };
98
241
  if (!MILESTONE_CONTEXT_RE.test(inputPath)) return { block: false };
99
- if (depthVerified) return { block: false };
242
+
243
+ const targetMilestoneId = extractContextMilestoneId(inputPath) ?? milestoneId;
244
+ if (!targetMilestoneId) {
245
+ return {
246
+ block: true,
247
+ reason: [
248
+ `HARD BLOCK: Cannot write milestone CONTEXT.md without knowing which milestone it belongs to.`,
249
+ `This is a mechanical gate — you MUST NOT proceed, retry, or rationalize past this block.`,
250
+ `Required action: call ask_user_questions with question id containing "depth_verification" and the milestone id.`,
251
+ ].join(" "),
252
+ };
253
+ }
254
+
255
+ if (isMilestoneDepthVerified(targetMilestoneId)) return { block: false };
100
256
 
101
257
  return {
102
258
  block: true,
@@ -110,6 +266,40 @@ export function shouldBlockContextWrite(
110
266
  };
111
267
  }
112
268
 
269
+ /**
270
+ * Check whether a gsd_summary_save CONTEXT artifact should be blocked.
271
+ * Slice-level CONTEXT artifacts are allowed; milestone-level CONTEXT writes
272
+ * require the milestone to be depth-verified first.
273
+ */
274
+ export function shouldBlockContextArtifactSave(
275
+ artifactType: string,
276
+ milestoneId: string | null,
277
+ sliceId?: string | null,
278
+ ): { block: boolean; reason?: string } {
279
+ if (artifactType !== "CONTEXT") return { block: false };
280
+ if (sliceId) return { block: false };
281
+ if (!milestoneId) {
282
+ return {
283
+ block: true,
284
+ reason: [
285
+ `HARD BLOCK: Cannot save milestone CONTEXT without a milestone_id.`,
286
+ `This is a mechanical gate — you MUST NOT proceed, retry, or rationalize past this block.`,
287
+ ].join(" "),
288
+ };
289
+ }
290
+ if (isMilestoneDepthVerified(milestoneId)) return { block: false };
291
+
292
+ return {
293
+ block: true,
294
+ reason: [
295
+ `HARD BLOCK: Cannot save milestone CONTEXT without depth verification for ${milestoneId}.`,
296
+ `This is a mechanical gate — you MUST NOT proceed, retry, or rationalize past this block.`,
297
+ `Required action: call ask_user_questions with question id containing "depth_verification_${milestoneId}".`,
298
+ `The user MUST select the "(Recommended)" confirmation option to unlock this gate.`,
299
+ ].join(" "),
300
+ };
301
+ }
302
+
113
303
  /**
114
304
  * Queue-mode execution guard (#2545).
115
305
  *
@@ -155,7 +345,10 @@ export function shouldBlockQueueExecution(
155
345
  };
156
346
  }
157
347
 
158
- // Unknown tools — allow by default (custom extension tools, etc.)
159
- return { block: false };
348
+ // Unknown tools — block by default in queue mode so custom tools cannot
349
+ // bypass execution restrictions.
350
+ return {
351
+ block: true,
352
+ reason: `Blocked: /gsd queue is a planning tool — it creates milestones, not executes work. Unknown tools are not permitted during queue mode.`,
353
+ };
160
354
  }
161
-
@@ -38,6 +38,10 @@ interface DirectoryGroup {
38
38
  const DEFAULT_EXCLUDES = [
39
39
  ".gsd/",
40
40
  ".planning/",
41
+ ".plans/",
42
+ ".claude/",
43
+ ".cursor/",
44
+ ".vscode/",
41
45
  ".git/",
42
46
  "node_modules/",
43
47
  "dist/",
@@ -15,7 +15,7 @@ export interface GsdCommandDefinition {
15
15
  type CompletionMap = Record<string, readonly GsdCommandDefinition[]>;
16
16
 
17
17
  export const GSD_COMMAND_DESCRIPTION =
18
- "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|discuss|capture|triage|dispatch|history|undo|undo-task|reset-slice|rate|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|inspect|extensions|update|fast|mcp|rethink|codebase|notifications";
18
+ "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|discuss|capture|triage|dispatch|history|undo|undo-task|reset-slice|rate|skip|export|cleanup|model|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|inspect|extensions|update|fast|mcp|rethink|codebase|notifications";
19
19
 
20
20
  export const TOP_LEVEL_SUBCOMMANDS: readonly GsdCommandDefinition[] = [
21
21
  { cmd: "help", desc: "Categorized command reference with descriptions" },
@@ -41,6 +41,7 @@ export const TOP_LEVEL_SUBCOMMANDS: readonly GsdCommandDefinition[] = [
41
41
  { cmd: "skip", desc: "Prevent a unit from auto-mode dispatch" },
42
42
  { cmd: "export", desc: "Export milestone/slice results" },
43
43
  { cmd: "cleanup", desc: "Remove merged branches or snapshots" },
44
+ { cmd: "model", desc: "Switch the active session model or open a picker" },
44
45
  { cmd: "mode", desc: "Switch workflow mode (solo/team)" },
45
46
  { cmd: "prefs", desc: "Manage preferences (model selection, timeouts, etc.)" },
46
47
  { cmd: "config", desc: "Set API keys for external tools" },
@@ -14,7 +14,7 @@ export async function handleGSDCommand(
14
14
  const trimmed = (typeof args === "string" ? args : "").trim();
15
15
 
16
16
  const handlers = [
17
- () => handleCoreCommand(trimmed, ctx),
17
+ () => handleCoreCommand(trimmed, ctx, pi),
18
18
  () => handleAutoCommand(trimmed, ctx, pi),
19
19
  () => handleParallelCommand(trimmed, ctx, pi),
20
20
  () => handleWorkflowCommand(trimmed, ctx, pi),
@@ -29,4 +29,3 @@ export async function handleGSDCommand(
29
29
 
30
30
  ctx.ui.notify(`Unknown: /gsd ${trimmed}. Run /gsd help for available commands.`, "warning");
31
31
  }
32
-
@@ -1,4 +1,5 @@
1
- import type { ExtensionCommandContext, ExtensionContext } from "@gsd/pi-coding-agent";
1
+ import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from "@gsd/pi-coding-agent";
2
+ import type { Model } from "@gsd/pi-ai";
2
3
  import type { GSDState } from "../../types.js";
3
4
 
4
5
  import { computeProgressScore, formatProgressLine } from "../../progress-score.js";
@@ -48,6 +49,7 @@ export function showHelp(ctx: ExtensionCommandContext): void {
48
49
  "SETUP & CONFIGURATION",
49
50
  " /gsd init Project init wizard — detect, configure, bootstrap .gsd/",
50
51
  " /gsd setup Global setup status [llm|search|remote|keys|prefs]",
52
+ " /gsd model Switch active session model [provider/model|model-id]",
51
53
  " /gsd mode Set workflow mode (solo/team) [global|project]",
52
54
  " /gsd prefs Manage preferences [global|project|status|wizard|setup|import-claude]",
53
55
  " /gsd cmux Manage cmux integration [status|on|off|notifications|sidebar|splits|browser]",
@@ -84,8 +86,8 @@ export async function handleStatus(ctx: ExtensionCommandContext): Promise<void>
84
86
  }
85
87
 
86
88
  const { GSDDashboardOverlay } = await import("../../dashboard-overlay.js");
87
- const result = await ctx.ui.custom<void>(
88
- (tui, theme, _kb, done) => new GSDDashboardOverlay(tui, theme, () => done()),
89
+ const result = await ctx.ui.custom<boolean>(
90
+ (tui, theme, _kb, done) => new GSDDashboardOverlay(tui, theme, () => done(true)),
89
91
  {
90
92
  overlay: true,
91
93
  overlayOptions: {
@@ -113,8 +115,8 @@ export async function handleVisualize(ctx: ExtensionCommandContext): Promise<voi
113
115
  }
114
116
 
115
117
  const { GSDVisualizerOverlay } = await import("../../visualizer-overlay.js");
116
- const result = await ctx.ui.custom<void>(
117
- (tui, theme, _kb, done) => new GSDVisualizerOverlay(tui, theme, () => done()),
118
+ const result = await ctx.ui.custom<boolean>(
119
+ (tui, theme, _kb, done) => new GSDVisualizerOverlay(tui, theme, () => done(true)),
118
120
  {
119
121
  overlay: true,
120
122
  overlayOptions: {
@@ -179,7 +181,106 @@ export async function handleSetup(args: string, ctx: ExtensionCommandContext): P
179
181
  );
180
182
  }
181
183
 
182
- export async function handleCoreCommand(trimmed: string, ctx: ExtensionCommandContext): Promise<boolean> {
184
+ function sortModelsForSelection(models: Model<any>[], currentModel: Model<any> | undefined): Model<any>[] {
185
+ return [...models].sort((a, b) => {
186
+ const aCurrent = currentModel && a.provider === currentModel.provider && a.id === currentModel.id;
187
+ const bCurrent = currentModel && b.provider === currentModel.provider && b.id === currentModel.id;
188
+ if (aCurrent && !bCurrent) return -1;
189
+ if (!aCurrent && bCurrent) return 1;
190
+ const providerCmp = a.provider.localeCompare(b.provider);
191
+ if (providerCmp !== 0) return providerCmp;
192
+ return a.id.localeCompare(b.id);
193
+ });
194
+ }
195
+
196
+ async function resolveRequestedModel(
197
+ query: string,
198
+ ctx: ExtensionCommandContext,
199
+ ): Promise<Model<any> | undefined> {
200
+ const { resolveModelId } = await import("../../auto-model-selection.js");
201
+ const models = ctx.modelRegistry.getAvailable();
202
+ const exact = resolveModelId(query, models, ctx.model?.provider);
203
+ if (exact) return exact;
204
+
205
+ const lowerQuery = query.toLowerCase();
206
+ const partialMatches = models.filter((model) =>
207
+ model.id.toLowerCase().includes(lowerQuery)
208
+ || `${model.provider}/${model.id}`.toLowerCase().includes(lowerQuery),
209
+ );
210
+
211
+ if (partialMatches.length === 1) return partialMatches[0];
212
+ if (partialMatches.length === 0 || !ctx.hasUI) return undefined;
213
+
214
+ const sorted = sortModelsForSelection(partialMatches, ctx.model);
215
+ const optionToModel = new Map<string, Model<any>>();
216
+ const options = sorted.map((model) => {
217
+ const label = `${model.provider}/${model.id}`;
218
+ optionToModel.set(label, model);
219
+ return label;
220
+ });
221
+ options.push("(cancel)");
222
+
223
+ const choice = await ctx.ui.select(`Multiple models match "${query}" — choose one:`, options);
224
+ if (!choice || typeof choice !== "string" || choice === "(cancel)") return undefined;
225
+ return optionToModel.get(choice);
226
+ }
227
+
228
+ async function handleModel(trimmedArgs: string, ctx: ExtensionCommandContext, pi: ExtensionAPI | undefined): Promise<void> {
229
+ const availableModels = ctx.modelRegistry.getAvailable();
230
+ if (availableModels.length === 0) {
231
+ ctx.ui.notify("No available models found. Check provider auth and model discovery.", "warning");
232
+ return;
233
+ }
234
+ if (!pi) {
235
+ ctx.ui.notify("Model switching is unavailable in this context.", "warning");
236
+ return;
237
+ }
238
+
239
+ const trimmed = trimmedArgs.trim();
240
+ let targetModel: Model<any> | undefined;
241
+
242
+ if (!trimmed) {
243
+ if (!ctx.hasUI) {
244
+ const current = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "(none)";
245
+ ctx.ui.notify(`Current model: ${current}\nUsage: /gsd model <provider/model|model-id>`, "info");
246
+ return;
247
+ }
248
+
249
+ const optionToModel = new Map<string, Model<any>>();
250
+ const options = sortModelsForSelection(availableModels, ctx.model).map((model) => {
251
+ const isCurrent = ctx.model && model.provider === ctx.model.provider && model.id === ctx.model.id;
252
+ const label = `${isCurrent ? "* " : ""}${model.provider}/${model.id}`;
253
+ optionToModel.set(label, model);
254
+ return label;
255
+ });
256
+ options.push("(cancel)");
257
+
258
+ const choice = await ctx.ui.select("Select session model:", options);
259
+ if (!choice || typeof choice !== "string" || choice === "(cancel)") return;
260
+ targetModel = optionToModel.get(choice);
261
+ } else {
262
+ targetModel = await resolveRequestedModel(trimmed, ctx);
263
+ }
264
+
265
+ if (!targetModel) {
266
+ ctx.ui.notify(`Model "${trimmed}" not found. Use /gsd model with an exact provider/model or a unique model ID.`, "warning");
267
+ return;
268
+ }
269
+
270
+ const ok = await pi.setModel(targetModel);
271
+ if (!ok) {
272
+ ctx.ui.notify(`No API key for ${targetModel.provider}/${targetModel.id}`, "warning");
273
+ return;
274
+ }
275
+
276
+ ctx.ui.notify(`Model: ${targetModel.provider}/${targetModel.id}`, "info");
277
+ }
278
+
279
+ export async function handleCoreCommand(
280
+ trimmed: string,
281
+ ctx: ExtensionCommandContext,
282
+ pi?: ExtensionAPI,
283
+ ): Promise<boolean> {
183
284
  if (trimmed === "help" || trimmed === "h" || trimmed === "?") {
184
285
  showHelp(ctx);
185
286
  return true;
@@ -203,6 +304,10 @@ export async function handleCoreCommand(trimmed: string, ctx: ExtensionCommandCo
203
304
  ctx.ui.notify(`Widget: ${getWidgetMode()}`, "info");
204
305
  return true;
205
306
  }
307
+ if (trimmed === "model" || trimmed.startsWith("model ")) {
308
+ await handleModel(trimmed.replace(/^model\s*/, "").trim(), ctx, pi);
309
+ return true;
310
+ }
206
311
  if (trimmed === "mode" || trimmed.startsWith("mode ")) {
207
312
  const modeArgs = trimmed.replace(/^mode\s*/, "").trim();
208
313
  const scope = modeArgs === "project" ? "project" : "global";
@@ -221,8 +326,8 @@ export async function handleCoreCommand(trimmed: string, ctx: ExtensionCommandCo
221
326
  }
222
327
  if (trimmed === "show-config") {
223
328
  const { GSDConfigOverlay, formatConfigText } = await import("../../config-overlay.js");
224
- const result = await ctx.ui.custom<void>(
225
- (tui, theme, _kb, done) => new GSDConfigOverlay(tui, theme, () => done()),
329
+ const result = await ctx.ui.custom<boolean>(
330
+ (tui, theme, _kb, done) => new GSDConfigOverlay(tui, theme, () => done(true)),
226
331
  {
227
332
  overlay: true,
228
333
  overlayOptions: {
@@ -165,10 +165,10 @@ export function buildCategorySummaries(prefs: Record<string, unknown>): Record<s
165
165
  const modeSummary = mode ?? "(not set)";
166
166
 
167
167
  // Models
168
- const models = prefs.models as Record<string, string> | undefined;
168
+ const models = prefs.models as Record<string, unknown> | undefined;
169
169
  let modelsSummary = "(not configured)";
170
170
  if (models && Object.keys(models).length > 0) {
171
- const parts = Object.entries(models).map(([phase, model]) => `${phase}: ${model}`);
171
+ const parts = Object.entries(models).map(([phase, model]) => `${phase}: ${formatConfiguredModel(model)}`);
172
172
  modelsSummary = parts.join(", ");
173
173
  }
174
174
 
@@ -255,9 +255,38 @@ export function buildCategorySummaries(prefs: Record<string, unknown>): Record<s
255
255
 
256
256
  // ─── Category configuration functions ────────────────────────────────────────
257
257
 
258
+ export function formatConfiguredModel(config: unknown): string {
259
+ if (typeof config === "string") return config;
260
+ if (!config || typeof config !== "object") return "(invalid)";
261
+ const maybeConfig = config as { model?: unknown; provider?: unknown };
262
+ if (typeof maybeConfig.model !== "string" || maybeConfig.model.trim() === "") return "(invalid)";
263
+ if (typeof maybeConfig.provider === "string" && maybeConfig.provider && !maybeConfig.model.includes("/")) {
264
+ return `${maybeConfig.provider}/${maybeConfig.model}`;
265
+ }
266
+ return maybeConfig.model;
267
+ }
268
+
269
+ export function toPersistedModelId(provider: string, modelId: string): string {
270
+ if (!provider.trim()) return modelId;
271
+ const normalizedProvider = provider.trim();
272
+ const normalizedModelId = modelId.trim();
273
+ return normalizedModelId.startsWith(`${normalizedProvider}/`)
274
+ ? normalizedModelId
275
+ : `${normalizedProvider}/${normalizedModelId}`;
276
+ }
277
+
258
278
  async function configureModels(ctx: ExtensionCommandContext, prefs: Record<string, unknown>): Promise<void> {
259
- const modelPhases = ["research", "planning", "execution", "completion"] as const;
260
- const models: Record<string, string> = (prefs.models as Record<string, string>) ?? {};
279
+ const modelPhases = [
280
+ "research",
281
+ "planning",
282
+ "discuss",
283
+ "execution",
284
+ "execution_simple",
285
+ "completion",
286
+ "validation",
287
+ "subagent",
288
+ ] as const;
289
+ const models: Record<string, unknown> = (prefs.models as Record<string, unknown>) ?? {};
261
290
 
262
291
  const availableModels = ctx.modelRegistry.getAvailable();
263
292
  if (availableModels.length > 0) {
@@ -277,15 +306,22 @@ async function configureModels(ctx: ExtensionCommandContext, prefs: Record<strin
277
306
  group.sort((a, b) => a.id.localeCompare(b.id));
278
307
  }
279
308
 
280
- // Build provider menu with model counts
309
+ // Display names for providers in the preferences wizard UI.
310
+ const PROVIDER_DISPLAY_NAMES: Record<string, string> = { anthropic: "anthropic-api" };
311
+ const displayName = (p: string) => PROVIDER_DISPLAY_NAMES[p] ?? p;
312
+
313
+ // Build provider menu with model counts (display name → real name lookup)
314
+ const displayToReal = new Map<string, string>();
281
315
  const providerOptions = providers.map(p => {
282
316
  const count = byProvider.get(p)!.length;
283
- return `${p} (${count} models)`;
317
+ const label = `${displayName(p)} (${count} models)`;
318
+ displayToReal.set(label, p);
319
+ return label;
284
320
  });
285
321
  providerOptions.push("(keep current)", "(clear)", "(type manually)");
286
322
 
287
323
  for (const phase of modelPhases) {
288
- const current = models[phase] ?? "";
324
+ const current = formatConfiguredModel(models[phase]);
289
325
  const phaseLabel = `Model for ${phase} phase${current ? ` (current: ${current})` : ""}`;
290
326
 
291
327
  // Step 1: pick provider
@@ -310,25 +346,25 @@ async function configureModels(ctx: ExtensionCommandContext, prefs: Record<strin
310
346
  }
311
347
 
312
348
  // Step 2: pick model within provider
313
- const providerName = providerChoice.replace(/ \(\d+ models?\)$/, "");
349
+ const providerName = displayToReal.get(providerChoice) ?? providerChoice.replace(/ \(\d+ models?\)$/, "");
314
350
  const group = byProvider.get(providerName);
315
351
  if (!group) continue;
316
352
 
317
353
  const modelOptions = group.map(m => m.id);
318
354
  modelOptions.push("(keep current)", "(clear)");
319
355
 
320
- const modelChoice = await ctx.ui.select(`${phaseLabel} — ${providerName}:`, modelOptions);
356
+ const modelChoice = await ctx.ui.select(`${phaseLabel} — ${displayName(providerName)}:`, modelOptions);
321
357
  if (modelChoice && typeof modelChoice === "string" && modelChoice !== "(keep current)") {
322
358
  if (modelChoice === "(clear)") {
323
359
  delete models[phase];
324
360
  } else {
325
- models[phase] = modelChoice;
361
+ models[phase] = toPersistedModelId(providerName, modelChoice);
326
362
  }
327
363
  }
328
364
  }
329
365
  } else {
330
366
  for (const phase of modelPhases) {
331
- const current = models[phase] ?? "";
367
+ const current = formatConfiguredModel(models[phase]);
332
368
  const input = await ctx.ui.input(
333
369
  `Model for ${phase} phase${current ? ` (current: ${current})` : ""}:`,
334
370
  current || "e.g. claude-sonnet-4-20250514",
@@ -345,6 +381,8 @@ async function configureModels(ctx: ExtensionCommandContext, prefs: Record<strin
345
381
  }
346
382
  if (Object.keys(models).length > 0) {
347
383
  prefs.models = models;
384
+ } else {
385
+ delete prefs.models;
348
386
  }
349
387
  }
350
388