Writing detailed PR descriptions is important for code review, but it's tedious. In this tutorial, you'll build an AI agent that reads your git diff and writes a clear, structured PR description automatically. No more skipped descriptions or vague bullet points.

What you'll build

  • A Python script that extracts the git diff and sends it to an AI model
  • A GitHub Action that triggers on PR creation and posts the AI-generated description
  • A structured output format that works for any team

Step 1: Set up the Python script

Create a file pr_description_generator.py in your repository root.

import os
import subprocess
from openai import OpenAI

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

def get_diff(base_branch="main", head_branch=None):
    if head_branch:
        result = subprocess.run(
            ["git", "diff", f"{base_branch}...{head_branch}"],
            capture_output=True, text=True
        )
    else:
        result = subprocess.run(
            ["git", "diff", "origin/main"],
            capture_output=True, text=True
        )
    return result.stdout

def generate_description(diff):
    response = client.chat.completions.create(
        model="gpt-4",
        messages=[
            {"role": "system", "content": "You are a helpful assistant that writes PR descriptions."},
            {"role": "user", "content": f"""
Given the following git diff, write a clear pull request description.
Include:
- Summary of changes (one paragraph)
- Motivation (why these changes were made)
- Key structural changes (new files, major refactors)
- Testing notes (what to verify)

Diff:
```
{diff[:10000]}
```
"""}
        ],
        temperature=0.5,
        max_tokens=500
    )
    return response.choices[0].message.content

if __name__ == "__main__":
    diff = get_diff()
    desc = generate_description(diff)
    print(desc)
Note: The diff is truncated to 10,000 characters to avoid hitting token limits. Adjust as needed for your model.

Step 2: Create the GitHub Action

Add a workflow file at .github/workflows/pr-description.yml.

name: AI PR Description
on:
  pull_request:
    types: [opened, synchronize]

permissions:
  pull-requests: write
  contents: read

jobs:
  generate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.10'
      - name: Install dependencies
        run: pip install openai
      - name: Generate PR description
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        id: generate
        run: |
          echo 'DESCRIPTION<> $GITHUB_ENV
          python pr_description_generator.py >> $GITHUB_ENV
          echo 'EOF' >> $GITHUB_ENV
      - name: Update PR body
        uses: actions/github-script@v7
        with:
          script: |
            const body = process.env.DESCRIPTION;
            github.rest.pulls.update({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.issue.number,
              body: body
            });

Step 3: Add your OpenAI API key

Go to your GitHub repository settings → Secrets and variables → Actions. Add a new secret named OPENAI_API_KEY with your key.

Step 4: Try it out

Create a new branch, make some changes, and open a pull request. Within a few seconds, the AI will overwrite the empty PR body with a full description.

Success! Your PR descriptions are now automated. You can customize the prompt to match your team's style.

Customization Tips

  • Use gpt-4-turbo for faster responses.
  • Adjust the temperature parameter to control creativity (0.1 for factual, 0.8 for creative).
  • Add a base_branch parameter to handle PRs targeting different branches.
Warning: Be mindful of API costs. Each PR update will consume tokens based on diff size. Consider caching descriptions for unchanged diffs.

That's it! You've built a practical AI agent that saves your team time. Now go ship that feature with a perfect PR description.