lean-ctx 3.1.5

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
use std::path::Path;

pub fn compress(path: &str) -> Option<String> {
    let content = std::fs::read_to_string(path).ok()?;
    let filename = Path::new(path)
        .file_name()
        .and_then(|n| n.to_str())
        .unwrap_or(path);

    match filename {
        "package.json" => compress_package_json(&content),
        "Cargo.toml" => compress_cargo_toml(&content),
        "requirements.txt" => compress_requirements(&content),
        "go.mod" => compress_go_mod(&content),
        "Gemfile" => compress_gemfile(&content),
        "pyproject.toml" => compress_pyproject(&content),
        _ => None,
    }
}

pub fn detect_and_compress(dir: &str) -> Option<String> {
    let candidates = [
        "package.json",
        "Cargo.toml",
        "requirements.txt",
        "go.mod",
        "Gemfile",
        "pyproject.toml",
    ];

    for name in &candidates {
        let path = format!("{}/{}", dir.trim_end_matches('/'), name);
        if Path::new(&path).exists() {
            if let Some(result) = compress(&path) {
                return Some(result);
            }
        }
    }

    None
}

fn compress_package_json(content: &str) -> Option<String> {
    let val: serde_json::Value = serde_json::from_str(content).ok()?;
    let obj = val.as_object()?;

    let name = obj.get("name").and_then(|v| v.as_str()).unwrap_or("?");
    let version = obj.get("version").and_then(|v| v.as_str()).unwrap_or("?");

    let mut result = format!("Node ({name}@{version}):");

    if let Some(deps) = obj.get("dependencies").and_then(|v| v.as_object()) {
        let dep_list: Vec<String> = deps
            .iter()
            .map(|(k, v)| format!("{k} ({})", v.as_str().unwrap_or("?")))
            .collect();
        result.push_str(&format!(
            "\n  deps ({}): {}",
            dep_list.len(),
            dep_list.join(", ")
        ));
    }

    if let Some(deps) = obj.get("devDependencies").and_then(|v| v.as_object()) {
        let dep_list: Vec<String> = deps
            .iter()
            .take(10)
            .map(|(k, v)| format!("{k} ({})", v.as_str().unwrap_or("?")))
            .collect();
        let suffix = if deps.len() > 10 {
            format!(", ... +{} more", deps.len() - 10)
        } else {
            String::new()
        };
        result.push_str(&format!(
            "\n  devDeps ({}): {}{}",
            deps.len(),
            dep_list.join(", "),
            suffix
        ));
    }

    Some(result)
}

fn compress_cargo_toml(content: &str) -> Option<String> {
    let mut name = String::new();
    let mut version = String::new();
    let mut deps = Vec::new();
    let mut in_deps = false;

    for line in content.lines() {
        let trimmed = line.trim();

        if trimmed.starts_with("[dependencies]") {
            in_deps = true;
            continue;
        }
        if trimmed.starts_with('[') && in_deps {
            in_deps = false;
        }

        if trimmed.starts_with("name") {
            if let Some(v) = extract_toml_string(trimmed) {
                name = v;
            }
        }
        if trimmed.starts_with("version") && !in_deps {
            if let Some(v) = extract_toml_string(trimmed) {
                version = v;
            }
        }

        if in_deps && !trimmed.is_empty() && !trimmed.starts_with('#') {
            if let Some(dep_name) = trimmed.split('=').next() {
                deps.push(dep_name.trim().to_string());
            }
        }
    }

    if deps.is_empty() {
        return None;
    }

    let mut result = format!("Rust ({name}@{version}):");
    result.push_str(&format!("\n  deps ({}): {}", deps.len(), deps.join(", ")));

    Some(result)
}

fn compress_requirements(content: &str) -> Option<String> {
    let deps: Vec<&str> = content
        .lines()
        .filter(|l| !l.trim().is_empty() && !l.trim().starts_with('#'))
        .collect();

    if deps.is_empty() {
        return None;
    }

    let short: Vec<String> = deps
        .iter()
        .map(|d| {
            d.split("==")
                .next()
                .unwrap_or(d)
                .split(">=")
                .next()
                .unwrap_or(d)
                .trim()
                .to_string()
        })
        .collect();

    Some(format!(
        "Python:\n  deps ({}): {}",
        short.len(),
        short.join(", ")
    ))
}

fn compress_go_mod(content: &str) -> Option<String> {
    let mut module = String::new();
    let mut deps = Vec::new();
    let mut in_require = false;

    for line in content.lines() {
        let trimmed = line.trim();

        if trimmed.starts_with("module ") {
            module = trimmed.strip_prefix("module ").unwrap_or("").to_string();
        }
        if trimmed == "require (" {
            in_require = true;
            continue;
        }
        if trimmed == ")" && in_require {
            in_require = false;
        }
        if in_require && !trimmed.is_empty() {
            if let Some(name) = trimmed.split_whitespace().next() {
                if !name.contains("// indirect") {
                    let short = name.rsplit('/').next().unwrap_or(name);
                    deps.push(short.to_string());
                }
            }
        }
    }

    if deps.is_empty() {
        return None;
    }

    Some(format!(
        "Go ({module}):\n  deps ({}): {}",
        deps.len(),
        deps.join(", ")
    ))
}

fn compress_gemfile(content: &str) -> Option<String> {
    let gems: Vec<&str> = content
        .lines()
        .filter(|l| l.trim().starts_with("gem "))
        .map(|l| {
            l.trim()
                .strip_prefix("gem ")
                .unwrap_or(l)
                .split(',')
                .next()
                .unwrap_or("")
                .trim()
                .trim_matches('\'')
                .trim_matches('"')
        })
        .collect();

    if gems.is_empty() {
        return None;
    }

    Some(format!(
        "Ruby:\n  gems ({}): {}",
        gems.len(),
        gems.join(", ")
    ))
}

fn compress_pyproject(content: &str) -> Option<String> {
    let mut deps = Vec::new();
    let mut in_deps = false;

    for line in content.lines() {
        let trimmed = line.trim();
        if trimmed == "dependencies = [" || trimmed.starts_with("dependencies") {
            in_deps = true;
            continue;
        }
        if trimmed == "]" && in_deps {
            in_deps = false;
        }
        if in_deps {
            let clean = trimmed.trim_matches(|c: char| c == '"' || c == '\'' || c == ',');
            let name = clean
                .split(">=")
                .next()
                .unwrap_or(clean)
                .split("==")
                .next()
                .unwrap_or(clean)
                .trim();
            if !name.is_empty() {
                deps.push(name.to_string());
            }
        }
    }

    if deps.is_empty() {
        return None;
    }

    Some(format!(
        "Python:\n  deps ({}): {}",
        deps.len(),
        deps.join(", ")
    ))
}

fn extract_toml_string(line: &str) -> Option<String> {
    let after_eq = line.split('=').nth(1)?.trim();
    Some(after_eq.trim_matches('"').to_string())
}