For tool builders
Want to show your crew somewhere else? A web page that draws your team, a light in your menu bar that turns on when a member waits for you, a script that pings your phone. Cadrei gives you two ways to read its state, and promises to keep them stable:
cadrei ls --jsonprints your crew’s state once.cadrei ls --json --watchkeeps running and prints a line each time something changes.
Both only read. They never start, stop or message anyone, and they never include chat text: no prompts, replies, questions or file contents.
Read it once
Section titled “Read it once”cadrei ls --jsonThis prints one JSON object (JSON is a plain-text data format most languages read out of the box). Add --all to get every crew on this machine. With jq, a small command-line JSON tool, here’s who’s doing what:
cadrei ls --json | jq -r '.running[].members[] | "\(.role): \(.state)"'engineer: workingreviewer: waitingIt exits with 0 when it worked. When there’s no crew to show, it exits with 1, says why on stderr, and prints nothing on stdout; with --all it exits with 0 and an empty cadreis list instead. It never asks you anything.
Watch it live
Section titled “Watch it live”cadrei ls --json --watchThis keeps running and prints one JSON object per line (a format often called NDJSON). The first line is the whole state, the same as cadrei ls --json. Every line after it is a change. Here’s a small Python script that tells you when someone needs you, both who’s already waiting when it starts and who starts waiting later:
import json, subprocess
stream = subprocess.Popen(["cadrei", "ls", "--json", "--watch"], stdout=subprocess.PIPE, text=True)needs_you = ("waiting", "stopped_by_safeguards")
for line in stream.stdout: event = json.loads(line) if event["type"] == "snapshot": if event["status"]["version"] != 1: raise SystemExit("This script knows version 1 of cadrei's JSON.") for team in event["status"]["running"]: for member in team["members"]: if member["state"] in needs_you: print(member["role"], "in", team["team"], "needs you") if event["type"] == "member_state": member = event["member"] if member["state"] in needs_you: print(member["role"], "in", event["team"], "needs you")A whole snapshot is one line, so lines can get long. Read whole lines, not fixed-size chunks (in Go, use bufio.Reader, or give bufio.Scanner a bigger buffer). Keep reading for as long as it runs: if your program stops reading but keeps the stream open, cadrei waits for it, heartbeats included.
Stop it with Ctrl-C, or just close its output. Either way it exits quietly with 0. It reads the same files cadrei ls reads and never asks Claude Code anything, so leaving it running costs next to nothing.
The lines
Section titled “The lines”Every line has type, seq (1 on the first line, then one more each line) and at (when cadrei noticed). Lines about a crew have crew, its name. Lines about a team or a member also have session, team, and project when the team works on one. project is always a project’s name; a project_state line has it too, with the whole project in project_info.
type |
When | Also has |
|---|---|---|
snapshot |
First, then every 5 minutes, and when your crew changed in a way the other lines don’t cover | reason and status (the same object as cadrei ls --json) |
heartbeat |
After 15 quiet seconds | nothing |
orchestrator |
Your orchestrator opened or closed | orchestrator |
team_started |
A team started | members, and legacy: true when it’s from cadre 0.1.x |
team_stopped |
A team stopped | nothing |
member_started |
A member joined a team that was already running | member |
member_stopped |
A member stopped while its team runs on | member (name and role) |
member_state |
Something about a running member changed | member (all of it) |
project_state |
A project’s folder came, went, or is on a drive that isn’t connected | project_info (all of it) |
A snapshot’s reason is start, periodic, crew_changed (you added a member, a team or a project, or changed settings), crew_gone, crew_back, crews_changed (only with --all) or woke (your computer slept). These are all the reasons in version 1: a new one would come with version 2.
Keeping your picture right
Section titled “Keeping your picture right”- Start from the first snapshot, and throw everything away and start again from each new one.
- Apply the other lines in order. Then your picture matches what
cadrei ls --jsonwould print at that line’sat. The exceptions arehook,problems,other_cadreisand a project’sfound, which only snapshots bring up to date. - You get where things are, not every step along the way. Changes within the same second can merge, so a member that went from working to waiting and back may show nothing. A line always means something changed.
- No line for 45 seconds means the stream is gone. Start it again.
- If your crew’s folder goes away (moved, deleted, archived, or on a drive you unplugged), you get a
crew_gonesnapshot wherecadrei.missingistrueand nothing runs. When it’s back, acrew_backsnapshot.
The fields
Section titled “The fields”Times look like 2026-10-10T14:02:07.118+07:00 and paths are full paths. A field marked optional is left out when it doesn’t apply; it’s never null. An empty list is []. Some names still say “cadrei” where they mean your crew: that’s the old word for it.
Top level of cadrei ls --json:
| Field | Type | What it is |
|---|---|---|
version |
number | This page’s version: 1 |
orchestrator_opens_in |
string | Where cadrei opens your orchestrator: tmux or terminal |
hook |
string | The every-chat hook, which makes every new Claude Code chat open as your orchestrator: on, off, or skipped (you said no) |
cadrei |
object | Your crew |
orchestrator |
object | Your orchestrator |
running |
list | Teams running now, one per tmux session |
teams |
object | Every team in your crew, running or not: the team’s name, then a list of its members, each with name and role |
projects |
list | Your projects |
other_cadreis |
list, optional | Your other crews, each with name, path, running (how many teams are running) and, when its folder is gone, missing: true |
problems |
list of strings | Things that keep members from starting. Show them, but don’t parse them: the wording can change |
cadrei, your crew:
| Field | Type | What it is |
|---|---|---|
name |
string | Its name, like main |
path |
string | Its folder, like /Users/you/.cadrei/main |
default |
bool | It’s your default crew |
from |
string, optional | How cadrei found it. Show it, don’t parse it |
project |
string, optional | The project it was found through, when you ran the command in a project’s folder |
missing |
bool, optional | true when its folder is gone (only in a --watch snapshot) |
model, effort |
string, optional | The model and effort level your crew sets for its members |
orchestrator:
| Field | Type | What it is |
|---|---|---|
running |
bool | It’s open |
mode |
string, optional | When it’s open: terminal or tmux |
session |
string, optional | In tmux, its tmux session, like cadrei-main |
since |
time, optional | When cadrei opened it |
Each team in running:
| Field | Type | What it is |
|---|---|---|
session |
string | Its tmux session, like cadrei-main-dev-my-app. It’s unique, so use it as the team’s key |
team |
string | The team, like dev |
project |
string, optional | The project it works on |
legacy |
bool | true for a team from cadre 0.1.x. Such a team counts as your default crew’s, so it shows in that crew’s running, and with --all under legacy too |
members |
list | Its members, one per window |
Each member in a running team:
| Field | Type | What it is |
|---|---|---|
name |
string | Its address, like main-dev-my-app-engineer. It’s unique, so use it as the member’s key |
role |
string | Its role, like engineer |
state |
string | What it’s doing: starting, working, waiting (on you), idle, unknown (cadrei can’t tell) or stopped_by_safeguards (Claude’s safeguards stopped its reply, so it won’t answer until you look) |
since |
time, optional | When it got to that state. Left out for unknown |
waiting_for |
string, optional | With waiting only: approval (it wants to run something) or question (it asked you something) |
no_activity_since |
time, optional | With working or waiting only: its chat hasn’t changed since then, for 10 minutes or more. Maybe someone stopped it with Esc |
turn_error |
bool, optional | With idle only: true when its last turn ended in an error |
model, effort |
string, optional | The model and effort level it started with |
model_from, effort_from |
string, optional | Where each one was set: start (the command that started it), member (its member file), machine (this machine’s crew settings) or crew (the crew’s settings) |
Each project in projects:
| Field | Type | What it is |
|---|---|---|
name |
string | Its name |
repo |
string, optional | Its git remote |
team |
string, optional | The team it usually goes to |
about |
string, optional | Its one-line description |
state |
string | On this machine: present, not here (no folder for it here yet), missing (its folder is gone) or drive (on a drive that isn’t connected) |
path |
string | Its folder, or "" when it’s not here |
cloned |
bool | The same as state being present. It’s kept for older tools |
found |
string, optional | When it isn’t present: a copy of its repo cadrei found in your projects folder |
cadrei ls --json --all has version, orchestrator_opens_in and hook as above, then:
| Field | Type | What it is |
|---|---|---|
cadreis |
list | Every crew on this machine, each shaped like the top level above, without version, orchestrator_opens_in, hook and other_cadreis |
legacy |
list | Teams from cadre 0.1.x, shaped like entries in running |
unknown |
list | tmux sessions cadrei didn’t start, or that belong to a crew it doesn’t know anymore, shaped like entries in running |
The promise
Section titled “The promise”versionis 1. Within version 1, cadrei only adds things: new fields, and new line types in the stream. It never removes or renames a field, changes its type, or changes what it means.- The values listed here are all of them for version 1. A new one, like a new member state, would change what a field means, so it would come with version 2.
- Every change to this page, added fields included, is in CHANGELOG.md under “For tool builders”.
- Your part: ignore fields and line types you don’t know, and check
version. If it isn’t one you know, stop and say so. - Only
cadrei ls --jsonand its stream are covered. The screens you read, the text inproblemsandfrom, other commands’ output, and the files in a crew’s.claude/build/can change in any release.
Why there’s no web server
Section titled “Why there’s no web server”Cadrei doesn’t open a network port, and it won’t. Any web page open in your browser can send requests to a server on your computer, and so can every other program on it. Cadrei’s state names your projects and folders, and your team can run commands, so a port would be one more way in, with logins and checks to get right in every release. Your tool already has what it needs: run cadrei ls --json --watch from your own program, and show the data however you like.
If your tool serves it to a browser, keep it on your computer: listen on 127.0.0.1 only, check the Host and Origin headers, and ask for a token.