Skip to main content

omni_dev/cli/
commands.rs

1//! Command template management.
2
3use std::fs;
4use std::path::Path;
5
6use anyhow::{Context, Result};
7use clap::{Parser, Subcommand};
8
9// Embed the template files as strings
10const COMMIT_TWIDDLE_TEMPLATE: &str = include_str!("../templates/commit-twiddle.md");
11const PR_CREATE_TEMPLATE: &str = include_str!("../templates/pr-create.md");
12const PR_UPDATE_TEMPLATE: &str = include_str!("../templates/pr-update.md");
13
14/// Command template management.
15#[derive(Parser)]
16pub struct CommandsCommand {
17    /// Commands subcommand to execute.
18    #[command(subcommand)]
19    pub command: CommandsSubcommands,
20}
21
22/// Commands subcommands.
23#[derive(Subcommand)]
24pub enum CommandsSubcommands {
25    /// Generates command templates.
26    Generate(GenerateCommand),
27}
28
29/// Generate command options.
30#[derive(Parser)]
31pub struct GenerateCommand {
32    /// Generate subcommand to execute.
33    #[command(subcommand)]
34    pub command: GenerateSubcommands,
35}
36
37/// Generate subcommands.
38#[derive(Subcommand)]
39pub enum GenerateSubcommands {
40    /// Generates commit-twiddle command template.
41    #[command(name = "commit-twiddle")]
42    CommitTwiddle,
43    /// Generates pr-create command template.
44    #[command(name = "pr-create")]
45    PrCreate,
46    /// Generates pr-update command template.
47    #[command(name = "pr-update")]
48    PrUpdate,
49    /// Generates all command templates.
50    All,
51}
52
53impl CommandsCommand {
54    /// Executes the commands command.
55    pub fn execute(self) -> Result<()> {
56        match self.command {
57            CommandsSubcommands::Generate(generate_cmd) => generate_cmd.execute(),
58        }
59    }
60}
61
62impl GenerateCommand {
63    /// Executes the generate command.
64    pub fn execute(self) -> Result<()> {
65        match self.command {
66            GenerateSubcommands::CommitTwiddle => {
67                generate_commit_twiddle()?;
68                println!("✅ Generated .claude/commands/commit-twiddle.md");
69            }
70            GenerateSubcommands::PrCreate => {
71                generate_pr_create()?;
72                println!("✅ Generated .claude/commands/pr-create.md");
73            }
74            GenerateSubcommands::PrUpdate => {
75                generate_pr_update()?;
76                println!("✅ Generated .claude/commands/pr-update.md");
77            }
78            GenerateSubcommands::All => {
79                generate_commit_twiddle()?;
80                generate_pr_create()?;
81                generate_pr_update()?;
82                println!("✅ Generated all command templates:");
83                println!("   - .claude/commands/commit-twiddle.md");
84                println!("   - .claude/commands/pr-create.md");
85                println!("   - .claude/commands/pr-update.md");
86            }
87        }
88        Ok(())
89    }
90}
91
92/// Generates the commit-twiddle command template.
93fn generate_commit_twiddle() -> Result<()> {
94    ensure_claude_commands_dir()?;
95    fs::write(
96        ".claude/commands/commit-twiddle.md",
97        COMMIT_TWIDDLE_TEMPLATE,
98    )
99    .context("Failed to write .claude/commands/commit-twiddle.md")?;
100    Ok(())
101}
102
103/// Generates the pr-create command template.
104fn generate_pr_create() -> Result<()> {
105    ensure_claude_commands_dir()?;
106    fs::write(".claude/commands/pr-create.md", PR_CREATE_TEMPLATE)
107        .context("Failed to write .claude/commands/pr-create.md")?;
108    Ok(())
109}
110
111/// Generates the pr-update command template.
112fn generate_pr_update() -> Result<()> {
113    ensure_claude_commands_dir()?;
114    fs::write(".claude/commands/pr-update.md", PR_UPDATE_TEMPLATE)
115        .context("Failed to write .claude/commands/pr-update.md")?;
116    Ok(())
117}
118
119/// Ensures the .claude/commands directory exists.
120fn ensure_claude_commands_dir() -> Result<()> {
121    let commands_dir = Path::new(".claude/commands");
122    if !commands_dir.exists() {
123        fs::create_dir_all(commands_dir).context("Failed to create .claude/commands directory")?;
124    }
125    Ok(())
126}