journal-tail: show timestamps and extract meaningful titles

Sort key normalization ensures consistent ordering across entries
with different date formats (content dates vs key dates). Title
extraction skips date-only lines, finds ## headers or falls back
to first content line truncated at 70 chars.

Also fixed: cargo bin had stale binary shadowing local bin install.
This commit is contained in:
ProofOfConcept 2026-03-01 01:41:37 -05:00
parent 7264bdc39c
commit 6096acb312

View file

@ -1188,14 +1188,26 @@ fn cmd_journal_tail(args: &[String]) -> Result<(), String> {
let date_re = regex::Regex::new(r"(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2})").unwrap();
let key_date_re = regex::Regex::new(r"^journal\.md#j-(\d{4}-\d{2}-\d{2}[t-]\d{2}-\d{2})").unwrap();
let extract_sort_key = |node: &capnp_store::Node| -> String {
// Try content header first (## 2026-02-28T23:11)
if let Some(caps) = date_re.captures(&node.content) {
return caps[1].to_string();
let normalize_date = |s: &str| -> String {
// Normalize to YYYY-MM-DDTHH:MM for consistent sorting
let s = s.replace('t', "T");
// Key dates use dashes everywhere: 2026-02-28-23-11
// Content dates use dashes and colons: 2026-02-28T23:11
// Normalize: first 10 chars keep dashes, rest convert dashes to colons
if s.len() >= 16 {
format!("{}T{}", &s[..10], s[11..].replace('-', ":"))
} else {
s
}
// Try key (journal.md#j-2026-02-28t23-11-...)
};
let extract_sort_key = |node: &capnp_store::Node| -> String {
// Try key first (journal.md#j-2026-02-28t23-11-...)
if let Some(caps) = key_date_re.captures(&node.key) {
return caps[1].replace('t', "T").replace('-', ":");
return normalize_date(&caps[1]);
}
// Try content header (## 2026-02-28T23:11)
if let Some(caps) = date_re.captures(&node.content) {
return normalize_date(&caps[1]);
}
// Fallback: use node timestamp
format!("{:.0}", node.timestamp)
@ -1206,11 +1218,37 @@ fn cmd_journal_tail(args: &[String]) -> Result<(), String> {
.collect();
journal.sort_by_key(|n| extract_sort_key(n));
// Show last N
// Show last N — each entry: [timestamp] ## Title
let skip = if journal.len() > n { journal.len() - n } else { 0 };
for node in journal.iter().skip(skip) {
println!("{}", node.content);
println!();
let ts = extract_sort_key(node);
// Find a meaningful title: first ## header, or first non-date non-empty line
let mut title = String::new();
for line in node.content.lines() {
let stripped = line.trim();
if stripped.is_empty() { continue; }
// Skip date-only lines like "## 2026-03-01T01:22"
if date_re.is_match(stripped) && stripped.len() < 25 { continue; }
if stripped.starts_with("## ") {
title = stripped[3..].to_string();
break;
} else if stripped.starts_with("# ") {
title = stripped[2..].to_string();
break;
} else {
// Use first content line, truncated
title = if stripped.len() > 70 {
format!("{}...", &stripped[..67])
} else {
stripped.to_string()
};
break;
}
}
if title.is_empty() {
title = node.key.clone();
}
println!("[{}] {}", ts, title);
}
Ok(())