Skip to main content

systemprompt_cli/commands/core/plugins/
show.rs

1//! `core plugins show` command for one plugin config.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use 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};
14use systemprompt_loader::ServicesRootBootstrap;
15
16#[derive(Debug, Clone, Args)]
17pub struct ShowArgs {
18    #[arg(help = "Plugin ID (directory name)")]
19    pub id: String,
20}
21
22pub(super) fn execute(args: &ShowArgs, _config: &CliConfig) -> Result<CommandOutput> {
23    let plugins_path = get_plugins_path()?;
24    execute_with_path(args, &plugins_path)
25}
26
27pub fn execute_with_path(args: &ShowArgs, plugins_path: &Path) -> Result<CommandOutput> {
28    let plugin_dir = plugins_path.join(&args.id);
29
30    if !plugin_dir.exists() {
31        return Err(anyhow!("Plugin '{}' not found", args.id));
32    }
33
34    let config_path = plugin_dir.join("config.yaml");
35    if !config_path.exists() {
36        return Err(anyhow!("Plugin '{}' has no config.yaml file", args.id));
37    }
38
39    let plugin_file = parse_plugin_config(&config_path)?;
40    let plugin = &plugin_file.plugin;
41
42    let output = PluginDetailOutput {
43        id: systemprompt_identifiers::PluginId::new(plugin.id.clone()),
44        name: plugin.name.clone(),
45        description: plugin.description.clone(),
46        version: plugin.version.clone(),
47        enabled: plugin.enabled,
48        skills: PluginComponentRef {
49            source: plugin.skills.source,
50            filter: plugin.skills.filter,
51            include: plugin.skills.include.clone(),
52            exclude: plugin.skills.exclude.clone(),
53        },
54        agents: PluginComponentRef {
55            source: plugin.agents.source,
56            filter: plugin.agents.filter,
57            include: plugin.agents.include.clone(),
58            exclude: plugin.agents.exclude.clone(),
59        },
60        mcp_servers: plugin.mcp_servers.clone(),
61        scripts: plugin.scripts.iter().map(|s| s.name.clone()).collect(),
62        keywords: plugin.keywords.clone(),
63        category: plugin.category.clone(),
64        author: plugin.author.name.clone(),
65    };
66
67    Ok(CommandOutput::card_value(
68        format!("Plugin: {}", args.id),
69        &output,
70    ))
71}
72
73fn get_plugins_path() -> Result<std::path::PathBuf> {
74    let profile = systemprompt_config::ProfileBootstrap::get().context("Failed to get profile")?;
75    Ok(ServicesRootBootstrap::active_path_or(
76        &profile.paths.services,
77        "plugins",
78    ))
79}
80
81fn parse_plugin_config(config_path: &Path) -> Result<systemprompt_models::PluginConfigFile> {
82    let content = std::fs::read_to_string(config_path)
83        .with_context(|| format!("Failed to read {}", config_path.display()))?;
84    let plugin_file: systemprompt_models::PluginConfigFile = serde_yaml::from_str(&content)
85        .with_context(|| format!("Failed to parse {}", config_path.display()))?;
86    Ok(plugin_file)
87}