Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Upgrade Linear SDK versions and handle breaking changes safely. Use when updating to a new SDK version, handling deprecations, or migrating between API versions. Trigger: "upgrade linear SDK", "linear SDK migration", "update linear", "linear breaking changes", "linear deprecation".
.claude/skills/jeremylongshore-linear-upgrade-migration/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-15 | ✗→✓ | ▲ Improved | 31% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 24% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 71% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 38% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 128% | 0% |
Safely upgrade @linear/sdk versions with zero downtime. The SDK is auto-generated from Linear's GraphQL schema -- major versions can rename fields, change return types, add required parameters, or remove deprecated methods. This skill covers version checking, upgrade procedure, compatibility layers, and rollback.
bashset -euo pipefail # Current installed version npm list @linear/sdk 2>/dev/null || echo "Not installed" # Latest available npm view @linear/sdk version # All recent versions npm view @linear/sdk versions --json | jq '.[-10:]'
bashset -euo pipefail # View SDK changelog on GitHub npm view @linear/sdk repository.url # Then check: https://github.com/linear/linear/blob/master/packages/sdk/CHANGELOG.md # Also review Linear's API changelog: # https://linear.app/changelog (filter for API/developer updates)
Common breaking changes between major versions:
issue.state property vs lazy relationbashset -euo pipefail git checkout -b upgrade/linear-sdk-$(npm view @linear/sdk version) npm install @linear/sdk@latest # Immediately check for type errors npx tsc --noEmit 2>&1 | head -50
typescript// src/linear-compat.ts // Bridge pattern for gradual migration across SDK versions import { LinearClient } from "@linear/sdk"; /** * Normalize issue state access across SDK versions. * SDK v2: issue.state was a direct string property * SDK v3+: issue.state is a lazy-loaded WorkflowState relation */ export async function getIssueStateName(issue: any): Promise<string> { if (typeof issue.state === "string") return issue.state; const state = await issue.state; return state?.name ?? "unknown"; } export async function getIssueStateType(issue: any): Promise<string> { if (typeof issue.stateType === "string") return issue.stateType; const state = await issue.state; return state?.type ?? "unknown"; } /** * Normalize team access — some versions changed from direct to paginated. */ export async function getTeamByKey(client: LinearClient, key: string) { const teams = await client.teams({ filter: { key: { eq: key } } }); return teams.nodes[0]; } /** * Normalize issue creation return — handle both success shapes. */ export async function createIssue( client: LinearClient, input: { teamId: string; title: string; [key: string]: any } ) { const result = await client.createIssue(input); // Some versions return { success, issue } others return directly if ("success" in result) { return { success: result.success, issue: await result.issue }; } return { success: true, issue: result }; }
bashset -euo pipefail # Type-check npx tsc --noEmit # Run unit tests npm test # Run integration tests (if API key available) npm run test:integration 2>&1 || true # Lint npm run lint 2>&1 || true
Common fixes:
typescript// Fix: Property 'x' does not exist // Old: issue.statusName // New: (await issue.state)?.name // Fix: Type 'X' is not assignable to type 'Y' // Old: const states: string[] = team.states // New: const states = await team.states() // Fix: Expected 2 arguments but got 1 // Check if mutation added required parameter // Old: client.updateIssue(id, { title: "new" }) // New: client.updateIssue(id, { title: "new" }) // usually same
bashset -euo pipefail # Deploy to staging npm run build npm run deploy:staging # Run integration tests against staging LINEAR_API_KEY=$STAGING_LINEAR_API_KEY npm run test:integration # Check health endpoint curl -s https://staging.yourapp.com/health/linear | jq .
bashset -euo pipefail # Commit upgrade git add package.json package-lock.json src/linear-compat.ts git commit -m "chore: upgrade @linear/sdk to $(npm list @linear/sdk --json | jq -r '.dependencies["@linear/sdk"].version')" git push origin upgrade/linear-sdk-* # If something breaks in production: git revert HEAD npm install # Restores previous version npm run deploy
| SDK Range | Node.js | TypeScript | Notable Changes | |-----------|---------|------------|-----------------| | 1.x | 14+ | 4.5+ | Initial release, callback-style | | 2.x-16.x | 16+ | 4.7+ | ESM support, typed models | | 17.x-28.x | 18+ | 5.0+ | Strict types, new entity models | | Latest | 18+ | 5.0+ | Refresh tokens, initiatives, agents |
| Error | Cause | Solution | |-------|-------|----------| | Property does not exist | Renamed field | Check changelog, update field name | | Type is not assignable | Changed return type | Update type annotations | | Module not found | ESM/CJS mismatch | Update import syntax or tsconfig | | Cannot find name | Removed export | Replace with new API equivalent | | Tests pass, prod fails | SDK version mismatch in lockfile | Delete node_modules, npm ci |
typescript// scripts/audit-linear-usage.ts // Run before upgrading to find all SDK touchpoints import { readFileSync } from "fs"; import { globSync } from "glob"; const files = globSync("src/**/*.ts"); const patterns = [ /LinearClient/g, /client\.issues/g, /client\.createIssue/g, /client\.updateIssue/g, /\.state\b/g, /\.assignee\b/g, /rawRequest/g, ]; for (const file of files) { const content = readFileSync(file, "utf-8"); for (const pattern of patterns) { const matches = content.match(pattern); if (matches) { console.log(`${file}: ${pattern.source} (${matches.length} occurrences)`); } } }
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 24,372 | 21,707 | -11% | 1 | 1 | 0% | 3,465 | 4,918 | +42% | 0 | 0 | — |
case-02 | fail→fail | 22,842 | 22,798 | -0% | 1 | 1 | 0% | 2,960 | 5,043 | +70% | 0 | 0 | — |
case-03 | fail→fail | 20,350 | 21,198 | +4% | 1 | 1 | 0% | 2,521 | 4,842 | +92% | 0 | 0 | — |
case-04 | pass→pass | 18,226 | 13,578 | -26% | 1 | 1 | 0% | 2,466 | 3,404 | +38% | 0 | 0 | — |
case-05 | pass→pass | 14,710 | 23,555 | +60% | 1 | 1 | 0% | 1,918 | 4,372 | +128% | 0 | 0 | — |
case-06 | pass→pass | 25,210 | 26,192 | +4% | 1 | 1 | 0% | 3,583 | 6,040 | +69% | 0 | 0 | — |
case-07 | fail→fail | 23,337 | 14,816 | -37% | 1 | 1 | 0% | 2,774 | 4,029 | +45% | 0 | 0 | — |
case-08 | pass→pass | 20,427 | 15,261 | -25% | 1 | 1 | 0% | 2,635 | 3,608 | +37% | 0 | 0 | — |
case-09 | fail→fail | 20,129 | 17,639 | -12% | 1 | 1 | 0% | 3,077 | 4,109 | +34% | 0 | 0 | — |
case-10 | fail→fail | 20,802 | 13,141 | -37% | 1 | 1 | 0% | 2,791 | 4,465 | +60% | 0 | 0 | — |
case-11 | fail→fail | 11,196 | 2,604 | -77% | 1 | 1 | 0% | 990 | 2,249 | +127% | 0 | 0 | — |
case-12 | fail→fail | 13,542 | 4,654 | -66% | 1 | 1 | 0% | 2,572 | 2,591 | +1% | 0 | 0 | — |
case-13 | fail→fail | 8,529 | 8,266 | -3% | 1 | 1 | 0% | 1,532 | 2,907 | +90% | 0 | 0 | — |
case-14 | pass→pass | 8,760 | 4,275 | -51% | 1 | 1 | 0% | 1,721 | 2,466 | +43% | 0 | 0 | — |
case-15 | fail→pass | 15,100 | 9,587 | -37% | 1 | 1 | 0% | 2,032 | 2,652 | +31% | 0 | 0 | — |
case-16 | pass→pass | 18,300 | 8,730 | -52% | 1 | 1 | 0% | 1,941 | 2,277 | +17% | 0 | 0 | — |
case-17 | fail→pass | 17,383 | 11,765 | -32% | 1 | 1 | 0% | 2,436 | 3,022 | +24% | 0 | 0 | — |
case-18 | pass→pass | 21,155 | 18,084 | -15% | 1 | 1 | 0% | 2,787 | 4,180 | +50% | 0 | 0 | — |
case-19 | fail→pass | 9,941 | 12,753 | +28% | 1 | 1 | 0% | 1,737 | 2,963 | +71% | 0 | 0 | — |
case-20 | pass→pass | 12,939 | 4,479 | -65% | 1 | 1 | 0% | 2,354 | 2,675 | +14% | 0 | 0 | — |
case-21 | pass→pass | 16,285 | 16,909 | +4% | 1 | 1 | 0% | 2,029 | 3,831 | +89% | 0 | 0 | — |
case-22 | pass→pass | 15,400 | 11,786 | -23% | 1 | 1 | 0% | 2,567 | 3,954 | +54% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +14 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.