magi-linear-axi
Agent-native Rust CLI for direct, non-interactive Linear GraphQL operations. It covers issues, teams, users, projects, cycles, milestones, initiatives, labels, documents, raw GraphQL, schema export, and agent session integration without depending on another Linear CLI.
Quick path: install with Cargo, export
LINEAR_API_KEY, verify withauth whoami, then use modeled resource commands. Default output is compact TOON; use--format jsonwhen a downstream tool requires JSON.
Read-only benchmark harness
The isolated Node benchmark under benchmarks/linear compares this CLI with Linear MCP using dynamic snapshot tasks. Its AXI condition never gives Claude or Bash the Linear key: a runner-owned, mode-0700 Unix-socket broker validates bounded read-only argv and injects the pinned endpoint/key only when spawning the resolved binary. The callable wrapper is key-free and temporary. Pipelines, chaining, command separators, substitutions, line continuations, parentheses, ampersands, and redirection are hard compliance/correctness failures. Snapshot generation uses query-only guards, confirms the persisted invalid identifier with a bounded direct lookup, and keeps generated exact values within AXI's 240-Unicode-code-point default output limit without slicing facts. The harness records an exact task-manifest SHA-256 and requires it to match across every cohort cell. See the benchmark README for the offline test contract and live-command safety gates.
Contents
- Quick start
- AXI contract
- Authentication and configuration
- Command guide
- Raw GraphQL and schema
- Agent integrations and completions
- Reliability, limits, and mutation safety
- Security boundaries
- Development
Quick start
Requirements: Rust 1.87 or newer with Cargo, network access to Linear, and a Linear personal API key. API keys remain environment-only.
Install latest published release:
Install from this checkout while developing:
Authenticate and verify account:
Find team IDs, inspect issues, then create or update one:
Use raw GraphQL when modeled commands do not cover an operation:
Install session-start context for supported coding agents:
Run focused help before unfamiliar reads or mutations:
AXI contract
magi-linear-axi follows an agent-facing command contract:
- No arguments returns compact home metadata and useful next commands; it does not require authentication or network access.
- Commands never prompt. Supply identifiers, values, files, and workspace through arguments, flags, stdin, or environment.
- API-backed success data and application errors go to stdout. Diagnostics, including retry notices, go to stderr.
- Default structured format is TOON.
--format jsonrenders API-backed results and post-parse application errors as compact JSON. - Strings longer than 240 Unicode code points are recursively truncated and include the original code-point count.
--fulldisables truncation. - Exit
0: success, explicit empty result, or safe no-op. Exit1: authentication, configuration, network, API, or output failure. Exit2: command or argument usage failure. - Unknown commands and flags fail before authentication or network access.
- Global options can appear before or after subcommands.
magi-linear-axi [--workspace <slug>] [--endpoint <url>]
[--format toon|json] [--full]
<command> [args]
Structured application errors contain stable fields. Operational failures use exit code 1; usage failures use exit code 2:
Current single-document exceptions: issue id and team id emit plain identifiers; generated shell completions, help, and version emit plain text; issue pull-request passes through gh JSON before rendering its Linear result. Parser-level failures occur before output-format resolution and are emitted as structured TOON even when --format json was supplied. Do not send these exceptional outputs directly to a strict JSON parser.
Authentication and configuration
Environment
| Variable | Purpose | Required |
|---|---|---|
LINEAR_API_KEY |
Linear personal API key | All network commands |
LINEAR_API_URL |
GraphQL endpoint override | No; defaults to Linear production API |
LINEAR_WORKSPACE |
Workspace slug used by browser-opening commands | Only when opening web/app URLs without --workspace |
LINEAR_API_KEY must be non-empty. It is never loaded from TOML, stored by setup, or printed.
Endpoint files
Project configuration uses ./.linear.toml. Global configuration uses:
$XDG_CONFIG_HOME/linear/linear.tomlwhenXDG_CONFIG_HOMEis set;- otherwise
~/.config/linear/linear.tomlon macOS/Linux; %APPDATA%\linear\linear.tomlon Windows.
Only endpoint is read from files; unknown keys are ignored:
= "https://api.linear.app/graphql"
Endpoint precedence, highest first:
--endpoint- non-empty
LINEAR_API_URL - project
.linear.toml - global
linear/linear.toml https://api.linear.app/graphql
Workspace precedence is --workspace then non-empty LINEAR_WORKSPACE. Workspace is not read from config files and is optional except for browser-opening commands. Existing malformed config fails with its path instead of being silently ignored.
Remote endpoints must use HTTPS. Plain HTTP is accepted only for loopback hosts (localhost, 127.0.0.1, and [::1]) so hermetic local tests can run without TLS.
Command guide
Issues
Issue references accept identifiers such as ENG-123. When omitted, relevant commands try to extract an identifier from current Git branch.
| Job | Commands |
|---|---|
| Discover | issue mine, issue list, issue query |
| Inspect | issue view, title, describe, url, id |
| Mutate | issue create, update, start, delete |
| Comments | issue comment list | add | update | delete |
| Attachments and links | issue attach, issue link |
| Relations | issue relation list | add | delete |
| Agent sessions | issue agent-session list | view |
| Local repository helpers | issue commits, issue pull-request |
# Filtered reads
# Opt-in compact reads
# Comments
# Links, uploads, and relations
# Open in Linear; workspace slug is required
Compact projections are opt-in: --fields compact is supported by issue view, issue mine/list/query, issue comment list, issue relation list, and project view. Compact issue lists return identifier/title plus pageInfo. Compact comments and both relation connections default to 50 records (override with --limit) and retain pageInfo, including hasNextPage, so bounded output reports whether records remain. Invalid or unsupported selectors fail during argument parsing with exit 2, before authentication or network access. Compact relation output omits relation IDs; use default issue relation list before deletion.
Representative mocked issue-view regression fixture reduces GraphQL selection from 148 to 87 bytes and rendered JSON from 287 to 134 bytes. These fixed measurements guard payload/output impact before benchmark use.
--assignee, --project, and --milestone filters expect Linear IDs; --team accepts team key. Repeat --state or --label to match multiple values. issue create currently sends title, description, team ID, and priority. issue update currently sends title, description, priority, and --unassign; use raw GraphQL for other issue fields.
Relation deletion currently requires issue, relation type, and relation ID arguments, but Linear deletion uses only relation ID. Run issue relation list first, copy exact relation ID, and do not assume first two arguments protect against deleting wrong relation.
Local helper requirements:
- Branch-based issue detection and
issue commitsrequiregit. issue pull-requestrequires authenticated GitHub CLI (gh).--web/--appuses platform opener:open,xdg-open, or Windowscmd.
Resource families
| Resource | Supported operations | Aliases |
|---|---|---|
| Team | list, create, delete, id, members, states, autolinks (team identity only; autolink entries are not returned) |
— |
| User | list |
— |
| Project | list, view, create, update, delete |
view: v |
| Project update | list, create |
family: pu; list: l; create: c |
| Cycle | list, view |
family: cy; view: v |
| Milestone | list, view, create, update, delete |
family: m; view: v |
| Initiative | list, view, create, update, archive, unarchive, delete, add-project, remove-project |
family: init; list: ls; view: v |
| Initiative update | list, create |
family: iu; list: l; create: c |
| Label | list, create, delete |
family: l |
| Document | list, view, create, update, delete |
family: document, docs, or doc; operations: l, v, c, u, d |
Representative commands:
# Teams and users
# Projects and project updates
# Cycles and milestones
# Initiatives and updates
# Labels
Use Linear IDs for mutations and nested resource queries unless focused help explicitly accepts another selector. --all is supported only by team list, user list, project list, and label list; do not use it with other families. Default --limit is 50. Cycle, milestone, and initiative lists currently ignore --limit because their GraphQL documents are unpaginated.
Documents
Document content accepts inline text, UTF-8 file content, or stdin:
|
Raw GraphQL and schema
api accepts GraphQL from positional argument or stdin. Repeat --variable key=value; values parse as JSON when valid and otherwise remain strings. --variables-json accepts one JSON object. Individual --variable values override same-name entries from --variables-json.
|
api --paginate follows first recursively discovered object containing nodes and pageInfo, updates after with pageInfo.endCursor, and merges nodes. Query must accept $after; variables must form an object. Raw requests, including raw pagination, are never retried automatically. api --silent suppresses successful rendered output, useful only when caller relies on exit status.
Raw GraphQL preserves authentication, endpoint validation, response bounds, and structured errors, but it can execute arbitrary Linear mutations. Prefer modeled commands and inspect raw mutation documents before sending them.
Agent integrations and completions
setup installs all targets when no target flag is supplied:
Managed files:
| Target | Files and integration |
|---|---|
| Claude Code | ~/.claude/settings.json, hooks.SessionStart |
| Codex | ~/.codex/config.toml, ~/.codex/hooks.json, hooks.SessionStart |
| OpenCode | ~/.config/opencode/plugins/magi-linear-axi.js, experimental.chat.system.transform |
Setup is explicit, preserving, and idempotent. Each managed file is written atomically; installation across multiple files or targets is not transactional, so an error can leave earlier target updates applied. Setup stores resolved absolute executable path, removes duplicate managed hooks, repairs stale managed paths, and preserves unrelated valid configuration. Malformed target configuration fails before that file is replaced. --path changes home root used for generated files; it is primarily useful for isolated installs and tests. Setup does not contact Linear or store API key.
Installable on-demand Agent Skill lives at skills/magi-linear-axi/SKILL.md. Skill guidance and ambient session hooks are complementary: skill provides detailed operating procedure when invoked; setup provides compact session-start discovery.
Generate shell completions:
Reliability, limits, and mutation safety
- Modeled read-only requests retry transient connection failures and HTTP
500,502,503, and504up to three total attempts, with 50 ms then 100 ms backoff. - Mutations, uploads, raw API calls, raw pagination, and HTTP-200 GraphQL errors never retry automatically.
- Successful response bodies are capped at 10 MiB. HTTP failure previews are sanitized and capped at 512 bytes.
- Uploads are capped at Linear's 100 MiB limit and read into memory before transfer.
--publicchanges attachment upload visibility. - Generic pagination follows first discovered GraphQL connection. Modeled
--allis supported only for team, user, project, and label lists. Useapi --paginateonly with a compatible query.
For mutations:
- Read target first and retain stable ID plus identifying fields.
- Use explicit IDs; do not guess that names resolve to IDs.
- Send one mutation and inspect returned
successvalue. - Never blindly retry mutation after success or ambiguous transport failure.
- Branch on exit status: fix syntax after exit
2; inspect structured error after exit1.
Issue deletion example:
success:true means Linear accepted delete request. Narrow query exclusion is eventual-consistency evidence, not transactional proof; stale reads and provider-controlled tombstones remain possible.
Security boundaries
- API key comes only from non-empty
LINEAR_API_KEY; it is never persisted or included in hooks. - Configured token values are redacted from bounded transport-error previews.
- Endpoint validation rejects credentials sent over non-HTTPS remote URLs.
- Normal operations are non-interactive and do not invoke a shell. Optional Git, GitHub, and browser helpers launch fixed executables with argument arrays.
- Setup records current executable's absolute path instead of relying on mutable
PATHresolution. - Raw GraphQL has full authority granted to API key. Review query and variables before executing mutations.
- Repository is self-contained and never invokes
linearorlinear-cli.
Development
CI and release gates use Rust 1.87.0 with committed lockfile:
Read-only AXI vs official Linear MCP benchmark harness: see benchmarks/linear/README.md.
Integration tests use local TCP GraphQL mocks, temporary home directories, and fake credentials; they do not require developer Linear access. CI currently validates Ubuntu. Platform-specific URL opening has macOS, Linux, and Windows code paths.
Maintainer and project references: