diff --git a/src/cli/graph.rs b/src/cli/graph.rs index 05c7f9e..2b9790f 100644 --- a/src/cli/graph.rs +++ b/src/cli/graph.rs @@ -6,6 +6,7 @@ // trace, spectral-*, organize, interference. use crate::{store, graph, neuro, spectral}; +use crate::store::StoreView; pub fn cmd_graph() -> Result<(), String> { let store = store::Store::load()?; @@ -109,6 +110,45 @@ pub fn cmd_normalize_strengths(apply: bool) -> Result<(), String> { Ok(()) } +pub fn cmd_spread(keys: &[String], max_results: usize) -> Result<(), String> { + if keys.is_empty() { + return Err("spread requires at least one seed key".into()); + } + + let store = store::Store::load()?; + let graph = graph::build_graph_fast(&store); + let params = store.params(); + + let seeds: Vec<(String, f64)> = keys.iter() + .filter_map(|k| { + let resolved = store.resolve_key(k).ok()?; + Some((resolved, 1.0)) + }) + .collect(); + + if seeds.is_empty() { + return Err("no valid seed keys found".into()); + } + + let results = crate::search::spreading_activation( + &seeds, &graph, &store, + params.max_hops, params.edge_decay, params.min_activation, + ); + + let seed_keys: std::collections::HashSet<&str> = seeds.iter() + .map(|(k, _)| k.as_str()) + .collect(); + + for (key, score) in results.iter() + .filter(|(k, _)| !seed_keys.contains(k.as_str())) + .take(max_results) + { + println!(" {:.2} {}", score, key); + } + + Ok(()) +} + pub fn cmd_link(key: &[String]) -> Result<(), String> { if key.is_empty() { return Err("link requires a key".into()); diff --git a/src/hippocampus/query/engine.rs b/src/hippocampus/query/engine.rs index 1237008..890b879 100644 --- a/src/hippocampus/query/engine.rs +++ b/src/hippocampus/query/engine.rs @@ -1377,7 +1377,7 @@ fn run_manifold( /// sum at each node, and the combined activation map propagates on /// the next hop. This creates interference patterns — nodes where /// multiple wavefronts overlap get reinforced and radiate stronger. -fn spreading_activation( +pub fn spreading_activation( seeds: &[(String, f64)], graph: &Graph, store: &impl StoreView, diff --git a/src/thought/memory.rs b/src/thought/memory.rs index f0206a2..fdac7de 100644 --- a/src/thought/memory.rs +++ b/src/thought/memory.rs @@ -20,6 +20,14 @@ pub fn definitions() -> Vec { ToolDef::new("memory_search", "Search the memory graph by keyword.", json!({"type":"object","properties":{"query":{"type":"string","description":"Search terms"}},"required":["query"]})), + ToolDef::new("memory_search_content", + "Search the memory graph by keyword (searches node content, not just keys).", + json!({"type":"object","properties":{"query":{"type":"string","description":"Search terms"}},"required":["query"]})), + ToolDef::new("memory_spread", + "Find related nodes via spreading activation from multiple seed nodes. \ + Propagates activation through the graph and returns nodes ranked by \ + total activation. Use to find nodes that connect multiple concepts.", + json!({"type":"object","properties":{"keys":{"type":"array","items":{"type":"string"},"description":"Seed node keys to activate from"}},"required":["keys"]})), ToolDef::new("memory_links", "Show a node's neighbors with link strengths.", json!({"type":"object","properties":{"key":{"type":"string","description":"Node key"}},"required":["key"]})),