Skip to content

Commit 8780a67

Browse files
authored
Update "get comments" skill to also fetch code comments (#3499)
* Move fetching PR comments to a skill * Update agent skills and guidelines * Add missing copyright headers * Update fetch_pr_comments skill to support code comments and improve formatting Also fixed a bug in push_to_github skill where it referenced a non-existent gradle task. * chore: fix exit code in fetch_comments.py * chore: update push skill to run checkCode instead of lintLocalDebug * chore: fix push skill to use root checkCode task
1 parent 0a8d0d6 commit 8780a67

File tree

3 files changed

+94
-32
lines changed

3 files changed

+94
-32
lines changed

.agent/skills/fetch_pr_comments/SKILL.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description: Fetches comments and reviews from the current GitHub Pull Request a
55

66
# Fetch PR Comments
77

8-
This skill allows you to retrieve comments and reviews from the current GitHub Pull Request (PR) associated with the active branch. It converts the data into a readable Markdown format, making it easier to analyze feedback.
8+
This skill allows you to retrieve comments and reviews from the current GitHub Pull Request (PR) associated with the active branch, or a specific PR by number/URL. It converts the data into a readable Markdown format, including code review comments with file and line context.
99

1010
## Usage
1111

@@ -14,16 +14,20 @@ To use this skill, run the provided Python script. It requires the GitHub CLI (`
1414
### Command
1515

1616
```bash
17+
# Fetch comments for the current branch's PR
1718
python3 .agent/skills/fetch_pr_comments/scripts/fetch_comments.py
19+
20+
# Fetch comments for a specific PR
21+
python3 .agent/skills/fetch_pr_comments/scripts/fetch_comments.py <PR_NUMBER_OR_URL>
1822
```
1923

2024
### Output
2125

2226
The script outputs Markdown text to stdout, containing:
2327
- PR Title and URL
24-
- Threads with resolved/unresolved status
25-
- Individual comments with author, date, and body
26-
- Review statuses
28+
- Reviews (Approved/Changes Requested)
29+
- General Comments (Pull Request level)
30+
- Code Comments (Grouped by file and line number)
2731

2832
## Dependencies
2933

.agent/skills/fetch_pr_comments/scripts/fetch_comments.py

Lines changed: 85 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import json
1818
import subprocess
1919
import sys
20+
import re
2021
from datetime import datetime
2122

2223
def run_command(command):
@@ -30,12 +31,8 @@ def run_command(command):
3031
)
3132
return result.stdout.strip()
3233
except subprocess.CalledProcessError as e:
33-
# Check if it's the specific "no pull requests" error
34-
if "no pull requests found" in e.stderr:
34+
if "no pull requests found" in e.stderr.lower():
3535
print("No pull request found for the current branch.", file=sys.stderr)
36-
sys.exit(0) # Exit gracefully with 0 or 1? Standard practice is 1 if it failed to do what was asked.
37-
# But for a skill finding "nothing", maybe 0 is okay?
38-
# Let's stick to 1 but with a clean message.
3936
sys.exit(1)
4037

4138
print(f"Error running command: {command}", file=sys.stderr)
@@ -49,51 +46,112 @@ def format_date(iso_date):
4946
except Exception:
5047
return iso_date
5148

49+
50+
def get_repo_owner_name(pr_url):
51+
# Expected format: https://github.com/OWNER/REPO/pull/NUMBER
52+
match = re.search(r"github\.com/([^/]+)/([^/]+)/pull/\d+", pr_url)
53+
if match:
54+
return match.group(1), match.group(2)
55+
return None, None
56+
57+
def get_code_comments(owner, repo, pr_number):
58+
cmd = f"gh api repos/{owner}/{repo}/pulls/{pr_number}/comments --paginate"
59+
try:
60+
return json.loads(run_command(cmd))
61+
except Exception as e:
62+
print(f"Warning: Failed to fetch code comments: {e}", file=sys.stderr)
63+
return []
64+
5265
def main():
53-
# Fetch PR data including comments and reviews
54-
cmd = "gh pr view --json title,url,number,state,comments,reviews,latestReviews"
55-
56-
# We rely on run_command to handle the subprocess exit
57-
json_output = run_command(cmd)
66+
# Allow optional PR argument (number or URL)
67+
pr_arg = ""
68+
if len(sys.argv) > 1:
69+
pr_arg = f" {sys.argv[1]}"
70+
71+
cmd = f"gh pr view{pr_arg} --json number,title,url,state,comments,reviews,latestReviews"
5872

5973
try:
74+
json_output = run_command(cmd)
6075
pr_data = json.loads(json_output)
61-
except json.JSONDecodeError:
62-
print("Failed to decode JSON from gh output.", file=sys.stderr)
76+
except Exception:
77+
# If run_command fails or json decode fails, we exit (mostly handled in run_command)
6378
sys.exit(1)
79+
number = pr_data.get('number')
80+
title = pr_data.get('title')
81+
url = pr_data.get('url')
82+
state = pr_data.get('state')
83+
84+
owner, repo = get_repo_owner_name(url)
85+
code_comments = []
86+
if owner and repo:
87+
code_comments = get_code_comments(owner, repo, number)
6488

65-
print(f"# PR #{pr_data.get('number')}: {pr_data.get('title')}")
66-
print(f"**URL:** {pr_data.get('url')}")
67-
print(f"**State:** {pr_data.get('state')}\n")
89+
# --- Header ---
90+
print(f"# PR #{number}: {title}")
91+
print(f"**URL:** {url}")
92+
print(f"**State:** {state}\n")
6893

94+
# --- Reviews ---
6995
print("## Reviews")
7096
reviews = pr_data.get('latestReviews', [])
7197
if not reviews:
72-
print("No reviews found.")
98+
print("No official reviews found.")
7399
else:
74100
for review in reviews:
75-
state = review.get('state')
101+
review_state = review.get('state')
76102
author = review.get('author', {}).get('login', 'Unknown')
77103
date = format_date(review.get('submittedAt', ''))
78-
print(f"- **{author}**: {state} ({date})")
104+
print(f"- **{author}**: {review_state} ({date})")
79105
print("\n" + "-"*40 + "\n")
80106

81-
print("## Comments")
82-
83-
comments = pr_data.get('comments', [])
84-
if not comments:
85-
print("No comments found.")
107+
# --- General Comments ---
108+
print("## General Comments")
109+
general_comments = pr_data.get('comments', [])
110+
if not general_comments:
111+
print("No general comments.")
86112
else:
87-
for comment in comments:
113+
for comment in general_comments:
88114
author = comment.get('author', {}).get('login', 'Unknown')
89115
body = comment.get('body', '').strip()
90116
date = format_date(comment.get('createdAt', ''))
91-
url = comment.get('url', '')
117+
comment_url = comment.get('url', '')
92118

93119
print(f"### {author} at {date}")
94-
print(f"[Link]({url})\n")
120+
print(f"[Link]({comment_url})\n")
95121
print(body)
96-
print("\n" + "-"*20 + "\n")
122+
print("\n")
123+
print("-" * 40 + "\n")
124+
125+
# --- Code Comments ---
126+
print("## Code Comments")
127+
if not code_comments:
128+
print("No code comments.")
129+
else:
130+
# Group by file path
131+
comments_by_file = {}
132+
for cc in code_comments:
133+
path = cc.get('path', 'Unknown File')
134+
if path not in comments_by_file:
135+
comments_by_file[path] = []
136+
comments_by_file[path].append(cc)
137+
138+
for path, comments in comments_by_file.items():
139+
print(f"### File: `{path}`\n")
140+
# Sort by line number (or position if line is None)
141+
comments.sort(key=lambda x: (x.get('line') or x.get('original_line') or 0))
142+
143+
for cc in comments:
144+
author = cc.get('user', {}).get('login', 'Unknown')
145+
body = cc.get('body', '').strip()
146+
date = format_date(cc.get('created_at', ''))
147+
line = cc.get('line') or cc.get('original_line') or "Outdated"
148+
html_url = cc.get('html_url', '')
149+
150+
print(f"#### Line {line} - {author} ({date})")
151+
print(f"[Link]({html_url})\n")
152+
print(body)
153+
print("\n")
154+
print("-" * 20 + "\n")
97155

98156
if __name__ == "__main__":
99157
main()

.agent/skills/push_to_github/scripts/push.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def main():
3434

3535
# 2. Check Code (Lint/Analysis)
3636
print("\n2️⃣ Running Code Checks...")
37-
run_command("./gradlew :app:checkCode")
37+
run_command("./gradlew checkCode")
3838

3939
# 3. Push
4040
print("\n3️⃣ Pushing to GitHub...")

0 commit comments

Comments
 (0)