1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
#! /bin/zsh
Red="\033[0;31m"
Green='\033[0;32m'
Color_Off='\033[0m'
# Ensure ssh-agent is running for git operations
. ensure-ssh-agent > /dev/null 2>&1
create_pr=false
target_branch=""
# Parse arguments
if [ "$1" = "-push" ]; then
create_pr=true
shift
target_branch="${1:-main}"
fi
# Get current branch name
current_branch=$(git branch 2>/dev/null | grep \* | awk -F '\\* ' '{$0=$2}1')
if [ -z "$current_branch" ]; then
echo -e "${Red}Error: Not in a git repository${Color_Off}"
exit 1
fi
# Fetch latest main branch
echo -e "${Green}Fetching latest main branch...${Color_Off}"
git fetch origin main --quiet 2>/dev/null || git fetch origin master --quiet 2>/dev/null
# Determine main branch name (main or master) - use for diff generation
main_branch="main"
if ! git rev-parse --verify origin/main >/dev/null 2>&1; then
if git rev-parse --verify origin/master >/dev/null 2>&1; then
main_branch="master"
else
echo -e "${Red}Error: Could not find main or master branch${Color_Off}"
exit 1
fi
fi
# If -push was specified but no target given, use the detected main branch
if [ "$create_pr" = true ] && [ -z "$target_branch" ]; then
target_branch="$main_branch"
fi
# Get diff between current branch and main
echo -e "${Green}Generating PR title and description...${Color_Off}"
diff_output=$(git diff origin/${main_branch}..HEAD --diff-algorithm=minimal 2>/dev/null)
if [ -z "$diff_output" ]; then
echo -e "${Red}Error: No differences found between ${current_branch} and ${main_branch}${Color_Off}"
exit 1
fi
# Get commit log for context
commit_log=$(git log --oneline origin/${main_branch}..HEAD 2>/dev/null)
# Generate PR title and description using Claude
pr_content=$(echo -e "$commit_log\n\n---\n\n$diff_output" | claude -p "You are helping write a GitHub pull request. Based on the commit messages and code diff below, generate a title and description.
Title requirements:
- Short and descriptive (under 60 chars)
- Natural, conversational tone
- Describe what changed, not how
- No prefixes like 'feat:' or 'fix:' unless that's the actual convention used in the commits
Description requirements:
- Write like a developer explaining their work to a teammate
- Start with a brief 1-2 sentence summary of what this PR does
- List the key changes in bullet points
- Be specific about what actually changed
- NO corporate-speak, NO AI phrases like 'This PR introduces', 'enhances', 'leverages', 'streamlines'
- Keep it clear and direct
- Don't explain obvious things
Format your response exactly as:
TITLE: <your title here>
DESCRIPTION:
<your description here>
Now analyze these changes:" 2>/dev/null)
CLAUDE_EXIT=$?
if [ -z "$pr_content" ] || [ $CLAUDE_EXIT -ne 0 ]; then
echo -e "${Red}Claude generation failed, using commit log${Color_Off}"
# Extract first 2-3 words from the most recent commit message as title
fallback_title=$(echo "$commit_log" | head -n 1 | sed 's/^[a-f0-9]* //' | awk '{print $1, $2}')
pr_content="TITLE: ${fallback_title}\n\nDESCRIPTION:\n## Changes\n$(echo "$commit_log" | head -n 10 | sed 's/^/- /')"
fi
# Extract title and description
pr_title=$(echo "$pr_content" | grep -A 1 "^TITLE:" | tail -n 1 | sed 's/^[[:space:]]*//')
pr_description=$(echo "$pr_content" | sed -n '/^DESCRIPTION:/,$p' | sed '1d' | sed 's/^[[:space:]]*//')
# Fallback if extraction failed
if [ -z "$pr_title" ]; then
# Use first 2 words from latest commit message
pr_title=$(echo "$commit_log" | head -n 1 | sed 's/^[a-f0-9]* //' | awk '{print $1, $2}')
fi
if [ -z "$pr_description" ]; then
pr_description="## Changes\n$(echo "$commit_log" | sed 's/^/- /')"
fi
# If -push was specified, create or update the PR
if [ "$create_pr" = true ]; then
# Check if PR already exists for current branch
echo -e "${Green}Checking for existing PR...${Color_Off}"
existing_pr=$(gh pr list --head "$current_branch" --json number --jq '.[0].number' 2>/dev/null)
if [ -n "$existing_pr" ]; then
# Update existing PR
echo -e "${Green}Updating existing PR #${existing_pr}...${Color_Off}"
# Create temp file for description
desc_file=$(mktemp)
echo "$pr_description" > "$desc_file"
gh pr edit "$existing_pr" --title "$pr_title" --body-file "$desc_file"
edit_exit=$?
rm -f "$desc_file"
if [ $edit_exit -eq 0 ]; then
echo -e "${Green}Successfully updated PR #${existing_pr}${Color_Off}"
gh pr view "$existing_pr" --web
else
echo -e "${Red}Failed to update PR${Color_Off}"
exit 1
fi
else
# Create new PR
echo -e "${Green}Creating new PR targeting ${target_branch}...${Color_Off}"
# Push current branch to remote
git push -u origin "$current_branch" 2>&1
if [ $? -ne 0 ]; then
echo -e "${Red}Failed to push branch${Color_Off}"
exit 1
fi
# Create temp file for description
desc_file=$(mktemp)
echo "$pr_description" > "$desc_file"
gh pr create --base "$target_branch" --title "$pr_title" --body-file "$desc_file" --web
create_exit=$?
rm -f "$desc_file"
if [ $create_exit -ne 0 ]; then
echo -e "${Red}Failed to create PR${Color_Off}"
exit 1
fi
fi
else
# Output in copyable format
echo -e "\n${Green}=== PR Title ===${Color_Off}"
echo "$pr_title"
echo -e "\n${Green}=== PR Description ===${Color_Off}"
echo "$pr_description"
echo ""
fi
|