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