@vibeframe/cli 0.109.2 → 0.110.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 CHANGED
@@ -67838,6 +67838,82 @@ var init_long_poll = __esm({
67838
67838
  }
67839
67839
  });
67840
67840
 
67841
+ // src/tools/_shared/elicit.ts
67842
+ function planBuildElicitation(args) {
67843
+ const assetsWillRun = args.stage === void 0 || args.stage === "all" || args.stage === "assets";
67844
+ if (!assetsWillRun) return null;
67845
+ const properties = {};
67846
+ const required = [];
67847
+ const narrationUndecided = !args.skipNarration && (args.ttsProvider === void 0 || args.ttsProvider === "auto");
67848
+ if (narrationUndecided) {
67849
+ properties.narration = {
67850
+ type: "string",
67851
+ title: "Narration voice-over",
67852
+ description: "Which TTS engine narrates the video?",
67853
+ enum: [...NARRATION_CHOICES],
67854
+ enumNames: [
67855
+ "Kokoro \u2014 free, runs locally on this machine",
67856
+ "ElevenLabs \u2014 cloud TTS, uses your ElevenLabs credits"
67857
+ ]
67858
+ };
67859
+ required.push("narration");
67860
+ }
67861
+ if (args.skipBackdrop === void 0 && args.imageProvider === void 0) {
67862
+ properties.backdrop_images = {
67863
+ type: "string",
67864
+ title: "Backdrop images",
67865
+ description: "Generate AI background images for each scene?",
67866
+ enum: [...BACKDROP_CHOICES],
67867
+ enumNames: [
67868
+ "Skip \u2014 free, pure typographic scenes",
67869
+ "OpenAI \u2014 paid image generation (roughly $1-3 per video)"
67870
+ ]
67871
+ };
67872
+ required.push("backdrop_images");
67873
+ }
67874
+ if (args.maxCostUsd === void 0) {
67875
+ properties.max_cost_usd = {
67876
+ type: "number",
67877
+ title: "Max provider spend (USD)",
67878
+ description: "Abort before any paid provider call when the estimate exceeds this cap. Leave empty for no cap.",
67879
+ minimum: 0
67880
+ };
67881
+ }
67882
+ if (Object.keys(properties).length === 0) return null;
67883
+ return {
67884
+ message: "VibeFrame is about to build this video. Choose how the assets are generated:",
67885
+ requestedSchema: { type: "object", properties, required }
67886
+ };
67887
+ }
67888
+ function applyElicitationAnswers(args, content) {
67889
+ if (!content) return args;
67890
+ const next = { ...args };
67891
+ const narration = content.narration;
67892
+ if (typeof narration === "string" && NARRATION_CHOICES.includes(narration)) {
67893
+ next.ttsProvider = narration;
67894
+ }
67895
+ const backdrop = content.backdrop_images;
67896
+ if (backdrop === "skip") {
67897
+ next.skipBackdrop = true;
67898
+ } else if (backdrop === "openai") {
67899
+ next.skipBackdrop = false;
67900
+ next.imageProvider = "openai";
67901
+ }
67902
+ const cap = content.max_cost_usd;
67903
+ if (typeof cap === "number" && Number.isFinite(cap) && cap >= 0) {
67904
+ next.maxCostUsd = cap;
67905
+ }
67906
+ return next;
67907
+ }
67908
+ var NARRATION_CHOICES, BACKDROP_CHOICES;
67909
+ var init_elicit = __esm({
67910
+ "src/tools/_shared/elicit.ts"() {
67911
+ "use strict";
67912
+ NARRATION_CHOICES = ["kokoro", "elevenlabs"];
67913
+ BACKDROP_CHOICES = ["skip", "openai"];
67914
+ }
67915
+ });
67916
+
67841
67917
  // src/tools/manifest/scene.ts
67842
67918
  import { resolve as resolve75, relative as relative12 } from "node:path";
67843
67919
  function summariseLint(result) {
@@ -67976,6 +68052,7 @@ var init_scene2 = __esm({
67976
68052
  init_zod();
67977
68053
  init_define_tool();
67978
68054
  init_long_poll();
68055
+ init_elicit();
67979
68056
  init_visual_styles();
67980
68057
  init_scene_project();
67981
68058
  init_scene();
@@ -68309,9 +68386,32 @@ var init_scene2 = __esm({
68309
68386
  name: "build",
68310
68387
  category: "scene",
68311
68388
  cost: "high",
68312
- description: "v0.60 one-shot orchestrator: read STORYBOARD.md per-beat YAML cues (narration / backdrop / duration), dispatch TTS + image generation per beat, compose scene HTML via the compose-scenes-with-skills pipeline, then render to MP4. Use this instead of chaining init + scene_add + render manually. Caches by SHA256 of (DESIGN.md + cue body) so re-runs are idempotent and cheap. Long runs: over MCP, calls exceeding ~45s return immediately with { promoted: true, jobId, status: 'running' } \u2014 poll status_job every 15-30s until completed/failed instead of re-invoking build. Emits MCP progress notifications when the client supplies a progressToken.",
68389
+ description: "v0.60 one-shot orchestrator: read STORYBOARD.md per-beat YAML cues (narration / backdrop / duration), dispatch TTS + image generation per beat, compose scene HTML via the compose-scenes-with-skills pipeline, then render to MP4. Use this instead of chaining init + scene_add + render manually. Caches by SHA256 of (DESIGN.md + cue body) so re-runs are idempotent and cheap. Long runs: over MCP, calls exceeding ~45s return immediately with { promoted: true, jobId, status: 'running' } \u2014 poll status_job every 15-30s until completed/failed instead of re-invoking build. Emits MCP progress notifications when the client supplies a progressToken. When the MCP host supports elicitation, unspecified asset choices (narration provider, backdrop images, cost cap) are confirmed with the user via a form before the build starts.",
68313
68390
  schema: sceneBuildSchema,
68314
68391
  async execute(args, ctx) {
68392
+ let warnings;
68393
+ if (ctx.surface === "mcp" && ctx.elicit) {
68394
+ const form = planBuildElicitation(args);
68395
+ if (form) {
68396
+ const outcome = await ctx.elicit(form).catch(() => null);
68397
+ if (outcome === null) {
68398
+ warnings = ["Asset-choice elicitation failed or timed out; proceeding with defaults."];
68399
+ } else if (outcome.action === "accept") {
68400
+ args = applyElicitationAnswers(args, outcome.content);
68401
+ } else {
68402
+ return {
68403
+ success: false,
68404
+ error: "Build cancelled \u2014 the user declined the asset choices.",
68405
+ data: {
68406
+ cancelled: true,
68407
+ retryWith: [
68408
+ `build { projectDir: "${args.projectDir ?? "."}", ttsProvider: "kokoro", skipBackdrop: true }`
68409
+ ]
68410
+ }
68411
+ };
68412
+ }
68413
+ }
68414
+ }
68315
68415
  const projectDir = args.projectDir ? resolve75(ctx.workingDirectory, args.projectDir) : ctx.workingDirectory;
68316
68416
  const runBuild = (report) => executeSceneBuild({
68317
68417
  projectDir,
@@ -68339,7 +68439,9 @@ var init_scene2 = __esm({
68339
68439
  ctx.onProgress?.({ message: update.message ?? update.stage });
68340
68440
  report(update);
68341
68441
  }
68342
- }).then(mapBuildResultToToolResult);
68442
+ }).then(mapBuildResultToToolResult).then(
68443
+ (result) => warnings === void 0 ? result : { ...result, data: { ...result.data, elicitationWarnings: warnings } }
68444
+ );
68343
68445
  if (ctx.surface !== "mcp") {
68344
68446
  return runBuild(() => void 0);
68345
68447
  }
@@ -72911,7 +73013,7 @@ init_source();
72911
73013
  // package.json
72912
73014
  var package_default = {
72913
73015
  name: "@vibeframe/cli",
72914
- version: "0.109.2",
73016
+ version: "0.110.0",
72915
73017
  description: "VibeFrame CLI - agentic video workflow runtime",
72916
73018
  private: false,
72917
73019
  license: "MIT",
@@ -0,0 +1,31 @@
1
+ import type { ElicitForm } from "../adapters/mcp.js";
2
+ /**
3
+ * @module tools/_shared/elicit
4
+ *
5
+ * Pre-flight elicitation for the `build` tool: when an MCP client supports
6
+ * elicitation, ask the user the choices the host agent would otherwise pick
7
+ * silently (narration provider, paid backdrop generation, cost ceiling).
8
+ * Pure functions — the MCP wiring lives in the tool's execute and the
9
+ * server's makeElicitFn; CLI/Agent surfaces never reach this code path.
10
+ */
11
+ /** The subset of build args that drive the question set. */
12
+ export interface BuildChoiceArgs {
13
+ stage?: "assets" | "compose" | "sync" | "render" | "all";
14
+ ttsProvider?: "auto" | "elevenlabs" | "kokoro";
15
+ skipNarration?: boolean;
16
+ skipBackdrop?: boolean;
17
+ imageProvider?: string;
18
+ maxCostUsd?: number;
19
+ }
20
+ /**
21
+ * Builds the elicitation form for a build call, asking ONLY about choices the
22
+ * caller left unspecified. Returns null when there is nothing to ask — every
23
+ * choice explicit, or the requested stage doesn't generate assets.
24
+ */
25
+ export declare function planBuildElicitation(args: BuildChoiceArgs): ElicitForm | null;
26
+ /**
27
+ * Maps accepted form answers back onto build args. Unknown or invalid values
28
+ * are ignored so a misbehaving client can never produce a worse state than
29
+ * the defaults the user would have gotten without elicitation.
30
+ */
31
+ export declare function applyElicitationAnswers<T extends BuildChoiceArgs>(args: T, content: Record<string, string | number | boolean | string[]> | undefined): T;
@@ -0,0 +1 @@
1
+ export {};
@@ -22,6 +22,25 @@ export interface McpTool {
22
22
  description: string;
23
23
  inputSchema: McpInputSchema;
24
24
  }
25
+ /**
26
+ * Structural mirror of the MCP elicitation form request/result (the CLI
27
+ * package deliberately has no dependency on @modelcontextprotocol/sdk).
28
+ * `requestedSchema.properties` values follow the spec's flat primitive
29
+ * schema: string (optionally with enum/enumNames), number, boolean.
30
+ */
31
+ export interface ElicitForm {
32
+ message: string;
33
+ requestedSchema: {
34
+ type: "object";
35
+ properties: Record<string, unknown>;
36
+ required?: string[];
37
+ };
38
+ }
39
+ export interface ElicitOutcome {
40
+ action: "accept" | "decline" | "cancel";
41
+ content?: Record<string, string | number | boolean | string[]>;
42
+ }
43
+ export type ElicitFn = (form: ElicitForm) => Promise<ElicitOutcome>;
25
44
  /** Per-call extras forwarded by the MCP server request handler. */
26
45
  export interface McpCallExtra {
27
46
  /**
@@ -34,6 +53,12 @@ export interface McpCallExtra {
34
53
  total?: number;
35
54
  message?: string;
36
55
  }) => void;
56
+ /**
57
+ * Sends an `elicitation/create` form to the client and resolves with the
58
+ * user's answer. Only present when the connected client advertises the
59
+ * elicitation capability.
60
+ */
61
+ elicit?: ElicitFn;
37
62
  }
38
63
  export type McpDispatcher = (name: string, args: Record<string, unknown>, extra?: McpCallExtra) => Promise<{
39
64
  content: Array<{
@@ -245,7 +245,8 @@ function buildMcpDispatcher(manifest) {
245
245
  () => tool.execute(parsed.data, {
246
246
  workingDirectory: process.cwd(),
247
247
  surface: "mcp",
248
- ...extra?.onProgress ? { onProgress: sink.send } : {}
248
+ ...extra?.onProgress ? { onProgress: sink.send } : {},
249
+ ...extra?.elicit ? { elicit: extra.elicit } : {}
249
250
  })
250
251
  );
251
252
  const text = result.success ? JSON.stringify({ success: true, ...result.data }) : result.data ? JSON.stringify({
@@ -49,6 +49,23 @@ export interface ExecuteContext {
49
49
  total?: number;
50
50
  message?: string;
51
51
  }) => void;
52
+ /**
53
+ * Optional MCP elicitation channel. Present only when the connected MCP
54
+ * client advertises the elicitation capability; CLI/Agent surfaces leave
55
+ * it undefined, so CLI behavior never changes. See adapters/mcp.ts for
56
+ * the structural types.
57
+ */
58
+ elicit?: (form: {
59
+ message: string;
60
+ requestedSchema: {
61
+ type: "object";
62
+ properties: Record<string, unknown>;
63
+ required?: string[];
64
+ };
65
+ }) => Promise<{
66
+ action: "accept" | "decline" | "cancel";
67
+ content?: Record<string, string | number | boolean | string[]>;
68
+ }>;
52
69
  }
53
70
  export interface ToolExecuteResult {
54
71
  success: boolean;
@@ -51300,6 +51300,76 @@ function pickOutputPath(data) {
51300
51300
  return typeof candidate === "string" ? candidate : void 0;
51301
51301
  }
51302
51302
 
51303
+ // src/tools/_shared/elicit.ts
51304
+ var NARRATION_CHOICES = ["kokoro", "elevenlabs"];
51305
+ var BACKDROP_CHOICES = ["skip", "openai"];
51306
+ function planBuildElicitation(args) {
51307
+ const assetsWillRun = args.stage === void 0 || args.stage === "all" || args.stage === "assets";
51308
+ if (!assetsWillRun) return null;
51309
+ const properties = {};
51310
+ const required = [];
51311
+ const narrationUndecided = !args.skipNarration && (args.ttsProvider === void 0 || args.ttsProvider === "auto");
51312
+ if (narrationUndecided) {
51313
+ properties.narration = {
51314
+ type: "string",
51315
+ title: "Narration voice-over",
51316
+ description: "Which TTS engine narrates the video?",
51317
+ enum: [...NARRATION_CHOICES],
51318
+ enumNames: [
51319
+ "Kokoro \u2014 free, runs locally on this machine",
51320
+ "ElevenLabs \u2014 cloud TTS, uses your ElevenLabs credits"
51321
+ ]
51322
+ };
51323
+ required.push("narration");
51324
+ }
51325
+ if (args.skipBackdrop === void 0 && args.imageProvider === void 0) {
51326
+ properties.backdrop_images = {
51327
+ type: "string",
51328
+ title: "Backdrop images",
51329
+ description: "Generate AI background images for each scene?",
51330
+ enum: [...BACKDROP_CHOICES],
51331
+ enumNames: [
51332
+ "Skip \u2014 free, pure typographic scenes",
51333
+ "OpenAI \u2014 paid image generation (roughly $1-3 per video)"
51334
+ ]
51335
+ };
51336
+ required.push("backdrop_images");
51337
+ }
51338
+ if (args.maxCostUsd === void 0) {
51339
+ properties.max_cost_usd = {
51340
+ type: "number",
51341
+ title: "Max provider spend (USD)",
51342
+ description: "Abort before any paid provider call when the estimate exceeds this cap. Leave empty for no cap.",
51343
+ minimum: 0
51344
+ };
51345
+ }
51346
+ if (Object.keys(properties).length === 0) return null;
51347
+ return {
51348
+ message: "VibeFrame is about to build this video. Choose how the assets are generated:",
51349
+ requestedSchema: { type: "object", properties, required }
51350
+ };
51351
+ }
51352
+ function applyElicitationAnswers(args, content) {
51353
+ if (!content) return args;
51354
+ const next = { ...args };
51355
+ const narration = content.narration;
51356
+ if (typeof narration === "string" && NARRATION_CHOICES.includes(narration)) {
51357
+ next.ttsProvider = narration;
51358
+ }
51359
+ const backdrop = content.backdrop_images;
51360
+ if (backdrop === "skip") {
51361
+ next.skipBackdrop = true;
51362
+ } else if (backdrop === "openai") {
51363
+ next.skipBackdrop = false;
51364
+ next.imageProvider = "openai";
51365
+ }
51366
+ const cap = content.max_cost_usd;
51367
+ if (typeof cap === "number" && Number.isFinite(cap) && cap >= 0) {
51368
+ next.maxCostUsd = cap;
51369
+ }
51370
+ return next;
51371
+ }
51372
+
51303
51373
  // src/commands/_shared/visual-styles.ts
51304
51374
  var STYLES = [
51305
51375
  {
@@ -53538,9 +53608,32 @@ var sceneBuildTool = defineTool({
53538
53608
  name: "build",
53539
53609
  category: "scene",
53540
53610
  cost: "high",
53541
- description: "v0.60 one-shot orchestrator: read STORYBOARD.md per-beat YAML cues (narration / backdrop / duration), dispatch TTS + image generation per beat, compose scene HTML via the compose-scenes-with-skills pipeline, then render to MP4. Use this instead of chaining init + scene_add + render manually. Caches by SHA256 of (DESIGN.md + cue body) so re-runs are idempotent and cheap. Long runs: over MCP, calls exceeding ~45s return immediately with { promoted: true, jobId, status: 'running' } \u2014 poll status_job every 15-30s until completed/failed instead of re-invoking build. Emits MCP progress notifications when the client supplies a progressToken.",
53611
+ description: "v0.60 one-shot orchestrator: read STORYBOARD.md per-beat YAML cues (narration / backdrop / duration), dispatch TTS + image generation per beat, compose scene HTML via the compose-scenes-with-skills pipeline, then render to MP4. Use this instead of chaining init + scene_add + render manually. Caches by SHA256 of (DESIGN.md + cue body) so re-runs are idempotent and cheap. Long runs: over MCP, calls exceeding ~45s return immediately with { promoted: true, jobId, status: 'running' } \u2014 poll status_job every 15-30s until completed/failed instead of re-invoking build. Emits MCP progress notifications when the client supplies a progressToken. When the MCP host supports elicitation, unspecified asset choices (narration provider, backdrop images, cost cap) are confirmed with the user via a form before the build starts.",
53542
53612
  schema: sceneBuildSchema,
53543
53613
  async execute(args, ctx) {
53614
+ let warnings;
53615
+ if (ctx.surface === "mcp" && ctx.elicit) {
53616
+ const form = planBuildElicitation(args);
53617
+ if (form) {
53618
+ const outcome = await ctx.elicit(form).catch(() => null);
53619
+ if (outcome === null) {
53620
+ warnings = ["Asset-choice elicitation failed or timed out; proceeding with defaults."];
53621
+ } else if (outcome.action === "accept") {
53622
+ args = applyElicitationAnswers(args, outcome.content);
53623
+ } else {
53624
+ return {
53625
+ success: false,
53626
+ error: "Build cancelled \u2014 the user declined the asset choices.",
53627
+ data: {
53628
+ cancelled: true,
53629
+ retryWith: [
53630
+ `build { projectDir: "${args.projectDir ?? "."}", ttsProvider: "kokoro", skipBackdrop: true }`
53631
+ ]
53632
+ }
53633
+ };
53634
+ }
53635
+ }
53636
+ }
53544
53637
  const projectDir = args.projectDir ? resolve26(ctx.workingDirectory, args.projectDir) : ctx.workingDirectory;
53545
53638
  const runBuild = (report) => executeSceneBuild({
53546
53639
  projectDir,
@@ -53568,7 +53661,9 @@ var sceneBuildTool = defineTool({
53568
53661
  ctx.onProgress?.({ message: update.message ?? update.stage });
53569
53662
  report(update);
53570
53663
  }
53571
- }).then(mapBuildResultToToolResult);
53664
+ }).then(mapBuildResultToToolResult).then(
53665
+ (result) => warnings === void 0 ? result : { ...result, data: { ...result.data, elicitationWarnings: warnings } }
53666
+ );
53572
53667
  if (ctx.surface !== "mcp") {
53573
53668
  return runBuild(() => void 0);
53574
53669
  }
@@ -167,9 +167,9 @@ export declare const sceneBuildTool: import("../define-tool.js").ToolDefinition<
167
167
  skipBackdrop?: boolean | undefined;
168
168
  skipRender?: boolean | undefined;
169
169
  imageProvider?: "openai" | undefined;
170
+ ttsProvider?: "elevenlabs" | "auto" | "kokoro" | undefined;
170
171
  skipVideo?: boolean | undefined;
171
172
  skipMusic?: boolean | undefined;
172
- ttsProvider?: "elevenlabs" | "auto" | "kokoro" | undefined;
173
173
  videoProvider?: "grok" | "seedance" | "kling" | "runway" | "veo" | undefined;
174
174
  musicProvider?: "elevenlabs" | "replicate" | undefined;
175
175
  imageQuality?: "standard" | "hd" | undefined;
@@ -188,9 +188,9 @@ export declare const sceneBuildTool: import("../define-tool.js").ToolDefinition<
188
188
  skipBackdrop?: boolean | undefined;
189
189
  skipRender?: boolean | undefined;
190
190
  imageProvider?: "openai" | undefined;
191
+ ttsProvider?: "elevenlabs" | "auto" | "kokoro" | undefined;
191
192
  skipVideo?: boolean | undefined;
192
193
  skipMusic?: boolean | undefined;
193
- ttsProvider?: "elevenlabs" | "auto" | "kokoro" | undefined;
194
194
  videoProvider?: "grok" | "seedance" | "kling" | "runway" | "veo" | undefined;
195
195
  musicProvider?: "elevenlabs" | "replicate" | undefined;
196
196
  imageQuality?: "standard" | "hd" | undefined;
@@ -107,9 +107,9 @@ export declare const planTool: import("../define-tool.js").ToolDefinition<z.ZodO
107
107
  skipNarration?: boolean | undefined;
108
108
  skipBackdrop?: boolean | undefined;
109
109
  imageProvider?: string | undefined;
110
+ ttsProvider?: "elevenlabs" | "auto" | "kokoro" | undefined;
110
111
  skipVideo?: boolean | undefined;
111
112
  skipMusic?: boolean | undefined;
112
- ttsProvider?: "elevenlabs" | "auto" | "kokoro" | undefined;
113
113
  videoProvider?: "grok" | "seedance" | "kling" | "runway" | "veo" | undefined;
114
114
  musicProvider?: "elevenlabs" | "replicate" | undefined;
115
115
  imageQuality?: "standard" | "hd" | undefined;
@@ -125,9 +125,9 @@ export declare const planTool: import("../define-tool.js").ToolDefinition<z.ZodO
125
125
  skipNarration?: boolean | undefined;
126
126
  skipBackdrop?: boolean | undefined;
127
127
  imageProvider?: string | undefined;
128
+ ttsProvider?: "elevenlabs" | "auto" | "kokoro" | undefined;
128
129
  skipVideo?: boolean | undefined;
129
130
  skipMusic?: boolean | undefined;
130
- ttsProvider?: "elevenlabs" | "auto" | "kokoro" | undefined;
131
131
  videoProvider?: "grok" | "seedance" | "kling" | "runway" | "veo" | undefined;
132
132
  musicProvider?: "elevenlabs" | "replicate" | undefined;
133
133
  imageQuality?: "standard" | "hd" | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/cli",
3
- "version": "0.109.2",
3
+ "version": "0.110.0",
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.109.2",
87
- "@vibeframe/core": "0.109.2"
86
+ "@vibeframe/ai-providers": "0.110.0",
87
+ "@vibeframe/core": "0.110.0"
88
88
  },
89
89
  "engines": {
90
90
  "node": ">=20"