Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generates and prompts video clips on the filmmaking canvas. Use when the user asks to generate, render, animate, continue, restyle, edit, shoot, or compose a video clip; render script or shot notes as video; animate a storyboard, starting frame, image, character, location, or reference; use image, video, audio, storyboard, starting-frame, or voice refs; compose an ad, brand film, product promo, music-video shot, or video sequence; or before calling generate_video.js. Owns video CLI flags, refs,
.claude/skills/utopai-research-video-compose/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 387% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 268% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 185% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 427% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 454% | 0% |
Intent dispatcher. Patterns name trigger, call, edges, and prompt reference.
PROJECT_AGENT.md.--no-audio only for explicit silent/no-audio requests. Trailer/portrait/cinematic framing is NOT a trigger; audio is the baseline, not optional polish.For the ask-once flow and per-mode prices, see the project PROJECT_AGENT.md § "First-use generation choices". Pass --resolution only for 480p Draft or 1080p Final.
node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..." [--version <2.0|2.5>] [--duration <seconds>]
[--aspect-ratio 16:9] [--resolution <480p|720p|1080p>] [--no-audio]
[--label "..."] [--ref-source-id <id> ...] [--ref-audio-source-id <audio_id> ...]
[--source-node-id <id>] [--shot-id <N>]Calls go via --stage — see the project PROJECT_AGENT.md § "Draft gate".
--label defaults to truncated prompt. Use --ref-source-id for image/video refs, --ref-audio-source-id for audio refs, and --source-node-id for the authoring note. Mirror external URLs first. Do not set --shot-id during speculative/partial generation unless user asks for a reel position; story sequences assign Timeline order after planned clips land.
Match stated single-clip duration with --duration; omit for 15s default. Split or chain >15s totals.
Each clip costs real money even after staging — only stage after the user has explicitly asked for a video.
--version)Omit it for 2.0 (today's model): output 4-15s, priced per output second by resolution.
--version 2.5 renders PAI Video 2.5. Differences that change how you call it:
--duration is 5-30 (not 4-15). -1 (follow-the-input edit mode) is rejected.--resolution must be 480p, 720p, or 1080p; --aspect-ratio must be one of16:9 | 9:16 | 4:3 | 3:4 | 1:1 | 21:9 | adaptive.
reference-video second (each clip measured locally with ffprobe and rounded up). Hard cap 60 billed seconds; over that the CLI refuses before staging and names both halves. Tier prices are in the project PROJECT_AGENT.md § "Media CLIs (server/cli/)" — quote them from there, never from memory.
Stage 2.5 clips outside Auto.
ffprobe on the host; without it the CLIfails with infra rather than guessing a price.
On failure the limits blob is model-scoped: read min_output_sec / max_output_sec and max_billed_sec off the returned JSON rather than assuming 2.0's numbers.
2.0 (video-generation) — ≤9 image refs, ≤3 audio refs, ≤3 video refs; video refs cap at 15s aggregate.
2.5 — ≤30 image refs, ≤10 audio refs, ≤10 video refs; reference video caps at 30s aggregate, and every reference second also counts toward the 60s billed-duration cap.
Both: audio/video refs must be 1.8s-15.2s each (the shared reference-upload step's window), and audio refs need an image or video anchor — on 2.5 that one is checked only after credits are frozen, so the CLI refuses it locally. Read durations from workflow.json; on failure use the returned limits + sent, which are model-scoped.
Prompt wording binds each ref role:
| Role | Flag | Wording in prompt | |---|---|---| | Character identity | --ref-source-id (image) | "the character in @Image1" | | Location / setting | --ref-source-id (image) | "the location shown in @Image1" | | Opening frame | --ref-source-id (image) | "opening frame @Image1, …" | | Closing frame | --ref-source-id (image) | "closing on the frame from @Image1" | | Source clip — continue (next clip in a chain) | --ref-source-id (video) | Default = hard cut: "Hard cut from @Video1: open on a NEW camera angle; do not match its final frame." Same-shot ("Continue from @Video1 … maintain camera position") only for an authored held beat / oner / explicit user request — see references/video-extension.md | | Source clip — transform | --ref-source-id (video) | "Re-render @Video1 in …" | | Camera-move source | --ref-source-id (video) | "camera moves match @Video1" | | Action source | --ref-source-id (video) | "action choreography matches @Video1" | | VFX template | --ref-source-id (video) | "use the visual-effects template from @Video1" | | Voice / timbre anchor | --ref-audio-source-id | "Use @Audio1 as voice/timbre reference. Speak once, no echo." |
@Image1 / @Video1 / @Audio1, positional by flag order. Every @ImageN/@VideoN/@AudioN MUST have a matching --ref-source-id/--ref-audio-source-id flag — the CLI rejects a mismatch (bad_args) before generating. Mentioning the same ref many times is fine; only the highest index per kind needs a flag.@AudioN reference. Do not generate per-line audio refs unless the user explicitly wants separate final audio.audio_result.data.text verbatim. If it is just a character voice sample, do not replace the shot dialogue with the sample text.No Music if none). Use exact terms: locked off, handheld, subtle, slow dolly in, slow orbit, whip pan, speed ramp.references/video-single-shot.md.Pick the one that fits. Source lookup follows PROJECT_AGENT.md.
Storyboard guard: storyboard images route to Pattern 7 / references/video-multi-shot.md, never generic I2V/opening-frame wording.
Triggers: fresh clip unrelated to canvas content. Call: node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..."; omitted flags default to 15s, 16:9, 720p, audio on. Add --resolution 480p or --resolution 1080p only if the chosen video mode requires it. Edges: none. For the bracket scaffold and slot-by-slot construction when the user wants polish: see references/video-single-shot.md.
Triggers: animate/make video/put motion on a specific canvas image_result. Source: named image_result; storyboard mosaics route through Storyboard guard. Call: node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..." --ref-source-id <image.id>. Edges: { from: <source.id>, to: video_<N>, kind: "derived" } — emitted by the CLI. Anchor wording: opening-frame default (opening frame @Image1) or closing-frame (closing on the frame from @Image1); both use --ref-source-id. For slot-by-slot construction and the opening- vs closing-frame phrasing: see references/video-single-shot.md.
Triggers: video of character, character in setting, character action in location. Source: character / location image_result nodes (cap from §Reference caps). Call: node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..." --ref-source-id <char1.id> --ref-source-id <char2.id> .... Edges: { from: <char.id>, to: video_<N>, kind: "derived" } — one per --ref-source-id.
For single-shot composition and adjacent-role wording: see references/video-single-shot.md. For ≥2 internal shots in one render: see references/video-multi-shot.md.
Triggers: continue/extend/what happens after/scene follows existing video_result. Source: any canvas video_result node — agent-generated or user-uploaded (data.metadata.source is "pai" for generated and "user_upload" for dropped). Call: node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..." --ref-source-id <source_video.id>. Edges: { from: <source_video.id>, to: video_<N>, kind: "derived" }. Boundary defaults to a HARD CUT (clip 2 opens on a new angle — this avoids the same-shot seam morph). If the whole sequence fits ≤15s, render ONE multi-shot clip (Pattern 7) instead of chaining. Same-shot continuation is the exception (authored held beat / story-required oner / explicit user request). For the hard-cut + same-shot prefixes, the ≤15s guard, the sub-intent decision tree, and sequencing across linked calls: see references/video-extension.md.
Triggers: re-render/restyle/add/remove/swap/change/rewrite existing video_result. Creative edits use generate_video.js; ffmpeg is for mechanical ops. Source: any canvas video_result node — agent-generated or user-uploaded. Call: node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..." --ref-source-id <source_video.id>. Edges: { from: <source_video.id>, to: video_<N>, kind: "derived" }. For the Restyle / Partial / Replace / Re-plot decision tree and per-mode templates: see references/video-editing.md.
Triggers: have character say/narrate, use character voice. Source: any canvas audio_result node — agent-generated or user-uploaded. Call: node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..." --ref-audio-source-id <audio_id>. Often combined with character image refs for face + voice — pass both --ref-source-id <character_id> (for the character image) and --ref-audio-source-id <audio_id> (for the voice). Prompt:
audio_result.data.text exactly and bind it with `Use @Audio1 for timing, cadence, and voice. Keep the words unchanged.`[Character] says exactly: "..."; add the once/no-echo guard and phonetic spellings for risky words.data.text: sound/timing only; do not invent transcript.Edges: depends on which character refs attach (one kind: "derived" per ref).
Triggers: ≥2 shots inside one render, ad/MV/brand framing, or ≥10s with multiple movements. Call: node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..." [--ref-source-id <image|video.id> ...] [--ref-audio-source-id <audio.id> ...]. Edges: as per the underlying pattern (3, 4, 5) for any refs attached. For the 4-section scaffold (timeline / effects inventory / density map / energy arc) and how to populate the timeline from canvas script shot notes or storyboard mosaic panels: see references/video-multi-shot.md.
Cross-pattern asks route to one primary reference:
| Combo | Primary reference | Extra refs to attach | |---|---|---| | Character + voice-over | (Pattern 6 inline) | character image | | Music video with characters | video-multi-shot.md | character images + audio (Pattern 6 wording) | | Restyle preserving identity | video-editing.md (Restyle) | source video + character image | | Multi-clip chained sequence | video-extension.md | source video for each link | | Compose with camera-move from reference | video-single-shot.md | character images + camera-move video ref | | Render one script shot from canvas | Pattern 1, 2, or 3 by shot content (no dispatch — translate the shot note body to slot rules; preserve dialogue/VO verbatim) | character / variant refs + location / variant refs + voice anchors if the shot involves them | | Render a continuous script span (>15s total) as a dependent sequence | video-extension.md (script-driven chain; hard-cut handoffs by default — keep a link same-shot only for an unbroken oner the viewer must read as one motion) | source video per link + character refs (mandatory under hard cut) for identity | | Render a short script (≤15s total) as one piece | video-multi-shot.md (cross-skill source) | character image refs locked across shots | | Render a storyboard mosaic as one 15s video (every panel becomes a shot block) | video-multi-shot.md (storyboard cross-skill source; required for image_result.subtype === "storyboard") | mosaic image + character / location image refs that authored the mosaic |
For multiple clips, prefer hybrid dispatch: independent for separate scenes/time jumps/wardrobe-state changes/montage; chain only same continuous action/location/lighting/wardrobe-state/emotional beat. Do not chain across breaks where continuity is undesirable.
If a budget-aware caller needs savings, lower video resolution before shortening runtime. Do not suggest dropping material character variants, location variants, or voice anchors that preserve continuity.
For draft-stage JSON, one sentence with the price/status — see the project PROJECT_AGENT.md § "Draft gate". For terminal results, run the alignment check (frames via extract_frames.js) before the next-step recommendation — see the project PROJECT_AGENT.md § "Prompt alignment check". --ref-source-id flags drive provenance edges; they're captured in the draft argv and materialize on the real video_result after the user fires.
Video-specific message hints:
asset_rejected with "DownloadFailed" — failed_url was unreachable; swap.asset_rejected with "DurationTooLong" / "DurationTooShort" — failed_url's duration is outside 1.8s–15.2s. Swap it or trim with ffmpeg.bad_args with "reference_audio cannot be the only reference input" — add an image or video ref alongside the audio.bad_args with "invalid video duration, exceeds 15s" — sum of video refs breached limits.max_total_video_sec. Read each video_result.data.duration from canvas and drop refs until the sum is ≤15s.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-15 | pass→pass | 6,034 | 3,889 | -36% | 1 | 1 | 0% | 1,016 | 4,517 | +345% | 0 | 0 | — |
case-16 | pass→pass | 5,433 | 4,556 | -16% | 1 | 1 | 0% | 917 | 4,562 | +397% | 0 | 0 | — |
case-01 | fail→pass | 5,798 | 5,281 | -9% | 1 | 1 | 0% | 964 | 4,697 | +387% | 0 | 0 | — |
case-02 | fail→pass | 8,366 | 5,152 | -38% | 1 | 1 | 0% | 1,276 | 4,696 | +268% | 0 | 0 | — |
case-03 | fail→pass | 10,993 | 6,913 | -37% | 1 | 1 | 0% | 1,757 | 5,010 | +185% | 0 | 0 | — |
case-04 | fail→pass | 4,906 | 4,448 | -9% | 1 | 1 | 0% | 857 | 4,514 | +427% | 0 | 0 | — |
case-05 | fail→pass | 4,631 | 4,278 | -8% | 1 | 1 | 0% | 821 | 4,545 | +454% | 0 | 0 | — |
case-06 | fail→pass | 6,445 | 5,458 | -15% | 1 | 1 | 0% | 1,078 | 4,643 | +331% | 0 | 0 | — |
case-07 | fail→pass | 9,657 | 6,858 | -29% | 1 | 1 | 0% | 2,013 | 5,070 | +152% | 0 | 0 | — |
case-08 | fail→pass | 5,920 | 3,010 | -49% | 1 | 1 | 0% | 851 | 4,167 | +390% | 0 | 0 | — |
case-09 | fail→pass | 4,410 | 5,944 | +35% | 1 | 1 | 0% | 644 | 4,795 | +645% | 0 | 0 | — |
case-10 | fail→pass | 6,590 | 6,531 | -1% | 1 | 1 | 0% | 1,045 | 4,791 | +358% | 0 | 0 | — |
case-11 | fail→pass | 4,998 | 4,810 | -4% | 1 | 1 | 0% | 818 | 4,669 | +471% | 0 | 0 | — |
case-12 | fail→pass | 6,476 | 4,493 | -31% | 1 | 1 | 0% | 1,214 | 4,572 | +277% | 0 | 0 | — |
case-13 | fail→pass | 6,040 | 4,630 | -23% | 1 | 1 | 0% | 865 | 4,567 | +428% | 0 | 0 | — |
case-14 | pass→pass | 3,267 | 2,519 | -23% | 1 | 1 | 0% | 590 | 4,238 | +618% | 0 | 0 | — |
case-17 | fail→pass | 5,785 | 4,597 | -21% | 1 | 1 | 0% | 984 | 4,600 | +367% | 0 | 0 | — |
case-18 | fail→pass | 5,167 | 9,276 | +80% | 1 | 1 | 0% | 850 | 5,664 | +566% | 0 | 0 | — |
case-19 | fail→pass | 13,201 | 7,849 | -41% | 1 | 1 | 0% | 2,136 | 5,103 | +139% | 0 | 0 | — |
case-20 | fail→pass | 4,577 | 4,572 | -0% | 1 | 1 | 0% | 756 | 4,646 | +515% | 0 | 0 | — |
case-21 | fail→pass | 4,045 | 3,867 | -4% | 1 | 1 | 0% | 753 | 4,450 | +491% | 0 | 0 | — |
case-22 | fail→pass | 15,439 | 3,152 | -80% | 1 | 1 | 0% | 764 | 4,257 | +457% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +86 percentage points is the difference between those two pass rates over the 21 comparable cases.
The publisher has shipped newer versions since this run, so these numbers describe v1, not the version currently listed.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.