Sixteen commands, no guesswork
Two of them do almost everything: init once, then check forever. The rest are there for the days something is wrong, or CI needs to work differently.
Six of them are verbs that run on their own — init, doctor, capture, compare, check, clean. The rest belong to a noun group and need a subcommand: cloud publish, not cloud. Typing a group on its own prints your choices, does nothing, and exits 2 — a noun is not a verb.
npx norma-scope initNothing to install first — npx fetches it on the first run. Node 18 or newer is the whole list of requirements.
- 16
- commands
- 1
- config file
- 0
- accounts to run locally

Grouped by what they do
Answer three questions,
get a working config
This writes a real .bridge/config.json and the exact commands to run with it. Copy it into your project and it works.
.bridge/config.json
{
"threshold": 5,
"source": { "type": "images", "dir": "designs" },
"app": { "baseUrl": "http://localhost:3000" },
"frames": [
{
"label": "Pricing",
"screenshot": "pricing.png",
"route": "/pricing",
"viewport": { "width": 1440, "height": 900 }
}
]
}init adds one more line per screen: a short code like fr_k3m9…. It is different for every project, so it is not shown here. It is what lets you rename pricing.png later without losing its history.
then run
$ npx norma-scope init
$ npx norma-scope doctor
# start your app, then:
$ npx norma-scope check
$ open .bridge/reports/report.html
No token, no network, no account — the reference is already in your repo.
Before choosing whether to share usage analytics.
- Default collection is release-gated off. Preview opt-in uses NORMA_TELEMETRY=1.
- Reads
- Local telemetry preference.
- Writes
- Nothing; does not create an installation ID.
- Network
- None — it makes no request at all
- Can it spend money?
- No
- Can it fail a build?
- No.
Before choosing whether to share usage analytics.
- Default collection is release-gated off. Preview opt-in uses NORMA_TELEMETRY=1.
- Reads
- Local telemetry preference.
- Writes
- Nothing; does not create an installation ID.
- Network
- None — it makes no request at all
- Can it spend money?
- No
- Can it fail a build?
- No.
What each one touches
Everything here runs on your machine unless the network column says otherwise, and nothing spends money unless the money column says so.
| Command | Reads | Writes | Network | Spends |
|---|---|---|---|---|
| telemetry status | Local telemetry preference. | Nothing; does not create an installation ID. | None | Nothing |
| telemetry disable | Local telemetry preference. | Persistent opt-out; removes the saved installation ID. | None | Nothing |
| init | your answers, the design source's API | .bridge/config.json, the .bridge/ folders, .gitignore entries, a pre-commit hook | your design source, to list its frames | Nothing |
| doctor | config, token, design file, your app URL | nothing — diagnosis only | your design source and your running app, to check both answer | Nothing |
| capture | config + your running app | .bridge/screenshots/*.png | your own running app, in a headless browser | Nothing |
| compare | .bridge/screenshots/ + the reference your source points at | .bridge/diff/, report.html, and summary.json with --json | your design source, unless a snapshot or cache already has the reference | Nothing |
| check | everything capture and compare read | everything capture and compare write | the same as capture and compare — your app, and your design source | Nothing |
| explain --local [frame] | screenshots, diffs, captured DOM context, optional source excerpts, your API key | findings in the terminal and in the report | your own model provider, under your own key. Nothing goes to Normascope Cloud | your own provider account |
| cloud publishCloud | .bridge/reports/summary.json and the images it names | .bridge/reports/cloud.json — the receipt, written on every run including a failed one | Normascope Cloud | nothing by itself; --explain spends Cloud credits |
| cloud validateCloud | .bridge/config.json | nothing — it exits 1 if it found something | None | Nothing |
| frames listCloud | .bridge/config.json | nothing — it just prints | None | Nothing |
| frames adoptCloud | .bridge/config.json | .bridge/config.json — then commit it | None | Nothing |
| ci comment | .bridge/reports/summary.json, and .bridge/reports/cloud.json when a publish wrote one | markdown on stdout | None | Nothing |
| baseline approve | .bridge/screenshots/ | .bridge/baseline/ plus a manifest — commit it | None | Nothing |
| design snapshot | the design file | .bridge/design/ — commit it | your design source | Nothing |
| clean | — | empties screenshots/, diff/, reports/ and the cache | None | Nothing |
The four upload modes
Set cloud.upload in .bridge/config.json, least disclosure first. This only ever applies to cloud publish — nothing else in the CLI sends anything.
| Mode | What it sends |
|---|---|
| off | Nothing at all. No request is made, and `cloud publish` becomes a no-op rather than an error. |
| metrics | The numbers and nothing else. No pixels leave your machine — and a run with no images cannot be explained by Cloud. |
| review | Full evidence for every frame over threshold, plus one preview for each clean frame. The default. |
| full | Full evidence for every compared frame. |
Every option, by command
Anything not on this list is refused. A misspelled flag never runs the command anyway — --dry-runn is an error, not a publish.
- --jsoncompare, check
- Also write summary.json (schema v3, published and validated)
- --jsondoctor
- Print the result as one line of JSON instead of a table, for a pipeline to read
- --fullcompare, check
- Embed full-resolution images in the report
- --freshcompare, check
- Bypass the design cache and refetch
- --strictcompare, check
- Exit 1 on a measured regression — the only way a comparison turns a job red
- --targetcompare
- Zero-config mode: the mock PNG to diff against. Requires --url
- --urlcompare
- The page to capture, in zero-config mode
- --selectorcompare
- Capture one element instead of the full page, in zero-config mode
- --localexplain
- Required. Analyse here, on your own provider key — nothing goes to Normascope Cloud
- --allexplain
- Explain every compared frame, not just the flagged ones
- --deepexplain
- Use the stronger, more expensive model
- --modecloud publish
- Override the configured upload mode for one run: off, metrics, review or full
- --dry-runcloud publish
- List exactly what would be sent, and send none of it
- --full-evidencecloud publish
- Full images for every frame, not previews for the clean ones
- --explaincloud publish
- Ask Cloud to analyse the flagged frames after publishing. Spends Cloud credits
- --checkdesign snapshot
- Has the live design drifted from the committed exports?
- --help, -hevery command
- Print what this command does and run nothing
Start from the problem
“Just tell me if this page matches this picture.”
compare --target x.png --url …
“Do that for five pages, on every commit.”
init, then check
“Take the screenshots for me.”
capture
“Capture and compare in one go.”
check
“Nothing loaded, or it says skipped.”
doctor
“Explain it using my own provider key.”
explain --local [frame]
“Explain it using the Cloud credits I bought.”
cloud publish --explain
“Will Cloud accept my config?”
cloud validate
“There's no designer — I just don't want surprises.”
mode: "baseline", then baseline approve
“CI shouldn't need a design token.”
design snapshot, and commit .bridge/design/
“Render the PR comment.”
ci comment
“Fail the build on a regression.”
compare --strict
Three things that trip people up
Start your app first
Normascope photographs a running app; it doesn't start one. Run your dev server, then run the command.
Nothing is ever blocked
A bad score is information. The git hook always succeeds and CI stays green unless you explicitly ask for the opposite.
Name the file after the page
The screenshot filename is what ties your design, your capture and the difference together. Keep them the same and everything lines up.
Every command ships with --help, and doctor explains anything that’s misconfigured in your project rather than making you go and look it up.