mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-06 01:52:05 +00:00
Capture pipeline:
- agentPromptGenerator now writes AGENTS.md + CLAUDE.md (drop legacy
.cursorrules), matching the dual-file convention already used by the
_shared templates in hyperframes init. AGENTS.md is picked up natively by
Cursor, Codex, Gemini CLI, Windsurf, Aider, and Jules; CLAUDE.md covers
Claude Code. Both files share the same content — a capture data inventory
that points agents to the website-to-hyperframes skill.
website-to-hyperframes skill refinements (derived from 8-site regression test):
- Drop slash-command phrasing throughout SKILL.md and step-6-build.md so the
skill works identically across Claude Code (slash), Cursor (auto-discover
by description), and other agents.
- Remove stale HANDOFF.md references from SKILL.md step-7 summary and
reference table — matches the intent of the prior step-7 cleanup.
- step-5-vo: specify narration.txt filename convention (pronunciation-
substituted spoken text; distinct from SCRIPT.md the creative doc).
- step-6 self-review adds three rules derived from actual lint warnings
observed across the 8 regression runs:
- Every <template> root needs data-start + data-duration (catches
root_composition_missing_data_start/duration, seen in 4/8 runs).
- Caption exits need a hard tl.set kill after tl.to(opacity:0), or
per-word karaoke tweens can leave captions stuck on screen
(caption_exit_missing_hard_kill).
- No duplicate media nodes with identical src + start + duration, or
the compiler discovers them twice (duplicate_media_discovery_risk).
Housekeeping:
- .gitignore: add cursor-tests/, basecamp-video/, projects/, videos/ —
local regression-test scratch dirs that should never be committed.
- Remove two broken symlinks from .claude/skills/ that pointed to paths
which never existed in the repo (.claude/skills/ is already gitignored).
Made-with: Cursor
100 lines
3.2 KiB
TypeScript
100 lines
3.2 KiB
TypeScript
/**
|
|
* Project scaffolding helpers for the website capture pipeline.
|
|
*
|
|
* Handles .env file loading and HyperFrames project scaffold generation
|
|
* (index.html, meta.json, AGENTS.md, CLAUDE.md).
|
|
*/
|
|
|
|
import { existsSync, writeFileSync, readFileSync } from "node:fs";
|
|
import { join, resolve } from "node:path";
|
|
import type { CatalogedAsset } from "./assetCataloger.js";
|
|
import type { CaptureResult, DesignTokens } from "./types.js";
|
|
|
|
/**
|
|
* Load .env file by walking up from startDir (up to 5 levels).
|
|
* Sets process.env keys that are not already set. Best-effort — never throws.
|
|
*/
|
|
export function loadEnvFile(startDir: string): void {
|
|
try {
|
|
let dir = resolve(startDir);
|
|
for (let i = 0; i < 5; i++) {
|
|
const envPath = resolve(dir, ".env");
|
|
try {
|
|
const envContent = readFileSync(envPath, "utf-8");
|
|
for (const line of envContent.split("\n")) {
|
|
const trimmed = line.trim();
|
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
const eq = trimmed.indexOf("=");
|
|
if (eq === -1) continue;
|
|
const key = trimmed.slice(0, eq).trim();
|
|
const val = trimmed
|
|
.slice(eq + 1)
|
|
.trim()
|
|
.replace(/^["']|["']$/g, "");
|
|
if (!process.env[key]) process.env[key] = val;
|
|
}
|
|
break;
|
|
} catch {
|
|
dir = resolve(dir, "..");
|
|
}
|
|
}
|
|
} catch {
|
|
/* .env loading is best-effort */
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate the project scaffold files: index.html, meta.json, AGENTS.md, CLAUDE.md.
|
|
*
|
|
* Only creates files that don't already exist (index.html, meta.json).
|
|
* Always (re)generates AGENTS.md + CLAUDE.md via agentPromptGenerator.
|
|
*/
|
|
export async function generateProjectScaffold(
|
|
outputDir: string,
|
|
url: string,
|
|
tokens: DesignTokens,
|
|
animationCatalog: CaptureResult["animationCatalog"],
|
|
hasScreenshots: boolean,
|
|
hasLotties: boolean,
|
|
hasShaders: boolean,
|
|
catalogedAssets: CatalogedAsset[],
|
|
progress: (stage: string, detail?: string) => void,
|
|
warnings: string[],
|
|
detectedLibraries?: string[],
|
|
): Promise<void> {
|
|
// Capture output is a DATA folder, not a video project.
|
|
// The agent builds index.html + compositions/ during step 6.
|
|
// We only write meta.json (project metadata) — NOT index.html.
|
|
// Writing index.html here caused a double-audio bug: the runtime
|
|
// discovered both the scaffold and the agent's real index.html as
|
|
// valid compositions, playing two audio tracks offset in time.
|
|
const metaPath = join(outputDir, "meta.json");
|
|
if (!existsSync(metaPath)) {
|
|
const hostname = new URL(url).hostname.replace(/^www\./, "");
|
|
writeFileSync(
|
|
metaPath,
|
|
JSON.stringify({ id: hostname + "-video", name: tokens.title || hostname }, null, 2),
|
|
"utf-8",
|
|
);
|
|
}
|
|
|
|
// Generate AGENTS.md + CLAUDE.md (AI agent instructions — always, regardless of API keys)
|
|
try {
|
|
const { generateAgentPrompt } = await import("./agentPromptGenerator.js");
|
|
generateAgentPrompt(
|
|
outputDir,
|
|
url,
|
|
tokens,
|
|
animationCatalog,
|
|
hasScreenshots,
|
|
hasLotties,
|
|
hasShaders,
|
|
catalogedAssets,
|
|
detectedLibraries,
|
|
);
|
|
progress("agent", "AGENTS.md + CLAUDE.md generated");
|
|
} catch (err) {
|
|
warnings.push(`AGENTS.md/CLAUDE.md generation failed: ${err}`);
|
|
}
|
|
}
|