@pellux/goodvibes-agent 1.0.40 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-agent",
3
- "version": "1.0.40",
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.columns || 80;
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.columns || 80;
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
- if (input.onboardingWizard.active) return stdout.rows || 24;
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 (stdout.rows || 24) - 2 - estimateShellFooterHeight(promptLines, currentModel.contextWindow);
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.columns || 80;
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, this.frontBuffer);
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(this.frontBuffer, newBuffer);
282
+ const diff = this.diffEngine.diff(previousFrontBuffer, newBuffer);
279
283
  if (diff) {
280
284
  allowTerminalWrite(() => this.stdout.write(diff));
281
285
  }
@@ -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.columns || 80;
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
  }
@@ -35,6 +35,7 @@ export function createOnboardingFullscreenComposite(
35
35
  header: [],
36
36
  viewport,
37
37
  footer: [],
38
+ forceFullRedraw: true,
38
39
  panelWidth: 0,
39
40
  };
40
41
  }
@@ -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.40';
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;