From bb70d04b88b6ebd5209b344889b9b5c594570b8e Mon Sep 17 00:00:00 2001 From: DenyCZ Date: Wed, 14 Jan 2026 00:31:04 +0100 Subject: [PATCH] fix: Resolve windows npx spawn errors --- .husky/pre-commit | 7 ++++++- apps/server/src/providers/opencode-provider.ts | 8 ++++++-- libs/platform/src/subprocess.ts | 8 ++++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 276c2fa0..f61fd35b 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -31,7 +31,12 @@ fi # Ensure common system paths are in PATH (for systems without nvm) # This helps find node/npm installed via Homebrew, system packages, etc. -export PATH="$PATH:/usr/local/bin:/opt/homebrew/bin:/usr/bin" +if [ -n "$WINDIR" ]; then + export PATH="$PATH:/c/Program Files/nodejs:/c/Program Files (x86)/nodejs" + export PATH="$PATH:$APPDATA/npm:$LOCALAPPDATA/Programs/nodejs" +else + export PATH="$PATH:/usr/local/bin:/opt/homebrew/bin:/usr/bin" +fi # Run lint-staged - works with or without nvm # Prefer npx, fallback to npm exec, both work with system-installed Node.js diff --git a/apps/server/src/providers/opencode-provider.ts b/apps/server/src/providers/opencode-provider.ts index a5b3bae2..6babb978 100644 --- a/apps/server/src/providers/opencode-provider.ts +++ b/apps/server/src/providers/opencode-provider.ts @@ -730,7 +730,7 @@ export class OpencodeProvider extends CliProvider { if (this.detectedStrategy === 'npx') { // NPX strategy: execute npx with opencode-ai package - command = 'npx'; + command = process.platform === 'win32' ? 'npx.cmd' : 'npx'; args = ['opencode-ai@latest', 'models']; opencodeLogger.debug(`Executing: ${command} ${args.join(' ')}`); } else if (this.useWsl && this.wslCliPath) { @@ -751,6 +751,8 @@ export class OpencodeProvider extends CliProvider { encoding: 'utf-8', timeout: 30000, windowsHide: true, + // Use shell on Windows for .cmd files + shell: process.platform === 'win32' && command.endsWith('.cmd'), }); opencodeLogger.debug( @@ -963,7 +965,7 @@ export class OpencodeProvider extends CliProvider { if (this.detectedStrategy === 'npx') { // NPX strategy - command = 'npx'; + command = process.platform === 'win32' ? 'npx.cmd' : 'npx'; args = ['opencode-ai@latest', 'auth', 'list']; opencodeLogger.debug(`Executing: ${command} ${args.join(' ')}`); } else if (this.useWsl && this.wslCliPath) { @@ -984,6 +986,8 @@ export class OpencodeProvider extends CliProvider { encoding: 'utf-8', timeout: 15000, windowsHide: true, + // Use shell on Windows for .cmd files + shell: process.platform === 'win32' && command.endsWith('.cmd'), }); opencodeLogger.debug( diff --git a/libs/platform/src/subprocess.ts b/libs/platform/src/subprocess.ts index 4fad5412..b17a9538 100644 --- a/libs/platform/src/subprocess.ts +++ b/libs/platform/src/subprocess.ts @@ -45,7 +45,9 @@ export async function* spawnJSONLProcess(options: SubprocessOptions): AsyncGener } // On Windows, .cmd files must be run through shell (cmd.exe) - const needsShell = process.platform === 'win32' && command.toLowerCase().endsWith('.cmd'); + const needsShell = + process.platform === 'win32' && + (command.toLowerCase().endsWith('.cmd') || command === 'npx' || command === 'npm'); const childProcess: ChildProcess = spawn(command, args, { cwd, @@ -199,7 +201,9 @@ export async function spawnProcess(options: SubprocessOptions): Promise { // On Windows, .cmd files must be run through shell (cmd.exe) - const needsShell = process.platform === 'win32' && command.toLowerCase().endsWith('.cmd'); + const needsShell = + process.platform === 'win32' && + (command.toLowerCase().endsWith('.cmd') || command === 'npx' || command === 'npm'); const childProcess = spawn(command, args, { cwd,