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