Communitygithub.com

product-video

Record and produce the demo video of a website, app, or feature with the vos CLI — the agent scripts the click path (actions.json), the recording auto-plans zooms, and every editing decision is data in doc.json, so fixes are edits and re-renders, never re-recordings. Renders are deterministic; export is free up to 4K with no watermark. Use when asked to make a product video, demo video, screen recording of a URL or feature, or a marketing clip, or when a feature was verified in agent-browser and that walk should become a take. A whole release

product-video とは?

product-video is a Cursor agent skill that record and produce the demo video of a website, app, or feature with the vos CLI — the agent scripts the click path (actions.json), the recording auto-plans zooms, and every editing decision is data in doc.json, so fixes are edits and re-renders, never re-recordings. Renders are deterministic; export is free up to 4K with no watermark. Use when asked to make a product video, demo video, screen recording of a URL or feature, or a marketing clip, or when a feature was verified in agent-browser and that walk should become a take. A whole release.

対応~Claude Code~Codex CLICursor
npx skills add https://github.com/vosjs/skills/tree/main/skills/product-video

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Product video (and the assets around it), end to end

You are producing a shippable product asset — a polished video, a set of exact-size stills, or both — from a live web page, with the vos CLI. Everything is data: you write a flow script, the CLI records and plans, you tune JSON, you re-render. Never re-record to fix pacing or zooms — edit doc.json and render again. Quality bar: references/taste.md — follow its quality loop and judge stills multimodally.

Setup

npm i -D @vosjs/cli

One command, one package: @vosjs/cli is the open source (MIT) vos binary with the engine verbs, the take pipeline used here (record / plan / frames / render on screen recordings) and the vos.so platform verbs. (Until 0.9 the take pipeline shipped separately as @vosso/vos-plugin; a project that still lists it can drop it.) Requirements:

  • A Chromium: system Chrome is found automatically; otherwise npx playwright install chromium or point VOS_BROWSER_PATH at one. Exit code 3 means no browser was found.
  • Network at render time: the render page loads three/mediabunny from esm.sh. Recording also needs to reach the target URL. Fully offline sandboxes cannot render — say so instead of shipping nothing.

Conventions: logs → stderr, results → stdout; --json streams NDJSON ending with {"event":"done",…}; exit codes 0 ok / 1 error / 2 usage or strict failure / 3 no browser.

Step 0 — pick the destination (it decides everything)

DestinationViewportOutputExtras
Landing-page clip (hero/section embed)2560×1440 (footage-native 2K)webm → VP9 re-encode + postersilent loop; see references/destinations.md
Launch video (PH, social, store promo)2560×1440 or 1280×720mp4 (--format mp4, needs system Chrome)music bed via doc.audio
Launch-kit stills (store screenshots, tiles, OG)sized to the assetframes --frame <t> --size WxH PNGs from the same takeone take → every asset
Quick demo (issue, PR, chat)1280×720webm, defaultsspeed over polish; drafts acceptable here ONLY

Per-channel dimensions and byte budgets: references/destinations.md.

The core loop (every destination)

  1. Explore the target page with your own tools (fetch HTML / Playwright). Identify the 3–6 moments that tell ONE story. Collect STABLE selectors (a[href='…'], ids, roles — not nth-child chains). Stage the content like a set: the script must leave the product in the state a proud screenshot would show — labels typed, real-looking data, the feature mid-story. An empty canvas records fast and demos nothing, and no downstream composition rescues it.

    Verified the feature with agent-browser already? Keep that walk and skip the second script. agent-browser's --json result does not say what ran (scroll answers {scrolled:true}), so wrap each call so the command rides beside its result, then convert:

    ab() { agent-browser "$@" --json | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const r=JSON.parse(s);process.stdout.write(JSON.stringify({command:process.argv.slice(1),...r})+"\n")})' -- "$@" >> steps.jsonl; }
    ab open https://target.example; ab snapshot -i -u; ab click @e27
    ab wait 800; ab snapshot -i          # the page changed: refs renumbered
    ab fill @e2 query; ab press Enter
    vos actions from-agent-browser steps.jsonl --out actions.json
    

    (a whole path run as one agent-browser batch … --json > steps.jsonl already has that shape). The log is steps.jsonl in the directory you stand in, so walk from one directory. Refs resolve through the last snapshot -i before them, and they RENUMBER after a navigation and again inside a dialog: re-snapshot after anything that changes the page and read the refs before you name one (-u gives links their href). Click the control, do not press its shortcut: ⌘K opens the dialog for a human, but a keystroke is the one step the recorder cannot replay. Whatever it cannot follow (a shortcut key, a drag, a second open) is NAMED in the output, never dropped: read the notes, write those steps by hand, then record.

  2. Write actions.json:

    {
      "url": "https://target.example",
      "viewport": { "width": 1280, "height": 720 },
      "steps": [
        { "do": "wait", "ms": 800 },
        { "do": "hover", "selector": "a[href='/pricing']", "ms": 700 },
        { "do": "click", "selector": "#cta" },
        { "do": "wait", "ms": 1500 },
        { "do": "scroll", "dy": 400 },
        { "do": "move", "x": 640, "y": 320 },
        { "do": "wait", "ms": 900 }
      ]
    }
    

    Verbs: wait hover click type scroll move drag (drag = real edits: {do:'drag', selector|x,y, tx, ty, ms} — slide a range input, drag a canvas element, move a timeline clip). Pacing IS the zoom plan: open wait ≥700ms; hover what matters 700–900ms (dwells become zooms); 1200–2000ms after navigations; end settled. Route the cursor away from hover-triggered menus (taste.md, flow rules). Check with vos validate actions.json.

  3. Record: vos record --actions actions.json --out take --strict --json --strict always: skipped selector / networkidle timeout → exit 2 with skipped[] in the done event. A skip means the flow is broken — fix it, never ship around it. The take auto-encodes and auto-plans. (vos create --actions actions.json out.webm --strict is the one-shot record+render verb — fine for a quick first pass, but THIS skill's loop reviews frames before rendering, so prefer the separate verbs here.)

  4. Tune doc.json (JSON Schema ships in the @vosjs/cli npm package: schema/doc.schema.json):

    • zoom: [{in, out, level, cx, cy, source}], SOURCE seconds; levels 1.4–2.8; cx/cy NORMALIZED [0..1] (0.5,0.5 = center) — NOT pixels; set "source": "manual" on spans you touch (survives re-plan).
    • segments (trims) · speed (rate 0.1–16) · frame.* · cursor.
    • tilt: [{in, out, rx, ry, source}], SOURCE seconds — the 3D card leans to the pose while active, returns to rest between. DEGREES (±5..18 reads premium): +rx = top edge closer, +ry = left edge closer (lean toward a right-side focus = negative ry). Spans ≥ 0.8s; pair with zoom moments (same in/out chains the moves), one pose change per ~5s beat. "source": "manual" on spans you touch; tiltStyle: "subtle"|"medium"|"strong" records the auto wand.
    • frame.backgroundMedia: a video loop / image behind the card — {"kind":"video","key":"/bg.webm","duration":10,"dim":0.2}. key = a file dropped in the take dir ("/bg.webm") or a media URL; video needs duration (OUTPUT-anchored modulo loop); dim 0..1 scrim. Ambience, not a subject — dim it behind dense UI.
    • audio: OUTPUT-anchored clips; key may be a file dropped into the take dir ("/music.mp3"); gain/fades/loop. Muxed on full renders (Opus/AAC); --range stays silent; forces single-flight.
    • export: {"resolution": "720p|1080p|2k|4k", "fps": 30} — never above the footage (validate warns). Then vos validate take --json — lints must pass.
  5. Look before you render (the taste.md quality loop):

    • vos frames take --at-zooms --times 0,25%,50%,75%,100% --json → judge every still against taste.md, zoom apexes hardest.
    • Iterate: edit doc.json → vos render take check.webm --range a..b --draft (seconds, half res — never ship drafts) → re-frame the changed region.
    • Trying a presentation? Use a flag, not a scratch script. render/frames take doc overrides — --set <path>=<value> (repeatable; JSON-or-string), --frame <macos|windows|minimal|none> (render), --background <url> — that patch the doc in memory (doc.json untouched) and are lint-gated. So vos frames take --frame 2.0 --set frame.browserBar.kind=mac-light --set tilt[0].rx=8 previews a framed, tilted card without touching the file.
  6. Final render: vos render take out.webm --json (or --format mp4). Re-frame the final (frames --at-zooms) against taste.md before declaring done. Renders are deterministic — only your edits change the output.

6b. Human review round (when the ask involves one): vos open take serves the take into the studio — your doc.json edits arrive intact and every zoom span is draggable.

  1. Package for the destination: references/destinations.md.

Launch kit (one take → every store asset)

The launch-kit skill owns this destination: it establishes the release, loops every channel against channel-specs.json, verifies each artifact against its spec, writes the kit.json manifest and pushes labelled for the release. The mechanics it loops are this skill's: vos frames take --frame <t> --size WxH per still spec, the mp4 render for video. Follow it when the ask is a release, not one video.

Gotchas

  • A WebGL-heavy page (a shader background, a 3D canvas) paints BLACK under headless Chromium's software GL, and the recording has no way to say so: the take looks right except for a dead canvas. Record such pages with VOS_BROWSER_PATH pointing at system Chrome (a real GPU), and check the digest's sheet for the canvas before cutting.
  • Render time ≈ 1.5× real-time at 1080p (a 12.5s take ≈ 19s; ~5s fixed startup); --parallel N pays off on takes ≳30s (ignored when audio rides); 2K ≈ 2× per-frame cost. Recording is always real-time.
  • Footage resolution = viewport size — decide 2K at RECORD time, from the DESTINATION's specs (a 720p take cannot honestly fill a 1080p video spec). Coordinate steps (x/y/drag) are VIEWPORT pixels: a viewport change means scaling every coordinate; selectors survive.
  • vos plan take regenerates only source:"auto" spans; manual spans survive.
  • Take dirs: frames/ is a deletable encode intermediate (~1GB at 2K); recording.webm is the re-render source — keep it.
  • More failure modes: references/troubleshooting.md.

Avoid (the traps that shipped)

  • A zoom that opens before the click it frames: validate names it ("points beside what was clicked"); start the span after the click, or aim at it.
  • A focus point in pixels: cx/cy are fractions of the frame.
  • A type verb's field click as a zoom target: the field's centre is empty; frame the text, and open the span after the click.
  • Routing the cursor through a hover-triggered menu between beats.
  • A first frame or a last frame that cannot stand alone as a poster.
  • A frozen opening: a static landing page records as freeze-then-bang; trim it or speed it, the story opens near the money shot.
  • A store screenshot cut from the composed frame: real UX is the page, full bleed (deliver does this; a text-heavy page also wants a store-size take).
  • A .png name on vos still: it writes WebP; convert, and vos validate <kit.json> reads the bytes.

Individual skills in this repo

This repo contains 7 individual skills — each has its own dedicated page.

launch-kit

Ship the media with the release — one take of the real product becomes the demo video, the store listing (Chrome Web Store screenshots, tile, marquee), the Product Hunt gallery, the social cuts and the OG card, each a frame of a poster document in the brand

vos-authoring

Author a vos animation program (VosConfigJson) from scratch or debug one — declarative configs compiling into deterministic Three.js + GSAP-dialect animations on the MIT vos engine (@vosjs/core), validated locally with compileVosConfig. Covers the schema, dialect rules, 2D overlay elements, shaders, and params/presets knobs. Use when asked to write a vos animation program, create a VosConfigJson, make a programmatic motion graphic or logo animation, or fix a config that fails to compile or lint.

vos-create

Create vos programs in the user

vos-cut

Cut a screen recording (a vosso take) into the product video it was recorded for, from its evidence, with the vos CLI — read the take

vos-footage

Hand a Remotion, HyperFrames, or any video composition a clean captured clip of the real product — recorded and auto-zoomed with the vos CLI, delivered as full-bleed footage with no chrome, and always backed by an editable take on the maker

vos-migrate

Turn an existing Loom, Screen Studio, Cap, or plain mp4/webm screen demo into an editable vosso take — re-encode the file, synthesize the take

vos-remix

Remix a vos.so gallery program into your own — fetch its config, edit the declared knobs or code locally, validate with vos check, and push a private copy back with lineage that iterates as versions. Carries the params/Looks doctrine and the GLB 3D-showcase recipe. Use when asked to remix, customize or personalize a gallery animation, swap a 3D model into a showcase, or iterate a fetched program as versions.

関連スキル