mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-11 23:00:03 +00:00
feat: scaffold package scripts on init (#576)
## Problem New HyperFrames projects should feel like normal JavaScript projects immediately after `hyperframes init`: users should have a canonical `npm run dev`, `npm run check`, `npm run render`, and `npm run publish` loop without needing to memorize raw CLI commands. At the same time, the scaffold should stay opinionated. Adding many aliases would make the project surface harder to explain and maintain. ## What this fixes - Writes a default `package.json` during `hyperframes init` when the selected example does not already provide one. - Adds four project scripts only: - `dev` -> preview in Studio - `check` -> lint, validate, and inspect in sequence - `render` -> render the video - `publish` -> publish the project - Pins generated scripts to the CLI version that created the project in packaged builds, while keeping source-checkout tests on the unpinned dev fallback. - Uses `npx --yes` inside scripts so first-run commands do not stop on an install confirmation prompt. - Updates generated `AGENTS.md` and `CLAUDE.md` guidance to present the same four-command workflow. - Updates the non-interactive init success message to include `npm run dev`, `npm run check`, and `npm run render`. ## Root cause `scaffoldProject()` copied the example, wrote `meta.json` and `hyperframes.json`, then copied agent guidance files. It never created a package manifest, so generated projects had no project-local command contract even though the workflow has stable repeated commands. This revision keeps the scaffold narrow: `package.json` is the project workflow contract, but direct CLI usage remains available for advanced or one-off commands. ## Verification ### Local checks - TDD red check from the first pass: `bun run --filter @hyperframes/cli test src/commands/init.test.ts` failed after updating the expected generated UX because `npm run check` was not emitted yet. - `bun run --filter @hyperframes/cli test src/commands/init.test.ts` - `bunx oxlint packages/cli/src/commands/init.ts packages/cli/src/commands/init.test.ts` - `bunx oxfmt --check packages/cli/src/commands/init.ts packages/cli/src/commands/init.test.ts packages/cli/src/templates/_shared/AGENTS.md packages/cli/src/templates/_shared/CLAUDE.md` - `bun run --filter @hyperframes/cli typecheck` - `bun run --filter @hyperframes/cli test` - `bun run --filter @hyperframes/cli build` - `bun run --filter @hyperframes/studio build` - `git diff --check` - `node packages/cli/dist/cli.js --version` Generated-project smoke at `/tmp/hf-init-package-scripts-opinionated`: - `node packages/cli/dist/cli.js init /tmp/hf-init-package-scripts-opinionated --example blank --non-interactive --skip-skills` - inspected generated `package.json` and confirmed exactly `dev`, `check`, `render`, and `publish` - confirmed packaged scripts use `npx --yes hyperframes@0.4.39 ...` - `npm run check` - `npm run render -- --quality draft --workers 1 --fps 24 --output /tmp/hf-init-package-scripts-opinionated.mp4` - `ffprobe -v error -select_streams v:0 -show_entries stream=width,height,avg_frame_rate,duration -show_entries format=duration,size -of json /tmp/hf-init-package-scripts-opinionated.mp4` - `npm run publish -- --help` ### Browser verification - Started the generated project through the new script: `npm run dev -- --port 5199`. - Used `agent-browser` to open `http://localhost:5199/#project/hf-init-package-scripts-opinionated`. - Verified the Studio project loaded with the expected project name, controls, timeline, and composition player frame. - Captured screenshot: `/tmp/hf-init-package-scripts-opinionated-browser.png`. - Captured agent-browser-driven recording: `/tmp/hf-init-package-scripts-opinionated-browser.webm`. - Verified recording metadata with `ffprobe`: 14.4s, 61 KB. ## Notes - The generated source-checkout test still expects unpinned `npx --yes hyperframes ...` because source mode reports `0.0.0-dev`; the packaged CLI smoke covers the real-user pinned path. - `npm run publish` was verified with `--help` only to avoid creating a real publish side effect during PR validation.
This commit is contained in:
@@ -32,6 +32,7 @@ import {
|
||||
import { fetchRemoteTemplate } from "../templates/remote.js";
|
||||
import { trackInitTemplate } from "../telemetry/events.js";
|
||||
import { hasFFmpeg } from "../whisper/manager.js";
|
||||
import { VERSION } from "../version.js";
|
||||
|
||||
interface VideoMeta {
|
||||
durationSeconds: number;
|
||||
@@ -168,6 +169,51 @@ function getSharedTemplateDir(): string {
|
||||
return resolveAssetDir(["..", "templates", "_shared"], ["templates", "_shared"]);
|
||||
}
|
||||
|
||||
function toPackageName(projectName: string): string {
|
||||
const normalized = basename(projectName)
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/^[._]+/, "")
|
||||
.replace(/[^a-z0-9._~-]+/g, "-")
|
||||
.replace(/-+/g, "-")
|
||||
.replace(/^[-.]+|[-.]+$/g, "");
|
||||
|
||||
return normalized || "hyperframes-project";
|
||||
}
|
||||
|
||||
function getHyperframesPackageSpecifier(): string {
|
||||
return VERSION === "0.0.0-dev" ? "hyperframes" : `hyperframes@${VERSION}`;
|
||||
}
|
||||
|
||||
function hyperframesScript(command: string): string {
|
||||
return `npx --yes ${getHyperframesPackageSpecifier()} ${command}`;
|
||||
}
|
||||
|
||||
function writeDefaultPackageJson(destDir: string, projectName: string): void {
|
||||
const packageJsonPath = resolve(destDir, "package.json");
|
||||
if (existsSync(packageJsonPath)) return;
|
||||
|
||||
writeFileSync(
|
||||
packageJsonPath,
|
||||
`${JSON.stringify(
|
||||
{
|
||||
name: toPackageName(projectName),
|
||||
private: true,
|
||||
type: "module",
|
||||
scripts: {
|
||||
dev: hyperframesScript("preview"),
|
||||
check: `${hyperframesScript("lint")} && ${hyperframesScript("validate")} && ${hyperframesScript("inspect")}`,
|
||||
render: hyperframesScript("render"),
|
||||
publish: hyperframesScript("publish"),
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
"utf-8",
|
||||
);
|
||||
}
|
||||
|
||||
function patchVideoSrc(
|
||||
dir: string,
|
||||
videoFilename: string | undefined,
|
||||
@@ -346,6 +392,8 @@ async function scaffoldProject(
|
||||
writeProjectConfig(destDir, DEFAULT_PROJECT_CONFIG);
|
||||
}
|
||||
|
||||
writeDefaultPackageJson(destDir, name);
|
||||
|
||||
// Copy shared files (CLAUDE.md, AGENTS.md) for AI agent context
|
||||
const sharedDir = getSharedTemplateDir();
|
||||
if (existsSync(sharedDir)) {
|
||||
@@ -559,10 +607,13 @@ export default defineCommand({
|
||||
console.log(` ${c.dim("More patterns: hyperframes.heygen.com/guides/prompting")}`);
|
||||
console.log();
|
||||
console.log(` ${c.accent("4.")} Preview in the browser:`);
|
||||
console.log(` ${c.accent(`cd ${name}`)} && ${c.accent("npx hyperframes preview")}`);
|
||||
console.log(` ${c.accent(`cd ${name}`)} && ${c.accent("npm run dev")}`);
|
||||
console.log();
|
||||
console.log(` ${c.accent("5.")} Render to MP4 when ready:`);
|
||||
console.log(` ${c.accent(`cd ${name}`)} && ${c.accent("npx hyperframes render")}`);
|
||||
console.log(` ${c.accent("5.")} Check the composition:`);
|
||||
console.log(` ${c.accent(`cd ${name}`)} && ${c.accent("npm run check")}`);
|
||||
console.log();
|
||||
console.log(` ${c.accent("6.")} Render to MP4 when ready:`);
|
||||
console.log(` ${c.accent(`cd ${name}`)} && ${c.accent("npm run render")}`);
|
||||
console.log();
|
||||
console.log(` ${c.dim("Full docs: hyperframes.heygen.com")}`);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user