lean-ctx 3.5.2

Context Runtime for AI Agents with CCP. 57 MCP tools, 10 read modes, 95+ 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 super::super::{mcp_server_quiet_mode, resolve_binary_path, write_file};

pub(crate) fn install_antigravity_hook() {
    let Some(home) = crate::core::home::resolve_home_dir() else {
        tracing::error!("Cannot resolve home directory");
        return;
    };

    install_antigravity_mcp_config(&home);
    install_antigravity_gemini_hooks(&home);
}

fn install_antigravity_mcp_config(home: &std::path::Path) {
    let binary = resolve_binary_path();
    let config_path = home
        .join(".gemini")
        .join("antigravity")
        .join("mcp_config.json");

    if let Some(parent) = config_path.parent() {
        let _ = std::fs::create_dir_all(parent);
    }

    let existing = if config_path.exists() {
        std::fs::read_to_string(&config_path).unwrap_or_default()
    } else {
        String::new()
    };

    let already_configured = existing.contains("lean-ctx");
    if already_configured {
        if !mcp_server_quiet_mode() {
            eprintln!(
                "Antigravity MCP: lean-ctx already configured at {}",
                config_path.display()
            );
        }
        return;
    }

    let config = serde_json::json!({
        "mcpServers": {
            "lean-ctx": {
                "command": binary
            }
        }
    });

    if existing.is_empty() || existing.trim() == "{}" || existing.contains("\"mcpServers\": {}") {
        write_file(
            &config_path,
            &serde_json::to_string_pretty(&config).unwrap_or_default(),
        );
    } else if let Ok(mut existing_json) = crate::core::jsonc::parse_jsonc(&existing) {
        if let Some(obj) = existing_json.as_object_mut() {
            let servers = obj
                .entry("mcpServers")
                .or_insert_with(|| serde_json::json!({}));
            if let Some(servers_obj) = servers.as_object_mut() {
                servers_obj.insert(
                    "lean-ctx".to_string(),
                    serde_json::json!({ "command": binary }),
                );
            }
            write_file(
                &config_path,
                &serde_json::to_string_pretty(&existing_json).unwrap_or_default(),
            );
        }
    }

    if !mcp_server_quiet_mode() {
        eprintln!(
            "Installed Antigravity MCP config at {}",
            config_path.display()
        );
    }
}

fn install_antigravity_gemini_hooks(home: &std::path::Path) {
    let binary = resolve_binary_path();
    let rewrite_cmd = format!("{binary} hook rewrite");
    let redirect_cmd = format!("{binary} hook redirect");

    let settings_path = home.join(".gemini").join("settings.json");
    let settings_content = if settings_path.exists() {
        std::fs::read_to_string(&settings_path).unwrap_or_default()
    } else {
        String::new()
    };

    let has_hooks = settings_content.contains("hook rewrite")
        && settings_content.contains("hook redirect")
        && settings_content.contains("\"matcher\"");

    if has_hooks {
        return;
    }

    let hook_config = serde_json::json!({
        "hooks": {
            "BeforeTool": [
                {
                    "matcher": "shell|execute_command|run_shell_command|run_command",
                    "hooks": [{
                        "type": "command",
                        "command": rewrite_cmd
                    }]
                },
                {
                    "matcher": "read_file|view_file|read_many_files|grep|grep_search|search|list_dir",
                    "hooks": [{
                        "type": "command",
                        "command": redirect_cmd
                    }]
                }
            ]
        }
    });

    if settings_content.is_empty() {
        write_file(
            &settings_path,
            &serde_json::to_string_pretty(&hook_config).unwrap_or_default(),
        );
    } else if let Ok(mut existing) = crate::core::jsonc::parse_jsonc(&settings_content) {
        if let Some(obj) = existing.as_object_mut() {
            obj.insert("hooks".to_string(), hook_config["hooks"].clone());
            write_file(
                &settings_path,
                &serde_json::to_string_pretty(&existing).unwrap_or_default(),
            );
        }
    }

    if !mcp_server_quiet_mode() {
        eprintln!(
            "Installed Gemini/Antigravity hooks at {}",
            settings_path.parent().unwrap_or(&settings_path).display()
        );
    }
}