//! Tool for reading a single memory entry or listing the whole memory index. use std::fmt::Write; use serde_json::{json, Value}; use anyhow::{Result, anyhow}; use super::super::Tool; use super::super::ToolCtx; use crate::model::memory::Memory; /// Tool that reads one memory entry by name, or lists all entries when name is omitted. pub struct Recall; impl Tool for Recall { fn name(&self) -> &'static str { "recall" } fn description(&self) -> &'static str { "Read memory entries. Pass a name to read a specific entry, or omit name to list all entries in the memory index. Use this to find stored lessons, references, and project conventions." } fn parameters(&self) -> Value { json!({ "type": "object", "properties": { "name": { "type": "string", "description": "Optional: exact name of a specific memory entry to read. If omitted, lists all entries." } } }) } /// Read a specific memory entry, or fall back to listing all entries. /// /// Flow: if `name` present and non-empty → `Memory::read` and format as frontmatter /// + body; otherwise → `list_all`. /// /// Return: formatted memory content, or the full index listing. fn run(&self, ctx: &ToolCtx, args: &Value) -> Result { if let Some(name) = args.get("name").and_then(|v| v.as_str()) { if name.is_empty() { return Ok(list_all(ctx)); } let memory = Memory::read(&ctx.memory_dir, name) .map_err(|e| anyhow!("memory '{name}' not found: {e}"))?; Ok(format!( "---\nname: {}\ndescription: {}\nkind: {}\nlifecycle: {}\n---\n\n{}", memory.name, memory.description, memory.kind, memory.lifecycle, memory.content, )) } else { Ok(list_all(ctx)) } } } /// List every memory entry in `ctx.memory_dir` as a one-line summary index. /// /// Flow: `Memory::list` names → for each, try `Memory::read` for kind/description → /// fall back to bare name if the file can't be parsed. /// /// Return: `Ok` with the formatted index (never fails; missing dir yields "(no memory entries)"). fn list_all(ctx: &ToolCtx) -> String { let names = Memory::list(&ctx.memory_dir); if names.is_empty() { return "(no memory entries)".to_string(); } let mut lines = String::new(); for name in &names { if let Ok(mem) = Memory::read(&ctx.memory_dir, name) { let _ = writeln!(lines, "- {} [{}]: {}", name, mem.kind, mem.description); } else { let _ = writeln!(lines, "- {name}"); } } lines }