Finding the Claude CLI where it actually is
A user ran claude --version, got 2.1.231, and Agento told them Claude Code was not installed. They were both right.
Agento does not ship the Claude Code CLI. It spawns it — every agent run is a
subprocess, and the CLI’s own sign-in is the credential. So the first thing the
app does on launch is work out where claude lives, and the first thing it did
wrong was assume the answer was on PATH.
A GUI app has a different PATH than your terminal
This is the part that surprises people, and it surprised us. When you launch an
application from Finder, the Dock or Spotlight, it inherits launchd’s
environment rather than your shell’s. That is
/usr/bin:/bin:/usr/sbin:/sbin and nothing else. Every line your .zshrc
exported is invisible.
The launch that matters — from the Dock, by someone who has not opened a terminal that day — is exactly the launch where scanning
PATHfinds nothing.
Worse, there is an install shape that is on no PATH at all.
claude migrate-installer puts the binary in ~/.claude/local and wires it up
as a shell alias. No amount of scanning directories finds it, because there is
no file to find under any name you would look for.
Five rules, in order
Resolution is now a single function with an explicit order, cached once per launch:
1. AGENTO_CLAUDE_EXECUTABLE explicit override, trusted2. claude_executable_path setting stored, trusted3. $SHELL -lic 'command -v claude' login shell — finds aliases4. the process PATH works when launched from a terminal5. known install locations last resort, verifiedRules three through five verify what they find: the path must be an executable
file and answer --version with Claude Code’s banner, so an unrelated
claude on the PATH is skipped rather than spawned on every turn. Rules one
and two are taken on trust — a wrapper script is a documented reason to set
them, and refusing one would remove the escape hatch the setting exists to be.
The banner and the spawn had to become one answer
The reported bug was really a pair. The startup banner said the CLI was
missing, and agents genuinely could not run — because the fallback was the
bare name claude, which fails to resolve for exactly the same reason the
banner did. Two lookups that agreed on being wrong.
They are one function now. If the banner is wrong, every run is wrong, and you find out at launch instead of at the first message.
The bounds are a startup budget, not a ceiling
Both subprocesses run inside the app’s setup block, before the window shows.
That makes three seconds for the shell probe and two per --version the worst
case a user waits for a window — not a generous limit. A pathological shell
degrades to “found by a later rule”, never to a window that does not open.
Resolution happens once per launch, so saving the setting takes effect at the
next start. Re-detecting live was considered and left out: a OnceLock filled
by a background thread races the first caller, and the loser fills it without
the stored override — which would mean a configured path being ignored on some
launches and not others.