@prestyj/cli 5.28.0 → 5.29.0

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 (398) hide show
  1. package/README.md +2 -2
  2. package/assets/motion/THIRD-PARTY.md +14 -19
  3. package/assets/motion/bin/contact-sheet.mjs +9 -3
  4. package/assets/motion/bin/cues.mjs +337 -0
  5. package/assets/motion/bin/flash-check.mjs +314 -0
  6. package/assets/motion/bin/fonts.mjs +17 -13
  7. package/assets/motion/bin/library.mjs +61 -18
  8. package/assets/motion/bin/motion-blur.mjs +943 -0
  9. package/assets/motion/bin/motion-check.mjs +366 -9
  10. package/assets/motion/bin/music-fit.mjs +436 -0
  11. package/assets/motion/bin/pdf-extract.mjs +22 -10
  12. package/assets/motion/bin/reference-study.mjs +346 -0
  13. package/assets/motion/bin/score-synth.mjs +1052 -93
  14. package/assets/motion/fonts/finger-paint/OFL.txt +93 -0
  15. package/assets/motion/fonts/finger-paint/finger-paint-normal.woff2 +0 -0
  16. package/assets/motion/fonts/fonts.json +56 -0
  17. package/assets/motion/fonts/short-stack/OFL.txt +94 -0
  18. package/assets/motion/fonts/short-stack/short-stack-normal.woff2 +0 -0
  19. package/assets/motion/fonts/sora/OFL.txt +93 -0
  20. package/assets/motion/fonts/sora/sora-normal.woff2 +0 -0
  21. package/assets/motion/fonts/specimen.jpg +0 -0
  22. package/assets/motion/fonts/unbounded/OFL.txt +93 -0
  23. package/assets/motion/fonts/unbounded/unbounded-normal.woff2 +0 -0
  24. package/assets/motion/library/README.md +52 -14
  25. package/assets/motion/library/kit/moves.js +1981 -0
  26. package/assets/motion/library/library.json +232 -0
  27. package/assets/motion/library/pieces/camera-rig/meta.json +13 -0
  28. package/assets/motion/library/pieces/camera-rig/piece.html +153 -0
  29. package/assets/motion/library/pieces/camera-rig/preview.jpg +0 -0
  30. package/assets/motion/library/pieces/chain-knock/meta.json +13 -0
  31. package/assets/motion/library/pieces/chain-knock/piece.html +195 -0
  32. package/assets/motion/library/pieces/chain-knock/preview.jpg +0 -0
  33. package/assets/motion/library/pieces/gather-to-logo/meta.json +13 -0
  34. package/assets/motion/library/pieces/gather-to-logo/piece.html +159 -0
  35. package/assets/motion/library/pieces/gather-to-logo/preview.jpg +0 -0
  36. package/assets/motion/library/pieces/morph-carry/meta.json +13 -0
  37. package/assets/motion/library/pieces/morph-carry/piece.html +173 -0
  38. package/assets/motion/library/pieces/morph-carry/preview.jpg +0 -0
  39. package/assets/motion/library/pieces/one-shape-journey/meta.json +13 -0
  40. package/assets/motion/library/pieces/one-shape-journey/piece.html +195 -0
  41. package/assets/motion/library/pieces/one-shape-journey/preview.jpg +0 -0
  42. package/assets/motion/library/pieces/open-from-subject/meta.json +13 -0
  43. package/assets/motion/library/pieces/open-from-subject/piece.html +168 -0
  44. package/assets/motion/library/pieces/open-from-subject/preview.jpg +0 -0
  45. package/assets/motion/library/pieces/request-to-result/meta.json +13 -0
  46. package/assets/motion/library/pieces/request-to-result/piece.html +212 -0
  47. package/assets/motion/library/pieces/request-to-result/preview.jpg +0 -0
  48. package/assets/motion/library/pieces/scale-dive/meta.json +13 -0
  49. package/assets/motion/library/pieces/scale-dive/piece.html +321 -0
  50. package/assets/motion/library/pieces/scale-dive/preview.jpg +0 -0
  51. package/assets/motion/library/pieces/screen-replica-steps/meta.json +13 -0
  52. package/assets/motion/library/pieces/screen-replica-steps/piece.html +366 -0
  53. package/assets/motion/library/pieces/screen-replica-steps/preview.jpg +0 -0
  54. package/assets/motion/library/pieces/zoom-into-card/meta.json +13 -0
  55. package/assets/motion/library/pieces/zoom-into-card/piece.html +179 -0
  56. package/assets/motion/library/pieces/zoom-into-card/preview.jpg +0 -0
  57. package/assets/motion/library/sheets/diagram.jpg +0 -0
  58. package/assets/motion/library/sheets/frame.jpg +0 -0
  59. package/assets/motion/library/sheets/transition.jpg +0 -0
  60. package/assets/motion/library/sheets/ui.jpg +0 -0
  61. package/assets/motion/plugin.json +1 -1
  62. package/assets/motion/references/build-sheet.md +206 -0
  63. package/assets/motion/references/runtime/determinism-rules.md +3 -3
  64. package/assets/motion/references/runtime/gsap-easing-and-stagger.md +28 -28
  65. package/assets/motion/references/runtime/gsap.md +3 -3
  66. package/assets/motion/references/runtime/inputs-and-assets.md +21 -16
  67. package/assets/motion/references/runtime/lint-validate-inspect.md +3 -3
  68. package/assets/motion/references/runtime/minimal-composition.md +6 -0
  69. package/assets/motion/references/runtime/preview-render.md +3 -3
  70. package/assets/motion/skills/app-walkthrough/SKILL.md +66 -0
  71. package/assets/motion/skills/before-after/SKILL.md +53 -0
  72. package/assets/motion/skills/brand-kit/SKILL.md +17 -16
  73. package/assets/motion/skills/dev-tool-video/SKILL.md +57 -0
  74. package/assets/motion/skills/launch-video/SKILL.md +62 -0
  75. package/assets/motion/skills/match-reference/SKILL.md +58 -0
  76. package/assets/motion/skills/motion/SKILL.md +167 -98
  77. package/assets/motion/skills/source-ingest/SKILL.md +23 -15
  78. package/assets/motion/skills/website-video/SKILL.md +59 -0
  79. package/assets/skills/bulletproof/SKILL.md +36 -11
  80. package/assets/skills/bulletproof/references/agent-surface.md +19 -9
  81. package/assets/skills/bulletproof/references/audit-protocol.md +20 -5
  82. package/assets/skills/bulletproof/references/platform-playbooks.md +5 -4
  83. package/assets/skills/bulletproof/references/provenance.md +26 -1
  84. package/assets/skills/bulletproof/references/secure-defaults.md +6 -5
  85. package/assets/skills/bulletproof/references/supply-chain.md +21 -17
  86. package/assets/skills/bulletproof/references/threat-landscape.md +28 -26
  87. package/assets/skills/bulletproof/references/verification.md +2 -0
  88. package/assets/skills/clarify/SKILL.md +25 -16
  89. package/assets/skills/code-review/SKILL.md +71 -13
  90. package/assets/skills/code-review/references/agent-diffs.md +27 -0
  91. package/assets/skills/code-review/references/tests.md +19 -0
  92. package/assets/skills/compliance-guard/SKILL.md +20 -5
  93. package/assets/skills/compliance-guard/references/artifacts.md +1 -1
  94. package/assets/skills/compliance-guard/references/eu-uk.md +16 -16
  95. package/assets/skills/compliance-guard/references/lawsuit-vectors.md +5 -5
  96. package/assets/skills/compliance-guard/references/provenance.md +41 -2
  97. package/assets/skills/compliance-guard/references/sector-gates.md +3 -3
  98. package/assets/skills/compliance-guard/references/security-baseline.md +2 -2
  99. package/assets/skills/compliance-guard/references/trigger-map.md +5 -5
  100. package/assets/skills/compliance-guard/references/us.md +27 -21
  101. package/assets/skills/durable/SKILL.md +87 -79
  102. package/assets/skills/durable/references/agent-db-safety.md +69 -0
  103. package/assets/skills/durable/references/backups-and-runtime.md +19 -12
  104. package/assets/skills/durable/references/migrations-and-schema.md +13 -6
  105. package/assets/skills/evidence-led-ui/SKILL.md +69 -127
  106. package/assets/skills/evidence-led-ui/references/anti-defaults.md +107 -208
  107. package/assets/skills/evidence-led-ui/references/direction.md +124 -0
  108. package/assets/skills/evidence-led-ui/references/production-contract.md +8 -0
  109. package/assets/skills/evidence-led-ui/references/provenance.md +24 -1
  110. package/assets/skills/lean/SKILL.md +90 -71
  111. package/assets/skills/lean/references/memory-and-processes.md +3 -2
  112. package/assets/skills/lean/references/playbooks.md +37 -12
  113. package/assets/skills/refactoring/SKILL.md +24 -3
  114. package/assets/skills/refactoring/references/agent-pitfalls.md +4 -1
  115. package/assets/skills/refactoring/references/legacy.md +21 -0
  116. package/assets/skills/root-cause/SKILL.md +20 -10
  117. package/assets/skills/shared-language/SKILL.md +16 -14
  118. package/assets/skills/tdd/SKILL.md +27 -15
  119. package/dist/app-sidecar.js +203 -47
  120. package/dist/app-sidecar.js.map +1 -1
  121. package/dist/cli.js +20 -28
  122. package/dist/cli.js.map +1 -1
  123. package/dist/core/acceptance-checks.d.ts +48 -0
  124. package/dist/core/acceptance-checks.js +144 -0
  125. package/dist/core/acceptance-checks.js.map +1 -0
  126. package/dist/core/agent-session.d.ts +119 -75
  127. package/dist/core/agent-session.js +561 -395
  128. package/dist/core/agent-session.js.map +1 -1
  129. package/dist/core/agents.d.ts +6 -5
  130. package/dist/core/agents.js +12 -3
  131. package/dist/core/agents.js.map +1 -1
  132. package/dist/core/ask-user.d.ts +90 -8
  133. package/dist/core/ask-user.js +124 -13
  134. package/dist/core/ask-user.js.map +1 -1
  135. package/dist/core/auth-providers.js +1 -1
  136. package/dist/core/auth-providers.js.map +1 -1
  137. package/dist/core/autopilot-verdict.d.ts +5 -1
  138. package/dist/core/autopilot-verdict.js +30 -13
  139. package/dist/core/autopilot-verdict.js.map +1 -1
  140. package/dist/core/bundled-agents.js +1 -3
  141. package/dist/core/bundled-agents.js.map +1 -1
  142. package/dist/core/cache-diagnostics.d.ts +68 -0
  143. package/dist/core/cache-diagnostics.js +196 -0
  144. package/dist/core/cache-diagnostics.js.map +1 -0
  145. package/dist/core/cache-expiry.d.ts +87 -0
  146. package/dist/core/cache-expiry.js +111 -0
  147. package/dist/core/cache-expiry.js.map +1 -0
  148. package/dist/core/compaction/compactor.js +78 -48
  149. package/dist/core/compaction/compactor.js.map +1 -1
  150. package/dist/core/compaction/plan-step-policy.d.ts +46 -0
  151. package/dist/core/compaction/plan-step-policy.js +57 -0
  152. package/dist/core/compaction/plan-step-policy.js.map +1 -0
  153. package/dist/core/destructive-git-guard.d.ts +90 -0
  154. package/dist/core/destructive-git-guard.js +871 -0
  155. package/dist/core/destructive-git-guard.js.map +1 -0
  156. package/dist/core/event-bus.d.ts +3 -0
  157. package/dist/core/event-bus.js +5 -0
  158. package/dist/core/event-bus.js.map +1 -1
  159. package/dist/core/fast-apply-benchmark.d.ts +1 -1
  160. package/dist/core/fast-apply-benchmark.js +2 -2
  161. package/dist/core/fast-apply-benchmark.js.map +1 -1
  162. package/dist/core/injection-detect.d.ts +38 -0
  163. package/dist/core/injection-detect.js +232 -0
  164. package/dist/core/injection-detect.js.map +1 -0
  165. package/dist/core/keep-awake.d.ts +88 -0
  166. package/dist/core/keep-awake.js +251 -0
  167. package/dist/core/keep-awake.js.map +1 -0
  168. package/dist/core/mcp/client.d.ts +72 -0
  169. package/dist/core/mcp/client.js +264 -41
  170. package/dist/core/mcp/client.js.map +1 -1
  171. package/dist/core/mcp/content.js +6 -2
  172. package/dist/core/mcp/content.js.map +1 -1
  173. package/dist/core/mcp/store.d.ts +6 -1
  174. package/dist/core/mcp/store.js +12 -1
  175. package/dist/core/mcp/store.js.map +1 -1
  176. package/dist/core/mcp/types.d.ts +18 -0
  177. package/dist/core/model-unavailable.d.ts +14 -0
  178. package/dist/core/model-unavailable.js +23 -0
  179. package/dist/core/model-unavailable.js.map +1 -0
  180. package/dist/core/node-debugger.d.ts +148 -0
  181. package/dist/core/node-debugger.js +642 -0
  182. package/dist/core/node-debugger.js.map +1 -0
  183. package/dist/core/nolan-context.d.ts +7 -5
  184. package/dist/core/nolan-context.js +106 -16
  185. package/dist/core/nolan-context.js.map +1 -1
  186. package/dist/core/nolan-prompt.js +24 -21
  187. package/dist/core/nolan-prompt.js.map +1 -1
  188. package/dist/core/package-threats.d.ts +18 -0
  189. package/dist/core/package-threats.js +168 -0
  190. package/dist/core/package-threats.js.map +1 -0
  191. package/dist/core/persistent-shell.d.ts +58 -6
  192. package/dist/core/persistent-shell.js +331 -49
  193. package/dist/core/persistent-shell.js.map +1 -1
  194. package/dist/core/process-manager.d.ts +14 -0
  195. package/dist/core/process-manager.js +61 -0
  196. package/dist/core/process-manager.js.map +1 -1
  197. package/dist/core/progress/git-xp.js +8 -14
  198. package/dist/core/progress/git-xp.js.map +1 -1
  199. package/dist/core/project-discovery.js +77 -26
  200. package/dist/core/project-discovery.js.map +1 -1
  201. package/dist/core/semantic-search-benchmark.d.ts +1 -1
  202. package/dist/core/semantic-search-benchmark.js +2 -2
  203. package/dist/core/semantic-search-benchmark.js.map +1 -1
  204. package/dist/core/session-history.d.ts +12 -0
  205. package/dist/core/session-history.js +27 -0
  206. package/dist/core/session-history.js.map +1 -1
  207. package/dist/core/session-manager.d.ts +13 -1
  208. package/dist/core/session-manager.js +38 -18
  209. package/dist/core/session-manager.js.map +1 -1
  210. package/dist/core/session-summary-index.d.ts +37 -0
  211. package/dist/core/session-summary-index.js +172 -0
  212. package/dist/core/session-summary-index.js.map +1 -0
  213. package/dist/core/settings-manager.d.ts +2 -0
  214. package/dist/core/settings-manager.js +10 -0
  215. package/dist/core/settings-manager.js.map +1 -1
  216. package/dist/core/shell-threats-popular-packages.d.ts +11 -0
  217. package/dist/core/shell-threats-popular-packages.js +675 -0
  218. package/dist/core/shell-threats-popular-packages.js.map +1 -0
  219. package/dist/core/shell-threats.d.ts +8 -0
  220. package/dist/core/shell-threats.js +186 -0
  221. package/dist/core/shell-threats.js.map +1 -0
  222. package/dist/core/skills.js +16 -4
  223. package/dist/core/skills.js.map +1 -1
  224. package/dist/core/stream-rules.d.ts +30 -0
  225. package/dist/core/stream-rules.js +151 -0
  226. package/dist/core/stream-rules.js.map +1 -0
  227. package/dist/core/subagent-manager.d.ts +23 -6
  228. package/dist/core/subagent-manager.js +25 -9
  229. package/dist/core/subagent-manager.js.map +1 -1
  230. package/dist/core/subagent-policy.js +1 -1
  231. package/dist/core/subagent-policy.js.map +1 -1
  232. package/dist/core/subagent-receipt.d.ts +54 -0
  233. package/dist/core/subagent-receipt.js +276 -0
  234. package/dist/core/subagent-receipt.js.map +1 -0
  235. package/dist/core/subagent-turn-record.d.ts +2 -0
  236. package/dist/core/subagent-turn-record.js.map +1 -1
  237. package/dist/core/test-impact.d.ts +73 -0
  238. package/dist/core/test-impact.js +467 -0
  239. package/dist/core/test-impact.js.map +1 -0
  240. package/dist/core/thinking-level.d.ts +1 -1
  241. package/dist/core/thinking-level.js +1 -1
  242. package/dist/core/thinking-level.js.map +1 -1
  243. package/dist/core/verification-gate.d.ts +2 -0
  244. package/dist/core/verification-gate.js +4 -0
  245. package/dist/core/verification-gate.js.map +1 -1
  246. package/dist/core/verification-snapshot.js +3 -5
  247. package/dist/core/verification-snapshot.js.map +1 -1
  248. package/dist/core/workspace-guard.d.ts +18 -7
  249. package/dist/core/workspace-guard.js +227 -60
  250. package/dist/core/workspace-guard.js.map +1 -1
  251. package/dist/core/worktree-setup.d.ts +23 -0
  252. package/dist/core/worktree-setup.js +128 -9
  253. package/dist/core/worktree-setup.js.map +1 -1
  254. package/dist/core/worktree.d.ts +20 -2
  255. package/dist/core/worktree.js +74 -31
  256. package/dist/core/worktree.js.map +1 -1
  257. package/dist/interactive.js +2 -1
  258. package/dist/interactive.js.map +1 -1
  259. package/dist/modes/json-mode.js +11 -2
  260. package/dist/modes/json-mode.js.map +1 -1
  261. package/dist/modes/subagent-worker-mode.d.ts +36 -1
  262. package/dist/modes/subagent-worker-mode.js +100 -42
  263. package/dist/modes/subagent-worker-mode.js.map +1 -1
  264. package/dist/motion-agent/motion-agent.d.ts +7 -3
  265. package/dist/motion-agent/motion-agent.js +6 -8
  266. package/dist/motion-agent/motion-agent.js.map +1 -1
  267. package/dist/motion-agent/motion-prompt.d.ts +1 -1
  268. package/dist/motion-agent/motion-prompt.js +16 -25
  269. package/dist/motion-agent/motion-prompt.js.map +1 -1
  270. package/dist/motion-agent/motion-review-session.js +1 -1
  271. package/dist/motion-agent/motion-review-session.js.map +1 -1
  272. package/dist/motion-agent/motion-review.d.ts +10 -3
  273. package/dist/motion-agent/motion-review.js +15 -7
  274. package/dist/motion-agent/motion-review.js.map +1 -1
  275. package/dist/motion-agent/motion-studio-context.js +1 -1
  276. package/dist/motion-agent/motion-studio-context.js.map +1 -1
  277. package/dist/system-prompt.d.ts +2 -1
  278. package/dist/system-prompt.js +15 -4
  279. package/dist/system-prompt.js.map +1 -1
  280. package/dist/test-support/keep-alive.d.ts +14 -0
  281. package/dist/test-support/keep-alive.js +17 -0
  282. package/dist/test-support/keep-alive.js.map +1 -0
  283. package/dist/tools/ask-user.js +3 -3
  284. package/dist/tools/ask-user.js.map +1 -1
  285. package/dist/tools/bash-read-evidence.d.ts +10 -0
  286. package/dist/tools/bash-read-evidence.js +133 -0
  287. package/dist/tools/bash-read-evidence.js.map +1 -0
  288. package/dist/tools/bash.d.ts +10 -1
  289. package/dist/tools/bash.js +179 -7
  290. package/dist/tools/bash.js.map +1 -1
  291. package/dist/tools/debug.d.ts +54 -0
  292. package/dist/tools/debug.js +233 -0
  293. package/dist/tools/debug.js.map +1 -0
  294. package/dist/tools/edit.js +13 -5
  295. package/dist/tools/edit.js.map +1 -1
  296. package/dist/tools/goals.d.ts +1 -1
  297. package/dist/tools/index.d.ts +25 -2
  298. package/dist/tools/index.js +48 -7
  299. package/dist/tools/index.js.map +1 -1
  300. package/dist/tools/prompt-hints.js +2 -0
  301. package/dist/tools/prompt-hints.js.map +1 -1
  302. package/dist/tools/read-tracker.d.ts +35 -2
  303. package/dist/tools/read-tracker.js +108 -11
  304. package/dist/tools/read-tracker.js.map +1 -1
  305. package/dist/tools/read.js +39 -7
  306. package/dist/tools/read.js.map +1 -1
  307. package/dist/tools/skill.js +5 -0
  308. package/dist/tools/skill.js.map +1 -1
  309. package/dist/tools/subagent-control.js +44 -8
  310. package/dist/tools/subagent-control.js.map +1 -1
  311. package/dist/tools/subagent-shared.d.ts +48 -8
  312. package/dist/tools/subagent-shared.js +75 -14
  313. package/dist/tools/subagent-shared.js.map +1 -1
  314. package/dist/tools/subagent.d.ts +8 -2
  315. package/dist/tools/subagent.js +28 -10
  316. package/dist/tools/subagent.js.map +1 -1
  317. package/dist/tools/task-output.js +3 -2
  318. package/dist/tools/task-output.js.map +1 -1
  319. package/dist/tools/task-send.d.ts +1 -1
  320. package/dist/tools/task-send.js +15 -1
  321. package/dist/tools/task-send.js.map +1 -1
  322. package/dist/tools/tool-tiers.d.ts +2 -2
  323. package/dist/tools/tool-tiers.js +3 -2
  324. package/dist/tools/tool-tiers.js.map +1 -1
  325. package/dist/tools/truncate.d.ts +21 -0
  326. package/dist/tools/truncate.js +187 -0
  327. package/dist/tools/truncate.js.map +1 -1
  328. package/dist/tools/ui-adopt.js +2 -0
  329. package/dist/tools/ui-adopt.js.map +1 -1
  330. package/dist/tools/write.js +4 -3
  331. package/dist/tools/write.js.map +1 -1
  332. package/dist/ui/App.d.ts +0 -4
  333. package/dist/ui/App.js +5 -28
  334. package/dist/ui/App.js.map +1 -1
  335. package/dist/ui/components/ActivityIndicator.js +1 -0
  336. package/dist/ui/components/ActivityIndicator.js.map +1 -1
  337. package/dist/ui/components/Footer.js +1 -1
  338. package/dist/ui/components/Footer.js.map +1 -1
  339. package/dist/ui/hooks/useAgentLoop.d.ts +1 -8
  340. package/dist/ui/hooks/useAgentLoop.js +1 -119
  341. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  342. package/dist/ui/render.d.ts +2 -4
  343. package/dist/ui/render.js +3 -2
  344. package/dist/ui/render.js.map +1 -1
  345. package/dist/utils/git.d.ts +77 -0
  346. package/dist/utils/git.js +285 -21
  347. package/dist/utils/git.js.map +1 -1
  348. package/dist/utils/github-ci.js +2 -1
  349. package/dist/utils/github-ci.js.map +1 -1
  350. package/dist/utils/github.js +11 -9
  351. package/dist/utils/github.js.map +1 -1
  352. package/dist/utils/image.d.ts +14 -0
  353. package/dist/utils/image.js +16 -0
  354. package/dist/utils/image.js.map +1 -1
  355. package/dist/utils/process.d.ts +20 -0
  356. package/dist/utils/process.js +98 -0
  357. package/dist/utils/process.js.map +1 -1
  358. package/dist/utils/text.d.ts +12 -0
  359. package/dist/utils/text.js +11 -0
  360. package/dist/utils/text.js.map +1 -1
  361. package/package.json +6 -6
  362. package/assets/motion/skills/mixkit-split-text-617/SKILL.md +0 -115
  363. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-480.json +0 -1
  364. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-494.json +0 -1
  365. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-5.json +0 -1
  366. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-508.json +0 -1
  367. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6525.json +0 -1
  368. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6539.json +0 -1
  369. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6647.json +0 -1
  370. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6663.json +0 -1
  371. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6677.json +0 -1
  372. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6691.json +0 -1
  373. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6706.json +0 -1
  374. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6722.json +0 -1
  375. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6736.json +0 -1
  376. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6750.json +0 -1
  377. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6764.json +0 -1
  378. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6780.json +0 -1
  379. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6794.json +0 -1
  380. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6810.json +0 -1
  381. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6823.json +0 -1
  382. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6872.json +0 -1
  383. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-6936.json +0 -1
  384. package/assets/motion/skills/mixkit-split-text-617/data/compositions/comp-76.json +0 -1
  385. package/assets/motion/skills/mixkit-split-text-617/data/manifest.json +0 -495
  386. package/assets/motion/skills/mixkit-split-text-617/references/RECONSTRUCTION.md +0 -201
  387. package/assets/motion/skills/mixkit-split-text-617/references/VERIFICATION.md +0 -107
  388. package/assets/motion/skills/mixkit-split-text-617/tools/inspect_motion.py +0 -130
  389. package/assets/motion/skills/video-qa/SKILL.md +0 -66
  390. package/dist/core/ideal-review-subagent.d.ts +0 -42
  391. package/dist/core/ideal-review-subagent.js +0 -95
  392. package/dist/core/ideal-review-subagent.js.map +0 -1
  393. package/dist/core/ideal-review.d.ts +0 -82
  394. package/dist/core/ideal-review.js +0 -242
  395. package/dist/core/ideal-review.js.map +0 -1
  396. package/dist/motion-agent/motion-check-tool.d.ts +0 -21
  397. package/dist/motion-agent/motion-check-tool.js +0 -206
  398. package/dist/motion-agent/motion-check-tool.js.map +0 -1
@@ -0,0 +1,314 @@
1
+ #!/usr/bin/env node
2
+ // Photosensitivity screen on decoded pixels against the WCAG 2.3.1 general and red flash
3
+ // thresholds. As in EA's open-source IRIS tool, an area's mean change only counts on frames
4
+ // where at least a quarter of the area changes, so moving content mostly cancels out rather
5
+ // than reading as flashing. IRIS applies this to the whole screen; here it applies to every
6
+ // WCAG 10° field (a third of the frame each way), so a flash filling part of the frame is
7
+ // caught too. A transition is a move of 0.1 relative luminance (or 20 red units) away from the
8
+ // last peak or valley, as WCAG defines it; more than six in any second fails. Renders above
9
+ // 60 fps are sampled at 60. A screen, not a certification.
10
+ import { spawn } from "node:child_process";
11
+ import { createReadStream } from "node:fs";
12
+ import { stat } from "node:fs/promises";
13
+ import { createHash } from "node:crypto";
14
+ import { once } from "node:events";
15
+ import { resolve } from "node:path";
16
+ import { pathToFileURL } from "node:url";
17
+ import { ffmpegBinary } from "./media-binaries.mjs";
18
+
19
+ /** Cells per side of the analysis grid; every aspect ratio maps onto it. */
20
+ export const GRID = 48;
21
+ /** Frames per analysed second; slower renders are repeated, so one second is always 60 frames. */
22
+ export const ANALYSIS_FPS = 60;
23
+ /** WCAG approximates the 10° field as 341×256 px of a 1024×768 screen: a third each way. */
24
+ const FIELD = GRID / 3;
25
+ const FIELD_CELLS = FIELD * FIELD;
26
+ /** A field's change counts once a quarter of it changes. */
27
+ const AREA = 0.25 * FIELD_CELLS;
28
+ const WINDOWS_PER_SIDE = GRID - FIELD + 1;
29
+ const WINDOWS = WINDOWS_PER_SIDE * WINDOWS_PER_SIDE;
30
+ /** More than three flashes (six opposing transitions) within any one second fails. */
31
+ const MAX_TRANSITIONS = 6;
32
+ /** Transitions per field within the trailing second never exceed one per analysed frame. */
33
+ const RING = 64;
34
+ const CELLS = GRID * GRID;
35
+ const FRAME_BYTES = CELLS * 3;
36
+ const CHANNELS = {
37
+ // Relative luminance 0–1: a 0.1 swing, unless even the darker state is 0.8 or brighter.
38
+ // A cell has changed once it moves by more than encoding noise (about 1/255 linear).
39
+ general: { threshold: 0.1, darkLimit: 0.8, noise: 0.004 },
40
+ // WCAG's red measure: (R − G − B) × 320 where R / (R + G + B) ≥ 0.8, else 0.
41
+ red: { threshold: 20, darkLimit: Infinity, noise: 1 },
42
+ };
43
+ /** sRGB to linear light, applied before averaging so mixed cells keep their real brightness. */
44
+ const LINEAR = "if(lte(val,10.31475),val/12.92,255*pow((val/255+0.055)/1.055,2.4))";
45
+
46
+ function createChannel({ threshold, darkLimit, noise }) {
47
+ const side = GRID + 1;
48
+ const changed = new Float64Array(side * side);
49
+ const change = new Float64Array(side * side);
50
+ const level = new Float64Array(side * side);
51
+ // Per field: the accumulated counted change, its last peak and valley, and the field's
52
+ // mean at each, for WCAG's "darker image" rule.
53
+ const signal = new Float64Array(WINDOWS);
54
+ const peakValue = new Float64Array(WINDOWS);
55
+ const peakLevel = new Float64Array(WINDOWS);
56
+ const valleyValue = new Float64Array(WINDOWS);
57
+ const valleyLevel = new Float64Array(WINDOWS);
58
+ /** 1 after a rise, -1 after a fall, 0 before the first transition. */
59
+ const direction = new Int8Array(WINDOWS);
60
+ const ring = new Int32Array(WINDOWS * RING);
61
+ const head = new Int32Array(WINDOWS);
62
+ const tail = new Int32Array(WINDOWS);
63
+ const failures = [];
64
+ let peak = 0;
65
+
66
+ return {
67
+ update(frame, previous, current) {
68
+ // Summed-area tables, so each field's totals cost four lookups.
69
+ for (let y = 0; y < GRID; y++) {
70
+ let rowChanged = 0;
71
+ let rowChange = 0;
72
+ let rowLevel = 0;
73
+ for (let x = 0; x < GRID; x++) {
74
+ const cell = y * GRID + x;
75
+ const delta = current[cell] - previous[cell];
76
+ if (Math.abs(delta) > noise) rowChanged++;
77
+ rowChange += delta;
78
+ rowLevel += current[cell];
79
+ const at = (y + 1) * side + x + 1;
80
+ changed[at] = changed[at - side] + rowChanged;
81
+ change[at] = change[at - side] + rowChange;
82
+ level[at] = level[at - side] + rowLevel;
83
+ }
84
+ }
85
+ let most = 0;
86
+ for (let wy = 0; wy < WINDOWS_PER_SIDE; wy++) {
87
+ for (let wx = 0; wx < WINDOWS_PER_SIDE; wx++) {
88
+ const window = wy * WINDOWS_PER_SIDE + wx;
89
+ const a = wy * side + wx;
90
+ const b = a + FIELD;
91
+ const c = a + FIELD * side;
92
+ const d = c + FIELD;
93
+ const area = changed[d] - changed[b] - changed[c] + changed[a];
94
+ const step = (change[d] - change[b] - change[c] + change[a]) / FIELD_CELLS;
95
+ const mean = (level[d] - level[b] - level[c] + level[a]) / FIELD_CELLS;
96
+ if (frame === 1) {
97
+ peakLevel[window] = mean - step;
98
+ valleyLevel[window] = mean - step;
99
+ }
100
+ const value = signal[window] + (area >= AREA ? step : 0);
101
+ signal[window] = value;
102
+ let transition = false;
103
+ if (direction[window] >= 0) {
104
+ if (value > peakValue[window]) {
105
+ peakValue[window] = value;
106
+ peakLevel[window] = mean;
107
+ } else if (peakValue[window] - value >= threshold) {
108
+ transition = Math.min(mean, peakLevel[window]) < darkLimit;
109
+ direction[window] = -1;
110
+ valleyValue[window] = value;
111
+ valleyLevel[window] = mean;
112
+ }
113
+ }
114
+ if (direction[window] <= 0) {
115
+ if (value < valleyValue[window]) {
116
+ valleyValue[window] = value;
117
+ valleyLevel[window] = mean;
118
+ } else if (value - valleyValue[window] >= threshold) {
119
+ transition = Math.min(mean, valleyLevel[window]) < darkLimit;
120
+ direction[window] = 1;
121
+ peakValue[window] = value;
122
+ peakLevel[window] = mean;
123
+ }
124
+ }
125
+ const base = window * RING;
126
+ while (
127
+ tail[window] < head[window] &&
128
+ frame - ring[base + (tail[window] % RING)] >= ANALYSIS_FPS
129
+ )
130
+ tail[window]++;
131
+ if (transition) {
132
+ ring[base + (head[window] % RING)] = frame;
133
+ head[window]++;
134
+ }
135
+ const count = head[window] - tail[window];
136
+ if (count > most) most = count;
137
+ }
138
+ }
139
+ peak = Math.max(peak, most);
140
+ if (most > MAX_TRANSITIONS) {
141
+ const start = Math.max(0, frame + 1 - ANALYSIS_FPS) / ANALYSIS_FPS;
142
+ const end = (frame + 1) / ANALYSIS_FPS;
143
+ const last = failures.at(-1);
144
+ if (last && start <= last.end) last.end = end;
145
+ else failures.push({ start, end });
146
+ }
147
+ },
148
+ summary() {
149
+ return {
150
+ maxFlashesPerSecond: peak / 2,
151
+ failures: failures.slice(0, 20).map(({ start, end }) => ({
152
+ start: Math.round(start * 100) / 100,
153
+ end: Math.round(end * 100) / 100,
154
+ })),
155
+ };
156
+ },
157
+ };
158
+ }
159
+
160
+ /** Pure analysis over linear-light RGB frames of GRID × GRID cells (3 bytes per cell). */
161
+ export function createFlashAnalyzer() {
162
+ const general = createChannel(CHANNELS.general);
163
+ const red = createChannel(CHANNELS.red);
164
+ let luminance = new Float64Array(CELLS);
165
+ let redness = new Float64Array(CELLS);
166
+ let previousLuminance = new Float64Array(CELLS);
167
+ let previousRedness = new Float64Array(CELLS);
168
+ let frames = 0;
169
+ return {
170
+ push(frame) {
171
+ if (!(frame instanceof Uint8Array) || frame.length !== FRAME_BYTES)
172
+ throw new Error(`Expected ${FRAME_BYTES}-byte analysis frames`);
173
+ for (let cell = 0; cell < CELLS; cell++) {
174
+ const r = frame[cell * 3] / 255;
175
+ const g = frame[cell * 3 + 1] / 255;
176
+ const b = frame[cell * 3 + 2] / 255;
177
+ luminance[cell] = 0.2126 * r + 0.7152 * g + 0.0722 * b;
178
+ const total = r + g + b;
179
+ redness[cell] = total > 0 && r / total >= 0.8 ? Math.max(0, (r - g - b) * 320) : 0;
180
+ }
181
+ if (frames > 0) {
182
+ general.update(frames, previousLuminance, luminance);
183
+ red.update(frames, previousRedness, redness);
184
+ }
185
+ [previousLuminance, luminance] = [luminance, previousLuminance];
186
+ [previousRedness, redness] = [redness, previousRedness];
187
+ frames++;
188
+ },
189
+ result() {
190
+ const generalSummary = general.summary();
191
+ const redSummary = red.summary();
192
+ return {
193
+ ok: frames >= 2 && !generalSummary.failures.length && !redSummary.failures.length,
194
+ method: "rendered-pixels",
195
+ standard: "WCAG 2.3.1 general and red flash thresholds",
196
+ analysisFps: ANALYSIS_FPS,
197
+ frames,
198
+ duration: frames / ANALYSIS_FPS,
199
+ general: generalSummary,
200
+ red: redSummary,
201
+ };
202
+ },
203
+ };
204
+ }
205
+
206
+ /** Decode a video into linear-light analysis frames and return the flash report. */
207
+ export async function analyzeFlashes(file, signal) {
208
+ const binary = await ffmpegBinary();
209
+ const child = spawn(
210
+ binary,
211
+ [
212
+ "-hide_banner",
213
+ "-nostdin",
214
+ "-loglevel",
215
+ "error",
216
+ "-xerror",
217
+ "-protocol_whitelist",
218
+ "file,pipe",
219
+ "-i",
220
+ file,
221
+ "-map",
222
+ "0:v:0",
223
+ "-an",
224
+ "-vf",
225
+ [
226
+ // Linearise before any averaging: shrinking sRGB values first darkens fine detail
227
+ // by up to half the flash threshold.
228
+ "format=gbrp",
229
+ `lutrgb=r='${LINEAR}':g='${LINEAR}':b='${LINEAR}'`,
230
+ `scale=${GRID}:${GRID}:flags=area`,
231
+ `fps=${ANALYSIS_FPS}`,
232
+ "format=rgb24",
233
+ ].join(","),
234
+ "-f",
235
+ "rawvideo",
236
+ "-pix_fmt",
237
+ "rgb24",
238
+ "pipe:1",
239
+ ],
240
+ { stdio: ["ignore", "pipe", "pipe"], windowsHide: true, signal },
241
+ );
242
+ let stderr = "";
243
+ child.stderr.setEncoding("utf8");
244
+ child.stderr.on("data", (text) => {
245
+ if (stderr.length < 16_000) stderr += text;
246
+ });
247
+ const closed = once(child, "close");
248
+ closed.catch(() => {});
249
+ const analyzer = createFlashAnalyzer();
250
+ const frame = new Uint8Array(FRAME_BYTES);
251
+ let filled = 0;
252
+ for await (const chunk of child.stdout) {
253
+ let offset = 0;
254
+ while (offset < chunk.length) {
255
+ const take = Math.min(FRAME_BYTES - filled, chunk.length - offset);
256
+ frame.set(chunk.subarray(offset, offset + take), filled);
257
+ filled += take;
258
+ offset += take;
259
+ if (filled === FRAME_BYTES) {
260
+ analyzer.push(frame);
261
+ filled = 0;
262
+ }
263
+ }
264
+ }
265
+ const [code] = await closed;
266
+ if (code !== 0)
267
+ throw new Error(`FFmpeg could not decode the video: ${stderr.trim().slice(-2000)}`);
268
+ if (filled !== 0) throw new Error("Decoded video ended mid-frame; flashing is unverified");
269
+ const result = analyzer.result();
270
+ if (result.frames < 2) throw new Error("Too few decoded frames; flashing is unverified");
271
+ return result;
272
+ }
273
+
274
+ async function videoHash(file, signal) {
275
+ const hash = createHash("sha256");
276
+ for await (const chunk of createReadStream(file, { signal })) hash.update(chunk);
277
+ return hash.digest("hex");
278
+ }
279
+
280
+ async function main() {
281
+ const [video, ...extra] = process.argv.slice(2);
282
+ if (!video || extra.length) throw new Error("usage: flash-check.mjs <rendered-video>");
283
+ const file = resolve(video);
284
+ if (!(await stat(file)).isFile()) throw new Error("Expected a local rendered video file");
285
+ const controller = new AbortController();
286
+ const abort = () => controller.abort();
287
+ process.once("SIGTERM", abort);
288
+ process.once("SIGINT", abort);
289
+ const started = performance.now();
290
+ try {
291
+ const videoSha256 = await videoHash(file, controller.signal);
292
+ const result = await analyzeFlashes(file, controller.signal);
293
+ if ((await videoHash(file, controller.signal)) !== videoSha256)
294
+ throw new Error("Video changed during analysis; flashing is unverified");
295
+ process.stdout.write(
296
+ `${JSON.stringify({ ...result, file, videoSha256, elapsedMs: Math.round(performance.now() - started) })}\n`,
297
+ );
298
+ if (!result.ok) process.exitCode = 1;
299
+ } finally {
300
+ process.removeListener("SIGTERM", abort);
301
+ process.removeListener("SIGINT", abort);
302
+ }
303
+ }
304
+
305
+ if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
306
+ try {
307
+ await main();
308
+ } catch (error) {
309
+ process.stderr.write(
310
+ `Flash verification failed: ${error instanceof Error ? error.message : "invalid input"}\n`,
311
+ );
312
+ process.exitCode = 1;
313
+ }
314
+ }
@@ -7,9 +7,11 @@
7
7
  // node fonts.mjs add <project-dir> <Family> [<Family> ...]
8
8
  //
9
9
  // `add` copies each family's woff2 files and OFL.txt into
10
- // <project>/assets/fonts/<family>/ and rewrites <project>/assets/fonts/fonts.css
11
- // with an @font-face for every bundled family installed there. Link it from
12
- // the composition <head>: <link rel="stylesheet" href="assets/fonts/fonts.css">.
10
+ // <project>/assets/fonts/<family>/, rewrites <project>/assets/fonts/fonts.css,
11
+ // and returns `head`: a <style> block with an @font-face for every bundled
12
+ // family installed there, using project-relative paths. Paste `head` into the
13
+ // composition <head>. `hf check` only sees @font-face written in the page, so a
14
+ // linked fonts.css renders but is reported as missing fonts.
13
15
  // Prints one JSON line: {"ok":true,...} or {"ok":false,"error":...}.
14
16
  import { copyFile, mkdir, readFile, stat, writeFile } from "node:fs/promises";
15
17
  import { dirname, join, resolve } from "node:path";
@@ -23,15 +25,16 @@ function done(result, code = 0) {
23
25
  }
24
26
  const fail = (error) => done({ ok: false, error }, 1);
25
27
 
26
- /** @returns {Promise<Array<{family:string,dir:string,license:string,files:Array<{file:string,style:string,weight:string,stretch:string|null}>}>>} */
28
+ /** @returns {Promise<Array<{family:string,dir:string,license:string,use?:string,files:Array<{file:string,style:string,weight:string,stretch:string|null}>}>>} */
27
29
  async function manifest() {
28
30
  return JSON.parse(await readFile(join(LIBRARY, "fonts.json"), "utf8"));
29
31
  }
30
32
 
31
- function fontFace(entry, file) {
33
+ /** `base` is the font folder as seen from the file that holds the rule. */
34
+ function fontFace(entry, file, base = "") {
32
35
  const lines = [
33
36
  ` font-family: "${entry.family}";`,
34
- ` src: url("${entry.dir}/${file.file}") format("woff2");`,
37
+ ` src: url("${base}${entry.dir}/${file.file}") format("woff2");`,
35
38
  ` font-style: ${file.style};`,
36
39
  ` font-weight: ${file.weight};`,
37
40
  ];
@@ -52,6 +55,7 @@ if (command === "list") {
52
55
  styles: f.files.map((file) => file.style),
53
56
  weight: f.files[0]?.weight,
54
57
  width: f.files[0]?.stretch ?? null,
58
+ ...(f.use ? { use: f.use } : {}),
55
59
  })),
56
60
  });
57
61
  }
@@ -93,15 +97,15 @@ for (const entry of fonts) {
93
97
  );
94
98
  if (present) installed.push(entry);
95
99
  }
96
- const css = [
97
- "/* Generated by EZ Motion fonts.mjs. SIL OFL 1.1: licenses sit beside each family. */",
98
- ...installed.flatMap((entry) => entry.files.map((file) => fontFace(entry, file))),
99
- ].join("\n\n");
100
- await writeFile(join(target, "fonts.css"), `${css}\n`);
100
+ const rules = (base) =>
101
+ installed.flatMap((entry) => entry.files.map((file) => fontFace(entry, file, base)));
102
+ const header =
103
+ "/* Generated by EZ Motion fonts.mjs. SIL OFL 1.1: licenses sit beside each family. */";
104
+ await writeFile(join(target, "fonts.css"), `${[header, ...rules("")].join("\n\n")}\n`);
101
105
 
102
106
  done({
103
107
  ok: true,
104
108
  installed: installed.map((f) => f.family),
105
- stylesheet: join(target, "fonts.css"),
106
- link: '<link rel="stylesheet" href="assets/fonts/fonts.css" />',
109
+ head: `<style>\n${[header, ...rules("assets/fonts/")].join("\n")}\n</style>`,
110
+ use: "Paste head into the composition <head>, replacing any earlier fonts block.",
107
111
  });
@@ -8,11 +8,14 @@
8
8
  // node library.mjs show <id>
9
9
  // node library.mjs look <project-dir> <look-id>
10
10
  // node library.mjs add <project-dir> <piece-id> [<piece-id> ...] [--force]
11
+ // node library.mjs kit <project-dir>
11
12
  //
12
13
  // `look` writes <project>/assets/looks/<id>.css (design tokens as CSS
13
14
  // variables) and installs the look's fonts. `add` copies pieces into
14
15
  // <project>/compositions/<id>.html, installs their fonts, and records
15
- // third-party credits in CREDITS.md. Every command prints one JSON line.
16
+ // third-party credits in CREDITS.md. `kit` installs the move kit
17
+ // (library/kit/moves.js) as <project>/assets/kit/moves.js; `add` does the
18
+ // same for pieces that require it. Every command prints one JSON line.
16
19
  import { execFileSync } from "node:child_process";
17
20
  import { appendFile, copyFile, mkdir, readFile, stat } from "node:fs/promises";
18
21
  import { dirname, join, resolve } from "node:path";
@@ -46,14 +49,29 @@ async function projectDir(arg) {
46
49
  return dir;
47
50
  }
48
51
 
52
+ /** Returns the installed families and fonts.mjs's page-ready <style> block. */
49
53
  function installFonts(project, families) {
50
- if (families.length === 0) return [];
51
- const out = execFileSync(process.execPath, [join(BIN, "fonts.mjs"), "add", project, ...families], {
52
- encoding: "utf8",
53
- });
54
+ if (families.length === 0) return { installed: [], head: [] };
55
+ const out = execFileSync(
56
+ process.execPath,
57
+ [join(BIN, "fonts.mjs"), "add", project, ...families],
58
+ {
59
+ encoding: "utf8",
60
+ },
61
+ );
54
62
  const result = JSON.parse(out);
55
63
  if (!result.ok) fail(`Font install failed: ${result.error}`);
56
- return result.installed;
64
+ // Inline, not a linked fonts.css: `hf check` only sees @font-face written in the page.
65
+ return { installed: result.installed, head: [result.head] };
66
+ }
67
+
68
+ const KIT_HEAD = '<script src="assets/kit/moves.js"></script>';
69
+
70
+ /** Copies the move kit into the project; returns the <script> for the root head. */
71
+ async function installKit(project) {
72
+ await mkdir(join(project, "assets", "kit"), { recursive: true });
73
+ await copyFile(join(LIBRARY, "kit", "moves.js"), join(project, "assets", "kit", "moves.js"));
74
+ return KIT_HEAD;
57
75
  }
58
76
 
59
77
  const summary = (entry, type) => ({
@@ -74,7 +92,8 @@ if (command === "list") {
74
92
  const which = rest[0] === "looks" || rest[0] === "pieces" ? rest[0] : undefined;
75
93
  const kindIndex = rest.indexOf("--kind");
76
94
  const kind = kindIndex >= 0 ? rest[kindIndex + 1] : undefined;
77
- if (kind && !lib.kinds.includes(kind)) fail(`Unknown kind "${kind}". Kinds: ${lib.kinds.join(", ")}`);
95
+ if (kind && !lib.kinds.includes(kind))
96
+ fail(`Unknown kind "${kind}". Kinds: ${lib.kinds.join(", ")}`);
78
97
  const looks = which === "pieces" || kind ? [] : lib.looks.map((l) => summary(l, "look"));
79
98
  const pieces =
80
99
  which === "looks"
@@ -83,7 +102,8 @@ if (command === "list") {
83
102
  // Contact sheets show many previews in one image read.
84
103
  const sheets = {};
85
104
  if (looks.length > 0) sheets.looks = join(LIBRARY, "sheets", "looks.jpg");
86
- for (const k of new Set(pieces.map((p) => p.kind))) sheets[k] = join(LIBRARY, "sheets", `${k}.jpg`);
105
+ for (const k of new Set(pieces.map((p) => p.kind)))
106
+ sheets[k] = join(LIBRARY, "sheets", `${k}.jpg`);
87
107
  done({ ok: true, kinds: lib.kinds, sheets, looks, pieces });
88
108
  }
89
109
 
@@ -118,7 +138,11 @@ if (command === "show") {
118
138
  ...(look ?? piece),
119
139
  preview: join(dir, "preview.jpg"),
120
140
  files: look
121
- ? { spec: join(dir, "look.md"), tokens: join(dir, "tokens.css"), sample: join(dir, "sample.html") }
141
+ ? {
142
+ spec: join(dir, "look.md"),
143
+ tokens: join(dir, "tokens.css"),
144
+ sample: join(dir, "sample.html"),
145
+ }
122
146
  : { source: join(dir, "piece.html") },
123
147
  });
124
148
  }
@@ -135,16 +159,26 @@ if (command === "look") {
135
159
  ok: true,
136
160
  look: look.id,
137
161
  tokens: css,
138
- fonts,
139
- head: [
140
- '<link rel="stylesheet" href="assets/fonts/fonts.css" />',
141
- `<link rel="stylesheet" href="assets/looks/${look.id}.css" />`,
142
- ],
162
+ fonts: fonts.installed,
163
+ head: [...fonts.head, `<link rel="stylesheet" href="assets/looks/${look.id}.css" />`],
164
+ use: "Paste head into the composition <head>, replacing any earlier fonts block.",
143
165
  root: `add class="look-${look.id}" to the root composition element`,
144
166
  spec: join(LIBRARY, "looks", look.id, "look.md"),
145
167
  });
146
168
  }
147
169
 
170
+ if (command === "kit") {
171
+ const project = await projectDir(rest[0]);
172
+ const kit = await installKit(project);
173
+ done({
174
+ ok: true,
175
+ installed: join(project, "assets", "kit", "moves.js"),
176
+ kit,
177
+ use: "Put kit in the root index.html <head> after GSAP; scripts then use window.GGMotionKit.",
178
+ reference: join(LIBRARY, "README.md"),
179
+ });
180
+ }
181
+
148
182
  if (command === "add") {
149
183
  const force = rest.includes("--force");
150
184
  const [projectArg, ...ids] = rest.filter((a) => a !== "--force");
@@ -187,17 +221,26 @@ if (command === "add") {
187
221
  let importmap;
188
222
  if (pieces.some((p) => (p.requires ?? []).includes("three"))) {
189
223
  const three = JSON.parse(
190
- execFileSync(process.execPath, [join(BIN, "three.mjs"), "add", project], { encoding: "utf8" }),
224
+ execFileSync(process.execPath, [join(BIN, "three.mjs"), "add", project], {
225
+ encoding: "utf8",
226
+ }),
191
227
  );
192
228
  if (!three.ok) fail(`Three.js install failed: ${three.error}`);
193
229
  importmap = three.importmap;
194
230
  }
231
+ // Kit pieces call window.GGMotionKit, loaded once by the root page.
232
+ const kit = pieces.some((p) => (p.requires ?? []).includes("kit"))
233
+ ? await installKit(project)
234
+ : undefined;
195
235
  done({
196
236
  ok: true,
197
237
  added,
198
238
  kept,
199
- fonts,
200
- ...(importmap ? { importmap, note: "put importmap in index.html <head> before any module script" } : {}),
239
+ fonts: fonts.installed,
240
+ ...(importmap
241
+ ? { importmap, note: "put importmap in index.html <head> before any module script" }
242
+ : {}),
243
+ ...(kit ? { kit, kitNote: "put kit in index.html <head> after GSAP" } : {}),
201
244
  mount: pieces.map(
202
245
  (p) =>
203
246
  `<div id="${p.id}-clip" data-composition-id="${p.id}" data-composition-src="compositions/${p.id}.html" data-start="0" data-duration="${p.duration}" data-width="1920" data-height="1080" data-track-index="0"></div>`,
@@ -206,5 +249,5 @@ if (command === "add") {
206
249
  }
207
250
 
208
251
  fail(
209
- "usage: library.mjs list [looks|pieces] [--kind k] | search <words> | show <id> | look <project> <look-id> | add <project> <piece-id>... [--force]",
252
+ "usage: library.mjs list [looks|pieces] [--kind k] | search <words> | show <id> | look <project> <look-id> | add <project> <piece-id>... [--force] | kit <project>",
210
253
  );