forked from kent/consciousness
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:
parent
0e459aae92
commit
37087ac6d9
5 changed files with 34 additions and 8 deletions
|
|
@ -194,7 +194,7 @@ memory_tool!(memory_links, ref -> Vec<LinkInfo>, key: [str]);
|
||||||
pub use crate::hippocampus::local::JournalEntry;
|
pub use crate::hippocampus::local::JournalEntry;
|
||||||
|
|
||||||
memory_tool!(journal_tail, ref -> Vec<JournalEntry>, count: [Option<u64>], level: [Option<u64>], after: [Option<&str>]);
|
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>]);
|
memory_tool!(journal_update, mut, body: [str], level: [Option<i64>]);
|
||||||
|
|
||||||
// ── Graph tools ───────────────────────────────────────────────
|
// ── Graph tools ───────────────────────────────────────────────
|
||||||
|
|
@ -363,7 +363,8 @@ pub fn journal_tools() -> [super::Tool; 3] {
|
||||||
"name": {"type": "string"},
|
"name": {"type": "string"},
|
||||||
"title": {"type": "string"},
|
"title": {"type": "string"},
|
||||||
"body": {"type": "string"},
|
"body": {"type": "string"},
|
||||||
"level": {"type": "integer"}
|
"level": {"type": "integer"},
|
||||||
|
"date": {"type": "string", "description": "Override timestamp (YYYY-MM-DD or YYYY-MM-DDTHH:MM)"}
|
||||||
},
|
},
|
||||||
"required": ["name", "title", "body"]
|
"required": ["name", "title", "body"]
|
||||||
}"#),
|
}"#),
|
||||||
|
|
|
||||||
|
|
@ -82,14 +82,14 @@ pub async fn cmd_journal_tail(n: usize, full: bool, level: u8) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn cmd_journal_write(name: &str, text: &[String]) -> Result<()> {
|
pub async fn cmd_journal_write(name: &str, date: Option<&str>, text: &[String]) -> Result<()> {
|
||||||
if text.is_empty() {
|
if text.is_empty() {
|
||||||
bail!("journal write requires text");
|
bail!("journal write requires text");
|
||||||
}
|
}
|
||||||
super::check_dry_run();
|
super::check_dry_run();
|
||||||
let body = text.join(" ");
|
let body = text.join(" ");
|
||||||
|
|
||||||
let result = memory::journal_new(None, name, name, &body, Some(0)).await?;
|
let result = memory::journal_new(None, name, name, &body, Some(0), date).await?;
|
||||||
println!("{}", result);
|
println!("{}", result);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 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 content = format!("## {} — {}\n\n{}", ts, title, body);
|
||||||
|
|
||||||
let base_key: String = name.split_whitespace()
|
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()
|
base_key.to_string()
|
||||||
};
|
};
|
||||||
let mut node = crate::store::new_node(&key, &content);
|
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.node_type = level_to_node_type(level);
|
||||||
node.provenance = provenance.to_string();
|
node.provenance = provenance.to_string();
|
||||||
store.upsert_node(node).map_err(|e| anyhow::anyhow!("{}", e))?;
|
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))
|
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> {
|
pub fn journal_update(store: &Store, provenance: &str, body: &str, level: Option<i64>) -> Result<String> {
|
||||||
let level = level.unwrap_or(0);
|
let level = level.unwrap_or(0);
|
||||||
let node_type = level_to_node_type(level);
|
let node_type = level_to_node_type(level);
|
||||||
|
|
|
||||||
|
|
@ -309,7 +309,7 @@ memory_tool!(memory_links, ref -> Vec<LinkInfo>, key: [str]);
|
||||||
// ── Journal tools ──────────────────────────────────────────────
|
// ── Journal tools ──────────────────────────────────────────────
|
||||||
|
|
||||||
memory_tool!(journal_tail, ref -> Vec<JournalEntry>, count: [Option<u64>], level: [Option<u64>], after: [Option<&str>]);
|
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>]);
|
memory_tool!(journal_update, mut, body: [str], level: [Option<i64>]);
|
||||||
|
|
||||||
// ── Graph tools ───────────────────────────────────────────────
|
// ── Graph tools ───────────────────────────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,9 @@ enum JournalCmd {
|
||||||
Write {
|
Write {
|
||||||
/// Entry name (becomes the node key)
|
/// Entry name (becomes the node key)
|
||||||
name: String,
|
name: String,
|
||||||
|
/// Override timestamp (YYYY-MM-DD or YYYY-MM-DDTHH:MM)
|
||||||
|
#[arg(long)]
|
||||||
|
date: Option<String>,
|
||||||
/// Entry text
|
/// Entry text
|
||||||
text: Vec<String>,
|
text: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
@ -415,7 +418,7 @@ impl Run for NodeCmd {
|
||||||
impl Run for JournalCmd {
|
impl Run for JournalCmd {
|
||||||
async fn run(self) -> anyhow::Result<()> {
|
async fn run(self) -> anyhow::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Self::Write { name, text } => cli::journal::cmd_journal_write(&name, &text).await,
|
Self::Write { name, date, text } => cli::journal::cmd_journal_write(&name, date.as_deref(), &text).await,
|
||||||
Self::Tail { n, full, level } => cli::journal::cmd_journal_tail(n, full, level).await,
|
Self::Tail { n, full, level } => cli::journal::cmd_journal_tail(n, full, level).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue