[nixos-configs-vru] Add skill for responding to Gitea PR review comments #26
@@ -1,17 +1,27 @@
|
||||
---
|
||||
description: Manage and respond to Gitea/Forgejo PR review comments
|
||||
description: Address Gitea/Forgejo PR review comments with code changes
|
||||
---
|
||||
|
||||
# Gitea PR Review
|
||||
|
||||
You are tasked with reading PR review comments and posting replies on Gitea/Forgejo instances. This skill uses the REST API for reading comments and the `tea` CLI for posting replies.
|
||||
You are tasked with **addressing** PR review comments by making code changes, then summarizing what was done. This skill drives PR progress, not just conversation.
|
||||
|
||||
## Philosophy
|
||||
|
||||
**Comments are work items, not conversation starters.**
|
||||
|
||||
|
johno marked this conversation as resolved
Outdated
|
||||
When a reviewer leaves a comment, they're identifying something that needs attention. This skill:
|
||||
1. Categorizes comments by actionability
|
||||
2. Makes code changes to address actionable comments
|
||||
3. Commits and pushes those changes
|
||||
4. Posts a single summary comment describing what was done
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `tea` CLI configured with a Gitea/Forgejo instance
|
||||
- `yq` installed for parsing tea config
|
||||
- Access token from tea config: `~/.config/tea/config.yml`
|
||||
- Repository must be a Gitea/Forgejo remote (not GitHub)
|
||||
- **Nix users**: All tools available via nixpkgs (`nix-shell -p tea`)
|
||||
|
johno marked this conversation as resolved
Outdated
johno
commented
let's use let's use `nix run nixpkgs#tea` instead of a nix-shell
|
||||
|
||||
## Initial Setup
|
||||
|
||||
@@ -23,26 +33,25 @@ When this command is invoked:
|
||||
|
||||
2. **Verify required tools are available**:
|
||||
```bash
|
||||
which tea && which yq
|
||||
which tea
|
||||
```
|
||||
|
||||
If either tool is missing:
|
||||
If tea is missing:
|
||||
```
|
||||
Error: Required tools not found.
|
||||
|
||||
Missing: {tea|yq|both}
|
||||
Error: `tea` CLI not found.
|
||||
|
||||
Please install:
|
||||
- tea: https://gitea.com/gitea/tea
|
||||
- yq: Available via package manager (nix, brew, apt)
|
||||
- Nix: nix-shell -p tea
|
||||
- Other: https://gitea.com/gitea/tea
|
||||
```
|
||||
**STOP** if tools are missing.
|
||||
**STOP** if tea is missing.
|
||||
|
||||
3. **Extract configuration from tea config**:
|
||||
```bash
|
||||
|
johno marked this conversation as resolved
johno
commented
we eventually want to run this in CI, where the tea token will be passed in as an env var we eventually want to run this in CI, where the tea token will be passed in as an env var
johno
commented
that will be a separate skill though, so no need to update this now that will be a separate skill though, so no need to update this now
|
||||
# Get the Gitea URL and token from tea config
|
||||
GITEA_URL=$(yq -r '.logins[0].url' ~/.config/tea/config.yml)
|
||||
TOKEN=$(yq -r '.logins[0].token' ~/.config/tea/config.yml)
|
||||
# Read tea config (it's YAML but simple enough to grep)
|
||||
TEA_CONFIG="$HOME/.config/tea/config.yml"
|
||||
GITEA_URL=$(grep -A1 'logins:' "$TEA_CONFIG" | grep 'url:' | head -1 | sed 's/.*url: //')
|
||||
TOKEN=$(grep -A5 'logins:' "$TEA_CONFIG" | grep 'token:' | head -1 | sed 's/.*token: //')
|
||||
```
|
||||
|
||||
If config is missing or invalid:
|
||||
@@ -50,27 +59,31 @@ When this command is invoked:
|
||||
Error: Could not read tea config at ~/.config/tea/config.yml
|
||||
|
||||
Please ensure `tea` is installed and configured:
|
||||
1. Install tea: https://gitea.com/gitea/tea
|
||||
1. Install tea
|
||||
2. Log in: tea login add --url https://your-gitea-instance --token YOUR_TOKEN
|
||||
```
|
||||
**STOP** if config is invalid.
|
||||
|
||||
4. **Detect repository info from git remote**:
|
||||
```bash
|
||||
# Get the remote URL and parse owner/repo
|
||||
REMOTE_URL=$(git remote get-url origin)
|
||||
# Parse owner and repo from URL (handles both SSH and HTTPS)
|
||||
# Example: git@git.example.com:owner/repo.git -> owner/repo
|
||||
# Example: https://git.example.com/owner/repo.git -> owner/repo
|
||||
OWNER=$(echo "$REMOTE_URL" | sed -E 's#.*[:/]([^/]+)/[^/]+\.git$#\1#')
|
||||
REPO=$(echo "$REMOTE_URL" | sed -E 's#.*/([^/]+)\.git$#\1#')
|
||||
```
|
||||
|
||||
5. **Respond with**:
|
||||
5. **Ensure we're on the PR branch**:
|
||||
```bash
|
||||
CURRENT_BRANCH=$(git branch --show-current)
|
||||
# Verify this branch corresponds to the PR
|
||||
```
|
||||
Fetching PR review comments for PR #{PR_NUMBER}...
|
||||
|
||||
6. **Respond with**:
|
||||
```
|
||||
Addressing PR review comments for PR #{PR_NUMBER}...
|
||||
|
||||
Repository: {OWNER}/{REPO}
|
||||
Branch: {CURRENT_BRANCH}
|
||||
Gitea URL: {GITEA_URL}
|
||||
```
|
||||
|
||||
@@ -79,10 +92,7 @@ When this command is invoked:
|
||||
If no PR number is provided, detect from the current branch:
|
||||
|
||||
```bash
|
||||
# Get current branch name
|
||||
CURRENT_BRANCH=$(git branch --show-current)
|
||||
|
||||
# List PRs and find one matching the current branch
|
||||
tea pr list --fields index,head --output simple | grep "$CURRENT_BRANCH"
|
||||
```
|
||||
|
||||
@@ -94,201 +104,223 @@ No PR found for branch '{CURRENT_BRANCH}'.
|
||||
Would you like to:
|
||||
1. Enter a PR number manually
|
||||
2. Cancel
|
||||
|
||||
Which option?
|
||||
```
|
||||
|
||||
- If option 1: Ask for the PR number and continue
|
||||
- If option 2: End the skill with "No PR to review. Exiting."
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch Reviews
|
||||
### Step 1: Fetch and Parse Comments
|
||||
|
||||
Fetch all reviews for the PR:
|
||||
Fetch all reviews and their comments:
|
||||
|
||||
```bash
|
||||
# Fetch reviews
|
||||
curl -s -H "Authorization: token $TOKEN" \
|
||||
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews"
|
||||
```
|
||||
|
||||
Parse the response to extract:
|
||||
- `id`: Review ID (needed for fetching comments)
|
||||
- `user.login`: Reviewer username
|
||||
- `state`: Review state (COMMENT, REQUEST_CHANGES, APPROVE, PENDING)
|
||||
- `body`: Review body text (may be empty for inline-only reviews)
|
||||
|
||||
If no reviews found:
|
||||
```
|
||||
No reviews found for PR #{PR_NUMBER}.
|
||||
|
||||
This PR has no review comments to respond to.
|
||||
```
|
||||
**STOP** here.
|
||||
|
||||
### Step 2: Fetch Review Comments
|
||||
|
||||
For each review with state `COMMENT` or `REQUEST_CHANGES`, fetch inline comments:
|
||||
|
||||
```bash
|
||||
REVIEW_ID="{review_id}"
|
||||
# For each review, fetch comments
|
||||
curl -s -H "Authorization: token $TOKEN" \
|
||||
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews/$REVIEW_ID/comments"
|
||||
```
|
||||
|
||||
Parse each comment for:
|
||||
- `id`: Comment ID
|
||||
- `path`: File path
|
||||
- `line`: Line number (may be null for file-level comments)
|
||||
- `body`: Comment text
|
||||
- `created_at`: Timestamp
|
||||
|
||||
### Step 3: Display Comments Needing Response
|
||||
|
||||
Format and display comments grouped by review:
|
||||
|
||||
If no reviews found:
|
||||
```
|
||||
## Review by {reviewer} ({state})
|
||||
No reviews found for PR #{PR_NUMBER}.
|
||||
Nothing to address.
|
||||
```
|
||||
**STOP** here.
|
||||
|
||||
### Comment 1: {file_path}:{line}
|
||||
{comment_body}
|
||||
### Step 2: Categorize Comments
|
||||
|
||||
### Comment 2: {file_path}:{line}
|
||||
{comment_body}
|
||||
For each comment, categorize it as one of:
|
||||
|
||||
---
|
||||
| Category | Description | Action |
|
||||
|----------|-------------|--------|
|
||||
| **actionable** | Requests a code change, addition, or fix | Launch subagent to make change |
|
||||
| **question** | Asks for clarification or explanation | Include answer in summary |
|
||||
| **acknowledged** | FYI, self-resolved, or "no action needed" noted | Note in summary |
|
||||
| **blocked** | Requires external input or is out of scope | Flag for user |
|
||||
|
||||
**Categorization heuristics**:
|
||||
- Contains "add", "change", "fix", "update", "consider adding", "should be" → **actionable**
|
||||
- Contains "?" or "why", "how", "what" → **question**
|
||||
- Contains "no need to update", "will be separate", "acknowledged" → **acknowledged**
|
||||
- Contains "discuss", "later", "out of scope", "blocked by" → **blocked**
|
||||
|
||||
Display the categorization:
|
||||
```
|
||||
## Comment Analysis
|
||||
|
||||
### Actionable (will make changes):
|
||||
1. {file}:{line} - "{comment_summary}" → Will add nix note to prerequisites
|
||||
|
||||
### Questions (will answer in summary):
|
||||
2. {file}:{line} - "{comment_summary}" → Explain CI token approach
|
||||
|
||||
### Acknowledged (no action needed):
|
||||
3. {file}:{line} - "{comment_summary}" → Reviewer noted separate skill
|
||||
|
||||
### Blocked (needs input):
|
||||
(none)
|
||||
```
|
||||
|
||||
If there are general review comments (without inline location), show them as:
|
||||
```
|
||||
### General Review Comment
|
||||
{review_body}
|
||||
```
|
||||
### Step 3: User Confirmation
|
||||
|
||||
### Step 4: Select Comments to Address
|
||||
|
||||
Use `AskUserQuestion` to let the user choose which comments to address:
|
||||
Use `AskUserQuestion` to confirm the plan:
|
||||
|
||||
```
|
||||
Which review comments would you like to address?
|
||||
I've categorized {N} comments. My plan:
|
||||
|
||||
1. {file_path}:{line} - "{truncated_comment_first_50_chars}..."
|
||||
2. {file_path}:{line} - "{truncated_comment_first_50_chars}..."
|
||||
3. All comments
|
||||
4. None (just viewing)
|
||||
**Will make changes for:**
|
||||
- {file}:{line}: {planned_change}
|
||||
|
||||
Enter your choice (comma-separated for multiple, e.g., "1,2"):
|
||||
**Will explain in summary:**
|
||||
- {file}:{line}: {planned_explanation}
|
||||
|
||||
**No action needed:**
|
||||
- {file}:{line}: {reason}
|
||||
|
||||
Proceed with this plan?
|
||||
```
|
||||
|
||||
- If "All comments" (3): Process all comments
|
||||
- If "None" (4): End with "No comments to address. You can view the PR at: {PR_URL}"
|
||||
- Otherwise: Process selected comment numbers
|
||||
Options:
|
||||
1. **Proceed** - Execute the plan
|
||||
2. **Modify** - Let user adjust categorization
|
||||
3. **Cancel** - Exit without changes
|
||||
|
||||
### Step 5: Compose Replies
|
||||
### Step 4: Address Actionable Comments (Parallel Subagents)
|
||||
|
||||
For each selected comment:
|
||||
For each actionable comment, launch a subagent using the Task tool:
|
||||
|
||||
1. **Show the full context**:
|
||||
```
|
||||
## Addressing comment on {file_path}:{line}
|
||||
```
|
||||
Launch Task subagent with:
|
||||
- subagent_type: "general-purpose"
|
||||
- prompt: |
|
||||
You are addressing a PR review comment. Make the requested change and nothing else.
|
||||
|
||||
**Original comment by {reviewer}:**
|
||||
> {full_comment_body}
|
||||
**File**: {file_path}
|
||||
**Line**: {line_number}
|
||||
**Comment**: {comment_body}
|
||||
**Diff context**:
|
||||
```
|
||||
{diff_hunk}
|
||||
```
|
||||
|
||||
**Current code at that location:**
|
||||
```
|
||||
Instructions:
|
||||
1. Read the file to understand context
|
||||
2. Make the minimal change to address the comment
|
||||
3. Do NOT commit - just make the edit
|
||||
4. Report what you changed
|
||||
|
||||
Read the file and show relevant code around the line:
|
||||
Be precise. Only change what's needed to address this specific comment.
|
||||
```
|
||||
|
||||
**Important**: Launch actionable comment subagents in parallel when they touch different files. For comments on the same file, run sequentially to avoid conflicts.
|
||||
|
||||
Wait for all subagents to complete and collect their results.
|
||||
|
||||
### Step 5: Commit and Push
|
||||
|
||||
After all subagents complete:
|
||||
|
||||
1. **Stage changes**:
|
||||
```bash
|
||||
# Show 5 lines of context around the commented line
|
||||
sed -n '{line-5},{line+5}p' {file_path}
|
||||
git add -A
|
||||
```
|
||||
|
||||
2. **Draft a response**:
|
||||
- If the comment asks a question, answer it based on the code
|
||||
- If the comment suggests a change, check if it was addressed and explain
|
||||
- If the comment points out an issue, acknowledge and describe the fix or reasoning
|
||||
2. **Create commit with summary**:
|
||||
```bash
|
||||
git commit -m "Address PR review comments
|
||||
|
||||
3. **Ask user to confirm or modify** using `AskUserQuestion`:
|
||||
```
|
||||
**Proposed response:**
|
||||
Changes made:
|
||||
- {file1}: {change_summary}
|
||||
- {file2}: {change_summary}
|
||||
|
||||
{draft_response}
|
||||
|
||||
Options:
|
||||
1. Send as-is
|
||||
2. Edit response (I'll ask for your text)
|
||||
3. Skip this comment
|
||||
|
||||
How would you like to proceed?
|
||||
Addresses comments from review by {reviewer}"
|
||||
```
|
||||
|
||||
- If "Edit response" (2): Ask "Please provide your response text:" and use that instead
|
||||
- If "Skip" (3): Move to next comment
|
||||
3. **Push to remote**:
|
||||
```bash
|
||||
git push
|
||||
```
|
||||
|
||||
### Step 6: Post Replies
|
||||
### Step 6: Post Summary Comment
|
||||
|
||||
Post each confirmed reply using `tea comment` with file:line context:
|
||||
Post a single comment summarizing all actions taken:
|
||||
|
||||
```bash
|
||||
tea comment $PR_NUMBER "Re: @{reviewer}'s comment on \`{file_path}:{line}\`
|
||||
tea comment $PR_NUMBER "$(cat <<'EOF'
|
||||
## Review Comments Addressed
|
||||
|
||||
{response_body}"
|
||||
**Changes made** (commit {SHORT_SHA}):
|
||||
- `{file1}:{line}`: {what_was_changed}
|
||||
- `{file2}:{line}`: {what_was_changed}
|
||||
|
||||
**Responses to questions**:
|
||||
- `{file3}:{line}`: {answer_to_question}
|
||||
|
||||
**Acknowledged** (no action needed):
|
||||
- `{file4}:{line}`: {reason_no_action}
|
||||
|
||||
---
|
||||
*Automated response via /gitea_pr_review*
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
**Important**: The Gitea REST API does not support replying directly to review comment threads. This skill posts top-level comments with context as a reliable workaround. For true inline thread replies, users should use the Gitea web UI.
|
||||
### Step 7: Final Summary
|
||||
|
||||
After posting:
|
||||
```
|
||||
Posted reply for {file_path}:{line}
|
||||
```
|
||||
|
||||
### Step 7: Summary
|
||||
|
||||
After processing all selected comments:
|
||||
Display to user:
|
||||
|
||||
```
|
||||
## Summary
|
||||
## PR Review Complete
|
||||
|
||||
Posted {N} replies to PR #{PR_NUMBER}:
|
||||
**Commit**: {SHA}
|
||||
**Changes**: {N} files modified
|
||||
|
||||
- {file_path}:{line} - Response posted
|
||||
- {file_path}:{line} - Response posted
|
||||
- {file_path}:{line} - Skipped
|
||||
### Actions Taken:
|
||||
- [x] {file1}:{line} - Added nix prerequisite note
|
||||
- [x] {file2}:{line} - Explained CI approach in comment
|
||||
- [ ] {file3}:{line} - Acknowledged (separate skill)
|
||||
|
||||
You can view the PR at: {GITEA_URL}/{OWNER}/{REPO}/pulls/{PR_NUMBER}
|
||||
**Comment posted**: {comment_url}
|
||||
|
||||
**Note**: Replies are posted as top-level comments with file:line context.
|
||||
For true inline thread replies, use the Gitea web UI.
|
||||
PR URL: {GITEA_URL}/{OWNER}/{REPO}/pulls/{PR_NUMBER}
|
||||
|
johno
commented
we should tag any of the commenters in our response as well we should tag any of the commenters in our response as well
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### API request failed
|
||||
### Subagent failed to make change
|
||||
|
||||
If a subagent fails:
|
||||
```
|
||||
Error fetching reviews: {error_message}
|
||||
Warning: Could not address comment on {file}:{line}
|
||||
|
||||
Please check:
|
||||
- Your token is valid and not expired
|
||||
- The repository exists and you have access
|
||||
- The Gitea instance is reachable
|
||||
Reason: {error}
|
||||
|
||||
HTTP Status: {status_code}
|
||||
Response: {response_body}
|
||||
Options:
|
||||
1. Skip this comment and continue
|
||||
2. Retry with manual guidance
|
||||
3. Abort all changes
|
||||
```
|
||||
|
||||
### tea comment failed
|
||||
### Push failed
|
||||
|
||||
```
|
||||
Error posting comment: {error_message}
|
||||
Error pushing changes: {error}
|
||||
|
||||
The comment was not posted. You can try:
|
||||
1. Post manually via Gitea web UI
|
||||
2. Retry the comment
|
||||
Your changes are committed locally. You may need to:
|
||||
1. Pull and resolve conflicts: git pull --rebase
|
||||
2. Push again: git push
|
||||
```
|
||||
|
||||
Comment text was:
|
||||
{comment_text}
|
||||
### No actionable comments
|
||||
|
||||
If all comments are questions/acknowledged:
|
||||
```
|
||||
No code changes needed.
|
||||
|
||||
All comments are either questions or acknowledged items.
|
||||
Posting summary comment with explanations...
|
||||
```
|
||||
|
||||
## API Reference
|
||||
@@ -299,7 +331,7 @@ Comment text was:
|
||||
|--------|--------|----------|
|
||||
| List reviews | GET | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews` |
|
||||
| Get review comments | GET | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments` |
|
||||
| Create issue comment | POST | `/api/v1/repos/{owner}/{repo}/issues/{index}/comments` (via `tea comment`) |
|
||||
| Create issue comment | POST | via `tea comment` |
|
||||
|
||||
### Review States
|
||||
|
||||
@@ -310,35 +342,60 @@ Comment text was:
|
||||
|
||||
## Limitations
|
||||
|
||||
1. **Thread replies**: The Gitea REST API does not support replying directly to review comment threads. This skill posts top-level comments with file:line context as a workaround.
|
||||
1. **Thread replies**: Gitea API doesn't support inline thread replies. We post a single summary comment instead.
|
||||
|
||||
2. **Authentication**: Uses the token from tea config. Session-based auth (for true thread replies) is not supported.
|
||||
2. **Complex changes**: For comments requiring significant refactoring, the subagent may need guidance. The skill will flag these as "blocked" for user input.
|
||||
|
||||
3. **Single Gitea instance**: Uses the first login from tea config. If you have multiple Gitea instances, ensure the correct one is first in your config.
|
||||
3. **Merge conflicts**: If the branch is behind, you may need to rebase before changes can be pushed.
|
||||
|
||||
## Example Invocation
|
||||
## Example Session
|
||||
|
||||
```
|
||||
User: /gitea_pr_review 5
|
||||
Assistant: Fetching PR review comments for PR #5...
|
||||
User: /gitea_pr_review 26
|
||||
Assistant: Addressing PR review comments for PR #26...
|
||||
|
||||
Repository: johno/nixos-configs
|
||||
Branch: bead/nixos-configs-vru
|
||||
Gitea URL: https://git.johnogle.info
|
||||
|
||||
## Review by johno (REQUEST_CHANGES)
|
||||
## Comment Analysis
|
||||
|
||||
### Comment 1: home/roles/base-linux/default.nix:15
|
||||
Consider adding a comment explaining why this option is needed.
|
||||
### Actionable (will make changes):
|
||||
1. gitea_pr_review.md:12 - "could we indicate nix+nixpkgs satisfies this?"
|
||||
→ Will add note that nix users can get tools via nixpkgs
|
||||
|
||||
---
|
||||
### Questions (will answer in summary):
|
||||
(none)
|
||||
|
||||
Which review comments would you like to address?
|
||||
### Acknowledged (no action needed):
|
||||
2. gitea_pr_review.md:50 - "we eventually want to run this in CI..."
|
||||
→ Reviewer noted this will be a separate skill
|
||||
|
||||
1. home/roles/base-linux/default.nix:15 - "Consider adding a comment..."
|
||||
2. All comments
|
||||
3. None (just viewing)
|
||||
Proceed with this plan? [Proceed]
|
||||
|
||||
Enter your choice:
|
||||
Launching subagent to address comment 1...
|
||||
[Subagent completes edit]
|
||||
|
||||
Committing changes...
|
||||
[abc1234] Address PR review comments
|
||||
|
||||
Pushing to remote...
|
||||
Done.
|
||||
|
||||
Posting summary comment...
|
||||
|
||||
## PR Review Complete
|
||||
|
||||
**Commit**: abc1234
|
||||
**Changes**: 1 file modified
|
||||
|
||||
### Actions Taken:
|
||||
- [x] gitea_pr_review.md:12 - Added nix prerequisite note
|
||||
- [ ] gitea_pr_review.md:50 - Acknowledged (separate skill)
|
||||
|
||||
**Comment posted**: https://git.johnogle.info/.../pulls/26#issuecomment-XXX
|
||||
|
||||
PR URL: https://git.johnogle.info/johno/nixos-configs/pulls/26
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
Reference in New Issue
Block a user
we heavily rely on nix and try to ensure all environments this skill will be running in will have it installed. could we indicate that here in Prerequisites that access to nix+nixpkgs satisfies this?