Skip to main content

starweaver_cli/
args.rs

1//! CLI argument parsing.
2
3use std::ffi::OsString;
4
5use clap::{Args, CommandFactory, Parser, Subcommand, ValueEnum};
6use clap_complete::Shell;
7use serde::{Deserialize, Serialize};
8
9use crate::{CliError, CliResult};
10
11/// Top-level CLI.
12#[derive(Clone, Debug, Parser)]
13#[command(name = "starweaver-cli", version, about = "Starweaver local CLI")]
14pub struct Cli {
15    /// Prompt shorthand for headless runs.
16    #[arg(short = 'p', long = "prompt", global = false)]
17    pub prompt: Option<String>,
18    /// Append a run to the selected session.
19    #[arg(
20        short = 's',
21        long,
22        global = false,
23        conflicts_with_all = ["new_session", "continue_session"]
24    )]
25    pub session: Option<String>,
26    /// Continue the latest local session.
27    #[arg(long = "continue", global = false, conflicts_with = "new_session")]
28    pub continue_session: bool,
29    /// Create a fresh session.
30    #[arg(long, global = false)]
31    pub new_session: bool,
32    /// Restore from a specific run before appending a run.
33    #[arg(long, global = false)]
34    pub run: Option<String>,
35    /// Branch from a specific run before appending a run.
36    #[arg(long, global = false, conflicts_with = "run")]
37    pub branch_from: Option<String>,
38    /// Agent profile name or YAML path.
39    #[arg(long, global = false)]
40    pub profile: Option<String>,
41    /// Enable worker mode or set an optional worker label.
42    #[arg(long, global = false, num_args = 0..=1, default_missing_value = "true")]
43    pub worker: Option<String>,
44    /// Explicit worker label.
45    #[arg(long = "worker-label", global = false)]
46    pub worker_label: Option<String>,
47    /// Enable a git worktree or set an optional worktree name/path.
48    #[arg(
49        short = 'w',
50        long,
51        global = false,
52        num_args = 0..=1,
53        default_missing_value = "true"
54    )]
55    pub worktree: Option<String>,
56    /// Explicit worktree name/path.
57    #[arg(long = "worktree-name", global = false)]
58    pub worktree_name: Option<String>,
59    /// Git branch for worktree metadata.
60    #[arg(long, global = false)]
61    pub branch: Option<String>,
62    /// Output mode.
63    #[arg(long, global = false)]
64    pub output: Option<OutputMode>,
65    /// Headless human-in-the-loop policy for prompt shorthand.
66    #[arg(long, global = false)]
67    pub hitl: Option<HitlPolicy>,
68    /// Override local store database path.
69    #[arg(long, global = true)]
70    pub store: Option<String>,
71    /// Optional subcommand.
72    #[command(subcommand)]
73    pub command: Option<CliCommand>,
74}
75
76/// CLI command families.
77#[allow(clippy::large_enum_variant)]
78#[derive(Clone, Debug, Subcommand)]
79pub enum CliCommand {
80    /// Print SDK identity.
81    Version,
82    /// Run a prompt.
83    Run(RunCommand),
84    /// Manage local sessions.
85    Session {
86        /// Session subcommand.
87        #[command(subcommand)]
88        command: SessionCommand,
89    },
90    /// Manage agent profiles.
91    Profile {
92        /// Profile subcommand.
93        #[command(subcommand)]
94        command: ProfileCommand,
95    },
96    /// Initialize local CLI configuration and catalogs.
97    Setup(SetupCommand),
98    /// Inspect OAuth-backed provider authentication.
99    Auth {
100        /// Auth subcommand.
101        #[command(subcommand)]
102        command: AuthCommand,
103    },
104    /// Inspect configured skills.
105    Skill {
106        /// Skill subcommand.
107        #[command(subcommand)]
108        command: CatalogCommand,
109    },
110    /// Inspect configured subagents.
111    Subagent {
112        /// Subagent subcommand.
113        #[command(subcommand)]
114        command: CatalogCommand,
115    },
116    /// Inspect configured MCP servers.
117    Mcp {
118        /// MCP subcommand.
119        #[command(subcommand)]
120        command: CatalogCommand,
121    },
122    /// Inspect default CLI tool catalog and policy.
123    Tools {
124        /// Tools subcommand.
125        #[command(subcommand)]
126        command: ToolsCommand,
127    },
128    /// Render a retained terminal UI from local session display messages.
129    Tui(TuiCommand),
130    /// Manage persisted approval requests.
131    Approval {
132        /// Approval subcommand.
133        #[command(subcommand)]
134        command: ApprovalCommand,
135    },
136    /// Manage deferred tool calls.
137    Deferred {
138        /// Deferred subcommand.
139        #[command(subcommand)]
140        command: DeferredCommand,
141    },
142    /// Resume a waiting session by appending a continuation run.
143    Resume(ResumeCommand),
144    /// Remove runtime session state while preserving configuration.
145    Reset(ResetCommand),
146    /// Print diagnostics.
147    Diagnostics,
148    /// Print replay-check guidance.
149    ReplayCheck,
150    /// Update installed Starweaver components.
151    Update(UpdateCommand),
152    /// Get or set configuration values.
153    Config {
154        /// Config subcommand.
155        #[command(subcommand)]
156        command: ConfigCommand,
157    },
158    /// Generate shell completion scripts.
159    Completion {
160        /// Target shell.
161        shell: Shell,
162    },
163}
164
165/// Prompt run command.
166#[derive(Clone, Debug, Args)]
167pub struct RunCommand {
168    /// Prompt text.
169    #[arg(short = 'p', long = "prompt")]
170    pub prompt: Option<String>,
171    /// Positional prompt text.
172    pub prompt_parts: Vec<String>,
173    /// Append a run to the selected session.
174    #[arg(short = 's', conflicts_with_all = ["new_session", "continue_session"], long)]
175    pub session: Option<String>,
176    /// Continue the latest local session.
177    #[arg(long = "continue", conflicts_with = "new_session")]
178    pub continue_session: bool,
179    /// Create a fresh session.
180    #[arg(long)]
181    pub new_session: bool,
182    /// Restore from a specific run before appending a run.
183    #[arg(long)]
184    pub run: Option<String>,
185    /// Branch from a specific run before appending a run.
186    #[arg(long, conflicts_with = "run")]
187    pub branch_from: Option<String>,
188    /// Agent profile name or YAML path.
189    #[arg(long)]
190    pub profile: Option<String>,
191    /// Enable worker mode or set an optional worker label.
192    #[arg(long, num_args = 0..=1, default_missing_value = "true")]
193    pub worker: Option<String>,
194    /// Explicit worker label.
195    #[arg(long = "worker-label")]
196    pub worker_label: Option<String>,
197    /// Enable a git worktree or set an optional worktree name/path.
198    #[arg(short = 'w', long, num_args = 0..=1, default_missing_value = "true")]
199    pub worktree: Option<String>,
200    /// Explicit worktree name/path.
201    #[arg(long = "worktree-name")]
202    pub worktree_name: Option<String>,
203    /// Git branch for worktree metadata.
204    #[arg(long)]
205    pub branch: Option<String>,
206    /// Output mode.
207    #[arg(long)]
208    pub output: Option<OutputMode>,
209    /// Headless human-in-the-loop policy.
210    #[arg(long)]
211    pub hitl: Option<HitlPolicy>,
212    /// Internal runtime goal-mode options.
213    #[arg(skip)]
214    pub goal: Option<GoalCommandOptions>,
215    /// Internal stable provider-routing affinity id.
216    #[arg(skip)]
217    pub session_affinity_id: Option<String>,
218    /// Internal host RPC environment attachments.
219    #[arg(skip)]
220    pub environment_attachments: Vec<starweaver_rpc_core::EnvironmentAttachmentRef>,
221    /// Internal marker requiring an exclusive durable HITL continuation claim.
222    #[arg(skip)]
223    pub hitl_resume: bool,
224}
225
226/// Internal goal-mode options attached by product surfaces such as the TUI.
227#[derive(Clone, Debug, Eq, PartialEq)]
228pub struct GoalCommandOptions {
229    /// Goal objective.
230    pub objective: String,
231    /// Maximum runtime goal retry iterations.
232    pub max_iterations: usize,
233}
234
235impl RunCommand {
236    /// Return prompt text.
237    pub fn prompt_text(&self) -> CliResult<String> {
238        let prompt = self
239            .prompt
240            .clone()
241            .unwrap_or_else(|| self.prompt_parts.join(" "));
242        if prompt.trim().is_empty() {
243            Err(CliError::Usage(
244                "usage: starweaver-cli run -p <prompt>".to_string(),
245            ))
246        } else {
247            Ok(prompt)
248        }
249    }
250}
251
252/// Compact session commands.
253#[derive(Clone, Debug, Subcommand)]
254pub enum SessionCommand {
255    /// List local sessions.
256    List(SessionListCommand),
257    /// Search local sessions by metadata and approved text projections.
258    Search(SessionSearchCommand),
259    /// Show one session with recent runs.
260    Show(SessionShowCommand),
261    /// Replay stored display messages.
262    Replay(SessionReplayCommand),
263    /// Delete one local session and its retained evidence.
264    Delete(SessionDeleteCommand),
265    /// Trim retained run evidence.
266    Trim(SessionTrimCommand),
267}
268
269/// Session list command.
270#[derive(Clone, Debug, Args)]
271pub struct SessionListCommand {
272    /// Output mode.
273    #[arg(long, default_value = "display-jsonl")]
274    pub output: OutputMode,
275    /// Maximum sessions to show.
276    #[arg(long, default_value_t = 50)]
277    pub limit: usize,
278}
279
280/// Session search command.
281#[derive(Clone, Debug, Args)]
282pub struct SessionSearchCommand {
283    /// Optional case-insensitive literal text. Omit it to browse metadata.
284    pub text: Option<String>,
285    /// Exact session status.
286    #[arg(long)]
287    pub status: Option<SessionSearchStatusArg>,
288    /// Exact profile name.
289    #[arg(long)]
290    pub profile: Option<String>,
291    /// Exact workspace display value.
292    #[arg(long)]
293    pub workspace: Option<String>,
294    /// Search source; repeat to select multiple sources.
295    #[arg(long = "source", value_enum)]
296    pub sources: Vec<SessionSearchSourceArg>,
297    /// Result grouping level.
298    #[arg(long, value_enum, default_value = "session")]
299    pub granularity: SessionSearchGranularityArg,
300    /// Maximum hits to return.
301    #[arg(long, default_value_t = 20)]
302    pub limit: u32,
303    /// Opaque next-page cursor.
304    #[arg(long = "after")]
305    pub cursor: Option<String>,
306    /// Output mode.
307    #[arg(long, default_value = "text")]
308    pub output: OutputMode,
309}
310
311/// Session status accepted by search.
312#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
313#[value(rename_all = "snake_case")]
314pub enum SessionSearchStatusArg {
315    /// Active session.
316    Active,
317    /// Archived session.
318    Archived,
319    /// Failed session.
320    Failed,
321}
322
323/// Search source accepted by the CLI.
324#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
325#[value(rename_all = "snake_case")]
326pub enum SessionSearchSourceArg {
327    /// Session title and approved metadata.
328    SessionMetadata,
329    /// Canonical text input.
330    RunInput,
331    /// Bounded run output preview.
332    RunOutputPreview,
333    /// User-visible display messages.
334    #[value(alias = "display")]
335    DisplayMessage,
336}
337
338/// Search grouping accepted by the CLI.
339#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
340#[value(rename_all = "snake_case")]
341pub enum SessionSearchGranularityArg {
342    /// One hit per session.
343    Session,
344    /// One hit per session/run pair.
345    Run,
346    /// Every stable projected occurrence.
347    Occurrence,
348}
349
350/// Session show command.
351#[derive(Clone, Debug, Args)]
352pub struct SessionShowCommand {
353    /// Session id.
354    pub session_id: String,
355    /// Output mode.
356    #[arg(long, default_value = "display-jsonl")]
357    pub output: OutputMode,
358    /// Recent run limit.
359    #[arg(long, default_value_t = 20)]
360    pub runs: usize,
361}
362
363/// Session replay command.
364#[derive(Clone, Debug, Args)]
365pub struct SessionReplayCommand {
366    /// Session id.
367    pub session_id: String,
368    /// Optional run id.
369    #[arg(long)]
370    pub run: Option<String>,
371    /// Cursor sequence to replay after.
372    #[arg(long)]
373    pub after: Option<usize>,
374    /// Output mode.
375    #[arg(long, default_value = "display-jsonl")]
376    pub output: OutputMode,
377}
378
379/// Session delete command.
380#[derive(Clone, Debug, Args)]
381pub struct SessionDeleteCommand {
382    /// Session id or unique prefix.
383    pub session_id: String,
384    /// Confirm deletion.
385    #[arg(long)]
386    pub yes: bool,
387    /// Output mode.
388    #[arg(long, default_value = "text")]
389    pub output: OutputMode,
390}
391
392/// Session trim command.
393#[derive(Clone, Debug, Args)]
394pub struct SessionTrimCommand {
395    /// Trim current session.
396    #[arg(long)]
397    pub current: bool,
398    /// Trim all sessions.
399    #[arg(long)]
400    pub all: bool,
401    /// Trim a selected session.
402    #[arg(long)]
403    pub session: Option<String>,
404    /// Retain this many recent runs per session.
405    #[arg(long, default_value_t = 20)]
406    pub keep_runs: usize,
407    /// Trim runs older than a duration such as 7d, 24h, or 3600s.
408    #[arg(long)]
409    pub older_than: Option<String>,
410    /// Preview trim results.
411    #[arg(long)]
412    pub dry_run: bool,
413    /// Output mode.
414    #[arg(long, default_value = "display-jsonl")]
415    pub output: OutputMode,
416}
417
418/// Profile commands.
419#[derive(Clone, Debug, Subcommand)]
420pub enum ProfileCommand {
421    /// List built-in and configured profiles.
422    List,
423    /// Show one built-in or configured profile.
424    Show { name: String },
425}
426
427/// Setup command.
428#[derive(Clone, Debug, Args)]
429pub struct SetupCommand {
430    /// Initialize global configuration only.
431    #[arg(long, conflicts_with = "project")]
432    pub global: bool,
433    /// Initialize project configuration only.
434    #[arg(long)]
435    pub project: bool,
436    /// Replace existing generated files.
437    #[arg(long)]
438    pub force: bool,
439}
440
441/// Auth commands.
442#[derive(Clone, Debug, Subcommand)]
443pub enum AuthCommand {
444    /// Log in to an OAuth provider.
445    Login(AuthProviderCommand),
446    /// Print provider auth status.
447    Status(AuthStatusCommand),
448    /// Refresh provider credentials.
449    Refresh(AuthProviderCommand),
450    /// Remove provider credentials from the local auth store.
451    Logout(AuthLogoutCommand),
452    /// Inspect OAuth store health without printing tokens.
453    Doctor(AuthDoctorCommand),
454}
455
456/// Provider-scoped auth command.
457#[derive(Clone, Debug, Args)]
458pub struct AuthProviderCommand {
459    /// Provider name.
460    #[arg(default_value = "codex", value_parser = ["codex"])]
461    pub provider: String,
462    /// Auth file path. Defaults to ~/.starweaver/auth.json.
463    #[arg(long = "auth-file")]
464    pub auth_file: Option<String>,
465    /// Device authorization timeout in seconds.
466    #[arg(long, default_value_t = 15 * 60)]
467    pub timeout_seconds: u64,
468}
469
470/// Auth status command.
471#[derive(Clone, Debug, Args)]
472pub struct AuthStatusCommand {
473    /// Provider name.
474    #[arg(default_value = "codex", value_parser = ["codex"])]
475    pub provider: Option<String>,
476    /// Auth file path. Defaults to ~/.starweaver/auth.json.
477    #[arg(long = "auth-file")]
478    pub auth_file: Option<String>,
479}
480
481/// Auth logout command.
482#[derive(Clone, Debug, Args)]
483pub struct AuthLogoutCommand {
484    /// Provider name.
485    #[arg(default_value = "codex", value_parser = ["codex"])]
486    pub provider: String,
487    /// Auth file path. Defaults to ~/.starweaver/auth.json.
488    #[arg(long = "auth-file")]
489    pub auth_file: Option<String>,
490    /// Revoke provider tokens before deleting local credentials.
491    #[arg(long, default_value_t = true, action = clap::ArgAction::Set)]
492    pub revoke: bool,
493}
494
495/// Auth doctor command.
496#[derive(Clone, Debug, Args)]
497pub struct AuthDoctorCommand {
498    /// Auth file path. Defaults to ~/.starweaver/auth.json.
499    #[arg(long = "auth-file")]
500    pub auth_file: Option<String>,
501}
502
503/// Catalog inspection commands.
504#[derive(Clone, Debug, Subcommand)]
505pub enum CatalogCommand {
506    /// List configured entries.
507    List,
508    /// Show one configured entry.
509    Show { name: String },
510    /// Validate configured entries and print findings.
511    Doctor,
512}
513
514/// Tool catalog commands.
515#[derive(Clone, Debug, Subcommand)]
516pub enum ToolsCommand {
517    /// List default first-party tools.
518    List,
519    /// Validate tool policy and default catalog.
520    Doctor,
521}
522
523/// TUI command.
524#[derive(Clone, Debug, Args)]
525pub struct TuiCommand {
526    /// Session id to render. Omit for a clean welcome screen.
527    #[arg(long)]
528    pub session: Option<String>,
529    /// Optional run id to render.
530    #[arg(long)]
531    pub run: Option<String>,
532    /// Render only messages after this display cursor.
533    #[arg(long)]
534    pub after: Option<usize>,
535    /// Force interactive terminal UI when stdout is a TTY.
536    #[arg(long)]
537    pub interactive: bool,
538    /// Force deterministic snapshot output for scripts and tests.
539    #[arg(long, conflicts_with = "interactive")]
540    pub snapshot: bool,
541    /// Output mode for non-interactive TUI snapshots.
542    #[arg(long, default_value = "text")]
543    pub output: OutputMode,
544    /// Transcript rendering mode for interactive TUI.
545    #[arg(long = "render-mode")]
546    pub render_mode: Option<TuiRenderMode>,
547}
548
549/// Approval commands.
550#[derive(Clone, Debug, Subcommand)]
551pub enum ApprovalCommand {
552    /// List persisted approval records.
553    List(ApprovalListCommand),
554    /// Show one approval record.
555    Show { approval_id: String },
556    /// Approve one pending approval record.
557    Approve(ApprovalDecisionCommand),
558    /// Reject one pending approval record.
559    Reject(ApprovalDecisionCommand),
560}
561
562/// Approval list command.
563#[derive(Clone, Debug, Args)]
564pub struct ApprovalListCommand {
565    /// Filter by session id.
566    #[arg(long)]
567    pub session: Option<String>,
568    /// Filter by run id.
569    #[arg(long)]
570    pub run: Option<String>,
571    /// Output mode.
572    #[arg(long, default_value = "display-jsonl")]
573    pub output: OutputMode,
574}
575
576/// Approval decision command.
577#[derive(Clone, Debug, Args)]
578pub struct ApprovalDecisionCommand {
579    /// Approval id.
580    pub approval_id: String,
581    /// Decision reason.
582    #[arg(long)]
583    pub reason: Option<String>,
584    /// Output mode.
585    #[arg(long, default_value = "text")]
586    pub output: OutputMode,
587}
588
589/// Deferred tool commands.
590#[derive(Clone, Debug, Subcommand)]
591pub enum DeferredCommand {
592    /// List persisted deferred tool records.
593    List(DeferredListCommand),
594    /// Show one deferred tool record.
595    Show { deferred_id: String },
596    /// Complete one deferred tool record with a JSON result payload.
597    Complete(DeferredCompleteCommand),
598    /// Fail one deferred tool record with an error message.
599    Fail(DeferredFailCommand),
600}
601
602/// Deferred list command.
603#[derive(Clone, Debug, Args)]
604pub struct DeferredListCommand {
605    /// Filter by session id.
606    #[arg(long)]
607    pub session: Option<String>,
608    /// Filter by run id.
609    #[arg(long)]
610    pub run: Option<String>,
611    /// Output mode.
612    #[arg(long, default_value = "display-jsonl")]
613    pub output: OutputMode,
614}
615
616/// Deferred complete command.
617#[derive(Clone, Debug, Args)]
618pub struct DeferredCompleteCommand {
619    /// Deferred id.
620    pub deferred_id: String,
621    /// JSON result payload.
622    #[arg(long)]
623    pub result: String,
624    /// Output mode.
625    #[arg(long, default_value = "text")]
626    pub output: OutputMode,
627}
628
629/// Deferred failure command.
630#[derive(Clone, Debug, Args)]
631pub struct DeferredFailCommand {
632    /// Deferred id.
633    pub deferred_id: String,
634    /// Error message.
635    #[arg(long)]
636    pub error: String,
637    /// Output mode.
638    #[arg(long, default_value = "text")]
639    pub output: OutputMode,
640}
641
642/// Resume command.
643#[derive(Clone, Debug, Args)]
644pub struct ResumeCommand {
645    /// Session id to resume. Defaults to current or latest session.
646    #[arg(long)]
647    pub session: Option<String>,
648    /// Run id to resume from. Defaults to the session active or head run.
649    #[arg(long)]
650    pub run: Option<String>,
651    /// Prompt to append for the continuation run.
652    #[arg(short = 'p', long = "prompt", default_value = "resume waiting run")]
653    pub prompt: String,
654    /// Output mode.
655    #[arg(long)]
656    pub output: Option<OutputMode>,
657    /// Headless human-in-the-loop policy.
658    #[arg(long)]
659    pub hitl: Option<HitlPolicy>,
660}
661
662/// Reset command.
663#[derive(Clone, Debug, Args)]
664pub struct ResetCommand {
665    /// Confirm runtime state removal.
666    #[arg(long)]
667    pub yes: bool,
668    /// Output mode.
669    #[arg(long, default_value = "text")]
670    pub output: OutputMode,
671}
672
673/// Update command.
674#[derive(Clone, Debug, Args)]
675pub struct UpdateCommand {
676    /// Update target, defaults to cli.
677    #[arg(default_value = "cli")]
678    pub target: String,
679    /// Print the update plan without downloading or installing.
680    #[arg(long)]
681    pub dry_run: bool,
682    /// Reinstall even when the selected release matches the current version.
683    #[arg(long, short = 'f')]
684    pub force: bool,
685}
686
687/// Config commands.
688#[derive(Clone, Debug, Subcommand)]
689pub enum ConfigCommand {
690    /// Initialize a Starweaver config file.
691    Init {
692        /// Write the global config file.
693        #[arg(long, conflicts_with = "project")]
694        global: bool,
695        /// Write the project config file.
696        #[arg(long)]
697        project: bool,
698        /// Replace an existing config file.
699        #[arg(long)]
700        force: bool,
701    },
702    /// Get a resolved config value.
703    Get { key: String },
704    /// Set a config value.
705    Set {
706        /// Write the global config file.
707        #[arg(long, conflicts_with = "project")]
708        global: bool,
709        /// Write the project config file.
710        #[arg(long)]
711        project: bool,
712        /// Config key.
713        key: String,
714        /// Config value.
715        value: String,
716    },
717}
718
719/// TUI transcript render mode.
720#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize, ValueEnum)]
721#[serde(rename_all = "kebab-case")]
722pub enum TuiRenderMode {
723    /// Show assistant text, reasoning, tool calls, and tool returns.
724    #[default]
725    Normal,
726    /// Hide ordinary tool calls from transcript while keeping high-level events.
727    Concise,
728    /// Show detailed diagnostic rendering.
729    Debug,
730}
731
732/// Output mode.
733#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize, ValueEnum)]
734#[serde(rename_all = "kebab-case")]
735pub enum OutputMode {
736    /// Human-readable text.
737    Text,
738    /// Starweaver durable `DisplayMessage` JSON lines.
739    #[default]
740    DisplayJsonl,
741    /// Starweaver/AGUI top-level event JSON lines.
742    AguiJsonl,
743    /// Compact JSON command result.
744    Json,
745    /// Persist and print compact status.
746    Silent,
747}
748
749/// HITL policy.
750#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize, ValueEnum)]
751#[serde(rename_all = "snake_case")]
752pub enum HitlPolicy {
753    /// Deny approvals.
754    Deny,
755    /// Defer approvals.
756    Defer,
757    /// Fail on approvals.
758    Fail,
759    /// Prompt interactively.
760    #[default]
761    Prompt,
762}
763
764/// Build the clap command schema.
765#[must_use]
766pub fn command() -> clap::Command {
767    Cli::command()
768}
769
770/// Parse CLI arguments.
771pub fn parse(args: impl IntoIterator<Item = String>) -> CliResult<Cli> {
772    Cli::try_parse_from(args).map_err(|error| clap_error(&error))
773}
774
775/// Parse CLI arguments from OS strings.
776#[allow(dead_code)]
777pub fn parse_os(args: impl IntoIterator<Item = OsString>) -> CliResult<Cli> {
778    Cli::try_parse_from(args).map_err(|error| clap_error(&error))
779}
780
781fn clap_error(error: &clap::Error) -> CliError {
782    match error.kind() {
783        clap::error::ErrorKind::DisplayHelp | clap::error::ErrorKind::DisplayVersion => {
784            CliError::Display(error.to_string())
785        }
786        _ => CliError::Usage(error.to_string()),
787    }
788}