@pellux/goodvibes-tui 2.0.21 → 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 +58 -0
  2. package/README.md +16 -16
  3. package/docs/README.md +4 -4
  4. package/docs/foundation-artifacts/operator-contract.json +1 -1
  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
@@ -21,6 +21,11 @@ export class BookmarkModal {
21
21
  public scrollOffset = 0;
22
22
  /** Max visible list rows. */
23
23
  public visibleRows = BookmarkModal.DEFAULT_VISIBLE_ROWS;
24
+ /**
25
+ * The always-live search row's query. Narrows the list by key and label;
26
+ * selectedIndex indexes the narrowed list (visibleEntries).
27
+ */
28
+ public query = '';
24
29
 
25
30
  public constructor(private readonly bookmarkManager: BookmarkManager) {}
26
31
 
@@ -29,6 +34,7 @@ export class BookmarkModal {
29
34
  */
30
35
  open(): void {
31
36
  this.entries = this.bookmarkManager.list();
37
+ this.query = '';
32
38
  this.selectedIndex = 0;
33
39
  this.scrollOffset = 0;
34
40
  this.active = true;
@@ -36,6 +42,21 @@ export class BookmarkModal {
36
42
 
37
43
  close(): void {
38
44
  this.active = false;
45
+ this.query = '';
46
+ }
47
+
48
+ /** The entries the search row lets through, in order. */
49
+ get visibleEntries(): BookmarkEntry[] {
50
+ const q = this.query.trim().toLowerCase();
51
+ if (!q) return this.entries;
52
+ return this.entries.filter((entry) => entry.key.toLowerCase().includes(q) || entry.label.toLowerCase().includes(q));
53
+ }
54
+
55
+ /** Replace the query; the selection moves to the first match. */
56
+ setQuery(query: string): void {
57
+ this.query = query;
58
+ this.selectedIndex = 0;
59
+ this.scrollOffset = 0;
39
60
  }
40
61
 
41
62
  setVisibleRows(rows: number): void {
@@ -44,19 +65,21 @@ export class BookmarkModal {
44
65
  }
45
66
 
46
67
  moveUp(): void {
47
- if (this.entries.length === 0) return;
48
- this.selectedIndex = (this.selectedIndex - 1 + this.entries.length) % this.entries.length;
68
+ const n = this.visibleEntries.length;
69
+ if (n === 0) return;
70
+ this.selectedIndex = (this.selectedIndex - 1 + n) % n;
49
71
  this._clampScroll();
50
72
  }
51
73
 
52
74
  moveDown(): void {
53
- if (this.entries.length === 0) return;
54
- this.selectedIndex = (this.selectedIndex + 1) % this.entries.length;
75
+ const n = this.visibleEntries.length;
76
+ if (n === 0) return;
77
+ this.selectedIndex = (this.selectedIndex + 1) % n;
55
78
  this._clampScroll();
56
79
  }
57
80
 
58
81
  getSelected(): BookmarkEntry | null {
59
- return this.entries[this.selectedIndex] ?? null;
82
+ return this.visibleEntries[this.selectedIndex] ?? null;
60
83
  }
61
84
 
62
85
  /**
@@ -69,10 +92,11 @@ export class BookmarkModal {
69
92
  this.bookmarkManager.toggle(entry.key); // toggle off = remove
70
93
  this.entries = this.bookmarkManager.list();
71
94
  // Clamp selectedIndex after removal
72
- if (this.entries.length === 0) {
95
+ const n = this.visibleEntries.length;
96
+ if (n === 0) {
73
97
  this.selectedIndex = 0;
74
- } else if (this.selectedIndex >= this.entries.length) {
75
- this.selectedIndex = this.entries.length - 1;
98
+ } else if (this.selectedIndex >= n) {
99
+ this.selectedIndex = n - 1;
76
100
  }
77
101
  this._clampScroll();
78
102
  return entry;
@@ -109,7 +133,7 @@ export class BookmarkModal {
109
133
  } else if (this.selectedIndex >= this.scrollOffset + visRows) {
110
134
  this.scrollOffset = this.selectedIndex - visRows + 1;
111
135
  }
112
- const maxOffset = Math.max(0, this.entries.length - visRows);
136
+ const maxOffset = Math.max(0, this.visibleEntries.length - visRows);
113
137
  this.scrollOffset = Math.max(0, Math.min(this.scrollOffset, maxOffset));
114
138
  }
115
139
  }
@@ -0,0 +1,251 @@
1
+ /**
2
+ * changes-git.ts, the git reads and writes behind the Changes modal.
3
+ *
4
+ * Every spawn captures stdout and stderr: a child left with inherited stdio
5
+ * would write straight to the TUI's terminal, outside the renderer. Nothing
6
+ * here throws into the render loop; failures come back as text to show.
7
+ */
8
+
9
+ import { isAbsolute, join, relative } from 'node:path';
10
+ import { GitService } from '@pellux/goodvibes-sdk/platform/git';
11
+ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
12
+
13
+ /** Which diff the modal shows. */
14
+ export type ChangesSource = 'session' | 'working' | 'staged' | 'head';
15
+
16
+ export const CHANGES_SOURCES: readonly ChangesSource[] = ['session', 'working', 'staged', 'head'];
17
+
18
+ export const CHANGES_SOURCE_LABELS: Readonly<Record<ChangesSource, string>> = {
19
+ session: 'this session',
20
+ working: 'not staged',
21
+ staged: 'staged',
22
+ head: 'all changes vs HEAD',
23
+ };
24
+
25
+ interface RunResult { readonly out: string; readonly err: string; readonly ok: boolean }
26
+
27
+ /**
28
+ * Pin the a/ and b/ path prefixes: a user's diff.mnemonicPrefix or
29
+ * diff.noprefix setting would otherwise turn paths into w/src/x.ts or
30
+ * src/x.ts, which the file list, hunk staging and the semantic reader all
31
+ * misread.
32
+ */
33
+ const DIFF_PREFIX_ARGS: readonly string[] = ['--src-prefix=a/', '--dst-prefix=b/'];
34
+
35
+ async function run(args: string[], cwd: string, stdin?: string): Promise<RunResult> {
36
+ try {
37
+ const proc = Bun.spawn(['git', ...args], { cwd, stdout: 'pipe', stderr: 'pipe', stdin: stdin !== undefined ? 'pipe' : 'ignore' });
38
+ if (stdin !== undefined && proc.stdin) {
39
+ proc.stdin.write(stdin);
40
+ await proc.stdin.end();
41
+ }
42
+ const [out, err] = await Promise.all([new Response(proc.stdout).text(), new Response(proc.stderr).text()]);
43
+ const code = await proc.exited;
44
+ return { out, err: err.trim(), ok: code === 0 };
45
+ } catch (error) {
46
+ return { out: '', err: summarizeError(error), ok: false };
47
+ }
48
+ }
49
+
50
+ export function isGitRepo(cwd: string): boolean {
51
+ return GitService.isGitRepo(cwd);
52
+ }
53
+
54
+ export interface LoadedDiff {
55
+ readonly raw: string;
56
+ /** What the diff is, in words ("this session: 3 files vs HEAD"). */
57
+ readonly label: string;
58
+ readonly error: string | null;
59
+ }
60
+
61
+ /** Untracked files larger than this show as added with a note instead of every line. */
62
+ export const MAX_UNTRACKED_BYTES = 256 * 1024;
63
+ /** Untracked files with more lines than this show as added with a note instead of every line. */
64
+ export const MAX_UNTRACKED_LINES = 4000;
65
+ /** At most this many untracked files are read into one diff; the label counts the rest. */
66
+ export const MAX_UNTRACKED_FILES = 200;
67
+
68
+ /**
69
+ * A new-file chunk with no hunks and a note line in place of the content. The
70
+ * note line starts with "# ", which no git header line does; parseChanges
71
+ * reads it into ChangeFile.note.
72
+ */
73
+ function noteChunk(path: string, note: string): string {
74
+ return `diff --git a/${path} b/${path}\nnew file mode 100644\n# ${note}\n`;
75
+ }
76
+
77
+ /** One untracked file as an all-added diff chunk (paths relative to the repo root). */
78
+ async function untrackedChunk(repoRoot: string, path: string): Promise<string> {
79
+ let size = 0;
80
+ try {
81
+ size = Bun.file(join(repoRoot, path)).size;
82
+ } catch {
83
+ size = 0;
84
+ }
85
+ if (size > MAX_UNTRACKED_BYTES) return noteChunk(path, `large new file (${Math.round(size / 1024)} KB), not shown line by line`);
86
+ // --no-index against /dev/null is git's own new-file patch: binary detection,
87
+ // "new file mode", "--- /dev/null", and a hunk staging can apply as is.
88
+ const result = await run(['diff', '--no-index', '--no-color', ...DIFF_PREFIX_ARGS, '--', '/dev/null', path], repoRoot);
89
+ // --no-index exits 1 when the files differ, which is the expected case here.
90
+ if (!result.out.trim()) return result.err ? noteChunk(path, `could not read: ${result.err.split('\n')[0]}`) : noteChunk(path, 'empty new file');
91
+ const lines = result.out.split('\n').length;
92
+ if (lines > MAX_UNTRACKED_LINES) return noteChunk(path, `large new file (${lines} lines), not shown line by line`);
93
+ return result.out.endsWith('\n') ? result.out : `${result.out}\n`;
94
+ }
95
+
96
+ /**
97
+ * Untracked, non-ignored files as all-added diff chunks, one per file. With
98
+ * `only` set, just the untracked files among those paths (the session's own
99
+ * new files); otherwise every untracked file in the repository.
100
+ */
101
+ export async function loadUntrackedDiff(cwd: string, only?: readonly string[]): Promise<{ raw: string; count: number; skipped: number }> {
102
+ if (only && only.length === 0) return { raw: '', count: 0, skipped: 0 };
103
+ const top = await run(['rev-parse', '--show-toplevel'], cwd);
104
+ if (!top.ok) return { raw: '', count: 0, skipped: 0 };
105
+ const repoRoot = top.out.trim() || cwd;
106
+ // A session path outside this repository would fail the whole listing; leave it out.
107
+ const specs = only ? only.filter((path) => !isAbsolute(path) || path.startsWith(`${repoRoot}/`)) : [':/'];
108
+ if (specs.length === 0) return { raw: '', count: 0, skipped: 0 };
109
+ const listed = await run(['ls-files', '--others', '--exclude-standard', '--full-name', '-z', '--', ...specs], cwd);
110
+ if (!listed.ok) return { raw: '', count: 0, skipped: 0 };
111
+ const paths = listed.out.split('\0').filter((path) => path.length > 0).sort();
112
+ const shown = paths.slice(0, MAX_UNTRACKED_FILES);
113
+ const chunks: string[] = [];
114
+ // A few at a time: one git process per file, never hundreds at once.
115
+ for (let i = 0; i < shown.length; i += 8) {
116
+ chunks.push(...await Promise.all(shown.slice(i, i + 8).map((path) => untrackedChunk(repoRoot, path))));
117
+ }
118
+ return { raw: chunks.join(''), count: shown.length, skipped: paths.length - shown.length };
119
+ }
120
+
121
+ /**
122
+ * The diff for a source. `sessionFiles` are the files this session edited.
123
+ * Untracked files are part of every source except "staged": they show as
124
+ * added files with their whole content (the session source takes only the
125
+ * untracked files this session wrote).
126
+ */
127
+ export async function loadDiff(cwd: string, source: ChangesSource, sessionFiles: readonly string[]): Promise<LoadedDiff> {
128
+ let args: string[];
129
+ let label: string;
130
+ let untrackedOnly: readonly string[] | undefined;
131
+ if (source === 'working') {
132
+ args = ['diff', '--no-color', ...DIFF_PREFIX_ARGS];
133
+ label = 'not staged';
134
+ } else if (source === 'staged') {
135
+ args = ['diff', '--cached', '--no-color', ...DIFF_PREFIX_ARGS];
136
+ label = 'staged';
137
+ } else if (source === 'session' && sessionFiles.length > 0) {
138
+ args = ['diff', '--no-color', ...DIFF_PREFIX_ARGS, 'HEAD', '--', ...sessionFiles];
139
+ label = `this session · ${sessionFiles.length} file${sessionFiles.length === 1 ? '' : 's'} vs HEAD`;
140
+ untrackedOnly = sessionFiles;
141
+ } else {
142
+ args = ['diff', '--no-color', ...DIFF_PREFIX_ARGS, 'HEAD'];
143
+ label = source === 'session' ? 'no files tracked this session · all changes vs HEAD' : 'all changes vs HEAD';
144
+ }
145
+ const [result, untracked] = await Promise.all([
146
+ run(args, cwd),
147
+ source === 'staged' ? Promise.resolve({ raw: '', count: 0, skipped: 0 }) : loadUntrackedDiff(cwd, untrackedOnly),
148
+ ]);
149
+ if (!result.ok) return { raw: '', label, error: result.err || 'git diff failed' };
150
+ const total = untracked.count + untracked.skipped;
151
+ if (total > 0) {
152
+ label += ` · ${total} untracked`;
153
+ if (untracked.skipped > 0) label += ` (${untracked.skipped} not shown)`;
154
+ }
155
+ const tracked = result.out && !result.out.endsWith('\n') ? `${result.out}\n` : result.out;
156
+ return { raw: tracked + untracked.raw, label, error: null };
157
+ }
158
+
159
+ export interface CommitEntry {
160
+ readonly hash: string;
161
+ readonly message: string;
162
+ readonly date: string;
163
+ }
164
+
165
+ export async function loadRecentCommits(cwd: string, count = 10): Promise<CommitEntry[]> {
166
+ try {
167
+ const entries = await new GitService(cwd).log(count);
168
+ return entries.map((entry) => ({ hash: entry.hash, message: entry.message, date: entry.date }));
169
+ } catch {
170
+ return [];
171
+ }
172
+ }
173
+
174
+ /** A commit's patch (works for root commits too). */
175
+ export async function loadCommitDiff(cwd: string, hash: string): Promise<LoadedDiff> {
176
+ const result = await run(['show', '--no-color', '--format=', '--patch', ...DIFF_PREFIX_ARGS, hash], cwd);
177
+ const label = `commit ${hash.slice(0, 7)}`;
178
+ if (!result.ok) return { raw: '', label, error: result.err || 'git show failed' };
179
+ return { raw: result.out, label, error: null };
180
+ }
181
+
182
+ export interface RepoSummary {
183
+ readonly branch: string;
184
+ readonly staged: number;
185
+ readonly unstaged: number;
186
+ }
187
+
188
+ export async function loadRepoSummary(cwd: string): Promise<RepoSummary | null> {
189
+ try {
190
+ const git = new GitService(cwd);
191
+ const [status, branch] = await Promise.all([git.status(), git.branch()]);
192
+ let staged = 0;
193
+ let unstaged = 0;
194
+ for (const f of status.files) {
195
+ if (f.index === '?' && f.working_dir === '?') { unstaged++; continue; }
196
+ if (f.index !== ' ') staged++;
197
+ if (f.working_dir !== ' ') unstaged++;
198
+ }
199
+ return { branch: branch.current || (branch.detached ? 'detached' : 'no branch'), staged, unstaged };
200
+ } catch {
201
+ return null;
202
+ }
203
+ }
204
+
205
+ /** Stage (or with reverse, unstage) exactly one hunk. */
206
+ export async function applyHunkToIndex(cwd: string, patch: string, reverse: boolean): Promise<string | null> {
207
+ const args = ['apply', '--cached', '--recount', ...(reverse ? ['-R'] : []), '-'];
208
+ const result = await run(args, cwd, patch);
209
+ return result.ok ? null : (result.err || 'git apply failed');
210
+ }
211
+
212
+ /** Stage or unstage a whole file. */
213
+ export async function setFileStaged(cwd: string, path: string, staged: boolean): Promise<string | null> {
214
+ const result = staged ? await run(['add', '--', path], cwd) : await run(['reset', '-q', '--', path], cwd);
215
+ return result.ok ? null : (result.err || `git ${staged ? 'add' : 'reset'} failed`);
216
+ }
217
+
218
+ export async function commitStaged(cwd: string, message: string): Promise<{ hash: string; error: string | null }> {
219
+ try {
220
+ const result = await new GitService(cwd).commit(message);
221
+ return { hash: result.hash, error: null };
222
+ } catch (error) {
223
+ return { hash: '', error: summarizeError(error) };
224
+ }
225
+ }
226
+
227
+ export function initRepo(cwd: string): string | null {
228
+ const result = GitService.initRepo(cwd);
229
+ return result.success ? null : (result.error ?? 'git init failed');
230
+ }
231
+
232
+ /**
233
+ * The semantic changes in one file (functions, classes, imports added,
234
+ * removed or changed), comparing `ref` against the file on disk. Null when it
235
+ * cannot be computed (a new file, an unsupported language, no repo).
236
+ */
237
+ export async function loadSemanticDiff(cwd: string, filePath: string, ref: string): Promise<import('../renderer/semantic-diff.ts').SemanticDiff | null> {
238
+ try {
239
+ const top = await run(['rev-parse', '--show-toplevel'], cwd);
240
+ const repoRoot = top.ok ? top.out.trim() || cwd : cwd;
241
+ const absPath = filePath.startsWith('/') ? filePath : join(repoRoot, filePath);
242
+ const repoRel = filePath.startsWith('/') ? relative(repoRoot, filePath) : filePath;
243
+ const before = await run(['show', `${ref}:${repoRel}`], repoRoot);
244
+ if (!before.ok) return null;
245
+ const after = await Bun.file(absPath).text();
246
+ const { computeSemanticDiff } = await import('../renderer/semantic-diff.ts');
247
+ return await computeSemanticDiff(filePath, before.out, after);
248
+ } catch {
249
+ return null;
250
+ }
251
+ }