| name | ui-recorder |
| description | Record a web UI and produce a branded, narrated 16:9 MP4 demo. Use when the user asks to "record a demo of {url}", "make a video of {site}", "screen demo", "ui recorder", "marketing clip", or similar. v0.1 supports local-headless recorder + Piper offline TTS + 16:9 output. |
| allowed-tools | Read Write Edit Bash Glob Grep Agent |
ui-recorder Skill
Orchestrates the 5-stage pipeline: plan → record → narrate → edit → render.
Workflow
Stage 0 — Plan-Build (verifies selectors against real DOM)
- Slugify the user's description → workspace slug.
- Run:
node "${CLAUDE_PLUGIN_ROOT}/scripts/plan-build.mjs" <slug> -- <user-prompt>
- Read stdout:
BYPASS → user already placed browse-plan.json (e.g. from test-fixtures/). A synthetic plan-report.json was written with source: "user-provided" for all actions. Skip to Stage 1.
NEEDS_AGENT → workspace is empty. Dispatch the plan-builder agent (Task tool, subagent_type plan-builder). Pass it the workspace path, user prompt, viewport, and playbackRate. The agent writes browse-plan.json, plan-report.json, and narration.txt into the workspace.
- On agent failure (errors in stderr): surface the
code + hint. If it reports PLAN_CHROMIUM_MISSING and no browser MCP was available, offer the user: (a) run setup.sh to install chromium, (b) proceed with the agent's partial plan, marking unverified actions as blind manually, (c) edit plan manually.
Stage 1 — Review (Gate 1)
- Read
workspace/{slug}/browse-plan.json and plan-report.json. Present to the user:
- Summary: N actions across M URLs, estimated output ~Xs at the configured
playbackRate.
- Per-action line:
#N <action> <label> — selector: <sel> [confidence: <level>, source: <tier>] + short rationale.
- If any action has
source: "blind", flag with a warning icon and show the warning field.
- Gate 1: "Proceed? (yes / edit selector N / drop action N / regenerate)". Wait for explicit approval.
- On "edit" — update
browse-plan.json and the matching action in plan-report.json (change source to "user-provided"), then re-prompt.
- On "regenerate" — ask the user for a refined prompt, then rerun Stage 0 from step 2.
Stage 2 — Record
- Invoke
node "${CLAUDE_PLUGIN_ROOT}/scripts/record.mjs" <plan.json> <slug>.
- On failure, surface the
code + hint from UIRecorderError. Suggest the fix verbatim.
- On success, report:
recording.mp4 path, moments.json summary (N actions captured, total duration).
Stage 3 — Narrate
- Check
workspace/{slug}/narration.txt — Stage 0's plan-builder agent writes a draft when it runs. Present the draft (if present) alongside the prompt: "Narration? (a) use agent draft, (b) write me a script, (c) auto from action labels, (d) silent". If no draft exists, offer (b)/(c)/(d) only.
- For (a): keep the existing
narration.txt. For (b): prompt for the script. For (c): join all label fields into a neutral sentence per action. For (d): delete narration.txt and skip to Stage 4.
- Write
workspace/{slug}/narration.txt (unless (a) kept it as-is or (d) deleted it).
- Invoke
node "${CLAUDE_PLUGIN_ROOT}/scripts/narrate.mjs" <slug>.
- If
TTS_OVERFLOW: auto-rewrite was already tried. Ask user to shorten manually.
Stage 4 — Edit (Gate 2)
- Dispatch the
edit-planner agent with the workspace path. It reads moments/alignment and writes camera-config.json.
- Gate 2: Show segments list, total output duration (with 4× speedup), camera movement summary. Ask "render now? (yes / adjust)".
- If user wants adjustments, let them specify zoom levels, segment bounds, or crop strategy → update
camera-config.json manually → proceed.
Stage 5 — Render
- Invoke
node "${CLAUDE_PLUGIN_ROOT}/scripts/render.mjs" <slug>.
- On success, report:
demo.mp4 path, file size, duration.
- Offer adjustments: re-render with different zoom, aspect ratio (v0.1: only 16:9), or brand.
Natural-language config updates
Recognize phrases like:
- "use voice X" → update
config.json tts_voice
- "faster pace" / "slower pace" → update
browse-plan.json playbackRate (default 2)
- "silent" → skip Stage 3
Write config via loadConfig/updateConfig from scripts/lib/config.mjs.
Caching
Never invoke an earlier stage if its cache sidecar is valid. The scripts handle this internally via .sha256 sidecars. If the user says "re-record", delete ${workspace}/recording.mp4.sha256.
Error recovery
When a script exits non-zero, read ${CLAUDE_PLUGIN_DATA}/logs/last-error.log via Read, extract the UIRecorderError code + hint, and present to the user. Do NOT auto-retry without asking (policy set in design §6.3).
What v0.1 does NOT support (redirect politely)
- Recorder:
local-headed, steel — redirect to local-headless
- TTS:
elevenlabs — redirect to piper
- Aspect ratios other than 16:9
- Brand extraction from screenshots
- Authenticated flows, iframes