CLI
Every kiri command, with its flags and real example output — build, export, watch, serve, run, mcp, create, install, cache, phpinfo.
Overview
kiri comes from the @kirigami/cli package. Install it as a dev dependency and run it as npx kiri <command>, or through the package.json scripts every official template ships (npm run serve, npm run build, …):
npm install --save-dev @kirigami/cli
Every command accepts --help, and so does bare kiri:
$ npx kiri --help
kiri — Kirigami CLI
USAGE
kiri <command> [options]
COMMANDS
build Compile project for development
export Compile and export project for production
watch Start dev-mode with hot-reload
serve Dev-mode with hot-reload, served locally in a browser
run Run a PHP command script from the scripts/ folder
mcp Serve this project over MCP (stdio) for an AI agent
create Create a new project from an official template
install Install a plugin and print its kirigami.yaml options
cache Purge the local caches (.node.db / .cache.db / .cookie.txt)
phpinfo Print phpinfo() from the embedded PHP-WASM runtime
GLOBAL OPTIONS
--help, -h Show help
--version, -v Show version
kiri --version also prints the bundled PHP version alongside the CLI's own — useful when filing an issue, since @kirigami/php-wasm's own version number encodes the PHP build it ships.
kiri build
Compiles the project for development: every entry under tasks: runs once, in order, with no minification and no export step. If a top-level prepros: block is set, every PHP page (plus sitemap.xml / robots.txt) renders first — page-source .html files land right next to their _index.php, so a plain static server can preview src/ directly.
kiri build
This is the real output from building this very site — the plugin loader banner, every page rendered, then the sass and esbuild tasks:
kiri — Build Project
› Project : Kirigami
› Base URL : https://example.com
› Root : /path/to/project/src
Plugins:
› ✔ @kirigami/plugin-highlight v0.1.7
› ✔ @kirigami/plugin-extlink v0.1.3
› ✔ @kirigami/plugin-embed v0.1.4
Tasks:
› PREPROS: render-all ✔
src/index.html
src/about/index.html
…
src/sitemap.xml
src/robots.txt
› SASS: css-core ✔
(this site's compiled stylesheet + source map)
› ESBUILD: js-core ✔
(this site's compiled bundle + source map)
✔ Build finished!
A task with no build step for the current change (nothing to re-run) is skipped, unless it sets force: true. kiri build fires the before-build trigger first — a scripts: entry can hook into that without any manual kiri run call. For production output (minified, exported to dist/), use kiri export instead.
kiri export
The production build: forces every task to run (even ones kiri build would have skipped), copies the result into export:path (default dist/), and stamps the banner (kirigami:banner, or the bundled ASCII one) into every exported text file with its ### ### tokens filled in.
kiri export
Trigger order is before-export → before-build → the build itself → after-export (see kiri run for what a trigger actually runs). That means anything a project wires to before-build also runs during export — no need to duplicate a script under both triggers.
Point CI at this command; kiribuild (the reusable GitHub Action) runs exactly kiri export under the hood. See Tutorial → Deploy for the full GitHub Pages setup.
kiri watch
Dev mode without a server: watches every file that feeds an esbuild / sass / prepros task and rebuilds on change — 150ms debounced, batched per task so five saves in the same second trigger one rebuild, not five. node_modules/, .git/ and dist/ are always ignored. Ctrl+C stops cleanly.
kiri watch
It only writes files to disk — no server, no browser reload. For that, use kiri serve, which does everything watch does plus the two things it's missing.
kiri serve
Everything kiri watch does, plus: serves kirigami.root over plain HTTP and reloads any open browser tab once a rebuild batch finishes (Server-Sent Events — no WebSocket library, no live-reload framework, just node:http + node:fs). A sass-only change hot-swaps the stylesheet in place instead of reloading the page, so scroll position and form state survive.
| Flag | Default | Purpose |
|---|---|---|
--port, -p <n> |
4321 |
Port to listen on. |
--host <host> |
127.0.0.1 |
Host to bind to. |
kiri serve
kiri serve --port 5000
If the port is already taken, kiri serve fails with a direct fix instead of a raw Node stack trace:
Error: Port 4321 on 127.0.0.1 is already in use — try a different one with --port 4322.
kiri run
Runs scripts/<name>.php inside the same PHP-WASM runtime and class library every page renders with — PREPROS::$config->data is populated, every autoloaded class (FS, IMG, CURL, …) is available — but the script produces no page. Use it for anything one-off or triggered that isn't itself content: image conversion, a data migration, a deploy notification.
kiri run <name> [args...]
Every word after <name> becomes a $argv entry inside the PHP script:
kiri run deploy production --force
// scripts/deploy.php
// $argv === ['production', '--force']
A script isn't limited to manual invocation — declare it under scripts: in kirigami.yaml with a trigger, and kiri build / kiri export fire it automatically:
scripts:
deploy:
trigger: after-export
Full reference: Config → scripts.
kiri create
Scaffolds a new project from an official template-* repo (the php-kirigami org). Running it with no arguments, in an interactive terminal, starts a wizard (template, directory, name / description / author / base URL, --email / --repo); every answer is written into package.json and kirigami.yaml for you.
| Flag | Purpose |
|---|---|
--list, -l |
List available templates (cached 1h). |
--name <name> |
Project name (package.json name + kirigami.yaml project). |
--description <s> |
Project description. |
--author <name> |
Author. |
--email <email> |
Author email (kirigami.yaml email). |
--baseurl <url> |
Site base URL (kirigami.yaml baseurl). |
--repo <url> |
Git repo URL (kirigami.yaml repo; default: derived from --baseurl). |
--yes, -y |
Non-interactive: take defaults, ask nothing. |
--no-git |
Don't run git init / the first commit. |
--no-install |
Don't run npm install afterwards. |
kiri create # interactive wizard
kiri create --list
kiri create blog my-blog
Extraction never overwrites: existing files are kept as-is, and package.json is deep-merged (your own dependencies win over the template's). A missing package.json / banner.txt gets a starter one — the banner keeps its ### ### tokens on disk, filled in on every build / export afterward. Unless --no-git / --no-install are passed, create also runs git init plus an initial commit (skipped if already inside a repo) and npm install. Every template ships its own CLAUDE.md, so a freshly created project is Claude Code-ready immediately.
kiri install
Installs a Kirigami plugin and prints the plugins: block to paste into kirigami.yaml — it never edits the file itself.
kiri install <plugin...>
kiri install --save <plugin...> # regular dependency, not devDependency
A bare name expands against the @kirigami/plugin-* / kirigami-plugin-* naming conventions and is checked against npm; a full package name (anyone's, not just @kirigami/*) is used as-is:
kiri install highlight # → @kirigami/plugin-highlight
kiri install @kirigami/plugin-highlight # same thing, spelled out
kiri install highlight @kirigami/plugin-embed # more than one at once
Already installed? install checks npm for a newer version and updates it. Either way it ends by printing the options block, built straight from the plugin's own kirigami.optionsSchema — so the flags you see match that exact version:
plugins:
- name: "@kirigami/plugin-highlight"
options:
languages: [js, ts, php, bash, yaml, json, html, css]
theme: auto
kiri cache
Kirigami caches two SQLite stores at the project root — .cache.db (CACHE, used by SCRAPER and the image pipeline) and .node.db (@kirigami/sdk's Cache, for plugin-side caching) — plus a .cookie.txt jar (CURL). All three are meant to be .gitignored locally but committed in CI-built projects, so a fresh checkout never needs to re-crawl or re-encode anything a previous run already resolved (see <extlink> for a real example).
kiri cache purge # delete .node.db, .cache.db, .cookie.txt
kiri cache purge meta_* # keep the files, delete only matching keys
kiri cache purge "colors_*"
<mask> is a glob over the key namespace, not a filename — quote it if your shell would otherwise expand the *. Acts on the current directory.
kiri mcp
Starts the Kirigami MCP server on stdio, bound to the current directory, so an AI assistant can build, validate, export and scaffold the project. It is meant to be launched by an MCP client, not typed by hand:
npx kiri mcp
kiri phpinfo
Prints phpinfo() from the embedded PHP-WASM runtime — the exact PHP version and extension set every page actually renders with, not whatever PHP (if any) happens to be on the host machine.
| Flag | Output |
|---|---|
| (none) | HTML, exactly like a browser-rendered phpinfo(). |
--md, -m |
Markdown table. |
--json, -j |
JSON. |
kiri phpinfo
kiri phpinfo --json > phpinfo.json
kiri phpinfo --md > phpinfo.md
Writes to stdout either way — redirect it to a file, or pipe it straight into a diff when chasing a version-specific bug.
Typical workflow
A normal session runs kiri serve once, in one terminal, and leaves it open through the whole editing session; kiri export is the last step, right before a push, to sanity-check the actual production output in dist/:
npx kiri serve --port 4321
# …edit src/**, save, the open tab reloads on its own…
npx kiri export
kiribuild runs kiri export again in CI on push — so a clean local kiri export is a strong signal the deploy will succeed too. If a build ever behaves differently locally than in CI, kiri cache purge rules out a stale local cache before anything else.