From ea794a677234e588cc2ba1bcc8275ccecd83eb99 Mon Sep 17 00:00:00 2001 From: Evan Reichard Date: Thu, 16 Apr 2026 09:37:58 -0400 Subject: [PATCH] feat(pi): add address-gh-review skill and enforce title case comments - Add new address-gh-review skill with SKILL.md and gh_review.sh script for fetching and addressing unresolved GitHub PR review comments - Update AGENTS.md comment style to require Title Case for block titles --- .../programs/terminal/pi/config/AGENTS.md | 2 +- .../config/skills/address-gh-review/SKILL.md | 64 +++++++++++++++++ .../skills/address-gh-review/gh_review.sh | 71 +++++++++++++++++++ 3 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 modules/home/programs/terminal/pi/config/skills/address-gh-review/SKILL.md create mode 100755 modules/home/programs/terminal/pi/config/skills/address-gh-review/gh_review.sh diff --git a/modules/home/programs/terminal/pi/config/AGENTS.md b/modules/home/programs/terminal/pi/config/AGENTS.md index 95e32da..649a8d8 100644 --- a/modules/home/programs/terminal/pi/config/AGENTS.md +++ b/modules/home/programs/terminal/pi/config/AGENTS.md @@ -42,7 +42,7 @@ write(path="file.txt", content="content") ### Comment Style -A logical "block" of code (doesn't have to be a scope, but a cohesive group of statements responsible for something) should have a comment above it with a short "title". For example: +A logical "block" of code (doesn't have to be a scope, but a cohesive group of statements responsible for something) should have a comment above it with a short "title". The title must be in **Title Case**. For example: ```go // Map Component Results diff --git a/modules/home/programs/terminal/pi/config/skills/address-gh-review/SKILL.md b/modules/home/programs/terminal/pi/config/skills/address-gh-review/SKILL.md new file mode 100644 index 0000000..ec09de3 --- /dev/null +++ b/modules/home/programs/terminal/pi/config/skills/address-gh-review/SKILL.md @@ -0,0 +1,64 @@ +--- +name: address-gh-review +description: 'Fetch and address unresolved GitHub PR review comments. Use when user asks to handle PR reviews, address review feedback, mentions "/address-gh-review", or wants to see what reviewers requested. Fetches unresolved threads, presents an actionable summary, and lets the user select which items to address.' +--- + +# GitHub PR Review + +## Overview + +Fetch unresolved review threads from the current PR, consolidate them into actionable items, and address selected items — each as a separate commit. + +## Prerequisites + +- `gh` CLI authenticated and in a repo with an open PR on the current branch +- The `git-commit` skill available for committing changes + +## Workflow + +### 1. Fetch Review Comments + +Run the bundled script to get unresolved threads: + +```bash +bash gh_review.sh +``` + +If the script fails (no PR, not authenticated, etc.), report the error and stop. + +### 2. Consolidate into Actionable Items + +Parse the output and group into actionable items. Combine threads that ask for the same change (e.g. multiple reviewers commenting on the same function about the same concern). Keep items separate when they require distinct code changes. + +Present a numbered list to the user: + +``` +## Unresolved Review Items + +1. **src/auth/login.ts:42** — Add rate limiting to prevent brute force attacks (alice-dev) +2. **src/utils/validators.ts:89** — Use stricter type checking for email validation (bob-coder) +3. **src/api/users.ts:156** — Add error handling for null responses (alice-dev, charlie-reviewer) +``` + +### 3. Ask User for Selection + +**Always ask before proceeding.** Prompt the user to select which items to address: + +``` +Which items would you like me to address? (e.g. "1,3", "all", or "none") +``` + +**Do not proceed until the user responds.** Respect "none" — just stop. + +### 4. Address Each Item + +For each selected item, in order: + +1. Read the relevant file and understand the context around the referenced line +2. Implement the requested change +3. Run relevant linting/tests if applicable (e.g. `quicklint`) +4. Commit using the `git-commit` skill — **one commit per item** + +### 5. Summary + +After all selected items are addressed, print a brief summary of what was done. diff --git a/modules/home/programs/terminal/pi/config/skills/address-gh-review/gh_review.sh b/modules/home/programs/terminal/pi/config/skills/address-gh-review/gh_review.sh new file mode 100755 index 0000000..5c59078 --- /dev/null +++ b/modules/home/programs/terminal/pi/config/skills/address-gh-review/gh_review.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# Fetch unresolved PR review threads, formatted for LLM consumption. +# Omits diff hunks (the LLM can read files from the repo directly). +# Groups comments into threads so conversation context is preserved. + +set -euo pipefail + +err() { echo "error: $1" >&2; exit 1; } + +# Verify gh CLI is installed +command -v gh >/dev/null 2>&1 || err "'gh' CLI not found. Install it from https://cli.github.com" + +# Verify we're inside a git repository +git rev-parse --is-inside-work-tree >/dev/null 2>&1 || err "not inside a git repository" + +# Verify the remote is a GitHub repo +gh repo view --json name -q .name >/dev/null 2>&1 || err "this repo does not appear to be hosted on GitHub" + +# Verify we're on a PR branch +PR_NUM=$(gh pr view --json number -q .number 2>/dev/null) || err "no pull request found for the current branch" + +# Fetch current user, repo owner, and repo name +ME=$(gh api user -q .login 2>/dev/null) || err "failed to fetch GitHub user - are you authenticated? Run 'gh auth login'" +OWNER=$(gh repo view --json owner -q .owner.login 2>/dev/null) || err "failed to determine repository owner" +REPO=$(gh repo view --json name -q .name 2>/dev/null) || err "failed to determine repository name" + +# Fetch unresolved review threads via GraphQL +OUTPUT=$(gh api graphql -f query=' +query { + repository(owner: "'"$OWNER"'", name: "'"$REPO"'") { + pullRequest(number: '"$PR_NUM"') { + reviewThreads(first: 100) { + nodes { + isResolved + comments(first: 100) { + nodes { + author { login } + body + path + line + } + } + } + } + } + } +}' --jq ' +[ + .data.repository.pullRequest.reviewThreads.nodes[] + | select(.isResolved == false) + | { + file: .comments.nodes[0].path, + line: .comments.nodes[0].line, + comments: [ + .comments.nodes[] + | select(.author.login != "'"$ME"'") + | {author: .author.login, body: .body} + ] + } + | select(.comments | length > 0) +] +| .[] +| "## \(.file):\(.line)\n\(.comments | map("- **\(.author)**: \(.body)") | join("\n"))\n" +' 2>/dev/null) || err "GraphQL query failed - check your permissions and token scopes" + +# Format output +if [[ -z "$OUTPUT" ]]; then + echo "No unresolved review comments found." +else + echo "$OUTPUT" | sed 's/\\n/\n/g' +fi