remem-ai 0.3.8

Persistent memory for Claude Code — single binary, zero subprocesses
Documentation
use crate::adapter::EventSummary;
use crate::db;
use crate::observe::short_path;

pub(super) fn event_summary(
    tool_name: &str,
    input: &Option<serde_json::Value>,
    response: &Option<serde_json::Value>,
) -> Option<EventSummary> {
    match tool_name {
        "Edit" => file_event(input, "file_path", "file_edit", "Edit "),
        "Write" => file_event(input, "file_path", "file_create", "Create "),
        "NotebookEdit" => notebook_event(input),
        "Bash" => bash_event(input, response),
        "Grep" => grep_event(input),
        "Glob" => glob_event(input),
        "Agent" | "Task" => agent_event(input),
        _ => None,
    }
}

fn file_event(
    input: &Option<serde_json::Value>,
    field: &str,
    event_type: &str,
    prefix: &str,
) -> Option<EventSummary> {
    let file = input.as_ref()?.get(field)?.as_str()?;
    Some(EventSummary {
        event_type: event_type.into(),
        summary: format!("{}{}", prefix, short_path(file)),
        detail: None,
        files_json: serde_json::to_string(&[file]).ok(),
        exit_code: None,
    })
}

fn notebook_event(input: &Option<serde_json::Value>) -> Option<EventSummary> {
    let file = input
        .as_ref()?
        .get("notebook_path")?
        .as_str()
        .or_else(|| input.as_ref()?.get("file_path")?.as_str())?;
    Some(EventSummary {
        event_type: "file_edit".into(),
        summary: format!("NotebookEdit {}", short_path(file)),
        detail: None,
        files_json: serde_json::to_string(&[file]).ok(),
        exit_code: None,
    })
}

fn bash_event(
    input: &Option<serde_json::Value>,
    response: &Option<serde_json::Value>,
) -> Option<EventSummary> {
    let command = input.as_ref()?.get("command")?.as_str()?;
    let exit_code = response
        .as_ref()
        .and_then(|value| value.get("exitCode"))
        .and_then(|code| code.as_i64())
        .map(|code| code as i32);
    let stderr = if exit_code.unwrap_or(0) != 0 {
        response
            .as_ref()
            .and_then(|value| value.get("stderr"))
            .and_then(|stderr| stderr.as_str())
            .map(|stderr| db::truncate_str(stderr, 500).to_string())
    } else {
        None
    };
    let code_label = exit_code
        .map(|code| code.to_string())
        .unwrap_or_else(|| "?".into());

    Some(EventSummary {
        event_type: "bash".into(),
        summary: format!(
            "Run `{}` (exit {})",
            db::truncate_str(command.trim(), 60),
            code_label
        ),
        detail: stderr,
        files_json: None,
        exit_code,
    })
}

fn grep_event(input: &Option<serde_json::Value>) -> Option<EventSummary> {
    let pattern = input.as_ref()?.get("pattern")?.as_str().unwrap_or("?");
    let path = input
        .as_ref()
        .and_then(|value| value.get("path"))
        .and_then(|path| path.as_str())
        .unwrap_or(".");
    Some(EventSummary {
        event_type: "search".into(),
        summary: format!(
            "Grep '{}' in {}",
            db::truncate_str(pattern, 40),
            short_path(path)
        ),
        detail: None,
        files_json: None,
        exit_code: None,
    })
}

fn glob_event(input: &Option<serde_json::Value>) -> Option<EventSummary> {
    let pattern = input.as_ref()?.get("pattern")?.as_str().unwrap_or("?");
    Some(EventSummary {
        event_type: "search".into(),
        summary: format!("Glob {}", pattern),
        detail: None,
        files_json: None,
        exit_code: None,
    })
}

fn agent_event(input: &Option<serde_json::Value>) -> Option<EventSummary> {
    let desc = input
        .as_ref()
        .and_then(|value| value.get("description").or_else(|| value.get("prompt")))
        .and_then(|desc| desc.as_str())
        .unwrap_or("agent task");
    Some(EventSummary {
        event_type: "agent".into(),
        summary: format!("Agent: {}", db::truncate_str(desc, 80)),
        detail: None,
        files_json: None,
        exit_code: None,
    })
}