Imported from carlosedm10/agi-jev-containment (
.agents/skills/generate-pr-description/SKILL.md). Install upstream withnpx skills add carlosedm10/agi-jev-containment --skill generate-pr-description. Copyright stays with the author.
Generate PR Description
What This Does
Analyzes the diff between the branch and the default branch and generates a short, plain-language PR description in this template:
- πͺ Why? β the problems this PR exists to solve, in one short paragraph
- π What? β a handful of bold-led paragraphs, one per theme
- π‘ Context β links to sibling PRs / dependencies, only when they exist
The output is a starting point the author refines. It must read like a teammate explaining the PR out loud, not like an engineering log.
If the repo has its own PR template (.github/pull_request_template.md), use its headings and
apply the style rules below inside them.
How to Use It
- "Generate my PR description"
- "Create a PR description for this branch"
- Optionally name a branch: "Generate PR description for
feature/auth"
Step 1: Get the Diff
BASE=$(git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null || echo origin/main)
git diff --stat $(git merge-base "$BASE" HEAD)
git diff $(git merge-base "$BASE" HEAD)
Include uncommitted working-tree changes if the user is describing work not yet committed. For
huge diffs, read --stat first and then the files that matter; don't try to narrate every file.
Step 2: Find the Themes, Not the Files
Group the diff into 4β8 themes β a theme is something a reviewer or a teammate would care
about as one idea ("uploads are virus-scanned before storage", "multi-region creates are
all-or-nothing"), never a file or a class. Everything that doesn't deserve its own theme goes
into a single Cleanups: paragraph, or is dropped.
Step 3: Write It β the style rules
This is the part that matters. The house style is concise and plain:
- Why? is one short paragraph. The user-visible problems only β what broke, what was risky, what was annoying. No architecture, no history.
- Each What? theme is one paragraph: a bold plain-language claim, then 1β3 simple sentences. The bold lead states the outcome ("Multi-region creates are all-or-nothing."), the sentences after say just enough to make it concrete.
- At most one or two identifiers per paragraph. Name the entry point (
createInvoice,handleUpload) so reviewers can find it; do not enumerate helpers, types, files, or constants. - No defensive rationale, no incident history, no design essays. "Why we didn't do X", "an earlier revision did Y", bound/limit enumerations, and invariant proofs belong in code comments or review threads β never in the body.
- Translate jargon. "The browser calls the backend directly" beats "the orchestration moved behind the API contract boundary". If a sentence needs the reader to know internal codenames to parse it, rewrite it.
- Bugs fixed along the way get half a sentence each, inside the theme they belong to ("Fixed along the way: the new-plan save didn't persist").
- One
Cleanups:paragraph for small unrelated improvements, and optionally oneCoverage:line if the tests are worth calling out. - One paragraph = one line. Never hard-wrap markdown prose.
- Total budget: the whole body fits on one screen (~150β250 words per PR is the norm; a giant PR may reach ~350). If the draft runs longer, merge or cut themes β do not compress by densifying sentences.
Example (the calibration target)
## πͺ Why?
Uploading a file was the least trustworthy path in the product. Anything a user attached went straight to storage unchecked, a failed multi-file upload left half the batch behind, and the only signal the author got back was a spinner that eventually stopped β with no way to tell a rejected file from a slow one.
## π What?
**Uploads are scanned before they are stored.** The API accepts the file into a quarantine bucket, scans it, and only then promotes it; a rejected file never reaches the public bucket.
**Batch uploads are all-or-nothing.** A batch validates every file first, then writes, and cleans up storage if anything fails mid-way β a failed batch can no longer leave orphaned blobs that count against the user's quota.
**The uploader tells you what happened.** Per-file status, the real rejection reason, and a retry that only re-sends the files that failed.
**Quota checks moved to the server.** `createUpload` is now the single place the limit is enforced, so the browser can't be talked out of it. Fixed along the way: the quota counter double-counted replaced files.
Cleanups: the upload component was split into small focused pieces, and the storage client's retry logic moved into a tested class β which immediately caught a real bug.
## π‘ Context
- [π» Other PR](https://github.com/acme/mobile/pull/412) β the mobile-side half.
Anti-example (what to avoid)
Uploads are scanned.
createUploadtakes the multipart body and its content hash and returns a promoted blob handle; the API owns the whole deterministic half inUseCases::Uploads::PromoteScanned: it re-derives the content type from the same sniffer the client used (never trusting the header), resolves the bucket policy, builds a quarantine key, strips path prefixes, rejects a partial body via a truncation guard measured againstContent-Length, refuses a payload pastMAX_UPLOAD_BYTESβ¦
Same content, wrong altitude: it enumerates the implementation, names five internals, and argues design rationale. Reviewers read the code for that.
Step 4: Output
Emit the markdown in a code block, using exactly:
## πͺ Why?
[one short paragraph]
## π What?
[4β8 bold-led theme paragraphs, then optional Cleanups:/Coverage: paragraphs]
## π‘ Context
[only if there are sibling PRs, dependencies, or docs worth linking β otherwise omit the section]