@vibeframe/cli 0.103.1 → 0.104.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 +334 -127
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -25726,11 +25726,15 @@ var config_exports = {};
25726
25726
  __export(config_exports, {
25727
25727
  CONFIG_DIR: () => CONFIG_DIR,
25728
25728
  CONFIG_PATH: () => CONFIG_PATH,
25729
+ LEGACY_USER_CONFIG_DIR: () => LEGACY_USER_CONFIG_DIR,
25730
+ LEGACY_USER_CONFIG_PATH: () => LEGACY_USER_CONFIG_PATH,
25729
25731
  PROVIDER_ENV_ALIASES: () => PROVIDER_ENV_ALIASES,
25730
25732
  PROVIDER_ENV_VARS: () => PROVIDER_ENV_VARS,
25731
25733
  PROVIDER_NAMES: () => PROVIDER_NAMES,
25734
+ USER_CACHE_DIR: () => USER_CACHE_DIR,
25732
25735
  USER_CONFIG_DIR: () => USER_CONFIG_DIR,
25733
25736
  USER_CONFIG_PATH: () => USER_CONFIG_PATH,
25737
+ USER_DATA_DIR: () => USER_DATA_DIR,
25734
25738
  createDefaultConfig: () => createDefaultConfig,
25735
25739
  findProjectConfigPath: () => findProjectConfigPath,
25736
25740
  getActiveScope: () => getActiveScope,
@@ -25739,6 +25743,7 @@ __export(config_exports, {
25739
25743
  getConfigPath: () => getConfigPath,
25740
25744
  getProjectConfigDir: () => getProjectConfigDir,
25741
25745
  getProjectConfigPath: () => getProjectConfigPath,
25746
+ getUserConfigStatus: () => getUserConfigStatus,
25742
25747
  isConfigured: () => isConfigured,
25743
25748
  loadConfig: () => loadConfig,
25744
25749
  saveConfig: () => saveConfig,
@@ -25747,6 +25752,17 @@ __export(config_exports, {
25747
25752
  import { resolve as resolve4 } from "node:path";
25748
25753
  import { homedir } from "node:os";
25749
25754
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, access as access2 } from "node:fs/promises";
25755
+ function resolveEnvPath(name) {
25756
+ const value = process.env[name]?.trim();
25757
+ return value ? resolve4(value) : null;
25758
+ }
25759
+ function resolveXdgAppDir(opts) {
25760
+ const override = resolveEnvPath(opts.overrideEnv);
25761
+ if (override) return override;
25762
+ const xdgBase = resolveEnvPath(opts.xdgEnv);
25763
+ const base = xdgBase ?? resolve4(homedir(), opts.fallbackHomeRel);
25764
+ return resolve4(base, "vibeframe");
25765
+ }
25750
25766
  function getProjectConfigDir(cwd = process.cwd()) {
25751
25767
  return resolve4(cwd, ".vibeframe");
25752
25768
  }
@@ -25767,11 +25783,29 @@ async function fileExists(path2) {
25767
25783
  return false;
25768
25784
  }
25769
25785
  }
25786
+ async function getUserConfigStatus() {
25787
+ const primaryConfigured = await fileExists(USER_CONFIG_PATH);
25788
+ const legacyConfigured = LEGACY_USER_CONFIG_PATH === USER_CONFIG_PATH ? primaryConfigured : await fileExists(LEGACY_USER_CONFIG_PATH);
25789
+ const source = primaryConfigured ? "user" : legacyConfigured && LEGACY_USER_CONFIG_PATH !== USER_CONFIG_PATH ? "legacy" : null;
25790
+ return {
25791
+ configured: primaryConfigured || legacyConfigured,
25792
+ configPath: source === "user" ? USER_CONFIG_PATH : source === "legacy" ? LEGACY_USER_CONFIG_PATH : USER_CONFIG_PATH,
25793
+ writePath: USER_CONFIG_PATH,
25794
+ legacyConfigPath: LEGACY_USER_CONFIG_PATH,
25795
+ primaryConfigured,
25796
+ legacyConfigured,
25797
+ source
25798
+ };
25799
+ }
25770
25800
  async function findProjectConfigPath(cwd = process.cwd()) {
25771
25801
  let dir = resolve4(cwd);
25802
+ const homeDir = resolve4(homedir());
25772
25803
  for (; ; ) {
25804
+ if (dir === homeDir) return null;
25773
25805
  const candidate = getProjectConfigPath(dir);
25774
- if (await fileExists(candidate)) return candidate;
25806
+ if (candidate !== USER_CONFIG_PATH && candidate !== LEGACY_USER_CONFIG_PATH && await fileExists(candidate)) {
25807
+ return candidate;
25808
+ }
25775
25809
  const parent = resolve4(dir, "..");
25776
25810
  if (parent === dir) return null;
25777
25811
  dir = parent;
@@ -25806,10 +25840,15 @@ async function readConfigFile(path2) {
25806
25840
  return null;
25807
25841
  }
25808
25842
  }
25843
+ async function readUserConfigFile() {
25844
+ if (await fileExists(USER_CONFIG_PATH)) return readConfigFile(USER_CONFIG_PATH);
25845
+ if (await fileExists(LEGACY_USER_CONFIG_PATH)) return readConfigFile(LEGACY_USER_CONFIG_PATH);
25846
+ return null;
25847
+ }
25809
25848
  async function loadConfig(options = {}) {
25810
25849
  const { scope, cwd, merge } = options;
25811
25850
  if (merge) {
25812
- const user = await readConfigFile(USER_CONFIG_PATH);
25851
+ const user = await readUserConfigFile();
25813
25852
  const projectPath2 = await findProjectConfigPath(cwd);
25814
25853
  const project2 = projectPath2 ? await readConfigFile(projectPath2) : null;
25815
25854
  if (!user && !project2) return null;
@@ -25830,12 +25869,12 @@ async function loadConfig(options = {}) {
25830
25869
  };
25831
25870
  }
25832
25871
  if (scope) {
25833
- return readConfigFile(getConfigPath(scope, cwd));
25872
+ return scope === "user" ? readUserConfigFile() : readConfigFile(getConfigPath(scope, cwd));
25834
25873
  }
25835
25874
  const projectPath = await findProjectConfigPath(cwd);
25836
25875
  const project = projectPath ? await readConfigFile(projectPath) : null;
25837
25876
  if (project) return project;
25838
- return readConfigFile(USER_CONFIG_PATH);
25877
+ return readUserConfigFile();
25839
25878
  }
25840
25879
  async function saveConfig(config2, options = {}) {
25841
25880
  const scope = options.scope ?? "user";
@@ -25845,8 +25884,8 @@ async function saveConfig(config2, options = {}) {
25845
25884
  const content = (0, import_yaml.stringify)(config2, { indent: 2, lineWidth: 0 });
25846
25885
  await writeFile2(path2, content, "utf-8");
25847
25886
  }
25848
- async function isConfigured() {
25849
- const config2 = await loadConfig();
25887
+ async function isConfigured(options = {}) {
25888
+ const config2 = await loadConfig({ cwd: options.cwd });
25850
25889
  if (!config2) return false;
25851
25890
  const provider = config2.llm.provider;
25852
25891
  const providerKey = provider === "gemini" ? "google" : provider === "claude" ? "anthropic" : provider;
@@ -25878,15 +25917,23 @@ async function updateProviderKey(providerKey, apiKey, options = {}) {
25878
25917
  config2.providers[providerKey] = apiKey;
25879
25918
  await saveConfig(config2, { scope, cwd: options.cwd });
25880
25919
  }
25881
- var import_yaml, USER_CONFIG_DIR, USER_CONFIG_PATH, CONFIG_DIR, CONFIG_PATH;
25920
+ var import_yaml, USER_CONFIG_DIR, USER_CONFIG_PATH, USER_DATA_DIR, USER_CACHE_DIR, LEGACY_USER_CONFIG_DIR, LEGACY_USER_CONFIG_PATH, CONFIG_DIR, CONFIG_PATH;
25882
25921
  var init_config = __esm({
25883
25922
  "src/config/index.ts"() {
25884
25923
  "use strict";
25885
25924
  import_yaml = __toESM(require_dist2(), 1);
25886
25925
  init_schema();
25887
25926
  init_schema();
25888
- USER_CONFIG_DIR = resolve4(homedir(), ".vibeframe");
25927
+ USER_CONFIG_DIR = resolveEnvPath("VIBEFRAME_CONFIG_HOME") ?? resolve4(homedir(), ".vibeframe");
25889
25928
  USER_CONFIG_PATH = resolve4(USER_CONFIG_DIR, "config.yaml");
25929
+ USER_DATA_DIR = resolveXdgAppDir({
25930
+ overrideEnv: "VIBEFRAME_DATA_HOME",
25931
+ xdgEnv: "XDG_DATA_HOME",
25932
+ fallbackHomeRel: ".local/share"
25933
+ });
25934
+ USER_CACHE_DIR = resolveEnvPath("VIBEFRAME_CACHE_HOME") ?? resolve4(USER_CONFIG_DIR, "cache");
25935
+ LEGACY_USER_CONFIG_DIR = resolve4(homedir(), ".vibeframe");
25936
+ LEGACY_USER_CONFIG_PATH = resolve4(LEGACY_USER_CONFIG_DIR, "config.yaml");
25890
25937
  CONFIG_DIR = USER_CONFIG_DIR;
25891
25938
  CONFIG_PATH = USER_CONFIG_PATH;
25892
25939
  }
@@ -28939,7 +28986,6 @@ import { GoogleGenerativeAI } from "@google/generative-ai";
28939
28986
  import { createHash as createHash2 } from "node:crypto";
28940
28987
  import { existsSync as existsSync6, mkdirSync } from "node:fs";
28941
28988
  import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile7 } from "node:fs/promises";
28942
- import { homedir as homedir4 } from "node:os";
28943
28989
  import { dirname as dirname2, join as join5, resolve as resolve10 } from "node:path";
28944
28990
  import { runHyperframeLint as runHyperframeLint2 } from "@hyperframes/producer";
28945
28991
  function buildSystemPrompt(ctx) {
@@ -29109,7 +29155,7 @@ function computeCacheKey(parts) {
29109
29155
  return createHash2("sha256").update(parts.provider).update("\u241E").update(parts.model).update("\u241E").update(parts.systemPrompt).update("\u241E").update(parts.userPrompt).digest("hex");
29110
29156
  }
29111
29157
  function defaultCacheDir() {
29112
- return join5(homedir4(), ".vibeframe", "cache", "compose-scenes");
29158
+ return join5(USER_CACHE_DIR, "compose-scenes");
29113
29159
  }
29114
29160
  function extractHtml(responseText) {
29115
29161
  const fenced = responseText.match(/```html\s*\n([\s\S]*?)\n```/);
@@ -29399,6 +29445,7 @@ var MODEL_SETTINGS_BY_PROVIDER, ComposeBeatError, defaultCallLLM, CONFIG_PROVIDE
29399
29445
  var init_compose_scenes_skills = __esm({
29400
29446
  "src/commands/_shared/compose-scenes-skills.ts"() {
29401
29447
  "use strict";
29448
+ init_config();
29402
29449
  init_bundle();
29403
29450
  init_scene_lint();
29404
29451
  init_storyboard_parse();
@@ -40732,7 +40779,7 @@ var init_ai_fill_gaps = __esm({
40732
40779
 
40733
40780
  // src/pipeline/renderers/chrome.ts
40734
40781
  import { existsSync as existsSync27 } from "node:fs";
40735
- import { homedir as homedir5 } from "node:os";
40782
+ import { homedir as homedir4 } from "node:os";
40736
40783
  import { join as join14 } from "node:path";
40737
40784
  function findChrome() {
40738
40785
  const candidates = [
@@ -40740,7 +40787,7 @@ function findChrome() {
40740
40787
  process.env.CHROME_PATH,
40741
40788
  // puppeteer auto-downloaded headless shell
40742
40789
  join14(
40743
- homedir5(),
40790
+ homedir4(),
40744
40791
  ".cache",
40745
40792
  "puppeteer",
40746
40793
  "chrome-headless-shell",
@@ -58580,6 +58627,18 @@ async function createBuildPlan(opts) {
58580
58627
  `vibe storyboard revise ${projectDir} --from "<request>" --dry-run --json`
58581
58628
  );
58582
58629
  }
58630
+ if (validationOk && includeAssets && !opts.skipBackdrop) {
58631
+ const generatedBackdrops = beats.map((beat) => beat.assets.backdrop).filter((asset) => Boolean(asset?.willGenerate));
58632
+ if (generatedBackdrops.length > 0) {
58633
+ const cost = generatedBackdrops.reduce((sum, asset) => sum + asset.estimatedCostUsd, 0);
58634
+ const beatFlag = opts.beat ? ` --beat ${opts.beat}` : "";
58635
+ const skipCommand = `vibe build ${projectDir}${beatFlag} --stage ${stage} --skip-backdrop --json`;
58636
+ warnings.push(
58637
+ `Backdrop image generation will run for ${generatedBackdrops.length} beat(s), estimated $${cost.toFixed(2)}. Use --skip-backdrop for HTML/CSS-derived visuals: ${skipCommand}`
58638
+ );
58639
+ retryWith.push(skipCommand);
58640
+ }
58641
+ }
58583
58642
  return finalizeBuildPlan({
58584
58643
  projectDir,
58585
58644
  config: config2,
@@ -69184,7 +69243,7 @@ var init_project2 = __esm({
69184
69243
  });
69185
69244
 
69186
69245
  // src/commands/export.ts
69187
- import { readFile as readFile50, access as access8 } from "node:fs/promises";
69246
+ import { readFile as readFile50, access as access7 } from "node:fs/promises";
69188
69247
  import { resolve as resolve77, basename as basename24 } from "node:path";
69189
69248
  import { spawn as spawn3 } from "node:child_process";
69190
69249
  function timelineBaseName(path2) {
@@ -69274,7 +69333,7 @@ async function runExport(projectPath, outputPath, options = {}) {
69274
69333
  const source = sources.find((s) => s.id === clip.sourceId);
69275
69334
  if (source) {
69276
69335
  try {
69277
- await access8(source.url);
69336
+ await access7(source.url);
69278
69337
  if (!sourceActualDurationMap.has(source.id)) {
69279
69338
  try {
69280
69339
  const dur = await getMediaDuration(source.url, source.type);
@@ -69971,7 +70030,7 @@ Run 'vibe schema export' for structured parameter info.`).action(async (projectP
69971
70030
  const source = sources.find((s) => s.id === clip.sourceId);
69972
70031
  if (source) {
69973
70032
  try {
69974
- await access8(source.url);
70033
+ await access7(source.url);
69975
70034
  if (!sourceActualDurationMap.has(source.id)) {
69976
70035
  try {
69977
70036
  const dur = await getMediaDuration(source.url, source.type);
@@ -70081,7 +70140,7 @@ var init_export2 = __esm({
70081
70140
  });
70082
70141
 
70083
70142
  // src/tools/manifest/agent-only.ts
70084
- import { readFile as readFile51, writeFile as writeFile57, readdir as readdir7, stat as stat9, access as access9, unlink as unlink4 } from "node:fs/promises";
70143
+ import { readFile as readFile51, writeFile as writeFile57, readdir as readdir7, stat as stat9, access as access8, unlink as unlink4 } from "node:fs/promises";
70085
70144
  import { resolve as resolve79, join as join25, basename as basename25, extname as extname16 } from "node:path";
70086
70145
  function matchPattern(filename, pattern) {
70087
70146
  const regex2 = new RegExp(
@@ -70234,7 +70293,7 @@ var init_agent_only = __esm({
70234
70293
  async execute(args, ctx) {
70235
70294
  const absPath = resolve79(ctx.workingDirectory, args.path);
70236
70295
  try {
70237
- await access9(absPath);
70296
+ await access8(absPath);
70238
70297
  const stats = await stat9(absPath);
70239
70298
  const type = stats.isDirectory() ? "directory" : "file";
70240
70299
  return { success: true, data: { exists: true, type }, humanLines: [`${type} exists: ${args.path}`] };
@@ -71325,7 +71384,7 @@ init_source();
71325
71384
  // package.json
71326
71385
  var package_default = {
71327
71386
  name: "@vibeframe/cli",
71328
- version: "0.103.1",
71387
+ version: "0.104.1",
71329
71388
  description: "VibeFrame CLI - natural language video editing from the terminal",
71330
71389
  private: false,
71331
71390
  license: "MIT",
@@ -78555,7 +78614,7 @@ init_tty();
78555
78614
  init_api_key();
78556
78615
  import { resolve as resolve52 } from "node:path";
78557
78616
  import { access as access6, readFile as readFile36 } from "node:fs/promises";
78558
- import { homedir as homedir6 } from "node:os";
78617
+ import { homedir as homedir5 } from "node:os";
78559
78618
 
78560
78619
  // src/utils/key-format.ts
78561
78620
  init_dist2();
@@ -79071,6 +79130,7 @@ function mergeSelectedFeatures(current, next) {
79071
79130
  }
79072
79131
  async function runNonInteractiveSetup(opts) {
79073
79132
  const scope = opts.scope ?? "user";
79133
+ const userStatusBefore = scope === "user" ? await getUserConfigStatus() : null;
79074
79134
  let config2 = await loadConfig({ scope });
79075
79135
  if (!config2) {
79076
79136
  config2 = createDefaultConfig();
@@ -79113,6 +79173,13 @@ async function runNonInteractiveSetup(opts) {
79113
79173
  await saveConfig(config2, { scope });
79114
79174
  console.log(source_default.green(`\u2713 Setup complete (non-interactive, ${scope} scope)`));
79115
79175
  console.log(source_default.dim(` Config: ${getConfigPath(scope)}`));
79176
+ if (userStatusBefore?.source === "legacy") {
79177
+ console.log(
79178
+ source_default.dim(
79179
+ ` Imported legacy config from ${userStatusBefore.legacyConfigPath}; future writes use ${userStatusBefore.writePath}`
79180
+ )
79181
+ );
79182
+ }
79116
79183
  if (changes.length > 0) {
79117
79184
  for (const c of changes) {
79118
79185
  console.log(source_default.dim(` ${c}`));
@@ -79162,10 +79229,19 @@ async function runNonInteractiveSetup(opts) {
79162
79229
  async function runSetupWizard(fullSetup = false, scope = "user") {
79163
79230
  console.log();
79164
79231
  loadEnv();
79165
- const cfgPath = getConfigPath(scope).replace(homedir6(), "~");
79232
+ const userStatus = scope === "user" ? await getUserConfigStatus() : null;
79233
+ const displayPath2 = scope === "user" ? userStatus?.source === "legacy" ? userStatus.configPath : USER_CONFIG_PATH : getConfigPath(scope);
79234
+ const cfgPath = displayPath2.replace(homedir5(), "~");
79166
79235
  const scopeNote = scope === "project" ? "applies to this project only \xB7 add .vibeframe/ to .gitignore" : "applies to every project for this user";
79167
79236
  console.log(source_default.bold.magenta("VibeFrame Setup") + source_default.bold(` \u2014 ${scope} scope`));
79168
79237
  console.log(source_default.dim(`${cfgPath} \xB7 ${scopeNote}`));
79238
+ if (userStatus?.source === "legacy") {
79239
+ console.log(
79240
+ source_default.dim(
79241
+ `Legacy user config detected; saving will migrate it to ${userStatus.writePath.replace(homedir5(), "~")}`
79242
+ )
79243
+ );
79244
+ }
79169
79245
  if (scope === "user") {
79170
79246
  console.log(
79171
79247
  source_default.dim("Tip: for per-project keys, cd into a project and run: vibe setup --scope project")
@@ -79523,8 +79599,14 @@ async function collectKeys(config2, keys) {
79523
79599
  console.log();
79524
79600
  }
79525
79601
  async function runCustomSetup(config2, scope = "user", opts = {}) {
79526
- console.log(source_default.bold("1. LLM Provider") + source_default.dim(" (optional built-in vibe agent / fallback)"));
79527
- console.log(source_default.dim(" Most Claude Code/Codex/Cursor users can skip this and let the host agent drive vibe directly."));
79602
+ console.log(
79603
+ source_default.bold("1. LLM Provider") + source_default.dim(" (optional built-in vibe agent / fallback)")
79604
+ );
79605
+ console.log(
79606
+ source_default.dim(
79607
+ " Most Claude Code/Codex/Cursor users can skip this and let the host agent drive vibe directly."
79608
+ )
79609
+ );
79528
79610
  console.log();
79529
79611
  const providers = ["claude", "openai", "gemini", "xai", "openrouter", "ollama"];
79530
79612
  const providerDescriptions = {
@@ -79703,9 +79785,10 @@ async function showConfig(opts = { verbose: false }) {
79703
79785
  } catch {
79704
79786
  }
79705
79787
  const activeScope = await getActiveScope();
79706
- const userPath = USER_CONFIG_PATH;
79788
+ const userStatus = await getUserConfigStatus();
79789
+ const userPath = userStatus.configPath;
79707
79790
  const projectPath = getConfigPath("project");
79708
- const userExists = await loadConfig({ scope: "user" }) !== null;
79791
+ const userExists = userStatus.configured;
79709
79792
  const projectExists = await loadConfig({ scope: "project" }) !== null;
79710
79793
  console.log();
79711
79794
  console.log(source_default.bold.magenta("VibeFrame Configuration"));
@@ -79715,6 +79798,11 @@ async function showConfig(opts = { verbose: false }) {
79715
79798
  const projectMark = activeScope === "project" ? source_default.cyan("\u2190 active") : projectExists ? source_default.dim("(present)") : source_default.dim("(missing)");
79716
79799
  console.log(source_default.dim(`Scopes:`));
79717
79800
  console.log(source_default.dim(` user ${userPath} ${userMark}`));
79801
+ if (userStatus.source === "legacy") {
79802
+ console.log(source_default.dim(` writes to ${userStatus.writePath} (migrates on save)`));
79803
+ } else if (!userStatus.configured) {
79804
+ console.log(source_default.dim(` writes to ${userStatus.writePath}`));
79805
+ }
79718
79806
  console.log(source_default.dim(` project ${projectPath} ${projectMark}`));
79719
79807
  if (hasCwdEnv) {
79720
79808
  console.log(source_default.dim(` .env ${cwdEnvPath} ${source_default.dim("(env vars override config)")}`));
@@ -80293,8 +80381,28 @@ defaults:
80293
80381
 
80294
80382
  // src/commands/init.ts
80295
80383
  init_output();
80296
- var VALID_AGENTS = ["claude-code", "codex", "cursor", "aider", "gemini-cli", "opencode", "all", "auto"];
80297
- var initCommand = new Command("init").description("Scaffold a VibeFrame project (video scene project or project-scope agent files)").argument("[project-dir]", "Project directory (defaults to cwd)", ".").option("--type <type>", "Project type: scene (video project) | agent (agent files only)", "scene").option("--profile <profile>", "Scene profile: minimal (storyboard/design only), agent (recommended), full (render scaffold upfront)", "agent").option("--from <brief>", "Draft STORYBOARD.md and DESIGN.md from a brief string or text/markdown file").option("-r, --ratio <ratio>", "Scene aspect ratio: 16:9, 9:16, 1:1, 4:5", "16:9").option("-d, --duration <sec>", "Default scene/root duration in seconds", "10").option("--visual-style <name>", "Seed scene DESIGN.md from a named style").option("--agent <id>", `Agent target: ${VALID_AGENTS.join(" | ")}`, "auto").option("--force", "Overwrite existing files instead of skipping").option("--dry-run", "Print the file list without writing anything").action(async (projectDirArg, options) => {
80384
+ var VALID_AGENTS = [
80385
+ "claude-code",
80386
+ "codex",
80387
+ "cursor",
80388
+ "aider",
80389
+ "gemini-cli",
80390
+ "opencode",
80391
+ "all",
80392
+ "auto"
80393
+ ];
80394
+ var initCommand = new Command("init").description("Scaffold a VibeFrame project (video scene project or project-scope agent files)").argument("[project-dir]", "Project directory (defaults to cwd)", ".").option(
80395
+ "--type <type>",
80396
+ "Project type: scene (video project) | agent (agent files only)",
80397
+ "scene"
80398
+ ).option(
80399
+ "--profile <profile>",
80400
+ "Scene profile: minimal (storyboard/design only), agent (recommended), full (render scaffold upfront)",
80401
+ "agent"
80402
+ ).option(
80403
+ "--from <brief>",
80404
+ "Draft STORYBOARD.md and DESIGN.md from a brief string or text/markdown file"
80405
+ ).option("-r, --ratio <ratio>", "Scene aspect ratio: 16:9, 9:16, 1:1, 4:5", "16:9").option("-d, --duration <sec>", "Default scene/root duration in seconds", "10").option("--visual-style <name>", "Seed scene DESIGN.md from a named style").option("--agent <id>", `Agent target: ${VALID_AGENTS.join(" | ")}`, "auto").option("--force", "Overwrite existing files instead of skipping").option("--dry-run", "Print the file list without writing anything").action(async (projectDirArg, options) => {
80298
80406
  const startedAt = Date.now();
80299
80407
  const initType = String(options.type ?? "scene");
80300
80408
  if (initType !== "scene" && initType !== "agent") {
@@ -80306,7 +80414,9 @@ var initCommand = new Command("init").description("Scaffold a VibeFrame project
80306
80414
  }
80307
80415
  const agent = options.agent;
80308
80416
  if (!VALID_AGENTS.includes(agent)) {
80309
- exitWithError(usageError(`Invalid --agent: ${agent}`, `Must be one of: ${VALID_AGENTS.join(", ")}`));
80417
+ exitWithError(
80418
+ usageError(`Invalid --agent: ${agent}`, `Must be one of: ${VALID_AGENTS.join(", ")}`)
80419
+ );
80310
80420
  }
80311
80421
  const projectDir = resolve53(projectDirArg);
80312
80422
  const projectName = basename19(projectDir);
@@ -80317,58 +80427,72 @@ var initCommand = new Command("init").description("Scaffold a VibeFrame project
80317
80427
  const actions = [];
80318
80428
  if (!existsSync40(projectDir)) {
80319
80429
  if (options.dryRun) {
80320
- actions.push({ path: projectDir, status: "would-write", reason: "(would create directory)" });
80430
+ actions.push({
80431
+ path: projectDir,
80432
+ status: "would-write",
80433
+ reason: "(would create directory)"
80434
+ });
80321
80435
  } else {
80322
80436
  await mkdir22(projectDir, { recursive: true });
80323
80437
  }
80324
80438
  }
80325
80439
  if (wantsCrossTool || !wantsClaude) {
80326
- actions.push(await writeIfMissing(
80327
- resolve53(projectDir, "AGENTS.md"),
80328
- AGENTS_MD,
80329
- options.force,
80330
- options.dryRun
80331
- ));
80440
+ actions.push(
80441
+ await writeIfMissing(
80442
+ resolve53(projectDir, "AGENTS.md"),
80443
+ AGENTS_MD,
80444
+ options.force,
80445
+ options.dryRun
80446
+ )
80447
+ );
80332
80448
  }
80333
80449
  if (wantsClaude) {
80334
- actions.push(await writeIfMissing(
80335
- resolve53(projectDir, "CLAUDE.md"),
80336
- CLAUDE_MD,
80337
- options.force,
80338
- options.dryRun
80339
- ));
80450
+ actions.push(
80451
+ await writeIfMissing(
80452
+ resolve53(projectDir, "CLAUDE.md"),
80453
+ CLAUDE_MD,
80454
+ options.force,
80455
+ options.dryRun
80456
+ )
80457
+ );
80340
80458
  }
80341
80459
  if (wantsGemini) {
80342
- actions.push(await writeIfMissing(
80343
- resolve53(projectDir, "GEMINI.md"),
80344
- GEMINI_MD,
80460
+ actions.push(
80461
+ await writeIfMissing(
80462
+ resolve53(projectDir, "GEMINI.md"),
80463
+ GEMINI_MD,
80464
+ options.force,
80465
+ options.dryRun
80466
+ )
80467
+ );
80468
+ }
80469
+ actions.push(
80470
+ await writeIfMissing(
80471
+ resolve53(projectDir, ".env.example"),
80472
+ renderEnvExample(),
80345
80473
  options.force,
80346
80474
  options.dryRun
80347
- ));
80348
- }
80349
- actions.push(await writeIfMissing(
80350
- resolve53(projectDir, ".env.example"),
80351
- renderEnvExample(),
80352
- options.force,
80353
- options.dryRun
80354
- ));
80355
- actions.push(await mergeGitignore(
80356
- resolve53(projectDir, ".gitignore"),
80357
- GITIGNORE_ADDITIONS,
80358
- options.dryRun
80359
- ));
80360
- actions.push(await writeIfMissing(
80361
- resolve53(projectDir, VIBE_CONFIG_FILENAME),
80362
- projectConfigJson({ name: projectName }),
80363
- options.force,
80364
- options.dryRun
80365
- ));
80366
- actions.push(await writeIfMissing(
80367
- resolve53(projectDir, "vibe.project.yaml"),
80368
- renderProjectYaml({ name: projectName }),
80369
- options.force,
80370
- options.dryRun
80371
- ));
80475
+ )
80476
+ );
80477
+ actions.push(
80478
+ await mergeGitignore(resolve53(projectDir, ".gitignore"), GITIGNORE_ADDITIONS, options.dryRun)
80479
+ );
80480
+ actions.push(
80481
+ await writeIfMissing(
80482
+ resolve53(projectDir, VIBE_CONFIG_FILENAME),
80483
+ projectConfigJson({ name: projectName }),
80484
+ options.force,
80485
+ options.dryRun
80486
+ )
80487
+ );
80488
+ actions.push(
80489
+ await writeIfMissing(
80490
+ resolve53(projectDir, "vibe.project.yaml"),
80491
+ renderProjectYaml({ name: projectName }),
80492
+ options.force,
80493
+ options.dryRun
80494
+ )
80495
+ );
80372
80496
  if (isJsonMode()) {
80373
80497
  outputSuccess({
80374
80498
  command: "init",
@@ -80387,7 +80511,11 @@ var initCommand = new Command("init").description("Scaffold a VibeFrame project
80387
80511
  console.log(source_default.bold.magenta("VibeFrame Init") + source_default.dim(" \u2014 project scope"));
80388
80512
  console.log(source_default.dim("\u2500".repeat(50)));
80389
80513
  console.log(source_default.dim(`Project: ${projectDir}`));
80390
- console.log(source_default.dim(`Agent: ${agent}${agent === "auto" ? ` (resolved \u2192 ${targetHosts.join(", ") || "fallback"})` : ""}`));
80514
+ console.log(
80515
+ source_default.dim(
80516
+ `Agent: ${agent}${agent === "auto" ? ` (resolved \u2192 ${targetHosts.join(", ") || "fallback"})` : ""}`
80517
+ )
80518
+ );
80391
80519
  console.log();
80392
80520
  for (const a of actions) {
80393
80521
  const icon = formatStatusIcon(a.status);
@@ -80403,8 +80531,14 @@ var initCommand = new Command("init").description("Scaffold a VibeFrame project
80403
80531
  console.log();
80404
80532
  console.log(source_default.bold(" Next steps:"));
80405
80533
  if (wantsClaude) {
80406
- console.log(source_default.dim(" bash <(curl -fsSL https://raw.githubusercontent.com/vericontext/vibeframe/main/scripts/install-skills.sh)"));
80407
- console.log(source_default.dim(" Install /vibeframe + /vibe-* slash commands"));
80534
+ console.log(
80535
+ source_default.dim(
80536
+ " bash <(curl -fsSL https://raw.githubusercontent.com/vericontext/vibeframe/main/scripts/install-skills.sh)"
80537
+ )
80538
+ );
80539
+ console.log(
80540
+ source_default.dim(" Install /vibeframe + /vibe-* slash commands")
80541
+ );
80408
80542
  }
80409
80543
  console.log(source_default.dim(" cp .env.example .env Add your API keys"));
80410
80544
  console.log(source_default.dim(" vibe doctor Check what's configured"));
@@ -80415,19 +80549,33 @@ var VALID_SCENE_ASPECTS = ["16:9", "9:16", "1:1", "4:5"];
80415
80549
  async function runSceneInit(projectDirArg, options, startedAt) {
80416
80550
  const profile = String(options.profile ?? "agent");
80417
80551
  if (!isSceneScaffoldProfile(profile)) {
80418
- exitWithError(usageError(`Invalid --profile: ${profile}`, "Must be one of: minimal, agent, full"));
80552
+ exitWithError(
80553
+ usageError(`Invalid --profile: ${profile}`, "Must be one of: minimal, agent, full")
80554
+ );
80419
80555
  }
80420
80556
  const aspect = String(options.ratio ?? "16:9");
80421
80557
  if (!VALID_SCENE_ASPECTS.includes(aspect)) {
80422
- exitWithError(usageError(`Invalid --ratio: ${aspect}`, `Must be one of: ${VALID_SCENE_ASPECTS.join(", ")}`));
80558
+ exitWithError(
80559
+ usageError(`Invalid --ratio: ${aspect}`, `Must be one of: ${VALID_SCENE_ASPECTS.join(", ")}`)
80560
+ );
80423
80561
  }
80424
80562
  const duration = Number.parseFloat(String(options.duration ?? "10"));
80425
80563
  if (!Number.isFinite(duration) || duration <= 0 || duration > 3600) {
80426
- exitWithError(usageError(`Invalid --duration: ${String(options.duration)}`, "Duration must be a positive number of seconds (\u22643600)"));
80564
+ exitWithError(
80565
+ usageError(
80566
+ `Invalid --duration: ${String(options.duration)}`,
80567
+ "Duration must be a positive number of seconds (\u22643600)"
80568
+ )
80569
+ );
80427
80570
  }
80428
80571
  const visualStyle = options.visualStyle ? getVisualStyle(String(options.visualStyle)) : void 0;
80429
80572
  if (options.visualStyle && !visualStyle) {
80430
- exitWithError(usageError(`Unknown visual style: ${String(options.visualStyle)}`, `Valid: ${visualStyleNames()}. Browse with \`vibe scene list-styles\`.`));
80573
+ exitWithError(
80574
+ usageError(
80575
+ `Unknown visual style: ${String(options.visualStyle)}`,
80576
+ `Valid: ${visualStyleNames()}. Browse with \`vibe scene list-styles\`.`
80577
+ )
80578
+ );
80431
80579
  }
80432
80580
  const projectDir = resolve53(projectDirArg);
80433
80581
  const projectName = basename19(projectDir);
@@ -80532,17 +80680,26 @@ async function runSceneInit(projectDirArg, options, startedAt) {
80532
80680
  for (const warning of draftWarnings) console.log(source_default.yellow(`Warning: ${warning}`));
80533
80681
  console.log();
80534
80682
  console.log(source_default.bold("Edit first"));
80535
- console.log(` ${source_default.cyan("STORYBOARD.md")} ${source_default.dim("beats: narration, backdrop, minimum duration")}`);
80536
- console.log(` ${source_default.cyan("DESIGN.md")} ${source_default.dim("palette, typography, motion rules")}`);
80683
+ console.log(
80684
+ ` ${source_default.cyan("STORYBOARD.md")} ${source_default.dim("beats: narration, backdrop, minimum duration")}`
80685
+ );
80686
+ console.log(
80687
+ ` ${source_default.cyan("DESIGN.md")} ${source_default.dim("palette, typography, motion rules")}`
80688
+ );
80537
80689
  console.log();
80538
80690
  console.log(source_default.bold("Then run"));
80539
- console.log(` ${source_default.cyan("vibe build")} ${source_default.dim("build storyboard assets/compositions")}`);
80691
+ console.log(
80692
+ ` ${source_default.cyan("vibe build")} ${source_default.dim("build storyboard assets/compositions")}`
80693
+ );
80540
80694
  console.log(` ${source_default.cyan("vibe render")} ${source_default.dim("render final MP4")}`);
80541
80695
  console.log();
80542
80696
  console.log(source_default.bold("Files"));
80543
- for (const p of result.created) console.log(source_default.green(` + ${p.replace(projectDir + "/", "")}`));
80544
- for (const p of result.merged) console.log(source_default.yellow(` ~ ${p.replace(projectDir + "/", "")} (merged)`));
80545
- for (const p of result.skipped) console.log(source_default.dim(` \xB7 ${p.replace(projectDir + "/", "")} (kept existing)`));
80697
+ for (const p of result.created)
80698
+ console.log(source_default.green(` + ${p.replace(projectDir + "/", "")}`));
80699
+ for (const p of result.merged)
80700
+ console.log(source_default.yellow(` ~ ${p.replace(projectDir + "/", "")} (merged)`));
80701
+ for (const p of result.skipped)
80702
+ console.log(source_default.dim(` \xB7 ${p.replace(projectDir + "/", "")} (kept existing)`));
80546
80703
  for (const f of skillResult.files.filter((f2) => f2.status === "wrote")) {
80547
80704
  console.log(source_default.green(` + ${f.path.replace(projectDir + "/", "")}`));
80548
80705
  }
@@ -80560,21 +80717,29 @@ function printSceneInitPlan(opts) {
80560
80717
  console.log(source_default.bold.magenta("VibeFrame Init") + source_default.dim(opts.dryRun ? " \u2014 dry run" : ""));
80561
80718
  console.log(source_default.dim("\u2500".repeat(60)));
80562
80719
  console.log(`${source_default.dim("Project")} ${opts.projectDir}`);
80563
- console.log(`${source_default.dim("Profile")} ${opts.profile} ${source_default.dim(sceneProfileSummary(opts.profile))}`);
80720
+ console.log(
80721
+ `${source_default.dim("Profile")} ${opts.profile} ${source_default.dim(sceneProfileSummary(opts.profile))}`
80722
+ );
80564
80723
  console.log(`${source_default.dim("Canvas")} ${opts.aspect} \xB7 ${opts.duration}s default`);
80565
80724
  if (opts.briefSummary) console.log(`${source_default.dim("Brief")} ${opts.briefSummary}`);
80566
80725
  if (opts.visualStyleName) console.log(`${source_default.dim("Style")} ${opts.visualStyleName}`);
80567
80726
  console.log();
80568
80727
  console.log(source_default.bold("Edit first"));
80569
- console.log(` ${source_default.cyan("STORYBOARD.md")} ${source_default.dim("beats: narration, backdrop, minimum duration")}`);
80570
- console.log(` ${source_default.cyan("DESIGN.md")} ${source_default.dim("palette, typography, motion rules")}`);
80728
+ console.log(
80729
+ ` ${source_default.cyan("STORYBOARD.md")} ${source_default.dim("beats: narration, backdrop, minimum duration")}`
80730
+ );
80731
+ console.log(
80732
+ ` ${source_default.cyan("DESIGN.md")} ${source_default.dim("palette, typography, motion rules")}`
80733
+ );
80571
80734
  console.log();
80572
80735
  console.log(source_default.bold("Project contents"));
80573
80736
  printGroup("authoring", opts.groups.authoring, opts.projectDir);
80574
80737
  printGroup("agent", opts.groups.agent, opts.projectDir);
80575
80738
  printGroup("render", opts.groups.render, opts.projectDir);
80576
80739
  console.log();
80577
- console.log(source_default.dim("Dry run \u2014 no files were written. Re-run without --dry-run to create the project."));
80740
+ console.log(
80741
+ source_default.dim("Dry run \u2014 no files were written. Re-run without --dry-run to create the project.")
80742
+ );
80578
80743
  console.log();
80579
80744
  }
80580
80745
  function printGroup(label, files, projectDir) {
@@ -81862,7 +82027,6 @@ init_composer_resolve();
81862
82027
  init_cost_tier();
81863
82028
  init_scene_build();
81864
82029
  init_output();
81865
- import { access as access7 } from "node:fs/promises";
81866
82030
  import { existsSync as existsSync48 } from "node:fs";
81867
82031
  import { resolve as resolve66 } from "node:path";
81868
82032
  var COMMAND_KEY_MAP = getCommandKeyMap();
@@ -81879,7 +82043,13 @@ var FREE_COMMANDS = [
81879
82043
  "timeline (all)",
81880
82044
  "batch import/concat"
81881
82045
  ];
81882
- var doctorCommand = new Command("doctor").description("Check system health and available commands").option("--json", "Output in JSON format").option("-v, --verbose", "Show full report (every provider row, scene composer block, free-command list)").option("--test-keys", "Make a lightweight authenticated request to each provider (validates configured keys; skips providers without a cheap test endpoint)").addHelpText(
82046
+ var doctorCommand = new Command("doctor").description("Check system health and available commands").option("--json", "Output in JSON format").option(
82047
+ "-v, --verbose",
82048
+ "Show full report (every provider row, scene composer block, free-command list)"
82049
+ ).option(
82050
+ "--test-keys",
82051
+ "Make a lightweight authenticated request to each provider (validates configured keys; skips providers without a cheap test endpoint)"
82052
+ ).addHelpText(
81883
82053
  "after",
81884
82054
  `
81885
82055
  Examples:
@@ -81986,12 +82156,20 @@ async function runDiagnostics() {
81986
82156
  }
81987
82157
  } else if (toolName === "chrome") {
81988
82158
  const { existsSync: existsSync54 } = await import("node:fs");
81989
- const { homedir: homedir7 } = await import("node:os");
82159
+ const { homedir: homedir6 } = await import("node:os");
81990
82160
  const { join: join26 } = await import("node:path");
81991
82161
  const chromePaths = [
81992
82162
  process.env.HYPERFRAMES_CHROME_PATH,
81993
82163
  process.env.CHROME_PATH,
81994
- join26(homedir7(), ".cache", "puppeteer", "chrome-headless-shell", "mac_arm-147.0.7727.56", "chrome-headless-shell-mac_arm", "chrome-headless-shell"),
82164
+ join26(
82165
+ homedir6(),
82166
+ ".cache",
82167
+ "puppeteer",
82168
+ "chrome-headless-shell",
82169
+ "mac_arm-147.0.7727.56",
82170
+ "chrome-headless-shell-mac_arm",
82171
+ "chrome-headless-shell"
82172
+ ),
81995
82173
  "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
81996
82174
  "/Applications/Chromium.app/Contents/MacOS/Chromium",
81997
82175
  "/usr/bin/google-chrome",
@@ -82008,12 +82186,8 @@ async function runDiagnostics() {
82008
82186
  install: info.install
82009
82187
  };
82010
82188
  }
82011
- let configExists = false;
82012
- try {
82013
- await access7(CONFIG_PATH);
82014
- configExists = true;
82015
- } catch {
82016
- }
82189
+ const userConfig = await getUserConfigStatus();
82190
+ const configExists = userConfig.configured;
82017
82191
  const cwd = process.cwd();
82018
82192
  const activeConfig = await loadConfig({ cwd });
82019
82193
  const providers = {};
@@ -82059,13 +82233,23 @@ async function runDiagnostics() {
82059
82233
  return {
82060
82234
  system: {
82061
82235
  node: { version: nodeVersion, ok: true },
82062
- ffmpeg: { version: ffmpegVersion, ok: ffmpegExists, ...ffmpegFilters ? { filters: ffmpegFilters } : {} },
82063
- config: { path: CONFIG_PATH, ok: configExists },
82236
+ ffmpeg: {
82237
+ version: ffmpegVersion,
82238
+ ok: ffmpegExists,
82239
+ ...ffmpegFilters ? { filters: ffmpegFilters } : {}
82240
+ },
82241
+ config: { path: userConfig.configPath, ok: configExists },
82064
82242
  ...Object.keys(optionalTools).length > 0 ? { optionalTools } : {}
82065
82243
  },
82066
82244
  scope: {
82067
82245
  activeScope,
82068
- user: { configured: configExists, configPath: CONFIG_PATH },
82246
+ user: {
82247
+ configured: configExists,
82248
+ configPath: userConfig.configPath,
82249
+ writePath: userConfig.writePath,
82250
+ legacyConfigPath: userConfig.legacyConfigPath,
82251
+ source: userConfig.source
82252
+ },
82069
82253
  project: {
82070
82254
  cwd,
82071
82255
  initialized: projectInitialized,
@@ -82094,11 +82278,11 @@ function printReport(results, opts = { verbose: false }) {
82094
82278
  const nodeIcon = results.system.node.ok ? source_default.green("OK") : source_default.red("MISSING");
82095
82279
  console.log(` Node.js ${results.system.node.version} ${nodeIcon}`);
82096
82280
  if (results.system.ffmpeg.ok) {
82281
+ console.log(` FFmpeg ${results.system.ffmpeg.version} ${source_default.green("OK")}`);
82282
+ } else {
82097
82283
  console.log(
82098
- ` FFmpeg ${results.system.ffmpeg.version} ${source_default.green("OK")}`
82284
+ ` FFmpeg ${source_default.red("NOT FOUND")} ${source_default.dim("Install: brew install ffmpeg")}`
82099
82285
  );
82100
- } else {
82101
- console.log(` FFmpeg ${source_default.red("NOT FOUND")} ${source_default.dim("Install: brew install ffmpeg")}`);
82102
82286
  }
82103
82287
  if (results.system.ffmpeg.ok && results.system.ffmpeg.filters) {
82104
82288
  const filters = results.system.ffmpeg.filters;
@@ -82113,7 +82297,9 @@ function printReport(results, opts = { verbose: false }) {
82113
82297
  }
82114
82298
  }
82115
82299
  } else {
82116
- console.log(` Filters ${source_default.green("OK")} ${source_default.dim("drawtext, subtitles, afftdn")}`);
82300
+ console.log(
82301
+ ` Filters ${source_default.green("OK")} ${source_default.dim("drawtext, subtitles, afftdn")}`
82302
+ );
82117
82303
  }
82118
82304
  }
82119
82305
  if (results.system.optionalTools) {
@@ -82121,7 +82307,9 @@ function printReport(results, opts = { verbose: false }) {
82121
82307
  if (verbose) {
82122
82308
  for (const [name, info] of tools) {
82123
82309
  if (info.installed) {
82124
- console.log(` ${name.padEnd(11)}${source_default.green("OK")} ${source_default.dim(info.commands.join(", "))}`);
82310
+ console.log(
82311
+ ` ${name.padEnd(11)}${source_default.green("OK")} ${source_default.dim(info.commands.join(", "))}`
82312
+ );
82125
82313
  } else {
82126
82314
  console.log(
82127
82315
  ` ${name.padEnd(11)}${source_default.yellow("MISSING")} ${source_default.dim(`needed by: ${info.commands.join(", ")}`)}`
@@ -82149,11 +82337,14 @@ function printReport(results, opts = { verbose: false }) {
82149
82337
  const projectActive = results.scope.activeScope === "project";
82150
82338
  const activeMark = source_default.cyan(" \u2190 active");
82151
82339
  if (results.scope.user.configured) {
82340
+ const userPath = results.scope.user.source === "legacy" ? `${results.scope.user.configPath} ${source_default.dim(`(legacy; writes ${results.scope.user.writePath})`)}` : results.scope.user.configPath;
82152
82341
  console.log(
82153
- ` Cfg(user) ${source_default.green("OK")} ${source_default.dim(results.scope.user.configPath)}${userActive ? activeMark : ""}`
82342
+ ` Cfg(user) ${source_default.green("OK")} ${source_default.dim(userPath)}${userActive ? activeMark : ""}`
82154
82343
  );
82155
82344
  } else {
82156
- console.log(` Cfg(user) ${source_default.yellow("NOT SET")} ${source_default.dim("Run: vibe setup")}`);
82345
+ console.log(
82346
+ ` Cfg(user) ${source_default.yellow("NOT SET")} ${source_default.dim(`Run: vibe setup (writes ${results.scope.user.writePath})`)}`
82347
+ );
82157
82348
  }
82158
82349
  if (results.scope.project.configFileExists) {
82159
82350
  console.log(
@@ -82168,7 +82359,9 @@ function printReport(results, opts = { verbose: false }) {
82168
82359
  const present = results.scope.project.files.filter((f) => f.exists).map((f) => f.path);
82169
82360
  console.log(` Init ${source_default.green("OK")} ${source_default.dim(present.join(", "))}`);
82170
82361
  } else {
82171
- console.log(` Init ${source_default.yellow("NOT INIT")} ${source_default.dim(`Run: vibe init (cwd: ${results.scope.project.cwd})`)}`);
82362
+ console.log(
82363
+ ` Init ${source_default.yellow("NOT INIT")} ${source_default.dim(`Run: vibe init (cwd: ${results.scope.project.cwd})`)}`
82364
+ );
82172
82365
  }
82173
82366
  console.log(` Agents ${source_default.dim(results.scope.agentHosts.summary)}`);
82174
82367
  if (verbose) {
@@ -82189,12 +82382,18 @@ function printReport(results, opts = { verbose: false }) {
82189
82382
  }
82190
82383
  if (sc.sceneProjectInCwd) {
82191
82384
  if (sc.skillInstalled) {
82192
- console.log(` SKILL.md ${source_default.green("OK")} ${source_default.dim("installed in this scene project")}`);
82385
+ console.log(
82386
+ ` SKILL.md ${source_default.green("OK")} ${source_default.dim("installed in this scene project")}`
82387
+ );
82193
82388
  } else {
82194
- console.log(` SKILL.md ${source_default.yellow("MISSING")} ${source_default.dim("Run: vibe scene install-skill")}`);
82389
+ console.log(
82390
+ ` SKILL.md ${source_default.yellow("MISSING")} ${source_default.dim("Run: vibe scene install-skill")}`
82391
+ );
82195
82392
  }
82196
82393
  } else {
82197
- console.log(` SKILL.md ${source_default.dim("(no STORYBOARD.md in cwd \u2014 skill is per-scene-project)")}`);
82394
+ console.log(
82395
+ ` SKILL.md ${source_default.dim("(no STORYBOARD.md in cwd \u2014 skill is per-scene-project)")}`
82396
+ );
82198
82397
  }
82199
82398
  }
82200
82399
  console.log();
@@ -82227,7 +82426,9 @@ function printReport(results, opts = { verbose: false }) {
82227
82426
  }
82228
82427
  } else {
82229
82428
  console.log(
82230
- source_default.dim(` ${missing.length} provider${missing.length === 1 ? "" : "s"} unconfigured (${missing.slice(0, 4).join(", ")}${missing.length > 4 ? ", \u2026" : ""}). Run with --verbose to list.`)
82429
+ source_default.dim(
82430
+ ` ${missing.length} provider${missing.length === 1 ? "" : "s"} unconfigured (${missing.slice(0, 4).join(", ")}${missing.length > 4 ? ", \u2026" : ""}). Run with --verbose to list.`
82431
+ )
82231
82432
  );
82232
82433
  }
82233
82434
  }
@@ -82240,7 +82441,7 @@ function printReport(results, opts = { verbose: false }) {
82240
82441
  const pct = Math.round(results.readyCount / results.totalCount * 100);
82241
82442
  const readyColor = pct >= 80 ? source_default.green : pct >= 40 ? source_default.yellow : source_default.red;
82242
82443
  let catalogTotal = 0;
82243
- let tierCounts = { "free": 0, "low": 0, "high": 0, "very-high": 0 };
82444
+ let tierCounts = { free: 0, low: 0, high: 0, "very-high": 0 };
82244
82445
  if (program3) {
82245
82446
  catalogTotal = countCatalog(program3);
82246
82447
  tierCounts = countCostTiers(program3);
@@ -82248,7 +82449,9 @@ function printReport(results, opts = { verbose: false }) {
82248
82449
  const taggedTotal = tierCounts.free + tierCounts.low + tierCounts.high + tierCounts["very-high"];
82249
82450
  console.log(source_default.bold(" Catalog"));
82250
82451
  if (catalogTotal > 0) {
82251
- console.log(` Total commands ${source_default.bold(catalogTotal)} ${source_default.dim("(see: vibe schema --list)")}`);
82452
+ console.log(
82453
+ ` Total commands ${source_default.bold(catalogTotal)} ${source_default.dim("(see: vibe schema --list)")}`
82454
+ );
82252
82455
  }
82253
82456
  console.log(
82254
82457
  ` Runnable with keys ${readyColor(`${results.readyCount}/${results.totalCount}`)} ${source_default.dim(`(${pct}%)`)}`
@@ -82260,7 +82463,9 @@ function printReport(results, opts = { verbose: false }) {
82260
82463
  `${TIER_COLOR.high(`${tierCounts.high} high`)}`,
82261
82464
  `${TIER_COLOR["very-high"](`${tierCounts["very-high"]} very-high`)}`
82262
82465
  ];
82263
- console.log(` Cost-tagged ${source_default.bold(taggedTotal)} ${parts.join(source_default.dim(", "))}`);
82466
+ console.log(
82467
+ ` Cost-tagged ${source_default.bold(taggedTotal)} ${parts.join(source_default.dim(", "))}`
82468
+ );
82264
82469
  }
82265
82470
  const nextStep = pickNextStep(results, missing.length > 0);
82266
82471
  if (nextStep) {
@@ -82290,9 +82495,9 @@ function countCatalog(program3) {
82290
82495
  }
82291
82496
  function countCostTiers(program3) {
82292
82497
  const counts = {
82293
- "free": 0,
82294
- "low": 0,
82295
- "high": 0,
82498
+ free: 0,
82499
+ low: 0,
82500
+ high: 0,
82296
82501
  "very-high": 0
82297
82502
  };
82298
82503
  const walk = (cmd) => {
@@ -82306,7 +82511,9 @@ function countCostTiers(program3) {
82306
82511
  async function runLiveKeyTests(results) {
82307
82512
  console.log();
82308
82513
  console.log(source_default.bold(" Live key tests"));
82309
- console.log(source_default.dim(" Hits each provider's cheapest authenticated endpoint with a 5s timeout."));
82514
+ console.log(
82515
+ source_default.dim(" Hits each provider's cheapest authenticated endpoint with a 5s timeout.")
82516
+ );
82310
82517
  const configured = Object.entries(results.providers).filter(([, info]) => info.configured);
82311
82518
  if (configured.length === 0) {
82312
82519
  console.log(source_default.dim(" No keys configured \u2014 nothing to test."));
@@ -83981,20 +84188,16 @@ init_source();
83981
84188
  init_config();
83982
84189
  init_schema();
83983
84190
  init_api_key();
83984
- import { access as access10, writeFile as writeFile58, mkdir as mkdir28 } from "node:fs/promises";
84191
+ import { access as access9, writeFile as writeFile58, mkdir as mkdir28 } from "node:fs/promises";
83985
84192
  import { dirname as dirname25 } from "node:path";
83986
- var BANNER_SHOWN_PATH = CONFIG_PATH.replace(/config\.yaml$/, ".banner-shown");
84193
+ var BANNER_SHOWN_PATH = `${USER_CONFIG_DIR}/.banner-shown`;
83987
84194
  async function isFirstRun() {
83988
84195
  try {
83989
- await access10(BANNER_SHOWN_PATH);
83990
- return false;
83991
- } catch {
83992
- }
83993
- try {
83994
- await access10(CONFIG_PATH);
84196
+ await access9(BANNER_SHOWN_PATH);
83995
84197
  return false;
83996
84198
  } catch {
83997
84199
  }
84200
+ if (await loadConfig({ scope: "user" })) return false;
83998
84201
  loadEnv();
83999
84202
  for (const envVar of Object.values(PROVIDER_ENV_VARS)) {
84000
84203
  if (process.env[envVar]) {
@@ -84015,12 +84218,16 @@ function showFirstRunBanner() {
84015
84218
  console.log(source_default.cyan.bold(" Welcome to VibeFrame!"));
84016
84219
  console.log(source_default.dim(" AI-native video editing from your terminal."));
84017
84220
  console.log();
84018
- console.log(` ${source_default.white("1.")} ${source_default.green("vibe setup")} Configure API keys ${source_default.dim("(1 min)")}`);
84221
+ console.log(
84222
+ ` ${source_default.white("1.")} ${source_default.green("vibe setup")} Configure API keys ${source_default.dim("(1 min)")}`
84223
+ );
84019
84224
  console.log(` ${source_default.white("2.")} ${source_default.green("vibe doctor")} Check system health`);
84020
84225
  console.log(` ${source_default.white("3.")} ${source_default.green("vibe --help")} See all commands`);
84021
84226
  console.log();
84022
84227
  console.log(source_default.dim(" Try without keys:"));
84023
- console.log(` ${source_default.green("vibe demo")} Run sample edits on a test video`);
84228
+ console.log(
84229
+ ` ${source_default.green("vibe demo")} Run sample edits on a test video`
84230
+ );
84024
84231
  console.log(source_default.dim(" vibe edit silence-cut video.mp4 -o clean.mp4"));
84025
84232
  console.log(source_default.dim(" vibe detect scenes video.mp4"));
84026
84233
  console.log();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/cli",
3
- "version": "0.103.1",
3
+ "version": "0.104.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.103.1",
187
- "@vibeframe/core": "0.103.1"
186
+ "@vibeframe/ai-providers": "0.104.1",
187
+ "@vibeframe/core": "0.104.1"
188
188
  },
189
189
  "engines": {
190
190
  "node": ">=20"