@agentuity/cli 0.0.111 → 0.0.112

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 (99) hide show
  1. package/bin/cli.ts +4 -0
  2. package/dist/agents-docs.d.ts +5 -4
  3. package/dist/agents-docs.d.ts.map +1 -1
  4. package/dist/agents-docs.js +28 -8
  5. package/dist/agents-docs.js.map +1 -1
  6. package/dist/cmd/auth/apikey.d.ts +2 -0
  7. package/dist/cmd/auth/apikey.d.ts.map +1 -0
  8. package/dist/cmd/auth/apikey.js +31 -0
  9. package/dist/cmd/auth/apikey.js.map +1 -0
  10. package/dist/cmd/auth/index.d.ts.map +1 -1
  11. package/dist/cmd/auth/index.js +9 -1
  12. package/dist/cmd/auth/index.js.map +1 -1
  13. package/dist/cmd/build/ast.d.ts.map +1 -1
  14. package/dist/cmd/build/ast.js +103 -2
  15. package/dist/cmd/build/ast.js.map +1 -1
  16. package/dist/cmd/build/entry-generator.d.ts +2 -1
  17. package/dist/cmd/build/entry-generator.d.ts.map +1 -1
  18. package/dist/cmd/build/entry-generator.js +152 -9
  19. package/dist/cmd/build/entry-generator.js.map +1 -1
  20. package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
  21. package/dist/cmd/build/vite/agent-discovery.js +4 -3
  22. package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
  23. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  24. package/dist/cmd/build/vite/index.js +2 -1
  25. package/dist/cmd/build/vite/index.js.map +1 -1
  26. package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
  27. package/dist/cmd/build/vite/registry-generator.js +45 -0
  28. package/dist/cmd/build/vite/registry-generator.js.map +1 -1
  29. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  30. package/dist/cmd/build/vite/vite-builder.js +2 -1
  31. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  32. package/dist/cmd/cloud/deploy-fork.d.ts +32 -0
  33. package/dist/cmd/cloud/deploy-fork.d.ts.map +1 -0
  34. package/dist/cmd/cloud/deploy-fork.js +258 -0
  35. package/dist/cmd/cloud/deploy-fork.js.map +1 -0
  36. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  37. package/dist/cmd/cloud/deploy.js +62 -3
  38. package/dist/cmd/cloud/deploy.js.map +1 -1
  39. package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -1
  40. package/dist/cmd/cloud/sandbox/get.js +19 -0
  41. package/dist/cmd/cloud/sandbox/get.js.map +1 -1
  42. package/dist/cmd/cloud/ssh.d.ts.map +1 -1
  43. package/dist/cmd/cloud/ssh.js +9 -3
  44. package/dist/cmd/cloud/ssh.js.map +1 -1
  45. package/dist/cmd/dev/index.d.ts.map +1 -1
  46. package/dist/cmd/dev/index.js +18 -12
  47. package/dist/cmd/dev/index.js.map +1 -1
  48. package/dist/config.js +1 -1
  49. package/dist/config.js.map +1 -1
  50. package/dist/log-collector.d.ts +30 -0
  51. package/dist/log-collector.d.ts.map +1 -0
  52. package/dist/log-collector.js +74 -0
  53. package/dist/log-collector.js.map +1 -0
  54. package/dist/output.d.ts.map +1 -1
  55. package/dist/output.js +2 -1
  56. package/dist/output.js.map +1 -1
  57. package/dist/steps.d.ts.map +1 -1
  58. package/dist/steps.js +48 -3
  59. package/dist/steps.js.map +1 -1
  60. package/dist/tui/box.d.ts.map +1 -1
  61. package/dist/tui/box.js +1 -6
  62. package/dist/tui/box.js.map +1 -1
  63. package/dist/tui/symbols.d.ts.map +1 -1
  64. package/dist/tui/symbols.js +4 -0
  65. package/dist/tui/symbols.js.map +1 -1
  66. package/dist/tui.d.ts +21 -12
  67. package/dist/tui.d.ts.map +1 -1
  68. package/dist/tui.js +74 -25
  69. package/dist/tui.js.map +1 -1
  70. package/dist/types.d.ts +71 -0
  71. package/dist/types.d.ts.map +1 -1
  72. package/dist/types.js.map +1 -1
  73. package/dist/typescript-errors.d.ts.map +1 -1
  74. package/dist/typescript-errors.js +2 -2
  75. package/dist/typescript-errors.js.map +1 -1
  76. package/package.json +5 -5
  77. package/src/agents-docs.ts +42 -8
  78. package/src/cmd/auth/apikey.ts +36 -0
  79. package/src/cmd/auth/index.ts +9 -1
  80. package/src/cmd/build/ast.ts +120 -2
  81. package/src/cmd/build/entry-generator.ts +157 -10
  82. package/src/cmd/build/vite/agent-discovery.ts +4 -1
  83. package/src/cmd/build/vite/index.ts +2 -1
  84. package/src/cmd/build/vite/registry-generator.ts +47 -0
  85. package/src/cmd/build/vite/vite-builder.ts +2 -1
  86. package/src/cmd/cloud/deploy-fork.ts +296 -0
  87. package/src/cmd/cloud/deploy.ts +70 -3
  88. package/src/cmd/cloud/sandbox/get.ts +17 -0
  89. package/src/cmd/cloud/ssh.ts +13 -3
  90. package/src/cmd/dev/index.ts +18 -13
  91. package/src/config.ts +1 -1
  92. package/src/log-collector.ts +77 -0
  93. package/src/output.ts +2 -1
  94. package/src/steps.ts +52 -4
  95. package/src/tui/box.ts +1 -7
  96. package/src/tui/symbols.ts +5 -0
  97. package/src/tui.ts +77 -25
  98. package/src/types.ts +85 -0
  99. package/src/typescript-errors.ts +2 -1
package/src/steps.ts CHANGED
@@ -8,7 +8,8 @@
8
8
  import type { ColorScheme } from './terminal';
9
9
  import type { LogLevel } from './types';
10
10
  import { ValidationInputError, ValidationOutputError, type IssuesType } from '@agentuity/server';
11
- import { clearLastLines } from './tui';
11
+ import { clearLastLines, isTTYLike } from './tui';
12
+ import { appendLog, isLogCollectionEnabled } from './log-collector';
12
13
 
13
14
  // Spinner frames
14
15
  const FRAMES = ['◐', '◓', '◑', '◒'];
@@ -129,6 +130,38 @@ function renderStepLine(step: StepState, spinner?: string): string {
129
130
  }
130
131
  }
131
132
 
133
+ /**
134
+ * Generate a clean log line for a completed step (no ANSI, no animation)
135
+ */
136
+ function getCleanStepLog(step: StepState): string {
137
+ if (step.status === 'success') {
138
+ return `${ICONS.success} ${step.label}`;
139
+ } else if (step.status === 'skipped') {
140
+ const reason = step.skipReason ? ` (${step.skipReason})` : '';
141
+ return `${ICONS.skipped} ${step.label}${reason}`;
142
+ } else if (step.status === 'error') {
143
+ const error = step.errorMessage ? `: ${step.errorMessage}` : '';
144
+ return `${ICONS.error} ${step.label}${error}`;
145
+ }
146
+ return `${ICONS.pending} ${step.label}`;
147
+ }
148
+
149
+ /**
150
+ * Emit clean log for a completed step if log collection is enabled
151
+ */
152
+ function emitCleanStepLog(step: StepState): void {
153
+ if (!isLogCollectionEnabled()) return;
154
+
155
+ appendLog(getCleanStepLog(step));
156
+
157
+ // Also emit any output lines
158
+ if (step.output && step.output.length > 0) {
159
+ for (const line of step.output) {
160
+ appendLog(` ${line}`);
161
+ }
162
+ }
163
+ }
164
+
132
165
  /**
133
166
  * Render all steps from state, including output boxes
134
167
  * Returns the rendered output and total line count
@@ -212,7 +245,7 @@ function enablePauseResume(
212
245
  * Returns resume function
213
246
  */
214
247
  export function pauseStepUI(clear = false): () => void {
215
- if (!process.stdout.isTTY || !getTotalLinesFn) {
248
+ if (!isTTYLike() || !getTotalLinesFn) {
216
249
  return () => {}; // No-op if not TTY or not in step context
217
250
  }
218
251
 
@@ -466,6 +499,9 @@ async function runStepsTUI(steps: Step[]): Promise<void> {
466
499
  activeInterval = null;
467
500
  }
468
501
 
502
+ // Emit clean log for this step (for external log collection)
503
+ emitCleanStepLog(stepState);
504
+
469
505
  // Final render with outcome
470
506
  stepState.progress = undefined;
471
507
  const finalRender = renderAllSteps(state, -1);
@@ -529,6 +565,19 @@ async function runStepsPlain(steps: Step[]): Promise<void> {
529
565
  };
530
566
  }
531
567
 
568
+ // Build step state for clean log emission
569
+ const stepState: StepState = {
570
+ label: step.label,
571
+ status: outcome.status,
572
+ output: outcome.output,
573
+ skipReason: outcome.status === 'skipped' ? outcome.reason : undefined,
574
+ errorMessage: outcome.status === 'error' ? outcome.message : undefined,
575
+ errorCause: outcome.status === 'error' ? outcome.cause : undefined,
576
+ };
577
+
578
+ // Emit clean log for this step
579
+ emitCleanStepLog(stepState);
580
+
532
581
  // Print final state
533
582
  if (outcome.status === 'success') {
534
583
  console.log(`${greenColor}${ICONS.success}${COLORS.reset} ${step.label}`);
@@ -579,8 +628,7 @@ async function runStepsPlain(steps: Step[]): Promise<void> {
579
628
  * Run a series of steps with animated progress
580
629
  */
581
630
  export async function runSteps(steps: Step[], logLevel?: LogLevel): Promise<void> {
582
- const useTUI =
583
- process.stdout.isTTY && (!logLevel || ['info', 'warn', 'error'].includes(logLevel));
631
+ const useTUI = isTTYLike() && (!logLevel || ['info', 'warn', 'error'].includes(logLevel));
584
632
 
585
633
  if (useTUI) {
586
634
  await runStepsTUI(steps);
package/src/tui/box.ts CHANGED
@@ -3,13 +3,7 @@
3
3
  */
4
4
  import { symbols } from './symbols';
5
5
  import { colors } from './colors';
6
-
7
- /**
8
- * Get terminal width
9
- */
10
- function getTerminalWidth(): number {
11
- return process.stdout.columns || 80;
12
- }
6
+ import { getTerminalWidth } from '../tui';
13
7
 
14
8
  /**
15
9
  * Get string width (accounting for ANSI codes and OSC 8 hyperlinks)
@@ -5,6 +5,11 @@
5
5
 
6
6
  // Detect unicode support
7
7
  const isUnicodeSupported = (): boolean => {
8
+ // FORCE_UNICODE overrides detection (used by fork wrapper)
9
+ if (process.env.FORCE_UNICODE === '1') {
10
+ return true;
11
+ }
12
+
8
13
  if (process.platform !== 'win32') {
9
14
  return process.env.TERM !== 'linux'; // Linux console (kernel) doesn't support Unicode
10
15
  }
package/src/tui.ts CHANGED
@@ -24,6 +24,10 @@ function ensureCursorRestoration(): void {
24
24
  exitHandlerInstalled = true;
25
25
 
26
26
  const restoreCursor = () => {
27
+ // Skip cursor restoration in CI - terminals don't support these sequences
28
+ if (process.env.CI) {
29
+ return;
30
+ }
27
31
  // Restore cursor visibility
28
32
  process.stderr.write('\x1B[?25h');
29
33
  };
@@ -62,7 +66,38 @@ export const ICONS = {
62
66
  bullet: '•',
63
67
  } as const;
64
68
 
69
+ /**
70
+ * Check if we should treat stdout as a TTY (real TTY or FORCE_COLOR set by fork wrapper)
71
+ * Returns false in CI environments since CI terminals don't support cursor control sequences
72
+ */
73
+ export function isTTYLike(): boolean {
74
+ if (process.env.CI) {
75
+ return false;
76
+ }
77
+ return process.stdout.isTTY || process.env.FORCE_COLOR === '1';
78
+ }
79
+
80
+ /**
81
+ * Get terminal width, respecting COLUMNS env var for piped processes
82
+ */
83
+ export function getTerminalWidth(defaultWidth = 80): number {
84
+ if (process.stdout.columns) {
85
+ return process.stdout.columns;
86
+ }
87
+ if (process.env.COLUMNS) {
88
+ const cols = parseInt(process.env.COLUMNS, 10);
89
+ if (!isNaN(cols) && cols > 0) {
90
+ return cols;
91
+ }
92
+ }
93
+ return defaultWidth;
94
+ }
95
+
65
96
  export function shouldUseColors(): boolean {
97
+ // FORCE_COLOR overrides TTY detection (used by fork wrapper)
98
+ if (process.env.FORCE_COLOR === '1') {
99
+ return true;
100
+ }
66
101
  return (
67
102
  !process.env.NO_COLOR &&
68
103
  !process.env.CI &&
@@ -71,7 +106,9 @@ export function shouldUseColors(): boolean {
71
106
  );
72
107
  }
73
108
 
74
- // Color definitions (light/dark adaptive) using Bun.color
109
+ // Color definitions (light/dark adaptive)
110
+ // Note: We use direct ANSI codes instead of Bun.color() because Bun.color()
111
+ // returns corrupted sequences when stdout is not a TTY (even with FORCE_COLOR=1)
75
112
  function getColors() {
76
113
  const USE_COLORS = shouldUseColors();
77
114
  if (!USE_COLORS) {
@@ -90,36 +127,36 @@ function getColors() {
90
127
 
91
128
  return {
92
129
  success: {
93
- light: Bun.color('#008000', 'ansi') || '\x1b[32m', // green
94
- dark: Bun.color('#00FF00', 'ansi') || '\x1b[92m', // bright green
130
+ light: '\x1b[32m', // green
131
+ dark: '\x1b[92m', // bright green
95
132
  },
96
133
  error: {
97
- light: Bun.color('#CC0000', 'ansi') || '\x1b[31m', // red
98
- dark: Bun.color('#FF5555', 'ansi') || '\x1b[91m', // bright red
134
+ light: '\x1b[31m', // red
135
+ dark: '\x1b[91m', // bright red
99
136
  },
100
137
  warning: {
101
- light: Bun.color('#B58900', 'ansi') || '\x1b[33m', // yellow
102
- dark: Bun.color('#FFFF55', 'ansi') || '\x1b[93m', // bright yellow
138
+ light: '\x1b[33m', // yellow
139
+ dark: '\x1b[93m', // bright yellow
103
140
  },
104
141
  info: {
105
- light: Bun.color('#008B8B', 'ansi') || '\x1b[36m', // dark cyan
106
- dark: Bun.color('#55FFFF', 'ansi') || '\x1b[96m', // bright cyan
142
+ light: '\x1b[36m', // dark cyan
143
+ dark: '\x1b[96m', // bright cyan
107
144
  },
108
145
  muted: {
109
- light: Bun.color('#808080', 'ansi') || '\x1b[90m', // gray
110
- dark: Bun.color('#888888', 'ansi') || '\x1b[90m', // darker gray
146
+ light: '\x1b[90m', // gray
147
+ dark: '\x1b[90m', // gray
111
148
  },
112
149
  bold: {
113
150
  light: '\x1b[1m',
114
151
  dark: '\x1b[1m',
115
152
  },
116
153
  link: {
117
- light: '\x1b[34;4m', // blue underline (need ANSI for underline)
154
+ light: '\x1b[34;4m', // blue underline
118
155
  dark: '\x1b[94;4m', // bright blue underline
119
156
  },
120
157
  primary: {
121
- light: Bun.color('#000000', 'ansi') || '\x1b[30m', // black
122
- dark: Bun.color('#FFFFFF', 'ansi') || '\x1b[97m', // white
158
+ light: '\x1b[30m', // black
159
+ dark: '\x1b[97m', // white
123
160
  },
124
161
  reset: '\x1b[0m',
125
162
  } as const;
@@ -410,8 +447,12 @@ export function getDisplayWidth(str: string): number {
410
447
  * Strip all ANSI escape sequences from a string
411
448
  */
412
449
  export function stripAnsi(str: string): string {
413
- // eslint-disable-next-line no-control-regex
414
- return str.replace(/\u001b\[[0-9;]*m/g, '').replace(/\u001b\]8;;[^\u0007]*\u0007/g, '');
450
+ /* eslint-disable no-control-regex */
451
+ return str
452
+ .replace(/\u001b\[[0-9;]*m/g, '') // SGR sequences (colors, bold, etc.)
453
+ .replace(/\u001b\[\?[0-9;]*[a-zA-Z]/g, '') // DEC private mode (cursor show/hide, etc.)
454
+ .replace(/\u001b\]8;;[^\u0007]*\u0007/g, ''); // OSC 8 hyperlinks
455
+ /* eslint-enable no-control-regex */
415
456
  }
416
457
 
417
458
  /**
@@ -457,8 +498,8 @@ export function truncateToWidth(str: string, maxWidth: number, ellipsis = '...')
457
498
  while (i < str.length && visibleIndex < cutIndex) {
458
499
  // Check for ANSI escape sequence
459
500
  if (str[i] === '\u001b') {
460
- // Copy entire ANSI sequence
461
- // eslint-disable-next-line no-control-regex
501
+ /* eslint-disable no-control-regex */
502
+ // Copy entire SGR sequence (colors, bold, etc.)
462
503
  const match = str.slice(i).match(/^\u001b\[[0-9;]*m/);
463
504
  if (match) {
464
505
  result += match[0];
@@ -466,14 +507,22 @@ export function truncateToWidth(str: string, maxWidth: number, ellipsis = '...')
466
507
  continue;
467
508
  }
468
509
 
510
+ // Check for DEC private mode (cursor show/hide, etc.)
511
+ const decMatch = str.slice(i).match(/^\u001b\[\?[0-9;]*[a-zA-Z]/);
512
+ if (decMatch) {
513
+ result += decMatch[0];
514
+ i += decMatch[0].length;
515
+ continue;
516
+ }
517
+
469
518
  // Check for OSC 8 hyperlink
470
- // eslint-disable-next-line no-control-regex
471
519
  const oscMatch = str.slice(i).match(/^\u001b\]8;;[^\u0007]*\u0007/);
472
520
  if (oscMatch) {
473
521
  result += oscMatch[0];
474
522
  i += oscMatch[0].length;
475
523
  continue;
476
524
  }
525
+ /* eslint-enable no-control-regex */
477
526
  }
478
527
 
479
528
  // Copy visible character
@@ -525,7 +574,7 @@ interface BannerOptions {
525
574
  */
526
575
  export function banner(title: string, body: string, options?: BannerOptions): void {
527
576
  // Get terminal width, default to 120 if not available
528
- const termWidth = process.stdout.columns || 120;
577
+ const termWidth = getTerminalWidth(120);
529
578
 
530
579
  const border = {
531
580
  topLeft: '╭',
@@ -853,8 +902,11 @@ function extractLeadingAnsiCodes(str: string): string {
853
902
  */
854
903
  function stripAnsiCodes(str: string): string {
855
904
  // Remove all ANSI escape sequences
856
- // eslint-disable-next-line no-control-regex
857
- return str.replace(/\x1b\[[0-9;]*m/g, '');
905
+ /* eslint-disable no-control-regex */
906
+ return str
907
+ .replace(/\x1b\[[0-9;]*m/g, '') // SGR sequences
908
+ .replace(/\x1b\[\?[0-9;]*[a-zA-Z]/g, ''); // DEC private mode
909
+ /* eslint-enable no-control-regex */
858
910
  }
859
911
 
860
912
  /**
@@ -1120,7 +1172,7 @@ export async function spinner<T>(
1120
1172
  let linesRendered = 0;
1121
1173
 
1122
1174
  // Get terminal width for truncation
1123
- const termWidth = process.stderr.columns || 80;
1175
+ const termWidth = getTerminalWidth(80);
1124
1176
  const maxLineWidth = Math.min(80, termWidth);
1125
1177
 
1126
1178
  // Function to render spinner with optional log lines
@@ -1399,7 +1451,7 @@ export async function runCommand(options: CommandRunnerOptions): Promise<number>
1399
1451
  const reset = getColor('reset');
1400
1452
 
1401
1453
  // Get terminal width
1402
- const termWidth = process.stdout.columns || 80;
1454
+ const termWidth = getTerminalWidth(80);
1403
1455
  const maxCmdWidth = Math.min(40, termWidth);
1404
1456
  const maxLineWidth = Math.min(80, termWidth);
1405
1457
 
@@ -1886,7 +1938,7 @@ export function table<T extends Record<string, unknown>>(
1886
1938
 
1887
1939
  // Determine layout mode
1888
1940
  const layout = options?.layout ?? 'auto';
1889
- const termWidth = process.stdout.columns || 80;
1941
+ const termWidth = getTerminalWidth(80);
1890
1942
  const tableWidth = calculateTableWidth(data, columnNames);
1891
1943
  const useVertical = layout === 'vertical' || (layout === 'auto' && tableWidth > termWidth);
1892
1944
 
package/src/types.ts CHANGED
@@ -128,6 +128,83 @@ export interface WorkbenchConfig {
128
128
  headers?: Record<string, string>;
129
129
  }
130
130
 
131
+ /**
132
+ * Web analytics configuration for SDK-created applications
133
+ */
134
+ export interface AnalyticsConfig {
135
+ /**
136
+ * Enable/disable analytics
137
+ * @default true
138
+ */
139
+ enabled?: boolean;
140
+
141
+ /**
142
+ * Require explicit user consent before tracking
143
+ * When true, analytics is no-op until optIn() is called
144
+ * @default false
145
+ */
146
+ requireConsent?: boolean;
147
+
148
+ /**
149
+ * Track click events on elements with data-analytics attribute
150
+ * @default true
151
+ */
152
+ trackClicks?: boolean;
153
+
154
+ /**
155
+ * Track scroll depth (25%, 50%, 75%, 100%)
156
+ * @default true
157
+ */
158
+ trackScroll?: boolean;
159
+
160
+ /**
161
+ * Track outbound link clicks
162
+ * @default true
163
+ */
164
+ trackOutboundLinks?: boolean;
165
+
166
+ /**
167
+ * Track form submissions
168
+ * @default false
169
+ */
170
+ trackForms?: boolean;
171
+
172
+ /**
173
+ * Track Core Web Vitals
174
+ * @default true
175
+ */
176
+ trackWebVitals?: boolean;
177
+
178
+ /**
179
+ * Track JavaScript errors
180
+ * @default true
181
+ */
182
+ trackErrors?: boolean;
183
+
184
+ /**
185
+ * Track SPA navigation (popstate, pushState)
186
+ * Automatically tracks virtual pageviews on client-side route changes
187
+ * @default true
188
+ */
189
+ trackSPANavigation?: boolean;
190
+
191
+ /**
192
+ * Sample rate (0-1). 1 = 100% of events
193
+ * @default 1
194
+ */
195
+ sampleRate?: number;
196
+
197
+ /**
198
+ * URL patterns to exclude from tracking (regex strings)
199
+ */
200
+ excludePatterns?: string[];
201
+
202
+ /**
203
+ * Custom data to include with every event
204
+ */
205
+ globalProperties?: Record<string, unknown>;
206
+ }
207
+
131
208
  /**
132
209
  * Agentuity project configuration (declarative)
133
210
  */
@@ -136,6 +213,14 @@ export interface AgentuityConfig {
136
213
  * Workbench configuration
137
214
  */
138
215
  workbench?: WorkbenchConfig;
216
+
217
+ /**
218
+ * Web analytics configuration
219
+ * Set to false to disable, or provide options object
220
+ * @default true (enabled with defaults)
221
+ */
222
+ analytics?: boolean | AnalyticsConfig;
223
+
139
224
  /**
140
225
  * Vite plugins to add to the client build
141
226
  * These are added AFTER Agentuity's built-in plugins
@@ -18,6 +18,7 @@ import {
18
18
  getColor,
19
19
  plural,
20
20
  sourceLink,
21
+ getTerminalWidth,
21
22
  truncateToWidth,
22
23
  } from './tui';
23
24
  import { symbols } from './tui/symbols';
@@ -318,7 +319,7 @@ export async function formatTypeScriptErrors(
318
319
  }
319
320
 
320
321
  // Calculate terminal constraints
321
- const terminalWidth = process.stdout.columns || 80;
322
+ const terminalWidth = getTerminalWidth(80);
322
323
  const boxChrome = 6;
323
324
  const maxAvailableWidth = terminalWidth - boxChrome;
324
325