gsd-pi 2.80.0-dev.c5f2443b3 → 2.80.0-dev.f55d16d13

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 (303) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/GSD-WORKFLOW.md +2 -2
  3. package/dist/resources/extensions/github-sync/templates.js +39 -8
  4. package/dist/resources/extensions/gsd/auto/loop.js +16 -9
  5. package/dist/resources/extensions/gsd/auto/phases.js +37 -30
  6. package/dist/resources/extensions/gsd/auto/run-unit.js +19 -15
  7. package/dist/resources/extensions/gsd/auto-dashboard.js +51 -15
  8. package/dist/resources/extensions/gsd/auto-dispatch.js +10 -0
  9. package/dist/resources/extensions/gsd/auto-post-unit.js +10 -10
  10. package/dist/resources/extensions/gsd/auto-prompts.js +111 -1
  11. package/dist/resources/extensions/gsd/auto-recovery.js +154 -8
  12. package/dist/resources/extensions/gsd/auto-start.js +2 -3
  13. package/dist/resources/extensions/gsd/auto.js +9 -1
  14. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +15 -1
  15. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -1
  16. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
  17. package/dist/resources/extensions/gsd/clean-root-preflight.js +42 -4
  18. package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
  19. package/dist/resources/extensions/gsd/custom-workflow-engine.js +22 -2
  20. package/dist/resources/extensions/gsd/db-base-schema.js +14 -0
  21. package/dist/resources/extensions/gsd/db-migration-steps.js +16 -0
  22. package/dist/resources/extensions/gsd/detection.js +106 -0
  23. package/dist/resources/extensions/gsd/graph.js +9 -3
  24. package/dist/resources/extensions/gsd/gsd-db.js +102 -2
  25. package/dist/resources/extensions/gsd/guided-flow.js +2 -2
  26. package/dist/resources/extensions/gsd/pr-evidence.js +57 -16
  27. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
  28. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  29. package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
  30. package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
  31. package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
  32. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  33. package/dist/web/standalone/.next/BUILD_ID +1 -1
  34. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  35. package/dist/web/standalone/.next/build-manifest.json +3 -3
  36. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  37. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  38. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  39. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  47. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  55. package/dist/web/standalone/.next/server/app/index.html +1 -1
  56. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  63. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  64. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  65. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  67. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  68. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  69. package/dist/web/standalone/.next/static/chunks/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
  70. package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
  71. package/package.json +10 -6
  72. package/packages/contracts/package.json +1 -1
  73. package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
  74. package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
  75. package/packages/pi-ai/dist/models/fake-model.js +27 -0
  76. package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
  77. package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
  78. package/packages/pi-ai/dist/models/index.js +8 -0
  79. package/packages/pi-ai/dist/models/index.js.map +1 -1
  80. package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
  81. package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
  82. package/packages/pi-ai/dist/providers/fake.js +319 -0
  83. package/packages/pi-ai/dist/providers/fake.js.map +1 -0
  84. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  85. package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
  86. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  87. package/packages/pi-ai/src/models/fake-model.ts +30 -0
  88. package/packages/pi-ai/src/models/index.ts +9 -0
  89. package/packages/pi-ai/src/providers/fake.ts +376 -0
  90. package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
  91. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  92. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +74 -0
  93. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  94. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
  95. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  96. package/packages/pi-coding-agent/dist/core/extensions/runner.js +14 -1
  97. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  98. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +97 -0
  99. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  100. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  101. package/packages/pi-coding-agent/dist/core/model-registry.js +5 -0
  102. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  103. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +4 -0
  104. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  105. package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
  106. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  107. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  108. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  109. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  110. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
  111. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
  112. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +54 -15
  113. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
  117. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
  118. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
  119. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
  120. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
  121. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
  122. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
  130. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
  132. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  133. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +16 -0
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +106 -17
  136. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  137. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +60 -1
  139. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +40 -1
  141. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
  143. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  144. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  145. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
  148. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  149. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +23 -0
  150. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  151. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  152. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
  153. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
  155. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
  156. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
  157. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
  158. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  159. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  160. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
  161. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  162. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  163. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
  165. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  167. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
  168. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  169. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
  170. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
  171. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
  172. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
  173. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
  174. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
  175. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
  176. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
  177. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +87 -0
  178. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +108 -0
  179. package/packages/pi-coding-agent/src/core/extensions/runner.ts +16 -1
  180. package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
  181. package/packages/pi-coding-agent/src/core/settings-manager.ts +12 -0
  182. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  183. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
  184. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +78 -15
  185. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
  186. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
  187. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
  188. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
  189. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  190. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
  191. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +118 -17
  192. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +43 -1
  193. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +75 -1
  194. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
  195. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
  196. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +25 -0
  197. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
  198. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
  199. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
  200. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
  201. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
  202. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
  203. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
  204. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  205. package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
  206. package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
  207. package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
  208. package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
  209. package/packages/pi-tui/dist/__tests__/tui.test.js +24 -3
  210. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  211. package/packages/pi-tui/dist/index.d.ts +1 -0
  212. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  213. package/packages/pi-tui/dist/index.js +2 -0
  214. package/packages/pi-tui/dist/index.js.map +1 -1
  215. package/packages/pi-tui/dist/style.d.ts +41 -0
  216. package/packages/pi-tui/dist/style.d.ts.map +1 -0
  217. package/packages/pi-tui/dist/style.js +158 -0
  218. package/packages/pi-tui/dist/style.js.map +1 -0
  219. package/packages/pi-tui/dist/tui.d.ts +0 -1
  220. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  221. package/packages/pi-tui/dist/tui.js +3 -8
  222. package/packages/pi-tui/dist/tui.js.map +1 -1
  223. package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
  224. package/packages/pi-tui/src/__tests__/tui.test.ts +29 -3
  225. package/packages/pi-tui/src/index.ts +9 -0
  226. package/packages/pi-tui/src/style.ts +225 -0
  227. package/packages/pi-tui/src/tui.ts +3 -8
  228. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  229. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  230. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  231. package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
  232. package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  233. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  234. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  235. package/pkg/dist/modes/interactive/theme/theme.js +18 -1
  236. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  237. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  238. package/pkg/dist/modes/interactive/theme/themes.js +36 -27
  239. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  240. package/src/resources/GSD-WORKFLOW.md +2 -2
  241. package/src/resources/extensions/github-sync/templates.ts +38 -8
  242. package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
  243. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  244. package/src/resources/extensions/gsd/auto/loop.ts +17 -10
  245. package/src/resources/extensions/gsd/auto/phases.ts +42 -28
  246. package/src/resources/extensions/gsd/auto/run-unit.ts +24 -14
  247. package/src/resources/extensions/gsd/auto-dashboard.ts +57 -8
  248. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
  249. package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
  250. package/src/resources/extensions/gsd/auto-prompts.ts +116 -1
  251. package/src/resources/extensions/gsd/auto-recovery.ts +153 -7
  252. package/src/resources/extensions/gsd/auto-start.ts +7 -6
  253. package/src/resources/extensions/gsd/auto.ts +12 -1
  254. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -1
  255. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +17 -1
  256. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
  257. package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
  258. package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
  259. package/src/resources/extensions/gsd/custom-workflow-engine.ts +24 -1
  260. package/src/resources/extensions/gsd/db-base-schema.ts +15 -0
  261. package/src/resources/extensions/gsd/db-migration-steps.ts +17 -0
  262. package/src/resources/extensions/gsd/detection.ts +128 -0
  263. package/src/resources/extensions/gsd/graph.ts +12 -5
  264. package/src/resources/extensions/gsd/gsd-db.ts +119 -1
  265. package/src/resources/extensions/gsd/guided-flow.ts +2 -2
  266. package/src/resources/extensions/gsd/pr-evidence.ts +63 -5
  267. package/src/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
  268. package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  269. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
  270. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +7 -1
  271. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +33 -0
  272. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +84 -5
  273. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +170 -1
  274. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
  275. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
  276. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +112 -6
  277. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +40 -2
  278. package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
  279. package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
  280. package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
  281. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
  282. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
  283. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
  284. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
  285. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +10 -0
  286. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
  287. package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
  288. package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
  289. package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
  290. package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
  291. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
  292. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +46 -2
  293. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  294. package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
  295. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
  296. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
  297. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
  298. package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
  299. package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
  300. package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
  301. package/packages/contracts/tsconfig.tsbuildinfo +0 -1
  302. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → mPZbi5BH9dwokaPZlrYuQ}/_buildManifest.js +0 -0
  303. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → mPZbi5BH9dwokaPZlrYuQ}/_ssgManifest.js +0 -0
@@ -0,0 +1,160 @@
1
+ // GSD2 - Adaptive terminal mode dashboard for the interactive TUI
2
+
3
+ import { style, truncateToWidth, visibleWidth, type Component } from "@gsd/pi-tui";
4
+ import type { TuiAdaptiveMode, TuiMode } from "../tui-mode.js";
5
+ import { resolveTuiMode } from "../tui-mode.js";
6
+ import { theme, type ThemeColor } from "../theme/theme.js";
7
+
8
+ export interface AdaptiveLayoutState {
9
+ override: TuiAdaptiveMode;
10
+ activeToolCount: number;
11
+ gsdPhase?: string;
12
+ lastError?: string;
13
+ sessionName?: string;
14
+ cwd: string;
15
+ }
16
+
17
+ export class AdaptiveLayoutComponent implements Component {
18
+ constructor(private readonly getState: () => AdaptiveLayoutState) {}
19
+
20
+ invalidate(): void {}
21
+
22
+ render(width: number): string[] {
23
+ const state = this.getState();
24
+ const mode = resolveTuiMode({
25
+ terminalWidth: width,
26
+ override: state.override,
27
+ activeToolCount: state.activeToolCount,
28
+ gsdPhase: state.gsdPhase,
29
+ hasBlockingError: !!state.lastError,
30
+ });
31
+
32
+ if (state.override === "auto" && mode === "chat" && !state.gsdPhase && !state.lastError && state.activeToolCount === 0) {
33
+ return [];
34
+ }
35
+
36
+ if (mode === "compact" || width < 72) return this.renderCompact(width, mode, state);
37
+ if (mode === "debug") return this.renderDebug(width, state);
38
+ if (mode === "validation") return this.renderValidation(width, state);
39
+ if (mode === "workflow") return this.renderWorkflow(width, state);
40
+ return this.renderChat(width, state);
41
+ }
42
+
43
+ private renderWorkflow(width: number, state: AdaptiveLayoutState): string[] {
44
+ if (width < 112) return this.renderCompact(width, "workflow", state);
45
+
46
+ const leftWidth = Math.max(44, Math.floor(width * 0.56));
47
+ const rightWidth = Math.max(32, width - leftWidth - 2);
48
+ const phase = state.gsdPhase ?? "Ready";
49
+ const left = this.frame(
50
+ [
51
+ this.metric("Active", phase, "modeWorkflow"),
52
+ this.metric("Tools", state.activeToolCount > 0 ? `${state.activeToolCount} running` : "idle", "toolRunning"),
53
+ this.metric("Mode", state.override === "auto" ? "auto workflow" : state.override, "modeWorkflow"),
54
+ ],
55
+ leftWidth,
56
+ "GSD Command Center",
57
+ "workflow",
58
+ "modeWorkflow",
59
+ );
60
+ const right = this.frame(
61
+ [
62
+ `Session ${state.sessionName ?? "current"}`,
63
+ `Path ${this.basename(state.cwd)}`,
64
+ `Next ${state.activeToolCount > 0 ? "watch tool output" : "continue from prompt"}`,
65
+ ],
66
+ rightWidth,
67
+ "signals",
68
+ "inspector",
69
+ "surfaceAccent",
70
+ );
71
+ return this.columns(left, right, leftWidth);
72
+ }
73
+
74
+ private renderValidation(width: number, state: AdaptiveLayoutState): string[] {
75
+ const phase = state.gsdPhase ?? "Validation pending";
76
+ return this.frame(
77
+ [
78
+ this.metric("Focus", phase, "modeValidation"),
79
+ this.metric("Checks", state.activeToolCount > 0 ? `${state.activeToolCount} active` : "waiting", "toolRunning"),
80
+ this.metric("Timeline", state.lastError ? "blocked" : "ready for completion evidence", state.lastError ? "toolError" : "toolSuccess"),
81
+ ],
82
+ width,
83
+ "validation",
84
+ state.override === "auto" ? "auto" : state.override,
85
+ "modeValidation",
86
+ );
87
+ }
88
+
89
+ private renderDebug(width: number, state: AdaptiveLayoutState): string[] {
90
+ const error = state.lastError ?? "Blocking error detected";
91
+ return this.frame(
92
+ [
93
+ this.metric("Failure", truncateToWidth(error, Math.max(20, width - 20), ""), "toolError"),
94
+ this.metric("Tools", state.activeToolCount > 0 ? `${state.activeToolCount} still running` : "none running", "toolRunning"),
95
+ this.metric("Next", "inspect the failed output, then retry the smallest step", "modeDebug"),
96
+ ],
97
+ width,
98
+ "blocking failure",
99
+ "debug",
100
+ "modeDebug",
101
+ );
102
+ }
103
+
104
+ private renderChat(width: number, state: AdaptiveLayoutState): string[] {
105
+ return this.frame(
106
+ [
107
+ this.metric("Mode", state.override === "auto" ? "auto chat" : state.override, "surfaceAccent"),
108
+ this.metric("Tools", state.activeToolCount > 0 ? `${state.activeToolCount} active` : "compact rows", "toolMuted"),
109
+ ],
110
+ width,
111
+ "chat",
112
+ state.sessionName ?? "conversation",
113
+ "surfaceAccent",
114
+ );
115
+ }
116
+
117
+ private renderCompact(width: number, mode: TuiMode, state: AdaptiveLayoutState): string[] {
118
+ const phase = state.lastError ?? state.gsdPhase ?? (state.activeToolCount > 0 ? `${state.activeToolCount} tools` : "ready");
119
+ const line = `${theme.fg("modeCompact", "GSD compact")} ${theme.fg("surfaceMuted", `${mode} · ${phase}`)}`;
120
+ return style()
121
+ .border("minimal")
122
+ .borderColor((text) => theme.fg("surfaceBorder", text))
123
+ .bodyGutter(" ")
124
+ .render([line], width);
125
+ }
126
+
127
+ private frame(lines: string[], width: number, title: string, rightTitle: string, accent: ThemeColor): string[] {
128
+ return style()
129
+ .border("rule")
130
+ .density("compact")
131
+ .toneColor((text) => theme.fg("surfaceMuted", text))
132
+ .borderColor((text) => theme.fg("surfaceBorder", text))
133
+ .title(theme.fg("surfaceTitle", title))
134
+ .rightTitle(theme.fg(accent, rightTitle))
135
+ .bodyGutter(theme.fg(accent, "│ "))
136
+ .render(lines, width);
137
+ }
138
+
139
+ private metric(label: string, value: string, color: ThemeColor): string {
140
+ return `${theme.fg("surfaceMuted", `${label.padEnd(8)} `)}${theme.fg(color, value)}`;
141
+ }
142
+
143
+ private columns(left: string[], right: string[], leftWidth: number): string[] {
144
+ const rows = Math.max(left.length, right.length);
145
+ const output: string[] = [];
146
+ for (let i = 0; i < rows; i++) {
147
+ const leftLine = left[i] ?? "";
148
+ const rightLine = right[i] ?? "";
149
+ const gap = " ".repeat(Math.max(2, leftWidth - visibleWidth(leftLine) + 2));
150
+ output.push(`${leftLine}${gap}${rightLine}`);
151
+ }
152
+ return output;
153
+ }
154
+
155
+ private basename(cwd: string): string {
156
+ const trimmed = cwd.replace(/\/+$/, "");
157
+ const slash = trimmed.lastIndexOf("/");
158
+ return slash === -1 ? trimmed : trimmed.slice(slash + 1);
159
+ }
160
+ }
@@ -1,3 +1,4 @@
1
+ // GSD2 TUI - Assistant message card renderer for interactive terminal sessions.
1
2
  import type { AssistantMessage } from "@gsd/pi-ai";
2
3
  import { Container, Markdown, type MarkdownTheme, Spacer, Text } from "@gsd/pi-tui";
3
4
  import { getMarkdownTheme, theme } from "../theme/theme.js";
@@ -1,4 +1,5 @@
1
- import { truncateToWidth, visibleWidth } from "@gsd/pi-tui";
1
+ // GSD2 TUI - Shared chat frame renderer for assistant, user, and system cards.
2
+ import { style, truncateToWidth, visibleWidth } from "@gsd/pi-tui";
2
3
  import { theme } from "../theme/theme.js";
3
4
  import { formatTimestamp, type TimestampFormat } from "./timestamp.js";
4
5
 
@@ -34,7 +35,7 @@ export function renderChatFrame(
34
35
  : "borderAccent";
35
36
  const borderMuted = isPurple ? "customMessageLabel" : "borderMuted";
36
37
  const border = (s: string) => theme.fg(borderColor, s);
37
- const leftRaw = `• ${opts.label}`;
38
+ const leftRaw = opts.label;
38
39
  const rightRaw =
39
40
  opts.showTimestamp === false || !opts.timestamp
40
41
  ? ""
@@ -67,21 +68,21 @@ export function renderChatFrame(
67
68
  const headerRow = `${leftStyled}${" ".repeat(gap)}${rightStyled}`;
68
69
  const headerPad = Math.max(0, outerWidth - visibleWidth(headerRow));
69
70
 
70
- const sourceLines = trimOuterBlankLines(contentLines);
71
71
  const bodyColor =
72
72
  opts.tone === "user"
73
73
  ? "userMessageText"
74
74
  : isPurple
75
75
  ? "customMessageText"
76
76
  : "assistantMessageText";
77
+ const sourceLines = trimOuterBlankLines(contentLines);
77
78
  const bodyLines = (sourceLines.length > 0 ? sourceLines : [""]).map((line) => {
78
79
  const clipped = truncateToWidth(line, contentWidth, "");
79
- return border("│ ") + theme.fg(bodyColor, clipped);
80
+ return theme.fg(bodyColor, clipped);
80
81
  });
81
82
 
82
- return [
83
- theme.fg(borderMuted, "".repeat(outerWidth)),
84
- headerRow + " ".repeat(headerPad),
85
- ...bodyLines,
86
- ];
83
+ return style()
84
+ .border("rule")
85
+ .borderColor((line) => (line.startsWith("─") ? theme.fg(borderMuted, line) : border(line)))
86
+ .title(headerRow + " ".repeat(headerPad))
87
+ .render(bodyLines, outerWidth);
87
88
  }
@@ -1,5 +1,6 @@
1
1
  import type { ThinkingLevel } from "@gsd/pi-agent-core";
2
2
  import type { Transport } from "@gsd/pi-ai";
3
+ import type { AdaptiveTuiMode } from "../../../core/settings-manager.js";
3
4
  import {
4
5
  Container,
5
6
  getCapabilities,
@@ -46,6 +47,7 @@ export interface SettingsConfig {
46
47
  quietStartup: boolean;
47
48
  clearOnShrink: boolean;
48
49
  timestampFormat: "date-time-iso" | "date-time-us";
50
+ adaptiveMode: AdaptiveTuiMode;
49
51
  }
50
52
 
51
53
  export interface SettingsCallbacks {
@@ -71,6 +73,7 @@ export interface SettingsCallbacks {
71
73
  onQuietStartupChange: (enabled: boolean) => void;
72
74
  onClearOnShrinkChange: (enabled: boolean) => void;
73
75
  onTimestampFormatChange: (format: "date-time-iso" | "date-time-us") => void;
76
+ onAdaptiveModeChange: (mode: AdaptiveTuiMode) => void;
74
77
  onCancel: () => void;
75
78
  }
76
79
 
@@ -367,6 +370,15 @@ export class SettingsSelectorComponent extends Container {
367
370
  values: ["date-time-iso", "date-time-us"],
368
371
  });
369
372
 
373
+ const timestampIndex = items.findIndex((item) => item.id === "timestamp-format");
374
+ items.splice(timestampIndex + 1, 0, {
375
+ id: "adaptive-mode",
376
+ label: "TUI adaptive mode",
377
+ description: "Auto-select or force the terminal layout mode",
378
+ currentValue: config.adaptiveMode,
379
+ values: ["auto", "chat", "workflow", "validation", "debug", "compact"],
380
+ });
381
+
370
382
  // Add borders
371
383
  this.addChild(new DynamicBorder());
372
384
 
@@ -435,6 +447,9 @@ export class SettingsSelectorComponent extends Container {
435
447
  case "timestamp-format":
436
448
  callbacks.onTimestampFormatChange(newValue as "date-time-iso" | "date-time-us");
437
449
  break;
450
+ case "adaptive-mode":
451
+ callbacks.onAdaptiveModeChange(newValue as AdaptiveTuiMode);
452
+ break;
438
453
  }
439
454
  },
440
455
  callbacks.onCancel,
@@ -1,3 +1,4 @@
1
+ // GSD2 TUI Tests - Runtime coverage for tool-card cleanup and success notification rendering.
1
2
  // Runtime regression tests for the post-compaction tool-card cleanup and the
2
3
  // green-bordered success-notification rendering. Replaces the source-grep
3
4
  // `src/tests/tui-running-and-success-box.test.ts` that was deleted in #4875
@@ -74,7 +75,7 @@ function makeMockTUI(): MockTui {
74
75
  // ─── Bug 1: tool cards stuck in "Running" after compaction ──────────────
75
76
 
76
77
  describe("ToolExecutionComponent post-compaction cleanup", () => {
77
- it("renders 'Running' status while the tool call has no result", () => {
78
+ it("renders 'running' status while the tool call has no result", () => {
78
79
  // Baseline: a freshly-constructed component (mid-stream) must show
79
80
  // the running badge — this is the state we need to flip OUT of when
80
81
  // compaction removes the result message.
@@ -88,12 +89,12 @@ describe("ToolExecutionComponent post-compaction cleanup", () => {
88
89
  );
89
90
  const rendered = c.render(60).map(stripAnsi).join("\n");
90
91
  assert.ok(
91
- rendered.includes("Running"),
92
- "freshly constructed component should render 'Running' badge",
92
+ rendered.includes("running"),
93
+ "freshly constructed component should render 'running' badge",
93
94
  );
94
95
  });
95
96
 
96
- it("markHistoricalNoResult flips a stuck tool card OUT of 'Running'", () => {
97
+ it("markHistoricalNoResult flips a stuck tool card OUT of 'running'", () => {
97
98
  // Real bug: after session-history replay (post-compaction or session
98
99
  // switch), tool calls without matching tool_result messages stay in
99
100
  // isPartial = true forever. markHistoricalNoResult must produce a
@@ -111,13 +112,13 @@ describe("ToolExecutionComponent post-compaction cleanup", () => {
111
112
 
112
113
  const rendered = c.render(60).map(stripAnsi).join("\n");
113
114
  assert.ok(
114
- !rendered.includes("Running"),
115
- "after markHistoricalNoResult, the tool card must NOT render 'Running' got:\n" +
115
+ !rendered.includes("running"),
116
+ "after markHistoricalNoResult, the tool card must NOT render 'running' -- got:\n" +
116
117
  rendered,
117
118
  );
118
119
  assert.ok(
119
- rendered.includes("Done"),
120
- "flipped card should render 'Done' status (no-result success)",
120
+ rendered.includes("success"),
121
+ "flipped card should render 'success' status (no-result success)",
121
122
  );
122
123
  });
123
124
 
@@ -145,7 +146,7 @@ describe("ToolExecutionComponent post-compaction cleanup", () => {
145
146
  );
146
147
 
147
148
  const before = c.render(60).map(stripAnsi).join("\n");
148
- assert.ok(before.includes("Done"), "after complete(), card shows 'Done'");
149
+ assert.ok(before.includes("success"), "after complete(), card shows 'success'");
149
150
 
150
151
  c.markHistoricalNoResult(); // must early-return
151
152
  const after = c.render(60).map(stripAnsi).join("\n");
@@ -1,3 +1,4 @@
1
+ // GSD-2 Interactive Tool Execution Rendering
1
2
  import {
2
3
  Box,
3
4
  Container,
@@ -6,6 +7,7 @@ import {
6
7
  type ImageDimensions,
7
8
  imageFallback,
8
9
  Spacer,
10
+ style,
9
11
  Text,
10
12
  type TUI,
11
13
  truncateToWidth,
@@ -103,13 +105,11 @@ function renderToolFrame(
103
105
  const outerWidth = Math.max(20, width);
104
106
  const contentWidth = Math.max(1, outerWidth - 2); // "│ " + content
105
107
 
106
- const borderColor = opts.tone === "error" ? "error" : "toolTitle";
107
- const topColor = opts.tone === "error" ? "error" : "toolTitle";
108
- const labelColor = opts.tone === "error" ? "error" : "toolTitle";
109
- const statusColor = opts.tone === "error" ? "error" : opts.tone === "pending" ? "warning" : "success";
110
- const border = (s: string) => theme.fg(borderColor, s);
111
-
112
- const leftStyled = theme.fg(labelColor, theme.bold(`• ${opts.label}`));
108
+ const borderColor = opts.tone === "error" ? "toolError" : opts.tone === "pending" ? "toolRunning" : "toolSuccess";
109
+ const topColor = borderColor;
110
+ const labelColor = opts.tone === "error" ? "toolError" : opts.tone === "pending" ? "toolRunning" : "toolSuccess";
111
+ const statusColor = opts.tone === "error" ? "toolError" : opts.tone === "pending" ? "toolRunning" : "toolSuccess";
112
+ const leftStyled = theme.fg(labelColor, theme.bold(opts.label));
113
113
  const rightStyled = theme.fg(statusColor, opts.status);
114
114
  const gap = Math.max(1, outerWidth - visibleWidth(leftStyled) - visibleWidth(rightStyled));
115
115
  const headerRow = `${leftStyled}${" ".repeat(gap)}${rightStyled}`;
@@ -118,20 +118,52 @@ function renderToolFrame(
118
118
  const sourceLines = trimOuterBlankLines(contentLines);
119
119
  const bodyLines = (sourceLines.length > 0 ? sourceLines : [""]).map((line) => {
120
120
  const clipped = truncateToWidth(line, contentWidth, "");
121
- return border("│ ") + clipped;
121
+ return clipped;
122
122
  });
123
123
 
124
- return [
125
- theme.fg(topColor, "".repeat(outerWidth)),
126
- headerRow + " ".repeat(headerPad),
127
- ...bodyLines,
128
- ];
124
+ return style()
125
+ .border("rule")
126
+ .borderColor((line) => theme.fg(line.startsWith("") ? topColor : borderColor, line))
127
+ .title(headerRow + " ".repeat(headerPad))
128
+ .render(bodyLines, outerWidth);
129
+ }
130
+
131
+ function renderCollapsedToolRow(
132
+ label: string,
133
+ status: string,
134
+ width: number,
135
+ tone: Extract<ToolFrameTone, "success">,
136
+ ): string[] {
137
+ const outerWidth = Math.max(20, width);
138
+ const contentWidth = Math.max(1, outerWidth - 2);
139
+ const statusColor = tone === "success" ? "toolSuccess" : "toolMuted";
140
+ const labelStyled = theme.fg(statusColor, label);
141
+ const statusStyled = theme.fg(statusColor, status);
142
+ const labelWidth = Math.max(1, contentWidth - visibleWidth(statusStyled) - 1);
143
+ const clippedLabel = truncateToWidth(labelStyled, labelWidth, "");
144
+ const gap = Math.max(1, contentWidth - visibleWidth(clippedLabel) - visibleWidth(statusStyled));
145
+ const line = `${clippedLabel}${" ".repeat(gap)}${statusStyled}`;
146
+ return style()
147
+ .border("minimal")
148
+ .borderColor((text) => theme.fg(statusColor, text))
149
+ .render([line], outerWidth);
129
150
  }
130
151
 
131
152
  const COMPACT_ARG_VALUE_LIMIT = 60;
132
153
  const GENERIC_OUTPUT_PREVIEW_LINES = 10;
133
154
  const GENERIC_ARGS_JSON_PREVIEW_LINES = 10;
134
155
 
156
+ export type ToolExecutionPhase = {
157
+ label: string;
158
+ count: number;
159
+ durationMs: number;
160
+ };
161
+
162
+ function formatElapsed(ms: number): string {
163
+ if (ms < 1000) return `${ms}ms`;
164
+ return `${Math.max(1, Math.round(ms / 1000))}s`;
165
+ }
166
+
135
167
  /**
136
168
  * Format tool args for the generic-renderer fallback. Produces a one-line
137
169
  * `k=v, k=v` summary when every value is a primitive that fits inline; falls
@@ -200,6 +232,8 @@ export class ToolExecutionComponent extends Container {
200
232
  private toolDefinition?: ToolDefinition;
201
233
  private ui: TUI;
202
234
  private cwd: string;
235
+ private readonly startedAt = Date.now();
236
+ private endedAt: number | undefined;
203
237
  private result?: {
204
238
  content: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;
205
239
  isError: boolean;
@@ -434,6 +468,9 @@ export class ToolExecutionComponent extends Container {
434
468
  ): void {
435
469
  this.result = result;
436
470
  this.isPartial = isPartial;
471
+ if (!isPartial) {
472
+ this.endedAt = this.endedAt ?? Date.now();
473
+ }
437
474
  if (this.normalizedToolName === "write" && !isPartial) {
438
475
  const rawPath = str(this.args?.file_path ?? this.args?.path);
439
476
  const fileContent = str(this.args?.content);
@@ -456,6 +493,7 @@ export class ToolExecutionComponent extends Container {
456
493
  markHistoricalNoResult(): void {
457
494
  if (this.result) return; // real result already set, nothing to do
458
495
  this.isPartial = false;
496
+ this.endedAt = this.endedAt ?? Date.now();
459
497
  this.result = {
460
498
  content: [],
461
499
  isError: false,
@@ -468,6 +506,7 @@ export class ToolExecutionComponent extends Container {
468
506
  */
469
507
  completeWithError(message?: string): void {
470
508
  this.isPartial = false;
509
+ this.endedAt = this.endedAt ?? Date.now();
471
510
  if (this.result) {
472
511
  let content = this.result.content;
473
512
  if (message) {
@@ -538,14 +577,18 @@ export class ToolExecutionComponent extends Container {
538
577
  }
539
578
  const frameWidth = Math.max(20, width);
540
579
  const contentWidth = Math.max(1, frameWidth - 4);
541
- const lines = super.render(contentWidth);
542
580
  const frameTone: ToolFrameTone =
543
581
  this.result?.isError ? "error" : this.isPartial || !this.result ? "pending" : "success";
544
- const frameStatus = this.isPartial || !this.result ? "Running" : this.result.isError ? "Error" : "Done";
582
+ const elapsed = formatElapsed((this.endedAt ?? Date.now()) - this.startedAt);
583
+ const frameStatus = `${this.isPartial || !this.result ? "running" : this.result.isError ? "failed" : "success"} · ${elapsed}`;
545
584
  const parsed = parseMcpToolName(this.toolName);
546
585
  const frameLabel = parsed
547
- ? `Tool ${parsed.server}·${parsed.tool}`
548
- : `Tool ${prettifyToolName(this.toolName, this.toolDefinition?.label) || "unknown"}`;
586
+ ? `${parsed.server}·${parsed.tool}`
587
+ : prettifyToolName(this.toolName, this.toolDefinition?.label) || "unknown";
588
+ if (this.shouldRenderCompactSuccess()) {
589
+ return ["", ...renderCollapsedToolRow(this.getCompactSummary(frameLabel), frameStatus, frameWidth, "success")];
590
+ }
591
+ const lines = super.render(contentWidth);
549
592
  const framed = renderToolFrame(lines, frameWidth, {
550
593
  label: frameLabel,
551
594
  status: frameStatus,
@@ -554,6 +597,43 @@ export class ToolExecutionComponent extends Container {
554
597
  return framed.length > 0 ? ["", ...framed] : framed;
555
598
  }
556
599
 
600
+ private shouldRenderCompactSuccess(): boolean {
601
+ if (this.expanded || this.isPartial || !this.result || this.result.isError) return false;
602
+ const hasImages = this.result.content?.some((block) => block.type === "image") ?? false;
603
+ return !hasImages;
604
+ }
605
+
606
+ getRollupPhase(): ToolExecutionPhase | null {
607
+ if (!this.shouldRenderCompactSuccess()) return null;
608
+ const label = this.getPhaseLabel();
609
+ const endedAt = this.endedAt ?? Date.now();
610
+ return {
611
+ label,
612
+ count: 1,
613
+ durationMs: Math.max(0, endedAt - this.startedAt),
614
+ };
615
+ }
616
+
617
+ private getPhaseLabel(): string {
618
+ const name = this.normalizedToolName;
619
+ const displayName = prettifyToolName(this.toolName, this.toolDefinition?.label);
620
+
621
+ if (name === "bash") return "Setup / shell";
622
+ if (name === "read" || name === "ls" || name === "find" || name === "grep") return "Context reads";
623
+ if (name === "write" || name === "edit") return "File changes";
624
+ if (name === "web_search" || displayName === "ToolSearch") return "Discovery";
625
+ if (displayName === "Memory Query" || displayName === "Memory Capture" || displayName === "Gsd Graph") {
626
+ return "Memory lookups";
627
+ }
628
+ if (displayName === "Update Requirement" || displayName === "Save Requirement") return "Requirement writes";
629
+ if (displayName.startsWith("Complete ")) return "Finalization";
630
+ return "Other tool actions";
631
+ }
632
+
633
+ private getCompactSummary(frameLabel: string): string {
634
+ return frameLabel;
635
+ }
636
+
557
637
  private updateDisplay(): void {
558
638
  // Tool body now uses transparent background; status is conveyed in the frame header.
559
639
  const bgFn = (text: string) => text;
@@ -1201,3 +1281,24 @@ export class ToolExecutionComponent extends Container {
1201
1281
  return text;
1202
1282
  }
1203
1283
  }
1284
+
1285
+ export class ToolPhaseSummaryComponent extends Container {
1286
+ constructor(private readonly phases: ToolExecutionPhase[]) {
1287
+ super();
1288
+ }
1289
+
1290
+ override render(width: number): string[] {
1291
+ const frameWidth = Math.max(20, width);
1292
+ const rows = this.phases.map((phase) => {
1293
+ const left = `${phase.label} ${phase.count} ${phase.count === 1 ? "action" : "actions"}`;
1294
+ const right = `success · ${formatElapsed(phase.durationMs)}`;
1295
+ const contentWidth = Math.max(1, frameWidth - 2);
1296
+ const leftWidth = Math.max(1, contentWidth - visibleWidth(right) - 1);
1297
+ const leftText = truncateToWidth(left, leftWidth, "");
1298
+ const gap = Math.max(1, contentWidth - visibleWidth(leftText) - visibleWidth(right));
1299
+ return `${theme.fg("toolSuccess", leftText)}${" ".repeat(gap)}${theme.fg("toolSuccess", right)}`;
1300
+ });
1301
+
1302
+ return ["", ...style().border("minimal").borderColor((text) => theme.fg("toolSuccess", text)).render(rows, frameWidth)];
1303
+ }
1304
+ }
@@ -1,7 +1,8 @@
1
1
  import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
3
 
4
- import { findLatestPinnableText } from "./chat-controller.js";
4
+ import { findLatestPinnableText, handleAgentEvent } from "./chat-controller.js";
5
+ import { initTheme } from "../theme/theme.js";
5
6
 
6
7
  test("findLatestPinnableText: empty content returns empty string", () => {
7
8
  assert.equal(findLatestPinnableText([]), "");
@@ -69,3 +70,44 @@ test("findLatestPinnableText: thinking blocks are not pinnable", () => {
69
70
  ];
70
71
  assert.equal(findLatestPinnableText(blocks), "");
71
72
  });
73
+
74
+ test("handleAgentEvent: agent_start clears stale adaptive blocking error", async () => {
75
+ initTheme("dark", false);
76
+ let cleared = false;
77
+ let requestedRender = false;
78
+ const host = {
79
+ isInitialized: true,
80
+ clearBlockingError: () => {
81
+ cleared = true;
82
+ },
83
+ retryEscapeHandler: undefined,
84
+ retryLoader: undefined,
85
+ loadingAnimation: undefined,
86
+ statusContainer: {
87
+ clear() {},
88
+ addChild() {},
89
+ },
90
+ ui: {
91
+ requestRender() {
92
+ requestedRender = true;
93
+ },
94
+ },
95
+ defaultEditor: {},
96
+ footer: {
97
+ invalidate() {},
98
+ },
99
+ settingsManager: {
100
+ getTimestampFormat() {
101
+ return "date-time-iso";
102
+ },
103
+ },
104
+ defaultWorkingMessage: "Working...",
105
+ pendingWorkingMessage: undefined,
106
+ } as any;
107
+
108
+ await handleAgentEvent(host, { type: "agent_start" } as any);
109
+ host.loadingAnimation?.stop();
110
+
111
+ assert.equal(cleared, true);
112
+ assert.equal(requestedRender, true);
113
+ });