@pellux/goodvibes-tui 1.1.0 → 1.9.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 (267) hide show
  1. package/CHANGELOG.md +85 -21
  2. package/README.md +28 -18
  3. package/docs/foundation-artifacts/operator-contract.json +4045 -1763
  4. package/package.json +2 -2
  5. package/src/audio/spoken-turn-controller.ts +12 -2
  6. package/src/audio/spoken-turn-wiring.ts +2 -1
  7. package/src/cli/help.ts +8 -1
  8. package/src/cli/service-posture.ts +2 -1
  9. package/src/cli/status.ts +2 -1
  10. package/src/cli/surface-command.ts +2 -2
  11. package/src/core/alert-gating.ts +67 -0
  12. package/src/core/approval-alert.ts +72 -0
  13. package/src/core/budget-breach-notifier.ts +106 -0
  14. package/src/core/composer-state.ts +11 -3
  15. package/src/core/conversation-line-cache.ts +27 -3
  16. package/src/core/conversation-rendering.ts +90 -14
  17. package/src/core/conversation.ts +18 -0
  18. package/src/core/focus-tracker.ts +41 -0
  19. package/src/core/long-task-notifier.ts +33 -6
  20. package/src/core/stream-event-wiring.ts +20 -1
  21. package/src/core/system-message-noise.ts +87 -0
  22. package/src/core/system-message-router.ts +104 -51
  23. package/src/core/turn-cancellation.ts +25 -0
  24. package/src/core/turn-event-wiring.ts +72 -3
  25. package/src/daemon/cli.ts +29 -2
  26. package/src/daemon/handlers/register.ts +8 -1
  27. package/src/daemon/service-commands.ts +329 -0
  28. package/src/export/cost-utils.ts +36 -0
  29. package/src/input/autocomplete.ts +27 -1
  30. package/src/input/command-registry.ts +84 -5
  31. package/src/input/commands/checkpoint-runtime.ts +280 -0
  32. package/src/input/commands/codebase-runtime.ts +232 -0
  33. package/src/input/commands/config.ts +43 -3
  34. package/src/input/commands/eval.ts +1 -1
  35. package/src/input/commands/health-runtime.ts +10 -2
  36. package/src/input/commands/image-runtime.ts +112 -0
  37. package/src/input/commands/intelligence-runtime.ts +11 -1
  38. package/src/input/commands/local-auth-runtime.ts +4 -1
  39. package/src/input/commands/local-runtime.ts +9 -3
  40. package/src/input/commands/marketplace-runtime.ts +2 -2
  41. package/src/input/commands/memory.ts +73 -35
  42. package/src/input/commands/operator-panel-runtime.ts +71 -31
  43. package/src/input/commands/planning-runtime.ts +116 -4
  44. package/src/input/commands/platform-sandbox-runtime.ts +1 -6
  45. package/src/input/commands/plugin-runtime.ts +2 -2
  46. package/src/input/commands/policy-dispatch.ts +21 -0
  47. package/src/input/commands/provider-accounts-runtime.ts +1 -1
  48. package/src/input/commands/qrcode-runtime.ts +26 -6
  49. package/src/input/commands/recall-review.ts +35 -0
  50. package/src/input/commands/remote-runtime-setup.ts +5 -3
  51. package/src/input/commands/remote-runtime.ts +3 -3
  52. package/src/input/commands/runtime-services.ts +54 -13
  53. package/src/input/commands/services-runtime.ts +1 -1
  54. package/src/input/commands/session-content.ts +20 -9
  55. package/src/input/commands/session-workflow.ts +16 -1
  56. package/src/input/commands/settings-sync-runtime.ts +16 -4
  57. package/src/input/commands/shell-core.ts +25 -8
  58. package/src/input/commands/skills-runtime.ts +2 -8
  59. package/src/input/commands/subscription-runtime.ts +2 -2
  60. package/src/input/commands/test-runtime.ts +277 -0
  61. package/src/input/commands/websearch-runtime.ts +101 -0
  62. package/src/input/commands/work-plan-runtime.ts +36 -10
  63. package/src/input/commands/workstream-runtime.ts +488 -0
  64. package/src/input/commands.ts +12 -0
  65. package/src/input/config-modal-types.ts +175 -0
  66. package/src/input/config-modal.ts +592 -0
  67. package/src/input/feed-context-factory.ts +10 -8
  68. package/src/input/handler-command-route.ts +37 -20
  69. package/src/input/handler-content-actions.ts +17 -2
  70. package/src/input/handler-feed-routes.ts +74 -114
  71. package/src/input/handler-feed.ts +79 -27
  72. package/src/input/handler-interactions.ts +1 -3
  73. package/src/input/handler-modal-routes.ts +136 -6
  74. package/src/input/handler-modal-stack.ts +14 -7
  75. package/src/input/handler-modal-token-routes.ts +16 -49
  76. package/src/input/handler-onboarding-daemon-adopt.ts +149 -0
  77. package/src/input/handler-onboarding.ts +71 -59
  78. package/src/input/handler-picker-routes.ts +26 -102
  79. package/src/input/handler-shortcuts.ts +74 -14
  80. package/src/input/handler-types.ts +2 -6
  81. package/src/input/handler-ui-state.ts +10 -22
  82. package/src/input/handler.ts +12 -29
  83. package/src/input/keybindings.ts +23 -8
  84. package/src/input/model-picker-types.ts +24 -6
  85. package/src/input/model-picker.ts +77 -2
  86. package/src/input/onboarding/onboarding-runtime-status.ts +10 -1
  87. package/src/input/onboarding/onboarding-wizard-apply.ts +8 -1
  88. package/src/input/onboarding/onboarding-wizard-constants.ts +4 -1
  89. package/src/input/onboarding/onboarding-wizard-network-adopt.ts +136 -0
  90. package/src/input/onboarding/onboarding-wizard-steps.ts +9 -6
  91. package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
  92. package/src/input/panel-integration-actions.ts +9 -170
  93. package/src/input/panel-mouse-geometry.ts +97 -0
  94. package/src/input/panel-paste-flood-guard.ts +86 -0
  95. package/src/input/selection-modal.ts +11 -0
  96. package/src/input/session-picker-modal.ts +44 -4
  97. package/src/input/settings-modal-data.ts +146 -2
  98. package/src/input/settings-modal-mutations.ts +6 -4
  99. package/src/input/settings-modal-types.ts +4 -2
  100. package/src/main.ts +49 -51
  101. package/src/panels/agent-inspector-shared.ts +2 -1
  102. package/src/panels/base-panel.ts +22 -1
  103. package/src/panels/builtin/agent.ts +21 -107
  104. package/src/panels/builtin/development.ts +15 -61
  105. package/src/panels/builtin/knowledge.ts +8 -32
  106. package/src/panels/builtin/operations.ts +84 -428
  107. package/src/panels/builtin/session.ts +21 -112
  108. package/src/panels/builtin/shared.ts +18 -46
  109. package/src/panels/builtin-modals.ts +218 -0
  110. package/src/panels/builtin-panels.ts +5 -0
  111. package/src/panels/cost-tracker-panel.ts +36 -10
  112. package/src/panels/diff-panel.ts +12 -43
  113. package/src/panels/eval-registry.ts +60 -0
  114. package/src/panels/fleet-deep-link.ts +31 -0
  115. package/src/panels/fleet-panel-format.ts +62 -0
  116. package/src/panels/fleet-panel-worktree-detail.ts +48 -0
  117. package/src/panels/fleet-panel.ts +758 -0
  118. package/src/panels/fleet-read-model.ts +520 -0
  119. package/src/panels/fleet-steer.ts +125 -0
  120. package/src/panels/fleet-stop.ts +153 -0
  121. package/src/panels/fleet-tabs.ts +230 -0
  122. package/src/panels/fleet-transcript.ts +356 -0
  123. package/src/panels/index.ts +7 -31
  124. package/src/panels/modals/hooks-modal.ts +187 -0
  125. package/src/panels/modals/keybindings-modal.ts +166 -0
  126. package/src/panels/modals/knowledge-modal.ts +158 -0
  127. package/src/panels/modals/local-auth-modal.ts +132 -0
  128. package/src/panels/modals/marketplace-modal.ts +218 -0
  129. package/src/panels/modals/memory-modal.ts +180 -0
  130. package/src/panels/modals/modal-surface-helpers.ts +54 -0
  131. package/src/panels/modals/modal-theme.ts +42 -0
  132. package/src/panels/modals/pairing-modal.ts +163 -0
  133. package/src/panels/modals/planning-modal.ts +304 -0
  134. package/src/panels/modals/plugins-modal.ts +174 -0
  135. package/src/panels/modals/policy-modal.ts +256 -0
  136. package/src/panels/modals/provider-health-modal.ts +136 -0
  137. package/src/panels/modals/remote-modal.ts +115 -0
  138. package/src/panels/modals/sandbox-modal.ts +150 -0
  139. package/src/panels/modals/security-modal.ts +217 -0
  140. package/src/panels/modals/services-modal.ts +179 -0
  141. package/src/panels/modals/settings-sync-modal.ts +142 -0
  142. package/src/panels/modals/skills-modal.ts +189 -0
  143. package/src/panels/modals/subscription-modal.ts +172 -0
  144. package/src/panels/modals/work-plan-modal.ts +177 -0
  145. package/src/panels/panel-confirm-overlay.ts +72 -0
  146. package/src/panels/panel-manager.ts +123 -9
  147. package/src/panels/polish-core.ts +38 -25
  148. package/src/panels/project-planning-answer-actions.ts +8 -11
  149. package/src/panels/skills-panel.ts +7 -51
  150. package/src/panels/types.ts +37 -0
  151. package/src/permissions/hunk-selection.ts +179 -0
  152. package/src/permissions/prompt.ts +216 -13
  153. package/src/renderer/autocomplete-overlay.ts +28 -2
  154. package/src/renderer/compaction-history-modal.ts +19 -3
  155. package/src/renderer/compaction-preview.ts +13 -2
  156. package/src/renderer/compaction-quality.ts +100 -0
  157. package/src/renderer/compositor.ts +2 -3
  158. package/src/renderer/config-modal.ts +95 -0
  159. package/src/renderer/conversation-overlays.ts +10 -17
  160. package/src/renderer/fleet-tab-strip.ts +56 -0
  161. package/src/renderer/footer-tips.ts +15 -3
  162. package/src/renderer/fullscreen-primitives.ts +32 -22
  163. package/src/renderer/git-status.ts +43 -1
  164. package/src/renderer/help-overlay.ts +2 -2
  165. package/src/renderer/layout.ts +0 -4
  166. package/src/renderer/markdown.ts +7 -3
  167. package/src/renderer/modal-factory.ts +25 -20
  168. package/src/renderer/model-workspace.ts +62 -4
  169. package/src/renderer/overlay-box.ts +21 -17
  170. package/src/renderer/panel-workspace-bar.ts +8 -2
  171. package/src/renderer/process-indicator.ts +14 -3
  172. package/src/renderer/selection-modal-overlay.ts +6 -1
  173. package/src/renderer/session-picker-modal.ts +196 -3
  174. package/src/renderer/settings-modal-helpers.ts +2 -0
  175. package/src/renderer/settings-modal.ts +7 -0
  176. package/src/renderer/shell-surface.ts +8 -1
  177. package/src/renderer/status-glyphs.ts +14 -15
  178. package/src/renderer/system-message.ts +15 -3
  179. package/src/renderer/terminal-bg-probe.ts +339 -0
  180. package/src/renderer/terminal-escapes.ts +20 -0
  181. package/src/renderer/theme-mode-config.ts +67 -0
  182. package/src/renderer/theme.ts +91 -1
  183. package/src/renderer/thinking.ts +11 -3
  184. package/src/renderer/tool-call.ts +15 -9
  185. package/src/renderer/tool-result-summary.ts +148 -0
  186. package/src/renderer/turn-injection.ts +133 -0
  187. package/src/renderer/ui-factory.ts +154 -85
  188. package/src/renderer/ui-primitives.ts +30 -129
  189. package/src/runtime/bootstrap-command-context.ts +27 -1
  190. package/src/runtime/bootstrap-command-parts.ts +41 -10
  191. package/src/runtime/bootstrap-core.ts +47 -15
  192. package/src/runtime/bootstrap-hook-bridge.ts +7 -0
  193. package/src/runtime/bootstrap-shell.ts +48 -17
  194. package/src/runtime/bootstrap.ts +129 -22
  195. package/src/runtime/code-index-services.ts +135 -0
  196. package/src/runtime/legacy-daemon-migration.ts +516 -0
  197. package/src/runtime/memory-fold.ts +26 -0
  198. package/src/runtime/onboarding/derivation.ts +7 -2
  199. package/src/runtime/onboarding/snapshot.ts +27 -1
  200. package/src/runtime/onboarding/types.ts +31 -1
  201. package/src/runtime/operator-token-cleanup.ts +82 -1
  202. package/src/runtime/orchestrator-core-services.ts +59 -0
  203. package/src/runtime/process-lifecycle.ts +3 -1
  204. package/src/runtime/resume-notice.ts +209 -0
  205. package/src/runtime/services.ts +98 -46
  206. package/src/runtime/session-inbound-inputs.ts +252 -0
  207. package/src/runtime/session-spine-transport.ts +64 -0
  208. package/src/runtime/terminal-output-guard.ts +15 -8
  209. package/src/runtime/ui-services.ts +27 -3
  210. package/src/runtime/workstream-services.ts +327 -0
  211. package/src/runtime/wrfc-persistence.ts +124 -17
  212. package/src/shell/blocking-input.ts +68 -4
  213. package/src/shell/recovery-input-helpers.ts +170 -1
  214. package/src/shell/ui-openers.ts +171 -26
  215. package/src/utils/format-duration.ts +8 -18
  216. package/src/utils/splash-lines.ts +1 -1
  217. package/src/utils/terminal-width.ts +52 -0
  218. package/src/version.ts +1 -1
  219. package/src/panels/agent-inspector-panel.ts +0 -786
  220. package/src/panels/approval-panel.ts +0 -252
  221. package/src/panels/automation-control-panel.ts +0 -479
  222. package/src/panels/cockpit-panel.ts +0 -481
  223. package/src/panels/cockpit-read-model.ts +0 -238
  224. package/src/panels/communication-panel.ts +0 -256
  225. package/src/panels/control-plane-panel.ts +0 -470
  226. package/src/panels/debug-panel.ts +0 -615
  227. package/src/panels/docs-panel.ts +0 -384
  228. package/src/panels/eval-panel.ts +0 -627
  229. package/src/panels/file-explorer-panel.ts +0 -673
  230. package/src/panels/file-preview-panel.ts +0 -517
  231. package/src/panels/hooks-panel.ts +0 -401
  232. package/src/panels/incident-review-panel.ts +0 -406
  233. package/src/panels/intelligence-panel.ts +0 -383
  234. package/src/panels/knowledge-graph-panel.ts +0 -515
  235. package/src/panels/marketplace-panel.ts +0 -399
  236. package/src/panels/memory-panel.ts +0 -558
  237. package/src/panels/ops-control-panel.ts +0 -329
  238. package/src/panels/ops-strategy-panel.ts +0 -321
  239. package/src/panels/orchestration-panel.ts +0 -465
  240. package/src/panels/panel-list-panel.ts +0 -566
  241. package/src/panels/plan-dashboard-panel.ts +0 -707
  242. package/src/panels/policy-panel.ts +0 -517
  243. package/src/panels/project-planning-panel.ts +0 -731
  244. package/src/panels/provider-health-panel.ts +0 -678
  245. package/src/panels/provider-health-tracker.ts +0 -310
  246. package/src/panels/provider-health-views.ts +0 -567
  247. package/src/panels/qr-panel.ts +0 -280
  248. package/src/panels/remote-panel.ts +0 -534
  249. package/src/panels/routes-panel.ts +0 -241
  250. package/src/panels/sandbox-panel.ts +0 -456
  251. package/src/panels/security-panel.ts +0 -447
  252. package/src/panels/services-panel.ts +0 -329
  253. package/src/panels/session-browser-panel.ts +0 -496
  254. package/src/panels/settings-sync-panel.ts +0 -398
  255. package/src/panels/subscription-panel.ts +0 -342
  256. package/src/panels/symbol-outline-panel.ts +0 -619
  257. package/src/panels/system-messages-panel.ts +0 -364
  258. package/src/panels/tasks-panel.ts +0 -608
  259. package/src/panels/thinking-panel.ts +0 -333
  260. package/src/panels/tool-inspector-panel.ts +0 -537
  261. package/src/panels/work-plan-panel.ts +0 -540
  262. package/src/panels/worktree-panel.ts +0 -360
  263. package/src/panels/wrfc-panel.ts +0 -790
  264. package/src/renderer/agent-detail-modal.ts +0 -466
  265. package/src/renderer/live-tail-modal.ts +0 -156
  266. package/src/renderer/process-modal.ts +0 -671
  267. package/src/renderer/qr-renderer.ts +0 -120
@@ -1,383 +0,0 @@
1
- import type { Line } from '../types/grid.ts';
2
- import { createEmptyLine } from '../types/grid.ts';
3
- import { truncateDisplay } from '../utils/terminal-width.ts';
4
- import { BasePanel } from './base-panel.ts';
5
- import { FilePreviewPanel } from './file-preview-panel.ts';
6
- import { SymbolOutlinePanel } from './symbol-outline-panel.ts';
7
- import type { PanelIntegrationContext } from './types.ts';
8
- import type { UiIntelligenceSnapshot, UiReadModel } from '../runtime/ui-read-models.ts';
9
- import {
10
- buildEmptyState,
11
- buildGuidanceLine,
12
- buildKeyboardHints,
13
- buildKeyValueLine,
14
- buildPanelLine,
15
- buildPanelWorkspace,
16
- buildStatusPill,
17
- resolveStackedScrollableSections,
18
- DEFAULT_PANEL_PALETTE,
19
- type PanelWorkspaceSection,
20
- type StyledPanelSegment,
21
- } from './polish.ts';
22
-
23
- // Base chrome only — state colors and text tokens come straight from
24
- // DEFAULT_PANEL_PALETTE (WO-002).
25
- const C = DEFAULT_PANEL_PALETTE;
26
-
27
- interface DiagnosticFile {
28
- readonly filePath: string;
29
- readonly errors: number;
30
- readonly warnings: number;
31
- readonly diagnostics: ReadonlyArray<{
32
- readonly line: number;
33
- readonly column: number;
34
- readonly severity: string;
35
- readonly message: string;
36
- readonly source?: string | undefined;
37
- readonly code?: string | undefined;
38
- }>;
39
- }
40
-
41
- /** Map an intelligence-surface status string to a status-pill segment. */
42
- function postureSegments(label: string, status: string): StyledPanelSegment[] {
43
- const state = status === 'ready' ? 'good' : status === 'degraded' ? 'warn' : status === 'loading' ? 'info' : 'bad';
44
- return buildStatusPill(state, `${label} ${status}`);
45
- }
46
-
47
- export class IntelligencePanel extends BasePanel {
48
- private readonly unsub: (() => void) | null;
49
- private selectedIndex = 0;
50
-
51
- /**
52
- * The diagnostic file under the cursor. This panel owns its own selection
53
- * state (`selectedIndex` navigates the `diagnosticFiles()` list directly), so
54
- * every selected-row read routes through this one accessor — indexing the
55
- * `diagnosticFiles()` list by the cursor directly is banned by the
56
- * no-raw-selectedindex-read architecture rule.
57
- */
58
- private selectedDiagnosticFile(): DiagnosticFile | undefined {
59
- return this.diagnosticFiles().at(this.selectedIndex);
60
- }
61
-
62
- private diagnosticsScrollOffset = 0;
63
- private findingsScrollOffset = 0;
64
- /** Set by handleInput('enter'); consumed by handlePanelIntegrationAction, which has the PanelManager reference needed to open the preview panel. */
65
- private _pendingOpenFile: { filePath: string; line: number } | null = null;
66
- /** Set by handleInput('s'); consumed by handlePanelIntegrationAction to open the file in preview AND sync the Symbols panel to it. */
67
- private _pendingSymbolsPivot: { filePath: string; line: number } | null = null;
68
-
69
- public constructor(private readonly readModel?: UiReadModel<UiIntelligenceSnapshot>) {
70
- super('intelligence', 'Intelligence', 'J', 'development');
71
- this.unsub = readModel ? readModel.subscribe(() => this.markDirty()) : null;
72
- }
73
-
74
- public override onDestroy(): void {
75
- this.unsub?.();
76
- }
77
-
78
- /** Diagnostic files sorted error-first so the most actionable file is first. */
79
- private diagnosticFiles(): DiagnosticFile[] {
80
- if (!this.readModel) return [];
81
- const state = this.readModel.getSnapshot();
82
- return [...state.diagnostics.entries()]
83
- .map(([filePath, diagnostics]) => ({
84
- filePath,
85
- errors: diagnostics.filter((entry) => entry.severity === 'error').length,
86
- warnings: diagnostics.filter((entry) => entry.severity === 'warning').length,
87
- diagnostics: diagnostics.map((entry) => ({
88
- line: entry.line,
89
- column: entry.column,
90
- severity: entry.severity,
91
- message: entry.message,
92
- source: entry.source,
93
- code: entry.code,
94
- })),
95
- }))
96
- .sort((a, b) => (b.errors - a.errors) || (b.warnings - a.warnings) || a.filePath.localeCompare(b.filePath));
97
- }
98
-
99
- /** The most actionable finding in a file — error-first, matching the file sort. */
100
- private _firstFinding(file: DiagnosticFile): DiagnosticFile['diagnostics'][number] | undefined {
101
- if (file.diagnostics.length === 0) return undefined;
102
- return [...file.diagnostics].sort((a, b) => (a.severity === 'error' ? 0 : 1) - (b.severity === 'error' ? 0 : 1))[0];
103
- }
104
-
105
- public handleInput(key: string): boolean {
106
- const files = this.diagnosticFiles();
107
- if (files.length === 0) return false;
108
- if (key === 'up' || key === 'k') {
109
- this.selectedIndex = Math.max(0, this.selectedIndex - 1);
110
- this.markDirty();
111
- return true;
112
- }
113
- if (key === 'down' || key === 'j') {
114
- this.selectedIndex = Math.min(files.length - 1, this.selectedIndex + 1);
115
- this.markDirty();
116
- return true;
117
- }
118
- if (key === 'home' || key === 'g') {
119
- this.selectedIndex = 0;
120
- this.markDirty();
121
- return true;
122
- }
123
- if (key === 'end' || key === 'G') {
124
- this.selectedIndex = files.length - 1;
125
- this.markDirty();
126
- return true;
127
- }
128
- if (key === 'enter' || key === 'return') {
129
- const selected = this.selectedDiagnosticFile();
130
- const first = selected ? this._firstFinding(selected) : undefined;
131
- if (!selected || !first) return false;
132
- this._pendingOpenFile = { filePath: selected.filePath, line: first.line + 1 };
133
- return true;
134
- }
135
- if (key === 's') {
136
- const selected = this.selectedDiagnosticFile();
137
- const first = selected ? this._firstFinding(selected) : undefined;
138
- if (!selected || !first) return false;
139
- this._pendingSymbolsPivot = { filePath: selected.filePath, line: first.line + 1 };
140
- return true;
141
- }
142
- return false;
143
- }
144
-
145
- /**
146
- * Cross-panel integration hook. Enter opens the selected file in the
147
- * preview panel at its first (error-first) finding; 's' does the same and
148
- * also syncs the Symbols panel to that file, pivoting straight from a
149
- * diagnostic to its document symbols.
150
- */
151
- handlePanelIntegrationAction(_key: string, ctx: PanelIntegrationContext): boolean {
152
- if (this._pendingSymbolsPivot) {
153
- const target = this._pendingSymbolsPivot;
154
- this._pendingSymbolsPivot = null;
155
- const previewPanel = this._openPreviewAt(ctx.panelManager, target.filePath, target.line);
156
- if (!previewPanel) return false;
157
- const symbols = ctx.panelManager.open('symbols');
158
- if (symbols instanceof SymbolOutlinePanel) {
159
- const source = previewPanel.getSource();
160
- if (source !== null) symbols.loadFile(target.filePath, source);
161
- }
162
- return true;
163
- }
164
- if (this._pendingOpenFile) {
165
- const target = this._pendingOpenFile;
166
- this._pendingOpenFile = null;
167
- return this._openPreviewAt(ctx.panelManager, target.filePath, target.line) !== null;
168
- }
169
- return false;
170
- }
171
-
172
- /** Open (or focus) the shared preview panel and jump it to filePath:line — same open/focus bridge FileExplorerPanel/DiffPanel use. */
173
- private _openPreviewAt(panelManager: PanelIntegrationContext['panelManager'], filePath: string, line: number): FilePreviewPanel | null {
174
- let previewPanel = panelManager.getPanel('preview');
175
- if (previewPanel instanceof FilePreviewPanel) {
176
- const pane = panelManager.getPaneOf('preview');
177
- panelManager.activateById('preview');
178
- if (pane) panelManager.focusPane(pane);
179
- } else {
180
- const targetPane: 'top' | 'bottom' = panelManager.isBottomPaneVisible()
181
- ? (panelManager.getFocusedPane() === 'top' ? 'bottom' : 'top')
182
- : 'bottom';
183
- const opened = panelManager.open('preview', targetPane);
184
- panelManager.show();
185
- panelManager.focusPane(targetPane);
186
- previewPanel = opened instanceof FilePreviewPanel ? opened : null;
187
- }
188
- if (!(previewPanel instanceof FilePreviewPanel)) return null;
189
- if (previewPanel.getCurrentFilePath() !== filePath) {
190
- previewPanel.openFile(filePath);
191
- }
192
- previewPanel.goToLine(line);
193
- return previewPanel;
194
- }
195
-
196
- public render(width: number, height: number): Line[] {
197
- this.needsRender = false;
198
- if (!this.readModel) {
199
- const lines = buildPanelWorkspace(width, height, {
200
- title: 'Intelligence Control Room',
201
- intro: 'Workspace intelligence posture across diagnostics, symbols, completions, and hover readiness.',
202
- sections: [{
203
- lines: buildEmptyState(
204
- width,
205
- ' Intelligence runtime store unavailable.',
206
- 'This surface needs the live runtime store so it can show diagnostics, symbol readiness, and recovery guidance.',
207
- [{ command: '/intelligence review', summary: 'review intelligence readiness from the command surface' }],
208
- C,
209
- ),
210
- }],
211
- palette: C,
212
- });
213
- while (lines.length < height) lines.push(createEmptyLine(width));
214
- return lines.slice(0, height);
215
- }
216
-
217
- const state = this.readModel.getSnapshot();
218
- const degraded = [
219
- state.diagnosticsStatus,
220
- state.completionsStatus,
221
- state.symbolSearchStatus,
222
- state.hoverStatus,
223
- ].filter((status) => status !== 'ready').length;
224
-
225
- const diagnosticFiles = this.diagnosticFiles();
226
- this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, Math.max(0, diagnosticFiles.length - 1)));
227
- const selectedFile = this.selectedDiagnosticFile();
228
- const sortedFindings = selectedFile
229
- ? [...selectedFile.diagnostics].sort((a, b) => (a.severity === 'error' ? 0 : 1) - (b.severity === 'error' ? 0 : 1))
230
- : [];
231
-
232
- const postureSection: PanelWorkspaceSection = {
233
- title: 'Intelligence posture',
234
- lines: [
235
- buildPanelLine(width, [
236
- { text: ' ', fg: C.dim },
237
- ...postureSegments('diagnostics', state.diagnosticsStatus),
238
- { text: ' ', fg: C.dim },
239
- ...postureSegments('symbols', state.symbolSearchStatus),
240
- ]),
241
- buildPanelLine(width, [
242
- { text: ' ', fg: C.dim },
243
- ...postureSegments('completions', state.completionsStatus),
244
- { text: ' ', fg: C.dim },
245
- ...postureSegments('hover', state.hoverStatus),
246
- ]),
247
- buildKeyValueLine(width, [
248
- { label: 'errors', value: String(state.errorCount), valueColor: state.errorCount > 0 ? C.bad : C.dim },
249
- { label: 'warnings', value: String(state.warningCount), valueColor: state.warningCount > 0 ? C.warn : C.dim },
250
- { label: 'requests', value: String(state.totalRequests), valueColor: C.value },
251
- { label: 'avg latency', value: `${Math.round(state.avgLatencyMs)}ms`, valueColor: C.info },
252
- ], C),
253
- ],
254
- };
255
-
256
- // Diagnostics — the most actionable surface. Render as a selectable list so
257
- // the operator can drill into a specific file's findings. Windowed via
258
- // resolveStackedScrollableSections (WO-136) instead of a flat slice(0,6)
259
- // so the cursor tracks into view instead of being able to scroll off-screen.
260
- const diagnosticsLines: Line[] = diagnosticFiles.length > 0
261
- ? diagnosticFiles.map((entry, idx) => {
262
- const selected = idx === this.selectedIndex;
263
- const tone = entry.errors > 0 ? C.bad : entry.warnings > 0 ? C.warn : C.dim;
264
- const counts = ` ${entry.errors} err ${entry.warnings} warn`;
265
- const pathBudget = Math.max(8, width - counts.length - 4);
266
- return buildPanelLine(width, [
267
- [selected ? ' ▸ ' : ' ', selected ? C.info : C.dim, selected ? C.headerBg : undefined],
268
- [truncateDisplay(entry.filePath, pathBudget), selected ? C.value : C.label, selected ? C.headerBg : undefined],
269
- [counts, tone, selected ? C.headerBg : undefined],
270
- ]);
271
- })
272
- : [buildPanelLine(width, [[' No tracked diagnostics. Symbol/diagnostic surfaces are clear.', C.dim]])];
273
-
274
- // Findings for the selected file — fully scrollable (WO-136: no 3-cap).
275
- const findingsFixedLines: Line[] = selectedFile
276
- ? [buildKeyValueLine(width, [
277
- { label: 'file', value: truncateDisplay(selectedFile.filePath, Math.max(8, width - 34)), valueColor: C.value },
278
- { label: 'errors', value: String(selectedFile.errors), valueColor: selectedFile.errors > 0 ? C.bad : C.dim },
279
- { label: 'warnings', value: String(selectedFile.warnings), valueColor: selectedFile.warnings > 0 ? C.warn : C.dim },
280
- ], C)]
281
- : [];
282
- const findingsLines: Line[] = selectedFile
283
- ? (sortedFindings.length > 0
284
- ? sortedFindings.map((finding) => {
285
- const loc = `${finding.line + 1}:${finding.column + 1}`;
286
- const tag = finding.source ? `${finding.source}${finding.code ? `(${finding.code})` : ''}` : finding.severity;
287
- const prefix = ` ${loc} ${tag}: `;
288
- return buildPanelLine(width, [
289
- [prefix, finding.severity === 'error' ? C.bad : C.warn],
290
- [truncateDisplay(finding.message, Math.max(8, width - prefix.length - 1)), C.value],
291
- ]);
292
- })
293
- : [buildPanelLine(width, [[' No findings recorded for this file.', C.dim]])])
294
- : [buildPanelLine(width, [[' Select a file above to inspect its findings.', C.dim]])];
295
-
296
- // Recovery/Workflows collapse to a single contextual guidance line
297
- // (WO-136) instead of a static wall of signposts.
298
- const guidanceSection: PanelWorkspaceSection = degraded > 0
299
- ? {
300
- title: 'Recovery',
301
- lines: [buildGuidanceLine(
302
- width,
303
- '/intelligence repair',
304
- `${degraded} of 4 surfaces degraded (diagnostics/symbols/hover/completions) — run repair`,
305
- C,
306
- )],
307
- }
308
- : {
309
- title: 'Workflows',
310
- lines: [buildGuidanceLine(
311
- width,
312
- '/intelligence symbols <file>',
313
- 'inspect document symbols for a file, or press s on a finding to pivot straight there',
314
- C,
315
- )],
316
- };
317
-
318
- const footerLines = diagnosticFiles.length > 0
319
- ? [buildKeyboardHints(width, [
320
- { keys: '↑/↓', label: 'select file' },
321
- { keys: 'Enter', label: 'open file at finding' },
322
- { keys: 's', label: 'pivot to symbols' },
323
- { keys: 'Home/End', label: 'jump' },
324
- ], C)]
325
- : [buildKeyboardHints(width, [
326
- { keys: '/intelligence diagnostics', label: 'review' },
327
- { keys: '/intelligence symbols <file>', label: 'inspect symbols' },
328
- ], C)];
329
-
330
- const [diagnosticsWindow, findingsWindow] = resolveStackedScrollableSections(width, height, {
331
- intro: 'Workspace intelligence posture across diagnostics, symbol search, hover, and completion readiness.',
332
- footerLines,
333
- palette: C,
334
- beforeSections: [postureSection],
335
- sections: [
336
- {
337
- title: `Diagnostics (${diagnosticFiles.length})`,
338
- scrollableLines: diagnosticsLines,
339
- selectedIndex: diagnosticFiles.length > 0 ? this.selectedIndex : undefined,
340
- scrollOffset: this.diagnosticsScrollOffset,
341
- minRows: 3,
342
- weight: 2,
343
- appendWindowSummary: diagnosticFiles.length > 0 ? {
344
- dimColor: C.dim,
345
- formatter: (window) => buildPanelLine(width, [[` showing ${window.start + 1}-${window.end} of ${window.total} files`, C.dim]]),
346
- } : undefined,
347
- },
348
- {
349
- title: selectedFile ? `Findings — ${truncateDisplay(selectedFile.filePath, Math.max(8, width - 14))}` : 'Findings',
350
- fixedLines: findingsFixedLines,
351
- scrollableLines: findingsLines,
352
- scrollOffset: this.findingsScrollOffset,
353
- minRows: 2,
354
- weight: 1,
355
- appendWindowSummary: sortedFindings.length > 0 ? {
356
- dimColor: C.dim,
357
- formatter: (window) => buildPanelLine(width, [[` showing ${window.start + 1}-${window.end} of ${window.total} findings`, C.dim]]),
358
- } : undefined,
359
- },
360
- ],
361
- afterSections: [guidanceSection],
362
- });
363
- this.diagnosticsScrollOffset = diagnosticsWindow?.scrollOffset ?? this.diagnosticsScrollOffset;
364
- this.findingsScrollOffset = findingsWindow?.scrollOffset ?? this.findingsScrollOffset;
365
-
366
- const sections: PanelWorkspaceSection[] = [
367
- postureSection,
368
- diagnosticsWindow?.section ?? { title: `Diagnostics (${diagnosticFiles.length})`, lines: diagnosticsLines },
369
- findingsWindow?.section ?? { title: 'Findings', lines: [...findingsFixedLines, ...findingsLines] },
370
- guidanceSection,
371
- ];
372
-
373
- const lines = buildPanelWorkspace(width, height, {
374
- title: 'Intelligence Control Room',
375
- intro: 'Workspace intelligence posture across diagnostics, symbol search, hover, and completion readiness.',
376
- sections,
377
- footerLines,
378
- palette: C,
379
- });
380
- while (lines.length < height) lines.push(createEmptyLine(width));
381
- return lines.slice(0, height);
382
- }
383
- }