Skip to main content

chronicle/cli/
mod.rs

1pub mod annotate;
2pub mod backfill;
3pub mod context;
4pub mod correct;
5pub mod deps;
6pub mod doctor;
7pub mod export;
8pub mod flag;
9pub mod history;
10pub mod import;
11pub mod init;
12pub mod read;
13pub mod reconfigure;
14pub mod setup;
15pub mod show;
16pub mod summary;
17pub mod sync;
18
19use clap::{Parser, Subcommand};
20
21#[derive(Parser)]
22#[command(
23    name = "git-chronicle",
24    version,
25    about = "AI-powered commit annotation"
26)]
27pub struct Cli {
28    #[command(subcommand)]
29    pub command: Commands,
30}
31
32#[derive(Subcommand)]
33pub enum Commands {
34    /// One-time machine-wide setup (provider, skills, hooks, CLAUDE.md)
35    Setup {
36        /// Overwrite existing files without prompting
37        #[arg(long)]
38        force: bool,
39
40        /// Print what would be done without writing
41        #[arg(long)]
42        dry_run: bool,
43
44        /// Skip installing Claude Code skills
45        #[arg(long)]
46        skip_skills: bool,
47
48        /// Skip installing Claude Code hooks
49        #[arg(long)]
50        skip_hooks: bool,
51
52        /// Skip modifying ~/.claude/CLAUDE.md
53        #[arg(long)]
54        skip_claude_md: bool,
55    },
56
57    /// Rerun the LLM provider selection prompt
58    Reconfigure,
59
60    /// Annotate historical commits that lack Chronicle annotations
61    Backfill {
62        /// Maximum number of commits to annotate
63        #[arg(long, default_value = "20")]
64        limit: usize,
65
66        /// List commits that would be annotated without calling the LLM
67        #[arg(long)]
68        dry_run: bool,
69    },
70
71    /// Initialize chronicle in the current repository
72    Init {
73        /// Disable notes sync (sync is enabled by default)
74        #[arg(long)]
75        no_sync: bool,
76
77        /// Skip hook installation
78        #[arg(long)]
79        no_hooks: bool,
80
81        /// LLM provider to use
82        #[arg(long)]
83        provider: Option<String>,
84
85        /// LLM model to use
86        #[arg(long)]
87        model: Option<String>,
88
89        /// Run backfill after init (annotate last 20 commits)
90        #[arg(long)]
91        backfill: bool,
92    },
93
94    /// Manage annotation context
95    Context {
96        #[command(subcommand)]
97        action: ContextAction,
98    },
99
100    /// Read annotations for a file
101    Read {
102        /// File path to read annotations for
103        path: String,
104
105        /// Filter by AST anchor name
106        #[arg(long)]
107        anchor: Option<String>,
108
109        /// Filter by line range (format: start:end)
110        #[arg(long)]
111        lines: Option<String>,
112    },
113
114    /// Annotate a specific commit
115    Annotate {
116        /// Commit to annotate (default: HEAD)
117        #[arg(long, default_value = "HEAD")]
118        commit: String,
119
120        /// Run synchronously
121        #[arg(long)]
122        sync: bool,
123
124        /// Read AnnotateInput JSON from stdin (live annotation path, zero LLM cost)
125        #[arg(long)]
126        live: bool,
127
128        /// Comma-separated source commit SHAs for squash synthesis (CI usage)
129        #[arg(long)]
130        squash_sources: Option<String>,
131
132        /// Old commit SHA to migrate annotation from (amend re-annotation)
133        #[arg(long)]
134        amend_source: Option<String>,
135    },
136
137    /// Flag a region annotation as potentially inaccurate
138    Flag {
139        /// File path relative to repository root
140        path: String,
141
142        /// Optional AST anchor name to scope the flag to a specific region
143        anchor: Option<String>,
144
145        /// Reason for flagging this annotation
146        #[arg(long)]
147        reason: String,
148    },
149
150    /// Apply a precise correction to a specific annotation field
151    Correct {
152        /// Commit SHA of the annotation to correct
153        sha: String,
154
155        /// AST anchor name of the region within the annotation
156        #[arg(long)]
157        region: String,
158
159        /// Annotation field to correct (intent, reasoning, constraints, risk_notes, semantic_dependencies, tags)
160        #[arg(long)]
161        field: String,
162
163        /// Specific value to remove or mark as incorrect
164        #[arg(long)]
165        remove: Option<String>,
166
167        /// Replacement or amendment text
168        #[arg(long)]
169        amend: Option<String>,
170    },
171
172    /// Manage notes sync with remotes
173    Sync {
174        #[command(subcommand)]
175        action: SyncAction,
176    },
177
178    /// Export annotations as JSONL
179    Export {
180        /// Write to file instead of stdout
181        #[arg(short, long)]
182        output: Option<String>,
183    },
184
185    /// Import annotations from a JSONL file
186    Import {
187        /// JSONL file to import
188        file: String,
189
190        /// Overwrite existing annotations
191        #[arg(long)]
192        force: bool,
193
194        /// Show what would be imported without writing
195        #[arg(long)]
196        dry_run: bool,
197    },
198
199    /// Run diagnostic checks on the chronicle setup
200    Doctor {
201        /// Output as JSON
202        #[arg(long)]
203        json: bool,
204    },
205
206    /// Find code that depends on a given file/anchor (dependency inversion)
207    Deps {
208        /// File path to query
209        path: String,
210
211        /// AST anchor name to query
212        anchor: Option<String>,
213
214        /// Output format (json or pretty)
215        #[arg(long, default_value = "json")]
216        format: String,
217
218        /// Maximum number of results to return
219        #[arg(long, default_value = "50")]
220        max_results: u32,
221
222        /// Maximum number of commits to scan
223        #[arg(long, default_value = "500")]
224        scan_limit: u32,
225    },
226
227    /// Show annotation timeline for a file/anchor across commits
228    History {
229        /// File path to query
230        path: String,
231
232        /// AST anchor name to query
233        anchor: Option<String>,
234
235        /// Maximum number of timeline entries
236        #[arg(long, default_value = "10")]
237        limit: u32,
238
239        /// Output format (json or pretty)
240        #[arg(long, default_value = "json")]
241        format: String,
242
243        /// Follow related annotation links
244        #[arg(long, default_value = "true")]
245        follow_related: bool,
246    },
247
248    /// Interactive TUI explorer for annotated source code
249    Show {
250        /// File path to show
251        path: String,
252
253        /// Focus on a specific AST anchor
254        anchor: Option<String>,
255
256        /// Commit to show file at
257        #[arg(long, default_value = "HEAD")]
258        commit: String,
259
260        /// Force non-interactive plain-text output
261        #[arg(long)]
262        no_tui: bool,
263    },
264
265    /// Show condensed annotation summary for a file
266    Summary {
267        /// File path to query
268        path: String,
269
270        /// Filter to a specific AST anchor
271        #[arg(long)]
272        anchor: Option<String>,
273
274        /// Output format (json or pretty)
275        #[arg(long, default_value = "json")]
276        format: String,
277    },
278}
279
280#[derive(Subcommand)]
281pub enum SyncAction {
282    /// Enable notes sync for a remote
283    Enable {
284        /// Remote name (default: origin)
285        #[arg(long, default_value = "origin")]
286        remote: String,
287    },
288
289    /// Show sync status
290    Status {
291        /// Remote name (default: origin)
292        #[arg(long, default_value = "origin")]
293        remote: String,
294    },
295
296    /// Fetch and merge remote notes
297    Pull {
298        /// Remote name (default: origin)
299        #[arg(long, default_value = "origin")]
300        remote: String,
301    },
302}
303
304#[derive(Subcommand)]
305pub enum ContextAction {
306    /// Set pending context for the next commit
307    Set {
308        #[arg(long)]
309        task: Option<String>,
310
311        #[arg(long)]
312        reasoning: Option<String>,
313
314        #[arg(long)]
315        dependencies: Option<String>,
316
317        #[arg(long)]
318        tags: Vec<String>,
319    },
320
321    /// Show current pending context
322    Show,
323
324    /// Clear pending context
325    Clear,
326}