Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ jobs:
code: ${{ steps.filter.outputs.code }}
cli: ${{ steps.filter.outputs.cli }}
skills: ${{ steps.filter.outputs.skills }}
codex_plugin: ${{ steps.filter.outputs.codex_plugin }}
steps:
# Force git-based change detection instead of the pull_request REST API.
# The API path can fail the whole workflow on transient listFiles
Expand Down Expand Up @@ -68,6 +69,13 @@ jobs:
- "scripts/check-skill-mirror.mjs"
- "package.json"
- ".github/workflows/ci.yml"
codex_plugin:
- ".codex-plugin/**"
- "assets/**"
- "skills/**"
- "scripts/package-codex-plugin.mjs"
- "package.json"
- ".github/workflows/ci.yml"

build:
name: Build
Expand Down Expand Up @@ -339,6 +347,20 @@ jobs:
- name: Verify .claude/skills/ and .agents/skills/ are byte-identical
run: node scripts/check-skill-mirror.mjs

codex-plugin-package:
name: "Codex plugin package"
needs: changes
if: needs.changes.outputs.codex_plugin == 'true'
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
- name: Build upload-ready Codex plugin
run: node scripts/package-codex-plugin.mjs

cli-npx-shim:
name: "CLI: npx shim (${{ matrix.os }})"
needs: changes
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ Default to the **core set** — the router installs each creation workflow on de

Installs stay lean after that: `npx hyperframes init` keeps the **core set** fresh (the router, the `hyperframes-*` domain skills, and `media-use` — plus whatever is already installed; `/figma` stays on demand) and never expands a partial install; the creation workflows install **on demand** — the router runs `npx hyperframes skills update <workflow>` before entering one. Nothing re-pulls the full set behind your back.

### Upload to Codex

Build the upload-ready Codex plugin archive from the committed `HEAD` version of the manifest, brand assets, and skills:

```bash
bun run package:codex-plugin
```

This writes `dist/hyperframes-plugin.zip` with a `hyperframes/` root folder and fails if the archive exceeds Codex's 100 MB upload limit.

### Router

| Skill | Use when |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ When SFX resolution succeeds through the bundled fallback because the HeyGen CLI
- Reuse the existing HeyGen CLI error classification produced by the failed catalog provider call.
- Retain only the latest actionable `not_found` or `outdated` remediation for the current resolver process.
- When `bundled.sfx` wins after that failure, include a structured advisory in JSON output and print the same concise hint in human output.
- Use the existing canonical commands:
- Missing: `curl -fsSL https://static.heygen.ai/cli/install.sh | bash && heygen auth login --oauth`
- Use the existing canonical remediation:
- Missing: install the [HeyGen CLI](https://developers.heygen.com/cli), then run `heygen auth login --oauth`
- Outdated: `heygen update`
- Do not emit this advisory for authentication, quota, network, or legitimate empty-catalog results; `--local-only`; or an explicitly forced bundled provider.
- Do not execute the install or update command.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"test:skills": "node --test 'skills/**/*.test.mjs'",
"generate:previews": "tsx scripts/generate-template-previews.ts",
"generate:catalog-previews": "tsx scripts/generate-catalog-previews.ts",
"package:codex-plugin": "node scripts/package-codex-plugin.mjs",
"upload:docs-images": "bash scripts/upload-docs-images.sh",
"prepare": "test -d .git && lefthook install || true"
},
Expand Down
67 changes: 67 additions & 0 deletions scripts/package-codex-plugin.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env node

import { execFileSync, spawnSync } from "node:child_process";
import { mkdirSync, readFileSync, rmSync, statSync } from "node:fs";
import { join } from "node:path";

const REPO_ROOT = join(import.meta.dirname, "..");
const OUTPUT = join(REPO_ROOT, "dist", "hyperframes-plugin.zip");
// Codex reports its upload limit in decimal MB.
const MAX_UPLOAD_BYTES = 100 * 1_000_000;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Nit — decimal MB vs binary MiB. 100 * 1_000_000 = 100,000,000 bytes (decimal MB). If Codex actually enforces 100 MiB (104,857,600 bytes = binary), this script rejects at ~95.4 MiB, which is a ~4.9 MB conservative margin. If Codex enforces exactly 100 MB (decimal) then the script is precise. Suggest confirming the exact byte value from Codex docs and adding a comment linking the source — a future contributor shouldn't have to re-derive this. — Rames D Jusso

const pluginManifest = JSON.parse(readFileSync(join(REPO_ROOT, ".codex-plugin", "plugin.json")));
const assetPaths = [
...new Set(
Object.values(pluginManifest.interface)
.filter((value) => typeof value === "string")
.map((value) => value.replace(/^\.\//, ""))
.filter((value) => value.startsWith("assets/")),
),
].sort();
if (assetPaths.some((assetPath) => assetPath.split("/").includes(".."))) {
throw new Error("Plugin manifest contains an unsafe asset path.");
}
const PLUGIN_PATHS = [".codex-plugin", ...assetPaths, "skills"];

for (const pluginPath of PLUGIN_PATHS) {
execFileSync("git", ["cat-file", "-e", `HEAD:${pluginPath}`], {
cwd: REPO_ROOT,
stdio: "inherit",
});
}

const dirtyCheck = spawnSync("git", ["diff", "--quiet", "HEAD", "--", ...PLUGIN_PATHS], {
cwd: REPO_ROOT,
});
if (dirtyCheck.error || (dirtyCheck.status !== 0 && dirtyCheck.status !== 1)) {
throw dirtyCheck.error ?? new Error("Unable to inspect the plugin working tree state.");
}
if (dirtyCheck.status === 1) {
console.warn("Warning: packaging committed HEAD; uncommitted plugin changes are excluded.");
}

mkdirSync(join(REPO_ROOT, "dist"), { recursive: true });

execFileSync(
"git",
[
"archive",
"--format=zip",
"--prefix=hyperframes/",
"--output",
OUTPUT,
"HEAD",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question — git archive HEAD intentionally ignores the working tree; how does this compose with scripts/set-version.ts? README notes the archive is built from committed HEAD, which is the correct default for a release-artifact packager. But if the release flow ever pairs this with scripts/set-version.ts (bump version → package → upload) and someone forgets to commit between the bump and the pack, the uploaded archive carries the previous version. A one-line dirty-tree warning (git diff --quiet HEAD -- .codex-plugin skills assets, non-fatal) would make the failure mode loud without changing the intentional HEAD semantic. Fine either way — just wanted to make sure the composition is intentional. — Rames D Jusso

"--",
...PLUGIN_PATHS,
],
{ cwd: REPO_ROOT, stdio: "inherit" },
);

const bytes = statSync(OUTPUT).size;
if (bytes > MAX_UPLOAD_BYTES) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Concern — 100 MB guard has no CI enforcement, only manual invocation. The throw fires only when someone runs bun run package:codex-plugin locally. If a future skill lands a big fixture (HEVC test asset, PNG-sequence render sample, model weights) that pushes the archive past 100 MB, the regression is invisible until the next manual upload — which might be days or weeks after merge, at which point bisecting to the guilty PR is annoying. Suggest wiring a lightweight CI step (in ci.yml or a nightly job) that runs bun run package:codex-plugin and asserts the size ceiling. Even a PR-time invocation would give a signal. Non-blocking because the current archive is 11.7 MB — plenty of headroom — but the guard's value is proportional to how often it runs. — Rames D Jusso

rmSync(OUTPUT);
throw new Error(
`Codex plugin archive is ${(bytes / 1_000_000).toFixed(1)} MB; the upload limit is 100 MB.`,
);
}

console.log(`Wrote ${OUTPUT} (${(bytes / 1_000_000).toFixed(1)} MB).`);
24 changes: 12 additions & 12 deletions skills-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,39 @@
"source": "heygen-com/hyperframes",
"skills": {
"embedded-captions": {
"hash": "cabda69328a29062",
"files": 144
"hash": "a30d5691b4389ec1",
"files": 140
},
"faceless-explainer": {
"hash": "ff47c02598dc7947",
"hash": "d53bcc3cbcfaa7bb",
"files": 22
},
"figma": {
"hash": "0e6e96f5a76ff824",
"hash": "517e4dc53c13ea05",
"files": 2
},
"general-video": {
"hash": "94fd399a0dd0b6ce",
"hash": "3303742225924078",
"files": 4
},
"hyperframes": {
"hash": "ab268ec1eca15ae9",
"files": 17
},
"hyperframes-animation": {
"hash": "b4adace47d4fa1b1",
"hash": "3d9855346af7d51c",
"files": 121
},
"hyperframes-cli": {
"hash": "b1a0725560016894",
"files": 11
},
"hyperframes-core": {
"hash": "23f64febbc5cae37",
"hash": "85004c9571ed19ce",
"files": 19
},
"hyperframes-creative": {
"hash": "b9e2cbfa49e6ed6c",
"hash": "bacb5205ea5eb3d8",
"files": 78
},
"hyperframes-keyframes": {
Expand All @@ -46,7 +46,7 @@
"files": 10
},
"media-use": {
"hash": "b10751149c9ea1da",
"hash": "f7f0ad56a0477a09",
"files": 145
},
"motion-graphics": {
Expand All @@ -58,11 +58,11 @@
"files": 132
},
"pr-to-video": {
"hash": "d37f9f5411c34089",
"hash": "17ed27aa2cc32501",
"files": 29
},
"product-launch-video": {
"hash": "9557c9b86f4239b9",
"hash": "84b33f077fff496d",
"files": 26
},
"remotion-to-hyperframes": {
Expand All @@ -74,7 +74,7 @@
"files": 2
},
"talking-head-recut": {
"hash": "0d0ede12041c51b7",
"hash": "2f5d99f823c48e75",
"files": 28
}
}
Expand Down
3 changes: 0 additions & 3 deletions skills/embedded-captions/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,3 @@ matte.fps
safe-zones.json

.DS_Store
# CDPR fan-kit source SVG (528KB) — only its extracted metrics
# (assets/brand/cyberpunk-widths.json) are consumed at runtime.
assets/brand/Cyberpunk2077.svg
Loading
Loading