From d6c26e27fe05fb48a540e226ac18b94cacdbd293 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Fri, 20 Mar 2026 13:47:14 -0400 Subject: [PATCH] render: extract render_node() + add {{seed}} placeholder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor cmd_render into render_node() that returns a String — reusable by both the CLI and agent placeholders. Add {{seed}} placeholder: renders each seed node using the same output as poc-memory render (content + deduped footer links). Agents see exactly what a human sees — no special formatting. Co-Authored-By: Claude Opus 4.6 (1M context) --- poc-memory/src/agents/defs.rs | 15 ++++++++++++ poc-memory/src/cli/node.rs | 45 +++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/poc-memory/src/agents/defs.rs b/poc-memory/src/agents/defs.rs index d94790e..868a952 100644 --- a/poc-memory/src/agents/defs.rs +++ b/poc-memory/src/agents/defs.rs @@ -164,6 +164,21 @@ fn resolve( }) } + // seed — render output for each seed node (content + deduped links) + "seed" => { + let mut text = String::new(); + let mut result_keys = Vec::new(); + for key in keys { + if let Some(rendered) = crate::cli::node::render_node(store, key) { + if !text.is_empty() { text.push_str("\n\n---\n\n"); } + text.push_str(&format!("## {}\n\n{}", key, rendered)); + result_keys.push(key.clone()); + } + } + if text.is_empty() { return None; } + Some(Resolved { text, keys: result_keys }) + } + "organize" => { // Show seed nodes with their neighbors for exploratory organizing use crate::store::NodeType; diff --git a/poc-memory/src/cli/node.rs b/poc-memory/src/cli/node.rs index ea261fb..db31b6f 100644 --- a/poc-memory/src/cli/node.rs +++ b/poc-memory/src/cli/node.rs @@ -187,42 +187,33 @@ pub fn cmd_node_rename(old_key: &str, new_key: &str) -> Result<(), String> { Ok(()) } -pub fn cmd_render(key: &[String]) -> Result<(), String> { - if key.is_empty() { - return Err("render requires a key".into()); - } - let key = key.join(" "); - let store = store::Store::load()?; - let bare = store::strip_md_suffix(&key); - - let node = store.nodes.get(&bare) - .ok_or_else(|| format!("Node not found: {}", bare))?; +/// Render a node to a string: content + deduped footer links. +/// Used by both the CLI command and agent placeholders. +pub fn render_node(store: &store::Store, key: &str) -> Option { + let node = store.nodes.get(key)?; + let mut out = node.content.clone(); // 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 { + if r.source_key == key { let e = neighbor_strengths.entry(&r.target_key).or_insert(0.0); *e = e.max(r.strength); - } else if r.target_key == bare { + } else if r.target_key == key { let e = neighbor_strengths.entry(&r.source_key).or_insert(0.0); *e = e.max(r.strength); } } // 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)) @@ -232,17 +223,31 @@ pub fn cmd_render(key: &[String]) -> Result<(), String> { 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) + let shown: Vec = footer_neighbors.iter().take(15) .map(|(k, s)| format!("({:.2}) `poc-memory render {}`", s, k)) .collect(); - print!("\n\n---\nLinks:"); + out.push_str("\n\n---\nLinks:"); for link in &shown { - println!("\n {}", link); + out.push_str(&format!("\n {}", link)); } if total > 15 { - println!(" ... and {} more (`poc-memory graph link {}`)", total - 15, bare); + out.push_str(&format!("\n ... and {} more (`poc-memory graph link {}`)", total - 15, key)); } } + Some(out) +} + +pub fn cmd_render(key: &[String]) -> Result<(), String> { + if key.is_empty() { + return Err("render requires a key".into()); + } + let key = key.join(" "); + let store = store::Store::load()?; + let bare = store::strip_md_suffix(&key); + + let rendered = render_node(&store, &bare) + .ok_or_else(|| format!("Node not found: {}", bare))?; + print!("{}", rendered); Ok(()) }