fix: AI CLI discovery and manual path overrides (v2.1.2)#39
Conversation
|
Warning Review limit reached
Next review available in: 25 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (11)
📝 WalkthroughWalkthroughAdds AI CLI discovery and status reporting, environment-backed CLI path settings, stdin-based prompt execution, backend and UI wiring for the new status flow, and tests covering discovery and env-setting behavior. ChangesAI CLI discovery, settings, and backend flow
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (5)
tests/test_ai_fallback.py (2)
348-357: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd symmetric coverage for
PODCLI_CODEX_PATH.Only the claude override path is tested; codex env override precedence isn't covered. Given
_find_ai_cli_candidatestreats both symmetrically, a similar test for codex would guard against regressions in that branch too.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_ai_fallback.py` around lines 348 - 357, Add a matching test for the codex override path so `_find_ai_cli_candidates` is covered symmetrically with `PODCLI_CLAUDE_PATH`. Mirror `test_env_override_prefers_podcli_claude_path` in `test_ai_fallback.py`, but set `PODCLI_CODEX_PATH`, stub `_find_cli`, and assert the codex candidate is preferred and labeled correctly. Use the existing `_find_ai_cli_candidates`, `_find_cli`, and `_ai_cli_search_paths` helpers so the new test guards the codex branch against regressions.
359-388: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winStrengthen stdin assertion in
test_run_ai_command_pipes_prompt_via_stdin.The test only checks
"stdin" in kwargsandshellis falsy, but doesn't verify the stdin actually carries the prompt file's content (or is the same file handle opened forprompt_file). A regression that passes the wrong stream (e.g., an empty file orpromptstring) would still pass this test.♻️ Suggested stronger assertion
args, kwargs = run_mock.call_args self.assertEqual(args[0], [cli, "--print", "-p", "-"]) self.assertIn("stdin", kwargs) + self.assertEqual(kwargs["stdin"].name, prompt_file) self.assertFalse(kwargs.get("shell"))🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_ai_fallback.py` around lines 359 - 388, The stdin check in test_run_ai_command_pipes_prompt_via_stdin is too weak and does not confirm the prompt file content is actually being piped. Strengthen the assertions around cs._run_ai_command and subprocess.run so the test verifies the stdin argument is the expected prompt_file handle or otherwise clearly bound to the file content, not just present in kwargs. Keep the existing checks for the command args and shell behavior, but add a more specific assertion on the stdin stream used for the call.backend/services/claude_suggest.py (3)
92-101: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMinor type-hint/style nits.
extra_paths: list[str] = Noneis an implicitOptional(RUF013); annotate aslist[str] | None. Line 100 concatenation can use unpacking (RUF005).♻️ Proposed tweak
-def _find_cli(name: str, extra_paths: list[str] = None) -> Optional[str]: +def _find_cli(name: str, extra_paths: list[str] | None = None) -> Optional[str]: import shutil for path in extra_paths or []: resolved = _resolve_cli_path(path) if resolved: return resolved - lookup_path = os.pathsep.join(_path_lookup_dirs() + [os.environ.get("PATH", "")]) + lookup_path = os.pathsep.join([*_path_lookup_dirs(), os.environ.get("PATH", "")]) return shutil.which(name, path=lookup_path)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/services/claude_suggest.py` around lines 92 - 101, Update the _find_cli helper in claude_suggest.py to use an explicit optional type for extra_paths by changing its annotation from an implicit None default to list[str] | None, and adjust the lookup_path construction to use iterable unpacking instead of list concatenation. Keep the changes localized to _find_cli and preserve the existing behavior of resolving paths and calling shutil.which.Source: Linters/SAST tools
59-63: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSimplify the
globimport.globis a stdlib module whose import cannot realistically fail, so the broadtry/except/passis effectively dead and only suppresses potential real errors fromglob.glob. Move the import to module scope and drop the blanket handler.♻️ Proposed simplification
- nvm_dir = os.environ.get("NVM_DIR") or os.path.join(home, ".nvm") - nvm_glob = os.path.join(nvm_dir, "versions", "node", "*", "bin") - try: - import glob - dirs.extend(sorted(glob.glob(nvm_glob), reverse=True)) - except Exception: - pass + nvm_dir = os.environ.get("NVM_DIR") or os.path.join(home, ".nvm") + nvm_glob = os.path.join(nvm_dir, "versions", "node", "*", "bin") + dirs.extend(sorted(glob.glob(nvm_glob), reverse=True))(add
import globat the top of the module)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/services/claude_suggest.py` around lines 59 - 63, Move the stdlib `glob` import in `claude_suggest.py` out of the `try/except` and into module scope, since `glob` cannot realistically fail to import. Remove the blanket `try/except/pass` around the `dirs.extend(sorted(glob.glob(nvm_glob), reverse=True))` logic so `glob.glob` errors are not silently swallowed. Keep the existing `dirs`-building flow intact in the same code path.Source: Linters/SAST tools
212-225: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winstdin piping fix looks correct; consider avoiding
shell=Trueon the Windows path. Feeding the prompt viastdin(rather thancat | claudewithshell=True) is the right fix, and the request-derived prompt never reaches the command string — so the static-analysis "command from incoming request" flags here are false positives.The remaining
shell=Truebranch still string-formatscli_path(which may originate from an operator-setPODCLI_CLAUDE_PATH) into a shell command, so a path containing quotes/spaces/metacharacters could break or alter the invocation. You can run.cmd/.baton Windows withoutshell=Trueby delegating tocmd /cwith an argv list, which letssubprocesshandle quoting:🔒 Proposed argv-based Windows invocation
- shell = sys.platform == "win32" and cli_path.lower().endswith((".cmd", ".bat")) - cmd = f'"{cli_path}" --print -p -' if shell else [cli_path, "--print", "-p", "-"] - with open(prompt_file, encoding="utf-8") as prompt_fh: - return subprocess.run( - cmd, - stdin=prompt_fh, - capture_output=True, - text=True, - encoding="utf-8", - errors="replace", - cwd=project_dir, - timeout=timeout, - shell=shell, - ) + if sys.platform == "win32" and cli_path.lower().endswith((".cmd", ".bat")): + cmd = ["cmd", "/c", cli_path, "--print", "-p", "-"] + else: + cmd = [cli_path, "--print", "-p", "-"] + with open(prompt_file, encoding="utf-8") as prompt_fh: + return subprocess.run( + cmd, + stdin=prompt_fh, + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + cwd=project_dir, + timeout=timeout, + )Please verify
.cmd/.batresolution on a real Windows environment, since this path can't be exercised here.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/services/claude_suggest.py` around lines 212 - 225, The stdin piping change is fine, but the Windows branch in `claude_suggest.py` still uses `shell=True` with a formatted command string in the subprocess call inside `subprocess.run`. Update this path to invoke `.cmd`/`.bat` via an argv-based command (for example, through `cmd /c`) so `cli_path` is never interpolated into a shell string, and keep the existing `prompt_file` stdin handling in place. Verify the behavior in the `shell`/`cmd` setup logic before returning from the helper that launches Claude.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@backend/services/claude_suggest.py`:
- Around line 92-101: Update the _find_cli helper in claude_suggest.py to use an
explicit optional type for extra_paths by changing its annotation from an
implicit None default to list[str] | None, and adjust the lookup_path
construction to use iterable unpacking instead of list concatenation. Keep the
changes localized to _find_cli and preserve the existing behavior of resolving
paths and calling shutil.which.
- Around line 59-63: Move the stdlib `glob` import in `claude_suggest.py` out of
the `try/except` and into module scope, since `glob` cannot realistically fail
to import. Remove the blanket `try/except/pass` around the
`dirs.extend(sorted(glob.glob(nvm_glob), reverse=True))` logic so `glob.glob`
errors are not silently swallowed. Keep the existing `dirs`-building flow intact
in the same code path.
- Around line 212-225: The stdin piping change is fine, but the Windows branch
in `claude_suggest.py` still uses `shell=True` with a formatted command string
in the subprocess call inside `subprocess.run`. Update this path to invoke
`.cmd`/`.bat` via an argv-based command (for example, through `cmd /c`) so
`cli_path` is never interpolated into a shell string, and keep the existing
`prompt_file` stdin handling in place. Verify the behavior in the `shell`/`cmd`
setup logic before returning from the helper that launches Claude.
In `@tests/test_ai_fallback.py`:
- Around line 348-357: Add a matching test for the codex override path so
`_find_ai_cli_candidates` is covered symmetrically with `PODCLI_CLAUDE_PATH`.
Mirror `test_env_override_prefers_podcli_claude_path` in `test_ai_fallback.py`,
but set `PODCLI_CODEX_PATH`, stub `_find_cli`, and assert the codex candidate is
preferred and labeled correctly. Use the existing `_find_ai_cli_candidates`,
`_find_cli`, and `_ai_cli_search_paths` helpers so the new test guards the codex
branch against regressions.
- Around line 359-388: The stdin check in
test_run_ai_command_pipes_prompt_via_stdin is too weak and does not confirm the
prompt file content is actually being piped. Strengthen the assertions around
cs._run_ai_command and subprocess.run so the test verifies the stdin argument is
the expected prompt_file handle or otherwise clearly bound to the file content,
not just present in kwargs. Keep the existing checks for the command args and
shell behavior, but add a more specific assertion on the stdin stream used for
the call.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a51d5c7c-c985-47cb-939d-b581ec772060
📒 Files selected for processing (3)
backend/main.pybackend/services/claude_suggest.pytests/test_ai_fallback.py
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/test_ai_fallback.py (1)
382-418: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winMissing test method boundary — two unrelated tests merged into one.
test_get_ai_cli_status_reports_candidates(382-390) assertsget_ai_cli_status()output, but the same method body continues (391-418, same indentation) into an unrelated scenario exercising_run_ai_command's stdin/shell behavior. The line-range summary describes this as a separate test concern, suggesting adef test_...(self):line was accidentally dropped when splitting these into two tests.Proposed fix: split into a separate test method
self.assertTrue(status["available"]) self.assertEqual(status["candidates"][0]["engine"], "claude") + + def test_run_ai_command_pipes_prompt_via_stdin(self): with tempfile.TemporaryDirectory() as tmp: prompt_file = os.path.join(tmp, "prompt.txt")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_ai_fallback.py` around lines 382 - 418, The `test_get_ai_cli_status_reports_candidates` body in `tests/test_ai_fallback.py` accidentally includes two unrelated assertions, so split the `_run_ai_command` stdin/shell scenario into its own test method with a proper `def test_...(self):` boundary. Keep the existing `test_get_ai_cli_status_reports_candidates` focused on `get_ai_cli_status()` and move the `subprocess.run` mock, temporary file setup, and `cs._run_ai_command` assertions into a separate test near it so each test covers one behavior.
🧹 Nitpick comments (5)
backend/services/claude_suggest.py (1)
92-113: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winRepeated npm subprocess spawns per status call add latency.
_npm_global_bin_dirs()is invoked multiple times during a singleget_ai_cli_status()(once per engine via_find_cli, plus again forsearched_dirs), and each invocation spawns twonpmprocesses with a 2s timeout apiece. Combined with thesh -lc/wherefallback,podcli info,env list, and the/api/ai-cli-statusendpoint can block for several seconds. Memoizing the result (e.g.functools.lru_cache) would remove the redundancy without behavior change.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/services/claude_suggest.py` around lines 92 - 113, The `_npm_global_bin_dirs()` helper is spawning repeated npm processes on every `get_ai_cli_status()` path, adding unnecessary latency through `_find_cli` and `searched_dirs` lookups. Memoize `_npm_global_bin_dirs()` so the npm prefix/bin discovery runs once per process instead of once per call, while preserving the existing return values and fallback behavior. Keep the change localized to the helper and any direct callers such as `_find_cli` and `get_ai_cli_status()`.backend/services/env_settings.py (2)
152-160: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winConsider caching
get_ai_cli_status()results.
run_env_action("list")recomputes AI CLI status (dir scans + subprocess calls to npm, possibly a login-shell fallback) on every invocation. This is called on every Config page load and everypodcli envrun; a short-lived cache would avoid repeated subprocess spawns without materially harming freshness.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/services/env_settings.py` around lines 152 - 160, The run_env_action("list") path in env_settings.py recalculates AI CLI status on every call via get_ai_cli_status(), which is expensive due to scans and subprocess checks. Add a short-lived cache around get_ai_cli_status() (for example in claude_suggest or alongside run_env_action) and have run_env_action reuse the cached value for the ai_cli field while preserving the existing list_settings() and path behavior.
137-142: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winValidate the resolved path is executable, not just a file.
_resolve_cli_path/the fallback only checkos.path.isfile; a non-executable file (missing +x on POSIX) would pass validation here but fail at actual CLI invocation time, producing a confusing runtime error instead of an immediate, actionable one.♻️ Proposed fix
if key in ("PODCLI_CLAUDE_PATH", "PODCLI_CODEX_PATH"): from services.claude_suggest import _resolve_cli_path resolved = _resolve_cli_path(value) or (value if os.path.isfile(value) else None) if not resolved: raise ValueError(f"path does not exist: {value}") + if os.name != "nt" and not os.access(resolved, os.X_OK): + raise ValueError(f"path is not executable: {resolved}") value = resolved🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/services/env_settings.py` around lines 137 - 142, The path validation in env_settings.py for PODCLI_CLAUDE_PATH and PODCLI_CODEX_PATH only checks that the resolved value is a file, so non-executable files can slip through. Update the validation around _resolve_cli_path and the fallback in the settings parsing logic to require an executable path, not just os.path.isfile, and keep raising a ValueError when the resolved CLI path is not usable.src/ui/client/ConfigPage.tsx (2)
171-233: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider surfacing
searched_dirsin the "not detected" message.The backend's
get_ai_cli_status()also returnssearched_dirs, useful diagnostic info for the exact troubleshooting scenario this PR targets (users with Claude Code installed but not discovered). Currently unused in the UI.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/ui/client/ConfigPage.tsx` around lines 171 - 233, The AI CLI status UI in ConfigPage should surface the backend-provided searched_dirs when aiCli.available is false so users can see where detection looked. Update the not-detected branch in ConfigPage’s AI CLI section to include searched_dirs from get_ai_cli_status(), using the existing aiCli object and its candidate/status rendering as the integration point. Keep the message concise but show the searched directories in a readable diagnostic format.
42-98: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winRedundant AI CLI status refresh on every save/clear, even for unrelated secrets.
saveSetting/clearSettingcallloadSettings()(which already refreshesaiClivia/settings) and then unconditionally callrefreshAiCli()again, doubling the backend AI CLI status computation. This also fires for secret-only saves likeHF_TOKEN, which have no bearing on AI CLI detection. Givenget_ai_cli_status()does directory scans and subprocess calls (npm, login-shell fallback), this adds avoidable latency/process spawns on every settings action.♻️ Proposed fix
async function saveSetting(key: string, secret: boolean) { setSavingKey(key); setMsg(null); const value = secret ? (secretInputs[key] ?? "") : (pathInputs[key] ?? ""); try { await api("/settings", { method: "POST", body: JSON.stringify({ key, value }) }); if (secret) { setSecretInputs((p) => ({ ...p, [key]: "" })); } else { setPathInputs((p) => ({ ...p, [key]: "" })); } setMsg({ text: `${key} saved.`, ok: true }); await loadSettings(); - await refreshAiCli(); + if (secret) return; // secrets don't affect AI CLI detection } catch (e: any) { setMsg({ text: e.message, ok: false }); } finally { setSavingKey(null); } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/ui/client/ConfigPage.tsx` around lines 42 - 98, The save/clear flow is refreshing AI CLI status twice and doing it even for settings that do not affect AI CLI detection. In ConfigPage, update saveSetting and clearSetting so they rely on loadSettings() for the ai_cli refresh and remove the extra refreshAiCli() call unless the changed key truly impacts AI CLI status. Keep the behavior aligned with loadSettings and refreshAiCli by referencing those functions directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/services/claude_suggest.py`:
- Around line 92-113: The `_npm_global_bin_dirs` helper is using bare `npm` and
the deprecated `npm bin -g` path, which can miss the Windows `npm.cmd` launcher
and fail on newer npm versions. Update this function to resolve the npm
executable first with `shutil.which("npm")` (or `npm.cmd` on Windows) and use
that resolved path for the subprocess calls, while removing or replacing the
`npm bin -g` probe so global bin detection works reliably across platforms and
npm versions.
In `@tests/test_ai_fallback.py`:
- Around line 372-380: The test `test_find_cli_falls_back_to_shell_lookup` is
not fully isolated from the real environment because `_find_cli` can still
resolve a real binary through `os.environ["PATH"]` via `_resolve_cli_path`.
Update this test to clear or override PATH within the test setup so
`_find_cli("claude", [])` cannot find any real `claude` executable and is forced
to use the mocked `cs._shell_lookup`/`shutil.which` path.
---
Outside diff comments:
In `@tests/test_ai_fallback.py`:
- Around line 382-418: The `test_get_ai_cli_status_reports_candidates` body in
`tests/test_ai_fallback.py` accidentally includes two unrelated assertions, so
split the `_run_ai_command` stdin/shell scenario into its own test method with a
proper `def test_...(self):` boundary. Keep the existing
`test_get_ai_cli_status_reports_candidates` focused on `get_ai_cli_status()` and
move the `subprocess.run` mock, temporary file setup, and `cs._run_ai_command`
assertions into a separate test near it so each test covers one behavior.
---
Nitpick comments:
In `@backend/services/claude_suggest.py`:
- Around line 92-113: The `_npm_global_bin_dirs()` helper is spawning repeated
npm processes on every `get_ai_cli_status()` path, adding unnecessary latency
through `_find_cli` and `searched_dirs` lookups. Memoize
`_npm_global_bin_dirs()` so the npm prefix/bin discovery runs once per process
instead of once per call, while preserving the existing return values and
fallback behavior. Keep the change localized to the helper and any direct
callers such as `_find_cli` and `get_ai_cli_status()`.
In `@backend/services/env_settings.py`:
- Around line 152-160: The run_env_action("list") path in env_settings.py
recalculates AI CLI status on every call via get_ai_cli_status(), which is
expensive due to scans and subprocess checks. Add a short-lived cache around
get_ai_cli_status() (for example in claude_suggest or alongside run_env_action)
and have run_env_action reuse the cached value for the ai_cli field while
preserving the existing list_settings() and path behavior.
- Around line 137-142: The path validation in env_settings.py for
PODCLI_CLAUDE_PATH and PODCLI_CODEX_PATH only checks that the resolved value is
a file, so non-executable files can slip through. Update the validation around
_resolve_cli_path and the fallback in the settings parsing logic to require an
executable path, not just os.path.isfile, and keep raising a ValueError when the
resolved CLI path is not usable.
In `@src/ui/client/ConfigPage.tsx`:
- Around line 171-233: The AI CLI status UI in ConfigPage should surface the
backend-provided searched_dirs when aiCli.available is false so users can see
where detection looked. Update the not-detected branch in ConfigPage’s AI CLI
section to include searched_dirs from get_ai_cli_status(), using the existing
aiCli object and its candidate/status rendering as the integration point. Keep
the message concise but show the searched directories in a readable diagnostic
format.
- Around line 42-98: The save/clear flow is refreshing AI CLI status twice and
doing it even for settings that do not affect AI CLI detection. In ConfigPage,
update saveSetting and clearSetting so they rely on loadSettings() for the
ai_cli refresh and remove the extra refreshAiCli() call unless the changed key
truly impacts AI CLI status. Keep the behavior aligned with loadSettings and
refreshAiCli by referencing those functions directly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2fc88f09-9724-43df-8786-b1450472d746
📒 Files selected for processing (11)
backend/cli.pybackend/main.pybackend/services/claude_suggest.pybackend/services/env_settings.pysrc/handlers/integrations.handler.tssrc/models/index.tssrc/server.tssrc/ui/client/ConfigPage.tsxsrc/ui/web-server.tstests/test_ai_fallback.pytests/test_env_settings.py
✅ Files skipped from review due to trivial changes (1)
- src/server.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- backend/main.py
974c9fb to
5b91712
Compare
Broaden Claude/Codex auto-discovery across version managers (nvm, fnm, volta, asdf, mise, rtx, bun), npm/pnpm/yarn global bins, npmrc prefix, system paths, scoop/winget shims, login-shell lookup, and limited home globs. Manual override via PODCLI_CLAUDE_PATH / PODCLI_CODEX_PATH in Studio Config, podcli env, and manage_env MCP. Pipe claude prompts via stdin instead of cat|claude on Windows. Split missing-CLI vs runtime failure errors. Add ai_cli_status diagnostics. Co-authored-by: Nika Siradze <siradze@nikusha.me>
5b91712 to
87881ab
Compare
Summary
Fixes "No AI CLI available" when Claude Code or Codex is installed but not found or fails on Windows.
Changes (isolated patch)
Auto-discovery (layered)
.env(PODCLI_CLAUDE_PATH,PODCLI_CODEX_PATH)~/.local/bin, legacy~/.claude/local, npm-global, Homebrew, nvm/fnm/volta/asdf/mise/rtx/bun,/usr/bin,/snap/bin, scoop/winget shimsnpm root -g.binprefix=from~/.npmrccommand -v/type -a, Windowswhere+ PowerShellGet-CommandManual override (all surfaces)
podcli env set PODCLI_CLAUDE_PATH /path/to/claudemanage_env(action=set, key=PODCLI_CLAUDE_PATH, value=...)Runtime fix
cat | claudewhich breaks on Windows)Diagnostics
podcli env list,podcli info,ai_cli_statusMCP toolTesting
27 tests pass. CI green.
Summary by CodeRabbit
New Features
Bug Fixes
Tests