Skip to main content

systemprompt_cli/commands/plugins/
show.rs

1use anyhow::{Result, anyhow};
2use clap::Args;
3use systemprompt_extension::{Extension, ExtensionRegistry};
4use systemprompt_loader::ExtensionLoader;
5
6use super::types::{
7    ExtensionDetailOutput, ExtensionSource, JobInfo, LlmProviderInfo, RoleInfo, SchemaInfo,
8    TemplateInfo, ToolInfo,
9};
10use crate::CliConfig;
11use crate::shared::CommandOutput;
12
13#[derive(Debug, Clone, Args)]
14pub struct ShowArgs {
15    #[arg(help = "Extension ID to show")]
16    pub id: String,
17}
18
19pub fn execute(args: &ShowArgs, _config: &CliConfig) -> Result<CommandOutput> {
20    let registry = ExtensionRegistry::discover()?;
21    let needle = args.id.to_lowercase();
22
23    let ext = registry.get(&args.id).or_else(|| {
24        registry
25            .extensions()
26            .iter()
27            .find(|e| e.id().to_lowercase() == needle || e.name().to_lowercase() == needle)
28    });
29
30    let Some(ext) = ext else {
31        return show_manifest(&args.id)
32            .ok_or_else(|| anyhow!("Extension '{}' not found", args.id))?;
33    };
34
35    let output = build_detail_output(ext.as_ref());
36
37    Ok(CommandOutput::card_value(
38        format!("Extension: {}", args.id),
39        &output,
40    ))
41}
42
43fn build_detail_output(ext: &dyn Extension) -> ExtensionDetailOutput {
44    ExtensionDetailOutput {
45        id: systemprompt_identifiers::PluginId::new(ext.id()),
46        name: ext.name().to_owned(),
47        version: ext.version().to_owned(),
48        priority: ext.priority(),
49        source: ExtensionSource::Compiled,
50        dependencies: ext.dependencies().iter().map(|s| (*s).to_owned()).collect(),
51        config_prefix: ext.config_prefix().map(String::from),
52        jobs: job_infos(ext),
53        templates: template_infos(ext),
54        schemas: schema_infos(ext),
55        routes: vec![],
56        tools: ext
57            .tool_providers()
58            .iter()
59            .map(|_provider| ToolInfo {
60                name: "tool_provider".to_owned(),
61            })
62            .collect(),
63        roles: role_infos(ext),
64        llm_providers: ext
65            .llm_providers()
66            .iter()
67            .map(|_provider| LlmProviderInfo {
68                name: "llm_provider".to_owned(),
69            })
70            .collect(),
71        storage_paths: ext
72            .required_storage_paths()
73            .iter()
74            .map(|s| (*s).to_owned())
75            .collect(),
76    }
77}
78
79fn job_infos(ext: &dyn Extension) -> Vec<JobInfo> {
80    ext.jobs()
81        .iter()
82        .map(|job| JobInfo {
83            name: job.name().to_owned(),
84            schedule: job.schedule().to_owned(),
85            enabled: job.enabled(),
86        })
87        .collect()
88}
89
90fn template_infos(ext: &dyn Extension) -> Vec<TemplateInfo> {
91    ext.template_providers()
92        .iter()
93        .flat_map(|provider| {
94            provider
95                .templates()
96                .iter()
97                .map(|t| TemplateInfo {
98                    name: t.name.clone(),
99                    description: t.content_types.join(", "),
100                })
101                .collect::<Vec<_>>()
102        })
103        .collect()
104}
105
106fn schema_infos(ext: &dyn Extension) -> Vec<SchemaInfo> {
107    ext.schemas()
108        .iter()
109        .map(|schema| SchemaInfo {
110            table: schema.table.clone(),
111            source: "inline".to_owned(),
112            required_columns: schema.required_columns.clone(),
113        })
114        .collect()
115}
116
117fn role_infos(ext: &dyn Extension) -> Vec<RoleInfo> {
118    ext.roles()
119        .iter()
120        .map(|role| RoleInfo {
121            name: role.name.clone(),
122            display_name: role.display_name.clone(),
123            description: role.description.clone(),
124            permissions: role.permissions.clone(),
125        })
126        .collect()
127}
128
129fn show_manifest(id: &str) -> Option<Result<CommandOutput>> {
130    let project_root = std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::new());
131    let needle = id.to_lowercase();
132    let ext = ExtensionLoader::discover(&project_root)
133        .into_iter()
134        .find(|e| e.manifest.extension.name.to_lowercase() == needle)?;
135
136    let name = ext.manifest.extension.name;
137    let output = ExtensionDetailOutput {
138        id: systemprompt_identifiers::PluginId::new(name.clone()),
139        name: name.clone(),
140        version: "manifest".to_owned(),
141        priority: 100,
142        source: ExtensionSource::Manifest,
143        dependencies: vec![],
144        config_prefix: None,
145        jobs: vec![],
146        templates: vec![],
147        schemas: vec![],
148        routes: vec![],
149        tools: vec![],
150        roles: vec![],
151        llm_providers: vec![],
152        storage_paths: vec![],
153    };
154
155    Some(Ok(CommandOutput::card_value(
156        format!("Extension: {}", name),
157        &output,
158    )))
159}