* refactor(skills): consolidate 15 skills into 3 for better trigger reliability
Merge 9 GSAP skills (core, timeline, scrolltrigger, plugins, utils, react,
frameworks, performance, effects) and 6 HyperFrames skills (compose, captions,
tts, audio-reactive, marker-highlight, cli) into 3 consolidated skills:
- `gsap` — core API + timelines + performance in SKILL.md; scrolltrigger,
plugins, utils, react, frameworks, effects in references/
- `hyperframes` — composition authoring rules in SKILL.md; captions, tts,
audio-reactive, marker-highlight in references/
- `hyperframes-cli` — CLI commands (init, lint, preview, render, etc.)
Why: With 15 separate skills, agents must correctly trigger the right subset
for any task. "Create an animated video with captions" needed 6+ skills to
fire — each with ~90% trigger accuracy means ~53% chance of getting all of
them. With 3 skills, that same task needs just `hyperframes` + `gsap` (~90%
both fire). Progressive disclosure still works via references/ files loaded
on demand.
Also fixes: CLAUDE.md referenced `window.__GSAP_TIMELINE` (incorrect) —
corrected to `window.__timelines`.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(cli): add --skip-skills flag to init command
Allow skipping the AI coding skills installation prompt during
`hyperframes init` with `--skip-skills`. Useful when skills are
already installed or when the user wants to scaffold without them.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(skills): address code review feedback on consolidation
Restore content lost during over-compression:
- captions: fix overflow to `visible` (not hidden — clips glow effects),
add container pattern warning, scale headroom formula, and self-lint
placement guidance
- audio-reactive: restore sampling frequency pattern (per-frame tl.call
loop vs single tween) and textShadow-on-container gotcha
- effects/typewriter: restore word rotation, appending words, spacing
with static text, and multi-line cursor handoff patterns
- effects/audio-visualizer: restore spatial mapping conventions, fetch vs
inline loading, WebGL/DOM rendering approaches, and canvas layering
- hyperframes-cli: restore --strict-all flag in render flags table
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): update build:copy and template for consolidated skill names
- build:copy: reference skills/hyperframes, skills/hyperframes-cli,
skills/gsap instead of the old 15 skill directory names
- _shared/CLAUDE.md template: update skill table to consolidated names
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the static "Tip" message at the end of `hyperframes init` with an
interactive prompt that offers to install AI coding skills. When the user
accepts, the skills command runs `npx skills add` with `--all` and
`stdio: "inherit"` so the native installer output is visible.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move per-command examples from the centralized `help.ts` record into
each command file as `export const examples: Example[]`. help.ts now
dynamically imports them at --help time.
This means adding a new command and its examples happens in one file
instead of two, reducing the chance of forgetting examples.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(cli): add `tts` command for local text-to-speech via Kokoro-82M
Adds `hyperframes tts` — generate speech audio locally using Kokoro-82M
(ONNX), no API key needed. Mirrors the transcribe command architecture.
- New command: `hyperframes tts "text" --voice af_heart --output speech.wav`
- 54 voices across 8 languages, ~5x realtime on CPU
- Auto-downloads model (~311 MB) + voices (~27 MB) to ~/.cache/hyperframes/tts/
- Requires Python 3.8+ with kokoro-onnx installed
- Extracted shared `downloadFile` utility from whisper/manager.ts with
atomic .tmp→rename to prevent partial download corruption
- Added hyperframes-tts skill with voice selection guide
- Updated CLAUDE.md with TTS docs, voice table, and skill reference
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(tts): improve skill per skill-creator guidelines
- Move trigger info from body to frontmatter description
- Remove `trigger` field (not a valid frontmatter field)
- Remove CLI flag docs Claude can derive from --help
- Remove redundant voice tables (keep content-to-voice mapping)
- Fix composition audio example to use actual <audio> element pattern
- Keep non-obvious workflows: TTS+transcribe for captions, long scripts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(tts): add guidance for using external TTS sources
Help users understand when to use cloud TTS (voice cloning, broader
languages, higher quality) vs the built-in Kokoro model, and how
external audio integrates into the same composition workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(tts): prioritize HeyGen API as recommended cloud TTS
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(tts): remove external TTS section for now
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(tts): set required: false on input arg so --list works standalone
Citty treats positional args as required by default unless explicitly
set to required: false. Without this, `hyperframes tts --list` fails
with "Missing required positional argument".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(tts): add --help examples and fix required:false for --list
Add examples section to `tts --help` matching the pattern from other
commands (transcribe, render, etc.). Fix citty positional arg requiring
explicit `required: false` for --list to work standalone.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add CLI command checklist to CLAUDE.md
Ensure new commands always get --help examples in help.ts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(cli): improve --help with grouped commands and per-command examples
Replaces citty's flat COMMANDS list with kubectl-style grouped categories
and adds examples to every subcommand. Root help now groups 14 commands
into 5 categories (Getting Started, Project, Tooling, AI & Integrations,
Settings). Per-command --help now appends a formatted Examples section
with practical usage patterns.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(cli): use shared colors, lazy-load help, fix description drift
- Replace hand-rolled ANSI helpers with existing ui/colors.ts (fixes
non-conformant NO_COLOR handling)
- Add cyan and gray to shared color module
- Lazy-load help.ts via dynamic import to avoid allocating help data
on non-help invocations
- Fix description drift: benchmark and transcribe descriptions now
match their command meta.description
- Unify tuple order: ROOT_EXAMPLES now uses [comment, command] to
match COMMAND_EXAMPLES
- Remove redundant comment restating type annotation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): resolve typecheck error in showUsage generic signature
The lazy-load wrapper needs to cast CommandDef<T> to CommandDef when
forwarding to the help module, since TypeScript's generic variance
makes the direct assignment incompatible.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore(cli): remove kubectl references from help comments
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(cli): add remote template fetching via giget
* fix: update remote.ts to use templates/ instead of examples/
* fix(cli): validate template ID against manifest before downloading
Fails fast with available template list instead of downloading
an empty directory for nonexistent templates.
* refactor(cli): simplify to single --template flag with dynamic validation
- Remove --example flag (--template handles bundled + remote)
- Remove static ALL_TEMPLATE_IDS list (validates against GitHub manifest)
- No CLI release needed to add new templates — just add to templates/ and templates.json
- scaffoldProject auto-detects bundled vs remote
* chore: update lockfiles for giget dependency
* fix(cli): remove undefined isAudioOnly reference
* refactor(cli): move templates to examples/ for remote fetching
* refactor(cli): rename examples/ to templates/ for clarity
Follows Remix/Vite convention — these are init scaffolds, not general examples.
* feat(docs): add template gallery page with visual previews
* fix(docs): remove invalid MDX heading anchors
* chore: retrigger CI
* feat(docs): merge gallery into templates page with hover-to-play video previews
- Consolidated gallery.mdx and templates.mdx into single templates.mdx
- Moved templates page to Getting Started section
- Added MP4 video previews rendered by hyperframes (hover to play)
- Custom JS for hover-to-play behavior (Mintlify strips JSX event handlers)
- 2-column grid for landscape, 3-column for portrait
- Remotion-style cards with gradient overlay labels
* fix(docs): update broken links after templates page move
* ci(regression): remove scripts/ from regression trigger paths
scripts/ contains dev utilities (lint, versioning, preview generation)
that don't affect the rendering engine.
## What
Add a script that uses `@hyperframes/producer` to render PNG thumbnails of each built-in template. Output goes to `docs/images/templates/`.
## Why
User feedback: "I would have loved more visual examples of what's actually possible. I had to scaffold every template just to see what they look like." This is the foundation for a visual template gallery in the docs.
## How
- New `scripts/generate-template-previews.ts` uses the producer's `createFileServer` + `createCaptureSession` + `captureFrame` APIs — hyperframes renders its own templates
- Patches out `__VIDEO_SRC__` placeholders (same logic as `init.ts`) so templates render without a video file
- Captures a frame at t=2s for each template (skips `blank` — it's just empty scaffolding)
- Handles varying dimensions (vignelli is 1080x1920 portrait)
- Adds `pnpm generate:previews` npm script
## Test plan
- [x] `pnpm generate:previews` generates 8 PNGs in `docs/images/templates/`
- [x] Each PNG is visually correct (verified by viewing)
- [x] `--only <template>` flag works for single template generation
- Replace hardcoded template list in build:copy with `cp -r src/templates/*`
so new templates are included automatically (kinetic-type, decision-tree,
product-promo, nyt-graph were missing from published package)
- Fix captions SKILL.md: reword `!` and `>` in inline backticks that
triggered Claude Code's bash permission checker
- Add scripts/lint-skills.ts to catch shell-unsafe patterns in SKILL.md
files (runs as part of `bun run lint` in CI)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(cli,core): add standalone transcribe command, transcript normalization, and caption lint rules
- Add `hyperframes transcribe` command for transcribing audio/video and importing
existing transcripts (SRT, VTT, OpenAI Whisper API JSON, whisper.cpp JSON)
- Add transcript format normalizer (normalize.ts) with auto-detection and
conversion to standard [{text, start, end}] word arrays
- Upgrade default whisper model from base.en to small.en for better accuracy
- Add --model and --language flags to both `transcribe` and `init` commands
- Extract shared patchCaptionHtml() to eliminate duplication between init.ts
and transcribe.ts (init.ts reduced by ~55 lines)
- Add 3 caption lint rules: caption_exit_missing_hard_kill,
caption_text_overflow_risk, caption_container_relative_position
- Update captions skill with model guide, format docs, music guidance,
text overflow prevention, caption exit guarantee pattern
- Expand captions skill trigger to cover lyrics, karaoke, lyric videos
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(cli): add transcribe command and --model/--language flags to CLI docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): fix blank template lint issues
- blank/index.html: remove data-start from video (was nested in timed parent),
add class="clip" for initial hidden state
- blank/captions.html: add max-width + overflow:hidden to prevent text clipping,
add tl.set hard kill after exit tween to prevent stuck captions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add lint-after-edit rule to repo and project CLAUDE.md
Agents must run `npx hyperframes lint` after editing compositions.
Also expand captions skill description in project template.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: format _shared/CLAUDE.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The --tag flag was optional, which led to v0.1.11 and v0.1.12 being
bumped without tags — skipping npm publish entirely. Invert the default:
always commit + tag, with --no-tag as the escape hatch.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add npm version and MIT license badges (matching React/Remotion/Next.js conventions)
- Remove non-standard Docs badge
- Move Documentation section from bottom of README to right after Quick Start
for better discoverability (follows patterns from React, Remotion, Vite)
- Link to quickstart at hyperframes.heygen.com/quickstart
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(cli): add system metrics to telemetry and expand doctor command
Enrich render telemetry with device/environment metadata (CPU, memory,
OS, Docker/CI/WSL detection) following patterns from Next.js and
Turborepo. Add speed_ratio (render time / composition duration),
per-frame capture timing, and resource usage to render events.
Expand the doctor command with CPU, memory, disk, /dev/shm, and
environment checks to help debug rendering issues on user machines.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): invert speed_ratio to match experiment-framework convention
composition_duration / render_time — higher is better, >1 means faster
than realtime. Matches magic_edit.render.speed_ratio in experiment-framework.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): wire errorMessage into render error telemetry
Address review feedback — the errorMessage field was declared in the
trackRenderError interface but never populated.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(cli): add render telemetry to embedded studio server
Track render_complete and render_error from the studio's render API
endpoint (hyperframes dev). Uses dynamic imports so telemetry is
resolved at call time within the CLI package — no telemetry coupling
added to @hyperframes/studio or @hyperframes/producer.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Namespace skill names with `hyperframes-` prefix for clearer identity in
OSS contexts where users may have other skills installed.
Updates skill directories, SKILL.md frontmatter, CLAUDE.md, README.md,
CLI build script, init command, and project template.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a contextual menu to every docs page with options to copy page
content, open in Claude, connect via MCP to Cursor/VS Code/Windsurf,
and file a GitHub issue — all directly from the docs header.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- README quick start now leads with opening in an AI agent after init
- Quickstart docs updated: interactive wizard is default, --non-interactive
replaces --human-friendly, edit step mentions AI agent workflow
- Init "next steps" now leads with opening in an AI agent as step 1,
followed by dev preview and render as steps 2 and 3
- Interactive init shows a tip about AI agent workflow after scaffolding
- Dev command output hints that compositions can be edited with an
AI agent and changes reload automatically in the studio
The shared CLAUDE.md in scaffolded projects mentioned
hyperframes.heygen.com alongside doc topic names, causing AI agents
to guess incorrect URLs like /compositions instead of using the local
`hyperframes docs` CLI command or the correct /concepts/compositions
path. Now explicitly directs agents to use the CLI for quick reference
and llms.txt for full doc URL discovery.
- Interactive wizard is now the default when running in a TTY terminal
- Non-interactive mode (for CI/agents) uses --non-interactive flag
- Template defaults to "blank" when not specified in non-interactive mode
- --template flag skips the template picker prompt in interactive mode
- Replaces --human-friendly flag with --non-interactive (inverted logic)
npm's sigstore provenance verification requires package.json
repository.url to match the GitHub repo. Without it, publish
fails with E422 "expected to match https://github.com/heygen-com/hyperframes".
- Compute CPU_CORE_COUNT once at module level instead of calling cpus()
multiple times
- Make RenderOptions.workers required (number, not optional) — the
caller resolves the default, callees don't re-derive it
- Remove redundant existsSync guard before mkdirSync({recursive})
- Trim oversized JSDoc on defaultWorkerCount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the hardcoded default of 4 workers with a CPU-aware heuristic:
half of available CPU cores, capped at 4. Each worker spawns a separate
Chrome browser process (~256MB RAM each), so the previous default of 4
caused resource contention on smaller machines.
The new defaults:
2-core laptop → 1 worker
4-core laptop → 2 workers
8-core desktop → 4 workers
16-core server → 4 workers (capped)
Also adds --workers auto flag support, improves help text to explain
what workers do, and adds a Workers section to the rendering docs with
guidance on when to increase or decrease parallelism.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract `resolveAssetDir()` helper to eliminate copy-paste across
getStaticTemplateDir, getSharedTemplateDir, getBundledSkillsDir
- Remove `counted` boolean in fallbackInstall() — collect installed
skills from first target explicitly, then copy to remaining targets
- Consolidate duplicate `installed.length > 0` check in runInstall()
into a single early return
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace custom git clone + copy logic with `npx skills add` from the
vercel-labs/skills ecosystem. This is the standard used by GSAP,
Remotion, and other skill providers.
- Primary path: `npx skills add heygen-com/hyperframes` and
`npx skills add greensock/gsap-skills` with `-g -y -a <agent>` flags
- Git clone + copy kept as fallback if npx is unavailable
- Added `skillsAgent` field to targets mapping CLI flags to the correct
agent names (e.g., gemini → gemini-cli)
- Removes ~80 lines of custom git/copy infrastructure
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New HyperFrames projects created via `hyperframes init` now include:
- CLAUDE.md + AGENTS.md — teaches AI agents about skills, commands,
project structure, and framework rules (class="clip", timeline
registration, determinism). Agents know to invoke /compose-video
before writing compositions.
- .claude/skills/{compose-video,captions} — project-level skills for
immediate availability in the current agent session (global skills
require a session restart to discover).
- Updated next-steps output with `hyperframes docs <topic>` and a
link to hyperframes.heygen.com.
- Updated README with "AI Agent Skills" section documenting
`npx hyperframes skills` and `npx skills add` install paths.
- Repo-level CLAUDE.md for framework contributors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The 3-workflow chain (release.yml → release-tag.yml → publish.yml) was
broken by design: tags created by GITHUB_TOKEN don't trigger other
workflows, so merging a release PR never actually published.
Consolidate into a single publish.yml that triggers on both tag push
and release PR merge. Delete the redundant prepare-release and
tag-release workflows.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The SIGINT handlers that were meant to do graceful cleanup (close server,
remove symlinks) override Node's default exit behavior. If the cleanup
hangs (e.g. server.close() blocked by open connections), the process is
stuck and Ctrl+C does nothing.
Fix: don't intercept SIGINT at all. Node's default behavior exits the
process immediately on Ctrl+C. The OS reclaims the port and file handles.
Use process.on("exit") for best-effort symlink cleanup instead.
Also fixes running the CLI via `tsx` in dev mode — __CLI_VERSION__ is a
tsup build-time define that crashes at runtime without a fallback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The embedded dev server (hyperframes dev) silently reported success even
when the requested port was already in use (e.g., by Cursor IDE on 3002).
This happened because the old isPortAvailable() probe had a TOCTOU race
and findAvailablePort() silently fell back to the original port on exhaustion.
Replace with serveWithPortFallback() that binds the real Hono server
directly via createAdaptorServer + manual listen(), retrying on EADDRINUSE.
Shows a yellow warning when auto-incrementing and a clear error when all
ports are exhausted.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use static import for copyFileSync (was unnecessary dynamic import)
- Shallow-copy config before mutating forceScreenshot (prevents
caller-provided config from being permanently modified)
- Consolidate isWebm/isWebmRender/outputFormat into single early
declaration in renderOrchestrator
- Fix debug output extension for WebM (was hardcoded .mp4)
- Log unexpected audio extraction errors instead of silently swallowing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Engine: document getEncoderPreset() for MP4/WebM, VP9 alpha flags,
Opus audio in mux step
- Producer: document format field in RenderConfig, WebM usage example,
pipeline steps updated for WebM
- CLI: add render command examples in --help (including WebM overlay)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Document the --format webm flag, VP9 alpha output, overlay workflow
with FFmpeg, and transparent background requirement for compositions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The VP9 alpha encoding works correctly — verified by overlaying on a
green background with explicit VP9 decoder. FFmpeg's default decoder
doesn't expose VP9 alpha through ffprobe, but browsers and VP9-aware
decoders read it correctly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Force screenshot capture mode for WebM (beginFrame doesn't support
alpha channel in chrome-headless-shell)
- Set Emulation.setDefaultBackgroundColorOverride once during session
creation (matching experiment-framework's approach)
- Fix acquireBrowser to pass executable path in screenshot mode on
Linux (was setting undefined, causing puppeteer-core to fail)
- Add Page.captureScreenshot params: fromSurface, captureBeyondViewport,
optimizeForSpeed (matching experiment-framework)
- Fix regression harness extractMonoPcm16 for videos without audio
- Add getEncoderPreset unit tests
- Add webm-transparency regression test with golden baseline
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New regression test `webm-transparency`: minimal transparent composition
rendered to WebM, validates VP9 codec and visual quality at 100
checkpoints against golden baseline
- Extend regression harness: `renderConfig.format` field ("mp4" | "webm"),
format-aware output paths and snapshot filenames
- Fix `extractMonoPcm16` to gracefully handle videos without audio
streams (WebM without audio was throwing instead of returning empty)
- Unit tests for `getEncoderPreset()`: VP9/yuva420p for WebM,
h264/yuv420p for MP4, preset mapping, quality preservation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Support rendering compositions with transparent backgrounds via
`--format webm`. VP9+alpha is the standard format for overlayable
video (captions, lower thirds, overlays).
Changes by layer:
- CLI: `--format mp4|webm` flag on render command
- Producer: threads format through RenderConfig, switches to PNG
capture and VP9 encoding when webm
- Engine: getEncoderPreset() returns VP9 config with yuva420p;
transparent page background via CDP when capturing PNG;
mux uses Opus audio for WebM; VP9 flags from production:
-row-mt 1, -auto-alt-ref 0, alpha_mode=1 metadata
- Frame capture: Emulation.setDefaultBackgroundColorOverride a=0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Document the three dev server modes (embedded/local studio/monorepo)
- Add --port flag to dev command
- Document _meta envelope on all --json commands
- Document upgrade --check --json for agent consumption
- Document passive update notices and HYPERFRAMES_NO_UPDATE_CHECK
- Update doctor output example with Version check row
- Fix README default port from 3000 to 3002
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New `updateCheck.ts` utility: cached npm registry check (24h TTL),
sync `getUpdateMeta()` for _meta envelope, `printUpdateNotice()` for
passive stderr banner
- `upgrade --check --json`: machine-readable version check for AI agents
Returns { current, latest, updateAvailable }
- `_meta` envelope on all --json commands (info, lint, benchmark,
compositions): includes version, latestVersion, updateAvailable
- `doctor` shows version check as first row
- Passive update notice on stderr after command completes (skipped in
CI, non-TTY, --json, --quiet)
- Background check fires on startup (non-blocking, populates cache)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add nullish coalescing for regex match groups that TypeScript flags as
possibly undefined.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wire up the "Export MP4" button in the studio UI. The render runs
async in the same process using @hyperframes/producer's executeRenderJob,
with SSE progress streaming and MP4 download on completion.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When installed via npx, `hyperframes dev` now starts a standalone
Hono HTTP server that serves the pre-built studio SPA and implements
the project API (file listing, read/write, preview bundling,
sub-composition rendering, runtime serving, SSE file watching).
Three modes are auto-detected:
1. Monorepo dev (running from .ts source) → spawn Vite (existing)
2. Local @hyperframes/studio installed → spawn Vite via package (new)
3. Default → embedded Hono server (new, zero extra deps needed)
Also patches the studio SPA to use EventSource SSE fallback when
Vite HMR is unavailable (production/embedded builds).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reverting the package rename — Vance needs @hyperframes/cli for local
dev workflow. Instead, rewrite the name to "hyperframes" in the publish
workflow just before npm publish, so the monorepo name stays intact.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The CLI is published to npm as unscoped `hyperframes` but the
package.json had `@hyperframes/cli`, causing ENEEDAUTH on publish
(wrong scope for the npm token).
Also replace per-step continue-on-error with a single publish script
that skips already-published versions and fails on real errors, making
re-runs safe.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The generated font data is a pure function of the generator script +
@fontsource package versions — no reason to store 566KB of base64
blobs in git. Generate it during the Docker test image build instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The regression CI runs the producer source directly via tsx (not the
bundled CLI), so it needs fontData.generated.ts to exist at import
time. Remove from .gitignore and commit the generated file.
Mark as linguist-generated in .gitattributes so GitHub collapses it
in PR diffs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous approach called loadHyperframeRuntimeSource() to regenerate
the runtime IIFE, but its output lacked the trailing newline present in
the pre-built artifact, causing a SHA256 checksum mismatch with the
manifest. Copy the pre-built files from core/dist directly instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Empty src="" on <audio> elements causes the browser to fetch the
current page URL as the audio source, producing 404 errors in the
console during rendering. These are placeholder sound effect slots
whose src should be set dynamically when audio is configured.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The CLI's ensureBrowser() finds Chrome via env var, cached download,
or system paths (including /Applications/Google Chrome.app on macOS).
But the result was discarded — the engine's acquireBrowser() only
checked the puppeteer cache for chrome-headless-shell, passing
undefined executablePath on macOS without headless-shell installed.
Bridge the two by setting PRODUCER_HEADLESS_SHELL_PATH from the CLI's
resolved browser path before creating the render job, so the engine's
resolveConfig() picks it up.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The hyperframeRuntimeLoader uses import.meta.url to resolve
hyperframe.manifest.json as a sibling file. When bundled into the CLI
via tsup, import.meta.url points to dist/cli.js, so the sibling lookup
checks dist/hyperframe.manifest.json — which wasn't being shipped.
Copy the manifest and the canonical-named runtime IIFE into dist/ during
the build-runtime step so the loader's sibling-path resolution works in
published npm packages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The CLI bundle uses tsup to inline @hyperframes/producer, but the
deterministicFonts module used require.resolve('@fontsource/*/package.json')
at runtime to find woff2 files on disk. When installed via npx, these
@fontsource packages don't exist, causing "Cannot find module" errors.
Replace runtime filesystem lookups with a build-time generator that reads
all @fontsource woff2 files and produces a TypeScript module with base64
data URIs. The generator runs before both producer and CLI builds, making
the bundle fully self-contained with zero @fontsource runtime dependencies.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace markdown issue templates with GitHub YAML form templates for
structured bug reports and feature requests. Add config.yml to disable
blank issues and enforce template usage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The producer's tsconfig.json included all src/**/* which caused tsc
to fail on test files that import vitest (not a producer dependency).
Exclude *.test.ts from the declaration build — vitest handles its own
tsconfig for running tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verifies:
- env var override takes priority
- sibling path is first candidate (catches the npm package bug)
- monorepo-relative fallback works in dev
- path construction invariant holds
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The build script already copies hyperframe.manifest.json into the
producer's dist/ directory, but the runtime loader never checked there.
This caused `window.__hf not ready` timeouts when rendering via the
published npm package outside the monorepo, since the only candidate
paths were monorepo-relative.
Add a sibling-directory lookup (same dir as the bundled module) as the
first candidate, so the manifest is found whether running from source,
a bundled dist/, or an npm install.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- flushSync: use detached spawn + unref() instead of execFileSync,
so process.exit() paths don't block up to 5s on slow networks
- showTelemetryNotice: persist notice flag BEFORE printing/tracking,
so users are never tracked without having seen the disclosure
- Config dir: set mode 0o700 on ~/.hyperframes/ directory (was umask default)
- $ip: null comment: clarify this is belt-and-suspenders with server-side discard
- shouldTrack: update comment — phc_ prefix check is a safety net, not dead code
- env.ts: add comment explaining try/catch fail-safe defaults to production
- init.ts: consistently call trackInitTemplate after scaffoldProject in both paths
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Run oxfmt on cli.ts and client.ts
- Replace literal placeholder comparison with phc_ prefix check
(TS2367: comparing two different string literals has no overlap)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PostHog's $ip: null property tells the server to not associate the
request IP with the event. Combined with the "Discard client IP data"
project setting for server-side enforcement.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract shared isDevMode() to utils/env.ts (was duplicated in dev.ts and client.ts)
- Use ui/colors.ts instead of raw ANSI escapes in telemetry notice (respects NO_COLOR)
- Derive known commands from subCommands object instead of maintaining duplicate set
- Skip telemetry on --help/--version and unknown commands
- Gate incrementCommandCount() behind shouldTrack() (no disk writes in CI)
- Add flushSync() for process.exit() paths (beforeExit doesn't fire on explicit exit)
- Remove dead trackBrowserInstall(success) param (failure path never called it)
- Remove redundant isEnabled/anonymousId caching in client.ts (config.ts cache suffices)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add anonymous usage telemetry to help improve the CLI. Uses PostHog's
HTTP batch API directly (zero new dependencies) with a 5-second timeout
and fail-silent behavior — telemetry never breaks the CLI.
What's collected: command names, render performance (duration, fps,
quality), template choices, OS/arch/Node version/CLI version.
What's NOT collected: file paths, project names, video content, or
any personally identifiable information.
Telemetry is:
- Disabled in dev mode (running via tsx)
- Disabled in CI (CI=true) or via HYPERFRAMES_NO_TELEMETRY=1
- Disabled when API key is placeholder (safe to merge before key is set)
- Controllable via `hyperframes telemetry [enable|disable|status]`
- Disclosed on first run with clear opt-out instructions
Config stored at ~/.hyperframes/config.json (0600 permissions).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>