1import { Issue, IssueLabel, LinearClient, User, WorkflowState } from '@linear/sdk'; 2import { 3 IssueCreateInput, 4 IssueFilter, 5 IssueLabelFilter, 6 UserFilter, 7} from '@linear/sdk/dist/_generated_documents'; 8 9const linearClient = new LinearClient({ 10 apiKey: process.env.LINEAR_API_KEY ?? '<LINEAR-API-KEY>', 11}); 12 13export const ENG_TEAM_ID = 'e678ab8b-874f-4ee2-bf4b-6c0b60ac2743'; 14 15/** 16 * Creates a new issue. 17 * Defaults teamId to the Engineering team. 18 */ 19export async function createIssueAsync( 20 issueInput: Omit<IssueCreateInput, 'teamId'> & Partial<IssueCreateInput> 21) { 22 await linearClient.createIssue({ 23 teamId: ENG_TEAM_ID, 24 ...issueInput, 25 }); 26} 27 28/** 29 * Gets a label by name or creates it if it doesn't exist. 30 * 31 */ 32export async function getOrCreateLabelAsync( 33 labelName: string, 34 teamId?: string 35): Promise<IssueLabel> { 36 const filter: IssueLabelFilter = { name: { eq: labelName } }; 37 if (teamId) { 38 filter.team = { id: { eq: teamId } }; 39 } 40 41 const labels = await linearClient.issueLabels({ filter }); 42 if (labels.nodes[0]) { 43 return labels.nodes[0]; 44 } 45 46 const payload = await linearClient.createIssueLabel({ name: labelName }); 47 const label = await payload.issueLabel; 48 49 if (!label) { 50 throw new Error(`Failed to create Linear label: ${labelName}`); 51 } 52 53 return label; 54} 55 56/** 57 * Gets a workflow state by name and team ID. 58 */ 59export async function getTeamWorkflowStateAsync( 60 workflowState: string, 61 teamId: string 62): Promise<WorkflowState> { 63 const team = await linearClient.team(teamId); 64 const states = await team.states({ filter: { name: { eq: workflowState } } }); 65 const state = states.nodes?.[0]; 66 67 if (!state) { 68 throw new Error(`Failed to find Linear state: ${state}`); 69 } 70 71 return state; 72} 73 74/** 75 * Gets a workflow state by name and team ID. 76 */ 77export async function getTeamMembersAsync({ 78 filter, 79 teamId, 80}: { 81 filter?: UserFilter; 82 teamId: string; 83}): Promise<User[]> { 84 const team = await linearClient.team(teamId); 85 const states = await team.members({ filter }); 86 87 return states.nodes; 88} 89 90/** 91 * Gets issues by filter and team ID. 92 */ 93export async function getIssuesAsync({ 94 filter, 95 teamId, 96}: { 97 filter?: IssueFilter; 98 teamId: string; 99}): Promise<Issue[]> { 100 const team = await linearClient.team(teamId); 101 const issues = await team.issues({ filter }); 102 103 return issues.nodes; 104} 105 106/** 107 * Updates the status of an issue to Done. 108 */ 109export async function closeIssueAsync({ 110 issueId, 111 teamId, 112}: { 113 issueId: string; 114 teamId: string; 115}): Promise<boolean> { 116 const doneWorkflowState = await getTeamWorkflowStateAsync('Done', teamId); 117 const payload = await linearClient.updateIssue(issueId, { stateId: doneWorkflowState.id }); 118 119 return payload.success; 120} 121 122/** 123 * Creates a comment on an issue. 124 */ 125export async function commentIssueAsync({ 126 issueId, 127 comment, 128}: { 129 issueId: string; 130 comment: string; 131}): Promise<boolean> { 132 const payload = await linearClient.createComment({ issueId, body: comment }); 133 134 return payload.success; 135} 136