use anyhow::Result;
use rusqlite::Connection;
use serde_json::{Value, json};
use crate::commands::shared::{resolve_files, summarize};
use crate::infrastructure::db;
mod render;
pub fn relearn_value(
conn: &Connection,
handle: &str,
text: Option<&str>,
tags: &[String],
files: &[String],
force: bool,
) -> Result<Value> {
let text = text.map(str::trim).filter(|t| !t.is_empty());
if text.is_none() && tags.is_empty() && files.is_empty() {
anyhow::bail!(
"Nothing to change — pass new body text, --tag, and/or --file.\n\
(Tags and files each REPLACE the existing set.)"
);
}
let mut item = db::get_item_by_handle(conn, handle)?;
if item.kind != "memory" {
anyhow::bail!("{handle} is not a memory.");
}
let mut updated: Vec<&str> = vec![];
if let Some(body) = text {
if !force {
crate::infrastructure::util::safety::check_memory_body(body)?;
}
item.body = body.to_string();
item.title = summarize(body);
item.summary = None;
updated.push("body");
}
if !tags.is_empty() {
item.tags = tags.to_vec();
updated.push("tags");
}
item.modified = chrono::Utc::now();
db::update_item(conn, &item)?;
if text.is_some() && matches!(db::get_embedding(conn, &item.uuid.to_string()), Ok(Some(_))) {
crate::infrastructure::memory::embedding::index_memory(conn, &item);
}
if !files.is_empty() {
let resolved = resolve_files(files);
db::set_item_files(conn, &item.uuid, &resolved)?;
updated.push("files");
} else {
db::refresh_item_file_fingerprints(conn, &item.uuid)?;
}
Ok(json!({
"label": handle,
"uuid": &item.uuid.to_string()[..8],
"updated": updated,
"title": item.title,
"tags": item.tags,
}))
}
pub fn run(
conn: &Connection,
handle: &str,
text: Option<&str>,
tags: &[String],
files: &[String],
force: bool,
) -> Result<()> {
let v = relearn_value(conn, handle, text, tags, files, force)?;
render::print_relearned(&v, handle);
Ok(())
}
#[cfg(test)]
#[path = "../../../tests/unit/commands/relearn/mod.rs"]
mod tests;