@vibeframe/cli 0.89.0 → 0.90.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +177 -70
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -25690,32 +25690,81 @@ var init_schema = __esm({
25690
25690
  import { resolve as resolve4 } from "node:path";
25691
25691
  import { homedir } from "node:os";
25692
25692
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, access as access2 } from "node:fs/promises";
25693
- async function loadConfig() {
25693
+ function getProjectConfigDir(cwd = process.cwd()) {
25694
+ return resolve4(cwd, ".vibeframe");
25695
+ }
25696
+ function getProjectConfigPath(cwd = process.cwd()) {
25697
+ return resolve4(getProjectConfigDir(cwd), "config.yaml");
25698
+ }
25699
+ function getConfigPath(scope, cwd) {
25700
+ return scope === "project" ? getProjectConfigPath(cwd) : USER_CONFIG_PATH;
25701
+ }
25702
+ function getConfigDir(scope, cwd) {
25703
+ return scope === "project" ? getProjectConfigDir(cwd) : USER_CONFIG_DIR;
25704
+ }
25705
+ async function fileExists(path2) {
25694
25706
  try {
25695
- await access2(CONFIG_PATH);
25696
- const content = await readFile2(CONFIG_PATH, "utf-8");
25697
- const config2 = (0, import_yaml.parse)(content);
25698
- const defaults = createDefaultConfig();
25699
- return {
25700
- ...defaults,
25701
- ...config2,
25702
- llm: { ...defaults.llm, ...config2.llm },
25703
- providers: { ...defaults.providers, ...config2.providers },
25704
- defaults: { ...defaults.defaults, ...config2.defaults },
25705
- repl: { ...defaults.repl, ...config2.repl }
25706
- };
25707
+ await access2(path2);
25708
+ return true;
25709
+ } catch {
25710
+ return false;
25711
+ }
25712
+ }
25713
+ async function getActiveScope(cwd) {
25714
+ return await fileExists(getProjectConfigPath(cwd)) ? "project" : "user";
25715
+ }
25716
+ function applyDefaults(parsed) {
25717
+ const defaults = createDefaultConfig();
25718
+ return {
25719
+ ...defaults,
25720
+ ...parsed,
25721
+ llm: { ...defaults.llm, ...parsed.llm },
25722
+ providers: { ...defaults.providers, ...parsed.providers },
25723
+ defaults: { ...defaults.defaults, ...parsed.defaults },
25724
+ repl: { ...defaults.repl, ...parsed.repl }
25725
+ };
25726
+ }
25727
+ async function readConfigFile(path2) {
25728
+ if (!await fileExists(path2)) return null;
25729
+ try {
25730
+ const content = await readFile2(path2, "utf-8");
25731
+ const parsed = (0, import_yaml.parse)(content);
25732
+ return applyDefaults(parsed);
25707
25733
  } catch {
25708
25734
  return null;
25709
25735
  }
25710
25736
  }
25711
- async function saveConfig(config2) {
25712
- await mkdir2(CONFIG_DIR, { recursive: true });
25713
- const content = (0, import_yaml.stringify)(config2, {
25714
- indent: 2,
25715
- lineWidth: 0
25716
- // Don't wrap lines
25717
- });
25718
- await writeFile2(CONFIG_PATH, content, "utf-8");
25737
+ async function loadConfig(options = {}) {
25738
+ const { scope, cwd, merge } = options;
25739
+ if (merge) {
25740
+ const user = await readConfigFile(USER_CONFIG_PATH);
25741
+ const project2 = await readConfigFile(getProjectConfigPath(cwd));
25742
+ if (!user && !project2) return null;
25743
+ if (!user) return project2;
25744
+ if (!project2) return user;
25745
+ return {
25746
+ ...user,
25747
+ ...project2,
25748
+ llm: { ...user.llm, ...project2.llm },
25749
+ providers: { ...user.providers, ...project2.providers },
25750
+ defaults: { ...user.defaults, ...project2.defaults },
25751
+ repl: { ...user.repl, ...project2.repl }
25752
+ };
25753
+ }
25754
+ if (scope) {
25755
+ return readConfigFile(getConfigPath(scope, cwd));
25756
+ }
25757
+ const project = await readConfigFile(getProjectConfigPath(cwd));
25758
+ if (project) return project;
25759
+ return readConfigFile(USER_CONFIG_PATH);
25760
+ }
25761
+ async function saveConfig(config2, options = {}) {
25762
+ const scope = options.scope ?? "user";
25763
+ const dir = getConfigDir(scope, options.cwd);
25764
+ const path2 = getConfigPath(scope, options.cwd);
25765
+ await mkdir2(dir, { recursive: true });
25766
+ const content = (0, import_yaml.stringify)(config2, { indent: 2, lineWidth: 0 });
25767
+ await writeFile2(path2, content, "utf-8");
25719
25768
  }
25720
25769
  async function isConfigured() {
25721
25770
  const config2 = await loadConfig();
@@ -25737,20 +25786,19 @@ async function getApiKeyFromConfig(providerKey) {
25737
25786
  return config2.providers[providerKey];
25738
25787
  }
25739
25788
  const envVar = PROVIDER_ENV_VARS[providerKey];
25740
- if (envVar) {
25741
- return process.env[envVar];
25742
- }
25789
+ if (envVar) return process.env[envVar];
25743
25790
  return void 0;
25744
25791
  }
25745
- var import_yaml, CONFIG_DIR, CONFIG_PATH;
25792
+ var import_yaml, USER_CONFIG_DIR, USER_CONFIG_PATH, CONFIG_PATH;
25746
25793
  var init_config = __esm({
25747
25794
  "src/config/index.ts"() {
25748
25795
  "use strict";
25749
25796
  import_yaml = __toESM(require_dist(), 1);
25750
25797
  init_schema();
25751
25798
  init_schema();
25752
- CONFIG_DIR = resolve4(homedir(), ".vibeframe");
25753
- CONFIG_PATH = resolve4(CONFIG_DIR, "config.yaml");
25799
+ USER_CONFIG_DIR = resolve4(homedir(), ".vibeframe");
25800
+ USER_CONFIG_PATH = resolve4(USER_CONFIG_DIR, "config.yaml");
25801
+ CONFIG_PATH = USER_CONFIG_PATH;
25754
25802
  }
25755
25803
  });
25756
25804
 
@@ -26277,9 +26325,8 @@ vibe scene lint --json # structured output for agent loops
26277
26325
  `;
26278
26326
  }
26279
26327
  function buildSceneGitignore() {
26280
- return `# VibeFrame caches
26281
- .vibeframe/cache/
26282
- .vibeframe/checkpoints/
26328
+ return `# VibeFrame \u2014 caches, checkpoints, and project-scope config.yaml (may contain API keys)
26329
+ .vibeframe/
26283
26330
 
26284
26331
  # Render outputs
26285
26332
  renders/*.mp4
@@ -62845,7 +62892,7 @@ init_source();
62845
62892
  // package.json
62846
62893
  var package_default = {
62847
62894
  name: "@vibeframe/cli",
62848
- version: "0.89.0",
62895
+ version: "0.90.0",
62849
62896
  description: "VibeFrame CLI - natural language video editing from the terminal",
62850
62897
  private: false,
62851
62898
  license: "MIT",
@@ -69840,13 +69887,34 @@ var VALID_LLM_PROVIDERS = [
69840
69887
  "xai",
69841
69888
  "openrouter"
69842
69889
  ];
69843
- var setupCommand = new Command("setup").description("Configure VibeFrame (LLM provider, API keys)").option("--reset", "Reset configuration to defaults").option("--full", "Run full setup with all optional providers").option("--show", "Show current configuration (for debugging)").option("-v, --verbose", "With --show: include unset providers + Resolution order + Defaults block").option("--claude-code", "Show Claude Code integration guide").option("-y, --yes", "Non-interactive: write config without prompting (CI / devcontainer)").option("--provider <id>", "Set the Agent LLM provider (claude | openai | gemini | xai | openrouter | ollama)").option("--import-env", "Promote API keys from .env / shell env into config.yaml").option("--test", "After save, live-test each configured key (exits 7 if any FAIL)").addHelpText(
69890
+ var VALID_SCOPES = ["user", "project"];
69891
+ function parseScope(raw) {
69892
+ const v = String(raw ?? "user");
69893
+ if (!VALID_SCOPES.includes(v)) {
69894
+ exitWithError(generalError(
69895
+ `Invalid --scope: ${v}`,
69896
+ `Must be one of: ${VALID_SCOPES.join(", ")}`
69897
+ ));
69898
+ }
69899
+ return v;
69900
+ }
69901
+ var setupCommand = new Command("setup").description("Configure VibeFrame (LLM provider, API keys)").option("--reset", "Reset configuration to defaults").option("--full", "Run full setup with all optional providers").option("--show", "Show current configuration (for debugging)").option("-v, --verbose", "With --show: include unset providers + Resolution order + Defaults block").option("--claude-code", "Show Claude Code integration guide").option("-y, --yes", "Non-interactive: write config without prompting (CI / devcontainer)").option("--provider <id>", "Set the Agent LLM provider (claude | openai | gemini | xai | openrouter | ollama)").option("--import-env", "Promote API keys from .env / shell env into config.yaml").option("--test", "After save, live-test each configured key (exits 7 if any FAIL)").option(
69902
+ "--scope <scope>",
69903
+ "Where to save: 'user' (~/.vibeframe/config.yaml, shared) or 'project' (./.vibeframe/config.yaml, gitignored, this project only)",
69904
+ "user"
69905
+ ).addHelpText(
69844
69906
  "after",
69845
69907
  `
69846
69908
  Non-interactive examples (no TTY required):
69847
69909
  vibe setup --yes --provider claude --import-env Bootstrap CI / devcontainer
69848
69910
  vibe setup --yes --import-env --test Persist .env keys + verify each one
69849
69911
  vibe setup --yes --provider openai Just switch the agent provider
69912
+ vibe setup --scope project --yes --import-env Project-only setup (./.vibeframe/config.yaml)
69913
+
69914
+ Scopes:
69915
+ user (default) ~/.vibeframe/config.yaml \u2014 shared across every project
69916
+ project ./.vibeframe/config.yaml \u2014 gitignored, isolates keys/defaults to this directory
69917
+ When a project config exists at the cwd, it is used and the user file is ignored.
69850
69918
 
69851
69919
  Exit codes (non-interactive):
69852
69920
  0 success 7 --test verification failed (one or more keys returned non-2xx)
@@ -69861,11 +69929,12 @@ Exit codes (non-interactive):
69861
69929
  await showConfig({ verbose: Boolean(options.verbose) });
69862
69930
  return;
69863
69931
  }
69932
+ const scope = parseScope(options.scope);
69864
69933
  if (options.reset) {
69865
69934
  const config2 = createDefaultConfig();
69866
- await saveConfig(config2);
69935
+ await saveConfig(config2, { scope });
69867
69936
  console.log(source_default.green("\u2713 Configuration reset to defaults"));
69868
- console.log(source_default.dim(` Saved to: ${CONFIG_PATH}`));
69937
+ console.log(source_default.dim(` Saved to: ${getConfigPath(scope)}`));
69869
69938
  return;
69870
69939
  }
69871
69940
  const wantsNonInteractive = Boolean(options.yes) || !hasTTY() && (options.provider || options.importEnv);
@@ -69873,7 +69942,8 @@ Exit codes (non-interactive):
69873
69942
  await runNonInteractiveSetup({
69874
69943
  provider: options.provider,
69875
69944
  importEnv: Boolean(options.importEnv),
69876
- test: Boolean(options.test)
69945
+ test: Boolean(options.test),
69946
+ scope
69877
69947
  });
69878
69948
  return;
69879
69949
  }
@@ -69884,7 +69954,7 @@ Exit codes (non-interactive):
69884
69954
  ));
69885
69955
  }
69886
69956
  try {
69887
- await runSetupWizard(options.full);
69957
+ await runSetupWizard(options.full, scope);
69888
69958
  closeTTYStream();
69889
69959
  process.exit(0);
69890
69960
  } catch (err) {
@@ -69930,7 +70000,8 @@ var AI_FEATURES = [
69930
70000
  }
69931
70001
  ];
69932
70002
  async function runNonInteractiveSetup(opts) {
69933
- let config2 = await loadConfig();
70003
+ const scope = opts.scope ?? "user";
70004
+ let config2 = await loadConfig({ scope });
69934
70005
  if (!config2) {
69935
70006
  config2 = createDefaultConfig();
69936
70007
  }
@@ -69967,9 +70038,9 @@ async function runNonInteractiveSetup(opts) {
69967
70038
  changes.push(`imported ${importedCount} key${importedCount === 1 ? "" : "s"} from env`);
69968
70039
  }
69969
70040
  }
69970
- await saveConfig(config2);
69971
- console.log(source_default.green("\u2713 Setup complete (non-interactive)"));
69972
- console.log(source_default.dim(` Config: ${CONFIG_PATH}`));
70041
+ await saveConfig(config2, { scope });
70042
+ console.log(source_default.green(`\u2713 Setup complete (non-interactive, ${scope} scope)`));
70043
+ console.log(source_default.dim(` Config: ${getConfigPath(scope)}`));
69973
70044
  if (changes.length > 0) {
69974
70045
  for (const c of changes) {
69975
70046
  console.log(source_default.dim(` ${c}`));
@@ -70012,20 +70083,21 @@ async function runNonInteractiveSetup(opts) {
70012
70083
  }
70013
70084
  }
70014
70085
  }
70015
- async function runSetupWizard(fullSetup = false) {
70086
+ async function runSetupWizard(fullSetup = false, scope = "user") {
70016
70087
  console.log();
70017
- console.log(source_default.bold.magenta("VibeFrame Setup") + source_default.dim(" \u2014 user scope"));
70088
+ const scopeLabel = scope === "project" ? source_default.dim(` \u2014 project scope (${getConfigPath("project")})`) : source_default.dim(" \u2014 user scope");
70089
+ console.log(source_default.bold.magenta("VibeFrame Setup") + scopeLabel);
70018
70090
  console.log(source_default.dim("\u2500".repeat(40)));
70019
70091
  console.log();
70020
70092
  const hosts = detectedAgentHosts();
70021
70093
  console.log(source_default.dim(`Agent hosts: ${summariseAgentHosts(hosts)}`));
70022
70094
  console.log();
70023
- let config2 = await loadConfig();
70095
+ let config2 = await loadConfig({ scope });
70024
70096
  if (!config2) {
70025
70097
  config2 = createDefaultConfig();
70026
70098
  }
70027
70099
  if (fullSetup) {
70028
- await runCustomSetup(config2);
70100
+ await runCustomSetup(config2, scope);
70029
70101
  return;
70030
70102
  }
70031
70103
  console.log(source_default.dim("1. Goal"));
@@ -70040,12 +70112,12 @@ async function runSetupWizard(fullSetup = false) {
70040
70112
  const topIndex = await promptSelect(source_default.cyan(" Select [1-4]: "), topLabels, 0);
70041
70113
  console.log();
70042
70114
  if (topIndex === 0) {
70043
- await saveConfig(config2);
70044
- await showComplete(config2, "vibe edit silence-cut video.mp4 -o clean.mp4");
70115
+ await saveConfig(config2, { scope });
70116
+ await showComplete(config2, "vibe edit silence-cut video.mp4 -o clean.mp4", [], scope);
70045
70117
  return;
70046
70118
  }
70047
70119
  if (topIndex === 3) {
70048
- await runCustomSetup(config2);
70120
+ await runCustomSetup(config2, scope);
70049
70121
  return;
70050
70122
  }
70051
70123
  if (topIndex === 2) {
@@ -70061,8 +70133,8 @@ async function runSetupWizard(fullSetup = false) {
70061
70133
  console.log(source_default.dim(" Saved locally, never shared. Press Enter to skip."));
70062
70134
  console.log();
70063
70135
  await collectKeys(config2, pipelineKeys);
70064
- await saveConfig(config2);
70065
- await showComplete(config2, "vibe build my-story/ # see CONTRIBUTING.md for STORYBOARD.md format");
70136
+ await saveConfig(config2, { scope });
70137
+ await showComplete(config2, "vibe build my-story/ # see CONTRIBUTING.md for STORYBOARD.md format", [], scope);
70066
70138
  return;
70067
70139
  }
70068
70140
  console.log(source_default.dim("2. Features"));
@@ -70083,8 +70155,8 @@ async function runSetupWizard(fullSetup = false) {
70083
70155
  if (selectedFeatures.length === 0) {
70084
70156
  console.log(source_default.dim(" No features selected. You can re-run setup anytime."));
70085
70157
  console.log();
70086
- await saveConfig(config2);
70087
- await showComplete(config2, "vibe --help", []);
70158
+ await saveConfig(config2, { scope });
70159
+ await showComplete(config2, "vibe --help", [], scope);
70088
70160
  return;
70089
70161
  }
70090
70162
  console.log(source_default.dim("3. API keys"));
@@ -70136,8 +70208,8 @@ async function runSetupWizard(fullSetup = false) {
70136
70208
  }
70137
70209
  console.log();
70138
70210
  }
70139
- await saveConfig(config2);
70140
- await showComplete(config2, selectedFeatures[0].tryCommand, selectedFeatures);
70211
+ await saveConfig(config2, { scope });
70212
+ await showComplete(config2, selectedFeatures[0].tryCommand, selectedFeatures, scope);
70141
70213
  }
70142
70214
  async function collectKeys(config2, keys) {
70143
70215
  loadEnv();
@@ -70173,7 +70245,7 @@ async function collectKeys(config2, keys) {
70173
70245
  }
70174
70246
  console.log();
70175
70247
  }
70176
- async function runCustomSetup(config2) {
70248
+ async function runCustomSetup(config2, scope = "user") {
70177
70249
  console.log(source_default.bold("1. Agent LLM Provider") + source_default.dim(" (for vibe agent)"));
70178
70250
  console.log(source_default.dim(" Only needed if you use the interactive Agent mode."));
70179
70251
  console.log();
@@ -70243,14 +70315,15 @@ async function runCustomSetup(config2) {
70243
70315
  }
70244
70316
  }
70245
70317
  console.log();
70246
- await saveConfig(config2);
70247
- await showComplete(config2, "vibe doctor");
70318
+ await saveConfig(config2, { scope });
70319
+ await showComplete(config2, "vibe doctor", [], scope);
70248
70320
  }
70249
- async function showComplete(config2, defaultTryCommand, features = []) {
70321
+ async function showComplete(config2, defaultTryCommand, features = [], scope = "user") {
70250
70322
  console.log(source_default.dim("\u2500".repeat(40)));
70251
70323
  console.log(source_default.green.bold("\u2713 Setup complete!"));
70252
70324
  console.log();
70253
- console.log(source_default.dim(` Config: ${CONFIG_PATH}`));
70325
+ const scopeNote = scope === "project" ? source_default.dim(" (project scope)") : source_default.dim(" (user scope)");
70326
+ console.log(source_default.dim(` Config: ${getConfigPath(scope)}${scopeNote}`));
70254
70327
  console.log();
70255
70328
  const primaryTry = features.length > 1 ? features[0].tryCommand : defaultTryCommand;
70256
70329
  if (features.length > 1) {
@@ -70340,13 +70413,22 @@ async function showConfig(opts = { verbose: false }) {
70340
70413
  hasCwdEnv = true;
70341
70414
  } catch {
70342
70415
  }
70416
+ const activeScope = await getActiveScope();
70417
+ const userPath = USER_CONFIG_PATH;
70418
+ const projectPath = getConfigPath("project");
70419
+ const userExists = await loadConfig({ scope: "user" }) !== null;
70420
+ const projectExists = await loadConfig({ scope: "project" }) !== null;
70343
70421
  console.log();
70344
70422
  console.log(source_default.bold.magenta("VibeFrame Configuration"));
70345
70423
  console.log(source_default.dim("\u2500".repeat(40)));
70346
70424
  console.log();
70347
- console.log(source_default.dim(`Config file: ${CONFIG_PATH}`));
70425
+ const userMark = activeScope === "user" && userExists ? source_default.cyan("\u2190 active") : userExists ? source_default.dim("(present)") : source_default.dim("(missing)");
70426
+ const projectMark = activeScope === "project" ? source_default.cyan("\u2190 active") : projectExists ? source_default.dim("(present)") : source_default.dim("(missing)");
70427
+ console.log(source_default.dim(`Scopes:`));
70428
+ console.log(source_default.dim(` user ${userPath} ${userMark}`));
70429
+ console.log(source_default.dim(` project ${projectPath} ${projectMark}`));
70348
70430
  if (hasCwdEnv) {
70349
- console.log(source_default.dim(`Project .env: ${cwdEnvPath}`));
70431
+ console.log(source_default.dim(` .env ${cwdEnvPath} ${source_default.dim("(env vars override config)")}`));
70350
70432
  }
70351
70433
  console.log();
70352
70434
  const config2 = await loadConfig();
@@ -70414,9 +70496,9 @@ async function showConfig(opts = { verbose: false }) {
70414
70496
  }
70415
70497
  console.log(source_default.bold("Resolution order:"));
70416
70498
  console.log(source_default.dim(" 1. --api-key CLI option"));
70417
- console.log(source_default.dim(` 2. ${CONFIG_PATH}`));
70418
- console.log(source_default.dim(" 3. .env in current directory"));
70419
- console.log(source_default.dim(" 4. Shell environment variables"));
70499
+ console.log(source_default.dim(` 2. Active config scope: ${activeScope === "project" ? projectPath : userPath}`));
70500
+ console.log(source_default.dim(` (project takes priority when present at cwd; user is ignored in that case)`));
70501
+ console.log(source_default.dim(" 3. .env / shell environment variables (process.env)"));
70420
70502
  console.log();
70421
70503
  }
70422
70504
  }
@@ -71551,6 +71633,9 @@ async function runDiagnostics() {
71551
71633
  exists: existsSync36(resolve50(cwd, rel))
71552
71634
  }));
71553
71635
  const projectInitialized = projectFiles.some((f) => f.exists);
71636
+ const projectConfigPath = getProjectConfigPath(cwd);
71637
+ const projectConfigExists = existsSync36(projectConfigPath);
71638
+ const activeScope = await getActiveScope(cwd);
71554
71639
  const hosts = detectAgentHosts();
71555
71640
  const detectedNames = hosts.filter((h) => h.detected).map((h) => h.label);
71556
71641
  const recommendedMode = resolveSceneBuildMode({});
@@ -71573,8 +71658,15 @@ async function runDiagnostics() {
71573
71658
  ...Object.keys(optionalTools).length > 0 ? { optionalTools } : {}
71574
71659
  },
71575
71660
  scope: {
71661
+ activeScope,
71576
71662
  user: { configured: configExists, configPath: CONFIG_PATH },
71577
- project: { cwd, initialized: projectInitialized, files: projectFiles },
71663
+ project: {
71664
+ cwd,
71665
+ initialized: projectInitialized,
71666
+ files: projectFiles,
71667
+ configPath: projectConfigPath,
71668
+ configFileExists: projectConfigExists
71669
+ },
71578
71670
  agentHosts: { detected: detectedNames, summary: summariseAgentHosts(hosts) },
71579
71671
  sceneComposer: {
71580
71672
  recommendedMode,
@@ -71647,16 +71739,30 @@ function printReport(results, opts = { verbose: false }) {
71647
71739
  }
71648
71740
  console.log();
71649
71741
  console.log(source_default.bold(" Scope"));
71742
+ const userActive = results.scope.activeScope === "user" && results.scope.user.configured;
71743
+ const projectActive = results.scope.activeScope === "project";
71744
+ const activeMark = source_default.cyan(" \u2190 active");
71650
71745
  if (results.scope.user.configured) {
71651
- console.log(` User ${source_default.green("OK")} ${source_default.dim(results.scope.user.configPath)}`);
71746
+ console.log(
71747
+ ` Cfg(user) ${source_default.green("OK")} ${source_default.dim(results.scope.user.configPath)}${userActive ? activeMark : ""}`
71748
+ );
71652
71749
  } else {
71653
- console.log(` User ${source_default.yellow("NOT SET")} ${source_default.dim("Run: vibe setup")}`);
71750
+ console.log(` Cfg(user) ${source_default.yellow("NOT SET")} ${source_default.dim("Run: vibe setup")}`);
71751
+ }
71752
+ if (results.scope.project.configFileExists) {
71753
+ console.log(
71754
+ ` Cfg(proj) ${source_default.green("OK")} ${source_default.dim(results.scope.project.configPath)}${projectActive ? activeMark : ""}`
71755
+ );
71756
+ } else {
71757
+ console.log(
71758
+ ` Cfg(proj) ${source_default.dim("none")} ${source_default.dim(`Run: vibe setup --scope project (cwd: ${results.scope.project.cwd})`)}`
71759
+ );
71654
71760
  }
71655
71761
  if (results.scope.project.initialized) {
71656
71762
  const present = results.scope.project.files.filter((f) => f.exists).map((f) => f.path);
71657
- console.log(` Project ${source_default.green("OK")} ${source_default.dim(present.join(", "))}`);
71763
+ console.log(` Init ${source_default.green("OK")} ${source_default.dim(present.join(", "))}`);
71658
71764
  } else {
71659
- console.log(` Project ${source_default.yellow("NOT INIT")} ${source_default.dim(`Run: vibe init (cwd: ${results.scope.project.cwd})`)}`);
71765
+ console.log(` Init ${source_default.yellow("NOT INIT")} ${source_default.dim(`Run: vibe init (cwd: ${results.scope.project.cwd})`)}`);
71660
71766
  }
71661
71767
  console.log(` Agents ${source_default.dim(results.scope.agentHosts.summary)}`);
71662
71768
  if (verbose) {
@@ -71812,8 +71918,9 @@ async function runLiveKeyTests(results) {
71812
71918
  console.log();
71813
71919
  }
71814
71920
  function pickNextStep(results, hasMissingProviders) {
71815
- if (!results.scope.user.configured) {
71816
- return "Next: run 'vibe setup' to configure your user scope (API keys + LLM provider).";
71921
+ const anyConfigured = results.scope.user.configured || results.scope.project.configFileExists;
71922
+ if (!anyConfigured) {
71923
+ return "Next: run 'vibe setup' (or 'vibe setup --scope project' to keep config inside the project) to configure API keys + LLM provider.";
71817
71924
  }
71818
71925
  if (!results.scope.project.initialized) {
71819
71926
  return "Next: run 'vibe init' in your project directory to scaffold AGENTS.md / CLAUDE.md / .env.example.";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/cli",
3
- "version": "0.89.0",
3
+ "version": "0.90.0",
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.89.0",
187
- "@vibeframe/core": "0.89.0"
186
+ "@vibeframe/ai-providers": "0.90.0",
187
+ "@vibeframe/core": "0.90.0"
188
188
  },
189
189
  "engines": {
190
190
  "node": ">=20"