diff --git a/src/create-prompt/index.ts b/src/create-prompt/index.ts index d0cc7e0..0a6c385 100644 --- a/src/create-prompt/index.ts +++ b/src/create-prompt/index.ts @@ -9,6 +9,7 @@ import { formatComments, formatReviewComments, formatChangedFilesWithSHA, + stripHtmlComments, } from "../github/data/formatter"; import { isIssuesEvent, @@ -418,14 +419,14 @@ ${ eventData.eventName === "pull_request_review") && eventData.commentBody ? ` -${eventData.commentBody} +${stripHtmlComments(eventData.commentBody)} ` : "" } ${ context.directPrompt ? ` -${context.directPrompt} +${stripHtmlComments(context.directPrompt)} ` : "" } diff --git a/src/github/data/formatter.ts b/src/github/data/formatter.ts index 4b04dd9..df7b485 100644 --- a/src/github/data/formatter.ts +++ b/src/github/data/formatter.ts @@ -7,6 +7,10 @@ import type { } from "../types"; import type { GitHubFileWithSHA } from "./fetcher"; +export function stripHtmlComments(text: string): string { + return text.replace(//g, ""); +} + export function formatContext( contextData: GitHubPullRequest | GitHubIssue, isPR: boolean, @@ -33,7 +37,7 @@ export function formatBody( body: string, imageUrlMap: Map, ): string { - let processedBody = body; + let processedBody = stripHtmlComments(body); // Replace image URLs with local paths for (const [originalUrl, localPath] of imageUrlMap) { @@ -49,7 +53,7 @@ export function formatComments( ): string { return comments .map((comment) => { - let body = comment.body; + let body = stripHtmlComments(comment.body); // Replace image URLs with local paths if we have a mapping if (imageUrlMap && body) { @@ -81,7 +85,7 @@ export function formatReviewComments( ) { const comments = review.comments.nodes .map((comment) => { - let body = comment.body; + let body = stripHtmlComments(comment.body); // Replace image URLs with local paths if we have a mapping if (imageUrlMap) { diff --git a/test/data-formatter.test.ts b/test/data-formatter.test.ts index 9ff2654..1729732 100644 --- a/test/data-formatter.test.ts +++ b/test/data-formatter.test.ts @@ -6,6 +6,7 @@ import { formatReviewComments, formatChangedFiles, formatChangedFilesWithSHA, + stripHtmlComments, } from "../src/github/data/formatter"; import type { GitHubPullRequest, @@ -578,3 +579,150 @@ describe("formatChangedFilesWithSHA", () => { expect(result).toBe(""); }); }); + +describe("stripHtmlComments", () => { + test("strips simple HTML comments", () => { + const text = "Hello world"; + expect(stripHtmlComments(text)).toBe("Hello world"); + }); + + test("strips multiple HTML comments", () => { + const text = "Start middle end"; + expect(stripHtmlComments(text)).toBe("Start middle end"); + }); + + test("strips multi-line HTML comments", () => { + const text = `Line 1 + +Line 2`; + expect(stripHtmlComments(text)).toBe(`Line 1 + +Line 2`); + }); + + test("strips nested comment-like content", () => { + const text = "Text still in comment --> after"; + // HTML doesn't support true nested comments - the first --> ends the comment + expect(stripHtmlComments(text)).toBe("Text still in comment --> after"); + }); + + test("handles empty string", () => { + expect(stripHtmlComments("")).toBe(""); + }); + + test("handles text without comments", () => { + const text = "No comments here!"; + expect(stripHtmlComments(text)).toBe("No comments here!"); + }); + + test("strips complex hidden content with XML tags", () => { + const text = `Normal request + +More normal text`; + expect(stripHtmlComments(text)).toBe(`Normal request + +More normal text`); + }); + + test("handles malformed comments - no closing", () => { + const text = "Text is not stripped + expect(stripHtmlComments(text)).toBe("Text comment"; + // Just --> without opening comment"); + }); + + test("preserves legitimate HTML-like content outside comments", () => { + const text = "Use the
tag and
closing tag"; + expect(stripHtmlComments(text)).toBe( + "Use the
tag and
closing tag", + ); + }); +}); + +describe("formatBody with HTML comment stripping", () => { + test("strips HTML comments from body", () => { + const body = "Issue description visible text"; + const imageUrlMap = new Map(); + + const result = formatBody(body, imageUrlMap); + expect(result).toBe("Issue description visible text"); + }); + + test("strips HTML comments and replaces images", () => { + const body = `Check this ![img](https://github.com/user-attachments/assets/test.png)`; + const imageUrlMap = new Map([ + [ + "https://github.com/user-attachments/assets/test.png", + "/tmp/github-images/image-1234-0.png", + ], + ]); + + const result = formatBody(body, imageUrlMap); + expect(result).toBe( + "Check this ![img](/tmp/github-images/image-1234-0.png)", + ); + }); +}); + +describe("formatComments with HTML comment stripping", () => { + test("strips HTML comments from comment bodies", () => { + const comments: GitHubComment[] = [ + { + id: "1", + databaseId: "100001", + body: "Good work on this PR", + author: { login: "user1" }, + createdAt: "2023-01-01T00:00:00Z", + }, + ]; + + const result = formatComments(comments); + expect(result).toBe( + "[user1 at 2023-01-01T00:00:00Z]: Good work on this PR", + ); + }); +}); + +describe("formatReviewComments with HTML comment stripping", () => { + test("strips HTML comments from review comment bodies", () => { + const reviewData = { + nodes: [ + { + id: "review1", + databaseId: "300001", + author: { login: "reviewer1" }, + body: "LGTM", + state: "APPROVED", + submittedAt: "2023-01-01T00:00:00Z", + comments: { + nodes: [ + { + id: "comment1", + databaseId: "200001", + body: "Nice work here", + author: { login: "reviewer1" }, + createdAt: "2023-01-01T00:00:00Z", + path: "src/index.ts", + line: 42, + }, + ], + }, + }, + ], + }; + + const result = formatReviewComments(reviewData); + expect(result).toBe( + `[Review by reviewer1 at 2023-01-01T00:00:00Z]: APPROVED\n [Comment on src/index.ts:42]: Nice work here`, + ); + }); +});