diff --git a/poc-memory/src/cli/node.rs b/poc-memory/src/cli/node.rs index 860a77c..7d2e536 100644 --- a/poc-memory/src/cli/node.rs +++ b/poc-memory/src/cli/node.rs @@ -198,23 +198,41 @@ pub fn cmd_render(key: &[String]) -> Result<(), String> { let node = store.nodes.get(&bare) .ok_or_else(|| format!("Node not found: {}", bare))?; - print!("{}", node.content); - - // Show links so the graph is walkable - let mut neighbors: Vec<(&str, f32)> = Vec::new(); + // Build neighbor lookup: key → strength + let mut neighbor_strengths: std::collections::HashMap<&str, f32> = std::collections::HashMap::new(); for r in &store.relations { if r.deleted { continue; } if r.source_key == bare { - neighbors.push((&r.target_key, r.strength)); + let e = neighbor_strengths.entry(&r.target_key).or_insert(0.0); + *e = e.max(r.strength); } else if r.target_key == bare { - neighbors.push((&r.source_key, r.strength)); + let e = neighbor_strengths.entry(&r.source_key).or_insert(0.0); + *e = e.max(r.strength); } } - if !neighbors.is_empty() { - neighbors.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); - neighbors.dedup_by(|a, b| a.0 == b.0); - let total = neighbors.len(); - let shown: Vec<_> = neighbors.iter().take(15) + + // Detect which neighbors are already referenced inline in the content. + // These are omitted from the footer to avoid duplication. + let mut inline_keys: std::collections::HashSet = std::collections::HashSet::new(); + for nbr_key in neighbor_strengths.keys() { + // Match `key` (backtick-quoted) or bare key after → arrow + if node.content.contains(nbr_key) { + inline_keys.insert(nbr_key.to_string()); + } + } + + print!("{}", node.content); + + // Footer: only show links NOT already referenced inline + let mut footer_neighbors: Vec<(&str, f32)> = neighbor_strengths.iter() + .filter(|(k, _)| !inline_keys.contains(**k)) + .map(|(k, s)| (*k, *s)) + .collect(); + + if !footer_neighbors.is_empty() { + footer_neighbors.sort_by(|a, b| b.1.total_cmp(&a.1)); + let total = footer_neighbors.len(); + let shown: Vec<_> = footer_neighbors.iter().take(15) .map(|(k, s)| format!("({:.2}) `poc-memory render {}`", s, k)) .collect(); print!("\n\n---\nLinks:"); diff --git a/poc-memory/src/query/parser.rs b/poc-memory/src/query/parser.rs index a07adb8..cf6e3e7 100644 --- a/poc-memory/src/query/parser.rs +++ b/poc-memory/src/query/parser.rs @@ -129,7 +129,7 @@ peg::parser! { = "WHERE" _ e:expr() { e } rule field() -> String - = s:$(['a'..='z' | 'A'..='Z' | '_']['a'..='z' | 'A'..='Z' | '0'..='9' | '_']*) { + = s:$(['a'..='z' | 'A'..='Z' | '_']['a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-']*) { s.to_string() }