diff --git a/config.example.toml b/config.example.toml index a38f73cf..31a61c8c 100644 --- a/config.example.toml +++ b/config.example.toml @@ -213,6 +213,27 @@ default_text_model = "deepseek-ai/deepseek-v4-pro" # threshold_secs = 30 # include_summary = false +# ───────────────────────────────────────────────────────────────────────────────── +# Workspace Snapshots (#137) +# ───────────────────────────────────────────────────────────────────────────────── +# Each turn the TUI takes a `pre-turn:` and `post-turn:` snapshot of +# your workspace into a side-git repo at: +# +# ~/.deepseek/snapshots///.git +# +# Your own `.git` is never touched — `--git-dir` and `--work-tree` are always +# set together when shelling out to git. Use `/restore N` (slash command) or +# the `revert_turn` tool to roll the working tree back. Conversation history +# is unaffected. +# +# Disk footprint: ~1-2 GB worst case for a 100 MB workspace × 12 turns/day, +# typically far less thanks to git's content-addressed storage. The session +# boot prunes anything older than `max_age_days` (default 7). +# +# [snapshots] +# enabled = true # Snapshot workspace pre/post each turn for /restore +# max_age_days = 7 # Older snapshots pruned at session start + # ───────────────────────────────────────────────────────────────────────────────── # Hooks (optional) # ───────────────────────────────────────────────────────────────────────────────── diff --git a/crates/tui/src/main.rs b/crates/tui/src/main.rs index c8a53d7b..1dbe14fc 100644 --- a/crates/tui/src/main.rs +++ b/crates/tui/src/main.rs @@ -2788,6 +2788,11 @@ async fn run_interactive( logging::warn(format!("Failed to install system skills: {e}")); } + // Prune stale workspace snapshots from prior sessions (7-day default). + // Non-fatal: a flaky disk, missing `git`, or read-only home should + // never block the TUI from starting. + session_manager::prune_workspace_snapshots_at_boot(&workspace); + tui::run_tui( config, tui::TuiOptions { diff --git a/crates/tui/src/session_manager.rs b/crates/tui/src/session_manager.rs index 81c8b408..3a98cfc1 100644 --- a/crates/tui/src/session_manager.rs +++ b/crates/tui/src/session_manager.rs @@ -370,6 +370,32 @@ pub fn default_sessions_dir() -> std::io::Result { Ok(home.join(".deepseek").join("sessions")) } +/// Boot-time hook: prune workspace snapshots older than the configured +/// retention window for `workspace`. Failure is logged but never fatal — +/// the session boot must not block on a flaky disk or missing `git`. +/// +/// Called once per `run_interactive` invocation. The default retention +/// (7 days) is governed by `crate::snapshot::DEFAULT_MAX_AGE`. +pub fn prune_workspace_snapshots_at_boot(workspace: &Path) { + prune_workspace_snapshots(workspace, crate::snapshot::DEFAULT_MAX_AGE); +} + +/// Prune snapshots older than `max_age` for `workspace`. +/// +/// Always non-fatal. Returns silently — callers don't need the count +/// (the underlying repo logs at WARN if anything blew up). +pub fn prune_workspace_snapshots(workspace: &Path, max_age: std::time::Duration) { + match crate::snapshot::prune_older_than(workspace, max_age) { + Ok(0) => {} + Ok(n) => { + tracing::debug!(target: "snapshot", "boot prune removed {n} snapshot(s)"); + } + Err(e) => { + tracing::warn!(target: "snapshot", "boot prune failed: {e}"); + } + } +} + /// Create a new `SavedSession` from conversation state pub fn create_saved_session( messages: &[Message],