#![deny(missing_docs)]
use clap::{Args, CommandFactory, Parser, Subcommand};
use resq_cli::commands;
use std::io::IsTerminal;
use tracing_subscriber::EnvFilter;
const LONG_ABOUT: &str = "\
ResQ developer CLI — audits, formatting, git hooks, and six TUI explorers.
Common commands:
resq scan audit Run cargo/bun/uv audit across the workspace
resq scan secrets Scan the repo for leaked secrets and credentials
resq format Format Rust / TS / Python / C++ / C# in one pass
resq pre-commit Run the full pre-commit gate (copyright, secrets, audit, format)
resq hooks Inspect and maintain installed git hooks
resq tui <screen> Launch a TUI explorer (explore, logs, health, deploy, clean, asm)
The old flat forms (`resq audit`, `resq explore`, …) still work as hidden aliases
for one release cycle. Run `resq <command> --help` for per-command options, or
`resq completions <shell>` to install shell tab-completion.";
#[derive(Parser)]
#[command(name = "resq")]
#[command(version, about = "ResQ CLI tools", long_about = LONG_ABOUT)]
pub struct Cli {
#[arg(long, action = clap::ArgAction::Count, global = true)]
verbose: u8,
#[arg(short, long, global = true)]
quiet: bool,
#[arg(long, global = true)]
no_color: bool,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Scan(ScanArgs),
Tui(TuiArgs),
Format(commands::format::FormatArgs),
Dev(commands::dev::DevArgs),
PreCommit(commands::pre_commit::PreCommitArgs),
Version(commands::version::VersionArgs),
Docs(commands::docs::DocsArgs),
Hooks(commands::hooks::HooksArgs),
Commit(commands::commit::CommitArgs),
Completions(commands::completions::CompletionsArgs),
#[command(hide = true)]
Copyright(commands::copyright::CopyrightArgs),
#[command(hide = true)]
Audit(commands::audit::AuditArgs),
#[command(hide = true)]
Secrets(commands::secrets::SecretsArgs),
#[command(hide = true)]
Explore(commands::explore::ExploreArgs),
#[command(hide = true)]
Logs(commands::explore::LogsArgs),
#[command(hide = true)]
Health(commands::explore::HealthArgs),
#[command(hide = true)]
Deploy(commands::explore::DeployArgs),
#[command(hide = true)]
Clean(commands::explore::CleanArgs),
#[command(hide = true)]
Asm(commands::explore::AsmArgs),
}
#[derive(Args, Debug)]
struct ScanArgs {
#[command(subcommand)]
kind: ScanKind,
}
#[derive(Subcommand, Debug)]
enum ScanKind {
Audit(commands::audit::AuditArgs),
Secrets(commands::secrets::SecretsArgs),
Copyright(commands::copyright::CopyrightArgs),
}
#[derive(Args, Debug)]
struct TuiArgs {
#[command(subcommand)]
screen: TuiScreen,
}
#[derive(Subcommand, Debug)]
enum TuiScreen {
Explore(commands::explore::ExploreArgs),
Logs(commands::explore::LogsArgs),
Health(commands::explore::HealthArgs),
Deploy(commands::explore::DeployArgs),
Clean(commands::explore::CleanArgs),
Asm(commands::explore::AsmArgs),
}
fn warn_deprecated(old: &str, new: &str) {
tracing::warn!("`resq {old}` is deprecated; use `resq {new}` instead.");
}
fn init_tracing(verbose: u8, quiet: bool, no_color: bool) {
let level = if quiet {
"error"
} else {
match verbose {
0 => "warn",
1 => "info",
2 => "debug",
_ => "trace",
}
};
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(level));
let use_ansi = !no_color && std::io::stderr().is_terminal();
tracing_subscriber::fmt()
.with_writer(std::io::stderr)
.with_env_filter(filter)
.with_ansi(use_ansi)
.with_target(false)
.init();
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
init_tracing(cli.verbose, cli.quiet, cli.no_color);
match cli.command {
Commands::Scan(ScanArgs { kind }) => match kind {
ScanKind::Audit(args) => commands::audit::run(args).await?,
ScanKind::Secrets(args) => commands::secrets::run(args).await?,
ScanKind::Copyright(args) => commands::copyright::run(&args)?,
},
Commands::Tui(TuiArgs { screen }) => match screen {
TuiScreen::Explore(args) => commands::explore::run_explore(args).await?,
TuiScreen::Logs(args) => commands::explore::run_logs(args).await?,
TuiScreen::Health(args) => commands::explore::run_health(args).await?,
TuiScreen::Deploy(args) => commands::explore::run_deploy(args).await?,
TuiScreen::Clean(args) => commands::explore::run_clean(args).await?,
TuiScreen::Asm(args) => commands::explore::run_asm(args).await?,
},
Commands::Format(args) => commands::format::run(args).await?,
Commands::Dev(args) => commands::dev::run(args)?,
Commands::PreCommit(args) => commands::pre_commit::run(args).await?,
Commands::Version(args) => commands::version::run(args)?,
Commands::Docs(args) => commands::docs::run(args)?,
Commands::Hooks(args) => commands::hooks::run(args)?,
Commands::Commit(args) => commands::commit::run(args).await?,
Commands::Completions(args) => commands::completions::run(args, Cli::command())?,
Commands::Copyright(args) => {
warn_deprecated("copyright", "scan copyright");
commands::copyright::run(&args)?;
}
Commands::Audit(args) => {
warn_deprecated("audit", "scan audit");
commands::audit::run(args).await?;
}
Commands::Secrets(args) => {
warn_deprecated("secrets", "scan secrets");
commands::secrets::run(args).await?;
}
Commands::Explore(args) => {
warn_deprecated("explore", "tui explore");
commands::explore::run_explore(args).await?;
}
Commands::Logs(args) => {
warn_deprecated("logs", "tui logs");
commands::explore::run_logs(args).await?;
}
Commands::Health(args) => {
warn_deprecated("health", "tui health");
commands::explore::run_health(args).await?;
}
Commands::Deploy(args) => {
warn_deprecated("deploy", "tui deploy");
commands::explore::run_deploy(args).await?;
}
Commands::Clean(args) => {
warn_deprecated("clean", "tui clean");
commands::explore::run_clean(args).await?;
}
Commands::Asm(args) => {
warn_deprecated("asm", "tui asm");
commands::explore::run_asm(args).await?;
}
}
Ok(())
}