rename agent: fix tool calls and target override

- Add memory_rename tool (in-place rename, preserves content and links)
- Update rename.agent prompt to use memory_rename() instead of text output
- Fix {{rename}} placeholder to respect --target keys when provided
- Add format_rename_targets() for targeted rename runs
This commit is contained in:
ProofOfConcept 2026-03-27 15:10:55 -04:00
parent bb2e3b9fbb
commit 37acb9502d
4 changed files with 62 additions and 8 deletions

View file

@ -52,13 +52,17 @@ search for — `bcachefs-transaction-restart`, `emotional-regulation-gap`,
- Keys shorter than 60 characters
- System keys (_consolidation-*)
## What to output
## How to rename
```
RENAME old_key new_key
```
Use the `memory_rename` tool:
If a node already has a reasonable name, skip it.
memory_rename(old_key, new_key)
This renames the node in place — same content, same links, new key.
Do NOT use `memory_write` or `memory_supersede` — just rename.
If a node already has a reasonable name, skip it. When in doubt, skip.
A bad rename is worse than an auto-slug.
## Guidelines
@ -66,7 +70,7 @@ If a node already has a reasonable name, skip it.
- **Be specific.** `journal#2026-02-14-session` is useless.
- **Use domain terms.** Use the words someone would search for.
- **Don't rename to something longer than the original.**
- **Preserve the date.** Always keep YYYY-MM-DD.
- **Preserve the date.** Always keep YYYY-MM-DD for journal entries.
- **When in doubt, skip.** A bad rename is worse than an auto-slug.
- **Respect search hits.** Nodes marked "actively found by search" are
being retrieved by their current name. Skip these unless the rename

View file

@ -237,8 +237,14 @@ fn resolve(
}
"rename" => {
let (rename_keys, section) = super::prompts::format_rename_candidates(store, count);
Some(Resolved { text: section, keys: rename_keys })
if !keys.is_empty() {
// --target provided: present those keys as candidates
let section = super::prompts::format_rename_targets(store, keys);
Some(Resolved { text: section, keys: vec![] })
} else {
let (rename_keys, section) = super::prompts::format_rename_candidates(store, count);
Some(Resolved { text: section, keys: rename_keys })
}
}
"split" => {

View file

@ -303,6 +303,38 @@ pub fn format_rename_candidates(store: &Store, count: usize) -> (Vec<String>, St
(keys, out)
}
/// Format specific target keys as rename candidates (for --target mode)
pub fn format_rename_targets(store: &Store, keys: &[String]) -> String {
let mut out = String::new();
out.push_str(&format!("## Nodes to rename ({} targets)\n\n", keys.len()));
for key in keys {
let Some(node) = store.nodes.get(key) else {
out.push_str(&format!("### {}\n\n(node not found)\n\n---\n\n", key));
continue;
};
out.push_str(&format!("### {}\n", key));
let created = if node.timestamp > 0 {
crate::store::format_datetime(node.timestamp)
} else {
"unknown".to_string()
};
out.push_str(&format!("Created: {}\n", created));
let content = &node.content;
if content.len() > 800 {
let truncated = crate::util::truncate(content, 800, "\n[...]");
out.push_str(&format!("\nContent ({} chars, truncated):\n{}\n\n",
content.len(), truncated));
} else {
out.push_str(&format!("\nContent:\n{}\n\n", content));
}
out.push_str("---\n\n");
}
out
}
/// Get split candidates sorted by size (largest first)
pub fn split_candidates(store: &Store) -> Vec<String> {
let mut candidates: Vec<(&str, usize)> = store.nodes.iter()