neuro-cli 4.1.0 → 4.1.2
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/core/approval.d.ts +7 -0
- package/dist/core/approval.js +32 -3
- package/dist/core/diff-preview.js +2 -1
- package/dist/core/engine.js +2 -2
- package/dist/core/sandbox.js +14 -3
- package/dist/index.js +16 -4
- package/dist/tools/file.js +8 -7
- package/dist/utils/crosspath.d.ts +28 -0
- package/dist/utils/crosspath.js +81 -0
- package/package.json +1 -1
package/dist/core/approval.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Interface as ReadLineInterface } from 'readline';
|
|
1
2
|
export type PermissionMode = 'manual' | 'auto' | 'plan' | 'yolo';
|
|
2
3
|
export interface ApprovalResult {
|
|
3
4
|
approved: boolean;
|
|
@@ -39,6 +40,7 @@ export declare class ApprovalSystem {
|
|
|
39
40
|
private persistDecisions;
|
|
40
41
|
private consecutiveAutoApproves;
|
|
41
42
|
private rl;
|
|
43
|
+
private mainRl;
|
|
42
44
|
private stats;
|
|
43
45
|
private pendingBatch;
|
|
44
46
|
private batchTimer;
|
|
@@ -65,6 +67,11 @@ export declare class ApprovalSystem {
|
|
|
65
67
|
private isWhitelisted;
|
|
66
68
|
private getPattern;
|
|
67
69
|
private getAlwaysKey;
|
|
70
|
+
/**
|
|
71
|
+
* Set the main readline interface from index.ts
|
|
72
|
+
* This prevents creating a second readline on the same stdin
|
|
73
|
+
*/
|
|
74
|
+
setMainReadline(rl: ReadLineInterface): void;
|
|
68
75
|
private readline;
|
|
69
76
|
close(): void;
|
|
70
77
|
addAutoApprove(toolName: string): void;
|
package/dist/core/approval.js
CHANGED
|
@@ -23,6 +23,7 @@ export class ApprovalSystem {
|
|
|
23
23
|
persistDecisions = true;
|
|
24
24
|
consecutiveAutoApproves = 0;
|
|
25
25
|
rl = null;
|
|
26
|
+
mainRl = null;
|
|
26
27
|
stats = new Map();
|
|
27
28
|
pendingBatch = [];
|
|
28
29
|
batchTimer = null;
|
|
@@ -333,10 +334,38 @@ export class ApprovalSystem {
|
|
|
333
334
|
getAlwaysKey(toolName, args) {
|
|
334
335
|
return `${toolName}:${this.getPattern(args)}`;
|
|
335
336
|
}
|
|
337
|
+
/**
|
|
338
|
+
* Set the main readline interface from index.ts
|
|
339
|
+
* This prevents creating a second readline on the same stdin
|
|
340
|
+
*/
|
|
341
|
+
setMainReadline(rl) {
|
|
342
|
+
this.mainRl = rl;
|
|
343
|
+
}
|
|
336
344
|
readline(prompt) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
345
|
+
// Pause the main readline to prevent it from also reading stdin input
|
|
346
|
+
// This is the root cause of double character input (YY instead of Y)
|
|
347
|
+
const mainInputStream = this.mainRl?.input;
|
|
348
|
+
const wasMainPaused = mainInputStream?.isPaused?.();
|
|
349
|
+
if (this.mainRl && mainInputStream && mainInputStream.isPaused()) {
|
|
350
|
+
// Already paused, skip
|
|
351
|
+
}
|
|
352
|
+
else if (this.mainRl) {
|
|
353
|
+
this.mainRl.pause();
|
|
354
|
+
}
|
|
355
|
+
return new Promise((resolve) => {
|
|
356
|
+
// Reuse the main readline if available, otherwise create a temporary one
|
|
357
|
+
const rl = this.mainRl || this.rl || createInterface({ input: process.stdin, output: process.stdout });
|
|
358
|
+
if (!this.rl && !this.mainRl)
|
|
359
|
+
this.rl = rl;
|
|
360
|
+
rl.question(prompt, (answer) => {
|
|
361
|
+
// Resume the main readline after getting the answer
|
|
362
|
+
if (this.mainRl) {
|
|
363
|
+
this.mainRl.resume();
|
|
364
|
+
this.mainRl.prompt();
|
|
365
|
+
}
|
|
366
|
+
resolve(answer);
|
|
367
|
+
});
|
|
368
|
+
});
|
|
340
369
|
}
|
|
341
370
|
close() {
|
|
342
371
|
if (this.rl) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// Shows file changes before applying them
|
|
4
4
|
// ============================================================
|
|
5
5
|
import { readFileSync, existsSync } from 'fs';
|
|
6
|
+
import { createInterface } from 'readline';
|
|
6
7
|
import chalk from 'chalk';
|
|
7
8
|
export class DiffPreview {
|
|
8
9
|
/**
|
|
@@ -128,7 +129,7 @@ export class DiffPreview {
|
|
|
128
129
|
*/
|
|
129
130
|
static async confirmDiff(diff) {
|
|
130
131
|
DiffPreview.renderDiff(diff);
|
|
131
|
-
const rl =
|
|
132
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
132
133
|
return new Promise((resolve) => {
|
|
133
134
|
rl.question(chalk.cyan(' Apply these changes? [y/n]: '), (answer) => {
|
|
134
135
|
rl.close();
|
package/dist/core/engine.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ============================================================
|
|
2
|
-
// NeuroCLI - NeuroEngine v4.1.
|
|
2
|
+
// NeuroCLI - NeuroEngine v4.1.2
|
|
3
3
|
// The main engine that ties everything together
|
|
4
4
|
// Now with: Sandbox, Plugin SDK, Enhanced MCP, Enhanced Approval,
|
|
5
5
|
// Model Router, Prompt Cache, Undo/Redo, Output Styles,
|
|
@@ -483,7 +483,7 @@ export class NeuroEngine {
|
|
|
483
483
|
this.gitWorktree = new GitWorktreeManager(process.cwd());
|
|
484
484
|
// Auto-Updater
|
|
485
485
|
this.updater = new AutoUpdater({
|
|
486
|
-
currentVersion: '4.1.
|
|
486
|
+
currentVersion: '4.1.2',
|
|
487
487
|
autoCheck: true,
|
|
488
488
|
autoUpdate: false,
|
|
489
489
|
});
|
package/dist/core/sandbox.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { join, resolve, relative, normalize } from 'path';
|
|
7
7
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync } from 'fs';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
|
+
import { normalizeCrossPlatformPath, isWindowsAbsolutePath } from '../utils/crosspath.js';
|
|
9
10
|
export const DEFAULT_SANDBOX_CONFIG = {
|
|
10
11
|
enabled: false,
|
|
11
12
|
rootDir: process.cwd(),
|
|
@@ -312,11 +313,21 @@ export class Sandbox {
|
|
|
312
313
|
}
|
|
313
314
|
// --- Private Helpers ---
|
|
314
315
|
resolvePath(filePath) {
|
|
316
|
+
// Expand home directory
|
|
317
|
+
if (filePath.startsWith('~/') || filePath === '~') {
|
|
318
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || require('os').homedir();
|
|
319
|
+
const expanded = filePath === '~' ? homeDir : join(homeDir, filePath.slice(2));
|
|
320
|
+
return normalize(expanded);
|
|
321
|
+
}
|
|
322
|
+
// Windows absolute path (C:\... or C:/...)
|
|
323
|
+
if (isWindowsAbsolutePath(filePath)) {
|
|
324
|
+
return normalizeCrossPlatformPath(filePath);
|
|
325
|
+
}
|
|
326
|
+
// POSIX absolute path
|
|
315
327
|
if (filePath.startsWith('/'))
|
|
316
328
|
return normalize(filePath);
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
return normalize(resolve(this.config.rootDir, filePath));
|
|
329
|
+
// Relative path - resolve against rootDir
|
|
330
|
+
return normalize(resolve(this.config.rootDir, normalizeCrossPlatformPath(filePath)));
|
|
320
331
|
}
|
|
321
332
|
isUnderRootDir(absPath) {
|
|
322
333
|
const relativePath = relative(this.config.rootDir, absPath);
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// ============================================================
|
|
3
3
|
// NeuroCLI - Advanced AI Terminal Coding Assistant
|
|
4
|
-
// Main Entry Point - v4.1.
|
|
4
|
+
// Main Entry Point - v4.1.2 with cross-platform path fix
|
|
5
5
|
// ============================================================
|
|
6
6
|
import { Command } from 'commander';
|
|
7
7
|
import { createInterface } from 'readline';
|
|
@@ -16,7 +16,7 @@ import { HeadlessMode } from './core/headless.js';
|
|
|
16
16
|
import { ShellCompletionGenerator } from './core/shell-completion.js';
|
|
17
17
|
import chalk from 'chalk';
|
|
18
18
|
import { AutoUpdater } from './core/updater.js';
|
|
19
|
-
const VERSION = '4.1.
|
|
19
|
+
const VERSION = '4.1.2';
|
|
20
20
|
// ---- CLI Setup ----
|
|
21
21
|
const program = new Command();
|
|
22
22
|
program
|
|
@@ -430,14 +430,22 @@ async function startInteractive(options) {
|
|
|
430
430
|
completer: completionEngine.complete,
|
|
431
431
|
});
|
|
432
432
|
rl.prompt();
|
|
433
|
+
// Wire the main readline to the approval system to prevent dual-readline bug
|
|
434
|
+
// (double character input when approval prompt and main rl both read stdin)
|
|
435
|
+
engine.approval.setMainReadline(rl);
|
|
433
436
|
let currentMode = 'auto';
|
|
434
437
|
let currentAgent;
|
|
438
|
+
let isProcessing = false;
|
|
435
439
|
rl.on('line', async (line) => {
|
|
436
440
|
const input = line.trim();
|
|
437
441
|
if (!input) {
|
|
438
442
|
rl.prompt();
|
|
439
443
|
return;
|
|
440
444
|
}
|
|
445
|
+
// Prevent input while processing (approval prompt may be active)
|
|
446
|
+
if (isProcessing) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
441
449
|
// Add to history
|
|
442
450
|
completionEngine.addHistory(input);
|
|
443
451
|
// Handle slash commands
|
|
@@ -909,7 +917,7 @@ async function startInteractive(options) {
|
|
|
909
917
|
}
|
|
910
918
|
break;
|
|
911
919
|
case 'doctor':
|
|
912
|
-
console.log(chalk.bold('\nNeuroCLI v4.1.
|
|
920
|
+
console.log(chalk.bold('\nNeuroCLI v4.1.2 Health Check:\n'));
|
|
913
921
|
console.log(` API Key: ${config.apiKey ? chalk.green('configured') : chalk.red('MISSING')}`);
|
|
914
922
|
console.log(` Default Model: ${chalk.cyan(config.defaultModel)} ${MODELS[config.defaultModel] ? chalk.green('valid') : chalk.red('INVALID')}`);
|
|
915
923
|
console.log(` MCP Servers: ${chalk.cyan(String(engine.mcpClient.listServers().length))}`);
|
|
@@ -1112,11 +1120,15 @@ async function startInteractive(options) {
|
|
|
1112
1120
|
}
|
|
1113
1121
|
// Process message with the engine
|
|
1114
1122
|
try {
|
|
1123
|
+
isProcessing = true;
|
|
1115
1124
|
await engine.processMessage(input, currentMode, currentAgent);
|
|
1116
1125
|
}
|
|
1117
1126
|
catch (error) {
|
|
1118
1127
|
engine.ui.error(error instanceof Error ? error.message : String(error));
|
|
1119
1128
|
}
|
|
1129
|
+
finally {
|
|
1130
|
+
isProcessing = false;
|
|
1131
|
+
}
|
|
1120
1132
|
rl.prompt();
|
|
1121
1133
|
});
|
|
1122
1134
|
rl.on('close', () => {
|
|
@@ -1128,7 +1140,7 @@ async function startInteractive(options) {
|
|
|
1128
1140
|
}
|
|
1129
1141
|
function printHelp(engine) {
|
|
1130
1142
|
const t = engine.ui.theme;
|
|
1131
|
-
console.log(`\n ${t.bold('NeuroCLI v4.1.
|
|
1143
|
+
console.log(`\n ${t.bold('NeuroCLI v4.1.2 Commands:')}\n`);
|
|
1132
1144
|
console.log(` ${t.tool('/help')} Show this help message`);
|
|
1133
1145
|
console.log(` ${t.tool('/model [id]')} Switch or list models`);
|
|
1134
1146
|
console.log(` ${t.tool('/agent [name]')} Switch or list agents`);
|
package/dist/tools/file.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, statSync, readdirSync } from 'fs';
|
|
6
6
|
import { join, relative, extname } from 'path';
|
|
7
7
|
import { execSync } from 'child_process';
|
|
8
|
+
import { resolvePath } from '../utils/crosspath.js';
|
|
8
9
|
const MAX_FILE_SIZE = 1024 * 1024; // 1MB
|
|
9
10
|
const MAX_OUTPUT_LENGTH = 50000;
|
|
10
11
|
function truncateOutput(output, maxLength = MAX_OUTPUT_LENGTH) {
|
|
@@ -32,7 +33,7 @@ export const readFileTool = {
|
|
|
32
33
|
definition: readFileDef,
|
|
33
34
|
risk: 'low',
|
|
34
35
|
async execute(args, context) {
|
|
35
|
-
const filePath =
|
|
36
|
+
const filePath = resolvePath(context.workingDirectory, args.path);
|
|
36
37
|
if (!existsSync(filePath))
|
|
37
38
|
return `Error: File not found: ${args.path}`;
|
|
38
39
|
const stat = statSync(filePath);
|
|
@@ -78,7 +79,7 @@ export const writeFileTool = {
|
|
|
78
79
|
};
|
|
79
80
|
},
|
|
80
81
|
async execute(args, context) {
|
|
81
|
-
const filePath =
|
|
82
|
+
const filePath = resolvePath(context.workingDirectory, args.path);
|
|
82
83
|
const dir = join(filePath, '..');
|
|
83
84
|
if (!existsSync(dir)) {
|
|
84
85
|
mkdirSync(dir, { recursive: true });
|
|
@@ -114,7 +115,7 @@ export const editFileTool = {
|
|
|
114
115
|
};
|
|
115
116
|
},
|
|
116
117
|
async execute(args, context) {
|
|
117
|
-
const filePath =
|
|
118
|
+
const filePath = resolvePath(context.workingDirectory, args.path);
|
|
118
119
|
if (!existsSync(filePath))
|
|
119
120
|
return `Error: File not found: ${args.path}`;
|
|
120
121
|
let content = readFileSync(filePath, 'utf-8');
|
|
@@ -157,7 +158,7 @@ export const deleteFileTool = {
|
|
|
157
158
|
};
|
|
158
159
|
},
|
|
159
160
|
async execute(args, context) {
|
|
160
|
-
const filePath =
|
|
161
|
+
const filePath = resolvePath(context.workingDirectory, args.path);
|
|
161
162
|
if (!existsSync(filePath))
|
|
162
163
|
return `Error: File not found: ${args.path}`;
|
|
163
164
|
unlinkSync(filePath);
|
|
@@ -183,7 +184,7 @@ export const listDirectoryTool = {
|
|
|
183
184
|
definition: listDirDef,
|
|
184
185
|
risk: 'low',
|
|
185
186
|
async execute(args, context) {
|
|
186
|
-
const dirPath =
|
|
187
|
+
const dirPath = resolvePath(context.workingDirectory, args.path || '.');
|
|
187
188
|
if (!existsSync(dirPath))
|
|
188
189
|
return `Error: Directory not found: ${args.path}`;
|
|
189
190
|
const stat = statSync(dirPath);
|
|
@@ -259,7 +260,7 @@ export const searchFilesTool = {
|
|
|
259
260
|
risk: 'low',
|
|
260
261
|
async execute(args, context) {
|
|
261
262
|
const pattern = args.pattern;
|
|
262
|
-
const searchPath =
|
|
263
|
+
const searchPath = resolvePath(context.workingDirectory, args.path || '.');
|
|
263
264
|
const fileType = args.file_type;
|
|
264
265
|
const maxResults = args.max_results || 50;
|
|
265
266
|
const caseInsensitive = args.case_insensitive || false;
|
|
@@ -332,7 +333,7 @@ export const applyDiffTool = {
|
|
|
332
333
|
};
|
|
333
334
|
},
|
|
334
335
|
async execute(args, context) {
|
|
335
|
-
const filePath =
|
|
336
|
+
const filePath = resolvePath(context.workingDirectory, args.path);
|
|
336
337
|
if (!existsSync(filePath))
|
|
337
338
|
return `Error: File not found: ${args.path}`;
|
|
338
339
|
try {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a path looks like a Windows absolute path (drive letter)
|
|
3
|
+
* Matches: C:\, C:/, D:\foo, etc.
|
|
4
|
+
*/
|
|
5
|
+
export declare function isWindowsAbsolutePath(p: string): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Normalize a path for cross-platform compatibility:
|
|
8
|
+
* 1. Convert backslashes to forward slashes
|
|
9
|
+
* 2. Detect Windows absolute paths (C:\...) and treat them as absolute
|
|
10
|
+
* 3. Resolve ~ to home directory
|
|
11
|
+
* 4. Handle relative paths correctly
|
|
12
|
+
*/
|
|
13
|
+
export declare function normalizeCrossPlatformPath(inputPath: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a path relative to a working directory with cross-platform support.
|
|
16
|
+
* This is the main function to use in file tools.
|
|
17
|
+
*
|
|
18
|
+
* - Absolute paths (POSIX or Windows) are used as-is after normalization
|
|
19
|
+
* - Relative paths are resolved against workingDirectory
|
|
20
|
+
* - Home directory (~) is expanded
|
|
21
|
+
* - Backslashes are normalized to forward slashes
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolvePath(workingDirectory: string, inputPath: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* Check if a path is absolute on any platform (POSIX or Windows)
|
|
26
|
+
*/
|
|
27
|
+
export declare function isAnyAbsolutePath(p: string): boolean;
|
|
28
|
+
//# sourceMappingURL=crosspath.d.ts.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// NeuroCLI - Cross-Platform Path Utility
|
|
3
|
+
// Handles Windows, macOS, and Linux path differences
|
|
4
|
+
// ============================================================
|
|
5
|
+
import { join, resolve, isAbsolute, normalize } from 'path';
|
|
6
|
+
/**
|
|
7
|
+
* Check if a path looks like a Windows absolute path (drive letter)
|
|
8
|
+
* Matches: C:\, C:/, D:\foo, etc.
|
|
9
|
+
*/
|
|
10
|
+
export function isWindowsAbsolutePath(p) {
|
|
11
|
+
return /^[A-Za-z]:[/\\]/.test(p);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Normalize a path for cross-platform compatibility:
|
|
15
|
+
* 1. Convert backslashes to forward slashes
|
|
16
|
+
* 2. Detect Windows absolute paths (C:\...) and treat them as absolute
|
|
17
|
+
* 3. Resolve ~ to home directory
|
|
18
|
+
* 4. Handle relative paths correctly
|
|
19
|
+
*/
|
|
20
|
+
export function normalizeCrossPlatformPath(inputPath) {
|
|
21
|
+
let p = inputPath;
|
|
22
|
+
// Step 1: Expand home directory
|
|
23
|
+
if (p.startsWith('~/') || p === '~') {
|
|
24
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
25
|
+
p = p === '~' ? homeDir : join(homeDir, p.slice(2));
|
|
26
|
+
return normalize(p);
|
|
27
|
+
}
|
|
28
|
+
// Step 2: Detect Windows absolute path with drive letter
|
|
29
|
+
if (isWindowsAbsolutePath(p)) {
|
|
30
|
+
// Normalize backslashes to forward slashes, then let Node normalize
|
|
31
|
+
p = p.replace(/\\/g, '/');
|
|
32
|
+
// On Windows, Node's path.resolve handles drive letters natively
|
|
33
|
+
// On non-Windows, we still need to handle this for the case where
|
|
34
|
+
// the LLM sends a Windows path from a user's context
|
|
35
|
+
if (process.platform === 'win32') {
|
|
36
|
+
return normalize(p);
|
|
37
|
+
}
|
|
38
|
+
// On non-Windows systems, a Windows path like C:/Users/foo
|
|
39
|
+
// won't resolve. This likely means the LLM is confused about
|
|
40
|
+
// the OS. We still normalize it so it can be used as-is.
|
|
41
|
+
return normalize(p);
|
|
42
|
+
}
|
|
43
|
+
// Step 3: Convert backslashes to forward slashes for mixed paths
|
|
44
|
+
// (handles cases where LLM or user provides backslash paths on any OS)
|
|
45
|
+
p = p.replace(/\\/g, '/');
|
|
46
|
+
// Step 4: If already absolute (POSIX), normalize directly
|
|
47
|
+
if (isAbsolute(p)) {
|
|
48
|
+
return normalize(p);
|
|
49
|
+
}
|
|
50
|
+
// Step 5: Relative path - return as-is (caller will resolve against working dir)
|
|
51
|
+
return normalize(p);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolve a path relative to a working directory with cross-platform support.
|
|
55
|
+
* This is the main function to use in file tools.
|
|
56
|
+
*
|
|
57
|
+
* - Absolute paths (POSIX or Windows) are used as-is after normalization
|
|
58
|
+
* - Relative paths are resolved against workingDirectory
|
|
59
|
+
* - Home directory (~) is expanded
|
|
60
|
+
* - Backslashes are normalized to forward slashes
|
|
61
|
+
*/
|
|
62
|
+
export function resolvePath(workingDirectory, inputPath) {
|
|
63
|
+
const normalized = normalizeCrossPlatformPath(inputPath);
|
|
64
|
+
// If it's already an absolute path after normalization, use it directly
|
|
65
|
+
if (isAbsolute(normalized)) {
|
|
66
|
+
return normalized;
|
|
67
|
+
}
|
|
68
|
+
// Windows absolute paths that became normalized (C:/...)
|
|
69
|
+
if (isWindowsAbsolutePath(normalized)) {
|
|
70
|
+
return normalized;
|
|
71
|
+
}
|
|
72
|
+
// Relative path: resolve against working directory
|
|
73
|
+
return resolve(workingDirectory, normalized);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check if a path is absolute on any platform (POSIX or Windows)
|
|
77
|
+
*/
|
|
78
|
+
export function isAnyAbsolutePath(p) {
|
|
79
|
+
return isAbsolute(p) || isWindowsAbsolutePath(p);
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=crosspath.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neuro-cli",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.2",
|
|
4
4
|
"description": "Advanced AI-powered terminal coding assistant with multi-agent orchestration, sub-agent spawning, ACP protocol, OS-level sandboxing, spec-driven development, smart monitoring, multi-model routing, MCP Apps, auto-updater, and 23+ free models",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|