diff --git a/src/headless.rs b/src/headless.rs index 531e17b..c76b4b1 100644 --- a/src/headless.rs +++ b/src/headless.rs @@ -12,6 +12,9 @@ pub fn run(config_or_task: &str, cwd_override: Option<&str>, name_override: Opti let _ = std::fs::remove_dir_all(STATE_DIR); create_state_dir(); + let args: Vec = std::env::args().collect(); + let once = args.iter().any(|a| a == "--once"); + let is_multi = config_or_task.ends_with(".json") || std::path::Path::new(config_or_task).is_dir(); let (configs, interval) = if is_multi { let loaded = config::load_full(config_or_task); @@ -34,6 +37,42 @@ pub fn run(config_or_task: &str, cwd_override: Option<&str>, name_override: Opti return run_once(&configs); } + // --once: run multi-agent once with AI integration, no loop + if once { + let running = Arc::new(AtomicBool::new(true)); + setup_ctrlc(running.clone()); + let agents = spawn_and_wait(&configs, &running); + if agents.is_empty() { return Ok(()); } + write_state(&agents); + + if agents.len() > 1 { + eprintln!("\n ◐ AI integrating..."); + match integrate_results(&agents, "", 1) { + Ok(d) => { + let data = serde_json::json!({ + "cycle": 1, + "agents": agents.iter().map(|a| a.to_ai_json()).collect::>(), + "decision": &d, + }); + atomic_write( + &format!("{STATE_DIR}/decision.json"), + serde_json::to_string_pretty(&data).unwrap_or_default().as_bytes(), + ); + eprintln!(" ✓ Done\n"); + println!("{d}"); + } + Err(e) => eprintln!(" ✗ {e}"), + } + } else { + println!("{}", strip_dir_listing(&agents[0].output)); + } + + let all: Vec = (0..agents.len()).collect(); + let path = save_session(1, &agents, "", &all); + eprintln!(" saved: {path}"); + return Ok(()); + } + let running = Arc::new(AtomicBool::new(true)); setup_ctrlc(running.clone()); @@ -142,7 +181,49 @@ pub fn run(config_or_task: &str, cwd_override: Option<&str>, name_override: Opti pub fn run_preset(preset_name: &str) -> Result<(), String> { let configs = config::preset(preset_name) .ok_or_else(|| format!("unknown preset: {preset_name}"))?; - run_with_configs(configs) + let once = std::env::args().any(|a| a == "--once"); + if once { + run_once_multi(configs) + } else { + run_with_configs(configs) + } +} + +fn run_once_multi(configs: Vec) -> Result<(), String> { + let _ = std::fs::remove_dir_all(STATE_DIR); + create_state_dir(); + let running = Arc::new(AtomicBool::new(true)); + setup_ctrlc(running.clone()); + let agents = spawn_and_wait(&configs, &running); + if agents.is_empty() { return Ok(()); } + write_state(&agents); + + if agents.len() > 1 { + eprintln!("\n ◐ AI integrating..."); + match integrate_results(&agents, "", 1) { + Ok(d) => { + let data = serde_json::json!({ + "cycle": 1, + "agents": agents.iter().map(|a| a.to_ai_json()).collect::>(), + "decision": &d, + }); + atomic_write( + &format!("{STATE_DIR}/decision.json"), + serde_json::to_string_pretty(&data).unwrap_or_default().as_bytes(), + ); + eprintln!(" ✓ Done\n"); + println!("{d}"); + } + Err(e) => eprintln!(" ✗ {e}"), + } + } else { + println!("{}", strip_dir_listing(&agents[0].output)); + } + + let all: Vec = (0..agents.len()).collect(); + let path = save_session(1, &agents, "", &all); + eprintln!(" saved: {path}"); + Ok(()) } fn run_with_configs(configs: Vec) -> Result<(), String> {