@vibeframe/mcp-server 0.108.0 → 0.108.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.
Files changed (2) hide show
  1. package/dist/index.js +72 -10
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -25267,6 +25267,8 @@ imageProvider: openai
25267
25267
 
25268
25268
  Edit these beats before running \`vibe build\`. Each beat starts with
25269
25269
  YAML cues that drive narration, backdrop generation, and timing.
25270
+ Pacing: keep beats 6-15 seconds (split longer ones); a 90s video should
25271
+ have 6-8 beats, not 3 long ones.
25270
25272
 
25271
25273
  ## Beat hook \u2014 Hook
25272
25274
 
@@ -450075,6 +450077,29 @@ function filterSubCompFalsePositives(findings, isSubComposition) {
450075
450077
  if (!isSubComposition) return noEpsilon;
450076
450078
  return noEpsilon.filter((f2) => !SUB_COMP_FALSE_POSITIVES.has(f2.code));
450077
450079
  }
450080
+ function findInternalPhaseClipFindings(html) {
450081
+ const findings = [];
450082
+ for (const tagMatch of html.matchAll(/<[a-z][a-z0-9-]*\s[^>]*>/gi)) {
450083
+ const tag = tagMatch[0];
450084
+ if (!/class="[^"]*\bclip\b[^"]*"/i.test(tag)) continue;
450085
+ const startAttr = tag.match(/data-start="([^"]+)"/i);
450086
+ if (!startAttr) continue;
450087
+ const start = Number(startAttr[1]);
450088
+ if (!Number.isFinite(start) || start === 0) continue;
450089
+ findings.push({
450090
+ code: "internal_phase_clip_unsupported",
450091
+ severity: "error",
450092
+ message: `Inner .clip has data-start="${startAttr[1]}". The renderer does not toggle internal clip visibility inside sub-compositions, so phased clips render ALL phases at once (overlapping text).`,
450093
+ fixHint: 'Give every inner .clip data-start="0" spanning the full beat, and drive phase changes with GSAP autoAlpha instead (animate the previous phase out, the next one in).',
450094
+ snippet: tag.slice(0, 120)
450095
+ });
450096
+ }
450097
+ return findings;
450098
+ }
450099
+ function withVibeframeSubCompFindings(findings, html, isSubComposition) {
450100
+ if (!isSubComposition) return findings;
450101
+ return [...findings, ...findInternalPhaseClipFindings(html)];
450102
+ }
450078
450103
  function applyMechanicalFixes(html, findings) {
450079
450104
  const fixedCodes = /* @__PURE__ */ new Set();
450080
450105
  let updated = html;
@@ -450120,7 +450145,11 @@ async function runProjectLint(opts) {
450120
450145
  source: "projectDir"
450121
450146
  };
450122
450147
  const raw2 = runHyperframeLint(prepared);
450123
- const findings = filterSubCompFalsePositives(raw2.findings, isSub);
450148
+ const findings = withVibeframeSubCompFindings(
450149
+ filterSubCompFalsePositives(raw2.findings, isSub),
450150
+ html,
450151
+ isSub
450152
+ );
450124
450153
  if (opts.fix && findings.length > 0) {
450125
450154
  const { html: nextHtml, fixedCodes } = applyMechanicalFixes(html, findings);
450126
450155
  if (fixedCodes.length > 0) {
@@ -450130,7 +450159,11 @@ async function runProjectLint(opts) {
450130
450159
  files.push({
450131
450160
  file: rel,
450132
450161
  isSubComposition: isSub,
450133
- findings: filterSubCompFalsePositives(reLinted.findings, isSub)
450162
+ findings: withVibeframeSubCompFindings(
450163
+ filterSubCompFalsePositives(reLinted.findings, isSub),
450164
+ nextHtml,
450165
+ isSub
450166
+ )
450134
450167
  });
450135
450168
  continue;
450136
450169
  }
@@ -469633,8 +469666,14 @@ ${finalDurationBullet}
469633
469666
  Animate the backdrop/media plane instead; let text enter briefly, then hold
469634
469667
  still at its final CSS position. Continuous transforms on text ancestors can
469635
469668
  create subpixel shimmer in screenshot-captured renders.
469636
- - Timed children inside the composition have \`class="clip"\` plus
469637
- \`data-start\`, \`data-duration\`, \`data-track-index\`.
469669
+ - Inner \`.clip\` elements carry \`data-start\`, \`data-duration\`,
469670
+ \`data-track-index\` \u2014 but **every inner clip MUST be full-window:
469671
+ \`data-start="0"\` spanning the whole beat.** The renderer does NOT toggle
469672
+ internal clip visibility inside sub-compositions, so "phase clips" with a
469673
+ non-zero \`data-start\` render ALL phases at once and text stacks on top of
469674
+ itself. For multi-phase beats, drive phase changes with GSAP \`autoAlpha\`
469675
+ inside full-window clips: animate phase A out (\`autoAlpha: 0\`), then
469676
+ phase B in, on the same timeline.
469638
469677
  - If \`assets/backdrop-${ctx.beat.id}.png\` exists, use that local file as the
469639
469678
  full-frame visual backdrop. The exact path string is
469640
469679
  \`assets/backdrop-${ctx.beat.id}.png\`; do NOT prefix it with \`../\` or \`./\`
@@ -469648,7 +469687,9 @@ ${finalDurationBullet}
469648
469687
  narration.
469649
469688
  - Keep a meaningful visual progression across the full beat duration: line
469650
469689
  tracing, gentle camera drift, staged labels, value cards, or parallax. Avoid
469651
- a completed static frame holding silently for most of the beat.
469690
+ a completed static frame holding silently for most of the beat. For longer
469691
+ beats, progress through content phases with GSAP autoAlpha (old phase out,
469692
+ new phase in) \u2014 never with inner clip windows.
469652
469693
  - Do not import external font or image URLs. Use the project DESIGN.md font
469653
469694
  choice when explicit; otherwise use \`Inter\`, which is available in the
469654
469695
  deterministic render font map.
@@ -469823,7 +469864,11 @@ function lintBeatHtml(html, beatId) {
469823
469864
  source: "projectDir"
469824
469865
  };
469825
469866
  const raw2 = runHyperframeLint(prepared);
469826
- const findings = filterSubCompFalsePositives(raw2.findings, true);
469867
+ const findings = withVibeframeSubCompFindings(
469868
+ filterSubCompFalsePositives(raw2.findings, true),
469869
+ html,
469870
+ true
469871
+ );
469827
469872
  return {
469828
469873
  errorCount: findings.filter((f2) => f2.severity === "error").length,
469829
469874
  warningCount: findings.filter((f2) => f2.severity === "warning").length,
@@ -470262,7 +470307,8 @@ function buildInstructions(args) {
470262
470307
  lines.push(`2. Read \`DESIGN.md\` for project-specific palette, typography, motion signature.`);
470263
470308
  lines.push(`3. For each beat in the \`beats\` array below, author HTML at \`outputPath\` matching the \`userPrompt\`. The beat \`body\` carries the narrative + visual + animation intent; \`cues\` carries machine-readable per-beat overrides (narration, duration, backdrop, voice).`);
470264
470309
  lines.push(`3b. Use each beat's \`finalDurationSec\` (narration-synced) for \`data-duration\` and timeline anchors when present \u2014 NOT the storyboard \`duration\`, which is only the minimum. Scenes composed at the storyboard duration end early and render black tails.`);
470265
- lines.push(`3c. If your environment cannot write files (e.g. Claude Desktop / MCP-only hosts), author each beat's HTML and submit it with the \`scene_submit\` tool (beat id + html). It validates with the same Hyperframes lint and writes the file for you; on lint errors it returns the findings without writing \u2014 fix and resubmit.`);
470310
+ lines.push(`3c. Never give inner \`.clip\` elements a non-zero \`data-start\` \u2014 the renderer does not toggle internal clip visibility inside sub-compositions, so phased clips render all phases at once (overlapping text). Use full-window clips and GSAP autoAlpha phase transitions instead. Also keep beats 6-15s; split anything longer in the storyboard first.`);
470311
+ lines.push(`3d. If your environment cannot write files (e.g. Claude Desktop / MCP-only hosts), author each beat's HTML and submit it with the \`scene_submit\` tool (beat id + html). It validates with the same Hyperframes lint and writes the file for you; on lint errors it returns the findings without writing \u2014 fix and resubmit.`);
470266
470312
  if (args.beatCount > 1) {
470267
470313
  lines.push(`4. After authoring all ${args.beatCount} beat(s), run \`vibe scene lint --fix\` to validate. Fix any remaining errors by editing the HTML directly.`);
470268
470314
  } else if (args.filtered) {
@@ -470584,7 +470630,11 @@ function lintHtml(html, rel, isSub) {
470584
470630
  source: "projectDir"
470585
470631
  };
470586
470632
  const raw2 = runHyperframeLint(prepared);
470587
- return filterSubCompFalsePositives(raw2.findings, isSub);
470633
+ return withVibeframeSubCompFindings(
470634
+ filterSubCompFalsePositives(raw2.findings, isSub),
470635
+ html,
470636
+ isSub
470637
+ );
470588
470638
  }
470589
470639
  function lintFilesToIssues(files) {
470590
470640
  const issues = [];
@@ -471655,6 +471705,16 @@ function validateStoryboardMarkdown(markdown) {
471655
471705
  }
471656
471706
  }
471657
471707
  }
471708
+ for (const beat of parsed.beats) {
471709
+ if (beat.duration !== void 0 && beat.duration > MAX_RECOMMENDED_BEAT_SEC) {
471710
+ issues.push({
471711
+ severity: "warning",
471712
+ code: "BEAT_DURATION_TOO_LONG",
471713
+ beatId: beat.id,
471714
+ message: `Beat "${beat.id}" is ${beat.duration}s \u2014 beats longer than ${MAX_RECOMMENDED_BEAT_SEC}s render static and overstuffed. Split it into 6-15s beats (a 90s video should have 6-8 beats).`
471715
+ });
471716
+ }
471717
+ }
471658
471718
  return {
471659
471719
  ok: !issues.some((i2) => i2.severity === "error"),
471660
471720
  beats: parsed.beats,
@@ -471753,7 +471813,7 @@ function readLeadingCueBlock(body) {
471753
471813
  return { full: match2[0], error: error instanceof Error ? error.message : String(error) };
471754
471814
  }
471755
471815
  }
471756
- var import_yaml5, STORYBOARD_CUE_KEYS, HEADING_RE2, LEADING_CUE_RE, ALLOWED_CUE_KEYS, STRING_CUE_KEYS;
471816
+ var import_yaml5, STORYBOARD_CUE_KEYS, HEADING_RE2, LEADING_CUE_RE, ALLOWED_CUE_KEYS, STRING_CUE_KEYS, MAX_RECOMMENDED_BEAT_SEC;
471757
471817
  var init_storyboard_edit = __esm({
471758
471818
  "../cli/src/commands/_shared/storyboard-edit.ts"() {
471759
471819
  "use strict";
@@ -471773,6 +471833,7 @@ var init_storyboard_edit = __esm({
471773
471833
  LEADING_CUE_RE = /^(\s*)```ya?ml\s*\n([\s\S]*?)\n```\s*(?:\n|$)/;
471774
471834
  ALLOWED_CUE_KEYS = new Set(STORYBOARD_CUE_KEYS);
471775
471835
  STRING_CUE_KEYS = /* @__PURE__ */ new Set(["narration", "backdrop", "video", "motion", "voice", "music", "asset"]);
471836
+ MAX_RECOMMENDED_BEAT_SEC = 15;
471776
471837
  }
471777
471838
  });
471778
471839
 
@@ -491942,7 +492003,8 @@ function revisionMessages(opts) {
491942
492003
  "The JSON object must have: storyboardMd string, summary string, changedBeats string[], warnings string[].",
491943
492004
  "Preserve existing frontmatter, beat ids, cue YAML keys, and useful prose unless the user asks to change structure.",
491944
492005
  "Keep cue YAML valid. Allowed cue keys are duration, narration, backdrop, video, motion, voice, music, asset.",
491945
- "If a target duration is supplied, every beat must have a positive duration cue and durations must sum to that target."
492006
+ "If a target duration is supplied, every beat must have a positive duration cue and durations must sum to that target.",
492007
+ "Aim for 6-15 seconds per beat and never exceed ~15s \u2014 long beats render as static, overstuffed scenes. A 90-second video should have 6-8 beats."
491946
492008
  ].join("\n");
491947
492009
  const repairBlock = opts.invalidStoryboard ? [
491948
492010
  "The previous revision failed validation. Repair it.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/mcp-server",
3
- "version": "0.108.0",
3
+ "version": "0.108.1",
4
4
  "description": "VibeFrame MCP Server - AI-native video editing via Model Context Protocol",
5
5
  "type": "module",
6
6
  "bin": {
@@ -60,8 +60,8 @@
60
60
  "tsx": "^4.21.0",
61
61
  "typescript": "^5.3.3",
62
62
  "vitest": "^1.2.2",
63
- "@vibeframe/cli": "0.108.0",
64
- "@vibeframe/core": "0.108.0"
63
+ "@vibeframe/core": "0.108.1",
64
+ "@vibeframe/cli": "0.108.1"
65
65
  },
66
66
  "engines": {
67
67
  "node": ">=20"