CLI overview
Install the flitz CLI and learn the conventions every command shares: global options, output streams, --json, and exit codes.
flitz is the one tool you install. It signs you in, downloads and pins the
Flitz SDK for a project, builds a bundle from that project, publishes it, and
tells you what is set up and what is not. There are no archive URLs to copy
and no PATH juggling required to build a bundle.
The CLI itself is free to obtain and needs no license. Only the actions behind it are gated: downloading an SDK, reading the loader-plugin credentials, and publishing a bundle each check your organization's license when they run.
Supported hosts
The CLI ships for linux-x64 and macos-arm64 (Apple Silicon). Any other
host is rejected with a clear error naming the supported set.
Install
The one-line installer detects your platform, downloads the matching
pre-compiled binary, verifies its SHA-256, and installs flitz into
${XDG_BIN_HOME:-$HOME/.local/bin}:
curl -fsSL https://download.flitz.dev/install.sh | shMake sure that directory is on your PATH, then confirm the install:
export PATH="$HOME/.local/bin:$PATH"
flitz --versionThe binary is self-contained. It needs no Dart or Flutter SDK on the machine — it is the bootstrap that installs one.
Prefer to install by hand? The download page at
download.flitz.dev lists each host's binary
with its SHA-256 and a copy-pasteable verify command (sha256sum -c on Linux,
shasum -a 256 -c on macOS). Download, verify, chmod +x, and move it onto
your PATH as flitz.
Update
Re-run the install script. It always installs the newest published CLI.
When a newer CLI exists, the CLI tells you once a day, after your command has
finished, and never in CI or under --json. flitz status reports the same
fact on demand. See Configuration → Update notices
for how to silence it.
The happy path
flitz login # once per machine
cd /path/to/your/flutter/app
flitz sdk use latest # pin an SDK for this project and download it
flitz publish # build a bundle and get a link + QR codeflitz --help prints the same three steps. Run flitz status at any point to
see what is set up and what to do next. For the full walkthrough, including
embedding the loader in your app, see Getting started.
Global options
Every option is accepted before or after the subcommand.
| Option | Effect |
|---|---|
--version | Print the CLI's own version and exit 0. Prints nothing else. |
-v, --verbose | Add stack traces to errors and stream build-tool output live. One level only; -vv is a usage error. See Verbose output. |
-y, --yes | Answer yes to every confirmation prompt without showing it. See Prompts and --yes. |
--json | Emit the result as one JSON document on stdout. Accepted by seven commands; a usage error elsewhere. See Machine-readable output. |
--no-color | Force plain output, overriding terminal detection, FORCE_COLOR, and config.yaml. |
-h, --help | Help for the CLI or the named command. |
Output conventions
Two streams
stdout carries results; stderr carries everything else. A result is the thing you ran the command to obtain: an SDK path, a plugin version, a JSON document, the published URLs, a completion script, the version string. Progress, timings, spinners, warnings, prompts, errors, and hints all go to stderr.
So every command works in a pipeline. flitz publish > urls.txt captures the
three result lines and nothing else while the terminal still shows the
progress; flitz sdk path, flitz plugins configure --version-only, and
flitz completions zsh each emit exactly their result.
Rich and plain output
There is one switch, rich versus plain, and it governs color, glyphs, and
animation together. Rich output uses color, the Unicode verdict glyphs
(✓ ⚠ ✗ ?), and in-place spinners. Plain output uses no escape sequences at
all: ASCII verdict words ([ok] [warn] [fail] [unknown]) and one appended
line per state change, so a CI log reads as a clean chronological record.
The mode is resolved once per stream, in this order:
--no-color→ plain.NO_COLORset and non-empty → plain (it beatsFORCE_COLOR).FORCE_COLORset and non-empty → rich.config.yamlcolor: always→ rich;color: never→ plain.- Otherwise rich when the stream is a terminal and
TERMis neither unset nordumb; plain otherwise.
Because it is per stream, redirecting stdout does not flatten the progress on stderr.
Verdict markers
Four markers are the CLI's whole visual vocabulary. They prefix status rows,
errors, and warnings alike.
| Verdict | Rich | Plain | Meaning |
|---|---|---|---|
| ok | ✓ | [ok] | Satisfied. |
| advisory | ⚠ | [warn] | Works today; worth attention. Never blocks. |
| blocking | ✗ | [fail] | Prevents building or publishing. |
| unknown | ? | [unknown] | Could not be determined — offline, or no credential to ask with. |
Errors and hints
Every failure is one lowercase sentence saying what went wrong, then one
indented → line per next action, naming the command that fixes it:
✗ the pinned SDK <tag> is not installed
→ run `flitz sdk use <tag>`
→ or pass --yesWarnings use ⚠ and never change the exit code. Compiler output is passed
through verbatim, never reformatted. The full contract, the six exit codes,
and the bug-report capture are on Exit codes and messages.
Prompts and --yes
A prompt is asked only when stdin and stderr are both terminals. --yes (or
assume_yes: true in config.yaml)
answers every prompt affirmatively without displaying it. There are exactly
three prompts:
| Prompt | Default | Non-interactive without --yes |
|---|---|---|
publish: download the pinned SDK now? | yes | Usage error (exit 1); the hint names flitz sdk use and --yes. |
sdk use: download the SDK just pinned? | yes | Downloads — you asked for it. |
apikey revoke: revoke the organization key? | no | Usage error (exit 1); the hint names --yes. |
There is no setup wizard. Onboarding is flitz status: it reports what is
missing and each finding names the command that fixes it.
Machine-readable output (--json)
--json makes a command emit its result as a single JSON object on stdout and
nothing else. Exactly these commands accept it:
| Command | Document |
|---|---|
status | cli_version, host, checks[] keyed by stable identifiers, blocking, advisory. |
sdk list | { "sdks": [ { "tag", "installed", "pinned" } ] } |
sdk releases | { "releases": [ { "tag", "installed", "pinned" } ] } |
sdk path | { "tag", "path", "installed" } |
plugins configure | { "version", "target_sdk_tag", "repositories": { "maven", "swift", "cocoapods" } } |
apikey status | { "present", "name", "obfuscated_value", "created_at", "last_used_at" } |
publish | { "page_url", "deeplink", "bundle_url" } |
Passing --json to any other command is a usage error (exit 1). The contract:
- stdout carries exactly one document — no progress, no warnings, no banner. Everything human-facing goes to stderr or is suppressed, including the ambient update notice.
- On failure the command emits no JSON. It reports the error on stderr and exits non-zero. Check the exit status first; parse stdout only on 0.
- Documents are objects, never bare arrays, and schemas grow additively:
fields are added, never removed or repurposed.
statuskeys its checks on identifiers, not prose. - No document ever contains a plaintext API key, a token, or a signed upload
URL.
apikey status --jsoncarries the obfuscated value only.
sdk path and plugins configure --version-only also print a bare string
without --json: the bare form is what a shell substitution wants, the JSON
form is what a tool wants.
Commands
| Command | License | Purpose |
|---|---|---|
flitz login | — | Sign in from a browser; stores a credential on this machine. |
flitz logout | — | Remove the stored credential. |
flitz status | — | Read-only health check of your account, project, plugins, and CLI. Always exits 0. |
flitz sdk install / use / releases | required | Download, pin, and list Flitz SDK releases. |
flitz sdk list / path / remove | — | Purely local cache reads and deletes; never touch the network. |
flitz publish | required | Build a .flitz bundle from the project and publish it. |
flitz plugins configure / credentials / skill | required | Resolve the loader-plugin version, store the repository credentials, print the wiring prompt. |
flitz apikey create / rotate / revoke / status | required | Manage the organization's single CI API key. |
flitz completions <shell> | — | Print a static completion script for bash, zsh, or fish. |
"Required" means the command needs a credential — an interactive session from
flitz login or the FLITZ_APIKEY environment variable — and the
organization's license must be active. There is no separate license
pre-flight: a licensed command goes straight to the endpoint that does its
work and reports what that endpoint answered. The apikey mutations
(create/rotate/revoke) are the one exception: they require an
interactive session and refuse an API key.
Where the CLI keeps things
| Path | Contents |
|---|---|
~/.config/flitz/credentials.json | Your sign-in credential, mode 0600. Written by login, deleted by logout. |
~/.config/flitz/config.yaml | Optional preferences you author; the CLI never writes it. See Configuration. |
~/.cache/flitz/sdk/<tag>/ | The SDK cache, one directory per release tag. |
~/.cache/flitz/update-check.json | Throttle state for the update notice. |
<project>/flitz.yaml | The project's SDK pin, committed to your repository. See sdk use. |
~/.config and ~/.cache follow XDG_CONFIG_HOME and XDG_CACHE_HOME when
those are set. There is no log file: diagnostics are produced on demand by
-v and by the bug-report capture.
flitz status
Start here when anything is unclear: every check and what each verdict means.
flitz publish
Flags, output streams, --json, and the on-demand SDK download.
Exit codes and messages
The six-code contract for scripts, and the bug-report capture.
Troubleshooting
Each failure class mapped to its remedy.