| 275 | } |
| 276 | |
| 277 | function escapeCSV(value) { |
| 278 | if (!value) { |
| 279 | return ''; |
| 280 | } |
| 281 | |
| 282 | // Normalize newlines for CSV compatibility |
| 283 | let stringValue = String(value).replace(/\r\n/g, '\n').replace(/\r/g, '\n'); |
| 284 | |
| 285 | // Prevent formula injection when opened in Excel/Google Sheets |
| 286 | if (/^[=+\-@\t\r]/.test(stringValue)) { |
| 287 | stringValue = `'${stringValue}`; |
| 288 | } |
| 289 | |
| 290 | // Escape any internal double quotes and wrap in quotes so commas/newlines don't break the cell |
| 291 | return `"${stringValue.replace(/"/g, '""')}"`; |
| 292 | } |
| 293 | |
| 294 | function removePRNumber(title) { |
| 295 | return title.replace(/\s*\(#\d+\)\s*$/, ''); |