Skip to content

Conversation

@him0
Copy link
Contributor

@him0him0 commented Nov 18, 2025

Problem

When users define claude as a shell function (e.g., a wrapper around ~/.claude/local/claude), the Claude adapter fails with "Claude Code not found" error. This happens because shell functions are not inherited by subshells.

Users often wrap the Claude CLI in a shell function to set environment variables (like HTTP headers for authentication) or customize the execution environment:

claude (){( export ANTHROPIC_API_KEY="your-key"~/.claude/local/claude "$@" ) }

Solution

This PR adds a find_claude_executable() helper function that:

  1. Checks common installation paths first - Directly tests ~/.claude/local/claude (the typical installation location)
  2. Uses type -P to find executable files only - Excludes shell functions and aliases (Bash/Zsh compatible)
  3. Provides fallback logic - Uses command -v with function detection for compatibility

Both ai_can_start() and ai_start() now use this helper to find the actual executable path, ensuring the adapter works correctly even when claude is defined as a shell function.

Testing

Tested with claude defined as a shell function. The adapter now correctly:

  • Detects the executable at ~/.claude/local/claude
  • Executes it successfully in the worktree directory
  • No longer shows "Claude Code not found" error

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores

    • Improved the tool's executable detection and startup logic for more reliable initialization across environments.
  • Bug Fixes

    • Unified startup path to avoid runtime branching, reducing startup errors.
    • Added early validation and clearer user-facing error messages when the required executable cannot be found.

When `claude` is defined as a shell function (e.g., wrapping ~/.claude/local/claude), the previous implementation failed because shell functions are not inherited by subshells. Changes: - Add `find_claude_executable()` helper to detect executable files only - Check common installation path (~/.claude/local/claude) first - Use `type -P` to find executables, excluding shell functions - Both `ai_can_start()` and `ai_start()` now use the executable path directly This allows the adapter to work correctly even when users have `claude` defined as a shell function that wraps the actual executable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
@him0him0 requested a review from NatoBoram as a code ownerNovember 18, 2025 16:08
@coderabbitai
Copy link

coderabbitaibot commented Nov 18, 2025

📝 Walkthrough

Walkthrough

Refactors the Claude adapter by adding find_claude_executable() to centralize executable detection; ai_can_start() and ai_start() now use this resolver, removing runtime branching and adding early error handling when the Claude command is not found.

Changes

Cohort / File(s)Summary
Claude adapter refactor
adapters/ai/claude.sh
Added find_claude_executable() to locate claude/claude-code via local path, type -P, and fallback checks. Updated ai_can_start() to delegate availability to the new function. Updated ai_start() to resolve and validate the executable up front, execute the resolved command, and emit an early error if not found; removed prior runtime branching.

Sequence Diagram

sequenceDiagram participant Caller participant Finder as find_claude_executable() participant ai_can_start participant ai_start participant System Caller->>Finder: Locate Claude executable Finder->>System: Check local path alt found Finder-->>Caller: return path else not found Finder->>System: type -P check alt found Finder-->>Caller: return path else fallback Finder->>System: other type checks Finder-->>Caller: return path or empty end end Caller->>ai_can_start: Is Claude available? ai_can_start->>Finder: resolve executable ai_can_start-->>Caller: availability (true/false) Caller->>ai_start: start Claude in target dir ai_start->>Finder: resolve executable alt executable resolved ai_start->>System: exec resolved command in target dir System-->>Caller: started else not resolved ai_start-->>Caller: error: claude command not found end 
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

  • Single-file change with clear, localizable logic.
  • Pay attention to shell edge-cases and exit/status propagation in ai_start() and find_claude_executable().

Poem

🐰 I sniff the PATH and hop with cheer,
One finder now keeps the commands near.
No branching maze, just one clear way,
I bound to run Claude — hip hip hooray!

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title directly and accurately summarizes the main change: refactoring the Claude adapter to support shell function definitions by introducing a helper function that locates the actual executable.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitaicoderabbitaibot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
adapters/ai/claude.sh (2)

15-24: Remove the redundant shell availability check on line 17.

type is a bash builtin and will always be available in this bash script, making if command -v type >/dev/null 2>&1; ineffective and confusing. The check always succeeds, adding unnecessary overhead.

Apply this diff to simplify:

 # Try to find executable using type -P (Bash/Zsh) for cmd in claude claude-code; do - if command -v type >/dev/null 2>&1; then- local exe_path- exe_path="$(type -P "$cmd" 2>/dev/null)" && [ -n "$exe_path" ] &&{- echo "$exe_path"- return 0- }- fi+ local exe_path+ exe_path="$(type -P "$cmd" 2>/dev/null)" && [ -n "$exe_path" ] &&{+ echo "$exe_path"+ return 0+ } done

26-34: Fallback logic may accept unintended command types (aliases, builtins).

The pattern grep -qv "function" correctly excludes shell functions but has loose filtering. The condition matches any type output that doesn't contain the word "function"—this includes aliases (aliased to), builtins, and executables. If claude is aliased to something else in a user's shell, this fallback would incorrectly accept it. Consider a more explicit check, such as verifying that the resolved command is actually an executable file.

For robustness, prefer using type -P (which only returns executables) or explicitly verify the command is a file path:

 # Fallback: use command -v but exclude functions for cmd in claude claude-code; do - if type "$cmd" 2>/dev/null | grep -qv "function"; then- if command -v "$cmd" >/dev/null 2>&1; then- echo "$cmd"- return 0- fi- fi+ if ! type "$cmd" 2>/dev/null | grep -q "^.*is a function"; then+ if [ -x "$(command -v "$cmd")" ]; then+ echo "$(command -v "$cmd")"+ return 0+ fi+ fi done

This ensures we only accept executables (files with execute permission) rather than aliases or builtins.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fae9160 and 3a68387.

📒 Files selected for processing (1)
  • adapters/ai/claude.sh (3 hunks)
🔇 Additional comments (1)
adapters/ai/claude.sh (1)

40-65: Updated functions correctly resolve and use the Claude executable.

The refactoring successfully centralizes executable detection in find_claude_executable(). The primary fix (checking ~/.claude/local/claude directly) correctly addresses the PR objective: when claude is a shell function, the subshell spawned on line 64 will inherit and execute the resolved executable path, not the function definition.

  • Line 41: ai_can_start() delegation is idiomatic and correct.
  • Lines 50–57: Defensive check for empty claude_cmd is good practice.
  • Line 64: Subshell execution with "$claude_cmd" "$@" works correctly regardless of whether the command is a full path or a searchable name.

fi

# Try to find executable using type -P (Bash/Zsh)
forcmdin claude claude-code;do
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code checks if type exists before using it, but type is a Bash builtin that's always available in bash scripts.


# Fallback: use command -v but exclude functions
forcmdin claude claude-code;do
iftype"$cmd"2>/dev/null | grep -qv "function";then
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback tier uses grep -qv "function" which matches anything that doesn't contain the word "function", including aliases

- Remove unnecessary check for 'type' builtin (always available in Bash) - Replace grep-based function filtering with 'type -t' for accurate detection - Now properly excludes both shell functions and aliases - Only detects actual executable files This addresses PR review feedback about redundant checks and potential false positives when aliases are present. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Copy link

@coderabbitaicoderabbitaibot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
adapters/ai/claude.sh (1)

4-33: Well-structured multi-tier resolution strategy to bypass shell functions.

The function correctly distinguishes between executables and shell functions by:

  1. Testing an explicit common install path first
  2. Using type -P to locate only actual executables (functions/aliases excluded)
  3. Adding a defensive fallback that verifies type -t = "file" before invoking command -v

This addresses the PR objective of supporting claude defined as a shell function, since type -P and the explicit path check bypass function definitions entirely, while Strategy 3 confirms any fallback match is an actual file, not a function.

Consider: Strategy 3 (lines 25–30) may be redundant—if type -P doesn't find an executable in PATH, the type -t = "file" check is unlikely to succeed either. If this tier never executes in practice, you could simplify by removing it (or keeping it defensively). Verify whether your test suite or real deployments actually hit this fallback.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3a68387 and a46631a.

📒 Files selected for processing (1)
  • adapters/ai/claude.sh (3 hunks)
🔇 Additional comments (2)
adapters/ai/claude.sh (2)

36-38: Clean delegation to helper.

Straightforward use of the new helper to check availability.


42-61: Robust error handling and clean execution pattern.

The function correctly:

  • Captures the resolved executable path upfront (lines 46–47)
  • Validates non-empty result with an informative error message (lines 49–53)
  • Executes in a subshell (line 60) to isolate the working directory change and prevent function-pollution

@helizagahelizaga merged commit b26f67c into coderabbitai:mainNov 21, 2025
1 check passed
@him0
Copy link
ContributorAuthor

him0 commented Nov 22, 2025

Thank you for your review.

@him0him0 deleted the fix/claude-shell-function-support branch November 22, 2025 07:36
Sign up for freeto 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.

2 participants

@him0@helizaga