frameyard 0.8.0 → 0.9.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 (285) hide show
  1. package/CHANGELOG.md +110 -14
  2. package/GLOSSARY.md +112 -2
  3. package/README.md +302 -265
  4. package/dist/assets/integrations.d.ts +8 -4
  5. package/dist/assets/integrations.d.ts.map +1 -1
  6. package/dist/assets/integrations.js +8 -4
  7. package/dist/assets/integrations.js.map +1 -1
  8. package/dist/assets/provenance.d.ts +2 -2
  9. package/dist/assets/provenance.d.ts.map +1 -1
  10. package/dist/assets/provenance.js +5 -3
  11. package/dist/assets/provenance.js.map +1 -1
  12. package/dist/assets/tts-supertonic.d.ts +28 -0
  13. package/dist/assets/tts-supertonic.d.ts.map +1 -0
  14. package/dist/assets/tts-supertonic.js +156 -0
  15. package/dist/assets/tts-supertonic.js.map +1 -0
  16. package/dist/assets/tts.d.ts +18 -6
  17. package/dist/assets/tts.d.ts.map +1 -1
  18. package/dist/assets/tts.js +219 -97
  19. package/dist/assets/tts.js.map +1 -1
  20. package/dist/cli.js +1685 -93
  21. package/dist/cli.js.map +1 -1
  22. package/dist/composition/format.d.ts +1 -1
  23. package/dist/composition/format.d.ts.map +1 -1
  24. package/dist/composition/format.js +1 -1
  25. package/dist/composition/format.js.map +1 -1
  26. package/dist/context.d.ts +82 -0
  27. package/dist/context.d.ts.map +1 -0
  28. package/dist/context.js +277 -0
  29. package/dist/context.js.map +1 -0
  30. package/dist/contracts.d.ts +25 -0
  31. package/dist/contracts.d.ts.map +1 -1
  32. package/dist/contracts.js.map +1 -1
  33. package/dist/doctor.d.ts +14 -1
  34. package/dist/doctor.d.ts.map +1 -1
  35. package/dist/doctor.js +166 -7
  36. package/dist/doctor.js.map +1 -1
  37. package/dist/errors.d.ts +1 -1
  38. package/dist/errors.d.ts.map +1 -1
  39. package/dist/errors.js.map +1 -1
  40. package/dist/fonts/add.d.ts +41 -0
  41. package/dist/fonts/add.d.ts.map +1 -0
  42. package/dist/fonts/add.js +259 -0
  43. package/dist/fonts/add.js.map +1 -0
  44. package/dist/fonts/binary.d.ts +4 -0
  45. package/dist/fonts/binary.d.ts.map +1 -0
  46. package/dist/fonts/binary.js +21 -0
  47. package/dist/fonts/binary.js.map +1 -0
  48. package/dist/fonts/css.d.ts +4 -0
  49. package/dist/fonts/css.d.ts.map +1 -0
  50. package/dist/fonts/css.js +50 -0
  51. package/dist/fonts/css.js.map +1 -0
  52. package/dist/fonts/fontsource.d.ts +107 -0
  53. package/dist/fonts/fontsource.d.ts.map +1 -0
  54. package/dist/fonts/fontsource.js +565 -0
  55. package/dist/fonts/fontsource.js.map +1 -0
  56. package/dist/fonts/index.d.ts +11 -0
  57. package/dist/fonts/index.d.ts.map +1 -0
  58. package/dist/fonts/index.js +11 -0
  59. package/dist/fonts/index.js.map +1 -0
  60. package/dist/fonts/install.d.ts +24 -0
  61. package/dist/fonts/install.d.ts.map +1 -0
  62. package/dist/fonts/install.js +145 -0
  63. package/dist/fonts/install.js.map +1 -0
  64. package/dist/fonts/manifest.d.ts +21 -0
  65. package/dist/fonts/manifest.d.ts.map +1 -0
  66. package/dist/fonts/manifest.js +96 -0
  67. package/dist/fonts/manifest.js.map +1 -0
  68. package/dist/fonts/options.d.ts +9 -0
  69. package/dist/fonts/options.d.ts.map +1 -0
  70. package/dist/fonts/options.js +66 -0
  71. package/dist/fonts/options.js.map +1 -0
  72. package/dist/fonts/schema.d.ts +4 -0
  73. package/dist/fonts/schema.d.ts.map +1 -0
  74. package/dist/fonts/schema.js +316 -0
  75. package/dist/fonts/schema.js.map +1 -0
  76. package/dist/fonts/summary.d.ts +16 -0
  77. package/dist/fonts/summary.d.ts.map +1 -0
  78. package/dist/fonts/summary.js +22 -0
  79. package/dist/fonts/summary.js.map +1 -0
  80. package/dist/fonts/types.d.ts +56 -0
  81. package/dist/fonts/types.d.ts.map +1 -0
  82. package/dist/fonts/types.js +8 -0
  83. package/dist/fonts/types.js.map +1 -0
  84. package/dist/fonts/verify.d.ts +33 -0
  85. package/dist/fonts/verify.d.ts.map +1 -0
  86. package/dist/fonts/verify.js +200 -0
  87. package/dist/fonts/verify.js.map +1 -0
  88. package/dist/html/validate.d.ts.map +1 -1
  89. package/dist/html/validate.js +59 -0
  90. package/dist/html/validate.js.map +1 -1
  91. package/dist/index.d.ts +3 -3
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +1 -1
  94. package/dist/index.js.map +1 -1
  95. package/dist/inspect.d.ts +61 -0
  96. package/dist/inspect.d.ts.map +1 -0
  97. package/dist/inspect.js +325 -0
  98. package/dist/inspect.js.map +1 -0
  99. package/dist/lint.d.ts +63 -0
  100. package/dist/lint.d.ts.map +1 -0
  101. package/dist/lint.js +265 -0
  102. package/dist/lint.js.map +1 -0
  103. package/dist/policy.d.ts +69 -0
  104. package/dist/policy.d.ts.map +1 -0
  105. package/dist/policy.js +361 -0
  106. package/dist/policy.js.map +1 -0
  107. package/dist/preview/timeline-data.d.ts +17 -69
  108. package/dist/preview/timeline-data.d.ts.map +1 -1
  109. package/dist/preview/timeline-host.d.ts.map +1 -1
  110. package/dist/preview/timeline-host.js +70 -45
  111. package/dist/preview/timeline-host.js.map +1 -1
  112. package/dist/preview/timeline-main.d.ts.map +1 -1
  113. package/dist/preview/timeline-main.js +47 -4
  114. package/dist/preview/timeline-main.js.map +1 -1
  115. package/dist/project.d.ts +10 -3
  116. package/dist/project.d.ts.map +1 -1
  117. package/dist/project.js +20 -17
  118. package/dist/project.js.map +1 -1
  119. package/dist/renderer/ffmpeg.d.ts +42 -2
  120. package/dist/renderer/ffmpeg.d.ts.map +1 -1
  121. package/dist/renderer/ffmpeg.js +254 -22
  122. package/dist/renderer/ffmpeg.js.map +1 -1
  123. package/dist/renderer/index.js +1 -1
  124. package/dist/renderer/index.js.map +1 -1
  125. package/dist/renderer/keyframe-expressions.d.ts +39 -0
  126. package/dist/renderer/keyframe-expressions.d.ts.map +1 -0
  127. package/dist/renderer/keyframe-expressions.js +180 -0
  128. package/dist/renderer/keyframe-expressions.js.map +1 -0
  129. package/dist/renderer/timeline.d.ts +2 -0
  130. package/dist/renderer/timeline.d.ts.map +1 -1
  131. package/dist/renderer/timeline.js +89 -116
  132. package/dist/renderer/timeline.js.map +1 -1
  133. package/dist/runtime/host.d.ts.map +1 -1
  134. package/dist/runtime/host.js +4 -0
  135. package/dist/runtime/host.js.map +1 -1
  136. package/dist/scaffold.d.ts +3 -0
  137. package/dist/scaffold.d.ts.map +1 -1
  138. package/dist/scaffold.js +48 -320
  139. package/dist/scaffold.js.map +1 -1
  140. package/dist/server/composition-html.d.ts +2 -0
  141. package/dist/server/composition-html.d.ts.map +1 -1
  142. package/dist/server/composition-html.js +7 -0
  143. package/dist/server/composition-html.js.map +1 -1
  144. package/dist/server/server.d.ts.map +1 -1
  145. package/dist/server/server.js +31 -1
  146. package/dist/server/server.js.map +1 -1
  147. package/dist/server/timeline-preview-data.d.ts.map +1 -1
  148. package/dist/server/timeline-preview-data.js +12 -34
  149. package/dist/server/timeline-preview-data.js.map +1 -1
  150. package/dist/server/timeline-preview-html.d.ts.map +1 -1
  151. package/dist/server/timeline-preview-html.js +18 -2
  152. package/dist/server/timeline-preview-html.js.map +1 -1
  153. package/dist/templates.d.ts +16 -0
  154. package/dist/templates.d.ts.map +1 -0
  155. package/dist/templates.js +186 -0
  156. package/dist/templates.js.map +1 -0
  157. package/dist/timeline/easing.d.ts +36 -0
  158. package/dist/timeline/easing.d.ts.map +1 -0
  159. package/dist/timeline/easing.js +225 -0
  160. package/dist/timeline/easing.js.map +1 -0
  161. package/dist/timeline/keyframes.d.ts +23 -0
  162. package/dist/timeline/keyframes.d.ts.map +1 -0
  163. package/dist/timeline/keyframes.js +95 -0
  164. package/dist/timeline/keyframes.js.map +1 -0
  165. package/dist/timeline/plan.d.ts +6 -12
  166. package/dist/timeline/plan.d.ts.map +1 -1
  167. package/dist/timeline/plan.js +17 -30
  168. package/dist/timeline/plan.js.map +1 -1
  169. package/dist/timeline/resolve.d.ts +12 -0
  170. package/dist/timeline/resolve.d.ts.map +1 -1
  171. package/dist/timeline/resolve.js +105 -9
  172. package/dist/timeline/resolve.js.map +1 -1
  173. package/dist/timeline/scene.d.ts +126 -0
  174. package/dist/timeline/scene.d.ts.map +1 -0
  175. package/dist/timeline/scene.js +109 -0
  176. package/dist/timeline/scene.js.map +1 -0
  177. package/dist/timeline/schema.d.ts +31 -0
  178. package/dist/timeline/schema.d.ts.map +1 -1
  179. package/dist/timeline/schema.js +240 -9
  180. package/dist/timeline/schema.js.map +1 -1
  181. package/dist/timeline/transitions.d.ts +21 -0
  182. package/dist/timeline/transitions.d.ts.map +1 -0
  183. package/dist/timeline/transitions.js +23 -0
  184. package/dist/timeline/transitions.js.map +1 -0
  185. package/dist/version.d.ts +1 -1
  186. package/dist/version.js +1 -1
  187. package/docs/adr/0007-make-html-documents-the-composition-unit.md +1 -1
  188. package/docs/adr/0010-make-timelines-the-render-unit.md +3 -2
  189. package/docs/adr/0014-compose-timeline-clips-with-css-blend-modes.md +2 -1
  190. package/docs/adr/0015-inherit-composition-format-from-the-project.md +2 -2
  191. package/docs/adr/0016-author-compositions-as-html-fragments.md +1 -1
  192. package/docs/adr/0018-animate-timeline-clips-with-keyframes.md +35 -0
  193. package/docs/adr/0019-make-same-track-overlap-the-transition-window.md +36 -0
  194. package/docs/adr/0020-crossfade-audio-clips-on-audio-tracks.md +27 -0
  195. package/docs/adr/0021-project-timeline-scenes-once.md +32 -0
  196. package/docs/adr/0022-gate-heavy-operations-with-a-workspace-resource-policy.md +35 -0
  197. package/docs/adr/0023-pin-fonts-as-project-assets.md +98 -0
  198. package/docs/migration-0.4.md +2 -2
  199. package/docs/preview-testing.md +19 -6
  200. package/docs/{v0.1-design.md → v0.x-design.md} +55 -30
  201. package/examples/blend/README.md +3 -3
  202. package/examples/demo/README.md +2 -2
  203. package/examples/intro/README.md +71 -0
  204. package/examples/intro/assets/fonts/figtree/LICENSE.txt +93 -0
  205. package/examples/intro/assets/fonts/figtree/figtree-latin-wght-normal.woff2 +0 -0
  206. package/examples/intro/assets/fonts/instrument-serif/LICENSE.txt +93 -0
  207. package/examples/intro/assets/fonts/instrument-serif/instrument-serif-latin-400-italic.woff2 +0 -0
  208. package/examples/intro/assets/fonts/space-mono/LICENSE.txt +93 -0
  209. package/examples/intro/assets/fonts/space-mono/space-mono-latin-400-normal.woff2 +0 -0
  210. package/examples/intro/assets/fonts/space-mono/space-mono-latin-700-normal.woff2 +0 -0
  211. package/examples/intro/assets/grain.png +0 -0
  212. package/examples/intro/assets/score.ogg +0 -0
  213. package/examples/intro/compositions/brand.html +412 -0
  214. package/examples/intro/compositions/chrome.controller.js +63 -0
  215. package/examples/intro/compositions/chrome.html +256 -0
  216. package/examples/intro/compositions/lattice.controller.js +148 -0
  217. package/examples/intro/compositions/lattice.html +308 -0
  218. package/examples/intro/compositions/opening.html +504 -0
  219. package/examples/intro/compositions/studio.html +930 -0
  220. package/examples/intro/compositions/theme.css +218 -0
  221. package/examples/intro/fy.config.js +13 -0
  222. package/examples/intro/fy.fonts.json +100 -0
  223. package/examples/intro/scripts/make-grain.mjs +79 -0
  224. package/examples/intro/scripts/make-score.mjs +702 -0
  225. package/examples/intro/timelines/intro.timeline.json +60 -0
  226. package/examples/keyframes/README.md +76 -0
  227. package/examples/keyframes/compositions/backdrop.html +60 -0
  228. package/examples/keyframes/compositions/card.html +99 -0
  229. package/examples/keyframes/fy.config.js +10 -0
  230. package/examples/keyframes/timelines/keyframes.timeline.json +60 -0
  231. package/examples/nested/README.md +112 -0
  232. package/examples/nested/assets/pulse.wav +0 -0
  233. package/examples/nested/compositions/backdrop.html +84 -0
  234. package/examples/nested/compositions/card.html +275 -0
  235. package/examples/nested/compositions/overlay.html +307 -0
  236. package/examples/nested/fy.config.js +13 -0
  237. package/examples/nested/scripts/make-pulse.sh +20 -0
  238. package/examples/nested/timelines/chapter-a.timeline.json +39 -0
  239. package/examples/nested/timelines/chapter-b.timeline.json +39 -0
  240. package/examples/nested/timelines/nested-reel.timeline.json +52 -0
  241. package/examples/static/README.md +2 -2
  242. package/examples/transitions/README.md +128 -0
  243. package/examples/transitions/assets/alpha.wav +0 -0
  244. package/examples/transitions/assets/beta.wav +0 -0
  245. package/examples/transitions/assets/gamma.wav +0 -0
  246. package/examples/transitions/compositions/alpha.html +56 -0
  247. package/examples/transitions/compositions/beta.html +56 -0
  248. package/examples/transitions/compositions/gamma.html +56 -0
  249. package/examples/transitions/fy.config.js +12 -0
  250. package/examples/transitions/timelines/audio-crossfade.timeline.json +44 -0
  251. package/examples/transitions/timelines/transitions.timeline.json +31 -0
  252. package/examples/video/README.md +4 -4
  253. package/examples/webgl/README.md +1 -1
  254. package/package.json +12 -5
  255. package/scaffolds/init/AGENTS.md +131 -0
  256. package/scaffolds/init/README.md +56 -0
  257. package/scaffolds/init/compositions/hello.html +79 -0
  258. package/scaffolds/init/fy.config.js +9 -0
  259. package/scaffolds/init/gitignore.template +1 -0
  260. package/scaffolds/init/package.json +11 -0
  261. package/scaffolds/init/props/hello.json +4 -0
  262. package/scaffolds/init/props/main.json +6 -0
  263. package/scaffolds/init/timelines/main.timeline.json +20 -0
  264. package/scaffolds/local-models/LOCAL-MODELS.md +77 -0
  265. package/scaffolds/local-models/fy.policy.json +5 -0
  266. package/scaffolds/local-models/opencode/opencode.jsonc +17 -0
  267. package/scaffolds/local-models/pi/APPEND_SYSTEM.md +21 -0
  268. package/scaffolds/local-models/pi/sandbox.json +15 -0
  269. package/scaffolds/local-models/pi/settings.json +15 -0
  270. package/skills/frameyard/README.md +1 -0
  271. package/skills/frameyard/SKILL.md +59 -61
  272. package/skills/frameyard/references/audio-assets.md +32 -3
  273. package/skills/frameyard/references/common-mistakes.md +53 -2
  274. package/skills/frameyard/references/deterministic-authoring.md +10 -3
  275. package/skills/frameyard/references/diagnostics.md +31 -1
  276. package/skills/frameyard/references/failure-recovery.md +32 -6
  277. package/skills/frameyard/references/fonts.md +132 -0
  278. package/skills/frameyard/references/json-results.md +119 -6
  279. package/skills/frameyard/references/quick-reference.md +222 -0
  280. package/skills/frameyard/references/timelines.md +97 -7
  281. /package/examples/blend/{video.config.js → fy.config.js} +0 -0
  282. /package/examples/demo/{video.config.js → fy.config.js} +0 -0
  283. /package/examples/static/{video.config.js → fy.config.js} +0 -0
  284. /package/examples/video/{video.config.js → fy.config.js} +0 -0
  285. /package/examples/webgl/{video.config.js → fy.config.js} +0 -0
@@ -0,0 +1,56 @@
1
+ # My Frameyard project
2
+
3
+ Coding agents should read `AGENTS.md` for the project rules and the lint, check, still, and render workflow.
4
+
5
+ The reusable composition is a partial HTML fragment at `compositions/hello.html`. The edit is `timelines/main.timeline.json`, which places that composition as a clip in the project format. The project format is fixed in `fy.config.js`.
6
+
7
+ Start the preview:
8
+
9
+ ```sh
10
+ npx frameyard dev
11
+ ```
12
+
13
+ The preview lists the timeline next to the compositions. Open the timeline to scrub it and inspect the program monitor.
14
+
15
+ Validate and render the timeline:
16
+
17
+ ```sh
18
+ npx frameyard doctor
19
+ npx frameyard check main
20
+ npx frameyard render main --props props/main.json
21
+ ```
22
+
23
+ `props/main.json` maps composition clip IDs to props overrides. The video is written to `out/main.mp4` in the project. Pass `--out <file.mp4>` to choose another path. Timelines with heavy composition work accept `--workers <n>` and `--scratch <dir>`.
24
+
25
+ The composition can still be previewed and rendered on its own:
26
+
27
+ ```sh
28
+ npx frameyard check hello
29
+ npx frameyard render hello --props props/hello.json
30
+ ```
31
+
32
+ Install a font when the design needs one:
33
+
34
+ ```sh
35
+ npx frameyard font search "space grotesk" --subset latin --json
36
+ npx frameyard font add space-grotesk --subset latin
37
+ ```
38
+
39
+ Or vendor a local font:
40
+
41
+ ```sh
42
+ npx frameyard font add --file ./fonts/SpaceGrotesk.woff2 \
43
+ --family "Space Grotesk" --license OFL-1.1 --license-file ./fonts/OFL.txt
44
+ npx frameyard font verify
45
+ ```
46
+
47
+ The server injects the font sheet into every composition; use the family name in CSS. Commit `fy.fonts.json`, `assets/fonts/`, and license files. See `AGENTS.md` for the policy limits.
48
+
49
+ Or run the pinned scripts from `package.json`:
50
+
51
+ ```sh
52
+ npm run dev
53
+ npm run check -- main
54
+ npm run render -- main --props props/main.json
55
+ npm run still -- main --frame 42 --out main-42.png
56
+ ```
@@ -0,0 +1,79 @@
1
+ <script type="application/json" data-fy="props">
2
+ { "title": "Hello, Frameyard", "accent": "#b8f36b" }
3
+ </script>
4
+ <style>
5
+ @keyframes fade-up {
6
+ from {
7
+ opacity: 0;
8
+ transform: translateY(28px);
9
+ }
10
+ to {
11
+ opacity: 1;
12
+ transform: translateY(0);
13
+ }
14
+ }
15
+ @keyframes drift {
16
+ from {
17
+ transform: translate(-120px, -80px);
18
+ }
19
+ to {
20
+ transform: translate(120px, 80px);
21
+ }
22
+ }
23
+ #hello {
24
+ position: relative;
25
+ width: 100%;
26
+ height: 100%;
27
+ display: grid;
28
+ place-items: center;
29
+ overflow: hidden;
30
+ background: #0b0d10;
31
+ color: #e8edf3;
32
+ font-family: Inter, system-ui, sans-serif;
33
+ }
34
+ #hello h1 {
35
+ position: relative;
36
+ margin: 0;
37
+ font-size: 96px;
38
+ letter-spacing: -0.04em;
39
+ opacity: 0;
40
+ animation: fade-up 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
41
+ }
42
+ #hello p {
43
+ position: relative;
44
+ margin: 18px 0 0;
45
+ color: #8a97a6;
46
+ font-size: 26px;
47
+ }
48
+ #glow {
49
+ position: absolute;
50
+ left: 50%;
51
+ top: 50%;
52
+ width: 520px;
53
+ height: 520px;
54
+ margin: -260px 0 0 -260px;
55
+ border-radius: 50%;
56
+ background: var(--fy-prop-accent);
57
+ filter: blur(120px);
58
+ opacity: 0.16;
59
+ animation: drift 12s ease-in-out infinite alternate;
60
+ }
61
+ #sweep {
62
+ position: absolute;
63
+ left: 0;
64
+ bottom: 0;
65
+ width: calc(var(--fy-progress) * 100%);
66
+ height: 4px;
67
+ background: var(--fy-prop-accent);
68
+ }
69
+ </style>
70
+ <fy-composition width="1280" height="720" fps="30" duration-in-frames="150">
71
+ <div id="hello">
72
+ <div id="glow"></div>
73
+ <div style="position: relative; text-align: center">
74
+ <h1 id="title" data-fy-text="title" data-fy-anim-start="0" data-fy-anim-duration="24"></h1>
75
+ <p>Deterministic video, authored on the web.</p>
76
+ </div>
77
+ <div id="sweep"></div>
78
+ </div>
79
+ </fy-composition>
@@ -0,0 +1,9 @@
1
+ export default {
2
+ format: { width: 1280, height: 720, fps: 30 },
3
+ compositions: {
4
+ hello: "./compositions/hello.html",
5
+ },
6
+ timelines: {
7
+ main: "./timelines/main.timeline.json",
8
+ },
9
+ };
@@ -0,0 +1 @@
1
+ out/
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "npx --yes frameyard@{{VERSION}} dev",
7
+ "check": "npx --yes frameyard@{{VERSION}} check",
8
+ "render": "npx --yes frameyard@{{VERSION}} render",
9
+ "still": "npx --yes frameyard@{{VERSION}} still"
10
+ }
11
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Hello, Frameyard",
3
+ "accent": "#b8f36b"
4
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "hello": {
3
+ "title": "Hello, Frameyard",
4
+ "accent": "#b8f36b"
5
+ }
6
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "version": 1,
3
+ "tracks": [
4
+ {
5
+ "id": "v1",
6
+ "kind": "video"
7
+ }
8
+ ],
9
+ "clips": [
10
+ {
11
+ "id": "hello",
12
+ "track": "v1",
13
+ "source": {
14
+ "kind": "composition",
15
+ "id": "hello"
16
+ },
17
+ "start": 0
18
+ }
19
+ ]
20
+ }
@@ -0,0 +1,77 @@
1
+ # Optional local-model scaffold
2
+
3
+ Install these files into a workspace when a local model and Frameyard share the
4
+ same machine. They are not active in this repository by default.
5
+
6
+ ```sh
7
+ npx frameyard add local-models
8
+ ```
9
+
10
+ For a new project, install the normal scaffold and the host setup together:
11
+
12
+ ```sh
13
+ npx frameyard init my-project --local-models
14
+ ```
15
+
16
+ Manual copy is still possible:
17
+
18
+ ```sh
19
+ mkdir -p .opencode .pi
20
+ cp -R scaffolds/local-models/opencode/. .opencode/
21
+ cp -R scaffolds/local-models/pi/. .pi/
22
+ cp scaffolds/local-models/fy.policy.json .
23
+ ```
24
+
25
+ The CLI refuses to overwrite an existing file. If the target workspace already
26
+ has an `.opencode/opencode.jsonc` or `.pi/` configuration, merge the scaffold
27
+ instead of copying over it.
28
+
29
+ ## What each file does
30
+
31
+ - `fy.policy.json`: the `authoring` tier and `shared-cpu` profile. It
32
+ blocks `tts`, `transcribe`, and `render`; allows `lint`, `context`, `doctor`,
33
+ `add`, `inspect`, `compare`, `font search`, `font list`, `font verify`,
34
+ `font add` without `--force`, `check`, and at most four still frames; and
35
+ denies `--force`/`--overwrite`, `font remove`, and an unlicensed `font add
36
+ --url`. Adjust the tier or profile for the machine. Remove the file to return
37
+ to the default `full` tier.
38
+ - `.opencode/opencode.jsonc`: project-local OpenCode permissions for a local
39
+ model session. It denies the common heavy shell forms and protects
40
+ `fy.policy.json`, `.opencode/`, `.pi/`, and `skills/frameyard/` from
41
+ edits. It also adds `./skills` as a skill search path, which assumes the
42
+ official skill lives in the workspace; remove that line if it does not.
43
+ - `.pi/settings.json`: points Pi at `http://127.0.0.1:9931` and defaults to
44
+ `LFM3-1.7B-Q8_0`, with only `read`, `bash`, `edit`, and `write` enabled.
45
+ - `.pi/APPEND_SYSTEM.md`: the local-model workflow: `context` → `lint` →
46
+ `still` → `check` → handoff with `--dry-run`.
47
+ - `.pi/sandbox.json`: committed but `enabled: false`. Enable it only after
48
+ testing Chromium, FFmpeg, and the local router with the filesystem/network
49
+ rules you need.
50
+
51
+ ## Human overrides
52
+
53
+ The policy is a guardrail, not a security boundary. A human who intends to run
54
+ a heavy operation can override it:
55
+
56
+ ```sh
57
+ FRAMEYARD_POLICY=full npx frameyard render main --props props/main.json
58
+ ```
59
+
60
+ The generated example READMEs in this repository use that prefix because they
61
+ may be run with the scaffold installed.
62
+
63
+ ## Pi project trust
64
+
65
+ Pi loads `.pi/` only for trusted projects. Either start Pi with:
66
+
67
+ ```sh
68
+ pi --approve
69
+ ```
70
+
71
+ or add the project to `~/.pi/agent/trust.json` once. The trust entry is the only
72
+ global change; the rest stays in the workspace.
73
+
74
+ ## Opting out
75
+
76
+ Remove `fy.policy.json` and the `.opencode/`/`.pi/` files you copied.
77
+ The CLI then defaults to `full`, and the host configuration no longer applies.
@@ -0,0 +1,5 @@
1
+ {
2
+ "version": 1,
3
+ "policy": "authoring",
4
+ "profile": "shared-cpu"
5
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://opencode.ai/config.json",
3
+ "skills": ["./skills"],
4
+ "permissions": [
5
+ { "action": "edit", "resource": "fy.policy.json", "effect": "deny" },
6
+ { "action": "edit", "resource": ".opencode/**", "effect": "deny" },
7
+ { "action": "edit", "resource": ".pi/**", "effect": "deny" },
8
+ { "action": "edit", "resource": "skills/frameyard/**", "effect": "deny" },
9
+ { "action": "shell", "resource": "*frameyard render*", "effect": "deny" },
10
+ { "action": "shell", "resource": "*frameyard tts*", "effect": "deny" },
11
+ { "action": "shell", "resource": "*frameyard transcribe*", "effect": "deny" },
12
+ { "action": "shell", "resource": "*dist/cli.js render*", "effect": "deny" },
13
+ { "action": "shell", "resource": "*dist/cli.js tts*", "effect": "deny" },
14
+ { "action": "shell", "resource": "*dist/cli.js transcribe*", "effect": "deny" },
15
+ { "action": "shell", "resource": "*--dry-run*", "effect": "allow" },
16
+ ],
17
+ }
@@ -0,0 +1,21 @@
1
+ # Frameyard resource policy
2
+
3
+ This workspace uses `fy.policy.json` at the repository root. Do not edit that
4
+ file, `.pi/`, `.opencode/`, or `skills/frameyard/`.
5
+
6
+ ## Working loop
7
+
8
+ 1. Run `frameyard lint <id> --json` while editing.
9
+ 2. Inspect at most four representative stills with `frameyard still <id> --frame <spec> --json`.
10
+ 3. Run `frameyard check <id> --json` once at the end when the active policy allows it.
11
+ 4. Do not run `frameyard tts`, `frameyard transcribe`, or `frameyard render` unless the
12
+ user explicitly asks. Use `--dry-run` and report `deferred: true` with the exact
13
+ handoff command instead.
14
+ 5. `frameyard font search`, `font list`, and `font verify` are allowed; `font add`
15
+ is allowed without `--force`. `font remove`, `font add --force`, and an
16
+ unlicensed `font add --url` require the `full` policy.
17
+ 6. Read `AGENTS.md` and `skills/frameyard/SKILL.md` before changing public behavior.
18
+
19
+ The local llama.cpp router is at `http://127.0.0.1:9931`. Use a small text model for
20
+ the lint loop and a vision-capable model only for representative stills. Do not
21
+ switch to a larger local model just to re-read the same files.
@@ -0,0 +1,15 @@
1
+ {
2
+ "enabled": false,
3
+ "allowBrowserProcess": false,
4
+ "network": {
5
+ "allowLocalBinding": true,
6
+ "allowAllUnixSockets": true,
7
+ "allowedDomains": [],
8
+ "deniedDomains": []
9
+ },
10
+ "filesystem": {
11
+ "allowRead": [".", "~/.config", "~/.local"],
12
+ "allowWrite": [".", "/tmp"],
13
+ "denyWrite": ["fy.policy.json", ".pi", ".opencode", "skills/frameyard"]
14
+ }
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "https://pi.dev/schemas/settings.schema.json",
3
+ "llamaSettings": {
4
+ "servers": [
5
+ {
6
+ "url": "http://127.0.0.1:9931",
7
+ "id": "local",
8
+ "name": "Local llama.cpp"
9
+ }
10
+ ]
11
+ },
12
+ "defaultTools": ["read", "bash", "edit", "write"],
13
+ "defaultProvider": "local",
14
+ "defaultModel": "LFM3-1.7B-Q8_0"
15
+ }
@@ -25,6 +25,7 @@ When you work inside the Frameyard repository itself, `npx frameyard` resolves t
25
25
  ## Contents
26
26
 
27
27
  - `SKILL.md`: trigger description and operating workflow
28
+ - `references/quick-reference.md`: compact skeletons, command table, policy table, and common fixes
28
29
  - `references/deterministic-authoring.md`
29
30
  - `references/bindings.md`
30
31
  - `references/controllers.md`
@@ -1,92 +1,90 @@
1
1
  ---
2
2
  name: frameyard
3
- description: Create, validate, inspect, and render deterministic Frameyard videos authored with HTML compositions and JSON timelines. Use when a user asks to build or operate a Frameyard project, produce parameterized video, assemble clips into a timeline, add a local TTS or transcription asset through Frameyard's wrappers, or diagnose a Frameyard render. Do not use for general-purpose media generation outside a Frameyard project.
4
- frameyard-api-version: 0.8.0
3
+ description: Create, validate, inspect, and render deterministic Frameyard videos authored with HTML compositions and JSON timelines. Use when a user asks to build or operate a Frameyard project, produce parameterized video, assemble clips into a timeline, search or install project fonts, add a local TTS or transcription asset through Frameyard's wrappers, or diagnose a Frameyard render. Do not use for general-purpose media generation outside a Frameyard project.
4
+ frameyard-api-version: 0.9.0
5
5
  ---
6
6
 
7
7
  # Frameyard
8
8
 
9
- Use Frameyard's public project format and CLI. Do not introduce an agent-only runtime, a hosted service, or direct calls to `qwen-tts` or `whisper-cli`.
9
+ Use the public project format and CLI. Do not add an agent-only runtime, a hosted service, or direct calls to `qwen-tts`, `sherpa-onnx-offline-tts`, or `whisper-cli`.
10
10
 
11
- ## First decisions
11
+ ## First moves
12
12
 
13
- 1. Find the project: look for `video.config.js` from the working directory upward, or run `npx frameyard init <directory>` to scaffold one.
14
- 2. Read the `format`, `compositions`, and `timelines` maps in `video.config.js`. Entries are explicit `.html` composition fragments and `.json` timeline documents; there is no directory scanning.
15
- 3. Read the timeline JSON, the referenced composition fragment, its optional controller, and the props files before changing behavior.
16
- 4. If the task needs speech or transcription, confirm that inference is requested or explicitly approved before producing assets.
13
+ 1. Find `fy.config.js` from the working directory upward, or run `npx frameyard init <directory>`.
14
+ 2. Run `npx frameyard context --json`; read `format`, `compositions`, `timelines`, `fonts`, the target, its props, and its controller before editing.
15
+ 3. Confirm that speech or transcription is requested or explicitly approved before producing assets.
16
+ 4. If context is small, read [quick reference](references/quick-reference.md) first. Router: static/card → quick reference; timelines → [timelines](references/timelines.md); fonts → [fonts](references/fonts.md); captions → [subtitles and karaoke](references/subtitles-karaoke.md); failures → [diagnostics](references/diagnostics.md) and [failure recovery](references/failure-recovery.md); Canvas/WebGL → [controllers](references/controllers.md).
17
17
 
18
18
  ## Workflow
19
19
 
20
- 1. **Author compositions** as partial HTML fragments with one top-level `<fy-composition>` root. Put metadata, default props, markup, CSS animation, bindings, audio, and transcripts in the fragment. Do not add `<!doctype>`, `<html>`, `<head>`, `<body>`, `<title>`, or `<meta>`; Frameyard generates the document shell. Use an optional controller for Canvas 2D, WebGL, karaoke, or per-frame computation CSS cannot express. Omit both `duration-in-frames` and `fps` only when the composition is intentionally static; a static composition is captured once, renders to PNG, and cannot use animation scheduling, media, or transcripts. Read [deterministic authoring](references/deterministic-authoring.md), [bindings](references/bindings.md), and [controllers](references/controllers.md).
21
- 2. **Assemble the timeline** in JSON: project format, video/audio tracks, composition, media, and nested-timeline clips, source ranges, static transforms and blend modes, and audio. Read [timelines](references/timelines.md).
22
- 3. **Provide props** as JSON. Composition props merge over defaults; timeline render props are a map keyed by composition clip ID. Validate domain-specific requirements with a controller's `validateProps()`.
23
- 4. **Produce optional assets** with `npx frameyard tts` and `npx frameyard transcribe`, never with the upstream executables. Read [audio assets](references/audio-assets.md). Do not overwrite or regenerate existing generated assets without permission.
24
- 5. **Validate** with `npx frameyard check <id> --json`, where `<id>` is a timeline or composition. Static validation runs before Chromium; then every animated frame is evaluated, or a static composition's single state is inspected once. Read the structured `diagnostics` array and the result `kind`. Read [diagnostics](references/diagnostics.md) and [common mistakes](references/common-mistakes.md).
25
- 6. **Inspect** representative frames with `npx frameyard still <id> --frame <spec> --out <path> --json`, where `<spec>` is a frame number, an inclusive range like `2-12`, or a comma-separated list with at most 32 unique frames. With one frame, `--out` is a `.png` file; with multiple frames, it is a directory and the files are `still-<id>-<frame>.png`. View the PNGs when the host supports image viewing. If visual inspection is impossible, say so explicitly.
26
- 7. **Render** with `npx frameyard render <id> [--out <file>] [--props <file>] --json`. Animated composition renders accept `--concurrency <n>`; timeline renders accept `--workers <n>` and `--scratch <dir>`. `--container <mp4|mov|mkv|webm>` and `--codec <h264|hevc|vp9>` select the delivery pair (MP4/H.264 by default, WebM defaults to VP9), and `--encoder <auto|software|nvenc|qsv>` selects how it is encoded, defaulting to `auto`. The output extension must match the container. The default output is `out/<id>.<ext>` for video and `out/<id>.png` for a static composition. A static composition render rejects `--container`, `--codec`, `--encoder`, and `--concurrency`; use a timeline when the deliverable must be video.
27
- 8. **Report** the project path, timeline and composition entries, controller, props, generated assets, provenance manifest, validation results, diagnostics, still paths and inspection status, final video path, and any failures.
20
+ 1. **Author** one `<fy-composition>` fragment. Props, the controller, import maps, and styles are top-level siblings; never add a document shell. Omit `duration-in-frames` and `fps` only for a static composition, which renders to PNG and cannot use animation/media/transcripts. Use a controller only for Canvas, WebGL, karaoke, or per-frame math. Read [deterministic authoring](references/deterministic-authoring.md).
21
+ 2. **Assemble** a version-1 timeline with explicit tracks and clips. Sources are composition IDs, nested timeline IDs, or local media. `start`, `sourceIn`, and `duration` are integer frames; no remote URLs. Read [timelines](references/timelines.md).
22
+ 3. **Provide props** as JSON. Composition props merge over defaults; timeline overrides are keyed by composition clip ID. Use `validateProps()` for domain rules.
23
+ 4. **Lint** with `npx frameyard lint <id> --json`. It validates the document/timeline, resolves references, projects every timeline scene/keyframe channel, scans controller determinism, and probes media metadata without Chromium. It is the edit loop, not the full gate.
24
+ 5. **Check** with `npx frameyard check <id> --json`. Every animated frame is evaluated, or a static composition once. Read `diagnostics` and `kind`; treat warnings as actionable.
25
+ 6. **Inspect** with `npx frameyard still <id> --frame <spec> --out <path> --json`. A spec is a frame, an inclusive range, or a comma-separated list. The policy allows at most 32 unique frames in `full` (4 in `authoring`, 1 in `light`). One frame uses a `.png` file; multiple frames use a directory. Use `npx frameyard inspect`/`compare` for text facts, and view the PNGs when the host supports it; if not, say so.
26
+ 7. **Render** with `npx frameyard render <id> [--out <file>] [--props <file>] --json`. Composition renders accept `--concurrency`; timeline renders accept `--workers` and `--scratch`; `--container`, `--codec`, and `--encoder` select delivery. A static composition rejects those four flags. If the policy blocks render, use `--dry-run` and hand off the command; do not claim a render.
27
+ 8. **Report** the project, entries, controller, props, generated assets, validation, diagnostics, still paths and inspection status, video path, and failures. Never claim success when `check` fails, a still is missing, a render exits nonzero, or inspection was skipped.
28
+
29
+ ## Authoring contract
30
+
31
+ - A composition is a partial fragment with one top-level `<fy-composition>` and a top-level `data-fy="props"` block. Do not use `data-fy-host` or `data-fy="format"`. `width`, `height`, and `fps` inherit the project `format`; declare both dimensions together.
32
+ - A static composition has neither `duration-in-frames` nor `fps`; it runs `mount()`/`validateProps()`, not `update()`, and has no frame-state variables. Declaring `fps` without `duration-in-frames` is an error. A static timeline clip needs an explicit `duration` and `sourceIn: 0`.
33
+ - Bindings: `data-fy-text`, `data-fy-attr-*`, `data-fy-class-*`, `data-fy-if`, and `<template data-fy-repeat>`. Primitive top-level props are also `--fy-prop-<name>` CSS variables. Bindings apply once, before `mount`.
34
+ - CSS/WAAPI animations are seeked from the frame clock. `data-fy-anim-start`, `data-fy-anim-duration`, and `data-fy-anim-easing` schedule one animation in frames. Frame state: `--fy-frame`, `--fy-time`, `--fy-progress`, `--fy-fps`, `--fy-duration`, `--fy-duration-frames`.
35
+ - Exactly one optional controller, inline or external, is the only executable code. It may export `mount`, `update`, `dispose`, and `validateProps`; at least one is required. Other scripts and `on*` handlers are rejected.
36
+ - Media and transcript `src` values resolve from the project root; controller `src` and ordinary HTML URLs resolve from the composition URL. Embedded media uses `data-fy-start` (frames), `data-fy-trim` (seconds), optional `data-fy-duration` (seconds), and `data-fy-volume` (0-1). Transcripts use `<fy-transcript src words>`.
37
+ - An optional top-level import map may add bare specifiers for controllers; `frameyard` and `frameyard/` are reserved.
38
+
39
+ ## Determinism
40
+
41
+ - A composition frame depends only on its integer `frame`, immutable `props`, and assets loaded before frame production. A timeline frame derives from its active clips.
42
+ - Use `random("stable:key")` from `frameyard/runtime`; never `Math.random()`. Never use `Date.now()`, `performance.now()`, timers, independent `requestAnimationFrame` loops, or CSS transitions as animation inputs.
43
+ - Load remote data, fonts, and images before frame production. Network is blocked during capture. Prefer project fonts installed with `npx frameyard font add`; remote font URLs produce `FONT_REMOTE` and are not reproducible. Keep `update({ frame, props })` synchronous and seek-safe; do not carry state between frames.
44
+ - Let the runtime seek embedded video; do not play, pause, or set `currentTime` in `update()`. Redraw WebGL from the current frame; production pins software WebGL.
45
+ - Do not run model inference inside `mount()`, `update()`, or frame production.
28
46
 
29
- Never claim success when `check` fails, a still is missing, a render exits nonzero, or visual inspection was skipped. A partial or unpublished render is not a successful result.
47
+ ## Resource policy
30
48
 
31
- ## Command rules
49
+ `fy.policy.json` is read from the working directory upward, independent of `fy.config.js`; `FRAMEYARD_POLICY` overrides it.
32
50
 
33
- - Use `--json` for every non-interactive command. Parse stdout as the structured result; treat stderr as progress and diagnostics.
34
- - Keep the current output files unless the user explicitly approves replacement. Use `--force` only with that approval.
35
- - Use `--project <dir>` when not running from the project root.
36
- - `render` writes to `out/<id>.mp4` in the project root for timelines and animated compositions, and to `out/<id>.png` for static compositions, unless `--out` is passed. Explicit `--out` paths are relative to the current working directory.
37
- - Use `--concurrency <n>` only for animated composition renders; the default is `1` and the maximum is `16`. Use `--workers <n>` for timeline renders; the default is `8`. Timeline renders also accept `--scratch <dir>` for FFV1 intermediates.
38
- - `still --frame <spec>` accepts `n`, `n-m`, or a comma-separated list; ranges are inclusive, duplicates are removed preserving the first occurrence, order is preserved, and one call captures at most 32 unique frames. A static composition exposes only frame 0. Do not use `still` as an image-sequence exporter. With multiple frames, `--out` is a directory and files are named `still-<id>-<frame>.png`; with one frame, `--out` is a `.png` file.
39
- - `--encoder` selects the encoder: `auto` (default) probes NVENC, then Quick Sync, then the codec's software encoder; `software` forces `libx264`, `libx265`, or `libvpx-vp9`; `nvenc` and `qsv` force a vendor. `--codec <h264|hevc|vp9>` and `--container <mp4|mov|mkv|webm>` select the delivery pair: MP4 and MOV hold H.264/HEVC with AAC, MKV holds H.264/HEVC/VP9, and WebM holds VP9 with Opus and defaults to VP9. `--out` must use the container's extension. The probe is a real one-frame encode. Encoders, codecs, and containers change encoded bytes and file size, not captured frames; do not switch them to fix a visual difference. With `nvenc`, timeline `--workers` is capped at eight, the consumer driver session limit, and a larger value reports an `ENCODER_WARNING`. HEVC MP4/MOV files use the `hvc1` tag and do not play in browsers without platform HEVC decoding.
40
- - For a timeline, `--props` is a JSON object keyed by top-level composition clip ID. For a composition, it is the props object.
41
- - Use `npx frameyard doctor --json` before reporting an environment problem. It inspects without repairing or downloading.
42
- - Read [JSON results](references/json-results.md) for result and error shapes, and [failure recovery](references/failure-recovery.md) for actionable error codes.
43
-
44
- ## Authoring rules
45
-
46
- - A composition is a partial HTML fragment with one top-level `<fy-composition>` root and a top-level `data-fy="props"` default-props block. Props, the controller, and import maps are top-level siblings of the root; top-level `<style>` and `<link rel="stylesheet">` elements are hoisted into the generated head. Do not use the reserved `data-fy-host` attribute or `data-fy="format"` block. `width`, `height`, and `fps` are optional and inherit from the project `format`; declare them to override it.
47
- - A composition with neither `duration-in-frames` nor `fps` is static. It is captured once and renders to PNG. It may use `<img>`, CSS, SVG, and canvas, but not `data-fy-anim-*`, `<video>`, `<audio>`, or `<fy-transcript>`. Its controller runs `mount()` and `validateProps()`; `update()` is not called and frame-state variables are not set. CSS animations are paused at time 0. Declaring `fps` without `duration-in-frames` is an error. A static composition clip in a timeline needs an explicit `duration` and `sourceIn: 0`.
48
- - A timeline is a versioned JSON document with explicit tracks and clips. Sources are composition IDs, nested timeline IDs, or local video/image/audio paths. Timeline frames, sourceIn, and duration are integer frames. No remote URLs.
49
- - Bindings are explicit: `data-fy-text`, `data-fy-attr-*`, `data-fy-class-*`, `data-fy-if`, and `data-fy-repeat` on `<template>`. Primitive top-level props are also `--fy-prop-<name>` CSS variables.
50
- - CSS `@keyframes` and the Web Animations API are seeked from the frame clock. `data-fy-anim-start`, `data-fy-anim-duration`, and `data-fy-anim-easing` schedule one animation per element in frames.
51
- - Frame-state variables on `:root`: `--fy-frame`, `--fy-time`, `--fy-progress`, `--fy-fps`, `--fy-duration`, and `--fy-duration-frames`.
52
- - A composition has exactly one optional controller, inline or external. It may export `mount`, `update`, `dispose`, and `validateProps`; at least one is required. Use inline for small controllers; extract to an external module for Canvas 2D, WebGL, karaoke, reuse, tests, or a large controller.
53
- - The controller is the only executable code. Other executable scripts and `on*` handlers are rejected.
54
- - Media and transcript `src` values resolve from the project root. The controller `src` and ordinary HTML asset URLs resolve relative to the composition URL.
55
- - Embedded video/audio use `data-fy-start` (frames), `data-fy-trim` (seconds), optional `data-fy-duration` (seconds), and `data-fy-volume` (0-1). Transcripts use `<fy-transcript src words>`.
56
- - An optional top-level `<script type="importmap">` may add bare specifiers for controllers. Frameyard hoists it before the host module; `frameyard` and `frameyard/` specifiers are reserved.
57
-
58
- ## Determinism guardrails
59
-
60
- - A composition frame may depend only on its integer `frame`, immutable `props`, and assets loaded before frame production. A timeline frame derives from its active clips and the same composition rules.
61
- - Use `random("stable:key")` from `frameyard/runtime`; never use `Math.random()`.
62
- - Never use `Date.now()`, `performance.now()`, timers, independent `requestAnimationFrame` loops, or CSS transitions as animation inputs.
63
- - Finish remote data, fonts, and image loading during the loading phase. Network requests are blocked during frame capture.
64
- - Keep `update({ frame, props })` synchronous and seek-safe. Do not carry state between frames.
65
- - Let the runtime seek embedded `<video>` elements. Do not play, pause, or set `currentTime` in `update()`.
66
- - Redraw WebGL canvases from the current frame. Production pins software WebGL; do not depend on GPU-specific results or the previous frame's drawing buffer.
67
- - Do not run model inference inside `mount()`, `update()`, or frame production.
51
+ - `light`: one still frame; blocks `check`, `tts`, `transcribe`, and `render`.
52
+ - `authoring`: four still frames and `check` at low priority; blocks `tts`, `transcribe`, and `render`.
53
+ - `full`: everything up to the 32-frame still cap.
54
+ - `lint`, `context`, `doctor`, `add`, `inspect`, `compare`, and `--dry-run` are available in every tier. `font search`, `font list`, and `font verify` are available in every tier; `font add` requires `authoring` or `full`; `font remove`, `font add --force`, and `font add --url` without `--license`/`--license-file` require `full`.
55
+ - When blocked, run `--dry-run`, report `deferred: true`, and give the user `commandLine`, `prerequisites`, and `blockers`. Never claim a render or asset exists.
56
+ - `--profile shared-cpu` caps timeline workers at 2 and composition concurrency at 1 and lowers priority; `--low-priority` lowers priority without caps. Values above a cap fail with `RESOURCE_PROFILE_CONFLICT`; do not clamp silently.
57
+ - `light`/`authoring` deny `--force` and `--overwrite`; ask before replacing an output. Do not edit the policy file, host configuration, or skill source. The policy is a guardrail, not a security boundary.
68
58
 
69
- ## Diagnostics rule
59
+ ## Command rules
60
+
61
+ - Use `--json` for non-interactive commands; parse stdout, keep stderr for progress/diagnostics. Use `--project <dir>` outside the project root.
62
+ - Outputs default to `out/<id>.<ext>` for video and `out/<id>.png` for static compositions; explicit `--out` is relative to the current directory. Replace outputs only with approval.
63
+ - For a timeline, `--props` is a map keyed by top-level composition clip ID; for a composition, it is the props object. Use `--concurrency` for animated compositions, `--workers`/`--scratch` for timelines. `--container <mp4|mov|mkv|webm>`, `--codec <h264|hevc|vp9>`, and `--encoder <auto|software|nvenc|qsv>` select delivery; the output extension must match the container.
64
+ - Produce speech and transcripts only with `npx frameyard tts` and `npx frameyard transcribe`, never upstream executables; read [audio assets](references/audio-assets.md). Do not overwrite generated assets without approval.
65
+ - Use `npx frameyard add --list --json` and `add composition|timeline <id> --template <name> --json` for templates; merge the returned registration object into `fy.config.js`. `add` never edits the config or overwrites an entry.
66
+ - Use `npx frameyard font search <query> --json` to find a Fontsource family, then `npx frameyard font add <fontsource-id> --json` to pin and vendor it. Use `font add --file` for a local or brand font, or `font add --url` for an explicit HTTPS source. Reference the returned CSS family. `font list`, `font verify`, and `font remove` manage the manifest; never edit `fy.fonts.json` hashes by hand. Read [fonts](references/fonts.md).
67
+ - Use `npx frameyard doctor --json` before reporting an environment problem. Read [JSON results](references/json-results.md) and [failure recovery](references/failure-recovery.md).
70
68
 
71
- `check`, `still`, and `render` run static composition validation before Chromium. Errors fail before a browser starts; warnings allow frame evaluation. Every diagnostic has a stable `code`, a `message`, and optional `details` with source locations and suggestions. Treat warnings as actionable. Do not ignore `BINDING_PATH_MISSING`, `ANIMATION_NOT_FOUND`, `ANIMATION_MULTIPLE`, `STATIC_UPDATE_IGNORED`, `STATIC_ANIMATION_PAUSED`, or `DETERMINISM_WARNING`. `STATIC_ANIMATION_UNSUPPORTED` and `STATIC_MEDIA_UNSUPPORTED` mean the composition should be animated with `duration-in-frames`/`fps` or the media should move to a timeline.
69
+ ## Diagnostics and karaoke
72
70
 
73
- ## Subtitle and karaoke rule
71
+ `check`, `still`, and `render` run static validation before Chromium; errors stop, warnings allow frames. Diagnostics carry a stable `code`, message, and optional source location/suggestion. Treat `BINDING_PATH_MISSING`, `ANIMATION_*`, `STATIC_*`, and `DETERMINISM_WARNING` as actionable.
74
72
 
75
- Use canonical transcripts produced by `npx frameyard transcribe`. Declare them with `<fy-transcript src="assets/voice.transcript.json" words>`. Convert integer source-relative milliseconds to composition frames with the audio clip's start frame, trim start, and the composition `fps`. Karaoke highlighting requires actual `words` timings; never interpolate segment timings. Read [subtitles and karaoke](references/subtitles-karaoke.md).
73
+ Use transcripts from `npx frameyard transcribe`. Convert source-relative milliseconds to composition frames with the clip start, trim start, and `fps`. Karaoke requires measured `words`; never interpolate segment timings.
76
74
 
77
75
  ## CLI invocation
78
76
 
79
- Use `npx frameyard` (or an installed `frameyard` binary) directly. `scripts/frameyard.mjs` is a fallback for hosts that cannot resolve package binaries: it prefers the package-local `dist/cli.js`, then the `frameyard` command on `PATH`. When the skill is copied out of the package, set `FRAMEYARD_CLI` to an installed CLI path if that command is unavailable.
77
+ Use `npx frameyard` or an installed binary. `scripts/frameyard.mjs` is the fallback; set `FRAMEYARD_CLI` when the command is unavailable. Pin the CLI with `npx frameyard@<version>` when it differs from `frameyard-api-version`.
80
78
 
81
79
  ```sh
82
80
  npx frameyard check <id> --json
83
- node scripts/frameyard.mjs check <id> --json # fallback
81
+ node scripts/frameyard.mjs check <id> --json
84
82
  ```
85
83
 
86
- When the CLI version differs from `frameyard-api-version`, pin it with `npx frameyard@<version>`.
87
-
88
84
  ## References
89
85
 
86
+ - [Quick reference](references/quick-reference.md)
87
+ - [Fonts](references/fonts.md)
90
88
  - [Timelines](references/timelines.md)
91
89
  - [Deterministic HTML authoring](references/deterministic-authoring.md)
92
90
  - [Bindings](references/bindings.md)
@@ -34,7 +34,9 @@ Declare required transcripts with:
34
34
 
35
35
  ## Local speech synthesis
36
36
 
37
- Use Frameyard, not `qwen-tts` directly:
37
+ Use Frameyard instead of calling the upstream executables directly:
38
+
39
+ ### qwentts.cpp (default)
38
40
 
39
41
  ```sh
40
42
  npx frameyard tts \
@@ -47,6 +49,7 @@ npx frameyard tts \
47
49
  --json
48
50
  ```
49
51
 
52
+ - `--engine qwen` is the default and may be omitted.
50
53
  - CustomVoice only: `--speaker` is required.
51
54
  - Text comes from `--text-file` or stdin.
52
55
  - `--style` maps to the upstream style instruction.
@@ -58,6 +61,28 @@ npx frameyard tts \
58
61
  - Executable resolution: `--executable`, then `FRAMEYARD_QWEN_TTS`, then `PATH`.
59
62
  - Frameyard does not build qwentts.cpp, download models, or redistribute weights.
60
63
 
64
+ ### Supertonic 3
65
+
66
+ ```sh
67
+ npx frameyard tts \
68
+ --engine supertonic \
69
+ --speaker 0 \
70
+ --model-dir models/sherpa-onnx-supertonic-3-tts-int8-2026-05-11 \
71
+ --language es \
72
+ --text-file script.txt \
73
+ --out assets/voice-es.wav \
74
+ --json
75
+ ```
76
+
77
+ - The engine covers 31 languages and requires one of their codes (`es`, `en`, `fr`, `de`, ...); `--language` is mandatory.
78
+ - `--speaker` is the numeric `--sid`, not a name.
79
+ - `--model-dir` points at the extracted Supertonic 3 archive with its seven files; do not pass individual files.
80
+ - `--style` and `--device` are qwen-only. Put Supertonic expression tags such as `<laugh>`, `<breath>`, and `<sigh>` in the text.
81
+ - The engine writes its native WAV; Frameyard normalizes it to 24 kHz mono and records `sourceSampleRate` and `sourceChannels` in the manifest.
82
+ - `--force` is required to replace an existing asset. Do not pass it without user approval.
83
+ - Executable resolution: `--executable`, then `FRAMEYARD_SHERPA_ONNX_TTS`, then `PATH`.
84
+ - Frameyard does not build sherpa-onnx, download the model archive, or redistribute weights. The upstream Supertonic repository is archived and its weights use the OpenRAIL-M license.
85
+
61
86
  ## Local transcription
62
87
 
63
88
  ```sh
@@ -77,9 +102,13 @@ npx frameyard transcribe assets/voice.wav \
77
102
  - Executable resolution: `--executable`, then `FRAMEYARD_WHISPER_CLI`, then `PATH`.
78
103
  - Frameyard does not download Whisper models or execute Whisper's generated karaoke shell script.
79
104
 
105
+ ## Pinned upstream revisions
106
+
107
+ The wrappers target the CLI surfaces of qwentts.cpp commit `5916bf7c79dbb4e66fc059cdc507a79a23a28bb4`, sherpa-onnx commit `af2d8d81dbfa61c4cea4c568da78f3982edca005` (v2.0.1), and whisper.cpp commit `284029c07012d2000941a08d38a31ffb71f4734e`.
108
+
80
109
  ## Provenance manifest
81
110
 
82
- Both commands update `frameyard.assets.json` in the project root unless `--manifest <file>` is supplied. The manifest records:
111
+ Both commands update `fy.assets.json` in the project root unless `--manifest <file>` is supplied. The manifest records:
83
112
 
84
113
  - artifact path, content hash, and size;
85
114
  - generator command, Frameyard version, executable, and integration revision;
@@ -93,5 +122,5 @@ Commit the manifest with the project. It explains how generated inputs were prod
93
122
  - Do not regenerate or overwrite an existing generated asset without permission.
94
123
  - Do not silently switch models, speakers, language, or text.
95
124
  - Report the manifest path and the exact asset paths after success.
96
- - `npx frameyard doctor` verifies manifest integrity, including artifact, source, model, and derived-export hashes, and optional integration executables; it does not download or repair anything. Pass `--talker-model`, `--codec-model`, or `--model` to inspect model readability and identity before generation.
125
+ - `npx frameyard doctor` verifies manifest integrity, including artifact, source, model, and derived-export hashes, and optional integration executables; it does not download or repair anything. Pass `--talker-model`, `--codec-model`, `--model-dir`, or `--model` to inspect model readability and identity before generation.
97
126
  - Once assets are frozen, checking and rendering do not need the model or inference executable.