{"id":"6c3b7e06-5633-4dc5-8e3b-2886f1e0b68a","slug":"narova","kind":"skill","name":"narova","summary":"Open video production system for humans and agents — local-first CLI and agent skill: scene scripts, deterministic rendering, local TTS, word-synced captions, product walkthroughs, AI clips (Sora/Runw","owner":"ammar-hasan","repoUrl":"https://github.com/ammar-hasan/narova","license":"Apache-2.0","licenseTier":"permissive","trust":"community","stars":4,"match":null,"fewStars":true,"format":"skill","createdAt":"2026-07-20T15:52:21.000Z","addedAt":"2026-10-14T06:14:25.645Z","logoUrl":"https://avatars.githubusercontent.com/u/6522620?u=6143911991c7ede38bd5af238ea31b096cd94ee1&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["design-media","files-documents"],"tags":["captions","cli","ffmpeg","local-first","motion-graphics","programmatic-video","prompt-to-video","text-to-speech"],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-17T08:51:42.437Z","sample":false,"url":"https://conduits.dev/tools/narova","language":"JavaScript","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"<div align=\"center\">\n\n<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"docs/assets/logo.svg\">\n  <img src=\"docs/assets/logo.svg\" alt=\"Narova\" width=\"84\" height=\"84\">\n</picture>\n\n# Narova — open video production system for humans and agents\n\n**Turn an idea, script, source, repository, or real product flow into video you\ncan direct, inspect, revise, reproduce, and ship.**\n\nNarova is not a video-generation model and it does not impose a house style.\nYour agent—or you—owns the creative decisions. Narova provides the production\nsystem underneath them: structured scenes, deterministic timelines, local\nspeech and rendering, proof branches, incremental revisions, walkthrough\ncapture, provenance, release gates, and machine-readable inspection. Agents\ncalling the CLI directly can use the versioned\n[`--json` protocol](AGENT_PROTOCOL.md) without parsing terminal prose.\n\n[![License: Apache-2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](./LICENSE)\n[![Version](https://img.shields.io/badge/version-0.57.0-4fd9e8.svg)](./package.json)\n[![npm](https://img.shields.io/npm/v/@narova/narova?color=f2418a&label=npm)](https://www.npmjs.com/package/@narova/narova)\n[![Site](https://img.shields.io/badge/site-ammar--hasan.github.io%2Fnarova-f2418a.svg)](https://ammar-hasan.github.io/narova/)\n\n<a href=\"https://raw.githubusercontent.com/ammar-hasan/narova-assets/74c9f225512b22140b92ff7fbfd615031ec8306e/explore/narova-skill-reel/video.mp4\">\n  <img src=\"https://raw.githubusercontent.com/ammar-hasan/narova-assets/74c9f225512b22140b92ff7fbfd615031ec8306e/demos/readme-preview/preview.gif\" alt=\"narova demo — prompt, voice, motion\" width=\"100%\">\n</a>\n\n*This 30-second reel was made by Narova, about Narova. [Watch it with sound](https://raw.githubusercontent.com/ammar-hasan/narova-assets/74c9f225512b22140b92ff7fbfd615031ec8306e/explore/narova-skill-reel/video.mp4) · [Explore the project](https://ammar-hasan.github.io/narova/explore/) · [Install from npm](https://www.npmjs.com/package/@narova/narova) · [View on skills.sh](https://skills.sh/ammar-hasan/narova) · [Live site](https://ammar-hasan.github.io/narova/)*\n\n</div>\n\n- [Why Narova exists](#why-narova-exists)\n- [Mental model](#mental-model)\n- [What makes it different](#what-makes-it-different)\n- [What you can make](#what-you-can-make)\n- [Try it — one command](#try-it-one-command)\n- [Agent quickstart](#agent-quickstart)\n- [Direct CLI workflow](#direct-cli-workflow)\n- [Creative proofs and branches](#creative-proofs-and-branches)\n- [Deterministic iteration and releases](#deterministic-iteration-and-releases)\n- [Judge, assertions, and evidence](#judge-assertions-and-evidence)\n- [Sources, assets, walkthroughs, and provenance](#sources-assets-walkthroughs-and-provenance)\n- [Renderers and speech](#renderers-and-speech)\n- [Machine protocol](#machine-protocol)\n- [Architecture](#architecture)\n- [Installation and reference](#installation-and-reference)\n- [License](#license)\n\n## Why Narova exists\n\nGenerating a video once is becoming easy. **Continuing to direct that video is\nstill hard.**\n\nA generated clip usually throws away most of the production state that created\nit. Changing one decision can mean starting over. Agents cannot reliably tell\nwhat changed, what was actually rendered, which source supported a claim,\nwhether a previous experiment was better, or which artifacts belong to the\nfinal release.\n\nNarova treats video as an **evolving production project** instead of a\ndisposable generation. A project is a folder with a reviewable scene script and\nthe measured production state around it — manifest, timings, audio\nfingerprints, a revision ledger, proof bundles, provenance registers, and named\nreleases. Every render is addressed by identity, so unchanged work is reused,\nchanged work is limited to exactly what changed, and the encoded result can be\ninspected against the intent that produced it.\n\n## Mental model\n\n```\nintent\n  ↓\ncreative direction\n  ↓\nsmall visual proofs\n  ↓\nstructured production\n  ↓\npreview + direction\n  ↓\ndeterministic render\n  ↓\ninspect the encoded result\n  ↓\nrevise / branch / release\n```\n\nA Narova project moves through: `source → brief → scene script → proof →\nsynthesis → compose → preview → build → judge → revision → release`. Each stage\nremains inspectable and scriptable.\n\nYou (or your agent) own **creative authorship**: the script, the visuals, the\ntaste, the selection. What you build and publish is your choice, and you are\nfully answerable for it — under law, by your own ethics, values, and conscience,\nand by the religious or cultural commitments you hold. Narova gives you\ninspectable sources, reviewable creative proofs, and explicit provider choice to\nsupport that judgment; it does not verify legality, rights, or acceptability for\nyou. Narova owns the production machinery underneath: **timing, orchestration,\nrendering, caching, inspection, and delivery**. It is zero-style by default — no\nimplicit palette, layout, chrome, or camera grammar. Every aesthetic choice is\nan explicit one.\n\n## What makes it different\n\n**Creative control without a built-in taste model.** Narova is zero-style by\ndefault. It gives the creator and the agent a raw production canvas rather than\nsilently deciding layouts, palettes, camera language, typography, or visual\ntaste. Narova owns timing, orchestration, rendering, caching, inspection, and\ndelivery; creative authorship stays with you.\n\n**Prove an idea before producing the whole thing.** For ambitious work, Narova\npreserves several small, orthogonal visual proofs rather than generating several\ncomplete videos. Each proof has a rationale and rendered evidence. Choose\ndeliberately, restore the selected production state, and expand only that\ndirection.\n\n**Change the part you meant to change.** Narration, scenes, walkthrough takes,\nand rendered outputs carry identities that let Narova reuse unchanged work. A\nvisual change does not require regenerating narration. A changed scene does not\nrequire re-rendering every scene. Branches preserve experiments instead of\nturning them into lost prompts.\n\n**Inspect evidence, not an invented quality score.** `narova judge` examines the\nencoded artifact against creator-authored assertions. It can measure timing,\nmotion, silence, captions, hierarchy, continuity evidence, and other observable\nproperties while keeping uncertainty explicit. It does not reduce creative work\nto a universal score or automatically \"fix\" creative decisions.\n\n**Built for agents without being dependent on one.** Humans get readable CLI\noutput. Agents can request the versioned `narova.result/1` JSON protocol, with\nstable operations, diagnostics, exit classes, and artifact records. Narova works\nthrough an agent skill or directly from the CLI, and the project remains\nordinary local files either way.\n\n## What you can make\n\nThe system is not limited to one input or one output. Any of these are\nfirst-class starting points or scene materials:\n\n- Explainers and social video\n- Product launch films\n- Real browser walkthroughs\n- Repository and technical overviews\n- Research and source-grounded videos\n- Multi-speaker dialogue\n- Silent motion pieces and marker-driven events\n- HTML/CSS/SVG motion design\n- Three.js/WebGL 3D and mixed compositing\n- AI-generated clips through explicitly selected providers (Sora, Runway)\n- Stock, original, and generated media combined in one timeline\n- Platform presets and SRT/VTT delivery\n\nNothing above requires a particular renderer, voice vendor, or cloud service.\nNarova renders through either of its two local renderers, speaks through local\nTTS or an explicitly registered hosted provider, and treats AI clip generation\nas one optional source of scene media — not the product itself.\n\n## Try it — one command\n\nWith Node 18+ as the only download prerequisite, the demo builds a real\nnarrated, captioned MP4 on your machine and leaves the project behind as a\nworking reference:\n\n```bash\nnpx @narova/narova demo\n```\n\nThe first run shows a readiness checklist with live progress, then fetches what\nthis machine is missing — a digest-verified ffmpeg (on pinned Linux platforms),\nthe pinned local voice, the renderer engine, and the Python speech environment —\ninto Narova user storage. No keys, no configuration, no decisions; nothing is\ninstalled on system paths. The completion report states the measured\ntime-to-first-video and network bytes. Reference budget, advisory only: about a\nminute on a modern laptop with a typical broadband link (measured 2026-08-19:\n47 s cold on macOS arm64, 3 s warm; the clean-machine Linux CI job prints its\nown measurement on every run). Re-running reuses everything.\n\nOn macOS, install ffmpeg yourself first (`brew install ffmpeg`) — Narova\nauto-provisions it only where a digest-verified source is pinned.\n\n## Agent quickstart\n\nInstall the CLI and the agent skill, then ask your agent for the video in\nnormal language:\n\n```bash\nnpm install --global @narova/narova\nnpx skills add ammar-hasan/narova --skill narova -g\n```\n\nNarova supports different starting points:\n\n- **Idea:** \"Make a 45-second vertical explainer about why starting small makes\n  habits easier to keep. Make it warm and practical, and show me a preview\n  before rendering.\"\n- **Product page:** \"Turn this product page into a 30-second LinkedIn launch\n  video: `[product URL]`. Lead with the user outcome and use the site's visual\n  identity.\"\n- **Product walkthrough:** \"Explore our demo account, then make a 45-second\n  sales walkthrough that creates a project and shows the finished result.\n  Narrate it, add word-synced captions and a CTA, and show me the preview.\"\n- **Research:** \"Turn this paper into a 60-second explainer: `[paper URL]`.\n  Separate the authors' findings from inference, cite the source on screen, and\n  keep the language accessible.\"\n- **Repository:** \"Read this repository and make a 45-second technical\n  overview: `[repository URL]`. Explain what it does, show the architecture,\n  and end with how to get started.\"\n- **Script or dialogue:** \"Turn the script below into a fast two-host vertical\n  reel. Keep the exchange natural, use distinct caption colors, and let the\n  visuals change with each speaker.\"\n\nThe exact instruction the agent needs is: *\"Make a 45-second launch video from\nthis product page. Show me a small proof of the visual direction before full\nproduction, then let me direct the result.\"*\n\n## Direct CLI workflow\n\nThe CLI is available when you want to inspect or automate each step yourself:\n\n```bash\nnarova init generated/myreel && cd generated/myreel\n# complete the medium-neutral creative brief; keep each direction to a small proof\nnarova check        # validate the config + estimate narration length\nnarova synth        # makes narration + word timings\nnarova compose && narova shots --motion --proof  # reject an invisible pilot\nnarova branch save proof-a --rationale \"why this direction may serve the brief\"\nnarova preview --detach   # direct the film in HyperFrames Studio\nnarova build --release    # fail-before-render final gate → out/video.mp4\nnarova judge        # inspect intent vs the encoded result; read-only, scoreless\n```\n\nYou need: **Node 18+**, plus **ffmpeg** and **Python 3.10+** (the published\npackage self-provisions the Python speech environment and, on pinned platforms,\na digest-verified ffmpeg; a source checkout expects them on PATH).\n\n## Creative proofs and branches\n\nFor ambitious work, your agent turns medium-neutral creative intent into 2–3\nsmall proof branches, renders their decisive states, records why each might\nwork, and expands only the selected branch. You direct revisions in the same\nconversation; deterministic timelines, sentence and scene caching, and explicit\nbranches keep bold exploration surgically editable.\n\n```bash\nnarova branch save proof-a --rationale \"why this direction may serve the brief\"\n# repeat for proof-b/proof-c; approve + restore one; record its proof identity\n# in creative-brief.md, then expand only that config\nnarova branch compare proof-a proof-b   # actual evidence; Narova chooses nothing\n```\n\nProof bundles are bound to their originating project, so a same-named global\nbranch cannot satisfy another project's final release gate. Saving a focused\nproof also preserves the actual encoded bytes plus one assertion-linked\nobservation; comparing two or three verifies and displays the stored evidence in\nrequested order but never ranks, recommends, selects, restores, renders, or\nmutates. Rejected and archived attempts remain inspectable creative memory;\nbranch status is the creator's explicit decision.\n\n## Deterministic iteration and releases\n\nRebuilds are incremental and dependency-aware: `narova build` (and `--release`)\nre-renders only the scenes whose audiovisual work actually changed, reuses the\nrest, and re-places reused scenes when an earlier scene's duration shifts them.\nUnchanged scenes cause zero renderer frame evaluation; one scene-referenced\nasset edit invalidates only the scenes that reference it; an unused asset edit\ninvalidates nothing. The build prints what rebuilt, what reused, and why, plus\ndirty-unit seconds and renderer invocation counts. Audio-only changes (bed/SFX\nor a voice with unchanged timing) re-mux audio over reused spans without\nre-rendering. If safe reuse cannot be proven, Narova rebuilds conservatively and\nsays why — it never serves a stale frame. The incremental result is\naudiovisually equivalent to a forced-clean build (decoded frames, frame\ncounts/boundaries, audio/sync, captions, duration).\n\nAuthored motion keeps that locality when it uses the scope it actually needs:\nattach one-scene JavaScript with `scene.choreographyFile`; reserve top-level\n`choreography` for behavior that intentionally coordinates the full\ncomposition. Global choreography remains unrestricted and valid, but an\nisolated browser renderer conservatively uses whole-video reuse because it\ncannot prove arbitrary project code is scene-local.\n\nRevisions are recorded as changes in effective authored state. `narova diff`\nreports the per-scene revision impact with predicted reuse and an honest render\nestimate; `narova history` lists, annotates, and compares the append-only\nproject ledger; every successful base build binds a measured reuse record into\n`out/revisions.jsonl`. Named releases (`narova release save/list/restore/remove`)\nare content-hashed snapshots you can compare, restore, and remove.\n\n## Judge, assertions, and evidence\n\n`narova judge` is a read-only rendered-evidence mirror. It inspects the existing\nencoded video against creator-authored assertions — explicit requirements,\nunusual hypotheses, deliberate violations, continuity, accessibility, brand,\nfactual constraints — and exposes measured motion, silence, timing, spatial\nhierarchy, and uncertainty, mapped back to production state. It emits no\nuniversal score (`score: null`), changes no project validity, mutates nothing,\ncalls no hosted model, and accesses no network. Stillness, silence, darkness,\nand abrupt change may be deliberate; uncertainty is preferable to an\nunevidenced defect.\n\n`narova judge --plan` adds plural, unranked intervention options for each\nassertion-linked divergence or uncertainty — always including keeping the work\nunchanged — without selecting or executing one. One deliberately bounded repair\nis available for missing or invalid caption sidecars (`narova judge --repair`),\nwhich publishes only an isolated, unapproved proof-branch candidate; creative\nfindings remain creator decisions. None of these surfaces emits a universal\nquality score or quietly changes current production.\n\nSee the exact assertion, judge, plan, and repair contracts in\n[`skills/narova/references/cli.md`](skills/narova/references/cli.md).\n\n## Sources, assets, walkthroughs, and provenance\n\n**Source-grounded claims.** Every factual claim in the narration must trace to\na source. Narova's `check` detects a supported class of numeric and superlative\nclaims and requires them to be ledged (`claims.md`); release checking fails\nunledgered claims. Agents interpret selected sources; `ingest` handles two\nbounded mechanical passes: an HTTP(S) page fetch with up to five images and an\noptional browser screenshot, or explicitly selected physical pages from a\nregular local PDF as PNG evidence plus available embedded text. PDF intake does\nnot follow links, execute actions, use OCR, interpret claims, or choose creative\nmaterial. These heuristics prove neither that every claim was detected nor that\na source is correct; for contested topics the ledger should preserve the major\nrelevant perspectives.\n\n**Assets and provenance.** The small asset lifecycle stays explicit:\n\n```bash\nnarova assets providers\nnarova assets search \"home\" --provider iconify --kind image --limit 5\nnarova assets acquire mdi:home --provider iconify --kind image \\\n  --output assets/home.svg\nnarova assets download \"https://cdn.example/clip.mp4\" --output assets/clip.mp4 \\\n  --origin stock --provider example --source-page \"https://example/items/clip\" \\\n  --license CC-BY-4.0 --attribution \"Creator / Example\"\nnarova assets import assets/logo.svg --origin original\nnarova assets verify\nnarova assets credits --format youtube   # or text|web|json\nnarova provenance                        # graded project trust report\n```\n\n`narova provenance` composes the evidence Narova already has into four read-only\nsections: claim grounding, tracked media and rights buckets, AI generation, and\nreproducibility. Every fact is labeled verified (artifact-backed), declared\n(authored statement), or unknown; missing records stay visible instead of\nbecoming green checks. The report is advisory and offline. It does not prove\nlegal permission or exact used-asset closure, and it never changes the project.\n\nCore owns every deterministic adapter: Wikimedia, Openverse, NASA, Internet\nArchive, Iconify, Poly Haven, The Met, Cleveland Museum, Library of Congress,\nPexels, Pixabay, and Freesound. The first nine need no key; the other three are\noptional and appear unavailable until their environment key is present. The\nseparate `narova-stock-extensions` skill is LLM-led discovery for long-tail\ncatalogues. Creative sourcing is not limited to the built-in catalogues: if an\nagent finds a better asset through a browser, archive, or new source, it can use\n`assets download` or `assets import` and preserve the same provenance record.\n`ingest`, AI `generate`, and walkthrough capture register their outputs\nautomatically; builds consume local files only and never acquire media.\n\n**AI clips as scene material.** When a project wants a generated shot, `narova\ngenerate \"<prompt>\"` asks an explicitly registered video companion to produce\na clip, then commits it to the project asset registry with a generation recipe,\nso it participates in the same provenance, cache identity, and release evidence\nas any other media. Sora lives in `narova-openai`; Runway lives in the separate\n`narova-runway` skill; Google Veo lives in the separate `narova-google` skill.\nVendor APIs and credentials never enter core. This is an\noptional scene input, never the product itself, and the network is used only\nwhen generation is invoked.\n\nGenerated shots can also use a small creator-owned continuity plan in the\nexisting project config. Name the characters, objects, or places that matter;\nselect what one shot should keep or change; and optionally attach one image\nanchor when the chosen provider declares support:\n\n```js\ncontinuity: {\n  entities: {\n    amina: { kind: \"character\", description: \"Amina in a teal scarf\" },\n    lantern: { kind: \"object\", description: \"brass lantern with blue glass\" },\n  },\n  shots: {\n    arrival: {\n      entities: [\"amina\", \"lantern\"],\n      keep: [\"same scarf and lantern\"],\n      change: [\"Amina raises the lantern\"],\n      // anchor: \"assets/arrival-reference.png\",\n    },\n  },\n}\n```\n\n```bash\nnarova generate \"Amina enters the observatory\" \\\n  --continuity arrival --output assets/arrival.mp4\n```\n\nNarova validates the selected context before paid work, fails rather than\nsilently dropping an unsupported anchor, and records the exact prompt,\ncontinuity snapshot, and anchor identity in recipe version 3. Regeneration\nretains it unless the author replaces it or uses `--no-continuity`. Narova does\nnot infer a world, choose style or camera, rank candidates, or claim that a\nprovider honored the reference in its pixels.\n\n**Real product walkthroughs.** Explore a website/app semantically, record\ncursor-guided actions on narration beats, frame or full-bleed the real UI, then\nlayer captions, callouts, branding, music, and SFX around it.\n\n```bash\nnpm install -g agent-browser && agent-browser install  # optional adapter\nnarova walkthrough explore onboarding   # inspect real semantic controls\nnarova synth                             # establishes measured narration timing\nnarova walkthrough capture onboarding   # explicit live action + WebM/evidence\nnarova preview --detach\nnarova shots --beats\nnarova build --reuse --release\n```\n\nRecipes prefer roles, labels, test ids, and visible text over brittle\ncoordinates. Captures are invalidated when narration timing or actions change;\nbody/theme/window/full presentation edits reuse them. Authentication belongs in\na dedicated browser restore/profile, never a scripted password. See the full\n[walkthrough contract](skills/narova/references/product-walkthroughs.md).\n\n[Watch the complete 83-second narrated walkthrough generated by this workflow.](https://raw.githubusercontent.com/ammar-hasan/narova-assets/74c9f225512b22140b92ff7fbfd615031ec8306e/explore/product-walkthrough/video.mp4)\n[Watch the voiced, captioned real-browser click proof.](https://raw.githubusercontent.com/ammar-hasan/narova-assets/74c9f225512b22140b92ff7fbfd615031ec8306e/demos/click-highlight-proof/video.mp4)\n\n## Renderers and speech\n\nNarova ships two free local renderers. HyperFrames is the default and\nfull-fidelity path: arbitrary scene HTML/CSS, the complete HyperFrames\ncomponent/effects surface, browser Studio, and captured walkthrough composition.\nNarova No-Browser is the browserless path: it draws a provider-neutral\n`scene.visual` tree with Skia and encodes it with FFmpeg. Both run on your\nmachine with no render service or fee.\n\n```bash\nnarova renderers list\nnarova build --renderer no-browser\nnarova preview --renderer no-browser   # writes out/preview-no-browser.mp4\n```\n\nSee the exact [renderer capability contract](skills/narova/references/renderers.md).\n\nLocal speech ships as built-in backends; optional hosted speech and video\nproviders are separate, explicitly registered companions — never dependencies.\nKeep credentials in the provider's required environment variables, never in\nthe config.\n\n| Backend | Quality | Speed | Setup | Notes |\n|---------|---------|-------|-------|-------|\n| `piper` | good | fast | none (default) | small local voices |\n| `xtts`  | higher | slow | `narova-setup --xtts` | ~1.9GB model, 58 speakers |\n| `qwen`  | high | slow | `narova-setup --qwen` | ~1.2GB model, Apache 2.0, 9 speakers |\n| `chatterbox` | voice cloning | slowest | `narova-setup --chatterbox` | `speaker` = absolute path to a 10–20s recording; own venv, ~1GB model |\n| `pockettts` | listening unverified | model/machine dependent | `narova-setup --pockettts` | local CPU, own Python 3.12 runtime, 27 presets, seven languages; cloning needs full-model access |\n\n```bash\nnarova providers add <provider-manifest.json>\nnarova providers doctor <name>\nnpx skills add ammar-hasan/narova --skill narova-elevenlabs -g\nnpx skills add ammar-hasan/narova --skill narova-openai -g\nnpx skills add ammar-hasan/narova --skill narova-runway -g\nnpx skills add ammar-hasan/narova --skill narova-google -g\nnpx skills add ammar-hasan/narova --skill narova-xiaomi -g\n```\n\nFor authored 3D work that needs intentional subject/world representation or\nspecialist direction, install the independent\n[`narova-3d-production`](skills/narova-3d-production/) companion:\n\n```bash\nnpx skills add ammar-hasan/narova --skill narova-3d-production -g\n```\n\nIt is a concise, high-freedom direction layer with conditional references. For\nwork where gravity, collision, support, or rigid constraints carry the idea, it\nalso includes a separately installed deterministic rigid-body baker that writes\nproject-local time-indexed transforms. The baker is optional, adds no Narova\ncore dependency or renderer behavior, and core Narova works identically\nwithout it.\n\n## Machine protocol\n\nAgents are first-class API consumers. Every public operation honors `--json`\nand returns exactly one versioned `narova.result/1` envelope on stdout — no\nprose to parse. The protocol provides a stable exit-status vocabulary\n(`success` / `operation-failure` / `usage-error` / `subject-non-pass`), a\nstable diagnostic-code registry, declared artifact records for paths the\noperation created or replaced, and additive-only schema evolution within schema\nmajor version 1. Redaction is a protocol property: known secrets are redacted\nfrom the envelope and from captured child-process boundaries.\n\n```bash\nnarova check --json\nnarova build --json\nnarova judge --json\n```\n\nThe canonical agent loop is inspect → modify authoring source → validate →\npreview → critique → build → perceive the encoded result → verify. See\n[`AGENT_PROTOCOL.md`](AGENT_PROTOCOL.md) for the full operation payloads,\ndiagnostic registry, and loop.\n\n## Architecture\n\n```\ncreative-brief.md\n   │  2–3 small proof branches → rendered evidence → select one\n   ▼\nreel.config.mjs\n   │\n   ▼  synth      Python makes the speech and the word timings.\n   │             Timings are scaled to match the real audio exactly.\n   ▼  capture    Optional: agent-browser records declared product actions\n   │             against measured narration anchors. Never runs in build.\n   ▼  compose    narova writes the selected local renderer project:\n   │             scene visuals, karaoke captions, reveals, one timeline.\n   ▼  render     HyperFrames or No-browser renders the mp4 with audio inside.\n   │\nout/video.mp4\n   │\n   ▼  judge      read-only rendered evidence ↔ authored intent; no score or repair\n```\n\n`out/`, `out/hf-*`, and `out/no-browser-*` are build folders. Never edit them.\nThe config remains the render source of truth; the approved brief records why\nthe creative direction is ready to scale.\n\n### The scene script\n\nA project is a folder with `reel.config.mjs` as its render source of truth. New\nprojects also include `creative-brief.md`; approve its pilot gate before\nreleasing a non-trivial production.\n\n```js\nexport default {\n  title: \"My Reel\",\n  renderer: \"hyperframes\",                    // default; or \"no-browser\"\n  size: \"16:9\",                              // \"16:9\" | \"1:1\" | \"9:16\"\n  safeLayout: true,                          // optional centering/gutters/max-width/caption reserve\n  assets: \"assets\",                          // copied into out/hf/assets/\n  voices: {\n    a: { backend: \"piper\", speaker: \"en_US-ryan-high\",         color: \"#2ee6d6\", label: \"host · A\" },\n    b: { backend: \"piper\", speaker: \"en_US-hfc_female-medium\", color: \"#ff7eb6\", label: \"host · B\" },\n  },\n  provenance: {                              // optional authored declarations\n    script: { authorship: \"mixed\", note: \"agent draft, human review\" },\n    disclosure: \"Contains AI-generated media\",\n  },\n  assertions: [{                             // optional creator-owned Video CI intent\n    id: \"restrained-opening\",\n    class: \"creative-hypothesis\",\n    expect: \"The opening should feel restrained; only the cue reveal should move.\",\n    origin: { kind: \"agent-hypothesis\", ref: \"opening proof\" },\n    scope: { scene: \"title\" },\n    observe: [\n      { metric: \"video.static_ratio\", operator: \"gte\", value: 0.7 },\n      { metric: \"video.cut_count\", operator: \"lte\", value: 1 },\n    ],\n    riskyBecause: [\"unconventional stillness\"],\n    questions: [\"Did unintended motion weaken the restraint?\"],\n    related: { scene: \"title\", source: \"reel.config.mjs\", protected: [\"camera rhythm\"] },\n  }],\n  sceneState: [{                             // optional hidden mechanical/spatial evidence\n    scene: \"title\", file: \"evidence/title.state.json\",\n  }],                                        // bound to built video; never changes rendering\n  theme: { accent: \"#2ee6d6\", bg: \"#080d16\" },   // optional\n  timing: { gapSentence: 0.24, gapTurn: 0.44, lead: 0.16, tail: 0.58, tempo: 1.12 },\n  scenes: [\n    {\n      id: \"title\",\n      // For a generated clip with visibly synchronized dialogue, an agent can\n      // choose the preserved source performance instead of synthesizing `vo`:\n      // clip: \"assets/title.mp4\",\n      // dur: 8,\n      // clipAudio: {\n      //   authority: \"native\", role: \"dialogue\",\n      //   rationale: \"The visible delivery requires lip sync.\",\n      //   wordTimings: \"assets/title.words.json\", // optional; one cue/vo turn\n      // },\n      vo: [                                   // what is SPOKEN, in order\n        { who: \"a\", text: \"This is narova.\" },\n        { who: \"b\", text: \"Scenes in, video out. Let's go.\" },\n      ],\n      body: `<div class=\"s-title\">\n        <h1 class=\"display reveal\">narova</h1>\n        <p class=\"lede cue\" data-cue=\"1\">scenes in, video out</p>\n      </div>`,\n    },\n  ],\n}\n```\n\nThe rules:\n\n- `vo` is the spoken dialogue. Each turn is `{ who, text }`.\n- Direct clips remain visual-only unless `clipAudio` records a choice. Use\n  `authority: \"native\"` when the source performance itself is authoritative\n  (especially generated visible dialogue requiring lip sync), or\n  `authority: \"synthesis\"` to document a deliberate replacement. How the file\n  was downloaded is not a decision rule. Native authority requires `clip`, an\n  explicit `dur`, and a non-empty rationale; native dialogue additionally\n  requires the matching `vo` transcript. Native audio plays once and any\n  remaining scene time is silence.\n- `body` is HTML for the screen.\n- `data-cue=\"1\"` means: stay hidden until turn 1 starts. Counting starts at 0.\n- `class=\"reveal\"` means: animate in when the scene starts.\n- You never set durations for spoken scenes — the real audio decides how long\n  each scene is. Silent and some external-narration scenes use explicit\n  authored/resolved durations, and named markers are another first-class timing\n  source.\n- Styling is optional. Set colors with `theme`, or add a CSS file with\n  `theme: { css: \"theme.css\" }`. Do not use `animation: ... infinite` in that\n  CSS. The renderer jumps between frames, so looping animations break.\n- Put logos, images, and local fonts in `assets/`; scene HTML and theme CSS\n  reference them as `assets/...`. Inline SVG and small data URIs also work.\n  Remote render-time files do not.\n- Word-level timing is estimated by default (distributed across the measured\n  sentence duration) and becomes measured when the optional alignment path\n  succeeds; captions and cue reveals follow the measured turn/sentence clock.\n\n`assertions` describe what the creator intends to survive into the finished\nartifact. Free-form expectations remain creator-owned prose; only explicit\n`observe` probes are mechanically compared. Assertions do not change rendering,\ncache identity, proof validity, or release eligibility.\n\n### Multi-video series\n\nUse `narova series pin <source> --episode <id>` to keep different shared\nrevisions in fresh CI checkouts without tracking shared file copies in every\nepisode. Commit the catalog pin and series store; builds verify and materialize\nits complete selected dependencies. See [pinning and migration](skills/narova/references/series.md#pin-shared-inputs-once-for-a-fresh-ci-checkout).\n\nCourses, vlogs and drama can share voices, characters, pronunciation, theme/caption defaults,\nselected local resources and creator-owned context. Episodes stay independent:\neach retains its selected shared bytes, and later edits affect only explicit\nadopters. Catalog relationships and authored handoffs never force render order.\nCreate/catalog the episode, then use `narova series build <source> --episode <id>`\nto copy its selected shared inputs and build in one command. Later builds keep\nthat saved revision; `--update-shared` explicitly adopts current inputs and builds\nonly that episode. Local files and overrides stay its own. Ordinary `build` also\nconsumes a saved binding without reading the live series.\nCI selects episodes independently: track shared originals, episode sources and\nselection recipes, then run the combined command after checkout. Derived copies\nneed not be committed; ignore membership and binding together. To preserve an\nolder shared revision, commit membership plus the complete current binding/files.\nIn a reused workspace, request `--update-shared` to pick up current originals.\n\nAn episode can mix shared fonts, CSS and images with local footage/assets and\nlocal overrides. Theme/caption properties merge by key; a matching local\nvoice/character ID replaces the entire shared record. Font files and custom CSS\nare selected resources with explicit episode references; shared theme defaults\ncarry mode/tokens, not stylesheet files. See the guide's worked mixed example.\n\n\nStart with the [complete two-episode example](skills/narova/references/series.md#start-with-two-episodes).\nIt needs a ready CLI/no-browser runtime and uses shared/local SVGs without voices,\nfonts, CSS or continuity settings. The ordinary workflow is:\n\n```bash\n# After creating the catalog and episode files in the example:\nnarova series build course --episode intro --resources logo\n# Repeat with the same saved shared inputs.\nnarova series build course --episode intro --reuse\n# Deliberately pick up edited shared originals for Intro only.\nnarova series build course --episode intro --update-shared --reuse\nnarova series --help\n```\n\n`logo` selects a catalog resource; the episode's scene reference applies the\nretained file. See [series authoring](skills/narova/references/series.md) for\noptional fonts/CSS, voice order, removals, continuity, restoration, exchange and\nstandalone detachment. Legacy `series: {part,total}` remains display metadata.\n\n### Narrated-series controls\n\nKeep optional alignment settings in the project with\n`align: { engine: \"faster-whisper\", model: \"base.en\", partial: true }`.\nExplicit settings override environment defaults; alignment requires the chosen\noptional engine. Hyphenated authored words can match split measured words.\n\nScene effects can follow speech rather than copied timestamps:\n\n```js\nsfx: [{ scene: \"title\", file: \"assets/chime.wav\",\n  at: { sentence: 0, word: 2, offset: 0.05 },\n  start: 1.5, duration: 0.8, fadeIn: 0.05, fadeOut: 0.2, volume: 0.6 }],\n```\n\nSentence and word indices start at zero within the selected scene. `start`\nselects the source offset; `duration` and fades shape that selected interval.\nThese controls work with synthesized and external narration. For a scene with\ncustom typography, set `captions: false` on that scene: visual captions disappear,\nwhile SRT/VTT exports and cue timing remain available. Zero-length word and\nsentence cues are valid instants. Speech at tempo 1 retains its original speed,\nand production bed/SFX mixes preserve stereo.\n\nSee the [audio reference](skills/narova/references/audio.md) and\n[choreography reference](skills/narova/references/choreography.md) for details.\n\n### Product walkthroughs\n\nProduct demos add a driver-neutral `walkthroughs` recipe and point scenes at\nit:\n\n```js\nexport default {\n  // voices, theme, …\n  walkthroughs: {\n    onboarding: {\n      url: \"https://app.example.com/projects\",\n      viewport: { w: 1440, h: 900 },\n      ready: { text: \"New project\" },\n      steps: [\n        { at: { scene: \"create\", cue: 0, offset: 0.25 },\n          action: \"click\",\n          target: { role: \"button\", name: \"New project\" } },\n        { at: { scene: \"create\", cue: 0, offset: 1.1 },\n          action: \"type\",\n          target: { label: \"Project name\" },\n          value: \"Launch plan\" },\n      ],\n    },\n  },\n  scenes: [{\n    id: \"create\",\n    walkthrough: \"onboarding\", // or { id, layout: \"full\", fit: \"cover\" }\n    vo: [{ who: \"a\", text: \"Create a project and give it a clear name.\" }],\n    body: `<p class=\"eyebrow reveal\">From idea to workspace</p>`,\n  }],\n}\n```\n\n## Installation and reference\n\n### The two-part install\n\nNarova has two parts: the CLI and the agent skill. Install the CLI first from\nthe public npm package:\n\n```bash\nnpm install --global @narova/narova\nnarova doctor\n```\n\nThe npm package does not change agent skills. Releases from `0.31.1` onward\npublish through npm Trusted Publishing with provenance linking the package to\nthis public repository and its release workflow. The manually bootstrapped\n`0.31.0` release has an npm registry signature but no provenance attestation.\n\nThen install the skill for any agent that supports skills. If the CLI is\nmissing or its version differs, the skill installs its exact matching published\nnpm version before use:\n\n[![skills.sh](https://skills.sh/b/ammar-hasan/narova)](https://skills.sh/ammar-hasan/narova)\n\n```bash\nnpx skills add ammar-hasan/narova --skill narova -g\n# check for updates: npx skills update narova -g (only when you're ready — upgrading replaces the skill files)\n```\n\nThe CLI and skill are distributed separately, but the skill is authoritative\nfor compatibility: after a skill update, its next session verifies\n`narova --version` and reconciles the global CLI to the exact pinned release.\nInstalling another CLI version manually does not change the skill; the next\nskill session restores the skill's pinned version.\n\n### Update or remove\n\n```bash\nnpm install --global @narova/narova@latest\n```\n\nRemove the CLI with `narova-uninstall` (it detects a custom install prefix\nautomatically; `--purge-tools` also removes the TTS venv, provisioned media\ntools, and the voice cache — projects, media assets, and voice samples are\nalways kept). The skill is updated separately with `npx skills update narova -g`.\n\n### Commands\n\n```\nnarova init <dir>     new project\nnarova demo           build the one-command sample film end to end\nnarova pack           write a deterministic .narova project archive\nnarova open <archive> verify, inspect, or materialize an untrusted archive\nnarova remix <source> copy a local project/archive or public github: locator\nnarova ingest <url>   bounded HTTP(S) source pass → sources.md/claims.md\nnarova ingest <pdf> --pages <1,3-5>   selected local PDF evidence\nnarova check          validate the config (fast, no side effects) + claim sniffing\nnarova plan           compare config vs the last manifest; classify what changed\nnarova diff           per-scene revision impact vs the latest recorded revision\nnarova history        list/annotate/compare the append-only revision ledger\nnarova synth          make the audio + word timings\nnarova walkthrough    explore/capture/status narrated product demos\nnarova compose        make the selected renderer project\nnarova captions       rewrite SRT/VTT from existing timings\nnarova shots          snapshot QA frames per scene, motion sample, or narration beat\nnarova build          synth + compose + render -> out/video.mp4\nnarova preview        HyperFrames Studio, or a no-browser draft MP4\nnarova preview --detach   keep Studio alive; stop with preview --stop\nnarova judge          inspect the encoded result against creative assertions\nnarova judge --plan   add plural, unranked intervention options; change nothing\nnarova judge --repair create only a delegated caption-sidecar proof candidate\nnarova branch save    optionally preserve a rendered proof for one assertion\nnarova branch compare compare 2–3 preserved proofs; no ranking or selection\nnarova branch set     record the proof decision (approved/rejected/archived)\nnarova review         observability suite (visual, timing, mix, and delivered-audio facts)\nnarova critique       optional craft guidance; not a correctness gate\nnarova generate       AI clip generation via an explicitly selected provider\nnarova voices         list or download voices\nnarova providers      add/list/remove/doctor external speech/video providers\nnarova renderers      list/doctor the two bundled local renderers\nnarova assets         import/download/search/acquire/verify/credits\nnarova provenance     graded project trust report (verified/declared/unknown)\nnarova release        save/list/restore/remove content-hashed named releases\nnarova doctor         check your machine\n```\n\nCommands find the project from any folder inside it (they walk up to the\nnearest `reel.config.*`). `check` also prints an estimated narration length, so\na target duration can be tuned before any audio exists.\n\nUseful flags: `--backend <built-in-or-registered-provider>`,\n`--renderer hyperframes|no-browser`, `--reuse` (keep old audio),\n`--tempo`, `--size`, `--fps`, `--quality draft|standard|high`,\n`--video <file>` (`judge` only), `--deliverables` (per-platform export presets\nvia scale+pad), `--variants`, `--json` (machine protocol). See\n[`skills/narova/references/cli.md`](skills/narova/references/cli.md) for the\nfull list.\n\n### Repo layout\n\n```\ntool/              CLI package: installer, Node/Python runtime, vendors, tests\nskills/narova/     agent instructions and references\nskills/narova-*/   optional production, sourcing, and voice companions\ndocs/              the marketing site (GitHub Pages) + /changelog + /explore\ndocs/explore/projects/  local verification mirrors of reviewed Explore projects\ndocs/public-assets.json pinned mapping to the CC0 Narova asset repository\nCHANGELOG.md       every notable change, version by version\nAGENT_PROTOCOL.md  versioned JSON envelopes, exit codes, and the agent loop\nVISION.md          the product vision, mapped to where each point is implemented\nLEARNINGS.md       non-normative implementation notes for public maintainers\n```\n\nRun everything with `npm test`, or test the CLI independently with\n`npm test --prefix tool`.\n\n### Release version contract\n\nThe root `package.json` is the only canonical Narova version. Prepare a release\non a branch with `npm version patch|minor|major --no-git-tag-version`; the npm\nversion lifecycle runs `version:sync` and propagates that value to the CLI\npackage, lockfile, skill metadata, exact npm compatibility pin, badge, spec,\nagent protocol, and website markers. Add the dated changelog entry, then require\n`npm run release:check` and CI before merging.\n\nAfter the release PR lands on `main`, tag that merge as `v<version>`. The tag\nworkflow rejects version drift and off-main tags, then publishes the matching\nnpm package through Trusted Publishing. skills.sh reads the skill from GitHub;\nusers receive it with `npx skills update narova -g`, and the updated skill\nreconciles the CLI to its exact pinned npm release before use.\n\n## License\n\nApache-2.0 — see [LICENSE](./LICENSE). Changes are tracked in\n[CHANGELOG.md](./CHANGELOG.md) and on the\n[changelog page](https://ammar-hasan.github.io/narova/changelog/).\n\nFirst-party Explore projects and demo media are published separately under\n[CC0 1.0](https://github.com/ammar-hasan/narova-assets) for unrestricted reuse;\nno attribution or proof of use is required.\n\n### Narration controls and portable speech\n\nUse turn `pauseAfter` for authored silence, turn `captions: false` to hide visual\nwords while retaining subtitles, and root `mix.loudness` for optional final mix\nnormalization. Literal word selectors can anchor effects and choreography by\ntext plus occurrence. `narova voice-cache export/import` carries selected sentence\nWAVs for matching keyless builds. See the [audio guide](skills/narova/references/audio.md).\nThe [ElevenLabs companion guide](skills/narova-elevenlabs/references/current-offerings.md)\ncovers v4/v4 turbo, phrasing context and optional hosted MCP authoring.\n\n\n### Script-to-speech checks\n\n`narova review --speech` reports per-turn transcripts and dropped, added or\nreplaced words from existing synthesized takes. Optional root\n`speech: { check: 'warn', retakes: 1 }` checks builds and re-synthesizes only\nmismatched turns with the next take nonce. Use `check: 'fail'` to stop before\nrendering when speech differs or recognition is unavailable. Warn retains the\nlatest completed candidate when recognition becomes unavailable. Explicit checks\nrequire current take records; replacing selected local model bytes with a positive\nretake budget starts fresh selection while reusing eligible sentence audio. Review remains\nadvisory and read-only; optional local ASR tools are required. Transcripts can\nmishear, so audition flagged turns. See the [speech check guide](skills/narova/references/audio.md#check-synthesized-speech-against-the-script).\n\n### Pocket TTS\n\n[Pocket TTS](skills/narova/references/pockettts.md) is a built-in optional local\nCPU backend alongside Piper, XTTS, Qwen and Chatterbox. It offers 27 presets,\nseven languages, reference cloning and saved voice states. Run\n`narova-setup --pockettts`, then select `backend: 'pockettts'` per voice or\n`--backend pockettts`. No separate skill or provider registration is required.\nSetup uses an isolated Python 3.12 environment and leaves other backends intact.\nPocket TTS is included in Narova 0.52.0 and later.\nFull-model access is required for cloning; it never silently switches to presets.\n\n`narova voices list --backend pockettts` and `narova pockettts catalog` need no\nPocket runtime or downloads. `narova pockettts doctor` explicitly checks the\nselected model; `narova pockettts export-voice --speaker alba --output alba.safetensors`\nsaves a compatible state. Narova retains timing, captions, processing and rendering.\nModel helpers show progress and default to a 120-second deadline; set\n`NAROVA_POCKETTTS_TIMEOUT` for slower acquisition. Failed exports preserve the\nexisting state file, and catalog metadata identifies preview models.\n\nPocket and external voices can declare\n`providerFiles: { voiceState: 'assets/voice.safetensors' }`. Core resolves and\nhashes files and inspects the Pocket runtime profile before reuse; manifests and\nsentence identities carry that evidence. See the\n[Pocket configuration guide](skills/narova/references/pockettts.md).\n\n\n## Pronunciation without changing captions\n\nKeep the real spelling in each turn's `text`. Add an explicit root dictionary\nfor speech input on any backend, including Pocket TTS:\n\n```js\npronounce: { 'CLAUDE.md': 'Claude Em Dee', 'Claude Code': 'Clawd Code' },\n```\n\nCaptions, SRT/VTT and word cues retain `CLAUDE.md`; the voice receives `Claude Em\nDee`. Matching is literal and case sensitive, outside letters, combining marks,\nnumbers and underscores. The longest matching entry wins; replacements do not\ntrigger other replacements. Clean sentences are split first, so replacements\ncannot change sentence/cue ownership or span sentence boundaries. Existing\nexternal-only `synthesisText` still selects the provider input first; the map\nthen applies to its sentences (or clean text after a sentence-count fallback).\nSupplied external/native audio is unchanged. Empty/unused maps retain speech\nreuse; an effective change replaces affected sentence keys.\n\nA speech check records clean `expectedText` and changed `spokenText`, compares\nagainst the spoken form, and keeps the raw recognizer transcript. ASR may write\nan applied term as its clean spelling, such as `Claude MD`; only clean/spoken\npairs actually applied to that turn are accepted as spelling equivalents.\nRecognition remains uncertain evidence; audition flagged turns. Alignment still\nuses clean caption tokens and may retain estimates when the spoken words differ.\n\nA series catalog can put the same dictionary in `defaults.pronounce`. Episodes\ninherit its frozen selection, override individual keys in their own `pronounce`,\nand remove an inherited literal key with\n`seriesOverrides: { remove: { pronounce: ['CLAUDE.md'] } }`. Empty episode maps\ninherit; removal is explicit. Repin/adopt to change existing episodes; detach\nretains the effective ordinary map. No runtime dictionary service is needed.\n"}