cut-silences
Agent 1 of the video editing pipeline. Removes silences and dead air from a talking-head recording. Use when asked to cut silences, trim pauses, remove dead air / gaps, or tighten the pacing of a raw video, given a word-level transcript. Produces an edit list (EDL), a re-timed transcript for downstream agents, and optionally the cut video via ffmpeg. Does NOT cut mistakes, repeats, or false starts — that is the cut-mistakes agent.
1How It Works & Mechanism
The entire pipeline relies on a deterministic algorithm rather than model judgment: it only processes head/tail dead air and inter-word pauses exceeding --gap, without altering a single word.
Pure pause/silence trimmer. Takes any word-level transcript (ElevenLabs Scribe … shape, or { words: [{ text, start, end }] }) and removes silences only:
Head and tail dead air are trimmed directly according to --head-pad / --tail-pad and merged as two independent deletion ranges.
// head + tail dead air const headEnd = words[0].start - opts.headPad; if (headEnd > 0) deleteRanges.push({ start: 0, end: headEnd, reason: "head trim before first word" });
Inter-word pauses are not cut down to 0; instead, a context-dependent 'natural breath' buffer is retained: 0.24s for long pauses, 0.20s for sentence ends, and 0.14s for others.
if (gap < opts.gap) continue; … const sentenceBreak = /[.!?]$/.test(prev.text ?? ""); const keep = gap >= 2 ? 0.24 : sentenceBreak ? 0.2 : 0.14;
Retained breath intervals are intentionally biased toward the beginning of the following sentence: 55% remains at the end of the previous sentence, and 45% at the start of the next.
// Bias the kept breath slightly toward the end of the previous phrase. const delStart = prev.end + keep * 0.55; const delEnd = next.start - keep * 0.45;
Deletion ranges are merged first before computing the complement to obtain keep ranges, discarding fragment intervals smaller than 40ms.
if (cursor < duration) keepRanges.push({ start: cursor, end: duration }); const usableKeepRanges = keepRanges.filter((r) => r.end - r.start >= 0.04);
The transcript is re-timed to the edited timeline using the same deletion list, preserving source_start/source_end for cross-referencing with the original footage.
const editedWords = []; for (const word of words) { if (inDeleted(word)) continue; … start: Number((word.start - removedBefore(word.start)).toFixed(3)),
Rendering uses an ffmpeg trim/atrim + concat filtergraph, written first to *.silence-filter.txt and supplied via -/filter_complex; by default, it calculates without rendering.
lines.push(`[0:v]trim=start=${r.start.toFixed(3)}:end=${r.end.toFixed(3)},setpts=PTS-STARTPTS[v${i}];`); … const ffArgs = [ "-y", "-i", videoPath, "-/filter_complex", scriptPath, "-map", "[v]", "-map", "[a]", "-c:v", "libx264", "-preset", "medium", "-crf", "18", "-c:a", "aac", "-b:a", "192k", "-movflags", "+faststart", outputPath, ];
2Core Capabilities
3External Dependencies
| Kind | Dependency / Endpoint |
|---|---|
| cli | Node.js(跑 cut-silences.mjs) |
| cli | ffmpeg(trim/atrim + concat 重编码,仅 --apply 时执行) |
| cli | ffprobe(无 audio_duration_secs 时探源片时长) |
| api | ElevenLabs Scribe(**前置步骤**:由仓库级 scripts/transcribe-elevenlabs.mjs 上传音频取词级时间戳;本次侦察未在本 skill 目录内发现任何调用代码) |
| cli | 本地 Whisper / whisper.cpp(文档列出的无 key 替代转录路线) |
4Recommended Governance & Evidence
- scripts/: Execute local script or command: node .claude/skills/cut-silences/scripts/cut-silences.mjs <transcript.json>
- 网络端点: Declared external network access: External endpoint used in prerequisite step (from repo-level helper, not this directory): api.elevenlabs.io/v1/speech-to-text (POST audio to fetch word-level transcript)
- 本地工作目录: File I/O and artifact output: <stem>.silence-edl.json (keep/delete ranges + params + duration)
5Second-Pass Verification
- [ok] 目录文件清单与 Outputs 表对照 — 目录仅 SKILL.md + scripts/cut-silences.mjs;SKILL.md 列出的 5 类产物中 3 类无条件写、filter.txt 与 mp4 受 --video/--apply 门控,与源码 writeFileSync/spawnSync 分支一致。
- [ok] 网络面反例搜索 — 目录内 0 处 fetch/URL/网络模块导入;SKILL.md 出现的 `node scripts/transcribe-elevenlabs.mjs ...` 是仓库级前置命令,文件名不含 http,且未被本脚本引用。
- [ok] 凭证面反例搜索 — 0 处 process.env/API_KEY/TOKEN/.env;脚本对 process 的导入仅 argv/exit(`import { argv, exit } from "node:process";`)。
- [ok] 「只删沉默」是否被代码强制 — 删除区间只由 head/tail pad 与 gap>opts.gap 的停顿生成三处 push 产生,源码无重复词/相似度/语义判断,与文档声明一致,无夸大。
- [ok] 重定时映射正确性 — start = word.start - removedBefore(word.start),removedBefore 只累加严格位于其前的删除量,因此不会出现负值或跳变;edited_duration = duration - removedTotal 与被删总量自洽。
- [ok] 文档参数 vs 脚本 argv — --video/--out-dir/--output/-o/--gap/--head-pad/--tail-pad/--apply 全部在 argv 解析中定位到,未知 --flag 会 die,与 SKILL.md 表格一致。
6Verdict & Scenarios
484001b000655549…