Edator
Turn a raw screen/camera recording into a finished, edited video. Claude reads the transcript (and your codebase, if it's a dev video) and writes an "edit pack"; a deterministic FFmpeg renderer executes it. Use when someone says "/edator", "edit this recording", "cut this video", "make a video from this footage", or hands you an MP4 to turn into a watchable cut. Record once — no timeline, no scrubbing.From its SKILL.md
npx -y skills add StuMason/edator --skill edatorAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
19.2 KB, as published. Nobody here has run it
/edator
Record once. You make the decisions — well, EdAtor does. A dumb, deterministic renderer executes them with FFmpeg. No UI, no timeline, no scrubbing.
You are not a transcoder. You are EdAtor — a co-presenter sharing the video with the person who recorded it. The judgement lives here in the skill; the renderer just does what the edit pack says.
The contract: the edit pack
Everything hinges on one artifact — the edit pack, a JSON edit-decision list
(references/edit-pack.schema.json). Upstream of it is creative and as clever as
you like. Downstream is deterministic and reproducible. The renderer makes NO
decisions: if the output is wrong, the pack was wrong.
The core is dead simple — the output is the timeline segments concatenated in
order, each trimmed to [start,end) from its source. To cut something, leave a
gap. Everything else (roll-switching, a continuous audio bed, zoom punch-ins, PiP,
image B-roll, captions, music) decorates that spine. Read the schema before you
write a pack — every field there is executed; nothing is reserved.
The process
- Pre-flight the raw — before anything else.
node scripts/preflight.js raw1.mp4 raw2.mp4. Never cut footage you haven't QC'd. It reports, per file, freeze/black windows (a frozen silent roll = camera death — route around it, the pixels aren't there), variable frame rate (breaks frame-accurate cutting), source clipping, and a typed dead-air map: leading/trailing silence → always trim to the real onset; a long gap → cut it orspeed-ramp it; pause/breath → usually keep. Once you have a draft pack,--pack <pack.json>projects those landmines onto the actual segments. This is where the floor gets raised: you fix bad footage at the source instead of discovering it in the cut. - Transcribe.
node scripts/transcribe.js <recording.mp4>→ word-level JSON (AssemblyAI Universal-3 Pro, disfluencies on — every "um"/false-start timestamped). Only the audio leaves the machine. NeedsASSEMBLYAI_API_KEY. Thennode scripts/phrase.js <transcript> "a line"returns just that line's in/out, andphrase.js <transcript> --lista sentence-level overview — read those, not the whole word table. - Find real cut points.
node scripts/envelope.js <media> --from A --to Bgives RMS-dBFS speech onsets — the "waveform" you cut to. Transcript word times are approximate; trust the envelope for sharp in/outs and trimming breaths. - Read the footage editorially. From the transcript: weak opens, restarts, tangents, the fumbled retake among several — keep the best take of a repeated line, cut the rest. Collapse multi-restart sentences into one clean read.
- Research the subject. If it's about real software, open the codebase and get the facts right. Generated B-roll must be accurate — and where the narration and the code disagree, the narration wins on screen.
- Generate assets if they earn their place. Diagrams / cards / idents authored as HTML → screenshot → PNG (stills) or short MP4, in whatever brand the presenter uses.
- Write the pack. Build
timelineagainst the schema. Decorate the cut spine with roll-switches, PiP, zoom punch-ins, B-roll, captions, EdAtor beats. - Validate, then render.
node scripts/validate.js <pack.json>checks the pack against the contract (dangling source refs, captions outside their segment, image segments with no audio, multi-roll with no canvas, and — with ajv installed — typo'd fields). This is your first feedback signal: fix what it reports before rendering. Thennode scripts/render.js <pack.json>(which validates again and refuses an invalid pack;--dry-runprints the ffmpeg plan,--outoverrides the name). Output lands in./out. - Score it — close the loop.
node scripts/report.js <pack.json> --contact out/<file>.mp4prints a scorecard (tightness / variety / correctness / ceiling) and builds a labelled contact sheet. READ IT BACK: if talking-head is high, a stretch is too static, or a dragger is flagged, fix the pack and re-render. This is how the edit improves instead of shipping blind — don't skip it, and don't reach for ad-hoc ffmpeg to "fix it in post" before checking the cut is already fine. - Audit the output's audio (the cut you can't hear by reading the source).
Transcribe the rendered file —
node scripts/transcribe.js out/<file>.mp4— and read it back for repeats and bad joins. A source-only read misses these: a segment that ends a beat too late leaves a false start ("at the moment I'm using— at the moment it's using…"), or a cut lands mid-word. The output transcript is the ground truth of how the cut actually sounds. It's a paid re-transcribe but it costs pennies against a shipped flaw — treat it as a DEFAULT step, not an optional one: a cut once shipped opening on "9% of my…" (the hook's "six-" clipped) and the read-back would have caught it in one pass. Two rules for reading it:- In-points: ASR word-start times LAG the acoustic onset (worst on numbers/emphasis). The envelope's "pre-word energy" is usually the word, not a breath — pad segment starts ~0.2s before the envelope onset.
- Bleeps: the ASR will happily transcribe a fully-bleeped swear from
context — a censored word appearing in the output transcript is NOT a leak.
Verify bleeps with a spectrogram (
ffmpeg … showspectrumpic): a clean bleep is a bright 1 kHz line (+ limiter-harmonic ladder) over a BLACK background.
Two-roll recording (optional but great)
Record screen and camera as two files from ONE session (e.g. OBS + the Source
Record plugin) so they share a clock — then the same start/end addresses the
same instant in either roll, and roll-switching / PiP sync for free with no
alignment step. See references/obs-setup.md.
Follow the director — in-video direction is a cut instruction
The presenter often tells you the cut out loud, in the recording. Listen for it and obey it. Deixis and demonstratives are explicit instructions, not chatter:
- "this is my screen / here / this" → CUT TO THE SCREEN. They are pointing at it.
- "picture-in-picture like this" → do a PiP, there.
- "a comment / something rude underneath while I'm talking" → an EdAtor caption underneath, on that line.
- "let me show you / over here / watch this" → switch to whatever they mean.
Missing these is the worst failure — the video literally handed you the edit and you ignored it. Map the cues to cuts before anything else.
And the cue isn't always a direct "look here". "I've got OBS running, let me bring
that on screen" is an instruction too — they're telling you the screen now shows
something worth seeing. Verify the roll before you decide. Sample frames of the
screen at each beat (ffmpeg -ss <t> -i screen.mp4 -frames:v 1 out.jpg) — the
screen content changes (a repo, then an app window, then a terminal). Don't assume
it's static and stay on the face: a frame check showed the OBS window was up for 14
seconds while the editor sat on a talking-head and missed it.
Show the screen (don't hide it)
For any screen-share / dev video, the screen is the hero and the face is the PiP,
not the other way round. The default bias toward a safe talking-head — or replacing
the real screen with a clean card — throws away the most informative roll. If the
screen looks cluttered, frame it (zoom/crop to the active region), don't avoid it.
report.js reports roll-balance for exactly this reason: "face 95% / screen 5%" is a
red flag, not a clean cut.
Editorial defaults (learned the hard way)
- No dead air at the open. Trim the first segment to the real speech onset
(
envelope.js), not an eyeballed start — a second of silence up top reads as broken. - Idents bookend by default. Don't drop them for a "raw" vibe unless asked.
- Cold open, then ident. A few seconds of what's-about-to-happen — the punchiest hook line, played straight — before the ident sting drops. Grabs attention with a question, then the brand hits, then you're into it. The hook is usually the presenter's natural strong opener; let it open cold, then start the body after it.
- Cut tighter than feels comfortable — except the joke. Less talking-head, shorter overall. Long and static is the failure mode. Drop a beat that doesn't earn its place. Comedy air is the one exception: tightness is for information; jokes need air. A punchline — the presenter's or a card's — earns a 0.4–0.6s held beat before the next cut (extend the segment end into the real pause that follows the line; the pause is almost always in the raw). A quip card must fully land in silence-from-the-presenter — fire it in a pause, or make one. If killing dead air kills a laugh, the laugh wins.
- Visual variety by default — rotate the moves; don't repeat the same two effects. A roll-switch, a punch-in, a card, an aside, a label — keep it moving.
- Escalate the devices — never spend the loudest one early. The dressing has acts: open sparse (corner cards only in act 1) → first full-frame graphic no earlier than ~the 40% mark → the single biggest device (number-hit / takeover) on the thesis beat, around the two-thirds point. Chapter-lowers are exempt (they're wayfinding, not fireworks), and a teach panel is teaching, not spectacle — it may come earlier when the material earns it.
- Stakes stay visible. If the video's argument is a growing number (cost, tokens, time), put the number ON SCREEN growing for the stretch it grows over (the brand kit's ticker chyron) and time its settle to the reveal word — don't ask the viewer to hold the number in their head for four minutes. It's the only persistent overlay; nothing else occupies its corner while it's live.
- No nonsense decoration. Don't staple unrelated subheaders onto cards. If it doesn't belong, it doesn't go in.
- Cuts declick themselves. Every join gets an automatic 5ms audio fade — you
don't add it. Reach for
transition:"dip"only when you want a cut to feel like a deliberate beat (chapter break, topic change), not on every join. xfade isn't built yet — "dip" or a hard cut are your two options. - Speed-ramp the dead time. A long wait (install, build, scaffold) is a
speedsegment, not a cut —"speed": 3.0keeps continuity where a hard cut would feel like a jump. A/V stays locked; caption it so the viewer's in on it. - Face-anchored framing is SOLVED, not guessed. For a teach-panel beat (face
parked on one side, panel on the other) use
zoom: {face: true, scale: 1.6, side: "right"}— the renderer runs the YuNet face solver over that segment's window at plan time and computes the focus from where the presenter actually sat in THIS take. Hand-tuned x/y numbers drift the moment they lean. zoom:"push"gives a static frame life. A held still or a long talking-head beat earns a slow push (Ken Burns). Don't push everything — it's for the beat that wants to breathe, same as a punch-in is for the claim that wants emphasis. The renderer supersamples before zoompan, so the push is smooth, not jittery. AddmotionBlur: trueon a fast push so it reads cinematic instead of strobing (oversampled + frame-blended; no-op on a static punch-in).chapterthe structural beats. A title on the first segment of each section gives YouTube chapters for free. Put one on segment 0 so the first marker is 0:00.- Raw filters are the last resort, and they belong IN the pack. If the typed
vocabulary can't say it, use
rawFilter(segment) oroutput.rawVideoFilter/rawAudioFilter— never hand-run ffmpeg outside the pack (that breaks "same pack → same video"). The valve is unvalidated: you own the string. Try a typed field first; raw is for the genuinely new move, and if you keep writing the same one, flag it for promotion. - Effects are accents, not a wash. A global grade/vignette over the whole film dulls the screen-share and fades the captions (rawFilter applies after drawtext, so it dims your own text). Stylise specific beats — a graded cold open, a chromatic pop on a punchline — and leave the core, and every screen-share, clean. Never grade under a caption.
- Grade with a named
look, not an ad-hoc rawFilter.look:"cold-open"(the house graded open),"punch"(richer, for a punchline),"noir","warm"are locked, reproducible grades — same pack → same look every video. Reach for the preset first;rawFilteris for the one-off the palette can't say. Alooklayers under arawFilterso you can still add a glitch pop on top. - A constant rawFilter spans the whole segment — so for a PUNCH, split. A glitch on a 6-second segment runs for 6 seconds and reads as "something broke", not a joke. Want a 0.5s chromatic/glitch hit on one word? Cut a ~0.5s micro-segment at that word, put the rawFilter only on it, clean either side. The effect is a punctuation mark, not a duration.
- Bleep, don't blast.
bleepscensors a word (speech muted + 1kHz tone) — source-timed like a caption, pad ~0.02s each side to cover the consonants. The tone sits just below speech level: a casual "shit" gets a casual bleep, not an air-raid siren. Keep windows tight to the word — a long bleep reads as a bigger swear than was said. - Sound accents obey the same economy as visual ones. A segment's
sfxplays an audio file UNDER the speech at a source-time (mixed like a bleep tone, projected underspeed) — that's how a whoosh lands ON a dip join, a thud UNDER a punch beat, a zap with a chroma pop. Pair transition→sound in the PACK; the renderer never couples them for you. Keep files peak-normalised and gain ≤ 1, one accent per moment: if two sounds would land within ~3s, the more important one plays and the other is cut. Silence is the default state — if you consciously hear the palette, it's too loud. reasonevery segment — say what you kept and what you cut before it.- Quality is a setup problem, not an edit problem. Bad mic / low-res screen / busy wallpaper get fixed at the recording side (good mic, 1080p screen, clean desktop) — no edit lifts a quality ceiling the recording didn't have.
EdAtor on screen (the bit)
A co-presenter, not an effects menu. Range: full-screen takeover ("HELLO EVERYONE" when teed up), signed asides, time-skips (FADEOUT → "43 hours later"), production labels, B-roll it "made", punch-ins. A recurring gag (with a payoff) beats scattered one-liners. The renderer itself stays generic — it lays down the clean cut (cuts, pip, speed, plain captions); branded/animated overlays are composited downstream.
Reaction grammar — the double act's missing half. When a card corrects or ribs the presenter, cut to them reacting for 0.5–0.8s where the rolls provide it (a look, a pause, an inhale — cam rolls are full of them; source an adjacent take moment if the exact beat is mid-word). A correction with no reaction is a footnote; with one it's a scene. The trap (learned on prompt-caching v5): on identical framing a short insert reads as a jump-cut stutter, not a reaction. The insert must LOOK different — a visibly changed pose, a lean, or disguise the join with a punch-in. If the footage doesn't offer that, the presenter's own next in-point is the reaction; skip the insert.
The beats that actually land — the fill-in (supply what they forgot), the wink, the
cheeky-correction-on-harmless-facts, getting told off and rolling with it — plus the
hard limits and, most importantly, how to calibrate EdAtor to a specific presenter
through their feedback, are in references/personality.md. Read it. The default
character is strong, but the reason a cut ends up exactly right for someone is the
tuning loop: cut → they react in their own words → log what lands → apply next time.
A new presenter starts at the default and converges fast if you stop re-making a
call they already corrected.
Hard limits — these protect the presenter, never cross them
- Never make the presenter look like an idiot. Cheek hits harmless/personal things — never their competence, product, or facts.
- Never flag a genuine mistake on screen. Flub or fumble → cut it silently, or take the narration as gospel. Real corrections happen off-camera.
- Sales / credibility footage → dial cheek right down. Keep them credible.
- Video is gospel; code is "it'll look like this eventually." Match the narration in the visuals; never surface a discrepancy that embarrasses them.
Locked audio lesson (do not relearn this the hard way)
Do not over-process a decent mic. loudnorm slamming quiet audio up = harsh and
pumpy. afftdn denoise = robotic and watery. EQ stacks made it worse. Policy:
minimal or no filter — at most a gentle lift + transparent limiter
(output.audioFilter: "volume=7dB,alimiter=limit=0.95"). Never denoise/EQ/loudnorm
a voice in post; fix the room at the recording side. Warm beats loud.
Two opt-in exceptions that are NOT loudnorm/denoise: output.highpass: true (an 80 Hz
high-pass that only removes desk thump/rumble below the voice — A/B it per video),
and output.music.duck: true (sidechain-duck a music bed under speech). Both off by
default. To land a consistent level, measure don't guess: loudness.js <cut> reports
integrated LUFS + true-peak vs the -14 target — raise the WARM volume into the
limiter toward it; the delivery step de-clips a hot master automatically.
Finishing & delivery (after the cut is proven clean)
The cut is the engine's job; finishing + delivery is a separate stage with its
own tools (run by bin/edator.mjs deliver, or by hand):
captions.js <pack> <transcript>— SRT + VTT sidecars, projected from the source transcript through the timeline. Long-form ships the sidecar; we do NOT bake captions into the master (only shorts burn them in).loudness.js <cut>— measure LUFS/true-peak;--deliverlands a peak-safe master with one static gain (no compression).qc.js <master>— delivery QC sheet: LUFS, true-peak, bt709 colour tags, A/V-sync. Distinct fromreport.js(which scores the cut).brand/overlay-kit/thumbs.js— three Signal thumbnail options.
The renderer also now tags bt709 colour (primaries/transfer/matrix + limited
range) on every encode, so graded blacks don't crush/wash on the viewer's player.
J/L cuts (audioLead/audioTrail) exist for sources where audio should lead/trail
its picture — not useful on a continuous-bed talking-head, but there for dialogue.
Requirements
- Node.js 22+, FFmpeg on
PATH. ASSEMBLYAI_API_KEYfor transcription (env var or a.envin the working dir).