| name | vlmkit |
| description | Entry-point for the `@mizchi/vlmkit` toolkit โ VLM-driven frontend kit covering visual regression (snapshot / diff / regression-watch), markup synthesis from screenshots, design-token / theme / a11y / i18n audits, and a 2-stage VLM + LLM CSS auto-repair loop. Use when a coding agent has edited HTML/CSS and needs to know whether the visible output changed, where it changed, and which CSS properties drove the change โ or when the task is markup-from-image / token / theme audit / fix-loop driven by VLM. This skill orients you to the 5 detailed sub-skills (vrt-visual-diff / vrt-migration-eval / vrt-markup-synth / vrt-regression-watch / vrt-css-fix-loop) and the verb-group CLI; pick the matching sub-skill once the task shape is clear. |
vrt
vlmkit is a TypeScript visual-regression toolkit built on Playwright +
pixelmatch. Beyond raw pixel diffs, it surfaces agent-friendly
signal: computed-style deltas split into universal vs.
breakpoint-gated, per-section diffRatio against component bboxes,
worst-viewport screenshot paths inline, and (optionally) VLM-emitted
CHANGE lists feeding an LLM CSS-fix step.
Source: https://github.com/mizchi/vlmkit. CHANGELOG +
old-CLI-to-new-CLI mapping: CHANGELOG.md.
When to invoke this skill
- An agent edited CSS / HTML / a component and you need "what visibly
changed, and where."
- You're auditing a refactor PR for unintended layout drift.
- You're comparing two URLs (dev server vs. preview deploy; baseline
vs. variant).
- You want a CI gate that flags regression across PRs.
- You're swapping a framework / CSS library and need to verify the
rewrite is visually equivalent.
When NOT to invoke
- Snapshot testing of pure-data structures (use Jest snapshots).
- Lighthouse / Web Vitals beyond CLS/LCP/FCP (use Lighthouse directly).
- Browser-driver-only flows with no diff component (use Playwright Test).
- Reviewing AI-generated screenshots for content correctness (use a
vision-LLM tool like
review-image).
Install
Prerequisite: Node 24+. The CLI and the workspace packages all
rely on Node's --experimental-strip-types (default-on at 24+).
Node 22 will not work โ verify with node --version before
installing. Use nvm install 24 && nvm use 24 (or fnm / volta /
your preferred manager) to upgrade.
Pre-flight check: run scripts/doctor.sh to verify Node version,
Playwright Chromium, optional API keys, and installed sub-skills in
one pass. Two invocation forms by setup phase:
- Pre-install (no apm yet): one-shot via curl โ
bash <(curl -sSL https://raw.githubusercontent.com/mizchi/skills/main/vlmkit/scripts/doctor.sh)
- Post-install (
apm install -g mizchi/skills/vlmkit/vlmkit done): local
copy โ bash ~/.claude/skills/vlmkit/scripts/doctor.sh
Severity rules: only Node 24+ and Playwright Chromium are
FAIL-class (block exit 0). Everything else โ including vrt CLI not
yet on PATH โ is WARN by design, because the script is meant to be
runnable mid-install (a WARN on vlmkit CLI is expected the first time
through; resolve all FAILs first, install vrt, re-run, and the WARN
clears).
pnpm add -g @mizchi/vrt
pnpm add -D @mizchi/vrt
npx playwright install chromium
pnpm add @mizchi/vlmkit-core @mizchi/vlmkit-capture @mizchi/vlmkit-markup @mizchi/vlmkit-ai
CLI cheatsheet (0.5.0 verb groups)
I/O conventions used below: --output <dir> always takes a
directory path (the engine writes diff-report.json + per-viewport
PNGs into it). vlmkit diff agent writes Markdown to stdout by
default; pass --out <path> to write to a file instead.
Commands taking two positional args (e.g. vlmkit diff html <baseline> <variant>) accept either two local file paths or two URLs; use
--url/--current-url if you want to be explicit.
vlmkit diff html <baseline> <variant> --output reports/
vlmkit diff agent reports/diff-report.json [--out diff.md]
vlmkit diff png base.png current.png
vlmkit diff elements --selector .card โฆ
vlmkit diff browsers <url>
vlmkit diff runs <dirs...>
vlmkit snapshot <url1> [url2]... --output snapshots/
vlmkit snapshot approve
vlmkit snapshot stability <url...> --iterations 5
vlmkit snapshot flipbook --output snapshots/
vlmkit check a11y contrast|touch|focus <html>
vlmkit check tokens <html>
vlmkit check theme <html>
vlmkit check perf <html>
vlmkit inspect interact|explore|smoke <html|url>
vlmkit stress i18n|media <html>
vlmkit scan component|breakpoints <โฆ>
vlmkit build component <target.png> <current.html>
vlmkit migration compare|blind|subagent <baseline> <variant> --output reports/
vlmkit watch <baseline> <variant>
vlmkit manifest add|list|rm|check
vlmkit diff-pr pin|verify|post
vlmkit baseline pin|verify|post|list|rm
The single-token commands from 0.4.x (vrt compare, vrt png-diff,
vrt theme-parity, โฆ) remain as deprecation shims that print a
one-line hint and forward.
Sub-skill routing
Two repos, by design: this orient skill lives in
mizchi/skills (general-purpose
skills); the five vrt-specific sub-skills live in
mizchi/vlmkit/vlmkit under
.claude/skills/. The two apm install paths below look different
because they target different repos โ that is intentional, not a typo.
The CLI is fully functional without any sub-skill installed โ
sub-skills are agent-facing reference material that deepens the routing
for a specific task shape. Install only when an agent needs the extra
context; end users running vrt from the command line never need
them.
Scope boundary: this orient skill stops at routing. Once you know
which sub-skill to load, deeper operational detail โ full flag lists,
persistence paths (e.g. .vrt/last-diff-for-agent.json for regression
watch), per-mode semantics, output schema โ lives in the corresponding
sub-skill. Install it via the apm command above when an agent needs
that depth.
apm install mizchi/vlmkit/.claude/skills/<skill-name>
Pick by task shape:
| Sub-skill | Use when | Entry workflow |
|---|
vrt-visual-diff | One-shot "did this CSS edit change pixels, and where?" | vlmkit diff html โ vlmkit diff agent |
vrt-regression-watch | CI gate / scheduled drift detection across runs | vlmkit diff agent --previous --fail-on-regression |
vrt-migration-eval | Framework / CSS-lib / build-system swap audit (deliberate large diff) | vlmkit migration compare|blind|subagent |
vrt-markup-synth | Screenshot โ HTML/CSS, token / theme / i18n / a11y audits | vlmkit build|scan|check|stress |
vrt-css-fix-loop | Automated CSS-repair loop with VLM + LLM | fix-loop.ts (VRT_VLM_MODEL=โฆ) |
Routing heuristic (ask yourself once the user states the task):
Is the markup deliberately different (rewrite / framework swap)?
โโ yes โ vrt-migration-eval
โโ no
โโ Need to detect change between runs over time? โ vrt-regression-watch
โโ Want a CSS-fix loop (auto-repair)? โ vrt-css-fix-loop
โโ Building from a screenshot / token audit? โ vrt-markup-synth
โโ One-shot "what changed" โ vrt-visual-diff
Output anatomy
vlmkit diff html writes:
<output>/
โโโ diff-report.json # canonical machine input for diff agent
โโโ migration-report.json # legacy alias, byte-identical (will be removed)
โโโ diff-mobile.png # per-viewport pixel diff
โโโ diff-desktop.png
โโโ diff-wide.png
vlmkit diff agent <report.json> then emits Markdown structured as:
# VRT diff (for agent)
### Diff by viewport (worst first)
| Viewport | Diff | Dominant category | Categories | Shift bands |
| ...
### Verified deltas (computed-style) ร viewport โ HOISTED to #2
#### Universal pairs โ fix the base rule
#### Breakpoint-gated pairs โ fix or add @media rule
### Per-section diffRatio
### Heuristic fix candidates
The hoisted Verified deltas is the load-bearing signal โ read
Universal pairs first, then Breakpoint-gated, then drill into
Per-section.
Common gotchas
- Masking is load-bearing. Pass
--mask ".marquee,.timestamp, [data-testid='live-counter']" for anything that flaps run-to-run.
An unexpected baseline-noise diff is almost always a missing mask,
not a real regression.
- Output is a directory, not a file.
--output reports/ makes
reports/diff-report.json + PNGs. Feed the JSON path (not the dir)
to vlmkit diff agent.
- Playwright must be installed. First run errors with "browser not
found" โ
npx playwright install chromium.
- Self-comparing the same URL with no mask is the false-positive
baseline. Use this to verify
--mask actually catches every
flapping element before relying on diff% threshold.
- The dist binary is bundled.
dist/vlmkit.mjs ships with all leaves
code-split. There is no source dependency at install time.
Environment variables
| Variable | Purpose | Default |
|---|
VRT_LLM_PROVIDER | LLM provider for fix-loop (gemini / openrouter / anthropic) | gemini |
VRT_LLM_MODEL | LLM model override | Provider default |
VRT_VLM_MODEL | VLM model (OpenRouter id, or gemini:/claude: prefix) | bytedance/ui-tars-1.5-7b |
OPENROUTER_API_KEY / GEMINI_API_KEY / ANTHROPIC_API_KEY | Per-provider auth | โ |
VRT_CAPTURE_BACKEND | local (default Playwright) or cloudflare (Browser Run) | local |
DEBUG_VRT | Verbose log output | unset |
Library use (vrt-core / vrt-capture / vrt-markup / vrt-ai)
When the CLI surface isn't enough, deep-import the relevant package:
import { compareScreenshots } from "@mizchi/vlmkit-core/heatmap.ts";
import { discoverViewports } from "@mizchi/vlmkit-capture/viewport-discovery.ts";
import { extractComponents } from "@mizchi/vlmkit-markup/component/component-extract.ts";
import { askVlm } from "@mizchi/vlmkit-ai";
@mizchi/vlmkit-core has no Playwright dependency for the lightweight
surface (image / DOM / a11y primitives). The other three pull in
Playwright transitively.
Reporting issues / contributing