vigthoria-cli 1.10.47 → 1.10.48

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.
Files changed (55) hide show
  1. package/dist/commands/auth.js +51 -68
  2. package/dist/commands/bridge.js +12 -19
  3. package/dist/commands/cancel.js +15 -22
  4. package/dist/commands/chat.d.ts +28 -0
  5. package/dist/commands/config.js +33 -73
  6. package/dist/commands/deploy.js +83 -123
  7. package/dist/commands/device.js +21 -61
  8. package/dist/commands/edit.js +32 -39
  9. package/dist/commands/explain.js +18 -25
  10. package/dist/commands/fork.d.ts +17 -0
  11. package/dist/commands/fork.js +164 -0
  12. package/dist/commands/generate.js +37 -44
  13. package/dist/commands/history.d.ts +17 -0
  14. package/dist/commands/history.js +113 -0
  15. package/dist/commands/hub.js +95 -102
  16. package/dist/commands/index.js +41 -46
  17. package/dist/commands/legion.js +146 -186
  18. package/dist/commands/preview.d.ts +55 -0
  19. package/dist/commands/preview.js +467 -0
  20. package/dist/commands/replay.d.ts +18 -0
  21. package/dist/commands/replay.js +156 -0
  22. package/dist/commands/repo.d.ts +97 -0
  23. package/dist/commands/repo.js +773 -0
  24. package/dist/commands/review.js +29 -36
  25. package/dist/commands/security.js +5 -12
  26. package/dist/commands/update.d.ts +9 -0
  27. package/dist/commands/update.js +201 -0
  28. package/dist/commands/wallet.js +28 -35
  29. package/dist/commands/workflow.js +13 -20
  30. package/dist/index.d.ts +21 -0
  31. package/dist/index.js +1826 -0
  32. package/dist/utils/api.d.ts +572 -0
  33. package/dist/utils/api.js +6629 -0
  34. package/dist/utils/brain-hub-client.js +1 -5
  35. package/dist/utils/bridge-client.js +11 -52
  36. package/dist/utils/cli-state.d.ts +54 -0
  37. package/dist/utils/cli-state.js +185 -0
  38. package/dist/utils/codebase-indexer.js +4 -41
  39. package/dist/utils/config.d.ts +85 -0
  40. package/dist/utils/config.js +267 -0
  41. package/dist/utils/context-ranker.js +15 -21
  42. package/dist/utils/files.js +5 -42
  43. package/dist/utils/logger.js +42 -50
  44. package/dist/utils/persona.js +3 -8
  45. package/dist/utils/post-write-validator.js +22 -29
  46. package/dist/utils/project-memory.js +16 -23
  47. package/dist/utils/session.d.ts +118 -0
  48. package/dist/utils/session.js +423 -0
  49. package/dist/utils/task-display.js +13 -20
  50. package/dist/utils/tools.d.ts +276 -0
  51. package/dist/utils/tools.js +3522 -0
  52. package/dist/utils/workspace-brain-service.js +8 -45
  53. package/dist/utils/workspace-cache.js +18 -26
  54. package/dist/utils/workspace-stream.js +21 -63
  55. package/package.json +1 -1
@@ -1,51 +1,12 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.DeviceCommand = void 0;
40
- const chalk_1 = __importDefault(require("chalk"));
41
- const fs = __importStar(require("fs"));
42
- const os = __importStar(require("os"));
43
- const path = __importStar(require("path"));
44
- const child_process_1 = require("child_process");
45
- const util_1 = require("util");
46
- const logger_js_1 = require("../utils/logger.js");
47
- const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
48
- class DeviceCommand {
1
+ import chalk from 'chalk';
2
+ import * as fs from 'fs';
3
+ import * as os from 'os';
4
+ import * as path from 'path';
5
+ import { execFile, spawn } from 'child_process';
6
+ import { promisify } from 'util';
7
+ import { CH } from '../utils/logger.js';
8
+ const execFileAsync = promisify(execFile);
9
+ export class DeviceCommand {
49
10
  logger;
50
11
  constructor(_config, logger) {
51
12
  this.logger = logger;
@@ -64,23 +25,23 @@ class DeviceCommand {
64
25
  return;
65
26
  }
66
27
  console.log();
67
- console.log(chalk_1.default.bold.white(` ${logger_js_1.CH.hLine.repeat(3)} Vigthoria Android Developer Bridge ${logger_js_1.CH.hLine.repeat(20)}`));
68
- console.log(chalk_1.default.gray(' ADB: ') + (adbPath ? chalk_1.default.green(adbPath) : chalk_1.default.yellow('not found')));
28
+ console.log(chalk.bold.white(` ${CH.hLine.repeat(3)} Vigthoria Android Developer Bridge ${CH.hLine.repeat(20)}`));
29
+ console.log(chalk.gray(' ADB: ') + (adbPath ? chalk.green(adbPath) : chalk.yellow('not found')));
69
30
  if (!adbPath) {
70
- console.log(chalk_1.default.gray(' Install Android SDK Platform Tools, then make adb available on PATH.'));
31
+ console.log(chalk.gray(' Install Android SDK Platform Tools, then make adb available on PATH.'));
71
32
  console.log();
72
33
  return;
73
34
  }
74
35
  if (devices.length === 0) {
75
- console.log(chalk_1.default.yellow(' No Android devices visible.'));
76
- console.log(chalk_1.default.gray(' Enable Developer Options + USB debugging, then run `vigthoria device list`.'));
36
+ console.log(chalk.yellow(' No Android devices visible.'));
37
+ console.log(chalk.gray(' Enable Developer Options + USB debugging, then run `vigthoria device list`.'));
77
38
  console.log();
78
39
  return;
79
40
  }
80
41
  for (const device of devices) {
81
42
  const label = device.model || device.product || 'Android device';
82
- const state = device.state === 'device' ? chalk_1.default.green(device.state) : chalk_1.default.yellow(device.state);
83
- console.log(` ${chalk_1.default.white(device.id)} ${state} ${chalk_1.default.gray(label)}`);
43
+ const state = device.state === 'device' ? chalk.green(device.state) : chalk.yellow(device.state);
44
+ console.log(` ${chalk.white(device.id)} ${state} ${chalk.gray(label)}`);
84
45
  }
85
46
  console.log();
86
47
  }
@@ -92,11 +53,11 @@ class DeviceCommand {
92
53
  return;
93
54
  }
94
55
  if (devices.length === 0) {
95
- console.log(chalk_1.default.yellow('No Android devices found.'));
56
+ console.log(chalk.yellow('No Android devices found.'));
96
57
  return;
97
58
  }
98
59
  for (const device of devices) {
99
- console.log(`${chalk_1.default.white(device.id)}\t${device.state}\t${device.model || device.product || ''}`.trim());
60
+ console.log(`${chalk.white(device.id)}\t${device.state}\t${device.model || device.product || ''}`.trim());
100
61
  }
101
62
  }
102
63
  async screenshot(options = {}) {
@@ -106,7 +67,7 @@ class DeviceCommand {
106
67
  const args = this.withDevice(deviceId, ['exec-out', 'screencap', '-p']);
107
68
  const result = await execFileAsync(adbPath, args, { encoding: 'buffer', maxBuffer: 20 * 1024 * 1024 });
108
69
  fs.writeFileSync(output, result.stdout);
109
- console.log(chalk_1.default.green(`Saved screenshot: ${output}`));
70
+ console.log(chalk.green(`Saved screenshot: ${output}`));
110
71
  }
111
72
  async install(apkPath, options = {}) {
112
73
  if (!apkPath)
@@ -137,7 +98,7 @@ class DeviceCommand {
137
98
  const args = this.withDevice(deviceId, options.follow ? ['logcat'] : ['logcat', '-d', '-t', lines]);
138
99
  if (options.follow) {
139
100
  await new Promise((resolve, reject) => {
140
- const child = (0, child_process_1.spawn)(adbPath, args, { stdio: 'inherit' });
101
+ const child = spawn(adbPath, args, { stdio: 'inherit' });
141
102
  child.on('error', reject);
142
103
  child.on('exit', (code) => {
143
104
  if (code && code !== 0)
@@ -235,7 +196,7 @@ class DeviceCommand {
235
196
  if (result.stdout)
236
197
  console.log(result.stdout);
237
198
  if (result.stderr)
238
- console.error(result.success ? chalk_1.default.gray(result.stderr) : chalk_1.default.red(result.stderr));
199
+ console.error(result.success ? chalk.gray(result.stderr) : chalk.red(result.stderr));
239
200
  if (!result.success)
240
201
  process.exitCode = 1;
241
202
  }
@@ -276,4 +237,3 @@ class DeviceCommand {
276
237
  return new Date().toISOString().replace(/[:.]/g, '-');
277
238
  }
278
239
  }
279
- exports.DeviceCommand = DeviceCommand;
@@ -1,18 +1,12 @@
1
- "use strict";
2
1
  /**
3
2
  * Edit Command - File editing with AI assistance
4
3
  */
5
- var __importDefault = (this && this.__importDefault) || function (mod) {
6
- return (mod && mod.__esModule) ? mod : { "default": mod };
7
- };
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.EditCommand = void 0;
10
- const chalk_1 = __importDefault(require("chalk"));
11
- const inquirer_1 = __importDefault(require("inquirer"));
12
- const logger_js_1 = require("../utils/logger.js");
13
- const api_js_1 = require("../utils/api.js");
14
- const files_js_1 = require("../utils/files.js");
15
- class EditCommand {
4
+ import chalk from 'chalk';
5
+ import inquirer from 'inquirer';
6
+ import { createSpinner } from '../utils/logger.js';
7
+ import { APIClient, CLIError, classifyError, formatCLIError } from '../utils/api.js';
8
+ import { FileUtils } from '../utils/files.js';
9
+ export class EditCommand {
16
10
  config;
17
11
  logger;
18
12
  api;
@@ -20,8 +14,8 @@ class EditCommand {
20
14
  constructor(config, logger) {
21
15
  this.config = config;
22
16
  this.logger = logger;
23
- this.api = new api_js_1.APIClient(config, logger);
24
- this.fileUtils = new files_js_1.FileUtils(process.cwd(), config.get('project').ignorePatterns);
17
+ this.api = new APIClient(config, logger);
18
+ this.fileUtils = new FileUtils(process.cwd(), config.get('project').ignorePatterns);
25
19
  }
26
20
  async run(filePath, options) {
27
21
  // Check auth
@@ -42,7 +36,7 @@ class EditCommand {
42
36
  return;
43
37
  }
44
38
  this.logger.section(`Editing: ${file.relativePath}`);
45
- console.log(chalk_1.default.gray(`Language: ${file.language} | Lines: ${file.lines}`));
39
+ console.log(chalk.gray(`Language: ${file.language} | Lines: ${file.lines}`));
46
40
  console.log();
47
41
  // Get instruction
48
42
  let instruction = options.instruction;
@@ -57,7 +51,7 @@ class EditCommand {
57
51
  this.logger.error('No --instruction provided and stdin is not interactive. Use: vigthoria edit file.ts --instruction "..."');
58
52
  return;
59
53
  }
60
- const answer = await inquirer_1.default.prompt([
54
+ const answer = await inquirer.prompt([
61
55
  {
62
56
  type: 'input',
63
57
  name: 'instruction',
@@ -68,7 +62,7 @@ class EditCommand {
68
62
  instruction = answer.instruction;
69
63
  }
70
64
  // Generate edit
71
- const spinner = (0, logger_js_1.createSpinner)({
65
+ const spinner = createSpinner({
72
66
  text: 'Generating changes...',
73
67
  spinner: 'dots',
74
68
  }).start();
@@ -122,8 +116,8 @@ Return the complete modified file content:`,
122
116
  }
123
117
  catch (error) {
124
118
  spinner.stop();
125
- const cliErr = error instanceof api_js_1.CLIError ? error : (0, api_js_1.classifyError)(error);
126
- this.logger.error((0, api_js_1.formatCLIError)(cliErr));
119
+ const cliErr = error instanceof CLIError ? error : classifyError(error);
120
+ this.logger.error(formatCLIError(cliErr));
127
121
  }
128
122
  }
129
123
  async fix(filePath, options) {
@@ -145,9 +139,9 @@ Return the complete modified file content:`,
145
139
  return;
146
140
  }
147
141
  this.logger.section(`Fixing: ${file.relativePath}`);
148
- console.log(chalk_1.default.gray(`Fix type: ${options.type} | Language: ${file.language}`));
142
+ console.log(chalk.gray(`Fix type: ${options.type} | Language: ${file.language}`));
149
143
  console.log();
150
- const spinner = (0, logger_js_1.createSpinner)({
144
+ const spinner = createSpinner({
151
145
  text: `Analyzing for ${options.type} issues...`,
152
146
  spinner: 'dots',
153
147
  }).start();
@@ -162,7 +156,7 @@ Return the complete modified file content:`,
162
156
  // diff and let the user decide instead of silently discarding.
163
157
  if (result.fixed && result.fixed !== file.content) {
164
158
  this.logger.section('Found 1 issue(s)');
165
- console.log(chalk_1.default.yellow('1. Operator/character-level fix detected'));
159
+ console.log(chalk.yellow('1. Operator/character-level fix detected'));
166
160
  console.log();
167
161
  if (options.apply) {
168
162
  await this.applyFix(file.path, file.content, result.fixed);
@@ -178,10 +172,10 @@ Return the complete modified file content:`,
178
172
  // Show fixes
179
173
  this.logger.section(`Found ${result.changes.length} issue(s)`);
180
174
  result.changes.forEach((change, i) => {
181
- console.log(chalk_1.default.yellow(`${i + 1}. Line ${change.line}:`));
182
- console.log(chalk_1.default.red(` - ${change.before}`));
183
- console.log(chalk_1.default.green(` + ${change.after}`));
184
- console.log(chalk_1.default.gray(` Reason: ${change.reason}`));
175
+ console.log(chalk.yellow(`${i + 1}. Line ${change.line}:`));
176
+ console.log(chalk.red(` - ${change.before}`));
177
+ console.log(chalk.green(` + ${change.after}`));
178
+ console.log(chalk.gray(` Reason: ${change.reason}`));
185
179
  console.log();
186
180
  });
187
181
  // Apply or confirm
@@ -194,8 +188,8 @@ Return the complete modified file content:`,
194
188
  }
195
189
  catch (error) {
196
190
  spinner.stop();
197
- const cliErr = error instanceof api_js_1.CLIError ? error : (0, api_js_1.classifyError)(error);
198
- this.logger.error((0, api_js_1.formatCLIError)(cliErr));
191
+ const cliErr = error instanceof CLIError ? error : classifyError(error);
192
+ this.logger.error(formatCLIError(cliErr));
199
193
  }
200
194
  }
201
195
  extractCode(response, language) {
@@ -358,10 +352,10 @@ Return the complete modified file content:`,
358
352
  // Show diff
359
353
  this.logger.section('Changes');
360
354
  diff.removed.forEach(line => {
361
- console.log(chalk_1.default.red(line));
355
+ console.log(chalk.red(line));
362
356
  });
363
357
  diff.added.forEach(line => {
364
- console.log(chalk_1.default.green(line));
358
+ console.log(chalk.green(line));
365
359
  });
366
360
  console.log();
367
361
  // Confirm
@@ -376,7 +370,7 @@ Return the complete modified file content:`,
376
370
  this.logger.info('Non-interactive mode. Re-run with --apply to apply changes.');
377
371
  return;
378
372
  }
379
- const { action } = await inquirer_1.default.prompt([
373
+ const { action } = await inquirer.prompt([
380
374
  {
381
375
  type: 'list',
382
376
  name: 'action',
@@ -394,7 +388,7 @@ Return the complete modified file content:`,
394
388
  break;
395
389
  case 'view':
396
390
  this.showFullDiff(original, modified);
397
- const { confirm } = await inquirer_1.default.prompt([
391
+ const { confirm } = await inquirer.prompt([
398
392
  {
399
393
  type: 'confirm',
400
394
  name: 'confirm',
@@ -415,7 +409,7 @@ Return the complete modified file content:`,
415
409
  const originalLines = original.split('\n');
416
410
  const modifiedLines = modified.split('\n');
417
411
  console.log();
418
- console.log(chalk_1.default.gray('─'.repeat(60)));
412
+ console.log(chalk.gray('─'.repeat(60)));
419
413
  // Use LCS-based diff to avoid line-shift inflation
420
414
  const m = originalLines.length;
421
415
  const n = modifiedLines.length;
@@ -454,26 +448,26 @@ Return the complete modified file content:`,
454
448
  if (op.type === 'keep') {
455
449
  displayLine++;
456
450
  const lineNum = String(displayLine).padStart(4, ' ');
457
- console.log(chalk_1.default.gray(`${lineNum} │ ${op.text || ''}`));
451
+ console.log(chalk.gray(`${lineNum} │ ${op.text || ''}`));
458
452
  }
459
453
  else if (op.type === 'remove') {
460
454
  displayLine++;
461
455
  const lineNum = String(displayLine).padStart(4, ' ');
462
- console.log(chalk_1.default.red(`${lineNum} - ${op.text}`));
456
+ console.log(chalk.red(`${lineNum} - ${op.text}`));
463
457
  }
464
458
  else {
465
459
  const lineNum = ' +';
466
- console.log(chalk_1.default.green(`${lineNum} + ${op.text}`));
460
+ console.log(chalk.green(`${lineNum} + ${op.text}`));
467
461
  }
468
462
  }
469
- console.log(chalk_1.default.gray('─'.repeat(60)));
463
+ console.log(chalk.gray('─'.repeat(60)));
470
464
  console.log();
471
465
  }
472
466
  async applyFix(filePath, original, modified) {
473
467
  // Create backup
474
468
  const backup = this.fileUtils.backupFile(filePath);
475
469
  if (backup) {
476
- this.logger.info(`Backup: ${chalk_1.default.gray(backup)}`);
470
+ this.logger.info(`Backup: ${chalk.gray(backup)}`);
477
471
  }
478
472
  // Apply changes
479
473
  if (this.fileUtils.writeFile(filePath, modified)) {
@@ -484,4 +478,3 @@ Return the complete modified file content:`,
484
478
  }
485
479
  }
486
480
  }
487
- exports.EditCommand = EditCommand;
@@ -1,19 +1,13 @@
1
- "use strict";
2
1
  /**
3
2
  * Explain Command - Explain code in files
4
3
  */
5
- var __importDefault = (this && this.__importDefault) || function (mod) {
6
- return (mod && mod.__esModule) ? mod : { "default": mod };
7
- };
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.ExplainCommand = void 0;
10
- const chalk_1 = __importDefault(require("chalk"));
11
- const marked_1 = require("marked");
12
- const marked_terminal_1 = require("marked-terminal");
13
- const logger_js_1 = require("../utils/logger.js");
14
- const api_js_1 = require("../utils/api.js");
15
- const files_js_1 = require("../utils/files.js");
16
- class ExplainCommand {
4
+ import chalk from 'chalk';
5
+ import { Marked } from 'marked';
6
+ import { markedTerminal } from 'marked-terminal';
7
+ import { createSpinner } from '../utils/logger.js';
8
+ import { APIClient, CLIError, classifyError, formatCLIError } from '../utils/api.js';
9
+ import { FileUtils } from '../utils/files.js';
10
+ export class ExplainCommand {
17
11
  config;
18
12
  logger;
19
13
  api;
@@ -22,10 +16,10 @@ class ExplainCommand {
22
16
  constructor(config, logger) {
23
17
  this.config = config;
24
18
  this.logger = logger;
25
- this.api = new api_js_1.APIClient(config, logger);
26
- this.fileUtils = new files_js_1.FileUtils(process.cwd(), config.get('project').ignorePatterns);
27
- this.marked = new marked_1.Marked();
28
- this.marked.use((0, marked_terminal_1.markedTerminal)({
19
+ this.api = new APIClient(config, logger);
20
+ this.fileUtils = new FileUtils(process.cwd(), config.get('project').ignorePatterns);
21
+ this.marked = new Marked();
22
+ this.marked.use(markedTerminal({
29
23
  showSectionPrefix: false,
30
24
  tab: 2,
31
25
  width: 80,
@@ -62,17 +56,17 @@ class ExplainCommand {
62
56
  }
63
57
  }
64
58
  this.logger.section(`Explaining: ${file.relativePath}${lineInfo}`);
65
- console.log(chalk_1.default.gray(`Language: ${file.language} | Detail: ${options.detail}`));
59
+ console.log(chalk.gray(`Language: ${file.language} | Detail: ${options.detail}`));
66
60
  console.log();
67
61
  // Show the code being explained
68
- console.log(chalk_1.default.gray('─'.repeat(60)));
62
+ console.log(chalk.gray('─'.repeat(60)));
69
63
  codeToExplain.split('\n').forEach((line, i) => {
70
- const lineNum = chalk_1.default.gray(String(i + 1).padStart(4, ' ') + ' │ ');
64
+ const lineNum = chalk.gray(String(i + 1).padStart(4, ' ') + ' │ ');
71
65
  console.log(lineNum + line);
72
66
  });
73
- console.log(chalk_1.default.gray('─'.repeat(60)));
67
+ console.log(chalk.gray('─'.repeat(60)));
74
68
  console.log();
75
- const spinner = (0, logger_js_1.createSpinner)({
69
+ const spinner = createSpinner({
76
70
  text: 'Analyzing code...',
77
71
  spinner: 'dots',
78
72
  }).start();
@@ -86,8 +80,8 @@ class ExplainCommand {
86
80
  }
87
81
  catch (error) {
88
82
  spinner.stop();
89
- const cliErr = error instanceof api_js_1.CLIError ? error : (0, api_js_1.classifyError)(error);
90
- this.logger.error((0, api_js_1.formatCLIError)(cliErr));
83
+ const cliErr = error instanceof CLIError ? error : classifyError(error);
84
+ this.logger.error(formatCLIError(cliErr));
91
85
  }
92
86
  }
93
87
  /**
@@ -132,4 +126,3 @@ class ExplainCommand {
132
126
  }
133
127
  }
134
128
  }
135
- exports.ExplainCommand = ExplainCommand;
@@ -0,0 +1,17 @@
1
+ import { Config } from '../utils/config.js';
2
+ import { Logger } from '../utils/logger.js';
3
+ interface ForkOptions {
4
+ eventIndex?: number;
5
+ project?: string;
6
+ json?: boolean;
7
+ }
8
+ export declare class ForkCommand {
9
+ private config;
10
+ private logger;
11
+ constructor(config: Config, logger: Logger);
12
+ private getHeaders;
13
+ private getBaseUrl;
14
+ private resolveWorkspaceRoot;
15
+ run(runId: string, message: string, options: ForkOptions): Promise<void>;
16
+ }
17
+ export {};
@@ -0,0 +1,164 @@
1
+ import { isServerRuntime } from '../utils/api.js';
2
+ /**
3
+ * fork.ts — Fork from an existing V3 agent run and stream the result.
4
+ */
5
+ import chalk from 'chalk';
6
+ import { createRequire } from 'node:module';
7
+ import { createSpinner, CH } from '../utils/logger.js';
8
+ const require = createRequire(import.meta.url);
9
+ export class ForkCommand {
10
+ config;
11
+ logger;
12
+ constructor(config, logger) {
13
+ this.config = config;
14
+ this.logger = logger;
15
+ }
16
+ getHeaders() {
17
+ const headers = { 'Content-Type': 'application/json' };
18
+ const token = process.env.VIGTHORIA_TOKEN ||
19
+ process.env.VIGTHORIA_AUTH_TOKEN ||
20
+ this.config.get('authToken');
21
+ if (token) {
22
+ headers['Authorization'] = `Bearer ${token}`;
23
+ headers['Cookie'] = `vigthoria-auth-token=${token}`;
24
+ }
25
+ const serviceKey = process.env.VIGTHORIA_V3_SERVICE_KEY;
26
+ if (serviceKey)
27
+ headers['X-Service-Key'] = serviceKey;
28
+ return headers;
29
+ }
30
+ getBaseUrl() {
31
+ const configuredApiUrl = String(this.config.get('apiUrl') || 'https://coder.vigthoria.io').replace(/\/$/, '');
32
+ const allowLocal = !isServerRuntime() || process.env.VIGTHORIA_ALLOW_LOCAL_V3_AGENT === '1';
33
+ return (process.env.VIGTHORIA_V3_AGENT_URL ||
34
+ process.env.V3_AGENT_URL ||
35
+ (allowLocal ? 'http://127.0.0.1:8030' : null) ||
36
+ configuredApiUrl);
37
+ }
38
+ resolveWorkspaceRoot(project) {
39
+ if (/^[a-zA-Z]:[\\/]/.test(project) || /^\\\\/.test(project))
40
+ return '';
41
+ if (typeof require !== 'undefined') {
42
+ try {
43
+ const path = require('path');
44
+ if (!path.isAbsolute(project))
45
+ return '';
46
+ }
47
+ catch { }
48
+ }
49
+ return project;
50
+ }
51
+ async run(runId, message, options) {
52
+ const project = options.project || process.cwd();
53
+ const workspace = this.resolveWorkspaceRoot(project);
54
+ const eventIndex = options.eventIndex || 0;
55
+ const spinner = createSpinner(`Forking run ${runId}...`).start();
56
+ try {
57
+ const baseUrl = this.getBaseUrl();
58
+ const body = {
59
+ workspace_root: workspace,
60
+ from_event_index: eventIndex,
61
+ new_request: message || '',
62
+ stream: true,
63
+ context: '',
64
+ };
65
+ const resp = await fetch(`${baseUrl}/api/runs/${encodeURIComponent(runId)}/fork`, {
66
+ method: 'POST',
67
+ headers: this.getHeaders(),
68
+ body: JSON.stringify(body),
69
+ });
70
+ if (!resp.ok) {
71
+ spinner.stop();
72
+ if (resp.status === 404) {
73
+ this.logger.error(`Run ${runId} not found or has no event log.`);
74
+ }
75
+ else if (resp.status === 502 || resp.status === 503) {
76
+ this.logger.error(`V3 run-history route is not available on ${baseUrl}. Backend may not expose /api/runs.`);
77
+ }
78
+ else {
79
+ this.logger.error(`Fork failed: ${resp.status} ${resp.statusText}`);
80
+ }
81
+ return;
82
+ }
83
+ spinner.stop();
84
+ console.log(chalk.bold(`\n${CH.success} Forked from ${chalk.cyan(runId)} at event ${eventIndex}\n`));
85
+ if (!resp.body) {
86
+ this.logger.error('No response body');
87
+ return;
88
+ }
89
+ // Stream SSE events
90
+ const reader = resp.body.getReader();
91
+ const decoder = new TextDecoder();
92
+ let buffer = '';
93
+ let toolCallNum = 0;
94
+ while (true) {
95
+ const { done, value } = await reader.read();
96
+ if (done)
97
+ break;
98
+ buffer += decoder.decode(value, { stream: true });
99
+ const lines = buffer.split('\n');
100
+ buffer = lines.pop() || '';
101
+ for (const line of lines) {
102
+ if (!line.startsWith('data: '))
103
+ continue;
104
+ const payload = line.slice(6).trim();
105
+ if (payload === '[DONE]') {
106
+ console.log(chalk.bold(`\n${CH.success} Fork run complete\n`));
107
+ return;
108
+ }
109
+ try {
110
+ const evt = JSON.parse(payload);
111
+ const type = evt.type || 'unknown';
112
+ if (options.json) {
113
+ console.log(JSON.stringify(evt));
114
+ continue;
115
+ }
116
+ switch (type) {
117
+ case 'context':
118
+ console.log(chalk.blue(` context: ${evt.context_id || '?'}`) +
119
+ (evt.forked_from ? chalk.dim(` (forked from ${evt.forked_from})`) : ''));
120
+ break;
121
+ case 'start':
122
+ console.log(chalk.green(` ▶ START`) + ` task=${evt.task_id || '?'}` +
123
+ (evt.forked_from ? chalk.dim(` forked_from=${evt.forked_from}`) : ''));
124
+ break;
125
+ case 'plan':
126
+ console.log(chalk.magenta(` 📋 PLAN`) + ` ${evt.tasks?.length || 0} tasks`);
127
+ break;
128
+ case 'tool_call':
129
+ toolCallNum++;
130
+ console.log(chalk.yellow(` 🔧 #${toolCallNum}`) + ` ${evt.name || '?'}(${JSON.stringify(evt.arguments || {}).substring(0, 60)})`);
131
+ break;
132
+ case 'tool_result': {
133
+ const icon = evt.success !== false ? chalk.green('✓') : chalk.red('✗');
134
+ console.log(` ${icon} ${evt.name || '?'}: ${chalk.dim((evt.output || '').substring(0, 100))}`);
135
+ break;
136
+ }
137
+ case 'message':
138
+ console.log(chalk.cyan(` 💬 `) + (evt.content || '').substring(0, 150));
139
+ break;
140
+ case 'complete': {
141
+ const seal = evt.seal_score ? `[${evt.seal_score.tier} ${evt.seal_score.overall}]` : '';
142
+ console.log(chalk.green(` ✅ COMPLETE `) + chalk.yellow(seal) +
143
+ ` ${evt.iterations || '?'} iterations, ${evt.tool_calls || '?'} tool calls`);
144
+ break;
145
+ }
146
+ case 'error':
147
+ console.log(chalk.red(` ❌ ERROR: `) + (evt.message || '').substring(0, 200));
148
+ break;
149
+ default:
150
+ console.log(chalk.dim(` ${type}: ${JSON.stringify(evt).substring(0, 80)}`));
151
+ }
152
+ }
153
+ catch {
154
+ // skip unparseable lines
155
+ }
156
+ }
157
+ }
158
+ }
159
+ catch (err) {
160
+ spinner.stop();
161
+ this.logger.error(`Fork error: ${err.message}`);
162
+ }
163
+ }
164
+ }