Skip to main content

rusty_commit/
cli.rs

1use clap::{Parser, Subcommand};
2
3use crate::output::prelude::OutputFormat;
4
5#[derive(Parser)]
6#[command(
7    name = "rco",
8    version,
9    author,
10    about = "Rusty Commit - AI-powered commit message generator written in Rust 🚀🤖",
11    after_help = r#"EXAMPLES:
12    # Generate a commit message for staged changes
13    rco
14
15    # Generate with context and copy to clipboard
16    rco -c "Focus on auth changes" --clipboard
17
18    # Generate 3 variations and skip confirmation
19    rco -g 3 -y
20
21    # Use GitMoji format
22    rco --fgm
23
24    # Use a custom prompt file
25    rco --prompt-file ~/.rco/prompts/my-prompt.md
26
27    # Preview commit message without committing (dry-run)
28    rco --dry-run
29
30    # Open generated message in $EDITOR before committing
31    rco --edit
32
33    # Authenticate with Anthropic
34    rco auth login
35
36    # Setup git hooks
37    rco hook set
38
39    # Generate PR description
40    rco pr generate --base main
41
42    # List available skills
43    rco skills list
44
45    # Create a new skill
46    rco skills create my-template --category template
47
48    # Use a skill for commit generation
49    rco --skill my-template
50
51    # Generate shell completions
52    rco completions bash
53    rco completions zsh
54    rco completions fish
55"#
56)]
57pub struct Cli {
58    #[command(subcommand)]
59    pub command: Option<Commands>,
60
61    #[command(flatten)]
62    pub global: GlobalOptions,
63}
64
65#[derive(Parser, Clone)]
66pub struct GlobalOptions {
67    /// Use full GitMoji specification
68    #[arg(long = "fgm", default_value = "false")]
69    pub full_gitmoji: bool,
70
71    /// Additional user input context for the commit message
72    #[arg(short = 'c', long = "context")]
73    pub context: Option<String>,
74
75    /// Skip commit confirmation prompt
76    #[arg(short = 'y', long = "yes", default_value = "false")]
77    pub skip_confirmation: bool,
78
79    /// Show the prompt that would be used without generating commit
80    #[arg(long = "show-prompt", default_value = "false")]
81    pub show_prompt: bool,
82
83    /// Disable running pre-hooks
84    #[arg(long = "no-pre-hooks", default_value = "false")]
85    pub no_pre_hooks: bool,
86
87    /// Disable running post-hooks
88    #[arg(long = "no-post-hooks", default_value = "false")]
89    pub no_post_hooks: bool,
90
91    /// Number of commit message variations to generate (1-5)
92    #[arg(short = 'g', long = "generate", default_value = "1")]
93    pub generate_count: u8,
94
95    /// Copy generated message to clipboard instead of committing
96    #[arg(short = 'C', long = "clipboard", default_value = "false")]
97    pub clipboard: bool,
98
99    /// Exclude specific files from the diff sent to AI
100    #[arg(short = 'x', long = "exclude")]
101    pub exclude_files: Option<Vec<String>>,
102
103    /// Show detailed timing information
104    #[arg(long = "timing", default_value = "false")]
105    pub timing: bool,
106
107    /// Strip <thinking> tags from AI responses (for reasoning models)
108    #[arg(long = "strip-thinking", default_value = "false")]
109    pub strip_thinking: bool,
110
111    /// Output commit message to stdout instead of committing (for hooks)
112    #[arg(long = "print", default_value = "false")]
113    pub print_message: bool,
114
115    /// Output format (pretty, json, markdown)
116    #[arg(long = "output-format", default_value = "pretty")]
117    pub output_format: OutputFormat,
118
119    /// Use a custom prompt template file
120    #[arg(long = "prompt-file")]
121    pub prompt_file: Option<String>,
122
123    /// Preview the generated commit message without committing (dry-run mode)
124    #[arg(long = "dry-run", default_value = "false")]
125    pub dry_run: bool,
126
127    /// Open the generated message in $EDITOR before committing
128    #[arg(short = 'e', long = "edit", default_value = "false")]
129    pub edit: bool,
130
131    /// Use a specific skill for commit generation
132    #[arg(long = "skill")]
133    pub skill: Option<String>,
134}
135
136#[derive(Parser)]
137pub struct SetupCommand {
138    /// Skip interactive prompts and use defaults
139    #[arg(long, default_value = "false")]
140    pub defaults: bool,
141
142    /// Run advanced setup with all configuration options
143    #[arg(long, default_value = "false")]
144    pub advanced: bool,
145}
146
147#[derive(Subcommand)]
148pub enum Commands {
149    /// Manage Rusty Commit configuration
150    Config(ConfigCommand),
151
152    /// Setup git hooks
153    Hook(HookCommand),
154
155    /// Generate commitlint configuration
156    #[command(name = "commitlint")]
157    CommitLint(CommitLintCommand),
158
159    /// Authenticate with Claude using OAuth
160    Auth(AuthCommand),
161
162    /// Start MCP (Model Context Protocol) server
163    Mcp(McpCommand),
164
165    /// Check for updates and update rusty-commit
166    Update(UpdateCommand),
167
168    /// Generate PR description
169    Pr(PrCommand),
170
171    /// Interactive model selection
172    Model(ModelCommand),
173
174    /// Interactive setup wizard
175    Setup(SetupCommand),
176
177    /// Generate shell completions
178    Completions(CompletionsCommand),
179
180    /// Manage skills (custom templates, analyzers, formatters)
181    Skills(SkillsCommand),
182}
183
184#[derive(Parser)]
185pub struct PrCommand {
186    #[command(subcommand)]
187    pub action: PrAction,
188}
189
190#[derive(Subcommand)]
191pub enum PrAction {
192    /// Generate a PR description
193    Generate {
194        /// Base branch to compare against (default: main)
195        #[arg(short, long)]
196        base: Option<String>,
197    },
198    /// Open PR creation page in browser
199    Browse {
200        /// Base branch to compare against (default: main)
201        #[arg(short, long)]
202        base: Option<String>,
203    },
204}
205
206#[derive(Parser)]
207pub struct ConfigCommand {
208    #[command(subcommand)]
209    pub action: ConfigAction,
210}
211
212#[derive(Subcommand)]
213pub enum ConfigAction {
214    /// Set a configuration value
215    Set {
216        /// Configuration key=value pairs
217        #[arg(required = true)]
218        pairs: Vec<String>,
219    },
220    /// Get a configuration value
221    Get {
222        /// Configuration key
223        key: String,
224    },
225    /// Reset configuration to defaults
226    Reset {
227        /// Reset all configuration
228        #[arg(long)]
229        all: bool,
230        /// Specific keys to reset
231        keys: Vec<String>,
232    },
233    /// Show secure storage status
234    Status,
235    /// Describe all configuration options with examples and descriptions
236    Describe,
237    /// Add a new provider account
238    AddProvider {
239        /// Provider to add (openai, anthropic, claude-code, qwen, ollama, xai, gemini, perplexity, azure, mlx, nvidia, and many more)
240        #[arg(short, long)]
241        provider: Option<String>,
242        /// Account alias (e.g., "work", "personal")
243        #[arg(short, long)]
244        alias: Option<String>,
245    },
246    /// List all configured accounts
247    ListAccounts,
248    /// Switch to a different account
249    UseAccount {
250        /// Account alias to use
251        alias: String,
252    },
253    /// Remove an account
254    RemoveAccount {
255        /// Account alias to remove
256        alias: String,
257    },
258    /// Show account details
259    ShowAccount {
260        /// Account alias (defaults to "default")
261        alias: Option<String>,
262    },
263}
264
265#[derive(Parser)]
266pub struct HookCommand {
267    #[command(subcommand)]
268    pub action: HookAction,
269}
270
271#[derive(Subcommand)]
272pub enum HookAction {
273    /// Install prepare-commit-msg git hook
274    PrepareCommitMsg,
275    /// Install commit-msg git hook (non-interactive)
276    CommitMsg,
277    /// Uninstall git hooks
278    Unset,
279    /// Install or uninstall pre-commit hooks
280    Precommit {
281        /// Install pre-commit hooks
282        #[arg(long)]
283        set: bool,
284        /// Uninstall pre-commit hooks
285        #[arg(long)]
286        unset: bool,
287    },
288}
289
290#[derive(Parser)]
291pub struct CommitLintCommand {
292    /// Set configuration non-interactively
293    #[arg(long)]
294    pub set: bool,
295}
296
297#[derive(Parser)]
298pub struct AuthCommand {
299    #[command(subcommand)]
300    pub action: AuthAction,
301}
302
303#[derive(Subcommand)]
304pub enum AuthAction {
305    /// Login with Claude OAuth
306    Login,
307    /// Logout and remove stored tokens
308    Logout,
309    /// Check authentication status
310    Status,
311}
312
313#[derive(Parser)]
314pub struct McpCommand {
315    #[command(subcommand)]
316    pub action: McpAction,
317}
318
319#[derive(Subcommand)]
320pub enum McpAction {
321    /// Start MCP server on TCP port (for Cursor integration)
322    Server {
323        /// Port to listen on
324        #[arg(short, long, default_value = "3000")]
325        port: Option<u16>,
326    },
327    /// Start MCP server over STDIO (for direct integration)
328    Stdio,
329}
330
331#[derive(Parser)]
332pub struct UpdateCommand {
333    /// Check for updates without installing
334    #[arg(short, long)]
335    pub check: bool,
336
337    /// Force update even if already on latest version
338    #[arg(short, long)]
339    pub force: bool,
340
341    /// Specify version to update to (e.g., "1.0.2")
342    #[arg(short, long)]
343    pub version: Option<String>,
344}
345
346#[derive(Parser)]
347pub struct ModelCommand {
348    /// List available models for current provider
349    #[arg(long = "list")]
350    pub list: bool,
351    /// Specify provider to list models for
352    #[arg(short, long)]
353    pub provider: Option<String>,
354}
355
356#[derive(Parser)]
357pub struct CompletionsCommand {
358    /// Shell to generate completions for
359    #[arg(value_enum)]
360    pub shell: clap_complete::Shell,
361}
362
363#[derive(Parser)]
364pub struct SkillsCommand {
365    #[command(subcommand)]
366    pub action: SkillsAction,
367}
368
369#[derive(Subcommand)]
370pub enum SkillsAction {
371    /// List all available skills
372    List {
373        /// Filter by category
374        #[arg(short, long)]
375        category: Option<String>,
376    },
377    /// Create a new skill from template
378    Create {
379        /// Skill name
380        name: String,
381        /// Skill category
382        #[arg(short, long, default_value = "template")]
383        category: String,
384        /// Create as a project-level skill (in .rco/skills/)
385        #[arg(short, long, default_value = "false")]
386        project: bool,
387    },
388    /// Show skill details
389    Show {
390        /// Skill name
391        name: String,
392    },
393    /// Remove a skill
394    Remove {
395        /// Skill name
396        name: String,
397        /// Force removal without confirmation
398        #[arg(short, long)]
399        force: bool,
400    },
401    /// Open skill directory in default editor/file manager
402    Open,
403    /// Import skills from external sources (claude-code, github, gist, url)
404    Import {
405        /// Source to import from (claude-code, github:owner/repo, gist:id, or URL)
406        source: String,
407        /// Specific skill name to import (optional, imports all if not specified)
408        #[arg(short, long)]
409        name: Option<String>,
410    },
411    /// List available external skills without importing
412    Available {
413        /// External source to check (claude-code)
414        #[arg(default_value = "claude-code")]
415        source: String,
416    },
417}