Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions src/codegen/cli/commands/claude/config/mcp_setup.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@

import subprocess

from codegen.cli.api.endpoints import MCP_SERVER_ENDPOINT
from codegen.cli.commands.claude.quiet_console import console
from codegen.cli.auth.token_manager import get_current_token
from codegen.cli.commands.claude.quiet_console import console
from codegen.cli.commands.claude.utils import resolve_claude_path


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

claude_path = resolve_claude_path()
if not claude_path:
console.print("⚠️ 'claude' CLI not found to add MCP server", style="yellow")
return

add_result = subprocess.run(
[
"claude",
claude_path,
"mcp",
"add",
"--transport",
Expand Down Expand Up @@ -45,13 +50,18 @@ def add_codegen_mcp_server():

def cleanup_codegen_mcp_server():
try:
claude_path = resolve_claude_path()
if not claude_path:
# Silently skip if claude is not found during cleanup
return

subprocess.run(
[
"claude",
claude_path,
"mcp",
"remove",
"codegen-tools",
],
)
except Exception as e:
console.print(f"⚠️ Error removing MCP server: {e}", style="yellow")
console.print(f"⚠️ Error removing MCP server: {e}", style="yellow")
29 changes: 22 additions & 7 deletions src/codegen/cli/commands/claude/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@
from codegen.cli.auth.token_manager import get_current_token
from codegen.cli.commands.claude.claude_log_watcher import ClaudeLogWatcherManager
from codegen.cli.commands.claude.claude_session_api import (
update_claude_session_status,
generate_session_id,
create_claude_session,
generate_session_id,
update_claude_session_status,
)
from codegen.cli.commands.claude.config.mcp_setup import add_codegen_mcp_server, cleanup_codegen_mcp_server
from codegen.cli.commands.claude.hooks import cleanup_claude_hook, ensure_claude_hook, get_codegen_url, SESSION_FILE
from codegen.cli.commands.claude.hooks import SESSION_FILE, cleanup_claude_hook, ensure_claude_hook, get_codegen_url
from codegen.cli.commands.claude.quiet_console import console
from codegen.cli.commands.claude.utils import resolve_claude_path
from codegen.cli.rich.spinners import create_spinner
from codegen.cli.utils.org import resolve_org_id

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

# Test if Claude Code is accessible first
console.print("🔍 Testing Claude Code accessibility...", style="blue")
# Resolve Claude CLI path and test accessibility
claude_path = resolve_claude_path()
if not claude_path:
console.print("❌ Claude Code CLI not found.", style="red")
console.print(
"💡 If you migrated a local install, ensure `~/.claude/local/claude` exists, or add it to PATH.",
style="dim",
)
console.print(
"💡 Otherwise install globally via npm (e.g., `npm i -g claude`) or run `claude /migrate`.",
style="dim",
)
update_claude_session_status(session_id, "ERROR", resolved_org_id)
raise typer.Exit(1)

console.print(f"🔍 Using Claude CLI at: {claude_path}", style="blue")
try:
test_result = subprocess.run(["claude", "--version"], capture_output=True, text=True, timeout=10)
test_result = subprocess.run([claude_path, "--version"], capture_output=True, text=True, timeout=10)
if test_result.returncode == 0:
console.print(f"✅ Claude Code found: {test_result.stdout.strip()}", style="green")
else:
Expand All @@ -153,7 +168,7 @@ def _run_claude_interactive(resolved_org_id: int, no_mcp: bool | None) -> None:
url = get_codegen_url(session_id)
console.print(f"\n🔵 Codegen URL: {url}\n", style="bold blue")

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

# Start log watcher for the session
console.print("📋 Starting log watcher...", style="blue")
Expand Down
39 changes: 39 additions & 0 deletions src/codegen/cli/commands/claude/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""Utility functions for Claude CLI integration."""

import os
from shutil import which


def resolve_claude_path() -> str | None:
"""Resolve the path to the Claude Code CLI.

Tries PATH first, then common local install locations created by `claude /migrate`.

Returns:
Path to the claude executable if found, None otherwise.
"""
# 1) Check system PATH first
path_from_path = which("claude")
if path_from_path:
return path_from_path

# 2) Check common local install locations
home = os.path.expanduser("~")
candidates = [
# Local install created by `claude /migrate`
os.path.join(home, ".claude", "local", "claude"),
os.path.join(home, ".claude", "local", "node_modules", ".bin", "claude"),
# Common global install locations
"/usr/local/bin/claude",
"/opt/homebrew/bin/claude", # Homebrew on Apple Silicon
]

for candidate in candidates:
try:
if os.path.isfile(candidate) and os.access(candidate, os.X_OK):
return candidate
except Exception:
# Best-effort checks only; ignore filesystem errors
pass

return None