@clipboard-health/ai-rules 2.29.10 → 2.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/ai-rules",
3
- "version": "2.29.10",
3
+ "version": "2.31.0",
4
4
  "description": "Pre-built AI agent rules for consistent coding standards.",
5
5
  "keywords": [
6
6
  "ai",
@@ -0,0 +1,306 @@
1
+ ---
2
+ name: cb-babysit
3
+ description: Watch a PR through CI and review feedback. Auto-fix high-confidence failures and address review comments. Use when the user says 'babysit <PR>' or 'respond to PR comments'.
4
+ argument-hint: "[pr-number-or-url]"
5
+ ---
6
+
7
+ ## Setup
8
+
9
+ The user invokes this skill with an optional PR number or URL. Parse in this order and stop at the first match:
10
+
11
+ 1. **Full PR URL**: if the user's text contains `https?://github\.com/[^/\s]+/[^/\s]+/pull/\d+`, capture the URL.
12
+ 2. **Explicit PR token**: match `(?:PR|pr|pull request)\s*#?(\d+)` or a bare `#(\d+)` in the user's text.
13
+ 3. **Bare numeric argument**: only when the complete text is a positive integer without surrounding prose.
14
+ 4. **None of the above**: operate on the PR for the current branch.
15
+
16
+ Resolve bundled "./scripts" paths relative to SKILL.md.
17
+
18
+ ## Sentinels
19
+
20
+ The skill uses two sentinels with visible footer lines.
21
+
22
+ **Addressed sentinel**: `<sub>🤖 <code>cb-babysit:addressed v1 core@3.12.0</code></sub>`. Appended on its own line at the end of every reply the skill posts so re-runs know which threads and review-body comments are already handled. Dedupe also recognizes the legacy `babysit-pr:addressed v1` prefix from before this skill was renamed.
23
+
24
+ **Follow-up sentinel**: `<sub>🤖 <code>cb-babysit:followup v1 core@3.12.0</code></sub>`. Attached to replies that defer an out-of-scope comment as a tracked follow-up. The sentinel is additive: the post-reply scripts still append the `addressed` sentinel at the end.
25
+
26
+ **Sentinel recency rules.** The script emits a per-thread `activityState` with three values. Step 6a owns the handling rules for each state.
27
+
28
+ - **`active`**: no sentinel yet, OR at least one human commented after the last sentinel.
29
+ - **`uncertain`**: a sentinel exists AND one or more bot comments appeared after it. The thread carries a `postSentinelBotComments` array listing EVERY such comment.
30
+ - **`addressed`**: the sentinel is the newest relevant activity on the thread.
31
+
32
+ Automated review bodies and top-level Conversation-tab comments each carry a stable `fingerprint` (sha256 of the normalized body); prior sentinel bodies embed them, and steps 4 and 7 do the dedupe.
33
+
34
+ ## Workflow
35
+
36
+ ### 1. Preflight
37
+
38
+ ```bash
39
+ git status --short
40
+ ```
41
+
42
+ If non-empty, classify the dirty files against the target PR's changed paths (`gh pr diff --name-only`):
43
+
44
+ - Any dirty file overlaps the PR's surface, or you cannot confidently classify → stop and report. The skill never starts where it might sweep up related in-progress work.
45
+ - All dirty files are clearly unrelated (disjoint paths, pre-existing edits) → stash exactly those files by name with a labeled stash (`git stash push -m "cb-babysit: pre-existing unrelated work (auto-restored)" -- <paths>`), proceed, and `git stash pop` as the final step of the pass, including on early exits. Report the stash/restore in the summary.
46
+
47
+ If a PR number or URL was parsed from inputs, check out that PR now:
48
+
49
+ ```bash
50
+ gh pr checkout <pr-number-or-url>
51
+ ```
52
+
53
+ If the command fails, stop and report.
54
+
55
+ ### 2. Locate the PR
56
+
57
+ ```bash
58
+ gh pr view --json number,url,headRefName,statusCheckRollup,mergeable,mergeStateStatus 2>/dev/null
59
+ ```
60
+
61
+ If no PR exists for the current branch, stop and report.
62
+
63
+ #### Resolve merge conflicts, if any
64
+
65
+ If `mergeable == "CONFLICTING"` (or `mergeStateStatus == "DIRTY"`), merge the base into the PR branch locally:
66
+
67
+ ```bash
68
+ BASE=$(gh pr view --json baseRefName --jq .baseRefName)
69
+ git fetch origin "$BASE"
70
+ git merge --no-edit "origin/$BASE"
71
+ ```
72
+
73
+ Resolve directly only for lockfile/generated regenerations, additive non-overlapping edits, or trivial textual conflicts in PR-touched files. `git merge --abort` for anything semantic, ambiguous, or outside the PR's intentional surface, and skip to step 10 to exit **stuck** with a diagnosis. After a clean resolution, commit the merge and `git push origin HEAD`.
74
+
75
+ ### 3. Wait for CI
76
+
77
+ Wrap the watch call with a timeout so a hung check doesn't wedge the turn:
78
+
79
+ ```bash
80
+ rc=0
81
+ if command -v gtimeout >/dev/null 2>&1; then
82
+ gtimeout 600 gh pr checks --watch || rc=$?
83
+ elif command -v timeout >/dev/null 2>&1; then
84
+ timeout 600 gh pr checks --watch || rc=$?
85
+ else
86
+ gh pr checks --watch || rc=$?
87
+ fi
88
+ case $rc in 0|1|8|124) ;; *) exit $rc;; esac
89
+ ```
90
+
91
+ Exit codes 0 (pass), 1 (fail), 8 (pending), and 124 (timeout) are expected and handled next. Other codes (auth errors, etc.) re-raise.
92
+
93
+ ### 4. Fetch review data
94
+
95
+ ```bash
96
+ bash scripts/unresolvedPrComments.sh
97
+ ```
98
+
99
+ The output JSON has:
100
+
101
+ - `threads`: every unresolved review thread, with `threadId`, `replyToCommentDatabaseId`, `comments[]`, `lastBabysitSentinelAt`, `lastHumanCommentAt`, `lastBotCommentAt`, `postSentinelBotComments[]`, `postSentinelHumanComments[]`, and `activityState` (`"active"` / `"uncertain"` / `"addressed"`).
102
+ - `activeThreads`: threads where `activityState != "addressed"`; these need attention this pass (active AND uncertain).
103
+ - `uncertainThreads`: just the uncertain subset. For each, read EVERY entry in `postSentinelBotComments` before deciding.
104
+ - `reviewBodyComments`: every review from a known automated reviewer (CodeRabbit, Mendral, Dependabot, etc.), with the raw body and a stable per-review `fingerprint`. The agent reads each body directly to extract findings.
105
+ - `issueComments`: every top-level Conversation-tab comment, each with `isBabysitSentinel`, `isKnownBot`, and a per-comment `fingerprint`.
106
+ - `activeIssueComments`: the subset of `issueComments` that are NOT cb-babysit sentinels, NOT from a known bot, and whose `fingerprint` is NOT already listed in any prior cb-babysit summary. These are the human Conversation-tab comments still needing a reply.
107
+ - `priorBabysitSentinels`: prior cb-babysit summary comments posted as PR issue-comments. The script does the dedupe lookup for `activeIssueComments` automatically; the agent uses this array for `reviewBodyComments` dedupe.
108
+ - `truncated`: array naming any GraphQL connection that hit GitHub's 100-item cap (`reviewThreads`, `thread-comments`, `reviews`, `issueComments`). Non-empty means some comments may not be in this JSON; surface this in the final summary.
109
+ - `totalActiveThreads`, `totalUncertainThreads`, `totalActiveIssueComments`, `totalReviewBodyComments`, `totalUnresolvedComments` for quick checks.
110
+
111
+ ### Scope
112
+
113
+ This PR's review-feedback scope is strict by default. Steps 6a (threads), 6b (top-level conversation comments), and 7 (automated review bodies) classify each comment as in-scope or out-of-scope using this rule before choosing a verdict. Step 5 (CI) uses the broader CI-scope rule in that step, not this one. CI can legitimately fail on unchanged lines because the PR changed a contract or dependency path.
114
+
115
+ Build the changed-line set from `gh pr diff` once per pass. Count changed diff lines on both sides: added lines in the new version, removed lines in the old version, and modified code represented by adjacent remove/add pairs. Do not count diff context lines. A reviewer comment or automated review-body comment is **in scope** when its anchor falls on a changed diff line on either side of the hunk. Deleted-line comments like "why remove this?" or "please add this back" are in scope by definition. For a range like `12-14`, any overlap with a changed diff line is in scope.
116
+
117
+ When matching review comments to hunks, use the anchor line provided by `unresolvedPrComments.sh`; it may be the current `line` or the script's fallback to `originalLine`. Compare that anchor against both new-side added ranges and old-side removed ranges.
118
+
119
+ Comments on unchanged/context lines, touched files outside changed lines, or untouched files are **out of scope by default**.
120
+
121
+ Narrow escape hatch: treat an unchanged/context-line comment as in scope only when there is an explicit external signal that this PR caused or requires the issue. Acceptable signals:
122
+
123
+ - The reviewer explicitly ties the concern to this PR's change.
124
+ - The comment points to an unchanged line directly used by a changed diff line, and you can name the changed `file:line` that creates the coupling.
125
+ - CI, test, or typecheck output proves the PR changed the contract or behavior for the symbol, API, or execution path named by the comment.
126
+
127
+ If you cannot name one of those signals, classify the comment as out of scope. Do not use broad judgment phrases like "related", "nearby", "maintainability", or "review confidence" to widen scope.
128
+
129
+ Default posture: focus on in-scope feedback. For out-of-scope feedback, apply the fix **only** if it meets the out-of-scope bar below. Otherwise defer with a follow-up reply.
130
+
131
+ **Out-of-scope fix bar** (apply the fix even though it's out of scope):
132
+
133
+ - Security vulnerability, data loss, or crash in the PR's execution path.
134
+ - Obvious correctness bug (wrong output, broken invariant) confirmed by reading the referenced code.
135
+ - One-line or trivial change that obviously cannot regress anything (typo, missing null check matching surrounding style, etc.).
136
+
137
+ **All other out-of-scope fix requests → Defer**: post a Defer reply tagged with the follow-up sentinel (see step 9). Do not expand the PR. Defer is specifically for "this is a real but out-of-scope ask we are choosing not to act on here."
138
+
139
+ ### 5. Handle CI failures
140
+
141
+ Run `bash scripts/fetchFailedLogs.sh` to stream failed output for every failing check on the PR. The first line is either:
142
+
143
+ - `# cb-babysit: no failing checks` → skip to step 6a.
144
+ - `# cb-babysit: failing checks` → followed by one delimited block per failing job or external check:
145
+ - `# --- run=<id> job=<id> ---` blocks carry the job's `--log-failed` output (GitHub Actions).
146
+ - `# --- external check: <name> (<url>) ---` blocks carry no logs, the check isn't a GitHub Actions run (e.g., Nx Cloud, semgrep, CodeRabbit). Treat these like "External checks with no inspectable logs" in the diagnosis-only list below: stop and report, don't guess a fix.
147
+
148
+ Read the logs and diagnose: **build/type errors first** (they cause cascading test failures), then lint/format, then tests.
149
+
150
+ **Apply a fix directly** only when the cause is high-confidence and inside the PR's changed surface:
151
+
152
+ - Compile/type errors in files the PR touched.
153
+ - Deterministic lint/format violations.
154
+ - Tests that the PR broke by renaming/removing symbols they reference.
155
+ - Missing test updates for intentional behavior changes.
156
+
157
+ **Stop and report a diagnosis** (do not guess a fix) for:
158
+
159
+ - Flaky/intermittent failures.
160
+ - Infrastructure or provider outages.
161
+ - Permission/auth/missing-secret failures.
162
+ - Unrelated failures (touching code this PR didn't modify).
163
+ - Ambiguous test intent.
164
+ - External checks with no inspectable logs.
165
+
166
+ Scope check for CI: scope is the PR's changed files plus failures directly caused by those changes in the PR's execution path. Use `gh pr diff --name-only` as the first signal. Allow fixes outside changed files only when the logs and code make causality clear (e.g., the PR renamed a symbol that a sibling test references). CI failures outside that surface are out of scope. Report the diagnosis, don't apply speculative fixes. CI fixes are never Deferred as follow-ups: CI needs to pass on this PR.
167
+
168
+ ### 6a. Assess active review threads
169
+
170
+ For every thread in `activeThreads` (this includes both `"active"` and `"uncertain"`):
171
+
172
+ - Group comments by file; read each file once (not per comment).
173
+ - If the referenced file no longer exists, record "comment may be outdated" and classify as **Already fixed**.
174
+ - If `activityState == "uncertain"`, read EVERY entry in `postSentinelBotComments` (not just the newest):
175
+ - If EVERY entry is a non-actionable acknowledgement (e.g., `"Thanks, resolved"`, `"LGTM"`, `"Learnings added"`) → mark the thread **Skip-reply** (the existing sentinel already covers the thread; posting again would be noise). Do not classify it Agree/Disagree/Already-fixed. Record this in the final summary so the skip is visible.
176
+ - If ANY entry carries new actionable content (new nit, new finding, corrected diagnosis) → treat the thread as new feedback and proceed below. Note in the final summary that an uncertain thread was reactivated, citing the specific comment.
177
+ - If you cannot confidently classify every entry → default to treating it as new feedback.
178
+ - Each remaining thread (i.e., NOT marked Skip-reply) gets a scope classification first. Use the Scope subsection to label it in-scope or out-of-scope. For comments on deleted lines, record that the anchor is on the removed side of the diff. For any unchanged/context-line comment classified in scope via the narrow escape hatch, record the external signal and the changed `file:line` when applicable.
179
+ - Then pick one verdict; each of these (except Skip-reply) will get a reply posted in step 9:
180
+ - **In-scope** threads use the original three verdicts:
181
+ - **Agree**: the comment identifies a real issue. Apply the fix. Record the thread ID and a one-line what-changed.
182
+ - **Disagree**: the current code is acceptable. Record a short reasoning.
183
+ - **Already fixed**: a prior commit addresses the concern. Record a pointer (commit SHA, file:line).
184
+ - **Out-of-scope** threads apply the out-of-scope fix bar from the Scope subsection:
185
+ - Meets the bar → **Agree** (apply the fix, and note in the reply that it was fixed despite being out of scope because it met the bar).
186
+ - Does not meet the bar → **Defer** (new verdict). Record a one-line rationale and, if relevant, a pointer to where the concern lives.
187
+ - Disagree and Already-fixed can still apply to out-of-scope comments (e.g., reviewer asks for a refactor that's already landed on main, or misreads the code).
188
+
189
+ ### 6b. Assess top-level Conversation-tab comments
190
+
191
+ For every entry in `activeIssueComments`, humans commenting on the PR Conversation tab without anchoring to a file/line:
192
+
193
+ - Apply the **Scope** subsection's rules. A top-level comment is in scope when the reviewer explicitly ties it to a changed file/line, behavior the PR introduced, or a contract the PR altered. Otherwise out of scope by default.
194
+ - Pick a verdict the same way as a thread: Agree / Disagree / Already fixed (in-scope), or Agree-meets-bar / Defer (out-of-scope). Apply fixes for Agree verdicts.
195
+ - Replies are NOT posted as individual top-level comments. That would clutter the conversation. Instead, every issue-comment verdict goes into the **same step-9 PR-level summary** as the review-body findings, under its own `## Conversation-tab comments` heading. Per-comment fingerprints join the fenced fingerprint block so future runs dedupe.
196
+ - If `activeIssueComments` is empty AND `reviewBodyComments` is empty (or all dedupe), skip the PR-level summary comment entirely in step 9.
197
+
198
+ ### 7. Assess automated review bodies
199
+
200
+ For every entry in `reviewBodyComments`:
201
+
202
+ - Dedupe first: if its `fingerprint` already appears in any `priorBabysitSentinels[].body`, skip; already covered.
203
+ - Otherwise, READ THE BODY IN FULL. Automated reviewers (CodeRabbit, Mendral, etc.) pack findings into nested `<details>/<blockquote>` HTML with file paths, line ranges, and titles inline. Identify each individual finding the body contains.
204
+ - For each finding, **classify scope** (in / out) using the Scope subsection. For ranges like `12-14`, any overlap with changed diff lines on either side of the hunk is in scope; no overlap is out of scope unless one of the explicit escape-hatch signals applies.
205
+ - Pick a verdict per finding:
206
+ - In-scope → Agree / Disagree / Already fixed (as with threads). If Agree, apply the fix.
207
+ - Out-of-scope → apply the out-of-scope fix bar. Meets the bar → Agree and apply the fix, noting in the summary that it was fixed despite being out of scope. Does not meet the bar → **Defer**. A Deferred finding does not get its own top-level comment; it goes into the summary under the **Deferred (out of scope)** heading (see step 9).
208
+
209
+ The whole-body `fingerprint` (not per-finding) goes in the fenced fingerprint block at the end of the summary. If the review body later changes (new findings, edits), the fingerprint changes and the next pass will post the summary again. It's slightly noisier but never silently drops a new finding.
210
+
211
+ If `reviewBodyComments` is empty (or all entries dedupe), skip ONLY the review-body section of the summary in step 9. Still post thread replies for every non-Skip-reply thread from step 6a and handle issue comments per step 6b.
212
+
213
+ ### 8. Commit and push (if any edits)
214
+
215
+ If steps 5, 6, or 7 modified any files, decide:
216
+
217
+ - **Which files are yours this pass.** The worktree may contain unrelated in-progress work. Only stage files this pass touched. If in doubt, run `git diff --name-only` and pick from that list deliberately.
218
+ - **A focused commit message.** Prefer something like `cb-babysit: <one-line what-changed>`; the project's commitlint expects conventional-commit form, so a `fix(core): ...` or `docs(core): ...` prefix is usually right.
219
+
220
+ Then run:
221
+
222
+ ```bash
223
+ bash scripts/commitAndPush.sh "<message>" <file1> [<file2> ...]
224
+ ```
225
+
226
+ The script enforces explicit staging (never `git add -A`), never skips hooks, and prints:
227
+
228
+ ```text
229
+ sha=<commit-sha>
230
+ url=https://github.com/<owner>/<repo>/commit/<sha>
231
+ ```
232
+
233
+ Capture the `url=` line for the reply templates in step 9.
234
+
235
+ ### 9. Post replies
236
+
237
+ For every thread assessed in step 6a that was NOT marked **Skip-reply** (i.e., one of Agree / Disagree / Already fixed / Defer):
238
+
239
+ ```bash
240
+ bash scripts/postSentinelReply.sh "$THREAD_ID" "$BODY"
241
+ ```
242
+
243
+ Skip-reply threads are left alone as the existing sentinel already covers them.
244
+
245
+ Body templates (the script appends the `addressed` sentinel if missing):
246
+
247
+ - **Agree**: `Addressed in <commit-url>. <one-line what-changed>.`
248
+ - **Disagree**: `Leaving current behavior. <reasoning>.`
249
+ - **Already fixed**: `Already handled by <commit-url-or-file:line>. <brief pointer>.`
250
+ - **Defer**: `Out of scope for this PR; this looks like follow-up work rather than something introduced or required by this change. <one-line rationale or pointer if useful>.\n\n<sub>🤖 <code>cb-babysit:followup v1 core@3.12.0</code></sub>`
251
+
252
+ For Defer replies, include the follow-up sentinel on its own line as shown. The script will append the `addressed` sentinel after it on its own line, so the final body ends with the follow-up sentinel followed by a blank line followed by the `addressed` sentinel. `grep cb-babysit:followup` finds the deferral and `grep cb-babysit:addressed` still marks the thread handled for dedupe.
253
+
254
+ The script uses the `addPullRequestReviewThreadReply` GraphQL mutation. It does NOT resolve the thread.
255
+
256
+ If any automated review bodies were assessed in step 7 OR any active issue comments were assessed in step 6b, post ONE top-level PR comment summarizing all of them:
257
+
258
+ ```bash
259
+ bash scripts/postSentinelPrComment.sh "$PR_NUMBER" "$BODY"
260
+ ```
261
+
262
+ The PR-level summary should:
263
+
264
+ - Group by source. Use `## Review-body findings` for step-7 work and `## Conversation-tab comments` for step-6b work. Omit a section if its list is empty.
265
+ - Inside each section, group verdicts under **Agree / Disagree / Already fixed / Deferred (out of scope)** subheadings. Omit a subheading if its list is empty.
266
+ - Under **Deferred (out of scope)**, list each deferred item as a bullet, followed on its own line by `<sub>🤖 <code>cb-babysit:followup v1 core@3.12.0</code></sub>` so grep catches them individually.
267
+ - Include the commit URL for fixes.
268
+ - End with a fenced fingerprint block listing every current fingerprint (addressed and deferred) one per line. Include both `reviewBodyComments[].fingerprint` (whole-body, one per automated review) and `activeIssueComments[].fingerprint` (per Conversation-tab comment). Future runs dedupe by matching these against `priorBabysitSentinels`.
269
+
270
+ ### 10. Summarize
271
+
272
+ Report:
273
+
274
+ - Commits made (with URLs).
275
+ - Merge conflict status if relevant (resolved or aborted with reason).
276
+ - CI checks fixed / still failing / skipped-with-diagnosis.
277
+ - Review threads replied to, grouped by verdict (including any Defer count: "X threads deferred as follow-ups").
278
+ - Conversation-tab comments addressed, grouped by verdict (e.g. "Z conversation comments deferred as follow-ups").
279
+ - Review-body findings summarized (or skipped because already covered), including the Deferred count: "Y review-body findings deferred as follow-ups".
280
+ - Threads left active because of bot-acknowledgement uncertainty (flag by thread URL).
281
+ - If `truncated` is non-empty: explicitly call out which connection hit GitHub's 100-item GraphQL cap (e.g. "`truncated: ['thread-comments']`, at least one review thread has more than 100 comments; this pass may have missed the tail. Investigate before relying on it for completeness.").
282
+ - The stop condition triggered for this pass (clean / progressing / stuck).
283
+
284
+ When the report mentions any deferrals, include a one-liner the user can run later to enumerate them, e.g.:
285
+
286
+ ```bash
287
+ gh api graphql -f query='query($o:String!,$r:String!,$n:Int!){repository(owner:$o,name:$r){pullRequest(number:$n){reviewThreads(first:100){nodes{comments(first:50){nodes{body url}}}}comments(first:100){nodes{body url}}}}}' -F o=<owner> -F r=<repo> -F n=<pr> | grep -B1 cb-babysit:followup
288
+ ```
289
+
290
+ Do not rely only on `gh pr view --json comments,reviews`. That view can miss inline review-thread replies, which is where most Defer replies live.
291
+
292
+ ## Loop control
293
+
294
+ After the single pass completes, pick exactly one outcome:
295
+
296
+ - **Exit clean**: all CI checks passed AND every thread in `activeThreads` was either marked Skip-reply during step 6a'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 entry in `activeIssueComments` is covered by this pass's PR-level summary, AND every current review-body fingerprint is covered by an existing sentinel comment (deferred review-body and conversation-comment fingerprints count; they're in the summary's fenced block). Do not use raw `totalActiveThreads` / `totalActiveIssueComments` from the script output. They're pre-inspection and will stay non-zero for Skip-reply or post-summary cases. A PR with Deferred items is still clean from babysit's perspective: the skill has done what it can without widening scope. Report success and stop.
297
+ - **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 `/cb-babysit` once CI settles, or to wrap the call with `/loop <cadence> /cb-babysit` (or a shell `while true; do ...; done`) for automatic re-runs.
298
+ - **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:
299
+ - Merge conflict in step 2 that exceeded the high-confidence resolution bar.
300
+ - CI still running (`gh pr checks --watch` timed out with pending checks).
301
+ - CI failing with a diagnosis-only verdict from Step 5 (flaky / infra / auth / external check / ambiguous / out-of-scope failure).
302
+ - Only Skip-reply threads remained AND CI was already red or pending.
303
+
304
+ 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.
305
+
306
+ This skill never waits or repeats internally.
@@ -4,12 +4,13 @@
4
4
  #
5
5
  # SENTINEL is the literal emitted on new replies: a visible footer (robot mark +
6
6
  # token in `<code>`, wrapped in `<sub>`). SENTINEL_PREFIX is the wrapper-free
7
- # substring used for matching/dedupe, so it matches both this footer and legacy
8
- # `<!-- babysit-pr:addressed v1 ... -->` sentinels. The `core@X.Y.Z` suffix is
9
- # substituted at build time by embedPluginVersion.mts.
7
+ # substring used for matching/dedupe. LEGACY_SENTINEL_PREFIX keeps old
8
+ # `babysit-pr:addressed v1` comments from being reprocessed after the rename.
9
+ # The `core@X.Y.Z` suffix is substituted at build time by embedPluginVersion.mts.
10
10
 
11
- SENTINEL_PREFIX='babysit-pr:addressed v1 '
12
- SENTINEL='<sub>🤖 <code>babysit-pr:addressed v1 core@3.10.1</code></sub>'
11
+ SENTINEL_PREFIX='cb-babysit:addressed v1 '
12
+ LEGACY_SENTINEL_PREFIX='babysit-pr:addressed v1 '
13
+ SENTINEL='<sub>🤖 <code>cb-babysit:addressed v1 core@3.12.0</code></sub>'
13
14
 
14
15
  # Bot author allowlist (JSON array literal). Used by unresolvedPrComments.sh
15
16
  # as a fallback when GraphQL's `author.__typename == "Bot"` misses a GitHub
@@ -18,11 +19,11 @@ SENTINEL='<sub>🤖 <code>babysit-pr:addressed v1 core@3.10.1</code></sub>'
18
19
  BOTS_JSON='["coderabbitai","coderabbitai[bot]","mendral-app","mendral-app[bot]","dependabot","dependabot[bot]","github-actions","github-actions[bot]","github-advanced-security","github-advanced-security[bot]","renovate","renovate[bot]","renovate-bot","pre-commit-ci","pre-commit-ci[bot]","codecov","codecov[bot]","sonarcloud","sonarcloud[bot]"]'
19
20
 
20
21
  # Echo $1 with SENTINEL appended on its own trailing paragraph, unless the
21
- # body already contains any version of the sentinel (matched via SENTINEL_PREFIX).
22
+ # body already contains any version of the sentinel.
22
23
  ensure_sentinel() {
23
24
  local body="$1"
24
25
  case "$body" in
25
- *"$SENTINEL_PREFIX"*) printf '%s' "$body" ;;
26
+ *"$SENTINEL_PREFIX"* | *"$LEGACY_SENTINEL_PREFIX"*) printf '%s' "$body" ;;
26
27
  *) printf '%s\n\n%s' "$body" "$SENTINEL" ;;
27
28
  esac
28
29
  }
@@ -5,9 +5,9 @@
5
5
  # pr-number: optional; defaults to the PR on the current branch.
6
6
  #
7
7
  # Output (plain text on stdout). First line is either:
8
- # # babysit-pr: no failing checks
8
+ # # cb-babysit: no failing checks
9
9
  # or:
10
- # # babysit-pr: failing checks
10
+ # # cb-babysit: failing checks
11
11
  # followed by one delimited block per failing job:
12
12
  # # --- run=<id> job=<id> ---
13
13
  # <log body>
@@ -16,10 +16,9 @@
16
16
  # first line). Exit 1 on infrastructure errors (gh missing, not authed, no PR).
17
17
  # Exit 2 on usage errors.
18
18
  #
19
- # Filter uses `bucket == "fail"` (gh CLI's normalized lowercase field) instead
20
- # of `.conclusion` because the two gh APIs disagree on case
21
- # `gh pr view --json statusCheckRollup` returns UPPER (GraphQL enum),
22
- # `gh run view --json jobs` returns lower (REST). `bucket` sidesteps it.
19
+ # Failing-check detection normalizes `bucket`, `.conclusion`, and `.state`
20
+ # because gh's PR GraphQL and Actions REST payloads expose different fields and
21
+ # casing, and some status contexts have a null `bucket`.
23
22
  #
24
23
  # Requires: gh, jq.
25
24
 
@@ -74,7 +73,7 @@ FAILING_RAW="$(printf '%s' "$ROLLUP" \
74
73
  ')"
75
74
 
76
75
  if [ -z "$FAILING_RAW" ]; then
77
- echo "# babysit-pr: no failing checks"
76
+ echo "# cb-babysit: no failing checks"
78
77
  exit 0
79
78
  fi
80
79
 
@@ -98,7 +97,7 @@ $FAILING_RAW
98
97
  EOF
99
98
  RUN_IDS="$(printf '%s\n' $RUN_IDS | sort -u | tr '\n' ' ')"
100
99
 
101
- echo "# babysit-pr: failing checks"
100
+ echo "# cb-babysit: failing checks"
102
101
 
103
102
  for RUN_ID in $RUN_IDS; do
104
103
  for JOB_ID in $(gh run view "$RUN_ID" --json jobs \
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  # postSentinelPrComment.sh — Post a top-level PR comment (used for nitpick summaries).
3
- # Appends the babysit-pr sentinel if missing.
3
+ # Appends the cb-babysit sentinel if missing.
4
4
  #
5
5
  # Usage: bash postSentinelPrComment.sh <pr-number> <body>
6
6
  #
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  # postSentinelReply.sh — Post a threaded reply to a PR review thread.
3
- # The body MUST end with the babysit-pr sentinel; this script enforces that.
3
+ # The body MUST end with the cb-babysit sentinel; this script enforces that.
4
4
  # Does NOT resolve the thread — that stays with the human.
5
5
  #
6
6
  # Usage: bash postSentinelReply.sh <thread-id> <body>
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # unresolvedPrComments.sh — Fetch review data for babysit-pr.
2
+ # unresolvedPrComments.sh — Fetch review data for cb-babysit.
3
3
  #
4
4
  # Returns one JSON document with:
5
5
  # - threads / activeThreads / uncertainThreads — review threads with
@@ -10,10 +10,10 @@
10
10
  # - issueComments — every top-level PR conversation comment, tagged with
11
11
  # isBabysitSentinel and isKnownBot flags.
12
12
  # - activeIssueComments — non-sentinel, non-bot issue comments whose
13
- # per-comment fingerprint is NOT already listed in any prior babysit-pr
13
+ # per-comment fingerprint is NOT already listed in any prior cb-babysit
14
14
  # summary. These are the human Conversation-tab comments needing a reply.
15
15
  # - priorBabysitSentinels — issue comments whose body contains the
16
- # babysit-pr sentinel prefix. Used for review-body + issue-comment dedupe.
16
+ # cb-babysit sentinel prefix. Used for review-body + issue-comment dedupe.
17
17
  # - truncated — array naming any GraphQL connection that hit GitHub's
18
18
  # 100-item cap (reviewThreads, thread-comments, reviews, issueComments).
19
19
  # Agent must surface this in the final summary.
@@ -258,9 +258,9 @@ main() {
258
258
  # - postSentinelHumanComments: ARRAY of every human comment after the sentinel
259
259
  # - activityState: "active" / "uncertain" / "addressed"
260
260
  local threads_json
261
- threads_json="$(printf '%s' "$response" | jq --arg sentinel_prefix "$SENTINEL_PREFIX" --argjson bots "$BOTS_JSON" '
261
+ threads_json="$(printf '%s' "$response" | jq --arg sentinel_prefix "$SENTINEL_PREFIX" --arg legacy_sentinel_prefix "$LEGACY_SENTINEL_PREFIX" --argjson bots "$BOTS_JSON" '
262
262
  def is_bot: ((.author.__typename // "") == "Bot") or ((.author.login // "") | IN($bots[]));
263
- def is_sentinel: ((.body // "") | contains($sentinel_prefix));
263
+ def is_sentinel: ((.body // "") | contains($sentinel_prefix) or contains($legacy_sentinel_prefix));
264
264
  [
265
265
  .data.repository.pullRequest.reviewThreads.nodes[]
266
266
  | select(.isResolved == false)
@@ -416,8 +416,8 @@ main() {
416
416
 
417
417
  # All issue comments (top-level Conversation-tab comments).
418
418
  local raw_issue_comments
419
- raw_issue_comments="$(printf '%s' "$response" | jq --arg sentinel_prefix "$SENTINEL_PREFIX" --argjson bots "$BOTS_JSON" '
420
- def is_sentinel_body: ((.body // "") | contains($sentinel_prefix));
419
+ raw_issue_comments="$(printf '%s' "$response" | jq --arg sentinel_prefix "$SENTINEL_PREFIX" --arg legacy_sentinel_prefix "$LEGACY_SENTINEL_PREFIX" --argjson bots "$BOTS_JSON" '
420
+ def is_sentinel_body: ((.body // "") | contains($sentinel_prefix) or contains($legacy_sentinel_prefix));
421
421
  def is_bot_author: ((.author.__typename // "") == "Bot") or ((.author.login // "") | IN($bots[]));
422
422
  [
423
423
  .data.repository.pullRequest.comments.nodes[]
@@ -443,12 +443,12 @@ main() {
443
443
 
444
444
  # Concatenate prior sentinel bodies into one blob — used as a haystack for
445
445
  # fingerprint dedupe (both review-body and issue-comment fingerprints land
446
- # in the fenced block at the end of a babysit-pr summary).
446
+ # in the fenced block at the end of a cb-babysit summary).
447
447
  local prior_sentinel_blob
448
448
  prior_sentinel_blob="$(printf '%s' "$prior_sentinels" | jq -r '[.[].body] | join("\n")')"
449
449
 
450
450
  # activeIssueComments: non-sentinel, non-bot comments whose fingerprint is
451
- # NOT already listed in any prior babysit-pr summary.
451
+ # NOT already listed in any prior cb-babysit summary.
452
452
  local active_issue_comments
453
453
  active_issue_comments="$(printf '%s' "$issue_comments" | jq --arg blob "$prior_sentinel_blob" '
454
454
  [.[]
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: cb-ship
3
+ description: Ship changes. Simplify the diff, commit, push, and open or update a PR. Use when the user says 'ship it', 'commit and push', or wants a PR created or updated.
4
+ argument-hint: "[--draft]"
5
+ ---
6
+
7
+ ## Setup
8
+
9
+ - If `gh auth status` fails, stop and tell the user.
10
+ - If `git rev-parse --verify origin/HEAD` fails, `origin/HEAD` is unset. Stop and tell the user to run `git remote set-head origin -a`.
11
+ - If `git status --short`, `git log --oneline origin/HEAD..HEAD`, and `gh pr view --json url --jq .url 2>/dev/null` are all empty, stop and reply "nothing to ship."
12
+
13
+ Resolve bundled "./references" paths relative to SKILL.md.
14
+
15
+ ## Workflow
16
+
17
+ 1. Create a new branch if on the default branch (e.g., `feat/add-user-validation`, `fix/null-check-in-parser`).
18
+ 2. Use ./references/simplify.md on the full PR diff: `git diff $(git merge-base HEAD origin/HEAD)..HEAD` and any uncommitted changes.
19
+ 3. Inspect `git status --short`, identify intended files, ask if ambiguous, then git add them. If uncommitted changes, create a conventional commit with `git commit --no-gpg-sign`.
20
+ 4. Push changes to origin.
21
+ 5. Create or update the PR using ./references/pr-template.md:
22
+ a. If the host exposes a session ID (e.g., CODEX_THREAD_ID, CLAUDE_CODE_SESSION_ID), include the resume command in the PR body in backticks: ``Agent session: `codex resume <id>` `` or ``Agent session: `claude --resume <id>` ``. Preserve existing `Agent session:` lines, append only.
23
+ b. If a PR exists, update the title and body if the new changes aren't reflected.
24
+ c. Otherwise, create one with `gh pr create`; make it a draft if the user passed `--draft`.
25
+ 6. Output:
26
+
27
+ ```plaintext
28
+ - Directory: [Absolute path]
29
+ - Branch: [Branch name]
30
+ - Session: [Resume command from 5a] (omit if none)
31
+ - PR: [Complete url] (e.g., `https://github.com/clipboardhealth/core-utils/pull/123`)
32
+ ```
@@ -0,0 +1,21 @@
1
+ ## Title
2
+
3
+ Conventional commit type and description with no scope, followed by the issue tracker ID in parentheses if you can derive it from the branch name or session context (e.g., `feat: add resume command (STAFF-123)`).
4
+
5
+ ## Body
6
+
7
+ ```md
8
+ ## Summary
9
+
10
+ Concisely explain the user intent from session history and the meaningful behavior/system change.
11
+
12
+ ## Validation
13
+
14
+ - List proof of validation: commands, screenshots, telemetry, Loom, or `Not run: <reason>`.
15
+
16
+ ## Notes
17
+
18
+ Optional, don't fabricate: ticket links, rollout plan, residual risk, or specific areas for reviewers to focus.
19
+
20
+ <sub>🤖 <code>cb-ship:created v1 core@3.12.0</code></sub>
21
+ ```
@@ -0,0 +1,50 @@
1
+ # Simplify
2
+
3
+ If the host supports it, launch three review agents concurrently, passing each the full diff. Otherwise, perform the reviews inline.
4
+
5
+ ## Agents
6
+
7
+ ### 1: Code reuse review
8
+
9
+ Review each change:
10
+
11
+ 1. **Search for existing utilities and helpers** that could replace newly written code. Look for similar patterns elsewhere in the codebase. Common locations are utility directories, shared modules, library monorepos `cbh-core` and `core-utils`, and files adjacent to the changed ones.
12
+ 2. **Flag any new function that duplicates existing functionality.** Suggest the existing function to use instead.
13
+ 3. **Flag any inline logic that could use an existing utility.** Hand-rolled string manipulation, manual path handling, custom environment checks, ad-hoc type guards, and similar patterns are common candidates.
14
+
15
+ ### 2: Code quality review
16
+
17
+ Review each change for hacky patterns:
18
+
19
+ 1. **Redundant state** that duplicates existing state, cached values that could be derived, observers/effects that could be direct calls.
20
+ 2. **Parameter sprawl**: Adding new parameters to a function instead of generalizing or restructuring existing ones.
21
+ 3. **Copy-paste with slight variation**: near-duplicate code blocks that should be unified with a shared abstraction. Exempt intentional, structurally-parallel repetition that aids readability — test arrange/act/assert blocks are the common case.
22
+ 4. **Leaky abstractions**: exposing internal details that should be encapsulated, or breaking existing abstraction boundaries.
23
+ 5. **Premature abstraction (YAGNI)**: an interface, wrapper, or config object introduced for a single caller — inline it until a second caller justifies it.
24
+ 6. **Stringly-typed code**: using raw strings where constants, enums (string unions), or branded types already exist in the codebase.
25
+ 7. **Unnecessary JSX nesting**: wrapper Boxes/elements that add no layout value. Check if inner component props (flexShrink, alignItems, etc.) already provide the needed behavior.
26
+ 8. **Nested conditionals**: ternary chains (`a ? x : b ? y : ...`), nested if/else, or nested switch 3+ levels deep. Flatten with early returns, guard clauses, a lookup table, or an if/else-if cascade.
27
+ 9. **Unnecessary comments**: Delete comments explaining WHAT the code does (well-named identifiers already do that), narrating the change, or referencing the task/caller. Keep only non-obvious WHY (hidden constraints, subtle invariants, workarounds).
28
+ 10. **Defensive code on trusted inputs**: null/empty/type guards on inputs already guaranteed upstream (a validated DTO, the type system, a controller that already checked), optional chaining where the types guarantee presence, fallbacks that mask bugs (`?? ''`, `|| []`, `?? 0` on values that should never be absent), and try/catch that only logs and rethrows or guards an impossible state. Leave guards at genuine trust boundaries (raw request bodies, webhook payloads, third-party responses) and any error handling around real I/O, network, parsing, or payments — removing those changes behavior.
29
+ 11. **Type escapes**: `as any`, `: any`, `as unknown as X`, gratuitous non-null `!`, `@ts-ignore`/`@ts-expect-error` added to dodge a type error — replace with the real type when it is determinable from the call site, the imported type, or the shape in use; if it is not, leave it and flag it rather than deleting it (breaks the build) or swapping in a TODO (more clutter).
30
+ 12. **Dead & leftover code**: unreachable branches and stray debug statements (`console.log`/print-style output) — delete outright rather than commenting out; keep intentional logging that goes through the repo's logger. Unused exports/symbols with no remaining references are already covered by `.rules/common/typeScript.md`'s Dead Code Cleanup rule.
31
+
32
+ ### 3: Efficiency review
33
+
34
+ Review each change for:
35
+
36
+ 1. **Unnecessary work**: redundant computations, repeated file reads, duplicate network/API calls, N+1 patterns.
37
+ 2. **Missed concurrency**: independent operations run sequentially when they could run in parallel.
38
+ 3. **Hot-path bloat**: new blocking work added to startup or per-request/per-render hot paths.
39
+ 4. **Recurring no-op updates**: state/store updates inside polling loops, intervals, or event handlers that fire unconditionally. Add a change-detection guard so downstream consumers aren't notified when nothing changed. If a wrapper function takes an updater/reducer callback, verify it honors same-reference returns (or whatever the "no change" signal is), otherwise callers' early-return no-ops are silently defeated.
40
+ 5. **Unnecessary existence checks**: pre-checking file/resource existence before operating (TOCTOU anti-pattern); operate directly and handle the error.
41
+ 6. **Memory**: unbounded data structures, missing cleanup, event listener leaks.
42
+ 7. **Overly broad operations**: reading entire files when only a portion is needed, loading all items when filtering for one.
43
+
44
+ ## Fix issues
45
+
46
+ Once all three reviews are complete, aggregate their findings and fix each issue directly. If a finding is a false positive or not worth addressing, note it and move on.
47
+
48
+ ## Validate fixes
49
+
50
+ Find validation commands in, e.g., `AGENTS.md`, `CLAUDE.md`, `CONTRIBUTING.md`, or pre-commit/pre-push hooks and fix any failures.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: cb-work
3
+ description: Implement a plan file or direct request end-to-end, validate, and ship via cb-ship. Use when the user says 'implement the plan' or clearly wants a change implemented and shipped.
4
+ argument-hint: "[--draft] [plan path or implementation request]"
5
+ ---
6
+
7
+ ## Setup
8
+
9
+ - **Path-like input** (absolute or relative): Resolve the path. If it doesn't resolve to a readable file, stop and tell the user. Do not reinterpret a missing path as a direct request.
10
+ - **Natural-language request**: Treat as the implementation task.
11
+ - **No argument**: Ask for either a plan path or the implementation request.
12
+
13
+ ## Prepare
14
+
15
+ Unless instructed otherwise:
16
+
17
+ - Create and `cd` into a git worktree based on a freshly fetched `origin/HEAD` using the host's worktree mechanism if it has one; use a descriptive branch name (e.g., `feat/add-user-validation`, `fix/null-check-in-parser`).
18
+ - Install dependencies according to repo docs and detected lock files, e.g., `npm ci`.
19
+
20
+ ## Implement
21
+
22
+ The plan or request is the source of truth for scope:
23
+
24
+ - Make exactly the changes requested, no extra refactors or cleanup.
25
+ - On any drift from the plan (e.g. referenced files or utilities missing, an assumption invalid, a constraint missed) stop and report. Do not silently rewrite the approach.
26
+ - Do not modify the plan file itself unless asked.
27
+
28
+ Run the relevant checks after each meaningful unit of work, not only at the end.
29
+
30
+ ## Validate
31
+
32
+ If the plan names specific checks, use those; skip clearly slow or CI-only suites. Otherwise find validation commands in, e.g., `AGENTS.md`, `CLAUDE.md`, `CONTRIBUTING.md`, or pre-commit/pre-push hooks.
33
+
34
+ Done when every check passes or the user has explicitly accepted a failure.
35
+
36
+ ## Hand off
37
+
38
+ Invoke the `cb-ship` skill, passing `--draft` if it was passed to this skill. Relay `cb-ship`'s reply to the user.