A command-line tool for the Linear GraphQL API.
Patterned after an internal request-cli tool: a thin, low-level passthrough
as the core primitive (linear graphql ...), with a handful of convenience
subcommands layered on top (issue, team, project, viewer).
cargo build --releaseThe compiled binary will be at target/release/linear.
# Copies the binary to ~/.local/bin (no sudo required)
target/release/linear configure install
# Or install to a directory of your choice
target/release/linear configure install --path /usr/local/binThe installed path is recorded in ~/.config/linear-cli/installation so it
can be cleanly reversed:
linear configure uninstall# Bash
linear configure completions bash > ~/.local/share/bash-completion/completions/linear
# Zsh (make sure ~/.zfunc is in your $fpath)
linear configure completions zsh > ~/.zfunc/_linear
# Fish
linear configure completions fish > ~/.config/fish/completions/linear.fish
# PowerShell
linear configure completions powershell | Out-File -Encoding UTF8 linear.ps1
# Elvish
linear configure completions elvish > ~/.elvish/lib/linear.elvLinear personal API keys are used (no OAuth app registration required). Generate one at https://linear.app/settings/account/security, then:
linear auth set --api-key lin_api_xxxxxxxx
# or, to be prompted (input hidden) and verified against the API:
linear auth setThis stores the key in $XDG_CONFIG_HOME/linear-cli/config (typically
~/.config/linear-cli/config):
endpoint=https://api.linear.app/graphql
api_key=lin_api_xxxxxxxx
Personal API keys are sent as-is in the Authorization header (Linear does
not use a Bearer prefix for these, unlike OAuth tokens).
Check who you're authenticated as:
linear auth statusMultiple profiles are supported by suffixing the filename with -config:
~/.config/linear-cli/work-config
~/.config/linear-cli/personal-config
linear -c work issue list
linear -c personal issue listLINEAR_CLI_CONFIG— profile name or path, same precedence as-cLINEAR_API_KEY/LINEAR_ENDPOINT— override the config file
The core primitive. Takes a literal query/mutation string, @path/to/file,
or - (stdin):
linear graphql 'query { viewer { id name } }'
linear graphql @query.graphql --variable id=abc123
echo 'query { viewer { id } }' | linear graphql -
linear graphql 'mutation IssueCreate($input: IssueCreateInput!) {
issueCreate(input: $input) { success issue { identifier url } }
}' --variables-json '{"input":{"teamId":"...","title":"Fix the thing"}}'linear viewer
linear team list
linear issue list --team ENG --mine
linear issue view ENG-123
linear issue create --team ENG --title "Fix the thing" --priority 2
linear issue update ENG-123 --state <workflow-state-id>
linear issue comment ENG-123 "Looking into this now."
linear project list
linear project view <project-id>
linear project create --name "Q1 Roadmap" --team-id <team-id>-c, --config <PROFILE>— alternate config profile-H, --header <"Key: Value">— extra request header (repeatable)--dry-run— print the equivalent curl command instead of sending it-v, --verbose— log request/response and rate-limit headers--read-only— refuse to run anything that looks like a mutation--json— compact JSON output instead of pretty-printed/tabular
# Default: pulls the published SDL from github.com/linear/linear (no auth,
# ~1MB, fast).
linear configure fetch-schema
# Live introspection instead. Cheap in rate-limit/complexity terms (it only
# reads the static schema, no business-data resolvers) but the response is
# several MB, so don't run this often.
linear configure fetch-schema --introspectCached at $XDG_DATA_HOME/linear-cli/schema.graphql (or schema.json for
--introspect).
linear ships with a bundled Agent Skills
Markdown file describing how an LLM/agent should use this CLI:
linear configure print-skill > linear-cli/SKILL.mdDrop the output into a skills directory your agent harness reads (e.g. for
pi, .pi/skills/linear-cli/SKILL.md
or ~/.pi/agent/skills/linear-cli/SKILL.md).
The CLI is a thin wrapper around a reusable library crate (same package,
target name linear). Add it as a path/git dependency and use it directly:
[dependencies]
linear = { path = "../linear-cli" }use linear::client::GraphQLClient;
use linear::config::Config;
use linear::ops;
fn main() -> anyhow::Result<()> {
let config = Config { api_key: "lin_api_...".into(), ..Config::default() };
let client = GraphQLClient::new(&config, Vec::new(), false);
let me = ops::viewer(&client)?;
println!("Hello, {}", me.name);
let issues = ops::list_issues(&client, 50, ops::IssueListFilter {
team_key: Some("ENG".into()),
mine: true,
})?;
for issue in issues {
println!("{}: {}", issue.identifier, issue.title);
}
// Escape hatch: raw GraphQL for anything `ops` doesn't cover, returning
// serde_json::Value.
let raw = client.execute("query { viewer { id } }", None)?.into_data()?;
println!("{raw}");
Ok(())
}See examples/sdk_usage.rs for a runnable version
(LINEAR_API_KEY=... cargo run --example sdk_usage).
Modules:
linear::client—GraphQLClient(rawexecute, dry-run curl rendering)linear::config—Config, XDG config/data dir + profile resolutionlinear::model— typed request/response structs (Issue,Team,Project,Viewer,IssueCreate,IssueUpdate,ProjectCreate, ...)linear::ops— typed operations (viewer,list_issues,get_issue,create_issue,update_issue,comment_issue,list_teams,list_projects,get_project,create_project)linear::schema— fetch/cache the GraphQL SDL or run introspectionlinear::util— variable parsing, mutation detection, query loading helpers used by the CLI's raw passthrough
Note: the CLI's --dry-run/--read-only guards live in the binary
(AppContext), not the library — ops::* functions always execute. If you
want similar guards in your own program, check linear::util::is_mutation
before calling client.execute, or gate calls to ops::* yourself.
Linear's API is rate-limited (complexity-based for most queries). -v shows
x-ratelimit-* response headers so you can see how much headroom you have.