Skip to content

Commit 6720e40

Browse files
Merge branch 'staging' into feature/instagram-integration
2 parents 6128a18 + 0aa2309 commit 6720e40

669 files changed

Lines changed: 73235 additions & 6206 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/babysit/SKILL.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
---
2+
name: babysit
3+
description: Drive a PR to a clean review (Greptile 5/5, zero open threads) — ships if needed, triggers Greptile/Cursor Bugbot, fixes real findings, replies to and resolves every thread, and loops until clean
4+
---
5+
6+
# Babysit PRs
7+
8+
Owns a PR end-to-end through review: ship it, wait for the automatic review round, and if it
9+
isn't already clean, drive fix → reply → resolve → re-review cycles until Greptile reports 5/5
10+
and there are zero open comment threads. Designed to be run under `/loop` (no fixed interval —
11+
let it self-pace on review latency) so it survives across multiple wakeups in the same session.
12+
13+
## When to use
14+
15+
- The user says "babysit this PR", "keep working the reviews until it's clean", or similar
16+
- As the natural follow-up to `/ship` when the user wants the review loop automated rather than
17+
manually re-triggering reviews and answering comments themselves
18+
19+
## Inputs
20+
21+
Needs a PR number. If none is given and there's no open PR for the current branch, run `/ship`
22+
first (which includes the `origin/staging` sync check — see `.agents/skills/ship/SKILL.md`) to
23+
create one.
24+
25+
## Definition of "clean"
26+
27+
Both must hold:
28+
1. The latest Greptile summary comment reports **Confidence Score: 5/5**
29+
2. `reviewThreads` (GraphQL, see below) has **zero threads with `isResolved: false`**
30+
31+
Do not stop early on "no new comments this round" alone — a thread can be open from an earlier
32+
round. Always check both conditions freshly after every push.
33+
34+
## Loop
35+
36+
1. **Check current state** before doing anything:
37+
```bash
38+
gh pr view <n> --json comments -q '[.comments[] | select(.author.login=="greptile-apps")] | last | .body'
39+
gh api graphql -f query='
40+
query { repository(owner: "<owner>", name: "<repo>") { pullRequest(number: <n>) {
41+
reviewThreads(first: 50) { pageInfo { hasNextPage endCursor } nodes { id isResolved path line
42+
comments(first: 5) { nodes { id databaseId author { login } body } } } } } } }'
43+
```
44+
`[.comments[]] | last | .body`, not `... | .body | tail -1` — the latter pipes every matching
45+
comment's full multi-line body through the pipeline and keeps only the final *line* of that
46+
combined output (usually the "Reviews (n): Last reviewed commit..." footer), not the last
47+
*comment*, so it silently misses the actual "Confidence Score: X/5" line.
48+
`reviewThreads(first: 50)` is a single page — check `pageInfo.hasNextPage`. If `true`, don't
49+
stop yet: re-run the same query with `after: "<endCursor>"` and keep paging until
50+
`hasNextPage` is `false` before evaluating "clean." A PR with more than 50 threads is rare but
51+
stopping on a partial page would silently miss unresolved ones past the cutoff.
52+
If Greptile is 5/5 and every thread across all pages has `isResolved: true`, stop — report the
53+
outcome (see "Reporting" below) and skip the rest of this list.
54+
55+
2. **If no review has run yet** (fresh PR, no Greptile/Cursor comments): they usually run
56+
automatically on PR open — confirm via `gh pr checks <n>` (look for `Cursor Bugbot` /
57+
`Greptile Review`) and wait for that first round before doing anything else.
58+
59+
3. **If a review round has landed and it isn't clean**: for every thread where
60+
`isResolved: false`, triage the finding on its own merits — this is the part that requires
61+
judgment, not a mechanical loop:
62+
- **Real bug**: fix it in the cleanest way available. Match the codebase's existing
63+
conventions for that kind of problem before inventing a new one (e.g. an SSRF-prone
64+
user-supplied-host fetch should use whatever `validateUrlWithDNS`/`secureFetchWithPinnedIP`
65+
pattern the rest of the codebase already uses for that exact situation — grep for a sibling
66+
integration solving the same problem first). Never patch around a finding with a
67+
workaround, a broad try/catch, or a suppression comment — fix the actual cause.
68+
- **False positive**: don't change code. Reply with the specific reason it doesn't apply
69+
(cite the type definition, the established pattern it matches, or the doc it follows) so
70+
the reviewer bot and a human skimming later both understand why it was left as-is.
71+
- **Already fixed by an earlier finding in the same round**: note that and resolve without a
72+
duplicate code change.
73+
74+
4. **Reply to every thread individually** before resolving it — never resolve silently:
75+
```bash
76+
gh api repos/<owner>/<repo>/pulls/<n>/comments/<databaseId>/replies -f body="<what was done and why>"
77+
```
78+
Then resolve via GraphQL (needs the thread `id` from step 1, not the comment id):
79+
```bash
80+
gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "<threadId>"}) { thread { isResolved } } }'
81+
```
82+
83+
5. **Before pushing, re-run the full sync check from `/ship` step 2** — not just the log command,
84+
the whole check-and-recover flow (stash WIP if needed, rebase, verify the rebase didn't just
85+
cleanly replay stray commits, cherry-pick rebuild if it did or if it conflicted). A babysit
86+
loop spanning a long session is exactly the scenario where a branch can drift, and pushing
87+
review fixes on top of undetected drift is how an oversized PR happens even after the branch
88+
was fixed once. Then run the repo's pre-ship checks the same way `/ship` does before
89+
committing — not just lint/typecheck/boundary-validation, but also the conditional `/cleanup`
90+
(if this round's fix touched UI code) and `/db-migrate` (if it touched schema/migrations)
91+
gates from `/ship` steps 4 and 5. A review-fix round is still a code change and can trip
92+
either gate just as easily as the original commit did.
93+
94+
6. **Commit and push** the round's fixes as one commit — `--force-with-lease` whenever step 5's
95+
sync check rewrote history, which includes a plain `git rebase origin/staging` that completed
96+
with no conflicts, not only the cherry-pick rebuild path; both rewrite commits already
97+
published to the remote, so a plain `git push` can be rejected either way — then run `/ship`
98+
step 9's post-push verify — not just before the first push, every push in the loop:
99+
```bash
100+
git fetch origin staging && git log --oneline --reverse origin/staging..HEAD
101+
gh pr view <n> --json commits -q '.commits[].messageHeadline'
102+
```
103+
`--reverse` matches `git log`'s newest-first default to the PR commit list's oldest-first
104+
order — without it a positional comparison can spuriously fail on any multi-commit branch.
105+
These two lists must describe the same commits. A review loop runs many pushes across many
106+
rounds; checking sync only before the push (step 5) and never after is how a bad push or a
107+
PR whose commit history quietly went stale between rounds goes unnoticed.
108+
109+
7. **Re-trigger review** by posting `@greptile` and `@cursor review` as **two separate PR
110+
comments** — never combine them into one comment, each bot only responds to its own mention:
111+
```bash
112+
gh pr comment <n> --body "@greptile"
113+
gh pr comment <n> --body "@cursor review"
114+
```
115+
116+
8. **Wait for the new round**, then go back to step 1. Pace the wait with `ScheduleWakeup` using
117+
a fallback delay of ~250–300s (Greptile/Cursor typically take 1–3 minutes) — never busy-poll
118+
in a sleep loop. Pass the same `/loop babysit PR <n>` prompt on each wakeup so the loop
119+
resumes correctly.
120+
121+
9. **Stop conditions**: clean state reached (see above), or the same unresolved finding survives
122+
two consecutive rounds with no new information (surface it to the user instead of looping
123+
forever), or the user interrupts.
124+
125+
## Reporting
126+
127+
When the loop ends, summarize: how many rounds it took, what was actually fixed (one line each),
128+
what was pushed back on as a false positive and why, and the final Greptile score / thread count.
129+
130+
## Hard rules
131+
132+
- Never post the two re-review mentions as a single combined comment.
133+
- Never resolve a thread without replying to it first.
134+
- Never fix a finding with a hacky workaround — if the clean fix isn't obvious, find the sibling
135+
pattern elsewhere in the codebase solving the same class of problem and match it.
136+
- Never silently drop a finding — every thread gets either a code fix or a reasoned reply.
137+
- Always re-run the `/ship`-style sync check before every push in the loop, not just the first.
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
---
2+
name: make-interfaces-feel-better
3+
description: Design engineering principles for making interfaces feel polished. Use when building UI components, reviewing frontend code, implementing animations, hover states, shadows, borders, typography, micro-interactions, enter/exit animations, or any visual detail work. Triggers on UI polish, design details, "make it feel better", "feels off", stagger animations, border radius, optical alignment, font smoothing, tabular numbers, image outlines, box shadows.
4+
---
5+
6+
# Details that make interfaces feel better
7+
8+
Great interfaces rarely come from a single thing. It's usually a collection of small details that compound into a great experience. Apply these principles when building or reviewing UI code.
9+
10+
## Quick Reference
11+
12+
| Category | When to Use |
13+
| --- | --- |
14+
| [Typography](typography.md) | Text wrapping, font smoothing, tabular numbers |
15+
| [Surfaces](surfaces.md) | Border radius, optical alignment, shadows, image outlines, hit areas |
16+
| [Animations](animations.md) | Interruptible animations, enter/exit transitions, icon animations, scale on press |
17+
| [Performance](performance.md) | Transition specificity, `will-change` usage |
18+
19+
## Core Principles
20+
21+
### 1. Concentric Border Radius
22+
23+
Outer radius = inner radius + padding. Mismatched radii on nested elements is the most common thing that makes interfaces feel off.
24+
25+
### 2. Optical Over Geometric Alignment
26+
27+
When geometric centering looks off, align optically. Buttons with icons, play triangles, and asymmetric icons all need manual adjustment.
28+
29+
### 3. Shadows Over Borders
30+
31+
Layer multiple transparent `box-shadow` values for natural depth. Shadows adapt to any background; solid borders don't.
32+
33+
### 4. Interruptible Animations
34+
35+
Use CSS transitions for interactive state changes — they can be interrupted mid-animation. Reserve keyframes for staged sequences that run once.
36+
37+
### 5. Split and Stagger Enter Animations
38+
39+
Don't animate a single container. Break content into semantic chunks and stagger each with ~100ms delay.
40+
41+
### 6. Subtle Exit Animations
42+
43+
Use a small fixed `translateY` instead of full height. Exits should be softer than enters.
44+
45+
### 7. Contextual Icon Animations
46+
47+
Animate icons with `opacity`, `scale`, and `blur` instead of toggling visibility. Use exactly these values: scale from `0.25` to `1`, opacity from `0` to `1`, blur from `4px` to `0px`. If the project has `motion` or `framer-motion` in `package.json`, use `transition: { type: "spring", duration: 0.3, bounce: 0 }` — bounce must always be `0`. If no motion library is installed, keep both icons in the DOM (one absolute-positioned) and cross-fade with CSS transitions using `cubic-bezier(0.2, 0, 0, 1)` — this gives both enter and exit animations without any dependency.
48+
49+
### 8. Font Smoothing
50+
51+
Apply `-webkit-font-smoothing: antialiased` to the root layout on macOS for crisper text.
52+
53+
### 9. Tabular Numbers
54+
55+
Use `font-variant-numeric: tabular-nums` for any dynamically updating numbers to prevent layout shift.
56+
57+
### 10. Text Wrapping
58+
59+
Use `text-wrap: balance` on headings. Use `text-wrap: pretty` for body text to avoid orphans.
60+
61+
### 11. Image Outlines
62+
63+
Add a subtle `1px` outline with low opacity to images for consistent depth. The color must be pure black in light mode (`rgba(0, 0, 0, 0.1)`) and pure white in dark mode (`rgba(255, 255, 255, 0.1)`) — never a near-black like slate, zinc, or any tinted neutral. A tinted outline picks up the surface color underneath it and reads as dirt on the image edge.
64+
65+
### 12. Scale on Press
66+
67+
A subtle `scale(0.96)` on click gives buttons tactile feedback. Always use `0.96`. Never use a value smaller than `0.95` — anything below feels exaggerated. Add a `static` prop to disable it when motion would be distracting.
68+
69+
### 13. Skip Animation on Page Load
70+
71+
Use `initial={false}` on `AnimatePresence` to prevent enter animations on first render. Verify it doesn't break intentional entrance animations.
72+
73+
### 14. Never Use `transition: all`
74+
75+
Always specify exact properties: `transition-property: scale, opacity`. Tailwind's `transition-transform` covers `transform, translate, scale, rotate`.
76+
77+
### 15. Use `will-change` Sparingly
78+
79+
Only for `transform`, `opacity`, `filter` — properties the GPU can composite. Never use `will-change: all`. Only add when you notice first-frame stutter.
80+
81+
### 16. Minimum Hit Area
82+
83+
Interactive elements need at least 40×40px hit area. Extend with a pseudo-element if the visible element is smaller. Never let hit areas of two elements overlap.
84+
85+
## Common Mistakes
86+
87+
| Mistake | Fix |
88+
| --- | --- |
89+
| Same border radius on parent and child | Calculate `outerRadius = innerRadius + padding` |
90+
| Icons look off-center | Adjust optically with padding or fix SVG directly |
91+
| Hard borders between sections | Use layered `box-shadow` with transparency |
92+
| Jarring enter/exit animations | Split, stagger, and keep exits subtle |
93+
| Numbers cause layout shift | Apply `tabular-nums` |
94+
| Heavy text on macOS | Apply `antialiased` to root |
95+
| Animation plays on page load | Add `initial={false}` to `AnimatePresence` |
96+
| `transition: all` on elements | Specify exact properties |
97+
| First-frame animation stutter | Add `will-change: transform` (sparingly) |
98+
| Tiny hit areas on small controls | Extend with pseudo-element to 40×40px |
99+
100+
## Review Output Format
101+
102+
Always present changes as a markdown table with **Before** and **After** columns. Include every change you made — not just a subset. Never list findings as separate "Before:" / "After:" lines outside of a table. Group changes by principle using a heading above each table, and keep each row focused on a single diff so the reader can scan the whole list quickly.
103+
104+
### Example
105+
106+
#### Concentric border radius
107+
| Before | After |
108+
| --- | --- |
109+
| `rounded-xl` on card + `rounded-xl` on inner button (`p-2`) | `rounded-2xl` on card (`12 + 8`), `rounded-lg` on inner button |
110+
| `border-radius: 16px` on both nested surfaces | Outer `24px`, inner `16px` with `8px` padding |
111+
112+
#### Tabular numbers
113+
| Before | After |
114+
| --- | --- |
115+
| `<span>{count}</span>` on animated counter | `<span className="tabular-nums">{count}</span>` |
116+
| Default numerals on timer | Added `font-variant-numeric: tabular-nums` to root |
117+
118+
#### Scale on press
119+
| Before | After |
120+
| --- | --- |
121+
| `<button className="...">` | Added `active:scale-[0.96] transition-transform` |
122+
| `scale(0.9)` on press | Raised to `scale(0.96)` — anything below `0.95` feels exaggerated |
123+
124+
Rows should cite the specific file and the specific property that changed when it isn't obvious from the snippet. If a principle was reviewed but nothing needed to change, omit that table entirely — empty tables add noise.
125+
126+
## Review Checklist
127+
128+
- [ ] Nested rounded elements use concentric border radius
129+
- [ ] Icons are optically centered, not just geometrically
130+
- [ ] Shadows used instead of borders where appropriate
131+
- [ ] Enter animations are split and staggered
132+
- [ ] Exit animations are subtle
133+
- [ ] Dynamic numbers use tabular-nums
134+
- [ ] Font smoothing is applied
135+
- [ ] Headings use text-wrap: balance
136+
- [ ] Images have subtle outlines
137+
- [ ] Buttons use scale on press where appropriate
138+
- [ ] AnimatePresence uses `initial={false}` for default-state elements
139+
- [ ] No `transition: all` — only specific properties
140+
- [ ] `will-change` only on transform/opacity/filter, never `all`
141+
- [ ] Interactive elements have at least 40×40px hit area
142+
143+
## Reference Files
144+
145+
- [typography.md](typography.md) — Text wrapping, font smoothing, tabular numbers
146+
- [surfaces.md](surfaces.md) — Border radius, optical alignment, shadows, image outlines
147+
- [animations.md](animations.md) — Interruptible animations, enter/exit transitions, icon animations, scale on press
148+
- [performance.md](performance.md) — Transition specificity, `will-change` usage

0 commit comments

Comments
 (0)