| name | linear-local-dev-loop |
| description | Set up local Linear development environment and testing workflow. Use when configuring local development, testing integrations, or setting up a development workflow with Linear. Trigger with phrases like "linear local development", "linear dev setup", "test linear locally", "linear development environment". |
| allowed-tools | Read, Write, Edit, Bash(npm:*), Bash(npx:*), Grep |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Linear Local Dev Loop
Overview
Set up an efficient local development workflow for Linear integrations.
Prerequisites
- Node.js 18+ with TypeScript
- Linear SDK installed
- Separate Linear workspace for development (recommended)
- ngrok or similar for webhook testing
Instructions
Step 1: Project Setup
# Initialize project
mkdir linear-integration && cd linear-integration
npm init -y
npm install @linear/sdk typescript ts-node dotenv
npm install -D @types/node vitest
# Create tsconfig.json
npx tsc --init --target ES2022 --module NodeNext --moduleResolution NodeNext
Step 2: Environment Configuration
# Create .env for local development
cat > .env << 'EOF'
LINEAR_API_KEY=lin_api_dev_xxxxxxxxxxxx
LINEAR_WEBHOOK_SECRET=your_webhook_secret
NODE_ENV=development
EOF
# Create .env.example (commit this)
cat > .env.example << 'EOF'
LINEAR_API_KEY=lin_api_xxxxxxxxxxxx
LINEAR_WEBHOOK_SECRET=
NODE_ENV=development
EOF
# Add to .gitignore
echo ".env" >> .gitignore
echo ".env.local" >> .gitignore
Step 3: Create Development Client
// src/client.ts
import { LinearClient } from "@linear/sdk";
import dotenv from "dotenv";
dotenv.config();
export const linearClient = new LinearClient({
apiKey: process.env.LINEAR_API_KEY!,
});
export async function verifyClient(): Promise<boolean> {
try {
const viewer = await linearClient.viewer;
console.log(`[Linear] Connected as ${viewer.name}`);
return true;
} catch (error) {
console.error("[Linear] Connection failed:", error);
return false;
}
}
Step 4: Create Test Utilities
// src/test-utils.ts
import { linearClient } from "./client";
export async function createTestIssue(teamKey: string) {
const teams = await linearClient.teams();
const team = teams.nodes.find(t => t.key === teamKey);
if (!team) throw new Error(`Team ${teamKey} not found`);
const result = await linearClient.createIssue({
teamId: team.id,
title: `[TEST] ${new Date().toISOString()}`,
description: "Automated test issue - safe to delete",
});
return result.issue;
}
export async function cleanupTestIssues(teamKey: string) {
const issues = await linearClient.issues({
filter: {
team: { key: { eq: teamKey } },
title: { startsWith: "[TEST]" },
},
});
for (const issue of issues.nodes) {
await issue.delete();
}
console.log(`Cleaned up ${issues.nodes.length} test issues`);
}
Step 5: Set Up Watch Mode
// package.json scripts
{
"scripts": {
"dev": "ts-node --watch src/index.ts",
"test": "vitest",
"test:watch": "vitest --watch",
"verify": "ts-node src/verify.ts"
}
}
Output
- Local development environment ready
- Environment variables configured
- Test utilities for creating/cleaning test data
- Watch mode for rapid iteration
Error Handling
| Error | Cause | Solution |
|---|---|---|
API key not set |
Missing .env file | Copy .env.example to .env |
Cannot find module |
TypeScript config issue | Check tsconfig.json paths |
Connection refused |
Network issue | Verify internet connectivity |
Webhook not received |
Tunnel not running | Start ngrok tunnel |
Examples
Webhook Development with ngrok
# Terminal 1: Start your webhook server
npm run dev
# Terminal 2: Start ngrok tunnel
ngrok http 3000
# Copy the https URL and add to Linear webhook settings
Integration Test Example
// tests/integration.test.ts
import { describe, it, expect, afterAll } from "vitest";
import { createTestIssue, cleanupTestIssues } from "../src/test-utils";
describe("Linear Integration", () => {
const teamKey = "ENG"; // Your test team
afterAll(async () => {
await cleanupTestIssues(teamKey);
});
it("should create and fetch an issue", async () => {
const issue = await createTestIssue(teamKey);
expect(issue).toBeDefined();
expect(issue?.title).toContain("[TEST]");
});
});
Resources
Next Steps
After setting up local dev, proceed to linear-sdk-patterns for best practices.