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
use crate::core::cache::SessionCache;
use crate::tools::CrpMode;

pub fn handle_status(cache: &SessionCache, turn_count: usize, crp_mode: CrpMode) -> String {
    let entries = cache.get_all_entries();
    let mut result = Vec::new();

    result.push(format!("Multi-turn context (turn {turn_count}):"));
    result.push(format!("  Cached files: {}", entries.len()));

    let total_tokens: usize = entries.iter().map(|(_, e)| e.original_tokens).sum();
    let total_reads: u32 = entries.iter().map(|(_, e)| e.read_count).sum();
    result.push(format!("  Total original tokens: {total_tokens}"));
    result.push(format!("  Total reads: {total_reads}"));

    let frequent: Vec<_> = entries.iter().filter(|(_, e)| e.read_count > 1).collect();
    if !frequent.is_empty() {
        result.push(format!("\n  Frequently accessed ({}):", frequent.len()));
        for (path, entry) in &frequent {
            result.push(format!(
                "    {} ({}x, {} tok)",
                crate::core::protocol::shorten_path(path),
                entry.read_count,
                entry.original_tokens
            ));
        }
    }

    let mode_label = match crp_mode {
        CrpMode::Off => "off",
        CrpMode::Compact => "compact",
        CrpMode::Tdd => "tdd",
    };
    result.push(format!("\n  CRP mode: {mode_label}"));

    let complexity = crate::core::adaptive::classify_from_context(cache);
    result.push(format!("\n  {}", complexity.encoded_suffix()));

    let hints = generate_prefill_hints(cache);
    if !hints.is_empty() {
        result.push("\nSMART HINTS:".to_string());
        for hint in &hints {
            result.push(format!("{hint}"));
        }
    }

    result.join("\n")
}

fn generate_prefill_hints(cache: &SessionCache) -> Vec<String> {
    let entries = cache.get_all_entries();
    let mut hints = Vec::new();

    let read_heavy: Vec<_> = entries
        .iter()
        .filter(|(_, e)| e.read_count >= 3 && e.original_tokens > 500)
        .collect();
    for (path, entry) in &read_heavy {
        let short = crate::core::protocol::shorten_path(path);
        hints.push(format!(
            "{short} read {}x ({} tok) — consider mode=map for future reads",
            entry.read_count, entry.original_tokens
        ));
    }

    let large: Vec<_> = entries
        .iter()
        .filter(|(_, e)| e.original_tokens > 2000 && e.read_count <= 1)
        .collect();
    for (path, entry) in &large {
        let short = crate::core::protocol::shorten_path(path);
        hints.push(format!(
            "{short} is large ({} tok) — consider mode=signatures or aggressive",
            entry.original_tokens
        ));
    }

    let stale_count = entries.iter().filter(|(_, e)| e.read_count == 1).count();
    if stale_count > 5 {
        hints.push(format!(
            "{stale_count} files read only once — ctx_cache clear to free context"
        ));
    }

    hints.truncate(5);
    hints
}