reference
CLI reference
runek init / add / list / info / validate / check-world / preview / skill: pull editable component source into your project and check your worlds.
⌖ Explore the library in 3D → · View as Markdown
The runek CLI copies component source from the registry into your project — you own and edit the files. Run it with npx @runek/cli <command>.
init
npx @runek/cli init [options]
Writes runek.config.json and creates the install directory. Run it once per project.
add
npx @runek/cli add <name...> [options]
Pulls one or more components. For each, it:
- resolves registry dependencies recursively (e.g.
housealso pulls its walls, floor, roof, door, and window as source), - writes the source into your install directory verbatim (components import
@runek/corefrom npm, so there’s no import to rewrite), - installs the npm dependencies, including
@runek/core, with your package manager (auto-detected from the lockfile).
npx @runek/cli add player terrain bookshelf
list
npx @runek/cli list [options]
Prints the catalog, grouped by category. --json prints the registry index items instead (name, title, type, category, description, files).
info
npx @runek/cli info <name> [options]
Shows one component without installing it: its description, the add command, the npm and registry dependencies it pulls, every prop with its type, default, and doc (code-only props such as callbacks are marked), the nested types it takes (like Person’s PersonGarment), and a minimal world node and JSX line to start from. <name> is the registry name (bookshelf) or the world-data type (Bookshelf), in any case; a near miss suggests the closest name. It reads only the registry, so it works before init.
npx @runek/cli info bench
# Bench (bench, interiors)
# ...
# Props
# length number = 1.6
# Length, in units.
# back boolean = true
# Include a backrest.
# color color = palette.wood
# World node
# {"type":"Bench","props":{"position":[0,0,0]}}
--json prints the same card as one object: name, title, kind, category, description, doc, add, dependencies, registryDependencies, props (name, type, optional, default, palette, computed, doc, codeOnly), types, and example (node, jsx).
validate
npx @runek/cli validate <world.json> [options]
Checks a world file against the components’ prop schema (served at /r/props.json): unknown world fields, node fields and component types, props a component doesn’t have, and values of the wrong type or outside an enum, each with a “did you mean” when a name is close. Invalid JSON is reported with its line and column. Components that are not in the registry but have a source file in your install directory (your own MyThing.tsx) are accepted as is.
npx @runek/cli validate public/world.json
# error avatar Avatar is "first", "third" or "overhead".
# warning nodes[1].props.colr Bookshelf has no prop "colr". Did you mean "color"?
# warning nodes[2].type Unknown component "Benchh". Did you mean "Bench"?
Errors (the world can’t load) exit non-zero; warnings (it renders, maybe not as meant) only with --strict. --json prints { valid, errors, warnings, issues: [{ path, pathString, severity, message }] }, where path is the JSON path as an array (["nodes", 1, "props", "colr"]). It uses validateWorld from your installed @runek/core (0.14 or newer). Editors can check the same rules as you type through the world JSON Schema: see Worlds as data.
check-world
npx @runek/cli check-world <world.json> [options]
Finds nodes that are buried (below the terrain) or floating (above the highest surface), and open water that stands above its shore. It reads the ground from your own component source (the surfaces/*.ts modules that ship with terrain, dock, floor, slab, shore, and lake) and the ground math from your installed @runek/core, so it checks exactly what your app renders. Only components marked groundSitting are checked, and anchored nodes pass by construction.
npx @runek/cli check-world public/world.json
# buried Tent [70] at (-28, 0.42, 110) is 1.8 below the ground (2.22); set position[1] to 2.216, or anchor it
--fix writes each suggested Y into the file; --strict exits non-zero when anything is found, so an app can run it in CI. --json prints { surfaces, issues } (plus fixed with --fix), each issue with its kind, ground-index path, type, id, at, ground, and suggested fix. Loading .ts surfaces needs Node 22.18 or newer. See Placing things on uneven ground.
preview
npx @runek/cli preview <world.json> [--open]
Prints a workshop link that carries the whole world in its URL, the same as the workshop’s own share links, so you (or whoever you send it to) can see a world file rendered without running an app. --open opens it in your browser. Very large worlds make long links; past 8,000 characters it warns. With a registry served by a Runek docs site (a URL ending in /r), the link points at that site’s workshop.
skill
npx @runek/cli skill [--out <path>] [--overwrite]
Installs the Runek agent skill (served at /r/agents/SKILL.md): how to add components, write and check world files, and place things, for coding agents working in your project. It writes .claude/skills/runek/SKILL.md by default; --out puts it anywhere else, such as another agent’s rules folder. An existing file is kept unless you pass --overwrite. See Runek for agents.
Options
| Option | Commands | Description |
|---|---|---|
--registry <url|path> |
all | Registry base. Defaults to the configured one (https://runek.nullorder.org/r). A local path works for development. |
--core <version|latest> |
add, list, info, validate, skill | Read the registry for this @runek/core instead of the project’s (see Versions). latest is the newest release. |
--dir <path> |
init, add, validate, check-world | Install directory (default src/runek). |
--overwrite |
add, skill | Replace files that already exist (otherwise they’re skipped). |
--no-install |
add | Print the dependency install command instead of running it. |
--force |
init | Overwrite an existing runek.config.json. |
--strict |
validate, check-world | Exit non-zero on any issue (validate: warnings too). |
--fix |
check-world | Write the suggested Y values into the world file. |
--tolerance <units> |
check-world | Allowed gap before a node is flagged (default 0.08). |
--json |
list, info, validate, check-world | Machine-readable JSON on stdout, for scripts and agents. |
--out <path> |
skill | Where to write the skill (default .claude/skills/runek/SKILL.md). |
--open |
preview | Open the link in your browser. |
-v, --version |
— | Print the CLI version (also runek version): a pixel banner in a terminal, the bare version when piped. |
-h, --help |
— | Show help. |
runek.config.json
{
"$schema": "https://runek.nullorder.org/registry/config-schema.json",
"registry": "https://runek.nullorder.org/r",
"dir": "src/runek"
}
- registry — where components are fetched from: the base URL or path, without a version (the CLI adds it).
- dir — where component source is written. The runtime comes from the
@runek/corenpm package, not copied here.
Registry index
The default registry is served at https://runek.nullorder.org/r. Its index lists every component (name, type, description); each entry resolves to a self-contained manifest under components/<name>.json. Point --registry at it (the default) or at a local copy for development.
Versions
Component source is written against a particular @runek/core, so the registry keeps one snapshot per release: /r/0.15.0/registry.json, /r/0.15.0/components/bookshelf.json, and so on. /r/versions.json lists them, and /r itself serves the latest release.
Every command that reads the registry picks the snapshot that matches your project’s @runek/core, and says which on stderr:
$ npx @runek/cli add bookshelf
registry 0.15.0 (matches installed @runek/core 0.15.0)
- It reads the installed
node_modules/@runek/core, else the version range in yourpackage.json. With neither (a new project), it uses the latest release, andaddinstalls the matching core. - With no exact snapshot it takes the newest older one in the same release line (
0.15.3→0.15.2). Before 1.0 a minor can break, so it never crosses into another minor. - A component that only exists in a newer release fails with the fix:
--core latestpulls from the newest release and upgrades@runek/coreto match. Components you added earlier came from the old snapshot, so re-add them (--overwrite) if the new core breaks them. - Releases before 0.14.0 serve only components, not the prop schema or the agent skill, so
info,validateandskillask you to upgrade there. - A registry without
versions.json(a local checkout, your own fork) is used as-is, so--registry ./registrybehaves as before.