vigthoria-cli 1.6.35 → 1.6.37

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.
@@ -27,7 +27,7 @@ class AuthCommand {
27
27
  return;
28
28
  }
29
29
  console.log();
30
- console.log(chalk_1.default.cyan('═══ Vigthoria Login ═══'));
30
+ console.log(chalk_1.default.cyan(`${logger_js_1.CH.hDouble.repeat(3)} Vigthoria Login ${logger_js_1.CH.hDouble.repeat(3)}`));
31
31
  console.log();
32
32
  // Prompt for credentials
33
33
  const answers = await inquirer_1.default.prompt([
@@ -132,7 +132,7 @@ class AuthCommand {
132
132
  }
133
133
  async status() {
134
134
  console.log();
135
- console.log(chalk_1.default.cyan('═══ Account Status ═══'));
135
+ console.log(chalk_1.default.cyan(`${logger_js_1.CH.hDouble.repeat(3)} Account Status ${logger_js_1.CH.hDouble.repeat(3)}`));
136
136
  console.log();
137
137
  if (!this.config.isAuthenticated()) {
138
138
  this.logger.warn('Not logged in');
@@ -166,16 +166,34 @@ class AuthCommand {
166
166
  console.log(chalk_1.default.white('Available Models:'));
167
167
  const models = this.config.getAvailableModels();
168
168
  models.forEach(m => {
169
- console.log(chalk_1.default.gray(' ') + chalk_1.default.cyan(m.id) + chalk_1.default.gray(' ') + chalk_1.default.white(m.name));
169
+ console.log(chalk_1.default.gray(` ${logger_js_1.CH.bullet} `) + chalk_1.default.cyan(m.id) + chalk_1.default.gray(' -> ') + chalk_1.default.white(m.name));
170
170
  const runtimeLabel = m.tier === 'cloud' ? 'cloud' : 'blackwell';
171
- console.log(chalk_1.default.gray(` ${m.tier === 'cloud' ? '☁️ ' : '🏠 '} ${m.description}`));
171
+ console.log(chalk_1.default.gray(` ${m.tier === 'cloud' ? logger_js_1.CH.cloud : logger_js_1.CH.home} ${m.description}`));
172
172
  console.log(chalk_1.default.gray(` Backend: ${m.backendModel} (${runtimeLabel})`));
173
173
  });
174
174
  console.log();
175
- // API status
176
- const spinner = (0, logger_js_1.createSpinner)('Checking API status...').start();
177
- const apiStatus = await this.api.getHealthStatus();
175
+ // Run ALL three probes in parallel — they are independent.
176
+ const spinner = (0, logger_js_1.createSpinner)('Checking API, capabilities and auth...').start();
177
+ const [apiStatus, capabilityStatus, tokenValidation] = await Promise.all([
178
+ this.api.getHealthStatus(),
179
+ Promise.race([
180
+ this.api.getCapabilityTruthStatus({
181
+ workspacePath: process.cwd(),
182
+ projectPath: process.cwd(),
183
+ targetPath: process.cwd(),
184
+ }),
185
+ new Promise(resolve => setTimeout(() => resolve({
186
+ overallOk: false,
187
+ v3Agent: { name: 'V3 Agent', endpoint: '', ok: false, error: 'Timed out (8s)' },
188
+ hyperLoop: { name: 'Hyper Loop', endpoint: '', ok: false, error: 'Timed out (8s)' },
189
+ repoMemory: { name: 'Repo Memory', endpoint: '', ok: false, error: 'Timed out (8s)' },
190
+ devtoolsBridge: { name: 'DevTools Bridge', endpoint: '', ok: false, error: 'Timed out (8s)' },
191
+ }), 8000)),
192
+ ]),
193
+ this.api.validateToken(),
194
+ ]);
178
195
  spinner.stop();
196
+ // --- Display API Status ---
179
197
  console.log(chalk_1.default.white('API Status:'));
180
198
  console.log(chalk_1.default.gray(' Overall: ') + (apiStatus.overallOk ? chalk_1.default.green('Healthy') : chalk_1.default.yellow('Degraded')));
181
199
  console.log(chalk_1.default.gray(' Coder API: ') + (apiStatus.coder.ok ? chalk_1.default.green('Online') : chalk_1.default.red('Offline')) + chalk_1.default.gray(` (${apiStatus.coder.endpoint})`));
@@ -198,30 +216,14 @@ class AuthCommand {
198
216
  else {
199
217
  console.log(chalk_1.default.gray(' Self-hosted Models: ') + chalk_1.default.gray('disabled'));
200
218
  }
201
- const capabilitySpinner = (0, logger_js_1.createSpinner)('Checking live capability truth...').start();
202
- const capabilityStatus = await Promise.race([
203
- this.api.getCapabilityTruthStatus({
204
- workspacePath: process.cwd(),
205
- projectPath: process.cwd(),
206
- targetPath: process.cwd(),
207
- }),
208
- new Promise(resolve => setTimeout(() => resolve({
209
- overallOk: false,
210
- v3Agent: { name: 'V3 Agent', endpoint: '', ok: false, error: 'Timed out (8s)' },
211
- hyperLoop: { name: 'Hyper Loop', endpoint: '', ok: false, error: 'Timed out (8s)' },
212
- repoMemory: { name: 'Repo Memory', endpoint: '', ok: false, error: 'Timed out (8s)' },
213
- devtoolsBridge: { name: 'DevTools Bridge', endpoint: '', ok: false, error: 'Timed out (8s)' },
214
- }), 8000)),
215
- ]);
216
- capabilitySpinner.stop();
219
+ // --- Display Capability Truth ---
217
220
  console.log();
218
221
  console.log(chalk_1.default.white('Capability Truth:'));
219
- console.log(chalk_1.default.gray(' Overall: ') + (capabilityStatus.overallOk ? chalk_1.default.green('Verified') : chalk_1.default.yellow('Partial')));
222
+ const coreOk = apiStatus.overallOk;
223
+ console.log(chalk_1.default.gray(' Overall: ') + (coreOk ? chalk_1.default.green('Verified') : chalk_1.default.yellow('Partial')));
220
224
  // V3 Agent — used by agent/chat commands (routed through model API)
221
225
  console.log(chalk_1.default.gray(' V3 Agent: ') + (capabilityStatus.v3Agent.ok ? chalk_1.default.green('Reachable') : chalk_1.default.gray('Not deployed (routed through model API)')));
222
226
  if (capabilityStatus.v3Agent.error && capabilityStatus.v3Agent.ok === false) {
223
- // Only show the raw error when it's an unexpected failure, not a
224
- // simple timeout/unreachable which is already covered by the label.
225
227
  const err = capabilityStatus.v3Agent.error;
226
228
  if (!/timed? out|not reachable|No V3 agent/i.test(err)) {
227
229
  console.log(chalk_1.default.gray(' Error: ') + chalk_1.default.red(err));
@@ -236,8 +238,7 @@ class AuthCommand {
236
238
  }
237
239
  // DevTools Bridge — local service, not required
238
240
  console.log(chalk_1.default.gray(' DevTools Bridge: ') + (capabilityStatus.devtoolsBridge.ok ? chalk_1.default.green('Connected') : chalk_1.default.gray('Not running (optional)')));
239
- // Auth scope summary use a real server-side probe for Model Auth
240
- const tokenValidation = await this.api.validateToken();
241
+ // --- Display Auth Scopes ---
241
242
  console.log();
242
243
  console.log(chalk_1.default.white('Auth Scopes:'));
243
244
  console.log(chalk_1.default.gray(' Model Auth: ') + (tokenValidation.valid ? chalk_1.default.green('Valid') : chalk_1.default.red('Invalid')) + chalk_1.default.gray(' (used by chat, agent, review, explain, generate, fix)'));
@@ -247,6 +248,9 @@ class AuthCommand {
247
248
  console.log(chalk_1.default.gray(' Repo Auth: ') + (capabilityStatus.repoMemory.ok ? chalk_1.default.green('Active') : chalk_1.default.gray('N/A — repo memory not deployed')) + chalk_1.default.gray(' (used by repo push/pull/list only)'));
248
249
  console.log(chalk_1.default.gray(' Bridge Auth: ') + (capabilityStatus.devtoolsBridge.ok ? chalk_1.default.green('Connected') : chalk_1.default.gray('N/A — bridge not running')) + chalk_1.default.gray(' (used by --bridge flag only)'));
249
250
  console.log();
251
+ // Force clean exit — lingering connections (bridge WebSocket,
252
+ // axios keep-alive) would otherwise keep the process alive.
253
+ process.exit(0);
250
254
  }
251
255
  printLoginSuccess() {
252
256
  const email = this.config.get('email');
@@ -1804,11 +1804,16 @@ class ChatCommand {
1804
1804
  }
1805
1805
  extractToolCalls(message) {
1806
1806
  const calls = [];
1807
+ const seen = new Set();
1807
1808
  const wrapperRegex = /<tool_call>([\s\S]*?)<\/tool_call>/g;
1808
1809
  for (const match of message.matchAll(wrapperRegex)) {
1809
1810
  const call = this.parseToolPayload(match[1] || '');
1810
1811
  if (call) {
1811
- calls.push(call);
1812
+ const key = `${call.tool}::${JSON.stringify(call.args)}`;
1813
+ if (!seen.has(key)) {
1814
+ seen.add(key);
1815
+ calls.push(call);
1816
+ }
1812
1817
  }
1813
1818
  }
1814
1819
  return calls;
@@ -44,6 +44,7 @@ const chalk_1 = __importDefault(require("chalk"));
44
44
  const inquirer_1 = __importDefault(require("inquirer"));
45
45
  const fs = __importStar(require("fs"));
46
46
  const path = __importStar(require("path"));
47
+ const logger_js_1 = require("../utils/logger.js");
47
48
  class ConfigCommand {
48
49
  config;
49
50
  logger;
@@ -73,7 +74,7 @@ class ConfigCommand {
73
74
  }
74
75
  async init() {
75
76
  console.log();
76
- console.log(chalk_1.default.cyan('═══ Initialize Vigthoria in Project ═══'));
77
+ console.log(chalk_1.default.cyan(`${logger_js_1.CH.hDouble.repeat(3)} Initialize Vigthoria in Project ${logger_js_1.CH.hDouble.repeat(3)}`));
77
78
  console.log();
78
79
  const cwd = process.cwd();
79
80
  const configFile = path.join(cwd, '.vigthoria.json');
@@ -182,7 +182,7 @@ class DeployCommand {
182
182
  if (!response.ok || !data.success) {
183
183
  if (data.requiresSubscription) {
184
184
  spinner.stop();
185
- console.log(chalk_1.default.yellow('\n⚠️ Subdomain hosting requires a subscription (€4.99/mo)'));
185
+ console.log(chalk_1.default.yellow(`\n${logger_js_1.CH.warnEmoji} Subdomain hosting requires a subscription (€4.99/mo)`));
186
186
  const { proceed } = await inquirer_1.default.prompt([{
187
187
  type: 'confirm',
188
188
  name: 'proceed',
@@ -232,7 +232,7 @@ class DeployCommand {
232
232
  if (!response.ok || !data.success) {
233
233
  if (data.requiresSubscription) {
234
234
  spinner.stop();
235
- console.log(chalk_1.default.yellow('\n⚠️ Custom domain hosting requires a subscription (€9.99/mo)'));
235
+ console.log(chalk_1.default.yellow(`\n${logger_js_1.CH.warnEmoji} Custom domain hosting requires a subscription (€9.99/mo)`));
236
236
  const { proceed } = await inquirer_1.default.prompt([{
237
237
  type: 'confirm',
238
238
  name: 'proceed',
@@ -366,7 +366,7 @@ class DeployCommand {
366
366
  console.log(chalk_1.default.cyan(`\n🌐 Your Deployments (${data.domains.length})\n`));
367
367
  for (const domain of data.domains) {
368
368
  const statusIcon = domain.is_active ? '🟢' : '🔴';
369
- const sslIcon = domain.ssl_status === 'active' ? '🔒' : '⚠️';
369
+ const sslIcon = domain.ssl_status === 'active' ? logger_js_1.CH.lock : logger_js_1.CH.warnEmoji;
370
370
  const url = domain.domain_type === 'subdomain'
371
371
  ? `${domain.subdomain}.vigthoria.io`
372
372
  : domain.domain_type === 'custom'
@@ -458,7 +458,7 @@ class DeployCommand {
458
458
  default: false
459
459
  }]);
460
460
  if (!confirm) {
461
- console.log(chalk_1.default.yellow('\n⚠️ Removal cancelled.\n'));
461
+ console.log(chalk_1.default.yellow(`\n${logger_js_1.CH.warnEmoji} Removal cancelled.\n`));
462
462
  return;
463
463
  }
464
464
  const spinner = (0, logger_js_1.createSpinner)(`Removing ${domain}...`).start();
@@ -43,7 +43,7 @@ class GenerateCommand {
43
43
  if (!options.language) {
44
44
  options.language = this.detectLanguageFromDescription(description);
45
45
  }
46
- this.logger.section(proMode ? '🚀 Senior Developer Mode' : 'Code Generation');
46
+ this.logger.section(proMode ? `${logger_js_1.CH.rocket} Senior Developer Mode` : 'Code Generation');
47
47
  console.log(chalk_1.default.gray(`Language: ${options.language}`));
48
48
  console.log(chalk_1.default.gray(`Description: ${description}`));
49
49
  if (proMode) {
@@ -191,7 +191,10 @@ class RepoCommand {
191
191
  formatRepoError(error) {
192
192
  const message = error instanceof Error ? error.message : 'Unknown error';
193
193
  if (/invalid or expired session/i.test(message)) {
194
- return `${message}. Repo commands currently require a valid Vigthoria Community session. Provide VIGTHORIA_COMMUNITY_EMAIL and VIGTHORIA_COMMUNITY_PASSWORD for live Community repo operations.`;
194
+ return 'Repo memory service is not deployed or not reachable. Check `vigthoria status` for details.';
195
+ }
196
+ if (/ECONNREFUSED|ENOTFOUND|ETIMEDOUT|socket hang up/i.test(message)) {
197
+ return 'Repo memory service is not reachable. Check `vigthoria status` for details.';
195
198
  }
196
199
  return message;
197
200
  }
@@ -360,7 +363,7 @@ class RepoCommand {
360
363
  }
361
364
  ]);
362
365
  if (!answers.confirm) {
363
- console.log(chalk_1.default.yellow('\n⚠️ Push cancelled.\n'));
366
+ console.log(chalk_1.default.yellow(`\n${logger_js_1.CH.warnEmoji} Push cancelled.\n`));
364
367
  return;
365
368
  }
366
369
  const uploadSpinner = (0, logger_js_1.createSpinner)('Preparing project files...').start();
@@ -434,7 +437,7 @@ class RepoCommand {
434
437
  default: false
435
438
  }]);
436
439
  if (!overwrite) {
437
- console.log(chalk_1.default.yellow('\n⚠️ Pull cancelled.\n'));
440
+ console.log(chalk_1.default.yellow(`\n${logger_js_1.CH.warnEmoji} Pull cancelled.\n`));
438
441
  return;
439
442
  }
440
443
  }
@@ -523,7 +526,7 @@ class RepoCommand {
523
526
  // Display owned projects grouped by visibility
524
527
  if (ownedProjects.length > 0) {
525
528
  console.log(chalk_1.default.bold.cyan(' 📁 YOUR PROJECTS'));
526
- console.log(chalk_1.default.gray(' ' + '─'.repeat(50)));
529
+ console.log(chalk_1.default.gray(' ' + logger_js_1.CH.hLine.repeat(50)));
527
530
  // Group by visibility
528
531
  const grouped = ownedProjects.reduce((acc, project) => {
529
532
  const vis = project.visibility || 'private';
@@ -548,8 +551,8 @@ class RepoCommand {
548
551
  const techStack = project.tech_stack || project.framework || 'Unknown';
549
552
  const updatedAt = project.updated_at || project.updatedAt || project.created_at || project.createdAt;
550
553
  const syncStatus = (project.last_synced_at || project.lastSyncedAt)
551
- ? chalk_1.default.green('✓ synced')
552
- : chalk_1.default.yellow(' not synced');
554
+ ? chalk_1.default.green(`${logger_js_1.CH.success} synced`)
555
+ : chalk_1.default.yellow('- not synced');
553
556
  console.log(chalk_1.default.white(` ${projectName.padEnd(30)} ${syncStatus}`));
554
557
  if (project.description) {
555
558
  console.log(chalk_1.default.gray(` ${project.description.substring(0, 50)}${project.description.length > 50 ? '...' : ''}`));
@@ -563,7 +566,7 @@ class RepoCommand {
563
566
  // Display shared projects
564
567
  if (sharedProjects.length > 0) {
565
568
  console.log(chalk_1.default.bold.magenta('\n 🤝 SHARED WITH YOU'));
566
- console.log(chalk_1.default.gray(' ' + '─'.repeat(50)));
569
+ console.log(chalk_1.default.gray(' ' + logger_js_1.CH.hLine.repeat(50)));
567
570
  sharedProjects.forEach((project) => {
568
571
  const projectName = project.project_name || project.name || 'Unnamed';
569
572
  const techStack = project.tech_stack || project.framework || 'Unknown';
@@ -578,7 +581,7 @@ class RepoCommand {
578
581
  console.log();
579
582
  });
580
583
  }
581
- console.log(chalk_1.default.gray('─'.repeat(60)));
584
+ console.log(chalk_1.default.gray(logger_js_1.CH.hLine.repeat(60)));
582
585
  console.log(chalk_1.default.cyan('\nCommands:'));
583
586
  console.log(chalk_1.default.gray(' vigthoria repo pull <name> - Download a project'));
584
587
  console.log(chalk_1.default.gray(' vigthoria repo push - Upload current directory'));
@@ -617,7 +620,7 @@ class RepoCommand {
617
620
  synced: '✅',
618
621
  ahead: '⬆️',
619
622
  behind: '⬇️',
620
- diverged: '⚠️'
623
+ diverged: logger_js_1.CH.warnEmoji
621
624
  };
622
625
  console.log(chalk_1.default.cyan(`\n📦 Project: ${chalk_1.default.white(data.project.project_name)}\n`));
623
626
  console.log(chalk_1.default.gray(` Path: ${projectPath}`));
@@ -688,7 +691,7 @@ class RepoCommand {
688
691
  default: false
689
692
  }]);
690
693
  if (!confirm) {
691
- console.log(chalk_1.default.yellow('\n⚠️ Delete cancelled.\n'));
694
+ console.log(chalk_1.default.yellow(`\n${logger_js_1.CH.warnEmoji} Delete cancelled.\n`));
692
695
  return;
693
696
  }
694
697
  const spinner = (0, logger_js_1.createSpinner)('Deleting project...').start();
@@ -134,13 +134,13 @@ class ReviewCommand {
134
134
  getSeverityIcon(severity) {
135
135
  switch (severity.toLowerCase()) {
136
136
  case 'error':
137
- return '✗';
137
+ return logger_js_1.CH.error;
138
138
  case 'warning':
139
- return '⚠';
139
+ return logger_js_1.CH.warn;
140
140
  case 'info':
141
- return 'ℹ';
141
+ return logger_js_1.CH.info;
142
142
  default:
143
- return '•';
143
+ return logger_js_1.CH.bullet;
144
144
  }
145
145
  }
146
146
  getSeverityColor(severity) {
@@ -56,7 +56,10 @@ class WorkflowCommand {
56
56
  });
57
57
  }
58
58
  catch (error) {
59
- const msg = `Workflow service is not reachable: ${error.message}`;
59
+ const raw = error.message || 'Unknown error';
60
+ const msg = /not available|not reachable|not deployed/i.test(raw)
61
+ ? raw
62
+ : `Workflow service is not reachable: ${raw}`;
60
63
  if (options.json) {
61
64
  this.printJson({ success: false, error: msg, templates: [] });
62
65
  }
@@ -88,7 +91,10 @@ class WorkflowCommand {
88
91
  workflows = await this.api.listVigFlowWorkflows();
89
92
  }
90
93
  catch (error) {
91
- const msg = `Workflow service is not reachable: ${error.message}`;
94
+ const raw = error.message || 'Unknown error';
95
+ const msg = /not available|not reachable|not deployed/i.test(raw)
96
+ ? raw
97
+ : `Workflow service is not reachable: ${raw}`;
92
98
  if (options.json) {
93
99
  this.printJson({ success: false, error: msg, workflows: [] });
94
100
  }
package/dist/index.js CHANGED
@@ -133,7 +133,7 @@ async function checkForUpdatesQuietly() {
133
133
  if (npmVersion && compareVersions(npmVersion, VERSION) > 0) {
134
134
  // Check if this is a security update (1.4.0+)
135
135
  if (compareVersions(npmVersion, '1.4.0') >= 0 && compareVersions(VERSION, '1.4.0') < 0) {
136
- console.log(chalk_1.default.red.bold('\n⚠️ SECURITY UPDATE AVAILABLE'));
136
+ console.log(chalk_1.default.red.bold(`\n${logger_js_1.CH.warnEmoji} SECURITY UPDATE AVAILABLE`));
137
137
  console.log(chalk_1.default.red(` Version ${VERSION} has security vulnerabilities.`));
138
138
  console.log(chalk_1.default.yellow(` Please update to ${npmVersion} immediately:`));
139
139
  console.log(chalk_1.default.white.bold(' npm install -g vigthoria-cli@latest\n'));
@@ -173,12 +173,12 @@ async function main() {
173
173
  const titlePad = Math.floor((boxWidth - 4 - titleText.length) / 2);
174
174
  const versionPad = Math.floor((boxWidth - 4 - versionText.length) / 2);
175
175
  if (process.env.VIGTHORIA_NO_BANNER !== '1' && !jsonOutputRequested) {
176
- console.log(chalk_1.default.cyan('╔' + '═'.repeat(boxWidth) + '╗'));
177
- console.log(chalk_1.default.cyan('║' + ' '.repeat(boxWidth) + '║'));
178
- console.log(chalk_1.default.cyan('║') + ' '.repeat(titlePad) + chalk_1.default.bold.white('VIGTHORIA CLI') + chalk_1.default.cyan(' - AI-Powered Coding Assistant') + ' '.repeat(boxWidth - titlePad - titleText.length) + chalk_1.default.cyan('║'));
179
- console.log(chalk_1.default.cyan('║') + ' '.repeat(versionPad) + chalk_1.default.gray(versionText) + ' '.repeat(boxWidth - versionPad - versionText.length) + chalk_1.default.cyan('║'));
180
- console.log(chalk_1.default.cyan('║' + ' '.repeat(boxWidth) + '║'));
181
- console.log(chalk_1.default.cyan('╚' + '═'.repeat(boxWidth) + '╝'));
176
+ console.log(chalk_1.default.cyan(logger_js_1.CH.dTl + logger_js_1.CH.dH.repeat(boxWidth) + logger_js_1.CH.dTr));
177
+ console.log(chalk_1.default.cyan(logger_js_1.CH.dV + ' '.repeat(boxWidth) + logger_js_1.CH.dV));
178
+ console.log(chalk_1.default.cyan(logger_js_1.CH.dV) + ' '.repeat(titlePad) + chalk_1.default.bold.white('VIGTHORIA CLI') + chalk_1.default.cyan(' - AI-Powered Coding Assistant') + ' '.repeat(boxWidth - titlePad - titleText.length) + chalk_1.default.cyan(logger_js_1.CH.dV));
179
+ console.log(chalk_1.default.cyan(logger_js_1.CH.dV) + ' '.repeat(versionPad) + chalk_1.default.gray(versionText) + ' '.repeat(boxWidth - versionPad - versionText.length) + chalk_1.default.cyan(logger_js_1.CH.dV));
180
+ console.log(chalk_1.default.cyan(logger_js_1.CH.dV + ' '.repeat(boxWidth) + logger_js_1.CH.dV));
181
+ console.log(chalk_1.default.cyan(logger_js_1.CH.dBl + logger_js_1.CH.dH.repeat(boxWidth) + logger_js_1.CH.dBr));
182
182
  console.log();
183
183
  }
184
184
  // Check for updates in background (don't wait)
package/dist/utils/api.js CHANGED
@@ -352,36 +352,31 @@ class APIClient {
352
352
  if (!token) {
353
353
  return { valid: false, error: 'No auth token configured. Run: vigthoria login' };
354
354
  }
355
- // Verify auth against the Model Router (api.vigthoria.io) which is
356
- // the backend all AI commands actually use. Falls back to the Coder
357
- // profile endpoint when the Model Router is unreachable so that
358
- // offline/degraded scenarios don't block the user.
359
- try {
360
- await this.modelRouterClient.get('/v1/models', { timeout: 10000 });
361
- return { valid: true };
362
- }
363
- catch (mrError) {
364
- const mrAxErr = mrError;
365
- if (mrAxErr.response?.status === 401 || mrAxErr.response?.status === 403) {
366
- return { valid: false, error: 'Auth token expired or invalid. Run: vigthoria login' };
367
- }
368
- // Model Router unreachable — try Coder profile as fallback
369
- try {
370
- await this.client.get('/api/user/profile', { timeout: 10000 });
355
+ // Probe both endpoints in parallel. If EITHER succeeds the token is
356
+ // valid. Only if both return 401/403 is the token truly invalid.
357
+ // If both are unreachable assume the token is fine (offline scenario).
358
+ const results = await Promise.allSettled([
359
+ this.modelRouterClient.get('/v1/models', { timeout: 5000 }),
360
+ this.client.get('/api/user/profile', { timeout: 5000 }),
361
+ ]);
362
+ for (const r of results) {
363
+ if (r.status === 'fulfilled')
371
364
  return { valid: true };
372
- }
373
- catch (error) {
374
- if (error instanceof CLIError && error.category === 'auth') {
365
+ }
366
+ // Both failed — check why
367
+ for (const r of results) {
368
+ if (r.status === 'rejected') {
369
+ const err = r.reason;
370
+ if (err.response?.status === 401 || err.response?.status === 403) {
375
371
  return { valid: false, error: 'Auth token expired or invalid. Run: vigthoria login' };
376
372
  }
377
- const axErr = error;
378
- if (axErr.response?.status === 401 || axErr.response?.status === 403) {
373
+ if (err instanceof CLIError && err.category === 'auth') {
379
374
  return { valid: false, error: 'Auth token expired or invalid. Run: vigthoria login' };
380
375
  }
381
- // Both unreachable — don't assume token is bad
382
- return { valid: true };
383
376
  }
384
377
  }
378
+ // Both unreachable — don't assume token is bad
379
+ return { valid: true };
385
380
  }
386
381
  getV3AgentBaseUrls(preferLocal = false) {
387
382
  const configuredApiUrl = String(this.config.get('apiUrl') || 'https://coder.vigthoria.io').replace(/\/$/, '');
@@ -922,7 +917,8 @@ class APIClient {
922
917
  this.logger.debug(`VigFlow ${operation} via ${baseUrl} failed:`, lastError.message);
923
918
  }
924
919
  }
925
- throw lastError || new Error(`No VigFlow backend available for ${operation}.`);
920
+ // Throw a clean message instead of the raw ECONNREFUSED from the last URL tried
921
+ throw new Error(`No VigFlow backend available for ${operation}. The workflow service is not deployed or not reachable.`);
926
922
  }
927
923
  /**
928
924
  * Build the correct sub-path for VigFlow endpoints.
@@ -3631,7 +3627,17 @@ document.addEventListener('DOMContentLoaded', () => {
3631
3627
  }
3632
3628
  }
3633
3629
  async explainCode(code, language) {
3634
- const sysPrompt = `You are a code explainer. Explain the following ${language} code clearly and concisely. Focus on what it does, how it works, and any notable patterns or potential issues.`;
3630
+ const sysPrompt = [
3631
+ `You are a code explainer. Explain the following ${language} code clearly and concisely.`,
3632
+ 'Focus on what it does, how it works, and any notable patterns or potential issues.',
3633
+ 'Format your response as clean Markdown:',
3634
+ '- Use ## headers for major sections.',
3635
+ '- Use numbered lists for sequential steps.',
3636
+ '- Use bullet points for properties or details.',
3637
+ '- Wrap code references in backticks.',
3638
+ '- Keep paragraphs short (2-3 sentences max).',
3639
+ '- Do NOT use raw HTML or excessive blank lines.',
3640
+ ].join('\n');
3635
3641
  return this.chatComplete(sysPrompt, code);
3636
3642
  }
3637
3643
  async reviewCode(code, language) {
@@ -4244,7 +4250,7 @@ document.addEventListener('DOMContentLoaded', () => {
4244
4250
  }
4245
4251
  async getCoderHealth() {
4246
4252
  try {
4247
- const response = await this.client.get('/api/health', { timeout: 10000 });
4253
+ const response = await this.client.get('/api/health', { timeout: 5000 });
4248
4254
  const ok = response.data?.status === 'ok' || response.data?.healthy === true;
4249
4255
  return {
4250
4256
  name: 'Coder API',
@@ -4266,8 +4272,8 @@ document.addEventListener('DOMContentLoaded', () => {
4266
4272
  const modelsApiUrl = this.config.get('modelsApiUrl');
4267
4273
  try {
4268
4274
  const [healthResponse, modelsResponse] = await Promise.all([
4269
- this.modelRouterClient.get('/health', { timeout: 10000 }),
4270
- this.modelRouterClient.get('/v1/models', { timeout: 15000 }),
4275
+ this.modelRouterClient.get('/health', { timeout: 5000 }),
4276
+ this.modelRouterClient.get('/v1/models', { timeout: 5000 }),
4271
4277
  ]);
4272
4278
  const healthOk = healthResponse.data?.status === 'healthy'
4273
4279
  || healthResponse.data?.status === 'ok'
@@ -4298,7 +4304,7 @@ document.addEventListener('DOMContentLoaded', () => {
4298
4304
  return null;
4299
4305
  }
4300
4306
  try {
4301
- const response = await this.selfHostedModelRouterClient.get('/health', { timeout: 10000 });
4307
+ const response = await this.selfHostedModelRouterClient.get('/health', { timeout: 5000 });
4302
4308
  const ok = response.data?.status === 'healthy'
4303
4309
  || response.data?.status === 'ok'
4304
4310
  || response.data?.healthy === true;
@@ -54,6 +54,7 @@ exports.getBridgeClient = getBridgeClient;
54
54
  const ws_1 = __importDefault(require("ws"));
55
55
  const os = __importStar(require("os"));
56
56
  const chalk_1 = __importDefault(require("chalk"));
57
+ const logger_js_1 = require("./logger.js");
57
58
  // ── Singleton accessor ───────────────────────────────────────────────
58
59
  let _instance = null;
59
60
  /** Get the active bridge client (may be null if --bridge was not used). */
@@ -136,7 +137,7 @@ class BridgeClient {
136
137
  this.stopHeartbeat();
137
138
  this.scheduleReconnect();
138
139
  if (process.env.DEBUG || process.env.VIGTHORIA_DEBUG) {
139
- console.log(chalk_1.default.yellow('⚠ Bridge: connection failed, will retry in background.'));
140
+ console.log(chalk_1.default.yellow(`${logger_js_1.CH.warn} Bridge: connection failed, will retry in background.`));
140
141
  }
141
142
  resolve(); // resolve even on failure – must never block CLI
142
143
  });
@@ -3,6 +3,36 @@
3
3
  */
4
4
  import { type Options as OraOptions, type Ora } from 'ora';
5
5
  export type { Ora };
6
+ export declare const CH: {
7
+ info: string;
8
+ warn: string;
9
+ error: string;
10
+ success: string;
11
+ ai: string;
12
+ user: string;
13
+ hLine: string;
14
+ hDouble: string;
15
+ vLine: string;
16
+ tl: string;
17
+ tr: string;
18
+ bl: string;
19
+ br: string;
20
+ teeR: string;
21
+ teeL: string;
22
+ cross: string;
23
+ bullet: string;
24
+ cloud: string;
25
+ home: string;
26
+ rocket: string;
27
+ lock: string;
28
+ warnEmoji: string;
29
+ dTl: string;
30
+ dTr: string;
31
+ dBl: string;
32
+ dBr: string;
33
+ dH: string;
34
+ dV: string;
35
+ };
6
36
  /**
7
37
  * Create an ora spinner that writes to stderr so it never
8
38
  * pollutes stdout JSON output.
@@ -6,10 +6,47 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
6
6
  return (mod && mod.__esModule) ? mod : { "default": mod };
7
7
  };
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.Logger = void 0;
9
+ exports.Logger = exports.CH = void 0;
10
10
  exports.createSpinner = createSpinner;
11
11
  const chalk_1 = __importDefault(require("chalk"));
12
12
  const ora_1 = __importDefault(require("ora"));
13
+ /**
14
+ * Platform-aware character map. Windows cmd.exe / PowerShell with legacy
15
+ * code pages (437/850) cannot render Unicode box-drawing or emoji, so we
16
+ * fall back to ASCII equivalents.
17
+ */
18
+ const isWin = process.platform === 'win32';
19
+ exports.CH = {
20
+ info: isWin ? 'i' : 'ℹ',
21
+ warn: isWin ? '!' : '⚠',
22
+ error: isWin ? 'x' : '✗',
23
+ success: isWin ? '+' : '✓',
24
+ ai: isWin ? '>' : '🤖',
25
+ user: isWin ? '$' : '👤',
26
+ hLine: isWin ? '-' : '─',
27
+ hDouble: isWin ? '=' : '═',
28
+ vLine: isWin ? '|' : '│',
29
+ tl: isWin ? '+' : '┌',
30
+ tr: isWin ? '+' : '┐',
31
+ bl: isWin ? '+' : '└',
32
+ br: isWin ? '+' : '┘',
33
+ teeR: isWin ? '+' : '├',
34
+ teeL: isWin ? '+' : '┤',
35
+ cross: isWin ? '+' : '┼',
36
+ bullet: isWin ? '*' : '•',
37
+ cloud: isWin ? '[cloud]' : '☁️ ',
38
+ home: isWin ? '[local]' : '🏠 ',
39
+ rocket: isWin ? '>>' : '🚀',
40
+ lock: isWin ? '[lock]' : '🔒',
41
+ warnEmoji: isWin ? '[!]' : '⚠️',
42
+ // Double-line box drawing (used for main banner)
43
+ dTl: isWin ? '+' : '╔',
44
+ dTr: isWin ? '+' : '╗',
45
+ dBl: isWin ? '+' : '╚',
46
+ dBr: isWin ? '+' : '╝',
47
+ dH: isWin ? '=' : '═',
48
+ dV: isWin ? '|' : '║',
49
+ };
13
50
  /**
14
51
  * Create an ora spinner that writes to stderr so it never
15
52
  * pollutes stdout JSON output.
@@ -42,33 +79,33 @@ class Logger {
42
79
  }
43
80
  }
44
81
  info(...args) {
45
- console.log(chalk_1.default.blue('ℹ'), ...args);
82
+ console.log(chalk_1.default.blue(exports.CH.info), ...args);
46
83
  }
47
84
  warn(...args) {
48
- console.log(chalk_1.default.yellow('⚠'), ...args);
85
+ console.log(chalk_1.default.yellow(exports.CH.warn), ...args);
49
86
  }
50
87
  error(...args) {
51
88
  // Write error messages to stdout (not stderr) to avoid triggering
52
89
  // PowerShell NativeCommandError styling. The red ✗ prefix already
53
90
  // signals an error visually; stderr redirection is unnecessary.
54
- console.log(chalk_1.default.red('✗'), ...args);
91
+ console.log(chalk_1.default.red(exports.CH.error), ...args);
55
92
  }
56
93
  success(...args) {
57
- console.log(chalk_1.default.green('✓'), ...args);
94
+ console.log(chalk_1.default.green(exports.CH.success), ...args);
58
95
  }
59
96
  // AI response formatting
60
97
  ai(message) {
61
- console.log(chalk_1.default.cyan('🤖'), message);
98
+ console.log(chalk_1.default.cyan(exports.CH.ai), message);
62
99
  }
63
100
  // User input formatting
64
101
  user(message) {
65
- console.log(chalk_1.default.white('👤'), message);
102
+ console.log(chalk_1.default.white(exports.CH.user), message);
66
103
  }
67
104
  // Code block
68
105
  code(code, language) {
69
- console.log(chalk_1.default.gray('─'.repeat(60)));
106
+ console.log(chalk_1.default.gray(exports.CH.hLine.repeat(60)));
70
107
  console.log(chalk_1.default.yellow(code));
71
- console.log(chalk_1.default.gray('─'.repeat(60)));
108
+ console.log(chalk_1.default.gray(exports.CH.hLine.repeat(60)));
72
109
  }
73
110
  // Diff output
74
111
  diff(added, removed) {
@@ -78,7 +115,7 @@ class Logger {
78
115
  // Section header
79
116
  section(title) {
80
117
  console.log();
81
- console.log(chalk_1.default.bold.cyan(`═══ ${title} ═══`));
118
+ console.log(chalk_1.default.bold.cyan(`${exports.CH.hDouble.repeat(3)} ${title} ${exports.CH.hDouble.repeat(3)}`));
82
119
  console.log();
83
120
  }
84
121
  // Progress
@@ -96,18 +133,18 @@ class Logger {
96
133
  const stripAnsi = (str) => str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '');
97
134
  const maxLen = Math.max(...lines.map(l => stripAnsi(l).length), title?.length || 0);
98
135
  const width = maxLen + 4;
99
- console.log(chalk_1.default.cyan('┌' + '─'.repeat(width - 2) + '┐'));
136
+ console.log(chalk_1.default.cyan(exports.CH.tl + exports.CH.hLine.repeat(width - 2) + exports.CH.tr));
100
137
  if (title) {
101
- console.log(chalk_1.default.cyan(' ') + chalk_1.default.bold.white(title.padEnd(width - 4)) + chalk_1.default.cyan(' '));
102
- console.log(chalk_1.default.cyan('├' + '─'.repeat(width - 2) + '┤'));
138
+ console.log(chalk_1.default.cyan(exports.CH.vLine + ' ') + chalk_1.default.bold.white(title.padEnd(width - 4)) + chalk_1.default.cyan(' ' + exports.CH.vLine));
139
+ console.log(chalk_1.default.cyan(exports.CH.teeR + exports.CH.hLine.repeat(width - 2) + exports.CH.teeL));
103
140
  }
104
141
  lines.forEach(line => {
105
142
  // Calculate visible length and pad accordingly
106
143
  const visibleLen = stripAnsi(line).length;
107
144
  const padding = ' '.repeat(Math.max(0, width - 4 - visibleLen));
108
- console.log(chalk_1.default.cyan(' ') + line + padding + chalk_1.default.cyan(' '));
145
+ console.log(chalk_1.default.cyan(exports.CH.vLine + ' ') + line + padding + chalk_1.default.cyan(' ' + exports.CH.vLine));
109
146
  });
110
- console.log(chalk_1.default.cyan('└' + '─'.repeat(width - 2) + '┘'));
147
+ console.log(chalk_1.default.cyan(exports.CH.bl + exports.CH.hLine.repeat(width - 2) + exports.CH.br));
111
148
  }
112
149
  // Table output
113
150
  table(headers, rows) {
@@ -116,18 +153,18 @@ class Logger {
116
153
  return Math.max(h.length, maxData);
117
154
  });
118
155
  // Header
119
- console.log(chalk_1.default.gray(' ') +
120
- headers.map((h, i) => chalk_1.default.bold(h.padEnd(colWidths[i]))).join(chalk_1.default.gray(' ')) +
121
- chalk_1.default.gray(' '));
156
+ console.log(chalk_1.default.gray(exports.CH.vLine + ' ') +
157
+ headers.map((h, i) => chalk_1.default.bold(h.padEnd(colWidths[i]))).join(chalk_1.default.gray(' ' + exports.CH.vLine + ' ')) +
158
+ chalk_1.default.gray(' ' + exports.CH.vLine));
122
159
  // Separator
123
- console.log(chalk_1.default.gray('├─') +
124
- colWidths.map(w => '─'.repeat(w)).join(chalk_1.default.gray('─┼─')) +
125
- chalk_1.default.gray('─┤'));
160
+ console.log(chalk_1.default.gray(exports.CH.teeR + exports.CH.hLine) +
161
+ colWidths.map(w => exports.CH.hLine.repeat(w)).join(chalk_1.default.gray(exports.CH.hLine + exports.CH.cross + exports.CH.hLine)) +
162
+ chalk_1.default.gray(exports.CH.hLine + exports.CH.teeL));
126
163
  // Rows
127
164
  rows.forEach(row => {
128
- console.log(chalk_1.default.gray(' ') +
129
- row.map((cell, i) => (cell || '').padEnd(colWidths[i])).join(chalk_1.default.gray(' ')) +
130
- chalk_1.default.gray(' '));
165
+ console.log(chalk_1.default.gray(exports.CH.vLine + ' ') +
166
+ row.map((cell, i) => (cell || '').padEnd(colWidths[i])).join(chalk_1.default.gray(' ' + exports.CH.vLine + ' ')) +
167
+ chalk_1.default.gray(' ' + exports.CH.vLine));
131
168
  });
132
169
  }
133
170
  }
@@ -56,6 +56,7 @@ const fs = __importStar(require("fs"));
56
56
  const path = __importStar(require("path"));
57
57
  const child_process_1 = require("child_process");
58
58
  const chalk_1 = __importDefault(require("chalk"));
59
+ const logger_js_1 = require("./logger.js");
59
60
  const TOOL_ARG_ALIASES = {
60
61
  read_file: {
61
62
  path: ['file', 'filePath', 'filepath', 'target', 'targetPath'],
@@ -172,7 +173,7 @@ class AgenticTools {
172
173
  fs.unlinkSync(lastOp.filePath);
173
174
  return {
174
175
  success: true,
175
- output: `✓ Undone: ${lastOp.description}\n File deleted: ${lastOp.filePath}`,
176
+ output: `${logger_js_1.CH.success} Undone: ${lastOp.description}\n File deleted: ${lastOp.filePath}`,
176
177
  metadata: { remainingUndos: this.undoStack.length },
177
178
  };
178
179
  }
@@ -182,7 +183,7 @@ class AgenticTools {
182
183
  fs.writeFileSync(lastOp.filePath, lastOp.originalContent, 'utf-8');
183
184
  return {
184
185
  success: true,
185
- output: `✓ Undone: ${lastOp.description}\n File restored: ${lastOp.filePath}`,
186
+ output: `${logger_js_1.CH.success} Undone: ${lastOp.description}\n File restored: ${lastOp.filePath}`,
186
187
  metadata: { remainingUndos: this.undoStack.length },
187
188
  };
188
189
  }
@@ -556,36 +557,36 @@ class AgenticTools {
556
557
  const riskColor = riskColors[tool.riskLevel];
557
558
  const riskIcon = riskIcons[tool.riskLevel];
558
559
  let msg = `\n${riskIcon} ${riskColor(`${tool.riskLevel.toUpperCase()} RISK`)} - AI wants to use ${chalk_1.default.cyan.bold(call.tool)}\n`;
559
- msg += chalk_1.default.gray('─'.repeat(50)) + '\n';
560
+ msg += chalk_1.default.gray(logger_js_1.CH.hLine.repeat(50)) + '\n';
560
561
  // Tool-specific details
561
562
  if (call.tool === 'bash') {
562
- msg += `${chalk_1.default.gray('├─')} ${chalk_1.default.white('Command:')} ${chalk_1.default.yellow(call.args.command)}\n`;
563
+ msg += `${chalk_1.default.gray(logger_js_1.CH.teeR + logger_js_1.CH.hLine)} ${chalk_1.default.white('Command:')} ${chalk_1.default.yellow(call.args.command)}\n`;
563
564
  if (call.args.cwd) {
564
- msg += `${chalk_1.default.gray('├─')} ${chalk_1.default.white('Directory:')} ${call.args.cwd}\n`;
565
+ msg += `${chalk_1.default.gray(logger_js_1.CH.teeR + logger_js_1.CH.hLine)} ${chalk_1.default.white('Directory:')} ${call.args.cwd}\n`;
565
566
  }
566
- msg += `${chalk_1.default.gray('├─')} ${chalk_1.default.white('Timeout:')} ${call.args.timeout || '30'}s\n`;
567
+ msg += `${chalk_1.default.gray(logger_js_1.CH.teeR + logger_js_1.CH.hLine)} ${chalk_1.default.white('Timeout:')} ${call.args.timeout || '30'}s\n`;
567
568
  }
568
569
  else if (call.tool === 'write_file') {
569
- msg += `${chalk_1.default.gray('├─')} ${chalk_1.default.white('File:')} ${chalk_1.default.cyan(call.args.path)}\n`;
570
+ msg += `${chalk_1.default.gray(logger_js_1.CH.teeR + logger_js_1.CH.hLine)} ${chalk_1.default.white('File:')} ${chalk_1.default.cyan(call.args.path)}\n`;
570
571
  const preview = call.args.content.substring(0, 100);
571
- msg += `${chalk_1.default.gray('├─')} ${chalk_1.default.white('Content preview:')} ${chalk_1.default.gray(preview)}${call.args.content.length > 100 ? '...' : ''}\n`;
572
- msg += `${chalk_1.default.gray('├─')} ${chalk_1.default.white('Size:')} ${call.args.content.length} bytes\n`;
572
+ msg += `${chalk_1.default.gray(logger_js_1.CH.teeR + logger_js_1.CH.hLine)} ${chalk_1.default.white('Content preview:')} ${chalk_1.default.gray(preview)}${call.args.content.length > 100 ? '...' : ''}\n`;
573
+ msg += `${chalk_1.default.gray(logger_js_1.CH.teeR + logger_js_1.CH.hLine)} ${chalk_1.default.white('Size:')} ${call.args.content.length} bytes\n`;
573
574
  }
574
575
  else if (call.tool === 'edit_file') {
575
- msg += `${chalk_1.default.gray('├─')} ${chalk_1.default.white('File:')} ${chalk_1.default.cyan(call.args.path)}\n`;
576
- msg += `${chalk_1.default.gray('├─')} ${chalk_1.default.white('Replace:')} ${chalk_1.default.red(call.args.old_text.substring(0, 50))}${call.args.old_text.length > 50 ? '...' : ''}\n`;
577
- msg += `${chalk_1.default.gray('├─')} ${chalk_1.default.white('With:')} ${chalk_1.default.green(call.args.new_text.substring(0, 50))}${call.args.new_text.length > 50 ? '...' : ''}\n`;
576
+ msg += `${chalk_1.default.gray(logger_js_1.CH.teeR + logger_js_1.CH.hLine)} ${chalk_1.default.white('File:')} ${chalk_1.default.cyan(call.args.path)}\n`;
577
+ msg += `${chalk_1.default.gray(logger_js_1.CH.teeR + logger_js_1.CH.hLine)} ${chalk_1.default.white('Replace:')} ${chalk_1.default.red(call.args.old_text.substring(0, 50))}${call.args.old_text.length > 50 ? '...' : ''}\n`;
578
+ msg += `${chalk_1.default.gray(logger_js_1.CH.teeR + logger_js_1.CH.hLine)} ${chalk_1.default.white('With:')} ${chalk_1.default.green(call.args.new_text.substring(0, 50))}${call.args.new_text.length > 50 ? '...' : ''}\n`;
578
579
  }
579
580
  else if (call.tool === 'git') {
580
- msg += `${chalk_1.default.gray('├─')} ${chalk_1.default.white('Command:')} git ${chalk_1.default.yellow(call.args.args)}\n`;
581
+ msg += `${chalk_1.default.gray(logger_js_1.CH.teeR + logger_js_1.CH.hLine)} ${chalk_1.default.white('Command:')} git ${chalk_1.default.yellow(call.args.args)}\n`;
581
582
  }
582
583
  // Safety info
583
- msg += chalk_1.default.gray('─'.repeat(50)) + '\n';
584
+ msg += chalk_1.default.gray(logger_js_1.CH.hLine.repeat(50)) + '\n';
584
585
  const canUndo = ['write_file', 'edit_file'].includes(call.tool);
585
- msg += `${chalk_1.default.gray('├─')} ${chalk_1.default.white('Undo:')} ${canUndo ? chalk_1.default.green(' Available (use /undo)') : chalk_1.default.gray(' Not available')}\n`;
586
- msg += `${chalk_1.default.gray('└─')} ${chalk_1.default.white('Category:')} ${tool.category}\n`;
586
+ msg += `${chalk_1.default.gray(logger_js_1.CH.teeR + logger_js_1.CH.hLine)} ${chalk_1.default.white('Undo:')} ${canUndo ? chalk_1.default.green(logger_js_1.CH.success + ' Available (use /undo)') : chalk_1.default.gray(logger_js_1.CH.error + ' Not available')}\n`;
587
+ msg += `${chalk_1.default.gray(logger_js_1.CH.bl + logger_js_1.CH.hLine)} ${chalk_1.default.white('Category:')} ${tool.category}\n`;
587
588
  if (tool.dangerous) {
588
- msg += `\n${chalk_1.default.red.bold('⚠️ WARNING: This is a potentially dangerous action!')}\n`;
589
+ msg += `\n${chalk_1.default.red.bold(logger_js_1.CH.warnEmoji + ' WARNING: This is a potentially dangerous action!')}\n`;
589
590
  msg += chalk_1.default.red(' The AI is requesting to execute commands on your system.\n');
590
591
  }
591
592
  // Add batch approval hint
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.6.35",
3
+ "version": "1.6.37",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -43,7 +43,8 @@
43
43
  "test:repo:live": "npm run build && node scripts/test-live-repo-e2e.js",
44
44
  "test:game:live": "npm run build && node scripts/test-live-game-push.js",
45
45
  "proof:agent": "npm run build && node scripts/proof-agent-mode.js",
46
- "prepublishOnly": "npm run build && node scripts/verify-package-hygiene.js"
46
+ "test:fresh-install": "node scripts/test-fresh-install.js",
47
+ "prepublishOnly": "npm run build && node scripts/verify-package-hygiene.js && node scripts/test-fresh-install.js"
47
48
  },
48
49
  "keywords": [
49
50
  "ai",
@@ -68,6 +69,7 @@
68
69
  "marked": "^11.0.0",
69
70
  "marked-terminal": "^6.2.0",
70
71
  "ora": "^7.0.1",
72
+ "rxjs": "^7.8.2",
71
73
  "simple-git": "^3.21.0",
72
74
  "ws": "^8.14.2"
73
75
  },