* Extract a compact tail preview from combined process output for failure logs. * @param {string} output * @param {{ maxChars?: number, maxLines?: number }} [options] * @returns {string}
(output, options)
| 313 | * @returns {string} |
| 314 | */ |
| 315 | function extractOutputTail(output, options) { |
| 316 | if (typeof output !== "string" || !output) return ""; |
| 317 | const maxChars = options?.maxChars ?? OUTPUT_TAIL_MAX_CHARS; |
| 318 | const maxLines = options?.maxLines ?? OUTPUT_TAIL_MAX_LINES; |
| 319 | const normalized = output.replace(/\0/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").trim(); |
| 320 | if (!normalized) return ""; |
| 321 | // filter(Boolean) removes empty strings from blank lines after trimEnd(); maxLines therefore counts non-empty lines. |
| 322 | const tailLines = normalized |
| 323 | .split("\n") |
| 324 | .map(line => line.trimEnd()) |
| 325 | .filter(Boolean) |
| 326 | .slice(-maxLines); |
| 327 | if (tailLines.length === 0) return ""; |
| 328 | let tail = tailLines.join("\n"); |
| 329 | if (tail.length > maxChars) { |
| 330 | const keep = maxChars - 1; |
| 331 | tail = keep > 0 ? `…${tail.slice(-keep)}` : "…"; |
| 332 | } |
| 333 | return tail; |
| 334 | } |
| 335 | |
| 336 | /** |
| 337 | * Classify a failed Copilot attempt into a short, named failure class. |
no outgoing calls
no test coverage detected