forked from kent/consciousness
Convert store and CLI to anyhow::Result for cleaner error handling
Replace Result<_, String> with anyhow::Result throughout: - hippocampus/store module (persist, ops, types, view, mod) - CLI modules (admin, agent, graph, journal, node) - Run trait in main.rs Use .context() and .with_context() instead of .map_err(|e| format!(...)) patterns. Add bail!() for early error returns. Add access_local() helper in hippocampus/mod.rs that returns Result<Arc<Mutex<Store>>> for direct local store access. Fix store access patterns to properly lock Arc<Mutex<Store>> before accessing fields in mind/unconscious.rs, mind/mod.rs, subconscious/learn.rs, and hippocampus/memory.rs. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
5db00e083f
commit
b8db8754be
17 changed files with 282 additions and 295 deletions
|
|
@ -1,24 +1,23 @@
|
|||
// cli/admin.rs — admin subcommand handlers
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use crate::hippocampus as memory;
|
||||
use crate::store;
|
||||
use crate::hippocampus::store;
|
||||
|
||||
fn install_default_file(data_dir: &std::path::Path, name: &str, content: &str) -> Result<(), String> {
|
||||
fn install_default_file(data_dir: &std::path::Path, name: &str, content: &str) -> Result<()> {
|
||||
let path = data_dir.join(name);
|
||||
if !path.exists() {
|
||||
std::fs::write(&path, content)
|
||||
.map_err(|e| format!("write {}: {}", name, e))?;
|
||||
std::fs::write(&path, content)?;
|
||||
println!("Created {}", path.display());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cmd_init() -> Result<(), String> {
|
||||
pub async fn cmd_init() -> Result<()> {
|
||||
let cfg = crate::config::get();
|
||||
|
||||
// Ensure data directory exists
|
||||
std::fs::create_dir_all(&cfg.data_dir)
|
||||
.map_err(|e| format!("create data_dir: {}", e))?;
|
||||
std::fs::create_dir_all(&cfg.data_dir)?;
|
||||
|
||||
// Install filesystem files (not store nodes)
|
||||
install_default_file(&cfg.data_dir, "instructions.md",
|
||||
|
|
@ -27,17 +26,17 @@ pub fn cmd_init() -> Result<(), String> {
|
|||
include_str!("../../defaults/on-consciousness.md"))?;
|
||||
|
||||
// Initialize store and seed default identity node if empty
|
||||
let mut store = store::Store::load()?;
|
||||
let count = store.init_from_markdown()?;
|
||||
let arc = memory::access_local()?;
|
||||
let mut store = arc.lock().await;
|
||||
let count = store.init_from_markdown().map_err(|e| anyhow::anyhow!("{}", e))?;
|
||||
for key in &cfg.core_nodes {
|
||||
if !store.nodes.contains_key(key) && key == "identity" {
|
||||
let default = include_str!("../../defaults/identity.md");
|
||||
store.upsert(key, default)
|
||||
.map_err(|e| format!("seed {}: {}", key, e))?;
|
||||
store.upsert(key, default).map_err(|e| anyhow::anyhow!("{}", e))?;
|
||||
println!("Seeded {} in store", key);
|
||||
}
|
||||
}
|
||||
store.save()?;
|
||||
store.save().map_err(|e| anyhow::anyhow!("{}", e))?;
|
||||
println!("Indexed {} memory units", count);
|
||||
|
||||
// Create config if none exists
|
||||
|
|
@ -49,11 +48,9 @@ pub fn cmd_init() -> Result<(), String> {
|
|||
});
|
||||
if !config_path.exists() {
|
||||
let config_dir = config_path.parent().unwrap();
|
||||
std::fs::create_dir_all(config_dir)
|
||||
.map_err(|e| format!("create config dir: {}", e))?;
|
||||
std::fs::create_dir_all(config_dir)?;
|
||||
let example = include_str!("../../config.example.jsonl");
|
||||
std::fs::write(&config_path, example)
|
||||
.map_err(|e| format!("write config: {}", e))?;
|
||||
std::fs::write(&config_path, example)?;
|
||||
println!("Created config at {} — edit with your name and context groups",
|
||||
config_path.display());
|
||||
}
|
||||
|
|
@ -62,7 +59,7 @@ pub fn cmd_init() -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cmd_fsck() -> Result<(), String> {
|
||||
pub fn cmd_fsck() -> Result<()> {
|
||||
let mut store = store::Store::load()?;
|
||||
|
||||
// Check cache vs log consistency
|
||||
|
|
@ -96,7 +93,7 @@ pub fn cmd_fsck() -> Result<(), String> {
|
|||
if cache_issues > 0 {
|
||||
eprintln!("{} cache inconsistencies found — rebuilding from logs", cache_issues);
|
||||
store = log_store;
|
||||
store.save().map_err(|e| format!("rebuild save: {}", e))?;
|
||||
store.save().context("rebuild save")?;
|
||||
}
|
||||
|
||||
// Check node-key consistency
|
||||
|
|
@ -153,10 +150,11 @@ pub fn cmd_fsck() -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cmd_dedup(apply: bool) -> Result<(), String> {
|
||||
pub async fn cmd_dedup(apply: bool) -> Result<()> {
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
let mut store = store::Store::load()?;
|
||||
let arc = memory::access_local()?;
|
||||
let mut store = arc.lock().await;
|
||||
let duplicates = store.find_duplicates()?;
|
||||
|
||||
if duplicates.is_empty() {
|
||||
|
|
@ -329,30 +327,31 @@ pub fn cmd_dedup(apply: bool) -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_health() -> Result<(), String> {
|
||||
pub async fn cmd_health() -> Result<()> {
|
||||
let result = memory::graph_health(None).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
?;
|
||||
print!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_topology() -> Result<(), String> {
|
||||
pub async fn cmd_topology() -> Result<()> {
|
||||
let result = memory::graph_topology(None).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
?;
|
||||
print!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cmd_daily_check() -> Result<(), String> {
|
||||
let store = store::Store::load()?;
|
||||
pub async fn cmd_daily_check() -> Result<()> {
|
||||
let arc = memory::access_local()?;
|
||||
let store = arc.lock().await;
|
||||
let report = crate::neuro::daily_check(&store);
|
||||
print!("{}", report);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cmd_import(files: &[String]) -> Result<(), String> {
|
||||
pub fn cmd_import(files: &[String]) -> Result<()> {
|
||||
if files.is_empty() {
|
||||
return Err("import requires at least one file path".into());
|
||||
anyhow::bail!("import requires at least one file path");
|
||||
}
|
||||
|
||||
let mut store = store::Store::load()?;
|
||||
|
|
@ -383,7 +382,7 @@ pub fn cmd_import(files: &[String]) -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cmd_export(files: &[String], export_all: bool) -> Result<(), String> {
|
||||
pub fn cmd_export(files: &[String], export_all: bool) -> Result<()> {
|
||||
let store = store::Store::load()?;
|
||||
|
||||
let targets: Vec<String> = if export_all {
|
||||
|
|
@ -394,7 +393,7 @@ pub fn cmd_export(files: &[String], export_all: bool) -> Result<(), String> {
|
|||
files.sort();
|
||||
files
|
||||
} else if files.is_empty() {
|
||||
return Err("export requires file keys or --all".into());
|
||||
anyhow::bail!("export requires file keys or --all");
|
||||
} else {
|
||||
files.iter().map(|a| {
|
||||
a.strip_suffix(".md").unwrap_or(a).to_string()
|
||||
|
|
@ -408,7 +407,7 @@ pub fn cmd_export(files: &[String], export_all: bool) -> Result<(), String> {
|
|||
Some(content) => {
|
||||
let out_path = mem_dir.join(format!("{}.md", file_key));
|
||||
std::fs::write(&out_path, &content)
|
||||
.map_err(|e| format!("write {}: {}", out_path.display(), e))?;
|
||||
.with_context(|| format!("write {}", out_path.display()))?;
|
||||
let section_count = content.matches("<!-- mem:").count() + 1;
|
||||
println!("Exported {} ({} sections)", file_key, section_count);
|
||||
}
|
||||
|
|
@ -419,9 +418,9 @@ pub fn cmd_export(files: &[String], export_all: bool) -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_status() -> Result<(), String> {
|
||||
pub async fn cmd_status() -> Result<()> {
|
||||
let result = memory::graph_topology(None).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
?;
|
||||
print!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// cli/agent.rs — agent subcommand handlers
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use crate::hippocampus as memory;
|
||||
use crate::store;
|
||||
|
||||
pub async fn cmd_run_agent(agent: &str, count: usize, target: &[String], query: Option<&str>, dry_run: bool, _local: bool, state_dir: Option<&str>) -> Result<(), String> {
|
||||
pub async fn cmd_run_agent(agent: &str, count: usize, target: &[String], query: Option<&str>, dry_run: bool, _local: bool, state_dir: Option<&str>) -> Result<()> {
|
||||
// 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)
|
||||
|
|
@ -11,7 +11,7 @@ pub async fn cmd_run_agent(agent: &str, count: usize, target: &[String], query:
|
|||
|
||||
// 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))?;
|
||||
std::fs::create_dir_all(dir).context("create state dir")?;
|
||||
unsafe { std::env::set_var("POC_AGENT_OUTPUT_DIR", dir); }
|
||||
}
|
||||
|
||||
|
|
@ -25,14 +25,13 @@ pub async fn cmd_run_agent(agent: &str, count: usize, target: &[String], query:
|
|||
} else if let Some(q) = query {
|
||||
// Resolve query via typed API
|
||||
let q_str = format!("{} | limit:{}", q, count);
|
||||
let result = memory::memory_query(None, &q_str, None).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let result = memory::memory_query(None, &q_str, None).await?;
|
||||
let keys: Vec<String> = result.lines()
|
||||
.filter(|l| !l.is_empty() && *l != "no results")
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
if keys.is_empty() {
|
||||
return Err(format!("query returned no results: {}", q));
|
||||
bail!("query returned no results: {}", q);
|
||||
}
|
||||
println!("[{}] query matched {} nodes", agent, keys.len());
|
||||
keys
|
||||
|
|
@ -40,10 +39,12 @@ pub async fn cmd_run_agent(agent: &str, count: usize, target: &[String], query:
|
|||
vec![] // use agent's built-in query
|
||||
};
|
||||
|
||||
let arc = memory::access_local()?;
|
||||
|
||||
if !resolved_targets.is_empty() {
|
||||
for (i, key) in resolved_targets.iter().enumerate() {
|
||||
println!("[{}] [{}/{}] {}", agent, i + 1, resolved_targets.len(), key);
|
||||
let mut store = store::Store::load()?;
|
||||
let mut store = arc.lock().await;
|
||||
if let Err(e) = crate::agent::oneshot::run_one_agent(
|
||||
&mut store, agent, count, Some(&[key.clone()]),
|
||||
).await {
|
||||
|
|
@ -52,10 +53,10 @@ pub async fn cmd_run_agent(agent: &str, count: usize, target: &[String], query:
|
|||
}
|
||||
} else {
|
||||
// Local execution (--local, --debug, dry-run, or daemon unavailable)
|
||||
let mut store = store::Store::load()?;
|
||||
let mut store = arc.lock().await;
|
||||
crate::agent::oneshot::run_one_agent(
|
||||
&mut store, agent, count, None,
|
||||
).await?;
|
||||
).await.map_err(|e| anyhow::anyhow!("{}", e))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,32 +4,31 @@
|
|||
// link, link-add, link-impact, link-audit, cap-degree,
|
||||
// normalize-strengths, trace, spectral-*, organize, communities.
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use crate::hippocampus as memory;
|
||||
use crate::store;
|
||||
|
||||
pub fn cmd_cap_degree(max_deg: usize) -> Result<(), String> {
|
||||
let mut store = store::Store::load()?;
|
||||
pub async fn cmd_cap_degree(max_deg: usize) -> Result<()> {
|
||||
let arc = memory::access_local()?;
|
||||
let mut store = arc.lock().await;
|
||||
let (hubs, pruned) = store.cap_degree(max_deg)?;
|
||||
store.save()?;
|
||||
println!("Capped {} hubs, pruned {} weak Auto edges (max_degree={})", hubs, pruned, max_deg);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_normalize_strengths(apply: bool) -> Result<(), String> {
|
||||
pub async fn cmd_normalize_strengths(apply: bool) -> Result<()> {
|
||||
if apply { super::check_dry_run(); }
|
||||
let result = memory::graph_normalize_strengths(None, Some(apply)).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let result = memory::graph_normalize_strengths(None, Some(apply)).await?;
|
||||
print!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_link(key: &[String]) -> Result<(), String> {
|
||||
pub async fn cmd_link(key: &[String]) -> Result<()> {
|
||||
if key.is_empty() {
|
||||
return Err("link requires a key".into());
|
||||
bail!("link requires a key");
|
||||
}
|
||||
let key = key.join(" ");
|
||||
let links = memory::memory_links(None, &key).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let links = memory::memory_links(None, &key).await?;
|
||||
println!("Neighbors of '{}':", key);
|
||||
for link in links {
|
||||
println!(" ({:.2}) {} [w={:.2}]", link.link_strength, link.key, link.node_weight);
|
||||
|
|
@ -37,36 +36,32 @@ pub async fn cmd_link(key: &[String]) -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_link_add(source: &str, target: &str, _reason: &[String]) -> Result<(), String> {
|
||||
pub async fn cmd_link_add(source: &str, target: &str, _reason: &[String]) -> Result<()> {
|
||||
super::check_dry_run();
|
||||
let result = memory::memory_link_add(None, source, target).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let result = memory::memory_link_add(None, source, target).await?;
|
||||
println!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_link_set(source: &str, target: &str, strength: f32) -> Result<(), String> {
|
||||
pub async fn cmd_link_set(source: &str, target: &str, strength: f32) -> Result<()> {
|
||||
super::check_dry_run();
|
||||
let result = memory::memory_link_set(None, source, target, strength).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let result = memory::memory_link_set(None, source, target, strength).await?;
|
||||
println!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_link_impact(source: &str, target: &str) -> Result<(), String> {
|
||||
let result = memory::graph_link_impact(None, source, target).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
pub async fn cmd_link_impact(source: &str, target: &str) -> Result<()> {
|
||||
let result = memory::graph_link_impact(None, source, target).await?;
|
||||
print!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_trace(key: &[String]) -> Result<(), String> {
|
||||
pub async fn cmd_trace(key: &[String]) -> Result<()> {
|
||||
if key.is_empty() {
|
||||
return Err("trace requires a key".into());
|
||||
bail!("trace requires a key");
|
||||
}
|
||||
let key = key.join(" ");
|
||||
let result = memory::graph_trace(None, &key).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let result = memory::graph_trace(None, &key).await?;
|
||||
print!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -74,9 +69,8 @@ pub async fn cmd_trace(key: &[String]) -> Result<(), String> {
|
|||
/// Show communities sorted by isolation (most isolated first).
|
||||
/// Useful for finding poorly-integrated knowledge clusters that need
|
||||
/// organize agents aimed at them.
|
||||
pub async fn cmd_communities(top_n: usize, min_size: usize) -> Result<(), String> {
|
||||
let result = memory::graph_communities(None, Some(top_n), Some(min_size)).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
pub async fn cmd_communities(top_n: usize, min_size: usize) -> Result<()> {
|
||||
let result = memory::graph_communities(None, Some(top_n), Some(min_size)).await?;
|
||||
print!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,26 @@
|
|||
// cli/journal.rs — journal subcommand handlers
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use crate::hippocampus as memory;
|
||||
|
||||
pub fn cmd_tail(n: usize, full: bool, provenance: Option<&str>, dedup: bool) -> Result<(), String> {
|
||||
pub fn cmd_tail(n: usize, full: bool, provenance: Option<&str>, dedup: bool) -> Result<()> {
|
||||
let path = crate::store::nodes_path();
|
||||
if !path.exists() {
|
||||
return Err("No node log found".into());
|
||||
bail!("No node log found");
|
||||
}
|
||||
|
||||
use std::io::BufReader;
|
||||
let file = std::fs::File::open(&path)
|
||||
.map_err(|e| format!("open {}: {}", path.display(), e))?;
|
||||
.with_context(|| format!("open {}", path.display()))?;
|
||||
let mut reader = BufReader::new(file);
|
||||
|
||||
// Read all entries, keep last N
|
||||
let mut entries: Vec<crate::store::Node> = Vec::new();
|
||||
while let Ok(msg) = capnp::serialize::read_message(&mut reader, capnp::message::ReaderOptions::new()) {
|
||||
let log = msg.get_root::<crate::memory_capnp::node_log::Reader>()
|
||||
.map_err(|e| format!("read log: {}", e))?;
|
||||
.with_context(|| "read log")?;
|
||||
for node_reader in log.get_nodes()
|
||||
.map_err(|e| format!("get nodes: {}", e))? {
|
||||
.with_context(|| "get nodes")? {
|
||||
let node = crate::store::Node::from_capnp_migrate(node_reader)?;
|
||||
entries.push(node);
|
||||
}
|
||||
|
|
@ -67,9 +68,8 @@ pub fn cmd_tail(n: usize, full: bool, provenance: Option<&str>, dedup: bool) ->
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_journal_tail(n: usize, full: bool, level: u8) -> Result<(), String> {
|
||||
let entries = memory::journal_tail(None, Some(n as u64), Some(level as u64), None).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
pub async fn cmd_journal_tail(n: usize, full: bool, level: u8) -> Result<()> {
|
||||
let entries = memory::journal_tail(None, Some(n as u64), Some(level as u64), None).await?;
|
||||
for entry in entries {
|
||||
if full {
|
||||
println!("--- {} ---", entry.key);
|
||||
|
|
@ -82,15 +82,14 @@ pub async fn cmd_journal_tail(n: usize, full: bool, level: u8) -> Result<(), Str
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_journal_write(name: &str, text: &[String]) -> Result<(), String> {
|
||||
pub async fn cmd_journal_write(name: &str, text: &[String]) -> Result<()> {
|
||||
if text.is_empty() {
|
||||
return Err("journal write requires text".into());
|
||||
bail!("journal write requires text");
|
||||
}
|
||||
super::check_dry_run();
|
||||
let body = text.join(" ");
|
||||
|
||||
let result = memory::journal_new(None, name, name, &body, Some(0)).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let result = memory::journal_new(None, name, name, &body, Some(0)).await?;
|
||||
println!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,46 +3,43 @@
|
|||
// render, write, node-delete, node-rename, history, list-keys,
|
||||
// list-edges, dump-json, lookup-bump, lookups.
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use crate::hippocampus as memory;
|
||||
use crate::store;
|
||||
|
||||
pub async fn cmd_weight_set(key: &str, weight: f32) -> Result<(), String> {
|
||||
pub async fn cmd_weight_set(key: &str, weight: f32) -> Result<()> {
|
||||
super::check_dry_run();
|
||||
let result = memory::memory_weight_set(None, key, weight).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let result = memory::memory_weight_set(None, key, weight).await?;
|
||||
println!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_node_delete(key: &[String]) -> Result<(), String> {
|
||||
pub async fn cmd_node_delete(key: &[String]) -> Result<()> {
|
||||
if key.is_empty() {
|
||||
return Err("node-delete requires a key".into());
|
||||
bail!("node-delete requires a key");
|
||||
}
|
||||
super::check_dry_run();
|
||||
let key = key.join(" ");
|
||||
let result = memory::memory_delete(None, &key).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let result = memory::memory_delete(None, &key).await?;
|
||||
println!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_node_rename(old_key: &str, new_key: &str) -> Result<(), String> {
|
||||
pub async fn cmd_node_rename(old_key: &str, new_key: &str) -> Result<()> {
|
||||
super::check_dry_run();
|
||||
let result = memory::memory_rename(None, old_key, new_key).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let result = memory::memory_rename(None, old_key, new_key).await?;
|
||||
println!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_render(key: &[String]) -> Result<(), String> {
|
||||
pub async fn cmd_render(key: &[String]) -> Result<()> {
|
||||
if key.is_empty() {
|
||||
return Err("render requires a key".into());
|
||||
bail!("render requires a key");
|
||||
}
|
||||
let key = key.join(" ");
|
||||
let bare = store::strip_md_suffix(&key);
|
||||
|
||||
let rendered = memory::memory_render(None, &bare, None).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rendered = memory::memory_render(None, &bare, None).await?;
|
||||
print!("{}", rendered);
|
||||
|
||||
// Mark as seen if we're inside a Claude session (not an agent subprocess —
|
||||
|
|
@ -66,40 +63,38 @@ pub async fn cmd_render(key: &[String]) -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_history(key: &[String], full: bool) -> Result<(), String> {
|
||||
pub async fn cmd_history(key: &[String], full: bool) -> Result<()> {
|
||||
if key.is_empty() {
|
||||
return Err("history requires a key".into());
|
||||
bail!("history requires a key");
|
||||
}
|
||||
let key = key.join(" ");
|
||||
let result = memory::memory_history(None, &key, Some(full)).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let result = memory::memory_history(None, &key, Some(full)).await?;
|
||||
print!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_write(key: &[String]) -> Result<(), String> {
|
||||
pub async fn cmd_write(key: &[String]) -> Result<()> {
|
||||
if key.is_empty() {
|
||||
return Err("write requires a key (reads content from stdin)".into());
|
||||
bail!("write requires a key (reads content from stdin)");
|
||||
}
|
||||
let key = key.join(" ");
|
||||
let mut content = String::new();
|
||||
std::io::Read::read_to_string(&mut std::io::stdin(), &mut content)
|
||||
.map_err(|e| format!("read stdin: {}", e))?;
|
||||
.context("read stdin")?;
|
||||
|
||||
if content.trim().is_empty() {
|
||||
return Err("No content on stdin".into());
|
||||
bail!("No content on stdin");
|
||||
}
|
||||
super::check_dry_run();
|
||||
|
||||
let result = memory::memory_write(None, &key, &content).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let result = memory::memory_write(None, &key, &content).await?;
|
||||
println!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_edit(key: &[String]) -> Result<(), String> {
|
||||
pub async fn cmd_edit(key: &[String]) -> Result<()> {
|
||||
if key.is_empty() {
|
||||
return Err("edit requires a key".into());
|
||||
bail!("edit requires a key");
|
||||
}
|
||||
let key = key.join(" ");
|
||||
|
||||
|
|
@ -109,21 +104,21 @@ pub async fn cmd_edit(key: &[String]) -> Result<(), String> {
|
|||
|
||||
let tmp = std::env::temp_dir().join(format!("poc-memory-edit-{}.md", key.replace('/', "_")));
|
||||
std::fs::write(&tmp, &content)
|
||||
.map_err(|e| format!("write temp file: {}", e))?;
|
||||
.with_context(|| format!("write temp file {}", tmp.display()))?;
|
||||
|
||||
let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".into());
|
||||
let status = std::process::Command::new(&editor)
|
||||
.arg(&tmp)
|
||||
.status()
|
||||
.map_err(|e| format!("spawn {}: {}", editor, e))?;
|
||||
.with_context(|| format!("spawn {}", editor))?;
|
||||
|
||||
if !status.success() {
|
||||
let _ = std::fs::remove_file(&tmp);
|
||||
return Err(format!("{} exited with {}", editor, status));
|
||||
bail!("{} exited with {}", editor, status);
|
||||
}
|
||||
|
||||
let new_content = std::fs::read_to_string(&tmp)
|
||||
.map_err(|e| format!("read temp file: {}", e))?;
|
||||
.with_context(|| format!("read temp file {}", tmp.display()))?;
|
||||
let _ = std::fs::remove_file(&tmp);
|
||||
|
||||
if new_content == content {
|
||||
|
|
@ -132,34 +127,31 @@ pub async fn cmd_edit(key: &[String]) -> Result<(), String> {
|
|||
}
|
||||
|
||||
if new_content.trim().is_empty() {
|
||||
return Err("Content is empty, aborting".into());
|
||||
bail!("Content is empty, aborting");
|
||||
}
|
||||
|
||||
super::check_dry_run();
|
||||
let result = memory::memory_write(None, &key, &new_content).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let result = memory::memory_write(None, &key, &new_content).await?;
|
||||
println!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_search(keys: &[String]) -> Result<(), String> {
|
||||
pub async fn cmd_search(keys: &[String]) -> Result<()> {
|
||||
if keys.is_empty() {
|
||||
return Err("search requires seed keys".into());
|
||||
bail!("search requires seed keys");
|
||||
}
|
||||
let result = memory::memory_search(None, keys.to_vec(), None, None, None, None).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let result = memory::memory_search(None, keys.to_vec(), None, None, None, None).await?;
|
||||
print!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cmd_query(expr: &[String]) -> Result<(), String> {
|
||||
pub async fn cmd_query(expr: &[String]) -> Result<()> {
|
||||
if expr.is_empty() {
|
||||
return Err("query requires an expression (try: poc-memory query --help)".into());
|
||||
bail!("query requires an expression (try: poc-memory query --help)");
|
||||
}
|
||||
|
||||
let query_str = expr.join(" ");
|
||||
let result = memory::memory_query(None, &query_str, None).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let result = memory::memory_query(None, &query_str, None).await?;
|
||||
print!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -210,7 +202,7 @@ pub async fn get_group_content(group: &crate::config::ContextGroup, cfg: &crate:
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn cmd_load_context(stats: bool) -> Result<(), String> {
|
||||
pub async fn cmd_load_context(stats: bool) -> Result<()> {
|
||||
let cfg = crate::config::get();
|
||||
|
||||
if stats {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue