Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Deploy Linear-integrated applications and track deployments. Use when deploying to production, linking deploys to issues, or setting up deployment tracking with Vercel/Railway/Cloud Run. Trigger: "deploy linear integration", "linear deployment", "linear vercel", "track linear deployments", "linear deploy tracking".
.claude/skills/jeremylongshore-linear-deploy-integration/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 63% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 11% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 73% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 40% | 0% |
| case-11 | ✗→✓ | ▲ Improved | -18% | 0% |
Deploy Linear-integrated applications with automatic deployment tracking. Linear's GitHub integration links PRs to issues using magic words (Fixes, Closes, Resolves) and auto-detects Vercel preview links. This skill adds custom deployment comments, state transitions, and rollback tracking.
yaml# .github/workflows/deploy.yml name: Deploy and Track on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # Full history for commit scanning - name: Deploy id: deploy run: | # Replace with your deploy command DEPLOY_URL=$(npx vercel deploy --prod --token=${{ secrets.VERCEL_TOKEN }} 2>&1 | tail -1) echo "url=$DEPLOY_URL" >> $GITHUB_OUTPUT - name: Track deployment in Linear if: success() env: LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }} run: | npx tsx scripts/track-deployment.ts \ --env production \ --url "${{ steps.deploy.outputs.url }}" \ --sha "${{ github.sha }}" \ --before "${{ github.event.before }}" - name: Create failure issue if: failure() env: LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }} run: | curl -s -X POST https://api.linear.app/graphql \ -H "Authorization: $LINEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "mutation($i: IssueCreateInput!) { issueCreate(input: $i) { success } }", "variables": { "i": { "teamId": "${{ vars.LINEAR_TEAM_ID }}", "title": "[Deploy] Failed: ${{ github.sha }}", "priority": 1 } } }'
typescript// scripts/track-deployment.ts import { LinearClient } from "@linear/sdk"; import { execSync } from "child_process"; import { parseArgs } from "util"; const { values } = parseArgs({ options: { env: { type: "string" }, url: { type: "string" }, sha: { type: "string" }, before: { type: "string" }, }, }); async function trackDeployment() { const client = new LinearClient({ apiKey: process.env.LINEAR_API_KEY! }); // Extract Linear issue IDs from commit messages since last deploy const log = execSync( `git log --oneline ${values.before}..${values.sha} 2>/dev/null || echo ""` ).toString(); const issueIds = [...new Set(log.match(/[A-Z]+-\d+/g) ?? [])]; console.log(`Found ${issueIds.length} Linear issues in commits: ${issueIds.join(", ")}`); for (const identifier of issueIds) { const results = await client.issueSearch(identifier); const issue = results.nodes.find(i => i.identifier === identifier); if (!issue) continue; // Add deployment comment await client.createComment({ issueId: issue.id, body: `Deployed to **${values.env}**: ${values.url}\n\nCommit: \`${values.sha?.substring(0, 7)}\``, }); // Auto-transition based on environment const team = await issue.team; const states = await team!.states(); if (values.env === "staging") { const reviewState = states.nodes.find(s => s.name.toLowerCase().includes("review") ); if (reviewState) await issue.update({ stateId: reviewState.id }); } else if (values.env === "production") { const doneState = states.nodes.find(s => s.type === "completed"); if (doneState) await issue.update({ stateId: doneState.id }); } console.log(`Updated ${identifier} — deployed to ${values.env}`); } } trackDeployment().catch(console.error);
typescriptasync function trackRollback( client: LinearClient, issueIdentifier: string, reason: string ) { const results = await client.issueSearch(issueIdentifier); const issue = results.nodes[0]; if (!issue) return; // Add rollback comment await client.createComment({ issueId: issue.id, body: `**Rolled back from production**\n\nReason: ${reason}\n\nIssue moved back to In Progress.`, }); // Move back to In Progress with elevated priority const team = await issue.team; const states = await team!.states(); const inProgress = states.nodes.find(s => s.name.toLowerCase() === "in progress" ); if (inProgress) { await issue.update({ stateId: inProgress.id, priority: 1 }); } }
markdown<!-- .github/PULL_REQUEST_TEMPLATE.md --> ## Linear Issues <!-- Linear auto-links when you use magic words --> Fixes ENG-XXX ## Deployment Notes - [ ] Requires database migration - [ ] Requires environment variable changes - [ ] Requires Linear webhook reconfiguration - [ ] Requires secret rotation
typescript// Query recently deployed issues async function getDeploymentSummary(client: LinearClient, days = 14) { const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString(); const completed = await client.issues({ filter: { state: { type: { eq: "completed" } }, completedAt: { gte: since }, }, first: 100, orderBy: "completedAt", }); console.log(`${completed.nodes.length} issues completed in last ${days} days:`); for (const issue of completed.nodes) { const assignee = await issue.assignee; console.log(` ${issue.identifier}: ${issue.title} (${assignee?.name ?? "unassigned"})`); } return completed.nodes; }
| Error | Cause | Solution | |-------|-------|----------| | LINEAR_API_KEY not set | Missing CI secret | Add to GitHub repo Settings > Secrets | | Issue not found | Wrong workspace or deleted | Verify team key matches workspace | | Preview links not appearing | GitHub integration off | Enable in Linear Settings > Integrations > GitHub | | Deploy comment missing | Issue ID not in commits | Follow branch naming: feature/ENG-123-desc |
yamlstrategy: matrix: include: - env: staging trigger: pull_request - env: production trigger: push
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 21,994 | 15,234 | -31% | 1 | 1 | 0% | 2,506 | 4,080 | +63% | 0 | 0 | — |
case-02 | fail→fail | 23,752 | 20,051 | -16% | 1 | 1 | 0% | 3,871 | 4,993 | +29% | 0 | 0 | — |
case-03 | fail→fail | 17,953 | 18,429 | +3% | 1 | 1 | 0% | 2,488 | 4,609 | +85% | 0 | 0 | — |
case-04 | fail→fail | 18,302 | 16,216 | -11% | 1 | 1 | 0% | 2,144 | 3,649 | +70% | 0 | 0 | — |
case-05 | fail→pass | 23,943 | 16,539 | -31% | 1 | 1 | 0% | 3,948 | 4,386 | +11% | 0 | 0 | — |
case-06 | fail→fail | 19,674 | 16,237 | -17% | 1 | 1 | 0% | 2,745 | 4,003 | +46% | 0 | 0 | — |
case-07 | pass→pass | 11,554 | 11,979 | +4% | 1 | 1 | 0% | 1,154 | 3,126 | +171% | 0 | 0 | — |
case-08 | fail→fail | 36,460 | 13,483 | -63% | 1 | 1 | 0% | 2,086 | 3,190 | +53% | 0 | 0 | — |
case-09 | fail→pass | 19,331 | 14,642 | -24% | 1 | 1 | 0% | 2,761 | 4,763 | +73% | 0 | 0 | — |
case-18 | fail→pass | 9,070 | 2,169 | -76% | 1 | 1 | 0% | 1,567 | 2,186 | +40% | 0 | 0 | — |
case-10 | pass→pass | 17,372 | 10,506 | -40% | 1 | 1 | 0% | 2,126 | 2,828 | +33% | 0 | 0 | — |
case-11 | fail→pass | 22,830 | 8,850 | -61% | 1 | 1 | 0% | 3,156 | 2,600 | -18% | 0 | 0 | — |
case-12 | fail→pass | 7,953 | 15,017 | +89% | 1 | 1 | 0% | 1,431 | 3,210 | +124% | 0 | 0 | — |
case-13 | pass→pass | 12,525 | 9,846 | -21% | 1 | 1 | 0% | 1,757 | 2,414 | +37% | 0 | 0 | — |
case-14 | fail→pass | 18,216 | 7,065 | -61% | 1 | 1 | 0% | 2,248 | 2,222 | -1% | 0 | 0 | — |
case-15 | fail→pass | 25,767 | 3,404 | -87% | 1 | 1 | 0% | 2,847 | 2,467 | -13% | 0 | 0 | — |
case-16 | pass→pass | 17,545 | 17,623 | +0% | 1 | 1 | 0% | 2,037 | 3,660 | +80% | 0 | 0 | — |
case-17 | fail→fail | 13,378 | 11,929 | -11% | 1 | 1 | 0% | 1,835 | 3,003 | +64% | 0 | 0 | — |
case-19 | fail→pass | 11,453 | 2,219 | -81% | 1 | 1 | 0% | 1,024 | 2,289 | +124% | 0 | 0 | — |
case-20 | pass→pass | 12,961 | 11,033 | -15% | 1 | 1 | 0% | 1,986 | 2,746 | +38% | 0 | 0 | — |
case-21 | pass→pass | 6,468 | 10,301 | +59% | 1 | 1 | 0% | 1,243 | 2,842 | +129% | 0 | 0 | — |
case-22 | pass→pass | 13,519 | 11,298 | -16% | 1 | 1 | 0% | 2,128 | 4,180 | +96% | 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 +41 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.