From 6a5b840db3d4a63b621a7d1397ea8fc897d395f6 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Wed, 15 Apr 2026 01:40:29 -0400 Subject: [PATCH] cli: add 'node restore' command for undeleting nodes Restores a deleted node to its last non-deleted content with proper version continuity (version number continues from absolute latest, content from last live version). Co-Authored-By: Proof of Concept --- src/cli/node.rs | 11 +++++++++++ src/hippocampus/mod.rs | 1 + src/main.rs | 12 +++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/cli/node.rs b/src/cli/node.rs index 9be9dcb..261d72f 100644 --- a/src/cli/node.rs +++ b/src/cli/node.rs @@ -32,6 +32,17 @@ pub async fn cmd_node_rename(old_key: &str, new_key: &str) -> Result<()> { Ok(()) } +pub async fn cmd_node_restore(key: &[String]) -> Result<()> { + if key.is_empty() { + bail!("node-restore requires a key"); + } + super::check_dry_run(); + let key = key.join(" "); + let result = memory::memory_restore(None, &key).await?; + println!("{}", result); + Ok(()) +} + pub async fn cmd_render(key: &[String]) -> Result<()> { if key.is_empty() { bail!("render requires a key"); diff --git a/src/hippocampus/mod.rs b/src/hippocampus/mod.rs index 095d9d4..d79640d 100644 --- a/src/hippocampus/mod.rs +++ b/src/hippocampus/mod.rs @@ -298,6 +298,7 @@ memory_tool!(memory_search, ref, keys: [Vec], max_hops: [Option], e memory_tool!(memory_link_set, mut, source: [str], target: [str], strength: [f32]); memory_tool!(memory_link_add, mut, source: [str], target: [str]); memory_tool!(memory_delete, mut, key: [str]); +memory_tool!(memory_restore, mut, key: [str]); memory_tool!(memory_history, ref, key: [str], full: [Option]); memory_tool!(memory_weight_set, mut, key: [str], weight: [f32]); memory_tool!(memory_rename, mut, old_key: [str], new_key: [str]); diff --git a/src/main.rs b/src/main.rs index 3cf2152..78bfa4f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -175,6 +175,11 @@ enum NodeCmd { /// Node key key: Vec, }, + /// Restore a deleted node to its last live state + Restore { + /// Node key + key: Vec, + }, /// Rename a node key Rename { /// Old key @@ -309,6 +314,9 @@ enum AdminCmd { Topology, /// Run consistency checks and repair Fsck, + /// Rebuild index from capnp logs (use after fsck finds issues) + #[command(name = "repair-index")] + RepairIndex, /// Find and merge duplicate nodes (same key, multiple UUIDs) Dedup { /// Apply the merge (default: dry run) @@ -397,7 +405,8 @@ impl Run for Command { impl Run for NodeCmd { async fn run(self) -> anyhow::Result<()> { match self { - Self::Delete { key } => cli::node::cmd_node_delete(&key).await, + Self::Delete { key } => cli::node::cmd_node_delete(&key).await, + Self::Restore { key } => cli::node::cmd_node_restore(&key).await, Self::Rename { old_key, new_key } => cli::node::cmd_node_rename(&old_key, &new_key).await, } } @@ -445,6 +454,7 @@ impl Run for AdminCmd { Self::Health => cli::admin::cmd_health().await, Self::Topology => cli::admin::cmd_topology().await, Self::Fsck => cli::admin::cmd_fsck().await, + Self::RepairIndex => cli::admin::cmd_repair_index().await, Self::Dedup { apply } => cli::admin::cmd_dedup(apply).await, Self::DailyCheck => cli::admin::cmd_daily_check().await, Self::LoadContext { stats } => cli::node::cmd_load_context(stats).await,