@americano98/peye 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,426 @@
1
+ # peye
2
+
3
+ `peye` is a standalone CLI for visual diffing an implemented UI against a Figma reference or another screenshot. It is designed for agent-driven workflows and local terminal use: feed it a preview URL or image, feed it a reference URL or image, and it will produce deterministic artifacts plus a machine-readable JSON report.
4
+
5
+ The comparison core is intentionally separated from screenshot acquisition so the tool stays scriptable, predictable, and easy to embed into automation pipelines.
6
+
7
+ ## What It Does
8
+
9
+ - Compare `preview` from a local screenshot or live URL
10
+ - Compare `reference` from a local screenshot or Figma URL
11
+ - Capture only a target element when the preview URL contains `#fragment`
12
+ - Generate a compact LLM-friendly `report.json`, `overlay.png`, `diff.png`, `heatmap.png`, plus normalized input images
13
+ - Group mismatches by DOM element for URL previews and by visual cluster for local image previews
14
+ - Expose structured failure metadata, normalized image dimensions, and per-finding hotspots for agent triage
15
+ - Return a recommendation:
16
+ - `pass`
17
+ - `pass_with_tolerated_differences`
18
+ - `retry_fix`
19
+ - `needs_human_review`
20
+
21
+ ## Requirements
22
+
23
+ - Node.js `>= 22`
24
+ - A Chromium browser available to Playwright for URL capture
25
+ - One of these when `--reference` is a Figma URL:
26
+ - Figma desktop MCP running locally
27
+ - remote Figma MCP authorization in an interactive terminal
28
+ - `FIGMA_TOKEN` as a fallback for CI or REST-only workflows
29
+
30
+ When Figma MCP returns a screenshot that is smaller than the selected node's metadata size, `peye` automatically upscales the reference image back to the node dimensions before diffing. If you need a strict export raster from Figma, force REST with `PEYE_FIGMA_SOURCE=rest`.
31
+
32
+ If Playwright Chromium is missing, install it with:
33
+
34
+ ```bash
35
+ npx playwright install chromium
36
+ ```
37
+
38
+ ## Install
39
+
40
+ From the repository:
41
+
42
+ ```bash
43
+ pnpm install
44
+ pnpm build
45
+ node dist/bin.js --help
46
+ node dist/bin.js --version
47
+ ```
48
+
49
+ After publishing, install the CLI globally with:
50
+
51
+ ```bash
52
+ npm install -g @americano98/peye
53
+ peye --help
54
+ ```
55
+
56
+ `npm install` only installs the CLI. Agent integration files are kept in [`agents/`](./agents) and are not part of the published npm package.
57
+
58
+ ## Agent Integration
59
+
60
+ If your agent runtime supports reusable skill or instruction files, copy or vendor [`agents/SKILL.md`](./agents/SKILL.md) into that runtime's skill registry.
61
+
62
+ Recommended practice:
63
+
64
+ - Install `peye` separately so the `peye` executable is available in `PATH`.
65
+ - Treat [`agents/SKILL.md`](./agents/SKILL.md) as an optional integration asset, not part of the CLI install step.
66
+ - Keep the skill file versioned alongside the CLI, so updates to command flags and report shape stay in sync.
67
+ - Adapt the file to your agent runtime if it expects a different frontmatter format or install location.
68
+
69
+ [`agents/openai.yaml`](./agents/openai.yaml) is included as a small example of agent-facing metadata and prompt wiring.
70
+
71
+ ## CLI
72
+
73
+ ```bash
74
+ peye compare \
75
+ --preview <url|path> \
76
+ --reference <figma-url|path> \
77
+ --output <dir> \
78
+ [--viewport 1920|1920x900] \
79
+ [--mode all|pixel|layout|color] \
80
+ [--selector <css>] \
81
+ [--full-page] \
82
+ [--quiet] \
83
+ [--report-stdout] \
84
+ [--threshold-pass 0.5] \
85
+ [--threshold-tolerated 1.5] \
86
+ [--threshold-retry 5]
87
+ ```
88
+
89
+ ### Important Rules
90
+
91
+ - `--viewport` is required when `--preview` is a URL.
92
+ - `--viewport 1920` is valid and means `width=1920`, `height=900` by default.
93
+ - Use explicit `WIDTHxHEIGHT` only when exact viewport height matters for the comparison.
94
+ - If `--preview` is a local image and `--viewport` is omitted, viewport is inferred from the image dimensions.
95
+ - If `--preview` contains a hash, for example `https://example.com/#road-map`, `peye` automatically treats it as selector `#road-map` unless `--selector` is passed explicitly.
96
+ - `--full-page` is allowed only for URL preview capture without a selector.
97
+ - If `--reference` is a Figma URL, it must include `node-id`.
98
+ - `--quiet` suppresses the human-readable terminal summary.
99
+ - `--report-stdout` writes the compact JSON report to stdout and suppresses the human-readable summary.
100
+
101
+ ## Examples
102
+
103
+ Compare two local screenshots:
104
+
105
+ ```bash
106
+ peye compare \
107
+ --preview ./artifacts/preview.png \
108
+ --reference ./artifacts/reference.png \
109
+ --output ./peye-output
110
+ ```
111
+
112
+ Capture a live preview page at a fixed viewport:
113
+
114
+ ```bash
115
+ peye compare \
116
+ --preview http://localhost:3000 \
117
+ --reference ./figma-export/home.png \
118
+ --viewport 1920 \
119
+ --output ./peye-output
120
+ ```
121
+
122
+ Capture only a single element via URL hash:
123
+
124
+ ```bash
125
+ peye compare \
126
+ --preview https://example.com/#road-map \
127
+ --reference ./figma-export/road-map.png \
128
+ --viewport 1920 \
129
+ --output ./peye-output
130
+ ```
131
+
132
+ Compare against a Figma node:
133
+
134
+ ```bash
135
+ peye compare \
136
+ --preview http://localhost:3000/#hero \
137
+ --reference "https://www.figma.com/design/FILE_KEY/Mockup?node-id=1-2" \
138
+ --viewport 1920 \
139
+ --output ./peye-output
140
+ ```
141
+
142
+ By default this prefers Figma MCP. If MCP returns a downscaled screenshot, `peye` upsizes it to the node's Figma metadata dimensions before comparison so the reference stays aligned with the selected frame size.
143
+
144
+ Force REST fallback explicitly, for example in CI:
145
+
146
+ ```bash
147
+ PEYE_FIGMA_SOURCE=rest \
148
+ FIGMA_TOKEN=your_token_here \
149
+ peye compare \
150
+ --preview http://localhost:3000/#hero \
151
+ --reference "https://www.figma.com/design/FILE_KEY/Mockup?node-id=1-2" \
152
+ --viewport 1920 \
153
+ --output ./peye-output
154
+ ```
155
+
156
+ Write the machine-readable report to stdout for automation:
157
+
158
+ ```bash
159
+ peye compare \
160
+ --preview ./artifacts/preview.png \
161
+ --reference ./artifacts/reference.png \
162
+ --output ./peye-output \
163
+ --report-stdout
164
+ ```
165
+
166
+ Keep the CLI silent while still writing artifacts to disk:
167
+
168
+ ```bash
169
+ peye compare \
170
+ --preview ./artifacts/preview.png \
171
+ --reference ./artifacts/reference.png \
172
+ --output ./peye-output \
173
+ --quiet
174
+ ```
175
+
176
+ ## Output
177
+
178
+ `peye` writes these files into `--output`:
179
+
180
+ - `preview.png`: normalized preview image used for analysis
181
+ - `reference.png`: normalized reference image used for analysis
182
+ - `overlay.png`: blended reference + preview image
183
+ - `diff.png`: raw pixel diff image
184
+ - `heatmap.png`: mismatch heatmap with highlighted findings
185
+ - `report.json`: compact machine-readable result optimized for agent workflows
186
+
187
+ `report.json` is versioned and compact by default:
188
+
189
+ - `analysisMode` is `dom-elements` for URL captures and `visual-clusters` for local image inputs
190
+ - `images` preserves normalized preview, reference, and padded canvas dimensions for fast debugging
191
+ - `findings` is capped to the top actionable mismatches
192
+ - `findings[].signals` adds stable heuristic hints such as probable text clipping, capture crop, and viewport mismatch
193
+ - `findings[].hotspots` exposes the top mismatch subregions without forcing the caller to inspect images first
194
+ - `rollups.rawRegionCount` preserves the internal mismatch count without emitting every low-level region
195
+ - `error` is `null` on successful comparisons and contains a stable `code`, `message`, and `exitCode` for failure reports
196
+
197
+ ## Automation
198
+
199
+ For agent workflows, prefer one of these modes:
200
+
201
+ - Default mode: writes `report.json` to `--output` and prints a short human summary to stdout
202
+ - `--quiet`: writes files to `--output` and keeps stdout empty on success
203
+ - `--report-stdout`: writes files to `--output` and also emits the full report JSON as a single stdout payload
204
+
205
+ `--report-stdout` is the most stable mode when another tool is parsing the command result directly.
206
+
207
+ Suggested orchestration policy:
208
+
209
+ - exit code `0`: accept or continue to the next step
210
+ - exit code `2`: fix the top finding and rerun
211
+ - exit code `3`: stop the auto-fix loop and inspect the report plus artifacts
212
+ - exit code `1`: treat as an operational or input error, read `error.code`, fix inputs or environment, and retry
213
+
214
+ Example `report.json` shape:
215
+
216
+ ```json
217
+ {
218
+ "analysisMode": "dom-elements",
219
+ "summary": {
220
+ "recommendation": "retry_fix",
221
+ "severity": "medium",
222
+ "reason": "Mismatch is 3.21%; localized issues were detected and should be fixed before retrying."
223
+ },
224
+ "inputs": {
225
+ "preview": {
226
+ "input": "http://localhost:3000/#hero",
227
+ "kind": "url",
228
+ "resolved": "http://localhost:3000/#hero",
229
+ "selector": "#hero"
230
+ },
231
+ "reference": {
232
+ "input": "https://www.figma.com/design/FILE_KEY/Mockup?node-id=1-2",
233
+ "kind": "figma-url",
234
+ "resolved": "https://www.figma.com/design/FILE_KEY/Mockup?node-id=1-2",
235
+ "selector": null,
236
+ "transport": "figma-mcp-desktop"
237
+ },
238
+ "viewport": {
239
+ "width": 1920,
240
+ "height": 900
241
+ },
242
+ "mode": "all",
243
+ "fullPage": false
244
+ },
245
+ "images": {
246
+ "preview": { "width": 1920, "height": 900 },
247
+ "reference": { "width": 1920, "height": 900 },
248
+ "canvas": { "width": 1920, "height": 900 }
249
+ },
250
+ "metrics": {
251
+ "mismatchPixels": 1234,
252
+ "mismatchPercent": 3.21,
253
+ "meanColorDelta": 7.42,
254
+ "maxColorDelta": 24.5,
255
+ "structuralMismatchPercent": 8.13,
256
+ "findingsCount": 2,
257
+ "affectedElementCount": 2,
258
+ "dimensionMismatch": {
259
+ "widthDelta": 0,
260
+ "heightDelta": 0,
261
+ "aspectRatioDelta": 0,
262
+ "hasMismatch": false
263
+ }
264
+ },
265
+ "rollups": {
266
+ "bySeverity": [{ "severity": "medium", "count": 2 }],
267
+ "byKind": [
268
+ { "kind": "mixed", "count": 1 },
269
+ { "kind": "color", "count": 1 }
270
+ ],
271
+ "byTag": [
272
+ { "tag": "button", "count": 1 },
273
+ { "tag": "h1", "count": 1 }
274
+ ],
275
+ "rawRegionCount": 18,
276
+ "findingsCount": 2,
277
+ "affectedElementCount": 2,
278
+ "omittedFindings": 0
279
+ },
280
+ "findings": [
281
+ {
282
+ "id": "finding-001",
283
+ "source": "dom-element",
284
+ "severity": "medium",
285
+ "kind": "mixed",
286
+ "summary": "Element <button> differs in both layout and styling.",
287
+ "bbox": {
288
+ "x": 20,
289
+ "y": 80,
290
+ "width": 120,
291
+ "height": 36
292
+ },
293
+ "regionCount": 11,
294
+ "mismatchPixels": 519,
295
+ "mismatchPercentOfCanvas": 1.54,
296
+ "issueTypes": ["position", "spacing", "style"],
297
+ "signals": [
298
+ {
299
+ "code": "probable_text_clipping",
300
+ "confidence": "medium",
301
+ "message": "Text content likely overflows the element bounds and is being clipped on the horizontal axis."
302
+ }
303
+ ],
304
+ "hotspots": [{ "x": 20, "y": 80, "width": 52, "height": 36 }],
305
+ "element": {
306
+ "tag": "button",
307
+ "selector": "section#hero > button#cta",
308
+ "role": null,
309
+ "textSnippet": "Buy",
310
+ "bbox": {
311
+ "x": 20,
312
+ "y": 80,
313
+ "width": 120,
314
+ "height": 36
315
+ }
316
+ }
317
+ }
318
+ ],
319
+ "artifacts": {
320
+ "reference": "/abs/path/reference.png",
321
+ "preview": "/abs/path/preview.png",
322
+ "overlay": "/abs/path/overlay.png",
323
+ "diff": "/abs/path/diff.png",
324
+ "heatmap": "/abs/path/heatmap.png",
325
+ "report": "/abs/path/report.json"
326
+ },
327
+ "error": null
328
+ }
329
+ ```
330
+
331
+ Failure reports keep the same top-level shape and set `error` to a structured object, for example:
332
+
333
+ ```json
334
+ {
335
+ "summary": {
336
+ "recommendation": "needs_human_review",
337
+ "severity": "medium",
338
+ "reason": "Preview URL requires --viewport so the browser screenshot is deterministic."
339
+ },
340
+ "error": {
341
+ "code": "preview_viewport_required",
342
+ "message": "Preview URL requires --viewport so the browser screenshot is deterministic.",
343
+ "exitCode": 1
344
+ }
345
+ }
346
+ ```
347
+
348
+ ## Exit Codes
349
+
350
+ - `0`: `pass` or `pass_with_tolerated_differences`
351
+ - `2`: `retry_fix`
352
+ - `3`: `needs_human_review`
353
+ - `1`: operational error
354
+
355
+ ## Troubleshooting
356
+
357
+ - `Preview URL requires --viewport`: pass `--viewport 1920` or `--viewport 1920x900` when `--preview` is a URL.
358
+ - Playwright cannot launch Chromium: install it with `npx playwright install chromium`.
359
+ - Figma URL falls back to REST unexpectedly: check `inputs.reference.transport` in `report.json` and ensure `PEYE_FIGMA_SOURCE` is not forcing `rest`.
360
+ - Remote Figma MCP requires authorization: run `peye compare` in an interactive terminal so it can complete the OAuth callback flow.
361
+ - `FIGMA_TOKEN is required`: either export `FIGMA_TOKEN`, or make sure a Figma MCP source is reachable for Figma URLs.
362
+ - Figma MCP reference still looks softer than a manual export: MCP screenshots may be downscaled by Figma first; `peye` upscales them back to the node size for comparison, but if you need the original export raster use `PEYE_FIGMA_SOURCE=rest` with `FIGMA_TOKEN`.
363
+ - Selector capture fails: verify the selector exists at capture time and do not combine selector capture with `--full-page`.
364
+ - Large dimension mismatch triggers `needs_human_review`: check that preview and reference target the same frame, selector, and viewport.
365
+
366
+ ## Limitations
367
+
368
+ - No config file support yet; inputs are provided through CLI flags only.
369
+ - No mask support yet; every visible pixel participates in the comparison.
370
+ - No automatic geometric alignment step yet; mismatches are evaluated on the captured canvas as-is.
371
+ - Browser capture currently uses Playwright Chromium.
372
+ - DOM-based findings are heuristic and depend on the element boxes collected during capture.
373
+ - Exit code `1` can still happen before the CLI is able to produce comparison artifacts; use `error.code` when `report.json` is present and stderr otherwise.
374
+
375
+ ## Uninstall
376
+
377
+ If `peye` was installed globally, remove it with your package manager:
378
+
379
+ ```bash
380
+ npm uninstall -g peye
381
+ ```
382
+
383
+ ```bash
384
+ pnpm remove -g peye
385
+ ```
386
+
387
+ If it was installed in a project, use:
388
+
389
+ ```bash
390
+ npm uninstall peye
391
+ ```
392
+
393
+ or:
394
+
395
+ ```bash
396
+ pnpm remove peye
397
+ ```
398
+
399
+ ## Development
400
+
401
+ ```bash
402
+ pnpm install
403
+ pnpm format:check
404
+ pnpm lint
405
+ pnpm typecheck
406
+ pnpm test
407
+ pnpm build
408
+ ```
409
+
410
+ Run the full quality gate with:
411
+
412
+ ```bash
413
+ pnpm check
414
+ ```
415
+
416
+ Clean generated local artifacts with:
417
+
418
+ ```bash
419
+ pnpm clean
420
+ ```
421
+
422
+ For local development without building every time:
423
+
424
+ ```bash
425
+ pnpm dev compare --preview ./preview.png --reference ./reference.png --output ./tmp/peye
426
+ ```