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
use crate::auth::{self, SecureString};
use crate::cli::args::ServerCommands;
use crate::cli::UI;
use crate::platform;
use crate::platform::server_registry::{ServerConfig, ServerPlatform, ServerRegistry};
use anyhow::{bail, Result};

pub async fn execute(action: ServerCommands, ui: &UI) -> Result<()> {
    match action {
        ServerCommands::Add {
            name,
            platform,
            api_url,
            token,
            no_push,
        } => add(name, platform, api_url, token, !no_push, ui).await,
        ServerCommands::Remove { name } => remove(name, ui),
        ServerCommands::List => list(ui),
        ServerCommands::Status => status(ui).await,
        ServerCommands::CreateRepo {
            name,
            server,
            namespace,
            private,
            description,
        } => create_repo(name, server, namespace, private, description, ui).await,
    }
}

async fn add(
    name: String,
    platform_str: String,
    api_url: String,
    token: Option<String>,
    push_enabled: bool,
    ui: &UI,
) -> Result<()> {
    let platform = match platform_str.to_lowercase().as_str() {
        "github" | "gh" => ServerPlatform::GitHub,
        "gitlab" | "gl" => ServerPlatform::GitLab,
        other => bail!("Unknown platform '{}'. Use 'github' or 'gitlab'.", other),
    };

    // Validate URL
    url::Url::parse(&api_url)?;

    ui.header("Register Server");
    ui.blank();
    ui.field("Name", &name);
    ui.field("Platform", platform.to_string());
    ui.field("API URL", &api_url);
    ui.field("Push", if push_enabled { "enabled" } else { "disabled" });
    ui.blank();

    let token_str = if let Some(t) = token {
        t
    } else {
        // Prompt for token
        let input = dialoguer::Password::new().with_prompt("Token").interact()?;
        input
    };

    let secure_token = SecureString::from_string(token_str);
    let store_key = format!("server:{}", name);

    // Store token
    auth::store::store_token(&store_key, &secure_token)?;

    // Validate by getting authenticated user
    let spinner = ui.spinner("Validating token...");
    let server_config = ServerConfig {
        name: name.clone(),
        platform: platform.clone(),
        api_url: api_url.clone(),
        web_url: None,
        push_enabled,
    };

    let temp_client =
        platform::create_client_for_server(&server_config, secure_token, "validation", "check");

    let username = match temp_client.get_authenticated_user().await {
        Ok(user) => {
            ui.finish_progress(&spinner, "Token validated");
            user
        }
        Err(e) => {
            ui.finish_progress(&spinner, "Validation failed");
            let _ = auth::store::delete_token(&store_key);
            bail!("Token validation failed: {}", e);
        }
    };

    // Add to registry
    let mut registry = ServerRegistry::load()?;
    registry.add(server_config)?;
    registry.save()?;

    ui.blank();
    ui.success(format!("Server '{}' registered", name));
    ui.field("Authenticated as", &username);
    ui.blank();

    Ok(())
}

fn remove(name: String, ui: &UI) -> Result<()> {
    let mut registry = ServerRegistry::load()?;
    registry.remove(&name)?;
    registry.save()?;

    let store_key = format!("server:{}", name);
    let _ = auth::store::delete_token(&store_key);

    ui.success(format!("Server '{}' removed", name));
    Ok(())
}

fn list(ui: &UI) -> Result<()> {
    let registry = ServerRegistry::load()?;

    if registry.servers.is_empty() {
        ui.info("No servers registered. Use 'securegit server add' to register one.");
        return Ok(());
    }

    ui.header("Registered Servers");
    ui.blank();

    for server in &registry.servers {
        let has_token = auth::token_for_server(server).is_some();
        let auth_icon = if has_token { "ok" } else { "--" };

        ui.raw(format!(
            "  {} {} ({}) {}  [push: {}]",
            auth_icon,
            server.name,
            server.platform,
            server.api_url,
            if server.push_enabled { "on" } else { "off" }
        ));
    }

    ui.blank();
    Ok(())
}

async fn status(ui: &UI) -> Result<()> {
    let registry = ServerRegistry::load()?;

    if registry.servers.is_empty() {
        ui.info("No servers registered.");
        return Ok(());
    }

    ui.header("Server Status");
    ui.blank();

    for server in &registry.servers {
        let token = auth::token_for_server(server);
        if let Some(t) = token {
            let client = platform::create_client_for_server(server, t, "status", "check");

            match client.get_authenticated_user().await {
                Ok(user) => {
                    ui.field(&server.name, format!("ok - {} ({})", user, server.platform));
                }
                Err(e) => {
                    ui.field(&server.name, format!("error - {} ({})", e, server.platform));
                }
            }
        } else {
            ui.field(&server.name, "no credentials");
        }
    }

    ui.blank();
    Ok(())
}

async fn create_repo(
    name: String,
    server_name: Option<String>,
    namespace: Option<String>,
    private: bool,
    description: Option<String>,
    ui: &UI,
) -> Result<()> {
    let registry = ServerRegistry::load()?;

    let server = if let Some(ref sname) = server_name {
        registry
            .get(sname)
            .ok_or_else(|| anyhow::anyhow!("Server '{}' not found", sname))?
            .clone()
    } else {
        // Try auto-detect from remote
        let remote = platform::detect_remote(&std::path::PathBuf::from("."))?;
        let platform_str = match remote.host {
            platform::PlatformHost::GitHub => "github",
            platform::PlatformHost::GitLab => "gitlab",
        };
        registry
            .servers
            .iter()
            .find(|s| s.platform.to_string().to_lowercase() == platform_str)
            .ok_or_else(|| {
                anyhow::anyhow!("No registered {} server found. Use --server to specify.", platform_str)
            })?
            .clone()
    };

    let token = auth::token_for_server(&server)
        .ok_or_else(|| anyhow::anyhow!("No credentials found for server '{}'", server.name))?;

    ui.header("Create Repository");
    ui.blank();
    ui.field("Name", &name);
    ui.field("Server", &server.name);
    ui.field("Platform", server.platform.to_string());
    if let Some(ref ns) = namespace {
        ui.field("Namespace", ns);
    }
    ui.field("Private", if private { "yes" } else { "no" });
    if let Some(ref desc) = description {
        ui.field("Description", desc);
    }
    ui.blank();

    let spinner = ui.spinner("Creating repository...");

    let client = platform::create_client_for_server(&server, token, "", "");

    let create_req = platform::types::CreateRepo {
        name: name.clone(),
        description,
        private,
        namespace,
    };

    let repo = client.create_repo(&create_req).await?;
    ui.finish_progress(&spinner, "Repository created");

    ui.blank();
    ui.success(format!("Repository '{}' created", repo.full_name));
    ui.field("Web URL", &repo.web_url);
    ui.field("Clone (HTTP)", &repo.clone_url_http);
    if let Some(ref ssh) = repo.clone_url_ssh {
        ui.field("Clone (SSH)", ssh);
    }
    ui.blank();

    Ok(())
}