24 Commits
Author SHA1 Message Date
Miguel Ángel 79dff20516 feat(scripts): typecheck the scripts directory (#3149)
* feat(scripts): typecheck the scripts directory

scripts/ was the one TypeScript surface nothing typechecked. Adding a project for it surfaced real errors rather than style: a preview generator passing string | undefined where a string was required, a readdir result indexed without a bound, and two non-null assertions standing in for a filter that could have narrowed the type instead.

The two preview generators had also drifted into sharing a capture setup, down to the comment explaining why the capture is opaque. That lifts into scripts/preview-capture.ts, so the reason is written once and both callers own the handles they have to close.

@hyperframes/core and @hyperframes/producer become dev dependencies because the scripts import them; without that the project resolves on a machine with a warm node_modules and fails in CI.

* fix(scripts): use source imports consistently
2026-08-09 22:07:57 -07:00
ukimsanov 618f73c266 Merge remote-tracking branch 'origin/main' into docs/pages-show-not-tell
# Conflicts:
#	skills-manifest.json
2026-08-04 16:16:58 -07:00
ukimsanov 2a7d9bd0ad docs: rebuild the catalog generator, and show what background removal and HDR do
**The catalog generator destroys hand-written documentation on every run.** It
does `rmSync(dir, { recursive: true })` on both catalog folders before
regenerating, so any section a human added to a generated page is deleted the
next time anyone runs it. There is now a carry-forward pass that reads those
sections off the existing pages first and re-emits any heading the template does
not own. Note this prevents future loss only — the earlier regeneration in this
stack already removed what was there, and it is recoverable from git if wanted.

It was also clobbering `docs.json`: rebuilding the Catalog tab dropped its icon
and unlinked `catalog/index.mdx` from the sidebar entirely.

And 27 of 36 component pages told the reader to "see the comment header in the
file" when no such header exists. The generator reads the file now and only says
it when true.

On presentation: 1,365 table rows across 168 pages became 0. `## Details` was a
table whose rows were "Type: Block" and the duration; `## Files` was three
columns where most items have exactly one file. Both are one sentence now. The
preview leads the page instead of sitting under title, description, tags, a
warning and a credit — four of which Mintlify already renders from frontmatter.

**Two guides that showed nothing now show the thing.** Background removal plays a
real cutout with the matte magnified at the hair edge and at a shoulder a plant
overlapped, because those are where matting fails. HDR is the honest one: you
cannot show HDR brightness on an SDR page, so it shows the `ffprobe` verdict and
a measured round-trip — 4000 nits authored, 4012 back out — and says on screen
for all 26 seconds that it is not simulating anything.

Both films were checked before publishing: no black or frozen stretches, and read
at the real 590px docs column width rather than full size. Nav after regeneration:
307 pages, 0 dangling, 0 redirect collisions.
2026-08-04 03:08:04 -07:00
Vance IngallsandClaude Opus 5 ab00b040f6 fix(release): print the release-PR flow for stable versions
set-version printed 'git push origin main; git push origin v<version>' as the
way to trigger publish. That is the PRERELEASE flow. publish.yml's push trigger
is 'v*-*', so a stable tag push fires nothing, and stable publishes only from a
merged release/v* PR.

Following the old text put a release commit on main with an unpublishable tag:
publish never ran, and the stray tag then fails the next release's
verify_remote_tag check. Now prints the branch + PR commands and says not to
push the local tag, matching docs/contributing/release-channels.mdx.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 01:48:53 -07:00
Miguel Ángel 7a4853dfe6 refactor: extract @hyperframes/studio-server from core (#1757)
* refactor: extract @hyperframes/studio-server package from core

Moves all studio-api routes, helpers, and Hono server wiring from
packages/core/src/studio-api/ into a new standalone packages/studio-server
package (@hyperframes/studio-server).

Core keeps thin re-export stubs at @hyperframes/core/studio-api and the
subpath helpers (screenshot-clip, draft-markers, etc.) for backward
compatibility. Consumer imports (cli studioServer, vite adapter/config,
producer htmlCompiler, studio manualEditsTypes) are updated to import from
@hyperframes/studio-server directly.

Also exports rewriteInlineStyleAssetUrls from @hyperframes/core root (was
in compiler/rewriteSubCompPaths.ts but not re-exported), required by
@hyperframes/studio-server/helpers/subComposition.

Removes postcss-selector-parser from @hyperframes/core dependencies (moved
to @hyperframes/studio-server which owns the routes that used it).

Depends on @hyperframes/parsers (PR #1755).

* fix(ci): add parsers+studio-server to Dockerfile and build before preview tests

* fix(ci): build @hyperframes/studio-server before Test and studio load smoke

Studio's vite.config.ts imports @hyperframes/studio-server, which resolves
via its "node" export condition to built dist. The Test and studio-load-smoke
jobs only built parsers + core, so esbuild's config load failed to resolve the
package entry. Build studio-server too.

* fix(studio): repoint sdkCutoverParity test import to studio-server

sourceMutation moved from core's studio-api to @hyperframes/studio-server;
the test still imported the deleted core path. This was masked while studio's
vite.config failed to load (couldn't resolve studio-server); now that the
config loads, the test runs and the stale import surfaced.
2026-06-27 01:24:01 -04:00
Miguel Ángel 98d0bdd73c refactor: extract @hyperframes/lint from core (#1756)
* refactor: extract @hyperframes/lint package from core

Moves all lint rules, hyperframeLinter, lintProject, and related types
from packages/core/src/lint/ into a new standalone packages/lint package.

Core keeps a thin re-export stub at @hyperframes/core/lint for backward
compatibility. Consumer imports (cli lint command, producer hyperframeLint)
are updated to import from @hyperframes/lint directly.

Depends on @hyperframes/parsers (PR #1755).

* fix: restore postcss-selector-parser in core (sourceMutation.ts still uses it)

* fix(ci): add parsers+lint to Dockerfile and build before preview tests

* chore: update bun.lock after restoring postcss-selector-parser dep

* test(cli): update lintProject test for string-dir signature from @hyperframes/lint

* refactor(core): single-source the lint engine in @hyperframes/lint

Delete core's byte-identical copy of the lint rule engine and re-point
staticGuard at @hyperframes/lint, so the render-time render-gate and the
studio preview share one rule engine instead of two copies that could
silently diverge. Back-compat preserved via the @hyperframes/core/lint stub.

Addresses review feedback on the dual-copy footgun.
2026-06-27 01:16:40 -04:00
Miguel Ángel cdf9c817e1 refactor: extract @hyperframes/parsers from core (#1755)
## Summary

Extracts the GSAP parser/writer suite, HTML parser, hf-ids, spring-ease, and the shared composition data types out of `@hyperframes/core/src/parsers/` into a new, independently-publishable **`@hyperframes/parsers`** package.

This is the foundation of the [#1749](https://github.com/heygen-com/hyperframes/issues/1749) effort: make HyperFrames' parsing/linting/validation usable as plain libraries in a Node app, without shelling out to the CLI. Parsers is the standalone base every other extracted package builds on.

**Part 1 of 3** — splits #1754 into independently-reviewable pieces. Parts 2 (lint) and 3 (studio-server) stack on this branch.

## What moves

| | |
|---|---|
| Source moved out of core | **~9,900 LOC** (`src/parsers/` → `packages/parsers/src/`) |
| Total lines removed from core (incl. tests + goldens) | ~19,600 |
| Files relocated | 39 |
| Tests carried over | **660 passing** (5 skipped, 3 todo) |

The big movers: `gsapParser` / `gsapParserAcorn` (the recast + acorn dual parsers), `gsapWriterAcorn`, `gsapSerialize`, `gsapUnroll`, `htmlParser`, `hfIds`, `springEase`, `stableIds`, plus the `__goldens__` corpus.

## Bundle footprint of the new package

| Artifact | Size |
|---|---|
| `dist/` (unpacked) | 1.7 MB |
| npm tarball (packed) | 409 KB |
| `dist/index.js` | 90 KB (**~21 KB gzipped**) |
| Heaviest entries | `gsapWriterAcorn.js` 93 KB · `gsapParser.js` 91 KB |

Most of the weight is the GSAP AST machinery (recast/babel/acorn). It's tree-shakeable via subpath entries (`@hyperframes/parsers/hf-ids`, `/gsap-constants`, etc.) so a consumer that only needs `hf-ids` (2 KB) doesn't pull the parsers.

## How `@hyperframes/core` changes

The interesting part: **core sheds its entire AST toolchain.**

| core `dependencies` | before | after |
|---|---|---|
| count | 9 | 6 |
| removed | — | `@babel/parser`, `acorn`, `acorn-walk`, `magic-string`, `recast` |
| added | — | `@hyperframes/parsers`, `linkedom` |

Before this PR, importing `@hyperframes/core` at all dragged in babel + recast + acorn just to construct types. Now those live behind `@hyperframes/parsers`, and a consumer that only wants core's runtime/compiler types never resolves the parser stack. Core keeps thin `@deprecated` re-export stubs at the old subpaths (`@hyperframes/core/gsap-parser`, `/gsap-constants`, …) so nothing downstream breaks.

## Design notes

- **`"bun"` export condition before `"node"`** in every package export. Bun resolves the TypeScript source directly (no pre-built `dist/`), while Node/tsx/Docker contexts fall through to `"node"` → `dist/`. This keeps the dev loop zero-build while published artifacts stay Node-consumable.
- `@hyperframes/parsers` is **standalone** — zero `@hyperframes/*` dependencies — so it can be the base of the stack.

## Test plan

- [x] `bun run --filter @hyperframes/parsers test` — 660 tests pass
- [x] `bun run --filter @hyperframes/sdk test` — 382 tests pass
- [x] `bun run build` — full monorepo build succeeds
- [x] Fallow audit passes on CI
2026-06-27 00:46:26 -04:00
Vance IngallsandClaude Opus 4.8 3a28d3f6b8 fix(release): scope tag-monotonicity guard to tags reachable from HEAD
The guard blocked on any semver-higher v* tag, including orphan tags on dead
branches (e.g. a stray `chore: release v1.0.3` never merged or published).
Such tags can't appear in the release history and shouldn't block a legitimate
release. Now only tags that are BOTH higher AND reachable from HEAD block;
extracted `findBlockingTags` with unit coverage for orphan/reachable/lower cases.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 00:34:58 -07:00
Vance IngallsandClaude Opus 4.8 9cc3550f7e fix(release): stop advising 'git push --tags' in release next-steps (#1521)
set-version's stable-release next-steps printed `git push origin main --tags`,
which pushes every local tag and fails the whole push on any pre-existing tag
(it broke the v0.6.107 release). #1517 fixed CONTRIBUTING.md + annotated the tag
but missed this console message. Now prints `git push origin main` +
`git push origin v<version>`, matching the pre-release branch.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 21:26:49 -07:00
Vance IngallsandClaude Opus 4.8 ca3cab340b fix(release): annotate version tag + correct release push instructions (#1517)
- set-version: create the release tag with `git tag -a -m` instead of a
  lightweight `git tag`, which fails ("no tag message?") when a contributor has
  tag.forceSignAnnotated / required-annotation set globally — it silently broke
  the v0.6.107 tag step.
- CONTRIBUTING: replace `git push origin main --tags` (pushes every local tag →
  whole push rejected on any pre-existing collision) with pushing the specific
  tag, and document the monotonicity guard (stale higher tag blocks tagging).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 13:30:15 -07:00
Kiyeon Jeon ad5229af83 fix(release): include sdk in fixed-version bump (#1363) 2026-06-12 14:04:42 -04:00
Miguel Ángel 24279c8c76 fix(release): guard against non-monotonic tags (#1283)
* fix(release): guard against non-monotonic tags in set-version

Add a pre-tag check that rejects stable releases when an existing tag
has a higher semver. This prevents tag-sorting installers (npx skills,
simple-git tags.latest) from resolving stale versions.

Closes #1282

* fix(review): add --skip-monotonicity-check escape hatch, fix formatting

Address review feedback:
- Add --skip-monotonicity-check flag for legitimate backport scenarios
- Soften error message to show both options (delete tag or skip check)
- Fix oxfmt formatting
- Add test for new flag parsing
2026-06-08 19:49:59 -04:00
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
James Russo 8228932e17 fix(scripts): make release change-guard robust to git status prefix (#1198)
The set-version guard parsed `git status --porcelain` and extracted the
path with a fixed `line.slice(3)`. The porcelain "XY <path>" prefix width
can shift, and when it did the slice dropped a leading character —
misreading `.claude-plugin/plugin.json` as `claude-plugin/plugin.json`,
which failed the allowed-paths match and falsely blocked a legitimate
release with "Unexpected uncommitted changes". There was no escape hatch.

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

Also document the release flow in CLAUDE.md (the repo had no release docs).
2026-06-04 00:58:09 -07:00
James Russo 17b0db1d3e chore: add release prepare command (#1165)
## What

- Add `bun run release:prepare <version>` as the maintainer-facing stable release entrypoint.
- Make the first run draft missing changelog artifacts and intentionally exit before tagging; rerunning after manual review delegates to `set-version`.
- Tighten the direct `set-version` guard so stable releases also fail when generated TODO changelog copy is still present.
- Update maintainer docs to recommend `release:prepare` while keeping `changelog:draft` as the lower-level regeneration tool.

## Why

Stable releases should be hard to run without reviewed GitHub release notes and Mintlify changelog copy. This keeps the existing manual rewrite step, but makes the expected path one command that engineers can rerun after review.

## How

- Added `scripts/release-prepare.ts` with parsing, draft/review/set-version action selection, and command forwarding.
- Added focused script tests for parser behavior, action selection, command forwarding, and TODO detection.
- Extracted shared script CLI parsing helpers so `changelog:draft` and `release:prepare` use the same option handling.
- Adjusted `changelog:draft --write` so an existing release file is left unchanged unless `--force` is passed, while still allowing a missing docs entry to be added.

## Test plan

- [x] Unit tests added/updated: `bun run test:scripts`
- [x] Format check: `bun run format:check`
- [x] Lint: `bun run lint`
- [x] Typecheck: `bun run --filter '*' typecheck`
- [x] Fallow audit: `bunx fallow audit --base origin/main --fail-on-issues`
- [x] Manual CLI checks: `bun run release:prepare --help`; `bun run set-version 9.9.9` fails before mutation when changelog artifacts are missing
- [x] Documentation updated
2026-06-02 20:34:29 -04:00
James RussoandClaude Opus 4.8 248f640734 feat(docs): add changelog release workflow (#1164)
* feat(docs): add changelog release workflow

* fix(scripts): resolve CodeQL findings in release scripts

- draft-changelog.ts: replace existsSync+writeFileSync check-then-act with
  an atomic exclusive-write flag (flag: wx) to fix the js/file-system-race
  TOCTOU finding; overwrite only under --force (flag: w).
- set-version.ts: switch execSync shell-string git calls to execFileSync with
  argument arrays so the interpolated version/paths can never be interpreted
  by a shell, resolving the js/indirect-command-line-injection findings.

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

* fix(scripts): lower writeReleaseNotes complexity below CRAP threshold

The exclusive-write fix pushed writeReleaseNotes to cyclomatic 5 / CRAP 30.0
(fallow/high-crap-score, threshold 30.0). The '!force' guard in the catch is
redundant — EEXIST is only reachable under the 'wx' flag (force=false), since
'w' overwrites without throwing. Dropping it returns the function to cyclomatic
4 / CRAP 20 with identical behavior.

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

* fix(docs): address changelog review feedback

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 15:51:16 -07:00
Akwasi Konadu Akuoko 1ee920b535 fix(release): bump plugin manifests in set-version
scripts/set-version.ts only bumped the npm package.json files, so the
Claude Code / Codex / Cursor plugin manifests stayed at 0.1.0 across every
release. Claude Code gates /plugin update on plugin.json's version, so users
never received accumulated changes (closes #1048).

Add a PLUGINS list and a parallel loop that rewrites each manifest's version,
and include the manifests in the clean-tree guard and the staged file set.
The loop replaces only the version string (not a JSON round-trip) to preserve
each file's exact formatting, since oxfmt keeps their short arrays inline and
JSON.stringify would expand them and fail the pre-commit format check.
2026-05-23 19:02:21 +00:00
James 6935eaabc4 chore(lambda): bump initial version to 0.6.20 + add to set-version
Two follow-ups to keep the new package in lockstep with the rest of the
@hyperframes/* release cadence from day one:

- Bump packages/aws-lambda/package.json version 0.6.18 → 0.6.20 so it
  matches what main released while this PR was in review. Without this,
  the package would land below the rest of the lockstep and the next
  release-bump would jump aws-lambda from 0.6.18 → 0.6.21 in one step.

- Add packages/aws-lambda to PACKAGES in scripts/set-version.ts so the
  next `chore: release vX.Y.Z` commit bumps aws-lambda alongside the
  other publishable packages. Without this, set-version silently skips
  aws-lambda — package.json stays frozen, pnpm publish would re-publish
  the same version on every release, and the npm-view precheck in
  publish.yml would skip-with-success and never actually push a new
  version of the package.
2026-05-17 23:06:19 +00:00
James RussoandClaude Opus 4.6 9bf4956fae chore(shader-transitions): add to CI publish pipeline and README (#264)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 22:07:19 -07:00
James Russo e6a38c9e0f feat(ci): add alpha/beta/rc pre-release support to publish workflow (#167) 2026-03-31 14:16:57 -07:00
James RussoandClaude Opus 4.6 24c4fa6041 fix: make set-version commit and tag by default (#143)
The --tag flag was optional, which led to v0.1.11 and v0.1.12 being
bumped without tags — skipping npm publish entirely. Invert the default:
always commit + tag, with --no-tag as the escape hatch.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 15:21:08 -07:00
Vance IngallsandClaude Opus 4.6 61c5257402 fix(ci): update publish workflow to use bun install (#36)
* fix(ci): update publish workflow to use bun install

pnpm-lock.yaml was removed in the bun migration but publish.yml
still referenced it. Use bun for install/build, keep pnpm for
publish (publishConfig overrides + --provenance).

* docs: update stale pnpm references to bun across docs and scripts

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-24 08:46:58 -07:00
Vance Ingalls 20be2ea1c2 style: apply oxfmt baseline formatting across all source files (#25)
## Summary
- Run `oxfmt .` across the entire codebase to establish formatted baseline
- 299 files changed — mechanical formatting only, no logic changes
- Double quotes, semicolons, 2-space indent, trailing commas, 100 print width

Part 3/4 of [VA-851](https://linear.app/heygen/issue/VA-851/pre-migration-configure-eslint-prettier-and-conventional-commits)

## Test plan
- [x] `pnpm format:check` — all 426 files pass
- [x] `pnpm -r typecheck` — all packages pass
- [x] `pnpm build` — all packages build
- [x] All 348 tests pass
2026-03-23 17:15:14 -07:00
James 306909534b ci: add tag-triggered npm publish workflow with OIDC provenance 2026-03-23 03:20:21 +00:00