Перейти к содержанию

On this page Linear: управление задачами, проектами и командами через GraphQL + curl.

Метаданные навыка

| | |---|---| |Источник| Встроенный (установлен по умолчанию) | |Путь| skills/productivity/linear | |Версия| 1.0.0 | |Автор| Hermes Agent | |Лицензия| MIT | |Теги| Linear, Project Management, Issues, GraphQL, API, Productivity |

Справочная информация: полный SKILL.md

info Далее приведено полное определение навыка, которое Hermes загружает при активации этого навыка. Агент видит эти инструкции, когда навык активен.

Linear — Управление задачами и проектами

Управляйте задачами, проектами и командами Linear напрямую через GraphQL API с помощью curl. Никакого MCP-сервера, OAuth-потока или дополнительных зависимостей.

Настройка

  1. Получите личный API-ключ в Linear Settings > Account > Security & access > Personal API keys (URL: https://linear.app/settings/account/security). Примечание: страница Settings > API на уровне организации показывает только OAuth-приложения и ключи участников рабочего пространства, а не личные ключи.
  2. Установите LINEAR_API_KEY в вашем окружении (через hermes setup или в конфигурации окружения).

Основы API

  • Endpoint: https://api.linear.app/graphql (POST)
  • Заголовок авторизации: Authorization: $LINEAR_API_KEY (префикс \"Bearer\" для API-ключей не требуется)
  • Все запросы — POST с Content-Type: application/json
  • UUID и короткие идентификаторы (например, ENG-123) работают с issue(id:)

Базовый шаблон curl: [code] 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

[/code]

Python-скрипт-помощник (эргономичная альтернатива)

Для быстрых однострочных команд, не требующих написания GraphQL вручную, в состав навыка входит CLI на Python из стандартной библиотеки по пути scripts/linear_api.py. Без дополнительных зависимостей. Использует ту же авторизацию (читает LINEAR_API_KEY). [code] 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      # получить документ по slugId из URL
python3 "$SCRIPT" raw 'query { viewer { name } }'

[/code] Все подкоманды: 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 использует объекты WorkflowState с полем type. 6 типов состояний: | Тип | Описание | |---|---| | triage | Входящие задачи, требующие рассмотрения | | backlog | Подтверждённые, но ещё не запланированные | | unstarted | Запланированные/готовые, но не начатые | | started | Активно выполняемые | | completed | Завершённые | | canceled | Не будут выполняться | У каждой команды есть собственные именованные состояния (например, «In Progress» имеет тип started). Чтобы изменить статус задачи, нужен stateId (UUID) целевого состояния — сначала запросите состояния рабочего процесса. Значения приоритета: 0 = Нет, 1 = Срочно, 2 = Высокий, 3 = Средний, 4 = Низкий

Часто используемые запросы

Получить текущего пользователя

[code] 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

[/code]

Список команд

[code] 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

[/code]

Список состояний рабочего процесса для команды

[code] 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

[/code]

Список задач (первые 20)

[code] 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

[/code]

Список моих назначенных задач

[code] 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

[/code]

Получить одну задачу (по идентификатору, например ENG-123)

[code] 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

[/code]

Поиск задач по тексту

[code] 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

[/code]

Фильтрация задач по типу состояния

[code] 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

[/code]

Фильтрация по команде и исполнителю

[code] 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

[/code]

Список проектов

[code] 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

[/code]

Список участников команды

[code] 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

[/code]

Список меток

[code] 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

[/code]

Часто используемые мутации

Создать задачу

[code] 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

[/code]

Обновить статус задачи

Сначала получите UUID целевого состояния из запроса состояний рабочего процесса выше, затем: [code] 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

[/code]

Назначить задачу

[code] 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

[/code]

Установить приоритет

[code] 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

[/code]

Добавить комментарий

[code] 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

[/code]

Установить срок выполнения

[code] 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

[/code]

Добавить метки к задаче

[code] 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

[/code]

Добавить задачу в проект

[code] 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

[/code]

Создать проект

[code] 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

[/code]

Документы

Документы Linear — это текстовые документы (RFC, спецификации, заметки), хранящиеся вместе с задачами. У них есть собственный корневой запрос documents и единичная выборка document(id:).

URL-адреса документов и slugId

URL-адреса документов выглядят так: [code] https://linear.app//document/-

[/code] Завершающий шестнадцатеричный сегмент — это slugId. Пример: https://linear.app/nousresearch/document/rfc-hermes-permission-gateway-discord-38359beef67cslugId равен 38359beef67c. Важная деталь схемы: тело в Markdown находится в поле content. JSON в формате ProseMirror находится в contentState (не contentData — это поле не существует, и API вернёт 400).

Получить документ по slugId

document(id:) принимает только UUID. Чтобы получить документ по шестнадцатеричному slug из URL, отфильтруйте коллекцию: [code] 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

[/code] Или через Python-помощник: [code] python3 scripts/linear_api.py get-document 38359beef67c

[/code]

Получить документ по UUID

[code] 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

[/code]

Список недавних документов

[code] 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

[/code]

Поиск документов по названию

В схеме Linear нет корневого запроса searchDocuments. Вместо этого используйте фильтр по подстроке названия: [code] 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

[/code]

Пагинация

Linear использует курсорную пагинацию в стиле Relay: [code] # Первая страница 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

# Следующая страница — используйте endCursor из предыдущего ответа
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

[/code] Размер страницы по умолчанию: 50. Максимум: 250. Всегда используйте first: N для ограничения результатов.

Справочник по фильтрации

Компараторы: eq, neq, in, nin, lt, lte, gt, gte, contains, startsWith, containsIgnoreCase Комбинируйте фильтры с or: [...] для логики ИЛИ (по умолчанию внутри объекта фильтра применяется И).

Типичный рабочий процесс

  1. Запросить команды, чтобы получить ID команд и ключи
  2. Запросить состояния рабочего процесса для целевой команды, чтобы получить UUID состояний
  3. Вывести список или выполнить поиск задач, чтобы найти то, что требует работы
  4. Создать задачи с ID команды, заголовком, описанием, приоритетом
  5. Обновить статус, установив stateId в целевое состояние рабочего процесса
  6. Добавить комментарии для отслеживания прогресса
  7. Отметить как выполненное, установив stateId в состояние типа «completed» команды

Лимиты запросов

  • 5 000 запросов/час на один API-ключ
  • 3 000 000 единиц сложности/час
  • Используйте first: N для ограничения результатов и снижения стоимости сложности
  • Следите за заголовком ответа X-RateLimit-Requests-Remaining

Важные замечания