From ff6e2103d7d7eda5f3515005abd2e393ac38c7b6 Mon Sep 17 00:00:00 2001 From: James Russo Date: Sun, 27 Sep 2026 21:05:28 +0000 Subject: [PATCH] fix(skills): keep plugin workflows on their installed release (#4607) * fix(skills): keep plugin workflows on their installed release * docs(skills): clarify plugin manifest discovery * fix(plugins): suppress stale notices and isolate plugin context --- .github/workflows/ci.yml | 1 + .../cli/src/utils/skillsUpdateCheck.test.ts | 6 + packages/cli/src/utils/skillsUpdateCheck.ts | 2 +- skills-manifest.json | 44 +++---- skills/embedded-captions/SKILL.md | 2 + skills/faceless-explainer/SKILL.md | 2 + skills/figma/SKILL.md | 2 + skills/general-video/SKILL.md | 2 + skills/hyperframes-animation/SKILL.md | 2 + skills/hyperframes-audio/SKILL.md | 2 + skills/hyperframes-cli/SKILL.md | 2 + skills/hyperframes-core/SKILL.md | 2 + skills/hyperframes-creative/SKILL.md | 2 + skills/hyperframes-keyframes/SKILL.md | 2 + skills/hyperframes-registry/SKILL.md | 2 + skills/hyperframes-studio/SKILL.md | 2 + skills/hyperframes/SKILL.md | 2 + .../references/plugin-installation.md | 43 +++++++ .../hyperframes/references/skill-lifecycle.md | 2 + skills/hyperframes/scripts/plugin-cli.mjs | 88 +++++++++++++ .../hyperframes/scripts/plugin-cli.test.mjs | 117 ++++++++++++++++++ skills/media-use/SKILL.md | 2 + skills/motion-graphics/SKILL.md | 2 + skills/music-to-video/SKILL.md | 2 + skills/pr-to-video/SKILL.md | 2 + skills/product-launch-video/SKILL.md | 2 + skills/remotion-to-hyperframes/SKILL.md | 2 + skills/slideshow/SKILL.md | 2 + skills/talking-head-recut/SKILL.md | 2 + 29 files changed, 322 insertions(+), 23 deletions(-) create mode 100644 skills/hyperframes/references/plugin-installation.md create mode 100644 skills/hyperframes/scripts/plugin-cli.mjs create mode 100644 skills/hyperframes/scripts/plugin-cli.test.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff29c8b268..e7e6fd800e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -578,6 +578,7 @@ jobs: node --test skills/hyperframes-animation/scripts/package-loader.test.mjs skills/hyperframes-creative/scripts/package-loader.test.mjs + skills/hyperframes/scripts/plugin-cli.test.mjs # Guards that skills-manifest.json (the published freshness fingerprint read # by `hyperframes skills check`) was regenerated when a skill changed. Runs diff --git a/packages/cli/src/utils/skillsUpdateCheck.test.ts b/packages/cli/src/utils/skillsUpdateCheck.test.ts index 893e895c95..43163529b8 100644 --- a/packages/cli/src/utils/skillsUpdateCheck.test.ts +++ b/packages/cli/src/utils/skillsUpdateCheck.test.ts @@ -97,6 +97,12 @@ describe("skillsUpdateCheck", () => { return String(writeSpy.mock.calls[0]?.[0]); } + it("suppresses a cached stale-skills notice in an attended plugin run", async () => { + vi.stubEnv("HYPERFRAMES_SKIP_SKILLS", "1"); + // The normal notice gate permits output (the attended TTY case). + expect(await noticeTextFor({ skillsOutdatedCount: 2, skillsMissingCount: 1 })).toBeNull(); + }); + it("the cached nudge total counts removed skills, not just outdated/missing", async () => { // Cache pre-populated as if a prior refreshSkillsCache had run — only // outdated + missing, no removed (the pre-fix shape). diff --git a/packages/cli/src/utils/skillsUpdateCheck.ts b/packages/cli/src/utils/skillsUpdateCheck.ts index 830d373a03..b60b5051c2 100644 --- a/packages/cli/src/utils/skillsUpdateCheck.ts +++ b/packages/cli/src/utils/skillsUpdateCheck.ts @@ -134,7 +134,7 @@ function skillsNoticeText(meta: SkillsUpdateMeta): string | null { * as the CLI self-update notice (CI, non-TTY, dev, HYPERFRAMES_NO_UPDATE_CHECK). */ export function printSkillsUpdateNotice(): void { - if (updateNoticesSuppressed()) return; + if (process.env["HYPERFRAMES_SKIP_SKILLS"] === "1" || updateNoticesSuppressed()) return; const text = skillsNoticeText(getSkillsUpdateMeta()); if (text) process.stderr.write(text); } diff --git a/skills-manifest.json b/skills-manifest.json index 858f9195d2..fd50843d28 100644 --- a/skills-manifest.json +++ b/skills-manifest.json @@ -2,87 +2,87 @@ "source": "heygen-com/hyperframes", "skills": { "embedded-captions": { - "hash": "f1b298aa899d86df", + "hash": "c6cac07dbd207deb", "files": 142 }, "faceless-explainer": { - "hash": "8b26b840d91f2b5d", + "hash": "7fda7a8bb75acabf", "files": 27 }, "figma": { - "hash": "642cce1b5e211182", + "hash": "1639b96642ac26c6", "files": 2 }, "general-video": { - "hash": "36b4d47cf01c451d", + "hash": "77938376e7cc61fc", "files": 5 }, "hyperframes": { - "hash": "a624fd0aa39bdd09", - "files": 26 + "hash": "93ee5e4fad423610", + "files": 29 }, "hyperframes-animation": { - "hash": "9add954e7eff1aa6", + "hash": "3055d5038b7fe96b", "files": 122 }, "hyperframes-audio": { - "hash": "7e9a13aba7143c08", + "hash": "ab4e6503ec360665", "files": 7 }, "hyperframes-cli": { - "hash": "f006eefddf345fd1", + "hash": "272d811ab4564fb7", "files": 11 }, "hyperframes-core": { - "hash": "b4d9c7b3d0e5ae75", + "hash": "bc03be27ef68003b", "files": 11 }, "hyperframes-creative": { - "hash": "0803c90800fda4ce", + "hash": "171a9a4540ef1d99", "files": 79 }, "hyperframes-keyframes": { - "hash": "16a7da9819e7e4ab", + "hash": "b7f56329c9ffb81a", "files": 3 }, "hyperframes-registry": { - "hash": "4a1a7daf23a9e569", + "hash": "aecbf18ab080fbfb", "files": 12 }, "hyperframes-studio": { - "hash": "b063eaa9eb1e4b9b", + "hash": "12fb9632d22e3259", "files": 1 }, "media-use": { - "hash": "d4270dba3ae09580", + "hash": "912204bb8f5fd200", "files": 100 }, "motion-graphics": { - "hash": "ab9d2e9c205ce1b1", + "hash": "3d3a35ffbe8b7e3e", "files": 23 }, "music-to-video": { - "hash": "190e9885dab9b11d", + "hash": "3c982c2ddeac43f2", "files": 169 }, "pr-to-video": { - "hash": "6ebe2e6a219d10e3", + "hash": "c62666bcc68d53e9", "files": 33 }, "product-launch-video": { - "hash": "89e5fa638fb9f2c0", + "hash": "3e5d21d03a808538", "files": 34 }, "remotion-to-hyperframes": { - "hash": "1da9494cf3a6b1f3", + "hash": "4fcef874801e58f7", "files": 77 }, "slideshow": { - "hash": "e9b8ea210cfb771e", + "hash": "365f19ebf1a562fc", "files": 2 }, "talking-head-recut": { - "hash": "7018bf017e8606d2", + "hash": "eac2082ea7096802", "files": 28 } } diff --git a/skills/embedded-captions/SKILL.md b/skills/embedded-captions/SKILL.md index e124913564..800bd86348 100644 --- a/skills/embedded-captions/SKILL.md +++ b/skills/embedded-captions/SKILL.md @@ -9,6 +9,8 @@ description: > including transcription and subject matting; split multi-shot footage before applying it. --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + > **First, keep this skill fresh — confirm with the user before running:** `npx hyperframes skills update embedded-captions`. A fast no-op when everything is current; otherwise it refreshes this skill plus the core domain skills it depends on before you rely on them. # Embedded Captions diff --git a/skills/faceless-explainer/SKILL.md b/skills/faceless-explainer/SKILL.md index e3012c3b67..aefc6e3590 100644 --- a/skills/faceless-explainer/SKILL.md +++ b/skills/faceless-explainer/SKILL.md @@ -3,6 +3,8 @@ name: faceless-explainer description: "Turn arbitrary text — an article, notes, a topic, a brief — into a faceless explainer video: there is no site or footage to capture, so the visuals are invented per scene (typography, abstract graphics, diagrams, data-viz). Use for topic explainers, concept breakdowns, how-tos, listicles. Not a video built from a website (/product-launch-video — promo or tour). Unclear → /hyperframes." --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + > **First, keep this skill fresh — confirm with the user before running:** `npx hyperframes skills update faceless-explainer`. A fast no-op when everything is current; otherwise it refreshes this skill plus the core domain skills it depends on before you rely on them. > **media-use**: Before sourcing audio/images/logos, call `/media-use` to resolve BGM/SFX/images from the HeyGen catalog and brand logos from their official sources. Run `--adopt` first to register existing assets. See `/media-use` skill. diff --git a/skills/figma/SKILL.md b/skills/figma/SKILL.md index c611e24f5f..9381ca33d4 100644 --- a/skills/figma/SKILL.md +++ b/skills/figma/SKILL.md @@ -3,6 +3,8 @@ name: figma description: Import Figma content into a HyperFrames composition — rendered assets, brand tokens, components, storyboard sections → reconstructed motion (frames read as states, not slides) (REST/CLI), connector-assisted motion when available, and shaders from a connector or native export. Use when the user pastes a figma.com link or asks to bring a Figma design, frame, logo, brand, or animation into a video/composition. --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + > **First, keep this skill fresh — confirm with the user before running:** `npx hyperframes skills update figma`. A fast no-op when everything is current; otherwise it refreshes this skill plus the core domain skills it depends on before you rely on them. # Figma → HyperFrames diff --git a/skills/general-video/SKILL.md b/skills/general-video/SKILL.md index 223880b164..d8d62368b3 100644 --- a/skills/general-video/SKILL.md +++ b/skills/general-video/SKILL.md @@ -8,6 +8,8 @@ description: > Route fresh creation through hyperframes before using this skill. --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + # General video Before relying on this workflow, run: diff --git a/skills/hyperframes-animation/SKILL.md b/skills/hyperframes-animation/SKILL.md index 5e64d30472..23ae488791 100644 --- a/skills/hyperframes-animation/SKILL.md +++ b/skills/hyperframes-animation/SKILL.md @@ -3,6 +3,8 @@ name: hyperframes-animation description: "All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). Also covers auditing an existing composition's choreography (animation map) and 24 named text-animation effects. HyperFrames-native: single paused timeline, seek-safe, deterministic." --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + # HyperFrames Animation All motion knowledge in one skill: **rules** (atomic recipes), **blueprints** (multi-phase scene templates), **transitions** (scene-to-scene), **techniques** (broader motion-design patterns), and **adapters** (per-runtime APIs). diff --git a/skills/hyperframes-audio/SKILL.md b/skills/hyperframes-audio/SKILL.md index 2fbedd407d..daa5d477a0 100644 --- a/skills/hyperframes-audio/SKILL.md +++ b/skills/hyperframes-audio/SKILL.md @@ -13,6 +13,8 @@ description: > `/hyperframes-core`. --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + # HyperFrames Audio A mix is a set of relationships, not a stack of processors. Two tracks that each diff --git a/skills/hyperframes-cli/SKILL.md b/skills/hyperframes-cli/SKILL.md index 61e43df69c..1a9b7d7072 100644 --- a/skills/hyperframes-cli/SKILL.md +++ b/skills/hyperframes-cli/SKILL.md @@ -9,6 +9,8 @@ description: > HeyGen-hosted cloud, AWS Lambda, and Google Cloud Run rendering. --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + # HyperFrames CLI Run commands as `npx hyperframes ...` unless project instructions provide a wrapper. Obey the wrapper when present. The CLI requires Node.js 22 or newer and FFmpeg. diff --git a/skills/hyperframes-core/SKILL.md b/skills/hyperframes-core/SKILL.md index f7555e09d4..a502d43df0 100644 --- a/skills/hyperframes-core/SKILL.md +++ b/skills/hyperframes-core/SKILL.md @@ -3,6 +3,8 @@ name: hyperframes-core description: The HyperFrames composition contract — build one renderable project. Use for composition structure, the `data-*` timing attributes, `class="clip"`, tracks, sub-compositions, variables, framework-owned media playback, deterministic-render rules, and validation. Read before writing composition HTML. --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + # HyperFrames Core **Agent pitfalls (read first):** diff --git a/skills/hyperframes-creative/SKILL.md b/skills/hyperframes-creative/SKILL.md index 4c41dfd7fc..abd8ef5705 100644 --- a/skills/hyperframes-creative/SKILL.md +++ b/skills/hyperframes-creative/SKILL.md @@ -3,6 +3,8 @@ name: hyperframes-creative description: Non-animation creative direction for HyperFrames videos. Use for design spec (frame.md / design.md) handling, palettes, typography, narration, beat planning, audio-reactive visuals, composition patterns, and brand / style decisions. For atomic motion patterns and scene blueprints, use `hyperframes-animation`. --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + # HyperFrames Creative Brand, pacing, style, narration, and composition direction. Use after the technical contract from `hyperframes-core` is in place. diff --git a/skills/hyperframes-keyframes/SKILL.md b/skills/hyperframes-keyframes/SKILL.md index 0640bd6c9c..1e8912e3b9 100644 --- a/skills/hyperframes-keyframes/SKILL.md +++ b/skills/hyperframes-keyframes/SKILL.md @@ -9,6 +9,8 @@ description: > general video planning. --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + # HyperFrames Keyframes Keyframes are a pose contract: visible states, continuous subject identity, seek-safe runtime, verified pixels. diff --git a/skills/hyperframes-registry/SKILL.md b/skills/hyperframes-registry/SKILL.md index 5a5096f3da..5d791b14b4 100644 --- a/skills/hyperframes-registry/SKILL.md +++ b/skills/hyperframes-registry/SKILL.md @@ -3,6 +3,8 @@ name: hyperframes-registry description: Search, install, and wire registry blocks and components into HyperFrames compositions. Use BEFORE hand-building any named visual — whenever a brief, a user, or a storyboard names a look, effect, treatment, or transition such as CRT scanlines, glitch, chromatic aberration, film grain, a shimmer sweep, a chart, a code or terminal window, a map, or a confetti burst — because roughly 400 hosted items already cover many of them and the search ranks all of them with nothing installed, no project, and no account. Also use when running hyperframes add or hyperframes catalog, installing one item or every block matching a tag, wiring an installed item into index.html, or working with hyperframes.json. Covers discovery, install locations, block sub-composition wiring, component snippet merging, and authoring a new block or component to contribute upstream (idea → scaffold → validate → PR). --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + # HyperFrames Registry The registry provides reusable blocks and components installable via `hyperframes add `. diff --git a/skills/hyperframes-studio/SKILL.md b/skills/hyperframes-studio/SKILL.md index f43c4e9746..2e198350be 100644 --- a/skills/hyperframes-studio/SKILL.md +++ b/skills/hyperframes-studio/SKILL.md @@ -9,6 +9,8 @@ description: > `creator-editing-recipes.md` in `/hyperframes-core`. --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + # HyperFrames Studio conventions Studio draws one timeline row per top-level element. A project that follows the diff --git a/skills/hyperframes/SKILL.md b/skills/hyperframes/SKILL.md index 09785aba6c..1ecd6d52f7 100644 --- a/skills/hyperframes/SKILL.md +++ b/skills/hyperframes/SKILL.md @@ -12,6 +12,8 @@ description: > browser session. --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + # HyperFrames entry point HyperFrames **renders video from HTML** — a composition is an HTML file whose DOM declares timing with `data-*` attributes, whose animation runtime is seekable, and whose media playback is owned by the framework. The full authoring contract lives in `/hyperframes-core`; read it before writing composition HTML. Brief, storyboard, review, production, dispatch, and frame-worker contracts live in this skill's `references/`. diff --git a/skills/hyperframes/references/plugin-installation.md b/skills/hyperframes/references/plugin-installation.md new file mode 100644 index 0000000000..4c51e9aa76 --- /dev/null +++ b/skills/hyperframes/references/plugin-installation.md @@ -0,0 +1,43 @@ +# Running from an installed plugin + +Check the directory two levels above the loaded `SKILL.md` for `plugin.json`, +`.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, or +`.cursor-plugin/plugin.json` identifying `hyperframes`, or a Gemini extension manifest. +That directory is ``. If none exists, this is a standalone skill; +follow the normal installation and freshness instructions. + +For a plugin installation, these rules replace the standalone update commands +in every workflow and reference: + +- Do not run `hyperframes skills update`, `skills check`, or `npx skills add`. + The agent's plugin manager owns installation and updates. Read the workflow at + `/skills//SKILL.md`; report a missing bundled skill instead + of downloading a different release. Resolve all skill references inside this + bundle, even if a standalone copy is also installed. Claude exposes the router + as `/hyperframes:hyperframes`; other clients may expose `/hyperframes`. +- Replace `npx hyperframes` (or bare `hyperframes`) in command examples with: + + ```bash + node "/skills/hyperframes/scripts/plugin-cli.mjs" + ``` + + Run in the user's project directory, never in the plugin directory. The launcher + selects the manifest's CLI version and disables automatic standalone skill + installation, including during `init`. The first call may download that exact + CLI version through npm. A download failure is an error, not permission to use + latest. Existing project dependency files and lockfiles must not be overwritten + to match the plugin; surface a runtime incompatibility before changing them. + +- For bundled Node helpers that bootstrap HyperFrames packages, use the same + launcher with `--script `. This passes the + release version to their dependency loader. External providers, registry + downloads, and existing project dependencies have their own versions; a plugin + version alone does not freeze those inputs. +- Treat the plugin directory as read-only. Put outputs and temporary work in the + project or a temporary directory. Pass this plugin root and launcher convention + to any delegated workflow so it does not fall back to global skills. + +To get newer skills, use the client's plugin update flow and reload the session. +Do not silently update an installed plugin during a video task. + +The launcher suppresses CLI update notices as well as standalone skill refreshes, including when running an older CLI with a stale notice cache. It passes the release as `HYPERFRAMES_PLUGIN_VERSION` for plugin-aware helpers and as `HYPERFRAMES_SKILL_PKG_VERSION` for the existing helper-package bootstrap. Our release process versions those packages together. diff --git a/skills/hyperframes/references/skill-lifecycle.md b/skills/hyperframes/references/skill-lifecycle.md index cf5c79d05e..7b2c4718aa 100644 --- a/skills/hyperframes/references/skill-lifecycle.md +++ b/skills/hyperframes/references/skill-lifecycle.md @@ -2,6 +2,8 @@ Read this reference when installing or updating skills, diagnosing unexpected workflow behavior, or running HyperFrames setup in CI. +For a plugin installation, follow [plugin execution rules](plugin-installation.md); the plugin manager owns updates and all workflows are bundled. The commands below apply only to standalone skills. + HyperFrames installs the core set eagerly and workflow skills lazily. - **Core set:** `/hyperframes`, the `hyperframes-*` domain skills, and `/media-use`. diff --git a/skills/hyperframes/scripts/plugin-cli.mjs b/skills/hyperframes/scripts/plugin-cli.mjs new file mode 100644 index 0000000000..4fa4e6d770 --- /dev/null +++ b/skills/hyperframes/scripts/plugin-cli.mjs @@ -0,0 +1,88 @@ +#!/usr/bin/env node +// Run from the user's project; locate the release from this installed file. +import { spawnSync } from "node:child_process"; +import { existsSync, readFileSync, realpathSync } from "node:fs"; +import { join, win32 } from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; + +const root = fileURLToPath(new URL("../../../", import.meta.url)); +const manifests = [ + "plugin.json", + ".claude-plugin/plugin.json", + ".codex-plugin/plugin.json", + ".cursor-plugin/plugin.json", + "gemini-extension.json", +]; + +export function pluginVersion(pluginRoot = root) { + for (const path of manifests) { + const file = join(pluginRoot, path); + if (!existsSync(file)) continue; + const manifest = JSON.parse(readFileSync(file, "utf8")); + if ( + manifest.name !== "hyperframes" || + !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(manifest.version ?? "") + ) { + throw new Error(`Invalid HyperFrames plugin release: ${file}`); + } + return manifest.version; + } + throw new Error( + "No HyperFrames plugin manifest found. Use the standalone skills installation instructions.", + ); +} + +export function invocation( + args, + { + version = pluginVersion(), + env = process.env, + platform = process.platform, + node = process.execPath, + pathExists = existsSync, + } = {}, +) { + if (args[0] === "skills") + throw new Error( + "Update HyperFrames through your agent's plugin manager; bundled skills are release-managed.", + ); + const childEnv = { + ...env, + HYPERFRAMES_SKIP_SKILLS: "1", + HYPERFRAMES_SKILL_PKG_VERSION: version, + HYPERFRAMES_PLUGIN_VERSION: version, + HYPERFRAMES_NO_UPDATE_CHECK: "1", + }; + if (args[0] === "--script") { + if (!args[1]) throw new Error("--script requires a Node script path."); + return { command: node, args: args.slice(1), env: childEnv }; + } + const cliArgs = ["--yes", `hyperframes@${version}`, ...args]; + if (platform !== "win32") return { command: "npx", args: cliArgs, env: childEnv }; + const candidates = [ + env.npm_execpath && win32.join(win32.dirname(env.npm_execpath), "npx-cli.js"), + win32.join(win32.dirname(node), "node_modules", "npm", "bin", "npx-cli.js"), + ].filter(Boolean); + const npx = candidates.find(pathExists); + if (!npx) + throw new Error( + "Cannot find npx-cli.js. Install Node.js with npm or run from an npm environment.", + ); + return { command: node, args: [npx, ...cliArgs], env: childEnv }; +} + +if (process.argv[1] && pathToFileURL(realpathSync(process.argv[1])).href === import.meta.url) { + try { + const command = invocation(process.argv.slice(2)); + const result = spawnSync(command.command, command.args, { + env: command.env, + stdio: "inherit", + windowsHide: true, + }); + if (result.error) throw result.error; + process.exitCode = result.status ?? 1; + } catch (error) { + console.error(error.message); + process.exitCode = 1; + } +} diff --git a/skills/hyperframes/scripts/plugin-cli.test.mjs b/skills/hyperframes/scripts/plugin-cli.test.mjs new file mode 100644 index 0000000000..df2d3e5f93 --- /dev/null +++ b/skills/hyperframes/scripts/plugin-cli.test.mjs @@ -0,0 +1,117 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { + copyFileSync, + mkdirSync, + mkdtempSync, + readFileSync, + realpathSync, + rmSync, + writeFileSync, +} from "node:fs"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; +import { test } from "node:test"; +import { invocation, pluginVersion } from "./plugin-cli.mjs"; + +function fixture(t, manifest = "plugin.json", version = "1.2.3") { + const root = mkdtempSync(join(tmpdir(), "hf-plugin-")); + t.after(() => rmSync(root, { recursive: true, force: true })); + mkdirSync(join(root, "skills/hyperframes/scripts"), { recursive: true }); + mkdirSync(join(root, manifest, ".."), { recursive: true }); + writeFileSync(join(root, manifest), JSON.stringify({ name: "hyperframes", version })); + const launcher = join(root, "skills/hyperframes/scripts/plugin-cli.mjs"); + copyFileSync(new URL("./plugin-cli.mjs", import.meta.url), launcher); + return { root, launcher }; +} + +for (const manifest of [ + "plugin.json", + ".claude-plugin/plugin.json", + ".codex-plugin/plugin.json", + ".cursor-plugin/plugin.json", + "gemini-extension.json", +]) { + test(`locates ${manifest} without repository packages`, (t) => { + const { root } = fixture(t, manifest); + assert.equal(pluginVersion(root), "1.2.3"); + }); +} + +test("rejects missing and floating plugin versions", (t) => { + const { root } = fixture(t, "plugin.json", "latest"); + assert.throws(() => pluginVersion(root), /Invalid HyperFrames plugin release/); + rmSync(join(root, "plugin.json")); + assert.throws(() => pluginVersion(root), /No HyperFrames plugin manifest/); +}); + +test("pins CLI, suppresses standalone refresh, preserves unrelated environment", () => { + const result = invocation(["init", "project with spaces", "--non-interactive"], { + version: "1.2.3", + platform: "darwin", + env: { PATH: "/bin", HYPERFRAMES_SKIP_SKILLS: "0" }, + }); + assert.equal(result.command, "npx"); + assert.deepEqual(result.args, [ + "--yes", + "hyperframes@1.2.3", + "init", + "project with spaces", + "--non-interactive", + ]); + assert.deepEqual(result.env, { + PATH: "/bin", + HYPERFRAMES_SKIP_SKILLS: "1", + HYPERFRAMES_SKILL_PKG_VERSION: "1.2.3", + HYPERFRAMES_PLUGIN_VERSION: "1.2.3", + HYPERFRAMES_NO_UPDATE_CHECK: "1", + }); + assert.throws(() => invocation(["skills", "update"], { version: "1.2.3" }), /plugin manager/); +}); + +test("Windows launches npx through Node without shell argument interpretation", () => { + const result = invocation(["render", "a & b"], { + version: "1.2.3", + platform: "win32", + node: String.raw`C:\Program Files\nodejs\node.exe`, + env: { npm_execpath: String.raw`C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js` }, + pathExists: (p) => p === String.raw`C:\Program Files\nodejs\node_modules\npm\bin\npx-cli.js`, + }); + assert.equal(result.command, String.raw`C:\Program Files\nodejs\node.exe`); + assert.deepEqual(result.args, [ + String.raw`C:\Program Files\nodejs\node_modules\npm\bin\npx-cli.js`, + "--yes", + "hyperframes@1.2.3", + "render", + "a & b", + ]); + assert.throws( + () => invocation([], { version: "1.2.3", platform: "win32", env: {}, pathExists: () => false }), + /Cannot find npx/, + ); +}); + +test("installed helper keeps cwd, arguments, release environment and exit code", (t) => { + const { root, launcher } = fixture(t); + const project = join(root, "user project"); + mkdirSync(project); + const helper = join(root, "helper.mjs"); + writeFileSync( + helper, + "console.log(JSON.stringify({ cwd: process.cwd(), args: process.argv.slice(2), skip: process.env.HYPERFRAMES_SKIP_SKILLS, version: process.env.HYPERFRAMES_SKILL_PKG_VERSION })); process.exit(7);", + ); + const before = readFileSync(join(root, "plugin.json"), "utf8"); + const result = spawnSync( + process.execPath, + [launcher, "--script", helper, "literal $value & spaces"], + { cwd: project, encoding: "utf8" }, + ); + assert.equal(result.status, 7, result.stderr); + assert.deepEqual(JSON.parse(result.stdout), { + cwd: realpathSync(project), + args: ["literal $value & spaces"], + skip: "1", + version: "1.2.3", + }); + assert.equal(readFileSync(join(root, "plugin.json"), "utf8"), before); +}); diff --git a/skills/media-use/SKILL.md b/skills/media-use/SKILL.md index 5067dab61f..4a56e599cd 100644 --- a/skills/media-use/SKILL.md +++ b/skills/media-use/SKILL.md @@ -3,6 +3,8 @@ name: media-use description: Agent Media OS, the single skill for every media need in a HyperFrames project. Resolve BGM, SFX, image, icon, brand logo, voice, color grade, or LUT into a frozen local file or paste-ready block + ledger record (one verb, `resolve`); generate via TTS / music / image models when the catalog misses; produce voiceover, transcription, captions, and background removal through one shared audio engine; operate on media (cut / reframe / transform); and reuse assets across projects. Also use for vague feedback that real footage looks dark, flat, boring, should feel retro/camcorder/print/ASCII, needs privacy, or needs a media reveal. --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + # media-use The media OS for HyperFrames: resolve · generate · operate · remember — every media type, one skill, zero context noise. diff --git a/skills/motion-graphics/SKILL.md b/skills/motion-graphics/SKILL.md index 0aba87e475..59ec607875 100644 --- a/skills/motion-graphics/SKILL.md +++ b/skills/motion-graphics/SKILL.md @@ -11,6 +11,8 @@ description: > Longer / narrated / multi-scene → /general-video. Unclear → /hyperframes. --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + > **First, keep this skill fresh — confirm with the user before running:** `npx hyperframes skills update motion-graphics`. A fast no-op when everything is current; otherwise it refreshes this skill plus the core domain skills it depends on before you rely on them. > **figma source**: If the logo/asset/animation to build from comes from a figma.com URL, run `/figma` first — asset export, brand tokens, and Motion→GSAP translation if the graphic is a Figma Motion import — then build from its output. Don't drive Figma via raw MCP tools directly: that skips SVG sanitization, `.media/manifest.jsonl` provenance, and brand-token `var()` binding, so a later brand change can't propagate without a full re-import. diff --git a/skills/music-to-video/SKILL.md b/skills/music-to-video/SKILL.md index a2e3a7c8cb..746f9eef04 100644 --- a/skills/music-to-video/SKILL.md +++ b/skills/music-to-video/SKILL.md @@ -3,6 +3,8 @@ name: music-to-video description: "Turn a music track (an audio file, a video to pull audio from, or a track generated from a mood brief) into a beat-synced video — lyric video, slideshow, or kinetic promo. The music drives all pacing; any user-supplied images/videos are cut onto the same beat grid, and a complete video needs zero assets. Narrated pieces → the input-matched workflow (see /hyperframes). Unclear → /hyperframes." --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + > **First, keep this skill fresh — confirm with the user before running:** `npx hyperframes skills update music-to-video`. A fast no-op when everything is current; otherwise it refreshes this skill plus the core domain skills it depends on before you rely on them. # music-to-video — one music-grounded, beat-synced video workflow diff --git a/skills/pr-to-video/SKILL.md b/skills/pr-to-video/SKILL.md index 3b41b85cb5..8228df491b 100644 --- a/skills/pr-to-video/SKILL.md +++ b/skills/pr-to-video/SKILL.md @@ -3,6 +3,8 @@ name: pr-to-video description: "Turn a GitHub pull request (a PR URL, owner/repo#N, or 'this PR' in a checked-out repo) into a code-change explainer video — changelog, feature reveal, fix, or refactor walkthrough built from the diff, commits, and files: the input is a code change, not a website. Not a product promo (/product-launch-video) or a no-PR topic explainer (/faceless-explainer). Unclear → /hyperframes." --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + > **First, keep this skill fresh — confirm with the user before running:** `npx hyperframes skills update pr-to-video`. A fast no-op when everything is current; otherwise it refreshes this skill plus the core domain skills it depends on before you rely on them. > **media-use**: Before sourcing audio/images/logos, call `/media-use` to resolve BGM/SFX/images from the HeyGen catalog and brand logos from their official sources. Run `--adopt` first to register existing assets. See `/media-use` skill. diff --git a/skills/product-launch-video/SKILL.md b/skills/product-launch-video/SKILL.md index 96127e2b7e..aaf576ab7d 100644 --- a/skills/product-launch-video/SKILL.md +++ b/skills/product-launch-video/SKILL.md @@ -3,6 +3,8 @@ name: product-launch-video description: "Turn a product or marketing URL, pasted script, or brief into a product launch / promo video — SaaS promos, feature reveals, product demos, app and company launches. Use when the user wants to market, launch, promote, or reveal a product; the default for any commercial URL. Site tours / showcases of a website route here too — the brief carries the show-it-as-is intent. Unclear → /hyperframes." --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + > **First, keep this skill fresh — confirm with the user before running:** `npx hyperframes skills update product-launch-video`. A fast no-op when everything is current; otherwise it refreshes this skill plus the core domain skills it depends on before you rely on them. > **media-use**: Before sourcing audio/images/logos, call `/media-use` to resolve BGM/SFX/images from the HeyGen catalog and brand logos from their official sources. Run `--adopt` first to register existing assets. See `/media-use` skill. diff --git a/skills/remotion-to-hyperframes/SKILL.md b/skills/remotion-to-hyperframes/SKILL.md index b56a8efc41..03f220825e 100644 --- a/skills/remotion-to-hyperframes/SKILL.md +++ b/skills/remotion-to-hyperframes/SKILL.md @@ -3,6 +3,8 @@ name: remotion-to-hyperframes description: 'Port an existing Remotion (React) composition''s source to HyperFrames HTML. Use ONLY on an explicit ask to port/convert/migrate/translate a Remotion source — one-way, Remotion-only. A passing Remotion mention, reference-only code, or "make something like my Remotion video" is a fresh build (/general-video). Unclear → /hyperframes.' --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + > **First, keep this skill fresh — confirm with the user before running:** `npx hyperframes skills update remotion-to-hyperframes`. A fast no-op when everything is current; otherwise it refreshes this skill plus the core domain skills it depends on before you rely on them. # Remotion to HyperFrames diff --git a/skills/slideshow/SKILL.md b/skills/slideshow/SKILL.md index 317798ffce..a1c3826873 100644 --- a/skills/slideshow/SKILL.md +++ b/skills/slideshow/SKILL.md @@ -9,6 +9,8 @@ description: > Unclear → /hyperframes. --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + > **First, keep this skill fresh — confirm with the user before running:** `npx hyperframes skills update slideshow`. A fast no-op when everything is current; otherwise it refreshes this skill plus the core domain skills it depends on before you rely on them. > **figma source**: If the deck's content or storyboard comes from a figma.com URL, run `/figma` first — asset export, brand tokens, and storyboard reconstruction if the source is a strip of scene frames — then build from its output. Don't drive Figma via raw MCP tools directly: that skips SVG sanitization, `.media/manifest.jsonl` provenance, and brand-token `var()` binding, so a later brand change can't propagate without a full re-import. diff --git a/skills/talking-head-recut/SKILL.md b/skills/talking-head-recut/SKILL.md index 128bac4c56..7784122d9a 100644 --- a/skills/talking-head-recut/SKILL.md +++ b/skills/talking-head-recut/SKILL.md @@ -3,6 +3,8 @@ name: talking-head-recut description: Package an existing talking-head / interview / podcast video with timed, designed GRAPHIC OVERLAY cards — kinetic titles, lower-thirds, data callouts, quotes, side panels, picture-in-picture — synced to the transcript, on a 16:9 / 9:16 / 4:5 canvas of your choice; the clip plays untouched underneath. Trigger on "graphic overlays", "on-screen graphics", "package / dress up my video". Not plain subtitles (/embedded-captions). Unclear → /hyperframes. --- +**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below. + > **First, keep this skill fresh — confirm with the user before running:** `npx hyperframes skills update talking-head-recut`. A fast no-op when everything is current; otherwise it refreshes this skill plus the core domain skills it depends on before you rely on them. # Talking Head Recut