use anyhow::{Context, Result};
use super::config::Config;
use crate::storage::Ledger;
pub fn run() -> Result<()> {
let cfg_path = Config::default_path()?;
let cfg = Config::load_or_default(&cfg_path)?;
println!("Carryover status");
println!("================");
println!("Config: {}", cfg_path.display());
println!("Resume mode: {}", cfg.resume_mode);
println!(
"Tools: {}",
if cfg.tools.is_empty() {
"<none configured>".to_string()
} else {
cfg.tools.join(", ")
}
);
let ledger_path = Ledger::default_path().context("resolve ledger path")?;
let ledger_size = std::fs::metadata(&ledger_path)
.map(|m| m.len())
.unwrap_or(0);
println!(
"Ledger: {} ({} bytes)",
ledger_path.display(),
ledger_size
);
if ledger_path.exists() {
let ledger = Ledger::open(&ledger_path).context("open ledger")?;
println!();
println!("Recent events:");
for tool in &cfg.tools {
let recent = ledger.query_recent(tool, 3).unwrap_or_default();
if recent.is_empty() {
println!(" {} — no events", tool);
} else {
println!(" {} ({} recent):", tool, recent.len());
for row in &recent {
let preview: String = row.content.chars().take(60).collect();
println!(" [{}] {}: {}", row.ts, row.role, preview);
}
}
}
} else {
println!();
println!(
"Ledger does not exist yet — run `carryover install` and let the daemon capture events."
);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn status_with_no_ledger_does_not_panic() {
let dir = tempfile::tempdir().unwrap();
let cfg_path = dir.path().join("config.json");
let cfg = Config::load_or_default(&cfg_path).unwrap();
assert_eq!(cfg.resume_mode, "ask");
assert!(cfg.tools.is_empty());
let ledger_path = dir.path().join("ledger.sqlite");
assert!(!ledger_path.exists());
let size = std::fs::metadata(&ledger_path)
.map(|m| m.len())
.unwrap_or(0);
assert_eq!(size, 0);
}
#[test]
fn status_with_empty_tools_shows_placeholder() {
let cfg = Config {
tools: vec![],
resume_mode: "ask".to_string(),
};
let display = if cfg.tools.is_empty() {
"<none configured>".to_string()
} else {
cfg.tools.join(", ")
};
assert_eq!(display, "<none configured>");
}
#[test]
fn status_with_tools_joins_names() {
let cfg = Config {
tools: vec!["claude".to_string(), "cursor".to_string()],
resume_mode: "ask".to_string(),
};
let display = if cfg.tools.is_empty() {
"<none configured>".to_string()
} else {
cfg.tools.join(", ")
};
assert_eq!(display, "claude, cursor");
}
#[test]
fn status_ledger_query_returns_empty_for_unknown_tool() {
let dir = tempfile::tempdir().unwrap();
let ledger_path = dir.path().join("ledger.sqlite");
let ledger = Ledger::open(&ledger_path).unwrap();
let rows = ledger.query_recent("no-such-tool", 3).unwrap();
assert!(rows.is_empty());
}
}