From 341955c144c630c4bf8ffce84af9525ab1dcb9dd Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Mon, 15 Jun 2026 11:24:37 -0500 Subject: [PATCH] Add transcript tail debug command --- src/cli/admin.rs | 25 +++++++++++++++++++++++++ src/main.rs | 14 ++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/cli/admin.rs b/src/cli/admin.rs index 464e97c..e5f1f0c 100644 --- a/src/cli/admin.rs +++ b/src/cli/admin.rs @@ -4,6 +4,31 @@ use anyhow::Result; use crate::hippocampus as memory; use crate::hippocampus::store; +pub fn cmd_transcript_tail(path: &str, count: usize, newest_first: bool) -> Result<()> { + let Some(iter) = crate::conversation::TailMessages::open(path) else { + anyhow::bail!("could not open transcript {}", path); + }; + + let mut messages: Vec<_> = iter.take(count).collect(); + if !newest_first { + messages.reverse(); + } + + for message in messages { + let role = match message.role { + crate::conversation::TranscriptRole::User => "user", + crate::conversation::TranscriptRole::Assistant => "assistant", + }; + let timestamp = message.timestamp.as_deref().unwrap_or("-"); + + println!("--- {role} offset={} timestamp={} ---", message.offset, timestamp); + println!("{}", message.text); + println!(); + } + + Ok(()) +} + fn install_default_file(data_dir: &std::path::Path, name: &str, content: &str) -> Result<()> { let path = data_dir.join(name); if !path.exists() { diff --git a/src/main.rs b/src/main.rs index 88b8703..1fdc901 100644 --- a/src/main.rs +++ b/src/main.rs @@ -333,6 +333,18 @@ enum AdminCmd { #[arg(long)] stats: bool, }, + /// Print normalized user/assistant messages from a transcript JSONL file + #[command(name = "transcript-tail")] + TranscriptTail { + /// Transcript JSONL path + path: String, + /// Maximum number of messages to print + #[arg(long, short = 'n', default_value_t = 40)] + count: usize, + /// Print newest messages first instead of chronological order + #[arg(long)] + newest_first: bool, + }, } /// Print help with subcommands expanded to show nested commands. @@ -458,6 +470,8 @@ impl Run for AdminCmd { Self::Dedup { apply } => cli::admin::cmd_dedup(apply).await, Self::DailyCheck => cli::admin::cmd_daily_check().await, Self::LoadContext { stats } => cli::node::cmd_load_context(stats).await, + Self::TranscriptTail { path, count, newest_first } + => cli::admin::cmd_transcript_tail(&path, count, newest_first), } } }