drep/cli/check/args.rs
1//! The `drep check` command line.
2//!
3//! Its own file because it is the published surface rather than any of the
4//! orchestration: the flags, their groups and the prose a user reads in `--help`.
5//! The orchestrator in the parent reads them and never redefines what one means.
6
7use std::path::PathBuf;
8
9use clap::{ArgGroup, Args};
10
11use crate::analysis::findings::Severity;
12use crate::cli::{OutputFormat, severity_parser};
13
14#[derive(Debug, Args)]
15// One rule, stated once. Paired `conflicts_with_all` attributes say the same
16// thing from each side and have to be kept in agreement; a fourth input mode
17// would mean editing every existing one, and missing a single edit silently
18// permits an illegal combination.
19// Deliberately NOT `.required(true)`. Bare `drep check` is a supported
20// invocation meaning "the whole tree": `input::resolve` expands `root` through
21// `files::expand_paths`, exactly as an explicit `.` would. Requiring one of the
22// three would turn the plainest invocation into a usage error. Pinned by
23// `bare_check_with_no_paths_expands_the_root_instead_of_reading_a_directory`,
24// which exists because an earlier version passed the root through as a *file*
25// and exited 2 without analyzing anything.
26#[command(
27 group(
28 ArgGroup::new("input")
29 .args(["paths", "staged", "diff", "pre_commit_push"])
30 .multiple(false)
31 ),
32 group(ArgGroup::new("cache_mode").args(["cache_only", "push_gate"]).multiple(false))
33)]
34pub struct CheckArgs {
35 /// Files or directories to check. Duplicates and overlaps are collapsed,
36 /// so `drep check a.rs .` analyzes `a.rs` once.
37 #[arg(value_name = "PATH")]
38 pub paths: Vec<PathBuf>,
39
40 /// Check the files staged for commit. For a pre-commit hook.
41 #[arg(long)]
42 pub staged: bool,
43
44 /// Check the files changed since REF, e.g. `origin/main`. For pre-push.
45 #[arg(long, value_name = "REF")]
46 pub diff: Option<String>,
47
48 /// The commit to diff *to*. Defaults to `HEAD`. Only valid with `--diff`.
49 ///
50 /// A pre-push hook needs this: git can push a ref that is not the
51 /// checked-out one (`git push origin feature:feature` from another branch,
52 /// or `git push --all`), and diffing to `HEAD` there reviews a different
53 /// branch and lets the pushed code through unseen.
54 #[arg(long, value_name = "REF", requires = "diff")]
55 pub tip: Option<String>,
56
57 /// Read the pushed base and tip from pre-commit's hook environment.
58 ///
59 /// Used by the published `drep-check-push` hook. pre-commit otherwise
60 /// passes filenames, which would make drep review whole files instead of
61 /// the hunks between `PRE_COMMIT_FROM_REF` and `PRE_COMMIT_TO_REF`.
62 #[arg(long)]
63 pub pre_commit_push: bool,
64
65 #[arg(long, value_enum, default_value_t = OutputFormat::Text)]
66 pub format: OutputFormat,
67
68 /// Also block on LLM findings at or above this severity.
69 ///
70 /// Deterministic tool findings always block; this opts the LLM's findings
71 /// into gating too. Left unset, they inform without blocking - which is
72 /// the useful default, because the model emits style suggestions on
73 /// nearly every file.
74 #[arg(long, value_name = "SEVERITY", value_parser = severity_parser())]
75 pub fail_on: Option<Severity>,
76
77 /// Use cached LLM reviews only; never contact a provider.
78 ///
79 /// An uncached file exits 3 without warming it; run a normal check to
80 /// populate the missing entry. The generated pre-push hook uses
81 /// `--push-gate` for the full warm-and-reconnect handshake.
82 #[arg(long)]
83 pub cache_only: bool,
84
85 /// Prepare a push without resuming a stale remote connection.
86 ///
87 /// Cached reviews pass immediately. A cold review is completed and cached,
88 /// then exits 3 so Git reconnects; repeating `git push` uses the cache.
89 #[arg(long)]
90 pub push_gate: bool,
91
92 /// Override the repository's maximum fresh LLM review rounds.
93 #[arg(
94 long,
95 value_name = "N",
96 value_parser = clap::value_parser!(u32).range(1..),
97 conflicts_with = "unlimited_reviews"
98 )]
99 pub max_review_rounds: Option<u32>,
100
101 /// Permit fresh LLM review rounds without a limit for this invocation.
102 #[arg(long)]
103 pub unlimited_reviews: bool,
104}