| name | refine-live |
| description | In-chat fallback for the Timeline Inspector Refine agent. Use when the user runs `/refine live`, asks to "refine live", "go live", or answer refine jobs — but ONLY when no persistent agent is wired (no `npx transitions-refine live`). Prefer `npx transitions-refine live` for run-and-forget (relay spawns agent per click, no idle credit burn). This skill long-polls the relay, posts suggestions, handles scan/apply jobs. |
Refine Live
Two modes
Persistent (recommended — run and forget)
Run npx transitions-refine live from your project. The CLI starts the relay and wires REFINE_AGENT_CMD so the relay spawns your agent CLI per Refine click. No chat loop; idle = zero credit burn. Works hours later as long as the relay process keeps running. Stop with Ctrl-C (or npx transitions-refine stop).
In-chat loop (fallback — this skill)
Run /refine live in Cursor/Claude/Codex when the relay is up but has no REFINE_AGENT_CMD. You become the poller via GET /jobs/next. The Agent tab stays available only while you keep polling — each idle poll cycle consumes chat turns/credits. Say "stop refine" to exit.
Use the in-chat loop only when you cannot wire a persistent agent CLI.
Turn yourself into the LLM behind the Timeline Inspector's Refine button (in-chat fallback mode). While
this loop runs, the panel's LLM tab is "available": each click sends one
transition here, you reason about it, and your suggestions appear in the panel.
You are the poller. Nothing is installed — you just talk to a small local relay
(default http://localhost:7331) that the npx injector already started.
How it works
Browser (Refine, LLM tab) ──POST /jobs──► relay ──GET /jobs/next──► YOU
◄──GET /jobs/:id── relay ◄──POST /jobs/:id/result── YOU
The loop — stay live, but don't burn credits forever
Keep polling so the panel's LLM tab stays "available", but this loop costs chat
turns/credits even while idle, so it is not truly run-and-forget — it has
three exits, in priority order:
- Relay stop signal (authoritative).
GET /jobs/next may return 200 with
{"stop": true}. The relay sends this when the user clicks Stop in the
panel, or automatically after ~10 min with no jobs. Always honor it: stop
looping immediately, tell the user the LLM tab will go unavailable and how to
resume (/refine live), and end your turn. Never re-poll after a stop signal.
- The user says so — "stop refine", "exit live", etc.
- Your own idle backoff (safety net). A long stretch of
204s is normal —
it just means no one has clicked Refine yet — but to avoid spending credits on
a forgotten loop, back off as idle grows instead of hammering immediately:
re-poll right away for the first few empty cycles, then pause ~5s between polls,
and after ~10 min of unbroken idle stop on your own (same as the relay's
auto-stop) and tell the user how to resume. Any real job resets the backoff.
The relay reports the agent as "available" for ~120s after your last poll, so
short pauses keep you live. A successful job always resets idle, so an active
session never backs off.
-
Announce yourself once, before the first poll. The relay keeps a sticky
Stop latch: after a panel Stop (or the idle auto-stop) it answers every
GET /jobs/next with {"stop": true} until a new agent explicitly resumes —
so a stopped session can't silently come back. Clear the latch a single time
at startup, then begin polling:
curl -s -X POST http://localhost:7331/poller/start
Do not call this again mid-loop (it would defeat a user's Stop). Only on a
fresh /refine live.
-
Claim the next job (long-poll). This call blocks up to ~25s, then returns.
curl -s http://localhost:7331/jobs/next
-
HTTP 204 / empty body → no work yet. Poll again, applying the idle
backoff above (immediate at first, then ~5s pauses, then stop after ~10 min).
-
HTTP 200 with {"stop": true} → the loop must end. Stop polling, tell
the user the LLM tab is now unavailable and that /refine live resumes it,
and end your turn. Do not treat it as a job.
-
HTTP 200 with a job JSON → work to do. Shape:
{
"id": "uuid",
"request": {
"label": "Resize + Color",
"selector": ".box-resize",
"mode": "llm",
"refineType": "small",
"timings": [
{ "property": "width", "durationMs": 400, "delayMs": 0, "easing": "ease-out" },
{ "property": "background", "durationMs": 400, "delayMs": 0, "easing": "ease-out" }
]
}
}
-
If request.kind === "scan" this is not a suggestion job — the panel is
asking you to group the page's transitions by reading the source. Jump to
## Scan jobs and return groups instead of
suggestions.
-
If request.kind === "apply" this is not a suggestion job — the user
pressed Accept to write changes to their code. Jump to
## Apply jobs and edit the source instead of
posting suggestions. Everything below (refineType, steps 3–4) is for the
normal Refine flow.
-
refineType chooses what kinds of suggestions to make (it mirrors the
panel's two tabs). The tabs scan independently, so answer only the one
you were asked for:
"small" (or missing) → Small refinements: nudge the existing
declarations toward the motion tokens only (step 3a). Do not
propose a recipe swap here — that's the Replace tab's separate job.
"replace" → Replace transition: suggest a whole-transition recipe
swap only (step 3b). Do not propose motion-token tweaks — skip
step 3a entirely.
-
(Optional) post progress so the panel shows what you're doing:
curl -s -X POST http://localhost:7331/jobs/<id>/status \
-H 'Content-Type: application/json' \
-d '{"message":"Matching to transitions.dev motion tokens…"}'
-
Answer in ONE shot — speed matters. Each click should feel instant, so
resolve the job from the data below plus what's already in this skill. Do not
spawn subagents or run a broad codebase search, and do not open the
transitions-dev SKILL.md — its tokens and decision rules are inlined here.
refineType === "small" → step 3a only, with zero file reads.
refineType === "replace" → step 3b only; open at most the one recipe
reference file you choose.
First, infer each declaration's usage from label + selector (modal
close, dropdown open, tooltip, badge, resize, color/theme change…). Match on
intent, not the nearest number.
3a. Motion-token tweaks (refineType === "small" only — no file reads).
Pick the token that fits the usage and propose a change only where the current
value actually differs.
- Durations: 40ms Stagger (per-item offset) · 80ms Micro (tooltip delay,
shake segment) · 150ms Quick (modal/dropdown close, text swap, tooltip
appear) · 250ms Fast (icon swap, dropdown/modal open, tabs slide, page
slide) · 350ms Medium (panel/toast close) · 400ms Slow (panel open, skeleton
reveal, input clear) · 500ms Very slow (emphasis, badge appear, text reveal,
success check).
- Default easing — "Smooth ease out":
cubic-bezier(0.22, 1, 0.36, 1)
(modal/dropdown/panel open+close, page slide, resize, position change).
- Other on-grid easings — LEAVE UNCHANGED:
ease-out (tooltip),
ease-in-out (icon/text swap, text reveal, skeleton reveal), linear
(shimmer, pulse, spinner), cubic-bezier(0.34, 1.36, 0.64, 1) (badge pop),
cubic-bezier(0.34, 3.85, 0.64, 1) (avatar return).
- Nudge toward Smooth ease out: generic
ease, ease-in, or any
hand-rolled cubic-bezier()/linear() that isn't a token above.
- Scale / blur / distance (the non-resting
transform pre-scale or
translate distance, and the filter pre-blur a lane carries): scale 0.96
Large (modal) · 0.97 Medium (dropdown open) · 0.98 Small (tooltip) · 0.99
Tiny (dropdown close); blur 2px (panel/icon/text swap, skeleton, number) ·
3px (page slide, text reveal) · 8px (success check); distance 4px (text
swap) · 6px (shake small) · 8px (badge, page slide, shake large) · 12px
(text reveal) · 30px (check badge). Propose kind:"scale"/"blur"/
"distance" (patch key scale/blur/translate, pass translateVarName
through) only where a lane's value differs from its usage token.
- Polish add-on (optional): if the project has the
transitions-polish
skill installed (.agents/skills/transitions-polish/_refine-rules.md,
.claude/…, or skills/…), read that ONE file a single time and also apply
its rules — open/close asymmetry (closes faster/quieter than opens), hover
in fast / out soft, stagger totals under ~300ms, 80ms intent delays, and
never delay a close. This is the only file read allowed on a Small job, and
only when that skill is present.
3b. Whole-transition recipe swap (refineType === "replace" only — no file
reads). Match the inferred usage to ONE recipe below (this list is the
decision rules — no SKILL.md or reference-file read needed). Emit ONE
kind: "replace" suggestion whose patch carries the motion-token
duration/easing for the recipe's phase (open vs close) on the property that
already transitions (or "all"), with a reference field naming the file and
the recipe in title + reason. The patch only drives the live preview —
exact keyframes/structure come from the user pasting that reference file, so you
never need to open it. If no recipe genuinely fits the usage, return an
empty suggestions array with a short summary.
- Card resize — a container changes width/height on a layout change (
01-card-resize.md)
- Number pop-in — a number/digit updates (
02-number-pop-in.md)
- Notification badge — a small dot/badge appears on a trigger (
03-notification-badge.md)
- Text states swap — text content changes in place (
04-text-states-swap.md)
- Menu dropdown — an anchored surface grows from its trigger (
05-menu-dropdown.md)
- Modal open/close — a centered dialog scales up, softer scale-down on close (
06-modal.md)
- Panel reveal — a surface slides into a region with a cross-blur (
07-panel-reveal.md)
- Page side-by-side — slide between list↔detail or step 1↔step 2 (
08-page-side-by-side.md)
- Icon swap — two icons cross-fade in the same slot (
09-icon-swap.md)
- Success check — a checkmark celebration: fade + rotate + bob + stroke-draw (
10-success-check.md)
- Avatar group hover — hover lifts an item in a horizontal stack (
11-avatar-group-hover.md)
- Error state shake — invalid-input shake (
12-error-state-shake.md)
- Input clear with dissolve — clearing a text field (
13-input-clear-dissolve.md)
- Skeleton loader and reveal — placeholder pulses then swaps to real content (
14-skeleton-reveal.md)
- Shimmer text — in-progress / "thinking" text shimmer (
15-shimmer-text.md)
- Tabs sliding — a moving highlight across segmented options (
16-tabs-sliding.md)
- Tooltip open/close — delayed fade+scale in, instant out (
17-tooltip.md)
- Texts reveal — staggered blurred rise of stacked text lines (
18-texts-reveal.md)
- Card hover tilt — 3D tilt toward the pointer (
19-card-tilt.md)
- Plus to menu morph — a circular trigger becomes the surface it opens (
20-plus-menu-morph.md)
- Accordion expand — a collapsible body grows/shrinks in height (
21-accordion.md)
Tie-break: prefer the lower-overhead recipe (card resize over panel reveal,
dropdown over modal). Only propose a swap when the current declarations are
clearly a hand-rolled version of a recipe or are missing the structure the usage
calls for; if the transition already is the right recipe, return empty.
-
Post the result (this completes the job and renders cards in the panel):
curl -s -X POST http://localhost:7331/jobs/<id>/result \
-H 'Content-Type: application/json' \
-d '{
"summary": "Tightened the resize and softened the color fade.",
"suggestions": [
{
"id": "width-duration",
"kind": "duration",
"property": "width",
"title": "Duration → Snappy (250ms)",
"from": "400ms",
"to": "250ms",
"patch": { "property": "width", "durationMs": 250 },
"reason": "A size change reads as direct manipulation — snappy is more responsive than 400ms."
}
]
}'
The example above is a small job (token tweaks only). A replace job instead
returns a single kind: "replace" card as its only suggestion:
{
"id": "replace-card-resize",
"kind": "replace",
"property": "width",
"title": "Replace with Card resize",
"from": "hand-rolled width tween",
"to": "transitions.dev · Card resize",
"patch": { "property": "width", "durationMs": 250, "easing": "cubic-bezier(0.22, 1, 0.36, 1)" },
"reference": "transitions-dev/01-card-resize.md",
"reason": "This is a width tween on layout change — the Card resize recipe handles it properly. Apply nudges the live timing; paste 01-card-resize.md (run `transitions apply card-resize`) for the full recipe."
}
If nothing should change, post "suggestions": [] with a short summary.
If something goes wrong, report it instead:
curl -s -X POST http://localhost:7331/jobs/<id>/error \
-H 'Content-Type: application/json' -d '{"message":"…"}'
-
Go back to step 1. Keep looping, but honor the three exits from
the loop section: a
{"stop": true} from the relay, the user telling you to stop, or your own idle
backoff/auto-stop after ~10 min quiet. A real job resets idle. Whenever you do
stop, tell them the LLM tab will go unavailable and how to restart
(/refine live).
Scan jobs (group from source)
When a claimed job has request.kind === "scan", the panel wants you to turn a
flat list of DOM-detected transitions into components with phases. A naive
DOM scan only sees each element's current computed transition — it can't tell
open from close, and lists related elements (panel, backdrop, staggered items)
separately. You fix that by reading the source. The request looks like:
{
"id": "uuid",
"request": {
"kind": "scan",
"url": "http://localhost:5173/",
"raw": [
{ "label": "div.dropdown-panel", "selector": ".dropdown-panel",
"properties": ["opacity","transform"],