@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.
- package/dist/index.js +154 -6
- 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:
|
|
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:
|
|
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: "
|
|
466282
|
-
description: "
|
|
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.
|
|
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.
|
|
61
|
-
"@vibeframe/core": "0.
|
|
60
|
+
"@vibeframe/cli": "0.99.0",
|
|
61
|
+
"@vibeframe/core": "0.99.0"
|
|
62
62
|
},
|
|
63
63
|
"engines": {
|
|
64
64
|
"node": ">=20"
|