diff --git a/apps/frontend/src/main/ipc-handlers/github/release-handlers.ts b/apps/frontend/src/main/ipc-handlers/github/release-handlers.ts index e6183e82..0330395f 100644 --- a/apps/frontend/src/main/ipc-handlers/github/release-handlers.ts +++ b/apps/frontend/src/main/ipc-handlers/github/release-handlers.ts @@ -19,8 +19,7 @@ import { getWhichCommand } from '../../platform'; */ function checkGhCli(): { installed: boolean; error?: string } { try { - const checkCmd = `${getWhichCommand()} gh`; - execSync(checkCmd, { encoding: 'utf-8', stdio: 'pipe' }); + execFileSync(getWhichCommand(), ['gh'], { encoding: 'utf-8', stdio: 'pipe' }); return { installed: true }; } catch { return { diff --git a/apps/frontend/src/main/platform/paths.ts b/apps/frontend/src/main/platform/paths.ts index c1758bcb..3019c6c6 100644 --- a/apps/frontend/src/main/platform/paths.ts +++ b/apps/frontend/src/main/platform/paths.ts @@ -9,6 +9,7 @@ import * as path from 'path'; import * as os from 'os'; import { existsSync, readdirSync } from 'fs'; import { isWindows, isMacOS, getHomebrewPath, joinPaths, getExecutableExtension } from './index'; +import { getWhereExePath } from '../utils/windows-paths'; /** * Resolve Claude CLI executable path @@ -174,7 +175,7 @@ export function getWindowsShellPaths(): Record { return {}; } - const systemRoot = process.env.SystemRoot || 'C:\\Windows'; + const systemRoot = process.env.SystemRoot || process.env.SYSTEMROOT || 'C:\\Windows'; // Note: path.join('C:', 'foo') produces 'C:foo' (relative to C: drive), not 'C:\foo' // We must use 'C:\\' or raw paths like 'C:\\Program Files' to get absolute paths @@ -297,11 +298,14 @@ export function getOllamaInstallCommand(): string { /** * Get the command to find executables in PATH * - * Windows: where.exe + * Windows: Full path to where.exe (C:\Windows\System32\where.exe) + * Using full path ensures it works even when System32 isn't in PATH, + * which can happen in restricted environments or when Electron doesn't + * inherit the full system PATH. * Unix: which */ export function getWhichCommand(): string { - return isWindows() ? 'where.exe' : 'which'; + return isWindows() ? getWhereExePath() : 'which'; } /** diff --git a/apps/frontend/src/main/utils/windows-paths.ts b/apps/frontend/src/main/utils/windows-paths.ts index 1a2c8e34..5f56f00e 100644 --- a/apps/frontend/src/main/utils/windows-paths.ts +++ b/apps/frontend/src/main/utils/windows-paths.ts @@ -130,6 +130,19 @@ export function getWindowsExecutablePaths( return validPaths; } +/** + * Get the full path to where.exe. + * Using the full path ensures where.exe works even when System32 isn't in PATH, + * which can happen in restricted environments or when Electron doesn't inherit + * the full system PATH. + * + * @returns Full path to where.exe (e.g., C:\Windows\System32\where.exe) + */ +export function getWhereExePath(): string { + const systemRoot = process.env.SystemRoot || process.env.SYSTEMROOT || 'C:\\Windows'; + return path.join(systemRoot, 'System32', 'where.exe'); +} + /** * Find a Windows executable using the `where` command. * This is the most reliable method as it searches: @@ -158,9 +171,10 @@ export function findWindowsExecutableViaWhere( } try { - // Use 'where' command to find the executable - // where.exe is a built-in Windows command that finds executables - const result = execFileSync('where.exe', [executable], { + // Use full path to where.exe to ensure it works even when System32 isn't in PATH + // This fixes issues in restricted environments or when Electron doesn't inherit system PATH + const whereExe = getWhereExePath(); + const result = execFileSync(whereExe, [executable], { encoding: 'utf-8', timeout: 5000, windowsHide: true, @@ -261,9 +275,10 @@ export async function findWindowsExecutableViaWhereAsync( } try { - // Use 'where' command to find the executable - // where.exe is a built-in Windows command that finds executables - const { stdout } = await execFileAsync('where.exe', [executable], { + // Use full path to where.exe to ensure it works even when System32 isn't in PATH + // This fixes issues in restricted environments or when Electron doesn't inherit system PATH + const whereExe = getWhereExePath(); + const { stdout } = await execFileAsync(whereExe, [executable], { encoding: 'utf-8', timeout: 5000, windowsHide: true,