Skip to content

Commit 26d522e

Browse files
authored
fix: update path resolution (#1232)
# Motivation <!-- Why is this change necessary? --> # Content <!-- Please include a summary of the change --> # Testing <!-- How was the change tested? --> # Please check the following before marking your PR as ready for review - [ ] I have added tests for my changes - [ ] I have updated the documentation or added new documentation as needed
1 parent 01341c6 commit 26d522e

File tree

3 files changed

+76
-12
lines changed

3 files changed

+76
-12
lines changed

src/codegen/cli/commands/claude/config/mcp_setup.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
21
import subprocess
32

43
from codegen.cli.api.endpoints import MCP_SERVER_ENDPOINT
5-
from codegen.cli.commands.claude.quiet_console import console
64
from codegen.cli.auth.token_manager import get_current_token
5+
from codegen.cli.commands.claude.quiet_console import console
6+
from codegen.cli.commands.claude.utils import resolve_claude_path
77

88

99
def add_codegen_mcp_server():
@@ -14,9 +14,14 @@ def add_codegen_mcp_server():
1414
console.print("⚠️ No authentication token found. Please run 'codegen login' first.", style="yellow")
1515
return
1616

17+
claude_path = resolve_claude_path()
18+
if not claude_path:
19+
console.print("⚠️ 'claude' CLI not found to add MCP server", style="yellow")
20+
return
21+
1722
add_result = subprocess.run(
1823
[
19-
"claude",
24+
claude_path,
2025
"mcp",
2126
"add",
2227
"--transport",
@@ -45,13 +50,18 @@ def add_codegen_mcp_server():
4550

4651
def cleanup_codegen_mcp_server():
4752
try:
53+
claude_path = resolve_claude_path()
54+
if not claude_path:
55+
# Silently skip if claude is not found during cleanup
56+
return
57+
4858
subprocess.run(
4959
[
50-
"claude",
60+
claude_path,
5161
"mcp",
5262
"remove",
5363
"codegen-tools",
5464
],
5565
)
5666
except Exception as e:
57-
console.print(f"⚠️ Error removing MCP server: {e}", style="yellow")
67+
console.print(f"⚠️ Error removing MCP server: {e}", style="yellow")

src/codegen/cli/commands/claude/main.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@
1616
from codegen.cli.auth.token_manager import get_current_token
1717
from codegen.cli.commands.claude.claude_log_watcher import ClaudeLogWatcherManager
1818
from codegen.cli.commands.claude.claude_session_api import (
19-
update_claude_session_status,
20-
generate_session_id,
2119
create_claude_session,
20+
generate_session_id,
21+
update_claude_session_status,
2222
)
2323
from codegen.cli.commands.claude.config.mcp_setup import add_codegen_mcp_server, cleanup_codegen_mcp_server
24-
from codegen.cli.commands.claude.hooks import cleanup_claude_hook, ensure_claude_hook, get_codegen_url, SESSION_FILE
24+
from codegen.cli.commands.claude.hooks import SESSION_FILE, cleanup_claude_hook, ensure_claude_hook, get_codegen_url
2525
from codegen.cli.commands.claude.quiet_console import console
26+
from codegen.cli.commands.claude.utils import resolve_claude_path
2627
from codegen.cli.rich.spinners import create_spinner
2728
from codegen.cli.utils.org import resolve_org_id
2829

@@ -125,10 +126,24 @@ def _run_claude_interactive(resolved_org_id: int, no_mcp: bool | None) -> None:
125126
# Initialize log watcher manager
126127
log_watcher_manager = ClaudeLogWatcherManager()
127128

128-
# Test if Claude Code is accessible first
129-
console.print("🔍 Testing Claude Code accessibility...", style="blue")
129+
# Resolve Claude CLI path and test accessibility
130+
claude_path = resolve_claude_path()
131+
if not claude_path:
132+
console.print("❌ Claude Code CLI not found.", style="red")
133+
console.print(
134+
"💡 If you migrated a local install, ensure `~/.claude/local/claude` exists, or add it to PATH.",
135+
style="dim",
136+
)
137+
console.print(
138+
"💡 Otherwise install globally via npm (e.g., `npm i -g claude`) or run `claude /migrate`.",
139+
style="dim",
140+
)
141+
update_claude_session_status(session_id, "ERROR", resolved_org_id)
142+
raise typer.Exit(1)
143+
144+
console.print(f"🔍 Using Claude CLI at: {claude_path}", style="blue")
130145
try:
131-
test_result = subprocess.run(["claude", "--version"], capture_output=True, text=True, timeout=10)
146+
test_result = subprocess.run([claude_path, "--version"], capture_output=True, text=True, timeout=10)
132147
if test_result.returncode == 0:
133148
console.print(f"✅ Claude Code found: {test_result.stdout.strip()}", style="green")
134149
else:
@@ -153,7 +168,7 @@ def _run_claude_interactive(resolved_org_id: int, no_mcp: bool | None) -> None:
153168
url = get_codegen_url(session_id)
154169
console.print(f"\n🔵 Codegen URL: {url}\n", style="bold blue")
155170

156-
process = subprocess.Popen(["claude", "--session-id", session_id])
171+
process = subprocess.Popen([claude_path, "--session-id", session_id])
157172

158173
# Start log watcher for the session
159174
console.print("📋 Starting log watcher...", style="blue")
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""Utility functions for Claude CLI integration."""
2+
3+
import os
4+
from shutil import which
5+
6+
7+
def resolve_claude_path() -> str | None:
8+
"""Resolve the path to the Claude Code CLI.
9+
10+
Tries PATH first, then common local install locations created by `claude /migrate`.
11+
12+
Returns:
13+
Path to the claude executable if found, None otherwise.
14+
"""
15+
# 1) Check system PATH first
16+
path_from_path = which("claude")
17+
if path_from_path:
18+
return path_from_path
19+
20+
# 2) Check common local install locations
21+
home = os.path.expanduser("~")
22+
candidates = [
23+
# Local install created by `claude /migrate`
24+
os.path.join(home, ".claude", "local", "claude"),
25+
os.path.join(home, ".claude", "local", "node_modules", ".bin", "claude"),
26+
# Common global install locations
27+
"/usr/local/bin/claude",
28+
"/opt/homebrew/bin/claude", # Homebrew on Apple Silicon
29+
]
30+
31+
for candidate in candidates:
32+
try:
33+
if os.path.isfile(candidate) and os.access(candidate, os.X_OK):
34+
return candidate
35+
except Exception:
36+
# Best-effort checks only; ignore filesystem errors
37+
pass
38+
39+
return None

0 commit comments

Comments
 (0)