StarSling
CLI

Configuration

Environment variables, global flags, output streams, and the exit-code table every command shares

View Markdown

Environment variables

sling is configured entirely through the environment — there are no config files beyond the credential it writes at ~/.config/sling/credentials.

VariableMeaning
SLING_HOSTControl-plane base URL.

SLING_HOST

Env-only by design — there is no --host flag. It must be https:// (or http:// on a loopback development host) so the token never travels in cleartext.

How agents authenticate

On your own machine, an agent needs nothing extra. sling login writes the session to ~/.config/sling/credentials for your user, so any agent running as you picks it up. Sign in once and the agent is authenticated.

See API credentials for the credentials the control plane accepts and the scopes each endpoint needs.

Context resolution

sling resolves the org and repo it is acting on so that in-repo invocations need zero addressing:

  • Org is auto-resolved when unambiguous, overridable with --org <slug>, and persisted as a default by sling org switch. Multi-org accounts do not pay a flag tax on every call.
  • Repo is detected from the git remote of the current directory, overridable with --repo <owner/name>.

This is a CLI convenience. The HTTP API never infers either — org is a required parameter on every org-scoped endpoint.

Global flags

Available on the root command and every subcommand.

FlagMeaning
--org <slug>Org context. Auto-resolved when unambiguous; persisted default via sling org switch.
--repo <owner/name>Repo context. Defaults to the git remote detected in the current directory.
--version, -vPrint the version and exit.
--help, -hShow help. An unknown command still fails, so a typo does not read as help.

Machine mode is one switch with individually available parts:

FlagMeaning
--agentMachine mode. Exactly equivalent to --json --compact --no-input --no-color --yes. This is the flag machine callers should pass.
--jsonJSON output on stdout.
--yesAssume "yes" for confirmations.

sling --help currently lists only --json, --help, and --version in its flags section — --agent, --compact, --no-input, --no-color, and --yes all work (confirmed by running each against sling whoami) but are not shown. This page is the only place --agent is documented today; the CLI owner should add these to sling --help.

Output streams

stdout carries data only. Human chrome — summary rows, spinners, prompts — goes to stderr. This happens unconditionally: the CLI does not check whether stderr is a TTY, so the same spinner and cursor-control sequences land on stderr even when it is redirected to a file or piped into another process. Don't rely on TTY detection to decide whether chrome is present — pipe stderr to /dev/null if you need to suppress it silently. That still leaves the more important case: on a prompt-bearing command like sling logout, --json by itself only changes what stdout serializes as — it does not imply --yes/--no-input, so a script passing --json alone still blocks on the confirmation prompt. --agent is the flag that guarantees non-interactive behavior, since it bundles --yes and --no-input; reach for --agent, not bare --json, in anything unattended. A failed command usually writes nothing to stdout: a bad id makes sling runs show <bad-id> --agent exit 3 with zero bytes on stdout. sling logs is the exception — asked for a real job that stores no logs it exits 3 and emits its envelope ({ "lines": [], "has_more": false, "local": { ... } }). Branch on $?, not on whether stdout is empty.

That is what makes sling usage --json | jq safe to pipe unconditionally: on failure jq receives empty input rather than half a table.

JSON casing is set per endpoint, not by whether the field crossed the network. Nearly every control-plane response sling passes through — top, labels, usage, bill, runs, jobs, resolve, why, time — is snake_case and frozen, matching its API schema. GET /api/whoami is the confirmed exception: it is a real control-plane call like any other (a bad SLING_HOST makes sling whoami --agent fail with exit 5, the same as any other command that reaches the API), but its schema is camelCase end-to-end (userId, githubLogin, expiresAt). The local envelope described below is composed by the CLI rather than passed through, but every key observed inside it so far is a single lowercase word (org, kind, slug, plan), so its casing convention for a multi-word key is not yet established by any shipped response. Don't infer a command's casing from "was this local" — check that command's reference page, or default to expecting snake_case and treat whoami as the documented exception.

The control-plane response, when there is one, is passed through verbatim at the top level of the JSON, with a sibling local key recording what the CLI resolved (org, repo, etc.) — not nested inside a wrapper. local is not on every response: sling top, sling usage, sling runs list and sling whoami all carry it, while sling labels list returns { "labels": [ ... ] } with no local key at all, so treat it as optional when parsing. For example (values redacted):

sling top --agent
{
  "by": "workflow",
  "metric": "runner-minutes",
  "window": { "from": "2026-07-25T22:47:14.510Z", "to": "2026-08-24T22:47:14.510Z" },
  "rows": [
    { "key": "ci", "repo": "example-repo", "runner_minutes": 69559, "cost_usd": 556.47, "p50_ms": 69000, "p95_ms": 180000, "p99_ms": 358540, "queue_wait_ms": 14000, "trend_pct": 170.7 }
  ],
  "local": { "org": "example-org" }
}
sling whoami --agent
{
  "identity": { "userId": "example-user-id", "name": "Example User", "email": "user@example.com", "githubLogin": "example-login" },
  "credential": { "type": "session", "expiresAt": "2026-08-31T20:38:25.617Z" },
  "local": { "org": { "kind": "set", "slug": "example-org", "plan": "paid" } }
}

Note the casing split across these two commands: runner_minutes, cost_usd, and p50_ms (the sling top control-plane fields) are snake_case, while userId, githubLogin, and expiresAt (the sling whoami control-plane fields) are camelCasewhoami calls the control plane the same as top does, its response schema is just camelCase. In both examples, the local block is camelCase-shaped regardless of the surrounding response's casing.

sling logs differs by mode, and the difference runs the opposite way to the rest of this page. In human mode it streams raw log lines to stdout, so | head and | less behave as you would expect, exiting cleanly when the pipe closes early. Under --agent or --json it returns a structured envelope like every other command — { "lines": [ ... ], "has_more": ..., "local": { ... } }, one object per line — so a machine caller can hand logs to the same JSON parser it uses everywhere else rather than special-casing it.

Exit codes

Every command exits from one table, so a script or agent can branch on $? without parsing output.

CodeMeaning
0Success.
1Unexpected CLI or internal error — reserved for a crash, never a mapped API outcome.
2Usage — bad flags, a prompt refused under --no-input or --agent, or org ambiguity.
3Not found — a resolved id has no such run, job, or attempt in this org, or it stores no logs.
4Auth — missing, expired, or under-scoped credential. The message includes sling login.
5Control-plane or API error — a 5xx, or a transport failure.
6Partial — telemetry incomplete; the result is still emitted. Used by sling time and sling why.
7Rate limited — the control plane returned HTTP 429.
10Remote outcome failed — sling doctor unhealthy, or sling runs show --wait on a non-success run.

Codes 3, 4, 5, and 7 are mapped by a shared error handler, so any command can surface them. See API errors for the HTTP responses behind each.

The binary's own sling exit-codes help text (as of v0.1.2) documents only codes 05 — it omits 6, 7, and 10, which are the codes this page documents for sling time/sling why (partial), rate limiting, and sling doctor/sling runs show --wait (remote outcome failed). This repo's git history for this page has no earlier version to check when 6, 7, and 10 were introduced, so it's unclear whether they're newer than the installed binary's help text or the help text is simply stale. This table is kept as-is (all nine codes) rather than trimmed to match the binary — a healthy local environment couldn't be made to exercise 10 to confirm it directly, but the table's own example below documents it as the unhealthy-sling doctor code, and neither this page nor sling exit-codes should be trusted blind until the CLI owner reconciles the two.

Exit 10 is not an error. It means the command succeeded and the answer was a failure — which is exactly what lets sling runs show --wait gate a pipeline on a run's conclusion.

Read a command's exit code with $? immediately after it, since the next command overwrites it:

sling doctor; echo $?
10   # 0 = healthy, 10 = unhealthy

On this page