systemprompt_cli/commands/core/plugins/
show.rs1use anyhow::{Context, Result, anyhow};
7use clap::Args;
8use std::path::Path;
9
10use crate::CliConfig;
11use crate::shared::CommandOutput;
12
13use super::types::{PluginComponentRef, PluginDetailOutput};
14
15#[derive(Debug, Clone, Args)]
16pub struct ShowArgs {
17 #[arg(help = "Plugin ID (directory name)")]
18 pub id: String,
19}
20
21pub(super) fn execute(args: &ShowArgs, _config: &CliConfig) -> Result<CommandOutput> {
22 let plugins_path = get_plugins_path()?;
23 execute_with_path(args, &plugins_path)
24}
25
26pub fn execute_with_path(args: &ShowArgs, plugins_path: &Path) -> Result<CommandOutput> {
27 let plugin_dir = plugins_path.join(&args.id);
28
29 if !plugin_dir.exists() {
30 return Err(anyhow!("Plugin '{}' not found", args.id));
31 }
32
33 let config_path = plugin_dir.join("config.yaml");
34 if !config_path.exists() {
35 return Err(anyhow!("Plugin '{}' has no config.yaml file", args.id));
36 }
37
38 let plugin_file = parse_plugin_config(&config_path)?;
39 let plugin = &plugin_file.plugin;
40
41 let output = PluginDetailOutput {
42 id: systemprompt_identifiers::PluginId::new(plugin.id.clone()),
43 name: plugin.name.clone(),
44 description: plugin.description.clone(),
45 version: plugin.version.clone(),
46 enabled: plugin.enabled,
47 skills: PluginComponentRef {
48 source: plugin.skills.source,
49 filter: plugin.skills.filter,
50 include: plugin.skills.include.clone(),
51 exclude: plugin.skills.exclude.clone(),
52 },
53 agents: PluginComponentRef {
54 source: plugin.agents.source,
55 filter: plugin.agents.filter,
56 include: plugin.agents.include.clone(),
57 exclude: plugin.agents.exclude.clone(),
58 },
59 mcp_servers: plugin.mcp_servers.clone(),
60 scripts: plugin.scripts.iter().map(|s| s.name.clone()).collect(),
61 keywords: plugin.keywords.clone(),
62 category: plugin.category.clone(),
63 author: plugin.author.name.clone(),
64 };
65
66 Ok(CommandOutput::card_value(
67 format!("Plugin: {}", args.id),
68 &output,
69 ))
70}
71
72fn get_plugins_path() -> Result<std::path::PathBuf> {
73 let profile = systemprompt_config::ProfileBootstrap::get().context("Failed to get profile")?;
74 Ok(std::path::PathBuf::from(profile.paths.plugins()))
75}
76
77fn parse_plugin_config(config_path: &Path) -> Result<systemprompt_models::PluginConfigFile> {
78 let content = std::fs::read_to_string(config_path)
79 .with_context(|| format!("Failed to read {}", config_path.display()))?;
80 let plugin_file: systemprompt_models::PluginConfigFile = serde_yaml::from_str(&content)
81 .with_context(|| format!("Failed to parse {}", config_path.display()))?;
82 Ok(plugin_file)
83}