From e54b56f3bbacd2eb925f067c8f7782e2b0deba92 Mon Sep 17 00:00:00 2001 From: cameronvarley Date: Mon, 30 Mar 2026 22:56:44 -0400 Subject: [PATCH 1/4] fix(greploop): simplify polling loop to use gh pr checks --- greploop/SKILL.md | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/greploop/SKILL.md b/greploop/SKILL.md index 3fcad84..edf6124 100644 --- a/greploop/SKILL.md +++ b/greploop/SKILL.md @@ -66,33 +66,15 @@ fi Then poll for the Greptile check to complete: ```bash -HEAD_SHA=$(gh pr view --json headRefOid -q .headRefOid) - -# Poll for Greptile check run to complete (check runs, not action runs) -while true; do - GREPTILE_CHECK=$(gh api "repos/{owner}/{repo}/commits/$HEAD_SHA/check-runs" \ - --jq '.check_runs[] | select(.name | test("greptile"; "i"))' 2>/dev/null) - - if [ -z "$GREPTILE_CHECK" ]; then - echo "Waiting for Greptile check to appear..." - sleep 5 - continue - fi - - STATUS=$(echo "$GREPTILE_CHECK" | jq -r '.status // "completed"') - CONCLUSION=$(echo "$GREPTILE_CHECK" | jq -r '.conclusion // "pending"') - - if [ "$STATUS" = "completed" ]; then - if [ "$CONCLUSION" = "success" ]; then - echo "Greptile check passed!" - else - echo "Greptile check completed with: $CONCLUSION" - fi +for i in $(seq 1 30); do + CHECKS=$(gh pr checks --json name,state 2>/dev/null) + STATE=$(echo "$CHECKS" | jq -r '.[] | select(.name | test("greptile"; "i")) | .state' 2>/dev/null) + if [ "$STATE" = "SUCCESS" ]; then + echo "Greptile passed!" break fi - - echo "Waiting for Greptile... (status: $STATUS)" - sleep 10 + echo "Waiting for Greptile... ($STATE) attempt $i" + sleep 30 done ``` From 562f82d43f9e1a3a5fa5036a6d442fd9cf3d63c2 Mon Sep 17 00:00:00 2001 From: cameronvarley Date: Mon, 30 Mar 2026 23:04:27 -0400 Subject: [PATCH 2/4] fix(greploop): use --jq flag and add failure/error handling --- greploop/SKILL.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/greploop/SKILL.md b/greploop/SKILL.md index edf6124..f5790a1 100644 --- a/greploop/SKILL.md +++ b/greploop/SKILL.md @@ -67,15 +67,20 @@ Then poll for the Greptile check to complete: ```bash for i in $(seq 1 30); do - CHECKS=$(gh pr checks --json name,state 2>/dev/null) - STATE=$(echo "$CHECKS" | jq -r '.[] | select(.name | test("greptile"; "i")) | .state' 2>/dev/null) + STATE=$(gh pr checks --json name,state --jq '.[] | select(.name | test("greptile"; "i")) | .state | ascii_upcase' 2>/dev/null) + if [ "$STATE" = "SUCCESS" ]; then echo "Greptile passed!" break + elif [ "$STATE" = "FAILURE" ] || [ "$STATE" = "ERROR" ]; then + echo "Greptile failed ($STATE)" + exit 1 fi - echo "Waiting for Greptile... ($STATE) attempt $i" + + echo "Waiting for Greptile... (Current state: ${STATE:-PENDING}) attempt $i" sleep 30 done + ``` #### B. Fetch Greptile review results From 4be7af329189544da6762d51f1eca08a08a10e28 Mon Sep 17 00:00:00 2001 From: Cameron Varley Date: Fri, 10 Apr 2026 11:43:49 -0400 Subject: [PATCH 3/4] fix(greploop): enhance documentation for PR/MR/CL handling and platform detection --- greploop/SKILL.md | 274 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 243 insertions(+), 31 deletions(-) diff --git a/greploop/SKILL.md b/greploop/SKILL.md index f5790a1..538bc24 100644 --- a/greploop/SKILL.md +++ b/greploop/SKILL.md @@ -1,35 +1,81 @@ --- name: greploop description: > - Iteratively improves a PR until Greptile gives it a 5/5 confidence score with zero - unresolved comments. Triggers Greptile review, fixes all actionable comments, pushes, - re-triggers review, and repeats. Use when the user wants to fully optimize a PR against - Greptile's code review standards. + Iteratively improves a PR (GitHub), MR (GitLab), or shelved changelist (Perforce) until Greptile + gives it a 5/5 confidence score with zero unresolved comments. Triggers Greptile review, fixes all + actionable comments, pushes/re-shelves, re-triggers review, and repeats. Use when the user wants to + fully optimize a PR/MR/CL against Greptile's code review standards. license: MIT -compatibility: Requires git, gh (GitHub CLI) authenticated, and Greptile installed on the repo. +compatibility: Requires git, gh (GitHub CLI) or glab (GitLab CLI) authenticated, and Greptile installed on the repo. For Perforce, requires p4 CLI authenticated. metadata: author: greptileai - version: "1.0" -allowed-tools: Bash(gh:*) Bash(git:*) + version: "1.2" +allowed-tools: Bash(gh:*) Bash(glab:*) Bash(git:*) Bash(p4:*) --- # Greploop -Iteratively fix a PR until Greptile gives a perfect review: 5/5 confidence, zero unresolved comments. +Iteratively fix a PR/MR/CL until Greptile gives a perfect review: 5/5 confidence, zero unresolved comments. ## Inputs -- **PR number** (optional): If not provided, detect the PR for the current branch. +- **PR/MR/CL number** (optional): If not provided, detect the PR/MR for the current branch, or the default pending changelist for p4. ## Instructions -### 1. Identify the PR +### 0. Detect platform + +First check for Perforce, then fall back to git remote detection: + +```bash +# Check for Perforce environment +if p4 info >/dev/null 2>&1; then + VCS="perforce" +else + REMOTE_URL=$(git remote get-url origin) + if echo "$REMOTE_URL" | grep -qi "gitlab"; then + VCS="gitlab" + else + VCS="github" + fi +fi +``` + +For self-hosted GitLab instances whose hostname doesn't contain "gitlab", the user can override by passing `--vcs gitlab` as an input. For Perforce, pass `--vcs perforce`. + +### 1. Identify the PR/MR/CL + +**GitHub:** ```bash gh pr view --json number,headRefName -q '{number: .number, branch: .headRefName}' ``` -Switch to the PR branch if not already on it. +**GitLab:** + +```bash +glab mr view --output json | jq '{iid: .iid, branch: .source_branch}' +``` + +Switch to the PR/MR branch if not already on it. + +**Perforce:** + +```bash +# List pending changelists for current user/client +p4 changes -s pending -u $P4USER -c $P4CLIENT + +# Describe a specific CL +p4 describe -s +``` + +Ensure the correct workspace (`p4 client`) is set before proceeding. + +Key field differences: + +- GitHub: `number`, `headRefName`, `headRefOid` +- GitLab: `iid`, `source_branch`, `sha` +- Perforce: changelist number, `P4CLIENT`, shelved files ### 2. Loop @@ -37,25 +83,34 @@ Repeat the following cycle. **Max 5 iterations** to avoid runaway loops. #### A. Trigger Greptile review -Push the latest changes (if any): +Push/shelve the latest changes (if any): + +**GitHub/GitLab:** ```bash git push ``` -Wait for checks to start after push: +**Perforce:** + +```bash +# Re-shelve to update the shelved files for review +p4 shelve -f -c +``` + +Wait for checks to start after push/shelve: ```bash sleep 5 ``` -Check if Greptile is already running on this PR before posting a new trigger comment: +**GitHub** — check if Greptile is already running before posting a new trigger comment: ```bash GREPTILE_STATE=$(gh pr checks --json name,state | jq -r '.[] | select(.name | test("greptile"; "i")) | .state') ``` -If Greptile is **not** already running (`PENDING` or `IN_PROGRESS`), request a fresh review by posting a PR comment (Greptile watches for this trigger): +If Greptile is **not** already running (`PENDING` or `IN_PROGRESS`), request a fresh review: ```bash if [ "$GREPTILE_STATE" != "PENDING" ] && [ "$GREPTILE_STATE" != "IN_PROGRESS" ]; then @@ -63,7 +118,7 @@ if [ "$GREPTILE_STATE" != "PENDING" ] && [ "$GREPTILE_STATE" != "IN_PROGRESS" ]; fi ``` -Then poll for the Greptile check to complete: +Then poll for the Greptile check run to complete: ```bash for i in $(seq 1 30); do @@ -80,31 +135,148 @@ for i in $(seq 1 30); do echo "Waiting for Greptile... (Current state: ${STATE:-PENDING}) attempt $i" sleep 30 done +``` +**GitLab** — check if Greptile is already running before posting a trigger comment: + +```bash +PIPELINES=$(glab api "projects/:fullpath/merge_requests//pipelines") +GREPTILE_RUNNING=$(echo "$PIPELINES" | jq '[.[] | select(.status == "running" or .status == "pending")] | length') +``` + +If no pipeline is running, post a trigger comment: + +```bash +if [ "$GREPTILE_RUNNING" = "0" ]; then + glab mr note --message "@greptile review" +fi +``` + +**Perforce** — Perforce does not have native check runs. If Greptile is integrated via a webhook triggered on `p4 shelve`, wait for it to process. Check your Greptile installation's webhook endpoint or dashboard for the review status. Poll by re-fetching the Greptile review comment on the CL until a score appears. + +Then poll for the Greptile pipeline job to complete (see [GitLab API reference](references/gitlab-api.md)): + +```bash +HEAD_SHA=$(glab mr view --output json | jq -r '.sha') + +while true; do + PIPELINES=$(glab api "projects/:fullpath/merge_requests//pipelines") + # Find the most recent pipeline for this SHA + PIPELINE_ID=$(echo "$PIPELINES" | jq -r --arg sha "$HEAD_SHA" \ + '[.[] | select(.sha == $sha)] | sort_by(.id) | last | .id // empty') + + if [ -z "$PIPELINE_ID" ]; then + echo "Waiting for Greptile pipeline to appear..." + sleep 5 + continue + fi + + JOBS=$(glab api "projects/:fullpath/pipelines/$PIPELINE_ID/jobs") + GREPTILE_JOB=$(echo "$JOBS" | jq '.[] | select(.name | test("greptile"; "i"))') + + if [ -z "$GREPTILE_JOB" ]; then + echo "Waiting for Greptile job to appear..." + sleep 5 + continue + fi + + JOB_STATUS=$(echo "$GREPTILE_JOB" | jq -r '.status') + + if [ "$JOB_STATUS" = "success" ] || [ "$JOB_STATUS" = "failed" ] || [ "$JOB_STATUS" = "canceled" ]; then + echo "Greptile job completed with: $JOB_STATUS" + break + fi + + echo "Waiting for Greptile... (status: $JOB_STATUS)" + sleep 10 +done ``` #### B. Fetch Greptile review results -Get the latest review from Greptile: +Greptile may surface its score in two places — check **both** (three for Perforce): + +**GitHub:** + +**1. PR description (body):** + +```bash +gh pr view --json body -q '.body' +``` + +**2. PR reviews:** ```bash gh api repos/{owner}/{repo}/pulls//reviews ``` -Look for the most recent review from `greptile-apps[bot]` or `greptile-apps-staging[bot]`. +Look for the most recent entry from `greptile-apps[bot]` or `greptile-apps-staging[bot]`. + +**GitLab:** + +**1. MR description (body):** + +```bash +glab mr view --output json | jq -r '.description' +``` + +**2. MR notes (comments):** -Parse the review body for: +```bash +glab api "projects/:fullpath/merge_requests//notes" +``` + +Filter for notes from the Greptile bot user (check the `author.username` field — the exact username may vary per installation; verify on first run). + +**Perforce:** -- **Confidence score**: Greptile includes a score like `3/5` or `5/5` in its review summary. -- **Comment count**: Number of inline review comments. +**1. CL description:** + +```bash +p4 describe -s +``` + +Check the description field for a Greptile-appended score block. + +**2. CL comments / review notes:** + +```bash +p4 review -c +``` + +Look for comments from the Greptile bot user. The exact mechanism depends on your Greptile installation (it may post via a review tool like Swarm or directly via `p4 review`). + +For all platforms, parse the text for: + +- **Confidence score**: a pattern like `3/5` or `5/5` (or `Confidence: 3/5`). +- **Comment count**: Number of inline review comments noted in the summary. + +Use whichever source has the **most recent** score. Also fetch all unresolved inline comments: +**GitHub:** + ```bash gh api repos/{owner}/{repo}/pulls//comments ``` -Filter to comments from Greptile that are on the latest commit. +**GitLab:** + +```bash +glab api "projects/:fullpath/merge_requests//discussions" +``` + +Filter to `DiffNote` type discussions (`notes[0].type == "DiffNote"`) from Greptile that are on the latest commit and not yet resolved (`"resolved": false`). + +**Perforce:** + +```bash +# Fetch inline diff comments from Greptile bot on the shelved CL +p4 review -c +``` + +Filter to comments from the Greptile bot user that have not been marked as resolved/addressed. #### C. Check exit conditions @@ -124,7 +296,7 @@ For each unresolved Greptile comment: #### E. Resolve threads -Fetch unresolved review threads and resolve all that have been addressed (see [GraphQL reference](references/graphql-queries.md)): +**GitHub** — fetch unresolved review threads and resolve all that have been addressed (see [GraphQL reference](references/graphql-queries.md)): ```bash gh api graphql -f query=' @@ -156,7 +328,25 @@ mutation { }' ``` -#### F. Commit and push +**GitLab** — fetch unresolved discussions and resolve each one (see [GitLab API reference](references/gitlab-api.md)): + +```bash +glab api "projects/:fullpath/merge_requests//discussions?per_page=100" +``` + +Filter for `"resolved": false` discussions. Then resolve each by its `id`: + +```bash +glab api --method PUT \ + "projects/:fullpath/merge_requests//discussions/" \ + --field resolved=true +``` + +Repeat for each unresolved discussion ID. (GitLab has no batch resolution — loop through each one.) + +#### F. Commit and push / re-shelve + +**GitHub/GitLab:** ```bash git add -A @@ -164,7 +354,14 @@ git commit -m "address greptile review feedback (greploop iteration N)" git push ``` -Wait for checks to start after push: +**Perforce:** + +```bash +# Stage changes back into the CL and re-shelve for the next review round +p4 shelve -f -c +``` + +Wait for checks to start after push/shelve: ```bash sleep 5 @@ -176,12 +373,13 @@ Then go back to step **A**. After exiting the loop, summarize: -| Field | Value | -| ------------------ | ---------- | -| Iterations | N | -| Final confidence | X/5 | -| Comments resolved | N | -| Remaining comments | N (if any) | +| Field | Value | +| ------------------ | -------------------------- | +| Platform | GitHub / GitLab / Perforce | +| Iterations | N | +| Final confidence | X/5 | +| Comments resolved | N | +| Remaining comments | N (if any) | If the loop exited due to max iterations, list any remaining unresolved comments and suggest next steps. @@ -189,6 +387,7 @@ If the loop exited due to max iterations, list any remaining unresolved comments ``` Greploop complete. + Platform: GitHub Iterations: 2 Confidence: 5/5 Resolved: 7 comments @@ -199,6 +398,7 @@ If not fully resolved: ``` Greploop stopped after 5 iterations. + Platform: GitLab Confidence: 4/5 Resolved: 12 comments Remaining: 2 @@ -207,3 +407,15 @@ Remaining issues: - src/auth.ts:45 — "Consider rate limiting this endpoint" - src/db.ts:112 — "Missing index on user_id column" ``` + +**Perforce example:** + +``` +Greploop complete. + Platform: Perforce + Changelist: 12345 + Iterations: 3 + Confidence: 5/5 + Resolved: 9 comments + Remaining: 0 +``` From fb2109ce94a1922e7e46ac6257610cab37081389 Mon Sep 17 00:00:00 2001 From: Cameron Varley Date: Fri, 10 Apr 2026 11:50:12 -0400 Subject: [PATCH 4/4] merge: bring branch up to date with main, keep polling loop fix Rebase onto main (which now includes GitLab/Perforce support from merged PRs #9 and #10) while preserving the `gh pr checks` polling loop improvement over the `while true` check-runs API approach. Co-Authored-By: Claude Sonnet 4.6 --- greploop/SKILL.md | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/greploop/SKILL.md b/greploop/SKILL.md index 538bc24..442a776 100644 --- a/greploop/SKILL.md +++ b/greploop/SKILL.md @@ -46,13 +46,11 @@ For self-hosted GitLab instances whose hostname doesn't contain "gitlab", the us ### 1. Identify the PR/MR/CL **GitHub:** - ```bash gh pr view --json number,headRefName -q '{number: .number, branch: .headRefName}' ``` **GitLab:** - ```bash glab mr view --output json | jq '{iid: .iid, branch: .source_branch}' ``` @@ -60,7 +58,6 @@ glab mr view --output json | jq '{iid: .iid, branch: .source_branch}' Switch to the PR/MR branch if not already on it. **Perforce:** - ```bash # List pending changelists for current user/client p4 changes -s pending -u $P4USER -c $P4CLIENT @@ -72,7 +69,6 @@ p4 describe -s Ensure the correct workspace (`p4 client`) is set before proceeding. Key field differences: - - GitHub: `number`, `headRefName`, `headRefOid` - GitLab: `iid`, `source_branch`, `sha` - Perforce: changelist number, `P4CLIENT`, shelved files @@ -86,13 +82,11 @@ Repeat the following cycle. **Max 5 iterations** to avoid runaway loops. Push/shelve the latest changes (if any): **GitHub/GitLab:** - ```bash git push ``` **Perforce:** - ```bash # Re-shelve to update the shelved files for review p4 shelve -f -c @@ -199,13 +193,11 @@ Greptile may surface its score in two places — check **both** (three for Perfo **GitHub:** **1. PR description (body):** - ```bash gh pr view --json body -q '.body' ``` **2. PR reviews:** - ```bash gh api repos/{owner}/{repo}/pulls//reviews ``` @@ -215,13 +207,11 @@ Look for the most recent entry from `greptile-apps[bot]` or `greptile-apps-stagi **GitLab:** **1. MR description (body):** - ```bash glab mr view --output json | jq -r '.description' ``` **2. MR notes (comments):** - ```bash glab api "projects/:fullpath/merge_requests//notes" ``` @@ -231,23 +221,18 @@ Filter for notes from the Greptile bot user (check the `author.username` field **Perforce:** **1. CL description:** - ```bash p4 describe -s ``` - Check the description field for a Greptile-appended score block. **2. CL comments / review notes:** - ```bash p4 review -c ``` - Look for comments from the Greptile bot user. The exact mechanism depends on your Greptile installation (it may post via a review tool like Swarm or directly via `p4 review`). For all platforms, parse the text for: - - **Confidence score**: a pattern like `3/5` or `5/5` (or `Confidence: 3/5`). - **Comment count**: Number of inline review comments noted in the summary. @@ -256,13 +241,11 @@ Use whichever source has the **most recent** score. Also fetch all unresolved inline comments: **GitHub:** - ```bash gh api repos/{owner}/{repo}/pulls//comments ``` **GitLab:** - ```bash glab api "projects/:fullpath/merge_requests//discussions" ``` @@ -270,7 +253,6 @@ glab api "projects/:fullpath/merge_requests//discussions" Filter to `DiffNote` type discussions (`notes[0].type == "DiffNote"`) from Greptile that are on the latest commit and not yet resolved (`"resolved": false`). **Perforce:** - ```bash # Fetch inline diff comments from Greptile bot on the shelved CL p4 review -c @@ -347,7 +329,6 @@ Repeat for each unresolved discussion ID. (GitLab has no batch resolution — lo #### F. Commit and push / re-shelve **GitHub/GitLab:** - ```bash git add -A git commit -m "address greptile review feedback (greploop iteration N)" @@ -355,7 +336,6 @@ git push ``` **Perforce:** - ```bash # Stage changes back into the CL and re-shelve for the next review round p4 shelve -f -c @@ -373,13 +353,13 @@ Then go back to step **A**. After exiting the loop, summarize: -| Field | Value | -| ------------------ | -------------------------- | +| Field | Value | +| ------------------ | ---------- | | Platform | GitHub / GitLab / Perforce | -| Iterations | N | -| Final confidence | X/5 | -| Comments resolved | N | -| Remaining comments | N (if any) | +| Iterations | N | +| Final confidence | X/5 | +| Comments resolved | N | +| Remaining comments | N (if any) | If the loop exited due to max iterations, list any remaining unresolved comments and suggest next steps.