Skip to main content

kaizen/shell/
gc.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2//! `kaizen gc` — prune old sessions from the local store.
3
4use crate::core::config;
5use crate::shell::cli::workspace_path;
6use crate::store::Store;
7use anyhow::{Context, Result};
8use std::path::Path;
9
10/// Prune sessions older than `keep_days` (from `--days` or `[retention].hot_days`).
11pub fn cmd_gc(workspace: Option<&Path>, days: Option<u32>, vacuum: bool) -> Result<()> {
12    let ws = workspace_path(workspace)?;
13    let cfg = config::load(&ws)?;
14    let keep = days.unwrap_or(cfg.retention.hot_days);
15    if keep == 0 {
16        anyhow::bail!(
17            "refusing to prune: keep window is 0 days (unlimited). \
18             Set [retention].hot_days > 0 or pass --days <N>"
19        );
20    }
21    let store = Store::open(&ws.join(".kaizen/kaizen.db"))?;
22    let now = std::time::SystemTime::now()
23        .duration_since(std::time::UNIX_EPOCH)
24        .unwrap_or_default()
25        .as_millis() as u64;
26    let cutoff = now.saturating_sub((keep as u64).saturating_mul(86_400_000));
27    let stats = store
28        .prune_sessions_started_before(cutoff as i64)
29        .context("prune sessions")?;
30    println!(
31        "pruned {} sessions, {} events (cutoff started_at_ms < {})",
32        stats.sessions_removed, stats.events_removed, cutoff
33    );
34    if vacuum {
35        store.vacuum().context("VACUUM")?;
36        println!("vacuum complete");
37    }
38    Ok(())
39}