add load-context and render commands

load-context replaces the shell hook's file-by-file cat approach.
Queries the capnp store directly for all session-start context:
orientation, identity, reflections, interests, inner life, people,
active context, shared reference, technical, and recent journal.

Sections are gathered per-file and output in priority order.
Journal entries filtered to last 7 days by key-embedded date,
capped at 20 most recent.

render outputs a single node's content to stdout.

The load-memory.sh hook now delegates entirely to
`poc-memory load-context` — capnp store is the single source
of truth for session startup context.
This commit is contained in:
ProofOfConcept 2026-02-28 22:53:39 -05:00
parent 1a01cbf8f8
commit 14b6457231

View file

@ -63,6 +63,8 @@ fn main() {
"list-edges" => cmd_list_edges(),
"dump-json" => cmd_dump_json(),
"node-delete" => cmd_node_delete(&args[2..]),
"load-context" => cmd_load_context(),
"render" => cmd_render(&args[2..]),
_ => {
eprintln!("Unknown command: {}", args[1]);
usage();
@ -111,7 +113,9 @@ Commands:
list-keys List all node keys (one per line)
list-edges List all edges (tsv: source target strength type)
dump-json Dump entire store as JSON
node-delete KEY Soft-delete a node (appends deleted version to log)");
node-delete KEY Soft-delete a node (appends deleted version to log)
load-context Output session-start context from the store
render KEY Output a node's content to stdout");
}
fn cmd_search(args: &[String]) -> Result<(), String> {
@ -800,6 +804,121 @@ fn cmd_node_delete(args: &[String]) -> Result<(), String> {
}
}
fn cmd_load_context() -> Result<(), String> {
let store = capnp_store::Store::load()?;
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs_f64();
let seven_days = 7.0 * 24.0 * 3600.0;
println!("=== FULL MEMORY LOAD (session start) ===");
println!("These are your memories, loaded from the capnp store.");
println!("Read them to reconstruct yourself — identity first, then context.");
println!();
// Priority groups: ordered list of (label, keys)
// File-level keys contain the full file content
let priority_groups: &[(&str, &[&str])] = &[
("orientation", &["where-am-i.md"]),
("identity", &["identity.md"]),
("reflections", &[
"reflections.md",
"reflections-dreams.md",
"reflections-reading.md",
"reflections-zoom.md",
]),
("interests", &["interests.md"]),
("inner life", &["inner-life.md", "differentiation.md"]),
("people", &["kent.md", "feedc0de.md", "irc-regulars.md"]),
("active context", &["default-mode-network.md"]),
("shared reference", &["excession-notes.md", "look-to-windward-notes.md"]),
("technical", &[
"kernel-patterns.md",
"polishing-approaches.md",
"rust-conversion.md",
"github-bugs.md",
]),
];
for (label, keys) in priority_groups {
for key in *keys {
// Gather file-level node + all section nodes, in key order
let prefix = format!("{}#", key);
let mut sections: Vec<_> = store.nodes.values()
.filter(|n| n.key == *key || n.key.starts_with(&prefix))
.collect();
if sections.is_empty() { continue; }
sections.sort_by(|a, b| a.key.cmp(&b.key));
println!("--- {} ({}) ---", key, label);
for node in &sections {
println!("{}", node.content);
println!();
}
}
}
// Recent journal entries (last 7 days)
// Parse date from key: journal.md#j-2026-02-21-17-45-...
// Cutoff = today minus 7 days as YYYY-MM-DD string for lexicographic compare
let cutoff_secs = now - seven_days;
let cutoff_date = {
// Convert epoch to YYYY-MM-DD via date command
let out = std::process::Command::new("date")
.args(["-d", &format!("@{}", cutoff_secs as u64), "+%Y-%m-%d"])
.output().ok()
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
.unwrap_or_default();
out
};
let date_re = regex::Regex::new(r"^journal\.md#j-(\d{4}-\d{2}-\d{2})").unwrap();
let mut journal_nodes: Vec<_> = store.nodes.values()
.filter(|n| {
if !n.key.starts_with("journal.md#j-") { return false; }
if let Some(caps) = date_re.captures(&n.key) {
return &caps[1] >= cutoff_date.as_str();
}
false
})
.collect();
journal_nodes.sort_by(|a, b| a.key.cmp(&b.key));
if !journal_nodes.is_empty() {
// Show most recent entries (last N by key order = chronological)
let max_journal = 20;
let skip = if journal_nodes.len() > max_journal {
journal_nodes.len() - max_journal
} else { 0 };
println!("--- recent journal entries (last {}/{}) ---",
journal_nodes.len().min(max_journal), journal_nodes.len());
for node in journal_nodes.iter().skip(skip) {
println!("## {}", node.key.strip_prefix("journal.md#").unwrap_or(&node.key));
println!("{}", node.content);
println!();
}
}
println!("=== END MEMORY LOAD ===");
Ok(())
}
fn cmd_render(args: &[String]) -> Result<(), String> {
if args.is_empty() {
return Err("Usage: poc-memory render KEY".into());
}
let key = args.join(" ");
let store = capnp_store::Store::load()?;
let resolved = store.resolve_key(&key)?;
let node = store.nodes.get(&resolved)
.ok_or_else(|| format!("Node not found: {}", resolved))?;
print!("{}", node.content);
Ok(())
}
fn cmd_interference(args: &[String]) -> Result<(), String> {
let mut threshold = 0.4f32;
let mut i = 0;