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(())
}
pub async fn cmd_memory_consolidate(base_url: &str) -> Result<(), Box<dyn std::error::Error>> {
let (_, bold, _, green, _, _, _, reset, _) = colors();
let (ok, _, _, _, _) = icons();
println!("\n {bold}Running memory consolidation...{reset}\n");
let resp = super::http_client()?
.post(format!("{base_url}/api/memory/consolidate"))
.send()
.await?;
let data: serde_json::Value = resp.json().await?;
let total = data["total_actions"].as_u64().unwrap_or(0);
let ms = data["duration_ms"].as_u64().unwrap_or(0);
println!(" {ok} {green}Consolidation complete{reset} ({total} actions, {ms}ms)");
if let Some(indexed) = data["indexed"].as_u64().filter(|n| *n > 0) {
println!(" Indexed: {indexed}");
}
if let Some(deduped) = data["deduped"].as_u64().filter(|n| *n > 0) {
println!(" Deduped: {deduped}");
}
if let Some(decayed) = data["decayed"].as_u64().filter(|n| *n > 0) {
println!(" Decayed: {decayed}");
}
if let Some(pruned) = data["pruned"].as_u64().filter(|n| *n > 0) {
println!(" Pruned: {pruned}");
}
if let Some(orphans) = data["orphans_cleaned"].as_u64().filter(|n| *n > 0) {
println!(" Orphans cleaned: {orphans}");
}
println!();
Ok(())
}
pub async fn cmd_memory_reindex(base_url: &str) -> Result<(), Box<dyn std::error::Error>> {
let (_, bold, _, green, _, _, _, reset, _) = colors();
let (ok, _, _, _, _) = icons();
println!("\n {bold}Reindexing memory...{reset}\n");
let resp = super::http_client()?
.post(format!("{base_url}/api/memory/reindex"))
.send()
.await?;
let data: serde_json::Value = resp.json().await?;
let indexed = data["indexed"].as_u64().unwrap_or(0);
println!(" {ok} {green}Reindex complete{reset}: {indexed} entries indexed");
println!();
Ok(())
}