← 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.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.FlagsExampleSee also: jui g project · jui buildGenerate Layout JSON plus ViewModel / Repository / UseCase declaration stubs from every screen spec, or from a single spec with --file.FlagsExampleSee also: jui build · jui verifyCreate screen-spec templates for one or more new screens (PascalCase names).FlagsExampleSee also: jui g projectGenerate a custom-component converter — from a name + --attributes, from a component spec with --from, or from every component spec with --all.FlagsExampleSee also: jui g projectGenerate DTO + Domain models from the swagger files (api.schemas filter applies). --dry-run previews without writing.FlagsExampleSee also: jui ls api-specs · jui ls api-modelsGenerate typed attribute-extraction code from the SSoT attribute_definitions.json. Deterministic output — re-running is byte-identical.FlagsExampleSee also: jui conformance coverageBuild 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.FlagsExampleSee also: jui verify · jui lint-generated · jui lint-stringsCompare 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.FlagsExampleSee also: jui buildList 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.FlagsExampleSee also: jui buildList swagger / OpenAPI files with parsed metadata (MCP discovery command).FlagsExampleSee also: jui g apiList generated DTO + Domain scaffold files per platform (MCP discovery command).FlagsExampleSee also: jui g apiOne-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.FlagsExampleSee also: jui initSync 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.FlagsExampleSee also: jui buildStart 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.FlagsExampleSee also: jui hotload status · jui hotload stopPrint whether a hotload server is running.ExampleSee also: jui hotload listenStop the running hotload server.ExampleSee also: jui hotload listenGenerate the conformance fixtures/ directory + manifest.json from attribute_definitions.json.FlagsExampleSee also: jui conformance reportMerge results/*.results.json into REPORT.md — the cross-platform compat matrix.FlagsExampleSee also: jui conformance gateRender REPORT.md and fail on regressions — the CI gate, runnable locally.FlagsExampleSee also: jui conformance reportGenerate the @generated attribute-compatibility markdown from conformance results.FlagsExampleSee also: jui conformance reportHash artifacts/<platform>/*.png into baselines/<platform>.hashes.json (perceptual-hash screenshot baselines).FlagsExampleSee also: jui conformance gateCheck declared attributes against what each platform's converters actually read; --update rewrites coverage.json from the current gaps (reasons preserved).FlagsExampleSee also: jui conformance effectRecord / check which fixtures render differently from their control (the attribute-effect ledger).FlagsExampleSee also: jui conformance coverageCompare 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.FlagsSee also: jui conformance baseline update · jui conformance reportCompare whether each attribute is active across platforms. At least two platforms are required — activeness agreement measured on one platform compares nothing.FlagsSee also: jui conformance effect · jui conformance inert-auditThe 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.FlagsSee also: jui conformance cross-effect · jui conformance coverageFor 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.FlagsSee also: jui conformance effect · jui conformance parityLocalize 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.FlagsExampleSee also: jui build · jui lint-generatedCI-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.FlagsExampleSee also: jui build · jui verifyInitialize 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.FlagsExampleSee also: sjui setup · sjui generateWire 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 listenGenerate 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.FlagsExampleSee also: sjui destroyDelete 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.FlagsExampleSee also: sjui generateGenerate 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.FlagsExampleSee also: jui buildConvert 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.FlagsSee also: sjui validateValidate 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.FlagsExampleSee also: jui verifyWatch 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`.FlagsSee also: sjui build · jui hotload listenInitialize 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.FlagsExampleSee also: kjui setupSet 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 generateGenerate 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.FlagsExampleSee also: kjui setupInitialize 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).Build React components from JSON layouts. Day to day this runs as part of `jui build --web-only` rather than directly.See also: jui buildWatch JSON files and auto-rebuild on changes.HotLoader control (listen / stop / status). A bare `rjui hotload` defaults to listen.Scaffold a view / component / collection cell / converter (short forms: v / c / col / conv).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.FlagsExampleSee also: jsonui-test mock generate · jsonui-test reportWrite 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.FlagsExampleSee also: jsonui-test mock serve · jsonui-test validateServe 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.FlagsSee also: jsonui-test mock generate · jsonui-test artifacts pullAsk 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.FlagsExampleSee also: jsonui-test mock serveScaffold 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 docHold 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.FlagsExampleSee also: jsonui-test validateConvert the drivers' results JSON (results.schema.json) into a JUnit XML or an HTML report for CI. Several input files merge into one report.FlagsExampleSee also: jsonui-test validate · jsonui-test artifacts pullCollect 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.FlagsExampleSee also: jsonui-test artifacts prune-legacy · jsonui-test artifacts status · jsonui-test reportShow the resolved artifacts config — artifacts dir, iOS xcresult source, Android app id and serial — and how many files already exist there.FlagsExampleSee also: jsonui-test artifacts pullThe 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.FlagsExampleSee also: jsonui-test artifacts pull · jsonui-test artifacts statusPrepare 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.FlagsExampleSee also: jsonui-test validateCreate 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`).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.FlagsExampleSee also: jsonui-doc validate specGenerate 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.FlagsExampleRender 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.FlagsExampleSee also: jsonui-doc generate spec · jsonui-doc validate specGenerate 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 generateDraw 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.FlagsExampleSee also: jsonui-doc generate html · jsonui-doc generate doc · jsonui-test generateEmit 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 componentValidate 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 specValidate one component specification file (`.component.json`).See also: jsonui-doc validate spec · jsonui-doc init componentManage project-local documentation rules. `init` writes a template `.jsonui-doc-rules.json`; `show` prints the effective ruleset (base plus custom).FlagsFetch 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 imagesDownload 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 fetchRun 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
juijui init
Also: jui ijui init --project-name <name> [--ios PATH] [--android PATH] [--web PATH]--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.$ jui init --project-name MyApp --ios ios/ --web web/juijui g project
jui g project [--file SPEC] [--ios-only|--android-only|--web-only]--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.$ jui g project --file login.spec.json --web-onlyjuijui g screen
jui g screen <Name> [<Name> ...] [--display-name <text>]names (positional, required, default: —) Screen names (PascalCase). --display-name (string, default: —) Display name (single screen only).$ jui g screen Login Settingsjuijui g converter
jui g converter [<Name>] [--from SPEC | --all]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).$ jui g converter --from rating-stars.component.jsonjuijui g api
jui g api [--dry-run] [--platform ios|android|web]--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).$ jui g api --dry-run --platform webjuijui g attr-bindings
jui g attr-bindings [--lang swift|kotlin|ruby|all] [--out DIR]--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.$ jui g attr-bindings --lang kotlinjuijui build
Also: jui bjui build [--clean] [--ios-only|--android-only|--web-only | --platform ios|android|web] [--allow-partial] [--lint-strings]--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.$ jui build --web-only --cleanjuijui verify
jui verify [--fail-on-diff] [--file SPEC] [--platform <p>] [--detail] [--json PATH]--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.$ 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 } }juijui screens
jui screens [--json] [--layouts-dir <dir>]--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.$ jui screensScreen 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)juijui ls api-specs
jui ls api-specs [--json]--json (flag, default: false) Emit JSON (used by MCP wrappers).$ jui ls api-specs --jsonjuijui ls api-models
jui ls api-models [--platform ios|android|web] [--json]--platform (enum, default: all) Restrict to a single platform. --json (flag, default: false) Emit JSON (used by MCP wrappers).$ jui ls api-models --platform iosjuijui migrate-layouts
jui migrate-layouts [--from ios|android|web] [--dry-run]--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.$ jui migrate-layouts --dry-runjuijui sync_tool
jui sync_tool [--from <dir>] [--platform android|ios|web] [--prune] [--dry-run]--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.$ jui sync_tool --platform webjuijui hotload listen
jui hotload listen [--host H] [--port P] [--ws-path PATH]--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.$ jui hotload listenjuijui hotload status
jui hotload status$ jui hotload statusjuijui hotload stop
jui hotload stop$ jui hotload stopjuijui conformance generate
jui conformance generate [--definitions PATH] [--out DIR]--definitions (path, default: bundled SSoT) Path to attribute_definitions.json. --out (path, default: conformance/) Output directory.$ jui conformance generatejuijui conformance report
jui conformance report [--dir DIR] [--results DIR] [--out PATH]--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.$ jui conformance reportjuijui 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]--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.$ jui conformance gate --platform web --no-visualjuijui conformance compat-doc
jui conformance compat-doc --platform <p> -o PATH--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.$ jui conformance compat-doc --platform ios -o Docs/attribute_compatibility.mdjuijui 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]--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.$ jui conformance baseline update --platform androidjuijui conformance coverage
jui conformance coverage [--platform <p>...] [--update]--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.$ jui conformance coverage --platform webjuijui conformance effect
jui conformance effect --platform <p> [--update]--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.$ jui conformance effect --platform ios --updatejuijui conformance parity
jui conformance parity --platform ios|android|web [--env <key>] [--update]--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.juijui conformance cross-effect
jui conformance cross-effect [--platform ios|android|web]... [--update]--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.juijui conformance inert-audit
jui conformance inert-audit [--platform ios|android|web]... [--untriaged-only] [--json <out>]--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.juijui conformance codegen-effect
jui conformance codegen-effect [--platform ios|android|web] [--check C0|C1|C2|C3] [--paired] [--update]--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.juijui lint-strings
jui lint-strings [--json] [--usage] [--update-allowlist] [--allowlist <path>]--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.$ jui lint-strings$ jui lint-strings --usage --jsonjuijui lint-generated
jui lint-generated [--fix] [--verbose] [--max-depth <n>] [--max-lines <n>] [--update-size-baseline] [--fail-on any|size-ratchet|health]--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.$ jui lint-generated --verbose OK: 1217 Missing header: 0 Oversized fns: 3 $ jui lint-generated --update-size-baseline✓ Wrote .jui-size-waivers (3 entries)sjuisjui init
sjui init [--mode all|uikit|swiftui]--mode (enum, default: detected) Initialize mode (all, uikit, swiftui).$ sjui init --mode swiftuiInitializing SwiftJsonUI project in swiftui mode...Created config file: sjui.config.jsonHotloader 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 swiftuiInitializing SwiftJsonUI project in swiftui mode...Config file already exists: sjui.config.jsonInitialization complete!sjuisjui setup
sjui setupsjuisjui generate
Also: sjui gsjui generate <subcommand> <name> [options]<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.# 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 HomeViewsjuisjui destroy
Also: sjui dsjui destroy <type> <name><type> (positional, required) `view` / `partial` / `collection` / `binding`. <name> (positional, required) The generated name, nested paths allowed (`home/dashboard`).# 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 customsjuisjui build
sjui build [--mode all|uikit|swiftui] [--clean] [--no-validate] [--strict]--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.$ sjui build --strict# the tool's own examples (sjui --help):$ sjui build # Build UIKit/SwiftUI files$ sjui build --quiet # Build with only error outputsjuisjui convert
sjui convert <input.json> [output.swift] [--from json] [--to swiftui]<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).sjuisjui validate
sjui validate [files...] [--verbose][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`.# 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 patternsjuisjui watch
sjui watch [--mode all|uikit|swiftui]--mode (enum, default: detected from sjui.config.json) Watch mode (all, uikit, swiftui).kjuikjui init
kjui init [--mode all|xml|compose]--mode (enum, default: detected) Initialize mode (all, xml, compose).$ kjui init --mode xmlInitializing KotlinJsonUI project in xml mode...Created config file: kjui.config.jsonInitialization 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 viewkjuikjui setup
kjui setupkjuikjui generate
Also: kjui gkjui generate [<subcommand> <name>] [--mode xml|compose] [options]--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`.# 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 HomeViewrjuirjui init
rjui initrjuirjui build
rjui buildrjuirjui watch
rjui watchrjuirjui hotload
rjui hotload [listen|stop|status]rjuirjui generate
Also: rjui grjui generate <type> <name>jsonui-testjsonui-test validate
jsonui-test validate <path>... [--no-install] [--no-mock-check] [--config <file>]--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.$ jsonui-test validate tests/✓ 12 screen tests valid✓ 3 flow tests valid✓ mock contract: 166 mocks checked, no driftjsonui-testjsonui-test mock generate
jsonui-test mock generate --swagger <openapi> --out <dir> [--check] [--update-default]--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.$ 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 missingjsonui-testjsonui-test mock serve
jsonui-test mock serve --mock-dir <dir> [--port N] [--no-validate-requests]--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).jsonui-testjsonui-test mock identity
jsonui-test mock identity [--port P] [--config FILE] [--expect-root DIR] [--any-project]--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.# 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 projectmock identity: port 8799 is serving '<owner>', not '<caller>' — another project's mock holds thisport. 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 listeningmock identity: nothing answered on port 8790 (<urlopen error [Errno 61] Connection refused>)$ echo $?2jsonui-testjsonui-test generate
jsonui-test generate test screen|flow <name> | generate description screen|flow <name> <case> | generate branch-tests <screen>jsonui-testjsonui-test generate unit-stubs
jsonui-test generate unit-stubs [--check] [--dry-run] [--spec-dir DIR]--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.# measured on a fixture: 2 cases declared, two platforms$ jsonui-test generate unit-stubs --check # neither side implementedunit contracts: 2 case(s) declared across 1 spec(s) scannedandroid: 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 onlyios: 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 nameandroid: 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 agreeandroid: 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) scannedno platforms to compare — nothing was checked (not: nothing drifted)jsonui-testjsonui-test report
jsonui-test report --format junit|html [-o <out>] <results.json>...--format (string, required) `junit` or `html`. Short form `-f`. --output (string, default: report.xml / report.html) Output file path. Short form `-o`.$ jsonui-test report tests/artifacts/web/latest/results.json --format junit -o report.xmljsonui-testjsonui-test artifacts pull
jsonui-test artifacts pull [--platform ios|android|web|all] [--xcresult <path>] [--serial <serial>] [--out <dir>] [--config <file>] [--clean] [--json]--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.$ jsonui-test artifacts pull --platform webjsonui-testjsonui-test artifacts status
jsonui-test artifacts status [--config <file>] [--json]--config (string, default: jui.config.json) Config file. --json (flag, default: false) Print the status as a single JSON object.$ jsonui-test artifacts statusjsonui-testjsonui-test artifacts prune-legacy
jsonui-test artifacts prune-legacy [--serial <serial>] [--yes] [--config <file>] [--json]--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`).$ jsonui-test artifacts prune-legacy # dry run: lists the suite dirs it would deleteLegacy 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.jsonui-testjsonui-test pregrant
jsonui-test pregrant [--platform ios|android] [--app-id <id>] [--serial <serial>] [--bundle-id <id>] [--udid <udid>] [--config <file>] [--force] [<paths>...]--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.$ 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 $?1jsonui-docjsonui-doc init
jsonui-doc init spec|componentjsonui-docjsonui-doc generate spec
jsonui-doc generate spec <spec-or-directory> [-o <out>] [--format markdown|html] [--layouts-dir <dir>]--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.$ jsonui-doc generate spec docs/screens/json -o out --format markdownGenerating markdown for 103 spec files...Generated: 103 files $ jsonui-doc generate spec docs/screens/json -o outGenerating html for 103 spec files...Generated: 103 filesjsonui-docjsonui-doc generate component
jsonui-doc generate component <spec-or-directory> [-o <out>] [--format markdown|html]--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.$ jsonui-doc generate component docs/components/json -o out --format markdownGenerating markdown for 6 component files...Generated: 6 filesjsonui-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]--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.$ jsonui-doc generate html tests -o html Component pages: 6 generated, 0 failedGenerated 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 $?1jsonui-docjsonui-doc generate doc
jsonui-doc generate doc [-f <test file>] [-o <out>] [--format markdown|html] [--schema]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>]--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.$ 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 $?1jsonui-docjsonui-doc generate adapter
jsonui-doc generate adapter ios|android|web [-o <dir>] [-n <name>] [-a <actions.json>]jsonui-docjsonui-doc validate spec
jsonui-doc validate spec <spec-or-directory>jsonui-docjsonui-doc validate component
jsonui-doc validate component <file>jsonui-docjsonui-doc rules
jsonui-doc rules init [-o DIR] [--flutter] | jsonui-doc rules show [-d DIR]--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]jsonui-docjsonui-doc figma images
jsonui-doc figma images <figma.json> [-k <file key>] [-t <token>] [--plan starter|pro|org|enterprise]jsonui-docjsonui-doc check
jsonui-doc check [<filter>] [--list] [-p <project>]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