vigthoria-cli 1.11.10 → 1.11.11

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.
@@ -48,6 +48,7 @@ export declare class ChatCommand {
48
48
  private retryPromptStreak;
49
49
  private v3SuppressThinkingStream;
50
50
  private v3StreamedAnswerBuffer;
51
+ private v3StreamedAnswerDisplayed;
51
52
  private v3SeenToolCalls;
52
53
  private v3SeenToolResults;
53
54
  private lastAgentRunOutcome;
@@ -123,6 +124,9 @@ export declare class ChatCommand {
123
124
  private isRawV3StreamPayload;
124
125
  private consumeV3StreamPayload;
125
126
  private writeV3StreamText;
127
+ private displayV3StreamedAnswer;
128
+ private renderAgentAnswerForTerminal;
129
+ private getTerminalContentWidth;
126
130
  private sanitizeV3VisibleStreamText;
127
131
  private writeAgentActivityLine;
128
132
  private updateV3AgentSpinner;
@@ -14,6 +14,7 @@ import { ProjectMemoryService } from '../utils/project-memory.js';
14
14
  import { buildPersonaOverlay, normalizePersonaMode } from '../utils/persona.js';
15
15
  import { runAgentSessionMenu, shouldShowAgentSessionMenu } from './agent-session-menu.js';
16
16
  import { createLiveOutcome, evaluateExecutorSuccess, handleTaskEvent, isSubstantiveAgentAnswer, isToolEvidenceStubAnswer, normalizeAgentAnswerContent, noteAnalysisToolUse, } from '../utils/agentRunOutcome.js';
17
+ import { looksLikeMarkdownReport, renderMarkdownToTerminal, summarizeMarkdownReport, } from '../utils/terminalMarkdown.js';
17
18
  const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
18
19
  const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS;
19
20
  if (!rawValue) {
@@ -67,6 +68,7 @@ export class ChatCommand {
67
68
  retryPromptStreak = 0;
68
69
  v3SuppressThinkingStream = false;
69
70
  v3StreamedAnswerBuffer = '';
71
+ v3StreamedAnswerDisplayed = false;
70
72
  v3SeenToolCalls = new Set();
71
73
  v3SeenToolResults = new Set();
72
74
  // Last completed Agent run — used by /retry, /continue, and the final summary block.
@@ -788,8 +790,32 @@ export class ChatCommand {
788
790
  else {
789
791
  spinner.stop();
790
792
  }
791
- process.stdout.write(safeText);
792
793
  this.v3StreamedAnswerBuffer += safeText;
794
+ spinner.text = looksLikeMarkdownReport(this.v3StreamedAnswerBuffer)
795
+ ? 'Writing analysis report...'
796
+ : 'Writing response...';
797
+ }
798
+ displayV3StreamedAnswer() {
799
+ if (this.jsonOutput || this.v3StreamedAnswerDisplayed) {
800
+ return;
801
+ }
802
+ const content = this.v3StreamedAnswerBuffer.trim();
803
+ if (!isSubstantiveAgentAnswer(content)) {
804
+ return;
805
+ }
806
+ console.log('');
807
+ console.log(renderMarkdownToTerminal(content, this.getTerminalContentWidth()));
808
+ this.v3StreamedAnswerDisplayed = true;
809
+ }
810
+ renderAgentAnswerForTerminal(content) {
811
+ const normalized = String(content || '').trim();
812
+ if (!normalized)
813
+ return '';
814
+ return renderMarkdownToTerminal(normalized, this.getTerminalContentWidth());
815
+ }
816
+ getTerminalContentWidth() {
817
+ const cols = process.stdout.columns || 80;
818
+ return Math.max(60, Math.min(cols - 2, 100));
793
819
  }
794
820
  sanitizeV3VisibleStreamText(text) {
795
821
  let output = String(text || '');
@@ -923,12 +949,15 @@ export class ChatCommand {
923
949
  if (event.type === 'thinking') {
924
950
  this.v3IterationCount += 1;
925
951
  const iterText = this.sanitizeServerPath(event.content || '');
926
- if (spinner.isSpinning)
927
- spinner.stop();
928
- const display = iterText && !/read-only analysis turn|run at least one discovery tool/i.test(iterText)
929
- ? iterText
930
- : `Step ${this.v3IterationCount}: inspecting workspace…`;
931
- this.writeAgentActivityLine(chalk.cyan(`\n── ${this.fitTerminalText(display, 6)} ──\n`));
952
+ const isGenericStep = !iterText
953
+ || /read-only analysis turn|run at least one discovery tool/i.test(iterText)
954
+ || /^Step \d+:/i.test(iterText)
955
+ || /reviewing attached files|inspecting workspace/i.test(iterText);
956
+ if (!isGenericStep) {
957
+ if (spinner.isSpinning)
958
+ spinner.stop();
959
+ this.writeAgentActivityLine(chalk.cyan(`\n── ${this.fitTerminalText(iterText, 6)} ──\n`));
960
+ }
932
961
  spinner.text = 'Analyzing...';
933
962
  return;
934
963
  }
@@ -951,6 +980,7 @@ export class ChatCommand {
951
980
  const tools = event.tool_calls || this.v3ToolCallCount;
952
981
  if (spinner.isSpinning)
953
982
  spinner.stop();
983
+ this.displayV3StreamedAnswer();
954
984
  let statLine = `${iters} iterations, ${tools} tool calls`;
955
985
  if (elapsed)
956
986
  statLine += `, ${elapsed}`;
@@ -2545,6 +2575,7 @@ export class ChatCommand {
2545
2575
  this.v3LastActivity = Date.now();
2546
2576
  this.v3StreamingStarted = false;
2547
2577
  this.v3StreamedAnswerBuffer = '';
2578
+ this.v3StreamedAnswerDisplayed = false;
2548
2579
  this.v3SeenToolCalls.clear();
2549
2580
  this.v3SeenToolResults.clear();
2550
2581
  const taskDisplay = new TaskDisplay(['Analyse workspace', 'Execute tasks', 'Validate output', 'Self-heal'], false);
@@ -2731,9 +2762,8 @@ export class ChatCommand {
2731
2762
  if (spinner) {
2732
2763
  spinner.stop();
2733
2764
  }
2734
- // End streaming line if we streamed text inline
2735
- if (this.v3StreamingStarted) {
2736
- process.stdout.write('\n');
2765
+ if (this.v3StreamingStarted && !this.v3StreamedAnswerDisplayed) {
2766
+ this.displayV3StreamedAnswer();
2737
2767
  }
2738
2768
  const previewGate = (response.metadata?.previewGate || null);
2739
2769
  const workspaceHasOutput = this.api.hasAgentWorkspaceOutput(workspaceContext);
@@ -2828,15 +2858,13 @@ export class ChatCommand {
2828
2858
  executorSucceeded = false;
2829
2859
  }
2830
2860
  if (!this.jsonOutput && !this.directPromptMode) {
2831
- if (this.v3StreamingStarted && isSubstantiveAgentAnswer(this.v3StreamedAnswerBuffer)) {
2832
- console.log('');
2833
- console.log(chalk.bold('Answer:'));
2834
- console.log(this.v3StreamedAnswerBuffer.trim());
2861
+ if (this.v3StreamedAnswerDisplayed) {
2862
+ // Highlighted report already printed once above.
2835
2863
  }
2836
2864
  else if (isSubstantiveAgentAnswer(finalAnswer)) {
2837
2865
  console.log('');
2838
2866
  console.log(chalk.bold('Answer:'));
2839
- console.log(finalAnswer);
2867
+ console.log(this.renderAgentAnswerForTerminal(finalAnswer));
2840
2868
  }
2841
2869
  else if (!requiresWorkspaceChanges) {
2842
2870
  console.log('');
@@ -2845,7 +2873,7 @@ export class ChatCommand {
2845
2873
  }
2846
2874
  else if (finalAnswer) {
2847
2875
  console.log('');
2848
- console.log(finalAnswer);
2876
+ console.log(this.renderAgentAnswerForTerminal(finalAnswer));
2849
2877
  }
2850
2878
  }
2851
2879
  if (!executorSucceeded && requiresWorkspaceChanges && changedFileCount === 0 && !liveOutcome.executorError) {
@@ -3456,10 +3484,20 @@ export class ChatCommand {
3456
3484
  console.log(' ' + chalk.white(taskLine + '.'));
3457
3485
  }
3458
3486
  else if (outcome.answerContent && isSubstantiveAgentAnswer(outcome.answerContent)) {
3459
- const preview = outcome.answerContent.length > 160
3460
- ? `${outcome.answerContent.slice(0, 160)}…`
3461
- : outcome.answerContent;
3462
- console.log(' ' + chalk.white(`Answer: ${preview}`));
3487
+ const reportSummary = summarizeMarkdownReport(outcome.answerContent);
3488
+ if (reportSummary) {
3489
+ const sectionPreview = reportSummary.sections.length > 0
3490
+ ? reportSummary.sections.slice(0, 4).join(', ')
3491
+ : `${Math.round(reportSummary.charCount / 1000)}k chars`;
3492
+ console.log(' ' + chalk.white(reportSummary.title));
3493
+ console.log(' ' + chalk.gray(`Sections: ${sectionPreview}`));
3494
+ }
3495
+ else {
3496
+ const preview = outcome.answerContent.length > 160
3497
+ ? `${outcome.answerContent.slice(0, 160)}…`
3498
+ : outcome.answerContent;
3499
+ console.log(' ' + chalk.white(preview));
3500
+ }
3463
3501
  }
3464
3502
  else if (changedFileCount > 0) {
3465
3503
  console.log(' ' + chalk.white('Workspace changes were produced and are ready for review.'));
package/dist/utils/api.js CHANGED
@@ -1524,7 +1524,7 @@ export class APIClient {
1524
1524
  if (json.length <= LIMIT)
1525
1525
  return json;
1526
1526
  if (process.env.DEBUG || process.env.VIGTHORIA_DEBUG) {
1527
- console.log(`[context] Payload ${json.length} chars exceeds ${LIMIT} limit, compacting...`);
1527
+ process.stderr.write(`[context] Payload ${json.length} chars exceeds ${LIMIT} limit, compacting...\n`);
1528
1528
  }
1529
1529
  // Phase 1 — shrink workspaceFiles to fit
1530
1530
  const summary = payload.localWorkspaceSummary;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Shared terminal markdown rendering for CLI commands.
3
+ */
4
+ export interface MarkdownReportSummary {
5
+ title: string;
6
+ sections: string[];
7
+ charCount: number;
8
+ }
9
+ /**
10
+ * Clean marked-terminal output for release-grade CLI presentation.
11
+ */
12
+ export declare function cleanTerminalMarkdownOutput(raw: string): string;
13
+ export declare function looksLikeMarkdownReport(text: string): boolean;
14
+ export declare function renderMarkdownToTerminal(markdown: string, width?: number): string;
15
+ export declare function summarizeMarkdownReport(markdown: string): MarkdownReportSummary | null;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Shared terminal markdown rendering for CLI commands.
3
+ */
4
+ import { Marked } from 'marked';
5
+ import { markedTerminal } from 'marked-terminal';
6
+ let cachedRenderer = null;
7
+ function getTerminalWidth() {
8
+ const cols = process.stdout.columns || 80;
9
+ return Math.max(60, Math.min(cols - 2, 100));
10
+ }
11
+ function getRenderer(width) {
12
+ const resolvedWidth = width ?? getTerminalWidth();
13
+ if (cachedRenderer && cachedRenderer.width === resolvedWidth) {
14
+ return cachedRenderer.marked;
15
+ }
16
+ const marked = new Marked();
17
+ marked.use(markedTerminal({
18
+ showSectionPrefix: false,
19
+ tab: 2,
20
+ width: resolvedWidth,
21
+ reflowText: true,
22
+ }));
23
+ cachedRenderer = { width: resolvedWidth, marked };
24
+ return marked;
25
+ }
26
+ /**
27
+ * Clean marked-terminal output for release-grade CLI presentation.
28
+ */
29
+ export function cleanTerminalMarkdownOutput(raw) {
30
+ let out = raw;
31
+ out = out.replace(/^ \* /gm, '- ');
32
+ out = out.replace(/^ \* /gm, ' - ');
33
+ const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, '');
34
+ out = out.split('\n').filter((line) => {
35
+ if (line === '')
36
+ return true;
37
+ return stripAnsi(line).trim().length > 0;
38
+ }).join('\n');
39
+ out = out.replace(/\n{3,}/g, '\n\n');
40
+ return out;
41
+ }
42
+ export function looksLikeMarkdownReport(text) {
43
+ const trimmed = String(text || '').trim();
44
+ if (!trimmed)
45
+ return false;
46
+ return /^#{1,3}\s+\S/m.test(trimmed)
47
+ || /^\|.+\|/m.test(trimmed)
48
+ || /^[-*]\s+\*\*/m.test(trimmed);
49
+ }
50
+ export function renderMarkdownToTerminal(markdown, width) {
51
+ const input = String(markdown || '').trim();
52
+ if (!input)
53
+ return '';
54
+ if (!looksLikeMarkdownReport(input)) {
55
+ return input;
56
+ }
57
+ const rendered = getRenderer(width).parse(input);
58
+ return cleanTerminalMarkdownOutput(rendered);
59
+ }
60
+ export function summarizeMarkdownReport(markdown) {
61
+ const text = String(markdown || '').trim();
62
+ if (!text || !looksLikeMarkdownReport(text)) {
63
+ return null;
64
+ }
65
+ const lines = text.split('\n');
66
+ let title = '';
67
+ const sections = [];
68
+ for (const line of lines) {
69
+ const h1 = line.match(/^#\s+(.+)$/);
70
+ if (h1 && !title) {
71
+ title = h1[1].replace(/[*_`]/g, '').trim();
72
+ continue;
73
+ }
74
+ const h2 = line.match(/^##\s+(.+)$/);
75
+ if (h2) {
76
+ const section = h2[1].replace(/[*_`]/g, '').trim();
77
+ if (section && sections.length < 8) {
78
+ sections.push(section);
79
+ }
80
+ }
81
+ }
82
+ if (!title) {
83
+ const firstLine = lines.find((line) => line.trim().length > 0)?.trim() || 'Analysis report';
84
+ title = firstLine.replace(/^#+\s*/, '').replace(/[*_`]/g, '').slice(0, 120);
85
+ }
86
+ return {
87
+ title,
88
+ sections,
89
+ charCount: text.length,
90
+ };
91
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.11.10",
3
+ "version": "1.11.11",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",