lean-ctx 3.3.7

Context Runtime for AI Agents with CCP. 46 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::marked_block;

const MARKER_START: &str = "# >>> lean-ctx shell hook >>>";
const MARKER_END: &str = "# <<< lean-ctx shell hook <<<";
const ALIAS_START: &str = "# >>> lean-ctx agent aliases >>>";
const ALIAS_END: &str = "# <<< lean-ctx agent aliases <<<";

const KNOWN_AGENT_ENV_VARS: &[&str] = &[
    "LEAN_CTX_AGENT",
    "CLAUDECODE",
    "CODEX_CLI_SESSION",
    "GEMINI_SESSION",
];

const AGENT_ALIASES: &[(&str, &str)] = &[
    ("claude", "claude"),
    ("codex", "codex"),
    ("gemini", "gemini"),
];

pub fn install_all(quiet: bool) {
    let home = match dirs::home_dir() {
        Some(h) => h,
        None => {
            eprintln!("Cannot resolve home directory");
            return;
        }
    };

    install_zshenv(&home, quiet);
    install_bashenv(&home, quiet);
    install_aliases(&home, quiet);
}

pub fn uninstall_all(quiet: bool) {
    let home = match dirs::home_dir() {
        Some(h) => h,
        None => return,
    };

    marked_block::remove_from_file(
        &home.join(".zshenv"),
        MARKER_START,
        MARKER_END,
        quiet,
        "shell hook from ~/.zshenv",
    );
    marked_block::remove_from_file(
        &home.join(".bashenv"),
        MARKER_START,
        MARKER_END,
        quiet,
        "shell hook from ~/.bashenv",
    );
    marked_block::remove_from_file(
        &home.join(".zshrc"),
        ALIAS_START,
        ALIAS_END,
        quiet,
        "agent aliases from ~/.zshrc",
    );
    marked_block::remove_from_file(
        &home.join(".bashrc"),
        ALIAS_START,
        ALIAS_END,
        quiet,
        "agent aliases from ~/.bashrc",
    );
}

fn install_zshenv(home: &std::path::Path, quiet: bool) {
    let path = home.join(".zshenv");
    let env_check = build_env_check();

    let hook = format!(
        r#"{MARKER_START}
if [[ -z "$LEAN_CTX_ACTIVE" && -n "$ZSH_EXECUTION_STRING" ]] && command -v lean-ctx &>/dev/null; then
  if {env_check}; then
    export LEAN_CTX_ACTIVE=1
    exec lean-ctx -c "$ZSH_EXECUTION_STRING"
  fi
fi
{MARKER_END}"#
    );

    marked_block::upsert(
        &path,
        MARKER_START,
        MARKER_END,
        &hook,
        quiet,
        "shell hook in ~/.zshenv",
    );
}

fn install_bashenv(home: &std::path::Path, quiet: bool) {
    let path = home.join(".bashenv");
    let env_check = build_env_check();

    let hook = format!(
        r#"{MARKER_START}
if [[ -z "$LEAN_CTX_ACTIVE" && -n "$BASH_EXECUTION_STRING" ]] && command -v lean-ctx &>/dev/null; then
  if {env_check}; then
    export LEAN_CTX_ACTIVE=1
    exec lean-ctx -c "$BASH_EXECUTION_STRING"
  fi
fi
{MARKER_END}"#
    );

    marked_block::upsert(
        &path,
        MARKER_START,
        MARKER_END,
        &hook,
        quiet,
        "shell hook in ~/.bashenv",
    );
}

fn install_aliases(home: &std::path::Path, quiet: bool) {
    let mut lines = Vec::new();
    lines.push(ALIAS_START.to_string());
    for (alias_name, bin_name) in AGENT_ALIASES {
        lines.push(format!(
            "alias {alias_name}='LEAN_CTX_AGENT=1 BASH_ENV=\"$HOME/.bashenv\" {bin_name}'"
        ));
    }
    lines.push(ALIAS_END.to_string());
    let block = lines.join("\n");

    for rc in &[home.join(".zshrc"), home.join(".bashrc")] {
        if rc.exists() {
            let label = format!(
                "agent aliases in ~/{}",
                rc.file_name().unwrap_or_default().to_string_lossy()
            );
            marked_block::upsert(rc, ALIAS_START, ALIAS_END, &block, quiet, &label);
        }
    }
}

fn build_env_check() -> String {
    let checks: Vec<String> = KNOWN_AGENT_ENV_VARS
        .iter()
        .map(|v| format!("-n \"${v}\""))
        .collect();
    format!("[[ {} ]]", checks.join(" || "))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn env_check_format() {
        let check = build_env_check();
        assert!(check.contains("LEAN_CTX_AGENT"));
        assert!(check.contains("CLAUDECODE"));
        assert!(check.contains("||"));
    }
}