neuro-cli 4.1.0 → 4.1.1

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.
@@ -1,5 +1,5 @@
1
1
  // ============================================================
2
- // NeuroCLI - NeuroEngine v4.1.0
2
+ // NeuroCLI - NeuroEngine v4.1.1
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.0',
486
+ currentVersion: '4.1.1',
487
487
  autoCheck: true,
488
488
  autoUpdate: false,
489
489
  });
@@ -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
- if (filePath.startsWith('~/'))
318
- return normalize(join(require('os').homedir(), filePath.slice(2)));
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.0 with auto-updater
4
+ // Main Entry Point - v4.1.1 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.0';
19
+ const VERSION = '4.1.1';
20
20
  // ---- CLI Setup ----
21
21
  const program = new Command();
22
22
  program
@@ -909,7 +909,7 @@ async function startInteractive(options) {
909
909
  }
910
910
  break;
911
911
  case 'doctor':
912
- console.log(chalk.bold('\nNeuroCLI v4.1.0 Health Check:\n'));
912
+ console.log(chalk.bold('\nNeuroCLI v4.1.1 Health Check:\n'));
913
913
  console.log(` API Key: ${config.apiKey ? chalk.green('configured') : chalk.red('MISSING')}`);
914
914
  console.log(` Default Model: ${chalk.cyan(config.defaultModel)} ${MODELS[config.defaultModel] ? chalk.green('valid') : chalk.red('INVALID')}`);
915
915
  console.log(` MCP Servers: ${chalk.cyan(String(engine.mcpClient.listServers().length))}`);
@@ -1128,7 +1128,7 @@ async function startInteractive(options) {
1128
1128
  }
1129
1129
  function printHelp(engine) {
1130
1130
  const t = engine.ui.theme;
1131
- console.log(`\n ${t.bold('NeuroCLI v4.1.0 Commands:')}\n`);
1131
+ console.log(`\n ${t.bold('NeuroCLI v4.1.1 Commands:')}\n`);
1132
1132
  console.log(` ${t.tool('/help')} Show this help message`);
1133
1133
  console.log(` ${t.tool('/model [id]')} Switch or list models`);
1134
1134
  console.log(` ${t.tool('/agent [name]')} Switch or list agents`);
@@ -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 = join(context.workingDirectory, args.path);
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 = join(context.workingDirectory, args.path);
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 = join(context.workingDirectory, args.path);
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 = join(context.workingDirectory, args.path);
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 = join(context.workingDirectory, args.path || '.');
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 = join(context.workingDirectory, args.path || '.');
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 = join(context.workingDirectory, args.path);
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.0",
3
+ "version": "4.1.1",
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": {