[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6BY0538NE2KRP6SWS094ZBT":3,"skill-same:01M6BY0538NE2KRP6SWS094ZBT":75},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":47,"linkOnly":48,"markdownContent":49,"markdownBody":50,"frontMatter":51,"refName":52,"commitSha":53,"skillDirectory":54,"forksCount":55,"repositoryHtmlUrl":56,"repositoryHomepageUrl":57,"ownerHtmlUrl":58,"ownerBlogUrl":12,"ownerDescription":59,"updatedAt":60,"install":61,"resources":74},"01M6BY0538NE2KRP6SWS094ZBT","launch-commercial",null,"Turn a product page URL into a finished launch commercial — research, storyboard, rendered stills and clips, voice, music, an assembled timeline — delivered as an editable NodeTool project with the real cost. Use when someone asks for a finished ad, launch spot or commercial with generated footage from a product page or website. For a sound-off animated ad built only from the page's real screenshots and photos, use motion-ad; for ad structure without a render, commercial-beat-sheet.","nodetool-ai\u002Fnodetool","nodetool-ai","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F164873569?v=4","01M6BXPFDW36K9K1MR8ZH63HG2","nodetool.ai","01M6BXPFJ893XM219GQN60B0WK",628,"AGPL-3.0","https:\u002F\u002Fgithub.com\u002Fnodetool-ai\u002Fnodetool\u002Fblob\u002Fmain\u002Fpackages\u002Fsystem-skills\u002Flaunch-commercial\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000021","资料检索与研究","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],"launch","launch commercial","commercial","generated","when","beat-sheet","for","URL","render","as","project","structure","storyboard","only","screenshots","nodetool","page","music","voice","timeline","website","product","research","2026-11-10 10:12:30",false,"---\nname: launch-commercial\ndescription: Turn a product page URL into a finished launch commercial — research, storyboard, rendered stills and clips, voice, music, an assembled timeline — delivered as an editable NodeTool project with the real cost. Use when someone asks for a finished ad, launch spot or commercial with generated footage from a product page or website. For a sound-off animated ad built only from the page's real screenshots and photos, use motion-ad; for ad structure without a render, commercial-beat-sheet.\n---\n\n# Product Page → Launch Commercial Agent\n\nTake a product page URL and deliver a finished launch commercial — rendered clips, voice, music, an assembled cut — as an **editable NodeTool project** (storyboard + script + timeline), plus the real cost of making it from the ledger. Call the capability modules directly; build no workflow.\n\nThis skill owns the outer loop: research → brief → entities → board → renders → voice → music → cut → cost report. The dramaturgy inside the board — hooks, beat budget, timecodes, VO discipline — belongs to the `commercial-beat-sheet` skill. Load it at phase 3 and follow it there. Do not duplicate its rules; do not skip it.\n\nWhen the user wants a sound-off motion-graphics ad (a 15s feed ad, kinetic type, the product's real screenshots animated on the timeline, no generated footage), this is the wrong skill: load `motion-ad`.\n\n---\n\n## Role\n\nWork as an integrated producer: a URL and a runtime in, a shot spot out. Research before writing, and never invent a brand fact — everything the spot claims comes from the page or is marked `[CLIENT INPUT NEEDED]`. Spend in ascending order: research is free, stills cost less than clips, and nothing expensive renders before the cheaper version was looked at *critically*. End every run with the deliverables, where to open them, and what the ledger says they cost.\n\n---\n\n## The contract\n\nThe user gives you at minimum:\n\n```text\nPRODUCT PAGE URL:   [required]\nRUNTIME:            [15s \u002F 30s \u002F 60s — default 30s]\nPLATFORM:           [default: 9:16 for ≤30s, 16:9 above]\n```\n\nEverything else — brand, category, audience, pain, USP, proof, tone, CTA — you **derive from the page** and present back as a brief for sign-off. A field the page cannot answer is `[CLIENT INPUT NEEDED]`, never a guess.\n\nThe user gets back: a **storyboard** (still + clip on every shot), a **script** (every line voiced), a **timeline** that validates, and a **cost report** from the ledger. All four persist. The render is not the deliverable; the editable project is.\n\n---\n\n## Tool contract — read this before writing any code\n\nThe storyboard, entity and render call shapes live in `storyboard-core` § Tool contract: return fields, all fifteen `edit_storyboard` ops, every key `set_board` accepts, and what text each generator reads. `load_skill` it before phase 3. What this skill adds:\n\n**Headless only.** `ui_*` tools need a browser with that document open. From this skill you are headless: use `list_entities` \u002F `apply_entities`, never `ui_entity_list` \u002F `ui_entity_apply`.\n\n**Read ids by name.** `create_storyboard` → `.storyboard_id`. `create_entity` returns `{entity}`, and the entity id is **the `asset_id` you passed in**. `nodetool.media.generateImage` → `.asset_id`. `extract_script_from_storyboard` → `.script_id`. Never write `result.id || result.asset_id` and proceed. If an id is `undefined`, stop and read it back with `list_*` — do not pass it onward.\n\n**The board holds the default models.** `image_model` \u002F `video_model` on `set_board` take the `.ref` object `find_model` returns, and `render_storyboard_stills` \u002F `render_storyboard_clips` fall back to them. With no board model and no `provider` + `model` on the call, the render fails and says so — it never picks one. Set them on the board once in phase 3 and pass `provider` + `model` only for a shot that needs a different line.\n\n**Measure with `media_probe`.** It takes an `asset:\u002F\u002F` uri and reports width and height — that is the aspect check, cheap enough to run on every keyframe. Grade the picture itself with `view_image` and `score_image_adherence`.\n\n**`take_screenshot` renders the page in headless Chrome on the machine NodeTool runs on**, or through the remote browser service when one is configured. If it fails, report the error once and fall back to the page's own downloaded images.\n\n---\n\n## Spend gates\n\n| Gate | After | You show | Money spent |\n| --- | --- | --- | --- |\n| **G1 Brief** | Research | Derived brief, entity plan | $0 |\n| **G2 Stills** | Keyframes + your own adversarial grade | Every keyframe, defects named | the stills (from the ledger) |\n| **G3 Cut** | Clips + voice + music + assembly | Timeline, validation, ledger cost | the real number |\n\nNever render clips before G2 is approved. Never pick a model silently. If the user says \"run it end to end\", G1\u002FG2 become progress reports; the cost report is never optional. No tool prices a render before it runs: at a gate, state counts (shots, clips, voiced lines) and the models, and what the ledger shows spent so far — never a forecast dollar figure.\n\n---\n\n## Phase 1 — Research the page\n\n```js\nimport { browser, take_screenshot, download_file } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fweb\";\n\nconst page = await browser({ url: productPageUrl });\n```\n\nExtract into the brief:\n\n- **Facts** — name, what it does, price, materials, claims. Verbatim or `[CLIENT INPUT NEEDED]`.\n- **Voice** — how the brand writes. The VO inherits it.\n- **Visual identity** — read hex values out of the raw HTML (design tokens beat eyeballing a screenshot), plus a live screenshot from `take_screenshot`.\n- **Product imagery** — download the hero photos. They become the prop entity's reference. The real product is the one thing the model must not reimagine.\n- **The one pain** — pages list five benefits; the spot escalates one. Take the one the page leads with.\n\nThen fill the beat-sheet skill's brief format and present it at **G1** with the entity plan.\n\n---\n\n## Phase 2 — Entities from the page\n\nFollow the beat-sheet skill's entity rules (kinds, descriptors, style-first, roster size). What this skill adds:\n\n**Priority per entity:** library (`list_entities`) → page asset → generated.\n\n**The style entity comes from the page whenever the page has a look.** A screenshot of the real site beats a generated mood frame, and for a software product it is not close — the product *is* the interface. Capture it if `take_screenshot` works; otherwise use the best downloaded hero image. Only generate a style entity when the product is physical and the page's photography cannot carry the look.\n\n**But a screenshot style anchor has one failure mode, and it is severe.** Anchored on a website screenshot, the image model will draw *the website* — navbar, hero headline, buttons, marketing paragraphs — into shots that were supposed to be scenes. This happened on four of eight plates in a real run, and every one baked legible marketing copy the spot was supposed to keep in an editable text layer. So the descriptor separates look from content, and **every scene prompt carries hard negatives**:\n\n```text\nABSOLUTELY NO website page, NO navbar, NO marketing headline, NO gradient\ntitle text, NO buttons, NO paragraphs, NO wordmark or logo. No readable\ntext of any kind.\n```\n\nTwo shot classes, prompted differently:\n\n- **Product shots** (the mechanism, the proof) — the interface *is* the subject, edge to edge, no page around it. UI chrome (a status pill, a label) is fine; marketing copy is not.\n- **Scene shots** (hook, problem, CTA, closing) — describe the scene only. Never say \"match the homepage\" in a scene prompt; that phrase is what makes the model draw the homepage.\n\nEntity ids: pass `asset_id`, and use that same asset id as the entity id afterward.\n\n```js\nimport { create_entity, list_entities } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fentities\";\n\nconst styleAssetId = shot.asset_id;           \u002F\u002F the screenshot\nawait create_entity({ asset_id: styleAssetId, kind: \"style\", name: \"...\", descriptor: \"...\", palette: [...] });\nconst styleId = styleAssetId;                 \u002F\u002F entity id === asset id\n```\n\n---\n\n## Phase 3 — Beat sheet and board\n\nLoad the **commercial-beat-sheet** skill and run its execution path with the phase-1 brief and phase-2 roster: three hooks, a recommendation, the full beat sheet. Then:\n\n```js\nimport { create_storyboard, edit_storyboard } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fstoryboards\";\n\nconst board = await create_storyboard({ name, brief, style, aspect_ratio });\nconst boardId = board.storyboard_id;\n\nawait edit_storyboard({\n  storyboard_id: boardId,\n  ops: [{ op: \"set_board\", entity_ids: roster }]   \u002F\u002F set_board, not set_entities\n});\nfor (const shot of shots) {\n  await edit_storyboard({ storyboard_id: boardId, ops: [{ op: \"add_shot\", ...shot }] });\n}\n```\n\nSet `render_mode` per shot: `\"keyframe\"` where the product or a face must hold steady, `\"direct\"` for the one or two heavy-motion shots. A launch spot usually has exactly one direct shot.\n\nResolve the model slate **before the shots are written**, with `find_model` for `text_to_image` and `image_to_video`. Name what you picked, put each result's `.ref` on the board with `set_board {image_model, video_model}`, and `load_skill` the `prompting_skill` each result names: the shot `action` and `motion` you are about to write are the prompt, and each model line wants them shaped differently. On-screen copy stays out of the prompt and goes on as a text layer in phase 7.\n\n---\n\n## Phase 4 — Stills, then grade them adversarially (G2)\n\n```js\nimport { render_storyboard_stills, get_storyboard } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fstoryboards\";\n\nawait render_storyboard_stills({ storyboard_id: boardId });   \u002F\u002F uses the board's image_model\n```\n\nThen grade. `get_cost_summary` shows what the stills cost. **This is the step most likely to fail, and it fails as flattery.** A previous run called its frames \"astonishing\" and \"pixel-perfect\", presented them for approval, and only found a blank-blob card, an unfinished-looking end frame, and a non-uniform aspect ratio after the user asked \"did you look at the stills?\".\n\nThe discipline:\n\n1. **Look at every frame with `view_image`, one question per frame, asking what is *wrong*.** \"Critique honestly: what is crude, off-brand, or unfinished here?\" — never \"does this look good?\".\n2. **Score adherence** with `score_image_adherence({image, prompt})` where the shot prompt is specific enough to score against.\n3. **Compare frames to each other**, not just to their prompts. The defect that killed two frames in the real run was only visible next to a third that got it right.\n4. **Report per frame: keep, or the specific defect.** Banned words in a grade: stunning, astonishing, gorgeous, perfect, night-and-day. If you cannot name a defect, write \"checked: card fidelity, baked text, palette, framing — none found\".\n5. **Never claim a property you did not verify.** Measure the aspect ratio with `media_probe` on each keyframe's `asset:\u002F\u002F` uri rather than eyeballing it; a plate that came back in the wrong ratio is a defect to name at G2, and it is still standardized at assembly.\n\nTwo failure classes, two different fixes — never confuse them:\n\n- **Entity drift** (wrong product, wrong face, off-palette) → fix the *entity*: sharpen its descriptor or replace its reference, then re-render every shot using it.\n- **Shot failure** (composition, unreadable action, baked marketing copy) → fix the *shot*: rewrite its action line, re-render that shot.\n\nPresent all keyframes at G2 with the clip count and video model of phase 5. No tool prices the clips in advance; do not quote a number.\n\n---\n\n## Phase 5 — Clips\n\n```js\nawait render_storyboard_clips({ storyboard_id: boardId });   \u002F\u002F uses the board's video_model\n```\n\nA clip wrong where its keyframe was right is a **motion** problem: one `revise_storyboard_clip`, never a board-wide re-render. Cap at one revision per shot before flagging. Video models overshoot the requested length and can ignore a 9:16 source: `analyze_video` on each clip reports duration and frame before you assemble (`video-audio-continuity` § Check what came back).\n\n---\n\n## Phase 6 — Voice\n\n```js\nimport { extract_script_from_storyboard } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fstoryboards\";\nimport { get_script, voice_script_lines } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fscripts\";\n\nimport { find_model } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fmodels\";\nimport { edit_script } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fscripts\";\n\nconst script = await extract_script_from_storyboard({ storyboard_id: boardId });\nconst scriptId = script.script_id;\nconst tts = await find_model({ capability: \"text_to_speech\" });\n\u002F\u002F The extracted cast has no usable voice: Narrator comes back with none, a\n\u002F\u002F character with at most its entity's voice_id and no provider or model. Bind\n\u002F\u002F one per speaker, or every line reads no_voice.\nconst cast = (await get_script({ script_id: scriptId })).cast;\nawait edit_script({ script_id: scriptId, ops: cast.map((speaker) => ({\n  op: \"set_speaker_voice\", target: speaker.id,\n  provider: tts.ref.provider, model: tts.ref.id, voice: \"\u003Cthe provider's voice name>\"\n})) });\nawait voice_script_lines({ script_id: scriptId });\nconst voiced = await get_script({ script_id: scriptId });\n```\n\nPick a voice per speaker that fits the brand voice from phase 1. `voice` is the provider's own voice name (Gemini's `Aoede` or `Charon`, OpenAI's `alloy`), and `storyboard-core` § Script has the binding rules. Every line must come back `voiced`. A `stale` or `no_voice` line is a defect, not a warning. If a voiced line overruns its beat, cut the line — never speed the read.\n\n---\n\n## Phase 7 — Assemble the cut\n\n```js\nimport { assemble_storyboard_timeline } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fstoryboards\";\nimport { edit_timeline, preview_timeline_frame, validate_timeline } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Ftimelines\";\nimport { generate_music } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fmedia\";\n```\n\n1. Assemble the clips.\n2. **Standardize aspect here** — this is where mixed plate ratios get resolved. Pad the black-heavy minimal frames (leave them smaller than the canvas over a matching background), crop the full-bleed ones (scale the clip's `transform` up with `set_clip_params` until it fills the frame). Say which you did to which.\n3. Voice track, aligned to beats.\n4. Music bed at the spot's exact runtime, ducked under VO, with the beat sheet's deliberate pre-CTA silence kept silent.\n5. **Motion and text.** Supers, the CTA card and the end mark are a text layer over the cut, never a render prompt — no model is asked to letter them. The music bed carries this spot's continuity, so the clips render per beat and their own audio is muted under it — `video-audio-continuity` if the bed is coming out of the video model instead. `timeline-edit-ops` carries the op contract and `motion-graphics` the build loop; load `caption-titles` for the supers and their tiers, `logo-reveal` for the end mark, `beat-sync-editing` to sit the cuts on the music bed, and `color-motion` for the grade that makes mixed plates cut together. `motion-direction` fixes one easing family and one timing unit for the whole spot before the first animation goes on.\n6. **Overrun check.** Video models overshoot requested durations. If the cut runs long, trim the **last** clip — trimming an earlier one opens a gap and leaves the runtime unchanged.\n7. `validate_timeline` must come back ok. Fix and re-validate.\n8. Look at frames, not at the document: `preview_timeline_frame` at each cut and each text entry. A change nobody looked at is not done.\n\n---\n\n## Phase 8 — Report (G3)\n\n```js\nimport { get_cost_summary } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fcosts\";\n```\n\n```text\nDELIVERED\n  Storyboard \u002F Script \u002F Timeline — each openable and editable\n\nCOST  (from the ledger, not an estimate)\n  Stills \u002F Clips \u002F Voice \u002F Music \u002F Agent \u002F TOTAL\n\nOPEN QUESTIONS\n  Every [CLIENT INPUT NEEDED] still in the project\n```\n\n**Report ledger figures, never a forecast.** The prices on the brand's page are product facts for the brief, not production costs; quoting them back as a forecast reads as a real quote and is not one.\n\n---\n\n## Craft rules (on top of the beat-sheet skill's)\n\n* Every claim in VO or on-screen text traces to the page, or is `[CLIENT INPUT NEEDED]`.\n* The product on screen is the page's product. Drift is fixed at the entity.\n* The palette is the page's palette, read from its own tokens.\n* **No lettering the model must invent.** Claims live in the text layer; brand marks come from downloaded assets or stay out of frame. Generated legible marketing copy is a defect even when it looks good.\n* Spend ascending: read → still → clip → revision.\n* Name what was wrong before spending to fix it.\n\n---\n\n## When it goes wrong\n\n* **Page unreachable or thin** → report what you could and could not read, fill the brief with `[CLIENT INPUT NEEDED]`, stop at G1. Never fabricate a product from the URL's words.\n* **A capability errors on argument shape** → re-read the tool contract above before retrying. Two consecutive failures on the same call means read the state back with `list_*` \u002F `get_*` and work from what actually exists, not from what you think you created.\n* **A partial action left orphans** (a board with no shots, an entity with no roster) → find them with `list_*` and finish or delete them. Do not create a second one alongside.\n* **A model is unavailable or unpriced** → say so at the gate it blocks, with alternatives. Never substitute silently.\n* **A shot will not converge** (three renders, still failing adherence) → stop spending, show the best take with what is wrong, offer: accept, rewrite, or change `render_mode`.\n* **Budget cap set** → check the ledger with `get_cost_summary` before each render phase. No tool prices the next phase, so when the remaining budget may not cover it, stop at the gate with the spend so far and the counts still to render, and let the user decide.\n","# Product Page → Launch Commercial Agent\n\nTake a product page URL and deliver a finished launch commercial — rendered clips, voice, music, an assembled cut — as an **editable NodeTool project** (storyboard + script + timeline), plus the real cost of making it from the ledger. Call the capability modules directly; build no workflow.\n\nThis skill owns the outer loop: research → brief → entities → board → renders → voice → music → cut → cost report. The dramaturgy inside the board — hooks, beat budget, timecodes, VO discipline — belongs to the `commercial-beat-sheet` skill. Load it at phase 3 and follow it there. Do not duplicate its rules; do not skip it.\n\nWhen the user wants a sound-off motion-graphics ad (a 15s feed ad, kinetic type, the product's real screenshots animated on the timeline, no generated footage), this is the wrong skill: load `motion-ad`.\n\n---\n\n## Role\n\nWork as an integrated producer: a URL and a runtime in, a shot spot out. Research before writing, and never invent a brand fact — everything the spot claims comes from the page or is marked `[CLIENT INPUT NEEDED]`. Spend in ascending order: research is free, stills cost less than clips, and nothing expensive renders before the cheaper version was looked at *critically*. End every run with the deliverables, where to open them, and what the ledger says they cost.\n\n---\n\n## The contract\n\nThe user gives you at minimum:\n\n```text\nPRODUCT PAGE URL:   [required]\nRUNTIME:            [15s \u002F 30s \u002F 60s — default 30s]\nPLATFORM:           [default: 9:16 for ≤30s, 16:9 above]\n```\n\nEverything else — brand, category, audience, pain, USP, proof, tone, CTA — you **derive from the page** and present back as a brief for sign-off. A field the page cannot answer is `[CLIENT INPUT NEEDED]`, never a guess.\n\nThe user gets back: a **storyboard** (still + clip on every shot), a **script** (every line voiced), a **timeline** that validates, and a **cost report** from the ledger. All four persist. The render is not the deliverable; the editable project is.\n\n---\n\n## Tool contract — read this before writing any code\n\nThe storyboard, entity and render call shapes live in `storyboard-core` § Tool contract: return fields, all fifteen `edit_storyboard` ops, every key `set_board` accepts, and what text each generator reads. `load_skill` it before phase 3. What this skill adds:\n\n**Headless only.** `ui_*` tools need a browser with that document open. From this skill you are headless: use `list_entities` \u002F `apply_entities`, never `ui_entity_list` \u002F `ui_entity_apply`.\n\n**Read ids by name.** `create_storyboard` → `.storyboard_id`. `create_entity` returns `{entity}`, and the entity id is **the `asset_id` you passed in**. `nodetool.media.generateImage` → `.asset_id`. `extract_script_from_storyboard` → `.script_id`. Never write `result.id || result.asset_id` and proceed. If an id is `undefined`, stop and read it back with `list_*` — do not pass it onward.\n\n**The board holds the default models.** `image_model` \u002F `video_model` on `set_board` take the `.ref` object `find_model` returns, and `render_storyboard_stills` \u002F `render_storyboard_clips` fall back to them. With no board model and no `provider` + `model` on the call, the render fails and says so — it never picks one. Set them on the board once in phase 3 and pass `provider` + `model` only for a shot that needs a different line.\n\n**Measure with `media_probe`.** It takes an `asset:\u002F\u002F` uri and reports width and height — that is the aspect check, cheap enough to run on every keyframe. Grade the picture itself with `view_image` and `score_image_adherence`.\n\n**`take_screenshot` renders the page in headless Chrome on the machine NodeTool runs on**, or through the remote browser service when one is configured. If it fails, report the error once and fall back to the page's own downloaded images.\n\n---\n\n## Spend gates\n\n| Gate | After | You show | Money spent |\n| --- | --- | --- | --- |\n| **G1 Brief** | Research | Derived brief, entity plan | $0 |\n| **G2 Stills** | Keyframes + your own adversarial grade | Every keyframe, defects named | the stills (from the ledger) |\n| **G3 Cut** | Clips + voice + music + assembly | Timeline, validation, ledger cost | the real number |\n\nNever render clips before G2 is approved. Never pick a model silently. If the user says \"run it end to end\", G1\u002FG2 become progress reports; the cost report is never optional. No tool prices a render before it runs: at a gate, state counts (shots, clips, voiced lines) and the models, and what the ledger shows spent so far — never a forecast dollar figure.\n\n---\n\n## Phase 1 — Research the page\n\n```js\nimport { browser, take_screenshot, download_file } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fweb\";\n\nconst page = await browser({ url: productPageUrl });\n```\n\nExtract into the brief:\n\n- **Facts** — name, what it does, price, materials, claims. Verbatim or `[CLIENT INPUT NEEDED]`.\n- **Voice** — how the brand writes. The VO inherits it.\n- **Visual identity** — read hex values out of the raw HTML (design tokens beat eyeballing a screenshot), plus a live screenshot from `take_screenshot`.\n- **Product imagery** — download the hero photos. They become the prop entity's reference. The real product is the one thing the model must not reimagine.\n- **The one pain** — pages list five benefits; the spot escalates one. Take the one the page leads with.\n\nThen fill the beat-sheet skill's brief format and present it at **G1** with the entity plan.\n\n---\n\n## Phase 2 — Entities from the page\n\nFollow the beat-sheet skill's entity rules (kinds, descriptors, style-first, roster size). What this skill adds:\n\n**Priority per entity:** library (`list_entities`) → page asset → generated.\n\n**The style entity comes from the page whenever the page has a look.** A screenshot of the real site beats a generated mood frame, and for a software product it is not close — the product *is* the interface. Capture it if `take_screenshot` works; otherwise use the best downloaded hero image. Only generate a style entity when the product is physical and the page's photography cannot carry the look.\n\n**But a screenshot style anchor has one failure mode, and it is severe.** Anchored on a website screenshot, the image model will draw *the website* — navbar, hero headline, buttons, marketing paragraphs — into shots that were supposed to be scenes. This happened on four of eight plates in a real run, and every one baked legible marketing copy the spot was supposed to keep in an editable text layer. So the descriptor separates look from content, and **every scene prompt carries hard negatives**:\n\n```text\nABSOLUTELY NO website page, NO navbar, NO marketing headline, NO gradient\ntitle text, NO buttons, NO paragraphs, NO wordmark or logo. No readable\ntext of any kind.\n```\n\nTwo shot classes, prompted differently:\n\n- **Product shots** (the mechanism, the proof) — the interface *is* the subject, edge to edge, no page around it. UI chrome (a status pill, a label) is fine; marketing copy is not.\n- **Scene shots** (hook, problem, CTA, closing) — describe the scene only. Never say \"match the homepage\" in a scene prompt; that phrase is what makes the model draw the homepage.\n\nEntity ids: pass `asset_id`, and use that same asset id as the entity id afterward.\n\n```js\nimport { create_entity, list_entities } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fentities\";\n\nconst styleAssetId = shot.asset_id;           \u002F\u002F the screenshot\nawait create_entity({ asset_id: styleAssetId, kind: \"style\", name: \"...\", descriptor: \"...\", palette: [...] });\nconst styleId = styleAssetId;                 \u002F\u002F entity id === asset id\n```\n\n---\n\n## Phase 3 — Beat sheet and board\n\nLoad the **commercial-beat-sheet** skill and run its execution path with the phase-1 brief and phase-2 roster: three hooks, a recommendation, the full beat sheet. Then:\n\n```js\nimport { create_storyboard, edit_storyboard } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fstoryboards\";\n\nconst board = await create_storyboard({ name, brief, style, aspect_ratio });\nconst boardId = board.storyboard_id;\n\nawait edit_storyboard({\n  storyboard_id: boardId,\n  ops: [{ op: \"set_board\", entity_ids: roster }]   \u002F\u002F set_board, not set_entities\n});\nfor (const shot of shots) {\n  await edit_storyboard({ storyboard_id: boardId, ops: [{ op: \"add_shot\", ...shot }] });\n}\n```\n\nSet `render_mode` per shot: `\"keyframe\"` where the product or a face must hold steady, `\"direct\"` for the one or two heavy-motion shots. A launch spot usually has exactly one direct shot.\n\nResolve the model slate **before the shots are written**, with `find_model` for `text_to_image` and `image_to_video`. Name what you picked, put each result's `.ref` on the board with `set_board {image_model, video_model}`, and `load_skill` the `prompting_skill` each result names: the shot `action` and `motion` you are about to write are the prompt, and each model line wants them shaped differently. On-screen copy stays out of the prompt and goes on as a text layer in phase 7.\n\n---\n\n## Phase 4 — Stills, then grade them adversarially (G2)\n\n```js\nimport { render_storyboard_stills, get_storyboard } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fstoryboards\";\n\nawait render_storyboard_stills({ storyboard_id: boardId });   \u002F\u002F uses the board's image_model\n```\n\nThen grade. `get_cost_summary` shows what the stills cost. **This is the step most likely to fail, and it fails as flattery.** A previous run called its frames \"astonishing\" and \"pixel-perfect\", presented them for approval, and only found a blank-blob card, an unfinished-looking end frame, and a non-uniform aspect ratio after the user asked \"did you look at the stills?\".\n\nThe discipline:\n\n1. **Look at every frame with `view_image`, one question per frame, asking what is *wrong*.** \"Critique honestly: what is crude, off-brand, or unfinished here?\" — never \"does this look good?\".\n2. **Score adherence** with `score_image_adherence({image, prompt})` where the shot prompt is specific enough to score against.\n3. **Compare frames to each other**, not just to their prompts. The defect that killed two frames in the real run was only visible next to a third that got it right.\n4. **Report per frame: keep, or the specific defect.** Banned words in a grade: stunning, astonishing, gorgeous, perfect, night-and-day. If you cannot name a defect, write \"checked: card fidelity, baked text, palette, framing — none found\".\n5. **Never claim a property you did not verify.** Measure the aspect ratio with `media_probe` on each keyframe's `asset:\u002F\u002F` uri rather than eyeballing it; a plate that came back in the wrong ratio is a defect to name at G2, and it is still standardized at assembly.\n\nTwo failure classes, two different fixes — never confuse them:\n\n- **Entity drift** (wrong product, wrong face, off-palette) → fix the *entity*: sharpen its descriptor or replace its reference, then re-render every shot using it.\n- **Shot failure** (composition, unreadable action, baked marketing copy) → fix the *shot*: rewrite its action line, re-render that shot.\n\nPresent all keyframes at G2 with the clip count and video model of phase 5. No tool prices the clips in advance; do not quote a number.\n\n---\n\n## Phase 5 — Clips\n\n```js\nawait render_storyboard_clips({ storyboard_id: boardId });   \u002F\u002F uses the board's video_model\n```\n\nA clip wrong where its keyframe was right is a **motion** problem: one `revise_storyboard_clip`, never a board-wide re-render. Cap at one revision per shot before flagging. Video models overshoot the requested length and can ignore a 9:16 source: `analyze_video` on each clip reports duration and frame before you assemble (`video-audio-continuity` § Check what came back).\n\n---\n\n## Phase 6 — Voice\n\n```js\nimport { extract_script_from_storyboard } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fstoryboards\";\nimport { get_script, voice_script_lines } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fscripts\";\n\nimport { find_model } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fmodels\";\nimport { edit_script } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fscripts\";\n\nconst script = await extract_script_from_storyboard({ storyboard_id: boardId });\nconst scriptId = script.script_id;\nconst tts = await find_model({ capability: \"text_to_speech\" });\n\u002F\u002F The extracted cast has no usable voice: Narrator comes back with none, a\n\u002F\u002F character with at most its entity's voice_id and no provider or model. Bind\n\u002F\u002F one per speaker, or every line reads no_voice.\nconst cast = (await get_script({ script_id: scriptId })).cast;\nawait edit_script({ script_id: scriptId, ops: cast.map((speaker) => ({\n  op: \"set_speaker_voice\", target: speaker.id,\n  provider: tts.ref.provider, model: tts.ref.id, voice: \"\u003Cthe provider's voice name>\"\n})) });\nawait voice_script_lines({ script_id: scriptId });\nconst voiced = await get_script({ script_id: scriptId });\n```\n\nPick a voice per speaker that fits the brand voice from phase 1. `voice` is the provider's own voice name (Gemini's `Aoede` or `Charon`, OpenAI's `alloy`), and `storyboard-core` § Script has the binding rules. Every line must come back `voiced`. A `stale` or `no_voice` line is a defect, not a warning. If a voiced line overruns its beat, cut the line — never speed the read.\n\n---\n\n## Phase 7 — Assemble the cut\n\n```js\nimport { assemble_storyboard_timeline } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fstoryboards\";\nimport { edit_timeline, preview_timeline_frame, validate_timeline } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Ftimelines\";\nimport { generate_music } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fmedia\";\n```\n\n1. Assemble the clips.\n2. **Standardize aspect here** — this is where mixed plate ratios get resolved. Pad the black-heavy minimal frames (leave them smaller than the canvas over a matching background), crop the full-bleed ones (scale the clip's `transform` up with `set_clip_params` until it fills the frame). Say which you did to which.\n3. Voice track, aligned to beats.\n4. Music bed at the spot's exact runtime, ducked under VO, with the beat sheet's deliberate pre-CTA silence kept silent.\n5. **Motion and text.** Supers, the CTA card and the end mark are a text layer over the cut, never a render prompt — no model is asked to letter them. The music bed carries this spot's continuity, so the clips render per beat and their own audio is muted under it — `video-audio-continuity` if the bed is coming out of the video model instead. `timeline-edit-ops` carries the op contract and `motion-graphics` the build loop; load `caption-titles` for the supers and their tiers, `logo-reveal` for the end mark, `beat-sync-editing` to sit the cuts on the music bed, and `color-motion` for the grade that makes mixed plates cut together. `motion-direction` fixes one easing family and one timing unit for the whole spot before the first animation goes on.\n6. **Overrun check.** Video models overshoot requested durations. If the cut runs long, trim the **last** clip — trimming an earlier one opens a gap and leaves the runtime unchanged.\n7. `validate_timeline` must come back ok. Fix and re-validate.\n8. Look at frames, not at the document: `preview_timeline_frame` at each cut and each text entry. A change nobody looked at is not done.\n\n---\n\n## Phase 8 — Report (G3)\n\n```js\nimport { get_cost_summary } from \"@nodetool-ai\u002Fsandbox-nodetool\u002Fcosts\";\n```\n\n```text\nDELIVERED\n  Storyboard \u002F Script \u002F Timeline — each openable and editable\n\nCOST  (from the ledger, not an estimate)\n  Stills \u002F Clips \u002F Voice \u002F Music \u002F Agent \u002F TOTAL\n\nOPEN QUESTIONS\n  Every [CLIENT INPUT NEEDED] still in the project\n```\n\n**Report ledger figures, never a forecast.** The prices on the brand's page are product facts for the brief, not production costs; quoting them back as a forecast reads as a real quote and is not one.\n\n---\n\n## Craft rules (on top of the beat-sheet skill's)\n\n* Every claim in VO or on-screen text traces to the page, or is `[CLIENT INPUT NEEDED]`.\n* The product on screen is the page's product. Drift is fixed at the entity.\n* The palette is the page's palette, read from its own tokens.\n* **No lettering the model must invent.** Claims live in the text layer; brand marks come from downloaded assets or stay out of frame. Generated legible marketing copy is a defect even when it looks good.\n* Spend ascending: read → still → clip → revision.\n* Name what was wrong before spending to fix it.\n\n---\n\n## When it goes wrong\n\n* **Page unreachable or thin** → report what you could and could not read, fill the brief with `[CLIENT INPUT NEEDED]`, stop at G1. Never fabricate a product from the URL's words.\n* **A capability errors on argument shape** → re-read the tool contract above before retrying. Two consecutive failures on the same call means read the state back with `list_*` \u002F `get_*` and work from what actually exists, not from what you think you created.\n* **A partial action left orphans** (a board with no shots, an entity with no roster) → find them with `list_*` and finish or delete them. Do not create a second one alongside.\n* **A model is unavailable or unpriced** → say so at the gate it blocks, with alternatives. Never substitute silently.\n* **A shot will not converge** (three renders, still failing adherence) → stop spending, show the best take with what is wrong, offer: accept, rewrite, or change `render_mode`.\n* **Budget cap set** → check the ledger with `get_cost_summary` before each render phase. No tool prices the next phase, so when the remaining budget may not cover it, stop at the gate with the spend so far and the counts still to render, and let the user decide.\n",{"name":5,"description":7},"main","c7226baa82daca42a19a5855c116544f97aa8bac","packages\u002Fsystem-skills\u002Flaunch-commercial",90,"https:\u002F\u002Fgithub.com\u002Fnodetool-ai\u002Fnodetool","https:\u002F\u002Fnodetool.ai\u002F","https:\u002F\u002Fgithub.com\u002Fnodetool-ai","No-code AI development","2026-11-12 03:00:58",{"source":8,"skillName":5,"commands":62,"prompt":72,"zipAvailable":73},[63,66,69],{"tool":64,"command":65},"npx","npx skills add nodetool-ai\u002Fnodetool --skill launch-commercial",{"tool":67,"command":68},"bunx","bunx skills add nodetool-ai\u002Fnodetool --skill launch-commercial",{"tool":70,"command":71},"pnpm","pnpm dlx skills add nodetool-ai\u002Fnodetool --skill launch-commercial","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fnodetool-ai\u002Fnodetool\u002Fblob\u002Fmain\u002Fpackages\u002Fsystem-skills\u002Flaunch-commercial\u002FSKILL.md",true,[],[76,109,138,152,166,190],{"skillNo":77,"name":39,"nameZh":6,"description":78,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":79,"categories":80,"keywords":89,"publishedAt":47},"01M6BY4D93R0Q9400ZHJ04DXP1","Use the NodeTool MCP server to build, validate, run, and debug visual AI workflows, and to work with assets, collections, and jobs. Use when the user mentions NodeTool or asks for a workflow graph, a node, or a media generation run on their NodeTool server.","https:\u002F\u002Fgithub.com\u002Fnodetool-ai\u002Fnodetool\u002Fblob\u002Fmain\u002Fplugins\u002Fchatgpt\u002Fnodetool\u002Fskills\u002Fnodetool\u002FSKILL.md",[81,84],{"categoryNo":82,"categoryName":83,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":85,"categoryName":86,"taxonomyType":87,"taxonomyTypeDesc":88},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[39,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,30,28],"MCP","mcp server","workflow","workflows","validate","debug","run","graph","server","Media","assets","work","node","use","visual","generation","collections","media generation","with",{"skillNo":110,"name":111,"nameZh":6,"description":112,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":113,"categories":114,"keywords":118,"publishedAt":47},"01M6BY4AEB05ZPBRVD078ATR2H","wan-2-6-prompting","Prompt Alibaba's Wan 2.6 video — text-to-video with a global style line plus timing-bracketed shots, and image-to-video that describes only what changes over time. Covers the shot-mode switch per provider, the prompt budget, negative prompts, motion intensity wording, audio input and prompt expansion. Use whenever the model id names a Wan 2.6 video route (alibaba\u002Fwan-2.6\u002Ftext-to-video, alibaba\u002Fwan-2.6\u002Fimage-to-video, wan-video\u002Fwan-2.6-t2v, wan-video\u002Fwan-2.6-i2v, wan-video\u002Fwan2.6-i2v-flash) on generate_video, animate_image or a TextToVideo node. Not for Wan 2.5 or 2.7, or the Wan 2.6 image routes.","https:\u002F\u002Fgithub.com\u002Fnodetool-ai\u002Fnodetool\u002Fblob\u002Fmain\u002Fpackages\u002Fsystem-skills\u002Fwan-2-6-prompting\u002FSKILL.md",[115],{"categoryNo":116,"categoryName":117,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体",[119,120,121,122,123,124,125,126,127,128,129,102,37,130,131,132,133,134,135,136,137],"wan 2 6 prompting","prompting","image","generate","Budget","Video","Audio","text","animate","model","flash","route","time","line","input","text-to-video","image-to-video","mode","expansion",{"skillNo":139,"name":140,"nameZh":6,"description":141,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":142,"categories":143,"keywords":146,"publishedAt":47},"01M6BY47T4748ZG5ZMTAR97ZWZ","video-workflow","Build a reusable NodeTool video workflow for batches or repeated inputs. For a single finished piece, use a storyboard skill.","https:\u002F\u002Fgithub.com\u002Fnodetool-ai\u002Fnodetool\u002Fblob\u002Fmain\u002Fpackages\u002Fsystem-skills\u002Fvideo-workflow\u002FSKILL.md",[144,145],{"categoryNo":116,"categoryName":117,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":82,"categoryName":83,"taxonomyType":21,"taxonomyTypeDesc":22},[92,124,147,39,103,148,36,149,150,30,151],"video workflow","reusable","build","batches","single",{"skillNo":153,"name":154,"nameZh":6,"description":155,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":156,"categories":157,"keywords":159,"publishedAt":47},"01M6BY4587N2SYN315SXGEMZC3","video-clone","Analyze a reference video and recreate its shot structure with a new product or cast in NodeTool.","https:\u002F\u002Fgithub.com\u002Fnodetool-ai\u002Fnodetool\u002Fblob\u002Fmain\u002Fpackages\u002Fsystem-skills\u002Fvideo-clone\u002FSKILL.md",[158],{"categoryNo":116,"categoryName":117,"taxonomyType":21,"taxonomyTypeDesc":22},[124,160,161,45,162,39,163,164,35,108,165],"video clone","clone","analyze","reference","cast","new",{"skillNo":167,"name":168,"nameZh":6,"description":169,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":170,"categories":171,"keywords":173,"publishedAt":47},"01M6BY4257C2KQYYMD9EK6DK8E","video-audio-continuity","Keep sound continuous across a multi-scene piece cut from generated video — why one clip per scene hard-cuts the audio at every boundary, when to write all the scenes into a single generation instead, the sound-design vocabulary that survives a provider's copyright filter, and how to check the length, the aspect ratio and the mix of what came back. Use when a piece has more than one scene and the video model writes its own audio (Seedance 2, Veo 3, MiniMax H3, Kling 2.6 and later), or when a cut's sound drops out at the shot changes.","https:\u002F\u002Fgithub.com\u002Fnodetool-ai\u002Fnodetool\u002Fblob\u002Fmain\u002Fpackages\u002Fsystem-skills\u002Fvideo-audio-continuity\u002FSKILL.md",[172],{"categoryNo":116,"categoryName":117,"taxonomyType":21,"taxonomyTypeDesc":22},[124,125,174,175,176,177,178,179,128,180,27,181,182,183,184,185,186,187,188,189,28],"video audio continuity","continuity","design","boundary","how","why","write","later","back","across","cut","how to","filter","mix","at","veo 3",{"skillNo":191,"name":192,"nameZh":6,"description":193,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":194,"categories":195,"keywords":197,"publishedAt":47},"01M6BY3ZANBSQMZRH4MG55R8Z7","veo-3-prompting","Prompt Google's Veo 3 video line — the five-element shot\u002Fsetting\u002Fsubject\u002Faction\u002Fdialogue structure, the cinematography vocabulary it reads directly, duration and aspect-ratio tradeoffs, the audio and auto-fix switches per provider, and the fixes for visual drift, temporal inconsistency and audio mismatch. Use whenever the model id contains veo3 or veo-3 (fal-ai\u002Fveo3.1 and its fast, lite, image-to-video, first-last-frame and reference-to-video routes, fal-ai\u002Fveo3\u002Fimage-to-video, google\u002Fveo3.1 on atlascloud, google\u002Fveo-3 and google\u002Fveo-3.1 on replicate, kie's veo-3-1, and Gemini's own veo-3.1-generate-preview) on generate_video, animate_image, generate_video_from_references or a TextToVideo node.","https:\u002F\u002Fgithub.com\u002Fnodetool-ai\u002Fnodetool\u002Fblob\u002Fmain\u002Fpackages\u002Fsystem-skills\u002Fveo-3-prompting\u002FSKILL.md",[196],{"categoryNo":116,"categoryName":117,"taxonomyType":21,"taxonomyTypeDesc":22},[198,199,120,189,121,200,201,122,202,203,124,125,127,128,204,205,206,207,208,209,210,35,211,212,213],"veo 3 prompting","veo","google","gemini","fast","first","fal","kie","replicate","fix","auto","references","drift","preview","element","auto-fix"]