mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-01-30 06:12:06 +00:00
init
This commit is contained in:
213
agent.py
Normal file
213
agent.py
Normal file
@@ -0,0 +1,213 @@
|
||||
"""
|
||||
Agent Session Logic
|
||||
===================
|
||||
|
||||
Core agent interaction functions for running autonomous coding sessions.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from claude_agent_sdk import ClaudeSDKClient
|
||||
|
||||
from client import create_client
|
||||
from progress import print_session_header, print_progress_summary, has_features
|
||||
from prompts import (
|
||||
get_initializer_prompt,
|
||||
get_coding_prompt,
|
||||
copy_spec_to_project,
|
||||
has_project_prompts,
|
||||
)
|
||||
|
||||
|
||||
# Configuration
|
||||
AUTO_CONTINUE_DELAY_SECONDS = 3
|
||||
|
||||
|
||||
async def run_agent_session(
|
||||
client: ClaudeSDKClient,
|
||||
message: str,
|
||||
project_dir: Path,
|
||||
) -> tuple[str, str]:
|
||||
"""
|
||||
Run a single agent session using Claude Agent SDK.
|
||||
|
||||
Args:
|
||||
client: Claude SDK client
|
||||
message: The prompt to send
|
||||
project_dir: Project directory path
|
||||
|
||||
Returns:
|
||||
(status, response_text) where status is:
|
||||
- "continue" if agent should continue working
|
||||
- "error" if an error occurred
|
||||
"""
|
||||
print("Sending prompt to Claude Agent SDK...\n")
|
||||
|
||||
try:
|
||||
# Send the query
|
||||
await client.query(message)
|
||||
|
||||
# Collect response text and show tool use
|
||||
response_text = ""
|
||||
async for msg in client.receive_response():
|
||||
msg_type = type(msg).__name__
|
||||
|
||||
# Handle AssistantMessage (text and tool use)
|
||||
if msg_type == "AssistantMessage" and hasattr(msg, "content"):
|
||||
for block in msg.content:
|
||||
block_type = type(block).__name__
|
||||
|
||||
if block_type == "TextBlock" and hasattr(block, "text"):
|
||||
response_text += block.text
|
||||
print(block.text, end="", flush=True)
|
||||
elif block_type == "ToolUseBlock" and hasattr(block, "name"):
|
||||
print(f"\n[Tool: {block.name}]", flush=True)
|
||||
if hasattr(block, "input"):
|
||||
input_str = str(block.input)
|
||||
if len(input_str) > 200:
|
||||
print(f" Input: {input_str[:200]}...", flush=True)
|
||||
else:
|
||||
print(f" Input: {input_str}", flush=True)
|
||||
|
||||
# Handle UserMessage (tool results)
|
||||
elif msg_type == "UserMessage" and hasattr(msg, "content"):
|
||||
for block in msg.content:
|
||||
block_type = type(block).__name__
|
||||
|
||||
if block_type == "ToolResultBlock":
|
||||
result_content = getattr(block, "content", "")
|
||||
is_error = getattr(block, "is_error", False)
|
||||
|
||||
# Check if command was blocked by security hook
|
||||
if "blocked" in str(result_content).lower():
|
||||
print(f" [BLOCKED] {result_content}", flush=True)
|
||||
elif is_error:
|
||||
# Show errors (truncated)
|
||||
error_str = str(result_content)[:500]
|
||||
print(f" [Error] {error_str}", flush=True)
|
||||
else:
|
||||
# Tool succeeded - just show brief confirmation
|
||||
print(" [Done]", flush=True)
|
||||
|
||||
print("\n" + "-" * 70 + "\n")
|
||||
return "continue", response_text
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error during agent session: {e}")
|
||||
return "error", str(e)
|
||||
|
||||
|
||||
async def run_autonomous_agent(
|
||||
project_dir: Path,
|
||||
model: str,
|
||||
max_iterations: Optional[int] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Run the autonomous agent loop.
|
||||
|
||||
Args:
|
||||
project_dir: Directory for the project
|
||||
model: Claude model to use
|
||||
max_iterations: Maximum number of iterations (None for unlimited)
|
||||
"""
|
||||
print("\n" + "=" * 70)
|
||||
print(" AUTONOMOUS CODING AGENT DEMO")
|
||||
print("=" * 70)
|
||||
print(f"\nProject directory: {project_dir}")
|
||||
print(f"Model: {model}")
|
||||
if max_iterations:
|
||||
print(f"Max iterations: {max_iterations}")
|
||||
else:
|
||||
print("Max iterations: Unlimited (will run until completion)")
|
||||
print()
|
||||
|
||||
# Create project directory
|
||||
project_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Check if this is a fresh start or continuation
|
||||
# Uses has_features() which checks if the database actually has features,
|
||||
# not just if the file exists (empty db should still trigger initializer)
|
||||
is_first_run = not has_features(project_dir)
|
||||
|
||||
if is_first_run:
|
||||
print("Fresh start - will use initializer agent")
|
||||
print()
|
||||
print("=" * 70)
|
||||
print(" NOTE: First session takes 10-20+ minutes!")
|
||||
print(" The agent is generating 200 detailed test cases.")
|
||||
print(" This may appear to hang - it's working. Watch for [Tool: ...] output.")
|
||||
print("=" * 70)
|
||||
print()
|
||||
# Copy the app spec into the project directory for the agent to read
|
||||
copy_spec_to_project(project_dir)
|
||||
else:
|
||||
print("Continuing existing project")
|
||||
print_progress_summary(project_dir)
|
||||
|
||||
# Main loop
|
||||
iteration = 0
|
||||
|
||||
while True:
|
||||
iteration += 1
|
||||
|
||||
# Check max iterations
|
||||
if max_iterations and iteration > max_iterations:
|
||||
print(f"\nReached max iterations ({max_iterations})")
|
||||
print("To continue, run the script again without --max-iterations")
|
||||
break
|
||||
|
||||
# Print session header
|
||||
print_session_header(iteration, is_first_run)
|
||||
|
||||
# Create client (fresh context)
|
||||
client = create_client(project_dir, model)
|
||||
|
||||
# Choose prompt based on session type
|
||||
# Pass project_dir to enable project-specific prompts
|
||||
if is_first_run:
|
||||
prompt = get_initializer_prompt(project_dir)
|
||||
is_first_run = False # Only use initializer once
|
||||
else:
|
||||
prompt = get_coding_prompt(project_dir)
|
||||
|
||||
# Run session with async context manager
|
||||
async with client:
|
||||
status, response = await run_agent_session(client, prompt, project_dir)
|
||||
|
||||
# Handle status
|
||||
if status == "continue":
|
||||
print(f"\nAgent will auto-continue in {AUTO_CONTINUE_DELAY_SECONDS}s...")
|
||||
print_progress_summary(project_dir)
|
||||
await asyncio.sleep(AUTO_CONTINUE_DELAY_SECONDS)
|
||||
|
||||
elif status == "error":
|
||||
print("\nSession encountered an error")
|
||||
print("Will retry with a fresh session...")
|
||||
await asyncio.sleep(AUTO_CONTINUE_DELAY_SECONDS)
|
||||
|
||||
# Small delay between sessions
|
||||
if max_iterations is None or iteration < max_iterations:
|
||||
print("\nPreparing next session...\n")
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# Final summary
|
||||
print("\n" + "=" * 70)
|
||||
print(" SESSION COMPLETE")
|
||||
print("=" * 70)
|
||||
print(f"\nProject directory: {project_dir}")
|
||||
print_progress_summary(project_dir)
|
||||
|
||||
# Print instructions for running the generated application
|
||||
print("\n" + "-" * 70)
|
||||
print(" TO RUN THE GENERATED APPLICATION:")
|
||||
print("-" * 70)
|
||||
print(f"\n cd {project_dir.resolve()}")
|
||||
print(" ./init.sh # Run the setup script")
|
||||
print(" # Or manually:")
|
||||
print(" npm install && npm run dev")
|
||||
print("\n Then open http://localhost:3000 (or check init.sh for the URL)")
|
||||
print("-" * 70)
|
||||
|
||||
print("\nDone!")
|
||||
Reference in New Issue
Block a user