[nixos-configs-vru] Add skill for responding to Gitea PR review comments #26

Merged
johno merged 4 commits from bead/nixos-configs-vru into main 2026-01-13 09:08:18 -08:00

View File

@@ -1,217 +1,350 @@
--- ---
description: Manage and respond to Gitea/Forgejo PR review comments description: Address Gitea/Forgejo PR review comments with code changes
--- ---
# Gitea PR Review Comments # Gitea PR Review
This skill enables reading PR review comments and posting inline thread replies on Gitea/Forgejo instances. 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
Outdated
Review

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?

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?
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 ## Prerequisites
- `tea` CLI configured with a Gitea/Forgejo instance - `tea` CLI configured with a Gitea/Forgejo instance
- Access token from tea config: `~/.config/tea/config.yml` - Access token from tea config: `~/.config/tea/config.yml`
- Repository must be a Gitea/Forgejo remote (not GitHub) - Repository must be a Gitea/Forgejo remote (not GitHub)
- **Nix users**: All tools available via nixpkgs (`nix run nixpkgs#tea`)
johno marked this conversation as resolved Outdated
Outdated
Review

let's use nix run nixpkgs#tea instead of a nix-shell

let's use `nix run nixpkgs#tea` instead of a nix-shell
## Configuration ## Initial Setup
Get the Gitea instance URL and token from tea config: When this command is invoked:
1. **Parse the input for PR number**:
- If a PR number is provided as argument, use it
- If no PR number, detect from current branch (see PR Detection section)
2. **Verify required tools are available**:
```bash
which tea
```
If tea is missing:
```
Error: `tea` CLI not found.
Please install:
- Nix: nix run nixpkgs#tea
- Other: https://gitea.com/gitea/tea
```
**STOP** if tea is missing.
3. **Extract configuration from tea config**:
```bash
johno marked this conversation as resolved
Review

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
Review

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
# 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:
```
Error: Could not read tea config at ~/.config/tea/config.yml
Please ensure `tea` is installed and configured:
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
REMOTE_URL=$(git remote get-url origin)
# Parse owner and repo from URL (handles both SSH and HTTPS)
OWNER=$(echo "$REMOTE_URL" | sed -E 's#.*[:/]([^/]+)/[^/]+\.git$#\1#')
REPO=$(echo "$REMOTE_URL" | sed -E 's#.*/([^/]+)\.git$#\1#')
```
5. **Ensure we're on the PR branch**:
```bash
CURRENT_BRANCH=$(git branch --show-current)
# Verify this branch corresponds to the PR
```
6. **Respond with**:
```
Addressing PR review comments for PR #{PR_NUMBER}...
Repository: {OWNER}/{REPO}
Branch: {CURRENT_BRANCH}
Gitea URL: {GITEA_URL}
```
## PR Detection
If no PR number is provided, detect from the current branch:
```bash ```bash
# Get the default login URL and token CURRENT_BRANCH=$(git branch --show-current)
yq -r '.logins[] | select(.name == "default") | .url' ~/.config/tea/config.yml tea pr list --fields index,head --output simple | grep "$CURRENT_BRANCH"
yq -r '.logins[] | select(.name == "default") | .token' ~/.config/tea/config.yml
``` ```
Or if you have a specific login name: If no PR exists for the current branch, use `AskUserQuestion`:
```bash
yq -r '.logins[] | select(.name == "YOUR_LOGIN") | .url' ~/.config/tea/config.yml ```
yq -r '.logins[] | select(.name == "YOUR_LOGIN") | .token' ~/.config/tea/config.yml No PR found for branch '{CURRENT_BRANCH}'.
Would you like to:
1. Enter a PR number manually
2. Cancel
``` ```
## Commands ## Workflow
### 1. List PR Review Comments ### Step 1: Fetch and Parse Comments
Fetch all reviews and their comments for a PR: Fetch all reviews and their comments:
```bash ```bash
# Set environment variables # Fetch reviews (filter out dismissed reviews)
GITEA_URL="https://git.johnogle.info"
TOKEN="<your-token>"
OWNER="<repo-owner>"
REPO="<repo-name>"
PR_NUMBER="<pr-number>"
# Get all reviews for the PR
curl -s -H "Authorization: token $TOKEN" \ curl -s -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" | jq "$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" \
| jq '[.[] | select(.dismissed != true)]'
# Get comments for a specific review # For each review, fetch comments
REVIEW_ID="<review-id>"
curl -s -H "Authorization: token $TOKEN" \ curl -s -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews/$REVIEW_ID/comments" | jq "$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews/$REVIEW_ID/comments"
``` ```
### 2. View All Review Comments (Combined) **Filter resolved comments**: When processing comments, skip any that have been marked as resolved. Check the `resolver` field in the comment response - if it's not null, the comment has been resolved and should be skipped.
```bash ```bash
# Get all reviews and their comments in one view # Example: Filter to only unresolved comments
curl -s -H "Authorization: token $TOKEN" \ jq '[.[] | select(.resolver == null)]'
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" | \
jq -r '.[] | "Review \(.id) by \(.user.login): \(.state)\n Body: \(.body)"'
# For each review, show inline comments
for REVIEW_ID in $(curl -s -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" | jq -r '.[].id'); do
echo "=== Review $REVIEW_ID comments ==="
curl -s -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews/$REVIEW_ID/comments" | \
jq -r '.[] | "[\(.path):\(.line)] \(.body)"'
done
``` ```
### 3. Reply to Review Comments (Web Endpoint Method) If no reviews found or all comments are resolved:
The Gitea REST API does not support replying to review comment threads. The web UI uses a different endpoint:
``` ```
POST /{owner}/{repo}/pulls/{pr_number}/files/reviews/comments No unresolved reviews found for PR #{PR_NUMBER}.
Content-Type: multipart/form-data Nothing to address.
```
**STOP** here.
### Step 2: Categorize Comments
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)
``` ```
**Required form fields:** ### Step 3: User Confirmation
- `reply`: Review ID to reply to
- `content`: The reply message
- `path`: File path
- `line`: Line number
- `side`: `proposed` or `original`
- `single_review`: `true`
- `origin`: `timeline`
- `_csrf`: CSRF token (required for web endpoint)
**Authentication Challenge:** Use `AskUserQuestion` to confirm the plan:
This endpoint requires session-based authentication, not API tokens. Options:
#### Option A: Use Browser Session (Recommended) ```
I've categorized {N} comments. My plan:
1. Log in to Gitea in your browser **Will make changes for:**
2. Open browser developer tools and copy cookies - {file}:{line}: {planned_change}
3. Use the session cookies with curl
```bash **Will explain in summary:**
# First, get CSRF token from the PR page - {file}:{line}: {planned_explanation}
CSRF=$(curl -s -c cookies.txt -b cookies.txt \
"$GITEA_URL/$OWNER/$REPO/pulls/$PR_NUMBER/files" | \
grep -oP 'name="_csrf" value="\K[^"]+')
# Post the reply **No action needed:**
curl -s -b cookies.txt \ - {file}:{line}: {reason}
-F "reply=$REVIEW_ID" \
-F "content=Your reply message here" \ Proceed with this plan?
-F "path=$FILE_PATH" \
-F "line=$LINE_NUMBER" \
-F "side=proposed" \
-F "single_review=true" \
-F "origin=timeline" \
-F "_csrf=$CSRF" \
"$GITEA_URL/$OWNER/$REPO/pulls/$PR_NUMBER/files/reviews/comments"
``` ```
#### Option B: Create Top-Level Comment (Fallback) Options:
1. **Proceed** - Execute the plan
2. **Modify** - Let user adjust categorization
3. **Cancel** - Exit without changes
If thread replies are not critical, use the API to create a top-level comment: ### Step 4: Address Actionable Comments (Parallel Subagents)
For each actionable comment, launch a subagent using the Task tool:
```bash
# Create a top-level comment mentioning the review context
curl -s -X POST \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"body\": \"Re: @reviewer's comment on $FILE_PATH:$LINE_NUMBER\n\nYour reply here\"}" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/issues/$PR_NUMBER/comments"
``` ```
Launch Task subagent with:
- subagent_type: "general-purpose"
- prompt: |
You are addressing a PR review comment. Make the requested change and nothing else.
Or use tea CLI: **File**: {file_path}
```bash **Line**: {line_number}
tea comment $PR_NUMBER "Re: @reviewer's comment on $FILE_PATH:$LINE_NUMBER **Comment**: {comment_body}
**Diff context**:
Your reply here" ```
``` {diff_hunk}
### 4. Submit a New Review
Create a new review with inline comments:
```bash
curl -s -X POST \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"body": "Overall review comments",
"event": "COMMENT",
"comments": [
{
"path": "path/to/file.py",
"body": "Comment on this line",
"new_position": 10
}
]
}' \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews"
```
Event types: `COMMENT`, `APPROVE`, `REQUEST_CHANGES`
## Workflow Example
### Reading and Responding to Reviews
1. **Set up environment**:
```bash
export GITEA_URL=$(yq -r '.logins[] | select(.name == "default") | .url' ~/.config/tea/config.yml)
export TOKEN=$(yq -r '.logins[] | select(.name == "default") | .token' ~/.config/tea/config.yml)
export OWNER="johno"
export REPO="nixos-configs"
export PR_NUMBER="5"
``` ```
2. **List all pending review comments**: 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
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 ```bash
# Get reviews git add -A
curl -s -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" | \
jq -r '.[] | select(.state == "REQUEST_CHANGES" or .state == "COMMENT") |
"Review \(.id) by \(.user.login) (\(.state)):\n\(.body)\n"'
``` ```
3. **Get detailed comments for a review**: 2. **Create commit with summary**:
```bash ```bash
REVIEW_ID="2" git commit -m "Address PR review comments
curl -s -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews/$REVIEW_ID/comments" | \ Changes made:
jq -r '.[] | "File: \(.path):\(.line)\nComment: \(.body)\nID: \(.id)\n---"' - {file1}: {change_summary}
- {file2}: {change_summary}
Addresses comments from review by {reviewer}"
``` ```
4. **Respond using top-level comment** (most reliable): 3. **Push to remote**:
```bash ```bash
tea comment $PR_NUMBER "Addressing review feedback: git push
- File \`path/to/file.py\` line 10: Fixed the issue by...
- File \`other/file.py\` line 25: Updated as suggested..."
``` ```
### Step 6: Post Summary Comment
Post a single comment summarizing all actions taken:
```bash
tea comment $PR_NUMBER "$(cat <<'EOF'
## Review Comments Addressed
cc @{reviewer1} @{reviewer2}
**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
)"
```
### Step 7: Final Summary
Display to user:
```
## PR Review Complete
**Commit**: {SHA}
Review

we should tag any of the commenters in our response as well

we should tag any of the commenters in our response as well
**Changes**: {N} files modified
### Actions Taken:
- [x] {file1}:{line} - Added nix prerequisite note
- [x] {file2}:{line} - Explained CI approach in comment
- [ ] {file3}:{line} - Acknowledged (separate skill)
**Reviewers tagged**: @{reviewer1}, @{reviewer2}
**Comment posted**: {comment_url}
PR URL: {GITEA_URL}/{OWNER}/{REPO}/pulls/{PR_NUMBER}
```
**Note**: When posting the summary comment, tag all reviewers who left comments so they receive notifications about the changes.
## Error Handling
### Subagent failed to make change
If a subagent fails:
```
Warning: Could not address comment on {file}:{line}
Reason: {error}
Options:
1. Skip this comment and continue
2. Retry with manual guidance
3. Abort all changes
```
### Push failed
```
Error pushing changes: {error}
Your changes are committed locally. You may need to:
1. Pull and resolve conflicts: git pull --rebase
2. Push again: git push
```
### 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 ## API Reference
### Endpoints ### Endpoints Used
| Action | Method | Endpoint | | Action | Method | Endpoint |
|--------|--------|----------| |--------|--------|----------|
| List reviews | GET | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews` | | List reviews | GET | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews` |
| Get review | GET | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}` |
| Get review comments | GET | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments` | | Get review comments | GET | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments` |
| Create review | POST | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews` | | Create issue comment | POST | via `tea comment` |
| Submit review | POST | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}` |
| Delete review | DELETE | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}` |
| Create issue comment | POST | `/api/v1/repos/{owner}/{repo}/issues/{index}/comments` |
### Review States ### Review States
@@ -220,25 +353,120 @@ Event types: `COMMENT`, `APPROVE`, `REQUEST_CHANGES`
- `APPROVE` - Approving the changes - `APPROVE` - Approving the changes
- `REQUEST_CHANGES` - Requesting changes before merge - `REQUEST_CHANGES` - Requesting changes before merge
## Shell Command Patterns
Claude Code's bash execution has quirks. Use these patterns for reliability:
### curl requests
**DO** - Use single quotes for URL and header separately:
```bash
curl -s 'https://git.example.com/api/v1/repos/owner/repo/pulls/1/reviews' \
-H 'Authorization: token YOUR_TOKEN_HERE' | jq .
```
**DON'T** - Variable expansion in `-H` flag often fails:
```bash
# This may fail with "blank argument" errors
curl -s -H "Authorization: token $TOKEN" "$URL"
```
### Iterating over reviews
**DO** - Run separate commands for each review ID:
```bash
echo "=== Review 4 ===" && curl -s 'URL/reviews/4/comments' -H 'Authorization: token ...' | jq .
echo "=== Review 5 ===" && curl -s 'URL/reviews/5/comments' -H 'Authorization: token ...' | jq .
```
**DON'T** - For loops with multiline bodies often fail:
```bash
# This may cause syntax errors
for id in 4 5 6; do
curl -s "URL/reviews/$id/comments"
done
```
### tea comment
**DO** - Use single-quoted string for comment body:
```bash
tea comment 26 '## Summary
Changes made:
- Item 1
- Item 2'
```
**DON'T** - Heredocs may hang or timeout:
```bash
# This may hang indefinitely
tea comment 26 "$(cat <<'EOF'
...
EOF
)"
```
## Limitations ## Limitations
1. **Thread replies**: The Gitea REST API does not support replying directly to review comment threads. This is a known limitation. Workarounds: 1. **Thread replies**: Gitea API doesn't support inline thread replies. We post a single summary comment instead.
- Use top-level comments with context
- Use the web UI manually for thread replies
- Implement session-based authentication to use the web endpoint
2. **CSRF tokens**: The web endpoint for thread replies requires CSRF tokens, which expire and need to be fetched from the page. 2. **Complex changes**: For comments requiring significant refactoring, the subagent may need guidance. The skill will flag these as "blocked" for user input.
3. **Session auth**: API tokens work for REST API but not for web endpoints that require session cookies. 3. **Merge conflicts**: If the branch is behind, you may need to rebase before changes can be pushed.
## Tips ## Example Session
- Always quote file paths and line numbers when responding via top-level comments ```
- Use `tea pr view $PR_NUMBER --comments` to see all comments User: /gitea_pr_review 26
- Use `tea open pulls/$PR_NUMBER` to open the PR in browser for manual thread replies Assistant: Addressing PR review comments for PR #26...
- Consider using `tea pr approve $PR_NUMBER` after addressing all comments
Repository: johno/nixos-configs
Branch: bead/nixos-configs-vru
Gitea URL: https://git.johnogle.info
## Comment Analysis
### 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)
### 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
Proceed with this plan? [Proceed]
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 ## See Also
- Gitea API Documentation: https://docs.gitea.com/api/1.20/
- `tea` CLI: https://gitea.com/gitea/tea - `tea` CLI: https://gitea.com/gitea/tea
- Gitea API: https://docs.gitea.com/api/
- `/beads_workflow` for full development workflow