Skip to content

Commit 43baed2

Browse files
authored
Merge pull request #3 from j2nullify/feature/add-ai-pr
feat(pr): Add AI-powered PR review and label generation
2 parents 3a6d820 + 2360ef6 commit 43baed2

File tree

4 files changed

+153
-5
lines changed

4 files changed

+153
-5
lines changed

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,20 @@ To generate automated commit messages:
2222
# Add your files like normal
2323
git add -u
2424
# Ai-generate commit messages
25-
how-commit
25+
ai-commit
2626
```
2727
![image](./images/image.png)
2828

2929

3030
Then, you can push your changes and use AI to auto-generate you a description
3131

32+
```bash
33+
ai-pr
3234
```
33-
how-pr
35+
36+
To then review:
37+
```bash
38+
ai-review
3439
```
3540

3641
See example PR: https://github.com/j2nullify/ai-git-commit/pull/2
37-

app/make_pull_request.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,39 @@ def main():
3838
prompt_body = f"Generate a concise and informative pull request description based on the following diff. Include key changes and their impact:\n\n{pr_diff}"
3939
pr_description = query_bedrock(prompt_body)
4040
pr_description = extract_bash_commands_no_line_split(pr_description)[0]
41+
prompt_label = f"""Based on the following diff, suggest the most appropriate label for this pull request based on SemVer2.
42+
Choose only one of: "minor", "patch", or "major". Respond with only the label.
43+
44+
<example>minor</example>
45+
<example>patch</example>
46+
<example>major</example>
47+
Diff:
48+
{pr_diff}
49+
"""
50+
pr_label = query_bedrock(prompt_label).strip()
51+
52+
for labels in pr_label:
53+
if "minor" in labels:
54+
pr_label = "minor"
55+
elif "patch" in labels:
56+
pr_label = "patch"
57+
elif "major" in labels:
58+
pr_label = "major"
59+
60+
# Validate the label
61+
if pr_label not in ["minor", "patch", "major"]:
62+
print(f"Warning: Invalid label '{pr_label}' generated. Defaulting to 'minor'.")
63+
pr_label = "minor"
64+
65+
# Create pull request using GitHub CLI with label
66+
result = subprocess.run([
67+
"gh", "pr", "create", "--title", pr_title, "--body", pr_description,
68+
"--label", pr_label])
69+
if result.returncode != 0:
70+
result = subprocess.run([
71+
"gh", "pr", "edit", "--title", pr_title, "--body", pr_description,
72+
"--label", pr_label])
73+
4174

4275
# Create pull request using GitHub CLI
4376
# Check if GitHub CLI (gh) is installed

app/pull_request_review.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
"""Make a pull request
2+
"""
3+
import subprocess
4+
from app.query_bedrock import query_bedrock
5+
from app.extract_bash import extract_bash_commands_no_line_split
6+
7+
def get_current_branch():
8+
return subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]).decode().strip()
9+
10+
def get_pr_number():
11+
try:
12+
pr_info = subprocess.check_output(["gh", "pr", "view", "--json", "number"]).decode()
13+
return int(pr_info.split(":")[1].strip()[:-1])
14+
except subprocess.CalledProcessError:
15+
return None
16+
17+
def get_pr_diff(pr_number):
18+
try:
19+
diff = subprocess.check_output(["gh", "pr", "diff", str(pr_number)]).decode()
20+
return diff
21+
except subprocess.CalledProcessError:
22+
print(f"Error: Unable to get the diff for PR #{pr_number}.")
23+
return None
24+
25+
def review_pull_request(pr_number):
26+
pr_diff = get_pr_diff(pr_number)
27+
if not pr_diff:
28+
return
29+
prompt = f"""Review the following pull request diff and provide a concise review comment.
30+
Focus on detecting any potential issues, bugs, or security vulnerabilities.
31+
Include suggestions for improvement, and an overall assessment of code quality and functionality.
32+
If you have any specific code change suggestions to address these concerns, please include them as well.
33+
34+
Additionally, highlight any unintended consequences or potential side effects that the developer may not have realized.
35+
Consider suggesting better approaches based on established frameworks or design patterns.
36+
37+
Analyze the changes in the context of:
38+
1. Code maintainability and readability
39+
2. Performance implications
40+
3. Scalability considerations
41+
4. Adherence to best practices and coding standards
42+
5. Potential impact on other parts of the system
43+
44+
If applicable, recommend using specific libraries, tools, or frameworks that could improve the implementation.
45+
46+
{pr_diff}
47+
48+
Respond with only the review comment, enclosed in triple backticks (```).
49+
50+
For example:
51+
52+
```
53+
The changes look good overall. Consider adding more unit tests for the new functionality.
54+
55+
Suggestion for improvement:
56+
In file.py, line 42, consider changing:
57+
if x == None:
58+
to:
59+
if x is None:
60+
61+
This is more idiomatic Python and avoids potential issues with custom __eq__ methods.
62+
```
63+
"""
64+
65+
review_comment = query_bedrock(prompt)
66+
review_comment = extract_bash_commands_no_line_split(review_comment)[0]
67+
68+
try:
69+
result = subprocess.run([
70+
"gh", "pr", "review", str(pr_number),
71+
"--body", review_comment,
72+
"--comment"
73+
])
74+
75+
if result.returncode == 0:
76+
print(f"✅ Review submitted successfully for PR #{pr_number}!")
77+
else:
78+
print(f"❌ Failed to submit review for PR #{pr_number}. Please try again.")
79+
except subprocess.CalledProcessError:
80+
print(f"Error: Unable to submit review for PR #{pr_number}.")
81+
82+
def main():
83+
# Check if GitHub CLI (gh) is installed
84+
try:
85+
subprocess.run(["gh", "--version"], check=True, capture_output=True)
86+
except (subprocess.CalledProcessError, FileNotFoundError):
87+
print("GitHub CLI (gh) is not installed or not found. Please install it to review pull requests.")
88+
print("Visit https://cli.github.com/ for installation instructions.")
89+
return
90+
91+
current_branch = get_current_branch()
92+
pr_number = get_pr_number()
93+
94+
if pr_number is None:
95+
print(f"No pull request found for the current branch '{current_branch}'.")
96+
create_pr = input("Would you like to create a pull request? (y/n): ").lower()
97+
if create_pr == 'y':
98+
subprocess.run(["python", "app/make_pull_request.py"])
99+
pr_number = get_pr_number()
100+
else:
101+
print("Exiting without creating a pull request.")
102+
return
103+
104+
if pr_number:
105+
review_pull_request(pr_number)
106+
else:
107+
print("Unable to find or create a pull request. Please check your branch and try again.")
108+
109+
if __name__ == "__main__":
110+
main()

setup.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@
3232
entry_points={
3333
"console_scripts": [
3434
"how=app.cli:main",
35-
"how-commit=app.commit:main",
36-
"how-pr=app.make_pull_request:main",
35+
"ai-commit=app.commit:main",
36+
"ai-pr=app.make_pull_request:main",
37+
"ai-review=app.pull_request_review:main",
3738
],
3839
},
3940
)

0 commit comments

Comments
 (0)