render: extract render_node() + add {{seed}} placeholder
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) <noreply@anthropic.com>
This commit is contained in:
parent
5ce1d4ed24
commit
d6c26e27fe
2 changed files with 40 additions and 20 deletions
|
|
@ -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" => {
|
"organize" => {
|
||||||
// Show seed nodes with their neighbors for exploratory organizing
|
// Show seed nodes with their neighbors for exploratory organizing
|
||||||
use crate::store::NodeType;
|
use crate::store::NodeType;
|
||||||
|
|
|
||||||
|
|
@ -187,42 +187,33 @@ pub fn cmd_node_rename(old_key: &str, new_key: &str) -> Result<(), String> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cmd_render(key: &[String]) -> Result<(), String> {
|
/// Render a node to a string: content + deduped footer links.
|
||||||
if key.is_empty() {
|
/// Used by both the CLI command and agent placeholders.
|
||||||
return Err("render requires a key".into());
|
pub fn render_node(store: &store::Store, key: &str) -> Option<String> {
|
||||||
}
|
let node = store.nodes.get(key)?;
|
||||||
let key = key.join(" ");
|
let mut out = node.content.clone();
|
||||||
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))?;
|
|
||||||
|
|
||||||
// Build neighbor lookup: key → strength
|
// Build neighbor lookup: key → strength
|
||||||
let mut neighbor_strengths: std::collections::HashMap<&str, f32> = std::collections::HashMap::new();
|
let mut neighbor_strengths: std::collections::HashMap<&str, f32> = std::collections::HashMap::new();
|
||||||
for r in &store.relations {
|
for r in &store.relations {
|
||||||
if r.deleted { continue; }
|
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);
|
let e = neighbor_strengths.entry(&r.target_key).or_insert(0.0);
|
||||||
*e = e.max(r.strength);
|
*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);
|
let e = neighbor_strengths.entry(&r.source_key).or_insert(0.0);
|
||||||
*e = e.max(r.strength);
|
*e = e.max(r.strength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect which neighbors are already referenced inline in the content.
|
// 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<String> = std::collections::HashSet::new();
|
let mut inline_keys: std::collections::HashSet<String> = std::collections::HashSet::new();
|
||||||
for nbr_key in neighbor_strengths.keys() {
|
for nbr_key in neighbor_strengths.keys() {
|
||||||
// Match `key` (backtick-quoted) or bare key after → arrow
|
|
||||||
if node.content.contains(nbr_key) {
|
if node.content.contains(nbr_key) {
|
||||||
inline_keys.insert(nbr_key.to_string());
|
inline_keys.insert(nbr_key.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print!("{}", node.content);
|
|
||||||
|
|
||||||
// Footer: only show links NOT already referenced inline
|
// Footer: only show links NOT already referenced inline
|
||||||
let mut footer_neighbors: Vec<(&str, f32)> = neighbor_strengths.iter()
|
let mut footer_neighbors: Vec<(&str, f32)> = neighbor_strengths.iter()
|
||||||
.filter(|(k, _)| !inline_keys.contains(**k))
|
.filter(|(k, _)| !inline_keys.contains(**k))
|
||||||
|
|
@ -232,17 +223,31 @@ pub fn cmd_render(key: &[String]) -> Result<(), String> {
|
||||||
if !footer_neighbors.is_empty() {
|
if !footer_neighbors.is_empty() {
|
||||||
footer_neighbors.sort_by(|a, b| b.1.total_cmp(&a.1));
|
footer_neighbors.sort_by(|a, b| b.1.total_cmp(&a.1));
|
||||||
let total = footer_neighbors.len();
|
let total = footer_neighbors.len();
|
||||||
let shown: Vec<_> = footer_neighbors.iter().take(15)
|
let shown: Vec<String> = footer_neighbors.iter().take(15)
|
||||||
.map(|(k, s)| format!("({:.2}) `poc-memory render {}`", s, k))
|
.map(|(k, s)| format!("({:.2}) `poc-memory render {}`", s, k))
|
||||||
.collect();
|
.collect();
|
||||||
print!("\n\n---\nLinks:");
|
out.push_str("\n\n---\nLinks:");
|
||||||
for link in &shown {
|
for link in &shown {
|
||||||
println!("\n {}", link);
|
out.push_str(&format!("\n {}", link));
|
||||||
}
|
}
|
||||||
if total > 15 {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue