use clap::{Parser, ValueEnum};
use std::path::PathBuf;
const HELP_TEMPLATE: &str = "\
{bin} - {about}
Usage: {usage}
Arguments:
<baseline> Path to the baseline (expected) image
[candidate] Path to the candidate (actual) image (required for diff)
Output:
-f, --format <format> Output format: json, summary, image [default: json]
-o, --output <path> Write visual diff image (works alongside any format)
-v, --verbose Include all diagnostic fields in JSON
--pretty Pretty-print JSON output
-q, --quiet Suppress stdout; check JSON output for results
Tuning:
-t, --threshold <float> Color difference sensitivity, 0.0-1.0 [default: 0.1]
--denoise <n> Remove noise clusters smaller than N pixels [default: 25]
--dilate <n> Expand diff mask by N pixels [default: 0]
--merge-distance <n> Merge regions within N pixels of each other [default: 50]
--min-region-size <n> Filter out regions smaller than N pixels [default: 25]
--connectivity <n> Pixel connectivity: 4 (cross) or 8 (diagonals) [default: 8]
--detect-antialias Ignore anti-aliased edge pixels [default: true]
Crop:
--crop Crop a region from the baseline image instead of diffing
--x <n> X coordinate of crop region (screenshot pixels)
--y <n> Y coordinate of crop region (screenshot pixels)
--crop-width <n> Width of crop region (screenshot pixels)
--crop-height <n> Height of crop region (screenshot pixels)
Options:
-h, --help Print help
-V, --version Print version
Examples:
{bin} baseline.png candidate.png
{bin} baseline.png candidate.png -v --pretty
{bin} baseline.png candidate.png -o diff.png
{bin} baseline.png candidate.png -q -o diff.png
{bin} screenshot.png --crop --x 100 --y 200 --crop-width 400 --crop-height 300 -o cropped.png";
#[derive(Parser)]
#[command(
name = "agent-image-diff",
version,
about = "structured image diff with JSON output for agent workflows",
help_template = HELP_TEMPLATE,
)]
pub struct Cli {
pub baseline: PathBuf,
pub candidate: Option<PathBuf>,
#[arg(long)]
pub crop: bool,
#[arg(long, requires = "crop")]
pub x: Option<u32>,
#[arg(long, requires = "crop")]
pub y: Option<u32>,
#[arg(long, requires = "crop")]
pub crop_width: Option<u32>,
#[arg(long, requires = "crop")]
pub crop_height: Option<u32>,
#[arg(short = 'f', long, default_value = "json", value_enum, hide = true)]
pub format: OutputFormat,
#[arg(short = 'o', long = "output", hide = true)]
pub output: Option<PathBuf>,
#[arg(short = 't', long, default_value = "0.1", hide = true)]
pub threshold: f64,
#[arg(long, default_value = "true", hide = true)]
pub detect_antialias: bool,
#[arg(long, default_value = "25", hide = true)]
pub min_region_size: u32,
#[arg(long, default_value = "8", hide = true)]
pub connectivity: u8,
#[arg(long, default_value = "25", hide = true)]
pub denoise: u32,
#[arg(long, default_value = "0", hide = true)]
pub dilate: u32,
#[arg(long, default_value = "50", hide = true)]
pub merge_distance: u32,
#[arg(short = 'v', long, hide = true)]
pub verbose: bool,
#[arg(long, hide = true)]
pub pretty: bool,
#[arg(short = 'q', long, hide = true)]
pub quiet: bool,
}
#[derive(ValueEnum, Clone)]
pub enum OutputFormat {
Json,
Image,
Summary,
}