77import threading
88import time
99
10+ import requests
1011import typer
12+ from rich import box
13+ from rich .panel import Panel
14+
15+
16+ from codegen .cli .api .endpoints import API_ENDPOINT
17+ from codegen .cli .auth .token_manager import get_current_token
1118from codegen .cli .commands .claude .claude_log_watcher import ClaudeLogWatcherManager
1219from codegen .cli .commands .claude .claude_session_api import end_claude_session , generate_session_id
1320from codegen .cli .commands .claude .config .mcp_setup import add_codegen_mcp_server , cleanup_codegen_mcp_server
1421from codegen .cli .commands .claude .hooks import cleanup_claude_hook , ensure_claude_hook , get_codegen_url
1522from codegen .cli .commands .claude .quiet_console import console
23+ from rich .console import Console
24+
25+ t_console = Console ()
26+
27+ from codegen .cli .rich .spinners import create_spinner
1628from codegen .cli .utils .org import resolve_org_id
1729
1830
19- def claude (
20- org_id : int | None = typer .Option (None , help = "Organization ID (defaults to CODEGEN_ORG_ID/REPOSITORY_ORG_ID or auto-detect)" ),
21- no_mcp : bool | None = typer .Option (False , "--no-mcp" , help = "Disable Codegen's MCP server with additional capabilities over HTTP" ),
22- ):
23- """Run Claude Code with session tracking.
31+ def _run_claude_background (resolved_org_id : int , prompt : str | None ) -> None :
32+ """Create a background agent run with Claude context and exit."""
33+ token = get_current_token ()
34+ if not token :
35+ console .print ("[red]Error:[/red] Not authenticated. Please run 'codegen login' first." )
36+ raise typer .Exit (1 )
37+
38+ payload = {"prompt" : prompt or "Start a Claude Code background session" }
2439
25- This command runs Claude Code and tracks the session in the backend API:
26- - Generates a unique session ID
27- - Creates an agent run when Claude starts
28- - Updates the agent run status when Claude exits
29- """
40+ spinner = create_spinner ("Creating agent run..." )
41+ spinner .start ()
42+ try :
43+ headers = {
44+ "Authorization" : f"Bearer { token } " ,
45+ "Content-Type" : "application/json" ,
46+ "x-codegen-client" : "codegen__claude_code" ,
47+ }
48+ url = f"{ API_ENDPOINT .rstrip ('/' )} /v1/organizations/{ resolved_org_id } /agent/run"
49+ response = requests .post (url , headers = headers , json = payload )
50+ response .raise_for_status ()
51+ agent_run_data = response .json ()
52+ finally :
53+ spinner .stop ()
54+
55+ run_id = agent_run_data .get ("id" , "Unknown" )
56+ status = agent_run_data .get ("status" , "Unknown" )
57+ web_url = agent_run_data .get ("web_url" , "" )
58+
59+ result_lines = [
60+ f"[cyan]Agent Run ID:[/cyan] { run_id } " ,
61+ f"[cyan]Status:[/cyan] { status } " ,
62+ ]
63+ if web_url :
64+ result_lines .append (f"[cyan]Web URL:[/cyan] { web_url } " )
65+
66+ t_console .print (
67+ Panel (
68+ "\n " .join (result_lines ),
69+ title = "🤖 [bold]Background Agent Run Created[/bold]" ,
70+ border_style = "green" ,
71+ box = box .ROUNDED ,
72+ padding = (1 , 2 ),
73+ )
74+ )
75+ t_console .print ("\n [dim]💡 Track progress with:[/dim] [cyan]codegen agents[/cyan]" )
76+ if web_url :
77+ t_console .print (f"[dim]🌐 View in browser:[/dim] [link]{ web_url } [/link]" )
78+
79+
80+ def _run_claude_interactive (resolved_org_id : int , no_mcp : bool | None ) -> None :
81+ """Launch Claude Code with session tracking and log watching."""
3082 # Generate session ID for tracking
3183 session_id = generate_session_id ()
3284 console .print (f"🆔 Generated session ID: { session_id [:8 ]} ..." , style = "dim" )
33-
34- # Resolve org_id early for session management
35- resolved_org_id = resolve_org_id (org_id )
36- if resolved_org_id is None :
37- console .print ("[red]Error:[/red] Organization ID not provided. Pass --org-id, set CODEGEN_ORG_ID, or REPOSITORY_ORG_ID." )
38- raise typer .Exit (1 )
39-
85+
4086 console .print ("🚀 Starting Claude Code with session tracking..." , style = "blue" )
4187 console .print (f"🎯 Organization ID: { resolved_org_id } " , style = "dim" )
4288
@@ -79,29 +125,27 @@ def claude(
79125 url = get_codegen_url (session_id )
80126 console .print (f"\n 🔵 Codegen URL: { url } \n " , style = "bold blue" )
81127
82-
83128 process = subprocess .Popen (["claude" , "--session-id" , session_id ])
84129
85-
86130 # Start log watcher for the session
87131 console .print ("📋 Starting log watcher..." , style = "blue" )
88132 log_watcher_started = log_watcher_manager .start_watcher (
89133 session_id = session_id ,
90134 org_id = resolved_org_id ,
91- poll_interval = 1.0 , # Check every second
92- on_log_entry = None
135+ poll_interval = 1.0 ,
136+ on_log_entry = None ,
93137 )
94-
138+
95139 if not log_watcher_started :
96140 console .print ("⚠️ Failed to start log watcher" , style = "yellow" )
97141
98142 # Handle Ctrl+C gracefully
99143 def signal_handler (signum , frame ):
100144 console .print ("\n 🛑 Stopping Claude Code..." , style = "yellow" )
101- log_watcher_manager .stop_all_watchers () # Stop log watchers
145+ log_watcher_manager .stop_all_watchers ()
102146 process .terminate ()
103- cleanup_claude_hook () # Clean up our hook
104- cleanup_codegen_mcp_server () # Clean up MCP Server
147+ cleanup_claude_hook ()
148+ cleanup_codegen_mcp_server ()
105149 end_claude_session (session_id , "ERROR" , resolved_org_id )
106150 sys .exit (0 )
107151
@@ -140,12 +184,33 @@ def signal_handler(signum, frame):
140184 log_watcher_manager .stop_all_watchers ()
141185 except Exception as e :
142186 console .print (f"⚠️ Error stopping log watchers: { e } " , style = "yellow" )
143-
187+
144188 cleanup_claude_hook ()
145189
146190 # Show final session info
147191 url = get_codegen_url (session_id )
148192 console .print (f"\n 🔵 Session URL: { url } " , style = "bold blue" )
149193 console .print (f"🆔 Session ID: { session_id } " , style = "dim" )
150194 console .print (f"🎯 Organization ID: { resolved_org_id } " , style = "dim" )
151- console .print ("💡 Check your backend to see the session data" , style = "dim" )
195+ console .print ("💡 Check your backend to see the session data" , style = "dim" )
196+
197+
198+ def claude (
199+ org_id : int | None = typer .Option (None , help = "Organization ID (defaults to CODEGEN_ORG_ID/REPOSITORY_ORG_ID or auto-detect)" ),
200+ no_mcp : bool | None = typer .Option (False , "--no-mcp" , help = "Disable Codegen's MCP server with additional capabilities over HTTP" ),
201+ background : str | None = typer .Option (None , "--background" , "-b" , help = "Create a background agent run with this prompt instead of launching Claude Code" ),
202+ ):
203+ """Run Claude Code with session tracking or create a background run."""
204+ # Resolve org_id early for session management
205+ resolved_org_id = resolve_org_id (org_id )
206+ if resolved_org_id is None :
207+ console .print ("[red]Error:[/red] Organization ID not provided. Pass --org-id, set CODEGEN_ORG_ID, or REPOSITORY_ORG_ID." )
208+ raise typer .Exit (1 )
209+
210+ if background is not None :
211+ # Use the value from --background as the prompt, with --prompt as fallback
212+ final_prompt = background or prompt
213+ _run_claude_background (resolved_org_id , final_prompt )
214+ return
215+
216+ _run_claude_interactive (resolved_org_id , no_mcp )
0 commit comments