fix(claude): give spawned CLI a working PATH; prefer configured binary#70
Merged
Conversation
A packaged .app launched from Finder inherits launchd's minimal PATH, which lacks Node — and `claude` is a `#!/usr/bin/env node` script, so the spawned process died with `env: node: No such file or directory`. Three related backend fixes in resolve_claude_binary / spawn_claude_resume: - Set the child's PATH via a new pure, unit-tested `build_child_path`: the resolved binary's own dir first, then the login-shell PATH, the inherited PATH, and well-known fallbacks (nvm/Homebrew/system), de-duplicated preserving first occurrence. - Reorder resolution so the user's configured CLI wins: which → login shell → hardcoded dir scan last. The scan could previously preempt the shell and pick a stale global install that 400s on a deprecated shape. - Read the shell with an interactive login shell (`-ilc`, not `-lc`) so toolchain PATH lines in .zshrc/.bashrc are sourced; read the child PATH behind a sentinel so rc/profile banners can't clobber it. Automated verification of the .app-from-Finder path is manual (the packaged launchd env can't be reconstructed in cargo test); the pure PATH builder is covered by four unit tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ESi6dnK3Wc1jYRpZN5qfZA
Records the Group 1 implementation plan and vendors the docs/to-improve/ behavior specs (01–12) that drive this multi-group port, so the porting provenance lives in the repo alongside the changes it produces. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ESi6dnK3Wc1jYRpZN5qfZA
Merged
sam-powers
added a commit
that referenced
this pull request
Jul 10, 2026
Patch release bundling the fixes from PRs #70–#74: - @claude CLI resolution: working PATH + prefer configured binary (#70) - desktop: menu stale-closure, window-close capability, deep-link window surfacing (#71) - retry failed @claude replies in place (#72) - re-run cancelled @claude replies; remove broken new-session (#73) - surface @claude in composer placeholders; widen reading measure (#74) Also removes the stray docs/to-improve and docs/plans files that landed via #70 — those are downstream porting notes, not public-repo content. Claude-Session: https://claude.ai/code/session_01ESi6dnK3Wc1jYRpZN5qfZA Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
When Quill runs as a packaged macOS
.applaunched from Finder, launchd hands it a minimal PATH (/usr/bin:/bin:/usr/sbin:/sbin) that lacks Node. Sinceclaudeis a#!/usr/bin/env nodescript, the spawned process died withenv: node: No such file or directory—@claudereplies and full-document review silently failed for installed users. (It never reproduced undertauri devfrom a terminal, which is why it went unnoticed.)This ports three related backend behavior specs (
docs/to-improve/01–03) into the public repo, adapted to this repo's conventions rather than pasted from the fork.Changes (
src-tauri/src/lib.rs)build_child_pathassembles the child's PATH — the resolved binary's own dir first (its siblingnodelives there for nvm/Homebrew layouts), then the login-shell PATH, the inherited PATH, and well-known fallbacks (nvm newest-first,~/.local/bin, Homebrew, system dirs) — de-duplicated preserving first occurrence. Wired intospawn_claude_resumevia.env("PATH", …)(alongside the existing.env_remove("ANTHROPIC_API_KEY"), untouched).resolve_claude_binary:which→ login shell → hardcoded dir scan last. Previously the scan ran before the shell and could pick a stale global install that emits a deprecated request shape and400s ("thinking.type.enabled" is not supported…). The scan is now a last-resort fallback, never a preemption.-ilcinstead of-lc, because toolchain PATH lines (nvm/fnm/Homebrew/Volta) commonly live in.zshrc/.bashrc, which a non-interactive login shell doesn't source. Binary resolution takes the last non-empty stdout line (banner-proof); the child-PATH read prints$PATHbehind a unique sentinel so rc/profile banners can't clobber it. Shell reads are factored intologin_shell_stdoutso the two callers don't duplicate the invocation.Testing
cargo test— 39 pass, including four newbuild_child_pathtests: includes the binary's own dir; claude dir comes first; de-dups preserving first occurrence; well-known dirs present when login + inherited PATH are both absent (the packaged-.appworst case).cargo clippy -- -D warningsandcargo fmt --check— clean..app-from-Finder spawn path can't be reconstructed incargo test— verify by building the.app, launching from Finder, and confirming an@claudereply streams instead ofenv: node: No such file or directory.Notes
claudeinstall — only the preference order.docs/to-improve/(this PR = Group 1, specs 01–03). The full spec set is vendored here for provenance; Groups 2–5 follow in separate PRs.🤖 Generated with Claude Code