use crate::core::db::DbManager;
use crate::core::{db_path, ensure_initialized};
use anyhow::Result;
use chrono::{Duration, Utc};
use colored::Colorize;
pub async fn execute(
since: Option<String>,
until: Option<String>,
agent: Option<String>,
file: Option<String>,
) -> Result<()> {
let project_root = ensure_initialized()?;
let db_file = db_path(&project_root);
let db = DbManager::open(&db_file)?;
let since_dt = since.map(|s| parse_time(&s)).transpose()?;
let until_dt = until.map(|s| parse_time(&s)).transpose()?;
let sessions = db.search_sessions(since_dt, until_dt, agent.as_deref(), file.as_deref())?;
if sessions.is_empty() {
println!("{}", "No sessions found matching your criteria.".yellow());
return Ok(());
}
println!(
"{}",
format!("Found {} session(s)", sessions.len())
.bold()
.underline()
);
println!();
for summary in sessions {
let short_id = &summary.session_id[..8];
let duration_ms = summary
.end_time
.signed_duration_since(summary.start_time)
.num_milliseconds() as u64;
let duration_secs = duration_ms as f64 / 1000.0;
let status = if summary.exit_code == 0 {
"✓".green()
} else {
"✗".red()
};
let agent = summary.agent_name.as_deref().unwrap_or("unknown");
println!("{} Session {}", status, short_id.cyan());
println!(" Agent: {}", agent);
println!(
" Time: {} | Duration: {:.2}s",
summary.start_time.format("%Y-%m-%d %H:%M"),
duration_secs
);
println!(" Files changed: {}", summary.files_changed);
println!(" Run: agentlog replay {}", short_id);
println!();
}
println!("{}:", "Quick actions".bold());
println!(" agentlog replay <session-id> - View full session details");
println!(" agentlog export --output session.jsonl - Export for sharing");
Ok(())
}
fn parse_time(input: &str) -> Result<chrono::DateTime<Utc>> {
let now = Utc::now();
if let Ok(dt) = chrono::DateTime::parse_from_rfc3339(input) {
return Ok(dt.with_timezone(&Utc));
}
if let Ok(dt) = chrono::NaiveDate::parse_from_str(input, "%Y-%m-%d") {
return Ok(chrono::DateTime::from_naive_utc_and_offset(
dt.and_hms_opt(0, 0, 0).unwrap(),
Utc,
));
}
let duration = parse_relative_time(input)?;
Ok(now - duration)
}
fn parse_relative_time(input: &str) -> Result<Duration> {
let input = input.trim().to_lowercase();
let input = input.replace(" ago", "").replace("s", "");
let (num, unit) = if let Some(pos) = input.find(|c: char| !c.is_ascii_digit() && c != ' ') {
let num: i64 = input[..pos]
.trim()
.parse()
.map_err(|_| anyhow::anyhow!("Invalid number in time expression: {}", input))?;
(num, input[pos..].trim())
} else {
return Err(anyhow::anyhow!("Invalid time format: {}", input));
};
let duration = match unit {
"m" | "min" | "minute" => Duration::minutes(num),
"h" | "hr" | "hour" => Duration::hours(num),
"d" | "day" => Duration::days(num),
"w" | "week" => Duration::weeks(num),
_ => return Err(anyhow::anyhow!("Unknown time unit: {}", unit)),
};
Ok(duration)
}