@vibeframe/cli 0.113.11 → 0.113.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CONTEXT.md +12 -11
  2. package/dist/commands/_shared/build-plan.d.ts +1 -1
  3. package/dist/commands/_shared/compose-aivideo.d.ts +82 -0
  4. package/dist/commands/_shared/compose-aivideo.test.d.ts +1 -0
  5. package/dist/commands/_shared/compose-prompts.d.ts +8 -0
  6. package/dist/commands/_shared/compose-scenes-skills.d.ts +16 -2
  7. package/dist/commands/_shared/design-parse.d.ts +26 -0
  8. package/dist/commands/_shared/design-parse.test.d.ts +1 -0
  9. package/dist/commands/_shared/frontmatter.d.ts +30 -0
  10. package/dist/commands/_shared/hf-skill-bundle/bundle.d.ts +8 -2
  11. package/dist/commands/_shared/init-templates.d.ts +2 -2
  12. package/dist/commands/_shared/install-skill.d.ts +47 -6
  13. package/dist/commands/_shared/project-config.d.ts +5 -1
  14. package/dist/commands/_shared/project-config.test.d.ts +1 -0
  15. package/dist/commands/_shared/root-sync.d.ts +25 -0
  16. package/dist/commands/_shared/scene-assemble.d.ts +47 -0
  17. package/dist/commands/_shared/scene-assemble.test.d.ts +1 -0
  18. package/dist/commands/_shared/scene-build.d.ts +6 -5
  19. package/dist/commands/_shared/scene-project.d.ts +47 -13
  20. package/dist/commands/_shared/scene-render.d.ts +6 -0
  21. package/dist/commands/_shared/storyboard-edit.d.ts +1 -1
  22. package/dist/commands/_shared/storyboard-parse.d.ts +6 -0
  23. package/dist/commands/ai-video.d.ts +1 -1
  24. package/dist/commands/assemble.d.ts +2 -0
  25. package/dist/commands/design.d.ts +2 -0
  26. package/dist/commands/export.d.ts +0 -2
  27. package/dist/commands/generate/video.d.ts +2 -1
  28. package/dist/commands/init.d.ts +1 -1
  29. package/dist/commands/preview.d.ts +2 -0
  30. package/dist/commands/preview.test.d.ts +1 -0
  31. package/dist/index.js +2089 -1065
  32. package/dist/tools/define-tool.js +3 -0
  33. package/dist/tools/manifest/design.d.ts +10 -0
  34. package/dist/tools/manifest/design.test.d.ts +1 -0
  35. package/dist/tools/manifest/generate.d.ts +3 -3
  36. package/dist/tools/manifest/index.d.ts +2 -1
  37. package/dist/tools/manifest/index.js +2144 -1458
  38. package/package.json +3 -3
package/CONTEXT.md CHANGED
@@ -5,8 +5,9 @@
5
5
  VibeFrame CLI (`vibe`) is a CLI-first video toolkit. Every operation is
6
6
  a shell command. The same surface is exposed as MCP tools through
7
7
  [`@vibeframe/mcp-server`](https://www.npmjs.com/package/@vibeframe/mcp-server).
8
- For long-running video work, native Codex Goal mode and Claude Code `/goal`
9
- should own the outer loop: persistence, iteration, and stop decisions.
8
+ For long-running video work, your host's agent loop (Claude Code, Codex,
9
+ Cursor, or another coding-agent host) should own the outer loop: persistence,
10
+ iteration, and stop decisions.
10
11
  VibeFrame owns the video-specific command surface, JSON reports, cost gates,
11
12
  deterministic repair, render inspection, and `retryWith` recovery hints.
12
13
 
@@ -15,21 +16,21 @@ For the full reference (every flag, default, enum), read
15
16
  from `vibe schema --list`. This file is the _agent quickstart_: rules,
16
17
  conventions, and discovery hooks.
17
18
 
18
- ## Native host goal loop
19
+ ## Host agent loop
19
20
 
20
- Use the host's native goal feature rather than inventing a VibeFrame-owned
21
- goal runner. The canonical loop is:
21
+ Use your host's agent loop as the outer loop rather than inventing a
22
+ VibeFrame-owned loop. The canonical loop is:
22
23
 
23
24
  ```text
24
- native host goal -> vibe context/schema -> plan dry-run -> build with budget
25
+ host agent loop -> vibe context/schema -> plan dry-run -> build with budget
25
26
  -> status polling -> inspect project -> render -> inspect render
26
27
  -> repair/edit using nextActions/fixOwner -> repeat until stop rules pass
27
28
  ```
28
29
 
29
- Copy-paste Codex goal:
30
+ Copy-paste agent prompt (Codex) — a plain prompt, not a built-in command:
30
31
 
31
32
  ```text
32
- /goal Build my-video/ into a reviewed VibeFrame MP4. Use --json for every vibe
33
+ Build my-video/ into a reviewed VibeFrame MP4. Use --json for every vibe
33
34
  command, run --dry-run before paid operations, cap generated-asset spend with
34
35
  --max-cost 5 where supported, and read build-report.json plus
35
36
  review-report.json before deciding the next action. Prefer nextActions: run
@@ -43,10 +44,10 @@ AI review score is >= 90 when AI review is requested, and unresolved host-agent
43
44
  accepted with a written reason, or reported as blocked.
44
45
  ```
45
46
 
46
- Copy-paste Claude Code goal:
47
+ Copy-paste agent prompt (Claude Code) — a plain prompt, not a built-in command:
47
48
 
48
49
  ```text
49
- /goal Finish the VibeFrame render for my-video/ using Claude Code goal mode as
50
+ Finish the VibeFrame render for my-video/ using your host's agent loop as
50
51
  the outer loop. Use vibe context/schema when unsure, --json everywhere,
51
52
  dry-run before paid operations, --max-cost 5 for builds, nextActions before
52
53
  custom recovery, and build-report.json/review-report.json as loop state.
@@ -223,7 +224,7 @@ Review report contract:
223
224
  for storyboard/design/composition edits the host agent should make. Prefer
224
225
  `nextActions` first: run only `safeToAutoRun:true` actions automatically, ask
225
226
  before `requiresConfirmation:true`, and use `retryWith` only as the fallback.
226
- A host-native goal should not stop while `host-agent` issues remain unless
227
+ The outer loop should not stop while `host-agent` issues remain unless
227
228
  they are fixed, intentionally accepted with a written reason, or reported as
228
229
  blocked.
229
230
 
@@ -2,7 +2,7 @@ import { type BuildAssetKind } from "./build-cache.js";
2
2
  import { type AssetFreshness } from "./build-asset-metadata.js";
3
3
  import { type LoadedProjectConfig } from "./project-config.js";
4
4
  import { type StoryboardValidationIssue } from "./storyboard-edit.js";
5
- export type BuildStage = "assets" | "compose" | "sync" | "render" | "all";
5
+ export type BuildStage = "assets" | "transcript" | "compose" | "sync" | "render" | "all";
6
6
  export type BuildPlanStatus = "ready" | "invalid";
7
7
  export type AssetPlanReason = "canonical-exists" | "canonical-stale" | "canonical-unknown" | "content-cache-hit" | "force" | "missing" | "referenced-asset" | "invalid-reference" | "stage-skipped";
8
8
  export type ProviderResolutionKind = BuildAssetKind | "composer";
@@ -0,0 +1,82 @@
1
+ import { type Beat } from "./storyboard-parse.js";
2
+ export interface AiVideoDesignTokens {
3
+ /** Headline + body text colour. */
4
+ primary: string;
5
+ /** Scrim / fade-to ground colour. */
6
+ ground: string;
7
+ /** Eyebrow rule + single accent. */
8
+ accent: string;
9
+ /** Headline / eyebrow font-family. */
10
+ headFont: string;
11
+ /** Sub-line (italic) font-family. */
12
+ bodyFont: string;
13
+ }
14
+ export declare const DEFAULT_AIVIDEO_TOKENS: AiVideoDesignTokens;
15
+ /** Per-scene overlay copy derived from a beat heading + optional cues. */
16
+ export interface OverlayText {
17
+ kicker: string;
18
+ title: string;
19
+ sub: string;
20
+ }
21
+ /**
22
+ * Derive the lower-third copy for a beat. Authors override deterministically via
23
+ * `eyebrow`/`title`/`caption` cues; otherwise it falls back to the heading.
24
+ */
25
+ export declare function beatOverlayText(beat: Beat): OverlayText;
26
+ /**
27
+ * ffmpeg args to concat clips into one uniform background video. Each input is
28
+ * scaled+cropped to fill {width}x{height} at {fps}, then concatenated (no
29
+ * audio — narration rides a separate track).
30
+ */
31
+ export declare function buildBgConcatArgs(clipPaths: string[], outPath: string, opts?: {
32
+ width?: number;
33
+ height?: number;
34
+ fps?: number;
35
+ crf?: number;
36
+ }): string[];
37
+ /**
38
+ * A transparent lower-third overlay scene. The root background video shows
39
+ * through; a bottom scrim keeps text legible. Entrances only (the producer
40
+ * cuts on `data-duration`); the final scene fades to ground.
41
+ */
42
+ export declare function emitOverlayScene(opts: {
43
+ id: string;
44
+ text: OverlayText;
45
+ duration: number;
46
+ tokens?: AiVideoDesignTokens;
47
+ isLast?: boolean;
48
+ }): string;
49
+ /**
50
+ * Insert the single background `<video>` into `#root` (idempotent). Custom and
51
+ * managed elements are otherwise untouched; the bg video carries no
52
+ * `class="clip"`, so it is not subject to same-track clip scheduling and sits
53
+ * behind the scene overlays via `z-index`.
54
+ */
55
+ export declare function injectBgVideo(indexHtml: string, src: string, totalDuration: number): string;
56
+ export interface ComposeAiVideoResult {
57
+ bgVideoRel: string;
58
+ sceneFiles: string[];
59
+ totalDurationSec: number;
60
+ beats: number;
61
+ }
62
+ /**
63
+ * Orchestrate the deterministic compose: concat clips → bg-full.mp4, emit
64
+ * transparent overlay scenes, inject the bg video into index.html. Caller runs
65
+ * `root-sync` afterward to wire scene clip refs + narration.
66
+ */
67
+ export declare function composeAiVideo(opts: {
68
+ projectDir: string;
69
+ tokens?: AiVideoDesignTokens;
70
+ /** Resolve the clip path for a beat (defaults to `assets/video-<id>.mp4`). */
71
+ clipRelFor?: (beat: Beat) => string;
72
+ }): Promise<ComposeAiVideoResult>;
73
+ /** Minimal root composition shell carrying the single bg video. The sync stage
74
+ * adds the `<!-- vibe-scene-build -->` block (scene clip refs + narration). */
75
+ export declare function buildRootShell(tokens: AiVideoDesignTokens, bgRel: string, totalDuration: number): string;
76
+ /**
77
+ * Map a DESIGN.md to background-video tokens. Named color tokens (front-matter
78
+ * `colors: { primary, ground, accent }` or descriptive keys) win; otherwise the
79
+ * palette is read by luminance (darkest → ground, lightest → primary) and the
80
+ * most-saturated remaining color becomes the accent.
81
+ */
82
+ export declare function extractDesignTokens(designMd: string): AiVideoDesignTokens;
@@ -0,0 +1 @@
1
+ export {};
@@ -73,6 +73,14 @@ export interface ComposePromptsResult {
73
73
  projectDir: string;
74
74
  /** Project-root-relative path to DESIGN.md (always "DESIGN.md"). */
75
75
  designReference: string;
76
+ /**
77
+ * Project-root-relative path to the optional COMPOSITION.md structural
78
+ * contract (`"COMPOSITION.md"`), or `null` when the project doesn't have one.
79
+ * Parallel to {@link designReference}: DESIGN.md is the visual hard-gate,
80
+ * COMPOSITION.md is the structural hard-gate (layout system, GSAP timeline
81
+ * conventions, element/track rules). The host agent must honor it when present.
82
+ */
83
+ compositionReference: string | null;
76
84
  /** Project-root-relative path to STORYBOARD.md (always "STORYBOARD.md"). */
77
85
  storyboardReference: string;
78
86
  /**
@@ -25,6 +25,8 @@
25
25
  * Lint feedback retry loop comes in C4 (extends this module).
26
26
  */
27
27
  import { type LintFinding } from "./scene-lint.js";
28
+ /** Resolve the compose fan-out limit (env override, clamped to the beat count). */
29
+ export declare function composeConcurrency(beatCount: number): number;
28
30
  import { type Beat } from "./storyboard-parse.js";
29
31
  import { type ComposerProvider } from "./composer-resolve.js";
30
32
  import type { SceneTranscriptWord } from "./scene-html-emit.js";
@@ -35,6 +37,13 @@ export interface ComposeBeatContext {
35
37
  beat: Beat;
36
38
  /** Project's `DESIGN.md` content (visual identity hard-gate). */
37
39
  designMd: string;
40
+ /**
41
+ * Optional `COMPOSITION.md` content — the per-project STRUCTURAL contract
42
+ * (layout system, GSAP timeline conventions, element/track rules), parallel
43
+ * to DESIGN.md's visual contract. Injected into the system prompt as a
44
+ * hard-gate when present.
45
+ */
46
+ compositionMd?: string;
38
47
  /** Storyboard's global direction (everything before the first `## Beat`). */
39
48
  storyboardGlobal: string;
40
49
  /** Hyperframes skill bundle content + hash, from `loadHyperframesSkillBundle()`. */
@@ -101,8 +110,8 @@ export interface ComposeBeatResult {
101
110
  /** Composer provider that produced this beat. */
102
111
  provider: ComposerProvider;
103
112
  }
104
- /** Build the system prompt — skill bundle + DESIGN.md hard-gate. */
105
- export declare function buildSystemPrompt(ctx: Pick<ComposeBeatContext, "skillBundle" | "designMd">): string;
113
+ /** Build the system prompt — skill bundle + DESIGN.md hard-gate (+ optional COMPOSITION.md). */
114
+ export declare function buildSystemPrompt(ctx: Pick<ComposeBeatContext, "skillBundle" | "designMd" | "compositionMd">): string;
106
115
  /**
107
116
  * Word-level timing entries above this count are downgraded to coarse,
108
117
  * phrase-level anchors in the prompt. A token-budget guard: a 60s narration
@@ -267,6 +276,11 @@ export type ComposeProgressEvent = {
267
276
  export interface ComposeScenesParams {
268
277
  /** Path to DESIGN.md, relative to project root. */
269
278
  design?: string;
279
+ /**
280
+ * Path to the optional COMPOSITION.md structural contract, relative to project
281
+ * root. Defaults to `COMPOSITION.md`; silently ignored when absent.
282
+ */
283
+ composition?: string;
270
284
  /** Path to STORYBOARD.md, relative to project root. */
271
285
  storyboard?: string;
272
286
  /** Scene project root, relative to outputDir. Defaults to outputDir. */
@@ -0,0 +1,26 @@
1
+ export interface ParsedDesign {
2
+ /** `name:` from front-matter, if present. */
3
+ name?: string;
4
+ /** Color tokens — front-matter `colors:` map, else extracted from prose hexes. */
5
+ colors: Record<string, string>;
6
+ /** Typography tokens from front-matter `typography:` (shape left to the spec). */
7
+ typography: Record<string, unknown>;
8
+ /** `## ` section title → prose body (trimmed). */
9
+ sections: Record<string, string>;
10
+ /** Raw parsed front-matter object (`{}` when absent/invalid). */
11
+ frontmatter: Record<string, unknown>;
12
+ /** Original document. */
13
+ raw: string;
14
+ }
15
+ export declare function parseDesign(md: string): ParsedDesign;
16
+ export interface DesignValidationIssue {
17
+ severity: "error" | "warning" | "info";
18
+ code: string;
19
+ message: string;
20
+ }
21
+ /**
22
+ * Lint a DESIGN.md for the soft contract: parseable front-matter, at least a
23
+ * couple of palette colors, and the core prose sections. Warnings/infos only —
24
+ * a thin DESIGN.md is valid, just less useful to the composer.
25
+ */
26
+ export declare function validateDesignMarkdown(md: string): DesignValidationIssue[];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @module _shared/frontmatter
3
+ *
4
+ * One canonical "YAML frontmatter + markdown body" splitter for all VibeFrame
5
+ * `.md` files. Previously each consumer (storyboard, design, install-skill)
6
+ * declared its own `/^---\n…\n---/` regex + `yaml.parse` — this consolidates
7
+ * them.
8
+ *
9
+ * The shape matches Google's Open Knowledge Format (OKF) and Mintlify: a leading
10
+ * `---` YAML block, then a free-form markdown body. VibeFrame project files
11
+ * (STORYBOARD.md, DESIGN.md, CHARACTERS.md, …) are OKF concept documents; docs/
12
+ * pages carry OKF/Mintlify frontmatter (`type`, `title`, `description`, `tags`).
13
+ */
14
+ /** Leading `---\n…\n---` block. Anchored to the start of the (normalized) text. */
15
+ export declare const FRONTMATTER_RE: RegExp;
16
+ export interface ExtractedFrontmatter {
17
+ /** Parsed frontmatter map, or `null` when absent / invalid / not a map. */
18
+ data: Record<string, unknown> | null;
19
+ /** Markdown body after the frontmatter block (or the whole doc when absent). */
20
+ body: string;
21
+ /** The full document, CRLF-normalized to LF. */
22
+ raw: string;
23
+ }
24
+ /**
25
+ * Split a leading YAML frontmatter block from the markdown body. CRLF is
26
+ * normalized to LF. Returns `data: null` (freeform) when there is no leading
27
+ * block, the YAML is invalid, or it is not a plain object — callers decide
28
+ * whether that means `undefined` or `{}`.
29
+ */
30
+ export declare function extractFrontmatter(md: string): ExtractedFrontmatter;
@@ -45,8 +45,14 @@ export declare const BUNDLE_VERSION = "970367f-2026-04-25";
45
45
  *
46
46
  * Side-effect free except for the optional `~/.claude/skills/hyperframes/`
47
47
  * filesystem read.
48
+ *
49
+ * Async because the vendored snapshot content is loaded via a lazy
50
+ * `await import("./bundle-content.js")` (see {@link buildVendored}) so paths
51
+ * that never need it (agent-mode, template composer) don't pay to evaluate the
52
+ * ~52KB string. When an installed skill is present, the vendored content module
53
+ * is never imported at all.
48
54
  */
49
- export declare function loadHyperframesSkillBundle(): {
55
+ export declare function loadHyperframesSkillBundle(): Promise<{
50
56
  content: string;
51
57
  /** "installed" (user has ~/.claude/skills/hyperframes/) or "vendored" (esbuild-inlined snapshot). */
52
58
  source: "installed" | "vendored";
@@ -54,4 +60,4 @@ export declare function loadHyperframesSkillBundle(): {
54
60
  hint: string;
55
61
  /** Stable hash of (BUNDLE_VERSION + content). Used as cache-key input. */
56
62
  hash: string;
57
- };
63
+ }>;
@@ -18,7 +18,7 @@
18
18
  * file. Keep this as the canonical source of truth for agent guidance —
19
19
  * \`CLAUDE.md\` and \`GEMINI.md\` just import it.
20
20
  */
21
- export declare const AGENTS_MD = "# AGENTS.md\n\nThis project uses [VibeFrame](https://github.com/vericontext/vibeframe) \u2014\na CLI for AI-powered video editing. The `vibe` command provides 80+\nsubcommands for video generation, editing, analysis, and pipeline\norchestration. Cost tiers below \u2014 **always confirm with the user before\nrunning High / Very High tier commands**.\n\n## Agent host coverage\n\nVibeFrame's CLI works with any bash-capable AI coding agent. Six host\nfamilies have first-class auto-detection and scaffolds today:\n\n- **Claude Code** \u2014 `CLAUDE.md` (this file's import wrapper) +\n `AGENTS.md` + `/vibe-*` slash commands.\n- **OpenAI Codex** \u2014 reads `AGENTS.md` natively (agents.md spec).\n- **Cursor** \u2014 reads `AGENTS.md` plus `.cursor/rules/` for project\n rules; supports MCP via `.cursor/mcp.json`.\n- **Aider** \u2014 reads `AGENTS.md` (binary-only detection by VibeFrame).\n- **Gemini CLI** \u2014 primary context file is `GEMINI.md`; also reads\n `AGENTS.md` if present.\n- **OpenCode** \u2014 reads `AGENTS.md` (officially supported per\n `opencode.ai/docs/rules`); supports MCP via `.opencode/mcp.json`.\n\nRun `vibe doctor` to see which host(s) the CLI detects in your\nenvironment. Anyone running another bash-capable agent (Continue,\nSourcegraph Cody, etc.) still gets this `AGENTS.md` as the universal\nfallback.\n\n## Self-discovery\n\nDon't memorise the command surface \u2014 read it from the CLI:\n\n```bash\nvibe --help # command groups overview\nvibe schema --list # full machine-readable catalog (80+)\nvibe schema --list --surface public # small first-run/product command surface\nvibe schema generate.video # JSON Schema for any single command\nvibe doctor # available providers + system health\nvibe host setup all # print Codex/Claude/Cursor app integration snippets\nvibe host doctor all --json # verify project guidance + MCP config\nvibe guide motion # choose text-overlay vs motion-overlay vs generate motion\nvibe guide scene # step-by-step authoring guide (scene)\nvibe guide pipeline # step-by-step authoring guide (pipeline)\n```\n\n## Route by the user's actual request\n\nRouting the user's request correctly is the most important judgement call\nyou'll make. Do not force everything into a scene project.\n\n**ASSET \u2014 default for any single-asset request OR ambiguous prompt.**\nIf the user asks for a single image, single video clip, sound effect, music\nbed, or narration file \u2014 **OR pastes a visual/audio brief without explicit\nstoryboard / scene / multi-scene language** \u2014 treat it as ASSET. Use\n`vibe generate ...` directly. Do **not** edit `DESIGN.md`,\n`STORYBOARD.md`, run `vibe scene ...`, or auto-load the hyperframes\nskill until BUILD intent is explicit. The visual-identity hard-gate\napplies to BUILD only.\n\nExamples:\n- \"make this image\" \u2192 `vibe generate image \"...\" -o assets/name.png`\n- \"use this image to make a video\" \u2192 `vibe generate video \"...\" -i image.png -o renders/name.mp4`\n- \"add a lower-third/title/animated overlay/grain/vignette to this clip\" \u2192 prefer `vibe edit motion-overlay clip.mp4 \"...\" --understand auto -o out.mp4` when the request asks for designed or animated motion graphics.\n- \"please add visuals using OpenAI image gen\" \u2192 `vibe generate image \"...\" -p openai ...`\n- *(verb-less paste)* \"aerial view of a misty mountain peak at sunrise...\" \u2192 `vibe generate image \"<paste>\" -o assets/mountain-peak.png`. **Don't** read it as a brief for DESIGN.md.\n\nIf genuinely uncertain, ask one short question: *\"single asset or\nmulti-scene project?\"* before authoring DESIGN.md or loading the\nhyperframes skill.\n\n**BUILD \u2014 create new video from text intent.**\nUse `vibe build` with a STORYBOARD.md + DESIGN.md. The\nskills-driven pipeline (v0.60+) dispatches narration TTS + backdrop\nimage-gen per beat, composes scene HTML via the bundled composition rules\nbundle, then renders to MP4. Idempotent re-runs reuse cached assets.\n\n**REMIX \u2014 transform existing video / audio.**\nUse `vibe remix`, `vibe edit`, or `vibe audio`. One-shot,\nbatch-oriented operations on a file the user already has on disk.\n`vibe edit text-overlay` is the free deterministic path for simple static\ntext burn-in. If the user asks for motion design, animated lower-thirds,\ndesigned titles, grain/vignette as part of a graphic treatment, or says\n\"motion graphics\", use `vibe edit motion-overlay ... --understand auto` instead.\n\nDecision rule: if the user asks for one asset, it's ASSET. If the user asks\nfor a multi-scene/storyboard/composed video, it's BUILD. If the user is\nstarting from a media file and wants it transformed, it's REMIX.\n\n## Common workflows\n\n### BUILD (new video from text)\n\n| Task | Command |\n|---|---|\n| One-shot storyboard \u2192 video | `vibe build [project-dir]` |\n| Render an existing scene project | `vibe render [project-dir] -o out.mp4` |\n| Lint scene HTML | `vibe scene lint --json` |\n| Generate a single image | `vibe generate image \"prompt\" -o img.png --quality hd` |\n| Generate a single video | `vibe generate video \"prompt\" -i image.png -o clip.mp4` |\n| Generate narration | `vibe generate narration \"text\" -o voice.mp3` |\n\n### REMIX (transform existing media)\n\n| Task | Command |\n|---|---|\n| Extract highlights from a long video | `vibe remix highlights file.mp4 -d 60` |\n| Long video \u2192 vertical shorts | `vibe remix auto-shorts file.mp4 -n 3 --add-captions` |\n| Add animated word-by-word captions | `vibe remix animated-caption file.mp4 -s karaoke-sweep` |\n| Add designed motion graphics overlay | `vibe edit motion-overlay file.mp4 \"lower-third...\" --understand auto -o out.mp4` |\n| Remove silence | `vibe edit silence-cut in.mp4 -o out.mp4` |\n| Add static captions | `vibe edit caption in.mp4 -o out.mp4` |\n| Translate audio (transcribe \u2192 TTS) | `vibe audio dub file.mp4 -t ko` |\n| Transcribe (Whisper, word-level) | `vibe audio transcribe file.mp4 --granularity word` |\n| Inspect media with Gemini | `vibe inspect media file.mp4 \"summarise\" --json` |\n\n### Compose pipelines (Video as Code)\n\n| Task | Command |\n|---|---|\n| Run a YAML pipeline | `vibe run pipeline.yaml --dry-run` |\n| Resume from last checkpoint | `vibe run pipeline.yaml --resume` |\n\n## Cost tiers\n\n| Tier | Examples | Approx. per call |\n|---|---|---|\n| **Free** | `schema/context/doctor`, `detect *`, `status *`, `plan`, `storyboard validate`, `inspect project/render --cheap`, deterministic edits | $0 |\n| **Low** | `generate narration/sound-effect/music`, `audio transcribe`, `inspect media`, optional AI review | $0.01\u2013$0.10 |\n| **High** | `generate image/motion`, `edit image/reframe/grade/speed-ramp` | $1\u2013$5 |\n| **Very High** | `generate video`, `edit fill-gaps`, `remix highlights/auto-shorts`, `build` with generated assets | $5\u2013$50+ |\n\n## Agent invariants\n\nWhen you run vibe commands programmatically:\n\n1. **Always `--json`** for structured output you can parse without regex\n2. **Always `--dry-run` first** for any High/Very-High tier command \u2014 it returns\n the cost estimate without spending API budget\n3. **Use `vibe schema <command>`** to discover parameters; do not guess flags\n4. **Pass complex options via `--stdin`**:\n ```bash\n echo '{\"video\":\"long.mp4\",\"count\":3,\"aspect\":\"9:16\"}' | vibe remix auto-shorts --stdin --json\n ```\n\n## Provider keys and project scope\n\nPrefer `vibe setup --scope project` for project-local keys. It writes\n`.vibeframe/config.yaml` in the setup directory; when you work from a\nnested scene directory, the `vibe` CLI automatically searches parent\ndirectories and uses the nearest project config. Do not assume the config\nfile must exist in the current subdirectory.\n\nRun `vibe doctor --json` from the directory where you will run commands\nand check `data.scope.project.configPath` / `data.scope.activeScope` before\ndeciding keys are missing. Never print or copy config file contents.\n\nFor generated project assets, use VibeFrame commands such as\n`vibe generate image|video|speech ...` so these project-scope keys are\nused. Do not switch to a host agent's built-in image/audio generation tool\nunless the user explicitly asks for a non-VibeFrame asset.\n\n`.env` is still supported as a gitignored fallback. Copy `.env.example`\nto start if you prefer environment variables.\n\n## App host setup\n\nVibeFrame is CLI-first, not terminal-only. Codex, Claude Code, and Cursor can\ndrive the same project through shell commands, and app hosts can also use\nVibeFrame's MCP server as a typed tool surface.\n\n```bash\nvibe host setup all # print snippets only\nvibe host setup all --write # write project/app config\nvibe host doctor all --json # verify readiness\n```\n\nDefault setup is snippet-only; use `--write` when you want VibeFrame to merge\nthe generated config. Keep provider keys in `.vibeframe/config.yaml`, user\nconfig, or env files \u2014 not in app host config.\n\n## Native host goal loop\n\nFor long-running work, use Codex Goal mode, Claude Code `/goal`, Cursor, or\nanother host-native goal feature as the outer loop. VibeFrame should not own a\ncompeting project-level goal runner. It provides `--json` commands, dry runs,\nbudget caps, `build-report.json`, `review-report.json`, `nextActions`,\n`safeToAutoRun`, `requiresConfirmation`, `fixOwner`, deterministic repair,\nand render inspection for the host agent to decide what to do next.\n\nCopy-paste Codex:\n\n```text\n/goal Build this VibeFrame project into renders/final.mp4. Use --json for every\nvibe command, run --dry-run before paid operations, use --max-cost 5 for builds\nunless the user gives another budget, read build-report.json and\nreview-report.json before deciding the next action, prefer nextActions before\nguessing, run only safeToAutoRun:true actions automatically, and ask before\nrequiresConfirmation:true actions. Treat retryWith as the compatibility\nfallback, fixOwner:\"vibe\" as CLI repair work, and fixOwner:\"host-agent\" as\nSTORYBOARD.md, DESIGN.md, or composition edits.\nStop only when renders/final.mp4 exists, target duration and aspect ratio are\nmet, inspect render --cheap has no errors, any AI review score is >= 90 when AI review is requested, and every\nremaining host-agent issue is fixed, accepted with rationale, or reported as\nblocked.\n```\n\nCopy-paste Claude Code:\n\n```text\n/goal Finish this VibeFrame render using Claude Code goal mode as the outer\nloop. Use vibe context/schema when unsure, --json everywhere, dry-run before\npaid operations, budget cap via --max-cost 5, nextActions before guessing,\nbuild-report.json/review-report.json as loop state, and safeToAutoRun,\nrequiresConfirmation, and fixOwner to decide between vibe scene repair and\nhost-agent edits. Stop only after final MP4,\ntarget duration/aspect ratio, clean render inspection, any AI review score >= 90 when AI review is requested, and\nno unresolved unacknowledged host-agent issues.\n```\n\n### Composition rules (scene HTML)\n\n`vibe init` installs local composition rules into your project. The\nuniversal copy lives at `SKILL.md` (with `references/*.md`), and\nhost-specific copies are placed where each agent expects them\n(`.claude/skills/hyperframes/` for Claude Code, `.cursor/rules/hyperframes.mdc`\nfor Cursor). **Read `SKILL.md` before authoring any scene composition HTML\nunder `compositions/`** \u2014 it defines the framework rules, motion\nprinciples, type system, and visual-identity hard-gate. The same skill\ngoverns `vibe scene lint` so your authored HTML and the linter stay in\nagreement.\n\nFor render-stable text, do not apply continuous `scale`, `x`, `y`,\n`filter`, or other transform tweens to `.scene-content` or any ancestor\ncontaining live text/cards. Put ambient zoom/parallax on background or media\nlayers only; text should enter briefly and then hold still at its final CSS\nposition.\n\nTo retro-install on a project scaffolded before this command existed, run\n`vibe scene install-skill [--host all]`.\n\n### Scene composer (batch / non-agent fallback)\n\nWhen you don't want to author HTML yourself, `vibe build --mode batch` runs an\nLLM internally with the same skill bundle. It auto-picks a provider based\non available keys (`claude > gemini > openai`) \u2014 pass `--composer <name>`\nto force one:\n\n| Provider | Env var | Spike notes (v0.70) |\n|---|---|---|\n| Claude (default) | `ANTHROPIC_API_KEY` | ~9 s/beat. Fastest, validated baseline. |\n| Gemini | `GOOGLE_API_KEY` | ~20 s/beat. ~2.6\u00D7 cheaper than Claude. |\n| OpenAI | `OPENAI_API_KEY` | ~70 s/beat. gpt-5 reasoning latency \u2014 opt-in only. |\n\nAll three pass first-shot lint at every effort tier on the `vibeframe-promo`\nfixture. Quality on more complex storyboards may differ \u2014 fall back to\n`--composer claude` if a non-default provider repeatedly fails lint.\n\n## Project conventions\n\n<!-- Edit this section with YOUR project's specifics. Examples below. -->\n\n- Default aspect ratio: `16:9`\n- Default scene duration: `3 seconds` (per beat)\n- Brand voice: <describe>\n- Output naming: `renders/<name>-<timestamp>.mp4`\n";
21
+ export declare const AGENTS_MD = "# AGENTS.md\n\nThis project uses [VibeFrame](https://github.com/vericontext/vibeframe) \u2014\na CLI for AI-powered video editing. The `vibe` command provides 80+\nsubcommands for video generation, editing, analysis, and pipeline\norchestration. Cost tiers below \u2014 **always confirm with the user before\nrunning High / Very High tier commands**.\n\n## Agent host coverage\n\nVibeFrame's CLI works with any bash-capable AI coding agent. Six host\nfamilies have first-class auto-detection and scaffolds today:\n\n- **Claude Code** \u2014 `CLAUDE.md` (this file's import wrapper) +\n `AGENTS.md` + `/vibe-*` slash commands.\n- **OpenAI Codex** \u2014 reads `AGENTS.md` natively (agents.md spec).\n- **Cursor** \u2014 reads `AGENTS.md` plus `.cursor/rules/` for project\n rules; supports MCP via `.cursor/mcp.json`.\n- **Aider** \u2014 reads `AGENTS.md` (binary-only detection by VibeFrame).\n- **Gemini CLI** \u2014 primary context file is `GEMINI.md`; also reads\n `AGENTS.md` if present.\n- **OpenCode** \u2014 reads `AGENTS.md` (officially supported per\n `opencode.ai/docs/rules`); supports MCP via `.opencode/mcp.json`.\n\nRun `vibe doctor` to see which host(s) the CLI detects in your\nenvironment. Anyone running another bash-capable agent (Continue,\nSourcegraph Cody, etc.) still gets this `AGENTS.md` as the universal\nfallback.\n\n## Self-discovery\n\nDon't memorise the command surface \u2014 read it from the CLI:\n\n```bash\nvibe --help # command groups overview\nvibe schema --list # full machine-readable catalog (80+)\nvibe schema --list --surface public # small first-run/product command surface\nvibe schema generate.video # JSON Schema for any single command\nvibe doctor # available providers + system health\nvibe host setup all # print Codex/Claude/Cursor app integration snippets\nvibe host doctor all --json # verify project guidance + MCP config\nvibe guide motion # choose text-overlay vs motion-overlay vs generate motion\nvibe guide scene # step-by-step authoring guide (scene)\nvibe guide pipeline # step-by-step authoring guide (pipeline)\n```\n\n## Route by the user's actual request\n\nRouting the user's request correctly is the most important judgement call\nyou'll make. Do not force everything into a scene project.\n\n**ASSET \u2014 default for any single-asset request OR ambiguous prompt.**\nIf the user asks for a single image, single video clip, sound effect, music\nbed, or narration file \u2014 **OR pastes a visual/audio brief without explicit\nstoryboard / scene / multi-scene language** \u2014 treat it as ASSET. Use\n`vibe generate ...` directly. Do **not** edit `DESIGN.md`,\n`STORYBOARD.md`, run `vibe scene ...`, or auto-load the hyperframes\nskill until BUILD intent is explicit. The visual-identity hard-gate\napplies to BUILD only.\n\nExamples:\n- \"make this image\" \u2192 `vibe generate image \"...\" -o assets/name.png`\n- \"use this image to make a video\" \u2192 `vibe generate video \"...\" -i image.png -o renders/name.mp4`\n- \"add a lower-third/title/animated overlay/grain/vignette to this clip\" \u2192 prefer `vibe edit motion-overlay clip.mp4 \"...\" --understand auto -o out.mp4` when the request asks for designed or animated motion graphics.\n- \"please add visuals using OpenAI image gen\" \u2192 `vibe generate image \"...\" -p openai ...`\n- *(verb-less paste)* \"aerial view of a misty mountain peak at sunrise...\" \u2192 `vibe generate image \"<paste>\" -o assets/mountain-peak.png`. **Don't** read it as a brief for DESIGN.md.\n\nIf genuinely uncertain, ask one short question: *\"single asset or\nmulti-scene project?\"* before authoring DESIGN.md or loading the\nhyperframes skill.\n\n**BUILD \u2014 create new video from text intent.**\nUse `vibe build` with a STORYBOARD.md + DESIGN.md. The\nskills-driven pipeline (v0.60+) dispatches narration TTS + backdrop\nimage-gen per beat, composes scene HTML via the bundled composition rules\nbundle, then renders to MP4. Idempotent re-runs reuse cached assets.\n\n**REMIX \u2014 transform existing video / audio.**\nUse `vibe remix`, `vibe edit`, or `vibe audio`. One-shot,\nbatch-oriented operations on a file the user already has on disk.\n`vibe edit text-overlay` is the free deterministic path for simple static\ntext burn-in. If the user asks for motion design, animated lower-thirds,\ndesigned titles, grain/vignette as part of a graphic treatment, or says\n\"motion graphics\", use `vibe edit motion-overlay ... --understand auto` instead.\n\nDecision rule: if the user asks for one asset, it's ASSET. If the user asks\nfor a multi-scene/storyboard/composed video, it's BUILD. If the user is\nstarting from a media file and wants it transformed, it's REMIX.\n\n## Common workflows\n\n### BUILD (new video from text)\n\n| Task | Command |\n|---|---|\n| One-shot storyboard \u2192 video | `vibe build [project-dir]` |\n| Render an existing scene project | `vibe render [project-dir] -o out.mp4` |\n| Lint scene HTML | `vibe scene lint --json` |\n| Generate a single image | `vibe generate image \"prompt\" -o img.png --quality hd` |\n| Generate a single video | `vibe generate video \"prompt\" -i image.png -o clip.mp4` |\n| Generate narration | `vibe generate narration \"text\" -o voice.mp3` |\n\n### REMIX (transform existing media)\n\n| Task | Command |\n|---|---|\n| Extract highlights from a long video | `vibe remix highlights file.mp4 -d 60` |\n| Long video \u2192 vertical shorts | `vibe remix auto-shorts file.mp4 -n 3 --add-captions` |\n| Add animated word-by-word captions | `vibe remix animated-caption file.mp4 -s karaoke-sweep` |\n| Add designed motion graphics overlay | `vibe edit motion-overlay file.mp4 \"lower-third...\" --understand auto -o out.mp4` |\n| Remove silence | `vibe edit silence-cut in.mp4 -o out.mp4` |\n| Add static captions | `vibe edit caption in.mp4 -o out.mp4` |\n| Translate audio (transcribe \u2192 TTS) | `vibe audio dub file.mp4 -t ko` |\n| Transcribe (Whisper, word-level) | `vibe audio transcribe file.mp4 --granularity word` |\n| Inspect media with Gemini | `vibe inspect media file.mp4 \"summarise\" --json` |\n\n### Compose pipelines (Video as Code)\n\n| Task | Command |\n|---|---|\n| Run a YAML pipeline | `vibe run pipeline.yaml --dry-run` |\n| Resume from last checkpoint | `vibe run pipeline.yaml --resume` |\n\n## Cost tiers\n\n| Tier | Examples | Approx. per call |\n|---|---|---|\n| **Free** | `schema/context/doctor`, `detect *`, `status *`, `plan`, `storyboard validate`, `inspect project/render --cheap`, deterministic edits | $0 |\n| **Low** | `generate narration/sound-effect/music`, `audio transcribe`, `inspect media`, optional AI review | $0.01\u2013$0.10 |\n| **High** | `generate image/motion`, `edit image/reframe/grade/speed-ramp` | $1\u2013$5 |\n| **Very High** | `generate video`, `edit fill-gaps`, `remix highlights/auto-shorts`, `build` with generated assets | $5\u2013$50+ |\n\n## Agent invariants\n\nWhen you run vibe commands programmatically:\n\n1. **Always `--json`** for structured output you can parse without regex\n2. **Always `--dry-run` first** for any High/Very-High tier command \u2014 it returns\n the cost estimate without spending API budget\n3. **Use `vibe schema <command>`** to discover parameters; do not guess flags\n4. **Pass complex options via `--stdin`**:\n ```bash\n echo '{\"video\":\"long.mp4\",\"count\":3,\"aspect\":\"9:16\"}' | vibe remix auto-shorts --stdin --json\n ```\n\n## Provider keys and project scope\n\nPrefer `vibe setup --scope project` for project-local keys. It writes\n`.vibeframe/config.yaml` in the setup directory; when you work from a\nnested scene directory, the `vibe` CLI automatically searches parent\ndirectories and uses the nearest project config. Do not assume the config\nfile must exist in the current subdirectory.\n\nRun `vibe doctor --json` from the directory where you will run commands\nand check `data.scope.project.configPath` / `data.scope.activeScope` before\ndeciding keys are missing. Never print or copy config file contents.\n\nFor generated project assets, use VibeFrame commands such as\n`vibe generate image|video|speech ...` so these project-scope keys are\nused. Do not switch to a host agent's built-in image/audio generation tool\nunless the user explicitly asks for a non-VibeFrame asset.\n\n`.env` is still supported as a gitignored fallback. Copy `.env.example`\nto start if you prefer environment variables.\n\n## App host setup\n\nVibeFrame is CLI-first, not terminal-only. Codex, Claude Code, and Cursor can\ndrive the same project through shell commands, and app hosts can also use\nVibeFrame's MCP server as a typed tool surface.\n\n```bash\nvibe host setup all # print snippets only\nvibe host setup all --write # write project/app config\nvibe host doctor all --json # verify readiness\n```\n\nDefault setup is snippet-only; use `--write` when you want VibeFrame to merge\nthe generated config. Keep provider keys in `.vibeframe/config.yaml`, user\nconfig, or env files \u2014 not in app host config.\n\n## Host agent loop\n\nFor long-running work, use your host's agent loop (Claude Code, Codex, Cursor,\nor another coding-agent host) as the outer loop. VibeFrame should not own a\ncompeting project-level agent loop. It provides `--json` commands, dry runs,\nbudget caps, `build-report.json`, `review-report.json`, `nextActions`,\n`safeToAutoRun`, `requiresConfirmation`, `fixOwner`, deterministic repair,\nand render inspection for the host agent to decide what to do next.\n\nCopy-paste agent prompt (Codex) \u2014 a plain prompt, not a built-in command:\n\n```text\nBuild this VibeFrame project into renders/final.mp4. Use --json for every\nvibe command, run --dry-run before paid operations, use --max-cost 5 for builds\nunless the user gives another budget, read build-report.json and\nreview-report.json before deciding the next action, prefer nextActions before\nguessing, run only safeToAutoRun:true actions automatically, and ask before\nrequiresConfirmation:true actions. Treat retryWith as the compatibility\nfallback, fixOwner:\"vibe\" as CLI repair work, and fixOwner:\"host-agent\" as\nSTORYBOARD.md, DESIGN.md, or composition edits.\nStop only when renders/final.mp4 exists, target duration and aspect ratio are\nmet, inspect render --cheap has no errors, any AI review score is >= 90 when AI review is requested, and every\nremaining host-agent issue is fixed, accepted with rationale, or reported as\nblocked.\n```\n\nCopy-paste agent prompt (Claude Code) \u2014 a plain prompt, not a built-in command:\n\n```text\nFinish this VibeFrame render using your host's agent loop as the outer\nloop. Use vibe context/schema when unsure, --json everywhere, dry-run before\npaid operations, budget cap via --max-cost 5, nextActions before guessing,\nbuild-report.json/review-report.json as loop state, and safeToAutoRun,\nrequiresConfirmation, and fixOwner to decide between vibe scene repair and\nhost-agent edits. Stop only after final MP4,\ntarget duration/aspect ratio, clean render inspection, any AI review score >= 90 when AI review is requested, and\nno unresolved unacknowledged host-agent issues.\n```\n\n### Composition rules (scene HTML)\n\nComposition rules live in the **Hyperframes skill** \u2014 framework rules, motion\nprinciples, type system, and visual-identity hard-gate. **Load that skill\nbefore authoring any scene composition HTML under `compositions/`.** The same\nrules govern `vibe scene lint`, so your authored HTML and the linter stay in\nagreement.\n\nWhere the skill comes from depends on your setup:\n\n- If the `hyperframes` skill is installed globally (e.g. Claude Code), your\n agent already has it \u2014 `vibe init` does **not** copy it into the project.\n- Otherwise `vibe init` writes a local copy: universal `SKILL.md` (with\n `references/*.md`) at the project root, plus host layouts\n (`.claude/skills/hyperframes/`, `.cursor/rules/hyperframes.mdc`).\n\nTo get editable per-project copies on demand, run\n`vibe scene install-skill [--host all]` (these copies are gitignored by\ndefault \u2014 see `.gitignore`).\n\nFor render-stable text, do not apply continuous `scale`, `x`, `y`,\n`filter`, or other transform tweens to `.scene-content` or any ancestor\ncontaining live text/cards. Put ambient zoom/parallax on background or media\nlayers only; text should enter briefly and then hold still at its final CSS\nposition.\n\n### Scene composer (batch / non-agent fallback)\n\nWhen you don't want to author HTML yourself, `vibe build --mode batch` runs an\nLLM internally with the same skill bundle. It auto-picks a provider based\non available keys (`claude > gemini > openai`) \u2014 pass `--composer <name>`\nto force one:\n\n| Provider | Env var | Spike notes (v0.70) |\n|---|---|---|\n| Claude (default) | `ANTHROPIC_API_KEY` | ~9 s/beat. Fastest, validated baseline. |\n| Gemini | `GOOGLE_API_KEY` | ~20 s/beat. ~2.6\u00D7 cheaper than Claude. |\n| OpenAI | `OPENAI_API_KEY` | ~70 s/beat. gpt-5 reasoning latency \u2014 opt-in only. |\n\nAll three pass first-shot lint at every effort tier on the `vibeframe-promo`\nfixture. Quality on more complex storyboards may differ \u2014 fall back to\n`--composer claude` if a non-default provider repeatedly fails lint.\n\n## Project conventions\n\n<!-- Edit this section with YOUR project's specifics. Examples below. -->\n\n- Default aspect ratio: `16:9`\n- Default scene duration: `3 seconds` (per beat)\n- Brand voice: <describe>\n- Output naming: `renders/<name>-<timestamp>.mp4`\n";
22
22
  /**
23
23
  * Claude Code project file. Imports AGENTS.md so guidance stays
24
24
  * single-sourced; adds Claude-specific overrides (slash commands, etc.).
@@ -47,7 +47,7 @@ export interface EnvExampleOptions {
47
47
  */
48
48
  export declare function renderEnvExample(opts?: EnvExampleOptions): string;
49
49
  /** Lines vibe init merges into the project's .gitignore (idempotent). */
50
- export declare const GITIGNORE_ADDITIONS = "\n# VibeFrame\n.env\n.env.local\nrenders/\n.pipeline-state.yaml\n*.vibe.json\n.vibeframe/\n";
50
+ export declare const GITIGNORE_ADDITIONS = "\n# VibeFrame\n.env\n.env.local\nrenders/\n.pipeline-state.yaml\n*.vibe.json\n.vibeframe/\n\n# Vendored Hyperframes skill copies \u2014 regenerable via 'vibe scene install-skill'.\n# Delete these lines if you eject and want to commit per-project customizations.\n/SKILL.md\n/references/\n/.claude/skills/hyperframes/\n/.cursor/rules/hyperframes.mdc\n";
51
51
  /**
52
52
  * vibe.project.yaml minimal scaffold. Only written when no project
53
53
  * config exists — `vibe init` never overwrites an existing file unless
@@ -22,16 +22,32 @@
22
22
  * references/{house-style,...}.md
23
23
  * .cursor/rules/hyperframes.mdc # if hosts includes "cursor"
24
24
  *
25
- * Codex + Aider are AGENTS.md-driven hosts; they read the project root
26
- * SKILL.md via an `@SKILL.md` reference in AGENTS.md (handled by the
27
- * init-templates AGENTS_MD section, not here).
25
+ * Codex + Aider are AGENTS.md-driven hosts; they discover the project-root
26
+ * SKILL.md because AGENTS.md points at it in prose (the init-templates
27
+ * AGENTS_MD "Composition rules" section, not an `@`-import).
28
28
  *
29
29
  * The content is byte-identical to what `loadHyperframesSkillBundle()`
30
- * uses — same vendored files, same `BUNDLE_VERSION`. After install, the
31
- * agentic compose path (Phase H2) reads these files instead of the
32
- * vendored TS string constants, so users can edit them per project.
30
+ * uses — same vendored files, same `BUNDLE_VERSION`. Build/render never read
31
+ * these project files (they use the vendored bundle); they exist only so host
32
+ * agents can read the rules and so users can edit them per project.
33
+ *
34
+ * Lean scaffolds: the automatic `vibe init` path passes `lean: true`, so when
35
+ * the Hyperframes skill is already installed globally
36
+ * (`hasGlobalHyperframesSkill()`) it skips the redundant copies — the
37
+ * `.claude/skills/hyperframes/` copy always, and the root `SKILL.md` +
38
+ * `references/` unless a root-reading host (codex/aider/gemini/opencode) needs
39
+ * them. Explicit `vibe scene install-skill` omits `lean`, fully materializing
40
+ * editable copies regardless (the "eject").
33
41
  */
34
42
  import type { AgentHostId } from "../../utils/agent-host-detect.js";
43
+ /**
44
+ * True when the Hyperframes skill is installed at the user's global Claude
45
+ * skills dir (`~/.claude/skills/hyperframes/`). When present, materializing the
46
+ * skill into a project is redundant — the host agent already loads it globally —
47
+ * so `vibe init` keeps the scaffold lean and skips the copies. Explicit
48
+ * `vibe scene install-skill` ignores this (it's the opt-in "eject" path).
49
+ */
50
+ export declare function hasGlobalHyperframesSkill(): boolean;
35
51
  /** Hosts the install-skill command knows file layouts for. */
36
52
  export type InstallSkillHost = "claude-code" | "cursor" | "all";
37
53
  export interface InstallSkillOptions {
@@ -47,6 +63,24 @@ export interface InstallSkillOptions {
47
63
  force?: boolean;
48
64
  /** Don't write — just describe what would happen. */
49
65
  dryRun?: boolean;
66
+ /**
67
+ * Opt into redundancy gating: skip copies the globally-installed Hyperframes
68
+ * skill already provides. The automatic `vibe init` path sets this so the
69
+ * scaffold stays lean; the explicit `vibe scene install-skill` path leaves it
70
+ * false to fully materialize editable copies (the "eject"). Default false.
71
+ */
72
+ lean?: boolean;
73
+ /**
74
+ * Caller signals a root-reading host (codex / aider / gemini / opencode) is
75
+ * present, so the root `SKILL.md` + `references/` must be written even when
76
+ * the global Claude skill exists. Default false. Only consulted when `lean`.
77
+ */
78
+ rootReaderHostPresent?: boolean;
79
+ /**
80
+ * Override the global-skill probe (testing seam). Defaults to
81
+ * {@link hasGlobalHyperframesSkill}. Only consulted when `lean`.
82
+ */
83
+ hasGlobalSkill?: boolean;
50
84
  }
51
85
  export type InstallSkillFileStatus = "wrote" | "skipped-exists" | "would-write" | "would-skip-exists";
52
86
  export interface InstallSkillFileAction {
@@ -59,6 +93,13 @@ export interface InstallSkillResult {
59
93
  bundleVersion: string;
60
94
  files: InstallSkillFileAction[];
61
95
  }
96
+ /**
97
+ * Map an `AgentHostId[]` to whether a root-reading host is present. Codex,
98
+ * Aider, Gemini, and OpenCode read the project-root `SKILL.md` via AGENTS.md
99
+ * prose — they have no global-skill mechanism, so the root files must be
100
+ * written for them even when the global Claude skill exists.
101
+ */
102
+ export declare function deriveRootReaderPresent(detected: AgentHostId[]): boolean;
62
103
  /**
63
104
  * Install Hyperframes skill files at the project + host-specific paths.
64
105
  *
@@ -1,4 +1,4 @@
1
- import type { SceneAspect } from "./scene-project.js";
1
+ import type { SceneAspect, SceneKind } from "./scene-project.js";
2
2
  export declare const VIBE_CONFIG_FILENAME = "vibe.config.json";
3
3
  export declare const LEGACY_VIBE_PROJECT_FILENAME = "vibe.project.yaml";
4
4
  export type CompositionEngine = "hyperframes";
@@ -11,6 +11,8 @@ export interface VibeProjectConfigV1 {
11
11
  schemaVersion: "1";
12
12
  name: string;
13
13
  aspect: SceneAspect;
14
+ /** Project kind — drives which pipeline stages run + the default composer. */
15
+ kind: SceneKind;
14
16
  defaults: {
15
17
  sceneDurationSec: number;
16
18
  narrationPaddingSec: number;
@@ -49,12 +51,14 @@ export interface LoadedProjectConfig {
49
51
  export declare function defaultProjectConfig(opts: {
50
52
  name: string;
51
53
  aspect?: SceneAspect;
54
+ kind?: SceneKind;
52
55
  sceneDurationSec?: number;
53
56
  }): VibeProjectConfigV1;
54
57
  export declare function mergeProjectConfig(parsed: Partial<VibeProjectConfigV1> | null | undefined, fallback: VibeProjectConfigV1): VibeProjectConfigV1;
55
58
  export declare function projectConfigJson(opts: {
56
59
  name: string;
57
60
  aspect?: SceneAspect;
61
+ kind?: SceneKind;
58
62
  sceneDurationSec?: number;
59
63
  }): string;
60
64
  export declare function readProjectConfig(projectDir: string): Promise<LoadedProjectConfig>;
@@ -0,0 +1 @@
1
+ export {};
@@ -22,6 +22,19 @@ export interface RootSyncPlan {
22
22
  nextHtml?: string;
23
23
  totalDurationSec?: number;
24
24
  }
25
+ export interface ExpectedRootSync {
26
+ block: string;
27
+ totalDurationSec: number;
28
+ audioRefs: Array<{
29
+ beatId: string;
30
+ kind: "narration" | "music";
31
+ src: string;
32
+ start: number;
33
+ duration: number;
34
+ trackIndex: number;
35
+ volume?: number;
36
+ }>;
37
+ }
25
38
  export declare function createRootSyncPlan(opts: {
26
39
  projectDir: string;
27
40
  beats: RootSyncBeatInput[];
@@ -58,3 +71,15 @@ export declare function resolveSyncedBeatDuration(opts: {
58
71
  * {@link loadProjectRootSyncBeats}.
59
72
  */
60
73
  export declare function resolveProjectBeatDurations(projectDir: string): Promise<Map<string, number>>;
74
+ /**
75
+ * The managed `<!-- vibe-scene-build -->` block is the SINGLE source of scene
76
+ * clip refs + narration/music audio. Custom elements (e.g. a `bg-video`,
77
+ * overlay decorations) are left untouched.
78
+ *
79
+ * Fast path: one clean block and no stray managed refs → in-place replace (no
80
+ * churn). Otherwise (zero/duplicate blocks, or stray `scene-*`/`narration-*`/
81
+ * `music-*` refs from a hand-edited root) → strip every managed block AND every
82
+ * stray managed ref, then insert exactly one block. This makes the sync
83
+ * idempotent and structurally unable to double-wire narration audio.
84
+ */
85
+ export declare function applyRootSyncHtml(html: string, expected: ExpectedRootSync): string;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @module _shared/scene-assemble
3
+ *
4
+ * The audio-assemble stage: lay a scene project's `<audio>` elements onto an
5
+ * already-rendered (silent) video in one FFmpeg pass. The Hyperframes producer
6
+ * emits silent video — sub-composition `<audio>` elements are not captured — so
7
+ * we scan the project ourselves and mux them in with `-c:v copy` (no re-encode).
8
+ *
9
+ * Extracted from `executeSceneRender` so the mux is a first-class, independently
10
+ * runnable step: `vibe render --silent` emits silent video, `vibe assemble` adds
11
+ * the audio. The default `vibe render` still renders + assembles in one shot.
12
+ */
13
+ import type { RenderFormat } from "./scene-render.js";
14
+ export interface SceneAssembleOptions {
15
+ /** Project directory (the Hyperframes scene project). */
16
+ projectDir: string;
17
+ /** Root composition file relative to projectDir (default: "index.html"). */
18
+ root?: string;
19
+ /** The rendered (silent) video to mux audio into. Relative paths resolve against projectDir. */
20
+ videoPath: string;
21
+ format?: RenderFormat;
22
+ /**
23
+ * Known total video duration in seconds (e.g. `job.totalFrames / fps`). When
24
+ * omitted, it is ffprobed from {@link videoPath}; the mux uses it to cap audio.
25
+ */
26
+ videoDuration?: number;
27
+ onProgress?: (pct: number, stage: string) => void;
28
+ }
29
+ export interface SceneAssembleResult {
30
+ success: boolean;
31
+ /** Number of `<audio>` elements found across the project. */
32
+ audioCount: number;
33
+ /** True when at least one audio track was muxed into the video. */
34
+ audioMuxApplied: boolean;
35
+ /** Non-fatal warning when the mux failed or threw (video left untouched). */
36
+ audioMuxWarning?: string;
37
+ /** Absolute path to the (in-place-updated) video. */
38
+ outputPath: string;
39
+ /** Set when {@link success} is false (missing inputs). */
40
+ error?: string;
41
+ }
42
+ /**
43
+ * Scan the project's audio and mux it onto {@link SceneAssembleOptions.videoPath}
44
+ * in place. Best-effort on the mux itself: a scan/mux failure becomes a warning,
45
+ * not a hard error. Returns `success:false` only when a required input is missing.
46
+ */
47
+ export declare function executeSceneAssemble(opts: SceneAssembleOptions): Promise<SceneAssembleResult>;
@@ -0,0 +1 @@
1
+ export {};
@@ -247,7 +247,7 @@ export interface SceneBuildOptions {
247
247
  * based on env keys (claude > gemini > openai). Pass an explicit value
248
248
  * to require that provider's key.
249
249
  */
250
- composer?: ComposerProvider;
250
+ composer?: ComposerProvider | "template";
251
251
  skipNarration?: boolean;
252
252
  skipBackdrop?: boolean;
253
253
  skipRender?: boolean;
@@ -302,9 +302,9 @@ export interface SceneBuildOptions {
302
302
  * - `failed` — primitives, compose, or render errored. {@link error}
303
303
  * carries the message; {@link beats} reflects partial state.
304
304
  */
305
- export type SceneBuildPhase = "done" | "assets-only" | "pending-jobs" | "compose-only" | "sync-only" | "render-only" | "needs-author" | "failed";
305
+ export type SceneBuildPhase = "done" | "assets-only" | "transcript-only" | "pending-jobs" | "compose-only" | "sync-only" | "render-only" | "needs-author" | "failed";
306
306
  export type BuildWorkflowStatus = "done" | "running" | "needs-author" | "failed" | "ready";
307
- export type BuildCurrentStage = "assets" | "compose" | "sync" | "render" | "done";
307
+ export type BuildCurrentStage = "assets" | "transcript" | "compose" | "sync" | "render" | "done";
308
308
  export interface BuildBeatSummary {
309
309
  total: number;
310
310
  assetsReady: number;
@@ -353,6 +353,7 @@ export interface SceneBuildResult {
353
353
  composePrompts?: {
354
354
  skillReference: string | null;
355
355
  designReference: string;
356
+ compositionReference: string | null;
356
357
  storyboardReference: string;
357
358
  compositionsDir: string;
358
359
  instructions: string[];
@@ -366,7 +367,7 @@ export interface SceneBuildResult {
366
367
  beatSummary?: BuildBeatSummary;
367
368
  estimatedCostUsd?: number;
368
369
  costUsd?: number;
369
- stageReports?: Record<"assets" | "compose" | "sync" | "render", StageReport>;
370
+ stageReports?: Record<"assets" | "transcript" | "compose" | "sync" | "render", StageReport>;
370
371
  sceneRepair?: BuildSceneRepairSummary;
371
372
  jobs?: JobRecord[];
372
373
  warnings?: string[];
@@ -499,7 +500,7 @@ declare function toBuildReport(projectDir: string, result: SceneBuildResult): {
499
500
  }[];
500
501
  outputPath: string | undefined;
501
502
  sceneRepair: BuildSceneRepairSummary;
502
- stageReports: Record<"assets" | "compose" | "sync" | "render", StageReport> | undefined;
503
+ stageReports: Record<"assets" | "compose" | "sync" | "render" | "transcript", StageReport> | undefined;
503
504
  warnings: string[];
504
505
  retryWith: string[];
505
506
  totalLatencyMs: number;