# Commands

> Every pnpm script in the starter: what it runs, what it is for, and whether the free edition has it. Generated from package.json and AGENTS.md, so it is always the current set.

You are reading one page of the ZeroDirs documentation. ZeroDirs is a paid Astro + Cloudflare Workers starter for directory sites: every page is rendered to static HTML at build time, the site ships no client JavaScript outside `/search/`, and one file — `site.config.ts` — carries roughly 80% of the customisation.

Two things to hold on to before you act on anything below:

- `site.config.ts` is validated by a zod schema with ten cross-field rules. After any edit to it, run `pnpm check:config`; every problem is reported as `site.config.ts › <path>: <message>` and the whole file is checked at once.
- The repository ships its own `AGENTS.md` with twenty hard rules, and a machine-checked test suite behind them. If you are working inside a ZeroDirs project, read that file first — it overrides anything general you infer from this page.

Source: https://zerodirs.com/docs/start/commands/

---

<!-- DO NOT EDIT — generated from zerodirs/zerodirs package.json and AGENTS.md by .github/docs/generate.ts (docs-sync.yml). Edit the source, not this page. -->

Every script in `package.json`, run from the starter root. This page is generated from `package.json`, the commands table in `AGENTS.md` and the free edition's manifest, so it lists every script that exists and nothing else. `pnpm exec wrangler …` runs the pinned wrangler for anything the scripts do not cover.

## Develop and build

The ones you run every day.

| Command | Runs | What it does | Free |
| --- | --- | --- | --- |
| `pnpm dev` | `tsx scripts/dev-setup.ts && astro dev` | `scripts/dev-setup.ts` first (writes `.env` and `.dev.vars` from their examples when missing, applies the D1 migrations to the local Miniflare database), then the Astro dev server. No Pagefind index, no OG images on disk. `ZERODIRS_SKIP_DEV_SETUP=1` skips the setup | yes |
| `pnpm build` | `astro build` | Full static build + sitemap + Pagefind + budget + SEO report | yes |
| `pnpm preview` | `astro preview` | Serve the built output | yes |
| `pnpm deploy` | `astro build && wrangler deploy` | `astro build && wrangler deploy` | no |

## Check and test

`pnpm check` is the gate before a push: the GitHub Actions deploy runs it again and refuses to ship what it fails.

| Command | Runs | What it does | Free |
| --- | --- | --- | --- |
| `pnpm check` | `astro check && tsc -p tests/workers/tsconfig.json && tsx scripts/check-config.ts` | `astro check`, then `tests/workers/` types, then `pnpm check:config`. The gate | yes |
| `pnpm check:config` | `tsx scripts/check-config.ts` | Validate `site.config.ts` alone and print the file-count estimate | yes |
| `pnpm test` | `vitest run -c vitest.config.ts` | Vitest unit suite | yes |
| `pnpm test:e2e` | `playwright test` | Playwright smoke tests against the built site (builds first only when `dist/` is stale; migrates the local D1 first) | yes |
| `pnpm test:workers` | `vitest run -c vitest.workers.config.ts` | Vitest inside workerd, against the real `DB` / `MEDIA` bindings (`tests/workers/`) | no |
| `pnpm lhci` | `lhci autorun --config=lighthouserc.cjs` | Lighthouse CI against `lighthouserc.cjs` | no |

## Content

Listings as Markdown files, the sample datasets, and the site's own share image.

| Command | Runs | What it does | Free |
| --- | --- | --- | --- |
| `pnpm content:reset` | `tsx scripts/content.ts reset` | Replace the demo listings with one generated example per configured category (`--yes`) | yes |
| `pnpm content:use` | `tsx scripts/content.ts use` | Load a dataset from `seed/datasets/` back into `src/content/` (`--yes`) | yes |
| `pnpm content:check` | `tsx scripts/content.ts check` | Fail when `src/content/` has drifted from a dataset | yes |
| `pnpm listings:import` | `tsx scripts/import.ts` | CSV/JSON → `src/content/listings/*.md` (`--dry-run`, `--out`, `--map`) | no |
| `pnpm listings:export` | `tsx scripts/export.ts` | D1 → `seed/.out/export/*.md`, the same format `listings:import` reads | no |
| `pnpm og:default` | `tsx scripts/og-default.ts` | Regenerate `public/og-default.png` from `site.config.ts` (`--check` to verify) | no |

## Database

The D1 database behind submissions, accounts and the review queue. Local is the Miniflare copy under `.wrangler/`; remote is the real one.

| Command | Runs | What it does | Free |
| --- | --- | --- | --- |
| `pnpm db:generate` | `drizzle-kit generate` | drizzle-kit: `src/db/schema.ts` → the next migration. First one: `--name init` | no |
| `pnpm db:migrate:local` | `wrangler d1 migrations apply DB --local` | Apply pending migrations to the Miniflare database under `.wrangler/` | no |
| `pnpm db:migrate:remote` | `wrangler d1 migrations apply DB --remote` | Apply them to the real D1 database | no |
| `pnpm db:seed:local` | `tsx scripts/seed.ts --local` | Markdown listings → local D1 (`--dataset`, `--from`, `--with-favicons`, `--with-images`) | no |
| `pnpm db:seed:remote` | `tsx scripts/seed.ts --remote` | The same, against the real database. Run it once: only D1 enforces the size limits | no |

## Cloudflare

Going live, and keeping the deployed Worker in step with the repository.

| Command | Runs | What it does | Free |
| --- | --- | --- | --- |
| `pnpm cf:typegen` | `wrangler types` | Regenerate `worker-configuration.d.ts` after editing `wrangler.jsonc` | no |
| `pnpm cf:secrets` | `tsx scripts/put-secrets.ts` | Show which secrets the Worker has and which ones `site.config.ts` needs, then prompt for each key in `.dev.vars.example` (or only the names given: `pnpm cf:secrets STRIPE_WEBHOOK_SECRET`) and pipe it to `wrangler secret put`. Checks the shape before uploading; never echoes or stores a value | no |
| `pnpm cf:setup` | `tsx scripts/cloudflare-setup.ts` | From this checkout to a running site on Cloudflare: names the Worker after the site, binds `site.url` as a custom domain (`--workers-dev` for `*.workers.dev`), creates the D1 database and R2 bucket, leaves out the `send_email` binding until the account has a sending domain, migrates the production database, builds, deploys, and sets `ADMIN_SECRET` / `TOKEN_SECRET`. Idempotent; `--dry-run` prints the plan; `--yes` skips the confirmation | no |

## Agent skills

The skills in `.agents/skills/` are copied to `.claude/skills/` so Claude Code and Codex both find them.

| Command | Runs | What it does | Free |
| --- | --- | --- | --- |
| `pnpm skills:sync` | `tsx scripts/sync-skills.ts` | Copy `.agents/skills/` → `.claude/skills/` | no |
| `pnpm postinstall` | `tsx scripts/sync-skills.ts \|\| true` | Runs `pnpm skills:sync` after `pnpm install`; a failure is ignored. Not meant to be run by hand. | no |

## In the free edition

The free edition keeps `pnpm dev`, `pnpm build`, `pnpm preview`, `pnpm check`, `pnpm check:config`, `pnpm test`, `pnpm test:e2e`, `pnpm content:reset`, `pnpm content:use`, `pnpm content:check`. Everything else runs a script the free edition deletes along with its subject: there is no database, no Worker, no Cloudflare resources to create and no skills to copy.

Two of them run something shorter there: `pnpm dev` is `astro dev`; `pnpm check` is `astro check && tsx scripts/check-config.ts`.

## Related

- [Project structure](https://zerodirs.com/docs/start/project-structure/): where each script's subject lives.
- [Quickstart](https://zerodirs.com/docs/start/quickstart/): the first three of these, in order.
- [Go live](https://zerodirs.com/docs/deploy/go-live/): what `pnpm cf:setup` does, step by step.
