yt
by progrmoiz
README.md
# yt
> Agent-first YouTube CLI — search, transcripts, metadata, and a local FTS5 cache for AI context retrieval. Wraps `yt-dlp` with JSON envelopes, NDJSON streaming, and an MCP co-binary.
No YouTube Data API key. No quotas. No rate-limit ceiling on the default path.
```
██╗ ██╗████████╗
╚██╗ ██╔╝╚══██╔══╝
╚████╔╝ ██║
╚██╔╝ ██║
██║ ██║
╚═╝ ╚═╝
```
## Why
Existing YouTube tooling is either:
- Built for humans (interactive TUIs like `ytfzf`),
- Built for content creators (niche analysis, competitor audits),
- Or locked inside MCP-only servers.
`yt` is built for **agents** — Claude Code, Cursor, Codex, Gemini CLI, and your bash scripts. One call (`yt context <url>`) hands an LLM everything it needs to reason about a video.
## Install
```bash
# Requires yt-dlp on PATH
brew install yt-dlp
# From source
git clone https://github.com/progrmoiz/yt-cli
cd yt-cli
npm install && npm run build && npm link
```
Sanity check:
```bash
yt doctor
```
## Quick start
```bash
# Search
yt search "rust async" --limit 5
# Grok a single video (the 80% agent use case)
yt context https://www.youtube.com/watch?v=dQw4w9WgXcQ
# Fetch transcript
yt transcript https://www.youtube.com/watch?v=dQw4w9WgXcQ --text
# Query everything you've cached
yt find "borrow checker"
# List a channel's recent videos
yt channel @ThePrimeTimeagen --limit 20
```
Piping switches output to JSON automatically (redis-cli pattern):
```bash
yt search "typescript" | jq '.data[0].url'
yt context <url> | jq '.data.transcript.full_text'
```
## Commands
| Command | Description |
|---|---|
| `yt search <query>` | YouTube search, no API key. `--limit`, `--since 7d`. |
| `yt transcript <url>` | Timestamped transcript JSON. Cached. `--lang en`, `--text`. |
| `yt info <url>` | Metadata: title, channel, chapters, duration. Cached. |
| `yt context <url>` | **Headline.** One-shot info + transcript + chapters for agents. |
| `yt find <query>` | FTS5 search over cached transcripts. Line-level hits with timestamps. |
| `yt channel <handle\|id\|url>` | Recent videos from a channel. |
| `yt doctor [--deep]` | Diagnostics: yt-dlp, cache, agent detection. |
| `yt self-test` | Regression suite — catches YouTube endpoint rot. |
| `yt schema <cmd>` | JSON schema for that command's output. |
| `yt mcp serve` | Stdio MCP server. Co-binary. |
## Agent integration
### Claude Code / Cursor / etc — MCP
```jsonc
// .mcp.json
{
"mcpServers": {
"yt": { "command": "yt", "args": ["mcp", "serve"] }
}
}
```
Tools exposed 1:1 with CLI commands: `search`, `info`, `transcript`, `context`, `find`, `channel`, `whoami`.
### Scripts / shell
```bash
# Ingest the top 10 search hits into the local cache
yt search "machine learning ops" --limit 10 --json \
| jq -r '.data[].url' \
| xargs -I{} yt transcript {} --json >/dev/null
# Then query across all of them
yt find "feature store" --limit 5
```
## Cache
SQLite database at `~/.cache/yt/yt.db`. Contents:
- `videos` — one row per video (metadata).
- `transcripts` — one row per (video, language) pair.
- `transcript_segments_fts` — FTS5 virtual table over every transcript segment.
Override with `YT_CACHE_DIR=/path/to/dir`.
Every `transcript` / `info` / `context` call cache-checks first. Pass `--refresh` to force a fresh fetch.
## Output shape
Default envelope (v2):
```json
{
"version": "2",
"status": "success",
"command": "context",
"cli": { "name": "yt", "version": "0.1.0" },
"data": { /* command payload */ },
"metadata": {
"elapsed_ms": 1200,
"cached": true,
"has_transcript": true,
"agent": "CLAUDECODE"
}
}
```
Use `--raw` for bare data. Full JSON schema via `yt schema <command>`.
## Exit codes
`0` ok · `1` generic · `2` usage · `3` validation · `4` auth · `5` rate_limit · `6` network · `7` not_found · `75` temp_fail
## Environment
| Var | Purpose |
|---|---|
| `YT_CACHE_DIR` | Cache directory (default: `~/.cache/yt`) |
| `YT_DLP_BIN` | Override path to the `yt-dlp` binary |
| `YT_NO_RETRY` | Set to `1` to disable the single auto-retry on RATE_LIMIT/NETWORK/TIMEOUT |
| `YT_JSON=1` | Force JSON output |
| `YT_API_KEY` | Reserved for future Data API extras |
| `NO_COLOR` | Disable ANSI |
| `CI` | Force non-interactive mode |
## License
MIT.