Skip to main content

Linear Integration Setup Guide

Quick setup for Linear integration with Claude Code CLI, Cursor IDE, and Git


Overview

This guide sets up Linear integration so you can:

  • From Claude Code CLI: "Create a Linear task for implementing the Fastify API"
  • From VS Code/Cursor IDE: Create tasks from code selections, update status inline, highlight TODOs
  • From Git: Auto-update Linear tasks when you commit code

Time to complete: 12 minutes

Note: Linear extension is pre-configured in the devcontainer - no manual installation needed!


Step 1: Get Linear API Key (5 minutes)

1.1 Create Linear Account (if needed)

  1. Go to linear.app
  2. Sign up with Google Workspace account (tvl@thevillalife.com)
  3. Create workspace: "The Villa Life"
  4. Create team: "FOUND" (Foundation Team)

1.2 Generate API Key

  1. Go to SettingsAPI
  2. Click "Personal API Keys"
  3. Click "Create new key"
  4. Name: TVL Platform - Development
  5. Scopes: Full Access (for now, can restrict later)
  6. Click "Create key"
  7. COPY THE KEY - you won't see it again!

Example key format: lin_api_1234567890abcdef1234567890abcdef


Step 2: Store API Key in Doppler (5 minutes)

2.1 Add to Doppler

# Login to Doppler (if not already logged in)
doppler login

# Set Linear API key in development environment
doppler secrets set LINEAR_API_KEY "lin_api_1234567890abcdef1234567890abcdef" \
--project tvl-platform \
--config dev

# Verify it was added
doppler secrets get LINEAR_API_KEY --project tvl-platform --config dev

Expected output:

LINEAR_API_KEY: lin_api_1234567890abcdef1234567890abcdef

2.2 Add to Local Environment

# Pull secrets to local .env file
doppler secrets download --project tvl-platform --config dev --no-file --format env > .env

# Verify Linear key is present
grep LINEAR_API_KEY .env

Should show:

LINEAR_API_KEY=lin_api_1234567890abcdef1234567890abcdef

Step 3: Install Linear SDK (2 minutes)

3.1 Install Package

# Already installed! Check version:
pnpm list @linear/sdk

# If not installed, run:
pnpm add @linear/sdk

Expected version: v31.0.0 or newer

3.2 Test Connection

Create a test script to verify the API key works:

# Create test script
cat > scripts/test-linear.ts << 'EOF'
import { LinearClient } from '@linear/sdk'

async function testLinearConnection() {
const linearClient = new LinearClient({
apiKey: process.env.LINEAR_API_KEY
})

try {
const viewer = await linearClient.viewer
console.log('✅ Linear connected successfully!')
console.log(`Logged in as: ${viewer.name} (${viewer.email})`)

const teams = await linearClient.teams()
console.log(`\nTeams: ${teams.nodes.map(t => t.name).join(', ')}`)

const issues = await linearClient.issues({ first: 5 })
console.log(`\nTotal issues: ${issues.nodes.length}`)
} catch (error) {
console.error('❌ Linear connection failed:', error.message)
process.exit(1)
}
}

testLinearConnection()
EOF

# Run test
pnpm tsx scripts/test-linear.ts

Expected output:

✅ Linear connected successfully!
Logged in as: John Doe (john@thevillalife.com)

Teams: FOUND

Total issues: 71

If it fails: Check that LINEAR_API_KEY is set correctly in Doppler


Step 4: Claude Code CLI Integration (3 minutes)

4.1 Create Claude Code Helper Script

Claude Code can execute bash scripts, so we create a helper for Linear operations:

# Create Linear CLI helper
cat > scripts/linear-cli.ts << 'EOF'
#!/usr/bin/env tsx
import { LinearClient } from '@linear/sdk'

const linearClient = new LinearClient({
apiKey: process.env.LINEAR_API_KEY
})

async function createIssue(title: string, description?: string, teamKey = 'FOUND') {
const teams = await linearClient.teams({ filter: { key: { eq: teamKey } } })
const team = teams.nodes[0]

if (!team) {
throw new Error(`Team ${teamKey} not found`)
}

const issue = await linearClient.createIssue({
teamId: team.id,
title,
description: description || '',
priority: 2, // Medium
})

const createdIssue = await issue.issue
if (!createdIssue) {
throw new Error('Failed to create issue')
}

console.log(`✅ Created issue: ${createdIssue.identifier} - ${title}`)
console.log(`🔗 ${createdIssue.url}`)

return createdIssue
}

async function updateIssue(issueId: string, updates: { state?: string; assignee?: string }) {
const issue = await linearClient.issue(issueId)

const updateData: any = {}

if (updates.state) {
const states = await linearClient.workflowStates()
const state = states.nodes.find(s => s.name.toLowerCase() === updates.state.toLowerCase())
if (state) {
updateData.stateId = state.id
}
}

if (updates.assignee) {
const users = await linearClient.users()
const user = users.nodes.find(u => u.email === updates.assignee || u.name === updates.assignee)
if (user) {
updateData.assigneeId = user.id
}
}

await linearClient.updateIssue(issueId, updateData)
console.log(`✅ Updated issue: ${issueId}`)
}

async function listIssues(filters?: { assignee?: string; state?: string; limit?: number }) {
const query: any = { first: filters?.limit || 20 }

if (filters?.assignee) {
const users = await linearClient.users()
const user = users.nodes.find(u => u.email === filters.assignee || u.name === filters.assignee)
if (user) {
query.filter = { assignee: { id: { eq: user.id } } }
}
}

const issues = await linearClient.issues(query)

console.log(`\n📋 Issues (${issues.nodes.length}):\n`)
for (const issue of issues.nodes) {
const assignee = await issue.assignee
console.log(`${issue.identifier}: ${issue.title}`)
console.log(` State: ${(await issue.state)?.name || 'Unknown'}`)
console.log(` Assignee: ${assignee?.name || 'Unassigned'}`)
console.log(` 🔗 ${issue.url}\n`)
}
}

// CLI interface
const command = process.argv[2]

async function main() {
switch (command) {
case 'create':
await createIssue(process.argv[3], process.argv[4])
break
case 'update':
await updateIssue(process.argv[3], { state: process.argv[4] })
break
case 'list':
await listIssues({ limit: parseInt(process.argv[3]) || 10 })
break
default:
console.log('Usage:')
console.log(' pnpm linear create "Task title" "Optional description"')
console.log(' pnpm linear update FOUND-123 "In Progress"')
console.log(' pnpm linear list [limit]')
}
}

main().catch(console.error)
EOF

# Make executable
chmod +x scripts/linear-cli.ts

# Add to package.json scripts
cat > /tmp/linear-scripts.json << 'EOF'
{
"linear": "tsx scripts/linear-cli.ts",
"linear:create": "tsx scripts/linear-cli.ts create",
"linear:update": "tsx scripts/linear-cli.ts update",
"linear:list": "tsx scripts/linear-cli.ts list"
}
EOF

# You can now use it manually or tell Claude Code to use it

4.2 Usage from Claude Code CLI

Now you can ask Claude Code:

Example 1: Create a task

You: "Create a Linear task for implementing the Fastify API endpoints"

Claude Code will run:
pnpm linear create "Implement Fastify API endpoints" "Create 8 core REST endpoints for MVP.0 (spaces, units, channel-targets, etc.)"

Example 2: List tasks

You: "Show me my Linear tasks"

Claude Code will run:
pnpm linear list 10

Example 3: Update task status

You: "Mark FOUND-123 as In Progress"

Claude Code will run:
pnpm linear update FOUND-123 "In Progress"

Step 5: Dev Container Integration (2 minutes)

5.1 Verify Extensions Installed

The Linear extension and supporting tools are pre-configured in the devcontainer!

Already installed:

  • Linear - Official Linear extension
  • TODO Tree - Highlights TODO comments (can sync to Linear)
  • Conventional Commits - Helps write proper commit messages
  • Error Lens - Better inline error display

Configured automatically:

  • Linear API key from $LINEAR_API_KEY environment variable
  • Default team: "FOUND"
  • TODO highlighting (TODO, FIXME, HACK, FOUND-XXX)
  • Conventional commit scopes

5.2 Verify Linear Extension Working

When you open VS Code/Cursor in the devcontainer, the extensions are automatically installed and configured.

1. View Linear issues:

  • Press Cmd+Shift+P"Linear: View Issues"
  • Should see Linear sidebar with your issues

2. View highlighted TODOs:

  • Press Cmd+Shift+P"Todo Tree: Show Tree"
  • See all TODO/FIXME/HACK comments in sidebar
  • TODO comments are highlighted in code (yellow background)
  • FOUND-XXX references are highlighted (purple background)

3. Create issue from code:

  • Select some code
  • Right-click → "Linear: Create Issue from Selection"
  • Enter title, press Enter
  • Issue created with code snippet in description!

4. Write commit with Conventional Commits:

  • Stage changes
  • Press Cmd+Shift+P"Conventional Commits"
  • Select type (feat, fix, docs, etc.)
  • Select scope (api, frontend, database, etc.)
  • Enter description
  • Reference Linear issue: (FOUND-123)
  • Commit created with proper format!

Step 6: Git Integration (5 minutes)

6.1 Linear Commit Message Convention

When you commit, reference Linear issues in the commit message:

Format:

<type>: <description> (FOUND-123)

<optional body>

Fixes FOUND-123

Example:

git commit -m "feat: implement Fastify API endpoints (FOUND-123)

Add 8 core REST endpoints for MVP.0:
- GET/POST/PUT/DELETE /api/v1/spaces
- GET/POST/PUT/DELETE /api/v1/units

Fixes FOUND-123"

Effect: Linear will:

  • Link commit to issue FOUND-123
  • Add commit as comment on issue
  • Auto-transition issue to "In Review" (if configured)

6.2 Setup Pre-commit Hook (Optional)

Create a pre-commit hook to validate Linear references:

# Create pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash

# Get commit message
commit_msg=$(cat .git/COMMIT_EDITMSG 2>/dev/null || echo "")

# Check for Linear reference (FOUND-XXX)
if [[ ! "$commit_msg" =~ FOUND-[0-9]+ ]]; then
echo "⚠️ Warning: No Linear issue reference found (e.g., FOUND-123)"
echo " Consider adding a Linear reference to link this commit."
echo ""
read -p "Continue anyway? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
EOF

# Make executable
chmod +x .git/hooks/pre-commit

Now when you commit:

git commit -m "feat: add API endpoint"

# Hook prompts:
⚠️ Warning: No Linear issue reference found (e.g., FOUND-123)
Consider adding a Linear reference to link this commit.

Continue anyway? (y/N)

Step 7: Test End-to-End Workflow (5 minutes)

7.1 Create Task from Claude Code

Ask Claude Code:

"Create a Linear task for setting up database migrations"

Claude Code runs:

pnpm linear create "Setup database migrations" "Create migration system using Drizzle ORM for MVP.0 schema"

Result: Issue created, e.g., FOUND-150

7.2 Work on Task in Cursor

  1. Open Cursor IDE
  2. Press Cmd+Shift+P"Linear: View Issues"
  3. Find FOUND-150
  4. Click to view details
  5. Change status: Backlog → In Progress

7.3 Commit Code with Linear Reference

# Make some changes
echo "-- Migration file" > migrations/001_initial.sql

# Commit with Linear reference
git add migrations/001_initial.sql
git commit -m "feat: add initial database migration (FOUND-150)

Create migration for core tables:
- organizations
- accounts
- users

Fixes FOUND-150"

# Push
git push

7.4 Verify in Linear

  1. Go to Linear app
  2. Find issue FOUND-150
  3. Should see:
    • Commit linked in "Activity" tab
    • Status changed to "In Review" (if auto-transition enabled)
    • Git branch linked (if created from Linear)

✅ Integration working!


Common Workflows

Workflow 1: Create Task from Claude Code

User: "Create a Linear task for implementing user authentication"

Claude Code runs:

pnpm linear create "Implement user authentication" "Setup Google OIDC with Supabase Auth (ADR-0001)"

Output:

✅ Created issue: FOUND-151 - Implement user authentication
🔗 https://linear.app/the-villa-life/issue/FOUND-151

Workflow 2: Create Task from Code Selection (Cursor)

  1. Select code:

    // TODO: Add error handling for failed API calls
    async function fetchData() {
    const response = await fetch('/api/data')
    return response.json()
    }
  2. Right-click → "Linear: Create Issue from Selection"

  3. Title: "Add error handling for API calls"

  4. Issue created with code snippet:

    FOUND-152: Add error handling for API calls

    Code reference:
    ```typescript
    // TODO: Add error handling for failed API calls
    async function fetchData() {
    const response = await fetch('/api/data')
    return response.json()
    }

    File: src/api/client.ts:42


Workflow 3: Update Task Status

From Cursor:

  1. Press Cmd+Shift+P"Linear: View My Issues"
  2. Click issue
  3. Change dropdown: In Progress → Done

From Claude Code:

"Mark FOUND-152 as Done"

Claude Code runs:

pnpm linear update FOUND-152 "Done"

Workflow 4: List Current Sprint Tasks

From Claude Code:

"Show me my Linear tasks"

Claude Code runs:

pnpm linear list 10

Output:

📋 Issues (10):

FOUND-150: Setup database migrations
State: In Progress
Assignee: John Doe
🔗 https://linear.app/the-villa-life/issue/FOUND-150

FOUND-151: Implement user authentication
State: Backlog
Assignee: Unassigned
🔗 https://linear.app/the-villa-life/issue/FOUND-151

...

Troubleshooting

Issue: "LinearClient is not a constructor"

Cause: Linear SDK not installed or wrong version

Fix:

pnpm add @linear/sdk@latest

Issue: "API key invalid"

Cause: LINEAR_API_KEY not set or incorrect

Fix:

# Check if set
echo $LINEAR_API_KEY

# If empty, pull from Doppler
doppler secrets download --project tvl-platform --config dev --no-file --format env > .env
source .env

# Or set manually
export LINEAR_API_KEY="lin_api_..."

Issue: "Team not found"

Cause: Team key mismatch

Fix:

# List teams
pnpm tsx -e "
import { LinearClient } from '@linear/sdk';
const client = new LinearClient({ apiKey: process.env.LINEAR_API_KEY });
client.teams().then(teams => console.log(teams.nodes.map(t => ({ key: t.key, name: t.name }))))
"

# Update team key in scripts to match

Issue: Cursor extension not connecting

Cause: API key not saved in Cursor settings

Fix:

  1. Press Cmd+Shift+P"Linear: Authenticate"
  2. Re-paste API key
  3. Reload Cursor window

Next Steps

Immediate (This Week)

  1. ✅ Complete this setup (15 minutes)
  2. Test creating tasks from Claude Code
  3. Test Cursor extension
  4. Make a commit with Linear reference

Short-term (Next Week)

  1. Setup GitHub Linear integration (auto-link PRs)
  2. Create pre-commit hook for commit message validation
  3. Configure auto-transitions (In Progress → In Review → Done)

Medium-term (Next 2 Weeks)

  1. Implement automated sync scripts:
    • Sync TODOs → Linear
    • Sync ADR checklists → Linear
    • Sync DEVELOPMENT_PREP_CHECKLIST.md → Linear
  2. Setup webhooks for bi-directional sync

Long-term (Month 2+)

  1. Velocity tracking and burndown charts
  2. Sprint report automation
  3. Auto-assignment based on git blame
  4. Effort vs estimate analysis

Reference Commands

Linear CLI Commands

# Create issue
pnpm linear create "Task title" "Optional description"

# Update issue status
pnpm linear update FOUND-123 "In Progress"

# List issues
pnpm linear list [limit]

# Test connection
pnpm tsx scripts/test-linear.ts

Cursor IDE Commands

  • Cmd+Shift+P"Linear: View Issues" - View all issues
  • Cmd+Shift+P"Linear: View My Issues" - View assigned issues
  • Cmd+Shift+P"Linear: Create Issue" - Create new issue
  • Right-click code → "Linear: Create Issue from Selection" - Create from code

Git Commit Format

<type>: <description> (FOUND-XXX)

<optional body>

Fixes FOUND-XXX
Closes FOUND-XXX
Resolves FOUND-XXX

Summary

Setup time: 15 minutes Tools configured: Doppler, Linear SDK, Claude Code, Cursor, Git Result: Seamless Linear integration across all development workflows

You can now:

  • ✅ Create Linear tasks by asking Claude Code
  • ✅ Create Linear tasks from code in Cursor
  • ✅ Update task status from Cursor sidebar
  • ✅ Auto-link commits to Linear issues
  • ✅ View all tasks without leaving your IDE

Next: Start using it! Create your first task and test the workflow.


Last Updated: 2025-01-26 Maintained By: Development Team Status: Ready for use