@pellux/goodvibes-tui 2.0.20 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (388) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +16 -16
  3. package/docs/README.md +4 -4
  4. package/docs/foundation-artifacts/operator-contract.json +59 -4
  5. package/package.json +16 -6
  6. package/src/cli/management.ts +5 -2
  7. package/src/cli/tui-startup.ts +34 -39
  8. package/src/core/agent-view-render.ts +157 -0
  9. package/src/core/approval-alert.ts +49 -11
  10. package/src/core/bookmark-navigation.ts +7 -1
  11. package/src/core/budget-breach-notifier.ts +27 -8
  12. package/src/core/compaction-receipt.ts +1 -1
  13. package/src/core/context-usage.ts +1 -1
  14. package/src/core/conversation-compaction-render.ts +3 -3
  15. package/src/core/conversation-display-only.ts +86 -0
  16. package/src/core/conversation-event-navigation.ts +27 -0
  17. package/src/core/conversation-line-cache.ts +216 -447
  18. package/src/core/conversation-render-context.ts +20 -27
  19. package/src/core/conversation-rendering.ts +157 -575
  20. package/src/core/conversation-splash-state.ts +6 -1
  21. package/src/core/conversation-types.ts +35 -0
  22. package/src/core/conversation-usage.ts +12 -0
  23. package/src/core/conversation.ts +130 -131
  24. package/src/core/long-task-notifier.ts +100 -31
  25. package/src/core/notices.ts +75 -0
  26. package/src/core/pairing-tailscale-gateway.ts +1 -1
  27. package/src/core/process-output-log.ts +143 -0
  28. package/src/core/rewind-receipt.ts +1 -1
  29. package/src/core/session-focus.ts +190 -0
  30. package/src/core/session-resume-core.ts +23 -81
  31. package/src/core/stream-event-wiring.ts +31 -5
  32. package/src/core/system-message-noise.ts +2 -2
  33. package/src/core/system-message-router.ts +16 -18
  34. package/src/core/turn-event-wiring.ts +272 -33
  35. package/src/core/work-tree-focus.ts +290 -0
  36. package/src/core/work-tree-fold-store.ts +143 -0
  37. package/src/core/work-tree-model.ts +799 -0
  38. package/src/core/work-tree-render.ts +384 -0
  39. package/src/core/work-tree-sources.ts +184 -0
  40. package/src/core/work-tree-wiring.ts +176 -0
  41. package/src/core/workstream-notification.ts +36 -6
  42. package/src/core/wrfc-notice-titles.ts +37 -0
  43. package/src/input/agents-modal-text.ts +119 -0
  44. package/src/input/agents-modal.ts +699 -0
  45. package/src/input/bookmark-modal.ts +33 -9
  46. package/src/input/changes-git.ts +251 -0
  47. package/src/input/changes-modal.ts +629 -0
  48. package/src/input/changes-model.ts +125 -0
  49. package/src/input/command-palette-types.ts +41 -0
  50. package/src/input/command-palette.ts +272 -0
  51. package/src/input/command-registry.ts +77 -35
  52. package/src/input/commands/acp-runtime.ts +19 -9
  53. package/src/input/commands/channel-runtime.ts +2 -2
  54. package/src/input/commands/ci-runtime.ts +1 -1
  55. package/src/input/commands/codebase-runtime.ts +1 -1
  56. package/src/input/commands/config.ts +7 -1
  57. package/src/input/commands/context-window.ts +25 -19
  58. package/src/input/commands/control-room-runtime.ts +18 -18
  59. package/src/input/commands/conversation-runtime.ts +0 -1
  60. package/src/input/commands/cost-runtime.ts +29 -20
  61. package/src/input/commands/diff-runtime.ts +35 -181
  62. package/src/input/commands/eval.ts +1 -1
  63. package/src/input/commands/experience-runtime.ts +3 -7
  64. package/src/input/commands/fresh-conversation.ts +86 -0
  65. package/src/input/commands/git-runtime.ts +5 -22
  66. package/src/input/commands/graph-runtime.ts +1 -1
  67. package/src/input/commands/guidance-runtime.ts +1 -1
  68. package/src/input/commands/health-runtime.ts +8 -9
  69. package/src/input/commands/hooks-runtime.ts +2 -2
  70. package/src/input/commands/hosted-runtime.ts +3 -3
  71. package/src/input/commands/incident-runtime.ts +3 -3
  72. package/src/input/commands/intelligence-runtime.ts +6 -9
  73. package/src/input/commands/knowledge.ts +2 -2
  74. package/src/input/commands/legacy-panel-command.ts +57 -0
  75. package/src/input/commands/local-auth-runtime.ts +8 -9
  76. package/src/input/commands/local-provider-runtime.ts +7 -4
  77. package/src/input/commands/local-runtime.ts +12 -15
  78. package/src/input/commands/local-setup-review.ts +1 -1
  79. package/src/input/commands/local-setup.ts +13 -19
  80. package/src/input/commands/memory.ts +1 -1
  81. package/src/input/commands/notify-runtime.ts +9 -0
  82. package/src/input/commands/operator-runtime.ts +10 -12
  83. package/src/input/commands/permissions-provenance.ts +3 -3
  84. package/src/input/commands/planning-runtime.ts +7 -7
  85. package/src/input/commands/platform-sandbox-runtime.ts +2 -2
  86. package/src/input/commands/platform-services-runtime.ts +1 -1
  87. package/src/input/commands/policy-dispatch.ts +2 -2
  88. package/src/input/commands/policy.ts +3 -3
  89. package/src/input/commands/provider.ts +1 -1
  90. package/src/input/commands/remote-runtime.ts +1 -1
  91. package/src/input/commands/review-runtime.ts +39 -94
  92. package/src/input/commands/rewind-runtime.ts +81 -108
  93. package/src/input/commands/runtime-services.ts +15 -17
  94. package/src/input/commands/services-runtime.ts +2 -2
  95. package/src/input/commands/session-content.ts +21 -5
  96. package/src/input/commands/session-workflow.ts +13 -38
  97. package/src/input/commands/settings-sync-runtime.ts +2 -2
  98. package/src/input/commands/shell-core.ts +19 -4
  99. package/src/input/commands/skills-runtime.ts +2 -2
  100. package/src/input/commands/status-runtime.ts +13 -0
  101. package/src/input/commands/subscription-runtime.ts +1 -1
  102. package/src/input/commands/tasks-runtime.ts +2 -8
  103. package/src/input/commands/work-plan-runtime.ts +4 -4
  104. package/src/input/commands/workstream-attempts.ts +24 -47
  105. package/src/input/commands/workstream-runtime.ts +2 -2
  106. package/src/input/commands/worktree-runtime.ts +3 -3
  107. package/src/input/commands.ts +3 -1
  108. package/src/input/composer-editor.ts +44 -0
  109. package/src/input/concealed-input.ts +1 -1
  110. package/src/input/config-modal-types.ts +20 -8
  111. package/src/input/config-modal.ts +106 -105
  112. package/src/input/confirm-dialog-types.ts +28 -0
  113. package/src/input/confirm-dialog.ts +70 -0
  114. package/src/input/delete-key-policy.ts +3 -3
  115. package/src/input/feed-context-factory.ts +15 -16
  116. package/src/input/handler-command-route.ts +7 -42
  117. package/src/input/handler-content-actions.ts +16 -15
  118. package/src/input/handler-feed-routes.ts +26 -211
  119. package/src/input/handler-feed.ts +68 -95
  120. package/src/input/handler-interactions.ts +5 -3
  121. package/src/input/handler-modal-routes.ts +215 -216
  122. package/src/input/handler-modal-stack.ts +81 -11
  123. package/src/input/handler-modal-token-routes.ts +4 -0
  124. package/src/input/handler-onboarding.ts +0 -1
  125. package/src/input/handler-picker-routes.ts +65 -107
  126. package/src/input/handler-session-view-route.ts +149 -0
  127. package/src/input/handler-shortcuts.ts +16 -154
  128. package/src/input/handler-types.ts +4 -2
  129. package/src/input/handler-ui-state.ts +45 -15
  130. package/src/input/handler-work-tree-route.ts +123 -0
  131. package/src/input/handler.ts +25 -70
  132. package/src/input/keybindings.ts +16 -71
  133. package/src/input/masked-entry-modal.ts +117 -0
  134. package/src/input/mcp-workspace.ts +48 -17
  135. package/src/input/model-picker-filter.ts +8 -0
  136. package/src/input/model-picker-open.ts +99 -0
  137. package/src/input/model-picker-types.ts +1 -1
  138. package/src/input/model-picker.ts +92 -9
  139. package/src/input/notifications-modal.ts +123 -0
  140. package/src/input/overlay-filter.ts +26 -0
  141. package/src/input/profile-picker-modal.ts +37 -8
  142. package/src/input/search.ts +3 -3
  143. package/src/input/selection-modal-queue.ts +2 -0
  144. package/src/input/selection-modal.ts +28 -16
  145. package/src/input/session-picker-modal.ts +33 -8
  146. package/src/input/settings-modal-activation.ts +7 -2
  147. package/src/input/settings-modal-data.ts +11 -6
  148. package/src/input/settings-modal-types.ts +1 -1
  149. package/src/input/settings-modal.ts +20 -20
  150. package/src/input/submission-intent.ts +1 -1
  151. package/src/input/submission-router.ts +4 -9
  152. package/src/input/surface-modal-host.ts +110 -0
  153. package/src/input/theme-settings-actions.ts +82 -0
  154. package/src/input/usage-modal.ts +183 -0
  155. package/src/input/views.ts +97 -0
  156. package/src/main.ts +175 -165
  157. package/src/permissions/hunk-selection.ts +1 -1
  158. package/src/permissions/prompt-card.ts +511 -0
  159. package/src/permissions/prompt.ts +59 -622
  160. package/src/renderer/agents-modal.ts +310 -0
  161. package/src/renderer/autocomplete-overlay.ts +42 -220
  162. package/src/renderer/back-to-bottom.ts +80 -0
  163. package/src/renderer/block-actions-overlay.ts +30 -48
  164. package/src/renderer/bookmark-modal.ts +48 -79
  165. package/src/renderer/changes-modal.ts +325 -0
  166. package/src/renderer/code-block.ts +184 -106
  167. package/src/renderer/command-palette.ts +93 -0
  168. package/src/renderer/composer-fragments.ts +4 -4
  169. package/src/renderer/composer.ts +146 -0
  170. package/src/renderer/compositor.ts +26 -160
  171. package/src/renderer/config-modal.ts +185 -81
  172. package/src/renderer/confirm-dialog.ts +50 -0
  173. package/src/renderer/context-inspector.ts +116 -154
  174. package/src/renderer/context-status-hint.ts +4 -4
  175. package/src/renderer/conversation-layout.ts +20 -0
  176. package/src/renderer/conversation-overlays.ts +94 -84
  177. package/src/renderer/conversation-surface.ts +19 -38
  178. package/src/renderer/diff-tint.ts +98 -0
  179. package/src/renderer/file-picker-overlay.ts +63 -90
  180. package/src/renderer/footer-targets.ts +38 -0
  181. package/src/renderer/git-status.ts +3 -4
  182. package/src/renderer/header-line.ts +140 -117
  183. package/src/renderer/help-overlay.ts +133 -219
  184. package/src/renderer/history-search-overlay.ts +15 -12
  185. package/src/renderer/lane-graph/bead.ts +800 -0
  186. package/src/renderer/lane-graph/glyphs.ts +134 -0
  187. package/src/renderer/lane-graph/layout.ts +248 -0
  188. package/src/renderer/lane-graph/paint.ts +537 -0
  189. package/src/renderer/lane-graph/semantic-memo.ts +77 -0
  190. package/src/renderer/layout-engine.ts +2 -57
  191. package/src/renderer/layout.ts +7 -13
  192. package/src/renderer/markdown-inline.ts +2 -1
  193. package/src/renderer/markdown-table.ts +17 -12
  194. package/src/renderer/markdown.ts +92 -34
  195. package/src/renderer/masked-entry-modal.ts +78 -0
  196. package/src/renderer/mcp-workspace.ts +166 -203
  197. package/src/renderer/model-workspace.ts +381 -436
  198. package/src/renderer/notifications-modal.ts +116 -0
  199. package/src/renderer/onboarding/onboarding-wizard.ts +178 -588
  200. package/src/renderer/process-view.ts +192 -0
  201. package/src/renderer/profile-picker-modal.ts +58 -110
  202. package/src/renderer/search-overlay.ts +6 -3
  203. package/src/renderer/selection-modal-overlay.ts +87 -327
  204. package/src/renderer/semantic-diff.ts +2 -2
  205. package/src/renderer/session-chips.ts +102 -0
  206. package/src/renderer/session-picker-modal.ts +177 -297
  207. package/src/renderer/settings-modal-connections.ts +14 -23
  208. package/src/renderer/settings-modal-context.ts +367 -0
  209. package/src/renderer/settings-modal-helpers.ts +6 -11
  210. package/src/renderer/settings-modal.ts +322 -726
  211. package/src/renderer/shell-surface.ts +277 -175
  212. package/src/renderer/shortcuts-overlay.ts +217 -0
  213. package/src/renderer/status-line.ts +475 -0
  214. package/src/renderer/surface-compose.ts +174 -0
  215. package/src/renderer/surface-kit-extra.ts +303 -0
  216. package/src/renderer/surface-kit-list.ts +366 -0
  217. package/src/renderer/surface-kit-parts.ts +360 -0
  218. package/src/renderer/surface-kit.ts +568 -0
  219. package/src/renderer/syntax-highlighter.ts +280 -197
  220. package/src/renderer/syntax-theme.ts +51 -0
  221. package/src/renderer/system-message.ts +0 -1
  222. package/src/renderer/term-caps.ts +42 -1
  223. package/src/renderer/terminal-bg-probe.ts +116 -19
  224. package/src/renderer/terminal-palette-probe.ts +322 -0
  225. package/src/renderer/terminal-palette-reader.ts +183 -0
  226. package/src/renderer/terminal-palette.ts +58 -0
  227. package/src/renderer/theme-mode-config.ts +16 -1
  228. package/src/renderer/theme.ts +234 -207
  229. package/src/renderer/thinking.ts +0 -1
  230. package/src/renderer/throbber.ts +188 -0
  231. package/src/renderer/toast-center.ts +119 -0
  232. package/src/renderer/ui-factory.ts +39 -602
  233. package/src/renderer/ui-primitives.ts +8 -25
  234. package/src/renderer/usage-modal.ts +353 -0
  235. package/src/renderer/user-message.ts +58 -0
  236. package/src/renderer/voice-capture-chip.ts +53 -0
  237. package/src/runtime/bootstrap-command-context.ts +0 -5
  238. package/src/runtime/bootstrap-command-parts.ts +38 -89
  239. package/src/runtime/bootstrap-core.ts +13 -15
  240. package/src/runtime/bootstrap-hook-bridge.ts +12 -21
  241. package/src/runtime/bootstrap-notifier-sync.ts +17 -1
  242. package/src/runtime/bootstrap-shell.ts +16 -50
  243. package/src/runtime/bootstrap.ts +14 -14
  244. package/src/runtime/client/fleet-union.ts +5 -5
  245. package/src/runtime/client/hosted-exit.ts +2 -2
  246. package/src/runtime/client/hosted-sessions.ts +1 -1
  247. package/src/runtime/client/spine-adoption.ts +1 -1
  248. package/src/runtime/context-accounting-source.ts +5 -4
  249. package/src/runtime/index.ts +1 -0
  250. package/src/runtime/notification-dispatch.ts +102 -29
  251. package/src/runtime/recovery-offer-wiring.ts +4 -1
  252. package/src/runtime/recovery-prompt.ts +31 -15
  253. package/src/runtime/runtime-services-types.ts +10 -6
  254. package/src/runtime/services.ts +17 -12
  255. package/src/runtime/session-continuity-hints.ts +2 -5
  256. package/src/runtime/session-inbound-inputs.ts +1 -1
  257. package/src/runtime/session-resume-liveness-confirm.ts +14 -2
  258. package/src/runtime/startup-typeahead-gate.ts +103 -0
  259. package/src/runtime/store/domains/domain-read-matrix.ts +0 -1
  260. package/src/runtime/store/domains/index.ts +0 -8
  261. package/src/runtime/store/selectors/index.ts +0 -28
  262. package/src/runtime/store/state.ts +0 -8
  263. package/src/runtime/ui-services.ts +4 -6
  264. package/src/runtime/usage-tracker.ts +348 -0
  265. package/src/runtime/wrfc-persistence.ts +4 -4
  266. package/src/shell/blocking-input.ts +92 -2
  267. package/src/shell/recovery-input-helpers.ts +1 -1
  268. package/src/shell/session-views.ts +539 -0
  269. package/src/shell/status-report.ts +86 -0
  270. package/src/shell/transcript-scroll.ts +68 -0
  271. package/src/shell/ui-openers.ts +84 -172
  272. package/src/shell/view-openers.ts +205 -0
  273. package/src/tools/wrfc-agent-guard.ts +22 -2
  274. package/src/utils/format-duration.ts +4 -8
  275. package/src/utils/splash-lines.ts +9 -1
  276. package/src/utils/terminal-width.ts +56 -0
  277. package/src/verification/verification-ledger.ts +18 -16
  278. package/src/{panels → views}/agent-inspector-shared.ts +2 -2
  279. package/src/{panels → views}/builtin-modals.ts +22 -21
  280. package/src/views/builtin-views.ts +135 -0
  281. package/src/{panels → views}/confirm-state.ts +9 -9
  282. package/src/{panels → views}/diff-review-model.ts +10 -3
  283. package/src/{panels → views}/eval-registry.ts +1 -1
  284. package/src/{panels → views}/fleet-acts.ts +49 -85
  285. package/src/{panels/fleet-panel-format.ts → views/fleet-format.ts} +40 -40
  286. package/src/{panels → views}/fleet-gateway.ts +5 -5
  287. package/src/{panels → views}/fleet-observed-render.ts +16 -16
  288. package/src/{panels → views}/fleet-read-model.ts +9 -9
  289. package/src/{panels/fleet-tabs.ts → views/fleet-session-tabs.ts} +13 -13
  290. package/src/{panels → views}/fleet-spawn.ts +33 -49
  291. package/src/{panels → views}/fleet-steer.ts +12 -12
  292. package/src/{panels → views}/fleet-stop.ts +4 -4
  293. package/src/{panels → views}/fleet-transcript.ts +26 -27
  294. package/src/{panels/fleet-panel-worktree-detail.ts → views/fleet-worktree-detail.ts} +7 -7
  295. package/src/{panels → views}/hosted-session-feed.ts +6 -6
  296. package/src/views/modal-surface-registry.ts +41 -0
  297. package/src/{panels → views}/modals/devices-modal.ts +2 -2
  298. package/src/{panels → views}/modals/hooks-modal.ts +13 -13
  299. package/src/{panels → views}/modals/keybindings-modal.ts +19 -27
  300. package/src/{panels → views}/modals/knowledge-modal.ts +6 -5
  301. package/src/{panels → views}/modals/local-auth-modal.ts +22 -21
  302. package/src/{panels → views}/modals/marketplace-modal.ts +11 -11
  303. package/src/{panels → views}/modals/memory-modal.ts +8 -8
  304. package/src/{panels → views}/modals/modal-surface-helpers.ts +15 -5
  305. package/src/{panels → views}/modals/pairing-modal.ts +9 -9
  306. package/src/{panels → views}/modals/planning-modal.ts +15 -20
  307. package/src/{panels → views}/modals/plugins-modal.ts +6 -6
  308. package/src/{panels → views}/modals/policy-modal.ts +13 -5
  309. package/src/{panels → views}/modals/provider-health-modal.ts +4 -4
  310. package/src/{panels → views}/modals/remote-modal.ts +3 -3
  311. package/src/{panels → views}/modals/sandbox-modal.ts +4 -4
  312. package/src/{panels → views}/modals/security-modal.ts +22 -16
  313. package/src/{panels → views}/modals/services-modal.ts +3 -3
  314. package/src/{panels → views}/modals/settings-sync-modal.ts +4 -4
  315. package/src/{panels → views}/modals/skills-modal.ts +9 -9
  316. package/src/{panels → views}/modals/subscription-modal.ts +3 -3
  317. package/src/{panels → views}/modals/work-plan-modal.ts +10 -13
  318. package/src/views/notifications-feed.ts +314 -0
  319. package/src/{panels → views}/polish-core.ts +38 -36
  320. package/src/{panels → views}/polish-tables.ts +35 -26
  321. package/src/{panels → views}/polish.ts +101 -95
  322. package/src/{panels → views}/project-planning-answer-actions.ts +3 -3
  323. package/src/{panels → views}/provider-health-routes.ts +14 -14
  324. package/src/{panels → views}/search-focus.ts +8 -8
  325. package/src/{panels → views}/session-maintenance.ts +11 -11
  326. package/src/views/skills-discovery.ts +159 -0
  327. package/src/views/view-deps.ts +80 -0
  328. package/src/views/view-panel-adapter.ts +53 -0
  329. package/src/{panels → views}/workstream-graph-render.ts +1 -1
  330. package/src/core/conversation-fold.ts +0 -101
  331. package/src/core/conversation-turn-structure.ts +0 -464
  332. package/src/core/conversation-user-receipts.ts +0 -42
  333. package/src/input/commands/operator-panel-runtime.ts +0 -186
  334. package/src/input/panel-integration-actions.ts +0 -33
  335. package/src/input/panel-mouse-geometry.ts +0 -97
  336. package/src/input/panel-paste-flood-guard.ts +0 -86
  337. package/src/panels/base-panel.ts +0 -301
  338. package/src/panels/builtin/agent.ts +0 -27
  339. package/src/panels/builtin/development.ts +0 -92
  340. package/src/panels/builtin/knowledge.ts +0 -12
  341. package/src/panels/builtin/operations.ts +0 -177
  342. package/src/panels/builtin/session.ts +0 -81
  343. package/src/panels/builtin/shared.ts +0 -357
  344. package/src/panels/builtin-panels.ts +0 -28
  345. package/src/panels/cost-tracker-panel.ts +0 -746
  346. package/src/panels/diff-panel.ts +0 -797
  347. package/src/panels/diff-review-panel.ts +0 -395
  348. package/src/panels/expandable-list-panel.ts +0 -189
  349. package/src/panels/fleet-deep-link.ts +0 -31
  350. package/src/panels/fleet-panel.ts +0 -799
  351. package/src/panels/git-panel.ts +0 -800
  352. package/src/panels/hosted-session-panel.ts +0 -221
  353. package/src/panels/index.ts +0 -21
  354. package/src/panels/local-auth-panel.ts +0 -478
  355. package/src/panels/notifications-feed.ts +0 -127
  356. package/src/panels/notifications-panel.ts +0 -160
  357. package/src/panels/panel-confirm-overlay.ts +0 -72
  358. package/src/panels/panel-manager.ts +0 -791
  359. package/src/panels/plugins-panel.ts +0 -371
  360. package/src/panels/scrollable-list-panel.ts +0 -498
  361. package/src/panels/skills-panel.ts +0 -454
  362. package/src/panels/token-budget-panel.ts +0 -688
  363. package/src/panels/types.ts +0 -191
  364. package/src/panels/wrfc-panel-format.ts +0 -133
  365. package/src/renderer/diff-view.ts +0 -134
  366. package/src/renderer/fleet-tab-strip.ts +0 -56
  367. package/src/renderer/footer-tips.ts +0 -53
  368. package/src/renderer/fullscreen-primitives.ts +0 -146
  369. package/src/renderer/fullscreen-workspace.ts +0 -194
  370. package/src/renderer/hint-grammar.ts +0 -52
  371. package/src/renderer/modal-factory.ts +0 -480
  372. package/src/renderer/overlay-box.ts +0 -154
  373. package/src/renderer/overlay-viewport.ts +0 -104
  374. package/src/renderer/panel-composite.ts +0 -145
  375. package/src/renderer/panel-workspace-bar.ts +0 -54
  376. package/src/renderer/process-indicator.ts +0 -163
  377. package/src/renderer/tab-strip.ts +0 -222
  378. package/src/renderer/text-layout.ts +0 -31
  379. package/src/renderer/tool-call.ts +0 -276
  380. package/src/renderer/tool-result-expanded-lines.ts +0 -48
  381. package/src/renderer/tool-result-summary.ts +0 -219
  382. package/src/runtime/diagnostics/panels/index.ts +0 -21
  383. package/src/runtime/diagnostics/panels/ops.ts +0 -156
  384. package/src/runtime/perf/panel-contracts.ts +0 -32
  385. package/src/runtime/store/domains/panels.ts +0 -117
  386. /package/src/{panels → views}/memory-consolidation-gateway.ts +0 -0
  387. /package/src/{panels → views}/modals/modal-theme.ts +0 -0
  388. /package/src/{panels → views}/provider-health-domains.ts +0 -0
@@ -1,237 +1,200 @@
1
+ /**
2
+ * renderMcpWorkspace, the MCP server workspace as a kit modal with a list and a detail side:
3
+ * the server and action list on the left (✦ servers / actions groups, a ● or
4
+ * ○ connection marker, the selected row as the gradient) and the selected
5
+ * row's details in an element inset on the right (connection, trust, launch
6
+ * command, allowed paths and hosts, quarantine, the server's tools, and the
7
+ * status line). The add/edit form and the remove confirmation are sub-views
8
+ * of the same modal: Esc returns from them to the browser.
9
+ */
10
+
1
11
  import type { McpWorkspace, McpWorkspaceRow } from '../input/mcp-workspace.ts';
2
- import type { Line } from '@pellux/goodvibes-sdk/platform/types';
3
- import { wrapText } from '../utils/terminal-width.ts';
4
- import { GLYPHS } from './ui-primitives.ts';
12
+ import { activeTokens } from './theme.ts';
5
13
  import {
6
- getFullscreenWorkspaceMetrics,
7
- padDisplay,
8
- renderFullscreenWorkspace,
9
- stableWindow,
10
- WORKSPACE_PALETTE as PALETTE,
11
- type WorkspaceRow,
12
- } from './fullscreen-workspace.ts';
14
+ beginModal,
15
+ finishModal,
16
+ searchRow,
17
+ scrollCountText,
18
+ type KitHint,
19
+ type ModalFrame,
20
+ type SurfaceLayer,
21
+ } from './surface-kit.ts';
22
+ import { drawList, type KitRow } from './surface-kit-list.ts';
23
+ import { inset } from './surface-kit-parts.ts';
24
+ import { drawTextBlock, splitListDetail, textBlockHeight, type TextLine } from './surface-kit-extra.ts';
13
25
 
14
26
  function statusColor(text: string): string {
15
- if (text.includes('failed') || text.includes('Save failed') || text.includes('Remove failed')) return PALETTE.bad;
16
- if (text.includes('attention') || text.includes('quarantine')) return PALETTE.warn;
17
- return PALETTE.muted;
18
- }
19
-
20
- function connectedColor(connected: boolean): string {
21
- return connected ? PALETTE.good : PALETTE.warn;
27
+ const t = activeTokens();
28
+ if (text.includes('failed')) return t.error;
29
+ if (text.includes('attention') || text.includes('quarantine')) return t.warning;
30
+ return t.textMuted;
22
31
  }
23
32
 
24
- function rowLabel(row: McpWorkspaceRow): string {
25
- if (row.type === 'server') return `${row.server.name} (${row.server.source})`;
26
- return row.label;
27
- }
28
-
29
- function rowDetail(row: McpWorkspaceRow): string {
30
- if (row.type === 'server') {
31
- const server = row.server;
32
- return `${server.connected ? 'connected' : 'offline'} · ${server.role} · ${server.trustMode} · schema ${server.freshness}`;
33
+ function browseRows(workspace: McpWorkspace): KitRow[] {
34
+ const t = activeTokens();
35
+ const rows: KitRow[] = [];
36
+ const visible = workspace.visibleRows;
37
+ const servers = visible.filter((row) => row.type === 'server');
38
+ rows.push({ header: 'Servers', headerRight: `${workspace.servers.filter((s) => s.connected).length}/${workspace.servers.length} connected` });
39
+ if (servers.length === 0) {
40
+ rows.push({ label: workspace.servers.length === 0 ? 'No configured servers' : 'No servers match', muted: true });
33
41
  }
34
- return row.detail;
35
- }
36
-
37
- function buildLeftRows(workspace: McpWorkspace, height: number): WorkspaceRow[] {
38
- const rendered: WorkspaceRow[] = [];
39
- let selectedRenderedIndex = 0;
40
- let sawServerGroup = false;
41
- let sawActionGroup = false;
42
-
43
- workspace.rows.forEach((row, rowIndex) => {
44
- if (row.type === 'server' && !sawServerGroup) {
45
- rendered.push({ text: 'SERVERS', kind: 'group', bold: true });
46
- sawServerGroup = true;
42
+ let sawAction = false;
43
+ visible.forEach((row: McpWorkspaceRow, index) => {
44
+ const selected = workspace.mode === 'browse' && index === workspace.selectedIndex;
45
+ if (row.type === 'server') {
46
+ rows.push({
47
+ label: row.server.name,
48
+ desc: row.server.source,
49
+ mark: row.server.connected ? '●' : '○',
50
+ markFg: row.server.connected ? t.success : t.warning,
51
+ right: row.server.quarantineReason ? 'quarantined' : row.server.connected ? '' : 'offline',
52
+ rightFg: row.server.quarantineReason ? t.error : t.textFaint,
53
+ selected,
54
+ });
55
+ return;
47
56
  }
48
- if (row.type === 'action' && !sawActionGroup) {
49
- if (!sawServerGroup) rendered.push({ text: 'SERVERS', kind: 'group', bold: true });
50
- if (workspace.servers.length === 0) rendered.push({ text: ' No configured servers', kind: 'item', fg: PALETTE.dim, dim: true });
51
- rendered.push({ text: 'ACTIONS', kind: 'group', bold: true });
52
- sawActionGroup = true;
57
+ if (!sawAction) {
58
+ rows.push({ header: 'Actions' });
59
+ sawAction = true;
53
60
  }
54
-
55
- const selected = workspace.mode === 'browse' && rowIndex === workspace.selectedIndex;
56
- if (selected) selectedRenderedIndex = rendered.length;
57
- const marker = selected ? GLYPHS.navigation.selected : row.type === 'server' ? (row.server.connected ? '✓' : '•') : '+';
58
- rendered.push({
59
- text: ` ${marker} ${rowLabel(row)}`,
60
- selected,
61
- kind: 'item',
62
- fg: row.type === 'server' ? connectedColor(row.server.connected) : PALETTE.info,
63
- bold: selected || row.type === 'action',
64
- });
61
+ rows.push({ label: row.label, mark: '+', markFg: t.info, selected });
65
62
  });
66
-
67
- const visible = Math.max(1, height);
68
- const window = stableWindow(rendered.length, selectedRenderedIndex, visible);
69
- const rows = rendered.slice(window.start, window.end);
70
- if (window.start > 0 && rows.length > 0) {
71
- rows[0] = { text: `${GLYPHS.navigation.moreAbove} ${window.start} more row(s) above`, kind: 'more', fg: PALETTE.dim, dim: true };
72
- }
73
- if (window.end < rendered.length && rows.length > 0) {
74
- rows[rows.length - 1] = { text: `${GLYPHS.navigation.moreBelow} ${rendered.length - window.end} more row(s) below`, kind: 'more', fg: PALETTE.dim, dim: true };
75
- }
76
- while (rows.length < height) rows.push({ text: '', kind: 'empty' });
77
- return rows.slice(0, height);
63
+ return rows;
78
64
  }
79
65
 
80
- function selectedDetailLines(workspace: McpWorkspace, width: number): WorkspaceRow[] {
81
- const lines: string[] = [];
82
- if (workspace.mode === 'form') {
83
- const field = workspace.formFields[workspace.formIndex];
84
- lines.push(
85
- workspace.editingServerName ? `Editing server: ${workspace.editingServerName}` : 'Adding an MCP server',
86
- 'Write a server through the SDK MCP config manager, then reload the live runtime without restarting the TUI.',
87
- field ? `${field.label}: ${field.help}` : '',
88
- 'Project scope writes to this workspace. Global scope writes to your user MCP config. External Claude/Desktop config files are shown but not edited here.',
89
- );
90
- } else if (workspace.mode === 'delete-confirm') {
66
+ function detailLines(workspace: McpWorkspace): TextLine[] {
67
+ const t = activeTokens();
68
+ const title = (text: string): TextLine => ({ text, style: { fg: t.text, bold: true } });
69
+ const body = (text: string): TextLine => ({ text, style: { fg: t.textMuted } });
70
+ const lines: TextLine[] = [];
71
+ const selected = workspace.selectedRow;
72
+ if (!selected) {
73
+ lines.push(body(workspace.query ? `No rows match "${workspace.query}".` : 'No MCP rows available.'));
74
+ } else if (selected.type === 'action') {
75
+ lines.push(title(selected.label), body(selected.detail));
76
+ } else {
77
+ const server = selected.server;
91
78
  lines.push(
92
- `Remove configured server: ${workspace.editingServerName ?? '(unknown)'}`,
93
- 'This removes the selected writable project/global config entry and reloads MCP runtime state.',
94
- 'Press y to remove, n or Esc to cancel.',
79
+ title(server.name),
80
+ { text: `${server.connected ? 'connected' : 'offline'} · ${server.source} · schema ${server.freshness}`, style: { fg: server.connected ? t.success : t.warning } },
81
+ { text: '' },
82
+ body(`Role ${server.role} · trust ${server.trustMode}`),
83
+ body(`Command ${server.command ? `${server.command}${server.args?.length ? ` ${server.args.join(' ')}` : ''}` : '(runtime only; no launch config found)'}`),
84
+ body(`Allowed paths ${server.allowedPaths.length > 0 ? server.allowedPaths.join(', ') : '(none)'}`),
85
+ body(`Allowed hosts ${server.allowedHosts.length > 0 ? server.allowedHosts.join(', ') : '(none)'}`),
95
86
  );
96
- } else {
97
- const selected = workspace.selectedRow;
98
- if (!selected) lines.push('No MCP rows available.');
99
- else if (selected.type === 'action') lines.push(selected.label, selected.detail);
100
- else {
101
- const server = selected.server;
102
- lines.push(
103
- server.name,
104
- `Connected: ${server.connected ? 'yes' : 'no'} Source: ${server.source} Schema: ${server.freshness}`,
105
- `Role: ${server.role} Trust: ${server.trustMode}`,
106
- `Command: ${server.command ? `${server.command}${server.args?.length ? ` ${server.args.join(' ')}` : ''}` : '(runtime only; no launch config found)'}`,
107
- `Allowed paths: ${server.allowedPaths.length > 0 ? server.allowedPaths.join(', ') : '(none)'}`,
108
- `Allowed hosts: ${server.allowedHosts.length > 0 ? server.allowedHosts.join(', ') : '(none)'}`,
109
- ...(server.quarantineReason ? [`Quarantine: ${server.quarantineReason}${server.quarantineDetail ? ` - ${server.quarantineDetail}` : ''}`] : []),
110
- );
87
+ if (server.quarantineReason) {
88
+ lines.push({ text: `Quarantine: ${server.quarantineReason}${server.quarantineDetail ? ` - ${server.quarantineDetail}` : ''}`, style: { fg: t.error } });
111
89
  }
112
90
  }
113
-
114
- lines.push('', `Status: ${workspace.status}`);
115
- return lines.flatMap((text, index): WorkspaceRow[] => {
116
- if (text === '') return [{ text: '', dim: true }];
117
- return wrapText(text, Math.max(1, width)).map((wrapped, wrapIndex): WorkspaceRow => ({
118
- text: wrapped,
119
- fg: index === 0 ? PALETTE.title : text.startsWith('Status:') ? statusColor(workspace.status) : PALETTE.text,
120
- bold: index === 0 && wrapIndex === 0,
121
- dim: text.length === 0,
122
- }));
123
- });
124
- }
125
-
126
- function buildFormRows(workspace: McpWorkspace, width: number, height: number): WorkspaceRow[] {
127
- const rows: WorkspaceRow[] = [];
128
- const fields = workspace.formFields;
129
- const labelWidth = Math.min(24, Math.max(14, Math.floor(width * 0.24)));
130
- const valueWidth = Math.max(12, width - labelWidth - 20);
131
- rows.push({
132
- text: ` ${padDisplay('Field', labelWidth)} ${padDisplay('Value', valueWidth)} ${padDisplay('Edit', 10)}`,
133
- fg: PALETTE.muted,
134
- bold: true,
135
- });
136
-
137
- const visible = Math.max(1, height - 2);
138
- const window = stableWindow(fields.length, workspace.formIndex, visible);
139
- if (window.start > 0) rows.push({ text: `${GLYPHS.navigation.moreAbove} ${window.start} more field(s) above`, kind: 'more', fg: PALETTE.dim, dim: true });
140
- for (let index = window.start; index < window.end; index += 1) {
141
- const field = fields[index]!;
142
- const selected = index === workspace.formIndex;
143
- const marker = selected ? GLYPHS.navigation.selected : ' ';
144
- const value = field.id === 'save' || field.id === 'cancel'
145
- ? field.help
146
- : field.value.length > 0 ? field.value : '(empty)';
147
- rows.push({
148
- text: `${marker} ${padDisplay(field.label, labelWidth)} ${padDisplay(value, valueWidth)} ${padDisplay(field.editable ? 'text' : 'cycle/action', 12)}`,
149
- selected,
150
- fg: field.id === 'save' ? PALETTE.good : field.id === 'cancel' ? PALETTE.warn : field.editable ? PALETTE.text : PALETTE.info,
151
- bold: selected,
152
- });
153
- }
154
- if (window.end < fields.length) rows.push({ text: `${GLYPHS.navigation.moreBelow} ${fields.length - window.end} more field(s) below`, kind: 'more', fg: PALETTE.dim, dim: true });
155
- while (rows.length < height) rows.push({ text: '', kind: 'empty' });
156
- return rows.slice(0, height);
157
- }
158
-
159
- function buildToolRows(workspace: McpWorkspace, width: number, height: number): WorkspaceRow[] {
91
+ lines.push({ text: '' });
160
92
  const server = workspace.selectedServer?.name;
161
93
  const tools = server ? workspace.tools.filter((tool) => tool.serverName === server) : workspace.tools;
162
- const toolWidth = Math.min(36, Math.max(18, Math.floor(width * 0.34)));
163
- const serverWidth = Math.min(24, Math.max(12, Math.floor(width * 0.20)));
164
- const descriptionWidth = Math.max(12, width - toolWidth - serverWidth - 8);
165
- const label = workspace.loadingTools
166
- ? 'Tools: loading...'
167
- : server
168
- ? `Tools for ${server}: ${tools.length}`
169
- : `Tools: ${tools.length}`;
170
- const rows: WorkspaceRow[] = [
171
- { text: label, fg: PALETTE.subtitle, bold: true },
172
- { text: ` ${padDisplay('Tool', toolWidth)} ${padDisplay('Server', serverWidth)} ${padDisplay('Description', descriptionWidth)}`, fg: PALETTE.muted, bold: true },
173
- ];
174
-
94
+ lines.push(title(workspace.loadingTools ? 'Tools: loading' : server ? `Tools for ${server}: ${tools.length}` : `Tools: ${tools.length}`));
175
95
  if (tools.length === 0) {
176
- rows.push({
177
- text: workspace.loadingTools ? 'Loading tool list from connected MCP servers.' : 'No tools cached for the selected server. Press t to refresh.',
178
- fg: PALETTE.muted,
179
- dim: true,
180
- });
96
+ lines.push(body(workspace.loadingTools ? 'Loading the tool list from connected MCP servers.' : 'No tools cached for the selected server. Press t to refresh.'));
181
97
  } else {
182
- for (const tool of tools.slice(0, Math.max(0, height - rows.length))) {
183
- rows.push({
184
- text: ` ${padDisplay(tool.toolName, toolWidth)} ${padDisplay(tool.serverName, serverWidth)} ${padDisplay(tool.description ?? '', descriptionWidth)}`,
185
- fg: PALETTE.text,
186
- });
98
+ for (const tool of tools) {
99
+ lines.push({ text: `${tool.toolName}${server ? '' : ` (${tool.serverName})`}${tool.description ? ` ${tool.description}` : ''}`, style: { fg: t.textMuted } });
187
100
  }
188
101
  }
102
+ return lines;
103
+ }
189
104
 
190
- while (rows.length < height) rows.push({ text: '', kind: 'empty' });
191
- return rows.slice(0, height);
105
+ function formRows(workspace: McpWorkspace): KitRow[] {
106
+ const t = activeTokens();
107
+ return workspace.formFields.map((field, index) => {
108
+ const selected = index === workspace.formIndex;
109
+ const isAction = field.id === 'save' || field.id === 'cancel';
110
+ const value = isAction ? undefined : field.value.length > 0 ? field.value : '(empty)';
111
+ return {
112
+ label: field.label,
113
+ desc: value !== undefined && selected && field.editable ? `${field.value}▏` : value,
114
+ right: !isAction && !field.editable ? '←→' : undefined,
115
+ labelFg: field.id === 'save' ? t.success : field.id === 'cancel' ? t.warning : undefined,
116
+ bold: isAction,
117
+ selected,
118
+ };
119
+ });
192
120
  }
193
121
 
194
- function buildDeleteRows(workspace: McpWorkspace, height: number): WorkspaceRow[] {
195
- const rows: WorkspaceRow[] = [
196
- { text: `${GLYPHS.navigation.selected} Confirm remove ${workspace.editingServerName ?? '(unknown)'}`, selected: true, fg: PALETTE.bad, bold: true },
197
- { text: ' Cancel and return to MCP server browser', fg: PALETTE.muted },
122
+ function formLines(workspace: McpWorkspace): TextLine[] {
123
+ const t = activeTokens();
124
+ const field = workspace.formFields[workspace.formIndex];
125
+ // The selected field's help leads: it is what the user needs right now.
126
+ return [
127
+ ...(field ? [{ text: field.label, style: { fg: t.text, bold: true } }, { text: field.help, style: { fg: t.textMuted } }, { text: '' }] : []),
128
+ { text: workspace.editingServerName ? `Editing server: ${workspace.editingServerName}` : 'Adding an MCP server', style: { fg: t.text, bold: true } },
129
+ { text: 'Writes a server through the SDK MCP config manager, then reloads the live runtime without restarting the TUI.', style: { fg: t.textMuted } },
130
+ { text: '' },
131
+ { text: 'Project scope writes to this workspace. Global scope writes to your user MCP config. External Claude/Desktop config files are shown but not edited here.', style: { fg: t.textFaint } },
198
132
  ];
199
- while (rows.length < height) rows.push({ text: '', kind: 'empty' });
200
- return rows.slice(0, height);
201
133
  }
202
134
 
203
- function buildControlRows(workspace: McpWorkspace, width: number, height: number): WorkspaceRow[] {
204
- if (workspace.mode === 'form') return buildFormRows(workspace, width, height);
205
- if (workspace.mode === 'delete-confirm') return buildDeleteRows(workspace, height);
206
- return buildToolRows(workspace, width, height);
135
+ function hintsFor(workspace: McpWorkspace): KitHint[] {
136
+ if (workspace.mode === 'form') return [['↑↓', 'field'], ['←→', 'cycle'], ['type', 'edit'], ['⏎', 'save or cancel row'], ['esc', 'back']];
137
+ if (workspace.mode === 'delete-confirm') return [['y', 'remove'], ['n', 'cancel'], ['esc', 'back']];
138
+ return [['↑↓', 'move'], ['⏎', 'edit or run'], ['a', 'add'], ['d', 'remove'], ['r', 'reload'], ['t', 'tools']];
207
139
  }
208
140
 
209
- function footerText(workspace: McpWorkspace): string {
210
- if (workspace.mode === 'form') return 'Focus server form · Up/Down field · Left/Right cycle · Type edit · Enter save/cancel row · Esc back';
211
- if (workspace.mode === 'delete-confirm') return 'Focus remove confirmation · y confirm · n/Esc cancel';
212
- return 'Focus MCP workspace · Up/Down choose · Enter edit/action · a add · d remove · r reload · t tools · Esc close';
141
+ function drawDetailInset(f: ModalFrame, split: ReturnType<typeof splitListDetail>, lines: readonly TextLine[]): void {
142
+ const t = activeTokens();
143
+ const p = inset(f.canvas, split.detailX, split.detailY, split.detailW, split.detailH);
144
+ const width = p.r - p.l + 1;
145
+ const need = textBlockHeight(lines, width);
146
+ const room = p.bottom - p.top + 1;
147
+ if (need <= room) {
148
+ drawTextBlock(f.canvas, p.l, p.top, width, lines, p.bottom);
149
+ return;
150
+ }
151
+ // Keep the last inset row for an honest count of what did not fit.
152
+ drawTextBlock(f.canvas, p.l, p.top, width, lines, p.bottom - 1);
153
+ f.canvas.put(p.l, p.bottom, `${need - room + 1} more lines`, { fg: t.textFaint });
213
154
  }
214
155
 
215
- export function renderMcpWorkspace(workspace: McpWorkspace, width: number, height: number): Line[] {
216
- const metrics = getFullscreenWorkspaceMetrics({ width, height });
217
- const connected = workspace.servers.filter((server) => server.connected).length;
218
- const stateLabel = workspace.mode === 'browse' ? 'Browse' : workspace.mode === 'form' ? 'Edit Server' : 'Confirm Remove';
219
- const mainHeader = workspace.mode === 'form'
220
- ? 'MCP server form'
221
- : workspace.mode === 'delete-confirm'
222
- ? 'Remove MCP server'
223
- : `Servers ${connected}/${workspace.servers.length} connected · Tools ${workspace.tools.length}`;
156
+ export function renderMcpWorkspace(workspace: McpWorkspace, screenWidth: number, screenHeight: number): SurfaceLayer {
157
+ const t = activeTokens();
158
+ const crumbs = workspace.mode === 'form'
159
+ ? [workspace.editingServerName ? 'Edit server' : 'Add server']
160
+ : workspace.mode === 'delete-confirm' ? ['Remove server'] : [];
161
+ const f = beginModal(screenWidth, screenHeight, { title: 'MCP servers', crumbs, hints: hintsFor(workspace) });
162
+
163
+ // Status sits under both sides, wrapped in full.
164
+ const status: TextLine[] = [{ text: `Status: ${workspace.status}`, style: { fg: statusColor(workspace.status) } }];
165
+ const statusRows = textBlockHeight(status, f.r - f.l + 1);
166
+ const bodyBottom = f.bottom - statusRows - 1;
167
+ drawTextBlock(f.canvas, f.l, bodyBottom + 2, f.r - f.l + 1, status, f.bottom);
168
+
169
+ let top = f.top;
170
+ if (workspace.mode === 'browse') {
171
+ const total = workspace.rows.length;
172
+ searchRow(f, top, workspace.query, 'Filter servers and actions', workspace.query ? `${workspace.visibleRows.length} of ${total}` : `${workspace.tools.length} tools`);
173
+ top += 2;
174
+ }
224
175
 
225
- return renderFullscreenWorkspace({
226
- width,
227
- height,
228
- title: 'MCP Workspace / Servers',
229
- stateLabel,
230
- leftHeader: 'Servers',
231
- mainHeader,
232
- leftRows: buildLeftRows(workspace, metrics.bodyRows),
233
- contextRows: selectedDetailLines(workspace, metrics.contextWidth),
234
- controlRows: buildControlRows(workspace, metrics.contextWidth, metrics.controlRows),
235
- footer: footerText(workspace),
236
- });
176
+ if (workspace.mode === 'delete-confirm') {
177
+ const split = splitListDetail(f.l, f.r, top, bodyBottom, 0.45, 3);
178
+ drawList(f.canvas, {
179
+ rows: [
180
+ { label: `Remove ${workspace.editingServerName ?? '(unknown)'}`, danger: true, selected: true, mark: '✕', markFg: t.error, right: 'y' },
181
+ { label: 'Cancel and return to the server browser', right: 'n' },
182
+ ],
183
+ top: split.top, bottom: split.bottom, x0: split.x0, x1: split.x1,
184
+ });
185
+ drawDetailInset(f, split, [
186
+ { text: `Remove configured server: ${workspace.editingServerName ?? '(unknown)'}`, style: { fg: t.text, bold: true } },
187
+ { text: 'This removes the selected writable project or global config entry and reloads the MCP runtime.', style: { fg: t.textMuted } },
188
+ { text: '' },
189
+ { text: 'Press y to remove, n or Esc to cancel.', style: { fg: t.warning } },
190
+ ]);
191
+ return finishModal(f);
192
+ }
193
+
194
+ const rows = workspace.mode === 'form' ? formRows(workspace) : browseRows(workspace);
195
+ const split = splitListDetail(f.l, f.r, top, bodyBottom, 0.45);
196
+ const res = drawList(f.canvas, { rows, top: split.top, bottom: split.bottom, x0: split.x0, x1: split.x1 });
197
+ f.hintRight = scrollCountText(res.above, res.below);
198
+ drawDetailInset(f, split, workspace.mode === 'form' ? formLines(workspace) : detailLines(workspace));
199
+ return finishModal(f);
237
200
  }