// mime-todo/lib/issue.ts export type RelationshipKind = "dependsOn" | "relatesTo" | "blocks" export interface IssueRelationships { [kind: string]: number[] } export type IssueType = "feature" | "bugfix" | "hotfix" export type IssueStatus = "open" | "in-progress" | "done" | "hold" | "cancelled" export type IssuePriority = "low" | "medium" | "high" export interface Issue { id: number type: IssueType title: string status: IssueStatus priority: IssuePriority created: string // YYYY-MM-DD relationships: IssueRelationships dueStart?: string // YYYY-MM-DD dueEnd?: string // YYYY-MM-DD description: string body: string } export function parseRelationships(text: string): IssueRelationships { const relationships: IssueRelationships = {} // Handle empty relationships if (!text || text.trim() === "") { return relationships } // Simple parsing for format like "dependsOn:3" const parts = text.split(/,\s*/).filter(p => p.trim() !== "") for (const part of parts) { const [key, value] = part.split(":").map(s => s.trim()) if (key && value) { relationships[key] = value.split(/[\s]+/).map(Number).filter(n => !isNaN(n)) } } return relationships } export function parseIssue(text: string): Issue { const lines = text.split(/\r?\n/) const issue: Partial = {} let inDescription = false const descLines: string[] = [] const bodyLines: string[] = [] for (const line of lines) { if (!inDescription) { if (line.startsWith("ID:")) issue.id = Number(line.slice(3).trim()) else if (line.startsWith("Type:")) issue.type = line.slice(5).trim() else if (line.startsWith("Title:")) issue.title = line.slice(6).trim() else if (line.startsWith("Status:")) issue.status = line.slice(7).trim() else if (line.startsWith("Priority:")) issue.priority = line.slice(9).trim() else if (line.startsWith("Created:")) issue.created = line.slice(8).trim() else if (line.startsWith("DueStart:")) issue.dueStart = line.slice(9).trim() else if (line.startsWith("DueEnd:")) issue.dueEnd = line.slice(7).trim() else if (line.startsWith("Relationships:")) { const rel = line.slice("Relationships:".length).trim() issue.relationships = parseRelationships(rel) } else if (line.startsWith("Description:")) { inDescription = true descLines.push(line.slice("Description:".length).trim()) } } else { // Description continuation if (line.trim() === "") { // blank line ends description inDescription = false continue } if (/^\s+/.test(line)) { descLines.push(line.trim()) continue } // first non-indented line ends description inDescription = false bodyLines.push(line) } } issue.description = descLines.join("\n") issue.body = bodyLines.join("\n") return issue as Issue }