Loading...
Loading...
Creates GitHub Pull Requests with existing PR detection, branch pushing, and intelligent title/body generation. Use when user requests to create pull request, open PR, update PR, push for review, ready for review, send for review, get this reviewed, make a PR, share code, request review, create draft PR, submit for review, run /create-pr command, or mentions "PR", "pull request", "merge request", "code review", "GitHub PR", or "draft".
npx skill4agent add joaquimscosta/arkhe-claude-plugins creating-pr/create-pr/git:create-pr<scope>--draft--base <branch><scope> --draftroot --base staginggh# Install GitHub CLI (if not installed)
# macOS: brew install gh
# Linux: See https://cli.github.com/
# Authenticate
gh auth login# Parse arguments
SCOPE=""
DRAFT_FLAG=""
BASE_BRANCH="main"
# Example parsing:
# "root --draft" → SCOPE="root", DRAFT_FLAG="--draft", BASE_BRANCH="main"
# "--base staging" → SCOPE="", DRAFT_FLAG="", BASE_BRANCH="staging"
# "my-service --draft --base develop" → SCOPE="my-service", DRAFT_FLAG="--draft", BASE_BRANCH="develop"# Find monorepo root (handles submodules)
if SUPERPROJECT=$(git rev-parse --show-superproject-working-tree 2>/dev/null) && [ -n "$SUPERPROJECT" ]; then
# We're in a submodule
MONOREPO_ROOT="$SUPERPROJECT"
else
# We're in root or standalone repo
MONOREPO_ROOT=$(git rev-parse --show-toplevel)
fi
# Get current working directory
CURRENT_DIR=$(pwd)
# Determine repository context
if [ -n "$SCOPE" ]; then
# User specified scope
if [ "$SCOPE" = "root" ]; then
REPO_PATH="$MONOREPO_ROOT"
else
REPO_PATH="$MONOREPO_ROOT/$SCOPE"
fi
else
# Auto-detect from current directory
# If in submodule, use submodule path
# If in root, use root path
if [[ "$CURRENT_DIR" == "$MONOREPO_ROOT" ]]; then
REPO_PATH="$MONOREPO_ROOT"
else
# Find which submodule we're in
REPO_PATH=$(git -C "$CURRENT_DIR" rev-parse --show-toplevel)
fi
fi
# Validate repository
if [ ! -d "$REPO_PATH/.git" ]; then
echo "❌ Error: Not a valid git repository: $REPO_PATH" >&2
exit 1
fi# Change to repository directory
cd "$REPO_PATH"
# Get current branch
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
# Check if on protected branch
if [ "$CURRENT_BRANCH" = "main" ] || [ "$CURRENT_BRANCH" = "master" ]; then
echo "❌ Cannot create PR from protected branch: $CURRENT_BRANCH" >&2
echo "" >&2
echo "Please create a feature branch first:" >&2
echo " git checkout -b feature/your-feature-name" >&2
echo "" >&2
exit 1
fi
# Check for uncommitted changes
UNCOMMITTED=$(git status --porcelain)
if [ -n "$UNCOMMITTED" ]; then
echo "❌ Error: You have uncommitted changes" >&2
echo "" >&2
echo "Please commit or stash your changes before creating a PR:" >&2
git status --short
echo "" >&2
exit 1
fi
# Check if branch has commits ahead of base
COMMITS_AHEAD=$(git rev-list --count "$BASE_BRANCH..HEAD" 2>/dev/null || echo "0")
if [ "$COMMITS_AHEAD" = "0" ]; then
echo "❌ Error: No commits to create PR from" >&2
echo "Current branch '$CURRENT_BRANCH' has no commits ahead of '$BASE_BRANCH'" >&2
exit 1
fi
echo "ℹ️ Branch: $CURRENT_BRANCH ($COMMITS_AHEAD commits ahead of $BASE_BRANCH)"# Check if branch exists on remote
REMOTE_BRANCH=$(git ls-remote --heads origin "$CURRENT_BRANCH" 2>/dev/null)
if [ -z "$REMOTE_BRANCH" ]; then
echo "ℹ️ Branch not on remote, pushing..."
git push -u origin "$CURRENT_BRANCH" || {
echo "❌ Failed to push branch to remote" >&2
exit 1
}
echo "✅ Branch pushed to origin/$CURRENT_BRANCH"
else
# Check if local is behind remote
LOCAL_HASH=$(git rev-parse HEAD)
REMOTE_HASH=$(git rev-parse "origin/$CURRENT_BRANCH" 2>/dev/null || echo "")
if [ "$LOCAL_HASH" != "$REMOTE_HASH" ]; then
echo "ℹ️ Local branch differs from remote, pushing..."
git push origin "$CURRENT_BRANCH" || {
echo "❌ Failed to push branch to remote" >&2
exit 1
}
echo "✅ Branch updated on remote"
else
echo "ℹ️ Branch already up to date on remote"
fi
fi# Check for existing PR using GitHub CLI
EXISTING_PR=$(gh pr view "$CURRENT_BRANCH" --json number,title,url 2>/dev/null || echo "")
if [ -n "$EXISTING_PR" ]; then
# PR exists - extract info
PR_NUMBER=$(echo "$EXISTING_PR" | jq -r '.number')
PR_TITLE=$(echo "$EXISTING_PR" | jq -r '.title')
PR_URL=$(echo "$EXISTING_PR" | jq -r '.url')
echo ""
echo "ℹ️ Existing PR found:"
echo " #$PR_NUMBER: $PR_TITLE"
echo " $PR_URL"
echo ""
# Use AskUserQuestion to ask user what to do
# Options:
# 1. Update PR (regenerate title/body and update)
# 2. View PR in browser (open URL)
# 3. Cancel (do nothing)
# If user chooses "Update PR":
# - Generate new title and body (Steps 6-7)
# - Update PR using: gh pr edit "$PR_NUMBER" --title "..." --body "..."
# - Show success message
# If user chooses "View PR":
# - Open PR URL in browser: gh pr view --web
# If user chooses "Cancel":
# - Exit gracefully
fi# Get all commits from base branch to current branch
COMMITS=$(git log "$BASE_BRANCH..HEAD" --oneline)
COMMIT_COUNT=$(echo "$COMMITS" | wc -l | tr -d ' ')
echo "ℹ️ Analyzing $COMMIT_COUNT commits..."
# Get the first commit message (most recent)
LATEST_COMMIT=$(git log -1 --pretty=%B)
# Detect commit type from latest commit or analyze all commits
# Try to extract type from conventional commit format
if echo "$LATEST_COMMIT" | grep -qE '^(feat|fix|docs|refactor|test|chore|perf|ci|build):'; then
# Extract type and description from conventional commit
COMMIT_TYPE=$(echo "$LATEST_COMMIT" | grep -oE '^[a-z]+' | head -1)
COMMIT_DESC=$(echo "$LATEST_COMMIT" | sed -E 's/^[a-z]+(\([^)]+\))?:\s*//')
else
# Analyze changes to determine type
ALL_COMMITS=$(git log "$BASE_BRANCH..HEAD" --pretty=%B)
if echo "$ALL_COMMITS" | grep -qi "fix\|bug"; then
COMMIT_TYPE="fix"
elif echo "$ALL_COMMITS" | grep -qi "feat\|feature\|add"; then
COMMIT_TYPE="feat"
elif echo "$ALL_COMMITS" | grep -qi "docs\|documentation"; then
COMMIT_TYPE="docs"
elif echo "$ALL_COMMITS" | grep -qi "refactor"; then
COMMIT_TYPE="refactor"
elif echo "$ALL_COMMITS" | grep -qi "test"; then
COMMIT_TYPE="test"
else
COMMIT_TYPE="feat"
fi
# Generate description from branch name or commit
COMMIT_DESC=$(echo "$LATEST_COMMIT" | head -1 | sed 's/^\s*//')
fi
# Generate PR title (capitalize first letter)
PR_TITLE="$COMMIT_TYPE: $COMMIT_DESC"
echo "ℹ️ Generated PR title: $PR_TITLE"# Generate PR body with summary from commits
PR_BODY="## Summary
"
# Add commit summaries
if [ "$COMMIT_COUNT" -eq 1 ]; then
# Single commit - use full message
PR_BODY+="$LATEST_COMMIT
"
else
# Multiple commits - list them
PR_BODY+="This PR includes $COMMIT_COUNT commits:
"
while IFS= read -r commit; do
PR_BODY+="- $commit
"
done <<< "$COMMITS"
PR_BODY+="
"
fi
# Add test plan section
PR_BODY+="## Test Plan
- [ ] Code builds successfully
- [ ] Tests pass
- [ ] Manual testing completed
- [ ] Documentation updated (if needed)
## Changes
"
# List changed files
CHANGED_FILES=$(git diff --name-only "$BASE_BRANCH..HEAD")
while IFS= read -r file; do
PR_BODY+="- \`$file\`
"
done <<< "$CHANGED_FILES"
echo ""
echo "Generated PR description:"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "$PR_BODY"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""# IMPORTANT: No Claude Code attribution in PR content
# Build gh pr create command
GH_CMD="gh pr create --title \"$PR_TITLE\" --body \"$PR_BODY\" --base \"$BASE_BRANCH\""
# Add draft flag if specified
if [ "$DRAFT_FLAG" = "--draft" ]; then
GH_CMD+=" --draft"
fi
# Execute PR creation
eval "$GH_CMD" || {
echo "❌ Failed to create PR" >&2
exit 1
}
# Get PR URL
PR_URL=$(gh pr view --json url -q '.url')
echo ""
echo "✅ Pull Request created successfully!"
echo ""
echo "PR URL: $PR_URL"
echo ""
echo "Next steps:"
echo " - Review PR in browser: gh pr view --web"
echo " - Check CI status: gh pr checks"
echo " - Request reviews: gh pr ready (if draft)"
echo ""# Update PR title and body
gh pr edit "$PR_NUMBER" --title "$PR_TITLE" --body "$PR_BODY" || {
echo "❌ Failed to update PR" >&2
exit 1
}
echo "✅ Pull Request #$PR_NUMBER updated successfully!"
echo "PR URL: $PR_URL"| Detected Pattern | Type | Example Title |
|---|---|---|
| "feat:" in commits | | feat: add user authentication |
| "fix:" in commits | | fix: resolve login timeout issue |
| "docs:" in commits | | docs: update API documentation |
| "refactor:" in commits | | refactor: optimize database queries |
| "test:" in commits | | test: add integration tests |
| Mentions "bug", "fix" | | fix: correct calculation error |
| Mentions "feature", "add" | | feat: implement new feature |
| Default | | feat: [description from commits] |
ghgh auth logingh# Check for existing PR
gh pr view <branch> --json number,title,url
# Create new PR
gh pr create --title "..." --body "..." --base <branch> [--draft]
# Update existing PR
gh pr edit <number> --title "..." --body "..."
# View PR in browser
gh pr view --web
# Check PR status
gh pr checks