vigthoria-cli 1.6.20 → 1.6.22

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.
@@ -15,6 +15,7 @@ interface ChatOptions {
15
15
  stream?: boolean;
16
16
  prompt?: string;
17
17
  json?: boolean;
18
+ bridge?: string;
18
19
  }
19
20
  export declare class ChatCommand {
20
21
  private config;
@@ -46,6 +47,11 @@ export declare class ChatCommand {
46
47
  private resolveAgentExecutionPolicy;
47
48
  private getMessagesForModel;
48
49
  private isDiagnosticPrompt;
50
+ /**
51
+ * Returns true when the prompt is a simple lookup / analysis / read-only
52
+ * question — these should use analysis_only workflow, not full_autonomy.
53
+ */
54
+ private isAnalysisLookupPrompt;
49
55
  private isBrowserTaskPrompt;
50
56
  private inferAgentTaskType;
51
57
  private buildTaskShapingInstructions;
@@ -59,6 +65,8 @@ export declare class ChatCommand {
59
65
  private updateOperatorSpinner;
60
66
  constructor(config: Config, logger: Logger);
61
67
  run(options: ChatOptions): Promise<void>;
68
+ /** Handle an inbound admin command from the Commando Bridge. */
69
+ private handleAdminCommand;
62
70
  private ensureProjectWorkspace;
63
71
  private resolveProjectPath;
64
72
  private shouldUseManagedWorkspace;
@@ -46,6 +46,7 @@ const logger_js_1 = require("../utils/logger.js");
46
46
  const api_js_1 = require("../utils/api.js");
47
47
  const tools_js_1 = require("../utils/tools.js");
48
48
  const session_js_1 = require("../utils/session.js");
49
+ const bridge_client_js_1 = require("../utils/bridge-client.js");
49
50
  const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
50
51
  const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS || '3900000';
51
52
  const parsed = Number.parseInt(rawValue, 10);
@@ -195,6 +196,13 @@ class ChatCommand {
195
196
  isDiagnosticPrompt(prompt) {
196
197
  return /(startup|start up|won'?t start|doesn'?t start|crash|crashes|error|errors|failing|fails|issue|issues|bug|bugs|diagnos|debug|runtime|log|logs|exception|traceback|stack trace|yaml|blocking|blocker)/i.test(prompt);
197
198
  }
199
+ /**
200
+ * Returns true when the prompt is a simple lookup / analysis / read-only
201
+ * question — these should use analysis_only workflow, not full_autonomy.
202
+ */
203
+ isAnalysisLookupPrompt(prompt) {
204
+ return /^(what|which|where|how many|who|find|list|show|check|inspect|analyze|analyse|audit|explain|describe|summarize|summarise|review|overview|count|read|look at|tell me|locate|search for|does .* exist)/i.test(prompt.trim());
205
+ }
198
206
  isBrowserTaskPrompt(prompt) {
199
207
  return /(browser|chrome|devtools|console|dom|network tab|network request|frontend runtime|client-side|client side|rendering|page load|websocket|ui bug|inspect element)/i.test(prompt);
200
208
  }
@@ -360,7 +368,13 @@ class ChatCommand {
360
368
  }
361
369
  async run(options) {
362
370
  if (!this.config.isAuthenticated()) {
363
- this.logger.error('Not authenticated. Run: vigthoria login');
371
+ if (options.json) {
372
+ process.exitCode = 1;
373
+ console.log(JSON.stringify({ success: false, error: 'Not authenticated. Run: vigthoria login' }, null, 2));
374
+ }
375
+ else {
376
+ this.logger.error('Not authenticated. Run: vigthoria login');
377
+ }
364
378
  return;
365
379
  }
366
380
  this.agentMode = options.agent === true;
@@ -382,6 +396,21 @@ class ChatCommand {
382
396
  this.directToolContinuationCount = 0;
383
397
  this.tools = new tools_js_1.AgenticTools(this.logger, this.currentProjectPath, async (action) => this.requestPermission(action), this.autoApprove);
384
398
  this.initializeSession(options.resume === true);
399
+ // ── Commando Bridge: connect if --bridge was specified ──────────
400
+ if (options.bridge) {
401
+ const bridgeClient = new bridge_client_js_1.BridgeClient({
402
+ bridgeUrl: options.bridge,
403
+ apiKey: this.config.get('authToken'),
404
+ onAdminCommand: (cmd) => this.handleAdminCommand(cmd),
405
+ });
406
+ await bridgeClient.connect();
407
+ const mode = this.operatorMode ? 'operator' : this.agentMode ? 'agent' : 'chat';
408
+ bridgeClient.emitStart({
409
+ command: mode,
410
+ flags: { model: this.currentModel, agent: this.agentMode, operator: this.operatorMode, autoApprove: this.autoApprove },
411
+ cwd: this.currentProjectPath,
412
+ });
413
+ }
385
414
  if (this.operatorMode && !this.hasOperatorAccess()) {
386
415
  if (this.currentSession) {
387
416
  this.currentSession.operatorMode = false;
@@ -393,9 +422,35 @@ class ChatCommand {
393
422
  }
394
423
  if (options.prompt) {
395
424
  await this.handleDirectPrompt(options.prompt);
425
+ (0, bridge_client_js_1.getBridgeClient)()?.emitEnd({ reason: 'prompt-complete' });
396
426
  return;
397
427
  }
398
428
  await this.startInteractiveChat();
429
+ (0, bridge_client_js_1.getBridgeClient)()?.emitEnd({ reason: 'interactive-exit' });
430
+ }
431
+ /** Handle an inbound admin command from the Commando Bridge. */
432
+ handleAdminCommand(cmd) {
433
+ switch (cmd.action) {
434
+ case 'ping':
435
+ (0, bridge_client_js_1.getBridgeClient)()?.emitModelResponse({ model: this.currentModel, chars: 0, hasToolCalls: false, preview: 'pong' });
436
+ break;
437
+ case 'set-model':
438
+ if (cmd.params?.value && typeof cmd.params.value === 'string') {
439
+ this.currentModel = cmd.params.value;
440
+ (0, bridge_client_js_1.getBridgeClient)()?.emitModeChange({ mode: this.operatorMode ? 'operator' : this.agentMode ? 'agent' : 'chat', model: this.currentModel });
441
+ if (!this.jsonOutput)
442
+ console.log(chalk_1.default.yellow(`[bridge] Model changed to: ${this.currentModel}`));
443
+ }
444
+ break;
445
+ case 'abort':
446
+ if (!this.jsonOutput)
447
+ console.log(chalk_1.default.red(`[bridge] Abort requested by admin`));
448
+ (0, bridge_client_js_1.getBridgeClient)()?.emitEnd({ reason: 'admin-abort' });
449
+ process.exit(0);
450
+ break;
451
+ default:
452
+ (0, bridge_client_js_1.getBridgeClient)()?.emitError({ message: `Unknown admin command: ${cmd.action}` });
453
+ }
399
454
  }
400
455
  ensureProjectWorkspace() {
401
456
  if (fs.existsSync(this.currentProjectPath)) {
@@ -611,7 +666,8 @@ class ChatCommand {
611
666
  }
612
667
  catch (error) {
613
668
  if (spinner) {
614
- spinner.fail('Workflow target execution failed');
669
+ spinner.stop();
670
+ this.logger.error('Workflow target execution failed');
615
671
  }
616
672
  throw error;
617
673
  }
@@ -621,10 +677,37 @@ class ChatCommand {
621
677
  this.logger.error(this.operatorAccessMessage());
622
678
  return;
623
679
  }
680
+ (0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: 'operator', model: this.currentModel });
624
681
  const runtimeContext = await this.getPromptRuntimeContext(prompt);
625
682
  const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: 'Thinking like an operator...', spinner: 'clock' }).start();
626
- const executionPrompt = this.buildExecutionPrompt(prompt);
627
- const workflowType = this.isDiagnosticPrompt(prompt) ? 'analysis_only' : 'full_autonomy';
683
+ const isLookup = this.isAnalysisLookupPrompt(prompt);
684
+ const workflowType = this.isDiagnosticPrompt(prompt) || isLookup
685
+ ? 'analysis_only'
686
+ : 'full_autonomy';
687
+ // For lightweight lookup tasks, prepend a grounding constraint so
688
+ // the operator returns a concise answer rather than a verbose report.
689
+ let executionPrompt = this.buildExecutionPrompt(prompt);
690
+ if (isLookup) {
691
+ // Inject workspace file listing into the prompt so the operator
692
+ // can answer "which file defines X" without a full agent loop.
693
+ let fileListing = '';
694
+ try {
695
+ const snapshot = this.api.getAgentWorkspaceSnapshot(this.currentProjectPath);
696
+ if (snapshot && snapshot.paths.length > 0) {
697
+ fileListing = '\n\nWorkspace file listing:\n' + snapshot.paths.slice(0, 80).join('\n');
698
+ }
699
+ }
700
+ catch { /* ignore */ }
701
+ executionPrompt = [
702
+ 'INSTRUCTION: This is a lightweight lookup task. Return ONLY the concise answer the user asked for.',
703
+ 'Do NOT produce a certification report, audit, or analysis blob.',
704
+ 'Do NOT return files_analyzed counts or summary statistics.',
705
+ 'Just answer the question directly.',
706
+ fileListing,
707
+ '',
708
+ executionPrompt,
709
+ ].join('\n');
710
+ }
628
711
  try {
629
712
  const response = await this.api.runOperatorWorkflow(executionPrompt, {
630
713
  workspacePath: this.currentProjectPath,
@@ -665,9 +748,12 @@ class ChatCommand {
665
748
  }
666
749
  catch (error) {
667
750
  if (spinner) {
668
- spinner.fail('Operator workflow failed');
751
+ spinner.stop();
669
752
  }
670
753
  const errorMsg = error.message || 'Operator workflow failed with an unknown error.';
754
+ if (!this.jsonOutput) {
755
+ this.logger.error('Operator workflow failed');
756
+ }
671
757
  if (this.jsonOutput) {
672
758
  process.exitCode = 1;
673
759
  console.log(JSON.stringify({
@@ -686,14 +772,30 @@ class ChatCommand {
686
772
  this.lastActionableUserInput = prompt;
687
773
  // In non-agent chat mode the model has no tool access. Inject a
688
774
  // grounding constraint so it doesn't fabricate file contents.
775
+ // Also inject a real file listing so the model can reference actual
776
+ // paths instead of guessing.
689
777
  const needsGrounding = /(repo|file|code|project|workspace|source|inspect|analyze|audit|review)/i.test(prompt);
690
778
  if (needsGrounding && !this.messages.some(m => m.role === 'system' && m.content.includes('no direct file access'))) {
779
+ let groundingContent = 'You are in simple chat mode with no direct file access or tools. Do not fabricate file contents, search results, or analysis steps. If the user asks you to modify, edit, or deeply inspect files, advise them to use agent mode (vig chat --agent) for grounded repo analysis.';
780
+ // Inject a workspace file listing so the model has real paths
781
+ try {
782
+ const snapshot = this.api.getAgentWorkspaceSnapshot(this.currentProjectPath);
783
+ if (snapshot && snapshot.paths.length > 0) {
784
+ const listing = snapshot.paths.slice(0, 60).join('\n');
785
+ groundingContent += `\n\nWorkspace: ${this.currentProjectPath}\nFile listing (${snapshot.fileCount} files total):\n${listing}`;
786
+ if (snapshot.fileCount > 60) {
787
+ groundingContent += `\n... and ${snapshot.fileCount - 60} more files.`;
788
+ }
789
+ }
790
+ }
791
+ catch { /* ignore snapshot errors */ }
691
792
  this.messages.push({
692
793
  role: 'system',
693
- content: 'You are in simple chat mode with no direct file access. Do not fabricate file contents, search results, or analysis steps. If the user asks about specific files, advise them to use agent mode (vig chat --agent) for grounded repo analysis.',
794
+ content: groundingContent,
694
795
  });
695
796
  }
696
797
  this.messages.push({ role: 'user', content: this.buildExecutionPrompt(prompt) });
798
+ (0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: 'chat', model: this.currentModel });
697
799
  const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: 'Thinking...', spinner: 'clock' }).start();
698
800
  try {
699
801
  const response = await this.api.chat(this.getMessagesForModel(), this.currentModel);
@@ -719,7 +821,9 @@ class ChatCommand {
719
821
  }
720
822
  catch (error) {
721
823
  if (spinner)
722
- spinner.fail('Failed to get response');
824
+ spinner.stop();
825
+ if (!this.jsonOutput)
826
+ this.logger.error('Failed to get response');
723
827
  const errorMsg = error.message;
724
828
  if (this.jsonOutput) {
725
829
  process.exitCode = 1;
@@ -772,6 +876,7 @@ class ChatCommand {
772
876
  this.directToolContinuationCount = 0;
773
877
  this.agentToolEvidence = { discovery: 0, mutation: 0, searchFailed: 0 };
774
878
  this.tools.clearSessionApprovals();
879
+ (0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: this.operatorMode ? 'operator' : 'agent', model: this.currentModel });
775
880
  this.ensureAgentSystemPrompt();
776
881
  this.messages.push({ role: 'user', content: this.buildScopedUserPrompt(prompt) });
777
882
  this.saveSession();
@@ -786,6 +891,12 @@ class ChatCommand {
786
891
  this.messages.push({ role: 'assistant', content: assistantMessage });
787
892
  const toolCalls = this.extractToolCalls(assistantMessage);
788
893
  const visibleText = this.stripToolPayloads(assistantMessage).trim();
894
+ (0, bridge_client_js_1.getBridgeClient)()?.emitModelResponse({
895
+ model: this.currentModel,
896
+ chars: assistantMessage.length,
897
+ hasToolCalls: toolCalls.length > 0,
898
+ preview: visibleText.slice(0, 300),
899
+ });
789
900
  if (toolCalls.length === 0) {
790
901
  // Phase 5: Quality gate — if the agent tries to conclude on the first
791
902
  // turn without any discovery, push it to gather evidence first.
@@ -830,7 +941,9 @@ class ChatCommand {
830
941
  }
831
942
  catch (error) {
832
943
  if (spinner)
833
- spinner.fail('Agent request failed');
944
+ spinner.stop();
945
+ if (!this.jsonOutput)
946
+ this.logger.error('Agent request failed');
834
947
  if (this.jsonOutput) {
835
948
  process.exitCode = 1;
836
949
  console.log(JSON.stringify({
@@ -1040,14 +1153,15 @@ class ChatCommand {
1040
1153
  if (!success) {
1041
1154
  if (this.isLegacyAgentFallbackAllowed()) {
1042
1155
  if (spinner) {
1043
- spinner.warn('Falling back to legacy CLI agent loop');
1156
+ spinner.stop();
1044
1157
  }
1158
+ this.logger.warn('Falling back to legacy CLI agent loop');
1045
1159
  this.logger.debug(`V3 agent workflow returned an incomplete result: ${previewGate?.error || 'workspace changes were not fully validated'}`);
1046
1160
  return false;
1047
1161
  }
1048
1162
  const errorMessage = `V3 agent workflow returned an incomplete result and legacy fallback is disabled. ${previewGate?.error || 'Workspace changes were not fully validated.'}`;
1049
1163
  if (spinner) {
1050
- spinner.fail('V3 agent workflow rejected the result');
1164
+ spinner.stop();
1051
1165
  }
1052
1166
  this.logger.error(errorMessage);
1053
1167
  this.messages.push({ role: 'assistant', content: errorMessage });
@@ -1109,13 +1223,14 @@ class ChatCommand {
1109
1223
  }
1110
1224
  if (this.isLegacyAgentFallbackAllowed()) {
1111
1225
  if (spinner) {
1112
- spinner.warn('Falling back to legacy CLI agent loop');
1226
+ spinner.stop();
1113
1227
  }
1228
+ this.logger.warn('Falling back to legacy CLI agent loop');
1114
1229
  this.logger.debug(`V3 agent workflow unavailable: ${error.message}`);
1115
1230
  return false;
1116
1231
  }
1117
1232
  if (spinner) {
1118
- spinner.fail('V3 agent workflow failed');
1233
+ spinner.stop();
1119
1234
  }
1120
1235
  const errorMessage = `Agent mode requires the V3 workflow and will not fall back to the legacy CLI loop. ${error.message}`;
1121
1236
  this.logger.error(errorMessage);
@@ -1718,6 +1833,7 @@ class ChatCommand {
1718
1833
  if (!this.jsonOutput) {
1719
1834
  console.log(chalk_1.default.cyan(`⚙ Executing: ${call.tool}`));
1720
1835
  }
1836
+ (0, bridge_client_js_1.getBridgeClient)()?.emitToolCall({ tool: call.tool, args: call.args });
1721
1837
  let result = await this.tools.execute(call);
1722
1838
  // Phase 2: If a search tool failed (search_failed), retry with alternate approach
1723
1839
  const searchStatus = result.metadata?.searchStatus;
@@ -1739,6 +1855,7 @@ class ChatCommand {
1739
1855
  console.log(result.success ? chalk_1.default.gray(summary) : chalk_1.default.red(summary));
1740
1856
  }
1741
1857
  this.messages.push({ role: 'system', content: summary });
1858
+ (0, bridge_client_js_1.getBridgeClient)()?.emitToolResult({ tool: call.tool, success: result.success, preview: (result.output || result.error || '').slice(0, 300) });
1742
1859
  // Phase 5: Track tool evidence for quality gates
1743
1860
  const finalStatus = result.metadata?.searchStatus;
1744
1861
  if (finalStatus === 'search_failed') {
@@ -151,7 +151,8 @@ class DeployCommand {
151
151
  console.log(chalk_1.default.gray(' Upgrade to a subdomain for permanent hosting.\n'));
152
152
  }
153
153
  catch (error) {
154
- spinner.fail('Deploy failed');
154
+ spinner.stop();
155
+ this.logger.error('Deploy failed');
155
156
  const errMsg = error instanceof Error ? error.message : 'Unknown error';
156
157
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
157
158
  }
@@ -204,7 +205,8 @@ class DeployCommand {
204
205
  console.log(chalk_1.default.gray(' ✓ Unlimited traffic included\n'));
205
206
  }
206
207
  catch (error) {
207
- spinner.fail('Deploy failed');
208
+ spinner.stop();
209
+ this.logger.error('Deploy failed');
208
210
  const errMsg = error instanceof Error ? error.message : 'Unknown error';
209
211
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
210
212
  }
@@ -258,7 +260,8 @@ class DeployCommand {
258
260
  }
259
261
  }
260
262
  catch (error) {
261
- spinner.fail('Deploy failed');
263
+ spinner.stop();
264
+ this.logger.error('Deploy failed');
262
265
  const errMsg = error instanceof Error ? error.message : 'Unknown error';
263
266
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
264
267
  }
@@ -334,7 +337,8 @@ class DeployCommand {
334
337
  console.log(chalk_1.default.cyan('\n Subscribe: vig deploy --subdomain myapp\n'));
335
338
  }
336
339
  catch (error) {
337
- spinner.fail('Failed to fetch plans');
340
+ spinner.stop();
341
+ this.logger.error('Failed to fetch plans');
338
342
  const errMsg = error instanceof Error ? error.message : 'Unknown error';
339
343
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
340
344
  }
@@ -374,7 +378,8 @@ class DeployCommand {
374
378
  }
375
379
  }
376
380
  catch (error) {
377
- spinner.fail('List failed');
381
+ spinner.stop();
382
+ this.logger.error('List failed');
378
383
  const errMsg = error instanceof Error ? error.message : 'Unknown error';
379
384
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
380
385
  }
@@ -401,7 +406,8 @@ class DeployCommand {
401
406
  console.log(JSON.stringify(data, null, 2));
402
407
  }
403
408
  catch (error) {
404
- spinner.fail('Status check failed');
409
+ spinner.stop();
410
+ this.logger.error('Status check failed');
405
411
  const errMsg = error instanceof Error ? error.message : 'Unknown error';
406
412
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
407
413
  }
@@ -434,7 +440,8 @@ class DeployCommand {
434
440
  }
435
441
  }
436
442
  catch (error) {
437
- spinner.fail('Verification failed');
443
+ spinner.stop();
444
+ this.logger.error('Verification failed');
438
445
  const errMsg = error instanceof Error ? error.message : 'Unknown error';
439
446
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
440
447
  }
@@ -468,7 +475,8 @@ class DeployCommand {
468
475
  console.log(chalk_1.default.gray('\n Your project files are still in your repository.\n'));
469
476
  }
470
477
  catch (error) {
471
- spinner.fail('Remove failed');
478
+ spinner.stop();
479
+ this.logger.error('Remove failed');
472
480
  const errMsg = error instanceof Error ? error.message : 'Unknown error';
473
481
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
474
482
  }
@@ -41,6 +41,11 @@ class EditCommand {
41
41
  // Get instruction
42
42
  let instruction = options.instruction;
43
43
  if (!instruction) {
44
+ // --apply requires --instruction to avoid hanging on non-interactive terminals
45
+ if (options.apply) {
46
+ this.logger.error('The --apply flag requires --instruction. Example: vigthoria edit file.ts --apply --instruction "fix the bug"');
47
+ return;
48
+ }
44
49
  const answer = await inquirer_1.default.prompt([
45
50
  {
46
51
  type: 'input',
@@ -60,9 +65,16 @@ class EditCommand {
60
65
  const response = await this.api.chat([
61
66
  {
62
67
  role: 'system',
63
- content: `You are a code editor. Edit the provided code according to the user's instruction.
64
- Return ONLY the complete modified code without any explanation or markdown formatting.
65
- Preserve the original formatting and style unless the instruction specifically asks to change it.`,
68
+ content: `You are a precise code editor. Edit the provided code EXACTLY as the user instructs.
69
+
70
+ MANDATORY RULES:
71
+ 1. Return ONLY the complete modified file content. No explanation, no markdown.
72
+ 2. Preserve all original formatting, indentation, and style.
73
+ 3. Make ONLY the changes the instruction asks for — nothing more, nothing less.
74
+ 4. If the instruction says "Replace X with Y", then X must become Y verbatim.
75
+ 5. Do NOT add, remove, or rewrite lines the instruction didn't mention.
76
+ 6. Do NOT summarize, shorten, paraphrase, or "improve" the requested text.
77
+ 7. The instruction text is LITERAL — reproduce it character-for-character.`,
66
78
  },
67
79
  {
68
80
  role: 'user',
@@ -76,7 +88,7 @@ ${file.content}
76
88
 
77
89
  Instruction: ${instruction}
78
90
 
79
- Return the complete modified code:`,
91
+ Return the complete modified file content:`,
80
92
  },
81
93
  ], options.model);
82
94
  spinner.stop();
@@ -148,12 +160,17 @@ Return the complete modified code:`,
148
160
  }
149
161
  }
150
162
  extractCode(response, language) {
151
- // Try to extract code block
163
+ // Try to extract code block (language-specific first, then any)
152
164
  const codeBlockRegex = new RegExp(`\`\`\`(?:${language})?\\n([\\s\\S]*?)\`\`\``, 'i');
153
165
  const match = response.match(codeBlockRegex);
154
166
  if (match) {
155
167
  return match[1].trim();
156
168
  }
169
+ // Try generic code block
170
+ const genericMatch = response.match(/```\w*\n([\s\S]*?)```/);
171
+ if (genericMatch) {
172
+ return genericMatch[1].trim();
173
+ }
157
174
  // If no code block, check if response looks like code
158
175
  const trimmed = response.trim();
159
176
  if (!trimmed.startsWith('```') && !trimmed.includes('Here') && !trimmed.includes('I ')) {
@@ -18,6 +18,11 @@ export declare class GenerateCommand {
18
18
  private fileUtils;
19
19
  constructor(config: Config, logger: Logger);
20
20
  run(description: string, options: GenerateOptions): Promise<void>;
21
+ /**
22
+ * Strip markdown code fences the model may wrap around the output.
23
+ * The CLI asks for raw code but some models still wrap in ```lang ... ```.
24
+ */
25
+ private stripMarkdownFences;
21
26
  private highlightCode;
22
27
  private highlightLine;
23
28
  private saveToFile;
@@ -81,13 +81,17 @@ class GenerateCommand {
81
81
  code = await this.api.generateCode(description, options.language, options.model);
82
82
  spinner.stop();
83
83
  }
84
- // Display generated code
85
- this.logger.section('Generated Code');
86
- console.log(chalk_1.default.gray('─'.repeat(60)));
87
- console.log(this.highlightCode(code, options.language));
88
- console.log(chalk_1.default.gray(''.repeat(60)));
89
- console.log();
90
- // Save options
84
+ // Strip any markdown fences the model may have wrapped around the code
85
+ code = this.stripMarkdownFences(code, options.language);
86
+ // Display generated code (skip display when --output is set to avoid noise)
87
+ if (!options.output) {
88
+ this.logger.section('Generated Code');
89
+ console.log(chalk_1.default.gray('─'.repeat(60)));
90
+ console.log(this.highlightCode(code, options.language));
91
+ console.log(chalk_1.default.gray('─'.repeat(60)));
92
+ console.log();
93
+ }
94
+ // Save options — when --output is specified, save directly (non-interactive)
91
95
  if (options.output) {
92
96
  await this.saveToFile(options.output, code);
93
97
  }
@@ -100,6 +104,25 @@ class GenerateCommand {
100
104
  this.logger.error('Generation failed:', error.message);
101
105
  }
102
106
  }
107
+ /**
108
+ * Strip markdown code fences the model may wrap around the output.
109
+ * The CLI asks for raw code but some models still wrap in ```lang ... ```.
110
+ */
111
+ stripMarkdownFences(code, language) {
112
+ const trimmed = code.trim();
113
+ // Try language-specific fence first
114
+ const langFenceRe = new RegExp(`^\`\`\`(?:${language})?\\s*\\n([\\s\\S]*?)\\n?\`\`\`\\s*$`, 'i');
115
+ const langMatch = trimmed.match(langFenceRe);
116
+ if (langMatch) {
117
+ return langMatch[1].trim();
118
+ }
119
+ // Try generic fence
120
+ const genericMatch = trimmed.match(/^```\w*\s*\n([\s\S]*?)\n?```\s*$/);
121
+ if (genericMatch) {
122
+ return genericMatch[1].trim();
123
+ }
124
+ return code;
125
+ }
103
126
  highlightCode(code, language) {
104
127
  // Basic syntax highlighting
105
128
  // In production, use a proper library like highlight.js
@@ -399,7 +399,8 @@ class RepoCommand {
399
399
  console.log(chalk_1.default.gray('\nTip: Use `vigthoria repo pull <name>` to restore this project anywhere.\n'));
400
400
  }
401
401
  catch (error) {
402
- spinner.fail('Push failed');
402
+ spinner.stop();
403
+ this.logger.error('Push failed');
403
404
  const errMsg = this.formatRepoError(error);
404
405
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
405
406
  }
@@ -486,7 +487,8 @@ class RepoCommand {
486
487
  console.log();
487
488
  }
488
489
  catch (error) {
489
- spinner.fail('Pull failed');
490
+ spinner.stop();
491
+ this.logger.error('Pull failed');
490
492
  const errMsg = this.formatRepoError(error);
491
493
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
492
494
  }
@@ -584,7 +586,8 @@ class RepoCommand {
584
586
  console.log(chalk_1.default.gray(' vigthoria repo share <name> - Share a project\n'));
585
587
  }
586
588
  catch (error) {
587
- spinner.fail('List failed');
589
+ spinner.stop();
590
+ this.logger.error('List failed');
588
591
  const errMsg = this.formatRepoError(error);
589
592
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
590
593
  }
@@ -634,7 +637,8 @@ class RepoCommand {
634
637
  }
635
638
  }
636
639
  catch (error) {
637
- spinner.fail('Status check failed');
640
+ spinner.stop();
641
+ this.logger.error('Status check failed');
638
642
  const errMsg = error instanceof Error ? error.message : 'Unknown error';
639
643
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
640
644
  }
@@ -666,7 +670,8 @@ class RepoCommand {
666
670
  console.log(chalk_1.default.gray('\n Anyone with this link can view/download the project.\n'));
667
671
  }
668
672
  catch (error) {
669
- spinner.fail('Share failed');
673
+ spinner.stop();
674
+ this.logger.error('Share failed');
670
675
  const errMsg = error instanceof Error ? error.message : 'Unknown error';
671
676
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
672
677
  }
@@ -700,7 +705,8 @@ class RepoCommand {
700
705
  console.log(chalk_1.default.gray('\nNote: Your local files are not affected.\n'));
701
706
  }
702
707
  catch (error) {
703
- spinner.fail('Delete failed');
708
+ spinner.stop();
709
+ this.logger.error('Delete failed');
704
710
  const errMsg = error instanceof Error ? error.message : 'Unknown error';
705
711
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
706
712
  }
@@ -728,7 +734,8 @@ class RepoCommand {
728
734
  await this.pull(data.project.project_name, options);
729
735
  }
730
736
  catch (error) {
731
- spinner.fail('Clone failed');
737
+ spinner.stop();
738
+ this.logger.error('Clone failed');
732
739
  const errMsg = error instanceof Error ? error.message : 'Unknown error';
733
740
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
734
741
  }
package/dist/index.js CHANGED
@@ -98,7 +98,7 @@ function getVersion() {
98
98
  catch (e) {
99
99
  // Fallback to hardcoded version
100
100
  }
101
- return '1.6.12';
101
+ return '1.6.22';
102
102
  }
103
103
  const VERSION = getVersion();
104
104
  /**
@@ -204,6 +204,7 @@ async function main() {
204
204
  .option('-w, --workflow <selector>', 'Route prompts through a named or explicit VigFlow workflow target')
205
205
  .option('--json', 'Emit machine-readable JSON output for direct prompt runs', false)
206
206
  .option('--auto-approve', 'Auto-approve agent actions (dangerous!)', false)
207
+ .option('--bridge <url>', 'Connect to Vigthoria Commando Bridge for remote admin observability')
207
208
  .action(async (options) => {
208
209
  const chat = new chat_js_1.ChatCommand(config, logger);
209
210
  await chat.run({
@@ -217,6 +218,7 @@ async function main() {
217
218
  autoApprove: options.autoApprove,
218
219
  resume: options.resume,
219
220
  prompt: options.prompt,
221
+ bridge: options.bridge,
220
222
  });
221
223
  });
222
224
  program
@@ -231,6 +233,7 @@ async function main() {
231
233
  .option('-w, --workflow <selector>', 'Route prompts through a named or explicit VigFlow workflow target')
232
234
  .option('--json', 'Emit machine-readable JSON output for direct prompt runs', false)
233
235
  .option('--auto-approve', 'Auto-approve agent actions (dangerous!)', false)
236
+ .option('--bridge <url>', 'Connect to Vigthoria Commando Bridge for remote admin observability')
234
237
  .action(async (options) => {
235
238
  const chat = new chat_js_1.ChatCommand(config, logger);
236
239
  await chat.run({
@@ -244,6 +247,7 @@ async function main() {
244
247
  autoApprove: options.autoApprove,
245
248
  resume: true,
246
249
  prompt: options.prompt,
250
+ bridge: options.bridge,
247
251
  });
248
252
  });
249
253
  // Agent command - Agentic mode (Vigthoria Autonomous)
@@ -259,6 +263,7 @@ async function main() {
259
263
  .option('-w, --workflow <selector>', 'Run the prompt through a named or explicit VigFlow workflow target')
260
264
  .option('--json', 'Emit machine-readable JSON output for direct prompt runs', false)
261
265
  .option('--auto-approve', 'Auto-approve all actions (dangerous!)', false)
266
+ .option('--bridge <url>', 'Connect to Vigthoria Commando Bridge for remote admin observability')
262
267
  .action(async (options) => {
263
268
  const chat = new chat_js_1.ChatCommand(config, logger);
264
269
  await chat.run({
@@ -272,6 +277,7 @@ async function main() {
272
277
  json: options.json,
273
278
  autoApprove: options.autoApprove,
274
279
  prompt: options.prompt,
280
+ bridge: options.bridge,
275
281
  });
276
282
  });
277
283
  program
@@ -285,6 +291,7 @@ async function main() {
285
291
  .option('-w, --workflow <selector>', 'Run the prompt through a named or explicit VigFlow workflow target')
286
292
  .option('--save-plan', 'Save the completed BMAD plan into VigFlow for rerun and audit', false)
287
293
  .option('--json', 'Emit machine-readable JSON output for direct prompt runs', false)
294
+ .option('--bridge <url>', 'Connect to Vigthoria Commando Bridge for remote admin observability')
288
295
  .action(async (options) => {
289
296
  const chat = new chat_js_1.ChatCommand(config, logger);
290
297
  await chat.run({
@@ -298,6 +305,7 @@ async function main() {
298
305
  savePlan: options.savePlan,
299
306
  json: options.json,
300
307
  prompt: options.prompt,
308
+ bridge: options.bridge,
301
309
  });
302
310
  });
303
311
  program