From 7bff1cca1aeef55d3ed00b518c05b7354ab62c11 Mon Sep 17 00:00:00 2001 From: Francesco Capponi Date: Wed, 31 Dec 2025 10:45:24 -0500 Subject: [PATCH 1/2] Add hidden header support for multi-bot sticky comments --- src/github/operations/comments/common.ts | 4 +++- .../operations/comments/create-initial.ts | 23 ++++++++++++++----- .../operations/comments/update-with-branch.ts | 4 +++- test/sticky-comment-header.test.ts | 22 ++++++++++++++++++ 4 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 test/sticky-comment-header.test.ts diff --git a/src/github/operations/comments/common.ts b/src/github/operations/comments/common.ts index df24c0329..bc592df69 100644 --- a/src/github/operations/comments/common.ts +++ b/src/github/operations/comments/common.ts @@ -24,8 +24,10 @@ export function createBranchLink( export function createCommentBody( jobRunLink: string, branchLink: string = "", + botName: string = "", ): string { - return `Claude Code is working… ${SPINNER_HTML} + const header = botName ? `\n` : ""; + return `${header}Claude Code is working… ${SPINNER_HTML} I'll analyze this and get back to you. diff --git a/src/github/operations/comments/create-initial.ts b/src/github/operations/comments/create-initial.ts index 1243035b7..5f820813b 100644 --- a/src/github/operations/comments/create-initial.ts +++ b/src/github/operations/comments/create-initial.ts @@ -14,8 +14,6 @@ import { } from "../../context"; import type { Octokit } from "@octokit/rest"; -const CLAUDE_APP_BOT_ID = 209825114; - export async function createInitialComment( octokit: Octokit, context: ParsedGitHubContext, @@ -23,7 +21,12 @@ export async function createInitialComment( const { owner, repo } = context.repository; const jobRunLink = createJobRunLink(owner, repo, context.runId); - const initialBody = createCommentBody(jobRunLink); + // Add hidden header with bot name for sticky comment identification + const initialBody = createCommentBody( + jobRunLink, + "", + context.inputs.useStickyComment ? context.inputs.botName : "", + ); try { let response; @@ -39,13 +42,21 @@ export async function createInitialComment( issue_number: context.entityNumber, }); const existingComment = comments.data.find((comment) => { - const idMatch = comment.user?.id === CLAUDE_APP_BOT_ID; + const idMatch = comment.user?.id === Number(context.inputs.botId); + + // Check for hidden header match to support multiple bots + const hiddenHeader = ``; + const headerMatch = comment.body?.includes(hiddenHeader); + const botNameMatch = comment.user?.type === "Bot" && - comment.user?.login.toLowerCase().includes("claude"); + comment.user?.login + .toLowerCase() + .includes(context.inputs.botName.toLowerCase()); const bodyMatch = comment.body === initialBody; - return idMatch || botNameMatch || bodyMatch; + // Match by ID OR hidden header OR bot name OR body + return idMatch || headerMatch || botNameMatch || bodyMatch; }); if (existingComment) { response = await octokit.rest.issues.updateComment({ diff --git a/src/github/operations/comments/update-with-branch.ts b/src/github/operations/comments/update-with-branch.ts index 838b15445..7fc41995b 100644 --- a/src/github/operations/comments/update-with-branch.ts +++ b/src/github/operations/comments/update-with-branch.ts @@ -33,7 +33,9 @@ export async function updateTrackingComment( branchLink = createBranchLink(owner, repo, branch); } - const updatedBody = createCommentBody(jobRunLink, branchLink); + // Preserve hidden header for sticky comment identification + const botName = context.inputs.useStickyComment ? context.inputs.botName : ""; + const updatedBody = createCommentBody(jobRunLink, branchLink, botName); // Update the existing comment with the branch link try { diff --git a/test/sticky-comment-header.test.ts b/test/sticky-comment-header.test.ts new file mode 100644 index 000000000..8955aa2c3 --- /dev/null +++ b/test/sticky-comment-header.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, test } from "bun:test"; +import { createCommentBody } from "../src/github/operations/comments/common"; + +describe("Sticky Comment Header Logic", () => { + test("generates hidden header when botName is provided", () => { + const body = createCommentBody("http://example.com", "", "claude-security"); + expect(body).toContain(""); + expect(body).toContain("Claude Code is working"); + }); + + test("does not generate header when botName is missing", () => { + const body = createCommentBody("http://example.com"); + expect(body).not.toContain("`; const headerMatch = comment.body?.includes(hiddenHeader); + // Check if comment has ANY hidden header (to detect other bots) + const hasAnyHeader = comment.body?.includes("