vigthoria-cli 1.6.52 → 1.6.53
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 +6 -1
- package/dist/commands/chat.js +4 -1
- package/dist/utils/tools.d.ts +1 -1
- package/dist/utils/tools.js +28 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,11 @@ AI-powered terminal coding assistant integrated with Vigthoria's AI models and s
|
|
|
24
24
|
- 🔐 **Secure Auth** - Integration with Vigthoria accounts
|
|
25
25
|
- 📦 **Project Context** - Understands your codebase
|
|
26
26
|
- 🗂️ **Vigthoria Repo** - Push/Pull projects to your personal cloud repository
|
|
27
|
+
- 🔍 **Deep Codebase Search** - Indexed search across files, symbols, and content via ripgrep
|
|
28
|
+
- ✏️ **Atomic Multi-File Edits** - Coordinated changes across multiple files with rollback
|
|
29
|
+
- 🧩 **Sub-Agent Delegation** - Spawn focused sub-agents for parallel investigation
|
|
30
|
+
- 🔒 **Persistent Permissions** - Per-project tool approvals saved across sessions
|
|
31
|
+
- 📝 **Multi-Line Input** - Block mode ({{{ }}}) and line continuation (\\) for complex prompts
|
|
27
32
|
|
|
28
33
|
## Installation
|
|
29
34
|
|
|
@@ -96,7 +101,7 @@ If you see `ENOTFOUND registry.npmjs.org`, try these solutions:
|
|
|
96
101
|
4. **Direct tarball download:**
|
|
97
102
|
```bash
|
|
98
103
|
# Download directly
|
|
99
|
-
npm install -g https://cli.vigthoria.io/downloads/vigthoria-cli-1.6.
|
|
104
|
+
npm install -g https://cli.vigthoria.io/downloads/vigthoria-cli-1.6.52.tgz
|
|
100
105
|
```
|
|
101
106
|
|
|
102
107
|
5. **Use Git clone method (no npm registry needed):**
|
package/dist/commands/chat.js
CHANGED
|
@@ -2473,13 +2473,16 @@ class ChatCommand {
|
|
|
2473
2473
|
});
|
|
2474
2474
|
console.log(action);
|
|
2475
2475
|
const answer = await new Promise((resolve) => {
|
|
2476
|
-
rl.question(chalk_1.default.yellow('Approve? [y]es / [n]o / [a]ll this turn: '), resolve);
|
|
2476
|
+
rl.question(chalk_1.default.yellow('Approve? [y]es / [n]o / [a]ll this turn / [p]ersist: '), resolve);
|
|
2477
2477
|
});
|
|
2478
2478
|
rl.close();
|
|
2479
2479
|
const normalized = answer.trim().toLowerCase();
|
|
2480
2480
|
if (normalized === 'a' || normalized === 'all') {
|
|
2481
2481
|
return 'batch';
|
|
2482
2482
|
}
|
|
2483
|
+
if (normalized === 'p' || normalized === 'persist') {
|
|
2484
|
+
return 'persist';
|
|
2485
|
+
}
|
|
2483
2486
|
return normalized === 'y' || normalized === 'yes';
|
|
2484
2487
|
}
|
|
2485
2488
|
getCurrentSessionInfo() {
|
package/dist/utils/tools.d.ts
CHANGED
|
@@ -74,7 +74,7 @@ export declare class AgenticTools {
|
|
|
74
74
|
private static permissionsFile;
|
|
75
75
|
constructor(logger: Logger, cwd: string, permissionCallback: (action: string, options?: {
|
|
76
76
|
batchApproval?: boolean;
|
|
77
|
-
}) => Promise<boolean | 'batch'>, autoApprove?: boolean);
|
|
77
|
+
}) => Promise<boolean | 'batch' | 'persist'>, autoApprove?: boolean);
|
|
78
78
|
/**
|
|
79
79
|
* Load persistent permissions for the current project
|
|
80
80
|
*/
|
package/dist/utils/tools.js
CHANGED
|
@@ -493,6 +493,11 @@ class AgenticTools {
|
|
|
493
493
|
if (approved === 'batch') {
|
|
494
494
|
this.sessionApprovedTools.add(normalizedCall.tool);
|
|
495
495
|
}
|
|
496
|
+
else if (approved === 'persist') {
|
|
497
|
+
this.sessionApprovedTools.add(normalizedCall.tool);
|
|
498
|
+
this.savePersistentPermission(normalizedCall.tool);
|
|
499
|
+
this.logger.info(`${normalizedCall.tool}: Saved as persistent permission`);
|
|
500
|
+
}
|
|
496
501
|
}
|
|
497
502
|
}
|
|
498
503
|
// Execute with retry logic for applicable operations
|
|
@@ -1069,6 +1074,11 @@ class AgenticTools {
|
|
|
1069
1074
|
/\bmkfs\b/i, // Format filesystems
|
|
1070
1075
|
/>\s*\/dev\/sd/i, // Writing to disk devices
|
|
1071
1076
|
/\bdd\s+.*of=/i, // dd write operations
|
|
1077
|
+
/\bnode\s+-e\b/i, // Node eval (sandbox bypass)
|
|
1078
|
+
/\bnode\s+--eval\b/i, // Node eval long form
|
|
1079
|
+
/\bpython3?\s+-c\b/i, // Python exec (sandbox bypass)
|
|
1080
|
+
/\bruby\s+-e\b/i, // Ruby eval (sandbox bypass)
|
|
1081
|
+
/\bperl\s+-e\b/i, // Perl eval (sandbox bypass)
|
|
1072
1082
|
];
|
|
1073
1083
|
for (const pattern of blockedPatterns) {
|
|
1074
1084
|
if (pattern.test(args.command)) {
|
|
@@ -1987,11 +1997,11 @@ class AgenticTools {
|
|
|
1987
1997
|
// Build a focused system prompt for the sub-agent
|
|
1988
1998
|
const systemPrompt = [
|
|
1989
1999
|
'You are a focused sub-agent spawned to complete a specific subtask.',
|
|
1990
|
-
'You have access to
|
|
2000
|
+
'You have access to standard tools (read_file, write_file, edit_file, bash, grep, list_dir, glob, git). You cannot spawn sub-agents.',
|
|
1991
2001
|
'Complete the task thoroughly and return a detailed result.',
|
|
1992
2002
|
`Working directory: ${workingDir}`,
|
|
1993
2003
|
'',
|
|
1994
|
-
AgenticTools.getToolsForPrompt(),
|
|
2004
|
+
AgenticTools.getToolsForPrompt().replace(/### task[\s\S]*?(?=###|$)/, ''), // Strip task from sub-agent
|
|
1995
2005
|
].join('\n');
|
|
1996
2006
|
const messages = [
|
|
1997
2007
|
{ role: 'system', content: systemPrompt },
|
|
@@ -2059,6 +2069,7 @@ class AgenticTools {
|
|
|
2059
2069
|
return this.createErrorResult(ToolErrorType.INVALID_ARGS, `Invalid edits JSON: ${parseError.message}`, 'Provide edits as a JSON array: [{"path": "file.ts", "old_text": "find", "new_text": "replace"}]');
|
|
2060
2070
|
}
|
|
2061
2071
|
// Validate all edits can proceed before modifying anything
|
|
2072
|
+
const contentMap = new Map();
|
|
2062
2073
|
const backups = [];
|
|
2063
2074
|
const resolvedEdits = [];
|
|
2064
2075
|
for (let i = 0; i < edits.length; i++) {
|
|
@@ -2073,7 +2084,13 @@ class AgenticTools {
|
|
|
2073
2084
|
if (!fs.existsSync(resolvedPath)) {
|
|
2074
2085
|
return this.createErrorResult(ToolErrorType.FILE_NOT_FOUND, `Edit ${i}: file not found: ${edit.path}`, 'Use write_file to create new files instead.');
|
|
2075
2086
|
}
|
|
2076
|
-
|
|
2087
|
+
// Use contentMap to track cumulative edits to the same file
|
|
2088
|
+
if (!contentMap.has(resolvedPath)) {
|
|
2089
|
+
const diskContent = fs.readFileSync(resolvedPath, 'utf-8');
|
|
2090
|
+
contentMap.set(resolvedPath, diskContent);
|
|
2091
|
+
backups.push({ path: resolvedPath, content: diskContent });
|
|
2092
|
+
}
|
|
2093
|
+
const content = contentMap.get(resolvedPath);
|
|
2077
2094
|
if (!content.includes(edit.old_text)) {
|
|
2078
2095
|
return this.createErrorResult(ToolErrorType.EXECUTION_FAILED, `Edit ${i}: old_text not found in ${edit.path}`, `The text to replace was not found. Use read_file to verify the file contents.`);
|
|
2079
2096
|
}
|
|
@@ -2082,15 +2099,16 @@ class AgenticTools {
|
|
|
2082
2099
|
if (matchCount > 1) {
|
|
2083
2100
|
return this.createErrorResult(ToolErrorType.EXECUTION_FAILED, `Edit ${i}: old_text matches ${matchCount} locations in ${edit.path}`, 'Make old_text more specific to match exactly one location. Include surrounding context.');
|
|
2084
2101
|
}
|
|
2085
|
-
|
|
2102
|
+
// Apply edit to contentMap so subsequent edits to same file see updated content
|
|
2103
|
+
contentMap.set(resolvedPath, content.replace(edit.old_text, edit.new_text));
|
|
2086
2104
|
resolvedEdits.push({ resolvedPath, old_text: edit.old_text, new_text: edit.new_text, content });
|
|
2087
2105
|
}
|
|
2088
2106
|
// Apply all edits
|
|
2089
2107
|
const applied = [];
|
|
2090
2108
|
try {
|
|
2091
2109
|
for (const edit of resolvedEdits) {
|
|
2092
|
-
const
|
|
2093
|
-
fs.writeFileSync(edit.resolvedPath,
|
|
2110
|
+
const finalContent = contentMap.get(edit.resolvedPath) || edit.content.replace(edit.old_text, edit.new_text);
|
|
2111
|
+
fs.writeFileSync(edit.resolvedPath, finalContent, 'utf-8');
|
|
2094
2112
|
applied.push(path.relative(this.cwd, edit.resolvedPath));
|
|
2095
2113
|
}
|
|
2096
2114
|
// Push undo operations for all edits
|
|
@@ -2163,7 +2181,7 @@ class AgenticTools {
|
|
|
2163
2181
|
if (pattern) {
|
|
2164
2182
|
const basename = entry.name;
|
|
2165
2183
|
// Simple glob matching for extension patterns like *.ts, *.js
|
|
2166
|
-
const globPattern = pattern.replace(
|
|
2184
|
+
const globPattern = pattern.replace(/\*\*\//g, '(.*/)?').replace(/\*/g, '[^/]*').replace(/\?/g, '.');
|
|
2167
2185
|
if (new RegExp(globPattern, 'i').test(basename) || fullPath.includes(pattern.replace(/\*/g, ''))) {
|
|
2168
2186
|
files.push(fullPath);
|
|
2169
2187
|
}
|
|
@@ -2247,7 +2265,9 @@ class AgenticTools {
|
|
|
2247
2265
|
if (includePattern)
|
|
2248
2266
|
rgArgs.push('-g', includePattern);
|
|
2249
2267
|
rgArgs.push('--', query, this.cwd);
|
|
2250
|
-
const
|
|
2268
|
+
const isWin = process.platform === 'win32';
|
|
2269
|
+
const quote = (s) => isWin ? `"${s}"` : `'${s}'`;
|
|
2270
|
+
const rgOutput = (0, child_process_1.execSync)(`rg ${rgArgs.map(a => quote(a)).join(' ')}`, {
|
|
2251
2271
|
encoding: 'utf-8',
|
|
2252
2272
|
timeout: 15000,
|
|
2253
2273
|
maxBuffer: 5 * 1024 * 1024,
|