Skip to main content

systemprompt_cli/commands/plugins/
validate.rs

1//! `plugins validate` command checking extension manifests.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use clap::Args;
7use systemprompt_identifiers::ExtensionId;
8
9use super::discover_registry;
10use super::types::{ExtensionValidationOutput, ValidationError, ValidationWarning};
11use crate::CliConfig;
12use crate::shared::CommandOutput;
13
14#[derive(Debug, Clone, Copy, Args)]
15pub struct ValidateArgs {
16    #[arg(long, help = "Show detailed validation information")]
17    pub verbose: bool,
18}
19
20pub fn execute(args: &ValidateArgs, _config: &CliConfig) -> (CommandOutput, bool) {
21    let registry = discover_registry();
22    let mut errors = Vec::new();
23    let mut warnings = Vec::new();
24
25    if let Err(e) = registry.validate_dependencies() {
26        errors.push(ValidationError {
27            extension_id: None,
28            error_type: "dependency".to_owned(),
29            message: e.to_string(),
30        });
31    }
32
33    for ext in registry.extensions() {
34        if ext.dependencies().is_empty() && args.verbose {
35            continue;
36        }
37
38        for dep in ext.dependencies() {
39            if !registry.has(dep) {
40                errors.push(ValidationError {
41                    extension_id: Some(ExtensionId::new(ext.id())),
42                    error_type: "missing_dependency".to_owned(),
43                    message: format!("Missing dependency: {}", dep),
44                });
45            }
46        }
47
48        if ext.config_prefix().is_some()
49            && let Some(schema) = ext.config_schema()
50            && schema.is_null()
51        {
52            warnings.push(ValidationWarning {
53                extension_id: Some(ExtensionId::new(ext.id())),
54                warning_type: "config".to_owned(),
55                message: "Config prefix defined but schema is null".to_owned(),
56            });
57        }
58    }
59
60    for ext in registry.asset_extensions() {
61        warnings.push(ValidationWarning {
62            extension_id: Some(ExtensionId::new(ext.id())),
63            warning_type: "asset_validation_skipped".to_owned(),
64            message: "Asset validation requires full profile initialization. Use 'systemprompt \
65                      infra db doctor'."
66                .to_owned(),
67        });
68    }
69
70    let valid = errors.is_empty();
71    let extension_count = registry.len();
72
73    let output = ExtensionValidationOutput {
74        valid,
75        extension_count,
76        errors,
77        warnings,
78    };
79
80    (
81        CommandOutput::card_value("Extension Validation", &output),
82        valid,
83    )
84}