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.
| Command | Exit 0 | Exit 1 | Exit 2 | Exit 3 |
|---|---|---|---|---|
zgx check | no errors | type errors | bad usage | luau-lsp not installed |
zgx lint | no findings | findings | bad usage or an unreadable file | |
zgx inspect | success | the app answered with an error | bad usage | no 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.
| Rule | Flags |
|---|---|
untracked-get | state:get() while an element is built is read once |
state-in-builder | ui.state in a component or row is shared by every instance |
timer-interval | ui.every faster than the display refreshes |
tick-tween | a tween re-fired by ui.every that runs most of each tick |
key-reads-state | an each key function reading state goes stale |
per-frame-state | an uncapped animation callback that writes state every frame |
update-copy | state:update(fn) copying the whole value for one change |
computed-sort | a computed that sorts a whole list again each time it runs |
list-key | a positional ui.list over a list state the file removes from or inserts into |
list-measured | a ui.list of fixed heights whose rows wrap text |
canvas-view | a ui.canvas that pans or zooms by drawing again rather than by `view` |
draw-item-state | a ui.canvas draw_item reading state, kept until its item changes |