video-cut — local-first raw-footage video editor
Drop raw clips in a folder → describe the cut → get edit/final.mp4. Fully local. The
editorial counterpart to our generative video skills (launch-video, Remotion,
cloned-voice-pitch-pipeline). Compounds on browser-use/video-use:
same two-layer reading architecture, but local ASR instead of cloud ElevenLabs Scribe.
Core principle — two-layer reading (never dump frames)
The agent reads video through two cheap layers, not by watching frames:
- Transcript layer —
transcribe_local.py runs faster_whisper with word-level
timestamps (local, MPS/CPU). Packed into takes_packed.md (~tens of KB) — the primary
reading artifact. This is the routing projection.
- Visual layer (on-demand) —
timeline_view.py <video> <start> <end> renders a
filmstrip + waveform + word-label PNG only at decision points (ambiguous pauses,
retake comparisons, cut-point checks). Never a scan — the body-grep expansion.
This is research/entities/pattern/llm-as-index-architecture.md applied to the video
modality: raw frames = the substrate you never dump; transcript = the projection that
routes; timeline_view = on-demand expansion. (See references/local-asr.md.)
Pipeline
Transcribe (local) → Pack → LLM reasons (proposes plain-English strategy, waits) →
EDL → Render (ffmpeg) → Self-eval (≤3 correction loops) → final.mp4
The EDL (edl.json) is the declarative cut IR — cut ranges + grade + overlays +
subtitles in one file. Decouples decision (the agent) from render (ffmpeg). Full
schema in references/edl-format.md.
Hard Rules (non-negotiable production correctness)
These are inherited from video-use's hard-won list. See references/hard-rules.md for the
ffmpeg specifics. Summary:
- Subtitles applied LAST in the filter chain, after every overlay.
- Per-segment extract → lossless concat (
-c copy). Never double-encode.
- 30 ms audio fades (
afade) at every cut — no audible pops.
- Snap cuts to word boundaries — never cut inside a word; use transcript timestamps.
- Cut padding 30–200 ms absorbs ASR drift.
- Word-level verbatim ASR only — never phrase-mode or normalized fillers.
- Cache transcripts per source (by content hash) — never re-transcribe unchanged input.
- Caption output-timeline offsets —
out = word.start - range.start + range_offset.
- Output isolation — all session files go to
<videos_dir>/edit/, never elsewhere.
- Strategy approval — confirm a plain-English plan before touching the cut.
Scripts
| Script / Library |
Purpose |
scripts/transcribe_local.py <video> [--model base] [--diarize] |
Local faster_whisper word-level transcript → edit/transcripts/<name>.json (cached by source hash + settings) |
scripts/pack_transcripts.py --edit-dir <dir> |
All transcripts/*.json → edit/takes_packed.md (phrase-level; breaks on silence ≥0.5 s or speaker change) |
scripts/timeline_view.py <video> <start> <end> [-o out.png] |
Filmstrip + waveform + word-label PNG for one range → edit/verify/ |
scripts/render.py <edl.json> [-o out.mp4] [--preview] |
EDL → ffmpeg: per-segment extract+grade+fades → lossless concat → overlays → subtitles (LAST) |
scripts/self_eval.py <edl.json> <rendered.mp4> |
Inspect render at each cut boundary; report discontinuities/pops/hidden-subs as JSON |
scripts/edl.py |
Shared lib: EDL load/validate, SRT generation, output-timeline offset math (imported by render/self_eval/tests) |
Directory layout (created under <videos_dir>/edit/)
<videos_dir>/
├── <source clips>
└── edit/
├── project.md # session memory (Strategy / Decisions / Outstanding)
├── takes_packed.md # phrase-level transcript (primary LLM input)
├── edl.json # cut decisions + grade + overlays + subtitles
├── transcripts/<name>.json # cached word-level transcript
├── clips_graded/seg_NNN.mp4 # per-segment extracts (grade + 30ms fades)
├── animations/slot_<id>/ # per-overlay source + render (Remotion/HyperFrames/PIL)
├── master.srt # output-timeline subtitles
├── verify/ # timeline_view PNGs + self-eval frames
├── preview.mp4
└── final.mp4
Workflow
- Inventory —
ffprobe sources; transcribe_local.py each (or batch); pack_transcripts.py.
- Pre-scan — read
takes_packed.md; flag filler/false-starts/retakes.
- Converse — shape, content type, pacing, grade, subtitle style, overlays.
- Propose strategy — 4–8 sentences; wait for confirmation (Hard Rule 10).
- Build EDL — author
edl.json; spawn parallel overlay sub-agents if needed (Remotion etc.).
- Render —
render.py edl.json --preview first.
- Self-eval —
self_eval.py before showing the user; correct, ≤3 passes.
- Iterate — natural-language feedback → re-render; append to
project.md.
Composition map
| Need |
Skill |
| Overlay animations (kinetic type, UI, charts) |
Remotion (PAI) / HyperFrames / PIL |
| Cinematic generated launch video |
launch-video (broomva) — generative, Liquid Glass |
| AI-generated B-roll / frames |
content-creation (Imagen/Veo) |
| Audio-only cleanup |
AudioEditor (PAI) |
| Narrated pitch from text + cloned voice |
cloned-voice-pitch-pipeline (OmniVoice → Remotion) |
| Local word-level ASR backbone |
faster_whisper (this skill) |
video-cut edits; the others generate. They share the EDL as a future common IR
(overlays produced by Remotion are referenced as EDL overlays[]).
Requirements
ffmpeg + ffprobe (required), faster_whisper (required, local ASR), yt-dlp (optional,
for --download). Install: uv pip install -r requirements.txt. See references/local-asr.md.
Anti-patterns (from video-use, confirmed)
- Dumping frames to the model (the 45M-token mistake) — use the two-layer reading.
- Phrase-level transcription (loses sub-second gaps) — word-level verbatim only.
- Burning subtitles before overlay composition — subtitles LAST.
- Single filtergraph that re-encodes everything twice — extract→lossless-concat→one final pass.
- Hard audio cuts at boundaries — 30 ms fades.
- Editing before strategy confirmation.
- Re-transcribing cached sources.
1---2name: video-cut3description: Edit raw footage into a finished cut by conversation, fully local. Drop clips in a folder, describe the video you want, get edit/final.mp4 back. Local-first raw-footage video editor — the editorial counterpart to the generative Remotion/launch-video skills. Uses a two-layer reading system (local faster_whisper word-level transcript + on-demand timeline_view composite) so the agent cuts with word-boundary precision without ever dumping video frames — llm-as-index applied to video. Swaps cloud ElevenLabs Scribe for local faster_whisper: free at any volume, nothing leaves the machine, no API key. Removes filler/dead-air, color-grades per segment, burns subtitles, composes overlay animations via Remotion, and self-evaluates the render before showing you. USE WHEN: edit this footage, cut these clips, make a video from this raw footage, remove filler words from video, trim this recording into a video, edit talking head, montage, tutorial cut, interview edit, turn these clips into a launch video. NOT FOR generating 4license: MIT5---67# video-cut — local-first raw-footage video editor89Drop raw clips in a folder → describe the cut → get `edit/final.mp4`. Fully local. The10**editorial** counterpart to our **generative** video skills (`launch-video`, `Remotion`,11`cloned-voice-pitch-pipeline`). Compounds on [`browser-use/video-use`](https://github.com/browser-use/video-use):12same two-layer reading architecture, but local ASR instead of cloud ElevenLabs Scribe.1314## Core principle — two-layer reading (never dump frames)1516The agent reads video through two cheap layers, not by watching frames:17181. **Transcript layer** — `transcribe_local.py` runs `faster_whisper` with word-level19 timestamps (local, MPS/CPU). Packed into `takes_packed.md` (~tens of KB) — the primary20 reading artifact. This is the *routing projection*.212. **Visual layer (on-demand)** — `timeline_view.py <video> <start> <end>` renders a22 filmstrip + waveform + word-label PNG **only at decision points** (ambiguous pauses,23 retake comparisons, cut-point checks). Never a scan — the *body-grep* expansion.2425This is `research/entities/pattern/llm-as-index-architecture.md` applied to the video26modality: raw frames = the substrate you never dump; transcript = the projection that27routes; `timeline_view` = on-demand expansion. (See `references/local-asr.md`.)2829## Pipeline3031```32Transcribe (local) → Pack → LLM reasons (proposes plain-English strategy, waits) →33EDL → Render (ffmpeg) → Self-eval (≤3 correction loops) → final.mp434```3536The **EDL** (`edl.json`) is the declarative cut IR — cut ranges + grade + overlays +37subtitles in one file. Decouples *decision* (the agent) from *render* (ffmpeg). Full38schema in `references/edl-format.md`.3940## Hard Rules (non-negotiable production correctness)4142These are inherited from video-use's hard-won list. See `references/hard-rules.md` for the43ffmpeg specifics. Summary:44451. **Subtitles applied LAST** in the filter chain, after every overlay.462. **Per-segment extract → lossless concat** (`-c copy`). Never double-encode.473. **30 ms audio fades** (`afade`) at every cut — no audible pops.484. **Snap cuts to word boundaries** — never cut inside a word; use transcript timestamps.495. **Cut padding 30–200 ms** absorbs ASR drift.506. **Word-level verbatim ASR only** — never phrase-mode or normalized fillers.517. **Cache transcripts per source** (by content hash) — never re-transcribe unchanged input.528. **Caption output-timeline offsets** — `out = word.start - range.start + range_offset`.539. **Output isolation** — all session files go to `<videos_dir>/edit/`, never elsewhere.5410. **Strategy approval** — confirm a plain-English plan before touching the cut.5556## Scripts5758| Script / Library | Purpose |59|---|---|60| `scripts/transcribe_local.py <video> [--model base] [--diarize]` | Local faster_whisper word-level transcript → `edit/transcripts/<name>.json` (cached by source hash + settings) |61| `scripts/pack_transcripts.py --edit-dir <dir>` | All `transcripts/*.json` → `edit/takes_packed.md` (phrase-level; breaks on silence ≥0.5 s or speaker change) |62| `scripts/timeline_view.py <video> <start> <end> [-o out.png]` | Filmstrip + waveform + word-label PNG for one range → `edit/verify/` |63| `scripts/render.py <edl.json> [-o out.mp4] [--preview]` | EDL → ffmpeg: per-segment extract+grade+fades → lossless concat → overlays → subtitles (LAST) |64| `scripts/self_eval.py <edl.json> <rendered.mp4>` | Inspect render at each cut boundary; report discontinuities/pops/hidden-subs as JSON |65| `scripts/edl.py` | Shared lib: EDL load/validate, SRT generation, output-timeline offset math (imported by render/self_eval/tests) |6667## Directory layout (created under `<videos_dir>/edit/`)6869```70<videos_dir>/71├── <source clips>72└── edit/73 ├── project.md # session memory (Strategy / Decisions / Outstanding)74 ├── takes_packed.md # phrase-level transcript (primary LLM input)75 ├── edl.json # cut decisions + grade + overlays + subtitles76 ├── transcripts/<name>.json # cached word-level transcript77 ├── clips_graded/seg_NNN.mp4 # per-segment extracts (grade + 30ms fades)78 ├── animations/slot_<id>/ # per-overlay source + render (Remotion/HyperFrames/PIL)79 ├── master.srt # output-timeline subtitles80 ├── verify/ # timeline_view PNGs + self-eval frames81 ├── preview.mp482 └── final.mp483```8485## Workflow86871. **Inventory** — `ffprobe` sources; `transcribe_local.py` each (or batch); `pack_transcripts.py`.882. **Pre-scan** — read `takes_packed.md`; flag filler/false-starts/retakes.893. **Converse** — shape, content type, pacing, grade, subtitle style, overlays.904. **Propose strategy** — 4–8 sentences; wait for confirmation (Hard Rule 10).915. **Build EDL** — author `edl.json`; spawn parallel overlay sub-agents if needed (Remotion etc.).926. **Render** — `render.py edl.json --preview` first.937. **Self-eval** — `self_eval.py` before showing the user; correct, ≤3 passes.948. **Iterate** — natural-language feedback → re-render; append to `project.md`.9596## Composition map9798| Need | Skill |99|---|---|100| Overlay animations (kinetic type, UI, charts) | **Remotion** (PAI) / HyperFrames / PIL |101| Cinematic generated launch video | **launch-video** (broomva) — generative, Liquid Glass |102| AI-generated B-roll / frames | **content-creation** (Imagen/Veo) |103| Audio-only cleanup | **AudioEditor** (PAI) |104| Narrated pitch from text + cloned voice | **cloned-voice-pitch-pipeline** (OmniVoice → Remotion) |105| Local word-level ASR backbone | `faster_whisper` (this skill) |106107video-cut **edits**; the others **generate**. They share the EDL as a future common IR108(overlays produced by Remotion are referenced as EDL `overlays[]`).109110## Requirements111112`ffmpeg` + `ffprobe` (required), `faster_whisper` (required, local ASR), `yt-dlp` (optional,113for `--download`). Install: `uv pip install -r requirements.txt`. See `references/local-asr.md`.114115## Anti-patterns (from video-use, confirmed)116117- Dumping frames to the model (the 45M-token mistake) — use the two-layer reading.118- Phrase-level transcription (loses sub-second gaps) — word-level verbatim only.119- Burning subtitles before overlay composition — subtitles LAST.120- Single filtergraph that re-encodes everything twice — extract→lossless-concat→one final pass.121- Hard audio cuts at boundaries — 30 ms fades.122- Editing before strategy confirmation.123- Re-transcribing cached sources.