@olegkoval/agent-skills 1.19.0 → 1.20.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.
- package/.claude-plugin/plugin.json +3 -2
- package/.cursor-plugin/index.json +5 -0
- package/.grok-plugin/index.json +5 -0
- package/.kiro/steering/garmin-watchface.md +201 -0
- package/.kiro/steering/qodoloop.md +13 -1
- package/.windsurf/rules/garmin-watchface.md +200 -0
- package/.windsurf/rules/qodoloop.md +13 -1
- package/README.md +7 -1
- package/catalog/skills.json +28 -4
- package/collections/wearables.json +7 -0
- package/package.json +1 -1
- package/packages/marketing/viral-launch/adapters/claude/skills/viral-launch/references/external-skills.md +12 -0
- package/packages/marketing/viral-launch/adapters/claude/skills/viral-launch/references/launch-checklist.md +42 -0
- package/packages/marketing/viral-launch/adapters/cursor/skills/viral-launch/references/external-skills.md +12 -0
- package/packages/marketing/viral-launch/adapters/cursor/skills/viral-launch/references/launch-checklist.md +42 -0
- package/packages/marketing/viral-launch/adapters/grok/skills/viral-launch/references/external-skills.md +12 -0
- package/packages/marketing/viral-launch/adapters/grok/skills/viral-launch/references/launch-checklist.md +42 -0
- package/packages/software-development/qodoloop/SKILL.md +13 -1
- package/packages/software-development/qodoloop/adapters/claude/skills/qodoloop/SKILL.md +13 -1
- package/packages/software-development/qodoloop/adapters/claude/skills/qodoloop/references/graphql-queries.md +151 -0
- package/packages/software-development/qodoloop/adapters/cursor/skills/qodoloop/SKILL.md +13 -1
- package/packages/software-development/qodoloop/adapters/cursor/skills/qodoloop/references/graphql-queries.md +151 -0
- package/packages/software-development/qodoloop/adapters/grok/skills/qodoloop/SKILL.md +13 -1
- package/packages/software-development/qodoloop/adapters/grok/skills/qodoloop/references/graphql-queries.md +151 -0
- package/packages/software-development/qodoloop/adapters/kiro/steering/qodoloop.md +13 -1
- package/packages/software-development/qodoloop/adapters/windsurf/rules/qodoloop.md +13 -1
- package/packages/software-development/semantic-release-beta/adapters/claude/skills/semantic-release-beta/references/github-actions.md +16 -0
- package/packages/software-development/semantic-release-beta/adapters/claude/skills/semantic-release-beta/references/node-semantic-release.md +13 -0
- package/packages/software-development/semantic-release-beta/adapters/claude/skills/semantic-release-beta/references/preset.md +47 -0
- package/packages/software-development/semantic-release-beta/adapters/claude/skills/semantic-release-beta/references/validation.md +17 -0
- package/packages/software-development/semantic-release-beta/adapters/cursor/skills/semantic-release-beta/references/github-actions.md +16 -0
- package/packages/software-development/semantic-release-beta/adapters/cursor/skills/semantic-release-beta/references/node-semantic-release.md +13 -0
- package/packages/software-development/semantic-release-beta/adapters/cursor/skills/semantic-release-beta/references/preset.md +47 -0
- package/packages/software-development/semantic-release-beta/adapters/cursor/skills/semantic-release-beta/references/validation.md +17 -0
- package/packages/software-development/semantic-release-beta/adapters/grok/skills/semantic-release-beta/references/github-actions.md +16 -0
- package/packages/software-development/semantic-release-beta/adapters/grok/skills/semantic-release-beta/references/node-semantic-release.md +13 -0
- package/packages/software-development/semantic-release-beta/adapters/grok/skills/semantic-release-beta/references/preset.md +47 -0
- package/packages/software-development/semantic-release-beta/adapters/grok/skills/semantic-release-beta/references/validation.md +17 -0
- package/packages/wearables/garmin-watchface/SKILL.md +204 -0
- package/packages/wearables/garmin-watchface/adapters/claude/plugin.json +5 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/SKILL.md +205 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/references/devices.md +98 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/references/display.md +120 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/references/layout.md +224 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/references/simulator.md +146 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/references/store.md +151 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/references/testing.md +154 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/scripts/ciq-calibrate +184 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/scripts/ciq-capture +215 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/scripts/ciq-devices +177 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/scripts/ciq-doctor +85 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/templates/Makefile +72 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/templates/monkey.jungle +13 -0
- package/packages/wearables/garmin-watchface/adapters/claude/skills/garmin-watchface/templates/monkey.test.jungle +7 -0
- package/packages/wearables/garmin-watchface/adapters/codex/README.md +3 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/plugin.json +6 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/SKILL.md +205 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/references/devices.md +98 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/references/display.md +120 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/references/layout.md +224 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/references/simulator.md +146 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/references/store.md +151 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/references/testing.md +154 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/scripts/ciq-calibrate +184 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/scripts/ciq-capture +215 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/scripts/ciq-devices +177 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/scripts/ciq-doctor +85 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/templates/Makefile +72 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/templates/monkey.jungle +13 -0
- package/packages/wearables/garmin-watchface/adapters/cursor/skills/garmin-watchface/templates/monkey.test.jungle +7 -0
- package/packages/wearables/garmin-watchface/adapters/grok/plugin.json +6 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/SKILL.md +205 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/references/devices.md +98 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/references/display.md +120 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/references/layout.md +224 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/references/simulator.md +146 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/references/store.md +151 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/references/testing.md +154 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/scripts/ciq-calibrate +184 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/scripts/ciq-capture +215 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/scripts/ciq-devices +177 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/scripts/ciq-doctor +85 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/templates/Makefile +72 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/templates/monkey.jungle +13 -0
- package/packages/wearables/garmin-watchface/adapters/grok/skills/garmin-watchface/templates/monkey.test.jungle +7 -0
- package/packages/wearables/garmin-watchface/adapters/kiro/steering/garmin-watchface.md +201 -0
- package/packages/wearables/garmin-watchface/adapters/windsurf/rules/garmin-watchface.md +200 -0
- package/packages/wearables/garmin-watchface/references/devices.md +98 -0
- package/packages/wearables/garmin-watchface/references/display.md +120 -0
- package/packages/wearables/garmin-watchface/references/layout.md +224 -0
- package/packages/wearables/garmin-watchface/references/simulator.md +146 -0
- package/packages/wearables/garmin-watchface/references/store.md +151 -0
- package/packages/wearables/garmin-watchface/references/testing.md +154 -0
- package/packages/wearables/garmin-watchface/scripts/ciq-calibrate +184 -0
- package/packages/wearables/garmin-watchface/scripts/ciq-capture +215 -0
- package/packages/wearables/garmin-watchface/scripts/ciq-devices +177 -0
- package/packages/wearables/garmin-watchface/scripts/ciq-doctor +85 -0
- package/packages/wearables/garmin-watchface/templates/Makefile +72 -0
- package/packages/wearables/garmin-watchface/templates/monkey.jungle +13 -0
- package/packages/wearables/garmin-watchface/templates/monkey.test.jungle +7 -0
- package/scripts/build-adapters.sh +1 -1
- package/scripts/validate-catalog.sh +2 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Launch checklist
|
|
2
|
+
|
|
3
|
+
Use this checklist before calling a project launch-ready.
|
|
4
|
+
|
|
5
|
+
## Repo
|
|
6
|
+
|
|
7
|
+
- README headline says what the project does and for whom.
|
|
8
|
+
- First screen contains a demo, screenshot, or quickstart.
|
|
9
|
+
- Install steps work from a clean checkout.
|
|
10
|
+
- Examples cover the highest-intent use cases.
|
|
11
|
+
- License and contribution expectations are clear.
|
|
12
|
+
- Known limitations are stated without weakening the launch angle.
|
|
13
|
+
|
|
14
|
+
## Message
|
|
15
|
+
|
|
16
|
+
- One-sentence positioning is specific.
|
|
17
|
+
- The hook names a real problem or desire.
|
|
18
|
+
- Differentiation is concrete.
|
|
19
|
+
- Claims are backed by repo content, screenshots, demos, benchmarks, or examples.
|
|
20
|
+
- Copy avoids fake urgency, fake social proof, and generic hype.
|
|
21
|
+
|
|
22
|
+
## Distribution
|
|
23
|
+
|
|
24
|
+
- Launch channels are chosen for the actual audience.
|
|
25
|
+
- Each channel gets native copy, not pasted generic copy.
|
|
26
|
+
- Assets include at least one short post, one longer post, and one direct outreach note.
|
|
27
|
+
- Launch timing includes a response window for comments, issues, and feedback.
|
|
28
|
+
|
|
29
|
+
## Shareability
|
|
30
|
+
|
|
31
|
+
- There is a public artifact people can link to.
|
|
32
|
+
- The demo shows a before/after or clear transformation.
|
|
33
|
+
- Users have a reason to share the output, not only the product.
|
|
34
|
+
- Any referral or waitlist mechanism is tied to user value.
|
|
35
|
+
|
|
36
|
+
## Metrics
|
|
37
|
+
|
|
38
|
+
- Activation event is defined.
|
|
39
|
+
- Share event is defined.
|
|
40
|
+
- Conversion event is defined.
|
|
41
|
+
- Feedback collection path is defined.
|
|
42
|
+
- Launch review date is defined before launch.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# External skills
|
|
2
|
+
|
|
3
|
+
Skill search found these related launch and marketing skills:
|
|
4
|
+
|
|
5
|
+
- `coreyhaines31/marketingskills@launch-strategy` - broad launch strategy skill, about 37K installs in Skills CLI search; source repo `coreyhaines31/marketingskills` has about 20K GitHub stars.
|
|
6
|
+
- `refoundai/lenny-skills@launch-marketing` - product management and launch marketing skill, under 1K installs in Skills CLI search.
|
|
7
|
+
|
|
8
|
+
Prefer this local `olko:viral-launch` skill when the work is specifically to make a repository launch-ready. Consider installing `coreyhaines31/marketingskills@launch-strategy` when the user wants a broader marketing strategy workflow:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npx skills add coreyhaines31/marketingskills@launch-strategy -g -y
|
|
12
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Launch checklist
|
|
2
|
+
|
|
3
|
+
Use this checklist before calling a project launch-ready.
|
|
4
|
+
|
|
5
|
+
## Repo
|
|
6
|
+
|
|
7
|
+
- README headline says what the project does and for whom.
|
|
8
|
+
- First screen contains a demo, screenshot, or quickstart.
|
|
9
|
+
- Install steps work from a clean checkout.
|
|
10
|
+
- Examples cover the highest-intent use cases.
|
|
11
|
+
- License and contribution expectations are clear.
|
|
12
|
+
- Known limitations are stated without weakening the launch angle.
|
|
13
|
+
|
|
14
|
+
## Message
|
|
15
|
+
|
|
16
|
+
- One-sentence positioning is specific.
|
|
17
|
+
- The hook names a real problem or desire.
|
|
18
|
+
- Differentiation is concrete.
|
|
19
|
+
- Claims are backed by repo content, screenshots, demos, benchmarks, or examples.
|
|
20
|
+
- Copy avoids fake urgency, fake social proof, and generic hype.
|
|
21
|
+
|
|
22
|
+
## Distribution
|
|
23
|
+
|
|
24
|
+
- Launch channels are chosen for the actual audience.
|
|
25
|
+
- Each channel gets native copy, not pasted generic copy.
|
|
26
|
+
- Assets include at least one short post, one longer post, and one direct outreach note.
|
|
27
|
+
- Launch timing includes a response window for comments, issues, and feedback.
|
|
28
|
+
|
|
29
|
+
## Shareability
|
|
30
|
+
|
|
31
|
+
- There is a public artifact people can link to.
|
|
32
|
+
- The demo shows a before/after or clear transformation.
|
|
33
|
+
- Users have a reason to share the output, not only the product.
|
|
34
|
+
- Any referral or waitlist mechanism is tied to user value.
|
|
35
|
+
|
|
36
|
+
## Metrics
|
|
37
|
+
|
|
38
|
+
- Activation event is defined.
|
|
39
|
+
- Share event is defined.
|
|
40
|
+
- Conversion event is defined.
|
|
41
|
+
- Feedback collection path is defined.
|
|
42
|
+
- Launch review date is defined before launch.
|
|
@@ -42,7 +42,19 @@ Qodo has **no check run to poll** — there is nothing in `gh pr checks` to wait
|
|
|
42
42
|
gh pr view --json number,headRefName,headRefOid -q '{number, branch: .headRefName, sha: .headRefOid}'
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
Check out the branch if not already on it
|
|
45
|
+
Check out the branch if not already on it, **then require a clean working
|
|
46
|
+
tree** (`git status --porcelain` empty) before touching anything:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
[ -z "$(git status --porcelain)" ] || { echo "working tree not clean — refusing to start, would risk committing unrelated changes"; exit 1; }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This is the precondition step E's scoped `git add <file>` depends on: staging
|
|
53
|
+
only the files a fix touched is safe *because* the tree started clean, so
|
|
54
|
+
nothing else could be sitting in those files. Skip this check and a file
|
|
55
|
+
with pre-existing local edits will have those edits swept into the fix
|
|
56
|
+
commit right along with it — the exact failure `git add -A` had, just
|
|
57
|
+
narrower.
|
|
46
58
|
|
|
47
59
|
### 2. Loop (max 5 iterations)
|
|
48
60
|
|
|
@@ -43,7 +43,19 @@ Qodo has **no check run to poll** — there is nothing in `gh pr checks` to wait
|
|
|
43
43
|
gh pr view --json number,headRefName,headRefOid -q '{number, branch: .headRefName, sha: .headRefOid}'
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
Check out the branch if not already on it
|
|
46
|
+
Check out the branch if not already on it, **then require a clean working
|
|
47
|
+
tree** (`git status --porcelain` empty) before touching anything:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
[ -z "$(git status --porcelain)" ] || { echo "working tree not clean — refusing to start, would risk committing unrelated changes"; exit 1; }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This is the precondition step E's scoped `git add <file>` depends on: staging
|
|
54
|
+
only the files a fix touched is safe *because* the tree started clean, so
|
|
55
|
+
nothing else could be sitting in those files. Skip this check and a file
|
|
56
|
+
with pre-existing local edits will have those edits swept into the fix
|
|
57
|
+
commit right along with it — the exact failure `git add -A` had, just
|
|
58
|
+
narrower.
|
|
47
59
|
|
|
48
60
|
### 2. Loop (max 5 iterations)
|
|
49
61
|
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# GraphQL queries reference
|
|
2
|
+
|
|
3
|
+
Field names verified by introspection against GitHub's live GraphQL schema
|
|
4
|
+
(`AddPullRequestReviewThreadReplyInput`) and against a real Qodo install's
|
|
5
|
+
comment output — not guessed.
|
|
6
|
+
|
|
7
|
+
**The bot's login string is spelled differently in REST vs GraphQL.** REST
|
|
8
|
+
(`.user.login` on `issues/.../comments`) returns `qodo-code-review[bot]`.
|
|
9
|
+
GraphQL (`author.login` on `reviewThreads`) returns `qodo-code-review` — no
|
|
10
|
+
`[bot]` suffix — confirmed live against a real PR with real Qodo inline
|
|
11
|
+
threads. Using the REST spelling in a GraphQL filter silently matches
|
|
12
|
+
nothing, which reads as "no findings" instead of "wrong filter" — exactly the
|
|
13
|
+
kind of bug that has no crash and no error to notice. Use `[bot]` only in
|
|
14
|
+
REST-based jq filters (`.user.login`); use the bare name everywhere you're
|
|
15
|
+
filtering a GraphQL `author.login`.
|
|
16
|
+
|
|
17
|
+
## Fetch unresolved Qodo threads (paginated)
|
|
18
|
+
|
|
19
|
+
```graphql
|
|
20
|
+
query($cursor: String) {
|
|
21
|
+
repository(owner: "OWNER", name: "REPO") {
|
|
22
|
+
pullRequest(number: PR_NUMBER) {
|
|
23
|
+
reviewThreads(first: 100, after: $cursor) {
|
|
24
|
+
pageInfo { hasNextPage endCursor }
|
|
25
|
+
nodes {
|
|
26
|
+
id
|
|
27
|
+
isResolved
|
|
28
|
+
comments(first: 1) {
|
|
29
|
+
nodes { body path line author { login } }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Filter client-side to `qodo-code-review` (GraphQL spelling — see the note
|
|
39
|
+
above) and `isResolved == false` — the author isn't filterable server-side in
|
|
40
|
+
this shape. **Follow `pageInfo.hasNextPage`/`endCursor` until it's exhausted**;
|
|
41
|
+
stopping after the first page silently drops every thread past the 100th on a
|
|
42
|
+
PR with a lot of findings:
|
|
43
|
+
|
|
44
|
+
A page fetch can occasionally come back malformed (a transient API hiccup,
|
|
45
|
+
not anything specific to this query — reproduced live, then failed to
|
|
46
|
+
reproduce on an identical immediate retry). Retry once per page before
|
|
47
|
+
giving up rather than letting the whole loop crash on it:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
CURSOR=null
|
|
51
|
+
ALL_THREADS='[]'
|
|
52
|
+
while :; do
|
|
53
|
+
for attempt in 1 2; do
|
|
54
|
+
PAGE=$(gh api graphql -f query='
|
|
55
|
+
query($cursor: String) {
|
|
56
|
+
repository(owner: "OWNER", name: "REPO") {
|
|
57
|
+
pullRequest(number: PR_NUMBER) {
|
|
58
|
+
reviewThreads(first: 100, after: $cursor) {
|
|
59
|
+
pageInfo { hasNextPage endCursor }
|
|
60
|
+
nodes {
|
|
61
|
+
id
|
|
62
|
+
isResolved
|
|
63
|
+
comments(first: 1) { nodes { body path line author { login } } }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}' -f cursor="$CURSOR")
|
|
69
|
+
jq -e . >/dev/null 2>&1 <<< "$PAGE" && break
|
|
70
|
+
[ "$attempt" = "2" ] && { echo "page fetch failed twice, aborting" >&2; exit 1; }
|
|
71
|
+
done
|
|
72
|
+
|
|
73
|
+
ALL_THREADS=$(jq -c --argjson acc "$ALL_THREADS" \
|
|
74
|
+
'$acc + .data.repository.pullRequest.reviewThreads.nodes' <<< "$PAGE")
|
|
75
|
+
|
|
76
|
+
HAS_NEXT=$(jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.hasNextPage' <<< "$PAGE")
|
|
77
|
+
[ "$HAS_NEXT" = "true" ] || break
|
|
78
|
+
CURSOR=$(jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.endCursor' <<< "$PAGE")
|
|
79
|
+
done
|
|
80
|
+
|
|
81
|
+
echo "$ALL_THREADS" | jq \
|
|
82
|
+
'.[] | select(.comments.nodes[0].author.login == "qodo-code-review") | select(.isResolved == false)'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Parsing a thread's finding out of `comments.nodes[0].body`
|
|
86
|
+
|
|
87
|
+
The body is markdown with embedded HTML. Extract:
|
|
88
|
+
- **Title + severity**: first line has a badge image; `alt="Remediation recommended"` = actionable, `alt="Informational"` = optional. The finding title/tags follow on the next line (e.g. `3\. pickclaimroaster branch untested <code>📘 Rule violation</code> ...`).
|
|
89
|
+
- **Agent Prompt**: the fenced ```` ```...``` ```` block under a `<summary>` reading either `Agent Prompt` or `Agent prompt` (Qodo's casing differs between the inline-comment surface and the rollup issue-comment surface — match case-insensitively). This block is the whole fix spec: issue description, context, fix focus files/lines, and often a suggested fix. Use it verbatim as the task.
|
|
90
|
+
|
|
91
|
+
If a thread somehow has no Agent Prompt block (seen only on the rollup's
|
|
92
|
+
lower-severity "Optional" entries in some cases), fall back to the finding's
|
|
93
|
+
plain-text description above the badge.
|
|
94
|
+
|
|
95
|
+
## Reply to a thread
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
gh api graphql -f query='
|
|
99
|
+
mutation($threadId: ID!, $body: String!) {
|
|
100
|
+
addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: $threadId, body: $body}) {
|
|
101
|
+
comment { id }
|
|
102
|
+
}
|
|
103
|
+
}' -f threadId="$THREAD_ID" -f body="$REPLY_TEXT"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Pass the thread id and reply text as GraphQL **variables** (`-f`), never
|
|
107
|
+
interpolated straight into the query string. A reply containing a quote,
|
|
108
|
+
backtick, or embedded newline breaks — or, worse, injects into — a
|
|
109
|
+
hand-built query string; `gh api graphql -f` handles the escaping correctly
|
|
110
|
+
when the value travels as a variable instead.
|
|
111
|
+
|
|
112
|
+
`pullRequestReviewThreadId` and `body` are the only required input fields
|
|
113
|
+
(`pullRequestReviewId` and `clientMutationId` are optional — omit them).
|
|
114
|
+
|
|
115
|
+
## Resolve a thread (batch)
|
|
116
|
+
|
|
117
|
+
```graphql
|
|
118
|
+
mutation {
|
|
119
|
+
t1: resolveReviewThread(input: {threadId: "ID1"}) { thread { isResolved } }
|
|
120
|
+
t2: resolveReviewThread(input: {threadId: "ID2"}) { thread { isResolved } }
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Only resolve a thread whose fix is already pushed and confirmed durable on
|
|
125
|
+
the branch (SKILL.md §2E-F) — resolving first and pushing second means a
|
|
126
|
+
failed push leaves GitHub showing the finding as handled when it isn't. Never
|
|
127
|
+
resolve a blocked/skipped finding at all; reply to it and leave it open.
|
|
128
|
+
|
|
129
|
+
## Fetch the rollup issue comment (context only, not the source of truth)
|
|
130
|
+
|
|
131
|
+
Useful for the human-readable summary counts (bugs/rule-violations) in your
|
|
132
|
+
final report, or as a fallback if `reviewThreads` ever comes back empty for a
|
|
133
|
+
PR that clearly has a completed review. Select the latest by `created_at`.
|
|
134
|
+
Whether Qodo edits this comment in place across passes or posts a new one
|
|
135
|
+
each time is **unconfirmed** — every PR checked had exactly one, untouched
|
|
136
|
+
since creation, which doesn't distinguish the two. Sort by `created_at` and
|
|
137
|
+
take the last one either way; it's correct under both behaviors:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
gh api --paginate "repos/{owner}/{repo}/issues/<PR_NUMBER>/comments?per_page=100" \
|
|
141
|
+
| jq -s 'add
|
|
142
|
+
| map(select(.user.login == "qodo-code-review[bot]"))
|
|
143
|
+
| map(select(.body | startswith("<h3>Code Review by Qodo</h3>")))
|
|
144
|
+
| sort_by(.created_at)
|
|
145
|
+
| last'
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
A rate-limit/paused comment reads `<h3>Qodo is busy working</h3>` while
|
|
149
|
+
in-flight, or contains "Qodo reviews are paused" / "review limit" when
|
|
150
|
+
terminally blocked — check for that text before assuming a missing review
|
|
151
|
+
comment just means "still running".
|
|
@@ -44,7 +44,19 @@ Qodo has **no check run to poll** — there is nothing in `gh pr checks` to wait
|
|
|
44
44
|
gh pr view --json number,headRefName,headRefOid -q '{number, branch: .headRefName, sha: .headRefOid}'
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
Check out the branch if not already on it
|
|
47
|
+
Check out the branch if not already on it, **then require a clean working
|
|
48
|
+
tree** (`git status --porcelain` empty) before touching anything:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
[ -z "$(git status --porcelain)" ] || { echo "working tree not clean — refusing to start, would risk committing unrelated changes"; exit 1; }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This is the precondition step E's scoped `git add <file>` depends on: staging
|
|
55
|
+
only the files a fix touched is safe *because* the tree started clean, so
|
|
56
|
+
nothing else could be sitting in those files. Skip this check and a file
|
|
57
|
+
with pre-existing local edits will have those edits swept into the fix
|
|
58
|
+
commit right along with it — the exact failure `git add -A` had, just
|
|
59
|
+
narrower.
|
|
48
60
|
|
|
49
61
|
### 2. Loop (max 5 iterations)
|
|
50
62
|
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# GraphQL queries reference
|
|
2
|
+
|
|
3
|
+
Field names verified by introspection against GitHub's live GraphQL schema
|
|
4
|
+
(`AddPullRequestReviewThreadReplyInput`) and against a real Qodo install's
|
|
5
|
+
comment output — not guessed.
|
|
6
|
+
|
|
7
|
+
**The bot's login string is spelled differently in REST vs GraphQL.** REST
|
|
8
|
+
(`.user.login` on `issues/.../comments`) returns `qodo-code-review[bot]`.
|
|
9
|
+
GraphQL (`author.login` on `reviewThreads`) returns `qodo-code-review` — no
|
|
10
|
+
`[bot]` suffix — confirmed live against a real PR with real Qodo inline
|
|
11
|
+
threads. Using the REST spelling in a GraphQL filter silently matches
|
|
12
|
+
nothing, which reads as "no findings" instead of "wrong filter" — exactly the
|
|
13
|
+
kind of bug that has no crash and no error to notice. Use `[bot]` only in
|
|
14
|
+
REST-based jq filters (`.user.login`); use the bare name everywhere you're
|
|
15
|
+
filtering a GraphQL `author.login`.
|
|
16
|
+
|
|
17
|
+
## Fetch unresolved Qodo threads (paginated)
|
|
18
|
+
|
|
19
|
+
```graphql
|
|
20
|
+
query($cursor: String) {
|
|
21
|
+
repository(owner: "OWNER", name: "REPO") {
|
|
22
|
+
pullRequest(number: PR_NUMBER) {
|
|
23
|
+
reviewThreads(first: 100, after: $cursor) {
|
|
24
|
+
pageInfo { hasNextPage endCursor }
|
|
25
|
+
nodes {
|
|
26
|
+
id
|
|
27
|
+
isResolved
|
|
28
|
+
comments(first: 1) {
|
|
29
|
+
nodes { body path line author { login } }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Filter client-side to `qodo-code-review` (GraphQL spelling — see the note
|
|
39
|
+
above) and `isResolved == false` — the author isn't filterable server-side in
|
|
40
|
+
this shape. **Follow `pageInfo.hasNextPage`/`endCursor` until it's exhausted**;
|
|
41
|
+
stopping after the first page silently drops every thread past the 100th on a
|
|
42
|
+
PR with a lot of findings:
|
|
43
|
+
|
|
44
|
+
A page fetch can occasionally come back malformed (a transient API hiccup,
|
|
45
|
+
not anything specific to this query — reproduced live, then failed to
|
|
46
|
+
reproduce on an identical immediate retry). Retry once per page before
|
|
47
|
+
giving up rather than letting the whole loop crash on it:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
CURSOR=null
|
|
51
|
+
ALL_THREADS='[]'
|
|
52
|
+
while :; do
|
|
53
|
+
for attempt in 1 2; do
|
|
54
|
+
PAGE=$(gh api graphql -f query='
|
|
55
|
+
query($cursor: String) {
|
|
56
|
+
repository(owner: "OWNER", name: "REPO") {
|
|
57
|
+
pullRequest(number: PR_NUMBER) {
|
|
58
|
+
reviewThreads(first: 100, after: $cursor) {
|
|
59
|
+
pageInfo { hasNextPage endCursor }
|
|
60
|
+
nodes {
|
|
61
|
+
id
|
|
62
|
+
isResolved
|
|
63
|
+
comments(first: 1) { nodes { body path line author { login } } }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}' -f cursor="$CURSOR")
|
|
69
|
+
jq -e . >/dev/null 2>&1 <<< "$PAGE" && break
|
|
70
|
+
[ "$attempt" = "2" ] && { echo "page fetch failed twice, aborting" >&2; exit 1; }
|
|
71
|
+
done
|
|
72
|
+
|
|
73
|
+
ALL_THREADS=$(jq -c --argjson acc "$ALL_THREADS" \
|
|
74
|
+
'$acc + .data.repository.pullRequest.reviewThreads.nodes' <<< "$PAGE")
|
|
75
|
+
|
|
76
|
+
HAS_NEXT=$(jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.hasNextPage' <<< "$PAGE")
|
|
77
|
+
[ "$HAS_NEXT" = "true" ] || break
|
|
78
|
+
CURSOR=$(jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.endCursor' <<< "$PAGE")
|
|
79
|
+
done
|
|
80
|
+
|
|
81
|
+
echo "$ALL_THREADS" | jq \
|
|
82
|
+
'.[] | select(.comments.nodes[0].author.login == "qodo-code-review") | select(.isResolved == false)'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Parsing a thread's finding out of `comments.nodes[0].body`
|
|
86
|
+
|
|
87
|
+
The body is markdown with embedded HTML. Extract:
|
|
88
|
+
- **Title + severity**: first line has a badge image; `alt="Remediation recommended"` = actionable, `alt="Informational"` = optional. The finding title/tags follow on the next line (e.g. `3\. pickclaimroaster branch untested <code>📘 Rule violation</code> ...`).
|
|
89
|
+
- **Agent Prompt**: the fenced ```` ```...``` ```` block under a `<summary>` reading either `Agent Prompt` or `Agent prompt` (Qodo's casing differs between the inline-comment surface and the rollup issue-comment surface — match case-insensitively). This block is the whole fix spec: issue description, context, fix focus files/lines, and often a suggested fix. Use it verbatim as the task.
|
|
90
|
+
|
|
91
|
+
If a thread somehow has no Agent Prompt block (seen only on the rollup's
|
|
92
|
+
lower-severity "Optional" entries in some cases), fall back to the finding's
|
|
93
|
+
plain-text description above the badge.
|
|
94
|
+
|
|
95
|
+
## Reply to a thread
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
gh api graphql -f query='
|
|
99
|
+
mutation($threadId: ID!, $body: String!) {
|
|
100
|
+
addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: $threadId, body: $body}) {
|
|
101
|
+
comment { id }
|
|
102
|
+
}
|
|
103
|
+
}' -f threadId="$THREAD_ID" -f body="$REPLY_TEXT"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Pass the thread id and reply text as GraphQL **variables** (`-f`), never
|
|
107
|
+
interpolated straight into the query string. A reply containing a quote,
|
|
108
|
+
backtick, or embedded newline breaks — or, worse, injects into — a
|
|
109
|
+
hand-built query string; `gh api graphql -f` handles the escaping correctly
|
|
110
|
+
when the value travels as a variable instead.
|
|
111
|
+
|
|
112
|
+
`pullRequestReviewThreadId` and `body` are the only required input fields
|
|
113
|
+
(`pullRequestReviewId` and `clientMutationId` are optional — omit them).
|
|
114
|
+
|
|
115
|
+
## Resolve a thread (batch)
|
|
116
|
+
|
|
117
|
+
```graphql
|
|
118
|
+
mutation {
|
|
119
|
+
t1: resolveReviewThread(input: {threadId: "ID1"}) { thread { isResolved } }
|
|
120
|
+
t2: resolveReviewThread(input: {threadId: "ID2"}) { thread { isResolved } }
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Only resolve a thread whose fix is already pushed and confirmed durable on
|
|
125
|
+
the branch (SKILL.md §2E-F) — resolving first and pushing second means a
|
|
126
|
+
failed push leaves GitHub showing the finding as handled when it isn't. Never
|
|
127
|
+
resolve a blocked/skipped finding at all; reply to it and leave it open.
|
|
128
|
+
|
|
129
|
+
## Fetch the rollup issue comment (context only, not the source of truth)
|
|
130
|
+
|
|
131
|
+
Useful for the human-readable summary counts (bugs/rule-violations) in your
|
|
132
|
+
final report, or as a fallback if `reviewThreads` ever comes back empty for a
|
|
133
|
+
PR that clearly has a completed review. Select the latest by `created_at`.
|
|
134
|
+
Whether Qodo edits this comment in place across passes or posts a new one
|
|
135
|
+
each time is **unconfirmed** — every PR checked had exactly one, untouched
|
|
136
|
+
since creation, which doesn't distinguish the two. Sort by `created_at` and
|
|
137
|
+
take the last one either way; it's correct under both behaviors:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
gh api --paginate "repos/{owner}/{repo}/issues/<PR_NUMBER>/comments?per_page=100" \
|
|
141
|
+
| jq -s 'add
|
|
142
|
+
| map(select(.user.login == "qodo-code-review[bot]"))
|
|
143
|
+
| map(select(.body | startswith("<h3>Code Review by Qodo</h3>")))
|
|
144
|
+
| sort_by(.created_at)
|
|
145
|
+
| last'
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
A rate-limit/paused comment reads `<h3>Qodo is busy working</h3>` while
|
|
149
|
+
in-flight, or contains "Qodo reviews are paused" / "review limit" when
|
|
150
|
+
terminally blocked — check for that text before assuming a missing review
|
|
151
|
+
comment just means "still running".
|
|
@@ -43,7 +43,19 @@ Qodo has **no check run to poll** — there is nothing in `gh pr checks` to wait
|
|
|
43
43
|
gh pr view --json number,headRefName,headRefOid -q '{number, branch: .headRefName, sha: .headRefOid}'
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
Check out the branch if not already on it
|
|
46
|
+
Check out the branch if not already on it, **then require a clean working
|
|
47
|
+
tree** (`git status --porcelain` empty) before touching anything:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
[ -z "$(git status --porcelain)" ] || { echo "working tree not clean — refusing to start, would risk committing unrelated changes"; exit 1; }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This is the precondition step E's scoped `git add <file>` depends on: staging
|
|
54
|
+
only the files a fix touched is safe *because* the tree started clean, so
|
|
55
|
+
nothing else could be sitting in those files. Skip this check and a file
|
|
56
|
+
with pre-existing local edits will have those edits swept into the fix
|
|
57
|
+
commit right along with it — the exact failure `git add -A` had, just
|
|
58
|
+
narrower.
|
|
47
59
|
|
|
48
60
|
### 2. Loop (max 5 iterations)
|
|
49
61
|
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# GraphQL queries reference
|
|
2
|
+
|
|
3
|
+
Field names verified by introspection against GitHub's live GraphQL schema
|
|
4
|
+
(`AddPullRequestReviewThreadReplyInput`) and against a real Qodo install's
|
|
5
|
+
comment output — not guessed.
|
|
6
|
+
|
|
7
|
+
**The bot's login string is spelled differently in REST vs GraphQL.** REST
|
|
8
|
+
(`.user.login` on `issues/.../comments`) returns `qodo-code-review[bot]`.
|
|
9
|
+
GraphQL (`author.login` on `reviewThreads`) returns `qodo-code-review` — no
|
|
10
|
+
`[bot]` suffix — confirmed live against a real PR with real Qodo inline
|
|
11
|
+
threads. Using the REST spelling in a GraphQL filter silently matches
|
|
12
|
+
nothing, which reads as "no findings" instead of "wrong filter" — exactly the
|
|
13
|
+
kind of bug that has no crash and no error to notice. Use `[bot]` only in
|
|
14
|
+
REST-based jq filters (`.user.login`); use the bare name everywhere you're
|
|
15
|
+
filtering a GraphQL `author.login`.
|
|
16
|
+
|
|
17
|
+
## Fetch unresolved Qodo threads (paginated)
|
|
18
|
+
|
|
19
|
+
```graphql
|
|
20
|
+
query($cursor: String) {
|
|
21
|
+
repository(owner: "OWNER", name: "REPO") {
|
|
22
|
+
pullRequest(number: PR_NUMBER) {
|
|
23
|
+
reviewThreads(first: 100, after: $cursor) {
|
|
24
|
+
pageInfo { hasNextPage endCursor }
|
|
25
|
+
nodes {
|
|
26
|
+
id
|
|
27
|
+
isResolved
|
|
28
|
+
comments(first: 1) {
|
|
29
|
+
nodes { body path line author { login } }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Filter client-side to `qodo-code-review` (GraphQL spelling — see the note
|
|
39
|
+
above) and `isResolved == false` — the author isn't filterable server-side in
|
|
40
|
+
this shape. **Follow `pageInfo.hasNextPage`/`endCursor` until it's exhausted**;
|
|
41
|
+
stopping after the first page silently drops every thread past the 100th on a
|
|
42
|
+
PR with a lot of findings:
|
|
43
|
+
|
|
44
|
+
A page fetch can occasionally come back malformed (a transient API hiccup,
|
|
45
|
+
not anything specific to this query — reproduced live, then failed to
|
|
46
|
+
reproduce on an identical immediate retry). Retry once per page before
|
|
47
|
+
giving up rather than letting the whole loop crash on it:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
CURSOR=null
|
|
51
|
+
ALL_THREADS='[]'
|
|
52
|
+
while :; do
|
|
53
|
+
for attempt in 1 2; do
|
|
54
|
+
PAGE=$(gh api graphql -f query='
|
|
55
|
+
query($cursor: String) {
|
|
56
|
+
repository(owner: "OWNER", name: "REPO") {
|
|
57
|
+
pullRequest(number: PR_NUMBER) {
|
|
58
|
+
reviewThreads(first: 100, after: $cursor) {
|
|
59
|
+
pageInfo { hasNextPage endCursor }
|
|
60
|
+
nodes {
|
|
61
|
+
id
|
|
62
|
+
isResolved
|
|
63
|
+
comments(first: 1) { nodes { body path line author { login } } }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}' -f cursor="$CURSOR")
|
|
69
|
+
jq -e . >/dev/null 2>&1 <<< "$PAGE" && break
|
|
70
|
+
[ "$attempt" = "2" ] && { echo "page fetch failed twice, aborting" >&2; exit 1; }
|
|
71
|
+
done
|
|
72
|
+
|
|
73
|
+
ALL_THREADS=$(jq -c --argjson acc "$ALL_THREADS" \
|
|
74
|
+
'$acc + .data.repository.pullRequest.reviewThreads.nodes' <<< "$PAGE")
|
|
75
|
+
|
|
76
|
+
HAS_NEXT=$(jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.hasNextPage' <<< "$PAGE")
|
|
77
|
+
[ "$HAS_NEXT" = "true" ] || break
|
|
78
|
+
CURSOR=$(jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.endCursor' <<< "$PAGE")
|
|
79
|
+
done
|
|
80
|
+
|
|
81
|
+
echo "$ALL_THREADS" | jq \
|
|
82
|
+
'.[] | select(.comments.nodes[0].author.login == "qodo-code-review") | select(.isResolved == false)'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Parsing a thread's finding out of `comments.nodes[0].body`
|
|
86
|
+
|
|
87
|
+
The body is markdown with embedded HTML. Extract:
|
|
88
|
+
- **Title + severity**: first line has a badge image; `alt="Remediation recommended"` = actionable, `alt="Informational"` = optional. The finding title/tags follow on the next line (e.g. `3\. pickclaimroaster branch untested <code>📘 Rule violation</code> ...`).
|
|
89
|
+
- **Agent Prompt**: the fenced ```` ```...``` ```` block under a `<summary>` reading either `Agent Prompt` or `Agent prompt` (Qodo's casing differs between the inline-comment surface and the rollup issue-comment surface — match case-insensitively). This block is the whole fix spec: issue description, context, fix focus files/lines, and often a suggested fix. Use it verbatim as the task.
|
|
90
|
+
|
|
91
|
+
If a thread somehow has no Agent Prompt block (seen only on the rollup's
|
|
92
|
+
lower-severity "Optional" entries in some cases), fall back to the finding's
|
|
93
|
+
plain-text description above the badge.
|
|
94
|
+
|
|
95
|
+
## Reply to a thread
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
gh api graphql -f query='
|
|
99
|
+
mutation($threadId: ID!, $body: String!) {
|
|
100
|
+
addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: $threadId, body: $body}) {
|
|
101
|
+
comment { id }
|
|
102
|
+
}
|
|
103
|
+
}' -f threadId="$THREAD_ID" -f body="$REPLY_TEXT"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Pass the thread id and reply text as GraphQL **variables** (`-f`), never
|
|
107
|
+
interpolated straight into the query string. A reply containing a quote,
|
|
108
|
+
backtick, or embedded newline breaks — or, worse, injects into — a
|
|
109
|
+
hand-built query string; `gh api graphql -f` handles the escaping correctly
|
|
110
|
+
when the value travels as a variable instead.
|
|
111
|
+
|
|
112
|
+
`pullRequestReviewThreadId` and `body` are the only required input fields
|
|
113
|
+
(`pullRequestReviewId` and `clientMutationId` are optional — omit them).
|
|
114
|
+
|
|
115
|
+
## Resolve a thread (batch)
|
|
116
|
+
|
|
117
|
+
```graphql
|
|
118
|
+
mutation {
|
|
119
|
+
t1: resolveReviewThread(input: {threadId: "ID1"}) { thread { isResolved } }
|
|
120
|
+
t2: resolveReviewThread(input: {threadId: "ID2"}) { thread { isResolved } }
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Only resolve a thread whose fix is already pushed and confirmed durable on
|
|
125
|
+
the branch (SKILL.md §2E-F) — resolving first and pushing second means a
|
|
126
|
+
failed push leaves GitHub showing the finding as handled when it isn't. Never
|
|
127
|
+
resolve a blocked/skipped finding at all; reply to it and leave it open.
|
|
128
|
+
|
|
129
|
+
## Fetch the rollup issue comment (context only, not the source of truth)
|
|
130
|
+
|
|
131
|
+
Useful for the human-readable summary counts (bugs/rule-violations) in your
|
|
132
|
+
final report, or as a fallback if `reviewThreads` ever comes back empty for a
|
|
133
|
+
PR that clearly has a completed review. Select the latest by `created_at`.
|
|
134
|
+
Whether Qodo edits this comment in place across passes or posts a new one
|
|
135
|
+
each time is **unconfirmed** — every PR checked had exactly one, untouched
|
|
136
|
+
since creation, which doesn't distinguish the two. Sort by `created_at` and
|
|
137
|
+
take the last one either way; it's correct under both behaviors:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
gh api --paginate "repos/{owner}/{repo}/issues/<PR_NUMBER>/comments?per_page=100" \
|
|
141
|
+
| jq -s 'add
|
|
142
|
+
| map(select(.user.login == "qodo-code-review[bot]"))
|
|
143
|
+
| map(select(.body | startswith("<h3>Code Review by Qodo</h3>")))
|
|
144
|
+
| sort_by(.created_at)
|
|
145
|
+
| last'
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
A rate-limit/paused comment reads `<h3>Qodo is busy working</h3>` while
|
|
149
|
+
in-flight, or contains "Qodo reviews are paused" / "review limit" when
|
|
150
|
+
terminally blocked — check for that text before assuming a missing review
|
|
151
|
+
comment just means "still running".
|
|
@@ -35,7 +35,19 @@ Qodo has **no check run to poll** — there is nothing in `gh pr checks` to wait
|
|
|
35
35
|
gh pr view --json number,headRefName,headRefOid -q '{number, branch: .headRefName, sha: .headRefOid}'
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
Check out the branch if not already on it
|
|
38
|
+
Check out the branch if not already on it, **then require a clean working
|
|
39
|
+
tree** (`git status --porcelain` empty) before touching anything:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
[ -z "$(git status --porcelain)" ] || { echo "working tree not clean — refusing to start, would risk committing unrelated changes"; exit 1; }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This is the precondition step E's scoped `git add <file>` depends on: staging
|
|
46
|
+
only the files a fix touched is safe *because* the tree started clean, so
|
|
47
|
+
nothing else could be sitting in those files. Skip this check and a file
|
|
48
|
+
with pre-existing local edits will have those edits swept into the fix
|
|
49
|
+
commit right along with it — the exact failure `git add -A` had, just
|
|
50
|
+
narrower.
|
|
39
51
|
|
|
40
52
|
### 2. Loop (max 5 iterations)
|
|
41
53
|
|