Scripts & tooling
This is the reference for videoclaw's own repo tooling — the main npm scripts defined in package.json and helpers under scripts/. These are the commands you run on the codebase itself: building it, testing it, smoke-proving the whole pipeline, and guarding against drift before changes land. (For commands you run to make videos, see the CLI reference — vclaw video ... — not this page.)
One command to remember
Before you change anything non-trivial, run npm run check:release-readiness-lite. It is the single pre-flight: it builds, runs every unit test, runs the main smokes, and runs the guardrails — in one shot. If it prints release-readiness-lite checks passed, you are clear.
The fixture smokes are designed to run without paid generation. Infrastructure, live acceptance and graph commands have different effects; inspect their contracts before running them. The smokes create throwaway projects in a temp directory, drive the real CLI end-to-end, assert the on-disk artifacts, and then delete the temp dir. None of them spend provider credits, call an API, or need a key — they stub providers and use --dry-run. The one exception (smoke:assemble-render) actually spawns FFmpeg locally and self-skips if FFmpeg is not installed.
How a run flows

Diagram source (live Mermaid)
Build & test
Run these constantly. build is a prerequisite for everything else — the smokes and test:node run against compiled dist/, never the TypeScript source.
| Script | Command | Purpose |
|---|---|---|
| build | npm run build | Stage a tsc build and swap it into dist/ (the previous build stays complete throughout), then chmod +x the four compiled entrypoints (cli/vclaw.js, cli/provider-adapter.js, cli/flow-quote-adapter.js, cli/reapi-quote-adapter.js; the published bin names are videoclaw/vclaw and vclaw-provider-adapter, the two quote adapters being invoked by path). The clean rebuild every other script depends on. |
| dev | npm run dev | tsc --watch — incremental recompile while you edit. Leave it running in a terminal during development. |
| test | npm test | Full gate: lint, check:module-size, build, then test:node. The canonical "did I break the unit suite" command. |
| test:node | npm run test:node | Re-run the compiled node:test suite (dist/tests/*.test.js) without rebuilding. Use after dev has already recompiled, for a fast inner loop. Runs single-threaded (--test-concurrency=1). |
| demo | npm run demo | build then node scripts/demo-quickstart.mjs — runs the quickstart demo walkthrough against the freshly compiled CLI. |
| demo:record | npm run demo:record | bash scripts/record-demo.sh — records the demo walkthrough (does not rebuild first). |
| lint | npm run lint | eslint src --max-warnings=0 — the lint gate CI runs. |
| check:module-size | npm run check:module-size | Fails when a src/video module grows past its ceiling (split it, do not raise the number). |
| setup:test-python | npm run setup:test-python | Creates the pinned .venv from requirements-test.txt (Pillow, fonttools, numpy, praat-parselmouth). |
| check:test-python | npm run check:test-python | Verifies the pinned Python test environment; the test runner refuses a drifted Pillow. |
| test:coverage | npm run test:coverage | build then the c8-instrumented critical-coverage suite. |
| test:coverage:compiled | npm run test:coverage:compiled | The coverage suite against an existing dist/ (no rebuild). |
| demo:creator | npm run demo:creator | Alias for npm run demo. |
| check:build-fresh | npm run check:build-fresh | Fails when dist/ is older than src/ — a stale build masquerading as tested. |
Never edit dist/
dist/ is generated. Edit src/, then npm run build. Editing compiled output is silently discarded on the next build.
End-to-end smokes
Each smoke:* script runs npm run build first, then drives the real vclaw binary through a complete slice of the pipeline in an isolated temp project and asserts the resulting JSON artifacts. They are your proof that the wiring — CLI → domain modules → on-disk artifacts — actually holds together, not just that units pass in isolation. Run the relevant smoke after touching the area it covers; run them all via the release pre-flight before shipping.
| Script | Command | What it verifies |
|---|---|---|
| smoke:runtime | npm run smoke:runtime | The core happy path: init → brief → storyboard → assets → execution-plan → execute --dry-run → status → report → export-obsidian. Confirms the dry-run produces a plan, the portfolio report sees the project, and the Obsidian note is written. |
| smoke:native-veo | npm run smoke:native-veo | The native Veo (veo-useapi) transport. Stubs the local Bun vclaw-cli binary, runs a storyboard-mode execute, and captures the exact command the transport would have run — proving the Veo route assembles its submission correctly. |
| smoke:character-hydration | npm run smoke:character-hydration | Create-time cast hydration. Stands up a fake Go Bananas API server, runs vclaw video create with --import-library-characters and --auto-create-characters, and asserts imported character IDs land in the brief and the characters.json store. |
| smoke:execution-cancel | npm run smoke:execution-cancel | Adapter- and project-level cancellation. Wires a stub UseAPI adapter, submits, then execute-cancel, and asserts the execution-report.json and an execution.cancelled event were written. |
| smoke:portfolio | npm run smoke:portfolio | Portfolio visibility: index → report → export-csv. Confirms a project surfaces in the index, the report metrics, and a well-formed projects CSV. |
| smoke:reference-sheets | npm run smoke:reference-sheets | The role-tagged reference-sheet surface end-to-end: reference-sheet-add/list/show/bind/validate, role-vocabulary rejection (non-zero exit), palette collision detection, status summary, and the on-disk reference-sheets.json artifact. |
| smoke:scene-candidates | npm run smoke:scene-candidates | The candidate/take surface: select-candidate, reject-candidate, reroll-scene, candidates-list, candidates-show, plus the Candidates section rendered into storyboard.md and a clean doctor-project run. |
| smoke:story-bible-image | npm run smoke:story-bible-image | Image-only story-bible continuity: create builds a deterministic story bible, --apply-content-fixes regenerates scene text without going stale, doctor-project accepts it, and a --dry-run execute reaches dry-run-complete with zero video outputs through review → publish. Never submits a provider job. |
| smoke:assemble | npm run smoke:assemble | The FFmpeg assemble/stitch layer in dry-run. Asserts the plan threads narration (TTS) → slide-animation → final-video in order, records the stitch ffmpeg plan, and writes assemble-report.json. Needs no FFmpeg and no key. |
| smoke:assemble-render | npm run smoke:assemble-render | Real FFmpeg render validation (the only non-dry smoke). Synthesizes inputs with FFmpeg lavfi + sharp, then actually renders MP4s and ffprobes them for h264/aac, resolution, fps, stream counts, durations, MP4-validity, and both demuxer and filter stitch paths plus music mixing. Self-skips and exits 0 if FFmpeg/ffprobe are not on PATH. Validates plumbing/codecs only — aesthetic quality still needs a human. |
| smoke:multi-shot | npm run smoke:multi-shot | A plan→validate round-trip across four of the six registered multi-shot presets (cinematic-15s, seedance-10s, veo-8s, runway-10s; music-video-15s and social-hook-8s are not covered). Fixture-driven, fully offline, no Gemini. |
| e2e:image-storyboard | npm run e2e:image-storyboard | The image-storyboard workflow end-to-end, with --verify-server (exercises the review-station UI surface). |
| e2e:image-storyboard:examples | npm run e2e:image-storyboard:examples | The same workflow with --include-examples — runs the bundled example manifests in addition to the default. |
| smoke:rap-dryrun | npm run smoke:rap-dryrun | Rap lane: a solo and a duet film built from nothing reach qc — no renders, no spend (needs ffmpeg-full, macOS say). |
Running a single smoke
Every smoke is a standalone Node script. After one npm run build, you can re-run just the compiled CLI driver directly — e.g. node scripts/smoke-assemble.mjs — to iterate fast without rebuilding each time.
Guardrails (check:*)
The check:* scripts are drift guards. They don't test runtime behavior; they assert the repo's invariants stay true — docs don't reference dead paths, schemas and writers stay in sync, helper wrappers still parse, and verification artifacts stay gitignored. Run the relevant guard after editing docs, skills, schemas, or artifact writers.
| Script | Command | Purpose |
|---|---|---|
| check:movie-director-wrappers | npm run check:movie-director-wrappers | bash -n syntax-checks the six bundled movie-director helper scripts (auto, iterate, run-pipeline, verify, list-library, interview). Catches a broken wrapper before it ships. |
| check:cleanroom-docs | npm run check:cleanroom-docs | Scans README.md, docs/, and the movie-director / seedance-prompts skills for stale clean-room references — absolute home paths, retired CLI invocations, and other dead pointers — failing if any are found. |
| check:skill-frontdoor | npm run check:skill-frontdoor | Scans every skills/*/SKILL.md front door for retired script references, ensuring each skill's entry doc points at the current vclaw CLI surface. The ignore list (e.g. seedance-prompts/SKILL.md) is load-bearing — do not "fix" it. |
| check:artifact-schema-coverage | npm run check:artifact-schema-coverage | Advisory (always exits 0). Asserts every artifact written via the typed writeArtifact() helper has a matching JSON Schema under schemas/video/artifacts/, and every schema has a writer (with a documented allowlist for artifacts written by other paths). Prints any drift. |
| check:artifact-schema-coverage:strict | npm run check:artifact-schema-coverage:strict | The same coverage check in strict mode — exits 1 on drift. Use in CI or when you want a hard failure on writer/schema mismatch. |
| frontdoors:install | npm run frontdoors:install | Materialises the /slash front doors declared in scripts/skill-frontdoors.json into .claude/ (pointer skills + trigger blocks). |
| frontdoors:check | npm run frontdoors:check | Reports drift between the declared front doors and what .claude/ has; exits 1, writes nothing. |
| check:seedance-engine | npm run check:seedance-engine | Offline self-test of the free Higgsfield engine that ships with videoclaw (engines/seedance-direct). |
| check:modelark-prices | npm run check:modelark-prices | Compares the seedance-modelark price table with the live BytePlus catalogue through the Ark CLI (arkcli pricing models). Fails when a list price or a sold resolution has drifted; reports account discounts without failing. Spends nothing; skips, out loud, when arkcli is missing or signed out — so it is a local check, never CI. Needs npm run build first. |
| docs:sync | npm run docs:sync | Regenerates docs-site/reference/ from docs/ — run after ANY docs/*.md edit. |
| check:docs-site | npm run check:docs-site | Fails when docs-site/reference/ does not match docs/ (+transforms); CI runs it. |
| diagrams:lint | npm run diagrams:lint | Lints docs-site/diagrams/manifest.json: unique ids, sources and pages exist, every labels[] string is in its Mermaid, and the mirrored README/docs diagrams match their fence in docs/DIAGRAMS_SOURCE.md. Renders nothing. |
| diagrams:mirror | npm run diagrams:mirror | Re-extracts the README/docs Mermaid blocks from docs/DIAGRAMS_SOURCE.md into docs-site/diagrams/src/docs-*.mmd + manifest entries (run after editing a fence there). |
| diagrams:render | npm run diagrams:render | Renders the mirrored README/docs diagrams with mermaid-cli (brand config) and publishes their JPGs to docs/assets/ and docs-site/public/diagrams/. Deterministic; the images in docs/assets/ are this render. |
Release pre-flight
| Script | Command | Purpose |
|---|---|---|
| prepublishOnly | (automatic) | npm test — npm lifecycle hook that runs the full build + test:node gate automatically before npm publish. You never invoke it directly. |
| check:release-readiness-lite | npm run check:release-readiness-lite | The one-shot local gate. It checks that the generated verification-artifact paths are gitignored, then runs, in order: build → check:build-fresh → lint → check:module-size → test:node → the compiled coverage run → the eight primary smokes (runtime, native-veo, character-hydration, execution-cancel, portfolio, reference-sheets, scene-candidates, assemble) → the e2e:image-storyboard workflow → all three doc/skill guardrails → the schema-coverage check with --strict, which fails the gate on drift. Prints release-readiness-lite checks passed on success. |
What the pre-flight does not cover
By design, check:release-readiness-lite excludes smoke:assemble-render (needs real FFmpeg). Run it manually when the area is in scope. It does run the schema-coverage check with --strict, so artifact-schema drift fails the gate rather than being tolerated.
When to run what
| Situation | Run |
|---|---|
| Actively editing TypeScript | npm run dev in a terminal, then npm run test:node to re-check |
| Quick "did I break units" check | npm test |
| Touched the assemble / FFmpeg layer | npm run smoke:assemble, then npm run smoke:assemble-render locally |
| Touched docs, skills, or schemas | the matching check:* guard |
| About to commit anything non-trivial | npm run check:release-readiness-lite |
| Wiring schema coverage into CI | npm run check:artifact-schema-coverage:strict |
| acceptance:live | npm run acceptance:live |
For agents
Treat check:release-readiness-lite as the success signal: a clean run is evidence the change is safe. Prefer running the narrow smoke for the area you touched first (fast feedback), then the full pre-flight before declaring done. Use the fixture smokes appropriate to the change; live acceptance and infrastructure commands require separate attention to their effects.
Shared queue & knowledge graph
Infrastructure scripts for the multi-machine render queue and the repo's knowledge graph.
| Script | Command | Purpose |
|---|---|---|
| shared-queue:setup | npm run shared-queue:setup | Configures this machine for the shared multi-machine render queue. |
| shared-queue:status | npm run shared-queue:status | Prints the shared queue's lane leases and receipts. |
| shared-queue:dashboard | npm run shared-queue:dashboard | Opens the shared queue's dashboard. |
| shared-queue:self-test | npm run shared-queue:self-test | Exercises acquire/heartbeat/release against the deployed coordinator. |
| shared-queue:verify | npm run shared-queue:verify | Runs coordinator typechecking, tests and a deployment dry run; does not prove the live deployment matches. |
| graph:full | npm run graph:full | Rebuilds the graphify knowledge graph (graphify-out/), including the Gemini stage. |
Additional installed checks
| Command | Purpose |
|---|---|
npm run smoke:story-bible-image | Exercise the image-only story-bible path. |
npm run smoke:multi-shot | Exercise multi-shot compilation. |
npm run smoke:rap-dryrun | Run solo and duet rap fixtures and stamp their dry-run evidence. |
npm run frontdoors:install | Install the shared skill front doors. |
npm run frontdoors:check | Check those front doors without installing. |
Read package.json for the exact current command bodies. Contributor tests require Node 20.10+, Python 3.12+ in the configured virtual environment, test requirements and FFmpeg/ffprobe. npm test does not run the separate Bun Flow suite or verify live providers.
