Skip to content
quest'n'click

ReferenceReferenceFor authors, contributorsApplies from 4.5.0

npm scripts

Cette page en françaisThis page on the GitHub wiki

Every npm run script of the engine repository, and every script a project made with quest-n-click create gets. Each script has its own section: link to it as reference.npm-scripts.md#verify-game (the name in lowercase, : written -; a created project's script is #project-<name>).

Where to run them

All the commands run from the root folder of their project, the one that holds package.json:

  • in a clone of the engine repository (quest-n-click/), after npm ci: the scripts of the engine repository below. They work on the current game, games/<id>/: the GAME variable (GAME=demo npm run dev), else package.json → "config": { "game": "demo" }, else demo. How to choose it, and how npm run game keeps the editor in step: create.working-loop;
  • in a created project (made by npx create-quest-n-click <folder>), after npm install: only the eight scripts of a created project. Each calls the quest-n-click command of the installed engine; its other commands are in reference.cli.

A script that takes arguments gets them after --: npm run solve -- --prove. npm run -s <script> silences npm's own lines (what an MCP client needs for npm run -s mcp).

The sections below are generated from package.json, from cli/create.mjs and from the descriptions of wiki/data/npm-scripts.json (npm run wiki -- generate reference.npm-scripts): a script added without a description, or a description whose script is gone, fails npm run wiki -- check.

Generated from the engine's own data

Scripts of the engine repository

Run in a clone of the engine repository, from its root folder (the one that holds package.json), after npm ci.

Playing, serving and choosing the game

Run the game or the Studio on this machine, serve a build, pick the current game of the repository.

npm run dev

The game in the browser on a loopback-only Vite dev server, reloading as you edit. It first points .cache/game at the current game (npm run game). URL parameters such as ?dev and ?edit=<room> work on it (reference.dev-url-parameters).

  • Runs: tsx tools/select-game.ts && vite

npm run dev:lan

The dev server in explicit LAN mode, for a phone on the same network: tools/serve.ts --lan starts Vite with a fresh capability token for every file-writing route and prints a one-session token URL to open on the phone.

  • Runs: tsx tools/select-game.ts && tsx tools/serve.ts --lan

npm run game

Points .cache/game (a symbolic link git ignores) at games/<GAME>/, where tsconfig.json's @game paths look first (games/demo second, so a fresh checkout type-checks). dev, check and build run it first; GAME=<id> npm run game switches the current game (4.1.6).

  • Runs: tsx tools/select-game.ts
  • Usage: GAME=<id> npm run game

npm run new-game

Scaffolds games/<id> from games/_template and makes it the current game; it writes a provenance.json for the art it borrows (all placeholders, CC BY 4.0).

  • Runs: node scripts/new-game.mjs
  • Usage: npm run new-game <id> ["Working title"]

npm run preview

Serves dist/ on 127.0.0.1 (vite preview): what the e2e scripts are pointed at after a build.

  • Runs: vite preview --host 127.0.0.1

npm run start

Serves dist/ on every interface at $PORT (8080 by default) with sirv, through scripts/start.mjs: a Node launcher, so it also runs on Windows (4.1.8). What a host such as Clever Cloud runs.

  • Runs: node scripts/start.mjs
  • Usage: npm start

npm run studio

The Studio (STUDIO=1), loopback-only, opened at /__studio/: rooms, dialogues, the puzzle graph, the mixer.

  • Runs: tsx tools/select-game.ts && cross-env STUDIO=1 vite --open /__studio/

npm run studio:lan

The Studio on the LAN, protected by a capability token (tools/serve.ts --lan --studio).

  • Runs: tsx tools/select-game.ts && tsx tools/serve.ts --lan --studio

Images, sounds and review pages

Prepare what the content cites, write the art prompts, and the pages a person reads on a phone.

npm run assets

Prepares in public/assets/ the images and sounds the content cites: tools/refs.ts lists them (rooms, characters, items, the map, minigame parameters, skin, the title and credits screens, ending.scratch, extraImages) into .cache/refs.json, then python3 tools/assets.py converts them (Python 3 with Pillow) and writes games/<GAME>/assets.gen.json, reprocessing only what changed.

  • Runs: node -e "require('node:fs').mkdirSync('.cache',{recursive:true})" && tsx --tsconfig tsconfig.json tools/refs.ts > .cache/refs.json && python3 tools/assets.py

npm run audio

The Mega Drive music and sound-effect pipeline (python3 tools/audio.py; create.music, create.sound-effects).

  • Runs: python3 tools/audio.py
  • Usage: npm run audio -- …

npm run code-wheel

The printable code wheel of a game (4.1.15): the large disc with the portraits from the sprites, the small disc with its windows, a booklet (assembly, the question, practice answers upside down), in colour and economy; SVG always, PDF with Pillow (tools/code-wheel-pdf.py; npm run doctor checks it).

  • Runs: tsx tools/code-wheel.ts
  • Usage: npm run code-wheel -- --game <id> [--format svg|pdf --seed <code> --out <dir>]

npm run icons

Writes public/icons/icon-192.png, icon-512.png and public/og.png from the game's site.json (title, optional ogLines: up to three lines above the title), with no dependency.

  • Runs: node scripts/gen-icons.mjs

npm run import-layout

Merges layouts exported from the placement page into games/<id>/layout/<room>.json. Accepted per file: { "layouts": { "<room>": Layout } } (the page's "Export JSON"), { "room", "layout" } (one document of the layouts collection), or a list of such documents. --dry: say, write nothing.

  • Runs: tsx --tsconfig tsconfig.json tools/pages/import-layout.ts
  • Usage: npm run import-layout -- <file.json|dir> [--dry]

npm run page:placement

Writes the placement page, to place things from a phone; its export comes back with npm run import-layout (create.review-pages).

  • Runs: tsx --tsconfig tsconfig.json tools/pages/placement.ts
  • Usage: npm run page:placement [-- --out <dir|file.html>]

npm run page:puzzles

Writes the puzzle graph as a page: what every rule needs and changes, a card per item and flag (create.review-pages).

  • Runs: tsx --tsconfig tsconfig.json tools/pages/puzzles.ts
  • Usage: npm run page:puzzles [-- --out <dir|file.html>]

npm run page:review

Writes the art review page: every cell of every sheet of games/<id>/art/ as a thumbnail with a keep / redo / unused decision and a note, the cells the game uses marked with the ids that cite them, the ids no file provides listed as missing (create.review-pages).

  • Runs: tsx --tsconfig tsconfig.json tools/pages/review.ts
  • Usage: npm run page:review [-- --out <dir|file.html>]

npm run page:storyboard

Writes games/<id>/storyboard.json as a phone-friendly page with a notes box under every panel (--md also writes games/<id>/storyboard.md, the plain text sub-agents read, the same text as the Studio's Markdown export) (create.review-pages).

  • Runs: tsx --tsconfig tsconfig.json tools/pages/storyboard.ts
  • Usage: npm run page:storyboard [-- --out <dir|file.html>] [--md]

npm run page:world

Writes the map of the world as a page: exits, gotos, unreachable rooms, the DOT source (create.review-pages).

  • Runs: tsx --tsconfig tsconfig.json tools/pages/world.ts
  • Usage: npm run page:world [-- --out <dir|file.html>]

npm run prompts

Writes games/<id>/prompts.md: every image-model prompt of the game ready to paste, one section per sheet, all sharing one STYLE block and one reference sheet (games/<id>/art/_reference.png, or the hero's validated sheet while it does not exist); cells already cut are marked "(exists — keep)". --missing: only the sheets with an image not cut yet, and only their missing cells; --out <file>: another output file. It reads the game module and the art folder: characters (cast.ts: the base 6 × 4 sheet with the engine's pose rows, human() or cat(); the special poses the content uses — extra sprites keys, { pose: [who, p] } and { anim: [who, p] } in rooms, actor poses, variants — a new <sheet>_poses sheet for poses no sprite provides yet with the lines to add to cast.ts, the seat rule, the mouth kit for the poses in mouths); objects (every image a prop, item, minigame or the interface uses, by sheet folder; a prop's states drawn as "the SAME object, state …"); backgrounds (decor/<name>: LOCATION, the hotspots to show, EMPTY SPOTS left / center / right from layout/<room>.json); furniture (props whose image is in a furniture_* folder, and a room's furniture list); and a checklist (reference → hero → other characters → objects → backgrounds → furniture, each with its python3 tools/cut-sheet.py command, --cells for the missing ones, then npm run assets). Three optional fields feed it without changing the game: description on a character (the CHARACTER line; missing, a <describe: …> placeholder stays), description on a room (the LOCATION line), furniture on a room (image ids drawn on their own sheet, e.g. furniture_dining/table). The MCP tool asset_prompts ({ missing?: boolean }) gives the same text and the missing image ids as JSON.

  • Runs: tsx --tsconfig tsconfig.json tools/prompts.ts
  • Usage: npm run prompts [-- --missing] [-- --out <file>]

npm run seal

Encrypts the sealed ending (ticket and final card) into public/data/dossier.bin, only the chosen outcome (one key of outcomes in games/<GAME>/private/ending.config.ts; without it, the committed ending.config.example.ts). The config's password must match ending.password in the game file.

  • Runs: tsx scripts/seal.ts
  • Usage: npm run seal -- --outcome=<key>

Checking the game

What a game must pass: validation, the solver and its proofs, the lint, translations, voices, weight, provenance, the release gates.

npm run audit:corpus

The solver's abstractions against the explicit search on many random games (plain, free items, three characters), every night: tried, compared, partial, diverged per kind; exit 1 on a divergence. --shard/--merge split and join the seeds (3.7, 3.7.1).

  • Runs: tsx tools/audit-corpus.ts
  • Usage: npm run audit:corpus [-- --seeds=500 --from=1 --max=3000 --json=<file> | --shard=i/n --total=N | --merge a.json b.json …]

npm run bench

A generated game of the given size, every tool timed on it (--prove adds the exhaustive proof, --v3 stable ids); --workers-table the proof workers' table; --matrix [--eras] the 3.3 reference table (contribute.benchmarks).

  • Runs: tsx --tsconfig tsconfig.json tools/bench.ts
  • Usage: npm run bench [-- --rooms=40 --players=3 … --prove --v3 | --workers-table | --matrix [--eras]]

npm run i18n

Translations: extract writes games/<id>/locales/<lang>.json (the base language's reference file, or a translation kept, followed and completed), status the coverage of every file (a text identical to the source fails unless i18n: { same: [paths] }), voices the lines with an id and no clip, and the clips no line claims.

  • Runs: tsx --tsconfig tsconfig.json tools/i18n.ts
  • Usage: npm run i18n -- extract [--lang <xx>] | status | voices

npm run ids

Stable ids (schema 3) for rules, topics, listeners, choices, once/nth/cycle/random blocks and script steps. Without flags, a dry run. --write: into the sources (rooms/*.ts, rules.ts, game.ts), translation keys renamed; --map: ids.migration.json and ids.paths.json, and the manual steps printed; --lines: also every say / toast / guide object, list line, hint and kind reaction (translations and voices follow it); --lines=all: plain strings too, required for a translated or voiced release.

  • Runs: tsx tools/ids.ts
  • Usage: npm run ids [-- --write] [--map] [--lines | --lines=all]

npm run ir

The game's intermediate representation (4.1.12): rooms and what stands in them, rules, topics, listeners, scripts, objectives, each with the file:line that writes it, and the hash of the trusted extensions; --json prints the whole IR, the same text for the same sources.

  • Runs: tsx tools/ir.ts
  • Usage: npm run ir [-- --game <id> --json]

npm run lint

The content lint (alias of lint:content since 4.1.0): conditions nothing can satisfy, hidden rules, red herrings, stuck hints, dead topics and options, then after a solver run the live actions never run and the rooms never reached. Exit 1 only on an error not ignored by GameDef.lint.ignore. --prove: the exhaustive search first; --static: no solver run.

  • Runs: npm run lint:content --
  • Usage: npm run lint [-- --json | --prove | --static | --max=20000]

npm run lint:content

The content lint itself (tools/lint.ts); npm run lint runs it with the same arguments, and npm run quality runs it last.

  • Runs: tsx tools/lint.ts
  • Usage: npm run lint:content [-- …]

npm run migrate

A game project moved to this release (quest-n-click migrate): an older schema gets its stable ids and the manual steps printed; since 4.3.0 it first renames the former name's web-scumm/… imports, the tsconfig's paths and the scripts' web-scumm commands (--check names them).

  • Runs: tsx tools/migrate.ts
  • Usage: npm run migrate [-- --check]

npm run playtests

Replays every session players shared (games/<id>/playtests/) on the current content and sums them up: time per room, stalls, hints, where they stopped, a heat map on the puzzle graph. A diverged session is reported (re-record or delete it); --strict (the release gate) makes it an error, exit 1, and asks for no number of sessions. Field quotas (3.7.1): --require=N sessions, --require-completed=N, --require-devices=N device families; one missed is an error. No file: exit 0.

  • Runs: tsx tools/playtests.ts
  • Usage: npm run playtests [-- --json --strict --require=N --require-completed=N --require-devices=N --dir=games/<id>/playtests --out=.cache/playtests]

npm run prove:game

The exhaustive softlock gate: the proof, the proof by chapters, then solve:reality; fails on softlocks or truncation. release-check runs it.

  • Runs: npm run solve -- --prove && npm run solve -- --prove --chapters && npm run solve:reality

npm run prove:matrix

The proof matrix (4.1.13): twelve generated three-character games, each proved in its own process under the published budgets; one row each (verdict, expected verdict, states, time, peak memory). Exit 1 on a false verdict; out of budget is unknown, never proved. Nightly only.

  • Runs: tsx tools/prove-matrix.ts
  • Usage: npm run prove:matrix [-- --only=c11,o21 --time=600 --max=10000000 --mem=4096 --json=<file> --profile=<file>]

npm run provenance

The shipped assets by licence and what changed since the reviewed lock; --lock writes games/<id>/provenance.lock.json (each file's SHA-256 and size) after a review. Exit 0 clean, 1 something to review.

  • Runs: tsx tools/provenance.ts
  • Usage: npm run provenance [-- --lock]

npm run remix

A Remix world of the current game (4.1.15): --seed prints each dimension's value and the world's hash (--json the whole WorldVariant), --preview adds validate and a solver witness on that world, --record=N solves the first N seeds of the published sample and writes each as a playtest session carrying its world (games/<id>/playtests/remix-<seed>.session.json).

  • Runs: tsx tools/remix.ts
  • Usage: npm run remix -- --seed <code> [--mode=remix --json] | --preview <code> [--max=N] | --record=N

npm run replay

Plays a session exported from the game (settings › save › "Export session", the Studio's Play tab, the DEV panel) on the real engine without a display; prints the journal, the semantic journal (4.1.11) and the final state; exit 1 where the replay stops matching the recording or its semantic journal differs from the file's.

  • Runs: tsx tools/replay.ts
  • Usage: npm run replay -- <session.json> [--upTo=N] [--json]

npm run solve

The solver: a fast witness that the game can be finished from New Game. --prove: the exhaustive proof of the reachable states, softlocks reported. Other options: --from=<checkpoint>, --max=<states>, --chapters (one bounded search per checkpoint with goals; with --prove, each chapter proved from every reachable boundary state), --goal=100% (every non-optional objective, 4.1.12; exit 2 for a game that declares none), --json, --profile, --por=sleep|stubborn, --workers=N|auto [--batch=64], --time=<s>, --mem=<MB>, --ownership=off, --dominance, --symmetry, --representation=objects, --checkpoint=<file> [--checkpoint-every=300] [--resume], --audit-abstractions. Exit 0 solved, 2 truncated (nothing proved: raise the budget), 1 otherwise (reference.error-codes); see create.solver-options.

  • Runs: tsx tools/solve.ts
  • Usage: npm run solve [-- --prove | --from=<checkpoint> | --max=<n> | --chapters | --goal=100% | --json | --profile …]

npm run solve:reality

A game that takes signals from outside, proved in every world it may meet (4.1.1): closed (through each required signal's fallback), each scenario of games/<id>/reality/scenarios/*.json, each recorded replay of games/<id>/replays/ (4.1.9), and adversarial.

  • Runs: tsx tools/solve-reality.ts
  • Usage: npm run solve:reality [-- --json]

npm run validate

Checks the game content: broken references, empty or too-long text, missing Look lines, flags never set or never read. Exit 1 for a blocking problem. --report: the content profiler (rooms, items, characters, what is thin, as Markdown). --release: also the asset provenance (games/<id>/provenance.json required, a placeholder an error unless an exception names it) and a stable id on every line of a game shipped in another language than its own or voiced. --commercial: no exception, no placeholder, no non-commercial licence, every source checkable.

  • Runs: tsx tools/validate.ts
  • Usage: npm run validate [-- --report | --release | --commercial [--errors]]

npm run verify:commercial

verify:release, then validate --commercial --errors (no exception, no placeholder, no NC/ND licence, every source checkable), then verify:dist.

  • Runs: npm run verify:release && npm run validate -- --commercial --errors && npm run verify:dist

npm run verify:dist

Every file of the built game accounted for (3.7.1): engine code, this game's locked assets (their reviewed bytes), named data, fonts, icons, the notices under licenses/; anything else, or a locked file missing, fails.

  • Runs: tsx tools/dist.ts
  • Usage: npm run verify:dist [-- --dir=dist] [--json]

npm run verify:field

verify:commercial, then strict playtests with the field quotas (--require=5 --require-completed=3 --require-devices=2): what a release tested by players needs; not a step of release-check (D12).

  • Runs: npm run verify:commercial && npm run playtests -- --strict --require=5 --require-completed=3 --require-devices=2

npm run verify:game

The game's gates: validate, solve, solve by chapters, verify:variants, i18n status, lint, playtests. A step of build:game.

  • Runs: npm run validate && npm run solve && npm run solve -- --chapters && npm run verify:variants && npm run i18n -- status && npm run lint && npm run playtests

npm run verify:release

validate --release, weight --release, i18n status, voices check --release, strict playtests: a step of release-check.

  • Runs: npm run validate -- --release && npm run weight -- --release && npm run i18n -- status && npm run voices -- check --release && npm run playtests -- --strict

npm run verify:variants

Every world of every Remix mode (4.1.15, D25): a catalogue's every logical world, a generator's published sample, each validated and solved to the ending and every objective (--prove: the exhaustive proof), a certificate per world in .cache/proofs/variants/, coverage per dimension and pair, values never chosen, dominant ones; --out writes the report a release publishes; exit 1 if one world fails. Part of verify:game.

  • Runs: tsx tools/verify-variants.ts
  • Usage: npm run verify:variants [-- --prove --max=N --sample=50 --draws=1000 --out=<file>]

npm run voices

The voice production table: status per language (lines by status draft, record, recorded, approved; a line with text switches has one row per value, <id>~form=f), export the table for the actors (CSV or JSON), import their statuses, actors and notes back into voices.json (exit 1 on an unknown line or status), check every clip measured with ffmpeg (codec, rate, length, loudness ≈ −16 LUFS, peak; exit 1 on an error; --release: an approved line needs its clip).

  • Runs: tsx tools/voices.ts
  • Usage: npm run voices -- status [--lang xx] | export --lang xx [--json] | import <file> --lang xx | check [--lang xx] [--release]

npm run weight

What a phone downloads before the first room is playable, per room and per chapter, against assetBudgets (initialKB, roomKB, chapterKB), from the built files and the asset graph. Exit 0 within budget, 1 over a budget or a file missing (--release: or a budget not set). --stems counts the scores' stems.

  • Runs: tsx tools/weight.ts
  • Usage: npm run weight [-- --release --json --stems]

Tests and code quality

The engine's own test suites, its mutation sets, the formatting, the types and the quality baseline.

npm run check

Points .cache/game at the current game, type-checks (tsc --noEmit), then runs test:node.

  • Runs: tsx tools/select-game.ts && npm run -s tsc -- --noEmit && npm run test:node

npm run doctor

An actionable report of the prerequisites, installing nothing: Node and Chromium required; Python, its modules, ffmpeg and WebKit optional. --release (4.1.8) requires Python, its modules and ffmpeg as release-check does, and Chromium as always; Firefox and WebKit stay optional.

  • Runs: tsx tools/doctor.ts
  • Usage: npm run doctor [-- --release]

npm run format

Biome's formatting, written (biome format --write .).

  • Runs: biome format --write .

npm run format:check

Biome's formatting, checked without writing (npm run quality runs it).

  • Runs: biome format .

npm run lint:code

Biome's lint of the code alone (biome lint .).

  • Runs: biome lint .

npm run quality

The engine code's gate (4.1.0): Biome formatting check and lint, knip (unused files and exports), tsconfig.json and tsconfig.strictest.json type checks, then the content lint.

  • Runs: npm run format:check && npm run lint:code && knip --no-progress && npm run -s tsc -- --noEmit && npm run -s tsc -- --noEmit -p tsconfig.strictest.json && npm run lint:content

npm run quality:baseline

The behaviour of 4.0.0 kept (4.1.0): for the demo, the reference game, the heroes example and the solver's fixtures, the witness and proof, every golden save played to its end, the public API and MCP surface, the first visit (tests/quality-baseline.json). --check compares; without it, it writes the file and the READMEs' three figures: a new test or a lighter bundle means running it (4.1.8).

  • Runs: tsx tools/quality-baseline.ts
  • Usage: npm run quality:baseline [-- --check] [--dist=dist]

npm run test

Runs test:node: the engine's and tools' Node tests and the selected game's, the heavy solver tests and the Python-bound ones excluded.

  • Runs: npm run test:node
  • Usage: npm test

npm run test:assets

The Python-backed image and asset-pipeline tests (tests/pixel.test.ts, tests/studio-assets.test.ts).

  • Runs: vitest run tests/pixel.test.ts tests/studio-assets.test.ts

npm run test:coverage

The suite under V8 coverage (heavy tests excluded), against the floors of vite.config.ts; then npx tsx tools/coverage-ratchet.ts --strict fails on a floor at least three points behind what the tests reach (CI on main and tags, release-check, 4.1.8; a warning on a pull request, 4.1.9).

  • Runs: vitest run --coverage --exclude tests/ownership.test.ts --exclude tests/audit.test.ts --exclude tests/canonical.test.ts --exclude tests/memo.test.ts --exclude tests/reference-proof.test.ts --exclude tests/solver-oracle.test.ts --exclude tests/dominance.test.ts --exclude tests/partition.test.ts --exclude tests/checkpoint-matrix.test.ts

npm run test:heavy

The CPU-bound solver tests (abstraction audits, canonical owner, memo and ownership proofs, the reference chapter's proof, dominance, partition, checkpoint matrix), one file at a time: nightly, minutes each.

  • Runs: vitest run --no-file-parallelism tests/ownership.test.ts tests/audit.test.ts tests/canonical.test.ts tests/memo.test.ts tests/reference-proof.test.ts tests/solver-oracle.test.ts tests/dominance.test.ts tests/partition.test.ts tests/checkpoint-matrix.test.ts

npm run test:mutation:core

Mutation testing of the modules a save, a session, a condition, a signal, a run or a store rest on, and of the connectors' pure parts (SDK, MIME reader, line editor, virtual disk, badge crypto and fetch: connectors-pure, which connectors still names until 4.2). Every set gated since 4.1.20, reality-store-postgres (the Postgres adapter, on a Postgres) and connectors-edge (configuration, email, badge verification, terminal guard and shell, SSH, Telnet, in 4 shards) among them. 4.1.20: --shard=i/n mutates one shard (whole files, a deterministic cut balanced on their mutants) and --merge joins a set's shards into its report, refusing a shard missing, doubled, cancelled or of other inputs; ci, the nightly, the candidate and the release run one matrix generated by npx tsx tools/mutation-shards.ts --matrix=gated|nightly. Every entry of docs/dev/mutants.json has a category, a why, a proof, an owner and a revisit version, refused from that version on (an entry due stops every set). A run that mutates reality-store-postgres (--set=all included, so release-check) needs BRIDGE_PG_URL. A report whose input hash (sources, tests, configurations, lockfile) is current is reused unless --fresh; --hash prints that hash (the CI cache key); --doc writes the named survivors' table into docs/dev/MUTANTS.md.

  • Runs: tsx tools/mutate.ts
  • Usage: npm run test:mutation:core [-- --set=core|reality|runs|remix|speedrun|connectors-pure|reality-store|connectors-edge|reality-store-postgres|all --file=… --shard=i/n --merge [--from=…] --fresh --hash --doc]

npm run test:mutation:reality

One mutation set (4.1.16): the Reality signal path. core, reality, runs, remix and speedrun are gated since 4.1.17 (--set=all, one job each in ci with full-ci, the nightly, the candidate and the release).

  • Runs: tsx tools/mutate.ts --set=reality

npm run test:mutation:remix

One mutation set (4.1.16): a world's seed, hash, compilation, application, constraints, category and Daily/Mystery tokens. core, reality, runs, remix and speedrun are gated since 4.1.17 (--set=all, one job each in ci with full-ci, the nightly, the candidate and the release).

  • Runs: tsx tools/mutate.ts --set=remix

npm run test:mutation:runs

The speedrun queue's mutation set (4.1.17): admission, quota and leaderboard ranking (bridge/src/runs.ts, runs-store.ts, runs-limiter.ts), judged by the four short Bridge run test files; gated from the start (--set=all, the release, one job of its own).

  • Runs: tsx tools/mutate.ts --set=runs

npm run test:mutation:speedrun

One mutation set (4.1.16): a run's envelope and h0, the recorder and the verifier. core, reality, runs, remix and speedrun are gated since 4.1.17 (--set=all, one job each in ci with full-ci, the nightly, the candidate and the release).

  • Runs: tsx tools/mutate.ts --set=speedrun

npm run test:node

The unit suite without the Python-bound tests (tests/pixel.test.ts, tests/studio-assets.test.ts) and the CPU-bound ones (test:heavy's files); npm run check runs it, the heavy ones run nightly.

  • Runs: vitest run --exclude tests/pixel.test.ts --exclude tests/studio-assets.test.ts --exclude tests/ownership.test.ts --exclude tests/audit.test.ts --exclude tests/canonical.test.ts --exclude tests/memo.test.ts --exclude tests/reference-proof.test.ts --exclude tests/solver-oracle.test.ts --exclude tests/dominance.test.ts --exclude tests/partition.test.ts --exclude tests/checkpoint-matrix.test.ts

npm run tsc

The TypeScript 7 compiler itself (4.1.8; the tsc bin link may belong to the tools' typescript6 package): check and quality call it.

  • Runs: node node_modules/typescript/bin/tsc
  • Usage: npm run tsc -- …

Browser tests (e2e)

Each runs the product in a real browser with Playwright (npm run doctor says which browsers are installed). A browser that cannot launch exits 3 where the script says so (0 with --allow-skip).

npm run e2e

A playthrough by touch in a phone-sized browser (E2E_BROWSER=chromium|webkit|firefox; screenshots in E2E_OUT, default /tmp/e2e); the server must already answer at the URL. A game's own games/<id>/e2e.mjs (exporting run(harness)) is used when present, otherwise the solver's path (npm run solve -- --json) is tapped through. --renderer canvas: the whole game drawn by the Canvas painter; --lang <xx>: the whole game in that language, failing on any visible English default of the engine.

  • Runs: node scripts/e2e.mjs
  • Usage: npm run e2e -- [url] [--game <id>] [--at=<checkpoint>] [--prod] [--generic] [--keyboard] [--renderer canvas] [--lang <xx>]

npm run e2e:a11y

Accessibility and storage gates in a real browser (E2E_BROWSER=chromium|webkit) on the production build of the sample game: axe on the conversation, map, slots, confirmations and every minigame; every minigame won at the keyboard; an older save upgraded. Exit 3: a check the browser cannot automate.

  • Runs: node scripts/e2e-a11y.mjs
  • Usage: npm run e2e:a11y -- <url> [--only=axe,keys,storage] [--allow-skip]

npm run e2e:canonical

canonicalJson (src/engine/core/canonical.ts, 4.1.12) on fifty edge values in Chromium, WebKit and Firefox, against the texts Node writes: one browser that writes one value differently fails; exit 3 when a browser cannot launch (0 with --allow-skip).

  • Runs: node scripts/e2e-canonical.mjs
  • Usage: npm run e2e:canonical [-- --browsers=chromium,webkit,firefox --allow-skip]

npm run e2e:music

The music director's gates (3.5) on the served production build: 30 minutes offline without drift, 100 changes without a click, real-time jitter, the theme's stems in the game; --only=reference (GAME=reference): two scores, bridges, a restore and a stop mid-transition, the decoded peak (Chromium and WebKit, 3.8).

  • Runs: node scripts/e2e-music.mjs
  • Usage: npm run e2e:music -- <url> [--only=offline|live|game|reference --live=chromium|webkit --browser=webkit]

npm run e2e:perf

Frames per second while the hero walks, the CPU slowed (default 4×, the phone stand-in); fails under --min (default 30).

  • Runs: node scripts/e2e-perf.mjs
  • Usage: npm run e2e:perf -- <url> [--renderer=canvas|dom --cpu=4 --min=30 --room=<id> --seconds=5]

npm run e2e:pwa

The PWA in a real browser (E2E_BROWSER): installed, the whole game warmed and opened offline. With --serve=dist the script serves the build itself and can publish a second one: --update (the banner, the save kept, the new worker in charge), --interrupted (the worker's fetch fails: no banner, the old one serves), --reinstall (worker and caches gone, installed again, the save kept); --allow-skip accepts WebKit's offline navigation, which Playwright cannot drive.

  • Runs: node scripts/e2e-pwa.mjs
  • Usage: npm run e2e:pwa [-- --serve=dist --update --interrupted --reinstall --allow-skip]

npm run e2e:reality

The Reality Bridge end to end on the production build of games/signals and a real reference Bridge it starts on 127.0.0.1:8787 (4.1.1): pairing, the same webhook twice, offline, back online, the signal applied once, the session replayed; since 4.1.9 two connectors and the sample chapter's replays.

  • Runs: node scripts/e2e-reality.mjs
  • Usage: npm run e2e:reality -- <url> [--browser=chromium|webkit]

npm run e2e:remix

The same seed makes the same world, the same hash and the same code wheel in Node, Chromium, WebKit and Firefox (4.1.15, ADR 0018): 50 seeds of the reference in three modes; a browser that differs fails with the seed named; exit 3 when one cannot launch (0 with --allow-skip).

  • Runs: node scripts/e2e-remix.mjs
  • Usage: npm run e2e:remix [-- --browsers=chromium,webkit,firefox --seeds=50 --allow-skip]

npm run e2e:remix-speedrun

Remix and Time Attack together (4.1.16): for each world policy of the reference (Story, Remix Fixed, Remix Random, Daily, Mystery) the world made, the solver's route recorded by the real recorder in Node, Chromium, WebKit and Firefox (the four .wsrun the same, byte for byte), verified in a new process, then by a Bridge queue's isolated worker: verdict, leaderboard key, trust; exit 3 when a browser cannot launch (0 with --allow-skip).

  • Runs: tsx scripts/e2e-remix-speedrun.ts
  • Usage: npm run e2e:remix-speedrun [-- --browsers=chromium,webkit,firefox --allow-skip]

npm run e2e:smoke

The generic playthrough of the production build (e2e.mjs --generic --prod): the solver's path replayed by touch.

  • Runs: node scripts/e2e.mjs --generic --prod

npm run e2e:speedrun

The speedrun's determinism in Chromium (also with its CPU four times slower), WebKit and Firefox (4.1.14): the generator's vectors, the reference run replayed to the same final state, IGT and final proof, a run resumed from IndexedDB after its tab closed; exit 3 when a browser cannot launch (0 with --allow-skip).

  • Runs: node scripts/e2e-speedrun.mjs
  • Usage: npm run e2e:speedrun [-- --browsers=chromium,webkit,firefox --allow-skip --bundle-only]

npm run e2e:studio

The Studio in a browser on the dev server: a stage edited without JSON by hand (the form, the diff preview, Apply validated by the server, Undo), the room file restored either way.

  • Runs: node scripts/e2e-studio.mjs

npm run e2e:studio-collab

Three people on one hosted Studio (4.2.1, CI's studio-hosted job; studio-client first): presence, a span held by another refused with the draft kept, a reviewer kept from writing, a cut caught up from the journal, the members screen, the login and "access not granted" pages.

  • Runs: tsx scripts/e2e-studio-collab.ts
  • Usage: npm run e2e:studio-collab [-- --browser=firefox|webkit]

npm run e2e:studio-hosted

The hosted Studio in a browser (4.2.1, CI's studio-hosted job; studio-client first): login, the room editor on the game's origin, its save through the Studio, the wall between the two origins, Play, logout (Firefox and WebKit over https; WebKit on macOS only).

  • Runs: tsx scripts/e2e-studio-hosted.ts
  • Usage: npm run e2e:studio-hosted [-- --browser=firefox|webkit]

npm run e2e:taps

The default verbs in the browser on the reference chapter (4.0, GAME=reference): a double tap on an exit goes through it, on a character talks to it; an item picked from the bag is given to a character, used on anything else.

  • Runs: node scripts/e2e-taps.mjs
  • Usage: npm run e2e:taps -- <url> [--browser=chromium|webkit]

npm run e2e:visual

Every room, still, against tests/visual/<game>/<room>.png (the scene only, animations frozen); more than 0.5 % of the pixels changed fails; --update writes the references. The production build must be served.

  • Runs: node scripts/e2e-visual.mjs
  • Usage: npm run e2e:visual -- <url> [--update] [--tolerance=0.005]

npm run e2e:weight

The weight budget against the bytes a browser really transfers: a first visit to the production build in Chromium, from the title to the first playable room; a request outside the predicted initial scope (npm run weight -- --json) fails.

  • Runs: node scripts/e2e-weight.mjs
  • Usage: npm run e2e:weight -- <url>

npm run e2e:worker-container

The speedrun worker in its container profile (4.1.17, Linux with Docker, CI's worker-container job): the reference run verified inside; a hostile script refused HTTP, DNS, TCP, writes outside /tmp and the host's secrets, its child process without a network, all killed by the container's own timeout; a run again. node tools/speedrun/container.mjs --image= --app= [--games=] [--allow-network] prints the profile as a worker command.

  • Runs: tsx scripts/e2e-worker-container.ts
  • Usage: npm run e2e:worker-container [-- --image=node:22-bookworm-slim]

Building, packaging and releasing

The build, the archives a release ships, the checks a candidate runs, the people's passes (Field Kit) and the release chain.

npm run audit

Runs audit:assets.

  • Runs: npm run audit:assets

npm run audit:assets

Fails the build if anything from the private origin project leaks into the public repository (names of real people, private sheet ids, the private project name), the blocked words stored as SHA-1 prefixes. build:game runs it. The licences of dist/'s files are verify:dist's job.

  • Runs: tsx tools/audit-assets.ts

npm run audit:deps

npm audit --omit=dev: the runtime dependencies' advisories (CI on main and release-check).

  • Runs: npm audit --omit=dev

npm run build

npm run check (types and the Node tests), then build:game: the static dist/ (the game, its assets, the service worker). It requires a winning witness.

  • Runs: npm run check && npm run build:game

npm run build:game

The game's gates (verify:game), the Vite bundle, verify:dist, the spoiler check, the leak audit (audit:assets): no tsc, no unit suite (CI runs those once).

  • Runs: npm run verify:game && vite build && npm run verify:dist && npm run check:spoilers && npm run audit:assets

npm run build:web

vite build alone, no gate: what a host runs after its own install (on Clever Cloud, CC_POST_BUILD_HOOK=npm run build:web).

  • Runs: vite build

npm run check:spoilers

Checks that no sealed-ending text, of any outcome, appears in clear in dist/.

  • Runs: tsx scripts/check-spoilers.ts

npm run connector-archive-e2e

The four connectors played end to end from their archive against a Bridge from its archive (4.1.19): email by webhook and by IMAP (a server of the script's own), Telnet, SSH and Open Badge; the candidate's connector-archive-e2e job. Real providers and exposure stay a person's pass (connectors-real-security).

  • Runs: node scripts/connector-archive-e2e.mjs
  • Usage: npm run connector-archive-e2e -- [--tarballs=<dir>] [--keep]

npm run external-consumer

The four archives used as a third party uses them, on Linux, macOS and Windows (4.1.18): read (names, versions, licences, binaries, exports, dependencies), a game made on the older release moved to them (since 4.3.0 as its author would: the old web-scumm package uninstalled, quest-n-click installed, migrate, verify, build, its save and a new game played to the end through quest-n-click/testing), create-quest-n-click, the Bridge (SQLite, Postgres with CONSUMER_PG_URL) and a connector scenario (a pairing code, a signed letter accepted, its duplicate, a forged one refused); no module of the checkout. The candidate runs it on its own archives.

  • Runs: node scripts/external-consumer.mjs
  • Usage: npm run external-consumer -- [--tarballs=<dir>] [--from=4.1.17] [--dir=<folder>] [--keep]

npm run field:bundle

A report and its listed evidence in one archive, logs redacted, refused on a key or a token. (contribute.field-passes)

  • Runs: tsx tools/field/field.ts bundle
  • Usage: npm run field:bundle -- --report=<report.json> --out=<bundle.tar.gz>

npm run field:carry

4.1.20 (ADR 0024): a pass carried to a new candidate when no file of its surface changed (the version included): a pending attestation the maintainer approves in a signed commit; --check-range=<a..b> refuses a range that changes the trust root and an attestation. (contribute.field-passes)

  • Runs: tsx tools/field/field.ts carry
  • Usage: npm run field:carry -- --from=<folder> --to=<folder> --pass=<id> [--check-range=<a..b>]

npm run field:check

Each pass report checked against its candidate (commit, run, digests), its evidence's SHA-256 and the leak scan. (contribute.field-passes)

  • Runs: tsx tools/field/field.ts check
  • Usage: npm run field:check -- --dir=<folder> [--require=<id,…>]

npm run field:init

The Field Kit (4.1.18): the people's passes as JSON reports at not-run, bound to that candidate run. (contribute.field-passes)

  • Runs: tsx tools/field/field.ts init
  • Usage: npm run field:init -- --release=<x.y.z> --candidate=<run id>

npm run field:report

The people's passes table generated from the reports. (contribute.field-passes)

  • Runs: tsx tools/field/field.ts report
  • Usage: npm run field:report -- --dir=<folder> --out=docs/dev/passes/<x.y.z>.md

npm run field:verify-bundle

4.1.19: a bundle's integrity, its candidate, then who made its manifest (its attestation, or the run). (contribute.field-passes)

  • Runs: tsx tools/field/field.ts verify-bundle
  • Usage: npm run field:verify-bundle -- <bundle.tar.gz> [--offline --trusted-root=<file>]

npm run fresh-install

The engine used as another project uses it (3.9): a game created from the packed template outside the repository, the tarball installed, its own assets, verify and build, then played to its end in a browser; create-quest-n-click installed from its tarball and run, the Bridge installed and serving, the connectors installed without native code and answering --help. CI runs it.

  • Runs: node scripts/fresh-install.mjs
  • Usage: npm run fresh-install [-- --dir=<folder>] [--keep]

npm run locks

The versioned locks of the Bridge, the connectors and the Bridge's image (scripts/locks/, 4.1.19), made from the root lock (every version kept); --check says whether each still fits its manifest and the root lock, without the network. scripts/pack.mjs copies them into the archives as npm-shrinkwrap.json; scripts/sbom.mjs and scripts/bridge-image.mjs make the candidate's five CycloneDX SBOMs and its image. Run it when a dependency of those packages changes, review the diff, commit it.

  • Runs: node scripts/locks.mjs
  • Usage: npm run locks [-- --check]

npm run ops:qualify

The Bridge's qualification profile (ops/qualify/, 4.1.18) built from the tarball and brought up behind HTTPS on Docker: three instances on one Postgres, two tenants, an instance killed during proposals, a stream on another, the tenants apart, backup, the topology destroyed and restored, then (4.1.20) a restore refused while the Bridges serve and --mode=replace-store with them stopped. It writes ops-qualify.json (schema 2: the profile, the commit, --candidate-run, the four archives' SHA-256, each step by its stable id with its duration and first defect; the qualify workflow). A candidate started by hand runs the full profile and its gate refuses less (tools/release/candidate.mjs qualification); a pull request's runs --short.

  • Runs: node scripts/ops-qualify.mjs
  • Usage: npm run ops:qualify -- [--tarballs=<dir>] [--image-from=<dir>] [--short] [--candidate-run=<id>] [--old-bridge=<tgz>] [--out=<dir>] [--keep]

npm run pack

The quest-n-click, create-quest-n-click, quest-n-click-bridge and quest-n-click-connectors (4.1.9) tarballs a release ships, from tracked files only: an untracked file under a shipped root refuses the pack (4.1.8); --publish-dry-run shows what npm publish would send (contribute.npm-packages).

  • Runs: node scripts/pack.mjs
  • Usage: npm run pack [-- --out=<dir> --publish-dry-run]

npm run readme-quickstart

The README's quick start as a reader copies it (4.1.19): the block under "## Start" run as written in a folder outside the repository, its archive URL pointed at the candidate's own archive (file:), npm run dev until it answers, then verify and build; the candidate's readme-quickstart job. --published, after the tag: the README's URL answers with the SHA-256 the release's SHA256SUMS lists.

  • Runs: node scripts/readme-quickstart.mjs
  • Usage: npm run readme-quickstart -- --tarballs=<dir> | --published

npm run release-check

What CI runs, in one go: doctor --release, quality, build, coverage and its strict ratchet, verify:release, prove:game, solve --audit-abstractions, the Rust cross-check (--require-rust), every mutation set (--set=all), the packages with --publish-dry-run, audit:deps and npm audit --audit-level=high.

  • Runs: npm run doctor -- --release && npm run quality && npm run build && npm run test:coverage && npx tsx tools/coverage-ratchet.ts --strict && npm run verify:release && npm run prove:game && npm run solve -- --audit-abstractions && npm run reality:xcheck -- --require-rust && npm run test:mutation:core -- --set=all && node scripts/pack.mjs --out=.cache/pack --publish-dry-run && npm run audit:deps && npm audit --audit-level=high

npm run release-check:ci

The same without the mutation sets (4.1.10; the release workflow runs them as jobs beside it), coverage reported for GitHub Actions and as JSON in .cache/test-report/vitest.json.

  • Runs: npm run doctor -- --release && npm run quality && npm run build && npm run test:coverage -- --reporter=default --reporter=github-actions --reporter=json --outputFile.json=.cache/test-report/vitest.json && npx tsx tools/coverage-ratchet.ts --strict && npm run verify:release && npm run prove:game && npm run solve -- --audit-abstractions && npm run reality:xcheck -- --require-rust && node scripts/pack.mjs --out=.cache/pack --publish-dry-run && npm run audit:deps && npm audit --audit-level=high

npm run release-check:local

What a machine can run of the release chain: quality, docs:truth, the unit suite, build:game, quality:baseline -- --check --dist.

  • Runs: npm run quality && npm run docs:truth && npm run test:node && npm run build:game && npm run quality:baseline -- --check --dist

npm run ship

The release chain as commands (4.1.8): wait for a pull request's checks (one re-run of a failed job), merge it, wait for main's CI on the merge, tag and push, follow the tag's CI and the release run, download the release and verify its sums and attestations; chain <pr> <version> does all of it. Each command writes its PID to .cache/pids/. tag requires --candidate=<run id> from 4.1.17 on: a green run of the candidate workflow (gh workflow run candidate --ref main -f sha=<sha>: release-check, the heavy suite three times on new runners, the load on SQLite and Postgres, the four runtimes, one job per gated mutation set) whose manifest names that SHA; release.yml publishes that run's .wsrun files after checking their SHA-256 (tools/release/candidate.mjs). 4.1.20: preflight-stable --candidate=<run> says what 4.2 stable still needs, check by check, changing nothing; tag --sign signs a tag with an SSH key; verify also runs gh release verify (contribute.releasing).

  • Runs: node tools/release/ship.mjs
  • Usage: npm run ship -- <checks|merge|main|tag|watch|verify|chain|preflight-stable> …

npm run upgrade-check

A game made on an older release moved to this one as a team would (4.0): created with the older package, verified, a save made in it, then this package installed, migrated, upgraded and played with that save. CI runs it.

  • Runs: node scripts/upgrade-check.mjs
  • Usage: npm run upgrade-check -- --from=<version|previous|engine tarball>

Studio, Studio Server and MCP

The Studio's static demo, the hosted Studio's server and worker, and the MCP server for an AI assistant.

npm run build:studio-demo

The Studio's static build in demo mode (STUDIO=1 VITE_STUDIO_DEMO=1 vite build), with its snapshot of the current game (studio.demo-mode).

  • Runs: tsx tools/select-game.ts && cross-env STUDIO=1 VITE_STUDIO_DEMO=1 vite build

npm run mcp

The MCP server of the current game on stdio, for an AI assistant (-s keeps npm's banner off the protocol's stdout); with --server <url>, the tools of a hosted Studio through its API, with the CLI's token (studio-server login --server <url> first, 4.2.3). studio.mcp-setup, studio.mcp-tools.

  • Runs: tsx tools/mcp/server.ts
  • Usage: npm run -s mcp [-- --server <url>]

npm run studio-apply

Applies a patch downloaded from the Studio demo ("Download patch") to the current game through the Studio core: the same writes the dev-server Studio would have made (texts, layouts, storyboard.json, notes).

  • Runs: tsx studio-server/src/ops/apply.ts
  • Usage: npm run studio-apply <patch.json>

npm run studio-client

The Studio's page the Studio Server serves (4.2.1): studio.html and its code alone, built into .cache/studio-client (STUDIO_CLIENT=1 VITE_STUDIO_HOSTED=1).

  • Runs: cross-env STUDIO_CLIENT=1 VITE_STUDIO_HOSTED=1 vite build

npm run studio-server

The Studio Server, a Studio online for a team (4.2.1; reference.cli lists its commands); backup --to-bucket [--keep n] writes the backup to the instance's bucket (4.2.3).

  • Runs: tsx studio-server/src/server/main.ts
  • Usage: npm run studio-server -- <serve|import|export|invite|worker-keys|login|token|deps|backup|restore|replay|gc> …

npm run studio-snapshot

The demo's snapshot alone: everything the Studio reads from the dev server for the current game, in one JSON file (default public/studio-demo/snapshot.json).

  • Runs: tsx tools/studio/snapshot.ts
  • Usage: npm run studio-snapshot [-- <out.json>]

npm run studio-worker

The Studio Server's processing worker, holding no secret of the platform (ADR 0028); WORKER_PUBLIC_KEY and WORKER_INSTANCE are required.

  • Runs: tsx studio-server/src/worker/main.ts

Reality Bridge, connectors and speedrun

The world outside the game (signals, connectors) and the speedrun tools.

npm run bridge

The Reality Bridge's command line for the current game: init, serve (--tenants=a,b, --store=, --trust-proxy=<list>, --tenant-header), grant, rotate, revoke, doctor, compact; since 4.1.10 migrate --from=jsonl --to=sqlite and migrate --schema=N, tenant export|delete, backup --out=, restore --from= --mode=merge|replace-store [--lock-timeout=<ms>] (4.1.20: the mode always said, the store held alone under a maintenance lease). bridge.cli.

  • Runs: tsx tools/bridge.ts
  • Usage: npm run bridge -- <init|serve|grant|rotate|revoke|doctor|compact|migrate|tenant|backup|restore> …

npm run bridge:load

The Bridge under load (4.1.10): three serve processes on one store (SQLite, or Postgres with --store or BRIDGE_STORE; an empty value is no value), the report naming the backend actually opened and the commit, checked by npx tsx tools/load-report.ts <report> --store=<kind> (4.1.17); throughput and latency p50/p95/p99, the journal and the streams checked afterwards (exit 1 on a lost, doubled or out-of-order signal). The nightly runs it on both stores.

  • Runs: tsx tools/bridge-load.ts
  • Usage: npm run bridge:load [-- --store=sqlite|postgres://… --instances=3 --players=1000 --proposals=50000 --concurrency=64 --streams=50 --out=<file>]

npm run connector

One connector of the world outside, as its own process, from the repository's sources (4.1.9): health and metrics on a local port, SIGTERM drains and exits 0; the package's command is quest-n-click-connector (bridge.connectors).

  • Runs: node connectors/bin.mjs
  • Usage: npm run connector -- <email|telnet|ssh|open-badge> --config <file.json>

npm run fuzz:connectors

The connectors' parsers under seeded mutations of their corpus, no network: every input answered, no crash, RSS before and after (4.1.9; nightly a minute per connector, CI half a minute).

  • Runs: tsx tools/fuzz-connectors.ts
  • Usage: npm run fuzz:connectors [-- --connector=email|telnet|ssh|open-badge|all --cases=10000 --seconds=60 --seed=1 --json]

npm run reality:spike

A load probe of the Bridge's cryptography (4.1.1): Ed25519 in WebCrypto (Node, Chromium, WebKit), a compact JWS verified on its exact bytes, the gzipped size of that verifier, Biscuit create, attenuate and authorise times; a Markdown table.

  • Runs: node scripts/reality-spike.mjs

npm run reality:xcheck

The Rust cross-check of the protocol (4.1.1): Biscuit's official samples through the JavaScript implementation the Bridge runs and through the Rust crate (bridge/xcheck, when cargo is there); --require-rust fails without it, as CI and release-check do.

  • Runs: tsx tools/reality-xcheck.ts
  • Usage: npm run reality:xcheck [-- --require-rust]

npm run runs:load

The speedrun leaderboard at size (4.1.17): a store of the Bridge's schema filled with --runs verified runs on one board (and as many on another), the board read --reads times; p50/p95 latency, resident memory and the SQL plan (EXPLAIN QUERY PLAN / EXPLAIN) reported with the backend and the commit; a bound passed exits 1. The candidate and the nightly run it on both stores (2 000 ms p95, 512 MB).

  • Runs: tsx tools/runs-load.ts
  • Usage: npm run runs:load [-- --store=sqlite|postgres://… --runs=100000 --players=5000 --reads=200 --max-p95-ms=<ms> --max-rss-mb=<MB> --out=<file>]

npm run speedrun:livesplit

LiveSplit (4.1.14, D23): export writes a .lss splits file from a run (its category, splits and in-game times as the personal best); serve is the autosplitter, driving LiveSplit through LiveSplit's own local WebSocket server from the events the game posts (?speedrunTool=7778).

  • Runs: node tools/speedrun/livesplit.mjs
  • Usage: npm run speedrun:livesplit -- export <run.wsrun> [--names=A,B,…] [--out=<file.lss>] | serve [--port=7778] [--origin=<url>] [--livesplit=ws://127.0.0.1:16834/livesplit]

npm run speedrun:overlay

An OBS Browser Source (4.1.14, D23): http://127.0.0.1:7777/?mode=full, compact or transparent, by Server-Sent Events, 127.0.0.1 only; the game posts its run's events with ?speedrunTool=<port>, nothing else, and only from its origin (the dev server and the preview by default).

  • Runs: node tools/speedrun/overlay.mjs
  • Usage: npm run speedrun:overlay [-- --port=7777 --origin=<url>]

npm run speedrun:verify

A speedrun's .wsrun replayed against the current game (GAME=<id>): one verdict, its code, its reason and the trust it grants (ADR 0017); exit 0 for valid and valid-unranked, 1 otherwise (inconclusive included). The CLI's quest-n-click speedrun verify and the MCP tool speedrun_verify run it. npx tsx tools/speedrun/reference-run.ts re-records the committed reference run (after npm version or a change of the reference chapter's logic).

  • Runs: tsx tools/speedrun/verify.ts
  • Usage: npm run speedrun:verify -- <run.wsrun> [--keys=<file> --json --timeout=<ms>]

Documentation, changes and CI

The tools that keep the documentation, the CHANGELOG and CI's plan true to the code.

npm run changes

The CHANGELOG and LOG fragments of a branch (changes/<slug>.md, <slug>.log.md): --check fails a pull request that changes the code without one (CI's check job); --assemble folds them into CHANGELOG.md and docs/dev/LOG.md in the order they reached main.

  • Runs: tsx tools/changes.ts
  • Usage: npm run changes -- --check [--base=origin/main] | --assemble [--date=YYYY-MM-DD]

npm run ci:plan

Which of CI's heavier jobs a change needs (4.1.9, tools/ci-plan.ts): each changed file classified by the first rule that matches, one line of JSON (a boolean per gate: node24, e2e, reference, reality, pwaFirefox, windows, secondGame, freshInstall, upgrade, auditDeps), and since 4.5.0 docs with docsPages, the wiki pages whose sources the change touches; the workflow, the plan, a shared configuration, the engine's core and a path no rule knows run everything; read-only.

  • Runs: tsx tools/ci-plan.ts
  • Usage: npm run ci:plan [-- --base=origin/main | --files=a,b | --all]

Every external link of the docs asked once (HEAD, then GET), statuses by domain, exit 1 on one not 2xx or 3xx; a person runs it before a release, CI never does (the network is not a test).

  • Runs: node scripts/check-links.mjs
  • Usage: npm run docs:links [-- --timeout=10000]

npm run docs:screenshots

The README's images from the real production bundle and the real Studio (Playwright, then Pillow): WebP files in docs/img/readme/, at most 1440 px wide, named for the release that took them; --keep-png keeps the raw captures in .cache/docs-shots.

  • Runs: node scripts/docs-screenshots.mjs
  • Usage: npm run docs:screenshots [-- --only=game|studio --keep-png]

npm run docs:truth

What the docs state against the code (4.1.16): the save and .wsrun schemas, every npm script and tools/ or scripts/ file they cite, the capabilities they call mounted, no release link newer than the version; exit 1 with each contradiction named. CI's check job.

  • Runs: tsx tools/docs-truth.ts

npm run wiki

The documentation's source in wiki/ (4.5.0, tools/wiki.ts): check validates the manifest (wiki/manifest.json: ids, URLs EN-<Domain>-<Topic> / FR-<Domaine>-<Sujet>, states, sources, questions), the pages, their links, sections and generated parts, and fails on errors only (links to planned pages are listed); stats counts the pages by state, domain, type and language; build --target wiki writes the folder published to the GitHub wiki (Home.md, a sidebar per language, the footer, the ready pages under their URL, --preview adding the drafts under a banner, the four indexes), into a folder outside the working tree (or under .cache/) that it empties only when it built it; env and generate rewrite the generated reference pages from the code; the other commands bundle, archive and publish (contribute.writing-docs).

  • Runs: tsx tools/wiki.ts
  • Usage: npm run wiki -- check | stats [--json] | coverage … | build --target wiki --out <dir> [--preview] | bundle … | archive … | publish … | pages … | env | generate [<page>…] | acceptance …

Scripts of a created project

Written by quest-n-click create (or npx create-quest-n-click) into the new project's package.json. Run them from the project's folder, after npm install: each calls the quest-n-click command of the installed engine.

npm run dev

quest-n-click dev: the game in the browser on Vite's dev server, reloading as you edit; ?edit=<room> opens the placement editor.

  • Runs: quest-n-click dev

npm run studio

quest-n-click studio: the Studio (rooms, dialogues, the puzzle graph, the mixer), opened at /__studio/.

  • Runs: quest-n-click studio

npm run assets

quest-n-click assets: game/art and game/audio into public/assets (Python 3 with Pillow).

  • Runs: quest-n-click assets

npm run verify

quest-n-click verify: validate, solve, i18n status, lint, playtests: the content checked, the solver finds the end.

  • Runs: quest-n-click verify

npm run build

quest-n-click build: assets, verify, the production build in dist/, every file of it accounted for, licences inside.

  • Runs: quest-n-click build

npm run release

quest-n-click release: build, then the release gates (validate and weight --release, i18n status, voices check --release, strict playtests, the exhaustive proof, solve-reality); --commercial: no exception, no placeholder. The template's art is a placeholder: a release refuses it until each file is replaced and reviewed (npx quest-n-click provenance --lock).

  • Runs: quest-n-click release
  • Usage: npm run release [-- --commercial]

npm run preview

quest-n-click preview: serves dist/ on 127.0.0.1.

  • Runs: quest-n-click preview

npm run quest-n-click

The engine's command itself, for every other command (doctor, validate, solve, lint, migrate…): npm run quest-n-click -- help lists them (reference.cli).

  • Runs: quest-n-click
  • Usage: npm run quest-n-click -- <command> [options]