πŸ“ Tutorials
Β· 3 min read

Build a GitHub PR Description Generator With AI


Every PR needs a description. Every developer hates writing them. The result: PRs with descriptions like β€œfixed stuff” or β€œupdates” that tell reviewers nothing.

In this tutorial, we’ll build a CLI tool that reads your git diff and generates a structured PR description β€” summary, list of changes, testing notes, and breaking changes. Run it before opening a PR and paste the output.

What we’re building

$ pr-describe

## Summary
Add rate limiting to the API to prevent abuse. Implements token bucket 
algorithm with Redis backend. Configurable per-route limits.

## Changes
- **New**: `src/middleware/rateLimit.js` β€” Token bucket rate limiter
- **New**: `src/config/rateLimits.js` β€” Per-route limit configuration  
- **Modified**: `src/app.js` β€” Applied rate limit middleware to all routes
- **Modified**: `package.json` β€” Added `ioredis` dependency
- **Modified**: `docker-compose.yml` β€” Added Redis service

## Testing
- Run `npm test` β€” 3 new tests for rate limiting
- Manual: Hit `/api/users` 101 times in 60 seconds, verify 429 response
- Check Redis: `redis-cli GET ratelimit:127.0.0.1:/api/users`

## Breaking Changes
None

The code

#!/usr/bin/env node
// index.js
import Anthropic from '@anthropic-ai/sdk';
import { execSync } from 'child_process';

const apiKey = process.env.ANTHROPIC_API_KEY;
if (!apiKey) { console.error('Set ANTHROPIC_API_KEY'); process.exit(1); }

// Get the diff against main
const baseBranch = process.argv[2] || 'main';
let diff;
try {
  diff = execSync(`git diff ${baseBranch}...HEAD`, { encoding: 'utf-8', maxBuffer: 1024 * 1024 });
} catch {
  diff = execSync('git diff --cached', { encoding: 'utf-8', maxBuffer: 1024 * 1024 });
}

if (!diff.trim()) { console.log('No changes found.'); process.exit(0); }

// Get commit messages for additional context
const commits = execSync(`git log ${baseBranch}..HEAD --oneline 2>/dev/null || echo "no commits"`, { encoding: 'utf-8' });

// Truncate large diffs
const maxDiff = diff.length > 15000 ? diff.slice(0, 15000) + '\n... (truncated)' : diff;

const anthropic = new Anthropic({ apiKey });
const response = await anthropic.messages.create({
  model: 'claude-sonnet-4-6',
  max_tokens: 1024,
  messages: [{
    role: 'user',
    content: `Generate a GitHub PR description from this git diff.

Commits: ${commits}

Diff:
${maxDiff}

Format:
## Summary
[2-3 sentences explaining WHAT changed and WHY]

## Changes
[Bullet list: New/Modified/Deleted files with brief description]

## Testing
[How to test these changes β€” commands, manual steps]

## Breaking Changes
[List any, or "None"]

Be specific. Reference actual file names and functions.`
  }],
});

console.log(response.content[0].text);

Setup

mkdir pr-describe && cd pr-describe
npm init -y && npm install @anthropic-ai/sdk
# Add "bin": {"pr-describe": "./index.js"} to package.json
chmod +x index.js && npm link

Usage

# Before opening a PR
pr-describe              # diff against main
pr-describe develop      # diff against develop

# Copy to clipboard (macOS)
pr-describe | pbcopy

# Pipe to a file
pr-describe > pr-description.md

Making it better

  • Git hook: Add as a prepare-commit-msg hook to auto-generate on every commit
  • Template support: Load a custom PR template from .github/pr-template.md
  • Jira integration: Extract ticket numbers from branch names and link them
  • Cost optimization: Use Haiku for small diffs, Sonnet for large ones

Example output for different change types

Bug fix PR:

## Summary
Fix race condition in WebSocket reconnection that caused duplicate messages 
when clients reconnected within 5 seconds of disconnecting.

## Changes
- **Modified**: `src/ws/handler.js` β€” Added connection dedup check using client ID
- **Modified**: `src/ws/store.js` β€” Track last disconnect timestamp per client

## Testing
- Run `npm test -- --grep "websocket"` β€” 2 new tests
- Manual: Open two tabs, disconnect one, reconnect within 3 seconds β€” verify no duplicate messages

## Breaking Changes
None

Dependency update PR:

## Summary
Upgrade Express from 4.18 to 5.0 and update middleware for compatibility.

## Changes
- **Modified**: `package.json` β€” Express 4.18.2 β†’ 5.0.1
- **Modified**: `src/app.js` β€” Updated middleware registration (Express 5 syntax)
- **Deleted**: `src/middleware/bodyParser.js` β€” Built into Express 5

## Breaking Changes
- Express 5 drops support for `app.del()` β€” use `app.delete()` instead
- `req.host` no longer includes port number

Total build time: ~20 minutes. API cost: ~$0.01-0.03 per description.

Previous: Build a Local AI Chatbot for Your Docs

Related: Git Cheat Sheet Β· Build Changelog Generator

πŸ“˜