zgx

CLI reference

Every command of zgx and zgx-run, as their --help prints it, with exit codes and the lint rules.

Each block below is read from the binary’s source when the site builds, so it says what --help says.

Install

LUAU_CXXFLAGS=-DNDEBUG cargo install --git https://github.com/zeronsh/zgx zgx-run
LUAU_CXXFLAGS=-DNDEBUG cargo install --git https://github.com/zeronsh/zgx zgx-script --bin zgx

zgx check needs luau-lsp on the PATH; zgx lint and zgx inspect need nothing else.

zgx-run

usage: zgx-run [--inspect] <app.luau-or-dir> [args…]

Opens the script (a file, or a directory's app.luau) in a window and
reloads it as its files change. A zgx.json beside it sets the window
(title, width, height, min_width, min_height, titlebar: standard, inset or
hidden, glass) and app_id, where ui.persist keeps its data and, on Linux,
the desktop file ID its windows carry (app_id.desktop). Its ui.command
entries are the menu bar. What follows the script is its ui.args, and the
files among them open with it (ui.on_open); with an application_id
(reverse-DNS) in zgx.json, so do those the system opens with it.

--inspect serves devtools for agents: `zgx inspect help` drives the
running app (its tree, states, clicks, typing, keys) over a socket only
this user can reach.

zgx

usage: zgx check [--nonstrict] <file-or-dir>...
       zgx lint [--format text|json] <file-or-dir>...
       zgx lint --rules
       zgx inspect [--app PID] [--json] <command> [args]   (zgx inspect help)

check: type checks zgx Luau scripts with luau-lsp, the bundled ui/anime
types, the standard platform and the new type solver. Strict mode applies
unless a .luaurc (languageMode) or a --!nonstrict comment says otherwise.

lint: zgx's best-practice rules (no luau-lsp needed). A finding names the
cheaper way to write it; `-- zgx-lint: allow(rule)` on its line or the
line above accepts a deliberate exception. It exits 1 when it finds any;
`--format json` prints them as one JSON object for tools.
CommandExit 0Exit 1Exit 2Exit 3
zgx checkno errorstype errorsbad usageluau-lsp not installed
zgx lintno findingsfindingsbad usage or an unreadable file
zgx inspectsuccessthe app answered with an errorbad usageno app answers

zgx inspect

usage: zgx inspect [--app PID] [--json] <command> [args]

Asks a running app serving devtools (`zgx-run --inspect`, or
ZGX_INSPECT_SOCKET=1): with one running, --app is not needed.

  list                                   apps serving devtools
  tree [target] [--depth N] [--budget N] [--all]
  node <target>        find <target>
  state [name] [--full]                  set <name> <json>
  click <target>       type <target> <text>      key <keys>
  scroll <target> <dy> command <id>
  errors               lint               frames [n]
  wait <target> [--text T] [--gone] [--timeout MS]
  snapshot <target> [--width PX] [--out file.png]
  record               record stop [--out gesture.tape]
  replay <target> <gesture.tape>         its pacing, through the window's input
  sleep <ms>           raw <json>
  help                 what the app answers
  mcp                  serve these as MCP tools on stdio, for agents
                       (claude mcp add zgx -- zgx inspect mcp)

Targets: #id, "label or text", role:button, @12, row 3, [2], a >> b.
Text by default, compact; --json prints the answer as JSON.

--json wraps every answer as {"ok":true,"text":…,"data":…} or {"ok":false,"error":…}. The commands are explained in reading and acting, the selector syntax in targets.

Lint rules

zgx lint --rules prints these. -- zgx-lint: allow(rule) on a finding’s line, or the line above it, accepts a deliberate exception.

RuleFlags
untracked-getstate:get() while an element is built is read once
state-in-builderui.state in a component or row is shared by every instance
timer-intervalui.every faster than the display refreshes
tick-tweena tween re-fired by ui.every that runs most of each tick
key-reads-statean each key function reading state goes stale
per-frame-statean uncapped animation callback that writes state every frame
update-copystate:update(fn) copying the whole value for one change
computed-sorta computed that sorts a whole list again each time it runs
list-keya positional ui.list over a list state the file removes from or inserts into
list-measureda ui.list of fixed heights whose rows wrap text
canvas-viewa ui.canvas that pans or zooms by drawing again rather than by `view`
draw-item-statea ui.canvas draw_item reading state, kept until its item changes
Edit on GitHub