systemprompt-cli 0.14.5

Unified CLI for systemprompt.io AI governance: agent orchestration, MCP governance, analytics, profiles, cloud deploy, and self-hosted operations.
Documentation
use anyhow::{Context, Result, anyhow};
use clap::Args;

use super::types::{AgentDetailOutput, AgentListOutput, AgentSummary};
use crate::CliConfig;
use crate::shared::CommandOutput;
use systemprompt_loader::ConfigLoader;

#[derive(Debug, Clone, Args)]
pub struct ListArgs {
    #[arg(help = "Agent name to show details (optional)")]
    pub name: Option<String>,

    #[arg(long, help = "Show only enabled agents")]
    pub enabled: bool,

    #[arg(long, help = "Show only disabled agents", conflicts_with = "enabled")]
    pub disabled: bool,
}

#[derive(Debug, serde::Serialize)]
#[serde(untagged)]
pub(super) enum ListOrDetail {
    List(AgentListOutput),
    Detail(AgentDetailOutput),
}

pub(super) fn execute(args: ListArgs, _config: &CliConfig) -> Result<CommandOutput> {
    let services_config = ConfigLoader::load().context("Failed to load services configuration")?;

    if let Some(name) = args.name {
        let agent = services_config
            .agents
            .get(&name)
            .ok_or_else(|| anyhow!("Agent '{}' not found", name))?;

        let output = AgentDetailOutput {
            name: agent.name.clone(),
            display_name: agent.card.display_name.clone(),
            description: agent.card.description.clone(),
            port: agent.port,
            endpoint: agent.endpoint.clone(),
            enabled: agent.enabled,
            provider: agent
                .metadata
                .provider
                .clone()
                .unwrap_or_else(|| "-".to_owned()),
            model: agent
                .metadata
                .model
                .clone()
                .unwrap_or_else(|| "-".to_owned()),
            mcp_servers: agent.metadata.mcp_servers.include.clone(),
            skills_count: agent.metadata.skills.include.len(),
        };

        return Ok(CommandOutput::card_value(
            format!("Agent: {}", name),
            &ListOrDetail::Detail(output),
        ));
    }

    let mut agents: Vec<AgentSummary> = services_config
        .agents
        .iter()
        .filter(|(_, agent)| {
            if args.enabled {
                agent.enabled
            } else if args.disabled {
                !agent.enabled
            } else {
                true
            }
        })
        .map(|(name, agent)| AgentSummary::from_config(name, agent))
        .collect();

    agents.sort_by(|a, b| a.name.cmp(&b.name));

    let output = ListOrDetail::List(AgentListOutput { agents });
    let ListOrDetail::List(list) = &output else {
        unreachable!("output is constructed as List above")
    };

    Ok(CommandOutput::table_of(
        vec![
            "name",
            "display_name",
            "port",
            "enabled",
            "is_primary",
            "is_default",
        ],
        &list.agents,
    )
    .with_title("Agents"))
}