@vibeframe/mcp-server 0.98.0 → 0.99.0

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 +154 -6
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -465926,12 +465926,21 @@ function isExternalRef(value) {
465926
465926
  }
465927
465927
 
465928
465928
  // ../cli/src/commands/_shared/render-inspect.ts
465929
+ init_ai_review();
465929
465930
  init_exec_safe();
465930
465931
  init_project_config();
465931
465932
  import { existsSync as existsSync50 } from "node:fs";
465932
465933
  import { readdir as readdir6, readFile as readFile27, stat as stat5 } from "node:fs/promises";
465933
465934
  import { basename as basename13, isAbsolute as isAbsolute6, join as join35, relative as relative12, resolve as resolve45 } from "node:path";
465934
465935
  var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([".mp4", ".mov", ".webm", ".m4v"]);
465936
+ var DEFAULT_AI_MODEL = "flash";
465937
+ var AI_CATEGORY_LABELS = {
465938
+ pacing: "Pacing",
465939
+ color: "Color",
465940
+ textReadability: "Text readability",
465941
+ audioVisualSync: "Audio-visual sync",
465942
+ composition: "Composition"
465943
+ };
465935
465944
  function parseBlackdetectOutput(output3) {
465936
465945
  const ranges = [];
465937
465946
  const regex2 = /black_start:(-?\d+(?:\.\d+)?)\s+black_end:(-?\d+(?:\.\d+)?)\s+black_duration:(-?\d+(?:\.\d+)?)/g;
@@ -465966,16 +465975,79 @@ function parseSilencedetectOutput(output3) {
465966
465975
  }
465967
465976
  return ranges;
465968
465977
  }
465978
+ async function previewInspectRender(opts) {
465979
+ const projectDir = resolve45(opts.projectDir);
465980
+ const videoPath = await resolveRenderVideoPath(projectDir, opts.videoPath);
465981
+ const writeReport = opts.writeReport !== false;
465982
+ const reportPath = writeReport ? opts.outputPath ? resolve45(process.cwd(), opts.outputPath) : defaultReviewReportPath(projectDir) : void 0;
465983
+ const storyboardPath = resolveStoryboardPath(projectDir);
465984
+ return {
465985
+ schemaVersion: "1",
465986
+ kind: "render",
465987
+ project: projectDir,
465988
+ videoPath,
465989
+ reportPath,
465990
+ params: {
465991
+ projectDir,
465992
+ videoPath: opts.videoPath,
465993
+ outputPath: opts.outputPath,
465994
+ writeReport,
465995
+ cheap: true,
465996
+ ai: opts.ai === true,
465997
+ model: opts.model ?? DEFAULT_AI_MODEL
465998
+ },
465999
+ checks: {
466000
+ renderFound: videoPath !== null,
466001
+ storyboardPath
466002
+ }
466003
+ };
466004
+ }
466005
+ function aiReviewSeverity(score) {
466006
+ if (score <= 4) return "error";
466007
+ if (score <= 6) return "warning";
466008
+ return "info";
466009
+ }
466010
+ function mapAiReviewFeedbackToIssues(feedback, videoPath) {
466011
+ const issues = [];
466012
+ for (const [key2, label] of Object.entries(AI_CATEGORY_LABELS)) {
466013
+ const category = feedback.categories[key2];
466014
+ if (!category || category.issues.length === 0) continue;
466015
+ for (const issue of category.issues) {
466016
+ issues.push({
466017
+ severity: aiReviewSeverity(category.score),
466018
+ code: `AI_REVIEW_${toSnakeCase(String(key2))}`,
466019
+ message: `${label}: ${issue}`,
466020
+ file: videoPath ? displayPath(videoPath) : void 0,
466021
+ suggestedFix: category.fixable ? "Adjust the relevant storyboard or scene composition, then rerender." : "Review this finding manually before rerendering."
466022
+ });
466023
+ }
466024
+ }
466025
+ return issues;
466026
+ }
466027
+ function scoreRenderReview(issues, aiOverallScore) {
466028
+ const localScore = scoreIssues(issues);
466029
+ if (aiOverallScore === void 0) return localScore;
466030
+ const aiScore = Math.max(0, Math.min(100, Math.round(aiOverallScore * 10)));
466031
+ return Math.round((localScore + aiScore) / 2);
466032
+ }
465969
466033
  async function inspectRender(opts) {
465970
466034
  const projectDir = resolve45(opts.projectDir);
465971
466035
  const issues = [];
465972
466036
  const retryWith = [];
466037
+ const model = opts.model ?? DEFAULT_AI_MODEL;
465973
466038
  const videoPath = await resolveRenderVideoPath(projectDir, opts.videoPath);
465974
466039
  const checks = {
465975
466040
  renderFound: videoPath !== null,
465976
466041
  blackFrames: [],
465977
466042
  silences: []
465978
466043
  };
466044
+ if (opts.ai) {
466045
+ checks.ai = {
466046
+ enabled: true,
466047
+ model,
466048
+ success: false
466049
+ };
466050
+ }
465979
466051
  if (!videoPath) {
465980
466052
  issues.push({
465981
466053
  severity: "error",
@@ -465984,13 +466056,16 @@ async function inspectRender(opts) {
465984
466056
  suggestedFix: "Run `vibe build --stage render --json` or `vibe render --json`."
465985
466057
  });
465986
466058
  retryWith.push(`vibe build ${projectDir} --stage render --json`, `vibe render ${projectDir} --json`);
466059
+ if (checks.ai) {
466060
+ checks.ai.error = "Skipped AI review because no rendered video was found.";
466061
+ }
465987
466062
  return maybeWriteRenderReport(projectDir, opts, {
465988
466063
  schemaVersion: "1",
465989
466064
  kind: "render",
465990
466065
  project: projectDir,
465991
466066
  videoPath: null,
465992
466067
  status: "fail",
465993
- score: scoreIssues(issues),
466068
+ score: scoreRenderReview(issues),
465994
466069
  issues,
465995
466070
  checks,
465996
466071
  retryWith: uniqueRetryWith(retryWith)
@@ -466128,6 +466203,40 @@ async function inspectRender(opts) {
466128
466203
  suggestedFix: "Install FFmpeg for full cheap render inspection."
466129
466204
  });
466130
466205
  }
466206
+ let aiOverallScore;
466207
+ if (opts.ai && checks.ai) {
466208
+ if (fileStat.size === 0) {
466209
+ checks.ai.error = "Skipped AI review because the rendered video file is empty.";
466210
+ } else {
466211
+ const aiResult = await runAiRenderReview(projectDir, videoPath, model);
466212
+ if (aiResult.success && aiResult.feedback) {
466213
+ checks.ai.success = true;
466214
+ checks.ai.overallScore = aiResult.feedback.overallScore;
466215
+ checks.ai.categories = aiResult.feedback.categories;
466216
+ checks.ai.recommendations = aiResult.feedback.recommendations;
466217
+ aiOverallScore = aiResult.feedback.overallScore;
466218
+ const aiIssues = mapAiReviewFeedbackToIssues(aiResult.feedback, videoPath);
466219
+ issues.push(...aiIssues);
466220
+ if (aiIssues.length > 0) {
466221
+ retryWith.push(
466222
+ 'codex "fix issues from review-report.json"',
466223
+ `vibe render ${projectDir} --json`,
466224
+ `vibe inspect render ${projectDir} --ai --json`
466225
+ );
466226
+ }
466227
+ } else {
466228
+ const message = aiResult.error ?? "Gemini video review failed";
466229
+ checks.ai.error = message;
466230
+ issues.push({
466231
+ severity: "error",
466232
+ code: "AI_REVIEW_FAILED",
466233
+ message: `AI render review failed: ${message}`,
466234
+ file: displayPath(videoPath),
466235
+ suggestedFix: "Set GOOGLE_API_KEY or retry the AI review later."
466236
+ });
466237
+ }
466238
+ }
466239
+ }
466131
466240
  const status = statusFromIssues(issues);
466132
466241
  const result = {
466133
466242
  schemaVersion: "1",
@@ -466135,7 +466244,7 @@ async function inspectRender(opts) {
466135
466244
  project: projectDir,
466136
466245
  videoPath,
466137
466246
  status,
466138
- score: scoreIssues(issues),
466247
+ score: scoreRenderReview(issues, aiOverallScore),
466139
466248
  issues,
466140
466249
  checks,
466141
466250
  retryWith: uniqueRetryWith(retryWith)
@@ -466184,6 +466293,19 @@ async function resolveRenderVideoPath(projectDir, explicit) {
466184
466293
  candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
466185
466294
  return candidates[0]?.path ?? null;
466186
466295
  }
466296
+ async function runAiRenderReview(projectDir, videoPath, model) {
466297
+ return executeReview({
466298
+ videoPath,
466299
+ storyboardPath: resolveStoryboardPath(projectDir) ?? void 0,
466300
+ autoApply: false,
466301
+ verify: false,
466302
+ model
466303
+ });
466304
+ }
466305
+ function resolveStoryboardPath(projectDir) {
466306
+ const storyboardPath = join35(projectDir, "STORYBOARD.md");
466307
+ return existsSync50(storyboardPath) ? storyboardPath : null;
466308
+ }
466187
466309
  async function expectedDurationFromBuildReport(projectDir) {
466188
466310
  const reportPath = join35(projectDir, "build-report.json");
466189
466311
  if (!existsSync50(reportPath)) return void 0;
@@ -466245,6 +466367,9 @@ function aspectRatio(aspect) {
466245
466367
  function displayPath(path14) {
466246
466368
  return relative12(process.cwd(), path14) || basename13(path14);
466247
466369
  }
466370
+ function toSnakeCase(value) {
466371
+ return value.replace(/[A-Z]/g, (match2) => `_${match2}`).toUpperCase();
466372
+ }
466248
466373
 
466249
466374
  // ../cli/src/tools/manifest/inspect.ts
466250
466375
  var inspectProjectTool = defineTool({
@@ -466278,21 +466403,44 @@ var inspectProjectTool = defineTool({
466278
466403
  var inspectRenderTool = defineTool({
466279
466404
  name: "inspect_render",
466280
466405
  category: "analyze",
466281
- cost: "free",
466282
- description: "Cheap local render inspection: finds the rendered video, probes duration/dimensions/audio, scans black frames and long silence, and writes review-report.json by default.",
466406
+ cost: "low",
466407
+ description: "Render inspection: runs cheap local video checks by default, optionally adds Gemini AI review with ai: true, and writes review-report.json by default.",
466283
466408
  schema: z4.object({
466284
466409
  projectDir: z4.string().optional().describe("Project directory. Defaults to the surface's cwd."),
466285
466410
  videoPath: z4.string().optional().describe("Rendered video path. Defaults to build-report outputPath or latest renders/* video."),
466286
466411
  outputPath: z4.string().optional().describe("Optional review report path. Defaults to <project>/review-report.json."),
466287
- report: z4.boolean().optional().describe("Write review-report.json. Default true.")
466412
+ report: z4.boolean().optional().describe("Write review-report.json. Default true."),
466413
+ ai: z4.boolean().optional().describe("Also run Gemini video review and merge findings into review-report.json. Default false."),
466414
+ model: z4.enum(["flash", "flash-2.5", "pro"]).optional().describe("Gemini model variant for ai review. Default flash."),
466415
+ dryRun: z4.boolean().optional().describe("Preview resolved inputs without probing video or calling Gemini.")
466288
466416
  }),
466289
466417
  async execute(args, ctx) {
466290
466418
  const projectDir = args.projectDir ? resolve46(ctx.workingDirectory, args.projectDir) : ctx.workingDirectory;
466419
+ if (args.dryRun) {
466420
+ const result2 = await previewInspectRender({
466421
+ projectDir,
466422
+ videoPath: args.videoPath ? resolve46(ctx.workingDirectory, args.videoPath) : void 0,
466423
+ outputPath: args.outputPath ? resolve46(ctx.workingDirectory, args.outputPath) : void 0,
466424
+ writeReport: args.report !== false,
466425
+ ai: args.ai === true,
466426
+ model: args.model
466427
+ });
466428
+ return {
466429
+ success: true,
466430
+ data: result2,
466431
+ humanLines: [
466432
+ `Render inspection dry-run \u2014 ${result2.videoPath ? "render found" : "render missing"}`,
466433
+ ...result2.reportPath ? [`report: ${result2.reportPath}`] : []
466434
+ ]
466435
+ };
466436
+ }
466291
466437
  const result = await inspectRender({
466292
466438
  projectDir,
466293
466439
  videoPath: args.videoPath ? resolve46(ctx.workingDirectory, args.videoPath) : void 0,
466294
466440
  outputPath: args.outputPath ? resolve46(ctx.workingDirectory, args.outputPath) : void 0,
466295
- writeReport: args.report !== false
466441
+ writeReport: args.report !== false,
466442
+ ai: args.ai === true,
466443
+ model: args.model
466296
466444
  });
466297
466445
  return {
466298
466446
  success: result.status !== "fail",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/mcp-server",
3
- "version": "0.98.0",
3
+ "version": "0.99.0",
4
4
  "description": "VibeFrame MCP Server - AI-native video editing via Model Context Protocol",
5
5
  "type": "module",
6
6
  "bin": {
@@ -57,8 +57,8 @@
57
57
  "tsx": "^4.21.0",
58
58
  "typescript": "^5.3.3",
59
59
  "vitest": "^1.2.2",
60
- "@vibeframe/cli": "0.98.0",
61
- "@vibeframe/core": "0.98.0"
60
+ "@vibeframe/cli": "0.99.0",
61
+ "@vibeframe/core": "0.99.0"
62
62
  },
63
63
  "engines": {
64
64
  "node": ">=20"