systemprompt_cli/commands/plugins/
show.rs1use 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}