@vibeframe/mcp-server 0.113.2 → 0.113.4

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 +196 -14
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -471698,6 +471698,7 @@ var init_storyboard_edit = __esm({
471698
471698
  "narration",
471699
471699
  "backdrop",
471700
471700
  "video",
471701
+ "keyframe",
471701
471702
  "motion",
471702
471703
  "voice",
471703
471704
  "music",
@@ -471707,7 +471708,7 @@ var init_storyboard_edit = __esm({
471707
471708
  HEADING_RE2 = /^##\s+(.+?)\s*$/gm;
471708
471709
  LEADING_CUE_RE = /^(\s*)```ya?ml\s*\n([\s\S]*?)\n```\s*(?:\n|$)/;
471709
471710
  ALLOWED_CUE_KEYS = new Set(STORYBOARD_CUE_KEYS);
471710
- STRING_CUE_KEYS = /* @__PURE__ */ new Set(["narration", "backdrop", "video", "motion", "voice", "music", "asset"]);
471711
+ STRING_CUE_KEYS = /* @__PURE__ */ new Set(["narration", "backdrop", "video", "keyframe", "motion", "voice", "music", "asset"]);
471711
471712
  MAX_RECOMMENDED_BEAT_SEC = 15;
471712
471713
  }
471713
471714
  });
@@ -472626,6 +472627,7 @@ var init_build_asset_reference = __esm({
472626
472627
  narration: [".mp3", ".wav", ".m4a"],
472627
472628
  backdrop: [".png", ".jpg", ".jpeg", ".webp"],
472628
472629
  character: [".png", ".jpg", ".jpeg", ".webp"],
472630
+ keyframe: [".png", ".jpg", ".jpeg", ".webp"],
472629
472631
  video: [".mp4", ".mov", ".webm"],
472630
472632
  music: [".mp3", ".wav", ".m4a"]
472631
472633
  };
@@ -472673,6 +472675,18 @@ function characterCacheDescriptor(opts) {
472673
472675
  ext: "png"
472674
472676
  });
472675
472677
  }
472678
+ function keyframeCacheDescriptor(opts) {
472679
+ return cacheAssetDescriptor("keyframe", {
472680
+ beatId: opts.beatId,
472681
+ cue: opts.cue,
472682
+ provider: opts.provider,
472683
+ quality: opts.quality,
472684
+ size: opts.size,
472685
+ ratio: opts.ratio,
472686
+ characters: opts.characters && opts.characters.length > 0 ? opts.characters : void 0,
472687
+ ext: "png"
472688
+ });
472689
+ }
472676
472690
  function imageRatioForSize(size) {
472677
472691
  switch (size) {
472678
472692
  case "1024x1024":
@@ -472694,6 +472708,8 @@ function videoCacheDescriptor(opts) {
472694
472708
  ratio: "16:9",
472695
472709
  // Omit when empty so existing (character-less) clips keep their cache key.
472696
472710
  characters: opts.characters && opts.characters.length > 0 ? opts.characters : void 0,
472711
+ // Omit when absent so existing (non-keyframe) clips keep their cache key.
472712
+ keyframe: opts.keyframe || void 0,
472697
472713
  ext: "mp4"
472698
472714
  });
472699
472715
  }
@@ -472844,6 +472860,7 @@ async function createBuildPlan(opts) {
472844
472860
  const backdropPrompt = stringOrUndefined3(cue.backdrop);
472845
472861
  const augmentedBackdropPrompt = backdropPrompt ? augmentBackdropPrompt(backdropPrompt) : null;
472846
472862
  const videoPrompt = stringOrUndefined3(cue.video);
472863
+ const keyframePrompt = stringOrUndefined3(cue.keyframe);
472847
472864
  const musicPrompt = stringOrUndefined3(cue.music);
472848
472865
  const genericReference = resolveGenericAssetReference(projectDir, cue.asset);
472849
472866
  const narrationReference = resolveTypedAssetReference(projectDir, "narration", cue.narration);
@@ -472852,7 +472869,7 @@ async function createBuildPlan(opts) {
472852
472869
  const musicReference = resolveTypedAssetReference(projectDir, "music", cue.music) ?? (!musicPrompt && genericReference?.kind === "music" ? genericReference : null);
472853
472870
  const narrationCue = narrationText ?? narrationReference?.raw;
472854
472871
  const backdropCue = backdropPrompt ?? backdropReference?.raw;
472855
- const videoCue = videoPrompt ?? videoReference?.raw;
472872
+ const videoCue = videoPrompt ?? videoReference?.raw ?? keyframePrompt;
472856
472873
  const musicCue = musicPrompt ?? musicReference?.raw;
472857
472874
  const narrationCost = resolved.narration.resolved === "elevenlabs" ? ELEVENLABS_NARRATION_COST_USD : resolved.narration.resolved === "openai" ? OPENAI_NARRATION_COST_USD : 0;
472858
472875
  const narrationCache = narrationText && !narrationReference ? narrationCacheDescriptor({
@@ -472870,11 +472887,20 @@ async function createBuildPlan(opts) {
472870
472887
  size: imageSize2,
472871
472888
  ratio: imageRatio
472872
472889
  }) : null;
472873
- const videoCache = videoPrompt && !videoReference ? videoCacheDescriptor({
472890
+ const videoCache = (videoPrompt || keyframePrompt) && !videoReference ? videoCacheDescriptor({
472874
472891
  beatId: beat.id,
472875
- cue: videoPrompt,
472892
+ cue: videoPrompt ?? keyframePrompt,
472876
472893
  provider: resolved.video.resolved,
472877
- duration: beat.duration
472894
+ duration: beat.duration,
472895
+ keyframe: keyframePrompt
472896
+ }) : null;
472897
+ const keyframeCache2 = keyframePrompt ? keyframeCacheDescriptor({
472898
+ beatId: beat.id,
472899
+ cue: keyframePrompt,
472900
+ provider: resolved.image.resolved,
472901
+ quality: imageQuality,
472902
+ size: imageSize2,
472903
+ ratio: imageRatio
472878
472904
  }) : null;
472879
472905
  const musicCache = musicPrompt && !musicReference ? musicCacheDescriptor({
472880
472906
  beatId: beat.id,
@@ -472911,6 +472937,19 @@ async function createBuildPlan(opts) {
472911
472937
  cost: backdropCostUsd(imageQuality),
472912
472938
  active: includeAssets
472913
472939
  }) : null;
472940
+ const keyframe = keyframePrompt && !opts.skipKeyframe ? assetPlan({
472941
+ kind: "keyframe",
472942
+ beatId: beat.id,
472943
+ cue: keyframePrompt,
472944
+ provider: resolved.image.resolved,
472945
+ path: `assets/keyframe-${beat.id}.png`,
472946
+ cache: keyframeCache2,
472947
+ reference: null,
472948
+ projectDir,
472949
+ force: opts.force,
472950
+ cost: backdropCostUsd(imageQuality),
472951
+ active: includeAssets
472952
+ }) : null;
472914
472953
  const video = videoCue && !opts.skipVideo ? assetPlan({
472915
472954
  kind: "video",
472916
472955
  beatId: beat.id,
@@ -472943,7 +472982,7 @@ async function createBuildPlan(opts) {
472943
472982
  }) : null;
472944
472983
  const compositionPath = `compositions/scene-${beat.id}.html`;
472945
472984
  const compositionExists = existsSync33(join30(projectDir, compositionPath));
472946
- for (const asset of [narration, backdrop, video, music]) {
472985
+ for (const asset of [narration, backdrop, keyframe, video, music]) {
472947
472986
  if (!asset) continue;
472948
472987
  if (asset.referenceError) {
472949
472988
  missing.add("assets");
@@ -472968,6 +473007,8 @@ async function createBuildPlan(opts) {
472968
473007
  if (narration?.willGenerate) noteProviderNeed(resolved.narration, "Narration");
472969
473008
  if (backdrop?.willGenerate && isSupportedBuildImageProvider(resolved.image.resolved))
472970
473009
  noteProviderNeed(resolved.image, "Backdrop generation");
473010
+ if (keyframe?.willGenerate && isSupportedBuildImageProvider(resolved.image.resolved))
473011
+ noteProviderNeed(resolved.image, "Keyframe generation");
472971
473012
  if (video?.willGenerate) noteProviderNeed(resolved.video, "Video generation");
472972
473013
  if (music?.willGenerate) noteProviderNeed(resolved.music, "Music generation");
472973
473014
  if (!compositionExists) missing.add("compositions");
@@ -472981,7 +473022,7 @@ async function createBuildPlan(opts) {
472981
473022
  heading: beat.heading,
472982
473023
  durationSec: beat.duration ?? null,
472983
473024
  cues: cue,
472984
- assets: { narration, backdrop, video, music },
473025
+ assets: { narration, backdrop, keyframe, video, music },
472985
473026
  composition: {
472986
473027
  path: compositionPath,
472987
473028
  exists: compositionExists
@@ -473197,6 +473238,7 @@ function providerResolutionsForPlan(resolved, beats, opts, includeAssets) {
473197
473238
  return [
473198
473239
  includeAssets && !opts.skipNarration && needsProvider("narration") ? resolved.narration : null,
473199
473240
  includeAssets && !opts.skipBackdrop && needsProvider("backdrop") ? resolved.image : null,
473241
+ includeAssets && !opts.skipKeyframe && needsProvider("keyframe") ? resolved.image : null,
473200
473242
  includeAssets && !opts.skipVideo && needsProvider("video") ? resolved.video : null,
473201
473243
  includeAssets && !opts.skipMusic && needsProvider("music") ? resolved.music : null,
473202
473244
  resolved.composer ?? null
@@ -473771,6 +473813,7 @@ async function executeSceneBuild(opts) {
473771
473813
  skipNarration: opts.skipNarration,
473772
473814
  skipBackdrop: opts.skipBackdrop,
473773
473815
  skipVideo: opts.skipVideo,
473816
+ skipKeyframe: opts.skipKeyframe,
473774
473817
  skipMusic: opts.skipMusic,
473775
473818
  ttsProvider: opts.ttsProvider,
473776
473819
  voice: opts.voice,
@@ -473956,6 +473999,7 @@ async function executeSceneBuild(opts) {
473956
473999
  skipNarration: opts.skipNarration ?? false,
473957
474000
  skipBackdrop: opts.skipBackdrop ?? false,
473958
474001
  skipVideo: opts.skipVideo ?? false,
474002
+ skipKeyframe: opts.skipKeyframe ?? false,
473959
474003
  skipMusic: opts.skipMusic ?? false,
473960
474004
  force: opts.force ?? false,
473961
474005
  onProgress,
@@ -474332,10 +474376,11 @@ async function executeSceneBuild(opts) {
474332
474376
  });
474333
474377
  }
474334
474378
  async function buildBeatPrimitives(beat, ctx) {
474379
+ const keyframe = ctx.skipKeyframe ? await skipped("keyframe", beat.id, "--skip-keyframe", ctx) : await dispatchKeyframe(beat, ctx);
474335
474380
  const [narration, backdrop, video, music] = await Promise.all([
474336
474381
  ctx.skipNarration ? skipped("narration", beat.id, "--skip-narration", ctx) : dispatchNarration(beat, ctx),
474337
474382
  ctx.skipBackdrop ? skipped("backdrop", beat.id, "--skip-backdrop", ctx) : dispatchBackdrop(beat, ctx),
474338
- ctx.skipVideo ? skipped("video", beat.id, "--skip-video", ctx) : dispatchVideo(beat, ctx),
474383
+ ctx.skipVideo ? skipped("video", beat.id, "--skip-video", ctx) : dispatchVideo(beat, ctx, keyframe),
474339
474384
  ctx.skipMusic ? skipped("music", beat.id, "--skip-music", ctx) : dispatchMusic(beat, ctx)
474340
474385
  ]);
474341
474386
  return {
@@ -474379,6 +474424,8 @@ async function buildBeatPrimitives(beat, ctx) {
474379
474424
  videoMetadataPath: video.metadataPath,
474380
474425
  videoFreshness: video.freshness,
474381
474426
  videoSourcePath: video.sourcePath,
474427
+ keyframeStatus: keyframe.status,
474428
+ keyframePath: keyframe.path,
474382
474429
  musicStatus: music.status,
474383
474430
  musicPath: music.path,
474384
474431
  musicJobId: music.job?.id,
@@ -474823,6 +474870,112 @@ async function generateBackdropImage(prompt3, ctx, ratio) {
474823
474870
  });
474824
474871
  return imageBufferFromResult(result);
474825
474872
  }
474873
+ async function editKeyframeImage(prompt3, sheetBuffers, ctx, ratio) {
474874
+ loadSceneBuildEnv(ctx.projectDir);
474875
+ const keyInfo = imageProviderKeyInfo(ctx.imageProvider);
474876
+ const apiKey = await getApiKeyFromConfig(keyInfo.configKey, { cwd: ctx.projectDir }) ?? process.env[keyInfo.envVar] ?? "";
474877
+ if (!apiKey) {
474878
+ return {
474879
+ success: false,
474880
+ error: `${keyInfo.envVar} not set \u2014 cannot generate keyframe with ${ctx.imageProvider}`
474881
+ };
474882
+ }
474883
+ if (ctx.imageProvider === "openai") {
474884
+ const provider2 = new OpenAIImageProvider();
474885
+ await provider2.initialize({ apiKey });
474886
+ const result2 = await provider2.editImage(sheetBuffers, prompt3);
474887
+ return imageBufferFromResult(result2);
474888
+ }
474889
+ if (ctx.imageProvider === "gemini") {
474890
+ const provider2 = new GeminiProvider();
474891
+ await provider2.initialize({ apiKey });
474892
+ const result2 = await provider2.editImage(sheetBuffers, prompt3, {
474893
+ model: "flash",
474894
+ aspectRatio: ratio
474895
+ });
474896
+ return imageBufferFromResult(result2);
474897
+ }
474898
+ const provider = new GrokProvider();
474899
+ await provider.initialize({ apiKey });
474900
+ const result = await provider.editImage(sheetBuffers[0], prompt3, { aspectRatio: ratio });
474901
+ return imageBufferFromResult(result);
474902
+ }
474903
+ async function ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel) {
474904
+ const rel = `assets/keyframe-${beat.id}.png`;
474905
+ const abs = join31(ctx.projectDir, rel);
474906
+ const size = ctx.imageSize ?? "1536x1024";
474907
+ const ratio = imageRatioForSize(size);
474908
+ const metadataOptions = { quality: ctx.imageQuality, size, ratio };
474909
+ const cache = keyframeCacheDescriptor({
474910
+ beatId: beat.id,
474911
+ cue: keyframeCue,
474912
+ provider: ctx.imageProvider,
474913
+ quality: ctx.imageQuality,
474914
+ size,
474915
+ ratio,
474916
+ characters: characterRefsRel
474917
+ });
474918
+ if (existsSync35(abs) && !ctx.force) {
474919
+ const metadata = readAssetMetadata(ctx.projectDir, "keyframe", beat.id);
474920
+ if (!metadata || isFreshCanonicalAsset({
474921
+ projectDir: ctx.projectDir,
474922
+ kind: "keyframe",
474923
+ beatId: beat.id,
474924
+ cue: keyframeCue,
474925
+ provider: ctx.imageProvider,
474926
+ options: metadataOptions,
474927
+ cacheKey: cache.key
474928
+ })) {
474929
+ return { status: "cached", rel };
474930
+ }
474931
+ }
474932
+ const cacheAbs = join31(ctx.projectDir, cache.path);
474933
+ if (existsSync35(cacheAbs) && !ctx.force) {
474934
+ await mkdir13(dirname18(abs), { recursive: true });
474935
+ await copyFile4(cacheAbs, abs);
474936
+ await writeAssetMetadata({
474937
+ projectDir: ctx.projectDir,
474938
+ kind: "keyframe",
474939
+ beatId: beat.id,
474940
+ cue: keyframeCue,
474941
+ provider: ctx.imageProvider,
474942
+ options: metadataOptions,
474943
+ cacheKey: cache.key,
474944
+ canonicalPath: rel,
474945
+ cachePath: cache.path
474946
+ });
474947
+ return { status: "cached", rel };
474948
+ }
474949
+ const sheetBuffers = [];
474950
+ for (const relPath of characterRefsRel) {
474951
+ const sheetAbs = join31(ctx.projectDir, relPath);
474952
+ if (existsSync35(sheetAbs)) sheetBuffers.push(await readFile18(sheetAbs));
474953
+ }
474954
+ const imgCtx = {
474955
+ projectDir: ctx.projectDir,
474956
+ imageProvider: ctx.imageProvider,
474957
+ imageSize: size,
474958
+ imageQuality: ctx.imageQuality
474959
+ };
474960
+ const generated = sheetBuffers.length > 0 ? await editKeyframeImage(keyframeCue, sheetBuffers, imgCtx, ratio) : await generateBackdropImage(keyframeCue, imgCtx, ratio);
474961
+ if (!generated.success) return { status: "failed", error: generated.error };
474962
+ await mkdir13(dirname18(abs), { recursive: true });
474963
+ await writeFile20(abs, generated.buffer);
474964
+ await mkdir13(dirname18(cacheAbs), { recursive: true });
474965
+ await writeFile20(cacheAbs, generated.buffer);
474966
+ await writeAssetMetadata({
474967
+ projectDir: ctx.projectDir,
474968
+ kind: "keyframe",
474969
+ beatId: beat.id,
474970
+ cue: keyframeCue,
474971
+ provider: ctx.imageProvider,
474972
+ options: metadataOptions,
474973
+ cacheKey: cache.key,
474974
+ canonicalPath: rel,
474975
+ cachePath: cache.path
474976
+ });
474977
+ return { status: "generated", rel };
474978
+ }
474826
474979
  async function imageBufferFromResult(result) {
474827
474980
  const image = result.images?.[0];
474828
474981
  if (!result.success || !image) {
@@ -474843,10 +474996,25 @@ function imageProviderKeyInfo(provider) {
474843
474996
  if (provider === "grok") return { configKey: "xai", envVar: "XAI_API_KEY" };
474844
474997
  return { configKey: "openai", envVar: "OPENAI_API_KEY" };
474845
474998
  }
474846
- async function dispatchVideo(beat, ctx) {
474999
+ async function dispatchKeyframe(beat, ctx) {
475000
+ const keyframeCue = stringOrUndefined4(beat.cues?.keyframe);
475001
+ if (!keyframeCue) return { status: "no-cue" };
475002
+ const characterRefsRel = beatCharacterNames(beat.cues).map((name) => ctx.characterPaths?.get(name)).filter((p2) => typeof p2 === "string");
475003
+ const kf = await ensureKeyframe(beat, ctx, keyframeCue, characterRefsRel);
475004
+ if (kf.status === "failed") {
475005
+ ctx.onProgress({ type: "keyframe-failed", beatId: beat.id, error: kf.error });
475006
+ return { status: "failed", error: kf.error };
475007
+ }
475008
+ ctx.onProgress(
475009
+ kf.status === "cached" ? { type: "keyframe-cached", beatId: beat.id, path: kf.rel } : { type: "keyframe-generated", beatId: beat.id, path: kf.rel, provider: ctx.imageProvider }
475010
+ );
475011
+ return { status: kf.status, path: kf.rel, provider: ctx.imageProvider };
475012
+ }
475013
+ async function dispatchVideo(beat, ctx, keyframe) {
474847
475014
  const reference = assetReferenceForBeat(ctx.projectDir, "video", beat);
474848
475015
  if (reference) return referencePrimitiveOutcome("video", beat, ctx, reference);
474849
- const prompt3 = stringOrUndefined4(beat.cues?.video);
475016
+ const keyframeCue = stringOrUndefined4(beat.cues?.keyframe);
475017
+ const prompt3 = stringOrUndefined4(beat.cues?.video) ?? keyframeCue;
474850
475018
  if (!prompt3) return { status: "no-cue" };
474851
475019
  const characterRefsRel = beatCharacterNames(beat.cues).map((name) => ctx.characterPaths?.get(name)).filter((p2) => typeof p2 === "string");
474852
475020
  const characterRefsAbs = characterRefsRel.map((p2) => join31(ctx.projectDir, p2));
@@ -474857,7 +475025,10 @@ async function dispatchVideo(beat, ctx) {
474857
475025
  cue: prompt3,
474858
475026
  provider: ctx.videoProvider,
474859
475027
  duration: normalizeVideoDuration(beat.duration),
474860
- characters: characterRefsRel
475028
+ // Keyframe mode keys the clip to the keyframe prompt; refs feed the keyframe,
475029
+ // not the clip directly.
475030
+ characters: keyframeCue ? void 0 : characterRefsRel,
475031
+ keyframe: keyframeCue
474861
475032
  });
474862
475033
  const metadataPath = assetMetadataPath("video", beat.id);
474863
475034
  if (existsSync35(abs) && !ctx.force) {
@@ -474909,6 +475080,13 @@ async function dispatchVideo(beat, ctx) {
474909
475080
  freshness: "fresh"
474910
475081
  };
474911
475082
  }
475083
+ const keyframeImageRel = keyframeCue && (keyframe.status === "generated" || keyframe.status === "cached") ? keyframe.path : void 0;
475084
+ if (keyframeCue && !keyframeImageRel) {
475085
+ const reason = keyframe.status === "failed" ? `keyframe unavailable: ${keyframe.error ?? "generation failed"}` : "keyframe not generated (build without --skip-keyframe first)";
475086
+ ctx.onProgress({ type: "video-skipped", beatId: beat.id, reason });
475087
+ return { status: "skipped" };
475088
+ }
475089
+ const keyframeImageAbs = keyframeImageRel ? join31(ctx.projectDir, keyframeImageRel) : void 0;
474912
475090
  loadSceneBuildEnv(ctx.projectDir);
474913
475091
  const result = await executeVideoGenerate({
474914
475092
  prompt: prompt3,
@@ -474917,7 +475095,10 @@ async function dispatchVideo(beat, ctx) {
474917
475095
  ratio: "16:9",
474918
475096
  output: abs,
474919
475097
  wait: false,
474920
- refImages: characterRefsAbs.length > 0 ? characterRefsAbs : void 0,
475098
+ // Keyframe mode → single init frame (image-to-video); otherwise character
475099
+ // reference-to-video.
475100
+ image: keyframeImageAbs,
475101
+ refImages: keyframeImageAbs ? void 0 : characterRefsAbs.length > 0 ? characterRefsAbs : void 0,
474921
475102
  apiKey: await apiKeyForVideoProvider(ctx.videoProvider, ctx.projectDir)
474922
475103
  });
474923
475104
  if (!result.success || !result.taskId) {
@@ -474959,7 +475140,7 @@ async function dispatchVideo(beat, ctx) {
474959
475140
  jobType: "generate-video",
474960
475141
  provider: result.provider ?? ctx.videoProvider,
474961
475142
  providerTaskId: result.taskId,
474962
- providerTaskType: "text2video",
475143
+ providerTaskType: keyframeImageAbs ? "image2video" : "text2video",
474963
475144
  status: "running",
474964
475145
  projectDir: ctx.projectDir,
474965
475146
  workingDirectory: ctx.projectDir,
@@ -475286,6 +475467,7 @@ function estimateActualAssetCost(outcomes, imageQuality) {
475286
475467
  for (const outcome of outcomes) {
475287
475468
  if (outcome.narrationStatus === "generated") cost += 0.05;
475288
475469
  if (outcome.backdropStatus === "generated") cost += backdropCostUsd(imageQuality);
475470
+ if (outcome.keyframeStatus === "generated") cost += backdropCostUsd(imageQuality);
475289
475471
  if (outcome.videoStatus === "generated" || outcome.videoStatus === "pending") {
475290
475472
  cost += outcome.videoProvider === "seedance" || outcome.videoProvider === "fal" ? estimateSeedanceVideoCostUsd({
475291
475473
  durationSec: normalizeVideoDuration(outcome.sceneDurationSec),
@@ -499542,7 +499724,7 @@ var server2 = new Server(
499542
499724
  {
499543
499725
  name: "vibeframe",
499544
499726
  // Stamped with the package version by build.js at bundle time.
499545
- version: "0.113.2"
499727
+ version: "0.113.4"
499546
499728
  },
499547
499729
  {
499548
499730
  capabilities: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/mcp-server",
3
- "version": "0.113.2",
3
+ "version": "0.113.4",
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.113.2",
64
- "@vibeframe/core": "0.113.2"
63
+ "@vibeframe/cli": "0.113.4",
64
+ "@vibeframe/core": "0.113.4"
65
65
  },
66
66
  "engines": {
67
67
  "node": ">=20"