@pellux/goodvibes-tui 0.18.12 → 0.18.17

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 (196) hide show
  1. package/CHANGELOG.md +172 -0
  2. package/README.md +1 -1
  3. package/docs/foundation-artifacts/operator-contract.json +1 -1
  4. package/package.json +3 -2
  5. package/src/config/index.ts +1 -138
  6. package/src/core/conversation-rendering.ts +3 -3
  7. package/src/core/conversation.ts +176 -423
  8. package/src/core/history.ts +45 -0
  9. package/src/core/orchestrator.ts +3 -735
  10. package/src/core/system-message-router.ts +19 -58
  11. package/src/daemon/cli.ts +82 -6
  12. package/src/input/command-registry.ts +2 -0
  13. package/src/input/commands/control-room-runtime.ts +1 -1
  14. package/src/input/commands/health-runtime.ts +1 -1
  15. package/src/input/commands/local-setup-review.ts +1 -1
  16. package/src/input/commands/platform-access-runtime.ts +1 -1
  17. package/src/input/commands/qrcode-runtime.ts +20 -0
  18. package/src/input/commands/subscription-runtime.ts +1 -1
  19. package/src/input/commands.ts +2 -0
  20. package/src/input/handler-content-actions.ts +2 -2
  21. package/src/input/handler-feed.ts +7 -1
  22. package/src/input/handler-modal-routes.ts +19 -2
  23. package/src/input/handler-modal-token-routes.ts +4 -1
  24. package/src/input/handler-picker-routes.ts +4 -2
  25. package/src/input/handler-ui-state.ts +1 -1
  26. package/src/input/handler.ts +1 -1
  27. package/src/input/model-picker.ts +11 -0
  28. package/src/input/search.ts +1 -1
  29. package/src/input/selection.ts +2 -2
  30. package/src/input/settings-modal.ts +31 -3
  31. package/src/main.ts +1 -1
  32. package/src/panels/agent-inspector-panel.ts +3 -3
  33. package/src/panels/agent-logs-panel.ts +26 -27
  34. package/src/panels/approval-panel.ts +2 -2
  35. package/src/panels/automation-control-panel.ts +3 -3
  36. package/src/panels/base-panel.ts +14 -14
  37. package/src/panels/builtin/operations.ts +1 -1
  38. package/src/panels/builtin/session.ts +67 -1
  39. package/src/panels/builtin/shared.ts +4 -4
  40. package/src/panels/cockpit-panel.ts +2 -2
  41. package/src/panels/communication-panel.ts +3 -3
  42. package/src/panels/context-visualizer-panel.ts +2 -2
  43. package/src/panels/control-plane-panel.ts +3 -3
  44. package/src/panels/cost-tracker-panel.ts +3 -3
  45. package/src/panels/debug-panel.ts +2 -2
  46. package/src/panels/diff-panel.ts +2 -2
  47. package/src/panels/docs-panel.ts +1 -1
  48. package/src/panels/eval-panel.ts +2 -2
  49. package/src/panels/file-explorer-panel.ts +3 -3
  50. package/src/panels/file-preview-panel.ts +3 -3
  51. package/src/panels/forensics-panel.ts +2 -2
  52. package/src/panels/git-panel.ts +1 -1
  53. package/src/panels/hooks-panel.ts +3 -3
  54. package/src/panels/incident-review-panel.ts +1 -1
  55. package/src/panels/intelligence-panel.ts +2 -2
  56. package/src/panels/knowledge-panel.ts +1 -1
  57. package/src/panels/local-auth-panel.ts +2 -2
  58. package/src/panels/marketplace-panel.ts +1 -1
  59. package/src/panels/mcp-panel.ts +3 -3
  60. package/src/panels/memory-panel.ts +1 -1
  61. package/src/panels/ops-control-panel.ts +3 -3
  62. package/src/panels/ops-strategy-panel.ts +2 -2
  63. package/src/panels/orchestration-panel.ts +2 -2
  64. package/src/panels/panel-list-panel.ts +6 -6
  65. package/src/panels/plan-dashboard-panel.ts +1 -1
  66. package/src/panels/plugins-panel.ts +2 -2
  67. package/src/panels/policy-panel.ts +2 -2
  68. package/src/panels/polish.ts +3 -3
  69. package/src/panels/provider-account-snapshot.ts +1 -1
  70. package/src/panels/provider-accounts-panel.ts +25 -29
  71. package/src/panels/provider-health-panel.ts +2 -2
  72. package/src/panels/provider-stats-panel.ts +3 -3
  73. package/src/panels/qr-panel.ts +182 -0
  74. package/src/panels/remote-panel.ts +3 -3
  75. package/src/panels/routes-panel.ts +3 -3
  76. package/src/panels/sandbox-panel.ts +2 -2
  77. package/src/panels/schedule-panel.ts +1 -1
  78. package/src/panels/scrollable-list-panel.ts +407 -0
  79. package/src/panels/security-panel.ts +2 -2
  80. package/src/panels/services-panel.ts +3 -3
  81. package/src/panels/session-browser-panel.ts +2 -2
  82. package/src/panels/settings-sync-panel.ts +2 -2
  83. package/src/panels/skills-panel.ts +6 -6
  84. package/src/panels/subscription-panel.ts +3 -3
  85. package/src/panels/symbol-outline-panel.ts +3 -3
  86. package/src/panels/system-messages-panel.ts +4 -4
  87. package/src/panels/tasks-panel.ts +2 -2
  88. package/src/panels/thinking-panel.ts +3 -3
  89. package/src/panels/token-budget-panel.ts +1 -1
  90. package/src/panels/tool-inspector-panel.ts +3 -3
  91. package/src/panels/types.ts +5 -5
  92. package/src/panels/watchers-panel.ts +3 -3
  93. package/src/panels/welcome-panel.ts +1 -1
  94. package/src/panels/worktree-panel.ts +22 -21
  95. package/src/panels/wrfc-panel.ts +3 -3
  96. package/src/permissions/prompt.ts +3 -22
  97. package/src/plugins/loader.ts +15 -304
  98. package/src/renderer/agent-detail-modal.ts +1 -1
  99. package/src/renderer/autocomplete-overlay.ts +2 -2
  100. package/src/renderer/bookmark-modal.ts +1 -1
  101. package/src/renderer/bottom-bar.ts +2 -2
  102. package/src/renderer/buffer.ts +1 -1
  103. package/src/renderer/code-block.ts +2 -2
  104. package/src/renderer/compositor.ts +2 -2
  105. package/src/renderer/context-inspector.ts +1 -1
  106. package/src/renderer/conversation-layout.ts +2 -2
  107. package/src/renderer/conversation-overlays.ts +1 -1
  108. package/src/renderer/conversation-surface.ts +2 -2
  109. package/src/renderer/diff-view.ts +2 -2
  110. package/src/renderer/diff.ts +1 -1
  111. package/src/renderer/file-picker-overlay.ts +2 -2
  112. package/src/renderer/file-tree.ts +2 -2
  113. package/src/renderer/help-overlay.ts +1 -1
  114. package/src/renderer/history-search-overlay.ts +2 -2
  115. package/src/renderer/live-tail-modal.ts +1 -1
  116. package/src/renderer/markdown.ts +2 -2
  117. package/src/renderer/modal-factory.ts +3 -3
  118. package/src/renderer/model-picker-overlay.ts +2 -2
  119. package/src/renderer/overlay-box.ts +2 -2
  120. package/src/renderer/panel-composite.ts +1 -1
  121. package/src/renderer/panel-picker-overlay.ts +2 -2
  122. package/src/renderer/panel-tab-bar.ts +1 -1
  123. package/src/renderer/panel-workspace-bar.ts +1 -1
  124. package/src/renderer/process-indicator.ts +2 -2
  125. package/src/renderer/process-modal.ts +1 -1
  126. package/src/renderer/profile-picker-modal.ts +2 -2
  127. package/src/renderer/progress.ts +2 -2
  128. package/src/renderer/qr-renderer.ts +117 -0
  129. package/src/renderer/search-overlay.ts +2 -2
  130. package/src/renderer/selection-modal-overlay.ts +2 -2
  131. package/src/renderer/session-picker-modal.ts +2 -2
  132. package/src/renderer/settings-modal-helpers.ts +122 -0
  133. package/src/renderer/settings-modal.ts +149 -113
  134. package/src/renderer/shell-surface.ts +1 -1
  135. package/src/renderer/system-message.ts +1 -1
  136. package/src/renderer/tab-strip.ts +2 -2
  137. package/src/renderer/text-layout.ts +1 -1
  138. package/src/renderer/thinking.ts +1 -1
  139. package/src/renderer/tool-call.ts +2 -2
  140. package/src/renderer/ui-factory.ts +2 -2
  141. package/src/runtime/bootstrap-command-context.ts +5 -6
  142. package/src/runtime/bootstrap-command-parts.ts +32 -18
  143. package/src/runtime/bootstrap-core.ts +3 -2
  144. package/src/runtime/bootstrap-hook-bridge.ts +15 -174
  145. package/src/runtime/bootstrap-shell.ts +4 -4
  146. package/src/runtime/bootstrap.ts +7 -2
  147. package/src/runtime/context.ts +4 -20
  148. package/src/runtime/diagnostics/panels/index.ts +6 -6
  149. package/src/runtime/diagnostics/panels/ops.ts +1 -1
  150. package/src/runtime/diagnostics/panels/panel-resources.ts +118 -0
  151. package/src/runtime/perf/panel-contracts.ts +32 -0
  152. package/src/runtime/perf/panel-health-monitor.ts +18 -0
  153. package/src/runtime/services.ts +5 -5
  154. package/src/runtime/store/domains/domain-read-matrix.ts +0 -2
  155. package/src/runtime/store/selectors/index.ts +11 -6
  156. package/src/runtime/store/state.ts +12 -4
  157. package/src/runtime/ui-events.ts +1 -0
  158. package/src/runtime/ui-read-model-helpers.ts +1 -32
  159. package/src/runtime/ui-read-models-observability-maintenance.ts +1 -81
  160. package/src/runtime/ui-read-models-observability-options.ts +1 -5
  161. package/src/runtime/ui-read-models-observability-remote.ts +1 -73
  162. package/src/runtime/ui-read-models-observability-security.ts +1 -172
  163. package/src/runtime/ui-read-models-observability-system.ts +1 -217
  164. package/src/runtime/ui-read-models-observability.ts +1 -59
  165. package/src/runtime/ui-service-queries.ts +1 -114
  166. package/src/runtime/ui-services.ts +1 -1
  167. package/src/shell/ui-openers.ts +1 -1
  168. package/src/tools/index.ts +1 -186
  169. package/src/types/grid.ts +48 -0
  170. package/src/utils/clipboard.ts +21 -0
  171. package/src/utils/splash-lines.ts +1 -1
  172. package/src/utils/terminal-width.ts +185 -0
  173. package/src/version.ts +1 -1
  174. package/src/config/service-registry.ts +0 -1
  175. package/src/config/subscription-providers.ts +0 -127
  176. package/src/daemon/facade-composition.ts +0 -398
  177. package/src/daemon/facade.ts +0 -638
  178. package/src/daemon/surface-policy.ts +0 -60
  179. package/src/daemon/types.ts +0 -191
  180. package/src/runtime/diagnostics/actions.ts +0 -776
  181. package/src/runtime/diagnostics/index.ts +0 -99
  182. package/src/runtime/diagnostics/panels/agents.ts +0 -252
  183. package/src/runtime/diagnostics/panels/events.ts +0 -188
  184. package/src/runtime/diagnostics/panels/health.ts +0 -242
  185. package/src/runtime/diagnostics/panels/tasks.ts +0 -251
  186. package/src/runtime/diagnostics/panels/tool-calls.ts +0 -267
  187. package/src/runtime/diagnostics/provider.ts +0 -262
  188. package/src/runtime/store/domains/conversation.ts +0 -181
  189. package/src/runtime/store/domains/permissions.ts +0 -143
  190. package/src/runtime/store/helpers/reducers/conversation.ts +0 -228
  191. package/src/runtime/store/helpers/reducers/lifecycle.ts +0 -440
  192. package/src/runtime/store/helpers/reducers/shared.ts +0 -60
  193. package/src/runtime/store/helpers/reducers/sync.ts +0 -555
  194. package/src/runtime/store/helpers/reducers.ts +0 -30
  195. package/src/runtime/ui-read-models-core.ts +0 -95
  196. package/src/runtime/ui-read-models-operations.ts +0 -203
@@ -0,0 +1,407 @@
1
+ import type { Line } from '../types/grid.ts';
2
+ import { createEmptyLine } from '../types/grid.ts';
3
+ import { BasePanel } from './base-panel.ts';
4
+ import type { PanelCategory } from './types.ts';
5
+ import type { ComponentHealthMonitor } from '../runtime/perf/panel-health-monitor.ts';
6
+ import {
7
+ buildEmptyState,
8
+ buildPanelWorkspace,
9
+ DEFAULT_PANEL_PALETTE,
10
+ resolveScrollablePanelSection,
11
+ type PanelPalette,
12
+ } from './polish.ts';
13
+ import {
14
+ isPanelSearchBackspace,
15
+ isPanelSearchCancel,
16
+ isPanelSearchPrintable,
17
+ } from './search-focus.ts';
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // ScrollableListPanel<T>
21
+ // ---------------------------------------------------------------------------
22
+
23
+ /**
24
+ * Base class for all list-based panels that require scroll/cursor navigation.
25
+ *
26
+ * Subclasses implement:
27
+ * - `getItems()` — the ordered list of items to display
28
+ * - `renderItem(item, index, selected, width)` — one `Line` per item
29
+ *
30
+ * Optionally override:
31
+ * - `getEmptyStateMessage()` / `getEmptyStateActions()` — empty-state copy
32
+ * - `onSelect(item)` — called when the user presses Enter
33
+ * - `onAction(item, action)` — for secondary key bindings
34
+ * - `getPalette()` — colour palette (defaults to `DEFAULT_PANEL_PALETTE`)
35
+ * - `getPageSize()` — rows per page-up/page-down (default 10)
36
+ *
37
+ * `renderList()` produces the full `Line[]` output that a trivial panel's
38
+ * `render()` can return directly:
39
+ *
40
+ * ```ts
41
+ * render(width: number, height: number): Line[] {
42
+ * return this.renderList(width, height, { header: this.buildHeader(width) });
43
+ * }
44
+ * ```
45
+ */
46
+ export abstract class ScrollableListPanel<T> extends BasePanel {
47
+ protected selectedIndex = 0;
48
+ /** Tracks the first visible row index; kept in sync with resolveScrollablePanelSection. */
49
+ protected scrollStart = 0;
50
+
51
+ constructor(
52
+ id: string,
53
+ name: string,
54
+ icon: string,
55
+ category: PanelCategory,
56
+ componentHealthMonitor?: ComponentHealthMonitor,
57
+ ) {
58
+ super(id, name, icon, category, componentHealthMonitor);
59
+ }
60
+
61
+ // -------------------------------------------------------------------------
62
+ // Abstract — subclasses must implement
63
+ // -------------------------------------------------------------------------
64
+
65
+ /** Return the full ordered list of items to display. */
66
+ protected abstract getItems(): readonly T[];
67
+
68
+ /** Render a single item as one terminal `Line`. */
69
+ protected abstract renderItem(
70
+ item: T,
71
+ index: number,
72
+ selected: boolean,
73
+ width: number,
74
+ ): Line;
75
+
76
+ // -------------------------------------------------------------------------
77
+ // Optional overrides
78
+ // -------------------------------------------------------------------------
79
+
80
+ /** Short label shown in the empty-state title. */
81
+ protected getEmptyStateMessage(): string {
82
+ return 'No items';
83
+ }
84
+
85
+ /** Suggested actions shown in the empty state. */
86
+ protected getEmptyStateActions(): Array<{ command: string; summary: string }> {
87
+ return [];
88
+ }
89
+
90
+ /** Called when the user presses Enter on the selected item. */
91
+ protected onSelect(_item: T): void {}
92
+
93
+ /** Called for secondary key bindings (e.g. 'd' for delete). */
94
+ protected onAction(_item: T, _action: string): void {}
95
+
96
+ /** Colour palette used by `renderList()`. */
97
+ protected getPalette(): PanelPalette {
98
+ return DEFAULT_PANEL_PALETTE;
99
+ }
100
+
101
+ /**
102
+ * Rows to jump on pageup / pagedown.
103
+ * Override in `render()` to pass the actual visible row count:
104
+ *
105
+ * ```ts
106
+ * this._pageSize = Math.max(1, visibleRows - 2);
107
+ * ```
108
+ */
109
+ protected getPageSize(): number {
110
+ return 10;
111
+ }
112
+
113
+ // -------------------------------------------------------------------------
114
+ // Navigation — consistent across ALL panels
115
+ // -------------------------------------------------------------------------
116
+
117
+ handleInput(key: string): boolean {
118
+ const items = this.getItems();
119
+ const total = items.length;
120
+
121
+ switch (key) {
122
+ case 'up':
123
+ case 'k':
124
+ if (total === 0) return false;
125
+ this.selectedIndex = Math.max(0, this.selectedIndex - 1);
126
+ this.needsRender = true;
127
+ return true;
128
+
129
+ case 'down':
130
+ case 'j':
131
+ if (total === 0) return false;
132
+ this.selectedIndex = Math.min(total - 1, this.selectedIndex + 1);
133
+ this.needsRender = true;
134
+ return true;
135
+
136
+ case 'pageup':
137
+ if (total === 0) return false;
138
+ this.selectedIndex = Math.max(0, this.selectedIndex - this.getPageSize());
139
+ this.needsRender = true;
140
+ return true;
141
+
142
+ case 'pagedown':
143
+ if (total === 0) return false;
144
+ this.selectedIndex = Math.min(total - 1, this.selectedIndex + this.getPageSize());
145
+ this.needsRender = true;
146
+ return true;
147
+
148
+ case 'home':
149
+ case 'g':
150
+ if (total === 0) return false;
151
+ this.selectedIndex = 0;
152
+ this.needsRender = true;
153
+ return true;
154
+
155
+ case 'end':
156
+ case 'G':
157
+ if (total === 0) return false;
158
+ this.selectedIndex = total - 1;
159
+ this.needsRender = true;
160
+ return true;
161
+
162
+ case 'return':
163
+ case 'enter': {
164
+ if (total === 0) return false;
165
+ const item = items[this.selectedIndex];
166
+ if (item !== undefined) this.onSelect(item);
167
+ return true;
168
+ }
169
+
170
+ default:
171
+ return false;
172
+ }
173
+ }
174
+
175
+ // -------------------------------------------------------------------------
176
+ // Scroll state helpers
177
+ // -------------------------------------------------------------------------
178
+
179
+ /**
180
+ * Clamp `selectedIndex` to the current item count.
181
+ * Must be called after any data refresh that may shrink the list.
182
+ */
183
+ protected clampSelection(): void {
184
+ const total = this.getItems().length;
185
+ if (total === 0) {
186
+ this.selectedIndex = 0;
187
+ } else {
188
+ this.selectedIndex = Math.min(this.selectedIndex, total - 1);
189
+ }
190
+ }
191
+
192
+ // -------------------------------------------------------------------------
193
+ // Render helper — the main convenience entry point
194
+ // -------------------------------------------------------------------------
195
+
196
+ /**
197
+ * Render the full panel including optional header/footer and an empty state.
198
+ *
199
+ * Uses `resolveScrollablePanelSection` + `buildPanelWorkspace` internally,
200
+ * keeping `scrollStart` in sync after each call.
201
+ *
202
+ * @param width Panel width in columns.
203
+ * @param height Panel height in rows.
204
+ * @param options.header Lines prepended as the first workspace section.
205
+ * @param options.footer Lines appended as the last workspace section.
206
+ * @param options.emptyMessage Override for the empty-state title text.
207
+ * @param options.title Workspace title (defaults to `this.name`).
208
+ */
209
+ protected renderList(
210
+ width: number,
211
+ height: number,
212
+ options: {
213
+ readonly header?: readonly Line[];
214
+ readonly footer?: readonly Line[];
215
+ readonly emptyMessage?: string;
216
+ readonly title?: string;
217
+ } = {},
218
+ ): Line[] {
219
+ this.needsRender = false;
220
+ const palette = this.getPalette();
221
+ const items = this.getItems();
222
+ const title = options.title ?? this.name;
223
+
224
+ // Build all item lines (pre-render for resolveScrollablePanelSection)
225
+ const scrollableLines: Line[] = items.map((item, index) =>
226
+ this.renderItem(item, index, index === this.selectedIndex, width),
227
+ );
228
+
229
+ // Empty state
230
+ if (scrollableLines.length === 0) {
231
+ const emptyLines = buildEmptyState(
232
+ width,
233
+ options.emptyMessage ?? this.getEmptyStateMessage(),
234
+ '',
235
+ this.getEmptyStateActions(),
236
+ palette,
237
+ );
238
+ const lines = buildPanelWorkspace(width, height, {
239
+ title,
240
+ sections: [
241
+ ...(options.header ? [{ lines: options.header as Line[] }] : []),
242
+ { lines: emptyLines },
243
+ ...(options.footer ? [{ lines: options.footer as Line[] }] : []),
244
+ ],
245
+ palette,
246
+ });
247
+ while (lines.length < height) lines.push(createEmptyLine(width));
248
+ return lines.slice(0, height);
249
+ }
250
+
251
+ // Resolve scrollable section (updates scrollStart)
252
+ const beforeSections = options.header ? [{ lines: options.header as Line[] }] : [];
253
+ const afterSections = options.footer ? [{ lines: options.footer as Line[] }] : [];
254
+
255
+ const resolved = resolveScrollablePanelSection(width, height, {
256
+ palette,
257
+ beforeSections,
258
+ afterSections,
259
+ section: {
260
+ scrollableLines,
261
+ selectedIndex: this.selectedIndex,
262
+ scrollOffset: this.scrollStart,
263
+ guardRows: 1,
264
+ appendWindowSummary: scrollableLines.length > 5 ? { dimColor: palette.dim } : undefined,
265
+ },
266
+ });
267
+ this.scrollStart = resolved.scrollOffset;
268
+
269
+ const sections = [
270
+ ...beforeSections,
271
+ resolved.section,
272
+ ...afterSections,
273
+ ];
274
+
275
+ const lines = buildPanelWorkspace(width, height, {
276
+ title,
277
+ sections,
278
+ palette,
279
+ });
280
+ while (lines.length < height) lines.push(createEmptyLine(width));
281
+ return lines.slice(0, height);
282
+ }
283
+ }
284
+
285
+ // ---------------------------------------------------------------------------
286
+ // SearchableListPanel<T>
287
+ // ---------------------------------------------------------------------------
288
+
289
+ /**
290
+ * Extends `ScrollableListPanel<T>` with inline search/filter support.
291
+ *
292
+ * Subclasses implement:
293
+ * - `getAllItems()` — the full (unfiltered) item list
294
+ * - `matchesSearch(item, query)` — case-insensitive filter predicate
295
+ *
296
+ * `getItems()` is implemented here and returns filtered results. Do NOT
297
+ * override `getItems()` in subclasses — override `getAllItems()` instead.
298
+ *
299
+ * Search state:
300
+ * - Printable characters append to `searchQuery`.
301
+ * - Backspace/Delete removes the last character.
302
+ * - Escape clears the query.
303
+ * - Navigation keys (up/down/etc.) are forwarded to the parent.
304
+ *
305
+ * Render the search input line by calling `buildSearchInput(width)` from
306
+ * your panel's header builder.
307
+ */
308
+ export abstract class SearchableListPanel<T> extends ScrollableListPanel<T> {
309
+ protected searchQuery = '';
310
+
311
+ private _filteredItems: readonly T[] = [];
312
+ private _filterDirty = true;
313
+
314
+ // -------------------------------------------------------------------------
315
+ // Abstract — subclasses must implement
316
+ // -------------------------------------------------------------------------
317
+
318
+ /** Return the full unfiltered item list. */
319
+ protected abstract getAllItems(): readonly T[];
320
+
321
+ /** Return true if `item` matches the search `query`. */
322
+ protected abstract matchesSearch(item: T, query: string): boolean;
323
+
324
+ // -------------------------------------------------------------------------
325
+ // getItems — returns filtered list (do NOT override in subclasses)
326
+ // -------------------------------------------------------------------------
327
+
328
+ protected getItems(): readonly T[] {
329
+ if (this._filterDirty) {
330
+ const all = this.getAllItems();
331
+ this._filteredItems = this.searchQuery
332
+ ? all.filter((item) => this.matchesSearch(item, this.searchQuery))
333
+ : all;
334
+ this._filterDirty = false;
335
+ // Clamp after filter to keep selection in bounds
336
+ this.clampSelection();
337
+ }
338
+ return this._filteredItems;
339
+ }
340
+
341
+ /**
342
+ * Mark the filter cache as stale.
343
+ * Call this whenever `getAllItems()` returns new data.
344
+ */
345
+ protected invalidateFilter(): void {
346
+ this._filterDirty = true;
347
+ this.needsRender = true;
348
+ }
349
+
350
+ // -------------------------------------------------------------------------
351
+ // Input — search first, navigation second
352
+ // -------------------------------------------------------------------------
353
+
354
+ handleInput(key: string): boolean {
355
+ // Backspace: trim query
356
+ if (isPanelSearchBackspace(key)) {
357
+ if (this.searchQuery.length > 0) {
358
+ this.searchQuery = this.searchQuery.slice(0, -1);
359
+ this._filterDirty = true;
360
+ this.needsRender = true;
361
+ return true;
362
+ }
363
+ return false;
364
+ }
365
+
366
+ // Escape: clear query
367
+ if (isPanelSearchCancel(key)) {
368
+ if (this.searchQuery.length > 0) {
369
+ this.searchQuery = '';
370
+ this._filterDirty = true;
371
+ this.needsRender = true;
372
+ return true;
373
+ }
374
+ return false;
375
+ }
376
+
377
+ // Printable characters: append to query
378
+ if (isPanelSearchPrintable(key)) {
379
+ this.searchQuery += key;
380
+ this._filterDirty = true;
381
+ this.needsRender = true;
382
+ return true;
383
+ }
384
+
385
+ // Navigation and Enter: delegate to parent
386
+ return super.handleInput(key);
387
+ }
388
+
389
+ /**
390
+ * Build the search input `Line` suitable for use in a panel header.
391
+ *
392
+ * Import `buildSearchInputLine` from `./polish.ts` and call it with
393
+ * `this.searchQuery`. Convenience wrapper:
394
+ *
395
+ * ```ts
396
+ * import { buildSearchInputLine } from './polish.ts';
397
+ *
398
+ * private buildHeader(width: number): Line[] {
399
+ * return [buildSearchInputLine(width, 'Filter', this.searchQuery, this.getPalette(), {})];
400
+ * }
401
+ * ```
402
+ *
403
+ * This method is intentionally left as a documentation reference rather
404
+ * than a concrete implementation to avoid coupling the base class to a
405
+ * specific label or search-input layout.
406
+ */
407
+ }
@@ -1,5 +1,5 @@
1
- import type { Line } from '@pellux/goodvibes-sdk/platform/types/grid';
2
- import { createEmptyLine } from '@pellux/goodvibes-sdk/platform/types/grid';
1
+ import type { Line } from '../types/grid.ts';
2
+ import { createEmptyLine } from '../types/grid.ts';
3
3
  import { BasePanel } from './base-panel.ts';
4
4
  import type { TokenAuditResult } from '@pellux/goodvibes-sdk/platform/security/token-audit';
5
5
  import type { UiReadModel, UiSecuritySnapshot } from '../runtime/ui-read-models.ts';
@@ -1,11 +1,11 @@
1
- import type { Line } from '@pellux/goodvibes-sdk/platform/types/grid';
2
- import { createEmptyLine } from '@pellux/goodvibes-sdk/platform/types/grid';
1
+ import type { Line } from '../types/grid.ts';
2
+ import { createEmptyLine } from '../types/grid.ts';
3
3
  import { BasePanel } from './base-panel.ts';
4
4
  import {
5
5
  type ServiceConfig,
6
6
  type ServiceInspection,
7
7
  type ServiceConnectionTestResult,
8
- } from '../config/service-registry.ts';
8
+ } from '@pellux/goodvibes-sdk/platform/config/service-registry';
9
9
  import type { ServiceInspectionQuery, SubscriptionAccessQuery } from '../runtime/ui-service-queries.ts';
10
10
  import {
11
11
  buildEmptyState,
@@ -2,7 +2,7 @@
2
2
  // SessionBrowserPanel — browse, search, and resume old sessions.
3
3
  // ---------------------------------------------------------------------------
4
4
 
5
- import type { Line } from '@pellux/goodvibes-sdk/platform/types/grid';
5
+ import type { Line } from '../types/grid.ts';
6
6
  import { BasePanel } from './base-panel.ts';
7
7
  import type { SessionInfo } from '@pellux/goodvibes-sdk/platform/sessions/manager';
8
8
  import { logger } from '@pellux/goodvibes-sdk/platform/utils/logger';
@@ -17,7 +17,7 @@ import {
17
17
  DEFAULT_PANEL_PALETTE,
18
18
  type PanelWorkspaceSection,
19
19
  } from './polish.ts';
20
- import { truncateDisplay } from '@pellux/goodvibes-sdk/platform/utils/terminal-width';
20
+ import { truncateDisplay } from '../utils/terminal-width.ts';
21
21
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils/error-display';
22
22
  import {
23
23
  getPanelSearchFocusTransition,
@@ -1,5 +1,5 @@
1
- import type { Line } from '@pellux/goodvibes-sdk/platform/types/grid';
2
- import { createEmptyLine } from '@pellux/goodvibes-sdk/platform/types/grid';
1
+ import type { Line } from '../types/grid.ts';
2
+ import { createEmptyLine } from '../types/grid.ts';
3
3
  import { BasePanel } from './base-panel.ts';
4
4
  import {
5
5
  buildDetailBlock,
@@ -1,10 +1,10 @@
1
1
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import type { Line } from '@pellux/goodvibes-sdk/platform/types/grid';
4
- import { createEmptyLine } from '@pellux/goodvibes-sdk/platform/types/grid';
5
- import { getDisplayWidth, truncateDisplay } from '@pellux/goodvibes-sdk/platform/utils/terminal-width';
3
+ import type { Line } from '../types/grid.ts';
4
+ import { createEmptyLine } from '../types/grid.ts';
5
+ import { getDisplayWidth, truncateDisplay } from '../utils/terminal-width.ts';
6
6
  import { BasePanel } from './base-panel.ts';
7
- import type { PanelHealthMonitor } from '@pellux/goodvibes-sdk/platform/runtime/perf/panel-health-monitor';
7
+ import type { ComponentHealthMonitor } from '../runtime/perf/panel-health-monitor.ts';
8
8
  import type { ShellPathService } from '@pellux/goodvibes-sdk/platform/runtime/shell-paths';
9
9
  import {
10
10
  buildEmptyState,
@@ -55,7 +55,7 @@ export interface SkillRecord {
55
55
 
56
56
  export interface SkillsPanelOptions {
57
57
  shellPaths: Pick<ShellPathService, 'workingDirectory' | 'homeDirectory'>;
58
- panelHealthMonitor?: PanelHealthMonitor;
58
+ componentHealthMonitor?: ComponentHealthMonitor;
59
59
  }
60
60
 
61
61
  function parseFrontmatter(content: string): Record<string, string> {
@@ -243,7 +243,7 @@ export class SkillsPanel extends BasePanel {
243
243
  private cacheDirty = true;
244
244
 
245
245
  public constructor(options: SkillsPanelOptions) {
246
- super('skills', 'Skills', 'K', 'monitoring', options.panelHealthMonitor);
246
+ super('skills', 'Skills', 'K', 'monitoring', options.componentHealthMonitor);
247
247
  this.shellPaths = options.shellPaths;
248
248
  }
249
249
 
@@ -1,8 +1,8 @@
1
- import type { Line } from '@pellux/goodvibes-sdk/platform/types/grid';
2
- import { createEmptyLine } from '@pellux/goodvibes-sdk/platform/types/grid';
1
+ import type { Line } from '../types/grid.ts';
2
+ import { createEmptyLine } from '../types/grid.ts';
3
3
  import { BasePanel } from './base-panel.ts';
4
4
  import type { ProviderSubscription, PendingSubscriptionLogin } from '@pellux/goodvibes-sdk/platform/config/subscriptions';
5
- import { listBuiltinSubscriptionProviders } from '../config/subscription-providers.ts';
5
+ import { listBuiltinSubscriptionProviders } from '@pellux/goodvibes-sdk/platform/config/subscription-providers';
6
6
  import type { ServiceInspectionQuery, SubscriptionAccessQuery } from '../runtime/ui-service-queries.ts';
7
7
  import {
8
8
  buildDetailBlock,
@@ -1,6 +1,6 @@
1
1
  import { BasePanel } from './base-panel.ts';
2
- import { createEmptyLine } from '@pellux/goodvibes-sdk/platform/types/grid';
3
- import type { Line } from '@pellux/goodvibes-sdk/platform/types/grid';
2
+ import { createEmptyLine } from '../types/grid.ts';
3
+ import type { Line } from '../types/grid.ts';
4
4
  import {
5
5
  buildEmptyState,
6
6
  buildPanelLine,
@@ -9,7 +9,7 @@ import {
9
9
  resolveScrollablePanelSection,
10
10
  DEFAULT_PANEL_PALETTE,
11
11
  } from './polish.ts';
12
- import { getDisplayWidth } from '@pellux/goodvibes-sdk/platform/utils/terminal-width';
12
+ import { getDisplayWidth } from '../utils/terminal-width.ts';
13
13
 
14
14
  // ── Symbol types ────────────────────────────────────────────────────────────
15
15
 
@@ -4,8 +4,8 @@
4
4
  */
5
5
 
6
6
  import { BasePanel } from './base-panel.ts';
7
- import type { Line } from '@pellux/goodvibes-sdk/platform/types/grid';
8
- import type { PanelHealthMonitor } from '@pellux/goodvibes-sdk/platform/runtime/perf/panel-health-monitor';
7
+ import type { Line } from '../types/grid.ts';
8
+ import type { ComponentHealthMonitor } from '../runtime/perf/panel-health-monitor.ts';
9
9
  import {
10
10
  buildBodyText,
11
11
  buildEmptyState,
@@ -54,8 +54,8 @@ export class SystemMessagesPanel extends BasePanel {
54
54
  private _scrollOffset = 0;
55
55
  private readonly configManager: ConfigManager;
56
56
 
57
- constructor(configManager: ConfigManager, panelHealthMonitor?: PanelHealthMonitor) {
58
- super('system-messages', 'System Messages', 'J', 'monitoring', panelHealthMonitor);
57
+ constructor(configManager: ConfigManager, componentHealthMonitor?: ComponentHealthMonitor) {
58
+ super('system-messages', 'System Messages', 'J', 'monitoring', componentHealthMonitor);
59
59
  this.configManager = configManager;
60
60
  }
61
61
 
@@ -1,5 +1,5 @@
1
- import type { Line } from '@pellux/goodvibes-sdk/platform/types/grid';
2
- import { createEmptyLine } from '@pellux/goodvibes-sdk/platform/types/grid';
1
+ import type { Line } from '../types/grid.ts';
2
+ import { createEmptyLine } from '../types/grid.ts';
3
3
  import { BasePanel } from './base-panel.ts';
4
4
  import type { RuntimeTask, TaskLifecycleState } from '@pellux/goodvibes-sdk/platform/runtime/store/domains/tasks';
5
5
  import type { ManagedWorktreeMeta } from '@pellux/goodvibes-sdk/platform/runtime/worktree/registry';
@@ -2,10 +2,10 @@
2
2
  // ThinkingPanel — streams model reasoning tokens in real-time.
3
3
  // ---------------------------------------------------------------------------
4
4
 
5
- import type { Line } from '@pellux/goodvibes-sdk/platform/types/grid';
5
+ import type { Line } from '../types/grid.ts';
6
6
  import { BasePanel } from './base-panel.ts';
7
7
  import type { TurnEvent } from '@pellux/goodvibes-sdk/platform/runtime/events/index';
8
- import type { UiEventFeed } from '@pellux/goodvibes-sdk/platform/runtime/ui-events';
8
+ import type { UiEventFeed } from '../runtime/ui-events.ts';
9
9
  import {
10
10
  buildEmptyState,
11
11
  buildPanelLine,
@@ -15,7 +15,7 @@ import {
15
15
  DEFAULT_PANEL_PALETTE,
16
16
  type PanelWorkspaceSection,
17
17
  } from './polish.ts';
18
- import { truncateDisplay } from '@pellux/goodvibes-sdk/platform/utils/terminal-width';
18
+ import { truncateDisplay } from '../utils/terminal-width.ts';
19
19
 
20
20
  const C = {
21
21
  headerBg: '#1a1a2e',
@@ -1,6 +1,6 @@
1
1
  import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config/manager';
2
2
  import { BasePanel } from './base-panel.ts';
3
- import { createEmptyLine, createStyledCell, type Line } from '@pellux/goodvibes-sdk/platform/types/grid';
3
+ import { createEmptyLine, createStyledCell, type Line } from '../types/grid.ts';
4
4
  import type { Orchestrator } from '../core/orchestrator';
5
5
  import { evaluateSessionMaintenance } from '@pellux/goodvibes-sdk/platform/runtime/session-maintenance';
6
6
  import type { UiReadModel, UiSessionSnapshot } from '../runtime/ui-read-models.ts';
@@ -2,10 +2,10 @@
2
2
  // ToolInspectorPanel — chronological list of tool calls with args/results.
3
3
  // ---------------------------------------------------------------------------
4
4
 
5
- import type { Line } from '@pellux/goodvibes-sdk/platform/types/grid';
5
+ import type { Line } from '../types/grid.ts';
6
6
  import { BasePanel } from './base-panel.ts';
7
7
  import type { ToolEvent, TurnEvent } from '@pellux/goodvibes-sdk/platform/runtime/events/index';
8
- import type { UiEventFeed } from '@pellux/goodvibes-sdk/platform/runtime/ui-events';
8
+ import type { UiEventFeed } from '../runtime/ui-events.ts';
9
9
  import {
10
10
  buildEmptyState,
11
11
  buildPanelLine,
@@ -15,7 +15,7 @@ import {
15
15
  DEFAULT_PANEL_PALETTE,
16
16
  type PanelWorkspaceSection,
17
17
  } from './polish.ts';
18
- import { truncateDisplay } from '@pellux/goodvibes-sdk/platform/utils/terminal-width';
18
+ import { truncateDisplay } from '../utils/terminal-width.ts';
19
19
 
20
20
  const C = {
21
21
  headerBg: '#1a1a2e',
@@ -1,5 +1,5 @@
1
- import type { Line } from '@pellux/goodvibes-sdk/platform/types/grid';
2
- import type { PanelResourceContract, PanelHealthState } from '@pellux/goodvibes-sdk/platform/runtime/perf/panel-contracts';
1
+ import type { Line } from '../types/grid.ts';
2
+ import type { ComponentResourceContract, ComponentHealthState } from '../runtime/perf/panel-contracts.ts';
3
3
 
4
4
  export type PanelCategory = 'development' | 'agent' | 'monitoring' | 'session' | 'ai';
5
5
 
@@ -23,10 +23,10 @@ export interface Panel {
23
23
  needsRender: boolean;
24
24
 
25
25
  // Resource contract (optional — panels may declare resource requirements)
26
- resourceContract?: Readonly<PanelResourceContract>;
26
+ resourceContract?: Readonly<ComponentResourceContract>;
27
27
 
28
- // Health state (optional — set by PanelHealthMonitor when panel is registered)
29
- healthState?: Readonly<PanelHealthState>;
28
+ // Health state (optional — set by ComponentHealthMonitor when panel is registered)
29
+ healthState?: Readonly<ComponentHealthState>;
30
30
 
31
31
  // Input (optional)
32
32
  handleInput?(key: string): boolean;
@@ -1,8 +1,8 @@
1
- import type { Line } from '@pellux/goodvibes-sdk/platform/types/grid';
2
- import { createEmptyLine } from '@pellux/goodvibes-sdk/platform/types/grid';
1
+ import type { Line } from '../types/grid.ts';
2
+ import { createEmptyLine } from '../types/grid.ts';
3
3
  import { BasePanel } from './base-panel.ts';
4
4
  import type { UiReadModel, UiWatchersSnapshot } from '../runtime/ui-read-models.ts';
5
- import { truncateDisplay } from '@pellux/goodvibes-sdk/platform/utils/terminal-width';
5
+ import { truncateDisplay } from '../utils/terminal-width.ts';
6
6
  import {
7
7
  buildEmptyState,
8
8
  buildGuidanceLine,
@@ -1,4 +1,4 @@
1
- import type { Line } from '@pellux/goodvibes-sdk/platform/types/grid';
1
+ import type { Line } from '../types/grid.ts';
2
2
  import { BasePanel } from './base-panel.ts';
3
3
  import { buildGuidanceLine, buildPanelLine, buildPanelWorkspace, DEFAULT_PANEL_PALETTE } from './polish.ts';
4
4