#!/usr/bin/env node // captions.mjs — merged caption pipeline CLI. Dispatches by subcommand: // group → (was build-captions.mjs) deterministic caption grouping // usage: node captions.mjs group --group-spec ./group_spec.json // --hyperframes . --tokens design-system/chunks/tokens.css // --out ./caption_groups.json // html → (was build-captions-html.mjs) deterministic caption-HTML builder // usage: node captions.mjs html --hyperframes . --groups ./caption_groups.json // --tokens design-system/chunks/tokens.css // [--inference design-system/inference.json] // [--out compositions/captions.html] // [--skin caption-pill-karaoke] [--skin-file ] [--no-emit] // keepout → (was check-caption-keepout.mjs) static caption keep-out gate // usage: node captions.mjs keepout --group-spec ./group_spec.json // --hyperframes . [--json] // // Each original file's body is wrapped verbatim in its own async function so // its local const/function names stay function-local and never collide. The // only edit inside each body: CLI args read from the passed-in `argv` param // (the dispatcher passes process.argv.slice(3), i.e. args after the subcommand). import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs"; import { resolve, join, dirname } from "node:path"; import { execFileSync } from "node:child_process"; import { readDims, captionBand } from "./lib/dimensions.mjs"; // ===================================================================== // group — was build-captions.mjs // ===================================================================== // Phase 4a.5 (engine) — deterministic caption grouping. No subagent. // // Owns the word-data half of the captions contract (clean / group / global-time // / class + the non-overlap invariant). Its output, caption_groups.json, is the // single source of grouping/timing truth consumed by the deterministic HTML // builder build-captions-html.mjs (no LLM, no hand-authored spans). // Color/contrast decisions are NOT made here — color-mix()/var() can only be // resolved by a browser at render time, so the A-lite scene-background // adaptation lives in the caption template's render-time