repopilot 0.13.0

Local-first CLI for repository audit, architecture risk detection, baseline tracking, and CI-friendly code review.
Documentation
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct CacheDiagnostics {
    pub cache_dir: PathBuf,
    pub exists: bool,
    pub schema_version: u32,
    pub repopilot_version: String,
    pub approximate_size_bytes: u64,
    pub file_hashes_count: usize,
    pub file_roles_count: usize,
    pub findings_count: usize,
    pub stale_entries_count: usize,
}

pub fn inspect_cache(root: &Path) -> CacheDiagnostics {
    let cache_root = cache_dir(root);
    let exists = cache_root.is_dir();
    let cache = ScanCache::load(root);

    CacheDiagnostics {
        cache_dir: cache_root.clone(),
        exists,
        schema_version: CACHE_SCHEMA_VERSION,
        repopilot_version: env!("CARGO_PKG_VERSION").to_string(),
        approximate_size_bytes: directory_size_bytes(&cache_root),
        file_hashes_count: cache.file_hashes.len(),
        file_roles_count: cache.file_roles.len(),
        findings_count: cache.findings.len(),
        stale_entries_count: stale_cache_entries_count(&cache),
    }
}

fn stale_cache_entries_count(cache: &ScanCache) -> usize {
    let mut count = 0;

    for key in cache.file_hashes.keys() {
        if !cache.file_roles.contains_key(key) || !cache.findings.contains_key(key) {
            count += 1;
        }
    }

    for key in cache.file_roles.keys() {
        if !cache.file_hashes.contains_key(key) {
            count += 1;
        }
    }

    for key in cache.findings.keys() {
        if !cache.file_hashes.contains_key(key) {
            count += 1;
        }
    }

    count
}

fn directory_size_bytes(path: &Path) -> u64 {
    let Ok(entries) = fs::read_dir(path) else {
        return 0;
    };

    entries
        .filter_map(Result::ok)
        .map(|entry| {
            let entry_path = entry.path();
            match entry.metadata() {
                Ok(metadata) if metadata.is_file() => metadata.len(),
                Ok(metadata) if metadata.is_dir() => directory_size_bytes(&entry_path),
                _ => 0,
            }
        })
        .sum()
}