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
58 changes: 57 additions & 1 deletion src/codegen/cli/commands/claude/claude_session_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
from typing import Optional

import requests
from .quiet_console import console

from codegen.cli.api.endpoints import API_ENDPOINT
from codegen.cli.auth.token_manager import get_current_token
from codegen.cli.utils.org import resolve_org_id

from .quiet_console import console


class ClaudeSessionAPIError(Exception):
"""Exception raised for Claude session API errors."""
Expand Down Expand Up @@ -192,6 +193,61 @@ def send_claude_session_log(session_id: str, log_entry: dict, org_id: Optional[i
return False


def get_cli_rules(org_id: Optional[int] = None) -> Optional[dict]:
"""Fetch CLI rules from the API endpoint.

Args:
org_id: Organization ID (will be resolved if None)

Returns:
Dictionary containing organization_rules and user_custom_prompt, or None if failed
"""
try:
# Resolve org_id
resolved_org_id = resolve_org_id(org_id)
if resolved_org_id is None:
console.print("⚠️ Could not resolve organization ID for CLI rules", style="yellow")
return None

# Get authentication token
token = get_current_token()
if not token:
console.print("⚠️ No authentication token found for CLI rules", style="yellow")
return None

# Prepare API request
url = f"{API_ENDPOINT.rstrip('/')}/v1/organizations/{resolved_org_id}/cli/rules"
headers = {"Authorization": f"Bearer {token}"}

# Make API request
response = requests.get(url, headers=headers, timeout=30)

if response.status_code == 200:
try:
result = response.json()
return result
except json.JSONDecodeError as e:
console.print(f"⚠️ Invalid response format from CLI rules: {e}", style="yellow")
return None
else:
error_msg = f"HTTP {response.status_code}"
try:
error_detail = response.json().get("detail", response.text)
error_msg = f"{error_msg}: {error_detail}"
except Exception:
error_msg = f"{error_msg}: {response.text}"

console.print(f"⚠️ Failed to fetch CLI rules: {error_msg}", style="yellow")
return None

except requests.RequestException as e:
console.print(f"⚠️ Network error fetching CLI rules: {e}", style="yellow")
return None
except Exception as e:
console.print(f"⚠️ Unexpected error fetching CLI rules: {e}", style="yellow")
return None


def write_session_hook_data(session_id: str, org_id: Optional[int] = None) -> str:
"""Write session data for Claude hook and create session via API.

Expand Down
38 changes: 36 additions & 2 deletions src/codegen/cli/commands/claude/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from codegen.cli.commands.claude.claude_session_api import (
create_claude_session,
generate_session_id,
get_cli_rules,
update_claude_session_status,
)
from codegen.cli.commands.claude.config.mcp_setup import add_codegen_mcp_server, cleanup_codegen_mcp_server
Expand Down Expand Up @@ -221,13 +222,46 @@ def _run_claude_interactive(resolved_org_id: int, no_mcp: bool | None) -> None:
console.print("🔵 Starting Claude Code session...", style="blue")

try:
# Fetch CLI rules for system prompt
console.print("📋 Fetching CLI rules...", style="blue")
cli_rules = get_cli_rules(resolved_org_id)

# Build Claude command
claude_cmd = [claude_path, "--session-id", session_id]

# Add system prompt if CLI rules were fetched successfully
if cli_rules:
system_prompt_parts = []

# Add organization rules if available
if cli_rules.get("organization_rules"):
system_prompt_parts.append("Organization Rules:")
system_prompt_parts.append(cli_rules["organization_rules"])

# Add user custom prompt if available
if cli_rules.get("user_custom_prompt"):
if system_prompt_parts: # Add separator if we already have org rules
system_prompt_parts.append("\n")
system_prompt_parts.append("User Custom Prompt:")
system_prompt_parts.append(cli_rules["user_custom_prompt"])

# Combine all parts into system prompt
if system_prompt_parts:
system_prompt = "\n".join(system_prompt_parts)
claude_cmd.extend(["--append-system-prompt", system_prompt])
console.print("✅ Added CLI rules to system prompt", style="green")
else:
console.print("⚠️ CLI rules response was empty", style="yellow")
else:
console.print("⚠️ Could not fetch CLI rules, continuing without system prompt", style="yellow")

# Launch Claude Code with our session ID
console.print(f"🚀 Launching Claude Code with session ID: {session_id[:8]}...", style="blue")

url = get_codegen_url(session_id)
console.print(f"\n🔵 Codegen URL: {url}\n", style="bold blue")

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

# Start log watcher for the session
console.print("📋 Starting log watcher...", style="blue")
Expand Down Expand Up @@ -288,7 +322,7 @@ def signal_handler(signum, frame):
console.print("✅ Claude Code finished successfully", style="green")

except FileNotFoundError:
logger.error(
logger.exception(
"Claude Code executable not found",
extra={"operation": "claude.interactive", "org_id": resolved_org_id, "claude_session_id": session_id, "error_type": "claude_executable_not_found", **_get_session_context()},
)
Expand Down