cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
// cli/admin.rs — admin subcommand handlers
|
|
|
|
|
|
2026-04-13 18:50:21 -04:00
|
|
|
use anyhow::Result;
|
2026-04-13 17:44:33 -04:00
|
|
|
use crate::hippocampus as memory;
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
use crate::hippocampus::store;
|
2026-04-12 23:19:28 -04:00
|
|
|
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
fn install_default_file(data_dir: &std::path::Path, name: &str, content: &str) -> Result<()> {
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
let path = data_dir.join(name);
|
|
|
|
|
if !path.exists() {
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
std::fs::write(&path, content)?;
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
println!("Created {}", path.display());
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
pub async fn cmd_init() -> Result<()> {
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
let cfg = crate::config::get();
|
|
|
|
|
|
|
|
|
|
// Ensure data directory exists
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
std::fs::create_dir_all(&cfg.data_dir)?;
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
|
|
|
|
|
// Install filesystem files (not store nodes)
|
|
|
|
|
install_default_file(&cfg.data_dir, "instructions.md",
|
|
|
|
|
include_str!("../../defaults/instructions.md"))?;
|
|
|
|
|
install_default_file(&cfg.data_dir, "on-consciousness.md",
|
|
|
|
|
include_str!("../../defaults/on-consciousness.md"))?;
|
|
|
|
|
|
2026-04-13 18:50:21 -04:00
|
|
|
// Seed identity node if empty
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
let arc = memory::access_local()?;
|
|
|
|
|
let mut store = arc.lock().await;
|
2026-04-13 19:49:09 -04:00
|
|
|
if !store.contains_key("identity").unwrap_or(false) {
|
2026-04-13 18:50:21 -04:00
|
|
|
let default = include_str!("../../defaults/identity.md");
|
|
|
|
|
store.upsert("identity", default)?;
|
|
|
|
|
println!("Seeded identity in store");
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
}
|
2026-04-13 18:50:21 -04:00
|
|
|
store.save()?;
|
2026-04-13 19:49:09 -04:00
|
|
|
println!("Initialized with {} nodes", store.all_keys().unwrap_or_default().len());
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
|
|
|
|
|
// Create config if none exists
|
|
|
|
|
let config_path = std::env::var("POC_MEMORY_CONFIG")
|
|
|
|
|
.map(std::path::PathBuf::from)
|
|
|
|
|
.unwrap_or_else(|_| {
|
2026-04-02 19:57:40 -04:00
|
|
|
dirs::home_dir().unwrap_or_default()
|
2026-03-27 21:32:28 -04:00
|
|
|
.join(".consciousness/config.jsonl")
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
});
|
|
|
|
|
if !config_path.exists() {
|
|
|
|
|
let config_dir = config_path.parent().unwrap();
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
std::fs::create_dir_all(config_dir)?;
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
let example = include_str!("../../config.example.jsonl");
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
std::fs::write(&config_path, example)?;
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
println!("Created config at {} — edit with your name and context groups",
|
|
|
|
|
config_path.display());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
println!("Done. Run `poc-memory load-context --stats` to verify.");
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-13 18:09:02 -04:00
|
|
|
pub async fn cmd_fsck() -> Result<()> {
|
Replace rkyv/bincode caching with redb indices
Remove three-tier loading (rkyv snapshot, bincode cache, capnp replay)
in favor of direct capnp log replay + redb for indexed access.
- Remove all rkyv derives from types (Node, Relation, enums, etc.)
- Remove Snapshot struct, RKYV_MAGIC, CACHE_MAGIC constants
- Remove load_snapshot_mmap(), save(), save_snapshot()
- Remove MmapView, AnyView from view.rs (keep StoreView trait)
- Simplify Store::load() to just replay capnp logs
- Add db.rs with redb schema: nodes, uuid_to_key, visits, transcript_progress
- Simplify cmd_fsck to just check capnp integrity + graph health
capnp logs remain source of truth; redb indices will be rebuilt on demand.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-13 18:30:58 -04:00
|
|
|
// Check/repair capnp log integrity first
|
|
|
|
|
store::fsck()?;
|
|
|
|
|
|
2026-04-13 18:09:02 -04:00
|
|
|
let arc = memory::access_local()?;
|
|
|
|
|
let mut store = arc.lock().await;
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
|
|
|
|
|
// Check node-key consistency
|
|
|
|
|
let mut issues = 0;
|
2026-04-13 19:49:09 -04:00
|
|
|
let all_keys = store.all_keys().unwrap_or_default();
|
|
|
|
|
for key in &all_keys {
|
|
|
|
|
if let Ok(Some(node)) = store.get_node(key) {
|
|
|
|
|
if key != &node.key {
|
|
|
|
|
eprintln!("MISMATCH: map key '{}' vs node.key '{}'", key, node.key);
|
|
|
|
|
issues += 1;
|
|
|
|
|
}
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check edge endpoints
|
|
|
|
|
let mut dangling = 0;
|
|
|
|
|
for rel in &store.relations {
|
|
|
|
|
if rel.deleted { continue; }
|
2026-04-13 19:49:09 -04:00
|
|
|
if !store.contains_key(&rel.source_key).unwrap_or(false) {
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
eprintln!("DANGLING: edge source '{}'", rel.source_key);
|
|
|
|
|
dangling += 1;
|
|
|
|
|
}
|
2026-04-13 19:49:09 -04:00
|
|
|
if !store.contains_key(&rel.target_key).unwrap_or(false) {
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
eprintln!("DANGLING: edge target '{}'", rel.target_key);
|
|
|
|
|
dangling += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Prune orphan edges
|
|
|
|
|
let mut to_tombstone = Vec::new();
|
|
|
|
|
for rel in &store.relations {
|
|
|
|
|
if rel.deleted { continue; }
|
2026-04-13 19:49:09 -04:00
|
|
|
if !store.contains_key(&rel.source_key).unwrap_or(false)
|
|
|
|
|
|| !store.contains_key(&rel.target_key).unwrap_or(false) {
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
let mut tombstone = rel.clone();
|
|
|
|
|
tombstone.deleted = true;
|
|
|
|
|
tombstone.version += 1;
|
|
|
|
|
to_tombstone.push(tombstone);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !to_tombstone.is_empty() {
|
|
|
|
|
let count = to_tombstone.len();
|
|
|
|
|
store.append_relations(&to_tombstone)?;
|
|
|
|
|
for t in &to_tombstone {
|
|
|
|
|
if let Some(r) = store.relations.iter_mut().find(|r| r.uuid == t.uuid) {
|
|
|
|
|
r.deleted = true;
|
|
|
|
|
r.version = t.version;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
eprintln!("Pruned {} orphan edges", count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let g = store.build_graph();
|
Replace rkyv/bincode caching with redb indices
Remove three-tier loading (rkyv snapshot, bincode cache, capnp replay)
in favor of direct capnp log replay + redb for indexed access.
- Remove all rkyv derives from types (Node, Relation, enums, etc.)
- Remove Snapshot struct, RKYV_MAGIC, CACHE_MAGIC constants
- Remove load_snapshot_mmap(), save(), save_snapshot()
- Remove MmapView, AnyView from view.rs (keep StoreView trait)
- Simplify Store::load() to just replay capnp logs
- Add db.rs with redb schema: nodes, uuid_to_key, visits, transcript_progress
- Simplify cmd_fsck to just check capnp integrity + graph health
capnp logs remain source of truth; redb indices will be rebuilt on demand.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-13 18:30:58 -04:00
|
|
|
println!("fsck: {} nodes, {} edges, {} issues, {} dangling",
|
2026-04-13 19:49:09 -04:00
|
|
|
all_keys.len(), g.edge_count(), issues, dangling);
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
pub async fn cmd_dedup(apply: bool) -> Result<()> {
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
|
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
let arc = memory::access_local()?;
|
|
|
|
|
let mut store = arc.lock().await;
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
let duplicates = store.find_duplicates()?;
|
|
|
|
|
|
|
|
|
|
if duplicates.is_empty() {
|
|
|
|
|
println!("No duplicate keys found.");
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Count edges per UUID
|
|
|
|
|
let mut edges_by_uuid: HashMap<[u8; 16], usize> = HashMap::new();
|
|
|
|
|
for rel in &store.relations {
|
|
|
|
|
if rel.deleted { continue; }
|
|
|
|
|
*edges_by_uuid.entry(rel.source).or_default() += 1;
|
|
|
|
|
*edges_by_uuid.entry(rel.target).or_default() += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut identical_groups = Vec::new();
|
|
|
|
|
let mut diverged_groups = Vec::new();
|
|
|
|
|
|
|
|
|
|
for (key, mut nodes) in duplicates {
|
|
|
|
|
// Sort by version descending so highest-version is first
|
|
|
|
|
nodes.sort_by(|a, b| b.version.cmp(&a.version));
|
|
|
|
|
|
|
|
|
|
// Check if all copies have identical content
|
|
|
|
|
let all_same = nodes.windows(2).all(|w| w[0].content == w[1].content);
|
|
|
|
|
|
|
|
|
|
let info: Vec<_> = nodes.iter().map(|n| {
|
|
|
|
|
let edge_count = edges_by_uuid.get(&n.uuid).copied().unwrap_or(0);
|
|
|
|
|
(n.clone(), edge_count)
|
|
|
|
|
}).collect();
|
|
|
|
|
|
|
|
|
|
if all_same {
|
|
|
|
|
identical_groups.push((key, info));
|
|
|
|
|
} else {
|
|
|
|
|
diverged_groups.push((key, info));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Report
|
|
|
|
|
println!("=== Duplicate key report ===\n");
|
|
|
|
|
println!("{} identical groups, {} diverged groups\n",
|
|
|
|
|
identical_groups.len(), diverged_groups.len());
|
|
|
|
|
|
|
|
|
|
if !identical_groups.is_empty() {
|
|
|
|
|
println!("── Identical (safe to auto-merge) ──");
|
|
|
|
|
for (key, copies) in &identical_groups {
|
|
|
|
|
let total_edges: usize = copies.iter().map(|c| c.1).sum();
|
|
|
|
|
println!(" {} ({} copies, {} total edges)", key, copies.len(), total_edges);
|
|
|
|
|
for (node, edges) in copies {
|
|
|
|
|
let uuid_hex = node.uuid.iter().map(|b| format!("{:02x}", b)).collect::<String>();
|
|
|
|
|
println!(" v{} uuid={}.. edges={}", node.version, &uuid_hex[..8], edges);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
println!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !diverged_groups.is_empty() {
|
|
|
|
|
println!("── Diverged (need review) ──");
|
|
|
|
|
for (key, copies) in &diverged_groups {
|
|
|
|
|
let total_edges: usize = copies.iter().map(|c| c.1).sum();
|
|
|
|
|
println!(" {} ({} copies, {} total edges)", key, copies.len(), total_edges);
|
|
|
|
|
for (node, edges) in copies {
|
|
|
|
|
let uuid_hex = node.uuid.iter().map(|b| format!("{:02x}", b)).collect::<String>();
|
|
|
|
|
let preview: String = node.content.chars().take(80).collect();
|
|
|
|
|
println!(" v{} uuid={}.. edges={} | {}{}",
|
|
|
|
|
node.version, &uuid_hex[..8], edges, preview,
|
|
|
|
|
if node.content.len() > 80 { "..." } else { "" });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
println!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !apply {
|
|
|
|
|
let total_dupes: usize = identical_groups.iter().chain(diverged_groups.iter())
|
|
|
|
|
.map(|(_, copies)| copies.len() - 1)
|
|
|
|
|
.sum();
|
|
|
|
|
println!("Dry run: {} duplicate nodes would be merged. Use --apply to execute.", total_dupes);
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Merge all groups: identical + diverged
|
|
|
|
|
// For diverged: keep the copy with most edges (it's the one that got
|
|
|
|
|
// woven into the graph — the version that lived). Fall back to highest version.
|
|
|
|
|
let all_groups: Vec<_> = identical_groups.into_iter()
|
2026-03-21 19:42:38 -04:00
|
|
|
.chain(diverged_groups)
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
let mut merged = 0usize;
|
|
|
|
|
let mut edges_redirected = 0usize;
|
|
|
|
|
let mut edges_deduped = 0usize;
|
|
|
|
|
|
|
|
|
|
for (_key, mut copies) in all_groups {
|
|
|
|
|
// Pick survivor: most edges first, then highest version
|
|
|
|
|
copies.sort_by(|a, b| b.1.cmp(&a.1).then(b.0.version.cmp(&a.0.version)));
|
|
|
|
|
|
|
|
|
|
let survivor_uuid = copies[0].0.uuid;
|
|
|
|
|
let doomed_uuids: Vec<[u8; 16]> = copies[1..].iter().map(|c| c.0.uuid).collect();
|
|
|
|
|
|
|
|
|
|
// Redirect edges from doomed UUIDs to survivor
|
|
|
|
|
let mut updated_rels = Vec::new();
|
|
|
|
|
for rel in &mut store.relations {
|
|
|
|
|
if rel.deleted { continue; }
|
|
|
|
|
let mut changed = false;
|
|
|
|
|
if doomed_uuids.contains(&rel.source) {
|
|
|
|
|
rel.source = survivor_uuid;
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
if doomed_uuids.contains(&rel.target) {
|
|
|
|
|
rel.target = survivor_uuid;
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
if changed {
|
|
|
|
|
rel.version += 1;
|
|
|
|
|
updated_rels.push(rel.clone());
|
|
|
|
|
edges_redirected += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Dedup edges: same (source, target, rel_type) → keep highest strength
|
|
|
|
|
let mut seen: HashSet<([u8; 16], [u8; 16], String)> = HashSet::new();
|
|
|
|
|
let mut to_tombstone_rels = Vec::new();
|
|
|
|
|
// Sort by strength descending so we keep the strongest
|
|
|
|
|
let mut rels_with_idx: Vec<(usize, &store::Relation)> = store.relations.iter()
|
|
|
|
|
.enumerate()
|
|
|
|
|
.filter(|(_, r)| !r.deleted && (r.source == survivor_uuid || r.target == survivor_uuid))
|
|
|
|
|
.collect();
|
|
|
|
|
rels_with_idx.sort_by(|a, b| b.1.strength.total_cmp(&a.1.strength));
|
|
|
|
|
|
|
|
|
|
for (idx, rel) in &rels_with_idx {
|
|
|
|
|
let edge_key = (rel.source, rel.target, format!("{:?}", rel.rel_type));
|
|
|
|
|
if !seen.insert(edge_key) {
|
|
|
|
|
to_tombstone_rels.push(*idx);
|
|
|
|
|
edges_deduped += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for &idx in &to_tombstone_rels {
|
|
|
|
|
store.relations[idx].deleted = true;
|
|
|
|
|
store.relations[idx].version += 1;
|
|
|
|
|
updated_rels.push(store.relations[idx].clone());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Tombstone doomed nodes
|
|
|
|
|
let mut tombstones = Vec::new();
|
|
|
|
|
for (doomed_node, _) in &copies[1..] {
|
|
|
|
|
let mut t = doomed_node.clone();
|
|
|
|
|
t.deleted = true;
|
|
|
|
|
t.version += 1;
|
|
|
|
|
tombstones.push(t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
store.append_nodes(&tombstones)?;
|
|
|
|
|
if !updated_rels.is_empty() {
|
|
|
|
|
store.append_relations(&updated_rels)?;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-13 19:49:09 -04:00
|
|
|
// Remove doomed nodes from index
|
|
|
|
|
for (doomed_node, _) in &copies[1..] {
|
|
|
|
|
store.remove_from_index(&doomed_node.key, &doomed_node.uuid)?;
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
merged += doomed_uuids.len();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove tombstoned relations from cache
|
|
|
|
|
store.relations.retain(|r| !r.deleted);
|
|
|
|
|
store.save()?;
|
|
|
|
|
|
|
|
|
|
println!("Merged {} duplicates, redirected {} edges, deduped {} duplicate edges",
|
|
|
|
|
merged, edges_redirected, edges_deduped);
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
pub async fn cmd_health() -> Result<()> {
|
2026-04-13 13:26:22 -04:00
|
|
|
let result = memory::graph_health(None).await
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
?;
|
2026-04-12 23:37:05 -04:00
|
|
|
print!("{}", result);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
pub async fn cmd_topology() -> Result<()> {
|
2026-04-13 13:26:22 -04:00
|
|
|
let result = memory::graph_topology(None).await
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
?;
|
2026-04-12 23:37:05 -04:00
|
|
|
print!("{}", result);
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
pub async fn cmd_daily_check() -> Result<()> {
|
|
|
|
|
let arc = memory::access_local()?;
|
|
|
|
|
let store = arc.lock().await;
|
cli: extract agent and admin commands from main.rs
Move agent handlers (consolidate, replay, digest, experience-mine,
fact-mine, knowledge-loop, apply-*) into cli/agent.rs.
Move admin handlers (init, fsck, dedup, bulk-rename, health,
daily-check, import, export) into cli/admin.rs.
Functions tightly coupled to Clap types (cmd_daemon, cmd_digest,
cmd_apply_agent, cmd_experience_mine) remain in main.rs.
main.rs: 3130 → 1586 lines (49% reduction).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-03-14 18:06:27 -04:00
|
|
|
let report = crate::neuro::daily_check(&store);
|
|
|
|
|
print!("{}", report);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
pub async fn cmd_status() -> Result<()> {
|
2026-04-13 13:26:22 -04:00
|
|
|
let result = memory::graph_topology(None).await
|
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>
2026-04-13 18:05:04 -04:00
|
|
|
?;
|
2026-04-12 23:37:05 -04:00
|
|
|
print!("{}", result);
|
2026-04-12 23:01:39 -04:00
|
|
|
Ok(())
|
|
|
|
|
}
|