Docs

CLI

Learn how the kizlo CLI scaffolds a project, generates the typed contract, and runs your tests.

The kizlo CLI is the tool you run alongside your project. It scaffolds Kizlo into a new or existing app, generates the contract that types your client against your server, runs local WordPress you can build against, and runs your tests against a real, disposable WordPress.

It ships with the kizlo package, so there's nothing extra to install — reach for it through your package runner:

npx kizlo <command>

The commands

A handful of commands cover the whole workflow, from first scaffold to a green test run:

CommandWhat it does
kizlo createScaffold a new project with Kizlo already wired
kizlo initSet up Kizlo in the current project
kizlo generateGenerate the contract once — for builds and CI
kizlo devWatch and regenerate the contract as you edit, and run local WordPress. Bare starts it; stop, reset
kizlo testBoot local WordPress, seed it, and run your tests — stop, reset

Every command reads kizlo.config.ts to find where your server lives and how your test environment is set up.

kizlo create

create scaffolds a new project with Kizlo already wired — reach for it when you're starting from scratch rather than adding to an app you already have. Give it a template and a directory name:

npx kizlo@latest create nextjs my-app

Both arguments are optional (kizlo create [template] [project-name]); create prompts for whichever you leave off, and the template auto-selects while nextjs is the only one. It creates my-app/, lays down a fully wired project for that template, and runs the same WordPress credential prompts as init. When it finishes, cd my-app, install dependencies, and make your first call — the Next.js quickstart walks it end to end.

kizlo init

init sets Kizlo up in an existing project. It detects your framework, package manager, src directory, and import alias, then scaffolds the server, client, route handler, and config — prompting for your WordPress credentials along the way and writing them to .env.

npx kizlo@latest init

It's the recommended path through the Installation guide, which walks the prompts in detail. Past the defaults, a few flags tune the run:

FlagEffect
--yes, -ySkip the prompts and scaffold with defaults — non-interactive, for CI
--force, -fOverwrite existing .env values and the server entry without asking
--preset <id>Force a setup preset (base, nextjs) instead of auto-detecting
--alias <prefix>Set the import alias for generated imports (e.g. @); blank for relative

Backend URL

The plugin delivers webhook events to your Kizlo backend, so init records where it's reachable. A detected framework mounts the handler at a known path (/api/kizlo for Next.js), so init asks one question — your app's public URL — and derives the backend from it, writing it to NEXT_PUBLIC_KIZLO_API_URL.

The base preset has no known mount, so init asks two URLs: your public site URL (what the plugin points back at) and the backend URL where you've mounted the handler (written to KIZLO_API_URL).

The generated client.ts routes to that backend. Same-origin deploys default to the page origin; a backend on a different origin is inlined into createKizloClient(contract, { url }). Swap that for your bundler's own public env var (e.g. import.meta.env.VITE_KIZLO_API_URL) to set it per environment.

kizlo generate

generate builds the contract once and exits. It's the command for builds and CI — run it before you typecheck or bundle, so server/generated/ exists and is current when the client imports it.

npx kizlo generate

It exits non-zero if it can't find a Kizlo server in your entry file or if generation fails, so a stale or broken contract fails the build rather than slipping through. It accepts --dir to point at a Kizlo directory other than the one in kizlo.config.ts.

Wire kizlo generate into your build script (e.g. "build": "kizlo generate && next build") so the contract is always regenerated as part of every production build.

kizlo dev

kizlo dev is the command you run while developing. It watches your server and regenerates the contract on every save — debounced, so a burst of saves collapses into one rebuild, and your client stays typed against the latest server without you thinking about it. A broken server entry doesn't stop it: it reports the error and keeps watching, so the next save can fix it. Only one watcher writes the contract at a time — a single-instance lock means a framework dev script and kizlo dev won't fight over the same file.

npx kizlo dev

When dev.local is set, kizlo dev also runs local WordPress alongside the watcher: a long-lived Docker WordPress on http://localhost:8080 you build your app against. Without it (a project pointed at your own WordPress), it runs the watcher alone. What sets local WordPress apart from the test environment:

  • Every file is editable. The whole install (core, themes, uploads, plugins) lives in .kizlo/local, bind-mounted into the container. Browse and edit it from your file manager, and changes are live.
  • Plugins come from your dev.fixtures, the same fixtures the test environment uses. Each fixture installs the plugins it needs (a wp.org slug or zip source), or bind-mounts a local { path: "..." } over wp-content/plugins so plugin source edits show up live.
  • Your data survives. It's never wiped on start; fixtures seed only on a fresh install.
CommandWhat it does
kizlo devStart local WordPress and the watcher — idempotent, never wipes
kizlo dev stopStop local WordPress, keeping everything for a fast resume
kizlo dev resetWipe the database and the .kizlo/local install, so the next kizlo dev rebuilds fresh

kizlo dev runs in the foreground and stops local WordPress when you press Ctrl+C or close the terminal — like next dev, so it never outlives your session. stop and reset are escape hatches for the rare times you need to act on it outside a kizlo dev run.

Enabling local WordPress

kizlo create and kizlo init offer to set up local WordPress during onboarding. Choose it and they provision the .kizlo/local install, write the connection to .env, and set dev.local and test.local in kizlo.config.ts for you. There's no folder to pick: the install location is fixed.

dev.local is committed, so a teammate who clones the repo gets local WordPress on their first kizlo dev (it provisions their own .kizlo/local). The install and credentials stay per-machine and gitignored.

On first install, kizlo dev mints a random admin password (not a guessable default) and prints it once so you can log into wp-admin. Save it then; it isn't stored on disk. Later runs are idempotent and leave that login untouched.

Local WordPress mints no application password — that's a test concern. To point an app at it over REST, create one in wp-admin → Users → Profile → Application Passwords.

Its MySQL is published on 127.0.0.1:3307 (override with dev.dbPort) so you can inspect or edit tables directly, beyond what wp-admin exposes. Kizlo doesn't bundle a database UI — point whatever client you already use (the mysql CLI, TablePlus, DBeaver, Sequel Ace) at host 127.0.0.1, that port, database wordpress, user wordpress, password wppass:

mysql -h127.0.0.1 -P3307 -uwordpress -pwppass wordpress

.kizlo/local is your persistent dev site: it holds the database-linked files, uploads, and edits. It sits under the gitignored .kizlo/ working dir, so don't delete it by hand. Only kizlo dev reset is meant to wipe it.

Local WordPress always enables PHP OPcache with dev-safe settings (it still picks up your live edits), which offsets most of the slower file I/O that a bind-mounted install costs on macOS/Windows.

On native Linux it also runs the container as your user, so the files WordPress writes into the folder are owned by you and stay editable — no sudo, no permission surprises. macOS/Windows handle that mapping already, so nothing extra happens there.

Seed with fixtures

If you want a code-defined starting point rather than a real-data snapshot, point dev.fixtures at the same fixtures your test environment uses. Each runs once over REST (and can drop to wp-cli for anything REST can't reach) on a fresh install — the first kizlo dev or after reset — so your dev environment mirrors your test data from versioned code:

dev: { fixtures: [postFixture()] }

On reruns of an existing install, fixtures don't re-run, so your manual edits are never clobbered.

kizlo test

With test.local set, kizlo test runs your test suite against real WordPress — no mocks. It boots a Docker-based WordPress (on http://localhost:8889, independent of your local dev WordPress), seeds it with your fixtures, then runs your project's own test command against it:

npx kizlo test

Local WordPress is idempotent here. The first run boots the containers and seeds them; later runs reuse the same install and skip the slow seed, so reruns are fast. After the suite finishes it's left running by default for that reason. Two flags change the lifecycle:

FlagEffect
--resetWipe the database and reseed from scratch before running
--teardownStop it after the suite finishes, instead of leaving it up
npx kizlo test --reset --teardown   # a clean install, torn down afterward

Under the hood test runs your package manager's test script — pnpm test, npm test, bun run test — detected from your lockfile, or whatever you set as the test command. Its output streams straight to your terminal, and kizlo test exits with your suite's exit code. Without test.local, kizlo test skips the Docker WordPress entirely and just runs that script.

See Development & Testing for how to write the suites that run against WordPress, and how fixtures seed the data they expect.

Managing test WordPress by hand

A kizlo test run leaves WordPress up by default, so you can poke at WP Admin, debug a fixture, or point another tool at the seeded credentials between runs. To get running WordPress without invoking your suite, run kizlo test once — it seeds and stays up.

These subcommands drive that same install's lifecycle by hand:

CommandWhat it does
kizlo test stopStop it, keeping the database volume for a fast resume
kizlo test resetWipe the database volume and reseed fresh

The difference between stop and reset is the database volume — stop keeps it, reset throws it away.

Configuration

kizlo.config.ts lives at your project root (.js and .mjs work too). init writes it for you; you edit it as your project grows. Define it with defineConfig for a fully typed config:

kizlo.config.ts
import { defineConfig } from "kizlo/config"

export default defineConfig({
  dir: "src/lib/kizlo",
  alias: "@",
  dev: {
    local: true,
    port: 8080,
    dbPort: 3307,
    fixtures: [],
  },
  test: {
    local: true,
    port: 8889,
    fixtures: [],
  },
})
FieldUsed byMeaning
dirallKizlo's home directory — where the CLI finds server/ and writes generated/. Defaults to src/lib/kizlo (or lib/kizlo without a src/ directory)
aliasinitImport-alias prefix for generated imports, e.g. @. Blank means relative imports
namedev, testBase name for the Docker projects (<name>-dev, <name>-test). Defaults to the sanitized package.json name, then the config dir basename
dev.localdevRun local WordPress under kizlo dev. When true, kizlo dev boots a Docker WordPress in .kizlo/local; unset or false runs the watcher alone. Set by create/init when you choose local WordPress
dev.portdevPort local WordPress listens on under kizlo dev. Defaults to 8080
dev.dbPortdevHost port the local MySQL is published on (bound to 127.0.0.1) so you can point a SQL client at the database. Defaults to 3307; change it if it clashes
dev.fixturesdevFixtures to build local WordPress from — the same fixtures the test environment uses. Each declares the plugins it needs (wp.org slugs / zip sources to install, or { path } local dirs bind-mounted live into wp-content/plugins by basename) and is seeded on a fresh install (first kizlo dev / after reset)
test.localtestRun local WordPress under kizlo test. When true, kizlo test boots a disposable Docker WordPress, seeds it, and runs your suite against it; unset or false just runs your test script. Set by create/init when you choose local WordPress
test.porttestPort test WordPress listens on. Defaults to 8889
test.fixturestestFixtures to install and seed onto test WordPress — core content plus any extension's fixtures
test.packageManagertestPackage manager for the test command. Auto-detected from your lockfile when unset
test.commandtestOverride the test command entirely, instead of <packageManager> test

On this page