feat: auto-append memory to memory index and don't necessarily require the LLM to remember to do it after a write

This commit is contained in:
2026-06-10 21:31:37 -06:00
parent 7143b50d98
commit 73c4449e7f
+77 -4
View File
@@ -1,6 +1,6 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::env; use std::path::{Path, PathBuf};
use std::path::PathBuf; use std::{env, fs};
use anyhow::{Context, Result, anyhow, bail}; use anyhow::{Context, Result, anyhow, bail};
use indexmap::IndexMap; use indexmap::IndexMap;
@@ -192,17 +192,21 @@ pub fn handle_memory_tool(ctx: &mut RequestContext, cmd_name: &str, args: &Value
path: target_dir.join(format!("{name}.md")), path: target_dir.join(format!("{name}.md")),
frontmatter: MemoryFrontmatter { frontmatter: MemoryFrontmatter {
name: name.clone(), name: name.clone(),
description: Some(description), description: Some(description.clone()),
kind, kind,
}, },
body: content, body: content,
}; };
file.save()?; file.save()?;
let index_path = target_dir.join("MEMORY.md");
let index_updated = ensure_index_entry(&index_path, &name, &description)?;
Ok(json!({ Ok(json!({
"status": "ok", "status": "ok",
"path": file.path.display().to_string(), "path": file.path.display().to_string(),
"reminder": "Update MEMORY.md to keep the index accurate.", "index_path": index_path.display().to_string(),
"index_updated": index_updated,
})) }))
} }
"lint" => lint_memory(&store), "lint" => lint_memory(&store),
@@ -210,6 +214,32 @@ pub fn handle_memory_tool(ctx: &mut RequestContext, cmd_name: &str, args: &Value
} }
} }
fn ensure_index_entry(index_path: &Path, name: &str, description: &str) -> Result<bool> {
let existing = fs::read_to_string(index_path).unwrap_or_default();
let already_referenced =
existing.contains(&format!("[[{name}]]")) || existing.contains(&format!("{name}.md"));
if already_referenced {
return Ok(false);
}
let entry = format!("- [[{name}]]: {description}\n");
let new_content = if existing.is_empty() {
format!("# Memory Index\n\n{entry}")
} else if existing.ends_with('\n') {
format!("{existing}{entry}")
} else {
format!("{existing}\n{entry}")
};
if let Some(parent) = index_path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(index_path, new_content)?;
Ok(true)
}
fn arg_str(args: &Value, key: &str) -> Result<String> { fn arg_str(args: &Value, key: &str) -> Result<String> {
args.get(key) args.get(key)
.and_then(Value::as_str) .and_then(Value::as_str)
@@ -339,6 +369,49 @@ mod tests {
assert!(extract_wikilinks("nothing here").is_empty()); assert!(extract_wikilinks("nothing here").is_empty());
} }
#[test]
fn ensure_index_entry_appends_when_missing() {
let root = temp_root("index_append");
let index = root.join("MEMORY.md");
fs::write(&index, "# Memory Index\n\n- [[existing]] — already here\n").unwrap();
let updated = ensure_index_entry(&index, "new_one", "newly added").unwrap();
assert!(updated);
let content = fs::read_to_string(&index).unwrap();
assert!(content.contains("- [[existing]] — already here"));
assert!(content.contains("- [[new_one]] — newly added"));
let _ = fs::remove_dir_all(&root);
}
#[test]
fn ensure_index_entry_skips_when_referenced() {
let root = temp_root("index_skip");
let index = root.join("MEMORY.md");
let original = "# Memory Index\n\n- [[existing]] — already here\n";
fs::write(&index, original).unwrap();
let updated = ensure_index_entry(&index, "existing", "different description").unwrap();
assert!(!updated);
assert_eq!(fs::read_to_string(&index).unwrap(), original);
let _ = fs::remove_dir_all(&root);
}
#[test]
fn ensure_index_entry_creates_index_when_absent() {
let root = temp_root("index_create");
let index = root.join("memory").join("MEMORY.md");
let updated = ensure_index_entry(&index, "first", "first ever").unwrap();
assert!(updated);
let content = fs::read_to_string(&index).unwrap();
assert!(content.starts_with("# Memory Index"));
assert!(content.contains("- [[first]] — first ever"));
let _ = fs::remove_dir_all(&root);
}
#[test] #[test]
fn workspace_write_dir_returns_structured_dir_directly() { fn workspace_write_dir_returns_structured_dir_directly() {
let root = temp_root("ws_structured"); let root = temp_root("ws_structured");