ScrappyLabs · Skill Brief

/watch — give Claude eyes on video

Paste a video URL or a file path, ask a question. Claude downloads it, looks at the frames, reads the timestamped transcript, and answers from what it actually saw and heard — not from the title.

v1.3.0 — current MIT · open source Claude Code · claude.ai · Codex github.com/scrappylabsai/watch

The one-line pitch

Claude can read a page, run a script, browse a repo. It cannot watch a video. This gives it that sense — and it's the single highest-leverage skill we run, because so much of what matters (competitor launches, ad creative, bug repros, conference talks, YouTube tutorials) only exists as video.

01Install

SurfaceInstall
Claude Code
recommended
/plugin marketplace add scrappylabsai/watch
/plugin install watch@watch
claude.ai (web) Download watch.skill → Settings → Capabilities → Skills → +
Codex git clone https://github.com/scrappylabsai/watch.git ~/.codex/skills/watch
Manual / dev git clone https://github.com/scrappylabsai/watch.git ~/.claude/skills/watch

Zero config to start. First run preflights itself: ffmpeg and yt-dlp auto-install via brew on macOS (Linux/Windows print the exact commands). Native captions cover most public videos for free — transcription only kicks in when a video has none.

Quick start

/watch https://youtu.be/dQw4w9WgXcQ what happens at the 30 second mark?

/watch bug-repro.mov what's going wrong here?

/watch https://youtu.be/<long-talk> summarize this

02What's new

1.3.0
Scene-aware frame selection biggest win

Frames are now chosen by content, not by clock. One ffmpeg pass keeps every real scene change, plus a density floor so slow screencasts still get covered, then a sliding-window pixel dedup drops shots Claude has already seen. A static slide collapses to one frame; a fast-cut reel keeps every beat. Net effect: a 27-second screencast went from 27 frames to ~10 with nothing lost — 2–3× cheaper on static content, and it now catches cuts that land between whole-second sample points. Falls back to the old fixed-interval sampling automatically if scene detection misbehaves, so no video regresses.

1.2.0
Sibling subtitles + audio chunking

Re-running on an already-downloaded file now picks up the cached .vtt/.srt sitting next to it instead of re-transcribing. And oversized audio is split into chunks, transcribed, and stitched back with correct timestamp offsets — which fixed the hard failure on anything past ~21 minutes.

1.1.0
--no-frames — transcript-only mode

Skips frame extraction entirely. Saves roughly 50–80k image tokens on talking-head content. Two people on Zoom for 50 minutes costs a fortune in tokens to tell you "yep, two people on Zoom."

1.0.0
The ScrappyLabs fork

Forked from bradautomates/claude-video v0.1.2. We rewired the audio path: upstream uploads every clip to a paid Whisper API. Ours prefers a local/self-hosted ASR endpoint via a small listen wrapper, with cloud Whisper kept as an explicit opt-in. Same public surface, four backends, all the upstream frame logic and security model preserved.

03How to actually use it well

These are the field-tested habits — the difference between "it works" and "it works cheaply and answers the question you asked."

Rule 1 Talking heads → --no-frames

Podcast, interview, fireside chat, AMA, conference panel, anything "in conversation with" — the visuals carry no signal. Go transcript-only. If the transcript then says "look at this graph", re-run with frames. We ran a 2h19m tutorial this way, got the verdict (90% skippable, one 10-minute section worth keeping) for almost nothing.

Rule 2 Long video + specific question → --start / --end

A sparse 100-frame scan of a 40-minute video is worse than a dense pass over the 30 seconds you care about. Focused mode gets a much denser per-second budget inside the range, and the transcript is auto-filtered to match. Accepts SS, MM:SS, or HH:MM:SS.

/watch "$URL" --start 2:15 --end 2:45     # zoom into one moment
/watch "$URL" --start 1:12:00             # from 1h12m to the end

Rule 3 Need to read on-screen text → --resolution 1024

Default frame width is 512px, which is fine for "what's happening." If you need to read code, a slide, or a prompt someone typed, bump it. It roughly quadruples image tokens per frame, so pair it with --start/--end rather than using it on a whole video. We've recovered full prompts verbatim off someone's screen recording this way.

Tuning knobs (rarely needed)

Don't Re-run for follow-ups

If Claude already watched the video this session, the frames and transcript are still in its context. Asking a follow-up question costs nothing extra — re-running the script pays the whole image-token bill a second time.

04Transcript: where the words come from

Three routes, tried in order. Most public videos never leave step 1.

RouteWhenCost
Native captions Source platform has manual or auto-generated subs. Tried first, always. Free · instant · already timestamped
Self-hosted ASR
--backend local
A listen wrapper is on your PATH. Points at any OpenAI-compatible ASR endpoint — yours, or ours at api.scrappylabs.ai as fallback. $0 marginal — runs on hardware you own
Cloud Whisper
--backend groq|openai
No local wrapper installed, or forced explicitly. Groq whisper-large-v3 preferred; OpenAI whisper-1 as fallback. Pay per call (Groq is the cheap/fast one)

What we'd suggest for you

Start with captions only — it covers the large majority of real usage at zero cost. When you hit a video with no captions, drop a GROQ_API_KEY into ~/.config/watch/.env and you're done; it's pennies. If you ever want the $0 path on your own box, the listen wrapper is a ~30-line shell script in the repo (docs/listen.sh) that points at any OpenAI-compatible ASR endpoint — happy to help wire it up. Or --no-asr to keep audio entirely local and go frames-only.

05Cost model — read this once

Token cost is dominated by frames. The transcript is cheap (a few thousand tokens for a 10-minute video). Roughly: 80 frames at 512px ≈ 50–80k image tokens.

DurationFrame budget (cap)What you get
≤ 30 s~30Dense — basically every key moment
30 s – 1 min~40Still dense
1 – 3 min~60Comfortable
3 – 10 min~80Sparse but workable
> 10 min100 + warningSparse scan — re-run focused instead

Those are ceilings. Since 1.3.0, static content (screencasts, slides, talking heads) typically lands 2–3× under them because dedup collapses repeated shots. Hard limits either way: 2 fps, 100 frames.

06Gotchas we already paid for

yt-dlp + Python 3.14

A regression in Python 3.14's re module segfaults yt-dlp mid-download. Fix: uv tool install --python 3.13 yt-dlp — the shim shadows the system one and it's durable.

Skill installed as a symlink

If you point ~/.claude/skills/watch at a dev directory, cleaning that directory silently breaks the skill — it fails with no obvious error. Install a real directory, or audit your symlinks periodically.

Login / region-locked videos

yt-dlp will fail plainly. That's a real failure, not a retry case — don't loop on it.

Clean up after yourself

Each run leaves a temp working directory with the video, frames, and audio. Claude removes it when you're done asking follow-ups; on a long session, check it did.

07What we actually use it for

Privacy & security posture

Everything runs locally except transcription. The video itself is never uploaded anywhere — only an extracted mono 16 kHz audio clip goes out, and only when native captions are missing and ASR isn't disabled. No platform login, no session cookies, no posting. API keys live in ~/.config/watch/.env at 0600, are never logged, and are never shared across providers. --no-asr keeps everything on your machine.