@pellux/goodvibes-tui 2.0.21 → 2.1.1

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 (391) hide show
  1. package/CHANGELOG.md +65 -1
  2. package/README.md +30 -29
  3. package/docs/README.md +8 -5
  4. package/docs/foundation-artifacts/operator-contract.json +1 -1
  5. package/package.json +28 -19
  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-reference.ts +2 -2
  52. package/src/input/command-registry.ts +77 -35
  53. package/src/input/commands/acp-runtime.ts +19 -9
  54. package/src/input/commands/channel-runtime.ts +2 -2
  55. package/src/input/commands/ci-runtime.ts +1 -1
  56. package/src/input/commands/codebase-runtime.ts +1 -1
  57. package/src/input/commands/config.ts +7 -1
  58. package/src/input/commands/context-window.ts +25 -19
  59. package/src/input/commands/control-room-runtime.ts +18 -18
  60. package/src/input/commands/conversation-runtime.ts +0 -1
  61. package/src/input/commands/cost-runtime.ts +29 -20
  62. package/src/input/commands/diff-runtime.ts +35 -181
  63. package/src/input/commands/eval.ts +1 -1
  64. package/src/input/commands/experience-runtime.ts +3 -7
  65. package/src/input/commands/fresh-conversation.ts +86 -0
  66. package/src/input/commands/git-runtime.ts +5 -22
  67. package/src/input/commands/graph-runtime.ts +1 -1
  68. package/src/input/commands/guidance-runtime.ts +1 -1
  69. package/src/input/commands/health-runtime.ts +8 -9
  70. package/src/input/commands/hooks-runtime.ts +2 -2
  71. package/src/input/commands/hosted-runtime.ts +3 -3
  72. package/src/input/commands/incident-runtime.ts +3 -3
  73. package/src/input/commands/intelligence-runtime.ts +6 -9
  74. package/src/input/commands/knowledge.ts +2 -2
  75. package/src/input/commands/legacy-panel-command.ts +57 -0
  76. package/src/input/commands/local-auth-runtime.ts +8 -9
  77. package/src/input/commands/local-provider-runtime.ts +7 -4
  78. package/src/input/commands/local-runtime.ts +12 -15
  79. package/src/input/commands/local-setup-review.ts +1 -1
  80. package/src/input/commands/local-setup.ts +13 -19
  81. package/src/input/commands/memory.ts +1 -1
  82. package/src/input/commands/notify-runtime.ts +9 -0
  83. package/src/input/commands/operator-runtime.ts +10 -12
  84. package/src/input/commands/permissions-provenance.ts +3 -3
  85. package/src/input/commands/planning-runtime.ts +7 -7
  86. package/src/input/commands/platform-sandbox-runtime.ts +2 -2
  87. package/src/input/commands/platform-services-runtime.ts +1 -1
  88. package/src/input/commands/policy-dispatch.ts +2 -2
  89. package/src/input/commands/policy.ts +3 -3
  90. package/src/input/commands/provider.ts +1 -1
  91. package/src/input/commands/remote-runtime.ts +1 -1
  92. package/src/input/commands/review-runtime.ts +39 -94
  93. package/src/input/commands/rewind-runtime.ts +81 -108
  94. package/src/input/commands/runtime-services.ts +15 -17
  95. package/src/input/commands/services-runtime.ts +2 -2
  96. package/src/input/commands/session-content.ts +21 -5
  97. package/src/input/commands/session-workflow.ts +13 -38
  98. package/src/input/commands/settings-sync-runtime.ts +2 -2
  99. package/src/input/commands/shell-core.ts +19 -4
  100. package/src/input/commands/skills-runtime.ts +2 -2
  101. package/src/input/commands/status-runtime.ts +13 -0
  102. package/src/input/commands/subscription-runtime.ts +1 -1
  103. package/src/input/commands/tasks-runtime.ts +2 -8
  104. package/src/input/commands/work-plan-runtime.ts +4 -4
  105. package/src/input/commands/workstream-attempts.ts +24 -47
  106. package/src/input/commands/workstream-runtime.ts +2 -2
  107. package/src/input/commands/worktree-runtime.ts +3 -3
  108. package/src/input/commands.ts +3 -1
  109. package/src/input/composer-editor.ts +44 -0
  110. package/src/input/concealed-input.ts +1 -1
  111. package/src/input/config-modal-types.ts +20 -8
  112. package/src/input/config-modal.ts +106 -105
  113. package/src/input/confirm-dialog-types.ts +28 -0
  114. package/src/input/confirm-dialog.ts +70 -0
  115. package/src/input/delete-key-policy.ts +3 -3
  116. package/src/input/feed-context-factory.ts +15 -16
  117. package/src/input/handler-command-route.ts +7 -42
  118. package/src/input/handler-content-actions.ts +16 -15
  119. package/src/input/handler-feed-routes.ts +26 -211
  120. package/src/input/handler-feed.ts +68 -95
  121. package/src/input/handler-interactions.ts +5 -3
  122. package/src/input/handler-modal-routes.ts +215 -216
  123. package/src/input/handler-modal-stack.ts +81 -11
  124. package/src/input/handler-modal-token-routes.ts +4 -0
  125. package/src/input/handler-onboarding.ts +0 -1
  126. package/src/input/handler-picker-routes.ts +65 -107
  127. package/src/input/handler-session-view-route.ts +149 -0
  128. package/src/input/handler-shortcuts.ts +16 -154
  129. package/src/input/handler-types.ts +4 -2
  130. package/src/input/handler-ui-state.ts +45 -15
  131. package/src/input/handler-work-tree-route.ts +123 -0
  132. package/src/input/handler.ts +25 -70
  133. package/src/input/keybindings.ts +16 -71
  134. package/src/input/masked-entry-modal.ts +117 -0
  135. package/src/input/mcp-workspace.ts +48 -17
  136. package/src/input/model-picker-filter.ts +8 -0
  137. package/src/input/model-picker-open.ts +99 -0
  138. package/src/input/model-picker-types.ts +1 -1
  139. package/src/input/model-picker.ts +92 -9
  140. package/src/input/notifications-modal.ts +123 -0
  141. package/src/input/overlay-filter.ts +26 -0
  142. package/src/input/profile-picker-modal.ts +37 -8
  143. package/src/input/search.ts +3 -3
  144. package/src/input/selection-modal-queue.ts +2 -0
  145. package/src/input/selection-modal.ts +28 -16
  146. package/src/input/session-picker-modal.ts +33 -8
  147. package/src/input/settings-modal-activation.ts +7 -2
  148. package/src/input/settings-modal-data.ts +11 -6
  149. package/src/input/settings-modal-types.ts +1 -1
  150. package/src/input/settings-modal.ts +20 -20
  151. package/src/input/submission-intent.ts +1 -1
  152. package/src/input/submission-router.ts +4 -9
  153. package/src/input/surface-modal-host.ts +110 -0
  154. package/src/input/theme-settings-actions.ts +82 -0
  155. package/src/input/usage-modal.ts +183 -0
  156. package/src/input/views.ts +97 -0
  157. package/src/main.ts +175 -165
  158. package/src/permissions/hunk-selection.ts +1 -1
  159. package/src/permissions/prompt-card.ts +511 -0
  160. package/src/permissions/prompt.ts +59 -622
  161. package/src/renderer/agents-modal.ts +310 -0
  162. package/src/renderer/autocomplete-overlay.ts +42 -220
  163. package/src/renderer/back-to-bottom.ts +80 -0
  164. package/src/renderer/block-actions-overlay.ts +30 -48
  165. package/src/renderer/bookmark-modal.ts +48 -79
  166. package/src/renderer/changes-modal.ts +325 -0
  167. package/src/renderer/code-block.ts +184 -106
  168. package/src/renderer/command-palette.ts +93 -0
  169. package/src/renderer/composer-fragments.ts +4 -4
  170. package/src/renderer/composer.ts +146 -0
  171. package/src/renderer/compositor.ts +26 -160
  172. package/src/renderer/config-modal.ts +185 -81
  173. package/src/renderer/confirm-dialog.ts +50 -0
  174. package/src/renderer/context-inspector.ts +116 -154
  175. package/src/renderer/context-status-hint.ts +4 -4
  176. package/src/renderer/conversation-layout.ts +20 -0
  177. package/src/renderer/conversation-overlays.ts +94 -84
  178. package/src/renderer/conversation-surface.ts +19 -38
  179. package/src/renderer/diff-tint.ts +98 -0
  180. package/src/renderer/file-picker-overlay.ts +63 -90
  181. package/src/renderer/footer-targets.ts +38 -0
  182. package/src/renderer/git-status.ts +3 -4
  183. package/src/renderer/header-line.ts +140 -117
  184. package/src/renderer/help-overlay.ts +133 -219
  185. package/src/renderer/history-search-overlay.ts +15 -12
  186. package/src/renderer/lane-graph/bead.ts +800 -0
  187. package/src/renderer/lane-graph/glyphs.ts +134 -0
  188. package/src/renderer/lane-graph/layout.ts +248 -0
  189. package/src/renderer/lane-graph/paint.ts +537 -0
  190. package/src/renderer/lane-graph/semantic-memo.ts +77 -0
  191. package/src/renderer/layout-engine.ts +2 -57
  192. package/src/renderer/layout.ts +7 -13
  193. package/src/renderer/markdown-inline.ts +2 -1
  194. package/src/renderer/markdown-table.ts +17 -12
  195. package/src/renderer/markdown.ts +92 -34
  196. package/src/renderer/masked-entry-modal.ts +78 -0
  197. package/src/renderer/mcp-workspace.ts +166 -203
  198. package/src/renderer/model-workspace.ts +381 -436
  199. package/src/renderer/notifications-modal.ts +116 -0
  200. package/src/renderer/onboarding/onboarding-wizard.ts +178 -588
  201. package/src/renderer/process-view.ts +192 -0
  202. package/src/renderer/profile-picker-modal.ts +58 -110
  203. package/src/renderer/search-overlay.ts +6 -3
  204. package/src/renderer/selection-modal-overlay.ts +87 -327
  205. package/src/renderer/semantic-diff.ts +2 -2
  206. package/src/renderer/session-chips.ts +102 -0
  207. package/src/renderer/session-picker-modal.ts +177 -297
  208. package/src/renderer/settings-modal-connections.ts +14 -23
  209. package/src/renderer/settings-modal-context.ts +367 -0
  210. package/src/renderer/settings-modal-helpers.ts +6 -11
  211. package/src/renderer/settings-modal.ts +322 -726
  212. package/src/renderer/shell-surface.ts +277 -175
  213. package/src/renderer/shortcuts-overlay.ts +217 -0
  214. package/src/renderer/status-line.ts +475 -0
  215. package/src/renderer/surface-compose.ts +174 -0
  216. package/src/renderer/surface-kit-extra.ts +303 -0
  217. package/src/renderer/surface-kit-list.ts +366 -0
  218. package/src/renderer/surface-kit-parts.ts +360 -0
  219. package/src/renderer/surface-kit.ts +568 -0
  220. package/src/renderer/syntax-highlighter.ts +280 -197
  221. package/src/renderer/syntax-theme.ts +51 -0
  222. package/src/renderer/system-message.ts +0 -1
  223. package/src/renderer/term-caps.ts +42 -1
  224. package/src/renderer/terminal-bg-probe.ts +116 -19
  225. package/src/renderer/terminal-palette-probe.ts +322 -0
  226. package/src/renderer/terminal-palette-reader.ts +183 -0
  227. package/src/renderer/terminal-palette.ts +58 -0
  228. package/src/renderer/theme-mode-config.ts +16 -1
  229. package/src/renderer/theme.ts +234 -207
  230. package/src/renderer/thinking.ts +0 -1
  231. package/src/renderer/throbber.ts +188 -0
  232. package/src/renderer/toast-center.ts +119 -0
  233. package/src/renderer/ui-factory.ts +39 -602
  234. package/src/renderer/ui-primitives.ts +8 -25
  235. package/src/renderer/usage-modal.ts +353 -0
  236. package/src/renderer/user-message.ts +58 -0
  237. package/src/renderer/voice-capture-chip.ts +53 -0
  238. package/src/runtime/bootstrap-command-context.ts +0 -5
  239. package/src/runtime/bootstrap-command-parts.ts +38 -89
  240. package/src/runtime/bootstrap-core.ts +13 -15
  241. package/src/runtime/bootstrap-hook-bridge.ts +12 -21
  242. package/src/runtime/bootstrap-notifier-sync.ts +17 -1
  243. package/src/runtime/bootstrap-shell.ts +16 -50
  244. package/src/runtime/bootstrap.ts +14 -14
  245. package/src/runtime/client/fleet-union.ts +5 -5
  246. package/src/runtime/client/hosted-exit.ts +2 -2
  247. package/src/runtime/client/hosted-sessions.ts +1 -1
  248. package/src/runtime/client/spine-adoption.ts +1 -1
  249. package/src/runtime/context-accounting-source.ts +5 -4
  250. package/src/runtime/index.ts +1 -0
  251. package/src/runtime/notification-dispatch.ts +102 -29
  252. package/src/runtime/recovery-offer-wiring.ts +4 -1
  253. package/src/runtime/recovery-prompt.ts +31 -15
  254. package/src/runtime/runtime-services-types.ts +10 -6
  255. package/src/runtime/services.ts +17 -12
  256. package/src/runtime/session-continuity-hints.ts +2 -5
  257. package/src/runtime/session-inbound-inputs.ts +1 -1
  258. package/src/runtime/session-resume-liveness-confirm.ts +14 -2
  259. package/src/runtime/startup-typeahead-gate.ts +103 -0
  260. package/src/runtime/store/domains/domain-read-matrix.ts +0 -1
  261. package/src/runtime/store/domains/index.ts +0 -8
  262. package/src/runtime/store/selectors/index.ts +0 -28
  263. package/src/runtime/store/state.ts +0 -8
  264. package/src/runtime/ui-services.ts +4 -6
  265. package/src/runtime/usage-tracker.ts +348 -0
  266. package/src/runtime/wrfc-persistence.ts +4 -4
  267. package/src/shell/blocking-input.ts +92 -2
  268. package/src/shell/recovery-input-helpers.ts +1 -1
  269. package/src/shell/session-views.ts +539 -0
  270. package/src/shell/status-report.ts +86 -0
  271. package/src/shell/transcript-scroll.ts +68 -0
  272. package/src/shell/ui-openers.ts +84 -172
  273. package/src/shell/view-openers.ts +205 -0
  274. package/src/tools/wrfc-agent-guard.ts +22 -2
  275. package/src/utils/format-duration.ts +4 -8
  276. package/src/utils/splash-lines.ts +9 -1
  277. package/src/utils/terminal-width.ts +56 -0
  278. package/src/verification/live-verifier.ts +0 -10
  279. package/src/version.ts +1 -1
  280. package/src/{panels → views}/agent-inspector-shared.ts +2 -2
  281. package/src/{panels → views}/builtin-modals.ts +22 -21
  282. package/src/views/builtin-views.ts +135 -0
  283. package/src/{panels → views}/confirm-state.ts +9 -9
  284. package/src/{panels → views}/diff-review-model.ts +10 -3
  285. package/src/{panels → views}/eval-registry.ts +1 -1
  286. package/src/{panels → views}/fleet-acts.ts +49 -85
  287. package/src/{panels/fleet-panel-format.ts → views/fleet-format.ts} +40 -40
  288. package/src/{panels → views}/fleet-gateway.ts +5 -5
  289. package/src/{panels → views}/fleet-observed-render.ts +16 -16
  290. package/src/{panels → views}/fleet-read-model.ts +9 -9
  291. package/src/{panels/fleet-tabs.ts → views/fleet-session-tabs.ts} +13 -13
  292. package/src/{panels → views}/fleet-spawn.ts +33 -49
  293. package/src/{panels → views}/fleet-steer.ts +12 -12
  294. package/src/{panels → views}/fleet-stop.ts +4 -4
  295. package/src/{panels → views}/fleet-transcript.ts +26 -27
  296. package/src/{panels/fleet-panel-worktree-detail.ts → views/fleet-worktree-detail.ts} +7 -7
  297. package/src/{panels → views}/hosted-session-feed.ts +6 -6
  298. package/src/views/modal-surface-registry.ts +41 -0
  299. package/src/{panels → views}/modals/devices-modal.ts +2 -2
  300. package/src/{panels → views}/modals/hooks-modal.ts +13 -13
  301. package/src/{panels → views}/modals/keybindings-modal.ts +19 -27
  302. package/src/{panels → views}/modals/knowledge-modal.ts +6 -5
  303. package/src/{panels → views}/modals/local-auth-modal.ts +22 -21
  304. package/src/{panels → views}/modals/marketplace-modal.ts +11 -11
  305. package/src/{panels → views}/modals/memory-modal.ts +8 -8
  306. package/src/{panels → views}/modals/modal-surface-helpers.ts +15 -5
  307. package/src/{panels → views}/modals/pairing-modal.ts +9 -9
  308. package/src/{panels → views}/modals/planning-modal.ts +15 -20
  309. package/src/{panels → views}/modals/plugins-modal.ts +6 -6
  310. package/src/{panels → views}/modals/policy-modal.ts +13 -5
  311. package/src/{panels → views}/modals/provider-health-modal.ts +4 -4
  312. package/src/{panels → views}/modals/remote-modal.ts +3 -3
  313. package/src/{panels → views}/modals/sandbox-modal.ts +4 -4
  314. package/src/{panels → views}/modals/security-modal.ts +22 -16
  315. package/src/{panels → views}/modals/services-modal.ts +3 -3
  316. package/src/{panels → views}/modals/settings-sync-modal.ts +4 -4
  317. package/src/{panels → views}/modals/skills-modal.ts +9 -9
  318. package/src/{panels → views}/modals/subscription-modal.ts +3 -3
  319. package/src/{panels → views}/modals/work-plan-modal.ts +10 -13
  320. package/src/views/notifications-feed.ts +314 -0
  321. package/src/{panels → views}/polish-core.ts +38 -36
  322. package/src/{panels → views}/polish-tables.ts +35 -26
  323. package/src/{panels → views}/polish.ts +101 -95
  324. package/src/{panels → views}/project-planning-answer-actions.ts +3 -3
  325. package/src/{panels → views}/provider-health-routes.ts +14 -14
  326. package/src/{panels → views}/search-focus.ts +8 -8
  327. package/src/{panels → views}/session-maintenance.ts +11 -11
  328. package/src/views/skills-discovery.ts +159 -0
  329. package/src/views/view-deps.ts +80 -0
  330. package/src/views/view-panel-adapter.ts +53 -0
  331. package/src/{panels → views}/workstream-graph-render.ts +1 -1
  332. package/src/core/conversation-fold.ts +0 -101
  333. package/src/core/conversation-turn-structure.ts +0 -464
  334. package/src/core/conversation-user-receipts.ts +0 -42
  335. package/src/input/commands/operator-panel-runtime.ts +0 -186
  336. package/src/input/panel-integration-actions.ts +0 -33
  337. package/src/input/panel-mouse-geometry.ts +0 -97
  338. package/src/input/panel-paste-flood-guard.ts +0 -86
  339. package/src/panels/base-panel.ts +0 -301
  340. package/src/panels/builtin/agent.ts +0 -27
  341. package/src/panels/builtin/development.ts +0 -92
  342. package/src/panels/builtin/knowledge.ts +0 -12
  343. package/src/panels/builtin/operations.ts +0 -177
  344. package/src/panels/builtin/session.ts +0 -81
  345. package/src/panels/builtin/shared.ts +0 -357
  346. package/src/panels/builtin-panels.ts +0 -28
  347. package/src/panels/cost-tracker-panel.ts +0 -746
  348. package/src/panels/diff-panel.ts +0 -797
  349. package/src/panels/diff-review-panel.ts +0 -395
  350. package/src/panels/expandable-list-panel.ts +0 -189
  351. package/src/panels/fleet-deep-link.ts +0 -31
  352. package/src/panels/fleet-panel.ts +0 -799
  353. package/src/panels/git-panel.ts +0 -800
  354. package/src/panels/hosted-session-panel.ts +0 -221
  355. package/src/panels/index.ts +0 -21
  356. package/src/panels/local-auth-panel.ts +0 -478
  357. package/src/panels/notifications-feed.ts +0 -127
  358. package/src/panels/notifications-panel.ts +0 -160
  359. package/src/panels/panel-confirm-overlay.ts +0 -72
  360. package/src/panels/panel-manager.ts +0 -791
  361. package/src/panels/plugins-panel.ts +0 -371
  362. package/src/panels/scrollable-list-panel.ts +0 -498
  363. package/src/panels/skills-panel.ts +0 -454
  364. package/src/panels/token-budget-panel.ts +0 -688
  365. package/src/panels/types.ts +0 -191
  366. package/src/panels/wrfc-panel-format.ts +0 -133
  367. package/src/renderer/diff-view.ts +0 -134
  368. package/src/renderer/fleet-tab-strip.ts +0 -56
  369. package/src/renderer/footer-tips.ts +0 -53
  370. package/src/renderer/fullscreen-primitives.ts +0 -146
  371. package/src/renderer/fullscreen-workspace.ts +0 -194
  372. package/src/renderer/hint-grammar.ts +0 -52
  373. package/src/renderer/modal-factory.ts +0 -480
  374. package/src/renderer/overlay-box.ts +0 -154
  375. package/src/renderer/overlay-viewport.ts +0 -104
  376. package/src/renderer/panel-composite.ts +0 -145
  377. package/src/renderer/panel-workspace-bar.ts +0 -54
  378. package/src/renderer/process-indicator.ts +0 -163
  379. package/src/renderer/tab-strip.ts +0 -222
  380. package/src/renderer/text-layout.ts +0 -31
  381. package/src/renderer/tool-call.ts +0 -276
  382. package/src/renderer/tool-result-expanded-lines.ts +0 -48
  383. package/src/renderer/tool-result-summary.ts +0 -219
  384. package/src/runtime/diagnostics/panels/index.ts +0 -21
  385. package/src/runtime/diagnostics/panels/ops.ts +0 -156
  386. package/src/runtime/perf/panel-contracts.ts +0 -32
  387. package/src/runtime/store/domains/panels.ts +0 -117
  388. package/src/verification/verification-ledger.ts +0 -707
  389. /package/src/{panels → views}/memory-consolidation-gateway.ts +0 -0
  390. /package/src/{panels → views}/modals/modal-theme.ts +0 -0
  391. /package/src/{panels → views}/provider-health-domains.ts +0 -0
@@ -1,191 +0,0 @@
1
- import type { Line } from '@pellux/goodvibes-sdk/platform/types';
2
- import type { StatusState } from '../renderer/status-glyphs.ts';
3
- import type { ComponentResourceContract, ComponentHealthState } from '../runtime/perf/panel-contracts.ts';
4
- // Routed through the `@/` alias (not `./panel-manager.ts`) so this foundational
5
- // types module stays a leaf in the relative-import graph the architecture
6
- // cycle-checker walks. Type-only, erased at runtime, no real dependency edge.
7
- import type { PanelManager } from '@/panels/panel-manager.ts';
8
-
9
- /**
10
- * Context passed to a panel's `handlePanelIntegrationAction` hook so it can
11
- * drive cross-panel behavior (e.g. file-explorer opening the preview panel)
12
- * without the input layer needing `instanceof` knowledge of each panel type.
13
- */
14
- export interface PanelIntegrationContext {
15
- readonly panelManager: PanelManager;
16
- readonly executeCommand?: (name: string, args: string[]) => Promise<unknown>;
17
- }
18
-
19
- /**
20
- * A cross-panel deep-link target (item 4): a specific node a panel
21
- * should select + reveal when opened via `PanelManager.open(id, pane, target)`,
22
- * e.g. a Fleet tree node keyed by its process id and (optionally) its
23
- * ProcessKind for disambiguation. Panel-agnostic on purpose, only panels that
24
- * have a "node" concept implement `Panel.receiveDeepLink`.
25
- */
26
- export interface PanelDeepLinkTarget {
27
- readonly id: string;
28
- readonly kind?: string;
29
- }
30
-
31
- /**
32
- * Named logical key identifiers emitted by the input tokenizer.
33
- * These are the ONLY key names that will appear in `handleInput` calls;
34
- * the tokenizer never emits DOM/browser-style names like 'ArrowUp' or 'Enter'.
35
- *
36
- * Printable single-character input is passed through as-is; the `string & {}`
37
- * fallback preserves that handling while making named-key completions discoverable
38
- * in editors and keeping non-null single-character values compatible.
39
- */
40
- export type NamedKey =
41
- | 'up' | 'down' | 'left' | 'right'
42
- | 'home' | 'end' | 'pageup' | 'pagedown'
43
- | 'insert' | 'delete' | 'backspace'
44
- | 'enter' | 'return' | 'escape' | 'space' | 'tab'
45
- | 'f1' | 'f2' | 'f3' | 'f4' | 'f5' | 'f6'
46
- | 'f7' | 'f8' | 'f9' | 'f10' | 'f11' | 'f12';
47
-
48
- /**
49
- * The full key type accepted by `Panel.handleInput`.
50
- *
51
- * Named keys (arrow keys, modifiers, function keys) are represented by
52
- * lowercase `NamedKey` members. Single printable characters are passed
53
- * verbatim via the `string & {}` escape hatch so panels can handle 'j', 'k',
54
- * 'r', etc. without losing type safety on the named members.
55
- */
56
- export type KeyName = NamedKey | (string & {});
57
-
58
- /**
59
- * the former single 'monitoring' bucket held 33 panels pre-merge,
60
- * too coarse for the picker to be useful and too large for any one operator
61
- * mental model. Split into named operator domains so each category holds a
62
- * bounded, coherent set (no category may exceed 10 registrations):
63
- * - providers: provider/model connectivity, cost, and token usage
64
- * - security-policy: auth, policy governance, and isolation posture
65
- * - automation-control: hooks, plugins, marketplace, automation jobs, worktrees
66
- * - incidents-diagnostics: failure review, eval gates, API call debugging
67
- * - runtime-ops: live operator consoles (cockpit, tasks, orchestration, comms)
68
- */
69
- export type PanelCategory =
70
- | 'development'
71
- | 'agent'
72
- | 'session'
73
- | 'ai'
74
- | 'providers'
75
- | 'security-policy'
76
- | 'automation-control'
77
- | 'incidents-diagnostics'
78
- | 'runtime-ops';
79
-
80
- export interface Panel {
81
- id: string;
82
- name: string;
83
- icon: string; // single char for tab bar
84
- category: PanelCategory;
85
-
86
- // Lifecycle
87
- onActivate(): void;
88
- onDeactivate(): void;
89
- onDestroy(): void;
90
-
91
- // Rendering
92
- render(width: number, height: number): Line[];
93
-
94
- // State
95
- isTransient: boolean;
96
- isPinned: boolean;
97
- needsRender: boolean;
98
-
99
- // Dirty-flag contract (activated panel render skipping)
100
- /** Mark this panel as needing a re-render on the next frame. */
101
- invalidate(): void;
102
- /** Called by the compositor after a successful render to clear the dirty flag. */
103
- markRendered(): void;
104
-
105
- // Resource contract (optional, panels may declare resource requirements)
106
- resourceContract?: Readonly<ComponentResourceContract>;
107
-
108
- // Health state (optional, set by ComponentHealthMonitor when panel is registered)
109
- healthState?: Readonly<ComponentHealthState>;
110
-
111
- // Input (optional)
112
- handleInput?(key: KeyName): boolean;
113
-
114
- /**
115
- * Optional: report `true` while this panel has an active inline text
116
- * capture (e.g. a `/`-to-filter or search buffer) that deliberately wants
117
- * every character of a burst, paste, or several keystrokes typed fast
118
- * enough to land in one `input.feed()` call, delivered to `handleInput`
119
- * one at a time, same as it always has. When this returns `false` or is
120
- * not implemented, the input router treats a printable burst as never a
121
- * deliberate single-key panel hotkey and routes it to the composer
122
- * instead of exploding it into per-char `handleInput` calls (see
123
- * `handlePanelFocusToken` in `src/input/handler-feed-routes.ts`).
124
- */
125
- isCapturingTextBurst?(): boolean;
126
-
127
- // Scroll input (optional)
128
- // Positive delta scrolls down; negative delta scrolls up.
129
- handleScroll?(deltaRows: number): boolean;
130
-
131
- /**
132
- * Cross-panel integration hook (optional). Called before the panel's own
133
- * `handleInput` when a navigation/confirm key is pressed, so a panel can
134
- * drive another panel (e.g. open a file in the preview panel). Return `true`
135
- * to consume the key. The legacy `handlePanelIntegrationAction` router in
136
- * `src/input/panel-integration-actions.ts` consults this first, then falls
137
- * back to its built-in `instanceof` routing.
138
- */
139
- handlePanelIntegrationAction?(key: string, ctx: PanelIntegrationContext): boolean;
140
-
141
- /**
142
- * Optional: called by the global Ctrl+X (`panel-close`) shortcut BEFORE it
143
- * closes this panel (handler-shortcuts.ts). Return `true` to consume
144
- * Ctrl+X for an in-panel action instead, e.g. FleetPanel (session
145
- * tabs) detaches its active tab and leaves the panel open. Return `false`
146
- * (or omit this hook) to fall through to the ordinary close behavior.
147
- * Ctrl+X never reaches a panel's own `handleInput` (ctrl/meta combos are
148
- * intercepted earlier, see handlePanelFocusToken in
149
- * src/input/handler-feed-routes.ts), so this is the one seam a panel has
150
- * to intervene before the global shortcut acts.
151
- */
152
- interceptPanelClose?(): boolean;
153
-
154
- /**
155
- * Optional: consume a deep-link target handed in by `PanelManager.open(id,
156
- * pane, target)` (item 4), select + reveal the matching node on the
157
- * panel's next snapshot. A panel without a "node" concept simply doesn't
158
- * implement this (the call is a no-op via optional chaining in
159
- * PanelManager). A panel that DOES implement it owns its own honest
160
- * "not found" surface (e.g. FleetPanel.setError) when the target no longer
161
- * resolves, PanelManager has no transcript/print seam to report on its
162
- * behalf.
163
- */
164
- receiveDeepLink?(target: PanelDeepLinkTarget): void;
165
- }
166
-
167
- export interface PanelRegistration extends Pick<Panel, 'id' | 'name' | 'icon' | 'category'> {
168
- factory: () => Panel;
169
- description: string;
170
- /**
171
- * lifecycle flags. `preload` and `retainOnClose` are independent,
172
- * a panel can eagerly instantiate without being kept alive on close, or be
173
- * kept alive on close without eager bootstrap instantiation. They happened
174
- * to be identical for all builtin panels before this split (both driven by
175
- * a single `preload: true`); that existing 10-panel retained set is
176
- * preserved unchanged by giving each of those 10 registrations both flags.
177
- *
178
- * - `preload`, instantiate this panel during `PanelManager.prewarmRegistered()`
179
- * (called once at bootstrap) so its factory runs and it starts
180
- * accumulating background data (subscriptions, timers) before the user
181
- * ever opens the workspace. Panels without `preload` are instantiated
182
- * lazily on first `open()`.
183
- * - `retainOnClose`, when this panel is closed, keep the live instance in
184
- * `PanelManager`'s retained-panel map (background subscriptions/timers
185
- * keep running) instead of calling `onDestroy()`. Reopening returns the
186
- * same instance with its accumulated state intact. Panels without
187
- * `retainOnClose` are destroyed on close and rebuilt fresh next open.
188
- */
189
- preload?: boolean;
190
- retainOnClose?: boolean;
191
- }
@@ -1,133 +0,0 @@
1
- import { truncateDisplay } from '../utils/terminal-width.ts';
2
- import type { WrfcState, Constraint, ConstraintFinding } from '@pellux/goodvibes-sdk/platform/agents';
3
- import { DEFAULT_PANEL_PALETTE, extendPalette } from './polish.ts';
4
-
5
- // ---------------------------------------------------------------------------
6
- // Colour palette + formatting helpers for the WRFC panel.
7
- //
8
- // Extracted from wrfc-panel.ts to keep that module under the architecture
9
- // line-count cap. Leaf module (only polish + terminal-width + sdk types); the
10
- // panel re-exports the public helpers so ./wrfc-panel.ts stays their import site.
11
- // ---------------------------------------------------------------------------
12
- export const C = extendPalette(DEFAULT_PANEL_PALETTE, {
13
- // WRFC state-machine colours (domain status -- no shared equivalent)
14
- passed: '#22c55e', // green
15
- failed: '#ef4444', // red
16
- reviewing: '#eab308', // yellow
17
- engineering:'#22d3ee', // cyan
18
- fixing: '#f97316', // orange
19
- pending: '#6b7280', // grey
20
- gating: '#a78bfa', // violet
21
- committing: '#38bdf8', // sky
22
- integrating:'#818cf8', // indigo
23
-
24
- // Issue-severity ramp (domain -- no shared equivalent)
25
- issueCrit: '#ef4444',
26
- issueMaj: '#f97316',
27
- issueMin: '#eab308',
28
- issueSug: '#6b7280',
29
-
30
- // Selection + divider chrome with no shared equivalent
31
- selected: '#1e40af', // selection bg
32
- selectedFg: '#f8fafc',
33
- border: '#334155',
34
- });
35
-
36
- // ---------------------------------------------------------------------------
37
- // Helpers
38
- // ---------------------------------------------------------------------------
39
- const SPARKLINE_CHARS = '._-:=+*#';
40
-
41
- export function sparkline(scores: number[], maxScore = 10): string {
42
- if (scores.length === 0) return '';
43
- return scores
44
- .map(s => {
45
- const ratio = Math.max(0, Math.min(1, s / maxScore));
46
- const idx = Math.round(ratio * (SPARKLINE_CHARS.length - 1));
47
- return SPARKLINE_CHARS[idx];
48
- })
49
- .join('');
50
- }
51
-
52
- export function stateColor(state: WrfcState): string {
53
- switch (state) {
54
- case 'passed': return C.passed;
55
- case 'failed': return C.failed;
56
- case 'reviewing': return C.reviewing;
57
- case 'engineering': return C.engineering;
58
- case 'fixing': return C.fixing;
59
- case 'gating':
60
- case 'awaiting_gates': return C.gating;
61
- case 'committing': return C.committing;
62
- case 'integrating': return C.integrating;
63
- default: return C.pending;
64
- }
65
- }
66
-
67
- export function stateLabel(state: WrfcState): string {
68
- switch (state) {
69
- case 'engineering': return 'ENG';
70
- case 'reviewing': return 'REV';
71
- case 'fixing': return 'FIX';
72
- case 'gating': return 'GATE';
73
- case 'awaiting_gates': return 'WAIT';
74
- case 'committing': return 'COMMIT';
75
- case 'integrating': return 'INTG';
76
- case 'passed': return 'PASS';
77
- case 'failed': return 'FAIL';
78
- default: return 'PEND';
79
- }
80
- }
81
-
82
- export function issueColor(severity: string): string {
83
- switch (severity) {
84
- case 'critical': return C.issueCrit;
85
- case 'major': return C.issueMaj;
86
- case 'minor': return C.issueMin;
87
- default: return C.issueSug;
88
- }
89
- }
90
-
91
- export function issuePrefix(severity: string): string {
92
- switch (severity) {
93
- case 'critical': return '[CRIT] ';
94
- case 'major': return '[MAJR] ';
95
- case 'minor': return '[MINR] ';
96
- default: return '[SUGG] ';
97
- }
98
- }
99
-
100
- export function truncate(s: string, max: number): string {
101
- return truncateDisplay(s, max);
102
- }
103
-
104
- // ---------------------------------------------------------------------------
105
- // Constraint helpers
106
- // ---------------------------------------------------------------------------
107
-
108
- /**
109
- * Returns display tag, foreground colour, and dim flag for a single constraint
110
- * based on whether a reviewer finding exists for it.
111
- */
112
- export function constraintStatusMarker(
113
- constraint: Constraint,
114
- findings: ConstraintFinding[] | undefined,
115
- ): { tag: string; fg: string; dim: boolean } {
116
- const finding = findings?.find(f => f.constraintId === constraint.id);
117
- if (!finding) {
118
- return { tag: '[UNV]', fg: C.dim, dim: true };
119
- }
120
- if (finding.satisfied) {
121
- return { tag: '[SAT]', fg: C.good, dim: false };
122
- }
123
- // Unsatisfied, use severity to pick colour and tag text
124
- const sev = finding.severity ?? 'major';
125
- let sevTag: string;
126
- let fg: string;
127
- switch (sev) {
128
- case 'critical': sevTag = '[UNS CRIT]'; fg = C.issueCrit; break;
129
- case 'minor': sevTag = '[UNS MINOR]'; fg = C.issueMin; break;
130
- default: sevTag = '[UNS MAJOR]'; fg = C.issueMaj; break;
131
- }
132
- return { tag: sevTag, fg, dim: false };
133
- }
@@ -1,134 +0,0 @@
1
- import { type Line, type Cell, createStyledCell } from '@pellux/goodvibes-sdk/platform/types';
2
- import { UIFactory } from './ui-factory.ts';
3
- import { getDisplayWidth, padDisplayEnd } from '../utils/terminal-width.ts';
4
- import { DIFF_TONES } from './ui-primitives.ts';
5
-
6
- /**
7
- * renderDiffView - Render a unified diff string as styled Line[].
8
- * '+' lines in green, '-' lines in red, '@@' hunks in blue (DIFF_TONES.hunk,
9
- * shared with diff-panel.ts and git-panel.ts's inline diff).
10
- */
11
- export function renderDiffView(diffText: string, width: number, filename?: string): Line[] {
12
- const lines: Line[] = [];
13
- const BG = '#0a0a0a';
14
-
15
- // Filename header
16
- if (filename) {
17
- const header = ` ≡ ${filename} `;
18
- lines.push(UIFactory.stringToLine(padDisplayEnd(header, width), width, { fg: '#1a1a1a', bg: '#569cd6', bold: true }));
19
- }
20
-
21
- const diffLines = diffText.split('\n');
22
- let oldLineNo = 0;
23
- let newLineNo = 0;
24
-
25
- for (const raw of diffLines) {
26
- if (raw === '') {
27
- const emptyLine = makeFilledLine(width, BG);
28
- lines.push(emptyLine);
29
- continue;
30
- }
31
-
32
- // Hunk header: @@ -old,count +new,count @@
33
- if (raw.startsWith('@@')) {
34
- const hunkMatch = raw.match(/@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
35
- if (hunkMatch) {
36
- oldLineNo = parseInt(hunkMatch[1], 10) - 1;
37
- newLineNo = parseInt(hunkMatch[2], 10) - 1;
38
- }
39
- lines.push(makeStyledLine(raw, width, DIFF_TONES.hunk, '#0f1f1f', false));
40
- continue;
41
- }
42
-
43
- // File headers: --- and +++
44
- if (raw.startsWith('--- ') || raw.startsWith('+++ ')) {
45
- lines.push(makeStyledLine(raw, width, '244', BG, false));
46
- continue;
47
- }
48
-
49
- // Added line
50
- if (raw.startsWith('+')) {
51
- newLineNo++;
52
- const lineLabel = `${String(newLineNo).padStart(4)} `;
53
- const content = raw.slice(1);
54
- lines.push(makeGutterLine('+', lineLabel, content, width, DIFF_TONES.add, '#0a1a0a'));
55
- continue;
56
- }
57
-
58
- // Removed line
59
- if (raw.startsWith('-')) {
60
- oldLineNo++;
61
- const lineLabel = `${String(oldLineNo).padStart(4)} `;
62
- const content = raw.slice(1);
63
- lines.push(makeGutterLine('-', lineLabel, content, width, DIFF_TONES.del, '#1a0a0a'));
64
- continue;
65
- }
66
-
67
- // Context line
68
- if (raw.startsWith(' ') || (!raw.startsWith('\\') && raw.length > 0)) {
69
- oldLineNo++;
70
- newLineNo++;
71
- const lineLabel = `${String(oldLineNo).padStart(4)} `;
72
- const content = raw.startsWith(' ') ? raw.slice(1) : raw;
73
- lines.push(makeGutterLine(' ', lineLabel, content, width, '244', BG));
74
- }
75
- }
76
-
77
- return lines;
78
- }
79
-
80
- /** Build a line with gutter indicator, line number, and content. */
81
- function makeGutterLine(
82
- gutter: string,
83
- lineLabel: string,
84
- content: string,
85
- width: number,
86
- fg: string,
87
- bg: string
88
- ): Line {
89
- const line = makeFilledLine(width, bg);
90
- let cx = 0;
91
-
92
- // Gutter char
93
- line[cx++] = createStyledCell(gutter, { fg, bg, bold: gutter !== ' ' });
94
-
95
- // Line number
96
- for (const ch of lineLabel) {
97
- if (cx >= width) break;
98
- line[cx++] = createStyledCell(ch, { fg: '238', bg, dim: true });
99
- }
100
-
101
- // Content
102
- for (const ch of content) {
103
- if (cx >= width) break;
104
- const cw = getDisplayWidth(ch);
105
- const code = ch.charCodeAt(0);
106
- if (code < 32) { cx++; continue; }
107
- line[cx] = createStyledCell(ch, { fg, bg });
108
- if (cw === 2 && cx + 1 < width) line[cx + 1] = { ...line[cx], char: '' };
109
- cx += cw;
110
- }
111
-
112
- return line;
113
- }
114
-
115
- /** Build a simple styled line from text. */
116
- function makeStyledLine(text: string, width: number, fg: string, bg: string, bold: boolean): Line {
117
- const line = makeFilledLine(width, bg);
118
- let cx = 0;
119
- for (const ch of text) {
120
- if (cx >= width) break;
121
- const cw = getDisplayWidth(ch);
122
- const code = ch.charCodeAt(0);
123
- if (code < 32) { cx++; continue; }
124
- line[cx] = createStyledCell(ch, { fg, bg, bold });
125
- if (cw === 2 && cx + 1 < width) line[cx + 1] = { ...line[cx], char: '' };
126
- cx += cw;
127
- }
128
- return line;
129
- }
130
-
131
- /** Create a line filled with bg color. */
132
- function makeFilledLine(width: number, bg: string): Cell[] {
133
- return Array.from({ length: width }, () => createStyledCell(' ', { bg }));
134
- }
@@ -1,56 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // fleet-tab-strip.ts
3
- //
4
- // Renders FleetPanel's session-tab strip by reusing
5
- // renderTabStrip exactly as panel-workspace-bar.ts does for the workspace
6
- // tab bar. Deliberately a SEPARATE, visually distinct strip from the
7
- // workspace bar: workspace tabs switch PANELS, fleet session tabs switch
8
- // ATTACHED PROCESSES within one panel, conflating their styling would blur
9
- // that distinction for the operator.
10
- // ---------------------------------------------------------------------------
11
-
12
- import type { Line } from '@pellux/goodvibes-sdk/platform/types';
13
- import type { FleetTabsState } from '../panels/fleet-tabs.ts';
14
- import { renderTabStrip, type TabHitRegion } from './tab-strip.ts';
15
- import { UI_TONES } from './ui-primitives.ts';
16
-
17
- const ACTIVE_FG = UI_TONES.fg.primary;
18
- const ACTIVE_BG = UI_TONES.bg.selected;
19
- const INACTIVE_FG = UI_TONES.fg.muted;
20
- const SEPARATOR_FG = UI_TONES.fg.dim;
21
- const LABEL_FG = UI_TONES.fg.secondary;
22
-
23
- /**
24
- * Render the fleet session-tab strip, or `null` when there are no attached
25
- * tabs, the panel omits the strip entirely in that case (root-tab-only),
26
- * which is what keeps the pre-session-tab fleet-panel goldens byte-identical.
27
- */
28
- export function renderFleetTabStrip(
29
- state: FleetTabsState,
30
- width: number,
31
- onLayout?: (regions: readonly TabHitRegion[]) => void,
32
- ): Line | null {
33
- if (state.tabs.length === 0) return null;
34
- return renderTabStrip({
35
- width,
36
- onLayout,
37
- tabs: [
38
- { label: 'Tree', active: state.activeTabIndex === 0 },
39
- ...state.tabs.map((tab, index) => ({
40
- label: tab.label,
41
- active: state.activeTabIndex === index + 1,
42
- })),
43
- ],
44
- prefixLabel: ' SESSIONS ',
45
- style: {
46
- activeFg: ACTIVE_FG,
47
- activeBg: ACTIVE_BG,
48
- activeBold: true,
49
- inactiveFg: INACTIVE_FG,
50
- separatorFg: SEPARATOR_FG,
51
- labelFg: LABEL_FG,
52
- overflowFg: SEPARATOR_FG,
53
- trailingFg: INACTIVE_FG,
54
- },
55
- });
56
- }
@@ -1,53 +0,0 @@
1
- /**
2
- * footer-tips.ts, the persistent discoverability hint shown in the shell
3
- * footer's tip slot.
4
- *
5
- * instead of a single frozen "/help for commands" line, the footer
6
- * surfaces a rotating set of the highest-value affordances (panels, process
7
- * monitor, help, quit). Rotation is *contextual, not timed*: when an agent turn
8
- * is actively running, the process-monitor tip is promoted to the front so the
9
- * operator can jump to F2 while work is in flight. Selection is a pure function
10
- * of context so the footer renders deterministically (golden frames stay
11
- * stable), no wall-clock input.
12
- */
13
-
14
- export interface FooterTipContext {
15
- /** True while an agent turn is actively running (streaming / tools / hooks). */
16
- readonly agentActive: boolean;
17
- }
18
-
19
- /** Composer status labels that mean an agent turn is actively in flight. */
20
- const ACTIVE_TURN_STATUSES = new Set(['preflight', 'streaming', 'tools', 'post-hooks']);
21
-
22
- /** Derive agent-active state from the composer status label passed to the footer. */
23
- export function isAgentActive(composerStatus: string | undefined): boolean {
24
- return composerStatus !== undefined && ACTIVE_TURN_STATUSES.has(composerStatus);
25
- }
26
-
27
- // Ctrl+P is a TOGGLE (open+focus when nothing is open or unfocused,
28
- // hide when the workspace already has focus, see openPanelPicker in
29
- // shell/ui-openers.ts). The bare noun 'panels' undersold that; naming the verb
30
- // keeps the tip honest about what the chord actually does.
31
- const TIP_PANELS = 'Ctrl+P toggle panels';
32
- // F2 now opens the Fleet panel (the process modal was retired), so the
33
- // tip names 'fleet', not 'processes'.
34
- const TIP_PROCESSES = 'F2 fleet';
35
- const TIP_HELP = '? help';
36
- // f: an empty-composer Ctrl+C does NOT quit on the first press, it arms a
37
- // ~1s "press again to exit" confirm, and only a SECOND Ctrl+C within that window
38
- // exits (an intentional accidental-exit guard). The old 'Ctrl+C quit' tip
39
- // implied a single press quits, so two presses seconds apart appeared to do
40
- // nothing. 'Ctrl+C x2 quit' matches the real double-press contract (and the
41
- // help overlay's "Ctrl+C x2 Exit" row).
42
- const TIP_QUIT = 'Ctrl+C x2 quit';
43
-
44
- /**
45
- * Build the footer discoverability tip. Segments join with the shared middle
46
- * dot. Agent-active state promotes the process-monitor tip to the front.
47
- */
48
- export function buildFooterTip(ctx: FooterTipContext): string {
49
- const lead = ctx.agentActive
50
- ? [TIP_PROCESSES, TIP_PANELS, TIP_HELP]
51
- : [TIP_PANELS, TIP_PROCESSES, TIP_HELP];
52
- return [...lead, TIP_QUIT].join(' · ');
53
- }