lean-ctx 3.7.3

Context Runtime for AI Agents with CCP. 68 MCP tools, 10 read modes, 60+ compression patterns, cross-session memory (CCP), persistent AI knowledge with temporal facts + contradiction detection, multi-agent context sharing, 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::contextops::{self, ContextOps};

pub fn handle(action: &str, agent: Option<&str>) -> String {
    let Some(home) = dirs::home_dir() else {
        return "Error: could not determine home directory".to_string();
    };

    let project_root = std::env::current_dir().unwrap_or_else(|_| home.clone());
    let ops = ContextOps::new(&home, &project_root);

    match action {
        "sync" => {
            let report = if let Some(agent_name) = agent {
                ops.sync_agent(agent_name)
            } else {
                ops.sync_all()
            };
            contextops::format_sync(&report)
        }
        "diff" => match ops.detect_drift() {
            Ok(reports) => {
                let mut output = contextops::format_drift(&reports);
                let drifted = reports
                    .iter()
                    .filter(|r| r.status == contextops::DriftStatus::Drifted)
                    .count();
                if drifted > 0 {
                    output.push_str(&format!(
                        "\n\n{drifted} target(s) drifted. Run ctx_rules(action=\"sync\") to fix."
                    ));
                }
                output
            }
            Err(e) => {
                let statuses = ops.status();
                let mut output = format!("Note: {e}\n\nFallback status check:\n");
                output.push_str(&contextops::format_status(&statuses));
                output
            }
        },
        "lint" => match ops.lint() {
            Ok(warnings) => contextops::format_lint(&warnings),
            Err(e) => {
                format!("Error: {e}\nRun ctx_rules(action=\"init\") to create .leanctx/rules.toml")
            }
        },
        "status" => {
            let statuses = ops.status();
            let mut output = contextops::format_status(&statuses);
            output.push('\n');
            if ops.has_config() {
                output.push_str("\nCentral config: present (.leanctx/rules.toml)");
            } else {
                output.push_str(
                    "\nCentral config: missing (run ctx_rules(action=\"init\") to create)",
                );
            }
            output
        }
        "init" => {
            if ops.has_config() {
                return "Config already exists at .leanctx/rules.toml. Delete it first to reinitialize.".to_string();
            }
            match ops.init() {
                Ok(_) => "Created .leanctx/rules.toml from existing rules.\nRun ctx_rules(action=\"lint\") to check, then ctx_rules(action=\"sync\") to distribute.".to_string(),
                Err(e) => format!("Error: {e}"),
            }
        }
        _ => "Unknown action. Use: sync, diff, lint, status, init".to_string(),
    }
}

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

    #[test]
    fn handle_status() {
        let result = handle("status", None);
        assert!(result.contains("Agent Rules Status"));
    }

    #[test]
    fn handle_unknown_action() {
        let result = handle("unknown_xyz", None);
        assert!(result.contains("Unknown action"));
    }

    #[test]
    fn handle_lint_without_config() {
        let result = handle("lint", None);
        assert!(
            result.contains("Error") || result.contains("Lint"),
            "Should show error or lint results: {result}"
        );
    }

    #[test]
    fn handle_diff_without_config() {
        let result = handle("diff", None);
        assert!(!result.is_empty());
    }
}