1use clap::{Parser, ValueEnum};
2use std::path::PathBuf;
3
4const HELP_TEMPLATE: &str = "\
5{bin} - {about}
6
7Usage: {usage}
8
9Arguments:
10 <baseline> Path to the baseline (expected) image
11 [candidate] Path to the candidate (actual) image (required for diff)
12
13Output:
14 -f, --format <format> Output format: json, summary, image [default: json]
15 -o, --output <path> Write visual diff image (works alongside any format)
16 -v, --verbose Include all diagnostic fields in JSON
17 --pretty Pretty-print JSON output
18 -q, --quiet Suppress stdout; check JSON output for results
19
20Tuning:
21 -t, --threshold <float> Color difference sensitivity, 0.0-1.0 [default: 0.1]
22 --denoise <n> Remove noise clusters smaller than N pixels [default: 25]
23 --dilate <n> Expand diff mask by N pixels [default: 0]
24 --merge-distance <n> Merge regions within N pixels of each other [default: 50]
25 --min-region-size <n> Filter out regions smaller than N pixels [default: 25]
26 --connectivity <n> Pixel connectivity: 4 (cross) or 8 (diagonals) [default: 8]
27 --detect-antialias Ignore anti-aliased edge pixels [default: true]
28
29Crop:
30 --crop Crop a region from the baseline image instead of diffing
31 --x <n> X coordinate of crop region (screenshot pixels)
32 --y <n> Y coordinate of crop region (screenshot pixels)
33 --crop-width <n> Width of crop region (screenshot pixels)
34 --crop-height <n> Height of crop region (screenshot pixels)
35
36Options:
37 -h, --help Print help
38 -V, --version Print version
39
40Examples:
41 {bin} baseline.png candidate.png
42 {bin} baseline.png candidate.png -v --pretty
43 {bin} baseline.png candidate.png -o diff.png
44 {bin} baseline.png candidate.png -q -o diff.png
45 {bin} screenshot.png --crop --x 100 --y 200 --crop-width 400 --crop-height 300 -o cropped.png";
46
47#[derive(Parser)]
48#[command(
49 name = "agent-image-diff",
50 version,
51 about = "structured image diff with JSON output for agent workflows",
52 help_template = HELP_TEMPLATE,
53)]
54pub struct Cli {
55 pub baseline: PathBuf,
57
58 pub candidate: Option<PathBuf>,
60
61 #[arg(long)]
63 pub crop: bool,
64
65 #[arg(long, requires = "crop")]
67 pub x: Option<u32>,
68
69 #[arg(long, requires = "crop")]
71 pub y: Option<u32>,
72
73 #[arg(long, requires = "crop")]
75 pub crop_width: Option<u32>,
76
77 #[arg(long, requires = "crop")]
79 pub crop_height: Option<u32>,
80
81 #[arg(short = 'f', long, default_value = "json", value_enum, hide = true)]
83 pub format: OutputFormat,
84
85 #[arg(short = 'o', long = "output", hide = true)]
87 pub output: Option<PathBuf>,
88
89 #[arg(short = 't', long, default_value = "0.1", hide = true)]
91 pub threshold: f64,
92
93 #[arg(long, default_value = "true", hide = true)]
95 pub detect_antialias: bool,
96
97 #[arg(long, default_value = "25", hide = true)]
99 pub min_region_size: u32,
100
101 #[arg(long, default_value = "8", hide = true)]
103 pub connectivity: u8,
104
105 #[arg(long, default_value = "25", hide = true)]
107 pub denoise: u32,
108
109 #[arg(long, default_value = "0", hide = true)]
111 pub dilate: u32,
112
113 #[arg(long, default_value = "50", hide = true)]
115 pub merge_distance: u32,
116
117 #[arg(short = 'v', long, hide = true)]
119 pub verbose: bool,
120
121 #[arg(long, hide = true)]
123 pub pretty: bool,
124
125 #[arg(short = 'q', long, hide = true)]
127 pub quiet: bool,
128}
129
130#[derive(ValueEnum, Clone)]
131pub enum OutputFormat {
132 Json,
133 Image,
134 Summary,
135}