Scripting
The CLI is built to be driven by scripts and coding agents. Data goes to stdout, everything else goes to stderr, exit codes are stable, and every prompt can be disabled.
Output format
Output is text when stdout is a terminal and JSON when it is piped or redirected. Force a format with --output json (or --output text), the POLYLANE_OUTPUT environment variable, or the config file. Precedence: flag, then environment variable, then config file, then terminal detection.
The stream contract:
- stdout carries pure data: JSON, table rows, streamed tokens. Safe to pipe.
- stderr carries spinners, progress, hints, and error messages.
polylane issue list --output json | jq '.items | length'
polylane issue list --quiet 2>/dev/null
Non-interactive mode
Pass --non-interactive to fail fast instead of prompting for missing input. polylane cloud disconnect refuses to run in non-interactive mode unless you also pass --yes. Other confirmations, such as polylane memory delete, are skipped in non-interactive mode, so pass --yes explicitly to make intent clear.
The flags you combine most often in scripts:
| Flag | Purpose |
|---|---|
--output json | Force JSON regardless of terminal state |
--quiet | Suppress spinners and progress; stdout stays pure data |
--non-interactive | Fail fast on missing input instead of prompting |
--yes | Skip confirmation on destructive commands |
--dry-run | Show what would happen without making changes |
--full | Return full objects on list commands instead of the narrow field projection |
--api-key <key> | Override the stored API key for one call |
--workspace <id> | Override the default workspace for one call |
POLYLANE_API_KEY and POLYLANE_WORKSPACE_ID set credentials and workspace from the environment, which suits continuous integration jobs. See Configuration and Authentication.
Worked example: list issues as JSON
polylane issue list returns { "items": [...], "count": n }. By default each item is projected to id, severity, status, title, resourceKind, resourceId, detectedAt, and investigationThreadId.
polylane issue list --active --severity critical --output json --quiet \
| jq -r '.items[] | "\(.id)\t\(.title)"'
Pass --full for the complete objects. See Issues for the lifecycle behind each status.
Worked example: trigger a thread from a script
polylane thread ask starts a thread and blocks until the agent replies. With --no-wait it returns immediately with { "id", "name", "status": "accepted" }, so you can fire it off and poll later.
TID=$(polylane thread ask "which services deployed in the last 24 hours?" \
--no-wait --output json --quiet | jq -r '.id')
polylane thread show "$TID" --output json | jq '.messages.items[-1]'
Use --stream instead to stream the reply tokens to stdout as they arrive; streaming requires an OAuth login. Attach context by ID with --context repo_xxx,acc_yyy; the CLI infers each resource type from the ID prefix.
Exit codes
Branch on $? to decide whether to retry, re-authenticate, or surface the failure.
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error |
| 2 | Usage error: bad flag, missing argument |
| 3 | Authentication error |
| 4 | Rate limit or plan upgrade required |
| 5 | Timeout |
| 6 | Network error |
| 130 | Interrupted with Ctrl-C |
Error envelope
Errors always go to stderr, so piped stdout stays parseable. In JSON mode the shape is:
{
"error": {
"code": 3,
"message": "Unauthorized - check your API key",
"hint": "Run `polylane auth login`"
}
}
code matches the process exit code. hint, when present, tells you how to fix the problem. Branch on the exit code first, then parse the envelope for detail.