linear
Skill0 starsChanged 3 months agoMITView on GitHub
Linear: manage issues, projects, teams via GraphQL + curl.
- Reads credentials
- Sends data out
We split this skill into the rules it gives an agent
9 rules. 0 are common, 2 are distinctive, and 7 are very specific to this project.
0
Common
Most of the 74 edited versions we read keep these.
2
Distinctive
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 54
All subcommands: whoami, list-teams, list-projects, list-states, list-issues, get-issue, search-issues, create-issue, update-issue, …
- Distinctive: line 56
Use the script when: you want a quick answer without crafting GraphQL.
- Distinctive: line 273
Document URLs look like:
- Very specific: line 349
Default page size: 50. Max: 250.
- Very specific: line 371
Use first: N to limit results and reduce complexity cost
- Very specific: line 376
Always use terminal tool with curl for API calls — do NOT use web_extract or browser
- Very specific: line 377
Always check the errors array in GraphQL responses — HTTP 200 can still contain errors
- Very specific: line 378
If stateId is omitted when creating issues, Linear defaults to the first backlog state
- Very specific: line 380
Use python3 -m json.tool or jq to format JSON responses for readability
“Common” is measured: kept by at least 6 in 10 of the 74 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Linear — Issue & Project Management
Manage Linear issues, projects, and teams directly via the GraphQL API using curl. No MCP server, no OAuth flow, no extra dependencies.
Setup
- Get a personal API key from Linear Settings > Account > Security & access > Personal API keys (URL: https://linear.app/settings/account/security). Note: the org-level Settings > API page only shows OAuth apps and workspace-member keys, not personal keys.
- Set
LINEAR_API_KEYin your environment (viahermes setupor your env config)
API Basics
- Endpoint:
https://api.linear.app/graphql(POST) - Auth header:
Authorization: $LINEAR_API_KEY(no "Bearer" prefix for API keys) - All requests are POST with
Content-Type: application/json - Both UUIDs and short identifiers (e.g.,
ENG-123) work forissue(id:)
Base curl pattern:
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ viewer { id name } }"}' | python3 -m json.toolPython helper script (ergonomic alternative)
For faster one-liners that don't need hand-written GraphQL, this skill ships a stdlib Python CLI at scripts/linear_api.py. Zero dependencies. Same auth (reads LINEAR_API_KEY).
SCRIPT=$(dirname "$(find ~/.hermes -path '*skills/productivity/linear/scripts/linear_api.py' 2>/dev/null | head -1)")/linear_api.py
python3 "$SCRIPT" whoami
python3 "$SCRIPT" list-teams
python3 "$SCRIPT" get-issue ENG-42
python3 "$SCRIPT" get-document 38359beef67c # fetch a doc by slugId from the URL
python3 "$SCRIPT" raw 'query { viewer { name } }'All subcommands: whoami, list-teams, list-projects, list-states, list-issues, get-issue, search-issues, create-issue, update-issue, update-status, add-comment, list-documents, get-document, search-documents, raw. Run with --help for flags.
Use the script when: you want a quick answer without crafting GraphQL. Use curl when: you need a query the script doesn't wrap, or you want to compose filters inline.
Workflow States
Linear uses WorkflowState objects with a type field. 6 state types:
| Type | Description |
triage | Incoming issues needing review |
backlog | Acknowledged but not yet planned |
unstarted | Planned/ready but not started |
started | Actively being worked on |
completed | Done |
canceled | Won't do |
Each team has its own named states (e.g., "In Progress" is type started). To change an issue's status, you need the stateId (UUID) of the target state — query workflow states first.
Priority values: 0 = None, 1 = Urgent, 2 = High, 3 = Medium, 4 = Low
Common Queries
Get current user
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ viewer { id name email } }"}' | python3 -m json.toolList teams
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ teams { nodes { id name key } } }"}' | python3 -m json.toolList workflow states for a team
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ workflowStates(filter: { team: { key: { eq: \"ENG\" } } }) { nodes { id name type } } }"}' | python3 -m json.toolList issues (first 20)
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ issues(first: 20) { nodes { identifier title priority state { name type } assignee { name } team { key } url } pageInfo { hasNextPage endCursor } } }"}' | python3 -m json.toolList my assigned issues
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ viewer { assignedIssues(first: 25) { nodes { identifier title state { name type } priority url } } } }"}' | python3 -m json.toolGet a single issue (by identifier like ENG-123)
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ issue(id: \"ENG-123\") { id identifier title description priority state { id name type } assignee { id name } team { key } project { name } labels { nodes { name } } comments { nodes { body user { name } createdAt } } url } }"}' | python3 -m json.toolSearch issues by text
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ issueSearch(query: \"bug login\", first: 10) { nodes { identifier title state { name } assignee { name } url } } }"}' | python3 -m json.toolFilter issues by state type
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ issues(filter: { state: { type: { in: [\"started\"] } } }, first: 20) { nodes { identifier title state { name } assignee { name } } } }"}' | python3 -m json.toolFilter by team and assignee
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ issues(filter: { team: { key: { eq: \"ENG\" } }, assignee: { email: { eq: \"user@example.com\" } } }, first: 20) { nodes { identifier title state { name } priority } } }"}' | python3 -m json.toolList projects
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ projects(first: 20) { nodes { id name description progress lead { name } teams { nodes { key } } url } } }"}' | python3 -m json.toolList team members
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ users { nodes { id name email active } } }"}' | python3 -m json.toolList labels
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ issueLabels { nodes { id name color } } }"}' | python3 -m json.toolCommon Mutations
Create an issue
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier title url } } }",
"variables": {
"input": {
"teamId": "TEAM_UUID",
"title": "Fix login bug",
"description": "Users cannot login with SSO",
"priority": 2
}
}
}' | python3 -m json.toolUpdate issue status
First get the target state UUID from the workflow states query above, then:
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { stateId: \"STATE_UUID\" }) { success issue { identifier state { name type } } } }"}' | python3 -m json.toolAssign an issue
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { assigneeId: \"USER_UUID\" }) { success issue { identifier assignee { name } } } }"}' | python3 -m json.toolSet priority
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { priority: 1 }) { success issue { identifier priority } } }"}' | python3 -m json.toolAdd a comment
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "mutation { commentCreate(input: { issueId: \"ISSUE_UUID\", body: \"Investigated. Root cause is X.\" }) { success comment { id body } } }"}' | python3 -m json.toolSet due date
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { dueDate: \"2026-04-01\" }) { success issue { identifier dueDate } } }"}' | python3 -m json.toolAdd labels to an issue
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { labelIds: [\"LABEL_UUID_1\", \"LABEL_UUID_2\"] }) { success issue { identifier labels { nodes { name } } } } }"}' | python3 -m json.toolAdd issue to a project
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "mutation { issueUpdate(id: \"ENG-123\", input: { projectId: \"PROJECT_UUID\" }) { success issue { identifier project { name } } } }"}' | python3 -m json.toolCreate a project
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation($input: ProjectCreateInput!) { projectCreate(input: $input) { success project { id name url } } }",
"variables": {
"input": {
"name": "Q2 Auth Overhaul",
"description": "Replace legacy auth with OAuth2 and PKCE",
"teamIds": ["TEAM_UUID"]
}
}
}' | python3 -m json.toolDocuments
Linear Documents are prose docs (RFCs, specs, notes) stored alongside issues. They have their own documents root query and document(id:) single-fetch.
Document URLs and slugId
Document URLs look like:
https://linear.app/<workspace>/document/<slug>-<hexSlugId>The trailing hex segment is the slugId. Example: https://linear.app/nousresearch/document/rfc-hermes-permission-gateway-discord-38359beef67c → slugId is 38359beef67c.
Important schema detail: the Markdown body is in the content field. The ProseMirror JSON is in contentState (not contentData — that field does not exist and the API returns 400).
Fetch a document by slugId
document(id:) only accepts UUIDs. To fetch by the URL's hex slug, filter the collection:
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "query($s: String!) { documents(filter: { slugId: { eq: $s } }, first: 1) { nodes { id title content contentState slugId url creator { name } project { name } updatedAt } } }", "variables": {"s": "38359beef67c"}}' \
| python3 -m json.toolOr via the Python helper:
python3 scripts/linear_api.py get-document 38359beef67cFetch a document by UUID
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ document(id: \"11700cff-b514-4db3-afcc-3ed1afacba1c\") { title content url } }"}' \
| python3 -m json.toolList recent documents
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ documents(first: 25, orderBy: updatedAt) { nodes { id title slugId url updatedAt project { name } } } }"}' \
| python3 -m json.toolSearch documents by title
Linear's schema has no searchDocuments root. Use a title-substring filter instead:
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ documents(filter: { title: { containsIgnoreCase: \"RFC\" } }, first: 25) { nodes { title slugId url } } }"}' \
| python3 -m json.toolPagination
Linear uses Relay-style cursor pagination:
# First page
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ issues(first: 20) { nodes { identifier title } pageInfo { hasNextPage endCursor } } }"}' | python3 -m json.tool
# Next page — use endCursor from previous response
curl -s -X POST https://api.linear.app/graphql \
-H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ issues(first: 20, after: \"CURSOR_FROM_PREVIOUS\") { nodes { identifier title } pageInfo { hasNextPage endCursor } } }"}' | python3 -m json.toolDefault page size: 50. Max: 250. Always use first: N to limit results.
Filtering Reference
Comparators: eq, neq, in, nin, lt, lte, gt, gte, contains, startsWith, containsIgnoreCase
Combine filters with or: [...] for OR logic (default is AND within a filter object).
Typical Workflow
- Query teams to get team IDs and keys
- Query workflow states for target team to get state UUIDs
- List or search issues to find what needs work
- Create issues with team ID, title, description, priority
- Update status by setting
stateIdto the target workflow state - Add comments to track progress
- Mark complete by setting
stateIdto the team's "completed" type state
Rate Limits
- 5,000 requests/hour per API key
- 3,000,000 complexity points/hour
- Use
first: Nto limit results and reduce complexity cost - Monitor
X-RateLimit-Requests-Remainingresponse header
Important Notes
- Always use
terminaltool withcurlfor API calls — do NOT useweb_extractorbrowser - Always check the
errorsarray in GraphQL responses — HTTP 200 can still contain errors - If
stateIdis omitted when creating issues, Linear defaults to the first backlog state - The
descriptionfield supports Markdown - Use
python3 -m json.toolorjqto format JSON responses for readability
Other versions80 projects use a version of this.
Word for word · 2
- tunnckoCore/skills★ 2100% same
- wm2313/my-skills★ 0100% same
Edited · 6
- math-inc/OpenGauss★ 1.3k94% same
- Tommy-yw/RunbookHermes★ 54696% same
- RedWoodOG/Hermes-Desktop★ 17796% same
- gquthier/CLAWG★ 1694% same
- qhkm/zeptoclaw-skills★ 417% same
- ekajaya740/skills★ 1100% same
Wrote their own version · 72
- lobehub/lobe-chat★ 84ksame name
- lobehub/lobehub★ 84ksame name
- openai/skills★ 27ksame name
- openai/symphony★ 27ksame name
- anthropics/claude-quickstarts★ 19ksame name
- ComposioHQ/awesome-codex-skills★ 17ksame name
- composio-community/awesome-codex-skills★ 16ksame name
- anyproto/anytype-ts★ 9ksame name
- vercel-labs/emulate★ 2ksame name
- Lilac-Labs/gini-agent★ 1.9ksame name
62 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
Used alongsideProjects with it also use agent-deep-links, helium-mcp and 9 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
ComposioHQSkillagent-deep-links
Build, validate, and troubleshoot deep links for Codex, Cursor, VS Code, Visual Studio, and similar tools. Use when users ask for clickable links (especially in Slack) that open threads, files, folders, or app settings.
Also in 3 projects that use this
ComposioHQSkillhelium-mcp
Search real-time news with bias scoring, get live stock/ETF/crypto data with AI analysis, ML options pricing, balanced news synthesis, and meme search via the Helium MCP server.
Also in 3 projects that use this
petestewartSkilldatadog-logs
Query Datadog logs via the Logs Search API v2. Use when the user asks to "check the logs", "search logs", "find errors in Datadog", "look up logs for", "show me production logs", "check for errors", "search Datadog", or any request involving log search, error investigation, or production debugging that requires Datadog log data.
Also in 3 projects that use this
OpenAISkillpull
Also in 5 projects that use this
- +7 more from ComposioHQ
In this repository82 other files it gives its agents.
- AGENTS.mdAGENTS.md
- apple-notesSkill
- apple-remindersSkill
- findmySkill
- imessageSkill
- macos-computer-useSkill
- claude-codeSkill
- codexSkill
- hermes-agentSkill
- kanban-codex-laneSkill
- opencodeSkill
- architecture-diagramSkill
- ascii-artSkill
- ascii-videoSkill
- baoyu-article-illustratorSkill
- baoyu-comicSkill
- baoyu-infographicSkill
- claude-designSkill
- comfyuiSkill
- ideationSkill
- design-mdSkill
- excalidrawSkill
- humanizerSkill
- manim-videoSkill
- p5jsSkill
- pixel-artSkill
- popular-web-designsSkill
- pretextSkill
- sketchSkill
- songwriting-and-ai-musicSkill
- touchdesigner-mcpSkill
- jupyter-live-kernelSkill
- kanban-orchestratorSkill
- kanban-workerSkill
- webhook-subscriptionsSkill
- dogfoodSkill
- himalayaSkill
- minecraft-modpack-serverSkill
- pokemon-playerSkill
- codebase-inspectionSkill
- github-authSkill
- github-code-reviewSkill
- github-issuesSkill
- github-pr-workflowSkill
- github-repo-managementSkill
- native-mcpSkill
- gif-searchSkill
- heartmulaSkill
- songseeSkill
- spotifySkill
- youtube-contentSkill
- huggingface-hubSkill
- obsidianSkill
- airtableSkill
- google-workspaceSkill
- mapsSkill
- nano-pdfSkill
- notionSkill
- ocr-and-documentsSkill
- powerpointSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

