Skip to main content

ralph/cli/
plugin.rs

1//! Plugin CLI surface.
2//!
3//! Responsibilities:
4//! - Define Clap args for plugin management.
5//!
6//! Not handled here:
7//! - Filesystem operations (see `crate::commands::plugin`).
8//!
9//! Invariants/assumptions:
10//! - `install` copies a plugin directory containing `plugin.json` into the chosen scope.
11//! - Installing does NOT auto-enable the plugin (security).
12
13use clap::{Args, Subcommand, ValueEnum};
14use std::path::PathBuf;
15
16#[derive(Args)]
17pub struct PluginArgs {
18    #[command(subcommand)]
19    pub command: PluginCommand,
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
23pub enum PluginScopeArg {
24    Project,
25    Global,
26}
27
28#[derive(Args, Debug, Clone)]
29#[command(
30    after_long_help = "Examples:\n  ralph plugin init acme.super_runner\n  ralph plugin init acme.super_runner --with-runner\n  ralph plugin init acme.super_runner --with-processor\n  ralph plugin init acme.super_runner --scope global\n  ralph plugin init acme.super_runner --dry-run\n"
31)]
32pub struct PluginInitArgs {
33    /// Plugin ID (used as directory name in default layout).
34    #[arg(value_name = "PLUGIN_ID")]
35    pub id: String,
36
37    /// Where to scaffold the plugin (ignored when --path is provided).
38    #[arg(long, value_enum, default_value = "project")]
39    pub scope: PluginScopeArg,
40
41    /// Target plugin directory (overrides --scope). Relative paths are resolved from repo root.
42    #[arg(long, value_name = "DIR")]
43    pub path: Option<PathBuf>,
44
45    /// Manifest name (default: derived from id).
46    #[arg(long)]
47    pub name: Option<String>,
48
49    /// Manifest version (SemVer string).
50    #[arg(long, default_value = "0.1.0")]
51    pub version: String,
52
53    /// Optional manifest description.
54    #[arg(long)]
55    pub description: Option<String>,
56
57    /// Include runner stub + runner manifest section.
58    #[arg(long)]
59    pub with_runner: bool,
60
61    /// Include processor stub + processors manifest section.
62    #[arg(long)]
63    pub with_processor: bool,
64
65    /// Preview what would be written without creating files.
66    #[arg(long)]
67    pub dry_run: bool,
68
69    /// Overwrite scaffolded files if the directory already exists.
70    #[arg(long)]
71    pub force: bool,
72}
73
74#[derive(Subcommand)]
75pub enum PluginCommand {
76    /// List discovered plugins (global + project) and whether they are enabled.
77    #[command(after_long_help = "Examples:\n  ralph plugin list\n  ralph plugin list --json\n")]
78    List {
79        /// Output JSON instead of human-readable text.
80        #[arg(long)]
81        json: bool,
82    },
83
84    /// Validate discovered plugin manifests and referenced executables.
85    #[command(
86        after_long_help = "Examples:\n  ralph plugin validate\n  ralph plugin validate --id acme.super_runner\n"
87    )]
88    Validate {
89        /// Validate only a single plugin id.
90        #[arg(long)]
91        id: Option<String>,
92    },
93
94    /// Install a plugin from a local directory (must contain plugin.json).
95    #[command(
96        after_long_help = "Examples:\n  ralph plugin install ./my-plugin --scope project\n  ralph plugin install ./my-plugin --scope global\n\nNotes:\n  - Install does not enable the plugin. Enable via config.plugins.plugins.<id>.enabled=true\n"
97    )]
98    Install {
99        /// Source directory containing plugin.json
100        source: String,
101
102        /// Install scope: project or global
103        #[arg(long, value_enum, default_value = "project")]
104        scope: PluginScopeArg,
105    },
106
107    /// Uninstall a plugin by id from the chosen scope.
108    #[command(
109        after_long_help = "Examples:\n  ralph plugin uninstall acme.super_runner --scope project\n"
110    )]
111    Uninstall {
112        id: String,
113
114        #[arg(long, value_enum, default_value = "project")]
115        scope: PluginScopeArg,
116    },
117
118    /// Scaffold a new plugin directory with plugin.json and optional scripts.
119    #[command(
120        after_long_help = "Examples:\n  ralph plugin init acme.super_runner\n  ralph plugin init acme.super_runner --with-runner\n  ralph plugin init acme.super_runner --with-processor\n  ralph plugin init acme.super_runner --scope global\n  ralph plugin init acme.super_runner --dry-run\n"
121    )]
122    Init(PluginInitArgs),
123}