From 0e971dee615c5e371f5e6a2981152c9bc232a401 Mon Sep 17 00:00:00 2001 From: ProofOfConcept Date: Wed, 11 Mar 2026 16:53:25 -0400 Subject: [PATCH] render: show links footer for graph walkability; query: full --help render now appends a links section showing up to 15 neighbors as copy-pasteable `poc-memory render` commands, making the graph naturally walkable without memorizing node keys. query --help now documents the full language: expressions, fields, operators, pipe stages, functions, and examples. Inline help in cmd_query replaced with pointer to --help. --- poc-memory/src/main.rs | 81 +++++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/poc-memory/src/main.rs b/poc-memory/src/main.rs index 4e82b06..d3a8773 100644 --- a/poc-memory/src/main.rs +++ b/poc-memory/src/main.rs @@ -116,8 +116,49 @@ enum Command { /// Summary of memory state Status, /// Query the memory graph + #[command(after_long_help = "\ +EXPRESSIONS: + * all nodes + key ~ 'pattern' regex match on node key + content ~ 'phrase' regex match on node content + degree > 15 numeric comparison on any field + field = value exact match + field != value not equal + expr AND expr boolean AND + expr OR expr boolean OR + NOT expr negation + neighbors('key') nodes linked to key + neighbors('key') WHERE expr ... with filter on edges/nodes + +FIELDS: + key, weight, content, degree, node_type, provenance, + emotion, retrievals, uses, wrongs, created, + clustering_coefficient (cc), community_id + +OPERATORS: + > < >= <= = != ~(regex) + +PIPE STAGES: + | sort FIELD [asc] sort (desc by default) + | limit N cap results + | select F,F,... output fields as TSV + | count just show count + +FUNCTIONS: + community('key') community id of a node + degree('key') degree of a node + +EXAMPLES: + key ~ 'inner-life' substring match on keys + content ~ 'made love' full-text search + degree > 15 | sort degree | limit 10 high-degree nodes + key ~ 'journal' AND degree > 10 | count count matching nodes + neighbors('identity') WHERE strength > 0.5 | sort strength + * | sort weight asc | limit 20 lowest-weight nodes + node_type = semantic | sort degree all semantic nodes by degree +")] Query { - /// Query expression (e.g. "degree > 15 | sort degree | limit 10") + /// Query expression (e.g. "key ~ 'inner-life'") expr: Vec, }, /// Mark a memory as useful (boosts weight) @@ -2048,6 +2089,32 @@ fn cmd_render(key: &[String]) -> Result<(), String> { .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(); + for r in &store.relations { + if r.deleted { continue; } + if r.source_key == bare { + neighbors.push((&r.target_key, r.strength)); + } else if r.target_key == bare { + neighbors.push((&r.source_key, 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) + .map(|(k, _)| format!("`poc-memory render {}`", k)) + .collect(); + print!("\n\n---\nLinks:"); + for link in &shown { + println!("\n {}", link); + } + if total > 15 { + println!(" ... and {} more (`poc-memory graph link {}`)", total - 15, bare); + } + } Ok(()) } @@ -2434,17 +2501,7 @@ fn cmd_interference(threshold: f32) -> Result<(), String> { fn cmd_query(expr: &[String]) -> Result<(), String> { if expr.is_empty() { - return Err("query requires an expression\n\n\ -Expressions:\n \ - degree > 15 property filter\n \ - key ~ 'journal.*' AND degree > 10 boolean + regex\n \ - neighbors('identity') WHERE ... graph traversal\n \ - * all nodes\n\n\ -Pipe stages:\n \ - | sort FIELD [asc] sort (desc by default)\n \ - | limit N cap results\n \ - | select F,F,... output fields as TSV\n \ - | count just show count".into()); + return Err("query requires an expression (try: poc-memory query --help)".into()); } let query_str = expr.join(" ");