@olegkoval/agent-skills 1.49.2 → 1.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.github/copilot-instructions.md +1 -0
  2. package/.github/prompts/coderabbitloop.prompt.md +90 -15
  3. package/.github/prompts/codexloop.prompt.md +33 -6
  4. package/.github/prompts/copilotloop.prompt.md +302 -0
  5. package/.github/prompts/geminiloop.prompt.md +34 -6
  6. package/.github/prompts/qodoloop.prompt.md +18 -0
  7. package/.kiro/steering/coderabbitloop.md +90 -15
  8. package/.kiro/steering/codexloop.md +33 -6
  9. package/.kiro/steering/copilotloop.md +301 -0
  10. package/.kiro/steering/geminiloop.md +34 -6
  11. package/.kiro/steering/qodoloop.md +18 -0
  12. package/.windsurf/rules/coderabbitloop.md +90 -15
  13. package/.windsurf/rules/codexloop.md +33 -6
  14. package/.windsurf/rules/copilotloop.md +300 -0
  15. package/.windsurf/rules/geminiloop.md +34 -6
  16. package/.windsurf/rules/qodoloop.md +18 -0
  17. package/README.md +1 -0
  18. package/adapters/claude/olko-github-pr/skills/coderabbitloop/SKILL.md +91 -16
  19. package/adapters/claude/olko-github-pr/skills/coderabbitloop/references/graphql-queries.md +41 -9
  20. package/adapters/claude/olko-github-pr/skills/codexloop/SKILL.md +35 -8
  21. package/adapters/claude/olko-github-pr/skills/copilotloop/SKILL.md +309 -0
  22. package/adapters/claude/olko-github-pr/skills/geminiloop/SKILL.md +36 -8
  23. package/adapters/claude/olko-github-pr/skills/lekker-review/SKILL.md +26 -1
  24. package/adapters/claude/olko-github-pr/skills/lekker-review/references/agents/completeness-critic.md +12 -0
  25. package/adapters/claude/olko-github-pr/skills/lekker-review/references/agents/implementation.md +26 -1
  26. package/adapters/claude/olko-github-pr/skills/lekker-review/references/agents/prover.md +8 -1
  27. package/adapters/claude/olko-github-pr/skills/lekker-review/references/agents/test-quality.md +20 -2
  28. package/adapters/claude/olko-github-pr/skills/lekker-review/references/agents/verifier.md +25 -0
  29. package/adapters/claude/olko-github-pr/skills/lekker-review/references/fix-mode.md +23 -0
  30. package/adapters/claude/olko-github-pr/skills/qodoloop/SKILL.md +19 -1
  31. package/adapters/claude/olko-github-pr/skills/qodoloop/references/graphql-queries.md +19 -2
  32. package/adapters/codex/olko-github-pr/README.md +1 -0
  33. package/adapters/cursor/olko-github-pr/skills/coderabbitloop/SKILL.md +91 -16
  34. package/adapters/cursor/olko-github-pr/skills/coderabbitloop/references/graphql-queries.md +41 -9
  35. package/adapters/cursor/olko-github-pr/skills/codexloop/SKILL.md +35 -8
  36. package/adapters/cursor/olko-github-pr/skills/copilotloop/SKILL.md +309 -0
  37. package/adapters/cursor/olko-github-pr/skills/geminiloop/SKILL.md +36 -8
  38. package/adapters/cursor/olko-github-pr/skills/qodoloop/SKILL.md +19 -1
  39. package/adapters/cursor/olko-github-pr/skills/qodoloop/references/graphql-queries.md +19 -2
  40. package/adapters/grok/olko-github-pr/skills/coderabbitloop/SKILL.md +91 -16
  41. package/adapters/grok/olko-github-pr/skills/coderabbitloop/references/graphql-queries.md +41 -9
  42. package/adapters/grok/olko-github-pr/skills/codexloop/SKILL.md +35 -8
  43. package/adapters/grok/olko-github-pr/skills/copilotloop/SKILL.md +309 -0
  44. package/adapters/grok/olko-github-pr/skills/geminiloop/SKILL.md +36 -8
  45. package/adapters/grok/olko-github-pr/skills/qodoloop/SKILL.md +19 -1
  46. package/adapters/grok/olko-github-pr/skills/qodoloop/references/graphql-queries.md +19 -2
  47. package/catalog/skills.json +23 -0
  48. package/package.json +1 -1
  49. package/plugins/olko-apple-kit/.claude-plugin/plugin.json +1 -1
  50. package/plugins/olko-creative/.claude-plugin/plugin.json +1 -1
  51. package/plugins/olko-garmin-kit/.claude-plugin/plugin.json +1 -1
  52. package/plugins/olko-git-tools/.claude-plugin/plugin.json +1 -1
  53. package/plugins/olko-github-pr/.claude-plugin/plugin.json +1 -1
  54. package/plugins/olko-github-pr/skills/coderabbitloop/SKILL.md +91 -16
  55. package/plugins/olko-github-pr/skills/coderabbitloop/references/graphql-queries.md +41 -9
  56. package/plugins/olko-github-pr/skills/codexloop/SKILL.md +35 -8
  57. package/plugins/olko-github-pr/skills/copilotloop/SKILL.md +308 -0
  58. package/plugins/olko-github-pr/skills/geminiloop/SKILL.md +36 -8
  59. package/plugins/olko-github-pr/skills/lekker-review/SKILL.md +26 -1
  60. package/plugins/olko-github-pr/skills/lekker-review/references/agents/completeness-critic.md +12 -0
  61. package/plugins/olko-github-pr/skills/lekker-review/references/agents/implementation.md +26 -1
  62. package/plugins/olko-github-pr/skills/lekker-review/references/agents/prover.md +8 -1
  63. package/plugins/olko-github-pr/skills/lekker-review/references/agents/test-quality.md +20 -2
  64. package/plugins/olko-github-pr/skills/lekker-review/references/agents/verifier.md +25 -0
  65. package/plugins/olko-github-pr/skills/lekker-review/references/fix-mode.md +23 -0
  66. package/plugins/olko-github-pr/skills/lekker-review/workflow.js +87 -5
  67. package/plugins/olko-github-pr/skills/qodoloop/SKILL.md +19 -1
  68. package/plugins/olko-github-pr/skills/qodoloop/references/graphql-queries.md +19 -2
  69. package/plugins/olko-obsidian/.claude-plugin/plugin.json +1 -1
  70. package/plugins/olko-product/.claude-plugin/plugin.json +1 -1
  71. package/plugins/olko-reflection/.claude-plugin/plugin.json +1 -1
  72. package/plugins/olko-release/.claude-plugin/plugin.json +1 -1
  73. package/plugins/olko-skill-meta/.claude-plugin/plugin.json +1 -1
  74. package/plugins/olko-web-ops/.claude-plugin/plugin.json +1 -1
  75. package/scripts/lib/catalog.mjs +1 -1
@@ -9,7 +9,7 @@ description: >
9
9
  CodeRabbit" / "resolve CodeRabbit comments".
10
10
  compatibility: Requires git and gh (GitHub CLI) authenticated, with the CodeRabbit GitHub App installed on the repo.
11
11
  metadata:
12
- version: "1.0"
12
+ version: "1.1"
13
13
  allowed-tools: Bash(gh:*) Bash(git:*)
14
14
  ---
15
15
  <!-- Generated by scripts/build-adapters.sh. Do not edit directly. -->
@@ -46,13 +46,30 @@ CodeRabbit has slash commands (official, `docs.coderabbit.ai/reference/review-co
46
46
  gh pr view --json number,headRefName,headRefOid -q '{number, branch: .headRefName, sha: .headRefOid}'
47
47
  ```
48
48
 
49
- Check out the branch if not already on it.
49
+ Check out the branch if not already on it, **then require a clean working tree**
50
+ (`git status --porcelain` empty) before touching anything:
51
+
52
+ ```bash
53
+ [ -z "$(git status --porcelain)" ] || { echo "working tree not clean, refusing to start: would risk committing unrelated changes"; exit 1; }
54
+ ```
55
+
56
+ This is the precondition step E's scoped `git add <file>` depends on: staging only the files a fix
57
+ touched is safe *because* the tree started clean. Skip it and a file with pre-existing local edits
58
+ gets those edits swept into the fix commit right along with it.
50
59
 
51
60
  ### 2. Loop (max 5 iterations)
52
61
 
53
- #### A. Push and wait
62
+ Keep an explicit iteration counter and stop at 5: the cap is a real bound to enforce, not a figure
63
+ of speech. Each pass through A–F is one iteration; on hitting the cap, go straight to the report.
64
+
65
+ #### A. Wait for a review of the current head
66
+
67
+ On **iteration 1**, if there is nothing to push yet (you haven't fixed anything in this loop), don't
68
+ wait for a brand-new review: use whatever CodeRabbit has already posted as your starting point and
69
+ go straight to step B. Otherwise:
54
70
 
55
71
  ```bash
72
+ SINCE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
56
73
  git push
57
74
  ```
58
75
 
@@ -62,11 +79,27 @@ If this repo doesn't auto-review on push, nudge it:
62
79
  gh pr comment <PR_NUMBER> --body "@coderabbitai review"
63
80
  ```
64
81
 
65
- Poll (every ~10s, timeout ~5min) for CodeRabbit's next formal review to land: a new entry in `gh api repos/{owner}/{repo}/pulls/<PR_NUMBER>/reviews` from `coderabbitai[bot]` created after this push. If you instead see a rate-limit comment ("review limit"), **stop the loop now** and go to Report with `blocked: rate-limited`.
82
+ Poll (every ~10s, timeout ~5min) for CodeRabbit's next formal review to land: a new entry in
83
+ `gh api --paginate repos/{owner}/{repo}/pulls/<PR_NUMBER>/reviews` from `coderabbitai[bot]`
84
+ (REST spelling carries the `[bot]` suffix) created after `$SINCE`. Follow pagination so a review
85
+ that lands on page two is not missed. If you instead see a rate-limit comment ("review limit"),
86
+ **stop the loop now** and go to Report with `blocked: rate-limited`. If no review arrives within
87
+ the timeout after one retry, stop and report the timeout honestly; do not claim success.
66
88
 
67
89
  #### B. Fetch unresolved findings
68
90
 
69
- Run the `unresolvedQodoThreads`-shaped query in `references/graphql-queries.md` (filter `author.login == "coderabbitai[bot]"` instead of Qodo's login). For each unresolved thread, parse the finding text and the **Prompt for AI Agents** block (heading text is `🤖 Prompt for AI Agents`, not `Agent Prompt`: match case/emoji-insensitively).
91
+ Run the `unresolvedQodoThreads`-shaped query in `references/graphql-queries.md` (filter `author.login`
92
+ on GraphQL's bare `coderabbitai`, **not** REST's `coderabbitai[bot]`: see the reference's login-spelling
93
+ note, and follow `pageInfo.hasNextPage`/`endCursor` until exhausted). For each unresolved thread,
94
+ parse the finding text, its `databaseId`, and the **Prompt for AI Agents** block (heading text is
95
+ `🤖 Prompt for AI Agents`, not `Agent Prompt`: match case/emoji-insensitively).
96
+
97
+ Mark each thread as seen with an 👀 reaction on its first comment (`databaseId` from the query), so
98
+ the bot and a watching human can tell it is being worked:
99
+
100
+ ```bash
101
+ gh api --method POST repos/{owner}/{repo}/pulls/comments/<DATABASE_ID>/reactions -f content=eyes
102
+ ```
70
103
 
71
104
  #### C. Check exit conditions
72
105
 
@@ -74,26 +107,68 @@ Stop if there are zero unresolved CodeRabbit threads, or max iterations reached.
74
107
 
75
108
  #### D. Fix each finding
76
109
 
77
- For each thread, in order: read the Prompt for AI Agents block in full: it names the issue and the fix. Apply it, staying inside its stated scope. If the suggestion is wrong, unsafe, or needs a product decision, don't force it: note that in step E instead.
110
+ For each thread, in order: read the Prompt for AI Agents block in full: it names the issue and the fix. Apply it, staying inside its stated scope. If the suggestion is wrong, unsafe, or needs a product decision, don't force it: note that in step F instead. Track two lists as you go: **fixed** (thread id + the exact files you touched) and **skipped** (thread id + why). Do not resolve anything yet.
111
+
112
+ #### E. Commit and push FIRST, before resolving anything
78
113
 
79
- #### E. Answer and resolve
114
+ Order matters. A resolved thread is a claim that the fix is on the branch, so the push has to succeed
115
+ before the claim is made: otherwise a failed commit or push leaves the PR unfixed with the finding
116
+ marked resolved, and nobody looks at it again.
80
117
 
81
- **Reply to the thread first, then resolve it**, the "answer to the comments" half of the job:
118
+ If step D changed code:
82
119
 
83
120
  ```bash
84
- gh api graphql -f query='mutation { addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: "<THREAD_ID>", body: "<REPLY>"}) { comment { id } } }'
121
+ # Stage ONLY the files your fixes touched: never `git add -A`, which sweeps up
122
+ # unrelated work and untracked secrets sitting in the worktree.
123
+ git status --short # look before you stage
124
+ git add <path> [<path>...] # the files named in the findings you fixed
125
+ git commit -m "address CodeRabbit review feedback (coderabbitloop iteration N)"
126
+ git push
85
127
  ```
86
128
 
87
- Reply body: one or two sentences, what you changed and why, or exactly why you didn't fix it. Then resolve (`resolveReviewThread`). A finding you deliberately skip still gets a reply and gets resolved.
129
+ Confirm the push actually landed before continuing:
88
130
 
89
- #### F. Commit and push
131
+ ```bash
132
+ git rev-parse HEAD
133
+ gh pr view <PR_NUMBER> --json headRefOid -q .headRefOid # must match
134
+ ```
135
+
136
+ If they differ, stop: the fix is not on the PR, so nothing may be resolved yet.
137
+
138
+ #### F. Reply to each finding, react, then resolve
139
+
140
+ Only now, with the fixes pushed, reply and resolve. **Reply to the thread first, then react, then
141
+ resolve it**, the "answer to the comments" half of the job:
90
142
 
91
143
  ```bash
92
- git add -A
93
- git commit -m "address CodeRabbit review feedback (coderabbitloop iteration N)"
94
- git push
144
+ gh api graphql -f query='
145
+ mutation($threadId: ID!, $body: String!) {
146
+ addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: $threadId, body: $body}) {
147
+ comment { id }
148
+ }
149
+ }' -f threadId="$THREAD_ID" -f body="$REPLY_TEXT"
95
150
  ```
96
151
 
152
+ Pass the thread id and reply text as GraphQL **variables** (`-f threadId=... -f body=...`), never
153
+ interpolated into the query string: a reply containing a quote, backtick, or newline breaks (or
154
+ injects into) a hand-built query.
155
+
156
+ Reply body: one or two sentences, what you changed and why, or exactly why you didn't fix it. A
157
+ finding you deliberately skip still gets a reply and gets resolved.
158
+
159
+ Then record the disposition as a reaction on the thread's first comment: the learning signal, and a
160
+ visible marker for a human scanning the PR. 👍 (`+1`) for fixed, 👎 (`-1`) for skipped/rebutted.
161
+ CodeRabbit reads a 👎 as feedback that the finding was wrong.
162
+
163
+ ```bash
164
+ gh api --method POST repos/{owner}/{repo}/pulls/comments/<DATABASE_ID>/reactions -f content=+1
165
+ # skipped/rebutted finding:
166
+ gh api --method POST repos/{owner}/{repo}/pulls/comments/<DATABASE_ID>/reactions -f content=-1
167
+ ```
168
+
169
+ Then resolve (`resolveReviewThread`). Reply first, react, then resolve: a reaction never replaces
170
+ the reply, and a resolved thread with no reply loses the reasoning.
171
+
97
172
  Go back to step A.
98
173
 
99
174
  ### 3. Report
@@ -104,7 +179,7 @@ Go back to step A.
104
179
  | Findings resolved | N |
105
180
  | Findings skipped | N (with reasons) |
106
181
  | Remaining | N (if any) |
107
- | Blocked | rate-limited / max-iterations / none |
182
+ | Blocked | rate-limited / timeout / max-iterations / none |
108
183
 
109
184
  ```
110
185
  Coderabbitloop complete.
@@ -115,4 +190,4 @@ Coderabbitloop complete.
115
190
 
116
191
  ## References
117
192
 
118
- - `references/graphql-queries.md`: shared with `qodoloop`: the exact `reviewThreads` query and the `resolveReviewThread` / `addPullRequestReviewThreadReply` mutations. Swap the author-login filter to `coderabbitai[bot]` and the prompt-heading match to `Prompt for AI Agents`.
193
+ - `references/graphql-queries.md`: shared with `qodoloop`: the exact `reviewThreads` query (including the `databaseId` the reaction step needs) and the `resolveReviewThread` / `addPullRequestReviewThreadReply` mutations. Filter `author.login` on GraphQL's bare `coderabbitai` (never `coderabbitai[bot]`) and match the prompt heading `Prompt for AI Agents`.
@@ -6,6 +6,15 @@ against CodeRabbit's own documentation (`docs.coderabbit.ai/reference/review-com
6
6
  plus a real `<!-- ... summarize by coderabbit.ai -->` comment observed live:
7
7
  not guessed.
8
8
 
9
+ **The bot's login string is spelled differently in REST vs GraphQL.** REST
10
+ (`.user.login` on `pulls/.../comments`) returns `coderabbitai[bot]`. GraphQL
11
+ (`author.login` on `reviewThreads`) returns `coderabbitai`, no `[bot]` suffix
12
+ (confirmed live against PR #114's own threads). Using the REST spelling in a
13
+ GraphQL filter silently matches nothing, which reads as "no findings" instead
14
+ of "wrong filter": exactly the kind of bug that has no crash and no error to
15
+ notice. Use `[bot]` only in REST-based jq filters (`.user.login`); use the
16
+ bare name everywhere you're filtering a GraphQL `author.login`.
17
+
9
18
  ## Fetch unresolved CodeRabbit threads (paginated)
10
19
 
11
20
  ```graphql
@@ -18,7 +27,7 @@ query($cursor: String) {
18
27
  id
19
28
  isResolved
20
29
  comments(first: 1) {
21
- nodes { body path line author { login } }
30
+ nodes { databaseId body path line author { login } }
22
31
  }
23
32
  }
24
33
  }
@@ -27,12 +36,13 @@ query($cursor: String) {
27
36
  }
28
37
  ```
29
38
 
30
- Filter client-side to `coderabbitai[bot]` and `isResolved == false`:
39
+ Filter client-side to GraphQL's `coderabbitai` (bare name, see the note above)
40
+ and `isResolved == false`:
31
41
 
32
42
  ```bash
33
43
  gh api graphql -f query='...' --jq \
34
44
  '.data.repository.pullRequest.reviewThreads.nodes[]
35
- | select(.comments.nodes[0].author.login == "coderabbitai[bot]")
45
+ | select(.comments.nodes[0].author.login == "coderabbitai")
36
46
  | select(.isResolved == false)'
37
47
  ```
38
48
 
@@ -53,14 +63,18 @@ description text above it: not every low-confidence nit gets one.
53
63
 
54
64
  ```bash
55
65
  gh api graphql -f query='
56
- mutation {
57
- addPullRequestReviewThreadReply(input: {
58
- pullRequestReviewThreadId: "THREAD_ID"
59
- body: "REPLY_TEXT"
60
- }) { comment { id } }
61
- }'
66
+ mutation($threadId: ID!, $body: String!) {
67
+ addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: $threadId, body: $body}) {
68
+ comment { id }
69
+ }
70
+ }' -f threadId="$THREAD_ID" -f body="$REPLY_TEXT"
62
71
  ```
63
72
 
73
+ Pass the thread id and reply text as GraphQL **variables** (`-f`), never interpolated straight into
74
+ the query string. A reply containing a quote, backtick, or embedded newline breaks (or, worse,
75
+ injects into) a hand-built query string; `gh api graphql -f` handles the escaping correctly when the
76
+ value travels as a variable instead.
77
+
64
78
  `pullRequestReviewThreadId` and `body` are the only required fields.
65
79
 
66
80
  ## Resolve a thread (batch)
@@ -81,6 +95,24 @@ marks every one of its threads resolved at once server-side. That's a valid
81
95
  bulk escape hatch if the user wants to blow through remaining low-value nits,
82
96
  but it skips the per-finding reply: don't use it as the default path.
83
97
 
98
+ ## React to a comment (the bot's learning signal)
99
+
100
+ Reacting needs the comment's REST id, which is why `databaseId` is selected in
101
+ the thread query above. The reaction set is fixed; only eight values exist:
102
+ `+1`, `-1`, `laugh`, `confused`, `heart`, `hooray`, `rocket`, `eyes`.
103
+
104
+ ```bash
105
+ # 👀 as soon as you start working a finding
106
+ gh api --method POST repos/{owner}/{repo}/pulls/comments/<DATABASE_ID>/reactions -f content=eyes
107
+ # 👍 when it was accepted and fixed, 👎 when it was rebutted (the correction signal)
108
+ gh api --method POST repos/{owner}/{repo}/pulls/comments/<DATABASE_ID>/reactions -f content=+1
109
+ gh api --method POST repos/{owner}/{repo}/pulls/comments/<DATABASE_ID>/reactions -f content=-1
110
+ ```
111
+
112
+ React after the reply and before the resolve, so GitHub records the reason and
113
+ the disposition together. A reaction never replaces the reply. CodeRabbit reads
114
+ a 👎 on one of its comments as feedback that the finding was wrong.
115
+
84
116
  ## Fetch the walkthrough/summary comment (context only, not the source of truth)
85
117
 
86
118
  ```bash
@@ -6,9 +6,9 @@ description: >
6
6
  ones, and rebut + resolve false positives WITHOUT changing correct code. Repeat until no
7
7
  unresolved Codex comments remain. Use when the user says "satisfy codex", "clear the codex
8
8
  review", "fix codex comments", "codex loop", or wants to iterate on Codex PR review feedback.
9
- compatibility: GitHub only (Codex code review is a GitHub app). Requires git + gh (GitHub CLI) authenticated, and the Codex / ChatGPT connector app installed on the repo with code review enabled.
9
+ compatibility: GitHub only (Codex code review is a GitHub app). Requires git + jq + gh (GitHub CLI) authenticated, and the Codex / ChatGPT connector app installed on the repo with code review enabled.
10
10
  metadata:
11
- version: "1.1"
11
+ version: "1.2"
12
12
  allowed-tools: Bash(gh:*) Bash(git:*)
13
13
  ---
14
14
  <!-- Generated by scripts/build-adapters.sh. Do not edit directly. -->
@@ -37,7 +37,7 @@ suggestion applied is worse than the comment itself.
37
37
  ## Not for
38
38
 
39
39
  - GitLab / Perforce (Codex code review is a GitHub app). For other review bots this catalog ships
40
- `geminiloop`, `coderabbitloop`, and `qodoloop`; for CI failures rather than review comments, use
40
+ `copilotloop`, `geminiloop`, `coderabbitloop`, and `qodoloop`; for CI failures rather than review comments, use
41
41
  `ci-fix-loop`.
42
42
 
43
43
  ## 0. Resolve the Codex bot login (do this first, do not hardcode)
@@ -84,9 +84,13 @@ report and list what is still unresolved rather than starting a sixth.
84
84
 
85
85
  ```bash
86
86
  HEAD_SHA=$(gh pr view <PR> --json headRefOid -q .headRefOid)
87
- # Only trigger if no Codex review already exists for this exact SHA:
88
- HAVE=$(gh api repos/{owner}/{repo}/pulls/<PR>/reviews --paginate \
89
- --jq "[.[] | select(.user.login==\"$BOT\" and .commit_id==\"$HEAD_SHA\")] | length")
87
+ # Only trigger if no Codex review already exists for this exact SHA.
88
+ # gh cannot combine --slurp with --jq, so pipe to `jq -s`: it folds every page
89
+ # into one array. Without it the filter runs once per page and HAVE becomes a
90
+ # multi-line count that never equals "0", silently skipping the trigger.
91
+ HAVE=$(gh api repos/{owner}/{repo}/pulls/<PR>/reviews --paginate |
92
+ jq -s --arg bot "$BOT" --arg head "$HEAD_SHA" \
93
+ '[.[][] | select(.user.login==$bot and .commit_id==$head)] | length')
90
94
  if [ "$HAVE" = "0" ]; then gh pr comment <PR> --body "@codex review"; fi
91
95
  ```
92
96
 
@@ -100,8 +104,9 @@ an honest timeout, not hang it.
100
104
  wait_for_review() { # $1 = attempt label
101
105
  local deadline=$(( SECONDS + 600 ))
102
106
  while [ "$SECONDS" -lt "$deadline" ]; do
103
- R=$(gh api repos/{owner}/{repo}/pulls/<PR>/reviews --paginate \
104
- --jq "[.[] | select(.user.login==\"$BOT\" and .commit_id==\"$HEAD_SHA\")] | last")
107
+ R=$(gh api repos/{owner}/{repo}/pulls/<PR>/reviews --paginate |
108
+ jq -s --arg bot "$BOT" --arg head "$HEAD_SHA" \
109
+ '[.[][] | select(.user.login==$bot and .commit_id==$head)] | sort_by(.submitted_at, .id) | last')
105
110
  if [ -n "$R" ] && [ "$R" != "null" ]; then return 0; fi
106
111
  echo "waiting for Codex review of $HEAD_SHA ($1)..."; sleep 15
107
112
  done
@@ -135,6 +140,13 @@ gh api repos/{owner}/{repo}/pulls/<PR>/comments --paginate \
135
140
  Also pull the review threads + their resolved state via GraphQL (see step F) so you only act on
136
141
  unresolved ones.
137
142
 
143
+ Mark each unresolved comment as seen, so the bot and a watching human can tell it is being handled.
144
+ GitHub's reaction set is fixed; `eyes` is the only "looking at it" value:
145
+
146
+ ```bash
147
+ gh api --method POST repos/{owner}/{repo}/pulls/comments/<COMMENT_ID>/reactions -f content=eyes
148
+ ```
149
+
138
150
  ### C. Critically evaluate EACH comment (the core of this skill)
139
151
 
140
152
  For every comment, **verify the claim against the actual code and repo conventions before touching
@@ -236,6 +248,21 @@ then resolve:
236
248
  gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "THREAD_ID"}) { thread { isResolved } } }'
237
249
  ```
238
250
 
251
+ Then record the disposition as a reaction on the bot's own comment: the learning signal, and a
252
+ visible marker for a human scanning the PR:
253
+
254
+ - 👍 (`+1`): the finding was accepted and fixed.
255
+ - 👎 (`-1`): the finding was rebutted as a false positive. **This is the correction signal.**
256
+
257
+ ```bash
258
+ gh api --method POST repos/{owner}/{repo}/pulls/comments/<COMMENT_ID>/reactions -f content=+1
259
+ # rebutted finding:
260
+ gh api --method POST repos/{owner}/{repo}/pulls/comments/<COMMENT_ID>/reactions -f content=-1
261
+ ```
262
+
263
+ Reply first, then react, then resolve: the reaction is not a substitute for the reply, and a
264
+ resolved thread with no reply loses the reasoning.
265
+
239
266
  Resolve a thread only for comments authored by `$BOT` that you have fixed or rebutted: never
240
267
  blanket-resolve, and never resolve a human reviewer's thread. For each fixed comment, verify the
241
268
  push first, reply with the change made, then resolve the thread and verify `isResolved: true`.
@@ -0,0 +1,309 @@
1
+ ---
2
+ name: copilotloop
3
+ description: >
4
+ Iteratively satisfy GitHub Copilot's code review on a GitHub PR, but treat every Copilot comment
5
+ SKEPTICALLY: verify each finding against the real code first, fix ONLY the genuinely-correct
6
+ ones, and rebut + resolve false positives WITHOUT changing correct code. Repeat until no
7
+ unresolved Copilot comments remain. Use when the user says "satisfy Copilot", "clear the Copilot
8
+ review", "fix Copilot comments", "Copilot loop", or wants to iterate on Copilot PR feedback.
9
+ compatibility: GitHub only (Copilot code review is a GitHub feature). Requires git + jq + gh (GitHub CLI) authenticated, Copilot code review enabled, and permission to request reviewers on the repository.
10
+ metadata:
11
+ version: "1.1"
12
+ allowed-tools: Bash(gh:*) Bash(git:*)
13
+ ---
14
+ <!-- Generated by scripts/build-adapters.sh. Do not edit directly. -->
15
+
16
+ # Copilotloop
17
+
18
+ Drive a GitHub PR until Copilot has no unresolved review comments, **but do not cargo-cult its
19
+ suggestions.** Every comment is a *claim to verify*, not an instruction to obey. A wrong
20
+ suggestion applied is worse than the comment itself.
21
+
22
+ ## How Copilot differs from other review bots
23
+
24
+ - **No check-run, no score.** Copilot posts an overview comment and a PR review with inline review
25
+ comments. Detection is by polling the reviews endpoint and paginated review threads. "Satisfied"
26
+ means zero unresolved Copilot threads and zero unaddressed body findings after each finding is
27
+ fixed or rebutted with a recorded reply.
28
+ - **Request through the reviewers API.** Copilot does not use a reliable slash-command trigger.
29
+ Request `copilot-pull-request-reviewer[bot]` through the pull request reviewers endpoint. A
30
+ repository may also be configured to review new pushes automatically.
31
+ - **Copilot may repeat comments.** A re-review can repeat comments that were resolved or downvoted,
32
+ so match findings to the current head and prior replies before reopening work, including body
33
+ findings that have no inline thread.
34
+ - **Copilot is concise and can reason from the diff alone.** Its characteristic failure is
35
+ confidently asserting a bug based only on the changed hunk, without the surrounding file or the
36
+ call sites. That makes "read the whole file before believing it" the single highest-value check.
37
+
38
+ ## Not for
39
+
40
+ - GitLab / Perforce. For other review bots this catalog ships `codexloop`, `geminiloop`,
41
+ `coderabbitloop`, and `qodoloop`; for CI failures rather than review comments, use
42
+ `ci-fix-loop`.
43
+
44
+ ## 0. Resolve the Copilot bot login (do this first)
45
+
46
+ The bot login is normally stable, but confirm it from the PR rather than assuming it:
47
+
48
+ ```bash
49
+ gh api repos/{owner}/{repo}/pulls/<PR>/reviews --paginate --jq '.[].user.login' | sort -u
50
+ gh api repos/{owner}/{repo}/pulls/<PR>/comments --paginate --jq '.[].user.login' | sort -u
51
+ ```
52
+
53
+ Use `copilot-pull-request-reviewer[bot]` as `BOT`. Confirm it appears after the first requested
54
+ review; if GitHub uses a different login for the installation, use the exact Copilot-looking login
55
+ returned by the probe rather than guessing.
56
+
57
+ **Finding nothing here is NOT a stop condition.** On a PR Copilot has never reviewed there is no bot
58
+ login to find yet. Fall through to step 2A, request the reviewer, and re-run this probe once a
59
+ review lands. Only conclude Copilot is unavailable after the bounded wait expires with no review.
60
+
61
+ ## 1. Identify the PR
62
+
63
+ ```bash
64
+ gh pr view --json number,headRefName,headRefOid -q '{number,branch:.headRefName,head:.headRefOid}'
65
+ ```
66
+
67
+ Switch to the PR branch if not already on it. Capture `OWNER`/`REPO` (`gh repo view --json owner,name`).
68
+
69
+ ## 2. The loop (max 5 iterations)
70
+
71
+ Keep an explicit iteration counter and stop at 5: the cap is a real bound to enforce, not a
72
+ figure of speech. Each pass through A–G is one iteration; on hitting the cap, go straight to the
73
+ report and list what is still unresolved rather than starting a sixth.
74
+
75
+ ### A. Ensure a fresh Copilot review on the current head
76
+
77
+ ```bash
78
+ HEAD_SHA=$(gh pr view <PR> --json headRefOid -q .headRefOid)
79
+ # Only trigger if no Copilot review exists for this exact SHA:
80
+ HAVE=$(gh api repos/{owner}/{repo}/pulls/<PR>/reviews --paginate |
81
+ jq -s --arg bot "$BOT" --arg head "$HEAD_SHA" \
82
+ '[.[][] | select(.user.login==$bot and .commit_id==$head)] | length')
83
+ if [ "$HAVE" = "0" ]; then
84
+ gh api --method POST repos/{owner}/{repo}/pulls/<PR>/requested_reviewers \
85
+ -f 'reviewers[]=copilot-pull-request-reviewer[bot]'
86
+ fi
87
+ ```
88
+
89
+ Poll for the review of THIS head to land. No check-run exists, so poll the reviews endpoint, and
90
+ poll it on a **deadline**, never `while true`: a review that never arrives must end the skill with
91
+ an honest timeout, not hang it.
92
+
93
+ ```bash
94
+ # 10-minute deadline, one retry, then give up. DEADLINE/RETRIES are the
95
+ # enforcement of the bounds this skill claims: do not drop them.
96
+ wait_for_review() { # $1 = attempt label
97
+ local deadline=$(( SECONDS + 600 ))
98
+ while [ "$SECONDS" -lt "$deadline" ]; do
99
+ R=$(gh api repos/{owner}/{repo}/pulls/<PR>/reviews --paginate |
100
+ jq -s --arg bot "$BOT" --arg head "$HEAD_SHA" \
101
+ '[.[][] | select(.user.login==$bot and .commit_id==$head)] | sort_by(.submitted_at, .id) | last')
102
+ if [ -n "$R" ] && [ "$R" != "null" ]; then return 0; fi
103
+ echo "waiting for Copilot review of $HEAD_SHA ($1)..."; sleep 15
104
+ done
105
+ return 1
106
+ }
107
+
108
+ if ! wait_for_review "first wait"; then
109
+ echo "no Copilot review after 10m, retrying once" # say the retry out loud
110
+ gh api --method POST repos/{owner}/{repo}/pulls/<PR>/requested_reviewers \
111
+ -f 'reviewers[]=copilot-pull-request-reviewer[bot]'
112
+ if ! wait_for_review "after retry"; then
113
+ echo "Copilot did not review $HEAD_SHA after a retry; stopping and reporting."
114
+ exit 1 # honest timeout, never a success claim
115
+ fi
116
+ fi
117
+ ```
118
+
119
+ Copilot usually reviews quickly, but large diffs can take longer; the deadline is bounded. Report the
120
+ retry in the final summary; two silent timeouts are the failure mode this guard exists to prevent.
121
+
122
+ ### B. Fetch the findings
123
+
124
+ - **Summary**: read the Copilot overview comment and review `.body`, including all matching reviews
125
+ for the current head. Extract and classify actionable claims even when they have no inline thread;
126
+ an approval assessment or lack of inline comments does not make those findings complete.
127
+ Track each body finding across iterations by source review/comment ID and item, with its
128
+ classification, fix commit or evidence-based rebuttal, PR-level reply URL, and completion status.
129
+ Link duplicates to the same finding so they are counted once; retain pending findings after a push.
130
+ - **Unresolved inline comments** on the current head:
131
+
132
+ ```bash
133
+ gh api repos/{owner}/{repo}/pulls/<PR>/comments --paginate \
134
+ --jq ".[] | select(.user.login==\"$BOT\") | {id, path, line, body}"
135
+ ```
136
+
137
+ Also pull the review threads + their resolved state via GraphQL (see step F) so you only act on
138
+ unresolved ones.
139
+
140
+ Mark each unresolved inline comment as seen, so the bot and a watching human can tell it is being
141
+ handled. GitHub's reaction set is fixed; `eyes` is the only "looking at it" value:
142
+
143
+ ```bash
144
+ gh api --method POST repos/{owner}/{repo}/pulls/comments/<COMMENT_ID>/reactions -f content=eyes
145
+ ```
146
+
147
+ Body findings (no inline thread) have no comment to react to; track them in step B's register and
148
+ give each a PR-level reply in step F instead.
149
+
150
+ ### C. Critically evaluate EACH finding (the core of this skill)
151
+
152
+ For every inline or body finding, **verify the claim against the actual code and repo conventions
153
+ before touching anything.** Read the whole file, not just the diff hunk Copilot saw, plus the types
154
+ and the call sites. Then classify:
155
+
156
+ 1. **CORRECT + actionable**: the finding is real and the fix improves the code. → fix it (step D).
157
+ 2. **FALSE POSITIVE / technically wrong**: the claim doesn't hold. → do **NOT** change code; write a
158
+ specific, evidence-based reply (cite the exact code/line/behavior that disproves it), then resolve
159
+ its thread or record completion for a body finding (step F).
160
+ 3. **Valid but out-of-scope / stylistic nit** that conflicts with repo convention or the PR's intent
161
+ → briefly decline with evidence of that convention or scope, then resolve its thread or record
162
+ completion for a body finding (step F). Do not expand the PR's scope to satisfy a nit.
163
+
164
+ **Hard rules:**
165
+ - **Never modify correct code just to silence Copilot.** Prefer a reasoned rebuttal.
166
+ - When uncertain whether a claim holds, **investigate** (read more code, run the type-checker / tests)
167
+ rather than assume Copilot is right. Default to skepticism.
168
+ - If a suggested change would break other call sites, alter public behavior, or contradict a verified
169
+ repo convention, it is a category-2 rebuttal, not a fix.
170
+ - Never fabricate identifiers to satisfy a comment (e.g. a Linear/ticket prefix). If Copilot asks for a
171
+ ticket reference and none exists, say so; do not invent one.
172
+
173
+ **Copilot's common failure modes to watch for (default these to category 2):**
174
+ - Diff-local reasoning: asserts a bug that the unchanged surrounding code already handles.
175
+ - "This can be null/undefined here" where the type or an earlier guard already rules it out.
176
+ - Invented race conditions or error paths with no actual trigger.
177
+ - Suggestions that compile-break or break other callers.
178
+ - Security/perf warnings with no exploit path or measurable cost.
179
+ - Restating library/framework semantics incorrectly.
180
+ - Style demands that contradict the repo's existing, consistent pattern.
181
+
182
+ ### D. Apply fixes: category 1 only
183
+
184
+ Make the minimal correct change. Re-run the local gate if the repo has one (typecheck/tests) before
185
+ moving on.
186
+
187
+ ### E. Commit and push FIRST, before resolving anything
188
+
189
+ Order matters. A resolved thread is a claim that the fix is on the branch, so the push has to
190
+ succeed before the claim is made: otherwise a failed commit or push leaves the PR unfixed with the
191
+ finding marked resolved, and nobody looks at it again.
192
+
193
+ If step D changed code:
194
+
195
+ ```bash
196
+ # Stage ONLY the files your fixes touched: never `git add -A`, which sweeps up
197
+ # unrelated work and untracked secrets sitting in the worktree.
198
+ git status --short # look before you stage
199
+ git add <path> [<path>...] # the files named in the findings you fixed
200
+ git commit -m "address Copilot review feedback (copilotloop iteration N)"
201
+ git push
202
+ ```
203
+
204
+ Author the commit per the repo's norms (e.g. the user's identity; no AI attribution if that is the
205
+ convention). Confirm the push actually landed before continuing:
206
+
207
+ ```bash
208
+ git rev-parse HEAD
209
+ gh pr view <PR> --json headRefOid -q .headRefOid # must match
210
+ ```
211
+
212
+ If they differ, stop: the fix is not on the PR, so nothing may be resolved yet.
213
+
214
+ ### F. Reply to every addressed finding and resolve its inline threads
215
+
216
+ Only now, with the fixes pushed, reply and resolve. Fetch unresolved threads, **following
217
+ pagination**: a PR with more than 100 threads will otherwise look clean while unresolved findings
218
+ sit on page two:
219
+
220
+ ```bash
221
+ # Loop until hasNextPage is false, passing endCursor back in as $cursor.
222
+ CURSOR=null
223
+ while : ; do
224
+ PAGE=$(gh api graphql -F cursor="$CURSOR" -f query='
225
+ query($cursor: String) {
226
+ repository(owner: "OWNER", name: "REPO") {
227
+ pullRequest(number: PR_NUMBER) {
228
+ reviewThreads(first: 100, after: $cursor) {
229
+ pageInfo { hasNextPage endCursor }
230
+ nodes { id isResolved comments(first: 1) { nodes { databaseId author { login } path body } } }
231
+ }
232
+ }
233
+ }
234
+ }')
235
+ echo "$PAGE" # collect nodes from every page before deciding the PR is clean
236
+ PI='.data.repository.pullRequest.reviewThreads.pageInfo'
237
+ [ "$(echo "$PAGE" | jq -r "$PI.hasNextPage")" = "true" ] || break
238
+ CURSOR=$(echo "$PAGE" | jq -r "$PI.endCursor")
239
+ done
240
+ ```
241
+
242
+ Reply on a thread's comment via `gh api repos/{owner}/{repo}/pulls/<PR>/comments -f body="..." -F in_reply_to=<comment_id>`,
243
+ then resolve:
244
+
245
+ ```bash
246
+ gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "THREAD_ID"}) { thread { isResolved } } }'
247
+ ```
248
+
249
+ Then record the disposition of each inline finding as a reaction on Copilot's own comment: the
250
+ learning signal, and a visible marker for a human scanning the PR:
251
+
252
+ - 👍 (`+1`): the finding was accepted and fixed.
253
+ - 👎 (`-1`): the finding was rebutted as a false positive. **This is the correction signal.**
254
+
255
+ ```bash
256
+ gh api --method POST repos/{owner}/{repo}/pulls/comments/<COMMENT_ID>/reactions -f content=+1
257
+ # rebutted finding:
258
+ gh api --method POST repos/{owner}/{repo}/pulls/comments/<COMMENT_ID>/reactions -f content=-1
259
+ ```
260
+
261
+ Reply first, then react, then resolve: the reaction is not a substitute for the reply, and a
262
+ resolved thread with no reply loses the reasoning.
263
+
264
+ Resolve a thread only for comments authored by `$BOT` that you have fixed or rebutted: never
265
+ blanket-resolve, and never resolve a human reviewer's thread.
266
+
267
+ Threads you are **rebutting** need no push, so they may be replied to and resolved regardless of
268
+ whether step D changed code.
269
+
270
+ For each body finding, post a PR-level reply (for example, using
271
+ `gh pr comment <PR> --body-file <reply-file>`) identifying the source review/comment and item.
272
+ Record the pushed fix commit and validation, or an evidence-based rebuttal, including the reason
273
+ for declining an out-of-scope finding. Record the reply URL in the tracking entry before marking
274
+ it complete. One reply may cover multiple explicitly identified findings. Fixed body findings
275
+ require the same successful push check as fixed threads; rebuttals need no push. If a finding also
276
+ has an inline thread, that thread still requires a reply and resolution.
277
+
278
+ ### G. Re-review
279
+
280
+ Pushing re-triggers Copilot when automatic review of new pushes is on; otherwise request Copilot again
281
+ through the reviewers endpoint. Go back to **A**
282
+ with the new head SHA. If step D changed nothing (all comments were rebutted), skip the push,
283
+ ensure all Copilot threads are resolved and all tracked body findings have recorded PR-level
284
+ replies and are complete, then exit.
285
+
286
+ ## 3. Exit conditions
287
+
288
+ Stop when **any** is true:
289
+ - Zero unresolved `$BOT` threads and zero pending body findings remain across iterations. Every
290
+ finding was fixed or rebutted with evidence, every inline thread was replied to and resolved,
291
+ and every body finding has a recorded PR-level reply. (There is no score to hit:
292
+ this is "done".)
293
+ - Max iterations (5) reached: report what remains.
294
+ - Copilot never responded after one retry: report the timeout honestly; do not claim success.
295
+
296
+ ## 4. Report
297
+
298
+ ```text
299
+ Copilotloop complete.
300
+ PR: #<n>
301
+ Bot login: <resolved $BOT>
302
+ Iterations: N
303
+ Findings fixed: N (inline + body findings, deduplicated)
304
+ Findings rebutted: N (inline + body findings, with recorded evidence-based replies)
305
+ Remaining: 0 (unresolved inline + pending body findings, deduplicated)
306
+ ```
307
+
308
+ If it stopped at max iterations, list the remaining threads and body findings with your current
309
+ assessment (fix-pending vs disputed) so a human can arbitrate.