gsd-pi 2.73.1 → 2.74.0-dev.b741afb

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 (378) hide show
  1. package/dist/cli-web-branch.d.ts +4 -3
  2. package/dist/cli-web-branch.js +10 -7
  3. package/dist/cli.js +184 -206
  4. package/dist/headless-query.js +4 -1
  5. package/dist/help-text.js +23 -0
  6. package/dist/logo.d.ts +1 -1
  7. package/dist/logo.js +1 -1
  8. package/dist/onboarding.js +59 -53
  9. package/dist/resource-loader.js +2 -2
  10. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +68 -4
  11. package/dist/resources/extensions/gsd/auto/detect-stuck.js +11 -4
  12. package/dist/resources/extensions/gsd/auto/phases.js +60 -10
  13. package/dist/resources/extensions/gsd/auto-dispatch.js +11 -3
  14. package/dist/resources/extensions/gsd/auto-model-selection.js +54 -11
  15. package/dist/resources/extensions/gsd/auto-post-unit.js +93 -57
  16. package/dist/resources/extensions/gsd/auto-prompts.js +12 -0
  17. package/dist/resources/extensions/gsd/auto-start.js +23 -6
  18. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +13 -0
  19. package/dist/resources/extensions/gsd/auto-verification.js +88 -3
  20. package/dist/resources/extensions/gsd/auto.js +37 -10
  21. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +21 -8
  22. package/dist/resources/extensions/gsd/commands/catalog.js +26 -1
  23. package/dist/resources/extensions/gsd/commands/handlers/ops.js +20 -0
  24. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +68 -9
  25. package/dist/resources/extensions/gsd/commands-add-tests.js +111 -0
  26. package/dist/resources/extensions/gsd/commands-backlog.js +140 -0
  27. package/dist/resources/extensions/gsd/commands-do.js +79 -0
  28. package/dist/resources/extensions/gsd/commands-handlers.js +8 -2
  29. package/dist/resources/extensions/gsd/commands-maintenance.js +6 -6
  30. package/dist/resources/extensions/gsd/commands-pr-branch.js +180 -0
  31. package/dist/resources/extensions/gsd/commands-session-report.js +82 -0
  32. package/dist/resources/extensions/gsd/commands-ship.js +187 -0
  33. package/dist/resources/extensions/gsd/db-writer.js +3 -5
  34. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  35. package/dist/resources/extensions/gsd/graph-context.js +66 -0
  36. package/dist/resources/extensions/gsd/gsd-db.js +321 -0
  37. package/dist/resources/extensions/gsd/index.js +15 -2
  38. package/dist/resources/extensions/gsd/md-importer.js +3 -4
  39. package/dist/resources/extensions/gsd/memory-store.js +19 -51
  40. package/dist/resources/extensions/gsd/milestone-validation-gates.js +13 -12
  41. package/dist/resources/extensions/gsd/native-git-bridge.js +7 -4
  42. package/dist/resources/extensions/gsd/notification-widget.js +2 -2
  43. package/dist/resources/extensions/gsd/preferences-models.js +43 -0
  44. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  45. package/dist/resources/extensions/gsd/preferences-validation.js +22 -0
  46. package/dist/resources/extensions/gsd/prompts/add-tests.md +35 -0
  47. package/dist/resources/extensions/gsd/state.js +66 -15
  48. package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -0
  49. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +3 -14
  50. package/dist/resources/extensions/gsd/triage-resolution.js +2 -5
  51. package/dist/resources/extensions/gsd/workflow-manifest.js +8 -69
  52. package/dist/resources/extensions/gsd/workflow-migration.js +21 -22
  53. package/dist/resources/extensions/gsd/workflow-projections.js +4 -1
  54. package/dist/resources/extensions/gsd/workflow-reconcile.js +14 -11
  55. package/dist/tsconfig.extensions.tsbuildinfo +1 -0
  56. package/dist/update-check.d.ts +1 -0
  57. package/dist/update-check.js +13 -5
  58. package/dist/update-cmd.js +4 -3
  59. package/dist/web/standalone/.next/BUILD_ID +1 -1
  60. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  61. package/dist/web/standalone/.next/build-manifest.json +3 -3
  62. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  63. package/dist/web/standalone/.next/required-server-files.json +3 -3
  64. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  65. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  75. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +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 +3 -3
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  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 +3 -3
  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/api/boot/route.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  91. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  103. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  123. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  133. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  139. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  153. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  155. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  157. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  159. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/index.html +1 -1
  169. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  170. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  171. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  172. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  173. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  174. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  175. package/dist/web/standalone/.next/server/app/page.js +2 -2
  176. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  178. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  179. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  180. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  182. package/dist/web/standalone/.next/server/middleware.js +2 -2
  183. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  185. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  186. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  187. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  188. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  189. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  190. package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
  191. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  192. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  193. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  194. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  195. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  196. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  197. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  198. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  199. package/dist/web/standalone/server.js +1 -1
  200. package/package.json +3 -3
  201. package/packages/daemon/package.json +2 -2
  202. package/packages/mcp-server/dist/index.d.ts +3 -0
  203. package/packages/mcp-server/dist/index.d.ts.map +1 -1
  204. package/packages/mcp-server/dist/index.js +3 -0
  205. package/packages/mcp-server/dist/index.js.map +1 -1
  206. package/packages/mcp-server/dist/readers/graph.d.ts +87 -0
  207. package/packages/mcp-server/dist/readers/graph.d.ts.map +1 -0
  208. package/packages/mcp-server/dist/readers/graph.js +548 -0
  209. package/packages/mcp-server/dist/readers/graph.js.map +1 -0
  210. package/packages/mcp-server/dist/readers/index.d.ts +2 -0
  211. package/packages/mcp-server/dist/readers/index.d.ts.map +1 -1
  212. package/packages/mcp-server/dist/readers/index.js +1 -0
  213. package/packages/mcp-server/dist/readers/index.js.map +1 -1
  214. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  215. package/packages/mcp-server/dist/server.js +65 -0
  216. package/packages/mcp-server/dist/server.js.map +1 -1
  217. package/packages/mcp-server/package.json +2 -2
  218. package/packages/mcp-server/src/index.ts +15 -0
  219. package/packages/mcp-server/src/readers/graph.test.ts +426 -0
  220. package/packages/mcp-server/src/readers/graph.ts +708 -0
  221. package/packages/mcp-server/src/readers/index.ts +12 -0
  222. package/packages/mcp-server/src/server.ts +83 -0
  223. package/packages/mcp-server/tsconfig.json +1 -0
  224. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -0
  225. package/packages/native/package.json +2 -2
  226. package/packages/native/tsconfig.tsbuildinfo +1 -0
  227. package/packages/pi-agent-core/package.json +1 -1
  228. package/packages/pi-agent-core/tsconfig.json +1 -0
  229. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -0
  230. package/packages/pi-ai/dist/index.d.ts +1 -0
  231. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  232. package/packages/pi-ai/dist/index.js +1 -0
  233. package/packages/pi-ai/dist/index.js.map +1 -1
  234. package/packages/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  235. package/packages/pi-ai/dist/utils/overflow.js +12 -0
  236. package/packages/pi-ai/dist/utils/overflow.js.map +1 -1
  237. package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts +2 -0
  238. package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts.map +1 -0
  239. package/packages/pi-ai/dist/utils/tests/overflow.test.js +50 -0
  240. package/packages/pi-ai/dist/utils/tests/overflow.test.js.map +1 -0
  241. package/packages/pi-ai/package.json +1 -1
  242. package/packages/pi-ai/src/index.ts +4 -0
  243. package/packages/pi-ai/src/utils/overflow.ts +14 -1
  244. package/packages/pi-ai/src/utils/tests/overflow.test.ts +58 -0
  245. package/packages/pi-ai/tsconfig.json +1 -0
  246. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -0
  247. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +313 -8
  248. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  249. package/packages/pi-coding-agent/dist/core/compaction/utils.js +5 -5
  250. package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
  251. package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts +2 -0
  252. package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts.map +1 -0
  253. package/packages/pi-coding-agent/dist/core/compaction-utils.test.js +45 -0
  254. package/packages/pi-coding-agent/dist/core/compaction-utils.test.js.map +1 -0
  255. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +12 -2
  256. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  257. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +61 -28
  258. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  259. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +2 -1
  260. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  261. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +9 -3
  262. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  263. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts +2 -0
  264. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts.map +1 -0
  265. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +52 -0
  266. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -0
  267. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  268. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +94 -16
  269. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  270. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  271. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +11 -3
  272. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  273. package/packages/pi-coding-agent/package.json +1 -1
  274. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +355 -8
  275. package/packages/pi-coding-agent/src/core/compaction/utils.ts +5 -5
  276. package/packages/pi-coding-agent/src/core/compaction-utils.test.ts +50 -0
  277. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +74 -32
  278. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +73 -0
  279. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +9 -3
  280. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +113 -21
  281. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +11 -3
  282. package/packages/pi-coding-agent/tsconfig.json +1 -0
  283. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -0
  284. package/packages/pi-tui/dist/__tests__/tui.test.js +60 -1
  285. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  286. package/packages/pi-tui/dist/tui.d.ts +8 -0
  287. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  288. package/packages/pi-tui/dist/tui.js +32 -3
  289. package/packages/pi-tui/dist/tui.js.map +1 -1
  290. package/packages/pi-tui/package.json +1 -1
  291. package/packages/pi-tui/src/__tests__/tui.test.ts +76 -1
  292. package/packages/pi-tui/src/tui.ts +31 -3
  293. package/packages/pi-tui/tsconfig.json +1 -0
  294. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -0
  295. package/packages/rpc-client/package.json +1 -1
  296. package/packages/rpc-client/tsconfig.json +1 -0
  297. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -0
  298. package/pkg/package.json +1 -1
  299. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +107 -5
  300. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +111 -2
  301. package/src/resources/extensions/gsd/auto/detect-stuck.ts +12 -4
  302. package/src/resources/extensions/gsd/auto/loop-deps.ts +6 -0
  303. package/src/resources/extensions/gsd/auto/phases.ts +90 -10
  304. package/src/resources/extensions/gsd/auto-dispatch.ts +10 -4
  305. package/src/resources/extensions/gsd/auto-model-selection.ts +85 -11
  306. package/src/resources/extensions/gsd/auto-post-unit.ts +107 -58
  307. package/src/resources/extensions/gsd/auto-prompts.ts +13 -0
  308. package/src/resources/extensions/gsd/auto-start.ts +30 -6
  309. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +17 -0
  310. package/src/resources/extensions/gsd/auto-verification.ts +98 -3
  311. package/src/resources/extensions/gsd/auto.ts +38 -14
  312. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -8
  313. package/src/resources/extensions/gsd/commands/catalog.ts +26 -1
  314. package/src/resources/extensions/gsd/commands/handlers/ops.ts +20 -0
  315. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +74 -9
  316. package/src/resources/extensions/gsd/commands-add-tests.ts +137 -0
  317. package/src/resources/extensions/gsd/commands-backlog.ts +182 -0
  318. package/src/resources/extensions/gsd/commands-do.ts +109 -0
  319. package/src/resources/extensions/gsd/commands-handlers.ts +8 -2
  320. package/src/resources/extensions/gsd/commands-maintenance.ts +6 -6
  321. package/src/resources/extensions/gsd/commands-pr-branch.ts +234 -0
  322. package/src/resources/extensions/gsd/commands-session-report.ts +101 -0
  323. package/src/resources/extensions/gsd/commands-ship.ts +219 -0
  324. package/src/resources/extensions/gsd/db-writer.ts +3 -5
  325. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  326. package/src/resources/extensions/gsd/graph-context.ts +85 -0
  327. package/src/resources/extensions/gsd/gsd-db.ts +467 -0
  328. package/src/resources/extensions/gsd/index.ts +18 -2
  329. package/src/resources/extensions/gsd/md-importer.ts +3 -5
  330. package/src/resources/extensions/gsd/memory-store.ts +31 -62
  331. package/src/resources/extensions/gsd/milestone-validation-gates.ts +13 -14
  332. package/src/resources/extensions/gsd/native-git-bridge.ts +11 -12
  333. package/src/resources/extensions/gsd/notification-widget.ts +2 -2
  334. package/src/resources/extensions/gsd/preferences-models.ts +41 -0
  335. package/src/resources/extensions/gsd/preferences-types.ts +12 -0
  336. package/src/resources/extensions/gsd/preferences-validation.ts +23 -0
  337. package/src/resources/extensions/gsd/prompts/add-tests.md +35 -0
  338. package/src/resources/extensions/gsd/state.ts +80 -17
  339. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +2 -2
  340. package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +53 -0
  341. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +51 -2
  342. package/src/resources/extensions/gsd/tests/commands-backlog.test.ts +158 -0
  343. package/src/resources/extensions/gsd/tests/commands-do.test.ts +127 -0
  344. package/src/resources/extensions/gsd/tests/commands-pr-branch.test.ts +68 -0
  345. package/src/resources/extensions/gsd/tests/commands-session-report.test.ts +82 -0
  346. package/src/resources/extensions/gsd/tests/commands-ship.test.ts +71 -0
  347. package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +14 -0
  348. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +142 -0
  349. package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +42 -0
  350. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -2
  351. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +3 -2
  352. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +68 -8
  353. package/src/resources/extensions/gsd/tests/derive-state.test.ts +3 -3
  354. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +154 -0
  355. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +137 -1
  356. package/src/resources/extensions/gsd/tests/graph-context.test.ts +337 -0
  357. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  358. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +68 -1
  359. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +91 -2
  360. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +140 -0
  361. package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -0
  362. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +180 -0
  363. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +5 -7
  364. package/src/resources/extensions/gsd/tests/token-profile.test.ts +1 -1
  365. package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +179 -0
  366. package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +223 -0
  367. package/src/resources/extensions/gsd/tools/complete-slice.ts +19 -0
  368. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +3 -11
  369. package/src/resources/extensions/gsd/triage-resolution.ts +2 -7
  370. package/src/resources/extensions/gsd/workflow-manifest.ts +9 -104
  371. package/src/resources/extensions/gsd/workflow-migration.ts +21 -29
  372. package/src/resources/extensions/gsd/workflow-projections.ts +8 -1
  373. package/src/resources/extensions/gsd/workflow-reconcile.ts +15 -15
  374. package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
  375. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  376. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  377. /package/dist/web/standalone/.next/static/{Qr27MOHx0lxRGnJvlhxxu → XnHY5eXUsTCFmNodWHetD}/_buildManifest.js +0 -0
  378. /package/dist/web/standalone/.next/static/{Qr27MOHx0lxRGnJvlhxxu → XnHY5eXUsTCFmNodWHetD}/_ssgManifest.js +0 -0
@@ -104,6 +104,7 @@ import {
104
104
  updateSliceProgressCache,
105
105
  unitVerb,
106
106
  hideFooter,
107
+ describeNextUnit,
107
108
  } from "./auto-dashboard.js";
108
109
  import { existsSync, unlinkSync } from "node:fs";
109
110
  import { join } from "node:path";
@@ -233,6 +234,18 @@ export function detectRogueFileWrites(
233
234
  return rogues;
234
235
  }
235
236
 
237
+ export const STEP_COMPLETE_FALLBACK_MESSAGE =
238
+ "Step complete. Run /clear, then /gsd to continue (or /gsd auto to run continuously).";
239
+
240
+ export function buildStepCompleteMessage(nextState: import("./types.js").GSDState): string {
241
+ if (nextState.phase === "complete") {
242
+ return "Step complete — milestone finished. Run /gsd status to review, or start the next milestone.";
243
+ }
244
+ const next = describeNextUnit(nextState);
245
+ return `Step complete. Next: ${next.label}\n`
246
+ + `Run /clear, then /gsd to continue (or /gsd auto to run continuously).`;
247
+ }
248
+
236
249
  export interface PreVerificationOpts {
237
250
  skipSettleDelay?: boolean;
238
251
  skipWorktreeSync?: boolean;
@@ -249,6 +262,65 @@ export interface PostUnitContext {
249
262
  updateProgressWidget: (ctx: ExtensionContext, unitType: string, unitId: string, state: import("./types.js").GSDState) => void;
250
263
  }
251
264
 
265
+ export async function autoCommitUnit(
266
+ basePath: string,
267
+ unitType: string,
268
+ unitId: string,
269
+ ctx?: ExtensionContext,
270
+ ): Promise<string | null> {
271
+ try {
272
+ let taskContext: TaskCommitContext | undefined;
273
+
274
+ if (unitType === "execute-task") {
275
+ const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
276
+ if (mid && sid && tid) {
277
+ const summaryPath = resolveTaskFile(basePath, mid, sid, tid, "SUMMARY");
278
+ if (summaryPath) {
279
+ try {
280
+ const summaryContent = await loadFile(summaryPath);
281
+ if (summaryContent) {
282
+ const summary = parseSummary(summaryContent);
283
+ let ghIssueNumber: number | undefined;
284
+ try {
285
+ const { getTaskIssueNumberForCommit } = await import("../github-sync/sync.js");
286
+ ghIssueNumber = getTaskIssueNumberForCommit(basePath, mid, sid, tid) ?? undefined;
287
+ } catch (err) {
288
+ logWarning("engine", `GitHub issue lookup failed: ${err instanceof Error ? err.message : String(err)}`);
289
+ }
290
+
291
+ taskContext = {
292
+ taskId: `${sid}/${tid}`,
293
+ taskTitle: summary.title?.replace(/^T\d+:\s*/, "") || tid,
294
+ oneLiner: summary.oneLiner || undefined,
295
+ keyFiles: summary.frontmatter.key_files?.filter(f => !f.includes("{{")) || undefined,
296
+ issueNumber: ghIssueNumber,
297
+ };
298
+ }
299
+ } catch (e) {
300
+ debugLog("postUnit", { phase: "task-summary-parse", error: String(e) });
301
+ }
302
+ }
303
+ }
304
+ }
305
+
306
+ _resetHasChangesCache();
307
+
308
+ if (LIFECYCLE_ONLY_UNITS.has(unitType)) {
309
+ return null;
310
+ }
311
+
312
+ const commitMsg = autoCommitCurrentBranch(basePath, unitType, unitId, taskContext);
313
+ if (commitMsg) {
314
+ ctx?.ui.notify(`Committed: ${commitMsg.split("\n")[0]}`, "info");
315
+ }
316
+ return commitMsg;
317
+ } catch (e) {
318
+ debugLog("postUnit", { phase: "auto-commit", error: String(e) });
319
+ ctx?.ui.notify(`Auto-commit failed: ${String(e).split("\n")[0]}`, "warning");
320
+ return null;
321
+ }
322
+ }
323
+
252
324
  /**
253
325
  * Pre-verification processing: parallel worker signal check, cache invalidation,
254
326
  * auto-commit, doctor run, state rebuild, worktree sync, artifact verification.
@@ -288,63 +360,7 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
288
360
  // Auto-commit
289
361
  if (s.currentUnit) {
290
362
  const unit = s.currentUnit;
291
- try {
292
- let taskContext: TaskCommitContext | undefined;
293
-
294
- if (s.currentUnit.type === "execute-task") {
295
- const { milestone: mid, slice: sid, task: tid } = parseUnitId(s.currentUnit.id);
296
- if (mid && sid && tid) {
297
- const summaryPath = resolveTaskFile(s.basePath, mid, sid, tid, "SUMMARY");
298
- if (summaryPath) {
299
- try {
300
- const summaryContent = await loadFile(summaryPath);
301
- if (summaryContent) {
302
- const summary = parseSummary(summaryContent);
303
- // Look up GitHub issue number for commit linking
304
- let ghIssueNumber: number | undefined;
305
- try {
306
- const { getTaskIssueNumberForCommit } = await import("../github-sync/sync.js");
307
- ghIssueNumber = getTaskIssueNumberForCommit(s.basePath, mid, sid, tid) ?? undefined;
308
- } catch (err) {
309
- // GitHub sync not available — skip
310
- logWarning("engine", `GitHub issue lookup failed: ${err instanceof Error ? err.message : String(err)}`);
311
- }
312
-
313
- taskContext = {
314
- taskId: `${sid}/${tid}`,
315
- taskTitle: summary.title?.replace(/^T\d+:\s*/, "") || tid,
316
- oneLiner: summary.oneLiner || undefined,
317
- keyFiles: summary.frontmatter.key_files?.filter(f => !f.includes("{{")) || undefined,
318
- issueNumber: ghIssueNumber,
319
- };
320
- }
321
- } catch (e) {
322
- debugLog("postUnit", { phase: "task-summary-parse", error: String(e) });
323
- }
324
- }
325
- }
326
- }
327
-
328
- // Invalidate the nativeHasChanges cache before auto-commit (#1853).
329
- // The cache has a 10-second TTL and is keyed by basePath. A stale
330
- // `false` result causes autoCommit to skip staging entirely, leaving
331
- // code files only in the working tree where they are destroyed by
332
- // `git worktree remove --force` during teardown.
333
- _resetHasChangesCache();
334
-
335
- // Skip auto-commit for lifecycle-only units (#2553) — they only touch
336
- // `.gsd/` internal state files. Those files are picked up by the next
337
- // actual task commit via smartStage().
338
- if (!LIFECYCLE_ONLY_UNITS.has(s.currentUnit.type)) {
339
- const commitMsg = autoCommitCurrentBranch(s.basePath, s.currentUnit.type, s.currentUnit.id, taskContext);
340
- if (commitMsg) {
341
- ctx.ui.notify(`Committed: ${commitMsg.split("\n")[0]}`, "info");
342
- }
343
- }
344
- } catch (e) {
345
- debugLog("postUnit", { phase: "auto-commit", error: String(e) });
346
- ctx.ui.notify(`Auto-commit failed: ${String(e).split("\n")[0]}`, "warning");
347
- }
363
+ await autoCommitUnit(s.basePath, unit.type, unit.id, ctx);
348
364
 
349
365
  // GitHub sync (non-blocking, opt-in)
350
366
  await runSafely("postUnit", "github-sync", async () => {
@@ -619,6 +635,30 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
619
635
  s.verificationRetryCount.set(retryKey, attempt);
620
636
 
621
637
  if (attempt > MAX_VERIFICATION_RETRIES) {
638
+ // #4175: For complete-milestone, a blocker placeholder is harmful —
639
+ // the stub SUMMARY has no recovery value (milestone is terminal),
640
+ // it does not update DB status (so deriveState never advances),
641
+ // and it fools stopAuto's presence check into merging a milestone
642
+ // that was never legitimately completed. Pause auto-mode with a
643
+ // clear single failure signal and preserve the worktree branch.
644
+ if (s.currentUnit.type === "complete-milestone") {
645
+ debugLog("postUnit", {
646
+ phase: "artifact-verify-pause-complete-milestone",
647
+ unitType: s.currentUnit.type,
648
+ unitId: s.currentUnit.id,
649
+ attempt,
650
+ maxRetries: MAX_VERIFICATION_RETRIES,
651
+ });
652
+ s.verificationRetryCount.delete(retryKey);
653
+ s.pendingVerificationRetry = null;
654
+ ctx.ui.notify(
655
+ `Milestone ${s.currentUnit.id} verification failed after ${MAX_VERIFICATION_RETRIES} retries — worktree branch preserved. Re-run /gsd auto once blockers are resolved.`,
656
+ "error",
657
+ );
658
+ await pauseAuto(ctx, pi);
659
+ return "dispatched";
660
+ }
661
+
622
662
  // Retries exhausted — write a blocker placeholder so the pipeline
623
663
  // can advance past this stuck unit (#2653).
624
664
  debugLog("postUnit", {
@@ -1025,8 +1065,17 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
1025
1065
  }
1026
1066
  }
1027
1067
 
1028
- // Step mode → show wizard instead of dispatch
1068
+ // Step mode → show wizard instead of dispatch.
1069
+ // Without this notify(), /gsd in step mode finishes a unit and silently
1070
+ // exits the loop, leaving the user with no hint to /clear and /gsd again.
1029
1071
  if (s.stepMode) {
1072
+ try {
1073
+ const nextState = await deriveState(s.basePath);
1074
+ ctx.ui.notify(buildStepCompleteMessage(nextState), "info");
1075
+ } catch (e) {
1076
+ debugLog("postUnit", { phase: "step-wizard-notify", error: String(e) });
1077
+ ctx.ui.notify(STEP_COMPLETE_FALLBACK_MESSAGE, "info");
1078
+ }
1030
1079
  return "step-wizard";
1031
1080
  }
1032
1081
 
@@ -34,6 +34,7 @@ import {
34
34
  import { formatDecisionsCompact, formatRequirementsCompact } from "./structured-data-formatter.js";
35
35
  import { readPhaseAnchor, formatAnchorForPrompt } from "./phase-anchor.js";
36
36
  import { logWarning } from "./workflow-logger.js";
37
+ import { inlineGraphSubgraph } from "./graph-context.js";
37
38
 
38
39
  // ─── Preamble Cap ─────────────────────────────────────────────────────────────
39
40
 
@@ -1175,6 +1176,10 @@ export async function buildResearchSlicePrompt(
1175
1176
  const knowledgeInlineRS = await inlineKnowledgeScoped(base, keywords);
1176
1177
  if (knowledgeInlineRS) inlined.push(knowledgeInlineRS);
1177
1178
 
1179
+ // Knowledge graph: subgraph for this slice (graceful — skipped if no graph.json)
1180
+ const graphBlockRS = await inlineGraphSubgraph(base, `${sid} ${sTitle}`, { budget: 3000 });
1181
+ if (graphBlockRS) inlined.push(graphBlockRS);
1182
+
1178
1183
  inlined.push(inlineTemplate("research", "Research"));
1179
1184
 
1180
1185
  const depContent = await inlineDependencySummaries(mid, sid, base);
@@ -1250,6 +1255,10 @@ export async function buildPlanSlicePrompt(
1250
1255
  const knowledgeInlinePS = await inlineKnowledgeScoped(base, keywordsPS);
1251
1256
  if (knowledgeInlinePS) inlined.push(knowledgeInlinePS);
1252
1257
 
1258
+ // Knowledge graph: subgraph for this slice (graceful — skipped if no graph.json)
1259
+ const graphBlockPS = await inlineGraphSubgraph(base, `${sid} ${sTitle}`, { budget: 3000 });
1260
+ if (graphBlockPS) inlined.push(graphBlockPS);
1261
+
1253
1262
  inlined.push(inlineTemplate("plan", "Slice Plan"));
1254
1263
  if (inlineLevel === "full") {
1255
1264
  inlined.push(inlineTemplate("task-plan", "Task Plan"));
@@ -1366,12 +1375,16 @@ export async function buildExecuteTaskPrompt(
1366
1375
  // Only include if it has content (not a "not found" result)
1367
1376
  const knowledgeContent = knowledgeInlineET && !knowledgeInlineET.includes("not found") ? knowledgeInlineET : null;
1368
1377
 
1378
+ // Knowledge graph: tight subgraph for this task (graceful — skipped if no graph.json)
1379
+ const graphBlockET = await inlineGraphSubgraph(base, `${tid} ${tTitle}`, { budget: 2000 });
1380
+
1369
1381
  const inlinedTemplates = inlineLevel === "minimal"
1370
1382
  ? inlineTemplate("task-summary", "Task Summary")
1371
1383
  : [
1372
1384
  inlineTemplate("task-summary", "Task Summary"),
1373
1385
  inlineTemplate("decisions", "Decisions"),
1374
1386
  ...(knowledgeContent ? [knowledgeContent] : []),
1387
+ ...(graphBlockET ? [graphBlockET] : []),
1375
1388
  ].join("\n\n---\n\n");
1376
1389
 
1377
1390
  const taskSummaryPath = join(base, `${relSlicePath(base, mid, sid)}/tasks/${tid}-SUMMARY.md`);
@@ -83,7 +83,11 @@ import { join } from "node:path";
83
83
  import { sep as pathSep } from "node:path";
84
84
 
85
85
  import { resolveProjectRootDbPath } from "./bootstrap/dynamic-tools.js";
86
- import { resolveDefaultSessionModel, resolveDynamicRoutingConfig } from "./preferences-models.js";
86
+ import {
87
+ isCustomProvider,
88
+ resolveDefaultSessionModel,
89
+ resolveDynamicRoutingConfig,
90
+ } from "./preferences-models.js";
87
91
  import type { WorktreeResolver } from "./worktree-resolver.js";
88
92
  import { getSessionModelOverride } from "./session-model-override.js";
89
93
 
@@ -274,8 +278,18 @@ export async function bootstrapAutoSession(
274
278
  //
275
279
  // This preserves #3517 defaults while honoring explicit runtime model
276
280
  // selection for subsequent /gsd runs in the same session.
281
+ //
282
+ // Exception (#4122): when the session provider is a custom provider declared
283
+ // in ~/.gsd/agent/models.json (Ollama, vLLM, OpenAI-compatible proxy, etc.),
284
+ // PREFERENCES.md is skipped entirely. PREFERENCES.md cannot reference custom
285
+ // providers, so honoring it would silently reroute auto-mode to a built-in
286
+ // provider the user is not logged into and surface as "Not logged in · Please
287
+ // run /login" before pausing and resetting to claude-code/claude-sonnet-4-6.
277
288
  const manualSessionOverride = getSessionModelOverride(ctx.sessionManager.getSessionId());
278
- const preferredModel = resolveDefaultSessionModel(ctx.model?.provider);
289
+ const sessionProviderIsCustom = isCustomProvider(ctx.model?.provider);
290
+ const preferredModel = sessionProviderIsCustom
291
+ ? null
292
+ : resolveDefaultSessionModel(ctx.model?.provider);
279
293
  // Validate the preferred model against the live registry + provider auth so
280
294
  // an unconfigured PREFERENCES.md entry (no API key / OAuth) can't become the
281
295
  // start-model snapshot. Without this, every subsequent unit would try to
@@ -792,6 +806,9 @@ export async function bootstrapAutoSession(
792
806
 
793
807
  ctx.ui.setStatus("gsd-auto", s.stepMode ? "next" : "auto");
794
808
  ctx.ui.setFooter(hideFooter);
809
+ // Hide gsd-health during AUTO — gsd-progress is the single source of truth
810
+ // for last-commit / cost / health signal while auto is running.
811
+ ctx.ui.setWidget("gsd-health", undefined);
795
812
  const modeLabel = s.stepMode ? "Step-mode" : "Auto-mode";
796
813
  const pendingCount = (state.registry ?? []).filter(
797
814
  (m) => m.status !== "complete" && m.status !== "parked",
@@ -811,12 +828,19 @@ export async function bootstrapAutoSession(
811
828
  ? `${s.autoModeStartModel.provider}/${s.autoModeStartModel.id}`
812
829
  : ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "default";
813
830
 
814
- // Flat-rate providers (e.g. GitHub Copilot, claude-code) suppress routing
815
- // at dispatch time (#3453) reflect that in the banner.
816
- const { isFlatRateProvider } = await import("./auto-model-selection.js");
831
+ // Flat-rate providers (e.g. GitHub Copilot, claude-code, user-declared
832
+ // subscription proxies, externalCli CLIs) suppress routing at dispatch
833
+ // time (#3453) reflect that in the banner. Thread the same
834
+ // FlatRateContext used by selectAndApplyModel so user-declared
835
+ // flat-rate providers and externalCli auto-detection are respected.
836
+ const { isFlatRateProvider, buildFlatRateContext } = await import("./auto-model-selection.js");
837
+ const bannerPrefs = loadEffectiveGSDPreferences()?.preferences;
817
838
  const effectiveProvider = s.autoModeStartModel?.provider ?? ctx.model?.provider;
818
839
  const effectivelyEnabled = routingConfig.enabled
819
- && !(effectiveProvider && isFlatRateProvider(effectiveProvider));
840
+ && !(effectiveProvider && isFlatRateProvider(
841
+ effectiveProvider,
842
+ buildFlatRateContext(effectiveProvider, ctx, bannerPrefs),
843
+ ));
820
844
 
821
845
  // The actual ceiling may come from tier_models.heavy, not the start model.
822
846
  const effectiveCeiling = (routingConfig.enabled && routingConfig.tier_models?.heavy)
@@ -230,6 +230,23 @@ export async function recoverTimedOutUnit(
230
230
  return "recovered";
231
231
  }
232
232
 
233
+ // #4175: For complete-milestone, never write a blocker placeholder — a stub
234
+ // SUMMARY has no recovery value (milestone is terminal), it does not update
235
+ // DB status, and downstream merge paths can treat the stub as a legitimate
236
+ // completion signal. Pause instead so the worktree branch is preserved.
237
+ if (unitType === "complete-milestone") {
238
+ writeUnitRuntimeRecord(basePath, unitType, unitId, currentUnitStartedAt, {
239
+ phase: "paused",
240
+ recoveryAttempts: recoveryAttempts + 1,
241
+ lastRecoveryReason: reason,
242
+ });
243
+ ctx.ui.notify(
244
+ `Milestone ${unitId} ${reason}-recovery exhausted ${maxRecoveryAttempts} attempt(s) — worktree branch preserved. Re-run /gsd auto once blockers are resolved.`,
245
+ "error",
246
+ );
247
+ return "paused";
248
+ }
249
+
233
250
  // Retries exhausted — write a blocker placeholder and advance the pipeline
234
251
  // instead of silently stalling.
235
252
  const placeholder = writeBlockerPlaceholder(
@@ -12,10 +12,15 @@
12
12
 
13
13
  import type { ExtensionContext, ExtensionAPI } from "@gsd/pi-coding-agent";
14
14
  import { mkdirSync, writeFileSync } from "node:fs";
15
- import { resolveSliceFile, resolveSlicePath } from "./paths.js";
15
+ import { resolveSliceFile, resolveSlicePath, resolveMilestoneFile } from "./paths.js";
16
16
  import { parseUnitId } from "./unit-id.js";
17
- import { isDbAvailable, getTask, getSliceTasks, type TaskRow } from "./gsd-db.js";
17
+ import { isDbAvailable, getTask, getSliceTasks, getMilestoneSlices, type TaskRow } from "./gsd-db.js";
18
18
  import { loadEffectiveGSDPreferences } from "./preferences.js";
19
+ import { extractVerdict } from "./verdict-parser.js";
20
+ import { isClosedStatus } from "./status-guards.js";
21
+ import { loadFile } from "./files.js";
22
+ import { parseRoadmap } from "./parsers-legacy.js";
23
+ import { isMilestoneComplete } from "./state.js";
19
24
  import {
20
25
  runVerificationGate,
21
26
  formatFailureContext,
@@ -43,6 +48,88 @@ function isInfraVerificationFailure(stderr: string): boolean {
43
48
  );
44
49
  }
45
50
 
51
+ /**
52
+ * Post-unit guard for `validate-milestone` units (#4094).
53
+ *
54
+ * When validate-milestone writes verdict=needs-remediation, the agent is
55
+ * expected to also call gsd_reassess_roadmap in the same turn to add
56
+ * remediation slices. If they don't, the state machine re-derives
57
+ * `phase: validating-milestone` indefinitely (all slices still complete +
58
+ * verdict still needs-remediation), wasting ~3 dispatches before the stuck
59
+ * detector fires.
60
+ *
61
+ * This guard fires immediately on the first occurrence: if VALIDATION.md
62
+ * verdict is needs-remediation and no incomplete slices exist for the
63
+ * milestone, pause the auto-loop with a clear blocker.
64
+ */
65
+ async function runValidateMilestonePostCheck(
66
+ vctx: VerificationContext,
67
+ pauseAuto: (ctx?: ExtensionContext, pi?: ExtensionAPI) => Promise<void>,
68
+ ): Promise<VerificationResult> {
69
+ const { s, ctx, pi } = vctx;
70
+ if (!s.currentUnit) return "continue";
71
+
72
+ const { milestone: mid } = parseUnitId(s.currentUnit.id);
73
+ if (!mid) return "continue";
74
+
75
+ const validationFile = resolveMilestoneFile(s.basePath, mid, "VALIDATION");
76
+ if (!validationFile) return "continue";
77
+
78
+ const validationContent = await loadFile(validationFile);
79
+ if (!validationContent) return "continue";
80
+
81
+ const verdict = extractVerdict(validationContent);
82
+ if (verdict !== "needs-remediation") return "continue";
83
+
84
+ const incompleteSliceCount = await countIncompleteSlices(s.basePath, mid);
85
+
86
+ // If any non-closed slices exist, the agent successfully queued remediation
87
+ // work — proceed normally. The state machine will execute those slices and
88
+ // re-validate per the #3596/#3670 fix.
89
+ if (incompleteSliceCount > 0) return "continue";
90
+
91
+ ctx.ui.notify(
92
+ `Milestone ${mid} validation returned verdict=needs-remediation but no remediation slices were added. Pausing for human review.`,
93
+ "error",
94
+ );
95
+ process.stderr.write(
96
+ `validate-milestone: pausing — verdict=needs-remediation with no incomplete slices for ${mid}. ` +
97
+ `The agent must call gsd_reassess_roadmap to add remediation slices before re-validation.\n`,
98
+ );
99
+ await pauseAuto(ctx, pi);
100
+ return "pause";
101
+ }
102
+
103
+ /**
104
+ * Count slices for a milestone that are not in a closed status.
105
+ * DB-backed projects are authoritative (#4094 peer review); falls back to
106
+ * roadmap parsing only when the DB is unavailable.
107
+ */
108
+ async function countIncompleteSlices(basePath: string, milestoneId: string): Promise<number> {
109
+ if (isDbAvailable()) {
110
+ const slices = getMilestoneSlices(milestoneId);
111
+ if (slices.length === 0) {
112
+ // No DB rows — treat as "unknown", do not pause.
113
+ return 1;
114
+ }
115
+ return slices.filter((slice) => !isClosedStatus(slice.status)).length;
116
+ }
117
+
118
+ // Filesystem fallback: parse the roadmap markdown.
119
+ try {
120
+ const roadmapFile = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
121
+ if (!roadmapFile) return 1;
122
+ const roadmapContent = await loadFile(roadmapFile);
123
+ if (!roadmapContent) return 1;
124
+ const roadmap = parseRoadmap(roadmapContent);
125
+ if (roadmap.slices.length === 0) return 1;
126
+ return isMilestoneComplete(roadmap) ? 0 : 1;
127
+ } catch {
128
+ // Parsing failures should not cause false-positive pauses.
129
+ return 1;
130
+ }
131
+ }
132
+
46
133
  /**
47
134
  * Run the verification gate for the current execute-task unit.
48
135
  * Returns:
@@ -56,7 +143,15 @@ export async function runPostUnitVerification(
56
143
  ): Promise<VerificationResult> {
57
144
  const { s, ctx, pi } = vctx;
58
145
 
59
- if (!s.currentUnit || s.currentUnit.type !== "execute-task") {
146
+ if (!s.currentUnit) {
147
+ return "continue";
148
+ }
149
+
150
+ if (s.currentUnit.type === "validate-milestone") {
151
+ return await runValidateMilestonePostCheck(vctx, pauseAuto);
152
+ }
153
+
154
+ if (s.currentUnit.type !== "execute-task") {
60
155
  return "continue";
61
156
  }
62
157
 
@@ -187,7 +187,7 @@ import {
187
187
  deregisterSigtermHandler as _deregisterSigtermHandler,
188
188
  detectWorkingTreeActivity,
189
189
  } from "./auto-supervisor.js";
190
- import { isDbAvailable } from "./gsd-db.js";
190
+ import { isDbAvailable, getMilestone } from "./gsd-db.js";
191
191
  import { countPendingCaptures } from "./captures.js";
192
192
  import { clearCmuxSidebar, logCmuxEvent, syncCmuxSidebar } from "../cmux/index.js";
193
193
 
@@ -195,10 +195,12 @@ import { clearCmuxSidebar, logCmuxEvent, syncCmuxSidebar } from "../cmux/index.j
195
195
  import { startUnitSupervision } from "./auto-timers.js";
196
196
  import { runPostUnitVerification } from "./auto-verification.js";
197
197
  import {
198
+ autoCommitUnit,
198
199
  postUnitPreVerification,
199
200
  postUnitPostVerification,
200
201
  } from "./auto-post-unit.js";
201
202
  import { bootstrapAutoSession, openProjectDbIfPresent, type BootstrapDeps } from "./auto-start.js";
203
+ import { initHealthWidget } from "./health-widget.js";
202
204
  import { autoLoop, resolveAgentEnd, resolveAgentEndCancelled, _resetPendingResolve, isSessionSwitchInFlight, type LoopDeps, type ErrorContext } from "./auto-loop.js";
203
205
  // Slice-level parallelism (#2340)
204
206
  import { getEligibleSlices } from "./slice-parallel-eligibility.js";
@@ -650,6 +652,7 @@ function handleLostSessionLock(
650
652
  ctx?.ui.setStatus("gsd-auto", undefined);
651
653
  ctx?.ui.setWidget("gsd-progress", undefined);
652
654
  ctx?.ui.setFooter(undefined);
655
+ if (ctx) initHealthWidget(ctx);
653
656
  }
654
657
 
655
658
  /**
@@ -684,6 +687,7 @@ function cleanupAfterLoopExit(ctx: ExtensionContext): void {
684
687
  ctx.ui.setStatus("gsd-auto", undefined);
685
688
  ctx.ui.setWidget("gsd-progress", undefined);
686
689
  ctx.ui.setFooter(undefined);
690
+ initHealthWidget(ctx);
687
691
  }
688
692
 
689
693
  // Restore CWD out of worktree back to original project root
@@ -758,24 +762,36 @@ export async function stopAuto(
758
762
  : { notify: () => {} };
759
763
  const resolver = buildResolver();
760
764
 
761
- // Check if the milestone is complete SUMMARY file is the authoritative signal.
765
+ // Check if the milestone is complete. DB status is the authoritative
766
+ // signal — only a successful gsd_complete_milestone call flips it to
767
+ // "complete" (tools/complete-milestone.ts). SUMMARY file presence is
768
+ // NOT sufficient: a blocker placeholder stub or a partial write can
769
+ // leave a file behind without the milestone actually being done,
770
+ // which previously caused stopAuto to merge a failed milestone and
771
+ // emit a misleading metadata-only merge warning (#4175).
772
+ // DB-unavailable projects fall back to SUMMARY-file presence.
762
773
  let milestoneComplete = false;
763
774
  try {
764
- const summaryPath = resolveMilestoneFile(
765
- s.originalBasePath || s.basePath,
766
- s.currentMilestoneId,
767
- "SUMMARY",
768
- );
769
- if (!summaryPath) {
770
- // Also check in the worktree path (SUMMARY may not be synced yet)
771
- const wtSummaryPath = resolveMilestoneFile(
772
- s.basePath,
775
+ if (isDbAvailable()) {
776
+ const dbRow = getMilestone(s.currentMilestoneId);
777
+ milestoneComplete = dbRow?.status === "complete";
778
+ } else {
779
+ const summaryPath = resolveMilestoneFile(
780
+ s.originalBasePath || s.basePath,
773
781
  s.currentMilestoneId,
774
782
  "SUMMARY",
775
783
  );
776
- milestoneComplete = wtSummaryPath !== null;
777
- } else {
778
- milestoneComplete = true;
784
+ if (!summaryPath) {
785
+ // Also check in the worktree path (SUMMARY may not be synced yet)
786
+ const wtSummaryPath = resolveMilestoneFile(
787
+ s.basePath,
788
+ s.currentMilestoneId,
789
+ "SUMMARY",
790
+ );
791
+ milestoneComplete = wtSummaryPath !== null;
792
+ } else {
793
+ milestoneComplete = true;
794
+ }
779
795
  }
780
796
  } catch (err) {
781
797
  // Non-fatal — fall through to preserveBranch path
@@ -943,6 +959,7 @@ export async function stopAuto(
943
959
  ctx?.ui.setStatus("gsd-auto", undefined);
944
960
  ctx?.ui.setWidget("gsd-progress", undefined);
945
961
  ctx?.ui.setFooter(undefined);
962
+ if (ctx) initHealthWidget(ctx);
946
963
  restoreProjectRootEnv();
947
964
  restoreMilestoneLockEnv();
948
965
 
@@ -1044,6 +1061,7 @@ export async function pauseAuto(
1044
1061
  ctx?.ui.setStatus("gsd-auto", "paused");
1045
1062
  ctx?.ui.setWidget("gsd-progress", undefined);
1046
1063
  ctx?.ui.setFooter(undefined);
1064
+ if (ctx) initHealthWidget(ctx);
1047
1065
  const resumeCmd = s.stepMode ? "/gsd next" : "/gsd auto";
1048
1066
  ctx?.ui.notify(
1049
1067
  `${s.stepMode ? "Step" : "Auto"}-mode paused (Escape). Type to interact, or ${resumeCmd} to resume.`,
@@ -1163,6 +1181,7 @@ function buildLoopDeps(): LoopDeps {
1163
1181
  getMainBranch,
1164
1182
  // Unit closeout + runtime records
1165
1183
  closeoutUnit,
1184
+ autoCommitUnit,
1166
1185
  recordOutcome,
1167
1186
  writeLock,
1168
1187
  captureAvailableSkills,
@@ -1383,6 +1402,11 @@ export async function startAuto(
1383
1402
  s.stepMode = requestedStepMode;
1384
1403
  s.cmdCtx = ctx;
1385
1404
  s.basePath = base;
1405
+ // Ensure the workflow-logger audit log is pinned to the project root
1406
+ // even when auto-mode is entered via a path that bypasses the
1407
+ // bootstrap/dynamic-tools ensureDbOpen() → setLogBasePath() chain
1408
+ // (e.g. /clear resume, hot-reload).
1409
+ setLogBasePath(base);
1386
1410
  s.unitDispatchCount.clear();
1387
1411
  s.unitLifetimeDispatches.clear();
1388
1412
  if (!getLedger()) initMetrics(base);
@@ -2,7 +2,6 @@
2
2
 
3
3
  import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
4
4
 
5
- import { registerGSDCommand } from "../commands.js";
6
5
  import { registerExitCommand } from "../exit-command.js";
7
6
  import { registerWorktreeCommand } from "../worktree-command.js";
8
7
  import { registerDbTools } from "./db-tools.js";
@@ -12,6 +11,7 @@ import { registerQueryTools } from "./query-tools.js";
12
11
  import { registerHooks } from "./register-hooks.js";
13
12
  import { registerShortcuts } from "./register-shortcuts.js";
14
13
  import { writeCrashLog } from "./crash-log.js";
14
+ import { logWarning } from "../workflow-logger.js";
15
15
 
16
16
  export { writeCrashLog } from "./crash-log.js";
17
17
 
@@ -58,7 +58,8 @@ function installEpipeGuard(): void {
58
58
  }
59
59
 
60
60
  export function registerGsdExtension(pi: ExtensionAPI): void {
61
- registerGSDCommand(pi);
61
+ // Note: registerGSDCommand is called by index.ts before this function,
62
+ // so we intentionally skip it here to avoid double-registration.
62
63
  registerWorktreeCommand(pi);
63
64
  registerExitCommand(pi);
64
65
 
@@ -71,10 +72,25 @@ export function registerGsdExtension(pi: ExtensionAPI): void {
71
72
  },
72
73
  });
73
74
 
74
- registerDynamicTools(pi);
75
- registerDbTools(pi);
76
- registerJournalTools(pi);
77
- registerQueryTools(pi);
78
- registerShortcuts(pi);
79
- registerHooks(pi);
75
+ // Wrap non-critical registrations individually so one failure
76
+ // doesn't prevent the others from loading.
77
+ const nonCriticalRegistrations: Array<[string, () => void]> = [
78
+ ["dynamic-tools", () => registerDynamicTools(pi)],
79
+ ["db-tools", () => registerDbTools(pi)],
80
+ ["journal-tools", () => registerJournalTools(pi)],
81
+ ["query-tools", () => registerQueryTools(pi)],
82
+ ["shortcuts", () => registerShortcuts(pi)],
83
+ ["hooks", () => registerHooks(pi)],
84
+ ];
85
+
86
+ for (const [name, register] of nonCriticalRegistrations) {
87
+ try {
88
+ register();
89
+ } catch (err) {
90
+ logWarning(
91
+ "bootstrap",
92
+ `Failed to register ${name}: ${err instanceof Error ? err.message : String(err)}`,
93
+ );
94
+ }
95
+ }
80
96
  }