Files
gh-joshuaoliphant-claude-pl…/skills/adw-bootstrap/reference/minimal/adws/adw_prompt.py
2025-11-30 08:28:42 +08:00

284 lines
8.5 KiB
Python
Executable File

#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "pydantic",
# "python-dotenv",
# "click",
# "rich",
# ]
# ///
"""
Run an adhoc Claude Code prompt from the command line.
Usage:
# Method 1: Direct execution (requires uv)
./adw_prompt.py "Write a hello world Python script"
# Method 2: Using uv run
uv run adw_prompt.py "Write a hello world Python script"
# Method 3: Using Python directly (requires dependencies installed)
python adw_prompt.py "Write a hello world Python script"
Examples:
# Run with specific model
./adw_prompt.py "Explain this code" --model opus
# Run with custom output file
./adw_prompt.py "Create a FastAPI app" --output my_result.jsonl
# Run from a different working directory
./adw_prompt.py "List files here" --working-dir /path/to/project
# Disable retry on failure
./adw_prompt.py "Quick test" --no-retry
# Use custom agent name
./adw_prompt.py "Debug this" --agent-name debugger
"""
import os
import sys
import json
from pathlib import Path
import click
from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from rich.syntax import Syntax
from rich.text import Text
# Add the adw_modules directory to the path so we can import agent
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "adw_modules"))
from agent import (
prompt_claude_code,
AgentPromptRequest,
AgentPromptResponse,
prompt_claude_code_with_retry,
generate_short_id,
)
# Output file name constants
OUTPUT_JSONL = "cc_raw_output.jsonl"
OUTPUT_JSON = "cc_raw_output.json"
FINAL_OBJECT_JSON = "cc_final_object.json"
SUMMARY_JSON = "custom_summary_output.json"
@click.command()
@click.argument("prompt", required=True)
@click.option(
"--model",
type=click.Choice(["sonnet", "opus", "haiku"]),
default="sonnet",
help="Claude model to use (sonnet=balanced, opus=max intelligence, haiku=fast & economical)",
)
@click.option(
"--output",
type=click.Path(),
help="Output file path (default: ./output/oneoff_<id>_output.jsonl)",
)
@click.option(
"--working-dir",
type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True),
help="Working directory for the prompt execution (default: current directory)",
)
@click.option("--no-retry", is_flag=True, help="Disable automatic retry on failure")
@click.option(
"--agent-name", default="oneoff", help="Agent name for tracking (default: oneoff)"
)
def main(
prompt: str,
model: str,
output: str,
working_dir: str,
no_retry: bool,
agent_name: str,
):
"""Run an adhoc Claude Code prompt from the command line."""
console = Console()
# Validate prompt is not empty
if not prompt or not prompt.strip():
console.print(
Panel(
"[bold red]Error: Prompt cannot be empty[/bold red]\n\n"
"Please provide a valid prompt string.",
title="❌ Invalid Input",
border_style="red"
)
)
sys.exit(1)
# Generate a unique ID for this execution
adw_id = generate_short_id()
# Set up output file path
if not output:
# Default: write to agents/<adw_id>/<agent_name>/
output_dir = Path(f"./agents/{adw_id}/{agent_name}")
output_dir.mkdir(parents=True, exist_ok=True)
output = str(output_dir / OUTPUT_JSONL)
# Use current directory if no working directory specified
if not working_dir:
working_dir = os.getcwd()
# Create the prompt request
request = AgentPromptRequest(
prompt=prompt,
adw_id=adw_id,
agent_name=agent_name,
model=model,
dangerously_skip_permissions=True,
output_file=output,
working_dir=working_dir,
)
# Create execution info table
info_table = Table(show_header=False, box=None, padding=(0, 1))
info_table.add_column(style="bold cyan")
info_table.add_column()
info_table.add_row("ADW ID", adw_id)
info_table.add_row("ADW Name", "adw_prompt")
info_table.add_row("Prompt", prompt)
info_table.add_row("Model", model)
info_table.add_row("Working Dir", working_dir)
info_table.add_row("Output", output)
console.print(
Panel(
info_table,
title="[bold blue]🚀 Inputs[/bold blue]",
border_style="blue",
)
)
console.print()
response: AgentPromptResponse | None = None
try:
# Execute the prompt
with console.status("[bold yellow]Executing prompt...[/bold yellow]"):
if no_retry:
# Direct execution without retry
response = prompt_claude_code(request)
else:
# Execute with retry logic
response = prompt_claude_code_with_retry(request)
# Display the result
if response.success:
# Success panel
result_panel = Panel(
response.output,
title="[bold green]✅ Success[/bold green]",
border_style="green",
padding=(1, 2),
)
console.print(result_panel)
if response.session_id:
console.print(
f"\n[bold cyan]Session ID:[/bold cyan] {response.session_id}"
)
else:
# Error panel
error_panel = Panel(
response.output,
title="[bold red]❌ Failed[/bold red]",
border_style="red",
padding=(1, 2),
)
console.print(error_panel)
if response.retry_code != "none":
console.print(
f"\n[bold yellow]Retry code:[/bold yellow] {response.retry_code}"
)
# Show output file info
console.print()
# Also create a JSON summary file with explicit error handling
try:
if output.endswith(f"/{OUTPUT_JSONL}"):
# Default path: save as custom_summary_output.json in same directory
simple_json_output = output.replace(f"/{OUTPUT_JSONL}", f"/{SUMMARY_JSON}")
else:
# Custom path: replace .jsonl with _summary.json
simple_json_output = output.replace(".jsonl", "_summary.json")
# Create summary data
summary_data = {
"adw_id": adw_id,
"prompt": prompt,
"model": model,
"working_dir": working_dir,
"success": response.success,
"session_id": response.session_id,
"retry_code": response.retry_code,
"output": response.output,
}
# Write summary file
with open(simple_json_output, "w") as f:
json.dump(summary_data, f, indent=2)
except Exception as e:
console.print(
f"[yellow]Warning: Could not create summary file: {e}[/yellow]"
)
# Files saved panel with descriptions
files_table = Table(show_header=True, box=None)
files_table.add_column("File Type", style="bold cyan")
files_table.add_column("Path", style="dim")
files_table.add_column("Description", style="italic")
# Determine paths for all files
output_dir = os.path.dirname(output)
json_array_path = os.path.join(output_dir, OUTPUT_JSON)
final_object_path = os.path.join(output_dir, FINAL_OBJECT_JSON)
files_table.add_row(
"JSONL Stream", output, "Raw streaming output from Claude Code"
)
files_table.add_row(
"JSON Array", json_array_path, "All messages as a JSON array"
)
files_table.add_row(
"Final Object", final_object_path, "Last message entry (final result)"
)
files_table.add_row(
"Summary", simple_json_output, "High-level execution summary with metadata"
)
console.print(
Panel(
files_table,
title="[bold blue]📄 Output Files[/bold blue]",
border_style="blue",
)
)
# Exit with appropriate code
sys.exit(0 if response.success else 1)
except Exception as e:
console.print(
Panel(
f"[bold red]{str(e)}[/bold red]",
title="[bold red]❌ Unexpected Error[/bold red]",
border_style="red",
)
)
sys.exit(2)
if __name__ == "__main__":
main()