Linearis CLI Reference
Verified against Linearis v2026.4.9 (2026-05-31). ⚠️ READ vs WRITE. Linear READS → the local replica by direct SQL, or linearreadticket <ID>. Never shell linearis issues read for a routine read — it 429s the shared fleet quota. WRITES → linearis. Read [Gotchas](#gotchas--traps) before scripting.
Reading Linear
Single source of the Linear read rule — other skills point here, they don't restate it.
- Cloud detection, every session — reuse the existing helpers, never write new ones:
``bash source "${CLAUDEPLUGINROOT:?}/scripts/lib/linear-read-replica.sh" replicafresh; rf=$? # 0 = writer heartbeat <5min AND seeded source "${CLAUDEPLUGINROOT:?}/scripts/lib/plugin-dirs.sh" marker="$(plugindirsrepoconfig_path)" # "" if no .catalyst/config.json found ` Either failing → no cloud mirror: say so loudly (never silent) and fall back to direct linearis/API reads — the non-fleet path (protects the 2500/hr quota), wrong to recommend on the fleet. Same pattern: steward's references/cloud-detection.md`.
- Cloud mode confirmed → query the replica and TRUST it. Don't re-verify against live Linear.
- Row missing / not fresh → an ALARM, not a silent reroute. Loud fallback, file a ticket.
The only reads you should shell directly are through the helper — it is the freshness gate, not a convenience wrapper. Never run a bare sqlite3 query against the replica yourself: it skips the $rf/$marker checks above and can return stale data (or an empty DB) with no fallback.
json=$(linear_read_ticket ENG-123) || return 1 # freshness-gate → SQL → loud fallback, ONE call
title=$(printf '%s' "$json" | jq -r '.title // empty')
Raw SQL syntax (only after the helper's gate already ran), schema discovery, apply-drift caveat, deprecated wrapper: [references/reading-linear-detail.md](references/reading-linear-detail.md).
Core Operations
Reads → direct SQL via the gated helper above; writes always linearis — run linearis usage / linearis <domain> usage for authoritative, current flag syntax. linearreadticket covers a single ticket only — a scope-wide list/search still goes through linearis (no bulk-query replica form yet; see [Reading Linear](references/reading-linear-detail.md#still-needs-linearis)).
linearis issues search "auth bug" --team ENG --status "Todo"
linearis issues update ENG-123 --status "In Progress" --labels "bug" --label-mode add
⛔ Agent comments → linear-reply.mjs, never issues discuss/reply — those post AS THE HUMAN (personal token; ask-resolution gate reads that as the human deciding, CTL-1567).
direnv exec . node "$CLAUDE_PLUGIN_ROOT/scripts/linear-reply.mjs" ENG-123 --as <AGENT> --body-file <path> --top
# --body-file <path> for anything longer than a one-line body; --body REFUSES a path (CTL-2204)
issues discussions <id> (read-only) is safe. Full CRUD, comment-thread commands, common mistakes, other domains: [references/core-operations.md](references/core-operations.md).
Workflow: Status Transitions
Single source of the Linear stateMap table — linear, create-plan, implement-plan, create-pr, research-codebase point here; none restates it.
| Workflow Phase |
Default State |
Config Key |
| New tickets |
Backlog |
stateMap.backlog |
| Acknowledged |
Todo |
stateMap.todo |
| Research / Planning started |
In Progress |
stateMap.research / .planning |
| Implementation |
In Progress |
stateMap.inProgress |
| Verify / Review phase |
In Progress |
stateMap.verifying / .reviewing |
| PR created |
In Review |
stateMap.inReview |
| Completed / Canceled |
Done / Canceled |
stateMap.done / .canceled |
Names come from .catalyst/config.json's linear.stateMap (null skips a transition). UUID calls + the team-key allowlist cache (linear-team-keys.json): [references/status-transitions.md](references/status-transitions.md).
Gotchas & Traps
issues list hides Done (shows Canceled) — --status "Done", or issues read <ID> for one.
linearis consumes stdin in a loop — append </dev/null.
- No
--json flag — JSON is the default; pipe to jq.
--status is server-side and fails empty on a typo — not an error; also deprecated --query (use issues search).
--status/--cycle require --team; --milestone requires --project; names collide across projects/teams.
project-milestones fails silently to the help dump — the domain is milestones.
status/state are zsh read-only vars (st/s/lstate); auth status is the diagnostic entry point when calls return nothing.
Cookbook, one topic per file: grooming/triage/stale sweeps — [references/backlog-grooming.md](references/backlog-grooming.md); milestone create/rename/audit — [references/milestones.md](references/milestones.md); labels + the cross-team same-name trap — [references/labels.md](references/labels.md); cycle review — [references/cycles.md](references/cycles.md).