@vibeframe/cli 0.52.1 → 0.53.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 (81) 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 +85 -0
  15. package/dist/commands/_shared/scene-html-emit.d.ts.map +1 -0
  16. package/dist/commands/_shared/scene-html-emit.js +353 -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 +203 -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/ai-script-pipeline-cli.d.ts.map +1 -1
  55. package/dist/commands/ai-script-pipeline-cli.js +49 -0
  56. package/dist/commands/ai-script-pipeline-cli.js.map +1 -1
  57. package/dist/commands/ai-script-pipeline.d.ts +16 -0
  58. package/dist/commands/ai-script-pipeline.d.ts.map +1 -1
  59. package/dist/commands/ai-script-pipeline.js +28 -0
  60. package/dist/commands/ai-script-pipeline.js.map +1 -1
  61. package/dist/commands/ai.test.js +4 -1
  62. package/dist/commands/ai.test.js.map +1 -1
  63. package/dist/commands/scene.d.ts +60 -0
  64. package/dist/commands/scene.d.ts.map +1 -0
  65. package/dist/commands/scene.js +621 -0
  66. package/dist/commands/scene.js.map +1 -0
  67. package/dist/commands/scene.test.d.ts +2 -0
  68. package/dist/commands/scene.test.d.ts.map +1 -0
  69. package/dist/commands/scene.test.js +133 -0
  70. package/dist/commands/scene.test.js.map +1 -0
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +2 -0
  73. package/dist/index.js.map +1 -1
  74. package/dist/pipeline/renderers/chrome.d.ts +26 -0
  75. package/dist/pipeline/renderers/chrome.d.ts.map +1 -0
  76. package/dist/pipeline/renderers/chrome.js +48 -0
  77. package/dist/pipeline/renderers/chrome.js.map +1 -0
  78. package/dist/pipeline/renderers/hyperframes.d.ts.map +1 -1
  79. package/dist/pipeline/renderers/hyperframes.js +2 -32
  80. package/dist/pipeline/renderers/hyperframes.js.map +1 -1
  81. package/package.json +32 -13
@@ -0,0 +1,110 @@
1
+ /**
2
+ * @module _shared/scene-lint
3
+ *
4
+ * In-process Hyperframes lint pipeline for VibeFrame scene projects. Wraps
5
+ * `runHyperframeLint` from `@hyperframes/producer` (its public lint entry
6
+ * point) and adapts the output to VibeFrame's two-tier file structure:
7
+ *
8
+ * project/
9
+ * index.html # root composition → linted as-is
10
+ * compositions/*.html # sub-compositions → producer's lint emits two
11
+ * spurious findings here, so
12
+ * we filter them out.
13
+ *
14
+ * Two findings are filtered for sub-compositions because they encode the
15
+ * inverse rule (sub-comps SHOULD be wrapped in `<template>` and SHOULD NOT
16
+ * have an `<html>` wrapper):
17
+ * - `standalone_composition_wrapped_in_template`
18
+ * - `root_composition_missing_html_wrapper`
19
+ *
20
+ * Pure helpers are exported for unit testing; `runProjectLint` does the
21
+ * filesystem walk + lint orchestration. Mechanical `--fix` is intentionally
22
+ * conservative — only `timed_element_missing_clip_class` is auto-fixed in
23
+ * MVP 1.
24
+ */
25
+ export type LintSeverity = "error" | "warning" | "info";
26
+ export interface LintFinding {
27
+ code: string;
28
+ severity: LintSeverity;
29
+ message: string;
30
+ file?: string;
31
+ selector?: string;
32
+ elementId?: string;
33
+ fixHint?: string;
34
+ snippet?: string;
35
+ }
36
+ export interface FileLintResult {
37
+ /** Path relative to the project directory. */
38
+ file: string;
39
+ /** True for files under `compositions/` — used to filter false positives. */
40
+ isSubComposition: boolean;
41
+ findings: LintFinding[];
42
+ }
43
+ export interface FileFixResult {
44
+ file: string;
45
+ /** Codes that were mechanically fixed. */
46
+ codes: string[];
47
+ }
48
+ export interface ProjectLintResult {
49
+ ok: boolean;
50
+ errorCount: number;
51
+ warningCount: number;
52
+ infoCount: number;
53
+ files: FileLintResult[];
54
+ fixed: FileFixResult[];
55
+ }
56
+ /** Producer's two findings that are inverse-rules for sub-compositions. */
57
+ export declare const SUB_COMP_FALSE_POSITIVES: ReadonlySet<string>;
58
+ /**
59
+ * Discover scene files in a project. Returns the root composition (or null
60
+ * if missing) plus every `*.html` file under `compositions/` recursively.
61
+ *
62
+ * Paths returned are absolute. The caller derives project-relative paths via
63
+ * `relative(projectDir, path)`.
64
+ */
65
+ export declare function discoverSceneFiles(opts: {
66
+ projectDir: string;
67
+ rootRel?: string;
68
+ }): Promise<{
69
+ root: string | null;
70
+ subs: string[];
71
+ }>;
72
+ /**
73
+ * Drop findings that producer's lint emits as inverse-rules for sub-comp
74
+ * files. Other findings pass through unchanged.
75
+ */
76
+ export declare function filterSubCompFalsePositives(findings: LintFinding[], isSubComposition: boolean): LintFinding[];
77
+ /**
78
+ * Apply mechanical `--fix` rewrites for known-safe finding codes. Returns the
79
+ * (possibly unchanged) HTML and the list of codes that were fixed at least
80
+ * once.
81
+ *
82
+ * MVP 1 only auto-fixes `timed_element_missing_clip_class` — adding the
83
+ * `clip` class to elements that already carry timing attributes.
84
+ *
85
+ * Other "registration"/"paused" fixes are intentionally NOT mechanical —
86
+ * they require structural reasoning (where to insert the `window.__timelines`
87
+ * block, how to convert an unpaused timeline) that goes beyond a regex.
88
+ * The lint output still surfaces them with `fixHint` for the agent or human
89
+ * to apply.
90
+ */
91
+ export declare function applyMechanicalFixes(html: string, findings: LintFinding[]): {
92
+ html: string;
93
+ fixedCodes: string[];
94
+ };
95
+ export interface RunProjectLintOptions {
96
+ projectDir: string;
97
+ rootRel?: string;
98
+ /** Apply mechanical fixes and write files back. */
99
+ fix?: boolean;
100
+ }
101
+ /**
102
+ * Walk the project, lint every HTML file, optionally apply mechanical fixes,
103
+ * and return aggregated counts. Throws only on FS errors; lint errors are
104
+ * surfaced as findings, not exceptions.
105
+ */
106
+ export declare function runProjectLint(opts: RunProjectLintOptions): Promise<ProjectLintResult>;
107
+ /** True if `root` exists and is a regular file. Used by callers that want to
108
+ * pre-validate before invoking `runProjectLint`. */
109
+ export declare function rootExists(projectDir: string, rootRel?: string): Promise<boolean>;
110
+ //# sourceMappingURL=scene-lint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-lint.d.ts","sourceRoot":"","sources":["../../../src/commands/_shared/scene-lint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAeH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAMD,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAGvD,CAAC;AAEH;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAanD;AAcD;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,WAAW,EAAE,EACvB,gBAAgB,EAAE,OAAO,GACxB,WAAW,EAAE,CAGf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,WAAW,EAAE,GACtB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAaxC;AA+BD,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA6D5F;AAED;qDACqD;AACrD,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,SAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ7F"}
@@ -0,0 +1,207 @@
1
+ /**
2
+ * @module _shared/scene-lint
3
+ *
4
+ * In-process Hyperframes lint pipeline for VibeFrame scene projects. Wraps
5
+ * `runHyperframeLint` from `@hyperframes/producer` (its public lint entry
6
+ * point) and adapts the output to VibeFrame's two-tier file structure:
7
+ *
8
+ * project/
9
+ * index.html # root composition → linted as-is
10
+ * compositions/*.html # sub-compositions → producer's lint emits two
11
+ * spurious findings here, so
12
+ * we filter them out.
13
+ *
14
+ * Two findings are filtered for sub-compositions because they encode the
15
+ * inverse rule (sub-comps SHOULD be wrapped in `<template>` and SHOULD NOT
16
+ * have an `<html>` wrapper):
17
+ * - `standalone_composition_wrapped_in_template`
18
+ * - `root_composition_missing_html_wrapper`
19
+ *
20
+ * Pure helpers are exported for unit testing; `runProjectLint` does the
21
+ * filesystem walk + lint orchestration. Mechanical `--fix` is intentionally
22
+ * conservative — only `timed_element_missing_clip_class` is auto-fixed in
23
+ * MVP 1.
24
+ */
25
+ import { readdir, readFile, writeFile, stat } from "node:fs/promises";
26
+ import { existsSync } from "node:fs";
27
+ import { resolve, relative, join } from "node:path";
28
+ import { runHyperframeLint, } from "@hyperframes/producer";
29
+ // ---------------------------------------------------------------------------
30
+ // Pure helpers
31
+ // ---------------------------------------------------------------------------
32
+ /** Producer's two findings that are inverse-rules for sub-compositions. */
33
+ export const SUB_COMP_FALSE_POSITIVES = new Set([
34
+ "standalone_composition_wrapped_in_template",
35
+ "root_composition_missing_html_wrapper",
36
+ ]);
37
+ /**
38
+ * Discover scene files in a project. Returns the root composition (or null
39
+ * if missing) plus every `*.html` file under `compositions/` recursively.
40
+ *
41
+ * Paths returned are absolute. The caller derives project-relative paths via
42
+ * `relative(projectDir, path)`.
43
+ */
44
+ export async function discoverSceneFiles(opts) {
45
+ const projectDir = resolve(opts.projectDir);
46
+ const rootAbs = resolve(projectDir, opts.rootRel ?? "index.html");
47
+ const root = existsSync(rootAbs) ? rootAbs : null;
48
+ const compsDir = resolve(projectDir, "compositions");
49
+ const subs = [];
50
+ if (existsSync(compsDir)) {
51
+ await collectHtmlRecursive(compsDir, subs);
52
+ subs.sort();
53
+ }
54
+ return { root, subs };
55
+ }
56
+ async function collectHtmlRecursive(dir, into) {
57
+ const entries = await readdir(dir, { withFileTypes: true });
58
+ for (const entry of entries) {
59
+ const full = join(dir, entry.name);
60
+ if (entry.isDirectory()) {
61
+ await collectHtmlRecursive(full, into);
62
+ }
63
+ else if (entry.isFile() && entry.name.toLowerCase().endsWith(".html")) {
64
+ into.push(full);
65
+ }
66
+ }
67
+ }
68
+ /**
69
+ * Drop findings that producer's lint emits as inverse-rules for sub-comp
70
+ * files. Other findings pass through unchanged.
71
+ */
72
+ export function filterSubCompFalsePositives(findings, isSubComposition) {
73
+ if (!isSubComposition)
74
+ return findings;
75
+ return findings.filter((f) => !SUB_COMP_FALSE_POSITIVES.has(f.code));
76
+ }
77
+ /**
78
+ * Apply mechanical `--fix` rewrites for known-safe finding codes. Returns the
79
+ * (possibly unchanged) HTML and the list of codes that were fixed at least
80
+ * once.
81
+ *
82
+ * MVP 1 only auto-fixes `timed_element_missing_clip_class` — adding the
83
+ * `clip` class to elements that already carry timing attributes.
84
+ *
85
+ * Other "registration"/"paused" fixes are intentionally NOT mechanical —
86
+ * they require structural reasoning (where to insert the `window.__timelines`
87
+ * block, how to convert an unpaused timeline) that goes beyond a regex.
88
+ * The lint output still surfaces them with `fixHint` for the agent or human
89
+ * to apply.
90
+ */
91
+ export function applyMechanicalFixes(html, findings) {
92
+ const fixedCodes = new Set();
93
+ let updated = html;
94
+ if (findings.some((f) => f.code === "timed_element_missing_clip_class")) {
95
+ const next = addClipClassToTimedElements(updated);
96
+ if (next !== updated) {
97
+ updated = next;
98
+ fixedCodes.add("timed_element_missing_clip_class");
99
+ }
100
+ }
101
+ return { html: updated, fixedCodes: [...fixedCodes] };
102
+ }
103
+ /**
104
+ * Add `class="clip"` (or merge into an existing `class="..."`) on every
105
+ * element that has both `data-start` and `data-duration` but no `clip` class.
106
+ *
107
+ * Skips `<audio>` and `<video>` elements — those have timing attributes but
108
+ * use their own track-management semantics, and the lint rule already exempts
109
+ * them.
110
+ */
111
+ function addClipClassToTimedElements(html) {
112
+ return html.replace(/<([a-z][a-z0-9-]*)([^>]*)>/gi, (full, tag, attrs) => {
113
+ const lower = tag.toLowerCase();
114
+ if (lower === "audio" || lower === "video")
115
+ return full;
116
+ if (!/\sdata-start="/.test(attrs))
117
+ return full;
118
+ if (!/\sdata-duration="/.test(attrs))
119
+ return full;
120
+ const classMatch = attrs.match(/\sclass="([^"]*)"/);
121
+ if (classMatch) {
122
+ const classes = classMatch[1].split(/\s+/).filter(Boolean);
123
+ if (classes.includes("clip"))
124
+ return full;
125
+ const merged = [...classes, "clip"].join(" ");
126
+ return `<${tag}${attrs.replace(/\sclass="[^"]*"/, ` class="${merged}"`)}>`;
127
+ }
128
+ return `<${tag} class="clip"${attrs}>`;
129
+ });
130
+ }
131
+ /**
132
+ * Walk the project, lint every HTML file, optionally apply mechanical fixes,
133
+ * and return aggregated counts. Throws only on FS errors; lint errors are
134
+ * surfaced as findings, not exceptions.
135
+ */
136
+ export async function runProjectLint(opts) {
137
+ const projectDir = resolve(opts.projectDir);
138
+ const { root, subs } = await discoverSceneFiles({ projectDir, rootRel: opts.rootRel });
139
+ const files = [];
140
+ const fixed = [];
141
+ const targets = [];
142
+ if (root)
143
+ targets.push({ abs: root, isSub: false });
144
+ for (const sub of subs)
145
+ targets.push({ abs: sub, isSub: true });
146
+ for (const { abs, isSub } of targets) {
147
+ const rel = relative(projectDir, abs) || abs;
148
+ const html = await readFile(abs, "utf-8");
149
+ const prepared = {
150
+ html,
151
+ entryFile: rel,
152
+ source: "projectDir",
153
+ };
154
+ const raw = runHyperframeLint(prepared);
155
+ const findings = filterSubCompFalsePositives(raw.findings, isSub);
156
+ if (opts.fix && findings.length > 0) {
157
+ const { html: nextHtml, fixedCodes } = applyMechanicalFixes(html, findings);
158
+ if (fixedCodes.length > 0) {
159
+ await writeFile(abs, nextHtml, "utf-8");
160
+ fixed.push({ file: rel, codes: fixedCodes });
161
+ // Re-lint after fix so the result reflects what's left.
162
+ const reLinted = runHyperframeLint({ ...prepared, html: nextHtml });
163
+ files.push({
164
+ file: rel,
165
+ isSubComposition: isSub,
166
+ findings: filterSubCompFalsePositives(reLinted.findings, isSub),
167
+ });
168
+ continue;
169
+ }
170
+ }
171
+ files.push({ file: rel, isSubComposition: isSub, findings });
172
+ }
173
+ let errorCount = 0;
174
+ let warningCount = 0;
175
+ let infoCount = 0;
176
+ for (const f of files) {
177
+ for (const finding of f.findings) {
178
+ if (finding.severity === "error")
179
+ errorCount++;
180
+ else if (finding.severity === "warning")
181
+ warningCount++;
182
+ else
183
+ infoCount++;
184
+ }
185
+ }
186
+ return {
187
+ ok: errorCount === 0,
188
+ errorCount,
189
+ warningCount,
190
+ infoCount,
191
+ files,
192
+ fixed,
193
+ };
194
+ }
195
+ /** True if `root` exists and is a regular file. Used by callers that want to
196
+ * pre-validate before invoking `runProjectLint`. */
197
+ export async function rootExists(projectDir, rootRel = "index.html") {
198
+ const abs = resolve(projectDir, rootRel);
199
+ try {
200
+ const s = await stat(abs);
201
+ return s.isFile();
202
+ }
203
+ catch {
204
+ return false;
205
+ }
206
+ }
207
+ //# sourceMappingURL=scene-lint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-lint.js","sourceRoot":"","sources":["../../../src/commands/_shared/scene-lint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scene-lint.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-lint.test.d.ts","sourceRoot":"","sources":["../../../src/commands/_shared/scene-lint.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,208 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { mkdtemp, mkdir, writeFile, readFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join, resolve } from "node:path";
5
+ import { applyMechanicalFixes, discoverSceneFiles, filterSubCompFalsePositives, rootExists, runProjectLint, SUB_COMP_FALSE_POSITIVES, } from "./scene-lint.js";
6
+ import { buildEmptyRootHtml, scaffoldSceneProject, } from "./scene-project.js";
7
+ import { buildClipReference, emitSceneHtml, insertClipIntoRoot, } from "./scene-html-emit.js";
8
+ async function makeTmp(label = "vibe-scene-lint-") {
9
+ return mkdtemp(join(tmpdir(), label));
10
+ }
11
+ // ── filterSubCompFalsePositives ─────────────────────────────────────────────
12
+ describe("filterSubCompFalsePositives", () => {
13
+ const findings = [
14
+ { code: "standalone_composition_wrapped_in_template", severity: "warning", message: "x" },
15
+ { code: "root_composition_missing_html_wrapper", severity: "warning", message: "x" },
16
+ { code: "missing_timeline_registry", severity: "error", message: "real" },
17
+ ];
18
+ it("drops the two known false positives for sub-compositions", () => {
19
+ const out = filterSubCompFalsePositives(findings, true);
20
+ expect(out).toHaveLength(1);
21
+ expect(out[0].code).toBe("missing_timeline_registry");
22
+ });
23
+ it("returns root-composition findings unchanged", () => {
24
+ const out = filterSubCompFalsePositives(findings, false);
25
+ expect(out).toHaveLength(3);
26
+ });
27
+ it("exposes the filtered codes via SUB_COMP_FALSE_POSITIVES", () => {
28
+ expect(SUB_COMP_FALSE_POSITIVES.has("standalone_composition_wrapped_in_template")).toBe(true);
29
+ expect(SUB_COMP_FALSE_POSITIVES.has("root_composition_missing_html_wrapper")).toBe(true);
30
+ expect(SUB_COMP_FALSE_POSITIVES.size).toBe(2);
31
+ });
32
+ });
33
+ // ── applyMechanicalFixes ────────────────────────────────────────────────────
34
+ describe("applyMechanicalFixes — timed_element_missing_clip_class", () => {
35
+ const finding = {
36
+ code: "timed_element_missing_clip_class",
37
+ severity: "error",
38
+ message: "missing class=\"clip\"",
39
+ };
40
+ it("adds class=\"clip\" to elements with timing attributes and no class", () => {
41
+ const html = `<div data-start="0" data-duration="3" data-track-index="1">x</div>`;
42
+ const { html: out, fixedCodes } = applyMechanicalFixes(html, [finding]);
43
+ expect(out).toContain('<div class="clip" data-start="0" data-duration="3"');
44
+ expect(fixedCodes).toEqual(["timed_element_missing_clip_class"]);
45
+ });
46
+ it("merges into an existing class list rather than overwriting", () => {
47
+ const html = `<div class="card glow" data-start="0" data-duration="3" data-track-index="1">x</div>`;
48
+ const { html: out } = applyMechanicalFixes(html, [finding]);
49
+ expect(out).toContain('class="card glow clip"');
50
+ });
51
+ it("is idempotent — already-clipped elements aren't modified", () => {
52
+ const html = `<div class="clip card" data-start="0" data-duration="3" data-track-index="1">x</div>`;
53
+ const { html: out, fixedCodes } = applyMechanicalFixes(html, [finding]);
54
+ expect(out).toBe(html);
55
+ expect(fixedCodes).toEqual([]);
56
+ });
57
+ it("skips <audio> and <video> (their lint rule already exempts them)", () => {
58
+ const html = `<audio data-start="0" data-duration="3" src="x.mp3"></audio>
59
+ <video data-start="0" data-duration="3" src="x.mp4"></video>`;
60
+ const { html: out, fixedCodes } = applyMechanicalFixes(html, [finding]);
61
+ expect(out).toBe(html);
62
+ expect(fixedCodes).toEqual([]);
63
+ });
64
+ it("does not fix codes outside the auto-fix allow-list", () => {
65
+ const html = `<div data-start="0" data-duration="3">x</div>`;
66
+ const other = { code: "missing_timeline_registry", severity: "error", message: "x" };
67
+ const { html: out, fixedCodes } = applyMechanicalFixes(html, [other]);
68
+ expect(out).toBe(html);
69
+ expect(fixedCodes).toEqual([]);
70
+ });
71
+ });
72
+ // ── discoverSceneFiles + rootExists ─────────────────────────────────────────
73
+ describe("discoverSceneFiles", () => {
74
+ it("returns null root when index.html is missing", async () => {
75
+ const dir = await makeTmp();
76
+ const out = await discoverSceneFiles({ projectDir: dir });
77
+ expect(out.root).toBeNull();
78
+ expect(out.subs).toEqual([]);
79
+ });
80
+ it("walks compositions/ recursively and sorts results", async () => {
81
+ const dir = await makeTmp();
82
+ await scaffoldSceneProject({ dir, name: "fix", aspect: "16:9", duration: 6 });
83
+ await mkdir(resolve(dir, "compositions/nested"), { recursive: true });
84
+ await writeFile(resolve(dir, "compositions/scene-b.html"), "x", "utf-8");
85
+ await writeFile(resolve(dir, "compositions/scene-a.html"), "x", "utf-8");
86
+ await writeFile(resolve(dir, "compositions/nested/deep.html"), "x", "utf-8");
87
+ await writeFile(resolve(dir, "compositions/notes.txt"), "ignored", "utf-8");
88
+ const out = await discoverSceneFiles({ projectDir: dir });
89
+ expect(out.root).toBe(resolve(dir, "index.html"));
90
+ expect(out.subs).toEqual([
91
+ resolve(dir, "compositions/nested/deep.html"),
92
+ resolve(dir, "compositions/scene-a.html"),
93
+ resolve(dir, "compositions/scene-b.html"),
94
+ ]);
95
+ });
96
+ });
97
+ describe("rootExists", () => {
98
+ it("true when index.html exists, false otherwise", async () => {
99
+ const dir = await makeTmp();
100
+ expect(await rootExists(dir)).toBe(false);
101
+ await scaffoldSceneProject({ dir, name: "x", aspect: "16:9", duration: 4 });
102
+ expect(await rootExists(dir)).toBe(true);
103
+ });
104
+ });
105
+ // ── runProjectLint — integration against scaffolded projects ────────────────
106
+ describe("runProjectLint — integration", () => {
107
+ async function scaffoldWithScenes() {
108
+ const dir = await makeTmp("vibe-lint-int-");
109
+ await scaffoldSceneProject({ dir, name: "fixture", aspect: "16:9", duration: 8 });
110
+ // Add two scenes by hand using the C2 emit helpers (avoids spawning the CLI).
111
+ let root = await readFile(resolve(dir, "index.html"), "utf-8");
112
+ const intro = emitSceneHtml({
113
+ id: "intro", preset: "announcement", width: 1920, height: 1080,
114
+ duration: 4, headline: "Hello",
115
+ });
116
+ await writeFile(resolve(dir, "compositions/scene-intro.html"), intro, "utf-8");
117
+ root = insertClipIntoRoot(root, { id: "intro", start: 0, duration: 4 });
118
+ const outro = emitSceneHtml({
119
+ id: "outro", preset: "simple", width: 1920, height: 1080,
120
+ duration: 3, subhead: "Thanks",
121
+ });
122
+ await writeFile(resolve(dir, "compositions/scene-outro.html"), outro, "utf-8");
123
+ root = insertClipIntoRoot(root, { id: "outro", start: 4, duration: 3 });
124
+ await writeFile(resolve(dir, "index.html"), root, "utf-8");
125
+ return dir;
126
+ }
127
+ it("produces ok=true on a freshly scaffolded project with valid scenes", async () => {
128
+ const dir = await scaffoldWithScenes();
129
+ const result = await runProjectLint({ projectDir: dir });
130
+ expect(result.ok).toBe(true);
131
+ expect(result.errorCount).toBe(0);
132
+ // 1 root + 2 sub-comps inspected
133
+ expect(result.files.map((f) => f.file).sort()).toEqual([
134
+ "compositions/scene-intro.html",
135
+ "compositions/scene-outro.html",
136
+ "index.html",
137
+ ]);
138
+ // Sub-comp false positives must not appear
139
+ for (const file of result.files) {
140
+ for (const f of file.findings) {
141
+ expect(SUB_COMP_FALSE_POSITIVES.has(f.code)).toBe(false);
142
+ }
143
+ }
144
+ });
145
+ it("flags a sub-comp that has timing attributes but no class=\"clip\"", async () => {
146
+ const dir = await scaffoldWithScenes();
147
+ // Mutate scene-intro.html: drop `class="clip"` from a timed element.
148
+ const broken = `<template id="bad-template">
149
+ <div data-composition-id="bad" data-start="0" data-duration="3" data-width="1920" data-height="1080">
150
+ <div data-start="0" data-duration="2" data-track-index="1">no class</div>
151
+ </div>
152
+ </template>`;
153
+ await writeFile(resolve(dir, "compositions/scene-bad.html"), broken, "utf-8");
154
+ const result = await runProjectLint({ projectDir: dir });
155
+ const badFile = result.files.find((f) => f.file.endsWith("scene-bad.html"));
156
+ expect(badFile).toBeDefined();
157
+ expect(badFile.findings.some((f) => f.code === "timed_element_missing_clip_class")).toBe(true);
158
+ });
159
+ it("--fix repairs timed_element_missing_clip_class and re-lint shows it gone", async () => {
160
+ const dir = await scaffoldWithScenes();
161
+ const broken = `<template id="bad-template">
162
+ <div data-composition-id="bad" data-start="0" data-duration="3" data-width="1920" data-height="1080">
163
+ <div data-start="0" data-duration="2" data-track-index="1">no class</div>
164
+ <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
165
+ <script>
166
+ window.__timelines = window.__timelines || {};
167
+ const tl = gsap.timeline({ paused: true });
168
+ window.__timelines["bad"] = tl;
169
+ </script>
170
+ </div>
171
+ </template>`;
172
+ const badPath = resolve(dir, "compositions/scene-bad.html");
173
+ await writeFile(badPath, broken, "utf-8");
174
+ const result = await runProjectLint({ projectDir: dir, fix: true });
175
+ // Fix tracked
176
+ const fix = result.fixed.find((f) => f.file.endsWith("scene-bad.html"));
177
+ expect(fix).toBeDefined();
178
+ expect(fix.codes).toContain("timed_element_missing_clip_class");
179
+ // File on disk is updated
180
+ const after = await readFile(badPath, "utf-8");
181
+ expect(after).toContain('<div class="clip" data-start="0" data-duration="2"');
182
+ // Re-lint result no longer reports it for that file
183
+ const badFile = result.files.find((f) => f.file.endsWith("scene-bad.html"));
184
+ expect(badFile.findings.some((f) => f.code === "timed_element_missing_clip_class")).toBe(false);
185
+ });
186
+ it("returns ok=true and empty findings on an empty root with no compositions/ dir", async () => {
187
+ const dir = await makeTmp();
188
+ await writeFile(resolve(dir, "index.html"), buildEmptyRootHtml({ aspect: "16:9", duration: 5 }), "utf-8");
189
+ const result = await runProjectLint({ projectDir: dir });
190
+ expect(result.ok).toBe(true);
191
+ expect(result.files).toHaveLength(1);
192
+ expect(result.files[0].file).toBe("index.html");
193
+ });
194
+ it("does not flag a buildClipReference()-built clip in the root", async () => {
195
+ const dir = await scaffoldWithScenes();
196
+ // Produce one extra clip via the C2 helper just to be sure the produced
197
+ // markup keeps lint clean.
198
+ const root = await readFile(resolve(dir, "index.html"), "utf-8");
199
+ const augmented = root.replace("</div>\n\n <script>", ` ${buildClipReference({ id: "extra", start: 7, duration: 2 })}\n </div>\n\n <script>`);
200
+ await writeFile(resolve(dir, "index.html"), augmented, "utf-8");
201
+ // The extra clip references a non-existent composition file — that's a
202
+ // semantic/runtime concern, not a lint rule. So lint should still pass.
203
+ const result = await runProjectLint({ projectDir: dir });
204
+ const rootFile = result.files.find((f) => f.file === "index.html");
205
+ expect(rootFile.findings.some((f) => f.code === "timed_element_missing_clip_class")).toBe(false);
206
+ });
207
+ });
208
+ //# sourceMappingURL=scene-lint.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-lint.test.js","sourceRoot":"","sources":["../../../src/commands/_shared/scene-lint.test.ts"],"names":[],"mappings":"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"}