2026-07-12 11:28:39 +07:00
//! Tool for reading a single memory entry or listing the whole memory index.
2026-07-11 20:44:15 +07:00
use serde_json ::{ json , Value };
use anyhow ::{ Result , anyhow };
use super ::super ::Tool ;
use super ::super ::ToolCtx ;
use crate ::model ::memory ::Memory ;
2026-07-12 11:28:39 +07:00
/// Tool that reads one memory entry by name, or lists all entries when name is omitted.
2026-07-11 20:44:15 +07:00
pub struct Recall ;
impl Tool for Recall {
fn name ( & self ) -> & 'static str {
"recall"
}
fn description ( & self ) -> & 'static str {
2026-07-12 12:21:46 +07:00
"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."
2026-07-11 20:44:15 +07:00
}
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."
}
}
})
}
2026-07-12 11:28:39 +07:00
/// 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.
2026-07-11 20:44:15 +07:00
fn run ( & self , ctx : & ToolCtx , args : & Value ) -> Result < String > {
if let Some ( name ) = args . get ( "name" ). and_then ( | v | v . as_str ()) {
if name . is_empty () {
return list_all ( ctx );
}
let memory = Memory ::read ( & ctx . memory_dir , name )
. map_err ( | e | anyhow! ( "memory '{}' not found: {}" , name , e )) ? ;
Ok ( format! (
"--- \n name: {} \n description: {} \n kind: {} \n lifecycle: {} \n --- \n\n {} " ,
memory . name ,
memory . description ,
memory . kind ,
memory . lifecycle ,
memory . content ,
))
} else {
list_all ( ctx )
}
}
}
2026-07-12 11:28:39 +07:00
/// 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)").
2026-07-11 20:44:15 +07:00
fn list_all ( ctx : & ToolCtx ) -> Result < String > {
let names = Memory ::list ( & ctx . memory_dir );
if names . is_empty () {
return Ok ( "(no memory entries)" . to_string ());
}
let mut lines = format! ( "Memory index ( {} entries): \n " , names . len ());
for name in & names {
if let Ok ( mem ) = Memory ::read ( & ctx . memory_dir , name ) {
lines . push_str ( & format! ( "- {} [ {} ]: {} \n " , name , mem . kind , mem . description ));
} else {
lines . push_str ( & format! ( "- {} \n " , name ));
}
}
Ok ( lines )
}