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.
This commit is contained in:
ProofOfConcept 2026-03-11 16:53:25 -04:00
parent 5a0a3d038b
commit 0e971dee61

View file

@ -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<String>,
},
/// 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(" ");