vigthoria-cli 1.11.8 → 1.11.10

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.
@@ -6,6 +6,15 @@ import inquirer from 'inquirer';
6
6
  import * as fs from 'fs';
7
7
  import * as os from 'os';
8
8
  import * as path from 'path';
9
+ function terminalColumns() {
10
+ const raw = Number(process.stdout.columns || 80);
11
+ return Number.isFinite(raw) && raw > 0 ? raw : 80;
12
+ }
13
+ function fitLine(text, reserve = 0) {
14
+ const width = Math.max(24, terminalColumns() - reserve);
15
+ const clean = String(text || '').replace(/\s+/g, ' ').trim();
16
+ return clean.length > width ? `${clean.slice(0, Math.max(1, width - 1))}…` : clean;
17
+ }
9
18
  function validateExistingDirectory(input) {
10
19
  const trimmed = String(input || '').trim().replace(/^['"]|['"]$/g, '');
11
20
  if (!trimmed) {
@@ -84,19 +93,19 @@ export async function runAgentSessionMenu(defaults = {}) {
84
93
  };
85
94
  console.log();
86
95
  console.log(chalk.cyan('═══ Agent Session Setup ═══'));
87
- console.log(chalk.gray(`Workspace: ${session.workspacePath}`));
88
- console.log(chalk.gray(`Debug: ${session.debugMode ? 'on' : 'off'} | Auto-approve: ${session.autoApprove ? 'on' : 'off'} | Auto-save: ${session.autoSave ? 'on' : 'off'}`));
96
+ console.log(chalk.gray(fitLine(`Workspace: ${session.workspacePath}`)));
97
+ console.log(chalk.gray(fitLine(`Debug: ${session.debugMode ? 'on' : 'off'} | Auto-approve: ${session.autoApprove ? 'on' : 'off'} | Auto-save: ${session.autoSave ? 'on' : 'off'}`)));
89
98
  console.log();
90
99
  const { action } = await inquirer.prompt([
91
100
  {
92
101
  type: 'list',
93
102
  name: 'action',
94
- message: 'Configure your session',
103
+ message: 'Session',
95
104
  choices: [
96
- { name: 'Start session with current settings', value: 'start' },
97
- { name: 'Set workspace path (existing directory)', value: 'set-workspace' },
98
- { name: 'Create new directory and set as workspace', value: 'create-workspace' },
99
- { name: 'Session settings (debug, auto-save, auto-approve)', value: 'settings' },
105
+ { name: 'Start now', value: 'start' },
106
+ { name: 'Set workspace', value: 'set-workspace' },
107
+ { name: 'Create workspace', value: 'create-workspace' },
108
+ { name: 'Settings', value: 'settings' },
100
109
  ],
101
110
  },
102
111
  ]);
@@ -153,8 +162,8 @@ export async function runAgentSessionMenu(defaults = {}) {
153
162
  }
154
163
  console.log();
155
164
  console.log(chalk.cyan('── Session ready ──'));
156
- console.log(chalk.gray(`Workspace: ${session.workspacePath}`));
157
- console.log(chalk.gray(`Debug: ${session.debugMode ? 'on' : 'off'} | Auto-approve: ${session.autoApprove ? 'on' : 'off'} | Auto-save: ${session.autoSave ? 'on' : 'off'}`));
165
+ console.log(chalk.gray(fitLine(`Workspace: ${session.workspacePath}`)));
166
+ console.log(chalk.gray(fitLine(`Debug: ${session.debugMode ? 'on' : 'off'} | Auto-approve: ${session.autoApprove ? 'on' : 'off'} | Auto-save: ${session.autoSave ? 'on' : 'off'}`)));
158
167
  console.log();
159
168
  return session;
160
169
  }
@@ -48,6 +48,8 @@ export declare class ChatCommand {
48
48
  private retryPromptStreak;
49
49
  private v3SuppressThinkingStream;
50
50
  private v3StreamedAnswerBuffer;
51
+ private v3SeenToolCalls;
52
+ private v3SeenToolResults;
51
53
  private lastAgentRunOutcome;
52
54
  private isJwtExpirationError;
53
55
  private isNetworkError;
@@ -114,6 +116,10 @@ export declare class ChatCommand {
114
116
  private sanitizeServerPath;
115
117
  private stripHiddenThoughtBlocks;
116
118
  private describeV3AgentTool;
119
+ private terminalColumns;
120
+ private fitTerminalText;
121
+ private normalizeToolOutputForDisplay;
122
+ private toolEventKey;
117
123
  private isRawV3StreamPayload;
118
124
  private consumeV3StreamPayload;
119
125
  private writeV3StreamText;
@@ -67,6 +67,8 @@ export class ChatCommand {
67
67
  retryPromptStreak = 0;
68
68
  v3SuppressThinkingStream = false;
69
69
  v3StreamedAnswerBuffer = '';
70
+ v3SeenToolCalls = new Set();
71
+ v3SeenToolResults = new Set();
70
72
  // Last completed Agent run — used by /retry, /continue, and the final summary block.
71
73
  lastAgentRunOutcome = null;
72
74
  isJwtExpirationError(error) {
@@ -706,6 +708,33 @@ export class ChatCommand {
706
708
  }
707
709
  return 'Working';
708
710
  }
711
+ terminalColumns() {
712
+ const raw = Number(process.stdout.columns || process.stderr.columns || 80);
713
+ return Number.isFinite(raw) && raw > 0 ? raw : 80;
714
+ }
715
+ fitTerminalText(text, reserve = 0) {
716
+ const width = Math.max(24, this.terminalColumns() - reserve);
717
+ const clean = String(text || '').replace(/\s+/g, ' ').trim();
718
+ const visible = clean.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '');
719
+ if (visible.length <= width)
720
+ return clean;
721
+ return `${visible.slice(0, Math.max(1, width - 1))}…`;
722
+ }
723
+ normalizeToolOutputForDisplay(output) {
724
+ return String(output || '')
725
+ .replace(/\/r\/n|\/n|\\n/g, '\n')
726
+ .replace(/\/r|\\r/g, '')
727
+ .replace(/\t/g, ' ');
728
+ }
729
+ toolEventKey(event) {
730
+ const name = String(event?.name || event?.tool || event?.tool_name || '').trim();
731
+ const args = event?.arguments && typeof event.arguments === 'object' ? event.arguments : {};
732
+ const target = String(args.path || args.file_path || args.pattern || args.command || event?.target || '').trim();
733
+ if (target)
734
+ return `${name}:${target}`;
735
+ const output = typeof event?.output === 'string' ? event.output.replace(/\s+/g, ' ').slice(0, 160) : '';
736
+ return `${name}:${output}`;
737
+ }
709
738
  isRawV3StreamPayload(event) {
710
739
  if (!event) {
711
740
  return false;
@@ -814,15 +843,20 @@ export class ChatCommand {
814
843
  }
815
844
  this.v3LastActivity = Date.now();
816
845
  if (event.type === 'tool_call') {
846
+ const eventKey = this.toolEventKey(event);
847
+ if (this.v3SeenToolCalls.has(eventKey)) {
848
+ return;
849
+ }
850
+ this.v3SeenToolCalls.add(eventKey);
817
851
  this.v3ToolCallCount += 1;
818
852
  const toolDesc = this.describeV3AgentTool(event.tool || event.name || event.tool_name);
819
853
  const toolTarget = event.arguments?.path || event.arguments?.file_path || event.arguments?.pattern || '';
820
854
  const sanitizedTarget = this.sanitizeServerPath(String(toolTarget));
821
- const shortTarget = sanitizedTarget ? ` ${sanitizedTarget.replace(/\\/g, '/').split('/').slice(-2).join('/')}` : '';
855
+ const shortTarget = sanitizedTarget ? ` -> ${sanitizedTarget.replace(/\\/g, '/').split('/').slice(-2).join('/')}` : '';
822
856
  const stepLabel = chalk.cyan(` [${this.v3ToolCallCount}]`) + ` ${toolDesc}${shortTarget}`;
823
857
  if (spinner.isSpinning)
824
858
  spinner.stop();
825
- this.writeAgentActivityLine(stepLabel + '\n');
859
+ this.writeAgentActivityLine(this.fitTerminalText(stepLabel) + '\n');
826
860
  // Show extra detail for key tools
827
861
  const args = event.arguments || {};
828
862
  const toolName = event.name || event.tool || '';
@@ -832,16 +866,21 @@ export class ChatCommand {
832
866
  }
833
867
  else if (toolName === 'bash' && typeof args.command === 'string') {
834
868
  const command = this.sanitizeServerPath(args.command);
835
- this.writeAgentActivityLine(chalk.gray(` $ ${command.slice(0, 120)}${command.length > 120 ? '…' : ''}\n`));
869
+ this.writeAgentActivityLine(chalk.gray(` ${this.fitTerminalText(`$ ${command}`, 6)}\n`));
836
870
  }
837
871
  else if (toolName === 'read_file') {
838
- this.writeAgentActivityLine(chalk.gray(` reading ${sanitizedTarget || 'file'}\n`));
872
+ this.writeAgentActivityLine(chalk.gray(` ${this.fitTerminalText(`reading ${sanitizedTarget || 'file'}`, 6)}\n`));
839
873
  }
840
- spinner.start();
841
874
  spinner.text = `Running ${toolDesc}...`;
842
875
  return;
843
876
  }
844
877
  if (event.type === 'tool_result') {
878
+ const eventKey = this.toolEventKey(event);
879
+ if (eventKey && this.v3SeenToolResults.has(eventKey)) {
880
+ return;
881
+ }
882
+ if (eventKey)
883
+ this.v3SeenToolResults.add(eventKey);
845
884
  const success = event.success !== false;
846
885
  const toolName = event.name || event.tool || '';
847
886
  const indicator = success ? chalk.green(' ✓') : chalk.red(' ✗');
@@ -850,27 +889,30 @@ export class ChatCommand {
850
889
  this.writeAgentActivityLine(`${indicator} ${toolName}\n`);
851
890
  // Show output for failures, or brief summary for successes — sanitize server paths
852
891
  const rawOutput = typeof event.output === 'string' ? event.output.trim() : '';
853
- const output = this.sanitizeServerPath(rawOutput);
892
+ const output = this.normalizeToolOutputForDisplay(this.sanitizeServerPath(rawOutput));
854
893
  if (!success && output) {
855
894
  const sanitizedError = this.sanitizeServerPath(typeof event.error === 'string' ? event.error : output);
856
895
  const lines = sanitizedError.split('\n').slice(0, 4);
857
- this.writeAgentActivityLine(chalk.red(` ${lines.join('\n ')}\n`));
896
+ this.writeAgentActivityLine(chalk.red(`${lines.map((line) => ` ${this.fitTerminalText(line, 6)}`).join('\n')}\n`));
858
897
  }
859
898
  else if (success && output && output.length > 0) {
860
899
  if (toolName === 'read_file' || toolName === 'grep' || toolName === 'search_files' || toolName === 'list_directory') {
861
900
  const lineCount = output.split('\n').length;
862
- const preview = output.split('\n').slice(0, 3).join('\n');
901
+ const preview = output.split('\n')
902
+ .map((line) => line.trim())
903
+ .filter((line) => line && !line.startsWith('.vigthoria/memory/brain.json:'))
904
+ .slice(0, 3)
905
+ .join('\n');
863
906
  this.writeAgentActivityLine(chalk.gray(` ${lineCount} line${lineCount === 1 ? '' : 's'} returned\n`));
864
907
  if (preview && preview.length > 20 && !/^\(no matches\)$/i.test(preview.trim())) {
865
- this.writeAgentActivityLine(chalk.gray(`${preview.split('\n').map((l) => ` ${l}`).join('\n')}\n`));
908
+ this.writeAgentActivityLine(chalk.gray(`${preview.split('\n').map((line) => ` ${this.fitTerminalText(line, 6)}`).join('\n')}\n`));
866
909
  }
867
910
  }
868
911
  else {
869
- const brief = output.split('\n')[0].slice(0, 120);
870
- this.writeAgentActivityLine(chalk.gray(` ${brief}${output.length > 120 ? '…' : ''}\n`));
912
+ const brief = output.split('\n')[0];
913
+ this.writeAgentActivityLine(chalk.gray(` ${this.fitTerminalText(brief, 6)}\n`));
871
914
  }
872
915
  }
873
- spinner.start();
874
916
  spinner.text = 'Next step...';
875
917
  return;
876
918
  }
@@ -886,8 +928,7 @@ export class ChatCommand {
886
928
  const display = iterText && !/read-only analysis turn|run at least one discovery tool/i.test(iterText)
887
929
  ? iterText
888
930
  : `Step ${this.v3IterationCount}: inspecting workspace…`;
889
- this.writeAgentActivityLine(chalk.cyan(`\n── ${display} ──\n`));
890
- spinner.start();
931
+ this.writeAgentActivityLine(chalk.cyan(`\n── ${this.fitTerminalText(display, 6)} ──\n`));
891
932
  spinner.text = 'Analyzing...';
892
933
  return;
893
934
  }
@@ -920,13 +961,13 @@ export class ChatCommand {
920
961
  if (fail > 0)
921
962
  statLine += chalk.yellow(`, ${fail} failed`);
922
963
  }
923
- process.stderr.write(chalk.green(`\n✓ Complete`) + ` ${statLine}\n`);
964
+ process.stdout.write(chalk.green(`\n✓ Complete`) + ` - ${this.fitTerminalText(statLine, 14)}\n`);
924
965
  // Show seal quality score if available
925
966
  if (event.seal_score && typeof event.seal_score.overall === 'number') {
926
967
  const score = event.seal_score.overall;
927
968
  const tier = event.seal_score.tier || '';
928
969
  const scoreColor = score >= 7 ? chalk.green : score >= 5 ? chalk.yellow : chalk.red;
929
- process.stderr.write(chalk.cyan(' [Quality] ') + scoreColor(`${score}/10`) + (tier ? chalk.gray(` (${tier})`) : '') + '\n');
970
+ process.stdout.write(chalk.cyan(' [Quality] ') + scoreColor(`${score}/10`) + (tier ? chalk.gray(` (${tier})`) : '') + '\n');
930
971
  }
931
972
  return;
932
973
  }
@@ -943,35 +984,31 @@ export class ChatCommand {
943
984
  }
944
985
  if (spinner.isSpinning)
945
986
  spinner.stop();
946
- process.stderr.write(chalk.cyan(` [Plan] `) + `Task: ${planKind || 'analyzing'}`);
947
- if (quality)
948
- process.stderr.write(chalk.gray(` (${quality})`));
949
- process.stderr.write('\n');
987
+ process.stdout.write(chalk.cyan(` [Plan] `) + this.fitTerminalText(`Task: ${planKind || 'analyzing'}${quality ? ` (${quality})` : ''}`, 9) + '\n');
950
988
  if (summary) {
951
- process.stderr.write(chalk.gray(` ${summary}\n`));
989
+ process.stdout.write(chalk.gray(` ${this.fitTerminalText(summary, 6)}\n`));
952
990
  }
953
991
  if (status === 'planning' && Number.isFinite(Number(plan.elapsed_seconds))) {
954
- process.stderr.write(chalk.gray(` elapsed: ${plan.elapsed_seconds}s\n`));
992
+ process.stdout.write(chalk.gray(` elapsed: ${plan.elapsed_seconds}s\n`));
955
993
  }
956
994
  if (Array.isArray(plan.tasks) && plan.tasks.length > 0) {
957
- process.stderr.write(chalk.gray(` ${plan.total_tasks || plan.tasks.length} tasks:\n`));
995
+ process.stdout.write(chalk.gray(` ${plan.total_tasks || plan.tasks.length} tasks:\n`));
958
996
  for (const t of plan.tasks.slice(0, 10)) {
959
997
  const targets = Array.isArray(t.targets) && t.targets.length ? ` → ${t.targets.map((target) => this.sanitizeServerPath(String(target))).join(', ')}` : '';
960
- process.stderr.write(chalk.gray(` ${this.sanitizeServerPath(String(t.title || t.id))}${targets}\n`));
998
+ process.stdout.write(chalk.gray(` - ${this.fitTerminalText(`${this.sanitizeServerPath(String(t.title || t.id))}${targets}`, 10)}\n`));
961
999
  }
962
1000
  if (plan.tasks.length > 10) {
963
- process.stderr.write(chalk.gray(` ... and ${plan.tasks.length - 10} more\n`));
1001
+ process.stdout.write(chalk.gray(` ... and ${plan.tasks.length - 10} more\n`));
964
1002
  }
965
1003
  }
966
1004
  if (Array.isArray(plan.notes) && plan.notes.length > 0) {
967
1005
  for (const note of plan.notes.slice(0, 3)) {
968
- process.stderr.write(chalk.gray(` note: ${this.sanitizeServerPath(String(note))}\n`));
1006
+ process.stdout.write(chalk.gray(` note: ${this.fitTerminalText(this.sanitizeServerPath(String(note)), 12)}\n`));
969
1007
  }
970
1008
  }
971
1009
  if (Array.isArray(plan.target_files) && plan.target_files.length > 0) {
972
- process.stderr.write(chalk.gray(` Files: ${plan.target_files.map((filePath) => this.sanitizeServerPath(String(filePath))).join(', ')}\n`));
1010
+ process.stdout.write(chalk.gray(` ${this.fitTerminalText(`Files: ${plan.target_files.map((filePath) => this.sanitizeServerPath(String(filePath))).join(', ')}`, 6)}\n`));
973
1011
  }
974
- spinner.start();
975
1012
  spinner.text = status === 'planning' ? 'Planning...' : 'Executing plan...';
976
1013
  return;
977
1014
  }
@@ -1194,7 +1231,7 @@ export class ChatCommand {
1194
1231
  if (sessionConfig.debugMode) {
1195
1232
  process.env.DEBUG = process.env.DEBUG || 'vigthoria:*';
1196
1233
  }
1197
- console.log(chalk.green(`Using workspace: ${this.currentProjectPath}`));
1234
+ console.log(chalk.green(this.fitTerminalText(`Using workspace: ${this.currentProjectPath}`)));
1198
1235
  }
1199
1236
  if (this.jsonOutput && !options.prompt && !options.retry && !options.continue) {
1200
1237
  throw new Error('--json is only supported together with --prompt, --retry, or --continue.');
@@ -2508,11 +2545,14 @@ export class ChatCommand {
2508
2545
  this.v3LastActivity = Date.now();
2509
2546
  this.v3StreamingStarted = false;
2510
2547
  this.v3StreamedAnswerBuffer = '';
2511
- const taskDisplay = new TaskDisplay(['Analyse workspace', 'Execute tasks', 'Validate output', 'Self-heal'], !this.jsonOutput);
2548
+ this.v3SeenToolCalls.clear();
2549
+ this.v3SeenToolResults.clear();
2550
+ const taskDisplay = new TaskDisplay(['Analyse workspace', 'Execute tasks', 'Validate output', 'Self-heal'], false);
2512
2551
  taskDisplay.start(0);
2513
2552
  const spinner = this.jsonOutput ? null : createSpinner({
2514
2553
  text: routingPolicy.cloudSelected ? 'Routing heavy task to Vigthoria Cloud...' : 'Routing to V3 Agent...',
2515
2554
  spinner: 'clock',
2555
+ isSilent: true,
2516
2556
  }).start();
2517
2557
  // Live run telemetry, used both for the final summary block and for /retry, /continue.
2518
2558
  const liveOutcome = {
@@ -3105,10 +3145,10 @@ export class ChatCommand {
3105
3145
  ? 'Interactive Agent Chat'
3106
3146
  : 'Interactive Chat';
3107
3147
  this.logger.section(this.workflowTarget ? `${chatTitle} Via Workflow Target` : chatTitle);
3108
- console.log(chalk.gray('Type /help for commands. Type /exit to quit.'));
3109
- console.log(chalk.gray('Multi-line: end a line with \\ or start a block with {{{ and end with }}}'));
3148
+ console.log(chalk.gray(this.fitTerminalText('Type /help for commands. Type /exit to quit.')));
3149
+ console.log(chalk.gray(this.fitTerminalText('Multi-line: end a line with \\ or start a block with {{{ and end with }}}')));
3110
3150
  if (this.workflowTarget) {
3111
- console.log(chalk.gray(`Workflow target: ${this.workflowTarget}`));
3151
+ console.log(chalk.gray(this.fitTerminalText(`Workflow target: ${this.workflowTarget}`)));
3112
3152
  }
3113
3153
  const rl = readline.createInterface({
3114
3154
  input: process.stdin,
package/dist/index.js CHANGED
@@ -567,22 +567,34 @@ export async function main(args) {
567
567
  return;
568
568
  }
569
569
  }
570
- // Banner - Fixed alignment with proper padding
571
- const boxWidth = 61; // Inner content width
572
- const titleText = 'VIGTHORIA CLI - AI-Powered Coding Assistant';
573
- const versionText = `Version ${VERSION}`;
574
- // Calculate padding for centering
575
- const titlePad = Math.floor((boxWidth - 4 - titleText.length) / 2);
576
- const versionPad = Math.floor((boxWidth - 4 - versionText.length) / 2);
577
570
  if (process.env.VIGTHORIA_NO_BANNER !== '1' &&
578
571
  !jsonOutputRequested &&
579
572
  !directPromptRequested &&
580
573
  !helpOrVersionRequested) {
574
+ const terminalWidth = Math.max(36, Number(process.stdout.columns || 80));
575
+ const boxWidth = Math.max(34, Math.min(61, terminalWidth - 2));
576
+ const innerWidth = boxWidth;
577
+ const fit = (text) => {
578
+ const clean = String(text || '').replace(/\s+/g, ' ').trim();
579
+ return clean.length > innerWidth ? `${clean.slice(0, Math.max(1, innerWidth - 1))}…` : clean;
580
+ };
581
+ const center = (text, colour = (value) => value) => {
582
+ const fitted = fit(text);
583
+ const left = Math.max(0, Math.floor((innerWidth - fitted.length) / 2));
584
+ const right = Math.max(0, innerWidth - fitted.length - left);
585
+ return chalk.cyan(CH.dV) + ' '.repeat(left) + colour(fitted) + ' '.repeat(right) + chalk.cyan(CH.dV);
586
+ };
587
+ const titleLine = boxWidth < 48 ? 'VIGTHORIA CLI' : 'VIGTHORIA CLI - AI Coding Assistant';
588
+ const subtitleLine = boxWidth < 48 ? 'AI Coding Assistant' : `Version ${VERSION}`;
589
+ const versionLine = boxWidth < 48 ? `Version ${VERSION}` : '';
581
590
  console.log(chalk.cyan(CH.dTl + CH.dH.repeat(boxWidth) + CH.dTr));
582
- console.log(chalk.cyan(CH.dV + ' '.repeat(boxWidth) + CH.dV));
583
- console.log(chalk.cyan(CH.dV) + ' '.repeat(titlePad) + chalk.bold.white('VIGTHORIA CLI') + chalk.cyan(' - AI-Powered Coding Assistant') + ' '.repeat(boxWidth - titlePad - titleText.length) + chalk.cyan(CH.dV));
584
- console.log(chalk.cyan(CH.dV) + ' '.repeat(versionPad) + chalk.gray(versionText) + ' '.repeat(boxWidth - versionPad - versionText.length) + chalk.cyan(CH.dV));
585
- console.log(chalk.cyan(CH.dV + ' '.repeat(boxWidth) + CH.dV));
591
+ console.log(center(''));
592
+ console.log(center(titleLine, (value) => chalk.bold.white(value)));
593
+ console.log(center(subtitleLine, (value) => chalk.gray(value)));
594
+ if (versionLine) {
595
+ console.log(center(versionLine, (value) => chalk.gray(value)));
596
+ }
597
+ console.log(center(''));
586
598
  console.log(chalk.cyan(CH.dBl + CH.dH.repeat(boxWidth) + CH.dBr));
587
599
  console.log();
588
600
  }
@@ -30,11 +30,35 @@ const LIST_ONLY_DISCOVERY_TOOLS = new Set([
30
30
  'dir',
31
31
  ]);
32
32
  const GENERIC_SUMMARY_RE = /^(completed the requested analysis|reviewed the workspace without writing changes|task completed|done|finished|analysis completed)([.\s]|$)/i;
33
+ function looksLikeRawV3EventPayload(text) {
34
+ const trimmed = String(text || '').trim();
35
+ if (!/^[{[]/.test(trimmed))
36
+ return false;
37
+ try {
38
+ const parsed = JSON.parse(trimmed);
39
+ const root = Array.isArray(parsed) ? { events: parsed } : parsed;
40
+ if (!root || typeof root !== 'object')
41
+ return false;
42
+ if (Array.isArray(root.events) && root.events.some((event) => event && typeof event === 'object' && typeof event.type === 'string')) {
43
+ return true;
44
+ }
45
+ return Boolean(root.context_id
46
+ || root.mcp_context_id
47
+ || root.workspace_uri
48
+ || root.execution_surface
49
+ || root.task_id);
50
+ }
51
+ catch {
52
+ return false;
53
+ }
54
+ }
33
55
  /** Tool-trace stubs like `[read_file] "index.html"` or `<read_file>…</read_file>` — not a user-facing answer. */
34
56
  export function isToolEvidenceStubAnswer(text) {
35
57
  const trimmed = String(text || '').trim();
36
58
  if (!trimmed)
37
59
  return true;
60
+ if (looksLikeRawV3EventPayload(trimmed))
61
+ return true;
38
62
  if (BOOTSTRAP_PLACEHOLDER_RE.test(trimmed))
39
63
  return true;
40
64
  if (GENERIC_SUMMARY_RE.test(trimmed.replace(/\.\s*\d+\s+tool steps.*$/i, '').trim()))
package/dist/utils/api.js CHANGED
@@ -3380,8 +3380,7 @@ document.addEventListener('DOMContentLoaded', () => {
3380
3380
  const fileList = Object.keys(data.files).join(', ');
3381
3381
  return `Agent wrote workspace files: ${sanitizeUserFacingPathText(fileList)}`;
3382
3382
  }
3383
- const text = sanitizeUserFacingPathText(JSON.stringify(data, null, 2));
3384
- return text.length > 12000 ? `${text.slice(0, 12000)}\n\n[V3 agent output truncated]` : text;
3383
+ return '';
3385
3384
  }
3386
3385
  sanitizeV3AgentResponseText(input) {
3387
3386
  let text = sanitizeUserFacingPathText(String(input || ''));
@@ -162,8 +162,13 @@ export class Logger {
162
162
  }
163
163
  // Section header
164
164
  section(title) {
165
+ const width = Math.max(24, Number(process.stdout.columns || 80));
166
+ const cleanTitle = String(title || '').replace(/\s+/g, ' ').trim();
167
+ const maxTitle = Math.max(10, width - 10);
168
+ const fittedTitle = cleanTitle.length > maxTitle ? `${cleanTitle.slice(0, maxTitle - 1)}…` : cleanTitle;
169
+ const side = Math.max(3, Math.floor((Math.min(width, 72) - fittedTitle.length - 2) / 2));
165
170
  console.log();
166
- console.log(chalk.bold.cyan(`${CH.hDouble.repeat(3)} ${title} ${CH.hDouble.repeat(3)}`));
171
+ console.log(chalk.bold.cyan(`${CH.hDouble.repeat(side)} ${fittedTitle} ${CH.hDouble.repeat(side)}`));
167
172
  console.log();
168
173
  }
169
174
  // Progress
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.11.8",
3
+ "version": "1.11.10",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",