A local-first pipeline for producing faceless animated YouTube videos.
Claude directs. Remotion renders. FFmpeg processes. Local TTS narrates.
Claude never produces media directly — it produces structured JSON (scripts, storyboards, visual specs, SEO). Deterministic engines consume that JSON and produce the actual video. That separation is the core architectural rule.
Everything except the Claude API is free and open-source, and the whole system runs on your machine. No paid SaaS, no rendering cloud, no hosting bill.
Status: Phase 11 of 14. The full pipeline exists, idea to upload, and the dashboard now drives it. Every stage from topic generation to a YouTube upload is implemented, behind three safety gates (QA must pass, public needs an explicit opt-in, no duplicates), and you can watch the render, read the script, approve a stage and start the upload from the browser instead of the CLI. The upload path has not yet run against the real YouTube API — see the verification note in DEVELOPMENT.md.
Generation needs an
ANTHROPIC_API_KEY. Everything else runs without one.
| Required | Install | |
|---|---|---|
| Node.js 20+ | yes | nvm install 22 |
| MongoDB Community | yes | brew tap mongodb/brew && brew install mongodb-community |
| Anthropic API key | Phase 3+ | console.anthropic.com |
| FFmpeg | not required | Remotion ships one; brew install ffmpeg only adds loudness measurement |
| Piper TTS | recommended | rhasspy/piper — see DEVELOPMENT.md |
| Google Cloud OAuth client | Phase 10+ | console.cloud.google.com |
Only Node and MongoDB are needed to run what exists today.
npm install
cp .env.example .env # then fill in what you have
brew services start mongodb-community
npm run doctor # tells you exactly what is missingnpm run doctor is the source of truth for environment problems. It reports
what is installed, what each missing piece blocks, and the command that fixes
it — and it never fails the build for something a later phase needs.
npm run dev # API (:4000) + dashboard (:5173), both watching
npm run studio # Remotion Studio — preview and scrub compositionsOpen http://localhost:5173.
Everything below can also be done from the CLI, but the dashboard is where reviewing happens: Videos starts a project and lists every one, each project's own page carries the approval gate plus the rendered video, thumbnail, script, storyboard, SEO and the 15 QA checks, Ready to Upload sends it to YouTube (private by default), and YouTube Connection walks through the OAuth setup.
| Command | What it does |
|---|---|
npm run dev |
API + dashboard with hot reload |
npm run studio |
Remotion Studio — preview Video and the component gallery |
npm run doctor |
Environment check |
npm run build |
Compile every package |
npm test |
Vitest suite |
npm run verify |
build + typecheck + lint + test |
npm run cli -- --help |
Full CLI surface, including not-yet-built commands |
npm run cli -- project new --topic "Why lifestyle inflation eats your raise"
npm run cli -- project ls
npm run cli -- project show vid_01k7m4h2q9x3f8ta
npm run cli -- project approve vid_01k7m4h2q9x3f8ta # clears the current gate
npm run cli -- channel show # niche, tone, pillars
npm run cli -- channel set --niche "behavioural economics"npm run cli -- topic --count 8 --evaluate # ideas, balanced across pillars
npm run cli -- ideas # review and triage them
npm run cli -- project new --topic "…" # start a project
npm run cli -- research --video-id vid_xxx # real sources, labelled claims
npm run cli -- script --video-id vid_xxx # hook, sections, conclusion, CTA
npm run cli -- hook --video-id vid_xxx # alternative openings
npm run cli -- storyboard --video-id vid_xxx # timed scenes with visual specs
npm run cli -- voice --video-id vid_xxx # narration, retiming, captions
npm run cli -- render --video-id vid_xxx # render + mux audio (--captions)
npm run cli -- thumbnail --video-id vid_xxx # Claude picks a template
npm run cli -- seo --video-id vid_xxx # scored titles, tags, chapters
npm run cli -- qa --video-id vid_xxx # the 15-check gate
npm run cli -- youtube connect # one-time OAuth
npm run cli -- upload --video-id vid_xxx # private by default
npm run cli -- upload-status --video-id vid_xxxUploads are private by default and cannot be made public by accident. A
--privacy public request is downgraded unless ALLOW_AUTO_PUBLISH=true, and
nothing uploads at all unless QA passed. Separately, while your Google OAuth app
is unverified, YouTube forces every upload private regardless — that is their
policy, not a limitation of this tool.
thumbnail accepts --template to force one of the five, and --spec-only to
plan without rendering. seo scores at least ten title candidates and shows
which would truncate in search; --pick <n> selects one over the
recommendation.
qa exits non-zero when a critical check fails, so it works as a gate in a
script. A missing thumbnail is a warning by default; --require-thumbnail and
--require-captions promote those to blocking.
voice also accepts --music <path> to mix a bed under the narration,
--voice/--rate to override the engine defaults, and --reuse to skip scenes
whose audio already exists after a partial failure.
Then preview the result:
npm run studio # open the Video composition, scrub the storyboardTwo guarantees worth knowing, both enforced in code rather than asked for in a prompt:
The storyboard cannot rewrite your script. Scene narration is checked word for word against the approved script. A single reworded sentence is a blocking error that names the exact divergence.
Scene durations are computed, not guessed. Length comes from word count at the speaking rate, and the video total is the sum of its scenes — so the timeline can never disagree with itself. Phase 6 replaces the estimates with the measured voiceover.
Every claim the research stage produces is labelled verified_fact,
estimate, opinion or hypothesis. Only a verified_fact with a source
that was actually fetched may be stated flatly in the script — anything else
must be hedged, and the script checker rejects the draft if it isn't. Sources
are never invented to fill a gap.
IDEA → RESEARCH → STRATEGY → SCRIPT → STORYBOARD → VOICEOVER
→ ANIMATION → CAPTIONS → MUSIC → THUMBNAIL → SEO → QA
→ YOUTUBE UPLOAD → ANALYTICS → feeds back into the next idea
Every stage runs independently and is resumable. A failed render never destroys the script, storyboard, audio or assets that came before it — only the failed stage is retried.
These are deliberate and take an explicit opt-in to change:
- Pipeline mode is
manual. Generation pauses for your approval at the script, the storyboard, and again before upload. - Uploads are
private.ALLOW_AUTO_PUBLISHdefaults tofalse. - QA gates the upload. A video with critical QA failures is never uploaded automatically.
- No secrets in source control. Only
.env.exampleis tracked. - No shell execution. Every subprocess is spawned via
execFilewith an argv array. No AI-generated string is ever executed.
data/
├── projects/{video-id}/
│ ├── script/ storyboard/ audio/
│ ├── assets/ render/ thumbnail/
│ ├── final/ logs/
├── credentials/ # OAuth tokens (gitignored)
├── library/ # reusable assets
└── music/ # royalty-free background tracks you supply
Nothing leaves your machine except Claude API calls and YouTube uploads.
packages/
├── shared/ config, logging, errors, domain types, path safety
├── core/ database, provider interfaces, system checks
├── server/ Fastify API
├── web/ React + Vite + Tailwind dashboard
├── video/ Remotion compositions and the animation component library
└── cli/ command-line interface
The tooling here is free, but check the licence of anything you add yourself — fonts, music, sound effects, images. This project ships no third-party media.
- DEVELOPMENT.md — architecture, phase plan, conventions, and the known limitations of the YouTube API