@nodetool-ai/agents 0.8.4-a24.2 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/dist/.nodetool-build-stamp +1 -1
  2. package/dist/_skills/LICENSE-iart-motion-design-skills +21 -0
  3. package/dist/_skills/README.md +211 -0
  4. package/dist/_skills/api-agents/SKILL.md +58 -0
  5. package/dist/_skills/api-apps/SKILL.md +54 -0
  6. package/dist/_skills/api-assets/SKILL.md +66 -0
  7. package/dist/_skills/api-collections/SKILL.md +33 -0
  8. package/dist/_skills/api-games/SKILL.md +81 -0
  9. package/dist/_skills/api-media/SKILL.md +131 -0
  10. package/dist/_skills/api-memory/SKILL.md +72 -0
  11. package/dist/_skills/api-models/SKILL.md +78 -0
  12. package/dist/_skills/api-scripts/SKILL.md +79 -0
  13. package/dist/_skills/api-settings/SKILL.md +49 -0
  14. package/dist/_skills/api-sketches/SKILL.md +61 -0
  15. package/dist/_skills/api-storyboards/SKILL.md +94 -0
  16. package/dist/_skills/api-timelines/SKILL.md +151 -0
  17. package/dist/_skills/api-web/SKILL.md +57 -0
  18. package/dist/_skills/api-workflows/SKILL.md +123 -0
  19. package/dist/_skills/beat-sync-editing/SKILL.md +208 -0
  20. package/dist/_skills/caption-titles/SKILL.md +94 -0
  21. package/dist/_skills/color-motion/SKILL.md +185 -0
  22. package/dist/_skills/commercial-beat-sheet/SKILL.md +338 -0
  23. package/dist/_skills/elevenlabs-audio-prompting/SKILL.md +163 -0
  24. package/dist/_skills/explainer-storyboard/SKILL.md +63 -0
  25. package/dist/_skills/flux-2-klein-prompting/SKILL.md +107 -0
  26. package/dist/_skills/frame-composition/SKILL.md +291 -0
  27. package/dist/_skills/game-direction/SKILL.md +99 -0
  28. package/dist/_skills/gpt-image-2-prompting/SKILL.md +161 -0
  29. package/dist/_skills/hailuo-prompting/SKILL.md +107 -0
  30. package/dist/_skills/kling-video-prompting/SKILL.md +188 -0
  31. package/dist/_skills/launch-commercial/SKILL.md +267 -0
  32. package/dist/_skills/launch-kit/SKILL.md +120 -0
  33. package/dist/_skills/logo-reveal/SKILL.md +225 -0
  34. package/dist/_skills/manimgx/SKILL.md +123 -0
  35. package/dist/_skills/minimax-h3-prompting/SKILL.md +134 -0
  36. package/dist/_skills/motion-ad/SKILL.md +281 -0
  37. package/dist/_skills/motion-background/SKILL.md +145 -0
  38. package/dist/_skills/motion-curves/SKILL.md +302 -0
  39. package/dist/_skills/motion-direction/SKILL.md +259 -0
  40. package/dist/_skills/motion-graphics/SKILL.md +217 -0
  41. package/dist/_skills/motion-principles/SKILL.md +255 -0
  42. package/dist/_skills/music-video-treatment/SKILL.md +40 -0
  43. package/dist/_skills/nano-banana-pro-prompting/SKILL.md +133 -0
  44. package/dist/_skills/native-game/SKILL.md +310 -0
  45. package/dist/_skills/native-game-reference/SKILL.md +879 -0
  46. package/dist/_skills/nodetool-3d-scene/SKILL.md +110 -0
  47. package/dist/_skills/nodetool-api-reference/SKILL.md +338 -0
  48. package/dist/_skills/nodetool-app-builder/SKILL.md +291 -0
  49. package/dist/_skills/nodetool-browser-agent/SKILL.md +119 -0
  50. package/dist/_skills/nodetool-chat-cli/SKILL.md +184 -0
  51. package/dist/_skills/nodetool-custom-node-developer/SKILL.md +294 -0
  52. package/dist/_skills/nodetool-deployment/SKILL.md +195 -0
  53. package/dist/_skills/nodetool-js-scripting/SKILL.md +223 -0
  54. package/dist/_skills/nodetool-model-provider-config/SKILL.md +348 -0
  55. package/dist/_skills/nodetool-rag-indexing/SKILL.md +153 -0
  56. package/dist/_skills/nodetool-sketch/SKILL.md +138 -0
  57. package/dist/_skills/nodetool-skill-author/SKILL.md +125 -0
  58. package/dist/_skills/nodetool-troubleshooter/SKILL.md +370 -0
  59. package/dist/_skills/nodetool-video-post/SKILL.md +149 -0
  60. package/dist/_skills/nodetool-workflow-builder/SKILL.md +295 -0
  61. package/dist/_skills/product-commercial/SKILL.md +132 -0
  62. package/dist/_skills/qwen-image-prompting/SKILL.md +122 -0
  63. package/dist/_skills/script-video/SKILL.md +106 -0
  64. package/dist/_skills/seedance-2-prompting/SKILL.md +194 -0
  65. package/dist/_skills/seedream-prompting/SKILL.md +117 -0
  66. package/dist/_skills/short-film/SKILL.md +123 -0
  67. package/dist/_skills/stable-audio-prompting/SKILL.md +130 -0
  68. package/dist/_skills/storyboard-core/SKILL.md +716 -0
  69. package/dist/_skills/timeline-edit-ops/SKILL.md +736 -0
  70. package/dist/_skills/trailer-template/SKILL.md +291 -0
  71. package/dist/_skills/ugc-video/SKILL.md +186 -0
  72. package/dist/_skills/veo-3-prompting/SKILL.md +140 -0
  73. package/dist/_skills/video-audio-continuity/SKILL.md +90 -0
  74. package/dist/_skills/video-clone/SKILL.md +96 -0
  75. package/dist/_skills/video-workflow/SKILL.md +119 -0
  76. package/dist/_skills/wan-2-6-prompting/SKILL.md +107 -0
  77. package/dist/app-build/bridge.d.ts.map +1 -1
  78. package/dist/app-build/bridge.js +44 -17
  79. package/dist/app-build/bridge.js.map +1 -1
  80. package/dist/capabilities/apps.d.ts.map +1 -1
  81. package/dist/capabilities/apps.js +55 -13
  82. package/dist/capabilities/apps.js.map +1 -1
  83. package/dist/capabilities/apps.specs.d.ts.map +1 -1
  84. package/dist/capabilities/apps.specs.js +5 -3
  85. package/dist/capabilities/apps.specs.js.map +1 -1
  86. package/dist/capabilities/game-asset-browser.d.ts.map +1 -1
  87. package/dist/capabilities/game-asset-browser.js +6 -1
  88. package/dist/capabilities/game-asset-browser.js.map +1 -1
  89. package/dist/capabilities/game.d.ts +1 -1
  90. package/dist/capabilities/game.d.ts.map +1 -1
  91. package/dist/capabilities/game.js +71 -56
  92. package/dist/capabilities/game.js.map +1 -1
  93. package/dist/capabilities/game.specs.js +1 -1
  94. package/dist/capabilities/game.specs.js.map +1 -1
  95. package/dist/capabilities/game3d.d.ts.map +1 -1
  96. package/dist/capabilities/game3d.js +14 -19
  97. package/dist/capabilities/game3d.js.map +1 -1
  98. package/dist/capabilities/gameAssertions.d.ts +52 -0
  99. package/dist/capabilities/gameAssertions.d.ts.map +1 -0
  100. package/dist/capabilities/gameAssertions.js +32 -0
  101. package/dist/capabilities/gameAssertions.js.map +1 -0
  102. package/dist/capabilities/models.d.ts.map +1 -1
  103. package/dist/capabilities/models.js +9 -0
  104. package/dist/capabilities/models.js.map +1 -1
  105. package/dist/capabilities/storyboards.d.ts.map +1 -1
  106. package/dist/capabilities/storyboards.js +36 -11
  107. package/dist/capabilities/storyboards.js.map +1 -1
  108. package/dist/capabilities/storyboards.specs.d.ts.map +1 -1
  109. package/dist/capabilities/storyboards.specs.js +11 -1
  110. package/dist/capabilities/storyboards.specs.js.map +1 -1
  111. package/dist/capabilities/workflows.d.ts.map +1 -1
  112. package/dist/capabilities/workflows.js +1 -0
  113. package/dist/capabilities/workflows.js.map +1 -1
  114. package/dist/capabilities/workflows.specs.d.ts.map +1 -1
  115. package/dist/capabilities/workflows.specs.js +4 -3
  116. package/dist/capabilities/workflows.specs.js.map +1 -1
  117. package/dist/evals/surfaces/game.d.ts.map +1 -1
  118. package/dist/evals/surfaces/game.js +29 -1
  119. package/dist/evals/surfaces/game.js.map +1 -1
  120. package/dist/game-authoring.d.ts +120 -0
  121. package/dist/game-authoring.d.ts.map +1 -1
  122. package/dist/game-build-integrity.d.ts +12 -0
  123. package/dist/game-build-integrity.d.ts.map +1 -0
  124. package/dist/game-build-integrity.js +78 -0
  125. package/dist/game-build-integrity.js.map +1 -0
  126. package/dist/game-examples.d.ts +44 -3
  127. package/dist/game-examples.d.ts.map +1 -1
  128. package/dist/game-examples.js +60 -10
  129. package/dist/game-examples.js.map +1 -1
  130. package/dist/system-skills.d.ts +1 -1
  131. package/dist/system-skills.js +1 -1
  132. package/dist/tools/builtin-tools.d.ts.map +1 -1
  133. package/dist/tools/builtin-tools.js +2 -0
  134. package/dist/tools/builtin-tools.js.map +1 -1
  135. package/package.json +19 -19
@@ -0,0 +1,295 @@
1
+ ---
2
+ name: nodetool-workflow-builder
3
+ description: "Create or edit NodeTool workflow graphs, connections, and properties using UI, headless tools, or requested workflow files."
4
+ featured: true
5
+ ---
6
+
7
+ # Build NodeTool workflows
8
+
9
+ Create or edit a workflow that produces the requested outputs from the specified
10
+ inputs. Preserve existing nodes and connections outside the requested change.
11
+
12
+ ## A graph is one surface among several
13
+
14
+ NodeTool holds several document kinds, and most of them are not graphs. Build a
15
+ graph when the user asked for one, when the job has to re-run on new inputs, or
16
+ when the result must be callable over the API. Otherwise route the request.
17
+
18
+ | The deliverable | Skill |
19
+ |---|---|
20
+ | A finished video, still set or campaign | `storyboard-core`, which picks the job skill |
21
+ | A screen someone clicks | `nodetool-app-builder` |
22
+ | A repair on footage already cut: cutout, upscale, lip sync, generated sound | `nodetool-video-post` |
23
+ | A cut, titles, transitions or animation on a timeline | the `motion-graphics` system skill, via `load_skill` |
24
+ | Logic in JavaScript rather than nodes | `nodetool-js-scripting` |
25
+ | A layered image, mask or overlay | `nodetool-sketch` |
26
+ | A 3D model or scene | `nodetool-3d-scene` |
27
+ | A playable 2D or 3D game | `native-game` |
28
+ | A math or explainer animation in Python | `manimgx` |
29
+ | Document indexing, vector search, RAG | `nodetool-rag-indexing` |
30
+ | Navigating, reading or filling in web pages | `nodetool-browser-agent` |
31
+ | A reusable video template for batches | `video-workflow` |
32
+ | A new node type | `nodetool-custom-node-developer` |
33
+
34
+ A workflow can still be the right answer alongside one of these: a mini app runs
35
+ workflows, and a timeline clip can be produced by one.
36
+
37
+ Use the authoring surface the user requested: the graph tools for a saved or
38
+ open workflow, and JSON or the TypeScript DSL when a workflow file is
39
+ requested. Do not claim a missing tool is callable. Use the live schemas and
40
+ registry as the source of identifiers.
41
+
42
+ ## Build and verify
43
+
44
+ 1. Inspect the existing graph for edits (`ui_get_graph` or `get_workflow`).
45
+ Identify required inputs, outputs, and behavior from the request.
46
+ 2. Find node types. On the canvas, `ui_search_nodes` with
47
+ `include_properties: true` and `include_outputs: true` returns property and
48
+ output names. Headlessly, `search_nodes {query: [...]}` finds the type and
49
+ `get_node_info {node_type}` returns its inputs, outputs, types and defaults.
50
+ Pick models with `find_model` (headless) or `ui_search_models` (canvas). Do
51
+ not invent node types, properties, handles, or model identifiers.
52
+ 3. Add or update nodes, set required properties, and connect compatible handles.
53
+ Keep existing layout and identifiers when editing.
54
+ 4. Validate with `ui_get_graph` (its `validation` field) or `validate_workflow`.
55
+ Fix errors and required-input warnings. Read back the saved graph before
56
+ claiming it was created or changed. Report any remaining limitation.
57
+ 5. Execute only when the request includes a run. A run of generation nodes
58
+ spends provider money, so confirm scope with the user first. Report the
59
+ workflow id, inputs, validation result, and actual run outcome.
60
+
61
+ If a node is unavailable, broaden the search and inspect supported alternatives.
62
+ Proceed with an equivalent composition when it preserves the requested behavior.
63
+ Otherwise explain the missing capability and ask only for the decision needed to
64
+ choose an alternative. Continue independent work on the rest of the graph.
65
+
66
+ ## Graph tools
67
+
68
+ These edit one workflow. With the workflow open in the editor they act on the
69
+ live canvas. With no editor open, the eight graph tools (`ui_get_graph`,
70
+ `ui_add_node`, `ui_connect_nodes`, `ui_update_node_data`, `ui_delete_node`,
71
+ `ui_delete_edge`, `ui_move_node`, `ui_set_node_title`) read and write the saved
72
+ workflow named by `workflow_id`. Each mutation also accepts
73
+ `based_on_revision` (from `ui_get_graph`), which refuses the edit if the graph
74
+ changed.
75
+
76
+ | Tool | Purpose | Key params |
77
+ |------|---------|-----------|
78
+ | `ui_search_nodes` | Find node types | `query`, `input_type`, `output_type`, `include_properties`, `include_outputs`, `limit` |
79
+ | `ui_search_models` | Models for a generic AI node | `kind` (`text_to_image`, `image_to_image`, `text_to_video`, `image_to_video`, `reference_to_video`, `text_to_speech`, `text_to_music`, `audio_to_audio`, `speech_to_text`, `text_generation`, `embedding`), `limit` |
80
+ | `ui_add_node` | Add one node | `id`, `type`, `position {x, y}`, `properties` |
81
+ | `ui_connect_nodes` | Connect two nodes | `source_node_id`, `source_handle`, `target_node_id`, `target_handle` |
82
+ | `ui_update_node_data` | Merge into a node's `data` | `node_id`, `data` (e.g. `{properties: {…}}`, `{dynamic_outputs: {…}}`, `{title}`) |
83
+ | `ui_get_graph` | Read graph, revision and validation | `workflow_id` |
84
+ | `ui_delete_node` / `ui_delete_edge` | Remove | `node_id` / `edge_id` |
85
+ | `ui_move_node` | Reposition | `node_id`, `position {x, y}` |
86
+ | `ui_set_node_title` | Rename a node | `node_id`, `title` |
87
+ | `ui_open_workflow` | Open a workflow tab | `workflow_id` |
88
+ | `ui_run_workflow` | Execute | `workflow_id`, `params` |
89
+
90
+ `ui_add_node` takes `properties` only. Anything else on the node's `data`, such
91
+ as `dynamic_outputs`, is set afterwards with `ui_update_node_data`.
92
+
93
+ ## Workflow tools
94
+
95
+ These need no editor.
96
+
97
+ | Tool | Purpose |
98
+ |---|---|
99
+ | `list_workflows`, `get_workflow` | Find and read a workflow |
100
+ | `search_nodes`, `get_node_info`, `list_nodes` | Node discovery. `search_nodes` hides provider nodes (`openai.*`, `kie.*`, …) unless `include_provider_nodes: true`, which is for a provider the user named |
101
+ | `find_model` | A `{provider, model}` ref for a generic AI node's `model` property |
102
+ | `create_workflow {name, graph}` | Save a new graph. Nodes are `{id, type, properties}`. Edges are `{source, sourceHandle, target, targetHandle}` |
103
+ | `validate_workflow {workflow_id \| graph}` | Static check: unknown types, missing properties, unselected models, bad edges |
104
+ | `run_workflow`, `start_background_job`, `debug_workflow` | Execute, execute without waiting, or execute with a full debug report |
105
+ | `run_node {node_type, inputs}` | Run one node to probe what it does (chat only) |
106
+
107
+ For a brief rather than a graph, the planner builds one: `plan_workflow
108
+ {workflow_id}` stores a plan whose steps map to registry node types,
109
+ `update_workflow_plan_step` fixes a step the registry lacks, and
110
+ `build_workflow_from_plan {workflow_id}` places and validates the nodes. On the
111
+ canvas the same flow is `ui_workflow_plan`, `ui_workflow_update_plan_step` and
112
+ `ui_workflow_build_from_plan`.
113
+
114
+ ## Node catalog
115
+
116
+ Catalog entries are search leads. Confirm each type with `get_node_info` or
117
+ `ui_search_nodes` before using it. From the CLI, inspect installed types with
118
+ `nodetool node list` and `nodetool node info <type>`.
119
+
120
+ `nodetool.output.Output` is supplied by the audio pack. If it is needed but
121
+ not installed, run `nodetool packs add audio --yes`. CLI workflow runs also
122
+ return terminal transform outputs: a core-only deterministic text graph can
123
+ end at `nodetool.text.Template` without a dedicated sink. For example,
124
+ `StringInput` → `Template` returns the template's values under `outputs.Template`.
125
+
126
+ | Namespace | Common nodes |
127
+ |---|---|
128
+ | `nodetool.input` | `StringInput`, `IntegerInput`, `FloatInput`, `BooleanInput`, `ImageInput`, `AudioInput`, `VideoInput`, `DocumentInput`, `MessageInput`, `LanguageModelInput` |
129
+ | `nodetool.output` | `Output`. `nodetool.workflows.base_node.Preview` shows an intermediate value |
130
+ | `nodetool.constant` | `String`, `Integer`, `Float`, `Bool`, `List`, `Dict`, `Image`, `Audio`, `Video`, `JSON` |
131
+ | `nodetool.agents` | `Agent`, `Summarizer`, `Extractor`, `Classifier`, `Decision`, `EnhancePrompt`, `CreateThread` |
132
+ | `nodetool.generators` | `ListGenerator`, `DataGenerator`, `StructuredOutputGenerator`, `ChartGenerator`, `SVGGenerator` |
133
+ | `nodetool.text` | `Template`, `Concat`, `Prompt`, `FilterString`, `FilterRegexString`, `Embedding`, `AutomaticSpeechRecognition`, `SaveText` |
134
+ | `nodetool.code` | `Code`: a JavaScript body. See `nodetool-js-scripting` |
135
+ | `nodetool.control` | `If`, `Switch`, `ForEach`, `Collect`, `Loop`, `Chunk`, `Take`, `Zip`, `RepeatCount`, `RepeatValue`, `TryCatch` |
136
+ | `nodetool.data` | `ForEachRow`, `LoadCSVAssets` |
137
+ | `nodetool.document` | `LoadDocumentFile`, `SaveDocumentFile`, `ListDocuments` |
138
+ | `nodetool.image` | `TextToImage`, `ImageToImage`, `LoadImageFile`, `SaveImage`, `Resize`, `Crop`, `RotateAndFlip`, `Compositor`, `Upscale`, `RemoveBackground` |
139
+ | `nodetool.audio` | `TextToSpeech`, `TextToMusic`, `LoadAudioFile`, `SaveAudio`, `AudioMixer`, `Concat`, `Trim`, `Normalize` |
140
+ | `nodetool.video` | `TextToVideo`, `ImageToVideo`, `ReferenceToVideo`, `LoadVideoFile`, `SaveVideo`, `Concat`, `Trim`, `ExtractFrame`, `ExtractAudio`, `AddAudio`, `GetVideoInfo` |
141
+ | `vector` | `Collection`, `IndexTextChunk`, `QueryText`, `HybridSearch`. See `nodetool-rag-indexing` |
142
+ | `lib.*` | `lib.pdf` (text, tables, page images), `lib.image.*` (filters, color, draw), `lib.audio.*` (effects), `lib.svg`, `lib.charts.ChartRenderer`, `lib.browser.Screenshot`, `lib.sqlite.GetDatabasePath` |
143
+
144
+ `lib.http`, `lib.os`, `lib.datetime`, `lib.json`, `lib.math`, `lib.uuid`,
145
+ `nodetool.boolean`, `nodetool.dictionary`, `nodetool.list`, `nodetool.numbers`,
146
+ the old `nodetool.text` helpers such as `Slugify` and `Chunk`, and all `skills.*`
147
+ nodes have been removed. Use the Code node (`nodetool.code.Code`) and its
148
+ snippet library instead. Its body is plain JavaScript with `fetch`,
149
+ `workspace.*` and the other globals `nodetool-js-scripting` lists. Libraries
150
+ such as `@nodetool-ai/sandbox-dates` are imported sandbox packages, not globals.
151
+
152
+ ### Generation nodes
153
+
154
+ Use the generic nodes with a `model` property: `nodetool.image.TextToImage`,
155
+ `ImageToImage`, `nodetool.video.TextToVideo`, `ImageToVideo`,
156
+ `ReferenceToVideo`, `nodetool.audio.TextToSpeech`, `TextToMusic`. Set `model`
157
+ from `find_model` or `ui_search_models`. A provider's own node (`kie.video.*`,
158
+ `openai.*`, `gemini.*`, …) exposes that model's extra settings. Search for one
159
+ only when the user named the provider or needs a setting the generic node does
160
+ not carry, with `include_provider_nodes: true` on `search_nodes`.
161
+
162
+ ### Data types
163
+
164
+ - **Primitives**: `str`, `int`, `float`, `bool`, `list`, `dict`, `any`
165
+ - **Assets**: `{type: "image|audio|video|document", uri: "..."}`
166
+ - **Models**: `language_model`, `image_model`, `video_model`, `embedding_model`, `tts_model`
167
+
168
+ Edges enforce type compatibility. Use `any` for flexible connections. Search
169
+ with `input_type` / `output_type` (`"str"`, `"image"`, `"list"`, …) to find a
170
+ node that fits a handle.
171
+
172
+ ## Workflow patterns
173
+
174
+ - **Pipeline.** Input → transforms → Output. For example `ImageInput` →
175
+ `lib.image.enhance.AutoContrast` → `SaveImage` → `Output`.
176
+ - **Agent generation.** Input → `Agent` or `Summarizer` → post-process →
177
+ Output. `ListGenerator` streams one item per line for fan-out.
178
+ - **Previews.** Put `Preview` nodes after intermediate stages of a multi-stage
179
+ generation so progress is visible.
180
+ - **RAG.** Index: Code node (`workspace.list`) → `LoadDocumentFile` → Code node
181
+ ("Chunk Text" snippet) → `vector.IndexTextChunk`. Query: `StringInput` →
182
+ `vector.HybridSearch` → `Template` → `Agent` → `Output`. See
183
+ `nodetool-rag-indexing`.
184
+ - **Realtime.** `RealtimeAudioInput` → `openai.agents.RealtimeAgent` or
185
+ `openai.agents.LiveAgent` → `Preview`. `LiveAgent` runs full-duplex speech;
186
+ feed it 24 kHz PCM16 microphone chunks.
187
+ - **Multi-modal.** Chain modalities: `AutomaticSpeechRecognition` → `Agent` →
188
+ `TextToImage`, or `ExtractAudio` → `AutomaticSpeechRecognition` →
189
+ `Summarizer`.
190
+ - **Generated media.** `StringInput` (prompt) → `TextToVideo` → `Output`, or
191
+ `ImageInput` + `StringInput` → `ImageToVideo` → `Output`. For lip sync,
192
+ upscaling or avatar work, search `"lip sync"`, `"upscale"` or `"avatar"`.
193
+
194
+ ### Agent outputs and tools
195
+
196
+ - `dynamic_outputs` on an `Agent` (`{"title": {"type": "str"}}`) declares
197
+ structured result fields. The model fills them through a submit tool, and each
198
+ becomes an output handle.
199
+ - To let an Agent call another node as a tool, connect it with a control edge
200
+ (`edge_type: "control"`, both handles `__control__`). The Agent's regular
201
+ outputs (`text`, `chunk`) still route to `Preview` or `Output`.
202
+
203
+ ### Streaming and control flow
204
+
205
+ - Everything is a stream. A single value is a one-item stream.
206
+ - `nodetool.control.Collect` gathers a stream into a list.
207
+ `nodetool.control.ForEach` emits each item of a list.
208
+ - `nodetool.control.Loop` repeats part of the graph: wire `initial` into the
209
+ body, the body's result back into `next`, and a bool into `condition` (loop
210
+ again while true). `max_iterations` bounds it and `done` carries the final
211
+ value. It is the only way to close a cycle. The DSL cannot express the back
212
+ edge, so build loops with the graph tools or workflow JSON.
213
+ - `nodetool.agents.Decision` lets a model make the call: `prompt` is a yes/no
214
+ question, `value` is what it judges (images are shown to the model),
215
+ `decision` is the bool and `if_true`/`if_false` route `value` like `If`. Wire
216
+ `decision` into `Loop.condition` to loop until a model is satisfied, and phrase
217
+ the question so yes means "loop again".
218
+
219
+ ## Debugging and validation
220
+
221
+ `ui_get_graph` returns a `validation` field:
222
+
223
+ - `errors`: blocking issues, such as invalid node types or a Code body that does
224
+ not parse.
225
+ - `warnings`: disconnected required inputs and empty required properties.
226
+ - `suggestions`: orphaned nodes.
227
+
228
+ `validate_workflow` is the headless equivalent and also catches unselected
229
+ models and model ids a provider does not offer. Fix blocking errors and the
230
+ warnings that prevent the requested behavior. Explain any remaining warning
231
+ without expanding the task.
232
+
233
+ - **"Required property 'X' is not set"**: set it with `ui_update_node_data` or in
234
+ the graph's `properties`. Common ones are `model` and `prompt`.
235
+ - **"Required input 'X' not connected"**: add an edge or set a value.
236
+ - **Wrong handle name**: re-read the type with `get_node_info` or
237
+ `ui_search_nodes {include_outputs: true}`.
238
+ - **Type mismatch**: compare the source output type with the target input type.
239
+ - **Node not found**: broaden the query or search by namespace prefix
240
+ (`"nodetool.text"`).
241
+
242
+ Do not retry a failing call unchanged. Agent and generator nodes need `model`,
243
+ generation nodes need `prompt`, and `ForEach` needs a list input. Set them or ask
244
+ the user which value to use.
245
+
246
+ ## Running workflows from a shell
247
+
248
+ This section is for a person or agent with a shell. The in-product agent uses
249
+ `run_workflow` instead.
250
+
251
+ ```bash
252
+ nodetool workflows run ./workflow.json --params '{"input": "hello"}' --json
253
+ nodetool workflows run ./workflow.ts --json # a DSL file, built then run
254
+ nodetool workflows run <workflow_id>
255
+ nodetool workflows list
256
+ nodetool workflows get <id>
257
+ nodetool jobs list
258
+ nodetool serve --port 7777
259
+
260
+ # From a NodeTool checkout
261
+ npm run workflow -- ./workflow.json --input text='hello' --show-messages
262
+ ```
263
+
264
+ ## TypeScript DSL
265
+
266
+ Write workflows as TypeScript with `@nodetool-ai/dsl`. Namespaces mirror node
267
+ namespaces: `nodetool.text` is `text`, `nodetool.control` is `control`,
268
+ `lib.pdf` is `libPdf`, `vector` is `vector`.
269
+
270
+ ```typescript
271
+ import { workflow, input, text, agents, output } from "@nodetool-ai/dsl";
272
+
273
+ const topic = input.stringInput({ name: "topic", value: "tides" });
274
+ const brief = text.template({
275
+ string: "Explain {{ topic }} to a ten-year-old.",
276
+ topic: topic.output()
277
+ });
278
+ const answer = agents.summarizer({ text: brief.output() });
279
+ const result = output.output({ name: "answer", value: answer.output("text") });
280
+
281
+ const wf = workflow(result);
282
+ console.log(JSON.stringify(wf));
283
+ ```
284
+
285
+ - **`node.output()`** is a function. It returns the default output handle and
286
+ only works on a node with one. Name the slot on a multi-output node:
287
+ `answer.output("text")`, `branch.output("if_true")`.
288
+ - **Connectable**: every input accepts a literal value or an output handle.
289
+ - **`workflow(...terminals)`** traces from the terminal nodes and returns
290
+ serializable `{nodes, edges}`. A node shared by two branches appears once.
291
+ - **`run(wf, opts?)`** executes in-process. **`runGraph(...terminals)`** is
292
+ `run(workflow(...terminals))`.
293
+ - Dynamic inputs are extra keys, as `topic` on `text.template` above.
294
+ - There is no `libMath`, `libHttp` or `list` namespace. Use `code.code` for
295
+ arithmetic, JSON, list and HTTP logic.
@@ -0,0 +1,132 @@
1
+ ---
2
+ name: product-commercial
3
+ description: "Direct the look of a polished product film or brand commercial in NodeTool. For the ad's beat structure pair with commercial-beat-sheet, for a finished spot from a product page use launch-commercial, and for phone-shot creator ads use ugc-video."
4
+ ---
5
+
6
+ **Load `/storyboard-core` first.** It carries the loop, the tool contract,
7
+ the entity rules and the gating this skill assumes. Everything below is what is
8
+ different about a camera-grade product film. This skill sets the look; an ad's
9
+ hook, beats and timecodes come from `/commercial-beat-sheet`, which composes with
10
+ it on the same board.
11
+
12
+ ## What makes this job type its own
13
+
14
+ There is no face to hold steady, so the thing that drifts is the **pack**: label
15
+ geometry, cap, proportions, colour. That is what the product entity is for, and it is
16
+ why the reference image matters more here than the descriptor.
17
+
18
+ The other difference is that a product film's shots are mostly about **light**, and
19
+ light lives in the board style, not in the shot.
20
+
21
+ ## Casting the pack
22
+
23
+ The user's pack shot is the entity. Save the attached image as an asset, then
24
+ `create_entity {asset_id, kind: "prop", name, descriptor}`. Write the descriptor as
25
+ geometry and finish — `squat 200ml amber glass cylinder, brushed gold screw cap,
26
+ single centred cream label, serif wordmark` — not as marketing copy.
27
+
28
+ Never invent a logo, a tagline or a label layout. If the label has to be readable, say
29
+ so in the shot's `action` and give that shot a macro framing. Inspect the still
30
+ before rendering its clip. If the label remains unreadable, retry only within the
31
+ user's retry and spending limits. With no retries authorized, report the defect
32
+ and follow the remaining requested stages without claiming the label is readable.
33
+
34
+ Given several angles (front, three-quarter, back, top), tag the best single hero angle
35
+ as the entity and keep the rest as plain assets. An entity is one image asset, so it
36
+ carries one reference into the prompt.
37
+
38
+ ## The look goes in the board style, once
39
+
40
+ | Register | `style` |
41
+ |---|---|
42
+ | Luxury studio | `black cyclorama, one warm key, gold rim, volumetric mist, 85-100mm, 24fps, no grain` |
43
+ | Clean e-comm | `white sweep, soft top light, even fill, 50mm, no shadow detail loss` |
44
+ | Location / lifestyle | `90s print / 35mm commercial, hard noon sun, mild bloom, no other brand marks` |
45
+ | Dark technical | `graphite surface, hard side key, specular edges, controlled falloff` |
46
+
47
+ Add `no on-screen type` to the style. Video models letter badly, and titles belong on
48
+ the timeline as text clips (`edit_timeline` → `add_text_clip`), where they stay
49
+ editable.
50
+
51
+ ## Shot pattern
52
+
53
+ Use the requested shot count. When the brief leaves it open, five to seven shots
54
+ are a starting pattern. Give each cut a distinct purpose. A product film reads as expensive
55
+ when the **light** moves and the pack does not — write that literally:
56
+ `the light streak crosses the glass, the bottle stays still`.
57
+
58
+ - Put the move in `motion` with a distance or a degree: `slow 8cm push-in`,
59
+ `low 30-degree orbit`, `rack focus from cap to liquid`.
60
+ - Give the last shot a **hold**: `last 1.2s dead hold` — models drift on the tail, and
61
+ a hold is what makes an end card usable.
62
+ - Count the physical events: `two condensation beads run`, `one ripple, then still`.
63
+ - On a location spot with talent, cast the person as a character entity per
64
+ `/ugc-video`'s casting pass, but keep the pack's shots free of them with an
65
+ explicit `entity_ids`.
66
+
67
+ ## Where a still is the wrong unit
68
+
69
+ Macro glass, mist and specular highlights are where `keyframe` mode pays — the still
70
+ is cheap and you can judge the light before spending on motion. Keep the default.
71
+
72
+ Switch a shot to `render_mode: "direct"` only for heavy motion the first frame would
73
+ stiffen: a fast orbit, liquid pouring, an explosive reveal. The direct clip prompt
74
+ carries the camera fields, the scene lighting and the board style itself.
75
+
76
+ ## Briefs
77
+
78
+ Adapt these examples to the user's brief. Example deliverables, style choices,
79
+ and approval checkpoints apply only when the user adopts them.
80
+
81
+ **Briefs** below has two: a 10s luxury pack shot with no talent, and a 30s
82
+ street CPG walk with a character. Replace the bracketed placeholders and hand one over
83
+ after the standing orders.
84
+
85
+ ## Briefs — product film
86
+
87
+ One per message, after the standing orders from `/storyboard-core`. Replace
88
+ `[PRODUCT]` and `[CITY]`. `Nova` stands for the character entity's **plain name**.
89
+
90
+ ---
91
+
92
+ ### Luxury pack shot, 10s 16:9
93
+
94
+ ```
95
+ Direct a 10s 16:9 luxury commercial. [PRODUCT] only, no talent, no type.
96
+
97
+ Cast the pack from the attached shot: save it as an asset, then tag it as a prop
98
+ entity. Descriptor is geometry and finish, not copy.
99
+
100
+ Board style: black cyclorama, one warm key, gold rim, volumetric mist, 85-100mm,
101
+ 24fps, no grain, no on-screen type.
102
+
103
+ 1a 0-2 macro. A light streak crosses the glass, two condensation beads run.
104
+ The light moves, the pack does not.
105
+ 2a 2-4 the bottle rises onto black acrylic, key holds.
106
+ 3a 4-6 low 30-degree orbit, label readable at the midpoint.
107
+ 4a 6-8 rack focus from cap to liquid, one ripple, then still.
108
+ 5a 8-10 pull back to a centred hero, key lifts ten percent, last 1.2s dead hold.
109
+
110
+ Stop after the board. Stills before clips.
111
+ ```
112
+
113
+ ### Street CPG walk, 30s 16:9
114
+
115
+ ```
116
+ Direct a 30s 16:9 street commercial in [CITY] for [PRODUCT] with Nova.
117
+
118
+ Cast both: the pack from the attached shot as a prop entity, Nova as a character
119
+ entity from a generated reference you show me first.
120
+
121
+ Board style: 90s print / 35mm commercial, hard noon sun, mild bloom, no other
122
+ brand marks, no on-screen type.
123
+
124
+ 1a 0-4 fisheye on a boom, Nova walking cobbles, [PRODUCT] in hand, long shadow
125
+ 2a 4-8 shoulder track, one bite or sip
126
+ 3a 8-12 POV from inside the cup or wrapper, a building cornice behind
127
+ 4a 12-16 low wide at a crosswalk, one unbranded taxi crosses
128
+ 5a 16-22 she stops and turns once, the pack reads
129
+ 6a 22-27 she leans to the lens, a small late smile. One line, this shot only.
130
+ 7a 27-30 the pack alone on a cafe table, street bokeh, one-second push, dead
131
+ hold on the tail. entity_ids: the product only.
132
+ ```
@@ -0,0 +1,122 @@
1
+ ---
2
+ name: qwen-image-prompting
3
+ description: Prompt Alibaba's Qwen-Image line for the typography work it is built for — quoting exact copy so the model stops inventing it, switching prompt expansion off before it rewrites your strings, describing layout in relative position and weight rather than point sizes, writing non-Latin scripts as characters, and addressing edit references as image 1/2/3. Use whenever the model id names Qwen-Image (alibaba/qwen-image-3/text-to-image, alibaba/qwen-image-3/edit, fal-ai/qwen-image-2/pro/text-to-image, fal-ai/qwen-image-edit-2511, fal-ai/qwen-image-2512, fal-ai/qwen-image-layered, qwen-image-3.0-pro/edit, qwen/qwen-image-edit-plus) on generate_image, edit_image or a TextToImage node.
4
+ ---
5
+
6
+ # Qwen-Image → the model you reach for when the image has words in it
7
+
8
+ Qwen-Image renders text other image models garble. Runware's guide claims
9
+ twelve scripts natively, legible down to roughly 10 px, and long layout
10
+ instructions honoured. That is the
11
+ reason to pick it, and the prompt should spend its length on the copy and the
12
+ layout rather than on adjectives.
13
+
14
+ Reach it with `find_model` for `text_to_image` or `image_to_image`, then
15
+ `generate_image` / `edit_image`. `generate_image` forwards only the prompt, a
16
+ negative prompt, the size and a quality string, and `edit_image` only the
17
+ images, the prompt, a negative prompt, a target size and a strength. The
18
+ expansion flag, guidance and step count exist only on the route's own node:
19
+ find it with `search_nodes`, read its fields with `get_node_info`, and run it
20
+ with `run_node`.
21
+
22
+ On Qwen-Image-3 (`alibaba/qwen-image-3/*`) prompts run to 5000 characters; the
23
+ negative prompt caps at 500. `fal-ai/qwen-image-2/edit` and the
24
+ `qwen-image-max` routes cap the prompt at 800.
25
+
26
+ ## Quote the copy
27
+
28
+ Anything in quotation marks is treated as literal copy. Anything you describe
29
+ instead, the model writes for you — usually plausibly and never what you meant.
30
+
31
+ > A vertical science poster. Title "轨道力学" in large bold characters across the
32
+ > upper third, subtitle "Orbital Mechanics" in small widely-spaced capitals
33
+ > directly beneath. Three labelled diagram panels down the centre, captions
34
+ > "近地点", "远地点", "转移轨道" under each. A thin rule separates the diagram block
35
+ > from a footer reading "第三版 · 2026". Flat editorial illustration, two-colour
36
+ > palette. No other text.
37
+
38
+ Three habits that make it land:
39
+
40
+ - **Write the characters, never describe them.** Paste the Chinese, Japanese,
41
+ Korean or Spanish string itself, with its diacritics. "The Chinese word for
42
+ orbit" produces something that is not that word.
43
+ - **State the relationship between scripts** in a bilingual layout — "on the
44
+ same line", "stacked directly beneath" — and match their size and weight, or
45
+ one language reads as an afterthought.
46
+ - **Close with "no other text".** Left to itself, the model fills empty margins
47
+ with invented lettering.
48
+
49
+ This is the model for copy that is part of the image — a poster, a label, a
50
+ sign in the scene. A title or a super on a still headed for a timeline is not:
51
+ that goes on as a text clip afterwards (`caption-titles`), where it can be
52
+ corrected and re-timed.
53
+
54
+ ## Turn prompt expansion off for typography
55
+
56
+ On the hosted tiers — Qwen-Image-2, Max and 3 — `enable_prompt_expansion`
57
+ defaults to true and runs an LLM over your prompt first. AtlasCloud's
58
+ `qwen-image-3.0` and `qwen-image-3.0-pro` routes spell it `prompt_extend`, also
59
+ true by default. It helps a three-word
60
+ prompt and ruins a typographic one: it edits the strings you quoted and adds
61
+ signage you did not ask for. Set it false whenever exact copy matters.
62
+
63
+ The open-weight checkpoints (`fal-ai/qwen-image`, the `qwen-image-edit-*`
64
+ releases) have no such flag and take your prompt as written. On fal they expose
65
+ `guidance_scale` and `num_inference_steps` instead, and LoRA variants — raise
66
+ guidance when the layout is being loosely interpreted, and leave the step
67
+ count alone until the prompt is settled. Replicate's `qwen/qwen-image` and
68
+ `qwen/qwen-image-2512` call the dial `guidance`, and `qwen/qwen-image` adds an
69
+ `enhance_prompt` rewrite that is off by default. Several Replicate edit routes
70
+ (`qwen/qwen-image-edit`, `qwen/qwen-image-edit-plus`,
71
+ `qwen/qwen-image-edit-2511`) expose neither dial.
72
+
73
+ ## Layout is relative, not metric
74
+
75
+ Point sizes and pixel coordinates mean nothing here. Vertical order, relative
76
+ size and relative weight mean everything. "Large bold capitals across the top,
77
+ small widely-spaced capitals directly beneath, body copy in two columns below
78
+ the rule" is a layout the model can execute. "24pt heading at x=120" is not.
79
+
80
+ For dense label sets, add structural marks — rules, borders, panels. Without
81
+ them a block of labels renders as a list.
82
+
83
+ ## Size
84
+
85
+ On Qwen-Image-3, text-to-image runs 512×512 to 2048×2048 total pixels and
86
+ editing runs 512×512 to 1440×1440; the other checkpoints have their own
87
+ ceilings, so read the endpoint. Small type needs the pixels: draft the
88
+ composition small, then render the deliverable near the ceiling, and choose a
89
+ taller canvas when the fine print has to survive.
90
+
91
+ ## Editing
92
+
93
+ Qwen-Image-3 Edit takes one to three reference images and **order matters** —
94
+ the prompt addresses them as "image 1", "image 2", "image 3". References run
95
+ 384–2048 px per side, 10 MB each, JPEG/PNG without alpha or WEBP. The earlier
96
+ edit checkpoints take a list too, and the same "image N" convention applies.
97
+
98
+ > Place the bottle from image 1 on the marble surface from image 2, keeping the
99
+ > bottle's label, proportions and cap exactly as they are. Match the lighting
100
+ > direction of image 2. Replace the label copy with "SERIES 04" in white
101
+ > sans-serif capitals.
102
+
103
+ Name what stays as explicitly as what changes. Recolouring a packshot, carrying
104
+ a product into a new scene, and staging two products together are all the same
105
+ instruction shape: what comes from which image, what must not move.
106
+
107
+ ## Symptoms
108
+
109
+ | What went wrong | What to change |
110
+ | :--- | :--- |
111
+ | The text is close to yours but not yours | Quote it, and set `enable_prompt_expansion` (or `prompt_extend`) false |
112
+ | Invented words in the margins | Add "no other text" |
113
+ | Non-Latin script renders as garbage | Paste the characters instead of describing them |
114
+ | Small type is illegible | Render nearer the pixel ceiling, or use a taller canvas |
115
+ | The wrong reference supplied the subject | Say "image 1" / "image 2" explicitly and check the order you sent |
116
+ | Hierarchy is flat | Give each line a relative size and weight, not a size in points |
117
+
118
+ Grade the result with `critique_image` and `score_image_adherence`, which will
119
+ tell you whether the copy rendered as written before you look at the picture.
120
+
121
+ Adapted from Runware's Qwen-Image-3.0 text and typography guide:
122
+ https://runware.ai/docs/models/alibaba-qwen-image-3-0/guides/text-and-typography
@@ -0,0 +1,106 @@
1
+ ---
2
+ name: script-video
3
+ description: "Direct a NodeTool video whose timing follows written voiceover, such as a narrated explainer or narrated b-roll: script, voice, and shots timed to the takes. For an explainer's beat structure, pair with explainer-storyboard. For on-camera creators, use ugc-video."
4
+ ---
5
+
6
+ **Load `/storyboard-core` first.** It carries the loop, the tool contract and
7
+ the gating this skill assumes. This job type inverts the order of that loop, so read
8
+ the sequence below before creating a board. For a product explainer, load
9
+ `/explainer-storyboard` as well: it sets the teaching arc and the beats, and this
10
+ skill turns their voiceover into a script that times the shots.
11
+
12
+ ## What makes this job type its own
13
+
14
+ Everywhere else the picture comes first and the words ride along. Here the words are
15
+ the spine: they set the running time, and each shot has to be exactly as long as the
16
+ line it covers. That is a different tool path — a **script resource** the storyboard
17
+ links to — not a storyboard with narration typed into it.
18
+
19
+ The payoff is that `assemble_storyboard_timeline` on a linked board cuts words and
20
+ picture together: each shot runs as long as the takes it covers, and every voiced line
21
+ becomes its own voiceover clip.
22
+
23
+ ## The sequence
24
+
25
+ 1. **Research and write the voiceover.** 150–165 words is about 60 seconds. Write it
26
+ before anything visual exists.
27
+ 2. `create_script {name}`.
28
+ 3. `edit_script {script_id, ops}` — `{"op": "add_speaker", "name": "Narrator"}`, then
29
+ one `{"op": "add_line", "speaker": "Narrator", "text": "..."}` per sentence. A line
30
+ with no `speaker` has no voice to inherit and reads `no_voice` however the cast is
31
+ set. One sentence per line: a line is the unit that gets a take and times a shot,
32
+ so a paragraph in one line gives you a shot you cannot cut.
33
+ 4. **Give the narrator a voice:** `find_model` (`text_to_speech`) →
34
+ `{"op": "set_speaker_voice", "target": "Narrator", "provider", "model", "voice"}`.
35
+ Without it every line reads `no_voice`. `voice` is the provider's own voice name
36
+ (`Aoede`, `alloy`), not a model id.
37
+ 5. `derive_storyboard_from_script {script_id, provider, model}` — one shot per line, in
38
+ reading order, each carrying the line ids it covers. Pass a language provider and
39
+ model for a director pass that writes each shot's visuals over that scaffold; omit
40
+ **both** for the bare scaffold.
41
+ 6. **Rewrite the visuals.** The derived shots are a starting point. Go through with
42
+ `edit_storyboard` `update_shot`, giving each an `action` that is concrete b-roll and
43
+ a `motion` with a count. Set the board `style` and `entity_ids` with `set_board`.
44
+ Confirm the shot count and the models with the user before the first render.
45
+ 7. **Stills on every shot**, reviewed, before any clip.
46
+ 8. `voice_script_lines {script_id}` — defaults to every draft or stale line. Word
47
+ timings come from a transcription pass (`transcribe: true` by default) and ride into
48
+ the clips as captions.
49
+ 9. Clips, then `assemble_storyboard_timeline`, then `validate_timeline`.
50
+ 10. **Duck the pictures' own sound.** Assembly gives every shot clip an audio twin on a
51
+ "Shot Audio" track. `edit_timeline` → `get_state`, then `set_clip_params` with
52
+ `muted: true` on those clips so the voiceover carries.
53
+
54
+ ## Timing
55
+
56
+ A shot on a linked board takes its length from its lines' takes — that is
57
+ `duration_source: "audio"`, the default once linked. Leave it alone. Pin a shot with
58
+ `duration_source: "manual"` plus `duration_seconds` only when it must hold past its
59
+ narration, such as a final card.
60
+
61
+ Voice **before** you cut. A line with no take leaves its shot on its own duration, and
62
+ the assembly lists it as skipped.
63
+
64
+ ## Style registers
65
+
66
+ Put one in the board `style`, and say `no on-screen type` — titles belong on the
67
+ timeline as text clips, where they stay editable.
68
+
69
+ | Register | `style` |
70
+ |---|---|
71
+ | Flat 2D | `flat vector illustration, three-colour palette, thick uniform strokes, white ground` |
72
+ | Documentary macro | `macro photography, shallow depth, natural light, matte film grade` |
73
+ | Dark UI | `dark interface on a black ground, single accent colour, crisp type-free panels` |
74
+ | Paper-craft | `cut-paper layers, soft drop shadows, warm raking light` |
75
+
76
+ ## Music
77
+
78
+ `edit_storyboard` → `{op: "set_board", music_prompt: "..."}` sets the score direction,
79
+ headless, and assembly turns it into a draft music clip under the voiceover. For a bed
80
+ you generate yourself instead: `generate_music`, then `edit_timeline` → `add_track` +
81
+ `add_media_clip`, with its `volumeDb` low enough to sit under the voiceover.
82
+
83
+ ## Brief
84
+
85
+ Adapt these examples to the user's brief. Example deliverables, style choices,
86
+ and approval checkpoints apply only when the user adopts them.
87
+
88
+ ```
89
+ Research [TOPIC], then direct a faceless 16:9 explainer. No on-camera talent.
90
+
91
+ Write the voiceover first, 150-165 words, one sentence per line, as a script:
92
+ create_script, then edit_script to add a Narrator speaker and the lines, each
93
+ line with speaker Narrator. Give the narrator a voice with set_speaker_voice
94
+ before anything else.
95
+
96
+ Then derive_storyboard_from_script with a director pass, and rewrite each of the
97
+ eight shots' action and motion as concrete b-roll to that voiceover.
98
+ Board style: [flat 2D / documentary macro / dark UI / paper-craft], no on-screen
99
+ type.
100
+
101
+ Stills on every shot before any clip. Then voice_script_lines so the shots take
102
+ their length from the takes, then clips, then assemble_storyboard_timeline and
103
+ validate_timeline. Finish by muting the Shot Audio clips under the voiceover.
104
+
105
+ Stop after the script and the board. Show me the words before you spend.
106
+ ```