roboticus-cli 0.11.3

CLI commands and migration engine for the Roboticus agent runtime
Documentation
use super::*;

pub async fn cmd_memory(
    url: &str,
    tier: &str,
    session_id: Option<&str>,
    query: Option<&str>,
    limit: Option<i64>,
    json: bool,
) -> Result<(), Box<dyn std::error::Error>> {
    let (DIM, BOLD, ACCENT, GREEN, YELLOW, RED, CYAN, RESET, MONO) = colors();
    let (OK, ACTION, WARN, DETAIL, ERR) = icons();
    let c = RoboticusClient::new(url)?;
    match tier {
        "working" => {
            let sid = session_id.ok_or("--session required for working memory. Use 'roboticus sessions list' to find session IDs.")?;
            let data = c
                .get(&format!("/api/memory/working/{sid}"))
                .await
                .map_err(|e| {
                    RoboticusClient::check_connectivity_hint(&*e);
                    e
                })?;
            if json {
                println!("{}", serde_json::to_string_pretty(&data)?);
                return Ok(());
            }
            heading("Working Memory");
            let entries = data["entries"].as_array();
            match entries {
                Some(arr) if !arr.is_empty() => {
                    let widths = [12, 14, 36, 10];
                    table_header(&["ID", "Type", "Content", "Importance"], &widths);
                    for e in arr {
                        table_row(
                            &[
                                format!(
                                    "{MONO}{}{RESET}",
                                    truncate_id(e["id"].as_str().unwrap_or(""), 9)
                                ),
                                e["entry_type"].as_str().unwrap_or("").to_string(),
                                truncate_id(e["content"].as_str().unwrap_or(""), 33),
                                e["importance"].to_string(),
                            ],
                            &widths,
                        );
                    }
                    eprintln!();
                    eprintln!("    {DIM}{} entries{RESET}", arr.len());
                }
                _ => empty_state("No working memory entries"),
            }
        }
        "episodic" => {
            let lim = limit.unwrap_or(20);
            let data = c
                .get(&format!("/api/memory/episodic?limit={lim}"))
                .await
                .map_err(|e| {
                    RoboticusClient::check_connectivity_hint(&*e);
                    e
                })?;
            if json {
                println!("{}", serde_json::to_string_pretty(&data)?);
                return Ok(());
            }
            heading("Episodic Memory");
            let entries = data["entries"].as_array();
            match entries {
                Some(arr) if !arr.is_empty() => {
                    let widths = [12, 16, 36, 10];
                    table_header(&["ID", "Classification", "Content", "Importance"], &widths);
                    for e in arr {
                        table_row(
                            &[
                                format!(
                                    "{MONO}{}{RESET}",
                                    truncate_id(e["id"].as_str().unwrap_or(""), 9)
                                ),
                                e["classification"].as_str().unwrap_or("").to_string(),
                                truncate_id(e["content"].as_str().unwrap_or(""), 33),
                                e["importance"].to_string(),
                            ],
                            &widths,
                        );
                    }
                    eprintln!();
                    eprintln!("    {DIM}{} entries (limit: {lim}){RESET}", arr.len());
                }
                _ => empty_state("No episodic memory entries"),
            }
        }
        "semantic" => {
            let category = session_id.unwrap_or("general");
            let data = c
                .get(&format!("/api/memory/semantic/{category}"))
                .await
                .map_err(|e| {
                    RoboticusClient::check_connectivity_hint(&*e);
                    e
                })?;
            if json {
                println!("{}", serde_json::to_string_pretty(&data)?);
                return Ok(());
            }
            heading(&format!("Semantic Memory [{category}]"));
            let entries = data["entries"].as_array();
            match entries {
                Some(arr) if !arr.is_empty() => {
                    let widths = [20, 34, 12];
                    table_header(&["Key", "Value", "Confidence"], &widths);
                    for e in arr {
                        table_row(
                            &[
                                format!("{ACCENT}{}{RESET}", e["key"].as_str().unwrap_or("")),
                                truncate_id(e["value"].as_str().unwrap_or(""), 31),
                                format!("{:.2}", e["confidence"].as_f64().unwrap_or(0.0)),
                            ],
                            &widths,
                        );
                    }
                    eprintln!();
                    eprintln!("    {DIM}{} entries{RESET}", arr.len());
                }
                _ => empty_state("No semantic memory entries in this category"),
            }
        }
        "search" => {
            let q = query.ok_or("--query/-q required for memory search")?;
            let data = c
                .get(&format!("/api/memory/search?q={}", urlencoding(q)))
                .await
                .map_err(|e| {
                    RoboticusClient::check_connectivity_hint(&*e);
                    e
                })?;
            if json {
                println!("{}", serde_json::to_string_pretty(&data)?);
                return Ok(());
            }
            heading(&format!("Memory Search: \"{q}\""));
            let results = data["results"].as_array();
            match results {
                Some(arr) if !arr.is_empty() => {
                    for (i, r) in arr.iter().enumerate() {
                        let fallback = r.to_string();
                        let text = r.as_str().unwrap_or(&fallback);
                        eprintln!("    {DIM}{:>3}.{RESET} {text}", i + 1);
                    }
                    eprintln!();
                    eprintln!("    {DIM}{} results{RESET}", arr.len());
                }
                _ => empty_state("No results found"),
            }
        }
        _ => {
            return Err(format!(
                "unknown memory tier: {tier}. Use: working, episodic, semantic, search"
            )
            .into());
        }
    }
    eprintln!();
    Ok(())
}