Catalogskills.sh · adrianbrowning

cc-pr-review-ci

Comprehensive TypeScript/React PR review. Outputs structured review.json consumed by a posting script that creates a GitHub Review with inline comments. Agent runs read-only; the workflow script holds write permissions.

Review advisedVerified hub445 installs

Savant verdict: Review advised

Security analysis or source-hub review identified patterns requiring attention.

Live evaluation

Not evaluated yet. Workspaces can request a live evaluation.

Security assessment

Scan pending.

Structure

  • Ships 1 executable scriptpost-review.js. Review what they do before enabling the skill for agents with tool access.
  • No license declaredConfirm you may reuse this skill before importing it into your repository.
  • The source hub's security checks flagged this skillSocket: warn (1 alert: gptAnomaly); Snyk: warn (Risk: MEDIUM · 1 issue)

SKILL.md

---
name: cc-pr-review-ci
description: Comprehensive TypeScript/React PR review. Outputs structured review.json consumed by a posting script that creates a GitHub Review with inline comments. Agent runs read-only; the workflow script holds write permissions.
---

# PR Review (single-agent, CI-safe)

Review a PR across 12 domains sequentially, then write `review.json`. A separate posting script turns the JSON into GitHub inline review comments.

*If no PR number provided, diff against `origin/main` and print JSON to stdout.*

---

## Step 0 — Load prior dismissals (PR runs only)

Skip if no PR number provided.

Fetch all existing bot reviews on this PR:

```bash
gh api repos/{owner}/{repo}/pulls/$PR_NUMBER/reviews \
  --jq '[.[] | select(.body | test("<!-- cc-pr-review -->";  "i")) | {id: .id, commit_id: .commit_id, submitted_at: .submitted_at}]'
```

For each bot review found, fetch its inline comments:

```bash
gh api repos/{owner}/{repo}/pulls/$PR_NUMBER/reviews/$REVIEW_ID/comments \
  --jq '[.[] | {id: .id, body: .body, path: .path, line: (.line // .original_line), reactions: .reactions}]'
```

Also fetch any reply threads on those inline comments:

```bash
gh api repos/{owner}/{repo}/pulls/$PR_NUMBER/comments \
  --jq '[.[] | select(.in_reply_to_id != null) | {id: .id, in_reply_to_id: .in_reply_to_id, body: .body}]'
```

Build **`SUPPRESSED_FINDINGS`**: one entry per dismissed finding, `{ id, path, line, commit_id }`, where `commit_id` is the commit the prior review was posted against.

A finding is dismissed if its prior inline comment has:
- A 👎 reaction (`.reactions["-1"] > 0`), **or**
- A human reply containing dismissal language: "intentional", "by design", "won't fix", "false positive", "ignore", "not applicable", "expected"

Extract the finding `id` from the prior comment body — each inline comment posted by the script contains an HTML comment `<!-- id: {finding-id} -->` in the first line.

**Escape hatch — re-evaluate if the code changed since the dismissal.** The PR diff always contains every PR-changed line, so compare against the prior review's commit instead:

```bash
HEAD_SHA=$(gh pr view $PR_NUMBER --json headRefOid --jq .headRefOid)
gh api repos/{owner}/{repo}/compare/$COMMIT_ID...$HEAD_SHA \
  --jq '[.files[] | {filename: .filename, previous_filename: .previous_filename, patch: .patch}]'
```

Run this once per distinct `commit_id`. Remove an entry from `SUPPRESSED_FINDINGS` when its `path` matches a file's `filename` or `previous_filename` and any of these hold:
- A hunk overlaps the finding. For each hunk header `@@ -start,count +...`, the old-side interval is `start` to `start + max(count, 1) - 1` (`count` is 1 when omitted). It overlaps if that interval intersects `line - 5` to `line + 5`. Check the whole interval, not just `start`: a hunk that begins well before the finding can still span it.
- The entry has no `line` (file-level finding).
- The file has no `patch` (binary or too large to diff).

Also remove every entry for a `commit_id` whose compare call fails (for example, the commit was force-pushed away).

If no prior reviews or no dismissals: `SUPPRESSED_FINDINGS` is empty.

---

## Step 1 — Get the diff and PR metadata

- PR number given → `gh pr view $PR_NUMBER` (title and body, used by the scope domain) and `gh pr diff $PR_NUMBER`
- No PR number → `git diff origin/main` (no metadata; the scope domain skips PR hygiene)

---

## Step 2 — Run 12 domain reviews sequentially

Read the reference file for each domain, then analyze the diff. Record findings with their exact file path and line number.

**Before recording any finding**: if its computed `id` (`{domain}-{kebab-title}`) is still in `SUPPRESSED_FINDINGS` after Step 0, skip it silently.

1. **Security**         — `Read .claude/skills/cc-pr-review-ci/references/security.md`
2. **Performance**      — `Read .claude/skills/cc-pr-review-ci/references/performance.md`
3. **React/TypeScript** — `Read .claude/skills/cc-pr-review-ci/references/react-ts.md`
4. **Testing**          — `Read .claude/skills/cc-pr-review-ci/references/testing.md`
5. **Test Validity**    — `Read .claude/skills/cc-pr-review-ci/references/test-validity.md`
6. **DevOps/CI**        — `Read .claude/skills/cc-pr-review-ci/references/devops.md`
7. **Holistic**         — `Read .claude/skills/cc-pr-review-ci/references/holistic.md`
8. **Duplication**      — `Read .claude/skills/cc-pr-review-ci/references/duplication.md`
9. **Bug Hunting**      — `Read .claude/skills/cc-pr-review-ci/references/bug.md`
10. **Scope/Hygiene**   — `Read .claude/skills/cc-pr-review-ci/references/scope.md`
11. **Maintainability** — `Read .claude/skills/cc-pr-review-ci/references/thermo.md`
12. **Comments**        — `Read .claude/skills/cc-pr-review-ci/references/comments.md`

---

## Step 3 — Write review.json

1. Read `.claude/skills/cc-pr-review-ci/references/format.md` for the exact schema.

2. Merge all domain findings into a single `findings` array. Assign each finding a stable `id` following the `{domain}-{kebab-title}` convention.

3. Determine verdict:
   - Any `critical` → `CHANGES_REQUESTED`
   - `high` only → `APPROVED_WITH_SUGGESTIONS`
   - `observations` only or none → `APPROVED`

4. Write the JSON:
   - **PR run**: write to `$REVIEW_OUTPUT_PATH` (default `/tmp/review.json`)
   - **Local run (no PR number)**: print to stdout

**Do not call `gh` to post anything.** The workflow's `post-review.js` step handles all GitHub writes.