diff --git a/docs/guides/troubleshooting.mdx b/docs/guides/troubleshooting.mdx index 28761ee9f92..5f4957a6a40 100644 --- a/docs/guides/troubleshooting.mdx +++ b/docs/guides/troubleshooting.mdx @@ -72,6 +72,18 @@ sudo apt install ffmpeg Verify with `ffmpeg -version`, then rerun `npx hyperframes doctor`. +If you already have executable FFmpeg and FFprobe binaries, you can use them +without a package manager: + +```bash +export HYPERFRAMES_FFMPEG_PATH="/absolute/path/to/ffmpeg" +export HYPERFRAMES_FFPROBE_PATH="/absolute/path/to/ffprobe" +npx hyperframes doctor --json +``` + +Check that both entries report `ok: true` and the expected paths. The command +always exits zero; its top-level `ok` also includes optional tools. + ### Docker render will not start Run `docker info`. diff --git a/packages/cli/src/browser/ffmpeg.test.ts b/packages/cli/src/browser/ffmpeg.test.ts index 4116504f2a3..40a717964bb 100644 --- a/packages/cli/src/browser/ffmpeg.test.ts +++ b/packages/cli/src/browser/ffmpeg.test.ts @@ -133,12 +133,17 @@ describe("getFFmpegInstallCommand / getFFmpegInstallHint", () => { setPlatform(realPlatform); }); - it("gives macOS a pasteable command and uses it verbatim as the hint", async () => { + it("gives macOS a pasteable command and a setup route without Homebrew", async () => { setPlatform("darwin"); const { getFFmpegInstallCommand, getFFmpegInstallHint } = await import("./ffmpeg.js"); expect(getFFmpegInstallCommand()).toBe("brew install ffmpeg"); - expect(getFFmpegInstallHint()).toBe("brew install ffmpeg"); + const hint = getFFmpegInstallHint(); + expect(hint).toContain("brew install ffmpeg"); + expect(hint).toContain("https://ffmpeg.org/download.html"); + expect(hint).toContain("HYPERFRAMES_FFMPEG_PATH"); + expect(hint).toContain("HYPERFRAMES_FFPROBE_PATH"); + expect(hint).toContain(".hyperframes/bin/"); }); it("gives Windows a winget command and keeps the manual route in the hint", async () => { diff --git a/packages/cli/src/browser/ffmpeg.ts b/packages/cli/src/browser/ffmpeg.ts index bfa96f42015..fe8ba98dda3 100644 --- a/packages/cli/src/browser/ffmpeg.ts +++ b/packages/cli/src/browser/ffmpeg.ts @@ -103,6 +103,9 @@ export function getFFmpegInstallHint(): string { if (command && process.platform === "win32") { return `${command}, or download the 64-bit build from ${FFMPEG_DOWNLOAD_URL}#build-windows and add its bin/ directory to PATH.`; } + if (command && process.platform === "darwin") { + return `${command}, or download FFmpeg and FFprobe from ${FFMPEG_DOWNLOAD_URL}, then set HYPERFRAMES_FFMPEG_PATH and HYPERFRAMES_FFPROBE_PATH to the executable paths or place both binaries in .hyperframes/bin/ in your project.`; + } if (command) return command; if (process.platform === "linux") return ffmpegInstallCommand("unknown"); return FFMPEG_DOWNLOAD_URL; diff --git a/packages/cli/src/browser/preflight.test.ts b/packages/cli/src/browser/preflight.test.ts index 6c63a30de4a..7028cd2df4a 100644 --- a/packages/cli/src/browser/preflight.test.ts +++ b/packages/cli/src/browser/preflight.test.ts @@ -174,7 +174,6 @@ describe("runEnvironmentChecks", () => { }); it.each([ - { platform: "darwin" as const, expectedHint: "brew install ffmpeg" }, { platform: "sunos" as const, expectedHint: "https://ffmpeg.org/download.html" }, { platform: "win32" as const, @@ -194,6 +193,25 @@ describe("runEnvironmentChecks", () => { expect(ffmpeg?.title).toBe("FFmpeg cannot start"); expect(ffmpeg?.hint).toBe(expectedHint); }); + + it("offers configured binaries on macOS when FFmpeg cannot launch", async () => { + Object.defineProperty(process, "platform", { value: "darwin", configurable: true }); + runProcess.mockImplementation((binaryPath: string) => { + if (binaryPath !== process.env.HYPERFRAMES_FFMPEG_PATH) + return Promise.resolve({ stdout: "ffprobe version 7.1.1\n", stderr: "" }); + throw Object.assign(new Error("cannot execute binary file"), { status: 126 }); + }); + + const result = await runEnvironmentChecks(); + const ffmpeg = result.outcomes.find((outcome) => outcome.name === "FFmpeg"); + + expect(ffmpeg).toMatchObject({ ok: false, title: "FFmpeg cannot start" }); + expect(ffmpeg?.hint).toContain("brew install ffmpeg"); + expect(ffmpeg?.hint).toContain("HYPERFRAMES_FFMPEG_PATH"); + expect(ffmpeg?.hint).toContain("HYPERFRAMES_FFPROBE_PATH"); + expect(ffmpeg?.hint).toContain(".hyperframes/bin/"); + expect(result.ffmpegPath).toBeUndefined(); + }); }); it("validates an explicit browser path without needing browser discovery", async () => { diff --git a/skills-manifest.json b/skills-manifest.json index a1564a8b5ff..175d2591ebd 100644 --- a/skills-manifest.json +++ b/skills-manifest.json @@ -6,11 +6,11 @@ "files": 2 }, "hyperframes": { - "hash": "76c914f47480032c", + "hash": "bde191d6992a5da0", "files": 77 }, "hyperframes-cli": { - "hash": "d82c156c1b2069d1", + "hash": "7d2aef84a47a17ea", "files": 13 }, "hyperframes-registry": { diff --git a/skills/hyperframes-cli/SKILL.md b/skills/hyperframes-cli/SKILL.md index 7f51bc528ab..c457cb2a1bb 100644 --- a/skills/hyperframes-cli/SKILL.md +++ b/skills/hyperframes-cli/SKILL.md @@ -3,11 +3,11 @@ name: hyperframes-cli description: The HyperFrames CLI by job - scaffold, check, look at frames, edit the timeline, preview, render locally or in the cloud, publish, project history - plus reading its JSON and recovering from build or render failures. --- -**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. +**Plugin installs:** Before setup or refresh, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) inside a HyperFrames plugin. # HyperFrames CLI -Run `npx hyperframes ` unless the project gives a wrapper; then use the wrapper. It needs Node.js 22+ and FFmpeg. Every command has `--help`; most take `--json`, and a failure prints `✗ …`, a `Fix:` line and exits non-zero. +Run the project's wrapper, or `npx hyperframes ` if none. Needs Node.js 22+ and FFmpeg. All commands have `--help`; most take `--json`. Failures print `✗ …`, a `Fix:` line and exit non-zero. ## Commands by job @@ -28,6 +28,8 @@ Run `npx hyperframes ` unless the project gives a wrapper; then use the ## The loop +Before planning or designing a local video: `references/doctor-browser.md`. + 1. Write, then `npx hyperframes lint` after the first pass and after structural changes. 2. `npx hyperframes check --json` and fix every error. While a lint error remains the layout and contrast audits don't run; "0 samples" means nothing was checked. Contrast failures carry a `suggestedColor`. A finding at a single sample (an entrance in flight) is info; a 3 s+ video with no visible change fails `sweep_static`. 3. `npx hyperframes snapshot` and read the PNGs (under `.hyperframes/artifacts/snapshots/`): that is how you see the video. With sub-compositions, snapshot a midpoint of every scene; tiny unstyled content or a missing hero is a mount defect (`../hyperframes/references/format.md`). @@ -71,7 +73,7 @@ After a verified render, send one `feedback --rating`, plus a report for anythin | Symptom | Fix | | --- | --- | -| Chrome missing or won't start | `npx hyperframes browser ensure`; `doctor --json` always exits 0, gate on `.ok` | +| Chrome missing or won't start | `npx hyperframes browser ensure`; `doctor --json` exits 0; `.ok` includes optional tools | | A heavy page isn't ready in time | Raise `--timeout` on `check` or `snapshot` | | Unusual codec looks blank | Keep `--proxy` on (the default) | | Render slow on Linux | The summary's second line says `screenshot` + `software gpu`: the slow path | diff --git a/skills/hyperframes-cli/references/doctor-browser.md b/skills/hyperframes-cli/references/doctor-browser.md index 7b01899eea7..1a3208ddd87 100644 --- a/skills/hyperframes-cli/references/doctor-browser.md +++ b/skills/hyperframes-cli/references/doctor-browser.md @@ -6,7 +6,7 @@ Environment diagnosis and bundled-Chrome management. Run these first when a rend ```bash npx hyperframes doctor -npx hyperframes doctor --json # CI / agent output (always exit 0; gate on payload `ok`) +npx hyperframes doctor --json # CI / agent output (always exit 0; inspect checks in payload) ``` Runs independent checks and reports each as ok/warn/fail: @@ -26,6 +26,14 @@ Run `doctor` first when: - `preview` opens but the composition fails to load. - A fresh machine has never run HyperFrames. +### Check local video dependencies before design + +For a workflow that will render a video locally, run `npx hyperframes doctor --json` from the project directory immediately after setup or resume, before planning, sourcing, or design. Keep this result for the current run; repeat after changing the binary configuration or environment. + +Require both **FFmpeg** and **FFprobe** entries with `ok: true`, and inspect their reported paths. Use [the two-tool jq gate](#configure-ffmpeg-without-a-package-manager) when `jq` is available, or inspect the JSON directly. `doctor --json` exits 0 even when dependencies are missing, and its top-level `ok` includes optional tools. + +When either binary is missing or cannot run, surface the affected check and use [the supported setup paths](#configure-ffmpeg-without-a-package-manager) before design starts. Verify the two checks again after setup. If installation is unavailable or needs authorization, report the dependency and agree on how to continue before starting design; retain the user's choice of local or cloud rendering. Check Chrome separately before browser validation and rendering. Deck-only and source-port tasks follow their own prerequisites. + Common issues: - **Missing FFmpeg** — install via `brew install ffmpeg` (macOS) or your package manager, or [configure existing binaries](#configure-ffmpeg-without-a-package-manager). diff --git a/skills/hyperframes/SKILL.md b/skills/hyperframes/SKILL.md index e0a01d0f3ae..f0147d66d76 100644 --- a/skills/hyperframes/SKILL.md +++ b/skills/hyperframes/SKILL.md @@ -3,13 +3,13 @@ name: hyperframes description: Read first to make, edit, animate or render any video with HyperFrames (HTML rendered to video), including launch films, explainers, music videos, charts, maps, vertical clips, title cards, captions and overlays. Holds the render contract, the work loop and what each kind of video needs. --- -**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](references/plugin-installation.md) when this skill is inside a HyperFrames plugin. +**Plugin installs:** Before setup or refresh, follow [plugin execution rules](references/plugin-installation.md) inside a HyperFrames plugin. # HyperFrames -HyperFrames renders HTML to video by seeking a paused timeline frame by frame, so every render is identical; the person edits the same files in the Studio preview. Use it unless another framework or a plain browser recording is asked for. Run the CLI as `npx hyperframes ` (`/hyperframes-cli`); at the start of a new video, `npx hyperframes usage --json` reports the person's remaining plan usage (say "unknown" when it is). +HyperFrames seeks a paused HTML timeline frame by frame for deterministic video; the person edits the same files in the Studio preview. Use it unless another framework or plain browser recording is requested. Run `npx hyperframes ` (`/hyperframes-cli`); for a new video, `npx hyperframes usage --json` reports remaining plan usage (say "unknown" when it is). -`index.html` is the root composition: it declares the video's variables and mounts one scene per sub-composition from `compositions/`. Media and fonts live in `assets/`. A new project: `npx hyperframes init `. A website as the subject: `npx hyperframes capture --json`. +`index.html` is the root composition: it declares the video's variables and mounts one scene per sub-composition from `compositions/`. Media and fonts live in `assets/`. Start with `npx hyperframes init `; for a website, `npx hyperframes capture --json`. Build what was asked: no voiceover or captions and no new aspect ratio unless the request or the source calls for them; a film over ~15 s that tells something gets music and sound on its visible actions unless the person asked for silence (`references/sound.md`). A new video is 1920×1080 at 30 fps. A question, a hold or a loose idea gets an answer or a proposal, not an edit. Before hand-writing a named effect, transition, caption style, chart or card, search the registry (`/hyperframes-registry`). Any media (a presenter or avatar, footage, voice, music): `/media-use`, which says when HeyGen is needed and what is free. Show real things from their source (the product's screens, the brand's logo, a real place's imagery); draw only what has none. @@ -42,6 +42,7 @@ Studio edits the tags you write. ## Work loop +- [Local video prerequisites](../hyperframes-cli/references/doctor-browser.md). - An existing project: `npx hyperframes timeline --json` shows what plays when; before any change, `npx hyperframes history --since mine --who ` shows what the person changed since your last turn: build on it (`/hyperframes-cli`). - A new film over ~15 s that tells something: its type's file (below), then `references/story.md`: three storylines that are three different films, in words only; the person picks unless they asked for the file or no stops. Then `SPEC.md` (`references/spec.md`): every adjective as a number, the defaults banned by name, the shots by frame. - After your last edit: `npx hyperframes check --json`; fix every error, weigh every warning. A film: `npx hyperframes snapshot --review` and `references/review.md`, ending in `REVIEW.md`. A small edit: snapshot what you changed (`--at`, `--range`, `--composition`) and look before you reply.