gsd-pi 2.81.0 → 2.82.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/README.md +36 -24
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/gsd/auto/loop.js +111 -8
  4. package/dist/resources/extensions/gsd/auto/phases.js +190 -97
  5. package/dist/resources/extensions/gsd/auto/run-unit.js +66 -3
  6. package/dist/resources/extensions/gsd/auto/session.js +9 -0
  7. package/dist/resources/extensions/gsd/auto/verification-retry-policy.js +43 -0
  8. package/dist/resources/extensions/gsd/auto-dashboard.js +182 -178
  9. package/dist/resources/extensions/gsd/auto-dispatch.js +14 -11
  10. package/dist/resources/extensions/gsd/auto-post-unit.js +7 -1
  11. package/dist/resources/extensions/gsd/auto-recovery.js +6 -181
  12. package/dist/resources/extensions/gsd/auto-runtime-state.js +5 -0
  13. package/dist/resources/extensions/gsd/auto-start.js +20 -23
  14. package/dist/resources/extensions/gsd/auto-unit-closeout.js +33 -5
  15. package/dist/resources/extensions/gsd/auto-verification.js +12 -6
  16. package/dist/resources/extensions/gsd/auto-worktree.js +8 -0
  17. package/dist/resources/extensions/gsd/auto.js +265 -76
  18. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +13 -6
  19. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +8 -2
  20. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +4 -8
  21. package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +4 -10
  22. package/dist/resources/extensions/gsd/commands/handlers/parallel.js +9 -0
  23. package/dist/resources/extensions/gsd/git-service.js +2 -1
  24. package/dist/resources/extensions/gsd/gsd-db.js +7 -23
  25. package/dist/resources/extensions/gsd/health-widget-core.js +1 -1
  26. package/dist/resources/extensions/gsd/health-widget.js +4 -10
  27. package/dist/resources/extensions/gsd/markdown-renderer.js +0 -95
  28. package/dist/resources/extensions/gsd/native-git-bridge.js +14 -14
  29. package/dist/resources/extensions/gsd/notification-overlay.js +35 -40
  30. package/dist/resources/extensions/gsd/parallel-merge.js +53 -30
  31. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +25 -33
  32. package/dist/resources/extensions/gsd/prompts/complete-slice.md +14 -12
  33. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +20 -2
  34. package/dist/resources/extensions/gsd/prompts/discuss.md +20 -2
  35. package/dist/resources/extensions/gsd/recovery-classification.js +15 -1
  36. package/dist/resources/extensions/gsd/session-lock.js +40 -0
  37. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +131 -0
  38. package/dist/resources/extensions/gsd/state-reconciliation/drift/merge-state.js +247 -0
  39. package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +50 -0
  40. package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +87 -0
  41. package/dist/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.js +50 -0
  42. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +124 -0
  43. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +32 -0
  44. package/dist/resources/extensions/gsd/state-reconciliation/errors.js +41 -0
  45. package/dist/resources/extensions/gsd/state-reconciliation/index.js +99 -0
  46. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +24 -0
  47. package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +43 -0
  48. package/dist/resources/extensions/gsd/state-reconciliation/types.js +3 -0
  49. package/dist/resources/extensions/gsd/state-reconciliation.js +5 -26
  50. package/dist/resources/extensions/gsd/tui/render-kit.js +74 -0
  51. package/dist/resources/extensions/gsd/watch/header-renderer.js +92 -69
  52. package/dist/resources/extensions/gsd/watch/splash-palette.js +10 -0
  53. package/dist/resources/extensions/gsd/workflow-mcp.js +2 -2
  54. package/dist/resources/extensions/gsd/worktree-lifecycle.js +722 -316
  55. package/dist/resources/extensions/gsd/worktree-telemetry.js +3 -1
  56. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  57. package/dist/web/standalone/.next/BUILD_ID +1 -1
  58. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  59. package/dist/web/standalone/.next/build-manifest.json +2 -2
  60. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/index.html +1 -1
  77. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  84. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  86. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  87. package/dist/welcome-screen.d.ts +0 -7
  88. package/dist/welcome-screen.js +60 -69
  89. package/package.json +1 -1
  90. package/packages/daemon/package.json +2 -2
  91. package/packages/mcp-server/package.json +2 -2
  92. package/packages/native/package.json +1 -1
  93. package/packages/pi-agent-core/package.json +1 -1
  94. package/packages/pi-ai/package.json +1 -1
  95. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.d.ts +2 -0
  96. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.d.ts.map +1 -0
  97. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.js +47 -0
  98. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.js.map +1 -0
  99. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +76 -9
  100. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  101. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.d.ts +2 -0
  102. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.d.ts.map +1 -0
  103. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.js +40 -0
  104. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.js.map +1 -0
  105. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +0 -1
  106. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -1
  107. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +30 -29
  108. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -1
  109. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +10 -3
  110. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -1
  111. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  112. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +13 -13
  113. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts +1 -3
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +58 -3
  117. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts +2 -2
  119. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts.map +1 -1
  120. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js +12 -6
  121. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js.map +1 -1
  122. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +14 -41
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +0 -1
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +86 -82
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.d.ts +35 -0
  130. package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.d.ts.map +1 -0
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.js +152 -0
  132. package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.js.map +1 -0
  133. package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.d.ts +16 -0
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.d.ts.map +1 -0
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.js +73 -0
  136. package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.js.map +1 -0
  137. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  139. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +12 -8
  140. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  141. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.d.ts +2 -0
  142. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.d.ts.map +1 -0
  143. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.js +17 -0
  144. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.js.map +1 -0
  145. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  146. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +105 -1
  147. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  148. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  149. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +27 -26
  150. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  151. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +9 -6
  152. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -1
  153. package/packages/pi-coding-agent/package.json +1 -1
  154. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/assistant-message-design.test.ts +56 -0
  155. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +113 -9
  156. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/user-message-design.test.ts +48 -0
  157. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +10 -3
  158. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +43 -42
  159. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +14 -14
  160. package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +64 -3
  161. package/packages/pi-coding-agent/src/modes/interactive/components/diff.ts +13 -7
  162. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +15 -42
  163. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +84 -104
  164. package/packages/pi-coding-agent/src/modes/interactive/components/transcript-design.ts +196 -0
  165. package/packages/pi-coding-agent/src/modes/interactive/components/tui-style-kit.ts +94 -0
  166. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +14 -9
  167. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-highlight.test.ts +23 -0
  168. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +106 -1
  169. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +27 -26
  170. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +9 -6
  171. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  172. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +14 -1
  173. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  174. package/packages/pi-tui/dist/overlay-layout.d.ts.map +1 -1
  175. package/packages/pi-tui/dist/overlay-layout.js +9 -6
  176. package/packages/pi-tui/dist/overlay-layout.js.map +1 -1
  177. package/packages/pi-tui/package.json +1 -1
  178. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +20 -1
  179. package/packages/pi-tui/src/overlay-layout.ts +10 -7
  180. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  181. package/packages/rpc-client/package.json +1 -1
  182. package/pkg/dist/modes/interactive/theme/theme-highlight.test.d.ts +2 -0
  183. package/pkg/dist/modes/interactive/theme/theme-highlight.test.d.ts.map +1 -0
  184. package/pkg/dist/modes/interactive/theme/theme-highlight.test.js +17 -0
  185. package/pkg/dist/modes/interactive/theme/theme-highlight.test.js.map +1 -0
  186. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  187. package/pkg/dist/modes/interactive/theme/theme.js +105 -1
  188. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  189. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  190. package/pkg/dist/modes/interactive/theme/themes.js +27 -26
  191. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  192. package/pkg/package.json +1 -1
  193. package/src/resources/extensions/gsd/auto/loop-deps.ts +9 -5
  194. package/src/resources/extensions/gsd/auto/loop.ts +113 -9
  195. package/src/resources/extensions/gsd/auto/phases.ts +144 -19
  196. package/src/resources/extensions/gsd/auto/run-unit.ts +69 -4
  197. package/src/resources/extensions/gsd/auto/session.ts +10 -0
  198. package/src/resources/extensions/gsd/auto/verification-retry-policy.ts +82 -0
  199. package/src/resources/extensions/gsd/auto-dashboard.ts +230 -183
  200. package/src/resources/extensions/gsd/auto-dispatch.ts +15 -1
  201. package/src/resources/extensions/gsd/auto-post-unit.ts +7 -1
  202. package/src/resources/extensions/gsd/auto-recovery.ts +7 -209
  203. package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
  204. package/src/resources/extensions/gsd/auto-start.ts +22 -22
  205. package/src/resources/extensions/gsd/auto-unit-closeout.ts +51 -0
  206. package/src/resources/extensions/gsd/auto-verification.ts +12 -6
  207. package/src/resources/extensions/gsd/auto-worktree.ts +8 -0
  208. package/src/resources/extensions/gsd/auto.ts +295 -75
  209. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +21 -6
  210. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +6 -2
  211. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +5 -8
  212. package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +4 -10
  213. package/src/resources/extensions/gsd/commands/handlers/parallel.ts +12 -0
  214. package/src/resources/extensions/gsd/git-service.ts +2 -0
  215. package/src/resources/extensions/gsd/gsd-db.ts +7 -23
  216. package/src/resources/extensions/gsd/health-widget-core.ts +1 -1
  217. package/src/resources/extensions/gsd/health-widget.ts +6 -10
  218. package/src/resources/extensions/gsd/journal.ts +2 -0
  219. package/src/resources/extensions/gsd/markdown-renderer.ts +4 -95
  220. package/src/resources/extensions/gsd/native-git-bridge.ts +14 -13
  221. package/src/resources/extensions/gsd/notification-overlay.ts +50 -46
  222. package/src/resources/extensions/gsd/parallel-merge.ts +61 -34
  223. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +33 -35
  224. package/src/resources/extensions/gsd/prompts/complete-slice.md +14 -12
  225. package/src/resources/extensions/gsd/prompts/discuss-headless.md +20 -2
  226. package/src/resources/extensions/gsd/prompts/discuss.md +20 -2
  227. package/src/resources/extensions/gsd/recovery-classification.ts +18 -1
  228. package/src/resources/extensions/gsd/session-lock.ts +41 -0
  229. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +172 -0
  230. package/src/resources/extensions/gsd/state-reconciliation/drift/merge-state.ts +337 -0
  231. package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +69 -0
  232. package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +109 -0
  233. package/src/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.ts +68 -0
  234. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +185 -0
  235. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +46 -0
  236. package/src/resources/extensions/gsd/state-reconciliation/errors.ts +67 -0
  237. package/src/resources/extensions/gsd/state-reconciliation/index.ts +142 -0
  238. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +27 -0
  239. package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +60 -0
  240. package/src/resources/extensions/gsd/state-reconciliation/types.ts +83 -0
  241. package/src/resources/extensions/gsd/state-reconciliation.ts +21 -53
  242. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +1 -1
  243. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +99 -0
  244. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +654 -176
  245. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +291 -4
  246. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +16 -1
  247. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +18 -0
  248. package/src/resources/extensions/gsd/tests/auto-unit-closeout.test.ts +68 -0
  249. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +28 -1
  250. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +20 -2
  251. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +44 -0
  252. package/src/resources/extensions/gsd/tests/header-renderer.test.ts +40 -0
  253. package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +10 -0
  254. package/src/resources/extensions/gsd/tests/health-widget.test.ts +14 -4
  255. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +26 -0
  256. package/src/resources/extensions/gsd/tests/integration/integration-proof.test.ts +1 -1
  257. package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +116 -24
  258. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -1
  259. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +1 -1
  260. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +46 -11
  261. package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +78 -41
  262. package/src/resources/extensions/gsd/tests/notifications-handler.test.ts +44 -0
  263. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +12 -217
  264. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +38 -6
  265. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +2 -2
  266. package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +1 -1
  267. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +24 -1
  268. package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +7 -3
  269. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +6 -3
  270. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +24 -0
  271. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +65 -58
  272. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +952 -0
  273. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -0
  274. package/src/resources/extensions/gsd/tests/tui-header-lifecycle.test.ts +121 -1
  275. package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +66 -0
  276. package/src/resources/extensions/gsd/tests/verification-retry-policy.test.ts +83 -0
  277. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +6 -0
  278. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +158 -58
  279. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +572 -118
  280. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +59 -2
  281. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +18 -0
  282. package/src/resources/extensions/gsd/tui/render-kit.ts +109 -0
  283. package/src/resources/extensions/gsd/watch/header-renderer.ts +121 -79
  284. package/src/resources/extensions/gsd/watch/splash-palette.ts +11 -0
  285. package/src/resources/extensions/gsd/workflow-mcp.ts +2 -2
  286. package/src/resources/extensions/gsd/worktree-lifecycle.ts +1151 -524
  287. package/src/resources/extensions/gsd/worktree-telemetry.ts +7 -2
  288. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +0 -1544
  289. /package/dist/web/standalone/.next/static/{drLMkgfHQ8lzS229_HWYR → S44UQTFCUdA44dkjfYt6S}/_buildManifest.js +0 -0
  290. /package/dist/web/standalone/.next/static/{drLMkgfHQ8lzS229_HWYR → S44UQTFCUdA44dkjfYt6S}/_ssgManifest.js +0 -0
@@ -1,7 +1,6 @@
1
1
  // GSD-2 + src/resources/extensions/gsd/auto-dashboard.ts - Auto-mode progress widget rendering and dashboard helpers.
2
- import { getCurrentBranch } from "./worktree.js";
3
2
  import { getActiveHook } from "./post-unit-hooks.js";
4
- import { getLedger, getProjectTotals } from "./metrics.js";
3
+ import { getLedger } from "./metrics.js";
5
4
  import { getErrorMessage } from "./error-utils.js";
6
5
  import { nativeIsRepo } from "./native-git-bridge.js";
7
6
  import { isDbAvailable, getMilestoneSlices, getSliceTasks } from "./gsd-db.js";
@@ -10,15 +9,12 @@ import { execFileSync } from "node:child_process";
10
9
  import { truncateToWidth, visibleWidth } from "@gsd/pi-tui";
11
10
  import { makeUI } from "../shared/tui.js";
12
11
  import { GLYPH, INDENT } from "../shared/mod.js";
12
+ import { padRightVisible, renderFrame, renderProgressBar, rightAlign, wrapVisibleText } from "./tui/render-kit.js";
13
13
  import { computeProgressScore } from "./progress-score.js";
14
- import { getActiveWorktreeName } from "./worktree-command.js";
15
14
  import { getGlobalGSDPreferencesPath, getProjectGSDPreferencesPath, parsePreferencesMarkdown, } from "./preferences.js";
16
- import { resolveServiceTierIcon, getEffectiveServiceTier } from "./service-tier.js";
17
15
  import { parseUnitId } from "./unit-id.js";
18
- import { formatRtkSavingsLabel, getRtkSessionSavings, } from "../shared/rtk-session-stats.js";
19
16
  import { logWarning } from "./workflow-logger.js";
20
17
  import { formattedShortcutPair } from "./shortcut-defs.js";
21
- import { homedir } from "node:os";
22
18
  import { readUnitRuntimeRecord } from "./unit-runtime.js";
23
19
  // ─── UAT Slice Extraction ─────────────────────────────────────────────────────
24
20
  /**
@@ -468,6 +464,7 @@ export function _resetWidgetModeForTests() {
468
464
  export function updateProgressWidget(ctx, unitType, unitId, state, accessors, tierBadge) {
469
465
  if (!ctx.hasUI)
470
466
  return;
467
+ ctx.ui.setWidget("gsd-outcome", undefined);
471
468
  // Welcome header is a startup-only banner — permanently suppress it once
472
469
  // auto-mode activates. The dashboard widget owns all status from here.
473
470
  // Note: setHeader(undefined) restores the built-in header (logo +
@@ -497,53 +494,11 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
497
494
  if (mid) {
498
495
  updateSliceProgressCache(accessors.getBasePath(), mid.id, slice?.id);
499
496
  }
500
- // Cache git branch at widget creation time (not per render)
501
- let cachedBranch = null;
502
- try {
503
- cachedBranch = getCurrentBranch(accessors.getBasePath());
504
- }
505
- catch (err) { /* not in git repo */
506
- logWarning("dashboard", `git branch detection failed: ${err instanceof Error ? err.message : String(err)}`);
507
- }
508
- // Cache short pwd (last 2 path segments only) + worktree/branch info
509
- let widgetPwd;
510
- {
511
- let fullPwd = process.cwd();
512
- const widgetHome = homedir();
513
- if (widgetHome && (fullPwd === widgetHome || fullPwd.startsWith(widgetHome + "/") || fullPwd.startsWith(widgetHome + "\\"))) {
514
- fullPwd = `~${fullPwd.slice(widgetHome.length)}`;
515
- }
516
- const parts = fullPwd.split("/");
517
- widgetPwd = parts.length > 2 ? parts.slice(-2).join("/") : fullPwd;
518
- }
519
- const worktreeName = getActiveWorktreeName();
520
- if (worktreeName && cachedBranch) {
521
- widgetPwd = `${widgetPwd} (\u2387 ${cachedBranch})`;
522
- }
523
- else if (cachedBranch) {
524
- widgetPwd = `${widgetPwd} (${cachedBranch})`;
525
- }
526
- // Pre-fetch last commit for display
527
- refreshLastCommit(accessors.getBasePath());
528
- // Cache the effective service tier at widget creation time (reads preferences)
529
- const effectiveServiceTier = getEffectiveServiceTier();
530
497
  ctx.ui.setWidget("gsd-progress", (tui, theme) => {
531
498
  let pulseBright = true;
532
499
  let cachedLines;
533
500
  let cachedWidth;
534
- let cachedRtkLabel;
535
501
  let cachedRuntimeRecord = null;
536
- const refreshRtkLabel = () => {
537
- try {
538
- const sessionId = ctx.sessionManager.getSessionId();
539
- const savings = sessionId ? getRtkSessionSavings(accessors.getBasePath(), sessionId) : null;
540
- cachedRtkLabel = formatRtkSavingsLabel(savings);
541
- }
542
- catch (err) {
543
- logWarning("dashboard", `RTK savings lookup failed: ${err instanceof Error ? err.message : String(err)}`);
544
- cachedRtkLabel = null;
545
- }
546
- };
547
502
  const refreshRuntimeRecord = () => {
548
503
  try {
549
504
  cachedRuntimeRecord = readUnitRuntimeRecord(accessors.getBasePath(), unitType, unitId);
@@ -552,7 +507,6 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
552
507
  cachedRuntimeRecord = null;
553
508
  }
554
509
  };
555
- refreshRtkLabel();
556
510
  refreshRuntimeRecord();
557
511
  const pulseTimer = setInterval(() => {
558
512
  pulseBright = !pulseBright;
@@ -568,7 +522,6 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
568
522
  if (mid) {
569
523
  updateSliceProgressCache(accessors.getBasePath(), mid.id, slice?.id);
570
524
  }
571
- refreshRtkLabel();
572
525
  refreshRuntimeRecord();
573
526
  cachedLines = undefined;
574
527
  }
@@ -635,44 +588,6 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
635
588
  lines.push(`${pad} ${signalStr}`);
636
589
  }
637
590
  }
638
- // ── Gather stats (needed by multiple modes) ─────────────────────
639
- const cmdCtx = accessors.getCmdCtx();
640
- let totalInput = 0;
641
- let totalCacheRead = 0;
642
- if (cmdCtx) {
643
- for (const entry of cmdCtx.sessionManager.getEntries()) {
644
- if (entry.type === "message") {
645
- const msgEntry = entry;
646
- if (msgEntry.message?.role === "assistant") {
647
- const u = msgEntry.message.usage;
648
- if (u) {
649
- totalInput += u.input || 0;
650
- totalCacheRead += u.cacheRead || 0;
651
- }
652
- }
653
- }
654
- }
655
- }
656
- const mLedger = getLedger();
657
- const autoTotals = mLedger ? getProjectTotals(mLedger.units) : null;
658
- const cumulativeCost = autoTotals?.cost ?? 0;
659
- const cxUsage = cmdCtx?.getContextUsage?.();
660
- const cxWindow = cxUsage?.contextWindow ?? cmdCtx?.model?.contextWindow ?? 0;
661
- const cxPctVal = cxUsage?.percent ?? 0;
662
- const cxPct = cxUsage?.percent !== null ? cxPctVal.toFixed(1) : "?";
663
- // Model display — prefer dispatched model ID (set after selectAndApplyModel
664
- // + hook overrides) over cmdCtx?.model which can be stale (#2899).
665
- const dispatchedModelId = accessors.getCurrentDispatchedModelId();
666
- const modelId = dispatchedModelId
667
- ? dispatchedModelId.split("/").slice(1).join("/") || dispatchedModelId
668
- : (cmdCtx?.model?.id ?? "");
669
- const modelProvider = dispatchedModelId
670
- ? dispatchedModelId.split("/")[0] || ""
671
- : (cmdCtx?.model?.provider ?? "");
672
- const tierIcon = resolveServiceTierIcon(effectiveServiceTier, modelId);
673
- const modelDisplay = (modelProvider && modelId
674
- ? `${modelProvider}/${modelId}`
675
- : modelId) + (tierIcon ? ` ${tierIcon}` : "");
676
591
  // ── Mode: off — return empty ──────────────────────────────────
677
592
  if (widgetMode === "off") {
678
593
  cachedLines = [];
@@ -686,7 +601,7 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
686
601
  cachedWidth = width;
687
602
  return lines;
688
603
  }
689
- // ── Mode: small — header + progress bar + compact stats ───────
604
+ // ── Mode: small — header + active work progress ───────────────
690
605
  if (widgetMode === "small") {
691
606
  lines.push("");
692
607
  // Action line
@@ -698,10 +613,7 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
698
613
  if (shouldRenderRoadmapProgress(roadmapSlices)) {
699
614
  const { done, total, activeSliceTasks } = roadmapSlices;
700
615
  const barWidth = Math.max(6, Math.min(18, Math.floor(width * 0.25)));
701
- const pct = total > 0 ? done / total : 0;
702
- const filled = Math.round(pct * barWidth);
703
- const bar = theme.fg("success", "━".repeat(filled))
704
- + theme.fg("dim", "─".repeat(barWidth - filled));
616
+ const bar = renderProgressBar(theme, done, total, barWidth);
705
617
  let meta = `${theme.fg("text", `${done}`)}${theme.fg("dim", `/${total} slices`)}`;
706
618
  if (activeSliceTasks && activeSliceTasks.total > 0) {
707
619
  const tn = Math.min(activeSliceTasks.done + 1, activeSliceTasks.total);
@@ -709,20 +621,6 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
709
621
  }
710
622
  lines.push(`${pad}${bar} ${meta}`);
711
623
  }
712
- // Compact stats: cost + context only
713
- const smallStats = [];
714
- if (cumulativeCost)
715
- smallStats.push(theme.fg("warning", `$${cumulativeCost.toFixed(2)}`));
716
- const cxDisplay = `${cxPct}%ctx`;
717
- if (cxPctVal > 90)
718
- smallStats.push(theme.fg("error", cxDisplay));
719
- else if (cxPctVal > 70)
720
- smallStats.push(theme.fg("warning", cxDisplay));
721
- else
722
- smallStats.push(theme.fg("dim", cxDisplay));
723
- if (smallStats.length > 0) {
724
- lines.push(rightAlign("", smallStats.join(theme.fg("dim", " ")), width));
725
- }
726
624
  lines.push(...ui.bar());
727
625
  cachedLines = lines;
728
626
  cachedWidth = width;
@@ -730,11 +628,10 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
730
628
  }
731
629
  // ── Mode: full — complete two-column layout ───────────────────
732
630
  lines.push("");
733
- // Context section: milestone + slice + model
631
+ // Context section: milestone + slice. Footer owns model/cost/context.
734
632
  const hasContext = !!(mid || (slice && unitType !== "research-milestone" && unitType !== "plan-milestone"));
735
633
  if (mid) {
736
- const modelTag = modelDisplay ? theme.fg("muted", ` ${modelDisplay}`) : "";
737
- lines.push(truncateToWidth(`${pad}${theme.fg("dim", mid.title)}${modelTag}`, width, "…"));
634
+ lines.push(truncateToWidth(`${pad}${theme.fg("dim", mid.title)}`, width, ""));
738
635
  }
739
636
  if (slice && unitType !== "research-milestone" && unitType !== "plan-milestone") {
740
637
  lines.push(truncateToWidth(`${pad}${theme.fg("text", theme.bold(`${slice.id}: ${slice.title}`))}`, width, "…"));
@@ -759,10 +656,7 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
759
656
  if (shouldRenderRoadmapProgress(roadmapSlices)) {
760
657
  const { done, total, activeSliceTasks } = roadmapSlices;
761
658
  const barWidth = Math.max(6, Math.min(18, Math.floor(leftColWidth * 0.4)));
762
- const pct = total > 0 ? done / total : 0;
763
- const filled = Math.round(pct * barWidth);
764
- const bar = theme.fg("success", "━".repeat(filled))
765
- + theme.fg("dim", "─".repeat(barWidth - filled));
659
+ const bar = renderProgressBar(theme, done, total, barWidth);
766
660
  let meta = `${theme.fg("text", `${done}`)}${theme.fg("dim", `/${total} slices`)}`;
767
661
  if (activeSliceTasks && activeSliceTasks.total > 0) {
768
662
  const taskNum = isHook
@@ -818,7 +712,7 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
818
712
  if (maxRows > 0) {
819
713
  lines.push("");
820
714
  for (let i = 0; i < maxRows; i++) {
821
- const left = padToWidth(truncateToWidth(leftLines[i] ?? "", leftColWidth, "…"), leftColWidth);
715
+ const left = padRightVisible(truncateToWidth(leftLines[i] ?? "", leftColWidth, "…"), leftColWidth);
822
716
  const right = rightLines[i] ?? "";
823
717
  lines.push(`${left}${right}`);
824
718
  }
@@ -831,46 +725,8 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
831
725
  lines.push(truncateToWidth(l, width, "…"));
832
726
  }
833
727
  }
834
- // ── Footer: simplified stats + pwd + last commit + hints ────────
728
+ // ── Auto controls. Footer owns cwd/branch/model/cost/context. ───
835
729
  lines.push("");
836
- {
837
- const sp = [];
838
- if (totalCacheRead + totalInput > 0) {
839
- const hitRate = Math.round((totalCacheRead / (totalCacheRead + totalInput)) * 100);
840
- const hitColor = hitRate >= 70 ? "success" : hitRate >= 40 ? "warning" : "error";
841
- sp.push(theme.fg(hitColor, `${hitRate}%hit`));
842
- }
843
- if (cumulativeCost)
844
- sp.push(theme.fg("warning", `$${cumulativeCost.toFixed(2)}`));
845
- const CX_BAR_WIDTH = 8;
846
- const cxBarFilled = Math.min(CX_BAR_WIDTH, Math.max(0, Math.round((cxPctVal / 100) * CX_BAR_WIDTH)));
847
- const cxBarColor = cxPctVal > 90 ? "error" : cxPctVal > 70 ? "warning" : "success";
848
- const cxBar = theme.fg(cxBarColor, "━".repeat(cxBarFilled)) +
849
- theme.fg("dim", "─".repeat(CX_BAR_WIDTH - cxBarFilled));
850
- const cxPctText = `${cxPct}%/${formatWidgetTokens(cxWindow)}`;
851
- const cxColorized = cxPctVal > 90
852
- ? theme.fg("error", cxPctText)
853
- : cxPctVal > 70
854
- ? theme.fg("warning", cxPctText)
855
- : cxPctText;
856
- sp.push(`${cxBar} ${cxColorized}`);
857
- const statsLine = sp.map(p => p.includes("\x1b[") ? p : theme.fg("dim", p))
858
- .join(theme.fg("dim", " "));
859
- if (statsLine) {
860
- lines.push(rightAlign("", statsLine, width));
861
- }
862
- if (cachedRtkLabel) {
863
- lines.push(rightAlign("", theme.fg("dim", cachedRtkLabel), width));
864
- }
865
- }
866
- // Last commit info
867
- const lastCommit = getLastCommit(accessors.getBasePath());
868
- const maxCommitLen = 65;
869
- const commitMsg = lastCommit
870
- ? lastCommit.message.length > maxCommitLen
871
- ? lastCommit.message.slice(0, maxCommitLen - 1) + "…"
872
- : lastCommit.message
873
- : "";
874
730
  // Step-mode guidance — shown above keyboard hints when auto is paused
875
731
  if (accessors.isStepMode()) {
876
732
  lines.push(`${pad}${theme.fg("accent", "→")} ${theme.fg("dim", "Ctrl+N to advance to next step · /gsd status for overview")}`);
@@ -881,16 +737,7 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
881
737
  hintParts.push(`${formattedShortcutPair("dashboard")} dashboard`);
882
738
  hintParts.push(`${formattedShortcutPair("parallel")} parallel`);
883
739
  const hintStr = theme.fg("dim", hintParts.join(" | "));
884
- const commitStr = lastCommit
885
- ? theme.fg("dim", `${lastCommit.timeAgo} ago: ${commitMsg}`)
886
- : "";
887
- const locationStr = theme.fg("dim", widgetPwd);
888
- if (commitStr) {
889
- lines.push(rightAlign(`${pad}${locationStr} · ${commitStr}`, hintStr, width));
890
- }
891
- else {
892
- lines.push(rightAlign(`${pad}${locationStr}`, hintStr, width));
893
- }
740
+ lines.push(rightAlign("", hintStr, width));
894
741
  lines.push(...ui.bar());
895
742
  cachedLines = lines;
896
743
  cachedWidth = width;
@@ -908,18 +755,175 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
908
755
  };
909
756
  });
910
757
  }
911
- // ─── Right-align Helper ───────────────────────────────────────────────────────
912
- /** Right-align helper: build a line with left content and right content. */
913
- function rightAlign(left, right, width) {
914
- const leftVis = visibleWidth(left);
915
- const rightVis = visibleWidth(right);
916
- const gap = Math.max(1, width - leftVis - rightVis);
917
- return truncateToWidth(left + " ".repeat(gap) + right, width, "…");
918
- }
919
- /** Pad a string with trailing spaces to fill exactly `colWidth` (ANSI-aware). */
920
- function padToWidth(s, colWidth) {
921
- const vis = visibleWidth(s);
922
- if (vis >= colWidth)
923
- return truncateToWidth(s, colWidth, "…");
924
- return s + " ".repeat(colWidth - vis);
758
+ export function setCompletionProgressWidget(ctx, snapshot) {
759
+ if (!ctx.hasUI)
760
+ return;
761
+ ctx.ui.setWidget("gsd-outcome", undefined);
762
+ if (typeof ctx.ui?.setHeader === "function") {
763
+ ctx.ui.setHeader(() => ({
764
+ render() { return []; },
765
+ invalidate() { },
766
+ }));
767
+ }
768
+ if (typeof ctx.ui?.setStatus === "function") {
769
+ ctx.ui.setStatus("gsd-step", undefined);
770
+ }
771
+ ctx.ui.setWidget("gsd-progress", (_tui, theme) => ({
772
+ render(width) {
773
+ const ui = makeUI(theme, width);
774
+ const pad = INDENT.base;
775
+ const lines = [];
776
+ const contentWidth = Math.max(20, width - visibleWidth(pad));
777
+ const add = (line = "") => {
778
+ lines.push(line ? truncateToWidth(`${pad}${line}`, width, "…") : "");
779
+ };
780
+ const addSection = (label, value, indent = "") => {
781
+ const clean = normalizeRollupText(value);
782
+ if (!clean)
783
+ return;
784
+ add(`${indent}${theme.fg("accent", label)} ${theme.fg("text", truncateToWidth(clean, contentWidth - indent.length - label.length - 1, "…"))}`);
785
+ };
786
+ const addList = (label, values, limit, indent = "") => {
787
+ const clean = (values ?? []).map(normalizeRollupText).filter((v) => !!v);
788
+ if (clean.length === 0)
789
+ return;
790
+ const shown = clean.slice(0, limit);
791
+ const more = clean.length > shown.length ? ` (+${clean.length - shown.length} more)` : "";
792
+ add(`${indent}${theme.fg("accent", label)} ${theme.fg("text", truncateToWidth(shown.join("; ") + more, contentWidth - indent.length - label.length - 1, "…"))}`);
793
+ };
794
+ lines.push(...ui.bar());
795
+ const elapsed = formatAutoElapsed(snapshot.startedAt);
796
+ const heading = snapshot.allMilestonesComplete
797
+ ? "All milestones complete"
798
+ : snapshot.milestoneId
799
+ ? `Milestone ${snapshot.milestoneId} roll-up`
800
+ : "Milestone roll-up";
801
+ lines.push(rightAlign(`${pad}${theme.fg("accent", theme.bold(heading))}`, elapsed ? theme.fg("dim", elapsed) : "", width));
802
+ if (snapshot.milestoneTitle) {
803
+ add(theme.fg("text", snapshot.milestoneTitle));
804
+ }
805
+ lines.push("");
806
+ add(theme.fg("accent", "Outcome"));
807
+ addSection("", snapshot.oneLiner, " ");
808
+ const changed = [
809
+ ...(snapshot.successCriteriaResults ? [snapshot.successCriteriaResults] : []),
810
+ ...(snapshot.requirementOutcomes ? [snapshot.requirementOutcomes] : []),
811
+ ...(snapshot.keyDecisions ?? []),
812
+ ].map(normalizeRollupText).filter((v) => !!v).slice(0, 4);
813
+ if (changed.length > 0) {
814
+ lines.push("");
815
+ add(theme.fg("accent", "What changed"));
816
+ for (const item of changed)
817
+ add(` - ${theme.fg("text", item)}`);
818
+ }
819
+ const verification = [
820
+ snapshot.definitionOfDoneResults,
821
+ snapshot.deviations ? `Deviations: ${snapshot.deviations}` : null,
822
+ snapshot.followUps ? `Follow-ups: ${snapshot.followUps}` : null,
823
+ ].map(normalizeRollupText).filter((v) => !!v);
824
+ if (verification.length > 0 || (snapshot.keyFiles?.length ?? 0) > 0) {
825
+ lines.push("");
826
+ add(theme.fg("accent", "Verification"));
827
+ for (const item of verification.slice(0, 3))
828
+ add(` - ${theme.fg("text", item)}`);
829
+ addList("Files:", snapshot.keyFiles, 4, " ");
830
+ }
831
+ if ((snapshot.lessonsLearned?.length ?? 0) > 0) {
832
+ lines.push("");
833
+ addList("Lessons:", snapshot.lessonsLearned, 2);
834
+ }
835
+ const hasSliceTotals = typeof snapshot.completedSlices === "number" && typeof snapshot.totalSlices === "number" && snapshot.totalSlices > 0;
836
+ lines.push("");
837
+ const stats = [];
838
+ if (hasSliceTotals)
839
+ stats.push(theme.fg("success", `${snapshot.completedSlices}/${snapshot.totalSlices} slices`));
840
+ if (snapshot.unitCount > 0)
841
+ stats.push(theme.fg("dim", `${snapshot.unitCount} units`));
842
+ if (snapshot.totalTokens > 0)
843
+ stats.push(theme.fg("dim", `${formatWidgetTokens(snapshot.totalTokens)} tokens`));
844
+ if (snapshot.totalCost > 0)
845
+ stats.push(theme.fg("warning", `$${snapshot.totalCost.toFixed(2)}`));
846
+ if (typeof snapshot.cacheHitRate === "number") {
847
+ const hitColor = snapshot.cacheHitRate >= 70 ? "success" : snapshot.cacheHitRate >= 40 ? "warning" : "error";
848
+ stats.push(theme.fg(hitColor, `${Math.round(snapshot.cacheHitRate)}% cache hit`));
849
+ }
850
+ if (stats.length > 0) {
851
+ add(`${theme.fg("accent", "Run totals")} ${stats.join(theme.fg("dim", " · "))}`);
852
+ }
853
+ lines.push("");
854
+ const nextAction = snapshot.allMilestonesComplete
855
+ ? "Review the roll-up, then start a new milestone when ready."
856
+ : "Review the roll-up, inspect status, or continue to the next milestone.";
857
+ const commands = snapshot.allMilestonesComplete
858
+ ? ["/gsd status for overview", "/gsd visualize to inspect", "/gsd notifications for history", "/gsd start for new work"]
859
+ : ["/gsd status for overview", "/gsd visualize to inspect", "/gsd notifications for history", "/gsd auto for next milestone"];
860
+ add(`${theme.fg("success", "Next")} ${theme.fg("text", nextAction)}`);
861
+ add(theme.fg("dim", commands.join(" · ")));
862
+ const location = snapshot.basePath ? theme.fg("dim", snapshot.basePath) : "";
863
+ const reason = theme.fg("dim", snapshot.reason);
864
+ lines.push(rightAlign(`${pad}${truncateToWidth(location, Math.max(0, width - 32), "…")}`, reason, width));
865
+ lines.push(...ui.bar());
866
+ return lines;
867
+ },
868
+ invalidate() { },
869
+ dispose() { },
870
+ }));
871
+ }
872
+ export function setAutoOutcomeWidget(ctx, snapshot) {
873
+ if (!ctx.hasUI)
874
+ return;
875
+ ctx.ui.setWidget("gsd-outcome", (_tui, theme) => ({
876
+ render(width) {
877
+ const color = snapshot.status === "failed" || snapshot.status === "blocked"
878
+ ? "warning"
879
+ : snapshot.status === "complete"
880
+ ? "success"
881
+ : "borderAccent";
882
+ const icon = snapshot.status === "complete" ? "✓"
883
+ : snapshot.status === "failed" ? "x"
884
+ : snapshot.status === "blocked" ? "!"
885
+ : snapshot.status === "paused" ? "||"
886
+ : "●";
887
+ const innerWidth = Math.max(8, width - 4);
888
+ const maxLines = 7;
889
+ const lines = [];
890
+ const elapsed = snapshot.startedAt ? formatAutoElapsed(snapshot.startedAt) : "";
891
+ const heading = `${theme.fg(color, icon)} ${theme.fg("accent", theme.bold("GSD"))} ${theme.fg("text", snapshot.title)}`;
892
+ lines.push(rightAlign(heading, elapsed ? theme.fg("dim", elapsed) : "", innerWidth));
893
+ const commands = snapshot.commands?.filter(Boolean) ?? [];
894
+ const commandLine = commands.length > 0 ? theme.fg("dim", commands.join(" · ")) : null;
895
+ const addWrapped = (text, prefix = "") => {
896
+ const reserve = commandLine ? 1 : 0;
897
+ const remaining = Math.max(0, maxLines - reserve - lines.length);
898
+ if (remaining === 0)
899
+ return;
900
+ const available = Math.max(8, innerWidth - visibleWidth(prefix));
901
+ for (const [idx, line] of wrapVisibleText(text, available).slice(0, remaining).entries()) {
902
+ lines.push(`${idx === 0 ? prefix : " ".repeat(visibleWidth(prefix))}${line}`);
903
+ }
904
+ };
905
+ if (snapshot.detail) {
906
+ addWrapped(snapshot.detail, `${theme.fg("dim", "Reason")} `);
907
+ }
908
+ if (snapshot.unitLabel) {
909
+ addWrapped(snapshot.unitLabel, `${theme.fg("dim", "Last")} `);
910
+ }
911
+ addWrapped(snapshot.nextAction, `${theme.fg("success", "Next")} `);
912
+ if (commandLine && lines.length < maxLines) {
913
+ lines.push(commandLine);
914
+ }
915
+ return renderFrame(theme, lines, width, { borderColor: color, paddingX: 1 });
916
+ },
917
+ invalidate() { },
918
+ dispose() { },
919
+ }));
920
+ }
921
+ function normalizeRollupText(value) {
922
+ const clean = value
923
+ ?.replace(/\s+/g, " ")
924
+ .replace(/^[-*]\s+/, "")
925
+ .trim();
926
+ if (!clean || clean === "(none)" || clean === "None." || clean === "Not provided.")
927
+ return null;
928
+ return clean;
925
929
  }
@@ -1,13 +1,5 @@
1
- /**
2
- * Auto-mode Dispatch Table declarative phase → unit mapping.
3
- *
4
- * Each rule maps a GSD state to the unit type, unit ID, and prompt builder
5
- * that should be dispatched. Rules are evaluated in order; the first match wins.
6
- *
7
- * This replaces the 130-line if-else chain in dispatchNextUnit with a
8
- * data structure that is inspectable, testable per-rule, and extensible
9
- * without modifying orchestration code.
10
- */
1
+ // Project/App: GSD-2
2
+ // File Purpose: Declarative auto-mode dispatch rules and dispatch resolver.
11
3
  import { loadFile, extractUatType, loadActiveOverrides } from "./files.js";
12
4
  import { isDbAvailable, getMilestoneSlices, getPendingGates, markAllGatesOmitted, getMilestone, insertAssessment, transaction } from "./gsd-db.js";
13
5
  import { isClosedStatus } from "./status-guards.js";
@@ -1191,9 +1183,20 @@ export const DISPATCH_RULES = [
1191
1183
  },
1192
1184
  {
1193
1185
  name: "complete → stop",
1194
- match: async ({ state }) => {
1186
+ match: async ({ state, mid, midTitle, basePath }) => {
1195
1187
  if (state.phase !== "complete")
1196
1188
  return null;
1189
+ if (mid && isDbAvailable()) {
1190
+ const milestone = getMilestone(mid);
1191
+ if (milestone && !isClosedStatus(milestone.status)) {
1192
+ return {
1193
+ action: "dispatch",
1194
+ unitType: "complete-milestone",
1195
+ unitId: mid,
1196
+ prompt: await buildCompleteMilestonePrompt(mid, midTitle, basePath),
1197
+ };
1198
+ }
1199
+ }
1197
1200
  return {
1198
1201
  action: "stop",
1199
1202
  reason: "All milestones complete.",
@@ -55,6 +55,7 @@ import { detectAbandonMilestone } from "./abandon-detect.js";
55
55
  import { isDeterministicPolicyError } from "./auto-tool-tracking.js";
56
56
  import { clearProjectResearchInflightMarker, finalizeProjectResearchTimeout, } from "./project-research-policy.js";
57
57
  import { validateArtifact } from "./schemas/validate.js";
58
+ import { verificationRetryKey } from "./auto/verification-retry-policy.js";
58
59
  // ─── Path Comparison Helper ───────────────────────────────────────────────
59
60
  /** Compare two paths for physical identity, tolerating trailing slashes and symlinks. */
60
61
  function isSamePathLocal(a, b) {
@@ -865,6 +866,7 @@ export async function postUnitPreVerification(pctx, opts) {
865
866
  const outcome = finalizeProjectResearchTimeout(s.basePath, "Project research unit ended before all required dimensions produced durable files.");
866
867
  s.pendingVerificationRetry = null;
867
868
  s.verificationRetryCount.delete(retryKey);
869
+ s.verificationRetryFailureHashes.delete(retryKey);
868
870
  triggerArtifactVerified = verifyExpectedArtifact(s.currentUnit.type, s.currentUnit.id, s.basePath);
869
871
  if (triggerArtifactVerified) {
870
872
  invalidateAllCaches();
@@ -915,6 +917,7 @@ export async function postUnitPreVerification(pctx, opts) {
915
917
  s.lastToolInvocationError = null;
916
918
  s.pendingVerificationRetry = null;
917
919
  s.verificationRetryCount.delete(retryKey);
920
+ s.verificationRetryFailureHashes.delete(retryKey);
918
921
  writeBlockerPlaceholder(s.currentUnit.type, s.currentUnit.id, s.basePath, reason);
919
922
  ctx.ui.notify(`${s.currentUnit.type} ${s.currentUnit.id} — deterministic policy rejection, wrote blocker placeholder (no retries) (#4973)`, "warning");
920
923
  // Fall through to "continue" — do NOT enter the retry or db-unavailable paths.
@@ -943,6 +946,7 @@ export async function postUnitPreVerification(pctx, opts) {
943
946
  const failureDetails = describeArtifactVerificationFailure(s.currentUnit.type, s.currentUnit.id, s.basePath);
944
947
  if (attempt > MAX_ARTIFACT_VERIFICATION_RETRIES) {
945
948
  s.verificationRetryCount.delete(retryKey);
949
+ s.verificationRetryFailureHashes.delete(retryKey);
946
950
  debugLog("postUnit", { phase: "artifact-verify-exhausted", unitType: s.currentUnit.type, unitId: s.currentUnit.id, attempt });
947
951
  ctx.ui.notify(`${failureDetails} Pausing auto-mode after ${MAX_ARTIFACT_VERIFICATION_RETRIES} retries.`, "error");
948
952
  await pauseAuto(ctx, pi);
@@ -962,7 +966,9 @@ export async function postUnitPreVerification(pctx, opts) {
962
966
  // Verification succeeded — clear the retry counter so a future failure
963
967
  // of the same unit gets a full retry budget instead of the stale count.
964
968
  if (triggerArtifactVerified) {
965
- s.verificationRetryCount.delete(`${s.currentUnit.type}:${s.currentUnit.id}`);
969
+ const retryKey = verificationRetryKey(s.currentUnit.type, s.currentUnit.id);
970
+ s.verificationRetryCount.delete(retryKey);
971
+ s.verificationRetryFailureHashes.delete(retryKey);
966
972
  }
967
973
  }
968
974
  else {