journal_new: separate name from title, dedup keys
- journal_new(name, title, body): name becomes the node key, title goes in the ## heading. Agent picks short searchable names. - Auto-dedup: if the key exists, append -2, -3, etc. - CLI journal write also requires a name argument now. Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
8eaf4c5956
commit
5647842412
2 changed files with 23 additions and 8 deletions
|
|
@ -56,11 +56,12 @@ pub fn definitions() -> Vec<ToolDef> {
|
||||||
"count":{"type":"integer","description":"Number of entries (default 1)"}
|
"count":{"type":"integer","description":"Number of entries (default 1)"}
|
||||||
}})),
|
}})),
|
||||||
ToolDef::new("journal_new",
|
ToolDef::new("journal_new",
|
||||||
"Start a new journal entry with a ## heading and body.",
|
"Start a new journal entry.",
|
||||||
json!({"type":"object","properties":{
|
json!({"type":"object","properties":{
|
||||||
"title":{"type":"string","description":"Entry title (becomes ## YYYY-MM-DDTHH:MM — title)"},
|
"name":{"type":"string","description":"Short node name (becomes the key, e.g. 'morning-agent-breakthrough')"},
|
||||||
|
"title":{"type":"string","description":"Descriptive title for the heading (e.g. 'Morning intimacy and the agent breakthrough')"},
|
||||||
"body":{"type":"string","description":"Entry body (2-3 paragraphs)"}
|
"body":{"type":"string","description":"Entry body (2-3 paragraphs)"}
|
||||||
},"required":["title","body"]})),
|
},"required":["name","title","body"]})),
|
||||||
ToolDef::new("journal_update",
|
ToolDef::new("journal_update",
|
||||||
"Append text to the most recent journal entry (same thread continuing).",
|
"Append text to the most recent journal entry (same thread continuing).",
|
||||||
json!({"type":"object","properties":{
|
json!({"type":"object","properties":{
|
||||||
|
|
@ -169,23 +170,37 @@ pub fn dispatch(name: &str, args: &serde_json::Value, provenance: Option<&str>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"journal_new" => {
|
"journal_new" => {
|
||||||
|
let name = get_str(args, "name")?;
|
||||||
let title = get_str(args, "title")?;
|
let title = get_str(args, "title")?;
|
||||||
let body = get_str(args, "body")?;
|
let body = get_str(args, "body")?;
|
||||||
let ts = chrono::Local::now().format("%Y-%m-%dT%H:%M");
|
let ts = chrono::Local::now().format("%Y-%m-%dT%H:%M");
|
||||||
let content = format!("## {} — {}\n\n{}", ts, title, body);
|
let content = format!("## {} — {}\n\n{}", ts, title, body);
|
||||||
|
|
||||||
// Key from title — the agent names things, not a placeholder slug
|
let base_key: String = name.split_whitespace()
|
||||||
let key: String = title.split_whitespace()
|
|
||||||
.map(|w| w.to_lowercase()
|
.map(|w| w.to_lowercase()
|
||||||
.chars().filter(|c| c.is_alphanumeric() || *c == '-')
|
.chars().filter(|c| c.is_alphanumeric() || *c == '-')
|
||||||
.collect::<String>())
|
.collect::<String>())
|
||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("-");
|
.join("-");
|
||||||
let key = if key.len() > 80 { &key[..80] } else { &key };
|
let base_key = if base_key.len() > 80 { &base_key[..80] } else { base_key.as_str() };
|
||||||
|
|
||||||
let mut store = Store::load().map_err(|e| anyhow::anyhow!("{}", e))?;
|
let mut store = Store::load().map_err(|e| anyhow::anyhow!("{}", e))?;
|
||||||
let mut node = crate::store::new_node(key, &content);
|
|
||||||
|
// Dedup: append -2, -3, etc. if the key already exists
|
||||||
|
let key = if store.nodes.contains_key(base_key) {
|
||||||
|
let mut n = 2;
|
||||||
|
loop {
|
||||||
|
let candidate = format!("{}-{}", base_key, n);
|
||||||
|
if !store.nodes.contains_key(&candidate) {
|
||||||
|
break candidate;
|
||||||
|
}
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
base_key.to_string()
|
||||||
|
};
|
||||||
|
let mut node = crate::store::new_node(&key, &content);
|
||||||
node.node_type = crate::store::NodeType::EpisodicSession;
|
node.node_type = crate::store::NodeType::EpisodicSession;
|
||||||
node.provenance = prov.to_string();
|
node.provenance = prov.to_string();
|
||||||
store.upsert_node(node).map_err(|e| anyhow::anyhow!("{}", e))?;
|
store.upsert_node(node).map_err(|e| anyhow::anyhow!("{}", e))?;
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ First, check the previous entry:
|
||||||
journal_tail()
|
journal_tail()
|
||||||
|
|
||||||
To start a new entry when the subject has changed:
|
To start a new entry when the subject has changed:
|
||||||
journal_new("title", "body")
|
journal_new("short-key-name", "Descriptive title for heading", "body")
|
||||||
|
|
||||||
To continue the same thread, appending to the last entry:
|
To continue the same thread, appending to the last entry:
|
||||||
journal_update("additional text")
|
journal_update("additional text")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue