@vibeframe/cli 0.103.0 → 0.104.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 +356 -131
  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();
@@ -32811,6 +32858,19 @@ var init_open_url = __esm({
32811
32858
  // src/utils/tty.ts
32812
32859
  import { createInterface as createInterface2 } from "node:readline";
32813
32860
  import { ReadStream } from "node:tty";
32861
+ function stripAnsi2(value) {
32862
+ return value.replace(ANSI_RE, "");
32863
+ }
32864
+ function terminalColumns() {
32865
+ return process.stdout.columns && process.stdout.columns > 0 ? process.stdout.columns : 80;
32866
+ }
32867
+ function fitOptionToLine(value, prefixColumns) {
32868
+ const maxColumns = Math.max(8, terminalColumns() - prefixColumns - 1);
32869
+ const visible = stripAnsi2(value);
32870
+ if (visible.length <= maxColumns) return value;
32871
+ if (maxColumns <= ELLIPSIS.length) return ELLIPSIS.slice(0, maxColumns);
32872
+ return `${visible.slice(0, maxColumns - ELLIPSIS.length)}${ELLIPSIS}`;
32873
+ }
32814
32874
  function splitTTYInput(chunk) {
32815
32875
  const tokens = [];
32816
32876
  for (let i = 0; i < chunk.length; i++) {
@@ -32931,7 +32991,8 @@ async function promptSelect(_question, options, defaultIndex = 0, opts = {}) {
32931
32991
  }
32932
32992
  for (let i = 0; i < options.length; i++) {
32933
32993
  const marker = i === selected ? "\x1B[36m\u276F\x1B[0m" : " ";
32934
- const text = i === selected ? `\x1B[1m${options[i]}\x1B[0m` : options[i];
32994
+ const optionText = fitOptionToLine(options[i], 5);
32995
+ const text = i === selected ? `\x1B[1m${optionText}\x1B[0m` : optionText;
32935
32996
  process.stdout.write(`\x1B[2K ${marker} ${text}
32936
32997
  `);
32937
32998
  }
@@ -33010,7 +33071,8 @@ async function promptConfirm(question, defaultYes = true) {
33010
33071
  }
33011
33072
  for (let i = 0; i < options.length; i++) {
33012
33073
  const marker = i === selected ? "\x1B[36m\u276F\x1B[0m" : " ";
33013
- const text = i === selected ? `\x1B[1m${options[i]}\x1B[0m` : options[i];
33074
+ const optionText = fitOptionToLine(options[i], 5);
33075
+ const text = i === selected ? `\x1B[1m${optionText}\x1B[0m` : optionText;
33014
33076
  process.stdout.write(`\x1B[2K ${marker} ${text}
33015
33077
  `);
33016
33078
  }
@@ -33078,7 +33140,8 @@ async function promptMultiSelect(_question, options, defaultSelected = [], opts
33078
33140
  for (let i = 0; i < options.length; i++) {
33079
33141
  const pointer = i === cursor ? "\x1B[36m\u276F\x1B[0m" : " ";
33080
33142
  const box = selected[i] ? "\x1B[36m[x]\x1B[0m" : "[ ]";
33081
- const text = i === cursor ? `\x1B[1m${options[i]}\x1B[0m` : options[i];
33143
+ const optionText = fitOptionToLine(options[i], 9);
33144
+ const text = i === cursor ? `\x1B[1m${optionText}\x1B[0m` : optionText;
33082
33145
  process.stdout.write(`\x1B[2K ${pointer} ${box} ${text}
33083
33146
  `);
33084
33147
  }
@@ -33170,12 +33233,14 @@ async function promptMultiSelect(_question, options, defaultSelected = [], opts
33170
33233
  }
33171
33234
  return [...picked].sort((a, b) => a - b);
33172
33235
  }
33173
- var ttyStream;
33236
+ var ttyStream, ANSI_RE, ELLIPSIS;
33174
33237
  var init_tty = __esm({
33175
33238
  "src/utils/tty.ts"() {
33176
33239
  "use strict";
33177
33240
  init_open_url();
33178
33241
  ttyStream = null;
33242
+ ANSI_RE = new RegExp(`${String.fromCharCode(27)}\\[[0-?]*[ -/]*[@-~]`, "g");
33243
+ ELLIPSIS = "...";
33179
33244
  }
33180
33245
  });
33181
33246
 
@@ -40714,7 +40779,7 @@ var init_ai_fill_gaps = __esm({
40714
40779
 
40715
40780
  // src/pipeline/renderers/chrome.ts
40716
40781
  import { existsSync as existsSync27 } from "node:fs";
40717
- import { homedir as homedir5 } from "node:os";
40782
+ import { homedir as homedir4 } from "node:os";
40718
40783
  import { join as join14 } from "node:path";
40719
40784
  function findChrome() {
40720
40785
  const candidates = [
@@ -40722,7 +40787,7 @@ function findChrome() {
40722
40787
  process.env.CHROME_PATH,
40723
40788
  // puppeteer auto-downloaded headless shell
40724
40789
  join14(
40725
- homedir5(),
40790
+ homedir4(),
40726
40791
  ".cache",
40727
40792
  "puppeteer",
40728
40793
  "chrome-headless-shell",
@@ -58562,6 +58627,18 @@ async function createBuildPlan(opts) {
58562
58627
  `vibe storyboard revise ${projectDir} --from "<request>" --dry-run --json`
58563
58628
  );
58564
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
+ }
58565
58642
  return finalizeBuildPlan({
58566
58643
  projectDir,
58567
58644
  config: config2,
@@ -69166,7 +69243,7 @@ var init_project2 = __esm({
69166
69243
  });
69167
69244
 
69168
69245
  // src/commands/export.ts
69169
- import { readFile as readFile50, access as access8 } from "node:fs/promises";
69246
+ import { readFile as readFile50, access as access7 } from "node:fs/promises";
69170
69247
  import { resolve as resolve77, basename as basename24 } from "node:path";
69171
69248
  import { spawn as spawn3 } from "node:child_process";
69172
69249
  function timelineBaseName(path2) {
@@ -69256,7 +69333,7 @@ async function runExport(projectPath, outputPath, options = {}) {
69256
69333
  const source = sources.find((s) => s.id === clip.sourceId);
69257
69334
  if (source) {
69258
69335
  try {
69259
- await access8(source.url);
69336
+ await access7(source.url);
69260
69337
  if (!sourceActualDurationMap.has(source.id)) {
69261
69338
  try {
69262
69339
  const dur = await getMediaDuration(source.url, source.type);
@@ -69953,7 +70030,7 @@ Run 'vibe schema export' for structured parameter info.`).action(async (projectP
69953
70030
  const source = sources.find((s) => s.id === clip.sourceId);
69954
70031
  if (source) {
69955
70032
  try {
69956
- await access8(source.url);
70033
+ await access7(source.url);
69957
70034
  if (!sourceActualDurationMap.has(source.id)) {
69958
70035
  try {
69959
70036
  const dur = await getMediaDuration(source.url, source.type);
@@ -70063,7 +70140,7 @@ var init_export2 = __esm({
70063
70140
  });
70064
70141
 
70065
70142
  // src/tools/manifest/agent-only.ts
70066
- 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";
70067
70144
  import { resolve as resolve79, join as join25, basename as basename25, extname as extname16 } from "node:path";
70068
70145
  function matchPattern(filename, pattern) {
70069
70146
  const regex2 = new RegExp(
@@ -70216,7 +70293,7 @@ var init_agent_only = __esm({
70216
70293
  async execute(args, ctx) {
70217
70294
  const absPath = resolve79(ctx.workingDirectory, args.path);
70218
70295
  try {
70219
- await access9(absPath);
70296
+ await access8(absPath);
70220
70297
  const stats = await stat9(absPath);
70221
70298
  const type = stats.isDirectory() ? "directory" : "file";
70222
70299
  return { success: true, data: { exists: true, type }, humanLines: [`${type} exists: ${args.path}`] };
@@ -71307,7 +71384,7 @@ init_source();
71307
71384
  // package.json
71308
71385
  var package_default = {
71309
71386
  name: "@vibeframe/cli",
71310
- version: "0.103.0",
71387
+ version: "0.104.0",
71311
71388
  description: "VibeFrame CLI - natural language video editing from the terminal",
71312
71389
  private: false,
71313
71390
  license: "MIT",
@@ -78537,7 +78614,7 @@ init_tty();
78537
78614
  init_api_key();
78538
78615
  import { resolve as resolve52 } from "node:path";
78539
78616
  import { access as access6, readFile as readFile36 } from "node:fs/promises";
78540
- import { homedir as homedir6 } from "node:os";
78617
+ import { homedir as homedir5 } from "node:os";
78541
78618
 
78542
78619
  // src/utils/key-format.ts
78543
78620
  init_dist2();
@@ -79053,6 +79130,7 @@ function mergeSelectedFeatures(current, next) {
79053
79130
  }
79054
79131
  async function runNonInteractiveSetup(opts) {
79055
79132
  const scope = opts.scope ?? "user";
79133
+ const userStatusBefore = scope === "user" ? await getUserConfigStatus() : null;
79056
79134
  let config2 = await loadConfig({ scope });
79057
79135
  if (!config2) {
79058
79136
  config2 = createDefaultConfig();
@@ -79095,6 +79173,13 @@ async function runNonInteractiveSetup(opts) {
79095
79173
  await saveConfig(config2, { scope });
79096
79174
  console.log(source_default.green(`\u2713 Setup complete (non-interactive, ${scope} scope)`));
79097
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
+ }
79098
79183
  if (changes.length > 0) {
79099
79184
  for (const c of changes) {
79100
79185
  console.log(source_default.dim(` ${c}`));
@@ -79144,10 +79229,19 @@ async function runNonInteractiveSetup(opts) {
79144
79229
  async function runSetupWizard(fullSetup = false, scope = "user") {
79145
79230
  console.log();
79146
79231
  loadEnv();
79147
- 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(), "~");
79148
79235
  const scopeNote = scope === "project" ? "applies to this project only \xB7 add .vibeframe/ to .gitignore" : "applies to every project for this user";
79149
79236
  console.log(source_default.bold.magenta("VibeFrame Setup") + source_default.bold(` \u2014 ${scope} scope`));
79150
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
+ }
79151
79245
  if (scope === "user") {
79152
79246
  console.log(
79153
79247
  source_default.dim("Tip: for per-project keys, cd into a project and run: vibe setup --scope project")
@@ -79505,8 +79599,14 @@ async function collectKeys(config2, keys) {
79505
79599
  console.log();
79506
79600
  }
79507
79601
  async function runCustomSetup(config2, scope = "user", opts = {}) {
79508
- console.log(source_default.bold("1. LLM Provider") + source_default.dim(" (optional built-in vibe agent / fallback)"));
79509
- 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
+ );
79510
79610
  console.log();
79511
79611
  const providers = ["claude", "openai", "gemini", "xai", "openrouter", "ollama"];
79512
79612
  const providerDescriptions = {
@@ -79685,9 +79785,10 @@ async function showConfig(opts = { verbose: false }) {
79685
79785
  } catch {
79686
79786
  }
79687
79787
  const activeScope = await getActiveScope();
79688
- const userPath = USER_CONFIG_PATH;
79788
+ const userStatus = await getUserConfigStatus();
79789
+ const userPath = userStatus.configPath;
79689
79790
  const projectPath = getConfigPath("project");
79690
- const userExists = await loadConfig({ scope: "user" }) !== null;
79791
+ const userExists = userStatus.configured;
79691
79792
  const projectExists = await loadConfig({ scope: "project" }) !== null;
79692
79793
  console.log();
79693
79794
  console.log(source_default.bold.magenta("VibeFrame Configuration"));
@@ -79697,6 +79798,11 @@ async function showConfig(opts = { verbose: false }) {
79697
79798
  const projectMark = activeScope === "project" ? source_default.cyan("\u2190 active") : projectExists ? source_default.dim("(present)") : source_default.dim("(missing)");
79698
79799
  console.log(source_default.dim(`Scopes:`));
79699
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
+ }
79700
79806
  console.log(source_default.dim(` project ${projectPath} ${projectMark}`));
79701
79807
  if (hasCwdEnv) {
79702
79808
  console.log(source_default.dim(` .env ${cwdEnvPath} ${source_default.dim("(env vars override config)")}`));
@@ -80275,8 +80381,28 @@ defaults:
80275
80381
 
80276
80382
  // src/commands/init.ts
80277
80383
  init_output();
80278
- var VALID_AGENTS = ["claude-code", "codex", "cursor", "aider", "gemini-cli", "opencode", "all", "auto"];
80279
- 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) => {
80280
80406
  const startedAt = Date.now();
80281
80407
  const initType = String(options.type ?? "scene");
80282
80408
  if (initType !== "scene" && initType !== "agent") {
@@ -80288,7 +80414,9 @@ var initCommand = new Command("init").description("Scaffold a VibeFrame project
80288
80414
  }
80289
80415
  const agent = options.agent;
80290
80416
  if (!VALID_AGENTS.includes(agent)) {
80291
- 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
+ );
80292
80420
  }
80293
80421
  const projectDir = resolve53(projectDirArg);
80294
80422
  const projectName = basename19(projectDir);
@@ -80299,58 +80427,72 @@ var initCommand = new Command("init").description("Scaffold a VibeFrame project
80299
80427
  const actions = [];
80300
80428
  if (!existsSync40(projectDir)) {
80301
80429
  if (options.dryRun) {
80302
- 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
+ });
80303
80435
  } else {
80304
80436
  await mkdir22(projectDir, { recursive: true });
80305
80437
  }
80306
80438
  }
80307
80439
  if (wantsCrossTool || !wantsClaude) {
80308
- actions.push(await writeIfMissing(
80309
- resolve53(projectDir, "AGENTS.md"),
80310
- AGENTS_MD,
80311
- options.force,
80312
- options.dryRun
80313
- ));
80440
+ actions.push(
80441
+ await writeIfMissing(
80442
+ resolve53(projectDir, "AGENTS.md"),
80443
+ AGENTS_MD,
80444
+ options.force,
80445
+ options.dryRun
80446
+ )
80447
+ );
80314
80448
  }
80315
80449
  if (wantsClaude) {
80316
- actions.push(await writeIfMissing(
80317
- resolve53(projectDir, "CLAUDE.md"),
80318
- CLAUDE_MD,
80319
- options.force,
80320
- options.dryRun
80321
- ));
80450
+ actions.push(
80451
+ await writeIfMissing(
80452
+ resolve53(projectDir, "CLAUDE.md"),
80453
+ CLAUDE_MD,
80454
+ options.force,
80455
+ options.dryRun
80456
+ )
80457
+ );
80322
80458
  }
80323
80459
  if (wantsGemini) {
80324
- actions.push(await writeIfMissing(
80325
- resolve53(projectDir, "GEMINI.md"),
80326
- 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(),
80327
80473
  options.force,
80328
80474
  options.dryRun
80329
- ));
80330
- }
80331
- actions.push(await writeIfMissing(
80332
- resolve53(projectDir, ".env.example"),
80333
- renderEnvExample(),
80334
- options.force,
80335
- options.dryRun
80336
- ));
80337
- actions.push(await mergeGitignore(
80338
- resolve53(projectDir, ".gitignore"),
80339
- GITIGNORE_ADDITIONS,
80340
- options.dryRun
80341
- ));
80342
- actions.push(await writeIfMissing(
80343
- resolve53(projectDir, VIBE_CONFIG_FILENAME),
80344
- projectConfigJson({ name: projectName }),
80345
- options.force,
80346
- options.dryRun
80347
- ));
80348
- actions.push(await writeIfMissing(
80349
- resolve53(projectDir, "vibe.project.yaml"),
80350
- renderProjectYaml({ name: projectName }),
80351
- options.force,
80352
- options.dryRun
80353
- ));
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
+ );
80354
80496
  if (isJsonMode()) {
80355
80497
  outputSuccess({
80356
80498
  command: "init",
@@ -80369,7 +80511,11 @@ var initCommand = new Command("init").description("Scaffold a VibeFrame project
80369
80511
  console.log(source_default.bold.magenta("VibeFrame Init") + source_default.dim(" \u2014 project scope"));
80370
80512
  console.log(source_default.dim("\u2500".repeat(50)));
80371
80513
  console.log(source_default.dim(`Project: ${projectDir}`));
80372
- 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
+ );
80373
80519
  console.log();
80374
80520
  for (const a of actions) {
80375
80521
  const icon = formatStatusIcon(a.status);
@@ -80385,8 +80531,14 @@ var initCommand = new Command("init").description("Scaffold a VibeFrame project
80385
80531
  console.log();
80386
80532
  console.log(source_default.bold(" Next steps:"));
80387
80533
  if (wantsClaude) {
80388
- console.log(source_default.dim(" bash <(curl -fsSL https://raw.githubusercontent.com/vericontext/vibeframe/main/scripts/install-skills.sh)"));
80389
- 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
+ );
80390
80542
  }
80391
80543
  console.log(source_default.dim(" cp .env.example .env Add your API keys"));
80392
80544
  console.log(source_default.dim(" vibe doctor Check what's configured"));
@@ -80397,19 +80549,33 @@ var VALID_SCENE_ASPECTS = ["16:9", "9:16", "1:1", "4:5"];
80397
80549
  async function runSceneInit(projectDirArg, options, startedAt) {
80398
80550
  const profile = String(options.profile ?? "agent");
80399
80551
  if (!isSceneScaffoldProfile(profile)) {
80400
- 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
+ );
80401
80555
  }
80402
80556
  const aspect = String(options.ratio ?? "16:9");
80403
80557
  if (!VALID_SCENE_ASPECTS.includes(aspect)) {
80404
- 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
+ );
80405
80561
  }
80406
80562
  const duration = Number.parseFloat(String(options.duration ?? "10"));
80407
80563
  if (!Number.isFinite(duration) || duration <= 0 || duration > 3600) {
80408
- 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
+ );
80409
80570
  }
80410
80571
  const visualStyle = options.visualStyle ? getVisualStyle(String(options.visualStyle)) : void 0;
80411
80572
  if (options.visualStyle && !visualStyle) {
80412
- 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
+ );
80413
80579
  }
80414
80580
  const projectDir = resolve53(projectDirArg);
80415
80581
  const projectName = basename19(projectDir);
@@ -80514,17 +80680,26 @@ async function runSceneInit(projectDirArg, options, startedAt) {
80514
80680
  for (const warning of draftWarnings) console.log(source_default.yellow(`Warning: ${warning}`));
80515
80681
  console.log();
80516
80682
  console.log(source_default.bold("Edit first"));
80517
- console.log(` ${source_default.cyan("STORYBOARD.md")} ${source_default.dim("beats: narration, backdrop, minimum duration")}`);
80518
- 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
+ );
80519
80689
  console.log();
80520
80690
  console.log(source_default.bold("Then run"));
80521
- 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
+ );
80522
80694
  console.log(` ${source_default.cyan("vibe render")} ${source_default.dim("render final MP4")}`);
80523
80695
  console.log();
80524
80696
  console.log(source_default.bold("Files"));
80525
- for (const p of result.created) console.log(source_default.green(` + ${p.replace(projectDir + "/", "")}`));
80526
- for (const p of result.merged) console.log(source_default.yellow(` ~ ${p.replace(projectDir + "/", "")} (merged)`));
80527
- 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)`));
80528
80703
  for (const f of skillResult.files.filter((f2) => f2.status === "wrote")) {
80529
80704
  console.log(source_default.green(` + ${f.path.replace(projectDir + "/", "")}`));
80530
80705
  }
@@ -80542,21 +80717,29 @@ function printSceneInitPlan(opts) {
80542
80717
  console.log(source_default.bold.magenta("VibeFrame Init") + source_default.dim(opts.dryRun ? " \u2014 dry run" : ""));
80543
80718
  console.log(source_default.dim("\u2500".repeat(60)));
80544
80719
  console.log(`${source_default.dim("Project")} ${opts.projectDir}`);
80545
- 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
+ );
80546
80723
  console.log(`${source_default.dim("Canvas")} ${opts.aspect} \xB7 ${opts.duration}s default`);
80547
80724
  if (opts.briefSummary) console.log(`${source_default.dim("Brief")} ${opts.briefSummary}`);
80548
80725
  if (opts.visualStyleName) console.log(`${source_default.dim("Style")} ${opts.visualStyleName}`);
80549
80726
  console.log();
80550
80727
  console.log(source_default.bold("Edit first"));
80551
- console.log(` ${source_default.cyan("STORYBOARD.md")} ${source_default.dim("beats: narration, backdrop, minimum duration")}`);
80552
- 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
+ );
80553
80734
  console.log();
80554
80735
  console.log(source_default.bold("Project contents"));
80555
80736
  printGroup("authoring", opts.groups.authoring, opts.projectDir);
80556
80737
  printGroup("agent", opts.groups.agent, opts.projectDir);
80557
80738
  printGroup("render", opts.groups.render, opts.projectDir);
80558
80739
  console.log();
80559
- 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
+ );
80560
80743
  console.log();
80561
80744
  }
80562
80745
  function printGroup(label, files, projectDir) {
@@ -81844,7 +82027,6 @@ init_composer_resolve();
81844
82027
  init_cost_tier();
81845
82028
  init_scene_build();
81846
82029
  init_output();
81847
- import { access as access7 } from "node:fs/promises";
81848
82030
  import { existsSync as existsSync48 } from "node:fs";
81849
82031
  import { resolve as resolve66 } from "node:path";
81850
82032
  var COMMAND_KEY_MAP = getCommandKeyMap();
@@ -81861,7 +82043,13 @@ var FREE_COMMANDS = [
81861
82043
  "timeline (all)",
81862
82044
  "batch import/concat"
81863
82045
  ];
81864
- 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(
81865
82053
  "after",
81866
82054
  `
81867
82055
  Examples:
@@ -81968,12 +82156,20 @@ async function runDiagnostics() {
81968
82156
  }
81969
82157
  } else if (toolName === "chrome") {
81970
82158
  const { existsSync: existsSync54 } = await import("node:fs");
81971
- const { homedir: homedir7 } = await import("node:os");
82159
+ const { homedir: homedir6 } = await import("node:os");
81972
82160
  const { join: join26 } = await import("node:path");
81973
82161
  const chromePaths = [
81974
82162
  process.env.HYPERFRAMES_CHROME_PATH,
81975
82163
  process.env.CHROME_PATH,
81976
- 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
+ ),
81977
82173
  "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
81978
82174
  "/Applications/Chromium.app/Contents/MacOS/Chromium",
81979
82175
  "/usr/bin/google-chrome",
@@ -81990,12 +82186,8 @@ async function runDiagnostics() {
81990
82186
  install: info.install
81991
82187
  };
81992
82188
  }
81993
- let configExists = false;
81994
- try {
81995
- await access7(CONFIG_PATH);
81996
- configExists = true;
81997
- } catch {
81998
- }
82189
+ const userConfig = await getUserConfigStatus();
82190
+ const configExists = userConfig.configured;
81999
82191
  const cwd = process.cwd();
82000
82192
  const activeConfig = await loadConfig({ cwd });
82001
82193
  const providers = {};
@@ -82041,13 +82233,23 @@ async function runDiagnostics() {
82041
82233
  return {
82042
82234
  system: {
82043
82235
  node: { version: nodeVersion, ok: true },
82044
- ffmpeg: { version: ffmpegVersion, ok: ffmpegExists, ...ffmpegFilters ? { filters: ffmpegFilters } : {} },
82045
- 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 },
82046
82242
  ...Object.keys(optionalTools).length > 0 ? { optionalTools } : {}
82047
82243
  },
82048
82244
  scope: {
82049
82245
  activeScope,
82050
- 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
+ },
82051
82253
  project: {
82052
82254
  cwd,
82053
82255
  initialized: projectInitialized,
@@ -82076,11 +82278,11 @@ function printReport(results, opts = { verbose: false }) {
82076
82278
  const nodeIcon = results.system.node.ok ? source_default.green("OK") : source_default.red("MISSING");
82077
82279
  console.log(` Node.js ${results.system.node.version} ${nodeIcon}`);
82078
82280
  if (results.system.ffmpeg.ok) {
82281
+ console.log(` FFmpeg ${results.system.ffmpeg.version} ${source_default.green("OK")}`);
82282
+ } else {
82079
82283
  console.log(
82080
- ` FFmpeg ${results.system.ffmpeg.version} ${source_default.green("OK")}`
82284
+ ` FFmpeg ${source_default.red("NOT FOUND")} ${source_default.dim("Install: brew install ffmpeg")}`
82081
82285
  );
82082
- } else {
82083
- console.log(` FFmpeg ${source_default.red("NOT FOUND")} ${source_default.dim("Install: brew install ffmpeg")}`);
82084
82286
  }
82085
82287
  if (results.system.ffmpeg.ok && results.system.ffmpeg.filters) {
82086
82288
  const filters = results.system.ffmpeg.filters;
@@ -82095,7 +82297,9 @@ function printReport(results, opts = { verbose: false }) {
82095
82297
  }
82096
82298
  }
82097
82299
  } else {
82098
- 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
+ );
82099
82303
  }
82100
82304
  }
82101
82305
  if (results.system.optionalTools) {
@@ -82103,7 +82307,9 @@ function printReport(results, opts = { verbose: false }) {
82103
82307
  if (verbose) {
82104
82308
  for (const [name, info] of tools) {
82105
82309
  if (info.installed) {
82106
- 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
+ );
82107
82313
  } else {
82108
82314
  console.log(
82109
82315
  ` ${name.padEnd(11)}${source_default.yellow("MISSING")} ${source_default.dim(`needed by: ${info.commands.join(", ")}`)}`
@@ -82131,11 +82337,14 @@ function printReport(results, opts = { verbose: false }) {
82131
82337
  const projectActive = results.scope.activeScope === "project";
82132
82338
  const activeMark = source_default.cyan(" \u2190 active");
82133
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;
82134
82341
  console.log(
82135
- ` 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 : ""}`
82136
82343
  );
82137
82344
  } else {
82138
- 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
+ );
82139
82348
  }
82140
82349
  if (results.scope.project.configFileExists) {
82141
82350
  console.log(
@@ -82150,7 +82359,9 @@ function printReport(results, opts = { verbose: false }) {
82150
82359
  const present = results.scope.project.files.filter((f) => f.exists).map((f) => f.path);
82151
82360
  console.log(` Init ${source_default.green("OK")} ${source_default.dim(present.join(", "))}`);
82152
82361
  } else {
82153
- 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
+ );
82154
82365
  }
82155
82366
  console.log(` Agents ${source_default.dim(results.scope.agentHosts.summary)}`);
82156
82367
  if (verbose) {
@@ -82171,12 +82382,18 @@ function printReport(results, opts = { verbose: false }) {
82171
82382
  }
82172
82383
  if (sc.sceneProjectInCwd) {
82173
82384
  if (sc.skillInstalled) {
82174
- 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
+ );
82175
82388
  } else {
82176
- 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
+ );
82177
82392
  }
82178
82393
  } else {
82179
- 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
+ );
82180
82397
  }
82181
82398
  }
82182
82399
  console.log();
@@ -82209,7 +82426,9 @@ function printReport(results, opts = { verbose: false }) {
82209
82426
  }
82210
82427
  } else {
82211
82428
  console.log(
82212
- 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
+ )
82213
82432
  );
82214
82433
  }
82215
82434
  }
@@ -82222,7 +82441,7 @@ function printReport(results, opts = { verbose: false }) {
82222
82441
  const pct = Math.round(results.readyCount / results.totalCount * 100);
82223
82442
  const readyColor = pct >= 80 ? source_default.green : pct >= 40 ? source_default.yellow : source_default.red;
82224
82443
  let catalogTotal = 0;
82225
- let tierCounts = { "free": 0, "low": 0, "high": 0, "very-high": 0 };
82444
+ let tierCounts = { free: 0, low: 0, high: 0, "very-high": 0 };
82226
82445
  if (program3) {
82227
82446
  catalogTotal = countCatalog(program3);
82228
82447
  tierCounts = countCostTiers(program3);
@@ -82230,7 +82449,9 @@ function printReport(results, opts = { verbose: false }) {
82230
82449
  const taggedTotal = tierCounts.free + tierCounts.low + tierCounts.high + tierCounts["very-high"];
82231
82450
  console.log(source_default.bold(" Catalog"));
82232
82451
  if (catalogTotal > 0) {
82233
- 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
+ );
82234
82455
  }
82235
82456
  console.log(
82236
82457
  ` Runnable with keys ${readyColor(`${results.readyCount}/${results.totalCount}`)} ${source_default.dim(`(${pct}%)`)}`
@@ -82242,7 +82463,9 @@ function printReport(results, opts = { verbose: false }) {
82242
82463
  `${TIER_COLOR.high(`${tierCounts.high} high`)}`,
82243
82464
  `${TIER_COLOR["very-high"](`${tierCounts["very-high"]} very-high`)}`
82244
82465
  ];
82245
- 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
+ );
82246
82469
  }
82247
82470
  const nextStep = pickNextStep(results, missing.length > 0);
82248
82471
  if (nextStep) {
@@ -82272,9 +82495,9 @@ function countCatalog(program3) {
82272
82495
  }
82273
82496
  function countCostTiers(program3) {
82274
82497
  const counts = {
82275
- "free": 0,
82276
- "low": 0,
82277
- "high": 0,
82498
+ free: 0,
82499
+ low: 0,
82500
+ high: 0,
82278
82501
  "very-high": 0
82279
82502
  };
82280
82503
  const walk = (cmd) => {
@@ -82288,7 +82511,9 @@ function countCostTiers(program3) {
82288
82511
  async function runLiveKeyTests(results) {
82289
82512
  console.log();
82290
82513
  console.log(source_default.bold(" Live key tests"));
82291
- 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
+ );
82292
82517
  const configured = Object.entries(results.providers).filter(([, info]) => info.configured);
82293
82518
  if (configured.length === 0) {
82294
82519
  console.log(source_default.dim(" No keys configured \u2014 nothing to test."));
@@ -83963,20 +84188,16 @@ init_source();
83963
84188
  init_config();
83964
84189
  init_schema();
83965
84190
  init_api_key();
83966
- 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";
83967
84192
  import { dirname as dirname25 } from "node:path";
83968
- var BANNER_SHOWN_PATH = CONFIG_PATH.replace(/config\.yaml$/, ".banner-shown");
84193
+ var BANNER_SHOWN_PATH = `${USER_CONFIG_DIR}/.banner-shown`;
83969
84194
  async function isFirstRun() {
83970
84195
  try {
83971
- await access10(BANNER_SHOWN_PATH);
83972
- return false;
83973
- } catch {
83974
- }
83975
- try {
83976
- await access10(CONFIG_PATH);
84196
+ await access9(BANNER_SHOWN_PATH);
83977
84197
  return false;
83978
84198
  } catch {
83979
84199
  }
84200
+ if (await loadConfig({ scope: "user" })) return false;
83980
84201
  loadEnv();
83981
84202
  for (const envVar of Object.values(PROVIDER_ENV_VARS)) {
83982
84203
  if (process.env[envVar]) {
@@ -83997,12 +84218,16 @@ function showFirstRunBanner() {
83997
84218
  console.log(source_default.cyan.bold(" Welcome to VibeFrame!"));
83998
84219
  console.log(source_default.dim(" AI-native video editing from your terminal."));
83999
84220
  console.log();
84000
- 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
+ );
84001
84224
  console.log(` ${source_default.white("2.")} ${source_default.green("vibe doctor")} Check system health`);
84002
84225
  console.log(` ${source_default.white("3.")} ${source_default.green("vibe --help")} See all commands`);
84003
84226
  console.log();
84004
84227
  console.log(source_default.dim(" Try without keys:"));
84005
- 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
+ );
84006
84231
  console.log(source_default.dim(" vibe edit silence-cut video.mp4 -o clean.mp4"));
84007
84232
  console.log(source_default.dim(" vibe detect scenes video.mp4"));
84008
84233
  console.log();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/cli",
3
- "version": "0.103.0",
3
+ "version": "0.104.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.103.0",
187
- "@vibeframe/core": "0.103.0"
186
+ "@vibeframe/ai-providers": "0.104.0",
187
+ "@vibeframe/core": "0.104.0"
188
188
  },
189
189
  "engines": {
190
190
  "node": ">=20"