--- name: video-ingest description: Ingests a video's content (YouTube and similar) for agent context, routing by capability, task need, and video length — a natively multimodal engine (Gemini via agy or the Gemini API) for direct-URL and long video, Claude vision over ffmpeg-extracted frames when no such engine is present, yt-dlp captions when the transcript alone suffices, and the logged-in claude-in-chrome session for auth-gated videos. The governor cross-checks any multimodal comprehensive-read. Triggered by "ingest this video", "what does this video show", "get the transcript from this YouTube video", "video-ingest". user-invocable: true allowed-tools: ["Read", "Bash"] model: sonnet --- # video-ingest — Video Content Ingestion (capability-routed) A video carries two separable signals: the **spoken transcript** (text) and the **visual content** (on-screen demos, slides, UI). Captions give only the first. So the route is chosen by **what the task needs** × **what engine is available** × **video length** — not a single fixed path. This reuses frontier-digest's capability-over-engine logic and adds on-demand, need-based routing, a Claude-native frame path, output normalization, and a governance cross-check. FH's increment over a raw video executor (sister asset `claude-video`, github bradautomates/claude-video; cross-audit note in a private companion store): the executors *watch*; FH **governs** — the governor never trusts a multimodal "comprehensive read" at face value, it cross-checks claims against the transcript / a second pass. Same compose pattern as the other FH↔executor pairs. ## Triggers - "ingest this video" · "what does this video show / demonstrate" - "get the transcript from this YouTube video" · "pull the captions from " - "summarize what's on screen in this video" - "video-ingest" ## Step 0 — What does the task need? (picks the tier) - **Video understanding** (visual: demos, UI walkthroughs, slides, anything not in the spoken words) → a vision path (Tier 1a or 1b below). Captions miss the screen. - **Transcript only** (spoken words suffice — a talk, an interview) → go straight to **yt-dlp** (Tier 3); do not spend a vision engine when text is all that's needed (cost guard). ## Step 1 — Capability + length probe, then tier ladder (REUSE the probe — do not re-specify) Resolve the engine by **capability, not name**, via frontier-digest's **Sidecar Engine Resolution Protocol** (`plugins/fh-meta/skills/frontier-digest/SKILL_detail.md §Video-Harvest` — single source of the probe commands). **Length is the 2nd axis** (claude-video is sparse past ~10min). The ladder: - **Tier 1a — native multimodal, URL-direct (best for LONG video)**: an engine that watches the video and returns spoken + visual detail, no frame cap. Verified: **Gemini ingests a YouTube URL directly** (returns verbatim dialogue AND on-screen detail). Probe for a live route — the direct `gemini` CLI is EOL (2026-06-18), so probe `agy` / the Gemini API. **Caveat (measured)**: a *YouTube URL* works, but an **arbitrary (non-YouTube) stream is not reachable headless** (needs file upload) — degrade for those. - **Tier 1b — Claude-native frame extraction (Claude-only; SHORT/MEDIUM ≤~10min)**: when no external multimodal engine is present, do not give up on visual content — `ffmpeg` extracts frames (auto-scaled, cap ~2fps/100 frames) → **Claude's own vision via Read** reads them as images (the claude-video pattern). No separate runtime, **no Gemini-EOL exposure**. Caution: claude-video itself is a 3rd-party executable (brew installs + optional Whisper egress) — if invoking it directly, review first; the ffmpeg+Read mechanism can also be run inline without it. - **Never route video to a coding-agent CLI** (codex, a non-multimodal model) — burns tokens, recovers only metadata. - **Tier 3 — yt-dlp transcript** (text only; conditional — probe first, never assume): ```bash yt-dlp --no-update --skip-download --write-auto-subs --sub-langs en \ --sub-format json3 -o "/tmp/yt_%(id)s.%(ext)s" "" python3 -c "import json,sys,re;d=json.load(open(sys.argv[1]));print(re.sub(r'\s+',' ',' '.join(s.get('utf8','') for e in d['events'] for s in e.get('segs',[]) if s.get('utf8','').strip())).strip())" /tmp/yt_.en.json3 ``` `--write-subs` (human captions) preferred when present. Keep yt-dlp current (`pip install -U yt-dlp`; version/impersonation warnings are non-blocking). yt-dlp also supplies the transcript the governor cross-checks a Tier-1 visual read against. - **Auth-gated** (members-only / age-gated) → **claude-in-chrome** (operator's logged-in session). video-ingest **surfaces this route as a handoff** — it does not drive the browser itself (the `mcp__claude-in-chrome__*` tools are outside this skill's `allowed-tools`). Caveat: needs the Anthropic "direct" plan — unavailable on a corp Bedrock laptop; surface the limit, do not loop. - **Unresolvable** (no vision engine, yt-dlp probe fails, not auth-fixable) → operator summary remains the path; say so plainly. ## Step 1.5 — Governance cross-check (FH's increment; do not skip on a multimodal read) A Tier-1 engine's "comprehensive read" is a **self-report**, untrusted like any sidecar output. The governor cross-checks its key claims against the transcript (Tier 3) or a second targeted frame pass before accepting; a visual claim that contradicts the transcript or cannot be re-grounded is flagged, not relayed as fact. (This is the governance claude-video has no equivalent for.) ## Step 2 — Normalize + hand off Write clean text (+ visual notes if Tier 1) and metadata (video id, title, duration, source: multimodal vs transcript, caption type human/auto) to a predictable path (`/tmp/yt__ingest.txt` or a caller-specified path). Downstream grounding ingest is `corpus-grounding-expander`'s job — video-ingest produces the artifact, it does not ingest into a store. ## Done When - Output artifact written AND character count > 0 (or a specific failure reason surfaced). *[measured]* - Tier chosen matches the task need × the live capability probe × length (video-understanding → Tier 1a for long / Tier 1b for short-medium; transcript-only → yt-dlp). *[judged, pair: the probe is the mechanical anchor]* - A Tier-1 multimodal comprehensive-read was cross-checked against the transcript / a second pass before its claims were relayed as fact. *[judged, pair: transcript diff is the mechanical anchor]* - On failure, a specific reason is surfaced (no-captions / auth-required / no-vision-engine / arbitrary-stream-headless-unreachable) — never a silent empty result. *[mandatory-pass]* ## Guards - **No-reinvention** — the capability probe + tier ladder are frontier-digest's Sidecar Engine Resolution Protocol (single source); the executors are yt-dlp (Bash), the Gemini path, ffmpeg+Read (claude-video pattern), and claude-in-chrome (MCP). This skill routes by need × length, cross-checks, normalizes, and reports — it builds no caption engine, no frame extractor, no browser automation. - **Governor never trusts a multimodal self-report** — Step 1.5 cross-check is FH's increment over a raw video executor; a visual claim is grounded before it is relayed. - **Capability over engine name** — probe what an engine can actually do (EOL/availability drifts); never assume `gemini`/`agy`/`yt-dlp`/`ffmpeg` is present. - **Cost guard** — transcript-only need never spends a vision engine. - **3rd-party executable caution** — claude-video (brew installs + optional Whisper egress) is reviewed before install; the ffmpeg+Read mechanism can run inline without adopting the whole tool. - **Corp-host honesty** — when a path is unavailable (Bedrock laptop, EOL CLI, headless-stream limit), state it; do not loop on a path that cannot run.