Initial commit: .claude folder with agents and skills

This commit is contained in:
Harivansh Rathi 2026-03-23 23:10:37 -04:00
commit aa9a785e26
42 changed files with 3367 additions and 0 deletions

View file

@ -0,0 +1,165 @@
---
name: implement-review-loop
description: Implement code from a plan, create PRs with Graphite CLI (gt create/submit), wait for code reviews, and iterate on feedback. Use when the user says "implement", "implement the plan", "create PR", "review loop", or wants to go from plan to merged PR. This skill handles the full cycle - implement code, create branch with Graphite, submit PR, wait for reviews (without wasting tokens), read comments, fix code, and push updates.
---
# Implement Review Loop
End-to-end workflow for implementing code from a plan, creating PRs with Graphite, and iterating on review feedback.
## Prerequisites
- Graphite CLI installed (`gt --version`)
- GitHub CLI installed (`gh --version`)
- Repository initialized with Graphite (`gt init` if not)
- Devin Review available via npx (`npx devin-review`)
## Workflow Overview
```
Plan -> Implement -> Create Branch (gt) -> Submit PR -> Wait for Review -> Read Comments -> Fix -> Push -> Repeat
```
## Phase 1: Implement the Plan
1. Read the current plan (from context, file, or conversation)
2. Implement all changes specified in the plan
3. Run tests and linting to verify implementation
4. Stage changes: `git add <specific-files>`
## Phase 2: Create Branch and PR with Graphite
Create branch with auto-generated name from commit message:
```bash
gt create --all --message "feat: <description from plan>"
```
Submit the PR with auto-generated description:
```bash
gt submit --ai --stack
```
Or with manual title/description:
```bash
gt submit --edit-title --edit-description
```
Capture the PR URL from output for later use.
## Phase 3: Request Code Review (Optional)
Run Devin Review for AI-powered code review:
```bash
cd /path/to/repo
npx devin-review https://github.com/owner/repo/pull/123
```
This creates an isolated worktree and sends diff to Devin for analysis.
## Phase 4: Wait for Reviews (Token-Efficient)
Use the polling script to wait without consuming tokens:
```bash
./scripts/poll_pr_comments.sh owner/repo 123 --timeout 60 --interval 30
```
The script:
- Polls GitHub API every 30 seconds (configurable)
- Returns when new comments are detected
- Exits after timeout with no-comments status
- Does NOT consume Claude context while waiting
For CI checks:
```bash
./scripts/wait_for_checks.sh owner/repo 123 --timeout 15
```
## Phase 5: Read and Address Comments
Fetch all comments for review:
```bash
./scripts/fetch_pr_comments.sh owner/repo 123
```
This outputs:
- Inline review comments with file:line locations
- PR-level comments
- Review decision status
For each comment:
1. Read the feedback
2. Implement the fix
3. Stage the changes
## Phase 6: Push Updates
Amend current commit and push:
```bash
gt modify --all
gt submit
```
Or create a new fixup commit:
```bash
gt create --all --message "fix: address review feedback"
gt submit
```
## Phase 7: Loop Until Approved
Repeat phases 4-6 until:
- All comments are resolved
- PR is approved
- Ready to merge
## Quick Reference
| Task | Command |
| -------------- | ---------------------------------------- |
| Create branch | `gt create -am "feat: description"` |
| Submit PR | `gt submit --ai --stack` |
| View PR | `gt pr` |
| Amend changes | `gt modify -a` |
| Push updates | `gt submit` |
| Poll comments | `./scripts/poll_pr_comments.sh repo pr` |
| Fetch comments | `./scripts/fetch_pr_comments.sh repo pr` |
| Devin review | `npx devin-review <pr-url>` |
## Bundled Scripts
### poll_pr_comments.sh
Polls for new PR comments without consuming context:
```bash
./scripts/poll_pr_comments.sh <owner/repo> <pr-number> [--timeout <min>] [--interval <sec>]
```
### wait_for_checks.sh
Waits for GitHub checks to complete:
```bash
./scripts/wait_for_checks.sh <owner/repo> <pr-number> [--timeout <min>]
```
### fetch_pr_comments.sh
Fetches and formats all PR comments:
```bash
./scripts/fetch_pr_comments.sh <owner/repo> <pr-number> [--unresolved-only]
```

View file

@ -0,0 +1,86 @@
#!/usr/bin/env bash
#
# Fetch all PR comments (both inline review comments and PR-level comments)
# Outputs structured JSON for Claude to process
#
# Usage: fetch_pr_comments.sh <owner/repo> <pr-number> [--unresolved-only]
#
# Example:
# fetch_pr_comments.sh myorg/myrepo 123
# fetch_pr_comments.sh myorg/myrepo 123 --unresolved-only
#
set -euo pipefail
REPO="${1:-}"
PR_NUMBER="${2:-}"
UNRESOLVED_ONLY=false
usage() {
echo "Usage: fetch_pr_comments.sh <owner/repo> <pr-number> [--unresolved-only]"
echo ""
echo "Options:"
echo " --unresolved-only Only show unresolved review threads"
echo ""
echo "Example:"
echo " fetch_pr_comments.sh myorg/myrepo 123"
exit 1
}
if [[ -z "$REPO" || -z "$PR_NUMBER" ]]; then
usage
fi
shift 2
while [[ $# -gt 0 ]]; do
case "$1" in
--unresolved-only)
UNRESOLVED_ONLY=true
shift
;;
*)
usage
;;
esac
done
echo "# PR #${PR_NUMBER} Comments"
echo ""
# Fetch inline review comments
echo "## Inline Review Comments"
echo ""
REVIEW_COMMENTS=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/comments" \
--jq '.[] | "### \(.path):\(.line // .original_line // "N/A")\n**Author:** \(.user.login)\n**Created:** \(.created_at)\n\n\(.body)\n\n---\n"' 2>/dev/null || echo "No inline comments")
if [[ -n "$REVIEW_COMMENTS" && "$REVIEW_COMMENTS" != "No inline comments" ]]; then
echo "$REVIEW_COMMENTS"
else
echo "No inline review comments."
echo ""
fi
# Fetch PR-level comments (issue comments)
echo "## PR-Level Comments"
echo ""
ISSUE_COMMENTS=$(gh api "repos/${REPO}/issues/${PR_NUMBER}/comments" \
--jq '.[] | "### Comment by \(.user.login)\n**Created:** \(.created_at)\n\n\(.body)\n\n---\n"' 2>/dev/null || echo "No PR comments")
if [[ -n "$ISSUE_COMMENTS" && "$ISSUE_COMMENTS" != "No PR comments" ]]; then
echo "$ISSUE_COMMENTS"
else
echo "No PR-level comments."
echo ""
fi
# Fetch review threads with resolution status
echo "## Review Threads Summary"
echo ""
gh pr view "$PR_NUMBER" -R "$REPO" --json reviews,reviewDecision \
--jq '"Review Decision: \(.reviewDecision // "PENDING")\n\nReviews:\n" + (.reviews | map("- \(.author.login): \(.state)") | join("\n"))' 2>/dev/null || echo "Could not fetch review summary"
echo ""

View file

@ -0,0 +1,109 @@
#!/usr/bin/env bash
#
# Poll for new PR comments using GitHub CLI
# Returns when new comments are found or timeout is reached
#
# Usage: poll_pr_comments.sh <owner/repo> <pr-number> [--timeout <minutes>] [--interval <seconds>]
#
# Example:
# poll_pr_comments.sh myorg/myrepo 123 --timeout 30 --interval 60
#
set -euo pipefail
REPO="${1:-}"
PR_NUMBER="${2:-}"
TIMEOUT_MINUTES=60
POLL_INTERVAL=30
INITIAL_COMMENT_COUNT=""
usage() {
echo "Usage: poll_pr_comments.sh <owner/repo> <pr-number> [--timeout <minutes>] [--interval <seconds>]"
echo ""
echo "Options:"
echo " --timeout <minutes> Maximum time to wait (default: 60)"
echo " --interval <seconds> Time between polls (default: 30)"
echo ""
echo "Example:"
echo " poll_pr_comments.sh myorg/myrepo 123 --timeout 30 --interval 60"
exit 1
}
if [[ -z "$REPO" || -z "$PR_NUMBER" ]]; then
usage
fi
shift 2
while [[ $# -gt 0 ]]; do
case "$1" in
--timeout)
TIMEOUT_MINUTES="$2"
shift 2
;;
--interval)
POLL_INTERVAL="$2"
shift 2
;;
*)
usage
;;
esac
done
get_comment_count() {
local review_comments issue_comments total
# Get review comments (inline code comments)
review_comments=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/comments" --jq 'length' 2>/dev/null || echo "0")
# Get issue comments (PR-level comments)
issue_comments=$(gh api "repos/${REPO}/issues/${PR_NUMBER}/comments" --jq 'length' 2>/dev/null || echo "0")
total=$((review_comments + issue_comments))
echo "$total"
}
get_unresolved_threads() {
# Get review threads that are not resolved
gh pr view "$PR_NUMBER" -R "$REPO" --json reviewDecision,reviews,latestReviews 2>/dev/null || echo "{}"
}
echo "Polling for comments on PR #${PR_NUMBER} in ${REPO}"
echo "Timeout: ${TIMEOUT_MINUTES} minutes, Poll interval: ${POLL_INTERVAL} seconds"
echo ""
# Get initial comment count
INITIAL_COMMENT_COUNT=$(get_comment_count)
echo "Initial comment count: ${INITIAL_COMMENT_COUNT}"
START_TIME=$(date +%s)
TIMEOUT_SECONDS=$((TIMEOUT_MINUTES * 60))
while true; do
CURRENT_TIME=$(date +%s)
ELAPSED=$((CURRENT_TIME - START_TIME))
if [[ $ELAPSED -ge $TIMEOUT_SECONDS ]]; then
echo ""
echo "Timeout reached after ${TIMEOUT_MINUTES} minutes"
echo "No new comments detected"
exit 0
fi
CURRENT_COUNT=$(get_comment_count)
if [[ "$CURRENT_COUNT" -gt "$INITIAL_COMMENT_COUNT" ]]; then
NEW_COMMENTS=$((CURRENT_COUNT - INITIAL_COMMENT_COUNT))
echo ""
echo "NEW_COMMENTS_DETECTED: ${NEW_COMMENTS} new comment(s) found!"
echo "Total comments: ${CURRENT_COUNT}"
exit 0
fi
REMAINING=$((TIMEOUT_SECONDS - ELAPSED))
REMAINING_MINS=$((REMAINING / 60))
echo -ne "\rWaiting... (${REMAINING_MINS}m remaining, current count: ${CURRENT_COUNT}) "
sleep "$POLL_INTERVAL"
done

View file

@ -0,0 +1,99 @@
#!/usr/bin/env bash
#
# Wait for GitHub checks to complete on a PR
# Returns check status when all checks complete or timeout is reached
#
# Usage: wait_for_checks.sh <owner/repo> <pr-number> [--timeout <minutes>]
#
# Example:
# wait_for_checks.sh myorg/myrepo 123 --timeout 15
#
set -euo pipefail
REPO="${1:-}"
PR_NUMBER="${2:-}"
TIMEOUT_MINUTES=30
POLL_INTERVAL=15
usage() {
echo "Usage: wait_for_checks.sh <owner/repo> <pr-number> [--timeout <minutes>]"
echo ""
echo "Options:"
echo " --timeout <minutes> Maximum time to wait (default: 30)"
echo ""
echo "Example:"
echo " wait_for_checks.sh myorg/myrepo 123 --timeout 15"
exit 1
}
if [[ -z "$REPO" || -z "$PR_NUMBER" ]]; then
usage
fi
shift 2
while [[ $# -gt 0 ]]; do
case "$1" in
--timeout)
TIMEOUT_MINUTES="$2"
shift 2
;;
*)
usage
;;
esac
done
echo "Waiting for checks on PR #${PR_NUMBER} in ${REPO}"
echo "Timeout: ${TIMEOUT_MINUTES} minutes"
echo ""
START_TIME=$(date +%s)
TIMEOUT_SECONDS=$((TIMEOUT_MINUTES * 60))
while true; do
CURRENT_TIME=$(date +%s)
ELAPSED=$((CURRENT_TIME - START_TIME))
if [[ $ELAPSED -ge $TIMEOUT_SECONDS ]]; then
echo ""
echo "TIMEOUT: Checks did not complete within ${TIMEOUT_MINUTES} minutes"
exit 1
fi
# Get check status
CHECK_STATUS=$(gh pr checks "$PR_NUMBER" -R "$REPO" 2>/dev/null || echo "error")
# Check if all checks passed
if echo "$CHECK_STATUS" | grep -q "All checks were successful"; then
echo ""
echo "CHECKS_PASSED: All checks successful"
exit 0
fi
# Check if any checks failed
if echo "$CHECK_STATUS" | grep -qE "fail|error"; then
echo ""
echo "CHECKS_FAILED: Some checks failed"
echo "$CHECK_STATUS"
exit 1
fi
# Check if checks are still pending
PENDING=$(echo "$CHECK_STATUS" | grep -c "pending\|queued\|in_progress" || true)
if [[ "$PENDING" -eq 0 ]]; then
# No pending checks and no failures means success
echo ""
echo "CHECKS_COMPLETE"
echo "$CHECK_STATUS"
exit 0
fi
REMAINING=$((TIMEOUT_SECONDS - ELAPSED))
REMAINING_MINS=$((REMAINING / 60))
echo -ne "\rWaiting for checks... (${REMAINING_MINS}m remaining, ${PENDING} pending) "
sleep "$POLL_INTERVAL"
done