Files
hyperframes/CLAUDE.md
T
James Russo 8228932e17 fix(scripts): make release change-guard robust to git status prefix (#1198)
The set-version guard parsed `git status --porcelain` and extracted the
path with a fixed `line.slice(3)`. The porcelain "XY <path>" prefix width
can shift, and when it did the slice dropped a leading character —
misreading `.claude-plugin/plugin.json` as `claude-plugin/plugin.json`,
which failed the allowed-paths match and falsely blocked a legitimate
release with "Unexpected uncommitted changes". There was no escape hatch.

Collect changed paths from `git diff --name-only -z HEAD` (tracked) plus
`git ls-files --others --exclude-standard -z` (untracked) instead. Both
emit bare NUL-separated repo-relative paths with no status column to
misparse, so the allowed-paths comparison is exact. Extract the pure
helpers (splitNulList, findUnexpectedChanges) and cover them with tests.

Also document the release flow in CLAUDE.md (the repo had no release docs).
2026-06-04 00:58:09 -07:00

107 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Hyperframes
Open-source video rendering framework: write HTML, render video.
```
packages/
cli/ → hyperframes CLI (create, preview, lint, render)
core/ → Types, parsers, generators, linter, runtime, frame adapters
engine/ → Seekable page-to-video capture engine (Puppeteer + FFmpeg)
player/ → Embeddable <hyperframes-player> web component
producer/ → Full rendering pipeline (capture + encode + audio mix)
studio/ → Browser-based composition editor UI
```
## Development
```bash
bun install # Install dependencies
bun run build # Build all packages
bun run test # Run tests
```
**This repo uses bun**, not pnpm. Do NOT run `pnpm install` — it creates a `pnpm-lock.yaml` that should not exist. Workspace linking relies on bun's resolution from `"workspaces"` in root `package.json`.
### Linting & Formatting
This project uses **oxlint** and **oxfmt** (not biome, not eslint, not prettier).
```bash
bunx oxlint <files> # Lint
bunx oxfmt <files> # Format (write)
bunx oxfmt --check <files> # Format (check only, used by pre-commit hook)
```
Always run both on changed files before committing. The lefthook pre-commit hook runs `bunx oxlint` and `bunx oxfmt --check` automatically.
### Adding CLI Commands
When adding a new CLI command:
1. Define the command in `packages/cli/src/commands/<name>.ts` using `defineCommand` from citty
2. **Export `examples`** in the same file — `export const examples: Example[] = [...]` (import `Example` from `./_examples.js`). These are displayed by `--help`.
3. Register it in `packages/cli/src/cli.ts` under `subCommands` (lazy-loaded)
4. **Add to help groups** in `packages/cli/src/help.ts` — add the command name and description to the appropriate `GROUPS` entry. Without this, the command won't appear in `hyperframes --help` even though it works.
5. **Document it** in `docs/packages/cli.mdx` — add a section with usage examples and flags.
6. Validate by running `npx tsx packages/cli/src/cli.ts --help` (command appears in the list) and `npx tsx packages/cli/src/cli.ts <name> --help` (examples appear).
### Regression Test Golden Baselines (producer)
`packages/producer/tests/<name>/output/output.mp4` baselines MUST be generated
inside `Dockerfile.test`, not on your host. CI renders inside that Docker image
with a specific Chrome + ffmpeg build; pixel-level output drifts across
different host Chrome/ffmpeg versions and will fail PSNR at dozens of
checkpoints even when the code is correct.
```bash
# Build the test image once:
docker build -t hyperframes-producer:test -f Dockerfile.test .
# Generate or update a baseline (runs the harness with --update inside Docker):
bun run --cwd packages/producer docker:test:update <test-name>
```
Never run `bun run --cwd packages/producer test:update` directly from the
host to capture a baseline that will be committed — the resulting output.mp4
will not match CI. Use it only for local-only experimentation.
## Releasing
All eight packages share one version. `.github/workflows/publish.yml` publishes
them when a `v*` tag is pushed (it also accepts a manual `workflow_dispatch` with
a version input). The CLI publishes as the unscoped `hyperframes` package; the
other seven as `@hyperframes/*`.
Cut a release from `main`:
```bash
bun run release:prepare <version> # e.g. 0.6.72
```
Run it twice:
1. **First run** writes `releases/v<version>.md` and prepends an entry to
`docs/changelog.mdx`, then stops with a non-zero exit. Edit both files: write
the 12 sentence summary and remove the `<!-- TODO -->` marker.
2. **Second run** (after the TODO is gone) bumps every package and plugin
manifest, creates the `chore: release v<version>` commit, and tags
`v<version>` (lightweight).
Then push to trigger the publish:
```bash
git push origin main --tags
```
Notes:
- Pre-release versions (`0.6.72-alpha.1`) publish to the matching npm dist-tag
(`alpha`) instead of `latest`.
- `--skip-changelog-check` skips the changelog gate for emergency releases.
- The publish step skips any version already on npm, so re-running a failed
workflow run is safe.
## Skills
Composition authoring (not repo development) is guided by skills installed via `npx skills add heygen-com/hyperframes`. See `skills/` for source. Invoke `/hyperframes`, `/hyperframes-cli`, `/hyperframes-registry`, `/tailwind`, or `/gsap` when authoring compositions. Use `/tailwind` for projects created with `hyperframes init --tailwind` so agents follow the pinned Tailwind v4 browser-runtime contract instead of Studio's Tailwind v3 setup. Use `/animejs`, `/css-animations`, `/lottie`, `/three`, or `/waapi` when a composition uses those first-party runtime adapters. Invoke `/hyperframes-media` for asset preprocessing (TTS narration, audio/video transcription, background removal for transparent overlays) — these commands have their own skill so the CLI skill stays focused on the dev loop. When a user provides a website URL and wants a video, invoke `/website-to-hyperframes` — it runs the full 7-step capture-to-video pipeline.