LI

linear-core-workflow-a

A guide to managing issues, sub-tasks, and transitions using the Linear SDK and GraphQL API.

Install

mkdir -p .claude/skills/linear-core-workflow-a && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/8717" && unzip -o skill.zip -d .claude/skills/linear-core-workflow-a && rm skill.zip

Installs to .claude/skills/linear-core-workflow-a

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Issue lifecycle management with Linear: create, update, transition,
67 charsno explicit “when” trigger
Intermediate

Key capabilities

  • Create and update issues
  • Transition issue workflow states
  • Build parent/sub-issue hierarchies
  • Manage issue labels and relations
  • Add comments to issues
  • Attach links to issues

How it works

The skill utilizes the Linear SDK to interact with the GraphQL API, allowing for CRUD operations on issues, state transitions based on workflow types, and management of issue relationships and hierarchies.

Inputs & outputs

You give it
Issue data and metadata
You get back
Updated Linear issue state

When to use linear-core-workflow-a

  • Create issues programmatically
  • Automate state transitions
  • Build parent/sub-issue hierarchies
  • Manage issue comments and labels

About this skill

Linear Core Workflow A: Issue Lifecycle

Overview

Master issue lifecycle management: creating, updating, transitioning states, building parent/sub-issue hierarchies, managing labels, and commenting. Linear issues flow through typed workflow states (triage -> backlog -> unstarted -> started -> completed | canceled), belong to a team, and support priorities 0-4, estimates, due dates, labels, and cycle/project assignment.

Prerequisites

  • @linear/sdk installed with API key or OAuth token configured
  • Access to target team(s)
  • Understanding of your team's workflow states

Instructions

Step 1: Create Issues

import { LinearClient } from "@linear/sdk";

const client = new LinearClient({ apiKey: process.env.LINEAR_API_KEY! });

// Get team
const teams = await client.teams();
const team = teams.nodes.find(t => t.key === "ENG") ?? teams.nodes[0];

// Basic issue
const result = await client.createIssue({
  teamId: team.id,
  title: "Implement user authentication",
  description: "Add OAuth 2.0 login flow with Google and GitHub providers.",
  priority: 2, // 0=None, 1=Urgent, 2=High, 3=Medium, 4=Low
});

if (result.success) {
  const issue = await result.issue;
  console.log(`Created: ${issue?.identifier} — ${issue?.title}`);
  console.log(`URL: ${issue?.url}`);
}

// Issue with full metadata
const labelResult = await client.issueLabels({ filter: { name: { eq: "Bug" } } });
const bugLabel = labelResult.nodes[0];
const states = await team.states();
const todoState = states.nodes.find(s => s.type === "unstarted")!;

await client.createIssue({
  teamId: team.id,
  title: "Fix login redirect loop on Safari",
  description: "Users get stuck in infinite redirect after SSO callback.",
  priority: 1,
  stateId: todoState.id,
  assigneeId: "user-uuid",
  labelIds: bugLabel ? [bugLabel.id] : [],
  estimate: 3,
  dueDate: "2026-04-15",
});

Step 2: Update Issues

// Update by issue ID
await client.updateIssue("issue-uuid", {
  title: "Updated title",
  priority: 1,
  estimate: 5,
  dueDate: "2026-04-30",
});

// Find issue by team key + number, then update
const issues = await client.issues({
  filter: { number: { eq: 123 }, team: { key: { eq: "ENG" } } },
});
const issue = issues.nodes[0];
if (issue) {
  await issue.update({
    priority: 2,
    description: "Updated description with more details.",
  });
}

// Add/remove labels
const featureLabel = (await client.issueLabels({
  filter: { name: { eq: "Feature" } },
})).nodes[0];

if (featureLabel) {
  await client.updateIssue(issue.id, {
    labelIds: [...(issue.labelIds ?? []), featureLabel.id],
  });
}

Step 3: State Transitions

// List all workflow states for a team
const teamStates = await team.states();
for (const state of teamStates.nodes) {
  console.log(`${state.name} (type: ${state.type}, position: ${state.position})`);
}
// Output example:
// Triage (type: triage, position: 0)
// Backlog (type: backlog, position: 1)
// Todo (type: unstarted, position: 2)
// In Progress (type: started, position: 3)
// In Review (type: started, position: 4)
// Done (type: completed, position: 5)
// Canceled (type: canceled, position: 6)

// Move to "In Progress"
const inProgress = teamStates.nodes.find(s => s.name === "In Progress");
if (inProgress) {
  await client.updateIssue(issue.id, { stateId: inProgress.id });
}

// Complete an issue
const done = teamStates.nodes.find(s => s.type === "completed");
if (done) {
  await issue.update({ stateId: done.id });
}

Step 4: Parent/Sub-Issue Hierarchy

// Create parent issue
const parentResult = await client.createIssue({
  teamId: team.id,
  title: "Auth system overhaul",
  description: "Epic: modernize authentication infrastructure.",
});
const parent = await parentResult.issue;

// Create sub-issues under parent
await client.createIssue({
  teamId: team.id,
  title: "Implement JWT token refresh",
  parentId: parent!.id,
  priority: 2,
});

await client.createIssue({
  teamId: team.id,
  title: "Add MFA support",
  parentId: parent!.id,
  priority: 3,
});

// List sub-issues
const children = await parent!.children();
for (const child of children.nodes) {
  console.log(`  Sub: ${child.identifier} — ${child.title}`);
}

Step 5: Issue Relations

// Relation types: "blocks", "duplicate", "related"
await client.createIssueRelation({
  issueId: "blocked-issue-id",
  relatedIssueId: "blocking-issue-id",
  type: "blocks",
});

// Mark as duplicate
await client.createIssueRelation({
  issueId: "duplicate-issue-id",
  relatedIssueId: "original-issue-id",
  type: "duplicate",
});

// List relations on an issue
const relations = await issue.relations();
for (const rel of relations.nodes) {
  const related = await rel.relatedIssue;
  console.log(`${rel.type}: ${related?.identifier}`);
}

Step 6: Comments

// Add a comment (supports Markdown)
await client.createComment({
  issueId: issue.id,
  body: "Deployed fix to staging.\n\n```bash\nnpm run test:e2e -- --filter auth\n```\n\nAll 47 tests passing.",
});

// List comments on an issue
const comments = await issue.comments();
for (const comment of comments.nodes) {
  const user = await comment.user;
  console.log(`${user?.name}: ${comment.body.substring(0, 80)}...`);
}

Step 7: Attachments

// Create a link attachment on an issue
await client.createAttachment({
  issueId: issue.id,
  title: "Figma Design",
  url: "https://figma.com/file/xxx",
  subtitle: "Login page redesign",
});

Error Handling

ErrorCauseSolution
Entity not foundInvalid issue ID or deletedVerify with client.issue(id) first
State not foundWrong team's state IDList states for correct team: team.states()
Validation error on createMissing required fieldteamId + title required; priority must be 0-4
Circular dependencyIssue blocks itself transitivelyValidate relation graph before creating
ForbiddenNo write access to teamCheck team membership and API key scope

Examples

Bulk Create from CSV

import { parse } from "csv-parse/sync";
import fs from "fs";

const rows = parse(fs.readFileSync("issues.csv"), { columns: true });
for (const row of rows) {
  const result = await client.createIssue({
    teamId: team.id,
    title: row.title,
    description: row.description,
    priority: parseInt(row.priority) || 3,
  });
  const issue = await result.issue;
  console.log(`Created: ${issue?.identifier}`);
}

Close Stale Issues

const stale = await client.issues({
  filter: {
    state: { type: { in: ["unstarted", "started"] } },
    updatedAt: { lt: new Date(Date.now() - 90 * 24 * 60 * 60 * 1000).toISOString() },
  },
  first: 50,
});

const canceled = (await team.states()).nodes.find(s => s.type === "canceled")!;
for (const issue of stale.nodes) {
  await issue.update({ stateId: canceled.id });
  await client.createComment({
    issueId: issue.id,
    body: "Auto-closed: no activity for 90 days.",
  });
  console.log(`Closed: ${issue.identifier} (last updated ${issue.updatedAt})`);
}

Resources

When not to use it

  • When managing issues outside of the Linear platform
  • When the task does not require programmatic issue manipulation

Prerequisites

@linear/sdk installedAPI key or OAuth tokenAccess to target team

Limitations

  • Requires valid API key with appropriate scope
  • State transitions must respect team workflow configuration

How it compares

It provides a typed interface for Linear's specific workflow states and hierarchy models, simplifying complex API interactions.

Compared to similar skills

linear-core-workflow-a side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
linear-core-workflow-a (this skill)027dReviewIntermediate
fable-new-issue01moNo flagsAdvanced
linear102moNo flagsBeginner
jira116moNo flagsBeginner

Try saying

Example prompts that trigger this skill in your AI assistant.

More by jeremylongshore

View all by jeremylongshore

analyzing-logs

jeremylongshore

Analyze application logs to detect performance issues, identify error patterns, and improve stability by extracting key insights.

14123

ollama-setup

jeremylongshore

Configure auto-configure Ollama when user needs local LLM deployment, free AI alternatives, or wants to eliminate hosted API costs. Trigger phrases: "install ollama", "local AI", "free LLM", "self-hosted AI", "replace OpenAI", "no API costs". Use when appropriate context detected. Trigger with relevant phrases based on skill purpose.

1167

backtesting-trading-strategies

jeremylongshore

Backtest crypto and traditional trading strategies against historical data. Calculates performance metrics (Sharpe, Sortino, max drawdown), generates equity curves, and optimizes strategy parameters. Use when user wants to test a trading strategy, validate signals, or compare approaches. Trigger with phrases like "backtest strategy", "test trading strategy", "historical performance", "simulate trades", "optimize parameters", or "validate signals".

1071

generating-database-seed-data

jeremylongshore

Process this skill enables AI assistant to generate realistic test data and database seed scripts for development and testing environments. it uses faker libraries to create realistic data, maintains relational integrity, and allows configurable data volumes. u... Use when working with databases or data models. Trigger with phrases like 'database', 'query', or 'schema'.

1033

cursor-codebase-indexing

jeremylongshore

Execute set up and optimize Cursor codebase indexing. Triggers on "cursor index setup", "codebase indexing", "index codebase", "cursor semantic search". Use when working with cursor codebase indexing functionality. Trigger with phrases like "cursor codebase indexing", "cursor indexing", "cursor".

885

testing-mobile-apps

jeremylongshore

Execute mobile app testing on iOS and Android devices/simulators. Use when performing specialized testing. Trigger with phrases like "test mobile app", "run iOS tests", or "validate Android functionality".

810

Search skills

Search the agent skills registry