mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-04 07:19:52 +00:00
4e9047018d3cbd9107fc841cf2ea060f7eb0b76d
15
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b9be0b2625 |
feat(skills,studio,media-use): the intent layer, review loop, and user memory — BRIEF.md, companion mode, recipes; /website-to-video folds into /product-launch-video (#2133)
* feat(studio,cli): per-frame board comments, self-refreshing storyboard, status-aware preview landing Per-frame comment boxes on the storyboard board batch into .hyperframes/frame-comments.json (a resubmit wins per frame; unconsumed comments on other frames are kept). Submitted-but-unconsumed comments stay visible — a toolbar banner plus a per-tile echo — until the agent consumes the file; the banner also says what to do next (reply anything in the agent chat). The board keeps itself current: GET /projects/:id/signature exposes the watcher-cached project signature, the storyboard payload carries the signature it was derived from, and the view polls at 2s (hidden tabs skipped, re-checked on visibility), refetching in place with no loading flash. Posters bake the signature into their URL so tiles fill in as sketches land and a poster that failed mid-write retries on the next version; the empty state upgrades itself when STORYBOARD.md appears, and its handoff prompt now points the agent at the review loop and uses the parser's real status vocabulary (outline, not planned). preview lands the browser on the storyboard view while the board is the review surface — any frame built, or pure planning (srcs declared, none on disk yet) — and on the timeline once the video is assembled. * feat(skills): the review loop — plan, sketch, build as one shared process hyperframes-core/references/review-loop.md is the single source for the three-pass collaborative review: the plan proposed on a live board (§ 1), wireframe sketches marked built with one layout question (§ 2 — real words on plain blocks, run no CLI; a confirmed board is itself a valid deliverable when the user asked for a storyboard, not a video), the build dressing confirmed layouts (§ 3, worker or inline), and the final look (§ 4). Autonomous runs skip every gate and keep one question before render. The three narrative workflows' Steps 3/4/6 collapse to references plus their sketch stand-ins (captured-asset blocks for product-launch-video, plain code panels for pr-to-video); the confirmed-sketch handoff stays in each frame-worker prompt. general-video plans on a board for multi-scene narrative pieces in collaborative mode — its sketch pass is layout-before-animation with the user watching. The router treats "I want a storyboard" as a process request rather than a route, and closes exploratory intake by recommending a route plus how the run will review. The supporting contracts land next door: the comments channel (silent submit, one reply picks it up, check the file before the words) in brief-contract § 1; the sidecar schema and the built status rung in storyboard-format; the mode question asked first and alone in the three workflows' Step 0. * feat(media-use): user memory — remembered preferences and frozen recipes Two tiers of memory on media-use's existing two-tier storage split. Preferences (lightweight): confirmed brief answers — destination, aspect, language, mode, voice, style preset — recorded to the project's .media/preferences.json (committed, the team inherits it) and promoted to the personal ~/.media/preferences.json once the same value is confirmed in two different projects (a sightings ledger accumulates the cross-project evidence user-side, since project files can't see each other). prefs.mjs get/record; merge reads project-over-user; a changed value restarts its provenance. Recipes (heavyweight): one approved run frozen as a named, versioned bundle — frame.md, the storyboard skeleton (structure kept: durations, transitions, srcs, Video direction; statuses reset to outline; content blanked to per-frame fill-ins naming the beat's role), and the confirmed brief values. Named folders, not content hashes: re-freezing bumps version and archives <name>@v<N>; a freeze is already confirmed, so it promotes to the user tier immediately. recipe.mjs freeze/list/use, plus resolve --type recipe --entity <name> delegating like grade/lut. 16 new node --test cases; the media-use lib suite is 168/168. * feat(skills): wire user memory into the brief and the review loop brief-contract § 2 gains Remembered defaults: read the merged preferences before Round 2 and let a remembered value become the recommended option with a receipt naming its source project. Memory changes the default, never the question — every ask-marked field still gets asked, and what the request says this time beats what was picked last time. Record only what the user actually confirmed (a defaulted voice nobody chose is not an answer; a "go" that accepts the recommended defaults is). The first record announces itself once; after that the receipts carry the reminder. In autonomous mode a remembered value becomes the decided value, receipt included. The three narrative workflows read the remembered defaults before Round 2, record the confirmed answers at the Step 0 gate, record the chosen preset at the Step 2 gate (pr-to-video excepted — its preset is fixed), and fall back to the remembered voice when the request names none. general-video's discovery reads the same defaults. Recipes wire in at both ends: Step 0 checks for a matching recipe before the mode question — one question, plural-aware, and adopting one fills the brief, skips the design step, and drafts the storyboard from the frozen skeleton while every review gate still runs. The review loop's final look (§ 4) offers the freeze once after approval, and the confirmation teaches the recall phrase — the name is something the system reminds the user of, never something they must remember. The router recognizes a named recipe or "like last time" as a route. * docs(skills): the sketch pass names check, not the deprecated validate * feat(skills): intent-layer references — process, route briefs, capability menu, BRIEF.md format * feat(media-use): brief skeleton as the recipe's fourth artifact; flow/storyboard preference keys * feat(skills): the intent layer conducts every brief — workflows execute BRIEF.md * feat(skills): retire the mode preference key; sync catalog surfaces for intent layer * refactor(skills): dedupe router vs intent-layer guidance — one owner per rule * feat(skills): the design ask — own spec, pick by eye from showcases, or defer * docs(skills): the design ask says the honest line on capture routes * feat(skills): product-launch-video absorbs website-to-video as the tour angle * refactor(skills): keep product-launch-video pristine — a tour is brief intent, not a pipeline branch * feat(skills): production loop + genre lenses; general-video goes freeform (route yours, laws hold) * refactor(skills): /hyperframes is the front door - route tables and scope lists leave the workflows * docs(skills): review-loop pass across skill catalog * fix(cli): pass project dir to openStudioBrowser in background-server path * feat(skills): add pitch-round reference - verbalized sampling concept gate * feat(skills): wire pitch round into intent layer - completeness triage + route eligibility * feat(skills): editorial capability recommendations, handoff disciplines, menu-probe split * feat(skills): pitches carry their machinery; source-only-formed requests pitch the telling * feat(skills): companion goes director - ceiling treatment plus blueprint/rule citation discipline * fix(scripts): sandbox npx-leak guard - private npm global prefix keeps npx on the branch CLI * chore(skills): resync manifest hash after formatter pass reflowed general-video tables * fix(skills): recipe freeze reads workflow from BRIEF.md; style_preset records require workflow scope Two holes found by a live companion-run freeze: the agent-supplied --workflow contradicted the run's actual workflow (recipe.json said faceless-explainer, brief-skeleton said general-video), and the style_preset lookup missed because the preference had been recorded under the bare key. - freezeRecipe resolves the workflow from BRIEF.md frontmatter; the flag is a fallback for briefless projects and a contradicting flag is ignored (noted). - recordPreference refuses a bare style_preset — the scoped key is the only writable shape; freeze tolerates legacy bare records via read fallback. - review-loop § 4 / media-use SKILL / brief-format wording follow the machinery. |
||
|
|
bf8432bca8 | fix(media-use): format resolver diagnostics (#2466) | ||
|
|
da19f9a692 | fix(media-use): explain missing bundled SFX (#2460) | ||
|
|
7c0dcb0b14 |
fix(media-use): ingest derived video outputs (#2310)
* fix(media-use): ingest derived video outputs * style(media-use): keep ingest types after imports |
||
|
|
2e34a2d5a0 |
fix(media-use): fall back to bundled SFX (#2257)
* fix(media-use): fall back to bundled SFX * chore(skills): refresh media-use manifest * docs(media-use): design CLI fallback advisory * docs(media-use): plan CLI fallback advisory * fix(media-use): surface HeyGen CLI fallback guidance * fix(media-use): derive bundled SFX extension |
||
|
|
521f2c9ba7 | feat(media-use): usage telemetry for HeyGen conversion (#2130) | ||
|
|
16eb11367a |
feat(media-use): usage visibility — shared telemetry identity, miss log, resolve --stats (#2113)
* feat(media-use): usage visibility — shared telemetry identity, miss log, resolve --stats - U6: join the CLI/studio telemetry identity — read the shared install id from ~/.hyperframes/config.json (seed if absent) instead of a media-use-only ~/.media/anon-id, and $identify to the HeyGen account (email/username) once per run on sign-in. One PostHog person across surfaces; pseudonymous before sign-in, account-linked after. Event properties stay coarse (no intent/paths). - U1: one-time first-run disclosure to stderr + Privacy section in SKILL.md; honors DO_NOT_TRACK / HYPERFRAMES_NO_TELEMETRY. - U2: persist resolve misses to ~/.media/misses.jsonl (local → intent kept; the media_use_resolve_miss telemetry event stays intent-free). - U3: `resolve --stats` (+ --days) — local usage report over .media/ + ~/.media (volume by type, source/provider/via split, hit-rate, top missed intents, global-cache size/reuse); human + --json. - U4: reproducible PostHog dashboard definition (references/telemetry-dashboard.md). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * fix(media-use): address #2113 review — shared notice state, legacy id migration, stats robustness - Notice-shown state now lives in the shared ~/.hyperframes/config.json (config.telemetryNoticeShown, the CLI's own field) instead of a media-use-only ~/.media marker — so shared-identity users see the first-run notice once per person, not once per tool. - Migrate a pre-existing ~/.media/anon-id into the shared config on upgrade, so media-use-only users keep their PostHog persona instead of resetting. - buildStats: --days only windows on a positive finite value (negative/NaN → all time, not an empty report); dropped the top-level catch that masked a real error as an all-zero "no usage" report (sub-reads are individually guarded). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
cdb8d736f1 |
feat(media-use): fast heygen CLI onboarding — actionable diagnostics, --doctor, free-usage framing (#2065)
* feat(media-use): fast heygen CLI onboarding — actionable diagnostics, --doctor, free-usage framing media-use resolves bgm/sfx/image/icon (catalog), voice (TTS), and avatar video through the heygen CLI — the free-usage path. Agents hit a dead end when it's missing/unauthed. This guides them to install it fast, at the point of need. - Centralized actionable diagnostics (lib/heygen-cli.mjs): every heygen-backed resolve, on failure, prints the exact fix on stderr — not-installed (curl install one-liner), not-authenticated (heygen auth login), outdated (heygen update). Routed through heygen-search + voice-provider. stdout stays clean JSON. - resolve --doctor preflight (human + --json): checks heygen present/version/ auth, ffmpeg, ffprobe, node, a fix per gap. Exit 0 unless ffmpeg missing. - SKILL reframe: install-first callout; heygen as the free-usage gateway for bgm/image/voice/avatar-video; removed the false "degrades gracefully" claim. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * fix(media-use): address #2065 review — classifier blocker, doctor contract, telemetry - Blocker: classifyHeygenError no longer treats a bare "not found" as CLI-missing (a stale voiceId → "voice not found" was sending users to reinstall a working CLI); keep only ENOENT + "command not found". Regression test added. - 401 now matches \b401\b, not any "401" substring (request IDs no longer misread). - --doctor: top-level ok requires ffmpeg AND ffprobe (matches SKILL.md); emits media_use_doctor_run telemetry; auth status queried with --json + JSON-only parse; auth timeout softened (network issue, not a false "unauthenticated"); node version gated on >= 18; version-without-semver labeled, not silently green. - Nits: install cmd uses && ; dropped the runResolveStatus alias. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * fix(media-use): require OAuth-capable heygen CLI (v0.3.0), fix auth-status probe E2E against the live free-usage backend surfaced three issues: - HEYGEN_MIN_VERSION was 0.1.6, but that CLI can't use OAuth ("heygen-cli can't use OAuth yet") — free usage needs >= v0.3.0. Bumped the floor; --doctor now also nudges `heygen update` when a newer stable exists (always-latest). - Onboarding pointed at `heygen auth login --key` (API credits / billing); the free path is `--oauth` (subscription/free credits). Fixed install + auth guidance and SKILL.md accordingly. - `heygen auth status --json` is an unknown flag on v0.3.0 (JSON is the default output) — the added --json broke auth detection. Dropped it; verified --doctor reports authenticated on a real free (OAuth) account. Tests assert against the exported message constants instead of brittle literals. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * fix(media-use): address #2065 review nits — one root cause on old CLI, floor policy - --doctor skips the auth check when the version check fails (below v0.3.0): an old CLI's auth probe fails for the same root cause, so users no longer see two errors ("outdated" + "not authenticated") — one root cause, one fix. - Comment links the auth-status probe's JSON-default assumption to HEYGEN_MIN_VERSION >= 0.3.0 so the floor isn't silently lowered later. - SKILL.md states the uniform v0.3.0 requirement (nudged even for API-key use). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * fix(media-use): doctor prints one heygen row per fact The 'heygen on PATH' and 'heygen version' checks both rendered their detail as `heygen v0.3.0`, so --doctor printed two byte-identical green lines. Make the PATH row report presence ("heygen found on PATH") and let the version row own the version string — one row per fact, no duplicate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
57b3c78987 |
feat(media-use): color grading — grade/lut resolve, smart-grade, grade-compare + compare (#2041)
* feat(media-use): color grading — grade/lut resolve, smart-grade, grade-compare CLI Add color grading to media-use as first-class resolve types plus a faithful comparison command. All local, offline, deterministic — no model, no GPU. - resolve -t grade / -t lut: produce a data-color-grading block (or a frozen .cube). Look cascade: core preset (no file) -> bundled .cube library -> parametric buildCube. Emitted .cube is Rec.709 and validated against core's colorLuts constraints (LUT_3D_SIZE <= 64) before it is frozen. - smart grade (grade --for <media>): ffmpeg signalstats -> adjust suggestion (exposure / contrast / white balance), surfaced with the measured evidence on stderr as a starting point; never auto-applied. - hyperframes grade-compare: renders N candidate grades onto a reference frame through the real runtime shader into one labeled comparison PNG, so an agent picks a look without opening Studio. Prepends an "original" baseline cell by default (--no-baseline to omit). Shares the headless-capture pipeline with snapshot via capture/captureCompositionFrame. - media-use SKILL: proactive "media opportunity pass" guidance (grounded signal -> offer, ask once, surface don't mutate). Verified: media-use 116/116, grade-compare 7/7, snapshot 9/9, lint + format clean, full build green, comparison renders end to end. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * test(cli): narrow grade-compare baseline assertion off unknown-typed grading Assert the whole cell via toEqual instead of reaching into .grading.preset / .grading.lut on the unknown-typed field, keeping the test typecheck-clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * feat(media-use): agent-authored LUTs via --params + validate --from cube; never-read-.cube guardrail - resolve -t lut / -t grade --params '<json>': build a parametric .cube from explicit params (bypassing the intent cascade), validate, and freeze in one step. --intent becomes the optional description. Lets an agent commit a look it computed itself. - --from <file.cube> now validates the ingested LUT for lut/grade types and rejects an invalid/oversized cube (no partial write) — the escape hatch for a LUT the agent generated with its own code. - SKILL.md: hard rule to never read a .cube body into context (~size^3 lines, zero legible signal) — inspect via grade-compare (see it) or cube-validate (ok/size), read the manifest description for meaning; plus both authoring paths and the parametric-vs-film-stock ceiling note. Verified: media-use 116/116, lint + format clean; smokes — --params builds a valid frozen cube, grade --params returns a lut block, bad JSON and an oversized --from cube are both rejected with no stray file. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * fix(cli): grade-compare validates referenced LUTs, warns on no-op cells, caps candidates Bug-bash follow-ups — grade-compare silently accepted bad input: - Validate LUT *content*, not just existence: each referenced .cube is parsed with core's parseCubeLut (now exported from @hyperframes/core) and rejected with a per-cell error ("LUT for \"<label>\" is not a valid .cube: ..."). A file that exists but isn't a valid cube no longer renders a silent no-op cell. - Warn on inactive cells: a grading that normalizes to inactive (e.g. a malformed {lut:12345}) emits a stderr warning naming the cell; the auto-prepended "original" baseline is intentionally inactive and stays silent. stdout remains valid JSON. - Cap candidates at 16 (excluding baseline): over-cap input renders the first N and reports {truncated:true, total:M} on stdout + a stderr note — no silent drop, no unbounded giant sheet. Verified: grade-compare 10/10; non-cube LUT → clear error; {lut:12345} → warning + ok; 20 cells → cells=17 truncated total=20; valid runs unchanged. Lint/format clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * feat(cli): general `hyperframes compare` visual-variant primitive Generalize grade-compare's "render N variants → one labeled sheet → the agent looks and picks" loop into a standalone command that works on ANY variation (font, layout, motion, grade, whole compositions) — the tool never needs to know what differs. - `hyperframes compare <path...> [--at <sec>] [--labels a,b,c] [--out] [--cols] [--json]`: renders each agent-authored composition variant through the real runtime (captureCompositionFrame) and stitches one labeled comparison sheet + JSON ({ok, sheet, rendered, variants, truncated?/total?}). 2+ paths required; caps at 16 with loud truncation. It presents, it does not judge — choosing is the caller's job. - Factored the shared "render a labeled set → contact sheet" path so compare, grade-compare, and snapshot all sit on it (no duplication). grade-compare is now the first color-specific specialization of this primitive. - New pathArgs util + contactSheet test; hyperframes-cli SKILL documents compare as the agent's "see your own renders and choose" primitive. Verified: 26/26 across compare + grade-compare + snapshot + contactSheet (no regressions); compare renders 3 variants into one visibly-distinct labeled sheet; 2+-path error path clean; lint/format clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * fix(ci): green the skills CI — skip ffmpeg tests when absent, oxfmt markdown The "Test: skills" CI job runs bare `node --test` with no ffmpeg on PATH (by design — skills tests are meant to be node-builtin-only). The grade-analyzer + smart-grade tests shell to ffmpeg and were failing there with ENOENT. Guard them to skip when ffmpeg isn't on PATH; they still run locally / where it is. Also oxfmt README.md + hyperframes/media-use SKILL.md (the whole-repo `oxfmt --check .` Format job caught markdown left unformatted by the rebase conflict resolution). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * fix(ci): skip core-conformance test when tsx is unavailable The "Test: skills" CI job installs no deps, so the normalizeHfColorGrading conformance test (which imports core's TS via `node --import tsx`) failed there. Guard it to skip when tsx can't resolve; runs locally / in the deps-installed Test job. Completes the skills-CI greening (the ffmpeg guards handled the rest). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * fix(cli): escape grade-compare src double-quotes (CodeQL XSS) + Windows-safe compare test - grade-compare built `<img src="...">` (double-quoted) with the single-quote escaper, leaving `"` unescaped — a `"` in the frame path could break out (CodeQL: incomplete HTML attribute sanitization). Use escapeXml for src. - compare label test hard-coded POSIX paths that can't match on Windows; assert the derived labels (the subject); path resolution is covered elsewhere. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * refactor(media-use): generate LUT library from params (drop committed .cube files) The 3 bundled .cube files were 733 lines each (2,199 total) and were themselves buildCube output — pure repo bloat. Replace with compact per-look params in luts/index.json, generated on resolve; add an optional `url` for future scanned LUTs to be CDN-hosted + downloaded on demand (freezeUrl) instead of committed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * feat(media-use): serve library LUTs from CDN on-demand (static.heygen.ai/luts), params fallback Looks now carry a CDN `url` (hosted at s3://heygen-public/luts → static.heygen.ai/luts/<id>.cube); resolve downloads + validates + freezes on demand, like bgm/image. `params` stays as the deterministic offline fallback (--local-only, or if the download fails), so resolution is never blocked on the network. Provider prefers url, falls back to params. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * fix(media-use): address #2041 review — atomic LUT writes, compare telemetry, follow-ups - Atomic .cube writes: library provider (url + params) and the parametric generator now write to a .tmp path, validate, then rename, so a crash can never orphan an invalid .cube at the final path (was validate-after-write). - track("media_use_resolve") now emits provenance.via (url/params-fallback/params). - grade-compare + compare: --timeout flag (was hardcoded 5000) and a media_use_compare event (cells, truncated, total, render_ready_timed_out); openSettledCompositionPage now surfaces the render-ready timeout. - compare staging skips node_modules/.git; --for gets an upfront existence check. - Rec.709 luma comment; HYPERFRAMES_ANALYZE_TIMEOUT_MS override; measured note uses basename; LUT s3 hosting moved from index.json into luts/README.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4d3cdc3e4b |
feat(media-use): resolve official brand logos via a four-tier cascade (#2061)
* feat(media-use): resolve official brand logos via a four-tier cascade Third-party brand logos (the meeting's 'credibility signals lost' gap) had no acquisition path: capture only grabs the product's own site assets, and HeyGen asset search returns generic look-alike icons for brand queries (0/3 in testing — an X-in-a-circle for LinkedIn). Workers could only fake a mark or drop it. New resolve type 'logo', four tiers verified by a 54-brand stress test (100% cascade hit across dev tools / big tech / non-tech / CN brands): - svgl — official full-color vector SVGs + wordmark variants (40/54 first-hits); search is substring-based, so entities pass through alias normalization (nextjs → 'next.js', aws → 'amazon web services') - simple-icons (pinned CDN build) — official monochrome glyphs; catches the long tail (nike, visa, toyota, wechat, bytedance) - github org avatar — known-org map only; a brand name is not a GitHub login, guessing risks same-named personal accounts - domain favicon (DuckDuckGo ip3) — small-raster last resort; sub-500B responses are DDG's placeholder and rejected; frozen with a low_res provenance flag (chip-size use only) logo joins the icon/image equivalence group (typesMatch) and the images/ subdir, so entity cache hits interop with figma-imported marks. A total miss falls through resolve's normal failure path — no special casing. HeyGen search stays the icon provider; it is deliberately absent from the logo cascade. Docs: media-use gap/types/providers tables + example; the five workflow banners now cover logos (catalog claim kept for media, 'from their official sources' added for logos); product-launch story-design and motion-graphics logo-reveal point at the new type; catalog surfaces (CLAUDE.md / README / docs) updated in lockstep. Verified: 19 unit tests + coverage row green; live smoke across all four tiers (linkedin→svgl, nike→simple-icons, heygen→github.avatar, amazon→favicon) plus a fabricated brand exiting 1 on the default miss path. oxlint + oxfmt clean. * test(media-use): sanction the four logo providers in the registry allowlist svgl / simple-icons / github.avatar / favicon.ddg join the sanctioned list — the logo cascade added in the previous commit. Full lib suite 95/95 green. * test(media-use): gate the logo cascade behavior in CI + single-fetch favicon tier Review follow-ups (miga-heygen, jrusso1020 on #2061): - Eight mocked-network tests pin what the manual 54-brand stress test only asserted: descriptor shape, alias retry (svgl non-array payload → next query, simple-icons 404 → next slug), network-error → null fallthrough, the sub-500B placeholder rejection, github's no-guessing (zero fetches for unmapped entities), and the real cascade order landing tier by tier under a mocked network. - faviconSearch now hands its verified bytes over as a local file, so the freeze step copies instead of re-downloading — one round-trip, and the size check is authoritative over what gets frozen. - The header's hit counts are labeled as a stress-test snapshot, not a live invariant. Full lib suite 103/103; live smoke re-verified (amazon → favicon.ddg, frozen .ico). |
||
|
|
401dd1d27f |
fix: media-use bug-bash fixes (codex gate, id race, provider/reuse/adopt guards) + CLI unknown-flag rejection (#2033)
* fix(media-use): codex gate misfires as 'not logged in' when piped codexUnavailableReason() gated generation on parsing `codex login status` stdout, but that command prints 'Logged in using ChatGPT' to stderr and exits 0 — so the piped stdout media-use captures (execFileSync returns stdout only on success) was empty, and the gate falsely reported 'not logged in'. Every headless / CI / agent run was blocked from codex image gen even when fully authed. Gate on the durable credentials file ($CODEX_HOME/auth.json) instead of the TTY/stderr-only human text. Token validity is still proven by the exec, which fails cleanly on a stale login. The stdout `features list` capability check is unchanged. Verified: reproduced the false 'not logged in' block, then after the fix generated end-to-end via `resolve -t image --provider codex` (valid 1254x1254 PNG, source=generated, provider=codex.image_gen). * fix(media-use): bug-bash fixes — id race, provider/reuse/adopt guards From the bug-bash against main: - MU-23 (HIGH): concurrent resolves raced on nextId (read-max-then-append, non-atomic), so parallel agents got duplicate ids and clobbered each other's files. Add allocateId(): a coarse per-project lock (.media/.lock, 15s stale-steal) around id allocation that scans the manifest AND the type dir for reserved ids, then O_EXCL-creates a placeholder file so the slow download between allocate and append can't collide. 5 parallel resolves now yield 5 distinct ids + files. - X4: --reuse imported across a type mismatch (bgm asset under images/). Apply typesMatch on the --reuse path; reject mismatches (icon<->image still interchangeable). - X5: --provider silently overrode --local-only and made a network call. --local-only is now a hard guard: network providers are skipped even under a forced provider; the miss message explains the conflict. - BUG-2: --provider ignored the exact-cache floor and could hand back an asset from a different provider. A forced --provider now bypasses all reuse rungs (regenerate with THIS provider); the unforced floor is intact. - MU-26/X6: 0-byte assets accepted. --adopt skips 0-byte files (loud); ingest refuses a 0-byte local file (freezeUrl already rejects empty responses). - BUG-4: unknown/unavailable --provider now errors with the available list instead of a generic 'no provider could resolve' (typo != catalog miss). - BUG-5: --reuse "" gave the wrong 'type and intent required' error; it now routes to a clear empty-sha message. - BUG-3: voice duration leaked an unrounded float into index.md; round all durations to 0.1s centrally at record build (matches probe). - Nits: whitespace-only --intent is rejected; nudge grammar (exists/exist). Tests: allocateId reservation + registry local-only-wins added; full media-use suite green. All fixes verified e2e. * fix(cli): reject unknown flags instead of silently ignoring them citty is permissive: an unrecognized flag was dropped, not rejected — so `render . --out x` (the flag is --output/-o) silently ignored --out and rendered to the default renders/<name>.mp4 path. A mistyped flag read as a render/catalog miss. Add assertKnownFlags(): validate every dash-prefixed token against the command's declared args + aliases + the global set (help/version/json) before the command runs, in the shared trackCommandFailures run-wrapper so every leaf command is covered. Handles --flag=value, --no-<bool> negation, camelCase<->kebab arg names, and combined shorts; stops at --; positionals and flag values pass through. Verified: `render . --out x` -> 'Error: Unknown flag: --out'; --output/-o/ --json/--help still accepted. Unit tests added. * docs(skills): install with --full-depth so agents get current main The documented `npx skills add heygen-com/hyperframes` fetched the skills.sh registry blob, which lags GitHub main by hours — so users following the docs got a stale skill (e.g. media-use v1: no --candidates, voice stubbed). The CLI's own `hyperframes skills` command already forces a full clone via --full-depth to bypass this; the docs didn't pass it. Add --full-depth to every documented install command (README, CLAUDE.md, docs/guides/skills.mdx) with a one-line note on the lag. Addresses the user-facing half of the publish/registry lag (#2034). * chore(media-use): collapse resolve.mjs import to satisfy oxfmt --check * fix(cli): extract longFlagName to keep flag validator under complexity gate Also regenerate skills-manifest.json (resolve.mjs formatting change re-hashed the media-use skill). Fixes the Fallow audit + skills-manifest-in-sync CI gates. |
||
|
|
35e54cae19 |
feat(media-use): agent-driven asset reuse (candidates + reuse)
Reuse now hands the semantic judgment to the coding agent instead of a string heuristic, while keeping the deterministic normalize-exact match as an automatic dedup floor. No LLM/embedding call enters resolve; it stays offline-capable. - lib/match.mjs: shared matchTokens + typesMatch (extracted from adopt.mjs and resolve.mjs so the icon<->image equivalence and token rules can't drift between the do-path and the look-path); adds tokenOverlap ranker. - resolve --candidates: side-effect-free listing of reusable assets across the project manifest AND the global ~/.media cache, ranked by lexical overlap, capped per scope, --json or human table. Never hard-filters on zero overlap (that would pre-empt the agent's judgment); the agent decides. - resolve --reuse <sha>: import a specific global-cache asset by content sha/prefix (from --candidates) into the project via importFromCache, marked source=reused-explicit / provenance.reused_by=agent. - Adherence nudge: on a resolve that misses the exact floor and is about to fetch, print a one-line stderr hint when similar cached assets exist, pointing at --candidates. Offline, stderr (safe under --json), never auto-reuses a fuzzy match. - cache.mjs: export readGlobalManifest; add findGlobalBySha (prefix resolve with ambiguity/miss handling). - Telemetry: media_use_candidates event + reused-explicit source on media_use_resolve (type/scope/counts only, no intent text or paths). - SKILL.md: 'Reuse before you resolve' guidance + trust guardrail (prefer-fresh-when-unsure, entity-exact for brand, cross-project bleed). - Tests: lib/candidates.test.mjs (ranking, no-hard-filter, cap/truncation, icon<->image, sha resolution, formatter); adopt.mjs refactor covered by existing lib/adopt.test.mjs. Full media-use suite green; verified e2e against the live catalog (cross-project resolve->candidates->reuse; hint fires on miss). |
||
|
|
5fe957363d | feat(media-use): v2 media OS core (resolve cascade, providers, local generation, telemetry) + retire hyperframes-media | ||
|
|
3900caaaa9 |
feat(core,cli): media-use interop — shared index.md regen + description/entity on figma imports (#1927)
Post-release review of media-use ↔ figma coupling (spec §13.1): - figma asset imports now regenerate .media/index.md, the agent-readable inventory media-use maintains — format locked byte-identical via a cross-runner parity test against media-use's own index-gen.mjs - figma asset --description/--entity land in the manifest record, the index table, and <img alt>; component rasterize auto-describes with the node name. Named brand marks become visible to media-use's resolve --entity lookups. - spec §13.1 records the review verdict (loose coupling correct) and the follow-up queue (shared media-ledger module, global cache for figma assets, media-use version-keyed idempotency) Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b2dc353725 |
feat(media-use): resolve engine + BGM/SFX/image/icon providers (#1683)
* feat(media-use): core infrastructure — manifest, cache, adopt, probe Foundation for media-use — the media resolution layer for HyperFrames. - manifest.mjs: JSONL read/write/find for .media/manifest.jsonl - index-gen.mjs: regenerate agent-readable index.md from manifest - cache.mjs: content-addressed global cache at ~/.media/ (SHA-256, sentinel) - freeze.mjs: download URL or copy local file to .media/ - probe.mjs: extract duration/dimensions via ffprobe - adopt.mjs: scan assets/ directory, register existing files with metadata - 19 passing tests (manifest round-trip, cache, promote, index generation) * fix(media-use): oxfmt formatting + cap freeze download size Format adopt/cache/probe/manifest.test (CI oxfmt --check gate). Cap freezeUrl downloads at 256MB so a hostile/runaway URL can't fill the disk (addresses CodeQL #670: network data written to file). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(media-use): resolve engine + all providers + brand from frame.md - resolve.mjs: cheapest-first cascade - BGM/SFX via heygen --headers, Image/Icon via heygen asset search - Brand tokens from frame.md / design.md (local, no API) - SKILL.md: full agent docs + hyperframes.dev/design redirect - Router skill + workflow skill references * fix(media-use): oxfmt formatting for resolve + providers Format brand/heygen-search/providers/sfx providers + resolve.mjs (CI oxfmt --check gate). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(media-use): align providers with the real heygen CLI surface (v0.1.6) Verified live against the official Go `heygen` CLI v0.1.6 with a valid key: - Caller attribution: pass `--headers 'X-HeyGen-Client-Source: media-use'` (the allowlisted flag the CLI added for media-use in v0.1.6). The old `--x-source media-use` was never a real flag and broke every call. - Command is `asset search` (the `list` leaf was dropped in v0.1.6), not `asset search list`. - `--min-score` is sent server-side: honored by `audio sounds list`, but the `asset search` backend rejects it and returns no score field, so only the audio providers pass it (image/icon don't). - Drop hardcoded `ext` so resolve.mjs derives it from the URL: catalog icons are .png (not .svg), some BGM is .wav (not .mp3). Also: surface CLI/auth failures on stderr instead of swallowing them as 'no results', carry icon width/height through, and document the heygen CLI install + >= v0.1.6 requirement. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |