| name | lorenz-linear |
| description | Use Lorenz's `linear_graphql` client tool for raw Linear GraphQL
operations such as comment editing and upload flows.
|
Linear GraphQL
Use this skill for raw Linear GraphQL work during Lorenz app-server sessions.
Primary tool
Use the linear_graphql client tool exposed by Lorenz's app-server session.
It reuses Lorenz's configured Linear auth for the session.
Tool input:
{
"query": "query or mutation document",
"variables": {
"optional": "graphql variables object"
}
}
Tool behavior:
- Send one GraphQL operation per tool call.
- Treat a top-level
errors array as a failed GraphQL operation even if the
tool call itself completed.
- Keep queries/mutations narrowly scoped; ask only for the fields you need.
Discovering unfamiliar operations
When you need an unfamiliar mutation, input type, or object field, use targeted
introspection through linear_graphql.
List mutation names:
query ListMutations {
__type(name: "Mutation") {
fields {
name
}
}
}
Inspect a specific input object:
query CommentCreateInputShape {
__type(name: "CommentCreateInput") {
inputFields {
name
type {
kind
name
ofType {
kind
name
}
}
}
}
}
Common workflows
Query an issue by key, identifier, or id
Use these progressively:
- Start with
issue(id: $key) when you have a ticket key such as MT-686.
- Fall back to
searchIssues(term: ...) when you need search semantics.
- Once you have the internal issue id, prefer
issue(id: $id) for narrower reads.
- Do not query
Issue.links; Linear exposes URL and PR links through
attachments.
- Do not use
issues(filter: { identifier: ... }); IssueFilter does not
support an identifier field.
Lookup by issue key:
query IssueByKey($key: String!) {
issue(id: $key) {
id
identifier
title
state {
id
name
type
}
project {
id
name
}
branchName
url
description
updatedAt
attachments {
nodes {
id
title
url
sourceType
}
}
}
}
Search fallback:
query SearchIssue($term: String!) {
searchIssues(term: $term, first: 1) {
nodes {
id
identifier
title
state {
id
name
type
}
project {
id
name
}
branchName
url
description
updatedAt
}
}
}
Resolve a key to an internal id:
query IssueByIdOrKey($id: String!) {
issue(id: $id) {
id
identifier
title
}
}
Read the issue once the internal id is known:
query IssueDetails($id: String!) {
issue(id: $id) {
id
identifier
title
url
description
state {
id
name
type
}
project {
id
name
}
attachments {
nodes {
id
title
url
sourceType
}
}
}
}
Query team workflow states for an issue
Use this before changing issue state when you need the exact stateId:
query IssueTeamStates($id: String!) {
issue(id: $id) {
id
team {
id
key
name
states {
nodes {
id
name
type
}
}
}
}
}
Edit an existing comment
Comment records expose resolvedAt and archivedAt. Do not query
resolved or archived.
Use commentUpdate through linear_graphql:
mutation UpdateComment($id: String!, $body: String!) {
commentUpdate(id: $id, input: { body: $body }) {
success
comment {
id
body
}
}
}
Create a comment
Use commentCreate through linear_graphql:
mutation CreateComment($issueId: String!, $body: String!) {
commentCreate(input: { issueId: $issueId, body: $body }) {
success
comment {
id
url
}
}
}
Create an issue
Resolve the project's team and a workflow state first, then call issueCreate. Look up the
project by slug to get its id, its team id, and the team's workflow states:
query ProjectForCreate($slug: String!) {
projects(filter: { slugId: { eq: $slug } }, first: 1) {
nodes {
id
teams(first: 1) {
nodes {
id
states(first: 100) {
nodes {
id
name
type
}
}
}
}
}
}
}
Pick the destination stateId by status name when one is requested, otherwise the first
unstarted state, otherwise the first state. Then create the issue:
mutation CreateIssue($input: IssueCreateInput!) {
issueCreate(input: $input) {
success
issue {
id
identifier
url
}
}
}
Pass { teamId, projectId, stateId, title, description, assigneeId } in input; include
assigneeId only when assigning the issue to a specific user.
Move an issue to a different state
Use issueUpdate with the destination stateId:
mutation MoveIssueToState($id: String!, $stateId: String!) {
issueUpdate(id: $id, input: { stateId: $stateId }) {
success
issue {
id
identifier
state {
id
name
}
}
}
}
Attach a GitHub PR to an issue
Use the GitHub-specific attachment mutation when linking a PR:
mutation AttachGitHubPR($issueId: String!, $url: String!, $title: String) {
attachmentLinkGitHubPR(issueId: $issueId, url: $url, title: $title, linkKind: links) {
success
attachment {
id
title
url
}
}
}
If Linear returns Duplicate attachment for duplicate url, treat the PR as
already linked and continue. Do not retry with a different attachment mutation
for the same URL.
If you only need a plain URL attachment and do not care about GitHub-specific
link metadata, use:
mutation AttachURL($issueId: String!, $url: String!, $title: String) {
attachmentLinkURL(issueId: $issueId, url: $url, title: $title) {
success
attachment {
id
title
url
}
}
}
Introspection patterns used during schema discovery
Use these when the exact field or mutation shape is unclear:
Do not run full __schema introspection. Linear rejects overly broad schema
queries as too complex. Use targeted __type(name: "...") queries instead.
query QueryFields {
__type(name: "Query") {
fields {
name
}
}
}
query IssueFieldArgs {
__type(name: "Query") {
fields {
name
args {
name
type {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
Upload a video to a comment
Do this in three steps:
- Call
linear_graphql with fileUpload to get uploadUrl, assetUrl, and
any required upload headers.
- Upload the local file bytes to
uploadUrl with curl -X PUT and the exact
headers returned by fileUpload.
- Call
linear_graphql again with commentCreate (or commentUpdate) and
include the resulting assetUrl in the comment body.
Useful mutations:
mutation FileUpload($filename: String!, $contentType: String!, $size: Int!, $makePublic: Boolean) {
fileUpload(filename: $filename, contentType: $contentType, size: $size, makePublic: $makePublic) {
success
uploadFile {
uploadUrl
assetUrl
headers {
key
value
}
}
}
}
Usage rules
- Use
linear_graphql for comment edits, uploads, and ad-hoc Linear API
queries.
- Do not upload
.md, .txt, or other text documents as attachments unless
the user explicitly asks or the content is too large to reasonably inline;
prefer fenced code blocks in comments instead.
- Prefer the narrowest issue lookup that matches what you already know:
key -> search fallback -> internal id.
- For state transitions, fetch team states first and use the exact
stateId
instead of hardcoding names inside mutations.
- Prefer
attachmentLinkGitHubPR over a generic URL attachment when linking a
GitHub PR to a Linear issue.
- Treat duplicate attachment URL errors as success when the URL is already the
PR you intended to attach.
- Do not introduce new raw-token shell helpers for GraphQL access.
- If you need shell work for uploads, only use it for signed upload URLs
returned by
fileUpload; those URLs already carry the needed authorization.