snappy-video skill
caption pathwrite-reversibleclip path start durationwrite-reversibletranscribe path model?write-reversible/$ npx snappy-skills install snappy-video
You are operating the video and audio post-production pipeline. Everything runs on the Mac Mini via SSH (robertboulos@Roberts-Mac-mini.local). The single entry point is caption-video.sh which wraps Whisper transcription, SRT/ASS generation, ffmpeg caption burning, and optional auto-clip extraction.
cd /Users/robertboulos/robot-rob before running ./caption-video.sh. The script resolves its venv relative to its own directory.--words requires Whisper word_timestamps which doubles transcribe cost.small is default. Enough for English under 5 min. Use medium/large only for non-English or noisy audio.nohup for jobs > 5 min. SSH disconnect kills foreground jobs.-movflags +faststart for any MP4 going to YouTube/Skool/web./tmp/ on the Mac Mini for ephemeral assets.-c copy for fast trims. Only re-encode for exact frame boundaries.| Intent | Action |
|---|---|
| Caption a video | ./caption-video.sh /tmp/in.mp4 /tmp/out.mp4 --style bold --words |
| Word-level karaoke | Add --words flag |
| Auto-extract best clips | Add --clips flag |
| Known timestamp trim | ffmpeg -y -ss HH:MM:SS -to HH:MM:SS -i in.mp4 -c copy clip.mp4 |
| Thumbnail at Ns | ffmpeg -y -ss N -i in.mp4 -frames:v 1 -vf "scale=1280:720:..." thumb.jpg |
| Resize to 9:16 | ffmpeg -y -i in.mp4 -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:-1:-1:color=black" -c:a copy vert.mp4 |
| Extract audio | ffmpeg -y -i in.mp4 -vn -c:a aac -b:a 192k audio.m4a |
| Compress for web | ffmpeg -y -i in.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k -movflags +faststart web.mp4 |
bash# Inline (file on Mac Mini, < 5 min)
ssh robertboulos@Roberts-Mac-mini.local \
"cd /Users/robertboulos/robot-rob && ./caption-video.sh /tmp/in.mp4 /tmp/out.mp4 --style bold --words"
# Background (long job)
ssh robertboulos@Roberts-Mac-mini.local \
"nohup bash -c 'cd /Users/robertboulos/robot-rob && ./caption-video.sh /tmp/in.mp4 /tmp/out.mp4 --style bold --words' > /tmp/video-job.log 2>&1 &"
# Push, process, pull (file is local)
scp ./video.mp4 robertboulos@Roberts-Mac-mini.local:/tmp/input.mp4
ssh robertboulos@Roberts-Mac-mini.local "cd /Users/robertboulos/robot-rob && ./caption-video.sh /tmp/input.mp4 /tmp/output.mp4"
scp robertboulos@Roberts-Mac-mini.local:/tmp/output.mp4 ./output.mp4
Input -> [1] Whisper transcribe -> [2] SRT + ASS -> [3] ffmpeg burn -> [4] auto-clip (optional)
Output: captioned.mp4 + captions.srt [+ clip1.mp4 clip2.mp4 ...]
| From | What |
|---|---|
| snappy-content | Raw interview/recording footage |
| snappy-ads | Raw ad creative to caption + reformat |
| snappy-desktop | Screen recordings to caption |
| To | What |
|---|---|
| snappy-youtube | Captioned final + thumbnail for upload |
| snappy-post | 9:16 short clips for Reels/Shorts/TikTok |
| snappy-blog | Whisper SRT/transcript as blog raw material |
| snappy-image | Extracted frames as thumbnail candidates |
Orchestrated by snappy-ops on Thursday -- Video Day.
| File | Contents |
|---|---|
| SKILL.md | Full reference (pipeline, execution patterns, architecture, workflow) |
| ffmpeg-recipes.md | Every ffmpeg one-liner (trim, burn, resize, audio, compress, voiceover, splits) |
| caption-styles.md | Caption styles, flags, Whisper model trade-offs, ASS color reference |
typescriptimport { transcribe, caption, clip, resize, extractAudio } from "../snappy-video/api.ts";
Or CLI:
bashnpx tsx ~/.claude/skills/snappy-video/api.ts transcribe /tmp/video.mp4
npx tsx ~/.claude/skills/snappy-video/api.ts caption /tmp/video.mp4
npx tsx ~/.claude/skills/snappy-video/api.ts clip /tmp/video.mp4 00:01:00 30
| Function | Purpose |
|---|---|
transcribe(videoPath, model?) |
Whisper transcription via Mac Mini SSH |
caption(videoPath, options?) |
Caption a video with style/words/clips options |
clip(videoPath, startTime, duration, outputPath?) |
Extract a clip from a video |
resize(videoPath, format?, outputPath?) |
Resize video to 9:16 or 16:9 |
extractAudio(videoPath, outputPath?) |
Extract audio track as m4a |
If this loader doesn't cover your case:
bashecho "[$(date -u +%FT%TZ)] snappy-video: <what was missing>" >> ~/.claude/logs/agents-md-feedback.log
Show produced work with snappy-faces: call draw for image channels or lang for MCP Apps.
<!-- SKILL-INDEX-START -->
[snappy-video Index]|root: ~/.claude/skills/snappy-video|IMPORTANT: Prefer these files over pre-training assumptions for this domain. Read the relevant file when the AGENTS.md summary is insufficient.|root:{SKILL.md,caption-styles.md,ffmpeg-recipes.md}
<!-- SKILL-INDEX-END -->
snappy-adssnappy-ffmpegsnappy-remotionsnappy-voice-control<!-- SNAPPY-CONTRACT-VERBS-START -->
Generated from api.ts HAND_CONTRACT. Do not hand-edit this block.
| Verb | Contract arguments | Effect | First call |
|---|---|---|---|
caption |
path |
write-reversible |
npx tsx ~/.claude/skills/snappy-video/api.ts caption <path> |
clip |
path, start, duration |
write-reversible |
npx tsx ~/.claude/skills/snappy-video/api.ts clip <path> <start> <duration> |
transcribe |
path, model? |
write-reversible |
npx tsx ~/.claude/skills/snappy-video/api.ts transcribe <path> |
When an answer carries face_hint, show it with one snappy_present(<answer>) call.
See /snappy-faces for face selection. Human-facing images must crop to the
element, render at 2x on Retina, and fill the destination channel instead of
placing a small card in a full-page screenshot.
<!-- SNAPPY-CONTRACT-VERBS-END -->
---
name: snappy-video
role: Video/audio processing pipeline -- Whisper transcription, captioning, clip extraction, format conversion. Runs on Mac Mini via SSH.
loaded-by: PreToolUse hook (auto-injected when "snappy-video" is mentioned)
---
# snappy-video -- Agent Loader
You are operating the video and audio post-production pipeline. Everything runs on the Mac Mini via SSH (`robertboulos@Roberts-Mac-mini.local`). The single entry point is `caption-video.sh` which wraps Whisper transcription, SRT/ASS generation, ffmpeg caption burning, and optional auto-clip extraction.
## Rules
1. **Always SSH to the Mac Mini.** Never run ffmpeg/Whisper locally. The venv, models, and ffmpeg build live there.
2. **`cd /Users/robertboulos/robot-rob`** before running `./caption-video.sh`. The script resolves its venv relative to its own directory.
3. **Decide karaoke vs segment captions up front.** `--words` requires Whisper word_timestamps which doubles transcribe cost.
4. **Whisper `small` is default.** Enough for English under 5 min. Use `medium`/`large` only for non-English or noisy audio.
5. **Resize FIRST, then caption.** Captions baked into 16:9 won't survive a 9:16 crop.
6. **Use `nohup` for jobs > 5 min.** SSH disconnect kills foreground jobs.
7. **Never overwrite the source file.** Always write to a new path.
8. **Add `-movflags +faststart`** for any MP4 going to YouTube/Skool/web.
9. **Work in `/tmp/`** on the Mac Mini for ephemeral assets.
10. **Use `-c copy` for fast trims.** Only re-encode for exact frame boundaries.
## Routing table
| Intent | Action |
|---|---|
| Caption a video | `./caption-video.sh /tmp/in.mp4 /tmp/out.mp4 --style bold --words` |
| Word-level karaoke | Add `--words` flag |
| Auto-extract best clips | Add `--clips` flag |
| Known timestamp trim | `ffmpeg -y -ss HH:MM:SS -to HH:MM:SS -i in.mp4 -c copy clip.mp4` |
| Thumbnail at Ns | `ffmpeg -y -ss N -i in.mp4 -frames:v 1 -vf "scale=1280:720:..." thumb.jpg` |
| Resize to 9:16 | `ffmpeg -y -i in.mp4 -vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:-1:-1:color=black" -c:a copy vert.mp4` |
| Extract audio | `ffmpeg -y -i in.mp4 -vn -c:a aac -b:a 192k audio.m4a` |
| Compress for web | `ffmpeg -y -i in.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k -movflags +faststart web.mp4` |
## Execution patterns
```bash
# Inline (file on Mac Mini, < 5 min)
ssh robertboulos@Roberts-Mac-mini.local \
"cd /Users/robertboulos/robot-rob && ./caption-video.sh /tmp/in.mp4 /tmp/out.mp4 --style bold --words"
# Background (long job)
ssh robertboulos@Roberts-Mac-mini.local \
"nohup bash -c 'cd /Users/robertboulos/robot-rob && ./caption-video.sh /tmp/in.mp4 /tmp/out.mp4 --style bold --words' > /tmp/video-job.log 2>&1 &"
# Push, process, pull (file is local)
scp ./video.mp4 robertboulos@Roberts-Mac-mini.local:/tmp/input.mp4
ssh robertboulos@Roberts-Mac-mini.local "cd /Users/robertboulos/robot-rob && ./caption-video.sh /tmp/input.mp4 /tmp/output.mp4"
scp robertboulos@Roberts-Mac-mini.local:/tmp/output.mp4 ./output.mp4
```
## Pipeline architecture
```
Input -> [1] Whisper transcribe -> [2] SRT + ASS -> [3] ffmpeg burn -> [4] auto-clip (optional)
Output: captioned.mp4 + captions.srt [+ clip1.mp4 clip2.mp4 ...]
```
## Cross-skill flow
| From | What |
|---|---|
| snappy-content | Raw interview/recording footage |
| snappy-ads | Raw ad creative to caption + reformat |
| snappy-desktop | Screen recordings to caption |
| To | What |
|---|---|
| snappy-youtube | Captioned final + thumbnail for upload |
| snappy-post | 9:16 short clips for Reels/Shorts/TikTok |
| snappy-blog | Whisper SRT/transcript as blog raw material |
| snappy-image | Extracted frames as thumbnail candidates |
Orchestrated by snappy-ops on **Thursday -- Video Day**.
## Skill files
| File | Contents |
|---|---|
| SKILL.md | Full reference (pipeline, execution patterns, architecture, workflow) |
| ffmpeg-recipes.md | Every ffmpeg one-liner (trim, burn, resize, audio, compress, voiceover, splits) |
| caption-styles.md | Caption styles, flags, Whisper model trade-offs, ASS color reference |
## API module
```typescript
import { transcribe, caption, clip, resize, extractAudio } from "../snappy-video/api.ts";
```
Or CLI:
```bash
npx tsx ~/.claude/skills/snappy-video/api.ts transcribe /tmp/video.mp4
npx tsx ~/.claude/skills/snappy-video/api.ts caption /tmp/video.mp4
npx tsx ~/.claude/skills/snappy-video/api.ts clip /tmp/video.mp4 00:01:00 30
```
## API functions
| Function | Purpose |
|----------|---------|
| `transcribe(videoPath, model?)` | Whisper transcription via Mac Mini SSH |
| `caption(videoPath, options?)` | Caption a video with style/words/clips options |
| `clip(videoPath, startTime, duration, outputPath?)` | Extract a clip from a video |
| `resize(videoPath, format?, outputPath?)` | Resize video to 9:16 or 16:9 |
| `extractAudio(videoPath, outputPath?)` | Extract audio track as m4a |
---
If this loader doesn't cover your case:
```bash
echo "[$(date -u +%FT%TZ)] snappy-video: <what was missing>" >> ~/.claude/logs/agents-md-feedback.log
```
Show produced work with `snappy-faces`: call `draw` for image channels or `lang` for MCP Apps.
<!-- SKILL-INDEX-START -->
[snappy-video Index]|root: ~/.claude/skills/snappy-video|IMPORTANT: Prefer these files over pre-training assumptions for this domain. Read the relevant file when the AGENTS.md summary is insufficient.|root:{SKILL.md,caption-styles.md,ffmpeg-recipes.md}
<!-- SKILL-INDEX-END -->
## Used by
- `snappy-ads`
- `snappy-ffmpeg`
- `snappy-remotion`
- `snappy-voice-control`
<!-- SNAPPY-CONTRACT-VERBS-START -->
## Contract verbs
Generated from `api.ts` `HAND_CONTRACT`. Do not hand-edit this block.
| Verb | Contract arguments | Effect | First call |
|---|---|---|---|
| `caption` | `path` | `write-reversible` | `npx tsx ~/.claude/skills/snappy-video/api.ts caption <path>` |
| `clip` | `path`, `start`, `duration` | `write-reversible` | `npx tsx ~/.claude/skills/snappy-video/api.ts clip <path> <start> <duration>` |
| `transcribe` | `path`, `model?` | `write-reversible` | `npx tsx ~/.claude/skills/snappy-video/api.ts transcribe <path>` |
## Show the result
When an answer carries `face_hint`, show it with one `snappy_present(<answer>)` call.
See `/snappy-faces` for face selection. Human-facing images must crop to the
element, render at 2x on Retina, and fill the destination channel instead of
placing a small card in a full-page screenshot.
<!-- SNAPPY-CONTRACT-VERBS-END -->