cli4ai 1.2.1 → 1.2.3

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.
@@ -4,8 +4,11 @@
4
4
  import { existsSync, symlinkSync, mkdirSync, cpSync, rmSync, readdirSync, unlinkSync, lstatSync } from 'fs';
5
5
  import { resolve, dirname, join, normalize } from 'path';
6
6
  import { createInterface } from 'readline';
7
- import { tmpdir } from 'os';
7
+ import { tmpdir, platform } from 'os';
8
8
  import { spawnSync } from 'child_process';
9
+ // Windows compatibility: use shell: true on Windows to handle .cmd wrappers
10
+ // This is safe because package names are validated with strict regex patterns
11
+ const isWindows = platform() === 'win32';
9
12
  import { output, outputError, log } from '../lib/cli.js';
10
13
  import { loadManifest, tryLoadManifest } from '../core/manifest.js';
11
14
  import { ensureCli4aiHome, ensureLocalDir, PACKAGES_DIR, LOCAL_PACKAGES_DIR, loadConfig } from '../core/config.js';
@@ -101,7 +104,8 @@ async function downloadFromNpm(packageName, targetDir) {
101
104
  const packResult = spawnSync('npm', ['pack', packageName, `--pack-destination=${tmpDir}`], {
102
105
  stdio: 'pipe',
103
106
  cwd: tmpDir,
104
- encoding: 'utf-8'
107
+ encoding: 'utf-8',
108
+ shell: isWindows // Required on Windows to find npm.cmd
105
109
  });
106
110
  if (packResult.error) {
107
111
  throw new Error(`npm not found or failed to execute: ${packResult.error.message}`);
@@ -120,7 +124,8 @@ async function downloadFromNpm(packageName, targetDir) {
120
124
  const tarPath = join(tmpDir, tarball);
121
125
  const extractResult = spawnSync('tar', ['-xzf', tarPath, '-C', tmpDir], {
122
126
  stdio: 'pipe',
123
- encoding: 'utf-8'
127
+ encoding: 'utf-8',
128
+ shell: isWindows // Required on Windows
124
129
  });
125
130
  if (extractResult.status !== 0) {
126
131
  throw new Error(`Failed to extract package: ${extractResult.stderr || 'tar extraction failed'}`);
@@ -161,7 +166,8 @@ async function downloadFromNpm(packageName, targetDir) {
161
166
  */
162
167
  function checkCliTool(name) {
163
168
  try {
164
- const result = spawnSync('which', [name], { stdio: 'pipe' });
169
+ const cmd = isWindows ? 'where' : 'which';
170
+ const result = spawnSync(cmd, [name], { stdio: 'pipe' });
165
171
  return result.status === 0;
166
172
  }
167
173
  catch {
@@ -178,7 +184,8 @@ async function installNpmDependencies(pkgPath, dependencies) {
178
184
  log(`Installing npm dependencies: ${deps.join(', ')}`);
179
185
  const result = spawnSync('npm', ['install', ...deps], {
180
186
  cwd: pkgPath,
181
- stdio: 'inherit'
187
+ stdio: 'inherit',
188
+ shell: isWindows // Required on Windows to find npm.cmd
182
189
  });
183
190
  if (result.status !== 0) {
184
191
  throw new Error(`Failed to install dependencies (exit code: ${result.status})`);
@@ -2,8 +2,11 @@
2
2
  * cli4ai browse - Interactive package browser
3
3
  */
4
4
  import { spawnSync } from 'child_process';
5
+ import { platform } from 'os';
5
6
  import { log, outputError } from '../lib/cli.js';
6
7
  import { getNpmGlobalPackages, getGlobalPackages, getLocalPackages } from '../core/config.js';
8
+ // Windows compatibility
9
+ const isWindows = platform() === 'win32';
7
10
  // ANSI codes
8
11
  const RESET = '\x1B[0m';
9
12
  const BOLD = '\x1B[1m';
@@ -328,7 +331,7 @@ async function installPackages(packages, scope) {
328
331
  if (scopeFlag)
329
332
  addArgs.push(scopeFlag);
330
333
  addArgs.push('-y');
331
- const result = spawnSync('cli4ai', addArgs, { stdio: 'pipe' });
334
+ const result = spawnSync('cli4ai', addArgs, { stdio: 'pipe', shell: isWindows });
332
335
  clearInterval(spinner);
333
336
  if (result.status === 0) {
334
337
  process.stderr.write(`\r ${GREEN}✓${RESET} ${BOLD}${shortName}${RESET} installed \n`);
@@ -3,7 +3,10 @@
3
3
  */
4
4
  import { resolve } from 'path';
5
5
  import { spawnSync } from 'child_process';
6
+ import { platform } from 'os';
6
7
  import { output, outputError, log } from '../lib/cli.js';
8
+ // Windows compatibility
9
+ const isWindows = platform() === 'win32';
7
10
  import { findPackage, loadConfig } from '../core/config.js';
8
11
  import { loadManifest, tryLoadManifest } from '../core/manifest.js';
9
12
  import { remotePackageInfo, RemoteConnectionError, RemoteApiError } from '../core/remote-client.js';
@@ -65,7 +68,8 @@ export async function infoCommand(packageName, options) {
65
68
  const result = spawnSync('npm', ['view', scopedName, '--json'], {
66
69
  encoding: 'utf-8',
67
70
  timeout: 10000,
68
- stdio: ['pipe', 'pipe', 'pipe']
71
+ stdio: ['pipe', 'pipe', 'pipe'],
72
+ shell: isWindows // Required on Windows to find npm.cmd
69
73
  });
70
74
  if (result.status === 0 && result.stdout) {
71
75
  const pkg = JSON.parse(result.stdout);
@@ -4,7 +4,10 @@
4
4
  import { readdirSync, existsSync } from 'fs';
5
5
  import { resolve } from 'path';
6
6
  import { spawnSync } from 'child_process';
7
+ import { platform } from 'os';
7
8
  import { output, outputError, log } from '../lib/cli.js';
9
+ // Windows compatibility
10
+ const isWindows = platform() === 'win32';
8
11
  import { loadConfig, getGlobalPackages, getLocalPackages } from '../core/config.js';
9
12
  import { tryLoadManifest } from '../core/manifest.js';
10
13
  import { remoteListPackages, RemoteConnectionError, RemoteApiError } from '../core/remote-client.js';
@@ -97,7 +100,8 @@ export async function searchCommand(query, options) {
97
100
  const searchResult = spawnSync('npm', ['search', `@cli4ai/${query}`, '--json'], {
98
101
  encoding: 'utf-8',
99
102
  timeout: 10000,
100
- stdio: ['pipe', 'pipe', 'pipe']
103
+ stdio: ['pipe', 'pipe', 'pipe'],
104
+ shell: isWindows // Required on Windows to find npm.cmd
101
105
  });
102
106
  let npmResults = searchResult.stdout || '';
103
107
  // Fallback to searching @cli4ai if specific query fails
@@ -105,7 +109,8 @@ export async function searchCommand(query, options) {
105
109
  const fallbackResult = spawnSync('npm', ['search', '@cli4ai', '--json'], {
106
110
  encoding: 'utf-8',
107
111
  timeout: 10000,
108
- stdio: ['pipe', 'pipe', 'pipe']
112
+ stdio: ['pipe', 'pipe', 'pipe'],
113
+ shell: isWindows // Required on Windows to find npm.cmd
109
114
  });
110
115
  npmResults = fallbackResult.stdout || '[]';
111
116
  }
@@ -2,7 +2,7 @@
2
2
  * Global cli4ai configuration (~/.cli4ai/)
3
3
  */
4
4
  import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, lstatSync, realpathSync, openSync, closeSync, unlinkSync, renameSync } from 'fs';
5
- import { resolve, join, normalize } from 'path';
5
+ import { resolve, join, normalize, sep } from 'path';
6
6
  import { homedir } from 'os';
7
7
  import { outputError, log } from '../lib/cli.js';
8
8
  // ═══════════════════════════════════════════════════════════════════════════
@@ -36,7 +36,7 @@ function validateSymlinkTarget(symlinkPath) {
36
36
  // Check if the real path is within a safe prefix
37
37
  const isSafe = SAFE_SYMLINK_PREFIXES.some(prefix => {
38
38
  const normalizedPrefix = normalize(prefix);
39
- return normalizedRealPath.startsWith(normalizedPrefix + '/') ||
39
+ return normalizedRealPath.startsWith(normalizedPrefix + sep) ||
40
40
  normalizedRealPath === normalizedPrefix;
41
41
  });
42
42
  if (!isSafe) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli4ai",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "The package manager for AI CLI tools - cli4ai.com",
5
5
  "type": "module",
6
6
  "bin": {