@vibeframe/cli 0.106.8 → 0.107.1

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.
@@ -38,8 +38,15 @@ export interface ComposePromptsBeat {
38
38
  heading: string;
39
39
  /** Path the agent should write the composition to (relative to projectDir). */
40
40
  outputPath: string;
41
- /** Beat duration in seconds (from cues or `### Beat duration` subsection), if known. */
41
+ /** Storyboard beat duration in seconds (the MINIMUM — see finalDurationSec). */
42
42
  duration?: number;
43
+ /**
44
+ * Narration-synced FINAL beat duration in seconds — the storyboard duration
45
+ * stretched to cover generated narration audio. Use THIS for
46
+ * `data-duration` and timeline anchors when present; using the storyboard
47
+ * `duration` produces scenes that end early and render black tails.
48
+ */
49
+ finalDurationSec?: number;
43
50
  /** Per-beat YAML cues parsed from the leading code block of the body. */
44
51
  cues?: Record<string, unknown>;
45
52
  /** Beat body markdown (with the leading `\`\`\`yaml` cue block stripped). */
@@ -62,6 +62,15 @@ export interface ComposeBeatContext {
62
62
  * Tests pass a temp dir.
63
63
  */
64
64
  cacheDir?: string;
65
+ /**
66
+ * Narration-synced FINAL beat duration in seconds. The root timeline
67
+ * stretches each beat to cover its generated narration
68
+ * (see resolveSyncedBeatDuration in root-sync.ts); when set, the prompt
69
+ * pins `data-duration` and every timeline anchor to this exact value so the
70
+ * scene cannot end early and render a black tail. The storyboard cue
71
+ * duration is then only the minimum and is annotated as superseded.
72
+ */
73
+ finalDurationSec?: number;
65
74
  }
66
75
  export interface ComposeBeatResult {
67
76
  /** Generated HTML (from cache or fresh API call). */
@@ -86,7 +95,7 @@ export interface ComposeBeatResult {
86
95
  /** Build the system prompt — skill bundle + DESIGN.md hard-gate. */
87
96
  export declare function buildSystemPrompt(ctx: Pick<ComposeBeatContext, "skillBundle" | "designMd">): string;
88
97
  /** Build the user prompt — instructions + storyboard global + beat body. */
89
- export declare function buildUserPrompt(ctx: Pick<ComposeBeatContext, "beat" | "storyboardGlobal" | "retryFeedback">): string;
98
+ export declare function buildUserPrompt(ctx: Pick<ComposeBeatContext, "beat" | "storyboardGlobal" | "retryFeedback" | "finalDurationSec">): string;
90
99
  /**
91
100
  * Compute the sha256 cache key for a (provider, model, system, user) tuple.
92
101
  * Provider id is folded in so switching `--composer` doesn't serve cached
@@ -37,3 +37,22 @@ export declare function syncRootComposition(opts: {
37
37
  rootRel?: string;
38
38
  }): Promise<RootSyncPlan>;
39
39
  export declare function loadProjectRootSyncBeats(projectDir: string): Promise<RootSyncBeatInput[]>;
40
+ /**
41
+ * Canonical FINAL beat duration: the storyboard duration acts as a minimum
42
+ * that gets stretched to cover the generated narration audio (+0.5s pad).
43
+ * This is the duration the root timeline allocates per beat — and therefore
44
+ * the duration scenes must be composed at (data-duration + timeline anchors),
45
+ * or they go dark for the stretched tail.
46
+ */
47
+ export declare function resolveSyncedBeatDuration(opts: {
48
+ projectDir: string;
49
+ beatDuration?: number;
50
+ narrationPath?: string;
51
+ sceneDurationSec?: number;
52
+ }): Promise<number>;
53
+ /**
54
+ * Final narration-synced duration per beat for a project on disk. Reads
55
+ * STORYBOARD.md + build-report.json + narration asset fallbacks via
56
+ * {@link loadProjectRootSyncBeats}.
57
+ */
58
+ export declare function resolveProjectBeatDurations(projectDir: string): Promise<Map<string, number>>;
@@ -70,8 +70,19 @@ export declare function discoverSceneFiles(opts: {
70
70
  subs: string[];
71
71
  }>;
72
72
  /**
73
- * Drop findings that producer's lint emits as inverse-rules for sub-comp
74
- * files. Other findings pass through unchanged.
73
+ * IEEE float noise threshold for clip-overlap findings. Authored timeline
74
+ * values are exact to 2dp, but the producer computes each clip's end as
75
+ * `start + duration` in binary floats — e.g. 8.26 + 10.98 =
76
+ * 19.240000000000002, which it then flags as "overlapping" a clip starting
77
+ * at 19.24. Anything under 5ms is float noise, not a real overlap.
78
+ */
79
+ export declare const OVERLAP_EPSILON_SEC = 0.005;
80
+ /** True for overlapping_clips_same_track findings whose overlap is float noise. */
81
+ export declare function isEpsilonOverlapFinding(finding: LintFinding): boolean;
82
+ /**
83
+ * Drop known false positives: float-epsilon clip overlaps (all files) and
84
+ * the producer findings that are inverse-rules for sub-comp files. Other
85
+ * findings pass through unchanged.
75
86
  */
76
87
  export declare function filterSubCompFalsePositives(findings: LintFinding[], isSubComposition: boolean): LintFinding[];
77
88
  /**
@@ -1,6 +1,15 @@
1
1
  import type { BuildAssetKind } from "./build-cache.js";
2
2
  export type JobStatus = "queued" | "running" | "completed" | "failed" | "cancelled" | "unknown";
3
- export type JobType = "generate-video" | "generate-music";
3
+ /**
4
+ * `generate-video` / `generate-music` are provider-backed jobs refreshed by
5
+ * polling the provider. `build` / `render` are LOCAL jobs: long-running MCP
6
+ * tool calls promoted to the background inside the MCP server process — their
7
+ * records are updated in-process, so refresh just reads the file (plus orphan
8
+ * detection when the owning process died).
9
+ */
10
+ export type JobType = "generate-video" | "generate-music" | "build" | "render";
11
+ /** Local in-process jobs (see {@link JobType}). */
12
+ export declare function isLocalJob(record: Pick<JobRecord, "jobType">): boolean;
4
13
  export type ProjectWorkflowStatus = "empty" | "ready" | "running" | "needs-author" | "failed" | "done" | "warn";
5
14
  export type ProjectCurrentStage = "init" | "assets" | "compose" | "sync" | "render" | "review" | "done";
6
15
  export interface JobRecord {
@@ -31,6 +40,16 @@ export interface JobRecord {
31
40
  error?: string;
32
41
  promptPreview?: string;
33
42
  retryWith: string[];
43
+ /** Owning process id — local jobs only; used for orphan detection. */
44
+ pid?: number;
45
+ /** Last liveness write by the owning process — local jobs only. */
46
+ heartbeatAt?: string;
47
+ /** Current pipeline stage label (e.g. "compose", "render") — local jobs. */
48
+ stage?: string;
49
+ /** Latest human-readable progress message — local jobs. */
50
+ message?: string;
51
+ /** Full tool result data, written when a local job completes. */
52
+ resultPayload?: Record<string, unknown>;
34
53
  }
35
54
  export interface CreateJobRecordOptions {
36
55
  id?: string;
@@ -56,6 +75,10 @@ export interface CreateJobRecordOptions {
56
75
  canonicalPath?: string;
57
76
  metadataPath?: string;
58
77
  error?: string;
78
+ pid?: number;
79
+ heartbeatAt?: string;
80
+ stage?: string;
81
+ message?: string;
59
82
  }
60
83
  export interface RefreshJobOptions {
61
84
  wait?: boolean;
@@ -166,6 +189,8 @@ export interface JobResult {
166
189
  outputPath?: string;
167
190
  cachePath?: string;
168
191
  error?: string;
192
+ /** Full tool result data for completed local build/render jobs. */
193
+ payload?: Record<string, unknown>;
169
194
  }
170
195
  export interface ProjectBeatReadiness {
171
196
  total: number;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @module _shared/sub-comp-duration-sync
3
+ *
4
+ * Propagate narration-synced beat durations INTO sub-composition HTML.
5
+ *
6
+ * The root sync (root-sync.ts) stretches each beat's clip window in
7
+ * index.html to `max(storyboard duration, narration + 0.5s)` — but scenes
8
+ * composed before the narration existed still carry the storyboard duration
9
+ * in their own markup: the composition root's `data-duration`, end-aligned
10
+ * inner `.clip` windows, and GSAP timeline anchor constants like
11
+ * `const DUR = 7;`. The runtime then ends the scene early and the stretched
12
+ * tail renders BLACK.
13
+ *
14
+ * The compose stage now pins prompts to the final duration
15
+ * (ComposeBeatContext.finalDurationSec), so fresh scenes are born correct.
16
+ * This module is the REPAIR path for already-composed scenes. It is
17
+ * deliberately surgical:
18
+ *
19
+ * 1. Composition root `data-duration` — always rewritten (attribute).
20
+ * 2. End-aligned `.clip` `data-duration` — rewritten when start+duration
21
+ * lands on the OLD beat end (±0.01s); other clips are untouched.
22
+ * 3. GSAP anchor const — rewritten only when exactly one
23
+ * `const NAME = <oldDur>;` exists and NAME is referenced in a timeline
24
+ * call. Anything ambiguous emits a SCENE_TIMELINE_DURATION_OUT_OF_SYNC
25
+ * warning for the host agent instead of risking a bad code edit.
26
+ */
27
+ import type { ReviewIssue } from "./review-report.js";
28
+ import { type RootSyncBeatInput } from "./root-sync.js";
29
+ export declare const SUB_COMP_SYNC_FIX_CODES: {
30
+ readonly rootDuration: "scene_duration_synced";
31
+ readonly clipDurations: "scene_clip_durations_synced";
32
+ readonly timelineConst: "scene_timeline_const_synced";
33
+ };
34
+ export interface SubCompDurationSyncOutcome {
35
+ oldDurationSec?: number;
36
+ newDurationSec: number;
37
+ changed: boolean;
38
+ nextHtml?: string;
39
+ fixCodes: string[];
40
+ issues: ReviewIssue[];
41
+ }
42
+ export interface SubCompDurationSyncFileResult extends SubCompDurationSyncOutcome {
43
+ /** Project-relative path, e.g. "compositions/scene-hook.html". */
44
+ file: string;
45
+ beatId: string;
46
+ }
47
+ /**
48
+ * Pure core: rewrite one sub-composition's internal durations to
49
+ * `newDurationSec`. Returns `changed: false` when the scene already matches
50
+ * (±0.01s) or its root duration can't be located.
51
+ */
52
+ export declare function syncSubCompositionDurationHtml(html: string, beatId: string, newDurationSec: number, fileRel?: string): SubCompDurationSyncOutcome;
53
+ /** I/O wrapper: plan duration sync for every beat's composition file. */
54
+ export declare function createSubCompDurationSyncPlans(opts: {
55
+ projectDir: string;
56
+ beats: RootSyncBeatInput[];
57
+ }): Promise<SubCompDurationSyncFileResult[]>;