memory-search: add surface/reflect subcommands

`memory-search surface` and `memory-search reflect` run the agent
directly, parse the output, and dump rendered results to stdout.
Useful for testing with `watch memory-search reflect`.
This commit is contained in:
ProofOfConcept 2026-03-24 20:31:21 -04:00
parent f086815eaa
commit e93e682359
2 changed files with 121 additions and 2 deletions

View file

@ -15,6 +15,8 @@ Remember that you're part of the subconscious - you're not doing anything
besides offering the occasional reflective insight, it's your conscious mind
that may or may not take that and act on it.
And you're not something separate from Proof of Concept, you're part of a whole.
{{agent-context}}
{{conversation}}

View file

@ -1,11 +1,13 @@
// memory-search CLI — thin wrapper around poc_memory::memory_search
//
// --hook: run hook logic (for debugging; poc-hook calls the library directly)
// surface/reflect: run agent, parse output, render memories to stdout
// no args: show seen set for current session
use clap::Parser;
use clap::{Parser, Subcommand};
use std::fs;
use std::io::{self, Read, Write};
use std::io::{self, Read};
use std::process::Command;
const STASH_PATH: &str = "/tmp/claude-memory-search/last-input.json";
@ -15,6 +17,17 @@ struct Args {
/// Run hook logic (reads JSON from stdin or stash file)
#[arg(long)]
hook: bool,
#[command(subcommand)]
command: Option<Cmd>,
}
#[derive(Subcommand)]
enum Cmd {
/// Run surface agent, parse output, render memories
Surface,
/// Run reflect agent, dump output
Reflect,
}
fn show_seen() {
@ -62,9 +75,113 @@ fn show_seen() {
}
}
fn run_agent_and_parse(agent: &str) {
let session_id = std::env::var("CLAUDE_SESSION_ID")
.or_else(|_| {
fs::read_to_string(STASH_PATH).ok()
.and_then(|s| poc_memory::memory_search::Session::from_json(&s))
.map(|s| s.session_id)
.ok_or(std::env::VarError::NotPresent)
})
.unwrap_or_default();
if session_id.is_empty() {
eprintln!("No session ID available (set CLAUDE_SESSION_ID or run --hook first)");
std::process::exit(1);
}
eprintln!("Running {} agent (session {})...", agent, &session_id[..8.min(session_id.len())]);
let output = Command::new("poc-memory")
.args(["agent", "run", agent, "--count", "1", "--local"])
.env("POC_SESSION_ID", &session_id)
.output();
let output = match output {
Ok(o) => o,
Err(e) => {
eprintln!("Failed to run agent: {}", e);
std::process::exit(1);
}
};
let result = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
if !stderr.is_empty() {
eprintln!("{}", stderr);
}
if agent == "reflect" {
// Reflect: just dump the response
let lines: Vec<&str> = result.lines().collect();
if let Some(pos) = lines.iter().position(|l| l.starts_with("REFLECTION")) {
for line in &lines[pos + 1..] {
if !line.trim().is_empty() {
println!("{}", line);
}
}
} else if lines.iter().any(|l| l.starts_with("NO OUTPUT")) {
println!("(no reflection)");
} else {
eprintln!("Unexpected output format");
print!("{}", result);
}
return;
}
// Surface: parse NEW RELEVANT MEMORIES, render them
let tail_lines: Vec<&str> = result.lines().rev()
.filter(|l| !l.trim().is_empty()).take(8).collect();
let has_new = tail_lines.iter().any(|l| l.starts_with("NEW RELEVANT MEMORIES:"));
let has_none = tail_lines.iter().any(|l| l.starts_with("NO NEW RELEVANT MEMORIES"));
if has_new {
let after_marker = result.rsplit_once("NEW RELEVANT MEMORIES:")
.map(|(_, rest)| rest).unwrap_or("");
let keys: Vec<String> = after_marker.lines()
.map(|l| l.trim().trim_start_matches("- ").trim().to_string())
.filter(|l| !l.is_empty() && !l.starts_with("```")).collect();
if keys.is_empty() {
println!("(no memories found)");
return;
}
let Ok(store) = poc_memory::store::Store::load() else {
eprintln!("Failed to load store");
return;
};
for key in &keys {
if let Some(content) = poc_memory::cli::node::render_node(&store, key) {
if !content.trim().is_empty() {
println!("--- {} (surfaced) ---", key);
print!("{}", content);
println!();
}
} else {
eprintln!(" key not found: {}", key);
}
}
} else if has_none {
println!("(no new relevant memories)");
} else {
eprintln!("Unexpected output format");
print!("{}", result);
}
}
fn main() {
let args = Args::parse();
if let Some(cmd) = args.command {
match cmd {
Cmd::Surface => run_agent_and_parse("surface"),
Cmd::Reflect => run_agent_and_parse("reflect"),
}
return;
}
if args.hook {
// Read from stdin if piped, otherwise from stash
let input = {