Skip to content

Reference

Output & scripting

Output formats, exit codes, workspace linking, environment variables, and shell completion.

The CLI is built to be scripted. Output is machine-parseable, exit codes are a stable contract, and the app a command operates on can be resolved from the environment instead of a flag on every invocation.

Data-returning commands generally take --output; mutation commands that only print a confirmation line (apps delete, keys revoke, agents revoke, and similar) do not. Formatted commands accept all three formats:

FormatBest forNotes
tableInteractive usePlain-ASCII, the default for list commands.
jsonScripts and jqPretty-printed. The default for single-object commands.
jsonlLine-oriented list pipelinesOne JSON object per line — pipe into jq -c / grep / awk. The CLI validates the page before writing it, so an invalid --fields projection never leaves a partial stream. Falls back to json for single-object responses.
Terminal window
alter apps list --output json
alter audit list --since 7d --output jsonl | jq -c 'select(.action == "token.retrieved")'

Narrow JSON / JSONL output to specific top-level keys with --fields:

Terminal window
alter apps list --fields id,name
# [
# { "id": "<app-id>", "name": "demo" },
# ...
# ]

The value is comma-separated (spaces around commas are fine; spaces inside a name are rejected as a typo). An unknown field name fails loudly rather than emitting null — so a typo surfaces on the first run. --fields is inert with --output table, since table columns are already a fixed slice.

The CLI returns a structured exit code so scripts can branch on the failure mode without parsing stderr. These are a stable contract across releases.

CodeNameMeaning
0OKCommand succeeded.
1ERRORGeneric runtime failure, including network/SDK errors and backend statuses without a dedicated mapping (for example 400, 422, and 5xx).
2USAGEBad flag, argument, or input format. Always paired with a stderr line naming the offending input.
3AUTHNot signed in, or the PAT was revoked / expired. Fix: re-run alter auth login.
4NOT_FOUNDResource not found (404), or a referenced local file is missing.
5CONFLICTA 409 server-state conflict, such as an active dependent that blocks an operation or an existing config entry.
6RATE_LIMITA 429 — retry with backoff.
7FORBIDDENThe request was refused with a 403: either the PAT lacks the required scope, or the organization’s plan does not include the feature. Fix: re-mint the PAT with broader scopes, or upgrade the plan — the printed message says which.
8CANCELLEDA destructive-action prompt was declined or could not read a matching confirmation. Pass the command’s --yes or --confirm <value> flag in CI. An explicit wrong --confirm value is normally USAGE (2) before the request.

managed-secrets delete is the exception for an explicit confirmation: the CLI can avoid the read needed to discover the secret slug, so it forwards --confirm and a mismatch returns generic server error code 1.

branding set without --replace, against an app that has no branding yet, exits 2 (usage) rather than 4: the backend’s 404 carries the machine code branding_not_configured, which is a fixable input problem — re-run with --replace — not an absent resource. A script branching on 4 to mean “create it instead” will never fire.

The pre-minted-token probe in auth login, the remote probe in auth status, and pats whoami use generic code 1 when the backend rejects a resolved PAT. Regular resource commands that use the shared authenticated-command path map a backend 401 to 3.

alter verify also uses code 1 for its verification-input failures: a malformed --key, a malformed --grant used with --runtime, or a missing or malformed --agent required by an agent-runtime design.

alter link predates the dedicated cancellation code: declining its overwrite prompt currently returns 1, and --force is the non-interactive form. All destructive resource prompts use 8.

Example — probe whether an app exists without erroring on the not-found case:

Terminal window
alter apps show "$APP_ID" --output json > /dev/null 2>&1
status=$?
if [ "$status" -eq 0 ]; then
echo "app exists"
elif [ "$status" -eq 4 ]; then
echo "app not found"
elif [ "$status" -eq 7 ]; then
echo "PAT lacks dashboard_apps:read — re-mint with broader scopes"
else
echo "unexpected error" && exit 1
fi

Capture $? into a variable immediately — every command clobbers it.

VariableUsed byEffect
ALTER_PATAll commandsThe PAT to authenticate with. Highest-precedence credential source.
ALTER_APP_IDApp-scoped commandsDefault app ID when --app is omitted (UUID only — the env var and workspace pin never take a name).
ALTER_API_KEYsdk-passthroughRuntime API key for the one-off request escape hatch (not a PAT).
XDG_CONFIG_HOMEAuthentication, Fish completionMoves the plaintext fallback to $XDG_CONFIG_HOME/alter/auth.toml and the Fish install target to $XDG_CONFIG_HOME/fish/completions/alter.fish.
XDG_CACHE_HOMEUpdate notifierMoves the 24-hour update-check cache from ~/.cache/alter/update-check.json.
XDG_DATA_HOMEBash completionChanges the Bash install target to $XDG_DATA_HOME/bash-completion/completions/alter.
SHELLCompletionAuto-detects bash, zsh, or fish when --shell is omitted.
ALTER_NO_UPDATE_NOTIFIER, NO_UPDATE_NOTIFIER, CIUpdate notifierAny non-empty value other than 0 or false disables the update notice.
ALTER_NO_TELEMETRY, DO_NOT_TRACKUsage telemetry1 or true disables telemetry when the CLI build has telemetry configured.

Published builds do not emit telemetry unless telemetry has been explicitly configured. When enabled, events use a random anonymous installation ID stored at ~/.config/alter/telemetry-id; no user PII is attached. Delivery is best-effort and bounded, so it cannot change a command’s result.

If you work primarily on one app, pin it to the current directory tree so app-scoped commands don’t need --app:

Terminal window
cd ~/code/my-product
alter link <app-id>
# alter: pinned app_id=<app-id> in .../.alter/config.yaml
# alter: appended `.alter/` to .gitignore so the pin isn't committed.
# From anywhere in this tree, --app is now optional:
alter keys list
alter agents create --name worker --type service \
--scopes '{"google":["openid"]}'
alter policy show-app
# Inspect or clear the pin
alter link --status
alter unlink

The app ID is resolved in this order (highest precedence first):

  1. --app <id> on the command line
  2. ALTER_APP_ID environment variable
  3. The nearest .alter/config.yaml, found by walking up from the current directory
  4. Otherwise: no app selected (exit code 2)

Discovery checks each parent directory through $HOME itself, then stops before walking above it. Therefore $HOME/.alter/config.yaml intentionally acts as a user-wide default; configs above the home directory are never read. The search tests for .alter/config.yaml, not merely an .alter/ directory.

alter link <app-id> validates the UUID and verifies that the app exists before writing. If the current directory already pins a different app, it prompts before overwriting; use --force for scripts. In an enclosing git repository it appends .alter/ to the repository root’s .gitignore. alter unlink removes only ./.alter/config.yaml in the current directory; it does not remove a pin discovered from a parent.

Generate static completion scripts for bash, zsh, or fish:

Terminal window
# Write the script to the conventional location and print the line to add to the rc file
alter completion install
# Or print to stdout to install it yourself
alter completion print --shell zsh >> ~/.zsh/completions/_alter

install writes the script and prints the line to activate it; it never edits .bashrc / .zshrc automatically. The shell is auto-detected from $SHELL unless --shell is passed. Unsupported or undetectable shells return usage code 2; installation on Windows also returns 2 and directs the operator to completion print.

ShellDefault install path
Bash$XDG_DATA_HOME/bash-completion/completions/alter, or ~/.local/share/bash-completion/completions/alter
Zsh~/.zsh/completions/_alter
Fish$XDG_CONFIG_HOME/fish/completions/alter.fish, or ~/.config/fish/completions/alter.fish

The generated scripts do not query Alter. They statically complete this current top-level subset: auth, apps, keys, agents, providers, policy, audit, pats, link, unlink, completion, sdk-passthrough, self-update, and help, plus a static subset of their verbs. They do not complete flags, resource IDs, or the remaining namespaces.

Terminal window
# Upgrade to the latest published version
alter self-update
# Pin a specific version, or preview the command without running it
alter self-update --to 0.3.0
alter self-update --to next
alter self-update --dry-run

--to accepts strict SemVer (including prerelease/build metadata) or the npm tags latest and next; other values fail locally with usage code 2. Legacy self-update --version <value> and --version=<value> are rewritten to --to with a deprecation warning, while a bare --version still prints the installed CLI version.

self-update runs npm install -g @alter-ai/cli@<version> and smoke-tests the resolved binary. If the active binary is not under npm’s global prefix, the command refuses with usage code 2; update Homebrew, apt, or other package-manager installations through that manager. A failed npm install forwards npm’s own exit code.

After a successful interactive command, the CLI may print a newer-version notice to stderr. It checks npm at most once per 24 hours, caches under the XDG cache directory, skips piped stdout, --version, and self-update, and silently ignores registry/cache failures. Use one of the notifier opt-out variables in the environment table when deterministic interactive stderr is required.

Report an issue with this page

Necessary

Required for sign-in, security, authorization, and remembering your choices.

Always active

Analytics

Helps us understand which product and documentation features are useful.

Performance diagnostics

Uses performance tracing and privacy-masked session replay to diagnose problems.

You can change these choices at any time from Cookie settings.