diff --git a/greploop/SKILL.md b/greploop/SKILL.md index 3fcad84..442a776 100644 --- a/greploop/SKILL.md +++ b/greploop/SKILL.md @@ -1,35 +1,77 @@ --- 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 +79,32 @@ 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,61 +112,153 @@ 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 -HEAD_SHA=$(gh pr view --json headRefOid -q .headRefOid) +for i in $(seq 1 30); do + 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... (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') -# 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..." + 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 - - 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 + + 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: $STATUS)" + + 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):** +```bash +glab api "projects/:fullpath/merge_requests//notes" +``` -Parse the review body for: +Filter for notes from the Greptile bot user (check the `author.username` field — the exact username may vary per installation; verify on first run). -- **Confidence score**: Greptile includes a score like `3/5` or `5/5` in its review summary. -- **Comment count**: Number of inline review comments. +**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. + +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 @@ -137,7 +278,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=' @@ -169,15 +310,38 @@ 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 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 @@ -191,6 +355,7 @@ After exiting the loop, summarize: | Field | Value | | ------------------ | ---------- | +| Platform | GitHub / GitLab / Perforce | | Iterations | N | | Final confidence | X/5 | | Comments resolved | N | @@ -202,6 +367,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 @@ -212,6 +378,7 @@ If not fully resolved: ``` Greploop stopped after 5 iterations. + Platform: GitLab Confidence: 4/5 Resolved: 12 comments Remaining: 2 @@ -220,3 +387,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 +```