# Command line

```
shotlist [<name>…] [options]
```

With one or more names, shotlist takes those recipes. With no names and no options, it lists the recipes it found.

## Options

| Option | Effect |
| --- | --- |
| `--all` | Take every recipe |
| `--install` | Copy each image to the recipe's install destination |
| `--check` | Re-take and compare against the installed images |
| `--diff` | With `--check`, write a three-panel diff image |
| `--json` | With `--check`, write the report to stdout as JSON |
| `--lint` | Parse every config, recipe, macro and data file, report everything wrong, and stop |
| `--warnings` | With `--lint`, also report what is legal but probably unmeant |
| `--keep-going` | Carry on past a recipe that fails, and list them all at the end |
| `--init` | Write a starter configuration and recipe; never overwrites |
| `--config <file>` | Use a specific configuration file |
| `--login <name>` | Sign in and save the named session |
| `--using <macro>` | With `--login`, sign in by running a macro |
| `--untrusted` | Run with the restrictions listed below |
| `--allow <destination>` | Approve one network protocol, host and port; repeatable |
| `--allow-path <dir>` | Permit an extra directory; repeatable |
| `--allow-env <name>` | Permit a recipe to read this variable from the run's environment; repeatable |
| `--deny <name>` | Forbid a file or folder name; repeatable |
| `--work-limit <name=value>` | Change one numerical Work limit for this Run; repeatable |
| `--help` | Print the option list |
| `--version` | Print the version |

## --login

Opens a browser at `site.url`, waits while you sign in, and writes the session named on the command line. With `--using` the browser is headless and a macro signs in instead, so the run names the host and the variables the macro may read before it starts.

Signing in

```
$ npx shotlist --login admin --using sign-in --allow-env ADMIN_PASSWORD
Signing in at https://app.example.com with `sign-in`, which may type ADMIN_PASSWORD into it.
  ✓ wrote .shotlist/admin.json
    left out 23 cookies and 1 origin for google.com, accounts.google.com, youtube.com
```

| Line | When |
| --- | --- |
| ``Signing in at <url> with `<macro>``` | Before the browser starts, with `--using` |
| `A browser is open at <url>` | Signing in by hand, before it waits for you |
| `left out N cookies …` | Something was dropped as not this site |
| ``! `site.sessions.<name>.keep` names …`` | The session declares `keep` |
| ``! this session has no `verify` selector …`` | Something was dropped and nothing could check what remains |

Nothing is written when the sign-in did not take, or when what is left after narrowing no longer satisfies `verify`. An `--untrusted` run is refused a session, so `--login` has no untrusted mode.

## Exit codes

| Code | Meaning |
| --- | --- |
| `0` | Every recipe succeeded; with `--check`, nothing changed |
| `non-zero` | A recipe failed, or a checked screenshot changed |
| `--lint` | Non-zero when a file has an error; warnings alone leave it at `0` |

## Environment variables

| Variable | Effect |
| --- | --- |
| `SHOTLIST_ENV` | Names a recipe may read, in addition to `--allow-env` |
| `SHOTLIST_ENV_DENY` | Variable names no config key or flag may grant; globs allowed, subtracted last |
| `SHOTLIST_DENY` | Forbidden names, separated by commas or colons |
| `SHOTLIST_ALLOW` | Network destinations approved by the operator, separated by commas or whitespace |
| `SHOTLIST_WORK_LIMITS` | Work limit changes as comma-separated `name=value` settings |
| `SHOTLIST_UNTRUSTED` | Set to `1` for the effect of `--untrusted` |

These are read from the process environment. shotlist does not load a `.env` file — that name is one it refuses to open — so anything kept there has to be in the environment before the command runs. See [Capture a page behind a sign-in](/docs/how-to/sign-in/).

## Network destinations

A bare host approves HTTPS on port 443. Prefix it with `*.` to approve its HTTPS subdomains. HTTP, WebSocket and non-default ports use a full destination, such as `http://localhost:4321` or `wss://events.example.com`. Protocols match exactly; HTTP and WebSocket connections use separate approvals. An approved HTTP destination also permits a redirect to HTTPS port 443 on the same host. A TCP readiness probe uses `tcp://localhost:4321`.

## Check statuses

| Status | Meaning |
| --- | --- |
| `same` | Within the configured threshold and tolerance |
| `changed` | Outside them |
| `new` | Nothing is committed at the destination |
| `skipped` | The recipe installs nowhere, so there is nothing to compare |
| `failed` | The recipe could not be taken |
| `cancelled` | The caller cancelled this recipe |
| `not-attempted` | An earlier failure stopped the request |

## JSON report

Written by `--check --json`. Human-readable output moves to stderr.

report.json

```
{
  "changed": 1,
  "total": 1,
  "results": [
    {
      "name": "order-row",
      "status": "changed",
      "ratio": 0.0213,
      "shot": "screenshots/out/order-row.png",
      "against": "content/guide/images/order-row.png",
      "diff": "screenshots/out/diff/order-row.png"
    }
  ],
  "failures": [],
  "drift": [{ "field": "chromium", "was": "141.0.0.0", "now": "139.0.0.0" }],
  "operatorDestinations": []
}
```

| Field | Type | Meaning |
| --- | --- | --- |
| `changed` | number | How many screenshots differ |
| `total` | number | How many were compared |
| `drift` | array | Differences between this machine and the recorded baseline |
| `results[].name` | string | Recipe name |
| `results[].status` | string | One of the statuses above |
| `results[].ratio` | number | Fraction of pixels that differ |
| `results[].shot` | string | Path to the image just taken |
| `results[].against` | string | Path to the committed image |
| `results[].diff` | string | Path to the diff image, with `--diff` |
| `results[].ignored` | number | Regions omitted from comparison |
| `failures` | array | Site, browser or baseline failures outside one recipe |
| `operatorDestinations` | array | Canonical destinations granted by the operator |
| `cancellation` | object | Request cancellation detail, when cancelled |
| `warnings` | array | Progress-observer warnings |

## Work limits

| Name | Default | Measures |
| --- | --- | --- |
| `recipeBytes` | `1048576` | Bytes in one recipe document |
| `macroBytes` | `1048576` | Bytes in one macro document |
| `dataBytes` | `10485760` | Bytes in one data document |
| `authoredSteps` | `1000` | Steps written in one document |
| `stepDepth` | `32` | Nested Step depth |
| `macroDepth` | `32` | Nested Macro expansion depth |
| `expandedSteps` | `5000` | Steps after Macro expansion |
| `executedSteps` | `10000` | Steps run by a recipe or sign-in |
| `eachItems` | `1000` | Items processed by one `each` Step |
| `matchingCharacters` | `256` | Characters in one matching pattern |
| `recipeMilliseconds` | `600000` | Elapsed recipe work |
| `teardownSteps` | `1000` | Steps run during teardown |
| `teardownMilliseconds` | `60000` | Elapsed teardown work |

## Baseline file

`--install` writes `shotlist.baseline.json` beside the configuration file, recording the shotlist, Playwright and Chromium versions and the platform that produced the images.

## Node.js API

`openRun()` takes explicit Operator authority and opens the configuration and Library. Capture and Checking requests run through that object.

```
import { lint, openRun, type CheckReport } from 'shotlist'

const authority = { untrusted: false }
const run = openRun(authority, 'shotlist.config.yaml')

await run.capture({ recipes: ['order-row'], install: true })

const controller = new AbortController()
const report: CheckReport = await run.check({
  all: true,
  diff: true,
  keepGoing: true,
  signal: controller.signal,
  onProgress: async (progress) => console.log(progress.type),
})

const review = lint(authority, 'shotlist.config.yaml')
```

| Export | Purpose |
| --- | --- |
| `openRun()` | Open a Project and Library under Operator authority |
| `run.capture()` | Capture a named selection and optionally install it |
| `run.check()` | Compare a named selection with installed images |
| `lint()` | Review a Project under Operator authority |
| `readSession()` | Read a configured Session by name through a Run |
| `signIn()` | Write a configured Session by name through a Run |
| `parseConfig()`, `parseRecipe()`, `parseMacro()`, `parseLibrary()`, `parseQuery()` | Validate a value already in memory |

A request selects `{ recipes: names }` or `{ all: true }`. `keepGoing`, `signal` and `onProgress` apply to Capture and Checking; `install` applies to Capture and `diff` to Checking. Progress observers are awaited in order. Their failures appear in `warnings` rather than changing a Recipe result.

Capture installs images only after every selected Recipe and owned resource has finished cleanup. Its report separates Recipe results, resource failures, Installation outcomes and Baseline recording. A Run accepts one request at a time and remains reusable after that request settles.

## JSON Schemas

| File | Describes |
| --- | --- |
| `shotlist/dist/config.schema.json` | `shotlist.config.yaml` |
| `shotlist/dist/recipe.schema.json` | A recipe |
| `shotlist/dist/macro.schema.json` | A macro |

Reference one from a file for editor completion and inline validation:

screenshots/recipes/order-row.yaml

```
# yaml-language-server: $schema=../../node_modules/shotlist/dist/recipe.schema.json
```

The config's schema was `dist/schema.json` before 0.4.0, which named one of three files "the schema". That file is still written and `shotlist/schema.json` still resolves to the config schema, so an existing YAML header or editor setting keeps working. Both go at 1.0.

Written by Nicola Mustone · Applies to shotlist 0.6.0 · Maintained by Nicola Mustone

Published date unavailable · Updated date unavailable · [View source](https://github.com/SirDarcanos/shotlist.dev/blob/main/src/pages/docs/reference/cli.astro) · [Propose a correction](https://github.com/SirDarcanos/shotlist.dev/edit/main/src/pages/docs/reference/cli.astro)
