diff --git a/apps/hook/server/index.ts b/apps/hook/server/index.ts index 69eb08af6..e30f62938 100644 --- a/apps/hook/server/index.ts +++ b/apps/hook/server/index.ts @@ -260,6 +260,14 @@ if (isInteractiveNoArgInvocation(args, process.stdin.isTTY)) { // Ensure session cleanup on exit process.on("exit", () => unregisterSession()); +// Route fatal signals through process.exit() so "exit" handlers run — by +// default a SIGINT/SIGTERM death skips them, leaking background-warmup +// children and stale `git worktree` registrations (the --local PR checkout +// cleanup below is registered on "exit"). `once` keeps a second Ctrl-C as a +// force-quit escape hatch if cleanup ever hangs. +process.once("SIGINT", () => process.exit(130)); +process.once("SIGTERM", () => process.exit(143)); + // Check if URL sharing is enabled (default: true) const sharingEnabled = process.env.PLANNOTATOR_SHARE !== "disabled"; @@ -842,7 +850,11 @@ if (args[0] === "sessions") { console.log(getReviewApprovedPrompt(detectedOrigin)); } else { console.log(result.feedback); - if (!isPRMode) { + // Append the triage-first suffix whenever the reviewer sent annotations to + // act on — in PR mode too. Platform PR actions (approve/comment posted to + // the host) come back with an empty annotation set and a status message; + // those must NOT get the "triage and don't change code" instruction. + if (result.annotations.length > 0) { console.log(getReviewDeniedSuffix(detectedOrigin)); } } diff --git a/apps/opencode-plugin/commands.ts b/apps/opencode-plugin/commands.ts index 19706d0cf..daa576e4c 100644 --- a/apps/opencode-plugin/commands.ts +++ b/apps/opencode-plugin/commands.ts @@ -165,11 +165,14 @@ export async function handleReviewCommand( const shouldSwitchAgent = result.agentSwitch && result.agentSwitch !== "disabled"; const targetAgent = result.agentSwitch || "build"; + // Append the triage-first suffix when the reviewer sent annotations to + // act on (PR mode included). Platform PR actions post a status message + // with no annotations — those go through verbatim, no suffix. const message = result.approved ? getReviewApprovedPrompt("opencode") - : isPRMode - ? result.feedback - : `${result.feedback}${getReviewDeniedSuffix("opencode")}`; + : result.annotations.length > 0 + ? `${result.feedback}${getReviewDeniedSuffix("opencode")}` + : result.feedback; try { await client.session.prompt({ diff --git a/apps/pi-extension/index.ts b/apps/pi-extension/index.ts index 539aaa770..031c81bef 100644 --- a/apps/pi-extension/index.ts +++ b/apps/pi-extension/index.ts @@ -408,7 +408,6 @@ export default function plannotator(pi: ExtensionAPI): void { try { const reviewArgs = parseReviewArgs(args ?? ""); - const isPRReview = reviewArgs.prUrl !== undefined; const session = await startCodeReviewBrowserSession(ctx, { prUrl: reviewArgs.prUrl, vcsType: reviewArgs.vcsType, @@ -437,21 +436,17 @@ export default function plannotator(pi: ExtensionAPI): void { safeNotify(ctx, "Code review closed (no feedback).", "info", origin); return; } - if (isPRReview) { - // Platform PR actions (approve/comment) return approved:false with a - // status message — don't tell the agent to "address" a platform action. - sendUserMessageWithCurrentSessionFallback( - pi, - result.feedback, - { deliverAs: "followUp" }, - "Plannotator code review feedback could not be sent", - origin, - ); - return; - } + // Append the triage-first suffix when the reviewer sent + // annotations to act on (PR mode included). Platform PR actions + // (approve/comment posted to the host) come back with an empty + // annotation set and a status message — don't tell the agent to + // "address" a platform action. + const reviewFeedback = (result.annotations?.length ?? 0) > 0 + ? `${result.feedback}${getReviewDeniedSuffix("pi", loadConfig())}` + : result.feedback; sendUserMessageWithCurrentSessionFallback( pi, - `${result.feedback}${getReviewDeniedSuffix("pi", loadConfig())}`, + reviewFeedback, { deliverAs: "followUp" }, "Plannotator code review feedback could not be sent", origin,