# Linear — 问题与项目管理
通过 GraphQL API 直接使用 curl 管理 Linear 的问题、项目和团队。无需 MCP 服务器、无需 OAuth 流程、无需额外依赖。
设置
- 从 Linear 设置 > 账户 > 安全与访问 > 个人 API 密钥 获取个人 API 密钥(URL:https://linear.app/settings/account/security)。注意:组织级的 *设置 > API* 页面只显示 OAuth 应用和工作区成员密钥,不显示个人密钥。
- 在环境中设置 `LINEAR_API_KEY`(通过 `hermes setup` 或你的环境配置)
API 基础
- 端点:
https://api.linear.app/graphql(POST) - 认证头:
Authorization: $LINEAR_API_KEY(API 密钥不需要 "Bearer" 前缀) - 所有请求都是 POST,带
Content-Type: application/json - UUID 和短标识符(如
ENG-123)都可用于issue(id:)
基础 curl 模式:
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.tool
Python 辅助脚本(更省力的替代方案)
如果你想要更快的单行命令、不必手写 GraphQL,本技能附带一个位于 scripts/linear_api.py 的标准库 Python CLI。零依赖。认证方式相同(读取 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 } }'
所有子命令: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。运行 --help 查看参数。
什么时候用脚本:你想要快速答案而不想手写 GraphQL。什么时候用 curl:你需要脚本未封装的查询,或想内联组合过滤器。
工作流状态
Linear 使用带 type 字段的 WorkflowState 对象。6 种状态类型:
| 类型 | 说明 |
|------|-------------|
| triage | 需要评审的入站问题 |
| backlog | 已确认但尚未规划 |
| unstarted | 已规划/就绪但未开始 |
| started | 正在积极处理 |
| completed | 已完成 |
| canceled | 不做了 |
每个团队都有自己的命名状态(例如「In Progress」的类型是 started)。要更改问题的状态,你需要目标状态的 stateId(UUID)——先查询工作流状态。
优先级值:0 = 无,1 = 紧急,2 = 高,3 = 中,4 = 低
常用查询
获取当前用户
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.tool
列出团队
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.tool
列出团队的工作流状态
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.tool
列出问题(前 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.tool
列出分配给我的问题
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.tool
获取单个问题(按标识符如 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.tool
按文本搜索问题
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.tool
按状态类型过滤问题
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.tool
按团队和负责人过滤
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.tool
列出项目
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.tool
列出团队成员
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.tool
列出标签
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.tool
常用变更(Mutations)
创建问题
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.tool
更新问题状态
先从上面的工作流状态查询中获取目标状态的 UUID,然后:
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.tool
分配问题
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.tool
设置优先级
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.tool
添加评论
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.tool
设置截止日期
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.tool
给问题添加标签
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.tool
将问题加入项目
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.tool
创建项目
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.tool
文档(Documents)
Linear 文档是与问题存放在一起的散文式文档(RFC、规格、笔记)。它们有自己的 documents 根查询和 document(id:) 单条获取。
文档 URL 与 `slugId`
文档 URL 形如:
https://linear.app//document/-
末尾的十六进制段就是 slugId。示例:https://linear.app/nousresearch/document/rfc-hermes-permission-gateway-discord-38359beef67c → slugId 是 38359beef67c。
重要的 schema 细节:Markdown 正文在 content 字段中。ProseMirror JSON 在 contentState 中(不是 contentData——该字段不存在,API 会返回 400)。
按 slugId 获取文档
document(id:) 只接受 UUID。要按 URL 中的十六进制 slug 获取,过滤集合:
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.tool
或者用 Python 辅助脚本:
python3 scripts/linear_api.py get-document 38359beef67c
按 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.tool
列出最近的文档
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.tool
按标题搜索文档
Linear 的 schema 没有 searchDocuments 根查询。改用标题子串过滤器:
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.tool
分页
Linear 使用 Relay 风格的游标分页:
# 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.tool
默认页大小:50。最大:250。始终使用 first: N 限制结果数。
过滤参考
比较器:eq、neq、in、nin、lt、lte、gt、gte、contains、startsWith、containsIgnoreCase
用 or: [...] 组合过滤器实现 OR 逻辑(过滤器对象内部默认是 AND)。
典型工作流
- 查询团队,获取团队 ID 和 key
- 查询工作流状态,获取目标团队的状态 UUID
- 列出或搜索问题,找到需要处理的内容
- 创建问题,带上团队 ID、标题、描述、优先级
- 更新状态,把 `stateId` 设为目标工作流状态
- 添加评论,跟踪进度
- 标记完成,把 `stateId` 设为团队类型为「completed」的状态
速率限制
- 每个 API 密钥每小时 5,000 次请求
- 每小时 3,000,000 复杂度点
- 使用
first: N限制结果数,降低复杂度成本 - 监控
X-RateLimit-Requests-Remaining响应头
重要提示
- API 调用始终使用
terminal工具 +curl——不要用web_extract或browser - 始终检查 GraphQL 响应中的
errors数组——HTTP 200 也可能包含错误 - 创建问题时如果省略
stateId,Linear 默认为第一个 backlog 状态 description字段支持 Markdown- 用
python3 -m json.tool或jq格式化 JSON 响应以便阅读
安装指南
复制下方命令,在终端运行即可安装:
使用指南
安装完成后,在对话框中直接使用此技能。