diff --git a/src/codegen/cli/commands/claude/claude_session_api.py b/src/codegen/cli/commands/claude/claude_session_api.py index 6ff5e5566..d737c494a 100644 --- a/src/codegen/cli/commands/claude/claude_session_api.py +++ b/src/codegen/cli/commands/claude/claude_session_api.py @@ -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.""" @@ -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. diff --git a/src/codegen/cli/commands/claude/main.py b/src/codegen/cli/commands/claude/main.py index 9f5b58d35..9a5239ec5 100644 --- a/src/codegen/cli/commands/claude/main.py +++ b/src/codegen/cli/commands/claude/main.py @@ -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 @@ -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") @@ -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()}, )