@nodetool-ai/agents 0.8.4-a24.3 → 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,259 @@
1
+ ---
2
+ name: motion-direction
3
+ description: Set the motion language for a piece before anything is animated — one easing family, one timing unit, one transition family, one stagger rhythm — and audit a timeline against it. Use when starting a title pass or a whole cut, when animation feels busy, cheap or inconsistent across shots, or when turning a brand or brief into motion rules an agent can follow. Not for individual clip mechanics — that is timeline-edit-ops.
4
+ ---
5
+
6
+ # Motion Direction → the rules everything else obeys
7
+
8
+ Direction is the judgment layer above craft. It turns a brief into a short set of
9
+ motion rules, so every shot reads as one hand. Most of the work is subtraction:
10
+ deciding what does not move.
11
+
12
+ `motion-principles` gives the numbers and owns them: where this page and it
13
+ disagree, it wins. This decides which of its numbers the whole piece is
14
+ allowed to use.
15
+
16
+ ## The one rule
17
+
18
+ Lock the motion language first, then animate to it. Pick **one** of each row
19
+ below and reuse it. Consistency reads as confidence; variety reads as noise. When
20
+ in doubt, repeat rather than invent.
21
+
22
+ ## The motion-language spec
23
+
24
+ Fill every row once, at the top of the job, and state it back to the user before
25
+ you animate. The rows guide the timeline document; `timeline-edit-ops` owns
26
+ the tool calls.
27
+
28
+ | Row | Pick one | Example |
29
+ |---|---|---|
30
+ | Easing family | One entrance `easing` and one exit `easing` | `cubic-bezier(0.16,1,0.3,1)` in, `cubic-bezier(0.7,0,0.84,0)` out |
31
+ | Base timing unit | The atomic `durationMs`; everything else is a multiple | 400 — micro 200, hero 800 |
32
+ | Transition family | Which cut style this piece uses | `crossfade` only, hard cuts elsewhere |
33
+ | Stagger rhythm | One `offsetMs` and one `from` | 80ms, `from: "start"` |
34
+ | Motion intensity | The travel, scale and overshoot budget | `distance` ≤ 0.15, `overshoot` ≤ 1.05 |
35
+ | Hold discipline | The read hold and the rest between moves | `motion-principles` § Holds: ≥ 1000ms read hold, ≥ 400ms rest |
36
+ | Type family and weights | One bundled family and a weight for each text tier | `Inter` 800 for the hero, 400 for support |
37
+ | Space and focus | One camera path and a depth plan, if the piece needs 2.5D | Hero at `depthPx: 0`, background farther away |
38
+ | Shutter and texture | Which layers blur, echo, or step | Hero blur on the impact, background held |
39
+
40
+ Two easings maximum: one for entrances and landings, one for exits. A third has
41
+ to justify itself.
42
+
43
+ ## Match depth to the brief
44
+
45
+ For a showcase, hero, launch, or "best" piece, plan each scene with a background
46
+ bed, midground, foreground, and a grain or grade finish. Group each scene. Use
47
+ path shapes, masks, repeaters, style tracks, animation links, and effects where
48
+ they give the composition depth. Read the full document with `get_timeline` and
49
+ use `set_timeline_document` for fields `edit_timeline` cannot write, including
50
+ `styleTracks` and `repeater`.
51
+
52
+ Study a shipped reference before building. Call `list_example_timelines` to
53
+ choose a slug, then `get_example_timeline` to read its stats, scene catalog and
54
+ first scene excerpt. Use `scene_id` to choose another scene and `clip_offset` /
55
+ `clip_limit` to page through its layers. The CodeAct forms are
56
+ `nodetool.timelines.examples.list()` and
57
+ `nodetool.timelines.examples.get("kite", {clip_limit: 12})`. No library install
58
+ or repository filesystem is needed. Kite, Prism, Voltra and Tidewater show
59
+ different ways to group scenes and combine keyframed layers. Cadence shows a
60
+ vertical data story built from the chart helpers and a reusable component.
61
+
62
+ Build with the craft methods, not hand-keyframed fades. `video({palette,
63
+ fonts, ...})` from `@nodetool-ai/sandbox-timeline` gives every scene
64
+ `s.backdrop()`, `s.glow()`, `s.flash()`, `s.kicker()`, `s.pill()`,
65
+ `s.streaks()` and `s.finish()` — the vocabulary the shipped examples build
66
+ scenes from. A slam, a rise or a grow is `el.enter()`/`el.animate()` with the
67
+ right `from`/props (`title.enter({from: {scale: 1.35, blur: 26, opacity: 0}})`
68
+ is a slam), a count-up is `el.count()`, a draw-on is `el.draw()`. The depth
69
+ plan below maps directly onto the API — reach for one of these before a bare
70
+ `animate()` custom curve:
71
+
72
+ | Plane | Built with |
73
+ |---|---|
74
+ | Background bed | `s.backdrop()`, `s.streaks()`, a low-amplitude `el.loop()` |
75
+ | Midground | supporting elements, laid out in a container (`frame-composition`'s `s.stack`/`s.row`) |
76
+ | Hero | the one element with the boldest `el.enter()`/`el.animate()`, on the beat |
77
+ | Finish | `s.finish()` inside each scene, or `v.adjust()` for a whole-video grade after `v.series()` |
78
+
79
+ Plan every showcase scene across all four before writing the hero's
80
+ animation. Read the pack documentation
81
+ (`nodetool.packs.docs("@nodetool-ai/sandbox-timeline")`) for every method's
82
+ signature. A generated still or music bed can lift a showcase, but generation
83
+ costs money. Build from the user's assets and the pack's own vocabulary by
84
+ default. Propose a generated asset, with how many and what for, and generate
85
+ it only after the user agrees.
86
+
87
+ After saving a showcase, call
88
+ `validate_timeline` with `{"timeline_id":"<id>","tier":"showcase"}`, or
89
+ `nodetool.timelines.validate(id, {tier: "showcase"})`. Address its warnings
90
+ about scene groups, custom keyframes, finish, camera and concurrent visual
91
+ layers. These checks inspect document structure. Preview the result to judge
92
+ composition and readability. Standard validation remains appropriate for
93
+ ordinary edits and intentionally minimal pieces.
94
+
95
+ ## Typography
96
+
97
+ Choose the family and weights in the motion-language spec before building text
98
+ clips. NodeTool ships `Inter`, `Space Grotesk`, `Bebas Neue`, `Playfair Display`,
99
+ `Lora`, and `JetBrains Mono`. Use one family across a piece and make the hero
100
+ visibly heavier than support, such as Inter 800 against 400. `Bebas Neue` ships
101
+ only at 400, so use size and spacing for contrast if you choose it.
102
+
103
+ On a 1920×1080 frame, start a hero title at 96–160 `fontSizePx`, support at
104
+ 42–64, and a short kicker at 28–36. Keep large display tracking tight
105
+ (`letterSpacingPx` −2 to 1); open an uppercase kicker to 2–5px. Check the
106
+ actual words at the target frame size and adjust for fit and legibility.
107
+
108
+ For an existing text clip, `edit_timeline` can set the type as a
109
+ `set_clip_params` patch:
110
+
111
+ ```json
112
+ {"timeline_id":"<id>","ops":[{"op":"set_clip_params","target":"Hero title","textStyle":{"fontFamily":"Inter","fontWeight":800,"letterSpacingPx":-1,"fontSizePx":128}}]}
113
+ ```
114
+
115
+ `timeline-edit-ops` owns the full op contract. `frame-composition` handles
116
+ placement and safe areas for each aspect ratio.
117
+
118
+ ## Tone and energy
119
+
120
+ Place the piece on two axes and commit. Mixing cells inside one piece is the
121
+ usual cause of "inconsistent".
122
+
123
+ | | Soft (organic, eased) | Sharp (precise, snappy) |
124
+ |---|---|---|
125
+ | **Calm** | Luxury, wellness, editorial: long windows, generous holds, minimal stagger | Premium tech, finance: deliberate, clean, unhurried, no overshoot |
126
+ | **Kinetic** | Lifestyle, playful, kids: overshoot and spring, loose timing | Sports, hype, gaming: short windows, hard cuts, accents on beats |
127
+
128
+ ## Motion personality
129
+
130
+ The named preset that fills the spec's easing, timing and intensity rows. Pick
131
+ one per project and hand it to every later step by name.
132
+
133
+ | Personality | Entrance `durationMs` | Entrance `easing` | Exit `easing` | Overshoot | Presets it lives on |
134
+ |---|---|---|---|---|---|
135
+ | **Playful** | 400–600 | `easeOutBack` or `cubic-bezier(0.34,1.56,0.64,1)` | `easeIn` | `overshoot` 1.1–1.2 | `pop`, `bounce`, `squash`, `float` |
136
+ | **Premium** | 400–600 | `cubic-bezier(0.16,1,0.3,1)` | `easeIn` | none | `fade`, `blur`, `kenBurns`, `breathe` |
137
+ | **Corporate** | 300–400 | `easeOut` | `cubic-bezier(0.7,0,0.84,0)` | `overshoot` ≤ 1.03 | `fade`, `slide`, `wipe` |
138
+ | **Energetic** | 150–250 | `easeOut` | `cubic-bezier(0.7,0,0.84,0)` | `overshoot` 1.15–1.3 | `pop`, `flash`, `shake`, `spin` |
139
+
140
+ The entrance ranges come from `motion-principles`: its playful landing for
141
+ Playful, its title and hero entrances for Premium and Corporate, and its micro
142
+ beat for Energetic. Exits run shorter than entrances, as its quick reference
143
+ gives.
144
+
145
+ Premium sits calm-soft, Corporate calm-sharp, Playful kinetic-soft, Energetic
146
+ kinetic-sharp. Default to Corporate for product and Playful for lifestyle.
147
+ `easeOutElastic` and `easeOutBounce` belong to Playful alone.
148
+
149
+ ## Motion hierarchy
150
+
151
+ Rank every element, animate down the list, and stop early.
152
+
153
+ | Tier | What it is | How it moves |
154
+ |---|---|---|
155
+ | Hero | The one thing the moment is about | The boldest, longest, most-eased move; lands on the beat |
156
+ | Support | Context that helps the hero land | Smaller and faster, out of the way, never competing |
157
+ | Texture | Bed, grain, ambient drift | A `loop` preset at low amplitude; no hard events |
158
+
159
+ These are the three layers `motion-principles` names Primary, Secondary and
160
+ Ambient. If two elements compete for the eye in one frame, the direction failed:
161
+ demote one before touching its keyframes.
162
+
163
+ Hierarchy is also a track decision. Lowest track `index` renders on top, so the
164
+ hero belongs on a low index and the bed on a high one; a scrim sits between the
165
+ picture it darkens and the text it carries. For a camera move, set clip
166
+ `transform.depthPx` separately from track order. The sequence's `camera2d`
167
+ can keyframe position and depth while `focusDepthPx` and `aperturePx` decide
168
+ which plane softens. Keep one plane legible while the others move.
169
+
170
+ Use `layout` (a flex container on a group clip, its real children via
171
+ `parentId`) for elements whose spacing should survive a text or shape
172
+ change: `flexDirection: "row" | "column"` with `gap` keeps siblings apart,
173
+ and a plate sized from live text is a `flexItem: {position: "absolute",
174
+ inset: 0}` sibling in a padded container. Use
175
+ `animationLinks` when one clip should follow another's authored position,
176
+ scale, rotation, or opacity. Linked followers share one motion decision;
177
+ they do not chain through a second link.
178
+
179
+ ## Restraint
180
+
181
+ For every element ask whether the motion carries meaning. If not, hold it still.
182
+
183
+ - Do not animate the whole frame at once. Leave the eye an anchor.
184
+ - Do not stack a transition on a transition — a `wipe` cut under a `spin` in
185
+ under a `flash` is three ideas competing for 500ms.
186
+ - Do not loop-animate text somebody is still reading.
187
+ - Do not give overshoot to serious content.
188
+ - One outsized moment per piece. A second cancels the first.
189
+ - A clip that both dissolves in and carries a `fade` in ramps twice and reads
190
+ slower than either alone. Pick one.
191
+
192
+ ## Pacing
193
+
194
+ Map energy across the whole timeline before timing any single move: a low open,
195
+ a build, one peak, a settled end. Vary it on purpose — tension, then release.
196
+ Stillness is pacing, not a gap. `beat-sync-editing` turns this shape into cut
197
+ points.
198
+
199
+ An animation's `beat` anchor follows the document tempo: one-based `index`,
200
+ `scope: "clip"` or `"sequence"`, and optional `offsetMs`. A measured audio
201
+ curve from `bake_audio_animation` follows the audio source instead. Use the
202
+ former for a rhythmic rule and the latter when a real onset or envelope must
203
+ drive the picture. `stagger_animations` offsets existing animations across an
204
+ ordered list of clip IDs while leaving media timing fixed.
205
+
206
+ Choose motion texture deliberately. `repeater` makes positioned, delayed
207
+ copies of one clip; `temporalEcho` trails it with fading delayed copies;
208
+ `steppedTime` quantizes its clock. Per-clip `motionBlur` controls its own shutter
209
+ angle and minimum sample count. When layers request different counts, the
210
+ scene uses the highest count up to 32 and samples each layer evenly across its
211
+ own shutter. Give blur to the fast hero when it clarifies direction, then
212
+ inspect the held frame for readability.
213
+
214
+ ## Direction notes, per shot
215
+
216
+ Write intent, not keyframes. One line per shot is enough for someone else — or a
217
+ later turn — to animate it:
218
+
219
+ > Shot 3, 4s. Hero: the price plate, `pop` in on the downbeat, Corporate.
220
+ > Support: the caption 80ms behind it, `fade`. Texture: bed holds `kenBurns`
221
+ > from shot 2. Nothing else moves.
222
+
223
+ For a repeated title or logo system, inspect `list_compositions` before
224
+ building bare clips. `title-slam`, `word-cards`, and `logo-sting` are starting
225
+ rigs when their timing matches the brief. `timeline-edit-ops` owns the tool
226
+ contract. `set_clip_params` does not accept the new camera, layout, link,
227
+ repeat, echo, step, or per-clip blur fields; author those through the full
228
+ document with `set_timeline_document` after reading it with `get_timeline`.
229
+
230
+ ## The consistency audit
231
+
232
+ Before calling a pass done, read the document back with `get_timeline` and check
233
+ every clip against the spec. A miss is a direction defect, not a preference.
234
+
235
+ - Same easing family on comparable moves, and no stray `linear` outside loops.
236
+ - Every `durationMs` a multiple of the base unit.
237
+ - Only the chosen transition types on the cut.
238
+ - One text stagger rhythm, and one `offset_ms` order for cross-clip builds.
239
+ - Hold discipline respected: every read hold and every rest between moves meets
240
+ `motion-principles` § Holds. A still hero is not a still frame: the Texture
241
+ bed keeps moving through the holds, so a static bed is still a finding in
242
+ the `motion-graphics` review pass.
243
+ - Exactly one hero animating at any instant.
244
+ - One font family, and it is a bundled one — `Inter`, `Space Grotesk`,
245
+ `Bebas Neue`, `Playfair Display`, `Lora`, `JetBrains Mono`. Anything else
246
+ reports `font_not_portable` and resolves differently per host.
247
+
248
+ Then look: `preview_timeline_frame` over the midpoints of the moves you just
249
+ audited. An audit that only read the document has checked the spec, not the
250
+ picture.
251
+
252
+ ## Required example comparison
253
+
254
+ Run the review pass `motion-graphics` owns (its `## Review pass`) once, not
255
+ twice per piece — contact sheet, per-scene checklist, example comparison,
256
+ `tier: "showcase"` validation, and a real revision. Judge what comes back
257
+ against this page's spec too: does the busiest scene still respect Motion
258
+ hierarchy, does Restraint survive under the example's density, is the chosen
259
+ Motion personality still legible once the gaps are closed.
@@ -0,0 +1,217 @@
1
+ ---
2
+ name: motion-graphics
3
+ description: Build and review a NodeTool timeline piece — the motion agent's entry point. Covers the build loop, choosing between the code pack and edit ops, the revise-by-editing-the-script loop, and the review pass. Use for title cards, kinetic text, lower thirds, clip entrances and exits, and layered motion.
4
+ featured: true
5
+ ---
6
+
7
+ # Motion Graphics → Timeline Agent
8
+
9
+ ## Before the first edit of a new piece
10
+
11
+ When starting a piece from a brief, load `motion-direction` and
12
+ `motion-principles` with `load_skill` before the first timeline edit. Use them
13
+ to set the motion language and timing for the whole piece. Do not start
14
+ animating from this file alone.
15
+
16
+ If the piece has a title layout or an end card, also load `frame-composition`
17
+ before that first edit. If it has an end card, load `logo-reveal` too. These
18
+ loads are required even when this skill was the only one selected from the
19
+ catalog.
20
+
21
+ For showcase, hero, launch, or "best" briefs, follow `motion-direction`'s
22
+ depth plan and example timelines before writing scenes.
23
+
24
+ Motion is built, then checked. Author with the pack or with ops (below), then
25
+ look at the frames with `preview_timeline_frame`. A change you have not looked
26
+ at is not done.
27
+
28
+ ## Load other craft skills for the job
29
+
30
+ This file is the build loop and the entry point, not the op contract. After
31
+ the required loads above, load other relevant craft skills for the job and
32
+ come back here to pick the authoring surface.
33
+
34
+ | The question | Skill |
35
+ |---|---|
36
+ | Where do the cuts land, how do I pace it, how do I ramp a hit | `beat-sync-editing` |
37
+ | Which colours, in what order do I grade, what can actually move | `color-motion` |
38
+ | A preset is close but not right; overshoot, decay, wiggle, arc, draw-on | `motion-curves` |
39
+ | An ambient bed behind the content | `motion-background` |
40
+
41
+ Three are neighbours rather than layers. `caption-titles` decides what text
42
+ says and when it appears. `video-audio-continuity` decides, before the first
43
+ clip renders, whether a multi-scene piece's sound comes from one generated clip
44
+ or from a track of your own — a decision this file's ops cannot undo. The
45
+ board skills (`explainer-storyboard`, `commercial-beat-sheet`,
46
+ `launch-commercial`, `music-video-treatment`, `trailer-template`) decide the
47
+ shots before there is a timeline to animate; `assemble_storyboard_timeline` is
48
+ where their board becomes this file's document.
49
+
50
+ Anything generated for the timeline — a bed, a sting, a voice, a clip — has a
51
+ model line with its own guide. `find_model` returns `prompting_skill` on a
52
+ route it covers; `load_skill` it before writing that prompt.
53
+
54
+ ## The build loop
55
+
56
+ Build a piece in four moves: **direct** (fix the motion language and the
57
+ scene list before writing anything — `motion-direction` and, when there is a
58
+ title or end card, `frame-composition`/`logo-reveal`), **build** (write the
59
+ scenes in code, one `v.scene` per beat), **save** (`v.save()` attaches the code; read
60
+ `saved.code` and `saved.warnings`), **review** (the
61
+ pass below). Revise by editing that script — see below — and repeat the
62
+ last two steps. A piece that renders clean on the first save has not been
63
+ looked at yet.
64
+
65
+ ## Choose the authoring surface
66
+
67
+ Two ways to touch a timeline document. Pick by what changed, not by habit.
68
+
69
+ | Situation | Surface |
70
+ |---|---|
71
+ | A new cut, or a scene with more than a few clips | `@nodetool-ai/sandbox-timeline` (code) |
72
+ | A few edits to a cut that already exists — retiming a clip, swapping a preset, nudging a stagger | `edit_timeline` ops |
73
+
74
+ **Code, for a new cut.** In a JS script or a Code node, `import { video } from
75
+ "@nodetool-ai/sandbox-timeline"`. Its `video()` builds a document of scenes
76
+ (`v.scene(name, seconds, fn)`, local time in seconds), lays them out with
77
+ `v.series([...])`, and `v.save(nodetool.timelines, {...})` validates, creates
78
+ the timeline, writes the whole document and validates again. It throws on any
79
+ validation error and returns `{timeline_id, warnings}` plus `code`. A cut
80
+ with hundreds of keyframed clips costs a handful of API calls this way,
81
+ instead of one `edit_timeline` op per clip. NodeTool's shipped example
82
+ timelines (Cadence, Kite, Prism, Serein, Voltra, Tidewater, T minus 30) are built with it. Its
83
+ full signatures, options and gotchas are the pack's own skill — read
84
+ `nodetool.packs.docs("@nodetool-ai/sandbox-timeline")` before writing scenes,
85
+ not this file. `frame-composition` covers how to lay out what goes inside a
86
+ scene; this file covers the build loop and the ops surface below.
87
+
88
+ **Ops, for an edit.** `edit_timeline` takes a timeline ID and an `ops` array,
89
+ run in order, up to 60 per call:
90
+
91
+ ```json
92
+ {"timeline_id":"<id>","ops":[{"op":"animate_clip","target":"Title","animations":[{"role":"in","preset":"fade","durationMs":400}]}]}
93
+ ```
94
+
95
+ Its full op shapes, what each op refuses, and when a change instead needs a
96
+ whole-document `set_timeline_document` write are `timeline-edit-ops` — load it
97
+ before a batch of ops, not this file.
98
+
99
+ ## A compact two-scene piece
100
+
101
+ The build loop end to end: a backdrop, a laid-out title block, a counter, a
102
+ transition, a finish pass, save, then read the warnings.
103
+
104
+ ```js
105
+ import { video } from "@nodetool-ai/sandbox-timeline";
106
+
107
+ const v = video({
108
+ width: 1920, height: 1080, fps: 30,
109
+ palette: { ink: "#06110e", ink2: "#0c2a21", text: "#f4fbf8", accent: "#34d399" },
110
+ fonts: { display: "Space Grotesk", body: "Inter" }
111
+ });
112
+
113
+ const hello = v.scene("Hello", 2.5, (s) => {
114
+ s.backdrop();
115
+ const kicker = s.kicker("LIVE");
116
+ const title = s.text("Round-ups", { size: 120, weight: 700, font: "display" });
117
+ const sub = s.text("on every card", { size: 40, color: "#8fb3a6" });
118
+ s.stack([kicker, title, sub], { gap: 20 }); // frame-composition: a title block
119
+ title.enter({ from: { scale: 1.2, opacity: 0 }, dur: 0.3, ease: "outExpo" });
120
+ sub.enter({ from: { offsetY: 24, opacity: 0 }, at: 0.15, dur: 0.25 });
121
+ });
122
+
123
+ const outro = v.scene("Outro", 1.8, (s) => {
124
+ s.backdrop({ colors: ["#0f3b2e", "#06110e"] });
125
+ const counter = s.text("$0.00", { size: 96, font: "display" });
126
+ counter.count({ from: 0, to: 1.6, dur: 1.2, prefix: "$", decimals: 2 });
127
+ s.finish({ vignette: 0.2, grain: 0.05 });
128
+ });
129
+
130
+ v.series([hello, v.transition("wipe", 0.3, { direction: "left" }), outro]);
131
+
132
+ const saved = await v.save(nodetool.timelines, { name: "Round-ups", showcase: true });
133
+ if (saved.warnings.length) {
134
+ // read them before calling this done — see Review pass below
135
+ }
136
+ ```
137
+
138
+ ## Author and revise with code
139
+
140
+ `v.save(nodetool.timelines, {...})` attaches the code that rebuilds the
141
+ timeline as its source — no separate `code.set` call. The stored code is the
142
+ retained program: the scene callbacks and the helpers they use, with every
143
+ research result, generated asset id and loop value written in as a literal.
144
+ The calls that produced those values ran once, in your action, and a rebake
145
+ never repeats them. `saved.code.embedded` is false, with the reason in
146
+ `saved.code.warnings`, when a scene callback calls `nodetool.*`, uses
147
+ `Math.random()`/`Date.now()`, or reads a value that is not plain data. Do the
148
+ research and generation at the top level, then build the scenes from the
149
+ results. To attach code after the fact,
150
+ `nodetool.timelines.code.set(timeline_id, code)` bakes the code, which must
151
+ build the timeline without a capability call.
152
+
153
+ Revising a code-backed timeline is: `code.get(id)` → `code.edit(id, edits)`
154
+ with a few small exact string replacements (each `old` matches exactly once)
155
+ → read the `conflicts` and `warnings` it answers → look at frames. This is
156
+ the review → revise → rerun loop from the build loop above, made cheap — a
157
+ two-line diff instead of a 500-line resend. `edit_timeline` ops still apply
158
+ for a timeline with no code, or a deliberate hand tweak — `timeline-edit-ops`
159
+ owns that contract and what it costs a code-backed scene. The pack's own
160
+ skill (`@nodetool-ai/sandbox-timeline`) covers what code-backed identity
161
+ means for scene names and clip ids; `api-timelines` owns the full `code.*`
162
+ call contract.
163
+
164
+ ## Review pass
165
+
166
+ For a showcase, hero, launch, or "best" piece, critique like a director before
167
+ calling it done. A render with no errors is not a finished piece — this pass is
168
+ part of the job, not a step to skip when the frames look clean. Reserve rounds
169
+ for it: the run budget is shared with authoring, so budget for at least one
170
+ full pass plus one revision, not just the build.
171
+
172
+ 1. **Contact sheet.** `preview_timeline_frame` with `sheet: true`, sampling
173
+ every scene at its entrance, its hero hold, and its exit.
174
+ 2. **Per-scene checklist**, answered in writing before you touch an op:
175
+ - What does the eye follow first, and is that the intended hero?
176
+ - What moves in the background? A static bed behind the action is a
177
+ finding, not a pass.
178
+ - Does any text touch or get crossed by a shape?
179
+ - How much of the frame is actually used? Look yourself — the validator's
180
+ `showcase_frame_underfilled` check is a bounding-box proxy and can miss a
181
+ thin outline or ring that reads as sparse against an empty backdrop.
182
+ - Does every number that is supposed to count actually count, and finish
183
+ while it is still on screen?
184
+ - How does the scene exit and the next one enter? A default crossfade on a
185
+ showcase brief is a finding to name, not the absence of one.
186
+ 3. **Compare against a reference.** Pick the closest scene with
187
+ `examples.list()` / `examples.get(slug)`. Render matching-beat sheets of
188
+ your piece and that example with `preview_timeline_frame`, `sheet: true`,
189
+ mapping beats across differing durations and sampling the middle of a
190
+ move. Call `view_image` on both with this explicit question: "Compared
191
+ with the example, what is missing in layer count, secondary motion,
192
+ texture and light, and camera depth? Name the beat and the gap." Read how
193
+ the example scene was actually built with `examples.source(slug)` when the
194
+ clip excerpt alone does not explain a moment — a repeater's phase offsets,
195
+ a two-pass text effect, a shared id scheme. `compare_timeline_frames`
196
+ cannot target a shipped example — both its sides must be your own timeline
197
+ (an id, version or document) — so use it after a revision to prove which
198
+ frames actually changed, not to judge against a reference.
199
+ 4. **Validate at the showcase tier.** `validate_timeline` with
200
+ `{"timeline_id": "<id>", "tier": "showcase"}` (in code,
201
+ `nodetool.timelines.validate(id, {tier: "showcase"})`) returns `errors`
202
+ and `warnings` — there is no `issues` field.
203
+ Every `showcase_*` warning is a hard stop: resolve it, or state the
204
+ specific artistic exclusion to the user before rendering. Expect
205
+ `showcase_scene_groups_missing`, `showcase_custom_motion_missing`,
206
+ `showcase_finish_missing`, `showcase_camera_missing`,
207
+ `showcase_layer_density_low`, `showcase_scene_group_density_low`,
208
+ `showcase_custom_animation_density_low`, `showcase_effect_variety_low`,
209
+ `showcase_clip_density_low`, `showcase_motion_blur_missing`, plus
210
+ `showcase_frame_underfilled`, `showcase_text_collision`,
211
+ `showcase_motion_range_narrow` and `showcase_dead_motion`. Each names a
212
+ shipped example to study. Zero warnings is a structural floor, not a
213
+ craft verdict — the medians in the warning text are the design target.
214
+ 5. **Revise, then re-run this pass.** Make at least one revision that changes
215
+ the composition itself, not only a fix for a validator error, before
216
+ calling the piece done. Then repeat steps 1–4 on the result. Stopping at
217
+ the first clean render is the failure this pass exists to catch.