systemprompt-cli 0.1.22

systemprompt.io OS - CLI for agent orchestration, AI operations, and system management
Documentation
use anyhow::{Context, Result};
use clap::Args;

use super::types::{ValidationIssue, ValidationOutput};
use crate::CliConfig;
use crate::shared::CommandResult;
use systemprompt_loader::ConfigLoader;
use systemprompt_models::SecretsBootstrap;

#[derive(Debug, Args)]
pub struct ValidateArgs {
    #[arg(help = "Agent name to validate (optional)")]
    pub name: Option<String>,
}

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

    if let Ok(secrets) = SecretsBootstrap::get() {
        for provider_config in services_config.ai.providers.values_mut() {
            if provider_config.api_key.starts_with("${") && provider_config.api_key.ends_with('}') {
                let var_name =
                    provider_config.api_key[2..provider_config.api_key.len() - 1].to_string();
                if let Some(v) = secrets.get(&var_name) {
                    provider_config.api_key.clone_from(v);
                }
            }
        }
    }

    let mut errors = Vec::new();
    let mut warnings = Vec::new();
    let mut agents_checked = 0;

    let agents_to_check: Vec<(&String, &systemprompt_models::AgentConfig)> = match &args.name {
        Some(name) => {
            let agent = services_config
                .agents
                .get(name)
                .ok_or_else(|| anyhow::anyhow!("Agent '{}' not found", name))?;
            vec![(name, agent)]
        },
        None => services_config.agents.iter().collect(),
    };

    for (name, agent) in agents_to_check {
        agents_checked += 1;

        if let Err(e) = agent.validate(name) {
            errors.push(ValidationIssue {
                source: name.clone(),
                message: e.to_string(),
                suggestion: None,
            });
        }

        if agent.port == 0 {
            errors.push(ValidationIssue {
                source: name.clone(),
                message: "Port cannot be 0".to_string(),
                suggestion: None,
            });
        }

        if agent.card.display_name.is_empty() {
            warnings.push(ValidationIssue {
                source: name.clone(),
                message: "Display name is empty".to_string(),
                suggestion: None,
            });
        }

        if agent.card.description.is_empty() {
            warnings.push(ValidationIssue {
                source: name.clone(),
                message: "Description is empty".to_string(),
                suggestion: None,
            });
        }

        if agent.enabled && agent.metadata.provider.is_none() {
            warnings.push(ValidationIssue {
                source: name.clone(),
                message: "Enabled agent has no AI provider configured".to_string(),
                suggestion: None,
            });
        }

        if agent.enabled {
            if let Some(provider_name) = &agent.metadata.provider {
                match services_config.ai.providers.get(provider_name) {
                    None => {
                        errors.push(ValidationIssue {
                            source: name.clone(),
                            message: format!(
                                "Provider '{}' is not configured in ai.providers",
                                provider_name
                            ),
                            suggestion: None,
                        });
                    },
                    Some(provider_config) => {
                        if !provider_config.enabled {
                            errors.push(ValidationIssue {
                                source: name.clone(),
                                message: format!(
                                    "Provider '{}' is disabled in AI config (set enabled: true)",
                                    provider_name
                                ),
                                suggestion: None,
                            });
                        }

                        if provider_config.api_key.is_empty()
                            || provider_config.api_key.starts_with("${")
                        {
                            errors.push(ValidationIssue {
                                source: name.clone(),
                                message: format!(
                                    "No API key configured for provider '{}' (check secrets file)",
                                    provider_name
                                ),
                                suggestion: None,
                            });
                        }
                    },
                }
            }
        }

        for mcp_server in &agent.metadata.mcp_servers {
            if !services_config.mcp_servers.contains_key(mcp_server) {
                errors.push(ValidationIssue {
                    source: name.clone(),
                    message: format!("Referenced MCP server '{}' not found in config", mcp_server),
                    suggestion: None,
                });
            }
        }
    }

    let output = ValidationOutput {
        valid: errors.is_empty(),
        items_checked: agents_checked,
        errors,
        warnings,
    };

    Ok(CommandResult::table(output).with_title("Validation Results"))
}