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
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Vigthoria CLI — Multi-Step Terminal Task Display
|
|
4
3
|
*
|
|
@@ -7,27 +6,22 @@
|
|
|
7
6
|
*
|
|
8
7
|
* Only activates when stderr is a real TTY; JSON mode and piped output stay clean.
|
|
9
8
|
*/
|
|
10
|
-
|
|
11
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
12
|
-
};
|
|
13
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.TaskDisplay = void 0;
|
|
15
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
import chalk from 'chalk';
|
|
16
10
|
const ICONS = {
|
|
17
|
-
pending:
|
|
18
|
-
running:
|
|
19
|
-
done:
|
|
20
|
-
error:
|
|
21
|
-
skipped:
|
|
11
|
+
pending: chalk.gray('○'),
|
|
12
|
+
running: chalk.cyan('⟳'),
|
|
13
|
+
done: chalk.green('✓'),
|
|
14
|
+
error: chalk.red('✗'),
|
|
15
|
+
skipped: chalk.gray('\u2013'),
|
|
22
16
|
};
|
|
23
17
|
const LABEL_FN = {
|
|
24
|
-
pending: (s) =>
|
|
25
|
-
running: (s) =>
|
|
26
|
-
done: (s) =>
|
|
27
|
-
error: (s) =>
|
|
28
|
-
skipped: (s) =>
|
|
18
|
+
pending: (s) => chalk.gray(s),
|
|
19
|
+
running: (s) => chalk.cyan.bold(s),
|
|
20
|
+
done: (s) => chalk.gray(s),
|
|
21
|
+
error: (s) => chalk.red(s),
|
|
22
|
+
skipped: (s) => chalk.gray(s),
|
|
29
23
|
};
|
|
30
|
-
class TaskDisplay {
|
|
24
|
+
export class TaskDisplay {
|
|
31
25
|
tasks = [];
|
|
32
26
|
linesRendered = 0;
|
|
33
27
|
enabled;
|
|
@@ -44,7 +38,7 @@ class TaskDisplay {
|
|
|
44
38
|
return this.tasks.map((t) => {
|
|
45
39
|
const icon = ICONS[t.status];
|
|
46
40
|
const label = LABEL_FN[t.status](t.label);
|
|
47
|
-
const detail = t.detail ?
|
|
41
|
+
const detail = t.detail ? chalk.gray(` \u2014 ${t.detail.slice(0, 60)}`) : '';
|
|
48
42
|
return ` ${icon} ${label}${detail}`;
|
|
49
43
|
});
|
|
50
44
|
}
|
|
@@ -112,4 +106,3 @@ class TaskDisplay {
|
|
|
112
106
|
return this.enabled;
|
|
113
107
|
}
|
|
114
108
|
}
|
|
115
|
-
exports.TaskDisplay = TaskDisplay;
|
package/dist/utils/tools.d.ts
CHANGED
|
@@ -34,6 +34,10 @@ export interface ToolResult {
|
|
|
34
34
|
suggestion?: string;
|
|
35
35
|
canRetry?: boolean;
|
|
36
36
|
undoable?: boolean;
|
|
37
|
+
/** True when VIGTHORIA_DRY_RUN / VIGTHORIA_READ_ONLY caused a mutating tool to short-circuit. */
|
|
38
|
+
dryRun?: boolean;
|
|
39
|
+
/** Optional human-readable message attached to a dry-run / informational result. */
|
|
40
|
+
message?: string;
|
|
37
41
|
metadata?: {
|
|
38
42
|
searchStatus?: SearchStatus;
|
|
39
43
|
[key: string]: any;
|
|
@@ -139,6 +143,21 @@ export declare class AgenticTools {
|
|
|
139
143
|
* Execute tool with automatic retry for transient failures
|
|
140
144
|
*/
|
|
141
145
|
private executeWithRetry;
|
|
146
|
+
/**
|
|
147
|
+
* Tool names that mutate the user's filesystem, shell state, or remote
|
|
148
|
+
* services. Used by the dry-run / read-only gate below.
|
|
149
|
+
*/
|
|
150
|
+
private static readonly MUTATING_TOOLS;
|
|
151
|
+
/**
|
|
152
|
+
* `VIGTHORIA_DRY_RUN=1` and `VIGTHORIA_READ_ONLY=1` are end-user safety
|
|
153
|
+
* switches: every mutating tool short-circuits with a clear "would have
|
|
154
|
+
* happened" result instead of touching the filesystem or network.
|
|
155
|
+
*
|
|
156
|
+
* The flags are checked at call-time (not at construction time) so they
|
|
157
|
+
* can be flipped per-prompt by users who want to inspect agent intent
|
|
158
|
+
* before committing.
|
|
159
|
+
*/
|
|
160
|
+
private isDryRunActive;
|
|
142
161
|
/**
|
|
143
162
|
* Execute the actual tool operation
|
|
144
163
|
*/
|
package/dist/utils/tools.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Vigthoria CLI Tools - Agentic Tool System
|
|
4
3
|
*
|
|
@@ -14,52 +13,17 @@
|
|
|
14
13
|
* @version 1.1.0
|
|
15
14
|
* @author Vigthoria Labs
|
|
16
15
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
-
}) : function(o, v) {
|
|
31
|
-
o["default"] = v;
|
|
32
|
-
});
|
|
33
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
-
var ownKeys = function(o) {
|
|
35
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
-
var ar = [];
|
|
37
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
-
return ar;
|
|
39
|
-
};
|
|
40
|
-
return ownKeys(o);
|
|
41
|
-
};
|
|
42
|
-
return function (mod) {
|
|
43
|
-
if (mod && mod.__esModule) return mod;
|
|
44
|
-
var result = {};
|
|
45
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
-
__setModuleDefault(result, mod);
|
|
47
|
-
return result;
|
|
48
|
-
};
|
|
49
|
-
})();
|
|
50
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
51
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
52
|
-
};
|
|
53
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
-
exports.AgenticTools = exports.ToolErrorType = void 0;
|
|
55
|
-
exports.installUpdateWindows = installUpdateWindows;
|
|
56
|
-
exports.robustifyStreamResponse = robustifyStreamResponse;
|
|
57
|
-
const fs = __importStar(require("fs"));
|
|
58
|
-
const path = __importStar(require("path"));
|
|
59
|
-
const child_process_1 = require("child_process");
|
|
60
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
61
|
-
const logger_js_1 = require("./logger.js");
|
|
62
|
-
const api_js_1 = require("./api.js");
|
|
16
|
+
import * as fs from 'fs';
|
|
17
|
+
import * as path from 'path';
|
|
18
|
+
import { execSync, spawn } from 'child_process';
|
|
19
|
+
import { createRequire } from 'node:module';
|
|
20
|
+
import chalk from 'chalk';
|
|
21
|
+
// ESM shim — re-create CommonJS-style `require()` so the inline
|
|
22
|
+
// `require('os')` / `require('minimatch')` / `require('glob')` calls
|
|
23
|
+
// inside lazy code paths continue to work unchanged in ESM mode.
|
|
24
|
+
const require = createRequire(import.meta.url);
|
|
25
|
+
import { CH } from './logger.js';
|
|
26
|
+
import { isServerRuntime } from './api.js';
|
|
63
27
|
const STREAM_RESPONSE_MAX_YIELD_CHARS = 32 * 1024;
|
|
64
28
|
const POWERSHELL_SAFE_PATH_PATTERN = /^[A-Za-z0-9_:\\/.\-\s]+$/;
|
|
65
29
|
const POWERSHELL_SAFE_INCLUDE_PATTERN = /^[A-Za-z0-9_*?.\-]+$/;
|
|
@@ -91,7 +55,7 @@ function resolveWindowsInstallerPath() {
|
|
|
91
55
|
];
|
|
92
56
|
return candidates.find((candidate) => fs.existsSync(candidate)) ?? candidates[0];
|
|
93
57
|
}
|
|
94
|
-
async function installUpdateWindows() {
|
|
58
|
+
export async function installUpdateWindows() {
|
|
95
59
|
const installerPath = resolveWindowsInstallerPath();
|
|
96
60
|
try {
|
|
97
61
|
if (!fs.existsSync(installerPath)) {
|
|
@@ -99,7 +63,7 @@ async function installUpdateWindows() {
|
|
|
99
63
|
}
|
|
100
64
|
await fs.promises.access(installerPath, fs.constants.F_OK);
|
|
101
65
|
await new Promise((resolve, reject) => {
|
|
102
|
-
const child =
|
|
66
|
+
const child = spawn(installerPath, [], {
|
|
103
67
|
cwd: path.dirname(installerPath),
|
|
104
68
|
detached: true,
|
|
105
69
|
stdio: 'ignore',
|
|
@@ -121,7 +85,7 @@ async function installUpdateWindows() {
|
|
|
121
85
|
return { success: false, platform: 'windows', error: message };
|
|
122
86
|
}
|
|
123
87
|
}
|
|
124
|
-
async function* robustifyStreamResponse(res) {
|
|
88
|
+
export async function* robustifyStreamResponse(res) {
|
|
125
89
|
const MAX_YIELD_CHARS = STREAM_RESPONSE_MAX_YIELD_CHARS;
|
|
126
90
|
const MAX_BUFFER_CHARS = 256 * 1024;
|
|
127
91
|
const body = res?.body ?? res;
|
|
@@ -351,7 +315,7 @@ const TOOL_ARG_ALIASES = {
|
|
|
351
315
|
},
|
|
352
316
|
};
|
|
353
317
|
// Error types for better handling
|
|
354
|
-
var ToolErrorType;
|
|
318
|
+
export var ToolErrorType;
|
|
355
319
|
(function (ToolErrorType) {
|
|
356
320
|
ToolErrorType["FILE_NOT_FOUND"] = "FILE_NOT_FOUND";
|
|
357
321
|
ToolErrorType["PERMISSION_DENIED"] = "PERMISSION_DENIED";
|
|
@@ -360,8 +324,8 @@ var ToolErrorType;
|
|
|
360
324
|
ToolErrorType["INVALID_ARGS"] = "INVALID_ARGS";
|
|
361
325
|
ToolErrorType["EXECUTION_FAILED"] = "EXECUTION_FAILED";
|
|
362
326
|
ToolErrorType["USER_CANCELLED"] = "USER_CANCELLED";
|
|
363
|
-
})(ToolErrorType || (
|
|
364
|
-
class AgenticTools {
|
|
327
|
+
})(ToolErrorType || (ToolErrorType = {}));
|
|
328
|
+
export class AgenticTools {
|
|
365
329
|
logger;
|
|
366
330
|
cwd;
|
|
367
331
|
permissionCallback;
|
|
@@ -421,7 +385,7 @@ class AgenticTools {
|
|
|
421
385
|
throw new Error(`Invalid external command for ${toolName} during ${operation}: command must be a non-empty string.`);
|
|
422
386
|
}
|
|
423
387
|
try {
|
|
424
|
-
return
|
|
388
|
+
return execSync(command, options);
|
|
425
389
|
}
|
|
426
390
|
catch (error) {
|
|
427
391
|
if (cleanup) {
|
|
@@ -609,7 +573,7 @@ class AgenticTools {
|
|
|
609
573
|
fs.unlinkSync(lastOp.filePath);
|
|
610
574
|
return {
|
|
611
575
|
success: true,
|
|
612
|
-
output: `${
|
|
576
|
+
output: `${CH.success} Undone: ${lastOp.description}\n File deleted: ${lastOp.filePath}`,
|
|
613
577
|
metadata: { remainingUndos: this.undoStack.length },
|
|
614
578
|
};
|
|
615
579
|
}
|
|
@@ -619,7 +583,7 @@ class AgenticTools {
|
|
|
619
583
|
fs.writeFileSync(lastOp.filePath, lastOp.originalContent, 'utf-8');
|
|
620
584
|
return {
|
|
621
585
|
success: true,
|
|
622
|
-
output: `${
|
|
586
|
+
output: `${CH.success} Undone: ${lastOp.description}\n File restored: ${lastOp.filePath}`,
|
|
623
587
|
metadata: { remainingUndos: this.undoStack.length },
|
|
624
588
|
};
|
|
625
589
|
}
|
|
@@ -977,11 +941,45 @@ class AgenticTools {
|
|
|
977
941
|
}
|
|
978
942
|
return lastError || this.createErrorResult(ToolErrorType.EXECUTION_FAILED, 'Unknown error after retries');
|
|
979
943
|
}
|
|
944
|
+
/**
|
|
945
|
+
* Tool names that mutate the user's filesystem, shell state, or remote
|
|
946
|
+
* services. Used by the dry-run / read-only gate below.
|
|
947
|
+
*/
|
|
948
|
+
static MUTATING_TOOLS = new Set([
|
|
949
|
+
'write_file', 'edit_file', 'multi_edit',
|
|
950
|
+
'bash', 'ssh_exec',
|
|
951
|
+
'git', 'repo',
|
|
952
|
+
]);
|
|
953
|
+
/**
|
|
954
|
+
* `VIGTHORIA_DRY_RUN=1` and `VIGTHORIA_READ_ONLY=1` are end-user safety
|
|
955
|
+
* switches: every mutating tool short-circuits with a clear "would have
|
|
956
|
+
* happened" result instead of touching the filesystem or network.
|
|
957
|
+
*
|
|
958
|
+
* The flags are checked at call-time (not at construction time) so they
|
|
959
|
+
* can be flipped per-prompt by users who want to inspect agent intent
|
|
960
|
+
* before committing.
|
|
961
|
+
*/
|
|
962
|
+
isDryRunActive() {
|
|
963
|
+
const flag = (name) => String(process.env[name] || '').trim().toLowerCase();
|
|
964
|
+
const isOn = (v) => v === '1' || v === 'true' || v === 'yes' || v === 'on';
|
|
965
|
+
return isOn(flag('VIGTHORIA_DRY_RUN')) || isOn(flag('VIGTHORIA_READ_ONLY'));
|
|
966
|
+
}
|
|
980
967
|
/**
|
|
981
968
|
* Execute the actual tool operation
|
|
982
969
|
*/
|
|
983
970
|
async executeTool(call) {
|
|
984
971
|
try {
|
|
972
|
+
if (this.isDryRunActive() && AgenticTools.MUTATING_TOOLS.has(call.tool)) {
|
|
973
|
+
const argSummary = this.safeStringifyArgs(call.args);
|
|
974
|
+
const message = `Dry-run: ${call.tool} would have run with ${argSummary}. Unset VIGTHORIA_DRY_RUN to execute.`;
|
|
975
|
+
this.logger.warn(message);
|
|
976
|
+
return {
|
|
977
|
+
success: true,
|
|
978
|
+
output: '',
|
|
979
|
+
dryRun: true,
|
|
980
|
+
message,
|
|
981
|
+
};
|
|
982
|
+
}
|
|
985
983
|
switch (call.tool) {
|
|
986
984
|
case 'read_file':
|
|
987
985
|
return this.readFile(call.args);
|
|
@@ -1171,10 +1169,10 @@ class AgenticTools {
|
|
|
1171
1169
|
*/
|
|
1172
1170
|
formatPermissionRequest(call, tool) {
|
|
1173
1171
|
const riskColors = {
|
|
1174
|
-
low:
|
|
1175
|
-
medium:
|
|
1176
|
-
high:
|
|
1177
|
-
critical:
|
|
1172
|
+
low: chalk.green,
|
|
1173
|
+
medium: chalk.yellow,
|
|
1174
|
+
high: chalk.red,
|
|
1175
|
+
critical: chalk.bgRed.white,
|
|
1178
1176
|
};
|
|
1179
1177
|
const riskIcons = {
|
|
1180
1178
|
low: '🟢',
|
|
@@ -1184,41 +1182,41 @@ class AgenticTools {
|
|
|
1184
1182
|
};
|
|
1185
1183
|
const riskColor = riskColors[tool.riskLevel];
|
|
1186
1184
|
const riskIcon = riskIcons[tool.riskLevel];
|
|
1187
|
-
let msg = `\n${riskIcon} ${riskColor(`${tool.riskLevel.toUpperCase()} RISK`)} - AI wants to use ${
|
|
1188
|
-
msg +=
|
|
1185
|
+
let msg = `\n${riskIcon} ${riskColor(`${tool.riskLevel.toUpperCase()} RISK`)} - AI wants to use ${chalk.cyan.bold(call.tool)}\n`;
|
|
1186
|
+
msg += chalk.gray(CH.hLine.repeat(50)) + '\n';
|
|
1189
1187
|
// Tool-specific details
|
|
1190
1188
|
if (call.tool === 'bash') {
|
|
1191
|
-
msg += `${
|
|
1189
|
+
msg += `${chalk.gray(CH.teeR + CH.hLine)} ${chalk.white('Command:')} ${chalk.yellow(call.args.command)}\n`;
|
|
1192
1190
|
if (call.args.cwd) {
|
|
1193
|
-
msg += `${
|
|
1191
|
+
msg += `${chalk.gray(CH.teeR + CH.hLine)} ${chalk.white('Directory:')} ${call.args.cwd}\n`;
|
|
1194
1192
|
}
|
|
1195
|
-
msg += `${
|
|
1193
|
+
msg += `${chalk.gray(CH.teeR + CH.hLine)} ${chalk.white('Timeout:')} ${call.args.timeout || '30'}s\n`;
|
|
1196
1194
|
}
|
|
1197
1195
|
else if (call.tool === 'write_file') {
|
|
1198
|
-
msg += `${
|
|
1196
|
+
msg += `${chalk.gray(CH.teeR + CH.hLine)} ${chalk.white('File:')} ${chalk.cyan(call.args.path)}\n`;
|
|
1199
1197
|
const preview = call.args.content.substring(0, 100);
|
|
1200
|
-
msg += `${
|
|
1201
|
-
msg += `${
|
|
1198
|
+
msg += `${chalk.gray(CH.teeR + CH.hLine)} ${chalk.white('Content preview:')} ${chalk.gray(preview)}${call.args.content.length > 100 ? '...' : ''}\n`;
|
|
1199
|
+
msg += `${chalk.gray(CH.teeR + CH.hLine)} ${chalk.white('Size:')} ${call.args.content.length} bytes\n`;
|
|
1202
1200
|
}
|
|
1203
1201
|
else if (call.tool === 'edit_file') {
|
|
1204
|
-
msg += `${
|
|
1205
|
-
msg += `${
|
|
1206
|
-
msg += `${
|
|
1202
|
+
msg += `${chalk.gray(CH.teeR + CH.hLine)} ${chalk.white('File:')} ${chalk.cyan(call.args.path)}\n`;
|
|
1203
|
+
msg += `${chalk.gray(CH.teeR + CH.hLine)} ${chalk.white('Replace:')} ${chalk.red(call.args.old_text.substring(0, 50))}${call.args.old_text.length > 50 ? '...' : ''}\n`;
|
|
1204
|
+
msg += `${chalk.gray(CH.teeR + CH.hLine)} ${chalk.white('With:')} ${chalk.green(call.args.new_text.substring(0, 50))}${call.args.new_text.length > 50 ? '...' : ''}\n`;
|
|
1207
1205
|
}
|
|
1208
1206
|
else if (call.tool === 'git') {
|
|
1209
|
-
msg += `${
|
|
1207
|
+
msg += `${chalk.gray(CH.teeR + CH.hLine)} ${chalk.white('Command:')} git ${chalk.yellow(call.args.args)}\n`;
|
|
1210
1208
|
}
|
|
1211
1209
|
// Safety info
|
|
1212
|
-
msg +=
|
|
1210
|
+
msg += chalk.gray(CH.hLine.repeat(50)) + '\n';
|
|
1213
1211
|
const canUndo = ['write_file', 'edit_file'].includes(call.tool);
|
|
1214
|
-
msg += `${
|
|
1215
|
-
msg += `${
|
|
1212
|
+
msg += `${chalk.gray(CH.teeR + CH.hLine)} ${chalk.white('Undo:')} ${canUndo ? chalk.green(CH.success + ' Available (use /undo)') : chalk.gray(CH.error + ' Not available')}\n`;
|
|
1213
|
+
msg += `${chalk.gray(CH.bl + CH.hLine)} ${chalk.white('Category:')} ${tool.category}\n`;
|
|
1216
1214
|
if (tool.dangerous) {
|
|
1217
|
-
msg += `\n${
|
|
1218
|
-
msg +=
|
|
1215
|
+
msg += `\n${chalk.red.bold(CH.warnEmoji + ' WARNING: This is a potentially dangerous action!')}\n`;
|
|
1216
|
+
msg += chalk.red(' The AI is requesting to execute commands on your system.\n');
|
|
1219
1217
|
}
|
|
1220
1218
|
// Add batch approval hint
|
|
1221
|
-
msg += `\n${
|
|
1219
|
+
msg += `\n${chalk.gray('Respond:')} ${chalk.white('[y]es')} ${chalk.gray('/')} ${chalk.white('[n]o')} ${chalk.gray('/')} ${chalk.cyan('[a]pprove all')} ${chalk.cyan(call.tool)} ${chalk.gray('for this turn')}\n`;
|
|
1222
1220
|
return msg;
|
|
1223
1221
|
}
|
|
1224
1222
|
sleep(ms) {
|
|
@@ -1695,7 +1693,7 @@ class AgenticTools {
|
|
|
1695
1693
|
grepWindows(args, searchPath) {
|
|
1696
1694
|
// 1. Try ripgrep (rg) first — best cross-platform search tool
|
|
1697
1695
|
try {
|
|
1698
|
-
|
|
1696
|
+
execSync('rg --version', { encoding: 'utf-8', timeout: 5000, stdio: 'pipe' });
|
|
1699
1697
|
return this.grepWithRg(args, searchPath);
|
|
1700
1698
|
}
|
|
1701
1699
|
catch (error) {
|
|
@@ -1820,7 +1818,7 @@ class AgenticTools {
|
|
|
1820
1818
|
const escapedPattern = args.pattern.replace(/'/g, "''");
|
|
1821
1819
|
const cmd = `powershell -NoProfile -Command "Get-ChildItem -Path '${psPath}' -Recurse -File ${includeFilter} | Select-String -Pattern '${escapedPattern}' | ForEach-Object { $_.Path + ':' + $_.LineNumber + ':' + $_.Line }"`;
|
|
1822
1820
|
try {
|
|
1823
|
-
const output =
|
|
1821
|
+
const output = execSync(cmd, {
|
|
1824
1822
|
cwd: this.cwd,
|
|
1825
1823
|
encoding: 'utf-8',
|
|
1826
1824
|
maxBuffer: 5 * 1024 * 1024,
|
|
@@ -1977,7 +1975,7 @@ class AgenticTools {
|
|
|
1977
1975
|
}
|
|
1978
1976
|
git(args) {
|
|
1979
1977
|
try {
|
|
1980
|
-
const output =
|
|
1978
|
+
const output = execSync(`git ${args.args}`, {
|
|
1981
1979
|
cwd: this.cwd,
|
|
1982
1980
|
encoding: 'utf-8',
|
|
1983
1981
|
timeout: 60000,
|
|
@@ -2094,7 +2092,7 @@ class AgenticTools {
|
|
|
2094
2092
|
// Try proxy first; direct Community server is only reachable when running on the
|
|
2095
2093
|
// Vigthoria backend itself (isServerRuntime), never expose this fallback to remote users.
|
|
2096
2094
|
const pushUrls = [`${apiBase}/api/community-repo/push`];
|
|
2097
|
-
if (
|
|
2095
|
+
if (isServerRuntime()) {
|
|
2098
2096
|
const directHost = ['local', 'host'].join('');
|
|
2099
2097
|
pushUrls.push(`http://${directHost}:9000/api/repo/push`);
|
|
2100
2098
|
}
|
|
@@ -2194,7 +2192,7 @@ class AgenticTools {
|
|
|
2194
2192
|
suggestion: 'Available actions: push, pull, list, status, share, delete, clone'
|
|
2195
2193
|
};
|
|
2196
2194
|
}
|
|
2197
|
-
const output =
|
|
2195
|
+
const output = execSync(command, {
|
|
2198
2196
|
cwd: this.cwd,
|
|
2199
2197
|
encoding: 'utf-8',
|
|
2200
2198
|
timeout: 120000,
|
|
@@ -2510,7 +2508,7 @@ class AgenticTools {
|
|
|
2510
2508
|
sshCommand = `ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 ${host} '${command.replace(/'/g, "'\\''")}'`;
|
|
2511
2509
|
execOptions.shell = '/bin/sh';
|
|
2512
2510
|
}
|
|
2513
|
-
const output =
|
|
2511
|
+
const output = execSync(sshCommand, execOptions);
|
|
2514
2512
|
return {
|
|
2515
2513
|
success: true,
|
|
2516
2514
|
output: output.trim(),
|
|
@@ -2739,7 +2737,7 @@ class AgenticTools {
|
|
|
2739
2737
|
}
|
|
2740
2738
|
return {
|
|
2741
2739
|
success: true,
|
|
2742
|
-
output: `${
|
|
2740
|
+
output: `${CH.success} Atomically edited ${applied.length} file(s):\n${applied.map(f => ` ✓ ${f}`).join('\n')}`,
|
|
2743
2741
|
undoable: true,
|
|
2744
2742
|
metadata: { filesEdited: applied.length, files: applied },
|
|
2745
2743
|
};
|
|
@@ -2882,7 +2880,7 @@ class AgenticTools {
|
|
|
2882
2880
|
rgArgs.push('--', query, this.cwd);
|
|
2883
2881
|
const isWin = process.platform === 'win32';
|
|
2884
2882
|
const quote = (s) => isWin ? `"${s}"` : `'${s}'`;
|
|
2885
|
-
const rgOutput =
|
|
2883
|
+
const rgOutput = execSync(`rg ${rgArgs.map(a => quote(a)).join(' ')}`, {
|
|
2886
2884
|
encoding: 'utf-8',
|
|
2887
2885
|
timeout: 15000,
|
|
2888
2886
|
maxBuffer: 5 * 1024 * 1024,
|
|
@@ -3450,4 +3448,3 @@ To use a tool, output a JSON block in a code fence with "tool" language:
|
|
|
3450
3448
|
return prompt;
|
|
3451
3449
|
}
|
|
3452
3450
|
}
|
|
3453
|
-
exports.AgenticTools = AgenticTools;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Vigthoria CLI — Workspace Hash Cache
|
|
4
3
|
*
|
|
@@ -7,24 +6,17 @@
|
|
|
7
6
|
* - Changed/new files → sent first (highest budget priority)
|
|
8
7
|
* - Unchanged files → sent last (trimmed by budget if token limit reached)
|
|
9
8
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.getChangedFiles = getChangedFiles;
|
|
15
|
-
exports.updateWorkspaceCache = updateWorkspaceCache;
|
|
16
|
-
exports.summarizeChanges = summarizeChanges;
|
|
17
|
-
const node_crypto_1 = require("node:crypto");
|
|
18
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
19
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
import { createHash } from 'node:crypto';
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
20
12
|
const CACHE_SUBDIR = '.vigthoria';
|
|
21
13
|
const CACHE_FILENAME = 'file-hashes.json';
|
|
22
14
|
function getCachePath(workspacePath) {
|
|
23
|
-
return
|
|
15
|
+
return path.join(workspacePath, CACHE_SUBDIR, CACHE_FILENAME);
|
|
24
16
|
}
|
|
25
17
|
function hashFile(absPath) {
|
|
26
18
|
try {
|
|
27
|
-
return
|
|
19
|
+
return createHash('sha256').update(fs.readFileSync(absPath)).digest('hex');
|
|
28
20
|
}
|
|
29
21
|
catch {
|
|
30
22
|
return '';
|
|
@@ -32,7 +24,7 @@ function hashFile(absPath) {
|
|
|
32
24
|
}
|
|
33
25
|
function loadCache(workspacePath) {
|
|
34
26
|
try {
|
|
35
|
-
return JSON.parse(
|
|
27
|
+
return JSON.parse(fs.readFileSync(getCachePath(workspacePath), 'utf-8'));
|
|
36
28
|
}
|
|
37
29
|
catch {
|
|
38
30
|
return {};
|
|
@@ -40,10 +32,10 @@ function loadCache(workspacePath) {
|
|
|
40
32
|
}
|
|
41
33
|
function persistCache(workspacePath, hashes) {
|
|
42
34
|
try {
|
|
43
|
-
const cacheDir =
|
|
44
|
-
if (!
|
|
45
|
-
|
|
46
|
-
|
|
35
|
+
const cacheDir = path.join(workspacePath, CACHE_SUBDIR);
|
|
36
|
+
if (!fs.existsSync(cacheDir))
|
|
37
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
38
|
+
fs.writeFileSync(getCachePath(workspacePath), JSON.stringify(hashes, null, 2), 'utf-8');
|
|
47
39
|
}
|
|
48
40
|
catch { /* non-fatal */ }
|
|
49
41
|
}
|
|
@@ -53,13 +45,13 @@ function persistCache(workspacePath, hashes) {
|
|
|
53
45
|
* @param workspacePath Absolute project root.
|
|
54
46
|
* @param relativePaths Workspace-relative (or absolute) file paths to check.
|
|
55
47
|
*/
|
|
56
|
-
function getChangedFiles(workspacePath, relativePaths) {
|
|
48
|
+
export function getChangedFiles(workspacePath, relativePaths) {
|
|
57
49
|
const cache = loadCache(workspacePath);
|
|
58
50
|
const changed = [];
|
|
59
51
|
const unchanged = [];
|
|
60
52
|
for (const rel of relativePaths) {
|
|
61
|
-
const abs =
|
|
62
|
-
const key =
|
|
53
|
+
const abs = path.isAbsolute(rel) ? rel : path.join(workspacePath, rel);
|
|
54
|
+
const key = path.relative(workspacePath, abs).replace(/\\/g, '/');
|
|
63
55
|
const currentHash = hashFile(abs);
|
|
64
56
|
if (!currentHash || cache[key] !== currentHash) {
|
|
65
57
|
changed.push(rel);
|
|
@@ -74,13 +66,13 @@ function getChangedFiles(workspacePath, relativePaths) {
|
|
|
74
66
|
* Write current hashes for the given files into the persistent cache.
|
|
75
67
|
* Call this after an agent run completes successfully.
|
|
76
68
|
*/
|
|
77
|
-
function updateWorkspaceCache(workspacePath, relativePaths) {
|
|
78
|
-
if (!workspacePath || !
|
|
69
|
+
export function updateWorkspaceCache(workspacePath, relativePaths) {
|
|
70
|
+
if (!workspacePath || !fs.existsSync(workspacePath))
|
|
79
71
|
return;
|
|
80
72
|
const cache = loadCache(workspacePath);
|
|
81
73
|
for (const rel of relativePaths) {
|
|
82
|
-
const abs =
|
|
83
|
-
const key =
|
|
74
|
+
const abs = path.isAbsolute(rel) ? rel : path.join(workspacePath, rel);
|
|
75
|
+
const key = path.relative(workspacePath, abs).replace(/\\/g, '/');
|
|
84
76
|
const hash = hashFile(abs);
|
|
85
77
|
if (hash)
|
|
86
78
|
cache[key] = hash;
|
|
@@ -90,7 +82,7 @@ function updateWorkspaceCache(workspacePath, relativePaths) {
|
|
|
90
82
|
/**
|
|
91
83
|
* Return a human-readable summary of changes since last cache update.
|
|
92
84
|
*/
|
|
93
|
-
function summarizeChanges(workspacePath, relativePaths) {
|
|
85
|
+
export function summarizeChanges(workspacePath, relativePaths) {
|
|
94
86
|
const { changed, unchanged, total } = getChangedFiles(workspacePath, relativePaths);
|
|
95
87
|
return `${changed.length} changed, ${unchanged.length} unchanged of ${total} files tracked`;
|
|
96
88
|
}
|