cli: convert simple commands to use memory_rpc

Commands now forward to daemon (or fallback to local store):
- query → memory_query
- journal tail → journal_tail
- graph link-set → memory_link_set
- graph link-add → memory_link_add
- weight-set → memory_weight_set
- node rename → memory_rename

Removes ~50 lines of duplicated store access code.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-12 21:54:34 -04:00
parent d2a82d4327
commit 11b58e6b0b
4 changed files with 34 additions and 87 deletions

View file

@ -153,35 +153,23 @@ pub fn cmd_link(key: &[String]) -> Result<(), String> {
&format!("neighbors('{}') | select strength,clustering_coefficient", resolved))
}
pub fn cmd_link_add(source: &str, target: &str, reason: &[String]) -> Result<(), String> {
pub fn cmd_link_add(source: &str, target: &str, _reason: &[String]) -> Result<(), String> {
super::check_dry_run();
let mut store = store::Store::load()?;
let source = store.resolve_key(source)?;
let target = store.resolve_key(target)?;
let reason = reason.join(" ");
match store.add_link(&source, &target, "manual") {
Ok(strength) => {
store.save()?;
println!("Linked: {}{} (strength={:.2}, {})", source, target, strength, reason);
}
Err(msg) if msg.contains("already exists") => {
println!("Link already exists: {}{}", source, target);
}
Err(e) => return Err(e),
}
let result = crate::mcp_server::memory_rpc(
"memory_link_add",
serde_json::json!({"source": source, "target": target}),
).map_err(|e| e.to_string())?;
println!("{}", result);
Ok(())
}
pub fn cmd_link_set(source: &str, target: &str, strength: f32) -> Result<(), String> {
super::check_dry_run();
let mut store = store::Store::load()?;
let source = store.resolve_key(source)?;
let target = store.resolve_key(target)?;
let old = store.set_link_strength(&source, &target, strength)?;
println!("Set: {}{} strength {:.2}{:.2}", source, target, old, strength);
store.save()?;
let result = crate::mcp_server::memory_rpc(
"memory_link_set",
serde_json::json!({"source": source, "target": target, "strength": strength}),
).map_err(|e| e.to_string())?;
println!("{}", result);
Ok(())
}

View file

@ -90,39 +90,14 @@ pub fn find_current_transcript() -> Option<String> {
newest.map(|(_, p)| p.to_string_lossy().to_string())
}
fn journal_tail_query(store: &crate::store::Store, query: &str, n: usize, full: bool) -> Result<(), String> {
let graph = store.build_graph();
let stages = crate::query_parser::parse_stages(query)?;
let results = crate::search::run_query(&stages, vec![], &graph, store, false, n);
// Query sorts desc and limits, so reverse to show oldest-to-newest
for (key, _score) in results.into_iter().rev() {
let Some(node) = store.nodes.get(&key) else { continue };
let ts = if node.created_at > 0 {
crate::store::format_datetime(node.created_at)
} else if node.timestamp > 0 {
crate::store::format_datetime(node.timestamp)
} else {
node.key.clone()
};
let title = extract_title(&node.content);
if full {
println!("--- [{}] {} ---\n{}\n", ts, title, node.content);
} else {
println!("[{}] {}", ts, title);
}
}
Ok(())
}
pub fn cmd_journal_tail(n: usize, full: bool, level: u8) -> Result<(), String> {
let store = crate::store::Store::load()?;
let query = format!("all | type:{} | sort:timestamp | limit:{}",
match level { 0 => "episodic", 1 => "daily", 2 => "weekly", _ => "monthly" },
n
);
journal_tail_query(&store, &query, n, full)
let format = if full { "full" } else { "compact" };
let result = crate::mcp_server::memory_rpc(
"journal_tail",
serde_json::json!({"count": n, "level": level, "format": format}),
).map_err(|e| e.to_string())?;
print!("{}", result);
Ok(())
}
pub fn cmd_journal_write(name: &str, text: &[String]) -> Result<(), String> {
@ -163,21 +138,3 @@ pub fn cmd_journal_write(name: &str, text: &[String]) -> Result<(), String> {
Ok(())
}
fn extract_title(content: &str) -> String {
let date_re = regex::Regex::new(r"(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2})").unwrap();
for line in content.lines() {
let stripped = line.trim();
if stripped.is_empty() { continue; }
if date_re.is_match(stripped) && stripped.len() < 25 { continue; }
if let Some(h) = stripped.strip_prefix("## ") {
return h.to_string();
} else if let Some(h) = stripped.strip_prefix("# ") {
return h.to_string();
} else {
return crate::util::truncate(stripped, 67, "...");
}
}
String::from("(untitled)")
}

View file

@ -203,9 +203,12 @@ pub fn cmd_query(expr: &[String]) -> Result<(), String> {
}
let query_str = expr.join(" ");
let store = crate::store::Store::load()?;
let graph = store.build_graph();
crate::query_parser::run_query(&store, &graph, &query_str)
let result = crate::mcp_server::memory_rpc(
"memory_query",
serde_json::json!({"query": query_str}),
).map_err(|e| e.to_string())?;
print!("{}", result);
Ok(())
}
pub fn get_group_content(group: &crate::config::ContextGroup, store: &crate::store::Store, cfg: &crate::config::Config) -> Vec<(String, String)> {

View file

@ -85,11 +85,11 @@ pub fn cmd_not_useful(key: &str) -> Result<(), String> {
pub fn cmd_weight_set(key: &str, weight: f32) -> Result<(), String> {
super::check_dry_run();
let mut store = store::Store::load()?;
let resolved = store.resolve_key(key)?;
let (old, new) = store.set_weight(&resolved, weight)?;
println!("Weight: {} {:.2}{:.2}", resolved, old, new);
store.save()?;
let result = crate::mcp_server::memory_rpc(
"memory_weight_set",
serde_json::json!({"key": key, "weight": weight}),
).map_err(|e| e.to_string())?;
println!("{}", result);
Ok(())
}
@ -171,13 +171,12 @@ pub fn cmd_node_delete(key: &[String]) -> Result<(), String> {
}
pub fn cmd_node_rename(old_key: &str, new_key: &str) -> Result<(), String> {
// args are positional, always valid if present
super::check_dry_run();
let mut store = store::Store::load()?;
let old_resolved = store.resolve_key(old_key)?;
store.rename_node(&old_resolved, new_key)?;
store.save()?;
println!("Renamed '{}' → '{}'", old_resolved, new_key);
let result = crate::mcp_server::memory_rpc(
"memory_rename",
serde_json::json!({"old_key": old_key, "new_key": new_key}),
).map_err(|e| e.to_string())?;
println!("{}", result);
Ok(())
}