Compare commits

..

4 Commits

Author SHA1 Message Date
Colin McDonnell 04c695038f 151 2025-12-22 13:57:51 -08:00
Colin McDonnell e9a585ce47 Improve debug logging for reviews. v0.0.150 2025-12-22 13:50:39 -08:00
Colin McDonnell 7407b6cbc5 Fix timeout 2025-12-22 12:51:04 -08:00
Colin McDonnell 507efb0c25 Fix timeout 2025-12-22 12:50:00 -08:00
7 changed files with 9237 additions and 16254 deletions
+9190 -16220
View File
File diff suppressed because one or more lines are too long
+14 -5
View File
@@ -275,11 +275,20 @@ export async function deleteProgressComment(ctx: ToolContext): Promise<boolean>
return false; return false;
} }
await ctx.octokit.rest.issues.deleteComment({ try {
owner: ctx.owner, await ctx.octokit.rest.issues.deleteComment({
repo: ctx.name, owner: ctx.owner,
comment_id: existingCommentId, repo: ctx.name,
}); comment_id: existingCommentId,
});
} catch (error) {
// ignore 404 - comment already deleted
if (error instanceof Error && error.message.includes("Not Found")) {
// comment already deleted, continue
} else {
throw error;
}
}
// reset state but mark as "updated" so ensureProgressCommentUpdated doesn't try to handle it // reset state but mark as "updated" so ensureProgressCommentUpdated doesn't try to handle it
progressCommentId = null; progressCommentId = null;
+18 -12
View File
@@ -123,19 +123,13 @@ export function StartReviewTool(ctx: ToolContext) {
id: reviewId, id: reviewId,
}; };
log.debug(`review session started: id=${reviewId}, nodeId=${reviewNodeId}`);
return { return {
message: `Review session started for PR #${pull_number}.`, message: `Review session started for PR #${pull_number}.`,
guidance: { instructions:
analyze: [ "Analyze: What does this PR change? Is the approach sound? What bugs, edge cases, or security issues exist? " +
"What does this PR change? Summarize in 1-2 sentences.", "Before commenting: Skip nitpicks unless requested. Only comment if the codebase maintainer would care.",
"Is the approach sound? If not, stop and comment on approach first.",
"What bugs, edge cases, or security issues exist?",
],
beforeCommenting: [
"Skip nitpicks unless explicitly requested.",
"Would the codebase maintainer care about this feedback, based on what you can infer about the code quality standards in this repo?",
],
},
}; };
}), }),
}); });
@@ -166,8 +160,12 @@ export function AddReviewCommentTool(ctx: ToolContext) {
throw new Error("No review session started. Call start_review first."); throw new Error("No review session started. Call start_review first.");
} }
log.debug(
`adding review comment: reviewNodeId=${ctx.toolState.review.nodeId}, path=${path}, line=${line}, side=${side || "RIGHT"}`
);
// add comment thread via GraphQL (REST doesn't support adding to existing pending review) // add comment thread via GraphQL (REST doesn't support adding to existing pending review)
await ctx.octokit.graphql<AddPullRequestReviewThreadResponse>( const result = await ctx.octokit.graphql<AddPullRequestReviewThreadResponse>(
ADD_PULL_REQUEST_REVIEW_THREAD, ADD_PULL_REQUEST_REVIEW_THREAD,
{ {
pullRequestReviewId: ctx.toolState.review.nodeId, pullRequestReviewId: ctx.toolState.review.nodeId,
@@ -178,9 +176,12 @@ export function AddReviewCommentTool(ctx: ToolContext) {
} }
); );
log.debug(`review comment added: threadId=${result.addPullRequestReviewThread.thread.id}`);
return { return {
success: true, success: true,
message: `Comment added to ${path}:${line}`, message: `Comment added to ${path}:${line}`,
threadId: result.addPullRequestReviewThread.thread.id,
}; };
}), }),
}); });
@@ -211,6 +212,9 @@ export function SubmitReviewTool(ctx: ToolContext) {
} }
const reviewId = ctx.toolState.review.id; const reviewId = ctx.toolState.review.id;
log.debug(
`submitting review: id=${reviewId}, nodeId=${ctx.toolState.review.nodeId}, prNumber=${ctx.toolState.prNumber}`
);
// build quick links footer // build quick links footer
const apiUrl = process.env.API_URL || "https://pullfrog.com"; const apiUrl = process.env.API_URL || "https://pullfrog.com";
@@ -234,6 +238,8 @@ export function SubmitReviewTool(ctx: ToolContext) {
body: bodyWithFooter, body: bodyWithFooter,
}); });
log.debug(`review submitted: reviewId=${result.data.id}, state=${result.data.state}`);
// clear review state // clear review state
delete ctx.toolState.review; delete ctx.toolState.review;
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@pullfrog/action", "name": "@pullfrog/action",
"version": "0.0.149", "version": "0.0.151",
"type": "module", "type": "module",
"files": [ "files": [
"index.js", "index.js",
+4 -4
View File
@@ -36,8 +36,8 @@ export interface WorkflowRunInfo {
export async function fetchWorkflowRunInfo(runId: string): Promise<WorkflowRunInfo> { export async function fetchWorkflowRunInfo(runId: string): Promise<WorkflowRunInfo> {
const apiUrl = process.env.API_URL || "https://pullfrog.com"; const apiUrl = process.env.API_URL || "https://pullfrog.com";
// add timeout to prevent hanging (5 seconds) // add timeout to prevent hanging (30 seconds)
const timeoutMs = 5000; const timeoutMs = 30000;
const controller = new AbortController(); const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs); const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
@@ -90,8 +90,8 @@ export async function getRepoSettings(
): Promise<RepoSettings> { ): Promise<RepoSettings> {
const apiUrl = process.env.API_URL || "https://pullfrog.com"; const apiUrl = process.env.API_URL || "https://pullfrog.com";
// Add timeout to prevent hanging (5 seconds) // Add timeout to prevent hanging (30 seconds)
const timeoutMs = 5000; const timeoutMs = 30000;
const controller = new AbortController(); const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs); const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
+5 -6
View File
@@ -342,12 +342,11 @@ export const log = {
*/ */
toolCall: ({ toolName, input }: { toolName: string; input: unknown }): void => { toolCall: ({ toolName, input }: { toolName: string; input: unknown }): void => {
const inputFormatted = formatJsonValue(input); const inputFormatted = formatJsonValue(input);
// if (inputFormatted !== "{}") const timestamp = isDebugEnabled() ? ` [${new Date().toISOString()}]` : "";
const output = inputFormatted !== "{}" ? `${toolName}(${inputFormatted})` : `${toolName}()`; const output =
inputFormatted !== "{}"
// if (inputFormatted !== "{}") { ? `${toolName}(${inputFormatted})${timestamp}`
// output += formatIndentedField("input", inputFormatted); : `${toolName}()${timestamp}`;
// }
log.info(output.trimEnd()); log.info(output.trimEnd());
}, },
+5 -6
View File
@@ -48,17 +48,16 @@ function isGitHubActionsEnvironment(): boolean {
} }
async function acquireTokenViaOIDC(): Promise<string> { async function acquireTokenViaOIDC(): Promise<string> {
log.debug("» generating OIDC token..."); log.info("» generating OIDC token...");
const oidcToken = await core.getIDToken("pullfrog-api"); const oidcToken = await core.getIDToken("pullfrog-api");
log.debug("» OIDC token generated successfully");
const apiUrl = process.env.API_URL || "https://pullfrog.com"; const apiUrl = process.env.API_URL || "https://pullfrog.com";
log.debug("» exchanging OIDC token for installation token..."); log.info("» exchanging OIDC token for installation token...");
// Add timeout to prevent long waits (5 seconds) // Add timeout to prevent long waits (30 seconds)
const timeoutMs = 5000; const timeoutMs = 30000;
const controller = new AbortController(); const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs); const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
@@ -79,7 +78,7 @@ async function acquireTokenViaOIDC(): Promise<string> {
} }
const tokenData = (await tokenResponse.json()) as InstallationToken; const tokenData = (await tokenResponse.json()) as InstallationToken;
log.debug(`» installation token obtained for ${tokenData.repository || "all repositories"}`); log.info(`» installation token obtained for ${tokenData.repository || "all repositories"}`);
return tokenData.token; return tokenData.token;
} catch (error) { } catch (error) {