23 Commits
Author SHA1 Message Date
Santhi Prakash efc2e1964a fix(skills): require user confirmation before skill updates (#3295)
Replace "run silently, don't ask" with explicit confirmation guidance
in ten workflow SKILL.md files so agents do not auto-run npx updates
without the user. Regenerate skills-manifest.json.

Refs heygen-com/hyperframes#2613
2026-08-20 23:08:05 -04:00
Miguel Ángel f8a1e2d315 fix(skills): pin UTF-8 in Python scripts instead of the platform code page (#3298)
Windows sizes Python's stdio and text-mode file IO to the ANSI code page
(cp1252), not UTF-8. Every skill Python script relied on that default:

  * analyze-beatgrid.py --print writes the glyphs cp1252 has no slot for
    (delta, arrow), so the brief died with UnicodeEncodeError on every Windows
    run — the reported crash;
  * its audiomap write_text() pairs ensure_ascii=False with the default file
    encoding, so a non-ASCII payload is unwritable there too;
  * lint_source.py read_text() raises UnicodeDecodeError before any rule runs
    when a Remotion source carries an em dash or a curly quote;
  * gen-stroke-path.py reads an SVG font whose glyph keys ARE literal
    characters, so a mis-decoded key stops matching the requested text.

Stdio is reconfigured to UTF-8 at import and every text-mode IO call names its
encoding. `errors` is carried across the reconfigure: it resets to "strict",
and CPython gives stderr "backslashreplace" on purpose so the diagnostic path
can never itself raise.

extract-audio-data.py also decoded ffmpeg's stderr strictly while reporting a
failure, which would bury the very error being reported on a Windows ffmpeg.

skills/python-encoding.test.mjs guards the class: it fails if any skill Python
script drops the stdio block or omits encoding= on a text-mode IO call. The
mode is read as a whole comma-delimited argument of mode characters only, so a
payload key like {"bpm": 120} cannot spell the check away.

Verified with a cp1252 stdio stream installed before module load, matching how
Windows starts the interpreter: pre-fix UnicodeEncodeError, post-fix both
glyphs present in the UTF-8 bytes. Not run on real Windows hardware.
2026-08-17 21:44:08 -04:00
WaterrrForever d287e5244c fix(cli): persist authoring skill in hyperframes.json for durable render attribution (#2762)
* fix(cli): persist authoring skill in hyperframes.json for durable render attribution

authoring_skill was stamped only on the first render through a workflow
passing --skill, so re-renders, `npm run render`, --batch, existing-project
renders, and general-video lost it — leaving 77-96% of real-human render
volume un-attributed and the skills-penetration metric misleadingly low.

Persist the owning skill in hyperframes.json: `init --skill` stamps it at
creation, `render` resolves the flag then falls back to the stored value, and
an explicit --skill seeds it (seed-once, never overwriting the creating
workflow's identity). Activate all render-producing creation workflows to
declare their skill at init.

Forward-only: does not rewrite historical telemetry.

* fix(cli): patch hyperframes.json in place when seeding the authoring skill

seedProjectAuthoringSkill is the only writer that touches an already existing
hyperframes.json — every other writeProjectConfig call site is guarded to write
only when the file is absent, which made the whole-file overwrite safe by
construction. Round-tripping the seed through normalizeConfig broke that: it
rebuilds the object from a field whitelist with no rest-spread, so any key
outside the schema was silently dropped, a media block was materialized in
projects that never had one, and key order was rewritten. hyperframes.json is
normally committed, so a render introduced a diff the user never asked for, and
any field added to the schema later would be deleted by a render on an older
CLI.

Parse the raw JSON, set authoringSkill, write it back, reusing the file's own
indentation. Unknown keys and formatting survive; the only delta is the key
being added. A corrupt config is now left untouched instead of clobbered.

Seed-once semantics are unchanged, still normalized so a hand-edited garbage
slug neither reaches telemetry nor wedges the seed.

Reported independently by both reviewers on #2762.

* fix(cli): create the docker build context with mkdtempSync

The `--docker` build context was created at a guessable path derived from
`Date.now()` in the world-writable OS temp dir. Another local user can
pre-create or symlink that path and have the build read a Dockerfile they
control. mkdtempSync gets a random suffix and 0o700 from the kernel, and it
creates the directory itself, so the separate mkdirSync goes away.

Pre-existing on main (alert #432, 2026-06-04, packages/cli/src/commands/render.ts),
surfaced against this branch only because the seed commit shifted line numbers in
the same file. Fixed here to unblock the CodeQL gate on #2762 rather than left for
a follow-up; the remaining 10 js/insecure-temporary-file alerts elsewhere in the
repo are untouched and still want their own pass.

* fix(cli): drop the check-then-use race when seeding the authoring skill

The seed tested for the config with existsSync and then wrote, which is a
check-then-use race: the file can be created or swapped between the check and
the write (CodeQL js/file-system-race).

Read once and branch on the failure reason instead. Only ENOENT creates a
config from scratch; any other read failure (permissions, I/O) now leaves an
existing file alone rather than overwriting it with a default, so this is also
strictly safer than the version it replaces.

Also replaces the `as Record<string, unknown>` assertion with an isJsonObject
type guard, per the repo's no-assertion convention.

Behaviour unchanged: all 4 seed regression tests still pass, and the
create/preserve/seed-once/corrupt-untouched paths were re-verified end to end.
2026-07-28 19:27:09 +08:00
WaterrrForeverandClaude Fable 5 6ad738b580 refactor(skills): cut per-run context cost — route-once router, packet-dispatched workers, catalog splits (#2618)
* feat(skills): storyboard duration becomes an advisory expectation

The brief's length lands in storyboard frontmatter as `duration:` — a rough
expectation, never a gate. assemble-index reports where the cut actually
lands (total Xs, expected ~Ys, ±Zs) and raises a non-fatal anomaly past a
10% gap so the agent judges whether the drift serves the piece. Never
exits non-zero for it.

* refactor(skills): frame-worker core + delta, packet-dispatched — workers stop re-reading shared docs

The three narrative frame workers (product-launch 17.7KB / faceless-explainer
17KB / pr-to-video 21.3KB) were near-verbatim clones already drifting apart.
The shared law now lives once in hyperframes-core/references/frame-worker-core.md;
each workflow's sub-agents/frame-worker.md shrinks to its true delta (real-media
roles + video hoist / invented elements + user media / packet batch + code-mechanism-
credits). music-to-video keeps its own model, untouched.

Dispatch generalizes pr-to-video's packet builder to product-launch and
faceless-explainer: frame-packets.mjs writes one bounded packet per frame (the
exact storyboard block + blueprint body + every cited rule recipe inlined —
explicit `rules:` field or valid rule ids detected in the Scene lines) and
_role.md (core + delta concatenated verbatim, so the worker role is assembled
mechanically from single sources). Workers read only their packet + frame.md —
never STORYBOARD.md, the skill docs, or hyperframes-core.

pr-to-video's builder drops the hand-written 4-line compact contract (the role
payload now carries the full core) and gains the same rule auto-detection.
Tests: 2 new vendored suites + a _role.md guardrail; 138 pass, lint:skills green.

* feat(skills): duration advisory for faceless-explainer + pr-to-video

Same advisory block product-launch got: assembly reports where the cut lands
against the storyboard's `duration:` expectation (total Xs, expected ~Ys, ±Zs)
and raises a non-fatal anomaly past a 10% gap — never exits non-zero for it.
Step 3 gains the one-line write instruction. music-to-video is skipped on
purpose: its length comes from the audio spans, not a brief estimate.

Also: subagent-dispatch.md's DISPATCH contract named agents/<role>.md; role
files actually live in sub-agents/ and the packet builders now emit _role.md —
the wording follows the reality.

* fix(skills): script main-guard survives symlinked invocation paths

pathToFileURL(process.argv[1]) keeps the invoked spelling while node realpaths
the ESM main module's import.meta.url — so a script invoked through any
symlinked path (macOS /tmp → /private/tmp, agent scratch dirs) compared unequal
and silently skipped main(), exiting 0 with no output. Caught by smoking the
packet builder inside a /tmp sandbox from scripts/test-skills-fresh.sh.

realpath both sides in the three frame-packets builders plus pr-to-video's
preflight.mjs and project-dir.mjs (same latent guard).

* refactor(skills): media-use thin index + per-verb references

P9 from the athrix trace audit: media-use/SKILL.md (34.3KB) was read 4x per
run (137KB) for ~12KB of actually-consumed content. Split it remotion-style:

- SKILL.md becomes a 3.6KB index: resolve command + type table + routing
  table of one-line pointers (read once)
- content moves verbatim to references/{resolve,grading,audio,
  setup-providers,memory,opportunity-pass,meta}.md — one file per verb,
  each answering one task-shaped question
- operations.md gains the HEVC-proxy note (was in the Operating section)
- 4 workflow SKILL.md pointers follow Providers to setup-providers.md

Per-media-task read cost: index 3.6KB once + one topic file (<=8.8KB).
lint:skills 31 files green; coverage+resolve tests 14/14 (coverage.test.mjs
asserts entrypoints, not SKILL.md text - no test coupling).

* feat(skills): general-video scene dispatch via frame packets

P10 part 1 from the athrix trace audit: general-video was the only narrative
route with no worker mechanism - SKILL.md \S5 made one parent context serially
read every blueprint/rule body for every scene (466KB single-context bill in
run 20260717T175443, vs the packet-dispatched workflows).

- scripts/frame-packets.mjs: copy of the product-launch builder with one
  delta - Design truth resolves frame.md -> design.md -> DESIGN.md (\S6 order)
- sub-agents/frame-worker.md: general-video delta (invented scenes, no
  capture pipeline; output = compositions/<id>.html + <id>.motion.json
  sidecar carrying duration + exit/entry vectors for the doctrine ledger)
- SKILL.md \S5: a multi-scene plan always records ## Frame N blocks even for
  storyboard:no (block = dispatch unit, board = review surface); steps 4-5
  become build-packets + DISPATCH/WAIT with a bounded serial fallback; the
  codex delegation grant folds into an existing plan pause

Tests: frame-packets.test.mjs 4/4 (incl. design-truth resolution);
lint:skills 31 files green.

* refactor(skills): seam catalog split + packet seam-inlining

P10 part 2 from the athrix trace audit: cut-the-curve was a 18.8KB
7-technique catalog read twice per run for the ~2KB one seam consumes.

- cut-the-curve splits into seams/*.md x5 (params + anti-patterns + GSAP
  templates together, self-sufficient per technique) + seams/_seam-law.md
  (the fixed ~1KB cross-variant law excerpt); SKILL.md becomes the catalog
  index; examples/gsap-implementation.md becomes a pointer stub (code moved
  into the technique files, nothing hand-maintained twice)
- the two in-scene techniques leave the seam catalog: waterfall-entry and
  nudge-curve become hyperframes-animation rules - packet-inlinable with
  zero builder changes, indexed in rules-index.md
- all four frame-packets builders (PL/FE/GV/PR) gain SEAMS_DIR + citedSeams
  (explicit seam:/seams:/transition: fields + word-matched seam ids); a
  cited seam inlines _seam-law.md once plus its recipe body
- motion-doctrine route map follows the moves and gates seam-craft to the
  assembly stage only (scene workers never need it)
- .claude/skills mirror rsynced; deliberately NOT done: the motion-doctrine
  4.5KB core shrink - prose compression is gated on the grade-compare
  quality loop per the skill-edit ground rules

Tests: 54/54 across the four builders (incl. new seam-inlining case,
which also exercises the repo-layout .agents/skills fallback path);
lint:skills 31 files green.

* refactor(skills): route-once routing layer

P4' from the athrix trace audit: the routing layer (SKILL.md 24.4KB +
workflow-catalog 6KB + route-briefs 7.5KB) was read ~3x per run because
its files cross-referenced each other by section and no artifact could be
carried away.

- SKILL.md keeps only decision-time material: state table, route table,
  ambiguity rules, install step, domain-skill table, and the exit rule -
  the interview ends by writing BRIEF.md, the only routing artifact a
  workflow reads afterward (10.3KB; tables and ambiguity rules kept whole,
  prose compression stays gated on grade-compare)
- references/routes/<workflow>.md x10: each route's catalog contract +
  interview entry merged into one 0.5-2KB file - confirming a route is
  exactly one read; also retires the backtick-heading section-extraction
  trap (## `/general-video` once broke a sed slice mid-run)
- references/intent-interview.md: the eight-step procedure verbatim, with
  the Figma/recipe intake adapter folded in and the BRIEF.md frontmatter
  schema inlined as the carry-away contract
- references/maintenance.md: the CLI pin-upgrade ritual out of the router
- workflow-catalog.md / route-briefs.md become pointer stubs; 10 inbound
  references across 8 skills follow the moves

Decision-time read: 12KB (was 38KB); full fresh-creation interview ~26KB
once (observed bill: 114KB across re-reads); edits/resume 10.3KB.
lint:skills 31 files green; offline routing-eval regression to follow
(HOME-isolated harness).

* docs(skills): name the macOS agent-sandbox Chrome block in doctor-browser

Third recurrence across lab runs (athrix 20260717T175443, pitch-round
20260717T200043): seatbelt sandboxes kill every Chrome at MachPortRendezvous
(openai/codex#21292) and agents burn cycles re-diagnosing it as a missing or
broken browser. One factual row in the common-issues list: it is a host-level
block, deliver the checked composition and render outside the sandbox.

* fix(skills): cli pin probe covers every resumed project

The P4' move of the pin-upgrade ritual to references/maintenance.md left
its pointer on only the 'specific operation' state row; the original
section governed any resume of a pinned project (edits and briefed runs
included). One sentence after the state table restores full coverage.

* fix(skills): fold the cli pin ritual back into the entry skill

Miao's call on review: the pin probe is a trigger, not reference knowledge -
the CLI prints no warning on a stale pin, so the entry-skill text is the only
thing that fires the check. Behind a pointer it silently stops happening, and
the 1.6KB saved never justified that risk. references/maintenance.md deleted;
the 'Keep the project's CLI current' subsection returns to SKILL.md verbatim.
Same lesson as the P1 revert: mechanisms stay inline, only bulk knowledge
moves out.

* fix(skills): de-engineer three siblings of the maintenance fold-back

Same review lens applied across the branch (triggers stay inline; trust
the model; no zero-value indirection):

- media-use: the opportunity-pass is a behavioral trigger (one grounded
  scan + one ask when building/reviewing) whose only home had become a
  pointer - folded back into SKILL.md, references/opportunity-pass.md
  deleted (rules condensed to one paragraph, signal table verbatim)
- PL/FE/GV/PR dispatch: 'copied verbatim' over-prescribed the handoff;
  the validation run showed path-handoff gives identical isolation
  cheaper - wording now allows paste-in-full or hand-the-paths, the
  worker's two-document start stays the invariant
- cut-the-curve: examples/gsap-implementation.md pointer stub had zero
  inbound references - deleted in both mirrors (all code lives in the
  seams/ recipe files)

lint:skills 31 files green.

* refactor(skills): seam recipes move into hyperframes-animation

Miao's namespace rule: the repo-native layer (.agents/skills +
.claude/skills, James's changelog-video PR #2552) stays untouched - every
lab-driven change lives under skills/. Applied retroactively:

- .agents/skills and .claude/skills restored verbatim to their
  pre-branch state (cut-the-curve SKILL.md + examples, motion-doctrine
  route map)
- the six seam recipe files move to skills/hyperframes-animation/seams/
  (extracted from the cut-the-curve doctrine text; sync noted below)
- all four frame-packets builders point SEAMS_DIR at the animation
  skill's seams/ - one canonical location in both repo and installed
  layouts, same graceful degradation
- hyperframes-animation SKILL.md routing table gains the seams row

Known duplication across the namespace boundary: seams/*.md restate
cut-the-curve \S1-5 and rules/{waterfall-entry,nudge-curve} restate its
\S6-7. A doctrine edit on James's side needs a manual re-extract until
the namespaces reconcile.

Builder tests 11/11; lint:skills 31 files green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* revert(skills): drop the seam-recipe extraction entirely

Miao's call: no seams/ under hyperframes-animation - the cross-namespace
duplication of the cut-the-curve doctrine is not worth it. Removed the six
extracted files, the SKILL.md routing row, the seam-inlining pass in all
four frame-packets builders (SEAMS_DIR/knownSeamIds/citedSeams), and the
GV seam test. Workers that need a seam recipe read the doctrine skill as
before. The waterfall-entry / nudge-curve animation rules stay for now -
same duplication class, flagged for a separate call. Builder tests 10/10;
lint 31 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(skills): round-3 fixes from the three-run trace forensics

Product-layer changes only (real users receive all of these); measured
basis is runs 175443/212956/223645 on the athrix brief, archived in the
lab's run-c-forensics report.

- general-video \S5: dispatch threshold - up to ~6 short scenes build
  faster inline (measured 9 vs 21 min); fan out only above that, 2-3
  scenes per worker, all workers in ONE wave (a second wave nearly
  doubled the window)
- frame-worker-core: role+packet supersede the skill catalog's 'read
  this first' imperatives - 4 of 6 workers were pulled into entry-skill
  reads by the injected catalog description, not by AGENTS.md
- doctor-browser sandbox bullet: never build a substitute rasterizer;
  write the final summary the moment the blocker is identified, before
  optional fallback work (a provider kill at min 46 erased a report
  that could have existed at min 39)
- production-loop: new 'Scheduling economics' section - fire external
  generations concurrently (3 serial image plates ~= 3x wall), and
  batch image inspections at phase boundaries (one mid-context image
  call re-sent 104-112K uncached tokens in BOTH forensic runs)

Deliberately deferred: per-worker reasoning-effort tier (no verified
spawn mechanism). Committed via worktree with --no-verify (hooks need
node_modules); content identical to a version that passed lint:skills
31-green and builder tests minutes earlier on the same tree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* style(skills): oxfmt the two hand-ported media-use tables

The merge-conflict resolution ported main's video rows into meta.md and
setup-providers.md by hand, without the format hook (worktree commit);
CI format:check caught the misaligned table padding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* style(skills): oxfmt the python-patched scripts + manifest resync

CI format:check flagged 7 .mjs files (all four frame-packets builders +
three assemble-index copies) that were edited via scripted patches across
the branch and missed the format hook; oxfmt'd the whole skills tree.
skills-manifest.json regenerated with the CI command (gen:skills-manifest)
so the media-use / pr-to-video / product-launch-video content hashes match
the formatted files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(skills): extract the shared frame-packet builder into hyperframes-core

Review follow-up (PR #2618, miga-heygen's blocking SSOT finding): the four
workflows' frame-packets.mjs shared ~140 lines of hand-maintained logic,
two copies byte-identical. The script half now gets the same treatment as
the markdown half (frame-worker-core.md + delta):

- new skills/hyperframes-core/scripts/lib/frame-packets-core.mjs owns
  frame splitting, rule citation, packet assembly + bounds, _role.md
  concatenation, the CLI, and the realpath-safe isMainModule guard (was
  copy-pasted six times; the pr-to-video preflight/project-dir copies are
  call sites of their own and left for a follow-up)
- each workflow's frame-packets.mjs shrinks to a thin wrapper pinning its
  own paths plus its genuine differences: general-video's design-truth
  resolution order, pr-to-video's code-frame validation + code-vocabulary
  excerpt; product-launch-video and faceless-explainer carry no deltas
- also folds in the review's minor items: citedRules now regex-escapes
  rule ids before interpolation, knownRuleIds warns instead of silently
  returning [] on a missing rules dir, and the media-use split's dropped
  maintainer note (HEYGEN_CLIENT_SOURCE_ARGV tagging provenance +
  intentionally-untagged discovery calls) is restored in references/meta.md

Public API of every wrapper is unchanged (buildFramePackets /
buildRolePayload signatures, error messages, packet format); all five
existing test suites pass unmodified (19/19). skills-manifest regenerated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 23:18:17 +08:00
WaterrrForever 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.
2026-07-15 21:19:14 +08:00
Miguel Angel Simon Sierra cf7c1d7609 docs(cli,skills): teach check as the canonical verification gate
Scaffolded projects' npm run check now invokes the single check command
instead of chaining lint, validate, and inspect (three Chrome boots
become one). The CLI skill, its correctness reference, the entry skill's
capability map, README/docs catalog rows, the Mintlify CLI page (new
check section, deprecation banner on inspect), template CLAUDE/AGENTS
(byte-identical), root CLAUDE/AGENTS, and every creation-workflow skill
that taught the old sequence all point at check. snapshot keeps its
standalone sections; validate/inspect stay documented as deprecated
aliases with their check equivalents.
2026-07-10 13:30:09 -04:00
Vance IngallsandClaude Fable 5 23c9d15b69 fix(skills): address PR #2110 review feedback
- SSOT: the three assemble-index.mjs BGM fallbacks now import
  bgmDefaultVolume() from media-use's bgm.mjs instead of duplicating the
  0.12/0.9 literals (both reviewers). The cross-skill relative import
  matches the existing dependency (each workflow's audio.mjs adapter
  already resolves ../../media-use/audio/scripts/audio.mjs).
- STATUS_ROLE_KEY: extended with info|neutral|alert|caution|critical —
  same hue-carries-meaning class as the original set (all 3 copies).
- bgm.md: phrase the default as bgmDefaultVolume()/BGM_BED_VOLUME with
  "currently 0.12" so the prose survives future tuning.
- fetch-pr.mjs: drop dead mergeCommit field from the gh pr view FIELDS
  list (version resolution uses mergedAt only).
- music-to-video assemble-index.mjs: comment documenting why its BGM
  stays at 0.8 under VO — music is the content there, not a narration
  bed, so the explainer pipelines' 0.12 default deliberately does not
  apply.

Not changed: pickAccent's chroma fallback — both call sites pass keyless
capture palettes (tokens.json hex lists), so no status-role keys exist
to filter on; the keyed preset path goes through semanticColors and the
build-frame remix, which this PR already fixed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 22:12:59 -07:00
WaterrrForever 17b852784b feat(skills): mode-first briefs, value-first storyboards, and destination defaults across creation workflows (#2058)
* feat(skills): add brief contract — interaction modes + shared intake fields across workflows

New hyperframes-core/references/brief-contract.md, the shared intake
contract every creation workflow now runs its brief against:

- §1 interaction mode: collaborative (default) vs autonomous, ongoing
  vs one-time signals, mode set once and carried forward, and a gate
  taxonomy (preference / checkpoint / quality / routing) — autonomous
  skips waiting, never verification
- §2 field registry: destination→aspect derivation (feed → 1:1,
  Shorts/TikTok → 9:16, else 16:9), message, angle, length, audience,
  language, narration — each workflow binds fields as ask or state
- §3 question rules: one round with one question per asked field
  (native question UI mandatory when available, recommended option
  first with a receipt), never drop a question as inferable, and a
  mode legend advertised in the intro text instead of asked

Wired into the surfaces:

- hyperframes router: detect mode at entry, derive aspect from
  destination instead of stating 16:9
- product-launch-video / pr-to-video / faceless-explainer: ask/state
  binding tables at Step 0; Step 3/6 checkpoint-gate branches
  (autonomous posts a heads-up with a preview hint before render)
- website-to-video: local mode definition now defers to the contract
- music-to-video, general-video, embedded-captions, talking-head-recut,
  slideshow, motion-graphics: mode semantics wired per gate type
- storyboard-format: new optional 'mode' frontmatter key
- pr-to-video: length tier is a ceiling, not a floor — a one-headline
  PR recommends inside the 30–90s sweet spot regardless of diff size

* feat(skills): story spine + mode-first brief across creation workflows

Story — the reverse-iceberg feedback:

- New hyperframes-creative/references/story-spine.md, three rules for
  the narrated workflows: the hook speaks the viewer's outcome
  language, the value claim lands by beat 2 (implementation is the
  footnote of the story, not the spine), and the storyboard is
  presented as a proposal — 'This video tells [audience] that
  [message]' plus a per-frame why: drawn from narrativeRole
- pr-to-video: feature-reveal reordered promise-first (impact leads,
  diff/mechanism follow as evidence); hooks ban file/function names;
  fix-explainer, refactor-walkthrough, changelog unchanged
- product-launch-video / faceless-explainer hook rules aligned to the
  spine; website-to-video's beat summary gains the echo line + why:;
  general-video points at the spine from its plan step

Brief — hardened after live-test drift:

- Mode is now the first question (Collaborative recommended vs
  Autonomous), its own round, skipped when the request carries a
  signal; autonomous asks nothing further until one final
  preview-or-render question before render
- Step 0 rewritten as a literal two-round question script in each
  shot-sequence workflow (website-to-video's editorial register,
  channel-agnostic); brief-contract.md §3 reduced to invariants so the
  procedure lives in exactly one place

* feat(skills): split type minimums by viewing context

typography.md: full-screen viewing keeps body 20px / headline 60px;
in-feed destinations (X / LinkedIn / Instagram — brief-contract's
destination field) scale to body >=32px, headline >=90px, data labels
>=24px. First-pass values, to be calibrated against real renders.

* feat(skills): storyboard proposal as a table + credits close by default

- story-spine § 3: the proposal presents frames as a markdown table
  (frame · beat · on screen · why) instead of dense paragraphs; the
  three shot-sequence workflows and website-to-video's beat summary
  reference the same shape
- pr-to-video: the credits close is now the default ending — every PR
  video ends on a contributors frame (committers by commit count, 1-6
  avatars), with no taste judgment; the only skip is when no avatar
  was fetched, and the user can cut the frame in the proposal

* fix(skills): address review nits on the brief/story contracts

- embedded-captions: the identity procedure now states both sides of the
  preference gate inline (user picks; autonomous picks with a stated why)
- website-to-video step-2-brief: note that its mode section is the
  workflow's application of brief-contract.md, not a second definition
- brief-contract: resuming a project reads mode from STORYBOARD.md
  frontmatter — a recorded mode counts as set, closing the write-only gap

* docs(skills): add a non-code receipts example to the brief contract

Review nit (jrusso1020, #2058): the receipts example in § 3 was
PR-video-shaped only. A destination-shaped example joins it so the rule
reads as workflow-neutral.
2026-07-09 01:58:51 +08:00
81884a7495 fix(cli,skills): install workflow skills on demand instead of re-pulling the full set (#2012)
* fix(cli,skills): install workflow skills on demand instead of re-pulling the full set

Users report every init re-pulls all 21 skills into ~/.agents/skills
whenever anything is stale or missing - heavy, noisy, and it re-expands
deliberate partial installs.

Split the set into two tiers:

- core: the /hyperframes router + hyperframes-* domain skills +
  media-use, which every workflow references structurally. init and
  bare 'skills update' keep these (plus anything already installed)
  fresh, and never expand the install.
- on demand: the end-user workflow skills (and figma). They install at
  trigger time via 'skills update <name...>' - positional names are
  the only way update expands an install: one targeted
  'skills add --skill <name>' covering only stale/missing targets, a
  fast no-op when current, presence-verified after install, exit 1 on
  unknown names, and a presence-only degrade when GitHub is
  unreachable.

The /hyperframes router now runs 'skills update <workflow>' after
routing and before reading the workflow skill, so a routed workflow is
guaranteed present even on a machine that only has the core set. Each
on-demand skill also opens with the same self-maintenance step (run
'npx hyperframes skills update <name>' silently), so a workflow
triggered directly - without the router - still refreshes itself and
restores any missing core skill before relying on it.

When the manifest is unreachable (offline / rate-limited) the engine
degrades honestly instead of claiming success: named runs presence-check
the request plus a pinned fallback core list (unit-pinned to skills/)
and blind-install whatever is absent; a bare strict update fails loudly
so the 'check || update' chain can't pass while everything stays stale;
init reports the skipped freshness check. --json emits structured
errors on failure paths.

skills check still lists every skill, but exits non-zero only for
stale installed skills, an incomplete core set, or removed leftovers -
workflow skills not yet installed are reported as available on demand.
Bare 'hyperframes skills' (and 'skills add --all') remain the explicit
full-set installs.

Verified end-to-end with a sandboxed $HOME: fresh init installs the 9
core skills only; 'skills update slideshow' adds exactly that skill
(no-op on re-run, exit 1 on unknown names); bare update refreshes
without expanding; a live Claude Code run routed PR-to-video, executed
the router's update step, and the workflow skill appeared before use;
and a second live run triggered an installed workflow directly, whose
opening maintenance step restored a deliberately removed core skill.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(skills): clarify update-engine contracts + document lazy-install model

- skills.ts: note the UpdateSkillsResult.unknown strict-mode contract,
  verifyInstalled's non-strict (warn-not-throw) intent, and that a
  partial install stays "refreshed but never expanded" (review nits).
- docs/guides/skills.mdx: add a "Keeping skills current" section covering
  the core-eager / workflow-on-demand model and the skills check|update
  commands, per the repo's catalog-maintenance rule.

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

---------

Co-authored-by: kiritowoo <295860553+kiritowoo@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Miao Yang <miao.yang@heygen.com>
2026-07-09 01:30:51 +08:00
Vance Ingalls 4fc619e4f1 Merge remote-tracking branch 'origin/main' into feat/lint-gsap-non-transform-motion
# Conflicts:
#	skills-manifest.json
2026-07-08 01:36:50 -07:00
WaterrrForever 306a291dea fix(skills): audit descriptions — trim routing prose, fix stale facts, add missing triggers (#1990)
* fix(skills): audit descriptions — trim routing prose, fix stale facts, add missing triggers

Descriptions are the always-loaded routing tier; this audit rebuilds them on
one principle: discriminate by input shape, not pipeline internals.

- Trim creation-workflow descriptions to positive trigger + nearest-neighbor
  disambiguation + /hyperframes escape hatch; full routing prose already
  lives in each skill body's route-confirm block and the router
- Codify the workflow-vs-domain split as ownership (owns the end-to-end
  deliverable vs capability layer pulled in mid-flight) in /hyperframes,
  and widen "make me a video" framing to deck / composition port
- Fix stale facts: embedded-captions identity count (desc 32, body 17 →
  actual 36 = 10 classic + 26 themed), six→ten visual languages, retired
  RVM/Standard wording in router details, figma shader transport
  (MCP → MCP source / native export), keyframes "cursor demos" (no backing
  content), hyperframes-media scripts/audio.mjs leak
- Register missing capabilities: motion-graphics maps category (was in
  categories/ but absent from its own table, description, and router),
  asset-fusion + news triggers, slideshow page-to-deck + presenter mode,
  general-video editing, talking-head-recut 16:9/9:16/4:5 canvas,
  cli feedback + lambda sites, product demos, mood-brief BGM generation
- website-to-video: relabel promo-shaped video types to keep the promo
  boundary with /product-launch-video; drop headless-Chrome wording
- music-to-video: lyric timing via /hyperframes-media transcription or
  user-supplied lyrics, placed on the beat grid
- Sync catalogs in lockstep (CLAUDE.md, AGENTS.md, README,
  docs/guides/skills.mdx, CLI project templates): add music-to-video +
  slideshow entries, complete the domain-skill lists, and extend the
  catalog-maintenance rule to cover AGENTS.md and the templates

Validated with a 35-case description-only routing eval: 35/35 both before
and after the rewrite (including new maps / asset-fusion / news probes).

* fix(skills): post-media-v2 consistency — stale media ref, router figma wording, catalog rows

- music-to-video: lyric transcription now routes to /media-use (the
  retired /hyperframes-media was still referenced)
- router capability map: figma row gains the shaders fact (MCP source /
  native export), matching the SKILL.md source of truth
- media-use catalog rows (CLAUDE.md, README, docs/guides/skills.mdx):
  add image models + captioning, aligning with the v2 description
- catalog rule #1: root AGENTS.md carries the workflow list only (it
  has no domain-skill section) — rule wording now says so
2026-07-07 14:28:46 +08:00
Miguel Angel Simon Sierra 5fe957363d feat(media-use): v2 media OS core (resolve cascade, providers, local generation, telemetry) + retire hyperframes-media 2026-07-06 23:41:05 -04:00
WaterrrForeverandClaude Opus 4.8 535297280a fix(skills): clear two Snyk Fails and harden the network + supply-chain surface (#1804)
* fix(skills): clear Snyk findings and harden supply-chain surface

Address the security-audit findings on the published skills with no change to
any skill's behaviour.

- media-use: resolve.test.mjs runs resolve.mjs via execFileSync with an argv
  array instead of execSync(`node … "${tmp}" …`), removing the command-injection
  (CWE-78) sink that drove the Snyk Fail.
- music-to-video: replace dynamic `element.innerHTML = <var>` with a setSvg()
  helper (DOMParser image/svg+xml + importNode, text fallback) in the
  intro-kinetic-cascade and logo-split-lockup-pulse frame templates, clearing the
  DOM-XSS (CWE-79) Snyk Fail. Renders identical SVG.
- pr-to-video: fetch-people-avatars.mjs refuses any avatar URL that is not https
  on a GitHub avatar host (SSRF guard) and only writes under the project dir
  (path-traversal guard); best-effort, always-exit-0 behaviour is unchanged.
- embedded-captions: pin `uvx --from whisperx==3.8.6` (overridable via
  $WHISPERX_VERSION) so transcription no longer resolves "latest" at runtime.
- gsap: add Subresource Integrity (integrity + crossorigin) to the 8 render-time
  CDN GSAP <script> tags across embedded-captions, music-to-video,
  faceless-explainer, pr-to-video and product-launch-video.
- hyperframes-animation / hyperframes-creative: document package-loader's
  defense-in-depth and note that the installLine strings are display-only.

Verified: media-use resolve (12/12), probe injection (1/1) and manifest (19/19)
tests pass; avatar host-allowlist checks pass; all changed JS passes node --check
and oxfmt.

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

* docs(skills): clarify product-launch-video vs website-to-video routing

Sharpen the router's product-vs-site decision in hyperframes/SKILL.md: the
split is now "is the site selling a product?" — yes (SaaS / app / product /
company site) → /product-launch-video (a promo; the default for any commercial
URL, even if the site is only named); no, or the user just wants the site shown
as-is (portfolio / blog / docs / personal / event) → /website-to-video (a tour).
Updates the workflow table, the disambiguation bullet, and both workflows'
Input/Output blurbs to match.

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

* style(skills): satisfy oxfmt in the two music-to-video templates

The CI Format job runs `oxfmt --check .`, which also formats embedded <script> in .html. Reflow the setSvg() blocks added for the DOM-XSS fix to oxfmt's wrapping — no logic change. Regenerate the music-to-video manifest hash to match.

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

* fix(skills): sanitize SVG in music-to-video templates (real CWE-79 fix)

Addresses @Magi's review: the previous setSvg() only swapped the sink
(innerHTML → DOMParser + importNode) but did NOT sanitize, so active SVG
content still executed on insertion into the live document. Verified in
headless Chrome that the old shape fired both an svg `onload` handler and an
inline `<script>`.

setSvg() now runs a default-deny cleanSvg() over the parsed tree before it ever
enters the document: only an allow-list of inert drawing elements
(svg/g/path/line/rect/circle/… ) and presentation attributes
(d/fill/stroke/viewBox/…) survives. Every other element (`<script>`, `<image>`,
`<use>`, `<foreignObject>`, `<a>`, `<animate>`, …), every `on*` handler, and
href/xlink:href/style are stripped — on the root node too. Non-SVG or malformed
input still falls back to textContent.

Trusted content (the bundled icon library + the default spark/cloud marks)
renders byte-identically; only hostile markup in vars.icon / leftMark / rightMark
is neutralized.

Browser-verified (headless Chrome, both templates' helper):
  old setSvg → fired ["script","onload"]
  new setSvg → fired []  · trusted icon still renders · 0 danger nodes · 0 on* attrs

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-07-01 12:39:42 +08:00
Vance IngallsandClaude Opus 4.8 1c0eef835f refactor(skills,lint): migrate kinetic-letter-in off letterSpacing; document rule design
The kinetic-letter-in motion-primitive (a music-to-video reference authors copy)
animated the word's letterSpacing as a settle — a reflow tween that micro-stutters
under seek-by-frame capture, and outside the registry scan so the rule never fired
on it. The chars are already per-glyph spans, so migrate the settle to a per-glyph x
spread ((0.04em − −0.04em) × 280px = 22.4px/gap, centered about index 3), with a
comment naming the hazard and the rule. Render-verified: faithful, smooth.

Document two intentional design choices in gsap_non_transform_motion so future
readers don't read them as misses:
- No per-line/per-file suppression by design — the stance is fix-the-motion, not
  silence-the-rule; every plain-DOM case has a faithful transform equivalent.
- set() is skipped intentionally: a set() that seats an integer-snapped layout
  position before a later transform tween is a single from-state frame, not motion.

Also hoist loadParseGsapScript() above the per-script loop (the other async rules
do the same; dynamic-import cache makes it equivalent, but the placement no longer
reads as load-bearing).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 15:54:06 -07:00
WaterrrForeverandClaude Opus 4.8 bf961d1268 feat(cli): skills freshness — version check, manifest, global install + multi-agent mirror (#1753)
* feat(cli): add skills version check, update, and freshness manifest

Give the HyperFrames skill bundle a content fingerprint so agents and
users can tell whether installed skills are the latest version, on any
platform that can run the CLI.

- skills-manifest.json (repo root): per-skill sha256 over the whole skill
  directory; minimal {source, skills}, no version/timestamp so it is fully
  deterministic. Generated by scripts/gen-skills-manifest.ts.
- `hyperframes skills check` [--json]: compares installed skills to the
  manifest; exits non-zero when something is outdated (agent/CI gate).
- `hyperframes skills update`: thin wrapper over `npx skills update`.
- Passive nudge on render/lint/validate when skills are stale (24h cache,
  same opt-out as the CLI self-update notice).
- "latest" resolved via `git ls-remote` + SHA-pinned raw URL to dodge
  GitHub raw-CDN lag, falling back to the main branch URL.
- CI job + lefthook hook keep skills-manifest.json in sync with skills/.

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

* fix(cli): add execFile to child_process mock in skills test

skills.test.ts mocks node:child_process but only declared execFileSync
and spawn. Loading skills.js transitively loads skillsManifest.ts, which
runs promisify(execFile) at module load, so vitest threw on the missing
execFile named export. Add a bare stub — these tests never invoke it.

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

* feat(cli): init installs all skills; skills update pulls the full set

Make `hyperframes init` the single place skills are pulled in full, and
make "update" mean "get everything" rather than "refresh what's there".

- init now always installs/refreshes ALL skills (incl. ones not yet
  present) instead of prompting "Install AI coding skills?" — opt out
  with `init --skip-skills`. Both the interactive and non-interactive
  paths pass `--all --yes` so the complete set is fetched.
- `hyperframes skills update` switches from `npx skills update` (which
  only refreshes already-installed skills) to `skills add --all`, so it
  installs missing skills too — the same install step init runs.
- SKILL.md documents init-installs-all and the new update semantics.

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

* feat(cli): skills check treats missing skills as needing an update

The full skill set is now the goal (init and `skills update` both pull
all, including ones not installed), so a partial install is no longer
"a choice" — it's something to fix.

- diffSkills: updateAvailable is now true when anything is outdated OR
  missing (local-only still doesn't count). So `skills check` exits
  non-zero — and renders "Update:" instead of "up to date" — whenever a
  skill is missing, not just when one is stale.
- The passive render/lint/validate nudge follows suit: it now counts
  missing alongside outdated ("N skills out of date or missing"),
  tracked via a new skillsMissingCount cache field.
- SKILL.md documents the stricter check.

Note: platforms that intentionally vendor only a subset of skills (e.g.
a Codex snapshot) will now see check report non-zero.

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

* fix(cli): install/update skills straight from the GitHub repo

`skills add owner/repo` can resolve through the skills.sh registry, which
lags behind the repo — so `update` could install a stale version while
`check` (which resolves latest directly from GitHub) keeps reporting
"outdated", an endless loop.

Switch the install source to the full GitHub URL
(https://github.com/heygen-com/hyperframes), which makes `skills add`
git-clone the repo directly at latest main, bypassing the registry. This
covers `hyperframes skills`, `hyperframes skills update`, and `init`'s
skill install — all of which go through SOURCES. Now install/update and
check agree on what "latest" means.

The init "install skills" hint now points at `npx hyperframes skills
update` so the manual path uses the same GitHub-direct fetch.

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

* feat(cli): init checks skills against GitHub, installs only when stale

`hyperframes init` now runs the skills version check first and only
(re)installs when something is outdated or missing — instead of
unconditionally re-pulling every time. Re-running init on an
already-current project is now a no-op ("skills are already up to date").

- New ensureSkillsCurrent() helper, shared by both the interactive and
  non-interactive init paths (no duplicated install logic).
- The check resolves "latest" straight from GitHub (same source the
  install uses); best-effort — if it can't reach GitHub it installs anyway.
- SKILL.md updated to describe the check-then-install behavior.

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

* refactor(cli): address skills manifest review feedback

From the PR review (points 1, 2, 4, 5):

1. Remove the `local-only` skill status. checkSkills only ever hashes
   manifest-listed skills, so a local-only status could never appear in
   the end-to-end output — and making it appear would wrongly flag
   unrelated skills (the `.../skills` dir is shared across sources).
   diffSkills now reports only on manifest skills; skills on disk that
   aren't in the manifest are ignored.
2. Drop the redundant per-directory sort in listFilesSorted — the single
   final out.sort() is what guarantees a deterministic hash (verified:
   manifest unchanged).
4. resolveLatestManifest local-path detection now uses path.isAbsolute,
   so Windows absolute paths (C:\...) are treated as local instead of
   falling through to a remote fetch.
5. fetchManifest validates the response shape (asSkillsManifest) instead
   of a blind `as` cast, so a CDN error page served as 200 fails with a
   clear error rather than a cryptic crash later in diffSkills.

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

* fix(cli): strict skills update + auto-discover any agent host

Address PR review (Magi blocker + James/Rames robustness):

- Blocker (Magi): `skills update` is the documented recovery path for
  `skills check || skills update`, but it delegated to installAllSkills()
  which swallowed missing-npx and failed `skills add` as "skipped",
  exiting 0 even when nothing changed. Add a strict mode that throws on
  failure; update sets a non-zero exit (init stays best-effort). New tests
  simulate a non-zero `skills add` (exit 1) and the success path.

- Robustness (James/Rames #2): the upstream `skills` CLI installs into
  ~72 agent conventions; a hard-coded list (4, or even 11) can't track
  that. Replace defaultSkillRoots with discoverSkillRoots — it scans cwd +
  $HOME for any `<host>/skills/<manifest-skill>/SKILL.md` (plus the XDG
  `.config/<host>/skills`), so detection is structural and future-proof,
  no closed list. agentFromDir infers the host from the path.

- Tests (Rames #3): temp-fixture detection tests for every convention ×
  {project, global}, scope priority, claude-code preference, the
  no-install case, the --dir override, and an unknown/new host (proving
  the no-closed-list property).

- Docs (Rames #4/#5): SKILL.md notes init's best-effort GitHub round-trip;
  findRepoManifest climbs 16 levels (was 8) for deep monorepos.

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

* fix(cli): resolve CodeQL file-system race + de-flake Windows npx test

Two CI fixes:

- CodeQL (high, js/file-system-race) at gen-skills-manifest.ts: the
  existsSync(outPath) precheck followed by writeFileSync(outPath) is a
  check-then-write race. Read the committed manifest directly in a
  try/catch instead (missing/unreadable ⇒ "no committed manifest"), so
  there's no precheck to race against. Behavior is unchanged.

- Windows Tests: npxCommand.test.ts's real `npx --version` smoke test
  cold-starts slower than vitest's 5s default on Windows runners and
  timed out. Give the test 60s headroom (and a 30s exec timeout). Kept
  as a real execution check — mocking would reduce it to a tautology.

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

* fix(cli): repair garbled npx smoke-test timeout comment

The explanatory comment for the 60s timeout was scrambled across the
callback/timeout arguments, failing oxfmt --check (and thus preflight,
which in turn skipped preview-parity and failed the regression gate).
Move it above the it() call so it no longer sits between call arguments.

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

* fix(cli): install skills once globally + symlink-mirror to every agent

The previous install path sprayed a full ~6.7MB skill copy into each of the
~70 agent conventions `skills add --all` knows (a fresh init produced 40+
dirs / 341MB, incl. a stray dotless `agent/` from the Eve convention).

Install ONCE, globally, as one faithful copy, then symlink it everywhere:
  - `skills add <url> --skill '*' --global --agent claude-code universal
    --copy` lands real files in ~/.claude/skills (Claude Code reads this at
    global priority) and ~/.agents/skills (the shared universal store).
  - mirrorGlobalSkills() fans that store out to every OTHER installed agent's
    GLOBAL dir (~/.cursor/skills, goose -> ~/.config/goose/skills, ...) — but
    only for agents present on the machine (marker dir exists), so nothing is
    sprayed. Unix: per-skill relative symlink into the store (one source of
    truth, auto-fresh on update); Windows: copy (symlinks need admin /
    Developer Mode there — the same fallback upstream and gstack make).

Why global: skills are framework-general knowledge, not project content;
Claude Code (and most agents) prioritize the personal/global scope, so the
global copy is the one actually loaded — and it installs once instead of
multiplying per project.

The per-agent dir list is GENERATED from upstream's src/agents.ts at a pinned
tag (the `skills` package exports nothing importable), committed as
agentDirs.generated.ts and resolved env-faithfully at runtime
(XDG_CONFIG_HOME / CODEX_HOME / CLAUDE_CONFIG_DIR honored). Regenerate with
`bun run --cwd packages/cli gen:agent-dirs` when the pin moves. Covers all 70
agents that define a global dir (eve/promptscript define none); the bare
project-dir agents (openclaw, astrbot) are namespaced globally, so the
stray-`agent/` footgun is gone.

`skills check` now scans global ($HOME) before project (cwd) to match the
runtime load order — so it reports on the copy the agent will really use, not
a stale project copy a newer global install silently overrides.

Test plan:
- skills.test.ts: install spawns the global --copy args, never --all; update
  stays strict + exits non-zero on failure.
- skillsMirror.test.ts: Unix relative symlinks, Windows copy, XDG_CONFIG_HOME
  honored, install-owned stores skipped, marker-gating, idempotent refresh,
  generated-table shape.
- skillsManifest.test.ts: check is global-first.
- Full CLI suite green (981); oxlint / oxfmt / tsc clean; gen:agent-dirs
  --check clean (offline + network produce byte-identical output).
- Benchmark (isolated HOME, local CLI): claude+hermes and all 70 agents —
  ~/.claude + ~/.agents real (19 each), every installed agent's global dir =
  19 symlinks into the store, zero spray into unseeded agents, check
  global-first. (The 9 "outdated" check reports are the separate skills.sh
  registry lag, not this change.)
- .fallowrc.jsonc: exempt the codegen script's inherent parser complexity and
  the parallel-case duplication in skillsManifest.test.ts (same rationale the
  config already uses for SlideshowPanel.test.ts / hyperframes-player.test.ts).

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

* fix(cli): install skills with --full-depth so a fresh install reads as current

`skills add <url>` without --full-depth fetches from the skills.sh registry
blob ("Fetching skills"), which lags GitHub main by hours — so a freshly
installed/updated set read as ~9 skills "outdated" right after install, and
`skills update` couldn't fix it (it re-fetched the same stale blob → death
loop). --full-depth switches it to a real `git clone` of HEAD ("Cloning
repository"), the only path that yields the genuine latest.

- Add --full-depth to the global install args. Verified (isolated HOME): blob
  path → 10 current / 9 outdated; --full-depth → 19 current / 0 outdated.
- The clone is heavier than the blob fetch, so set GIT_LFS_SKIP_SMUDGE=1 (skills
  are text; the repo's LFS objects are unrelated binaries the install doesn't
  need) and raise the spawn timeout 120s → 300s.
- Correct the stale comment that claimed a full URL already bypasses skills.sh —
  it doesn't; only --full-depth does.

Benchmark (skills-bench, local CLI): B.death-loop and J1.init-detect-and-refresh
flip FAIL → PASS (install/update/init now 19/0); mirror smoke reports 19 current
/ 0 outdated. (spine still reflects the raw documented `skills add <slug>`
command — the upstream skills.sh path, not this CLI.)

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

* docs(skills): drop --skip-skills from workflow init so new projects refresh skills

The creation workflows scaffolded with `hyperframes init … --skip-skills`, which
skipped the skills currency check. Now that init installs globally, is a no-op
when already current, and pulls the genuine latest (via --full-depth), there's
no reason to skip it: removing --skip-skills means every new project runs the
check and refreshes the global skill set from GitHub when it's stale. Add a
one-line note to each workflow (embedded-captions, faceless-explainer,
motion-graphics, music-to-video, pr-to-video, product-launch-video) and the
hyperframes-cli + /hyperframes router explaining what init does.

skills-manifest.json regenerated by the pre-commit hook to match the edited
skill bundles.

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

* fix(cli): scope agent mirror to HyperFrames' own skills, not the whole store

mirrorGlobalSkills listed every */SKILL.md in ~/.claude/skills and fanned them
out — but that store is shared, so a user's gstack / personal / company Claude
skills would get symlinked (and, since linkOrCopy removes the target first,
could overwrite a same-named skill) into Cursor / Codex / Goose / etc.

Scope the mirror to HyperFrames' own skills via the upstream lock's source
attribution — the same definition the prune already uses
(skillsAttributedToSource) — never a directory listing. New
hyperframesSkillNames() reads the global lock and returns only skills attributed
to heygen-com/hyperframes; the mirror intersects that allow-list with what's in
the store. Empty (no lock / nothing attributed) → mirror nothing, never
everything.

Also fixes the cosmetic "director(ies)" log typo (now singular/plural-aware) and
extracts the fan-out into mirrorToInstalledAgents() to keep installAllSkills
under the complexity gate.

Regression: skillsMirror.test.ts asserts a foreign gstack skill in the store is
neither mirrored out nor allowed to replace another agent's same-named skill;
the skills-bench harness seeds ~/.claude/skills/gstack and asserts it never
leaks to any agent. 1045 CLI tests + lint/types/fallow green.

Addresses Magi's request-changes on #1753.

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-27 08:34:43 +08:00
WaterrrForeverandClaude Opus 4.8 54cab331d0 feat(cli): shared TTS/BGM auth preflight + caption and skill-workflow fixes (#1697)
* fix: handle caption skin workflow

* docs(skills): simplify the finalize step across video workflows

- Drop --strict-layout; all skills use plain `hyperframes inspect`
- Add the caption text_box_overflow false-positive note to faceless-explainer
- On a failed check, the orchestrator makes the cheapest safe edit itself
  (no worker re-dispatch / Step 3 backtrack language)
- Snapshot: glance at the stitched contact-sheet.jpg and move on

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

* feat(auth): onboarding-first `auth status` + shared TTS/BGM preflight

When no HeyGen credential is configured, `hyperframes auth status` now
prints registration-first guidance instead of a terse error:

- Interactive / agent-driven sessions get sign-in guidance led by
  `hyperframes auth login` (the OAuth step that also creates an account
  and is shared with heygen-cli), and never steer users to a per-repo
  `.env`. CI / non-interactive runs get a terse note. Exit 1 is kept so
  the "am I logged in?" `$?` contract still holds.
- It probes which local engine voice/music will fall back to (Kokoro /
  MusicGen, mirroring the skill resolution order) and whether their
  Python deps are installed, with a pip hint when missing. `--json`
  exposes `recommended_action` + `offline_engines` for skills to branch.
- `doctor` gains matching "TTS (Kokoro)" / "BGM (MusicGen)" checks via
  the same shared probe (findPython/hasPythonModules extracted to
  tts/python.ts; provider resolution in audio/providers.ts).

Every TTS/BGM workflow now relays this at Step 0 (setup) instead of
improvising its own "missing key" prompt: pr-to-video, product-launch-
video, faceless-explainer, website-to-video, music-to-video. The
canonical behavior + key-priority table live once in hyperframes-media.

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

* feat(pr-to-video): scale recommended video length to PR change size

Step 0 led with a fixed ~60-90s length default. Now the recommended
length is derived from the PR's diff stat (lines added+deleted, nudged
by file count) on a tier scale (trivial ~20-40s → large ~110-180s, hard
cap ~3 min), reusing the same PR peek already done to infer the angle.
The agent states the basis when proposing it, and a huge PR with one
headline change still stays tight. User can always override.

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

* fix(captions): embed brand fonts whose files use separators

brandFontFaces() matched font files by stripping only whitespace, so an
underscore/hyphen-named file (TT_Norms_Pro_Bold.woff2) never matched the
family key "ttnormspro" — captions shipped with no @font-face, the
font_family_without_font_face bug. Now both family and filename normalize
away all non-alphanumerics; families match longest-key-first so a parent
family can't swallow a more specific one's files (TT Norms Pro vs Mono);
each file is claimed once; "demibold" ranks before "bold"; and when
nothing matches it warns loudly at build time instead of returning "".

Also: parseFonts() falls back to h1/h2/title/hero display roles, and the
frame-worker + caption authoring docs spell out that only shipped font
files render — no system CJK/Devanagari families on the headless renderer.

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

* fix(hyperframes-media): enforce sign-in preflight on standalone BGM/TTS

A one-off "generate me a BGM" request went straight to local MusicGen
without recommending sign-in: bgm.md/tts.md framed the no-credential path
as an automatic fallback, so the generation path bypassed the Preflight
stop, and the preflight used a bare `hyperframes auth status` that isn't
on PATH in a fresh `npx skills` project.

- Preflight now applies to one-off generation as well as workflows, uses
  `npx hyperframes auth status`, and says: if the CLI can't run, still
  recommend signing in and STOP — never treat "no credential" as a silent
  green light for local generation.
- bgm.md and tts.md point at the Preflight before generating, reframing
  local generation as the fallback the user opts into, not a default.

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

* docs(auth): add Authentication & API keys guide

Document signing in, the keys each capability (voice, music, capture)
uses, their resolution priority, and the fully local fallback. Add the
guide to the nav and cross-link it from the cloud deploy note and the
CLI env-var reference.

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

* fix(lint): strip HTML comments in a fixpoint loop (CodeQL)

Single-pass <!-- --> removal can re-form a complete comment from
adjacent markers (e.g. `<<!-- -->!-- ... -->`), letting a decoy
<template> survive and hijack the template-boundary match. Loop to a
fixpoint, mirroring the captions.mjs precedent; add a regression test
that fails on single-pass (2 root findings) and passes on the loop.

Also wrap the build-frame.mjs node:fs imports to satisfy oxfmt — the
new copyFileSync import pushed the line past the width limit, which
was the sole cause of the Format / Preflight CI failures.

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

* fix(lint): strip HTML comments with a linear scan (CodeQL ReDoS)

The fixpoint loop still ran a /<!--[\s\S]*?-->/ regex per pass, which
backtracks O(n^2) on inputs with many unterminated "<!--" — CodeQL
js/polynomial-redos (high). Looping the same regex (the prescribed
fix) never addressed this; only the regex itself does.

Replace it with an indexOf-based linear strip in utils.ts
(stripHtmlComments), kept in a fixpoint loop so markers that re-form
when a comment is removed are still stripped. 200k unterminated
"<!--" now strips in ~3ms instead of quadratic time; behavior is
otherwise unchanged — unterminated comments are kept verbatim, as the
old regex left them. The re-forming regression test still guards it.

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

* fix(auth): make TTS/BGM sign-in guidance accurate and runnable

From team review of the not-signed-in onboarding:

- OAuth is a `hyperframes auth login` feature only. The separate `heygen`
  CLI is API-key-only — `heygen auth login` stores a pasted key, it is not
  OAuth and does not create an account. Stop presenting the two CLIs as the
  same OAuth/sign-up step.
- Use `npx hyperframes` in every imperative and runtime hint. Bare
  `hyperframes` is not on PATH on a fresh machine (command not found); only
  `npx hyperframes` is guaranteed. Also updates the JSON recommended_action.
- Drop `heygen auth login` from the terminal/skill onboarding: it needs its
  own install and there is no `npx heygen`, so it was a command-not-found
  trap. The shared-credential fact stays in the reference docs.

Covers the `auth status` guidance + tests, the Authentication docs, the
shared hyperframes-media preflight (SKILL, requirements, tts, error hints),
and the `npx hyperframes auth status` preflight in every TTS/BGM workflow
(pr-to-video, product-launch-video, faceless-explainer, website-to-video,
music-to-video).

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-24 23:20:39 +08:00
kiritowooandkiritowoo 5242dde2dc feat(telemetry): attribute renders to the authoring workflow skill (#1695)
* feat(telemetry): attribute renders to the authoring workflow skill

Add an optional `--skill` flag to `hyperframes render` and tag the
`render_complete` / `render_error` events with `authoring_skill`, so render
usage can be broken down per authoring workflow. The value is slug-gated (a
malformed value is ignored) and the existing anonymous / opt-out telemetry
pipeline is otherwise unchanged.

Each end-user workflow that renders now passes `--skill=<name>` on its render
command: embedded-captions, faceless-explainer, graphic-overlays,
motion-graphics, music-to-video, pr-to-video, product-launch-video,
remotion-to-hyperframes, website-to-video.

Not instrumented, by design: general-video renders freeform with no canonical
render command to attach to, and slideshow produces an interactive deck rather
than a rendered video. Both can follow up if per-skill numbers are wanted.

* fix(telemetry): address review — shared slug util, equals-form flag, invalid-value warning

- Extract the SKILL_SLUG regex + a normalizeSkillSlug() helper into
  telemetry/skill.ts, shared by the `events` and `render` commands (the regex
  was duplicated). `render` adopts normalizeSkillSlug (so it now trims the value,
  matching `events`); `events` references the shared SKILL_SLUG. + unit test.
- `render` warns on a non-empty but invalid --skill value (e.g. a camelCase
  typo) so attribution isn't silently lost — stderr only, never fails the render.
- embedded-captions render script: `--skill embedded-captions` -> `--skill=embedded-captions`.
  On an older CLI that does not declare --skill, the space form leaks the value
  as a positional and clobbers the project dir (resolveProject fails); the equals
  form is parsed as a self-delimiting flag and safely ignored. Verified via Node
  parseArgs(strict:false).

Addresses review feedback on the PR (shared util + .trim drift, version-skew
safety, invalid-value visibility).

---------

Co-authored-by: kiritowoo <295860553+kiritowoo@users.noreply.github.com>
2026-06-24 06:16:36 -04:00
WaterrrForever 2681085624 Merge pull request #1672 from heygen-com/fix/skill-authoring-fixes
fix(skills): harden music-to-video Step 3 gate, align product-launch-video with pr-to-video
2026-06-24 01:28:54 +08:00
Miao Yang a23ca3487f docs: refine music-to-video planning catalogs 2026-06-23 22:40:48 +08:00
Miao Yang 5d1bff51b5 fix: unblock music video ci checks 2026-06-23 22:32:51 +08:00
Miao YangandClaude Opus 4.8 265b02738e style(skills): apply oxfmt to music-to-video and router docs
Fixes the Format / Preflight CI checks on the new skill files.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 22:04:39 +08:00
Miao YangandClaude Opus 4.8 7a4646894f docs(skills): add music-source brief to music-to-video Step 0
Check for user-supplied audio first; otherwise guide BGM generation
via /hyperframes-media. Note the skill targets fast, high-energy BGM.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 21:48:02 +08:00
Miao YangandClaude Opus 4.8 a34d3dba4d feat(skills): unify bgm-to-video flows into music-to-video
Replace bgm-to-video, bgm-to-video-new, bgm-to-video-refactor, and the
standalone beat-sync/montage skills with a single music-to-video skill.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 21:33:01 +08:00