git-slop 0.16.4

A deterministic repository token-defragmenter for humans and AI agents.
Documentation
fn token_density(
    paths: &BTreeSet<String>,
    token_counts: &BTreeMap<String, usize>,
    line_counts: &BTreeMap<String, usize>,
) -> BTreeMap<String, f64> {
    paths
        .iter()
        .map(|path| {
            let lines = line_counts.get(path).copied().unwrap_or(0).max(1);
            let tokens = token_counts.get(path).copied().unwrap_or(0);
            (path.clone(), ((tokens as f64) / (lines as f64)).max(1.0))
        })
        .collect()
}

fn shannon_entropy(weights: impl IntoIterator<Item = usize>) -> f64 {
    let weights: Vec<usize> = weights.into_iter().collect();
    let total: usize = weights.iter().sum();
    if total == 0 {
        return 0.0;
    }
    weights
        .into_iter()
        .filter(|weight| *weight > 0)
        .map(|weight| {
            let probability = weight as f64 / total as f64;
            -probability * probability.log2()
        })
        .sum()
}

fn round_to(value: f64, digits: i32) -> f64 {
    let factor = 10_f64.powi(digits);
    (value * factor).round_ties_even() / factor
}

fn nearest_rank_percentile(values: &[f64], quantile: f64) -> f64 {
    if values.is_empty() {
        return 0.0;
    }
    let mut sorted = values.to_vec();
    sorted.sort_by(f64::total_cmp);
    let rank = ((sorted.len() as f64 * quantile).ceil() as usize).max(1);
    sorted[rank.saturating_sub(1).min(sorted.len() - 1)]
}

fn repo_baselines(commits: &[BaselineCommit]) -> Value {
    if commits.is_empty() {
        return json!({
            "p95_files_touched": 0.0,
            "p99_files_touched": 0.0,
            "p95_token_delta_mass": 0.0,
            "p95_top_level_root_spread": 0.0,
            "p95_change_entropy": 0.0,
        });
    }
    let files_touched: Vec<f64> = commits
        .iter()
        .map(|commit| commit.file_count as f64)
        .collect();
    let token_delta: Vec<f64> = commits
        .iter()
        .map(|commit| commit.total_token_delta)
        .collect();
    let root_spread: Vec<f64> = commits
        .iter()
        .map(|commit| commit.top_level_root_count as f64)
        .collect();
    let entropy: Vec<f64> = commits.iter().map(|commit| commit.change_entropy).collect();
    json!({
        "p95_files_touched": nearest_rank_percentile(&files_touched, 0.95),
        "p99_files_touched": nearest_rank_percentile(&files_touched, 0.99),
        "p95_token_delta_mass": nearest_rank_percentile(&token_delta, 0.95),
        "p95_top_level_root_spread": nearest_rank_percentile(&root_spread, 0.95),
        "p95_change_entropy": nearest_rank_percentile(&entropy, 0.95),
    })
}

fn is_bot(author: &str, markers: &[String]) -> bool {
    let author = author.to_lowercase();
    markers.iter().any(|marker| author.contains(marker))
}

fn empty_result(
    analyzed_paths: &[String],
    status: &str,
) -> (BTreeMap<String, HistoryMetrics>, Vec<CommitRecord>, Value) {
    let metrics = analyzed_paths
        .iter()
        .map(|path| (normalized_path(path), HistoryMetrics::default()))
        .collect();
    let mut diagnostics = repo_baselines(&[]);
    diagnostics["history_status"] = json!(status);
    diagnostics["history_cap_reached"] = json!(false);
    (metrics, Vec::new(), diagnostics)
}