roboticus_cli/cli/
memory.rs1use super::*;
2
3pub async fn cmd_memory(
4 url: &str,
5 tier: &str,
6 session_id: Option<&str>,
7 query: Option<&str>,
8 limit: Option<i64>,
9 json: bool,
10) -> Result<(), Box<dyn std::error::Error>> {
11 let (DIM, BOLD, ACCENT, GREEN, YELLOW, RED, CYAN, RESET, MONO) = colors();
12 let (OK, ACTION, WARN, DETAIL, ERR) = icons();
13 let c = RoboticusClient::new(url)?;
14 match tier {
15 "working" => {
16 let sid = session_id.ok_or("--session required for working memory. Use 'roboticus sessions list' to find session IDs.")?;
17 let data = c
18 .get(&format!("/api/memory/working/{sid}"))
19 .await
20 .map_err(|e| {
21 RoboticusClient::check_connectivity_hint(&*e);
22 e
23 })?;
24 if json {
25 println!("{}", serde_json::to_string_pretty(&data)?);
26 return Ok(());
27 }
28 heading("Working Memory");
29 let entries = data["entries"].as_array();
30 match entries {
31 Some(arr) if !arr.is_empty() => {
32 let widths = [12, 14, 36, 10];
33 table_header(&["ID", "Type", "Content", "Importance"], &widths);
34 for e in arr {
35 table_row(
36 &[
37 format!(
38 "{MONO}{}{RESET}",
39 truncate_id(e["id"].as_str().unwrap_or(""), 9)
40 ),
41 e["entry_type"].as_str().unwrap_or("").to_string(),
42 truncate_id(e["content"].as_str().unwrap_or(""), 33),
43 e["importance"].to_string(),
44 ],
45 &widths,
46 );
47 }
48 eprintln!();
49 eprintln!(" {DIM}{} entries{RESET}", arr.len());
50 }
51 _ => empty_state("No working memory entries"),
52 }
53 }
54 "episodic" => {
55 let lim = limit.unwrap_or(20);
56 let data = c
57 .get(&format!("/api/memory/episodic?limit={lim}"))
58 .await
59 .map_err(|e| {
60 RoboticusClient::check_connectivity_hint(&*e);
61 e
62 })?;
63 if json {
64 println!("{}", serde_json::to_string_pretty(&data)?);
65 return Ok(());
66 }
67 heading("Episodic Memory");
68 let entries = data["entries"].as_array();
69 match entries {
70 Some(arr) if !arr.is_empty() => {
71 let widths = [12, 16, 36, 10];
72 table_header(&["ID", "Classification", "Content", "Importance"], &widths);
73 for e in arr {
74 table_row(
75 &[
76 format!(
77 "{MONO}{}{RESET}",
78 truncate_id(e["id"].as_str().unwrap_or(""), 9)
79 ),
80 e["classification"].as_str().unwrap_or("").to_string(),
81 truncate_id(e["content"].as_str().unwrap_or(""), 33),
82 e["importance"].to_string(),
83 ],
84 &widths,
85 );
86 }
87 eprintln!();
88 eprintln!(" {DIM}{} entries (limit: {lim}){RESET}", arr.len());
89 }
90 _ => empty_state("No episodic memory entries"),
91 }
92 }
93 "semantic" => {
94 let category = session_id.unwrap_or("general");
95 let data = c
96 .get(&format!("/api/memory/semantic/{category}"))
97 .await
98 .map_err(|e| {
99 RoboticusClient::check_connectivity_hint(&*e);
100 e
101 })?;
102 if json {
103 println!("{}", serde_json::to_string_pretty(&data)?);
104 return Ok(());
105 }
106 heading(&format!("Semantic Memory [{category}]"));
107 let entries = data["entries"].as_array();
108 match entries {
109 Some(arr) if !arr.is_empty() => {
110 let widths = [20, 34, 12];
111 table_header(&["Key", "Value", "Confidence"], &widths);
112 for e in arr {
113 table_row(
114 &[
115 format!("{ACCENT}{}{RESET}", e["key"].as_str().unwrap_or("")),
116 truncate_id(e["value"].as_str().unwrap_or(""), 31),
117 format!("{:.2}", e["confidence"].as_f64().unwrap_or(0.0)),
118 ],
119 &widths,
120 );
121 }
122 eprintln!();
123 eprintln!(" {DIM}{} entries{RESET}", arr.len());
124 }
125 _ => empty_state("No semantic memory entries in this category"),
126 }
127 }
128 "search" => {
129 let q = query.ok_or("--query/-q required for memory search")?;
130 let data = c
131 .get(&format!("/api/memory/search?q={}", urlencoding(q)))
132 .await
133 .map_err(|e| {
134 RoboticusClient::check_connectivity_hint(&*e);
135 e
136 })?;
137 if json {
138 println!("{}", serde_json::to_string_pretty(&data)?);
139 return Ok(());
140 }
141 heading(&format!("Memory Search: \"{q}\""));
142 let results = data["results"].as_array();
143 match results {
144 Some(arr) if !arr.is_empty() => {
145 for (i, r) in arr.iter().enumerate() {
146 let fallback = r.to_string();
147 let text = r.as_str().unwrap_or(&fallback);
148 eprintln!(" {DIM}{:>3}.{RESET} {text}", i + 1);
149 }
150 eprintln!();
151 eprintln!(" {DIM}{} results{RESET}", arr.len());
152 }
153 _ => empty_state("No results found"),
154 }
155 }
156 _ => {
157 return Err(format!(
158 "unknown memory tier: {tier}. Use: working, episodic, semantic, search"
159 )
160 .into());
161 }
162 }
163 eprintln!();
164 Ok(())
165}