CLI
Every kiri command, with its flags and real example output — build, export, watch, serve, run, create, install, cache, phpinfo.
- Overview
- kiri build
- kiri export
- kiri watch
- kiri serve
- kiri run
- kiri create
- kiri install
- kiri cache
- kiri phpinfo
- Typical workflow
Overview
kiri is provided by the @kirigami/kirigami dev dependency — run it as npx kiri <command>, or add scripts to package.json (every official template does: npm run dev, npm run build, …). 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
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
Type `kiri <command> --help` for detailed help on a command.
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://php-kirigami.github.io
› 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 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.