Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ This writes `dist/hyperframes-plugin.zip` with a `hyperframes/` root folder and
| Skill | Use when |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/hyperframes` | **Read first** for any request to make, edit, animate or render a video, motion graphic, captions or overlays with HyperFrames (HTML rendered to video). Holds the render contract and the work loop, and points to the references for format, motion, look, captions, whole films and storyboards. |
| `/hyperframes-cli` | 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. |
| `/hyperframes-cli` | The HyperFrames CLI by job — scaffold, check, look at frames, edit the timeline, preview, render locally or in the cloud, share, project history — plus reading its JSON and recovering from build or render failures. |
| `/hyperframes-registry` | About 450 ready-made blocks and components installed as editable source — transitions, overlays, titles, kinetic type, captions, charts, maps, 3D, camera moves. Use before hand-building any named visual; also for `hyperframes add` and `catalog`. |
| `/media-use` | Get or make any media for a video — music, sound effects, images, icons, logos, voiceover, avatar and generated video, transcripts, cutouts, colour grades and looks — with HeyGen or free local tools, and mix placed audio. Also for footage that looks dark, flat or dull. |

Expand Down Expand Up @@ -360,3 +360,11 @@ GIT_LFS_SKIP_SMUDGE=1 git clone https://github.com/heygen-com/hyperframes.git
## License

[Apache 2.0](LICENSE)

### Share a video for review

Sign in with `npx hyperframes auth login`, then run `npx hyperframes share` to
choose who can open the rendered review. Use `share --update` when edits should
become a new version and `share --manage` for web settings. CLI and Desktop use
the same sharing system and five free projects per workspace. Existing legacy
published links remain reachable.
7 changes: 7 additions & 0 deletions docs/packages/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,13 @@ editor, just playback. Same flags, `--port` defaults to 3003, plus `--proxy` /

### `publish`

For new video reviews, use [`share`](#share). Legacy source publishing stays
available with a notice for 60 days from an announced notice release. That
release date has not been scheduled. After the window, new `publish` runs guide
you to `share`, where you choose a new audience; old flags are never silently
translated. Existing published URLs and legacy updates remain supported under
a separate retirement plan.

Upload the project and get back a stable `hyperframes.studio` URL.

```bash
Expand Down
4 changes: 3 additions & 1 deletion packages/cli/src/commands/publish.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { checkPublishTransition } from "../share/publishTransition.js";
import { join, posix, relative, resolve } from "node:path";
import { failCommand, setCommandExitCode } from "../utils/commandResult.js";
import { existsSync } from "node:fs";
Expand Down Expand Up @@ -60,7 +61,7 @@ export function parseUpdateTarget(value: string): string {
export default defineCommand({
meta: {
name: "publish",
description: "Upload the project to a stable URL (private by default)",
description: "Legacy source publishing; use share for new video reviews",
},
args: {
dir: { type: "positional", description: "Project directory", required: false },
Expand Down Expand Up @@ -95,6 +96,7 @@ export default defineCommand({
},
},
async run({ args }) {
checkPublishTransition(args);
if (args.yes !== true && isUnattended()) {
clack.confirmationRequired("hyperframes publish", "Pass --yes to publish without asking.");
}
Expand Down
23 changes: 23 additions & 0 deletions packages/cli/src/share/publishTransition.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { describe, expect, it } from "vitest";
import { publishPhase, publishTransitionDecision } from "./publishTransition.js";
describe("announced 60-day publish transition", () => {
const start = "2027-01-01T00:00:00.000Z";
const day = 86_400_000;
it("never starts the clock from approval or installation", () => {
expect(publishPhase(null, Date.parse(start) + 365 * day)).toBe("unscheduled");
expect(publishPhase(start, Date.parse(start) - 1)).toBe("unscheduled");
});
it("preserves exactly sixty days and guides only after the boundary", () => {
expect(publishPhase(start, Date.parse(start))).toBe("compatibility");
expect(publishPhase(start, Date.parse(start) + 60 * day - 1)).toBe("compatibility");
expect(publishPhase(start, Date.parse(start) + 60 * day)).toBe("share");
});
it("retains legacy updates after the new-project transition", () => {
expect(publishTransitionDecision("share", true)).toBe("keep_legacy");
expect(publishTransitionDecision("share", false)).toBe("guide_to_share");
expect(publishTransitionDecision("compatibility", false)).toBe("keep_legacy");
});
it("fails on an invalid scheduled date rather than silently switching behavior", () => {
expect(() => publishPhase("invalid")).toThrow("timestamp");
});
});
63 changes: 63 additions & 0 deletions packages/cli/src/share/publishTransition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { resolve } from "node:path";
import { CliUsageError } from "../utils/commandResult.js";
import { asNotice } from "../utils/jsonError.js";
import { readProjectLink, readTeamProject } from "../utils/projectLink.js";

/** Set to the announced notice release's UTC timestamp in the release PR, after launch gates pass. */
const PUBLISH_NOTICE_RELEASE: string | null = null;
const PUBLISH_COMPATIBILITY_DAYS = 60;

export function publishPhase(
start: string | null,
now: number = Date.now(),
): "unscheduled" | "compatibility" | "share" {
if (!start) return "unscheduled";
const begins = Date.parse(start);
if (!Number.isFinite(begins)) throw new Error("Invalid publish transition release timestamp.");
if (now < begins) return "unscheduled";
return now < begins + PUBLISH_COMPATIBILITY_DAYS * 86_400_000 ? "compatibility" : "share";
}
export function publishTransitionDecision(
phase: ReturnType<typeof publishPhase>,
legacyUpdate: boolean,
) {
if (phase === "share" && !legacyUpdate) return "guide_to_share";
return "keep_legacy";
}
export function checkPublishTransition(args: { dir?: string; update?: string }): void {
const dir = resolve(args.dir ?? ".");
const prior = readProjectLink(dir);
const legacyUpdate = Boolean(
args.update?.trim() || prior?.url || readTeamProject(dir)?.projectId,
);
const phase = publishPhase(PUBLISH_NOTICE_RELEASE);
asNotice(() => process.stderr.write(publishNotice(phase, legacyUpdate) + "\n"));
if (publishTransitionDecision(phase, legacyUpdate) === "guide_to_share") {
throw new CliUsageError(
"New projects now use hyperframes share. Run it from this project folder and choose an audience; for scripts pass --public, --team, --invite-only or --private explicitly.",
{
code: "publish_replaced",
hint: "Existing publications remain reachable. Use publish --update <url|id> for a legacy update. --yes and legacy --public do not convert a project or consent to a new review audience.",
},
);
}
}
function publishNotice(phase: ReturnType<typeof publishPhase>, update: boolean): string {
const kept = update
? " This run keeps the existing publication."
: " This run keeps legacy publishing behavior.";
if (phase === "unscheduled")
return (
"For new video reviews, use hyperframes share. The legacy publish transition date has not been scheduled." +
kept
);
if (phase === "compatibility")
return (
"Legacy publish is in its 60-day compatibility window. Use hyperframes share for new video reviews." +
kept
);
return (
"New projects use hyperframes share. Existing published URLs and explicit legacy updates remain supported." +
(update ? kept : "")
);
}
2 changes: 1 addition & 1 deletion skills-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"files": 77
},
"hyperframes-cli": {
"hash": "aeb9494dee6777a7",
"hash": "9414b94dd04c54c9",
"files": 13
},
"hyperframes-registry": {
Expand Down
6 changes: 3 additions & 3 deletions skills/hyperframes-cli/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
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.
description: HyperFrames CLI - scaffold, check frames, edit timelines, preview, render, share and project history; read JSON and recover from 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.
Expand All @@ -20,7 +20,7 @@ Run `npx hyperframes <command>` unless the project gives a wrapper; then use the
| Edit the timeline | `timeline` with `move`, `trim`, `split`, `set`, `duplicate`, `delete`, `apply`, `undo`, `ids` |
| Reuse | `catalog`, `add` (`/hyperframes-registry`) |
| Media and sound | `media-treatment`, `grade-compare`, `remove-background`, `tts`, `transcribe`, `beats`, `normalize-audio` (`/media-use`) |
| Show and ship | `preview`, `present` (`/slideshow`), `render`, `publish`, `cloud`, `lambda`, `cloudrun` |
| Show and ship | `preview`, `present` (`/slideshow`), `render`, `share`, `publish` (legacy), `cloud`, `lambda`, `cloudrun` |
| Desktop app | `open`, `catch-up` |
| Upkeep | `doctor`, `browser`, `upgrade`, `skills`, `usage`, `clean`, `feedback` |

Expand Down Expand Up @@ -88,7 +88,7 @@ Read the row before running its command.
| `lint`, `check`, motion sidecars, `snapshot` | `references/lint-validate-inspect.md` |
| `compare`, `grade-compare`, `render --batch` | `references/compare-and-batch.md` |
| `beats` | `references/beats.md` |
| `preview`, `play`, `render`, `publish`, Studio context | `references/preview-render.md` |
| `preview`, `play`, `render`, `share`, `publish` (legacy), Studio context | `references/preview-render.md` |
| `doctor`, `browser` | `references/doctor-browser.md` |
| `auth`, `cloud`, template variables | `references/cloud.md` |
| `lambda`, `cloudrun` | `references/lambda.md`, `references/cloudrun.md` |
Expand Down
19 changes: 19 additions & 0 deletions skills/hyperframes-cli/references/preview-render.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,22 @@ Uploads the project's source (HTML + assets) and returns a stable hosted URL tha
Use cloud rendering when the user wants hosted rendering without local Chrome, FFmpeg, or AWS, and for the final render on a paid HyperFrames plan (`cloud.md`). Use Lambda only when AWS ownership is a requirement. Use Cloud Run only when GCP ownership is a requirement. Read the matching reference before running any cloud path.

Skill attribution is automatic — the examples above need no `--skill`. A project scaffolded with `hyperframes init --skill=<slug>` records its owning skill in `hyperframes.json`, and every later render inherits it on usage telemetry: re-renders, `npm run render`, and `--batch` alike. Pass `--skill=<slug>` explicitly only to stamp a project that was not scaffolded with one (its first render then persists it).

## Share a video for review

Use `hyperframes share --public` only when the user explicitly wants anyone with
the link to access the review. First sign in with `hyperframes auth login`.
Other first-share choices are `--team`, `--invite-only` and `--private`, subject
to workspace entitlement. `--yes` is not audience consent.

Read `hyperframes share --status --json`: `ready: true` is required before
calling the video ready to watch. Re-running `share` retrieves the existing
link; `share --update` explicitly renders/uploads changed content. Five retained
free projects are counted per workspace across CLI and Desktop; versions do
not consume slots and turning a link off does not release a slot.

`publish` uploads source to legacy `/p/` pages. It is not a review share alias.
During the announced 60-day window its old behavior stays available with a
notice. The start date is release-owned and currently unscheduled. After it, new
projects are guided to Share; existing publications and legacy updates stay
reachable. Never convert legacy IDs or carry a legacy flag into review consent.
Loading