Skip to content

CLI surface

The gaia binary is the bundled CLI at .gaia/cli/gaia, shipped in every project. Projects rarely run it directly; skills, hooks, and slash commands run it for them. gaia --help lists the subcommands, and most of them print a help block on --help. A few leaf commands do not: gaia scaffold hook --help, for example, prints an unknown-flag error. To read one in plain words, ask:

Run .gaia/cli/gaia <subcommand> --help and explain each option in plain words.

If you work on the CLI, this page maps its subcommands. The source lives under .gaia/cli/src/, which the release bundle leaves out.

.gaia/cli is a package in the root pnpm (opens in a new tab) workspace. At release, the scrub removes it from the shipped pnpm-workspace.yaml and pnpm-lock.yaml, and every CLI source file is left out of the bundle. The build (pnpm -C .gaia/cli bundle) produces two binaries, and both are committed:

  • .gaia/cli/gaia: the shipped binary. Entry point src/index.ts.
  • .gaia/cli/gaia-maintainer: the maintainer binary, carrying labels docs and the release commands. Entry point src/index.maintainer.ts. Excluded from the bundle. labels docs rewrites the label tables in wiki/concepts/GitHub Labels.md from .gaia/labels.json.

src/index.ts imports no maintainer-only code, so esbuild (the bundler) leaves that code out of the shipped binary.

The committed binaries must match a fresh build from source, and you never rebuild them by hand. When a commit stages CLI source, .gaia/cli/package.json, a .gaia/cli/tsconfig*.json, or the root pnpm-lock.yaml, the pre-commit hook rebuilds both binaries and adds them to the commit. If CLI source has unstaged or untracked edits, it stops the commit and names them, because the build reads the working files and would bake those edits in. Claude stages the edits or sets them aside, then commits again. The hook needs the CLI’s dependencies in .gaia/cli/node_modules, which pnpm install provides; without them it only warns, and CI’s freshness check fails the pull request instead. If that happens, ask:

CI says the committed CLI binaries are stale. Rebuild them from .gaia/cli/src and commit them in a new commit.

Source for each lives in its folder under .gaia/cli/src/.

Per-step handlers for /gaia-init. Each records its state in .gaia/init-state.json so the command can resume.

gaia init strip-branding --title <T>
gaia init configure-i18n --locales <list> --strip <bool>
gaia init configure-data-layer [--casing <camel|snake>] [--query <bool>] [--layer <folder>]
gaia init rename --title <T> --kebab <K>
gaia init wire-statusline
gaia init bootstrap-env
gaia init write-project-config --sandbox-recommended <bool> [--isolation-policy <policy>]
gaia init finalize
gaia init resume [--from-step <N>]

configure-data-layer prints one JSON line naming the files it changed and the commands to run next, and can be re-run after init (for example --query true to add TanStack Query (opens in a new tab) later). init stops with a message outside a scaffolded project root, and in the GAIA source itself.

The engine behind the scaffolder skills: component, hook, route (--data <server|client|query> --service <name> --shape <list|detail>, groups _public and _session, --dry-run), and service (--layer, --mocks, --queries-only).

The steps behind /setup-gaia.

gaia setup status|mark-step|finalize [--force]|bootstrap-env
gaia setup detect-remote|check-admin|warn-existing-tools
gaia setup enable-delete-branch|configure-dependabot-alerts
gaia setup write-isolation-policy <always-worktree|prefer-worktree|prefer-branch>
gaia setup write-sandbox-recommendation <true|false>
gaia setup record <id> <applied|declined|not-applicable>
gaia setup drift [--features <path>]

drift lists setup features from .gaia/setup-features.json with no recorded decision; /setup-gaia and /update-gaia act on it.

detect, apply, record, and status for the decision on whether Bash runs inside the operating system’s sandbox. Exits 2 on invalid arguments.

gaia labels sync brings a repository’s labels in line with .gaia/labels.json. See Labels.

The label tables in wiki/concepts/GitHub Labels.md come from the same file, through the maintainer binary’s labels docs. labels docs --check writes nothing and fails when the page no longer matches the file. The pre-commit hook runs it when a commit stages .gaia/labels.json or the page, and the required CI check runs it too. Regenerating is still a step Claude takes on request, so after any change to .gaia/labels.json, ask:

I changed .gaia/labels.json. Regenerate the label tables in wiki/concepts/GitHub Labels.md with the maintainer CLI's labels docs command and stage the result.

gaia packages sync-settings [--check] generates <package>/.claude/settings.json from the root settings and the package’s overlay. The pre-commit hook checks it.

The merge steps behind /update-gaia: merge-package, merge-workspace, and merge-audit-ci (each merges its file field by field and reports what it applied and what conflicted), merge-region and regen-regions (for generated regions, below), and lockfile-command.

A generated region is a block inside a shipped file, between a pair of marker comments, that a GAIA script writes rather than a person. Today there is one: the gaia:audit-remit block in each Code Audit Team member’s agent file, written from the .gaia/audit-ci.yml roster by .gaia/scripts/write-audit-remits.sh. /update-gaia regenerates a region by running its script instead of merging the text line by line.

Every region must be registered in .gaia/cli/src/release/region-registry.ts. Nothing else in the build detects one, so an unregistered region gets merged like hand-written text.

The steps behind /update-deps: run (finds what is outdated and sorts it into update waves), decline (snoozes an update), global-tools, advisories, advisory-landed, dismiss-alert, write-security-cache, and check-security-override.

The /gaia-fitness linter: check (the rule catalog and grade), rules, inventory (also used by /gaia-cleanse), builtins, apply (the only subcommand that writes a fix, and only to a file you own that has not changed since the check), and render-card.

For /gaia-codify: codify-tally counts how often each finding recurs, and codify-ledger records the ones you declined.

gaia residue-tally, gaia residue-cursor, gaia residue-record

Section titled “gaia residue-tally, gaia residue-cursor, gaia residue-record”

For /gaia-residue: residue-tally counts the residuals to triage, residue-cursor remembers where a run stopped so the next one resumes there, and residue-record records what you decided for each residual (dismissed, kept, or suppressed). GAIA_RESIDUE_CAP sets one batch’s size.

gaia react-perf reduce <raw.json> [--frame-budget-ms <n>] condenses a raw render recording into a summary for /gaia-react-perf.

The steps behind /gaia-wiki, including chain, which runs the whole /gaia-wiki chain on one branch with one pull request. The full subcommand table and the chain steps are in Contributor wiki content.

The fire-and-forget usage ping sent by /gaia-init, /setup-gaia, and /update-gaia. GAIA_TELEMETRY_PING_DISABLE=1 suppresses it.

Test files sit beside their source under .gaia/cli/src/, run by Vitest (opens in a new tab). Fixtures live under .gaia/cli/test-fixtures/. GAIA’s shell layer is tested by bats (opens in a new tab) suites under .gaia/scripts/tests/ and .gaia/tests/.

When a commit touches .gaia/cli/, the Quality Gate adds the CLI’s typecheck and lint. The required CI check also runs the CLI’s Vitest tests (see CI workflows). Before the first audit on your branch, the verification runner runs the bats suites that cover the files you changed (see Before you open a pull request). To run any of them on demand, ask:

Run the CLI's typecheck, lint, and Vitest tests, then the bats suites that cover the files I changed, under bash 5. Fix anything that fails.

The prompt asks for bash 5 because CI uses it. macOS ships bash 3.2, where some failing assertions pass silently, so a local pass there is weaker than CI’s.

Some namespaces are deliberately undocumented in public contributor docs and are not listed above. They remain visible in the source itself.