--- name: ghost-editor description: "AI video editor for talking-head reels. Turns a raw phone recording of someone talking to camera (retakes, pauses, false starts) into a finished vertical 1080x1920 reel for Instagram, TikTok or Shorts: the best take of each sentence, pause trimming, word-timed captions that never cover the face, motion scenes on the spoken word, sound effects mixed against the voice and checked B-roll. Use when the user hands over a talking-head video and says \"edit this\", \"make a reel\", \"make it look like this video\", \"add captions/sound effects\", \"pick the best takes\", \"remove the pauses\", asks for a re-cut, or names a style. Named styles (recipes/, docs/PLAYBOOK.md): Clean OS, Seoul list, RESULT, Face RESULT, stan style, vector explainer, POV montage, Whiteboard, Kinetic doc, Step Deck, Product page, Tactile paper, Caption News, News Package, Telestrator, Halftone Dossier, Swiss Grid, Data Story, Hologram HUD; plus seven presets (clean, editorial, meme, cinematic, launch, kinetic, pop). Not for landscape screen recordings." --- # ghost-editor One recording in, one reel out. The creative work is choosing takes and planning beats. The scripts do everything else the same way every time: cut math, zoom tweens, captions, SFX levels, asset copying, QA. Nothing on screen says anything the speaker did not say. A meme is a reaction, so it lands ON or just AFTER the punchline, never before it. Skill dir: `~/.claude/skills/ghost-editor` (below: `$S`). A reel project is any directory (`$P`); `build.mjs` copies what it needs into `$P/assets` so the project renders on its own. ## Cost: one fresh helper per reel, the right model per step Measured on a batch of 21 reels: looking at frames was ~5 % of the Claude usage. The cost is context re-read on every turn (one helper that built 3 reels in a row re-read 212 M cached tokens over 464 turns) and new generator code written per reel (two helpers per style each wrote their own lib). Numbers: docs/PLAYBOOK.md "Cost". - **One reel = one FRESH helper** (Agent tool). Never one helper for several reels or a wave. The session that talks to the user orchestrates: fill `templates/briefs/build-one-reel.txt` (build -> preview -> stop), send them the preview, and on their OK fill `templates/briefs/finish-one-reel.txt` for a new helper. Changes after their notes go to a fresh helper with the reel's `reel.py` and their words (the generator is the state, not the old transcript); SendMessage the old helper only for a one-line fix. Spawn helpers as `reel-helper` when the session lists it (`templates/agents/reel-helper.md`, copied to ~/.claude/agents/; shell and file tools only): a general-purpose helper starts at ~85 k tokens of tool and skill lists and re-reads them on every call. Long jobs run in one blocking call, never a background job polled per turn. - **Model per step:** Opus (the default) plans and judges: takes, hook, beats, components, `reel.py`, snapshots, the user's notes. Sonnet (Agent tool `model: "sonnet"`) runs fixed recipes: preview / final renders, `finals.py` qa / deliver, `components.py record`, file moves and commits. It stops on a hard QA fail and reports; the fix goes back to Opus. Many frames are Gemini Flash's job (broll_vision / broll_qa), not Claude's. - **Little code per reel:** a named style's reel is a short generator on its recipe's builder (`clean-os/cleanos.py` + `kit.py`, `result-podcast/faceresult.py`, `whiteboard/board.py`, `seoul-list/seoul.py`); read ` --api`, not old generators, and never write a per-batch lib. A part two reels needed goes into the builder's kit. - **Small context:** grep / `sed -n`, not cat; one overview sheet per look (`snap.py beats` -> overview.jpg), not single frames; never re-read what you just wrote. Measure a reel with `scripts/reel_usage.py `. ## Pipeline ``` prep.sh -> $P/assets/talk.mp4 1080x1920 30 fps, voice at -16 LUFS transcribe.py -> $P/build/words.whisper.json + take table (all takes) face_track.py -> $P/build/face.json (captions and cards never cover the face) choose takes -> $P/reel.json takes (references/take-selection.md) a scripted recording with many takes: rawcut_prep.sh -> vad_pauses.py -> islands_tx.py -> facescan.py --pts -> takes.py -> plan_cut.py -> subcheck.py -> assemble.py --plan -> voice_smooth.py --plan -> qa_cut.py: the clean raw cut every style builds on (take-selection.md) plan beats -> $P/reel.json beats/zoom (references/reel-json.md; taste.py prompt first) film_look.py / transitions.py reel optional: graded plate, transition windows (after takes are final) build.mjs -> $P/index.html + SFX table + rule and taste warnings approval_sheet.py -> build/approval.html lint, a frame at every cut and beat; WAIT for the user's yes own_footage.py -> the user's own clips for the lines (face-checked: only them); `sheet` -> show the picked frames, WAIT for yes broll_fetch.py -> $P/broll/: stock originals (Pexels by link, Mixkit search) + manifest.json; when to use which: references/broll.md 3 pictures: wikimedia_fetch.py (archival first) -> google_images.py; EN + RU, >= 1080 px, people-checked (references/broll.md 3) brand_kit.py pull --dir $P/brand -> a reel about a named tool/app/company: its real logo (light + dark), icon, brand colour, nearest Cyrillic Google font, people-checked product shots; LOOK at brand/sheet.jpg image_pick.py brief/pull/score/pick/sheet -> per line 6-10 candidates scored 0-5 (says the line at a glance >= 4 or FLAG); the scores sheet goes next to the preview insert_prep.py -> every B-roll insert: people + line check, cut, SDR, fps conform, graded onto the speaker's plate as shot (never the reverse) components.py plan/check -> new components per reel for a named style (catalog.json); record after the user's yes broll_vision.py picks -> build/broll-picks.jpg: every B-roll shot + stock image, its line, the people check; WAIT for the user's yes finals.py preview -> " (preview).mp4": 720x1280 at ~4.5 Mbit/s for the user's phone (+ a frame-0 still); WAIT for their OK finals.py render -> ".mp4": delivery master -> Instagram H.264 (refuses a reel with no current preview) finals.py qa -> render_report.py + broll_qa.py per reel, one summary line each; fix and re-render, at most 3 rounds qa.py -> SFX-vs-voice on a stem, A/V lengths, contact sheet finals.py deliver -> optional: -14 LUFS into the user's library of finished videos (CONTENT): NOT POSTED, _moves.tsv, TRACKER; taste.py log-reel; save_style.py if the user loved it ``` ### 0. Check the toolchain once per session `bash $S/scripts/doctor.sh`: tools, GEMINI_API_KEY and the media library, each with its fix. The Gemini key is needed for the B-roll checks (the free tier is enough), and doctor proves it with one test call: insert_prep.py, broll_vision.py and broll_qa.py's people / match checks ask Gemini, and with people rules set a shot nobody could check fails the QA. On a fresh machine run `python3 $S/scripts/library_restore.py` first (SFX, memes, music and the animated emoji aren't in git). Say what is missing; don't improvise around it. Lines marked `opt` are optional tools with their own `scripts/setup_.sh` (BiRefNet matte, audio QA, film-look LUTs, caption metrics); the core works without them. ### 1. Prep and look ```bash bash $S/scripts/prep.sh $P ffmpeg -v error -i $P/assets/talk.mp4 -vf "fps=1/10,scale=216:384,tile=6x3" -frames:v 1 -update 1 $P/build/sheet.jpg ``` Look at the sheet. Check that it is the right person (show the frames and confirm with the user when there is any doubt). Note where the face sits (the zoom origin, default `50% 29%`), where the chin is (the card band starts at y 990), and how bright the wall is. ### 2. Transcribe everything ```bash python3 $S/scripts/transcribe.py $P/assets/talk.mp4 --out $P/build/words.whisper.json --lang en ``` The table is the take map in original seconds; `*` marks restart phrases ("okay, again"). Fix mangled names later in `captions.fixes`. ### 2b. Track the face ```bash python3 $S/scripts/face_track.py $P/assets/talk.mp4 --out $P/build/face.json ``` Always. The build places captions, cards and reactions around the face, inside the platform's safe area. ### 2c. Noisy outdoor audio? Crickets, hum, wind, traffic: `bash $S/scripts/denoise.sh $P/assets/talk.mp4 $P/build/voice-clean.wav` (DeepFilterNet3, capped at 20 dB; full strength makes the voice thin and watery). Use the clean track for the cut and the captions. Band-limited voice (Bluetooth mic, a call, a downloaded clip): `voice_enhance.py IN OUT.wav --mode superres` (ClearerVoice; does nothing on full-band phone audio). Speech islands on noisy raw audio: `vad_pauses.py` / `autocut.py --vad` (silero-vad). Both run in the audio QA venv (`bash $S/scripts/setup_audioqa.sh` prints its python). ### 3. Choose takes (`references/take-selection.md`) Ask for the script if there is one. Pick the take of each sentence that is fluent and later, keep one-breath runs as one take, and cut on WORD times: `a` = first word - 0.10..0.15, `b` = last word + 0.20..0.30. When a meme will follow a line, end that take 0.6 to 1 s after the last word so the meme sound has air. Aim for 45 to 75 s. **A scripted recording with many takes:** the raw-cut tools (take-selection.md, "The raw cut, step by step"): `rawcut_prep.sh` makes the voice files and a frame-exact proxy, `islands_tx.py` transcribes each speech island on its own, `takes.py` finds every attempt at every script sentence with its face flags, `plan_cut.py` turns your picks (cut.json) into frame-exact pieces with trimmed pauses, blink-safe edges and keep/cover labels, `subcheck.py` catches merged restarts, `assemble.py --plan` + `voice_smooth.py --plan` make the plates and the voice, `qa_cut.py` checks the result. On Apple silicon, transcribe with mlx-whisper (`transcribe.py --backend mlx`). **Audit the rough cut before building.** Whisper merges restarts and false starts into one clean sentence and smears timestamps across them ("And even the… and even the thing that…"). Render the rough cut, re-transcribe THAT voice, and ask Gemini 2.5 Pro (analyze_video) for a verbatim audit: repeats, fillers, clipped words, breaths, look-aways. A long stretch of voice with few words in the transcript is a hidden retake. A throat-clear ("khm") shows up in a pitch scan as a short voiced hum with falling pitch; Gemini's sub-second timestamps are not reliable enough to cut on. Leave ~0.2 s of breath between sentences. ### 3b. A single clean take? If the recording is already one good take, don't pick takes by hand: `python3 $S/scripts/autocut.py $P/assets/talk.mp4 --noise -30` prints `takes` with the pauses removed. ### 4. Pick a style, plan beats (`references/styles.md`, `references/reel-json.md`) Seven presets: `clean`, `editorial`, `meme`, `cinematic`, `launch`, `kinetic`, `pop`. **Recipes** (custom builds, not reel.json presets; each has its own README, SYSTEM.md and scripts). Follow the README end to end: - `recipes/clean-os/`, **Clean OS**: Apple-clean word cards, wide/tight from one 4K camera, literal phone-UI scenes (date picker, lock-screen notifications, search + stalled loading bar, Photos memory), a red Figma selection box with a cursor. Call: "Clean OS". Talking heads, reflective or explanatory. - `recipes/explainer-vector/`, **Vector explainer**: no footage; an ElevenLabs narrator; one continuous SVG world a camera travels through, a subject that draws itself, serif headlines, word-reveal captions. Call: "animated explainer", "like the tree video". - `recipes/result-podcast/`, **RESULT**: warm podcast edit: one or two Inter Tight words with a two-tier emphasis, Caveat handwriting, navy dust cards, polaroids, a stop-motion paper stack, film-burn leaks, comic props, dissolves between lines. Call: "RESULT style". **Face RESULT** (its talking-head mode; `faceresult.py` + `example-face/`, README "Face RESULT"): the speaker's face as shot, paper props / polaroids / quote cards in zones beside, above or below the face measured from face.json (the build fails one that touches the face), big two-tier captions on a navy card, film grain + dust only, full-screen cards <= 2.5 s. Call: "Face RESULT", "RESULT on my face". - `recipes/whiteboard/`, **Whiteboard**: a split screen, a white marker board on the top half and the speaker's face on the bottom half as shot; each thought drawn live in rough marker strokes with a visible marker following the stroke, Caveat handwriting, an eraser between pages, white pill captions on the seam with the spoken word swept yellow; the board goes full screen only over a look-away (`board.py` + `example/reel.py`, a generator per reel keyed to words). Call: "Whiteboard", "a marker board edit". Read its SYSTEM.md first. - `recipes/pov-montage/`, **POV montage**: several first-person clips cut into a story with an edit list (speed-ups, freezes), the editorial motion layer, a freeze-frame moment with a meme. Call: "cut these POV clips". - `recipes/seoul-list/`, **Seoul list** (a format, not a footage source: the footage the user hands over first, then their library, then stock or AI full screen; one world conformed to the main footage as shot, no posed stock models, the people rules): a voice-over list of observations over 1.4 s B-roll shots (no talking head); one big word per line in Playfair Display Italic (mood, places) or Inter Tight 800 (facts) with a subline, white word flashes, white collage pages (shot shrinks into a card, red glitch word, photo card, Fluent stickers), triptychs, an Instagram comment card, a macOS frosted window swapping cut-outs, blue highlight words, an iMessage outro. Built from list.json + proxies + the VO (build.mjs). Call: "Seoul list", "a list reel over B-roll". Read its SYSTEM.md first. - `recipes/stan-style/`, **stan style** (NEEDS real screen recordings of the tool; without them say so and propose Clean OS or RESULT): a seated talking-head tool tutorial on the core pipeline (reel.json beats from `scripts/lib/stan.mjs`): `panel` (a screen recording in the top band, behind the speaker's BiRefNet cut-out; the speaker moves down only as far as their eyes need, once per run of panels), `chest` captions (small lowercase phrases at 68 % that cut on their first word; single words in the hook and the diagram, forehead words near the end), a `hook` card (icon rows + black-on-white strip title), one white `diagram` with drawn-on lines entered by a zoom-blur, `step` and `cta` labels under the chin, `aside`. Call: "stan style", "tool tutorial". Read its SYSTEM.md first. - `recipes/kinetic-doc/`, **Kinetic doc**: black-and-white archive documentary with one red accent: words pop on as spoken in one centred line (a big heavy word + small words), pictures arrive as prints, windows and polaroids, punch lines get their own page; works for a talking head, a voice-over with visuals and B-roll only. Not the `kinetic` preset. Call: "kinetic doc". Read its SYSTEM.md first. - `recipes/step-deck/`, **Step Deck**: a tips reel as a numbered deck over a talking head: every word lands on its onset behind the speaker's head, the picture switches every 1-2 s between the face and flat word cards, paper prints and line drawings, and each step opens on a card whose giant italic numeral shrinks into place; from @ant.lon. Call: "Step Deck", "steps reel". Read its SYSTEM.md first. - `recipes/product-page/`, **Product page**: a review or demo of a named tool, app or book laid out like a product page: white pages with the real picture, rebuilt UI and hairline drawings that a virtual camera moves through on the spoken words, the speaker's cut-out over a rounded footage card, two-tier headlines (heavy sans + italic serif), one spoken word at a time in a grey pill; from @adrien.ninet. Call: "Product page", "tool review page". Read its SYSTEM.md first. - `recipes/tactile-paper/`, **Tactile paper**: the talk told on printed paper that moves like stop-motion (8 fps): newspaper and archive sheets that unfurl and turn with a real page fold on a maroon ground, white arrows and a crimson marker, black pill captions, the paper turning away like a page to reveal the speaker's face; from @chrismoran__. Call: "Tactile paper", "paper page turn". Read its SYSTEM.md first. - `recipes/caption-news/`, **Caption News**: big chunked captions slammed on the spoken words with one keyword on a yellow block, a title sting that becomes the channel bug, a source tag and a highlighted document beat when the speaker names a source, the face as shot; ported from lemo-opuscar (MIT). Call: "Caption News", "news captions". Read its SYSTEM.md first. - `recipes/news-package/`, **News Package**: the speaker is the anchor of a one-story TV segment: a sting that flies into the corner as the bug, a chest strap with a category tab, a split-flap board, schematic cards and a rundown of the points ticked off at the end; ported from lemo-opuscar (MIT). Call: "News Package", "news segment". Read its SYSTEM.md first. - `recipes/telestrator/`, **Telestrator**: the footage freezes on the key word and gets drawn on like a sports replay (rings, marker arrows, zones, a rewind), always below or around the face, never on it; ported from lemo-opuscar (MIT). Call: "Telestrator", "sports breakdown", "freeze and draw". Read its SYSTEM.md first. - `recipes/halftone-dossier/`, **Halftone Dossier**: the story as a retro print case file: halftone pages in four flat inks, heavy misregistered headlines, stamps that slam, a profile card, the speaker's face as shot between pages; ported from lemo-opuscar (MIT). Call: "Halftone Dossier", "case file", "dossier". Read its SYSTEM.md first. - `recipes/swiss-grid/`, **Swiss Grid**: International Typographic Style around the speaker's face: a 6-column grid, one typeface, one red, key words laid out like a poster, a counter, exact quiet motion; ported from lemo-opuscar (MIT). Call: "Swiss Grid", "swiss poster". Read its SYSTEM.md first. - `recipes/data-story/`, **Data Story**: when a line carries a quantity a chart draws itself on cream paper with a red-blue pencil and handwritten notes; only real numbers, each with its source on screen; ported from lemo-opuscar (MIT). Call: "Data Story", "pencil charts". Read its SYSTEM.md first. - `recipes/hologram-hud/`, **Hologram HUD**: the thing the speaker talks about is scanned into a glowing wireframe, target boxes lock onto its parts and values roll in, cut against the face as shot, never over it; ported from lemo-opuscar (MIT). Call: "Hologram HUD", "hologram scan". Read its SYSTEM.md first. The visual inventory of every style (playing loops, soundboard, rules) is docs/PLAYBOOK.md. **Check the style's prerequisite before planning** (references/broll.md section 6): stan style needs real screen recordings, cinematic needs one consistent world of footage. If it is missing, say what is missing and propose another style; never fill the gap with stock. Seoul list has no footage prerequisite: it is a format (its README "Footage"). Ask the user which one (show `examples/gallery/` or the showcase video) or pick by the speaker's energy. Start from `examples/gallery/