[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
Showing only changes of commit f3af982304 - Show all commits

View File

@@ -2,216 +2,304 @@
description: Manage and respond to Gitea/Forgejo PR review comments
---
# 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 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.
## Prerequisites
- `tea` CLI configured with a Gitea/Forgejo instance
- `yq` installed for parsing tea config
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?
- Access token from tea config: `~/.config/tea/config.yml`
- Repository must be a Gitea/Forgejo remote (not GitHub)
## 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**:
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
```bash
which tea && which yq
```
If either tool is missing:
```
Error: Required tools not found.
Missing: {tea|yq|both}
Please install:
- tea: https://gitea.com/gitea/tea
- yq: Available via package manager (nix, brew, apt)
```
**STOP** if tools are missing.
3. **Extract configuration from tea config**:
```bash
# 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)
```
If config is missing or invalid:
```
Error: Could not read tea config at ~/.config/tea/config.yml
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
Please ensure `tea` is installed and configured:
1. Install tea: https://gitea.com/gitea/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**:
```
Fetching PR review comments for PR #{PR_NUMBER}...
Repository: {OWNER}/{REPO}
Gitea URL: {GITEA_URL}
```
## PR Detection
If no PR number is provided, detect from the current branch:
```bash
# Get the default login URL and token
yq -r '.logins[] | select(.name == "default") | .url' ~/.config/tea/config.yml
yq -r '.logins[] | select(.name == "default") | .token' ~/.config/tea/config.yml
# 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"
```
Or if you have a specific login name:
```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
If no PR exists for the current branch, use `AskUserQuestion`:
```
No PR found for branch '{CURRENT_BRANCH}'.
Would you like to:
1. Enter a PR number manually
2. Cancel
Which option?
```
## Commands
- If option 1: Ask for the PR number and continue
- If option 2: End the skill with "No PR to review. Exiting."
### 1. List PR Review Comments
## Workflow
Fetch all reviews and their comments for a PR:
### Step 1: Fetch Reviews
Fetch all reviews for the PR:
```bash
# Set environment variables
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" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" | jq
# Get comments for a specific review
REVIEW_ID="<review-id>"
curl -s -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews/$REVIEW_ID/comments" | jq
```
### 2. View All Review Comments (Combined)
```bash
# Get all reviews and their comments in one view
curl -s -H "Authorization: token $TOKEN" \
"$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)
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
Content-Type: multipart/form-data
```
**Required form fields:**
- `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:**
This endpoint requires session-based authentication, not API tokens. Options:
#### Option A: Use Browser Session (Recommended)
1. Log in to Gitea in your browser
2. Open browser developer tools and copy cookies
3. Use the session cookies with curl
```bash
# First, get CSRF token from the PR page
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
curl -s -b cookies.txt \
-F "reply=$REVIEW_ID" \
-F "content=Your reply message here" \
-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)
If thread replies are not critical, use the API to create a top-level comment:
```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"
```
Or use tea CLI:
```bash
tea comment $PR_NUMBER "Re: @reviewer's comment on $FILE_PATH:$LINE_NUMBER
Your reply here"
```
### 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`
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)
## Workflow Example
If no reviews found:
```
No reviews found for PR #{PR_NUMBER}.
### Reading and Responding to Reviews
This PR has no review comments to respond to.
```
**STOP** here.
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"
### Step 2: Fetch Review Comments
For each review with state `COMMENT` or `REQUEST_CHANGES`, fetch inline comments:
```bash
REVIEW_ID="{review_id}"
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:
```
## Review by {reviewer} ({state})
### Comment 1: {file_path}:{line}
{comment_body}
### Comment 2: {file_path}:{line}
{comment_body}
---
```
If there are general review comments (without inline location), show them as:
```
### General Review Comment
{review_body}
```
### Step 4: Select Comments to Address
Use `AskUserQuestion` to let the user choose which comments to address:
```
Which review comments would you like to address?
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)
Enter your choice (comma-separated for multiple, e.g., "1,2"):
```
- 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
### Step 5: Compose Replies
For each selected comment:
1. **Show the full context**:
```
## Addressing comment on {file_path}:{line}
**Original comment by {reviewer}:**
> {full_comment_body}
**Current code at that location:**
```
2. **List all pending review comments**:
Read the file and show relevant code around the line:
```bash
# Get reviews
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"'
# Show 5 lines of context around the commented line
sed -n '{line-5},{line+5}p' {file_path}
```
3. **Get detailed comments for a review**:
```bash
REVIEW_ID="2"
curl -s -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews/$REVIEW_ID/comments" | \
jq -r '.[] | "File: \(.path):\(.line)\nComment: \(.body)\nID: \(.id)\n---"'
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
3. **Ask user to confirm or modify** using `AskUserQuestion`:
```
**Proposed response:**
{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?
```
4. **Respond using top-level comment** (most reliable):
```bash
tea comment $PR_NUMBER "Addressing review feedback:
- If "Edit response" (2): Ask "Please provide your response text:" and use that instead
- If "Skip" (3): Move to next comment
- File \`path/to/file.py\` line 10: Fixed the issue by...
- File \`other/file.py\` line 25: Updated as suggested..."
```
### Step 6: Post Replies
Post each confirmed reply using `tea comment` with file:line context:
```bash
tea comment $PR_NUMBER "Re: @{reviewer}'s comment on \`{file_path}:{line}\`
{response_body}"
```
**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.
After posting:
```
Posted reply for {file_path}:{line}
```
### Step 7: Summary
After processing all selected comments:
```
## Summary
Posted {N} replies to PR #{PR_NUMBER}:
- {file_path}:{line} - Response posted
- {file_path}:{line} - Response posted
- {file_path}:{line} - Skipped
You can view the PR at: {GITEA_URL}/{OWNER}/{REPO}/pulls/{PR_NUMBER}
**Note**: Replies are posted as top-level comments with file:line context.
For true inline thread replies, use the Gitea web UI.
```
## Error Handling
### API request failed
```
Error fetching reviews: {error_message}
Please check:
- Your token is valid and not expired
- The repository exists and you have access
- The Gitea instance is reachable
HTTP Status: {status_code}
Response: {response_body}
```
### tea comment failed
```
Error posting comment: {error_message}
The comment was not posted. You can try:
1. Post manually via Gitea web UI
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
2. Retry the comment
Comment text was:
{comment_text}
```
## API Reference
### Endpoints
### Endpoints Used
| Action | Method | Endpoint |
|--------|--------|----------|
| 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` |
| Create review | POST | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews` |
| 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` |
| Create issue comment | POST | `/api/v1/repos/{owner}/{repo}/issues/{index}/comments` (via `tea comment`) |
### Review States
@@ -222,23 +310,39 @@ Event types: `COMMENT`, `APPROVE`, `REQUEST_CHANGES`
## Limitations
1. **Thread replies**: The Gitea REST API does not support replying directly to review comment threads. This is a known limitation. Workarounds:
- Use top-level comments with context
- Use the web UI manually for thread replies
- Implement session-based authentication to use the web endpoint
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.
2. **CSRF tokens**: The web endpoint for thread replies requires CSRF tokens, which expire and need to be fetched from the page.
2. **Authentication**: Uses the token from tea config. Session-based auth (for true thread replies) is not supported.
3. **Session auth**: API tokens work for REST API but not for web endpoints that require session cookies.
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.
## Tips
## Example Invocation
- Always quote file paths and line numbers when responding via top-level comments
- Use `tea pr view $PR_NUMBER --comments` to see all comments
- Use `tea open pulls/$PR_NUMBER` to open the PR in browser for manual thread replies
- Consider using `tea pr approve $PR_NUMBER` after addressing all comments
```
User: /gitea_pr_review 5
Assistant: Fetching PR review comments for PR #5...
Repository: johno/nixos-configs
Gitea URL: https://git.johnogle.info
## Review by johno (REQUEST_CHANGES)
### Comment 1: home/roles/base-linux/default.nix:15
Consider adding a comment explaining why this option is needed.
---
Which review comments would you like to address?
1. home/roles/base-linux/default.nix:15 - "Consider adding a comment..."
2. All comments
3. None (just viewing)
Enter your choice:
```
## See Also
- Gitea API Documentation: https://docs.gitea.com/api/1.20/
- `tea` CLI: https://gitea.com/gitea/tea
- Gitea API: https://docs.gitea.com/api/
- `/beads_workflow` for full development workflow