502 lines
16 KiB
Python
Executable File
502 lines
16 KiB
Python
Executable File
#!/usr/bin/env -S uv run --script
|
|
# /// script
|
|
# requires-python = ">=3.10"
|
|
# dependencies = [
|
|
# "pydantic",
|
|
# "python-dotenv",
|
|
# "click",
|
|
# "rich",
|
|
# ]
|
|
# ///
|
|
"""
|
|
Run chore planning and implementation workflow.
|
|
|
|
This script runs two slash commands in sequence:
|
|
1. /chore - Creates a plan based on the prompt
|
|
2. /implement - Implements the plan created by /chore
|
|
|
|
Usage:
|
|
# Method 1: Direct execution (requires uv)
|
|
./adws/adw_chore_implement.py "Add error handling to all API endpoints"
|
|
|
|
# Method 2: Using uv run
|
|
uv run adws/adw_chore_implement.py "Refactor database connection logic"
|
|
|
|
Examples:
|
|
# Run with specific model
|
|
./adws/adw_chore_implement.py "Add logging to agent.py" --model opus
|
|
|
|
# Run from a different working directory
|
|
./adws/adw_chore_implement.py "Update documentation" --working-dir /path/to/project
|
|
|
|
# Run with verbose output
|
|
./adws/adw_chore_implement.py "Add tests" --verbose
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
import click
|
|
from rich.console import Console
|
|
from rich.panel import Panel
|
|
from rich.table import Table
|
|
from rich.rule import Rule
|
|
|
|
# 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 (
|
|
AgentTemplateRequest,
|
|
AgentPromptResponse,
|
|
execute_template,
|
|
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"
|
|
|
|
|
|
def extract_plan_path(output: str) -> str:
|
|
"""Extract the plan file path from the chore command output.
|
|
|
|
Looks for patterns like:
|
|
- specs/chore-12345678-update-readme.md
|
|
- Created plan at: specs/chore-...
|
|
- Plan file: specs/chore-...
|
|
"""
|
|
# Try multiple patterns to find the plan path
|
|
patterns = [
|
|
r"specs/chore-[a-zA-Z0-9\-]+\.md",
|
|
r"Created plan at:\s*(specs/chore-[a-zA-Z0-9\-]+\.md)",
|
|
r"Plan file:\s*(specs/chore-[a-zA-Z0-9\-]+\.md)",
|
|
r"path.*?:\s*(specs/chore-[a-zA-Z0-9\-]+\.md)",
|
|
]
|
|
|
|
for pattern in patterns:
|
|
match = re.search(pattern, output, re.IGNORECASE | re.MULTILINE)
|
|
if match:
|
|
return match.group(1) if match.groups() else match.group(0)
|
|
|
|
# If no match found, raise an error
|
|
raise ValueError("Could not find plan file path in chore output")
|
|
|
|
|
|
@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(
|
|
"--working-dir",
|
|
type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True),
|
|
help="Working directory for command execution (default: current directory)",
|
|
)
|
|
def main(
|
|
prompt: str,
|
|
model: str,
|
|
working_dir: str,
|
|
):
|
|
"""Run chore planning and implementation workflow."""
|
|
console = Console()
|
|
|
|
# Generate a unique ID for this workflow
|
|
adw_id = generate_short_id()
|
|
|
|
# Use current directory if no working directory specified
|
|
if not working_dir:
|
|
working_dir = os.getcwd()
|
|
|
|
# Set default agent names
|
|
planner_name = "planner"
|
|
builder_name = "builder"
|
|
|
|
console.print(
|
|
Panel(
|
|
f"[bold blue]ADW Chore & Implement Workflow[/bold blue]\n\n"
|
|
f"[cyan]ADW ID:[/cyan] {adw_id}\n"
|
|
f"[cyan]Model:[/cyan] {model}\n"
|
|
f"[cyan]Working Dir:[/cyan] {working_dir}",
|
|
title="[bold blue]🚀 Workflow Configuration[/bold blue]",
|
|
border_style="blue",
|
|
)
|
|
)
|
|
console.print()
|
|
|
|
# Phase 1: Run /chore command
|
|
console.print(Rule("[bold yellow]Phase 1: Planning (/chore)[/bold yellow]"))
|
|
console.print()
|
|
|
|
# Create the chore request
|
|
chore_request = AgentTemplateRequest(
|
|
agent_name=planner_name,
|
|
slash_command="/chore",
|
|
args=[adw_id, prompt],
|
|
adw_id=adw_id,
|
|
model=model,
|
|
working_dir=working_dir,
|
|
)
|
|
|
|
# Display chore execution info
|
|
chore_info_table = Table(show_header=False, box=None, padding=(0, 1))
|
|
chore_info_table.add_column(style="bold cyan")
|
|
chore_info_table.add_column()
|
|
|
|
chore_info_table.add_row("ADW ID", adw_id)
|
|
chore_info_table.add_row("ADW Name", "adw_chore_implement (planning)")
|
|
chore_info_table.add_row("Command", "/chore")
|
|
chore_info_table.add_row("Args", f'{adw_id} "{prompt}"')
|
|
chore_info_table.add_row("Model", model)
|
|
chore_info_table.add_row("Agent", planner_name)
|
|
|
|
console.print(
|
|
Panel(
|
|
chore_info_table,
|
|
title="[bold blue]🚀 Chore Inputs[/bold blue]",
|
|
border_style="blue",
|
|
)
|
|
)
|
|
console.print()
|
|
|
|
plan_path = None
|
|
|
|
try:
|
|
# Execute the chore command
|
|
with console.status("[bold yellow]Creating plan...[/bold yellow]"):
|
|
chore_response = execute_template(chore_request)
|
|
|
|
# Display the chore result
|
|
if chore_response.success:
|
|
# Success panel
|
|
console.print(
|
|
Panel(
|
|
chore_response.output,
|
|
title="[bold green]✅ Planning Success[/bold green]",
|
|
border_style="green",
|
|
padding=(1, 2),
|
|
)
|
|
)
|
|
|
|
# Extract the plan path from the output
|
|
try:
|
|
plan_path = extract_plan_path(chore_response.output)
|
|
console.print(f"\n[bold cyan]Plan created at:[/bold cyan] {plan_path}")
|
|
except ValueError as e:
|
|
console.print(
|
|
Panel(
|
|
f"[bold red]Could not extract plan path: {str(e)}[/bold red]\n\n"
|
|
"The chore command succeeded but the plan file path could not be found in the output.",
|
|
title="[bold red]❌ Parse Error[/bold red]",
|
|
border_style="red",
|
|
)
|
|
)
|
|
sys.exit(3)
|
|
|
|
else:
|
|
# Error panel
|
|
console.print(
|
|
Panel(
|
|
chore_response.output,
|
|
title="[bold red]❌ Planning Failed[/bold red]",
|
|
border_style="red",
|
|
padding=(1, 2),
|
|
)
|
|
)
|
|
console.print(
|
|
"\n[bold red]Workflow aborted: Planning phase failed[/bold red]"
|
|
)
|
|
sys.exit(1)
|
|
|
|
# Save chore phase summary
|
|
chore_output_dir = f"./agents/{adw_id}/{planner_name}"
|
|
chore_summary_path = f"{chore_output_dir}/{SUMMARY_JSON}"
|
|
|
|
with open(chore_summary_path, "w") as f:
|
|
json.dump(
|
|
{
|
|
"phase": "planning",
|
|
"adw_id": adw_id,
|
|
"slash_command": "/chore",
|
|
"args": [adw_id, prompt],
|
|
"path_to_slash_command_prompt": ".claude/commands/chore.md",
|
|
"model": model,
|
|
"working_dir": working_dir,
|
|
"success": chore_response.success,
|
|
"session_id": chore_response.session_id,
|
|
"retry_code": chore_response.retry_code,
|
|
"output": chore_response.output,
|
|
"plan_path": plan_path,
|
|
},
|
|
f,
|
|
indent=2,
|
|
)
|
|
|
|
# Show chore output files
|
|
console.print()
|
|
|
|
# Files saved panel for chore phase
|
|
chore_files_table = Table(show_header=True, box=None)
|
|
chore_files_table.add_column("File Type", style="bold cyan")
|
|
chore_files_table.add_column("Path", style="dim")
|
|
chore_files_table.add_column("Description", style="italic")
|
|
|
|
chore_files_table.add_row(
|
|
"JSONL Stream",
|
|
f"{chore_output_dir}/{OUTPUT_JSONL}",
|
|
"Raw streaming output from Claude Code",
|
|
)
|
|
chore_files_table.add_row(
|
|
"JSON Array",
|
|
f"{chore_output_dir}/{OUTPUT_JSON}",
|
|
"All messages as a JSON array",
|
|
)
|
|
chore_files_table.add_row(
|
|
"Final Object",
|
|
f"{chore_output_dir}/{FINAL_OBJECT_JSON}",
|
|
"Last message entry (final result)",
|
|
)
|
|
chore_files_table.add_row(
|
|
"Summary",
|
|
chore_summary_path,
|
|
"High-level execution summary with metadata",
|
|
)
|
|
|
|
console.print(
|
|
Panel(
|
|
chore_files_table,
|
|
title="[bold blue]📄 Planning Output Files[/bold blue]",
|
|
border_style="blue",
|
|
)
|
|
)
|
|
|
|
console.print()
|
|
|
|
# Phase 2: Run /implement command
|
|
console.print(
|
|
Rule("[bold yellow]Phase 2: Implementation (/implement)[/bold yellow]")
|
|
)
|
|
console.print()
|
|
|
|
# Create the implement request
|
|
implement_request = AgentTemplateRequest(
|
|
agent_name=builder_name,
|
|
slash_command="/implement",
|
|
args=[plan_path],
|
|
adw_id=adw_id,
|
|
model=model,
|
|
working_dir=working_dir,
|
|
)
|
|
|
|
# Display implement execution info
|
|
implement_info_table = Table(show_header=False, box=None, padding=(0, 1))
|
|
implement_info_table.add_column(style="bold cyan")
|
|
implement_info_table.add_column()
|
|
|
|
implement_info_table.add_row("ADW ID", adw_id)
|
|
implement_info_table.add_row("ADW Name", "adw_chore_implement (building)")
|
|
implement_info_table.add_row("Command", "/implement")
|
|
implement_info_table.add_row("Args", plan_path)
|
|
implement_info_table.add_row("Model", model)
|
|
implement_info_table.add_row("Agent", builder_name)
|
|
|
|
console.print(
|
|
Panel(
|
|
implement_info_table,
|
|
title="[bold blue]🚀 Implement Inputs[/bold blue]",
|
|
border_style="blue",
|
|
)
|
|
)
|
|
console.print()
|
|
|
|
# Execute the implement command
|
|
with console.status("[bold yellow]Implementing plan...[/bold yellow]"):
|
|
implement_response = execute_template(implement_request)
|
|
|
|
# Display the implement result
|
|
if implement_response.success:
|
|
# Success panel
|
|
console.print(
|
|
Panel(
|
|
implement_response.output,
|
|
title="[bold green]✅ Implementation Success[/bold green]",
|
|
border_style="green",
|
|
padding=(1, 2),
|
|
)
|
|
)
|
|
|
|
if implement_response.session_id:
|
|
console.print(
|
|
f"\n[bold cyan]Session ID:[/bold cyan] {implement_response.session_id}"
|
|
)
|
|
else:
|
|
# Error panel
|
|
console.print(
|
|
Panel(
|
|
implement_response.output,
|
|
title="[bold red]❌ Implementation Failed[/bold red]",
|
|
border_style="red",
|
|
padding=(1, 2),
|
|
)
|
|
)
|
|
|
|
# Save implement phase summary
|
|
implement_output_dir = f"./agents/{adw_id}/{builder_name}"
|
|
implement_summary_path = f"{implement_output_dir}/{SUMMARY_JSON}"
|
|
|
|
with open(implement_summary_path, "w") as f:
|
|
json.dump(
|
|
{
|
|
"phase": "implementation",
|
|
"adw_id": adw_id,
|
|
"slash_command": "/implement",
|
|
"args": [plan_path],
|
|
"path_to_slash_command_prompt": ".claude/commands/implement.md",
|
|
"model": model,
|
|
"working_dir": working_dir,
|
|
"success": implement_response.success,
|
|
"session_id": implement_response.session_id,
|
|
"retry_code": implement_response.retry_code,
|
|
"output": implement_response.output,
|
|
},
|
|
f,
|
|
indent=2,
|
|
)
|
|
|
|
# Show implement output files
|
|
console.print()
|
|
|
|
# Files saved panel for implement phase
|
|
implement_files_table = Table(show_header=True, box=None)
|
|
implement_files_table.add_column("File Type", style="bold cyan")
|
|
implement_files_table.add_column("Path", style="dim")
|
|
implement_files_table.add_column("Description", style="italic")
|
|
|
|
implement_files_table.add_row(
|
|
"JSONL Stream",
|
|
f"{implement_output_dir}/{OUTPUT_JSONL}",
|
|
"Raw streaming output from Claude Code",
|
|
)
|
|
implement_files_table.add_row(
|
|
"JSON Array",
|
|
f"{implement_output_dir}/{OUTPUT_JSON}",
|
|
"All messages as a JSON array",
|
|
)
|
|
implement_files_table.add_row(
|
|
"Final Object",
|
|
f"{implement_output_dir}/{FINAL_OBJECT_JSON}",
|
|
"Last message entry (final result)",
|
|
)
|
|
implement_files_table.add_row(
|
|
"Summary",
|
|
implement_summary_path,
|
|
"High-level execution summary with metadata",
|
|
)
|
|
|
|
console.print(
|
|
Panel(
|
|
implement_files_table,
|
|
title="[bold blue]📄 Implementation Output Files[/bold blue]",
|
|
border_style="blue",
|
|
)
|
|
)
|
|
|
|
# Show workflow summary
|
|
console.print()
|
|
console.print(Rule("[bold blue]Workflow Summary[/bold blue]"))
|
|
console.print()
|
|
|
|
summary_table = Table(show_header=True, box=None)
|
|
summary_table.add_column("Phase", style="bold cyan")
|
|
summary_table.add_column("Status", style="bold")
|
|
summary_table.add_column("Output Directory", style="dim")
|
|
|
|
# Planning phase row
|
|
planning_status = "✅ Success" if chore_response.success else "❌ Failed"
|
|
summary_table.add_row(
|
|
"Planning (/chore)",
|
|
planning_status,
|
|
f"./agents/{adw_id}/{planner_name}/",
|
|
)
|
|
|
|
# Implementation phase row
|
|
implement_status = "✅ Success" if implement_response.success else "❌ Failed"
|
|
summary_table.add_row(
|
|
"Implementation (/implement)",
|
|
implement_status,
|
|
f"./agents/{adw_id}/{builder_name}/",
|
|
)
|
|
|
|
console.print(summary_table)
|
|
|
|
# Create overall workflow summary
|
|
workflow_summary_path = f"./agents/{adw_id}/workflow_summary.json"
|
|
os.makedirs(f"./agents/{adw_id}", exist_ok=True)
|
|
|
|
with open(workflow_summary_path, "w") as f:
|
|
json.dump(
|
|
{
|
|
"workflow": "chore_implement",
|
|
"adw_id": adw_id,
|
|
"prompt": prompt,
|
|
"model": model,
|
|
"working_dir": working_dir,
|
|
"plan_path": plan_path,
|
|
"phases": {
|
|
"planning": {
|
|
"success": chore_response.success,
|
|
"session_id": chore_response.session_id,
|
|
"agent": planner_name,
|
|
"output_dir": f"./agents/{adw_id}/{planner_name}/",
|
|
},
|
|
"implementation": {
|
|
"success": implement_response.success,
|
|
"session_id": implement_response.session_id,
|
|
"agent": builder_name,
|
|
"output_dir": f"./agents/{adw_id}/{builder_name}/",
|
|
},
|
|
},
|
|
"overall_success": chore_response.success
|
|
and implement_response.success,
|
|
},
|
|
f,
|
|
indent=2,
|
|
)
|
|
|
|
console.print(
|
|
f"\n[bold cyan]Workflow summary:[/bold cyan] {workflow_summary_path}"
|
|
)
|
|
console.print()
|
|
|
|
# Exit with appropriate code
|
|
if chore_response.success and implement_response.success:
|
|
console.print(
|
|
"[bold green]✅ Workflow completed successfully![/bold green]"
|
|
)
|
|
sys.exit(0)
|
|
else:
|
|
console.print(
|
|
"[bold yellow]⚠️ Workflow completed with errors[/bold yellow]"
|
|
)
|
|
sys.exit(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()
|