vigthoria-cli 1.11.7 → 1.11.8

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.
@@ -64,6 +64,7 @@ export declare class ChatCommand {
64
64
  private resolveInitialModel;
65
65
  private isLegacyAgentFallbackAllowed;
66
66
  private shouldRescueFailedAnalysis;
67
+ private isDeepAnalysisPrompt;
67
68
  private tryAnalysisLocalRescue;
68
69
  private resolveAgentExecutionPolicy;
69
70
  private getMessagesForModel;
@@ -262,6 +262,10 @@ export class ChatCommand {
262
262
  shouldRescueFailedAnalysis() {
263
263
  return process.env.VIGTHORIA_DISABLE_ANALYSIS_RESCUE !== '1';
264
264
  }
265
+ isDeepAnalysisPrompt(prompt) {
266
+ return /\b(deep|deeper|full|complete|thorough|systematic|architecture|architectural)\s+(analyse|analyze|analysis|review|dive|scan)\b/i.test(prompt)
267
+ || /\b(analyse|analyze|analysis|review)\b[\s\S]{0,40}\b(deep|deeper|full|complete|thorough|systematic|architecture|architectural)\b/i.test(prompt);
268
+ }
265
269
  async tryAnalysisLocalRescue(prompt, workspacePath) {
266
270
  if (!this.shouldRescueFailedAnalysis() || !this.tools) {
267
271
  return false;
@@ -784,6 +788,9 @@ export class ChatCommand {
784
788
  .replace(/^\s*(?:list_dir|read_file|write_file|edit_file|glob|grep|bash)\s*$/gim, '')
785
789
  .replace(/<tool_call>[\s\S]*?<\/tool_call>/gi, '')
786
790
  .replace(/<tool_code>[\s\S]*?<\/tool_code>/gi, '')
791
+ .replace(/<read_file>\s*<path>[\s\S]*?<\/path>\s*<\/read_file>/gi, '')
792
+ .replace(/<(?:grep|list_directory|search_files|glob|tool_result)\b[\s\S]*?<\/(?:grep|list_directory|search_files|glob|tool_result)>/gi, '')
793
+ .replace(/bootstrap discovery completed\s*[—-]\s*inspect the most relevant files next\.?/gi, '')
787
794
  .replace(/\n{3,}/g, '\n\n');
788
795
  return output;
789
796
  }
@@ -1988,6 +1995,7 @@ export class ChatCommand {
1988
1995
  buildLocalLoopLiveOutcome(prompt) {
1989
1996
  const liveOutcome = createLiveOutcome();
1990
1997
  liveOutcome.requiresWorkspaceChanges = this.taskRequiresWorkspaceChanges(prompt);
1998
+ liveOutcome.deepAnalysisRequired = this.isDeepAnalysisPrompt(prompt);
1991
1999
  liveOutcome.analysisToolsUsed = this.agentToolEvidence.discovery;
1992
2000
  liveOutcome.changedFileCount = this.agentToolEvidence.mutation;
1993
2001
  liveOutcome.workspaceHasOutput = this.agentToolEvidence.mutation > 0;
@@ -2514,6 +2522,7 @@ export class ChatCommand {
2514
2522
  qualityMissing: [],
2515
2523
  qualityBlockers: [],
2516
2524
  };
2525
+ liveOutcome.deepAnalysisRequired = this.isDeepAnalysisPrompt(prompt);
2517
2526
  const parsePlannerSummary = (raw) => {
2518
2527
  if (!raw || typeof raw !== 'string')
2519
2528
  return;
@@ -2648,6 +2657,12 @@ export class ChatCommand {
2648
2657
  if (Number(event.discovery_tools_used) > 0) {
2649
2658
  liveOutcome.analysisToolsUsed = Math.max(liveOutcome.analysisToolsUsed, Number(event.discovery_tools_used));
2650
2659
  }
2660
+ if (Number(event.discovery_read_files) > 0) {
2661
+ liveOutcome.analysisReadToolsUsed = Math.max(liveOutcome.analysisReadToolsUsed, Number(event.discovery_read_files));
2662
+ }
2663
+ if (event.analysis_deep_required === true) {
2664
+ liveOutcome.deepAnalysisRequired = true;
2665
+ }
2651
2666
  taskDisplay.complete(1);
2652
2667
  }
2653
2668
  else if (event.type === 'executor_error') {
@@ -2759,6 +2774,9 @@ export class ChatCommand {
2759
2774
  const runEvaluation = evaluateExecutorSuccess(liveOutcome);
2760
2775
  let executorSucceeded = runEvaluation.executorSucceeded;
2761
2776
  let finalAnswer = answerContent;
2777
+ if (isToolEvidenceStubAnswer(finalAnswer)) {
2778
+ finalAnswer = '';
2779
+ }
2762
2780
  const needsAnalysisRescue = !requiresWorkspaceChanges && !this.jsonOutput
2763
2781
  && (!executorSucceeded || !isSubstantiveAgentAnswer(finalAnswer));
2764
2782
  if (needsAnalysisRescue) {
@@ -2876,7 +2894,7 @@ export class ChatCommand {
2876
2894
  qualityMissing: liveOutcome.qualityMissing,
2877
2895
  qualityBlockers: liveOutcome.qualityBlockers,
2878
2896
  hasOutput: workspaceHasOutput,
2879
- answerContent: answerContent || null,
2897
+ answerContent: finalAnswer || null,
2880
2898
  selfHealStatus,
2881
2899
  selfHealTool,
2882
2900
  plannerError: liveOutcome.plannerError ? sanitizeUserFacingErrorText(liveOutcome.plannerError) : null,
@@ -2904,7 +2922,7 @@ export class ChatCommand {
2904
2922
  taskId: response.taskId || null,
2905
2923
  contextId: response.contextId || null,
2906
2924
  partial: response.partial === true || (!executorSucceeded && liveOutcome.tasksSucceeded > 0),
2907
- content: answerContent || response.content || runEvaluation.statusHeadline,
2925
+ content: finalAnswer || runEvaluation.statusHeadline,
2908
2926
  statusHeadline: runEvaluation.statusHeadline,
2909
2927
  tasksSucceeded: liveOutcome.tasksSucceeded,
2910
2928
  tasksTotal: liveOutcome.tasksTotal,
@@ -2920,7 +2938,7 @@ export class ChatCommand {
2920
2938
  },
2921
2939
  }, null, 2));
2922
2940
  }
2923
- this.messages.push({ role: 'assistant', content: answerContent || response.content || runEvaluation.statusHeadline });
2941
+ this.messages.push({ role: 'assistant', content: finalAnswer || runEvaluation.statusHeadline });
2924
2942
  watcher?.stop();
2925
2943
  return true;
2926
2944
  }
@@ -18,6 +18,8 @@ export interface LiveOutcome {
18
18
  analysisReadToolsUsed: number;
19
19
  /** Final user-facing answer text (stream + response body) */
20
20
  answerContent?: string;
21
+ /** User asked for deep/project-scale analysis, so shallow evidence must not pass. */
22
+ deepAnalysisRequired?: boolean;
21
23
  streamAborted?: boolean;
22
24
  }
23
25
  export interface TaskSummaryEvent {
@@ -31,7 +33,7 @@ export interface RunEvaluation {
31
33
  uiTheme: 'success' | 'warning' | 'error';
32
34
  }
33
35
  export declare function createLiveOutcome(): LiveOutcome;
34
- /** Tool-trace stubs like `[read_file] "index.html"` — not a user-facing answer. */
36
+ /** Tool-trace stubs like `[read_file] "index.html"` or `<read_file>…</read_file>` — not a user-facing answer. */
35
37
  export declare function isToolEvidenceStubAnswer(text: string): boolean;
36
38
  /** True when text looks like a real answer, not an executor/system placeholder. */
37
39
  export declare function isSubstantiveAgentAnswer(text: string): boolean;
@@ -17,10 +17,12 @@ export function createLiveOutcome() {
17
17
  analysisToolsUsed: 0,
18
18
  analysisReadToolsUsed: 0,
19
19
  answerContent: '',
20
+ deepAnalysisRequired: false,
20
21
  streamAborted: false,
21
22
  };
22
23
  }
23
24
  const EXECUTOR_PLACEHOLDER_RE = /read-only analysis turn|run at least one discovery tool|reviewing attached files and workspace findings/i;
25
+ const BOOTSTRAP_PLACEHOLDER_RE = /bootstrap discovery completed|inspect the most relevant files next|reviewing attached files and workspace findings/i;
24
26
  const LIST_ONLY_DISCOVERY_TOOLS = new Set([
25
27
  'list_directory',
26
28
  'list_dir',
@@ -28,19 +30,29 @@ const LIST_ONLY_DISCOVERY_TOOLS = new Set([
28
30
  'dir',
29
31
  ]);
30
32
  const GENERIC_SUMMARY_RE = /^(completed the requested analysis|reviewed the workspace without writing changes|task completed|done|finished|analysis completed)([.\s]|$)/i;
31
- /** Tool-trace stubs like `[read_file] "index.html"` — not a user-facing answer. */
33
+ /** Tool-trace stubs like `[read_file] "index.html"` or `<read_file>…</read_file>` — not a user-facing answer. */
32
34
  export function isToolEvidenceStubAnswer(text) {
33
35
  const trimmed = String(text || '').trim();
34
36
  if (!trimmed)
35
37
  return true;
38
+ if (BOOTSTRAP_PLACEHOLDER_RE.test(trimmed))
39
+ return true;
36
40
  if (GENERIC_SUMMARY_RE.test(trimmed.replace(/\.\s*\d+\s+tool steps.*$/i, '').trim()))
37
41
  return true;
38
42
  const lines = trimmed.split('\n').map((line) => line.trim()).filter(Boolean);
39
43
  if (lines.length === 0)
40
44
  return true;
41
45
  const stubLine = /^\[(?:read_file|grep|list_directory|search_files|glob|tool_call)\]/i;
46
+ const xmlToolLine = /^<\/?(?:read_file|grep|list_directory|search_files|glob|tool_call|tool_result|path)\b[^>]*>/i;
47
+ const xmlToolBlockOnly = trimmed
48
+ .replace(/<read_file>\s*<path>[\s\S]*?<\/path>\s*<\/read_file>/gi, '')
49
+ .replace(/<(?:grep|list_directory|search_files|glob|tool_call|tool_result)\b[\s\S]*?<\/(?:grep|list_directory|search_files|glob|tool_call|tool_result)>/gi, '')
50
+ .replace(/\s+/g, '')
51
+ .trim();
52
+ if (!xmlToolBlockOnly)
53
+ return true;
42
54
  const filesOnly = /^Files inspected:/i;
43
- const stubLines = lines.filter((line) => stubLine.test(line) || filesOnly.test(line)).length;
55
+ const stubLines = lines.filter((line) => stubLine.test(line) || xmlToolLine.test(line) || filesOnly.test(line)).length;
44
56
  if (stubLines === lines.length)
45
57
  return true;
46
58
  if (stubLines > 0 && stubLines / lines.length >= 0.6 && trimmed.length < 400)
@@ -101,6 +113,7 @@ const READ_DISCOVERY_TOOLS = new Set([
101
113
  'grep',
102
114
  'list_directory',
103
115
  'glob',
116
+ 'search_files',
104
117
  'search_project',
105
118
  'list_dir',
106
119
  'dir',
@@ -171,6 +184,10 @@ export function evaluateExecutorSuccess(liveOutcome) {
171
184
  const answerText = normalizeAgentAnswerContent(liveOutcome.answerContent);
172
185
  const hasSubstantiveAnswer = isSubstantiveAgentAnswer(answerText);
173
186
  const hasDeepRead = liveOutcome.analysisReadToolsUsed > 0;
187
+ const meetsDeepAnalysisDepth = !liveOutcome.deepAnalysisRequired
188
+ || (liveOutcome.analysisToolsUsed >= 4
189
+ && liveOutcome.analysisReadToolsUsed >= 3
190
+ && answerText.length >= 700);
174
191
  if (hasFatalError && !hasSubstantiveAnswer) {
175
192
  return {
176
193
  executorSucceeded: false,
@@ -199,6 +216,13 @@ export function evaluateExecutorSuccess(liveOutcome) {
199
216
  uiTheme: 'warning',
200
217
  };
201
218
  }
219
+ if (!meetsDeepAnalysisDepth) {
220
+ return {
221
+ executorSucceeded: false,
222
+ statusHeadline: '⚠ Deep analysis was too shallow — retrying needs a project map, multiple file reads, and a structured report',
223
+ uiTheme: 'warning',
224
+ };
225
+ }
202
226
  return {
203
227
  executorSucceeded: true,
204
228
  statusHeadline: '✓ Analysis completed',
package/dist/utils/api.js CHANGED
@@ -3358,11 +3358,15 @@ document.addEventListener('DOMContentLoaded', () => {
3358
3358
  if (Array.isArray(data?.events)) {
3359
3359
  const completionEvent = [...data.events].reverse().find((event) => event && event.type === 'complete' && typeof event.summary === 'string' && event.summary.trim());
3360
3360
  if (completionEvent) {
3361
- return sanitizeUserFacingPathText(completionEvent.summary);
3361
+ const text = this.sanitizeV3AgentResponseText(completionEvent.summary);
3362
+ if (isSubstantiveAgentAnswer(text))
3363
+ return text;
3362
3364
  }
3363
3365
  const messageEvent = [...data.events].reverse().find((event) => event && event.type === 'message' && typeof event.content === 'string' && event.content.trim());
3364
3366
  if (messageEvent) {
3365
- return this.sanitizeV3AgentResponseText(messageEvent.content);
3367
+ const text = this.sanitizeV3AgentResponseText(messageEvent.content);
3368
+ if (isSubstantiveAgentAnswer(text))
3369
+ return text;
3366
3370
  }
3367
3371
  // Synthesize a grounded answer from the tool-call evidence the
3368
3372
  // agent produced, rather than dumping the raw event trace.
@@ -3387,6 +3391,9 @@ document.addEventListener('DOMContentLoaded', () => {
3387
3391
  .replace(/<thinking>[\s\S]*?<\/thinking>/gi, '')
3388
3392
  .replace(/<tool_call>[\s\S]*?<\/tool_call>/gi, '')
3389
3393
  .replace(/<tool_code>[\s\S]*?<\/tool_code>/gi, '')
3394
+ .replace(/<read_file>\s*<path>[\s\S]*?<\/path>\s*<\/read_file>/gi, '')
3395
+ .replace(/<(?:grep|list_directory|search_files|glob|tool_result)\b[\s\S]*?<\/(?:grep|list_directory|search_files|glob|tool_result)>/gi, '')
3396
+ .replace(/bootstrap discovery completed\s*[—-]\s*inspect the most relevant files next\.?/gi, '')
3390
3397
  .replace(/```json\s*\[\s*\{[\s\S]*?"tool"[\s\S]*?\}\s*\]\s*```/gi, '')
3391
3398
  .replace(/```json\s*\{[\s\S]*?"tool"[\s\S]*?\}\s*```/gi, '')
3392
3399
  .replace(/^\s*(?:json\s*)?\[\s*\{[\s\S]*?"tool"[\s\S]*$/gim, '')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.11.7",
3
+ "version": "1.11.8",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",