diff --git a/mcp/selectMode.ts b/mcp/selectMode.ts
index 35929eb..3df16d3 100644
--- a/mcp/selectMode.ts
+++ b/mcp/selectMode.ts
@@ -1,6 +1,6 @@
import { type } from "arktype";
import { formatMcpToolRef } from "../external.ts";
-import type { Mode } from "../modes.ts";
+import { PR_SUMMARY_FORMAT, type Mode } from "../modes.ts";
import { apiFetch } from "../utils/apiFetch.ts";
import { log } from "../utils/log.ts";
import type { ToolContext } from "./server.ts";
@@ -39,9 +39,11 @@ An existing summary comment was found for this PR. Update it rather than creatin
1. Use \`previousSummaryBody\` from this response as the current summary to revise.
2. Checkout the PR via \`${t("checkout_pr")}\` — this returns PR metadata and a \`diffPath\`.
-3. Read the diff using the TOC to selectively read relevant sections. Produce an updated summary reflecting the current state of the PR, using the existing summary (\`previousSummaryBody\`) as a starting point. Follow format instructions from EVENT INSTRUCTIONS (if any).
+3. Read the diff using the TOC to selectively read relevant sections. Produce an updated summary reflecting the current state of the PR, using the existing summary (\`previousSummaryBody\`) as a starting point. If EVENT INSTRUCTIONS specify a custom format, follow that instead of the default format below.
4. Call \`${t("edit_issue_comment")}\` with \`commentId: existingSummaryCommentId\` (from this response) and the updated summary body.
-5. Call \`${t("report_progress")}\` with a brief note (e.g., "Updated PR summary.").`,
+5. Call \`${t("report_progress")}\` with a brief note (e.g., "Updated PR summary.").
+
+${PR_SUMMARY_FORMAT}`,
};
}
diff --git a/modes.ts b/modes.ts
index ccb440d..a3ad290 100644
--- a/modes.ts
+++ b/modes.ts
@@ -9,6 +9,55 @@ export interface Mode {
prompt?: string | undefined;
}
+export const PR_SUMMARY_FORMAT = `### Default format
+
+Follow this structure exactly:
+
+TL;DR — 1-3 sentences on what the PR does and why. Focus on intent, not mechanics.
+NOTE: use HTML bold TL;DR, NOT markdown bold **TL;DR**.
+
+### Key changes
+
+- **Short human-readable title** — 1 sentence per change. Write a short prose phrase (title case or sentence case); when you name a file, type, or function, put that name in backticks (e.g. **Add \`TodoTracker\` for live checklists**). A reviewer should understand the full PR from this list alone.
+
+Summary | {file_count} files | {commit_count} commits | base: \`{base}\` ← \`{head}\`
+NOTE: the metadata line goes AFTER the bullet list, not before it.
+
+Then for each key change, a ## section with a short descriptive title that reads like a documentation heading (e.g. ## Live todo checklist tracking).
+
+
+
+## Example readable section title
+
+> **Before:** [old behavior/state]
**After:** [new behavior/state]
+IMPORTANT: Before and After MUST be on a SINGLE blockquote line with an inline
between them. Two separate \`>\` lines creates a double line break.
+
+1-2 sentences of explanation. Break up text with tables, blockquotes, or lists — NEVER 3+ plain paragraphs in a row.
+
+If a change warrants deeper explanation, use a blockquoted details/summary framed as a question:
+> How does X work?
+> Extended explanation here.
+>
+
+End each section with a file links trail (3-4 key files max):
+[\`file.ts\`](https://github.com/{owner}/{repo}/pull/{number}/files#diff-{sha256hex_of_filepath}) · ...
+
+Single-feature PRs: skip the ## sections. Fold before/after and explanation into the header after key changes.
+
+CRITICAL — GitHub markdown rendering rule:
+GitHub's markdown parser requires a blank line between ALL block-level elements. This includes transitions between: HTML tags (
, , , , etc.) and markdown syntax (headings, lists, blockquotes, paragraphs). Without a blank line, GitHub treats the following content as a continuation of the HTML block and renders markdown syntax as literal text. ALWAYS separate block-level elements with a blank line.
+
+Rules:
+- \`##\` titles and key-change bullet lead-ins are plain-language summaries; backtick only actual code tokens (files, types, functions) where they appear in the title
+- ALL variable names, identifiers, and file names in body text must be in backticks
+- ALL file references MUST link to the PR Files Changed view. Compute anchors by running \`echo -n 'path/to/file.ts' | sha256sum\` via shell for each file. NEVER fabricate hex strings — run the actual command. If shell is unavailable, omit the #diff- anchor rather than guessing.
+- Add
before each ## heading for visual spacing. Do NOT use horizontal rules (---)
+- Do NOT include raw diff stats like '+123 / -45' or line counts
+- Do NOT include code blocks or repeat diff contents
+- Do NOT include a changelog section — the key changes list serves this purpose
+- Focus on *intent*, not *what* — the diff already shows what changed
+- Get the file count and commit count from the checkout_pr metadata, not by counting manually`;
+
function learningsStep(t: (toolName: string) => string, n: number): string {
return `${n}. **learnings** (only if high confidence): if you discovered something about repo setup, test commands, conventions, or patterns that you are confident is correct and would reliably help future runs, call \`${t("update_learnings")}\` to persist it. skip this step if you are unsure or the finding is speculative/one-off. format as a flat bullet list (\`- \` per line, one fact per bullet). merge with existing learnings from the prompt — pass the FULL merged list. deduplicate, and drop bullets that are clearly wrong or no longer relevant to the current codebase.`;
}
@@ -240,9 +289,11 @@ ${learningsStep(t, 4)}`,
prompt: `### Checklist
1. Checkout the PR via \`${t("checkout_pr")}\` — this returns PR metadata and a \`diffPath\`.
-2. Read the diff using the TOC to selectively read relevant sections (not the entire file). Produce a structured summary using format instructions from EVENT INSTRUCTIONS (if any); otherwise use default format: TL;DR, key changes list, per-change sections with plain-language \`##\` titles and before/after framing.
+2. Read the diff using the TOC to selectively read relevant sections (not the entire file). Produce a structured summary. If EVENT INSTRUCTIONS specify a custom format, follow that instead of the default format below.
3. Call \`${t("create_issue_comment")}\` with \`type: "Summary"\` and the summary body.
-4. Call \`${t("report_progress")}\` with a brief note (e.g., "Posted PR summary.").`,
+4. Call \`${t("report_progress")}\` with a brief note (e.g., "Posted PR summary.").
+
+${PR_SUMMARY_FORMAT}`,
},
];
}
diff --git a/package.json b/package.json
index 013e935..0aa5182 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,6 @@
"scratch": "node scratch.ts",
"upDeps": "pnpm up --latest",
"lock": "pnpm install --no-frozen-lockfile",
- "postinstall": "node scripts/generate-proxies.ts",
"prepare": "cd .. && husky action/.husky"
},
"devDependencies": {
diff --git a/scripts/generate-proxies.ts b/scripts/generate-proxies.ts
deleted file mode 100644
index 390c5cd..0000000
--- a/scripts/generate-proxies.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { mkdirSync, writeFileSync } from "node:fs";
-
-const proxies = [
- { dest: "dist/index.js", source: "../index.ts" },
- { dest: "dist/internal.js", source: "../internal/index.ts" },
-];
-
-mkdirSync("dist", { recursive: true });
-
-for (const proxy of proxies) {
- writeFileSync(proxy.dest, `export * from "${proxy.source}";\n`);
- writeFileSync(proxy.dest.replace(/\.js$/, ".d.ts"), `export * from "${proxy.source}";\n`);
-}