Skip to main content

snapshot/
snapshot.rs

1//! Developer-only native harness for the context engine.
2//!
3//! This is NOT part of the user-facing product flow. End users install the
4//! extension into Zed and the artifacts appear automatically — see
5//! `src/auto_refresh.rs` and the README. Use this binary only when you need
6//! to inspect or debug the engine without launching Zed.
7//!
8//! Usage: `cargo run --example snapshot -- [worktree_root]`
9//! Default worktree_root is the current directory.
10
11use std::env;
12use std::path::PathBuf;
13use std::process::Command;
14use context_bar::context_engine;
15use context_bar::git_signal::{
16    self, ChangeSummary, CommitSummary, GitSignals,
17};
18use context_bar::state_writer;
19
20fn main() {
21    if let Err(error) = run() {
22        eprintln!("snapshot failed: {error}");
23        std::process::exit(1);
24    }
25}
26
27fn run() -> Result<(), String> {
28    let root = env::args()
29        .nth(1)
30        .map(PathBuf::from)
31        .unwrap_or_else(|| env::current_dir().expect("cwd"));
32    let root = root
33        .canonicalize()
34        .map_err(|error| format!("cannot canonicalize {}: {error}", root.display()))?;
35
36    let git = collect_git(&root)?;
37    let files = context_engine::collect_files(&root)?;
38    let snapshot = context_engine::assemble(root.clone(), git, files)?;
39    let result = state_writer::write(&root, &snapshot)?;
40
41    println!("Wrote:");
42    println!("  {}", result.state_path.display());
43    println!("  {}", result.now_brief_path.display());
44    println!("  {}", result.session_brief_path.display());
45    println!("  {}", result.week_brief_path.display());
46    println!("  {}  <- primary agent surface", result.agent_brief_path.display());
47    println!("  {}  <- Claude Code surface", result.claude_brief_path.display());
48    Ok(())
49}
50
51fn collect_git(root: &PathBuf) -> Result<GitSignals, String> {
52    let branch = run_git(root, &["rev-parse", "--abbrev-ref", "HEAD"])?
53        .trim()
54        .to_string();
55    let log = run_git(
56        root,
57        &["log", "--since=7 days ago", "--max-count=40", "--format=%H%x09%ct%x09%s"],
58    )?;
59    let recent_commits: Vec<CommitSummary> = git_signal::parse_commits(&log);
60    let status = run_git(root, &["status", "--short"])?;
61    let (staged, unstaged): (Vec<ChangeSummary>, Vec<ChangeSummary>) =
62        git_signal::parse_status_public(&status);
63    let clean = staged.is_empty() && unstaged.is_empty();
64    Ok(GitSignals {
65        branch,
66        recent_commits,
67        staged_changes: staged,
68        unstaged_changes: unstaged,
69        clean_worktree: clean,
70    })
71}
72
73fn run_git(root: &PathBuf, args: &[&str]) -> Result<String, String> {
74    let output = Command::new("git")
75        .arg("-C")
76        .arg(root)
77        .args(args)
78        .output()
79        .map_err(|error| format!("git spawn failed: {error}"))?;
80    if !output.status.success() {
81        return Err(format!(
82            "git {:?} failed: {}",
83            args,
84            String::from_utf8_lossy(&output.stderr).trim()
85        ));
86    }
87    String::from_utf8(output.stdout).map_err(|error| format!("git utf8: {error}"))
88}