clud-bug 0.5.7 → 0.5.9

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/lib/skills.js CHANGED
@@ -348,4 +348,56 @@ function sanitizeSlug(name) {
348
348
  return name.toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '');
349
349
  }
350
350
 
351
+ // Extract the `review_mode` field from a SKILL.md's frontmatter.
352
+ //
353
+ // Contract (from the v0.6 plan, option D-unified):
354
+ // - `shared` → the skill loads alongside other shared skills in ONE
355
+ // Claude call. Bug-finding baselines + most skills.sh
356
+ // contributions live here; they benefit from cross-
357
+ // correlation (an evidence-based finding flagged for
358
+ // critical-issues-only also gets the convention check).
359
+ // - `dedicated` → the skill gets its OWN focused Claude call. Reserved
360
+ // for domain-specific skills (brand voice, compliance,
361
+ // API-contract) where attention dilution at high skill
362
+ // counts is the real failure mode.
363
+ // - Missing field → default to `shared`. Conservative: the skill loads,
364
+ // no surprise per-skill API cost. Users opt skills INTO
365
+ // `dedicated` by authoring the field.
366
+ //
367
+ // The CLI runtime (v0.5.9) honors this via prompt restructuring inside a
368
+ // single claude-code-action call. The v0.6 GitHub App will use the same
369
+ // field to route to literal parallel API calls. Single source of truth.
370
+ export function readReviewMode(skillContent) {
371
+ if (typeof skillContent !== 'string') return 'shared';
372
+ // Scope to the YAML frontmatter block (between the first two `---` lines).
373
+ // A `review_mode:` line in the body is documentation, not configuration.
374
+ const fm = skillContent.match(/^---\n([\s\S]*?)\n---/);
375
+ if (!fm) return 'shared';
376
+ const m = fm[1].match(/^review_mode:\s*(\S+)\s*$/m);
377
+ if (!m) return 'shared';
378
+ // Strip optional YAML string-quotes — `review_mode: "dedicated"` and
379
+ // `review_mode: 'dedicated'` are both valid YAML, but the (\S+) capture
380
+ // grabs the quotes too. Without this, quoted forms silently fell back
381
+ // to `shared` even though the author clearly meant dedicated.
382
+ const value = m[1].toLowerCase().replace(/^["']|["']$/g, '');
383
+ return value === 'dedicated' ? 'dedicated' : 'shared';
384
+ }
385
+
386
+ // Partition a set of loaded skills into {shared, dedicated} buckets per
387
+ // each skill's review_mode frontmatter. Expects skills with a `content`
388
+ // field (SKILL.md text). Skills without content default to `shared`.
389
+ //
390
+ // Shape: input is the same skill objects produced by loadBaseline /
391
+ // writeSkills / listInstalled. Output is two arrays of the same shape;
392
+ // caller decides what to do with each bucket.
393
+ export function partitionByReviewMode(skills) {
394
+ const shared = [];
395
+ const dedicated = [];
396
+ for (const skill of skills) {
397
+ const mode = readReviewMode(skill?.content);
398
+ (mode === 'dedicated' ? dedicated : shared).push(skill);
399
+ }
400
+ return { shared, dedicated };
401
+ }
402
+
351
403
  export const _internal = { normalizeList, sanitizeSlug, entryKey, MAX_SKILLS, API_BASE, MANIFEST_FILE };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clud-bug",
3
- "version": "0.5.7",
3
+ "version": "0.5.9",
4
4
  "description": "Claude PR review with project-aware skills. CLI installs a working GitHub Actions workflow and curates skills from skills.sh.",
5
5
  "homepage": "https://cludbug.dev",
6
6
  "bugs": "https://github.com/thrillmot/clud-bug/issues",
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: clud-bug-collaboration
3
3
  description: How Claude Code agents working in a clud-bug-installed repo should interact with the bot's review threads, strict-mode gate, and skill set. Use this skill whenever you're about to push a commit, address a clud-bug PR review comment, edit anything under .claude/skills/, modify .github/workflows/clud-bug-*.yml, or wonder why a PR check is red. Also use when planning work in a repo that has a `clud-bug-review` workflow installed — even if the user didn't mention clud-bug by name.
4
+ review_mode: shared
4
5
  ---
5
6
 
6
7
  # Working in a clud-bug-installed repo
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: critical-issues-only
3
3
  description: PR review discipline - flag only correctness, security, and performance issues. Skip nits.
4
+ review_mode: shared
4
5
  ---
5
6
 
6
7
  # Critical issues only
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: evidence-based-review
3
3
  description: Every PR review claim must quote the specific code being criticized. No hand-waving.
4
+ review_mode: shared
4
5
  ---
5
6
 
6
7
  # Evidence-based review
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: respect-existing-conventions
3
3
  description: Don't suggest changes that fight the codebase's established patterns. Match what's already there.
4
+ review_mode: shared
4
5
  ---
5
6
 
6
7
  # Respect existing conventions
@@ -1,4 +1,4 @@
1
- # clud-bug-template-version: v1
1
+ # clud-bug-template-version: v3
2
2
  name: Clud Bug 🐛 Crawls Your Code
3
3
 
4
4
  on:
@@ -56,7 +56,7 @@ jobs:
56
56
  anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
57
57
  track_progress: true
58
58
  claude_args: |
59
- --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh api graphql:*),Bash(gh api repos/:*),Bash(git show:*),Bash(cat .claude/skills/.clud-bug.json)"
59
+ --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh api graphql:*),Bash(gh api repos/:*),Bash(git show:*),Bash(cat .claude/skills/.clud-bug.json),Bash(cat .claude/skills/*/SKILL.md)"
60
60
  prompt: |
61
61
  {{PROJECT_DESCRIPTION}}
62
62
 
@@ -80,6 +80,23 @@ jobs:
80
80
  based-review]: this claim isn't anchored to a line"). Generic
81
81
  advice that contradicts a project skill is wrong by definition.
82
82
 
83
+ Skill routing — shared vs dedicated:
84
+ Each loaded skill carries a `review_mode:` field in its YAML
85
+ frontmatter at .claude/skills/<name>/SKILL.md. Two values:
86
+
87
+ - `review_mode: shared` — bug-finding / convention / evidence
88
+ skills. Their findings bundle into the standard "Critical
89
+ findings" / "Minor findings" sections.
90
+ - `review_mode: dedicated` — domain-specific skills (brand
91
+ voice, compliance, API-contract, test-discipline). Each
92
+ gets its own focused H3 section in the review.
93
+ - Missing field → treat as `shared`.
94
+
95
+ Before writing the review, scan each loaded skill's frontmatter
96
+ (the first `---`-delimited block of its SKILL.md) to identify
97
+ its review_mode. You can read them with:
98
+ cat .claude/skills/*/SKILL.md
99
+
83
100
  At the end of every review, append a single-line footer:
84
101
  Skills referenced: [skill-name-1, skill-name-2, ...]
85
102
  If you genuinely cited none, list "[none]" and explain why no
@@ -147,6 +164,38 @@ jobs:
147
164
  open" are both 0. On follow-up reviews after a fix-push,
148
165
  "resolved from prior" should typically be positive.
149
166
 
167
+ Per-skill scan block (required, immediately under the status line):
168
+ After the **This round:** counters, emit a "### Per-skill scan"
169
+ section with ONE line per loaded skill — even silent ones. This
170
+ is the anti-dilution layer: every loaded skill must be
171
+ acknowledged so authors can see their skill ran, even when it
172
+ produced no findings.
173
+
174
+ ### Per-skill scan
175
+ - [<skill-name>]: <one-sentence outcome>
176
+
177
+ Examples (mix of shared + dedicated, with and without findings):
178
+ - [critical-issues-only]: scanned all paths. 2 critical findings below.
179
+ - [evidence-based-review]: applied to all findings. ✓ all anchored.
180
+ - [respect-existing-conventions]: scanned for pattern fights. 0 findings.
181
+ - [brand-voice-review]: scanned 3 microcopy changes. 1 finding (below).
182
+ - [pii-and-compliance]: scanned logging + analytics. 0 findings.
183
+
184
+ Per-skill findings sections (dedicated-mode skills only):
185
+ For each dedicated-mode skill that produced one or more
186
+ findings, emit a dedicated H3 section before the standard
187
+ critical/minor buckets:
188
+
189
+ ### Brand voice [brand-voice-review]
190
+ - Finding: button label "Click here!" violates verb-noun rule
191
+ (lib/ui/Button.tsx:42). Suggested: "Open settings."
192
+
193
+ Shared-mode skill findings stay in the existing combined
194
+ "Critical findings" / "Minor findings" buckets — they
195
+ cross-correlate (a logging-PII issue belongs in both the
196
+ critical-issues-only and pii-and-compliance lens at once), so
197
+ bundling preserves that signal.
198
+
150
199
  Post it via:
151
200
  gh pr comment "$PR_NUMBER" --body "<your review>"
152
201
 
@@ -168,23 +217,9 @@ jobs:
168
217
  with confirmed: true.
169
218
  If there are no critical issues, post a one-line comment saying so.
170
219
 
171
- # Strict-mode gate — see workflow.yml.tmpl for full design notes.
220
+ # Strict-mode gate — composite action; see workflow.yml.tmpl for design notes.
172
221
  - name: Strict mode — fail check on critical findings
173
222
  if: success()
174
- env:
175
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
176
- PR_NUMBER: ${{ github.event.pull_request.number }}
177
- run: |
178
- BASE_MANIFEST=$(git show "origin/${{ github.base_ref }}:.claude/skills/.clud-bug.json" 2>&1) || {
179
- echo "::warning::Base manifest not found on ${{ github.base_ref }} — strict mode disabled for this run."
180
- exit 0
181
- }
182
- STRICT=$(echo "$BASE_MANIFEST" | node -e "let s='';process.stdin.on('data',c=>s+=c);process.stdin.on('end',()=>{try{console.log(JSON.parse(s).strictMode===true)}catch(e){console.log('false')}})")
183
- [ "$STRICT" = "true" ] || exit 0
184
- LATEST=$(gh api "repos/${{ github.repository }}/issues/${PR_NUMBER}/comments?sort=created&direction=desc&per_page=100" \
185
- --jq '[.[] | select(.user.login == "claude[bot]" and (.body | startswith("## 🐛 Clud Bug review")))][0].body // ""')
186
- if echo "$LATEST" | head -n1 | grep -q "Clud Bug review — critical findings"; then
187
- echo "::error title=Clud Bug 🐛::Critical issues found and strictMode is enabled — failing this check."
188
- echo "::error::See the latest Clud Bug review comment for details. Push a fix and the gate will clear on the next run."
189
- exit 1
190
- fi
223
+ uses: thrillmot/clud-bug/.github/actions/strict-mode-gate@v0.5.8
224
+ with:
225
+ github-token: ${{ secrets.GITHUB_TOKEN }}
@@ -1,4 +1,4 @@
1
- # clud-bug-template-version: v1
1
+ # clud-bug-template-version: v3
2
2
  name: Clud Bug 🐛 Crawls Your Code
3
3
 
4
4
  on:
@@ -56,7 +56,7 @@ jobs:
56
56
  anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
57
57
  track_progress: true
58
58
  claude_args: |
59
- --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh api graphql:*),Bash(gh api repos/:*),Bash(git show:*),Bash(cat .claude/skills/.clud-bug.json)"
59
+ --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh api graphql:*),Bash(gh api repos/:*),Bash(git show:*),Bash(cat .claude/skills/.clud-bug.json),Bash(cat .claude/skills/*/SKILL.md)"
60
60
  prompt: |
61
61
  {{PROJECT_DESCRIPTION}}
62
62
 
@@ -81,6 +81,23 @@ jobs:
81
81
  based-review]: this claim isn't anchored to a line"). Generic
82
82
  advice that contradicts a project skill is wrong by definition.
83
83
 
84
+ Skill routing — shared vs dedicated:
85
+ Each loaded skill carries a `review_mode:` field in its YAML
86
+ frontmatter at .claude/skills/<name>/SKILL.md. Two values:
87
+
88
+ - `review_mode: shared` — bug-finding / convention / evidence
89
+ skills. Their findings bundle into the standard "Critical
90
+ findings" / "Minor findings" sections.
91
+ - `review_mode: dedicated` — domain-specific skills (brand
92
+ voice, compliance, API-contract, test-discipline). Each
93
+ gets its own focused H3 section in the review.
94
+ - Missing field → treat as `shared`.
95
+
96
+ Before writing the review, scan each loaded skill's frontmatter
97
+ (the first `---`-delimited block of its SKILL.md) to identify
98
+ its review_mode. You can read them with:
99
+ cat .claude/skills/*/SKILL.md
100
+
84
101
  At the end of every review, append a single-line footer:
85
102
  Skills referenced: [skill-name-1, skill-name-2, ...]
86
103
  If you genuinely cited none, list "[none]" and explain why no
@@ -148,6 +165,38 @@ jobs:
148
165
  open" are both 0. On follow-up reviews after a fix-push,
149
166
  "resolved from prior" should typically be positive.
150
167
 
168
+ Per-skill scan block (required, immediately under the status line):
169
+ After the **This round:** counters, emit a "### Per-skill scan"
170
+ section with ONE line per loaded skill — even silent ones. This
171
+ is the anti-dilution layer: every loaded skill must be
172
+ acknowledged so authors can see their skill ran, even when it
173
+ produced no findings.
174
+
175
+ ### Per-skill scan
176
+ - [<skill-name>]: <one-sentence outcome>
177
+
178
+ Examples (mix of shared + dedicated, with and without findings):
179
+ - [critical-issues-only]: scanned all paths. 2 critical findings below.
180
+ - [evidence-based-review]: applied to all findings. ✓ all anchored.
181
+ - [respect-existing-conventions]: scanned for pattern fights. 0 findings.
182
+ - [brand-voice-review]: scanned 3 microcopy changes. 1 finding (below).
183
+ - [pii-and-compliance]: scanned logging + analytics. 0 findings.
184
+
185
+ Per-skill findings sections (dedicated-mode skills only):
186
+ For each dedicated-mode skill that produced one or more
187
+ findings, emit a dedicated H3 section before the standard
188
+ critical/minor buckets:
189
+
190
+ ### Brand voice [brand-voice-review]
191
+ - Finding: button label "Click here!" violates verb-noun rule
192
+ (lib/ui/Button.tsx:42). Suggested: "Open settings."
193
+
194
+ Shared-mode skill findings stay in the existing combined
195
+ "Critical findings" / "Minor findings" buckets — they
196
+ cross-correlate (a logging-PII issue belongs in both the
197
+ critical-issues-only and pii-and-compliance lens at once), so
198
+ bundling preserves that signal.
199
+
151
200
  Post it via:
152
201
  gh pr comment "$PR_NUMBER" --body "<your review>"
153
202
 
@@ -169,23 +218,9 @@ jobs:
169
218
  with confirmed: true.
170
219
  If there are no critical issues, post a one-line comment saying so.
171
220
 
172
- # Strict-mode gate — see workflow.yml.tmpl for full design notes.
221
+ # Strict-mode gate — composite action; see workflow.yml.tmpl for design notes.
173
222
  - name: Strict mode — fail check on critical findings
174
223
  if: success()
175
- env:
176
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
177
- PR_NUMBER: ${{ github.event.pull_request.number }}
178
- run: |
179
- BASE_MANIFEST=$(git show "origin/${{ github.base_ref }}:.claude/skills/.clud-bug.json" 2>&1) || {
180
- echo "::warning::Base manifest not found on ${{ github.base_ref }} — strict mode disabled for this run."
181
- exit 0
182
- }
183
- STRICT=$(echo "$BASE_MANIFEST" | node -e "let s='';process.stdin.on('data',c=>s+=c);process.stdin.on('end',()=>{try{console.log(JSON.parse(s).strictMode===true)}catch(e){console.log('false')}})")
184
- [ "$STRICT" = "true" ] || exit 0
185
- LATEST=$(gh api "repos/${{ github.repository }}/issues/${PR_NUMBER}/comments?sort=created&direction=desc&per_page=100" \
186
- --jq '[.[] | select(.user.login == "claude[bot]" and (.body | startswith("## 🐛 Clud Bug review")))][0].body // ""')
187
- if echo "$LATEST" | head -n1 | grep -q "Clud Bug review — critical findings"; then
188
- echo "::error title=Clud Bug 🐛::Critical issues found and strictMode is enabled — failing this check."
189
- echo "::error::See the latest Clud Bug review comment for details. Push a fix and the gate will clear on the next run."
190
- exit 1
191
- fi
224
+ uses: thrillmot/clud-bug/.github/actions/strict-mode-gate@v0.5.8
225
+ with:
226
+ github-token: ${{ secrets.GITHUB_TOKEN }}
@@ -1,4 +1,4 @@
1
- # clud-bug-template-version: v1
1
+ # clud-bug-template-version: v3
2
2
  name: Clud Bug 🐛 Crawls Your Code
3
3
 
4
4
  on:
@@ -80,7 +80,7 @@ jobs:
80
80
  anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
81
81
  track_progress: true
82
82
  claude_args: |
83
- --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh api graphql:*),Bash(gh api repos/:*),Bash(git show:*),Bash(cat .claude/skills/.clud-bug.json)"
83
+ --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh api graphql:*),Bash(gh api repos/:*),Bash(git show:*),Bash(cat .claude/skills/.clud-bug.json),Bash(cat .claude/skills/*/SKILL.md)"
84
84
  prompt: |
85
85
  {{PROJECT_DESCRIPTION}}
86
86
 
@@ -101,6 +101,23 @@ jobs:
101
101
  based-review]: this claim isn't anchored to a line"). Generic
102
102
  advice that contradicts a project skill is wrong by definition.
103
103
 
104
+ Skill routing — shared vs dedicated:
105
+ Each loaded skill carries a `review_mode:` field in its YAML
106
+ frontmatter at .claude/skills/<name>/SKILL.md. Two values:
107
+
108
+ - `review_mode: shared` — bug-finding / convention / evidence
109
+ skills. Their findings bundle into the standard "Critical
110
+ findings" / "Minor findings" sections.
111
+ - `review_mode: dedicated` — domain-specific skills (brand
112
+ voice, compliance, API-contract, test-discipline). Each
113
+ gets its own focused H3 section in the review.
114
+ - Missing field → treat as `shared`.
115
+
116
+ Before writing the review, scan each loaded skill's frontmatter
117
+ (the first `---`-delimited block of its SKILL.md) to identify
118
+ its review_mode. You can read them with:
119
+ cat .claude/skills/*/SKILL.md
120
+
104
121
  At the end of every review, append a single-line footer:
105
122
  Skills referenced: [skill-name-1, skill-name-2, ...]
106
123
  If you genuinely cited none, list "[none]" and explain why no
@@ -168,6 +185,38 @@ jobs:
168
185
  open" are both 0. On follow-up reviews after a fix-push,
169
186
  "resolved from prior" should typically be positive.
170
187
 
188
+ Per-skill scan block (required, immediately under the status line):
189
+ After the **This round:** counters, emit a "### Per-skill scan"
190
+ section with ONE line per loaded skill — even silent ones. This
191
+ is the anti-dilution layer: every loaded skill must be
192
+ acknowledged so authors can see their skill ran, even when it
193
+ produced no findings.
194
+
195
+ ### Per-skill scan
196
+ - [<skill-name>]: <one-sentence outcome>
197
+
198
+ Examples (mix of shared + dedicated, with and without findings):
199
+ - [critical-issues-only]: scanned all paths. 2 critical findings below.
200
+ - [evidence-based-review]: applied to all findings. ✓ all anchored.
201
+ - [respect-existing-conventions]: scanned for pattern fights. 0 findings.
202
+ - [brand-voice-review]: scanned 3 microcopy changes. 1 finding (below).
203
+ - [pii-and-compliance]: scanned logging + analytics. 0 findings.
204
+
205
+ Per-skill findings sections (dedicated-mode skills only):
206
+ For each dedicated-mode skill that produced one or more
207
+ findings, emit a dedicated H3 section before the standard
208
+ critical/minor buckets:
209
+
210
+ ### Brand voice [brand-voice-review]
211
+ - Finding: button label "Click here!" violates verb-noun rule
212
+ (lib/ui/Button.tsx:42). Suggested: "Open settings."
213
+
214
+ Shared-mode skill findings stay in the existing combined
215
+ "Critical findings" / "Minor findings" buckets — they
216
+ cross-correlate (a logging-PII issue belongs in both the
217
+ critical-issues-only and pii-and-compliance lens at once), so
218
+ bundling preserves that signal.
219
+
171
220
  Post it via:
172
221
  gh pr comment "$PR_NUMBER" --body "<your review>"
173
222
 
@@ -189,12 +238,14 @@ jobs:
189
238
  with confirmed: true.
190
239
  If there are no critical issues, post a one-line comment saying so.
191
240
 
192
- # Strict-mode gate. Fails the check when both
193
- # (a) the BASE ref's .claude/skills/.clud-bug.json has
194
- # { "strictMode": true } read from base so a PR can't opt
195
- # itself out of strict mode by editing its own manifest, AND
196
- # (b) the LATEST clud-bug review comment on this PR has a body
197
- # whose FIRST LINE is "## 🐛 Clud Bug review critical findings".
241
+ # Strict-mode gate. Fails the check when the BASE ref's manifest
242
+ # has { "strictMode": true } AND the latest clud-bug review's first
243
+ # line starts with "## 🐛 Clud Bug review critical findings".
244
+ #
245
+ # Logic lives in the composite action so it's revised once across
246
+ # all 3 templates + the App runtime. Pinned to the same clud-bug
247
+ # tag the user installed (rendered by `clud-bug init`), so the
248
+ # action's contract is stable for the lifetime of that install.
198
249
  #
199
250
  # if: success() — only run when claude-code-action succeeded. If the
200
251
  # action errored, no new comment was posted for this run; falling back
@@ -202,30 +253,6 @@ jobs:
202
253
  # Letting the action's own failure fail the check is louder and right.
203
254
  - name: Strict mode — fail check on critical findings
204
255
  if: success()
205
- env:
206
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
207
- PR_NUMBER: ${{ github.event.pull_request.number }}
208
- run: |
209
- # Loud failure if the base manifest can't be read — silently falling
210
- # back to advisory would silently disable strict mode for every
211
- # opted-in repo. (Requires fetch-depth: 0 on the checkout above.)
212
- BASE_MANIFEST=$(git show "origin/${{ github.base_ref }}:.claude/skills/.clud-bug.json" 2>&1) || {
213
- echo "::warning::Base manifest not found on ${{ github.base_ref }} — strict mode disabled for this run."
214
- exit 0
215
- }
216
- STRICT=$(echo "$BASE_MANIFEST" | node -e "let s='';process.stdin.on('data',c=>s+=c);process.stdin.on('end',()=>{try{console.log(JSON.parse(s).strictMode===true)}catch(e){console.log('false')}})")
217
- [ "$STRICT" = "true" ] || exit 0
218
-
219
- # Use startswith (not regex contains) so comments that *quote* the
220
- # sentinel header (other reviews, @claude responses, meta-PRs about
221
- # strict mode itself) don't get picked up as "the latest review."
222
- LATEST=$(gh api "repos/${{ github.repository }}/issues/${PR_NUMBER}/comments?sort=created&direction=desc&per_page=100" \
223
- --jq '[.[] | select(.user.login == "claude[bot]" and (.body | startswith("## 🐛 Clud Bug review")))][0].body // ""')
224
-
225
- # Scope the critical-findings match to the FIRST LINE so quoted
226
- # sentinels deeper in the review can't trip the gate.
227
- if echo "$LATEST" | head -n1 | grep -q "Clud Bug review — critical findings"; then
228
- echo "::error title=Clud Bug 🐛::Critical issues found and strictMode is enabled — failing this check."
229
- echo "::error::See the latest Clud Bug review comment for details. Push a fix and the gate will clear on the next run."
230
- exit 1
231
- fi
256
+ uses: thrillmot/clud-bug/.github/actions/strict-mode-gate@v0.5.8
257
+ with:
258
+ github-token: ${{ secrets.GITHUB_TOKEN }}