difflore-cli 0.1.0

Your AI coding agent, taught by your team's PR reviews — a local-first, open-source MCP server that turns past review comments into rules your agent follows automatically.
Documentation
/// Read the rolling audit history (most-recent `window` runs by `ts_ms`).
/// Returns Err on read error or any corrupt JSONL line so diagnostics can
/// surface a damaged history file instead of treating it as "no activity".
pub(crate) fn load_audit_history(
    window: usize,
) -> anyhow::Result<Vec<difflore_core::context::intent_filter::AuditRunRecord>> {
    use difflore_core::context::intent_filter::AuditRunRecord;

    let Some(path) = audit_history_path() else {
        return Ok(Vec::new());
    };
    let body = match std::fs::read_to_string(&path) {
        Ok(b) => b,
        Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()),
        Err(e) => return Err(anyhow::anyhow!("read {}: {e}", path.display())),
    };
    let mut all: Vec<AuditRunRecord> = Vec::new();
    let mut corrupt = 0usize;
    for line in body.lines().filter(|line| !line.trim().is_empty()) {
        match serde_json::from_str::<AuditRunRecord>(line) {
            Ok(record) => all.push(record),
            Err(_) => corrupt += 1,
        }
    }
    if corrupt > 0 {
        return Err(anyhow::anyhow!(
            "audit history at {} has {corrupt} corrupt line(s)",
            path.display()
        ));
    }
    all.sort_by_key(|record| record.ts_ms);
    if all.len() > window {
        all.drain(..all.len() - window);
    }
    Ok(all)
}

fn audit_history_path() -> Option<std::path::PathBuf> {
    difflore_core::paths::data_home()
        .ok()
        .map(|dir| dir.join("audit-history.jsonl"))
}