@vibeframe/cli 0.52.1 → 0.54.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 (92) hide show
  1. package/LICENSE +21 -0
  2. package/dist/agent/tools/index.d.ts +1 -0
  3. package/dist/agent/tools/index.d.ts.map +1 -1
  4. package/dist/agent/tools/index.js +3 -0
  5. package/dist/agent/tools/index.js.map +1 -1
  6. package/dist/agent/tools/scene.d.ts +17 -0
  7. package/dist/agent/tools/scene.d.ts.map +1 -0
  8. package/dist/agent/tools/scene.js +263 -0
  9. package/dist/agent/tools/scene.js.map +1 -0
  10. package/dist/agent/tools/scene.test.d.ts +2 -0
  11. package/dist/agent/tools/scene.test.d.ts.map +1 -0
  12. package/dist/agent/tools/scene.test.js +139 -0
  13. package/dist/agent/tools/scene.test.js.map +1 -0
  14. package/dist/commands/_shared/scene-html-emit.d.ts +120 -0
  15. package/dist/commands/_shared/scene-html-emit.d.ts.map +1 -0
  16. package/dist/commands/_shared/scene-html-emit.js +422 -0
  17. package/dist/commands/_shared/scene-html-emit.js.map +1 -0
  18. package/dist/commands/_shared/scene-html-emit.test.d.ts +2 -0
  19. package/dist/commands/_shared/scene-html-emit.test.d.ts.map +1 -0
  20. package/dist/commands/_shared/scene-html-emit.test.js +396 -0
  21. package/dist/commands/_shared/scene-html-emit.test.js.map +1 -0
  22. package/dist/commands/_shared/scene-lint.d.ts +110 -0
  23. package/dist/commands/_shared/scene-lint.d.ts.map +1 -0
  24. package/dist/commands/_shared/scene-lint.js +207 -0
  25. package/dist/commands/_shared/scene-lint.js.map +1 -0
  26. package/dist/commands/_shared/scene-lint.test.d.ts +2 -0
  27. package/dist/commands/_shared/scene-lint.test.d.ts.map +1 -0
  28. package/dist/commands/_shared/scene-lint.test.js +208 -0
  29. package/dist/commands/_shared/scene-lint.test.js.map +1 -0
  30. package/dist/commands/_shared/scene-project.d.ts +101 -0
  31. package/dist/commands/_shared/scene-project.d.ts.map +1 -0
  32. package/dist/commands/_shared/scene-project.js +281 -0
  33. package/dist/commands/_shared/scene-project.js.map +1 -0
  34. package/dist/commands/_shared/scene-project.test.d.ts +2 -0
  35. package/dist/commands/_shared/scene-project.test.d.ts.map +1 -0
  36. package/dist/commands/_shared/scene-project.test.js +203 -0
  37. package/dist/commands/_shared/scene-project.test.js.map +1 -0
  38. package/dist/commands/_shared/scene-render.d.ts +79 -0
  39. package/dist/commands/_shared/scene-render.d.ts.map +1 -0
  40. package/dist/commands/_shared/scene-render.js +138 -0
  41. package/dist/commands/_shared/scene-render.js.map +1 -0
  42. package/dist/commands/_shared/scene-render.test.d.ts +2 -0
  43. package/dist/commands/_shared/scene-render.test.d.ts.map +1 -0
  44. package/dist/commands/_shared/scene-render.test.js +145 -0
  45. package/dist/commands/_shared/scene-render.test.js.map +1 -0
  46. package/dist/commands/_shared/segments-to-scenes.d.ts +85 -0
  47. package/dist/commands/_shared/segments-to-scenes.d.ts.map +1 -0
  48. package/dist/commands/_shared/segments-to-scenes.js +202 -0
  49. package/dist/commands/_shared/segments-to-scenes.js.map +1 -0
  50. package/dist/commands/_shared/segments-to-scenes.test.d.ts +2 -0
  51. package/dist/commands/_shared/segments-to-scenes.test.d.ts.map +1 -0
  52. package/dist/commands/_shared/segments-to-scenes.test.js +219 -0
  53. package/dist/commands/_shared/segments-to-scenes.test.js.map +1 -0
  54. package/dist/commands/_shared/tts-resolve.d.ts +75 -0
  55. package/dist/commands/_shared/tts-resolve.d.ts.map +1 -0
  56. package/dist/commands/_shared/tts-resolve.js +89 -0
  57. package/dist/commands/_shared/tts-resolve.js.map +1 -0
  58. package/dist/commands/_shared/tts-resolve.test.d.ts +6 -0
  59. package/dist/commands/_shared/tts-resolve.test.d.ts.map +1 -0
  60. package/dist/commands/_shared/tts-resolve.test.js +147 -0
  61. package/dist/commands/_shared/tts-resolve.test.js.map +1 -0
  62. package/dist/commands/ai-script-pipeline-cli.d.ts.map +1 -1
  63. package/dist/commands/ai-script-pipeline-cli.js +49 -0
  64. package/dist/commands/ai-script-pipeline-cli.js.map +1 -1
  65. package/dist/commands/ai-script-pipeline.d.ts +16 -0
  66. package/dist/commands/ai-script-pipeline.d.ts.map +1 -1
  67. package/dist/commands/ai-script-pipeline.js +28 -0
  68. package/dist/commands/ai-script-pipeline.js.map +1 -1
  69. package/dist/commands/ai.test.js +4 -1
  70. package/dist/commands/ai.test.js.map +1 -1
  71. package/dist/commands/scene.d.ts +86 -0
  72. package/dist/commands/scene.d.ts.map +1 -0
  73. package/dist/commands/scene.js +719 -0
  74. package/dist/commands/scene.js.map +1 -0
  75. package/dist/commands/scene.test.d.ts +2 -0
  76. package/dist/commands/scene.test.d.ts.map +1 -0
  77. package/dist/commands/scene.test.js +219 -0
  78. package/dist/commands/scene.test.js.map +1 -0
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +2 -0
  81. package/dist/index.js.map +1 -1
  82. package/dist/pipeline/renderers/chrome.d.ts +26 -0
  83. package/dist/pipeline/renderers/chrome.d.ts.map +1 -0
  84. package/dist/pipeline/renderers/chrome.js +48 -0
  85. package/dist/pipeline/renderers/chrome.js.map +1 -0
  86. package/dist/pipeline/renderers/hyperframes.d.ts.map +1 -1
  87. package/dist/pipeline/renderers/hyperframes.js +2 -32
  88. package/dist/pipeline/renderers/hyperframes.js.map +1 -1
  89. package/dist/utils/provider-resolver.d.ts.map +1 -1
  90. package/dist/utils/provider-resolver.js +6 -2
  91. package/dist/utils/provider-resolver.js.map +1 -1
  92. package/package.json +32 -13
@@ -0,0 +1,281 @@
1
+ /**
2
+ * @module _shared/scene-project
3
+ *
4
+ * Helpers for scaffolding a "scene project" — a directory that works as both
5
+ * a VibeFrame project (via `vibe.project.yaml`) AND a HeyGen Hyperframes
6
+ * project (via `hyperframes.json` + `meta.json` + `index.html`). Either
7
+ * toolchain can be run inside the directory.
8
+ *
9
+ * Pure functions; no I/O beyond `scaffoldSceneProject()` which orchestrates
10
+ * file writes. Everything else returns strings or JSON-serializable objects
11
+ * so it can be unit-tested without touching the filesystem.
12
+ */
13
+ import { mkdir, readFile, writeFile, access } from "node:fs/promises";
14
+ import { resolve, basename } from "node:path";
15
+ import { stringify as yamlStringify } from "yaml";
16
+ const ASPECT_DIMS = {
17
+ "16:9": { width: 1920, height: 1080 },
18
+ "9:16": { width: 1080, height: 1920 },
19
+ "1:1": { width: 1080, height: 1080 },
20
+ "4:5": { width: 1080, height: 1350 },
21
+ };
22
+ export function aspectToDims(aspect) {
23
+ return ASPECT_DIMS[aspect];
24
+ }
25
+ /** Defaults for a fresh scene project. */
26
+ export function defaultVibeProjectConfig(name) {
27
+ return {
28
+ name,
29
+ aspect: "16:9",
30
+ defaultSceneDuration: 5,
31
+ providers: { image: null, tts: null, transcribe: null },
32
+ budget: { maxUsd: 0 },
33
+ };
34
+ }
35
+ /** The Hyperframes config we write on init. Matches the format at
36
+ * hyperframe-learn/my-first-video/hyperframes.json.
37
+ */
38
+ export function buildHyperframesConfig() {
39
+ return {
40
+ $schema: "https://hyperframes.heygen.com/schema/hyperframes.json",
41
+ registry: "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry",
42
+ paths: {
43
+ blocks: "compositions",
44
+ components: "compositions/components",
45
+ assets: "assets",
46
+ },
47
+ };
48
+ }
49
+ export function buildHyperframesMeta(name, now = new Date()) {
50
+ return { id: name, name, createdAt: now.toISOString() };
51
+ }
52
+ /**
53
+ * Merge an existing Hyperframes config with our defaults, preserving any
54
+ * user-authored keys and nested values. `vibe scene init` is idempotent:
55
+ * running it on a directory that already has `hyperframes.json` must never
56
+ * lose user config.
57
+ */
58
+ export function mergeHyperframesConfig(existing, defaults) {
59
+ const out = { ...defaults, ...existing };
60
+ // Preserve nested `paths` by shallow-merging.
61
+ if (existing.paths || defaults.paths) {
62
+ out.paths = { ...(defaults.paths ?? {}), ...(existing.paths ?? {}) };
63
+ }
64
+ return out;
65
+ }
66
+ /**
67
+ * Minimal valid Hyperframes root composition — empty (no sub-compositions
68
+ * yet). A later `vibe scene add` inserts `<div class="clip" ...>` children.
69
+ */
70
+ export function buildEmptyRootHtml(opts) {
71
+ const { width, height } = ASPECT_DIMS[opts.aspect];
72
+ return `<!doctype html>
73
+ <html lang="en">
74
+ <head>
75
+ <meta charset="UTF-8" />
76
+ <meta name="viewport" content="width=${width}, height=${height}" />
77
+ <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
78
+ <style>
79
+ * { margin: 0; padding: 0; box-sizing: border-box; }
80
+ html, body {
81
+ margin: 0;
82
+ width: ${width}px;
83
+ height: ${height}px;
84
+ overflow: hidden;
85
+ background: #000;
86
+ }
87
+ </style>
88
+ </head>
89
+ <body>
90
+ <div
91
+ id="root"
92
+ data-composition-id="main"
93
+ data-start="0"
94
+ data-duration="${opts.duration}"
95
+ data-width="${width}"
96
+ data-height="${height}"
97
+ >
98
+ <!-- Scenes added via \`vibe scene add\` are inserted here. -->
99
+ <!-- Each scene reference: data-composition-id, data-composition-src, data-start, data-duration, data-track-index. -->
100
+ <!-- See compositions/*.html for sub-composition contents. -->
101
+
102
+ </div>
103
+
104
+ <script>
105
+ window.__timelines = window.__timelines || {};
106
+ window.__timelines["main"] = gsap.timeline({ paused: true });
107
+ </script>
108
+ </body>
109
+ </html>
110
+ `;
111
+ }
112
+ /** Project-local CLAUDE.md that orients an AI agent to both toolchains. */
113
+ export function buildProjectClaudeMd(name) {
114
+ return `# ${name} — Scene Authoring Project
115
+
116
+ This project is **bilingual**: it works with both VibeFrame (\`vibe\`) and
117
+ HeyGen Hyperframes (\`hyperframes\`). You can run either CLI inside this
118
+ directory.
119
+
120
+ ## Skills — USE THESE FIRST
121
+
122
+ **Always invoke the relevant skill before authoring scenes.** Skills encode
123
+ framework-specific patterns (GSAP timeline registration, data-attribute
124
+ semantics, VibeFrame pipeline conventions) that are NOT in generic web docs.
125
+
126
+ | Skill | Command | When to use |
127
+ | ----------------- | ---------------- | ------------------------------------------------------------------------------------- |
128
+ | **vibe-scene** | \`/vibe-scene\` | Authoring / editing per-scene HTML in this project — preferred |
129
+ | **hyperframes** | \`/hyperframes\` | Fallback: direct HF authoring (install via \`npx skills add heygen-com/hyperframes\`) |
130
+ | **gsap** | \`/gsap\` | GSAP tweens, timelines, easing |
131
+
132
+ If the skill is not available, follow the **Key Rules** below.
133
+
134
+ ## Project structure
135
+
136
+ - \`index.html\` — root composition (timeline)
137
+ - \`compositions/scene-*.html\` — per-scene HTML authored by you or the agent
138
+ - \`assets/\` — shared media (narration audio, images, video)
139
+ - \`transcript.json\` — Whisper word-level transcript (if narration exists)
140
+ - \`hyperframes.json\` — HF registry config (speak to both toolchains)
141
+ - \`vibe.project.yaml\` — VibeFrame config (providers, budget)
142
+ - \`renders/\` — output MP4s
143
+
144
+ ## Commands
145
+
146
+ \`\`\`bash
147
+ vibe scene add <name> --narration "..." --visuals "..." # Author a new scene via AI
148
+ vibe scene lint # Validate scenes (in-process HF linter)
149
+ vibe scene render # Render to MP4
150
+
151
+ # Hyperframes CLI (if installed — works in this project too)
152
+ npx hyperframes preview
153
+ npx hyperframes render
154
+ \`\`\`
155
+
156
+ ## Key Rules (for hand-authored scene HTML)
157
+
158
+ 1. Every timed element needs \`data-start\`, \`data-duration\`, and \`data-track-index\`.
159
+ 2. Elements with timing **MUST** have \`class="clip"\` — the framework uses this for visibility control.
160
+ 3. Timelines must be paused and registered on \`window.__timelines\`:
161
+ \`\`\`js
162
+ window.__timelines = window.__timelines || {};
163
+ window.__timelines["composition-id"] = gsap.timeline({ paused: true });
164
+ \`\`\`
165
+ 4. Videos use \`muted\` with a separate \`<audio>\` element for the audio track.
166
+ 5. Sub-compositions use \`data-composition-src="compositions/file.html"\`.
167
+ 6. Only deterministic logic — no \`Date.now()\`, \`Math.random()\`, or network fetches.
168
+
169
+ ## Linting — run after changes
170
+
171
+ \`\`\`bash
172
+ vibe scene lint # preferred — in-process, no network
173
+ vibe scene lint --fix # auto-fix mechanical issues
174
+ vibe scene lint --json # structured output for agent loops
175
+ \`\`\`
176
+ `;
177
+ }
178
+ /** Minimal .gitignore for a scene project. */
179
+ export function buildSceneGitignore() {
180
+ return `# VibeFrame caches
181
+ .vibeframe/cache/
182
+ .vibeframe/checkpoints/
183
+
184
+ # Render outputs
185
+ renders/*.mp4
186
+ tmp/
187
+
188
+ # OS / editor
189
+ .DS_Store
190
+ *.log
191
+ `;
192
+ }
193
+ async function pathExists(p) {
194
+ try {
195
+ await access(p);
196
+ return true;
197
+ }
198
+ catch {
199
+ return false;
200
+ }
201
+ }
202
+ /**
203
+ * Scaffold (or update) a scene project at `dir`. Idempotent: running twice is
204
+ * a no-op; running on an existing Hyperframes project merges `hyperframes.json`
205
+ * instead of overwriting.
206
+ */
207
+ export async function scaffoldSceneProject(opts) {
208
+ const dir = resolve(opts.dir);
209
+ const name = opts.name ?? basename(dir);
210
+ const aspect = opts.aspect ?? "16:9";
211
+ const duration = opts.duration ?? 10;
212
+ const now = opts.now ?? new Date();
213
+ await mkdir(dir, { recursive: true });
214
+ await mkdir(resolve(dir, "compositions"), { recursive: true });
215
+ await mkdir(resolve(dir, "assets"), { recursive: true });
216
+ const created = [];
217
+ const skipped = [];
218
+ const merged = [];
219
+ // hyperframes.json — merge if exists, else create.
220
+ const hfPath = resolve(dir, "hyperframes.json");
221
+ const hfDefaults = buildHyperframesConfig();
222
+ if (await pathExists(hfPath)) {
223
+ const existingRaw = await readFile(hfPath, "utf-8");
224
+ const existing = JSON.parse(existingRaw);
225
+ const mergedConfig = mergeHyperframesConfig(existing, hfDefaults);
226
+ await writeFile(hfPath, JSON.stringify(mergedConfig, null, 2) + "\n", "utf-8");
227
+ merged.push(hfPath);
228
+ }
229
+ else {
230
+ await writeFile(hfPath, JSON.stringify(hfDefaults, null, 2) + "\n", "utf-8");
231
+ created.push(hfPath);
232
+ }
233
+ // meta.json — preserve existing (id shouldn't change).
234
+ const metaPath = resolve(dir, "meta.json");
235
+ if (await pathExists(metaPath)) {
236
+ skipped.push(metaPath);
237
+ }
238
+ else {
239
+ await writeFile(metaPath, JSON.stringify(buildHyperframesMeta(name, now), null, 2) + "\n", "utf-8");
240
+ created.push(metaPath);
241
+ }
242
+ // index.html — preserve existing (user may have edited root).
243
+ const rootPath = resolve(dir, "index.html");
244
+ if (await pathExists(rootPath)) {
245
+ skipped.push(rootPath);
246
+ }
247
+ else {
248
+ await writeFile(rootPath, buildEmptyRootHtml({ aspect, duration }), "utf-8");
249
+ created.push(rootPath);
250
+ }
251
+ // vibe.project.yaml — preserve existing; this is VibeFrame's own config.
252
+ const vibePath = resolve(dir, "vibe.project.yaml");
253
+ if (await pathExists(vibePath)) {
254
+ skipped.push(vibePath);
255
+ }
256
+ else {
257
+ const cfg = { ...defaultVibeProjectConfig(name), aspect };
258
+ await writeFile(vibePath, yamlStringify(cfg), "utf-8");
259
+ created.push(vibePath);
260
+ }
261
+ // CLAUDE.md — preserve existing.
262
+ const claudePath = resolve(dir, "CLAUDE.md");
263
+ if (await pathExists(claudePath)) {
264
+ skipped.push(claudePath);
265
+ }
266
+ else {
267
+ await writeFile(claudePath, buildProjectClaudeMd(name), "utf-8");
268
+ created.push(claudePath);
269
+ }
270
+ // .gitignore — preserve existing.
271
+ const gitignorePath = resolve(dir, ".gitignore");
272
+ if (await pathExists(gitignorePath)) {
273
+ skipped.push(gitignorePath);
274
+ }
275
+ else {
276
+ await writeFile(gitignorePath, buildSceneGitignore(), "utf-8");
277
+ created.push(gitignorePath);
278
+ }
279
+ return { created, skipped, merged };
280
+ }
281
+ //# sourceMappingURL=scene-project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-project.js","sourceRoot":"","sources":["../../../src/commands/_shared/scene-project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scene-project.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-project.test.d.ts","sourceRoot":"","sources":["../../../src/commands/_shared/scene-project.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,203 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { mkdtemp, readFile, writeFile, mkdir, access } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join, resolve } from "node:path";
5
+ import { parse as yamlParse } from "yaml";
6
+ import { aspectToDims, buildEmptyRootHtml, buildHyperframesConfig, buildHyperframesMeta, buildProjectClaudeMd, buildSceneGitignore, defaultVibeProjectConfig, mergeHyperframesConfig, scaffoldSceneProject, } from "./scene-project.js";
7
+ async function makeTmp(label = "vibe-scene-test-") {
8
+ return mkdtemp(join(tmpdir(), label));
9
+ }
10
+ async function pathExists(p) {
11
+ try {
12
+ await access(p);
13
+ return true;
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ }
19
+ describe("aspectToDims", () => {
20
+ it.each([
21
+ ["16:9", 1920, 1080],
22
+ ["9:16", 1080, 1920],
23
+ ["1:1", 1080, 1080],
24
+ ["4:5", 1080, 1350],
25
+ ])("%s → %dx%d", (ratio, w, h) => {
26
+ expect(aspectToDims(ratio)).toEqual({ width: w, height: h });
27
+ });
28
+ });
29
+ describe("buildHyperframesConfig", () => {
30
+ it("produces the shape expected by hyperframes CLI", () => {
31
+ const cfg = buildHyperframesConfig();
32
+ expect(cfg.$schema).toContain("hyperframes.json");
33
+ expect(cfg.paths).toEqual({
34
+ blocks: "compositions",
35
+ components: "compositions/components",
36
+ assets: "assets",
37
+ });
38
+ });
39
+ });
40
+ describe("buildHyperframesMeta", () => {
41
+ it("uses project name as both id and name", () => {
42
+ const meta = buildHyperframesMeta("my-video", new Date("2026-04-25T00:00:00Z"));
43
+ expect(meta).toEqual({
44
+ id: "my-video",
45
+ name: "my-video",
46
+ createdAt: "2026-04-25T00:00:00.000Z",
47
+ });
48
+ });
49
+ });
50
+ describe("mergeHyperframesConfig", () => {
51
+ it("preserves existing top-level keys", () => {
52
+ const existing = {
53
+ $schema: "custom-schema",
54
+ registry: "https://my.registry/",
55
+ customField: "user-value",
56
+ };
57
+ const merged = mergeHyperframesConfig(existing, buildHyperframesConfig());
58
+ expect(merged.$schema).toBe("custom-schema");
59
+ expect(merged.registry).toBe("https://my.registry/");
60
+ expect(merged.customField).toBe("user-value");
61
+ });
62
+ it("shallow-merges nested paths preserving user keys", () => {
63
+ const existing = {
64
+ paths: { blocks: "custom-blocks", assets: "media" },
65
+ };
66
+ const merged = mergeHyperframesConfig(existing, buildHyperframesConfig());
67
+ expect(merged.paths).toEqual({
68
+ blocks: "custom-blocks", // preserved
69
+ components: "compositions/components", // from defaults
70
+ assets: "media", // preserved
71
+ });
72
+ });
73
+ it("falls back to defaults when existing is empty", () => {
74
+ const merged = mergeHyperframesConfig({}, buildHyperframesConfig());
75
+ expect(merged).toEqual(buildHyperframesConfig());
76
+ });
77
+ });
78
+ describe("buildEmptyRootHtml", () => {
79
+ it("embeds aspect-correct canvas dimensions", () => {
80
+ const html = buildEmptyRootHtml({ aspect: "9:16", duration: 12 });
81
+ expect(html).toContain("width: 1080px");
82
+ expect(html).toContain("height: 1920px");
83
+ expect(html).toContain('data-duration="12"');
84
+ expect(html).toContain('data-composition-id="main"');
85
+ });
86
+ it("registers a paused main timeline", () => {
87
+ const html = buildEmptyRootHtml({ aspect: "16:9", duration: 5 });
88
+ expect(html).toContain('window.__timelines["main"] = gsap.timeline({ paused: true });');
89
+ });
90
+ });
91
+ describe("buildProjectClaudeMd", () => {
92
+ it("names the project and references both toolchains", () => {
93
+ const md = buildProjectClaudeMd("my-promo");
94
+ expect(md).toContain("# my-promo");
95
+ expect(md).toContain("/vibe-scene");
96
+ expect(md).toContain("/hyperframes");
97
+ expect(md).toContain("vibe scene add");
98
+ expect(md).toContain("npx hyperframes");
99
+ });
100
+ });
101
+ describe("buildSceneGitignore", () => {
102
+ it("excludes caches and rendered outputs", () => {
103
+ const out = buildSceneGitignore();
104
+ expect(out).toContain(".vibeframe/cache/");
105
+ expect(out).toContain("renders/*.mp4");
106
+ });
107
+ });
108
+ describe("defaultVibeProjectConfig", () => {
109
+ it("produces sane defaults", () => {
110
+ const cfg = defaultVibeProjectConfig("promo");
111
+ expect(cfg.name).toBe("promo");
112
+ expect(cfg.aspect).toBe("16:9");
113
+ expect(cfg.defaultSceneDuration).toBe(5);
114
+ expect(cfg.providers).toEqual({ image: null, tts: null, transcribe: null });
115
+ expect(cfg.budget.maxUsd).toBe(0);
116
+ });
117
+ });
118
+ describe("scaffoldSceneProject", () => {
119
+ it("creates all expected files in an empty directory", async () => {
120
+ const dir = await makeTmp();
121
+ const result = await scaffoldSceneProject({
122
+ dir,
123
+ name: "fixture",
124
+ aspect: "16:9",
125
+ duration: 8,
126
+ now: new Date("2026-04-25T00:00:00Z"),
127
+ });
128
+ const expected = [
129
+ "hyperframes.json",
130
+ "meta.json",
131
+ "index.html",
132
+ "vibe.project.yaml",
133
+ "CLAUDE.md",
134
+ ".gitignore",
135
+ ];
136
+ for (const f of expected) {
137
+ expect(await pathExists(resolve(dir, f))).toBe(true);
138
+ }
139
+ expect(await pathExists(resolve(dir, "compositions"))).toBe(true);
140
+ expect(await pathExists(resolve(dir, "assets"))).toBe(true);
141
+ expect(result.created.length).toBe(expected.length);
142
+ expect(result.merged).toEqual([]);
143
+ expect(result.skipped).toEqual([]);
144
+ });
145
+ it("vibe.project.yaml parses as valid YAML and carries the chosen aspect", async () => {
146
+ const dir = await makeTmp();
147
+ await scaffoldSceneProject({ dir, name: "fixture", aspect: "9:16", duration: 10 });
148
+ const raw = await readFile(resolve(dir, "vibe.project.yaml"), "utf-8");
149
+ const parsed = yamlParse(raw);
150
+ expect(parsed).toMatchObject({
151
+ name: "fixture",
152
+ aspect: "9:16",
153
+ defaultSceneDuration: 5,
154
+ providers: { image: null, tts: null, transcribe: null },
155
+ });
156
+ });
157
+ it("is idempotent: running twice is a no-op (no overwrites of user-editable files)", async () => {
158
+ const dir = await makeTmp();
159
+ const first = await scaffoldSceneProject({ dir, name: "fixture" });
160
+ const claudeBefore = await readFile(resolve(dir, "CLAUDE.md"), "utf-8");
161
+ // User edits CLAUDE.md
162
+ await writeFile(resolve(dir, "CLAUDE.md"), claudeBefore + "\n\n## My notes\n\nHand-written.\n", "utf-8");
163
+ const second = await scaffoldSceneProject({ dir, name: "fixture" });
164
+ const claudeAfter = await readFile(resolve(dir, "CLAUDE.md"), "utf-8");
165
+ // CLAUDE.md edit survives.
166
+ expect(claudeAfter).toContain("Hand-written.");
167
+ // Second run reports every non-hyperframes.json file as skipped.
168
+ expect(second.created).toEqual([]);
169
+ expect(second.skipped.length).toBeGreaterThan(0);
170
+ // hyperframes.json is always merge-updated (idempotent shape).
171
+ expect(second.merged.map((p) => p.split("/").pop())).toContain("hyperframes.json");
172
+ // First run created hyperframes.json; second merged it.
173
+ expect(first.created.map((p) => p.split("/").pop())).toContain("hyperframes.json");
174
+ });
175
+ it("preserves user keys in existing hyperframes.json (merge, not overwrite)", async () => {
176
+ const dir = await makeTmp();
177
+ await mkdir(dir, { recursive: true });
178
+ // Pre-seed a Hyperframes project with custom paths + user field.
179
+ const preExisting = {
180
+ $schema: "https://hyperframes.heygen.com/schema/hyperframes.json",
181
+ paths: { blocks: "custom-blocks", assets: "media" },
182
+ userField: "kept",
183
+ };
184
+ await writeFile(resolve(dir, "hyperframes.json"), JSON.stringify(preExisting, null, 2) + "\n", "utf-8");
185
+ const result = await scaffoldSceneProject({ dir, name: "fixture" });
186
+ const merged = JSON.parse(await readFile(resolve(dir, "hyperframes.json"), "utf-8"));
187
+ expect(merged.userField).toBe("kept");
188
+ expect(merged.paths.blocks).toBe("custom-blocks");
189
+ expect(merged.paths.assets).toBe("media");
190
+ // Default key backfilled:
191
+ expect(merged.paths.components).toBe("compositions/components");
192
+ expect(result.merged.some((p) => p.endsWith("hyperframes.json"))).toBe(true);
193
+ });
194
+ it("creates index.html with the correct aspect", async () => {
195
+ const dir = await makeTmp();
196
+ await scaffoldSceneProject({ dir, name: "fixture", aspect: "1:1", duration: 6 });
197
+ const html = await readFile(resolve(dir, "index.html"), "utf-8");
198
+ expect(html).toContain("width: 1080px");
199
+ expect(html).toContain("height: 1080px");
200
+ expect(html).toContain('data-duration="6"');
201
+ });
202
+ });
203
+ //# sourceMappingURL=scene-project.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-project.test.js","sourceRoot":"","sources":["../../../src/commands/_shared/scene-project.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * @module _shared/scene-render
3
+ *
4
+ * Render a VibeFrame scene project to MP4/WebM/MOV using the Hyperframes
5
+ * producer directly. Unlike the FFmpeg-bridge backend in
6
+ * `pipeline/renderers/hyperframes.ts` which has to convert a `TimelineState`
7
+ * into a temp Hyperframes project, scene projects already ARE Hyperframes
8
+ * projects — so we hand the producer the user's project dir and entry file
9
+ * verbatim.
10
+ *
11
+ * `executeSceneRender()` is decoupled from CLI flags so that the C6 agent
12
+ * tool and the C5 `--format scenes` pipeline can call it the same way. It
13
+ * returns a structured result instead of throwing or exiting.
14
+ */
15
+ import { type RenderConfig } from "@hyperframes/producer";
16
+ export type RenderFps = 24 | 30 | 60;
17
+ export type RenderQuality = "draft" | "standard" | "high";
18
+ export type RenderFormat = "mp4" | "webm" | "mov";
19
+ export interface SceneRenderOptions {
20
+ /** Project directory (defaults to cwd). */
21
+ projectDir?: string;
22
+ /** Root composition file relative to projectDir (default: "index.html"). */
23
+ root?: string;
24
+ /** Output file. When relative, resolved against projectDir. Default:
25
+ * `renders/<projectName>-<isoStamp>.<format>`. */
26
+ output?: string;
27
+ fps?: RenderFps;
28
+ quality?: RenderQuality;
29
+ format?: RenderFormat;
30
+ /** Hyperframes capture worker count. Default 1 (the existing backend's
31
+ * default — auto-worker mode times out on small comps). */
32
+ workers?: number;
33
+ signal?: AbortSignal;
34
+ onProgress?: (pct: number, stage: string) => void;
35
+ }
36
+ export interface SceneRenderResult {
37
+ success: boolean;
38
+ outputPath?: string;
39
+ durationMs?: number;
40
+ framesRendered?: number;
41
+ totalFrames?: number;
42
+ fps?: RenderFps;
43
+ quality?: RenderQuality;
44
+ format?: RenderFormat;
45
+ error?: string;
46
+ }
47
+ /** Map a quality preset to an x264 CRF (lower = higher quality). */
48
+ export declare function qualityToCrf(quality?: RenderQuality): number;
49
+ /**
50
+ * Compute the default output path for a render. Pure — does no I/O. Returns
51
+ * an absolute path under `<projectDir>/renders/`.
52
+ *
53
+ * `now` is injectable so tests get deterministic output.
54
+ */
55
+ export declare function defaultOutputPath(opts: {
56
+ projectDir: string;
57
+ projectName?: string;
58
+ format?: RenderFormat;
59
+ now?: Date;
60
+ }): string;
61
+ /**
62
+ * Build the producer's `RenderConfig` from caller options. Pure — useful for
63
+ * unit tests that want to assert defaults without touching Chrome.
64
+ */
65
+ export declare function buildRenderConfig(opts: {
66
+ fps?: RenderFps;
67
+ quality?: RenderQuality;
68
+ format?: RenderFormat;
69
+ workers?: number;
70
+ entryFile?: string;
71
+ }): RenderConfig;
72
+ /**
73
+ * Render a scene project. Mirrors the contract of the existing Hyperframes
74
+ * backend (preflight → execute → structured result), but skips the
75
+ * `TimelineState` → temp project conversion because the user's project is
76
+ * already a valid Hyperframes project.
77
+ */
78
+ export declare function executeSceneRender(opts?: SceneRenderOptions): Promise<SceneRenderResult>;
79
+ //# sourceMappingURL=scene-render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-render.d.ts","sourceRoot":"","sources":["../../../src/commands/_shared/scene-render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAI/B,MAAM,MAAM,SAAS,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACrC,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;AAC1D,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;AAElD,MAAM,WAAW,kBAAkB;IACjC,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;uDACmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;gEAC4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,OAAO,GAAE,aAA0B,GAAG,MAAM,CAExE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ,GAAG,MAAM,CAST;AAmBD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,YAAY,CAUf;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA+DlG"}