A token-efficient Linear.app CLI built for AI agents — and humans who like structured data.
Linearis is a command-line interface for Linear that speaks JSON only. It resolves human-friendly IDs (like ENG-42 or a team name) to UUIDs for you, and exposes a two-tier usage system so an agent can discover exactly the commands it needs without loading the whole API surface into context.
npm install -g linearis
linearis auth login
linearis issues list --limit 10The official Linear MCP works well, but it costs ~13k tokens just by being connected — before an agent does anything. Linearis takes a different approach: agents discover capabilities on demand through a two-tier usage system.
linearis usage— a compact overview of every domain (~200 tokens).linearis <domain> usage— the full reference for one domain (~300–500 tokens).
A typical agent interaction costs ~500–700 tokens of context instead of ~13k. The agent pays only for what it uses, one domain at a time.
Note
The trade-off is coverage. Linearis focuses on the operations that matter for day-to-day work — issues, discussions, cycles, projects, documents, and files. For custom workflows, integrations, or workspace settings, the MCP is the better choice. See Coverage for the exact picture.
- JSON-only output — pipe into
jq, no parsing of tables or prose. - Smart ID resolution — pass
ENG-42, a team name, or a UUID interchangeably. - Two-tier discovery — self-documenting
usagecommands keep agent context small. - Discussion threads — first-class root/reply modeling on issues.
- File attachments — upload and download with signed URLs.
- Broad domain coverage — issues, projects, cycles, milestones, initiatives, documents, labels, teams, users, and more.
npm install -g linearisRequires Node.js ≥ 22. The linearis command is canonical; linear is a fully supported alias that runs the same CLI.
The interactive flow opens Linear in your browser, walks you through creating an API key, and stores it encrypted in ~/.linearis/token:
linearis auth loginOr provide a token directly:
linearis --api-token <token> issues list # via flag
LINEAR_API_TOKEN=<token> linearis issues list # via environment variableToken resolution order: --api-token flag → LINEAR_API_TOKEN env → ~/.linearis/token → ~/.linear_api_token (deprecated).
All output is JSON. Start with discovery, then act.
# Discover what's available (~200 tokens)
linearis usage
# Drill into one domain for its full command reference
linearis issues usage
# List and search
linearis issues list --limit 10
linearis issues search "authentication bug"
# Create an issue
linearis issues create "Fix login flow" --team Platform --priority 2
# Read an issue (includes embeds with signed download URLs)
linearis issues read ENG-42For the complete reference of every command and flag, run linearis <domain> usage.
Discussions are modeled as root threads with replies, rather than a flat comment list:
# Start a discussion thread on an issue
linearis issues discuss ENG-42 --body "Investigating this now"
# List root discussion threads for an issue
linearis issues discussions ENG-42
# List replies in one root thread
linearis issues replies <root-thread-id>
# Reply to a thread (use a root discussion thread ID, not a reply ID)
linearis issues reply <root-thread-id> --body "I found the root cause"Both batch commands take a JSON document instead of flags, and apply it in a single transaction — either every issue changes or none does. Unknown keys are rejected rather than ignored, so a typo fails the command instead of quietly dropping a field.
issues batch create takes an array with one object per issue, keys named after the issues create flags:
[
{ "title": "Fix login redirect loop", "team": "ENG", "labels": ["bug"] },
{ "title": "Document the SSO flow", "team": "ENG", "project": "Q3 Auth" }
]issues batch update takes the targets plus the one patch they share, keys named after the issues update flags, where null clears a field:
{
"issues": ["ENG-42", "ENG-43"],
"patch": { "status": "In Progress", "assignee": "alice", "cycle": null }
}linearis issues batch create --file issues.json
linearis issues batch update --file patch.json
# - reads stdin, and --json takes the document inline for one-offs
generate-issues | linearis issues batch create --file -
linearis issues batch update --json '{"issues":["ENG-42"],"patch":{"status":"Done"}}'Both formats are published as JSON Schema (draft 2020-12) in schemas/, shipped in the npm package and served raw from the default branch — point a validator or an editor at them to check a document before spending an API call on it.
A schema is the input contract only: it cannot know your team's workflow states, label names, or estimation scale, so a document that validates can still be rejected when a name does not resolve.
Linear's GraphQL API exposes 520 root operations (159 queries, 361 mutations). Linearis wires 114 of them directly, plus a number of nested reads — chosen to cover planning and issue work end to end rather than the whole API.
Both figures are checkable rather than asserted: npm run count:root-fields -- --verify parses graphql/**/*.graphql and cross-checks the result against a live introspection of the schema.
The table below is the honest picture of the whole surface — what works today, and what you'll need the Linear MCP or a raw API call for.
Legend — ✅ complete for practical purposes · 🟡 core operations, known gaps · 🟠 read-only or narrow slice · 🔴 no CLI surface yet
| Area | Extent | What you can do | Not covered |
|---|---|---|---|
auth |
✅ | Interactive login, token status, logout | — |
| Discussions | ✅ | Root threads and replies on issues, projects, and initiatives; edit, delete, resolve/unresolve; emoji reactions on any of them | Custom workspace emoji management, and comment threads on a status update — CommentCreateInput.projectUpdateId makes an update its own discussion surface, which neither projects updates nor initiatives updates exposes |
issues |
✅ | List, filter, full-text search, read, create, update, batch create/update, archive/unarchive, delete/restore, snooze; assign labels/assignee/delegate/state/priority/project/cycle/team (including moves between teams); subscribe/unsubscribe, share/unshare, reminders; find the issue for a git branch (from-branch); relations (list/add/remove); activity history |
Deliberately excluded: the AI-assist and integration-suggestion queries (Figma file lookup, filter/repository suggestions, title-from-customer-request) — see the Integrations row — and issuePriorityValues, a static list already in the help text |
initiatives |
🟡 | List, read, create, update, archive/unarchive, delete; attach/detach projects; initiative-to-initiative relations; initiative updates (list, read, create, update, archive/unarchive); discussions | Initiative labels, lead-team reassignment, relation reordering |
projects |
✅ | List, full-text search, read, create, update, delete (trash) and unarchive (restore), disable external sync; assign project labels by name (--labels, --label-mode, --clear-labels); status updates (list, read, create, edit, archive/unarchive, remind); dependency relations (list, read, add, update, remove); administer the workspace project status flow (projects statuses); discussions; activity timeline |
Deliberately excluded: Slack-channel creation and the AI filter suggestion, which are [Internal] integration plumbing — see the Integrations row. Project labels live under labels --type project; milestone delete/move under milestones |
documents |
🟡 | List, read, create, update, delete | Content history, document full-text search, unarchive |
milestones |
🟡 | List, read, create, update (per project) | Delete, reordering/move between projects |
attachments |
🟡 | List on an issue, create from a URL, delete, disable external sync | Update, and the provider-specific link mutations (GitHub PR/issue, GitLab MR, Slack, Jira, Zendesk, Intercom, Front, Salesforce, Discord) |
files |
🟡 | Upload a file, download via signed URL | Delete uploads, image-from-URL, CSV export reports |
teams |
🟡 | List, read, create, update; list/add/remove members | Delete, workflow-state administration, triage responsibility, git automation, SLA configuration |
labels |
🟡 | Issue and project labels alike (--type issue|project): list, read, create, update, delete, retire/restore; label groups (--group, --parent) |
Initiative labels |
cycles |
🟠 | List cycles, read a cycle with its issues | Create, update, archive, shift all, start upcoming cycle |
users |
🟠 | List workspace members | Read a single user, update, role changes, suspend/unsuspend, user settings, session management |
| Integrations | 🔴 | — | All 70 integration root fields (62 mutations, 8 queries): Slack, GitHub, GitLab, Jira, Figma, Sentry, PagerDuty, Intercom, Salesforce, and more. Also the [Internal] per-entity integration plumbing excluded from the issues and projects rows: Slack channel creation and dismissal on a project, Slack/Teams/Jira project posts, and the AI-assist suggestion queries (issue and project filter suggestions, Figma file lookup, title-from-customer-request) |
| Organization & admin | 🔴 | — | Org settings, invites, domains, webhooks, OAuth apps, audit log, SSO |
| Releases | 🔴 | — | Releases, release pipelines, stages, release notes |
| Customers (CRM) | 🔴 | — | Customers, needs, tiers, customer statuses |
| Views & templates | 🔴 | — | Custom views, favorites, templates, view preferences |
| Notifications | 🔴 | — | Inbox, subscriptions, snooze, mark read, push subscriptions |
| Agent sessions | 🔴 | — | Agent sessions, activities, skills, semantic search |
| Roadmaps | 🔴 | — | Roadmaps and roadmap-to-project links. roadmapToProject* is deprecated in favour of initiativeToProject*, which initiatives already wires |
| Imports & exports | 🔴 | — | Jira/Asana/Clubhouse/GitHub/CSV import jobs |
| Schedules | 🔴 | — | Time schedules and on-call rotations |
The 🔴 rows are mostly workspace administration and integration plumbing — work an agent rarely does mid-task, and the main reason the API-wide coverage number is low while day-to-day coverage is not.
Tip
Missing something you need day to day? Open an issue — the covered set is driven by what people actually reach for, not by API completeness.
Every failure is JSON on stderr, and the exit code says which class of failure it is:
| Code | Meaning | Payload |
|---|---|---|
0 |
Success | Result JSON on stdout |
1 |
Application error — the request was well-formed but could not be fulfilled (entity not found, API rejection) | { "error": "<message>" } |
2 |
Invalid invocation — unknown command or option, wrong number of arguments, or a command group named without a subcommand | Usage envelope (below) |
42 |
Authentication required — no usable token, or the stored one is invalid | { "error": "AUTHENTICATION_REQUIRED", … } |
Exit code 2 carries a machine-readable recovery path:
{
"error": "UNKNOWN_COMMAND",
"message": "Unknown command \"get\" for \"linearis issues\".",
"suggestion": "Did you mean read?",
"command": "linearis issues",
"available_commands": ["read", "list", "create", "…", "usage"],
"instruction": "Run 'linearis issues usage' to list valid subcommands.",
"exit_code": 2
}error is one of UNKNOWN_COMMAND, UNKNOWN_OPTION, MISSING_ARGUMENT,
MISSING_REQUIRED_OPTION, MISSING_OPTION_ARGUMENT, TOO_MANY_ARGUMENTS,
MISSING_SUBCOMMAND, or INVALID_USAGE. message is always
a single line. suggestion is present only when there is a close-enough near miss,
and available_commands only when the failing scope has subcommands to choose from.
Naming a command group without a subcommand (linearis issues, linearis issues threads) is a MISSING_SUBCOMMAND failure, not a request for help — use
linearis issues usage for the machine-readable reference or linearis issues --help for the human one. linearis on its own still prints the overview.
Linearis is structured around a discover-then-act pattern that matches how agents work:
- Discover —
linearis usagereturns a compact overview of all domains. The agent reads it once. - Drill down —
linearis <domain> usagegives the full reference for a single domain. The agent loads only what it needs. - Execute — every command returns structured JSON. No table or prose parsing.
The agent never loads the full API surface into context — it pays for what it uses, one domain at a time.
| Linearis | Linear MCP | |
|---|---|---|
| Context cost | ~500–700 tokens per interaction | ~13k tokens on connect |
| Coverage | Common operations (issues, discussions, cycles, docs, files) | Full Linear API |
| Output | JSON via stdout | Tool-call responses |
| Setup | npm install -g linearis + Bash tool |
MCP server connection |
Use Linearis when token efficiency matters and you work primarily with issues and related data. Use the MCP when you need full API coverage or tight tool-call integration.
Linearis ships an agent skill (following the agentskills.io standard) so your agent knows how to use it — no prompt to paste. The skill preflights the install, advisory-checks for updates, then follows the discover-then-act protocol above.
Any harness (recommended) — Vercel's skills CLI installs into the right place for 70+ agents and lists it on skills.sh:
npx skills add linearis-oss/linearisClaude Code — native plugin:
/plugin marketplace add linearis-oss/linearis
/plugin install linearis@linearis
OpenAI Codex — npx skills add linearis-oss/linearis installs to ~/.agents/skills/; invoke with /skills or $.
pi — npx skills add linearis-oss/linearis (or drop skills/linearis/ into .pi/skills/); invoke /skill:linearis.
Google Antigravity — npx skills add linearis-oss/linearis installs to .agents/skills/; auto-discovered from the skill list.
- MIGRATION_2026.4.9.md — migrating from the deprecated
commentsdomain to discussions (v2026.4.9). docs/— architecture, development, testing, and build-system references.schemas/— JSON Schemas for the commands that take a JSON document (batch operations).docs/ci-run-model.md— the authoritative CI/release trigger matrix.- CONTRIBUTING.md — contributor guidelines.
- SECURITY.md — how to report security issues.
Made with contrib.rocks.
This project is neither affiliated with nor endorsed by Linear.