- name
- explainer
- description
- Use when the user asks for explainer or a task matching the examples below. ~60-80s explainer video for any URL — GitHub repo, product page, docs site, blog post, or launch. Canonical workflow for URL walkthroughs. Use when the user asks to "explain this URL / repo / website / product", "make a walkthrough video for [url]", "demo this site", "Loom-style explainer of [url]", "explainer for github.com/...", or "explain this product link". Drives a real browser through the URL, generates an avatar lipsync, and composites in a 1280×800 macOS Sonoma frame with a 246-pixel bottom-left avatar circle. GitHub URLs activate a repo-aware mode (README scan + live-demo detection); other URLs use a generic page-walkthrough flow.
- argument-hint
- <url> [--focus angles] [--avatar url] [--voice id] [--live-url url] [--lipsync-provider pika|kling] [--no-captions] [--preview]
# /pika:explainer
Generate a ~60–80s URL explainer video: drive a real browser through the URL along a beat-sheet timeline, generate an avatar lipsync of the narration, and composite it all in a 1280×800 macOS Sonoma frame with a 240-pixel inner avatar (246-pixel outer including 3px white stroke ring) at canvas (20, 476) and element-targeted zoom on every mid-section beat. Works on any URL — product pages, docs sites, blog posts, launches. GitHub URLs activate a repo-aware mode (README scan + live-demo detection); all other URLs use a generic page-walkthrough flow.
**Usage:** `/pika:explainer <url> [--focus "angles"] [--avatar <url>] [--voice <id>] [--lipsync-provider pika|kling] [--preview] [--live-url <url>]`
## Cost transparency gate
Before any paid MCP call, call `identity_balance({verbose: true})` once. Surface the current balance, recent burn rate, and remaining runway, then gate the run with this exact message:
> Estimated cost: about 50-500 credits (~$0.50-$5.00) depending on lipsync provider, narration, captions, and preview mode. This can reach $5, so Reply `proceed` to continue or `cancel` to stop.
Do not call any paid MCP tool until the user replies `proceed`. If the user replies `cancel`, stop without generating. The gate runs after the URL and optional flags are known, before avatar generation, speech, lipsync, captions, or video composition.
## Behavior
### Defaults — fire fast, no unnecessary mid-flow confirmation
- **Resolve avatar / voice silently.** Never ask "should I use your avatar?" or "which voice?" before firing. Honor explicit overrides (`--avatar`, `--voice`) when supplied; otherwise generate a presenter avatar and pick a default voice, and proceed. See Step 1 for the full resolution waterfall.
- **Only the cost transparency gate asks for `proceed`.** Step 5 preview is normally **opt-in via `--preview`** for explicit avatars, but it becomes mandatory auto-preview when the avatar source is a `generated` or `regenerated` fallback. After the cost gate, the flow runs end-to-end except for that required fallback-avatar preview guardrail.
- **Do not solicit `--focus` either.** Make a confident first attempt from page structure; users re-run with `--focus "X"` if the angle missed.
These defaults match industry standard for media-gen tools (Midjourney / Sora / Runway / HeyGen / Pika.art): submit → render → return. Account credit balance + provider failover (Step 9) are the canonical guardrails.
### Local avatar images on Claude Desktop
Claude Desktop can't pass inline-pasted images to MCP tools yet (Anthropic-side limitation). If the user pastes a photo inline, or mentions a local file they want as `--avatar`, pause Step 1 and kindly send them this — something like:
> Heads up — pasted images don't reach MCP tools on Claude Desktop yet (Anthropic limitation). Two easy options for your avatar:
>
> - **Paste a URL** if it's already hosted (Imgur, S3, your site) — fastest
> - **Attach the image file** so I can upload it before generation.
When a local file arrives, convert it to a public URL with `upload_asset` and use the returned `public_url` as `--avatar <url>` before Step 1. Already-hosted `https://...` URLs work as-is and skip this entirely. If no avatar is supplied at all, a presenter portrait is generated silently (Step 1).
### Step 0 — Resolve URL (empty-args menu)
Strip flags (`--focus`, `--avatar`, `--voice`, `--live-url`, `--lipsync-provider`, `--no-captions`, `--preview`, `--skip-preview`, `--yes`) and `key=value` parameters from `$ARGUMENTS`. **If what remains contains no `https://...` URL** (or is empty / whitespace-only), print this menu **verbatim** as your full response, then **stop and wait for the user's next message**. Calling a tool here risks recording or explaining the wrong page. If `$ARGUMENTS` already carries a URL, skip this step silently and proceed to Step 1.
> **Which URL would you like me to walk through?** Works on any of:
>
> - **A GitHub repo** — e.g. `https://github.com/anthropics/claude-code` (activates repo-aware mode: README scan + live-demo detection)
> - **A product page / launch page** — e.g. `https://pika.art`
> - **A docs site** — e.g. `https://docs.anthropic.com`
> - **A blog post / article URL**
>
> Output: 1280×800 macOS Sonoma frame with a bottom-left avatar lipsync and element-targeted zoom on every mid-section beat. Default flow runs end-to-end after the cost gate; pass `--preview` if you want a 3-second lipsync sanity check for an explicit avatar. Generated / regenerated fallback avatars auto-run that preview guardrail.
>
> Reply with the URL and I'll start.
>
> *Tip: you don't need to type `/pika:explainer` — just say things like "walk me through <url>", "make a demo video of <url>", or "explain this repo: <github-url>" and I'll fire this skill automatically.*
When the user replies with a URL, treat it as the resolved input and proceed to Step 1. Do not re-prompt.
### Step 1 — Parse input + detect mode
Required: `url` (must be `https://...`).
Optional: `--avatar <url>` (the presenter photo; if omitted, one is generated), `--voice <minimax-voice-id>`, `--focus "..."` (editorial guidance woven into vo_text), `--live-url <url>` (force-supply live demo URL — GitHub mode only), `--lipsync-provider <pika|kling>` (defaults to **`pika`** — parrot a2v, ~2-5 min wall-clock, slightly more dramatic head motion. Pass `kling` for tighter face-centered output at ~5-30 min wall-clock — Kling produces minimal-head-motion presenter shots but is the long-pole stage; reserve for high-stakes renders), `--no-captions` (skip the Step 11 caption burn — default is captions on), `--preview` (opt-in to the Step 5 preview gate for explicit avatars; generated and regenerated fallback avatars auto-run the preview gate before full lipsync). `--skip-preview` and `--yes` are accepted as no-ops for backward compatibility.
**Mode detection:**
- **GitHub mode** — URL host is `github.com` AND path matches `/{owner}/{repo}` (no further path segments past the repo root). Activates the repo-aware extras: README scan, live-demo detection, GitHub-specific selectors.
- **Generic-URL mode** — anything else (a product page, docs site, blog post, deeper GitHub path like `/blob/HEAD/path`). Skips the GitHub extras; uses generic CSS selectors and walks through the URL itself.
**Avatar resolution (silent — never ask the user):**
1. If `--avatar <url>` was passed, use it.
2. Else call `generate_image` once with prompt `"professional presenter, friendly tech narrator, studio portrait, 1:1, natural lighting"` and use the returned URL. Do **not** ask the user "should I generate one?" — just generate silently.
Track `avatar_source` as one of `explicit`, `generated`, or `regenerated`.
**Avatar suitability gate (mandatory before any lipsync spend):**
Call `analyze_media(media=<avatar>, query=<gate_query>)` once on the resolved avatar image before Step 4 TTS and before any `generate_lipsync` preview/full call. This is the one case where avatar analysis is required, because the avatar is the central presenter asset and a bad generated avatar can burn the whole render.
Gate query:
```
Return JSON only: {
"is_single_front_facing_coherent_human_face": boolean,
"has_visible_mouth": boolean,
"is_faceless_or_masked": boolean,
"is_mascot_illustration_or_non_human": boolean,
"face_suitability_score": 0-100,
"apparent_gender": "female" | "male" | "unclear",
"reason": string
}
Is this image suitable for talking-head lipsync: a single front-facing coherent human face with a visible mouth? Flag faceless, masked, mascot, illustration-only, non-human, or distorted avatars.
```
Pass only if `is_single_front_facing_coherent_human_face == true`, `has_visible_mouth == true`, `is_faceless_or_masked == false`, `is_mascot_illustration_or_non_human == false`, and `face_suitability_score >= 75`.
If the avatar fails and `avatar_source` is `explicit`, stop before paid lipsync and ask for `--avatar <real-looking-photo-url>` or permission to generate a presenter portrait. If the avatar fails and `avatar_source` is `generated`, call `generate_image` once with prompt `"realistic professional presenter portrait, single front-facing coherent human face, visible mouth, friendly tech narrator, neutral studio background, 1:1, natural lighting"`; set `avatar_source = "regenerated"` and re-run this suitability gate on the regenerated avatar. If the regenerated avatar also fails, abort with a clear error instead of attempting lipsync.
Set `avatar_auto_preview_required = true` whenever `avatar_source` is `generated` or `regenerated`; otherwise false unless the user passed `--preview`.
**Voice resolution (silent — never ask the user):**
1. If `--voice <id>` was passed, use it.
2. Else pick a casual MiniMax `speech-2.8-hd` preset matching the resolved avatar's apparent gender:
- **Female-coded avatar** → `English_PlayfulGirl` (warm, casual, clearly female-voiced — verified)
- **Male-coded avatar** → `English_Jovialman` (warm, casual male)
- **Unclear / gender-neutral** → `English_Jovialman` (default)
Infer gender from the avatar suitability gate result (`apparent_gender`). Do **not** ask the user.
**Do NOT use `English_FriendlyPerson`** — despite being categorized under "female" in MiniMax's catalog, its display name is "Friendly Guy" and it reads as male in playback. `English_PlayfulGirl` is the canonical casual-female pick. Other verified-female alternates: `English_Upbeat_Woman`, `English_LovelyGirl`, `English_radiant_girl`.
The flow below is annotated per step: **GitHub-only**, **Generic-only**, or **Both** modes.
### Step 2 — Read source (no MCP call)
**Both modes:** use Claude's `WebFetch` on the input URL to pull the page's main content (h1, hero section, headings, primary copy).
Build `proper_noun_glossary` during this source read. Include canonical spellings of product, repo, company, model, framework, and proper nouns from the URL/domain, page title, h1, README headings, repo metadata, package names, and repeated capitalized tokens. For GitHub repos, preserve exact spellings surfaced by README/source scan, e.g. `Ollama`, `Llama`, `DeepSeek`, `Gemma`. Use this glossary later when authoring narration and when burning manual captions so caption text does not phonetically drift into misspellings like "Olama" or "DeepSeq".
**GitHub mode additions:** also fetch top-level file tree, (best-effort) `package.json` / `pyproject.toml`, and GitHub API repo metadata via `gh api repos/{owner}/{repo}` for `homepage`, `description`, `language`, `topics`. Detect a candidate `live_url` in this priority:
1. User-supplied `--live-url`.
2. **GitHub API `meta.homepage` field** — set when the maintainer configured the repo's homepage in GitHub settings.
3. `package.json` `"homepage"` field.
4. First match in README of `https?://[^\s)\"'<>]+(?:vercel\.app|netlify\.app|github\.io|fly\.dev|railway\.app|render\.com|herokuapp\.com|surge\.sh)[^\s)\"'<>]*`.
5. **Any other URL in README that the badge area / "Live Demo" / "Project Page" / "Demo" text points at.** The allowlist regex above misses arbitrary custom domains (e.g. `<project>-project-page.com`); when the README explicitly designates a project page, prefer that over the github.io fallback.
6. GitHub Pages convention `https://{owner}.github.io/{repo}` — but only if the deep tree contains a frontend signal (one of `index.html`, `App.tsx`, `App.jsx`, `App.vue`, `app.py`, `main.py`).
If no candidate resolves, the beat sheet skips beats 6–7.
**Generic-URL mode:** the input URL itself is the only URL the beats walk through — no `live_url` inference, no extra metadata fetches. Skip Step 2.5 and Step 3.0; jump straight to Step 3.
### Step 2.5 — Verify `live_url` reachability (GitHub mode only, no MCP call)
If a candidate `live_url` was selected, verify it serves real content **before** authoring beats 6–7. Use `WebFetch` on the candidate and check the response:
- If the response status is 4xx / 5xx, **drop `live_url` to None** and skip beats 6–7. The github.io fallback in particular is reachable as a hostname but often returns 404 ("There isn't a GitHub Pages site here") for repos that haven't enabled Pages — recording that 404 page wastes ~12s of the explainer on wrong content.
- If the response renders the GitHub Pages "404 — There isn't a GitHub Pages site here." template (heuristic: response body contains `"There isn't a GitHub Pages site here"`), drop `live_url` and skip beats 6–7.
- Otherwise, keep `live_url` for beats 6–7.
This mirrors the legacy reachability gate that checked `live_url` with a short timeout and followed redirects.
### Step 2.6 — Generic-URL pre-flight (Generic-URL mode only, no MCP call)
Before authoring beats for a non-GitHub URL, WebFetch the input URL and inspect the response. This step prevents three common Generic-URL failure modes: (a) recording a captcha / bot-block page instead of content, (b) the cookie/consent banner eating the first ~3 seconds of video, (c) generic CSS selectors missing the page's actual hero / sections.
**A. Bot-block / captcha detection — abort if matched:**
If the response body contains any of:
- `"Verify you are human"` / `"verify you are not a robot"`
- `"captcha"` / `"CAPTCHA"` / `"reCAPTCHA"`
- `"403 Forbidden"` / `"Access Denied"`
- `"Just a moment"` + `cf-chl-bypass` (Cloudflare challenge)
- `"We're sorry, something went wrong"` (Amazon-style bot block)
- A `<title>` or h1 of just "Robot Check" / "Are you a robot?"
→ **ABORT** with a clear error to the user: "Generic-URL mode can't render this site — the page is showing a bot-detection / captcha challenge under headless Chrome. Try a different URL, or run a real-user version of the page first to verify it loads cleanly."
**B. Cookie / consent-banner detection — defuse with `extra_css` + optional click:**
Scan the response for these patterns (case-insensitive):
- IDs / classes starting with `onetrust-`, `truste-`, `cookie-banner`, `cookie-consent`, `gdpr-`, `consent-`, `cmp-`
- Buttons matching `(?i)accept (all )?cookies` / `(?i)agree.{0,10}cookies` / `(?i)i (accept|agree)`
- Apple-specific banner: id `ac-gdpr-banner` or class `as-globalfooter-curtain`
- Google consent: `[role="dialog"]` with text "Before you continue"
If detected, set `cookie_banner_present = true`. Defense in depth — the recording uses BOTH:
1. **CSS injection (`extra_css`)** in the `capture_website` call to hide common banners universally — even if the click below misses, the banner is visually gone.
2. **A `click` `timed_action`** at `at_s: 0.0` against the most likely dismissal selector (extracted from the WebFetch DOM, e.g. `#onetrust-accept-btn-handler`, `[aria-label*="Accept all" i]`, `button[id*="accept"]`).
The `extra_css` payload (use this verbatim — covers ~80% of consent platforms):
```
#onetrust-banner-sdk, #onetrust-pc-sdk, #onetrust-consent-sdk { display: none !important; }
#truste-consent-track, #truste-consent-content, .truste_box_overlay { display: none !important; }
[id*="gdpr-cookie"], [id*="cookie-consent"], [id*="cookie-banner"] { display: none !important; }
[class*="cookie-banner"], [class*="cookie-consent"], [class*="consent-banner"] { display: none !important; }
View on GitHub