Fix Claude Code CLI detection for npm-local installs#1978
Fix Claude Code CLI detection for npm-local installs#1978one-kash wants to merge 2 commits intogithub:mainfrom
Conversation
`specify check` reports "Claude Code CLI (not found)" for users who installed Claude Code via npm-local (the default installer path, common with nvm). The binary lives at ~/.claude/local/node_modules/.bin/claude which was not checked. Add CLAUDE_NPM_LOCAL_PATH as a second well-known location alongside the existing migrate-installer path. Fixes github#550
There was a problem hiding this comment.
Pull request overview
Fixes specify check incorrectly reporting Claude Code as missing when installed via npm-local by adding an additional well-known local binary path and covering the behavior with targeted tests.
Changes:
- Add
CLAUDE_NPM_LOCAL_PATH(~/.claude/local/node_modules/.bin/claude) and treat it as a valid Claude local install location incheck_tool("claude"). - Update Claude-specific detection logic comments to reflect multiple local install methods.
- Add unit tests covering Claude detection via migrate-installer path, npm-local path, PATH, and tracker updates; plus regression checks for other tools.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/specify_cli/__init__.py |
Adds npm-local Claude binary path constant and checks it in check_tool before falling back to PATH. |
tests/test_check_tool.py |
New tests validating Claude detection across install methods and ensuring non-Claude tools are unaffected. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
tests/test_check_tool.py
Outdated
| import pytest | ||
|
|
There was a problem hiding this comment.
pytest is imported but not used in this test module (the tmp_path fixture doesn't require an explicit import). Consider removing the unused import to keep the tests clean.
| import pytest |
tests/test_check_tool.py
Outdated
| fake_missing = Path("/nonexistent/claude") | ||
|
|
||
| with patch("specify_cli.CLAUDE_LOCAL_PATH", fake_missing), \ | ||
| patch("specify_cli.CLAUDE_NPM_LOCAL_PATH", fake_missing), \ | ||
| patch("shutil.which", return_value="/usr/local/bin/claude"): | ||
| assert check_tool("claude") is True | ||
|
|
||
| def test_not_found_when_nowhere(self): | ||
| """Should return False when claude is genuinely not installed.""" | ||
| fake_missing = Path("/nonexistent/claude") | ||
|
|
||
| with patch("specify_cli.CLAUDE_LOCAL_PATH", fake_missing), \ | ||
| patch("specify_cli.CLAUDE_NPM_LOCAL_PATH", fake_missing), \ | ||
| patch("shutil.which", return_value=None): |
There was a problem hiding this comment.
These tests use a hard-coded absolute path (/nonexistent/claude) to represent a missing file. Prefer constructing a non-existent path under tmp_path (without creating it) so the tests are fully hermetic and don't depend on assumptions about the host filesystem.
src/specify_cli/__init__.py
Outdated
| if (CLAUDE_LOCAL_PATH.exists() and CLAUDE_LOCAL_PATH.is_file()) or \ | ||
| (CLAUDE_NPM_LOCAL_PATH.exists() and CLAUDE_NPM_LOCAL_PATH.is_file()): |
There was a problem hiding this comment.
The Claude local-path check does two filesystem stats per path (exists() then is_file()). Path.is_file() already returns False when the path doesn't exist, so the exists() calls are redundant. Consider simplifying to reduce repeated I/O and improve readability (and avoid the backslash continuation by using parentheses or any(...)).
| if (CLAUDE_LOCAL_PATH.exists() and CLAUDE_LOCAL_PATH.is_file()) or \ | |
| (CLAUDE_NPM_LOCAL_PATH.exists() and CLAUDE_NPM_LOCAL_PATH.is_file()): | |
| if CLAUDE_LOCAL_PATH.is_file() or CLAUDE_NPM_LOCAL_PATH.is_file(): |
mnriem
left a comment
There was a problem hiding this comment.
Please address Copilot feedback. If not applicable, please explain why
|
You brought the big guns, I like it! I might have to call my buddy Claude too ;) |
- Remove unused pytest import from test_check_tool.py - Use tmp_path instead of hardcoded /nonexistent/claude for hermetic tests - Simplify redundant exists() + is_file() to just is_file() AI-assisted: Changes applied with Claude Code.
Summary
specify checkreporting "Claude Code CLI (not found)" for users with npm-local installs (the default Claude Code install method, common with nvm)CLAUDE_NPM_LOCAL_PATH(~/.claude/local/node_modules/.bin/claude) as a second well-known detection path alongside the existingmigrate-installerpathRoot Cause
check_tool("claude")only checks:~/.claude/local/claude(migrate-installer path)shutil.which("claude")(system PATH)For npm-local installs, the binary is at
~/.claude/local/node_modules/.bin/claude— neither of the above paths match, so detection fails even though Claude is installed and working.Test plan
~/.claude/local/claude)~/.claude/local/node_modules/.bin/claude)Fixes #550