From 967bba90cfb8f7bf821d68404e36553f0fb66f55 Mon Sep 17 00:00:00 2001 From: ukimsanov Date: Tue, 20 Oct 2026 02:57:38 -0400 Subject: [PATCH 1/4] docs(cli): document media-use and usage commands Both command groups are registered and visible but had no section in the CLI reference. - media-use: subcommands, resolve flags, the per-type source order, where files land, which sources need a HeyGen sign-in or can cost money, and telemetry. - usage: flags, the known and unknown result shapes, which logins it reads, and that it sends no telemetry. - Add both to the "Find your command" table. The from, reuse, and params subcommands cannot take their value in this release, so the page points to the working resolve --from, --reuse, and --params forms instead. Co-Authored-By: Claude Opus 5.5 --- docs/packages/cli.mdx | 158 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 156 insertions(+), 2 deletions(-) diff --git a/docs/packages/cli.mdx b/docs/packages/cli.mdx index 684a0a85150..386f63c3687 100644 --- a/docs/packages/cli.mdx +++ b/docs/packages/cli.mdx @@ -23,14 +23,14 @@ npm install -g hyperframes | You want to | Use | | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Start a project | [`init`](#init), [`add`](#add), [`catalog`](#catalog) | -| Bring in source material | [`capture`](#capture), [`transcribe`](#transcribe), [`tts`](#tts), [`remove-background`](#remove-background), [`media-treatment`](#media-treatment), [`beats`](#beats), [`audio`](#audio), [`normalize-audio`](#normalize-audio) | +| Bring in source material | [`capture`](#capture), [`media-use`](#media-use), [`transcribe`](#transcribe), [`tts`](#tts), [`remove-background`](#remove-background), [`media-treatment`](#media-treatment), [`beats`](#beats), [`audio`](#audio), [`normalize-audio`](#normalize-audio) | | Look at it, or share it | [`preview`](#preview), [`present`](#present-and-play), [`play`](#present-and-play), [`publish`](#publish), [`open`](#open-and-catch-up), [`catch-up`](#open-and-catch-up) | | Find problems | [`lint`](#lint), [`check`](#check), [`snapshot`](#snapshot), [`keyframes`](#keyframes), [`compare`](#compare-and-grade-compare), [`grade-compare`](#compare-and-grade-compare) | | Edit from the terminal | [`timeline`](#timeline), [`history`](#history) | | Make a file | [`render`](#render), [`benchmark`](#benchmark) | | Render somewhere else | [`cloud`](#hyperframes-cloud), [`lambda`](#hyperframes-lambda), [`cloudrun`](#hyperframes-cloudrun) | | Fix your setup | [`doctor`](#doctor), [`browser`](#browser), [`upgrade`](#upgrade), [`docs`](#docs), [`info`](#info-and-compositions), [`compositions`](#info-and-compositions) | -| Connect other tools | [`auth`](#hyperframes-auth), [`skills`](#skills), [`figma`](#figma-and-events), [`telemetry`](#telemetry), [`feedback`](#feedback) | +| Connect other tools | [`auth`](#hyperframes-auth), [`skills`](#skills), [`usage`](#usage), [`figma`](#figma-and-events), [`telemetry`](#telemetry), [`feedback`](#feedback) | `validate`, `inspect`, and `layout` still work but are [deprecated](#deprecated-validate-inspect-and-layout). Use `check` instead. @@ -496,6 +496,110 @@ The command writes `data-color-grading` — the same contract Studio, preview, a render all read, so the two can never disagree. It does not recognise subjects or isolate part of an image; the effect covers the whole media layer. +### `media-use` + +Get the music, sound effects, images, icons, logos, voiceover, and color grades +a video needs, saved as files inside the project. Every asset is recorded, so +asking for the same thing again reuses the file instead of fetching it twice. +The `media-use` skill drives this command; you can also run it yourself. + +```bash +# Find or make one asset and save it in the project +npx hyperframes media-use resolve --type sfx --intent "whoosh" +npx hyperframes media-use resolve --type logo --entity linkedin --intent "LinkedIn logo" +npx hyperframes media-use resolve --type grade --intent "warm daylight" --json + +# Look at what you already have before fetching more +npx hyperframes media-use candidates --type bgm --intent "upbeat tech launch" +npx hyperframes media-use resolve --type bgm --reuse 06e052c0 + +# Bring in a file you already have +npx hyperframes media-use resolve --type image --from ./photo.jpg + +# Check the setup, and see what has been used +npx hyperframes media-use doctor +npx hyperframes media-use stats --days 7 +``` + +`resolve` prints one line, such as +`resolved sfx_001 → .media/audio/sfx/sfx_001.mp3 (sfx, 0.6s)`, or the whole +record with `--json`. It never edits your composition HTML; you reference the +file yourself. + +| Subcommand | What it does | +| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `resolve` | Find, make, or import one asset. Takes every flag below | +| `candidates` | List assets already in the project and the global cache for `--type`, best match for `--intent` first. Downloads nothing and changes nothing. Same as `resolve --candidates` | +| `adopt` | Record the files already in `assets/` so later requests find them, with duration and size read by `ffprobe` | +| `analyze` | Measure an image or video and print suggested exposure and color corrections, saving nothing. Needs `--type grade --for ` | +| `doctor` | Check ffmpeg, ffprobe, Node.js, the bundled sound library, and the `heygen` CLI (installed, v0.3.0 or newer, signed in). Changes nothing | +| `stats` | Count requests, misses, and reuse from the project's `.media/` and the global `~/.media/` | + +`from`, `reuse`, and `params` also appear in `media-use --help`, but in this +release they cannot take their value. Use `resolve --from`, `resolve --reuse`, +and `resolve --params` instead. + +| Flag | Description | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `--type` | Media type, from the table below. Required for every subcommand except `doctor`, `stats`, and `adopt` | +| `--intent` | What you need, in plain words. For `voice` and `video` it is the text to speak | +| `--entity` | A name such as a brand (`linkedin`), so a later request for the same thing reuses the file | +| `--project, -p` | Project directory (default: current directory) | +| `--candidates` | List reusable assets instead of resolving. `--dry-run` does the same | +| `--reuse ` | Copy one global-cache asset into this project, by the content hash or prefix that `candidates` prints | +| `--from ` | Import a local file or a direct public media URL. Pages such as YouTube links are refused | +| `--params ` | Build a LUT from explicit values, such as `'{"contrast":0.2}'`. `lut` and `grade` only | +| `--for ` | Measure this image or video and add the suggested corrections to the grade. `grade` only | +| `--analyze` | With `--type grade --for`, print the measurement and stop | +| `--local-only` | Skip every network provider. Only the caches and tools installed on this machine are used | +| `--provider ` | Use only this provider and skip reuse: for example `heygen`, `codex`, `mflux`, `kokoro`. An unknown name fails and lists valid ones | +| `--voice-id` | HeyGen voice for `voice` and `video` (default: the first voice the catalog lists) | +| `--avatar-id` | HeyGen avatar for `video` (default: the first public avatar) | +| `--days ` | With `stats`, count only the last `n` days | +| `--json` | Print JSON instead of one line | + +Where each type comes from, tried in order until one succeeds: + +| `--type` | You get | Sources | +| -------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| `bgm` | Background music | HeyGen music catalog | +| `sfx` | Sound effects | Openverse (CC0 or CC BY, no account), then a bundled library that works offline | +| `image` | Photos and backgrounds | HeyGen asset search, then generated by mflux on this machine, then by the `codex` CLI | +| `icon` | Icons | HeyGen asset search | +| `logo` | Official brand marks | theSVG, then the GitHub organization avatar, then the site's favicon | +| `voice` | Spoken audio of the `--intent` text | HeyGen text to speech, then Kokoro on this machine | +| `video` | An avatar speaking the `--intent` text | HeyGen avatar video, then LTX on this machine when it is enabled | +| `brand` | Brand colors, font, and logo | The project's own `frame.md` or `design.md`. Never the network | +| `grade` | A color-grading block for `data-color-grading` | A built-in preset, a look-library LUT, or a LUT built from the words in `--intent` | +| `lut` | A `.cube` LUT file | A look-library LUT, or one built from the words in `--intent` | + +Before it fetches anything, `resolve` reuses an exact earlier match: the same +`--type` and `--intent` (or `--entity`) in this project, a file in `assets/` +whose name shares a word with the intent, then the same request in the global +cache at `~/.media/`. `--provider` skips this. New files land in +`.media//`, are listed in `.media/manifest.jsonl` and `.media/index.md`, +and are copied to `~/.media/` so other projects can reuse them. Sound effects +under CC BY get their credit written to `.media/audio/sfx/CREDITS.md`. + +**What needs an account, and what can cost money.** The HeyGen sources run +through the separate `heygen` CLI, signed in with `heygen auth login --oauth`. +Searching the music, image, and icon catalogs is free. Text to speech and avatar +video draw on a free allowance when you are signed in with OAuth, then on your +HeyGen billing, so an agent should ask before generating them. When a +host app sets `HEYGEN_API_BASE` and `HEYGEN_API_KEY`, HeyGen calls go through that +host instead and are charged to its key. The `codex` image fallback uses your +ChatGPT subscription. Openverse, theSVG, GitHub, favicons, color grades, and the +local generators cost nothing. `doctor` exits 1 only when ffmpeg, ffprobe, or the +bundled sounds are missing; HeyGen problems are listed with a fix but do not fail it. + +`media-use` sends anonymous usage events — the media type, which source won, and +whether it reused a file, never your intent text or paths — and honors the same +opt-outs as [`telemetry`](#telemetry). + +See [Voice, sound, and captions](/guides/voice-and-audio) and +[Color grading](/guides/color-grading) for the workflows around these assets, and +[Authentication](/guides/authentication) for HeyGen credentials. + ### `beats` Find the beats in a composition's music and write them where Studio expects @@ -1685,6 +1789,56 @@ If you called the upstream command directly instead, set it yourself: GIT_CLONE_PROTECTION_ACTIVE=0 npx skills add heygen-com/hyperframes ``` +### `usage` + +See how much of your AI coding subscription is left — Claude Code, Codex, or +Grok — so an agent can size a video to the allowance before it starts. The +HyperFrames skills run it at the start of a video and again before rendering. + +```bash +npx hyperframes usage +npx hyperframes usage --json +npx hyperframes usage --harness codex --json +``` + +| Flag | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `--harness` | `claude-code`, `codex`, or `grok`. Default: the agent running the command, detected from `CLAUDECODE`, `CODEX_THREAD_ID`, or `GROK_AGENT=1` | +| `--json` | Print the result as one line of JSON | + +```json +{ + "status": "known", + "harness": "claude-code", + "session": { "usedPercent": 42, "remainingPercent": 58, "resetsAt": "2026-10-20T18:00:00Z" }, + "weekly": { "usedPercent": 20, "remainingPercent": 80, "resetsAt": "2026-10-24T09:00:00Z" }, + "spend": null, + "planTier": "max" +} +``` + +`session` is the short window (five hours on Claude Code and Codex) and `weekly` +the weekly one; a window the plan does not have is `null`. Grok reports only +`weekly`. `spend` appears only for spend-metered Claude Code plans, such as +Enterprise: the share of the billing period's spend limit already used. +`planTier` is the subscription name when the provider gives one. + +Without `--json`, a known result prints this object indented. When it cannot +tell, it says so rather than guessing: `Usage unknown (no_subscription_login).`, +or `{"status":"unknown","reason":"no_subscription_login"}` with `--json`. Other reasons include +`expired_login`, `unsupported_auth` (for example Claude Code on an API key), +`unsupported_harness`, `usage_request_failed`, and `http_`. The command +exits 0 either way, so check `status`. + +It reads the login your agent already has: for Claude Code the macOS keychain or +`~/.claude/.credentials.json`, for Codex `~/.codex/auth.json`, for Grok +`~/.grok/auth.json` (`CLAUDE_CONFIG_DIR`, `CODEX_HOME`, `GROK_HOME`, and +`GROK_AUTH_PATH` move them). +It never refreshes or rewrites that login and never prints the token. The token +goes only to that provider's own usage endpoint. The command sends no telemetry +and skips the update check. The numbers are a snapshot: they do not reserve +allowance or estimate what the next video will cost. + ### `figma` and `events` `npx hyperframes figma` imports through the Figma REST API, with three From b8070073785398d7754cae45d8291520f7889b04 Mon Sep 17 00:00:00 2001 From: ukimsanov Date: Tue, 20 Oct 2026 04:10:37 -0400 Subject: [PATCH 2/4] docs(cli): drop the media-use bug workaround note The bugs go to the media-use owner instead of living in the docs. Co-Authored-By: Claude Opus 5.5 --- docs/packages/cli.mdx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/packages/cli.mdx b/docs/packages/cli.mdx index 386f63c3687..edca7a10d73 100644 --- a/docs/packages/cli.mdx +++ b/docs/packages/cli.mdx @@ -535,10 +535,6 @@ file yourself. | `doctor` | Check ffmpeg, ffprobe, Node.js, the bundled sound library, and the `heygen` CLI (installed, v0.3.0 or newer, signed in). Changes nothing | | `stats` | Count requests, misses, and reuse from the project's `.media/` and the global `~/.media/` | -`from`, `reuse`, and `params` also appear in `media-use --help`, but in this -release they cannot take their value. Use `resolve --from`, `resolve --reuse`, -and `resolve --params` instead. - | Flag | Description | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `--type` | Media type, from the table below. Required for every subcommand except `doctor`, `stats`, and `adopt` | From 204ee17ea46af2540c312aaf4fc16ee1b4d5baea Mon Sep 17 00:00:00 2001 From: ukimsanov Date: Tue, 20 Oct 2026 04:34:57 -0400 Subject: [PATCH 3/4] docs(cli): list every command in the finder, and check it in CI A ~20-line check fails the docs workflow when a CLI command is missing from "Find your command". On main it finds media-use, usage, models, clean and events. AGENTS.md and CLAUDE.md ask agents to ship docs with the change. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/docs.yml | 9 +++++++++ AGENTS.md | 1 + CLAUDE.md | 1 + docs/packages/cli.mdx | 6 +++--- scripts/check-docs-cli-commands.mjs | 20 ++++++++++++++++++++ 5 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 scripts/check-docs-cli-commands.mjs diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9417b77a0f9..69bce6b8819 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,6 +16,9 @@ on: - "DOCS_GUIDELINES.md" - "packages/core/schemas/**" - "scripts/sync-schemas.ts" + # A new CLI command must be listed in the docs (check-docs-cli-commands.mjs). + - "packages/cli/src/cli.ts" + - "scripts/check-docs-cli-commands.mjs" push: branches: [main] paths: @@ -23,6 +26,9 @@ on: - "DOCS_GUIDELINES.md" - "packages/core/schemas/**" - "scripts/sync-schemas.ts" + # A new CLI command must be listed in the docs (check-docs-cli-commands.mjs). + - "packages/cli/src/cli.ts" + - "scripts/check-docs-cli-commands.mjs" concurrency: group: docs-${{ github.ref }} @@ -43,6 +49,9 @@ jobs: - name: Check schema mirror (core → docs) run: npx tsx scripts/sync-schemas.ts --check + - name: Check every CLI command is listed in the docs + run: node scripts/check-docs-cli-commands.mjs + - name: Validate build working-directory: docs run: npx mint validate diff --git a/AGENTS.md b/AGENTS.md index 74a602b8c41..19c3afb788d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -104,6 +104,7 @@ skills/ → AI agent skill definitions - **Compositions**: HTML files with `data-*` attributes. Clips need `class="clip"`. Register one paused GSAP root timeline per composition on `window.__timelines`. Scene timelines manually added to that root must not be paused, or they will not advance when the root is seeked. - **Frame Adapters**: Animation runtimes plug in via the seek-by-frame adapter pattern. GSAP is the primary adapter. - **Deterministic rendering**: No `Date.now()`, no unseeded `Math.random()`, no render-time network fetches. +- **Docs ship with the change**: a PR that adds or changes a CLI command, flag, or user-visible behavior updates `docs/` in the same PR. A new command also goes in "Find your command" in `docs/packages/cli.mdx` (CI checks it). ## Documentation diff --git a/CLAUDE.md b/CLAUDE.md index e0d9b9f7a19..7c707a7567d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -117,6 +117,7 @@ skills/ → AI agent skill definitions - **Compositions**: HTML files with `data-*` attributes. Clips need `class="clip"`. Register one paused GSAP root timeline per composition on `window.__timelines`. Scene timelines manually added to that root must not be paused, or they will not advance when the root is seeked. - **Frame Adapters**: Animation runtimes plug in via the seek-by-frame adapter pattern. GSAP is the primary adapter. - **Deterministic rendering**: No `Date.now()`, no unseeded `Math.random()`, no render-time network fetches. +- **Docs ship with the change**: a PR that adds or changes a CLI command, flag, or user-visible behavior updates `docs/` in the same PR. A new command also goes in "Find your command" in `docs/packages/cli.mdx` (CI checks it). ## Documentation diff --git a/docs/packages/cli.mdx b/docs/packages/cli.mdx index edca7a10d73..3be217a97cf 100644 --- a/docs/packages/cli.mdx +++ b/docs/packages/cli.mdx @@ -23,14 +23,14 @@ npm install -g hyperframes | You want to | Use | | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Start a project | [`init`](#init), [`add`](#add), [`catalog`](#catalog) | -| Bring in source material | [`capture`](#capture), [`media-use`](#media-use), [`transcribe`](#transcribe), [`tts`](#tts), [`remove-background`](#remove-background), [`media-treatment`](#media-treatment), [`beats`](#beats), [`audio`](#audio), [`normalize-audio`](#normalize-audio) | +| Bring in source material | [`capture`](#capture), [`media-use`](#media-use), [`transcribe`](#transcribe), [`tts`](#tts), [`remove-background`](#remove-background), [`media-treatment`](#media-treatment), [`beats`](#beats), [`audio`](#audio), [`normalize-audio`](#normalize-audio), [`models`](#models) | | Look at it, or share it | [`preview`](#preview), [`present`](#present-and-play), [`play`](#present-and-play), [`publish`](#publish), [`open`](#open-and-catch-up), [`catch-up`](#open-and-catch-up) | | Find problems | [`lint`](#lint), [`check`](#check), [`snapshot`](#snapshot), [`keyframes`](#keyframes), [`compare`](#compare-and-grade-compare), [`grade-compare`](#compare-and-grade-compare) | | Edit from the terminal | [`timeline`](#timeline), [`history`](#history) | | Make a file | [`render`](#render), [`benchmark`](#benchmark) | | Render somewhere else | [`cloud`](#hyperframes-cloud), [`lambda`](#hyperframes-lambda), [`cloudrun`](#hyperframes-cloudrun) | -| Fix your setup | [`doctor`](#doctor), [`browser`](#browser), [`upgrade`](#upgrade), [`docs`](#docs), [`info`](#info-and-compositions), [`compositions`](#info-and-compositions) | -| Connect other tools | [`auth`](#hyperframes-auth), [`skills`](#skills), [`usage`](#usage), [`figma`](#figma-and-events), [`telemetry`](#telemetry), [`feedback`](#feedback) | +| Fix your setup | [`doctor`](#doctor), [`browser`](#browser), [`upgrade`](#upgrade), [`docs`](#docs), [`info`](#info-and-compositions), [`compositions`](#info-and-compositions), [`clean`](#clean) | +| Connect other tools | [`auth`](#hyperframes-auth), [`skills`](#skills), [`usage`](#usage), [`figma`](#figma-and-events), [`events`](#figma-and-events), [`telemetry`](#telemetry), [`feedback`](#feedback) | `validate`, `inspect`, and `layout` still work but are [deprecated](#deprecated-validate-inspect-and-layout). Use `check` instead. diff --git a/scripts/check-docs-cli-commands.mjs b/scripts/check-docs-cli-commands.mjs new file mode 100644 index 00000000000..ad4415c614a --- /dev/null +++ b/scripts/check-docs-cli-commands.mjs @@ -0,0 +1,20 @@ +// Every CLI command must be listed in "Find your command" in docs/packages/cli.mdx, so a new command +// cannot ship undocumented (media-use and usage did). Commands are read from cli.ts's commandLoaders. +import { readFileSync } from "node:fs"; + +const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), "utf8"); +const cli = read("packages/cli/src/cli.ts"); +const start = cli.indexOf("const commandLoaders = {"); +const commands = [...cli.slice(start, cli.indexOf("\n};", start)).matchAll(/^ {2}"?([a-z][a-z-]*)"?:/gm)].map((m) => m[1]); +if (commands.length === 0) throw new Error("cli.ts no longer declares `const commandLoaders = { ... };`"); + +const docs = read("docs/packages/cli.mdx"); +const from = docs.indexOf("## Find your command"); +const finder = docs.slice(from, docs.indexOf("\n## ", from + 1)); +const missing = commands.filter((command) => !finder.includes(`\`${command}\``)); + +if (missing.length > 0) { + console.error(`Add these commands to "Find your command" in docs/packages/cli.mdx, with a section: ${missing.join(", ")}`); + process.exit(1); +} +console.log(`All ${commands.length} CLI commands are listed in the docs.`); From 676bec0f56a4321177b0a491f9305c2e5eb6441a Mon Sep 17 00:00:00 2001 From: ukimsanov Date: Wed, 21 Oct 2026 19:06:23 -0400 Subject: [PATCH 4/4] chore: format the docs command check --- scripts/check-docs-cli-commands.mjs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/scripts/check-docs-cli-commands.mjs b/scripts/check-docs-cli-commands.mjs index ad4415c614a..90ff35c4e46 100644 --- a/scripts/check-docs-cli-commands.mjs +++ b/scripts/check-docs-cli-commands.mjs @@ -5,8 +5,11 @@ import { readFileSync } from "node:fs"; const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), "utf8"); const cli = read("packages/cli/src/cli.ts"); const start = cli.indexOf("const commandLoaders = {"); -const commands = [...cli.slice(start, cli.indexOf("\n};", start)).matchAll(/^ {2}"?([a-z][a-z-]*)"?:/gm)].map((m) => m[1]); -if (commands.length === 0) throw new Error("cli.ts no longer declares `const commandLoaders = { ... };`"); +const commands = [ + ...cli.slice(start, cli.indexOf("\n};", start)).matchAll(/^ {2}"?([a-z][a-z-]*)"?:/gm), +].map((m) => m[1]); +if (commands.length === 0) + throw new Error("cli.ts no longer declares `const commandLoaders = { ... };`"); const docs = read("docs/packages/cli.mdx"); const from = docs.indexOf("## Find your command"); @@ -14,7 +17,9 @@ const finder = docs.slice(from, docs.indexOf("\n## ", from + 1)); const missing = commands.filter((command) => !finder.includes(`\`${command}\``)); if (missing.length > 0) { - console.error(`Add these commands to "Find your command" in docs/packages/cli.mdx, with a section: ${missing.join(", ")}`); + console.error( + `Add these commands to "Find your command" in docs/packages/cli.mdx, with a section: ${missing.join(", ")}`, + ); process.exit(1); } console.log(`All ${commands.length} CLI commands are listed in the docs.`);