@vibeframe/cli 0.113.10 → 0.113.11

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.
@@ -96,6 +96,13 @@ export interface CreateBuildPlanOptions {
96
96
  skipVideo?: boolean;
97
97
  skipKeyframe?: boolean;
98
98
  skipMusic?: boolean;
99
+ /**
100
+ * Skip Whisper word-level transcription of narration. Recorded for plan
101
+ * fidelity; transcription is a negligible-cost step (~$0.002/beat) that only
102
+ * runs when a beat has narration and an OpenAI key is configured, so it is
103
+ * not itemised as a separate cost line.
104
+ */
105
+ skipTranscript?: boolean;
99
106
  ttsProvider?: string;
100
107
  voice?: string;
101
108
  imageProvider?: string;
@@ -57,6 +57,13 @@ export interface ComposePromptsBeat {
57
57
  * agentic and batch paths produce equivalent HTML.
58
58
  */
59
59
  userPrompt: string;
60
+ /**
61
+ * Project-relative path to the beat's Whisper word-level transcript
62
+ * (`assets/transcript-<id>.json`), present only when the asset stage
63
+ * transcribed this beat's narration. The host agent can read it for exact
64
+ * word timings; the same timings are already summarised in `userPrompt`.
65
+ */
66
+ transcriptPath?: string;
60
67
  /** True when `compositions/scene-<id>.html` already exists on disk. */
61
68
  exists: boolean;
62
69
  }
@@ -27,6 +27,7 @@
27
27
  import { type LintFinding } from "./scene-lint.js";
28
28
  import { type Beat } from "./storyboard-parse.js";
29
29
  import { type ComposerProvider } from "./composer-resolve.js";
30
+ import type { SceneTranscriptWord } from "./scene-html-emit.js";
30
31
  /** Effort level → token caps. Model is per-provider (see MODEL_SETTINGS_BY_PROVIDER). */
31
32
  export type ComposeEffort = "low" | "medium" | "high";
32
33
  export interface ComposeBeatContext {
@@ -71,6 +72,14 @@ export interface ComposeBeatContext {
71
72
  * duration is then only the minimum and is annotated as superseded.
72
73
  */
73
74
  finalDurationSec?: number;
75
+ /**
76
+ * Whisper word-level timings for this beat's narration audio, read from
77
+ * `assets/transcript-<id>.json` by the asset stage. When present,
78
+ * {@link buildUserPrompt} surfaces a compact timing table so the composer
79
+ * can sync caption / kinetic-type word reveals to speech. Folded into the
80
+ * user prompt, so the compose cache key invalidates when timings change.
81
+ */
82
+ transcript?: SceneTranscriptWord[];
74
83
  }
75
84
  export interface ComposeBeatResult {
76
85
  /** Generated HTML (from cache or fresh API call). */
@@ -94,8 +103,28 @@ export interface ComposeBeatResult {
94
103
  }
95
104
  /** Build the system prompt — skill bundle + DESIGN.md hard-gate. */
96
105
  export declare function buildSystemPrompt(ctx: Pick<ComposeBeatContext, "skillBundle" | "designMd">): string;
106
+ /**
107
+ * Word-level timing entries above this count are downgraded to coarse,
108
+ * phrase-level anchors in the prompt. A token-budget guard: a 60s narration
109
+ * can exceed 150 words, and inlining every `[start, "word"]` pair bloats the
110
+ * prompt (and the cache key) for little composer benefit past a point.
111
+ */
112
+ export declare const TRANSCRIPT_PROMPT_MAX_WORDS = 120;
113
+ /**
114
+ * Render narration word timings as a compact, deterministic prompt section.
115
+ * Token-budget guard (the `oversized` case is the reason this is a function,
116
+ * not an inline template):
117
+ * - no transcript → `""` (section omitted entirely; prompt unchanged).
118
+ * - ≤ {@link TRANSCRIPT_PROMPT_MAX_WORDS} → full word-level `[start, "word"]`
119
+ * table the composer can map 1:1 to GSAP reveals.
120
+ * - more → group into ~`MAX/2` phrase anchors (start time of each phrase),
121
+ * tagged "approximate" so the composer paces reveals across the beat
122
+ * rather than over-trusting per-word truth.
123
+ * Pure and stable for a given input → the compose cache stays consistent.
124
+ */
125
+ export declare function formatTranscriptSection(transcript: SceneTranscriptWord[] | undefined, maxWords?: number): string;
97
126
  /** Build the user prompt — instructions + storyboard global + beat body. */
98
- export declare function buildUserPrompt(ctx: Pick<ComposeBeatContext, "beat" | "storyboardGlobal" | "retryFeedback" | "finalDurationSec">): string;
127
+ export declare function buildUserPrompt(ctx: Pick<ComposeBeatContext, "beat" | "storyboardGlobal" | "retryFeedback" | "finalDurationSec" | "transcript">): string;
99
128
  /**
100
129
  * Compute the sha256 cache key for a (provider, model, system, user) tuple.
101
130
  * Provider id is folded in so switching `--composer` doesn't serve cached
@@ -64,6 +64,19 @@ export type SceneBuildProgressEvent = {
64
64
  type: "narration-skipped";
65
65
  beatId: string;
66
66
  reason: string;
67
+ } | {
68
+ type: "transcript-generated";
69
+ beatId: string;
70
+ path: string;
71
+ wordCount: number;
72
+ } | {
73
+ type: "transcript-cached";
74
+ beatId: string;
75
+ path: string;
76
+ } | {
77
+ type: "transcript-skipped";
78
+ beatId: string;
79
+ reason: string;
67
80
  } | {
68
81
  type: "backdrop-cached";
69
82
  beatId: string;
@@ -254,6 +267,13 @@ export interface SceneBuildOptions {
254
267
  skipKeyframe?: boolean;
255
268
  /** Skip music generation even when beats declare music cues. */
256
269
  skipMusic?: boolean;
270
+ /**
271
+ * Skip Whisper word-level transcription of generated narration. Default is
272
+ * to transcribe whenever narration exists and an OpenAI key is configured,
273
+ * writing `assets/transcript-<beat>.json` so the composer can sync caption /
274
+ * kinetic-type reveals to speech. Pass true to opt out (no transcribe cost).
275
+ */
276
+ skipTranscript?: boolean;
257
277
  /** OpenAI image quality — see `vibe generate image --quality`. */
258
278
  imageQuality?: "standard" | "hd";
259
279
  /** OpenAI image size. Default 1536x1024 for cinematic 16:9-ish framing. */
@@ -0,0 +1,23 @@
1
+ import type { SceneTranscriptWord } from "./scene-html-emit.js";
2
+ export interface TranscribeNarrationOptions {
3
+ /** OpenAI API key for Whisper. */
4
+ apiKey: string;
5
+ /** Optional BCP-47 language hint passed to Whisper (auto-detect when unset). */
6
+ language?: string;
7
+ }
8
+ /**
9
+ * Transcribe a narration audio file to word-level timings. Returns an empty
10
+ * array on ANY failure (no words, API error, bad audio) so callers can treat
11
+ * the absence of timings as "no word-sync available" rather than abort.
12
+ */
13
+ export declare function transcribeNarrationWords(audioAbsPath: string, opts: TranscribeNarrationOptions): Promise<SceneTranscriptWord[]>;
14
+ /** Canonical per-beat transcript path, relative to the project root. */
15
+ export declare function beatTranscriptRelPath(beatId: string): string;
16
+ /**
17
+ * Read a previously generated `assets/transcript-<beatId>.json` from disk.
18
+ * Returns `undefined` when the file is missing or malformed — compose then
19
+ * proceeds without word timings (identical to a project that never
20
+ * transcribed). Validates each entry's shape so a hand-edited or partial file
21
+ * can't inject `NaN`/`undefined` timings into the prompt.
22
+ */
23
+ export declare function readBeatTranscript(projectDir: string, beatId: string): Promise<SceneTranscriptWord[] | undefined>;