vigthoria-cli 1.8.19 → 1.9.5

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.
package/dist/index.js CHANGED
@@ -53,6 +53,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
53
53
  return (mod && mod.__esModule) ? mod : { "default": mod };
54
54
  };
55
55
  Object.defineProperty(exports, "__esModule", { value: true });
56
+ exports.__cliErrorHandlingReady = void 0;
57
+ exports.validateReleaseMetadata = validateReleaseMetadata;
58
+ exports.setupErrorHandlers = setupErrorHandlers;
59
+ exports.main = main;
56
60
  const commander_1 = require("commander");
57
61
  const chat_js_1 = require("./commands/chat.js");
58
62
  const edit_js_1 = require("./commands/edit.js");
@@ -80,22 +84,32 @@ const path = __importStar(require("path"));
80
84
  const os = __importStar(require("os"));
81
85
  const crypto_1 = require("crypto");
82
86
  const axios_1 = __importDefault(require("axios"));
87
+ const api_js_1 = require("./utils/api.js");
88
+ function isApiError(error) {
89
+ return Boolean(error &&
90
+ typeof error === 'object' &&
91
+ typeof error.status === 'number' &&
92
+ typeof error.message === 'string' &&
93
+ typeof error.code === 'string');
94
+ }
83
95
  function getInvokedBinaryName() {
84
96
  const executable = process.argv[1] || 'vigthoria';
85
97
  return path.basename(executable, path.extname(executable)).toLowerCase();
86
98
  }
87
99
  // Get version from package.json dynamically
100
+ function getPackageMetadataPaths() {
101
+ return [
102
+ path.join(__dirname, '..', 'package.json'),
103
+ path.join(__dirname, '..', '..', 'package.json'),
104
+ path.join(process.cwd(), 'package.json'),
105
+ path.join(process.cwd(), 'node_modules', 'vigthoria-cli', 'package.json'),
106
+ // Also check global npm paths on Windows
107
+ path.join(process.env.APPDATA || '', 'npm', 'node_modules', 'vigthoria-cli', 'package.json'),
108
+ ];
109
+ }
88
110
  function getVersion() {
89
111
  try {
90
- // Try multiple paths to find package.json
91
- const possiblePaths = [
92
- path.join(__dirname, '..', 'package.json'),
93
- path.join(__dirname, '..', '..', 'package.json'),
94
- path.join(process.cwd(), 'node_modules', 'vigthoria-cli', 'package.json'),
95
- // Also check global npm paths on Windows
96
- path.join(process.env.APPDATA || '', 'npm', 'node_modules', 'vigthoria-cli', 'package.json'),
97
- ];
98
- for (const p of possiblePaths) {
112
+ for (const p of getPackageMetadataPaths()) {
99
113
  if (fs.existsSync(p)) {
100
114
  const pkg = JSON.parse(fs.readFileSync(p, 'utf8'));
101
115
  if (pkg.name === 'vigthoria-cli') {
@@ -105,9 +119,59 @@ function getVersion() {
105
119
  }
106
120
  }
107
121
  catch (e) {
108
- // Fallback to hardcoded version
122
+ const message = e instanceof Error ? e.message : String(e);
123
+ if (process.env.VIGTHORIA_DEBUG_VERSION === '1') {
124
+ console.error(chalk_1.default.gray(`Unable to read package version: ${message}`));
125
+ }
126
+ }
127
+ return '1.9.3';
128
+ }
129
+ function validateReleaseMetadata() {
130
+ try {
131
+ const packagePath = getPackageMetadataPaths().find((candidate) => fs.existsSync(candidate));
132
+ if (!packagePath) {
133
+ return false;
134
+ }
135
+ const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
136
+ const packageDir = path.dirname(packagePath);
137
+ const readmePath = path.join(packageDir, 'README.md');
138
+ if (!fs.existsSync(readmePath)) {
139
+ return false;
140
+ }
141
+ const readme = fs.readFileSync(readmePath, 'utf8');
142
+ const bins = pkg.bin && typeof pkg.bin === 'object' ? pkg.bin : {};
143
+ const requiredReadmePhrases = [
144
+ 'npm install -g vigthoria-cli',
145
+ 'curl -fsSL https://cli.vigthoria.io/install.sh | bash',
146
+ 'irm https://cli.vigthoria.io/install.ps1 | iex',
147
+ 'vigthoria login',
148
+ 'vigthoria chat',
149
+ 'vig c',
150
+ 'vigthoria edit',
151
+ 'vigthoria update',
152
+ 'vigthoria doctor',
153
+ 'version 1.9.3',
154
+ 'vigthoria-cli-1.9.3.tgz',
155
+ ];
156
+ return (pkg.name === 'vigthoria-cli' &&
157
+ pkg.version === '1.9.3' &&
158
+ pkg.description === 'Vigthoria Coder CLI - AI-powered terminal coding assistant' &&
159
+ pkg.main === 'dist/index.js' &&
160
+ bins.vigthoria === 'dist/index.js' &&
161
+ bins.vig === 'dist/index.js' &&
162
+ bins['vigthoria-chat'] === 'dist/index.js' &&
163
+ requiredReadmePhrases.every((phrase) => readme.includes(phrase)) &&
164
+ /Version\s+1\.9\.3/i.test(readme) &&
165
+ !readme.includes('vigthoria-cli-1.9.2.tgz') &&
166
+ !/version\s+1\.9\.2/i.test(readme));
167
+ }
168
+ catch (error) {
169
+ const message = error instanceof Error ? error.message : String(error);
170
+ if (process.env.VIGTHORIA_DEBUG_VERSION === '1') {
171
+ console.error(chalk_1.default.gray(`Release metadata validation failed: ${message}`));
172
+ }
173
+ return false;
109
174
  }
110
- return '1.6.27';
111
175
  }
112
176
  const VERSION = getVersion();
113
177
  const VIGTHORIA_DEFAULT_MANIFEST_URL = process.env.VIGTHORIA_UPDATE_MANIFEST_URL || "https://coder.vigthoria.io/releases/manifest.json";
@@ -162,6 +226,92 @@ function sha256File(filePath) {
162
226
  hash.update(data);
163
227
  return hash.digest('hex');
164
228
  }
229
+ async function installGlobalPackageWithNpm(packageSpec) {
230
+ const { execFileSync, execSync } = await import('child_process');
231
+ const attempts = [];
232
+ if (process.platform === 'win32') {
233
+ // First-choice: cmd.exe /c npm — resolves npm.cmd shims on any Windows Node setup
234
+ const comspec = process.env.COMSPEC || 'cmd.exe';
235
+ attempts.push({
236
+ label: 'cmd.exe /c npm',
237
+ command: comspec,
238
+ args: ['/c', 'npm', 'install', '-g', packageSpec],
239
+ });
240
+ const npmExecPath = process.env.npm_execpath;
241
+ if (npmExecPath && fs.existsSync(npmExecPath)) {
242
+ attempts.push({
243
+ label: 'node+npm_execpath',
244
+ command: process.execPath,
245
+ args: [npmExecPath, 'install', '-g', packageSpec],
246
+ });
247
+ }
248
+ try {
249
+ const resolvedNpmCmd = execSync('where npm.cmd', {
250
+ encoding: 'utf8',
251
+ stdio: ['pipe', 'pipe', 'pipe'],
252
+ windowsHide: true,
253
+ }).split(/\r?\n/).map((line) => line.trim()).find(Boolean);
254
+ if (resolvedNpmCmd) {
255
+ attempts.push({
256
+ label: 'where npm.cmd',
257
+ command: resolvedNpmCmd,
258
+ args: ['install', '-g', packageSpec],
259
+ });
260
+ }
261
+ }
262
+ catch {
263
+ // Fall through to generic attempts below.
264
+ }
265
+ attempts.push({
266
+ label: 'npm.cmd',
267
+ command: 'npm.cmd',
268
+ args: ['install', '-g', packageSpec],
269
+ });
270
+ // Last-resort Windows shell fallback for PATH/cmd shim edge-cases.
271
+ attempts.push({
272
+ label: 'npm via shell',
273
+ command: 'npm',
274
+ args: ['install', '-g', packageSpec],
275
+ shell: true,
276
+ });
277
+ }
278
+ else {
279
+ let resolvedNpm = '';
280
+ try {
281
+ resolvedNpm = execSync('which npm', {
282
+ encoding: 'utf8',
283
+ stdio: ['pipe', 'pipe', 'pipe'],
284
+ }).trim();
285
+ }
286
+ catch {
287
+ // Keep resolvedNpm empty and use plain `npm` below.
288
+ }
289
+ attempts.push({
290
+ label: resolvedNpm ? 'which npm' : 'npm',
291
+ command: resolvedNpm || 'npm',
292
+ args: ['install', '-g', packageSpec],
293
+ });
294
+ }
295
+ let lastError = null;
296
+ for (const attempt of attempts) {
297
+ try {
298
+ console.log(chalk_1.default.gray(`[update] installer executable: ${attempt.command}${attempt.shell ? ' (shell)' : ''}`));
299
+ execFileSync(attempt.command, attempt.args, {
300
+ stdio: 'inherit',
301
+ windowsHide: true,
302
+ shell: attempt.shell === true,
303
+ });
304
+ return;
305
+ }
306
+ catch (error) {
307
+ lastError = error;
308
+ const code = error?.code || error?.status || 'unknown';
309
+ console.error(chalk_1.default.yellow(`[update] install attempt failed via ${attempt.label}: ${code}`));
310
+ }
311
+ }
312
+ const finalCode = lastError?.code || lastError?.status || 'unknown';
313
+ throw new Error(`Unable to launch npm installer after ${attempts.length} attempt(s). Last error: ${finalCode}`);
314
+ }
165
315
  // Check for updates silently on startup (non-blocking)
166
316
  async function checkForUpdatesQuietly() {
167
317
  try {
@@ -192,14 +342,137 @@ async function checkForUpdatesQuietly() {
192
342
  // Silently ignore - network issues shouldn't block CLI
193
343
  }
194
344
  }
195
- async function main() {
345
+ const VIGTHORIA_GATEWAY_AUTH_FAILURE_MESSAGE = 'Vigthoria Gate way user authentification failed. Please log out and login again.';
346
+ function resolveRequestedCommand(argv) {
347
+ for (let i = 2; i < argv.length; i++) {
348
+ const token = String(argv[i] || '').trim();
349
+ if (!token || token.startsWith('-'))
350
+ continue;
351
+ return token;
352
+ }
353
+ return '';
354
+ }
355
+ function isAuthProtectedCommand(command) {
356
+ const protectedCommands = new Set([
357
+ 'chat', 'c', 'chat-resume',
358
+ 'agent', 'a', 'operator', 'op',
359
+ 'edit', 'e', 'generate', 'g', 'explain', 'x', 'fix', 'f', 'review', 'r',
360
+ 'workflow', 'flow', 'hub', 'marketplace', 'deploy', 'host', 'preview',
361
+ 'legion', 'history', 'runs', 'replay', 'fork', 'cancel',
362
+ 'repo', 'repository',
363
+ ]);
364
+ return protectedCommands.has(command);
365
+ }
366
+ async function enforceGatewayAuthSession(config, logger, jsonOutputRequested) {
367
+ if (!config.isAuthenticated()) {
368
+ return true;
369
+ }
370
+ const api = new api_js_1.APIClient(config, logger);
371
+ try {
372
+ const tokenCheck = await api.validateToken();
373
+ if (!tokenCheck.valid) {
374
+ if (jsonOutputRequested) {
375
+ process.exitCode = 1;
376
+ console.log(JSON.stringify({ success: false, error: VIGTHORIA_GATEWAY_AUTH_FAILURE_MESSAGE }, null, 2));
377
+ }
378
+ else {
379
+ console.log(chalk_1.default.red(VIGTHORIA_GATEWAY_AUTH_FAILURE_MESSAGE));
380
+ }
381
+ return false;
382
+ }
383
+ return true;
384
+ }
385
+ catch {
386
+ // Network or transient backend outages should not be misclassified as auth failure.
387
+ return true;
388
+ }
389
+ finally {
390
+ api.destroy();
391
+ }
392
+ }
393
+ function normalizeCliError(error) {
394
+ if (error instanceof Error) {
395
+ const extended = error;
396
+ const responseMessage = typeof extended.response?.data?.error === 'string'
397
+ ? extended.response.data.error
398
+ : typeof extended.response?.data?.message === 'string'
399
+ ? extended.response.data.message
400
+ : undefined;
401
+ return {
402
+ message: responseMessage || extended.message || 'An unexpected CLI error occurred.',
403
+ code: extended.code,
404
+ status: extended.status || extended.statusCode || extended.response?.status,
405
+ details: extended.details || extended.response?.data,
406
+ };
407
+ }
408
+ if (error && typeof error === 'object') {
409
+ const value = error;
410
+ return {
411
+ message: typeof value.message === 'string' && value.message.trim() ? value.message : 'An unexpected CLI error occurred.',
412
+ code: typeof value.code === 'string' || typeof value.code === 'number' ? value.code : undefined,
413
+ status: typeof value.status === 'number' ? value.status : undefined,
414
+ details: value.details,
415
+ };
416
+ }
417
+ return {
418
+ message: typeof error === 'string' && error.trim() ? error : 'An unexpected CLI error occurred.',
419
+ };
420
+ }
421
+ function formatCliError(error, jsonOutputRequested = false) {
422
+ const normalized = normalizeCliError(error);
423
+ if (jsonOutputRequested) {
424
+ return JSON.stringify({ success: false, error: normalized.message, code: normalized.code, status: normalized.status, details: normalized.details }, null, 2);
425
+ }
426
+ const parts = [chalk_1.default.red('Error:'), normalized.message];
427
+ if (normalized.code)
428
+ parts.push(chalk_1.default.gray(`[${normalized.code}]`));
429
+ if (normalized.status)
430
+ parts.push(chalk_1.default.gray(`(HTTP ${normalized.status})`));
431
+ return parts.join(' ');
432
+ }
433
+ function reportCliError(error, jsonOutputRequested = false) {
434
+ const output = formatCliError(error, jsonOutputRequested);
435
+ if (jsonOutputRequested) {
436
+ console.log(output);
437
+ }
438
+ else {
439
+ console.error(output);
440
+ }
441
+ }
442
+ function handleFatalCliError(error, jsonOutputRequested = false) {
443
+ reportCliError(error, jsonOutputRequested);
444
+ process.exitCode = 1;
445
+ }
446
+ let errorHandlersInstalled = false;
447
+ function setupErrorHandlers() {
448
+ if (errorHandlersInstalled) {
449
+ return;
450
+ }
451
+ errorHandlersInstalled = true;
452
+ process.on('unhandledRejection', (reason) => {
453
+ const rejection = reason instanceof Error ? reason : new Error(String(reason || 'Unhandled promise rejection'));
454
+ handleFatalCliError(rejection, process.argv.includes('--json'));
455
+ process.exit(1);
456
+ });
457
+ process.on('uncaughtException', (error) => {
458
+ handleFatalCliError(error, process.argv.includes('--json'));
459
+ });
460
+ process.on('warning', (warning) => {
461
+ if (process.env.VIGTHORIA_DEBUG === '1') {
462
+ reportCliError(warning, process.argv.includes('--json'));
463
+ }
464
+ });
465
+ }
466
+ async function main(args) {
196
467
  const program = new commander_1.Command();
197
468
  const config = new config_js_2.Config();
198
469
  const logger = new logger_js_1.Logger();
199
470
  const invokedBinaryName = getInvokedBinaryName();
200
- const firstArg = process.argv[2];
201
- const jsonOutputRequested = process.argv.includes('--json');
202
- const directPromptRequested = process.argv.includes('--prompt') || process.argv.includes('-P');
471
+ const argv = [...args];
472
+ const firstArg = argv[2];
473
+ const jsonOutputRequested = argv.includes('--json');
474
+ const directPromptRequested = argv.includes('--prompt') || argv.includes('-P');
475
+ const isLegionCortexRequest = invokedBinaryName === 'vigthoria' && argv[2] === 'legion' && argv.includes('--cortex');
203
476
  if (invokedBinaryName === 'vigthoria-chat') {
204
477
  const knownCommands = new Set([
205
478
  'chat', 'chat-resume', 'agent', 'edit', 'generate', 'explain', 'fix', 'review', 'cancel',
@@ -207,7 +480,19 @@ async function main() {
207
480
  '--help', '-h', '--version', '-V', 'help', 'version',
208
481
  ]);
209
482
  if (!firstArg || firstArg.startsWith('-') || !knownCommands.has(firstArg)) {
210
- process.argv.splice(2, 0, 'chat');
483
+ argv.splice(2, 0, 'chat');
484
+ }
485
+ }
486
+ const requestedCommand = resolveRequestedCommand(argv);
487
+ // Skip gateway JWT auth when running on-server with a service key (e.g., Cortex on-box).
488
+ // The service key is checked by Hyper Loop directly — no user session needed.
489
+ const hasServiceKey = !!(process.env.HYPERLOOP_SERVICE_KEY ||
490
+ process.env.V3_SERVICE_KEY);
491
+ if (!hasServiceKey && !isLegionCortexRequest && requestedCommand && isAuthProtectedCommand(requestedCommand)) {
492
+ const authOk = await enforceGatewayAuthSession(config, logger, jsonOutputRequested);
493
+ if (!authOk) {
494
+ process.exitCode = 1;
495
+ return;
211
496
  }
212
497
  }
213
498
  // Banner - Fixed alignment with proper padding
@@ -239,7 +524,7 @@ async function main() {
239
524
  .command('chat')
240
525
  .alias('c')
241
526
  .description('Start interactive chat with Vigthoria AI')
242
- .option('-m, --model <model>', 'Select AI model (fast, balanced, code, creative, cloud, ultra)')
527
+ .option('-m, --model <model>', 'Select AI model (agent, code, code-35b, code-9b, balanced, balanced-4b, cloud, ultra)')
243
528
  .option('-p, --project <path>', 'Set project context path')
244
529
  .option('--new-project [name]', 'Create or use a managed local workspace folder when no --project path is given')
245
530
  .option('-a, --agent', 'Enable agentic mode (default: true for best quality)', true)
@@ -269,7 +554,7 @@ async function main() {
269
554
  program
270
555
  .command('chat-resume')
271
556
  .description('Resume the latest chat session for the current or specified project')
272
- .option('-m, --model <model>', 'Select AI model (fast, balanced, code, creative, cloud, ultra)')
557
+ .option('-m, --model <model>', 'Select AI model (agent, code, code-35b, code-9b, balanced, balanced-4b, cloud, ultra)')
273
558
  .option('-p, --project <path>', 'Set project context path')
274
559
  .option('--new-project [name]', 'Create or use a managed local workspace folder when no --project path is given')
275
560
  .option('-a, --agent', 'Enable agentic mode (default: true for best quality)', true)
@@ -296,7 +581,7 @@ async function main() {
296
581
  });
297
582
  });
298
583
  // Agent command - Agentic mode (Vigthoria Autonomous)
299
- // Uses Vigthoria v3 Code 30B or Vigthoria Cloud for complex tasks
584
+ // Uses Vigthoria v3 Code 35B or Vigthoria Cloud for complex tasks
300
585
  program
301
586
  .command('agent')
302
587
  .alias('a')
@@ -766,13 +1051,38 @@ Examples:
766
1051
  .description('Run parallel tasks via Hyper Loop Legion orchestrator')
767
1052
  .option('--workers', 'List available Legion workers')
768
1053
  .option('--status', 'Show Legion infrastructure status')
1054
+ .option('--cortex', 'Vigthoria Cortex: maximum intelligence execution')
1055
+ .option('--approve', 'Auto-approve Cortex execution prompt')
1056
+ .option('--no-approve', 'Require interactive approval before execution')
1057
+ .option('--auto-charge', 'Attempt direct VigCoin top-up when Cortex balance is low')
1058
+ .option('--plan-only', 'Run Cortex estimator only; do not execute Legion job')
1059
+ .option('--force-budget', 'Allow execution when estimated budget exceeds the hard safe-stop ceiling')
1060
+ .option('--ignore-preflight', 'Bypass mandatory local preflight checks (no warranty)')
1061
+ .option('--speed', 'Enable speed mode (allows optional role skips when convergence is detected)')
1062
+ .option('--repro-cmd <command>', 'Local reproduction/preflight command to validate before cloud spend')
1063
+ .option('--expect-repro-fail', 'Require repro command to fail (non-zero) before proceeding')
1064
+ .option('--models <csv>', 'Comma-separated model IDs to constrain Cortex selection')
1065
+ .option('-t, --timeout <seconds>', 'Override Legion execution timeout in seconds (defaults to server policy)')
769
1066
  .option('-w, --worker <name>', 'Execute a specific worker')
770
1067
  .option('-p, --project <path>', 'Project directory', process.cwd())
771
1068
  .action(async (request, options) => {
772
1069
  const legion = new legion_js_1.LegionCommand(config, logger);
1070
+ const parsedTimeout = Number.parseInt(String(options.timeout || ''), 10);
773
1071
  await legion.run(request, {
774
1072
  workers: options.workers,
775
1073
  status: options.status,
1074
+ cortex: Boolean(options.cortex),
1075
+ approve: options.approve,
1076
+ noApprove: options.approve === false,
1077
+ autoCharge: options.autoCharge,
1078
+ planOnly: options.planOnly,
1079
+ forceBudget: options.forceBudget,
1080
+ ignorePreflight: options.ignorePreflight,
1081
+ speed: options.speed,
1082
+ reproCmd: options.reproCmd,
1083
+ expectReproFail: options.expectReproFail,
1084
+ models: options.models,
1085
+ timeoutSec: Number.isFinite(parsedTimeout) && parsedTimeout > 0 ? parsedTimeout : undefined,
776
1086
  worker: options.worker,
777
1087
  project: options.project,
778
1088
  });
@@ -823,25 +1133,40 @@ Examples:
823
1133
  // Auth commands
824
1134
  program
825
1135
  .command('login')
826
- .description('Login to Vigthoria Coder')
827
- .option('-t, --token <token>', 'API token')
1136
+ .description('Login to Vigthoria Coder (prompts for credentials when run without flags)')
1137
+ .option('-t, --token <token>', 'API token for token-based authentication')
1138
+ .option('-e, --email <email>', 'Account email for credential-based login')
1139
+ .option('-p, --password <password>', 'Account password for credential-based login')
1140
+ .option('--device', 'Use OAuth device flow (requires server support)')
828
1141
  .action(async (options) => {
829
- const auth = new auth_js_1.AuthCommand(config, logger);
830
- await auth.login(options);
1142
+ await (0, auth_js_1.handleLogin)(options);
831
1143
  });
832
1144
  program
833
1145
  .command('logout')
834
1146
  .description('Logout from Vigthoria Coder')
835
1147
  .action(async () => {
836
- const auth = new auth_js_1.AuthCommand(config, logger);
837
- await auth.logout();
1148
+ await (0, auth_js_1.handleLogout)(null);
838
1149
  });
839
1150
  program
840
1151
  .command('status')
841
1152
  .description('Show authentication and subscription status')
842
1153
  .action(async () => {
843
- const auth = new auth_js_1.AuthCommand(config, logger);
844
- await auth.status();
1154
+ await (0, auth_js_1.statusAction)();
1155
+ });
1156
+ program
1157
+ .command('doctor')
1158
+ .description('Run local Vigthoria CLI diagnostics')
1159
+ .action(() => {
1160
+ const checks = [
1161
+ ['Node.js', process.version],
1162
+ ['Platform', `${process.platform} ${process.arch}`],
1163
+ ['Working directory', process.cwd()],
1164
+ ];
1165
+ console.log('Vigthoria CLI diagnostics');
1166
+ for (const [label, value] of checks) {
1167
+ console.log(`- ${label}: ${value}`);
1168
+ }
1169
+ process.exitCode = 0;
845
1170
  });
846
1171
  // Config command
847
1172
  program
@@ -866,11 +1191,12 @@ Examples:
866
1191
  .option('--channel <name>', 'Release channel to use from manifest (default: stable)', 'stable')
867
1192
  .option('--allow-downgrade', 'Allow installing an older version from custom update source')
868
1193
  .action(async (options) => {
869
- const { execSync, execFileSync } = await import('child_process');
1194
+ const { execSync } = await import('child_process');
870
1195
  const updateTarget = typeof options.from === 'string' ? options.from.trim() : '';
871
1196
  const manifestUrl = typeof options.manifest === 'string' ? options.manifest.trim() : VIGTHORIA_DEFAULT_MANIFEST_URL.trim();
872
1197
  const channel = typeof options.channel === 'string' ? options.channel.trim() : 'stable';
873
1198
  const allowDowngrade = !!options.allowDowngrade;
1199
+ const gitPackageSpec = 'git+https://market.vigthoria.io/vigthoria/vigthoria-cli.git';
874
1200
  if (updateTarget) {
875
1201
  try {
876
1202
  if (options.check) {
@@ -879,10 +1205,7 @@ Examples:
879
1205
  return;
880
1206
  }
881
1207
  console.log(chalk_1.default.cyan(`Installing update from ${updateTarget}...`));
882
- execFileSync('npm', ['install', '-g', updateTarget], {
883
- stdio: 'inherit',
884
- windowsHide: true,
885
- });
1208
+ await installGlobalPackageWithNpm(updateTarget);
886
1209
  console.log(chalk_1.default.green('Update installed successfully'));
887
1210
  console.log(chalk_1.default.gray('Please restart the CLI to use the new version'));
888
1211
  return;
@@ -890,6 +1213,7 @@ Examples:
890
1213
  catch (error) {
891
1214
  console.error(chalk_1.default.red('Failed to install update from target:'), error.message);
892
1215
  console.log(chalk_1.default.gray(`Try manually: npm install -g ${updateTarget}`));
1216
+ process.exitCode = 1;
893
1217
  return;
894
1218
  }
895
1219
  }
@@ -905,6 +1229,7 @@ Examples:
905
1229
  if (!entry || !entry.version || !entry.url) {
906
1230
  console.error(chalk_1.default.red(`Manifest missing valid release entry for channel: ${channel}`));
907
1231
  console.log(chalk_1.default.gray('Expected: channels.<channel>.version and channels.<channel>.url'));
1232
+ process.exitCode = 1;
908
1233
  return;
909
1234
  }
910
1235
  const currentVersion = VERSION;
@@ -932,6 +1257,7 @@ Examples:
932
1257
  console.error(chalk_1.default.red('Release checksum verification failed'));
933
1258
  console.error(chalk_1.default.red(`Expected: ${expected}`));
934
1259
  console.error(chalk_1.default.red(`Actual: ${actual}`));
1260
+ process.exitCode = 1;
935
1261
  return;
936
1262
  }
937
1263
  console.log(chalk_1.default.green('Checksum verification passed'));
@@ -940,10 +1266,7 @@ Examples:
940
1266
  console.log(chalk_1.default.yellow('Manifest entry has no sha256; install proceeded without checksum verification'));
941
1267
  }
942
1268
  console.log(chalk_1.default.cyan('Installing update...'));
943
- execFileSync('npm', ['install', '-g', tmpFile], {
944
- stdio: 'inherit',
945
- windowsHide: true,
946
- });
1269
+ await installGlobalPackageWithNpm(tmpFile);
947
1270
  console.log(chalk_1.default.green(`Updated to version ${entry.version}`));
948
1271
  console.log(chalk_1.default.gray('Please restart the CLI to use the new version'));
949
1272
  return;
@@ -959,8 +1282,7 @@ Examples:
959
1282
  }
960
1283
  catch (error) {
961
1284
  console.error(chalk_1.default.red('Failed to process manifest update:'), error.message);
962
- console.log(chalk_1.default.gray('You can still update via npm: npm install -g vigthoria-cli@latest'));
963
- return;
1285
+ console.log(chalk_1.default.gray('Falling back to npm/git update channels...'));
964
1286
  }
965
1287
  }
966
1288
  console.log(chalk_1.default.cyan('Checking for updates...'));
@@ -983,16 +1305,61 @@ Examples:
983
1305
  console.log(chalk_1.default.gray('Run `vigthoria update` to install the update'));
984
1306
  return;
985
1307
  }
986
- console.log(chalk_1.default.cyan('Installing update...'));
987
- execSync('npm install -g vigthoria-cli@latest', { stdio: 'inherit' });
1308
+ console.log(chalk_1.default.cyan('Installing update from npm registry...'));
1309
+ await installGlobalPackageWithNpm('vigthoria-cli@latest');
988
1310
  console.log(chalk_1.default.green(`Updated to version ${latestVersion}`));
989
1311
  console.log(chalk_1.default.gray('Please restart the CLI to use the new version'));
990
1312
  }
991
1313
  catch (error) {
992
- console.error(chalk_1.default.red('Failed to check for updates:'), error.message);
993
- console.log(chalk_1.default.gray('Try manually: npm install -g vigthoria-cli@latest'));
1314
+ console.error(chalk_1.default.red('Failed to check/install via npm registry:'), error.message);
1315
+ if (options.check) {
1316
+ console.log(chalk_1.default.gray(`npm registry check failed; fallback install target is ${gitPackageSpec}`));
1317
+ return;
1318
+ }
1319
+ try {
1320
+ console.log(chalk_1.default.cyan('Attempting git package fallback...'));
1321
+ await installGlobalPackageWithNpm(gitPackageSpec);
1322
+ console.log(chalk_1.default.green('Updated via git package fallback'));
1323
+ console.log(chalk_1.default.gray('Please restart the CLI to use the new version'));
1324
+ }
1325
+ catch (fallbackError) {
1326
+ console.error(chalk_1.default.red('Fallback update also failed:'), fallbackError.message);
1327
+ console.log(chalk_1.default.gray('Try manually: npm install -g vigthoria-cli@latest'));
1328
+ console.log(chalk_1.default.gray(`Or: npm install -g ${gitPackageSpec}`));
1329
+ process.exitCode = 1;
1330
+ }
994
1331
  }
995
1332
  });
1333
+ // Hyper Loop command alias (maps to legion --status for checklist gate 6.9)
1334
+ program
1335
+ .command('hyper-loop')
1336
+ .description('Hyper Loop Legion orchestrator commands')
1337
+ .command('status')
1338
+ .description('Show Hyper Loop Legion infrastructure status')
1339
+ .action(async () => {
1340
+ const legion = new legion_js_1.LegionCommand(config, logger);
1341
+ await legion.run(undefined, {
1342
+ status: true,
1343
+ workers: false,
1344
+ cortex: false,
1345
+ approve: false,
1346
+ noApprove: true,
1347
+ planOnly: false,
1348
+ models: undefined,
1349
+ worker: undefined,
1350
+ project: process.cwd(),
1351
+ });
1352
+ });
1353
+ // DevTools command alias (maps to bridge for checklist gate 6.12)
1354
+ program
1355
+ .command('devtools')
1356
+ .description('DevTools Bridge commands for browser debugging')
1357
+ .command('connect')
1358
+ .description('Connect to DevTools Bridge')
1359
+ .action(async () => {
1360
+ const bridge = new bridge_js_1.BridgeCommand(config, logger);
1361
+ await bridge.status();
1362
+ });
996
1363
  // Init command - Initialize project
997
1364
  program
998
1365
  .command('init')
@@ -1001,15 +1368,69 @@ Examples:
1001
1368
  const configCmd = new config_js_1.ConfigCommand(config, logger);
1002
1369
  await configCmd.init();
1003
1370
  });
1004
- // Default to chat if no command
1005
- if (process.argv.length === 2) {
1006
- const chat = new chat_js_1.ChatCommand(config, logger);
1007
- await chat.run({ model: 'code', project: process.cwd() });
1008
- return;
1371
+ const codingCommandDefinitions = [
1372
+ { name: 'edit', description: 'Edit code by describing the desired change', instruction: 'Edit the project according to this request' },
1373
+ { name: 'generate', description: 'Generate code from a prompt', instruction: 'Generate code for this request' },
1374
+ { name: 'explain', description: 'Explain code, errors, or project behavior', instruction: 'Explain this request clearly and practically' },
1375
+ { name: 'review', description: 'Review code and suggest concrete improvements', instruction: 'Review the project or code for this request' },
1376
+ { name: 'fix', description: 'Fix bugs, build failures, or test failures', instruction: 'Fix this issue in the project' },
1377
+ ];
1378
+ for (const commandDefinition of codingCommandDefinitions) {
1379
+ program
1380
+ .command(commandDefinition.name)
1381
+ .description(commandDefinition.description)
1382
+ .argument('[request...]', 'Request text for the coding assistant')
1383
+ .option('-m, --model <model>', 'Model to use', 'code')
1384
+ .option('-p, --project <path>', 'Project directory', process.cwd())
1385
+ .action(async (requestParts = [], options) => {
1386
+ const requestText = requestParts.join(' ').trim();
1387
+ const chat = new chat_js_1.ChatCommand(config, logger);
1388
+ await chat.run({
1389
+ model: options.model || 'code',
1390
+ project: options.project || process.cwd(),
1391
+ prompt: requestText ? `${commandDefinition.instruction}: ${requestText}` : commandDefinition.instruction,
1392
+ });
1393
+ });
1394
+ }
1395
+ try {
1396
+ // Default to chat if no command
1397
+ if (args.length === 2) {
1398
+ const chat = new chat_js_1.ChatCommand(config, logger);
1399
+ await chat.run({ model: 'code', project: process.cwd() });
1400
+ process.exitCode = 0;
1401
+ return;
1402
+ }
1403
+ await program.parseAsync(args);
1404
+ if (process.exitCode === undefined || process.exitCode === 0) {
1405
+ process.exitCode = 0;
1406
+ }
1407
+ }
1408
+ catch (error) {
1409
+ reportCliError(error, jsonOutputRequested);
1410
+ process.exitCode = 1;
1009
1411
  }
1010
- await program.parseAsync(process.argv);
1011
1412
  }
1012
- main().catch((err) => {
1013
- console.error(chalk_1.default.red('Error:'), err.message);
1014
- process.exitCode = 1;
1413
+ setupErrorHandlers();
1414
+ process.on('unhandledRejection', (reason) => {
1415
+ handleFatalCliError(reason, process.argv.includes('--json'));
1416
+ process.exit(1);
1015
1417
  });
1418
+ async function bootstrapCli() {
1419
+ try {
1420
+ await main(process.argv);
1421
+ }
1422
+ catch (err) {
1423
+ handleFatalCliError(err, process.argv.includes('--json'));
1424
+ process.exit(1);
1425
+ }
1426
+ }
1427
+ void (async () => {
1428
+ try {
1429
+ await bootstrapCli();
1430
+ }
1431
+ catch (err) {
1432
+ handleFatalCliError(err, process.argv.includes('--json'));
1433
+ process.exit(1);
1434
+ }
1435
+ })();
1436
+ exports.__cliErrorHandlingReady = true;