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

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 (342) 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 +48 -10
  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/dispatcher.js +5 -0
  19. package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
  20. package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
  21. package/dist/resources/extensions/gsd/custom-workflow-engine.js +22 -2
  22. package/dist/resources/extensions/gsd/db-base-schema.js +14 -0
  23. package/dist/resources/extensions/gsd/db-migration-steps.js +16 -0
  24. package/dist/resources/extensions/gsd/detection.js +106 -0
  25. package/dist/resources/extensions/gsd/graph.js +9 -3
  26. package/dist/resources/extensions/gsd/gsd-db.js +102 -2
  27. package/dist/resources/extensions/gsd/guided-flow.js +49 -12
  28. package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
  29. package/dist/resources/extensions/gsd/pr-evidence.js +57 -16
  30. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
  31. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  32. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  33. package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
  34. package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
  35. package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
  36. package/dist/resources/extensions/gsd/unit-runtime.js +11 -0
  37. package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
  38. package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
  39. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  40. package/dist/web/standalone/.next/BUILD_ID +1 -1
  41. package/dist/web/standalone/.next/app-path-routes-manifest.json +16 -16
  42. package/dist/web/standalone/.next/build-manifest.json +3 -3
  43. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  44. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  45. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  46. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  54. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  62. package/dist/web/standalone/.next/server/app/index.html +1 -1
  63. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app-paths-manifest.json +16 -16
  70. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  71. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  72. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  74. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  75. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  76. package/dist/web/standalone/.next/static/chunks/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
  77. package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
  78. package/package.json +10 -6
  79. package/packages/contracts/package.json +1 -1
  80. package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
  81. package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
  82. package/packages/pi-ai/dist/models/fake-model.js +27 -0
  83. package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
  84. package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
  85. package/packages/pi-ai/dist/models/index.js +8 -0
  86. package/packages/pi-ai/dist/models/index.js.map +1 -1
  87. package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
  88. package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
  89. package/packages/pi-ai/dist/providers/fake.js +319 -0
  90. package/packages/pi-ai/dist/providers/fake.js.map +1 -0
  91. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  92. package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
  93. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  94. package/packages/pi-ai/src/models/fake-model.ts +30 -0
  95. package/packages/pi-ai/src/models/index.ts +9 -0
  96. package/packages/pi-ai/src/providers/fake.ts +376 -0
  97. package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
  98. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  99. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +74 -0
  100. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  101. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
  102. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
  103. package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
  104. package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
  105. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
  106. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
  107. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
  108. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
  109. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
  110. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  111. package/packages/pi-coding-agent/dist/core/extensions/runner.js +14 -1
  112. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  113. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +97 -0
  114. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  115. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/core/model-registry.js +5 -0
  117. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +4 -0
  119. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  120. package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
  121. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  122. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  124. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +54 -15
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
  130. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
  132. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
  133. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
  136. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
  137. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  139. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
  141. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
  143. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  144. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
  145. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  146. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
  147. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  148. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +16 -0
  149. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  150. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +106 -17
  151. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +60 -1
  154. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +40 -1
  156. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
  158. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  159. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  160. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  161. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  162. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
  163. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +23 -0
  165. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  167. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
  168. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  169. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
  170. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
  171. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
  172. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
  173. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  174. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  175. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
  176. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  177. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  179. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
  180. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  181. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  182. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
  183. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
  185. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
  186. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
  187. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
  188. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
  189. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
  190. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
  191. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
  192. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
  193. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
  194. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +10 -0
  195. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
  196. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
  197. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
  198. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
  199. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +87 -0
  200. package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
  201. package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
  202. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +108 -0
  203. package/packages/pi-coding-agent/src/core/extensions/runner.ts +16 -1
  204. package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
  205. package/packages/pi-coding-agent/src/core/settings-manager.ts +12 -0
  206. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  207. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
  208. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +78 -15
  209. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
  210. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
  211. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
  212. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
  213. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  214. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
  215. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +118 -17
  216. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +43 -1
  217. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +75 -1
  218. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
  219. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
  220. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +25 -0
  221. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
  222. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
  223. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
  224. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
  225. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
  226. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
  227. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
  228. package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +14 -0
  229. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
  230. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  231. package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
  232. package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
  233. package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
  234. package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
  235. package/packages/pi-tui/dist/__tests__/tui.test.js +24 -3
  236. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  237. package/packages/pi-tui/dist/index.d.ts +1 -0
  238. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  239. package/packages/pi-tui/dist/index.js +2 -0
  240. package/packages/pi-tui/dist/index.js.map +1 -1
  241. package/packages/pi-tui/dist/style.d.ts +41 -0
  242. package/packages/pi-tui/dist/style.d.ts.map +1 -0
  243. package/packages/pi-tui/dist/style.js +158 -0
  244. package/packages/pi-tui/dist/style.js.map +1 -0
  245. package/packages/pi-tui/dist/tui.d.ts +0 -1
  246. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  247. package/packages/pi-tui/dist/tui.js +3 -8
  248. package/packages/pi-tui/dist/tui.js.map +1 -1
  249. package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
  250. package/packages/pi-tui/src/__tests__/tui.test.ts +29 -3
  251. package/packages/pi-tui/src/index.ts +9 -0
  252. package/packages/pi-tui/src/style.ts +225 -0
  253. package/packages/pi-tui/src/tui.ts +3 -8
  254. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  255. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  256. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  257. package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
  258. package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  259. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  260. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  261. package/pkg/dist/modes/interactive/theme/theme.js +18 -1
  262. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  263. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  264. package/pkg/dist/modes/interactive/theme/themes.js +36 -27
  265. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  266. package/src/resources/GSD-WORKFLOW.md +2 -2
  267. package/src/resources/extensions/github-sync/templates.ts +38 -8
  268. package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
  269. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  270. package/src/resources/extensions/gsd/auto/loop.ts +67 -18
  271. package/src/resources/extensions/gsd/auto/phases.ts +42 -28
  272. package/src/resources/extensions/gsd/auto/run-unit.ts +24 -14
  273. package/src/resources/extensions/gsd/auto-dashboard.ts +57 -8
  274. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
  275. package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
  276. package/src/resources/extensions/gsd/auto-prompts.ts +116 -1
  277. package/src/resources/extensions/gsd/auto-recovery.ts +153 -7
  278. package/src/resources/extensions/gsd/auto-start.ts +7 -6
  279. package/src/resources/extensions/gsd/auto.ts +12 -1
  280. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -1
  281. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +17 -1
  282. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
  283. package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
  284. package/src/resources/extensions/gsd/commands/dispatcher.ts +6 -0
  285. package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
  286. package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
  287. package/src/resources/extensions/gsd/custom-workflow-engine.ts +24 -1
  288. package/src/resources/extensions/gsd/db-base-schema.ts +15 -0
  289. package/src/resources/extensions/gsd/db-migration-steps.ts +17 -0
  290. package/src/resources/extensions/gsd/detection.ts +128 -0
  291. package/src/resources/extensions/gsd/graph.ts +12 -5
  292. package/src/resources/extensions/gsd/gsd-db.ts +119 -1
  293. package/src/resources/extensions/gsd/guided-flow.ts +49 -12
  294. package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
  295. package/src/resources/extensions/gsd/pr-evidence.ts +63 -5
  296. package/src/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
  297. package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  298. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  299. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
  300. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +7 -1
  301. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +33 -0
  302. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +84 -5
  303. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +170 -1
  304. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
  305. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
  306. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +55 -0
  307. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
  308. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +112 -6
  309. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +40 -2
  310. package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
  311. package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
  312. package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
  313. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
  314. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
  315. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
  316. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
  317. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +10 -0
  318. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
  319. package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
  320. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
  321. package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
  322. package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
  323. package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
  324. package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
  325. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
  326. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +38 -0
  327. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +46 -2
  328. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +7 -0
  329. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  330. package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
  331. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
  332. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
  333. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
  334. package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
  335. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
  336. package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
  337. package/src/resources/extensions/gsd/unit-runtime.ts +14 -0
  338. package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
  339. package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
  340. package/packages/contracts/tsconfig.tsbuildinfo +0 -1
  341. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → y73quA-XdLo9n41nxphjW}/_buildManifest.js +0 -0
  342. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → y73quA-XdLo9n41nxphjW}/_ssgManifest.js +0 -0
@@ -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
+ });
@@ -1,9 +1,14 @@
1
+ // GSD-2 Interactive Chat Controller
1
2
  import { Loader, Markdown, Spacer, Text } from "@gsd/pi-tui";
2
3
 
3
4
  import type { InteractiveModeEvent, InteractiveModeStateHost } from "../interactive-mode-state.js";
4
5
  import { theme } from "../theme/theme.js";
5
6
  import { AssistantMessageComponent } from "../components/assistant-message.js";
6
- import { ToolExecutionComponent } from "../components/tool-execution.js";
7
+ import {
8
+ ToolExecutionComponent,
9
+ ToolPhaseSummaryComponent,
10
+ type ToolExecutionPhase,
11
+ } from "../components/tool-execution.js";
7
12
  import { DynamicBorder } from "../components/dynamic-border.js";
8
13
  import { appKey } from "../components/keybinding-hints.js";
9
14
 
@@ -100,6 +105,71 @@ let pinnedBorder: DynamicBorder | undefined;
100
105
  // Reference to the pinned markdown component below the border
101
106
  let pinnedTextComponent: Markdown | undefined;
102
107
 
108
+ function mergeToolPhases(phases: ToolExecutionPhase[]): ToolExecutionPhase[] {
109
+ const merged: ToolExecutionPhase[] = [];
110
+ for (const phase of phases) {
111
+ const previous = merged[merged.length - 1];
112
+ if (previous?.label === phase.label) {
113
+ previous.count += phase.count;
114
+ previous.durationMs += phase.durationMs;
115
+ } else {
116
+ merged.push({ ...phase });
117
+ }
118
+ }
119
+ return merged;
120
+ }
121
+
122
+ function replaceCompactToolRowsWithPhaseSummary(
123
+ host: InteractiveModeStateHost & { ui: { requestRender: () => void } },
124
+ ): void {
125
+ let changed = false;
126
+ const nextRenderedSegments: RenderedSegment[] = [];
127
+ let rollupRun: Array<{ seg: Extract<RenderedSegment, { kind: "tool" }>; phase: ToolExecutionPhase }> = [];
128
+
129
+ const flushRollupRun = () => {
130
+ if (rollupRun.length < 2) {
131
+ nextRenderedSegments.push(...rollupRun.map((item) => item.seg));
132
+ rollupRun = [];
133
+ return;
134
+ }
135
+
136
+ const firstIndex = Math.max(0, host.chatContainer.children.indexOf(rollupRun[0].seg.component));
137
+ const summary = new ToolPhaseSummaryComponent(mergeToolPhases(rollupRun.map((item) => item.phase)));
138
+
139
+ for (const { seg } of rollupRun) {
140
+ host.chatContainer.removeChild(seg.component);
141
+ }
142
+
143
+ host.chatContainer.addChild(summary);
144
+ const summaryIndex = host.chatContainer.children.indexOf(summary);
145
+ if (summaryIndex !== -1 && summaryIndex !== firstIndex) {
146
+ host.chatContainer.children.splice(summaryIndex, 1);
147
+ host.chatContainer.children.splice(firstIndex, 0, summary);
148
+ (host.chatContainer as unknown as { _prevRender: string[] | null })._prevRender = null;
149
+ }
150
+
151
+ changed = true;
152
+ rollupRun = [];
153
+ };
154
+
155
+ for (const seg of renderedSegments) {
156
+ const phase = seg.kind === "tool" ? seg.component.getRollupPhase() : null;
157
+ if (seg.kind === "tool" && phase) {
158
+ rollupRun.push({ seg, phase });
159
+ continue;
160
+ }
161
+
162
+ flushRollupRun();
163
+ nextRenderedSegments.push(seg);
164
+ }
165
+ flushRollupRun();
166
+
167
+ if (changed) {
168
+ renderedSegments = nextRenderedSegments;
169
+ host.ui.requestRender();
170
+ }
171
+ }
172
+
103
173
  export async function handleAgentEvent(host: InteractiveModeStateHost & {
104
174
  init: () => Promise<void>;
105
175
  getMarkdownThemeWithSettings: () => any;
@@ -177,6 +247,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
177
247
  return;
178
248
  }
179
249
  case "agent_start":
250
+ host.clearBlockingError();
180
251
  if (host.retryEscapeHandler) {
181
252
  host.defaultEditor.onEscape = host.retryEscapeHandler;
182
253
  host.retryEscapeHandler = undefined;
@@ -772,6 +843,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
772
843
  host.streamingComponent.setShowMetadata(true);
773
844
  host.streamingComponent.updateContent(host.streamingMessage);
774
845
  }
846
+ replaceCompactToolRowsWithPhaseSummary(host);
775
847
 
776
848
  if (host.streamingMessage.stopReason === "aborted" || host.streamingMessage.stopReason === "error") {
777
849
  if (!errorMessage) {
@@ -822,6 +894,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
822
894
  component.setExpanded(host.toolOutputExpanded);
823
895
  host.chatContainer.addChild(component);
824
896
  host.pendingTools.set(event.toolCallId, component);
897
+ renderedSegments.push({ kind: "tool", contentIndex: Number.MAX_SAFE_INTEGER, component });
825
898
  host.ui.requestRender();
826
899
  }
827
900
  break;
@@ -855,6 +928,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
855
928
  host.streamingComponent.setShowMetadata(true);
856
929
  host.streamingComponent.updateContent(host.streamingMessage);
857
930
  }
931
+ replaceCompactToolRowsWithPhaseSummary(host);
858
932
  host.streamingComponent = undefined;
859
933
  host.streamingMessage = undefined;
860
934
  renderedSegments = [];
@@ -49,6 +49,7 @@ function createMockHost() {
49
49
  isInitialized: true,
50
50
  loadingAnimation: undefined,
51
51
  pendingWorkingMessage: undefined,
52
+ clearBlockingError: () => {},
52
53
  defaultWorkingMessage: "Working...",
53
54
  streamingComponent: undefined,
54
55
  streamingMessage: undefined,
@@ -20,6 +20,7 @@ export interface InteractiveModeStateHost {
20
20
  isInitialized: boolean;
21
21
  loadingAnimation?: any;
22
22
  pendingWorkingMessage?: string;
23
+ clearBlockingError(): void;
23
24
  defaultWorkingMessage: string;
24
25
  streamingComponent?: any;
25
26
  streamingMessage?: any;
@@ -37,4 +38,3 @@ export interface InteractiveModeStateHost {
37
38
  }
38
39
 
39
40
  export type InteractiveModeEvent = AgentSessionEvent;
40
-
@@ -64,6 +64,7 @@ import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/cha
64
64
  import { readClipboardImage } from "../../utils/clipboard-image.js";
65
65
  import { ensureTool } from "../../utils/tools-manager.js";
66
66
  import { AssistantMessageComponent } from "./components/assistant-message.js";
67
+ import { AdaptiveLayoutComponent } from "./components/adaptive-layout.js";
67
68
  import { BashExecutionComponent } from "./components/bash-execution.js";
68
69
  import { BorderedLoader } from "./components/bordered-loader.js";
69
70
  import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
@@ -246,6 +247,7 @@ export class InteractiveMode {
246
247
  private ui: TUI;
247
248
  private chatContainer: Container;
248
249
  private pendingMessagesContainer: Container;
250
+ private adaptiveLayout: AdaptiveLayoutComponent;
249
251
  private statusContainer: Container;
250
252
  private pinnedMessageContainer: Container;
251
253
  private defaultEditor: CustomEditor;
@@ -261,6 +263,7 @@ export class InteractiveMode {
261
263
  private loadingAnimation: Loader | undefined = undefined;
262
264
  private pendingWorkingMessage: string | undefined = undefined;
263
265
  private readonly defaultWorkingMessage = "Working...";
266
+ private lastBlockingError: string | undefined = undefined;
264
267
 
265
268
  private lastSigintTime = 0;
266
269
  private lastEscapeTime = 0;
@@ -367,6 +370,14 @@ export class InteractiveMode {
367
370
  this.headerContainer = new Container();
368
371
  this.chatContainer = new Container();
369
372
  this.pendingMessagesContainer = new Container();
373
+ this.adaptiveLayout = new AdaptiveLayoutComponent(() => ({
374
+ override: this.settingsManager.getAdaptiveMode(),
375
+ activeToolCount: this.pendingTools.size,
376
+ gsdPhase: this.pendingWorkingMessage,
377
+ lastError: this.lastBlockingError,
378
+ sessionName: this.sessionManager.getSessionName(),
379
+ cwd: process.cwd(),
380
+ }));
370
381
  this.statusContainer = new Container();
371
382
  this.pinnedMessageContainer = new Container();
372
383
  this.widgetContainerAbove = new Container();
@@ -571,6 +582,7 @@ export class InteractiveMode {
571
582
  }
572
583
  }
573
584
 
585
+ this.ui.addChild(this.adaptiveLayout);
574
586
  this.ui.addChild(this.chatContainer);
575
587
  this.ui.addChild(this.pendingMessagesContainer);
576
588
  this.ui.addChild(this.statusContainer);
@@ -1221,6 +1233,7 @@ export class InteractiveMode {
1221
1233
  this.streamingComponent = undefined;
1222
1234
  this.streamingMessage = undefined;
1223
1235
  this.pendingTools.clear();
1236
+ this.clearBlockingError();
1224
1237
 
1225
1238
  // Render any messages added via setup, or show empty session
1226
1239
  this.renderInitialMessages();
@@ -2880,11 +2893,16 @@ export class InteractiveMode {
2880
2893
  }
2881
2894
 
2882
2895
  showError(errorMessage: string): void {
2896
+ this.lastBlockingError = errorMessage;
2883
2897
  this.chatContainer.addChild(new Spacer(1));
2884
2898
  this.chatContainer.addChild(new Text(theme.fg("error", `Error: ${errorMessage}`), 1, 0));
2885
2899
  this.ui.requestRender();
2886
2900
  }
2887
2901
 
2902
+ clearBlockingError(): void {
2903
+ this.lastBlockingError = undefined;
2904
+ }
2905
+
2888
2906
  showWarning(warningMessage: string): void {
2889
2907
  this.chatContainer.addChild(new Spacer(1));
2890
2908
  this.chatContainer.addChild(new Text(theme.fg("warning", `Warning: ${warningMessage}`), 1, 0));
@@ -3195,6 +3213,7 @@ export class InteractiveMode {
3195
3213
  quietStartup: this.settingsManager.getQuietStartup(),
3196
3214
  clearOnShrink: this.settingsManager.getClearOnShrink(),
3197
3215
  timestampFormat: this.settingsManager.getTimestampFormat(),
3216
+ adaptiveMode: this.settingsManager.getAdaptiveMode(),
3198
3217
  },
3199
3218
  {
3200
3219
  onAutoCompactChange: (enabled) => {
@@ -3301,6 +3320,10 @@ export class InteractiveMode {
3301
3320
  onTimestampFormatChange: (format) => {
3302
3321
  this.settingsManager.setTimestampFormat(format);
3303
3322
  },
3323
+ onAdaptiveModeChange: (mode) => {
3324
+ this.settingsManager.setAdaptiveMode(mode);
3325
+ this.ui.requestRender();
3326
+ },
3304
3327
  onCancel: () => {
3305
3328
  done();
3306
3329
  this.ui.requestRender();
@@ -3689,6 +3712,7 @@ export class InteractiveMode {
3689
3712
  this.streamingComponent = undefined;
3690
3713
  this.streamingMessage = undefined;
3691
3714
  this.pendingTools.clear();
3715
+ this.clearBlockingError();
3692
3716
 
3693
3717
  // Switch session via AgentSession (emits extension session events)
3694
3718
  await this.session.switchSession(sessionPath);
@@ -4010,6 +4034,7 @@ export class InteractiveMode {
4010
4034
  this.streamingMessage = undefined;
4011
4035
  this.pendingTools.clear();
4012
4036
  this.pendingImages.length = 0;
4037
+ this.clearBlockingError();
4013
4038
 
4014
4039
  // Reset contextual tips for the new session
4015
4040
  this.contextualTips.reset();
@@ -0,0 +1,95 @@
1
+ // GSD2 - Slash command tests for interactive TUI settings commands
2
+
3
+ import assert from "node:assert/strict";
4
+ import { describe, it } from "node:test";
5
+ import { Container } from "@gsd/pi-tui";
6
+ import { SettingsManager } from "../../core/settings-manager.js";
7
+ import { dispatchSlashCommand, type SlashCommandContext } from "./slash-command-handlers.js";
8
+
9
+ function makeContext(settingsManager = SettingsManager.inMemory()): SlashCommandContext {
10
+ const statuses: string[] = [];
11
+ const warnings: string[] = [];
12
+ const renders: string[] = [];
13
+ return {
14
+ session: {} as never,
15
+ ui: {} as never,
16
+ keybindings: {} as never,
17
+ chatContainer: new Container(),
18
+ statusContainer: new Container(),
19
+ editorContainer: new Container(),
20
+ headerContainer: new Container(),
21
+ pendingMessagesContainer: new Container(),
22
+ editor: {} as never,
23
+ defaultEditor: {} as never,
24
+ sessionManager: {} as never,
25
+ settingsManager,
26
+ invalidateFooter() {},
27
+ showStatus(message: string) {
28
+ statuses.push(message);
29
+ },
30
+ showError(message: string) {
31
+ throw new Error(message);
32
+ },
33
+ showWarning(message: string) {
34
+ warnings.push(message);
35
+ },
36
+ showSelector() {},
37
+ updateEditorBorderColor() {},
38
+ getMarkdownThemeWithSettings: () => ({} as never),
39
+ requestRender() {
40
+ renders.push("render");
41
+ },
42
+ updateTerminalTitle() {},
43
+ showSettingsSelector() {},
44
+ showModelsSelector: async () => {},
45
+ handleModelCommand: async () => {},
46
+ showUserMessageSelector() {},
47
+ showTreeSelector() {},
48
+ showProviderManager() {},
49
+ showOAuthSelector: async () => {},
50
+ showSessionSelector() {},
51
+ handleClearCommand: async () => {},
52
+ handleReloadCommand: async () => {},
53
+ handleDebugCommand() {},
54
+ shutdown: async () => {},
55
+ executeCompaction: async () => undefined,
56
+ handleBashCommand: async () => {},
57
+ _testStatuses: statuses,
58
+ _testWarnings: warnings,
59
+ _testRenders: renders,
60
+ } as SlashCommandContext & {
61
+ _testStatuses: string[];
62
+ _testWarnings: string[];
63
+ _testRenders: string[];
64
+ };
65
+ }
66
+
67
+ describe("dispatchSlashCommand /tui", () => {
68
+ it("persists /tui mode validation to terminal adaptive mode", async () => {
69
+ const settingsManager = SettingsManager.inMemory();
70
+ const ctx = makeContext(settingsManager) as SlashCommandContext & {
71
+ _testStatuses: string[];
72
+ _testRenders: string[];
73
+ };
74
+
75
+ const handled = await dispatchSlashCommand("/tui mode validation", ctx);
76
+
77
+ assert.equal(handled, true);
78
+ assert.equal(settingsManager.getAdaptiveMode(), "validation");
79
+ assert.deepEqual(ctx._testStatuses, ["TUI mode: validation"]);
80
+ assert.equal(ctx._testRenders.length, 1);
81
+ });
82
+
83
+ it("rejects unknown TUI modes without changing settings", async () => {
84
+ const settingsManager = SettingsManager.inMemory({ terminal: { adaptiveMode: "workflow" } });
85
+ const ctx = makeContext(settingsManager) as SlashCommandContext & {
86
+ _testWarnings: string[];
87
+ };
88
+
89
+ const handled = await dispatchSlashCommand("/tui mode poster", ctx);
90
+
91
+ assert.equal(handled, true);
92
+ assert.equal(settingsManager.getAdaptiveMode(), "workflow");
93
+ assert.match(ctx._testWarnings[0], /Usage: \/tui mode/);
94
+ });
95
+ });
@@ -31,7 +31,7 @@ import {
31
31
  import type { AgentSession } from "../../core/agent-session.js";
32
32
  import type { AppAction, KeybindingsManager } from "../../core/keybindings.js";
33
33
  import type { SessionManager } from "../../core/session-manager.js";
34
- import type { SettingsManager } from "../../core/settings-manager.js";
34
+ import type { AdaptiveTuiMode, SettingsManager } from "../../core/settings-manager.js";
35
35
  import { copyToClipboard } from "../../utils/clipboard.js";
36
36
  import { getChangelogPath, parseChangelog } from "../../utils/changelog.js";
37
37
  import { ArminComponent } from "./components/armin.js";
@@ -235,6 +235,10 @@ export async function dispatchSlashCommand(
235
235
  await ctx.handleBashCommand(command, { loginShell: true });
236
236
  return true;
237
237
  }
238
+ if (text === "/tui" || text.startsWith("/tui ")) {
239
+ handleTuiCommand(text, ctx);
240
+ return true;
241
+ }
238
242
 
239
243
  return false;
240
244
  }
@@ -665,6 +669,25 @@ function handleEditModeCommand(arg: string | undefined, ctx: SlashCommandContext
665
669
  ctx.showStatus(`Edit mode: ${next}${next === "hashline" ? " (LINE#ID anchored edits)" : " (text-match edits)"}`);
666
670
  }
667
671
 
672
+ function handleTuiCommand(text: string, ctx: SlashCommandContext): void {
673
+ const parts = text.trim().split(/\s+/);
674
+ const mode = parts[1] === "mode" ? parts[2] : parts[1];
675
+ const valid: AdaptiveTuiMode[] = ["auto", "chat", "workflow", "validation", "debug", "compact"];
676
+
677
+ if (!mode) {
678
+ ctx.showStatus(`TUI mode: ${ctx.settingsManager.getAdaptiveMode()}`);
679
+ return;
680
+ }
681
+ if (!valid.includes(mode as AdaptiveTuiMode)) {
682
+ ctx.showWarning(`Usage: /tui mode ${valid.join("|")}`);
683
+ return;
684
+ }
685
+
686
+ ctx.settingsManager.setAdaptiveMode(mode as AdaptiveTuiMode);
687
+ ctx.showStatus(`TUI mode: ${mode}`);
688
+ ctx.requestRender();
689
+ }
690
+
668
691
  function handleArminSaysHi(ctx: SlashCommandContext): void {
669
692
  ctx.chatContainer.addChild(new Spacer(1));
670
693
  ctx.chatContainer.addChild(new ArminComponent(ctx.ui));
@@ -70,6 +70,19 @@ export const ThemeJsonSchema = Type.Object({
70
70
  thinkingXhigh: ColorValueSchema,
71
71
  // Bash Mode (1 color)
72
72
  bashMode: ColorValueSchema,
73
+ // Adaptive TUI semantic tokens (optional; defaults are derived from core colors)
74
+ surfaceBorder: Type.Optional(ColorValueSchema),
75
+ surfaceMuted: Type.Optional(ColorValueSchema),
76
+ surfaceTitle: Type.Optional(ColorValueSchema),
77
+ surfaceAccent: Type.Optional(ColorValueSchema),
78
+ toolRunning: Type.Optional(ColorValueSchema),
79
+ toolSuccess: Type.Optional(ColorValueSchema),
80
+ toolError: Type.Optional(ColorValueSchema),
81
+ toolMuted: Type.Optional(ColorValueSchema),
82
+ modeWorkflow: Type.Optional(ColorValueSchema),
83
+ modeValidation: Type.Optional(ColorValueSchema),
84
+ modeDebug: Type.Optional(ColorValueSchema),
85
+ modeCompact: Type.Optional(ColorValueSchema),
73
86
  }),
74
87
  export: Type.Optional(
75
88
  Type.Object({