Skip to content

fix(windows): complete System32 executable path fixes for where.exe and taskkill.exe#1715

Open
VDT-91 wants to merge 7 commits intoAndyMik90:developfrom
VDT-91:fix/windows-system32-bare-executables
Open

fix(windows): complete System32 executable path fixes for where.exe and taskkill.exe#1715
VDT-91 wants to merge 7 commits intoAndyMik90:developfrom
VDT-91:fix/windows-system32-bare-executables

Conversation

@VDT-91
Copy link
Collaborator

@VDT-91 VDT-91 commented Feb 4, 2026

Summary

This PR completes the Windows System32 executable path fixes started in #1659. The previous fix addressed where.exe in a few frontend files, but missed several critical locations in both the backend and frontend where bare executable names (where, taskkill) were still being used.

Background

The Problem:
When Auto Claude is launched from GUI contexts (Windows Start Menu, desktop shortcut, or file associations), the application may not inherit the full system PATH. This causes bare executable calls like where git or taskkill /pid ... to fail with "command not found" errors because C:\Windows\System32 isn't in the PATH.

Previous Fix (#1659):
PR #1659 introduced getWhereExePath() in windows-paths.ts and updated a few frontend locations. However, it missed:

  1. Backend Python code - All 4 executable finder modules (auth.py, git_executable.py, gh_executable.py, glab_executable.py) were still using bare where or where.exe commands
  2. Frontend taskkill usage - Process termination code in platform/index.ts, subprocess-runner.ts, and pty-daemon-client.ts used bare taskkill
  3. Frontend where usage - claude-code-handlers.ts was still using bare where
  4. PATH augmentation - env-utils.ts only ensured Unix system paths, not Windows System32

Changes

Backend

  • Add get_where_exe_path() helper in core/platform/__init__.py
  • Update auth.py, git_executable.py, gh_executable.py, glab_executable.py to use full path
  • Remove shell=True from subprocess calls (security improvement)

Frontend

  • Add getTaskkillExePath() helper in windows-paths.ts
  • Update platform/index.ts, subprocess-runner.ts, pty-daemon-client.ts to use full path for taskkill.exe
  • Update claude-code-handlers.ts to use getWhereExePath()
  • Add System32 to ESSENTIAL_SYSTEM_PATHS in env-utils.ts
  • Update process-kill.test.ts to mock the new helper

Why Full Paths?

  1. Works without PATH - Using C:\Windows\System32\where.exe works regardless of PATH configuration
  2. SystemRoot is trustworthy - Protected Windows system environment variable set during boot
  3. Security improvement - Removing shell=True prevents command injection; full paths prevent PATH hijacking
  4. Defense in depth - Both use full paths AND ensure System32 is in PATH

Test plan

  • Frontend: Biome CI passes
  • Frontend: TypeScript type check passes
  • Frontend: Vitest tests pass
  • Frontend: Production build succeeds
  • Backend: Ruff check passes
  • Backend: Ruff format passes
  • Cross-platform: All Windows-specific code properly guarded by platform checks

Related

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved reliability of Windows system executable detection by using resolved System32 paths for where/taskkill instead of shell-based lookups, improving discovery of git/gh/glab and process-kill behavior.
    • Enhanced cross-platform PATH augmentation to include platform-specific essential system directories.
    • Better error reporting for command health checks with actionable messages for missing or permission-denied utilities.
  • New Features

    • Added helpers to return full System32 paths for common Windows utilities.
  • Tests

    • Updated Windows tests to expect resolved full executable paths for process-kill verification.

…nd taskkill.exe

Complete the Windows System32 executable path fixes started in AndyMik90#1659.
The previous fix addressed where.exe in a few frontend files but missed
several critical locations in both backend and frontend.

Backend changes:
- Add get_where_exe_path() helper in core/platform/__init__.py
- Update auth.py, git_executable.py, gh_executable.py, glab_executable.py
  to use full path instead of bare 'where' command
- Remove shell=True from subprocess calls (security improvement)

Frontend changes:
- Add getTaskkillExePath() helper in windows-paths.ts
- Update platform/index.ts, subprocess-runner.ts, pty-daemon-client.ts
  to use full path for taskkill.exe
- Update claude-code-handlers.ts to use getWhereExePath()
- Add System32 to ESSENTIAL_SYSTEM_PATHS in env-utils.ts
- Update process-kill.test.ts to mock the new helper

Why full paths:
- Works even when System32 isn't in PATH (GUI launch scenarios)
- SystemRoot env var is a protected Windows system variable
- Prevents PATH hijacking attacks
- Removing shell=True prevents command injection

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

Replaces literal/shell-invoked Windows commands with resolved System32 paths and platform-aware PATH handling: adds get_where_exe_path() and getTaskkillExePath() helpers, updates backend/frontend callers to invoke those executables directly, and makes frontend PATH augmentation platform-specific (unix/win32).

Changes

Cohort / File(s) Summary
Backend platform helper
apps/backend/core/platform/__init__.py
Add get_where_exe_path() returning full System32 path to where.exe using SystemRoot/SYSTEMROOT fallback.
Backend callers
apps/backend/core/auth.py, apps/backend/core/gh_executable.py, apps/backend/core/git_executable.py, apps/backend/core/glab_executable.py
Replace shell/string "where" invocations with list-based calls using get_where_exe_path(); remove shell=True and update comments.
Frontend Windows helpers
apps/frontend/src/main/utils/windows-paths.ts
Add getTaskkillExePath() that returns full System32 path to taskkill.exe (uses SystemRoot/SYSTEMROOT fallback).
Frontend env/path handling
apps/frontend/src/main/env-utils.ts
Convert ESSENTIAL_SYSTEM_PATHS into a platform-specific mapping (unix/win32); include System32 in win32 list and make PATH augmentation platform-aware (sync and async).
Frontend callers / process kill
apps/frontend/src/main/ipc-handlers/claude-code-handlers.ts, apps/frontend/src/main/ipc-handlers/github/utils/subprocess-runner.ts, apps/frontend/src/main/platform/index.ts, apps/frontend/src/main/terminal/pty-daemon-client.ts
Replace literal where/taskkill usages with getWhereExePath() / getTaskkillExePath() on Windows; preserve args and error handling.
Frontend command health & MCP handlers
apps/frontend/src/main/ipc-handlers/mcp-handlers.ts
Switch where invocations to use getWhereExePath() and improve error handling to surface ENOENT/EACCES-specific guidance.
Tests
apps/frontend/src/main/platform/__tests__/process-kill.test.ts
Mock/expect resolved taskkill full path (e.g., C:\\Windows\\System32\\taskkill.exe) instead of bare 'taskkill'.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

bug, area/fullstack, os/windows, size/M, priority/medium

Suggested reviewers

  • AndyMik90

Poem

🐰
I hopped through System32 at break of day,
Replaced vague "where" with a sure-footed way.
Taskkill and where now wear full home signs,
Hopping bytes and carrots between the lines. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: completing Windows System32 executable path fixes for where.exe and taskkill.exe, which is the core objective of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @VDT-91, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the reliability and security of the application on Windows by ensuring that critical system executables like where.exe and taskkill.exe are always invoked using their absolute paths. This change prevents failures in environments where the system's PATH variable might not be fully inherited, such as when the application is launched from a GUI. By explicitly specifying full paths and removing unnecessary shell invocations, the application becomes more robust and less susceptible to command injection vulnerabilities.

Highlights

  • Windows Executable Path Fixes: Completed the fix for Windows System32 executable paths, specifically for where.exe and taskkill.exe, addressing issues where bare executable names would fail in GUI contexts due to incomplete PATH inheritance.
  • Backend Python Updates: Introduced a get_where_exe_path() helper in the Python backend and updated all relevant modules (auth.py, git_executable.py, gh_executable.py, glab_executable.py) to use the full path for where.exe. This also allowed for the removal of shell=True from subprocess calls, improving security.
  • Frontend TypeScript Updates: Added a getTaskkillExePath() helper in TypeScript and updated frontend files (platform/index.ts, subprocess-runner.ts, pty-daemon-client.ts, claude-code-handlers.ts) to use full paths for where.exe and taskkill.exe.
  • PATH Augmentation: Modified env-utils.ts to ensure that C:\Windows\System32 is always included in the PATH for Windows environments, mirroring the existing behavior for Unix systems.
  • Dependency Updates: Updated package-lock.json to version 2.7.6-beta.2 and removed peer: true declarations from several dependencies, which might resolve potential dependency conflicts or warnings.
Changelog
  • apps/backend/core/auth.py
    • Imported get_where_exe_path from core.platform.
    • Updated subprocess.run calls to use get_where_exe_path() for where.exe.
  • apps/backend/core/gh_executable.py
    • Imported get_where_exe_path from core.platform.
    • Updated subprocess.run calls to use get_where_exe_path() for where gh.
    • Removed shell=True from subprocess.run calls.
  • apps/backend/core/git_executable.py
    • Imported get_where_exe_path from core.platform.
    • Updated subprocess.run calls to use get_where_exe_path() for where git.
    • Removed shell=True from subprocess.run calls.
  • apps/backend/core/glab_executable.py
    • Imported get_where_exe_path from core.platform.
    • Updated subprocess.run calls to use get_where_exe_path() for where glab.
    • Removed shell=True from subprocess.run calls.
  • apps/backend/core/platform/init.py
    • Added a new function get_where_exe_path() to return the full path to where.exe on Windows.
  • apps/frontend/src/main/env-utils.ts
    • Refactored ESSENTIAL_SYSTEM_PATHS from a string array to a Record<string, string[]> to support platform-specific paths.
    • Added C:\Windows\System32 to ESSENTIAL_SYSTEM_PATHS for Windows.
    • Updated getAugmentedEnv and getAugmentedEnvAsync to use the correct platform-specific essential paths.
  • apps/frontend/src/main/ipc-handlers/claude-code-handlers.ts
    • Imported getWhereExePath from ../utils/windows-paths.
    • Updated execFileAsync call to use getWhereExePath() when checking for 'claude' executable.
  • apps/frontend/src/main/ipc-handlers/github/utils/subprocess-runner.ts
    • Imported getTaskkillExePath from ../../../utils/windows-paths.
    • Updated execFile calls to use getTaskkillExePath() for taskkill when terminating processes on Windows.
  • apps/frontend/src/main/platform/tests/process-kill.test.ts
    • Added a mock for getTaskkillExePath to return a fixed path for testing.
    • Updated test assertions to expect taskkill to be called with its full path.
  • apps/frontend/src/main/platform/index.ts
    • Imported getTaskkillExePath from ../utils/windows-paths.
    • Updated spawn call to use getTaskkillExePath() for taskkill when gracefully killing processes on Windows.
  • apps/frontend/src/main/terminal/pty-daemon-client.ts
    • Imported getTaskkillExePath from ../utils/windows-paths.
    • Updated spawn call to use getTaskkillExePath() for taskkill when force killing the daemon process tree on Windows.
  • apps/frontend/src/main/utils/windows-paths.ts
    • Added a new function getTaskkillExePath() to return the full path to taskkill.exe on Windows.
  • package-lock.json
    • Updated project version from 2.7.6-beta.1 to 2.7.6-beta.2.
    • Removed peer: true declarations from various dependencies, including @babel/plugin-transform-react-jsx, @dnd-kit/core, @opentelemetry/api, @opentelemetry/context-async-hooks, @opentelemetry/core, @opentelemetry/instrumentation, @opentelemetry/resources, @opentelemetry/sdk-trace-base, @opentelemetry/semantic-conventions, @types/babel__core, @types/react, @types/react-dom, acorn, ajv, autoprefixer, electron-builder, electron-rebuild, postcss, react, react-dom, tailwindcss, terser, typescript, vite, and zod.
Activity
  • This pull request builds upon and completes previous work initiated in fix(windows): use full path to where.exe for reliable executable lookup #1659, which aimed to address Windows System32 executable path issues.
  • The author has provided a detailed summary of the problem, background, and specific changes made in both the backend and frontend.
  • A comprehensive test plan is outlined, covering frontend (Biome CI, TypeScript, Vitest, production build) and backend (Ruff check, Ruff format) aspects, along with cross-platform checks for Windows-specific code.
  • The changes include security improvements by removing shell=True from Python subprocess calls.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@sentry
Copy link

sentry bot commented Feb 4, 2026

Codecov Report

❌ Patch coverage is 66.66667% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
apps/backend/core/platform/__init__.py 33.33% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
apps/backend/core/glab_executable.py (1)

137-138: ⚠️ Potential issue | 🟡 Minor

Stale comment: shell=True is no longer used.

The comment mentions shell=True but _run_where_command() now uses a list-based invocation without shell=True. Update the comment to reflect the current implementation.

📝 Suggested fix
-        # 5. Try 'where' command with shell=True (more reliable on Windows)
+        # 5. Try 'where' command to find glab in PATH
         return _run_where_command()
apps/backend/core/gh_executable.py (1)

137-138: ⚠️ Potential issue | 🟡 Minor

Stale comment: shell=True is no longer used.

Same issue as in glab_executable.py - the comment mentions shell=True but the implementation uses list-based invocation without shell.

📝 Suggested fix
-        # 5. Try 'where' command with shell=True (more reliable on Windows)
+        # 5. Try 'where' command to find gh in PATH
         return _run_where_command()
apps/frontend/src/main/utils/windows-paths.ts (1)

141-143: 🧹 Nitpick | 🔵 Trivial

Consider adding windir as a fallback for consistency.

The codebase has another fallback pattern in apps/frontend/scripts/download-python.cjs (line 367) that includes process.env.windir as an additional fallback. For extra robustness, you could align these helpers:

♻️ Optional enhancement for both functions
 export function getWhereExePath(): string {
-  const systemRoot = process.env.SystemRoot || process.env.SYSTEMROOT || 'C:\\Windows';
+  const systemRoot = process.env.SystemRoot || process.env.SYSTEMROOT || process.env.windir || 'C:\\Windows';
   return path.join(systemRoot, 'System32', 'where.exe');
 }

 export function getTaskkillExePath(): string {
-  const systemRoot = process.env.SystemRoot || process.env.SYSTEMROOT || 'C:\\Windows';
+  const systemRoot = process.env.SystemRoot || process.env.SYSTEMROOT || process.env.windir || 'C:\\Windows';
   return path.join(systemRoot, 'System32', 'taskkill.exe');
 }

Also applies to: 154-156

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This is a great pull request that thoroughly addresses the issue of finding Windows System32 executables in GUI-launched contexts. The changes are consistent across both the frontend and backend, and the use of full paths for where.exe and taskkill.exe is a robust solution. The removal of shell=True from Python subprocess calls is also a welcome security improvement.

I've added a few suggestions to reduce code duplication by extracting the logic for finding the SystemRoot directory into shared helper functions. This will make the code more maintainable.

Overall, excellent work on this important fix for Windows users.

Comment on lines +433 to +446
def get_where_exe_path() -> str:
"""Get full path to where.exe on Windows.

Using the full path ensures where.exe works even when System32 isn't in PATH,
which can happen in restricted environments or when the app doesn't inherit
the full system PATH.

Returns:
Full path to where.exe (e.g., C:\\Windows\\System32\\where.exe)
"""
system_root = os.environ.get(
"SystemRoot", os.environ.get("SYSTEMROOT", "C:\\Windows")
)
return os.path.join(system_root, "System32", "where.exe")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve maintainability and reduce code duplication, the logic for getting the SystemRoot path could be extracted into a private helper function. This would also make it easy to reuse in get_comspec_path for consistency, although that's outside the scope of this PR.

Suggested change
def get_where_exe_path() -> str:
"""Get full path to where.exe on Windows.
Using the full path ensures where.exe works even when System32 isn't in PATH,
which can happen in restricted environments or when the app doesn't inherit
the full system PATH.
Returns:
Full path to where.exe (e.g., C:\\Windows\\System32\\where.exe)
"""
system_root = os.environ.get(
"SystemRoot", os.environ.get("SYSTEMROOT", "C:\\Windows")
)
return os.path.join(system_root, "System32", "where.exe")
def _get_system_root() -> str:
"""Get the SystemRoot path, handling case variations and providing a fallback."""
return os.environ.get("SystemRoot", os.environ.get("SYSTEMROOT", "C:\\Windows"))
def get_where_exe_path() -> str:
"""Get full path to where.exe on Windows.
Using the full path ensures where.exe works even when System32 isn't in PATH,
which can happen in restricted environments or when the app doesn't inherit
the full system PATH.
Returns:
Full path to where.exe (e.g., C:\\Windows\\System32\\where.exe)
"""
return os.path.join(_get_system_root(), "System32", "where.exe")

const ESSENTIAL_SYSTEM_PATHS: Record<string, string[]> = {
unix: ['/usr/bin', '/bin', '/usr/sbin', '/sbin'],
win32: [
`${process.env.SystemRoot || process.env.SYSTEMROOT || 'C:\\Windows'}\\System32`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic for getting the SystemRoot path is duplicated. As suggested in windows-paths.ts, you could create a shared getSystemRoot() helper function there, export it, and then import and use it here to keep the logic centralized. You would need to add an import like import { getSystemRoot } from '../utils/windows-paths';.

Suggested change
`${process.env.SystemRoot || process.env.SYSTEMROOT || 'C:\\Windows'}\\System32`,
path.join(getSystemRoot(), 'System32'),

Comment on lines +146 to +157
/**
* Get the full path to taskkill.exe.
* Using the full path ensures taskkill.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 taskkill.exe (e.g., C:\Windows\System32\taskkill.exe)
*/
export function getTaskkillExePath(): string {
const systemRoot = process.env.SystemRoot || process.env.SYSTEMROOT || 'C:\\Windows';
return path.join(systemRoot, 'System32', 'taskkill.exe');
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic to determine the SystemRoot path is duplicated in getWhereExePath() and this new getTaskkillExePath() function. To improve maintainability, you could extract this into a shared helper function. This helper could also be exported and used in env-utils.ts to centralize the logic.

/**
 * Get the Windows SystemRoot path, handling case variations and providing a fallback.
 * @returns The SystemRoot path (e.g., C:\Windows)
 */
export function getSystemRoot(): string {
  return process.env.SystemRoot || process.env.SYSTEMROOT || 'C:\\Windows';
}

/**
 * Get the full path to taskkill.exe.
 * Using the full path ensures taskkill.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 taskkill.exe (e.g., C:\Windows\System32\taskkill.exe)
 */
export function getTaskkillExePath(): string {
  return path.join(getSystemRoot(), 'System32', 'taskkill.exe');
}

@@ -261,7 +262,7 @@ export function runPythonSubprocess<T = unknown>(
process.kill(-child.pid, 'SIGKILL');
} else {
// On Windows, use taskkill to kill the process tree

This comment was marked as outdated.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/frontend/src/main/ipc-handlers/github/utils/subprocess-runner.ts (1)

174-174: ⚠️ Potential issue | 🟡 Minor

Inconsistent: stopFn still uses bare taskkill instead of getTaskkillExePath().

This invocation was not updated to use the full path, unlike the auth and billing failure handlers at lines 265 and 324. For consistency and to avoid PATH-related failures, this should also use getTaskkillExePath().

🔧 Proposed fix
             if (!isWindows()) {
               process.kill(-child.pid, 'SIGTERM');
             } else {
-              execFile('taskkill', ['/pid', String(child.pid), '/T', '/F'], () => {});
+              execFile(getTaskkillExePath(), ['/pid', String(child.pid), '/T', '/F'], () => {});
             }
🤖 Fix all issues with AI agents
In `@apps/frontend/src/main/env-utils.ts`:
- Around line 148-158: ESSENTIAL_SYSTEM_PATHS currently constructs the Windows
System32 path using a hardcoded backslash; change this to build the path with
joinPaths so it uses the platform path helper. Locate the ESSENTIAL_SYSTEM_PATHS
constant and replace the string interpolation that appends '\\System32' to
process.env.SystemRoot or process.env.SYSTEMROOT with a call to
joinPaths(process.env.SystemRoot || process.env.SYSTEMROOT || 'C:\\Windows',
'System32'), ensuring you import joinPaths from the platform utilities and
preserve the fallback to 'C:\\Windows'.

Comment on lines 148 to +158
/**
* Essential system directories that must always be in PATH
* Required for core system functionality (e.g., /usr/bin/security for Keychain access)
* Required for core system functionality (e.g., /usr/bin/security for Keychain access on macOS,
* System32 for where.exe/taskkill.exe on Windows)
*/
const ESSENTIAL_SYSTEM_PATHS: string[] = ['/usr/bin', '/bin', '/usr/sbin', '/sbin'];
const ESSENTIAL_SYSTEM_PATHS: Record<string, string[]> = {
unix: ['/usr/bin', '/bin', '/usr/sbin', '/sbin'],
win32: [
`${process.env.SystemRoot || process.env.SYSTEMROOT || 'C:\\Windows'}\\System32`,
],
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Use joinPaths for System32 to avoid hardcoded separators.

The \\System32 suffix hardcodes a Windows separator instead of using the platform path helper. Please build the path with joinPaths for consistency and cross-platform hygiene.

♻️ Suggested fix
-import { isWindows, isUnix, getPathDelimiter, getNpmCommand } from './platform';
+import { isWindows, isUnix, getPathDelimiter, getNpmCommand, joinPaths } from './platform';
@@
 const ESSENTIAL_SYSTEM_PATHS: Record<string, string[]> = {
   unix: ['/usr/bin', '/bin', '/usr/sbin', '/sbin'],
   win32: [
-    `${process.env.SystemRoot || process.env.SYSTEMROOT || 'C:\\Windows'}\\System32`,
+    joinPaths(process.env.SystemRoot || process.env.SYSTEMROOT || 'C:\\Windows', 'System32'),
   ],
 };

As per coding guidelines: Cross-platform executable lookup and path handling must use findExecutable(), getPathDelimiter(), joinPaths(), and requiresShell() functions from platform modules. Never hardcode paths or platform-specific separators.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Essential system directories that must always be in PATH
* Required for core system functionality (e.g., /usr/bin/security for Keychain access)
* Required for core system functionality (e.g., /usr/bin/security for Keychain access on macOS,
* System32 for where.exe/taskkill.exe on Windows)
*/
const ESSENTIAL_SYSTEM_PATHS: string[] = ['/usr/bin', '/bin', '/usr/sbin', '/sbin'];
const ESSENTIAL_SYSTEM_PATHS: Record<string, string[]> = {
unix: ['/usr/bin', '/bin', '/usr/sbin', '/sbin'],
win32: [
`${process.env.SystemRoot || process.env.SYSTEMROOT || 'C:\\Windows'}\\System32`,
],
};
const ESSENTIAL_SYSTEM_PATHS: Record<string, string[]> = {
unix: ['/usr/bin', '/bin', '/usr/sbin', '/sbin'],
win32: [
joinPaths(process.env.SystemRoot || process.env.SYSTEMROOT || 'C:\\Windows', 'System32'),
],
};
🤖 Prompt for AI Agents
In `@apps/frontend/src/main/env-utils.ts` around lines 148 - 158,
ESSENTIAL_SYSTEM_PATHS currently constructs the Windows System32 path using a
hardcoded backslash; change this to build the path with joinPaths so it uses the
platform path helper. Locate the ESSENTIAL_SYSTEM_PATHS constant and replace the
string interpolation that appends '\\System32' to process.env.SystemRoot or
process.env.SYSTEMROOT with a call to joinPaths(process.env.SystemRoot ||
process.env.SYSTEMROOT || 'C:\\Windows', 'System32'), ensuring you import
joinPaths from the platform utilities and preserve the fallback to
'C:\\Windows'.

@AndyMik90 AndyMik90 self-assigned this Feb 5, 2026
Copy link
Owner

@AndyMik90 AndyMik90 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Auto Claude PR Review

Merge Verdict: 🟠 NEEDS REVISION

🟠 Needs revision - 3 issue(s) require attention.

3 issue(s) must be addressed (0 required, 3 recommended), 2 suggestions

Risk Assessment

Factor Level Notes
Complexity Low Based on lines changed
Security Impact None Based on security findings
Scope Coherence Good Based on structural review

Findings Summary

  • Medium: 3 issue(s)
  • Low: 2 issue(s)

Generated by Auto Claude PR Review

Findings (5 selected of 5 total)

🟡 [84c04f78174f] [MEDIUM] Missed bare 'taskkill' — incomplete refactoring in same file

📁 apps/frontend/src/main/ipc-handlers/github/utils/subprocess-runner.ts:174

This PR updated taskkill to getTaskkillExePath() at lines 265 and 324 in this same file, but missed the third occurrence at line 174 inside the stopFn closure. This is the exact same issue the PR is trying to fix — bare taskkill will fail when System32 isn't in PATH. The import for getTaskkillExePath is already present at line 25, so this is a straightforward miss. All three usages in this file are semantically identical (kill process tree on Windows), and only 2 of 3 were updated.

Suggested fix:

Change line 174 to: execFile(getTaskkillExePath(), ['/pid', String(child.pid), '/T', '/F'], () => {});

🔵 [6e9520ff3687] [LOW] Stale comment still references removed shell=True

📁 apps/backend/core/gh_executable.py:137

The comment on line 137 reads '# 5. Try 'where' command with shell=True (more reliable on Windows)' but this PR removed shell=True from the actual _run_where_command() function (lines 57-63). The comment should be updated to reflect the new approach (full path to where.exe), consistent with the comment in git_executable.py line 129 which was properly updated to '# 4. Try 'where' command with full path (works even when System32 isn't in PATH)'.

Suggested fix:

Update comment to: # 5. Try 'where' command with full path (works even when System32 isn't in PATH)

🔵 [a26366090696] [LOW] Stale comment still references removed shell=True

📁 apps/backend/core/glab_executable.py:138

Same issue as gh_executable.py — the comment on line 138 reads '# 5. Try 'where' command with shell=True (more reliable on Windows)' but shell=True was removed from _run_where_command() in this PR. This is identical to the stale comment in gh_executable.py, suggesting a copy-paste pattern where both were missed during the comment update.

Suggested fix:

Update comment to: # 5. Try 'where' command with full path (works even when System32 isn't in PATH)

🟡 [76ee61a33822] [MEDIUM] Stale comment references removed shell=True

📁 apps/backend/core/gh_executable.py:137

The comment at line 137 says '# 5. Try 'where' command with shell=True (more reliable on Windows)' but this PR specifically removed shell=True from _run_where_command() and replaced the bare string command with get_where_exe_path(). The comment is now misleading since shell=True is no longer used. Should be updated to reference the full path approach instead.

Suggested fix:

Update comment to: # 5. Try 'where' command with full System32 path (works without PATH)

🟡 [7cb5235a83cd] [MEDIUM] Stale comment references removed shell=True

📁 apps/backend/core/glab_executable.py:138

Same issue as gh_executable.py - the comment at line 138 says '# 5. Try 'where' command with shell=True (more reliable on Windows)' but shell=True was removed from _run_where_command() by this PR. The comment is misleading and should reflect the new approach of using get_where_exe_path() with a full System32 path.

Suggested fix:

Update comment to: # 5. Try 'where' command with full System32 path (works without PATH)

This review was generated by Auto Claude.

VDT-91 and others added 2 commits February 5, 2026 15:21
- Fix missed bare 'taskkill' at subprocess-runner.ts:174 (stopFn closure)
- Fix missed bare 'where' at mcp-handlers.ts:198 (MCP health check)
- Update stale comments in gh_executable.py and glab_executable.py
  (removed incorrect "shell=True" reference, now matches git_executable.py)
- Add error logging callback for taskkill in stopFn (was empty callback)
- Improve MCP health check error messages with actionable diagnostics
  for ENOENT and EACCES errors on both Windows and Unix

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/backend/core/gh_executable.py (1)

50-75: 🧹 Nitpick | 🔵 Trivial

Optional: Consider extracting common executable discovery logic.

The _run_where_command() pattern is now duplicated across gh_executable.py, glab_executable.py, and git_executable.py. A future refactor could extract this into a shared helper:

# In core/platform/__init__.py or a new module
def run_where_command(executable_name: str) -> str | None:
    """Run Windows 'where' command to find an executable."""
    ...

This is not blocking for the current security-focused PR, but could reduce duplication going forward.

🤖 Fix all issues with AI agents
In `@apps/frontend/src/main/ipc-handlers/github/utils/subprocess-runner.ts`:
- Line 25: The current getTaskkillExePath helper builds a System32 path using
path.join and a hardcoded "C:\\Windows", which bypasses the platform helpers;
update the getTaskkillExePath implementation to use the platform abstraction
functions (joinPaths, getPathDelimiter, findExecutable and requiresShell) to
construct and locate taskkill.exe instead of hardcoding paths or separators, and
keep subprocess-runner.ts using getTaskkillExePath as-is; ensure any other
occurrences noted (lines ~174-176, 267-269, 326-328) are refactored similarly so
executable lookup calls rely on findExecutable and path construction uses
joinPaths/getPathDelimiter.

In `@apps/frontend/src/main/ipc-handlers/mcp-handlers.ts`:
- Around line 198-201: Replace the spawn-based which/where lookup with the
cross-platform helper: import findExecutableAsync from ./env-utils alongside
existing env-utils imports, remove use of getWhereExePath()/spawn, and call
await findExecutableAsync(server.command!) instead; then treat a non-null return
as "found" (rather than checking proc exit code), and handle the null case as
"not found" (preserving existing timeout/error handling semantics). Ensure
references to spawn and getWhereExePath() in this async handler (the block using
server.command!) are removed or replaced so only findExecutableAsync controls
the executable lookup.

import { getClaudeProfileManager } from '../../../claude-profile-manager';
import { getOperationRegistry, type OperationType } from '../../../claude-profile/operation-registry';
import { isWindows, isMacOS } from '../../../platform';
import { getTaskkillExePath } from '../../../utils/windows-paths';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Align taskkill path resolution with platform helpers.

getTaskkillExePath (apps/frontend/src/main/utils/windows-paths.ts Lines 153-156) builds the System32 path via path.join and a hardcoded C:\Windows fallback, which bypasses the required platform helpers for executable lookup and path handling. Please refactor the helper to use platform joinPaths/getPathDelimiter (and findExecutable/requiresShell if your platform API expects it) and keep using the helper here.

♻️ Example adjustment in windows-paths.ts
-import path from 'path';
+import { joinPaths, getPathDelimiter } from '../platform';

 export function getTaskkillExePath(): string {
-  const systemRoot = process.env.SystemRoot || process.env.SYSTEMROOT || 'C:\\Windows';
-  return path.join(systemRoot, 'System32', 'taskkill.exe');
+  const systemRoot =
+    process.env.SystemRoot ||
+    process.env.SYSTEMROOT ||
+    `C:${getPathDelimiter()}Windows`;
+  return joinPaths(systemRoot, 'System32', 'taskkill.exe');
 }

As per coding guidelines, cross-platform executable lookup and path handling must use findExecutable(), getPathDelimiter(), joinPaths(), and requiresShell() functions from platform modules, and never hardcode paths or platform-specific separators.

Also applies to: 174-176, 267-269, 326-328

🤖 Prompt for AI Agents
In `@apps/frontend/src/main/ipc-handlers/github/utils/subprocess-runner.ts` at
line 25, The current getTaskkillExePath helper builds a System32 path using
path.join and a hardcoded "C:\\Windows", which bypasses the platform helpers;
update the getTaskkillExePath implementation to use the platform abstraction
functions (joinPaths, getPathDelimiter, findExecutable and requiresShell) to
construct and locate taskkill.exe instead of hardcoding paths or separators, and
keep subprocess-runner.ts using getTaskkillExePath as-is; ensure any other
occurrences noted (lines ~174-176, 267-269, 326-328) are refactored similarly so
executable lookup calls rely on findExecutable and path construction uses
joinPaths/getPathDelimiter.

Comment on lines +198 to 201
const command = isWindows() ? getWhereExePath() : 'which';
const proc = spawn(command, [server.command!], {
timeout: 5000,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find apps/frontend/src/main/platform -type f -name "*.ts" | head -20

Repository: AndyMik90/Auto-Claude

Length of output: 309


🏁 Script executed:

rg -n "findExecutable" apps/frontend/src/main/platform -g "*.ts"

Repository: AndyMik90/Auto-Claude

Length of output: 141


🏁 Script executed:

rg -n "export function findExecutable\|export const findExecutable" apps/frontend/src/main/platform -g "*.ts"

Repository: AndyMik90/Auto-Claude

Length of output: 47


🏁 Script executed:

cat -n apps/frontend/src/main/ipc-handlers/mcp-handlers.ts | sed -n '190,210p'

Repository: AndyMik90/Auto-Claude

Length of output: 764


🏁 Script executed:

cat -n apps/frontend/src/main/platform/index.ts | sed -n '350,390p'

Repository: AndyMik90/Auto-Claude

Length of output: 1380


🏁 Script executed:

rg -n "getWhereExePath" apps/frontend/src/main/platform -g "*.ts" -A 3

Repository: AndyMik90/Auto-Claude

Length of output: 583


🏁 Script executed:

cat -n apps/frontend/src/main/platform/index.ts | sed -n '1,50p'

Repository: AndyMik90/Auto-Claude

Length of output: 1959


🏁 Script executed:

cat -n apps/frontend/src/main/ipc-handlers/mcp-handlers.ts | sed -n '195,220p'

Repository: AndyMik90/Auto-Claude

Length of output: 931


🏁 Script executed:

rg -n "findExecutable" apps/frontend/src -g "*.ts" -B 2 -A 4 | head -60

Repository: AndyMik90/Auto-Claude

Length of output: 5480


🏁 Script executed:

find apps/frontend/src/main -name "env-utils*" -type f

Repository: AndyMik90/Auto-Claude

Length of output: 240


🏁 Script executed:

rg -n "findExecutableAsync" apps/frontend/src/main -g "*.ts" -B 2 -A 8 | head -80

Repository: AndyMik90/Auto-Claude

Length of output: 6299


🏁 Script executed:

cat -n apps/frontend/src/main/env-utils.ts | sed -n '465,495p'

Repository: AndyMik90/Auto-Claude

Length of output: 1159


🏁 Script executed:

cat -n apps/frontend/src/main/ipc-handlers/mcp-handlers.ts | sed -n '1,30p'

Repository: AndyMik90/Auto-Claude

Length of output: 1419


Replace hardcoded which/where spawn with findExecutableAsync() from platform utilities.

The code hardcodes the which utility for POSIX systems and spawns an external process. Use findExecutableAsync() from env-utils instead, which provides cross-platform executable lookup without relying on external utilities. This aligns with the platform abstraction guidelines and removes the shell dependency.

Change the async handler pattern from spawn-based checking to direct path lookup:

  • Call findExecutableAsync(server.command!) instead of spawning which/where
  • Check if the result is not null (executable found) rather than checking exit code
  • Import findExecutableAsync from ./env-utils alongside existing env-utils imports
Current code pattern (lines 198–201)
    const command = isWindows() ? getWhereExePath() : 'which';
    const proc = spawn(command, [server.command!], {
      timeout: 5000,
    });
🤖 Prompt for AI Agents
In `@apps/frontend/src/main/ipc-handlers/mcp-handlers.ts` around lines 198 - 201,
Replace the spawn-based which/where lookup with the cross-platform helper:
import findExecutableAsync from ./env-utils alongside existing env-utils
imports, remove use of getWhereExePath()/spawn, and call await
findExecutableAsync(server.command!) instead; then treat a non-null return as
"found" (rather than checking proc exit code), and handle the null case as "not
found" (preserving existing timeout/error handling semantics). Ensure references
to spawn and getWhereExePath() in this async handler (the block using
server.command!) are removed or replaced so only findExecutableAsync controls
the executable lookup.

Copy link
Owner

@AndyMik90 AndyMik90 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Auto Claude PR Review

🟠 Follow-up Review: Needs Revision

🟠 Needs revision - 1 blocking issue(s) require fixes.

Resolution Status

  • Resolved: 5 previous findings addressed
  • Unresolved: 0 previous findings remain
  • 🆕 New Issues: 1 new findings in recent changes

Finding Validation

  • 🔍 Dismissed as False Positives: 3 findings were re-investigated and found to be incorrect
  • Confirmed Valid: 1 findings verified as genuine issues
  • 👤 Needs Human Review: 0 findings require manual verification

🚨 Blocking Issues

  • quality: Missed bare 'where gh' — incomplete refactoring in same file

Verdict

All 21 CI checks are passing. All 5 previous findings have been verified as resolved — the contributor addressed every piece of feedback from the initial review. However, 1 new medium-severity finding (NEW-001) was confirmed valid: a bare 'where gh' call at line 617 in subprocess-runner.ts was missed in this refactoring. This is the exact class of issue the PR aims to fix, and the file already imports getTaskkillExePath from the same windows-paths module, making getWhereExePath readily available. Three other findings were dismissed as false positives after validation (interactive terminal context mitigates bare taskkill, Python os.environ is case-insensitive on Windows, SystemRoot never changes during process lifetime). CodeRabbit's duplication refactor suggestion is non-blocking. The one remaining fix is small and straightforward — import getWhereExePath and use it at line 617.

Review Process

Agents invoked: resolution-verifier, new-code-reviewer, comment-analyzer, finding-validator


This is an AI-generated follow-up review using parallel specialist analysis with finding validation.

Findings (1 selected of 1 total)

🟡 [NEW-001] [MEDIUM] Missed bare 'where gh' — incomplete refactoring in same file

📁 apps/frontend/src/main/ipc-handlers/github/utils/subprocess-runner.ts:617

The validateGitHubModule() function at line 617 uses bare 'where gh' via execAsync() instead of using getWhereExePath() from windows-paths.ts. This is a missed instance of the exact class of issue this PR fixes. The file already imports getTaskkillExePath from the same module (line 25) and uses it consistently at lines 174, 267, and 326, but getWhereExePath is not imported and 'where' is called bare. On Windows systems where System32 is not in the inherited PATH, this gh CLI validation check would fail even though gh is installed.

Suggested fix:

Import getWhereExePath from '../../../utils/windows-paths' alongside getTaskkillExePath, then replace line 617 with: const whichCommand = isWindows() ? `"${getWhereExePath()}" gh` : 'which gh'; Or better yet, use execFileAsync with getWhereExePath() as the command and ['gh'] as args to avoid shell invocation entirely.

This review was generated by Auto Claude.

VDT-91 and others added 2 commits February 6, 2026 07:24
Fix missed bare 'where gh' at line 617 in subprocess-runner.ts.
This completes the System32 executable path refactoring.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/frontend/src/main/ipc-handlers/github/utils/subprocess-runner.ts (1)

631-633: ⚠️ Potential issue | 🟡 Minor

gh auth status at line 633 uses bare gh instead of the resolved path from verification.

Line 617–618 properly resolves where.exe on Windows to handle incomplete PATH, but line 633 invokes gh by name without first capturing and reusing the resolved path. In the same restricted environment, this call would fail identically. Use findExecutable('gh') (available in env-utils.ts) to obtain and cache the gh path during verification, then pass it to execAsync or switch to execFileAsync with the resolved path.

🤖 Fix all issues with AI agents
In `@apps/frontend/src/main/ipc-handlers/github/utils/subprocess-runner.ts`:
- Around line 174-176: Extract the repeated execFile(getTaskkillExePath(),
['/pid', String(child.pid), '/T', '/F'], callback) pattern into a single helper
(e.g., killChildProcess or taskkillPid) and use it from the SubprocessRunner
code paths (stopFn, the auth-failure kill, and the billing-failure kill). The
helper should accept the pid and an optional reason/context string, call
execFile(getTaskkillExePath(), ['/pid', String(pid), '/T', '/F'], ...), and
centralize the warning log message so all three sites call
killChildProcess(child.pid, 'stopFn'|'auth-failure'|'billing-failure') instead
of repeating the execFile call and console.warn.
- Around line 617-618: Replace the shell-based whichCommand + execAsync
invocation with a direct execFile call to the where.exe binary: when isWindows()
is true call execFile(getWhereExePath(), ['gh']) (or the equivalent execFile
pattern used by taskkill calls) instead of constructing a quoted command string;
keep the existing execAsync usage for non-Windows or replace both branches
consistently so subprocess-runner uses execFile/execFileAsync (referencing
isWindows(), getWhereExePath(), whichCommand and execAsync) to remove shell
invocation.

Comment on lines +174 to +176
execFile(getTaskkillExePath(), ['/pid', String(child.pid), '/T', '/F'], (err: Error | null) => {
if (err) console.warn('[SubprocessRunner] taskkill error (process may have already exited):', err.message);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider extracting the repeated taskkill invocation into a helper.

The identical execFile(getTaskkillExePath(), ['/pid', ...], callback) pattern appears three times (stopFn, auth-failure kill, billing-failure kill). A small local helper would reduce duplication and make future changes (e.g., adding a timeout or changing logging) a single-point edit.

♻️ Suggested helper
+/** Kill a Windows process tree by PID using taskkill.exe */
+function killWindowsProcessTree(pid: number): void {
+  execFile(getTaskkillExePath(), ['/pid', String(pid), '/T', '/F'], (err: Error | null) => {
+    if (err) console.warn('[SubprocessRunner] taskkill error (process may have already exited):', err.message);
+  });
+}

Then replace each call site, e.g.:

-              execFile(getTaskkillExePath(), ['/pid', String(child.pid), '/T', '/F'], (err: Error | null) => {
-                if (err) console.warn('[SubprocessRunner] taskkill error (process may have already exited):', err.message);
-              });
+              killWindowsProcessTree(child.pid);

Also applies to: 267-269, 326-328

🤖 Prompt for AI Agents
In `@apps/frontend/src/main/ipc-handlers/github/utils/subprocess-runner.ts` around
lines 174 - 176, Extract the repeated execFile(getTaskkillExePath(), ['/pid',
String(child.pid), '/T', '/F'], callback) pattern into a single helper (e.g.,
killChildProcess or taskkillPid) and use it from the SubprocessRunner code paths
(stopFn, the auth-failure kill, and the billing-failure kill). The helper should
accept the pid and an optional reason/context string, call
execFile(getTaskkillExePath(), ['/pid', String(pid), '/T', '/F'], ...), and
centralize the warning log message so all three sites call
killChildProcess(child.pid, 'stopFn'|'auth-failure'|'billing-failure') instead
of repeating the execFile call and console.warn.

Comment on lines +617 to 618
const whichCommand = isWindows() ? `"${getWhereExePath()}" gh` : 'which gh';
await execAsync(whichCommand);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Use execFile instead of exec for where.exe — consistent with the taskkill calls and the PR's goal of removing shell invocations.

The taskkill calls in this file already use execFile (no shell). The where.exe call should follow the same pattern, especially since the PR objective explicitly includes removing shell=True. where.exe is a standalone executable, so no shell is needed on Windows.

♻️ Proposed fix
+const execFileAsync = promisify(execFile);
+
 // 2. Check gh CLI installation (cross-platform)
 try {
-    const whichCommand = isWindows() ? `"${getWhereExePath()}" gh` : 'which gh';
-    await execAsync(whichCommand);
+    if (isWindows()) {
+      await execFileAsync(getWhereExePath(), ['gh']);
+    } else {
+      await execAsync('which gh');
+    }
     result.ghCliInstalled = true;
🤖 Prompt for AI Agents
In `@apps/frontend/src/main/ipc-handlers/github/utils/subprocess-runner.ts` around
lines 617 - 618, Replace the shell-based whichCommand + execAsync invocation
with a direct execFile call to the where.exe binary: when isWindows() is true
call execFile(getWhereExePath(), ['gh']) (or the equivalent execFile pattern
used by taskkill calls) instead of constructing a quoted command string; keep
the existing execAsync usage for non-Windows or replace both branches
consistently so subprocess-runner uses execFile/execFileAsync (referencing
isWindows(), getWhereExePath(), whichCommand and execAsync) to remove shell
invocation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants