DEV Community

Hiroshi Toyama
Hiroshi Toyama

Posted on

Auto-generate Pull Requests with Claude API and Shell Function

Created git-pr-auto

I built a shell function that sends git diff to Claude to auto-generate Pull Request titles and descriptions. It includes practical features like automatic GitHub account switching and remote branch checking.

Key features:

  • Claude automatically generates PR title and body from git diff
  • Auto-detects master and main branches
  • Checks remote branch existence before proceeding
  • Automatic GitHub account switching (personal/work)
  • Language switching based on account type
  • Confirmation step before creating PR
  • Automatically opens PR in browser after creation

Why I Built This

Writing Pull Requests was tedious. I faced several challenges:

PR Creation Takes Too Much Time

  • Recalling changes to write a title
  • Organizing and describing changes
  • Formatting the description
  • These tasks took 5-10 minutes every time

Switching Between Multiple Accounts is Annoying

  • Using separate GitHub accounts for work and personal projects
  • Need to configure git config for each repository
  • GitHub CLI authentication needs switching too
  • Wrong settings lead to PRs from the wrong account

Inconsistent PR Quality

  • Descriptions become sloppy when tired
  • Information gets insufficient when rushed
  • Sometimes fail to accurately convey changes

git-pr-auto solves these issues. Claude API analyzes the diff and generates appropriate titles and descriptions. Automatic account switching prevents configuration mistakes.

Prerequisites

You need the following setup to use this function.

GitHub CLI

# macOS
brew install gh

# Authentication (personal)
gh auth login

# Authentication (work)
GH_HOST=github.example.com gh auth login
Enter fullscreen mode Exit fullscreen mode

Claude CLI

# For macOS
brew install anthropic/claude/claude

# Authentication
claude auth
Enter fullscreen mode Exit fullscreen mode

Check the official documentation for Claude CLI.

Environment Variables

# Add to .zshrc or .bashrc

# Pattern for personal account repositories (regex)
export GITHUB_PERSONAL_PATTERN="github.com/(your-username|your-org)"

# Work account settings
export GITHUB_WORK_USER="work.user@company.com"
export GITHUB_WORK_HOST="github.example.com"
Enter fullscreen mode Exit fullscreen mode

Implementation

Here's the complete implementation.

git-pr-auto() {
  # Auto-configure account
  if ! git-auth-setup; then
    return 1
  fi

  # Check GitHub CLI authentication
  if ! gh auth status >/dev/null 2>&1; then
    echo "Error: GitHub CLI is not authenticated"
    if [ -n "$GH_HOST" ]; then
      echo "Please run: GH_HOST=$GH_HOST gh auth login"
    else
      echo "Please run: gh auth login"
    fi
    echo ""
    echo "Current directory: $(pwd)"
    echo "Remote URL: $(git remote get-url origin 2>/dev/null)"
    return 1
  fi

  # Get current branch
  CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)

  # Detect master or main branch
  if git branch -a | grep -q "^\s*master$\|^\s*\*\s*master$\|remotes/origin/master$"; then
    BASE_BRANCH=master
  elif git branch -a | grep -q "^\s*main$\|^\s*\*\s*main$\|remotes/origin/main$"; then
    BASE_BRANCH=main
  else
    echo "Error: Neither master nor main branch found"
    return 1
  fi

  # Error if current branch is base branch
  if [ "$CURRENT_BRANCH" = "$BASE_BRANCH" ]; then
    echo "Error: Cannot create PR from $BASE_BRANCH branch"
    return 1
  fi

  # Determine whether to use local or remote branch
  if git rev-parse --verify "$BASE_BRANCH" >/dev/null 2>&1; then
    COMPARE_BASE="$BASE_BRANCH"
  elif git rev-parse --verify "origin/$BASE_BRANCH" >/dev/null 2>&1; then
    COMPARE_BASE="origin/$BASE_BRANCH"
  else
    echo "Error: Cannot find $BASE_BRANCH branch locally or remotely"
    return 1
  fi

  echo "πŸ“Š Checking diff between $CURRENT_BRANCH and $COMPARE_BASE..."
  echo "----------------------------------------"

  # Get diff
  DIFF=$(git diff $COMPARE_BASE..HEAD)

  if [ -z "$DIFF" ]; then
    echo "Error: No differences found between $CURRENT_BRANCH and $COMPARE_BASE"
    return 1
  fi

  # Show diff summary
  echo "Files changed:"
  STAT_OUTPUT=$(git diff --stat $COMPARE_BASE..HEAD)
  if [ -z "$STAT_OUTPUT" ]; then
    echo "Error: No file changes detected"
    return 1
  fi
  echo "$STAT_OUTPUT"
  echo "----------------------------------------"

  # Check remote branch existence
  if ! git ls-remote --exit-code --heads origin "$CURRENT_BRANCH" >/dev/null 2>&1; then
    echo ""
    echo "❌ Error: Remote branch 'origin/$CURRENT_BRANCH' does not exist"
    echo ""
    echo "Please push your branch to remote first:"
    echo "  git push -u origin $CURRENT_BRANCH"
    return 1
  fi

  # Generate PR content with claude command
  echo "πŸ€– Analyzing diff with Claude..."

  # Change Body language based on account type
  if [ "$GITHUB_ACCOUNT_TYPE" = "personal" ]; then
    BODY_LANG="English"
  else
    BODY_LANG="Japanese"
  fi

  PR_CONTENT=$(claude -p "Generate a Pull Request title and description from the following git diff.

Please output in the following format only (do not output any other text):

TITLE: [Concise title (English, within 50 characters)]

BODY:
[Description of changes (in ${BODY_LANG} in detail)]
- Change 1
- Change 2
...

Below is the git diff:
$DIFF
")

  # Debug: Check Claude output
  if [ -z "$PR_CONTENT" ]; then
    echo "Error: Claude returned empty response"
    return 1
  fi

  # Extract title and body
  PR_TITLE=$(echo "$PR_CONTENT" | grep "^TITLE:" | head -n 1 | sed 's/^TITLE:[[:space:]]*//')
  PR_BODY=$(echo "$PR_CONTENT" | sed -n '/^BODY:/,$ p' | tail -n +2)

  # Error if title is empty
  if [ -z "$PR_TITLE" ]; then
    echo "Error: Failed to extract PR title from Claude response"
    echo "Claude output:"
    echo "$PR_CONTENT"
    return 1
  fi

  echo "----------------------------------------"
  echo "πŸ“ Generated PR Content:"
  echo "Title: $PR_TITLE"
  echo ""
  echo "Body:"
  echo "$PR_BODY"
  echo "----------------------------------------"

  # Create PR
  echo ""
  read "?Create PR? (y/N): " CONFIRM
  if [ "$CONFIRM" = "y" ] || [ "$CONFIRM" = "Y" ]; then
    echo "πŸš€ Creating PR..."
    PR_URL=$(gh pr create --base "$BASE_BRANCH" --title "$PR_TITLE" --body "$PR_BODY" 2>&1 | grep -o 'https://github.com/[^[:space:]]*')

    if [ -n "$PR_URL" ]; then
      echo ""
      echo "βœ… PR created successfully!"
      echo "Opening PR in browser..."
      open "$PR_URL"
    else
      echo ""
      echo "βœ… PR created successfully!"
      gh pr view --web
    fi
  else
    echo "PR creation cancelled"
  fi
}
Enter fullscreen mode Exit fullscreen mode

Key Points

1. Remote Branch Check

Before calling Claude API, it checks if the remote branch exists. This prevents wasting API costs and time.

if ! git ls-remote --exit-code --heads origin "$CURRENT_BRANCH" >/dev/null 2>&1; then
  echo "❌ Error: Remote branch 'origin/$CURRENT_BRANCH' does not exist"
  echo "Please push your branch to remote first:"
  echo "  git push -u origin $CURRENT_BRANCH"
  return 1
fi
Enter fullscreen mode Exit fullscreen mode

2. Language Switching Based on Account Type

Generates PR body in English for personal accounts and Japanese for work accounts.

if [ "$GITHUB_ACCOUNT_TYPE" = "personal" ]; then
  BODY_LANG="English"
else
  BODY_LANG="Japanese"
fi
Enter fullscreen mode Exit fullscreen mode

3. Prompt for Claude

Specifying a clear format for Claude ensures parseable output.

PR_CONTENT=$(claude -p "Generate a Pull Request title and description from the following git diff.

Please output in the following format only (do not output any other text):

TITLE: [Concise title (English, within 50 characters)]

BODY:
[Description of changes (in ${BODY_LANG} in detail)]
- Change 1
- Change 2
...

Below is the git diff:
$DIFF
")
Enter fullscreen mode Exit fullscreen mode

Usage

Basic Usage

# Commit changes on feature branch
git add .
git commit -m "Add new feature"

# Push to remote
git push -u origin feature-branch

# Auto-create PR
git-pr-auto
Enter fullscreen mode Exit fullscreen mode

The execution flow:

  1. Automatic account configuration
  2. GitHub CLI authentication check
  3. Base branch detection
  4. Diff verification and display
  5. Remote branch check
  6. PR content generation with Claude API
  7. Review generated content
  8. Confirm PR creation
  9. Create PR and open in browser

Example Execution

$ git-pr-auto

πŸ“Š Checking diff between feature-branch and main...
----------------------------------------
Files changed:
 functions/git.sh | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)
----------------------------------------

πŸ€– Analyzing diff with Claude...
----------------------------------------
πŸ“ Generated PR Content:
Title: Add remote branch check before PR creation

Body:
Added remote branch existence check for more robust PR creation flow.

Changes:
- Check remote branch existence before Claude API call
- Display appropriate error message and push command when branch doesn't exist
- Reduce API cost and time waste
----------------------------------------

Create PR? (y/N): y
πŸš€ Creating PR...

βœ… PR created successfully!
Opening PR in browser...
Enter fullscreen mode Exit fullscreen mode

Automatic GitHub Account Switching

This function calls another function called git-auth-setup to automatically configure accounts.

git-auth-setup() {
  # Get remote URL
  REMOTE_URL=$(git remote get-url origin 2>/dev/null)

  if [ -z "$REMOTE_URL" ]; then
    echo "Error: Not a git repository or no remote configured"
    return 1
  fi

  # Determine if personal or work account
  if echo "$REMOTE_URL" | grep -qE "$GITHUB_PERSONAL_PATTERN"; then
    # Personal account settings
    export GITHUB_ACCOUNT_TYPE="personal"
    unset GH_HOST
    git config user.name "Your Name"
    git config user.email "personal@example.com"
  else
    # Work account settings
    export GITHUB_ACCOUNT_TYPE="work"
    export GH_HOST="$GITHUB_WORK_HOST"
    git config user.name "Your Work Name"
    git config user.email "$GITHUB_WORK_USER"
  fi
}
Enter fullscreen mode Exit fullscreen mode

This mechanism automatically switches to the appropriate account settings based on the repository URL.

Troubleshooting

Claude CLI Not Installed

command not found: claude
Enter fullscreen mode Exit fullscreen mode

Install Claude CLI:

brew install anthropic/claude/claude
claude auth
Enter fullscreen mode Exit fullscreen mode

GitHub CLI Not Authenticated

Error: GitHub CLI is not authenticated
Please run: gh auth login
Enter fullscreen mode Exit fullscreen mode

Authenticate with the displayed command:

# Personal account
gh auth login

# Work account
GH_HOST=github.example.com gh auth login
Enter fullscreen mode Exit fullscreen mode

Remote Branch Doesn't Exist

❌ Error: Remote branch 'origin/feature-branch' does not exist
Please push your branch to remote first:
  git push -u origin feature-branch
Enter fullscreen mode Exit fullscreen mode

Push with the displayed command:

git push -u origin feature-branch
Enter fullscreen mode Exit fullscreen mode

Empty Claude Response

Error: Claude returned empty response
Enter fullscreen mode Exit fullscreen mode

Possible causes:

  • Claude API authentication expired β†’ Re-authenticate with claude auth
  • Diff is too large β†’ Split commits to make them smaller
  • Network issues β†’ Check connection

Can't Extract PR Title

Error: Failed to extract PR title from Claude response
Enter fullscreen mode Exit fullscreen mode

This occurs when Claude's output format differs from expectations. Check the debug output and adjust the prompt if needed.

Further Improvements

While the current implementation is practical, there's room for improvement:

Draft PR Support

gh pr create --draft --base "$BASE_BRANCH" --title "$PR_TITLE" --body "$PR_BODY"
Enter fullscreen mode Exit fullscreen mode

Add an option to create as a draft PR.

Auto-assign Reviewers

gh pr create --base "$BASE_BRANCH" --title "$PR_TITLE" --body "$PR_BODY" \
  --reviewer "reviewer1,reviewer2"
Enter fullscreen mode Exit fullscreen mode

Automatically assign team members as reviewers.

Auto-label

gh pr create --base "$BASE_BRANCH" --title "$PR_TITLE" --body "$PR_BODY" \
  --label "enhancement,needs-review"
Enter fullscreen mode Exit fullscreen mode

Automatically add labels based on the changes.

Template Support

Read GitHub's PR template and pass it to Claude to generate PRs that follow the template.

Summary

git-pr-auto makes PR creation dramatically easier. It's particularly effective in these scenarios:

  1. Managing multiple GitHub accounts
  2. Finding PR descriptions tedious to write
  3. Spending too much time organizing and summarizing changes
  4. Wanting to automate the PR creation process

Claude API's accuracy is high, generating content that's usable as-is in most cases. Occasional adjustments are needed, but it's much faster than writing from scratch.

Since using this function, time spent on PR creation has significantly decreased. Give it a try if you face similar challenges.

References

Top comments (0)