@clipboard-health/ai-rules 2.15.4 → 2.15.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/ai-rules",
3
- "version": "2.15.4",
3
+ "version": "2.15.6",
4
4
  "description": "Pre-built AI agent rules for consistent coding standards.",
5
5
  "keywords": [
6
6
  "ai",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: babysit-pr
3
- description: "Watch a PR through CI and review feedback: commit/push, wait for CI, auto-fix high-confidence failures, reply to active review threads, and summarize parsed CodeRabbit review-body comments with sentinel-tagged comments. Runs once by default; pass a short interval like `30s` or `2m` for best-effort same-turn polling; longer cadences should use an external loop wrapper. Use when the user says 'babysit my PR', 'watch my PR', 'keep my PR moving', 'respond to comments', or 'loop on CI'."
4
- argument-hint: "[interval]"
3
+ description: "Watch a PR through CI and review feedback: commit/push, wait for CI, auto-fix high-confidence failures, reply to active review threads, and summarize parsed CodeRabbit review-body comments with sentinel-tagged comments. Runs one pass against the current branch's PR; pass a PR number or URL to `gh pr checkout` that PR first. Use when the user says 'babysit my PR', 'babysit PR 482', 'watch my PR', 'keep my PR moving', or 'respond to comments'."
4
+ argument-hint: "[pr-number-or-url]"
5
5
  ---
6
6
 
7
7
  # Babysit PR
@@ -12,22 +12,16 @@ This skill is self-contained: it does not invoke other skills. It works in Claud
12
12
 
13
13
  ## Inputs
14
14
 
15
- Parse an optional interval from the invocation arguments if the host exposes them; otherwise read the user's request text.
15
+ Parse an optional PR number or URL from the invocation arguments if the host exposes them; otherwise read the user's request text. Parse in **this priority order** and stop at the first match — do not fall back to a generic "first integer in prose" regex, which would grab unrelated numbers (issue refs, quoted counts, etc.):
16
16
 
17
- Recognize intervals with this regex (case-insensitive):
17
+ 1. **Full PR URL** if `$ARGUMENTS` or the user's text contains `https?://github\.com/[^/\s]+/[^/\s]+/pull/\d+`, capture the URL and pass it to `gh pr checkout` as-is.
18
+ 2. **Explicit PR token** — match `(?:PR|pr|pull request)\s*#?(\d+)` or a bare `#(\d+)` in the user's text. Capture the numeric group.
19
+ 3. **Bare numeric argument** — only when the entire `$ARGUMENTS` string is a positive integer (no surrounding prose).
20
+ 4. **None of the above** — operate on the PR for the current branch (existing Step 2 behavior).
18
21
 
19
- ```regex
20
- \b(\d+)\s*(s|sec|secs|second|seconds|m|min|mins|minute|minutes|h|hr|hrs|hour|hours)\b
21
- ```
22
-
23
- Rules:
22
+ When a match is found, the checkout happens in Preflight before Step 2.
24
23
 
25
- - If multiple matches appear in the user's text, take the **first** match.
26
- - Accept a bare number as seconds **only** when the bare number is the entire argument (not embedded in prose).
27
- - Normalize to seconds: `s*=1`, `m*=60`, `h*=3600`.
28
- - Empty → one iteration, then exit with a summary.
29
- - Normalized `<= 240` → best-effort same-turn loop: `sleep <seconds>` between iterations.
30
- - Normalized `> 240` → run one pass, then report that longer cadences need an external loop wrapper (the Claude Code `/loop` skill or a shell `while` loop outside the agent). Do not sleep inside the agent turn — blocking `sleep` past ~5 minutes will exceed prompt-cache TTLs and may hit tool-call timeouts.
24
+ This skill always runs exactly one pass. It never waits or repeats internally. For recurring execution, wrap the call with `/loop <cadence> /babysit-pr` or an external shell `while` loop.
31
25
 
32
26
  ## Sentinels
33
27
 
@@ -72,12 +66,22 @@ gh auth status
72
66
 
73
67
  If this fails, stop and tell the user to run `gh auth login`.
74
68
 
69
+ If a PR number or URL was parsed from Inputs, check out that PR now:
70
+
71
+ ```bash
72
+ gh pr checkout <pr-number-or-url>
73
+ ```
74
+
75
+ This switches the local worktree to the PR's head branch (and handles forks automatically). If the command fails (PR not found, conflicting local branch, etc.), stop and report. The `git status --short` check above runs first so we never check out over dirty work.
76
+
75
77
  ### 2. Locate the PR
76
78
 
77
79
  ```bash
78
80
  gh pr view --json number,url,headRefName,statusCheckRollup 2>/dev/null
79
81
  ```
80
82
 
83
+ If Preflight checked out a PR explicitly, `gh pr view` will find it by construction and the fallback below does not apply. The fallback only fires when no PR number was supplied and the current branch has no open PR.
84
+
81
85
  If no PR exists for the current branch:
82
86
 
83
87
  - Verify there are commits ahead of the base branch: `git log --oneline @{u}..HEAD 2>/dev/null || git log --oneline origin/HEAD..HEAD`. If nothing is ahead, stop and report "no commits to push".
@@ -278,7 +282,7 @@ Report:
278
282
  - Review threads replied to, grouped by verdict (including any Defer count: "X threads deferred as follow-ups").
279
283
  - Nitpicks summarized (or skipped because already covered), including the Deferred count: "Y nitpicks deferred as follow-ups".
280
284
  - Threads left active because of bot-acknowledgement uncertainty (flag by thread URL).
281
- - The stop condition triggered for this iteration (clean / stuck / continue / long-interval / sanity-cap).
285
+ - The stop condition triggered for this pass (clean / progressing / stuck).
282
286
 
283
287
  When the report mentions any deferrals, include a one-liner the user can run later to enumerate them, e.g.:
284
288
 
@@ -290,20 +294,18 @@ Do not rely only on `gh pr view --json comments,reviews` — that view can miss
290
294
 
291
295
  ## Loop control
292
296
 
293
- After an iteration, pick exactly one outcome:
294
-
295
- - **Exit clean** — all CI checks passed AND every thread in `activeThreads` was either marked Skip-reply during step 6's inspection or has already received a fresh sentinel reply in this iteration (Agree / Disagree / Already-fixed / **Defer** all count — a Defer reply is a sentinel reply), AND every current nitpick fingerprint is covered by an existing sentinel comment (deferred nitpicks count; they're in the summary's fingerprint block). Do not use raw `totalActiveThreads` from the script output — it is pre-inspection and will stay non-zero for Skip-reply cases. A PR with Deferred threads is still clean from babysit's perspective: the skill has done what it can without widening scope. Report success and stop.
296
- - **Exit stuck** — iteration made no commits, posted no new replies, and no CI check changed state from the previous iteration. Report state and stop; tell the user to investigate.
297
- - **Continue** — interval set, normalized `<= 240`, not clean, not stuck:
297
+ After the single pass completes, pick exactly one outcome:
298
298
 
299
- ```bash
300
- sleep <interval-seconds>
301
- ```
299
+ - **Exit clean** — all CI checks passed AND every thread in `activeThreads` was either marked Skip-reply during step 6's inspection or has already received a fresh sentinel reply in this pass (Agree / Disagree / Already-fixed / **Defer** all count — a Defer reply is a sentinel reply), AND every current nitpick fingerprint is covered by an existing sentinel comment (deferred nitpicks count; they're in the summary's fingerprint block). Do not use raw `totalActiveThreads` from the script output — it is pre-inspection and will stay non-zero for Skip-reply cases. A PR with Deferred threads is still clean from babysit's perspective: the skill has done what it can without widening scope. Report success and stop.
300
+ - **Exit progressing** — pass made commits, posted new replies, or both, and the PR is not yet clean (CI is still pending, a new CI run was triggered by this pass's commits, or more work remains). There is real work still in flight that another run would pick up. Report what was done and what is pending, and tell the user to re-run `/babysit-pr` once CI settles, or to wrap the call with `/loop <cadence> /babysit-pr` (or a shell `while true; do ...; done`) for automatic re-runs.
301
+ - **Exit stuck** — pass made no commits and posted no new replies, and the PR is still not clean. Nothing actionable happened this pass. Use this whenever progress is blocked on something outside the skill's scope, including:
302
+ - CI still running (`gh pr checks --watch` timed out with pending checks).
303
+ - CI failing with a diagnosis-only verdict from Step 5 (flaky / infra / auth / external check / ambiguous / out-of-scope failure).
304
+ - Only Skip-reply threads remained AND CI was already red or pending.
302
305
 
303
- Then go back to step 1.
306
+ Report the specific blocker (pending vs. diagnosed-failure, with the diagnosis text) and tell the user to investigate or re-run once state may have changed.
304
307
 
305
- - **Long interval** interval set, normalized `> 240`. Do not sleep. Run one pass, report state, and tell the user to wrap the call in an external loop (Claude Code `/loop <interval> /babysit-pr` or a shell `while true; do ...; sleep ...; done`).
306
- - **Sanity cap** — hard-stop at 20 iterations regardless, with a clear "sanity cap hit" message. The cap only applies to internal looping.
308
+ This skill never waits or repeats internally.
307
309
 
308
310
  ## Portability notes
309
311
 
@@ -319,20 +321,22 @@ After an iteration, pick exactly one outcome:
319
321
 
320
322
  User: `babysit my PR`
321
323
 
322
- - No intervalone iteration.
324
+ - No PR arg operate on the current branch.
323
325
  - Preflight OK, PR #482 found.
324
326
  - `gh pr checks --watch` times out at 600s — two checks still pending.
325
327
  - `unresolvedPrComments.sh` returns 0 active threads, 0 nitpicks.
326
328
  - No commits, no replies posted, CI state unchanged vs. start.
327
- - Outcome: **stuck**. Report: "CI still running after 10 min; no comments to address. Re-run `/babysit-pr 2m` once CI settles, or wait and invoke again."
329
+ - Outcome: **stuck**. Report: "CI still running after 10 min; no comments to address. Re-run `/babysit-pr` once CI settles, or wrap with `/loop 2m /babysit-pr`."
328
330
 
329
- ### Example 2: `babysit-pr 2m` loop, exits clean on iteration 3
331
+ ### Example 2: explicit PR number checks out and babysits that PR
330
332
 
331
- User: `babysit-pr 2m`
333
+ User: `babysit PR 482`
332
334
 
333
- - Iteration 1: CI green, 3 active threads (1 Agree, 1 Disagree, 1 Already-fixed), 2 nitpicks (both Agree). Apply fixes, commit `a1b2c3d`, post 3 thread replies + 1 nitpick summary. Not clean (CI needs to re-run), not stuck. `sleep 120`.
334
- - Iteration 2: CI fails (lint on the nitpick fix). Log shows unused import. High-confidence + in scope → remove import, commit `d4e5f6a`, push. Threads are all addressed. `sleep 120`.
335
- - Iteration 3: CI green, 0 active threads, 0 new nitpick fingerprints. **Exit clean.** Report final commit SHAs and reply URLs.
335
+ - Preflight OK. Input parser matches the explicit-token rule and captures `482`.
336
+ - `gh pr checkout 482` switches the worktree to PR #482's head branch (say, `feat/xyz`).
337
+ - Step 2's `gh pr view` confirms PR #482 on the now-current branch; the new-PR fallback does not fire.
338
+ - Remainder proceeds as a normal single pass (CI watch, thread / nitpick assessment, replies).
339
+ - Report final state on exit.
336
340
 
337
341
  ### Example 3: out-of-scope nitpick gets deferred
338
342
 
@@ -353,4 +357,4 @@ User: `babysit my PR`
353
357
 
354
358
  ## Input
355
359
 
356
- Interval: $ARGUMENTS
360
+ PR number or URL: $ARGUMENTS
@@ -1,85 +0,0 @@
1
- ---
2
- name: fix-ci
3
- description: "Analyze and fix CI failures for a GitHub pull request. Use this when CI checks are failing, the build is red, or you need to diagnose GitHub Actions failures. Triggers on: 'fix CI', 'CI is failing', 'checks failed', 'build is broken', 'tests failing in CI', 'why is CI red', or any request to investigate and resolve PR check failures."
4
- argument-hint: "[pr-url]"
5
- ---
6
-
7
- # Fix CI Failures
8
-
9
- Diagnose and fix CI failures for a GitHub pull request by retrieving logs, identifying root causes, and applying targeted fixes.
10
-
11
- ## Arguments
12
-
13
- - `$ARGUMENTS` - GitHub PR URL (e.g., `https://github.com/owner/repo/pull/123`). If omitted, uses the PR for the current branch.
14
-
15
- ## Instructions
16
-
17
- ### Step 1: Get PR and Identify Failed Checks
18
-
19
- ```bash
20
- # If $ARGUMENTS contains a PR URL, extract the PR number from the path.
21
- # Otherwise, get the PR for the current branch:
22
- gh pr view --json number,url,headRefName,statusCheckRollup
23
- ```
24
-
25
- From `statusCheckRollup`, find checks where `conclusion` is `"failure"` or `state` is `"FAILURE"`. If no checks have failed, report that CI is green and stop.
26
-
27
- When multiple checks failed, prioritize **build/compile checks over test checks** — build errors are often the root cause of downstream test failures, so fixing them first may resolve multiple failures at once.
28
-
29
- ### Step 2: Retrieve Failed Job Logs
30
-
31
- Extract the run ID from the failed check's `detailsUrl` (the numeric suffix of the GitHub Actions URL, e.g., `.../actions/runs/123456789`).
32
-
33
- ```bash
34
- # List failed job IDs for the run
35
- gh run view $RUN_ID --json jobs --jq '.jobs[] | select(.conclusion == "failure") | .databaseId'
36
-
37
- # Get only failed step output (concise)
38
- gh run view --job $JOB_ID --log-failed
39
- ```
40
-
41
- Start with `--log-failed` because it shows only the output from failed steps, cutting through potentially thousands of lines of passing output. Only fall back to `--log` with targeted grep if `--log-failed` doesn't provide enough context.
42
-
43
- When parsing large log output, focus on:
44
-
45
- - **The first error message** — later failures often cascade from this
46
- - **Summary lines** like "Test Suites:", "FAILED", build error counts
47
- - **Stack traces** immediately after assertion or compilation errors
48
-
49
- ### Step 3: Diagnose Root Cause
50
-
51
- Read the relevant source files and tests to understand the failure in context.
52
-
53
- **Build/compilation errors** — type errors, missing imports, syntax issues. These block everything else.
54
- **Test failures** — read both the failing test and the code it exercises. The fix could be in either place; don't assume the test is wrong just because it's failing.
55
- **Lint/format errors** — usually auto-fixable with project formatting tools.
56
- **Infrastructure issues** — missing env vars, CI config problems, dependency resolution failures.
57
-
58
- ### Step 4: Present Findings and Proposed Fix
59
-
60
- Before changing code, present:
61
-
62
- 1. **Root cause** — what's failing and why
63
- 2. **Proposed fix** — which files to change and what the changes are
64
-
65
- Then ask the user whether to proceed. This confirmation step matters because CI failures can be ambiguous — what looks like a test bug might actually be the test correctly catching a real regression.
66
-
67
- ### Step 5: Apply Fix and Verify
68
-
69
- After approval, implement the changes. Then run the relevant checks locally (build, test, lint for the affected project/files) to verify the fix before pushing. Report the local results.
70
-
71
- ### Failure Patterns
72
-
73
- **Removed code but tests still reference it:** Delete or update the tests. Check for leftover references in shared test fixtures or configurations.
74
-
75
- **Missing mocks/providers after adding a dependency:** Add the mock to test module setup. Update existing mocks when signatures change.
76
-
77
- **Type errors from interface changes:** Fix in the changed files. If the type change is intentional, update downstream consumers too.
78
-
79
- **Snapshot mismatches:** Check if the change is expected (you modified rendering/output). If so, update snapshots. If not, investigate the regression.
80
-
81
- **Flaky/intermittent failures:** If the test passes locally, the failure looks timing-dependent, or it's unrelated to the PR's changes, flag this to the user rather than making speculative changes.
82
-
83
- ## Input
84
-
85
- Pull Request URL: $ARGUMENTS
@@ -1,135 +0,0 @@
1
- ---
2
- name: iterate-pr
3
- description: "Iteratively commit, push, and address PR feedback until CI passes and comments are resolved. Use this skill when the user wants to get a PR to a mergeable state, fix CI and address review comments in a loop, or says things like 'iterate on my PR', 'make this PR pass', 'fix everything on the PR', 'get this PR ready to merge', or 'keep going until CI is green'."
4
- argument-hint: "[max-iterations]"
5
- ---
6
-
7
- # Iterate PR
8
-
9
- Autonomously iterate on a pull request until CI checks pass and all comments are resolved. Each iteration runs in a fresh subagent.
10
-
11
- ## Arguments
12
-
13
- - `$ARGUMENTS` - Maximum iterations (default: 3)
14
-
15
- ## Instructions
16
-
17
- ### Step 1: Initialize
18
-
19
- Parse max iterations from `$ARGUMENTS` (default: 3). Set iteration counter to 0.
20
-
21
- ### Step 2: Check State
22
-
23
- Get the PR for the current branch:
24
-
25
- !`gh pr view --json number,url,headRefName,statusCheckRollup 2>/dev/null || echo "NO_PR"`
26
-
27
- **If no PR exists:** Proceed to Step 3 (first iteration will create one).
28
-
29
- **If PR exists:** Get unresolved comments data:
30
-
31
- !`bash "${CLAUDE_PLUGIN_ROOT:-.agents}/skills/unresolved-pr-comments/scripts/unresolvedPrComments.sh" 2>/dev/null`
32
-
33
- Parse the JSON output and evaluate exit conditions.
34
-
35
- **If any CI checks are still PENDING, QUEUED, or IN_PROGRESS:** Proceed to Step 3 regardless of comment count. Automated reviewers (e.g. CodeRabbit) may not have posted comments yet, so comment data is unreliable until all checks complete.
36
-
37
- **If statusCheckRollup is empty or null:** Treat CI as passing (some PRs have no required checks configured).
38
-
39
- **Exit with success** when ALL conditions are met:
40
-
41
- - All CI checks have completed (no PENDING, QUEUED, or IN_PROGRESS statuses in statusCheckRollup) and none have failed (or no checks exist)
42
- - No unresolved comments (totalUnresolvedComments == 0)
43
- - No nitpicks (totalNitpicks == 0)
44
-
45
- **JSON Structure for Exit Evaluation:**
46
-
47
- ```json
48
- {
49
- "totalUnresolvedComments": number,
50
- "totalNitpicks": number,
51
- "unresolvedComments": [...],
52
- "nitpickComments": [...]
53
- }
54
- ```
55
-
56
- Check `totalUnresolvedComments == 0` and `totalNitpicks == 0` for exit condition.
57
-
58
- Report: "PR is clean! All CI checks pass and no unresolved comments."
59
-
60
- **Exit with status report** when iteration counter >= max iterations:
61
-
62
- - List failing CI checks
63
- - List unresolved comment/nitpick counts
64
- - Suggest running `/iterate-pr` again to continue
65
-
66
- ### Step 3: Spawn Iteration Subagent
67
-
68
- Spawn a Task subagent with `subagent_type: "general-purpose"` using this prompt:
69
-
70
- > Handle one iteration of the PR feedback loop:
71
- >
72
- > 1. **Record Starting Commit**: !`git rev-parse HEAD` (save as `startCommit`)
73
- > 2. **Commit and Push**: Invoke `core:commit-push-pr` via the Skill tool
74
- > 3. **Get PR Number**: !`gh pr view --json number --jq '.number'`
75
- > 4. **Wait for CI**: !`rc=0; if command -v gtimeout >/dev/null 2>&1; then gtimeout 600 gh pr checks --watch || rc=$?; elif command -v timeout >/dev/null 2>&1; then timeout 600 gh pr checks --watch || rc=$?; else gh pr checks --watch || rc=$?; fi; case $rc in 0|1|8|124) ;; *) exit $rc;; esac` (10 minute timeout; exit codes: 0=pass, 1=fail, 8=pending, 124=timeout are expected and handled in next step; other codes like 4=auth error are re-raised; uses gtimeout on macOS, timeout on Linux, no timeout as fallback)
76
- > 5. **Check CI Status**: Run `gh pr checks --json name,state,bucket` and parse the output
77
- > - If any check has `bucket: "fail"`, invoke `core:fix-ci` via the Skill tool. Since you are running autonomously, do NOT wait for user approval — apply the fixes directly. Report what was fixed and exit.
78
- > 6. **Check Comments**: Run `bash "${CLAUDE_PLUGIN_ROOT:-.agents}/skills/unresolved-pr-comments/scripts/unresolvedPrComments.sh"` and parse the JSON output
79
- > - If unresolved comments or nitpicks exist:
80
- > 1. Group comments by file path and read each file once (not per-comment)
81
- > 2. If a file no longer exists, note the comment may be outdated and skip it
82
- > 3. Assess each comment with an explicit verdict:
83
- > - **Agree**: Explain why, then fix it
84
- > - **Disagree**: Explain why the current code is acceptable; do NOT change the code
85
- > - **Already fixed**: Note that the code already addresses this concern
86
- > 4. After assessing all comments, fix only those you agreed with and exit (next iteration will commit fixes)
87
- > 7. **Report**: Run `git rev-parse HEAD` and compare to `startCommit` to determine if commits were made. Include PR status, CI status, and comments addressed
88
-
89
- ### Step 4: Loop
90
-
91
- After the subagent completes:
92
-
93
- 1. Increment iteration counter
94
- 2. If no commits were made this iteration:
95
- - Get unresolved comments by running: `bash "${CLAUDE_PLUGIN_ROOT:-.agents}/skills/unresolved-pr-comments/scripts/unresolvedPrComments.sh"`
96
- - If unresolved comments remain, exit with: "Comments addressed, awaiting reviewer resolution. Run `/iterate-pr` after reviewer responds."
97
- 3. Report: "Iteration [N]/[max] complete. Checking state..."
98
- 4. Return to Step 2
99
-
100
- ## Examples
101
-
102
- **Successful completion:**
103
-
104
- ```text
105
- Iteration 1/3: No PR exists
106
- > commit-push-pr -> PR #347 created
107
- > CI failed (lint errors) -> fix-ci -> fixed missing semicolons
108
-
109
- Iteration 2/3: CI failures remain
110
- > commit-push-pr -> pushed fixes
111
- > CI passed, 2 review comments
112
- > unresolved-pr-comments -> addressed null check, const usage
113
-
114
- Iteration 3/3: Comments pending
115
- > commit-push-pr -> pushed fixes
116
- > CI passed, no unresolved comments
117
-
118
- PR is clean! All CI checks pass and no unresolved comments.
119
- ```
120
-
121
- **Awaiting reviewer resolution:**
122
-
123
- ```text
124
- Iteration 1/3: PR exists with comments
125
- > commit-push-pr -> pushed comment fixes
126
- > CI passed, 1 comment remains (disagreed with suggestion)
127
-
128
- Iteration 2/3: No commits made, comments remain
129
-
130
- Comments addressed, awaiting reviewer resolution. Run `/iterate-pr` after reviewer responds.
131
- ```
132
-
133
- ## Input
134
-
135
- Maximum iterations: $ARGUMENTS
@@ -1,123 +0,0 @@
1
- ---
2
- name: learn-from-session
3
- description: |
4
- Analyze the current session for agent efficiency, quality, and actionable improvements. Use this skill when the user says things like "learn from this session", "session retro", "what went well", "how did the agent do", "what should I improve", "review this session", "session review", or any request to reflect on how the current session went. Also use when the user wants to extract learnings, identify friction, or improve their workflow based on what just happened.
5
- ---
6
-
7
- You are reviewing the current Claude Code session. Your job is to surface the 2-3 most impactful findings — things that would actually change how the next session goes — not to produce an exhaustive report card.
8
-
9
- Be candid. A session where everything scores 4/5 but you have nothing concrete to suggest is a wasted review. Prioritize specificity over coverage: one sharp observation beats five generic ones.
10
-
11
- Analyze the session transcript and produce the following:
12
-
13
- ## Agent efficiency analysis
14
-
15
- Evaluate how well the agent used tools and how much human course-correction was needed.
16
-
17
- Score each dimension 1-5. Calibration: 1=actively harmful or completely wrong approach, 2=significant waste or frequent missteps, 3=adequate but with clear room for improvement, 4=good with minor issues, 5=genuinely impressive and hard to improve on. Reserve 5 for sessions that would make you say "I wish the agent always worked like this."
18
-
19
- - **Tool precision**: Did the agent use the right tools for each task, or did it flail between tools, run unnecessary reads, or use grep when it should have used targeted file reads?
20
- - **Iteration efficiency**: How many attempts did it take to get things right? Count tool retries, failed bash commands, and edit-then-re-edit cycles.
21
- - **Context utilization**: Did the agent leverage CLAUDE.md, AGENTS.md, and project conventions, or did it ignore available context and make assumptions?
22
- - **Autonomy level**: How often did the agent work without human intervention? Each rejection/abort/course-correction is a friction event.
23
- - **Autonomy span**: What was the longest streak of productive tool calls without human intervention?
24
-
25
- Provide a brief narrative (2-3 sentences) explaining the scores with specific examples from the session.
26
-
27
- ## Agent quality analysis
28
-
29
- Evaluate how well the agent followed project coding standards and CLAUDE.md rules.
30
-
31
- Use the same 1-5 calibration as above.
32
-
33
- - **CLAUDE.md compliance**: Did the agent follow the rules and conventions defined in CLAUDE.md and AGENTS.md? Flag specific violations.
34
- - **Code pattern adherence**: Did the generated code follow established project patterns (naming conventions, error handling, file organization, test structure)?
35
- - **Test coverage intent**: Did the agent write or update tests when modifying behavior? Did it run tests before declaring done?
36
- - **PR hygiene**: Is the commit history clean? Are changes scoped appropriately? Did the agent avoid touching unrelated files?
37
- - **Documentation awareness**: Did the agent update relevant docs, comments, or type definitions when changing interfaces?
38
-
39
- Provide a brief narrative (2-3 sentences) explaining the scores with specific examples.
40
-
41
- ## Session reflection
42
-
43
- Surface things that would save time if known at the start of the next session. The bar for inclusion: "would I tell a teammate about this before they start working on this codebase?" Skip categories with nothing worth noting.
44
-
45
- - **Bash commands**: Commands that were used, discovered, or would have been useful to know upfront.
46
- - **Code style patterns**: Patterns followed or discovered that aren't yet documented.
47
- - **Testing approaches**: Testing strategies that worked or should have been used.
48
- - **Environment/configuration quirks**: Gotchas, workarounds, or setup details encountered.
49
- - **Warnings**: Errors, deprecations, or edge cases that tripped up the agent.
50
-
51
- ## Actionable improvements
52
-
53
- This is the most important section. Produce recommendations in these categories, but only if genuinely actionable — a suggestion the user can apply in under 5 minutes. Skip any category with nothing worth doing.
54
-
55
- ### CLAUDE.md updates
56
-
57
- **Do not suggest rules that duplicate existing automated enforcement.** If a lint rule, pre-commit hook, CI check, or other tooling already catches an issue, documenting it in CLAUDE.md/AGENTS.md is redundant. Before suggesting a rule, check whether the session transcript shows the error was already caught and blocked by automated tooling (e.g. a pre-commit hook rejected the commit, a linter flagged the issue). If so, skip it — the tooling is already doing its job.
58
-
59
- For each suggestion, specify whether it belongs in:
60
-
61
- - **Team-shared** (checked into git, e.g. `./CLAUDE.md` or `./AGENTS.md`)
62
- - **Personal/local** (git-ignored, e.g. `~/.claude/CLAUDE.md`)
63
-
64
- Format each as a ready-to-paste diff:
65
-
66
- ```diff
67
- + [the addition - keep it brief]
68
- ```
69
-
70
- ### Hooks, skills, or agents
71
-
72
- Suggest new or revised hooks, skills, or agents that would have prevented issues or improved the workflow.
73
-
74
- ### Prompt technique
75
-
76
- One concrete thing the engineer could do differently in their prompts to get better results (be specific, not generic).
77
-
78
- ### Agent pattern note
79
-
80
- One observation about how the agent behaved that the team should know about — a strength to replicate or a weakness to work around.
81
-
82
- ## Structured data block
83
-
84
- After the human-readable review, emit a fenced JSON block that a scraper can parse:
85
-
86
- ```json
87
- {
88
- "session_review": {
89
- "version": "1.1",
90
- "timestamp": "<ISO 8601>",
91
- "efficiency": {
92
- "tool_precision": <1-5>,
93
- "iteration_efficiency": <1-5>,
94
- "context_utilization": <1-5>,
95
- "autonomy_level": <1-5>,
96
- "autonomy_span": <1-5>,
97
- "friction_events": <count of rejections/aborts/course-corrections>,
98
- "total_tool_calls": <count>,
99
- "failed_tool_calls": <count>
100
- },
101
- "quality": {
102
- "claude_md_compliance": <1-5>,
103
- "code_pattern_adherence": <1-5>,
104
- "test_coverage_intent": <1-5>,
105
- "pr_hygiene": <1-5>,
106
- "documentation_awareness": <1-5>,
107
- "violations": ["<brief description of each CLAUDE.md violation>"]
108
- },
109
- "improvements": {
110
- "claude_md_rules": ["<each suggested rule, ready to paste>"],
111
- "hooks_skills_agents": ["<each suggestion>"],
112
- "prompt_technique": "<the suggestion>",
113
- "agent_pattern_note": "<the observation>"
114
- }
115
- }
116
- }
117
- ```
118
-
119
- ## Presenting the review
120
-
121
- 1. Display the human-readable sections.
122
- 2. If there is an open PR for the current branch, offer to post the review as a PR comment (using `gh pr comment` with `--body-file`).
123
- 3. If the user has CLAUDE.md updates to apply, offer to apply them directly.
@@ -1,47 +0,0 @@
1
- ---
2
- name: unresolved-pr-comments
3
- description: "Get unresolved review comments from a GitHub pull request. Use this skill when the user asks about PR feedback, review comments, unresolved threads, what reviewers said, CodeRabbit review-body comments, or wants to address PR review feedback. Also use when the user says 'check my PR', 'what's left on my PR', or 'resolve comments'."
4
- argument-hint: "[pr-number]"
5
- ---
6
-
7
- # Unresolved PR Comments
8
-
9
- Fetch and analyze unresolved review comments from a GitHub pull request.
10
-
11
- ## Usage
12
-
13
- Run the script to fetch PR comment data:
14
-
15
- ```bash
16
- bash scripts/unresolvedPrComments.sh [pr-number]
17
- ```
18
-
19
- If no PR number is provided, it uses the PR associated with the current branch.
20
-
21
- Note: Limited to 100 review threads, 10 comments per thread, and 100 reviews.
22
-
23
- ## Processing Instructions
24
-
25
- Using the JSON output from the script:
26
-
27
- 1. **If error**: Display the error message and suggest the fix
28
- 2. **If no comments**: Report the PR has no pending feedback
29
- 3. **If comments exist**: Present a brief summary (e.g., "Found 3 unresolved comments and 5 CodeRabbit review-body comments")
30
-
31
- Then, for EVERY comment (both `unresolvedComments` AND `nitpickComments`):
32
-
33
- 1. Group comments by file path and read each file once (not per-comment)
34
- 2. If a file no longer exists, note that the comment may be outdated
35
- 3. Assess each comment against the current code. When multiple comments appear at the same file and line, they are part of the same review thread — read them together as a conversation
36
- 4. Group your assessment as follows:
37
-
38
- **Should address**
39
- - Description of the comment with file path(s)
40
- - Why it's a real issue (bug, a11y, UX, etc.)
41
-
42
- **Can ignore**
43
- - Description of the comment with file path(s)
44
- - Why: already fixed, not actionable, not worth addressing in this PR, etc.
45
-
46
- **Net**
47
- Summary of how many are worth fixing and what kind of issues they are. Offer to fix, but **do NOT start until the user confirms**.
@@ -1,165 +0,0 @@
1
- #!/usr/bin/env bash
2
- # parseNitpicks.sh — Parse CodeRabbit review-body comments from PR review bodies.
3
- # Sourced by unresolvedPrComments.sh. Requires: jq, perl.
4
-
5
- # Extract CodeRabbit review-body comments from reviews JSON (passed via stdin).
6
- # Outputs a JSON array of comment objects. The function name is retained for
7
- # backward compatibility with existing skill scripts.
8
- extract_nitpick_comments() {
9
- local reviews_json="$1"
10
-
11
- printf '%s' "$reviews_json" | perl -e '
12
- use strict;
13
- use warnings;
14
- use JSON::PP;
15
-
16
- local $/;
17
- my $reviews_json = <STDIN>;
18
- my $reviews = decode_json($reviews_json);
19
-
20
- # Find latest coderabbitai review with supported review-body comment sections.
21
- my $latest_review;
22
- my $latest_time = "";
23
- for my $review (@$reviews) {
24
- my $author = $review->{author}{login} // "";
25
- my $body = $review->{body} // "";
26
- next unless $author eq "coderabbitai" && has_supported_sections($body);
27
- my $created = $review->{createdAt} // "";
28
- if ($created gt $latest_time) {
29
- $latest_time = $created;
30
- $latest_review = $review;
31
- }
32
- }
33
-
34
- unless ($latest_review) {
35
- print "[]";
36
- exit 0;
37
- }
38
-
39
- my $body = $latest_review->{body};
40
- my $author = $latest_review->{author}{login} // "deleted-user";
41
- my $created_at = $latest_review->{createdAt} // "";
42
-
43
- my @sections = extract_review_body_comment_sections($body);
44
- unless (@sections) {
45
- print "[]";
46
- exit 0;
47
- }
48
-
49
- my @comments;
50
- for my $section (@sections) {
51
- my $section_content = $section->{content};
52
- my $category = $section->{category};
53
-
54
- # Extract file sections: <details><summary>filename (count)</summary><blockquote>...</blockquote></details>
55
- while ($section_content =~ /<details>\s*<summary>([^<]+?)\s+\(\d+\)<\/summary>\s*<blockquote>([\s\S]*?)<\/blockquote>\s*<\/details>/g) {
56
- my $raw_file_name = trim($1);
57
- my $file_content = $2;
58
-
59
- # Extract individual comments: `line-range`: severity metadata, **title**, body
60
- while ($file_content =~ /`(\d+(?:-\d+)?)`:\s*(?:_[^_]+_\s*\|\s*_[^_]+_\s*)?\*\*([^*]+)\*\*\s*([\s\S]*?)(?=---|\n`\d|<\/blockquote>|$)/g) {
61
- my $line_range = $1;
62
- my $title = trim($2);
63
- my $clean_body = clean_comment_body(trim($3));
64
- my $file_name = normalize_file_name($raw_file_name, $line_range);
65
- push @comments, {
66
- author => $author,
67
- body => "$title\n\n$clean_body",
68
- category => $category,
69
- createdAt => $created_at,
70
- file => $file_name,
71
- line => $line_range,
72
- };
73
- }
74
- }
75
- }
76
-
77
- print encode_json(\@comments);
78
-
79
- sub has_supported_sections {
80
- my ($text) = @_;
81
- $text = strip_markdown_blockquote_prefixes($text);
82
- return $text =~ /<summary>\s*[^<]*(?:Nitpick comments|Minor comments|Outside diff range comments)\s*\(\d+\)<\/summary>\s*<blockquote>/i;
83
- }
84
-
85
- sub extract_review_body_comment_sections {
86
- my ($text) = @_;
87
- $text = strip_markdown_blockquote_prefixes($text);
88
-
89
- my @sections;
90
- while ($text =~ /<summary>\s*[^<]*(Nitpick comments|Minor comments|Outside diff range comments)\s*\(\d+\)<\/summary>\s*<blockquote>/ig) {
91
- my $category = section_category($1);
92
- my $content_start = $+[0];
93
- my $after = substr($text, $content_start);
94
-
95
- my $depth = 1;
96
- my @tags;
97
- while ($after =~ /(<blockquote>|<\/blockquote>)/gi) {
98
- my $tag = $1;
99
- my $pos = $-[0];
100
- my $is_open = ($tag =~ /^<blockquote>/i) ? 1 : 0;
101
- push @tags, [$pos, $is_open];
102
- }
103
- for my $tag (@tags) {
104
- $depth += $tag->[1] ? 1 : -1;
105
- if ($depth == 0) {
106
- push @sections, {
107
- category => $category,
108
- content => substr($after, 0, $tag->[0]),
109
- };
110
- last;
111
- }
112
- }
113
- }
114
- return @sections;
115
- }
116
-
117
- sub section_category {
118
- my ($label) = @_;
119
- return "nitpick" if $label =~ /Nitpick comments/i;
120
- return "minor" if $label =~ /Minor comments/i;
121
- return "outside-diff" if $label =~ /Outside diff range comments/i;
122
- return "unknown";
123
- }
124
-
125
- sub normalize_file_name {
126
- my ($file_name, $line_range) = @_;
127
- my $suffix = "-" . $line_range;
128
- $file_name =~ s/\Q$suffix\E$//;
129
- return $file_name;
130
- }
131
-
132
- sub strip_markdown_blockquote_prefixes {
133
- my ($text) = @_;
134
- $text =~ s/^[ \t]*>[ \t]?//mg;
135
- return $text;
136
- }
137
-
138
- sub clean_comment_body {
139
- my ($text) = @_;
140
- # Iteratively remove innermost <details> elements
141
- my $prev = "";
142
- while ($text ne $prev) {
143
- $prev = $text;
144
- $text =~ s/<details>(?:(?!<details>)[\s\S])*?<\/details>//g;
145
- }
146
- $text =~ s/</&lt;/g;
147
- $text =~ s/>/&gt;/g;
148
- return trim($text);
149
- }
150
-
151
- sub trim {
152
- my ($s) = @_;
153
- $s =~ s/^\s+//;
154
- $s =~ s/\s+$//;
155
- return $s;
156
- }
157
- '
158
- }
159
-
160
- # Extract code scanning alert number from comment body.
161
- # Outputs the alert number or empty string.
162
- extract_code_scanning_alert_number() {
163
- local body="$1"
164
- printf '%s' "$body" | perl -ne 'print $1 if m{/code-scanning/(\d+)}'
165
- }
@@ -1,246 +0,0 @@
1
- #!/usr/bin/env bash
2
- # unresolvedPrComments.sh — Fetch unresolved review comments from a GitHub PR.
3
- # Usage: bash unresolvedPrComments.sh [pr-number]
4
- # Outputs JSON with unresolved comments and CodeRabbit nitpicks.
5
- # Compatible with macOS bash 3.2. Requires: gh, jq, perl.
6
-
7
- set -euo pipefail
8
-
9
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
10
- # shellcheck source=parseNitpicks.sh
11
- source "${SCRIPT_DIR}/parseNitpicks.sh"
12
-
13
- # Save original stdout so output_error works inside $() command substitutions
14
- exec 3>&1
15
-
16
- # --- Helpers (inlined from ghClient.ts / prClient.ts) ---
17
-
18
- output_error() {
19
- printf '%s' "$1" | jq -Rsc '{ error: . }' >&3
20
- exit 1
21
- }
22
-
23
- validate_prerequisites() {
24
- if ! command -v jq >/dev/null 2>&1; then
25
- printf '{"error":"jq not found. Install from https://stedolan.github.io/jq"}\n' >&3
26
- exit 1
27
- fi
28
- if ! command -v gh >/dev/null 2>&1; then
29
- output_error "gh CLI not found. Install from https://cli.github.com"
30
- fi
31
- if ! command -v perl >/dev/null 2>&1; then
32
- output_error "perl not found."
33
- fi
34
- if ! gh api user --jq '.login' >/dev/null 2>&1; then
35
- output_error "Not authenticated with GitHub. Run: gh auth login"
36
- fi
37
- }
38
-
39
- get_pr_number() {
40
- local arg="${1:-}"
41
- if [ -n "$arg" ]; then
42
- if ! printf '%s' "$arg" | grep -qE '^[0-9]+$'; then
43
- output_error "Invalid PR number: ${arg}"
44
- fi
45
- printf '%s' "$arg"
46
- return
47
- fi
48
-
49
- local pr_json
50
- if ! pr_json="$(gh pr view --json number 2>/dev/null)"; then
51
- output_error "No PR found for current branch. Provide PR number as argument."
52
- fi
53
-
54
- local pr_num
55
- pr_num="$(printf '%s' "$pr_json" | jq -r '.number // empty')"
56
- if [ -z "$pr_num" ]; then
57
- output_error "No PR found for current branch. Provide PR number as argument."
58
- fi
59
- printf '%s' "$pr_num"
60
- }
61
-
62
- get_repo_info() {
63
- local repo_json
64
- if ! repo_json="$(gh repo view --json owner,name 2>/dev/null)"; then
65
- output_error "Could not determine repository. Are you in a git repo with a GitHub remote?"
66
- fi
67
-
68
- REPO_OWNER="$(printf '%s' "$repo_json" | jq -r '.owner.login // empty')"
69
- REPO_NAME="$(printf '%s' "$repo_json" | jq -r '.name // empty')"
70
-
71
- if [ -z "$REPO_OWNER" ] || [ -z "$REPO_NAME" ]; then
72
- output_error "Failed to parse repository info from gh CLI output."
73
- fi
74
- }
75
-
76
- # --- GraphQL ---
77
-
78
- # Pagination limits: 100 review threads, 10 comments per thread, 100 reviews.
79
- # Sufficient for typical PRs; data may be truncated on exceptionally active PRs.
80
- GRAPHQL_QUERY='
81
- query($owner: String!, $repo: String!, $pr: Int!) {
82
- repository(owner: $owner, name: $repo) {
83
- pullRequest(number: $pr) {
84
- title
85
- url
86
- reviewThreads(first: 100) {
87
- nodes {
88
- isResolved
89
- comments(first: 10) {
90
- nodes {
91
- body
92
- path
93
- line
94
- originalLine
95
- author { login }
96
- createdAt
97
- }
98
- }
99
- }
100
- }
101
- reviews(first: 100) {
102
- nodes {
103
- body
104
- author { login }
105
- createdAt
106
- }
107
- }
108
- }
109
- }
110
- }'
111
-
112
- execute_graphql_query() {
113
- local owner="$1" repo="$2" pr_number="$3"
114
- local result
115
- if ! result="$(gh api graphql \
116
- -f "query=${GRAPHQL_QUERY}" \
117
- -f "owner=${owner}" \
118
- -f "repo=${repo}" \
119
- -F "pr=${pr_number}" 2>&1)"; then
120
- output_error "GraphQL query failed: ${result}"
121
- fi
122
- printf '%s' "$result"
123
- }
124
-
125
- # --- Code scanning filter ---
126
-
127
- is_code_scanning_alert_fixed() {
128
- local owner="$1" repo="$2" alert_number="$3"
129
- local result
130
- if ! result="$(gh api "repos/${owner}/${repo}/code-scanning/alerts/${alert_number}" 2>/dev/null)"; then
131
- return 1
132
- fi
133
-
134
- local state
135
- state="$(printf '%s' "$result" | jq -r '.most_recent_instance.state // empty')"
136
- [ "$state" = "fixed" ]
137
- }
138
-
139
- # --- Main ---
140
-
141
- main() {
142
- validate_prerequisites
143
-
144
- local pr_number
145
- pr_number="$(get_pr_number "${1:-}")"
146
-
147
- get_repo_info
148
- local owner="$REPO_OWNER"
149
- local repo="$REPO_NAME"
150
-
151
- local response
152
- response="$(execute_graphql_query "$owner" "$repo" "$pr_number")"
153
-
154
- # Validate response
155
- if [ "$(printf '%s' "$response" | jq -r '.data.repository // empty')" = "" ]; then
156
- output_error "Repository ${owner}/${repo} not found or not accessible."
157
- fi
158
- if [ "$(printf '%s' "$response" | jq -r '.data.repository.pullRequest // empty')" = "" ]; then
159
- output_error "PR #${pr_number} not found or not accessible."
160
- fi
161
-
162
- local title url
163
- title="$(printf '%s' "$response" | jq -r '.data.repository.pullRequest.title')"
164
- url="$(printf '%s' "$response" | jq -r '.data.repository.pullRequest.url')"
165
-
166
- # Extract unresolved comments: filter unresolved threads, flatten comments
167
- local all_unresolved
168
- all_unresolved="$(printf '%s' "$response" | jq '[
169
- .data.repository.pullRequest.reviewThreads.nodes[]
170
- | select(.isResolved == false)
171
- | .comments.nodes[]
172
- | {
173
- author: (.author.login // "deleted-user"),
174
- body: .body,
175
- createdAt: .createdAt,
176
- file: .path,
177
- line: (.line // .originalLine)
178
- }
179
- ]')"
180
-
181
- # Filter out fixed code-scanning alerts from github-advanced-security
182
- local unresolved_comments="[]"
183
- local count
184
- count="$(printf '%s' "$all_unresolved" | jq 'length')"
185
-
186
- local i=0
187
- while [ "$i" -lt "$count" ]; do
188
- local comment
189
- comment="$(printf '%s' "$all_unresolved" | jq ".[$i]")"
190
- local comment_author
191
- comment_author="$(printf '%s' "$comment" | jq -r '.author')"
192
-
193
- local keep=true
194
- if [ "$comment_author" = "github-advanced-security" ]; then
195
- local comment_body alert_number
196
- comment_body="$(printf '%s' "$comment" | jq -r '.body')"
197
- alert_number="$(extract_code_scanning_alert_number "$comment_body")"
198
- if [ -n "$alert_number" ]; then
199
- if is_code_scanning_alert_fixed "$owner" "$repo" "$alert_number"; then
200
- keep=false
201
- fi
202
- fi
203
- fi
204
-
205
- if [ "$keep" = true ]; then
206
- unresolved_comments="$(printf '%s' "$unresolved_comments" | jq --argjson c "$comment" '. + [$c]')"
207
- fi
208
-
209
- i=$((i + 1))
210
- done
211
-
212
- # Extract nitpick comments from reviews
213
- local reviews_json
214
- reviews_json="$(printf '%s' "$response" | jq '[.data.repository.pullRequest.reviews.nodes[]]')"
215
- local nitpick_comments
216
- nitpick_comments="$(extract_nitpick_comments "$reviews_json")"
217
-
218
- # Build final output
219
- local total_unresolved total_nitpicks
220
- total_unresolved="$(printf '%s' "$unresolved_comments" | jq 'length')"
221
- total_nitpicks="$(printf '%s' "$nitpick_comments" | jq 'length')"
222
-
223
- jq -n \
224
- --argjson nitpickComments "$nitpick_comments" \
225
- --arg owner "$owner" \
226
- --argjson prNumber "$pr_number" \
227
- --arg repo "$repo" \
228
- --arg title "$title" \
229
- --argjson totalNitpicks "$total_nitpicks" \
230
- --argjson totalUnresolvedComments "$total_unresolved" \
231
- --argjson unresolvedComments "$unresolved_comments" \
232
- --arg url "$url" \
233
- '{
234
- nitpickComments: $nitpickComments,
235
- owner: $owner,
236
- prNumber: $prNumber,
237
- repo: $repo,
238
- title: $title,
239
- totalNitpicks: $totalNitpicks,
240
- totalUnresolvedComments: $totalUnresolvedComments,
241
- unresolvedComments: $unresolvedComments,
242
- url: $url
243
- }'
244
- }
245
-
246
- main "$@"