Skip to content

Workflow · Coding

PR Description from Diff

Turn a git diff into a reviewer-ready PR description — summary, changes, test plan, and risks — via CLI, Copilot, or a GitHub Action.

Beginner30–60 minutesStack: Claude, GPT, or Gemini, git / GitHub CLI, Node.js or shell (optional Action)
GitHubPull RequestsDevOpsProductivity
Edited by The AIKnowHub team · Editorial team

Problem

Developers ship PRs with empty descriptions or 'fix stuff' bodies. Reviewers waste time reconstructing intent from the diff. The same sections (summary, test plan, risks) get rewritten from scratch every time. Copilot's auto-generated text is a start but rarely matches team conventions.

Final output

A Markdown PR body matching your template — Summary, Changes, Test plan (checkboxes), Risks — plus an optional title suggestion. Runnable as: `npm run pr-describe`, a `prepare-commit-msg` hook, or a GitHub Action comment on draft PRs.

Architecture

git diff (+ optional issue/PR metadata)
  → Collect context (commits, changed paths, linked issue body)
  → LLM with team PR template + conventions
  → Structured Markdown body (+ title)
  → Paste into PR / gh pr create / Action updates description

Step-by-step

  1. 01

    Define your PR template

    Write the sections reviewers expect: Summary, Changes, Test plan, Risks, Screenshots (if UI). Store as `.github/pull_request_template.md` and as the system prompt anchor.

  2. 02

    Capture diff context

    Script gathers `git diff main...HEAD`, commit subjects, list of changed paths, and optional issue text from `gh issue view`. Cap diff at ~100K tokens — summarize file list if larger.

  3. 03

    Build the generation prompt

    System: technical writer + your conventions. User: diff + metadata. Require Markdown only, no preamble. Instruct: user-visible changes first, honest test plan, flag breaking changes.

  4. 04

    Generate and review

    Call Claude Haiku/Flash for cost or Sonnet for nuance. Human scans for hallucinated features or missing risks — 30-second review before publish.

  5. 05

    Wire into workflow

    Options: local CLI alias, `gh pr create --body-file`, GitHub Action on `pull_request` opened, or [GitHub Copilot](/tools/github-copilot) PR UI with the same template pasted as instructions.

What you'll build

A small script (or GitHub Action) that reads your branch diff and outputs a reviewer-ready PR description — not a generic AI summary, but your team's exact sections with a real test plan and honest risks.

Why this matters

Review latency is often "waiting for context," not "reading code." A good PR body answers:

  • What changed (user-visible)
  • Why now
  • How to verify
  • What could go wrong

Developers skip writing this because it's tedious. LLMs are unusually good at it when you give them the diff and a rigid template. The failure mode is trusting output blindly — 30 seconds of human edit beats 15 minutes of reviewer archaeology.

Architecture

git diff main...HEAD
  + commit messages + changed paths + linked issue
    → LLM (template-locked prompt)
      → Markdown PR body
        → gh pr create / GitHub UI / Action

Pair with GitHub Copilot PR tools or Claude Code hooks — same prompt, different surface.

Step 1 — PR template

Create .github/pull_request_template.md:

## Summary

## Changes

## Test plan

## Risks

Mirror the same headings in your generator's system prompt. Reviewers and the model should see identical structure.

Add conventions inline:

  • Test command: npm test / pytest / go test ./...
  • Breaking changes require migration steps in Risks
  • UI PRs need screenshot checklist in Test plan

Step 2 — Capture context

Minimal shell collector:

#!/usr/bin/env bash
set -euo pipefail
BASE="${1:-main}"
COMMITS=$(git log --oneline "${BASE}...HEAD")
FILES=$(git diff --name-only "${BASE}...HEAD")
DIFF=$(git diff "${BASE}...HEAD")
STAT=$(git diff --stat "${BASE}...HEAD")

# Optional: linked issue from branch name feat/1234-foo
ISSUE=$(gh issue view 1234 --json body -q .body 2>/dev/null || echo "")

Cap diff size. If wc -c exceeds ~300KB, run the "large diff — summarize first" prompt on file list + stat, then describe per group.

Step 3 — Generate

Call your model with the PR description generator prompt (see frontmatter). Rules that matter:

  1. Diff wins — don't claim features not in the diff.
  2. Checkboxes — Test plan must be executable steps.
  3. Risks — prefer "none identified" over silence on large PRs.

Example local runner sketch (Node + Anthropic):

import Anthropic from "@anthropic-ai/sdk";
import { readFileSync } from "fs";

const template = readFileSync(".github/pull_request_template.md", "utf8");
const client = new Anthropic();

const msg = await client.messages.create({
  model: "claude-sonnet-4-20250514",
  max_tokens: 2048,
  system: `You write PR bodies for this repo. Template:\n${template}`,
  messages: [{ role: "user", content: buildUserPrompt({ commits, files, diff, issue }) }],
});

console.log(msg.content[0].type === "text" ? msg.content[0].text : "");

For high volume, default to Haiku or Gemini Flash — see cheapest AI APIs.

Step 4 — Human review (non-optional)

Scan for:

  • Invented endpoints or flags
  • Test steps that don't exist ("run e2e suite" when you only ran unit tests)
  • Missing BREAKING callouts on API changes

Fixing one hallucinated checkbox is cheaper than a reviewer chasing a ghost test.

Step 5 — Wire it up

Option A — CLI before push

npm run pr-describe > /tmp/pr-body.md
gh pr create --title "$(npm run pr-title --silent)" --body-file /tmp/pr-body.md

Option B — GitHub Action on pull_request: opened for draft PRs only — posts a comment "Suggested description" or updates body if empty. Reuse the pattern from AI Code Reviewer for Action scaffolding.

Option C — Copilot UI — paste the generator rules into your first Copilot Chat message when drafting the PR on GitHub.

Option D — Claude Code hook — PostToolUse on git push opens the description in your editor.

Large PR strategy

Diff sizeStrategy
< 500 linesSingle-pass full diff
500–5000 linesdiff stat + per-file one-liners from git diff --name-only
> 5000 linesGroup by directory/theme, describe groups, link to full diff

Monorepo tip: pass packages/foo as scope so Changes stays readable.

Extending

  • Auto labels — model suggests area:api, risk:high from diff paths.
  • Changelog entry — second prompt for CHANGELOG.md semver section.
  • Linear/Jira sync — pull ticket acceptance criteria into Test plan checkboxes.
  • Pair with review botAI Code Reviewer runs after description is set.

Operating cost

At 20 PRs/week and $0.02/run, inference costs **$1.60/month**. The win is reviewer time, not token savings. The highest ROI AI workflow for most engineering teams is "better PR hygiene" — this is the implementation.

Prompt examples

Copy any of these, replace the placeholders, run.

PR description generator

You write pull request descriptions for {{org}}/{{repo}}.

Template (use exactly these headings):

## Summary
1-2 sentences: what changed and why.

## Changes
- Bullet list, user-visible impact first, then internal refactors.

## Test plan
- [ ] Checkbox items a reviewer can execute

## Risks
- What could break, rollout notes, migrations

Rules:
- Only describe changes supported by the diff/commits below.
- If uncertain, say "verify" instead of inventing behavior.
- Breaking changes go in Risks with **BREAKING** prefix.
- No filler ("this PR improves code quality").

Commits:
{{commits}}

Changed files:
{{files}}

Linked issue (if any):
{{issue}}

Diff:
{{diff}}

Title suggestion (short)

Given this PR diff and description, suggest ONE title line.

Format: type(scope): imperative summary
Types: feat, fix, chore, refactor, docs, test
Max 72 characters. No period at end.

Diff summary:
{{diff_stat}}

Large diff — summarize first

This diff is too large to describe line-by-line. First pass:

1. Group changes by theme (max 8 groups).
2. For each group: files involved + one-sentence purpose.
3. Flag generated/lockfile-only groups as "mechanical."

Output JSON: { "groups": [{ "name", "files", "summary", "mechanical": boolean }] }

Diff stat:
{{diff_stat}}

File list:
{{files}}

Cost estimate

Line itemApprox. cost
Local run (Haiku / Flash, typical PR)$0.01
Local run (Sonnet, large feature PR)$0.08
GitHub Action per PR (cached template prompt)$0.02
Engineer time saved (5–15 min × loaded cost)positive ROI day one
Total per run (approx.)~$0.11

Costs depend on model choice, content length, and how aggressively you cache.

Optimizations

  • Skip generation when diff only touches `*.md` or `package-lock.json` — use a one-line template.
  • Cache the system prompt (PR template + conventions) via provider prompt caching.
  • Two-pass for huge PRs — summarize file groups first, then describe groups (not raw 5K-line diff).
  • Include `CLAUDE.md` / `.github/copilot-instructions.md` excerpts so the model knows your test commands.
  • Store generated bodies in Action artifacts for audit — helps tune prompts over time.

Common mistakes

  • Sending the entire diff with no file cap — blows token budget and dilutes quality.
  • No template — the model invents inconsistent sections every PR.
  • Publishing without a 30-second human scan — hallucinated test steps erode trust.
  • Describing intent from the issue when the diff doesn't implement it — instruct "diff wins."
  • Using the same prompt for 10-line fixes and 80-file refactors — branch on diff size.

Frequently asked questions

Copilot's built-in generator is a great default on [GitHub Copilot](/tools/github-copilot). This workflow adds your exact template, linked-issue context, and CI automation. Many teams use both — Copilot for draft, this prompt for final polish.