Skip to content

Commit 207d9db

Browse files
authored
Improve agentic coding skills (#3497)
* Move fetching PR comments to a skill * Update agent skills and guidelines * Add missing copyright headers
1 parent d00b1ac commit 207d9db

File tree

10 files changed

+318
-21
lines changed

10 files changed

+318
-21
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
name: Fetch PR Comments
3+
description: Fetches comments and reviews from the current GitHub Pull Request and formats them as Markdown.
4+
---
5+
6+
# Fetch PR Comments
7+
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.
9+
10+
## Usage
11+
12+
To use this skill, run the provided Python script. It requires the GitHub CLI (`gh`) to be installed and authenticated.
13+
14+
### Command
15+
16+
```bash
17+
python3 .agent/skills/fetch_pr_comments/scripts/fetch_comments.py
18+
```
19+
20+
### Output
21+
22+
The script outputs Markdown text to stdout, containing:
23+
- PR Title and URL
24+
- Threads with resolved/unresolved status
25+
- Individual comments with author, date, and body
26+
- Review statuses
27+
28+
## Dependencies
29+
30+
- `gh` (GitHub CLI) must be installed and in your PATH.
31+
- `python3` must be available.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#
2+
# Copyright 2026 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
import json
18+
import subprocess
19+
import sys
20+
from datetime import datetime
21+
22+
def run_command(command):
23+
try:
24+
result = subprocess.run(
25+
command,
26+
check=True,
27+
capture_output=True,
28+
text=True,
29+
shell=True
30+
)
31+
return result.stdout.strip()
32+
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:
35+
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.
39+
sys.exit(1)
40+
41+
print(f"Error running command: {command}", file=sys.stderr)
42+
print(f"Stderr: {e.stderr}", file=sys.stderr)
43+
sys.exit(1)
44+
45+
def format_date(iso_date):
46+
try:
47+
dt = datetime.fromisoformat(iso_date.replace("Z", "+00:00"))
48+
return dt.strftime("%Y-%m-%d %H:%M")
49+
except Exception:
50+
return iso_date
51+
52+
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)
58+
59+
try:
60+
pr_data = json.loads(json_output)
61+
except json.JSONDecodeError:
62+
print("Failed to decode JSON from gh output.", file=sys.stderr)
63+
sys.exit(1)
64+
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")
68+
69+
print("## Reviews")
70+
reviews = pr_data.get('latestReviews', [])
71+
if not reviews:
72+
print("No reviews found.")
73+
else:
74+
for review in reviews:
75+
state = review.get('state')
76+
author = review.get('author', {}).get('login', 'Unknown')
77+
date = format_date(review.get('submittedAt', ''))
78+
print(f"- **{author}**: {state} ({date})")
79+
print("\n" + "-"*40 + "\n")
80+
81+
print("## Comments")
82+
83+
comments = pr_data.get('comments', [])
84+
if not comments:
85+
print("No comments found.")
86+
else:
87+
for comment in comments:
88+
author = comment.get('author', {}).get('login', 'Unknown')
89+
body = comment.get('body', '').strip()
90+
date = format_date(comment.get('createdAt', ''))
91+
url = comment.get('url', '')
92+
93+
print(f"### {author} at {date}")
94+
print(f"[Link]({url})\n")
95+
print(body)
96+
print("\n" + "-"*20 + "\n")
97+
98+
if __name__ == "__main__":
99+
main()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
name: Prepare Commit
3+
description: Prepares the codebase for a commit by formatting code and helping identify temporary comments.
4+
---
5+
6+
# Prepare Commit
7+
8+
Use this skill **before every commit** to ensure your code is formatted and clean.
9+
10+
## Usage
11+
12+
This skill performs the following:
13+
1. Runs standard formatting (`ktfmtFormat`).
14+
2. (Optional/Manual) Reminds you to check for temporary comments.
15+
16+
### Command
17+
18+
```bash
19+
python3 .agent/skills/prepare_commit/scripts/prepare.py
20+
```
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#
2+
# Copyright 2026 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
import subprocess
18+
import sys
19+
20+
def run_command(command):
21+
print(f"Running: {command}")
22+
try:
23+
subprocess.run(command, check=True, shell=True)
24+
except subprocess.CalledProcessError:
25+
print(f"Error running {command}")
26+
sys.exit(1)
27+
28+
def main():
29+
print("🎨 Formatting code with ktfmt...")
30+
run_command("./gradlew ktfmtFormat")
31+
32+
print("\n✅ Formatting complete.")
33+
print("⚠️ REMINDER: Please check for any temporary comments or debug code before committing.")
34+
35+
if __name__ == "__main__":
36+
main()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
name: Push to GitHub
3+
description: Runs all necessary checks (lint, tests) and pushes to GitHub. Use this as the final safety gate.
4+
---
5+
6+
# Push to GitHub
7+
8+
Use this skill **when you are ready to push** your changes to the remote repository. It acts as a safety gate to prevent breaking CI.
9+
10+
## Usage
11+
12+
1. Verifies changes (Unit Tests).
13+
2. Runs code checks (Lint/CheckCode).
14+
3. Pushes to the current branch.
15+
16+
### Command
17+
18+
```bash
19+
python3 .agent/skills/push_to_github/scripts/push.py
20+
```
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#
2+
# Copyright 2026 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
import subprocess
18+
import sys
19+
20+
def run_command(command):
21+
print(f"Running: {command}")
22+
try:
23+
subprocess.run(command, check=True, shell=True)
24+
except subprocess.CalledProcessError:
25+
print(f"❌ Command failed: {command}")
26+
sys.exit(1)
27+
28+
def main():
29+
print("🚧 Starting Pre-Push Checks...")
30+
31+
# 1. Verify (Tests)
32+
print("\n1️⃣ Running Unit Tests...")
33+
run_command("./gradlew :app:testLocalDebugUnitTest")
34+
35+
# 2. Check Code (Lint/Analysis)
36+
print("\n2️⃣ Running Code Checks...")
37+
run_command("./gradlew :app:checkCode")
38+
39+
# 3. Push
40+
print("\n3️⃣ Pushing to GitHub...")
41+
run_command("git push")
42+
43+
print("\n✅ Successfully pushed to GitHub!")
44+
45+
if __name__ == "__main__":
46+
main()
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
name: Verify Changes
3+
description: Runs unit tests to quickly verify changes during the development loop.
4+
---
5+
6+
# Verify Changes
7+
8+
Use this skill **after each meaningful change** to verify you haven't broken existing functionality.
9+
10+
## Usage
11+
12+
Runs the local unit tests for the debug variant.
13+
14+
### Command
15+
16+
```bash
17+
python3 .agent/skills/verify_changes/scripts/verify.py
18+
```
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#
2+
# Copyright 2026 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
import subprocess
18+
import sys
19+
20+
def run_command(command):
21+
print(f"Running: {command}")
22+
try:
23+
subprocess.run(command, check=True, shell=True)
24+
except subprocess.CalledProcessError:
25+
print(f"Error running {command}")
26+
sys.exit(1)
27+
28+
def main():
29+
print("🧪 Running local unit tests...")
30+
run_command("./gradlew :app:testLocalDebugUnitTest")
31+
print("\n✅ Verified.")
32+
33+
if __name__ == "__main__":
34+
main()

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
**/google-services.json
33
secrets.properties
44
!app/src/debug/local/google-services.json
5+
!.agent
56
!.gitignore
67
!gradle/
78

@@ -50,3 +51,4 @@ captures/
5051

5152
# Ctags
5253
**/tags
54+
GEMINI.md

GEMINI.md

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,15 @@
22

33
These rules apply to all interactions in this project.
44

5-
1. Limit File Access: Restrict file system read/write operations ONLY to files explicitly provided or mentioned in the current request. ABSOLUTELY DO NOT access files in other directories (e.g., /etc, ~, /usr).
6-
2. Confirm Dangerous Commands: If the intended command is potentially destructive (e.g., rm, mv, sudo, systemctl), you MUST explicitly preface the command proposal with a warning: 'WARNING: POTENTIALLY DESTRUCTIVE ACTION REQUIRED.'
7-
3. Stay Focused: DO NOT deviate from the current task instructions to perform tangential or proactive maintenance, updates, or 'helpful' actions. Only address the explicit request.
8-
9-
When making changes to the code:
10-
11-
1. Ensure all new files have the standard copyright header with the current year.
12-
2. Add unit tests of all new or changed behaviors and remove obsolete unit tests for removed behaviors.
13-
3. Run and fix all unit tests with `./gradlew :app:testLocalDebugUnitTest`.
14-
15-
Before pushing changes:
16-
17-
1. Remove any commented out code or temporary comments added in the process of debugging and authoring new or changed code.
18-
2. Run `./gradlew ktfmtFormat` to fix lint errors.
19-
3. Run checks with `./gradlew :app:checkCode` and fix all errors and warnings.
20-
21-
When asked to resolve pending comments:
22-
23-
1. Get the current PR number using `gh pr view $(git branch --show-current) --json number`.
24-
2. Fetch comments with `gh api -H "Accept: application/vnd.github.v3.full+json" /repos/google/ground-android/pulls/<PR number>/comments` to get the pending comments.
25-
3. Resolve the pending comments.
5+
1. Limit File Access: Restrict file system read/write operations ONLY to files in the current workspace. ABSOLUTELY DO NOT access files in other directories (e.g., /etc, ~, /usr).
6+
2. Confirm Dangerous Commands: If the intended command is potentially destructive (e.g., rm, mv, sudo, systemctl), you MUST explicitly preface the command proposal with a warning: "WARNING: POTENTIALLY DESTRUCTIVE ACTION REQUIRED."
7+
3. Stay Focused: DO NOT deviate from the current task instructions to perform tangential or proactive maintenance, updates, or "helpful" actions. Only address the explicit request.
8+
4. Ensure all new files have the standard copyright header with the current year.
9+
5. Add or update unit tests for all new or changed behaviors. Remove unit tests for code which has been removed.
10+
6. Run `verify_changes` skill to ensure tests pass.
11+
12+
When asked to push changes to GitHub:
13+
14+
1. **Prepare**: Run `prepare_commit` skill to format code and remove temp comments.
15+
2. **Verify**: Run `verify_changes` skill after meaningful changes.
16+
3. **Push**: Run `push_to_github` skill to finalize checks and push.

0 commit comments

Comments
 (0)