Skip to content

CLI · 12 commands

CLI reference

void is a Bun CLI. It audits a running website in real Chromium on a throttled mobile and a desktop profile, lints source without a browser, extracts design systems from sites you admire, and explains every rule it can report.

From a checkout$ bun packages/cli/src/index.ts audit http://localhost:3000
Linked$ cd packages/cli && bun link && void audit http://localhost:3000

Commands

12 commands

void audit [url]
Run every page category: perf, smooth, a11y, seo, geo, design, plus lint when a source directory is configured.
exit 0 · 1 · 2 · 3 · 4
void perf [url]
Core Web Vitals and bytes from web-vitals and resource timing, median of --runs, optional Lighthouse.
exit 0 · 1 · 2 · 3 · 4
void smooth [url]
Scripted scroll and interactions: dropped frames, long animation frames, INP, shifts after input, reduced motion, idle rAF, WebGL contexts.
exit 0 · 1 · 2 · 3 · 4
void a11y [url]
axe-core WCAG 2.2 AA, keyboard focus visibility, target size on mobile, 320px reflow.
exit 0 · 1 · 2 · 3 · 4
void seo [url]
Status codes, head placement per user agent, canonical, titles, h1, links, Open Graph, icons, robots.txt, sitemap.
exit 0 · 1 · 2 · 3 · 4
void geo [url]
Raw HTML versus rendered content for AI crawlers, JSON-LD, AI robots policy, llms.txt, Markdown mirrors.
exit 0 · 1 · 2 · 3 · 4
void design [url]
Fonts, type scale, line length, palette and accent sprawl, spacing grid, radius sprawl, generated-UI tells, overflow.
exit 0 · 1 · 2 · 3 · 4
void lint [dir]
Static source checks, no browser. Defaults to the config's src, else the current directory.
exit 0 · 1 · 2
void extract <url>
Measure a site's design DNA and write dna.json, brief.md and direction.css.
exit 0 · 2 · 4
void rules [id|category]
List all rules, a category or prefix, or explain one rule: why, fix, example, refs.
exit 0 · 2 unknown id
void report <report.json>
Re-render a saved JSON report as pretty, md or json.
exit 0 if it passed, else 1
void doctor
Check Bun, Chromium launch, playwright-core, axe-core, web-vitals, optional Lighthouse and the config file.
exit 0 · 2 · 4

Audit flags

audit, perf, smooth, a11y, seo, geo, design

--only
perf,smooth,a11y,seo,geo,design,lint
Categories to run. The shorthand commands set this.
--profile
mobile | desktop | both
Default both. Mobile is 412×823 at 2.625x with 4× CPU and 150 ms RTT; desktop is 1350×940 unthrottled.
--routes
/,/pricing
Paths resolved against the URL. Default: the URL's own path.
--runs
1–20 (3)
Perf runs per profile; the median is reported.
--start
"bun run start -p 3100"
Start a server, wait for a 2xx on readyPath, audit, stop the process group. Requires --port.
--port
1–65535
Port of the started server; the URL becomes http://localhost:<port>.
--site-url
https://example.com
Production origin, so canonical and og:url checks pass while auditing localhost.
--src
./src
Also lint this directory as part of the audit.
--kind
marketing | app
Selects the default budget set (JS 170 KB vs 300 KB).
--lighthouse
boolean
Also run Lighthouse 13 (optional peer dependency).
--screenshots
dir
Save fold and full-page screenshots.
--format, -f
pretty | md | json
md is compact and grouped by rule, for agents.
--json
boolean
JSON only on stdout; progress goes to stderr.
--out, -o
report.json | report.md
Also write the report to a file; format from the extension.
--fail-on
error | warn | info | never
Exit 1 threshold, default error. Budgets also fail unless never.
--max-per-rule
number
Occurrences listed per rule (md default 5).
--config, -c
path
Default: void.config.ts, .mts, .js or .mjs in the current directory.
--quiet, -q
boolean
No progress output.
--no-color
boolean
Plain output. NO_COLOR is honoured too.

Exit codes

stable across versions

  1. 0Pass: no findings at or above --fail-on and every budget holds.
  2. 1Findings at or above --fail-on, or a budget exceeded.
  3. 2Usage or config error: a bad flag, an invalid void.config.ts, an unknown rule id.
  4. 3Target unreachable, or the --start server exited or timed out. The last server log lines are printed.
  5. 4Chromium failed to launch. Run void doctor, install Chromium or set VOID_CHROMIUM.

Output

real output, void 0.1.0

A terminal gets the pretty report: scores, then findings grouped by rule with the file and line, a one-line fix and the docs link. Agents should pass --format md, which groups the same data compactly, or --json to parse it.

~/demo — void lint srcexit 1
$ void lint src  void 0.1.0  62 ms  Source lint      23  ━━━━━━━────────────────────────  ✖ 2 ▲ 9 ● 2  SOURCE LINT  23  ────────────────────────   Root layout metadata has no `metadataBase`  lint/missing-metadata-base    Root layout exports no metadata at all (no `metadataBase`, no default title/description).    app/layout.tsx:1:1     Set `metadataBase: new URL(site.url)` in the root layout, with the production origin.    https://void.parthkapoor.me/rules/lint/missing-metadata-base   `utility-[--var]` is Tailwind v3 syntax; v4 needs `utility-(--var)`  lint/tw-v3-arbitrary-var    `duration-[--duration-fast]` is Tailwind v3 syntax; v4 emits the literal `--duration-fast` as…    app/page.tsx:9:43     Replace `duration-[--duration-fast]` with `duration-(--duration-fast)`.    https://void.parthkapoor.me/rules/lint/tw-v3-arbitrary-var   Google Fonts loaded via `<link>` / `@import`  lint/google-fonts-link    <link> to https://fonts.googleapis.com/css2?family=Inter is render-blocking and adds a third-…    app/layout.tsx:3:33     Use `next/font/google` (self-hosted at build time, with metric-matched fallbacks) or self-h…    https://void.parthkapoor.me/rules/lint/google-fonts-link  … 10 more findings  FAIL  ✖ 2 errors   ▲ 9 warnings   ● 2 info  fail-on error  Details: void rules <id>  ·  Agent-friendly: --format md
~/demo — void rulesexit 0
$ void rules lint/tw-v3-arbitrary-var  `utility-[--var]` is Tailwind v3 syntax; v4 needs `utility-(--var)`  lint/tw-v3-arbitrary-var  ✖ error  Source lint  Why  In v4 square brackets are literal arbitrary values, so `duration-[--duration-fast]`  emits `transition-duration: --duration-fast` — invalid CSS that the browser drops. The  transition, color or size silently falls back to the default.  Fix  Replace `[--name]` with `(--name)` (parentheses are v4's CSS-variable shorthand), or  write `[var(--name)]`.  Example  ```tsx  // before (v3)  <div className="duration-[--duration-fast] bg-[--brand]" />  // after (v4)  <div className="duration-(--duration-fast) bg-(--brand)" />  ```  Docs  https://void.parthkapoor.me/rules/lint/tw-v3-arbitrary-var        https://tailwindcss.com/docs/upgrade-guide#variables-in-arbitrary-values        https://tailwindcss.com/docs/adding-custom-styles#using-arbitrary-values

JSON report

The shape of --json, abridged, with illustrative values. Every finding links to its rule page, for example perf/lcp-image-lazy.

json
{
  "schemaVersion": 1,
  "tool": { "name": "void", "version": "0.1.0" },
  "scores": { "perf": 97, "smooth": 100, "a11y": 100, "seo": 100, "geo": 96, "design": 94 },
  "overall": 98,
  "metrics": [{ "name": "lcp", "value": 1180, "unit": "ms", "profile": "mobile", "budget": 2500 }],
  "findings": [{
    "id": "perf/lcp-image-lazy",
    "category": "perf",
    "severity": "error",
    "message": "LCP image /hero.avif has loading=lazy",
    "source": "web-vitals",
    "target": { "url": "http://localhost:3100/", "profile": "mobile", "selector": "main img" },
    "fix": { "hint": "Remove loading=\"lazy\" from the LCP image." },
    "docs": "https://void.parthkapoor.me/rules/perf/lcp-image-lazy"
  }],
  "summary": { "errors": 1, "warnings": 0, "infos": 2, "pass": false }
}

void.config.ts

auto-detected in the working directory

urlstring
Base URL when none is passed.
routesstring[]
Paths to audit.
start{ command, port, cwd?, readyPath?, timeoutMs? }
Server to spawn. readyPath defaults to /, timeout to 120 s.
srcstring
Source directory for lint; also added to audit.
profiles("mobile" | "desktop")[]
Default both.
runs1–20
Perf runs per profile, default 3.
kind"marketing" | "app"
Default budget set.
budgets{ mobile?: Budgets, desktop?: Budgets }
lcp, inp, tbt, ttfb (ms), cls, jsKb, cssKb, fontKb, imageKb, htmlKb, requests, droppedFramePct. Merged over the defaults.
rulesRecord<ruleId, "error" | "warn" | "info" | "off">
Severity overrides per rule id.
failOn"error" | "warn" | "info" | "never"
Default error.
lighthouseboolean
Default false.
onlyCategory[]
Default: all page categories.
ts
/** @type {import('@void/cli/config').VoidConfig} */
const config = {
  kind: "marketing",
  src: "src",
  start: { command: "bun run start -p 3100", port: 3100, readyPath: "/" },
  routes: ["/", "/pricing", "/this-page-does-not-exist"],
  profiles: ["mobile", "desktop"],
  runs: 3,
  budgets: { mobile: { lcp: 2000, jsKb: 150 } },
  rules: { "geo/answer-first": "off" },
  failOn: "error",
};

export default config;

Default budgets

override per key in void.config.ts

ProfileLCPCLSINPTBTTTFBJSCSSFontsHTML
marketing · mobile2.5 s0.1200 ms200 ms800 ms170 KB25 KB120 KB30 KB
marketing · desktop1.5 s0.1200 ms200 ms800 ms170 KB25 KB120 KB30 KB
app · mobile2.5 s0.1200 ms300 ms800 ms300 KB40 KB120 KB60 KB
app · desktop1.5 s0.1200 ms200 ms800 ms300 KB40 KB120 KB60 KB

The full reference agents load is audit / commands.