Commit Graph
34 Commits
Author SHA1 Message Date
James RussoandClaude Opus 4.8 4da567df22 feat(gcp-cloud-run): Google Cloud Run + Workflows distributed render adapter (#1253)
* feat(gcp-cloud-run): add Google Cloud Run + Workflows distributed render adapter

Adds @hyperframes/gcp-cloud-run, the GCP counterpart to @hyperframes/aws-lambda
(issue #932). The OSS distributed primitives (plan, renderChunk x N, assemble)
are unchanged; this package is the storage/compute/orchestration glue.

Package: Cloud Run handler (one image, three actions), runs under bun; GCS
transport; in-image chrome-headless-shell resolver; client SDK
(renderToCloudRun, getRenderProgress, deploySite, computeRenderCost); Dockerfile;
Cloud Workflows definition; Terraform module; CLI cloudrun
deploy|sites|render|render-batch|progress|destroy with --output-resolution and
--strict-variables; 62 unit tests + docs + live smoke script.

Shared extraction (removes ~640 lines of adapter duplication): move the
cloud-agnostic config validator + content-hash into producer/distributed; both
adapters import them. Validated end-to-end on GCP at 37.4 dB PSNR vs baseline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(cli): resolve @hyperframes/gcp-cloud-run in the CLI build + root build

The CLI bundle (esbuild) couldn't resolve `@hyperframes/gcp-cloud-run/sdk`,
failing Build/Typecheck/CLI-smoke (and the perf/windows/regression jobs that
build first). Mirror the aws-lambda handling: mark the gcp adapter + its /sdk
subpath external in tsup.config.ts with a source alias, and add gcp-cloud-run
to the root `build` filter so its dist exists for publish + runtime.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ci): copy gcp-cloud-run manifest in Dockerfile.test for frozen install

The regression test image runs `bun install --frozen-lockfile` after copying
each workspace package.json individually. The CLI now depends on
@hyperframes/gcp-cloud-run (workspace:*), so the frozen install fails to
resolve it unless its manifest is present. Add the COPY line.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(cli): add machine-sizing flags to `cloudrun deploy`

Closes the parity gap with `lambda deploy` (which exposes --memory etc.).
`cloudrun deploy` now threads --cpu, --memory, --max-instances, and --timeout
into the Terraform apply; omitted flags keep the module defaults
(4 vCPU / 16Gi / 100 instances / 3600s). For finer control, apply the module
directly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(gcp-cloud-run): address PR review (security, waste, limits, alerts)

- server.ts: bucket-allowlist guard no longer fails open silently. Unset env
  logs a one-time WARNING; "*" is an explicit opt-out; otherwise it enforces.
- server.ts: stop double-shipping audio.aac. It already rides in the plan
  tarball every consumer downloads, so drop the redundant standalone upload
  (plan) + re-download/overwrite (assemble); assemble reads it from the untar,
  falling back to a supplied AudioGcsUri for compat.
- server.ts: chunk extension via path.extname() instead of slice(lastIndexOf).
- workflow.yaml: clamp parallel concurrency_limit to math.min(chunkCount, 20)
  — Cloud Workflows hard-caps concurrent iterations at 20.
- Dockerfile: pin bun (bun-v1.3.9) so an interop change can't silently break
  the image rebuild.
- terraform: add min_instances var (default 0); add a workflow-failure alert
  (finished_execution_count status=FAILED) alongside the request-count one.
- costAccounting: document that displayCost excludes GCS storage/egress.

Verified against the actual APIs: @google-cloud/workflows@4.4.0
ICreateExecutionRequest has no executionId (so the idempotency-token suggestion
isn't available in this client); Workflows concurrency cap is 20; failure
metric is workflows.googleapis.com/finished_execution_count (status label).
174 adapter tests pass, fallow/oxlint/oxfmt/terraform clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(gcp-cloud-run): address round-2 review — error code + CFR forwarding

- workflow.yaml: rename the zero-chunk failure code PLAN_TOO_LARGE →
  PLAN_PRODUCED_ZERO_CHUNKS. The old code implied a size-ceiling breach (the
  opposite cause), misleading anyone triaging the alert.
- workflow.yaml: forward Config.cfr to the assemble step
  (`Cfr: ${("cfr" in config) and config.cfr}`). It was read by the handler
  but never sent, so exact-CFR was silently off for every Cloud Run render.
  Uses the same `in`-operator guard already proven in the retryable predicate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(release): include gcp-cloud-run in set-version PACKAGES list

set-version.ts (driven by release:prepare) bumps an explicit package list to
the shared version on each release. gcp-cloud-run was wired into the build +
publish.yml but missing here, so a release would leave it at a stale version
and publish.yml would push the wrong version. Add it so the new package
version-bumps + publishes in lockstep with the others.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 14:43:38 -07:00
Miguel Ángel cabd0616ea fix(cli): suppress EPIPE crashes in piped agent environments (#1184)
* fix(cli): suppress EPIPE crashes in piped agent environments

When the CLI runs inside a piped environment (Claude Code, Codex,
Cursor), the reader may close the pipe before we finish writing.
Node treats EPIPE on stdout/stderr as an uncaughtException, crashing
the process with a non-zero exit code.

Add stream-level EPIPE handlers on stdout/stderr at the top of the
entry point (before any output) and make the uncaughtException handler
EPIPE-aware so it exits cleanly (code 0) instead of crash-reporting.

PostHog data: ~10,300 EPIPE errors over 10 days, contributing to the
preview command's 43-59% failure rate in agent environments.

* fix(cli): set commandFailed before EPIPE exit to prevent false success telemetry

EPIPE is a pipe-reader-closed signal, not a successful run. The exit handler
uses 'code === 0 && !commandFailed' to determine success — without setting
commandFailed=true before process.exit(0), every EPIPE exit was recorded as
success:true in telemetry.

Moves the commandFailed declaration to the top of the file so the stream-error
EPIPE handlers (which must run before any writes) can reference it. Also sets
commandFailed=true in the uncaughtException EPIPE path for the same reason.
2026-06-03 22:53:08 -04:00
James Russo ce5e872e51 feat(cli): add hyperframes cloud render/list/get/delete commands (#1110)
* feat(cli): vendor initial hyperframes cloud client codegen

Generated by experiment-framework/scripts/generate_hyperframes_cli_client.py
(see heygen-com/experiment-framework#37896). Sets up the baseline for the
sync workflow to diff against on future spec changes.

The follow-up PR adds the orchestration layer (zip + upload + poll +
download) and the user-facing 'hyperframes cloud render/list/get/delete'
commands on top of this generated client.

The fallow ignore pattern is necessary because the generated request()
method is intentionally a single switch that handles all 5 endpoints
in one place; refactoring it here would just be re-introduced on the
next codegen run.

* chore(cli): regenerate cloud client with mimeType parameter on multipart uploads

Adds optional mimeType arg to uploadAsset (and any future multipart
endpoints). Without it, FormData sends application/octet-stream which
is correct for the documented media surface (png/jpeg/mp4/etc.) but
ambiguous for the private-beta zip uploads the cloud render flow uses.
Callers that pass `mimeType: "application/zip"` tag the multipart
part with the right Content-Type so downstream proxies, WAFs, and any
future server-side change that keys off the part MIME (instead of the
current magic-byte detection) all see the intended type.

Addresses review feedback on heygen-com/experiment-framework#37896.
Generated by scripts/generate_hyperframes_cli_client.py with the
matching update to the multipart emit path.

* feat(cli): add hyperframes cloud render/list/get/delete commands

Hand-rolled orchestration layer on top of the auto-generated cloud
client (vendored in the previous PR):

- cloud render <dir>: zip via createPublishArchive → upload to
  /v3/assets → submit /v3/hyperframes/renders → poll
  /v3/hyperframes/renders/{id} every 10s (max 60min) → stream the
  signed video_url to disk.
- cloud render --no-wait: submit and exit with the render_id.
- cloud render --asset-id / --url: skip zip+upload and use a
  pre-uploaded asset or public HTTPS zip.
- cloud render --variables / --variables-file: same UX as the local
  render command; variables are validated against
  data-composition-variables only when there's a local project.
- cloud list / cloud get / cloud delete: thin wrappers around the
  matching client methods, with cursor-pagination support on list.

Auth comes from the existing cli/src/auth/ chain via cloud/auth.ts —
no new credential store, no new env var. The cloud client receives a
getAuthHeaders() callback that re-resolves credentials on every
request, so OAuth refreshes mid-poll are picked up automatically.

Also extracts a parent-scoped path lookup in help.ts so 'cloud render
--help' surfaces the right examples instead of falling through to the
top-level 'render' command's examples.

* fix(cli): address 15 code-review findings on cloud commands

Correctness fixes
- delete: require --no-confirm when stdin isn't a TTY OR --json is
  passed; previously both silently auto-bypassed the irreversible-
  delete prompt. Explicit decline now exits 2 (distinct from API/system
  errors which still exit 1).
- render: mutex check now counts the positional dir alongside
  --asset-id / --url; `cloud render ./foo --asset-id X` now errors
  instead of silently dropping the dir.
- render: docstring updated — only --no-wait short-circuits the poll
  loop; --callback-url is independent (webhook fires either way).
- render: removed dead try/catch around resolveProject (it calls
  process.exit, never throws). resolveVariablesAndValidateIfLocal also
  takes the resolved project source instead of re-parsing args.
- render: createPublishArchive errors now surface via errorBox instead
  of bubbling a raw stack trace past citty.
- help: loadExamples now only catches ERR_MODULE_NOT_FOUND; real load
  errors (syntax error, broken import) propagate so a broken
  cloud/render.ts no longer silently shows the local render command's
  examples. Also skips the parent-scoped lookup when parentName is the
  root command ("hyperframes").
- list: fetchAll gained a 50-page safety cap + duplicate-cursor
  detection so a buggy backend serving the same next_token on a loop
  can't OOM the CLI.
- download: drain await now listens for error / close / abort so a
  failing write stream (ENOSPC, AbortSignal) rejects promptly instead
  of hanging forever. Partial files are unlinked on any error so the
  caller never observes a truncated MP4. content-length is verified
  against the actual byte count.
- poll: default sleep is abort-aware so Ctrl+C feels immediate instead
  of waiting out the full interval.
- pollWithProgress: ANSI carriage-return redraws now gated on
  process.stdout.isTTY — non-TTY runs (CI, file redirects) emit one
  line per status transition instead of polluting the log with
  literal escape codes.

Cloud client: 401-retry-with-refresh
- createCloudClient now wraps the generated client with a Proxy that
  catches HyperframesApiError(status=401), force-refreshes the OAuth
  token via forceRefreshCredentials, and retries the call exactly
  once. Mirrors AuthClient's onUnauthenticatedRefresh so server-side
  revocations and clock-skew rejections recover automatically.
- auth.ts gained forceRefreshCredentials() and now updates expires_at
  on the refreshed credential it returns (fixed stale-expiry race).

Shared helpers
- cloud/errors.ts: reportApiError(stage, err, opts) is the single
  error-funnel. ERROR_CODE_HINTS now applies to every subverb — fixes
  hyperframes_render_not_found being unreachable from get/delete and
  cuts ~70 LOC of duplicated try/catch/instanceof from render/list/
  get/delete.
- cloud/parsing.ts: parseIntFlag / parseNumericFlag / parseEnumFlag
  strict-mode parsers reject trailing garbage that Number.parseInt
  silently accepts.
- cloud/ansi.ts: stripAnsi / visibleLength / padEndVisible — covers
  ESC + 24-bit truecolor (c.accent palette) instead of the previous
  regex which undercounted overhead and missed truecolor.

JSON-output consistency + _meta envelope
- Every cloud subverb's --json output now goes through withMeta(...)
  so it carries the standard _meta envelope documented in cli.mdx.
- Single-render outputs use {render: detail} across get, delete,
  render-no-wait, render-failed, and render-success. list uses
  {renders: [...], has_more, next_token?}. delete adds deleted: true.

Tests
- 25 new tests across ansi.test.ts, parsing.test.ts, plus truncation
  + abort-cleanup tests for download.test.ts.
- 589 / 589 total CLI tests pass.

* fix(cli): address Vai's review on cloud commands

- render: pass mimeType: "application/zip" to uploadAsset so the
  multipart Content-Type is correct (was application/octet-stream).
  Server currently magic-byte-detects from file bytes so this is
  belt-and-suspenders today, but any downstream proxy / WAF / future
  server change that keys off the part MIME now sees the intended
  type instead of relying on detection.
- render: poll error path now surfaces "Resume with: hyperframes
  cloud get <renderId>" via reportApiError's new `suggestion`
  option, matching the PollTimeoutError handler. The server-side
  render keeps running through a transient 5xx; the user just
  needs the right command to pick it back up.
- list: fetchAll now errorBox-exits on the malformed
  {has_more: true, next_token: null} shape instead of silently
  returning a truncated list (matching the duplicate-cursor guard).
- download: closeFile now listens for 'error' on the write stream
  in addition to the end() callback, so a late ENOSPC during flush
  doesn't leak an unhandled error onto the stream and resolves the
  finally promptly.
- errors: reportApiError accepts an optional `suggestion` that's
  used as the errorBox third line when no code-specific hint
  matches — gives callers a place to surface always-actionable
  recovery context.
- docs(cli): document --idempotency-key as the safe-retry mechanism
  for the upload step. The 401-retry Proxy replays POST requests
  on a stale token; without an idempotency key, the upload may
  land twice. A UUID per logical render is the recommended pattern.
2026-05-28 14:10:05 -04:00
Miguel Ángel d625dc8509 feat: post-render and Studio feedback collection via PostHog surveys (#1101)
* feat(cli): prompt for render satisfaction after successful renders

* feat: add text feedback, doctor context, and Studio render feedback UI

* feat(studio): replace render feedback with session-based Studio experience bar

Move the feedback prompt out of RenderQueueItem (where it triggered every
5th render) into a standalone StudioFeedbackBar mounted at the bottom of
the preview area. The new bar is session-gated (shows after the 5th studio
session), auto-dismisses after 20s, and respects a 30-day cooldown once
dismissed or submitted. Renames telemetry to trackStudioFeedback with a
"studio_experience" survey ID to reflect the broader scope.

* feat(studio): attach browser doctor summary to feedback events

* fix(studio): use recurring interval for feedback instead of one-time cooldown

* fix(cli): skip feedback prompt when an agent runtime is detected

* feat(cli): add hyperframes feedback command and agent render hint

- New `hyperframes feedback --rating <1-5> --comment "..."` command
  for submitting anonymous render satisfaction feedback via telemetry.
- When an AI agent runtime is detected after a render, print a dimmed
  hint to stdout so the agent can optionally call the command instead
  of silently skipping the readline prompt.
- Export getDoctorSummary from telemetry/feedback.ts to share the
  system-info collector between the interactive prompt and the CLI command.
- Register the command in cli.ts and help.ts under Settings.

* fix(studio): align feedback interval to every 15 sessions

* fix: show CLI feedback on first render, Studio every 10 sessions

* feat: add env flags to disable feedback prompts

* feat: env flags to configure feedback prompt frequency

* fix: address review — agent hint reachability, cadence gate, session debounce, deprecated API
2026-05-28 12:17:47 -04:00
James Russo b9dbafdf6a feat(cli): add hyperframes auth login --api-key, status, logout (#1081)
## What

Introduces the `hyperframes auth` command group + a shared credential
store library that hyperframes-CLI and heygen-cli will both read from.

- `hyperframes auth login --api-key` saves a HeyGen API key to
  `~/.heygen/credentials.json` (stdin pipe or hidden-input prompt).
- `hyperframes auth status` resolves the active credential (env vars
  → file) and verifies it against `GET /v3/users/me`, printing
  identity + billing.
- `hyperframes auth logout` removes the credential (`--keep-api-key`
  drops only the OAuth block).

Internals (`packages/cli/src/auth/`):
- `paths.ts` — `~/.heygen` layout, `HEYGEN_CONFIG_DIR` override.
- `store.ts` — read/write `credentials.json` (file 0600, dir 0700)
  with legacy single-line plaintext fallback so existing heygen-cli
  users don't lose their session.
- `resolver.ts` — chain: `HEYGEN_API_KEY` → `HYPERFRAMES_API_KEY` →
  file (unexpired OAuth wins over api_key).
- `client.ts` — hand-written typed wrapper for `GET /v3/users/me`
  (intentionally not OpenAPI codegen — single endpoint).
- `errors.ts` — typed `AuthError` with discriminating `code`.

## Why

This is the foundation for `hyperframes cloud render`. Splitting it
out keeps the cloud-render PR small and lets users sign in today.

The plan originally called for a library-only PR followed by a
commands PR. The `fallow` dead-code gate flagged the library-only
shape as unused exports, so I bundled them — the library and its
first consumers ship together. PR 3 (OAuth PKCE) and PR 4
(heygen-cli read-side JSON support) follow.

## How

- Credential file format: JSON with optional `api_key` + `oauth`
  blocks. Both CLIs read it; the resolver picks the freshest valid
  credential.
- Auth header selection happens in the HTTP client: OAuth →
  `Authorization: Bearer ...`, API key → `x-api-key: ...`.
- `HEYGEN_API_URL` lets dev testing target `api.dev.heygen.com`
  without rebuilding.
- The new `auth` command lazy-loads its subverbs (same pattern as
  `lambda`).

## Test plan

- [x] Unit tests added (`vitest`) for paths, store, resolver,
      client, and errors — 45 tests, all green.
- [x] `bunx tsc --noEmit -p packages/cli/tsconfig.json` clean.
- [x] `bunx oxlint` + `bunx oxfmt --check` clean.
- [x] `bunx fallow audit --base origin/main --fail-on-issues` —
      zero new findings.
- [ ] Smoke test against dev API:
      `HEYGEN_API_URL=https://api.dev.heygen.com hyperframes auth login --api-key`
      then `hyperframes auth status`.
2026-05-28 01:48:25 -04:00
James 8a9291c434 fix(cli): address code-review findings on auth PR 2026-05-28 05:39:24 +00:00
James 7f755913a6 fix(cli): print the update-available notice once, not on every event-loop drain
`process.on("beforeExit", ...)` re-fires every time the event loop
drains, and the handler kicks off a fire-and-forget async telemetry
flush — so on a successful command the user sees the
"Update available: …" notice twice (once after the initial drain, again
after the flush settles). Using `process.once` detaches the listener
after first invocation, fixing the double-print and also preventing a
double-flush of telemetry.

Reported during local testing of `auth login`, but the bug affects every
command (any path where `_flush()` schedules work).
2026-05-28 05:23:27 +00:00
Miguel Ángel f19d6fd471 feat: CLI observability + fix studio save failures on JS-created elements (#1091)
* feat(core): add probeElementInSource for source-existence checks

* feat(core): add probe-element endpoint for source-existence checks

* feat(studio): gate editing capabilities on source existence

* fix(studio): enrich save_failure telemetry with target details

* feat(studio): async selection resolution with source probe

Make `resolveDomEditSelection` async and wire a `probeSourceElement` call
into the selection path so elements generated by scripts (not present in the
source HTML) are detected early and have all edit capabilities disabled with
a clear reason message ("This element is generated by a script and cannot be
edited visually.").

Part A – core probe logic:
- `domEditingLayers.ts`: `resolveDomEditSelection` is now async; calls
  `probeSourceElement` (POST /api/projects/:id/file-mutations/probe-element/:file)
  when `projectId` is supplied and the element has a stable id/selector.
  `existsInSource: false` flows into `resolveDomEditCapabilities`, which
  disables all write capabilities with the appropriate reason.
- `domEditingLayers.ts`: `refreshDomEditSelection` promoted to async.
- `files.ts`: new `probe-element` route; extracted `resolveProjectPath`,
  `resolveFileMutationContext`, `writeIfChanged`, and `parseMutationBody`
  helpers to eliminate repeated boilerplate across remove/patch/probe handlers.

Part B – caller propagation (all eight consumer sites):
- `useDomSelection.ts`: `buildDomSelectionFromTarget`,
  `resolveDomSelectionFromPreviewPoint`,
  `buildDomSelectionForTimelineElement`, `handleTimelineElementSelect`,
  `refreshDomEditSelectionFromPreview`, and
  `refreshDomEditGroupSelectionsFromPreview` all made async; `projectId`
  forwarded into `resolveDomEditSelection`.
- `useDomEditCommits.ts`, `useDomEditTextCommits.ts`: updated
  `buildDomSelectionFromTarget` parameter type; added `await` at call sites.
- `useDomEditSession.ts`: inner `syncSelectionFromDocument` made async; fire
  with `void` to satisfy the surrounding effect.
- `usePreviewInteraction.ts`: `handlePreviewCanvasMouseDown` and
  `handlePreviewCanvasPointerMove` made async (React ignores handler return
  values, so this is safe).
- `useStudioUrlState.ts`: deferred `buildDomSelectionFromTarget` call
  converted to `.then()` chain with `void` prefix so the effect stays sync.
- `LayersPanel.tsx`: `seekToLayer`, `handleSelectLayer`, and
  `handleLayerHover` made async.
- `DomEditOverlay.tsx` / `useDomEditOverlayGestures.ts`: `onCanvasPointerMove`
  return type widened to `Promise<DomEditSelection | null>`; pointer-down
  handler falls back to `hoverSelectionRef.current` (always populated by a
  prior hover) instead of awaiting the async move callback inline.

Part C – test and tooling fixes:
- `lefthook.yml`: filesize hook shell loop explicitly skips `*.test.ts/tsx`
  files as a guard against a lefthook v2.1.6 bug where `exclude` patterns are
  not applied to `{staged_files}` in shell scripts.
- `domEditing.test.ts`: all `it()` blocks calling `resolveDomEditSelection`
  made async with `await`.
- `DomEditOverlay.test.ts`: mock updated to return `Promise.resolve(selection)`
  and `hoverSelection` pre-seeded so pointer-down test works with the new
  hover-first path.
- `studioUrlState.test.ts`: `buildDomSelectionFromTarget` mocks wrapped in
  `Promise.resolve()`; seek/selection hydration test made async with
  `await act(async () => { await Promise.resolve(); })` to flush microtasks.

* feat(cli): add global error handlers for crash telemetry

Register process-level uncaughtException and unhandledRejection handlers
that fire trackCliError so unhandled crashes are captured in telemetry.
Add the trackCliError function to events.ts and re-export it from the
telemetry barrel.

* feat(cli): track per-command success/failure and duration

* test(core): add integration test for JS-created element probe scenario

* fix: address PR review feedback

- uncaughtException handler now calls process.exit(1) after flushing
- cli_command_result uses real exit code from process "exit" event
- drop stack_trace from cli_error (contains filesystem paths)
- skip source probe during hover — only probe on click/selection
- format .fallowrc.jsonc

* fix(cli): restore stack_trace in cli_error telemetry

* fix(cli): use captured module refs in exit handlers instead of dead import()
2026-05-27 01:44:31 -04:00
ukimsanov 62b55171e9 feat(capture): pipeline improvements — contact sheets, design styles, snapshot
Capture pipeline work that came out of the 11-round website-to-video
eval branch. The wins that actually moved quality were the artifacts
agents read (contact sheets, design-styles) and the snapshot tool
visual-verification fixes; the rest are smaller follow-ons.

**Contact sheets (`contactSheet.ts`, new)**
- Replaces the embedded one-image-per-asset listing with paginated
  labeled grids (3-col screenshots / 4-col raster / 5-col SVG). Each
  page contains 9–15 cells with filename labels baked in via SVG
  text overlay (`escapeXml` covers `&<>"'`).
- `fit: "contain"` keeps every asset visible at its real aspect
  ratio; the old `fit: "cover"` cropped to the first image's box.
- Returns `string[]` (page paths) — single-page captures get one
  file, multi-page produce `contact-sheet-1.jpg`, `contact-sheet-2.jpg`,
  etc.
- `createSvgContactSheet` scans both `assets/svgs/` (inline-extracted
  SVGs) and `assets/` root (external SVGs from `<img src="*.svg">`)
  and de-dupes by filename. Sites with all-external SVGs (huly.io)
  now get coverage they previously didn't.

**Design styles extractor (`designStyleExtractor.ts`, new)**
- Walks the live DOM and reads computed styles to produce
  `extracted/design-styles.json`: typography hierarchy (every text
  role with exact font-size / weight / line-height / letter-spacing),
  button variants (background / padding / radius / shadow), card /
  container / nav styles, spacing scale with base unit, border-radius
  scale, box-shadow values with usage counts.
- Primary data source for DESIGN.md authoring at Step 1. Replaces
  the prior "guess from screenshots" workflow.

**Snapshot tool (`snapshot.ts`)**
- HyperShader pre-rendering used to swallow the entire snapshot
  capture window (every frame after the first showed the loading
  overlay or final-opacity-zero exit fades). Wait signal is now
  `window.__hf.shaderTransitions[].ready` (set after both warm and
  cold cache paths complete); local-time seek for sub-comps means
  exit fades read at their own t=0..duration, not global time.
- Gemini vision per-frame analysis runs by default (`descriptions.md`
  next to the contact sheet). `--describe "custom Q"` overrides the
  prompt; `--describe false` opts out.
- 3-column contact sheet generation for snapshot frames so reviewers
  see all beats at a glance.

**Screenshot capture (`screenshotCapture.ts`)**
- Replaces `querySelectorAll('*') + getComputedStyle` overlay scan
  with a TreeWalker that early-exits on cheap rect checks before
  reaching the expensive style read. Caps at 5000 elements per page.
- Cookie/consent dismissal selectors are scoped under cookie /
  consent / gdpr ancestors so we don't click "Accept invitation" or
  similar unrelated buttons.

**Agent prompt (`agentPromptGenerator.ts`)**
- Auto-discovers contact-sheet page count (matches base name plus
  paginated `-NNN` variants only, with regex escaping on the base
  name and numeric sort for 10+ pages).
- `inferColorRole`: classifies extracted hex colors as bg-dark /
  bg-light / accent / surface / neutral via luminance + saturation,
  so the agent prompt shows `#533AFD (accent)` instead of bare hex.
- `design-styles.json` row is gated on `existsSync` — the upstream
  write is wrapped in try/catch and may skip on failure, so the
  prompt only points to files actually on disk.

**Other CLI ergonomics**
- `cli.ts`: auto-load `.env` from CWD on startup so subcommands like
  `snapshot` don't need explicit `export GEMINI_API_KEY=…`. Handles
  `export FOO=bar`, quoted values, inline `# comments`.
- `commands/transcribe.ts`: default output dir is the input file's
  directory, not CWD. Stops the "wrote transcript.json somewhere
  unexpected" footgun.
- `assetDownloader.ts`: improved asset naming uses catalog context;
  de-duplicates inline SVG filenames.
- `contentExtractor.ts`: captions SVGs via Gemini (code-as-text) and
  integrates them into asset descriptions.
- `tokenExtractor.ts` + `types.ts`: SVG bounding box dimensions and
  new DesignStyles schema added.
2026-05-21 10:57:37 -07:00
James Russo e90ad2da61 feat(cli): add hyperframes lambda deploy/render/progress/destroy (#910)
* feat(cli): add hyperframes lambda deploy/render/progress/destroy

Wraps the @hyperframes/aws-lambda SDK + the Phase 6a SAM template behind
a single CLI surface so an end-to-end render is three commands instead
of the ~8 manual bun+sam+aws steps the smoke script does today:

  hyperframes lambda deploy
  hyperframes lambda render ./my-project --width 1920 --height 1080 --wait
  hyperframes lambda destroy

Subcommands:
  - deploy:        build handler.zip + sam-deploy + persist stack outputs
                   to <cwd>/.hyperframes/lambda-stack-<name>.json
  - sites create:  pre-upload a project to S3 with a stable content hash
                   so re-renders skip the tar+PUT pass
  - render:        start a Step Functions execution; --wait blocks and
                   streams per-chunk progress + accrued cost
  - progress:      one-shot snapshot — status, frames, cost breakdown,
                   errors. Accepts renderId or executionArn
  - destroy:       sam-delete + drop the local state file (S3 bucket
                   is Retain'd by the template; documented in --help
                   and in docs/packages/cli.mdx)

To keep @sparticuz/chromium out of the CLI's transitive deps, this also
adds a dedicated ./sdk subpath export to @hyperframes/aws-lambda; the
CLI imports from @hyperframes/aws-lambda/sdk exclusively. The existing
. barrel still re-exports both handler + SDK for adopters who want one
entry point.

Defaults are deliberately cost-conservative for first-time users:
--concurrency=8 (low enough to never surprise) and --memory=10240 (the
common case; documented for adopters who want to tune down).

Tests: 5 unit tests on the state-file round-trip. CLI integration
against sam local invoke is part of the upcoming PR 6.6 (lambda-local
regression harness).

* refactor(cli): /simplify pass on the lambda command group

Two small cleanups on top of the lambda CLI:

  - Replace parseFormat / parseCodec / parseQuality / parseChromeSource
    (four near-identical helpers) with a single generic parseEnum() +
    typed const-tuple lookups. The four callers now read as one-line
    arrow functions that lift the allowed values out of the function
    body so they're easy to extend.

  - DEFAULT_STACK_NAME was const-declared then re-exported at the
    bottom of state.ts; just mark the const export inline.

No behavior changes. All CLI tests still pass.

* fix(cli): keep @hyperframes/aws-lambda external in the tsup bundle

esbuild can't bundle @hyperframes/aws-lambda's transitive AWS SDK
deps (@aws-sdk/* + @smithy/*) cleanly into a node binary — the
SDK's .browser.js conditional re-exports break the resolver:

  ESM Build failed
    No matching export in "splitStream.browser.js" for import
    "splitStream" (and ~10 similar errors)

Mark aws-lambda as `external` so esbuild doesn't follow it, and
move it from devDependencies to dependencies so the published CLI
can resolve it from node_modules at runtime. The lambda subverb
files dynamic-import only on `hyperframes lambda *` invocation, so
the CLI cold-start cost is unchanged.

The install-size hit (AWS SDK + @sparticuz/chromium ≈ 200 MiB) is
documented as a v1 tradeoff; a future split into a lambda-sdk-only
subpackage can pare this back.

* fix(cli): address PR review on lambda CLI

Two blockers + four important items from Vai's review:

  - `--memory` was parsed and recorded in the local state file but
    never forwarded to `sam deploy` as a parameter override. Worse,
    `progress.ts` then read the *recorded* value for cost math, so
    `--memory 5120` produced wrong cost numbers downstream. Thread
    `LambdaMemoryMb` through samDeploy's --parameter-overrides.

  - `--profile` was only consumed by deploy / destroy. render and
    progress fell back to the default credentials chain — a user
    with `--profile prod` would silently render against their
    default account (wrong-account billing footgun). Set
    `process.env.AWS_PROFILE` (and `AWS_REGION`) in the dispatcher
    before any subverb runs; the AWS SDK reads them natively, so
    render / progress / sites all benefit without each subverb
    threading the flag through the SDK call.

  - `--profile` + destroy now also reads `process.env.AWS_PROFILE`
    as a fallback (matching deploy's existing env fallback).

  - `--wait --json` printed both the start handle AND the final
    progress snapshot, producing two concatenated JSON blobs that
    `jq` rejected. Now emits a single document: handle (without
    --wait) OR final progress (with --wait).

  - Negative integers on `--width` / `--height` / `--chunk-size` /
    `--max-parallel-chunks` / `--memory` / `--concurrency` now fail
    loudly via a new `parsePositiveInt` wrapper instead of flowing
    into the SDK and producing opaque AWS validation errors mid-
    render.

  - `DEFAULT_STACK_NAME` is now centralized to the literal
    `"hyperframes-default"` and consumed from one place. Previously
    the value was assembled as `hyperframes-${"default"}` in three
    sites and hardcoded as `"hyperframes-default"` in a fourth.
    `requireStack`'s hint now matches the dispatcher's default.

The faked `SiteHandle` for `--site-id` keeps the documented
placeholder fields but also surfaces `bucketName` (from PR 909's
extended SiteHandle interface), matching the SDK contract.

All CLI unit tests + the full bundler build still pass.

* fix(cli): keep aws-lambda out of CLI runtime deps

The "Smoke: global install" CI step packs the CLI via `npm pack` and
installs it globally via `npm install -g <tgz>`. npm doesn't understand
the workspace: protocol, so a runtime `dependencies` entry of
`@hyperframes/aws-lambda: workspace:*` blows up with:

  npm error code EUNSUPPORTEDPROTOCOL
  npm error Unsupported URL Type "workspace:": workspace:*

(pnpm rewrites workspace:* on publish; npm pack doesn't.)

Three changes to unblock the smoke + keep the published CLI install
small for users who don't deploy to Lambda:

  - Move `@hyperframes/aws-lambda` from CLI's `dependencies` back to
    `devDependencies`. It's already external in tsup.config.ts; the
    bundle references it via runtime resolution only.

  - Convert the static `import { … } from "@hyperframes/aws-lambda/sdk"`
    in sites.ts / render.ts / progress.ts to `await import()` inside
    each function. tsup with `splitting: false` was inlining those
    static imports at the top of the bundle, which made Node eagerly
    resolve them at CLI startup (MODULE_NOT_FOUND before any lambda
    subcommand even runs). Dynamic imports stay dynamic in the bundle.

  - Add a friendly missing-module check in the lambda dispatcher.
    When a user runs `hyperframes lambda deploy / render / sites /
    progress / destroy` without aws-lambda installed, they now see:

      @hyperframes/aws-lambda is not installed.
      The `hyperframes lambda deploy` command needs it at runtime.
      Install it alongside the CLI:
        npm install -g @hyperframes/aws-lambda

Verified locally: pack + global install + `hyperframes init --example
blank` now succeeds end-to-end (was the same scenario the CI smoke job
runs).
2026-05-17 13:06:00 -04:00
Vance Ingalls 1596fcbe70 perf(producer): hybrid layered/parallel path for SDR shader-transition renders (hf#732 PR 4/5) (#759)
## Summary

PR 4 of 5 in the hf#732 decomposition stack. **This is where the bulk of the shader-transition speedup lives** (`~2×` verified — see Empirical validation below).

Spreads per-frame DOM capture work across N DOM worker sessions and offloads the per-pixel shader-blend onto a `worker_threads` pool (the pool added in #758).

### Gating

The hybrid path is gated by `shouldUseHybridLayeredPath`:

- SDR content only — HDR raw-frame sources are fd-bound to one worker (per-worker `dup(fd)` is out of scope here).
- `workerCount >= 2`.
- Not every frame inside a transition window.

When the gate trips, the hybrid loop spawns `workerCount - 1` extra DOM sessions, allocates per-worker scratch buffers, and partitions the frame range into contiguous slices via `distributeLayeredHybridFrameRanges`. Each worker walks its slice; transitions dispatch through the shader-blend pool (with inline fallback). A frame-reorder buffer fences the encoder.

Pool teardown is guaranteed via try/finally on both the success and error paths.

### Structural change (heads-up to reviewers)

`captureHdrStage.ts` on main was already 921 lines (over the project's 500-line ceiling). Adding the hybrid path on top would push it past 1100 and the local pre-commit hook refuses to stage files past 500. **PR 4 splits `captureHdrStage.ts` into 5 files**:

- `captureHdrStage.ts` (orchestrator + cleanup invariants, 469 lines)
- `captureHdrResources.ts` (HDR video extraction + image decode + dim probing)
- `captureHdrFrameShared.ts` (gating predicates, partitioning, per-scene capture)
- `captureHdrSequentialLoop.ts` (legacy single-session loop)
- `captureHdrHybridLoop.ts` (new multi-worker path)

No behavior change in any pre-existing code path: the sequential loop is byte-equivalent to the previous inline implementation (both consume `captureSceneIntoBuffer` from the shared module, so behavior parity is enforced structurally rather than by comment-keeping).

`renderOrchestrator.ts` is intentionally unchanged — the stage computes its own worker budget via `calculateOptimalWorkers` rather than receiving it through the call signature.

## Stack

Stacked on top of #758 (PR 3: shaderTransition pool).

## Test plan

- [x] 14 new vitest tests in `captureHdrFrameShared.test.ts` pinning the hybrid gating predicate and the contiguous-chunking partitioner — all pass
- [x] Producer typecheck clean
- [x] oxlint clean

### Empirical validation

Mark Witt fixture (Mac, Apple Silicon, hardware GPU, no beginframe):
- Published CLI (pre-stack): 2m 12.2s
- Cascade CLI (this stack): 1m 07.7s
- **Measured speedup: 1.95× on Mac.** (Earlier "2.22×" wording was a projection from per-component micro-benchmarks; the empirical end-to-end number is 1.95× on the validated fixture.)

Linux CI confirmation pending top-of-stack regression run.

— Vai
2026-05-13 20:38:25 -07:00
WadydX 2778845bd3 fix(cli): handle init -V with explicit migration error 2026-05-10 21:31:17 +01:00
JamesandClaude Opus 4.7 d2ca45ef75 feat(cli): add remove-background command for transparent video
Adds `hyperframes remove-background` — a local-AI subcommand that mattes a
video or image with the u2net_human_seg ONNX model and emits a transparent
WebM (VP9-alpha), ProRes 4444 .mov, or RGBA PNG. Drops directly into any
composition's <video> tag — no green screen, no API keys, no upload.

Auto-picks the fastest available execution provider via onnxruntime-node:
CoreML on Apple Silicon, CUDA when HYPERFRAMES_CUDA=1, CPU otherwise.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 04:17:51 +00:00
Miguel Ángel b947966a8b feat(cli): add visual inspect command (#480)
* feat: add layout audit command

* feat: refine visual inspect command
2026-04-25 02:07:47 +02:00
Miguel Ángel b4e9d64e29 feat(cli): hyperframes publish — share projects via a public URL (#312)
## Summary

This PR adds `hyperframes publish` as the OSS handoff into the persisted HyperFrames publish flow.

Instead of opening a local tunnel, the CLI now:

1. zips the local project
2. uploads it to the HeyGen publish backend
3. gets back a stable `hyperframes.dev` project URL plus claim token
4. prints a claimable URL for the user

Example output:

```bash
$ hyperframes publish

  Project    my-video
  Files      12
  Public     https://hyperframes.dev/p/hfp_123?claim_token=...

  Open the URL on hyperframes.dev to claim the project and continue editing.
```

## User Flow

The intended user flow is:

1. Run `hyperframes publish` from a local HyperFrames project.
2. The CLI uploads the project as a zip to the publish API.
3. The CLI prints a stable `hyperframes.dev` URL with the claim token attached.
4. The user opens that URL in the browser.
5. `hyperframes.dev` uses that URL to claim the published project and import it into the web app.
6. The user continues editing from a normal web session.

So the CLI is only responsible for packaging, upload, and printing the URL. The browser-side claim/import flow lives in the backend and web app stack.

## Routing

This PR does not expose a separate user-facing canary mode.

The CLI posts to the normal publish API host:
- `https://api2.heygen.com/v1/hyperframes/projects/publish`

Backend routing behavior is handled server-side. If the default path routes through canary, it does so without a dedicated CLI flag; if that path is unavailable, traffic falls back to prod behavior on the backend side.

## What Changed

| File | Role |
|---|---|
| `packages/cli/src/commands/publish.ts` | Adds the `hyperframes publish` command, confirmation prompt, lint-before-upload behavior, and user-facing output. |
| `packages/cli/src/utils/publishProject.ts` | Zips the local project, filters ignored files/directories, posts the archive to the publish API, and returns the published project metadata. |
| `packages/cli/src/utils/publishProject.test.ts` | Covers archive creation and successful upload response parsing. |
| `packages/cli/src/cli.ts` | Registers the new `publish` command. |
| `packages/cli/src/help.ts` | Adds `publish` to root help and examples. |
| `docs/packages/cli.mdx` | Documents the persisted publish flow. |

## Important Behavior

- Requires `index.html` at the project root.
- Ignores hidden files and common non-project directories like `.git`, `node_modules`, `dist`, `.next`, and `coverage`.
- Lints the project before upload and prints findings, but does not block publish on warnings.
- Does **not** keep a local process alive after upload.
- Does **not** open a public tunnel.
- Does **not** require HeyGen OAuth inside the CLI.

## Why This Shape

This keeps the OSS CLI simple and matches the current product direction:

- project persistence lives in HeyGen's backend
- the public URL comes from the persisted project row
- claiming/importing happens on `hyperframes.dev`
- the CLI should not own browser auth or long-lived sharing infrastructure

## Verification

In the earlier PR worktree, this flow was verified locally with the CLI build/test path and with real backend integration.

In this cleanup worktree, the narrow code/doc change was verified by inspection, but the repo-level commands are currently blocked here by missing local tool binaries and typings in the worktree environment:

- `bun run --filter @hyperframes/cli test` -> `vitest: command not found`
- `bun run --filter @hyperframes/cli typecheck` -> local dependency/type resolution failures outside this diff
- `bun run --filter @hyperframes/cli build` -> `tsx: command not found`

## Notes

This PR only covers the OSS CLI side of the flow.

The full end-to-end experience depends on the corresponding backend and `hyperframes.dev` changes that store published projects, return the stable URL, and support claim/import in the web app.
2026-04-23 04:11:34 +02:00
Miguel Ángel 5db554c7b6 feat(cli): silent auto-update on next run (#306)
## Summary

Today users have to run `hyperframes upgrade` (or the right install command for their package manager) to get a new release — we ship fixes but they don't reach the install until the user remembers. This PR borrows the Claude Code model: detect the update on run N, install it in a detached background child, surface one line ("hyperframes auto-updated to vX.Y.Z") on run N+1. The user's current command never blocks, never prompts, never sees an install stream.

## Flow across two runs

```
Run N     → checkForUpdate() sees latest > current → spawn detached
            child running `npm install -g hyperframes@X` (or bun /
            pnpm / brew equivalent). Parent exits immediately.
(between) → detached child installs, writes completedUpdate into
            ~/.hyperframes/config.json, clears pendingUpdate.
Run N+1   → reportCompletedUpdate() prints one line and clears the
            marker. User is on the new version.
```

## Installer detection

Walks `realpathSync(process.argv[1])` against each package manager's well-known global prefix. Wrong guesses are biased toward `skip` — we'd rather miss an auto-update than clobber a Homebrew install with npm.

| Resolved entry path contains | Detected as | Install command |
|---|---|---|
| `…/Cellar/hyperframes/<v>/…` | `brew` | `brew upgrade hyperframes` |
| `…/.bun/…` | `bun` | `bun add -g hyperframes@<v>` |
| `…/pnpm/global/…` or `…/.pnpm/…` | `pnpm` | `pnpm add -g hyperframes@<v>` |
| `…/lib/node_modules/hyperframes/…` | `npm` | `npm install -g hyperframes@<v>` |
| `…/packages/cli/…` (workspace link) | `skip` | (no-op) |
| `…/_npx/…`, `…/bunx-…/…` | `skip` | (no-op) |
| Anything else | `skip` | (no-op) |

## Guardrails

- **Never auto-update across a major version.** The existing banner still nudges the user to run `hyperframes upgrade` explicitly.
- **Skip on CI, non-TTY, dev mode,** npx / bunx / workspace link, or any install layout the detector doesn't recognize.
- **`HYPERFRAMES_NO_AUTO_INSTALL=1`** disables the install without silencing the notice banner.
- **`HYPERFRAMES_NO_UPDATE_CHECK=1`** silences both (existing knob).
- **Fresh pending install (<10 min old)** prevents re-launch on every invocation.
- **Installer stdout + stderr go to `~/.hyperframes/auto-update.log`** for postmortem — the terminal stays clean.
- **Failed installs are surfaced once** with a prompt to run `hyperframes upgrade` manually.

## What changed

| File | Role |
|---|---|
| `packages/cli/src/utils/installerDetection.ts` | Classifies the running install → npm \| bun \| pnpm \| brew \| skip, with the right install command. |
| `packages/cli/src/utils/autoUpdate.ts` | `scheduleBackgroundInstall` + `reportCompletedUpdate`. Spawns a detached `node -e "..."` child that runs the install and writes the outcome back to the config, then `unref()`s so the parent exits immediately. |
| `packages/cli/src/telemetry/config.ts` | `pendingUpdate` + `completedUpdate` fields on the config schema. |
| `packages/cli/src/cli.ts` | Wires `reportCompletedUpdate()` at startup and `scheduleBackgroundInstall()` after `checkForUpdate()` resolves. |

## Verification

### Unit tests — 19 / 19 pass (full CLI suite 115 / 115)

- `installerDetection.test.ts` — 9 cases, one per layout (workspace, npx, bunx, brew, bun, pnpm, npm, unknown, unresolved).
- `autoUpdate.test.ts` — 10 scheduling-policy cases:
  - Minor/patch → schedules + writes pendingUpdate
  - Major bump → **does not** schedule
  - Dev mode → skipped
  - `CI=1` → skipped
  - `HYPERFRAMES_NO_AUTO_INSTALL=1` → skipped
  - Unknown installer → skipped
  - Already-on-latest → skipped
  - Fresh pending install → de-duplicated
  - Stale pending install (>10 min) → supersedes
  - Previous run already completed this version → skipped

Unit tests mock `spawn` and the installer — they verify the **policy**, not the real detached-child path.

### Live end-to-end smoke test (on this Mac, real processes)

To validate the parts the unit tests can't — actual detached spawn, real config writeback, banner surfacing in a fresh subsequent process — I wired a smoke script that exercises the exact same code path `autoUpdate.ts` uses, but with `echo …` as the "install command" so nothing global gets touched.

**Steps exercised:**

1. Backed up the user's real `~/.hyperframes/config.json`.
2. Wrote a `pendingUpdate` marker for version `0.4.99` (like `scheduleBackgroundInstall` does).
3. Spawned the **exact same detached `node -e "..."` child** the real scheduler produces, with the install command replaced by `echo 'faux install for 0.4.99'`.
4. The parent `unref()`d and continued; 800 ms later the parent re-read `config.json`.
5. Ran `reportCompletedUpdate()` in a **fresh subprocess** (via `bunx tsx -e ...`) to match the real "Run N+1" conditions, capturing its stderr.
6. Asserted the marker was cleared.
7. Restored the original config on exit.

**Observed output:**

```
  [setup] Backed up config to /Users/miguel/.hyperframes/config.json.smoke-backup
  [setup] Wrote pendingUpdate for v0.4.99
  [spawn] Detached child pid=49469
  [after] completedUpdate = {"version":"0.4.99","ok":true,"finishedAt":"2026-04-17T17:26:50.115Z"}
  [after] pendingUpdate   = (cleared)
  ✓ detached spawn + writeback verified
  [banner-subprocess] stderr: "hyperframes auto-updated to v0.4.99"
  ✓ banner fired in fresh process + marker cleared

  ALL CHECKS PASSED ✓
  [cleanup] Config restored
```

**What this proves:**

| Claim | Evidence |
|---|---|
| Detached spawn works (doesn't block the parent) | `[spawn] pid=49469` logged, parent continued immediately |
| Detached child is process-independent | Parent exited its own work while child ran `exec(CMD)` |
| Child writes correct config shape | `completedUpdate = { version: "0.4.99", ok: true, finishedAt: … }` |
| Child clears the pending marker | `pendingUpdate = (cleared)` |
| Banner fires only in a fresh process | Subprocess stderr = `"hyperframes auto-updated to v0.4.99"` |
| Banner message format | Matches the copy in `autoUpdate.ts:reportCompletedUpdate` exactly |
| Marker clears after banner | Second file read shows `completedUpdate` absent |

Both the original test-plan checkboxes (fresh install, `HYPERFRAMES_NO_AUTO_INSTALL=1`, `CI=1`) are covered by either the unit-test suite or this smoke test — the scheduling-policy gates are unit-tested under `CI=true`, and the real detached-spawn path is smoke-tested above.

### What's still worth doing

- **Physical installer test on a real `npm i -g` / `brew` / `bun add -g` environment** — the smoke test above replaces the install command with `echo`, so we've never actually seen npm/bun/brew run the real command. That's the one remaining unknown. Worth one manual run on the maintainer's machine before cutting v0.4.4.

## Test plan

- [x] `bunx vitest run` on `packages/cli` — 115 / 115 pass (incl. 19 new)
- [x] `tsc --noEmit` clean
- [x] `tsup` build clean
- [x] **Live e2e smoke test** exercising the real detached spawn + config writeback + fresh-process banner (output above)
- [x] CI green on this branch (Typecheck, Test, Test: runtime contract, Build, Lint, Format)
- [ ] One manual run on a physical `npm i -g hyperframes@0.4.2` install to confirm the real `npm install -g hyperframes@0.4.3` command actually runs when `autoUpdate.ts` delegates to it (the smoke test stopped short of executing `npm`)

## Notes

- Independent of any version bump — ship whenever.
- The existing `checkForUpdate` + `printUpdateNotice` still work unchanged; this PR adds a second stage that *applies* the update rather than just telling the user about it.
- `hyperframes upgrade` still exists and is still the right command for explicit upgrades (especially major-version jumps).
2026-04-21 00:05:04 +02:00
Ular KimsanovandClaude Opus 4.6 87f4c77e2f feat: website capture pipeline + 7-step video production skill (#284)
* feat(cli): add website capture with AI-powered DESIGN.md generation

Adds `hyperframes capture <url>` command that extracts a complete design
system from any website, producing AI-agent-ready output:

- Full-page screenshot (lazy-load aware, nav at top)
- AI-generated DESIGN.md via Claude API (colors, typography, elevation,
  components, do's/don'ts) with programmatic asset catalog (136+ assets
  with HTML context annotations like img[src], css url(), link[rel=preload])
- CSS-purged compositions (87% size reduction via PurgeCSS)
- HTML-prettified compositions (one-tag-per-line for AI readability)
- CLAUDE.md + .cursorrules auto-generated for AI agent instructions
- Asset deduplication (srcset variants) and tracking pixel filtering

* feat(cli): add gemini 3.1 pro, playwright screenshots, replica refinement

- switch to gemini 3.1 pro (gemini-3.1-pro-preview) with claude fallback
- playwright for full-page screenshots (fixes puppeteer gradient/fixed bugs)
- replica refinement loop: generate, screenshot, compare, fix
- extract inline svgs (50 max, 10kb each) to assets/svgs/
- extract visible text in dom order for content accuracy
- detect js libraries (gsap, three.js, scrolltrigger) via globals
- improved asset catalog grouping and naming
- reverse-engineered aura system prompt documentation
- comprehensive session handoff doc

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: update session handoff with slack research findings

- key finding: team already wants DESIGN.md integration (James, Bin, Vance)
- skills quality matters enormously - must invoke /hyperframes-compose
- eval infrastructure exists (Abhay's dashboards, Teodora's 78-criteria guide)
- templates at templates/ need study before finalizing skill
- session handoff updated with critical next steps

* refactor(cli): simplify capture pipeline, remove replica generator

* feat(capture): add Lottie detection and WebGL shader extraction

Captures Lottie animations via network interception and WebGL shader
source via gl.shaderSource hooking during site crawl. Updates
website-to-hyperframes skill with asset planning guidance, Lottie/shader
reading instructions, and stronger creative direction for scene planning.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(capture): clean pipeline + shader-first creative workflow

Capture pipeline:
- Remove dead deps (puppeteer-extra, stealth plugin, duplicate devDeps)
- Remove duplicate generateAgentPrompt() call (first lied about DESIGN.md)
- Remove dead canvas-to-image code in htmlExtractor (post canvas removal)
- Parallelize image downloads (batches of 5 via Promise.allSettled)
- Fix pre-existing TS error (match[1] guard in font downloader)
- Default capture output to captures/<hostname>

Skill creative overhaul:
- Add shader transition selection to creative director step (Step 4)
- Add shader wiring instructions to engineer step (Step 5)
- Replace 4-line energy modifiers with visual vocabulary table
- Strip rigid scene-by-scene templates from video-recipes.md
- Strip example fill data from scene plan tables
- Add "read transition refs before planning" instruction
- Add creative ambition language ("how the hell did they make this")

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add skill architecture redesign spec

Comprehensive redesign of website-to-hyperframes skill and capture
pipeline based on code review findings and Claude Code architecture
research. Key changes: remove AI auto-generation, restructure skill
into phases, embed shader boilerplate in scaffold, fix color format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add implementation plan for skill architecture redesign

13-task plan covering: capture pipeline cleanup (remove AI generation,
fix colors to HEX, add asset descriptions, shader-ready scaffold),
skill restructuring (4 phases with artifact gates), and compose skill
Visual Identity Gate upgrade.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(capture): remove AI auto-generation and SDK dependencies

* fix(capture): convert extracted colors to HEX format

* refactor(capture): remove AI key path, add asset descriptions generator

* refactor(capture): update agent prompt, remove hasDesignMd, add asset descriptions

* feat(capture): pre-wire shader transitions in index.html scaffold

* chore: remove duplicate visual-styles.md (canonical is in hyperframes/)

* refactor(skill): rewrite website-to-hyperframes as phase-based orchestrator

* feat(skill): add Phase 1 understand reference

* feat(skill): add Phase 2 design reference with full DESIGN.md schema

* feat(skill): add Phase 3 creative direction reference

* feat(skill): add Phase 4 build reference with inline shader example

* feat(skill): upgrade Visual Identity Gate to produce full DESIGN.md

* docs: update CLAUDE.md skill references for phase-based workflow

* fix: address code review findings

- Remove orphaned `false` argument in generateAgentPrompt call (critical:
  was shifting hasLottie, hasShaders, catalogedAssets parameters)
- Add HSL color handling in rgbToHex via temp element resolution
- Remove build artifact commit section from phase-4-build.md
- Fix __GSAP_TIMELINE reference to __timelines

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(capture): regex double-escape + simplify scaffold + fix asset descriptions

- Double-escape regex in tokenExtractor template literal (\s→\\s, \d→\\d, \(→\\()
  so browser receives valid regex patterns via page.evaluate()
- Simplify index.html scaffold: scene slots + audio + timeline + comment pointing
  to shader-setup.md reference (no broken inline shader boilerplate)
- Fix asset descriptions: use CatalogedAsset.contexts/notes instead of
  nonexistent htmlContext field

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: code review — 16 bugs, 7-step skill rewrite, cleanup

Code fixes:
- snapshot.ts: path traversal guard, browser leak (try/finally), div-by-zero
  for --frames 1, port bind error handling, rAF-based render settle
- index.ts: remove invalid thinkingConfig for gemini-2.5-flash, fix Gemini
  batch/rate-limit comments, fix video preview viewport y-coordinate
- tokenExtractor.ts: remove dead seen[si] dedup code
- gsap.ts: index ALL classes for inline-style transform conflict detection

Skill architecture rewrite (4-phase → 7-step):
- Replace phase-1 through phase-4 with step-1 through step-7
- Add techniques.md (10 visual techniques with code patterns)
- Fix /hyperframes-compose → /hyperframes (skill doesn't exist)
- Fix captures/arc-browser reference → shader-setup.md (file doesn't exist)
- Fix step-7 hardcoded captures/stripe path
- Document Gemini API free/paid rate limits in step-1

Cleanup:
- CLAUDE.md: restore from Stripe-capture overwrite, update 4-phase → 7-step
- .gitignore: add PR #267 skills (hyperframes-animation-map, hyperframes-contrast)
- Delete old phase-*.md, animation-recreation.md, tts-integration.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove dev artifacts, research docs, wrong lockfiles

Remove files that shouldn't ship in this PR:
- docs/research/ (aura analysis, prompt catalogs)
- docs/session-*.md, docs/SESSION-HANDOFF.md (dev notes)
- docs/superpowers/ planning and spec docs
- pnpm-lock.yaml at root and cli (repo uses bun, not pnpm)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(CLAUDE.md): align with main — slim format, add website-to-hyperframes mention

Main PR #283 removed the full skills table from CLAUDE.md and moved it
to AGENTS.md. Align with that decision: use main's slim dev-focused
format, fix pnpm→bun references, add one-line /website-to-hyperframes
pointer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cli): add capture command to help groups

The capture command was registered in cli.ts but missing from
the help groups, so it wouldn't appear in `hyperframes --help`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: format skill reference files (oxfmt)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: regenerate bun.lock after rebase

The lockfile was stale after rebasing onto main — bun install
--frozen-lockfile failed in CI because new dependencies (google/genai,
patchright, purgecss) weren't reflected in the lockfile.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR review comments + improve capture quality

Review fixes (16 comments from jrusso1020 + vanceingalls):
- screenshotCapture: remove Playwright dep, use Puppeteer for all screenshots
- screenshotCapture: dynamic screenshot count based on page height (30% overlap)
- snapshot.ts: fix duration() function-vs-property bug, cross-platform path guard
- htmlExtractor: fix code injection via parameterized evaluate
- index.ts: video preview re-measures position after scroll, .env file loading
- capture.ts: BLOCKED.md on timeout failures
- gsap.ts: 5 inline-style lint tests added (all pass)
- Remove Playwright, patchright deps; @google/genai to optionalDependencies
- Gitignore: generic patterns instead of 20 hardcoded directories
- Remove asset-sourcing.md, video-recipes.md (unused, duplicated guidance)

Capture quality improvements (tested on 10+ websites):
- Color extraction: canvas-based oklch/lab resolver, pixel sampling via
  elementFromPoint, broad sweep for accent colors, gradient/shadow extraction
- Section detection: broadened selectors for div-based layouts, height cap
  to skip page-level wrappers, parent bg walkup for dark sites
- Font downloads: cap 6 per family / 30 total (Cal.com: 306→30)
- CTA detection: text pattern matching + nav context filtering
- Heading text: innerText with whitespace normalization
- Gemini captioning: maxOutputTokens 100→300, .env auto-loading
- .env.example updated with GEMINI_API_KEY docs
- TTS ranking: Kokoro first with Python 3.10+ note

* fix: address PR review comments + improve capture quality

Review round 2 fixes (jrusso1020 + vanceingalls):
- verify/index.ts: add path traversal guard (relative + isAbsolute)
- verify/index.ts: fix sections[i] undefined typecheck error (CI green)
- index.ts: escape Lottie JSON with \u003c to prevent </script> breakout
- step-4-storyboard: fix technique count contradiction (2-3 per beat, not
  across whole video)
- step-6-build: perspective tilt uses gsap.set() instead of CSS transform
  (avoids GSAP overwrite conflict)
- step-1-capture: reorder — command first, Gemini note after (zero-config
  is the default path, API key is optional enhancement)
- step-7-validate: add tsx fallback for snapshot command
- step-3-script: vary hook patterns, don't default to number every time
- assetDownloader: exempt SVGs from 10KB minimum filter (company logos
  like Hubspot/Intel/DHL are 2-6KB; HeyGen capture: 13→75 assets)

Note: adm-zip was NOT removed (reviewer #3) — it's still in
packages/cli/package.json:30. The root package.json had patchright
and purgecss removed, not adm-zip.

Note: ANTHROPIC_API_KEY not restored in .env.example — grep confirms
zero references in the entire codebase. The @anthropic-ai/sdk dependency
was removed earlier in this branch.

* refactor(capture): split index.ts (1175 to 566 lines) into modules

Mechanical extraction, zero logic changes.

New files:
- mediaCapture.ts (345 lines): Lottie preview, video manifest/screenshots
- contentExtractor.ts (314 lines): library detection, text, Gemini, asset descriptions
- scaffolding.ts (135 lines): .env loading, project scaffold generation

Also fixes false-positive BLOCKED.md with structural Cloudflare detection.
Tested on 20 websites, pre/post output identical.

* chore(capture): remove --split flow (splitter, verify, cssPurger, purgecss)

The --split feature auto-generates compositions from captured HTML — a
different approach from the /website-to-hyperframes skill workflow where
agents build compositions from scratch using the storyboard.

No skill file, no step reference, and no test session ever used --split.
Removes 923 lines of unused code + purgecss dependency.

Backed up to ~/Desktop/capture-split-backup/ for reference.

* fix(security): add ssrf protection, lottie injection fix, oom guard

- assetDownloader: add isPrivateUrl() guard blocking private IP ranges
  (127.x, 10.x, 172.16-31.x, 192.168.x, 169.254.x), cloud metadata
  endpoints, localhost, and non-HTTP schemes
- mediaCapture: fix Lottie JSON injection by loading shell HTML first
  then passing animation data via parameterized page.evaluate()
- index.ts: check Content-Length header before response.buffer() in
  Lottie network interception to avoid OOM on multi-GB responses

* fix(capture): security fixes, timeout, sub-agent dispatch instructions

Security (from miguel-heygen review):
- assetDownloader: export isPrivateUrl() SSRF guard
- htmlExtractor: add isPrivateUrl check before CSS fetch
- mediaCapture: add isPrivateUrl check before Lottie fetch
- mediaCapture: fix previewPage leak (try/finally)
- mediaCapture: skip Lottie files > 2MB for preview (CDP limit)
- contentExtractor: skip images > 4MB for Gemini captioning
- index.ts: check Content-Length before response.buffer() (OOM guard)
- snapshot.ts: register error handler before server.listen()

Capture improvements:
- Default timeout 30s to 120s (Shopify needs ~90s for Cloudflare)
- step-6-build: sub-agent dispatch template with explicit rules:
  pass file PATHS not contents, use local fonts not Google Fonts,
  verify ../assets/ references after each beat

* fix(capture): catalog before DOM mutation, networkidle2, faster Gemini

Critical: asset cataloger now runs BEFORE extractHtml which converts img
src to data URLs. Framer sites like heykuba.com went from 2 to 78 images.

- networkidle2 instead of networkidle0 (unblocks SPAs with WebSockets)
- Lazy-load wait: scroll to bottom, wait for img.complete
- CSS background-image cataloging for Framer/Webflow
- SVG naming: checks class, id, parent, inner text (not just aria-label)
- Gemini batch 5->20, pause 12s->2s (paid tier: 2000 RPM, ~0.001/img)
- maxOutputTokens 300->500, descriptions sorted captioned-first
- Remove tsx fallback from step-1 (reviewer nit, published CLI has it)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:03:16 -07:00
James Russo 13ab1932ad feat(cli): catalog browser command (#271)
Adds `hyperframes catalog` for browsing the registry:

- Default: non-interactive table output (agent-friendly)
- --type block/component and --tag filters
- --json for machine-readable output
- --human-friendly for interactive picker that installs on select

Registered in cli.ts, help.ts, documented in docs/packages/cli.mdx.
2026-04-14 16:46:42 -07:00
James Russo 08fb1de61f feat(cli): add command + hyperframes.json (#256)
## What

PR 5/17 of the catalog system rollout. Adds the `hyperframes add` verb for installing blocks and components from the registry into an existing project, plus the `hyperframes.json` project config that tells `add` which registry to use and where to drop files. Stacks on #255.

- **`packages/cli/src/commands/add.ts`** — new `hyperframes add <name>` command. Resolves an item, validates target paths, installs files in parallel, builds an include snippet, copies it to the clipboard. Exposes a testable `runAdd(opts)` function; the citty default wraps it with console output + exit handling
- **`packages/cli/src/utils/projectConfig.ts`** — read/write/normalize `hyperframes.json`. Tolerant to missing and partial configs
- **`packages/cli/src/utils/clipboard.ts`** — minimal cross-platform clipboard (pbcopy / clip.exe / wl-copy / xclip / xsel). Zero deps. Gracefully no-ops in headless environments
- **`packages/cli/src/commands/init.ts`** — write `hyperframes.json` during scaffold if not already present
- **`packages/cli/src/cli.ts`** + **`help.ts`** — register `add` under Getting Started (directly below `init`)

Design doc: [Hyperframes Catalog System](https://www.notion.so/heygen/Hyperframes-Catalog-System-Design-Plan-341449792c69813f899dcd53b4c0383a).

## UX

```bash
# Scaffold a project (now writes hyperframes.json too)
npx hyperframes init my-video --example blank
cd my-video

# Add a block — files land, snippet copied to clipboard
npx hyperframes add claude-code-window
#  ✓ Added claude-code-window (hyperframes:block)
#    compositions/claude-code-window.html
#
#  Include snippet:
#    <iframe src="compositions/claude-code-window.html" data-start="0" data-duration="6"></iframe>
#
#  Copied to clipboard — paste into your host composition.

# Add a component effect
npx hyperframes add shader-wipe

# Headless / CI — no clipboard, JSON output for tooling
npx hyperframes add shader-wipe --no-clipboard --json
```

Running `hyperframes add warm-grain` (an example) errors clearly pointing to `init --example`.

## Docs (bundled in this PR per the tracker principle)

- `docs/packages/cli.mdx` — new `add` subsection under Commands (flags, examples, trigger rules) + new `hyperframes.json` section describing the config file shape

## Tests

- **`packages/cli/src/commands/add.test.ts`** — 11 tests:
  - `remapTarget` / `buildSnippet` pure helpers (5 tests)
  - `runAdd` integration against a mocked `fetch` registry: block install lands files + returns snippet, component install respects `paths.components` remap, example-typed names throw `AddError` with code `example-type`, unknown names throw `AddError` with code `unknown-item` (4 tests plus 2 covering block default path and non-default path preservation)
- **`packages/cli/src/utils/projectConfig.test.ts`** — 9 tests:
  - Write/read round-trip, partial-config normalization, corrupt-file handling, absent-file fallback to defaults, custom paths preserved
- **CLI suite:** 92 passed (was 72 on #255, **+20**). Same 4 pre-existing failures unchanged

## Scope decisions

- **`init.ts` full port to new resolver deferred.** The original plan bundled a removal of the `packages/cli/src/templates/` compat shim. That's ~300 more lines and isn't required for `add` to work. The compat shim from #254 still functions; a separate cleanup PR handles it
- **No ajv runtime schema validation.** Manifests are trusted as schema-valid. Full validation lands when third-party registries arrive (PR 14/15). Path safety is still enforced by the installer's `assertSafeTarget` guard
- **Default project paths stay under `compositions/`.** Blocks → `compositions/<name>.html`; components → `compositions/components/<name>/<file>`. Users override via `hyperframes.json#paths`

## Breaking / migration

**None.** Pure additive — new command, new file types, no existing commands or flags change. `init.ts` now writes `hyperframes.json` but that's a new additional file, not a modification of existing output.

## Stacks on

#255 — base branch. When #255 merges, this rebases onto `main`.

## Next in stack

PR 6 — `feat(registry): seed block — claude-code-window`. First real registry item. Exercises the full `hyperframes add <name>` flow end-to-end against a committed item on `main`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-04-13 21:04:59 -07:00
Miguel Ángel 5655dabff6 feat: allow clip animation + ship <hyperframes-player> web component (#209)
## Summary

Two independent initiatives that improve agent DX and expand HyperFrames' reach.

### Initiative 1: Fix the Clip Animation Footgun

- `gsap_animates_clip_element` lint rule now uses smart detection — only errors when GSAP animates `visibility` or `display` on a clip element
- All other properties (opacity, transform, x, y, scale, etc.) are allowed silently
- This was the #1 agent failure in QA (10/10 agents hit it on v0.2.1)

### Initiative 2: `<hyperframes-player>` Web Component

- New `@hyperframes/player` package — zero dependencies, 3.3KB gzipped
- Iframe-based web component with Shadow DOM for perfect isolation
- Video-like API: `play()`, `pause()`, `seek()`, `currentTime`, `duration`, events
- Controls overlay with play/pause, scrubber (mouse + touch), time display, auto-hide
- Full docs page at `docs/packages/player.mdx`

## Before / After

### Clip animation lint

**Before (10/10 agents hit this):**

```
✗ gsap_animates_clip_element: GSAP animation targets a clip element.
  Selector "#title" resolves to element <div id="title" class="clip">.
  The framework manages clip visibility — animate an inner wrapper instead.
  Fix: Wrap content in a child <div> and target that with GSAP.
```

**After (only errors on actual conflicts):**

```
# This passes lint — no error:
tl.from("#title", { opacity: 0, y: -50, scale: 0.8 }, 0);

# This still errors — actual conflict with runtime:
tl.to("#title", { visibility: "hidden" }, 3);
✗ gsap_animates_clip_element: GSAP animation sets visibility on a clip element.
  Fix: Remove the visibility/display tween. Use opacity for fade effects.
```

### Embeddable player

**Before:** No way to embed a composition in a web page.
**After:**

```html
<script src="https://cdn.jsdelivr.net/npm/@hyperframes/player"></script>
<hyperframes-player src="./composition/index.html" controls></hyperframes-player>
```

```js
const player = document.querySelector('hyperframes-player');
player.play();
player.pause();
player.seek(2.5);
player.addEventListener('ready', (e) => console.log('Duration:', e.detail.duration));
```

## Test plan

- [x] 427 core tests pass (20 GSAP lint tests with smart detection)
- [x] 7 player tests pass (formatTime + element registration)
- [x] TypeScript compiles cleanly (core + player)
- [x] Lint: GSAP animating clip with safe props → 0 errors
- [x] Lint: GSAP animating clip with `visibility` → 1 error (correct)
- [x] Player builds to 3.3KB gzipped ESM
- [x] Lockfile updated for CI
- [x] Docs page added at `docs/packages/player.mdx`
2026-04-06 19:59:39 +02:00
Miguel ÁngelandClaude Opus 4.6 49e333e9de feat(cli): add skills command that installs all without selection (#192)
* feat(cli): add skills command that installs all without selection

Wraps `npx skills add --all -g` so users don't need to manually
select skills or targets. Just run `hyperframes skills` and everything
gets installed to all supported AI tools.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cli): fix typecheck errors in skills command

Use spawn instead of execFile to avoid stdio type mismatch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:11:56 -07:00
James RussoandClaude Opus 4.6 7389c0c89b feat(cli): add tts command for local text-to-speech via Kokoro-82M (#201)
* 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>
2026-04-02 14:09:25 -07:00
Vance Ingalls d36c1785b9 feat(captions): energy-based technique selection and mandatory quality checks (#176)
## Summary

- Rewrite script-to-style mapping as an energy detection table (high → low) with mandatory animation requirements: karaoke baseline, 2+ highlight techniques, kinetic exits
- Replace `tl.call()` per-frame audio-reactive pattern with group-level GSAP tweens — read peak bass/treble for each group's time range and modulate entrance intensity at build time, no per-frame callbacks needed
- Add transcript quality check with automatic retry rules (>20% music tokens = retry with larger model)
- Add caption word structure lint rule (`.caption-group` + `<span>`) for studio editor compatibility
- Add multilingual model guidance and decision tree for model selection

## Test plan

- [ ] Skill files render correctly as markdown
- [ ] Cross-references between SKILL.md, dynamic-techniques.md, and transcript-guide.md resolve correctly
- [ ] `dynamic-techniques.md` audio-reactive section uses `tl.to()`/`tl.set()` only, no `tl.call()` loops

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-04-02 00:47:43 -07:00
Vance Ingalls ad2d63db32 feat(cli): skill install targets + remove custom install in favor of vercel-labs/skills (#177)
## Summary

**Skill install targets (original):**
- Add project-level skill install targets: Windsurf, Cline, Roo Code, Trae (opt-in via flag)
- Split install logic into global vs project-level
- Fix lint false positive: timed tags with `data-composition-id` no longer flagged by media rule

**Skill system cleanup (folded from #189):**
- Delete `install-skills.ts` (~485 lines) — remove custom installation wrapper entirely
- Strip skill logic from `init` — no more project-level `.claude/skills/` copies, no `--skip-skills` flag; replaced with post-scaffold message: `npx skills add heygen-com/hyperframes`
- Front-load SKILL.md trigger words — all 5 skill descriptions rewritten so activation language comes first (~150 chars)
- Update CLAUDE.md — install instructions now point to [vercel-labs/skills](https://github.com/vercel-labs/skills)
- Fix `.claude/settings.json` — pre-commit hook changed from `pnpm` to `bun`

## Test plan

- [ ] `npx hyperframes skills` → "Unknown command skills"
- [ ] `npx hyperframes init test --template blank --non-interactive --skip-transcribe` → prints `npx skills add heygen-com/hyperframes`
- [ ] `grep -r "install-skills" packages/cli/src/` → no results
- [ ] All 5 `skills/*/SKILL.md` have front-loaded descriptions

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-04-02 00:47:25 -07:00
James RussoandClaude Opus 4.6 2d30654632 feat(cli): improve --help with grouped commands and per-command examples (#184)
* 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>
2026-04-01 12:55:26 -07:00
Miguel Ángel 5bb74cc0fc feat(cli): simplify init flow (#163)
## Summary

- **Streamline init from 7 steps to 3**: name → template → auto-opens studio preview
- **Remove** interactive "Got a video or audio file?" prompt (`--video`/`--audio` flags still work)
- **Remove** "What do you want to do?" post-scaffold menu (auto-launches preview)
- **Fast startup**: `--version` exits in ~10ms (lazy-load telemetry + update checker)
- **Brand teal**: CLI accent color is now `#3CE6AC` instead of generic cyan

### Before

```
name → overwrite? → media file? → [drag file] → transcribe? → pick template (9) → what next?
```

### After

```
name → pick template → auto-opens studio
```

## Test plan

- [x] `hyperframes init my-project` — should prompt name + template, then auto-open preview
- [x] `hyperframes --version` — should print version instantly (~10ms)
- [x] `hyperframes init --video path.mp4 my-project` — video flag still works
- [x] `hyperframes init --template blank my-project` — template flag still works
- [x] Verify teal accent color in terminal output
2026-03-31 21:16:18 +02:00
Vance IngallsandClaude Opus 4.6 9cbfec1eca feat(skills): add hyperframes-cli skill (#154)
* feat(skills): add hyperframes-cli skill for CLI workflow guidance

Adds a new skill that teaches AI agents how to use the HyperFrames CLI
(init, lint, dev, render, doctor). Previously, agents had no way to
discover the CLI — the compose-video skill only covered HTML authoring.
This led to agents searching for binaries, finding the monorepo, and
running bun run studio manually instead of using npx hyperframes dev.

Also registers the skill in init.ts so new projects get it bundled
alongside hyperframes-compose and hyperframes-captions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(cli): rename dev command to preview

The command starts a preview server — "preview" describes what users
are doing more accurately than "dev". Updates the command name, file
name, all CLI references, docs, skills, and template CLAUDE.md.

22 files updated across CLI source, docs, skills, and templates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(skills): replace stale dev reference with preview in CLI skill

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(docs): catch remaining dev references missed in rename

- testing-local-changes.mdx: two inline command examples
- troubleshooting.mdx: anchor link #dev → #preview, "dev server" → "preview server"
- cli.mdx: "dev server" → "preview server"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 00:30:55 -07:00
James RussoandClaude Opus 4.6 2f99e33bbe feat(cli,core): standalone transcribe command, transcript normalization, caption lint rules (#151)
* 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>
2026-03-30 18:58:06 -07:00
JamesandClaude Opus 4.6 cd15c68a2e feat(cli): add version check system with agent-friendly output
- 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>
2026-03-27 01:41:34 +00:00
Vance IngallsandClaude Opus 4.6 5fceab9279 feat(cli): add skills install command and init integration (#48)
* fix(ci): publish CLI from temp copy to avoid workspace mutation

Copy packages/cli to a temp directory before renaming to "hyperframes"
for publish. Avoids corrupting the workspace if the job fails mid-way.

Addresses review feedback on #47.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ci): resolve leftover conflict markers in publish.yml

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(cli): add skills install command

Adds `hyperframes skills install` to download and install HyperFrames
and GSAP skills globally to ~/.claude/skills/. Also adds
`hyperframes skills list` to show installed skills.

- HyperFrames skills: bundled in CLI dist, copied from dist/skills/
- GSAP skills: cloned from github.com/greensock/gsap-skills
- Cache: ~/.cache/hyperframes/gsap-skills/ (shallow clone, updated on install)
- Handles overwriting existing skills (removes before copy)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(cli): simplify skills to flat command

`hyperframes skills` directly installs + shows summary.
No subcommands needed — list was redundant since install
already prints all installed skills.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cli): scope skills summary to only HyperFrames and GSAP skills

The summary now only lists skills installed by this command, grouped
by source (HyperFrames vs GSAP), instead of listing everything in
~/.claude/skills/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cli): correct dev path for bundled skills directory

Path needed 4 levels up from cli/src/commands/ to reach repo root,
not 3. Was resolving to packages/.claude/skills/ instead of
.claude/skills/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(cli): support multiple AI coding tools for skills install

Install to Claude Code, Gemini CLI, and Codex CLI by default.
Use flags to target specific tools:

  hyperframes skills              # claude + gemini + codex
  hyperframes skills --cursor     # cursor only (project-level)
  hyperframes skills --claude     # claude only

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(cli): move skills to repo root, support multi-CLI install

- Move skills from .claude/skills/ to skills/ (tool-agnostic location)
- Install to Claude Code, Gemini CLI, Codex CLI by default
- Add --claude, --gemini, --codex, --cursor flags for targeting specific tools
- Update build script to copy from skills/ instead of .claude/skills/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add infographics skill for data visualization compositions

Professional infographic design and animation patterns:
- Typography hierarchy (hero stat, label, context)
- Layout rules (grid-aligned, generous whitespace, 2-color max)
- 5 infographic types: single stat, comparison, bar chart, progress, steps
- Animation patterns: count-up, bar growth, entrance choreography, exits
- Narration sync (stat appears when narrator says the number)
- Design constraints (no gradients, no shadows, no clip art)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add evals for infographics skill

Three eval scenarios testing design quality and animation correctness:
1. Single stat — count-up animation synced to narration
2. Comparison — before/after reveal with visual hierarchy
3. Process steps — sequential reveal with dimming

Each eval has PASS/FAIL criteria covering: composition structure,
design rules (typography, layout, color), animation patterns
(timing, easing, choreography), and anti-patterns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: remove infographics and transitions skills, add asset-management

Removed skills that don't add value beyond model knowledge:
- infographics: model already produces equivalent output without it
- transitions: patterns are derivable from compose-video constraints

Added:
- asset-management: organize user-uploaded files into assets/ and fonts/
- Typography section in compose-video: min font sizes, font loading
  (Google Fonts + local @font-face), weight pairing, font-display:block
- Assets section in compose-video: project structure with assets/ and
  fonts/ directories, path rules, CORS, "check before creating" rule

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: consolidate skills to 3, split compose-video under 500 lines

Removed skills that don't add value beyond model knowledge:
- media: duplicates compose-video
- social-media: platform safe areas are the only unique content
- infographics: model produces equivalent output without it
- transitions: patterns derivable from compose-video

Remaining skills (3):
- compose-video: core framework contract (452 lines, under 500 limit)
  - patterns.md: PiP, title card, slideshow examples (loaded on demand)
  - typography-and-assets.md: font loading, sizes, asset paths (on demand)
- captions: tone-adaptive caption styling from script analysis
- asset-management: organize uploaded files into project directories

Rewrote captions skill to focus on style detection from transcript
content (per-word styling, tone mapping) rather than mechanical rules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(cli): create assets/ and fonts/ directories on init

- scaffoldProject now creates assets/ and fonts/ directories
- Video files are placed in assets/ instead of project root
- Template __VIDEO_SRC__ placeholders resolve to assets/filename

Aligns with the compose-video skill's project structure convention
where user-provided media goes in assets/ and fonts in fonts/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(cli): fetch skills from GitHub repos instead of bundling

Skills are now fetched directly from their source repos at runtime:
- HyperFrames skills: github.com/heygen-com/hyperframes (skills/ dir)
- GSAP skills: github.com/greensock/gsap-skills (skills/ dir)

Both cached in ~/.cache/hyperframes/ and updated on each run.
Removed skills bundling from build:copy step.

Requires the hyperframes repo to be public for HyperFrames skills
to install. GSAP skills work immediately (public repo).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(cli): install skills automatically during init

After scaffolding a new project, `hyperframes init` now runs
`hyperframes skills` to install HyperFrames and GSAP skills.
Best-effort — if skill installation fails (no git, no network),
project creation still succeeds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(cli): show skills install feedback during init

Added installAllSkills() export for programmatic use by init.
Init now shows a spinner and result message:
  "11 AI skills installed (Claude Code, Gemini CLI, Codex CLI)"

Falls back gracefully if git or network unavailable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(cli): let users select which AI tools to install skills for

Interactive init now shows a multi-select prompt:
  "Install AI coding skills for: Claude Code, Gemini CLI, Codex CLI, Cursor"
Users can deselect tools they don't use or add Cursor (off by default).
Non-interactive mode still installs to all default targets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cli): prevent git credential prompt from hanging skills install

Set GIT_TERMINAL_PROMPT=0 so git clone/pull fails immediately on
private repos instead of hanging for username/password input.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cli): show skipped skill sources and accurate counts

Skills install now reports which sources failed (e.g., private repo)
and only counts skills that actually installed. Prompt text simplified
to "Install skills for:".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cli): clean up skills command output

- Skipped sources shown as dim text, not error with full command
- Summary shows "Skipped: HyperFrames (repo not accessible)"
- Outro says "ready" not "installed"
- Shows "No skills installed" if everything failed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cli): clarify partial skill install in outro message

When some sources fail, the outro now says which skills are ready
and which are unavailable, instead of a misleading total count.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove planning docs and test scaffolding

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove eval projects and test examples

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove remaining test project data

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cli): use generic source names in skills install outro message

Replace hardcoded "GSAP skills ready. HyperFrames skills unavailable."
with a dynamic message listing which sources succeeded and which were
skipped, so the message stays correct as sources change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cli): address review feedback on skills install

- Lazy process.cwd() for Cursor target (getter, not module-load)
- Track overwritten skills (logged in install output)
- Add --skip-skills flag to init for agent-friendly non-interactive use
- Guard both interactive and non-interactive paths with skipSkills

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cli): remove assets/ and fonts/ directories from init

Video files go to project root, not assets/. Removes assetsDir,
fonts/ directory creation, and assets/ prefix from video path.
Flat project root convention consistent with compose-video skill.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cli): address review items 5, 6, 13 — execFileSync, resilient cache, clear counting

- Replace all execSync with execFileSync for git commands (prevent injection)
- On git pull failure, reuse stale cache if skills dir exists instead of nuking
- Extract gitClone() helper for consistent clone calls
- Use explicit counted flag instead of confusing target === targets[0]

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:01:27 -07:00
JamesandClaude Opus 4.6 6c88c56cd2 fix(cli): fix CI format and typecheck failures
- 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>
2026-03-25 23:13:12 +00:00
JamesandClaude Opus 4.6 6e530bc2dd refactor(cli): address review findings in telemetry code
- 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>
2026-03-25 23:02:33 +00:00
JamesandClaude Opus 4.6 b7c75b814c feat(cli): add opt-out anonymous telemetry via PostHog
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>
2026-03-25 22:55:56 +00:00
Vance IngallsandClaude Opus 4.6 9f8e5ba5a1 initial code (#2)
* feat: initial code port from hyperframes-internal

Port all OSS-ready packages from the internal monorepo:
- @hyperframes/core — shared types, HTML generation, GSAP utilities, runtime
- @hyperframes/cli — CLI for creating, previewing, and rendering compositions
- @hyperframes/engine — framework-agnostic rendering engine (BeginFrame + FFmpeg)
- @hyperframes/producer — video rendering pipeline (Puppeteer + FFmpeg)
- @hyperframes/ui-player — browser-based video player component
- @hyperframes/studio — composition editor (React frontend + Hono backend)

Includes regression test suite with Docker-based test harness.

All HeyGen-internal references, deployment infrastructure, and
proprietary assets have been removed. Package names migrated
from @app/* to @hyperframes/*.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: scrub internal codenames and stale references from OSS port

- Replace static.heygen.ai runtime URLs in test fixtures
- Remove internal CDN publish script (publish-hyperframe-runtime.ts)
- Replace sandbox-studio, sandbox-interceptor, __magicEditRuntime
  with neutral names (studio, hyperframe-runtime, __hyperframeRuntime)
- Fix stale Vault API / localhost references in docs
- Remove broken deprecated_studio link

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove remaining internal codenames and stale references

- Delete stale producer README.md and PIPELINE.md (referenced nonexistent files)
- Replace "Cerberus" codename with "HyperFrames" in test design reviews
- Replace magic-edit postMessage identifiers with hf-preview/hf-parent
- Rename debug-magic-edit-timeline.ts to debug-timeline.ts
- Replace "Motion Cut" with "HyperFrames" in Timeline comments
- Fix studio/CLI references to nonexistent archive package
  (use local data/projects/ dir, stub render proxy)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 22:43:56 -07:00