systemprompt-cli 0.2.1

Unified CLI for systemprompt.io AI governance: agent orchestration, MCP governance, analytics, profiles, cloud deploy, and self-hosted operations.
Documentation
use crate::cli_settings::CliConfig;
use anyhow::{Context, Result};
use clap::Args;
use std::process::Stdio;
use systemprompt_loader::ExtensionLoader;
use systemprompt_models::{ProfileBootstrap, SecretsBootstrap};
use tokio::process::Command;


#[derive(Debug, Args)]
pub struct RunArgs {
    #[arg(help = "Extension name (binary name or manifest name)")]
    pub extension: String,

    #[arg(help = "Arguments to pass to the extension", trailing_var_arg = true)]
    pub args: Vec<String>,
}

pub async fn execute(args: RunArgs, config: &CliConfig) -> Result<()> {
    let project_root = std::env::current_dir().context("Failed to get current directory")?;

    let extension = ExtensionLoader::find_cli_extension(&project_root, &args.extension)
        .ok_or_else(|| {
            anyhow::anyhow!(
                "CLI extension '{}' not found. Use 'plugins list --type cli' to see available CLI \
                 extensions",
                args.extension
            )
        })?;

    let binary_name = extension
        .binary_name()
        .ok_or_else(|| anyhow::anyhow!("Extension '{}' has no binary defined", args.extension))?;

    let binary_path =
        ExtensionLoader::get_cli_binary_path(&project_root, binary_name).ok_or_else(|| {
            anyhow::anyhow!(
                "Binary '{}' not found. Build with: cargo build --release --package {}",
                binary_name,
                binary_name
            )
        })?;

    let profile_path = ProfileBootstrap::get_path().context("Profile path required")?;

    let mut cmd = Command::new(&binary_path);
    cmd.args(&args.args)
        .env("SYSTEMPROMPT_PROFILE", profile_path)
        .stdin(Stdio::inherit())
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit());

    if let Ok(jwt_secret) = SecretsBootstrap::jwt_secret() {
        cmd.env("JWT_SECRET", jwt_secret);
    }

    if let Ok(database_url) = std::env::var("DATABASE_URL") {
        cmd.env("DATABASE_URL", database_url);
    }

    if config.is_json_output() && extension.supports_json_output() {
        cmd.arg("--json");
    }

    let status = cmd.status().await.with_context(|| {
        format!(
            "Failed to execute extension binary: {}",
            binary_path.display()
        )
    })?;

    if status.success() {
        Ok(())
    } else {
        let code = status.code().unwrap_or(1);
        anyhow::bail!("Extension exited with code {}", code)
    }
}