lean-ctx 3.1.3

Context Runtime for AI Agents with CCP. 42 MCP tools, 10 read modes, 90+ compression patterns, cross-session memory (CCP), persistent AI knowledge with temporal facts + contradiction detection, multi-agent context sharing + diaries, LITM-aware positioning, AAAK compact format, adaptive compression with Thompson Sampling bandits. Supports 24 AI tools. Reduces LLM token consumption by up to 99%.
Documentation
pub fn compress(cmd: &str, output: &str) -> Option<String> {
    let trimmed = output.trim();
    if trimmed.is_empty() {
        return Some("ok".to_string());
    }

    if is_table_output(trimmed) {
        return Some(compress_table(trimmed));
    }

    if cmd.contains("\\dt") || cmd.contains("\\d") {
        return Some(compress_describe(trimmed));
    }

    if trimmed.starts_with("INSERT")
        || trimmed.starts_with("UPDATE")
        || trimmed.starts_with("DELETE")
        || trimmed.starts_with("CREATE")
        || trimmed.starts_with("ALTER")
        || trimmed.starts_with("DROP")
    {
        return Some(trimmed.lines().next().unwrap_or(trimmed).to_string());
    }

    Some(compact_lines(trimmed, 20))
}

fn is_table_output(output: &str) -> bool {
    let lines: Vec<&str> = output.lines().collect();
    lines.len() >= 3
        && lines
            .iter()
            .any(|l| l.contains("---+---") || l.contains("-+-"))
}

fn compress_table(output: &str) -> String {
    let lines: Vec<&str> = output.lines().collect();
    let mut separator_idx = 0;
    let mut data_rows = 0u32;

    for (i, line) in lines.iter().enumerate() {
        if line.contains("---+---") || line.contains("-+-") {
            separator_idx = i;
            break;
        }
    }

    for line in lines.iter().skip(separator_idx + 1) {
        let trimmed = line.trim();
        if trimmed.is_empty() || trimmed.starts_with('(') {
            continue;
        }
        data_rows += 1;
    }

    let row_count_line = lines
        .iter()
        .rev()
        .find(|l| l.trim().starts_with('(') && l.contains("row"));
    let count_str = row_count_line
        .map(|l| l.trim().to_string())
        .unwrap_or_else(|| format!("({data_rows} rows)"));

    if data_rows <= 20 {
        return output.to_string();
    }

    let preview_end = (separator_idx + 11).min(lines.len());
    let preview: Vec<&str> = lines[..preview_end].to_vec();
    format!("{}\n... {count_str}", preview.join("\n"))
}

fn compress_describe(output: &str) -> String {
    let lines: Vec<&str> = output.lines().filter(|l| !l.trim().is_empty()).collect();
    if lines.len() <= 30 {
        return lines.join("\n");
    }
    format!(
        "{}\n... ({} more lines)",
        lines[..20].join("\n"),
        lines.len() - 20
    )
}

fn compact_lines(text: &str, max: usize) -> String {
    let lines: Vec<&str> = text.lines().filter(|l| !l.trim().is_empty()).collect();
    if lines.len() <= max {
        return lines.join("\n");
    }
    format!(
        "{}\n... ({} more lines)",
        lines[..max].join("\n"),
        lines.len() - max
    )
}