move Claude Code-specific code from thalamus/ to claude/
Separates the Claude-specific daemon (idle timer, tmux pane detection, prompt injection, RPC server, session hooks) from the universal infrastructure (channels, supervisor, notify, daemon protocol). thalamus/ now contains only substrate-independent code: the channel client/supervisor, notification system, daemon_capnp protocol, and shared helpers (now(), home()). claude/ contains: idle.rs, tmux.rs, context.rs, rpc.rs, config.rs, hook.rs (moved from subconscious/), plus the daemon CLI and server startup code from thalamus/mod.rs. All re-exports preserved for backward compatibility. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
36afa90cdb
commit
dd7f1e3f86
19 changed files with 627 additions and 612 deletions
|
|
@ -1,220 +0,0 @@
|
|||
// 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, Subcommand};
|
||||
use std::fs;
|
||||
use std::io::{self, Read};
|
||||
use std::process::Command;
|
||||
|
||||
fn stash_path() -> std::path::PathBuf {
|
||||
poc_memory::store::memory_dir().join("sessions/last-input.json")
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "memory-search")]
|
||||
struct Args {
|
||||
/// Run hook logic (reads JSON from stdin or stash file)
|
||||
#[arg(long)]
|
||||
hook: bool,
|
||||
|
||||
/// Session ID (overrides stash file; for multiple concurrent sessions)
|
||||
#[arg(long)]
|
||||
session: Option<String>,
|
||||
|
||||
#[command(subcommand)]
|
||||
command: Option<Cmd>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Cmd {
|
||||
/// Run surface agent, parse output, render memories
|
||||
Surface,
|
||||
/// Run reflect agent, dump output
|
||||
Reflect,
|
||||
}
|
||||
|
||||
fn resolve_session(session_arg: &Option<String>) -> Option<poc_memory::memory_search::HookSession> {
|
||||
use poc_memory::memory_search::HookSession;
|
||||
|
||||
if let Some(id) = session_arg {
|
||||
return HookSession::from_id(id.clone());
|
||||
}
|
||||
let input = fs::read_to_string(stash_path()).ok()?;
|
||||
HookSession::from_json(&input)
|
||||
}
|
||||
|
||||
fn show_seen(session_arg: &Option<String>) {
|
||||
let Some(session) = resolve_session(session_arg) else {
|
||||
eprintln!("No session state available (use --session ID)");
|
||||
return;
|
||||
};
|
||||
|
||||
println!("Session: {}", session.session_id);
|
||||
|
||||
if let Ok(cookie) = fs::read_to_string(&session.path("cookie")) {
|
||||
println!("Cookie: {}", cookie.trim());
|
||||
}
|
||||
|
||||
match fs::read_to_string(&session.path("compaction")) {
|
||||
Ok(s) => {
|
||||
let offset: u64 = s.trim().parse().unwrap_or(0);
|
||||
let ts = poc_memory::transcript::compaction_timestamp(&session.transcript_path, offset);
|
||||
match ts {
|
||||
Some(t) => println!("Last compaction: offset {} ({})", offset, t),
|
||||
None => println!("Last compaction: offset {}", offset),
|
||||
}
|
||||
}
|
||||
Err(_) => println!("Last compaction: none detected"),
|
||||
}
|
||||
|
||||
let pending = fs::read_dir(&session.path("chunks")).ok()
|
||||
.map(|d| d.flatten().count()).unwrap_or(0);
|
||||
if pending > 0 {
|
||||
println!("Pending chunks: {}", pending);
|
||||
}
|
||||
|
||||
for (label, suffix) in [("Current seen set", ""), ("Previous seen set (pre-compaction)", "-prev")] {
|
||||
let path = session.state_dir.join(format!("seen{}-{}", suffix, session.session_id));
|
||||
let content = fs::read_to_string(&path).unwrap_or_default();
|
||||
let lines: Vec<&str> = content.lines().filter(|s| !s.is_empty()).collect();
|
||||
if lines.is_empty() { continue; }
|
||||
|
||||
println!("\n{} ({}):", label, lines.len());
|
||||
for line in &lines { println!(" {}", line); }
|
||||
}
|
||||
}
|
||||
|
||||
fn run_agent_and_parse(agent: &str, session_arg: &Option<String>) {
|
||||
let session_id = session_arg.clone()
|
||||
.or_else(|| std::env::var("CLAUDE_SESSION_ID").ok())
|
||||
.or_else(|| {
|
||||
fs::read_to_string(stash_path()).ok()
|
||||
.and_then(|s| poc_memory::memory_search::HookSession::from_json(&s))
|
||||
.map(|s| s.session_id)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
if session_id.is_empty() {
|
||||
eprintln!("No session ID available (use --session ID, 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);
|
||||
}
|
||||
|
||||
// Extract the final response — after the last "=== RESPONSE ===" marker
|
||||
let response = result.rsplit_once("=== RESPONSE ===")
|
||||
.map(|(_, rest)| rest.trim())
|
||||
.unwrap_or(result.trim());
|
||||
|
||||
if agent == "reflect" {
|
||||
// Reflect: find REFLECTION marker and dump what follows
|
||||
if let Some(pos) = response.find("REFLECTION") {
|
||||
let after = &response[pos + "REFLECTION".len()..];
|
||||
let text = after.trim();
|
||||
if !text.is_empty() {
|
||||
println!("{}", text);
|
||||
}
|
||||
} else if response.contains("NO OUTPUT") {
|
||||
println!("(no reflection)");
|
||||
} else {
|
||||
eprintln!("Unexpected output format");
|
||||
println!("{}", response);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Surface: parse NEW RELEVANT MEMORIES, render them
|
||||
let tail_lines: Vec<&str> = response.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 = response.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!("{}", response);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
if let Some(cmd) = args.command {
|
||||
match cmd {
|
||||
Cmd::Surface => run_agent_and_parse("surface", &args.session),
|
||||
Cmd::Reflect => run_agent_and_parse("reflect", &args.session),
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if args.hook {
|
||||
// Read from stdin if piped, otherwise from stash
|
||||
let input = {
|
||||
let mut buf = String::new();
|
||||
io::stdin().read_to_string(&mut buf).ok();
|
||||
if buf.trim().is_empty() {
|
||||
fs::read_to_string(stash_path()).unwrap_or_default()
|
||||
} else {
|
||||
let _ = fs::create_dir_all(stash_path().parent().unwrap());
|
||||
let _ = fs::write(stash_path(), &buf);
|
||||
buf
|
||||
}
|
||||
};
|
||||
|
||||
let output = poc_memory::memory_search::run_hook(&input);
|
||||
print!("{}", output);
|
||||
} else {
|
||||
show_seen(&args.session)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue