vigthoria-cli 1.9.9 → 1.9.19
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/README.md +5 -5
- package/dist/commands/auth.js +48 -65
- package/dist/commands/bridge.js +12 -19
- package/dist/commands/cancel.js +15 -22
- package/dist/commands/chat.d.ts +11 -0
- package/dist/commands/chat.js +404 -248
- package/dist/commands/config.js +31 -71
- package/dist/commands/deploy.js +83 -123
- package/dist/commands/device.d.ts +35 -0
- package/dist/commands/device.js +239 -0
- package/dist/commands/edit.js +32 -39
- package/dist/commands/explain.js +18 -25
- package/dist/commands/fork.js +22 -27
- package/dist/commands/generate.js +37 -44
- package/dist/commands/history.js +20 -25
- package/dist/commands/hub.js +95 -102
- package/dist/commands/index.js +41 -46
- package/dist/commands/legion.d.ts +1 -0
- package/dist/commands/legion.js +162 -209
- package/dist/commands/preview.js +60 -98
- package/dist/commands/replay.js +27 -32
- package/dist/commands/repo.js +103 -141
- package/dist/commands/review.js +29 -36
- package/dist/commands/security.js +5 -12
- package/dist/commands/update.js +15 -49
- package/dist/commands/workflow.d.ts +8 -1
- package/dist/commands/workflow.js +53 -19
- package/dist/index.js +409 -234
- package/dist/utils/api.d.ts +5 -0
- package/dist/utils/api.js +398 -176
- 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/config.d.ts +5 -0
- package/dist/utils/config.js +35 -14
- package/dist/utils/context-ranker.js +15 -21
- package/dist/utils/files.js +5 -42
- package/dist/utils/logger.js +42 -50
- package/dist/utils/post-write-validator.js +22 -29
- package/dist/utils/project-memory.d.ts +56 -0
- package/dist/utils/project-memory.js +289 -0
- package/dist/utils/session.d.ts +29 -3
- package/dist/utils/session.js +137 -85
- package/dist/utils/task-display.js +13 -20
- package/dist/utils/tools.d.ts +19 -0
- package/dist/utils/tools.js +84 -87
- package/dist/utils/workspace-cache.js +18 -26
- package/dist/utils/workspace-stream.js +26 -64
- package/install.ps1 +14 -0
- package/package.json +5 -3
- package/scripts/release/LOCAL_MACHINE_USER_VERIFICATION.md +1 -1
- package/scripts/release/validate-no-go-gates.sh +2 -2
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Config } from '../utils/config.js';
|
|
2
|
+
import { Logger } from '../utils/logger.js';
|
|
3
|
+
type DeviceOptions = {
|
|
4
|
+
device?: string;
|
|
5
|
+
json?: boolean;
|
|
6
|
+
output?: string;
|
|
7
|
+
lines?: string;
|
|
8
|
+
follow?: boolean;
|
|
9
|
+
port?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare class DeviceCommand {
|
|
12
|
+
private logger;
|
|
13
|
+
constructor(_config: Config, logger: Logger);
|
|
14
|
+
status(options?: DeviceOptions): Promise<void>;
|
|
15
|
+
list(options?: DeviceOptions): Promise<void>;
|
|
16
|
+
screenshot(options?: DeviceOptions): Promise<void>;
|
|
17
|
+
install(apkPath: string, options?: DeviceOptions): Promise<void>;
|
|
18
|
+
launch(packageName: string, options?: DeviceOptions & {
|
|
19
|
+
activity?: string;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
logs(options?: DeviceOptions): Promise<void>;
|
|
22
|
+
tcpip(options?: DeviceOptions): Promise<void>;
|
|
23
|
+
connect(address: string, options?: DeviceOptions): Promise<void>;
|
|
24
|
+
disconnect(address?: string, options?: DeviceOptions): Promise<void>;
|
|
25
|
+
private getDevices;
|
|
26
|
+
private parseDeviceLine;
|
|
27
|
+
private resolveDeviceId;
|
|
28
|
+
private withDevice;
|
|
29
|
+
private runAdb;
|
|
30
|
+
private printResult;
|
|
31
|
+
private requireAdbPath;
|
|
32
|
+
private findAdbPath;
|
|
33
|
+
private timestamp;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,239 @@
|
|
|
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 {
|
|
10
|
+
logger;
|
|
11
|
+
constructor(_config, logger) {
|
|
12
|
+
this.logger = logger;
|
|
13
|
+
}
|
|
14
|
+
async status(options = {}) {
|
|
15
|
+
const adbPath = this.findAdbPath();
|
|
16
|
+
const devices = adbPath ? await this.getDevices(adbPath) : [];
|
|
17
|
+
const payload = {
|
|
18
|
+
adbAvailable: Boolean(adbPath),
|
|
19
|
+
adbPath,
|
|
20
|
+
deviceCount: devices.filter((device) => device.state === 'device').length,
|
|
21
|
+
devices,
|
|
22
|
+
};
|
|
23
|
+
if (options.json) {
|
|
24
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
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')));
|
|
30
|
+
if (!adbPath) {
|
|
31
|
+
console.log(chalk.gray(' Install Android SDK Platform Tools, then make adb available on PATH.'));
|
|
32
|
+
console.log();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (devices.length === 0) {
|
|
36
|
+
console.log(chalk.yellow(' No Android devices visible.'));
|
|
37
|
+
console.log(chalk.gray(' Enable Developer Options + USB debugging, then run `vigthoria device list`.'));
|
|
38
|
+
console.log();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
for (const device of devices) {
|
|
42
|
+
const label = device.model || device.product || 'Android device';
|
|
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)}`);
|
|
45
|
+
}
|
|
46
|
+
console.log();
|
|
47
|
+
}
|
|
48
|
+
async list(options = {}) {
|
|
49
|
+
const adbPath = this.requireAdbPath();
|
|
50
|
+
const devices = await this.getDevices(adbPath);
|
|
51
|
+
if (options.json) {
|
|
52
|
+
console.log(JSON.stringify({ devices }, null, 2));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (devices.length === 0) {
|
|
56
|
+
console.log(chalk.yellow('No Android devices found.'));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
for (const device of devices) {
|
|
60
|
+
console.log(`${chalk.white(device.id)}\t${device.state}\t${device.model || device.product || ''}`.trim());
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async screenshot(options = {}) {
|
|
64
|
+
const adbPath = this.requireAdbPath();
|
|
65
|
+
const deviceId = await this.resolveDeviceId(adbPath, options.device);
|
|
66
|
+
const output = path.resolve(options.output || `vigthoria-android-screenshot-${this.timestamp()}.png`);
|
|
67
|
+
const args = this.withDevice(deviceId, ['exec-out', 'screencap', '-p']);
|
|
68
|
+
const result = await execFileAsync(adbPath, args, { encoding: 'buffer', maxBuffer: 20 * 1024 * 1024 });
|
|
69
|
+
fs.writeFileSync(output, result.stdout);
|
|
70
|
+
console.log(chalk.green(`Saved screenshot: ${output}`));
|
|
71
|
+
}
|
|
72
|
+
async install(apkPath, options = {}) {
|
|
73
|
+
if (!apkPath)
|
|
74
|
+
throw new Error('APK path is required.');
|
|
75
|
+
const resolvedApk = path.resolve(apkPath);
|
|
76
|
+
if (!fs.existsSync(resolvedApk))
|
|
77
|
+
throw new Error(`APK not found: ${resolvedApk}`);
|
|
78
|
+
const adbPath = this.requireAdbPath();
|
|
79
|
+
const deviceId = await this.resolveDeviceId(adbPath, options.device);
|
|
80
|
+
const result = await this.runAdb(adbPath, this.withDevice(deviceId, ['install', '-r', resolvedApk]), 120000);
|
|
81
|
+
this.printResult(result, options.json);
|
|
82
|
+
}
|
|
83
|
+
async launch(packageName, options = {}) {
|
|
84
|
+
if (!packageName)
|
|
85
|
+
throw new Error('Android package name is required.');
|
|
86
|
+
const adbPath = this.requireAdbPath();
|
|
87
|
+
const deviceId = await this.resolveDeviceId(adbPath, options.device);
|
|
88
|
+
const args = options.activity
|
|
89
|
+
? ['shell', 'am', 'start', '-n', `${packageName}/${options.activity}`]
|
|
90
|
+
: ['shell', 'monkey', '-p', packageName, '-c', 'android.intent.category.LAUNCHER', '1'];
|
|
91
|
+
const result = await this.runAdb(adbPath, this.withDevice(deviceId, args));
|
|
92
|
+
this.printResult(result, options.json);
|
|
93
|
+
}
|
|
94
|
+
async logs(options = {}) {
|
|
95
|
+
const adbPath = this.requireAdbPath();
|
|
96
|
+
const deviceId = await this.resolveDeviceId(adbPath, options.device);
|
|
97
|
+
const lines = String(options.lines || '250');
|
|
98
|
+
const args = this.withDevice(deviceId, options.follow ? ['logcat'] : ['logcat', '-d', '-t', lines]);
|
|
99
|
+
if (options.follow) {
|
|
100
|
+
await new Promise((resolve, reject) => {
|
|
101
|
+
const child = spawn(adbPath, args, { stdio: 'inherit' });
|
|
102
|
+
child.on('error', reject);
|
|
103
|
+
child.on('exit', (code) => {
|
|
104
|
+
if (code && code !== 0)
|
|
105
|
+
reject(new Error(`adb logcat exited with code ${code}`));
|
|
106
|
+
else
|
|
107
|
+
resolve();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const result = await this.runAdb(adbPath, args, 30000, 8 * 1024 * 1024);
|
|
113
|
+
this.printResult(result, options.json);
|
|
114
|
+
}
|
|
115
|
+
async tcpip(options = {}) {
|
|
116
|
+
const adbPath = this.requireAdbPath();
|
|
117
|
+
const deviceId = await this.resolveDeviceId(adbPath, options.device);
|
|
118
|
+
const port = String(options.port || '5555');
|
|
119
|
+
const result = await this.runAdb(adbPath, this.withDevice(deviceId, ['tcpip', port]));
|
|
120
|
+
this.printResult(result, options.json);
|
|
121
|
+
}
|
|
122
|
+
async connect(address, options = {}) {
|
|
123
|
+
if (!address)
|
|
124
|
+
throw new Error('Device address is required, for example 192.168.1.30:5555.');
|
|
125
|
+
const adbPath = this.requireAdbPath();
|
|
126
|
+
const result = await this.runAdb(adbPath, ['connect', address]);
|
|
127
|
+
this.printResult(result, options.json);
|
|
128
|
+
}
|
|
129
|
+
async disconnect(address, options = {}) {
|
|
130
|
+
const adbPath = this.requireAdbPath();
|
|
131
|
+
const result = await this.runAdb(adbPath, address ? ['disconnect', address] : ['disconnect']);
|
|
132
|
+
this.printResult(result, options.json);
|
|
133
|
+
}
|
|
134
|
+
async getDevices(adbPath) {
|
|
135
|
+
const result = await this.runAdb(adbPath, ['devices', '-l']);
|
|
136
|
+
if (!result.success)
|
|
137
|
+
return [];
|
|
138
|
+
return result.stdout
|
|
139
|
+
.split(/\r?\n/)
|
|
140
|
+
.slice(1)
|
|
141
|
+
.map((line) => line.trim())
|
|
142
|
+
.filter(Boolean)
|
|
143
|
+
.map((line) => this.parseDeviceLine(line));
|
|
144
|
+
}
|
|
145
|
+
parseDeviceLine(line) {
|
|
146
|
+
const parts = line.split(/\s+/);
|
|
147
|
+
const id = parts[0] || '';
|
|
148
|
+
const state = parts[1] || 'unknown';
|
|
149
|
+
const metadata = new Map();
|
|
150
|
+
for (const token of parts.slice(2)) {
|
|
151
|
+
const separator = token.indexOf(':');
|
|
152
|
+
if (separator > 0)
|
|
153
|
+
metadata.set(token.slice(0, separator), token.slice(separator + 1));
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
id,
|
|
157
|
+
state,
|
|
158
|
+
model: metadata.get('model'),
|
|
159
|
+
product: metadata.get('product'),
|
|
160
|
+
transport: metadata.get('transport_id'),
|
|
161
|
+
raw: line,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
async resolveDeviceId(adbPath, requested) {
|
|
165
|
+
if (requested && requested.trim())
|
|
166
|
+
return requested.trim();
|
|
167
|
+
const devices = (await this.getDevices(adbPath)).filter((device) => device.state === 'device');
|
|
168
|
+
if (devices.length === 0)
|
|
169
|
+
throw new Error('No authorized Android device found. Run `vigthoria device status`.');
|
|
170
|
+
if (devices.length > 1)
|
|
171
|
+
throw new Error('Multiple Android devices found. Pass --device <id>.');
|
|
172
|
+
return devices[0].id;
|
|
173
|
+
}
|
|
174
|
+
withDevice(deviceId, args) {
|
|
175
|
+
return deviceId ? ['-s', deviceId, ...args] : args;
|
|
176
|
+
}
|
|
177
|
+
async runAdb(adbPath, args, timeout = 30000, maxBuffer = 1024 * 1024) {
|
|
178
|
+
try {
|
|
179
|
+
const result = await execFileAsync(adbPath, args, { timeout, maxBuffer });
|
|
180
|
+
return { success: true, stdout: String(result.stdout || '').trim(), stderr: String(result.stderr || '').trim() };
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
const failure = error;
|
|
184
|
+
return {
|
|
185
|
+
success: false,
|
|
186
|
+
stdout: String(failure.stdout || '').trim(),
|
|
187
|
+
stderr: String(failure.stderr || failure.message || '').trim(),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
printResult(result, json = false) {
|
|
192
|
+
if (json) {
|
|
193
|
+
console.log(JSON.stringify(result, null, 2));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (result.stdout)
|
|
197
|
+
console.log(result.stdout);
|
|
198
|
+
if (result.stderr)
|
|
199
|
+
console.error(result.success ? chalk.gray(result.stderr) : chalk.red(result.stderr));
|
|
200
|
+
if (!result.success)
|
|
201
|
+
process.exitCode = 1;
|
|
202
|
+
}
|
|
203
|
+
requireAdbPath() {
|
|
204
|
+
const adbPath = this.findAdbPath();
|
|
205
|
+
if (!adbPath) {
|
|
206
|
+
throw new Error('ADB not found. Install Android SDK Platform Tools and make adb available on PATH.');
|
|
207
|
+
}
|
|
208
|
+
return adbPath;
|
|
209
|
+
}
|
|
210
|
+
findAdbPath() {
|
|
211
|
+
const executable = process.platform === 'win32' ? 'adb.exe' : 'adb';
|
|
212
|
+
const candidates = [
|
|
213
|
+
process.env.VIGTHORIA_ADB_PATH,
|
|
214
|
+
process.env.ADB_PATH,
|
|
215
|
+
process.env.ANDROID_HOME ? path.join(process.env.ANDROID_HOME, 'platform-tools', executable) : undefined,
|
|
216
|
+
process.env.ANDROID_SDK_ROOT ? path.join(process.env.ANDROID_SDK_ROOT, 'platform-tools', executable) : undefined,
|
|
217
|
+
path.join(os.homedir(), 'Android', 'Sdk', 'platform-tools', executable),
|
|
218
|
+
process.platform === 'win32' ? path.join(process.env.LOCALAPPDATA || '', 'Android', 'Sdk', 'platform-tools', executable) : undefined,
|
|
219
|
+
process.platform === 'darwin' ? path.join(os.homedir(), 'Library', 'Android', 'sdk', 'platform-tools', executable) : undefined,
|
|
220
|
+
'/usr/bin/adb',
|
|
221
|
+
'/usr/local/bin/adb',
|
|
222
|
+
'/opt/android-sdk/platform-tools/adb',
|
|
223
|
+
].filter((candidate) => Boolean(candidate));
|
|
224
|
+
for (const candidate of candidates) {
|
|
225
|
+
if (fs.existsSync(candidate))
|
|
226
|
+
return candidate;
|
|
227
|
+
}
|
|
228
|
+
const pathDirs = String(process.env.PATH || '').split(path.delimiter).filter(Boolean);
|
|
229
|
+
for (const pathDir of pathDirs) {
|
|
230
|
+
const candidate = path.join(pathDir, executable);
|
|
231
|
+
if (fs.existsSync(candidate))
|
|
232
|
+
return candidate;
|
|
233
|
+
}
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
timestamp() {
|
|
237
|
+
return new Date().toISOString().replace(/[:.]/g, '-');
|
|
238
|
+
}
|
|
239
|
+
}
|
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;
|
package/dist/commands/fork.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.ForkCommand = void 0;
|
|
7
|
-
const api_js_1 = require("../utils/api.js");
|
|
1
|
+
import { isServerRuntime } from '../utils/api.js';
|
|
8
2
|
/**
|
|
9
3
|
* fork.ts — Fork from an existing V3 agent run and stream the result.
|
|
10
4
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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 {
|
|
14
10
|
config;
|
|
15
11
|
logger;
|
|
16
12
|
constructor(config, logger) {
|
|
@@ -33,7 +29,7 @@ class ForkCommand {
|
|
|
33
29
|
}
|
|
34
30
|
getBaseUrl() {
|
|
35
31
|
const configuredApiUrl = String(this.config.get('apiUrl') || 'https://coder.vigthoria.io').replace(/\/$/, '');
|
|
36
|
-
const allowLocal =
|
|
32
|
+
const allowLocal = isServerRuntime() && process.env.VIGTHORIA_ALLOW_LOCAL_V3_AGENT === '1';
|
|
37
33
|
return (process.env.VIGTHORIA_V3_AGENT_URL ||
|
|
38
34
|
process.env.V3_AGENT_URL ||
|
|
39
35
|
(allowLocal ? 'http://127.0.0.1:8030' : null) ||
|
|
@@ -56,7 +52,7 @@ class ForkCommand {
|
|
|
56
52
|
const project = options.project || process.cwd();
|
|
57
53
|
const workspace = this.resolveWorkspaceRoot(project);
|
|
58
54
|
const eventIndex = options.eventIndex || 0;
|
|
59
|
-
const spinner =
|
|
55
|
+
const spinner = createSpinner(`Forking run ${runId}...`).start();
|
|
60
56
|
try {
|
|
61
57
|
const baseUrl = this.getBaseUrl();
|
|
62
58
|
const body = {
|
|
@@ -85,7 +81,7 @@ class ForkCommand {
|
|
|
85
81
|
return;
|
|
86
82
|
}
|
|
87
83
|
spinner.stop();
|
|
88
|
-
console.log(
|
|
84
|
+
console.log(chalk.bold(`\n${CH.success} Forked from ${chalk.cyan(runId)} at event ${eventIndex}\n`));
|
|
89
85
|
if (!resp.body) {
|
|
90
86
|
this.logger.error('No response body');
|
|
91
87
|
return;
|
|
@@ -107,7 +103,7 @@ class ForkCommand {
|
|
|
107
103
|
continue;
|
|
108
104
|
const payload = line.slice(6).trim();
|
|
109
105
|
if (payload === '[DONE]') {
|
|
110
|
-
console.log(
|
|
106
|
+
console.log(chalk.bold(`\n${CH.success} Fork run complete\n`));
|
|
111
107
|
return;
|
|
112
108
|
}
|
|
113
109
|
try {
|
|
@@ -119,39 +115,39 @@ class ForkCommand {
|
|
|
119
115
|
}
|
|
120
116
|
switch (type) {
|
|
121
117
|
case 'context':
|
|
122
|
-
console.log(
|
|
123
|
-
(evt.forked_from ?
|
|
118
|
+
console.log(chalk.blue(` context: ${evt.context_id || '?'}`) +
|
|
119
|
+
(evt.forked_from ? chalk.dim(` (forked from ${evt.forked_from})`) : ''));
|
|
124
120
|
break;
|
|
125
121
|
case 'start':
|
|
126
|
-
console.log(
|
|
127
|
-
(evt.forked_from ?
|
|
122
|
+
console.log(chalk.green(` ▶ START`) + ` task=${evt.task_id || '?'}` +
|
|
123
|
+
(evt.forked_from ? chalk.dim(` forked_from=${evt.forked_from}`) : ''));
|
|
128
124
|
break;
|
|
129
125
|
case 'plan':
|
|
130
|
-
console.log(
|
|
126
|
+
console.log(chalk.magenta(` 📋 PLAN`) + ` ${evt.tasks?.length || 0} tasks`);
|
|
131
127
|
break;
|
|
132
128
|
case 'tool_call':
|
|
133
129
|
toolCallNum++;
|
|
134
|
-
console.log(
|
|
130
|
+
console.log(chalk.yellow(` 🔧 #${toolCallNum}`) + ` ${evt.name || '?'}(${JSON.stringify(evt.arguments || {}).substring(0, 60)})`);
|
|
135
131
|
break;
|
|
136
132
|
case 'tool_result': {
|
|
137
|
-
const icon = evt.success !== false ?
|
|
138
|
-
console.log(` ${icon} ${evt.name || '?'}: ${
|
|
133
|
+
const icon = evt.success !== false ? chalk.green('✓') : chalk.red('✗');
|
|
134
|
+
console.log(` ${icon} ${evt.name || '?'}: ${chalk.dim((evt.output || '').substring(0, 100))}`);
|
|
139
135
|
break;
|
|
140
136
|
}
|
|
141
137
|
case 'message':
|
|
142
|
-
console.log(
|
|
138
|
+
console.log(chalk.cyan(` 💬 `) + (evt.content || '').substring(0, 150));
|
|
143
139
|
break;
|
|
144
140
|
case 'complete': {
|
|
145
141
|
const seal = evt.seal_score ? `[${evt.seal_score.tier} ${evt.seal_score.overall}]` : '';
|
|
146
|
-
console.log(
|
|
142
|
+
console.log(chalk.green(` ✅ COMPLETE `) + chalk.yellow(seal) +
|
|
147
143
|
` ${evt.iterations || '?'} iterations, ${evt.tool_calls || '?'} tool calls`);
|
|
148
144
|
break;
|
|
149
145
|
}
|
|
150
146
|
case 'error':
|
|
151
|
-
console.log(
|
|
147
|
+
console.log(chalk.red(` ❌ ERROR: `) + (evt.message || '').substring(0, 200));
|
|
152
148
|
break;
|
|
153
149
|
default:
|
|
154
|
-
console.log(
|
|
150
|
+
console.log(chalk.dim(` ${type}: ${JSON.stringify(evt).substring(0, 80)}`));
|
|
155
151
|
}
|
|
156
152
|
}
|
|
157
153
|
catch {
|
|
@@ -166,4 +162,3 @@ class ForkCommand {
|
|
|
166
162
|
}
|
|
167
163
|
}
|
|
168
164
|
}
|
|
169
|
-
exports.ForkCommand = ForkCommand;
|