securegit 0.8.5

Zero-trust git replacement with 12 built-in security scanners, LLM redteam bridge, universal undo, durable backups, and a 50-tool MCP server
Documentation
pub mod fickling;
pub mod mcp_scan;
pub mod modelscan;
pub mod picklescan;

/// Error type for CLI tool bridges — mirrors redteam::BridgeError semantics.
#[derive(Debug)]
pub enum CliError {
    /// Tool binary not installed — caller should skip gracefully.
    NotInstalled(String),
    /// Tool found but execution failed — surface to user.
    CallFailed(String),
}

impl std::fmt::Display for CliError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::NotInstalled(msg) => write!(f, "tool not installed: {}", msg),
            Self::CallFailed(msg) => write!(f, "tool call failed: {}", msg),
        }
    }
}

impl std::error::Error for CliError {}

/// Generic bridge for invoking external CLI tools.
pub struct CliToolBridge {
    pub binary: String,
}

impl CliToolBridge {
    pub fn new(binary: impl Into<String>) -> Self {
        Self {
            binary: binary.into(),
        }
    }

    pub fn is_available(&self) -> bool {
        which::which(&self.binary).is_ok()
    }

    /// Run the CLI tool with given args. Returns (exit_code, stdout, stderr).
    pub async fn run(&self, args: &[&str]) -> Result<(i32, String, String), CliError> {
        if !self.is_available() {
            return Err(CliError::NotInstalled(format!(
                "{} not found on PATH",
                self.binary
            )));
        }

        let output = tokio::process::Command::new(&self.binary)
            .args(args)
            .stdin(std::process::Stdio::null())
            .stdout(std::process::Stdio::piped())
            .stderr(std::process::Stdio::piped())
            .output()
            .await
            .map_err(|e| CliError::CallFailed(format!("Failed to spawn {}: {}", self.binary, e)))?;

        let exit_code = output.status.code().unwrap_or(-1);
        let stdout = String::from_utf8_lossy(&output.stdout).to_string();
        let stderr = String::from_utf8_lossy(&output.stderr).to_string();

        Ok((exit_code, stdout, stderr))
    }
}