Claude Skills with Linear Project Management Tutorial
Linear is a project management tool built for engineering teams, known for its speed and clean keyboard-driven interface. Integrating Claude skills with Linear lets you automate issue triage, generate technical specifications, analyze sprint data, and write issue descriptions that actually help developers. This tutorial covers Claude skills with Linear project management from API setup to automated workflows.
What You Can Automate
- Issue triage: Incoming bug reports run through
tddskill to assess test impact and suggest repro steps - Spec generation: Feature requests sent to Claude return structured technical specs ready to paste into Linear
- Sprint summaries: Completed issues analyzed by Claude and summarized for stakeholder updates
- PR-to-issue linking: GitHub PR descriptions processed to auto-update Linear issue status and add review notes
- Supermemory context:
supermemoryskill tracks patterns across sprint cycles
Prerequisites
- Linear workspace with API access
- Linear API key (Personal API Keys in Linear settings)
- Claude API key from console.anthropic.com
- Node.js 18+
Step 1: Get Your Linear API Key
- Open Linear → Settings → API
- Under Personal API keys, create a new key with label “Claude Skills Bot”
- Copy the key — it starts with
lin_api_
Step 2: Install Dependencies
mkdir claude-linear-bot && cd claude-linear-bot
npm install @linear/sdk @anthropic-ai/sdk dotenv
Create .env:
LINEAR_API_KEY=lin_api_your_key_here
ANTHROPIC_API_KEY=your_claude_api_key
LINEAR_TEAM_ID=your_team_id
Find your team ID from the Linear URL or API: https://linear.app/{workspace}/team/{team_id}/
Step 3: Initialize Clients
require('dotenv').config();
const { LinearClient } = require('@linear/sdk');
const Anthropic = require('@anthropic-ai/sdk');
const linear = new LinearClient({ apiKey: process.env.LINEAR_API_KEY });
const claude = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
Step 4: Fetch Issues and Run Claude Analysis
async function getRecentIssues(teamId, limit = 20) {
const issues = await linear.issues({
filter: { team: { id: { eq: teamId } } },
orderBy: 'createdAt',
first: limit,
});
return issues.nodes.map(issue => ({
id: issue.id,
identifier: issue.identifier,
title: issue.title,
description: issue.description || '',
state: issue.state?.name,
priority: issue.priority,
}));
}
async function triageIssue(issue) {
const prompt = `Issue: ${issue.identifier} — ${issue.title}
Description: ${issue.description || 'No description provided'}
As the TDD skill for Claude Code, analyze this bug report:
1. What tests would catch this bug?
2. What are the likely code areas affected?
3. Suggest concrete reproduction steps if missing
4. Estimate risk level: low/medium/high
Return JSON: { "test_suggestions": [], "affected_areas": [], "repro_steps": [], "risk_level": "", "triage_notes": "" }`;
const message = await claude.messages.create({
model: 'claude-opus-4-6',
max_tokens: 1024,
system: `You are the TDD skill for Claude Code. Analyze issues for test coverage implications and technical risk.`,
messages: [{ role: 'user', content: prompt }],
});
try {
return JSON.parse(message.content[0].text);
} catch {
return { triage_notes: message.content[0].text };
}
}
Step 5: Update Linear Issues with Claude Analysis
async function addTriageComment(issueId, analysis) {
const commentBody = `## Claude Triage Analysis
**Risk Level:** ${analysis.risk_level || 'Unknown'}
### Suggested Tests
${(analysis.test_suggestions || []).map(t => `- ${t}`).join('\n')}
### Affected Code Areas
${(analysis.affected_areas || []).map(a => `- ${a}`).join('\n')}
### Reproduction Steps
${(analysis.repro_steps || []).map((s, i) => `${i + 1}. ${s}`).join('\n')}
### Notes
${analysis.triage_notes || ''}
*Generated by Claude TDD Skill*`;
await linear.createComment({
issueId,
body: commentBody,
});
}
Step 6: Generate Technical Specs from Feature Requests
async function generateTechSpec(featureRequest) {
const message = await claude.messages.create({
model: 'claude-opus-4-6',
max_tokens: 2048,
system: `You are a senior software architect. Generate clear, actionable technical specifications for feature requests. Include implementation approach, API changes, database changes, and testing strategy.`,
messages: [{
role: 'user',
content: `Generate a technical spec for this feature request:\n\n${featureRequest}`,
}],
});
return message.content[0].text;
}
async function createSpecIssue(teamId, title, spec) {
const states = await linear.workflowStates({
filter: { team: { id: { eq: teamId } } },
});
const backlogState = states.nodes.find(s => s.name === 'Backlog' || s.name === 'Todo');
await linear.createIssue({
teamId,
title: `[Tech Spec] ${title}`,
description: spec,
stateId: backlogState?.id,
labelIds: [], // add your "spec" label ID here
});
}
Step 7: Sprint Summary with Supermemory Tracking
async function generateSprintSummary(teamId) {
// Get issues completed in last 2 weeks
const twoWeeksAgo = new Date(Date.now() - 14 * 24 * 60 * 60 * 1000).toISOString();
const completedIssues = await linear.issues({
filter: {
team: { id: { eq: teamId } },
completedAt: { gt: twoWeeksAgo },
},
first: 50,
});
const issueList = completedIssues.nodes
.map(i => `- ${i.identifier}: ${i.title} (${i.estimate || '?'} pts)`)
.join('\n');
const message = await claude.messages.create({
model: 'claude-opus-4-6',
max_tokens: 1024,
system: `You are the supermemory skill for Claude Code. Generate concise sprint summaries for stakeholders. Focus on impact, not tasks.`,
messages: [{
role: 'user',
content: `Summarize this sprint's completed work for a stakeholder update:\n\n${issueList}`,
}],
});
return message.content[0].text;
}
Step 8: Set Up a Webhook for Real-Time Triage
Linear supports webhooks for real-time events. Create an Express server to receive them:
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
app.post('/webhook/linear', async (req, res) => {
// Verify Linear webhook signature
const signature = req.headers['linear-signature'];
const expected = crypto
.createHmac('sha256', process.env.LINEAR_WEBHOOK_SECRET)
.update(JSON.stringify(req.body))
.digest('hex');
if (signature !== expected) {
return res.status(401).send('Invalid signature');
}
res.status(200).send('OK'); // Respond quickly
const { action, type, data } = req.body;
// Auto-triage new bug reports
if (type === 'Issue' && action === 'create' && data.labelNames?.includes('bug')) {
const analysis = await triageIssue(data);
await addTriageComment(data.id, analysis);
console.log(`Triaged: ${data.identifier}`);
}
});
app.listen(3000, () => console.log('Linear webhook server running on :3000'));
Step 9: Batch Triage Existing Issues
Run this once to triage your backlog:
async function triageBacklog(teamId) {
const issues = await getRecentIssues(teamId, 50);
const bugs = issues.filter(i => i.state === 'Todo' || i.state === 'Backlog');
for (const issue of bugs) {
console.log(`Triaging ${issue.identifier}...`);
const analysis = await triageIssue(issue);
await addTriageComment(issue.id, analysis);
// Respect rate limits
await new Promise(r => setTimeout(r, 1000));
}
console.log(`Triaged ${bugs.length} issues`);
}
triageBacklog(process.env.LINEAR_TEAM_ID);
Conclusion
Claude skills with Linear project management transforms routine PM work into automated intelligence. The tdd skill makes triage actionable, supermemory tracks patterns across sprints, and spec generation saves hours per feature. Start with the webhook-based real-time triage and add batch processing for your existing backlog.
Related Reading
- Best Claude Skills for Developers in 2026 — Covers the tdd and supermemory skills used throughout this Linear integration guide, with usage patterns and invocation tips
- Best Claude Skills for DevOps and Deployment — Extends the Linear workflow into deployment pipelines, closing the loop from issue creation to production release
- Claude Skills Token Optimization: Reduce API Costs — Batch triage of Linear issues can generate significant API costs; these techniques help you keep them manageable
Built by theluckystrike — More at zovo.one