(
report: PiDiagnosticReport,
description: string,
title: string,
options: { cwd?: string; now?: Date; sessionFilter?: string | null } = {},
)
| 47 | } |
| 48 | |
| 49 | export async function bundleIssueReport( |
| 50 | report: PiDiagnosticReport, |
| 51 | description: string, |
| 52 | title: string, |
| 53 | options: { cwd?: string; now?: Date; sessionFilter?: string | null } = {}, |
| 54 | ): Promise<BundledIssueReport> { |
| 55 | const LOG_TAIL_LINES = 400; |
| 56 | const allLogLines = report.logFile.exists |
| 57 | ? readFileSync(report.logFile.path, "utf-8").split(/\r?\n/) |
| 58 | : []; |
| 59 | const logLines = filterLogLinesBySession(allLogLines, options.sessionFilter ?? null); |
| 60 | const recentLog = sanitizeLogContent(logLines.slice(-LOG_TAIL_LINES).join("\n")).trim(); |
| 61 | |
| 62 | // Pull the most recent 20 ERROR-shaped lines into their own dedicated |
| 63 | // section. See logs-opencode.ts and issue-body.ts for the full |
| 64 | // rationale: this section survives even when the main log block is |
| 65 | // truncated to fit GitHub's ~64KB issue body limit. We scan a wide |
| 66 | // window so that a flood of debug noise after the error doesn't push |
| 67 | // the cause out of view. |
| 68 | const errorScanWindow = sanitizeLogContent(logLines.slice(-4000).join("\n")); |
| 69 | const recentErrorLines = extractRecentErrors(errorScanWindow, 20); |
| 70 | |
| 71 | const rawBodyMarkdown = [ |
| 72 | "## Title", |
| 73 | `[pi] ${sanitizeString(title)}`, |
| 74 | "", |
| 75 | "## Description", |
| 76 | sanitizeString(description), |
| 77 | "", |
| 78 | "## Environment", |
| 79 | `- Pi plugin: v${report.pluginVersion}`, |
| 80 | `- Pi: ${report.piVersion ?? "not installed"}`, |
| 81 | `- OS: ${report.platform} ${report.arch}`, |
| 82 | `- Node: ${report.nodeVersion}`, |
| 83 | "", |
| 84 | "## Diagnostics", |
| 85 | renderDiagnosticsMarkdown(report), |
| 86 | "", |
| 87 | "## Recent errors (last 20, sanitized)", |
| 88 | recentErrorLines.length === 0 |
| 89 | ? "_No error-shaped log lines found in recent history._" |
| 90 | : ["```", recentErrorLines.join("\n"), "```"].join("\n"), |
| 91 | "", |
| 92 | `## Log (last ${LOG_TAIL_LINES} lines, sanitized)`, |
| 93 | "```", |
| 94 | recentLog || "<no log output>", |
| 95 | "```", |
| 96 | ].join("\n"); |
| 97 | |
| 98 | // Cap the body at GitHub's ~64KB issue limit. If the rendered report |
| 99 | // is already short enough this is a pass-through; otherwise the main |
| 100 | // log block gets shrunk from the top (older lines first) and a |
| 101 | // truncation marker inserted. The error section above survives intact. |
| 102 | const bodyMarkdown = capBodyToGithubLimit(rawBodyMarkdown); |
| 103 | |
| 104 | const cwd = options.cwd ?? process.cwd(); |
| 105 | const path = join( |
| 106 | cwd, |
no test coverage detected