vigthoria-cli 1.11.5 → 1.11.6

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.
@@ -63,6 +63,8 @@ export declare class ChatCommand {
63
63
  private getDefaultChatModel;
64
64
  private resolveInitialModel;
65
65
  private isLegacyAgentFallbackAllowed;
66
+ private shouldRescueFailedAnalysis;
67
+ private tryAnalysisLocalRescue;
66
68
  private resolveAgentExecutionPolicy;
67
69
  private getMessagesForModel;
68
70
  private getActivePersonaMode;
@@ -259,6 +259,28 @@ export class ChatCommand {
259
259
  // Operators can still opt in during emergency local debugging.
260
260
  return process.env.VIGTHORIA_ALLOW_LEGACY_AGENT_FALLBACK === '1';
261
261
  }
262
+ shouldRescueFailedAnalysis() {
263
+ return process.env.VIGTHORIA_DISABLE_ANALYSIS_RESCUE !== '1';
264
+ }
265
+ async tryAnalysisLocalRescue(prompt, workspacePath) {
266
+ if (!this.shouldRescueFailedAnalysis() || !this.tools) {
267
+ return false;
268
+ }
269
+ const clientToolErrors = this.api.getClientToolErrors();
270
+ if (clientToolErrors.some((err) => /could not accept the result|client tool bridge failed/i.test(err))) {
271
+ return false;
272
+ }
273
+ console.log('');
274
+ console.log(chalk.yellow('V3 analysis did not produce a grounded answer — retrying with local workspace tools...'));
275
+ console.log(chalk.gray(`Workspace: ${workspacePath}`));
276
+ console.log('');
277
+ const last = this.messages[this.messages.length - 1];
278
+ if (last?.role === 'assistant') {
279
+ this.messages.pop();
280
+ }
281
+ await this.runLocalAgentLoop(prompt);
282
+ return true;
283
+ }
262
284
  resolveAgentExecutionPolicy(prompt) {
263
285
  const explicitModel = this.modelExplicitlySelected;
264
286
  const heavyTask = this.config.isComplexTask(prompt);
@@ -2744,7 +2766,14 @@ export class ChatCommand {
2744
2766
  let selfHealStatus = null;
2745
2767
  let selfHealTool = null;
2746
2768
  const runEvaluation = evaluateExecutorSuccess(liveOutcome);
2747
- const executorSucceeded = runEvaluation.executorSucceeded;
2769
+ let executorSucceeded = runEvaluation.executorSucceeded;
2770
+ if (!executorSucceeded && !requiresWorkspaceChanges && !this.jsonOutput) {
2771
+ const rescued = await this.tryAnalysisLocalRescue(prompt, workspacePath);
2772
+ if (rescued) {
2773
+ watcher?.stop();
2774
+ return true;
2775
+ }
2776
+ }
2748
2777
  if (!executorSucceeded && requiresWorkspaceChanges && changedFileCount === 0 && !liveOutcome.executorError) {
2749
2778
  const clientToolErrors = this.api.getClientToolErrors();
2750
2779
  const transportErrors = this.api.getLastChatTransportErrors();
@@ -2839,7 +2868,7 @@ export class ChatCommand {
2839
2868
  clientToolErrors: this.api.getClientToolErrors(),
2840
2869
  transportErrors: this.api.getLastChatTransportErrors(),
2841
2870
  workspacePath: workspacePath || null,
2842
- workspaceSyncIssue: (!executorSucceeded && workspaceHasOutput && changedFileCount === 0)
2871
+ workspaceSyncIssue: (!executorSucceeded && requiresWorkspaceChanges && workspaceHasOutput && changedFileCount === 0)
2843
2872
  ? 'Local workspace has files but the agent did not confirm tracked changes.'
2844
2873
  : null,
2845
2874
  finishedAt: Date.now(),
package/dist/utils/api.js CHANGED
@@ -2745,9 +2745,10 @@ menu {
2745
2745
  .map((entry) => `${entry.isDirectory() ? '[DIR] ' : ''}${entry.name}`);
2746
2746
  return { success: true, output: entries.join('\n') || '(empty)' };
2747
2747
  }
2748
- if (name === 'glob' || name === 'search_files') {
2748
+ if (name === 'glob' || name === 'search_files' || name === 'grep') {
2749
2749
  const pattern = String(args.pattern || args.query || '').toLowerCase();
2750
2750
  const files = [];
2751
+ const matches = [];
2751
2752
  const walk = (dir) => {
2752
2753
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
2753
2754
  if (['node_modules', '.git'].includes(entry.name))
@@ -2758,18 +2759,30 @@ menu {
2758
2759
  continue;
2759
2760
  }
2760
2761
  const relative = path.relative(rootPath, absolute).replace(/\\/g, '/');
2761
- if (!pattern || relative.toLowerCase().includes(pattern.replace(/\*/g, ''))) {
2762
- files.push(relative);
2763
- }
2764
- else if (name === 'search_files') {
2765
- const content = fs.readFileSync(absolute, 'utf8');
2766
- if (content.toLowerCase().includes(pattern))
2762
+ if (name === 'glob' && pattern) {
2763
+ if (!pattern || relative.toLowerCase().includes(pattern.replace(/\*/g, ''))) {
2767
2764
  files.push(relative);
2765
+ }
2766
+ }
2767
+ else if (pattern) {
2768
+ try {
2769
+ const content = fs.readFileSync(absolute, 'utf8');
2770
+ if (relative.toLowerCase().includes(pattern.replace(/\*/g, '')) || content.toLowerCase().includes(pattern)) {
2771
+ matches.push(`${relative}: ${content.split('\n').find((line) => line.toLowerCase().includes(pattern))?.trim() || '(match)'}`);
2772
+ }
2773
+ }
2774
+ catch {
2775
+ // skip unreadable/binary files
2776
+ }
2768
2777
  }
2769
2778
  }
2770
2779
  };
2771
- walk(fs.statSync(target.absolutePath).isDirectory() ? target.absolutePath : rootPath);
2772
- return { success: true, output: files.slice(0, 200).join('\n') || '(no matches)' };
2780
+ const searchRoot = fs.statSync(target.absolutePath).isDirectory() ? target.absolutePath : rootPath;
2781
+ walk(searchRoot);
2782
+ if (name === 'glob') {
2783
+ return { success: true, output: files.slice(0, 200).join('\n') || '(no matches)' };
2784
+ }
2785
+ return { success: true, output: matches.slice(0, 100).join('\n') || '(no matches)' };
2773
2786
  }
2774
2787
  if (name === 'syntax_check') {
2775
2788
  const content = fs.readFileSync(target.absolutePath, 'utf8');
@@ -2805,8 +2818,27 @@ menu {
2805
2818
  if (!contextId || !callId)
2806
2819
  return;
2807
2820
  const backendUrl = responseUrl ? new URL(responseUrl).origin : (context.mcpContextBackendUrl || null);
2821
+ const emitStream = (streamEvent) => {
2822
+ if (typeof context.onStreamEvent === 'function') {
2823
+ try {
2824
+ context.onStreamEvent(streamEvent);
2825
+ }
2826
+ catch {
2827
+ // UI callbacks must never break the client tool bridge.
2828
+ }
2829
+ }
2830
+ };
2831
+ emitStream({ type: 'tool_call', name: toolName, tool: toolName, arguments: event.arguments || {} });
2808
2832
  try {
2809
2833
  const result = await this.executeV3ClientToolRequest(event, context);
2834
+ emitStream({
2835
+ type: 'tool_result',
2836
+ name: toolName,
2837
+ tool: toolName,
2838
+ success: result.success,
2839
+ output: result.output,
2840
+ error: result.error || '',
2841
+ });
2810
2842
  try {
2811
2843
  await this.submitClientToolResult(contextId, callId, result, backendUrl);
2812
2844
  }
@@ -3646,6 +3678,7 @@ document.addEventListener('DOMContentLoaded', () => {
3646
3678
  // Exit stream early on complete — agent is done; server-side teardown
3647
3679
  // can hold the connection open for many seconds otherwise.
3648
3680
  if (event.type === 'complete') {
3681
+ await Promise.allSettled([...this.pendingV3ClientToolTasks]);
3649
3682
  reader.cancel().catch(() => { });
3650
3683
  return {
3651
3684
  task_id: events.find((entry) => entry && entry.task_id)?.task_id || null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.11.5",
3
+ "version": "1.11.6",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",