Skip to main content

git_hunk/
cli.rs

1use std::path::PathBuf;
2
3use clap::{Args, Parser, Subcommand, ValueEnum};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Parser)]
7#[command(name = "git-hunk")]
8#[command(about = "Non-interactive hunk staging for AI agents")]
9pub struct Cli {
10    #[command(subcommand)]
11    pub command: Command,
12}
13
14impl Cli {
15    pub fn json(&self) -> bool {
16        match &self.command {
17            Command::Scan(args) => args.json,
18            Command::Show(args) => args.json,
19            Command::Resolve(args) => args.json,
20            Command::Stage(args) => args.json,
21            Command::Unstage(args) => args.json,
22            Command::Commit(args) => args.json,
23        }
24    }
25}
26
27#[derive(Debug, Subcommand)]
28pub enum Command {
29    Scan(ScanArgs),
30    Show(ShowArgs),
31    Resolve(ResolveArgs),
32    Stage(MutateArgs),
33    Unstage(MutateArgs),
34    Commit(CommitArgs),
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ValueEnum)]
38#[serde(rename_all = "snake_case")]
39pub enum Mode {
40    Stage,
41    Unstage,
42}
43
44impl Mode {
45    pub fn as_str(self) -> &'static str {
46        match self {
47            Mode::Stage => "stage",
48            Mode::Unstage => "unstage",
49        }
50    }
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ValueEnum)]
54#[serde(rename_all = "snake_case")]
55pub enum ResolveSide {
56    Auto,
57    Old,
58    New,
59}
60
61impl ResolveSide {
62    pub fn as_str(self) -> &'static str {
63        match self {
64            ResolveSide::Auto => "auto",
65            ResolveSide::Old => "old",
66            ResolveSide::New => "new",
67        }
68    }
69}
70
71#[derive(Debug, Args)]
72pub struct ScanArgs {
73    #[arg(long, value_enum)]
74    pub mode: Mode,
75    #[arg(long)]
76    pub compact: bool,
77    #[arg(long)]
78    pub json: bool,
79}
80
81#[derive(Debug, Args)]
82pub struct ShowArgs {
83    #[arg(long, value_enum)]
84    pub mode: Mode,
85    pub id: String,
86    #[arg(long)]
87    pub json: bool,
88}
89
90#[derive(Debug, Args)]
91pub struct ResolveArgs {
92    #[arg(long, value_enum)]
93    pub mode: Mode,
94    #[arg(long)]
95    pub snapshot: String,
96    #[arg(long)]
97    pub path: String,
98    #[arg(long)]
99    pub start: u32,
100    #[arg(long)]
101    pub end: Option<u32>,
102    #[arg(long, value_enum, default_value = "auto")]
103    pub side: ResolveSide,
104    #[arg(long)]
105    pub json: bool,
106}
107
108#[derive(Debug, Args)]
109pub struct MutateArgs {
110    #[arg(long)]
111    pub snapshot: Option<String>,
112    #[arg(long)]
113    pub plan: Option<PathBuf>,
114    #[arg(long = "hunk")]
115    pub hunks: Vec<String>,
116    #[arg(long = "change")]
117    pub changes: Vec<String>,
118    #[arg(long = "change-key")]
119    pub change_keys: Vec<String>,
120    #[arg(long)]
121    pub compact: bool,
122    #[arg(long)]
123    pub json: bool,
124}
125
126#[derive(Debug, Args)]
127pub struct CommitArgs {
128    #[arg(short = 'm', long = "message", required = true)]
129    pub messages: Vec<String>,
130    #[arg(long)]
131    pub snapshot: Option<String>,
132    #[arg(long)]
133    pub plan: Option<PathBuf>,
134    #[arg(long = "hunk")]
135    pub hunks: Vec<String>,
136    #[arg(long = "change")]
137    pub changes: Vec<String>,
138    #[arg(long = "change-key")]
139    pub change_keys: Vec<String>,
140    #[arg(long)]
141    pub allow_empty: bool,
142    #[arg(long)]
143    pub dry_run: bool,
144    #[arg(long)]
145    pub compact: bool,
146    #[arg(long)]
147    pub json: bool,
148}