sb is the official StefanBrain command line. It wraps the TypeScript SDK and is designed for humans or coding agents running work from a shell. It requires Node.js 20.9 or later.
Install and sign in
sb login signs you in through your browser with OAuth and PKCE. It receives the result on the loopback address 127.0.0.1, so the browser must run on the same machine. The stored credential uses the member’s plan usage and refreshes itself.
The CLI stores the login in $XDG_CONFIG_HOME/stefanbrain/credentials.json, or in ~/.config/stefanbrain/credentials.json when XDG_CONFIG_HOME is unset. The path is the same on every operating system, and the file has mode 0600. sb logout revokes the stored login and removes it from the file.
For containers, continuous integration, SSH servers, or another headless environment, set an API key instead:
sb whoami prints the active credential, the account behind it, and why that credential won. The CLI has no credential flag because command-line arguments can be retained in shell history and process listings.
Run common workflows
sb jobs submit static_ad runs the create_images tool. Put its arguments in payload.json:
sb tools invoke edit_image --input edit.json.
Job identifiers from submit tools are UUIDs. The acceptance on stdout carries the job_id and chat, and stderr prints the exact sb jobs wait command to run next.
JSON payloads come from --input file.json or --input - for standard input. The CLI does not accept an inline JSON payload in command-line arguments.
Command reference
- The job verbs find a job through its chat. Pass the acceptance’s
--chat, or the same--projectand--sessionyou submitted with. sb tools invokeon an asynchronous submit tool prints the sametool_jobacceptance assb jobs submit.--inputonsb runs startreadsmessageandoutput_config. Pass every other option as a flag.--syncprints the finished run, or the202acceptance withnext: sb runs events <run_id>onstderr. Both exit0, so check.status.--followprints one compact JSON line per event, then the finished run as one more compact line. Since CLI 0.4.0,--timeoutbounds it: past the deadline the run keeps going, and the command exits4with aResume:command onstderr.sb runs eventsprints one JSON line per event until the run ends.--onceprints a single page. Pass itsnext_aftervalue to--afterto read the next page.sb askwaits up to 600 seconds for a long turn. If the wait ends first, it exits4and prints aResume:command onstderr. Since CLI 0.4.0, that command carries the turn’s--chat, even after--chat new. Before 0.4.0, add the same--projectand--sessionyou asked with.sb files listprints a chat’s workspace files, each with itspath,id, anddownload_url.sb files downloadfetches one of them by--chatand--path, by--chatand the file’sid, or by itsdownload_url, and writes it to--out. Both need CLI 0.4.0 or later.sb jobs artifactsdownloads the files the job’s result names, from the listing of the job’s chat.- Since CLI 0.5.0,
sb feedback sendtells the StefanBrain team where the API got in your way and prints the receipt, with the report’sfb_id.--inputreads the whole report, with the REST field names, from a JSON file or from standard input with-, and flags override its fields.sb feedback listandsb feedback getprint your reports with the team’s replies. See Agent feedback.
Set global flags
Without
--timeout, these deadlines apply:
Every Developer API request carries
User-Agent: stefanbrain-cli/<version> and the x-stefanbrain-client-name and x-stefanbrain-client-version headers. When CLAUDECODE, CURSOR_AGENT, CODEX_CI, CODEX_SANDBOX, or AGENT is set, the CLI also sends x-stefanbrain-agent-env with the coding agent it detected.
Script the output contract
stdoutcontains result data only: the API JSON envelope, newline-delimited stream events, or acli_*receipt for CLI-owned operations.- The receipts are
cli_login,cli_logout,cli_whoami,cli_version,cli_init,cli_artifacts, andcli_file_download. - A tool error, a failed or cancelled job, and a busy chat on
sb askalso print their envelope or receipt onstdout, even though the command exits with a nonzero code. stderrcontains progress, warnings, andnext:command suggestions.- The CLI never prompts, opens a pager, prints a spinner, or adds ANSI styling to machine output.
--pretty only for human-readable rendering. JSON is the default.
Handle exit codes
An unknown family on
sb jobs status, result, cancel, wait, or artifacts returns 400 unknown_job_family and exits 1.
When sb runs start, sb jobs submit, sb tools invoke on a submit tool, or sb ask exits 4 after a 5xx, a network error, or --timeout, the server may already have accepted the work. Re-run it only with the same --idempotency-key. Without a key, check sb runs get, sb jobs status, or the chat first. Since CLI 0.4.0, stderr says which applies. Before 0.4.0, it called every re-run safe, so follow this rule even when it says so.
When 4 follows a wait timeout, the work is still running: run the printed Resume: command.
sb feedback send takes no key. If it fails after a 5xx, a network error, or --timeout, the report may already be filed, and stderr says to check sb feedback list before re-running it.
Make retries safe
Pass--idempotency-key on sb runs start, sb ask, sb jobs submit, and sb tools invoke for an asynchronous submit tool. The server then returns the original accepted work rather than creating a duplicate.
A–Z, a–z, 0–9, _, ., :, or -. The CLI drops an empty key. Any other key outside that format returns 400 invalid_idempotency_key.
Keys on sb runs start, sb ask, and every asynchronous submit tool except research_shortform last for the rest of the same UTC day. A reused key returns the original work even when the input changed, so use a new key for new work. research_shortform is not idempotent. Keep the same chat, project, session, and input when retrying.
Do not pass a key to other tools. The server ignores it there, but the CLI still retries the request automatically, which can repeat a side effect such as send_email.
Work across parallel sessions
sb init writes .mcp.json in the current directory for Claude Code. It adds a stefanbrain server entry with the x-stefanbrain-project header pinned and keeps every other entry in the file. --print shows the result without writing it. --force replaces a different existing stefanbrain entry.
The entry’s Authorization header is Bearer ${STEFANBRAIN_API_KEY}. Claude Code expands the variable when it connects, so the key never lands on disk. Export STEFANBRAIN_API_KEY before you start the agent; a stored sb login does not supply it.
Session labels remain per-call arguments; there is no session header.
Run sb --help for the full current command and flag contract. Run sb version to compare the installed CLI version with the server’s specification version.
