@pellux/goodvibes-tui 0.28.0 → 0.29.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.
- package/CHANGELOG.md +101 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/input/commands/cost-runtime.ts +49 -0
- package/src/input/commands/operator-runtime.ts +5 -1
- package/src/input/commands.ts +2 -0
- package/src/input/handler-feed-routes.ts +12 -35
- package/src/input/handler-feed.ts +4 -8
- package/src/input/handler-shortcuts.ts +51 -0
- package/src/input/handler.ts +43 -6
- package/src/input/keybindings.ts +48 -8
- package/src/input/panel-integration-actions.ts +107 -1
- package/src/main.ts +5 -1
- package/src/panels/agent-inspector-panel.ts +125 -36
- package/src/panels/agent-inspector-shared.ts +144 -0
- package/src/panels/approval-panel.ts +67 -16
- package/src/panels/automation-control-panel.ts +368 -124
- package/src/panels/builtin/agent.ts +28 -37
- package/src/panels/builtin/development.ts +40 -32
- package/src/panels/builtin/knowledge.ts +15 -6
- package/src/panels/builtin/operations.ts +178 -117
- package/src/panels/builtin/session.ts +35 -9
- package/src/panels/builtin/shared.ts +98 -6
- package/src/panels/cockpit-panel.ts +232 -73
- package/src/panels/communication-panel.ts +58 -20
- package/src/panels/confirm-state.ts +8 -1
- package/src/panels/control-plane-panel.ts +351 -118
- package/src/panels/cost-tracker-panel.ts +165 -7
- package/src/panels/debug-panel.ts +312 -159
- package/src/panels/diff-panel.ts +203 -57
- package/src/panels/docs-panel.ts +152 -66
- package/src/panels/eval-panel.ts +220 -42
- package/src/panels/file-explorer-panel.ts +202 -122
- package/src/panels/file-preview-panel.ts +132 -57
- package/src/panels/git-panel.ts +309 -128
- package/src/panels/hooks-panel.ts +150 -22
- package/src/panels/incident-review-panel.ts +223 -35
- package/src/panels/index.ts +0 -4
- package/src/panels/intelligence-panel.ts +212 -86
- package/src/panels/knowledge-graph-panel.ts +461 -101
- package/src/panels/local-auth-panel.ts +240 -28
- package/src/panels/marketplace-panel.ts +193 -26
- package/src/panels/memory-panel.ts +78 -77
- package/src/panels/ops-control-panel.ts +146 -29
- package/src/panels/ops-strategy-panel.ts +72 -4
- package/src/panels/orchestration-panel.ts +231 -69
- package/src/panels/panel-list-panel.ts +154 -131
- package/src/panels/panel-manager.ts +97 -9
- package/src/panels/plan-dashboard-panel.ts +333 -17
- package/src/panels/plugins-panel.ts +184 -29
- package/src/panels/policy-panel.ts +210 -38
- package/src/panels/polish-core.ts +7 -2
- package/src/panels/polish.ts +23 -4
- package/src/panels/project-planning-answer-actions.ts +134 -0
- package/src/panels/project-planning-panel.ts +62 -113
- package/src/panels/provider-health-panel.ts +434 -518
- package/src/panels/provider-health-routes.ts +203 -0
- package/src/panels/provider-health-tracker.ts +194 -6
- package/src/panels/provider-health-views.ts +560 -0
- package/src/panels/qr-panel.ts +116 -30
- package/src/panels/remote-panel.ts +114 -36
- package/src/panels/routes-panel.ts +63 -22
- package/src/panels/sandbox-panel.ts +174 -42
- package/src/panels/scrollable-list-panel.ts +19 -135
- package/src/panels/security-panel.ts +133 -33
- package/src/panels/services-panel.ts +116 -64
- package/src/panels/session-browser-panel.ts +73 -2
- package/src/panels/session-maintenance.ts +4 -122
- package/src/panels/settings-sync-panel.ts +335 -72
- package/src/panels/skills-panel.ts +157 -89
- package/src/panels/subscription-panel.ts +86 -33
- package/src/panels/symbol-outline-panel.ts +248 -108
- package/src/panels/system-messages-panel.ts +114 -13
- package/src/panels/tasks-panel.ts +326 -139
- package/src/panels/thinking-panel.ts +43 -10
- package/src/panels/token-budget-panel.ts +194 -18
- package/src/panels/tool-inspector-panel.ts +144 -34
- package/src/panels/types.ts +40 -4
- package/src/panels/work-plan-panel.ts +280 -17
- package/src/panels/worktree-panel.ts +175 -42
- package/src/panels/wrfc-panel.ts +116 -24
- package/src/renderer/conversation-overlays.ts +25 -11
- package/src/renderer/footer-tips.ts +41 -0
- package/src/renderer/fullscreen-primitives.ts +22 -16
- package/src/renderer/help-overlay.ts +91 -14
- package/src/renderer/hint-grammar.ts +52 -0
- package/src/renderer/layout.ts +7 -7
- package/src/renderer/modal-factory.ts +23 -15
- package/src/renderer/model-picker-overlay.ts +21 -7
- package/src/renderer/overlay-box.ts +16 -10
- package/src/renderer/process-indicator.ts +3 -1
- package/src/renderer/progress.ts +5 -4
- package/src/renderer/search-overlay.ts +27 -6
- package/src/renderer/session-picker-modal.ts +6 -4
- package/src/renderer/settings-modal.ts +70 -10
- package/src/renderer/shell-surface.ts +41 -15
- package/src/renderer/status-glyphs.ts +11 -9
- package/src/renderer/theme.ts +60 -3
- package/src/renderer/ui-factory.ts +41 -30
- package/src/renderer/ui-primitives.ts +23 -2
- package/src/runtime/bootstrap-shell.ts +35 -18
- package/src/runtime/diagnostics/panels/index.ts +0 -3
- package/src/shell/ui-openers.ts +14 -22
- package/src/utils/format-duration.ts +2 -2
- package/src/utils/splash-lines.ts +44 -3
- package/src/version.ts +1 -1
- package/src/work-plans/work-plan-store.ts +13 -0
- package/src/panels/agent-logs-panel.ts +0 -635
- package/src/panels/agent-logs-shared.ts +0 -129
- package/src/panels/context-visualizer-panel.ts +0 -238
- package/src/panels/forensics-panel.ts +0 -378
- package/src/panels/provider-account-snapshot.ts +0 -259
- package/src/panels/provider-accounts-panel.ts +0 -255
- package/src/panels/provider-stats-panel.ts +0 -391
- package/src/panels/schedule-panel.ts +0 -365
- package/src/panels/watchers-panel.ts +0 -213
- package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
- package/src/runtime/diagnostics/panels/policy.ts +0 -177
|
@@ -109,12 +109,41 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
|
|
|
109
109
|
homeDirectory: services.homeDirectory,
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
+
const foundationClients = createRuntimeFoundationClients({
|
|
113
|
+
runtimeServices: services,
|
|
114
|
+
tasksReadModel: uiServices.readModels.tasks,
|
|
115
|
+
taskManager,
|
|
116
|
+
opsControlPlane,
|
|
117
|
+
});
|
|
118
|
+
const {
|
|
119
|
+
directTransport,
|
|
120
|
+
hookApi,
|
|
121
|
+
knowledgeApi,
|
|
122
|
+
mcpApi,
|
|
123
|
+
opsApi,
|
|
124
|
+
providerApi,
|
|
125
|
+
} = foundationClients;
|
|
126
|
+
const planRuntime = createShellPlanRuntime({
|
|
127
|
+
adaptivePlanner: services.adaptivePlanner,
|
|
128
|
+
runtimeBus,
|
|
129
|
+
});
|
|
130
|
+
|
|
112
131
|
const openAgentDetailRef: { fn: (agentId: string) => void } = { fn: (_agentId: string) => {} };
|
|
113
132
|
|
|
133
|
+
// WO-139: initial cost-budget alert threshold (USD; 0/unset = disabled).
|
|
134
|
+
// Once the session starts, the real control surface is the CostTrackerPanel
|
|
135
|
+
// itself — the in-panel 'b' key and /cost budget <usd> both call
|
|
136
|
+
// CostTrackerPanel.setBudgetThreshold() directly on the live panel instance.
|
|
137
|
+
const parsedBudgetThreshold = Number(process.env.GOODVIBES_COST_BUDGET_USD);
|
|
138
|
+
const initialCostBudgetThreshold = Number.isFinite(parsedBudgetThreshold) && parsedBudgetThreshold > 0
|
|
139
|
+
? parsedBudgetThreshold
|
|
140
|
+
: 0;
|
|
141
|
+
|
|
114
142
|
let commandContextRef: CommandContext | null = null;
|
|
115
143
|
registerBuiltinPanels(services.panelManager, {
|
|
116
144
|
configManager,
|
|
117
145
|
getOrchestratorUsage: () => orchestrator.usage as { input: number; output: number; cacheRead: number; cacheWrite: number; model?: string },
|
|
146
|
+
budgetThreshold: initialCostBudgetThreshold,
|
|
118
147
|
toolRegistry,
|
|
119
148
|
providerRegistry: services.providerRegistry,
|
|
120
149
|
contextWindow: services.providerRegistry.getContextWindowForModel(services.providerRegistry.getCurrentModel()),
|
|
@@ -153,6 +182,12 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
|
|
|
153
182
|
mcpRegistry: services.mcpRegistry,
|
|
154
183
|
openAgentDetail: (agentId: string) => openAgentDetailRef.fn(agentId),
|
|
155
184
|
daemonHomeDir: join(services.homeDirectory, '.goodvibes', 'daemon'),
|
|
185
|
+
opsApi,
|
|
186
|
+
planRuntime,
|
|
187
|
+
watcherRegistry: services.watcherRegistry,
|
|
188
|
+
runtimeStore,
|
|
189
|
+
openPanel: (panelId: string) => { services.panelManager.open(panelId); },
|
|
190
|
+
knowledgeApi,
|
|
156
191
|
});
|
|
157
192
|
services.panelManager.prewarmRegistered();
|
|
158
193
|
|
|
@@ -170,29 +205,11 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
|
|
|
170
205
|
|
|
171
206
|
const commandRegistry = new CommandRegistry();
|
|
172
207
|
registerBuiltinCommands(commandRegistry);
|
|
173
|
-
const foundationClients = createRuntimeFoundationClients({
|
|
174
|
-
runtimeServices: services,
|
|
175
|
-
tasksReadModel: uiServices.readModels.tasks,
|
|
176
|
-
taskManager,
|
|
177
|
-
opsControlPlane,
|
|
178
|
-
});
|
|
179
|
-
const {
|
|
180
|
-
directTransport,
|
|
181
|
-
hookApi,
|
|
182
|
-
knowledgeApi,
|
|
183
|
-
mcpApi,
|
|
184
|
-
opsApi,
|
|
185
|
-
providerApi,
|
|
186
|
-
} = foundationClients;
|
|
187
208
|
const remoteRuntime = createShellRemoteCommandService({
|
|
188
209
|
readModels: uiServices.readModels,
|
|
189
210
|
remoteRunnerRegistry: services.remoteRunnerRegistry,
|
|
190
211
|
runtimeStore,
|
|
191
212
|
});
|
|
192
|
-
const planRuntime = createShellPlanRuntime({
|
|
193
|
-
adaptivePlanner: services.adaptivePlanner,
|
|
194
|
-
runtimeBus,
|
|
195
|
-
});
|
|
196
213
|
|
|
197
214
|
const commandContext: CommandContext = createBootstrapCommandContext({
|
|
198
215
|
configManager,
|
|
@@ -12,13 +12,10 @@ export type { InspectableDomain } from '@/runtime/index.ts';
|
|
|
12
12
|
export { HealthPanel } from '@/runtime/index.ts';
|
|
13
13
|
export { DivergencePanel } from '@/runtime/index.ts';
|
|
14
14
|
export { ReplayPanel } from '@/runtime/index.ts';
|
|
15
|
-
export { PolicyPanel } from './policy.ts';
|
|
16
|
-
export type { PolicyPanelSnapshot } from './policy.ts';
|
|
17
15
|
export { ToolContractsPanel } from '@/runtime/index.ts';
|
|
18
16
|
export { TransportPanel } from '@/runtime/index.ts';
|
|
19
17
|
export type { TransportPanelSnapshot } from '@/runtime/index.ts';
|
|
20
18
|
export { OpsPanel } from './ops.ts';
|
|
21
19
|
export type { OpsAuditEntry } from './ops.ts';
|
|
22
|
-
export { PanelResourcesPanel } from './panel-resources.ts';
|
|
23
20
|
export { SecurityPanel } from '@/runtime/index.ts';
|
|
24
21
|
export type { SecurityPanelSnapshot } from '@/runtime/index.ts';
|
package/src/shell/ui-openers.ts
CHANGED
|
@@ -253,6 +253,7 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
|
|
|
253
253
|
if (!input.helpOverlayActive) input.modalOpened('help');
|
|
254
254
|
input.helpOverlayActive = !input.helpOverlayActive;
|
|
255
255
|
input.helpScrollOffset = 0;
|
|
256
|
+
render();
|
|
256
257
|
};
|
|
257
258
|
|
|
258
259
|
commandContext.openShortcutsOverlay = () => {
|
|
@@ -292,19 +293,16 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
|
|
|
292
293
|
};
|
|
293
294
|
|
|
294
295
|
commandContext.openPanelPicker = () => {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
panelManager.show();
|
|
304
|
-
input.panelFocused = true;
|
|
305
|
-
conversation.setSplashSuppressed(true);
|
|
296
|
+
// Focus ownership lives in PanelManager (focusTarget); read it there rather
|
|
297
|
+
// than tracking a parallel input.panelFocused flag. Toggle semantics: if the
|
|
298
|
+
// workspace is visible AND already focused, hide it; otherwise reveal and
|
|
299
|
+
// focus it (opening the panel list when nothing is open yet).
|
|
300
|
+
if (panelManager.isVisible() && panelManager.getFocusTarget() === 'panel') {
|
|
301
|
+
panelManager.hide();
|
|
302
|
+
panelManager.focusPrompt();
|
|
303
|
+
conversation.setSplashSuppressed(false);
|
|
306
304
|
conversation.rebuildHistory();
|
|
307
|
-
} else
|
|
305
|
+
} else {
|
|
308
306
|
if (panelManager.getAllOpen().length === 0) {
|
|
309
307
|
try {
|
|
310
308
|
panelManager.open('panel-list');
|
|
@@ -313,26 +311,20 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
|
|
|
313
311
|
}
|
|
314
312
|
}
|
|
315
313
|
panelManager.show();
|
|
316
|
-
|
|
314
|
+
panelManager.focusPanels();
|
|
317
315
|
conversation.setSplashSuppressed(true);
|
|
318
316
|
conversation.rebuildHistory();
|
|
319
|
-
} else {
|
|
320
|
-
panelManager.hide();
|
|
321
|
-
input.panelFocused = false;
|
|
322
|
-
conversation.setSplashSuppressed(false);
|
|
323
|
-
conversation.rebuildHistory();
|
|
324
317
|
}
|
|
325
318
|
render();
|
|
326
319
|
};
|
|
327
320
|
|
|
328
321
|
commandContext.focusPanels = () => {
|
|
329
|
-
|
|
330
|
-
input.panelFocused = true;
|
|
322
|
+
panelManager.focusPanels();
|
|
331
323
|
render();
|
|
332
324
|
};
|
|
333
325
|
|
|
334
326
|
commandContext.focusPrompt = () => {
|
|
335
|
-
|
|
327
|
+
panelManager.focusPrompt();
|
|
336
328
|
input.indicatorFocused = false;
|
|
337
329
|
render();
|
|
338
330
|
};
|
|
@@ -340,7 +332,7 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
|
|
|
340
332
|
commandContext.showPanel = (panelId, pane) => {
|
|
341
333
|
panelManager.open(panelId, pane);
|
|
342
334
|
panelManager.show();
|
|
343
|
-
|
|
335
|
+
panelManager.focusPanels();
|
|
344
336
|
conversation.setSplashSuppressed(true);
|
|
345
337
|
conversation.rebuildHistory();
|
|
346
338
|
render();
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
/**
|
|
11
11
|
* Format a latency value in milliseconds with sub-second precision.
|
|
12
12
|
*
|
|
13
|
-
* Used by: debug-panel, provider-health-panel
|
|
13
|
+
* Used by: debug-panel, provider-health-panel
|
|
14
14
|
*
|
|
15
15
|
* ms <= 0 → 'n/a'
|
|
16
16
|
* ms >= 10000 → '12.3s' (one decimal)
|
|
@@ -42,7 +42,7 @@ export function formatDuration(ms: number): string {
|
|
|
42
42
|
/**
|
|
43
43
|
* Format a short eval/forensics duration; treats undefined as '?ms'.
|
|
44
44
|
*
|
|
45
|
-
* Used by:
|
|
45
|
+
* Used by: incident-review-panel, eval-panel
|
|
46
46
|
*
|
|
47
47
|
* undefined → '?ms'
|
|
48
48
|
* ms < 1000 → '500ms'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { center, getDisplayWidth } from './terminal-width.ts';
|
|
1
|
+
import { center, getDisplayWidth, truncateDisplay } from './terminal-width.ts';
|
|
2
2
|
import { VERSION } from '../version.ts';
|
|
3
3
|
|
|
4
4
|
const ART_LINES = [
|
|
@@ -22,15 +22,33 @@ const TAGLINE = '[ good vibes ・ A I ・ いい雰囲気
|
|
|
22
22
|
|
|
23
23
|
const VERSION_LINE = ` ✦ v${VERSION} █ terminal AI assistant █ 自動コード ✦`;
|
|
24
24
|
|
|
25
|
+
/** Fixed hint line — the three primary shell entry points. */
|
|
26
|
+
const HINT_LINE = 'Ctrl+P panels / ? help / F2 processes';
|
|
27
|
+
|
|
25
28
|
export interface SplashOptions {
|
|
26
29
|
workingDir?: string;
|
|
27
30
|
model?: string;
|
|
28
31
|
provider?: string;
|
|
29
32
|
toolCount?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Session id of the most recent session, as resolved by readLastSessionPointer.
|
|
35
|
+
* When present, the splash advertises a resume affordance.
|
|
36
|
+
*/
|
|
37
|
+
lastSessionId?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Collapse a $HOME-prefixed working directory to a leading `~`. */
|
|
41
|
+
function collapseHome(dir: string): string {
|
|
42
|
+
const home = typeof process !== 'undefined' ? process.env.HOME ?? '' : '';
|
|
43
|
+
return home && dir.startsWith(home) ? '~' + dir.slice(home.length) : dir;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Center a meta line, truncating first so it never overflows the terminal width. */
|
|
47
|
+
function metaLine(text: string, columns: number): string {
|
|
48
|
+
return center(truncateDisplay(text, Math.max(0, columns)), columns);
|
|
30
49
|
}
|
|
31
50
|
|
|
32
51
|
export function getSplashLines(columns: number, opts: SplashOptions = {}): string[] {
|
|
33
|
-
const splashHint = 'start chatting or type /help for commands';
|
|
34
52
|
const lines: string[] = [
|
|
35
53
|
center(TOP_BORDER, columns),
|
|
36
54
|
...ART_LINES.map((line) => center(line, columns)),
|
|
@@ -40,7 +58,30 @@ export function getSplashLines(columns: number, opts: SplashOptions = {}): strin
|
|
|
40
58
|
'',
|
|
41
59
|
];
|
|
42
60
|
|
|
43
|
-
|
|
61
|
+
// Live session context — real state pulled from SplashOptions rather than a
|
|
62
|
+
// static "/help" signpost: model (provider) and tool count on one line, cwd
|
|
63
|
+
// on the next.
|
|
64
|
+
const contextBits: string[] = [];
|
|
65
|
+
if (opts.model) {
|
|
66
|
+
contextBits.push(opts.provider ? `${opts.model} (${opts.provider})` : opts.model);
|
|
67
|
+
}
|
|
68
|
+
if (typeof opts.toolCount === 'number') {
|
|
69
|
+
contextBits.push(`${opts.toolCount} tool${opts.toolCount === 1 ? '' : 's'}`);
|
|
70
|
+
}
|
|
71
|
+
if (contextBits.length > 0) {
|
|
72
|
+
lines.push(metaLine(contextBits.join(' · '), columns));
|
|
73
|
+
}
|
|
74
|
+
if (opts.workingDir) {
|
|
75
|
+
lines.push(metaLine(collapseHome(opts.workingDir), columns));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Last-session resume pointer (readLastSessionPointer result), when recorded.
|
|
79
|
+
if (opts.lastSessionId) {
|
|
80
|
+
lines.push(metaLine(`↩ resume last session — /sessions resume ${opts.lastSessionId}`, columns));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
lines.push('');
|
|
84
|
+
lines.push(metaLine(HINT_LINE, columns));
|
|
44
85
|
|
|
45
86
|
return lines;
|
|
46
87
|
}
|
package/src/version.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
// The prebuild script updates the fallback value before compilation.
|
|
7
7
|
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
8
|
// which is correct regardless of the process working directory.
|
|
9
|
-
let _version = '0.
|
|
9
|
+
let _version = '0.29.0';
|
|
10
10
|
try {
|
|
11
11
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
|
|
12
12
|
_version = pkg.version ?? _version;
|
|
@@ -267,6 +267,19 @@ export class WorkPlanStore {
|
|
|
267
267
|
return removed;
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Writes the current plan's `toMarkdown()` output to a sibling `.md` file
|
|
272
|
+
* next to the JSON store file, so the checklist can be opened outside the
|
|
273
|
+
* TUI. Returns the written path alongside the markdown that was written.
|
|
274
|
+
*/
|
|
275
|
+
exportMarkdown(): { readonly path: string; readonly markdown: string } {
|
|
276
|
+
const plan = this.readPlan();
|
|
277
|
+
const markdown = this.toMarkdown(plan);
|
|
278
|
+
const path = this.filePath.replace(/\.json$/, '.md');
|
|
279
|
+
atomicWriteFileSync(path, `${markdown}\n`, { mkdirp: true });
|
|
280
|
+
return { path, markdown };
|
|
281
|
+
}
|
|
282
|
+
|
|
270
283
|
toMarkdown(plan: WorkPlan = this.readPlan()): string {
|
|
271
284
|
const lines = [
|
|
272
285
|
`# ${plan.title}`,
|