clud-bug 0.5.12 → 0.5.14

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/README.md CHANGED
@@ -3,9 +3,11 @@
3
3
 
4
4
  > **[cludbug.dev](https://cludbug.dev)** · live field journal.
5
5
 
6
- Clud Bug is a Claude PR-review naturalist for your GitHub repo. It pins **project-aware skills** auto-discovered from [skills.sh](https://skills.sh) and ships a baseline kit of review discipline so reviews stay focused on what matters: bugs, security, performance, and missing tests.
6
+ Clud Bug is **skill-driven PR review** for your GitHub repo. Ship a brand-voice skill, get brand reviews. Ship a compliance skill, get PII checks on every diff. Each finding cites the skill that motivated it the bot's authority comes from your specimens, not from generic advice.
7
7
 
8
- One command to install. The first PR you open afterwards gets a real review comment backtypically within two minutes.
8
+ Four baseline skills ship by default covering bug-finding, evidence-based review discipline, pattern conformity, and agent coordination. Add more from [skills.sh](https://skills.sh) or write your own the bot loads them automatically.
9
+
10
+ One command to install. The first PR you open afterwards gets a real review back — typically within two minutes.
9
11
 
10
12
  ## Quickstart
11
13
 
package/lib/skills.js CHANGED
@@ -457,7 +457,20 @@ export function extractPerSkillLine(comment, skillName) {
457
457
  export function selectReviewHeader(comments, botLogin) {
458
458
  if (!Array.isArray(comments)) return null;
459
459
  if (typeof botLogin !== 'string' || !botLogin) return null;
460
- for (const c of comments) {
460
+ // Sort newest-first by created_at. The composite passes the result of
461
+ // `gh api .../comments?sort=created&direction=desc` — but GitHub's
462
+ // REST issue-comments endpoint ignores `direction=desc` and returns
463
+ // ascending (oldest first) regardless. PR #64 caught this: the gate
464
+ // was selecting the OLDER "— critical findings" comment instead of
465
+ // the newer "— clean" follow-up, so fix-push reviews that resolved
466
+ // critical findings still failed the gate. Explicit sort here makes
467
+ // selection deterministic regardless of upstream API quirks.
468
+ const sorted = [...comments].sort((a, b) => {
469
+ const ta = a?.created_at ? Date.parse(a.created_at) : 0;
470
+ const tb = b?.created_at ? Date.parse(b.created_at) : 0;
471
+ return tb - ta; // newest first
472
+ });
473
+ for (const c of sorted) {
461
474
  if (!c || typeof c !== 'object') continue;
462
475
  const author = c.user?.login;
463
476
  const body = c.body;
@@ -496,7 +509,16 @@ export function extractFirstReviewHeaderLine(body) {
496
509
  export function selectReviewBody(comments, botLogin) {
497
510
  if (!Array.isArray(comments)) return null;
498
511
  if (typeof botLogin !== 'string' || !botLogin) return null;
499
- for (const c of comments) {
512
+ // Same explicit newest-first sort as selectReviewHeader — gh api
513
+ // ignores direction=desc on issue-comments and returns ascending,
514
+ // so without this BB.3 was parsing per-skill outcomes from the
515
+ // OLDEST review comment, not the latest. See selectReviewHeader.
516
+ const sorted = [...comments].sort((a, b) => {
517
+ const ta = a?.created_at ? Date.parse(a.created_at) : 0;
518
+ const tb = b?.created_at ? Date.parse(b.created_at) : 0;
519
+ return tb - ta;
520
+ });
521
+ for (const c of sorted) {
500
522
  if (!c || typeof c !== 'object') continue;
501
523
  const author = c.user?.login;
502
524
  const body = c.body;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "clud-bug",
3
- "version": "0.5.12",
4
- "description": "Claude PR review with project-aware skills. CLI installs a working GitHub Actions workflow and curates skills from skills.sh.",
3
+ "version": "0.5.14",
4
+ "description": "Skill-driven Claude PR review. Ship a brand-voice skill, get brand reviews. Each finding cites the skill that motivated it. CLI installs the workflow + a baseline kit; add more from skills.sh.",
5
5
  "homepage": "https://cludbug.dev",
6
6
  "bugs": "https://github.com/thrillmot/clud-bug/issues",
7
7
  "type": "module",
@@ -1,4 +1,4 @@
1
- # clud-bug-template-version: v6
1
+ # clud-bug-template-version: v8
2
2
  name: Clud Bug 🐛 Crawls Your Code
3
3
 
4
4
  on:
@@ -125,7 +125,45 @@ jobs:
125
125
  never at the cost of clarity, evidence, or the critical-issues-only
126
126
  discipline. Don't perform the bit; let the precision speak.
127
127
 
128
- When you finish, post your review as a single PR comment.
128
+ Your review lives in TWO surfaces, in this order:
129
+
130
+ 1. INLINE REVIEW THREADS — one per finding, anchored to the
131
+ file:line cited in the finding. Use the
132
+ mcp__github_inline_comment__create_inline_comment MCP tool
133
+ for each finding (critical, minor, AND per-skill section
134
+ findings). The body should be the finding text itself
135
+ (without the leading "- " bullet). This is what creates
136
+ *resolvable conversations* the author can mark resolved
137
+ when the fix lands; branch protection's
138
+ required_review_thread_resolution rule gates the merge on
139
+ these threads — without inline review comments, the gate
140
+ has nothing to gate on and the loop never closes.
141
+
142
+ Pass `confirmed: true` on every call to the tool. These
143
+ are final review comments, not test probes. Without
144
+ `confirmed: true` the tool defers each call to an
145
+ auto-classifier that decides post-hoc whether the comment
146
+ is "real" — and a classifier miscategorization re-opens
147
+ the exact silent-no-op failure mode this prompt is
148
+ designed to prevent.
149
+
150
+ Findings that genuinely don't anchor to a specific line
151
+ (cross-cutting observations, "missing test coverage for
152
+ the new endpoint as a whole", etc.) stay in the summary
153
+ comment only. The default should be: if you can name
154
+ file:line, post it inline. Only fall back to summary-only
155
+ when the finding spans many files or is structural.
156
+
157
+ 2. SUMMARY PR COMMENT — one top-level comment via
158
+ `gh pr comment` that contains the H2 header, status line,
159
+ per-skill scan block, and per-skill findings sections.
160
+ This is what the strict-mode gate reads (it greps the
161
+ H2 header for "— critical findings"). The findings
162
+ sections here can be brief summaries that point to the
163
+ inline threads above, OR include the same finding text
164
+ for grep-ability — your call, but the master verdict
165
+ header MUST appear in this comment.
166
+
129
167
  The comment body MUST start with this exact line so the
130
168
  project's identity is visible (the bot account will say
131
169
  claude[bot], but the comment header brands it as Clud Bug):
@@ -198,30 +236,51 @@ jobs:
198
236
  critical-issues-only and pii-and-compliance lens at once), so
199
237
  bundling preserves that signal.
200
238
 
201
- Post it via:
239
+ Post the summary via:
202
240
  gh pr comment "$PR_NUMBER" --body "<your review>"
203
241
 
204
- If you previously reviewed this PR (you'll see prior claude[bot]
205
- comments starting with "## 🐛 Clud Bug review"), resolve your
206
- prior unresolved inline review threads where the flagged issue
207
- is fixed in the current diff. List threads:
242
+ Each inline finding is posted separately via the
243
+ mcp__github_inline_comment__create_inline_comment tool
244
+ (with `confirmed: true` per surface 1 above). Ordering
245
+ within the review pass that matters for counter accuracy:
246
+ (a) post new inline findings, (b) resolve prior threads
247
+ whose issue is now fixed (FIX-PUSH FLOW below — this is
248
+ what feeds the "resolved from prior" counter), (c) post
249
+ the summary comment. The summary's "still open" and
250
+ "resolved from prior" counters depend on the resolve-
251
+ mutations in step (b), not on the new posts in (a) —
252
+ so step (b) MUST run before the summary, but step (a)
253
+ and (b) can run in either order.
254
+
255
+ FIX-PUSH FLOW (when prior claude[bot] threads exist):
256
+ If you see prior claude[bot] inline review threads from
257
+ earlier passes, list them and resolve the ones whose issue
258
+ is verifiably fixed in the current diff. This is what closes
259
+ the loop for the author — the "resolved from prior" counter
260
+ in the status block proves the bot read the fixes, not just
261
+ re-ran a fresh review.
262
+
263
+ List threads:
208
264
 
209
265
  gh api graphql -f query='{ repository(owner: "${{ github.repository_owner }}", name: "${{ github.event.repository.name }}") { pullRequest(number: '"$PR_NUMBER"') { reviewThreads(first: 30) { nodes { id isResolved comments(first: 1) { nodes { body author { login } } } } } } } }'
210
266
 
211
- For each unresolved thread you (claude[bot]) authored where the
212
- issue is now addressed:
267
+ For each unresolved thread you (claude[bot]) authored where
268
+ the issue is now addressed by the head diff:
213
269
 
214
270
  gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "<id>"}) { thread { isResolved } } }'
215
271
 
216
- Only resolve threads where the fix is verifiable in the diff.
217
- Leave unresolved any thread whose issue still stands.
218
- For line-specific issues, use the github_inline_comment MCP tool
219
- with confirmed: true.
220
- If there are no critical issues, post a one-line comment saying so.
272
+ Only resolve threads where the fix is verifiable in the
273
+ diff. Leave unresolved any thread whose issue still stands
274
+ those become "still open" in the status block.
275
+
276
+ If there are no critical findings, you still post the summary
277
+ comment with the H2 header and "**This round:** 0 critical · …"
278
+ status line — strict mode + the status counters need the
279
+ comment to exist for every review pass.
221
280
 
222
281
  # Strict-mode gate — composite action; see workflow.yml.tmpl for design notes.
223
282
  - name: Strict mode — fail check on critical findings
224
283
  if: success()
225
- uses: thrillmot/clud-bug/.github/actions/strict-mode-gate@v0.5.12
284
+ uses: thrillmot/clud-bug/.github/actions/strict-mode-gate@v0.5.13
226
285
  with:
227
286
  github-token: ${{ secrets.GITHUB_TOKEN }}
@@ -1,4 +1,4 @@
1
- # clud-bug-template-version: v6
1
+ # clud-bug-template-version: v8
2
2
  name: Clud Bug 🐛 Crawls Your Code
3
3
 
4
4
  on:
@@ -126,7 +126,45 @@ jobs:
126
126
  never at the cost of clarity, evidence, or the critical-issues-only
127
127
  discipline. Don't perform the bit; let the precision speak.
128
128
 
129
- When you finish, post your review as a single PR comment.
129
+ Your review lives in TWO surfaces, in this order:
130
+
131
+ 1. INLINE REVIEW THREADS — one per finding, anchored to the
132
+ file:line cited in the finding. Use the
133
+ mcp__github_inline_comment__create_inline_comment MCP tool
134
+ for each finding (critical, minor, AND per-skill section
135
+ findings). The body should be the finding text itself
136
+ (without the leading "- " bullet). This is what creates
137
+ *resolvable conversations* the author can mark resolved
138
+ when the fix lands; branch protection's
139
+ required_review_thread_resolution rule gates the merge on
140
+ these threads — without inline review comments, the gate
141
+ has nothing to gate on and the loop never closes.
142
+
143
+ Pass `confirmed: true` on every call to the tool. These
144
+ are final review comments, not test probes. Without
145
+ `confirmed: true` the tool defers each call to an
146
+ auto-classifier that decides post-hoc whether the comment
147
+ is "real" — and a classifier miscategorization re-opens
148
+ the exact silent-no-op failure mode this prompt is
149
+ designed to prevent.
150
+
151
+ Findings that genuinely don't anchor to a specific line
152
+ (cross-cutting observations, "missing test coverage for
153
+ the new endpoint as a whole", etc.) stay in the summary
154
+ comment only. The default should be: if you can name
155
+ file:line, post it inline. Only fall back to summary-only
156
+ when the finding spans many files or is structural.
157
+
158
+ 2. SUMMARY PR COMMENT — one top-level comment via
159
+ `gh pr comment` that contains the H2 header, status line,
160
+ per-skill scan block, and per-skill findings sections.
161
+ This is what the strict-mode gate reads (it greps the
162
+ H2 header for "— critical findings"). The findings
163
+ sections here can be brief summaries that point to the
164
+ inline threads above, OR include the same finding text
165
+ for grep-ability — your call, but the master verdict
166
+ header MUST appear in this comment.
167
+
130
168
  The comment body MUST start with this exact line so the
131
169
  project's identity is visible (the bot account will say
132
170
  claude[bot], but the comment header brands it as Clud Bug):
@@ -199,30 +237,51 @@ jobs:
199
237
  critical-issues-only and pii-and-compliance lens at once), so
200
238
  bundling preserves that signal.
201
239
 
202
- Post it via:
240
+ Post the summary via:
203
241
  gh pr comment "$PR_NUMBER" --body "<your review>"
204
242
 
205
- If you previously reviewed this PR (you'll see prior claude[bot]
206
- comments starting with "## 🐛 Clud Bug review"), resolve your
207
- prior unresolved inline review threads where the flagged issue
208
- is fixed in the current diff. List threads:
243
+ Each inline finding is posted separately via the
244
+ mcp__github_inline_comment__create_inline_comment tool
245
+ (with `confirmed: true` per surface 1 above). Ordering
246
+ within the review pass that matters for counter accuracy:
247
+ (a) post new inline findings, (b) resolve prior threads
248
+ whose issue is now fixed (FIX-PUSH FLOW below — this is
249
+ what feeds the "resolved from prior" counter), (c) post
250
+ the summary comment. The summary's "still open" and
251
+ "resolved from prior" counters depend on the resolve-
252
+ mutations in step (b), not on the new posts in (a) —
253
+ so step (b) MUST run before the summary, but step (a)
254
+ and (b) can run in either order.
255
+
256
+ FIX-PUSH FLOW (when prior claude[bot] threads exist):
257
+ If you see prior claude[bot] inline review threads from
258
+ earlier passes, list them and resolve the ones whose issue
259
+ is verifiably fixed in the current diff. This is what closes
260
+ the loop for the author — the "resolved from prior" counter
261
+ in the status block proves the bot read the fixes, not just
262
+ re-ran a fresh review.
263
+
264
+ List threads:
209
265
 
210
266
  gh api graphql -f query='{ repository(owner: "${{ github.repository_owner }}", name: "${{ github.event.repository.name }}") { pullRequest(number: '"$PR_NUMBER"') { reviewThreads(first: 30) { nodes { id isResolved comments(first: 1) { nodes { body author { login } } } } } } } }'
211
267
 
212
- For each unresolved thread you (claude[bot]) authored where the
213
- issue is now addressed:
268
+ For each unresolved thread you (claude[bot]) authored where
269
+ the issue is now addressed by the head diff:
214
270
 
215
271
  gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "<id>"}) { thread { isResolved } } }'
216
272
 
217
- Only resolve threads where the fix is verifiable in the diff.
218
- Leave unresolved any thread whose issue still stands.
219
- For line-specific issues, use the github_inline_comment MCP tool
220
- with confirmed: true.
221
- If there are no critical issues, post a one-line comment saying so.
273
+ Only resolve threads where the fix is verifiable in the
274
+ diff. Leave unresolved any thread whose issue still stands
275
+ those become "still open" in the status block.
276
+
277
+ If there are no critical findings, you still post the summary
278
+ comment with the H2 header and "**This round:** 0 critical · …"
279
+ status line — strict mode + the status counters need the
280
+ comment to exist for every review pass.
222
281
 
223
282
  # Strict-mode gate — composite action; see workflow.yml.tmpl for design notes.
224
283
  - name: Strict mode — fail check on critical findings
225
284
  if: success()
226
- uses: thrillmot/clud-bug/.github/actions/strict-mode-gate@v0.5.12
285
+ uses: thrillmot/clud-bug/.github/actions/strict-mode-gate@v0.5.13
227
286
  with:
228
287
  github-token: ${{ secrets.GITHUB_TOKEN }}
@@ -1,4 +1,4 @@
1
- # clud-bug-template-version: v6
1
+ # clud-bug-template-version: v8
2
2
  name: Clud Bug 🐛 Crawls Your Code
3
3
 
4
4
  on:
@@ -148,7 +148,45 @@ jobs:
148
148
  never at the cost of clarity, evidence, or the critical-issues-only
149
149
  discipline. Don't perform the bit; let the precision speak.
150
150
 
151
- When you finish, post your review as a single PR comment.
151
+ Your review lives in TWO surfaces, in this order:
152
+
153
+ 1. INLINE REVIEW THREADS — one per finding, anchored to the
154
+ file:line cited in the finding. Use the
155
+ mcp__github_inline_comment__create_inline_comment MCP tool
156
+ for each finding (critical, minor, AND per-skill section
157
+ findings). The body should be the finding text itself
158
+ (without the leading "- " bullet). This is what creates
159
+ *resolvable conversations* the author can mark resolved
160
+ when the fix lands; branch protection's
161
+ required_review_thread_resolution rule gates the merge on
162
+ these threads — without inline review comments, the gate
163
+ has nothing to gate on and the loop never closes.
164
+
165
+ Pass `confirmed: true` on every call to the tool. These
166
+ are final review comments, not test probes. Without
167
+ `confirmed: true` the tool defers each call to an
168
+ auto-classifier that decides post-hoc whether the comment
169
+ is "real" — and a classifier miscategorization re-opens
170
+ the exact silent-no-op failure mode this prompt is
171
+ designed to prevent.
172
+
173
+ Findings that genuinely don't anchor to a specific line
174
+ (cross-cutting observations, "missing test coverage for
175
+ the new endpoint as a whole", etc.) stay in the summary
176
+ comment only. The default should be: if you can name
177
+ file:line, post it inline. Only fall back to summary-only
178
+ when the finding spans many files or is structural.
179
+
180
+ 2. SUMMARY PR COMMENT — one top-level comment via
181
+ `gh pr comment` that contains the H2 header, status line,
182
+ per-skill scan block, and per-skill findings sections.
183
+ This is what the strict-mode gate reads (it greps the
184
+ H2 header for "— critical findings"). The findings
185
+ sections here can be brief summaries that point to the
186
+ inline threads above, OR include the same finding text
187
+ for grep-ability — your call, but the master verdict
188
+ header MUST appear in this comment.
189
+
152
190
  The comment body MUST start with this exact line so the
153
191
  project's identity is visible (the bot account will say
154
192
  claude[bot], but the comment header brands it as Clud Bug):
@@ -221,26 +259,47 @@ jobs:
221
259
  critical-issues-only and pii-and-compliance lens at once), so
222
260
  bundling preserves that signal.
223
261
 
224
- Post it via:
262
+ Post the summary via:
225
263
  gh pr comment "$PR_NUMBER" --body "<your review>"
226
264
 
227
- If you previously reviewed this PR (you'll see prior claude[bot]
228
- comments starting with "## 🐛 Clud Bug review"), resolve your
229
- prior unresolved inline review threads where the flagged issue
230
- is fixed in the current diff. List threads:
265
+ Each inline finding is posted separately via the
266
+ mcp__github_inline_comment__create_inline_comment tool
267
+ (with `confirmed: true` per surface 1 above). Ordering
268
+ within the review pass that matters for counter accuracy:
269
+ (a) post new inline findings, (b) resolve prior threads
270
+ whose issue is now fixed (FIX-PUSH FLOW below — this is
271
+ what feeds the "resolved from prior" counter), (c) post
272
+ the summary comment. The summary's "still open" and
273
+ "resolved from prior" counters depend on the resolve-
274
+ mutations in step (b), not on the new posts in (a) —
275
+ so step (b) MUST run before the summary, but step (a)
276
+ and (b) can run in either order.
277
+
278
+ FIX-PUSH FLOW (when prior claude[bot] threads exist):
279
+ If you see prior claude[bot] inline review threads from
280
+ earlier passes, list them and resolve the ones whose issue
281
+ is verifiably fixed in the current diff. This is what closes
282
+ the loop for the author — the "resolved from prior" counter
283
+ in the status block proves the bot read the fixes, not just
284
+ re-ran a fresh review.
285
+
286
+ List threads:
231
287
 
232
288
  gh api graphql -f query='{ repository(owner: "${{ github.repository_owner }}", name: "${{ github.event.repository.name }}") { pullRequest(number: '"$PR_NUMBER"') { reviewThreads(first: 30) { nodes { id isResolved comments(first: 1) { nodes { body author { login } } } } } } } }'
233
289
 
234
- For each unresolved thread you (claude[bot]) authored where the
235
- issue is now addressed:
290
+ For each unresolved thread you (claude[bot]) authored where
291
+ the issue is now addressed by the head diff:
236
292
 
237
293
  gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "<id>"}) { thread { isResolved } } }'
238
294
 
239
- Only resolve threads where the fix is verifiable in the diff.
240
- Leave unresolved any thread whose issue still stands.
241
- For line-specific issues, use the github_inline_comment MCP tool
242
- with confirmed: true.
243
- If there are no critical issues, post a one-line comment saying so.
295
+ Only resolve threads where the fix is verifiable in the
296
+ diff. Leave unresolved any thread whose issue still stands
297
+ those become "still open" in the status block.
298
+
299
+ If there are no critical findings, you still post the summary
300
+ comment with the H2 header and "**This round:** 0 critical · …"
301
+ status line — strict mode + the status counters need the
302
+ comment to exist for every review pass.
244
303
 
245
304
  # Strict-mode gate. Fails the check when the BASE ref's manifest
246
305
  # has { "strictMode": true } AND the latest clud-bug review's first
@@ -257,6 +316,6 @@ jobs:
257
316
  # Letting the action's own failure fail the check is louder and right.
258
317
  - name: Strict mode — fail check on critical findings
259
318
  if: success()
260
- uses: thrillmot/clud-bug/.github/actions/strict-mode-gate@v0.5.12
319
+ uses: thrillmot/clud-bug/.github/actions/strict-mode-gate@v0.5.13
261
320
  with:
262
321
  github-token: ${{ secrets.GITHUB_TOKEN }}