vigthoria-cli 1.10.47 → 1.10.49
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/commands/agent-session-menu.js +2 -8
- package/dist/commands/auth.js +51 -68
- package/dist/commands/bridge.js +42 -22
- package/dist/commands/cancel.js +15 -22
- package/dist/commands/chat.d.ts +3 -0
- package/dist/commands/chat.js +326 -295
- package/dist/commands/config.js +33 -73
- package/dist/commands/deploy.js +83 -123
- package/dist/commands/device.js +21 -61
- package/dist/commands/edit.js +32 -39
- package/dist/commands/explain.js +18 -25
- package/dist/commands/fork.d.ts +17 -0
- package/dist/commands/fork.js +164 -0
- package/dist/commands/generate.js +37 -44
- package/dist/commands/history.d.ts +17 -0
- package/dist/commands/history.js +113 -0
- package/dist/commands/hub.js +95 -102
- package/dist/commands/index.js +41 -46
- package/dist/commands/legion.js +146 -186
- package/dist/commands/preview.d.ts +55 -0
- package/dist/commands/preview.js +467 -0
- package/dist/commands/replay.d.ts +18 -0
- package/dist/commands/replay.js +156 -0
- package/dist/commands/repo.d.ts +97 -0
- package/dist/commands/repo.js +773 -0
- package/dist/commands/review.js +29 -36
- package/dist/commands/security.js +5 -12
- package/dist/commands/update.d.ts +9 -0
- package/dist/commands/update.js +201 -0
- package/dist/commands/wallet.js +28 -35
- package/dist/commands/workflow.js +13 -20
- package/dist/index.d.ts +21 -0
- package/dist/index.js +1652 -0
- package/dist/utils/api.d.ts +544 -0
- package/dist/utils/api.js +5486 -0
- package/dist/utils/brain-hub-client.js +1 -5
- package/dist/utils/bridge-client.js +11 -52
- package/dist/utils/cli-state.d.ts +54 -0
- package/dist/utils/cli-state.js +185 -0
- package/dist/utils/codebase-indexer.js +4 -41
- package/dist/utils/config.d.ts +82 -0
- package/dist/utils/config.js +269 -0
- package/dist/utils/context-ranker.js +15 -21
- package/dist/utils/desktop-bridge-client.d.ts +12 -0
- package/dist/utils/desktop-bridge-client.js +30 -0
- package/dist/utils/files.js +5 -42
- package/dist/utils/logger.js +42 -50
- package/dist/utils/persona.js +3 -8
- package/dist/utils/post-write-validator.js +26 -33
- package/dist/utils/project-memory.js +16 -23
- package/dist/utils/session.d.ts +118 -0
- package/dist/utils/session.js +423 -0
- package/dist/utils/task-display.js +13 -20
- package/dist/utils/tools.d.ts +269 -0
- package/dist/utils/tools.js +3450 -0
- package/dist/utils/workspace-brain-service.js +8 -45
- package/dist/utils/workspace-cache.js +18 -26
- package/dist/utils/workspace-stream.js +21 -63
- package/package.json +2 -1
- package/scripts/release/validate-no-go-gates.sh +7 -4
package/dist/commands/device.js
CHANGED
|
@@ -1,51 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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(
|
|
68
|
-
console.log(
|
|
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(
|
|
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(
|
|
76
|
-
console.log(
|
|
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' ?
|
|
83
|
-
console.log(` ${
|
|
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(
|
|
56
|
+
console.log(chalk.yellow('No Android devices found.'));
|
|
96
57
|
return;
|
|
97
58
|
}
|
|
98
59
|
for (const device of devices) {
|
|
99
|
-
console.log(`${
|
|
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(
|
|
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 =
|
|
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 ?
|
|
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;
|
package/dist/commands/edit.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Edit Command - File editing with AI assistance
|
|
4
3
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
|
24
|
-
this.fileUtils = new
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
126
|
-
this.logger.error(
|
|
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(
|
|
142
|
+
console.log(chalk.gray(`Fix type: ${options.type} | Language: ${file.language}`));
|
|
149
143
|
console.log();
|
|
150
|
-
const spinner =
|
|
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(
|
|
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(
|
|
182
|
-
console.log(
|
|
183
|
-
console.log(
|
|
184
|
-
console.log(
|
|
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
|
|
198
|
-
this.logger.error(
|
|
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(
|
|
355
|
+
console.log(chalk.red(line));
|
|
362
356
|
});
|
|
363
357
|
diff.added.forEach(line => {
|
|
364
|
-
console.log(
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
456
|
+
console.log(chalk.red(`${lineNum} - ${op.text}`));
|
|
463
457
|
}
|
|
464
458
|
else {
|
|
465
459
|
const lineNum = ' +';
|
|
466
|
-
console.log(
|
|
460
|
+
console.log(chalk.green(`${lineNum} + ${op.text}`));
|
|
467
461
|
}
|
|
468
462
|
}
|
|
469
|
-
console.log(
|
|
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: ${
|
|
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;
|
package/dist/commands/explain.js
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Explain Command - Explain code in files
|
|
4
3
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
26
|
-
this.fileUtils = new
|
|
27
|
-
this.marked = new
|
|
28
|
-
this.marked.use(
|
|
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(
|
|
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(
|
|
62
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
69
63
|
codeToExplain.split('\n').forEach((line, i) => {
|
|
70
|
-
const lineNum =
|
|
64
|
+
const lineNum = chalk.gray(String(i + 1).padStart(4, ' ') + ' │ ');
|
|
71
65
|
console.log(lineNum + line);
|
|
72
66
|
});
|
|
73
|
-
console.log(
|
|
67
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
74
68
|
console.log();
|
|
75
|
-
const spinner =
|
|
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
|
|
90
|
-
this.logger.error(
|
|
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
|
+
}
|