Skip to content

fix(cli): resolve native-installer Claude binary instead of falling back to a stale install#1394

Open
AlexiFeng wants to merge 1 commit into
slopus:mainfrom
AlexiFeng:fix/native-claude-binary-resolution
Open

fix(cli): resolve native-installer Claude binary instead of falling back to a stale install#1394
AlexiFeng wants to merge 1 commit into
slopus:mainfrom
AlexiFeng:fix/native-claude-binary-resolution

Conversation

@AlexiFeng

Copy link
Copy Markdown

Problem

On macOS/Linux, happy can launch an older Claude Code than the one which claude resolves to.

findClaudeInPath() in packages/happy-cli/scripts/claude_version_utils.cjs takes the first which claude result, resolves the symlink, and — if the resolved path doesn't end in .js / .cjs / .exe — assumes it's a Windows npm shim, looks for an adjacent node_modules/@anthropic-ai/claude-code, and returns null when none is found.

But the native installer (Claude Code ≥ 2.1.113) ships a self-contained, extensionless compiled binary at ~/.local/share/claude/versions/<ver>. That path is therefore wrongly rejected, and findGlobalClaudeCliPath() silently falls through to the next finder — frequently a stale Homebrew copy.

Repro

Native installer on PATH (~/.local/bin/claude~/.local/share/claude/versions/2.1.175) plus an old brew-installed @anthropic-ai/claude-code:

$ which claude
/Users/me/.local/bin/claude          # native, 2.1.175

$ happy --version
1.0.102 (Claude Code)                 # from Homebrew — NOT what `which` resolves to

findClaudeInPath() returns null, so resolution falls back to /opt/homebrew/.../cli.js (v1.0.102).

Fix

Gate the shim-redirect branch to win32, where extensionless npm shims actually exist. On every other platform an extensionless binary is the native installer and is used directly — runClaudeCli() already spawns non-JS entrypoints as binaries.

After the change:

$ happy --version
2.1.175 (Claude Code)                 # native installer, matches `which claude`

findClaudeInPath(){ path: '~/.local/share/claude/versions/2.1.175', source: 'native installer' }.

Tests

New claude_version_utils.path.test.ts:

  • native binary resolved on Linux (no longer null)
  • native binary resolved on macOS (~/.local/binversions/<ver>)
  • npm cli.js resolution unchanged

Windows behaviour is unchanged — the shim path is preserved, just guarded behind process.platform === 'win32'.

…ack to a stale install

findClaudeInPath() treated any `which claude` result whose resolved path
did not end in .js/.cjs/.exe as a Windows npm shim: it looked for an
adjacent node_modules/@anthropic-ai/claude-code and returned null when
none was found.

On macOS/Linux that assumption is wrong. The native installer
(>= 2.1.113) ships a self-contained, extensionless compiled binary at
~/.local/share/claude/versions/<ver>. It was therefore rejected, and
findGlobalClaudeCliPath() silently fell back to a different — often
older — installation (e.g. a stale Homebrew copy) even when
`which claude` already pointed at the newer native binary.

Gate the shim-redirect logic to win32, where extensionless npm shims
actually exist. On other platforms the extensionless binary is used
directly; runClaudeCli() already spawns non-JS entrypoints as binaries.

Add regression tests covering native-binary resolution on macOS/Linux
and unchanged npm cli.js resolution.
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.

1 participant