From 907e25e14618fb06cd35b12a6161c09f48690532 Mon Sep 17 00:00:00 2001 From: "Abdullah M. Nazif" Date: Fri, 9 Oct 2026 11:13:43 +0800 Subject: [PATCH 1/7] fix(cli): remove stale briefs when re-freezing recipes --- .../cli/src/media-use/lib/recipe-store.mjs | 1 + .../src/media-use/lib/recipe-store.test.mjs | 24 +++++++++++++++++++ skills-manifest.json | 2 +- skills/media-use/scripts/lib/recipe-store.mjs | 1 + 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/media-use/lib/recipe-store.mjs b/packages/cli/src/media-use/lib/recipe-store.mjs index 2b710e049eb..4f017fd57ec 100644 --- a/packages/cli/src/media-use/lib/recipe-store.mjs +++ b/packages/cli/src/media-use/lib/recipe-store.mjs @@ -280,6 +280,7 @@ export function freezeRecipe({ projectDir, name, workflow, blocks }) { const userDir = join(userRecipesDir(), slug); mkdirSync(userDir, { recursive: true }); cpSync(dir, userDir, { recursive: true, force: true }); + if (!briefSkeleton) rmSync(join(userDir, "brief-skeleton.md"), { force: true }); } catch { // The project-tier freeze already landed. } diff --git a/packages/cli/src/media-use/lib/recipe-store.test.mjs b/packages/cli/src/media-use/lib/recipe-store.test.mjs index 2bf751dca8e..e33fa78fa5f 100644 --- a/packages/cli/src/media-use/lib/recipe-store.test.mjs +++ b/packages/cli/src/media-use/lib/recipe-store.test.mjs @@ -318,6 +318,30 @@ test("re-freezing bumps the version and archives the old folder", () => { }); }); +test("re-freezing without a brief does not import the previous version's brief", () => { + withSandbox(({ project, root }) => { + writeFileSync(join(project, "BRIEF.md"), BRIEF); + freezeRecipe({ projectDir: project, name: "promo" }); + + rmSync(join(project, "BRIEF.md")); + const frozen = freezeRecipe({ + projectDir: project, + name: "promo", + workflow: "product-launch-video", + }); + assert.equal(frozen.version, 2); + assert.equal(frozen.briefSkeleton, false); + assert.ok(existsSync(join(project, ".media/recipes/promo@v1/brief-skeleton.md"))); + + const fresh = join(root, "fresh-project"); + mkdirSync(fresh, { recursive: true }); + const used = useRecipe({ projectDir: fresh, name: "promo" }); + assert.equal(used.recipe.version, 2); + assert.equal(used.briefSkeletonPath, undefined); + assert.ok(!existsSync(join(fresh, ".media/recipes/promo/brief-skeleton.md"))); + }); +}); + test("list merges tiers (project wins), filters by workflow, skips archives", () => { withSandbox(({ project, root }) => { freezeRecipe({ projectDir: project, name: "promo", workflow: "product-launch-video" }); diff --git a/skills-manifest.json b/skills-manifest.json index 49ddfb2a1e9..cce0790eeea 100644 --- a/skills-manifest.json +++ b/skills-manifest.json @@ -54,7 +54,7 @@ "files": 1 }, "media-use": { - "hash": "c8800249da2d1ce6", + "hash": "0482d6a233ee5efa", "files": 109 }, "motion-graphics": { diff --git a/skills/media-use/scripts/lib/recipe-store.mjs b/skills/media-use/scripts/lib/recipe-store.mjs index 2b710e049eb..4f017fd57ec 100644 --- a/skills/media-use/scripts/lib/recipe-store.mjs +++ b/skills/media-use/scripts/lib/recipe-store.mjs @@ -280,6 +280,7 @@ export function freezeRecipe({ projectDir, name, workflow, blocks }) { const userDir = join(userRecipesDir(), slug); mkdirSync(userDir, { recursive: true }); cpSync(dir, userDir, { recursive: true, force: true }); + if (!briefSkeleton) rmSync(join(userDir, "brief-skeleton.md"), { force: true }); } catch { // The project-tier freeze already landed. } From 176b61b600a37a110d144d13e16ef942c79fce78 Mon Sep 17 00:00:00 2001 From: "Abdullah M. Nazif" Date: Wed, 14 Oct 2026 04:20:05 +0800 Subject: [PATCH 2/7] chore: prepare conflict resolution against current main --- skills-manifest.json | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/skills-manifest.json b/skills-manifest.json index cce0790eeea..960e5a88ac8 100644 --- a/skills-manifest.json +++ b/skills-manifest.json @@ -2,35 +2,35 @@ "source": "heygen-com/hyperframes", "skills": { "embedded-captions": { - "hash": "643579aed5919847", - "files": 144 + "hash": "1dc7ec2fd4a6e3fc", + "files": 145 }, "faceless-explainer": { - "hash": "1f10a69e0f879698", - "files": 28 + "hash": "bb2225ebaf7f9622", + "files": 29 }, "figma": { "hash": "f8cf9f2c78ea8374", "files": 2 }, "general-video": { - "hash": "7194a47a82647a88", - "files": 5 + "hash": "17dfbb1d34efb012", + "files": 6 }, "hyperframes": { - "hash": "589ae40d15628917", - "files": 29 + "hash": "9787b91e4c16bf7f", + "files": 31 }, "hyperframes-animation": { - "hash": "3bd80388da91c689", + "hash": "a5d09375a50f71c8", "files": 122 }, "hyperframes-audio": { - "hash": "e96de47ed2e6097a", - "files": 7 + "hash": "780c9d4d8d23ab40", + "files": 8 }, "hyperframes-cli": { - "hash": "c053e99be2d209b1", + "hash": "56a46bc15c308d98", "files": 11 }, "hyperframes-core": { @@ -54,11 +54,11 @@ "files": 1 }, "media-use": { - "hash": "0482d6a233ee5efa", - "files": 109 + "hash": "c4c3c723aa04bb7d", + "files": 112 }, "motion-graphics": { - "hash": "6db77031ad839f6a", + "hash": "7e4af0881390ac2e", "files": 23 }, "music-to-video": { @@ -66,12 +66,12 @@ "files": 169 }, "pr-to-video": { - "hash": "88f4c68788eaba9e", - "files": 34 + "hash": "d377b802ef54bc46", + "files": 35 }, "product-launch-video": { - "hash": "76149a9f3514626e", - "files": 36 + "hash": "965b9b93bf19b173", + "files": 37 }, "remotion-to-hyperframes": { "hash": "d02be65b7f3f4a63", @@ -82,7 +82,7 @@ "files": 2 }, "talking-head-recut": { - "hash": "9aac076ceddd2499", + "hash": "4b2cf6d7b6e336b4", "files": 28 } } From 02dbef42ee6ae2dc3671809961e24de1608ebe82 Mon Sep 17 00:00:00 2001 From: "Abdullah M. Nazif" Date: Wed, 14 Oct 2026 04:20:14 +0800 Subject: [PATCH 3/7] chore: preserve reviewed fix on current main --- skills-manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills-manifest.json b/skills-manifest.json index 960e5a88ac8..c94246073fa 100644 --- a/skills-manifest.json +++ b/skills-manifest.json @@ -54,7 +54,7 @@ "files": 1 }, "media-use": { - "hash": "c4c3c723aa04bb7d", + "hash": "6ea61b83187755ae", "files": 112 }, "motion-graphics": { From 2cd30913de201f0e46aa8501b90248406b64f69d Mon Sep 17 00:00:00 2001 From: "Abdullah M. Nazif" Date: Sat, 24 Oct 2026 13:02:50 +0800 Subject: [PATCH 4/7] chore: prepare conflict resolution against current main --- .../cli/src/media-use/lib/recipe-store.mjs | 10 ++- skills-manifest.json | 78 +++---------------- skills/media-use/scripts/lib/recipe-store.mjs | 10 ++- 3 files changed, 23 insertions(+), 75 deletions(-) diff --git a/packages/cli/src/media-use/lib/recipe-store.mjs b/packages/cli/src/media-use/lib/recipe-store.mjs index 4f017fd57ec..44b95ecafe2 100644 --- a/packages/cli/src/media-use/lib/recipe-store.mjs +++ b/packages/cli/src/media-use/lib/recipe-store.mjs @@ -14,6 +14,9 @@ import { appendRecord, mediaDir, nextId } from "./manifest.mjs"; import { regenerateIndex } from "./index-gen.mjs"; import { mergedPreferences } from "./prefs-store.mjs"; +// A filter keeps Node 22 off its native copy, which writes nothing under a non-ASCII path on Windows. +const NON_ASCII_SAFE_DIR_COPY = { recursive: true, force: true, filter: () => true }; + /** * Recipes — the heavyweight tier of HyperFrames user memory. * @@ -35,7 +38,7 @@ import { mergedPreferences } from "./prefs-store.mjs"; const FRONTMATTER_CONTENT_KEYS = new Set(["message", "audience", "mode"]); /** BRIEF.md frontmatter keys that describe this run, not the reusable type — - * a recipe never locks the run's shape, so the intent layer always re-asks. */ + * a recipe never locks the run's shape, so these are always asked again. */ const BRIEF_CONTENT_KEYS = new Set(["flow", "storyboard", "message", "audience"]); /** Per-frame metadata that is content, not structure. */ @@ -279,8 +282,7 @@ export function freezeRecipe({ projectDir, name, workflow, blocks }) { try { const userDir = join(userRecipesDir(), slug); mkdirSync(userDir, { recursive: true }); - cpSync(dir, userDir, { recursive: true, force: true }); - if (!briefSkeleton) rmSync(join(userDir, "brief-skeleton.md"), { force: true }); + cpSync(dir, userDir, NON_ASCII_SAFE_DIR_COPY); } catch { // The project-tier freeze already landed. } @@ -340,7 +342,7 @@ export function useRecipe({ projectDir, name }) { ); } mkdirSync(dir, { recursive: true }); - cpSync(userDir, dir, { recursive: true, force: true }); + cpSync(userDir, dir, NON_ASCII_SAFE_DIR_COPY); const imported = readRecipeJson(dir); appendRecord(root, { id: nextId(root, "recipe"), diff --git a/skills-manifest.json b/skills-manifest.json index c94246073fa..e6442718b8a 100644 --- a/skills-manifest.json +++ b/skills-manifest.json @@ -1,89 +1,33 @@ { "source": "heygen-com/hyperframes", "skills": { - "embedded-captions": { - "hash": "1dc7ec2fd4a6e3fc", - "files": 145 - }, - "faceless-explainer": { - "hash": "bb2225ebaf7f9622", - "files": 29 - }, "figma": { - "hash": "f8cf9f2c78ea8374", + "hash": "af57c9b6411c81f5", "files": 2 }, - "general-video": { - "hash": "17dfbb1d34efb012", - "files": 6 - }, "hyperframes": { - "hash": "9787b91e4c16bf7f", - "files": 31 - }, - "hyperframes-animation": { - "hash": "a5d09375a50f71c8", - "files": 122 - }, - "hyperframes-audio": { - "hash": "780c9d4d8d23ab40", - "files": 8 + "hash": "8fc4b05db245cbbb", + "files": 77 }, "hyperframes-cli": { - "hash": "56a46bc15c308d98", - "files": 11 - }, - "hyperframes-core": { - "hash": "010a65915fc7b45e", - "files": 11 - }, - "hyperframes-creative": { - "hash": "8d0601ce26adc560", - "files": 79 - }, - "hyperframes-keyframes": { - "hash": "a3d43b98955b1366", - "files": 3 + "hash": "d82c156c1b2069d1", + "files": 13 }, "hyperframes-registry": { - "hash": "d7c1885fb4cce2cf", - "files": 12 - }, - "hyperframes-studio": { - "hash": "f5e0b44d68152c12", - "files": 1 + "hash": "221af9e7fbffe732", + "files": 13 }, "media-use": { - "hash": "6ea61b83187755ae", - "files": 112 - }, - "motion-graphics": { - "hash": "7e4af0881390ac2e", - "files": 23 - }, - "music-to-video": { - "hash": "b775f27b16efb1f2", - "files": 169 - }, - "pr-to-video": { - "hash": "d377b802ef54bc46", - "files": 35 - }, - "product-launch-video": { - "hash": "965b9b93bf19b173", - "files": 37 + "hash": "1082bd1c4e3f033b", + "files": 116 }, "remotion-to-hyperframes": { - "hash": "d02be65b7f3f4a63", + "hash": "3bf96d306215e384", "files": 77 }, "slideshow": { - "hash": "efc3c0be622fa9a1", + "hash": "c8a591e2aa7a6d86", "files": 2 - }, - "talking-head-recut": { - "hash": "4b2cf6d7b6e336b4", - "files": 28 } } } diff --git a/skills/media-use/scripts/lib/recipe-store.mjs b/skills/media-use/scripts/lib/recipe-store.mjs index 4f017fd57ec..44b95ecafe2 100644 --- a/skills/media-use/scripts/lib/recipe-store.mjs +++ b/skills/media-use/scripts/lib/recipe-store.mjs @@ -14,6 +14,9 @@ import { appendRecord, mediaDir, nextId } from "./manifest.mjs"; import { regenerateIndex } from "./index-gen.mjs"; import { mergedPreferences } from "./prefs-store.mjs"; +// A filter keeps Node 22 off its native copy, which writes nothing under a non-ASCII path on Windows. +const NON_ASCII_SAFE_DIR_COPY = { recursive: true, force: true, filter: () => true }; + /** * Recipes — the heavyweight tier of HyperFrames user memory. * @@ -35,7 +38,7 @@ import { mergedPreferences } from "./prefs-store.mjs"; const FRONTMATTER_CONTENT_KEYS = new Set(["message", "audience", "mode"]); /** BRIEF.md frontmatter keys that describe this run, not the reusable type — - * a recipe never locks the run's shape, so the intent layer always re-asks. */ + * a recipe never locks the run's shape, so these are always asked again. */ const BRIEF_CONTENT_KEYS = new Set(["flow", "storyboard", "message", "audience"]); /** Per-frame metadata that is content, not structure. */ @@ -279,8 +282,7 @@ export function freezeRecipe({ projectDir, name, workflow, blocks }) { try { const userDir = join(userRecipesDir(), slug); mkdirSync(userDir, { recursive: true }); - cpSync(dir, userDir, { recursive: true, force: true }); - if (!briefSkeleton) rmSync(join(userDir, "brief-skeleton.md"), { force: true }); + cpSync(dir, userDir, NON_ASCII_SAFE_DIR_COPY); } catch { // The project-tier freeze already landed. } @@ -340,7 +342,7 @@ export function useRecipe({ projectDir, name }) { ); } mkdirSync(dir, { recursive: true }); - cpSync(userDir, dir, { recursive: true, force: true }); + cpSync(userDir, dir, NON_ASCII_SAFE_DIR_COPY); const imported = readRecipeJson(dir); appendRecord(root, { id: nextId(root, "recipe"), From 8f3cca70fecac702211aa6214f378159e18d449d Mon Sep 17 00:00:00 2001 From: "Abdullah M. Nazif" Date: Sat, 24 Oct 2026 13:03:01 +0800 Subject: [PATCH 5/7] fix(cli): retain recipe brief cleanup with safe directory copies --- docs/guides/media.mdx | 10 ++++++++++ packages/cli/src/media-use/lib/recipe-store.mjs | 1 + skills-manifest.json | 2 +- skills/media-use/scripts/lib/recipe-store.mjs | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/guides/media.mdx b/docs/guides/media.mdx index 3e13dae9ed5..71c33128e3b 100644 --- a/docs/guides/media.mdx +++ b/docs/guides/media.mdx @@ -63,6 +63,16 @@ Watch once from beginning to end and check: - voice clarity on ordinary headphones and laptop speakers; - missing files, temporary URLs, and late-loading media. +## Reuse an approved recipe + +Ask the agent to save an approved video's design and storyboard as a named +recipe for later projects. Saving the same name again creates a new version +and archives the previous project version. + +If the new version has no `BRIEF.md`, importing that recipe into another +project does not restore the older brief. The earlier brief remains available +in the archived version. + ## Choose a media task diff --git a/packages/cli/src/media-use/lib/recipe-store.mjs b/packages/cli/src/media-use/lib/recipe-store.mjs index 44b95ecafe2..9e1ac57487c 100644 --- a/packages/cli/src/media-use/lib/recipe-store.mjs +++ b/packages/cli/src/media-use/lib/recipe-store.mjs @@ -283,6 +283,7 @@ export function freezeRecipe({ projectDir, name, workflow, blocks }) { const userDir = join(userRecipesDir(), slug); mkdirSync(userDir, { recursive: true }); cpSync(dir, userDir, NON_ASCII_SAFE_DIR_COPY); + if (!briefSkeleton) rmSync(join(userDir, "brief-skeleton.md"), { force: true }); } catch { // The project-tier freeze already landed. } diff --git a/skills-manifest.json b/skills-manifest.json index e6442718b8a..e211bfbca93 100644 --- a/skills-manifest.json +++ b/skills-manifest.json @@ -18,7 +18,7 @@ "files": 13 }, "media-use": { - "hash": "1082bd1c4e3f033b", + "hash": "a8c9aa9d68bf732f", "files": 116 }, "remotion-to-hyperframes": { diff --git a/skills/media-use/scripts/lib/recipe-store.mjs b/skills/media-use/scripts/lib/recipe-store.mjs index 44b95ecafe2..9e1ac57487c 100644 --- a/skills/media-use/scripts/lib/recipe-store.mjs +++ b/skills/media-use/scripts/lib/recipe-store.mjs @@ -283,6 +283,7 @@ export function freezeRecipe({ projectDir, name, workflow, blocks }) { const userDir = join(userRecipesDir(), slug); mkdirSync(userDir, { recursive: true }); cpSync(dir, userDir, NON_ASCII_SAFE_DIR_COPY); + if (!briefSkeleton) rmSync(join(userDir, "brief-skeleton.md"), { force: true }); } catch { // The project-tier freeze already landed. } From 9684186922482107bfdf0a248688ea9513299ef2 Mon Sep 17 00:00:00 2001 From: "Abdullah M. Nazif" Date: Sun, 25 Oct 2026 10:27:32 +0800 Subject: [PATCH 6/7] chore: prepare conflict resolution against current main --- skills-manifest.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/skills-manifest.json b/skills-manifest.json index e211bfbca93..75aabb96626 100644 --- a/skills-manifest.json +++ b/skills-manifest.json @@ -6,7 +6,7 @@ "files": 2 }, "hyperframes": { - "hash": "8fc4b05db245cbbb", + "hash": "63994723e5533606", "files": 77 }, "hyperframes-cli": { @@ -18,11 +18,11 @@ "files": 13 }, "media-use": { - "hash": "a8c9aa9d68bf732f", + "hash": "6550641984b19843", "files": 116 }, "remotion-to-hyperframes": { - "hash": "3bf96d306215e384", + "hash": "b585361591366636", "files": 77 }, "slideshow": { From e97fa6e51648e97cfe263f56a12deaf3079db4fb Mon Sep 17 00:00:00 2001 From: "Abdullah M. Nazif" Date: Sun, 25 Oct 2026 10:27:39 +0800 Subject: [PATCH 7/7] chore: merge main and refresh recipe skill manifest --- skills-manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills-manifest.json b/skills-manifest.json index 75aabb96626..43ab65ba261 100644 --- a/skills-manifest.json +++ b/skills-manifest.json @@ -18,7 +18,7 @@ "files": 13 }, "media-use": { - "hash": "6550641984b19843", + "hash": "a1e407b34da1e96a", "files": 116 }, "remotion-to-hyperframes": {