@devinilabs/reelstack 1.3.2 → 1.4.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.
package/cli/scaffold.js CHANGED
@@ -1,8 +1,20 @@
1
1
  /**
2
- * `reelstack scaffold` — copies a family template into the buyer's
3
- * src/<Name>Reel.tsx, substitutes preset metadata (palette, BEATs,
4
- * frame count, copy), and patches src/Root.tsx to register the new
5
- * composition.
2
+ * `reelstack scaffold` — produces a production-quality reel scaffold by
3
+ * preferring the reference reel (cloned from a real Devini Labs production
4
+ * reel) over the generic family template. Substitutes preset metadata,
5
+ * does a mechanical export rename, strips the reference header, and
6
+ * patches src/Root.tsx to register the new composition.
7
+ *
8
+ * v1.4 flow:
9
+ * 1. Resolve reference reel for family + preset.
10
+ * 2. If reference exists (and --minimal not passed):
11
+ * - Read reference verbatim (2000-3800 lines of production motion).
12
+ * - Strip the reference header JSDoc.
13
+ * - Rename `export const <PresetClass>Reel` → `export const <Name>Reel`.
14
+ * - Inject a /** SCAFFOLDED FROM: <ref> *\/ marker.
15
+ * 3. Else fall back to generic template + placeholder substitution.
16
+ * 4. Write to src/<Name>Reel.tsx.
17
+ * 5. Register in src/Root.tsx.
6
18
  */
7
19
  const fs = require("node:fs");
8
20
  const path = require("node:path");
@@ -127,6 +139,77 @@ function injectReferenceComment(tsx, refPath) {
127
139
  return tsx.slice(0, after) + block + tsx.slice(after);
128
140
  }
129
141
 
142
+ /**
143
+ * v1.4: Strip the reference reel's leading JSDoc header block. References
144
+ * start with `/** REFERENCE — <ClassName> (Family: <fam>) ... *\/`.
145
+ * When the reference becomes a buyer's reel, that header doesn't apply.
146
+ */
147
+ function stripReferenceHeader(tsx) {
148
+ if (!tsx.startsWith("/**")) return tsx;
149
+ if (!/^\/\*\*\s*\n\s*\*\s*REFERENCE\b/.test(tsx)) return tsx;
150
+ const end = tsx.indexOf("*/");
151
+ if (end === -1) return tsx;
152
+ // Skip the closing `*/` plus any trailing newline.
153
+ let cut = end + 2;
154
+ while (tsx[cut] === "\n") cut++;
155
+ return tsx.slice(cut);
156
+ }
157
+
158
+ /**
159
+ * v1.4: Mechanical export rename — replace `export const <X>Reel` with
160
+ * `export const <NewName>Reel`. The reference reel has the production
161
+ * class name (e.g. GraphifyReel); we rewrite it to the buyer's chosen
162
+ * name (e.g. MyLaunchReel). Returns the rewritten source and the original
163
+ * class name (so we can announce what changed in the scaffold summary).
164
+ */
165
+ function renameReelExport(tsx, newName) {
166
+ const m = tsx.match(/export\s+const\s+(\w+Reel)\b/);
167
+ if (!m) return { tsx, originalClassName: null };
168
+ const originalClassName = m[1];
169
+ const targetClassName = `${newName}Reel`;
170
+ if (originalClassName === targetClassName) {
171
+ return { tsx, originalClassName };
172
+ }
173
+ // Replace ALL occurrences of the original class name with the new one.
174
+ // This catches the export, any internal references (helper components
175
+ // named after the reel, JSDoc, comments), and keeps the file coherent.
176
+ const re = new RegExp(`\\b${originalClassName}\\b`, "g");
177
+ return {
178
+ tsx: tsx.replace(re, targetClassName),
179
+ originalClassName,
180
+ };
181
+ }
182
+
183
+ /**
184
+ * v1.4: Inject a top-of-file marker so the buyer (and Claude on subsequent
185
+ * iteration) knows this file was scaffolded from a reference reel and the
186
+ * canonical hooks/CTAs are Devini Labs strings that should be replaced.
187
+ */
188
+ function injectScaffoldHeader({ name, family, preset, refPath, originalClassName }) {
189
+ const rel = path.relative(REELSTACK_PKG, refPath) || refPath;
190
+ return `/**
191
+ * ${name}Reel — scaffolded by ReelStack v1.4
192
+ *
193
+ * Family: ${family}
194
+ * Preset: ${preset}
195
+ * Source: ${rel}
196
+ * Cloned at: ${new Date().toISOString()}
197
+ *
198
+ * This file is a faithful clone of the production ${originalClassName || preset + "Reel"}
199
+ * — same motion vocabulary, palette, and scene structure. Canonical Devini
200
+ * Labs hook / sub / CTA strings are still in place. REPLACE them with your
201
+ * own content before publishing. The motion floor (≥4 layers in opener,
202
+ * ≥3 in anchor scenes) is preserved.
203
+ *
204
+ * Next steps:
205
+ * 1. Replace the hero hook, sub, and CTA copy with your reel's narrative.
206
+ * 2. Run \`/reelstack-beats <vo.wav>\` to lock motion to your voiceover.
207
+ * 3. Look for REFERENCE-STRIP markers and swap in your assets.
208
+ * 4. Run \`/reelstack-lint src/${name}Reel.tsx\` and address any errors.
209
+ */
210
+ `;
211
+ }
212
+
130
213
  function patchRoot(rootPath, name, durationFrames) {
131
214
  if (!fs.existsSync(rootPath)) {
132
215
  fail(`src/Root.tsx not found at ${rootPath}. Skipping registration.`);
@@ -193,15 +276,35 @@ async function run(argv, opts = {}) {
193
276
  }
194
277
 
195
278
  const presetMeta = loadPreset(family, preset);
196
- const template = loadTemplate(family);
197
- let tsx = substitute(template, presetMeta, name);
198
-
199
- // v1.1.1+ — resolve the reference reel for this preset (or buyer's override
200
- // via --reference=<preset>) so a `/** REFERENCE: <path> */` comment can be
201
- // injected into the scaffold, and a hint surfaced after Studio.
202
279
  const ref = resolveReference(family, preset, args.reference);
203
- if (ref) {
204
- tsx = injectReferenceComment(tsx, ref.path);
280
+
281
+ // v1.4 flow: prefer reference reel (production-grade DNA) over the generic
282
+ // template. Buyer can opt into the generic template via --minimal.
283
+ let tsx;
284
+ let scaffoldedFromReference = false;
285
+ let originalClassName = null;
286
+
287
+ if (ref && !args.minimal) {
288
+ let refSrc = fs.readFileSync(ref.path, "utf8");
289
+ refSrc = stripReferenceHeader(refSrc);
290
+ const renamed = renameReelExport(refSrc, name);
291
+ refSrc = renamed.tsx;
292
+ originalClassName = renamed.originalClassName;
293
+ const header = injectScaffoldHeader({
294
+ name,
295
+ family,
296
+ preset,
297
+ refPath: ref.path,
298
+ originalClassName,
299
+ });
300
+ tsx = header + refSrc;
301
+ scaffoldedFromReference = true;
302
+ } else {
303
+ const template = loadTemplate(family);
304
+ tsx = substitute(template, presetMeta, name);
305
+ if (ref) {
306
+ tsx = injectReferenceComment(tsx, ref.path);
307
+ }
205
308
  }
206
309
 
207
310
  const outFile = path.join(cwd, "src", `${name}Reel.tsx`);
@@ -225,13 +328,18 @@ async function run(argv, opts = {}) {
225
328
  console.log(c.gray(` Family: `) + family);
226
329
  console.log(c.gray(` Preset: `) + `${preset} (from ${presetMeta.source})`);
227
330
  console.log(c.gray(` Frame count: `) + `${presetMeta.durationFrames} (${(presetMeta.durationFrames / 30).toFixed(1)}s @ 30fps)`);
331
+ console.log(c.gray(` Source: `) + (scaffoldedFromReference
332
+ ? `reference reel (${path.basename(ref.path)})${originalClassName ? ` — renamed ${originalClassName} → ${name}Reel` : ""}`
333
+ : `generic template`));
228
334
  console.log(c.gray(` Output: `) + path.relative(cwd, outFile));
229
335
  console.log("");
230
336
  console.log(c.cyan("→ ") + `Open Remotion Studio with ${c.bold("npm run dev")} and select "${name}Reel" from the comp list.`);
231
- if (ref) {
232
- console.log(c.cyan("ℹ ") + `Reference reel available: ${ref.path}`);
233
- console.log(c.gray(" Claude will study this reel when iterating on your scaffold. Open it"));
234
- console.log(c.gray(" yourself to learn the family's motion vocabulary."));
337
+ if (scaffoldedFromReference) {
338
+ console.log(c.cyan("ℹ ") + `Scaffolded from production reference. Canonical Devini Labs hook/sub/CTA copy is in the file —`);
339
+ console.log(c.gray(" replace those strings with your own narrative before publishing. The motion, palette, and scene"));
340
+ console.log(c.gray(" structure are production-grade and preserved verbatim."));
341
+ } else if (ref) {
342
+ console.log(c.cyan("ℹ ") + `Reference reel available for study: ${ref.path}`);
235
343
  }
236
344
  console.log("");
237
345
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devinilabs/reelstack",
3
- "version": "1.3.2",
3
+ "version": "1.4.0",
4
4
  "description": "Premium 9:16 Reel OS for Remotion. 5 cinematic style families, 22 production-tested presets, audio-locked motion, IG-safe by default. v1.1+ bakes in leonxlnx/taste-skill design discipline + huashu-design productivity patterns.",
5
5
  "keywords": [
6
6
  "remotion",