#![cfg_attr(coverage_nightly, coverage(off))]
use clap::Parser;
use super::commands_enum::Commands;
#[derive(Parser)]
#[command(
name = "pmat",
about = "PMAT - Professional Multi-language Analysis Toolkit for code quality, complexity, and technical debt",
version,
// `--version` reports the revision the binary was actually built from.
//
// A version number repeats across builds, so it cannot distinguish a fresh
// binary from a stale one. v3.28.2 shipped a headline fix that did not work
// because three measurements were taken against binaries that did not match
// the tree under test. This makes that mismatch checkable.
//
// The plain version stays the first token, so existing assertions of the form
// "`--version` output contains <the version number>" keep working, and `-V`
// remains a single line for packagers and scripts that parse it.
long_version = concat!(
env!("CARGO_PKG_VERSION"),
"\ncommit: ",
env!("PMAT_GIT_SHA"),
"\nworktree: ",
env!("PMAT_GIT_DIRTY"),
),
long_about = None,
after_help = "EXAMPLES:
# Analyze code complexity
pmat analyze complexity --path .
# Calculate Technical Debt Grade (TDG)
pmat tdg .
# Find self-admitted technical debt markers
pmat analyze satd --path .
# Find dead code
pmat analyze dead-code --path .
# Generate AI-ready project context
pmat context --format llm-optimized
# Run quality gates
pmat quality-gate
# Calculate repository health score
pmat repo-score
# Start background agent daemon
pmat agent start"
)]
#[cfg_attr(test, derive(Debug))]
pub struct Cli {
#[arg(long, value_enum, global = true)]
pub mode: Option<Mode>,
#[arg(short, long, global = true)]
pub verbose: bool,
#[arg(short, long, global = true, conflicts_with = "verbose")]
pub quiet: bool,
#[arg(long, global = true)]
pub debug: bool,
#[arg(long, global = true)]
pub trace: bool,
#[arg(long, global = true, env = "RUST_LOG")]
pub trace_filter: Option<String>,
#[arg(long, global = true, value_enum, default_value = "auto")]
pub color: ColorMode,
#[command(subcommand)]
pub command: Commands,
}
#[derive(Clone, Debug, clap::ValueEnum, PartialEq)]
pub enum Mode {
Cli,
Mcp,
}
#[derive(Clone, Debug, clap::ValueEnum, PartialEq, Default)]
pub enum ColorMode {
#[default]
Auto,
Always,
Never,
}
#[cfg(test)]
mod help_text_guards {
fn on_a_big_stack<F: FnOnce() + Send + 'static>(body: F) {
std::thread::Builder::new()
.stack_size(8 * 1024 * 1024)
.spawn(body)
.expect("spawn")
.join()
.expect("the clap tree must build");
}
#[test]
fn high_risk_only_help_names_the_threshold_the_code_uses() {
on_a_big_stack(|| {
use crate::services::facades::defect_prediction_facade::HIGH_RISK_PROBABILITY;
use clap::CommandFactory;
let cmd = crate::cli::Cli::command();
let analyze = cmd.find_subcommand("analyze").expect("`analyze`");
let defect_prediction = analyze
.find_subcommand("defect-prediction")
.expect("`analyze defect-prediction`");
let arg = defect_prediction
.get_arguments()
.find(|a| a.get_id() == "high_risk_only")
.expect("--high-risk-only");
let help = arg
.get_help()
.expect("--high-risk-only must be documented")
.to_string();
assert!(
help.contains(&HIGH_RISK_PROBABILITY.to_string()),
"help says {help:?} but the band starts at {HIGH_RISK_PROBABILITY}"
);
assert!(
!help.contains("0.7"),
"0.7 is not a boundary of any band this flag selects: {help:?}"
);
});
}
#[test]
fn the_mode_flag_clap_accepts_is_the_one_argv_scanning_recognises() {
on_a_big_stack(|| {
use crate::cli::commands::Mode;
use crate::cli::forced_mode_from_args;
use clap::Parser;
for (args, expected) in [
(vec!["pmat", "--mode", "mcp", "list"], Some(Mode::Mcp)),
(vec!["pmat", "--mode=cli", "list"], Some(Mode::Cli)),
(vec!["pmat", "list"], None),
] {
let parsed = crate::cli::Cli::try_parse_from(&args).expect("clap accepts these");
let owned: Vec<String> = args.iter().map(|s| (*s).to_string()).collect();
assert_eq!(
parsed.mode, expected,
"clap and forced_mode_from_args must agree on {args:?}"
);
assert_eq!(forced_mode_from_args(&owned), expected, "{args:?}");
}
});
}
}