| name | linear-migration-deep-dive |
| description | Migrate from Jira, Asana, GitHub Issues, or other tools to Linear.
Use when planning a migration to Linear, executing data transfer,
or mapping workflows between tools.
Trigger with phrases like "migrate to linear", "jira to linear",
"asana to linear", "import to linear", "linear migration".
|
| allowed-tools | Read, Write, Edit, Bash(node:*), Bash(npx:*), Grep |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Linear Migration Deep Dive
Overview
Comprehensive guide for migrating from other issue trackers to Linear.
Prerequisites
- Admin access to source system
- Linear workspace with admin access
- API access to both systems
- Migration timeline and rollback plan
Migration Planning
Phase 1: Assessment
## Migration Assessment Checklist
### Data Volume
- [ ] Count total issues: ____
- [ ] Count total projects: ____
- [ ] Count total users: ____
- [ ] Attachments size: ____ GB
- [ ] Custom fields count: ____
### Workflow Analysis
- [ ] Document current statuses/states
- [ ] Map status transitions
- [ ] Identify automation rules
- [ ] List integrations in use
### User Mapping
- [ ] Export user list from source
- [ ] Map to Linear users
- [ ] Plan for unmapped users
### Timeline
- [ ] Migration window: ____
- [ ] Parallel run period: ____
- [ ] Cutover date: ____
- [ ] Rollback deadline: ____
Phase 2: Workflow Mapping
const JIRA_STATUS_MAP: Record<string, string> = {
"To Do": "Todo",
"In Progress": "In Progress",
"In Review": "In Review",
"Done": "Done",
"Closed": "Done",
"Backlog": "Backlog",
"Blocked": "In Progress",
};
const JIRA_PRIORITY_MAP: Record<string, number> = {
"Highest": 1,
"High": 2,
"Medium": 3,
"Low": 4,
"Lowest": 4,
};
const JIRA_TYPE_MAP: Record<string, { labelName: string }> = {
"Bug": { labelName: "Bug" },
"Story": { labelName: "Feature" },
"Task": { : },
: { : },
: { : },
};
: <, > = {
: ,
: ,
: ,
: ,
};
Instructions
Step 1: Export from Source System
Jira Export:
import JiraClient from "jira-client";
const jira = new JiraClient({
host: process.env.JIRA_HOST,
basic_auth: {
email: process.env.JIRA_EMAIL,
api_token: process.env.JIRA_API_TOKEN,
},
});
interface JiraIssue {
key: string;
fields: {
summary: string;
description: string;
status: { name: string };
priority: { name: string };
issuetype: { name: string };
assignee: { emailAddress: string } | null;
reporter: { emailAddress: string };
created: string;
updated: string;
parent?: { key: string };
subtasks: { key: string }[];
labels: [];
?: ;
};
}
(): <[]> {
: [] = [];
startAt = ;
maxResults = ;
() {
result = jira.(
,
{
startAt,
maxResults,
: [
,
,
,
,
,
,
,
,
,
,
,
,
,
],
}
);
issues.(...result.);
(issues. >= result.) ;
startAt += maxResults;
.();
}
fs.(
,
.(issues, , )
);
issues;
}
Asana Export:
import Asana from "asana";
const asana = Asana.Client.create().useAccessToken(process.env.ASANA_TOKEN);
export async function exportAsanaProject(projectGid: string) {
const tasks = [];
const result = await asana.tasks.getTasks({
project: projectGid,
opt_fields: [
"name",
"notes",
"assignee",
"due_on",
"completed",
"memberships.section.name",
"tags.name",
"parent.gid",
"subtasks.gid",
"created_at",
"modified_at",
],
});
for await (const task of result) {
tasks.push(task);
}
return tasks;
}
Step 2: Transform Data
import { LinearClient } from "@linear/sdk";
interface LinearIssueInput {
teamId: string;
title: string;
description?: string;
priority?: number;
stateId?: string;
assigneeId?: string;
labelIds?: string[];
estimate?: number;
parentId?: string;
}
interface TransformContext {
linearClient: LinearClient;
teamId: string;
stateMap: Map<string, string>;
userMap: Map<string, string>;
labelMap: Map<string, string>;
issueIdMap: Map<string, string>;
}
export async function transformJiraIssue(
jiraIssue: ,
:
): <> {
linearStatus = [jiraIssue...] || ;
stateId = context..(linearStatus);
priority = [jiraIssue..?.] || ;
assigneeEmail = jiraIssue..?.;
assigneeId = assigneeEmail ? context..(assigneeEmail) : ;
: [] = [];
typeLabel = [jiraIssue...];
(typeLabel && context..(typeLabel.)) {
labelIds.(context..(typeLabel.)!);
}
( label jiraIssue..) {
linearLabelId = context..(label);
(linearLabelId) {
labelIds.(linearLabelId);
}
}
description = (jiraIssue..);
{
: context.,
: ,
description,
priority,
stateId,
assigneeId,
labelIds,
: jiraIssue..,
};
}
(): {
(!jiraMarkup) ;
md = jiraMarkup;
md = md.(, );
md = md.(, );
md = md.(, );
md = md.(, );
md = md.(, );
md = md.(, );
md = md.(, );
md = md.(, );
md = md.(, );
md = md.(, );
md = md.(, );
md;
}
Step 3: Import to Linear
import { LinearClient } from "@linear/sdk";
interface ImportStats {
total: number;
created: number;
skipped: number;
errors: { sourceId: string; error: string }[];
}
export async function importToLinear(
issues: JiraIssue[],
context: TransformContext
): Promise<ImportStats> {
const stats: ImportStats = {
total: issues.length,
created: 0,
skipped: 0,
errors: [],
};
const sorted = sortByHierarchy(issues);
for (const jiraIssue of sorted) {
try {
if (context.issueIdMap.has(jiraIssue.key)) {
stats.skipped++;
;
}
input = (jiraIssue, context);
(jiraIssue..) {
input. = context..(jiraIssue...);
}
result = context..(input);
(result.) {
issue = result.;
context..(jiraIssue., issue!.);
stats.++;
();
} {
();
}
.();
} (error) {
stats..({
: jiraIssue.,
: error ? error. : ,
});
.(, error);
}
}
stats;
}
(): [] {
byKey = (issues.( [i., i]));
: [] = [];
processed = <>();
(): {
(processed.(issue.)) ;
(issue..) {
parent = byKey.(issue...);
(parent) (parent);
}
sorted.(issue);
processed.(issue.);
}
( issue issues) {
(issue);
}
sorted;
}
Step 4: Validation & Verification
export async function validateMigration(
sourceIssues: JiraIssue[],
context: TransformContext
): Promise<{ valid: boolean; issues: string[] }> {
const issues: string[] = [];
for (const source of sourceIssues) {
if (!context.issueIdMap.has(source.key)) {
issues.push(`Missing: ${source.key}`);
}
}
const sampleSize = Math.min(50, sourceIssues.length);
const sample = sourceIssues.slice(0, sampleSize);
for (const source of sample) {
const linearId = context.issueIdMap.get(source.key);
if (!linearId) continue;
try {
const linearIssue = await context..(linearId);
(!linearIssue..(source.)) {
issues.();
}
expectedPriority = [source..?.] || ;
(linearIssue. !== expectedPriority) {
issues.();
}
} (error) {
issues.();
}
}
{
: issues. === ,
issues,
};
}
Step 5: Post-Migration
export async function createMigrationReport(
stats: ImportStats,
context: TransformContext
): Promise<string> {
const report = `
# Migration Report
**Date:** ${new Date().toISOString()}
**Source:** Jira
**Target:** Linear
## Statistics
- Total issues: ${stats.total}
- Successfully imported: ${stats.created}
- Skipped (duplicates): ${stats.skipped}
- Errors: ${stats.errors.length}
## ID Mapping
${Array.from(context.issueIdMap.entries())
.map(([source, linear]) => `- ${source} -> ${linear}`)
.join("\n")}
## Errors
${stats.errors.map(e => `- ${e.sourceId}: ${e.error}`).join("\n") || "None"}
## Next Steps
1. Verify critical issues manually
2. Update integrations to use Linear
3. Archive source project after parallel run
4. Train team on Linear workflows
`;
await fs.writeFile("migration-report.md", report);
return report;
}
Migration Checklist
## Pre-Migration
[ ] Backup source system data
[ ] Create Linear workspace and teams
[ ] Set up workflow states and labels
[ ] Map users between systems
[ ] Create API credentials
## Migration
[ ] Export data from source
[ ] Transform to Linear format
[ ] Import in batches
[ ] Validate sample issues
[ ] Import attachments (if needed)
## Post-Migration
[ ] Run full validation
[ ] Set up redirects (if applicable)
[ ] Update integrations
[ ] Train team
[ ] Run parallel for 1-2 weeks
[ ] Archive source after cutover
Error Handling
| Error | Cause | Solution |
|---|
User not found | Unmapped user | Add to user mapping |
Rate limited | Too fast import | Add delays between requests |
State not found | Unmapped status | Update state mapping |
Parent not found | Import order wrong | Sort by hierarchy |
Resources
Conclusion
You have completed the Linear Flagship Skill Pack. You now have comprehensive knowledge of Linear integrations from basic setup through enterprise deployment.