Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes an issue where Claude agents failed to spawn on macOS with /bin/sh: claude: command not found errors by ensuring Homebrew binary directories are included in the PATH when spawning non-Windows agents.
Changes:
- Added macOS-specific PATH enrichment that prepends common Homebrew locations (
/opt/homebrew/bin,/usr/local/bin) to the PATH when spawning agents, if PATH is not explicitly configured - Updated package-lock.json version from 0.1.0 to 0.1.8 to match package.json
Reviewed changes
Copilot reviewed 1 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src-tauri/src/agent.rs | Added conditional macOS PATH enrichment logic to ensure Homebrew binaries (like npx) are available when spawning agents |
| package-lock.json | Updated version to 0.1.8 for consistency with package.json |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #[cfg(target_os = "macos")] | ||
| { | ||
| if !config.env.contains_key("PATH") { | ||
| let existing_path = env::var("PATH").unwrap_or_default(); | ||
| let mut path_entries: Vec<&str> = existing_path | ||
| .split(':') | ||
| .filter(|s| !s.is_empty()) | ||
| .collect(); | ||
| for extra in ["/opt/homebrew/bin", "/usr/local/bin"] { | ||
| if !path_entries.iter().any(|p| p == &extra) { | ||
| path_entries.push(extra); | ||
| } | ||
| } | ||
| cmd.env("PATH", path_entries.join(":")); | ||
| } | ||
| } | ||
|
|
||
| cmd.envs(&config.env) | ||
| .stdin(Stdio::piped()) |
There was a problem hiding this comment.
When PATH is explicitly set in config.env, the macOS Homebrew path enrichment is completely bypassed. This means users who want to customize their PATH must manually include /opt/homebrew/bin and /usr/local/bin, or they'll lose access to Homebrew-installed binaries like npx.
Consider prepending Homebrew paths to a user-provided PATH instead of skipping enrichment entirely. For example, you could merge the Homebrew paths with the user's PATH value from config.env.
| #[cfg(target_os = "macos")] | |
| { | |
| if !config.env.contains_key("PATH") { | |
| let existing_path = env::var("PATH").unwrap_or_default(); | |
| let mut path_entries: Vec<&str> = existing_path | |
| .split(':') | |
| .filter(|s| !s.is_empty()) | |
| .collect(); | |
| for extra in ["/opt/homebrew/bin", "/usr/local/bin"] { | |
| if !path_entries.iter().any(|p| p == &extra) { | |
| path_entries.push(extra); | |
| } | |
| } | |
| cmd.env("PATH", path_entries.join(":")); | |
| } | |
| } | |
| cmd.envs(&config.env) | |
| .stdin(Stdio::piped()) | |
| // On macOS, ensure Homebrew paths are present even when PATH is provided in config.env | |
| #[cfg(target_os = "macos")] | |
| let merged_path: Option<String> = { | |
| // Start from PATH in config.env if present; otherwise, fall back to the current process PATH | |
| let base_path = config | |
| .env | |
| .get("PATH") | |
| .cloned() | |
| .unwrap_or_else(|| env::var("PATH").unwrap_or_default()); | |
| let mut path_entries: Vec<String> = base_path | |
| .split(':') | |
| .filter(|s| !s.is_empty()) | |
| .map(|s| s.to_string()) | |
| .collect(); | |
| for extra in ["/opt/homebrew/bin", "/usr/local/bin"] { | |
| if !path_entries.iter().any(|p| p == extra) { | |
| // Prepend Homebrew paths so they take precedence | |
| path_entries.insert(0, extra.to_string()); | |
| } | |
| } | |
| Some(path_entries.join(":")) | |
| }; | |
| #[cfg(not(target_os = "macos"))] | |
| let merged_path: Option<String> = None; | |
| cmd.envs(&config.env); | |
| if let Some(path) = merged_path { | |
| cmd.env("PATH", path); | |
| } | |
| cmd.stdin(Stdio::piped()) |
macOS “New Session” still failed with
/bin/sh: claude: command not foundbecause Homebrew binaries weren’t on PATH when spawning the Claude agent./opt/homebrew/bin,/usr/local/bin) sonpx/Claude binaries resolve.Example (spawn path enrichment):