MCP Linear App

by zalab-inc
Verified
/** * Helper function to safely convert priority number to descriptive label * Handles invalid priority values gracefully * * @param priority Priority number (0-4) * @returns Descriptive label for the priority */ export function getPriorityLabel(priority: number | null | undefined): string { if (priority === null || priority === undefined) { return "No priority"; } switch (priority) { case 0: return "No priority"; case 1: return "Urgent"; case 2: return "High"; case 3: return "Medium"; case 4: return "Low"; default: return `Unknown (${priority})`; } } /** * Safely formats a date value to a human-readable string * Handles various date formats and potential null/undefined values * * @param dateValue Date value to format (string, Date, or undefined) * @returns Formatted date string */ export function formatDate(dateValue: string | Date | null | undefined): string { if (!dateValue) return "None"; try { const date = typeof dateValue === 'string' ? new Date(dateValue) : dateValue; // Check if date is valid if (isNaN(date.getTime())) { return "Invalid date format"; } return date.toLocaleDateString('en-US', { day: 'numeric', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch { return "Date format error"; } } /** * Safely gets text content, handling null/undefined values * * @param text Text to process * @param defaultText Default text if input is empty * @returns Processed text string */ export function safeText(text: string | null | undefined, defaultText: string = "None"): string { if (text === null || text === undefined || text.trim() === "") { return defaultText; } return text; } /** * Cache for storing state IDs by team to reduce API calls * Structure: { teamId: { stateName: stateId } } */ const stateIdCache: Record<string, Record<string, string>> = {}; /** * Get state ID from Linear API based on state name and team ID * Uses caching to reduce API calls * * @param stateName The name of the state (triage, backlog, todo, in_progress, done, canceled) * @param teamId The ID of the team * @param linearClient The Linear client instance * @returns Promise with the state ID or undefined if not found */ export async function getStateId(stateName: string, teamId: string, linearClient: import('@linear/sdk').LinearClient): Promise<string | undefined> { // Check if we have a cached state ID for this team if (stateIdCache[teamId] && stateIdCache[teamId][stateName]) { return stateIdCache[teamId][stateName]; } try { // Get workflow states for the team const team = await linearClient.team(teamId); if (!team) { console.error(`Team with ID ${teamId} not found`); return undefined; } const states = await team.states(); if (!states || states.nodes.length === 0) { console.error(`No workflow states found for team ${teamId}`); return undefined; } // Initialize cache for this team if it doesn't exist if (!stateIdCache[teamId]) { stateIdCache[teamId] = {}; } // Find the state by name and populate the cache while we're at it let targetStateId: string | undefined; for (const state of states.nodes) { const normalizedName = state.name.toLowerCase().replace(/\s+/g, '_'); stateIdCache[teamId][normalizedName] = state.id; // Also handle our standard state naming (triage, backlog, etc.) if (normalizedName === stateName || state.name.toLowerCase() === stateName.replace(/_/g, ' ')) { targetStateId = state.id; } } return targetStateId; } catch (error) { console.error(`Error fetching state ID for ${stateName} in team ${teamId}:`, error); return undefined; } } /** * Map common state names to standard Linear workflow state names * This helps standardize user input to match Linear's expected values * * @param stateName The state name to normalize * @returns Normalized state name */ export function normalizeStateName(stateName: string): string { const mapping: Record<string, string> = { 'triage': 'triage', 'backlog': 'backlog', 'todo': 'todo', 'to_do': 'todo', 'in_progress': 'in_progress', 'in-progress': 'in_progress', 'inprogress': 'in_progress', 'done': 'done', 'completed': 'done', 'canceled': 'canceled', 'cancelled': 'canceled' }; const normalized = stateName.toLowerCase().replace(/\s+/g, '_'); return mapping[normalized] || normalized; }