Paste a YouTube link, get a vault-ready research digest in 90 seconds: TL;DR, timestamped takeaways, slide screenshots, transcript. A Claude Code skill.
SaferSkills independently audited yt-digest (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Process a YouTube video into a single markdown research note in your notes vault. Drop a link, get a digest that's faster to consume than the video.
vault/research/youtube/<slug>.md — frontmatter + TL;DR + numbered takeaways (each with timestamp deep-link + screenshot if visual) + collapsed transcriptvault/attachments/youtube/<slug>/frame-MM-SS.png — embedded in digest via Obsidian's ![[...]] syntax/tmp/yt-digest/<slug>/ — video + raw VTT, deleted at end of runHeadless-safe: all inputs come from CLI args, no interactive prompts, deterministic output path so an orchestrator can read the result back.
Required: a YouTube URL (any form — youtu.be/x, youtube.com/watch?v=x, youtube.com/shorts/x).
Optional flags:
--no-screenshots — skip video download + frame extraction (transcript-only, ~$0.01, ~5s)--tags a,b,c — add to frontmatter tags: list--slug name — override auto-generated slugIf no URL is provided, abort with a one-line error — do NOT prompt the user (this skill runs headless).
mkdir -p /tmp/yt-digest
META_JSON=$(.claude/skills/yt-digest/scripts/fetch.sh "$URL" "/tmp/yt-digest/staging-$$" ${NO_SCREENSHOTS:+--no-video})Parse META_JSON (one-line JSON) for: id, title, channel, duration_seconds, upload_date (YYYYMMDD), slug, transcript_path, video_path.
Override slug if --slug was passed.
Set up paths:
WORK_DIR="/tmp/yt-digest/staging-$$"
ATTACH_DIR="vault/attachments/youtube/$SLUG"
DIGEST_PATH="vault/research/youtube/$SLUG.md"
mkdir -p "$ATTACH_DIR" "$(dirname "$DIGEST_PATH")"If `transcript_path` is empty: YouTube has no captions for this video. Abort with: "No captions available for this video. Re-run with audio re-transcription (not yet implemented) or skip." Clean up $WORK_DIR before exiting.
TRANSCRIPT_JSON=$(.claude/skills/yt-digest/scripts/parse_vtt.py "$TRANSCRIPT_PATH")TRANSCRIPT_JSON is [{start_seconds, text}, ...] with rolling-caption duplicates removed.
Read the full transcript. Identify 5-10 key moments that earn a bullet in the digest. Each moment is:
{
"start_seconds": 754,
"title": "Concise one-line insight (≤ 12 words)",
"quote": "Verbatim line from the transcript that earned this bullet",
"needs_screenshot": true,
"rationale": "Why a screenshot would help — e.g. 'slide showing the 6-bucket framework'"
}Selection criteria:
Set needs_screenshot: true ONLY when the speaker is clearly referencing a visual (slide, demo, chart, code). Pure talking-head insights get needs_screenshot: false.
Anti-patterns — don't pick:
If --no-screenshots was passed, skip this step.
Collect timestamps where needs_screenshot: true:
.claude/skills/yt-digest/scripts/extract_frames.sh "$VIDEO_PATH" "$ATTACH_DIR" 754 1230 1890Frames land as frame-MM-SS.png in $ATTACH_DIR.
Write $DIGEST_PATH using this exact structure:
---
source: youtube
url: https://youtube.com/watch?v=<id>
channel: <channel>
title: <title>
duration: <hh:mm:ss>
published: <YYYY-MM-DD from upload_date>
digested: <today's date YYYY-MM-DD>
tags: [youtube, research, <user-supplied tags>]
---
# <title>
**Channel:** <channel> · **Length:** <hh:mm:ss> · **Published:** <YYYY-MM-DD> · [Watch ↗](<url>)
## TL;DR
- 3-5 punchy bullets capturing the video's thesis. Each bullet stands alone.
## Key Takeaways
### 1. <title from moment> · [<MM:SS>](<url>&t=<start_seconds>s)
<2-4 sentences expanding the insight. Reference the screenshot if present.>
![[frame-MM-SS.png]] <!-- only if needs_screenshot was true -->
> <verbatim quote>
### 2. <next moment...>
...
## Open Questions
- 1-3 questions the video raises but doesn't answer, or things worth trying
- Skip this section if nothing concrete comes to mind — don't manufacture filler
## Full Transcript
<details>
<summary>Click to expand</summary>
[00:00] First line of transcript text. [00:08] Second line...
</details>Notes on assembly:
<url>&t=<start_seconds>s — YouTube timestamp deep-link. Use & if URL already has ?v=, else ?t=.H:MM:SS if ≥ 1hr, else MM:SS.![[frame-MM-SS.png]] resolves via the vault's attachment folder config. If that fails for any reader, use the explicit form  as fallback.[MM:SS] so future search lands you at the right second.Use Python rather than rm -rf — Claude Code's safety policy hard-blocks rm -rf even when explicitly allowlisted, which would break headless dispatch:
python3 -c "import shutil, sys; shutil.rmtree(sys.argv[1], ignore_errors=True)" "$WORK_DIR"This deletes the source MP4 + raw VTT. The vault keeps only: digest markdown + screenshots (~2-3 MB total per video).
Final line of output must be:
DIGEST: <absolute path to digest>Any orchestrator can parse this to surface the file back to the originating thread.
| Component | Cost per 30-min video |
|---|---|
| yt-dlp transcript + video | $0 |
| Model digest pass (~7K in / 800 out) | ~$0.01–0.04 |
| ffmpeg frame extraction | $0 |
| Vault storage (digest + screenshots) | ~3 MB |
Source MP4 (~100 MB) is deleted in step 6.
The skill writes to a deterministic path and prints DIGEST: <path> as its final line, so it composes with any orchestrator that can shell out — cron, CI, or a chat bot. The pattern worth building: a Slack/Telegram bot that runs the skill on a pasted URL and posts the path (or the rendered TL;DR) back into the thread. "Drop a link in chat, get a digest in your vault" becomes a one-message workflow.
Model note for unattended runs: in a headless claude -p run, a small/cheap model will sometimes describe what the skill would do rather than invoking the scripts (turns burned, zero tool calls, nothing written). Route the unattended dispatch to a more capable model. The interactive run is fine on the cheap model.
![[...]] syntax).vault/research/youtube/<slug>.md already exists, append a -2 suffix to the slug. Don't overwrite.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.