Releases: rekpero/claude-bugbot-github-action
v1.0.11
Fixed
-
HTTP 422 on review POST when multiple bugs share an anchor —
POST /repos/{owner}/{repo}/pulls/{n}/reviewsrejects payloads with two inline comments at the same(path, line, side). This collided easily because both fallback branches inpostReviewconverge on a single anchor: bugs whose reported line falls outside the diff all anchor toMath.min(...fileLines), and bugs whose file isn't in the diff at all anchor to the first file in the diff. With two or more such bugs in a single run, GitHub returned 422 and BugBot fell back to a plain PR comment, losing inline placement entirely.postReviewnow de-duplicates inline comments by(path, line)via a Map and merges duplicate bodies with a---separator, so multiple bugs at the same anchor produce one well-formed inline comment instead of a rejected payload. -
parseDiffmisread+++ b/content as a file header — A hunk content line that happens to start with+++ b/(e.g. someone adding a literal diff snippet to a doc or test fixture) was matched by the file-header check, pollutingcurrentFileand dropping the rest of that hunk's lines fromvalidLines. AddedinHunkstate tracking that only treats+++ b/as a header in the diff's header section (betweendiff --gitand the first@@). Verified against three test cases including the tricky++ b/examplecontent. -
bug.severity.toUpperCase()crash when Claude omitted severity — A malformed bug entry from Claude (missing or non-stringseverity) crashed the action with a TypeError. Added anormalizeBugstep that runs once afterparseResponse, defaultingseverityto'unknown', coercingbug.lineto an integer (handling string"42"and float42.5), and dropping non-object entries. Downstream code can now trust the shape. -
Brittle JSON merging in the >300-files fallback —
fetchDiffViaFilesAPIparsedgh api --paginate's concatenated array output viaJSON.parse(raw.replace(/]\s*\[/g, ',')), which corrupts data whenever any patch contains]adjacent to[(regex source code, JSON examples, etc.). Replaced with an explicit per-page loop using?per_page=100&page=N, eliminating the regex hack entirely. -
#LundefinedURL fragments in inline comments — When a bug or itsadditional_locationsentry was missing a line number, the generated GitHub link contained#Lundefined. Added alineFragmenthelper that emits the fragment only when the line is a positive integer, and tightenedadditional_locationsrendering to skip entries without a stringfile. -
Hidden
ghAPI error bodies on failure —gh apierrors only printgh: ... (HTTP 422)to stderr; the structured response body that explains why the request was rejected was being swallowed byexecSync's error wrapping.postReview,postFallbackComment, andfetchDiffnow usespawnSyncso we can log the full stderr/stdout and the payload path on failure, making future review-API rejections debuggable from the Actions log alone.
Changed
-
bugId→bugIdsin open-thread tracking — A merged inline comment carries multiple<!-- bugbot-id:file:line -->tags, sofetchOpenBugThreadsnow usesmatchAllto collect every tag in a comment and exposesbugIds: string[](one entry per merged bug) instead of a singlebugId. The dedup set built inmain()flattens these arrays, ensuring all bugs in a merged thread are correctly suppressed on subsequent runs. The Claude resolution prompt was rewritten to evaluate eachbugIdin a thread independently and resolve the thread only when every bug is fixed. -
postReviewcleans up its temp dir on success only — On failure the directory is intentionally retained and its path logged so the rejected payload can be inspected directly.
v1.0.10
Changed
- Full codebase available for targeted verification — Claude now runs with its working directory set to
GITHUB_WORKSPACE(the checked-out repo root) and is instructed to use its file-reading tools to verify findings against the actual source before reporting a bug. For example, if a function call in the diff looks wrong, Claude reads the file that defines it to confirm the signature. Lookups are explicitly scoped to things directly referenced by the changed lines — broad codebase scanning is prohibited. This reduces false positives caused by incomplete diff context (e.g. flagging a function as missing when it exists outside the diff).
v1.0.9
Changed
.github/directory excluded from bug analysis — Files under.github/(GitHub Actions workflows, configs, etc.) are now stripped from the diff before BugBot analyzes it. These are infrastructure files, not application code, and flagging bugs in them is noise. Exclusions are driven by a top-levelEXCLUDED_PATH_PREFIXESlist inanalyze.mjs— add entries there to exclude additional directories as needed.
v1.0.8
Fixed
- Large diffs are no longer truncated to 200KB — The 200KB truncation limit was added when the diff was passed inline in the prompt. Since the diff is now written to a temp file and Claude reads it via file I/O, the context-window concern no longer applies. Removed
MAX_DIFF_SIZEand the truncation block so Claude always analyzes the full diff.
v1.0.7
Fixed
- Review threads beyond the first 100 are no longer silently missed —
fetchOpenBugThreadspreviously fetched review threads withfirst: 100and no pagination, so PRs with more than 100 threads would silently drop any threads past the first page. This caused BugBot to re-post comments it had already posted (failing deduplication) and miss threads it should have resolved. The query now uses cursor-based pagination (after: $cursor+pageInfo { hasNextPage endCursor }) and loops until all pages are fetched, accumulating threads across pages before filtering for open BugBot comments.
v1.0.6
Fixed
- PRs with more than 300 changed files no longer crash BugBot —
gh pr diffcalls the GitHub diff endpoint which returns HTTP 406 (too_large) when a PR touches more than 300 files. Previously this caused an unhandled error and BugBot would exit without analysis. NowfetchDiffcatches the 406/too_largeerror and automatically falls back tofetchDiffViaFilesAPI, which uses the paginatedGET /repos/{owner}/{repo}/pulls/{number}/filesAPI (supporting up to 3 000 files). Each file'spatchfield is reassembled into standard unified diff format so all downstream parsing (parseDiff, inline comment placement, etc.) continues to work without modification. Binary files and individual files whose patch GitHub cannot produce are silently skipped, consistent with the existing diff-truncation behaviour.
v1.0.5
Changed
- Analysis timeout raised from 10 minutes to 30 minutes —
ANALYSIS_TIMEOUT_MSincreased from10 * 60_000to30 * 60_000. Large or complex diffs can legitimately require more than 10 minutes for Claude to fully analyze; the previous limit caused premature SIGKILL and retry cycles on heavier PRs.
v1.0.4
Fixed
-
JSON parse failures now trigger retries instead of immediate CI failure — Claude Code CLI occasionally responds with natural-language preamble (e.g.
"After reading through the full diff, here is my analysis:") instead of the requested bare JSON, causingparseResponseto throw"Could not extract JSON from Claude's response". Previously this error propagated straight to the top-level handler and failed the job.parseResponseis now called inside therunClauderetry loop: if parsing fails on an attempt, a warning is logged and the full Claude invocation is retried up toMAX_ATTEMPTS(3) times before giving up. -
BugBot analysis failure no longer fails the CI job — The top-level
main().catch()handler previously calledprocess.exit(1), causing the GitHub Actions job to reportProcess completed with exit code 1and block the PR status check whenever BugBot encountered any unrecoverable error. Changed toprocess.exit(0)so a BugBot failure is visible in the logs but does not mark the job as failed or block merging.
v1.0.3
Changed
-
Bugs with unmappable lines are now always posted as inline comments — Previously, any bug whose exact line was not present in the diff was appended as plain text to the review body under "Additional findings". This made them invisible in the Files Changed tab and impossible for BugBot to auto-resolve later (no review thread was created). Now all bugs are always posted as inline comments:
- If the bug's exact line is in the diff → normal inline comment at that line (unchanged).
- If the bug's file is in the diff but the exact line is not → comment anchored to the first valid line of that file, with a note: "Could not locate exact line in the diff. The bug is at
file:line." - If the bug's file is not in the diff at all → comment anchored to the first valid line of the first file in the diff, with a note: "This file is not part of this PR's diff. The bug is at
file:line." - In all cases the hidden
<!-- bugbot-id:file:line -->tag uses the real bug location, so deduplication and auto-resolution work correctly on subsequent commits. - The "Additional findings" section in the review body has been removed entirely.
-
Resolution prompt hardened for anchored threads — The Claude prompt now explicitly states that
bugIdalways reflects the real bug location regardless of where the review thread is anchored. This prevents Claude from evaluating resolution based on the anchor file instead of the actual file where the bug lives.
Fixed
Infinityline number crash when first diff file is deletion-only — The last-resort anchor (used when a bug's file is absent from the diff) was computed withMath.min(...validLines.get(firstFile)). If the first file in the diff had only deleted lines, its valid-lines Set was empty, causingMath.min()to returnInfinity. The GitHub API rejected this with an error. Fixed by iteratingvalidLinesto find the first file that has at least one valid commentable line before computing the anchor.
v1.0.2
Fixed
-
Open threads not resolved when PR has no new bugs — When Claude reported no new bugs (
bugs: []), the "no bugs found" example template in the prompt showed"resolved_thread_ids": [], causing Claude to copy it verbatim and return an empty list regardless of whether previously-reported bugs were actually fixed. The template now shows a semantic placeholder ("<threadId of each thread now fixed>") and adds an explicit note thatresolved_thread_idsmust be evaluated independently ofbugs— an emptybugsarray is not a reason to leaveresolved_thread_idsempty. -
Threads not resolved when fixed file is absent from the diff — The prompt instruction "if a thread's bug is untouched by the diff, omit it from
resolved_thread_ids" caused Claude to conservatively skip resolution whenever the bug's file did not appear in the diff (e.g. a bug fixed indirectly via a shared function or caller). Resolution rules are now explicit: if the bug's file is in the diff and the bug is gone → resolve; if the file is in the diff and the bug remains → keep open; if the file is NOT in the diff, use broader judgment and resolve if the PR addresses the root cause elsewhere or nothing related looks broken. -
Silent failure when Claude returned
bugIdinstead ofthreadId— Theresolved_thread_idsfield in the prompt schema used placeholder names ("threadId1","threadId2") that did not distinguish between thethreadId(GitHub GraphQL node ID, e.g.PRRT_kwDO...) andbugId(file:line) fields present in the open-threads JSON. Claude occasionally placedbugIdvalues inresolved_thread_ids, causing theresolveReviewThreadGraphQL mutation to fail silently (logged asconsole.warn). The prompt now explicitly states:resolved_thread_ids must contain the "threadId" field value (the GitHub GraphQL node ID, e.g. "PRRT_kwDO...") — NOT the "bugId" field value.