JsonUI
← ReferenceCLI command referenceEvery jui / sjui / kjui / rjui / jsonui-test / jsonui-doc subcommand with its synopsis and purpose, the flags this reference records, and the exit codes and environment variables the binaries share. What is checked, and what is not: for `jui`, `jsonui-test` and `jsonui-doc`, a deploy gate compares this catalogue with the binary's `--help` at the pinned version in both directions, so a subcommand with no card — or a card for a command the binary no longer accepts — stops the deploy. The three platform CLIs are not checked that way. Nothing checks the wording: each card's synopsis and purpose are written by hand from `--help`, which stays the authority whenever the two disagree.reference
How the reference is laid outThe catalogue below runs binary by binary — `jui` first, then sjui, kjui, rjui, jsonui-test and jsonui-doc — with one card per subcommand carrying its synopsis, what the command is for, its flags, an example invocation, and the related commands worth reading next. Flag coverage is uneven on purpose: most `jui` and `jsonui-test` cards list their flags, while the platform CLIs and most `jsonui-doc` cards carry synopsis and purpose only. For flags, each binary's own `--help` is always the fuller source — the gate that keeps this catalogue honest compares which subcommands exist, never which flags they take. Exit codes, environment variables and the stability policy are shared across binaries, so they get their own sections instead of being repeated on every card.
The six binariesOne install brings all six. Which one you reach for follows the platform you are generating for — `jui` is the cross-platform entry point that most workflows drive, and the platform CLIs sit underneath it doing the per-language work.
juiPython · all · 29
Unified generator / verifier / orchestrator for JsonUI. Cross-platform.
sjuiRuby · ios · 8
SwiftJsonUI development CLI (iOS).
kjuiRuby · android · 3
KotlinJsonUI development CLI (Android).
rjuiRuby · web · 5
ReactJsonUI development CLI (Web).
jsonui-testPython · all · 11
Test file validator and generator. Cross-platform.
jsonui-docPython · all · 13
Documentation generator for specs and components.
Command catalogueEvery subcommand, grouped by binary in the order above. The synopsis is the invocation shape; `Flags` lists what the command accepts wherever this reference records them.
juijui init
Also: jui i
jui init --project-name <name> [--ios PATH] [--android PATH] [--web PATH]
Initialize a JsonUI project: write jui.config.json and register the platform roots you pass. Copies platform tools into each root unless --no-sync-tools is given.Flags
--project-name (string, required, default: —)
Project name.
 
--ios (path, default: —)
iOS project root path.
 
--ios-mode (enum, default: swiftui)
iOS rendering mode: `swiftui`, `uikit`, or `all` (generate for both renderers).
 
--android (path, default: —)
Android project root path.
 
--android-mode (enum, default: compose)
Android rendering mode: compose or xml.
 
--package-name (string, default: —)
Android package name (e.g. com.example.app).
 
--web (path, default: —)
Web project root path.
 
--no-sync-tools (flag, default: false)
Skip copying sjui_tools / kjui_tools / rjui_tools into the platform roots.
Example
$ jui init --project-name MyApp --ios ios/ --web web/
See also: jui g project · jui build
juijui g project
jui g project [--file SPEC] [--ios-only|--android-only|--web-only]
Generate Layout JSON plus ViewModel / Repository / UseCase declaration stubs from every screen spec, or from a single spec with --file.Flags
--file (path, default: all specs)
Single spec file to process.
 
--force (flag, default: false)
Force-overwrite declaration files.
 
--skip-layout (flag, default: false)
Skip Layout JSON generation.
 
--dry-run (flag, default: false)
Show what would be generated without writing.
 
--ios-only (flag, default: false)
Generate iOS files only.
 
--android-only (flag, default: false)
Generate Android files only.
 
--web-only (flag, default: false)
Generate Web files only.
 
--type-map (path, default: .jsonui-type-map.json)
Path to the type map file.
Example
$ jui g project --file login.spec.json --web-only
See also: jui build · jui verify
juijui g screen
jui g screen <Name> [<Name> ...] [--display-name <text>]
Create screen-spec templates for one or more new screens (PascalCase names).Flags
names (positional, required, default: —)
Screen names (PascalCase).
 
--display-name (string, default: —)
Display name (single screen only).
Example
$ jui g screen Login Settings
See also: jui g project
juijui g converter
jui g converter [<Name>] [--from SPEC | --all]
Generate a custom-component converter — from a name + --attributes, from a component spec with --from, or from every component spec with --all.Flags
name (positional, default: —)
Component name.
 
--from (path, default: —)
Component spec file (resolved against component_spec_directory).
 
--all (flag, default: false)
Generate from all component specs.
 
--attributes (string, default: —)
Attributes as key:type,... pairs.
 
--container (flag, default: false)
Mark as a container component.
 
--skip-existing (flag, default: false)
Skip converters that already exist (used by jui build).
Example
$ jui g converter --from rating-stars.component.json
See also: jui g project
juijui g api
jui g api [--dry-run] [--platform ios|android|web]
Generate DTO + Domain models from the swagger files (api.schemas filter applies). --dry-run previews without writing.Flags
--dry-run (flag, default: false)
Show what would be generated without writing files.
 
--platform (enum, default: all)
Restrict to a single platform.
 
--json (flag, default: false)
Emit JSON (used by MCP wrappers).
Example
$ jui g api --dry-run --platform web
See also: jui ls api-specs · jui ls api-models
juijui g attr-bindings
jui g attr-bindings [--lang swift|kotlin|ruby|all] [--out DIR]
Generate typed attribute-extraction code from the SSoT attribute_definitions.json. Deterministic output — re-running is byte-identical.Flags
--lang (enum, default: all)
Target language: swift, kotlin, ruby, or all.
 
--out (path, default: build/attr_codegen/<lang>)
Output directory (per-language subdirs with --lang all).
 
--definitions (path, default: bundled SSoT)
Override the attribute definitions file.
Example
$ jui g attr-bindings --lang kotlin
See also: jui conformance coverage
juijui build
Also: jui b
jui build [--clean] [--ios-only|--android-only|--web-only | --platform ios|android|web] [--allow-partial] [--lint-strings]
Build all configured platforms (or one with --platform / the --*-only flags). Diagnostics are printed, not gated: validation warnings and localize findings leave the exit code at 0. It exits non-zero on the failures that stop the run instead — no jui.config.json, a layout that is not valid JSON, a responsive-variant contract violation, an @canonical mark that does not resolve. Running clean is a rule in the agent rulebook, not something this command enforces.Flags
--clean (flag, default: false)
Clean build — regenerate everything.
 
--ios-only (flag, default: false)
Build iOS only.
 
--android-only (flag, default: false)
Build Android only.
 
--web-only (flag, default: false)
Build Web only.
 
--platform (enum)
Build one platform: `ios`, `android` or `web`. Equivalent to the matching `--*-only` flag — it exists so the CLI and the MCP `jui_build` tool share one vocabulary (since jsonui-cli 1.8.10).
 
--allow-partial (flag, default: false)
Exit 0 even when a screen failed to generate. Without it a failed stage fails the command, which is the default since jsonui-cli 1.8.43 — an exit-0 run that dropped a screen ships a tree with a hole in it, and a CI gating on the exit code goes green over it. Measured before that release: a layout that could not be generated printed four `[ERROR]` lines, wrote no file, and exited 0.
 
--lint-strings (flag, default: false)
Run the localize scan (`jui lint-strings`) as part of the build and print its findings as build warnings. The build's exit code does not change; to fail on them, run `jui lint-strings` directly, which exits 2. Also settable as `"lint": {"strings": true}` in jui.config.json.
Example
$ jui build --web-only --clean
See also: jui verify · jui lint-generated · jui lint-strings
juijui verify
jui verify [--fail-on-diff] [--file SPEC] [--platform <p>] [--detail] [--json PATH]
Compare generated Layout JSON against the committed Layouts to detect spec drift. `--fail-on-diff` exits non-zero for CI (JsonUI invariant #2). Since jsonui-cli 1.8.5 the summary names its denominator — `verified N of M screen(s)`, with the skipped ones and why — so a green run no longer hides how much it actually compared: a screen whose layout is authored by hand rather than generated is skipped, and a project where every screen is skipped reports `verified 0 of M` while still exiting 0.Flags
--fail-on-diff (flag, default: false)
Exit non-zero if any diff is detected.
 
--detail (flag, default: false)
Include per-screen diff details in the report.
 
--file (path, default: all specs)
Verify a single spec file.
 
--platform (enum, default: first in config)
Target platform.
 
--json (path)
Write the counts (`verified` / `skipped` / `total`, with `skippedByReason`) to PATH as JSON, for automation that cannot read the report.
Example
$ jui verify --fail-on-diff
**Total: match=0, missing=0, extra=0, type_mismatch=0**
 
**verified 0 of 2 screen(s)** — 2 skipped (layout authored externally)
 
$ jui verify --json counts.json && cat counts.json
{ "verified": 0, "skipped": 2, "total": 2, "skippedByReason": { "layout authored externally": 2 } }
See also: jui build
juijui screens
jui screens [--json] [--layouts-dir <dir>]
List every layout with the screen / cell / partial classification the screen-identity resolver derived, and why. Read this back before writing tests: only a layout classified as a screen is a valid `screen` value or an `assert: "screen"` target.Flags
--json (flag, default: false)
Emit JSON instead of the aligned table (what the MCP wrappers consume). Adds `derivedScreens` — every screen the resolver derived rather than read from an explicit `role`, which is the complete set to audit. The `needsReview` list beside it is a name-shaped hint, never complete.
 
--layouts-dir (string, default: project's shared layouts)
Classify a different layout directory instead of the one in jui.config.json.
Example
$ jui screens
Screen identity: 95 screen(s), 36 non-screen(s)
95 of 95 screen(s) DERIVED, not declared. Derivation cannot see cells built from host code; declare "role": "cell" on any that are not screens ('jui screens --json' lists them under derivedScreens).
cell agent_row (referenced)
screen agents (default)
cell breadcrumb_item (referenced)
screen data-binding (default)
See also: jui build
juijui ls api-specs
jui ls api-specs [--json]
List swagger / OpenAPI files with parsed metadata (MCP discovery command).Flags
--json (flag, default: false)
Emit JSON (used by MCP wrappers).
Example
$ jui ls api-specs --json
See also: jui g api
juijui ls api-models
jui ls api-models [--platform ios|android|web] [--json]
List generated DTO + Domain scaffold files per platform (MCP discovery command).Flags
--platform (enum, default: all)
Restrict to a single platform.
 
--json (flag, default: false)
Emit JSON (used by MCP wrappers).
Example
$ jui ls api-models --platform ios
See also: jui g api
juijui migrate-layouts
jui migrate-layouts [--from ios|android|web] [--dry-run]
One-shot adoption helper: copy the Layout JSON that already lives under a platform project into the shared `layouts_directory`, so an existing app can move to the shared tree without hand-copying. `--from` selects which platform to copy out of, not a path.Flags
--from (enum, default: ios)
Which platform project to copy the layouts out of: `ios`, `android` or `web`.
 
--dry-run (flag, default: false)
Show planned changes without writing.
Example
$ jui migrate-layouts --dry-run
See also: jui init
juijui sync_tool
jui sync_tool [--from <dir>] [--platform android|ios|web] [--prune] [--dry-run]
Sync the project-local platform tools (sjui_tools / kjui_tools / rjui_tools) from the source tree given by --from, which defaults to $JSONUI_CLI_PATH or, when that is unset, to the home install (~/.jsonui-cli). Each tool's extensions/ is preserved.Flags
--from (path, default: $JSONUI_CLI_PATH or ~/.jsonui-cli)
Source directory.
 
--platform (enum, default: all in config)
Sync only one platform.
 
--prune (flag, default: false)
Delete target files missing from the source (outside extensions/).
 
--dry-run (flag, default: false)
Print actions without modifying files.
Example
$ jui sync_tool --platform web
See also: jui build
juijui hotload listen
jui hotload listen [--host H] [--port P] [--ws-path PATH]
Start the unified hot-reload server in the foreground. One process serves iOS and Android; it watches the layouts directory and pushes resolved JSON over WebSocket.Flags
--host (string, default: from docs/hotload/config.json)
Override server.host.
 
--port (number, default: 8081)
Override server.port.
 
--ws-path (string, default: /ws)
Override server.wsPath.
Example
$ jui hotload listen
See also: jui hotload status · jui hotload stop
juijui hotload status
jui hotload status
Print whether a hotload server is running.Example
$ jui hotload status
See also: jui hotload listen
juijui hotload stop
jui hotload stop
Stop the running hotload server.Example
$ jui hotload stop
See also: jui hotload listen
juijui conformance generate
jui conformance generate [--definitions PATH] [--out DIR]
Generate the conformance fixtures/ directory + manifest.json from attribute_definitions.json.Flags
--definitions (path, default: bundled SSoT)
Path to attribute_definitions.json.
 
--out (path, default: conformance/)
Output directory.
Example
$ jui conformance generate
See also: jui conformance report
juijui conformance report
jui conformance report [--dir DIR] [--results DIR] [--out PATH]
Merge results/*.results.json into REPORT.md — the cross-platform compat matrix.Flags
--dir (path, default: conformance/)
Conformance directory containing manifest.json.
 
--results (path, default: <dir>/results)
Results directory.
 
--out (path, default: <dir>/REPORT.md)
Report output path.
 
--env (string, default: local)
Render-environment key for the visual comparison — baselines are read from `baselines/<env>/`. A baseline is a fact about one renderer, so CI lanes pass their own key and never compare against developer-machine bakes.
Example
$ jui conformance report
See also: jui conformance gate
juijui conformance gate
jui conformance gate --platform <p> [--platform <p>...] [--dir <dir>] [--results <dir>] [--out <path>] [--no-visual] [--env <key>] [--value-discrimination] [--no-ledger-keys] [--rendered-by NAME=SHA] [--codegen-effect] [--parity] [--inert-complete] [--cross-effect]
Render REPORT.md and fail on regressions — the CI gate, runnable locally.Flags
--platform (enum (repeatable), required, default: —)
Platform whose results this gate judges; repeatable. Cross-platform mismatches are judged only when all three platforms are selected — with fewer, the results for the unselected ones are committed snapshots rather than this run's.
 
--dir (path, default: conformance/)
Conformance directory.
 
--results (path, default: <dir>/results)
Results directory.
 
--out (path, default: <dir>/REPORT.md)
Report output path.
 
--no-visual (flag, default: false)
Skip the screenshot-dependent checks — baseline comparison, attribute effect, artifact ratchets. For lanes that cannot compare renders: the per-push web lane runs on a different OS than the committed baselines were rendered on.
 
--env (string, default: local)
Render-environment key. Visual checks compare against `baselines/<env>/` and use that environment's ratchet ceilings. Severity is env-dependent: the attribute-effect ledger fails the gate only under `local`, where it is asserted, and is reported as a notice elsewhere.
 
--value-discrimination (flag, default: false)
Also judge that two declared VALUES of one attribute draw different pictures. This is the only check that can see a collapse: every other one compares a fixture to its control, to another platform, or to the codegen's render of itself, so an attribute that maps every value onto the same layout passes all of them. Collapses must be recorded in `value_discrimination.json` with an owner and a reason, and stale entries fail too.
 
--no-ledger-keys (flag, default: false)
Skip the check that every ledger key still names a fixture the manifest has. That check runs by DEFAULT, and this is the opt-out: a rename leaves the row pointing nowhere, the entry still looks live, the gate keeps running, and nothing that row claims is ever checked again. Dangling rows are reported, never deleted. Use it only on a checkout mid-rename.
 
--rendered-by (string (NAME=SHA, repeatable), default: —)
Which library drew this run's screenshots, e.g. `swiftjsonui=<sha>`. Compared against what the baseline records; when a regression is reported against a baseline drawn by a different library, the gate says so out loud. That is a notice, not a failure — the library is meant to move and the baseline is meant to be rebaked.
 
--codegen-effect (flag, default: false)
Also judge the codegen differential (C0/C1/C2/C3) against `codegen_effect.json`: defects must be recorded with an owner and a reason, and recorded entries must still measure — unrecorded defects and stale entries both fail. Needs no renders, so `--no-visual` does not affect it.
 
--parity (flag, default: false)
Also judge dynamic ≡ codegen: the codegen-host screenshots (`artifacts/<platform>-codegen`) must match the dynamic baseline within its threshold, deviations must be recorded in `codegen_parity.json`, and recorded entries must still measure — unrecorded drift and stale entries fail.
 
--inert-complete (flag, default: false)
Also judge inert COMPLETENESS: every fixture that renders identically to its control must be accounted for by some ledger or contract, or recorded in `inert_audit.json`. This is what catches an attribute that silently stops rendering — it looks exactly like an unrecorded inert verdict, and no other check can see it. Unrecorded verdicts and stale entries fail under env `local` and are a notice elsewhere. Needs the visual checks.
 
--cross-effect (flag, default: false)
Also judge cross-platform activeness agreement: a fixture's control-diff verdict (active or inert) must agree across the selected platforms its attribute is declared for, and values enumerated by the SSoT must not be inert everywhere. Findings must be recorded in `cross_effect.json`, and recorded entries must still measure — unrecorded findings and stale entries fail under env `local`, a notice elsewhere. Needs at least two selected platforms and the visual checks.
Example
$ jui conformance gate --platform web --no-visual
See also: jui conformance report
juijui conformance compat-doc
jui conformance compat-doc --platform <p> -o PATH
Generate the @generated attribute-compatibility markdown from conformance results.Flags
--platform (enum, required, default: —)
Platform whose results feed the coverage column.
 
--definitions (path, default: bundled SSoT)
Path to attribute_definitions.json.
 
--dir (path, default: conformance/)
Conformance directory.
 
-o, --out (path, required, default: —)
Output markdown path.
Example
$ jui conformance compat-doc --platform ios -o Docs/attribute_compatibility.md
See also: jui conformance report
juijui conformance baseline update
jui conformance baseline update --platform ios|android|web [--dir DIR] [--artifacts DIR] [--env ENV] [--only-new] [--fail-on-moved] [--rendered-by NAME=SHA] [--threshold T]
Hash artifacts/<platform>/*.png into baselines/<platform>.hashes.json (perceptual-hash screenshot baselines).Flags
--platform (enum, required, default: —)
Platform whose artifacts to baseline.
 
--dir (path, default: conformance/)
Conformance directory.
 
--artifacts (path, default: <dir>/artifacts/<platform>)
Artifacts directory.
 
--env (string, default: )
Render-environment key to bake the baseline under.
 
--only-new (flag, default: false)
Bake only fixtures that have no baseline yet, leaving existing ones untouched.
 
--fail-on-moved (flag, default: false)
Exit non-zero when a fixture that already had a baseline moved. Which of two behaviours you get is decided by the jui version the machine resolves, and by nothing else — not the flag, not a project setting. From jsonui-cli 1.8.85 the command refuses before writing, so the baseline file is left untouched; up to 1.8.84 it wrote the file first and chose the exit code afterwards, so a run stopped there had already replaced the baseline — restore it from version control before re-running, and do not read the non-zero exit as "nothing was written". Pairing this with `--only-new` turns the refusal off: that combination writes entries for fixtures that had no baseline, and only leaves the existing ones alone.
 
--rendered-by (string, default: )
Metadata only (NAME=SHA): recorded in the manifest, never part of a comparison. A local bake does not know what CI would have used, so nothing looks it up.
 
--threshold (number, default: the shared calibrated value)
Comparison threshold stored in this manifest. Recalibrate per (env, platform) by measuring the renderer's repeat-run noise first.
Example
$ jui conformance baseline update --platform android
See also: jui conformance gate
juijui conformance coverage
jui conformance coverage [--platform <p>...] [--update]
Check declared attributes against what each platform's converters actually read; --update rewrites coverage.json from the current gaps (reasons preserved).Flags
--platform (enum (repeatable), default: all)
Limit the check to a platform.
 
--definitions (path, default: bundled SSoT)
Path to attribute_definitions.json.
 
--dir (path, default: conformance/)
Conformance directory holding coverage.json.
 
--repo-root (path, default: —)
Repo root holding the converter sources.
 
--update (flag, default: false)
Rewrite coverage.json from the current gaps.
 
--binding (flag, default: false)
Report the BINDING lane instead: attributes the SSoT declares a `@{...}` form for that no fixture ever writes in that form, so no platform's behaviour on it is measured. Reporting only — it cannot gate until the queue is consumed.
 
--json (path, default: —)
With `--binding`, write the queue to this path as JSON.
Example
$ jui conformance coverage --platform web
See also: jui conformance effect
juijui conformance effect
jui conformance effect --platform <p> [--update]
Record / check which fixtures render differently from their control (the attribute-effect ledger).Flags
--platform (enum, required, default: —)
Platform whose artifacts to measure.
 
--dir (path, default: conformance/)
Conformance directory.
 
--artifacts (path, default: <dir>/artifacts/<platform>)
Directory holding this platform's screenshots.
 
--update (flag, default: false)
Record fixtures that differ from control into control_diff.json (existing entries kept).
 
--env (string, default: local)
Render-environment key, passed through to the fixture-vs-control comparison so that environment's system chrome bands are excluded — the Android CI status bar's clock ticks between a pair's two captures and otherwise reads as the attribute having had an effect.
Example
$ jui conformance effect --platform ios --update
See also: jui conformance coverage
juijui conformance parity
jui conformance parity --platform ios|android|web [--env <key>] [--update]
Compare one platform's codegen artifacts against the dynamic baseline recorded for a render environment. `--update` records the measured deviations, preserving the reasons and notes already reviewed on surviving entries and marking new ones unreviewed.Flags
--platform (enum, default: —)
Platform whose codegen artifacts to measure.
 
--env (string, default: local)
Render-environment key of the dynamic baseline to compare against.
 
--dir (path, default: conformance/)
Conformance directory.
 
--codegen-artifacts (path, default: <dir>/artifacts/<platform>-codegen)
Codegen screenshots directory.
 
--update (flag, default: false)
Record the measured deviations into `codegen_parity.json`: reasons and notes on surviving entries are preserved and new entries get the unreviewed marker. Without it, unrecorded deviations and stale entries exit non-zero — the same check that `gate --parity` runs.
See also: jui conformance baseline update · jui conformance report
juijui conformance cross-effect
jui conformance cross-effect [--platform ios|android|web]... [--update]
Compare whether each attribute is active across platforms. At least two platforms are required — activeness agreement measured on one platform compares nothing.Flags
--platform (enum (repeatable), default: all three)
Platform to include in the comparison. At least two are required — activeness agreement across one platform compares nothing.
 
--dir (path, default: conformance/)
Conformance directory.
 
--results (path, default: <dir>/results)
Results directory.
 
--definitions (path, default: shared/core/attribute_definitions.json)
Path to the attribute declarations, for the uniformly-inert check.
 
--semantics (path, default: shared/core/attribute_semantics.json)
Path to the attribute-semantics contract; adjudicated rulings are verified from it instead of from the ledger.
 
--update (flag, default: false)
Record the measured findings into `cross_effect.json`: reasons and notes are preserved where the fact still holds, new entries get the unreviewed marker, and entries no longer supported are pruned. Without it, unrecorded findings and stale entries exit non-zero — the same check that `gate --cross-effect` runs.
 
--env (string, default: local)
Render-environment key, passed through to the fixture-vs-control comparison so that environment's system chrome bands are excluded — the Android CI status bar's clock ticks between a pair's two captures and otherwise reads as the attribute having had an effect.
See also: jui conformance effect · jui conformance inert-audit
juijui conformance inert-audit
jui conformance inert-audit [--platform ios|android|web]... [--untriaged-only] [--json <out>]
The triage queue of attributes measured inert. Attribution is per platform, but every item is reported with its full cross-platform picture so a single-platform verdict is not read as a general one.Flags
--platform (enum (repeatable), default: all three)
Platform to audit. Attribution is per platform, but the queue reports every item's full cross-platform picture.
 
--dir (path, default: conformance/)
Conformance directory.
 
--results (path, default: <dir>/results)
Results directory.
 
--definitions (path, default: shared/core/attribute_definitions.json)
Path to the attribute declarations, for the declared-value and value-is-default triage.
 
--semantics (path, default: shared/core/attribute_semantics.json)
Path to the attribute-semantics contract; fixtures it declares an observable for are attributed to the contract.
 
--json (path, default: —)
Write the adjudication queue here as JSON. The file is a report, not a ledger — nothing reads it back.
 
--untriaged-only (flag, default: false)
List only the items no mechanical triage family closes.
 
--update (flag, default: false)
Record the unattributed inert verdicts into `inert_audit.json` — the ledger that `gate --inert-complete` reads. Reasons already written are kept as long as the platform set still holds; entries the measurement no longer supports are dropped.
 
--env (string, default: local)
Render-environment key, passed through to the fixture-vs-control comparison so that environment's system chrome bands are excluded.
See also: jui conformance cross-effect · jui conformance coverage
juijui conformance codegen-effect
jui conformance codegen-effect [--platform ios|android|web] [--check C0|C1|C2|C3] [--paired] [--update]
For every declared (component, attribute, platform), run the production converter over a layout hash four times and compare the emitted source text: C0 is the spelling read at all, C1 does the `@{...}` binding form survive, C2 is the value read or is a constant emitted. This is the codegen stage only — it proves the converter puts the attribute in its output, never that the library then honours it. UIKit and the dynamic renderers have no codegen output to compare and are scoped out with a reason.Flags
--platform (enum (repeatable), default: all three)
Limit to a platform. UIKit and the dynamic renderers have no codegen output to compare and are scoped out with a reason.
 
--definitions (path, default: shared/core/attribute_definitions.json)
Path to the attribute declarations the run enumerates.
 
--repo-root (path, default: the toolchain root)
Repo root holding the three tool trees.
 
--ruby (path, default: ruby)
Ruby interpreter used to run the probe scripts.
 
--check (enum (repeatable), default: all)
Report only these judgements: `C0` attribute-vs-control (is the spelling read at all), `C1` bound-vs-control (does the `@{...}` form survive), `C2` value1-vs-value2 (is the value read, or is a constant emitted).
 
--paired (flag, default: false)
Paired probe: write the COMPANION attributes the adjudication ledger says an attribute needs onto the control and onto every case alike, so they cancel and the attribute under test is again the only difference. Without it, an attribute the ledger rules inert on its own — `borderWidth` needs `borderColor` — can only ever measure the ruling. The companion sets are derived from the semantics contract, never listed by hand.
 
--json (path, default: —)
Write the full queue — findings, evidence and the scope ledger — here.
 
--limit (number, default: 40)
How many findings to print. The JSON holds them all.
 
--update (flag, default: false)
Record the measured defects into `codegen_effect.json` (owners and reasons on surviving entries are preserved) instead of judging against it. Without it, unrecorded defects and stale entries both exit non-zero — the same check that `gate --codegen-effect` runs.
 
--conformance-dir (path, default: conformance/)
Where `codegen_effect.json` lives.
See also: jui conformance effect · jui conformance parity
juijui lint-strings
jui lint-strings [--json] [--usage] [--update-allowlist] [--allowlist <path>]
Localize gate: every user-visible string in a layout must resolve through strings.json, not sit there as a raw literal. Bare keys resolve only within the sections the layout owns, so a key that exists only in someone else's section is reported rather than silently borrowed.Flags
--json (flag, default: false)
Emit the report as JSON (findings, stale entries, counts). With --usage the report also carries a `usage` object.
 
--usage (flag, default: false)
Since v1.6.15. Also check set agreement between strings.json and every referencing face — unused keys, missing keys, and dynamic references that bypass a *_STRING_KEYS map. Opt-in per run, or per project via lint.stringsUsage in jui.config.json.
 
--update-allowlist (flag, default: false)
Write the current raw-literal set into the allowlist ledger, preserving existing reasons. New entries get an empty reason and keep the lint failing until documented.
 
--allowlist (string, default: .jui-strings-allowlist.json)
Ledger path. Defaults to the project root file, or lint.stringsAllowlist from jui.config.json.
Example
$ jui lint-strings
$ jui lint-strings --usage --json
See also: jui build · jui lint-generated
juijui lint-generated
jui lint-generated [--fix] [--verbose] [--max-depth <n>] [--max-lines <n>] [--update-size-baseline] [--fail-on any|size-ratchet|health]
CI-side audit of the generated tree: every generated file must carry the `@generated` sentinel, and every generated view function must stay inside the size bounds. Not part of `jui build` — run it in CI or before a release.Flags
--fix (flag, default: false)
Print the regeneration command when markers are missing (it does not edit files).
 
--verbose (flag, default: false)
List every checked file, not just the failures.
 
--max-depth (number, default: 5 (or lint.max_depth))
Per-function brace-nesting bound for generated view code. Depth is what actually matters on iOS: SwiftUI composes one generic type per view and the runtime decodes its metadata on a 1 MB device stack. Omit the flag and `jui.config.json`'s `lint.max_depth` applies; omit both and the built-in 5 does. A configured value looser than 5 is refused with a warning — record accepted depths in `.jui-size-waivers` instead.
 
--max-lines (number, default: 250 (or lint.max_lines))
Per-function line-count bound. A soft target: a function that satisfies the depth bound but overruns the line count is reported, not rejected. Resolved the same way as `--max-depth` — flag, then `lint.max_lines`, then the built-in 250 — and a looser configured value is likewise refused.
 
--fail-on (enum, default: any)
Which finding sets a non-zero exit code: `any`, `size-ratchet`, or `health`. Exit 2 is a size-ratchet regression, 3 is generated-artifact health, and non-gating findings are still printed. Splitting them lets one signal gate a job without the other holding it red.
 
--update-size-baseline (flag, default: false)
Write the current oversized-function set to `.jui-size-waivers`. Commit that file and later runs can separate the known set from functions that became oversized after it.
Example
$ jui lint-generated --verbose
OK: 1217 Missing header: 0 Oversized fns: 3
 
$ jui lint-generated --update-size-baseline
✓ Wrote .jui-size-waivers (3 entries)
See also: jui build · jui verify
sjuisjui init
sjui init [--mode all|uikit|swiftui]
Initialize the current iOS project for SwiftJsonUI: writes sjui.config.json for the chosen mode (all, uikit or swiftui; detected when --mode is omitted). An existing sjui.config.json is kept, and only an empty source_directory is filled in. Libraries and base files are installed by sjui setup, not here.Flags
--mode (enum, default: detected)
Initialize mode (all, uikit, swiftui).
Example
$ sjui init --mode swiftui
Initializing SwiftJsonUI project in swiftui mode...
Created config file: sjui.config.json
Hotloader is now managed centrally — run 'jui hotload listen' from the project root.
Initialization complete!
 
SwiftUI mode initialized. Use SwiftUI-specific commands for your project.
 
# run again where the file already exists:
$ sjui init --mode swiftui
Initializing SwiftJsonUI project in swiftui mode...
Config file already exists: sjui.config.json
Initialization complete!
See also: sjui setup · sjui generate
sjuisjui setup
sjui setup
Wire an existing Xcode project up for SwiftJsonUI, in the mode sjui.config.json declares. Ensures an SPM workspace, creates the layout/style/view-model directories, adds the library package references to the project, installs the Hot Loader hook (an AppDelegate hook in UIKit mode, a HotLoaderSetup.swift in SwiftUI mode), and in UIKit mode removes the storyboard references from Info.plist. The packages themselves download on the next Xcode build. Requires a .xcodeproj; run once after `sjui init`.See also: jui hotload listen
sjuisjui generate
Also: sjui g
sjui generate <subcommand> <name> [options]
Generate SwiftJsonUI files in the current iOS project. Subcommands from the tool's own help: `view` (a view with JSON and binding), `partial`, `collection`, `binding`, `converter` (a custom converter; `--attributes` and, for UIKit, `--class-name` / `--import-module`), and `adapter` (an adapter for an existing view in Dynamic mode). Requires an Xcode project or Package.swift; `g` is the alias.Flags
<subcommand> (positional, required)
`view` / `partial` / `collection` / `binding` / `converter` / `adapter`.
 
<name> (positional, required)
PascalCase name; `collection` takes `Parent/Cell`.
 
--root (flag, default: false)
`view` only: make the generated view the root view.
Example
# the tool's own examples (sjui generate --help):
$ sjui g view HomeView # Generate a view
$ sjui g view RootView --root # Generate root view
$ sjui g partial Header # Generate a partial
$ sjui g collection Post/Cell # Generate collection cell
$ sjui g converter MyConverter --attributes text:String,color:Color
$ sjui g adapter Home # Creates HomeViewAdapter for HomeView
See also: sjui destroy
sjuisjui destroy
Also: sjui d
sjui destroy <type> <name>
Delete the files `sjui generate` created for a view, partial, collection or binding (the ViewController, JSON and binding as applicable). The tool asks for confirmation first; the deletion is permanent. `d` is the alias.Flags
<type> (positional, required)
`view` / `partial` / `collection` / `binding`.
 
<name> (positional, required)
The generated name, nested paths allowed (`home/dashboard`).
Example
# the tool's own examples (sjui destroy --help):
$ sjui destroy view splash
$ sjui destroy view home/dashboard
$ sjui destroy partial header
$ sjui destroy collection product_list
$ sjui destroy binding custom
See also: sjui generate
sjuisjui build
sjui build [--mode all|uikit|swiftui] [--clean] [--no-validate] [--strict]
Generate the SwiftJsonUI files for the project's layouts: extracts strings, validates every layout's attributes and bindings, then builds the UIKit binding files and/or the SwiftUI views for the mode in sjui.config.json (or --mode). Validation warnings are printed; canonical binding errors always fail the build, other validation errors fail it only with --strict. It does not run xcodebuild.Flags
--mode (enum, default: detected from sjui.config.json)
Build mode (all, uikit, swiftui).
 
--clean (flag, default: false)
Clean cache before building.
 
--no-validate (flag, default: false)
Skip JSON attribute validation.
 
--strict (flag, default: false)
Fail the build on validation findings. Since jsonui-cli 1.8.43 that includes binding warnings, not attribute ones alone: measured on 1.8.44, a layout whose only findings are two undefined-variable binding warnings exits 0 plain and 1 under `--strict`. A canonical binding-rule error (`@{a ?? b ?? c}`, say) fails the build whether or not you pass the flag. Before 1.8.43 the counter held attribute warnings alone, and a binding-only layout passed `--strict`. One more thing moved in 1.8.45: findings are reported on a cached run too. Until then a build that skipped codegen skipped validation with it, so a second run over an unchanged tree printed nothing and `--strict` exited 0 — the gate answered from the cache rather than from the layouts. Measured on both sides, on the run where the log says `No files need updating (all cached)`: 1.8.44 exits 0 with no findings, 1.8.45 exits 1 and prints them.
Example
$ sjui build --strict
# the tool's own examples (sjui --help):
$ sjui build # Build UIKit/SwiftUI files
$ sjui build --quiet # Build with only error output
See also: jui build
sjuisjui convert
sjui convert <input.json> [output.swift] [--from json] [--to swiftui]
Convert one Layout JSON into SwiftUI source code (the only conversion the tool supports: --from json --to swiftui). The output path is optional. A second form, `sjui convert to-group [--force]`, converts a UIKit project's file references to group references.Flags
<input.json> (positional, required)
The layout to convert.
 
[output.swift] (positional)
Where to write the SwiftUI source.
 
--from (enum, default: json)
Input format (json).
 
--to (enum, default: swiftui)
Output format (swiftui).
See also: sjui validate
sjuisjui validate
sjui validate [files...] [--verbose]
Validate Layout JSON files against the SwiftJsonUI attribute schema: every JSON file in the layouts directory when no argument is given, or the files and patterns named.Flags
[files...] (positional)
Files or glob patterns; default is every JSON under the layouts directory.
 
--verbose (flag, default: false)
Show detailed error messages. Short form `-v`.
Example
# the tool's own examples (sjui validate --help):
$ sjui validate # Validate all JSON files in layouts directory
$ sjui validate Layouts/main.json # Validate specific file
$ sjui validate Layouts/**/*.json # Validate files matching pattern
See also: jui verify
sjuisjui watch
sjui watch [--mode all|uikit|swiftui]
Watch the layouts and styles directories for JSON changes and re-run the SwiftJsonUI build for the mode (an initial build runs first; Ctrl+C stops). It does not talk to the Hot Loader — that runs centrally as `jui hotload listen`.Flags
--mode (enum, default: detected from sjui.config.json)
Watch mode (all, uikit, swiftui).
See also: sjui build · jui hotload listen
kjuikjui init
kjui init [--mode all|xml|compose]
Initialize the current Android project for KotlinJsonUI: writes kjui.config.json for the chosen mode (all, xml or compose; detected when --mode is omitted) and prints the next steps. Directories and base files are created by `kjui setup`, not here. Measured at 1.8.19 and 1.8.20: `--mode compose` stops with 'uninitialized constant KjuiTools::Core::Resources' and writes nothing (reported upstream); xml and all work.Flags
--mode (enum, default: detected)
Initialize mode (all, xml, compose).
Example
$ kjui init --mode xml
Initializing KotlinJsonUI project in xml mode...
Created config file: kjui.config.json
Initialization complete!
 
Next steps:
1. Edit kjui.config.json to customize paths if needed
2. Run 'kjui setup' to create directories and base files
3. Run 'kjui g view HomeView' to generate your first view
See also: kjui setup
kjuikjui setup
kjui setup
Set up the current Android project for KotlinJsonUI in the mode kjui.config.json declares. Creates the directory structure and base files (Theme.kt, MainActivity.kt in Compose mode) and adds the framework's dependencies to build.gradle. It does not resolve them — that happens on the next Gradle build — and it does not configure Navigation. Run after `kjui init`.See also: kjui init · kjui generate
kjuikjui generate
Also: kjui g
kjui generate [<subcommand> <name>] [--mode xml|compose] [options]
Generate KotlinJsonUI files. With no subcommand it generates everything for the configured mode (all XML layouts, or all Compose views). Subcommands from the tool's own help: `view`, `partial`, `collection` (alias `cell`), `binding`, `converter`, `adapter`. In XML mode a view can be an Activity (default) or a Fragment. `g` is the alias.Flags
--mode (enum, default: from kjui.config.json)
Override the mode (xml / compose). Short form `-m`.
 
--activity (flag, default: true)
XML mode, `view`: generate as an Activity.
 
--fragment (flag, default: false)
XML mode, `view`: generate as a Fragment.
 
--type (enum)
XML mode, `view`: activity or fragment.
 
--force (flag, default: false)
Overwrite existing files. Short form `-f`.
Example
# the tool's own examples (kjui generate --help):
$ kjui g # Generate all (based on config mode)
$ kjui g --mode xml # Generate all XML layouts
$ kjui g view HomeView --mode xml --activity # Generate Activity
$ kjui g view ProfileView --mode xml --fragment # Generate Fragment
$ kjui g view MainView --mode compose # Generate Compose view
$ kjui g adapter Home # Creates HomeViewAdapter for HomeView
See also: kjui setup
rjuirjui init
rjui init
Initialize ReactJsonUI in the current web project. Creates rjui.config.json — where `web_framework` selects the target framework (Next.js by default; a custom adapter object retargets any React-family framework).
rjuirjui build
rjui build
Build React components from JSON layouts. Day to day this runs as part of `jui build --web-only` rather than directly.See also: jui build
rjuirjui watch
rjui watch
Watch JSON files and auto-rebuild on changes.
rjuirjui hotload
rjui hotload [listen|stop|status]
HotLoader control (listen / stop / status). A bare `rjui hotload` defaults to listen.
rjuirjui generate
Also: rjui g
rjui generate <type> <name>
Scaffold a view / component / collection cell / converter (short forms: v / c / col / conv).
jsonui-testjsonui-test validate
jsonui-test validate <path>... [--no-install] [--no-mock-check] [--config <file>]
Validate test JSON files (schema, reference integrity, element ID existence). Since 2026-07-30 it also runs the mock contract check, and regenerates `<mockDir>/generated/` when it is missing or older than the swagger — a fresh clone or a first CI run therefore starts from a complete mock set. If that regeneration fails the suite does not run, because an empty `generated/` turns into a confusing pile of 404s.Flags
--no-mock-check (flag, default: false)
Skip the mock contract check. The check is on by default deliberately: `mock generate --check` existed for a long time while nothing called it, which is why stale mocks survived unnoticed.
 
--no-install (flag, default: false)
Validate only: skip the flatten-install even when `test.install` is configured.
 
--config (path, default: jui.config.json)
Config file the `test.install` destinations are read from.
Example
$ jsonui-test validate tests/
✓ 12 screen tests valid
✓ 3 flow tests valid
✓ mock contract: 166 mocks checked, no drift
See also: jsonui-test mock generate · jsonui-test report
jsonui-testjsonui-test mock generate
jsonui-test mock generate --swagger <openapi> --out <dir> [--check] [--update-default]
Write one mock file per OpenAPI operation into `<out>/generated/`, clearing that directory first so it always matches the current contract. Hand-written mocks live outside `generated/` and are never touched; the two overlay per scenario, so a hand-written file only needs the scenarios its tests drive.Flags
--check (flag, default: false)
Write nothing; compare each scenario body against the schema for its status code — declared types, `required` (including nested fields and array elements) and `enum`, with `nullable` respected. Scenarios are matched by route, not filename. Findings under `generated/` are warnings (regeneration fixes them); findings outside it are errors.
 
--update-default (flag, default: false)
Rewrite only the `default` body and the `source` route of existing files, leaving every other scenario byte-identical so test-driven data survives. `source.swagger` is preserved, so running from a different directory does not churn every file.
 
--swagger (path)
Path to an OpenAPI file; repeatable.
 
--out (path, default: mock.mockDir or tests/mocks)
Output mock directory.
 
--config (path, default: jui.config.json)
Config file.
 
--strict (flag, default: false)
With `--check`, treat a missing OPTIONAL field as drift too. Off by default: omitting an optional field is a valid instance, and failing on it buries the real violations.
 
--dry-run (flag, default: false)
With `--update-default`, report what would change without writing.
Example
$ jsonui-test mock generate --swagger docs/api/app.json --out tests/mocks --check
[NAME] products/list.mock.json does not follow <tag>/<operationId>.mock.json
[ERROR] tests/mocks/products/list.mock.json default: 'total' is required but missing
See also: jsonui-test mock serve · jsonui-test validate
jsonui-testjsonui-test mock serve
jsonui-test mock serve --mock-dir <dir> [--port N] [--no-validate-requests]
Serve the mock files over HTTP with a browser control panel at `/__jsonui__/panel` for flipping scenarios live. Binds to 127.0.0.1, mints a fresh token per launch, verifies the Host header and blocks path traversal. Not exposed over MCP — it is a long-running server that executes run targets.Flags
--no-validate-requests (flag, default: false)
Turn off request-side validation. On by default: request bodies and query parameters are checked against the contract (path-level `parameters` merged in). The request is still answered — a violation means the implementation does not satisfy the contract, and returning 422 would rewrite what every test on that endpoint is testing. Violations are deduplicated, summarized at end of run with a non-zero exit code, and readable at `/__jsonui__/contract-violations`. Also settable as `mock.validateRequests=false`, or per scenario with `skipRequestValidation`.
 
--port (number)
Port to listen on.
 
--mock-dir (path, default: mock.mockDir or tests/mocks)
Directory of mock files to serve.
 
--config (path, default: jui.config.json)
Config file to read the mock settings from.
 
--artifacts (flag, default: false)
After an ios/android run target finishes, pull its artifacts automatically (the same collection `jsonui-test artifacts pull` performs).
See also: jsonui-test mock generate · jsonui-test artifacts pull
jsonui-testjsonui-test mock identity
jsonui-test mock identity [--port P] [--config FILE] [--expect-root DIR] [--any-project]
Ask the mock server that is listening whose corpus it serves, and fail when it is not this project's. Exit codes carry the answer: 0 this project, 1 another project, 2 nothing answered, 3 answered without an identity. It selects the server by PORT — the one thing a name match cannot do — which is what makes it safe to run while several projects have a mock server up. Exit 3 (a server that answers but carries no identity, i.e. one older than this feature) is documented by the tool and not measured here.Flags
--port (number, default: mock.server.port or 8790)
Port to ask.
 
--config (path, default: jui.config.json)
Config file to read the port and project root from.
 
--expect-root (path, default: this project's root)
The project root to require of the answer.
 
--any-project (flag, default: false)
Report the identity without failing on a mismatch.
Example
# measured against a running mock server (project paths shown as <owner> / <caller>):
$ jsonui-test mock identity --port 8799 # asked from the project that started it
{ "pid": 83664, "projectRoot": "<owner>", "mockDir": "<owner>/tests/mocks", ... }
$ echo $?
0
 
$ jsonui-test mock identity --port 8799 # asked from a different project
mock identity: port 8799 is serving '<owner>', not '<caller>' — another project's mock holds this
port. Its corpus would answer this run's requests and the failures would look like your change.
$ echo $?
1
 
$ jsonui-test mock identity --port 8799 --any-project # same, reporting only
$ echo $?
0
 
$ jsonui-test mock identity # with nothing listening
mock identity: nothing answered on port 8790 (<urlopen error [Errno 61] Connection refused>)
$ echo $?
2
See also: jsonui-test mock serve
jsonui-testjsonui-test generate
jsonui-test generate test screen|flow <name> | generate description screen|flow <name> <case> | generate branch-tests <screen>
Scaffold test files. `generate test screen|flow <name>` writes a skeleton test file, `generate description screen|flow <name> <case>` writes the description JSON for one case, and `generate branch-tests <screen>` emits unit tests from the screen spec's branchContracts (see the branch tests guide). `-p` picks the platform, `--path` the destination.See also: jsonui-test validate · jsonui-doc generate doc
jsonui-testjsonui-test generate unit-stubs
jsonui-test generate unit-stubs [--check] [--dry-run] [--spec-dir DIR]
Hold a spec's `unitContracts` block and the hand-written unit tests to each other. `--check` compares the declared set with the implemented one per platform and exits non-zero on drift in any of three directions: declared with no implementation, implemented but declared nowhere, and declared for two platforms while present on one. It does not write the test bodies — only stubs for cases that have none.Flags
--check (flag, default: false)
Compare and exit non-zero on drift. Writes nothing.
 
--dry-run (flag, default: false)
Report what would be written without writing it.
 
--spec-dir (path, default: spec_directory from jui.config.json)
Spec directory to scan.
Example
# measured on a fixture: 2 cases declared, two platforms
$ jsonui-test generate unit-stubs --check # neither side implemented
unit contracts: 2 case(s) declared across 1 spec(s) scanned
android: declared 2, implemented 0, missing 2, undeclared 0 (0 file(s) read)
MISSING sendsOnTap (declared, no implementation)
$ echo $?
1
 
$ jsonui-test generate unit-stubs --check # implemented on one platform only
ios: declared 2, implemented 2, missing 0, undeclared 0 (1 file(s) read)
android: declared 2, implemented 0, missing 2, undeclared 0 (0 file(s) read) -> exit 1
 
$ jsonui-test generate unit-stubs --check # a test the spec does not name
android: declared 2, implemented 3, missing 0, undeclared 1 (1 file(s) read)
UNDECLARED zzUndeclared (implemented, declared nowhere) -> exit 1
 
$ jsonui-test generate unit-stubs --check # both sides agree
android: declared 2, implemented 2, missing 0, undeclared 0 (1 file(s) read) -> exit 0
 
# on a project that declares none, it says so rather than reporting success:
unit contracts: 0 case(s) declared across 103 spec(s) scanned
no platforms to compare — nothing was checked (not: nothing drifted)
See also: jsonui-test validate
jsonui-testjsonui-test report
jsonui-test report --format junit|html [-o <out>] <results.json>...
Convert the drivers' results JSON (results.schema.json) into a JUnit XML or an HTML report for CI. Several input files merge into one report.Flags
--format (string, required)
`junit` or `html`. Short form `-f`.
 
--output (string, default: report.xml / report.html)
Output file path. Short form `-o`.
Example
$ jsonui-test report tests/artifacts/web/latest/results.json --format junit -o report.xml
See also: jsonui-test validate · jsonui-test artifacts pull
jsonui-testjsonui-test artifacts pull
jsonui-test artifacts pull [--platform ios|android|web|all] [--xcresult <path>] [--serial <serial>] [--out <dir>] [--config <file>] [--clean] [--json]
Collect run evidence — iOS xcresult attachments, Android on-device files, web Playwright videos / traces / screenshots — into `tests/artifacts/<platform>/<stamp>/` and refresh the `latest` symlink. Configured under `test.artifacts` in jui.config.json; `mock serve --artifacts` runs it after each run-target. The Android leg is scoped to one app since jsonui-cli 1.8.32 and requires `test.artifacts.android.appId`; without it the command stops with a config error rather than guessing.Flags
--platform (string, default: all)
Platform to pull from: `ios` / `android` / `web` / `all`.
 
--xcresult (string)
Explicit .xcresult path or glob (overrides `test.artifacts.ios.xcresult`).
 
--serial (string)
adb device serial (overrides `test.artifacts.android.serial`). Selects the device, not the app — see `--clean`.
 
--out (string, default: tests/artifacts)
Output dir (overrides `test.artifacts.dir`).
 
--config (string, default: jui.config.json)
Config file.
 
--clean (flag, default: false)
After pulling, remove the app's OWN artifact directories from the Android device — its external files dir and the uninstall-surviving mirror `/data/local/tmp/jsonui-artifacts/<appId>`, which android driver 1.8.9 and later write. A device still running an older driver mirrors into the flat root instead. It never removes the flat legacy mirror `/data/local/tmp/jsonui-artifacts`, which has no app dimension: on a shared device that path holds every app's suites side by side, and one project's `pull --clean` was measured taking 74 directories and 71 MB of another project's runs off the device. `--serial` does not help there — it narrows the device, not the app. `artifacts prune-legacy` is the one command that empties that mirror, and it lists before it deletes.
 
--json (flag, default: false)
Print the result as a single JSON object.
Example
$ jsonui-test artifacts pull --platform web
See also: jsonui-test artifacts prune-legacy · jsonui-test artifacts status · jsonui-test report
jsonui-testjsonui-test artifacts status
jsonui-test artifacts status [--config <file>] [--json]
Show the resolved artifacts config — artifacts dir, iOS xcresult source, Android app id and serial — and how many files already exist there.Flags
--config (string, default: jui.config.json)
Config file.
 
--json (flag, default: false)
Print the status as a single JSON object.
Example
$ jsonui-test artifacts status
See also: jsonui-test artifacts pull
jsonui-testjsonui-test artifacts prune-legacy
jsonui-test artifacts prune-legacy [--serial <serial>] [--yes] [--config <file>] [--json]
The one deliberate exit from the flat legacy Android mirror `/data/local/tmp/jsonui-artifacts`. `artifacts pull --clean` never touches that path — it has no app dimension, so on a shared device its entries can belong to any app that ran with an android driver below 1.8.9 — and a device still running such a driver keeps refilling it. Once every app on the device writes the scoped mirror, a person runs this to close the migration. The default is a dry run that lists what would go; `--yes` deletes. Only suite-shaped entries are removed: package-shaped entries are other apps' scoped mirrors, and neither they nor the root are ever touched. The command cannot tell whether an older driver is still in use on the device — that judgement is why the dry run is the default.Flags
--serial (string)
adb device serial (overrides `test.artifacts.android.serial`). Required when more than one device or emulator is attached — adb refuses to choose.
 
--yes (flag, default: false)
Actually delete the listed suite directories. Without it the command only lists them.
 
--config (path, default: jui.config.json)
Config file to read `test.artifacts.android` from.
 
--json (flag, default: false)
Print the result as a single JSON object (`root`, `suites`, `packages`, `deleted`, `dryRun`, `skipped`).
Example
$ jsonui-test artifacts prune-legacy # dry run: lists the suite dirs it would delete
Legacy mirror: /data/local/tmp/jsonui-artifacts
suite dirs: none
skipped (adb: adb: more than one device/emulator)
$ echo $?
1
# measured on a machine with several devices attached: adb refuses to choose, so the
# listing is never reached and --serial is required. Deleting (--yes) is not shown here
# because it was deliberately not run on a shared device.
See also: jsonui-test artifacts pull · jsonui-test artifacts status
jsonui-testjsonui-test pregrant
jsonui-test pregrant [--platform ios|android] [--app-id <id>] [--serial <serial>] [--bundle-id <id>] [--udid <udid>] [--config <file>] [--force] [<paths>...]
Prepare the device before a run so the `launch` declarations in your tests can hold. iOS arm: grants the simulator photo permission (`photos-add`) to the UITest runner for every reachable `addMedia` step, so no permission dialog interrupts the run. Android arm (cli 1.8.0): collects every `deny` under `launch.permissions` across the scanned files (`unset` is not collected) and runs `pm revoke` for those permissions on the app before the instrumentation starts — the per-run denied baseline that driver 1.8.5's `deny` assertion checks against. Both arms run by default and each prints its own count; the exit code is non-zero when either arm fails. When the app is not installed on the device, the Android arm refuses rather than guesses, so an unrelated `pm` failure never reads as a satisfied `deny`; a permission the app does not request is reported as vacuously satisfied.Flags
--platform (string, default: both)
Limit the run to one platform: `ios` or `android`. Default: both — each arm no-ops when its scan finds nothing.
 
--app-id (string)
Android application id to revoke on. Default: `test.install.android.applicationId`, falling back to `test.artifacts.android.appId`.
 
--serial (string)
adb device serial. Default: `test.artifacts.android.serial`, or the single connected device — with several devices connected and no serial, the arm stops and lists them.
 
--bundle-id (string)
UITest target bundle id (`.xctrunner` is appended automatically). Default: `test.install.ios.uitestBundleId` from the config.
 
--udid (string)
Simulator UDID. Default: the single booted simulator.
 
--config (string, default: jui.config.json)
Config file.
 
--force (flag, default: false)
iOS arm: grant even when no `addMedia` usage is found.
Example
$ jsonui-test pregrant --platform android tests/
pregrant(android): revoked 1 permission(s) on com.example.myapp: android.permission.ACCESS_FINE_LOCATION (1 test file(s) declare deny)
 
$ jsonui-test pregrant tests/
pregrant: granted photos-add to com.example.myapp.UITests.xctrunner on 1A2B3C4D-0000-0000-0000-000000000000 (2 test file(s) use addMedia)
pregrant(android): 0 deny declaration(s) across 3 file(s) — nothing to revoke
 
$ jsonui-test pregrant --platform android tests/
pregrant(android): cannot determine which permissions 'com.example.myapp' requests on emulator-5554 — is the app installed? Install it first, then pregrant. Refusing to guess: an unrelated pm failure must not read as a vacuously-satisfied deny.
$ echo $?
1
See also: jsonui-test validate
jsonui-docjsonui-doc init
jsonui-doc init spec|component
Create a specification template: `init spec` writes a new screen specification, `init component` a new component specification. The argument is the kind of template, not a path — there is no project-level initialisation here (that is `jui init`).
jsonui-docjsonui-doc generate spec
jsonui-doc generate spec <spec-or-directory> [-o <out>] [--format markdown|html] [--layouts-dir <dir>]
Generate documentation for a screen spec. The argument is a `.spec.json` file or a directory of them (batch). For a single file the format is inferred from the output extension, else Markdown; for a directory it is HTML unless `--format markdown` is given. Before 1.8.18 batch mode ignored `--format` and always wrote HTML.Flags
--output (path)
Output file (single spec) or output directory (batch). Short form `-o`.
 
--format (enum, default: single file: inferred from the output extension, else markdown; directory: html)
`markdown` or `html`. Honoured for a single file and, since 1.8.18, for a directory.
 
--layouts-dir (path)
Shared layouts directory, for resolving `layoutFile` imports. Auto-detected from jui.config.json when omitted.
Example
$ jsonui-doc generate spec docs/screens/json -o out --format markdown
Generating markdown for 103 spec files...
Generated: 103 files
 
$ jsonui-doc generate spec docs/screens/json -o out
Generating html for 103 spec files...
Generated: 103 files
See also: jsonui-doc validate spec
jsonui-docjsonui-doc generate component
jsonui-doc generate component <spec-or-directory> [-o <out>] [--format markdown|html]
Generate documentation for a component spec. The argument is a `.component.json` file or a directory of them (batch). For a single file the format is inferred from the output extension, else Markdown; for a directory it is HTML unless `--format markdown` is given. Before 1.8.18 batch mode ignored `--format` and always wrote HTML.Flags
--output (path)
Output file (single spec) or output directory (batch). Short form `-o`.
 
--format (enum, default: single file: inferred from the output extension, else markdown; directory: html)
`markdown` or `html`. Honoured for a single file and, since 1.8.18, for a directory.
Example
$ jsonui-doc generate component docs/components/json -o out --format markdown
Generating markdown for 6 component files...
Generated: 6 files
jsonui-docjsonui-doc generate html
jsonui-doc generate html <input-dir> [-o <out>] [-t <title>] [-d <docs>] [--app NAME:DIR] [--config <path>] [--layouts-dir <dir>] [--allow-partial] [--with-checks]
Render the whole documentation site — test files, specs, API docs, Figma assets — as static HTML. Since 2026-07-29 a page that fails to generate no longer passes silently: every page is still attempted, the intact ones are written, and the run ends with a summary naming each failure, the input file behind it, and a non-zero exit code. Failed pages get a placeholder carrying the reason, so a link in the index never dead-ends. This command also writes OUTSIDE `-o`: before building the site it regenerates, in the SOURCE tree, `<docs>/<screens|requirements>/html/` and `.../md/`, plus `<docs>/components/html/` and `.../md/` — for the root scope and for every `--app` passed in the same run. Two lanes pointing `-o` at different directories are therefore NOT isolated from each other: whichever runs last leaves its version in the source tree. The paths actually written are printed at the end of the run. The help declares this since jsonui-cli 1.8.52. Since jsonui-cli 1.8.66 the flow diagram on the site is drawn from the screen specs and the flow tests are checked against it: a forward transition a flow test performs that no spec declares prints one `ERROR [doc-diagram]:` line per transition after every page is written, and the command exits 1 — `--allow-partial` does not cover it, since that flag accepts a missing page and this is a wrong spec. The closing line ends with `warnings N`, the tool's own tally, printed even at 0.Flags
--allow-partial (flag, default: false)
Accept an incomplete site: page failures are still reported, but the exit code stays 0. The escape hatch for a run that legitimately cannot fix its inputs yet.
 
--with-checks (flag, default: false)
Run the doc contract checks during generation and put the drift onto the page. Independent of `--allow-partial`: this is about drift being reported, that one is about the page existing at all.
 
--output (path, default: html)
Site output directory. Short form `-o`. NOT the only place this command writes — the run also regenerates doc trees in the source tree; see the purpose above.
 
--title (string, default: JsonUI Test Documentation)
Title for the index page. Short form `-t`.
 
--docs (path)
Directory holding OpenAPI / Swagger files; repeatable. Short form `-d`.
 
--figma (path, default: auto-detected: figma/ next to the input)
Directory holding Figma JSON files. Short form `-fig`.
 
--app (string)
An app and its docs directory as `name:path`; repeatable, for multi-app documentation. Each app resolves its own `jui.config.json` from its directory rather than sharing one found above the tests tree — the `--config` help names that as the default alongside the walk-up.
 
--config (path, default: walk up from the input directory, or per --app directory)
The `jui.config.json` to read `unitContracts` from. Name it when the config governing the specs is not the first one above the tests tree — the released help gives the split-tree case, where an unrelated config sits at the repository root. Since jsonui-cli 1.8.41.
 
--layouts-dir (path, default: auto-detected per spec from jui.config.json)
Override the layouts directory used to resolve `layoutFile` imports.
Example
$ jsonui-doc generate html tests -o html
Component pages: 6 generated, 0 failed
Generated 174 HTML files this run (screens 120 / flows 48 / unitContracts not read / warnings 0)
Open html/index.html to view documentation
 
# a run with a page that could not be generated ends differently:
1 page(s) failed to generate:
API doc Broken table [db/broken_table.json]: unterminated string
$ echo $?
1
See also: jsonui-doc generate spec · jsonui-doc validate spec
jsonui-docjsonui-doc generate doc
jsonui-doc generate doc [-f <test file>] [-o <out>] [--format markdown|html] [--schema]
Generate documentation for a test file, in Markdown or HTML. `--schema` generates the schema reference instead of test documentation.See also: jsonui-doc generate html · jsonui-test generate
jsonui-docjsonui-doc generate mermaid
jsonui-doc generate mermaid <tests dir> [-o <out.html>] [-t <title>] [-s <screens dir>] [--specs <spec dir>] [--layouts-dir <dir>]
Draw the Mermaid flow diagram from the screen SPECS — each spec's `transitions[].destination`, plus `test.appOwnedScreens[].transitions` for a screen with no spec — and check the flow tests under the tests directory against it. Since jsonui-cli 1.8.66; before it the diagram was drawn from the flow tests and a face with none got no diagram. A forward transition a flow test performs that no spec declares is an `ERROR [doc-diagram]:` line and the command exits 1. Without `-o` the Mermaid source goes to stdout. `--layouts-dir` is what lets it tell screens from Collection cells and partials, so sub-areas are not drawn as screens. The diagram needs the classifier from `jui_cli`; where that package cannot be imported the command exits 1.Flags
--specs (path, default: `spec_directory` of the governing jui.config.json)
The screen spec directory the diagram is drawn from. Flow tests under the input directory are checked against it. Since jsonui-cli 1.8.66.
 
--output (path)
Output HTML file path. Short form `-o`. Without it the Mermaid source is written to stdout.
 
--title (string, default: Flow Diagram)
Title of the diagram page. Short form `-t`.
 
--screens (path, default: auto-detected)
Path to the screens directory. Short form `-s`.
 
--layouts-dir (path, default: auto-detected from jui.config.json)
Path to the layout tree, used to tell screens from Collection cells and partials so sub-areas are not drawn as screens.
Example
$ jsonui-doc generate mermaid tests --specs docs/screens/json -o diagram.html
 
# a flow test that reaches a screen its spec does not declare:
ERROR [doc-diagram]: flow test "checkout" (flows/checkout.test.json) transitions item_detail -> cart: item_detail's spec declares no transition to cart
$ echo $?
1
See also: jsonui-doc generate html · jsonui-doc generate doc · jsonui-test generate
jsonui-docjsonui-doc generate adapter
jsonui-doc generate adapter ios|android|web [-o <dir>] [-n <name>] [-a <actions.json>]
Emit the platform adapter scaffold for one target. `-n` namespaces the generated project, `-a` points at a JSON file defining custom actions.See also: jsonui-doc generate component
jsonui-docjsonui-doc validate spec
jsonui-doc validate spec <spec-or-directory>
Validate a screen specification against the schema and the project's rules. The argument is a `.spec.json` file or a directory, in which case every spec under it is validated. This checks the spec itself — it does not compare it with any generated document.See also: jsonui-doc generate spec
jsonui-docjsonui-doc validate component
jsonui-doc validate component <file>
Validate one component specification file (`.component.json`).See also: jsonui-doc validate spec · jsonui-doc init component
jsonui-docjsonui-doc rules
jsonui-doc rules init [-o DIR] [--flutter] | jsonui-doc rules show [-d DIR]
Manage project-local documentation rules. `init` writes a template `.jsonui-doc-rules.json`; `show` prints the effective ruleset (base plus custom).Flags
--output (path, default: current directory)
`init` only: where to write the file. Short form `-o`.
 
--flutter (flag, default: false)
`init` only: include Flutter-specific rules (Scaffold, AppBar, lifecycle methods).
 
--directory (path, default: current directory)
`show` only: directory to search for the config file. Short form `-d`.
jsonui-docjsonui-doc figma fetch
jsonui-doc figma fetch [<file key>] [--url <url>] [-o <out.json>] [-t <token>] [--node-ids ...] [--pages] [--images]
Fetch a Figma file into JSON. The file key can come from `--url` directly. The token defaults to the `FIGMA_TOKEN` environment variable; `--plan` throttles requests to the rate limit of your Figma plan.See also: jsonui-doc figma images
jsonui-docjsonui-doc figma images
jsonui-doc figma images <figma.json> [-k <file key>] [-t <token>] [--plan starter|pro|org|enterprise]
Download the images referenced by a fetched Figma JSON file. The file key defaults to the one inferred from the filename.See also: jsonui-doc figma fetch
jsonui-docjsonui-doc check
jsonui-doc check [<filter>] [--list] [-p <project>]
Run the declared contract checks — a real database or an implementation's OpenAPI document against the docs. It executes only the commands declared in jui.config.json, and `--list` shows what would run (name, type, exact command) without running it. The filter restricts the run to `db` / `api` / `db:<name>` / a declared check name.See also: jsonui-doc validate spec
Exit code conventions0 = success. 1 = generic failure. 2 = spec validation failure. 3 = drift detected by verify. 4 = platform build failure. 5 = test failure. Any code ≥128 is a signal termination (not interesting for CI). The point of having distinct codes is letting CI branch on cause without parsing stderr.
Environment variablesJSONUI_CLI_DIR overrides the default `~/.jsonui-cli` install location. JSONUI_MCP_DIR overrides the MCP server directory. JSONUI_CLI_PATH, when set, wins over both for the MCP fallback chain — useful when you have the CLI installed somewhere non-default and want agents to find it without further config. Individual commands also honour tool-scoped vars (RBENV_VERSION for sjui / kjui / rjui); those are listed per-command.
Command stabilityCore flags (--file, --fail-on-diff, --platform) are stable across majors. One-shot migration helpers such as `jui migrate-layouts` are adoption tooling rather than pipeline commands — they may change shape without a major bump, so keep them out of CI. Everything the reference documents for build / verify / generate is what your pipeline can safely depend on.
API model commands (since 2026-05)`jui g api` regenerates API DTOs + Domain scaffolds from swagger files under `api_directory` (default `docs/api`) without running the full build. Flags: `--dry-run` (filter preview JSON; also the backing for the MCP `preview_api_model_sync` tool) / `--fail-on-diff` (CI exit non-zero on drift) / `--platform ios|android|web` / `--json`. The new `jui ls` discovery group has two subcommands: `jui ls api-specs` lists swagger files with title / version / schema_count / endpoint_count metadata; `jui ls api-models` lists generated DTO + Domain scaffold files per platform, including orphans (schema deleted from swagger but file still on disk). `--json` makes that list machine-readable, which is the CI-side hook for orphan cleanup. `jui verify --fail-on-diff` now also re-generates API DTOs in-memory and compares them byte-equal against disk, so it covers DTO drift in addition to layout drift.
Renderer SSoT commands (since 2026-07)The Renderer SSoT rollout added the `jui conformance` command family. `jui conformance generate` reads `shared/core/attribute_definitions.json` and emits fixture layouts + screen tests per component × attribute; `jui conformance report` collates the per-platform results into a matrix (rows = fixtures, columns = iOS / Android / Web) and prints where the three implementations disagree; `jui conformance compat-doc --platform android` renders KotlinJsonUI's `attribute_compatibility.md` from the SSoT + latest conformance results, replacing the hand-maintained table with a deterministic `@generated` artifact. `jui conformance coverage` (2026-07-29) answers a different question — not whether a fixture exists, but whether the converter actually reads the attribute — by scanning converter source rather than output, because a fixture for a dropped attribute renders a blank screen that matches its blank baseline and passes. It is a ratchet backed by `conformance/coverage.json`: a gap missing from the ledger fails, and so does a ledger entry whose gap is gone, otherwise the file rots into a list of things that used to be broken. The first measurement reported 209 gaps, and that number turned out to be mostly a declaration problem: attributes with no `platform` narrowing were counted against all three platforms, iOS-only concepts included. What followed (2026-07-31) fixed the input rather than the meter — platform-specific attributes now declare their surface, with a `platform_reasons` field carrying *why* an excluded platform is excluded; the scan became per component × attribute pair instead of matching bare attribute names, which surfaced 62 real gaps the flat scan had been hiding; and that backlog was then implemented. The ledger stood at 15 pairs when that backlog was cleared (5 genuinely unimplemented, UIKit-facing surfaces like the TextField accessory family, and 10 legacy aliases); measured with the tool itself at jsonui-cli 1.8.101, `jui conformance coverage` checks 1145 declared attribute/platform pairs and records 3 gaps (2 unimplemented, 1 legacy alias), each entry carrying the file-level evidence for why it is there. The `mode: uikit` attributes still sit outside the scan — SwiftJsonUI's Swift runtime reads those directly, and the scanner maps `uikit` to no hosted platform on purpose — a blind spot, not a clean bill of health; 85 was their count on 2026-07-31, and the scanner prints no current one. `jui generate attr-bindings [--lang swift|kotlin|ruby|all]` emits typed attribute extractors for the same SSoT — Swift structs / Kotlin data classes / Ruby modules — so converters read typed objects instead of raw JSON. Related: `normalizeLayouts` in `jui.config.json` is default-true since 2026-07 (opt out with `"normalizeLayouts": false`); it rewrites aliases to canonical names and stamps a `$jui` marker on distributed layouts, and `attribute_validator` treats that marker as a known key.
Implementation contract checks (since 2026-07)`jsonui-doc check` runs the checks declared in `jui.config.json` under the top-level `"checks"` array. Positional filters narrow scope: `jsonui-doc check db` runs only DB checkers, `check api` only API checkers, `check db:main` runs checkers targeting the DB named `main`, and a bare checker name runs just that entry. `--list` prints what would run without executing anything. Exit codes: `0` = all OK, `1` = mismatch found (drift), `2` = execution error (connection failure, timeout, malformed plugin output). Findings downgraded via `downgrade_to_warning` carry the `warning` status and are excluded from the mismatch count, so they appear in the report and its HTML badges without changing the exit code. Reports are saved under `docs/**/.check-report.json` and `jsonui-doc generate html` renders them as an "Implementation contract" page when present. `jsonui-doc generate html --with-checks` is a convenience sugar that runs check → generate in one invocation. Two builtin checkers ship: `builtin:openapi-diff` compares the impl-declared OpenAPI (adapter-type: an `impl_openapi_command` you declare, e.g. a FastAPI export script) against `docs/api/*.json`; `builtin:db-schema` compares the real database schema (MySQL / PostgreSQL / SQLite via SQLAlchemy inspector, or an adapter-type `dump_command`) against `docs/db/*.json`. Two plugin tiers: `type: checker` is the full-checker type (your command emits the result JSON contract directly), and adapter-type entries hand off comparison to the builtin engine. Worked examples live in /concepts/db-schema-check (the builtin DB checker end to end) and /concepts/implementation-contract-check (confidence levels and the two plugin tiers).
Test tooling commands (since 2026-07)The `jsonui-test` CLI ships inside jsonui-cli. `jsonui-test validate <files/dirs>` schema-checks screen / flow tests + descriptions (exit 0 = pass, 1 = schema violation); when `jui.config.json` carries a `test.install` block, a passing validate also flatten-installs the tests into the platform test dirs — success-gated (broken tests are never distributed), full-sync (stale files removed first), and a screen-name collision aborts with exit 1. `--no-install` skips the install; `--config` selects the config file; no `test.install` block ⇒ validate-only. `jsonui-test generate test screen|flow <name>` and `generate description screen|flow <name> <case>` scaffold files. `jsonui-test report <results…> --format junit|html -o <out>` turns the drivers' results JSON (results.schema.json) into JUnit / HTML for CI. `jsonui-test mock generate` writes OpenAPI mocks into `<mockDir>/generated/` (`--check` compares scenario bodies against the schema for their status code; `--update-default` refreshes only the default body) and `mock serve` runs the local mock server, validating request bodies and query parameters against the contract as it goes — see the API Mock guide. `jsonui-test artifacts pull` collects run evidence — iOS xcresult attachments, Android on-device files, and web Playwright videos / traces / screenshots — into `tests/artifacts/<platform>/<stamp>/` with a `latest` symlink (`--platform ios|android|web`; `artifacts status` shows the resolved config; `mock serve --artifacts` auto-pulls after each run-target; configured via `test.artifacts` in jui.config.json). `jsonui-test pregrant` prepares the device before a run: the iOS arm grants `photos-add` to the UITest runner for every reachable `addMedia` step, and the Android arm (cli 1.8.0) revokes every `deny` under `launch.permissions` on the app before the instrumentation starts — the baseline that driver 1.8.5's `deny` assertion checks against (`--platform ios|android` limits it to one arm). Eight of these are also exposed over MCP as Group F; the long-running `mock serve` and `pregrant` stay CLI-only.
Keep reading
jsonui-cli overviewThe landing page this reference drills into./tools/cli
Writing your first specCalls most of these commands in sequence./guides/writing-your-first-spec