remem-ai 0.4.4

Persistent memory for Claude Code — single binary, zero subprocesses
Documentation
use anyhow::Result;

use crate::db;
use crate::memory::format::{xml_escape_attr, xml_escape_text, OBSERVATION_TYPES};

pub(crate) fn build_existing_context(conn: &rusqlite::Connection, project: &str) -> Result<String> {
    let recent_obs = db::query_observations(conn, project, OBSERVATION_TYPES, 10)?;
    let recent_mem = crate::memory::list_memories(conn, project, None, 10, 0, false, None)?;

    if recent_obs.is_empty() && recent_mem.is_empty() {
        return Ok(String::new());
    }

    let mut buf = String::from("<existing_memories>\n");

    for obs in &recent_obs {
        let title_attr = obs
            .title
            .as_deref()
            .map(|title| format!(" title=\"{}\"", xml_escape_attr(title)))
            .unwrap_or_default();
        let body = obs
            .subtitle
            .as_deref()
            .map(xml_escape_text)
            .unwrap_or_default();
        buf.push_str(&format!(
            "<memory type=\"{}\" source=\"observation\"{}>{}</memory>\n",
            xml_escape_attr(&obs.r#type),
            title_attr,
            body,
        ));
    }

    for mem in &recent_mem {
        let preview = mem
            .text
            .lines()
            .next()
            .map(|line| db::truncate_str(line, 120))
            .unwrap_or("");
        buf.push_str(&format!(
            "<memory type=\"{}\" source=\"memory\" title=\"{}\">{}</memory>\n",
            xml_escape_attr(&mem.memory_type),
            xml_escape_attr(&mem.title),
            xml_escape_text(preview),
        ));
    }

    buf.push_str("</existing_memories>\n");
    Ok(buf)
}

pub(crate) fn build_session_events_xml(batch: &[db::PendingObservation]) -> String {
    let mut events = String::new();
    for (index, pending) in batch.iter().enumerate() {
        events.push_str(&format!(
            "<event index=\"{}\">\n\
             <tool>{}</tool>\n\
             <working_directory>{}</working_directory>\n\
             <parameters>{}</parameters>\n\
             <outcome>{}</outcome>\n\
             </event>\n",
            index + 1,
            xml_escape_text(&pending.tool_name),
            xml_escape_text(pending.cwd.as_deref().unwrap_or(".")),
            xml_escape_text(pending.tool_input.as_deref().unwrap_or("")),
            xml_escape_text(pending.tool_response.as_deref().unwrap_or("")),
        ));
    }
    events
}

#[cfg(test)]
mod tests;