@pellux/goodvibes-agent 1.0.39 → 1.0.41
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 +10 -0
- package/dist/package/{ast-grep-napi.linux-x64-gnu-mkk8xwww.node → ast-grep-napi.linux-x64-gnu-swtppvy9.node} +0 -0
- package/dist/package/{ast-grep-napi.linux-x64-musl-ryqtgdv6.node → ast-grep-napi.linux-x64-musl-ttfcdtap.node} +0 -0
- package/dist/package/main.js +48366 -49999
- package/package.json +1 -1
- package/src/main.ts +7 -6
- package/src/renderer/compositor.ts +7 -3
- package/src/renderer/onboarding/onboarding-wizard.ts +1 -1
- package/src/renderer/onboarding/onboarding-workspace.ts +199 -0
- package/src/runtime/bootstrap-core.ts +2 -1
- package/src/shell/onboarding-fullscreen.ts +1 -0
- package/src/shell/terminal-size.ts +37 -0
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-agent",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.41",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "GoodVibes personal operator assistant TUI with a proactive Agent product brain, isolated Agent Knowledge, local profiles, routines, skills, personas, and explicit build delegation.",
|
|
6
6
|
"type": "module",
|
package/src/main.ts
CHANGED
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
import type { SessionSnapshot } from '@/runtime/index.ts';
|
|
42
42
|
import { handleBlockingShellInput, type PendingPermissionState } from './shell/blocking-input.ts';
|
|
43
43
|
import { createOnboardingFullscreenComposite } from './shell/onboarding-fullscreen.ts';
|
|
44
|
+
import { getTerminalSize } from './shell/terminal-size.ts';
|
|
44
45
|
import { wireShellUiOpeners } from './shell/ui-openers.ts';
|
|
45
46
|
import { deriveComposerState } from './core/composer-state.ts';
|
|
46
47
|
import { buildPersistedSessionContext, formatReturnContextForDisplay, getReturnContextMode, maybeAssistReturnContextSummary } from '@/runtime/index.ts';
|
|
@@ -115,7 +116,7 @@ async function main() {
|
|
|
115
116
|
sessionLineageTracker: ctx.services.sessionLineageTracker,
|
|
116
117
|
idempotencyStore: ctx.services.idempotencyStore,
|
|
117
118
|
});
|
|
118
|
-
let activeConversationWidth = stdout.
|
|
119
|
+
let activeConversationWidth = getTerminalSize(stdout).width;
|
|
119
120
|
conversation.setWidthProvider(() => activeConversationWidth);
|
|
120
121
|
{
|
|
121
122
|
const hitlMode = configManager.get('behavior.hitlMode') as HITLMode | undefined;
|
|
@@ -166,17 +167,18 @@ async function main() {
|
|
|
166
167
|
let scrollLocked = true;
|
|
167
168
|
|
|
168
169
|
const getPromptContentWidth = () => {
|
|
169
|
-
const w = stdout.
|
|
170
|
+
const w = getTerminalSize(stdout).width;
|
|
170
171
|
const boxMargin = 2;
|
|
171
172
|
const boxWidth = w - (boxMargin * 2);
|
|
172
173
|
return boxWidth - 4 - 3; // minus padding (4) minus prefix width (3: ' > ')
|
|
173
174
|
};
|
|
174
175
|
|
|
175
176
|
const getViewportHeight = (): number => {
|
|
176
|
-
|
|
177
|
+
const { height } = getTerminalSize(stdout);
|
|
178
|
+
if (input.onboardingWizard.active) return height;
|
|
177
179
|
const promptLines: number = input.getVisiblePromptLineCount(getPromptContentWidth());
|
|
178
180
|
const currentModel = providerRegistry.getCurrentModel();
|
|
179
|
-
return
|
|
181
|
+
return height - 2 - estimateShellFooterHeight(promptLines, currentModel.contextWindow);
|
|
180
182
|
};
|
|
181
183
|
|
|
182
184
|
const scroll = (delta: number) => {
|
|
@@ -469,8 +471,7 @@ async function main() {
|
|
|
469
471
|
};
|
|
470
472
|
|
|
471
473
|
const render = () => {
|
|
472
|
-
const width = stdout
|
|
473
|
-
const height = stdout.rows || 24;
|
|
474
|
+
const { width, height } = getTerminalSize(stdout);
|
|
474
475
|
|
|
475
476
|
if (input.onboardingWizard.active) {
|
|
476
477
|
input.setPanelMouseLayout(null);
|
|
@@ -44,6 +44,7 @@ export interface CompositeRequest {
|
|
|
44
44
|
header: Line[];
|
|
45
45
|
viewport: Line[];
|
|
46
46
|
footer: Line[];
|
|
47
|
+
forceFullRedraw?: boolean;
|
|
47
48
|
selection?: SelectionInfo;
|
|
48
49
|
search?: SearchInfo;
|
|
49
50
|
panel?: PanelCompositeData;
|
|
@@ -74,12 +75,15 @@ export class Compositor {
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
public composite(params: CompositeRequest): void {
|
|
77
|
-
const { width, height, header, viewport, footer, selection, search, panel, panelWidth } = params;
|
|
78
|
+
const { width, height, header, viewport, footer, forceFullRedraw, selection, search, panel, panelWidth } = params;
|
|
79
|
+
const previousFrontBuffer = forceFullRedraw ? null : this.frontBuffer;
|
|
80
|
+
if (forceFullRedraw) this.diffEngine.reset();
|
|
81
|
+
|
|
78
82
|
// R3: Reuse back-buffer instead of allocating each frame
|
|
79
83
|
if (!this.backBuffer) {
|
|
80
84
|
this.backBuffer = new TerminalBuffer(width, height);
|
|
81
85
|
} else {
|
|
82
|
-
this.backBuffer.reset(width, height,
|
|
86
|
+
this.backBuffer.reset(width, height, previousFrontBuffer);
|
|
83
87
|
}
|
|
84
88
|
const newBuffer = this.backBuffer;
|
|
85
89
|
|
|
@@ -275,7 +279,7 @@ export class Compositor {
|
|
|
275
279
|
|
|
276
280
|
// 4. Diff and Render
|
|
277
281
|
// R3: Diff against front-buffer (last-rendered), then swap front/back — no clone() needed
|
|
278
|
-
const diff = this.diffEngine.diff(
|
|
282
|
+
const diff = this.diffEngine.diff(previousFrontBuffer, newBuffer);
|
|
279
283
|
if (diff) {
|
|
280
284
|
allowTerminalWrite(() => this.stdout.write(diff));
|
|
281
285
|
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import type { Line } from '../../types/grid.ts';
|
|
2
|
+
import { GLYPHS } from '../ui-primitives.ts';
|
|
3
|
+
import {
|
|
4
|
+
borderLine,
|
|
5
|
+
clamp,
|
|
6
|
+
contentLine,
|
|
7
|
+
drawHorizontalRule,
|
|
8
|
+
drawVerticalRule,
|
|
9
|
+
fillRange,
|
|
10
|
+
FULLSCREEN_PALETTE,
|
|
11
|
+
makeLine,
|
|
12
|
+
writeText,
|
|
13
|
+
} from '../fullscreen-primitives.ts';
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
borderLine,
|
|
17
|
+
clamp,
|
|
18
|
+
contentLine,
|
|
19
|
+
fillRange,
|
|
20
|
+
makeLine,
|
|
21
|
+
padDisplay,
|
|
22
|
+
stableWindow,
|
|
23
|
+
writeText,
|
|
24
|
+
} from '../fullscreen-primitives.ts';
|
|
25
|
+
export { FULLSCREEN_PALETTE as WORKSPACE_PALETTE } from '../fullscreen-primitives.ts';
|
|
26
|
+
|
|
27
|
+
const WORKSPACE_PALETTE = FULLSCREEN_PALETTE;
|
|
28
|
+
|
|
29
|
+
export interface WorkspaceRow {
|
|
30
|
+
readonly text: string;
|
|
31
|
+
readonly selected?: boolean;
|
|
32
|
+
readonly bold?: boolean;
|
|
33
|
+
readonly dim?: boolean;
|
|
34
|
+
readonly fg?: string;
|
|
35
|
+
readonly bg?: string;
|
|
36
|
+
readonly kind?: 'group' | 'item' | 'more' | 'empty';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface FullscreenWorkspaceRenderOptions {
|
|
40
|
+
readonly width: number;
|
|
41
|
+
readonly height: number;
|
|
42
|
+
readonly title: string;
|
|
43
|
+
readonly stateLabel?: string;
|
|
44
|
+
readonly leftHeader: string;
|
|
45
|
+
readonly mainHeader: string;
|
|
46
|
+
readonly leftRows: readonly WorkspaceRow[];
|
|
47
|
+
readonly contextRows: readonly WorkspaceRow[];
|
|
48
|
+
readonly controlRows: readonly WorkspaceRow[];
|
|
49
|
+
readonly footer: string;
|
|
50
|
+
readonly leftWidth?: number;
|
|
51
|
+
readonly contextRatio?: number;
|
|
52
|
+
readonly minContextRows?: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface FullscreenWorkspaceMetrics {
|
|
56
|
+
readonly safeWidth: number;
|
|
57
|
+
readonly safeHeight: number;
|
|
58
|
+
readonly leftWidth: number;
|
|
59
|
+
readonly centerWidth: number;
|
|
60
|
+
readonly bodyRows: number;
|
|
61
|
+
readonly contextWidth: number;
|
|
62
|
+
readonly contextRows: number;
|
|
63
|
+
readonly controlRows: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function drawVertical(line: Line, x: number, bg = ''): void {
|
|
67
|
+
if (x <= 0 || x >= line.length - 1) return;
|
|
68
|
+
drawVerticalRule(line, x, WORKSPACE_PALETTE.border, bg);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function drawHorizontalRange(line: Line, startX: number, endX: number, bg = ''): void {
|
|
72
|
+
drawHorizontalRule(line, Math.max(1, startX), Math.min(line.length - 2, endX), WORKSPACE_PALETTE.border, bg);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function leftWidthFor(width: number, explicit?: number): number {
|
|
76
|
+
if (explicit !== undefined) return clamp(explicit, 14, Math.max(14, width - 24));
|
|
77
|
+
return width < 80
|
|
78
|
+
? clamp(Math.round(width * 0.32), 14, Math.max(14, width - 24))
|
|
79
|
+
: clamp(Math.round(width * 0.22), 24, 34);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function getFullscreenWorkspaceMetrics(options: Pick<
|
|
83
|
+
FullscreenWorkspaceRenderOptions,
|
|
84
|
+
'width' | 'height' | 'leftWidth' | 'contextRatio' | 'minContextRows'
|
|
85
|
+
>): FullscreenWorkspaceMetrics {
|
|
86
|
+
const safeWidth = Math.max(1, options.width);
|
|
87
|
+
const safeHeight = Math.max(12, options.height);
|
|
88
|
+
const leftWidth = leftWidthFor(safeWidth, options.leftWidth);
|
|
89
|
+
const centerWidth = Math.max(20, safeWidth - leftWidth - 3);
|
|
90
|
+
const bodyTop = 3;
|
|
91
|
+
const footerY = safeHeight - 2;
|
|
92
|
+
const bodyRows = Math.max(4, footerY - bodyTop);
|
|
93
|
+
const contextWidth = Math.max(10, centerWidth - 2);
|
|
94
|
+
const maxContextRows = Math.max(3, bodyRows - 4);
|
|
95
|
+
const minContextRows = clamp(options.minContextRows ?? 10, 3, maxContextRows);
|
|
96
|
+
const contextRows = clamp(
|
|
97
|
+
Math.round(bodyRows * (options.contextRatio ?? 0.4)),
|
|
98
|
+
Math.min(minContextRows, maxContextRows),
|
|
99
|
+
maxContextRows,
|
|
100
|
+
);
|
|
101
|
+
const controlRows = Math.max(3, bodyRows - contextRows - 1);
|
|
102
|
+
return { safeWidth, safeHeight, leftWidth, centerWidth, bodyRows, contextWidth, contextRows, controlRows };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function rowFg(row: WorkspaceRow, fallback: string): string {
|
|
106
|
+
if (row.fg) return row.fg;
|
|
107
|
+
if (row.kind === 'group') return WORKSPACE_PALETTE.subtitle;
|
|
108
|
+
if (row.kind === 'more') return WORKSPACE_PALETTE.dim;
|
|
109
|
+
return fallback;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function renderFullscreenWorkspace(options: FullscreenWorkspaceRenderOptions): Line[] {
|
|
113
|
+
const { safeWidth, safeHeight, leftWidth, centerWidth, bodyRows, contextWidth, contextRows } = getFullscreenWorkspaceMetrics(options);
|
|
114
|
+
const leftStart = 1;
|
|
115
|
+
const dividerX = leftWidth + 1;
|
|
116
|
+
const centerStart = dividerX + 1;
|
|
117
|
+
const centerEnd = safeWidth - 2;
|
|
118
|
+
const bodyTop = 3;
|
|
119
|
+
const separatorY = bodyTop + contextRows;
|
|
120
|
+
const lines: Line[] = [];
|
|
121
|
+
|
|
122
|
+
const top = borderLine(safeWidth, GLYPHS.frame.topLeft, GLYPHS.frame.horizontal, GLYPHS.frame.topRight);
|
|
123
|
+
writeText(top, 2, safeWidth - 4, ` ${options.title} `, { fg: WORKSPACE_PALETTE.title, bold: true });
|
|
124
|
+
if (options.stateLabel) {
|
|
125
|
+
writeText(top, Math.max(2, safeWidth - options.stateLabel.length - 4), options.stateLabel.length, options.stateLabel, {
|
|
126
|
+
fg: WORKSPACE_PALETTE.subtitle,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
lines.push(top);
|
|
130
|
+
|
|
131
|
+
const header = contentLine(safeWidth, WORKSPACE_PALETTE.footerBg);
|
|
132
|
+
drawVertical(header, dividerX, WORKSPACE_PALETTE.footerBg);
|
|
133
|
+
writeText(header, leftStart + 1, leftWidth - 2, options.leftHeader, {
|
|
134
|
+
fg: WORKSPACE_PALETTE.subtitle,
|
|
135
|
+
bold: true,
|
|
136
|
+
bg: WORKSPACE_PALETTE.footerBg,
|
|
137
|
+
});
|
|
138
|
+
writeText(header, centerStart + 1, centerWidth - 2, options.mainHeader, {
|
|
139
|
+
fg: WORKSPACE_PALETTE.subtitle,
|
|
140
|
+
bold: true,
|
|
141
|
+
bg: WORKSPACE_PALETTE.footerBg,
|
|
142
|
+
});
|
|
143
|
+
lines.push(header);
|
|
144
|
+
|
|
145
|
+
const headerSep = contentLine(safeWidth, '');
|
|
146
|
+
drawVertical(headerSep, dividerX);
|
|
147
|
+
drawHorizontalRange(headerSep, 1, safeWidth - 2);
|
|
148
|
+
lines.push(headerSep);
|
|
149
|
+
|
|
150
|
+
for (let row = 0; row < bodyRows; row += 1) {
|
|
151
|
+
const y = bodyTop + row;
|
|
152
|
+
const inContext = y < separatorY;
|
|
153
|
+
const inSeparator = y === separatorY;
|
|
154
|
+
const bg = inSeparator ? '' : inContext ? WORKSPACE_PALETTE.contextBg : WORKSPACE_PALETTE.controlsBg;
|
|
155
|
+
const line = contentLine(safeWidth, bg);
|
|
156
|
+
fillRange(line, 1, dividerX - 1, WORKSPACE_PALETTE.categoryBg);
|
|
157
|
+
drawVertical(line, dividerX, bg);
|
|
158
|
+
|
|
159
|
+
const leftRow = options.leftRows[row] ?? { text: '', kind: 'empty' as const };
|
|
160
|
+
if (leftRow.selected) fillRange(line, leftStart, dividerX - 1, WORKSPACE_PALETTE.selectedBg);
|
|
161
|
+
writeText(line, leftStart + 1, leftWidth - 3, leftRow.text, {
|
|
162
|
+
fg: leftRow.selected ? WORKSPACE_PALETTE.text : rowFg(leftRow, WORKSPACE_PALETTE.muted),
|
|
163
|
+
bg: leftRow.selected ? WORKSPACE_PALETTE.selectedBg : WORKSPACE_PALETTE.categoryBg,
|
|
164
|
+
bold: leftRow.bold ?? (leftRow.selected || leftRow.kind === 'group'),
|
|
165
|
+
dim: leftRow.dim,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
if (inSeparator) {
|
|
169
|
+
drawHorizontalRange(line, centerStart, centerEnd);
|
|
170
|
+
} else if (inContext) {
|
|
171
|
+
const contextRow = options.contextRows[row] ?? { text: '', kind: 'empty' as const };
|
|
172
|
+
writeText(line, centerStart + 1, contextWidth, contextRow.text, {
|
|
173
|
+
fg: rowFg(contextRow, WORKSPACE_PALETTE.text),
|
|
174
|
+
bg,
|
|
175
|
+
bold: contextRow.bold,
|
|
176
|
+
dim: contextRow.dim ?? contextRow.text.length === 0,
|
|
177
|
+
});
|
|
178
|
+
} else {
|
|
179
|
+
const controlRow = options.controlRows[row - contextRows - 1] ?? { text: '', kind: 'empty' as const };
|
|
180
|
+
if (controlRow.selected) fillRange(line, centerStart, centerEnd, WORKSPACE_PALETTE.selectedBg);
|
|
181
|
+
writeText(line, centerStart + 1, contextWidth, controlRow.text, {
|
|
182
|
+
fg: controlRow.selected ? WORKSPACE_PALETTE.text : rowFg(controlRow, WORKSPACE_PALETTE.text),
|
|
183
|
+
bg: controlRow.selected ? WORKSPACE_PALETTE.selectedBg : bg,
|
|
184
|
+
bold: controlRow.bold ?? controlRow.selected,
|
|
185
|
+
dim: controlRow.dim ?? controlRow.text.length === 0,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
lines.push(line);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const footer = contentLine(safeWidth, WORKSPACE_PALETTE.footerBg);
|
|
193
|
+
writeText(footer, 2, safeWidth - 4, options.footer, { fg: WORKSPACE_PALETTE.muted, bg: WORKSPACE_PALETTE.footerBg });
|
|
194
|
+
lines.push(footer);
|
|
195
|
+
lines.push(borderLine(safeWidth, GLYPHS.frame.bottomLeft, GLYPHS.frame.horizontal, GLYPHS.frame.bottomRight));
|
|
196
|
+
|
|
197
|
+
while (lines.length < safeHeight) lines.unshift(makeLine(safeWidth));
|
|
198
|
+
return lines.slice(-safeHeight);
|
|
199
|
+
}
|
|
@@ -38,6 +38,7 @@ import { registerAgentNotifyTool } from '../tools/agent-notify-tool.ts';
|
|
|
38
38
|
import { registerAgentOperatorActionTool } from '../tools/agent-operator-action-tool.ts';
|
|
39
39
|
import { registerAgentOperatorBriefingTool } from '../tools/agent-operator-briefing-tool.ts';
|
|
40
40
|
import { registerAgentReminderScheduleTool } from '../tools/agent-reminder-schedule-tool.ts';
|
|
41
|
+
import { getTerminalSize } from '../shell/terminal-size.ts';
|
|
41
42
|
import { registerAgentWorkPlanTool } from '../tools/agent-work-plan-tool.ts';
|
|
42
43
|
import { compactRegisteredToolDefinitions } from '../tools/tool-definition-compaction.ts';
|
|
43
44
|
import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
|
|
@@ -200,7 +201,7 @@ export async function initializeBootstrapCore(
|
|
|
200
201
|
});
|
|
201
202
|
|
|
202
203
|
const conversation = new ConversationManager(() => {
|
|
203
|
-
const width = stdout.
|
|
204
|
+
const width = getTerminalSize(stdout).width;
|
|
204
205
|
if (panelManager.isVisible() && panelManager.getAllOpen().length > 0) {
|
|
205
206
|
return Math.max(1, panelManager.getLeftWidth(width) - 1);
|
|
206
207
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface TerminalSize {
|
|
2
|
+
readonly width: number;
|
|
3
|
+
readonly height: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
type TerminalSizeSource = Pick<NodeJS.WriteStream, 'columns' | 'rows'> & {
|
|
7
|
+
readonly getWindowSize?: () => readonly [number, number] | readonly [number, number, number, number];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
function positiveInteger(value: unknown): number | undefined {
|
|
11
|
+
if (typeof value === 'number' && Number.isFinite(value) && value > 0) return Math.floor(value);
|
|
12
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
13
|
+
const parsed = Number(value);
|
|
14
|
+
if (Number.isFinite(parsed) && parsed > 0) return Math.floor(parsed);
|
|
15
|
+
}
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getTerminalSize(stdout: TerminalSizeSource): TerminalSize {
|
|
20
|
+
let windowWidth: number | undefined;
|
|
21
|
+
let windowHeight: number | undefined;
|
|
22
|
+
try {
|
|
23
|
+
const size = stdout.getWindowSize?.();
|
|
24
|
+
if (Array.isArray(size)) {
|
|
25
|
+
windowWidth = positiveInteger(size[0]);
|
|
26
|
+
windowHeight = positiveInteger(size[1]);
|
|
27
|
+
}
|
|
28
|
+
} catch {
|
|
29
|
+
windowWidth = undefined;
|
|
30
|
+
windowHeight = undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
width: positiveInteger(stdout.columns) ?? windowWidth ?? positiveInteger(process.env.COLUMNS) ?? 80,
|
|
35
|
+
height: positiveInteger(stdout.rows) ?? windowHeight ?? positiveInteger(process.env.LINES) ?? 24,
|
|
36
|
+
};
|
|
37
|
+
}
|
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 = '1.0.
|
|
9
|
+
let _version = '1.0.41';
|
|
10
10
|
try {
|
|
11
11
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
|
|
12
12
|
readonly version?: unknown;
|