vigthoria-cli 1.8.15 → 1.9.2

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.d.ts CHANGED
@@ -15,4 +15,6 @@
15
15
  * vigthoria workflow - Manage repeatable VigFlow workflows
16
16
  * vigthoria operator - Start BMAD operator mode
17
17
  */
18
- export {};
18
+ export declare function setupErrorHandlers(): void;
19
+ export declare function main(): Promise<void>;
20
+ export declare const __cliErrorHandlingReady = true;
package/dist/index.js CHANGED
@@ -53,6 +53,9 @@ 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.setupErrorHandlers = setupErrorHandlers;
58
+ exports.main = main;
56
59
  const commander_1 = require("commander");
57
60
  const chat_js_1 = require("./commands/chat.js");
58
61
  const edit_js_1 = require("./commands/edit.js");
@@ -77,6 +80,17 @@ const logger_js_1 = require("./utils/logger.js");
77
80
  const chalk_1 = __importDefault(require("chalk"));
78
81
  const fs = __importStar(require("fs"));
79
82
  const path = __importStar(require("path"));
83
+ const os = __importStar(require("os"));
84
+ const crypto_1 = require("crypto");
85
+ const axios_1 = __importDefault(require("axios"));
86
+ const api_js_1 = require("./utils/api.js");
87
+ function isApiError(error) {
88
+ return Boolean(error &&
89
+ typeof error === 'object' &&
90
+ typeof error.status === 'number' &&
91
+ typeof error.message === 'string' &&
92
+ typeof error.code === 'string');
93
+ }
80
94
  function getInvokedBinaryName() {
81
95
  const executable = process.argv[1] || 'vigthoria';
82
96
  return path.basename(executable, path.extname(executable)).toLowerCase();
@@ -104,9 +118,10 @@ function getVersion() {
104
118
  catch (e) {
105
119
  // Fallback to hardcoded version
106
120
  }
107
- return '1.6.27';
121
+ return '1.9.2';
108
122
  }
109
123
  const VERSION = getVersion();
124
+ const VIGTHORIA_DEFAULT_MANIFEST_URL = process.env.VIGTHORIA_UPDATE_MANIFEST_URL || "https://coder.vigthoria.io/releases/manifest.json";
110
125
  /**
111
126
  * Compare semantic versions properly
112
127
  * Returns: -1 if v1 < v2, 0 if equal, 1 if v1 > v2
@@ -124,6 +139,119 @@ function compareVersions(v1, v2) {
124
139
  }
125
140
  return 0;
126
141
  }
142
+ function resolveManifestEntry(manifest, channel) {
143
+ const normalized = String(channel || 'stable').trim() || 'stable';
144
+ if (manifest.channels && manifest.channels[normalized]) {
145
+ return manifest.channels[normalized];
146
+ }
147
+ const direct = manifest[normalized];
148
+ if (direct && typeof direct === 'object') {
149
+ return direct;
150
+ }
151
+ if (normalized === 'stable') {
152
+ const rootVersion = typeof manifest.version === 'string' ? manifest.version : '';
153
+ const rootUrl = typeof manifest.url === 'string' ? manifest.url : '';
154
+ const rootSha = typeof manifest.sha256 === 'string' ? manifest.sha256 : undefined;
155
+ if (rootVersion && rootUrl) {
156
+ return { version: rootVersion, url: rootUrl, sha256: rootSha };
157
+ }
158
+ }
159
+ return null;
160
+ }
161
+ async function downloadFile(url, targetPath) {
162
+ const response = await axios_1.default.get(url, {
163
+ responseType: 'arraybuffer',
164
+ timeout: 20000,
165
+ maxRedirects: 5,
166
+ validateStatus: (status) => status >= 200 && status < 300,
167
+ });
168
+ fs.writeFileSync(targetPath, Buffer.from(response.data));
169
+ }
170
+ function sha256File(filePath) {
171
+ const hash = (0, crypto_1.createHash)('sha256');
172
+ const data = fs.readFileSync(filePath);
173
+ hash.update(data);
174
+ return hash.digest('hex');
175
+ }
176
+ async function installGlobalPackageWithNpm(packageSpec) {
177
+ const { execFileSync, execSync } = await import('child_process');
178
+ const attempts = [];
179
+ if (process.platform === 'win32') {
180
+ const npmExecPath = process.env.npm_execpath;
181
+ if (npmExecPath && fs.existsSync(npmExecPath)) {
182
+ attempts.push({
183
+ label: 'node+npm_execpath',
184
+ command: process.execPath,
185
+ args: [npmExecPath, 'install', '-g', packageSpec],
186
+ });
187
+ }
188
+ try {
189
+ const resolvedNpmCmd = execSync('where npm.cmd', {
190
+ encoding: 'utf8',
191
+ stdio: ['pipe', 'pipe', 'pipe'],
192
+ windowsHide: true,
193
+ }).split(/\r?\n/).map((line) => line.trim()).find(Boolean);
194
+ if (resolvedNpmCmd) {
195
+ attempts.push({
196
+ label: 'where npm.cmd',
197
+ command: resolvedNpmCmd,
198
+ args: ['install', '-g', packageSpec],
199
+ });
200
+ }
201
+ }
202
+ catch {
203
+ // Fall through to generic attempts below.
204
+ }
205
+ attempts.push({
206
+ label: 'npm.cmd',
207
+ command: 'npm.cmd',
208
+ args: ['install', '-g', packageSpec],
209
+ });
210
+ // Last-resort Windows shell fallback for PATH/cmd shim edge-cases.
211
+ attempts.push({
212
+ label: 'npm via shell',
213
+ command: 'npm',
214
+ args: ['install', '-g', packageSpec],
215
+ shell: true,
216
+ });
217
+ }
218
+ else {
219
+ let resolvedNpm = '';
220
+ try {
221
+ resolvedNpm = execSync('which npm', {
222
+ encoding: 'utf8',
223
+ stdio: ['pipe', 'pipe', 'pipe'],
224
+ }).trim();
225
+ }
226
+ catch {
227
+ // Keep resolvedNpm empty and use plain `npm` below.
228
+ }
229
+ attempts.push({
230
+ label: resolvedNpm ? 'which npm' : 'npm',
231
+ command: resolvedNpm || 'npm',
232
+ args: ['install', '-g', packageSpec],
233
+ });
234
+ }
235
+ let lastError = null;
236
+ for (const attempt of attempts) {
237
+ try {
238
+ console.log(chalk_1.default.gray(`[update] installer executable: ${attempt.command}${attempt.shell ? ' (shell)' : ''}`));
239
+ execFileSync(attempt.command, attempt.args, {
240
+ stdio: 'inherit',
241
+ windowsHide: true,
242
+ shell: attempt.shell === true,
243
+ });
244
+ return;
245
+ }
246
+ catch (error) {
247
+ lastError = error;
248
+ const code = error?.code || error?.status || 'unknown';
249
+ console.error(chalk_1.default.yellow(`[update] install attempt failed via ${attempt.label}: ${code}`));
250
+ }
251
+ }
252
+ const finalCode = lastError?.code || lastError?.status || 'unknown';
253
+ throw new Error(`Unable to launch npm installer after ${attempts.length} attempt(s). Last error: ${finalCode}`);
254
+ }
127
255
  // Check for updates silently on startup (non-blocking)
128
256
  async function checkForUpdatesQuietly() {
129
257
  try {
@@ -154,6 +282,127 @@ async function checkForUpdatesQuietly() {
154
282
  // Silently ignore - network issues shouldn't block CLI
155
283
  }
156
284
  }
285
+ const VIGTHORIA_GATEWAY_AUTH_FAILURE_MESSAGE = 'Vigthoria Gate way user authentification failed. Please log out and login again.';
286
+ function resolveRequestedCommand(argv) {
287
+ for (let i = 2; i < argv.length; i++) {
288
+ const token = String(argv[i] || '').trim();
289
+ if (!token || token.startsWith('-'))
290
+ continue;
291
+ return token;
292
+ }
293
+ return '';
294
+ }
295
+ function isAuthProtectedCommand(command) {
296
+ const protectedCommands = new Set([
297
+ 'chat', 'c', 'chat-resume',
298
+ 'agent', 'a', 'operator', 'op',
299
+ 'edit', 'e', 'generate', 'g', 'explain', 'x', 'fix', 'f', 'review', 'r',
300
+ 'workflow', 'flow', 'hub', 'marketplace', 'deploy', 'host', 'preview',
301
+ 'legion', 'history', 'runs', 'replay', 'fork', 'cancel',
302
+ 'repo', 'repository',
303
+ ]);
304
+ return protectedCommands.has(command);
305
+ }
306
+ async function enforceGatewayAuthSession(config, logger, jsonOutputRequested) {
307
+ if (!config.isAuthenticated()) {
308
+ return true;
309
+ }
310
+ const api = new api_js_1.APIClient(config, logger);
311
+ try {
312
+ const tokenCheck = await api.validateToken();
313
+ if (!tokenCheck.valid) {
314
+ if (jsonOutputRequested) {
315
+ process.exitCode = 1;
316
+ console.log(JSON.stringify({ success: false, error: VIGTHORIA_GATEWAY_AUTH_FAILURE_MESSAGE }, null, 2));
317
+ }
318
+ else {
319
+ console.log(chalk_1.default.red(VIGTHORIA_GATEWAY_AUTH_FAILURE_MESSAGE));
320
+ }
321
+ return false;
322
+ }
323
+ return true;
324
+ }
325
+ catch {
326
+ // Network or transient backend outages should not be misclassified as auth failure.
327
+ return true;
328
+ }
329
+ finally {
330
+ api.destroy();
331
+ }
332
+ }
333
+ function normalizeCliError(error) {
334
+ if (error instanceof Error) {
335
+ const extended = error;
336
+ const responseMessage = typeof extended.response?.data?.error === 'string'
337
+ ? extended.response.data.error
338
+ : typeof extended.response?.data?.message === 'string'
339
+ ? extended.response.data.message
340
+ : undefined;
341
+ return {
342
+ message: responseMessage || extended.message || 'An unexpected CLI error occurred.',
343
+ code: extended.code,
344
+ status: extended.status || extended.statusCode || extended.response?.status,
345
+ details: extended.details || extended.response?.data,
346
+ };
347
+ }
348
+ if (error && typeof error === 'object') {
349
+ const value = error;
350
+ return {
351
+ message: typeof value.message === 'string' && value.message.trim() ? value.message : 'An unexpected CLI error occurred.',
352
+ code: typeof value.code === 'string' || typeof value.code === 'number' ? value.code : undefined,
353
+ status: typeof value.status === 'number' ? value.status : undefined,
354
+ details: value.details,
355
+ };
356
+ }
357
+ return {
358
+ message: typeof error === 'string' && error.trim() ? error : 'An unexpected CLI error occurred.',
359
+ };
360
+ }
361
+ function formatCliError(error, jsonOutputRequested = false) {
362
+ const normalized = normalizeCliError(error);
363
+ if (jsonOutputRequested) {
364
+ return JSON.stringify({ success: false, error: normalized.message, code: normalized.code, status: normalized.status, details: normalized.details }, null, 2);
365
+ }
366
+ const parts = [chalk_1.default.red('Error:'), normalized.message];
367
+ if (normalized.code)
368
+ parts.push(chalk_1.default.gray(`[${normalized.code}]`));
369
+ if (normalized.status)
370
+ parts.push(chalk_1.default.gray(`(HTTP ${normalized.status})`));
371
+ return parts.join(' ');
372
+ }
373
+ function reportCliError(error, jsonOutputRequested = false) {
374
+ const output = formatCliError(error, jsonOutputRequested);
375
+ if (jsonOutputRequested) {
376
+ console.log(output);
377
+ }
378
+ else {
379
+ console.error(output);
380
+ }
381
+ }
382
+ function handleFatalCliError(error, jsonOutputRequested = false) {
383
+ reportCliError(error, jsonOutputRequested);
384
+ process.exitCode = 1;
385
+ }
386
+ let errorHandlersInstalled = false;
387
+ function setupErrorHandlers() {
388
+ if (errorHandlersInstalled) {
389
+ return;
390
+ }
391
+ errorHandlersInstalled = true;
392
+ process.on('unhandledRejection', (reason) => {
393
+ const rejection = reason instanceof Error ? reason : new Error(String(reason || 'Unhandled promise rejection'));
394
+ handleFatalCliError(rejection, process.argv.includes('--json'));
395
+ process.exit(1);
396
+ });
397
+ process.on('uncaughtException', (error) => {
398
+ handleFatalCliError(error, process.argv.includes('--json'));
399
+ });
400
+ process.on('warning', (warning) => {
401
+ if (process.env.VIGTHORIA_DEBUG === '1') {
402
+ reportCliError(warning, process.argv.includes('--json'));
403
+ }
404
+ });
405
+ }
157
406
  async function main() {
158
407
  const program = new commander_1.Command();
159
408
  const config = new config_js_2.Config();
@@ -172,6 +421,17 @@ async function main() {
172
421
  process.argv.splice(2, 0, 'chat');
173
422
  }
174
423
  }
424
+ const requestedCommand = resolveRequestedCommand(process.argv);
425
+ // Skip gateway JWT auth when running on-server with a service key (e.g., GodMode on-box).
426
+ // The service key is checked by Hyper Loop directly — no user session needed.
427
+ const hasServiceKey = !!(process.env.HYPERLOOP_SERVICE_KEY ||
428
+ process.env.V3_SERVICE_KEY);
429
+ if (!hasServiceKey && requestedCommand && isAuthProtectedCommand(requestedCommand)) {
430
+ const authOk = await enforceGatewayAuthSession(config, logger, jsonOutputRequested);
431
+ if (!authOk) {
432
+ return;
433
+ }
434
+ }
175
435
  // Banner - Fixed alignment with proper padding
176
436
  const boxWidth = 61; // Inner content width
177
437
  const titleText = 'VIGTHORIA CLI - AI-Powered Coding Assistant';
@@ -201,7 +461,7 @@ async function main() {
201
461
  .command('chat')
202
462
  .alias('c')
203
463
  .description('Start interactive chat with Vigthoria AI')
204
- .option('-m, --model <model>', 'Select AI model (fast, balanced, code, creative, cloud, ultra)')
464
+ .option('-m, --model <model>', 'Select AI model (agent, code, code-35b, code-9b, balanced, balanced-4b, cloud, ultra)')
205
465
  .option('-p, --project <path>', 'Set project context path')
206
466
  .option('--new-project [name]', 'Create or use a managed local workspace folder when no --project path is given')
207
467
  .option('-a, --agent', 'Enable agentic mode (default: true for best quality)', true)
@@ -231,7 +491,7 @@ async function main() {
231
491
  program
232
492
  .command('chat-resume')
233
493
  .description('Resume the latest chat session for the current or specified project')
234
- .option('-m, --model <model>', 'Select AI model (fast, balanced, code, creative, cloud, ultra)')
494
+ .option('-m, --model <model>', 'Select AI model (agent, code, code-35b, code-9b, balanced, balanced-4b, cloud, ultra)')
235
495
  .option('-p, --project <path>', 'Set project context path')
236
496
  .option('--new-project [name]', 'Create or use a managed local workspace folder when no --project path is given')
237
497
  .option('-a, --agent', 'Enable agentic mode (default: true for best quality)', true)
@@ -258,7 +518,7 @@ async function main() {
258
518
  });
259
519
  });
260
520
  // Agent command - Agentic mode (Vigthoria Autonomous)
261
- // Uses Vigthoria v3 Code 30B or Vigthoria Cloud for complex tasks
521
+ // Uses Vigthoria v3 Code 35B or Vigthoria Cloud for complex tasks
262
522
  program
263
523
  .command('agent')
264
524
  .alias('a')
@@ -728,13 +988,28 @@ Examples:
728
988
  .description('Run parallel tasks via Hyper Loop Legion orchestrator')
729
989
  .option('--workers', 'List available Legion workers')
730
990
  .option('--status', 'Show Legion infrastructure status')
991
+ .option('--godmode', 'Estimate, isolate, parallel-attack, and synthesize with strongest models')
992
+ .option('--approve', 'Auto-approve Godmode execution prompt')
993
+ .option('--no-approve', 'Require interactive approval before execution')
994
+ .option('--auto-charge', 'Attempt direct VigCoin top-up when Godmode balance is low')
995
+ .option('--plan-only', 'Run Godmode estimator only; do not execute Legion job')
996
+ .option('--models <csv>', 'Comma-separated model IDs to constrain Godmode selection')
997
+ .option('--timeout <seconds>', 'Override Legion execution timeout in seconds (defaults to server policy)')
731
998
  .option('-w, --worker <name>', 'Execute a specific worker')
732
999
  .option('-p, --project <path>', 'Project directory', process.cwd())
733
1000
  .action(async (request, options) => {
734
1001
  const legion = new legion_js_1.LegionCommand(config, logger);
1002
+ const parsedTimeout = Number.parseInt(String(options.timeout || ''), 10);
735
1003
  await legion.run(request, {
736
1004
  workers: options.workers,
737
1005
  status: options.status,
1006
+ godmode: options.godmode,
1007
+ approve: options.approve,
1008
+ noApprove: options.approve === false,
1009
+ autoCharge: options.autoCharge,
1010
+ planOnly: options.planOnly,
1011
+ models: options.models,
1012
+ timeoutSec: Number.isFinite(parsedTimeout) && parsedTimeout > 0 ? parsedTimeout : undefined,
738
1013
  worker: options.worker,
739
1014
  project: options.project,
740
1015
  });
@@ -788,22 +1063,33 @@ Examples:
788
1063
  .description('Login to Vigthoria Coder')
789
1064
  .option('-t, --token <token>', 'API token')
790
1065
  .action(async (options) => {
791
- const auth = new auth_js_1.AuthCommand(config, logger);
792
- await auth.login(options);
1066
+ await (0, auth_js_1.handleLogin)(options);
793
1067
  });
794
1068
  program
795
1069
  .command('logout')
796
1070
  .description('Logout from Vigthoria Coder')
797
1071
  .action(async () => {
798
- const auth = new auth_js_1.AuthCommand(config, logger);
799
- await auth.logout();
1072
+ await (0, auth_js_1.handleLogout)(null);
800
1073
  });
801
1074
  program
802
1075
  .command('status')
803
1076
  .description('Show authentication and subscription status')
804
1077
  .action(async () => {
805
- const auth = new auth_js_1.AuthCommand(config, logger);
806
- await auth.status();
1078
+ await (0, auth_js_1.statusAction)();
1079
+ });
1080
+ program
1081
+ .command('doctor')
1082
+ .description('Run local Vigthoria CLI diagnostics')
1083
+ .action(() => {
1084
+ const checks = [
1085
+ ['Node.js', process.version],
1086
+ ['Platform', `linux x64`],
1087
+ ['Working directory', process.cwd()],
1088
+ ];
1089
+ console.log('Vigthoria CLI diagnostics');
1090
+ for (const [label, value] of checks) {
1091
+ console.log(`- ${label}: ${value}`);
1092
+ }
807
1093
  });
808
1094
  // Config command
809
1095
  program
@@ -821,11 +1107,108 @@ Examples:
821
1107
  program
822
1108
  .command('update')
823
1109
  .alias('upgrade')
824
- .description('Check for updates and upgrade Vigthoria CLI')
1110
+ .description('Check for updates and upgrade Vigthoria CLI. Default manifest: https://coder.vigthoria.io/releases/manifest.json')
825
1111
  .option('-c, --check', 'Only check for updates, don\'t install')
1112
+ .option('-f, --from <target>', 'Install update from npm spec, local .tgz path, or URL')
1113
+ .option('-m, --manifest <url>', 'Update manifest URL for server-driven releases')
1114
+ .option('--channel <name>', 'Release channel to use from manifest (default: stable)', 'stable')
1115
+ .option('--allow-downgrade', 'Allow installing an older version from custom update source')
826
1116
  .action(async (options) => {
827
1117
  const { execSync } = await import('child_process');
828
- console.log(chalk_1.default.cyan('🔍 Checking for updates...'));
1118
+ const updateTarget = typeof options.from === 'string' ? options.from.trim() : '';
1119
+ const manifestUrl = typeof options.manifest === 'string' ? options.manifest.trim() : VIGTHORIA_DEFAULT_MANIFEST_URL.trim();
1120
+ const channel = typeof options.channel === 'string' ? options.channel.trim() : 'stable';
1121
+ const allowDowngrade = !!options.allowDowngrade;
1122
+ const gitPackageSpec = 'git+https://market.vigthoria.io/vigthoria/vigthoria-cli.git';
1123
+ if (updateTarget) {
1124
+ try {
1125
+ if (options.check) {
1126
+ console.log(chalk_1.default.cyan(`Update source configured: ${updateTarget}`));
1127
+ console.log(chalk_1.default.gray('Run `vigthoria update --from <target>` to install from this source'));
1128
+ return;
1129
+ }
1130
+ console.log(chalk_1.default.cyan(`Installing update from ${updateTarget}...`));
1131
+ await installGlobalPackageWithNpm(updateTarget);
1132
+ console.log(chalk_1.default.green('Update installed successfully'));
1133
+ console.log(chalk_1.default.gray('Please restart the CLI to use the new version'));
1134
+ return;
1135
+ }
1136
+ catch (error) {
1137
+ console.error(chalk_1.default.red('Failed to install update from target:'), error.message);
1138
+ console.log(chalk_1.default.gray(`Try manually: npm install -g ${updateTarget}`));
1139
+ process.exitCode = 1;
1140
+ return;
1141
+ }
1142
+ }
1143
+ if (manifestUrl) {
1144
+ try {
1145
+ console.log(chalk_1.default.cyan(`Checking manifest channel ${channel}...`));
1146
+ const response = await axios_1.default.get(manifestUrl, {
1147
+ timeout: 10000,
1148
+ maxRedirects: 5,
1149
+ validateStatus: (status) => status >= 200 && status < 300,
1150
+ });
1151
+ const entry = resolveManifestEntry(response.data || {}, channel);
1152
+ if (!entry || !entry.version || !entry.url) {
1153
+ console.error(chalk_1.default.red(`Manifest missing valid release entry for channel: ${channel}`));
1154
+ console.log(chalk_1.default.gray('Expected: channels.<channel>.version and channels.<channel>.url'));
1155
+ process.exitCode = 1;
1156
+ return;
1157
+ }
1158
+ const currentVersion = VERSION;
1159
+ const comparison = compareVersions(entry.version, currentVersion);
1160
+ if (!allowDowngrade && comparison <= 0) {
1161
+ console.log(chalk_1.default.green(`You are running the latest version for channel ${channel} (${currentVersion})`));
1162
+ return;
1163
+ }
1164
+ console.log(chalk_1.default.yellow(`Update available from manifest: ${currentVersion} -> ${entry.version} (${channel})`));
1165
+ if (entry.notes) {
1166
+ console.log(chalk_1.default.gray(` ${entry.notes}`));
1167
+ }
1168
+ if (options.check) {
1169
+ console.log(chalk_1.default.gray(`Install with: vigthoria update --manifest ${manifestUrl} --channel ${channel}`));
1170
+ return;
1171
+ }
1172
+ const tmpFile = path.join(os.tmpdir(), `vigthoria-update-${Date.now()}.tgz`);
1173
+ try {
1174
+ console.log(chalk_1.default.cyan('Downloading release package...'));
1175
+ await downloadFile(entry.url, tmpFile);
1176
+ if (entry.sha256) {
1177
+ const expected = entry.sha256.toLowerCase();
1178
+ const actual = sha256File(tmpFile).toLowerCase();
1179
+ if (actual !== expected) {
1180
+ console.error(chalk_1.default.red('Release checksum verification failed'));
1181
+ console.error(chalk_1.default.red(`Expected: ${expected}`));
1182
+ console.error(chalk_1.default.red(`Actual: ${actual}`));
1183
+ process.exitCode = 1;
1184
+ return;
1185
+ }
1186
+ console.log(chalk_1.default.green('Checksum verification passed'));
1187
+ }
1188
+ else {
1189
+ console.log(chalk_1.default.yellow('Manifest entry has no sha256; install proceeded without checksum verification'));
1190
+ }
1191
+ console.log(chalk_1.default.cyan('Installing update...'));
1192
+ await installGlobalPackageWithNpm(tmpFile);
1193
+ console.log(chalk_1.default.green(`Updated to version ${entry.version}`));
1194
+ console.log(chalk_1.default.gray('Please restart the CLI to use the new version'));
1195
+ return;
1196
+ }
1197
+ finally {
1198
+ if (fs.existsSync(tmpFile)) {
1199
+ try {
1200
+ fs.unlinkSync(tmpFile);
1201
+ }
1202
+ catch { }
1203
+ }
1204
+ }
1205
+ }
1206
+ catch (error) {
1207
+ console.error(chalk_1.default.red('Failed to process manifest update:'), error.message);
1208
+ console.log(chalk_1.default.gray('Falling back to npm/git update channels...'));
1209
+ }
1210
+ }
1211
+ console.log(chalk_1.default.cyan('Checking for updates...'));
829
1212
  try {
830
1213
  // Get latest version from npm - cross-platform
831
1214
  const latestVersion = execSync('npm view vigthoria-cli version', {
@@ -837,24 +1220,69 @@ Examples:
837
1220
  // Use semantic version comparison (1.6.0 > 1.5.9)
838
1221
  const comparison = compareVersions(latestVersion, currentVersion);
839
1222
  if (comparison <= 0) {
840
- console.log(chalk_1.default.green(`✅ You are running the latest version (${currentVersion})`));
1223
+ console.log(chalk_1.default.green(`You are running the latest version (${currentVersion})`));
841
1224
  return;
842
1225
  }
843
- console.log(chalk_1.default.yellow(`📦 Update available: ${currentVersion} ${latestVersion}`));
1226
+ console.log(chalk_1.default.yellow(`Update available: ${currentVersion} -> ${latestVersion}`));
844
1227
  if (options.check) {
845
1228
  console.log(chalk_1.default.gray('Run `vigthoria update` to install the update'));
846
1229
  return;
847
1230
  }
848
- console.log(chalk_1.default.cyan('📥 Installing update...'));
849
- execSync('npm install -g vigthoria-cli@latest', { stdio: 'inherit' });
850
- console.log(chalk_1.default.green(`✅ Updated to version ${latestVersion}`));
1231
+ console.log(chalk_1.default.cyan('Installing update from npm registry...'));
1232
+ await installGlobalPackageWithNpm('vigthoria-cli@latest');
1233
+ console.log(chalk_1.default.green(`Updated to version ${latestVersion}`));
851
1234
  console.log(chalk_1.default.gray('Please restart the CLI to use the new version'));
852
1235
  }
853
1236
  catch (error) {
854
- console.error(chalk_1.default.red('Failed to check for updates:'), error.message);
855
- console.log(chalk_1.default.gray('Try manually: npm install -g vigthoria-cli@latest'));
1237
+ console.error(chalk_1.default.red('Failed to check/install via npm registry:'), error.message);
1238
+ if (options.check) {
1239
+ console.log(chalk_1.default.gray(`npm registry check failed; fallback install target is ${gitPackageSpec}`));
1240
+ return;
1241
+ }
1242
+ try {
1243
+ console.log(chalk_1.default.cyan('Attempting git package fallback...'));
1244
+ await installGlobalPackageWithNpm(gitPackageSpec);
1245
+ console.log(chalk_1.default.green('Updated via git package fallback'));
1246
+ console.log(chalk_1.default.gray('Please restart the CLI to use the new version'));
1247
+ }
1248
+ catch (fallbackError) {
1249
+ console.error(chalk_1.default.red('Fallback update also failed:'), fallbackError.message);
1250
+ console.log(chalk_1.default.gray('Try manually: npm install -g vigthoria-cli@latest'));
1251
+ console.log(chalk_1.default.gray(`Or: npm install -g ${gitPackageSpec}`));
1252
+ process.exitCode = 1;
1253
+ }
856
1254
  }
857
1255
  });
1256
+ // Hyper Loop command alias (maps to legion --status for checklist gate 6.9)
1257
+ program
1258
+ .command('hyper-loop')
1259
+ .description('Hyper Loop Legion orchestrator commands')
1260
+ .command('status')
1261
+ .description('Show Hyper Loop Legion infrastructure status')
1262
+ .action(async () => {
1263
+ const legion = new legion_js_1.LegionCommand(config, logger);
1264
+ await legion.run(undefined, {
1265
+ status: true,
1266
+ workers: false,
1267
+ godmode: false,
1268
+ approve: false,
1269
+ noApprove: true,
1270
+ planOnly: false,
1271
+ models: undefined,
1272
+ worker: undefined,
1273
+ project: process.cwd(),
1274
+ });
1275
+ });
1276
+ // DevTools command alias (maps to bridge for checklist gate 6.12)
1277
+ program
1278
+ .command('devtools')
1279
+ .description('DevTools Bridge commands for browser debugging')
1280
+ .command('connect')
1281
+ .description('Connect to DevTools Bridge')
1282
+ .action(async () => {
1283
+ const bridge = new bridge_js_1.BridgeCommand(config, logger);
1284
+ await bridge.status();
1285
+ });
858
1286
  // Init command - Initialize project
859
1287
  program
860
1288
  .command('init')
@@ -863,15 +1291,65 @@ Examples:
863
1291
  const configCmd = new config_js_1.ConfigCommand(config, logger);
864
1292
  await configCmd.init();
865
1293
  });
866
- // Default to chat if no command
867
- if (process.argv.length === 2) {
868
- const chat = new chat_js_1.ChatCommand(config, logger);
869
- await chat.run({ model: 'code', project: process.cwd() });
870
- return;
1294
+ const codingCommandDefinitions = [
1295
+ { name: 'edit', description: 'Edit code by describing the desired change', instruction: 'Edit the project according to this request' },
1296
+ { name: 'generate', description: 'Generate code from a prompt', instruction: 'Generate code for this request' },
1297
+ { name: 'explain', description: 'Explain code, errors, or project behavior', instruction: 'Explain this request clearly and practically' },
1298
+ { name: 'review', description: 'Review code and suggest concrete improvements', instruction: 'Review the project or code for this request' },
1299
+ { name: 'fix', description: 'Fix bugs, build failures, or test failures', instruction: 'Fix this issue in the project' },
1300
+ ];
1301
+ for (const commandDefinition of codingCommandDefinitions) {
1302
+ program
1303
+ .command(commandDefinition.name)
1304
+ .description(commandDefinition.description)
1305
+ .argument('[request...]', 'Request text for the coding assistant')
1306
+ .option('-m, --model <model>', 'Model to use', 'code')
1307
+ .option('-p, --project <path>', 'Project directory', process.cwd())
1308
+ .action(async (requestParts = [], options) => {
1309
+ const requestText = requestParts.join(' ').trim();
1310
+ const chat = new chat_js_1.ChatCommand(config, logger);
1311
+ await chat.run({
1312
+ model: options.model || 'code',
1313
+ project: options.project || process.cwd(),
1314
+ prompt: requestText ? `${commandDefinition.instruction}: ${requestText}` : commandDefinition.instruction,
1315
+ });
1316
+ });
1317
+ }
1318
+ try {
1319
+ // Default to chat if no command
1320
+ if (process.argv.length === 2) {
1321
+ const chat = new chat_js_1.ChatCommand(config, logger);
1322
+ await chat.run({ model: 'code', project: process.cwd() });
1323
+ return;
1324
+ }
1325
+ await program.parseAsync(process.argv);
1326
+ }
1327
+ catch (error) {
1328
+ reportCliError(error, jsonOutputRequested);
1329
+ process.exitCode = 1;
871
1330
  }
872
- await program.parseAsync(process.argv);
873
1331
  }
874
- main().catch((err) => {
875
- console.error(chalk_1.default.red('Error:'), err.message);
876
- process.exitCode = 1;
1332
+ setupErrorHandlers();
1333
+ process.on('unhandledRejection', (reason) => {
1334
+ handleFatalCliError(reason, process.argv.includes('--json'));
1335
+ process.exit(1);
877
1336
  });
1337
+ async function bootstrapCli() {
1338
+ try {
1339
+ await main();
1340
+ }
1341
+ catch (err) {
1342
+ handleFatalCliError(err, process.argv.includes('--json'));
1343
+ process.exit(1);
1344
+ }
1345
+ }
1346
+ void (async () => {
1347
+ try {
1348
+ await bootstrapCli();
1349
+ }
1350
+ catch (err) {
1351
+ handleFatalCliError(err, process.argv.includes('--json'));
1352
+ process.exit(1);
1353
+ }
1354
+ })();
1355
+ exports.__cliErrorHandlingReady = true;