HyperFrames Video

SkillMedia

Use when the user wants to generate a rendered promotional video (not just a script) for a PR, feature, or product change — produces a HyperFrames HTML composition, iterates with `npx hyperframes preview`, and outputs mp4 + poster for X/LinkedIn/social.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the HyperFrames Video skill

What this skill tells your AI

The instructions your AI receives, as published by alemtuzlak/skills in skills/hyperframes-video/SKILL.md and read by ahel’s review.

Turn a PR into a rendered short-form promo video with a hook, code moments, and CTA, using HyperFrames. Iterate with live preview via npx hyperframes preview, then render video.mp4 and poster.jpg for X/LinkedIn/social.

Unlike /video-script (which produces a textual script), this skill produces the actual video file. Counterpart to the remotion-video skill — same workflow shape, same storytelling rules, but built on the HyperFrames stack instead of Remotion.

Triggers

Invoke when the user says: "make a video for this PR", "hyperframes video", "render a promo video", "video for X/LinkedIn", or when selected from /marketing-pipeline.

How this skill relates to the hyperframes ecosystem

This skill carries only the promo-video-from-PR logic: input resolution, narrative planning, motif derivation, hook enforcement, scene-plan auditing, render-time gates. The mechanics of authoring HyperFrames compositions, running the CLI, animating with GSAP, and installing registry components are owned by separate skills you should invoke when their topic comes up:

  • hyperframes — composition authoring rules: DESIGN.md gate, Layout Before Animation, palettes, transitions, typography, motion principles, captions, audio, TTS.
  • hyperframes-cli — every npx hyperframes <command> (init, lint, inspect, preview, render, transcribe, tts, doctor, browser, info, upgrade, compositions, docs, benchmark).
  • gsap — GSAP timeline patterns, easing, stagger, performance, position parameter, labels, nesting, playback.
  • hyperframes-registry — registry blocks/components and how to wire them via hyperframes add.

When this skill needs to do something covered by one of those skills, defer to that skill's instructions rather than re-deriving them here.

Process Flow

Resolve input
  → Phase 1: Discovery
  → Phase 2: Configuration
  → Phase 3: Narrative planning
  → Phase 4: Scaffold
  → Phase 5: First draft + iterate
  → Phase 6: Render
  → Phase 7: Cleanup

Phases 1, 3, 5, and 7 have explicit approval gates. Phase 2 is an interactive Q&A. Phase 5 is a freeform iteration loop that can run many rounds.

"Use Sane Defaults" / "Don't Ask Questions" — What It Does and Doesn't Override

When the user invokes the skill with phrasing like "use sane defaults", "don't ask questions", "non-interactive", "just ship it", or any equivalent — interpret it precisely:

It DOES override (skip the prompt, pick the default):

  • Q2.1 duration → derive from scope using the ladder in Q2.1 (still in the 15–60s window); pick the midpoint of the matched scope band and proceed without asking
  • Q2.2 aspect ratio → 16:9 landscape
  • Q2.3 project location → marketing/<feature-slug>/hyperframes/
  • Q2.4 brand confirmation (the "use these / customize / provide your own?" question)
  • Phase 1.4 scope confirmation
  • Phase 3.0 motif confirmation
  • Phase 3.1 story-pattern confirmation
  • Phase 3.3 scene-plan approval
  • Phase 5 freeform iteration loop (the "what would you like to change?" prompt)

It does NOT override (must always run regardless):

  • Brand color/font/logo scanning (Q2.4 detection — see HARD-GATE in Q2.4). Hardcoding colors from training-data assumptions about a project is a forbidden shortcut.
  • Phase 5 preview (HARD-GATE in Phase 5). Even in fully unattended mode, npx hyperframes preview must start and the studio URL must be opened in the browser before the render runs. The user can interrupt; the agent must not pre-decide for them.
  • Phase 6 pre-render audits (storytelling, hook rules, motif presence, pacing variance, value-prop timing, contrast). These exist to prevent shipping a generic video.
  • Phase 7 cleanup question (the user owns project disposition).

If you're tempted to skip a HARD-GATE because the user "said no questions" — re-read this section. The user said no questions, not no gates.

Input Resolution

Resolve the argument (if provided) in this order:

  1. Path to a marketing brief (.md containing "Executive Summary" or "Key Messages") → marketing brief
  2. Path to a blog post (.md with blog post structure) → blog post
  3. Path to a changelog → changelog
  4. GitHub PR URL or #\d+ pattern → PR
  5. Matches <ref>..<ref> or <ref>...<ref> (alphanumeric + /, _, ., - on each side) → git ref range
  6. Resolves to an existing file/directory → codebase feature
  7. Otherwise → freeform text

If no argument is provided, ask: "What should the video be about? You can provide a PR URL/number, marketing brief, blog post, changelog, git ref range, file/directory path, or just describe the feature."

When invoked from the pipeline with a PR and upstream marketing-brief/blog-post paths, read both: PR for technical accuracy, upstream content for positioning/tone.

(Detailed phase specs begin below — see Phase 1.)

Phase 1: Discovery

Step 1.1 — Check hyperframes skills availability

Attempt to invoke the hyperframes and hyperframes-cli skills via the Skill tool. If either is unavailable, present:

"The hyperframes / hyperframes-cli skills aren't installed. Options: a) proceed with baseline knowledge (quality may be reduced) b) wait while you install them c) cancel"

If the user picks (a), emit a warning in the final summary noting reduced quality.

Step 1.2 — Analyze input

Input typeWhat to read
Marketing briefpositioning, key messages, audience
Blog postheadline, narrative, examples
Changeloghighest-impact entry
PRgh pr view <n> --json title,body,files,labels, diff (gh pr diff), commit messages, linked issues
Git refsgit diff <range> + git log <range> --oneline
Codebase pathread the specified files/directories
Freeformparse the user's description

For PRs with 20+ files, filter to user-facing changes only — skip tests/, ci/, .github/, lockfile changes, dep bumps.

Error handling:

  • gh not available → tell the user, ask for an alternative (diff file, freeform description)
  • Invalid PR/ref → ask user to verify
  • File not found → ask for the correct path

Step 1.2a — PR deep analysis (when input is a PR)

When the input is a PR, the brief table in Step 1.2 is not enough. PR bodies are routinely vague, outdated, or focused on implementation rather than user value, and a video built off the body alone tends to overclaim or miss the headline angle entirely. Before continuing to Step 1.3, run this structured analysis and produce a written PR analysis block that becomes the source of truth for Steps 1.4 (scope confirmation), Q2.1 (duration ladder), and Phase 3 (narrative planning).

Mandatory steps — do all of them:

  1. Pull metadata and identify the base branch.

    gh pr view <n> --json number,title,body,baseRefName,headRefName,files,labels,commits,additions,deletions
    

    Record baseRefName (usually main / master / develop) — that is the diff reference. gh pr diff <n> automatically compares the PR head against this base.

  2. Pull the full diff against the base.

    gh pr diff <n>
    

    For very large PRs (>500 lines or >20 files), also list changed files via gh pr view <n> --json files.

  3. Filter to user-facing surfaces. Ignore (do not let these shape the headline angle): tests/, __tests__/, *.test.*, *.spec.*, __mocks__/, ci/, .github/, lockfiles, dep bumps without behavior change, generated/build artifacts (dist/, build/, .next/), and formatting-only diffs. What remains is the user-facing surface of the PR.

  4. Enumerate the public-API delta. From the filtered diff, list every change a user could observe or write code against:

    • New, renamed, or removed exports (functions, types, components, hooks, classes, constants)
    • New CLI commands, flags, or environment variables
    • New routes, endpoints, or event names
    • New config keys or schema fields
    • Changed default values for existing public surfaces
    • Changed error messages, log shapes, or response shapes the user could rely on

    Grep the diff for added lines beginning with export , new top-level function/class/const in src/ / lib/ / packages/*/src/, new files in those trees, and changes to public type signatures. If you cannot point at a line in the diff for a claimed API, the claim is wrong — drop it.

  5. Enumerate the behavior delta. Beyond API surfaces, list user-visible behavior changes: UI elements added/changed (with file references), CLI / network output that looks different, side effects firing under new conditions, removed limitations, performance characteristics that changed.

  6. Write the before / after value statement. Exactly two sentences, both grounded in concrete diff evidence:

    • Before: "Before this PR, a user who wanted to ___ had to ___."
    • After: "After this PR, the same user can ___."

    The "had to" half must be a real prior workflow you can describe — copy-pasting an adapter from the docs, installing a second package, writing boilerplate, hitting an error, switching to a different tool. If the honest "Before" sentence is "they could already do this", the PR has no user-visible value delta — see step 8.

  7. Cross-check the PR body against the diff. Walk the body's claims and the diff side-by-side:

    • Body claims a feature → does the diff confirm? If the body says "added X" and the diff has no public surface for X (only tests, only docs, only internal helpers), flag it: "PR body claims X but the diff doesn't expose X to users — should I shift the angle to ?"
    • Diff shows multiple distinct features → body emphasizes one? Flag the others as candidate angles: "The body emphasizes A, but the diff also adds B and C. Which is the headline angle?"
    • Body is empty / boilerplate / [BLANK]? Infer from the diff; make the inferred angle explicit and confirm with the user at Step 1.4.
    • List "surprises" — anything in the diff not mentioned in the body that affects user-visible behavior. Surprises are often the real story.
  8. Bail-out check: is this PR actually user-visible? If after steps 4–6 you cannot name a single new thing the user can do or observe, the PR is not video-worthy as a feature launch. Stop and ask the user:

    "This PR looks like an internal refactor / test-only / dep-bump PR — I can't find a user-visible value delta. Options: a) shift to a performance / DX / cleanup angle if numbers support it b) pick a different PR or input c) cancel"

    Do not invent a feature angle to fill the gap. A confabulated angle wastes a full iteration round and destroys user trust on the very first draft.

  9. Produce the PR analysis block. Write the result as a short structured block before continuing to Step 1.3. This block — not the PR body — is the source of truth for everything downstream:

    PR analysis — #1234 "Add fromZodSchema support"
    Base: main · Head: feature/zod-schema · Author: <login>
    User-facing files: 3 (packages/core/src/index.ts, packages/core/src/zod.ts, packages/core/src/types.ts)
    Filtered out: 5 test files, 2 doc files, lockfile
    
    Public-API delta:
      + export function fromZodSchema(schema: ZodSchema): StandardSchema
      + export type ZodCompatibleSchema
      ~ default error code for ZodError changed: 'invalid_type' → 'STANDARD/type'
    
    Behavior delta:
      - Users importing zod schemas no longer need a manual adapter.
      - Error messages from zod paths now use the StandardError shape.
    
    Before / after:
      Before: copy a 15-line adapter from the docs into every project that mixes zod with this library.
      After:  one import + one call.
    
    Surprises (in diff but not in PR body):
      - Default error shape change (above) — could be the headline angle for migration-aware audiences.
    
    Headline angle: "drop the 15-line adapter — one import, one call"
    Scope band for Q2.1: one idea (15–25s)
    

    The "Headline angle" line feeds Phase 3.0's motif derivation and Phase 3.3's hook copy. The "Scope band" line feeds Q2.1's scope-derived duration proposal.

  10. Forbidden shortcuts (each is a fast path to a wrong video):

    • Writing the analysis block from the PR title alone — fail; you must read the diff.
    • Skipping the cross-check because the body "looks complete" — bodies often look complete and are wrong.
    • Treating the PR body as truth when it conflicts with the diff — the diff wins.
    • Filling in a plausible "Before" sentence when the diff doesn't support one — run the bail-out check instead.
    • Collapsing two genuinely distinct user-visible features into one "feature X" bullet to make the scope band look smaller — record both and pick a headline angle at Step 1.4.

For git-ref-range and codebase-path inputs, apply the same structure with the diff coming from git diff <range> / direct file reads in place of gh pr diff. Steps 4–9 (public-API delta → analysis block) are not PR-specific.

Step 1.3 — Read product context

Read if they exist: README.md, docs/, package.json. If nothing found, ask: "Can you briefly describe the product and who it's for?"

Step 1.4 — Present understanding + get scope confirmation

For PR inputs, the bullets and the compelling angle below must be derived from the PR analysis block written in Step 1.2a — not from the PR title or body. Quote the "Headline angle" line directly as the proposed story seed; turn the public-API delta and the before/after statement into the bullets. If the user added clarifying context after Step 1.2a, fold it in here.

For non-PR inputs, derive the bullets from the relevant entry in Step 1.2 (marketing brief positioning, blog headline, changelog highlight, codebase reading, freeform description).

Present:

"Here's what I'll base the video on:

  • [feature summary bullet 1 — concrete, grounded in the diff / source]
  • [feature summary bullet 2 — the before → after value, in one line]

The compelling angle: [proposed story seed — for PRs, the "Headline angle" from the analysis block]

Anything to add, remove, or correct?"

Do not proceed until the user confirms.

Step 1.5 — Sensitive content scan

Before continuing, scan for: security patches, internal pricing, credentials, unreleased roadmap items, content marked confidential. Flag anything questionable to the user.

Phase 2: Configuration

Ask these questions one at a time, in order.

Q2.1 — Duration (derived from scope, never offered as a menu)

Do not ask the user to pick a fixed length from a menu. (This restriction is specific to duration; other questions like aspect ratio use a numbered menu.) A fixed number becomes a constraint, and the dominant failure mode is padding — freeze-frames, repeated beats, black or empty trailing frames, or filler bullets added solely to reach the chosen target. Instead, derive a duration from the scope of the change and present it as a proposal the user can confirm or override.

Allowed range: 15–60 seconds. Anything outside this window is wrong by default. Going under 15s means the story can't breathe; going over 60s means it's two videos.

Scope → duration ladder:

Scope of the PR / featureDistinct payoff beatsTarget
One idea (single API addition, single bug fix, one QoL win)hook + 1 delivery + CTA15–25s
Typical PR-sized feature (problem → solution → proof, or a multi-chapter code walk)hook + 2–3 delivery + CTA25–40s
Multi-faceted release (multiple distinct sub-features, or comparison needing problem + solution + proof beats)hook + 3–4 delivery + CTA40–60s

A distinct payoff beat is one new thing the viewer learns. Two scenes whose payoff sentences (see Phase 3.3 item 1) reduce to the same idea are one beat, not two.

Sanity check before proposing — do this silently first:

  1. List the distinct payoff beats the video must contain.
  2. Estimate: hook ≈ 3s, CTA ≈ 5–7s, each delivery beat ≈ 6–10s (longer if it contains a code chapter ladder).
  3. Sum the floor and the ceiling. If the floor is under 15s, you're padding the beat list — cut beats or shrink the scope claim. If the ceiling is over 60s, you have two videos — pick one angle.

Propose to the user (no menu, no fixed lengths):

"Based on the scope of this PR, I'm targeting ~Xs (range Ys–Zs). Beats: . Confirm, or override with a different length anywhere in 15–60s."

Hard rule — story sets duration, never the other way around. If at any later phase a scene needs to be stretched, held on a static frame, repeated, backed by black/empty frames, or filled with filler bullets to reach the chosen target — stop, shorten the target, re-confirm with the user, and ship the shorter video. Padding to hit a number is the single behavior this rule exists to forbid. If a 30s scope honestly tells in 18s, ship 18s.

Breathing-room constraint. Whatever duration is chosen, it must allow every on-screen text element to (a) finish animating in, (b) dwell long enough to be read, and (c) settle for at least ~0.4s before the next scene begins. Cutting to a new scene the instant a line of text finishes appearing is forbidden. See Phase 3.3 (item 3) and the Phase 6 audit for the concrete dwell-time table.

Q2.2 — Aspect ratio

"Which aspect ratio?

  1. 16:9 landscape (1920×1080, default) — desktop X/LinkedIn
  2. 1:1 square (1080×1080) — mobile-friendly feed
  3. 9:16 vertical (1080×1920) — Reels/Shorts/TikTok
  4. Multi-format — render all three from the same story"

Frame rate is fixed at 30fps.

Concrete rules for every 1:1 scene (and apply the same spirit to 9:16):

  • ≤10–20% total dead margin — AND that margin must be evenly distributed. At each scene's hero frame the foreground content's bounding box must reach within ~8–12% of every canvas edge. But the edge-margin target alone is necessary, NOT sufficient: it says nothing about the gaps between blocks. A layout can pass a 10% edge check and still look broken if the air is dumped in the middle.
  • Distribute with space-evenly + gap: 0, not center or space-between. This is the load-bearing rule the edge-margin check misses. The spacing above the first block, between every adjacent pair of blocks, and below the last block must all be equal — like even leading on a column of text. Concretely:
    • justify-content: center (or flex-start with a fixed gap) clusters the blocks in the middle and leaves a big dead band at top and bottom — the classic top-cluster failure.
    • justify-content: space-between does the opposite: it flushes the first and last block hard against the edges (zero edge breathing room) and dumps all the free space into one giant middle gap. If content is 10% of the height with 10% edge margins, the middle gap becomes ~40% — visibly wrong even though the edges "pass."
    • justify-content: space-evenly with gap: 0 is the fix: the n+1 spaces (top, each inter-block, bottom) are all equal. The eye reads even rhythm as "designed"; a 10/40/10 distribution reads as "broken."
    • Do not combine a large fixed gap with space-evenly — the gap adds a floor that makes inter-block spacing exceed edge spacing, reintroducing the imbalance. Set gap: 0 (or a tiny value) and let space-evenly own all vertical spacing. Padding sets the outer frame inset; space-evenly distributes inside it.
  • Scale everything up vs the landscape defaults. On 1080×1080 the usable width (~940px after padding) is half of landscape's, so per-element sizes go up: hero/headline text at or above the landscape size, body/captions +20–40%, pills/cards/code-hub fonts noticeably larger. If a 1:1 looks like a shrunken 16:9, the sizes are wrong.
  • Verify it. Run npx hyperframes inspect AND eyeball the hero frame of every scene: squint — does the content own the frame with even rhythm, or float in a sea of black / cluster with one fat gap? If the spacing is uneven, switch the scene to justify-content: space-evenly; gap: 0 and enlarge elements until the dead margin is ≤10–20% and evenly distributed.

This is enforced at scene-plan time (Phase 3.3) and as a pre-render audit (Phase 6.1) — it is not optional for square output.

Q2.3 — HyperFrames project location

"Where should the HyperFrames project live? Default: marketing/<feature-slug>/hyperframes/ (fresh per-video) Override: specify a path."

Q2.4 — Brand assets (auto-detect → confirm)

You MUST run the heuristics in brand-detection.md against the actual target repository — the source code that owns the feature, not the marketing/ output directory — before selecting any color, font, or logo.

Forbidden shortcuts (these produce wrong colors and waste an iteration):

  • "I know this project — TanStack uses amber, Vercel uses black/white, Stripe uses purple" → No. Run the scan. Recall is unreliable; brand details drift between training data and now.
  • "It's a dev tool, dark + neon green is fine" → No. Generic vibes ≠ this product's brand.
  • "User said no questions, so I'll skip detection" → No. Detection is silent. Confirmation is what the "no questions" instruction skips.
  • Picking from a palette in your head because it "fits the topic" → No. Read the repo's CSS/Tailwind/theme files.

The scan must produce a written record before any composition file is written. Output a short block listing, for each field: the source file checked, the value found (or not found), and the final value used. Example:

Brand scan — TanStack/ai
  Primary  : checked tailwind.config.* (none) · packages/*/styles.css (--brand: #0a3d2e) → #0a3d2e
  Accent   : checked theme.json (none) · brand.json (none) · derived from primary → #14b870
  Logo     : checked public/logo.svg → media/header_ai.png
  Font     : checked next/font (none) · @fontsource (none) · README ref → Inter (fallback)

If the scan finds nothing for a field, fall through to the neutral defaults below — but only after the scan ran and is recorded. Skipping the scan and going straight to defaults is the failure mode this gate exists to prevent.

In interactive mode, present findings (the block above) and ask for confirmation. In non-interactive / "use sane defaults" mode, print the same block and proceed without asking — the record is required either way.

Present findings as:

"I found:

  • Logo: media/header_ai.png (copied from public/logo.svg)
  • Primary: #0066ff (from tailwind.config.js)
  • Font: Inter (from next/font)

Use these, customize some, or provide your own?"

Persistence: write chosen brand to .marketing/brand.json (relative to repo root) and to a generated DESIGN.md inside the project (the file the hyperframes skill's Visual Identity Gate requires). On subsequent runs, ask:

"I loaded brand settings from .marketing/brand.json. Use saved, or re-detect?"

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
39
Forks
1
Last commit
Aug 2026
Advanced
Catalog kind
skill
Gateway key
hyperframes-video
Source
github.com/alemtuzlak/skills