Skip to main content

systemprompt_cli/runner/args/
mod.rs

1//! Top-level clap argument definitions and the command tree.
2//!
3//! Defines the global option groups, the [`Cli`] parser, and the [`Commands`]
4//! subcommand tree, along with the mapping from each command to its bootstrap
5//! [`CommandDescriptor`] and the argument-reconstruction used when forwarding
6//! to a remote tenant.
7//!
8//! Copyright (c) systemprompt.io — Business Source License 1.1.
9//! See <https://systemprompt.io> for licensing details.
10
11use clap::{Parser, Subcommand};
12
13use crate::commands::{admin, analytics, build, cloud, core, infrastructure, plugins, web};
14use crate::descriptor::{CommandDescriptor, DescribeCommand};
15
16#[derive(Debug, Clone, Copy, clap::Args)]
17pub struct VerbosityOpts {
18    #[arg(
19        long,
20        short = 'v',
21        global = true,
22        hide = true,
23        help = "Increase verbosity"
24    )]
25    pub verbose: bool,
26
27    #[arg(
28        long,
29        short = 'q',
30        global = true,
31        hide = true,
32        conflicts_with = "verbose",
33        help = "Suppress output"
34    )]
35    pub quiet: bool,
36
37    #[arg(long, global = true, hide = true, help = "Debug logging")]
38    pub debug: bool,
39}
40
41#[derive(Debug, Clone, Copy, clap::Args)]
42pub struct OutputOpts {
43    #[arg(long, global = true, hide = true, help = "JSON output")]
44    pub json: bool,
45
46    #[arg(
47        long,
48        global = true,
49        hide = true,
50        conflicts_with = "json",
51        help = "YAML output"
52    )]
53    pub yaml: bool,
54}
55
56#[derive(Debug, Clone, Copy, clap::Args)]
57pub struct DisplayOpts {
58    #[arg(long, global = true, hide = true, help = "Disable colors")]
59    pub no_color: bool,
60
61    #[arg(long, global = true, hide = true, help = "Non-interactive mode")]
62    pub non_interactive: bool,
63}
64
65#[derive(Debug, clap::Args)]
66pub struct DatabaseOpts {
67    #[arg(
68        long,
69        global = true,
70        env = "SYSTEMPROMPT_DATABASE_URL",
71        help = "Direct database URL (bypasses profile)"
72    )]
73    pub database_url: Option<String>,
74}
75
76#[derive(Debug, clap::Args)]
77pub struct ProfileOpts {
78    #[arg(
79        long,
80        global = true,
81        help = "Profile name to use (overrides active session)"
82    )]
83    pub profile: Option<String>,
84}
85
86#[derive(Debug, Parser)]
87#[command(name = "systemprompt")]
88#[command(about = "Agent orchestration and AI operations.")]
89#[command(version = env!("CARGO_PKG_VERSION"))]
90#[command(
91    before_help = "\x1b[38;5;208m</\x1b[1;37mSYSTEMPROMPT\x1b[38;5;208m.\x1b[0;37mio\x1b[38;5;\
92                   208m>\x1b[0m"
93)]
94#[command(after_help = "\
95GETTING STARTED:
96  systemprompt core skills list                 List all skills
97
98GLOBAL OPTIONS (apply to all commands):
99  -v, --verbose         Increase verbosity
100  -q, --quiet           Suppress output
101      --debug           Debug logging
102      --json            JSON output
103      --yaml            YAML output
104      --no-color        Disable colors
105      --non-interactive Non-interactive mode
106      --database-url    Direct database URL (bypasses profile)
107      --profile         Profile name to use (overrides active session)")]
108pub struct Cli {
109    #[command(flatten)]
110    pub verbosity: VerbosityOpts,
111
112    #[command(flatten)]
113    pub output: OutputOpts,
114
115    #[command(flatten)]
116    pub display: DisplayOpts,
117
118    #[command(flatten)]
119    pub database: DatabaseOpts,
120
121    #[command(flatten)]
122    pub profile_opts: ProfileOpts,
123
124    #[command(subcommand)]
125    pub command: Option<Commands>,
126}
127
128#[derive(Debug, Subcommand)]
129pub enum Commands {
130    #[command(
131        subcommand,
132        about = "Core operations: skills, content, files, contexts"
133    )]
134    Core(core::CoreCommands),
135
136    #[command(
137        subcommand,
138        about = "Infrastructure management (services, db, jobs, logs, system)"
139    )]
140    Infra(infrastructure::InfraCommands),
141
142    #[command(
143        subcommand,
144        about = "Administration (users, agents, config, setup, session)"
145    )]
146    Admin(admin::AdminCommands),
147
148    #[command(subcommand, about = "Cloud deployment, backup, and setup")]
149    Cloud(cloud::CloudCommands),
150
151    #[command(subcommand, about = "Analytics and metrics reporting")]
152    Analytics(analytics::AnalyticsCommands),
153
154    #[command(subcommand, about = "Web service configuration management")]
155    Web(web::WebCommands),
156
157    #[command(subcommand, about = "Plugins, extensions, and MCP server management")]
158    Plugins(plugins::PluginsCommands),
159
160    #[command(subcommand, about = "Build MCP extensions")]
161    Build(build::BuildCommands),
162}
163
164impl DescribeCommand for Commands {
165    fn descriptor(&self) -> CommandDescriptor {
166        match self {
167            Self::Cloud(cmd) => cmd.descriptor(),
168            Self::Plugins(cmd) => cmd.descriptor(),
169            Self::Admin(admin::AdminCommands::Setup(_)) => CommandDescriptor::NONE,
170            Self::Admin(admin::AdminCommands::Session(cmd)) => cmd.descriptor(),
171            Self::Admin(admin::AdminCommands::Config(admin::config::ConfigCommands::Secret(_)))
172            | Self::Build(_) => CommandDescriptor::PROFILE_ONLY,
173            Self::Admin(admin::AdminCommands::Config(_))
174            | Self::Web(_)
175            | Self::Core(
176                core::CoreCommands::Hooks(_)
177                | core::CoreCommands::Plugins(_)
178                | core::CoreCommands::Skills(
179                    core::skills::SkillsCommands::List(_) | core::skills::SkillsCommands::Show(_),
180                ),
181            ) => CommandDescriptor::PROFILE_SECRETS_AND_PATHS,
182            Self::Infra(infrastructure::InfraCommands::Services(_)) => {
183                CommandDescriptor::PROFILE_SECRETS_AND_PATHS
184            },
185            Self::Infra(infrastructure::InfraCommands::Jobs(
186                infrastructure::jobs::JobsCommands::Run(_)
187                | infrastructure::jobs::JobsCommands::List,
188            )) => CommandDescriptor::FULL.with_skip_validation(),
189            // Why: reads. They may fall back to local data with a warning rather
190            // than refusing when a cloud profile cannot route remotely.
191            Self::Analytics(_) => CommandDescriptor::FULL
192                .with_skip_validation()
193                .with_read_only(),
194            Self::Infra(infrastructure::InfraCommands::Logs(_)) => {
195                CommandDescriptor::FULL.with_read_only()
196            },
197            _ => CommandDescriptor::FULL,
198        }
199    }
200}
201
202mod assemble;
203
204pub use assemble::{
205    build_cli_config, has_local_export_flag, has_local_export_flag_in, reconstruct_args,
206    reconstruct_args_from,
207};