---
title: "Script the CLI with JSON"
url: "https://docs.alisx.com/how-to/cli/script-the-cli-with-json/"
---
# Script the CLI with JSON

Drive `alis` from a script or a coding agent: get one machine-readable result per command, keep progress separate, and branch on exit codes.

## Prerequisites

- A signed-in CLI with an active account.
- A shell, or an agent tool that runs a command and captures stdout, stderr and the exit code separately.

## Steps

1. Add `--json` to every call, and run one standalone command at a time. Pass the working directory with your tool's workdir setting or `--cwd`; do not wrap the command in a shell chain, pipe or redirect.

    ```bash title="Terminal"
    alis --cwd /home/me/alis.build/acme/build/shop/orders/v1 build --json --branch main
    ```

2. Parse stdout as one JSON document. It is the result, the final operation, or an error envelope. For long-running commands it is the final operation even when the operation failed, so parse stdout before looking anywhere else:

    ```json
    {"schemaVersion": 1, "name": "operations/<id>", "done": true, "status": "failed", "error": {"…": "…"}, "logsUri": "https://…"}
    ```

3. Treat stderr as progress only. Under `--json` it carries one small NDJSON event per line and never a result:

    ```text
    {"elapsed":"0:12","operation":"operations/<id>","progress":"Building image neurons/orders-v1"}
    ```

    There is no final stderr event; stdout arriving is the completion signal.

4. Branch on the exit code:

    | Code | Do |
    |---|---|
    | 0 | Parse the result. |
    | 1 | Read the structured failure on stdout (`error` on the operation, or an envelope), then stderr. |
    | 3 | Stop. Show the human the envelope's `retry` command and run it only after they approve. Never add `--approve` or `--confirm-production` yourself. |
    | 4 | Have the human run `alis login`, then retry. |
    | 5 | No change (`reply_timeout` on `alis specialist wait`). Not a failure. |

5. When stdout is an error envelope, follow its fields:

    ```json
    {"error": {"code": "ENVIRONMENT_SELECTION_REQUIRED", "message": "…", "retry": "alis deploy --json -e <env-id>", "retryArgv": ["…"], "retryCwd": "…", "agent": "…", "details": {}}}
    ```

    `retry` is the recovery command, `retryArgv` and `retryCwd` its exact arguments and directory, `agent` the instruction for an automated caller, `details` structured extras such as the deployment environments to pick from.

6. Read stdout whole. A streamed result is pruned to what is actionable and a success is under 1 KB, so never pass it through `head`, `tail`, `grep` or `2>/dev/null`: you would drop the fields you need. Absent fields mean zero or empty. Add `--verbose` when you want every field and full progress snapshots.

7. Verify creations by exit code 0 and the identifiers in the result. If a repeat returns an `AlreadyExists` envelope, follow its `agent` field instead of repeating the mutation.

## Headless budgets

A headless invocation (`--json`, or stdin and stdout not a terminal) has a time budget. If a command overruns it, usually because it hit a prompt nobody could answer, it exits 1 with a `TIMEOUT` envelope whose `details.phase` names the step. Run its `retry` once; if it repeats, `alis doctor --json` carries the command log. `ALIS_COMMAND_TIMEOUT=<duration>` changes the budget.

## Example: build, then act on the result

```bash title="build.sh"
#!/usr/bin/env bash
set -u
out=$(alis build --json --branch main 2>progress.ndjson)
code=$?
case $code in
  0) version=$(printf '%s' "$out" | jq -r .version); echo "built $version" ;;
  3) echo "approval needed:"; printf '%s' "$out" | jq -r .error.retry; exit 3 ;;
  4) echo "run: alis login"; exit 4 ;;
  *) printf '%s' "$out" | jq .error; exit "$code" ;;
esac
```

The script captures stdout into a variable and stderr into a file, then parses the whole result. It does not pipe `alis` itself.

Related: [Output contract](https://docs.alisx.com/reference/cli/output-contract.md), [Exit codes](https://docs.alisx.com/reference/cli/exit-codes.md), [Run long operations asynchronously](https://docs.alisx.com/how-to/cli/run-long-operations-asynchronously.md).
