Skip to main content

gn_cli/
lib.rs

1use clap::{Parser, Subcommand};
2use tracing_subscriber::EnvFilter;
3
4pub mod commands;
5
6/// git-notes CLI — decentralized code comments, inline reviews, and discussions
7#[derive(Parser)]
8#[command(name = "git-notes", version, about, long_about = None)]
9pub struct Cli {
10    #[command(subcommand)]
11    pub command: Commands,
12}
13
14#[derive(Subcommand)]
15pub enum Commands {
16    /// Add a new note [alias: a]
17    #[command(alias = "a")]
18    Add(commands::add::AddArgs),
19
20    /// Reply to an existing note thread [alias: r]
21    #[command(alias = "r")]
22    Reply(commands::reply::ReplyArgs),
23
24    /// List notes [aliases: l, ls]
25    #[command(alias = "l", alias = "ls")]
26    List(commands::list::ListArgs),
27
28    /// Show a specific note or thread [alias: s]
29    #[command(alias = "s")]
30    Show(commands::show::ShowArgs),
31
32    /// Sync notes with remote or offline peers [aliases: push, pull, p2p]
33    #[command(alias = "push", alias = "pull", alias = "p2p")]
34    Sync(commands::sync::SyncArgs),
35
36    /// Resolve a note [aliases: ok, close]
37    #[command(alias = "ok", alias = "close")]
38    Resolve(commands::resolve::ResolveArgs),
39
40    /// Export notes [alias: exp]
41    #[command(alias = "exp")]
42    Export(commands::export::ExportArgs),
43
44    /// Check repository health and configuration [alias: doc]
45    #[command(alias = "doc")]
46    Doctor,
47
48    /// View git blame with inline notes [alias: b]
49    #[command(alias = "b")]
50    Blame(commands::blame::BlameArgs),
51
52    /// Import review comments from a GitHub Pull Request [alias: pr]
53    #[command(alias = "pr")]
54    ImportPr(commands::import_pr::ImportPrArgs),
55
56    /// Multi-provider importer for GitLab MRs, Bitbucket PRs, Jira issues, and GitHub [alias: imp]
57    #[command(alias = "imp")]
58    Import(commands::import::ImportArgs),
59
60    /// AI-powered summary of open discussion threads (Gemini) [alias: sum]
61    #[command(alias = "sum")]
62    Summarize(commands::summarize::SummarizeArgs),
63
64    /// Setup git hooks and remote tracking refspec in 1 second [alias: i]
65    #[command(alias = "i")]
66    Init,
67
68    /// Manage git auto-sync hooks
69    Hook(commands::hook::HookArgs),
70
71    /// View diff with inline notes attached to hunks [alias: d]
72    #[command(alias = "d")]
73    Diff(commands::diff::DiffArgs),
74
75    /// View and customize quickies, CLI abbreviations, and keyboard shortcuts [aliases: shortcut, keys, quickies]
76    #[command(alias = "shortcut", alias = "keys", alias = "quickies")]
77    Shortcuts(commands::shortcuts::ShortcutsArgs),
78
79    /// Generate shell completions
80    Completions(commands::completions::CompletionsArgs),
81
82    /// Automatically re-anchor notes after a git rebase, amend, or cherry-pick [alias: heal]
83    #[command(alias = "heal")]
84    RebaseHeal(commands::rebase_heal::RebaseHealArgs),
85
86    /// CI quality gating and merge-readiness check [alias: gate]
87    #[command(alias = "gate")]
88    Check(commands::check::CheckArgs),
89
90    /// Verify cryptographic signatures of notes [alias: sig]
91    #[command(alias = "sig")]
92    Verify(commands::verify::VerifyArgs),
93}
94
95pub fn run_cli() -> anyhow::Result<()> {
96    tracing_subscriber::fmt()
97        .with_env_filter(EnvFilter::from_default_env())
98        .init();
99
100    let mut profile = commands::learning::UserBehaviorProfile::load();
101    let cli = Cli::parse();
102
103    let result = match &cli.command {
104        Commands::Add(args) => {
105            profile.record_interaction("add", Some(&args.file), Some(&args.namespace));
106            commands::add::run(args)
107        }
108        Commands::Reply(args) => {
109            profile.record_interaction("reply", None, None);
110            commands::reply::run(args)
111        }
112        Commands::List(args) => {
113            profile.record_interaction("list", args.file.as_deref(), args.namespace.as_deref());
114            commands::list::run(args)
115        }
116        Commands::Show(args) => {
117            profile.record_interaction("show", None, None);
118            commands::show::run(args)
119        }
120        Commands::Sync(args) => {
121            profile.record_interaction("sync", None, None);
122            commands::sync::run(args)
123        }
124        Commands::Resolve(args) => {
125            profile.record_interaction("resolve", None, None);
126            commands::resolve::run(args)
127        }
128        Commands::Export(args) => {
129            profile.record_interaction("export", None, None);
130            commands::export::run(args)
131        }
132        Commands::Doctor => {
133            profile.record_interaction("doctor", None, None);
134            commands::doctor::run()
135        }
136        Commands::Blame(args) => {
137            profile.record_interaction("blame", Some(&args.file), Some(&args.namespace));
138            commands::blame::run(args)
139        }
140        Commands::ImportPr(args) => {
141            profile.record_interaction("import-pr", None, Some(&args.namespace));
142            commands::import_pr::run(args)
143        }
144        Commands::Import(args) => {
145            profile.record_interaction("import", None, None);
146            commands::import::run(args)
147        }
148        Commands::Summarize(args) => {
149            profile.record_interaction("summarize", None, Some(&args.namespace));
150            commands::summarize::run(args)
151        }
152        Commands::Init => {
153            profile.record_interaction("init", None, None);
154            commands::init::run()
155        }
156        Commands::Hook(args) => {
157            profile.record_interaction("hook", None, None);
158            commands::hook::run(args)
159        }
160        Commands::Diff(args) => {
161            profile.record_interaction("diff", None, Some(&args.namespace));
162            commands::diff::run(args)
163        }
164        Commands::Shortcuts(args) => {
165            profile.record_interaction("shortcuts", None, None);
166            commands::shortcuts::run(args)
167        }
168        Commands::RebaseHeal(args) => {
169            profile.record_interaction("rebase-heal", None, args.namespace.as_deref());
170            commands::rebase_heal::run(args)
171        }
172        Commands::Check(args) => {
173            profile.record_interaction("check", None, args.namespace.as_deref());
174            commands::check::run(args)
175        }
176        Commands::Verify(args) => {
177            profile.record_interaction("verify", None, args.namespace.as_deref());
178            commands::verify::run(args)
179        }
180        Commands::Completions(args) => commands::completions::run(args),
181    };
182
183    if let Some(tip) = profile.suggest_next_action(None) {
184        println!("{}", tip);
185    }
186
187    result
188}