@pellux/goodvibes-tui 1.1.0 → 1.7.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 (194) hide show
  1. package/CHANGELOG.md +85 -21
  2. package/README.md +20 -11
  3. package/docs/foundation-artifacts/operator-contract.json +1 -1
  4. package/package.json +2 -2
  5. package/src/core/alert-gating.ts +67 -0
  6. package/src/core/approval-alert.ts +72 -0
  7. package/src/core/budget-breach-notifier.ts +106 -0
  8. package/src/core/conversation-rendering.ts +19 -0
  9. package/src/core/conversation.ts +18 -0
  10. package/src/core/focus-tracker.ts +41 -0
  11. package/src/core/long-task-notifier.ts +33 -6
  12. package/src/core/system-message-router.ts +37 -51
  13. package/src/core/turn-cancellation.ts +20 -0
  14. package/src/core/turn-event-wiring.ts +64 -3
  15. package/src/export/cost-utils.ts +36 -0
  16. package/src/input/command-registry.ts +38 -1
  17. package/src/input/commands/checkpoint-runtime.ts +280 -0
  18. package/src/input/commands/codebase-runtime.ts +192 -0
  19. package/src/input/commands/eval.ts +1 -1
  20. package/src/input/commands/health-runtime.ts +1 -1
  21. package/src/input/commands/image-runtime.ts +112 -0
  22. package/src/input/commands/intelligence-runtime.ts +11 -1
  23. package/src/input/commands/local-auth-runtime.ts +4 -1
  24. package/src/input/commands/local-runtime.ts +9 -3
  25. package/src/input/commands/marketplace-runtime.ts +2 -2
  26. package/src/input/commands/memory.ts +6 -1
  27. package/src/input/commands/operator-panel-runtime.ts +40 -24
  28. package/src/input/commands/planning-runtime.ts +26 -3
  29. package/src/input/commands/platform-sandbox-runtime.ts +1 -6
  30. package/src/input/commands/plugin-runtime.ts +2 -2
  31. package/src/input/commands/policy-dispatch.ts +21 -0
  32. package/src/input/commands/provider-accounts-runtime.ts +1 -1
  33. package/src/input/commands/qrcode-runtime.ts +3 -3
  34. package/src/input/commands/recall-review.ts +35 -0
  35. package/src/input/commands/remote-runtime.ts +3 -3
  36. package/src/input/commands/runtime-services.ts +54 -13
  37. package/src/input/commands/services-runtime.ts +1 -1
  38. package/src/input/commands/session-workflow.ts +16 -1
  39. package/src/input/commands/settings-sync-runtime.ts +1 -1
  40. package/src/input/commands/shell-core.ts +15 -7
  41. package/src/input/commands/skills-runtime.ts +2 -8
  42. package/src/input/commands/subscription-runtime.ts +2 -2
  43. package/src/input/commands/test-runtime.ts +277 -0
  44. package/src/input/commands/websearch-runtime.ts +101 -0
  45. package/src/input/commands/work-plan-runtime.ts +36 -10
  46. package/src/input/commands/workstream-runtime.ts +338 -0
  47. package/src/input/commands.ts +12 -0
  48. package/src/input/config-modal-types.ts +161 -0
  49. package/src/input/config-modal.ts +377 -0
  50. package/src/input/feed-context-factory.ts +7 -8
  51. package/src/input/handler-command-route.ts +27 -17
  52. package/src/input/handler-feed-routes.ts +61 -23
  53. package/src/input/handler-feed.ts +47 -23
  54. package/src/input/handler-interactions.ts +1 -3
  55. package/src/input/handler-modal-routes.ts +65 -0
  56. package/src/input/handler-modal-stack.ts +3 -5
  57. package/src/input/handler-modal-token-routes.ts +16 -49
  58. package/src/input/handler-picker-routes.ts +11 -94
  59. package/src/input/handler-shortcuts.ts +24 -14
  60. package/src/input/handler-types.ts +2 -6
  61. package/src/input/handler-ui-state.ts +10 -22
  62. package/src/input/handler.ts +6 -28
  63. package/src/input/keybindings.ts +22 -8
  64. package/src/input/model-picker-types.ts +24 -6
  65. package/src/input/model-picker.ts +58 -0
  66. package/src/input/panel-integration-actions.ts +9 -170
  67. package/src/input/settings-modal-data.ts +95 -0
  68. package/src/input/settings-modal-mutations.ts +6 -4
  69. package/src/main.ts +24 -27
  70. package/src/panels/agent-inspector-shared.ts +2 -1
  71. package/src/panels/base-panel.ts +22 -1
  72. package/src/panels/builtin/agent.ts +21 -107
  73. package/src/panels/builtin/development.ts +15 -61
  74. package/src/panels/builtin/knowledge.ts +8 -32
  75. package/src/panels/builtin/operations.ts +84 -428
  76. package/src/panels/builtin/session.ts +21 -112
  77. package/src/panels/builtin/shared.ts +9 -43
  78. package/src/panels/builtin-modals.ts +218 -0
  79. package/src/panels/builtin-panels.ts +5 -0
  80. package/src/panels/cost-tracker-panel.ts +36 -10
  81. package/src/panels/diff-panel.ts +12 -43
  82. package/src/panels/eval-registry.ts +60 -0
  83. package/src/panels/fleet-panel.ts +800 -0
  84. package/src/panels/fleet-read-model.ts +477 -0
  85. package/src/panels/fleet-steer.ts +92 -0
  86. package/src/panels/fleet-stop.ts +125 -0
  87. package/src/panels/fleet-tabs.ts +230 -0
  88. package/src/panels/fleet-transcript.ts +356 -0
  89. package/src/panels/index.ts +7 -31
  90. package/src/panels/modals/hooks-modal.ts +187 -0
  91. package/src/panels/modals/keybindings-modal.ts +151 -0
  92. package/src/panels/modals/knowledge-modal.ts +158 -0
  93. package/src/panels/modals/local-auth-modal.ts +132 -0
  94. package/src/panels/modals/marketplace-modal.ts +218 -0
  95. package/src/panels/modals/memory-modal.ts +180 -0
  96. package/src/panels/modals/modal-surface-helpers.ts +54 -0
  97. package/src/panels/modals/modal-theme.ts +36 -0
  98. package/src/panels/modals/pairing-modal.ts +144 -0
  99. package/src/panels/modals/planning-modal.ts +282 -0
  100. package/src/panels/modals/plugins-modal.ts +174 -0
  101. package/src/panels/modals/policy-modal.ts +256 -0
  102. package/src/panels/modals/provider-health-modal.ts +136 -0
  103. package/src/panels/modals/remote-modal.ts +115 -0
  104. package/src/panels/modals/sandbox-modal.ts +150 -0
  105. package/src/panels/modals/security-modal.ts +217 -0
  106. package/src/panels/modals/services-modal.ts +179 -0
  107. package/src/panels/modals/settings-sync-modal.ts +142 -0
  108. package/src/panels/modals/skills-modal.ts +189 -0
  109. package/src/panels/modals/subscription-modal.ts +172 -0
  110. package/src/panels/modals/work-plan-modal.ts +149 -0
  111. package/src/panels/panel-confirm-overlay.ts +72 -0
  112. package/src/panels/panel-manager.ts +108 -5
  113. package/src/panels/project-planning-answer-actions.ts +4 -2
  114. package/src/panels/skills-panel.ts +7 -51
  115. package/src/panels/types.ts +13 -0
  116. package/src/permissions/hunk-selection.ts +179 -0
  117. package/src/permissions/prompt.ts +60 -4
  118. package/src/renderer/compaction-history-modal.ts +19 -3
  119. package/src/renderer/compaction-preview.ts +13 -2
  120. package/src/renderer/compaction-quality.ts +100 -0
  121. package/src/renderer/config-modal.ts +81 -0
  122. package/src/renderer/conversation-overlays.ts +10 -17
  123. package/src/renderer/fleet-tab-strip.ts +56 -0
  124. package/src/renderer/footer-tips.ts +10 -2
  125. package/src/renderer/git-status.ts +40 -0
  126. package/src/renderer/help-overlay.ts +2 -2
  127. package/src/renderer/model-workspace.ts +44 -1
  128. package/src/renderer/panel-workspace-bar.ts +8 -2
  129. package/src/renderer/turn-injection.ts +114 -0
  130. package/src/runtime/bootstrap-command-context.ts +21 -1
  131. package/src/runtime/bootstrap-command-parts.ts +34 -7
  132. package/src/runtime/bootstrap-core.ts +14 -4
  133. package/src/runtime/bootstrap-shell.ts +29 -16
  134. package/src/runtime/bootstrap.ts +11 -17
  135. package/src/runtime/code-index-services.ts +112 -0
  136. package/src/runtime/orchestrator-core-services.ts +49 -0
  137. package/src/runtime/process-lifecycle.ts +3 -1
  138. package/src/runtime/services.ts +91 -43
  139. package/src/runtime/ui-services.ts +8 -0
  140. package/src/runtime/workstream-services.ts +195 -0
  141. package/src/shell/blocking-input.ts +22 -1
  142. package/src/shell/ui-openers.ts +129 -17
  143. package/src/utils/format-duration.ts +8 -18
  144. package/src/utils/splash-lines.ts +1 -1
  145. package/src/version.ts +1 -1
  146. package/src/panels/agent-inspector-panel.ts +0 -786
  147. package/src/panels/approval-panel.ts +0 -252
  148. package/src/panels/automation-control-panel.ts +0 -479
  149. package/src/panels/cockpit-panel.ts +0 -481
  150. package/src/panels/cockpit-read-model.ts +0 -238
  151. package/src/panels/communication-panel.ts +0 -256
  152. package/src/panels/control-plane-panel.ts +0 -470
  153. package/src/panels/debug-panel.ts +0 -615
  154. package/src/panels/docs-panel.ts +0 -384
  155. package/src/panels/eval-panel.ts +0 -627
  156. package/src/panels/file-explorer-panel.ts +0 -673
  157. package/src/panels/file-preview-panel.ts +0 -517
  158. package/src/panels/hooks-panel.ts +0 -401
  159. package/src/panels/incident-review-panel.ts +0 -406
  160. package/src/panels/intelligence-panel.ts +0 -383
  161. package/src/panels/knowledge-graph-panel.ts +0 -515
  162. package/src/panels/marketplace-panel.ts +0 -399
  163. package/src/panels/memory-panel.ts +0 -558
  164. package/src/panels/ops-control-panel.ts +0 -329
  165. package/src/panels/ops-strategy-panel.ts +0 -321
  166. package/src/panels/orchestration-panel.ts +0 -465
  167. package/src/panels/panel-list-panel.ts +0 -566
  168. package/src/panels/plan-dashboard-panel.ts +0 -707
  169. package/src/panels/policy-panel.ts +0 -517
  170. package/src/panels/project-planning-panel.ts +0 -731
  171. package/src/panels/provider-health-panel.ts +0 -678
  172. package/src/panels/provider-health-tracker.ts +0 -310
  173. package/src/panels/provider-health-views.ts +0 -567
  174. package/src/panels/qr-panel.ts +0 -280
  175. package/src/panels/remote-panel.ts +0 -534
  176. package/src/panels/routes-panel.ts +0 -241
  177. package/src/panels/sandbox-panel.ts +0 -456
  178. package/src/panels/security-panel.ts +0 -447
  179. package/src/panels/services-panel.ts +0 -329
  180. package/src/panels/session-browser-panel.ts +0 -496
  181. package/src/panels/settings-sync-panel.ts +0 -398
  182. package/src/panels/subscription-panel.ts +0 -342
  183. package/src/panels/symbol-outline-panel.ts +0 -619
  184. package/src/panels/system-messages-panel.ts +0 -364
  185. package/src/panels/tasks-panel.ts +0 -608
  186. package/src/panels/thinking-panel.ts +0 -333
  187. package/src/panels/tool-inspector-panel.ts +0 -537
  188. package/src/panels/work-plan-panel.ts +0 -540
  189. package/src/panels/worktree-panel.ts +0 -360
  190. package/src/panels/wrfc-panel.ts +0 -790
  191. package/src/renderer/agent-detail-modal.ts +0 -466
  192. package/src/renderer/live-tail-modal.ts +0 -156
  193. package/src/renderer/process-modal.ts +0 -671
  194. package/src/renderer/qr-renderer.ts +0 -120
@@ -1,280 +0,0 @@
1
- import type { Line } from '../types/grid.ts';
2
- import { createEmptyLine } from '../types/grid.ts';
3
- import { BasePanel } from './base-panel.ts';
4
- import {
5
- buildKeyboardHints,
6
- buildPanelLine,
7
- buildPanelTitle,
8
- DEFAULT_PANEL_PALETTE,
9
- extendPalette,
10
- } from './polish.ts';
11
- import { type ConfirmState, handleConfirmInput, renderConfirmLines } from './confirm-state.ts';
12
- import { truncateDisplay } from '../utils/terminal-width.ts';
13
- import { renderQrMatrix, generateQrMatrix } from '../renderer/qr-renderer.ts';
14
- import { encodeConnectionPayload } from '@pellux/goodvibes-sdk/platform/pairing';
15
- import type { UserAuthManager } from '@pellux/goodvibes-sdk/platform/security';
16
- import type { UiControlPlaneSnapshot, UiReadModel } from '../runtime/ui-read-models.ts';
17
-
18
- // Fixed-width placeholder — deliberately NOT derived from the real secret's
19
- // length, so masking doesn't leak how long the token/password is.
20
- const SECRET_MASK = '••••••••••••';
21
-
22
- // Domain accents only; base chrome (header/headerBg/label/info) comes from
23
- // DEFAULT_PANEL_PALETTE. qrFg/qrBg stay pure black/white — scanner contrast
24
- // requirements override theming.
25
- const C = extendPalette(DEFAULT_PANEL_PALETTE, {
26
- token: '#a78bfa',
27
- qrFg: '#000000',
28
- qrBg: '#ffffff',
29
- } as const);
30
-
31
- /**
32
- * Connection info passed to the QR panel.
33
- * Populated at construction; updated when the token is regenerated.
34
- */
35
- export interface QrPanelConnectionInfo {
36
- /** Full connection URL (e.g. http://192.168.1.x:3141) */
37
- readonly url: string;
38
- /** Auth token */
39
- readonly token: string;
40
- /** Username associated with the companion session */
41
- readonly username: string;
42
- /** Bootstrap password for companion authentication */
43
- readonly password?: string;
44
- /** SDK/surface version (defaults to '0.0.0' if omitted) */
45
- readonly version?: string;
46
- /** Surface identifier (defaults to 'tui' if omitted) */
47
- readonly surface?: string;
48
- }
49
-
50
- /**
51
- * Callback used by the panel to regenerate the companion token.
52
- * Returns updated connection info.
53
- */
54
- export type RegenerateTokenFn = () => QrPanelConnectionInfo;
55
-
56
- /**
57
- * Callback used by the panel to copy text to the clipboard.
58
- */
59
- export type CopyToClipboardFn = (text: string) => void;
60
-
61
- /**
62
- * QrPanel - displays a QR code for companion app pairing.
63
- *
64
- * Shows connection URL, truncated token, and username above the QR code.
65
- * Supports `r` to regenerate the token and `c` to copy the token.
66
- *
67
- * QR matrix generation uses the SDK's `generateQrMatrix` via `encodeConnectionPayload`.
68
- */
69
- export class QrPanel extends BasePanel {
70
- private connectionInfo: QrPanelConnectionInfo;
71
- private readonly regenerateToken: RegenerateTokenFn | undefined;
72
- private readonly copyToClipboard: CopyToClipboardFn | undefined;
73
- private readonly controlPlaneReadModel: UiReadModel<UiControlPlaneSnapshot> | undefined;
74
- private readonly localUserAuthManager: Pick<UserAuthManager, 'inspect'> | undefined;
75
- private readonly unsub: (() => void) | null;
76
- private lastStatus = '';
77
- /** v toggles whether the token/password render in the clear. */
78
- private revealed = false;
79
- /** Set when r is pressed while a companion session is live — confirmed with y/Enter, cancelled with n/Esc. */
80
- private confirmRegenerate: ConfirmState<null> | null = null;
81
-
82
- public constructor(
83
- connectionInfo: QrPanelConnectionInfo,
84
- regenerateToken?: RegenerateTokenFn,
85
- copyToClipboard?: CopyToClipboardFn,
86
- controlPlaneReadModel?: UiReadModel<UiControlPlaneSnapshot>,
87
- localUserAuthManager?: Pick<UserAuthManager, 'inspect'>,
88
- ) {
89
- super('qr-code', 'QR Code', 'Q', 'session');
90
- this.connectionInfo = connectionInfo;
91
- this.regenerateToken = regenerateToken;
92
- this.copyToClipboard = copyToClipboard;
93
- this.controlPlaneReadModel = controlPlaneReadModel;
94
- this.localUserAuthManager = localUserAuthManager;
95
- this.unsub = controlPlaneReadModel ? controlPlaneReadModel.subscribe(() => this.markDirty()) : null;
96
- }
97
-
98
- public override onDestroy(): void {
99
- this.unsub?.();
100
- }
101
-
102
- private hasLiveCompanionSession(): boolean {
103
- return (this.localUserAuthManager?.inspect().sessionCount ?? 0) > 0;
104
- }
105
-
106
- private doRegenerate(): void {
107
- if (!this.regenerateToken) return;
108
- this.connectionInfo = this.regenerateToken();
109
- this.lastStatus = 'Token regenerated.';
110
- this.markDirty();
111
- }
112
-
113
- public handleInput(key: string): boolean {
114
- if (this.confirmRegenerate) {
115
- const result = handleConfirmInput(this.confirmRegenerate, key);
116
- if (result === 'confirmed') {
117
- this.confirmRegenerate = null;
118
- this.doRegenerate();
119
- return true;
120
- }
121
- if (result === 'cancelled') {
122
- this.confirmRegenerate = null;
123
- this.lastStatus = 'Regeneration cancelled.';
124
- this.markDirty();
125
- return true;
126
- }
127
- return true; // absorbed
128
- }
129
- if (key === 'r') {
130
- if (!this.regenerateToken) {
131
- this.lastStatus = 'Regeneration not available.';
132
- this.markDirty();
133
- return true;
134
- }
135
- if (this.hasLiveCompanionSession()) {
136
- this.confirmRegenerate = {
137
- subject: null,
138
- label: 'pairing token — a companion session is live and will be disconnected',
139
- verb: 'Regenerate',
140
- };
141
- this.markDirty();
142
- return true;
143
- }
144
- this.doRegenerate();
145
- return true;
146
- }
147
- if (key === 'v') {
148
- this.revealed = !this.revealed;
149
- this.markDirty();
150
- return true;
151
- }
152
- if (key === 'c') {
153
- if (this.copyToClipboard) {
154
- this.copyToClipboard(this.connectionInfo.token);
155
- this.lastStatus = 'Token copied to clipboard.';
156
- } else {
157
- this.lastStatus = 'Clipboard not available.';
158
- }
159
- this.markDirty();
160
- return true;
161
- }
162
- return false;
163
- }
164
-
165
- public render(width: number, height: number): Line[] {
166
- this.needsRender = false;
167
- const lines: Line[] = [];
168
-
169
- const { url, token, username, password } = this.connectionInfo;
170
- const valueWidth = Math.max(0, width - 12);
171
- const displayToken = this.revealed ? token : SECRET_MASK;
172
- const displayPassword = password !== undefined ? (this.revealed ? password : SECRET_MASK) : undefined;
173
-
174
- // ── Title + purpose: tell the operator exactly what this code is for ────
175
- lines.push(buildPanelTitle(width, 'Companion Pairing', C));
176
- lines.push(
177
- buildPanelLine(width, [
178
- [' Scan with the GoodVibes companion app to pair this session.', C.label],
179
- ]),
180
- );
181
-
182
- // ── Connection info header ─────────────────────────────────────────────
183
- // Token and (if present) bootstrap password are masked by default — they
184
- // are credentials, not display text — and only render in the clear while
185
- // `revealed` is toggled on via v.
186
- lines.push(createEmptyLine(width));
187
- lines.push(
188
- buildPanelLine(width, [
189
- [' URL ', C.label],
190
- [truncateDisplay(url, valueWidth), C.info],
191
- ]),
192
- );
193
- lines.push(
194
- buildPanelLine(width, [
195
- [' Token ', C.label],
196
- [truncateDisplay(displayToken, valueWidth), C.token],
197
- ]),
198
- );
199
- lines.push(
200
- buildPanelLine(width, [
201
- [' Username ', C.label],
202
- [truncateDisplay(username, valueWidth), C.value],
203
- ]),
204
- );
205
- if (displayPassword !== undefined) {
206
- lines.push(
207
- buildPanelLine(width, [
208
- [' Password ', C.label],
209
- [truncateDisplay(displayPassword, valueWidth), C.value],
210
- ]),
211
- );
212
- }
213
-
214
- // ── Connected companions ──────────────────────────────────────────────
215
- if (this.controlPlaneReadModel) {
216
- const connected = this.controlPlaneReadModel.getSnapshot().activeClientIds.length;
217
- lines.push(
218
- buildPanelLine(width, [
219
- [' Companions ', C.label],
220
- [`connected: ${connected}`, connected > 0 ? C.good : C.dim],
221
- ]),
222
- );
223
- }
224
- lines.push(createEmptyLine(width));
225
-
226
- // ── QR code ────────────────────────────────────────────────────────────
227
- const payload = encodeConnectionPayload({
228
- url: this.connectionInfo.url,
229
- token: this.connectionInfo.token,
230
- username: this.connectionInfo.username,
231
- ...(this.connectionInfo.password !== undefined ? { password: this.connectionInfo.password } : {}),
232
- version: this.connectionInfo.version ?? '0.0.0',
233
- surface: this.connectionInfo.surface ?? 'tui',
234
- });
235
- const matrix = generateQrMatrix(payload);
236
- const qrLines = renderQrMatrix(matrix.modules, width, { fg: C.qrFg, bg: C.qrBg });
237
- for (const qrLine of qrLines) {
238
- lines.push(qrLine);
239
- }
240
-
241
- lines.push(createEmptyLine(width));
242
-
243
- // ── Footer: confirm overlay (regenerate while a companion is live) takes
244
- // priority over the ephemeral status message ──────────────────────────
245
- const footerLines: Line[] = [];
246
- if (this.confirmRegenerate) {
247
- footerLines.push(...renderConfirmLines(width, this.confirmRegenerate));
248
- } else if (this.lastStatus) {
249
- footerLines.push(
250
- buildPanelLine(width, [
251
- [` ${this.lastStatus} `, C.label],
252
- ]),
253
- );
254
- }
255
-
256
- // ── Hints ── only advertise actions that are actually wired ─────────────
257
- const hints: Array<{ keys: string; label: string }> = [];
258
- if (this.regenerateToken) hints.push({ keys: 'r', label: 'regenerate token' });
259
- if (this.copyToClipboard) hints.push({ keys: 'c', label: 'copy token' });
260
- hints.push({ keys: 'v', label: this.revealed ? 'hide token' : 'reveal token' });
261
- const hintsLine = hints.length > 0
262
- ? buildKeyboardHints(width, hints, C)
263
- : buildPanelLine(width, [[' Pairing is read-only in this surface.', C.dim]]);
264
- footerLines.push(hintsLine);
265
-
266
- // Push the footer at the bottom if we have room, otherwise append it
267
- // directly after the QR code.
268
- const remaining = height - lines.length - footerLines.length;
269
- if (remaining > 0) {
270
- for (let i = 0; i < remaining; i++) lines.push(createEmptyLine(width));
271
- }
272
- lines.push(...footerLines);
273
-
274
- // Clamp to the exact requested height — the QR matrix's row count is
275
- // driven by payload size, not by `height`, so it can legitimately
276
- // overflow a small pane; never hand back more (or fewer) lines than asked.
277
- while (lines.length < height) lines.push(createEmptyLine(width));
278
- return lines.slice(0, height);
279
- }
280
- }