gsd-pi 2.76.0-dev.4100bd590 → 2.76.0-dev.479ad0e78

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 (362) hide show
  1. package/dist/claude-cli-check.js +32 -3
  2. package/dist/mcp-server.d.ts +7 -0
  3. package/dist/mcp-server.js +35 -1
  4. package/dist/onboarding.js +45 -0
  5. package/dist/resource-loader.d.ts +1 -1
  6. package/dist/resource-loader.js +2 -8
  7. package/dist/resources/extensions/claude-code-cli/readiness.js +4 -3
  8. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +77 -17
  9. package/dist/resources/extensions/gsd/auto/loop.js +9 -0
  10. package/dist/resources/extensions/gsd/auto/phases.js +58 -5
  11. package/dist/resources/extensions/gsd/auto/run-unit.js +38 -2
  12. package/dist/resources/extensions/gsd/auto/session.js +22 -1
  13. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -3
  14. package/dist/resources/extensions/gsd/auto-model-selection.js +14 -3
  15. package/dist/resources/extensions/gsd/auto-post-unit.js +25 -2
  16. package/dist/resources/extensions/gsd/auto-prompts.js +14 -0
  17. package/dist/resources/extensions/gsd/auto-recovery.js +32 -1
  18. package/dist/resources/extensions/gsd/auto-start.js +58 -57
  19. package/dist/resources/extensions/gsd/auto-worktree.js +51 -53
  20. package/dist/resources/extensions/gsd/auto.js +70 -28
  21. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -1
  22. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +39 -9
  23. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +93 -0
  24. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +52 -6
  26. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +34 -2
  27. package/dist/resources/extensions/gsd/clean-root-preflight.js +93 -0
  28. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +968 -23
  29. package/dist/resources/extensions/gsd/compaction-snapshot.js +121 -0
  30. package/dist/resources/extensions/gsd/complexity-classifier.js +5 -3
  31. package/dist/resources/extensions/gsd/error-classifier.js +10 -3
  32. package/dist/resources/extensions/gsd/exec-history.js +120 -0
  33. package/dist/resources/extensions/gsd/exec-sandbox.js +258 -0
  34. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  35. package/dist/resources/extensions/gsd/gsd-db.js +149 -31
  36. package/dist/resources/extensions/gsd/guided-flow.js +190 -1
  37. package/dist/resources/extensions/gsd/health-widget.js +4 -1
  38. package/dist/resources/extensions/gsd/init-wizard.js +15 -1
  39. package/dist/resources/extensions/gsd/key-manager.js +28 -0
  40. package/dist/resources/extensions/gsd/model-router.js +36 -3
  41. package/dist/resources/extensions/gsd/pre-execution-checks.js +44 -9
  42. package/dist/resources/extensions/gsd/preferences-types.js +9 -0
  43. package/dist/resources/extensions/gsd/preferences-validation.js +83 -0
  44. package/dist/resources/extensions/gsd/preferences.js +17 -17
  45. package/dist/resources/extensions/gsd/prompt-loader.js +22 -7
  46. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
  47. package/dist/resources/extensions/gsd/prompts/discuss.md +29 -2
  48. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
  49. package/dist/resources/extensions/gsd/safety/evidence-collector.js +96 -0
  50. package/dist/resources/extensions/gsd/safety/file-change-validator.js +13 -5
  51. package/dist/resources/extensions/gsd/safety/safety-harness.js +5 -1
  52. package/dist/resources/extensions/gsd/token-counter.js +22 -5
  53. package/dist/resources/extensions/gsd/tools/complete-milestone.js +16 -10
  54. package/dist/resources/extensions/gsd/tools/exec-search-tool.js +59 -0
  55. package/dist/resources/extensions/gsd/tools/exec-tool.js +126 -0
  56. package/dist/resources/extensions/gsd/tools/resume-tool.js +23 -0
  57. package/dist/resources/extensions/gsd/uok/plan-v2.js +20 -3
  58. package/dist/resources/extensions/gsd/workflow-mcp.js +3 -0
  59. package/dist/resources/extensions/gsd/worktree-resolver.js +50 -10
  60. package/dist/resources/skills/verify-before-complete/SKILL.md +2 -1
  61. package/dist/resources/skills/write-docs/SKILL.md +2 -1
  62. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  63. package/dist/web/standalone/.next/BUILD_ID +1 -1
  64. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  65. package/dist/web/standalone/.next/build-manifest.json +2 -2
  66. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  67. package/dist/web/standalone/.next/required-server-files.json +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/index.html +1 -1
  85. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  92. package/dist/web/standalone/.next/server/chunks/6897.js +2 -2
  93. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  95. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  96. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  97. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  98. package/dist/web/standalone/server.js +1 -1
  99. package/dist/welcome-screen.js +6 -1
  100. package/dist/wizard.js +2 -0
  101. package/package.json +1 -1
  102. package/packages/mcp-server/dist/remote-questions.d.ts +45 -0
  103. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -0
  104. package/packages/mcp-server/dist/remote-questions.js +732 -0
  105. package/packages/mcp-server/dist/remote-questions.js.map +1 -0
  106. package/packages/mcp-server/dist/server.d.ts +7 -0
  107. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  108. package/packages/mcp-server/dist/server.js +70 -8
  109. package/packages/mcp-server/dist/server.js.map +1 -1
  110. package/packages/mcp-server/dist/session-manager.d.ts +14 -0
  111. package/packages/mcp-server/dist/session-manager.d.ts.map +1 -1
  112. package/packages/mcp-server/dist/session-manager.js +49 -1
  113. package/packages/mcp-server/dist/session-manager.js.map +1 -1
  114. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  115. package/packages/mcp-server/dist/workflow-tools.js +64 -25
  116. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  117. package/packages/mcp-server/package.json +2 -1
  118. package/packages/mcp-server/src/mcp-server.test.ts +67 -0
  119. package/packages/mcp-server/src/remote-questions.test.ts +294 -0
  120. package/packages/mcp-server/src/remote-questions.ts +916 -0
  121. package/packages/mcp-server/src/server.ts +89 -14
  122. package/packages/mcp-server/src/session-manager.ts +43 -1
  123. package/packages/mcp-server/src/workflow-tools.test.ts +146 -1
  124. package/packages/mcp-server/src/workflow-tools.ts +84 -43
  125. package/packages/mcp-server/tsconfig.test.json +19 -0
  126. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  127. package/packages/pi-ai/dist/models/custom.d.ts +38 -0
  128. package/packages/pi-ai/dist/models/custom.d.ts.map +1 -1
  129. package/packages/pi-ai/dist/models/custom.js +41 -0
  130. package/packages/pi-ai/dist/models/custom.js.map +1 -1
  131. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +1 -1
  132. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
  133. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  134. package/packages/pi-ai/dist/providers/anthropic-shared.js +27 -4
  135. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  136. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  137. package/packages/pi-ai/dist/providers/anthropic.js +8 -3
  138. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  139. package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts +2 -0
  140. package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts.map +1 -0
  141. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +80 -0
  142. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -0
  143. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  144. package/packages/pi-ai/dist/providers/openai-completions.js +60 -15
  145. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  146. package/packages/pi-ai/dist/providers/simple-options.d.ts +10 -0
  147. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  148. package/packages/pi-ai/dist/providers/simple-options.js +16 -1
  149. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  150. package/packages/pi-ai/dist/providers/think-tag-parser.d.ts +17 -0
  151. package/packages/pi-ai/dist/providers/think-tag-parser.d.ts.map +1 -0
  152. package/packages/pi-ai/dist/providers/think-tag-parser.js +75 -0
  153. package/packages/pi-ai/dist/providers/think-tag-parser.js.map +1 -0
  154. package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts +2 -0
  155. package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts.map +1 -0
  156. package/packages/pi-ai/dist/providers/think-tag-parser.test.js +41 -0
  157. package/packages/pi-ai/dist/providers/think-tag-parser.test.js.map +1 -0
  158. package/packages/pi-ai/src/models/custom.ts +42 -0
  159. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +1 -1
  160. package/packages/pi-ai/src/providers/anthropic-shared.ts +26 -5
  161. package/packages/pi-ai/src/providers/anthropic.ts +9 -3
  162. package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +98 -0
  163. package/packages/pi-ai/src/providers/openai-completions.ts +57 -16
  164. package/packages/pi-ai/src/providers/simple-options.ts +17 -1
  165. package/packages/pi-ai/src/providers/think-tag-parser.test.ts +44 -0
  166. package/packages/pi-ai/src/providers/think-tag-parser.ts +94 -0
  167. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  168. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +3 -2
  169. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  170. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +2 -0
  171. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  172. package/packages/pi-coding-agent/dist/core/agent-session.js +7 -0
  173. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  174. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
  175. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  176. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  177. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
  178. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  179. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  180. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +3 -1
  181. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -1
  182. package/packages/pi-coding-agent/dist/core/model-discovery.js +92 -12
  183. package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/model-discovery.test.js +16 -1
  185. package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts +2 -0
  187. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts.map +1 -0
  188. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js +203 -0
  189. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js.map +1 -0
  190. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +61 -1
  191. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -1
  192. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +5 -0
  193. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  194. package/packages/pi-coding-agent/dist/core/model-registry.js +90 -10
  195. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts +2 -0
  197. package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts.map +1 -0
  198. package/packages/pi-coding-agent/dist/core/redact-secrets.js +49 -0
  199. package/packages/pi-coding-agent/dist/core/redact-secrets.js.map +1 -0
  200. package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts +2 -0
  201. package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts.map +1 -0
  202. package/packages/pi-coding-agent/dist/core/redact-secrets.test.js +67 -0
  203. package/packages/pi-coding-agent/dist/core/redact-secrets.test.js.map +1 -0
  204. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  205. package/packages/pi-coding-agent/dist/core/session-manager.js +10 -6
  206. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/core/session-manager.test.js +45 -1
  208. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  209. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +1 -1
  210. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  211. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +5 -4
  212. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  213. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  214. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +13 -7
  215. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  216. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts +7 -6
  217. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  218. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +29 -21
  219. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  220. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  221. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +13 -1
  222. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  223. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +3 -2
  224. package/packages/pi-coding-agent/src/core/agent-session.ts +11 -0
  225. package/packages/pi-coding-agent/src/core/extensions/runner.ts +2 -0
  226. package/packages/pi-coding-agent/src/core/extensions/types.ts +7 -0
  227. package/packages/pi-coding-agent/src/core/model-discovery.test.ts +19 -0
  228. package/packages/pi-coding-agent/src/core/model-discovery.ts +99 -12
  229. package/packages/pi-coding-agent/src/core/model-registry-custom-caps.test.ts +245 -0
  230. package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +75 -0
  231. package/packages/pi-coding-agent/src/core/model-registry.ts +102 -10
  232. package/packages/pi-coding-agent/src/core/redact-secrets.test.ts +86 -0
  233. package/packages/pi-coding-agent/src/core/redact-secrets.ts +58 -0
  234. package/packages/pi-coding-agent/src/core/session-manager.test.ts +65 -1
  235. package/packages/pi-coding-agent/src/core/session-manager.ts +10 -6
  236. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +6 -6
  237. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +16 -7
  238. package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +36 -22
  239. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +13 -1
  240. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  241. package/scripts/link-workspace-packages.cjs +1 -0
  242. package/src/resources/extensions/claude-code-cli/readiness.ts +4 -3
  243. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +78 -17
  244. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +149 -5
  245. package/src/resources/extensions/gsd/auto/loop-deps.ts +14 -0
  246. package/src/resources/extensions/gsd/auto/loop.ts +9 -0
  247. package/src/resources/extensions/gsd/auto/phases.ts +82 -4
  248. package/src/resources/extensions/gsd/auto/run-unit.ts +40 -2
  249. package/src/resources/extensions/gsd/auto/session.ts +35 -2
  250. package/src/resources/extensions/gsd/auto-dispatch.ts +16 -3
  251. package/src/resources/extensions/gsd/auto-model-selection.ts +17 -2
  252. package/src/resources/extensions/gsd/auto-post-unit.ts +29 -3
  253. package/src/resources/extensions/gsd/auto-prompts.ts +28 -1
  254. package/src/resources/extensions/gsd/auto-recovery.ts +26 -1
  255. package/src/resources/extensions/gsd/auto-start.ts +60 -68
  256. package/src/resources/extensions/gsd/auto-worktree.ts +62 -63
  257. package/src/resources/extensions/gsd/auto.ts +73 -28
  258. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +23 -1
  259. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +40 -9
  260. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +109 -0
  261. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  262. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +54 -6
  263. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +35 -2
  264. package/src/resources/extensions/gsd/clean-root-preflight.ts +111 -0
  265. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +898 -32
  266. package/src/resources/extensions/gsd/compaction-snapshot.ts +165 -0
  267. package/src/resources/extensions/gsd/complexity-classifier.ts +5 -3
  268. package/src/resources/extensions/gsd/error-classifier.ts +10 -3
  269. package/src/resources/extensions/gsd/exec-history.ts +153 -0
  270. package/src/resources/extensions/gsd/exec-sandbox.ts +326 -0
  271. package/src/resources/extensions/gsd/gitignore.ts +1 -1
  272. package/src/resources/extensions/gsd/gsd-db.ts +157 -33
  273. package/src/resources/extensions/gsd/guided-flow.ts +222 -1
  274. package/src/resources/extensions/gsd/health-widget.ts +3 -1
  275. package/src/resources/extensions/gsd/init-wizard.ts +15 -1
  276. package/src/resources/extensions/gsd/journal.ts +2 -1
  277. package/src/resources/extensions/gsd/key-manager.ts +28 -0
  278. package/src/resources/extensions/gsd/model-router.ts +42 -1
  279. package/src/resources/extensions/gsd/pre-execution-checks.ts +46 -10
  280. package/src/resources/extensions/gsd/preferences-types.ts +46 -0
  281. package/src/resources/extensions/gsd/preferences-validation.ts +79 -0
  282. package/src/resources/extensions/gsd/preferences.ts +17 -17
  283. package/src/resources/extensions/gsd/prompt-loader.ts +30 -7
  284. package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
  285. package/src/resources/extensions/gsd/prompts/discuss.md +29 -2
  286. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
  287. package/src/resources/extensions/gsd/safety/evidence-collector.ts +119 -0
  288. package/src/resources/extensions/gsd/safety/file-change-validator.ts +17 -4
  289. package/src/resources/extensions/gsd/safety/safety-harness.ts +9 -0
  290. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +188 -2
  291. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +12 -0
  292. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +12 -0
  293. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +49 -0
  294. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +141 -0
  295. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +33 -3
  296. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +38 -0
  297. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +23 -0
  298. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +186 -0
  299. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +123 -0
  300. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +61 -1
  301. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  302. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  303. package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +3 -3
  304. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
  305. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +31 -0
  306. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +1 -1
  307. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +1 -1
  308. package/src/resources/extensions/gsd/tests/escalation.test.ts +1 -1
  309. package/src/resources/extensions/gsd/tests/exec-history.test.ts +237 -0
  310. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +210 -0
  311. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +58 -0
  312. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +447 -1
  313. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +27 -0
  314. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +1 -0
  315. package/src/resources/extensions/gsd/tests/integration/gitignore-tracked-gsd.test.ts +1 -0
  316. package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +30 -0
  317. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +1 -1
  318. package/src/resources/extensions/gsd/tests/issue-4540-regressions.test.ts +288 -0
  319. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +2 -0
  320. package/src/resources/extensions/gsd/tests/key-manager.test.ts +9 -0
  321. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  322. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +12 -0
  323. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  324. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +19 -0
  325. package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +37 -0
  326. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +14 -0
  327. package/src/resources/extensions/gsd/tests/pre-exec-gate-loop.test.ts +272 -0
  328. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +356 -0
  329. package/src/resources/extensions/gsd/tests/preferences.test.ts +110 -0
  330. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +44 -0
  331. package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +49 -0
  332. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +48 -0
  333. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +388 -0
  334. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +9 -3
  335. package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +230 -0
  336. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +205 -0
  337. package/src/resources/extensions/gsd/tests/save-gate-result-render.test.ts +95 -0
  338. package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +413 -0
  339. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +32 -40
  340. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +56 -0
  341. package/src/resources/extensions/gsd/tests/token-counter.test.ts +105 -1
  342. package/src/resources/extensions/gsd/tests/tool-compatibility.test.ts +107 -0
  343. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +23 -0
  344. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +65 -2
  345. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +35 -0
  346. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +6 -1
  347. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -5
  348. package/src/resources/extensions/gsd/tests/write-gate.test.ts +64 -0
  349. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +3 -1
  350. package/src/resources/extensions/gsd/token-counter.ts +22 -5
  351. package/src/resources/extensions/gsd/tools/complete-milestone.ts +15 -9
  352. package/src/resources/extensions/gsd/tools/exec-search-tool.ts +81 -0
  353. package/src/resources/extensions/gsd/tools/exec-tool.ts +183 -0
  354. package/src/resources/extensions/gsd/tools/resume-tool.ts +40 -0
  355. package/src/resources/extensions/gsd/uok/plan-v2.ts +26 -3
  356. package/src/resources/extensions/gsd/workflow-logger.ts +3 -1
  357. package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
  358. package/src/resources/extensions/gsd/worktree-resolver.ts +54 -9
  359. package/src/resources/skills/verify-before-complete/SKILL.md +2 -1
  360. package/src/resources/skills/write-docs/SKILL.md +2 -1
  361. /package/dist/web/standalone/.next/static/{YnUwu2WWaT0_hyTLUF4nq → JgU2F-5N9mTyB7kUSSk9A}/_buildManifest.js +0 -0
  362. /package/dist/web/standalone/.next/static/{YnUwu2WWaT0_hyTLUF4nq → JgU2F-5N9mTyB7kUSSk9A}/_ssgManifest.js +0 -0
@@ -135,7 +135,7 @@ function openRawDb(path) {
135
135
  const Database = providerModule;
136
136
  return new Database(path);
137
137
  }
138
- const SCHEMA_VERSION = 21;
138
+ const SCHEMA_VERSION = 22;
139
139
  function indexExists(db, name) {
140
140
  return !!db.prepare("SELECT 1 as present FROM sqlite_master WHERE type = 'index' AND name = ?").get(name);
141
141
  }
@@ -491,15 +491,6 @@ function initSchema(db, fileBacked) {
491
491
  )
492
492
  `);
493
493
  db.exec("CREATE INDEX IF NOT EXISTS idx_memories_active ON memories(superseded_by)");
494
- // Existing DBs may arrive here before migrateSchema() has added columns
495
- // that fresh installs already have. Add only columns needed by bootstrap
496
- // indexes so old DBs can open far enough for the normal migration chain.
497
- ensureBootstrapIndexColumns(db);
498
- db.exec("CREATE INDEX IF NOT EXISTS idx_memories_scope ON memories(scope)");
499
- db.exec("CREATE INDEX IF NOT EXISTS idx_memory_sources_kind ON memory_sources(kind)");
500
- db.exec("CREATE INDEX IF NOT EXISTS idx_memory_sources_scope ON memory_sources(scope)");
501
- db.exec("CREATE INDEX IF NOT EXISTS idx_memory_relations_from ON memory_relations(from_id)");
502
- db.exec("CREATE INDEX IF NOT EXISTS idx_memory_relations_to ON memory_relations(to_id)");
503
494
  db.exec("CREATE INDEX IF NOT EXISTS idx_replan_history_milestone ON replan_history(milestone_id, created_at)");
504
495
  // v13 indexes — hot-path dispatch queries
505
496
  db.exec("CREATE INDEX IF NOT EXISTS idx_tasks_active ON tasks(milestone_id, slice_id, status)");
@@ -515,14 +506,21 @@ function initSchema(db, fileBacked) {
515
506
  db.exec("CREATE INDEX IF NOT EXISTS idx_turn_git_tx_turn ON turn_git_transactions(trace_id, turn_id)");
516
507
  db.exec("CREATE INDEX IF NOT EXISTS idx_audit_events_trace ON audit_events(trace_id, ts)");
517
508
  db.exec("CREATE INDEX IF NOT EXISTS idx_audit_events_turn ON audit_events(trace_id, turn_id, ts)");
518
- // ADR-011 Phase 2 — also created by the v17 migration; fresh installs
519
- // skip migrations so the index must be created here too.
520
- db.exec("CREATE INDEX IF NOT EXISTS idx_tasks_escalation_pending ON tasks(milestone_id, slice_id, escalation_pending)");
521
509
  db.exec(`CREATE VIEW IF NOT EXISTS active_decisions AS SELECT * FROM decisions WHERE superseded_by IS NULL`);
522
510
  db.exec(`CREATE VIEW IF NOT EXISTS active_requirements AS SELECT * FROM requirements WHERE superseded_by IS NULL`);
523
511
  db.exec(`CREATE VIEW IF NOT EXISTS active_memories AS SELECT * FROM memories WHERE superseded_by IS NULL`);
524
512
  const existing = db.prepare("SELECT count(*) as cnt FROM schema_version").get();
525
513
  if (existing && existing["cnt"] === 0) {
514
+ // Fresh install — all tables are created above with the full current schema,
515
+ // so it is safe to create all migration-specific indexes here. For existing
516
+ // databases these indexes are created inside the individual migration guards
517
+ // in migrateSchema() after the corresponding columns have been added.
518
+ db.exec("CREATE INDEX IF NOT EXISTS idx_tasks_escalation_pending ON tasks(milestone_id, slice_id, escalation_pending)");
519
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memories_scope ON memories(scope)");
520
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memory_sources_kind ON memory_sources(kind)");
521
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memory_sources_scope ON memory_sources(scope)");
522
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memory_relations_from ON memory_relations(from_id)");
523
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memory_relations_to ON memory_relations(to_id)");
526
524
  db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
527
525
  ":version": SCHEMA_VERSION,
528
526
  ":applied_at": new Date().toISOString(),
@@ -594,13 +592,6 @@ function ensureColumn(db, table, column, ddl) {
594
592
  if (!columnExists(db, table, column))
595
593
  db.exec(ddl);
596
594
  }
597
- function ensureBootstrapIndexColumns(db) {
598
- ensureColumn(db, "memories", "scope", `ALTER TABLE memories ADD COLUMN scope TEXT NOT NULL DEFAULT 'project'`);
599
- ensureColumn(db, "memories", "tags", `ALTER TABLE memories ADD COLUMN tags TEXT NOT NULL DEFAULT '[]'`);
600
- ensureColumn(db, "memory_sources", "scope", `ALTER TABLE memory_sources ADD COLUMN scope TEXT NOT NULL DEFAULT 'project'`);
601
- ensureColumn(db, "memory_sources", "tags", `ALTER TABLE memory_sources ADD COLUMN tags TEXT NOT NULL DEFAULT '[]'`);
602
- ensureColumn(db, "tasks", "escalation_pending", `ALTER TABLE tasks ADD COLUMN escalation_pending INTEGER NOT NULL DEFAULT 0`);
603
- }
604
595
  function migrateSchema(db) {
605
596
  const row = db.prepare("SELECT MAX(version) as v FROM schema_version").get();
606
597
  const currentVersion = row ? row["v"] : 0;
@@ -854,19 +845,24 @@ function migrateSchema(db) {
854
845
  });
855
846
  }
856
847
  if (currentVersion < 12) {
848
+ // NOTE: The original DDL used COALESCE(task_id, '') in the PRIMARY KEY
849
+ // expression, which is invalid SQLite syntax and causes startup errors on
850
+ // DBs that migrate through v12. The corrected DDL uses
851
+ // task_id TEXT NOT NULL DEFAULT '' with a plain column list PK. DBs that
852
+ // were created with the broken DDL are repaired by the v22 migration below.
857
853
  db.exec(`
858
854
  CREATE TABLE IF NOT EXISTS quality_gates (
859
855
  milestone_id TEXT NOT NULL,
860
856
  slice_id TEXT NOT NULL,
861
857
  gate_id TEXT NOT NULL,
862
858
  scope TEXT NOT NULL DEFAULT 'slice',
863
- task_id TEXT DEFAULT NULL,
859
+ task_id TEXT NOT NULL DEFAULT '',
864
860
  status TEXT NOT NULL DEFAULT 'pending',
865
861
  verdict TEXT NOT NULL DEFAULT '',
866
862
  rationale TEXT NOT NULL DEFAULT '',
867
863
  findings TEXT NOT NULL DEFAULT '',
868
864
  evaluated_at TEXT DEFAULT NULL,
869
- PRIMARY KEY (milestone_id, slice_id, gate_id, COALESCE(task_id, '')),
865
+ PRIMARY KEY (milestone_id, slice_id, gate_id, task_id),
870
866
  FOREIGN KEY (milestone_id, slice_id) REFERENCES slices(milestone_id, id)
871
867
  )
872
868
  `);
@@ -1018,6 +1014,10 @@ function migrateSchema(db) {
1018
1014
  tags TEXT NOT NULL DEFAULT '[]'
1019
1015
  )
1020
1016
  `);
1017
+ // If memory_sources already existed before v18 (created by an earlier
1018
+ // version of initSchema that lacked scope/tags), add the missing columns.
1019
+ ensureColumn(db, "memory_sources", "scope", `ALTER TABLE memory_sources ADD COLUMN scope TEXT NOT NULL DEFAULT 'project'`);
1020
+ ensureColumn(db, "memory_sources", "tags", `ALTER TABLE memory_sources ADD COLUMN tags TEXT NOT NULL DEFAULT '[]'`);
1021
1021
  db.exec("CREATE INDEX IF NOT EXISTS idx_memories_scope ON memories(scope)");
1022
1022
  db.exec("CREATE INDEX IF NOT EXISTS idx_memory_sources_kind ON memory_sources(kind)");
1023
1023
  db.exec("CREATE INDEX IF NOT EXISTS idx_memory_sources_scope ON memory_sources(scope)");
@@ -1085,6 +1085,51 @@ function migrateSchema(db) {
1085
1085
  ":applied_at": new Date().toISOString(),
1086
1086
  });
1087
1087
  }
1088
+ if (currentVersion < 22) {
1089
+ // v22: Repair quality_gates tables that were created by the broken v12
1090
+ // migration (which used COALESCE(task_id, '') as a PK expression — invalid
1091
+ // SQLite DDL). Those DBs have task_id nullable (dflt_value NULL, notnull 0).
1092
+ // Rebuild the table with the correct schema, migrating existing rows via
1093
+ // COALESCE so no data is lost.
1094
+ const qgInfo = db.prepare("PRAGMA table_info(quality_gates)").all();
1095
+ const taskIdCol = qgInfo.find((r) => r["name"] === "task_id");
1096
+ const needsRepair = taskIdCol && (taskIdCol["notnull"] === 0 || taskIdCol["notnull"] === "0");
1097
+ if (needsRepair) {
1098
+ db.exec(`
1099
+ CREATE TABLE quality_gates_new (
1100
+ milestone_id TEXT NOT NULL,
1101
+ slice_id TEXT NOT NULL,
1102
+ gate_id TEXT NOT NULL,
1103
+ scope TEXT NOT NULL DEFAULT 'slice',
1104
+ task_id TEXT NOT NULL DEFAULT '',
1105
+ status TEXT NOT NULL DEFAULT 'pending',
1106
+ verdict TEXT NOT NULL DEFAULT '',
1107
+ rationale TEXT NOT NULL DEFAULT '',
1108
+ findings TEXT NOT NULL DEFAULT '',
1109
+ evaluated_at TEXT DEFAULT NULL,
1110
+ PRIMARY KEY (milestone_id, slice_id, gate_id, task_id),
1111
+ FOREIGN KEY (milestone_id, slice_id) REFERENCES slices(milestone_id, id)
1112
+ )
1113
+ `);
1114
+ db.exec(`
1115
+ INSERT OR IGNORE INTO quality_gates_new
1116
+ (milestone_id, slice_id, gate_id, scope, task_id, status, verdict, rationale, findings, evaluated_at)
1117
+ SELECT milestone_id, slice_id, gate_id, scope, COALESCE(task_id, ''), status, verdict, rationale, findings, evaluated_at
1118
+ FROM quality_gates
1119
+ `);
1120
+ db.exec("DROP TABLE quality_gates");
1121
+ db.exec("ALTER TABLE quality_gates_new RENAME TO quality_gates");
1122
+ db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
1123
+ }
1124
+ // Ensure scope column exists on quality_gates and assessments (guard
1125
+ // against DBs that somehow lack it after a partial migration).
1126
+ ensureColumn(db, "quality_gates", "scope", "ALTER TABLE quality_gates ADD COLUMN scope TEXT NOT NULL DEFAULT 'slice'");
1127
+ ensureColumn(db, "assessments", "scope", "ALTER TABLE assessments ADD COLUMN scope TEXT NOT NULL DEFAULT ''");
1128
+ db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
1129
+ ":version": 22,
1130
+ ":applied_at": new Date().toISOString(),
1131
+ });
1132
+ }
1088
1133
  db.exec("COMMIT");
1089
1134
  }
1090
1135
  catch (err) {
@@ -1097,6 +1142,8 @@ let currentPath = null;
1097
1142
  let currentPid = 0;
1098
1143
  let _exitHandlerRegistered = false;
1099
1144
  let _dbOpenAttempted = false;
1145
+ let _lastDbError = null;
1146
+ let _lastDbPhase = null;
1100
1147
  export function getDbProvider() {
1101
1148
  loadProvider();
1102
1149
  return providerName;
@@ -1113,13 +1160,54 @@ export function isDbAvailable() {
1113
1160
  export function wasDbOpenAttempted() {
1114
1161
  return _dbOpenAttempted;
1115
1162
  }
1163
+ export function getDbStatus() {
1164
+ loadProvider();
1165
+ return {
1166
+ available: currentDb !== null,
1167
+ provider: providerName,
1168
+ attempted: _dbOpenAttempted,
1169
+ lastError: _lastDbError,
1170
+ lastPhase: _lastDbPhase,
1171
+ };
1172
+ }
1116
1173
  export function openDatabase(path) {
1117
1174
  _dbOpenAttempted = true;
1118
1175
  if (currentDb && currentPath !== path)
1119
1176
  closeDatabase();
1120
1177
  if (currentDb && currentPath === path)
1121
1178
  return true;
1122
- const rawDb = openRawDb(path);
1179
+ // Reset error state only when a new open attempt is actually going to run.
1180
+ _lastDbError = null;
1181
+ _lastDbPhase = null;
1182
+ let rawDb;
1183
+ let fallbackProvider = null;
1184
+ let fallbackModule = null;
1185
+ try {
1186
+ rawDb = openRawDb(path);
1187
+ }
1188
+ catch (primaryErr) {
1189
+ _lastDbPhase = "open";
1190
+ _lastDbError = primaryErr instanceof Error ? primaryErr : new Error(String(primaryErr));
1191
+ // node:sqlite loaded but failed to open this file — try better-sqlite3 as fallback.
1192
+ if (providerName === "node:sqlite") {
1193
+ try {
1194
+ const mod = _require("better-sqlite3");
1195
+ const Db = (mod && mod.default) ? mod.default : mod;
1196
+ if (typeof Db === "function") {
1197
+ rawDb = new Db(path);
1198
+ fallbackProvider = "better-sqlite3";
1199
+ fallbackModule = Db;
1200
+ _lastDbError = null;
1201
+ _lastDbPhase = null;
1202
+ }
1203
+ }
1204
+ catch {
1205
+ // fallback unavailable; surface original error
1206
+ }
1207
+ }
1208
+ if (!rawDb)
1209
+ throw primaryErr;
1210
+ }
1123
1211
  if (!rawDb)
1124
1212
  return false;
1125
1213
  const adapter = createAdapter(rawDb);
@@ -1137,6 +1225,8 @@ export function openDatabase(path) {
1137
1225
  process.stderr.write("gsd-db: recovered corrupt database via VACUUM\n");
1138
1226
  }
1139
1227
  catch (retryErr) {
1228
+ _lastDbPhase = "vacuum-recovery";
1229
+ _lastDbError = retryErr instanceof Error ? retryErr : new Error(String(retryErr));
1140
1230
  try {
1141
1231
  adapter.close();
1142
1232
  }
@@ -1147,15 +1237,22 @@ export function openDatabase(path) {
1147
1237
  }
1148
1238
  }
1149
1239
  else {
1240
+ _lastDbPhase = "initSchema";
1241
+ _lastDbError = err instanceof Error ? err : new Error(String(err));
1150
1242
  try {
1151
1243
  adapter.close();
1152
1244
  }
1153
1245
  catch (e) {
1154
- logWarning("db", `close after VACUUM failed: ${e.message}`);
1246
+ logWarning("db", `close after initSchema failed: ${e.message}`);
1155
1247
  }
1156
1248
  throw err;
1157
1249
  }
1158
1250
  }
1251
+ // Commit fallback provider switch only after open + schema both succeeded.
1252
+ if (fallbackProvider) {
1253
+ providerName = fallbackProvider;
1254
+ providerModule = fallbackModule;
1255
+ }
1159
1256
  currentDb = adapter;
1160
1257
  currentPath = path;
1161
1258
  currentPid = process.pid;
@@ -1194,8 +1291,12 @@ export function closeDatabase() {
1194
1291
  currentDb = null;
1195
1292
  currentPath = null;
1196
1293
  currentPid = 0;
1197
- _dbOpenAttempted = false;
1198
1294
  }
1295
+ // Reset session-scoped state unconditionally so stale error info from a
1296
+ // failed open doesn't persist into the next open attempt or status check.
1297
+ _dbOpenAttempted = false;
1298
+ _lastDbError = null;
1299
+ _lastDbPhase = null;
1199
1300
  }
1200
1301
  /** Run a full VACUUM — call sparingly (e.g. after milestone completion). */
1201
1302
  export function vacuumDatabase() {
@@ -2295,7 +2396,10 @@ export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
2295
2396
  SELECT path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
2296
2397
  FROM wt.artifacts
2297
2398
  `).run());
2298
- // Merge milestones — worktree may have updated status/planning fields
2399
+ // Merge milestones — worktree may have updated status/planning fields.
2400
+ // Never downgrade status: complete > active > pre-planning (#4372).
2401
+ // A stale worktree may carry an older 'active' status for a milestone
2402
+ // that the main DB has already marked 'complete'; preserve the higher status.
2299
2403
  merged.milestones = countChanges(adapter.prepare(`
2300
2404
  INSERT OR REPLACE INTO milestones (
2301
2405
  id, title, status, depends_on, created_at, completed_at,
@@ -2303,11 +2407,25 @@ export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
2303
2407
  verification_contract, verification_integration, verification_operational, verification_uat,
2304
2408
  definition_of_done, requirement_coverage, boundary_map_markdown
2305
2409
  )
2306
- SELECT id, title, status, depends_on, created_at, completed_at,
2307
- vision, success_criteria, key_risks, proof_strategy,
2308
- verification_contract, verification_integration, verification_operational, verification_uat,
2309
- definition_of_done, requirement_coverage, boundary_map_markdown
2310
- FROM wt.milestones
2410
+ SELECT w.id, w.title,
2411
+ CASE
2412
+ WHEN m.status IN ('complete', 'done') AND w.status NOT IN ('complete', 'done')
2413
+ THEN m.status ELSE w.status
2414
+ END,
2415
+ w.depends_on,
2416
+ CASE
2417
+ WHEN m.status IN ('complete', 'done') AND w.status NOT IN ('complete', 'done')
2418
+ THEN m.created_at ELSE w.created_at
2419
+ END,
2420
+ CASE
2421
+ WHEN m.status IN ('complete', 'done') AND w.status NOT IN ('complete', 'done')
2422
+ THEN m.completed_at ELSE w.completed_at
2423
+ END,
2424
+ w.vision, w.success_criteria, w.key_risks, w.proof_strategy,
2425
+ w.verification_contract, w.verification_integration, w.verification_operational, w.verification_uat,
2426
+ w.definition_of_done, w.requirement_coverage, w.boundary_map_markdown
2427
+ FROM wt.milestones w
2428
+ LEFT JOIN milestones m ON m.id = w.id
2311
2429
  `).run());
2312
2430
  // Merge slices — preserve worktree progress but never downgrade completed status (#2558).
2313
2431
  // ADR-011 Phase 1: carry is_sketch + sketch_scope so reconcile doesn't
@@ -69,7 +69,7 @@ function runPlanV2Gate(ctx, basePath, state) {
69
69
  const compiled = ensurePlanV2Graph(basePath, state);
70
70
  if (!compiled.ok) {
71
71
  const reason = compiled.reason ?? "plan-v2 compilation failed";
72
- ctx.ui.notify(`Plan gate failed-closed: ${reason}. Complete plan/discuss artifacts before execution.`, "error");
72
+ ctx.ui.notify(`Plan gate failed-closed: ${reason}. Complete plan/discuss artifacts before execution.\n\nIf this keeps happening, try: /gsd doctor heal`, "error");
73
73
  return false;
74
74
  }
75
75
  return true;
@@ -79,6 +79,13 @@ function runPlanV2Gate(ctx, basePath, state) {
79
79
  function buildDocsCommitInstruction(_message) {
80
80
  return "Do not commit planning artifacts — .gsd/ is managed externally.";
81
81
  }
82
+ // #4573: cap for how many times we nudge the LLM after a premature ready
83
+ // phrase before giving up and asking the user to re-run /gsd.
84
+ const MAX_READY_REJECTS = 2;
85
+ // #4573: matches the canonical ready phrase the discuss prompt asks the LLM
86
+ // to emit. Accepts any M-prefixed milestone ID (three digits + optional
87
+ // suffix) with optional trailing punctuation.
88
+ const READY_PHRASE_RE = /\bMilestone\s+M\d{3}[A-Z0-9-]*\s+ready\.?/i;
82
89
  const pendingAutoStartMap = new Map();
83
90
  /**
84
91
  * Backward-compat bridge: returns a mutable reference to the entry matching
@@ -225,6 +232,188 @@ export function checkAutoStartAfterDiscuss() {
225
232
  startAutoDetached(ctx, pi, basePath, false, { step });
226
233
  return true;
227
234
  }
235
+ /**
236
+ * Extract the concatenated text content from an assistant message, whether it
237
+ * stores content as a string or as an array of text blocks.
238
+ */
239
+ function extractAssistantText(msg) {
240
+ if (!msg)
241
+ return "";
242
+ const content = msg.content;
243
+ if (typeof content === "string")
244
+ return content;
245
+ if (!Array.isArray(content))
246
+ return "";
247
+ const parts = [];
248
+ for (const block of content) {
249
+ if (!block || typeof block !== "object")
250
+ continue;
251
+ if (block.type === "text" && typeof block.text === "string")
252
+ parts.push(block.text);
253
+ }
254
+ return parts.join("\n");
255
+ }
256
+ /**
257
+ * Return true if the assistant message contains any tool-use block.
258
+ */
259
+ function hasToolUse(msg) {
260
+ if (!msg)
261
+ return false;
262
+ const content = msg.content;
263
+ if (!Array.isArray(content))
264
+ return false;
265
+ return content.some((b) => b && typeof b === "object" && (b.type === "tool_use" || b.type === "tool-use"));
266
+ }
267
+ /**
268
+ * #4573 — Detect and recover from the "ready phrase without files" failure mode.
269
+ *
270
+ * When the LLM emits "Milestone {{id}} ready." but has not written CONTEXT.md
271
+ * or ROADMAP.md, `checkAutoStartAfterDiscuss()` silently returns false and the
272
+ * next /gsd invocation loops into the "All milestones complete" warning.
273
+ *
274
+ * This function, called from `handleAgentEnd` after `checkAutoStartAfterDiscuss`
275
+ * returns false, pattern-matches the ready phrase on the last assistant message.
276
+ * If it fired AND neither CONTEXT.md nor ROADMAP.md exists, it:
277
+ * 1. Notifies the user that the signal was rejected.
278
+ * 2. Injects a system message via `pi.sendMessage(..., {triggerTurn:true})`
279
+ * telling the LLM the signal was premature and to emit the writes now.
280
+ * 3. Caps at `MAX_READY_REJECTS` per-entry; beyond that, gives up and asks
281
+ * the user to re-run /gsd.
282
+ *
283
+ * Returns true when a nudge (or give-up) was emitted, signaling the caller to
284
+ * skip `resolveAgentEnd`.
285
+ */
286
+ export function maybeHandleReadyPhraseWithoutFiles(event) {
287
+ const entry = _getPendingAutoStart();
288
+ if (!entry)
289
+ return false;
290
+ const { ctx, pi, basePath, milestoneId } = entry;
291
+ // Gate: last assistant message must contain the ready phrase
292
+ const lastMsg = event.messages[event.messages.length - 1];
293
+ const text = extractAssistantText(lastMsg);
294
+ if (!READY_PHRASE_RE.test(text))
295
+ return false;
296
+ // Gate: artifacts must still be missing — if they exist, the happy path
297
+ // already fired and we have nothing to do.
298
+ const contextFile = resolveMilestoneFile(basePath, milestoneId, "CONTEXT");
299
+ const roadmapFile = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
300
+ if (contextFile || roadmapFile)
301
+ return false;
302
+ entry.readyRejectCount = (entry.readyRejectCount ?? 0) + 1;
303
+ if (entry.readyRejectCount > MAX_READY_REJECTS) {
304
+ // Give up: clear state and tell the user to re-run /gsd. Avoids an
305
+ // infinite nudge loop when the LLM never produces the writes.
306
+ pendingAutoStartMap.delete(basePath);
307
+ ctx.ui.notify(`Milestone ${milestoneId}: LLM signaled "ready" ${entry.readyRejectCount} times without writing files. ` +
308
+ `Stopping auto-nudge. Run /gsd to try again.`, "error");
309
+ return true;
310
+ }
311
+ ctx.ui.notify(`Milestone ${milestoneId}: "ready" signal rejected — CONTEXT.md and ROADMAP.md are missing. Asking the LLM to complete the writes.`, "warning");
312
+ const nudge = `You emitted "Milestone ${milestoneId} ready." but neither ` +
313
+ `.gsd/milestones/${milestoneId}/${milestoneId}-CONTEXT.md nor ` +
314
+ `.gsd/milestones/${milestoneId}/${milestoneId}-ROADMAP.md exists on disk. ` +
315
+ `The ready phrase is a POST-WRITE signal and has been rejected. ` +
316
+ `In this turn: (1) write PROJECT.md, REQUIREMENTS.md, and the milestone ` +
317
+ `CONTEXT.md, (2) call gsd_plan_milestone, then (3) emit the ready phrase. ` +
318
+ `Do not describe these steps — execute them as tool calls. ` +
319
+ `This is retry ${entry.readyRejectCount}/${MAX_READY_REJECTS}; further ` +
320
+ `premature signals will clear the session.`;
321
+ try {
322
+ pi.sendMessage({ customType: "gsd-ready-no-files", content: nudge, display: false }, { triggerTurn: true });
323
+ }
324
+ catch (e) {
325
+ logWarning("guided", `ready-phrase nudge sendMessage failed: ${e.message}`);
326
+ return false;
327
+ }
328
+ return true;
329
+ }
330
+ /**
331
+ * #4573 — Detect and recover from the "announces tool, never calls it" stall.
332
+ *
333
+ * The LLM emits text like "I'll now write the CONTEXT.md file" but the turn
334
+ * ends with zero tool-use blocks. The harness has no post-turn tool-call
335
+ * validation, so the unit promise resolves and the user sees a stalled state.
336
+ *
337
+ * This function, called from `handleAgentEnd`, inspects the last assistant
338
+ * message. If ALL of the following are true, it injects a recovery message:
339
+ * - Text-only (no tool-use blocks)
340
+ * - Contains a commit-intent phrase ("I'll write", "I'll call", etc.)
341
+ * - Auto-mode is active OR a discussion autostart is pending
342
+ * - `emptyTurnRetryCount` is under the cap
343
+ *
344
+ * Per-handler state is held on the `PendingAutoStartEntry` when present, and
345
+ * on a module-level map otherwise. The counter resets on any successful
346
+ * tool-use turn via `resetEmptyTurnCounter`.
347
+ */
348
+ const emptyTurnCounterByBase = new Map();
349
+ const MAX_EMPTY_TURN_RETRIES = 2;
350
+ // Phrases that indicate the LLM is about to do something but has not yet.
351
+ // Kept tight to avoid flagging legitimate narration like "I'll wait for your answer."
352
+ const COMMIT_INTENT_RE = /\b(?:I['’]ll|I will|Next,? I['’]ll|Now I['’]ll|Let me|I['’]m going to|I am going to)\s+(?:now\s+)?(?:write|create|call|invoke|update|add|make|run|execute|generate|produce|emit|compose|implement|save|apply|commit)\b/i;
353
+ /**
354
+ * Reset the empty-turn counter for a basePath after a successful tool-use turn.
355
+ * Called from handleAgentEnd when the last message contains tool_use blocks.
356
+ */
357
+ export function resetEmptyTurnCounter(basePath) {
358
+ if (basePath)
359
+ emptyTurnCounterByBase.delete(basePath);
360
+ else
361
+ emptyTurnCounterByBase.clear();
362
+ }
363
+ export function maybeHandleEmptyIntentTurn(event, isAuto) {
364
+ // Gate: only fire when there is system-driven work in flight. Interactive
365
+ // /gsd discuss (user-driven) produces legitimate text-only turns.
366
+ if (!isAuto && pendingAutoStartMap.size === 0)
367
+ return false;
368
+ const lastMsg = event.messages[event.messages.length - 1];
369
+ if (!lastMsg)
370
+ return false;
371
+ if (hasToolUse(lastMsg))
372
+ return false;
373
+ const text = extractAssistantText(lastMsg).trim();
374
+ if (!text)
375
+ return false;
376
+ // Skip if the LLM is emitting the ready phrase — that is the ready-no-files
377
+ // path, handled by maybeHandleReadyPhraseWithoutFiles.
378
+ if (READY_PHRASE_RE.test(text))
379
+ return false;
380
+ // Skip if the LLM is clearly handing back to the user. Keep the heuristic
381
+ // tight: a trailing question mark on the last non-empty line is the common
382
+ // signal for "I asked the user a question and stopped."
383
+ const lines = text.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
384
+ const lastLine = lines[lines.length - 1] ?? "";
385
+ if (lastLine.endsWith("?"))
386
+ return false;
387
+ // Must contain a commit-intent phrase — this is the stall we care about.
388
+ if (!COMMIT_INTENT_RE.test(text))
389
+ return false;
390
+ // Resolve the target basePath + pi for injection. Prefer the pending
391
+ // autostart entry (discuss flow); otherwise we cannot inject.
392
+ const entry = _getPendingAutoStart();
393
+ if (!entry)
394
+ return false;
395
+ const { ctx, pi, basePath } = entry;
396
+ const count = (emptyTurnCounterByBase.get(basePath) ?? 0) + 1;
397
+ emptyTurnCounterByBase.set(basePath, count);
398
+ if (count > MAX_EMPTY_TURN_RETRIES) {
399
+ ctx.ui.notify(`Empty-turn recovery: LLM announced intent ${count} times without calling any tool. ` +
400
+ `Stopping auto-nudge.`, "error");
401
+ return false; // let the normal flow resolve/pause the unit
402
+ }
403
+ ctx.ui.notify(`Empty-turn detected: LLM announced intent but called no tool. Prompting it to execute.`, "info");
404
+ const nudge = `Your last turn announced an action (e.g. "I'll write…" or "Let me call…") ` +
405
+ `but contained no tool call. The system records zero tool-use blocks for ` +
406
+ `that turn. Execute the announced action NOW as a tool call in this turn. ` +
407
+ `Do not describe it again. Retry ${count}/${MAX_EMPTY_TURN_RETRIES}.`;
408
+ try {
409
+ pi.sendMessage({ customType: "gsd-empty-turn-recovery", content: nudge, display: false }, { triggerTurn: true });
410
+ }
411
+ catch (e) {
412
+ logWarning("guided", `empty-turn nudge sendMessage failed: ${e.message}`);
413
+ return false;
414
+ }
415
+ return true;
416
+ }
228
417
  /**
229
418
  * Extract milestone IDs from PROJECT.md milestone sequence table.
230
419
  * Looks for rows like "| M001 | Name | Status |" and extracts the ID column.
@@ -91,6 +91,7 @@ export function initHealthWidget(ctx) {
91
91
  let data = initialData;
92
92
  let cachedLines;
93
93
  let refreshInFlight = false;
94
+ let isDisposed = false;
94
95
  const refresh = async () => {
95
96
  if (refreshInFlight)
96
97
  return;
@@ -98,7 +99,8 @@ export function initHealthWidget(ctx) {
98
99
  try {
99
100
  data = loadHealthWidgetData(basePath);
100
101
  cachedLines = undefined;
101
- _tui.requestRender();
102
+ if (!isDisposed)
103
+ _tui.requestRender();
102
104
  }
103
105
  catch { /* non-fatal */ }
104
106
  finally {
@@ -122,6 +124,7 @@ export function initHealthWidget(ctx) {
122
124
  },
123
125
  invalidate() { cachedLines = undefined; cachedWidth = undefined; },
124
126
  dispose() {
127
+ isDisposed = true;
125
128
  clearInterval(refreshTimer);
126
129
  },
127
130
  };
@@ -8,7 +8,7 @@
8
8
  import { existsSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
9
9
  import { join } from "node:path";
10
10
  import { showNextAction } from "../shared/tui.js";
11
- import { nativeInit } from "./native-git-bridge.js";
11
+ import { nativeInit, nativeAddAll, nativeCommit } from "./native-git-bridge.js";
12
12
  import { ensureGitignore, untrackRuntimeFiles } from "./gitignore.js";
13
13
  import { gsdRoot } from "./paths.js";
14
14
  import { assertSafeDirectory } from "./validate-directory.js";
@@ -40,6 +40,7 @@ export async function showProjectInit(ctx, pi, basePath, detection) {
40
40
  ctx.ui.notify(`Project detected:\n${detectionSummary.join("\n")}`, "info");
41
41
  }
42
42
  // ── Step 2: Git setup ──────────────────────────────────────────────────────
43
+ let didInitGit = false;
43
44
  if (!signals.isGitRepo) {
44
45
  const gitChoice = await showNextAction(ctx, {
45
46
  title: "GSD — Project Setup",
@@ -54,6 +55,7 @@ export async function showProjectInit(ctx, pi, basePath, detection) {
54
55
  return { completed: false, bootstrapped: false };
55
56
  if (gitChoice === "init_git") {
56
57
  nativeInit(basePath, prefs.mainBranch);
58
+ didInitGit = true;
57
59
  }
58
60
  }
59
61
  else {
@@ -244,6 +246,18 @@ export async function showProjectInit(ctx, pi, basePath, detection) {
244
246
  // Ensure .gitignore
245
247
  ensureGitignore(basePath);
246
248
  untrackRuntimeFiles(basePath);
249
+ // Create initial commit so git log and git worktree work immediately (#4530).
250
+ // Without this, the branch is "unborn" (zero commits) and downstream operations
251
+ // like `git log` and `git worktree add` fail.
252
+ if (didInitGit) {
253
+ try {
254
+ nativeAddAll(basePath);
255
+ nativeCommit(basePath, "chore: init project");
256
+ }
257
+ catch {
258
+ // Non-fatal — user can commit manually; don't block project init
259
+ }
260
+ }
247
261
  // Auto-generate codebase map for instant agent orientation
248
262
  try {
249
263
  const result = generateCodebaseMap(basePath);
@@ -12,6 +12,12 @@ import { getErrorMessage } from "./error-utils.js";
12
12
  export const PROVIDER_REGISTRY = [
13
13
  // LLM Providers
14
14
  { id: "anthropic", label: "Anthropic (Claude)", category: "llm", envVar: "ANTHROPIC_API_KEY", prefixes: ["sk-ant-"], hasOAuth: true, dashboardUrl: "console.anthropic.com" },
15
+ // Claude Code CLI: routes through the local `claude` binary — no API key,
16
+ // authentication is handled by the CLI's own OAuth flow.
17
+ // Referenced by doctor-providers.ts, auto-model-selection.ts, and others;
18
+ // must be in the canonical registry so all consumers see the same catalog.
19
+ // See: https://github.com/gsd-build/gsd-2/issues/4541
20
+ { id: "claude-code", label: "Claude Code CLI", category: "llm", hasOAuth: true },
15
21
  { id: "openai", label: "OpenAI", category: "llm", envVar: "OPENAI_API_KEY", prefixes: ["sk-"], dashboardUrl: "platform.openai.com/api-keys" },
16
22
  { id: "github-copilot", label: "GitHub Copilot", category: "llm", envVar: "GITHUB_TOKEN", hasOAuth: true },
17
23
  { id: "openai-codex", label: "ChatGPT Plus/Pro (Codex)", category: "llm", hasOAuth: true },
@@ -22,6 +28,8 @@ export const PROVIDER_REGISTRY = [
22
28
  { id: "xai", label: "xAI (Grok)", category: "llm", envVar: "XAI_API_KEY", dashboardUrl: "console.x.ai" },
23
29
  { id: "openrouter", label: "OpenRouter", category: "llm", envVar: "OPENROUTER_API_KEY", dashboardUrl: "openrouter.ai/keys" },
24
30
  { id: "mistral", label: "Mistral", category: "llm", envVar: "MISTRAL_API_KEY", dashboardUrl: "console.mistral.ai" },
31
+ { id: "minimax", label: "MiniMax", category: "llm", envVar: "MINIMAX_API_KEY", dashboardUrl: "platform.minimax.io" },
32
+ { id: "minimax-cn", label: "MiniMax CN", category: "llm", envVar: "MINIMAX_CN_API_KEY", dashboardUrl: "platform.minimax.io" },
25
33
  { id: "ollama-cloud", label: "Ollama Cloud", category: "llm", envVar: "OLLAMA_API_KEY" },
26
34
  { id: "custom-openai", label: "Custom (OpenAI-compat)", category: "llm", envVar: "CUSTOM_OPENAI_API_KEY" },
27
35
  { id: "cerebras", label: "Cerebras", category: "llm", envVar: "CEREBRAS_API_KEY" },
@@ -387,6 +395,26 @@ const TEST_ENDPOINTS = {
387
395
  url: "https://api.mistral.ai/v1/models",
388
396
  headers: (key) => ({ Authorization: `Bearer ${key}` }),
389
397
  },
398
+ minimax: {
399
+ url: "https://api.minimax.io/anthropic/v1/messages",
400
+ method: "POST",
401
+ headers: (key) => ({
402
+ "x-api-key": key,
403
+ "anthropic-version": "2023-06-01",
404
+ "content-type": "application/json",
405
+ }),
406
+ body: JSON.stringify({ model: "MiniMax-M2.7", max_tokens: 1, messages: [{ role: "user", content: "hi" }] }),
407
+ },
408
+ "minimax-cn": {
409
+ url: "https://api.minimaxi.com/anthropic/v1/messages",
410
+ method: "POST",
411
+ headers: (key) => ({
412
+ "x-api-key": key,
413
+ "anthropic-version": "2023-06-01",
414
+ "content-type": "application/json",
415
+ }),
416
+ body: JSON.stringify({ model: "MiniMax-M2.7", max_tokens: 1, messages: [{ role: "user", content: "hi" }] }),
417
+ },
390
418
  openrouter: {
391
419
  url: "https://openrouter.ai/api/v1/models",
392
420
  headers: (key) => ({ Authorization: `Bearer ${key}` }),