Compare commits

...

3 Commits

Author SHA1 Message Date
2bdbece800 fix(flake): Remove duplicate home-manager imports from wixos and zix790prors
The nixosModules list already includes inputs.home-manager.nixosModules.home-manager,
so these individual configuration imports were redundant.
2026-01-13 14:25:21 -08:00
92b6cfb710 fix(common): Add ghostty terminfo for SSH compatibility
Installs ghostty.terminfo on all NixOS machines so tmux works
when SSH'ing from a Ghostty terminal.
2026-01-13 14:09:57 -08:00
996fb86ed8 [nixos-configs-vru] Add skill for responding to Gitea PR review comments (#26)
## Summary
- Rewrote gitea_pr_review.md as a comprehensive interactive skill
- Accepts PR number as argument or auto-detects from current branch
- Reads Gitea config from tea CLI config file
- Fetches and displays review comments via REST API
- Interactive comment selection via AskUserQuestion
- Posts replies via `tea comment` with file:line context

## Bead Reference
Implements bead: nixos-configs-vru

## Changes
- Rewritten `home/roles/development/skills/gitea_pr_review.md` (+259/-155 lines)

## Testing
Please leave a review comment on this PR so we can test the skill!

## Limitations
- Thread replies are posted as top-level comments (Gitea API limitation)
- Uses first login from tea config

Reviewed-on: #26
Co-authored-by: John Ogle <john@ogle.fyi>
Co-committed-by: John Ogle <john@ogle.fyi>
2026-01-13 09:08:17 -08:00
3 changed files with 388 additions and 161 deletions

View File

@@ -162,7 +162,6 @@
modules = nixosModules ++ [ modules = nixosModules ++ [
nixos-wsl.nixosModules.default nixos-wsl.nixosModules.default
./machines/wixos/configuration.nix ./machines/wixos/configuration.nix
inputs.home-manager.nixosModules.home-manager
{ {
home-manager.users.johno = import ./home/home-desktop.nix; home-manager.users.johno = import ./home/home-desktop.nix;
home-manager.extraSpecialArgs = { inherit system; }; home-manager.extraSpecialArgs = { inherit system; };
@@ -174,7 +173,6 @@
system = "x86_64-linux"; system = "x86_64-linux";
modules = nixosModules ++ [ modules = nixosModules ++ [
./machines/zix790prors/configuration.nix ./machines/zix790prors/configuration.nix
inputs.home-manager.nixosModules.home-manager
{ {
home-manager.users.johno = import ./home/home-desktop.nix; home-manager.users.johno = import ./home/home-desktop.nix;
home-manager.extraSpecialArgs = { inherit system; }; home-manager.extraSpecialArgs = { inherit system; };

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.**
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`)
## 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 ```bash
# Get the default login URL and token which tea
yq -r '.logins[] | select(.name == "default") | .url' ~/.config/tea/config.yml
yq -r '.logins[] | select(.name == "default") | .token' ~/.config/tea/config.yml
``` ```
Or if you have a specific login name: 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 ```bash
yq -r '.logins[] | select(.name == "YOUR_LOGIN") | .url' ~/.config/tea/config.yml # Read tea config (it's YAML but simple enough to grep)
yq -r '.logins[] | select(.name == "YOUR_LOGIN") | .token' ~/.config/tea/config.yml 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: //')
``` ```
## Commands If config is missing or invalid:
```
Error: Could not read tea config at ~/.config/tea/config.yml
### 1. List PR Review Comments 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.
Fetch all reviews and their comments for a PR: 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
# Set environment variables CURRENT_BRANCH=$(git branch --show-current)
GITEA_URL="https://git.johnogle.info" tea pr list --fields index,head --output simple | grep "$CURRENT_BRANCH"
TOKEN="<your-token>" ```
OWNER="<repo-owner>"
REPO="<repo-name>"
PR_NUMBER="<pr-number>"
# Get all reviews for the PR 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
```
## Workflow
### Step 1: Fetch and Parse Comments
Fetch all reviews and their comments:
```bash
# Fetch reviews (filter out dismissed reviews)
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
**Will explain in summary:**
- {file}:{line}: {planned_explanation}
**No action needed:**
- {file}:{line}: {reason}
Proceed with this plan?
```
Options:
1. **Proceed** - Execute the plan
2. **Modify** - Let user adjust categorization
3. **Cancel** - Exit without changes
### Step 4: Address Actionable Comments (Parallel Subagents)
For each actionable comment, launch a subagent using the Task tool:
```
Launch Task subagent with:
- subagent_type: "general-purpose"
- prompt: |
You are addressing a PR review comment. Make the requested change and nothing else.
**File**: {file_path}
**Line**: {line_number}
**Comment**: {comment_body}
**Diff context**:
```
{diff_hunk}
```
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
git add -A
```
2. **Create commit with summary**:
```bash
git commit -m "Address PR review comments
Changes made:
- {file1}: {change_summary}
- {file2}: {change_summary}
Addresses comments from review by {reviewer}"
```
3. **Push to remote**:
```bash
git push
```
### Step 6: Post Summary Comment
Post a single comment summarizing all actions taken:
```bash ```bash
# First, get CSRF token from the PR page tea comment $PR_NUMBER "$(cat <<'EOF'
CSRF=$(curl -s -c cookies.txt -b cookies.txt \ ## Review Comments Addressed
"$GITEA_URL/$OWNER/$REPO/pulls/$PR_NUMBER/files" | \
grep -oP 'name="_csrf" value="\K[^"]+')
# Post the reply cc @{reviewer1} @{reviewer2}
curl -s -b cookies.txt \
-F "reply=$REVIEW_ID" \ **Changes made** (commit {SHORT_SHA}):
-F "content=Your reply message here" \ - `{file1}:{line}`: {what_was_changed}
-F "path=$FILE_PATH" \ - `{file2}:{line}`: {what_was_changed}
-F "line=$LINE_NUMBER" \
-F "side=proposed" \ **Responses to questions**:
-F "single_review=true" \ - `{file3}:{line}`: {answer_to_question}
-F "origin=timeline" \
-F "_csrf=$CSRF" \ **Acknowledged** (no action needed):
"$GITEA_URL/$OWNER/$REPO/pulls/$PR_NUMBER/files/reviews/comments" - `{file4}:{line}`: {reason_no_action}
---
*Automated response via /gitea_pr_review*
EOF
)"
``` ```
#### Option B: Create Top-Level Comment (Fallback) ### Step 7: Final Summary
If thread replies are not critical, use the API to create a top-level comment: Display to user:
```bash ```
# Create a top-level comment mentioning the review context ## PR Review Complete
curl -s -X POST \
-H "Authorization: token $TOKEN" \ **Commit**: {SHA}
-H "Content-Type: application/json" \ **Changes**: {N} files modified
-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" ### 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}
``` ```
Or use tea CLI: **Note**: When posting the summary comment, tag all reviewers who left comments so they receive notifications about the changes.
```bash
tea comment $PR_NUMBER "Re: @reviewer's comment on $FILE_PATH:$LINE_NUMBER
Your reply here" ## 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
``` ```
### 4. Submit a New Review ### Push failed
Create a new review with inline comments: ```
Error pushing changes: {error}
```bash Your changes are committed locally. You may need to:
curl -s -X POST \ 1. Pull and resolve conflicts: git pull --rebase
-H "Authorization: token $TOKEN" \ 2. Push again: git push
-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` ### No actionable comments
## Workflow Example If all comments are questions/acknowledged:
### 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"
``` ```
No code changes needed.
2. **List all pending review comments**: All comments are either questions or acknowledged items.
```bash Posting summary comment with explanations...
# 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"'
```
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---"'
```
4. **Respond using top-level comment** (most reliable):
```bash
tea comment $PR_NUMBER "Addressing review feedback:
- File \`path/to/file.py\` line 10: Fixed the issue by...
- File \`other/file.py\` line 25: Updated as suggested..."
``` ```
## 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

View File

@@ -8,6 +8,7 @@
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
git git
glances glances
ghostty.terminfo # So tmux works when SSH'ing from ghostty
pciutils pciutils
tree tree
usbutils usbutils