@vibeframe/cli 0.91.0 → 0.91.1

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 +181 -39
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -31895,7 +31895,7 @@ async function promptConfirm(question, defaultYes = true) {
31895
31895
  if (answer === "") return defaultYes;
31896
31896
  return answer.toLowerCase().startsWith("y");
31897
31897
  }
31898
- async function promptMultiSelect(_question, options, defaultSelected = []) {
31898
+ async function promptMultiSelect(_question, options, defaultSelected = [], opts = {}) {
31899
31899
  const input = getTTYInputStream();
31900
31900
  const selected = options.map((_, i) => Boolean(defaultSelected[i]));
31901
31901
  if (typeof input.setRawMode === "function") {
@@ -31931,6 +31931,9 @@ async function promptMultiSelect(_question, options, defaultSelected = []) {
31931
31931
  };
31932
31932
  const onData = (char) => {
31933
31933
  if (char === "\r" || char === "\n") {
31934
+ if (opts.enterSelectsFocusedWhenEmpty && selected.every((v) => !v)) {
31935
+ selected[cursor] = true;
31936
+ }
31934
31937
  finish();
31935
31938
  } else if (char === "") {
31936
31939
  input.setRawMode(false);
@@ -62910,7 +62913,7 @@ init_source();
62910
62913
  // package.json
62911
62914
  var package_default = {
62912
62915
  name: "@vibeframe/cli",
62913
- version: "0.91.0",
62916
+ version: "0.91.1",
62914
62917
  description: "VibeFrame CLI - natural language video editing from the terminal",
62915
62918
  private: false,
62916
62919
  license: "MIT",
@@ -69987,6 +69990,26 @@ var AI_FEATURES = [
69987
69990
  defaultProvider: "OpenAI gpt-image-2 (Artificial Analysis #1, since v0.56)",
69988
69991
  alsoAvailable: "Gemini Nano Banana, Grok Imagine",
69989
69992
  keys: [{ configKey: "openai", envVar: "OPENAI_API_KEY", name: "OpenAI", url: "https://platform.openai.com/api-keys", what: "gpt-image-2 image generation + editing (also Whisper, Agent)" }],
69993
+ providerChoices: [
69994
+ {
69995
+ label: "OpenAI gpt-image-2",
69996
+ desc: "recommended default, image generation + editing",
69997
+ key: { configKey: "openai", envVar: "OPENAI_API_KEY", name: "OpenAI", url: "https://platform.openai.com/api-keys", what: "gpt-image-2 image generation + editing (also Whisper, Agent)" },
69998
+ defaultFor: { kind: "image", value: "openai" }
69999
+ },
70000
+ {
70001
+ label: "Gemini",
70002
+ desc: "Google image generation + multimodal analysis",
70003
+ key: { configKey: "google", envVar: "GOOGLE_API_KEY", name: "Google", url: "https://aistudio.google.com/apikey", what: "Gemini image generation and multimodal analysis" },
70004
+ defaultFor: { kind: "image", value: "gemini" }
70005
+ },
70006
+ {
70007
+ label: "Grok",
70008
+ desc: "xAI image generation",
70009
+ key: { configKey: "xai", envVar: "XAI_API_KEY", name: "xAI", url: "https://console.x.ai/", what: "Grok image generation and Agent" },
70010
+ defaultFor: { kind: "image", value: "grok" }
70011
+ }
70012
+ ],
69990
70013
  tryCommand: 'vibe generate image "a sunset over mountains" -o test.png'
69991
70014
  },
69992
70015
  {
@@ -69995,6 +70018,38 @@ var AI_FEATURES = [
69995
70018
  defaultProvider: "Seedance 2.0 via fal.ai (Artificial Analysis #2 t2v + i2v, since v0.57)",
69996
70019
  alsoAvailable: "Grok Imagine, Kling, Runway Gen-4.5, Google Veo",
69997
70020
  keys: [{ configKey: "fal", envVar: "FAL_KEY", name: "Seedance 2.0 (via fal.ai)", url: "https://fal.ai/dashboard/keys", what: "ByteDance Seedance 2.0 text-to-video and image-to-video" }],
70021
+ providerChoices: [
70022
+ {
70023
+ label: "Seedance 2.0 via fal.ai",
70024
+ desc: "recommended default, text-to-video + image-to-video",
70025
+ key: { configKey: "fal", envVar: "FAL_KEY", name: "Seedance 2.0 (via fal.ai)", url: "https://fal.ai/dashboard/keys", what: "ByteDance Seedance 2.0 text-to-video and image-to-video" },
70026
+ defaultFor: { kind: "video", value: "seedance" }
70027
+ },
70028
+ {
70029
+ label: "Grok",
70030
+ desc: "xAI video generation with audio",
70031
+ key: { configKey: "xai", envVar: "XAI_API_KEY", name: "xAI", url: "https://console.x.ai/", what: "Grok video generation and Agent" },
70032
+ defaultFor: { kind: "video", value: "grok" }
70033
+ },
70034
+ {
70035
+ label: "Kling",
70036
+ desc: "Kling v2.5/v3 video generation",
70037
+ key: { configKey: "kling", envVar: "KLING_API_KEY", name: "Kling", url: "https://platform.klingai.com/", what: "Kling video generation" },
70038
+ defaultFor: { kind: "video", value: "kling" }
70039
+ },
70040
+ {
70041
+ label: "Runway",
70042
+ desc: "Runway Gen-4.5 video generation",
70043
+ key: { configKey: "runway", envVar: "RUNWAY_API_SECRET", name: "Runway", url: "https://app.runwayml.com/settings/api-keys", what: "Runway Gen-4.5 video generation" },
70044
+ defaultFor: { kind: "video", value: "runway" }
70045
+ },
70046
+ {
70047
+ label: "Veo",
70048
+ desc: "Google Veo video generation",
70049
+ key: { configKey: "google", envVar: "GOOGLE_API_KEY", name: "Google", url: "https://aistudio.google.com/apikey", what: "Google Veo video generation" },
70050
+ defaultFor: { kind: "video", value: "veo" }
70051
+ }
70052
+ ],
69998
70053
  tryCommand: 'vibe generate video "ocean waves" -o waves.mp4'
69999
70054
  },
70000
70055
  {
@@ -70003,6 +70058,23 @@ var AI_FEATURES = [
70003
70058
  defaultProvider: "ElevenLabs (paid, premium quality) \u2014 falls back to local Kokoro when no key (free, since v0.54)",
70004
70059
  alsoAvailable: "Replicate MusicGen (music only)",
70005
70060
  keys: [{ configKey: "elevenlabs", envVar: "ELEVENLABS_API_KEY", name: "ElevenLabs", url: "https://elevenlabs.io/app/settings/api-keys", what: "Text-to-speech, sound effects, music, voice cloning (skip to use local Kokoro)" }],
70061
+ providerChoices: [
70062
+ {
70063
+ label: "ElevenLabs",
70064
+ desc: "premium TTS, SFX, music, voice clone",
70065
+ key: { configKey: "elevenlabs", envVar: "ELEVENLABS_API_KEY", name: "ElevenLabs", url: "https://elevenlabs.io/app/settings/api-keys", what: "Text-to-speech, sound effects, music, voice cloning" }
70066
+ },
70067
+ {
70068
+ label: "Kokoro local",
70069
+ desc: "free local TTS fallback, no API key",
70070
+ keyless: true
70071
+ },
70072
+ {
70073
+ label: "Replicate MusicGen",
70074
+ desc: "background music generation",
70075
+ key: { configKey: "replicate", envVar: "REPLICATE_API_TOKEN", name: "Replicate", url: "https://replicate.com/account/api-tokens", what: "MusicGen background music" }
70076
+ }
70077
+ ],
70006
70078
  tryCommand: 'vibe generate speech "Hello world" -o hello.mp3'
70007
70079
  },
70008
70080
  {
@@ -70017,6 +70089,19 @@ var AI_FEATURES = [
70017
70089
  tryCommand: "vibe edit caption video.mp4 -o captioned.mp4"
70018
70090
  }
70019
70091
  ];
70092
+ function addUniqueKey(keys, keyDef) {
70093
+ if (!keys.some((k) => k.configKey === keyDef.configKey)) {
70094
+ keys.push(keyDef);
70095
+ }
70096
+ }
70097
+ function applyProviderDefault(config2, choice) {
70098
+ if (!choice.defaultFor) return;
70099
+ if (choice.defaultFor.kind === "image") {
70100
+ config2.defaults.imageProvider = choice.defaultFor.value;
70101
+ } else {
70102
+ config2.defaults.videoProvider = choice.defaultFor.value;
70103
+ }
70104
+ }
70020
70105
  async function runNonInteractiveSetup(opts) {
70021
70106
  const scope = opts.scope ?? "user";
70022
70107
  let config2 = await loadConfig({ scope });
@@ -70158,6 +70243,7 @@ async function runSetupWizard(fullSetup = false, scope = "user") {
70158
70243
  console.log(source_default.dim("2. Features"));
70159
70244
  console.log(source_default.bold("Which AI features do you need?"));
70160
70245
  console.log(source_default.dim(" \u2191\u2193 navigate \xB7 space to toggle \xB7 enter to confirm"));
70246
+ console.log(source_default.dim(" Tip: pressing enter on the highlighted item selects it if nothing is checked yet."));
70161
70247
  console.log();
70162
70248
  const featureLabels = AI_FEATURES.map((f) => {
70163
70249
  const keyCount = f.keys.length;
@@ -70166,7 +70252,9 @@ async function runSetupWizard(fullSetup = false, scope = "user") {
70166
70252
  });
70167
70253
  const picked = await promptMultiSelect(
70168
70254
  source_default.cyan(" Pick (e.g. 1,3 or 'all'): "),
70169
- featureLabels
70255
+ featureLabels,
70256
+ [],
70257
+ { enterSelectsFocusedWhenEmpty: true }
70170
70258
  );
70171
70259
  const selectedFeatures = picked.map((i) => AI_FEATURES[i]);
70172
70260
  console.log();
@@ -70177,53 +70265,107 @@ async function runSetupWizard(fullSetup = false, scope = "user") {
70177
70265
  await showComplete(config2, "vibe --help", [], scope);
70178
70266
  return;
70179
70267
  }
70180
- console.log(source_default.dim("3. API keys"));
70181
- console.log(source_default.bold("API Keys"));
70182
- console.log(source_default.dim(" Saved locally, never shared. Press Enter to skip."));
70268
+ const plannedKeys = /* @__PURE__ */ new Map();
70269
+ console.log(source_default.dim("3. Providers"));
70270
+ console.log(source_default.bold("Choose providers for each selected feature"));
70271
+ console.log(source_default.dim(" Recommended defaults are pre-selected. You can toggle more than one."));
70183
70272
  console.log();
70184
- const collectedKeys = /* @__PURE__ */ new Set();
70185
70273
  for (const feature of selectedFeatures) {
70186
- console.log(source_default.bold.cyan(` ${feature.label}`));
70187
- console.log(source_default.dim(` Default: ${feature.defaultProvider}`));
70188
- if (feature.alsoAvailable) {
70189
- console.log(source_default.dim(` Also available: ${feature.alsoAvailable}`));
70274
+ const keysForFeature = [];
70275
+ if (feature.providerChoices && feature.providerChoices.length > 0) {
70276
+ console.log(source_default.bold.cyan(` ${feature.label}`));
70277
+ console.log(source_default.dim(` Default path: ${feature.defaultProvider}`));
70278
+ if (feature.alsoAvailable) {
70279
+ console.log(source_default.dim(` Alternatives: ${feature.alsoAvailable}`));
70280
+ }
70281
+ console.log();
70282
+ const defaultSelected = feature.providerChoices.map((_, i) => i === 0);
70283
+ const providerLabels = feature.providerChoices.map((choice) => {
70284
+ const keyHint = choice.keyless ? source_default.dim("no key") : choice.key ? source_default.dim(choice.key.envVar) : source_default.dim("no key");
70285
+ return `${choice.label} ${source_default.dim(`- ${choice.desc}`)} ${keyHint}`;
70286
+ });
70287
+ const pickedProviders = await promptMultiSelect(
70288
+ source_default.cyan(" Providers (space toggles, enter confirms): "),
70289
+ providerLabels,
70290
+ defaultSelected
70291
+ );
70292
+ const selectedChoices = pickedProviders.map((i) => feature.providerChoices[i]);
70293
+ const defaultChoice = selectedChoices.find((choice) => choice.defaultFor);
70294
+ if (defaultChoice) {
70295
+ applyProviderDefault(config2, defaultChoice);
70296
+ }
70297
+ for (const choice of selectedChoices) {
70298
+ if (choice.key) addUniqueKey(keysForFeature, choice.key);
70299
+ }
70300
+ if (selectedChoices.some((choice) => choice.keyless)) {
70301
+ console.log(source_default.dim(" No API key needed for selected local provider(s)."));
70302
+ }
70303
+ console.log();
70304
+ } else {
70305
+ for (const keyDef of feature.keys) {
70306
+ addUniqueKey(keysForFeature, keyDef);
70307
+ }
70190
70308
  }
70309
+ plannedKeys.set(feature.label, keysForFeature);
70310
+ }
70311
+ const needsKeys = [...plannedKeys.values()].some((keys) => keys.length > 0);
70312
+ if (needsKeys) {
70313
+ console.log(source_default.dim("4. API keys"));
70314
+ console.log(source_default.bold("API Keys"));
70315
+ console.log(source_default.dim(" Saved locally, never shared. Press Enter to skip."));
70191
70316
  console.log();
70192
- for (const keyDef of feature.keys) {
70193
- if (collectedKeys.has(keyDef.configKey)) {
70194
- console.log(` ${source_default.green("\u2713")} ${keyDef.name.padEnd(14)} (already set above)`);
70317
+ const collectedKeys = /* @__PURE__ */ new Set();
70318
+ for (const feature of selectedFeatures) {
70319
+ const keysForFeature = plannedKeys.get(feature.label) ?? [];
70320
+ if (keysForFeature.length === 0) {
70195
70321
  continue;
70196
70322
  }
70197
- loadEnv();
70198
- const configValue = config2.providers[keyDef.configKey];
70199
- const envValue = process.env[keyDef.envVar];
70200
- if (configValue || envValue) {
70201
- const value = configValue || envValue;
70202
- const source = configValue ? "config" : ".env";
70203
- console.log(` ${source_default.green("\u2713")} ${keyDef.name.padEnd(14)} ${maskApiKey(value)} ${source_default.dim(`(${source})`)}`);
70204
- collectedKeys.add(keyDef.configKey);
70205
- continue;
70323
+ console.log(source_default.bold.cyan(` ${feature.label}`));
70324
+ console.log(source_default.dim(` Default: ${feature.defaultProvider}`));
70325
+ if (feature.alsoAvailable) {
70326
+ console.log(source_default.dim(` Also available: ${feature.alsoAvailable}`));
70206
70327
  }
70207
- console.log(source_default.dim(` ${keyDef.what}`));
70208
- console.log(source_default.dim(` Get key: ${keyDef.url} [o] open in browser`));
70209
- const newKey = await promptHidden(
70210
- source_default.cyan(` ${keyDef.name.padEnd(14)} ${source_default.dim(keyDef.envVar)}: `),
70211
- { openHotkeyUrl: keyDef.url }
70212
- );
70213
- if (newKey.trim()) {
70214
- const trimmed = newKey.trim();
70215
- config2.providers[keyDef.configKey] = trimmed;
70216
- const fmt = validateKeyFormat(keyDef.configKey, trimmed);
70217
- if (!fmt.ok && fmt.expected) {
70218
- console.log(` ${source_default.yellow("\u26A0")} Saved, but format looks unusual ${source_default.dim(`(expected ${fmt.expected})`)}`);
70328
+ console.log();
70329
+ for (const keyDef of keysForFeature) {
70330
+ if (collectedKeys.has(keyDef.configKey)) {
70331
+ console.log(` ${source_default.green("\u2713")} ${keyDef.name.padEnd(14)} (already set above)`);
70332
+ continue;
70333
+ }
70334
+ loadEnv();
70335
+ const configValue = config2.providers[keyDef.configKey];
70336
+ const envValue = process.env[keyDef.envVar];
70337
+ if (configValue || envValue) {
70338
+ const value = configValue || envValue;
70339
+ const source = configValue ? "config" : ".env";
70340
+ console.log(` ${source_default.green("\u2713")} ${keyDef.name.padEnd(14)} ${maskApiKey(value)} ${source_default.dim(`(${source})`)}`);
70341
+ collectedKeys.add(keyDef.configKey);
70342
+ continue;
70343
+ }
70344
+ console.log(source_default.dim(` ${keyDef.what}`));
70345
+ console.log(source_default.dim(` Get key: ${keyDef.url} [o] open in browser`));
70346
+ const newKey = await promptHidden(
70347
+ source_default.cyan(` ${keyDef.name.padEnd(14)} ${source_default.dim(keyDef.envVar)}: `),
70348
+ { openHotkeyUrl: keyDef.url }
70349
+ );
70350
+ if (newKey.trim()) {
70351
+ const trimmed = newKey.trim();
70352
+ config2.providers[keyDef.configKey] = trimmed;
70353
+ const fmt = validateKeyFormat(keyDef.configKey, trimmed);
70354
+ if (!fmt.ok && fmt.expected) {
70355
+ console.log(` ${source_default.yellow("\u26A0")} Saved, but format looks unusual ${source_default.dim(`(expected ${fmt.expected})`)}`);
70356
+ } else {
70357
+ console.log(` ${source_default.green("\u2713")} Saved`);
70358
+ }
70359
+ collectedKeys.add(keyDef.configKey);
70219
70360
  } else {
70220
- console.log(` ${source_default.green("\u2713")} Saved`);
70361
+ console.log(` ${source_default.yellow("\u26A0")} Skipped ${source_default.dim(`(set ${keyDef.envVar} in .env later)`)}`);
70221
70362
  }
70222
- collectedKeys.add(keyDef.configKey);
70223
- } else {
70224
- console.log(` ${source_default.yellow("\u26A0")} Skipped ${source_default.dim(`(set ${keyDef.envVar} in .env later)`)}`);
70225
70363
  }
70364
+ console.log();
70226
70365
  }
70366
+ } else {
70367
+ console.log(source_default.dim("4. API keys"));
70368
+ console.log(source_default.dim(" No API keys needed for the selected provider(s)."));
70227
70369
  console.log();
70228
70370
  }
70229
70371
  await saveConfig(config2, { scope });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/cli",
3
- "version": "0.91.0",
3
+ "version": "0.91.1",
4
4
  "description": "VibeFrame CLI - natural language video editing from the terminal",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -183,8 +183,8 @@
183
183
  "tsx": "^4.21.0",
184
184
  "typescript": "^5.3.3",
185
185
  "vitest": "^1.2.2",
186
- "@vibeframe/ai-providers": "0.91.0",
187
- "@vibeframe/core": "0.91.0"
186
+ "@vibeframe/ai-providers": "0.91.1",
187
+ "@vibeframe/core": "0.91.1"
188
188
  },
189
189
  "engines": {
190
190
  "node": ">=20"