@loworbitstudio/visor 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { readFileSync as readFileSync32 } from "fs";
5
- import { dirname as dirname11, join as join30 } from "path";
5
+ import { dirname as dirname12, join as join32 } from "path";
6
6
  import { fileURLToPath as fileURLToPath4 } from "url";
7
7
  import { Command as Command2 } from "commander";
8
8
 
@@ -2792,8 +2792,8 @@ function infoCommand(name, cwd, options = {}) {
2792
2792
  }
2793
2793
 
2794
2794
  // src/commands/theme-apply.ts
2795
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync6, mkdirSync as mkdirSync4 } from "fs";
2796
- import { resolve as resolve5, dirname as dirname6, join as join12 } from "path";
2795
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6 } from "fs";
2796
+ import { resolve as resolve7, dirname as dirname7, join as join15 } from "path";
2797
2797
  import { generateTheme, generateThemeData as generateThemeData3 } from "@loworbitstudio/visor-theme-engine";
2798
2798
  import {
2799
2799
  nextjsAdapter as nextjsAdapter2,
@@ -2802,6 +2802,143 @@ import {
2802
2802
  docsAdapter,
2803
2803
  flutterAdapter
2804
2804
  } from "@loworbitstudio/visor-theme-engine/adapters";
2805
+
2806
+ // src/lib/blessed-manifest.ts
2807
+ import { existsSync as existsSync10, readFileSync as readFileSync12 } from "fs";
2808
+ import { join as join12 } from "path";
2809
+ import { z } from "zod";
2810
+ var BLESSED_MANIFEST_FILENAME = "blessed-manifest.json";
2811
+ var themeApplyTargetSchema = z.discriminatedUnion("kind", [
2812
+ z.object({
2813
+ kind: z.literal("globals-css"),
2814
+ /** Build-root-relative destination file. Defaults to `app/globals.css`. */
2815
+ path: z.string().min(1).optional()
2816
+ }).strict(),
2817
+ z.object({
2818
+ kind: z.literal("themes-css-dir"),
2819
+ /** Build-root-relative directory. The adapter output is written to `<path>/<theme-id>.css`. */
2820
+ path: z.string().min(1)
2821
+ }).strict()
2822
+ ]);
2823
+ var blessedManifestSchema = z.object({
2824
+ /** Blessed-build shape, e.g. `admin-ui`. Matched against the `{shape}` in `blessed:{shape}:{pattern}`. */
2825
+ shape: z.string().min(1),
2826
+ /** Pattern name within the shape, e.g. `organization-management`. */
2827
+ pattern: z.string().min(1),
2828
+ /** The theme the reference build was authored + captured against. */
2829
+ base_theme: z.string().min(1),
2830
+ /** Minimum Visor version this build is known to work with (semver range). */
2831
+ requires_visor: z.string().min(1),
2832
+ /** Path (relative to the build root) to the approved capture baseline. */
2833
+ captures_baseline: z.string().min(1),
2834
+ /** Three-gates disposition at the time the build was blessed. */
2835
+ three_gates_status: z.string().min(1),
2836
+ /** Optional: where to write the nextjs-adapter CSS. Absent = `globals-css`. */
2837
+ theme_apply_target: themeApplyTargetSchema.optional()
2838
+ }).strict();
2839
+ function parseBlessedManifest(buildDir) {
2840
+ const manifestPath = join12(buildDir, BLESSED_MANIFEST_FILENAME);
2841
+ if (!existsSync10(manifestPath)) {
2842
+ return {
2843
+ ok: false,
2844
+ error: `this directory is not a blessed build (missing ${BLESSED_MANIFEST_FILENAME}); see docs/blessed-builds.md`
2845
+ };
2846
+ }
2847
+ let raw;
2848
+ try {
2849
+ raw = readFileSync12(manifestPath, "utf-8");
2850
+ } catch {
2851
+ return { ok: false, error: `could not read ${manifestPath}` };
2852
+ }
2853
+ let json;
2854
+ try {
2855
+ json = JSON.parse(raw);
2856
+ } catch (err) {
2857
+ const message = err instanceof Error ? err.message : "invalid JSON";
2858
+ return { ok: false, error: `invalid JSON in ${manifestPath}: ${message}` };
2859
+ }
2860
+ const result = blessedManifestSchema.safeParse(json);
2861
+ if (!result.success) {
2862
+ const issues = result.error.issues.map((issue) => {
2863
+ const path2 = issue.path.join(".") || "(root)";
2864
+ return `${path2}: ${issue.message}`;
2865
+ }).join("; ");
2866
+ return {
2867
+ ok: false,
2868
+ error: `invalid ${BLESSED_MANIFEST_FILENAME} at ${manifestPath}: ${issues}`
2869
+ };
2870
+ }
2871
+ return { ok: true, manifest: result.data };
2872
+ }
2873
+
2874
+ // src/lib/theme-apply-targets/globals-css.ts
2875
+ import { existsSync as existsSync11, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6 } from "fs";
2876
+ import { dirname as dirname6, isAbsolute, join as join13, resolve as resolve5 } from "path";
2877
+ function applyGlobalsCss(input) {
2878
+ const outFile = input.path !== void 0 ? resolveInsideBuild(input.buildDir, input.path) : resolveDefaultGlobalsCss(input.buildDir);
2879
+ mkdirSync4(dirname6(outFile), { recursive: true });
2880
+ writeFileSync6(outFile, input.adapterCss, "utf-8");
2881
+ return outFile;
2882
+ }
2883
+ function resolveDefaultGlobalsCss(buildDir) {
2884
+ const candidates = [
2885
+ join13(buildDir, "app", "globals.css"),
2886
+ join13(buildDir, "src", "app", "globals.css")
2887
+ ];
2888
+ return candidates.find((candidate) => existsSync11(candidate)) ?? candidates[0];
2889
+ }
2890
+ function resolveInsideBuild(buildDir, relativeOrAbsolute) {
2891
+ return isAbsolute(relativeOrAbsolute) ? relativeOrAbsolute : resolve5(buildDir, relativeOrAbsolute);
2892
+ }
2893
+
2894
+ // src/lib/theme-apply-targets/themes-css-dir.ts
2895
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync7 } from "fs";
2896
+ import { isAbsolute as isAbsolute2, join as join14, resolve as resolve6 } from "path";
2897
+ function applyThemesCssDir(input) {
2898
+ const dir = isAbsolute2(input.path) ? input.path : resolve6(input.buildDir, input.path);
2899
+ const outFile = join14(dir, `${input.themeId}.css`);
2900
+ mkdirSync5(dir, { recursive: true });
2901
+ writeFileSync7(outFile, input.adapterCss, "utf-8");
2902
+ return outFile;
2903
+ }
2904
+
2905
+ // src/lib/theme-apply-targets/index.ts
2906
+ var IMPLICIT_TARGET = { kind: "globals-css" };
2907
+ function applyThemeToBuild(input) {
2908
+ const target = input.manifest.theme_apply_target ?? IMPLICIT_TARGET;
2909
+ switch (target.kind) {
2910
+ case "globals-css": {
2911
+ const writtenPath = applyGlobalsCss({
2912
+ buildDir: input.buildDir,
2913
+ adapterCss: input.adapterCss,
2914
+ path: target.path
2915
+ });
2916
+ return { writtenPath, target };
2917
+ }
2918
+ case "themes-css-dir": {
2919
+ if (!input.themeId || input.themeId.length === 0) {
2920
+ throw new Error(
2921
+ `theme_apply_target kind 'themes-css-dir' requires a theme id (used to name the emitted file); see docs/blessed-builds.md`
2922
+ );
2923
+ }
2924
+ const writtenPath = applyThemesCssDir({
2925
+ buildDir: input.buildDir,
2926
+ adapterCss: input.adapterCss,
2927
+ themeId: input.themeId,
2928
+ path: target.path
2929
+ });
2930
+ return { writtenPath, target };
2931
+ }
2932
+ default: {
2933
+ const unknown = target.kind;
2934
+ throw new Error(
2935
+ `Unknown theme_apply_target kind: '${unknown}'. Supported: 'globals-css', 'themes-css-dir'. See docs/blessed-builds.md.`
2936
+ );
2937
+ }
2938
+ }
2939
+ }
2940
+
2941
+ // src/commands/theme-apply.ts
2805
2942
  function defaultOutputPath(adapter, themeName) {
2806
2943
  switch (adapter) {
2807
2944
  case "nextjs":
@@ -2823,10 +2960,10 @@ function defaultOutputPath(adapter, themeName) {
2823
2960
  }
2824
2961
  }
2825
2962
  function themeApplyCommand(file, cwd, options) {
2826
- const filePath = resolve5(cwd, file);
2963
+ const filePath = resolve7(cwd, file);
2827
2964
  let yamlContent;
2828
2965
  try {
2829
- yamlContent = readFileSync12(filePath, "utf-8");
2966
+ yamlContent = readFileSync13(filePath, "utf-8");
2830
2967
  } catch {
2831
2968
  if (options.json) {
2832
2969
  console.log(
@@ -2904,16 +3041,74 @@ function themeApplyCommand(file, cwd, options) {
2904
3041
  }
2905
3042
  process.exit(1);
2906
3043
  }
3044
+ if (options.targetPath) {
3045
+ if (options.adapter !== "nextjs") {
3046
+ const message = "--target-path requires --adapter nextjs; see docs/blessed-builds.md";
3047
+ if (options.json) console.log(JSON.stringify({ success: false, error: message }));
3048
+ else logger.error(message);
3049
+ process.exit(1);
3050
+ }
3051
+ if (css === null) {
3052
+ process.exit(1);
3053
+ }
3054
+ if (!themeName || themeName.length === 0) {
3055
+ const message = "theme config is missing a `name` \u2014 required to derive the theme id for --target-path dispatch; see docs/blessed-builds.md";
3056
+ if (options.json) console.log(JSON.stringify({ success: false, error: message }));
3057
+ else logger.error(message);
3058
+ process.exit(1);
3059
+ }
3060
+ const buildDir = resolve7(cwd, options.targetPath);
3061
+ const manifestResult = parseBlessedManifest(buildDir);
3062
+ if (!manifestResult.ok) {
3063
+ if (options.json) {
3064
+ console.log(JSON.stringify({ success: false, error: manifestResult.error }));
3065
+ } else {
3066
+ logger.error(manifestResult.error);
3067
+ }
3068
+ process.exit(2);
3069
+ }
3070
+ let writtenPath;
3071
+ try {
3072
+ writtenPath = applyThemeToBuild({
3073
+ manifest: manifestResult.manifest,
3074
+ buildDir,
3075
+ themeId: themeName,
3076
+ adapterCss: css
3077
+ }).writtenPath;
3078
+ } catch (err) {
3079
+ const message = err instanceof Error ? err.message : String(err);
3080
+ if (options.json) console.log(JSON.stringify({ success: false, error: message }));
3081
+ else logger.error(message);
3082
+ process.exit(1);
3083
+ }
3084
+ if (options.json) {
3085
+ console.log(
3086
+ JSON.stringify({
3087
+ success: true,
3088
+ file: writtenPath,
3089
+ adapter: options.adapter,
3090
+ size: css.length,
3091
+ targetPath: buildDir
3092
+ })
3093
+ );
3094
+ } else {
3095
+ logger.success(`Theme CSS written via blessed-manifest dispatch: ${writtenPath}`);
3096
+ logger.info(`Adapter: ${options.adapter}`);
3097
+ logger.item(`Build root: ${buildDir}`);
3098
+ logger.item(`Size: ${formatSize(css.length)}`);
3099
+ }
3100
+ return;
3101
+ }
2907
3102
  const outputTarget = options.output ?? defaultOutputPath(options.adapter, themeName);
2908
- const outputPath = resolve5(cwd, outputTarget);
3103
+ const outputPath = resolve7(cwd, outputTarget);
2909
3104
  if (fileMap) {
2910
3105
  try {
2911
- mkdirSync4(outputPath, { recursive: true });
3106
+ mkdirSync6(outputPath, { recursive: true });
2912
3107
  let totalBytes = 0;
2913
3108
  for (const [relPath, content] of Object.entries(fileMap.files)) {
2914
- const filePath2 = join12(outputPath, relPath);
2915
- mkdirSync4(dirname6(filePath2), { recursive: true });
2916
- writeFileSync6(filePath2, content, "utf-8");
3109
+ const filePath2 = join15(outputPath, relPath);
3110
+ mkdirSync6(dirname7(filePath2), { recursive: true });
3111
+ writeFileSync8(filePath2, content, "utf-8");
2917
3112
  totalBytes += content.length;
2918
3113
  }
2919
3114
  if (options.json) {
@@ -2950,10 +3145,10 @@ function themeApplyCommand(file, cwd, options) {
2950
3145
  if (css === null) {
2951
3146
  process.exit(1);
2952
3147
  }
2953
- const outputDir = dirname6(outputPath);
3148
+ const outputDir = dirname7(outputPath);
2954
3149
  try {
2955
- mkdirSync4(outputDir, { recursive: true });
2956
- writeFileSync6(outputPath, css, "utf-8");
3150
+ mkdirSync6(outputDir, { recursive: true });
3151
+ writeFileSync8(outputPath, css, "utf-8");
2957
3152
  } catch {
2958
3153
  if (options.json) {
2959
3154
  console.log(
@@ -2994,8 +3189,8 @@ function formatSize(bytes) {
2994
3189
  }
2995
3190
 
2996
3191
  // src/commands/theme-export.ts
2997
- import { readFileSync as readFileSync13 } from "fs";
2998
- import { resolve as resolve6 } from "path";
3192
+ import { readFileSync as readFileSync14 } from "fs";
3193
+ import { resolve as resolve8 } from "path";
2999
3194
  import {
3000
3195
  parseConfig,
3001
3196
  resolveConfig,
@@ -3003,10 +3198,10 @@ import {
3003
3198
  exportTheme
3004
3199
  } from "@loworbitstudio/visor-theme-engine";
3005
3200
  function themeExportCommand(file, cwd, options) {
3006
- const filePath = resolve6(cwd, file ?? ".visor.yaml");
3201
+ const filePath = resolve8(cwd, file ?? ".visor.yaml");
3007
3202
  let yamlContent;
3008
3203
  try {
3009
- yamlContent = readFileSync13(filePath, "utf-8");
3204
+ yamlContent = readFileSync14(filePath, "utf-8");
3010
3205
  } catch {
3011
3206
  if (options.json) {
3012
3207
  console.log(
@@ -3058,16 +3253,16 @@ function themeExportCommand(file, cwd, options) {
3058
3253
  }
3059
3254
 
3060
3255
  // src/commands/theme-validate.ts
3061
- import { readFileSync as readFileSync14 } from "fs";
3062
- import { resolve as resolve7 } from "path";
3256
+ import { readFileSync as readFileSync15 } from "fs";
3257
+ import { resolve as resolve9 } from "path";
3063
3258
  import { parse as parseYaml2 } from "yaml";
3064
3259
  import { validate } from "@loworbitstudio/visor-theme-engine";
3065
3260
  import pc3 from "picocolors";
3066
3261
  function themeValidateCommand(file, cwd, options) {
3067
- const filePath = resolve7(cwd, file);
3262
+ const filePath = resolve9(cwd, file);
3068
3263
  let fileContent;
3069
3264
  try {
3070
- fileContent = readFileSync14(filePath, "utf-8");
3265
+ fileContent = readFileSync15(filePath, "utf-8");
3071
3266
  } catch {
3072
3267
  if (options.json) {
3073
3268
  console.log(
@@ -3156,8 +3351,8 @@ function printIssue(issue) {
3156
3351
 
3157
3352
  // src/commands/theme-verify.ts
3158
3353
  import { spawnSync as _spawnSync } from "child_process";
3159
- import { existsSync as existsSync10 } from "fs";
3160
- import { resolve as resolve8 } from "path";
3354
+ import { existsSync as existsSync12 } from "fs";
3355
+ import { resolve as resolve10 } from "path";
3161
3356
  function themeVerifyCommand(dir, cwd, options, _spawnFn = _spawnSync) {
3162
3357
  const target = options.target ?? "flutter";
3163
3358
  if (target !== "flutter") {
@@ -3179,8 +3374,8 @@ function themeVerifyCommand(dir, cwd, options, _spawnFn = _spawnSync) {
3179
3374
  }
3180
3375
  process.exit(1);
3181
3376
  }
3182
- const dirPath = resolve8(cwd, dir);
3183
- if (!existsSync10(dirPath)) {
3377
+ const dirPath = resolve10(cwd, dir);
3378
+ if (!existsSync12(dirPath)) {
3184
3379
  if (options.json) {
3185
3380
  console.log(
3186
3381
  JSON.stringify({
@@ -3267,8 +3462,8 @@ function themeVerifyCommand(dir, cwd, options, _spawnFn = _spawnSync) {
3267
3462
  }
3268
3463
 
3269
3464
  // src/commands/theme-extract.ts
3270
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync7, existsSync as existsSync11, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
3271
- import { resolve as resolve9, join as join13, basename as basename3, extname as extname3, relative } from "path";
3465
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync9, existsSync as existsSync13, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
3466
+ import { resolve as resolve11, join as join16, basename as basename3, extname as extname3, relative } from "path";
3272
3467
  import { stringify as stringifyYaml } from "yaml";
3273
3468
  import {
3274
3469
  extractFromCSS,
@@ -3297,8 +3492,8 @@ var CSS_DIRS = [
3297
3492
  "packages/design-tokens"
3298
3493
  ];
3299
3494
  function themeExtractCommand(cwd, options) {
3300
- const targetDir = resolve9(cwd, options.from ?? ".");
3301
- if (!existsSync11(targetDir)) {
3495
+ const targetDir = resolve11(cwd, options.from ?? ".");
3496
+ if (!existsSync13(targetDir)) {
3302
3497
  if (options.json) {
3303
3498
  console.log(JSON.stringify({ success: false, error: `Directory not found: ${targetDir}` }));
3304
3499
  } else {
@@ -3352,16 +3547,16 @@ function collectCSSFiles(targetDir) {
3352
3547
  const files = [];
3353
3548
  const seen = /* @__PURE__ */ new Set();
3354
3549
  for (const pattern2 of CSS_FILE_PATTERNS) {
3355
- const rootPath = join13(targetDir, pattern2);
3550
+ const rootPath = join16(targetDir, pattern2);
3356
3551
  addFileIfExists(rootPath, files, seen);
3357
3552
  for (const dir of CSS_DIRS) {
3358
- const dirPath = join13(targetDir, dir, pattern2);
3553
+ const dirPath = join16(targetDir, dir, pattern2);
3359
3554
  addFileIfExists(dirPath, files, seen);
3360
3555
  }
3361
3556
  }
3362
3557
  for (const dir of CSS_DIRS) {
3363
- const dirPath = join13(targetDir, dir);
3364
- if (existsSync11(dirPath) && statSync5(dirPath).isDirectory()) {
3558
+ const dirPath = join16(targetDir, dir);
3559
+ if (existsSync13(dirPath) && statSync5(dirPath).isDirectory()) {
3365
3560
  scanDirForCSS(dirPath, files, seen, 2);
3366
3561
  }
3367
3562
  }
@@ -3369,11 +3564,11 @@ function collectCSSFiles(targetDir) {
3369
3564
  return files;
3370
3565
  }
3371
3566
  function addFileIfExists(filePath, files, seen) {
3372
- const resolved = resolve9(filePath);
3567
+ const resolved = resolve11(filePath);
3373
3568
  if (seen.has(resolved)) return;
3374
- if (!existsSync11(resolved)) return;
3569
+ if (!existsSync13(resolved)) return;
3375
3570
  try {
3376
- const content = readFileSync15(resolved, "utf-8");
3571
+ const content = readFileSync16(resolved, "utf-8");
3377
3572
  if (content.includes("--")) {
3378
3573
  files.push({ path: resolved, content });
3379
3574
  seen.add(resolved);
@@ -3382,7 +3577,7 @@ function addFileIfExists(filePath, files, seen) {
3382
3577
  }
3383
3578
  }
3384
3579
  function scanDirForCSS(dir, files, seen, maxDepth) {
3385
- if (!existsSync11(dir)) return;
3580
+ if (!existsSync13(dir)) return;
3386
3581
  const SKIP_DIRS = /* @__PURE__ */ new Set([
3387
3582
  "node_modules",
3388
3583
  ".next",
@@ -3401,10 +3596,10 @@ function scanDirForCSS(dir, files, seen, maxDepth) {
3401
3596
  if (entry.isDirectory()) {
3402
3597
  if (SKIP_DIRS.has(entry.name)) continue;
3403
3598
  if (maxDepth > 0) {
3404
- scanDirForCSS(join13(dir, entry.name), files, seen, maxDepth - 1);
3599
+ scanDirForCSS(join16(dir, entry.name), files, seen, maxDepth - 1);
3405
3600
  }
3406
3601
  } else if (entry.isFile() && extname3(entry.name) === ".css") {
3407
- addFileIfExists(join13(dir, entry.name), files, seen);
3602
+ addFileIfExists(join16(dir, entry.name), files, seen);
3408
3603
  }
3409
3604
  }
3410
3605
  } catch {
@@ -3486,10 +3681,10 @@ function extractVarName(varExpr) {
3486
3681
  function parseNextFontFromLayouts(targetDir) {
3487
3682
  const fontMap = /* @__PURE__ */ new Map();
3488
3683
  for (const relPath of LAYOUT_FILE_PATHS) {
3489
- const fullPath = join13(targetDir, relPath);
3490
- if (!existsSync11(fullPath)) continue;
3684
+ const fullPath = join16(targetDir, relPath);
3685
+ if (!existsSync13(fullPath)) continue;
3491
3686
  try {
3492
- const content = readFileSync15(fullPath, "utf-8");
3687
+ const content = readFileSync16(fullPath, "utf-8");
3493
3688
  parseNextFontDeclarations(content, fontMap);
3494
3689
  } catch {
3495
3690
  }
@@ -3574,10 +3769,10 @@ var MONO_FONT_NAMES = /* @__PURE__ */ new Set([
3574
3769
  "IBM Plex Mono"
3575
3770
  ]);
3576
3771
  function extractFontHints(targetDir) {
3577
- const pkgPath = join13(targetDir, "package.json");
3578
- if (!existsSync11(pkgPath)) return void 0;
3772
+ const pkgPath = join16(targetDir, "package.json");
3773
+ if (!existsSync13(pkgPath)) return void 0;
3579
3774
  try {
3580
- const pkg2 = JSON.parse(readFileSync15(pkgPath, "utf-8"));
3775
+ const pkg2 = JSON.parse(readFileSync16(pkgPath, "utf-8"));
3581
3776
  const allDeps = { ...pkg2.dependencies, ...pkg2.devDependencies };
3582
3777
  const fonts2 = [];
3583
3778
  for (const [dep, _] of Object.entries(allDeps)) {
@@ -3613,10 +3808,10 @@ function extractFontHints(targetDir) {
3613
3808
  }
3614
3809
  }
3615
3810
  function inferThemeName(targetDir) {
3616
- const pkgPath = join13(targetDir, "package.json");
3617
- if (existsSync11(pkgPath)) {
3811
+ const pkgPath = join16(targetDir, "package.json");
3812
+ if (existsSync13(pkgPath)) {
3618
3813
  try {
3619
- const pkg2 = JSON.parse(readFileSync15(pkgPath, "utf-8"));
3814
+ const pkg2 = JSON.parse(readFileSync16(pkgPath, "utf-8"));
3620
3815
  if (pkg2.name) {
3621
3816
  const name = pkg2.name.replace(/^@[\w-]+\//, "");
3622
3817
  return `${name}-theme`;
@@ -3653,7 +3848,7 @@ function outputJSON(result, validationResult) {
3653
3848
  }
3654
3849
  function outputYAML(result, outputPath, cwd, validationResult) {
3655
3850
  const yamlStr = buildAnnotatedYAML(result);
3656
- const outFile = resolve9(cwd, outputPath ?? ".visor.yaml");
3851
+ const outFile = resolve11(cwd, outputPath ?? ".visor.yaml");
3657
3852
  const high = result.tokens.filter((t) => t.confidence === "high").length;
3658
3853
  const med = result.tokens.filter((t) => t.confidence === "medium").length;
3659
3854
  const low = result.tokens.filter((t) => t.confidence === "low").length;
@@ -3681,7 +3876,7 @@ function outputYAML(result, outputPath, cwd, validationResult) {
3681
3876
  }
3682
3877
  logger.blank();
3683
3878
  }
3684
- writeFileSync7(outFile, yamlStr, "utf-8");
3879
+ writeFileSync9(outFile, yamlStr, "utf-8");
3685
3880
  logger.success(`Theme written to ${relative(cwd, outFile)}`);
3686
3881
  if (validationResult) {
3687
3882
  logger.blank();
@@ -3739,14 +3934,14 @@ function buildAnnotatedYAML(result) {
3739
3934
  }
3740
3935
 
3741
3936
  // src/commands/theme-register.ts
3742
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5, existsSync as existsSync13 } from "fs";
3743
- import { resolve as resolve11, join as join15 } from "path";
3937
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync10, mkdirSync as mkdirSync7, existsSync as existsSync15 } from "fs";
3938
+ import { resolve as resolve13, join as join18 } from "path";
3744
3939
  import { generateThemeData as generateThemeData4 } from "@loworbitstudio/visor-theme-engine";
3745
3940
  import { docsAdapter as docsAdapter2 } from "@loworbitstudio/visor-theme-engine/adapters";
3746
3941
 
3747
3942
  // src/utils/theme-helpers.ts
3748
- import { existsSync as existsSync12, lstatSync, readdirSync as readdirSync5, readFileSync as readFileSync16, readlinkSync, realpathSync, statSync as statSync6 } from "fs";
3749
- import { resolve as resolve10, dirname as dirname7, join as join14 } from "path";
3943
+ import { existsSync as existsSync14, lstatSync, readdirSync as readdirSync5, readFileSync as readFileSync17, readlinkSync, realpathSync, statSync as statSync6 } from "fs";
3944
+ import { resolve as resolve12, dirname as dirname8, join as join17 } from "path";
3750
3945
  import { execFileSync as execFileSync3 } from "child_process";
3751
3946
  function toSlug(name) {
3752
3947
  return name.toLowerCase().replace(/\s+/g, "-");
@@ -3755,12 +3950,12 @@ function toLabel(name) {
3755
3950
  return name.split(/[\s-]+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
3756
3951
  }
3757
3952
  function findRepoRoot(startDir) {
3758
- let current = resolve10(startDir);
3953
+ let current = resolve12(startDir);
3759
3954
  while (true) {
3760
- if (existsSync12(join14(current, "packages", "docs"))) {
3955
+ if (existsSync14(join17(current, "packages", "docs"))) {
3761
3956
  return current;
3762
3957
  }
3763
- const parent = dirname7(current);
3958
+ const parent = dirname8(current);
3764
3959
  if (parent === current) break;
3765
3960
  current = parent;
3766
3961
  }
@@ -3774,9 +3969,9 @@ function findMainRepoRoot(startDir) {
3774
3969
  stdio: ["ignore", "pipe", "ignore"]
3775
3970
  }).trim();
3776
3971
  if (commonDir) {
3777
- const absoluteCommonDir = resolve10(startDir, commonDir);
3778
- const candidate = dirname7(absoluteCommonDir);
3779
- if (existsSync12(join14(candidate, "packages", "docs"))) {
3972
+ const absoluteCommonDir = resolve12(startDir, commonDir);
3973
+ const candidate = dirname8(absoluteCommonDir);
3974
+ if (existsSync14(join17(candidate, "packages", "docs"))) {
3780
3975
  return candidate;
3781
3976
  }
3782
3977
  }
@@ -3795,10 +3990,10 @@ var BrokenSymlinkError = class extends Error {
3795
3990
  code = "BROKEN_SYMLINK";
3796
3991
  };
3797
3992
  function assertNoBrokenSymlinks(dir) {
3798
- if (!existsSync12(dir)) return;
3993
+ if (!existsSync14(dir)) return;
3799
3994
  const entries = readdirSync5(dir, { withFileTypes: true });
3800
3995
  for (const entry of entries) {
3801
- const entryPath = join14(dir, entry.name);
3996
+ const entryPath = join17(dir, entry.name);
3802
3997
  let lst;
3803
3998
  try {
3804
3999
  lst = lstatSync(entryPath);
@@ -3816,39 +4011,39 @@ function assertNoBrokenSymlinks(dir) {
3816
4011
  }
3817
4012
  }
3818
4013
  function scanParentForPrivateThemes(parentDir) {
3819
- if (!existsSync12(parentDir)) return [];
4014
+ if (!existsSync14(parentDir)) return [];
3820
4015
  const entries = readdirSync5(parentDir, { withFileTypes: true });
3821
4016
  const matches = [];
3822
4017
  for (const entry of entries) {
3823
4018
  if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
3824
- const candidate = join14(parentDir, entry.name, "visor-themes-private", "themes");
3825
- if (existsSync12(candidate)) {
4019
+ const candidate = join17(parentDir, entry.name, "visor-themes-private", "themes");
4020
+ if (existsSync14(candidate)) {
3826
4021
  matches.push(candidate);
3827
4022
  }
3828
4023
  }
3829
4024
  return matches.sort();
3830
4025
  }
3831
4026
  function scanNestedThemeDir(dir) {
3832
- if (!existsSync12(dir)) return [];
4027
+ if (!existsSync14(dir)) return [];
3833
4028
  assertNoBrokenSymlinks(dir);
3834
4029
  const entries = readdirSync5(dir, { withFileTypes: true });
3835
4030
  const out = [];
3836
4031
  for (const entry of entries) {
3837
4032
  if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
3838
- const themeFile = join14(dir, entry.name, "theme.visor.yaml");
3839
- if (existsSync12(themeFile)) {
4033
+ const themeFile = join17(dir, entry.name, "theme.visor.yaml");
4034
+ if (existsSync14(themeFile)) {
3840
4035
  out.push({ filePath: themeFile, slug: entry.name });
3841
4036
  }
3842
4037
  }
3843
4038
  return out;
3844
4039
  }
3845
4040
  function detectVisorWorkspace(cwd) {
3846
- let current = resolve10(cwd);
4041
+ let current = resolve12(cwd);
3847
4042
  while (true) {
3848
- const pkgPath = join14(current, "package.json");
3849
- if (existsSync12(pkgPath)) {
4043
+ const pkgPath = join17(current, "package.json");
4044
+ if (existsSync14(pkgPath)) {
3850
4045
  try {
3851
- const pkg2 = JSON.parse(readFileSync16(pkgPath, "utf-8"));
4046
+ const pkg2 = JSON.parse(readFileSync17(pkgPath, "utf-8"));
3852
4047
  const workspaces = pkg2.workspaces ?? [];
3853
4048
  const hasCli = workspaces.some((w) => w.includes("packages/cli"));
3854
4049
  const hasEngine = workspaces.some((w) => w.includes("packages/theme-engine"));
@@ -3858,7 +4053,7 @@ function detectVisorWorkspace(cwd) {
3858
4053
  } catch {
3859
4054
  }
3860
4055
  }
3861
- const parent = dirname7(current);
4056
+ const parent = dirname8(current);
3862
4057
  if (parent === current) break;
3863
4058
  current = parent;
3864
4059
  }
@@ -3866,18 +4061,18 @@ function detectVisorWorkspace(cwd) {
3866
4061
  }
3867
4062
  function isLocalVisorBinary(workspaceRoot, scriptPath) {
3868
4063
  if (!scriptPath) return false;
3869
- const expectedPrefix = join14(workspaceRoot, "packages", "cli", "dist");
4064
+ const expectedPrefix = join17(workspaceRoot, "packages", "cli", "dist");
3870
4065
  let resolvedScript;
3871
4066
  let resolvedPrefix;
3872
4067
  try {
3873
4068
  resolvedScript = realpathSync(scriptPath);
3874
4069
  } catch {
3875
- resolvedScript = resolve10(scriptPath);
4070
+ resolvedScript = resolve12(scriptPath);
3876
4071
  }
3877
4072
  try {
3878
4073
  resolvedPrefix = realpathSync(expectedPrefix);
3879
4074
  } catch {
3880
- resolvedPrefix = resolve10(expectedPrefix);
4075
+ resolvedPrefix = resolve12(expectedPrefix);
3881
4076
  }
3882
4077
  return resolvedScript.startsWith(resolvedPrefix + "/") || resolvedScript === resolvedPrefix;
3883
4078
  }
@@ -3967,10 +4162,10 @@ ${indent}${newEntry},
3967
4162
  return { updated, changed: true };
3968
4163
  }
3969
4164
  function themeRegisterCommand(file, cwd, options) {
3970
- const filePath = resolve11(cwd, file);
4165
+ const filePath = resolve13(cwd, file);
3971
4166
  let yamlContent;
3972
4167
  try {
3973
- yamlContent = readFileSync17(filePath, "utf-8");
4168
+ yamlContent = readFileSync18(filePath, "utf-8");
3974
4169
  } catch {
3975
4170
  if (options.json) {
3976
4171
  console.log(JSON.stringify({ success: false, error: `Could not read file: ${filePath}` }));
@@ -4013,11 +4208,11 @@ function themeRegisterCommand(file, cwd, options) {
4013
4208
  process.exit(1);
4014
4209
  return;
4015
4210
  }
4016
- const docsAppDir = join15(repoRoot, "packages", "docs", "app");
4017
- const cssFilePath = join15(docsAppDir, `${slug2}-theme.css`);
4018
- const globalsPath = join15(docsAppDir, "globals.css");
4019
- const themeConfigPath = join15(repoRoot, "packages", "docs", "lib", "theme-config.ts");
4020
- if (!existsSync13(docsAppDir)) {
4211
+ const docsAppDir = join18(repoRoot, "packages", "docs", "app");
4212
+ const cssFilePath = join18(docsAppDir, `${slug2}-theme.css`);
4213
+ const globalsPath = join18(docsAppDir, "globals.css");
4214
+ const themeConfigPath = join18(repoRoot, "packages", "docs", "lib", "theme-config.ts");
4215
+ if (!existsSync15(docsAppDir)) {
4021
4216
  const msg = `Docs app directory not found: ${docsAppDir}`;
4022
4217
  if (options.json) {
4023
4218
  console.log(JSON.stringify({ success: false, error: msg }));
@@ -4030,8 +4225,8 @@ function themeRegisterCommand(file, cwd, options) {
4030
4225
  let globalsContent = "";
4031
4226
  let themeConfigContent = "";
4032
4227
  try {
4033
- globalsContent = readFileSync17(globalsPath, "utf-8");
4034
- themeConfigContent = readFileSync17(themeConfigPath, "utf-8");
4228
+ globalsContent = readFileSync18(globalsPath, "utf-8");
4229
+ themeConfigContent = readFileSync18(themeConfigPath, "utf-8");
4035
4230
  } catch (err) {
4036
4231
  const msg = err instanceof Error ? err.message : "Could not read docs files";
4037
4232
  if (options.json) {
@@ -4042,8 +4237,8 @@ function themeRegisterCommand(file, cwd, options) {
4042
4237
  process.exit(1);
4043
4238
  return;
4044
4239
  }
4045
- const cssExists = existsSync13(cssFilePath);
4046
- const cssChanged = !cssExists || readFileSync17(cssFilePath, "utf-8") !== css;
4240
+ const cssExists = existsSync15(cssFilePath);
4241
+ const cssChanged = !cssExists || readFileSync18(cssFilePath, "utf-8") !== css;
4047
4242
  const { updated: newGlobals, changed: globalsChanged } = insertGlobalsImport(globalsContent, slug2);
4048
4243
  const { updated: newThemeConfig, changed: themeConfigChanged, error: configError } = insertThemeConfig(
4049
4244
  themeConfigContent,
@@ -4086,14 +4281,14 @@ function themeRegisterCommand(file, cwd, options) {
4086
4281
  }
4087
4282
  try {
4088
4283
  if (cssChanged) {
4089
- mkdirSync5(docsAppDir, { recursive: true });
4090
- writeFileSync8(cssFilePath, css, "utf-8");
4284
+ mkdirSync7(docsAppDir, { recursive: true });
4285
+ writeFileSync10(cssFilePath, css, "utf-8");
4091
4286
  }
4092
4287
  if (globalsChanged) {
4093
- writeFileSync8(globalsPath, newGlobals, "utf-8");
4288
+ writeFileSync10(globalsPath, newGlobals, "utf-8");
4094
4289
  }
4095
4290
  if (themeConfigChanged) {
4096
- writeFileSync8(themeConfigPath, newThemeConfig, "utf-8");
4291
+ writeFileSync10(themeConfigPath, newThemeConfig, "utf-8");
4097
4292
  }
4098
4293
  } catch (err) {
4099
4294
  const msg = err instanceof Error ? err.message : "Write failed";
@@ -4127,8 +4322,8 @@ function themeRegisterCommand(file, cwd, options) {
4127
4322
  }
4128
4323
 
4129
4324
  // src/commands/theme-unregister.ts
4130
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync9, existsSync as existsSync14, unlinkSync } from "fs";
4131
- import { join as join16 } from "path";
4325
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync11, existsSync as existsSync16, unlinkSync } from "fs";
4326
+ import { join as join19 } from "path";
4132
4327
  function removeGlobalsImport(content, slug2) {
4133
4328
  const importLine = `@import './${slug2}-theme.css';`;
4134
4329
  if (!content.includes(importLine)) {
@@ -4160,11 +4355,11 @@ function themeUnregisterCommand(slug2, cwd, options) {
4160
4355
  process.exit(1);
4161
4356
  return;
4162
4357
  }
4163
- const docsAppDir = join16(repoRoot, "packages", "docs", "app");
4164
- const cssFilePath = join16(docsAppDir, `${slug2}-theme.css`);
4165
- const globalsPath = join16(docsAppDir, "globals.css");
4166
- const themeConfigPath = join16(repoRoot, "packages", "docs", "lib", "theme-config.ts");
4167
- if (!existsSync14(docsAppDir)) {
4358
+ const docsAppDir = join19(repoRoot, "packages", "docs", "app");
4359
+ const cssFilePath = join19(docsAppDir, `${slug2}-theme.css`);
4360
+ const globalsPath = join19(docsAppDir, "globals.css");
4361
+ const themeConfigPath = join19(repoRoot, "packages", "docs", "lib", "theme-config.ts");
4362
+ if (!existsSync16(docsAppDir)) {
4168
4363
  const msg = `Docs app directory not found: ${docsAppDir}`;
4169
4364
  if (options.json) {
4170
4365
  console.log(JSON.stringify({ success: false, error: msg }));
@@ -4177,8 +4372,8 @@ function themeUnregisterCommand(slug2, cwd, options) {
4177
4372
  let globalsContent = "";
4178
4373
  let themeConfigContent = "";
4179
4374
  try {
4180
- globalsContent = readFileSync18(globalsPath, "utf-8");
4181
- themeConfigContent = readFileSync18(themeConfigPath, "utf-8");
4375
+ globalsContent = readFileSync19(globalsPath, "utf-8");
4376
+ themeConfigContent = readFileSync19(themeConfigPath, "utf-8");
4182
4377
  } catch (err) {
4183
4378
  const msg = err instanceof Error ? err.message : "Could not read docs files";
4184
4379
  if (options.json) {
@@ -4189,7 +4384,7 @@ function themeUnregisterCommand(slug2, cwd, options) {
4189
4384
  process.exit(1);
4190
4385
  return;
4191
4386
  }
4192
- const cssExists = existsSync14(cssFilePath);
4387
+ const cssExists = existsSync16(cssFilePath);
4193
4388
  const { updated: newGlobals, changed: globalsChanged } = removeGlobalsImport(globalsContent, slug2);
4194
4389
  const { updated: newThemeConfig, changed: themeConfigChanged } = removeThemeConfigEntry(themeConfigContent, slug2);
4195
4390
  if (!cssExists && !globalsChanged && !themeConfigChanged) {
@@ -4202,8 +4397,8 @@ function themeUnregisterCommand(slug2, cwd, options) {
4202
4397
  }
4203
4398
  try {
4204
4399
  if (cssExists) unlinkSync(cssFilePath);
4205
- if (globalsChanged) writeFileSync9(globalsPath, newGlobals, "utf-8");
4206
- if (themeConfigChanged) writeFileSync9(themeConfigPath, newThemeConfig, "utf-8");
4400
+ if (globalsChanged) writeFileSync11(globalsPath, newGlobals, "utf-8");
4401
+ if (themeConfigChanged) writeFileSync11(themeConfigPath, newThemeConfig, "utf-8");
4207
4402
  } catch (err) {
4208
4403
  const msg = err instanceof Error ? err.message : "Write failed";
4209
4404
  if (options.json) {
@@ -4230,15 +4425,15 @@ function themeUnregisterCommand(slug2, cwd, options) {
4230
4425
 
4231
4426
  // src/commands/theme-sync.ts
4232
4427
  import {
4233
- readFileSync as readFileSync19,
4234
- writeFileSync as writeFileSync10,
4235
- mkdirSync as mkdirSync6,
4236
- existsSync as existsSync15,
4428
+ readFileSync as readFileSync20,
4429
+ writeFileSync as writeFileSync12,
4430
+ mkdirSync as mkdirSync8,
4431
+ existsSync as existsSync17,
4237
4432
  readdirSync as readdirSync6,
4238
4433
  unlinkSync as unlinkSync2,
4239
4434
  copyFileSync
4240
4435
  } from "fs";
4241
- import { join as join17, basename as basename4, resolve as resolve12, sep, relative as relative2 } from "path";
4436
+ import { join as join20, basename as basename4, resolve as resolve14, sep, relative as relative2 } from "path";
4242
4437
  import { parse as parseYaml3 } from "yaml";
4243
4438
  import { generateThemeData as generateThemeData5, validateFontCoverage, formatFontCoverageError } from "@loworbitstudio/visor-theme-engine";
4244
4439
  import { docsAdapter as docsAdapter3 } from "@loworbitstudio/visor-theme-engine/adapters";
@@ -4252,9 +4447,9 @@ var CUSTOM_OVERLAY_CSS_PATH = "packages/docs/app/custom-themes.generated.css";
4252
4447
  var CUSTOM_OVERLAY_TS_PATH = "packages/docs/lib/theme-config.custom.generated.ts";
4253
4448
  var CUSTOM_OVERLAY_IMPORT_LINE = "@import './custom-themes.generated.css';";
4254
4449
  function scanThemeDir(dir) {
4255
- if (!existsSync15(dir)) return [];
4450
+ if (!existsSync17(dir)) return [];
4256
4451
  assertNoBrokenSymlinks(dir);
4257
- return readdirSync6(dir).filter((f) => f.endsWith(".visor.yaml")).map((f) => join17(dir, f));
4452
+ return readdirSync6(dir).filter((f) => f.endsWith(".visor.yaml")).map((f) => join20(dir, f));
4258
4453
  }
4259
4454
  function resolveCustomSources(repoRoot, mainRepoRoot, warn) {
4260
4455
  const merged = /* @__PURE__ */ new Map();
@@ -4278,20 +4473,20 @@ function resolveCustomSources(repoRoot, mainRepoRoot, warn) {
4278
4473
  };
4279
4474
  const envPath = process.env[PRIVATE_THEMES_ENV_VAR];
4280
4475
  if (envPath && envPath.trim() !== "") {
4281
- const resolved = resolve12(envPath);
4282
- if (!existsSync15(resolved)) {
4476
+ const resolved = resolve14(envPath);
4477
+ if (!existsSync17(resolved)) {
4283
4478
  throw new Error(
4284
4479
  `${PRIVATE_THEMES_ENV_VAR} is set to "${envPath}" but the path does not exist. Expected a directory containing {slug}/theme.visor.yaml entries.`
4285
4480
  );
4286
4481
  }
4287
4482
  addNested(resolved, "env");
4288
4483
  }
4289
- const siblingPath = join17(mainRepoRoot, "..", "visor-themes-private", "themes");
4290
- const siblingExists = existsSync15(siblingPath);
4484
+ const siblingPath = join20(mainRepoRoot, "..", "visor-themes-private", "themes");
4485
+ const siblingExists = existsSync17(siblingPath);
4291
4486
  if (siblingExists) {
4292
4487
  addNested(siblingPath, "sibling");
4293
4488
  }
4294
- const parentDir = join17(mainRepoRoot, "..");
4489
+ const parentDir = join20(mainRepoRoot, "..");
4295
4490
  const parentGlobMatches = scanParentForPrivateThemes(parentDir).filter(
4296
4491
  (path2) => !path2.startsWith(mainRepoRoot + sep)
4297
4492
  );
@@ -4312,7 +4507,7 @@ function resolveCustomSources(repoRoot, mainRepoRoot, warn) {
4312
4507
  }
4313
4508
  }
4314
4509
  }
4315
- const legacyDir = join17(repoRoot, "custom-themes");
4510
+ const legacyDir = join20(repoRoot, "custom-themes");
4316
4511
  const legacyFiles = scanThemeDir(legacyDir);
4317
4512
  for (const legacyFile of legacyFiles) {
4318
4513
  const slug2 = basename4(legacyFile).replace(/\.visor\.yaml$/, "");
@@ -4369,14 +4564,14 @@ function reportBrokenSymlink(err, options) {
4369
4564
  }
4370
4565
  }
4371
4566
  function buildEmptySourcesMessage(mainRepoRoot) {
4372
- const expectedSibling = join17(mainRepoRoot, "..", "visor-themes-private");
4567
+ const expectedSibling = join20(mainRepoRoot, "..", "visor-themes-private");
4373
4568
  return [
4374
4569
  "No theme sources discovered. Cannot proceed \u2014 refusing to wipe generated CSS.",
4375
4570
  "",
4376
4571
  "Resolution order checked:",
4377
4572
  ` 1. Env var ${PRIVATE_THEMES_ENV_VAR} (unset or empty)`,
4378
4573
  ` 2. Sibling checkout at ${expectedSibling}/themes/ (not found)`,
4379
- ` 3. One-level-deeper at ${join17(mainRepoRoot, "..")}/<parent>/visor-themes-private/themes/ (not found)`,
4574
+ ` 3. One-level-deeper at ${join20(mainRepoRoot, "..")}/<parent>/visor-themes-private/themes/ (not found)`,
4380
4575
  " 4. Legacy custom-themes/ (no .visor.yaml files)",
4381
4576
  "",
4382
4577
  "To fix, clone the private themes repo as a sibling:",
@@ -4581,14 +4776,14 @@ function themeSyncCommand(cwd, options) {
4581
4776
  return;
4582
4777
  }
4583
4778
  const mainRepoRoot = findMainRepoRoot(cwd) ?? repoRoot;
4584
- const themesDir = join17(repoRoot, "themes");
4585
- const docsAppDir = join17(repoRoot, "packages", "docs", "app");
4586
- const docsLibDir = join17(repoRoot, "packages", "docs", "lib");
4587
- const docsPublicThemesDir = join17(repoRoot, "packages", "docs", "public", "themes");
4588
- const themeConfigPath = join17(repoRoot, "packages", "docs", "lib", "theme-config.ts");
4589
- const globalsPath = join17(docsAppDir, "globals.css");
4590
- const customOverlayCssPath = join17(repoRoot, CUSTOM_OVERLAY_CSS_PATH);
4591
- const customOverlayTsPath = join17(repoRoot, CUSTOM_OVERLAY_TS_PATH);
4779
+ const themesDir = join20(repoRoot, "themes");
4780
+ const docsAppDir = join20(repoRoot, "packages", "docs", "app");
4781
+ const docsLibDir = join20(repoRoot, "packages", "docs", "lib");
4782
+ const docsPublicThemesDir = join20(repoRoot, "packages", "docs", "public", "themes");
4783
+ const themeConfigPath = join20(repoRoot, "packages", "docs", "lib", "theme-config.ts");
4784
+ const globalsPath = join20(docsAppDir, "globals.css");
4785
+ const customOverlayCssPath = join20(repoRoot, CUSTOM_OVERLAY_CSS_PATH);
4786
+ const customOverlayTsPath = join20(repoRoot, CUSTOM_OVERLAY_TS_PATH);
4592
4787
  let stockFiles;
4593
4788
  try {
4594
4789
  stockFiles = scanThemeDir(themesDir);
@@ -4645,7 +4840,7 @@ function themeSyncCommand(cwd, options) {
4645
4840
  const processFile = (filePath, isCustom, slugOverride) => {
4646
4841
  let yamlContent;
4647
4842
  try {
4648
- yamlContent = readFileSync19(filePath, "utf-8");
4843
+ yamlContent = readFileSync20(filePath, "utf-8");
4649
4844
  } catch {
4650
4845
  failures.push({ filePath, error: `Could not read: ${filePath}` });
4651
4846
  return;
@@ -4695,8 +4890,8 @@ function themeSyncCommand(cwd, options) {
4695
4890
  let globalsContent;
4696
4891
  let themeConfigContent;
4697
4892
  try {
4698
- globalsContent = readFileSync19(globalsPath, "utf-8");
4699
- themeConfigContent = readFileSync19(themeConfigPath, "utf-8");
4893
+ globalsContent = readFileSync20(globalsPath, "utf-8");
4894
+ themeConfigContent = readFileSync20(themeConfigPath, "utf-8");
4700
4895
  } catch (err) {
4701
4896
  const msg = err instanceof Error ? err.message : "Could not read docs files";
4702
4897
  if (options.json) {
@@ -4711,12 +4906,12 @@ function themeSyncCommand(cwd, options) {
4711
4906
  const newThemeConfig = updateStockThemeConfigBlock(themeConfigContent, stockManifest);
4712
4907
  const newCustomOverlayCss = generateCustomOverlayCss(customManifest);
4713
4908
  const newCustomOverlayTs = generateCustomOverlayTs(customManifest);
4714
- const existingCssFiles = existsSync15(docsAppDir) ? readdirSync6(docsAppDir).filter(
4909
+ const existingCssFiles = existsSync17(docsAppDir) ? readdirSync6(docsAppDir).filter(
4715
4910
  (f) => f.endsWith("-theme.css") && f !== "custom-themes.generated.css"
4716
4911
  ) : [];
4717
4912
  const newCssSet = new Set(allSlugs.map((s) => `${s}-theme.css`));
4718
4913
  const staleCssFiles = existingCssFiles.filter((f) => !newCssSet.has(f));
4719
- const existingPublicYamls = existsSync15(docsPublicThemesDir) ? readdirSync6(docsPublicThemesDir).filter((f) => f.endsWith(".visor.yaml")) : [];
4914
+ const existingPublicYamls = existsSync17(docsPublicThemesDir) ? readdirSync6(docsPublicThemesDir).filter((f) => f.endsWith(".visor.yaml")) : [];
4720
4915
  const newPublicYamlSet = new Set(manifest.map((e) => `${e.yamlFilename}.visor.yaml`));
4721
4916
  const stalePublicYamls = existingPublicYamls.filter((f) => !newPublicYamlSet.has(f));
4722
4917
  if (options.dryRun) {
@@ -4756,28 +4951,28 @@ function themeSyncCommand(cwd, options) {
4756
4951
  return;
4757
4952
  }
4758
4953
  try {
4759
- mkdirSync6(docsAppDir, { recursive: true });
4760
- mkdirSync6(docsLibDir, { recursive: true });
4761
- mkdirSync6(docsPublicThemesDir, { recursive: true });
4954
+ mkdirSync8(docsAppDir, { recursive: true });
4955
+ mkdirSync8(docsLibDir, { recursive: true });
4956
+ mkdirSync8(docsPublicThemesDir, { recursive: true });
4762
4957
  for (const entry of manifest) {
4763
- writeFileSync10(join17(docsAppDir, `${entry.slug}-theme.css`), entry.css, "utf-8");
4958
+ writeFileSync12(join20(docsAppDir, `${entry.slug}-theme.css`), entry.css, "utf-8");
4764
4959
  }
4765
4960
  for (const stale of staleCssFiles) {
4766
- unlinkSync2(join17(docsAppDir, stale));
4961
+ unlinkSync2(join20(docsAppDir, stale));
4767
4962
  }
4768
- writeFileSync10(customOverlayCssPath, newCustomOverlayCss, "utf-8");
4769
- writeFileSync10(customOverlayTsPath, newCustomOverlayTs, "utf-8");
4770
- writeFileSync10(themeConfigPath, newThemeConfig, "utf-8");
4771
- writeFileSync10(globalsPath, newGlobals, "utf-8");
4963
+ writeFileSync12(customOverlayCssPath, newCustomOverlayCss, "utf-8");
4964
+ writeFileSync12(customOverlayTsPath, newCustomOverlayTs, "utf-8");
4965
+ writeFileSync12(themeConfigPath, newThemeConfig, "utf-8");
4966
+ writeFileSync12(globalsPath, newGlobals, "utf-8");
4772
4967
  for (const srcFile of stockFiles) {
4773
- copyFileSync(srcFile, join17(docsPublicThemesDir, basename4(srcFile)));
4968
+ copyFileSync(srcFile, join20(docsPublicThemesDir, basename4(srcFile)));
4774
4969
  }
4775
4970
  for (const c of customSources) {
4776
4971
  const targetName = c.origin === "legacy" ? basename4(c.filePath) : `${c.slug}.visor.yaml`;
4777
- copyFileSync(c.filePath, join17(docsPublicThemesDir, targetName));
4972
+ copyFileSync(c.filePath, join20(docsPublicThemesDir, targetName));
4778
4973
  }
4779
4974
  for (const stale of stalePublicYamls) {
4780
- unlinkSync2(join17(docsPublicThemesDir, stale));
4975
+ unlinkSync2(join20(docsPublicThemesDir, stale));
4781
4976
  }
4782
4977
  } catch (err) {
4783
4978
  const msg = err instanceof Error ? err.message : "Write failed";
@@ -4830,19 +5025,19 @@ function themeSyncCommand(cwd, options) {
4830
5025
 
4831
5026
  // src/commands/theme-batch-apply-flutter.ts
4832
5027
  import {
4833
- readFileSync as readFileSync20,
4834
- writeFileSync as writeFileSync11,
4835
- mkdirSync as mkdirSync7,
4836
- existsSync as existsSync16,
5028
+ readFileSync as readFileSync21,
5029
+ writeFileSync as writeFileSync13,
5030
+ mkdirSync as mkdirSync9,
5031
+ existsSync as existsSync18,
4837
5032
  readdirSync as readdirSync7,
4838
5033
  rmSync
4839
5034
  } from "fs";
4840
- import { join as join18, basename as basename5, dirname as dirname8 } from "path";
5035
+ import { join as join21, basename as basename5, dirname as dirname9 } from "path";
4841
5036
  import { generateThemeData as generateThemeData6 } from "@loworbitstudio/visor-theme-engine";
4842
5037
  import { flutterAdapter as flutterAdapter2 } from "@loworbitstudio/visor-theme-engine/adapters";
4843
5038
  function scanThemeDir2(dir) {
4844
- if (!existsSync16(dir)) return [];
4845
- return readdirSync7(dir).filter((f) => f.endsWith(".visor.yaml")).map((f) => join18(dir, f)).sort();
5039
+ if (!existsSync18(dir)) return [];
5040
+ return readdirSync7(dir).filter((f) => f.endsWith(".visor.yaml")).map((f) => join21(dir, f)).sort();
4846
5041
  }
4847
5042
  function slugToCamel(slug2) {
4848
5043
  return slug2.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
@@ -4975,9 +5170,9 @@ function themeBatchApplyFlutterCommand(cwd, options) {
4975
5170
  process.exit(1);
4976
5171
  return;
4977
5172
  }
4978
- const themesDir = join18(repoRoot, "themes");
4979
- const customThemesDir = join18(repoRoot, "custom-themes");
4980
- const outputDir = join18(repoRoot, "packages", "visor_themes");
5173
+ const themesDir = join21(repoRoot, "themes");
5174
+ const customThemesDir = join21(repoRoot, "custom-themes");
5175
+ const outputDir = join21(repoRoot, "packages", "visor_themes");
4981
5176
  const stockFiles = scanThemeDir2(themesDir);
4982
5177
  const customFiles = scanThemeDir2(customThemesDir);
4983
5178
  const allFiles = [...stockFiles, ...customFiles];
@@ -4998,7 +5193,7 @@ function themeBatchApplyFlutterCommand(cwd, options) {
4998
5193
  for (const filePath of allFiles) {
4999
5194
  let yamlContent;
5000
5195
  try {
5001
- yamlContent = readFileSync20(filePath, "utf-8");
5196
+ yamlContent = readFileSync21(filePath, "utf-8");
5002
5197
  } catch {
5003
5198
  errors.push(`Could not read: ${filePath}`);
5004
5199
  continue;
@@ -5077,8 +5272,8 @@ function themeBatchApplyFlutterCommand(cwd, options) {
5077
5272
  return;
5078
5273
  }
5079
5274
  const slugs = processed.map((p) => p.slug);
5080
- const libSrcDir = join18(outputDir, "lib", "src");
5081
- if (existsSync16(libSrcDir)) {
5275
+ const libSrcDir = join21(outputDir, "lib", "src");
5276
+ if (existsSync18(libSrcDir)) {
5082
5277
  rmSync(libSrcDir, { recursive: true, force: true });
5083
5278
  }
5084
5279
  const packageFiles = {
@@ -5090,17 +5285,17 @@ function themeBatchApplyFlutterCommand(cwd, options) {
5090
5285
  };
5091
5286
  let totalFiles = 0;
5092
5287
  for (const [relPath, content] of Object.entries(packageFiles)) {
5093
- const absPath = join18(outputDir, relPath);
5094
- mkdirSync7(dirname8(absPath), { recursive: true });
5095
- writeFileSync11(absPath, content, "utf-8");
5288
+ const absPath = join21(outputDir, relPath);
5289
+ mkdirSync9(dirname9(absPath), { recursive: true });
5290
+ writeFileSync13(absPath, content, "utf-8");
5096
5291
  totalFiles++;
5097
5292
  }
5098
5293
  for (const { slug: slug2, tokenFiles } of processed) {
5099
- const themeBaseDir = join18(outputDir, "lib", "src", slug2);
5294
+ const themeBaseDir = join21(outputDir, "lib", "src", slug2);
5100
5295
  for (const [relPath, content] of Object.entries(tokenFiles)) {
5101
- const absPath = join18(themeBaseDir, relPath);
5102
- mkdirSync7(dirname8(absPath), { recursive: true });
5103
- writeFileSync11(absPath, content, "utf-8");
5296
+ const absPath = join21(themeBaseDir, relPath);
5297
+ mkdirSync9(dirname9(absPath), { recursive: true });
5298
+ writeFileSync13(absPath, content, "utf-8");
5104
5299
  totalFiles++;
5105
5300
  }
5106
5301
  }
@@ -5121,8 +5316,8 @@ function themeBatchApplyFlutterCommand(cwd, options) {
5121
5316
  }
5122
5317
 
5123
5318
  // src/commands/fonts-add.ts
5124
- import { existsSync as existsSync17, statSync as statSync7, readdirSync as readdirSync8, readFileSync as readFileSync21 } from "fs";
5125
- import { resolve as resolve13, basename as basename6, extname as extname4 } from "path";
5319
+ import { existsSync as existsSync19, statSync as statSync7, readdirSync as readdirSync8, readFileSync as readFileSync22 } from "fs";
5320
+ import { resolve as resolve15, basename as basename6, extname as extname4 } from "path";
5126
5321
  import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
5127
5322
  function deriveFamilySlug(filename) {
5128
5323
  const name = basename6(filename, extname4(filename));
@@ -5166,8 +5361,8 @@ function deriveFamilySlug(filename) {
5166
5361
  return parts.join("-").toLowerCase();
5167
5362
  }
5168
5363
  function collectWoff2Files(inputPath) {
5169
- const resolved = resolve13(inputPath);
5170
- if (!existsSync17(resolved)) {
5364
+ const resolved = resolve15(inputPath);
5365
+ if (!existsSync19(resolved)) {
5171
5366
  throw new Error(`Path not found: ${resolved}`);
5172
5367
  }
5173
5368
  const stat = statSync7(resolved);
@@ -5180,7 +5375,7 @@ function collectWoff2Files(inputPath) {
5180
5375
  return [resolved];
5181
5376
  }
5182
5377
  if (stat.isDirectory()) {
5183
- const files = readdirSync8(resolved).filter((f) => extname4(f).toLowerCase() === ".woff2").map((f) => resolve13(resolved, f));
5378
+ const files = readdirSync8(resolved).filter((f) => extname4(f).toLowerCase() === ".woff2").map((f) => resolve15(resolved, f));
5184
5379
  if (files.length === 0) {
5185
5380
  throw new Error(
5186
5381
  `No .woff2 files found in directory: ${resolved}`
@@ -5195,8 +5390,8 @@ function collectWoff2Files(inputPath) {
5195
5390
  throw new Error(`Path is neither a file nor a directory: ${resolved}`);
5196
5391
  }
5197
5392
  function getNonWoff2Fonts(inputPath) {
5198
- const resolved = resolve13(inputPath);
5199
- if (!existsSync17(resolved) || !statSync7(resolved).isDirectory()) {
5393
+ const resolved = resolve15(inputPath);
5394
+ if (!existsSync19(resolved) || !statSync7(resolved).isDirectory()) {
5200
5395
  return [];
5201
5396
  }
5202
5397
  return readdirSync8(resolved).filter((f) => {
@@ -5233,7 +5428,7 @@ function createR2Client(config) {
5233
5428
  });
5234
5429
  }
5235
5430
  async function uploadFile(client, bucket, key, filePath) {
5236
- const body = readFileSync21(filePath);
5431
+ const body = readFileSync22(filePath);
5237
5432
  await client.send(
5238
5433
  new PutObjectCommand({
5239
5434
  Bucket: bucket,
@@ -5249,7 +5444,7 @@ async function fontsAddCommand(inputPath, options) {
5249
5444
  const r2Config = getR2Config();
5250
5445
  const files = collectWoff2Files(inputPath);
5251
5446
  const familySlug = options.family ?? deriveFamilySlug(basename6(files[0]));
5252
- const resolved = resolve13(inputPath);
5447
+ const resolved = resolve15(inputPath);
5253
5448
  const nonWoff2 = statSync7(resolved).isDirectory() ? getNonWoff2Fonts(resolved) : [];
5254
5449
  if (!json) {
5255
5450
  logger.heading("Visor Font Upload");
@@ -5537,27 +5732,27 @@ function findCssFiles(dir, maxDepth = 3) {
5537
5732
  }
5538
5733
 
5539
5734
  // src/utils/patterns.ts
5540
- import { existsSync as existsSync19, readdirSync as readdirSync10, readFileSync as readFileSync23 } from "fs";
5541
- import { join as join20 } from "path";
5735
+ import { existsSync as existsSync21, readdirSync as readdirSync10, readFileSync as readFileSync24 } from "fs";
5736
+ import { join as join23 } from "path";
5542
5737
  import { parse as parseYAML } from "yaml";
5543
5738
  function loadPatternsFromYaml(repoRoot) {
5544
- const patternsDir = join20(repoRoot, "patterns");
5545
- if (!existsSync19(patternsDir)) return [];
5739
+ const patternsDir = join23(repoRoot, "patterns");
5740
+ if (!existsSync21(patternsDir)) return [];
5546
5741
  const files = readdirSync10(patternsDir).filter(
5547
5742
  (f) => f.endsWith(".visor-pattern.yaml")
5548
5743
  );
5549
5744
  return files.map((file) => {
5550
- const content = readFileSync23(join20(patternsDir, file), "utf-8");
5745
+ const content = readFileSync24(join23(patternsDir, file), "utf-8");
5551
5746
  return parseYAML(content);
5552
5747
  }).filter(Boolean);
5553
5748
  }
5554
5749
  function findRepoRoot2(startDir) {
5555
5750
  let current = startDir;
5556
5751
  while (true) {
5557
- if (existsSync19(join20(current, "patterns"))) {
5752
+ if (existsSync21(join23(current, "patterns"))) {
5558
5753
  return current;
5559
5754
  }
5560
- const parent = join20(current, "..");
5755
+ const parent = join23(current, "..");
5561
5756
  if (parent === current) return null;
5562
5757
  current = parent;
5563
5758
  }
@@ -5936,8 +6131,8 @@ Visor Tokens (${categoryLabel}) \u2014 ${tokens2.length} tokens
5936
6131
  }
5937
6132
 
5938
6133
  // src/commands/migrate-token-substitution.ts
5939
- import { readFileSync as readFileSync24, writeFileSync as writeFileSync12, readdirSync as readdirSync11, statSync as statSync8, existsSync as existsSync20 } from "fs";
5940
- import { resolve as resolve14, join as join21, relative as relative4 } from "path";
6134
+ import { readFileSync as readFileSync25, writeFileSync as writeFileSync14, readdirSync as readdirSync11, statSync as statSync8, existsSync as existsSync22 } from "fs";
6135
+ import { resolve as resolve16, join as join24, relative as relative4 } from "path";
5941
6136
  import { parse as parseYaml4 } from "yaml";
5942
6137
  import pc4 from "picocolors";
5943
6138
  var V7_ENTR_SUBSTITUTION_MAP = {
@@ -5960,14 +6155,14 @@ var BUILT_IN_SUBSTITUTION_MAPS = {
5960
6155
  var DEFAULT_THEME_ID = "entr";
5961
6156
  function readMapFromThemeFile(themeId, cwd) {
5962
6157
  const candidates = [
5963
- join21(cwd, "themes", `${themeId}.visor.yaml`),
5964
- join21(cwd, "custom-themes", `${themeId}.visor.yaml`),
5965
- join21(cwd, "packages", "docs", "public", "themes", `${themeId}.visor.yaml`)
6158
+ join24(cwd, "themes", `${themeId}.visor.yaml`),
6159
+ join24(cwd, "custom-themes", `${themeId}.visor.yaml`),
6160
+ join24(cwd, "packages", "docs", "public", "themes", `${themeId}.visor.yaml`)
5966
6161
  ];
5967
6162
  for (const candidate of candidates) {
5968
- if (existsSync20(candidate)) {
6163
+ if (existsSync22(candidate)) {
5969
6164
  try {
5970
- const raw = readFileSync24(candidate, "utf-8");
6165
+ const raw = readFileSync25(candidate, "utf-8");
5971
6166
  const parsed = parseYaml4(raw);
5972
6167
  if (parsed?.migrate?.["token-substitution"]) {
5973
6168
  return parsed.migrate["token-substitution"];
@@ -6023,7 +6218,7 @@ function collectCssFiles(dirPath) {
6023
6218
  function walk2(current) {
6024
6219
  const entries = readdirSync11(current, { withFileTypes: true });
6025
6220
  for (const entry of entries) {
6026
- const fullPath = join21(current, entry.name);
6221
+ const fullPath = join24(current, entry.name);
6027
6222
  if (entry.isDirectory()) {
6028
6223
  if (["node_modules", ".git", ".next", "dist", "build", ".cache"].includes(entry.name)) {
6029
6224
  continue;
@@ -6051,7 +6246,7 @@ function runSubstitutionPass(targetPath, map, themeId) {
6051
6246
  const fileResults = [];
6052
6247
  let totalSubstitutions = 0;
6053
6248
  for (const file of files) {
6054
- const content = readFileSync24(file, "utf-8");
6249
+ const content = readFileSync25(file, "utf-8");
6055
6250
  const { newContent, substitutions } = applySubstitutionsToContent(content, map);
6056
6251
  if (substitutions.length > 0) {
6057
6252
  fileResults.push({ file, substitutions, originalContent: content, newContent });
@@ -6085,7 +6280,7 @@ function migrateTokenSubstitutionCommand(targetArg, cwd, options) {
6085
6280
  process.exit(1);
6086
6281
  return;
6087
6282
  }
6088
- const targetPath = resolve14(cwd, targetArg ?? ".");
6283
+ const targetPath = resolve16(cwd, targetArg ?? ".");
6089
6284
  try {
6090
6285
  statSync8(targetPath);
6091
6286
  } catch {
@@ -6113,7 +6308,7 @@ function migrateTokenSubstitutionCommand(targetArg, cwd, options) {
6113
6308
  if (options.json) {
6114
6309
  if (apply) {
6115
6310
  for (const f of result.files) {
6116
- writeFileSync12(f.file, f.newContent, "utf-8");
6311
+ writeFileSync14(f.file, f.newContent, "utf-8");
6117
6312
  }
6118
6313
  console.log(JSON.stringify({
6119
6314
  success: true,
@@ -6153,7 +6348,7 @@ function migrateTokenSubstitutionCommand(targetArg, cwd, options) {
6153
6348
  logger.warn(`Dry run \u2014 no files written. Re-run with ${pc4.bold("--apply")} to commit changes.`);
6154
6349
  } else {
6155
6350
  for (const f of result.files) {
6156
- writeFileSync12(f.file, f.newContent, "utf-8");
6351
+ writeFileSync14(f.file, f.newContent, "utf-8");
6157
6352
  }
6158
6353
  logger.heading(`visor migrate token-substitution \u2014 applied`);
6159
6354
  logger.blank();
@@ -6174,23 +6369,23 @@ function migrateTokenSubstitutionCommand(targetArg, cwd, options) {
6174
6369
  }
6175
6370
 
6176
6371
  // src/commands/sandbox/init.ts
6177
- import { existsSync as existsSync23, mkdirSync as mkdirSync10, readFileSync as readFileSync27, rmSync as rmSync2 } from "fs";
6178
- import { isAbsolute as isAbsolute2, join as join24, resolve as resolve16, dirname as dirname10 } from "path";
6372
+ import { existsSync as existsSync25, mkdirSync as mkdirSync12, readFileSync as readFileSync28, rmSync as rmSync2 } from "fs";
6373
+ import { isAbsolute as isAbsolute4, join as join27, resolve as resolve18, dirname as dirname11 } from "path";
6179
6374
  import { fileURLToPath as fileURLToPath3 } from "url";
6180
6375
  import * as childProcess3 from "child_process";
6181
6376
 
6182
6377
  // src/commands/sandbox/parse-handoff.ts
6183
- import { readFileSync as readFileSync25, existsSync as existsSync21 } from "fs";
6184
- import { dirname as dirname9, resolve as resolve15, isAbsolute } from "path";
6378
+ import { readFileSync as readFileSync26, existsSync as existsSync23 } from "fs";
6379
+ import { dirname as dirname10, resolve as resolve17, isAbsolute as isAbsolute3 } from "path";
6185
6380
  function parseHandoff(handoffPath) {
6186
- const text = readFileSync25(handoffPath, "utf-8");
6381
+ const text = readFileSync26(handoffPath, "utf-8");
6187
6382
  const lines2 = text.split("\n");
6188
6383
  const warnings = [];
6189
6384
  const pattern2 = extractPatternSlug(lines2, warnings);
6190
6385
  const theme2 = extractMetaField(lines2, "Theme");
6191
6386
  const recipePath = extractRecipePath(text, handoffPath, warnings);
6192
6387
  const primitives = extractPrimitives(text, warnings);
6193
- const { screens, mockShapes } = recipePath && existsSync21(recipePath) ? extractRecipeArtifacts(recipePath, warnings) : { screens: [], mockShapes: [] };
6388
+ const { screens, mockShapes } = recipePath && existsSync23(recipePath) ? extractRecipeArtifacts(recipePath, warnings) : { screens: [], mockShapes: [] };
6194
6389
  return { pattern: pattern2, theme: theme2, recipePath, primitives, screens, mockShapes, warnings };
6195
6390
  }
6196
6391
  function extractPatternSlug(lines2, warnings) {
@@ -6221,8 +6416,8 @@ function extractRecipePath(text, handoffPath, warnings) {
6221
6416
  return void 0;
6222
6417
  }
6223
6418
  const href = m[2].trim();
6224
- if (isAbsolute(href)) return href;
6225
- return resolve15(dirname9(handoffPath), href);
6419
+ if (isAbsolute3(href)) return href;
6420
+ return resolve17(dirname10(handoffPath), href);
6226
6421
  }
6227
6422
  var STATUS_PATTERNS = [
6228
6423
  { re: /NEW\s+gap/i, status: "gap-new" },
@@ -6269,7 +6464,7 @@ function extractPrimitives(text, warnings) {
6269
6464
  return primitives;
6270
6465
  }
6271
6466
  function extractRecipeArtifacts(recipePath, warnings) {
6272
- const text = readFileSync25(recipePath, "utf-8");
6467
+ const text = readFileSync26(recipePath, "utf-8");
6273
6468
  const screens = extractScreens(text);
6274
6469
  const mockShapes = extractMockFields(text, warnings);
6275
6470
  return { screens, mockShapes };
@@ -6343,8 +6538,8 @@ function tryPort(port) {
6343
6538
  }
6344
6539
 
6345
6540
  // src/commands/sandbox/scaffold.ts
6346
- import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync13, existsSync as existsSync22, readdirSync as readdirSync12 } from "fs";
6347
- import { join as join22 } from "path";
6541
+ import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync15, existsSync as existsSync24, readdirSync as readdirSync12 } from "fs";
6542
+ import { join as join25 } from "path";
6348
6543
 
6349
6544
  // src/commands/sandbox/templates.ts
6350
6545
  var SANDBOX_PACKAGE_VERSION = "16.2.6";
@@ -6844,12 +7039,12 @@ function toPascalCase(s) {
6844
7039
 
6845
7040
  // src/commands/sandbox/scaffold.ts
6846
7041
  function writeScaffold(sandboxDir, manifest, port, options = {}) {
6847
- mkdirSync8(sandboxDir, { recursive: true });
7042
+ mkdirSync10(sandboxDir, { recursive: true });
6848
7043
  const created = [];
6849
7044
  const writeIfNew = (rel, contents) => {
6850
- const full = join22(sandboxDir, rel);
6851
- mkdirSync8(dirOf(full), { recursive: true });
6852
- writeFileSync13(full, contents, "utf-8");
7045
+ const full = join25(sandboxDir, rel);
7046
+ mkdirSync10(dirOf(full), { recursive: true });
7047
+ writeFileSync15(full, contents, "utf-8");
6853
7048
  created.push(rel);
6854
7049
  };
6855
7050
  writeIfNew("package.json", packageJsonTemplate(manifest.pattern));
@@ -6875,7 +7070,7 @@ function writeScaffold(sandboxDir, manifest, port, options = {}) {
6875
7070
  }
6876
7071
  }
6877
7072
  writeIfNew("playwright.capture.mjs", captureScriptTemplate());
6878
- mkdirSync8(join22(sandboxDir, "captures", "approved"), { recursive: true });
7073
+ mkdirSync10(join25(sandboxDir, "captures", "approved"), { recursive: true });
6879
7074
  return { created };
6880
7075
  }
6881
7076
  function dirOf(p) {
@@ -6909,10 +7104,10 @@ function writeSandboxConfig(sandboxDir, manifest, port, options) {
6909
7104
  stripChromeSelectors: options.prototypeImport.stripChromeSelectors
6910
7105
  } : null
6911
7106
  };
6912
- writeFileSync13(join22(sandboxDir, "sandbox.json"), JSON.stringify(config, null, 2) + "\n", "utf-8");
7107
+ writeFileSync15(join25(sandboxDir, "sandbox.json"), JSON.stringify(config, null, 2) + "\n", "utf-8");
6913
7108
  }
6914
7109
  function sandboxIsEmpty(sandboxDir) {
6915
- if (!existsSync22(sandboxDir)) return true;
7110
+ if (!existsSync24(sandboxDir)) return true;
6916
7111
  try {
6917
7112
  return readdirSync12(sandboxDir).filter((f) => !f.startsWith(".")).length === 0;
6918
7113
  } catch {
@@ -6921,8 +7116,8 @@ function sandboxIsEmpty(sandboxDir) {
6921
7116
  }
6922
7117
 
6923
7118
  // src/commands/sandbox/html-prototype.ts
6924
- import { mkdirSync as mkdirSync9, readdirSync as readdirSync13, readFileSync as readFileSync26, statSync as statSync9, writeFileSync as writeFileSync14 } from "fs";
6925
- import { join as join23 } from "path";
7119
+ import { mkdirSync as mkdirSync11, readdirSync as readdirSync13, readFileSync as readFileSync27, statSync as statSync9, writeFileSync as writeFileSync16 } from "fs";
7120
+ import { join as join26 } from "path";
6926
7121
 
6927
7122
  // src/commands/sandbox/strip-chrome.ts
6928
7123
  var DEFAULT_STRIP_SELECTORS = [
@@ -7106,8 +7301,8 @@ function stripDocumentaryChrome(html, selectors) {
7106
7301
  var SCREEN_FILE_PATTERN = /^screen-(\d+)-([^/.]+)\.html$/i;
7107
7302
  function copyHtmlPrototype(sourceDir, sandboxDir, manifest, options = {}) {
7108
7303
  const warnings = [];
7109
- const destAbs = join23(sandboxDir, "public", "prototype");
7110
- mkdirSync9(destAbs, { recursive: true });
7304
+ const destAbs = join26(sandboxDir, "public", "prototype");
7305
+ mkdirSync11(destAbs, { recursive: true });
7111
7306
  const stripChromeSelectors = options.stripChromeSelectors ?? [];
7112
7307
  const copiedFiles = copyTreeRelative(sourceDir, destAbs, "", stripChromeSelectors);
7113
7308
  const screenFiles = listOrderedScreenFiles(sourceDir);
@@ -7160,24 +7355,24 @@ function deriveStateCoverageScreen(file, existingNames) {
7160
7355
  }
7161
7356
  function copyTreeRelative(srcDir, destDir, relDir = "", stripChromeSelectors = []) {
7162
7357
  const out = [];
7163
- const entries = readdirSync13(join23(srcDir, relDir));
7358
+ const entries = readdirSync13(join26(srcDir, relDir));
7164
7359
  for (const name of entries) {
7165
7360
  if (name.startsWith(".")) continue;
7166
- const srcPath = join23(srcDir, relDir, name);
7167
- const destPath = join23(destDir, relDir, name);
7361
+ const srcPath = join26(srcDir, relDir, name);
7362
+ const destPath = join26(destDir, relDir, name);
7168
7363
  const stat = statSync9(srcPath);
7169
7364
  if (stat.isDirectory()) {
7170
- mkdirSync9(destPath, { recursive: true });
7171
- out.push(...copyTreeRelative(srcDir, destDir, join23(relDir, name), stripChromeSelectors));
7365
+ mkdirSync11(destPath, { recursive: true });
7366
+ out.push(...copyTreeRelative(srcDir, destDir, join26(relDir, name), stripChromeSelectors));
7172
7367
  } else if (stat.isFile()) {
7173
7368
  if (stripChromeSelectors.length > 0 && name.toLowerCase().endsWith(".html")) {
7174
- const html = readFileSync26(srcPath, "utf-8");
7369
+ const html = readFileSync27(srcPath, "utf-8");
7175
7370
  const stripped = stripDocumentaryChrome(html, stripChromeSelectors);
7176
- writeFileSync14(destPath, stripped);
7371
+ writeFileSync16(destPath, stripped);
7177
7372
  } else {
7178
- writeFileSync14(destPath, readFileSync26(srcPath));
7373
+ writeFileSync16(destPath, readFileSync27(srcPath));
7179
7374
  }
7180
- out.push(join23("public", "prototype", relDir, name).replace(/\\/g, "/"));
7375
+ out.push(join26("public", "prototype", relDir, name).replace(/\\/g, "/"));
7181
7376
  }
7182
7377
  }
7183
7378
  return out;
@@ -7229,8 +7424,8 @@ async function runInit(name, cwd, options) {
7229
7424
  if (!name || !/^[a-z0-9][a-z0-9-_]*$/i.test(name)) {
7230
7425
  throw new Error(`Invalid sandbox name '${name}'. Use letters, digits, '-' or '_'.`);
7231
7426
  }
7232
- const handoffPath = isAbsolute2(options.handoff) ? options.handoff : resolve16(cwd, options.handoff);
7233
- if (!existsSync23(handoffPath)) {
7427
+ const handoffPath = isAbsolute4(options.handoff) ? options.handoff : resolve18(cwd, options.handoff);
7428
+ if (!existsSync25(handoffPath)) {
7234
7429
  throw new Error(`Handoff manifest not found: ${handoffPath}`);
7235
7430
  }
7236
7431
  const manifest = parseHandoff(handoffPath);
@@ -7239,7 +7434,7 @@ async function runInit(name, cwd, options) {
7239
7434
  `Handoff has no primitives \u2014 refusing to scaffold an empty sandbox. Check the manifest at ${handoffPath}`
7240
7435
  );
7241
7436
  }
7242
- const sandboxDir = join24(cwd, ".lo", "sandbox", name);
7437
+ const sandboxDir = join27(cwd, ".lo", "sandbox", name);
7243
7438
  if (!sandboxIsEmpty(sandboxDir)) {
7244
7439
  if (!options.overwrite) {
7245
7440
  throw new Error(
@@ -7248,12 +7443,12 @@ async function runInit(name, cwd, options) {
7248
7443
  }
7249
7444
  rmSync2(sandboxDir, { recursive: true, force: true });
7250
7445
  }
7251
- mkdirSync10(sandboxDir, { recursive: true });
7446
+ mkdirSync12(sandboxDir, { recursive: true });
7252
7447
  const port = await findOpenPort();
7253
7448
  let prototypeImport;
7254
7449
  if (options.fromHtmlPrototype) {
7255
- const prototypeDir = isAbsolute2(options.fromHtmlPrototype) ? options.fromHtmlPrototype : resolve16(cwd, options.fromHtmlPrototype);
7256
- if (!existsSync23(prototypeDir)) {
7450
+ const prototypeDir = isAbsolute4(options.fromHtmlPrototype) ? options.fromHtmlPrototype : resolve18(cwd, options.fromHtmlPrototype);
7451
+ if (!existsSync25(prototypeDir)) {
7257
7452
  throw new Error(`HTML prototype directory not found: ${prototypeDir}`);
7258
7453
  }
7259
7454
  const stripChromeSelectors = resolveStripSelectors(
@@ -7312,22 +7507,22 @@ async function runInit(name, cwd, options) {
7312
7507
  function applyThemeIfPossible(sandboxDir, manifest, theme2, cwd, json, themeFile) {
7313
7508
  const candidates = [];
7314
7509
  if (themeFile) {
7315
- candidates.push(isAbsolute2(themeFile) ? themeFile : resolve16(cwd, themeFile));
7510
+ candidates.push(isAbsolute4(themeFile) ? themeFile : resolve18(cwd, themeFile));
7316
7511
  }
7317
- candidates.push(isAbsolute2(theme2) ? theme2 : resolve16(cwd, theme2));
7512
+ candidates.push(isAbsolute4(theme2) ? theme2 : resolve18(cwd, theme2));
7318
7513
  const privateRoot = process.env.VISOR_THEMES_PRIVATE_PATH;
7319
7514
  if (privateRoot && privateRoot.length > 0) {
7320
- candidates.push(join24(privateRoot, "themes", theme2, "theme.visor.yaml"));
7515
+ candidates.push(join27(privateRoot, "themes", theme2, "theme.visor.yaml"));
7321
7516
  }
7322
7517
  candidates.push(
7323
- join24(cwd, "themes", `${theme2}.visor.yaml`),
7324
- join24(cwd, "custom-themes", `${theme2}.visor.yaml`)
7518
+ join27(cwd, "themes", `${theme2}.visor.yaml`),
7519
+ join27(cwd, "custom-themes", `${theme2}.visor.yaml`)
7325
7520
  );
7326
- const yamlPath = candidates.find((p) => existsSync23(p));
7521
+ const yamlPath = candidates.find((p) => existsSync25(p));
7327
7522
  if (!yamlPath) {
7328
7523
  const searched = candidates.join(", ");
7329
7524
  manifest.warnings.push(
7330
- `Theme '${theme2}' not found (searched: ${searched}) \u2014 sandbox uses placeholder globals.css. Run 'npx visor theme apply <path-to-theme.visor.yaml> --adapter nextjs -o ${join24(sandboxDir, "app", "globals.css")}' manually, or re-run with --theme-file <path>, or set VISOR_THEMES_PRIVATE_PATH to a directory containing themes/${theme2}/theme.visor.yaml.`
7525
+ `Theme '${theme2}' not found (searched: ${searched}) \u2014 sandbox uses placeholder globals.css. Run 'npx visor theme apply <path-to-theme.visor.yaml> --adapter nextjs -o ${join27(sandboxDir, "app", "globals.css")}' manually, or re-run with --theme-file <path>, or set VISOR_THEMES_PRIVATE_PATH to a directory containing themes/${theme2}/theme.visor.yaml.`
7331
7526
  );
7332
7527
  if (!json) {
7333
7528
  logger.warn(`Theme '${theme2}' not found \u2014 leaving placeholder globals.css.`);
@@ -7335,12 +7530,12 @@ function applyThemeIfPossible(sandboxDir, manifest, theme2, cwd, json, themeFile
7335
7530
  `Re-run with --theme-file <path>, or set VISOR_THEMES_PRIVATE_PATH=<dir-containing-themes/${theme2}/theme.visor.yaml>.`
7336
7531
  );
7337
7532
  logger.item(
7338
- `Or apply manually: npx visor theme apply <path> --adapter nextjs -o ${join24(sandboxDir, "app", "globals.css")}`
7533
+ `Or apply manually: npx visor theme apply <path> --adapter nextjs -o ${join27(sandboxDir, "app", "globals.css")}`
7339
7534
  );
7340
7535
  }
7341
7536
  return;
7342
7537
  }
7343
- const globalsOut = join24(sandboxDir, "app", "globals.css");
7538
+ const globalsOut = join27(sandboxDir, "app", "globals.css");
7344
7539
  try {
7345
7540
  themeApplyCommand(yamlPath, sandboxDir, {
7346
7541
  output: globalsOut,
@@ -7393,12 +7588,12 @@ function loadKnownPrimitives() {
7393
7588
  }
7394
7589
  function readCliVersion() {
7395
7590
  try {
7396
- const here = dirname10(fileURLToPath3(import.meta.url));
7591
+ const here = dirname11(fileURLToPath3(import.meta.url));
7397
7592
  for (let i = 0; i < 6; i++) {
7398
7593
  const segments = new Array(i).fill("..");
7399
- const candidate = join24(here, ...segments, "package.json");
7594
+ const candidate = join27(here, ...segments, "package.json");
7400
7595
  try {
7401
- const pkg2 = JSON.parse(readFileSync27(candidate, "utf-8"));
7596
+ const pkg2 = JSON.parse(readFileSync28(candidate, "utf-8"));
7402
7597
  if (pkg2.name === "@loworbitstudio/visor" && pkg2.version) return pkg2.version;
7403
7598
  } catch {
7404
7599
  }
@@ -7409,14 +7604,14 @@ function readCliVersion() {
7409
7604
  }
7410
7605
 
7411
7606
  // src/commands/sandbox/dev.ts
7412
- import { existsSync as existsSync24, readFileSync as readFileSync28 } from "fs";
7413
- import { join as join25 } from "path";
7607
+ import { existsSync as existsSync26, readFileSync as readFileSync29 } from "fs";
7608
+ import { join as join28 } from "path";
7414
7609
  import * as childProcess4 from "child_process";
7415
7610
  function sandboxDevCommand(cwd, options) {
7416
7611
  const json = options.json ?? false;
7417
- const sandboxDir = join25(cwd, ".lo", "sandbox", options.name);
7418
- const configPath = join25(sandboxDir, "sandbox.json");
7419
- if (!existsSync24(configPath)) {
7612
+ const sandboxDir = join28(cwd, ".lo", "sandbox", options.name);
7613
+ const configPath = join28(sandboxDir, "sandbox.json");
7614
+ if (!existsSync26(configPath)) {
7420
7615
  fail(
7421
7616
  json,
7422
7617
  `Sandbox '${options.name}' not found at ${sandboxDir}. Run 'visor sandbox init ${options.name} --handoff ... --theme ...' first.`
@@ -7425,7 +7620,7 @@ function sandboxDevCommand(cwd, options) {
7425
7620
  }
7426
7621
  let config;
7427
7622
  try {
7428
- config = JSON.parse(readFileSync28(configPath, "utf-8"));
7623
+ config = JSON.parse(readFileSync29(configPath, "utf-8"));
7429
7624
  } catch (err) {
7430
7625
  fail(json, `Invalid sandbox.json at ${configPath}: ${err.message}`);
7431
7626
  return;
@@ -7483,20 +7678,20 @@ function fail(json, message) {
7483
7678
  // src/commands/sandbox/approve.ts
7484
7679
  import {
7485
7680
  copyFileSync as copyFileSync2,
7486
- existsSync as existsSync25,
7487
- mkdirSync as mkdirSync11,
7488
- readFileSync as readFileSync29,
7681
+ existsSync as existsSync27,
7682
+ mkdirSync as mkdirSync13,
7683
+ readFileSync as readFileSync30,
7489
7684
  readdirSync as readdirSync14,
7490
7685
  rmSync as rmSync3,
7491
- writeFileSync as writeFileSync15
7686
+ writeFileSync as writeFileSync17
7492
7687
  } from "fs";
7493
- import { basename as basename7, join as join26 } from "path";
7688
+ import { basename as basename7, join as join29 } from "path";
7494
7689
  import * as childProcess5 from "child_process";
7495
7690
  function sandboxApproveCommand(cwd, options) {
7496
7691
  const json = options.json ?? false;
7497
- const sandboxDir = join26(cwd, ".lo", "sandbox", options.name);
7498
- const configPath = join26(sandboxDir, "sandbox.json");
7499
- if (!existsSync25(configPath)) {
7692
+ const sandboxDir = join29(cwd, ".lo", "sandbox", options.name);
7693
+ const configPath = join29(sandboxDir, "sandbox.json");
7694
+ if (!existsSync27(configPath)) {
7500
7695
  fail2(
7501
7696
  json,
7502
7697
  `Sandbox '${options.name}' not found at ${sandboxDir}. Run 'visor sandbox init' first.`
@@ -7510,9 +7705,9 @@ function sandboxApproveCommand(cwd, options) {
7510
7705
  runCapture(sandboxDir, configPath, options.name, json);
7511
7706
  }
7512
7707
  function runCapture(sandboxDir, configPath, sandboxName, json) {
7513
- const config = JSON.parse(readFileSync29(configPath, "utf-8"));
7514
- const captureScriptPath = join26(sandboxDir, "playwright.capture.mjs");
7515
- writeFileSync15(captureScriptPath, captureScriptTemplate(), "utf-8");
7708
+ const config = JSON.parse(readFileSync30(configPath, "utf-8"));
7709
+ const captureScriptPath = join29(sandboxDir, "playwright.capture.mjs");
7710
+ writeFileSync17(captureScriptPath, captureScriptTemplate(), "utf-8");
7516
7711
  ensurePlaywrightInstalled(sandboxDir, json);
7517
7712
  const result = childProcess5.spawnSync(
7518
7713
  "npx",
@@ -7533,9 +7728,9 @@ function runCapture(sandboxDir, configPath, sandboxName, json) {
7533
7728
  ${result.stderr}`);
7534
7729
  return;
7535
7730
  }
7536
- const pendingDir = join26(sandboxDir, "captures", "pending");
7537
- const diffsDir = join26(sandboxDir, "captures", "diffs");
7538
- const approvedDir = join26(sandboxDir, "captures", "approved");
7731
+ const pendingDir = join29(sandboxDir, "captures", "pending");
7732
+ const diffsDir = join29(sandboxDir, "captures", "diffs");
7733
+ const approvedDir = join29(sandboxDir, "captures", "approved");
7539
7734
  const pendingFiles = safeListPngs(pendingDir);
7540
7735
  const diffFiles = safeListPngs(diffsDir);
7541
7736
  const hasBaseline = safeListPngs(approvedDir).length > 0;
@@ -7572,9 +7767,9 @@ ${result.stderr}`);
7572
7767
  }
7573
7768
  }
7574
7769
  function runPromotion(sandboxDir, sandboxName, json) {
7575
- const pendingDir = join26(sandboxDir, "captures", "pending");
7576
- const approvedDir = join26(sandboxDir, "captures", "approved");
7577
- const diffsDir = join26(sandboxDir, "captures", "diffs");
7770
+ const pendingDir = join29(sandboxDir, "captures", "pending");
7771
+ const approvedDir = join29(sandboxDir, "captures", "approved");
7772
+ const diffsDir = join29(sandboxDir, "captures", "diffs");
7578
7773
  const pendingFiles = safeListPngs(pendingDir);
7579
7774
  if (pendingFiles.length === 0) {
7580
7775
  fail2(
@@ -7583,10 +7778,10 @@ function runPromotion(sandboxDir, sandboxName, json) {
7583
7778
  );
7584
7779
  return;
7585
7780
  }
7586
- mkdirSync11(approvedDir, { recursive: true });
7781
+ mkdirSync13(approvedDir, { recursive: true });
7587
7782
  const promoted = [];
7588
7783
  for (const src of pendingFiles) {
7589
- const dest = join26(approvedDir, basename7(src));
7784
+ const dest = join29(approvedDir, basename7(src));
7590
7785
  copyFileSync2(src, dest);
7591
7786
  promoted.push(dest);
7592
7787
  }
@@ -7606,8 +7801,8 @@ function runPromotion(sandboxDir, sandboxName, json) {
7606
7801
  }
7607
7802
  }
7608
7803
  function ensurePlaywrightInstalled(sandboxDir, json) {
7609
- const markerPath = join26(sandboxDir, ".playwright-installed");
7610
- if (existsSync25(markerPath)) return;
7804
+ const markerPath = join29(sandboxDir, ".playwright-installed");
7805
+ if (existsSync27(markerPath)) return;
7611
7806
  if (!json) logger.info("Installing Playwright Chromium (one-time)...");
7612
7807
  const result = childProcess5.spawnSync(
7613
7808
  "npx",
@@ -7623,11 +7818,11 @@ function ensurePlaywrightInstalled(sandboxDir, json) {
7623
7818
  if (typeof result.status === "number" && result.status !== 0) {
7624
7819
  throw new Error(`playwright install exited with code ${result.status}`);
7625
7820
  }
7626
- writeFileSync15(markerPath, (/* @__PURE__ */ new Date()).toISOString(), "utf-8");
7821
+ writeFileSync17(markerPath, (/* @__PURE__ */ new Date()).toISOString(), "utf-8");
7627
7822
  }
7628
7823
  function safeListPngs(dir) {
7629
- if (!existsSync25(dir)) return [];
7630
- return readdirSync14(dir).filter((f) => f.endsWith(".png")).map((f) => join26(dir, f));
7824
+ if (!existsSync27(dir)) return [];
7825
+ return readdirSync14(dir).filter((f) => f.endsWith(".png")).map((f) => join29(dir, f));
7631
7826
  }
7632
7827
  function tryParseJson(s) {
7633
7828
  try {
@@ -7648,14 +7843,12 @@ function fail2(json, message) {
7648
7843
  // src/commands/spawn.ts
7649
7844
  import {
7650
7845
  cpSync,
7651
- existsSync as existsSync28,
7652
- mkdirSync as mkdirSync12,
7846
+ existsSync as existsSync29,
7653
7847
  readFileSync as readFileSync31,
7654
7848
  readdirSync as readdirSync16,
7655
- rmSync as rmSync4,
7656
- writeFileSync as writeFileSync16
7849
+ rmSync as rmSync4
7657
7850
  } from "fs";
7658
- import { basename as basename8, isAbsolute as isAbsolute3, join as join29, resolve as resolve17 } from "path";
7851
+ import { basename as basename8, isAbsolute as isAbsolute5, join as join31, resolve as resolve19 } from "path";
7659
7852
  import { homedir as homedir3 } from "os";
7660
7853
  import * as childProcess6 from "child_process";
7661
7854
  import { parse as parseYaml5 } from "yaml";
@@ -7664,64 +7857,8 @@ import { nextjsAdapter as nextjsAdapter3 } from "@loworbitstudio/visor-theme-eng
7664
7857
  import { validate as validate3 } from "@loworbitstudio/visor-theme-engine";
7665
7858
 
7666
7859
  // src/lib/blessed-discovery.ts
7667
- import { existsSync as existsSync27, readdirSync as readdirSync15 } from "fs";
7668
- import { join as join28 } from "path";
7669
-
7670
- // src/lib/blessed-manifest.ts
7671
- import { existsSync as existsSync26, readFileSync as readFileSync30 } from "fs";
7672
- import { join as join27 } from "path";
7673
- import { z } from "zod";
7674
- var BLESSED_MANIFEST_FILENAME = "blessed-manifest.json";
7675
- var blessedManifestSchema = z.object({
7676
- /** Blessed-build shape, e.g. `admin-ui`. Matched against the `{shape}` in `blessed:{shape}:{pattern}`. */
7677
- shape: z.string().min(1),
7678
- /** Pattern name within the shape, e.g. `organization-management`. */
7679
- pattern: z.string().min(1),
7680
- /** The theme the reference build was authored + captured against. */
7681
- base_theme: z.string().min(1),
7682
- /** Minimum Visor version this build is known to work with (semver range). */
7683
- requires_visor: z.string().min(1),
7684
- /** Path (relative to the build root) to the approved capture baseline. */
7685
- captures_baseline: z.string().min(1),
7686
- /** Three-gates disposition at the time the build was blessed. */
7687
- three_gates_status: z.string().min(1)
7688
- }).strict();
7689
- function parseBlessedManifest(buildDir) {
7690
- const manifestPath = join27(buildDir, BLESSED_MANIFEST_FILENAME);
7691
- if (!existsSync26(manifestPath)) {
7692
- return {
7693
- ok: false,
7694
- error: `this directory is not a blessed build (missing ${BLESSED_MANIFEST_FILENAME}); see docs/blessed-builds.md`
7695
- };
7696
- }
7697
- let raw;
7698
- try {
7699
- raw = readFileSync30(manifestPath, "utf-8");
7700
- } catch {
7701
- return { ok: false, error: `could not read ${manifestPath}` };
7702
- }
7703
- let json;
7704
- try {
7705
- json = JSON.parse(raw);
7706
- } catch (err) {
7707
- const message = err instanceof Error ? err.message : "invalid JSON";
7708
- return { ok: false, error: `invalid JSON in ${manifestPath}: ${message}` };
7709
- }
7710
- const result = blessedManifestSchema.safeParse(json);
7711
- if (!result.success) {
7712
- const issues = result.error.issues.map((issue) => {
7713
- const path2 = issue.path.join(".") || "(root)";
7714
- return `${path2}: ${issue.message}`;
7715
- }).join("; ");
7716
- return {
7717
- ok: false,
7718
- error: `invalid ${BLESSED_MANIFEST_FILENAME} at ${manifestPath}: ${issues}`
7719
- };
7720
- }
7721
- return { ok: true, manifest: result.data };
7722
- }
7723
-
7724
- // src/lib/blessed-discovery.ts
7860
+ import { existsSync as existsSync28, readdirSync as readdirSync15 } from "fs";
7861
+ import { join as join30 } from "path";
7725
7862
  var WALK_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
7726
7863
  "node_modules",
7727
7864
  ".next",
@@ -7734,7 +7871,7 @@ var WALK_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
7734
7871
  function discoverBlessedBuilds(blessedDir, maxDepth = 8) {
7735
7872
  const builds = [];
7736
7873
  const errors = [];
7737
- if (!existsSync27(blessedDir)) {
7874
+ if (!existsSync28(blessedDir)) {
7738
7875
  return { builds, errors };
7739
7876
  }
7740
7877
  walk2(blessedDir, 0);
@@ -7768,7 +7905,7 @@ function discoverBlessedBuilds(blessedDir, maxDepth = 8) {
7768
7905
  for (const entry of entries) {
7769
7906
  if (!entry.isDirectory()) continue;
7770
7907
  if (WALK_EXCLUDE_DIRS.has(entry.name)) continue;
7771
- walk2(join28(dir, entry.name), depth + 1);
7908
+ walk2(join30(dir, entry.name), depth + 1);
7772
7909
  }
7773
7910
  }
7774
7911
  }
@@ -7781,7 +7918,7 @@ function resolveBlessedBuild(blessedDir, shape, pattern2) {
7781
7918
  }
7782
7919
 
7783
7920
  // src/commands/spawn.ts
7784
- var DEFAULT_BLESSED_DIR = join29(
7921
+ var DEFAULT_BLESSED_DIR = join31(
7785
7922
  homedir3(),
7786
7923
  "Code",
7787
7924
  "low-orbit",
@@ -7817,23 +7954,23 @@ function parseBlessedIdentifier(from) {
7817
7954
  }
7818
7955
  function resolveBlessedDir(cwd, options) {
7819
7956
  const raw = options.blessedDir ?? (process.env.VISOR_BLESSED_DIR && process.env.VISOR_BLESSED_DIR.length > 0 ? process.env.VISOR_BLESSED_DIR : void 0) ?? DEFAULT_BLESSED_DIR;
7820
- return isAbsolute3(raw) ? raw : resolve17(cwd, raw);
7957
+ return isAbsolute5(raw) ? raw : resolve19(cwd, raw);
7821
7958
  }
7822
7959
  function resolveThemeFile(theme2, cwd, themeFile) {
7823
7960
  const candidates = [];
7824
7961
  if (themeFile) {
7825
- candidates.push(isAbsolute3(themeFile) ? themeFile : resolve17(cwd, themeFile));
7962
+ candidates.push(isAbsolute5(themeFile) ? themeFile : resolve19(cwd, themeFile));
7826
7963
  }
7827
- candidates.push(isAbsolute3(theme2) ? theme2 : resolve17(cwd, theme2));
7964
+ candidates.push(isAbsolute5(theme2) ? theme2 : resolve19(cwd, theme2));
7828
7965
  const privateRoot = process.env.VISOR_THEMES_PRIVATE_PATH;
7829
7966
  if (privateRoot && privateRoot.length > 0) {
7830
- candidates.push(join29(privateRoot, "themes", theme2, "theme.visor.yaml"));
7967
+ candidates.push(join31(privateRoot, "themes", theme2, "theme.visor.yaml"));
7831
7968
  }
7832
7969
  candidates.push(
7833
- join29(cwd, "themes", `${theme2}.visor.yaml`),
7834
- join29(cwd, "custom-themes", `${theme2}.visor.yaml`)
7970
+ join31(cwd, "themes", `${theme2}.visor.yaml`),
7971
+ join31(cwd, "custom-themes", `${theme2}.visor.yaml`)
7835
7972
  );
7836
- const found = candidates.find((candidate) => existsSync28(candidate));
7973
+ const found = candidates.find((candidate) => existsSync29(candidate));
7837
7974
  if (!found) {
7838
7975
  throw new Error(
7839
7976
  `Theme '${theme2}' not found (searched: ${candidates.join(", ")}). Pass --theme-file <path>, use a direct path, or set VISOR_THEMES_PRIVATE_PATH.`
@@ -7841,23 +7978,25 @@ function resolveThemeFile(theme2, cwd, themeFile) {
7841
7978
  }
7842
7979
  return { path: found, searched: candidates };
7843
7980
  }
7844
- function resolveGlobalsCssPath(outputDir) {
7845
- const candidates = [
7846
- join29(outputDir, "app", "globals.css"),
7847
- join29(outputDir, "src", "app", "globals.css")
7848
- ];
7849
- const existing = candidates.find((candidate) => existsSync28(candidate));
7850
- return existing ?? candidates[0];
7851
- }
7852
- function applyThemeToFork(themeFile, globalsOut) {
7981
+ function applyThemeToFork(themeFile, outputDir, manifest) {
7853
7982
  const yaml = readFileSync31(themeFile, "utf-8");
7854
7983
  const data = generateThemeData7(yaml);
7984
+ const themeId = data.config.name;
7985
+ if (!themeId || themeId.length === 0) {
7986
+ throw new Error(
7987
+ `Theme file '${themeFile}' is missing a config.name; required to derive the theme id for spawn's theme-apply dispatch. See docs/blessed-builds.md.`
7988
+ );
7989
+ }
7855
7990
  const css = nextjsAdapter3(
7856
7991
  { primitives: data.primitives, tokens: data.tokens, config: data.config },
7857
7992
  {}
7858
7993
  );
7859
- mkdirSync12(join29(globalsOut, ".."), { recursive: true });
7860
- writeFileSync16(globalsOut, css, "utf-8");
7994
+ applyThemeToBuild({
7995
+ manifest,
7996
+ buildDir: outputDir,
7997
+ themeId,
7998
+ adapterCss: css
7999
+ });
7861
8000
  }
7862
8001
  function runNpmInstall2(outputDir, json) {
7863
8002
  const result = childProcess6.spawnSync("npm", ["install", "--no-audit", "--no-fund"], {
@@ -7886,8 +8025,8 @@ Available builds:
7886
8025
  ${list}`
7887
8026
  );
7888
8027
  }
7889
- const outputDir = isAbsolute3(options.output) ? options.output : resolve17(cwd, options.output);
7890
- if (existsSync28(outputDir) && readdirSync16(outputDir).length > 0) {
8028
+ const outputDir = isAbsolute5(options.output) ? options.output : resolve19(cwd, options.output);
8029
+ if (existsSync29(outputDir) && readdirSync16(outputDir).length > 0) {
7891
8030
  throw new Error(`Output directory already exists and is not empty: ${outputDir}`);
7892
8031
  }
7893
8032
  const { path: themeFile } = resolveThemeFile(options.theme, cwd, options.themeFile);
@@ -7898,8 +8037,7 @@ ${list}`
7898
8037
  let validated = false;
7899
8038
  let installed = false;
7900
8039
  try {
7901
- const globalsOut = resolveGlobalsCssPath(outputDir);
7902
- applyThemeToFork(themeFile, globalsOut);
8040
+ applyThemeToFork(themeFile, outputDir, build.manifest);
7903
8041
  if (options.validate) {
7904
8042
  const parsed = parseYaml5(readFileSync31(themeFile, "utf-8"));
7905
8043
  const result = validate3(parsed);
@@ -8003,9 +8141,9 @@ function spawnCommand(cwd, options) {
8003
8141
  }
8004
8142
 
8005
8143
  // src/index.ts
8006
- var __dirname2 = dirname11(fileURLToPath4(import.meta.url));
8144
+ var __dirname2 = dirname12(fileURLToPath4(import.meta.url));
8007
8145
  var pkg = JSON.parse(
8008
- readFileSync32(join30(__dirname2, "..", "package.json"), "utf-8")
8146
+ readFileSync32(join32(__dirname2, "..", "package.json"), "utf-8")
8009
8147
  );
8010
8148
  var program = new Command2();
8011
8149
  program.name("visor").description("CLI for the Visor design system").version(pkg.version);
@@ -8058,6 +8196,9 @@ theme.command("apply").description(
8058
8196
  ).option(
8059
8197
  "--theme-class-name <name>",
8060
8198
  "(flutter) class name for generated theme (default: VisorAppTheme)"
8199
+ ).option(
8200
+ "--target-path <path>",
8201
+ "(nextjs) path to a blessed-build root; dispatches CSS through the build's theme_apply_target (VI-601), ignores --output"
8061
8202
  ).action(
8062
8203
  (file, options) => {
8063
8204
  themeApplyCommand(file, process.cwd(), {