Global flags, environment variables, persistent state on disk, the --json envelope and the semantic exit codes. Everything you need to drive the CLI from a script — or from a coding agent inside an IDE — without parsing prose.

Global flags

Every command accepts these. Their effective values are read from process.argv before subcommand parsing, so they propagate transparently.

Flag Env equivalent Default Description
--json STACKBONE_JSON=1 off Emit machine-readable JSON wrapped in { "schema_version": 1, ... }. Errors get error + exit_code keys.
-y, --yes (none) off Skip every confirmation prompt. Required in CI / non-TTY contexts.
--verbose STACKBONE_VERBOSE=1 off Stream the raw firehose of log lines and docker compose output instead of the per-stage spinner UI.

Environment variables

The CLI reads these from the parent shell:

Variable Effect
STACKBONE_JSON =1 ⇒ same as --json.
STACKBONE_LOG_LEVEL Pino logger level (debug, info, warn, error). Defaults to info.
STACKBONE_FRPC_BIN Absolute path to a frpc binary. Used by stackbone dev for the relay tunnel.
STACKBONE_FRPC_VERSION Override the pinned frpc release the auto-fetcher downloads.
STACKBONE_VERBOSE =1 ⇒ same as --verbose (raw firehose of log + docker compose output).
STACKBONE_CORS_ALLOW_ORIGINS CSV of origins added to the stackbone dev Studio CORS allowlist.
OPENROUTER_API_KEY Your own OpenRouter key. When set in your shell, stackbone dev seeds it into your agents so stackbone.ai resolves models with it (bring-your-own-key wins over the organization key).
OPENROUTER_BASE_URL Override the OpenAI-compatible base URL the model client uses. Defaults to OpenRouter cloud.
STACKBONE_NO_UPDATE_CHECK =1 ⇒ silence the "a new release of the Stackbone CLI is available" banner. The check is a background local-cache read that never blocks a command, and it is already suppressed in CI, on a non-TTY, and under --json.

stackbone dev also injects the env vars your agents and workflows read through the ambient stackbone client (STACKBONE_POSTGRES_URL, WORKFLOW_REDIS_URL, STACKBONE_AGENT_ID, …). See SDK integration → how the CLI connects to the SDK.

State on disk

Per-machine (global)

Lives under ~/.stackbone/:

Path What it stores Permissions
~/.stackbone/credentials.json Map keyed by control-plane URL → SessionInfo. Lets one user stay logged into local + staging + prod simultaneously; only one is active at a time. 0600 (owner read/write).
~/.stackbone/config.json Global preferences — defaultApiUrl, telemetry (opt-in PostHog, default false until V1). 0644.

Per-machine (cache)

Path What it stores
~/.cache/stackbone/bin/ Auto-downloaded binaries (today: frpc-<version>).

Per-project

Lives at the project root:

Path Tracked? What it stores
stackbone.config.ts Optional An optional workflow override. By default stackbone dev and stackbone publish derive the workspace registry by convention — scanning every deep-agents/<name>/index.ts and every workflows/<name>.workflow.ts file — so most projects need no config file at all. When this file is present (default-exporting defineWorkspace({ workflows }) from @stackbone/sdk) its workflows win over the convention scan; agents are always discovered from deep-agents/. See Workflows.
.stackbone/project.json No (gitignored by stackbone init) { schemaVersion, organizationId, agentId, controlPlaneUrl }. Identifies which organization this workspace points at.
.stackbone/*.d.ts No (gitignored) Generated editor types — config.d.ts, agents.d.ts, connect.d.ts — that give the ambient stackbone client typed autocompletion. Regenerated by stackbone dev; config.d.ts also by stackbone config types (no control plane needed).
stackbone.config.json Yes { studio: { corsOrigins } } and other team-shared knobs. Reviewable in PRs. Different file from stackbone.config.ts above — the .json holds CLI/Studio knobs, the .ts holds the workspace manifest.
agent.yaml Yes Manifest. See agent.yaml reference.
dist/stackbone/ No (build output) Written by stackbone publish — the workspace bundle (workspace-bundle.tar) plus its JSON pointer (workspace-bundle.json, carrying the content digest).
.claude/, .cursor/, .windsurf/, … No (gitignored by stackbone init / link) Per-coding-agent skill directories the upstream skills registry materialises. See Agent skills overview.

Output contract

Human mode (default)

Free-form text on stdout. May span multiple lines, may include @clack/prompts chrome and ANSI colour codes. Never parse this. Logging (debug, trace, deprecation warnings) goes to stderr through Pino regardless of mode.

JSON mode

Triggered by --json or STACKBONE_JSON=1. Every command emits a single JSON line on stdout wrapped in:

// success
{ "schema_version": 1, ...payload }

Errors go on stderr and include the original exit code:

{
  "schema_version": 1,
  "error": {
    "code": "auth",
    "message": "No active session for https://api.stackbone.ai",
    "suggestion": "Run `stackbone login`",
  },
  "exit_code": 2,
}

schema_version is an integer that only bumps on backward-incompatible shape changes. If a coding agent sees a value greater than the one it was written against, it should refuse to parse and surface the version mismatch.

Paginated lists

Every list-style command takes --limit <n> and --cursor <opaque>, and its JSON payload carries an items array plus nextCursor and prevCursor (either may be null). To walk forward, pass the previous page's nextCursor value back as --cursor. Some lists add domain-specific fields alongside items (for example common_prefixes on a storage listing); the three pagination keys are always present and always shaped the same way.

{
  "schema_version": 1,
  "items": [
    /* … */
  ],
  "nextCursor": "eyJ…",
  "prevCursor": null,
}

Exit codes

Code Name When
0 ok Command succeeded.
1 generic Catch-all error. Anything that isn't auth / project / not-found / permission.
2 auth Not logged in or session expired. Protected commands never auto-trigger login — run stackbone login first.
3 no project The command needs a target it can't resolve: the cwd has no .stackbone/project.json, or it targets the local-dev agent but stackbone dev is not running (dev_not_running).
4 not found Workspace, agent or publication does not exist.
5 permission denied Caller is authenticated but unauthorised for the resource.

These map 1:1 from error.code in the JSON envelope, so a coding agent can branch on either without parsing the message:

# In a shell script
if ! out=$(stackbone whoami --json 2>err); then
  case "$(jq -r '.error.code' < err)" in
    auth)       stackbone login ;;
    not_found)  echo "stale session" ;;
    *)          echo "unexpected: $out" ; exit 1 ;;
  esac
fi

The mapping is stable across CLI releases — scripts and coding agents can pin to the table above.

Putting it together

A coding-agent-friendly invocation looks like:

STACKBONE_JSON=1 stackbone metadata

…and produces a single line that fully describes the organization state. That's the pattern this whole CLI is optimised for.

Not on this page: the stackbone config document

This page is about CLI and on-disk configuration — flags, env vars, and the files that live in your project. It is not about the versioned AGENT_CONFIG document your running agents and workflows read at runtime through the ambient client (stackbone.config.get(...)). That runtime config is its own surface, edited with the stackbone config command family (get, set, versions, rollback, types) and shaped by a config.schema.ts in your project. See the config module reference.

The platform for agent developers.Build, ship and observe agentsthat survive prod.

© 2026 · STACKBONEBUILT WITH ❤️ FROM CANADA AND SPAIN