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
@@ -180,14 +180,6 @@ function clearProjectRootStateFiles(basePath, milestoneId) {
180
180
  }
181
181
  }
182
182
  }
183
- function isProjectGsdSymlink(basePath) {
184
- try {
185
- return lstatSyncFn(join(basePath, ".gsd")).isSymbolicLink();
186
- }
187
- catch {
188
- return false;
189
- }
190
- }
191
183
  // ─── Build Artifact Auto-Resolve ─────────────────────────────────────────────
192
184
  /** Patterns for machine-generated build artifacts that can be safely
193
185
  * auto-resolved by accepting --theirs during merge. These files are
@@ -1429,50 +1421,15 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1429
1421
  });
1430
1422
  }
1431
1423
  }
1432
- // 7. Stash any pre-existing dirty files so the squash merge is not
1433
- // blocked by unrelated local changes (#2151). clearProjectRootStateFiles
1434
- // only removes untracked .gsd/ files; tracked dirty files elsewhere (e.g.
1435
- // .planning/work-state.json with stash conflict markers) are invisible to
1436
- // that cleanup but will cause `git merge --squash` to reject.
1437
- let stashed = false;
1438
- try {
1439
- const status = execFileSync("git", ["status", "--porcelain"], {
1440
- cwd: originalBasePath_,
1441
- stdio: ["ignore", "pipe", "pipe"],
1442
- encoding: "utf-8",
1443
- }).trim();
1444
- if (status) {
1445
- // Use --include-untracked to stash untracked files that would block
1446
- // the squash merge, but EXCLUDE .gsd/milestones/ (#2505).
1447
- // --include-untracked without exclusion sweeps queued milestone
1448
- // CONTEXT files into the stash. If stash pop later fails, those files
1449
- // are permanently trapped in the stash entry and lost on the next
1450
- // stash push or drop.
1451
- //
1452
- // When `.gsd` itself is a symlink, Git rejects pathspecs below it
1453
- // ("beyond a symbolic link"). In that layout, exclude the whole symlink
1454
- // and keep stashing real project files that could block the merge.
1455
- const stashPathspecs = isProjectGsdSymlink(originalBasePath_)
1456
- ? [".", ":(exclude).gsd"]
1457
- : [":(exclude).gsd/milestones"];
1458
- execFileSync("git", [
1459
- "stash", "push", "--include-untracked",
1460
- "-m", `gsd: pre-merge stash for ${milestoneId}`,
1461
- "--", ...stashPathspecs,
1462
- ], { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" });
1463
- stashed = true;
1464
- }
1465
- }
1466
- catch (err) {
1467
- // Stash failure is non-fatal — proceed without stash and let the merge
1468
- // report the dirty tree if it fails.
1469
- logWarning("worktree", `git stash failed: ${err instanceof Error ? err.message : String(err)}`);
1470
- }
1471
- // 7a. Shelter queued milestone directories before the squash merge (#2505).
1424
+ // 7. Shelter queued milestone directories before the squash merge (#2505).
1472
1425
  // The milestone branch may contain copies of queued milestone dirs (via
1473
1426
  // copyPlanningArtifacts), so `git merge --squash` rejects when those same
1474
1427
  // files exist as untracked in the working tree. Temporarily move them to
1475
1428
  // a backup location, then restore after the merge+commit.
1429
+ //
1430
+ // MUST run BEFORE the pre-merge stash (step 7a) so `--include-untracked`
1431
+ // does not sweep queued CONTEXT files into the stash. If stash pop later
1432
+ // fails, files trapped inside the stash are permanently lost (#2505).
1476
1433
  const milestonesDir = join(gsdRoot(originalBasePath_), "milestones");
1477
1434
  const shelterDir = join(gsdRoot(originalBasePath_), ".milestone-shelter");
1478
1435
  const shelteredDirs = [];
@@ -1526,6 +1483,31 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1526
1483
  // Non-fatal — proceed with merge; untracked files may block it
1527
1484
  logWarning("worktree", `milestone shelter operation failed: ${err instanceof Error ? err.message : String(err)}`);
1528
1485
  }
1486
+ // 7a. Stash pre-existing dirty files so the squash merge is not blocked by
1487
+ // unrelated local changes (#2151). Includes untracked files to handle
1488
+ // locally-added files that conflict with tracked files on the milestone
1489
+ // branch. Passing NO pathspec lets git skip gitignored paths silently;
1490
+ // adding an explicit pathspec trips a `git add`-style fatal on ignored
1491
+ // entries (e.g. a gitignored `.gsd` symlink under ADR-002) (#4573).
1492
+ // Queued CONTEXT files under `.gsd/milestones/*` are already sheltered
1493
+ // in step 7 above, so they won't be swept into the stash.
1494
+ let stashed = false;
1495
+ try {
1496
+ const status = execFileSync("git", ["status", "--porcelain"], {
1497
+ cwd: originalBasePath_,
1498
+ stdio: ["ignore", "pipe", "pipe"],
1499
+ encoding: "utf-8",
1500
+ }).trim();
1501
+ if (status) {
1502
+ execFileSync("git", ["stash", "push", "--include-untracked", "-m", `gsd: pre-merge stash for ${milestoneId}`], { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" });
1503
+ stashed = true;
1504
+ }
1505
+ }
1506
+ catch (err) {
1507
+ // Stash failure is non-fatal — proceed without stash and let the merge
1508
+ // report the dirty tree if it fails.
1509
+ logWarning("worktree", `git stash failed: ${err instanceof Error ? err.message : String(err)}`);
1510
+ }
1529
1511
  // 7b. Clean up stale merge state before attempting squash merge (#2912).
1530
1512
  // A leftover MERGE_HEAD (from a previous failed merge, libgit2 native path,
1531
1513
  // or interrupted operation) causes `git merge --squash` to refuse with
@@ -1761,16 +1743,32 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1761
1743
  // When a milestone only produced .gsd/ metadata (summaries, roadmaps) but no
1762
1744
  // real code, the user sees "milestone complete" but nothing changed in their
1763
1745
  // codebase. Surface this so the caller can warn the user.
1746
+ //
1747
+ // Bug #4385 fix: use `git diff-tree --root` instead of `git diff HEAD~1 HEAD`.
1748
+ // `HEAD~1` does not exist on initial commits and is unreliable on shallow clones
1749
+ // and merge commits. `diff-tree --root` handles all three cases correctly.
1750
+ // The empty-tree hash (4b825dc…) is the universal fallback for refs that don't exist.
1751
+ const GIT_EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
1764
1752
  let codeFilesChanged = false;
1765
1753
  if (!nothingToCommit) {
1766
1754
  try {
1767
- const mergedFiles = nativeDiffNumstat(originalBasePath_, "HEAD~1", "HEAD");
1768
- codeFilesChanged = mergedFiles.some((entry) => !entry.path.startsWith(".gsd/"));
1755
+ const diffTreeOutput = execFileSync("git", ["diff-tree", "--root", "--no-commit-id", "-r", "--name-only", "HEAD"], { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
1756
+ const mergedFiles = diffTreeOutput ? diffTreeOutput.split("\n").filter(Boolean) : [];
1757
+ codeFilesChanged = mergedFiles.some((f) => !f.startsWith(".gsd/"));
1769
1758
  }
1770
1759
  catch (e) {
1771
- // If HEAD~1 doesn't exist (first commit), assume code was changed
1772
- logWarning("worktree", `diff numstat failed (assuming code changed): ${e.message}`);
1773
- codeFilesChanged = true;
1760
+ // diff-tree failed (e.g. unborn HEAD in a brand-new repo) fall back to
1761
+ // comparing against the empty tree so initial-commit repos still report changes.
1762
+ try {
1763
+ const fallbackOutput = execFileSync("git", ["diff", "--name-only", GIT_EMPTY_TREE, "HEAD"], { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
1764
+ const fallbackFiles = fallbackOutput ? fallbackOutput.split("\n").filter(Boolean) : [];
1765
+ codeFilesChanged = fallbackFiles.some((f) => !f.startsWith(".gsd/"));
1766
+ }
1767
+ catch {
1768
+ // Truly unable to determine — assume code was changed to avoid silent data loss
1769
+ logWarning("worktree", `diff-tree and empty-tree fallback both failed (assuming code changed): ${e.message}`);
1770
+ codeFilesChanged = true;
1771
+ }
1774
1772
  }
1775
1773
  }
1776
1774
  // 10. Auto-push if enabled
@@ -37,8 +37,9 @@ import { getRtkSessionSavings } from "../shared/rtk-session-stats.js";
37
37
  import { deactivateGSD } from "../shared/gsd-phase-state.js";
38
38
  import { initMetrics, resetMetrics, getLedger, getProjectTotals, formatCost, formatTokenCount, } from "./metrics.js";
39
39
  import { setLogBasePath, logWarning } from "./workflow-logger.js";
40
+ import { preflightCleanRoot, postflightPopStash } from "./clean-root-preflight.js";
40
41
  import { homedir } from "node:os";
41
- import { join } from "node:path";
42
+ import { isAbsolute, join } from "node:path";
42
43
  import { pathToFileURL } from "node:url";
43
44
  import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
44
45
  import { atomicWriteSync } from "./atomic-write.js";
@@ -55,7 +56,7 @@ import { getErrorMessage } from "./error-utils.js";
55
56
  import { recoverFailedMigration } from "./migrate-external.js";
56
57
  import { initRegistry, convertDispatchRules } from "./rule-registry.js";
57
58
  import { emitJournalEvent as _emitJournalEvent } from "./journal.js";
58
- import { updateProgressWidget as _updateProgressWidget, updateSliceProgressCache, clearSliceProgressCache, hideFooter, } from "./auto-dashboard.js";
59
+ import { updateProgressWidget as _updateProgressWidget, updateSliceProgressCache, clearSliceProgressCache, } from "./auto-dashboard.js";
59
60
  import { registerSigtermHandler as _registerSigtermHandler, deregisterSigtermHandler as _deregisterSigtermHandler, } from "./auto-supervisor.js";
60
61
  import { isDbAvailable, getMilestone } from "./gsd-db.js";
61
62
  import { countPendingCaptures } from "./captures.js";
@@ -136,6 +137,24 @@ function restoreMilestoneLockEnv() {
136
137
  s.hadMilestoneLockEnv = false;
137
138
  s.milestoneLockEnvCaptured = false;
138
139
  }
140
+ function normalizeSessionFilePath(raw) {
141
+ if (typeof raw !== "string")
142
+ return null;
143
+ const trimmed = raw.trim();
144
+ if (!trimmed)
145
+ return null;
146
+ const firstLine = trimmed.split(/\r?\n/, 1)[0]?.trim() ?? "";
147
+ if (!firstLine)
148
+ return null;
149
+ // Guard against accidental message concatenation by trimming to .jsonl.
150
+ const jsonlIndex = firstLine.toLowerCase().indexOf(".jsonl");
151
+ const candidate = jsonlIndex >= 0 ? firstLine.slice(0, jsonlIndex + ".jsonl".length) : firstLine;
152
+ if (!isAbsolute(candidate))
153
+ return null;
154
+ if (!candidate.toLowerCase().endsWith(".jsonl"))
155
+ return null;
156
+ return candidate;
157
+ }
139
158
  export function startAutoDetached(ctx, pi, base, verboseMode, options) {
140
159
  void startAuto(ctx, pi, base, verboseMode, options).catch((err) => {
141
160
  const message = getErrorMessage(err);
@@ -145,8 +164,8 @@ export function startAutoDetached(ctx, pi, base, verboseMode, options) {
145
164
  });
146
165
  }
147
166
  /** Returns true if the project is configured for `isolation:worktree` mode. */
148
- export function shouldUseWorktreeIsolation() {
149
- const prefs = loadEffectiveGSDPreferences()?.preferences?.git;
167
+ export function shouldUseWorktreeIsolation(basePath) {
168
+ const prefs = loadEffectiveGSDPreferences(basePath)?.preferences?.git;
150
169
  if (prefs?.isolation === "worktree")
151
170
  return true;
152
171
  // Default is false — worktree isolation requires explicit opt-in
@@ -215,7 +234,7 @@ export function getAutoDashboardData() {
215
234
  const rtkSavings = sessionId && s.basePath
216
235
  ? getRtkSessionSavings(s.basePath, sessionId)
217
236
  : null;
218
- const rtkEnabled = loadEffectiveGSDPreferences()?.preferences.experimental?.rtk === true;
237
+ const rtkEnabled = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences.experimental?.rtk === true;
219
238
  // Pending capture count — lazy check, non-fatal
220
239
  let pendingCaptureCount = 0;
221
240
  try {
@@ -393,7 +412,7 @@ function clearUnitTimeout() {
393
412
  }
394
413
  /** Build snapshot metric opts. */
395
414
  function buildSnapshotOpts(_unitType, _unitId) {
396
- const prefs = loadEffectiveGSDPreferences()?.preferences;
415
+ const prefs = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences;
397
416
  const uokFlags = resolveUokFlags(prefs);
398
417
  return {
399
418
  ...(s.autoStartTime > 0 ? { autoSessionKey: String(s.autoStartTime) } : {}),
@@ -427,7 +446,7 @@ function handleLostSessionLock(ctx, lockStatus) {
427
446
  restoreProjectRootEnv();
428
447
  restoreMilestoneLockEnv();
429
448
  deregisterSigtermHandler();
430
- clearCmuxSidebar(loadEffectiveGSDPreferences()?.preferences);
449
+ clearCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences);
431
450
  const base = lockBase();
432
451
  const lockFilePath = base ? join(gsdRoot(base), "auto.lock") : "unknown";
433
452
  const recoverySuggestion = "\nTo recover, run: gsd doctor --fix";
@@ -443,7 +462,6 @@ function handleLostSessionLock(ctx, lockStatus) {
443
462
  ctx?.ui.notify(message, "error");
444
463
  ctx?.ui.setStatus("gsd-auto", undefined);
445
464
  ctx?.ui.setWidget("gsd-progress", undefined);
446
- ctx?.ui.setFooter(undefined);
447
465
  if (ctx)
448
466
  initHealthWidget(ctx);
449
467
  }
@@ -480,7 +498,6 @@ function cleanupAfterLoopExit(ctx) {
480
498
  if (!s.paused) {
481
499
  ctx.ui.setStatus("gsd-auto", undefined);
482
500
  ctx.ui.setWidget("gsd-progress", undefined);
483
- ctx.ui.setFooter(undefined);
484
501
  initHealthWidget(ctx);
485
502
  }
486
503
  // Restore CWD out of worktree back to original project root
@@ -498,7 +515,7 @@ function cleanupAfterLoopExit(ctx) {
498
515
  export async function stopAuto(ctx, pi, reason) {
499
516
  if (!s.active && !s.paused)
500
517
  return;
501
- const loadedPreferences = loadEffectiveGSDPreferences()?.preferences;
518
+ const loadedPreferences = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences;
502
519
  const reasonSuffix = reason ? ` — ${reason}` : "";
503
520
  try {
504
521
  // ── Step 1: Timers and locks ──
@@ -695,13 +712,16 @@ export async function stopAuto(ctx, pi, reason) {
695
712
  catch (err) { /* non-fatal */
696
713
  logWarning("engine", `file unlink failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
697
714
  }
698
- // ── Step 13: Restore original model (before reset clears IDs) ──
715
+ // ── Step 13: Restore original model + thinking (before reset clears IDs) ──
699
716
  try {
700
717
  if (pi && ctx && s.originalModelId && s.originalModelProvider) {
701
718
  const original = ctx.modelRegistry.find(s.originalModelProvider, s.originalModelId);
702
719
  if (original)
703
720
  await pi.setModel(original);
704
721
  }
722
+ if (pi && s.originalThinkingLevel) {
723
+ pi.setThinkingLevel(s.originalThinkingLevel);
724
+ }
705
725
  }
706
726
  catch (e) {
707
727
  debugLog("stop-cleanup-model", { error: e instanceof Error ? e.message : String(e) });
@@ -740,7 +760,6 @@ export async function stopAuto(ctx, pi, reason) {
740
760
  // UI cleanup
741
761
  ctx?.ui.setStatus("gsd-auto", undefined);
742
762
  ctx?.ui.setWidget("gsd-progress", undefined);
743
- ctx?.ui.setFooter(undefined);
744
763
  if (ctx)
745
764
  initHealthWidget(ctx);
746
765
  restoreProjectRootEnv();
@@ -775,7 +794,7 @@ export async function pauseAuto(ctx, _pi, _errorContext) {
775
794
  // Pass errorContext so runUnitPhase can distinguish user-initiated pause
776
795
  // from provider-error pause and avoid hard-stopping (#2762).
777
796
  resolveAgentEndCancelled(_errorContext);
778
- s.pausedSessionFile = ctx?.sessionManager?.getSessionFile() ?? null;
797
+ s.pausedSessionFile = normalizeSessionFilePath(ctx?.sessionManager?.getSessionFile() ?? null);
779
798
  // Persist paused-session metadata so resume survives /exit (#1383).
780
799
  // The fresh-start bootstrap checks for this file and restores worktree context.
781
800
  try {
@@ -829,7 +848,6 @@ export async function pauseAuto(ctx, _pi, _errorContext) {
829
848
  s.verificationRetryCount.clear();
830
849
  ctx?.ui.setStatus("gsd-auto", "paused");
831
850
  ctx?.ui.setWidget("gsd-progress", undefined);
832
- ctx?.ui.setFooter(undefined);
833
851
  if (ctx)
834
852
  initHealthWidget(ctx);
835
853
  const resumeCmd = s.stepMode ? "/gsd next" : "/gsd auto";
@@ -975,6 +993,9 @@ function buildLoopDeps() {
975
993
  },
976
994
  // Journal
977
995
  emitJournalEvent: (entry) => _emitJournalEvent(s.basePath, entry),
996
+ // Clean-root preflight gate (#2909)
997
+ preflightCleanRoot,
998
+ postflightPopStash,
978
999
  };
979
1000
  }
980
1001
  /**
@@ -1031,7 +1052,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1031
1052
  unlinkSync(pausedPath);
1032
1053
  }
1033
1054
  catch (e) {
1034
- logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
1055
+ if (e.code !== "ENOENT") {
1056
+ logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
1057
+ }
1035
1058
  }
1036
1059
  ctx.ui.notify(`Resuming paused custom workflow${meta.activeRunDir ? ` (${meta.activeRunDir})` : ""}.`, "info");
1037
1060
  }
@@ -1049,7 +1072,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1049
1072
  unlinkSync(pausedPath);
1050
1073
  }
1051
1074
  catch (err) {
1052
- logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1075
+ if (err.code !== "ENOENT") {
1076
+ logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1077
+ }
1053
1078
  }
1054
1079
  ctx.ui.notify(`Paused milestone ${meta.milestoneId} is ${!mDir ? "missing" : "already complete"}. Starting fresh.`, "info");
1055
1080
  }
@@ -1057,7 +1082,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1057
1082
  s.currentMilestoneId = meta.milestoneId;
1058
1083
  s.originalBasePath = meta.originalBasePath || base;
1059
1084
  s.stepMode = meta.stepMode ?? requestedStepMode;
1060
- s.pausedSessionFile = meta.sessionFile ?? null;
1085
+ s.pausedSessionFile = normalizeSessionFilePath(meta.sessionFile ?? null);
1061
1086
  s.pausedUnitType = meta.unitType ?? null;
1062
1087
  s.pausedUnitId = meta.unitId ?? null;
1063
1088
  s.autoStartTime = meta.autoStartTime || Date.now();
@@ -1067,7 +1092,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1067
1092
  unlinkSync(pausedPath);
1068
1093
  }
1069
1094
  catch (e) {
1070
- logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
1095
+ if (e.code !== "ENOENT") {
1096
+ logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
1097
+ }
1071
1098
  }
1072
1099
  ctx.ui.notify(`Resuming paused session for ${meta.milestoneId}${meta.worktreePath && existsSync(meta.worktreePath) ? ` (worktree)` : ""}.`, "info");
1073
1100
  }
@@ -1077,7 +1104,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1077
1104
  unlinkSync(pausedPath);
1078
1105
  }
1079
1106
  catch (e) {
1080
- logWarning("session", `stale pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
1107
+ if (e.code !== "ENOENT") {
1108
+ logWarning("session", `stale pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
1109
+ }
1081
1110
  }
1082
1111
  }
1083
1112
  }
@@ -1133,7 +1162,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1133
1162
  unlinkSync(s.pausedSessionFile);
1134
1163
  }
1135
1164
  catch (err) {
1136
- logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1165
+ if (err.code !== "ENOENT") {
1166
+ logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1167
+ }
1137
1168
  }
1138
1169
  s.pausedSessionFile = null;
1139
1170
  }
@@ -1143,6 +1174,17 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1143
1174
  s.stepMode = requestedStepMode;
1144
1175
  s.cmdCtx = ctx;
1145
1176
  s.basePath = base;
1177
+ // ── Resume worktree: if the paused session was inside a milestone worktree,
1178
+ // apply that path as the dispatch basePath immediately (#3723).
1179
+ // This ensures the dispatch loop runs from the worktree directory even when
1180
+ // enterMilestone guard conditions differ between the original and resumed
1181
+ // session (e.g. isolation mode changed, detectWorktreeName differs across
1182
+ // process restarts). We guard with existsSync so a stale or deleted
1183
+ // worktree directory safely falls back to the project root.
1184
+ const resumeWorktreePath = freshStartAssessment.pausedSession?.worktreePath;
1185
+ if (resumeWorktreePath && existsSync(resumeWorktreePath)) {
1186
+ s.basePath = resumeWorktreePath;
1187
+ }
1146
1188
  // Ensure the workflow-logger audit log is pinned to the project root
1147
1189
  // even when auto-mode is entered via a path that bypasses the
1148
1190
  // bootstrap/dynamic-tools ensureDbOpen() → setLogBasePath() chain
@@ -1161,7 +1203,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1161
1203
  });
1162
1204
  // ── Auto-worktree / branch-mode: re-enter on resume ──
1163
1205
  if (s.currentMilestoneId &&
1164
- getIsolationMode() !== "none" &&
1206
+ getIsolationMode(s.originalBasePath || s.basePath) !== "none" &&
1165
1207
  s.originalBasePath &&
1166
1208
  !isInAutoWorktree(s.basePath) &&
1167
1209
  !detectWorktreeName(s.basePath) &&
@@ -1172,7 +1214,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1172
1214
  }
1173
1215
  registerSigtermHandler(lockBase());
1174
1216
  ctx.ui.setStatus("gsd-auto", s.stepMode ? "next" : "auto");
1175
- ctx.ui.setFooter(hideFooter);
1217
+ ctx.ui.setWidget("gsd-health", undefined);
1176
1218
  ctx.ui.notify(s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "info");
1177
1219
  restoreHookState(s.basePath);
1178
1220
  // Re-sync managed resources on resume so long-lived auto sessions pick up
@@ -1194,7 +1236,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1194
1236
  await openProjectDbIfPresent(s.basePath);
1195
1237
  try {
1196
1238
  await rebuildState(s.basePath);
1197
- syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
1239
+ syncCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, await deriveState(s.basePath));
1198
1240
  }
1199
1241
  catch (e) {
1200
1242
  debugLog("resume-rebuild-state-failed", {
@@ -1224,7 +1266,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1224
1266
  }
1225
1267
  updateSessionLock(lockBase(), "resuming", s.currentMilestoneId ?? "unknown");
1226
1268
  writeLock(lockBase(), "resuming", s.currentMilestoneId ?? "unknown");
1227
- logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
1269
+ logCmuxEvent(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
1228
1270
  captureProjectRootEnv(s.originalBasePath || s.basePath);
1229
1271
  startAutoCommandPolling(s.basePath);
1230
1272
  await runAutoLoopWithUok({
@@ -1250,13 +1292,13 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1250
1292
  return;
1251
1293
  captureProjectRootEnv(s.originalBasePath || s.basePath);
1252
1294
  try {
1253
- syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
1295
+ syncCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, await deriveState(s.basePath));
1254
1296
  }
1255
1297
  catch (err) {
1256
1298
  // Best-effort only — sidebar sync must never block auto-mode startup
1257
1299
  logWarning("engine", `cmux sync failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1258
1300
  }
1259
- logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, requestedStepMode ? "Step-mode started." : "Auto-mode started.", "progress");
1301
+ logCmuxEvent(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, requestedStepMode ? "Step-mode started." : "Auto-mode started.", "progress");
1260
1302
  startAutoCommandPolling(s.basePath);
1261
1303
  // Dispatch the first unit
1262
1304
  await runAutoLoopWithUok({
@@ -1361,8 +1403,8 @@ export async function dispatchHookUnit(ctx, pi, hookName, triggerUnitType, trigg
1361
1403
  `Ensure the model is defined in models.json and has auth configured.`, "warning");
1362
1404
  }
1363
1405
  }
1364
- const sessionFile = ctx.sessionManager.getSessionFile();
1365
- writeLock(lockBase(), hookUnitType, triggerUnitId, sessionFile);
1406
+ const sessionFile = normalizeSessionFilePath(ctx.sessionManager.getSessionFile());
1407
+ writeLock(lockBase(), hookUnitType, triggerUnitId, sessionFile ?? undefined);
1366
1408
  clearUnitTimeout();
1367
1409
  const supervisor = resolveAutoSupervisorConfig();
1368
1410
  const hookHardTimeoutMs = (supervisor.hard_timeout_minutes ?? 30) * 60 * 1000;
@@ -1,5 +1,5 @@
1
1
  import { logWarning } from "../workflow-logger.js";
2
- import { checkAutoStartAfterDiscuss } from "../guided-flow.js";
2
+ import { checkAutoStartAfterDiscuss, maybeHandleReadyPhraseWithoutFiles, maybeHandleEmptyIntentTurn, resetEmptyTurnCounter, } from "../guided-flow.js";
3
3
  import { getAutoDashboardData, getAutoModeStartModel, isAutoActive, pauseAuto, setCurrentDispatchedModelId } from "../auto.js";
4
4
  import { getNextFallbackModel, resolveModelWithFallbacksForUnit } from "../preferences.js";
5
5
  import { pauseAutoForProviderError } from "../provider-error-pause.js";
@@ -53,6 +53,19 @@ export async function handleAgentEnd(pi, event, ctx) {
53
53
  clearDiscussionFlowState();
54
54
  return;
55
55
  }
56
+ // #4573 — When the LLM emits "Milestone X ready." but the required files
57
+ // are missing, `checkAutoStartAfterDiscuss` returns false silently. Surface
58
+ // that and nudge the LLM to complete the writes before the user hits the
59
+ // downstream "All milestones complete" warning loop.
60
+ if (maybeHandleReadyPhraseWithoutFiles(event))
61
+ return;
62
+ // #4573 — Empty-turn recovery: if the LLM announced intent in prose but
63
+ // emitted no tool calls, nudge it to execute. Fires only when auto-mode is
64
+ // active or a discussion autostart is pending (non-auto interactive discuss
65
+ // is user-driven). Runs before `isAutoActive` early return so pending
66
+ // discussions (where isAutoActive may be false) still get recovered.
67
+ if (maybeHandleEmptyIntentTurn(event, isAutoActive()))
68
+ return;
56
69
  if (!isAutoActive())
57
70
  return;
58
71
  if (isSessionSwitchInFlight())
@@ -286,6 +299,9 @@ export async function handleAgentEnd(pi, event, ctx) {
286
299
  // ── Success path ─────────────────────────────────────────────────────────
287
300
  try {
288
301
  resetRetryState(retryState);
302
+ // #4573 — Reset the empty-turn counter on any successful agent turn so
303
+ // transient stalls don't accumulate across independent units.
304
+ resetEmptyTurnCounter();
289
305
  resolveAgentEnd(event);
290
306
  }
291
307
  catch (err) {
@@ -19,6 +19,18 @@ function registerAlias(pi, toolDef, aliasName, canonicalName) {
19
19
  promptGuidelines: [`Alias for ${canonicalName} — prefer the canonical name.`],
20
20
  });
21
21
  }
22
+ /**
23
+ * Read a tool result's structured payload, accommodating MCP's `details` →
24
+ * `structuredContent` rename (#4472, #4477). In-process executions still
25
+ * deliver the payload on `result.details`; MCP-routed executions deliver it
26
+ * on `result.structuredContent` (post `adaptExecutorResult` transform). All
27
+ * `renderResult` callbacks in this file route through this helper so a future
28
+ * field rename only needs to be applied in one place.
29
+ */
30
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- result shape varies by tool
31
+ function readDetails(result) {
32
+ return result?.details ?? result?.structuredContent;
33
+ }
22
34
  export function registerDbTools(pi) {
23
35
  // ─── gsd_decision_save (formerly gsd_save_decision) ─────────────────────
24
36
  const decisionSaveExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
@@ -92,7 +104,7 @@ export function registerDbTools(pi) {
92
104
  return new Text(text, 0, 0);
93
105
  },
94
106
  renderResult(result, _options, theme) {
95
- const d = result.details;
107
+ const d = readDetails(result);
96
108
  if (result.isError || d?.error) {
97
109
  return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
98
110
  }
@@ -175,7 +187,7 @@ export function registerDbTools(pi) {
175
187
  return new Text(text, 0, 0);
176
188
  },
177
189
  renderResult(result, _options, theme) {
178
- const d = result.details;
190
+ const d = readDetails(result);
179
191
  if (result.isError || d?.error) {
180
192
  return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
181
193
  }
@@ -255,7 +267,7 @@ export function registerDbTools(pi) {
255
267
  return new Text(text, 0, 0);
256
268
  },
257
269
  renderResult(result, _options, theme) {
258
- const d = result.details;
270
+ const d = readDetails(result);
259
271
  if (result.isError || d?.error) {
260
272
  return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
261
273
  }
@@ -301,7 +313,7 @@ export function registerDbTools(pi) {
301
313
  return new Text(text, 0, 0);
302
314
  },
303
315
  renderResult(result, _options, theme) {
304
- const d = result.details;
316
+ const d = readDetails(result);
305
317
  if (result.isError || d?.error) {
306
318
  return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
307
319
  }
@@ -382,7 +394,7 @@ export function registerDbTools(pi) {
382
394
  return new Text(theme.fg("toolTitle", theme.bold("milestone_generate_id")), 0, 0);
383
395
  },
384
396
  renderResult(result, _options, theme) {
385
- const d = result.details;
397
+ const d = readDetails(result);
386
398
  if (result.isError || d?.error) {
387
399
  return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
388
400
  }
@@ -967,13 +979,31 @@ export function registerDbTools(pi) {
967
979
  text += theme.fg("dim", ` → ${args.verdict ?? ""}`);
968
980
  return new Text(text, 0, 0);
969
981
  },
982
+ /**
983
+ * Render the save_gate_result tool output for the TUI.
984
+ *
985
+ * Prefers structured fields, but falls back to `content[0].text` when the
986
+ * structured payload is empty. Defensive: the structural fix on this
987
+ * branch plumbs `details` through MCP via `structuredContent`, but older
988
+ * hosts, a future handler that forgets `structuredContent`, or any drop
989
+ * of non-standard return fields would otherwise render as
990
+ * "undefined: undefined". Same fallback applies to error rendering, and
991
+ * we strip a leading `Error:` from the fallback text to avoid producing
992
+ * `Error: Error: ...`.
993
+ */
970
994
  renderResult(result, _options, theme) {
971
- const d = result.details;
995
+ const d = readDetails(result);
972
996
  if (result.isError || d?.error) {
973
- return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
997
+ const rawMsg = d?.error ?? result.content?.[0]?.text ?? "unknown";
998
+ const msg = rawMsg.replace(/^\s*Error:\s*/i, "");
999
+ return new Text(theme.fg("error", `Error: ${msg}`), 0, 0);
1000
+ }
1001
+ if (!d?.gateId || !d?.verdict) {
1002
+ const text = result.content?.[0]?.text ?? "Gate result saved";
1003
+ return new Text(theme.fg("success", text), 0, 0);
974
1004
  }
975
- const color = d?.verdict === "flag" ? "warning" : "success";
976
- return new Text(theme.fg(color, `${d?.gateId}: ${d?.verdict}`), 0, 0);
1005
+ const color = d.verdict === "flag" ? "warning" : "success";
1006
+ return new Text(theme.fg(color, `${d.gateId}: ${d.verdict}`), 0, 0);
977
1007
  },
978
1008
  };
979
1009
  pi.registerTool(saveGateResultTool);