mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+22
-22
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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):**
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 <name>`.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/`.
|
||||
|
||||
@@ -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 `<PLUGIN_ROOT>`. 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
|
||||
`<PLUGIN_ROOT>/skills/<name>/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 "<PLUGIN_ROOT>/skills/hyperframes/scripts/plugin-cli.mjs" <command> <args...>
|
||||
```
|
||||
|
||||
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 <absolute-script-path> <args...>`. 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.
|
||||
@@ -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`.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user