// cli/agent.rs — agent subcommand handlers use crate::store; pub fn cmd_run_agent(agent: &str, count: usize, target: &[String], query: Option<&str>, dry_run: bool, _local: bool, state_dir: Option<&str>) -> Result<(), String> { // Mark as agent so tool calls (e.g. poc-memory render) don't // pollute the user's seen set as a side effect // SAFETY: single-threaded at this point (CLI startup, before any agent work) unsafe { std::env::set_var("POC_AGENT", "1"); } // Override agent output/state directory if specified if let Some(dir) = state_dir { std::fs::create_dir_all(dir).map_err(|e| format!("create state dir: {}", e))?; unsafe { std::env::set_var("POC_AGENT_OUTPUT_DIR", dir); } } if dry_run { unsafe { std::env::set_var("POC_MEMORY_DRY_RUN", "1"); } } let mut store = store::Store::load()?; // Resolve targets: explicit --target, --query, or agent's default query let resolved_targets: Vec = if !target.is_empty() { target.to_vec() } else if let Some(q) = query { let graph = store.build_graph(); let stages = crate::query_parser::parse_stages(q)?; let results = crate::search::run_query(&stages, vec![], &graph, &store, false, count); if results.is_empty() { return Err(format!("query returned no results: {}", q)); } let keys: Vec = results.into_iter().map(|(k, _)| k).collect(); println!("[{}] query matched {} nodes", agent, keys.len()); keys } else { vec![] // use agent's built-in query }; if !resolved_targets.is_empty() { for (i, key) in resolved_targets.iter().enumerate() { println!("[{}] [{}/{}] {}", agent, i + 1, resolved_targets.len(), key); if i > 0 { store = store::Store::load()?; } if let Err(e) = crate::agent::oneshot::run_one_agent( &mut store, agent, count, Some(&[key.clone()]), ) { println!("[{}] ERROR on {}: {}", agent, key, e); } } } else { // Local execution (--local, --debug, dry-run, or daemon unavailable) crate::agent::oneshot::run_one_agent( &mut store, agent, count, None, )?; } Ok(()) } pub fn cmd_consolidate_batch(count: usize, auto: bool, agent: Option) -> Result<(), String> { let store = store::Store::load()?; if let Some(agent_name) = agent { let batch = crate::agents::prompts::agent_prompt(&store, &agent_name, count)?; for (i, s) in batch.steps.iter().enumerate() { if batch.steps.len() > 1 { println!("=== STEP {} ({}) ===\n", i + 1, s.phase); } println!("{}", s.prompt); } Ok(()) } else { crate::agents::prompts::consolidation_batch(&store, count, auto) } } pub fn cmd_replay_queue(count: usize) -> Result<(), String> { let store = store::Store::load()?; let queue = crate::neuro::replay_queue(&store, count); println!("Replay queue ({} items):", queue.len()); for (i, item) in queue.iter().enumerate() { println!(" {:2}. [{:.3}] {:>10} {} (interval={}d, emotion={:.1}, spectral={:.1})", i + 1, item.priority, item.classification, item.key, item.interval_days, item.emotion, item.outlier_score); } Ok(()) } pub fn cmd_consolidate_session() -> Result<(), String> { let store = store::Store::load()?; let plan = crate::neuro::consolidation_plan(&store); println!("{}", crate::neuro::format_plan(&plan)); Ok(()) } pub fn cmd_consolidate_full() -> Result<(), String> { let mut store = store::Store::load()?; crate::consolidate::consolidate_full(&mut store) } pub fn cmd_digest_links(do_apply: bool) -> Result<(), String> { let store = store::Store::load()?; let links = crate::digest::parse_all_digest_links(&store); drop(store); println!("Found {} unique links from digest nodes", links.len()); if !do_apply { for (i, link) in links.iter().enumerate() { println!(" {:3}. {} → {}", i + 1, link.source, link.target); if !link.reason.is_empty() { println!(" ({})", &link.reason[..link.reason.floor_char_boundary(link.reason.len().min(80))]); } } println!("\nTo apply: poc-memory digest-links --apply"); return Ok(()); } let mut store = store::Store::load()?; let (applied, skipped, fallbacks) = crate::digest::apply_digest_links(&mut store, &links); println!("\nApplied: {} ({} file-level fallbacks) Skipped: {}", applied, fallbacks, skipped); Ok(()) }