vigthoria-cli 1.8.19 → 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");
@@ -80,6 +83,14 @@ const path = __importStar(require("path"));
80
83
  const os = __importStar(require("os"));
81
84
  const crypto_1 = require("crypto");
82
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
+ }
83
94
  function getInvokedBinaryName() {
84
95
  const executable = process.argv[1] || 'vigthoria';
85
96
  return path.basename(executable, path.extname(executable)).toLowerCase();
@@ -107,7 +118,7 @@ function getVersion() {
107
118
  catch (e) {
108
119
  // Fallback to hardcoded version
109
120
  }
110
- return '1.6.27';
121
+ return '1.9.2';
111
122
  }
112
123
  const VERSION = getVersion();
113
124
  const VIGTHORIA_DEFAULT_MANIFEST_URL = process.env.VIGTHORIA_UPDATE_MANIFEST_URL || "https://coder.vigthoria.io/releases/manifest.json";
@@ -162,6 +173,85 @@ function sha256File(filePath) {
162
173
  hash.update(data);
163
174
  return hash.digest('hex');
164
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
+ }
165
255
  // Check for updates silently on startup (non-blocking)
166
256
  async function checkForUpdatesQuietly() {
167
257
  try {
@@ -192,6 +282,127 @@ async function checkForUpdatesQuietly() {
192
282
  // Silently ignore - network issues shouldn't block CLI
193
283
  }
194
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
+ }
195
406
  async function main() {
196
407
  const program = new commander_1.Command();
197
408
  const config = new config_js_2.Config();
@@ -210,6 +421,17 @@ async function main() {
210
421
  process.argv.splice(2, 0, 'chat');
211
422
  }
212
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
+ }
213
435
  // Banner - Fixed alignment with proper padding
214
436
  const boxWidth = 61; // Inner content width
215
437
  const titleText = 'VIGTHORIA CLI - AI-Powered Coding Assistant';
@@ -239,7 +461,7 @@ async function main() {
239
461
  .command('chat')
240
462
  .alias('c')
241
463
  .description('Start interactive chat with Vigthoria AI')
242
- .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)')
243
465
  .option('-p, --project <path>', 'Set project context path')
244
466
  .option('--new-project [name]', 'Create or use a managed local workspace folder when no --project path is given')
245
467
  .option('-a, --agent', 'Enable agentic mode (default: true for best quality)', true)
@@ -269,7 +491,7 @@ async function main() {
269
491
  program
270
492
  .command('chat-resume')
271
493
  .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)')
494
+ .option('-m, --model <model>', 'Select AI model (agent, code, code-35b, code-9b, balanced, balanced-4b, cloud, ultra)')
273
495
  .option('-p, --project <path>', 'Set project context path')
274
496
  .option('--new-project [name]', 'Create or use a managed local workspace folder when no --project path is given')
275
497
  .option('-a, --agent', 'Enable agentic mode (default: true for best quality)', true)
@@ -296,7 +518,7 @@ async function main() {
296
518
  });
297
519
  });
298
520
  // Agent command - Agentic mode (Vigthoria Autonomous)
299
- // Uses Vigthoria v3 Code 30B or Vigthoria Cloud for complex tasks
521
+ // Uses Vigthoria v3 Code 35B or Vigthoria Cloud for complex tasks
300
522
  program
301
523
  .command('agent')
302
524
  .alias('a')
@@ -766,13 +988,28 @@ Examples:
766
988
  .description('Run parallel tasks via Hyper Loop Legion orchestrator')
767
989
  .option('--workers', 'List available Legion workers')
768
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)')
769
998
  .option('-w, --worker <name>', 'Execute a specific worker')
770
999
  .option('-p, --project <path>', 'Project directory', process.cwd())
771
1000
  .action(async (request, options) => {
772
1001
  const legion = new legion_js_1.LegionCommand(config, logger);
1002
+ const parsedTimeout = Number.parseInt(String(options.timeout || ''), 10);
773
1003
  await legion.run(request, {
774
1004
  workers: options.workers,
775
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,
776
1013
  worker: options.worker,
777
1014
  project: options.project,
778
1015
  });
@@ -826,22 +1063,33 @@ Examples:
826
1063
  .description('Login to Vigthoria Coder')
827
1064
  .option('-t, --token <token>', 'API token')
828
1065
  .action(async (options) => {
829
- const auth = new auth_js_1.AuthCommand(config, logger);
830
- await auth.login(options);
1066
+ await (0, auth_js_1.handleLogin)(options);
831
1067
  });
832
1068
  program
833
1069
  .command('logout')
834
1070
  .description('Logout from Vigthoria Coder')
835
1071
  .action(async () => {
836
- const auth = new auth_js_1.AuthCommand(config, logger);
837
- await auth.logout();
1072
+ await (0, auth_js_1.handleLogout)(null);
838
1073
  });
839
1074
  program
840
1075
  .command('status')
841
1076
  .description('Show authentication and subscription status')
842
1077
  .action(async () => {
843
- const auth = new auth_js_1.AuthCommand(config, logger);
844
- 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
+ }
845
1093
  });
846
1094
  // Config command
847
1095
  program
@@ -866,11 +1114,12 @@ Examples:
866
1114
  .option('--channel <name>', 'Release channel to use from manifest (default: stable)', 'stable')
867
1115
  .option('--allow-downgrade', 'Allow installing an older version from custom update source')
868
1116
  .action(async (options) => {
869
- const { execSync, execFileSync } = await import('child_process');
1117
+ const { execSync } = await import('child_process');
870
1118
  const updateTarget = typeof options.from === 'string' ? options.from.trim() : '';
871
1119
  const manifestUrl = typeof options.manifest === 'string' ? options.manifest.trim() : VIGTHORIA_DEFAULT_MANIFEST_URL.trim();
872
1120
  const channel = typeof options.channel === 'string' ? options.channel.trim() : 'stable';
873
1121
  const allowDowngrade = !!options.allowDowngrade;
1122
+ const gitPackageSpec = 'git+https://market.vigthoria.io/vigthoria/vigthoria-cli.git';
874
1123
  if (updateTarget) {
875
1124
  try {
876
1125
  if (options.check) {
@@ -879,10 +1128,7 @@ Examples:
879
1128
  return;
880
1129
  }
881
1130
  console.log(chalk_1.default.cyan(`Installing update from ${updateTarget}...`));
882
- execFileSync('npm', ['install', '-g', updateTarget], {
883
- stdio: 'inherit',
884
- windowsHide: true,
885
- });
1131
+ await installGlobalPackageWithNpm(updateTarget);
886
1132
  console.log(chalk_1.default.green('Update installed successfully'));
887
1133
  console.log(chalk_1.default.gray('Please restart the CLI to use the new version'));
888
1134
  return;
@@ -890,6 +1136,7 @@ Examples:
890
1136
  catch (error) {
891
1137
  console.error(chalk_1.default.red('Failed to install update from target:'), error.message);
892
1138
  console.log(chalk_1.default.gray(`Try manually: npm install -g ${updateTarget}`));
1139
+ process.exitCode = 1;
893
1140
  return;
894
1141
  }
895
1142
  }
@@ -905,6 +1152,7 @@ Examples:
905
1152
  if (!entry || !entry.version || !entry.url) {
906
1153
  console.error(chalk_1.default.red(`Manifest missing valid release entry for channel: ${channel}`));
907
1154
  console.log(chalk_1.default.gray('Expected: channels.<channel>.version and channels.<channel>.url'));
1155
+ process.exitCode = 1;
908
1156
  return;
909
1157
  }
910
1158
  const currentVersion = VERSION;
@@ -932,6 +1180,7 @@ Examples:
932
1180
  console.error(chalk_1.default.red('Release checksum verification failed'));
933
1181
  console.error(chalk_1.default.red(`Expected: ${expected}`));
934
1182
  console.error(chalk_1.default.red(`Actual: ${actual}`));
1183
+ process.exitCode = 1;
935
1184
  return;
936
1185
  }
937
1186
  console.log(chalk_1.default.green('Checksum verification passed'));
@@ -940,10 +1189,7 @@ Examples:
940
1189
  console.log(chalk_1.default.yellow('Manifest entry has no sha256; install proceeded without checksum verification'));
941
1190
  }
942
1191
  console.log(chalk_1.default.cyan('Installing update...'));
943
- execFileSync('npm', ['install', '-g', tmpFile], {
944
- stdio: 'inherit',
945
- windowsHide: true,
946
- });
1192
+ await installGlobalPackageWithNpm(tmpFile);
947
1193
  console.log(chalk_1.default.green(`Updated to version ${entry.version}`));
948
1194
  console.log(chalk_1.default.gray('Please restart the CLI to use the new version'));
949
1195
  return;
@@ -959,8 +1205,7 @@ Examples:
959
1205
  }
960
1206
  catch (error) {
961
1207
  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;
1208
+ console.log(chalk_1.default.gray('Falling back to npm/git update channels...'));
964
1209
  }
965
1210
  }
966
1211
  console.log(chalk_1.default.cyan('Checking for updates...'));
@@ -983,16 +1228,61 @@ Examples:
983
1228
  console.log(chalk_1.default.gray('Run `vigthoria update` to install the update'));
984
1229
  return;
985
1230
  }
986
- console.log(chalk_1.default.cyan('Installing update...'));
987
- execSync('npm install -g vigthoria-cli@latest', { stdio: 'inherit' });
1231
+ console.log(chalk_1.default.cyan('Installing update from npm registry...'));
1232
+ await installGlobalPackageWithNpm('vigthoria-cli@latest');
988
1233
  console.log(chalk_1.default.green(`Updated to version ${latestVersion}`));
989
1234
  console.log(chalk_1.default.gray('Please restart the CLI to use the new version'));
990
1235
  }
991
1236
  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'));
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
+ }
994
1254
  }
995
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
+ });
996
1286
  // Init command - Initialize project
997
1287
  program
998
1288
  .command('init')
@@ -1001,15 +1291,65 @@ Examples:
1001
1291
  const configCmd = new config_js_1.ConfigCommand(config, logger);
1002
1292
  await configCmd.init();
1003
1293
  });
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;
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;
1009
1330
  }
1010
- await program.parseAsync(process.argv);
1011
1331
  }
1012
- main().catch((err) => {
1013
- console.error(chalk_1.default.red('Error:'), err.message);
1014
- process.exitCode = 1;
1332
+ setupErrorHandlers();
1333
+ process.on('unhandledRejection', (reason) => {
1334
+ handleFatalCliError(reason, process.argv.includes('--json'));
1335
+ process.exit(1);
1015
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;
@@ -9,12 +9,30 @@ export declare class CLIError extends Error {
9
9
  category: CLIErrorCategory;
10
10
  statusCode?: number;
11
11
  endpoint?: string;
12
+ code: string;
13
+ details?: any;
14
+ isCritical: boolean;
12
15
  constructor(message: string, category: CLIErrorCategory, opts?: {
13
16
  statusCode?: number;
14
17
  endpoint?: string;
15
18
  cause?: Error;
16
19
  });
17
20
  }
21
+ export type CliError = {
22
+ code: string;
23
+ message: string;
24
+ details?: any;
25
+ isCritical: boolean;
26
+ };
27
+ export type ApiError = {
28
+ code: number;
29
+ message: string;
30
+ details?: any;
31
+ isAuthError: boolean;
32
+ };
33
+ export declare function handleApiError(error: any): CliError;
34
+ export declare function handleAuthError(error: any): CliError;
35
+ export declare function propagateError(err: any): never;
18
36
  /** Classify an axios or fetch error into a structured CLIError. */
19
37
  export declare function classifyError(error: unknown, fallbackCategory?: CLIErrorCategory): CLIError;
20
38
  /** Format a CLIError for user-facing display. */
@@ -189,11 +207,34 @@ export interface VigthoriUser {
189
207
  adminAccess: boolean;
190
208
  };
191
209
  }
210
+ export interface JwtPayload {
211
+ exp?: number;
212
+ iat?: number;
213
+ sub?: string;
214
+ [claim: string]: unknown;
215
+ }
216
+ export type JwtState = {
217
+ token: string | null;
218
+ expiresAt: number | null;
219
+ isExpired?: () => boolean;
220
+ };
221
+ export declare function validateJwtExpiry(token: string): boolean;
222
+ export declare function validateJwt(token: string): JwtPayload | null;
223
+ export type ApiConfig = Config;
224
+ export type ApiClient = APIClient;
225
+ export declare function refreshJwtIfNeeded(state: JwtState): Promise<string | null>;
226
+ export declare function createApiClient(config: any): {
227
+ get: (path: string) => Promise<any>;
228
+ post: (path: string, body: any) => Promise<any>;
229
+ handleAuthError: (err: any) => void;
230
+ };
192
231
  export declare class APIClient {
193
232
  private client;
194
233
  private modelRouterClient;
195
234
  private selfHostedModelRouterClient;
196
- private config;
235
+ config: Config;
236
+ token: string | null;
237
+ expiresAt: number | null;
197
238
  private logger;
198
239
  private ws;
199
240
  private vigFlowTokens;
@@ -226,6 +267,7 @@ export declare class APIClient {
226
267
  getMcpBaseUrls(): string[];
227
268
  getVigFlowBaseUrls(): string[];
228
269
  getTemplateServiceBaseUrls(): string[];
270
+ private allowLocalServiceFallbacks;
229
271
  private isFrontendTask;
230
272
  /**
231
273
  * Returns true when the prompt describes a read-only / analysis task.
@@ -439,6 +481,24 @@ export declare class APIClient {
439
481
  * the fix has fewer closers than the original, appends the missing ones.
440
482
  */
441
483
  private repairBracketBalance;
484
+ /**
485
+ * Build workspace summary re-ordered by semantic relevance to the prompt.
486
+ * Changed files are listed first, then keyword-matched files, then the rest.
487
+ * Falls back to plain buildLocalWorkspaceSummary when no prompt is provided.
488
+ */
489
+ private buildSemanticWorkspaceSummary;
490
+ /**
491
+ * Self-healing cycle: run post-write validators and, if errors are found,
492
+ * send a targeted correction prompt to the V3 agent (max one healing round).
493
+ *
494
+ * This is a best-effort operation — failures never propagate to the user as
495
+ * hard errors; they are surfaced as a status line in the terminal output.
496
+ */
497
+ runSelfHealingCycle(originalPrompt: string, workspacePath: string, context?: Record<string, any>): Promise<{
498
+ healingAttempted: boolean;
499
+ passed: boolean;
500
+ tool: string;
501
+ }>;
442
502
  private resolveModelId;
443
503
  private getCoderHealth;
444
504
  private getModelsHealth;