Reference for Linearis CLI commands to interact with Linear project management...
Verified against Linearis v2026.4.9 (2026-05-31). ā ļø READ vs WRITE. Linear READS ā the local replica by direct SQL, or
linear_read_ticket <ID>. Never shelllinearis issues readfor a routine read ā it 429s the shared fleet quota. WRITES ālinearis. Read Gotchas before scripting.
node "${CLAUDE_SKILL_DIR}/scripts/identity-report.mjs" ā one line per identity (tenant, human, team, cloud host), and every unresolved line is a stop-and-say (CTL-2300). Paths like that name files inside this skill: Claude Code fills in ${CLAUDE_SKILL_DIR}; on another harness set CLAUDE_SKILL_DIR to this SKILL.md's directory, or stop and report skill_dir_unresolved. A write addressed to the wrong team or the wrong workspace does not error; it lands somewhere plausible, which is why this runs before the first read as well as the first write.
Single source of the Linear read rule ā other skills point here, they don't restate it.
source "${CLAUDE_SKILL_DIR}/scripts/lib/linear-read-replica.sh"
replica_fresh; rf=$? # 0 = writer heartbeat <5min AND seeded
source "${CLAUDE_SKILL_DIR}/scripts/lib/plugin-dirs.sh"
marker="$(plugin_dirs_repo_config_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.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.
Reads ā direct SQL via the gated helper above; writes always linearis ā run linearis usage / linearis <domain> usage for authoritative, current flag syntax. linear_read_ticket covers a single ticket only ā a scope-wide list/search still goes through linearis (no bulk-query replica form yet; see Reading Linear).
state() { bash "${CLAUDE_SKILL_DIR}/scripts/linear-transition.sh" --print-state --transition "$1" --team "$TEAM"; } # ā never TYPE a stage name
linearis issues search "auth bug" --team "$TEAM" --status "$(state todo)"
linearis issues update ENG-123 --status "$(state inProgress)" --labels "bug" --label-mode add
ā Agent comments ā
linear-reply.mjs, neverissues discuss/replyā those post AS THE HUMAN (personal token; ask-resolution gate reads that as the human deciding, CTL-1567).
direnv exec . node "${CLAUDE_SKILL_DIR}/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.
Single source of the Linear
stateMaptable ālinear,create-plan,implement-plan,create-pr,research-codebasepoint here; none restates it.
ā A stage is addressed by SLOT, never by name (CTL-2300). The table below deliberately has no column of stage names: a tenant renames its stages freely ā CTC-1597 renamed one mid-flight ā and this repo's own board calls inProgress something other than "In Progress" today. Resolve the slot with linear-transition.sh --print-state --transition <slot> --team <KEY>, which walks the one resolution chain (per-project stateMap ā global stateMap ā registry triageStatus ā bootstrap) and REFUSES rather than substituting our word when a tenant declares a stateMap without the slot. The reason this matters more than it looks: --status is server-side and fails empty on a typo (Gotcha 1) ā a name the board does not have returns an empty list, not an error.
| Workflow Phase | Slot (config key) |
|---|---|
| New tickets | stateMap.backlog |
| Acknowledged | stateMap.todo |
| Research / Planning started | stateMap.research / .planning |
| Implementation | stateMap.inProgress |
| Verify / Review phase | stateMap.verifying / .reviewing |
| PR created | stateMap.inReview |
| Completed / Canceled | stateMap.done / .canceled |
Names come from .catalyst/config.json's linear.stateMap (null skips a transition); the canonical bootstrap for a repo that declares none is scripts/lib/tenant-contract.default.json. UUID calls + the team-key allowlist cache (linear-team-keys.json): references/status-transitions.md.
issues list hides the done stage (shows the canceled one) ā pass --status "$(state done)", or issues read <ID> for one.linearis consumes stdin in a loop ā append </dev/null.--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; milestone create/rename/audit ā references/milestones.md; labels + the cross-team same-name trap ā references/labels.md; cycle review ā references/cycles.md.