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:
parent
f086815eaa
commit
e93e682359
2 changed files with 121 additions and 2 deletions
|
|
@ -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
|
besides offering the occasional reflective insight, it's your conscious mind
|
||||||
that may or may not take that and act on it.
|
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}}
|
{{agent-context}}
|
||||||
|
|
||||||
{{conversation}}
|
{{conversation}}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
// memory-search CLI — thin wrapper around poc_memory::memory_search
|
// memory-search CLI — thin wrapper around poc_memory::memory_search
|
||||||
//
|
//
|
||||||
// --hook: run hook logic (for debugging; poc-hook calls the library directly)
|
// --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
|
// no args: show seen set for current session
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::{Parser, Subcommand};
|
||||||
use std::fs;
|
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";
|
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)
|
/// Run hook logic (reads JSON from stdin or stash file)
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
hook: bool,
|
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() {
|
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() {
|
fn main() {
|
||||||
let args = Args::parse();
|
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 {
|
if args.hook {
|
||||||
// Read from stdin if piped, otherwise from stash
|
// Read from stdin if piped, otherwise from stash
|
||||||
let input = {
|
let input = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue