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)
- Go to linear.app
- Sign up with Google Workspace account (tvl@thevillalife.com)
- Create workspace: "The Villa Life"
- Create team: "FOUND" (Foundation Team)
1.2 Generate API Key
- Go to Settings → API
- Click "Personal API Keys"
- Click "Create new key"
- Name: TVL Platform - Development
- Scopes: Full Access (for now, can restrict later)
- Click "Create key"
- 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_KEYenvironment 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
- Open Cursor IDE
- Press Cmd+Shift+P→ "Linear: View Issues"
- Find FOUND-150
- Click to view details
- 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
- Go to Linear app
- Find issue FOUND-150
- 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)
- 
Select code: // TODO: Add error handling for failed API calls
 async function fetchData() {
 const response = await fetch('/api/data')
 return response.json()
 }
- 
Right-click → "Linear: Create Issue from Selection" 
- 
Title: "Add error handling for API calls" 
- 
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:
- Press Cmd+Shift+P→ "Linear: View My Issues"
- Click issue
- 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:
- Press Cmd+Shift+P→ "Linear: Authenticate"
- Re-paste API key
- Reload Cursor window
Next Steps
Immediate (This Week)
- ✅ Complete this setup (15 minutes)
- Test creating tasks from Claude Code
- Test Cursor extension
- Make a commit with Linear reference
Short-term (Next Week)
- Setup GitHub Linear integration (auto-link PRs)
- Create pre-commit hook for commit message validation
- Configure auto-transitions (In Progress → In Review → Done)
Medium-term (Next 2 Weeks)
- Implement automated sync scripts:
- Sync TODOs → Linear
- Sync ADR checklists → Linear
- Sync DEVELOPMENT_PREP_CHECKLIST.md → Linear
 
- Setup webhooks for bi-directional sync
Long-term (Month 2+)
- Velocity tracking and burndown charts
- Sprint report automation
- Auto-assignment based on git blame
- 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