@olegkoval/agent-skills 1.18.0 → 1.19.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-agent-skills",
3
3
  "description": "Agent-agnostic skill catalog for Codex, Claude, Cursor, Grok, Copilot, Windsurf, Kiro, and other skill-aware tools.",
4
- "version": "1.17.0",
4
+ "version": "1.18.0",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -31,6 +31,7 @@
31
31
  "./packages/software-development/apple-store-submit",
32
32
  "./packages/software-development/macos-menubar-app",
33
33
  "./packages/software-development/skill-budget-audit",
34
- "./packages/software-development/crash-course"
34
+ "./packages/software-development/crash-course",
35
+ "./packages/software-development/qodoloop"
35
36
  ]
36
37
  }
@@ -105,6 +105,11 @@
105
105
  "name": "olko:crash-course",
106
106
  "source": "./packages/software-development/crash-course/adapters/cursor",
107
107
  "description": "Expert tutor for rapid, source-grounded learning of any topic: a timed 4-hour sprint plus cheat-sheet, learning-ladder, quiz-me, Feynman, and resource-curation modes."
108
+ },
109
+ {
110
+ "name": "olko:qodoloop",
111
+ "source": "./packages/software-development/qodoloop/adapters/cursor",
112
+ "description": "Iteratively drives a GitHub PR to zero unresolved Qodo findings — reads each finding's own Agent Prompt, applies the fix, replies to the thread, resolves it, pushes, and waits for Qodo's next pass."
108
113
  }
109
114
  ]
110
115
  }
@@ -115,6 +115,11 @@
115
115
  "name": "olko:crash-course",
116
116
  "source": "./packages/software-development/crash-course/adapters/grok",
117
117
  "description": "Expert tutor for rapid, source-grounded learning of any topic: a timed 4-hour sprint plus cheat-sheet, learning-ladder, quiz-me, Feynman, and resource-curation modes."
118
+ },
119
+ {
120
+ "name": "olko:qodoloop",
121
+ "source": "./packages/software-development/qodoloop/adapters/grok",
122
+ "description": "Iteratively drives a GitHub PR to zero unresolved Qodo findings — reads each finding's own Agent Prompt, applies the fix, replies to the thread, resolves it, pushes, and waits for Qodo's next pass."
118
123
  }
119
124
  ]
120
125
  }
@@ -0,0 +1,127 @@
1
+ <!-- Generated by scripts/build-adapters.sh. Do not edit directly. -->
2
+
3
+ ---
4
+ inclusion: manual
5
+ description: "Iteratively drives a GitHub PR to zero unresolved Qodo findings — reads each finding's own Agent Prompt, applies the fix, replies to the thread, resolves it, pushes, and waits for Qodo's next pass."
6
+ ---
7
+
8
+ # Qodoloop
9
+
10
+ Fix every actionable Qodo finding on a PR, answer each thread, resolve it, and keep going until Qodo has nothing left to say (or a finding needs a human call).
11
+
12
+ ## Inputs
13
+
14
+ - **PR number** (optional): detect from the current branch if not given.
15
+ - **Include optional findings** (optional, default off): Qodo tags each finding "Review recommended" (actionable) or "Optional" (informational/lower-confidence). By default only "Review recommended" findings count toward done; pass `--include-optional` to also fix and resolve the optional ones.
16
+
17
+ ## How Qodo actually posts (verified against a live install — don't assume otherwise)
18
+
19
+ Qodo runs as `qodo-code-review[bot]` and posts **two independent things** per review pass, not one:
20
+
21
+ 1. A rollup **issue comment** titled `<h3>Code Review by Qodo</h3>` — human-readable, all findings in one place. A separate `<h3>PR Summary by Qodo</h3>` comment is a plain description, not a review — ignore it. While running, Qodo posts `<h3>Qodo is busy working</h3>` as its own comment (not an edit of a prior one).
22
+ 2. A **formal PR review** (`state: COMMENTED`) carrying one **inline review comment per finding**, each a real, resolvable GitHub review thread at the finding's file/line — same machinery as any human inline comment.
23
+
24
+ **Use the inline threads as the source of truth.** They carry `isResolved` state natively (fetch via GraphQL, see `references/graphql-queries.md`), so "done" is answerable directly instead of re-parsing the rollup's HTML on every iteration. Each thread's body contains a `<details><summary>Agent Prompt</summary>` (or `**Agent Prompt**`) section with a ready-made fix spec, and a severity badge image whose `alt` text is `Remediation recommended` or `Informational` (recommended ↔ "Review recommended", informational ↔ "Optional").
25
+
26
+ Qodo has **no check run to poll** — there is nothing in `gh pr checks` to wait on. Completion means a new `qodo-code-review[bot]` comment/review appears after your push; poll comments, not checks.
27
+
28
+ **Rate limit is a real terminal state, not a bug.** A completed-looking comment reading "Qodo reviews are paused" / "you've reached your PR review limit" means Qodo will not review again this cycle — stop and report it, don't loop waiting for something that isn't coming.
29
+
30
+ ## Instructions
31
+
32
+ ### 1. Identify the PR
33
+
34
+ ```bash
35
+ gh pr view --json number,headRefName,headRefOid -q '{number, branch: .headRefName, sha: .headRefOid}'
36
+ ```
37
+
38
+ Check out the branch if not already on it.
39
+
40
+ ### 2. Loop (max 5 iterations)
41
+
42
+ #### A. Wait for a review of the current head
43
+
44
+ On **iteration 1**, if there is nothing to push yet (you haven't fixed anything in this loop), don't wait for a brand-new review — use whatever Qodo has already posted as your starting point and go straight to step B. Otherwise:
45
+
46
+ ```bash
47
+ SINCE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
48
+ git push
49
+ ```
50
+
51
+ Poll (every ~10s, timeout ~5min), fetching **every page** of comments, not just the first:
52
+
53
+ ```bash
54
+ gh api --paginate "repos/{owner}/{repo}/issues/<PR_NUMBER>/comments?per_page=100" | jq -s '
55
+ add | map(select(.user.login == "qodo-code-review[bot]")) | sort_by(.created_at)'
56
+ ```
57
+
58
+ From the comments created **after `$SINCE`** (ignore anything older — that's leftover from a prior push, not this one):
59
+ - Any of them contains "review limit" or "paused" → **stop the loop now**, go to Report with `blocked: rate-limited`.
60
+ - The latest one starts with `<h3>Code Review by Qodo</h3>` → proceed to step B.
61
+ - Otherwise (nothing yet, or only "Qodo is busy working") → sleep 10s and poll again, up to the 5-minute timeout.
62
+
63
+ #### B. Fetch unresolved findings
64
+
65
+ Run the paginated GraphQL query in `references/graphql-queries.md` (`unresolvedQodoThreads`) — **follow `pageInfo.hasNextPage`/`endCursor` until it's exhausted**; a PR with more than 100 Qodo threads silently loses every finding past the first page otherwise. For each unresolved thread from `qodo-code-review[bot]`: parse the finding title, severity (recommended/optional), file/line, and the **Agent Prompt** block.
66
+
67
+ Build the working set: all `recommended` threads, plus `optional` ones too if `--include-optional`.
68
+
69
+ #### C. Check exit conditions
70
+
71
+ Stop if the working set is empty, or max iterations reached.
72
+
73
+ #### D. Fix each finding
74
+
75
+ For each thread in the working set, in order: read the Agent Prompt in full — it already names the issue, the context, the fix focus files/lines, and often the suggested fix. Apply it. Do not go beyond its stated scope. Track two lists as you go: **fixed** (thread id + the exact files you touched for it) and **blocked** (thread id + why — the prompt's suggestion was wrong, unsafe, or needs a product decision). Do not force a fix into the blocked list; do not resolve anything yet.
76
+
77
+ #### E. Commit and push
78
+
79
+ If **fixed** is empty, skip straight to F (nothing to commit). Otherwise, stage **only the files you touched in D** — never `git add -A`, which can sweep in unrelated local changes or secrets that have nothing to do with this loop:
80
+
81
+ ```bash
82
+ git add <files touched by this iteration's fixes>
83
+ git commit -m "address Qodo review feedback (qodoloop iteration N)"
84
+ git push
85
+ ```
86
+
87
+ Confirm the push actually succeeded before moving on — a resolved thread whose fix never made it to the branch is worse than an unresolved one.
88
+
89
+ #### F. Answer each finding
90
+
91
+ This is the "answer to the comments" half of the job, not optional cleanup — and it only runs **after** E's push is confirmed durable, so a thread is never marked resolved while its fix still only exists locally:
92
+
93
+ - **Fixed** findings: reply with one or two sentences (what you changed and why), **then** resolve:
94
+ ```bash
95
+ gh api graphql -f query='
96
+ mutation($threadId: ID!, $body: String!) {
97
+ addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: $threadId, body: $body}) {
98
+ comment { id }
99
+ }
100
+ }' -f threadId="$THREAD_ID" -f body="$REPLY_TEXT"
101
+ ```
102
+ Pass the thread id and reply text as GraphQL **variables** (`-f threadId=... -f body=...`), never interpolated into the query string itself — a reply containing a quote, backtick, or newline breaks (or worse, injects into) a hand-built query. Then resolve with `resolveReviewThread` (see references file).
103
+ - **Blocked** findings: reply explaining exactly why (false positive / needs a human call), but **do not resolve** — leave the thread open. A blocked finding that gets silently resolved is a false "done".
104
+
105
+ Go back to step A.
106
+
107
+ ### 3. Report
108
+
109
+ | Field | Value |
110
+ |--------------------|-------|
111
+ | Iterations | N |
112
+ | Findings resolved | N |
113
+ | Findings blocked | N (with reasons, left unresolved) |
114
+ | Remaining | N (if any) |
115
+ | Blocked | rate-limited / human-intervention / max-iterations / none |
116
+
117
+ ```text
118
+ Qodoloop complete.
119
+ Iterations: 2
120
+ Resolved: 4 (fixed, replied + resolved)
121
+ Blocked: 1 (needs a human call, replied, left unresolved)
122
+ Remaining: 0
123
+ ```
124
+
125
+ ## References
126
+
127
+ - `references/graphql-queries.md` — the exact `unresolvedQodoThreads` query and the `resolveReviewThread` / `addPullRequestReviewThreadReply` mutations, with field notes.
@@ -0,0 +1,126 @@
1
+ <!-- Generated by scripts/build-adapters.sh. Do not edit directly. -->
2
+
3
+ ---
4
+ description: "Iteratively drives a GitHub PR to zero unresolved Qodo findings — reads each finding's own Agent Prompt, applies the fix, replies to the thread, resolves it, pushes, and waits for Qodo's next pass."
5
+ ---
6
+
7
+ # Qodoloop
8
+
9
+ Fix every actionable Qodo finding on a PR, answer each thread, resolve it, and keep going until Qodo has nothing left to say (or a finding needs a human call).
10
+
11
+ ## Inputs
12
+
13
+ - **PR number** (optional): detect from the current branch if not given.
14
+ - **Include optional findings** (optional, default off): Qodo tags each finding "Review recommended" (actionable) or "Optional" (informational/lower-confidence). By default only "Review recommended" findings count toward done; pass `--include-optional` to also fix and resolve the optional ones.
15
+
16
+ ## How Qodo actually posts (verified against a live install — don't assume otherwise)
17
+
18
+ Qodo runs as `qodo-code-review[bot]` and posts **two independent things** per review pass, not one:
19
+
20
+ 1. A rollup **issue comment** titled `<h3>Code Review by Qodo</h3>` — human-readable, all findings in one place. A separate `<h3>PR Summary by Qodo</h3>` comment is a plain description, not a review — ignore it. While running, Qodo posts `<h3>Qodo is busy working</h3>` as its own comment (not an edit of a prior one).
21
+ 2. A **formal PR review** (`state: COMMENTED`) carrying one **inline review comment per finding**, each a real, resolvable GitHub review thread at the finding's file/line — same machinery as any human inline comment.
22
+
23
+ **Use the inline threads as the source of truth.** They carry `isResolved` state natively (fetch via GraphQL, see `references/graphql-queries.md`), so "done" is answerable directly instead of re-parsing the rollup's HTML on every iteration. Each thread's body contains a `<details><summary>Agent Prompt</summary>` (or `**Agent Prompt**`) section with a ready-made fix spec, and a severity badge image whose `alt` text is `Remediation recommended` or `Informational` (recommended ↔ "Review recommended", informational ↔ "Optional").
24
+
25
+ Qodo has **no check run to poll** — there is nothing in `gh pr checks` to wait on. Completion means a new `qodo-code-review[bot]` comment/review appears after your push; poll comments, not checks.
26
+
27
+ **Rate limit is a real terminal state, not a bug.** A completed-looking comment reading "Qodo reviews are paused" / "you've reached your PR review limit" means Qodo will not review again this cycle — stop and report it, don't loop waiting for something that isn't coming.
28
+
29
+ ## Instructions
30
+
31
+ ### 1. Identify the PR
32
+
33
+ ```bash
34
+ gh pr view --json number,headRefName,headRefOid -q '{number, branch: .headRefName, sha: .headRefOid}'
35
+ ```
36
+
37
+ Check out the branch if not already on it.
38
+
39
+ ### 2. Loop (max 5 iterations)
40
+
41
+ #### A. Wait for a review of the current head
42
+
43
+ On **iteration 1**, if there is nothing to push yet (you haven't fixed anything in this loop), don't wait for a brand-new review — use whatever Qodo has already posted as your starting point and go straight to step B. Otherwise:
44
+
45
+ ```bash
46
+ SINCE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
47
+ git push
48
+ ```
49
+
50
+ Poll (every ~10s, timeout ~5min), fetching **every page** of comments, not just the first:
51
+
52
+ ```bash
53
+ gh api --paginate "repos/{owner}/{repo}/issues/<PR_NUMBER>/comments?per_page=100" | jq -s '
54
+ add | map(select(.user.login == "qodo-code-review[bot]")) | sort_by(.created_at)'
55
+ ```
56
+
57
+ From the comments created **after `$SINCE`** (ignore anything older — that's leftover from a prior push, not this one):
58
+ - Any of them contains "review limit" or "paused" → **stop the loop now**, go to Report with `blocked: rate-limited`.
59
+ - The latest one starts with `<h3>Code Review by Qodo</h3>` → proceed to step B.
60
+ - Otherwise (nothing yet, or only "Qodo is busy working") → sleep 10s and poll again, up to the 5-minute timeout.
61
+
62
+ #### B. Fetch unresolved findings
63
+
64
+ Run the paginated GraphQL query in `references/graphql-queries.md` (`unresolvedQodoThreads`) — **follow `pageInfo.hasNextPage`/`endCursor` until it's exhausted**; a PR with more than 100 Qodo threads silently loses every finding past the first page otherwise. For each unresolved thread from `qodo-code-review[bot]`: parse the finding title, severity (recommended/optional), file/line, and the **Agent Prompt** block.
65
+
66
+ Build the working set: all `recommended` threads, plus `optional` ones too if `--include-optional`.
67
+
68
+ #### C. Check exit conditions
69
+
70
+ Stop if the working set is empty, or max iterations reached.
71
+
72
+ #### D. Fix each finding
73
+
74
+ For each thread in the working set, in order: read the Agent Prompt in full — it already names the issue, the context, the fix focus files/lines, and often the suggested fix. Apply it. Do not go beyond its stated scope. Track two lists as you go: **fixed** (thread id + the exact files you touched for it) and **blocked** (thread id + why — the prompt's suggestion was wrong, unsafe, or needs a product decision). Do not force a fix into the blocked list; do not resolve anything yet.
75
+
76
+ #### E. Commit and push
77
+
78
+ If **fixed** is empty, skip straight to F (nothing to commit). Otherwise, stage **only the files you touched in D** — never `git add -A`, which can sweep in unrelated local changes or secrets that have nothing to do with this loop:
79
+
80
+ ```bash
81
+ git add <files touched by this iteration's fixes>
82
+ git commit -m "address Qodo review feedback (qodoloop iteration N)"
83
+ git push
84
+ ```
85
+
86
+ Confirm the push actually succeeded before moving on — a resolved thread whose fix never made it to the branch is worse than an unresolved one.
87
+
88
+ #### F. Answer each finding
89
+
90
+ This is the "answer to the comments" half of the job, not optional cleanup — and it only runs **after** E's push is confirmed durable, so a thread is never marked resolved while its fix still only exists locally:
91
+
92
+ - **Fixed** findings: reply with one or two sentences (what you changed and why), **then** resolve:
93
+ ```bash
94
+ gh api graphql -f query='
95
+ mutation($threadId: ID!, $body: String!) {
96
+ addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: $threadId, body: $body}) {
97
+ comment { id }
98
+ }
99
+ }' -f threadId="$THREAD_ID" -f body="$REPLY_TEXT"
100
+ ```
101
+ Pass the thread id and reply text as GraphQL **variables** (`-f threadId=... -f body=...`), never interpolated into the query string itself — a reply containing a quote, backtick, or newline breaks (or worse, injects into) a hand-built query. Then resolve with `resolveReviewThread` (see references file).
102
+ - **Blocked** findings: reply explaining exactly why (false positive / needs a human call), but **do not resolve** — leave the thread open. A blocked finding that gets silently resolved is a false "done".
103
+
104
+ Go back to step A.
105
+
106
+ ### 3. Report
107
+
108
+ | Field | Value |
109
+ |--------------------|-------|
110
+ | Iterations | N |
111
+ | Findings resolved | N |
112
+ | Findings blocked | N (with reasons, left unresolved) |
113
+ | Remaining | N (if any) |
114
+ | Blocked | rate-limited / human-intervention / max-iterations / none |
115
+
116
+ ```text
117
+ Qodoloop complete.
118
+ Iterations: 2
119
+ Resolved: 4 (fixed, replied + resolved)
120
+ Blocked: 1 (needs a human call, replied, left unresolved)
121
+ Remaining: 0
122
+ ```
123
+
124
+ ## References
125
+
126
+ - `references/graphql-queries.md` — the exact `unresolvedQodoThreads` query and the `resolveReviewThread` / `addPullRequestReviewThreadReply` mutations, with field notes.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  <p><strong>Agent-agnostic skill catalog for Codex, Claude, Cursor, Grok, Copilot, Windsurf, Kiro, and other skill-aware tools.</strong></p>
4
4
  <p>
5
5
  <img src="https://img.shields.io/badge/license-MIT-16a34a" alt="MIT license">
6
- <img src="https://img.shields.io/badge/skills-23-2563eb" alt="23 skills">
6
+ <img src="https://img.shields.io/badge/skills-24-2563eb" alt="24 skills">
7
7
  <img src="https://img.shields.io/badge/platforms-Codex%20%7C%20Claude%20%7C%20Cursor%20%7C%20Grok%20%7C%20Copilot%20%7C%20Windsurf%20%7C%20Kiro-111827" alt="Codex Claude Cursor Grok Copilot Windsurf Kiro">
8
8
  <img src="https://img.shields.io/badge/status-public%20catalog-16a34a" alt="Public catalog">
9
9
  </p>
@@ -136,11 +136,11 @@ packages/{category}/{skill}/SKILL.md
136
136
 
137
137
  </details>
138
138
 
139
- ## All 23 Skills
139
+ ## All 24 Skills
140
140
 
141
141
  Each entry links to its `SKILL.md`. Reference any skill by its `olko:*` lookup name in a new agent session.
142
142
 
143
- ### Software development (19)
143
+ ### Software development (21)
144
144
 
145
145
  | Skill | What it does | Use when |
146
146
  |-------|-------------|----------|
@@ -160,6 +160,7 @@ Each entry links to its `SKILL.md`. Reference any skill by its `olko:*` lookup n
160
160
  | [open-source-publisher](packages/software-development/open-source-publisher/SKILL.md) | Prepares an open-source repository for public publishing with branding, CI/CD, and release hygiene | Releasing a private project publicly with proper GitHub Pages, README, and social preview |
161
161
  | [product-builder](packages/software-development/product-builder/SKILL.md) | Builds a full-stack web app or SaaS product from a user description using production-oriented defaults | Building a complete app, SaaS, dashboard, or product rather than a prototype |
162
162
  | [promptctl](packages/software-development/promptctl/SKILL.md) | Uses `promptctl` for reusable prompt templates, scoring, and workflow automation | A project needs prompt conventions, review, scoring, or reusable prompt workflows |
163
+ | [qodoloop](packages/software-development/qodoloop/SKILL.md) | Iteratively drives a GitHub PR to zero unresolved Qodo findings, reading each finding's own Agent Prompt, replying to the thread, and resolving it | Fully addressing a PR against Qodo's code review before merging |
163
164
  | [review-past-performance](packages/software-development/review-past-performance/SKILL.md) | Pulls 24h of ICM memories, git history, and skill analytics; detects repeated mistakes and slow workflows; proposes 1-3 concrete fixes | Daily self-improvement loop or codifying a repeated workflow |
164
165
  | [semantic-release-beta](packages/software-development/semantic-release-beta/SKILL.md) | Sets up `semantic-release` with stable `main` releases and beta prereleases on a `beta` branch | A Node package needs stable npm publishing plus beta prereleases |
165
166
  | [skill-budget-audit](packages/software-development/skill-budget-audit/SKILL.md) | Diagnoses and fixes Claude Code's skill context budget overflow — identifies heavy plugin bundles that exceed the 2% budget | Skills failing to load or Claude hitting context limits from plugin bundles |
@@ -532,6 +532,30 @@
532
532
  "kiro",
533
533
  "grok"
534
534
  ]
535
+ },
536
+ {
537
+ "name": "qodoloop",
538
+ "lookupName": "olko:qodoloop",
539
+ "category": "software-development",
540
+ "path": "packages/software-development/qodoloop",
541
+ "description": "Iteratively drives a GitHub PR to zero unresolved Qodo findings — reads each finding's own Agent Prompt, applies the fix, replies to the thread, resolves it, pushes, and waits for Qodo's next pass.",
542
+ "tags": [
543
+ "code-review",
544
+ "qodo",
545
+ "github",
546
+ "pull-requests",
547
+ "automation",
548
+ "agents"
549
+ ],
550
+ "adapters": [
551
+ "claude",
552
+ "codex",
553
+ "cursor",
554
+ "copilot",
555
+ "grok",
556
+ "windsurf",
557
+ "kiro"
558
+ ]
535
559
  }
536
560
  ]
537
561
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olegkoval/agent-skills",
3
- "version": "1.18.0",
3
+ "version": "1.19.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -0,0 +1,134 @@
1
+ ---
2
+ name: qodoloop
3
+ description: >
4
+ Iteratively drives a GitHub PR to zero unresolved Qodo (qodo-code-review[bot]) findings. Reads
5
+ each finding's own "Agent Prompt" (Qodo hands you a ready-made fix spec per issue), applies the
6
+ fix, replies to the thread explaining what was done, resolves it, pushes, and waits for Qodo's
7
+ next pass. Repeats until clean or a real blocker needs a human. Use when the user wants a PR fully
8
+ addressed against Qodo's review, or says "run qodoloop" / "satisfy Qodo" / "resolve Qodo comments".
9
+ compatibility: Requires git and gh (GitHub CLI) authenticated, with Qodo Merge installed on the repo.
10
+ metadata:
11
+ version: "1.0"
12
+ allowed-tools: Bash(gh:*) Bash(git:*)
13
+ ---
14
+
15
+ # Qodoloop
16
+
17
+ Fix every actionable Qodo finding on a PR, answer each thread, resolve it, and keep going until Qodo has nothing left to say (or a finding needs a human call).
18
+
19
+ ## Inputs
20
+
21
+ - **PR number** (optional): detect from the current branch if not given.
22
+ - **Include optional findings** (optional, default off): Qodo tags each finding "Review recommended" (actionable) or "Optional" (informational/lower-confidence). By default only "Review recommended" findings count toward done; pass `--include-optional` to also fix and resolve the optional ones.
23
+
24
+ ## How Qodo actually posts (verified against a live install — don't assume otherwise)
25
+
26
+ Qodo runs as `qodo-code-review[bot]` and posts **two independent things** per review pass, not one:
27
+
28
+ 1. A rollup **issue comment** titled `<h3>Code Review by Qodo</h3>` — human-readable, all findings in one place. A separate `<h3>PR Summary by Qodo</h3>` comment is a plain description, not a review — ignore it. While running, Qodo posts `<h3>Qodo is busy working</h3>` as its own comment (not an edit of a prior one).
29
+ 2. A **formal PR review** (`state: COMMENTED`) carrying one **inline review comment per finding**, each a real, resolvable GitHub review thread at the finding's file/line — same machinery as any human inline comment.
30
+
31
+ **Use the inline threads as the source of truth.** They carry `isResolved` state natively (fetch via GraphQL, see `references/graphql-queries.md`), so "done" is answerable directly instead of re-parsing the rollup's HTML on every iteration. Each thread's body contains a `<details><summary>Agent Prompt</summary>` (or `**Agent Prompt**`) section with a ready-made fix spec, and a severity badge image whose `alt` text is `Remediation recommended` or `Informational` (recommended ↔ "Review recommended", informational ↔ "Optional").
32
+
33
+ Qodo has **no check run to poll** — there is nothing in `gh pr checks` to wait on. Completion means a new `qodo-code-review[bot]` comment/review appears after your push; poll comments, not checks.
34
+
35
+ **Rate limit is a real terminal state, not a bug.** A completed-looking comment reading "Qodo reviews are paused" / "you've reached your PR review limit" means Qodo will not review again this cycle — stop and report it, don't loop waiting for something that isn't coming.
36
+
37
+ ## Instructions
38
+
39
+ ### 1. Identify the PR
40
+
41
+ ```bash
42
+ gh pr view --json number,headRefName,headRefOid -q '{number, branch: .headRefName, sha: .headRefOid}'
43
+ ```
44
+
45
+ Check out the branch if not already on it.
46
+
47
+ ### 2. Loop (max 5 iterations)
48
+
49
+ #### A. Wait for a review of the current head
50
+
51
+ On **iteration 1**, if there is nothing to push yet (you haven't fixed anything in this loop), don't wait for a brand-new review — use whatever Qodo has already posted as your starting point and go straight to step B. Otherwise:
52
+
53
+ ```bash
54
+ SINCE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
55
+ git push
56
+ ```
57
+
58
+ Poll (every ~10s, timeout ~5min), fetching **every page** of comments, not just the first:
59
+
60
+ ```bash
61
+ gh api --paginate "repos/{owner}/{repo}/issues/<PR_NUMBER>/comments?per_page=100" | jq -s '
62
+ add | map(select(.user.login == "qodo-code-review[bot]")) | sort_by(.created_at)'
63
+ ```
64
+
65
+ From the comments created **after `$SINCE`** (ignore anything older — that's leftover from a prior push, not this one):
66
+ - Any of them contains "review limit" or "paused" → **stop the loop now**, go to Report with `blocked: rate-limited`.
67
+ - The latest one starts with `<h3>Code Review by Qodo</h3>` → proceed to step B.
68
+ - Otherwise (nothing yet, or only "Qodo is busy working") → sleep 10s and poll again, up to the 5-minute timeout.
69
+
70
+ #### B. Fetch unresolved findings
71
+
72
+ Run the paginated GraphQL query in `references/graphql-queries.md` (`unresolvedQodoThreads`) — **follow `pageInfo.hasNextPage`/`endCursor` until it's exhausted**; a PR with more than 100 Qodo threads silently loses every finding past the first page otherwise. For each unresolved thread from `qodo-code-review[bot]`: parse the finding title, severity (recommended/optional), file/line, and the **Agent Prompt** block.
73
+
74
+ Build the working set: all `recommended` threads, plus `optional` ones too if `--include-optional`.
75
+
76
+ #### C. Check exit conditions
77
+
78
+ Stop if the working set is empty, or max iterations reached.
79
+
80
+ #### D. Fix each finding
81
+
82
+ For each thread in the working set, in order: read the Agent Prompt in full — it already names the issue, the context, the fix focus files/lines, and often the suggested fix. Apply it. Do not go beyond its stated scope. Track two lists as you go: **fixed** (thread id + the exact files you touched for it) and **blocked** (thread id + why — the prompt's suggestion was wrong, unsafe, or needs a product decision). Do not force a fix into the blocked list; do not resolve anything yet.
83
+
84
+ #### E. Commit and push
85
+
86
+ If **fixed** is empty, skip straight to F (nothing to commit). Otherwise, stage **only the files you touched in D** — never `git add -A`, which can sweep in unrelated local changes or secrets that have nothing to do with this loop:
87
+
88
+ ```bash
89
+ git add <files touched by this iteration's fixes>
90
+ git commit -m "address Qodo review feedback (qodoloop iteration N)"
91
+ git push
92
+ ```
93
+
94
+ Confirm the push actually succeeded before moving on — a resolved thread whose fix never made it to the branch is worse than an unresolved one.
95
+
96
+ #### F. Answer each finding
97
+
98
+ This is the "answer to the comments" half of the job, not optional cleanup — and it only runs **after** E's push is confirmed durable, so a thread is never marked resolved while its fix still only exists locally:
99
+
100
+ - **Fixed** findings: reply with one or two sentences (what you changed and why), **then** resolve:
101
+ ```bash
102
+ gh api graphql -f query='
103
+ mutation($threadId: ID!, $body: String!) {
104
+ addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: $threadId, body: $body}) {
105
+ comment { id }
106
+ }
107
+ }' -f threadId="$THREAD_ID" -f body="$REPLY_TEXT"
108
+ ```
109
+ Pass the thread id and reply text as GraphQL **variables** (`-f threadId=... -f body=...`), never interpolated into the query string itself — a reply containing a quote, backtick, or newline breaks (or worse, injects into) a hand-built query. Then resolve with `resolveReviewThread` (see references file).
110
+ - **Blocked** findings: reply explaining exactly why (false positive / needs a human call), but **do not resolve** — leave the thread open. A blocked finding that gets silently resolved is a false "done".
111
+
112
+ Go back to step A.
113
+
114
+ ### 3. Report
115
+
116
+ | Field | Value |
117
+ |--------------------|-------|
118
+ | Iterations | N |
119
+ | Findings resolved | N |
120
+ | Findings blocked | N (with reasons, left unresolved) |
121
+ | Remaining | N (if any) |
122
+ | Blocked | rate-limited / human-intervention / max-iterations / none |
123
+
124
+ ```text
125
+ Qodoloop complete.
126
+ Iterations: 2
127
+ Resolved: 4 (fixed, replied + resolved)
128
+ Blocked: 1 (needs a human call, replied, left unresolved)
129
+ Remaining: 0
130
+ ```
131
+
132
+ ## References
133
+
134
+ - `references/graphql-queries.md` — the exact `unresolvedQodoThreads` query and the `resolveReviewThread` / `addPullRequestReviewThreadReply` mutations, with field notes.
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "olko-qodoloop",
3
+ "description": "Iteratively drives a GitHub PR to zero unresolved Qodo findings — reads each finding's own Agent Prompt, applies the fix, replies to the thread, resolves it, pushes, and waits for Qodo's next pass.",
4
+ "skills": "./skills"
5
+ }
@@ -0,0 +1,135 @@
1
+ ---
2
+ name: qodoloop
3
+ description: >
4
+ Iteratively drives a GitHub PR to zero unresolved Qodo (qodo-code-review[bot]) findings. Reads
5
+ each finding's own "Agent Prompt" (Qodo hands you a ready-made fix spec per issue), applies the
6
+ fix, replies to the thread explaining what was done, resolves it, pushes, and waits for Qodo's
7
+ next pass. Repeats until clean or a real blocker needs a human. Use when the user wants a PR fully
8
+ addressed against Qodo's review, or says "run qodoloop" / "satisfy Qodo" / "resolve Qodo comments".
9
+ compatibility: Requires git and gh (GitHub CLI) authenticated, with Qodo Merge installed on the repo.
10
+ metadata:
11
+ version: "1.0"
12
+ allowed-tools: Bash(gh:*) Bash(git:*)
13
+ ---
14
+ <!-- Generated by scripts/build-adapters.sh. Do not edit directly. -->
15
+
16
+ # Qodoloop
17
+
18
+ Fix every actionable Qodo finding on a PR, answer each thread, resolve it, and keep going until Qodo has nothing left to say (or a finding needs a human call).
19
+
20
+ ## Inputs
21
+
22
+ - **PR number** (optional): detect from the current branch if not given.
23
+ - **Include optional findings** (optional, default off): Qodo tags each finding "Review recommended" (actionable) or "Optional" (informational/lower-confidence). By default only "Review recommended" findings count toward done; pass `--include-optional` to also fix and resolve the optional ones.
24
+
25
+ ## How Qodo actually posts (verified against a live install — don't assume otherwise)
26
+
27
+ Qodo runs as `qodo-code-review[bot]` and posts **two independent things** per review pass, not one:
28
+
29
+ 1. A rollup **issue comment** titled `<h3>Code Review by Qodo</h3>` — human-readable, all findings in one place. A separate `<h3>PR Summary by Qodo</h3>` comment is a plain description, not a review — ignore it. While running, Qodo posts `<h3>Qodo is busy working</h3>` as its own comment (not an edit of a prior one).
30
+ 2. A **formal PR review** (`state: COMMENTED`) carrying one **inline review comment per finding**, each a real, resolvable GitHub review thread at the finding's file/line — same machinery as any human inline comment.
31
+
32
+ **Use the inline threads as the source of truth.** They carry `isResolved` state natively (fetch via GraphQL, see `references/graphql-queries.md`), so "done" is answerable directly instead of re-parsing the rollup's HTML on every iteration. Each thread's body contains a `<details><summary>Agent Prompt</summary>` (or `**Agent Prompt**`) section with a ready-made fix spec, and a severity badge image whose `alt` text is `Remediation recommended` or `Informational` (recommended ↔ "Review recommended", informational ↔ "Optional").
33
+
34
+ Qodo has **no check run to poll** — there is nothing in `gh pr checks` to wait on. Completion means a new `qodo-code-review[bot]` comment/review appears after your push; poll comments, not checks.
35
+
36
+ **Rate limit is a real terminal state, not a bug.** A completed-looking comment reading "Qodo reviews are paused" / "you've reached your PR review limit" means Qodo will not review again this cycle — stop and report it, don't loop waiting for something that isn't coming.
37
+
38
+ ## Instructions
39
+
40
+ ### 1. Identify the PR
41
+
42
+ ```bash
43
+ gh pr view --json number,headRefName,headRefOid -q '{number, branch: .headRefName, sha: .headRefOid}'
44
+ ```
45
+
46
+ Check out the branch if not already on it.
47
+
48
+ ### 2. Loop (max 5 iterations)
49
+
50
+ #### A. Wait for a review of the current head
51
+
52
+ On **iteration 1**, if there is nothing to push yet (you haven't fixed anything in this loop), don't wait for a brand-new review — use whatever Qodo has already posted as your starting point and go straight to step B. Otherwise:
53
+
54
+ ```bash
55
+ SINCE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
56
+ git push
57
+ ```
58
+
59
+ Poll (every ~10s, timeout ~5min), fetching **every page** of comments, not just the first:
60
+
61
+ ```bash
62
+ gh api --paginate "repos/{owner}/{repo}/issues/<PR_NUMBER>/comments?per_page=100" | jq -s '
63
+ add | map(select(.user.login == "qodo-code-review[bot]")) | sort_by(.created_at)'
64
+ ```
65
+
66
+ From the comments created **after `$SINCE`** (ignore anything older — that's leftover from a prior push, not this one):
67
+ - Any of them contains "review limit" or "paused" → **stop the loop now**, go to Report with `blocked: rate-limited`.
68
+ - The latest one starts with `<h3>Code Review by Qodo</h3>` → proceed to step B.
69
+ - Otherwise (nothing yet, or only "Qodo is busy working") → sleep 10s and poll again, up to the 5-minute timeout.
70
+
71
+ #### B. Fetch unresolved findings
72
+
73
+ Run the paginated GraphQL query in `references/graphql-queries.md` (`unresolvedQodoThreads`) — **follow `pageInfo.hasNextPage`/`endCursor` until it's exhausted**; a PR with more than 100 Qodo threads silently loses every finding past the first page otherwise. For each unresolved thread from `qodo-code-review[bot]`: parse the finding title, severity (recommended/optional), file/line, and the **Agent Prompt** block.
74
+
75
+ Build the working set: all `recommended` threads, plus `optional` ones too if `--include-optional`.
76
+
77
+ #### C. Check exit conditions
78
+
79
+ Stop if the working set is empty, or max iterations reached.
80
+
81
+ #### D. Fix each finding
82
+
83
+ For each thread in the working set, in order: read the Agent Prompt in full — it already names the issue, the context, the fix focus files/lines, and often the suggested fix. Apply it. Do not go beyond its stated scope. Track two lists as you go: **fixed** (thread id + the exact files you touched for it) and **blocked** (thread id + why — the prompt's suggestion was wrong, unsafe, or needs a product decision). Do not force a fix into the blocked list; do not resolve anything yet.
84
+
85
+ #### E. Commit and push
86
+
87
+ If **fixed** is empty, skip straight to F (nothing to commit). Otherwise, stage **only the files you touched in D** — never `git add -A`, which can sweep in unrelated local changes or secrets that have nothing to do with this loop:
88
+
89
+ ```bash
90
+ git add <files touched by this iteration's fixes>
91
+ git commit -m "address Qodo review feedback (qodoloop iteration N)"
92
+ git push
93
+ ```
94
+
95
+ Confirm the push actually succeeded before moving on — a resolved thread whose fix never made it to the branch is worse than an unresolved one.
96
+
97
+ #### F. Answer each finding
98
+
99
+ This is the "answer to the comments" half of the job, not optional cleanup — and it only runs **after** E's push is confirmed durable, so a thread is never marked resolved while its fix still only exists locally:
100
+
101
+ - **Fixed** findings: reply with one or two sentences (what you changed and why), **then** resolve:
102
+ ```bash
103
+ gh api graphql -f query='
104
+ mutation($threadId: ID!, $body: String!) {
105
+ addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: $threadId, body: $body}) {
106
+ comment { id }
107
+ }
108
+ }' -f threadId="$THREAD_ID" -f body="$REPLY_TEXT"
109
+ ```
110
+ Pass the thread id and reply text as GraphQL **variables** (`-f threadId=... -f body=...`), never interpolated into the query string itself — a reply containing a quote, backtick, or newline breaks (or worse, injects into) a hand-built query. Then resolve with `resolveReviewThread` (see references file).
111
+ - **Blocked** findings: reply explaining exactly why (false positive / needs a human call), but **do not resolve** — leave the thread open. A blocked finding that gets silently resolved is a false "done".
112
+
113
+ Go back to step A.
114
+
115
+ ### 3. Report
116
+
117
+ | Field | Value |
118
+ |--------------------|-------|
119
+ | Iterations | N |
120
+ | Findings resolved | N |
121
+ | Findings blocked | N (with reasons, left unresolved) |
122
+ | Remaining | N (if any) |
123
+ | Blocked | rate-limited / human-intervention / max-iterations / none |
124
+
125
+ ```text
126
+ Qodoloop complete.
127
+ Iterations: 2
128
+ Resolved: 4 (fixed, replied + resolved)
129
+ Blocked: 1 (needs a human call, replied, left unresolved)
130
+ Remaining: 0
131
+ ```
132
+
133
+ ## References
134
+
135
+ - `references/graphql-queries.md` — the exact `unresolvedQodoThreads` query and the `resolveReviewThread` / `addPullRequestReviewThreadReply` mutations, with field notes.