@vibeframe/cli 0.106.6 → 0.106.8

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.
@@ -17,6 +17,7 @@ import { type SceneAspect } from "./scene-project.js";
17
17
  export type RenderFps = 24 | 30 | 60;
18
18
  export type RenderQuality = "draft" | "standard" | "high";
19
19
  export type RenderFormat = "mp4" | "webm" | "mov";
20
+ export type RenderOpenAction = "open" | "reveal";
20
21
  export interface SceneRenderOptions {
21
22
  /** Project directory (defaults to cwd). */
22
23
  projectDir?: string;
@@ -33,6 +34,10 @@ export interface SceneRenderOptions {
33
34
  /** Hyperframes capture worker count. Default 1 (the existing backend's
34
35
  * default — auto-worker mode times out on small comps). */
35
36
  workers?: number;
37
+ /** Open the rendered media in the OS default app after a successful render. */
38
+ openAfterRender?: boolean;
39
+ /** Reveal the rendered media in Finder/file manager after a successful render. */
40
+ revealInFinder?: boolean;
36
41
  signal?: AbortSignal;
37
42
  onProgress?: (pct: number, stage: string) => void;
38
43
  }
@@ -42,6 +47,13 @@ export interface SceneRenderResult {
42
47
  beat?: string | null;
43
48
  root?: string;
44
49
  outputPath?: string;
50
+ absoluteOutputPath?: string;
51
+ openCommand?: string;
52
+ revealCommand?: string;
53
+ opened?: boolean;
54
+ revealed?: boolean;
55
+ openError?: string;
56
+ revealError?: string;
45
57
  durationMs?: number;
46
58
  framesRendered?: number;
47
59
  totalFrames?: number;
@@ -59,6 +71,11 @@ export interface SceneRenderResult {
59
71
  error?: string;
60
72
  retryWith?: string[];
61
73
  }
74
+ export interface MediaOpenCommand {
75
+ command: string;
76
+ args: string[];
77
+ display: string;
78
+ }
62
79
  /** Map a quality preset to an x264 CRF (lower = higher quality). */
63
80
  export declare function qualityToCrf(quality?: RenderQuality): number;
64
81
  /**
@@ -85,6 +102,7 @@ export declare function buildRenderConfig(opts: {
85
102
  workers?: number;
86
103
  entryFile?: string;
87
104
  }): RenderConfig;
105
+ export declare function buildMediaOpenCommand(action: RenderOpenAction, filePath: string, platform?: NodeJS.Platform): MediaOpenCommand;
88
106
  /**
89
107
  * Render a scene project. Mirrors the contract of the existing Hyperframes
90
108
  * backend (preflight → execute → structured result), but skips the
package/dist/index.js CHANGED
@@ -31472,11 +31472,11 @@ registerRoot(Root);
31472
31472
  return dir;
31473
31473
  }
31474
31474
  async function this_npmInstall(dir) {
31475
- const { execFile: execFile2 } = await import("node:child_process");
31476
- const { promisify: promisify2 } = await import("node:util");
31477
- const execFileAsync2 = promisify2(execFile2);
31475
+ const { execFile: execFile3 } = await import("node:child_process");
31476
+ const { promisify: promisify3 } = await import("node:util");
31477
+ const execFileAsync3 = promisify3(execFile3);
31478
31478
  try {
31479
- await execFileAsync2("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund"], {
31479
+ await execFileAsync3("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund"], {
31480
31480
  cwd: dir,
31481
31481
  timeout: 18e4
31482
31482
  });
@@ -31500,13 +31500,13 @@ async function renderMotion(options) {
31500
31500
  );
31501
31501
  try {
31502
31502
  const entryPoint = join9(dir, "Root.tsx");
31503
- const { execFile: execFile2 } = await import("node:child_process");
31504
- const { promisify: promisify2 } = await import("node:util");
31505
- const execFileAsync2 = promisify2(execFile2);
31503
+ const { execFile: execFile3 } = await import("node:child_process");
31504
+ const { promisify: promisify3 } = await import("node:util");
31505
+ const execFileAsync3 = promisify3(execFile3);
31506
31506
  if (transparent) {
31507
31507
  const webmOut = options.outputPath.replace(/\.\w+$/, ".webm");
31508
31508
  try {
31509
- await execFileAsync2("npx", [
31509
+ await execFileAsync3("npx", [
31510
31510
  "remotion",
31511
31511
  "render",
31512
31512
  entryPoint,
@@ -31523,7 +31523,7 @@ async function renderMotion(options) {
31523
31523
  } catch {
31524
31524
  }
31525
31525
  try {
31526
- await execFileAsync2("npx", [
31526
+ await execFileAsync3("npx", [
31527
31527
  "remotion",
31528
31528
  "render",
31529
31529
  entryPoint,
@@ -31543,7 +31543,7 @@ async function renderMotion(options) {
31543
31543
  }
31544
31544
  }
31545
31545
  const mp4Out = options.outputPath.replace(/\.\w+$/, ".mp4");
31546
- await execFileAsync2("npx", [
31546
+ await execFileAsync3("npx", [
31547
31547
  "remotion",
31548
31548
  "render",
31549
31549
  entryPoint,
@@ -59857,15 +59857,18 @@ var init_scene_audio_mux = __esm({
59857
59857
  // src/commands/_shared/scene-render.ts
59858
59858
  var scene_render_exports = {};
59859
59859
  __export(scene_render_exports, {
59860
+ buildMediaOpenCommand: () => buildMediaOpenCommand,
59860
59861
  buildRenderConfig: () => buildRenderConfig,
59861
59862
  defaultOutputPath: () => defaultOutputPath,
59862
59863
  executeSceneRender: () => executeSceneRender,
59863
59864
  prepareBeatRenderRoot: () => prepareBeatRenderRoot,
59864
59865
  qualityToCrf: () => qualityToCrf2
59865
59866
  });
59867
+ import { execFile as execFile2 } from "node:child_process";
59866
59868
  import { existsSync as existsSync46 } from "node:fs";
59867
59869
  import { mkdir as mkdir24, readFile as readFile43, stat as stat8, writeFile as writeFile48 } from "node:fs/promises";
59868
59870
  import { resolve as resolve62, relative as relative11, dirname as dirname22, basename as basename21, join as join22, isAbsolute as isAbsolute6 } from "node:path";
59871
+ import { promisify as promisify2 } from "node:util";
59869
59872
  import {
59870
59873
  createRenderJob,
59871
59874
  executeRenderJob
@@ -59892,6 +59895,38 @@ function buildRenderConfig(opts) {
59892
59895
  workers: opts.workers ?? 1
59893
59896
  };
59894
59897
  }
59898
+ function buildMediaOpenCommand(action, filePath, platform3 = process.platform) {
59899
+ if (platform3 === "darwin") {
59900
+ const args = action === "reveal" ? ["-R", filePath] : [filePath];
59901
+ return {
59902
+ command: "open",
59903
+ args,
59904
+ display: ["open", ...args].map(quoteCliArg).join(" ")
59905
+ };
59906
+ }
59907
+ if (platform3 === "win32") {
59908
+ if (action === "reveal") {
59909
+ const args2 = [`/select,${filePath}`];
59910
+ return {
59911
+ command: "explorer",
59912
+ args: args2,
59913
+ display: ["explorer", ...args2].map(quoteCliArg).join(" ")
59914
+ };
59915
+ }
59916
+ const args = ["/c", "start", "", filePath];
59917
+ return {
59918
+ command: "cmd",
59919
+ args,
59920
+ display: ["cmd", ...args].map(quoteCliArg).join(" ")
59921
+ };
59922
+ }
59923
+ const target = action === "reveal" ? dirname22(filePath) : filePath;
59924
+ return {
59925
+ command: "xdg-open",
59926
+ args: [target],
59927
+ display: ["xdg-open", target].map(quoteCliArg).join(" ")
59928
+ };
59929
+ }
59895
59930
  async function executeSceneRender(opts = {}) {
59896
59931
  const projectDir = resolve62(opts.projectDir ?? ".");
59897
59932
  const projectConfig = await readProjectConfig(projectDir);
@@ -60015,6 +60050,9 @@ async function executeSceneRender(opts = {}) {
60015
60050
  beat: opts.beatId ?? null,
60016
60051
  root,
60017
60052
  outputPath: relative11(process.cwd(), outputPath) || outputPath,
60053
+ absoluteOutputPath: outputPath,
60054
+ openCommand: buildMediaOpenCommand("open", outputPath).display,
60055
+ revealCommand: buildMediaOpenCommand("reveal", outputPath).display,
60018
60056
  durationMs: Date.now() - start,
60019
60057
  framesRendered: job.framesRendered,
60020
60058
  totalFrames: job.totalFrames,
@@ -60025,12 +60063,38 @@ async function executeSceneRender(opts = {}) {
60025
60063
  audioMuxApplied,
60026
60064
  audioMuxWarning
60027
60065
  };
60066
+ if (opts.revealInFinder) {
60067
+ try {
60068
+ await runMediaOpenAction("reveal", outputPath);
60069
+ result.revealed = true;
60070
+ } catch (err) {
60071
+ result.revealed = false;
60072
+ result.revealError = err instanceof Error ? err.message : String(err);
60073
+ }
60074
+ }
60075
+ if (opts.openAfterRender) {
60076
+ try {
60077
+ await runMediaOpenAction("open", outputPath);
60078
+ result.opened = true;
60079
+ } catch (err) {
60080
+ result.opened = false;
60081
+ result.openError = err instanceof Error ? err.message : String(err);
60082
+ }
60083
+ }
60028
60084
  result.reportPath = await writeRenderReport(projectDir, {
60029
60085
  ...result,
60030
60086
  outputPath
60031
60087
  });
60032
60088
  return result;
60033
60089
  }
60090
+ async function runMediaOpenAction(action, filePath) {
60091
+ const cmd = buildMediaOpenCommand(action, filePath);
60092
+ await execFileAsync2(cmd.command, cmd.args);
60093
+ }
60094
+ function quoteCliArg(value) {
60095
+ if (/^[A-Za-z0-9_/@%+=:,.-]+$/.test(value)) return value;
60096
+ return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
60097
+ }
60034
60098
  async function safeStat(p) {
60035
60099
  try {
60036
60100
  return await stat8(p);
@@ -60201,6 +60265,13 @@ async function writeRenderReport(projectDir, result) {
60201
60265
  beat: result.beat ?? null,
60202
60266
  root: result.root,
60203
60267
  outputPath: result.outputPath,
60268
+ absoluteOutputPath: result.absoluteOutputPath,
60269
+ openCommand: result.openCommand,
60270
+ revealCommand: result.revealCommand,
60271
+ opened: result.opened,
60272
+ revealed: result.revealed,
60273
+ openError: result.openError,
60274
+ revealError: result.revealError,
60204
60275
  fps: result.fps,
60205
60276
  quality: result.quality,
60206
60277
  format: result.format,
@@ -60245,6 +60316,7 @@ function escapeAttr(value) {
60245
60316
  function unique4(items) {
60246
60317
  return [...new Set(items.filter((item) => Boolean(item)))];
60247
60318
  }
60319
+ var execFileAsync2;
60248
60320
  var init_scene_render = __esm({
60249
60321
  "src/commands/_shared/scene-render.ts"() {
60250
60322
  "use strict";
@@ -60254,6 +60326,7 @@ var init_scene_render = __esm({
60254
60326
  init_scene_audio_mux();
60255
60327
  init_project_config();
60256
60328
  init_scene_project();
60329
+ execFileAsync2 = promisify2(execFile2);
60257
60330
  }
60258
60331
  });
60259
60332
 
@@ -67341,7 +67414,9 @@ var init_scene2 = __esm({
67341
67414
  fps: external_exports.number().optional().describe("Frames per second. Must be 24, 30, or 60. Default 30."),
67342
67415
  quality: external_exports.enum(["draft", "standard", "high"]).optional().describe("Quality preset. Default 'standard'."),
67343
67416
  format: external_exports.enum(["mp4", "webm", "mov"]).optional().describe("Container format. Default 'mp4'."),
67344
- workers: external_exports.number().optional().describe("Capture worker count (1-16). Default 1.")
67417
+ workers: external_exports.number().optional().describe("Capture worker count (1-16). Default 1."),
67418
+ openAfterRender: external_exports.boolean().optional().describe("Open the rendered video in the OS default app after render. Default false."),
67419
+ revealInFinder: external_exports.boolean().optional().describe("Reveal the rendered video in Finder/file manager after render. Default false.")
67345
67420
  });
67346
67421
  sceneRenderTool = defineTool({
67347
67422
  name: "render",
@@ -67359,7 +67434,9 @@ var init_scene2 = __esm({
67359
67434
  fps: args.fps,
67360
67435
  quality: args.quality,
67361
67436
  format: args.format,
67362
- workers: args.workers
67437
+ workers: args.workers,
67438
+ openAfterRender: args.openAfterRender,
67439
+ revealInFinder: args.revealInFinder
67363
67440
  });
67364
67441
  if (!result.success) {
67365
67442
  return { success: false, error: result.error ?? "render failed" };
@@ -67368,6 +67445,13 @@ var init_scene2 = __esm({
67368
67445
  success: true,
67369
67446
  data: {
67370
67447
  outputPath: result.outputPath,
67448
+ absoluteOutputPath: result.absoluteOutputPath,
67449
+ openCommand: result.openCommand,
67450
+ revealCommand: result.revealCommand,
67451
+ opened: result.opened,
67452
+ revealed: result.revealed,
67453
+ openError: result.openError,
67454
+ revealError: result.revealError,
67371
67455
  beat: result.beat,
67372
67456
  root: result.root,
67373
67457
  reportPath: result.reportPath,
@@ -67376,13 +67460,24 @@ var init_scene2 = __esm({
67376
67460
  totalFrames: result.totalFrames,
67377
67461
  fps: result.fps,
67378
67462
  quality: result.quality,
67379
- format: result.format
67463
+ format: result.format,
67464
+ audioCount: result.audioCount,
67465
+ audioMuxApplied: result.audioMuxApplied,
67466
+ audioMuxWarning: result.audioMuxWarning
67380
67467
  },
67381
67468
  humanLines: [
67382
- `\u2705 Render complete: ${result.outputPath}`,
67469
+ `\u2705 Render complete: ${result.absoluteOutputPath ?? result.outputPath}`,
67383
67470
  ` duration: ${((result.durationMs ?? 0) / 1e3).toFixed(1)}s`,
67384
67471
  ` frames: ${result.framesRendered ?? "?"}${result.totalFrames ? ` / ${result.totalFrames}` : ""}`,
67385
- ` config: ${result.fps}fps \xB7 ${result.quality} \xB7 ${result.format}`
67472
+ ` config: ${result.fps}fps \xB7 ${result.quality} \xB7 ${result.format}`,
67473
+ ` audio: ${result.audioCount && result.audioCount > 0 ? `${result.audioCount} track${result.audioCount === 1 ? "" : "s"} muxed` : "silent"}`,
67474
+ ...result.openCommand ? [` open: ${result.openCommand}`] : [],
67475
+ ...result.revealCommand ? [` reveal: ${result.revealCommand}`] : [],
67476
+ ` inspect: vibe inspect render ${projectDir} --cheap --json`,
67477
+ ...result.opened ? [` opened: yes`] : [],
67478
+ ...result.revealed ? [` revealed: yes`] : [],
67479
+ ...result.openError ? [` open warning: ${result.openError}`] : [],
67480
+ ...result.revealError ? [` reveal warning: ${result.revealError}`] : []
67386
67481
  ]
67387
67482
  };
67388
67483
  }
@@ -72028,7 +72123,7 @@ init_source();
72028
72123
  // package.json
72029
72124
  var package_default = {
72030
72125
  name: "@vibeframe/cli",
72031
- version: "0.106.6",
72126
+ version: "0.106.8",
72032
72127
  description: "VibeFrame CLI - agentic video workflow runtime",
72033
72128
  private: false,
72034
72129
  license: "MIT",
@@ -80703,7 +80798,18 @@ function resolveHostSelection(selection) {
80703
80798
  return [selection];
80704
80799
  }
80705
80800
  function vibeframeMcpJson(host, projectDir) {
80706
- return host.id === "claude-desktop" ? { ...VIBEFRAME_MCP_JSON, cwd: resolve53(projectDir) } : { ...VIBEFRAME_MCP_JSON };
80801
+ if (host.id !== "claude-desktop") return { ...VIBEFRAME_MCP_JSON };
80802
+ const workspace = resolve53(projectDir);
80803
+ if (process.platform === "win32") {
80804
+ return {
80805
+ command: "cmd.exe",
80806
+ args: ["/d", "/s", "/c", `cd /d ${cmdQuote(workspace)} && npx -y @vibeframe/mcp-server`]
80807
+ };
80808
+ }
80809
+ return {
80810
+ command: "bash",
80811
+ args: ["-lc", `cd ${shellQuote(workspace)} && exec npx -y @vibeframe/mcp-server`]
80812
+ };
80707
80813
  }
80708
80814
  function renderHostSnippet(host, projectDir) {
80709
80815
  if (host.configKind === "codex-toml") return CODEX_MCP_BLOCK.trimEnd();
@@ -80768,9 +80874,9 @@ async function inspectHost(host, projectDir) {
80768
80874
  const json = JSON.parse(content);
80769
80875
  configured = hasVibeframeMcpServer(json);
80770
80876
  const server = readVibeframeMcpServer(json);
80771
- if (host.id === "claude-desktop" && configured && !server?.cwd) {
80877
+ if (host.id === "claude-desktop" && configured && !isAnchoredClaudeDesktopServer(server)) {
80772
80878
  warnings.push(
80773
- "Claude Desktop vibeframe MCP config has no cwd; run `vibe host setup claude-desktop <workspace> --write` to anchor relative paths."
80879
+ "Claude Desktop vibeframe MCP config is not workspace-anchored; run `vibe host setup claude-desktop <workspace> --write` to anchor relative paths."
80774
80880
  );
80775
80881
  }
80776
80882
  }
@@ -80794,17 +80900,17 @@ async function mergeMcpJson(configPath, opts) {
80794
80900
  const mcpServers = typeof root.mcpServers === "object" && root.mcpServers !== null && !Array.isArray(root.mcpServers) ? root.mcpServers : {};
80795
80901
  if (mcpServers.vibeframe && !opts.force) {
80796
80902
  const existing = mcpServers.vibeframe;
80797
- if (canAddMissingCwd(existing, opts.server)) {
80903
+ if (canUpgradeDefaultServer(existing, opts.server)) {
80798
80904
  root.mcpServers = {
80799
80905
  ...mcpServers,
80800
- vibeframe: { ...existing, cwd: opts.server.cwd }
80906
+ vibeframe: opts.server
80801
80907
  };
80802
80908
  await mkdir22(dirname18(configPath), { recursive: true });
80803
80909
  if (exists && opts.backup) {
80804
80910
  await writeFile44(`${configPath}.bak-${timestamp()}`, await readFile37(configPath, "utf-8"), "utf-8");
80805
80911
  }
80806
80912
  await writeFile44(configPath, JSON.stringify(root, null, 2) + "\n", "utf-8");
80807
- return { path: configPath, status: "merged", reason: "added cwd to existing vibeframe MCP server" };
80913
+ return { path: configPath, status: "merged", reason: "anchored existing default vibeframe MCP server" };
80808
80914
  }
80809
80915
  return {
80810
80916
  path: configPath,
@@ -80874,14 +80980,24 @@ function readVibeframeMcpServer(value) {
80874
80980
  if (!server || typeof server !== "object" || Array.isArray(server)) return null;
80875
80981
  return server;
80876
80982
  }
80877
- function canAddMissingCwd(existing, target) {
80878
- if (!target.cwd) return false;
80983
+ function isAnchoredClaudeDesktopServer(server) {
80984
+ if (!server) return false;
80985
+ if (server.command === "bash" && Array.isArray(server.args)) {
80986
+ return server.args[0] === "-lc" && typeof server.args[1] === "string" && server.args[1].includes("exec npx -y @vibeframe/mcp-server");
80987
+ }
80988
+ if (server.command === "cmd.exe" && Array.isArray(server.args)) {
80989
+ return server.args.some((arg) => typeof arg === "string" && arg.includes("npx -y @vibeframe/mcp-server"));
80990
+ }
80991
+ return false;
80992
+ }
80993
+ function canUpgradeDefaultServer(existing, target) {
80879
80994
  if (!existing || typeof existing !== "object" || Array.isArray(existing)) return false;
80880
80995
  const current = existing;
80881
- if (current.cwd) return false;
80882
- if (current.command !== target.command) return false;
80883
80996
  if (!Array.isArray(current.args)) return false;
80884
- return JSON.stringify(current.args) === JSON.stringify(target.args);
80997
+ if (current.command === target.command && JSON.stringify(current.args) === JSON.stringify(target.args)) {
80998
+ return false;
80999
+ }
81000
+ return current.command === VIBEFRAME_MCP_JSON.command && JSON.stringify(current.args) === JSON.stringify(VIBEFRAME_MCP_JSON.args);
80885
81001
  }
80886
81002
  function hostNextSteps(host) {
80887
81003
  if (host.id === "codex") {
@@ -80921,6 +81037,12 @@ function ensureTrailingNewline(value) {
80921
81037
  function escapeRegExp3(value) {
80922
81038
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
80923
81039
  }
81040
+ function shellQuote(value) {
81041
+ return `'${value.replace(/'/g, `'\\''`)}'`;
81042
+ }
81043
+ function cmdQuote(value) {
81044
+ return `"${value.replace(/"/g, '""')}"`;
81045
+ }
80924
81046
 
80925
81047
  // src/commands/_shared/init-templates.ts
80926
81048
  var AGENTS_MD = `# AGENTS.md
@@ -82704,7 +82826,7 @@ import { resolve as resolve65 } from "node:path";
82704
82826
  var VALID_FPS = [24, 30, 60];
82705
82827
  var VALID_QUALITIES = ["draft", "standard", "high"];
82706
82828
  var VALID_FORMATS = ["mp4", "webm", "mov"];
82707
- var renderCommand = new Command("render").description("Render a VibeFrame video project to MP4/WebM/MOV").argument("[project-dir]", "Video project directory", ".").option("-o, --out <path>", "Output file (default: renders/<name>-<timestamp>.<format>)").option("--root <file>", "Root composition file", "index.html").option("--beat <id>", "Render only one storyboard beat using a temporary root").option("--fps <n>", `Frames per second: ${VALID_FPS.join("|")}`, "30").option("--quality <q>", `Quality preset: ${VALID_QUALITIES.join("|")}`, "standard").option("--format <f>", `Output container: ${VALID_FORMATS.join("|")}`, "mp4").option("--workers <n>", "Capture workers (1-16, default 1)", "1").option("--dry-run", "Preview parameters without rendering").addHelpText("after", `
82829
+ var renderCommand = new Command("render").description("Render a VibeFrame video project to MP4/WebM/MOV").argument("[project-dir]", "Video project directory", ".").option("-o, --out <path>", "Output file (default: renders/<name>-<timestamp>.<format>)").option("--root <file>", "Root composition file", "index.html").option("--beat <id>", "Render only one storyboard beat using a temporary root").option("--fps <n>", `Frames per second: ${VALID_FPS.join("|")}`, "30").option("--quality <q>", `Quality preset: ${VALID_QUALITIES.join("|")}`, "standard").option("--format <f>", `Output container: ${VALID_FORMATS.join("|")}`, "mp4").option("--workers <n>", "Capture workers (1-16, default 1)", "1").option("--open", "Open the rendered video in the OS default app after render").option("--reveal", "Reveal the rendered video in Finder/file manager after render").option("--dry-run", "Preview parameters without rendering").addHelpText("after", `
82708
82830
  Examples:
82709
82831
  $ vibe render my-video
82710
82832
  $ vibe render my-video -o renders/final.mp4 --quality high
@@ -82724,7 +82846,9 @@ Alias note: this is the project-level entrypoint for \`vibe scene render\`.`).ac
82724
82846
  fps,
82725
82847
  quality,
82726
82848
  format,
82727
- workers
82849
+ workers,
82850
+ openAfterRender: Boolean(options.open),
82851
+ revealInFinder: Boolean(options.reveal)
82728
82852
  };
82729
82853
  if (options.dryRun) {
82730
82854
  if (!isJsonMode() && !isQuietMode()) {
@@ -82749,6 +82873,8 @@ Alias note: this is the project-level entrypoint for \`vibe scene render\`.`).ac
82749
82873
  quality,
82750
82874
  format,
82751
82875
  workers,
82876
+ openAfterRender: Boolean(options.open),
82877
+ revealInFinder: Boolean(options.reveal),
82752
82878
  onProgress: (pct, stage) => {
82753
82879
  if (spinner2) spinner2.text = `Rendering [${Math.round(pct * 100)}%] ${stage}`;
82754
82880
  }
@@ -82779,6 +82905,8 @@ function printRenderDryRun(projectDirArg, params) {
82779
82905
  console.log(` Format: ${source_default.bold(params.format)}`);
82780
82906
  console.log(` Quality/FPS: ${source_default.bold(`${params.quality} / ${params.fps}`)}`);
82781
82907
  console.log(` Workers: ${source_default.bold(String(params.workers))}`);
82908
+ if (params.openAfterRender) console.log(` Open: ${source_default.bold("yes")}`);
82909
+ if (params.revealInFinder) console.log(` Reveal: ${source_default.bold("yes")}`);
82782
82910
  console.log();
82783
82911
  console.log(source_default.dim("No browser capture, FFmpeg mux, or video files were created."));
82784
82912
  }
@@ -82787,7 +82915,7 @@ function printRenderResult(spinner2, result) {
82787
82915
  console.log();
82788
82916
  console.log(source_default.bold.cyan("Output"));
82789
82917
  console.log(source_default.dim("-".repeat(60)));
82790
- console.log(` File: ${source_default.bold(result.outputPath)}`);
82918
+ console.log(` File: ${source_default.bold(result.absoluteOutputPath ?? result.outputPath)}`);
82791
82919
  if (result.beat) console.log(` Beat: ${result.beat}`);
82792
82920
  if (result.root) console.log(` Root: ${result.root}`);
82793
82921
  console.log(` Format: ${result.format ?? "mp4"}`);
@@ -82800,6 +82928,12 @@ function printRenderResult(spinner2, result) {
82800
82928
  console.log(` Audio: ${audio}`);
82801
82929
  }
82802
82930
  if (result.audioMuxWarning) console.log(source_default.yellow(` Warning: ${result.audioMuxWarning}`));
82931
+ if (result.openCommand) console.log(` Open: ${source_default.dim(result.openCommand)}`);
82932
+ if (result.revealCommand) console.log(` Reveal: ${source_default.dim(result.revealCommand)}`);
82933
+ if (result.opened) console.log(source_default.green(" Opened: yes"));
82934
+ if (result.revealed) console.log(source_default.green(" Revealed: yes"));
82935
+ if (result.openError) console.log(source_default.yellow(` Open warn: ${result.openError}`));
82936
+ if (result.revealError) console.log(source_default.yellow(` Reveal warn: ${result.revealError}`));
82803
82937
  }
82804
82938
  function parseFps(value) {
82805
82939
  const n = Number.parseInt(value, 10);
@@ -109,6 +109,56 @@ function zodToJsonSchema(schema) {
109
109
  }
110
110
 
111
111
  // src/tools/adapters/mcp.ts
112
+ function formatConsolePart(value) {
113
+ if (typeof value === "string") return value;
114
+ if (value instanceof Error) return value.stack ?? value.message;
115
+ try {
116
+ return JSON.stringify(value);
117
+ } catch {
118
+ return String(value);
119
+ }
120
+ }
121
+ function invokeWriteCallback(encodingOrCallback, callback) {
122
+ const cb = typeof encodingOrCallback === "function" ? encodingOrCallback : typeof callback === "function" ? callback : void 0;
123
+ if (cb) queueMicrotask(() => cb());
124
+ }
125
+ function formatStdoutChunk(chunk, encodingOrCallback) {
126
+ if (Buffer.isBuffer(chunk)) {
127
+ const encoding = typeof encodingOrCallback === "string" ? encodingOrCallback : "utf8";
128
+ return chunk.toString(encoding);
129
+ }
130
+ return String(chunk);
131
+ }
132
+ async function withCapturedStdout(fn) {
133
+ const originalWrite = process.stdout.write;
134
+ const originalConsoleLog = console.log;
135
+ const originalConsoleInfo = console.info;
136
+ const originalConsoleDebug = console.debug;
137
+ let captured = "";
138
+ const captureConsole = (...values) => {
139
+ captured += `${values.map(formatConsolePart).join(" ")}
140
+ `;
141
+ };
142
+ process.stdout.write = (chunk, encodingOrCallback, callback) => {
143
+ captured += formatStdoutChunk(chunk, encodingOrCallback);
144
+ invokeWriteCallback(encodingOrCallback, callback);
145
+ return true;
146
+ };
147
+ console.log = captureConsole;
148
+ console.info = captureConsole;
149
+ console.debug = captureConsole;
150
+ try {
151
+ return await fn();
152
+ } finally {
153
+ process.stdout.write = originalWrite;
154
+ console.log = originalConsoleLog;
155
+ console.info = originalConsoleInfo;
156
+ console.debug = originalConsoleDebug;
157
+ if (captured.trim() && process.env.VIBE_MCP_DEBUG_STDIO === "1") {
158
+ process.stderr.write(captured);
159
+ }
160
+ }
161
+ }
112
162
  function formatZodError(err) {
113
163
  const missing = err.issues.filter((i) => {
114
164
  if (i.code !== "invalid_type") return false;
@@ -155,10 +205,12 @@ function buildMcpDispatcher(manifest) {
155
205
  };
156
206
  }
157
207
  try {
158
- const result = await tool.execute(parsed.data, {
159
- workingDirectory: process.cwd(),
160
- surface: "mcp"
161
- });
208
+ const result = await withCapturedStdout(
209
+ () => tool.execute(parsed.data, {
210
+ workingDirectory: process.cwd(),
211
+ surface: "mcp"
212
+ })
213
+ );
162
214
  const text = result.success ? JSON.stringify({ success: true, ...result.data }) : result.data ? JSON.stringify({
163
215
  success: false,
164
216
  error: result.error ?? "unknown error",
@@ -29537,15 +29537,18 @@ var init_scene_audio_mux = __esm({
29537
29537
  // src/commands/_shared/scene-render.ts
29538
29538
  var scene_render_exports = {};
29539
29539
  __export(scene_render_exports, {
29540
+ buildMediaOpenCommand: () => buildMediaOpenCommand,
29540
29541
  buildRenderConfig: () => buildRenderConfig,
29541
29542
  defaultOutputPath: () => defaultOutputPath,
29542
29543
  executeSceneRender: () => executeSceneRender,
29543
29544
  prepareBeatRenderRoot: () => prepareBeatRenderRoot,
29544
29545
  qualityToCrf: () => qualityToCrf
29545
29546
  });
29547
+ import { execFile as execFile2 } from "node:child_process";
29546
29548
  import { existsSync as existsSync11 } from "node:fs";
29547
29549
  import { mkdir as mkdir6, readFile as readFile12, stat as stat2, writeFile as writeFile11 } from "node:fs/promises";
29548
29550
  import { resolve as resolve15, relative as relative6, dirname as dirname5, basename as basename3, join as join10, isAbsolute } from "node:path";
29551
+ import { promisify as promisify2 } from "node:util";
29549
29552
  import {
29550
29553
  createRenderJob,
29551
29554
  executeRenderJob
@@ -29572,6 +29575,38 @@ function buildRenderConfig(opts) {
29572
29575
  workers: opts.workers ?? 1
29573
29576
  };
29574
29577
  }
29578
+ function buildMediaOpenCommand(action, filePath, platform = process.platform) {
29579
+ if (platform === "darwin") {
29580
+ const args = action === "reveal" ? ["-R", filePath] : [filePath];
29581
+ return {
29582
+ command: "open",
29583
+ args,
29584
+ display: ["open", ...args].map(quoteCliArg).join(" ")
29585
+ };
29586
+ }
29587
+ if (platform === "win32") {
29588
+ if (action === "reveal") {
29589
+ const args2 = [`/select,${filePath}`];
29590
+ return {
29591
+ command: "explorer",
29592
+ args: args2,
29593
+ display: ["explorer", ...args2].map(quoteCliArg).join(" ")
29594
+ };
29595
+ }
29596
+ const args = ["/c", "start", "", filePath];
29597
+ return {
29598
+ command: "cmd",
29599
+ args,
29600
+ display: ["cmd", ...args].map(quoteCliArg).join(" ")
29601
+ };
29602
+ }
29603
+ const target = action === "reveal" ? dirname5(filePath) : filePath;
29604
+ return {
29605
+ command: "xdg-open",
29606
+ args: [target],
29607
+ display: ["xdg-open", target].map(quoteCliArg).join(" ")
29608
+ };
29609
+ }
29575
29610
  async function executeSceneRender(opts = {}) {
29576
29611
  const projectDir = resolve15(opts.projectDir ?? ".");
29577
29612
  const projectConfig = await readProjectConfig(projectDir);
@@ -29695,6 +29730,9 @@ async function executeSceneRender(opts = {}) {
29695
29730
  beat: opts.beatId ?? null,
29696
29731
  root,
29697
29732
  outputPath: relative6(process.cwd(), outputPath) || outputPath,
29733
+ absoluteOutputPath: outputPath,
29734
+ openCommand: buildMediaOpenCommand("open", outputPath).display,
29735
+ revealCommand: buildMediaOpenCommand("reveal", outputPath).display,
29698
29736
  durationMs: Date.now() - start,
29699
29737
  framesRendered: job.framesRendered,
29700
29738
  totalFrames: job.totalFrames,
@@ -29705,12 +29743,38 @@ async function executeSceneRender(opts = {}) {
29705
29743
  audioMuxApplied,
29706
29744
  audioMuxWarning
29707
29745
  };
29746
+ if (opts.revealInFinder) {
29747
+ try {
29748
+ await runMediaOpenAction("reveal", outputPath);
29749
+ result.revealed = true;
29750
+ } catch (err) {
29751
+ result.revealed = false;
29752
+ result.revealError = err instanceof Error ? err.message : String(err);
29753
+ }
29754
+ }
29755
+ if (opts.openAfterRender) {
29756
+ try {
29757
+ await runMediaOpenAction("open", outputPath);
29758
+ result.opened = true;
29759
+ } catch (err) {
29760
+ result.opened = false;
29761
+ result.openError = err instanceof Error ? err.message : String(err);
29762
+ }
29763
+ }
29708
29764
  result.reportPath = await writeRenderReport(projectDir, {
29709
29765
  ...result,
29710
29766
  outputPath
29711
29767
  });
29712
29768
  return result;
29713
29769
  }
29770
+ async function runMediaOpenAction(action, filePath) {
29771
+ const cmd = buildMediaOpenCommand(action, filePath);
29772
+ await execFileAsync2(cmd.command, cmd.args);
29773
+ }
29774
+ function quoteCliArg(value) {
29775
+ if (/^[A-Za-z0-9_/@%+=:,.-]+$/.test(value)) return value;
29776
+ return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
29777
+ }
29714
29778
  async function safeStat(p) {
29715
29779
  try {
29716
29780
  return await stat2(p);
@@ -29881,6 +29945,13 @@ async function writeRenderReport(projectDir, result) {
29881
29945
  beat: result.beat ?? null,
29882
29946
  root: result.root,
29883
29947
  outputPath: result.outputPath,
29948
+ absoluteOutputPath: result.absoluteOutputPath,
29949
+ openCommand: result.openCommand,
29950
+ revealCommand: result.revealCommand,
29951
+ opened: result.opened,
29952
+ revealed: result.revealed,
29953
+ openError: result.openError,
29954
+ revealError: result.revealError,
29884
29955
  fps: result.fps,
29885
29956
  quality: result.quality,
29886
29957
  format: result.format,
@@ -29925,6 +29996,7 @@ function escapeAttr(value) {
29925
29996
  function unique(items) {
29926
29997
  return [...new Set(items.filter((item) => Boolean(item)))];
29927
29998
  }
29999
+ var execFileAsync2;
29928
30000
  var init_scene_render = __esm({
29929
30001
  "src/commands/_shared/scene-render.ts"() {
29930
30002
  "use strict";
@@ -29934,6 +30006,7 @@ var init_scene_render = __esm({
29934
30006
  init_scene_audio_mux();
29935
30007
  init_project_config();
29936
30008
  init_scene_project();
30009
+ execFileAsync2 = promisify2(execFile2);
29937
30010
  }
29938
30011
  });
29939
30012
 
@@ -35843,11 +35916,11 @@ registerRoot(Root);
35843
35916
  return dir;
35844
35917
  }
35845
35918
  async function this_npmInstall(dir) {
35846
- const { execFile: execFile2 } = await import("node:child_process");
35847
- const { promisify: promisify2 } = await import("node:util");
35848
- const execFileAsync2 = promisify2(execFile2);
35919
+ const { execFile: execFile3 } = await import("node:child_process");
35920
+ const { promisify: promisify3 } = await import("node:util");
35921
+ const execFileAsync3 = promisify3(execFile3);
35849
35922
  try {
35850
- await execFileAsync2("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund"], {
35923
+ await execFileAsync3("npm", ["install", "--prefer-offline", "--no-audit", "--no-fund"], {
35851
35924
  cwd: dir,
35852
35925
  timeout: 18e4
35853
35926
  });
@@ -35871,13 +35944,13 @@ async function renderMotion(options) {
35871
35944
  );
35872
35945
  try {
35873
35946
  const entryPoint = join16(dir, "Root.tsx");
35874
- const { execFile: execFile2 } = await import("node:child_process");
35875
- const { promisify: promisify2 } = await import("node:util");
35876
- const execFileAsync2 = promisify2(execFile2);
35947
+ const { execFile: execFile3 } = await import("node:child_process");
35948
+ const { promisify: promisify3 } = await import("node:util");
35949
+ const execFileAsync3 = promisify3(execFile3);
35877
35950
  if (transparent) {
35878
35951
  const webmOut = options.outputPath.replace(/\.\w+$/, ".webm");
35879
35952
  try {
35880
- await execFileAsync2("npx", [
35953
+ await execFileAsync3("npx", [
35881
35954
  "remotion",
35882
35955
  "render",
35883
35956
  entryPoint,
@@ -35894,7 +35967,7 @@ async function renderMotion(options) {
35894
35967
  } catch {
35895
35968
  }
35896
35969
  try {
35897
- await execFileAsync2("npx", [
35970
+ await execFileAsync3("npx", [
35898
35971
  "remotion",
35899
35972
  "render",
35900
35973
  entryPoint,
@@ -35914,7 +35987,7 @@ async function renderMotion(options) {
35914
35987
  }
35915
35988
  }
35916
35989
  const mp4Out = options.outputPath.replace(/\.\w+$/, ".mp4");
35917
- await execFileAsync2("npx", [
35990
+ await execFileAsync3("npx", [
35918
35991
  "remotion",
35919
35992
  "render",
35920
35993
  entryPoint,
@@ -52583,7 +52656,9 @@ var sceneRenderSchema = external_exports.object({
52583
52656
  fps: external_exports.number().optional().describe("Frames per second. Must be 24, 30, or 60. Default 30."),
52584
52657
  quality: external_exports.enum(["draft", "standard", "high"]).optional().describe("Quality preset. Default 'standard'."),
52585
52658
  format: external_exports.enum(["mp4", "webm", "mov"]).optional().describe("Container format. Default 'mp4'."),
52586
- workers: external_exports.number().optional().describe("Capture worker count (1-16). Default 1.")
52659
+ workers: external_exports.number().optional().describe("Capture worker count (1-16). Default 1."),
52660
+ openAfterRender: external_exports.boolean().optional().describe("Open the rendered video in the OS default app after render. Default false."),
52661
+ revealInFinder: external_exports.boolean().optional().describe("Reveal the rendered video in Finder/file manager after render. Default false.")
52587
52662
  });
52588
52663
  var sceneRenderTool = defineTool({
52589
52664
  name: "render",
@@ -52601,7 +52676,9 @@ var sceneRenderTool = defineTool({
52601
52676
  fps: args.fps,
52602
52677
  quality: args.quality,
52603
52678
  format: args.format,
52604
- workers: args.workers
52679
+ workers: args.workers,
52680
+ openAfterRender: args.openAfterRender,
52681
+ revealInFinder: args.revealInFinder
52605
52682
  });
52606
52683
  if (!result.success) {
52607
52684
  return { success: false, error: result.error ?? "render failed" };
@@ -52610,6 +52687,13 @@ var sceneRenderTool = defineTool({
52610
52687
  success: true,
52611
52688
  data: {
52612
52689
  outputPath: result.outputPath,
52690
+ absoluteOutputPath: result.absoluteOutputPath,
52691
+ openCommand: result.openCommand,
52692
+ revealCommand: result.revealCommand,
52693
+ opened: result.opened,
52694
+ revealed: result.revealed,
52695
+ openError: result.openError,
52696
+ revealError: result.revealError,
52613
52697
  beat: result.beat,
52614
52698
  root: result.root,
52615
52699
  reportPath: result.reportPath,
@@ -52618,13 +52702,24 @@ var sceneRenderTool = defineTool({
52618
52702
  totalFrames: result.totalFrames,
52619
52703
  fps: result.fps,
52620
52704
  quality: result.quality,
52621
- format: result.format
52705
+ format: result.format,
52706
+ audioCount: result.audioCount,
52707
+ audioMuxApplied: result.audioMuxApplied,
52708
+ audioMuxWarning: result.audioMuxWarning
52622
52709
  },
52623
52710
  humanLines: [
52624
- `\u2705 Render complete: ${result.outputPath}`,
52711
+ `\u2705 Render complete: ${result.absoluteOutputPath ?? result.outputPath}`,
52625
52712
  ` duration: ${((result.durationMs ?? 0) / 1e3).toFixed(1)}s`,
52626
52713
  ` frames: ${result.framesRendered ?? "?"}${result.totalFrames ? ` / ${result.totalFrames}` : ""}`,
52627
- ` config: ${result.fps}fps \xB7 ${result.quality} \xB7 ${result.format}`
52714
+ ` config: ${result.fps}fps \xB7 ${result.quality} \xB7 ${result.format}`,
52715
+ ` audio: ${result.audioCount && result.audioCount > 0 ? `${result.audioCount} track${result.audioCount === 1 ? "" : "s"} muxed` : "silent"}`,
52716
+ ...result.openCommand ? [` open: ${result.openCommand}`] : [],
52717
+ ...result.revealCommand ? [` reveal: ${result.revealCommand}`] : [],
52718
+ ` inspect: vibe inspect render ${projectDir} --cheap --json`,
52719
+ ...result.opened ? [` opened: yes`] : [],
52720
+ ...result.revealed ? [` revealed: yes`] : [],
52721
+ ...result.openError ? [` open warning: ${result.openError}`] : [],
52722
+ ...result.revealError ? [` reveal warning: ${result.revealError}`] : []
52628
52723
  ]
52629
52724
  };
52630
52725
  }
@@ -108,6 +108,8 @@ export declare const sceneRenderTool: import("../define-tool.js").ToolDefinition
108
108
  quality: z.ZodOptional<z.ZodEnum<["draft", "standard", "high"]>>;
109
109
  format: z.ZodOptional<z.ZodEnum<["mp4", "webm", "mov"]>>;
110
110
  workers: z.ZodOptional<z.ZodNumber>;
111
+ openAfterRender: z.ZodOptional<z.ZodBoolean>;
112
+ revealInFinder: z.ZodOptional<z.ZodBoolean>;
111
113
  }, "strip", z.ZodTypeAny, {
112
114
  output?: string | undefined;
113
115
  fps?: number | undefined;
@@ -117,6 +119,8 @@ export declare const sceneRenderTool: import("../define-tool.js").ToolDefinition
117
119
  root?: string | undefined;
118
120
  beat?: string | undefined;
119
121
  workers?: number | undefined;
122
+ openAfterRender?: boolean | undefined;
123
+ revealInFinder?: boolean | undefined;
120
124
  }, {
121
125
  output?: string | undefined;
122
126
  fps?: number | undefined;
@@ -126,6 +130,8 @@ export declare const sceneRenderTool: import("../define-tool.js").ToolDefinition
126
130
  root?: string | undefined;
127
131
  beat?: string | undefined;
128
132
  workers?: number | undefined;
133
+ openAfterRender?: boolean | undefined;
134
+ revealInFinder?: boolean | undefined;
129
135
  }>>;
130
136
  export declare const sceneBuildTool: import("../define-tool.js").ToolDefinition<z.ZodObject<{
131
137
  projectDir: z.ZodOptional<z.ZodString>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/cli",
3
- "version": "0.106.6",
3
+ "version": "0.106.8",
4
4
  "description": "VibeFrame CLI - agentic video workflow runtime",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -83,8 +83,8 @@
83
83
  "tsx": "^4.21.0",
84
84
  "typescript": "^5.3.3",
85
85
  "vitest": "^1.2.2",
86
- "@vibeframe/ai-providers": "0.106.6",
87
- "@vibeframe/core": "0.106.6"
86
+ "@vibeframe/ai-providers": "0.106.8",
87
+ "@vibeframe/core": "0.106.8"
88
88
  },
89
89
  "engines": {
90
90
  "node": ">=20"