An AI agent built to do Ralph loops - plan mode for planning and ralph mode for implementing.
at main 152 lines 4.9 kB view raw
1use rustagent::{config, logging, planning, ralph}; 2 3use clap::{Parser, Subcommand}; 4use std::path::PathBuf; 5 6#[derive(Parser)] 7#[command(name = "rustagent")] 8#[command(about = "A Rust-based AI agent for task execution", long_about = None)] 9struct Cli { 10 #[command(subcommand)] 11 command: Option<Commands>, 12} 13 14#[derive(Subcommand)] 15enum Commands { 16 /// Initialize a new agent specification 17 Init { 18 /// Directory to store the specification 19 #[arg(long)] 20 spec_dir: Option<String>, 21 }, 22 /// Create an execution plan from a specification 23 Plan { 24 /// Directory containing the specification 25 #[arg(long)] 26 spec_dir: Option<String>, 27 }, 28 /// Execute a plan 29 Run { 30 /// Path to the specification file 31 spec_file: String, 32 /// Maximum number of iterations 33 #[arg(long)] 34 max_iterations: Option<usize>, 35 }, 36 /// Launch interactive TUI 37 Tui, 38} 39 40/// Find config file in standard locations 41fn find_config_path() -> anyhow::Result<PathBuf> { 42 // Try current directory 43 let local_config = PathBuf::from("rustagent.toml"); 44 if local_config.exists() { 45 return Ok(local_config); 46 } 47 48 // Try XDG config directory 49 if let Some(config_dir) = dirs::config_dir() { 50 let xdg_config = config_dir.join("rustagent").join("config.toml"); 51 if xdg_config.exists() { 52 return Ok(xdg_config); 53 } 54 } 55 56 // Try home directory 57 if let Some(home_dir) = dirs::home_dir() { 58 let home_config = home_dir.join(".rustagent").join("config.toml"); 59 if home_config.exists() { 60 return Ok(home_config); 61 } 62 } 63 64 anyhow::bail!( 65 "Config file not found. Please create rustagent.toml in current directory or ~/.rustagent/config.toml" 66 ) 67} 68 69#[tokio::main] 70async fn main() -> anyhow::Result<()> { 71 let _log_guard = logging::init_logging()?; 72 73 let cli = Cli::parse(); 74 75 // Default to TUI if no command specified 76 let command = cli.command.unwrap_or(Commands::Tui); 77 78 match command { 79 Commands::Init { spec_dir } => { 80 let dir = spec_dir.clone().unwrap_or_else(|| "specs".to_string()); 81 82 let spec_path = std::path::Path::new(&dir); 83 if !spec_path.exists() { 84 std::fs::create_dir_all(spec_path)?; 85 println!("Created spec directory: {}", dir); 86 } else { 87 println!("Spec directory already exists: {}", dir); 88 } 89 90 let config_path = std::path::Path::new("rustagent.toml"); 91 if !config_path.exists() { 92 let template = include_str!("../rustagent.toml.example"); 93 std::fs::write(config_path, template)?; 94 println!("Created config template: rustagent.toml"); 95 println!("Please edit rustagent.toml to add your API keys."); 96 } else { 97 println!("Config file already exists: rustagent.toml"); 98 } 99 100 println!("\nInitialization complete!"); 101 println!("Next steps:"); 102 println!(" 1. Edit rustagent.toml with your API keys"); 103 println!(" 2. Run 'rustagent plan' to create a spec"); 104 println!(" 3. Run 'rustagent run <spec-file>' to execute"); 105 } 106 Commands::Plan { spec_dir } => { 107 // Load config from standard locations 108 let config_path = find_config_path()?; 109 let config = config::Config::load(&config_path)?; 110 111 // Use provided spec_dir or default from config 112 let dir = spec_dir 113 .clone() 114 .unwrap_or_else(|| config.rustagent.spec_dir.clone()); 115 116 // Create and run planning agent 117 let mut agent = planning::PlanningAgent::new(config, dir)?; 118 agent.run().await?; 119 } 120 Commands::Run { 121 spec_file, 122 max_iterations, 123 } => { 124 // Load config from standard locations 125 let config_path = find_config_path()?; 126 let config = config::Config::load(&config_path)?; 127 128 // Create and run Ralph loop 129 let ralph = ralph::RalphLoop::new(config, spec_file.clone(), max_iterations)?; 130 ralph.run().await?; 131 } 132 Commands::Tui => { 133 let config_path = find_config_path()?; 134 let config = config::Config::load(&config_path)?; 135 let spec_dir = config.rustagent.spec_dir.clone(); 136 137 use rustagent::tui::{self, agent_channel}; 138 139 let mut terminal = tui::setup_terminal()?; 140 let (tx, mut rx) = agent_channel(); 141 let mut app = tui::App::new(&spec_dir, tx, Some(config)); 142 143 let result = tui::run(&mut terminal, &mut app, &mut rx).await; 144 145 tui::restore_terminal(&mut terminal)?; 146 147 result?; 148 } 149 } 150 151 Ok(()) 152}