pithy schema

Describe every command, flag, payload and error code, as JSON, from the binary you have installed.

Synopsis

pithy schema [command…] [--json]

Flags

FlagTypeDefaultMeaning
[command…]positional, optionalthe whole treeDescribe only this command and those beneath it. Any number of words: pithy schema token mint
--jsonbooleanfalseMachine-readable output. The answer is JSON either way; see below

What it does

pithy schema is the CLI describing itself, so an agent can build a call without reading a page or guessing at a flag. It is served from the binary, and that is the point: the answer is true of the version installed on this machine and of no other. This reference says what the latest release does; pithy schema says what the pithy in front of you will accept.

Without a command it answers for the whole tree. Every command, groups included, sorted by path; the flags every command takes; the environment variables the CLI reads; the exit codes; and every error code the kit defines, each with its exit class and what to do about it. With a command it answers for that command and everything beneath it, so pithy schema token is the group and its five subcommands, and pithy schema token mint is the one.

What each command says

Two things: what it parses, and what it promises.

What it parses is read off the parser itself. Its positionals come in order, each required the way the parser decides it: a positional with no default is required unless it says otherwise. Its flags come with their type, whether they are required, their default, the fixed set of values they take where there is one, whether they repeat, and their description. A boolean with no default publishes none, because leaving it off is its own state, not false.

Every other spelling the parser answers to is listed under the canonical name and marked by how it arises: camelCase (--workerUrl reaches --worker-url), declared (-n reaches --lines), or negation (--no-json sets --json to false). These are exactly the spellings a command accepts — any other flag is refused, as the CLI overview explains.

What it promises is everything a caller needs before running it: how it answers --json — one document, a stream of records, or none — and the JSON Schema of that payload; whether a successful run can change anything (mutates); whether running it again with the same arguments succeeds and changes nothing further (idempotent); whether it can reach the network (needsNetwork); and the rules over several flags the parser cannot express, such as provision taking exactly one of --env or --feature.

mutates and idempotent describe the default run

That is the command with its required arguments and no optional flag. A flag that changes the answer says so in its own description — --dry-run and ui sync --check make a write read nothing, and doctor --disable-notifier makes a read persist a preference. Counting every flag against every command would publish true nearly everywhere and tell you nothing.

The CLI’s own update-notifier cache, which any command may refresh, is not a change to anything you own, so pithy doctor is mutates: false.

A second run that is refused is not idempotent, however clean the refusal. ui add refuses a Worker that already has its front end, and testers invite refuses an address already on the roster, so both are false. A test in the kit runs every command it can drive offline twice and holds the flag to what the second run did.

The payload schemas

Every payload schema is the Zod schema the command itself declares, and a test in the kit parses each command’s real --json output against the one published here, so the two cannot drift. The JSON Schemas describe what a parse accepts, and every object in them is open. Adding a key to a payload is not a breaking change, and a published additionalProperties: false would make it one for every consumer that validates with it.

Some nested objects are published with no fields named yet. These are a known gap, not a promise: most of pithy doctor’s per-check detail, the parts of the testers cohort view, pithy seed’s per-store writes, pithy secrets verify’s sweep, and what pithy upgrade --packages moved. Each says what it is in its description and nothing about its keys. A test lists every one of them, so the list can only shrink.

The error line is published once. It is the same for every command, and large, so each command’s errorLine is {"$ref": "#/errorLine"}, a pointer at the document’s own errorLine.

JSON with or without --json

The answer is a document for a program to read, and a shorter human rendering would just be --help again. So without --json you still get JSON, formatted for whoever is reading exactly as a --json line is: indented at a terminal, compact in a pipe, and PITHY_JSON settles it for a session — see --json, and who is reading it.

It reads nothing from your project and writes nothing anywhere, so it runs the same inside a project, outside one, and offline.

--json

One line on stdout. For the whole tree:

KeyTypeMeaning
command"schema"The command that produced the line
schemaVersionintegerThe version of this payload’s shape. Bumped only when a key is removed or retyped
versionstringThe installed CLI’s version. Everything in the document describes this version
commandsobject[]Every command in the tree, groups included, sorted by path
globalFlagsobject[]The flags every command takes, answered before any command runs
environmentVariablesobject[]Every environment variable the CLI reads
exitCodesobject[]Every exit code the CLI uses on purpose, in order
errorCodesobject[]Every error code the kit defines, sorted by code
errorLineobjectThe JSON Schema of the {"error": …} line a failed run writes to stderr

With a command, globalFlags, environmentVariables, exitCodes and errorCodes are left out, and one key is added:

KeyTypeMeaning
pathstringThe command asked about, as typed after pithy schema. commands holds it first, then everything beneath it

Each entry of commands:

FieldTypeMeaning
commands[].pathstringThe command as typed after pithy: add, token mint
commands[].descriptionstringWhat it does, as --help says it
commands[].aliasesstring[]Other names the command answers to. Empty for every command today
commands[].subcommandsstring[]The paths of the commands beneath it
commands[].runsbooleanWhether it acts on its own. false for a group, which only prints its subcommands
commands[].positionalsobject[]name, required, variadic, default when it has one, enum when the values it takes are fixed, and description, in the order the command reads them. variadic is true for a positional that takes every remaining word — pithy schema’s own command is the one today — and only a command’s last positional can be
commands[].flagsobject[]name, type (boolean or string), required, default, enum, repeatable, description, negativeDescription, and aliases — each { flag, kind }
commands[].flagGroupsobject[]Rules over several flags. kind is exactlyOne, atMostOne or allOrNone with flags, or requires with flag, needs and any — whether any one of needs is enough. Each carries its rule in a sentence
commands[].outputstringdocument, stream or none
commands[].payloadobject or nullThe JSON Schema of its --json output: the one document, or any one record of a stream. null when output is none. It names schemaVersion as a constant, so a validator refuses a line at another version
commands[].schemaVersioninteger or nullThe version every --json line the command writes carries as schemaVersion. null when output is none
commands[].eventsobject or nullThe JSON Schema of one --json --events progress record. null for every command but deploy and provision, the two that take --events
commands[].errorLineobject{"$ref": "#/errorLine"}
commands[].mutatesbooleanWhether a successful run can change anything: files, Cloudflare, a database, a store. It describes the default run
commands[].idempotentbooleanWhether running it again with the same arguments, after it succeeded, succeeds and changes nothing further. A second run that is refused is not idempotent
commands[].needsNetworkbooleanWhether it can reach the network: Cloudflare, npm, a browser flow

Each entry of globalFlags is name, aliases, description and hidden — --pithier and --pithiest are left out of --help, not out of this. Each entry of environmentVariables is name, values (what it takes, in words) and description. Each entry of exitCodes is exit, exitClass (null for 0, 130 and 141), meaning, agentShould and retryable — the rows of the exit-code table. Each entry of errorCodes is code, status, exitClass, exit, retryable and remedy.

{"command":"schema","schemaVersion":1,"version":"0.13.1","path":"token mint","commands":[{"path":"token mint","description":"Mint a profile's scoped account token, or roll an existing one's value (rotate re-scopes)","aliases":[],"subcommands":[],"runs":true,"positionals":[{"name":"profile","required":true,"variadic":false,"description":"Token profile: ci-system (the CI credential) or runtime (the one token a deployed application holds)"}],"flags":[{"name":"--env","type":"string","required":false,"default":"dev","repeatable":false,"description":"Target environment: dev, staging, prod, or one declared in pithy.config.ts","aliases":[]},…],"flagGroups":[],"output":"document","payload":{…},"schemaVersion":1,"events":null,"errorLine":{"$ref":"#/errorLine"},"mutates":true,"idempotent":false,"needsNetwork":true}],"errorLine":{…}}

Nothing it prints is a credential. It reads no configuration and no environment beyond the CLI’s own code.

Errors

The command exits 0 whenever it can describe what was asked, which is always but once.

The words name no command. The action line names what the deepest command they do reach has beneath it — or the top-level commands, when even the first word is not one. When that command is a leaf, the words past it are a value rather than a command, and the action line says so and points at its own schema. It is validation/invalid_input, exit 2.

No command is named `token mnit`.
`pithy token` has forget, list, mint, revoke, rotate.
No command is named `add auth`.
`pithy add` takes no subcommand; its arguments are in `pithy schema add --json`.

A flag the command does not declare is refused as it is everywhere, exit 2.

Examples

# Everything the installed CLI has.
pithy schema --json

# One command: its flags, its groups, the JSON Schema of what it writes.
pithy schema provision --json

# A group and everything beneath it.
pithy schema token --json

# What --worker-url is called by every other name, with jq.
pithy schema dashboard connect --json | jq '.commands[0].flags[] | select(.name == "--worker-url")'
ESC