Scroll Promo Site Builder
kangarooking/kangarooking-skills
Create a scroll-controlled cinematic product website with rich motion (动效网站) from product materials, reference pages or videos, and brand assets.
Use NodeTool storyboard, entity-casting, rendering, and timeline-assembly tools for video production or debugging their contracts.
$ npx skills add nodetool-ai/nodetool --skill storyboard-core -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install nodetool-ai/nodetool storyboard-core --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/nodetool-ai/nodetool.git skills-src && mkdir -p .claude/skills && cp -r skills-src/packages/system-skills/storyboard-core .claude/skills/storyboard-core && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "storyboard-core" agent skill from https://github.com/nodetool-ai/nodetool/tree/main/packages/system-skills/storyboard-core into .claude/skills/storyboard-core/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "storyboard-core", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/nodetool-ai/nodetool/tree/main/packages/system-skills/storyboard-coreType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add nodetool-ai/nodetool --skill storyboard-core -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install nodetool-ai/nodetool storyboard-core --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/nodetool-ai/nodetool.git skills-src && mkdir -p .agents/skills && cp -r skills-src/packages/system-skills/storyboard-core .agents/skills/storyboard-core && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "storyboard-core" agent skill from https://github.com/nodetool-ai/nodetool/tree/main/packages/system-skills/storyboard-core into .agents/skills/storyboard-core/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "storyboard-core", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add nodetool-ai/nodetool --skill storyboard-core -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install nodetool-ai/nodetool storyboard-core --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/nodetool-ai/nodetool.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/packages/system-skills/storyboard-core .cursor/skills/storyboard-core && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "storyboard-core" agent skill from https://github.com/nodetool-ai/nodetool/tree/main/packages/system-skills/storyboard-core into .cursor/skills/storyboard-core/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "storyboard-core", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/nodetool-ai/nodetool.git --path packages/system-skills/storyboard-core--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add nodetool-ai/nodetool --skill storyboard-core -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install nodetool-ai/nodetool storyboard-core --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/nodetool-ai/nodetool.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/packages/system-skills/storyboard-core .gemini/skills/storyboard-core && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "storyboard-core" agent skill from https://github.com/nodetool-ai/nodetool/tree/main/packages/system-skills/storyboard-core into .gemini/skills/storyboard-core/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "storyboard-core", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install nodetool-ai/nodetool storyboard-coreInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add nodetool-ai/nodetool --skill storyboard-core -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/nodetool-ai/nodetool.git skills-src && mkdir -p .github/skills && cp -r skills-src/packages/system-skills/storyboard-core .github/skills/storyboard-core && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "storyboard-core" agent skill from https://github.com/nodetool-ai/nodetool/tree/main/packages/system-skills/storyboard-core into .github/skills/storyboard-core/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "storyboard-core", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add nodetool-ai/nodetool --skill storyboard-core -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install nodetool-ai/nodetool storyboard-core --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/nodetool-ai/nodetool.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/packages/system-skills/storyboard-core .opencode/skills/storyboard-core && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "storyboard-core" agent skill from https://github.com/nodetool-ai/nodetool/tree/main/packages/system-skills/storyboard-core into .opencode/skills/storyboard-core/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "storyboard-core", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
storyboard-coreUse NodeTool storyboard, entity-casting, rendering, and timeline-assembly tools for video production or debugging their contracts.
Storyboard Core is an agent skill from nodetool-ai/nodetool. Use NodeTool storyboard, entity-casting, rendering, and timeline-assembly tools for video production or debugging their contracts.
Its SKILL.md is about 8.4k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Media & Creative, covering Comics and storyboards and Video production. The repository describes itself as: Agent-first Creative Workspace. The licence is AGPL-3.0.
11 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 339f069. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are json).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Storyboard Core loads about 8.4k tokens when it runs. Until then it costs about 37 tokens; SKILL.md has 3,826 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from nodetool-ai/nodetool at commit 339f069, republished under its AGPL-3.0 licence (© nodetool-ai). 3,826 words, ~8,378 tokens.
.claude/skills/storyboard-core/SKILL.md (or your agent's skills folder).The shared half of every NodeTool video job. The use-case skills carry the style, the shot pattern and the briefs; this carries the machinery they all assume.
Direct video on the storyboard surface, then finish in the timeline. Use a node graph when the user requests one or needs a reusable template, as
described in video-workflow.
Read the relevant part of Tool contract below before the first call in that tool family. Use the live tool schema when it differs from an example. Load other sections only when the task reaches them.
| Job | Skill |
|---|---|
| Phone-shot video with a person: UGC ad, testimonial, comedy, day-in-the-life, vlog | /ugc-video |
| Product film: pack shot, hero, brand spot, with or without talent | /product-commercial |
| Sound-off motion-graphics ad from real screenshots and product photos, no generated footage | /motion-ad |
| Rebuild an existing ad or clip you were given | /video-clone |
| Voiceover drives the picture: explainer, faceless video, narrated piece | /script-video |
| Narrative with dialogue, score and a title: short film, trailer, scene | /short-film |
| A campaign, not one video: entity sheets, still set, several cuts | /launch-kit |
| A template to re-run on new inputs | /video-workflow |
| Footage already exists and needs repair: cutout, upscale, outpaint, lip sync, generated sound | /nodetool-video-post |
| A screen someone clicks, over the workflows behind a piece | /nodetool-app-builder |
If none fits, run the loop below directly.
Those skills say what the picture looks like. A second set says where the beats go, and the two compose: load the job skill for the look and the structure skill for the spine.
| The structure question | Skill |
|---|---|
| Ad beats: hook, escalation, timecodes, VO discipline | /commercial-beat-sheet |
| A product page URL in, a finished cut and its cost out | /launch-commercial |
| A 30–120s explanation with a claim per beat | /explainer-storyboard |
| A treatment cut to a track's sections | /music-video-treatment |
| Eight audio-first beats, the drop at ~65%, a title card | /trailer-template |
These own their own briefs and beat budgets. /commercial-beat-sheet carries
the board contract the other four quote; where it and this skill both describe
a call, this skill is the fuller version.
| Headless | Browser | |
|---|---|---|
| Names | create_storyboard, get_storyboard, edit_storyboard, render_storyboard_stills, render_storyboard_clips, revise_storyboard_clip, assemble_storyboard_timeline | ui_storyboard_ + get_state, set_screenplay, add_shot, update_shot, generate_keyframe, generate_clip, revise_shot, assemble_timeline, select_shot |
| Needs | nothing open | the board open as a tab, listed in the ui_context system block |
| Shot target | id, 0-based index, or slug | id, 0-based index, or "selected" — no slug |
| Permission gate | yes | no (client tools bypass it) |
Every tool in both families takes an explicit storyboard_id. There is no
"act on whatever is open" fallback.
Prefer headless. Reach for ui_storyboard_* when the user is watching the board fill
in, or when you need set_screenplay for title, logline or style_bible.
Headless edit_storyboard → set_board writes screenplay-level narration and
music_prompt while preserving the other screenplay fields.
Bridge the two with ui_open_document {type: "storyboard", id}.
list_storyboards, or read ui_context. No board yet →
create_storyboard {name, brief, style, aspect_ratio}.list_entities first. A new one needs an image asset —
generate_image or the user's upload — then
create_entity {asset_id, kind, name, descriptor}. There is no create-from-text.edit_storyboard → {op: "set_board", entity_ids: [...]},
or ui_storyboard_set_entities {storyboard_id, entity_ids} on an open board.
An entity not on the board seasons nothing, no matter what the shots say.find_model with capability text_to_image, then
image_to_video or text_to_video; write both through
{op: "set_board", image_model, video_model}. There is no default — an unset
model fails the render rather than spending on a model nobody chose.{op: "add_shot", ...} per shot. Report the shot count and
selected models. Continue to the stages authorized by the request. A board-only
request ends here. Honor any checkpoint the user requested.render_storyboard_stills {storyboard_id} — omit targets for every
shot that still needs one. Cap 24 shots per call.keyframe_versions. Selecting a different one requires the board's takes gallery. No tool does it.
Pause if the user reserved that selection. Otherwise continue with the current
take. Never claim to have switched takes without a supported action.render_storyboard_clips {storyboard_id} within the requested
generation scope and budget. Reuse prior authorization instead of asking again.revise_storyboard_clip {target, instruction}. Needs an
existing clip; it is video-to-video and touches no other shot.assemble_storyboard_timeline, then validate_timeline. Re-running
rebuilds the same sequence in place and keeps tracks the board does not own.edit_timeline ops (Tool contract § Timeline, below).
Load the motion-graphics system skill with load_skill for the full op
list, the presets and the preview contract. A repair on a delivered clip
belongs to nodetool-video-post.Stills-only mode. When the user wants a board and frames but no motion — a spec
ad, a pitch, a look test — run steps 1 to 7 and stop. Adding the motion later is
update_shot on motion alone: action, wardrobe, set and cast stay put, so the
approved stills still describe the finished piece.
The prompt is assembled from named fields, not from the whole shot:
action, then "<camera.framing> shot", then the board's style.
camera.lens, camera.angle and camera.movement do not reach it.motion, then action. The style does not reach it —
the selected still carries the look.action, "<framing> shot", motion, style.So a lens, an angle or a camera move only lands if you write it into action or
motion. Keep the structured camera object as well — it is what the user reads on
the card.
Entities season by plain name. A shot with entity_ids uses exactly those.
Otherwise: every style and location entity applies to every shot, and a character or
prop applies when its name appears in the shot's action, motion, dialogue,
narration or slug. Write Nova reaches for the bottle, not @Nova reaches....
@ is a chat-composer affordance that emits an entity://<id> token into your
message; it never becomes shot text.
descriptor per entity and let it do the work. Do not re-describe a face
in shot text.slug (1a, 2a, …) at add_shot time. Headless targets
accept it, and "fix 3a" then means one shot.keyframe (default) is cheaper to iterate and holds a look
steady. direct skips the still — worth it for heavy motion, where first-frame
conditioning stiffens the result, and for native-audio video models, which are
weakest on their image path.video-audio-continuity
system skill through load_skill: either the whole piece is one shot
carrying the cuts, or the
continuity comes from a narration or music track and the shot audio is muted.
A silent cut is also valid when sound is not requested. Reuse existing audio
or keep the cut silent when extra generation would exceed the authorized scope.Four constraints that are not bugs and have no tool to fix them. Each has a working shape.
A video model returns a fixed length. It ignores duration_seconds — the same
model gives 5.184s whether the shot was directed at 1.5s or 5s. For a fast cut,
render one generation per run of short beats with bracketed timecodes in the prompt
and record the split with covered_by. Tool contract § Fusing shots,
below, has the call.
Moderation reads the words, not the story. A prompt naming institutional force — police grabbing someone, a raid, restraint — trips a safety filter that a shot of the same threat does not. Reframe with non-trigger vocabulary and keep the stakes: "two uniformed city officials approaching with a clipboard and caution tape" passes where "two aggressive police officers grabbing her arms" does not. Reframe once; do not retry the rejected prompt.
A music model cannot leave a hole. Ask for 1.5s of silence at 00:19.5 and the decoder fills it with reverb tail and room tone — there is no envelope gate in the prompt. Cut the silence on the timeline instead: generate the score in two parts, lay them as two clips with the gap between them, and fade the first out (~300ms) and the second in (~100ms) so the edges are not clicks.
Speech does not fit a 1.5s shot. An expressive take of a line runs 4s, and compressing it to the picture beat makes it sound processed. Let the words cross the cut: keep the voiceover clip on its own track and start it under the previous shot (J-cut) or run it past the cut into the next (L-cut). The picture cuts on the beat; the line finishes where it finishes. On a script-linked board that is automatic — each shot runs as long as the lines it covers — so use this on an unlinked board or where the picture has to stay on the beat.
Permission mode decides what runs without asking. read tools always run.
| Mode | write / execute / external |
|---|---|
auto | runs |
default | asks the user |
plan | blocked |
plan blocks create_storyboard and edit_storyboard too, so the board cannot be
written in it. Respect the active permission mode and tool approval responses. For a board-only
request, write the board and omit render calls. Do not switch modes or use browser
tools to evade a denied headless action. A tool being callable does not authorize
spend beyond the user's request.
You cannot price a render. No tool estimates it, and Shot.cost_estimate is not
populated. Report the shot count and model without inventing a dollar estimate. If a user
budget cannot be checked before spending, resolve that constraint first. Report
actual spend afterwards with get_cost_summary. If authorization is missing,
prepare the board and explain the specific render action that needs approval.
memory_save is durable across every conversation — use it for the style bible, the
cast and the grade, with the entity and board ids in resources. share_result /
read_shared are run-scoped and discarded; they are for passing values inside one
turn, not for locking a look across sessions.
Fix 3a only. Leave the rest.
Drop the contre-jour across the whole board — soft even daylight. Update the
board style, do not rewrite the shots.
New still on 2a. Keep the old take.
revise_storyboard_clip 5a: darker, add rain, same blocking.
Add a reaction shot after 3a and re-slug from there. Do not re-render 1a-3a.
Collapse 8, 9 and 10 into one 8s shot, keeping the lean-in as the last second.
The face drifted on 4a. I picked take 2 in the gallery — regenerate the clip.
The label is unreadable on 2a. New macro still, then a clip from it.
Cut it. Assemble, validate, narration on its own track, mute Shot Audio on 1-6.Exact arguments. Every field named here is checked in the implementation; an unrecognized field is refused with an error, not dropped.
Work with no editor open. Shot target = shot id, 0-based index as a string, or slug.
| Tool | Arguments |
|---|---|
list_storyboards | limit? → id, name, shots, with_keyframe, with_clip, timeline_id, updated_at |
create_storyboard | name (required), brief?, style?, aspect_ratio? (default 16:9), project_id?, id? |
get_storyboard | storyboard_id → brief, style, aspect ratio, narration, music_prompt, image_model, video_model, script link, and every shot with id/index/slug/action/camera/motion/duration/status/has_keyframe/has_clip/covered_by |
edit_storyboard | storyboard_id, ops[] — see below |
render_storyboard_stills | storyboard_id, targets?, provider?, model?, style?, concurrency? |
render_storyboard_clips | storyboard_id, targets?, provider?, model?, resolution?, mode? (keyframe|direct), concurrency? |
revise_storyboard_clip | storyboard_id, target, instruction, provider?, model? |
assemble_storyboard_timeline | storyboard_id, name?, fps? (default 30) |
extract_script_from_storyboard | storyboard_id, name?, relink? |
delete_storyboard | storyboard_id |
Render limits: 24 shots per call, concurrency default 3, max 8.
Omitting targets selects every shot that still needs that step. For stills that is
every shot with no keyframe that is not set to render directly; for clips, every shot
with no picture — its own or one it is covered by (§ Fusing shots). A keyframe-mode
shot that reaches render_storyboard_clips with no
still is reported in the results, not rendered — render its still first, or set its
render_mode to direct. Each render appends to keyframe_versions /
clip_versions and makes the new one selected.
edit_storyboard ops{"op": "add_shot", ...} · update_shot {target, ...} · remove_shot {target} ·
reorder_shot {target, index} · set_board {...}
Shot fields (add and update both): action, slug, camera, motion, graphics, dialogue,
narration, notes, duration_seconds, duration_source (audio|manual),
render_mode (keyframe|direct|reference), entity_ids, location_id, covered_by, index.
graphics is semantic design intent (mode, direction, exact text/assets in elements), not Timeline keyframes; pass null on update_shot to clear it.
camera is {framing?, lens?, angle?, movement?}.
covered_by is {shot_id, start_seconds?, end_seconds?} — this shot's picture is a
window into another shot's clip. See § Fusing shots below. null undoes it.
Board fields (set_board only): brief, style, aspect_ratio, entity_ids,
image_model, video_model, narration, music_prompt. The two model fields
take a model object from find_model, or null. narration and music_prompt
take strings and update the screenplay. An empty string clears that direction.
An edit cannot set keyframe, clip or status. Those belong to the render
tools; passing them raises an error naming the right tool. covered_by is the one
exception: coverage is a picture that arrived without a render, so setting it marks
the shot rendered and clearing it puts the shot back to keyframe_ready/planned.
An edit cannot set title, logline or style_bible at the board level.
Use ui_storyboard_set_screenplay for those fields. Per-shot narration remains
a shot field. Board narration and music are separate screenplay fields:
assemble_storyboard_timeline turns them into draft audio clips on unlinked boards.
A video model returns a fixed window — 5.184s, 6s, 8s — whatever length the shot was directed at. A cut whose beats run 1.5-2.5s is therefore cheaper to render as one generation covering a run of shots and split afterwards, than as one generation per shot: fifteen 1.5-2.5s beats bought one at a time is ~77s of footage for a 30s cut.
Direct the run as one prompt with bracketed timecodes
([00:00 to 00:02.5] Event … [00:02.5 to 00:05.2] Reception), render it on the first
shot of the run, then tell the board which slice each sibling uses:
{"op": "update_shot", "target": "06",
"covered_by": {"shot_id": "05", "start_seconds": 2.5, "end_seconds": 5.184}}The covered shot then reads has_clip: true with status: "rendered", is skipped by
render_storyboard_clips (so it is never generated twice), and
assemble_storyboard_timeline lays it down as that window of the covering clip
instead of skipping it, and the covering shot ends where the first covered shot takes
over. Without end_seconds the covered shot runs to the end of the covering clip.
One hop only: shot_id must name a shot that owns its clip, not another covered shot.
Removing the covering shot clears the coverage and reports which shots it uncovered.
ui_storyboard_*)The board must be open as a workspace tab; its id comes from the ui_context system
block. Not open → edit_storyboard edits the saved board. Use ui_open_document {type: "storyboard", id} to show it to the user. Target =
shot id, index as a string, or "selected". Slugs are not resolved here.
| Tool | Arguments |
|---|---|
ui_storyboard_get_state | storyboard_id |
ui_storyboard_set_screenplay | storyboard_id, screenplay |
ui_storyboard_set_entities | storyboard_id, entity_ids (replaces the cast; [] clears it) |
ui_storyboard_add_shot | storyboard_id, action, camera?, motion?, durationSeconds?, index? |
ui_storyboard_update_shot | storyboard_id, target, action?, camera?, motion?, status? |
ui_storyboard_generate_keyframe | storyboard_id, target |
ui_storyboard_generate_clip | storyboard_id, target |
ui_storyboard_revise_shot | storyboard_id, target, instruction |
ui_storyboard_assemble_timeline | storyboard_id |
ui_storyboard_select_shot | storyboard_id, target (nullable) |
ui_storyboard_extract_script / _relink_script / _reproject_shots | storyboard_id (+ targets?) |
ui_storyboard_set_duration_source | storyboard_id, targets, source |
ui_storyboard_add_shot and update_shot take no slug, dialogue,
narration, entity_ids or render_mode. In the browser those arrive only through
set_screenplay; headless, through edit_storyboard.
Generation is asynchronous: generate_keyframe / generate_clip start the job and
return the shot. Poll ui_storyboard_get_state for status.
Statuses: planned → keyframe_generating → keyframe_ready → clip_generating →
rendered, or failed. (approved is legacy and reads as "still ready"; nothing
sets it. Do not use it as a gate.)
screenplay for set_screenplay{
"type": "screenplay",
"title": "…",
"logline": "…",
"brief": "…",
"styleBible": "…",
"aspectRatio": "9:16",
"narration": "…",
"musicPrompt": "…",
"entityIds": ["<asset id>"],
"shots": [
{
"slug": "1a",
"action": "Nova touches untreated hair, front-camera close",
"camera": { "framing": "close-up", "lens": "26mm", "angle": "eye", "movement": "handheld" },
"motion": "she lifts one section of hair and lets it fall, once",
"dialogue": "I wanted lighter hair…",
"durationSeconds": 4,
"entityIds": ["<asset id>"]
}
]
}Ids, indexes and statuses are filled in. style is accepted as an alias of
styleBible. Only action is required per shot.
The top-level entityIds casts those entities on the board — that cast is what
seasons every shot's still and clip prompt. Use ui_storyboard_set_entities to
recast without replacing the shots; ui_storyboard_get_state reports the current
cast as entityIds. A shot's own entityIds overrides the board cast for that shot
([] means "no entities on this shot"); without one, styles and locations apply to
every shot and characters and props apply to the shots whose text names them.
| Tool | Arguments |
|---|---|
list_entities | kind? (character|location|style|prop), query?, limit? |
get_entity | entity_id |
create_entity | asset_id, kind, name, descriptor (all required), description?, voice_id?, tags?, lora?, palette? |
update_entity | entity_id + any field, including asset_id to move it to a new photo |
delete_entity | entity_id — clears the marker, keeps the image |
apply_entities | text, entity_ids? → seasoned prompt + reference asset ids |
An entity is an image asset carrying a marker. Its id is the asset id. To make one
from nothing: find_model (text_to_image) → generate_image → create_entity with
the returned asset id. To make one from a photo the user attached: find it with
asset_search / list_assets, then create_entity.
apply_entities is for seasoning a prompt you are about to send yourself
(generate_image, generate_video). The storyboard render tools season internally —
do not pre-season shot text with it.
assemble_storyboard_timeline lays rendered clips end to end, stamps each with its
board and shot id, and gives every shot clip an audio twin on a "Shot Audio" track
(each surface mutes video elements and mixes audio clips only). Board narration and
music become draft clips on their own tracks.
Each shot is as long as its render, not as long as its duration_seconds — a shot
directed at 1.5s that came back at 5.184s occupies 5.184s of the cut. Shots that came
back off their directed length are returned in retimed_shots and warned about, so a
cut that runs longer than planned is visible without playing it. duration_seconds
only decides a shot whose footage has no measurable length. A board linked to a script
is the exception: there the words decide, and trimmed_shots names the shots whose
footage does not fill the slot its lines gave it.
edit_timeline {timeline_id, ops[]} ops: get_state, add_track, add_media_clip,
add_text_clip, add_shape_clip, split_clip, trim_clip, move_clip,
duplicate_clip, delete_clip, set_clip_params, set_clip_binding,
animate_clip, clear_animations, list_animation_presets, select_clip, seek.
Start with get_state for ids.
To duck the shots' own sound under a voiceover: set_clip_params on the Shot Audio
clips with muted: true, or volumeDb: -18. set_clip_params also takes name,
opacity, speedMultiplier, fadeInMs, fadeOutMs, blendMode, borderRadius,
hidden, locked, textStyle, shapeStyle.
Every field goes on the op itself. A params / props wrapper is unwrapped, but the
flat form is the contract:
{"op": "set_clip_params", "target": "clip_1",
"textStyle": {"fontFamily": "Space Grotesk", "fontWeight": 800}}textStyle is merged over the clip's own, so send only what you are changing —
re-sending the whole object is how the fields you did not mean to touch get
overwritten. fontWeight is a number, and the CSS keywords (bold, semibold,
extrabold) are accepted and stored as one.
Set a bundled family or none at all. sans-serif, serif, system-ui and the
other CSS generics are refused where the clip is authored: they name no typeface, so
the editor preview, the render and preview_timeline_frame each pick a different one.
NodeTool ships Inter (the default), Space Grotesk, Bebas Neue, Playfair Display, Lora
and JetBrains Mono. A named system font is still allowed and validate_timeline
reports it as font_not_portable.
validate_timeline {timeline_id | document, fps?} after every edit pass. It catches
overlaps, fades longer than their clip, unknown presets and incomplete bindings.
Other timeline tools: list_timelines, create_timeline, get_timeline,
delete_timeline, and the version family (list_/get_/create_/restore_/
delete_timeline_version). Browser twins are ui_timeline_*.
Use a script when the words drive the cut. extract_script_from_storyboard projects a
board's dialogue and narration into a script and links the two: each shot keeps the
line ids it covers, and from then on the script owns the words. derive_storyboard_from_script
goes the other way, one shot per line, optionally with a director pass over the scaffold.
voice_script_lines synthesizes each line with its cast voice; assemble_script_timeline
lays the takes end to end.
Give the cast their voices first, then voice with no override. A voice is the
triple {provider, model, voice} where voice is the voice's own name (Aoede,
Charon, alloy) — a provider and a model alone do not pick one, so
voice_script_lines {provider, model} is refused. Set each speaker once:
{"op": "set_speaker_voice", "target": "MOM",
"provider": "gemini", "model": "tts-hd", "voice": "Aoede"}The three keys sit on the op, though a voice: {provider, model, voice_id} object —
the shape get_script reports a voice in — is flattened onto it. Then call
voice_script_lines {script_id} with no override and every speaker resolves its own. On a linked board, assemble_storyboard_timeline cuts words
and picture together: each shot runs as long as the takes it covers and every voiced
line gets its own voiceover clip.
Others: list_scripts, get_script (line status: draft, stale, voiced,
no_voice), create_script, edit_script, delete_script. Browser twins are
ui_script_*.
create_script makes an empty one. edit_script {script_id, ops[]} ops:
set_setup (brief, format, length, pace, language, stage), add_speaker,
set_speaker, set_speaker_voice, add_section, add_line, set_line_text,
set_line_speaker, remove_line, remove_speaker. Call get_script first for
line and speaker ids.
write_script {script_id, rewrite?, imported_text?} drafts the lines from the
setup and saves the cast and lines it produces, replacing what was there. Set
the brief with set_setup first. rewrite: true keeps the ids of the lines it
keeps, so their takes and storyboard links survive. Words passed as
imported_text are only split into lines and attributed to speakers, never
reworded. It records no take: voice the result with voice_script_lines.
Rewriting a line leaves its takes in place as stale, so a following
voice_script_lines re-records exactly those and nothing else.
find_model {capability, query?, task?, provider_hint?, model_hint?, prefer_local?, limit?} is the only way to name a model. capability is required and is one of
text_to_image, image_to_image, text_to_video, image_to_video, text_to_speech,
text_to_music, automatic_speech_recognition, generate_embedding,
generate_message. query is the search box ("flux schnell"); task is a hint
matched against a model's supported tasks, not a search.
NodeTool ships curated ids under the nodetool provider, which are the sane defaults
when the user has no preference:
| Id | Use |
|---|---|
nodetool/flux-schnell | cheapest stills, text prompts only |
nodetool/nano-banana | sharper stills, takes reference images |
nodetool/seedream | most detail, slowest |
nodetool/hailuo-fast | cheapest clips, for blocking out a cut |
nodetool/kling-turbo | steadier motion, mid price |
nodetool/kling-standard | most faithful motion |
nodetool/kokoro | eight TTS voices |
generate_image {provider, model, prompt, negative_prompt?, width?, height?, quality?, output_file?}, plus edit_image, generate_video, animate_image, generate_speech,
generate_music and transcribe_audio — each takes a find_model provider+model and
saves an asset:// result. model also accepts a whole find_model hit or its .ref,
in which case provider may be omitted.
understand_video {provider, model, video, prompt?, max_tokens?} reads a whole video
with a multimodal model (Gemini reads video natively); use it to break down a reference
cut. video takes an asset id, an asset:// URI, a URL or a data URI. view_image
puts pixels in your own context; read_media_bytes returns the bytes.
nodetool.creative.Director is a workflow node, not a chat surface. It turns a
brief into a Screenplay (props: model, brief, style, shot_count; outputs
screenplay, narration, music_prompt, title). Reach for it only inside a
reusable graph. In chat, write the shots yourself with edit_storyboard — same
artifact, no graph to build and validate.
Use this optional template only when the user wants a staged review with approval between the board, stills, clips, and cut. Its checkpoints express that user's chosen workflow. They do not apply to other requests or override authorization already given to finish a piece.
Respect the thread's existing permission mode. Plan mode blocks writes. Do not switch modes or use another tool family to evade a denial. The user can adapt the brief to authorize the desired stages and set a budget.
You are directing on the storyboard surface, then finishing in the timeline.
Do not build a node graph unless I ask for a reusable template.
SURFACES
Headless: create_storyboard, get_storyboard, edit_storyboard,
render_storyboard_stills, render_storyboard_clips, revise_storyboard_clip,
assemble_storyboard_timeline, extract_script_from_storyboard.
Browser (only for a board open as a tab): ui_storyboard_get_state,
ui_storyboard_set_screenplay, ui_storyboard_generate_keyframe,
ui_storyboard_generate_clip, ui_storyboard_revise_shot,
ui_storyboard_assemble_timeline. Use ui_open_document to open a board.
LOOP
1. list_storyboards, or read ui_context. No board -> create_storyboard.
2. Cast before anything renders. list_entities first. A new entity needs an
image asset: generate_image (or my attachment), then create_entity with
{asset_id, kind, name, descriptor}. There is no create-from-text.
Each entity: one name, one kind, one canonical descriptor sentence.
3. edit_storyboard set_board {entity_ids, image_model, video_model,
brief, style, aspect_ratio, narration, music_prompt}. Models come from find_model — there is no
default and an unset model fails the render.
4. Direct with edit_storyboard add_shot, one op per shot. Give every shot a
slug (1a, 2a, ...). Fields: action, slug, camera {framing, lens, angle,
movement}, motion, dialogue, narration, duration_seconds, render_mode.
5. STOP after the board. Report shot count and the two model ids. Do not
estimate dollars — no tool prices a render. Wait for "stills".
6. render_storyboard_stills. I pick takes in the board's takes gallery; you
cannot select a take and must not claim you did. Wait for "clips".
7. render_storyboard_clips. revise_storyboard_clip touches one shot only.
8. On "cut": assemble_storyboard_timeline, then validate_timeline.
WRITING THE SHOTS
Only these reach the image model: action, camera.framing, and the board style.
Only these reach the video model: motion and action (plus framing and style on
a direct-mode shot). So put lens, angle and camera move into the action or
motion text as well as into the camera object.
Name entities in plain text — "Nova reaches for the bottle", never "@Nova".
Entities season a shot when their name appears in its action, motion, dialogue,
narration or slug; styles and locations season every shot. A shot's entity_ids,
if set, overrides that entirely.
LOCKS
- The descriptor is the face. Do not re-describe an entity in shot text.
- Product geometry, label, cap and colour come from the product entity. No new
logos.
- A state change (wet, stained, new outfit) is a second entity, not a morph
instruction inside one prompt.
- Slugs stay stable. "Fix 3a" edits 3a and renders 3a.
- UGC: handheld phone, grain, natural audio, no score, no beauty filter.
Hero: locked grade in the style, smooth camera, score allowed.
- render_mode "keyframe" (default) for anything where the look must hold;
"direct" for heavy motion or a native-audio video model.
MEMORY
memory_save the style, the cast and the grade, with the entity and board ids in
`resources` — that survives into later threads. share_result/read_shared are
run-scoped and get discarded; do not use them to lock a look.
If the shot count looks expensive, propose a collapse before rendering clips.© nodetool-ai, AGPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in packages/system-skills/storyboard-core of nodetool-ai/nodetool.
Open the folder on GitHubat commit 339f069
Storyboard Core next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Storyboard Core this skillnodetool-ai/nodetool | 560 | — | ~8.4k | Automated safety check: Pass | AGPL-3.0 | |
| Scroll Promo Site Builderkangarooking/kangarooking-skills | 662 | — | ~2.6k | Automated safety check: Pass | MIT | |
| Paper Collage Explainer Generatortl2012tl/comfyUI-llama-TE | 241 | 4 repos | ~5.2k | Automated safety check: Pass | None | |
| Video ComposeUtopai-Research/pai-code | 357 | — | ~4.1k | Automated safety check: Pass | Custom licence | |
| Ffmpegrendi-api/ffmpeg-cheatsheet | 1.7k | — | ~1.2k | Automated safety check: Pass | None | |
| Remotion VideoTechNomadCode/AI-Product-Development-Toolkit | 1k | — | ~1.1k | Automated safety check: Pass | MIT |
kangarooking/kangarooking-skills
Create a scroll-controlled cinematic product website with rich motion (动效网站) from product materials, reference pages or videos, and brand assets.
tl2012tl/comfyUI-llama-TE
For creators, educators, and social-video editors who need a tactile paper-collage language for narration, knowledge points, opinions, or abstract topics.
Utopai-Research/pai-code
Generates and prompts video clips on the filmmaking canvas. An agent skill from Utopai-Research/pai-code.
rendi-api/ffmpeg-cheatsheet
A skill your agent uses when the user asks for FFmpeg or FFprobe commands, video/audio conversion, trimming, resizing, padding, overlays, subtitles, thumbnails, GIFs, storyboards, slideshows…
TechNomadCode/AI-Product-Development-Toolkit
Make a short video about a product or brand (promo, launch, explainer, demo or social clip) entirely in code with Remotion, from idea to a rendered MP4 with sound.
MagicCube/agentara
Plan short-form video, vlog, travel, lifestyle, documentary, or cinematic social video scripts by negotiating duration, choosing a story arc and visual style, then splitting the film into timed…
nodetool-ai/nodetool
Cut a NodeTool timeline to music and shape its pacing — detect the beat grid, place cuts on phrases, pick a cut type, build speed ramps with time remap, and give the piece an arc.
nodetool-ai/nodetool
Add and animate a consistent text layer on an existing NodeTool timeline.
nodetool-ai/nodetool
Choose and animate colour on a NodeTool timeline, including shape and text gradients, colour grades, 3D LUTs, and dither.
nodetool-ai/nodetool
Write a shootable, precisely timed commercial beat sheet and store it as a NodeTool storyboard, with a consistent entity roster behind every shot.
nodetool-ai/nodetool
Direct ElevenLabs speech, dialogue, sound effects and music — the bracketed audio tags v3 acts on and why the voice decides whether a tag lands, stability as the delivery dial, punctuation instead…
nodetool-ai/nodetool
Stage the frame on a NodeTool timeline — grids, focal placement, safe areas per aspect ratio, depth layers and parallax, camera moves, and where elements enter and leave.
Categories
Use NodeTool storyboard, entity-casting, rendering, and timeline-assembly tools for video production or debugging their contracts. Storyboard Core is an agent skill from nodetool-ai/nodetool. Use NodeTool storyboard, entity-casting, rendering, and timeline-assembly tools for video production or debugging their contracts.
Storyboard Core fits situations like: tasks that involve Comics and storyboards; tasks that involve Video production.
Run `npx skills add nodetool-ai/nodetool --skill storyboard-core -a claude-code`. Or copy the skill folder (packages/system-skills/storyboard-core in nodetool-ai/nodetool) into .claude/skills/storyboard-core in your project. Claude Code loads it when a task matches its description.
Run `npx skills add nodetool-ai/nodetool --skill storyboard-core -a codex`. Or copy the skill folder (packages/system-skills/storyboard-core in nodetool-ai/nodetool) into .agents/skills/storyboard-core in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add nodetool-ai/nodetool --skill storyboard-core -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/storyboard-core, .gemini/skills/storyboard-core, .github/skills/storyboard-core and .opencode/skills/storyboard-core in your project.
SKILL.md names no scripts, command-line tools or credentials: Storyboard Core is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Storyboard Core is published under the AGPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 8.4k tokens (SKILL.md is roughly 34k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Storyboard Core: Scroll Promo Site Builder (kangarooking/kangarooking-skills, 662 stars), Paper Collage Explainer Generator (tl2012tl/comfyUI-llama-TE, 241 stars), Video Compose (Utopai-Research/pai-code, 357 stars) and Ffmpeg (rendi-api/ffmpeg-cheatsheet, 1.7k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
nodetool-ai (a GitHub organization) maintains it in nodetool-ai/nodetool, which has 560 GitHub stars. The repository holds 127 skills in this directory. The repository was last updated on October 10, 2026.
Source: nodetool-ai/nodetool on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.