https://github.com/ibbybuilds/mvd
Movie discovery + watchlist CLI. Built for humans and AI agents.
https://github.com/ibbybuilds/mvd
agent-tools cli movies tmdb typescript watchlist
Last synced: about 1 month ago
JSON representation
Movie discovery + watchlist CLI. Built for humans and AI agents.
- Host: GitHub
- URL: https://github.com/ibbybuilds/mvd
- Owner: ibbybuilds
- License: mit
- Created: 2026-05-23T22:37:56.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-23T23:20:33.000Z (about 2 months ago)
- Last Synced: 2026-05-24T01:11:08.696Z (about 2 months ago)
- Topics: agent-tools, cli, movies, tmdb, typescript, watchlist
- Language: TypeScript
- Homepage: https://github.com/ibbybuilds/mvd
- Size: 85.9 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# mvd
Movie discovery + watchlist CLI. Built for humans and AI agents.
Discover movies by year, genre, rating, votes. Maintain a watchlist. Mark watched + rate. All local — no cloud sync, no telemetry.
```
$ mvd discover --year 2024 --min-rating 8 --top 5
ID TITLE YEAR TMDB VOTES GENRES
────── ─────────────────────── ──── ──── ───── ────────────────────
1184918 The Wild Robot 2024 8.4 3500 Animation, Family
933260 The Substance 2024 8.2 2800 Horror, Drama
...
```
## Install
```bash
npm install -g @ibbybuilds/mvd
mvd init # prompts for TMDB + OMDb keys
```
Get a free TMDB v4 token at . OMDb key (optional, for IMDB/RT/Metacritic enrichment) at .
## Quick start
```bash
mvd discover --year 2024 --min-rating 7 --top 10
mvd search "fight club"
mvd add 550 # add Fight Club (TMDB id) to watchlist
mvd add "fight club" --no-prompt # agent-friendly: top match, no interactive disambiguation
mvd list --sort rating
mvd watched mark 550 --rating 9
mvd watched list
mvd unwatch 550 # back to watchlist, preserves history
```
## Commands
| Command | What it does |
|---|---|
| `mvd init` | Save TMDB + OMDb API keys |
| `mvd discover` | Filter by year, genre, rating, votes |
| `mvd search ` | Search by title |
| `mvd info ` | Full details for one TMDB id |
| `mvd add ` | Add to watchlist |
| `mvd remove ` | Remove from watchlist |
| `mvd list` | Show watchlist |
| `mvd watched list` | Show watched movies |
| `mvd watched mark ` | Mark as watched |
| `mvd unwatch ` | Move from watched back to watchlist |
| `mvd export` | Dump full state (JSON or SQL) |
| `mvd import ` | Restore from export (merge or --replace) |
Every command supports `--help`.
### Global flags
- `--format ` — `json` \| `yaml` \| `table` \| `auto` (default). `auto` = yaml when piped, table in TTY.
### `discover` options
```
--year Primary release year
--genre Genre name(s). Comma = AND, pipe = OR. Examples:
--genre Action (single)
--genre "Action,Drama" (both)
--genre "Action|Comedy" (either) — quote to avoid shell pipe
--min-rating TMDB vote average ≥ (default 0)
--min-votes TMDB vote count ≥ (default 300)
--top Limit results (default 10)
--sort tmdb | imdb | popularity | release | title (default tmdb)
--enrich Fetch OMDb (IMDB/RT/Metacritic) per result
```
`--sort imdb` and `--enrich` use OMDb (free tier: 1000/day). Each enriched result = 2 API calls (TMDB details + OMDb lookup). Cap `--top` accordingly.
### `add ` options
```
--id Force numeric arg as TMDB id (use for movies titled "1984", "300", etc.)
--no-prompt Auto-pick top result on multi-match (agent mode)
```
Numeric heuristic: bare digits > 999 treated as TMDB id. Use `--id` to override for low-id or numeric titles.
## Config
State lives in `~/.mvd/`:
- `config.json` — defaults (min_votes, top, sort, min_rating)
- `.env` — API keys (`TMDB_API_KEY`, `OMDB_API_KEY`)
- `movies.db` — SQLite cache + watchlist + watched + history
Override config dir with `MVD_HOME=/path/to/dir`.
Env vars override `.env` file: `TMDB_API_KEY`, `OMDB_API_KEY`.
## Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | User error (bad args, no match, validation fail) |
| 2 | API error (TMDB/OMDb HTTP failure) |
| 3 | Config / setup error (missing key, no init) |
## For AI agents
`mvd` is built for programmatic use. See [AGENTS.md](AGENTS.md). Key points:
- `--format auto` emits YAML when piped, JSON parses cleanly via `--format json`
- All commands deterministic — same input, same output
- No spinners, no progress bars, no TTY tricks in piped output
- `mvd add --no-prompt` for headless disambiguation
- Re-adding same id is a no-op
## Sync across devices
`mvd` is local-only by design — no cloud account, no telemetry. To sync state across machines, export + ship the snapshot anywhere you want:
```bash
mvd export --out ~/mvd-snap.json # dump full state
mvd import ~/mvd-snap.json # merge into local db (idempotent)
mvd import ~/mvd-snap.json --replace # wipe local + restore from file
```
### Pattern 1: Syncthing / Dropbox / iCloud (zero-infra)
Put the export in a synced folder. Run on a timer:
```bash
# crontab -e
*/30 * * * * /usr/local/bin/mvd export --out ~/Dropbox/mvd/snap.json
@reboot /usr/local/bin/mvd import ~/Dropbox/mvd/snap.json
```
### Pattern 2: Git (versioned, free)
```bash
cd ~/mvd-sync && git init
mvd export --out ./snap.json
git add snap.json && git commit -m "snap $(date -I)" && git push
```
JSON diffs cleanly. Use `git log -p snap.json` to see when you added each movie.
### Pattern 3: Litestream (continuous, advanced)
Stream the SQLite WAL to S3/B2/SFTP. No code changes:
```bash
litestream replicate ~/.mvd/movies.db s3://my-bucket/mvd.db
# on restore:
litestream restore -o ~/.mvd/movies.db s3://my-bucket/mvd.db
```
See [litestream.io](https://litestream.io).
### Merge semantics
`mvd import ` (default = merge):
- **movies**: upsert by `tmdb_id` (newest wins, COALESCE preserves enriched fields)
- **watchlist**: idempotent (re-add same id = no-op)
- **watched**: upsert by `tmdb_id` (latest record replaces previous)
- **watched_history**: dedupe by `(tmdb_id, watched_date, archived_at)` triple
- **genres_cache**: replaced wholesale (newest export wins)
No multi-writer conflict resolution. If two devices edit between syncs, last-export-wins on the conflicting row. For true multi-master, use [Turso/libsql](https://turso.tech) (vendor) or [PouchDB-style CRDTs](https://pouchdb.com) (heavy).
## Database schema
See [SCHEMA.md](SCHEMA.md).
## Contributing
```bash
git clone https://github.com/ibbybuilds/mvd
cd mvd
npm install
npm run build
npm test
npm link # makes `mvd` available globally
```
Conventional Commits. Strict TypeScript, no `any` without reason.
## License
MIT © ibbybuilds