diff --git a/poc-memory/agents/reflect.agent b/poc-memory/agents/reflect.agent index de8c619..060e3fc 100644 --- a/poc-memory/agents/reflect.agent +++ b/poc-memory/agents/reflect.agent @@ -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}} diff --git a/poc-memory/src/bin/memory-search.rs b/poc-memory/src/bin/memory-search.rs index d0c2cd2..15563ca 100644 --- a/poc-memory/src/bin/memory-search.rs +++ b/poc-memory/src/bin/memory-search.rs @@ -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, +} + +#[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 = 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 = {