journal: add --date flag for backdating entries

Adds an optional date parameter to journal_new that overrides the
timestamp on new entries. Accepts YYYY-MM-DD or YYYY-MM-DDTHH:MM
format. Used for seeding the memory graph with historical journal
entries from existing memory files.

Threading: CLI --date flag → cmd_journal_write → journal_new tool →
local store, with parse_date_to_epoch setting both timestamp and
created_at on the node.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Waffles 2026-05-22 15:38:29 -04:00
commit 37087ac6d9
5 changed files with 34 additions and 8 deletions

View file

@ -315,9 +315,13 @@ fn level_to_node_type(level: i64) -> crate::store::NodeType {
}
}
pub fn journal_new(store: &Store, provenance: &str, name: &str, title: &str, body: &str, level: Option<i64>) -> Result<String> {
pub fn journal_new(store: &Store, provenance: &str, name: &str, title: &str, body: &str, level: Option<i64>, date: Option<&str>) -> Result<String> {
let level = level.unwrap_or(0);
let ts = chrono::Local::now().format("%Y-%m-%dT%H:%M");
let ts = if let Some(d) = date {
d.to_string()
} else {
chrono::Local::now().format("%Y-%m-%dT%H:%M").to_string()
};
let content = format!("## {}{}\n\n{}", ts, title, body);
let base_key: String = name.split_whitespace()
@ -340,6 +344,12 @@ pub fn journal_new(store: &Store, provenance: &str, name: &str, title: &str, bod
base_key.to_string()
};
let mut node = crate::store::new_node(&key, &content);
if let Some(d) = date {
if let Some(epoch) = parse_date_to_epoch(d) {
node.timestamp = epoch;
node.created_at = epoch;
}
}
node.node_type = level_to_node_type(level);
node.provenance = provenance.to_string();
store.upsert_node(node).map_err(|e| anyhow::anyhow!("{}", e))?;
@ -348,6 +358,18 @@ pub fn journal_new(store: &Store, provenance: &str, name: &str, title: &str, bod
Ok(format!("New entry '{}' ({} words)", title, word_count))
}
fn parse_date_to_epoch(date: &str) -> Option<i64> {
use chrono::NaiveDate;
use chrono::NaiveDateTime;
if let Ok(dt) = NaiveDateTime::parse_from_str(date, "%Y-%m-%dT%H:%M") {
Some(dt.and_local_timezone(chrono::Local).single()?.timestamp())
} else if let Ok(d) = NaiveDate::parse_from_str(date, "%Y-%m-%d") {
Some(d.and_hms_opt(12, 0, 0)?.and_local_timezone(chrono::Local).single()?.timestamp())
} else {
None
}
}
pub fn journal_update(store: &Store, provenance: &str, body: &str, level: Option<i64>) -> Result<String> {
let level = level.unwrap_or(0);
let node_type = level_to_node_type(level);

View file

@ -309,7 +309,7 @@ memory_tool!(memory_links, ref -> Vec<LinkInfo>, key: [str]);
// ── Journal tools ──────────────────────────────────────────────
memory_tool!(journal_tail, ref -> Vec<JournalEntry>, count: [Option<u64>], level: [Option<u64>], after: [Option<&str>]);
memory_tool!(journal_new, mut, name: [str], title: [str], body: [str], level: [Option<i64>]);
memory_tool!(journal_new, mut, name: [str], title: [str], body: [str], level: [Option<i64>], date: [Option<&str>]);
memory_tool!(journal_update, mut, body: [str], level: [Option<i64>]);
// ── Graph tools ───────────────────────────────────────────────