MCPcopy Create free account
hub / github.com/cortexkit/magic-context / capBodyToGithubLimit

Function capBodyToGithubLimit

packages/cli/src/lib/issue-body.ts:108–176  ·  view source on GitHub ↗
(
    body: string,
    maxBytes: number = MAX_GITHUB_BODY_BYTES,
)

Source from the content-addressed store, hash-verified

106 * `body` payloads above the limit).
107 */
108export function capBodyToGithubLimit(
109 body: string,
110 maxBytes: number = MAX_GITHUB_BODY_BYTES,
111): string {
112 if (Buffer.byteLength(body, "utf8") <= maxBytes) return body;
113
114 // Find the main log fence (we use a stable heading that the bundlers
115 // always emit below). We don't search for the closing ``` directly
116 // because there are several fenced blocks; instead we anchor on the
117 // heading line and assume the fence pair is the next ``` / ``` after
118 // it (the bundler's structure guarantees this).
119 const heading = "## Log (last";
120 const headingIdx = body.indexOf(heading);
121 if (headingIdx === -1) {
122 // No main log section to shrink — fall back to a raw byte truncation
123 // with a marker. This shouldn't happen with the bundlers we control,
124 // but keeps the function defensive for callers passing arbitrary
125 // markdown.
126 const marker = "\n\n[truncated for GitHub 64KB limit]\n";
127 const markerBytes = Buffer.byteLength(marker, "utf8");
128 // Slice the body to a code-point boundary so we never split a multi-
129 // byte UTF-8 character. Naive `Buffer.subarray(...).toString("utf8")`
130 // would replace any half-codepoint at the cut with U+FFFD (3 bytes),
131 // pushing the output OVER the requested budget.
132 return truncateToByteBudget(body, maxBytes - markerBytes) + marker;
133 }
134 const fenceOpenIdx = body.indexOf("\n```", headingIdx);
135 if (fenceOpenIdx === -1) return body; // malformed; pass through unchanged
136 const logStart = fenceOpenIdx + "\n```\n".length;
137 const fenceCloseIdx = body.indexOf("\n```", logStart);
138 if (fenceCloseIdx === -1) return body;
139
140 const head = body.slice(0, logStart);
141 const log = body.slice(logStart, fenceCloseIdx);
142 const tail = body.slice(fenceCloseIdx);
143
144 const overheadBytes = Buffer.byteLength(head, "utf8") + Buffer.byteLength(tail, "utf8");
145 // Reserve room for the truncation marker that we'll prepend to the log
146 // body so the agent / human reading the issue knows lines were dropped.
147 const truncationMarker = "[truncated for GitHub 64KB limit — older log lines dropped]\n";
148 const markerBytes = Buffer.byteLength(truncationMarker, "utf8");
149 const logBudget = maxBytes - overheadBytes - markerBytes;
150 if (logBudget <= 0) {
151 // Even with no log content we'd be over budget. Drop the log block
152 // entirely (keep the heading + a stub marker) so the rest survives.
153 return `${head}${truncationMarker}${tail}`;
154 }
155
156 // Drop oldest lines (from the top) until what's left fits the budget.
157 // We split on newlines to preserve line boundaries; binary truncation
158 // would corrupt the final line.
159 const lines = log.split("\n");
160 let keepLines = lines;
161 let kept = keepLines.join("\n");
162 while (Buffer.byteLength(kept, "utf8") > logBudget && keepLines.length > 1) {
163 // Drop ~5% from the top each iteration for fast convergence on
164 // very-oversized inputs. Caps at "drop at least one line".
165 const dropCount = Math.max(1, Math.floor(keepLines.length * 0.05));

Callers 3

bundleIssueReportFunction · 0.90
issue-body.test.tsFile · 0.90
bundleIssueReportFunction · 0.90

Calls 1

truncateToByteBudgetFunction · 0.85

Tested by

no test coverage detected