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 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.12.tgz
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):**
@@ -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() {
@@ -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
  */
@@ -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 all standard tools (read_file, write_file, edit_file, bash, grep, list_dir, glob, git).',
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
- const content = fs.readFileSync(resolvedPath, 'utf-8');
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
- backups.push({ path: resolvedPath, content });
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 newContent = edit.content.replace(edit.old_text, edit.new_text);
2093
- fs.writeFileSync(edit.resolvedPath, newContent, 'utf-8');
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(/\*\*/g, '').replace(/\*/g, '.*').replace(/\?/g, '.');
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 rgOutput = (0, child_process_1.execSync)(`rg ${rgArgs.map(a => `'${a}'`).join(' ')}`, {
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.6.52",
3
+ "version": "1.6.53",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "main": "dist/index.js",
6
6
  "files": [