| name | video-orchestrator |
| description | Decide HOW to edit a video before editing. Use when the user says 'edit a video', 'edit this video', 'cut my podcast', 'cut this', 'make verticals from', 'turn this into clips', 'produce this video', 'polish this audio', 'chop this up', 'repurpose this', or shares a video file path with vague intent. ALWAYS triggers BEFORE watch-video and before any direct tool call - this skill picks the tool, watch-video only executes CutSense. |
| version | 1.0.0 |
| model | opus |
video-orchestrator
You route every video-edit request to the right tool. You never render. You ask, you decide, you dispatch.
Precedence rule (read this first)
If the user explicitly named a tool ("use cutsense", "run hyperframes", "build it in remotion"), DO NOT run the orchestrator flow. Hand off:
- "cutsense" -> invoke
watch-video skill
- "hyperframes" -> invoke
hyperframes skill
- any other named tool -> follow that tool's adapter at
adapters/<tool>.md
Otherwise, run the full orchestrator flow below.
Available tools
You route to one or more of these. Each has a corresponding adapter in adapters/.
| Tool | Adapter | Strength |
|---|
| CutSense | adapters/cutsense.md | Footage cuts with brand-pill captions, silence/filler removal, multi-vertical. Default for talking-head footage. |
| HyperFrames | adapters/hyperframes.md | HTML+CSS+GSAP compositions. Branded title cards, kinetic type, audio-reactive visuals. |
| video-use | adapters/video-use.md | LLM-driven cut picking from raw transcript. Best when you have many takes or need semantic chapter detection. |
| HeyGen | adapters/heygen.md | AI avatar lipsync from script. No camera needed. |
| Hedra | adapters/hedra.md | AI avatar lipsync, alternate to HeyGen. |
| Remotion | adapters/remotion.md | Standalone brand cards (title / lower-third / outro CTA), pixel-perfect, fast. Composition source at remotion-templates/brand-card/. Also used internally by CutSense for footage rendering. |
The flow
Step 1: Identify the source
Read what the user said and what they attached. If you have a file path, run ffprobe on it to get duration + resolution. If you have a URL, note it. If neither, ask.
Step 2: Match to a template
Check templates/INDEX.md for the closest template. Most requests match one of:
zoom-podcast-multi.md
walking-video-topics.md
zoom-lecture-verticals.md
talking-head-shorts.md
avatar-explainer.md
branded-promo-from-url.md
screen-recording-tutorial.md
ad-hook-variations.md
If no template matches cleanly, build the brief from scratch.
Step 3: Ask the 6 batched questions
Use AskUserQuestion in one tool call with these 6 questions. Phrase options to match the matched template's likely intent.
- Source type: zoom_podcast / zoom_lecture / walking_selfie / screen_recording / url / other
- Primary intent: vertical_clips / long_cut_polish / educational_verticals / promo / ad_variants / avatar_explainer
- Output count + length: e.g. "1 x 60s vertical", "5 x 30-60s verticals + 1 x long cut", "3 x 30s ads"
- Captions: brand_pill (cyan/orange) / jumbo / none
- Cleanup level: heavy (silences + fillers + retakes) / standard (silences only) / off
- Tool mode: pick_best / show_3_options / fork_to_3
Step 4: Surface 2-3 options
Even if tool_mode = pick_best, present 2-3 candidate tools with one-line pros and cons EACH. Format:
Three tools fit this:
1. <tool_name> - <one-line summary>
pros: <best fit reason>
cons: <trade-off>
2. <tool_name> - <one-line summary>
pros: ...
cons: ...
3. <tool_name> - <one-line summary>
pros: ...
cons: ...
Pick: 1, 2, 3, or "fork all three".
The first option is the recommended default. Bold it. The user can override or fork.
Step 5: Build the brief
Fill brief-schema.json with the user's answers + template defaults + sensible inferences. Save to ${VIDEO_JOBS_DIR}/<brief_id>/brief.json. Show the brief inline before executing.
brief_id format: YYYY-MM-DD-<3-word-slug> (e.g. 2026-04-26-podcast-acme-launch).
Step 6: Dispatch
Single-tool mode:
- Read
adapters/<tool>.md
- Run the commands listed there with values from the brief
- Wait for output at
<output_dir>\<tool>\out_NN.mp4
- Run QA:
python qa/run-qa.py <brief_id> (write QA.json per output)
- Write
<output_dir>\COMPARE.md (single-tool variant: tool, output link, duration, LUFS, face-safe, notes)
Fork mode (tool_mode = fork_to_3 or user said "fork all three"):
- Write
<output_dir>\brief.json
- In ONE assistant turn, spawn N parallel
Agent calls (subagent_type: general-purpose). Each call receives:
- The full brief JSON
- One tool name they own
- Path to that tool's adapter MD
- Output dir
<output_dir>\<tool>\
- After all sub-agents return, run
python qa/run-qa.py <brief_id> once over all forks
- Write
<output_dir>\COMPARE.md with the multi-tool table (see fork/compare-template.md)
- Show COMPARE.md inline; surface the local file paths
Step 7: Verify before handoff
Before saying "done":
- ffprobe each output to confirm duration matches expected
- Check QA.json - if any FAIL, fix or flag explicitly
- For fork mode, confirm all N outputs exist
- Show the user the absolute path(s) they can click
Per the verify-before-handoff rule: never say "test at " without confirming the file is present and ffprobe passes.
Universal brief contract
Every adapter takes the same brief JSON. Schema at brief-schema.json. Filled example at brief-example.json.
Required top-level keys: brief_id, source, intent, output, cleanup, brand, tools, fork_mode, qa_gates, output_dir.
Adapters MUST NOT mutate the brief. They read it, run their tool, write to <output_dir>/<tool_name>/.
Brand defaults
Default brand pack: brand/pack-personal-social.json (cyan #40DCE0 + orange #FCA529 + near-black #0A0A0A, Outfit + JetBrains Mono).
This is wired in CutSense's render-cut.mjs. Override per-job by setting brief.brand.pack to local/brand-pack-mine.json (your custom site brand pack, generated locally) or any other file.
QA gates (always run)
Three checks at qa/:
face-safety.py (mediapipe) - no captions/overlays on face bbox
loudness.py (pyloudnorm) - integrated LUFS within +/-1 of target, true peak < -1.0 dBTP
timing.py (ffprobe) - duration within +/-2s of target, no cut spacing < 0.4s
run-qa.py orchestrates and writes QA.json per output.
Anti-patterns
- Picking a tool without surfacing alternatives. Always show 2-3 options.
- Skipping the question batch and inferring from a one-liner. Ask.
- Letting watch-video grab the turn. The orchestrator's description must trigger first.
- Sequential sub-agents in fork mode. Always parallel, always in one assistant turn.
- Saying "done" before QA passes. Always verify, always include the path.
- Mutating the brief inside an adapter. Brief is read-only contract.
- Burying captions on faces. Face-safety QA is non-optional.
- Em-dashes anywhere in user-facing output (project rule).
Where things live
- Skill root:
<skill_root>/
- Output jobs:
${VIDEO_JOBS_DIR}/<brief_id>/
- video-use install:
${VIDEO_USE_DIR}/ (clone from https://github.com/browser-use/video-use)
- CutSense source:
${CUTSENSE_DIR}/ (your local CutSense clone)
- HyperFrames CLI:
npx hyperframes (junctioned skill at ~/.claude/skills/hyperframes/)
- HeyGen call shape: a typical 2-step provider-pick pattern (avatar_id present -> HeyGen, else Hedra)
Keys (read from local/.env, never hardcode)
All API keys live in local/.env (copy from .env.example at skill root). Required keys:
ELEVENLABS_API_KEY (Scribe + TTS)
GROQ_API_KEY (Whisper alternate)
HEYGEN_API_KEY
HEDRA_API_KEY