Install
$ agentstack add skill-kurbaitaev-ghost-editor-ghost-editor ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
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.
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)
plan beats -> $P/reel.json beats/zoom (references/reel-json.md)
build.mjs -> $P/index.html + SFX table + rule warnings
lint + snapshot, look at the frames
render -> $P/build/reel.mp4
qa.py -> levels, SFX-vs-voice on a stem, silences, contact sheet
deliver -> ~/Desktop/-reel.mp4
0. Check the toolchain once per session
bash $S/scripts/doctor.sh: tools, GEMINIAPIKEY and the media library, each with its fix. On a fresh machine run python3 $S/scripts/library_restore.py first (SFX, memes and music aren't in git). Say what is missing; don't improvise around it.
1. Prep and look
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
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
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.
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.
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. Ask the user which one (show examples/gallery/ or the showcase video) or pick by the speaker's energy. Start from examples/gallery/.reel.json. To match a reference edit: python3 $S/scripts/reference_study.py --out [--raw ] writes a 1 fps contact sheet, full-res frames every 2 s and Gemini 2.5 Pro's second-by-second edit log with a mapping onto our beats. Treat the log as a draft: verify type, sizes and framing against the frames. Then map every scene to a word time. Start from the closest file in examples/. Walk the transcript sentence by sentence. For each one, ask what the speaker just made the viewer imagine, and show it on the word. Leave some sentences bare; silence in the band makes the next card land. Memes: 1 to 3 per minute, on punchlines, from the library:
python3 $S/scripts/meme_find.py "the line with the punchline"
python3 $S/scripts/meme_find.py --list
If the library has nothing that fits, say so and offer to add one (step 9). Don't force a meme.
5. Build
node $S/scripts/build.mjs $P
Read its output: the edit timeline (a missing word means a cut inside it), the caption text, the SFX table (time, sound, role, volume, why) and the WARNINGS. Fix every warning in reel.json or explain why it stays.
6. Lint and look
cd $P && npx hyperframes lint # must be 0 errors; sub-composition/track-density warnings are fine
npx hyperframes snapshot --at
Edit seconds = the take's edit start (printed) + (original t - take a). Look at snapshots/contact-sheet.jpg: cards on the chin, text wrapping, the meme clipped by the frame edge, captions colliding with a card.
7. Render and QA
npx hyperframes render -o build/reel.mp4 --quiet
python3 $S/scripts/qa.py $P build/reel.mp4
QA renders an SFX-only stem and prints each hit's peak against the voice and its role target. Targets: ui ≈ -14, whoosh -12, impact -8, meme -3 dB vs voice p95 peak; flags anything louder than the voice or inaudible. It also checks loudness (-16 ±1.5 LUFS, TP ≤ -0.5), silences > 0.8 s inside the speech, and writes build/contact.jpg. It never adds gain.
8. Deliver
Copy to ~/Desktop/-reel.mp4 (plus a 720p preview under 30 MB if asked). Handover: the takes chosen (original seconds and why), the beats and memes in plain words, the SFX count per minute, anything QA flagged, and the obvious follow-ups (music from the platform's library at post time, a different meme, the URL for the end card).
9. Library work
python3 $S/scripts/trends.py # what's new on imgflip / Know Your Meme, what we lack
python3 $S/scripts/meme_add.py --tags ... --use ... --triggers ... --trend hot
python3 $S/scripts/sfx_fetch.py --only --force # after editing KIT in sfx_fetch.py
python3 $S/scripts/audition.py --open # soundboard + meme wall for a human listen
Always look at a new meme's preview.png before using it. Sounds with a review flag in the manifest have not been confirmed by ear. references/sound-and-memes.md has the mixing reasoning, sources, licences and the dead ends not to retry.
Rules that came from getting it wrong
- Transcribe the whole recording; choose takes from the table, never by scrubbing.
- Cut on word times, never whisper segment times.
- Snap zooms, not crossfades, between takes of the same sentence.
- Voice loudness is fixed in prep, before SFX. Never lift the finished mix.
- SFX level is relative to this voice (build measures it). Never hand-tune
data-volume. - A meme sound never sits on top of a word. Give it a pause.
- No meme sound twice in one reel. Meme sounds: 1 to 3 per minute.
- One card in the band and one thing in the reaction slot at a time.
- No em dashes in on-screen text.
- Meme sounds and green-screen clips are unlicensed rips: fine for IG and
TikTok, not for YouTube (Content ID). Say so if the user mentions YouTube.
Files
scripts/prep.shtranscode + voice loudnormscripts/transcribe.pywhisper (PATH) → words json + take tablescripts/build.mjsreel.json → HyperFrames index.html, SFX mixing and rulesscripts/qa.pyrender QA with SFX stem level checkscripts/sfx_fetch.pybuild/refresh the SFX kit (Mixkit + yt-dlp), normalized, manifestscripts/meme_add.pyadd a meme clip/still (key, crop, alpha WebM, preview)scripts/meme_find.pyshortlist memes and meme sounds for a linescripts/trends.pytrending meme names not yet in the libraryscripts/audition.pyHTML soundboard + meme wallscripts/doctor.shchecks tools, key and library, with fixesscripts/reference_study.pyreverse-engineer a reference edit (sheet, frames, Gemini edit log)references/reference-study-prompt.mdthe edit-log prompt (improve it here)examples/real reel.json files from shipped reels (motion EN, motion RU, memes RU)README.mdinstall, agent setup (Claude Code / Codex / any agent), quick startscripts/lib/motion.mjsscenes (card, stats, fly3d, image, sentence), scene transitions, editorial captions, music bedscripts/autocut.pypause-trimmed takes for a single clean takescripts/broll_gen.pyAI still B-roll with Gemini (9:16), needs GEMINIAPIKEYlibrary/music/licensed music beds + manifestlibrary/sfx/kit + manifest.json;library/memes//clips + meta.jsontemplates/fonts/Geist (OFL)references/reel-json.mdthe spec and beat typesreferences/take-selection.mdchoosing takes (adapted from mariagorskikh/talking-head-reel, MIT)references/sound-and-memes.mdmixing, sources, licences, dead ends
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: kurbaitaev
- Source: kurbaitaev/ghost-editor
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.