rafters 0.0.8 → 0.0.9

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
@@ -9,8 +9,8 @@ import { Command } from "commander";
9
9
 
10
10
  // src/commands/add.ts
11
11
  import { existsSync } from "fs";
12
- import { access, mkdir, readFile, writeFile } from "fs/promises";
13
- import { dirname, join as join2 } from "path";
12
+ import { access, mkdir, readFile as readFile2, writeFile } from "fs/promises";
13
+ import { dirname, join as join3 } from "path";
14
14
 
15
15
  // ../../node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/external.js
16
16
  var external_exports = {};
@@ -12558,10 +12558,16 @@ config(en_default());
12558
12558
  var RegistryFileSchema = external_exports.object({
12559
12559
  path: external_exports.string(),
12560
12560
  content: external_exports.string(),
12561
- dependencies: external_exports.array(external_exports.string())
12561
+ dependencies: external_exports.array(external_exports.string()),
12562
12562
  // e.g., ["lodash@4.17.21"] - versioned
12563
+ devDependencies: external_exports.array(external_exports.string()).default([])
12564
+ // e.g., ["vitest"] - from @devDependencies JSDoc
12563
12565
  });
12564
- var RegistryItemTypeSchema = external_exports.enum(["registry:ui", "registry:primitive"]);
12566
+ var RegistryItemTypeSchema = external_exports.enum([
12567
+ "registry:ui",
12568
+ "registry:primitive",
12569
+ "registry:composite"
12570
+ ]);
12565
12571
  var RegistryItemSchema = external_exports.object({
12566
12572
  name: external_exports.string(),
12567
12573
  type: RegistryItemTypeSchema,
@@ -12714,18 +12720,64 @@ var RegistryClient = class {
12714
12720
  };
12715
12721
  var registryClient = new RegistryClient();
12716
12722
 
12717
- // src/utils/paths.ts
12718
- import { join } from "path";
12719
- function getRaftersPaths(projectRoot = process.cwd()) {
12720
- const root = join(projectRoot, ".rafters");
12723
+ // src/utils/exports.ts
12724
+ var DEFAULT_EXPORTS = {
12725
+ tailwind: true,
12726
+ typescript: true,
12727
+ dtcg: false,
12728
+ compiled: false
12729
+ };
12730
+ var EXPORT_CHOICES = [
12731
+ {
12732
+ name: "Tailwind CSS (web projects)",
12733
+ value: "tailwind",
12734
+ checked: true
12735
+ },
12736
+ {
12737
+ name: "TypeScript (type-safe constants)",
12738
+ value: "typescript",
12739
+ checked: true
12740
+ },
12741
+ {
12742
+ name: "DTCG JSON (Figma Tokens, Style Dictionary)",
12743
+ value: "dtcg",
12744
+ checked: false
12745
+ },
12746
+ {
12747
+ name: "Standalone CSS (pre-built, no Tailwind required)",
12748
+ value: "compiled",
12749
+ checked: false
12750
+ }
12751
+ ];
12752
+ var FUTURE_EXPORTS = [
12753
+ {
12754
+ name: "iOS (Swift/SwiftUI)",
12755
+ value: "tailwind",
12756
+ // placeholder
12757
+ checked: false,
12758
+ disabled: "coming soon"
12759
+ },
12760
+ {
12761
+ name: "Android (Compose)",
12762
+ value: "tailwind",
12763
+ // placeholder
12764
+ checked: false,
12765
+ disabled: "coming soon"
12766
+ }
12767
+ ];
12768
+ function selectionsToConfig(selections) {
12721
12769
  return {
12722
- root,
12723
- config: join(root, "config.rafters.json"),
12724
- tokens: join(root, "tokens"),
12725
- output: join(root, "output")
12770
+ tailwind: selections.includes("tailwind"),
12771
+ typescript: selections.includes("typescript"),
12772
+ dtcg: selections.includes("dtcg"),
12773
+ compiled: selections.includes("compiled")
12726
12774
  };
12727
12775
  }
12728
12776
 
12777
+ // src/utils/install-registry-deps.ts
12778
+ import { readFile } from "fs/promises";
12779
+ import { join } from "path";
12780
+
12729
12781
  // src/utils/ui.ts
12730
12782
  import ora from "ora";
12731
12783
  var context = {
@@ -12844,8 +12896,9 @@ function log(event) {
12844
12896
  context.spinner?.succeed("Files written");
12845
12897
  const deps = event.dependencies;
12846
12898
  const devDeps = event.devDependencies;
12899
+ const skippedDeps = event.skipped ?? [];
12847
12900
  if (deps.length > 0 || devDeps.length > 0) {
12848
- console.log(" Dependencies to install:");
12901
+ console.log(" Dependencies installed:");
12849
12902
  for (const dep of deps) {
12850
12903
  console.log(` ${dep}`);
12851
12904
  }
@@ -12853,9 +12906,32 @@ function log(event) {
12853
12906
  console.log(` ${dep} (dev)`);
12854
12907
  }
12855
12908
  }
12856
- context.spinner = ora("Installing dependencies...").start();
12909
+ if (skippedDeps.length > 0) {
12910
+ console.log(" Dependencies skipped (already installed or internal):");
12911
+ for (const dep of skippedDeps) {
12912
+ console.log(` ${dep}`);
12913
+ }
12914
+ }
12857
12915
  break;
12858
12916
  }
12917
+ case "add:deps:no-package-json":
12918
+ console.warn(` Warning: ${event.message}`);
12919
+ break;
12920
+ case "add:deps:dry-run": {
12921
+ const dryDeps = event.dependencies;
12922
+ console.log(" [dry-run] Would install:");
12923
+ for (const dep of dryDeps) {
12924
+ console.log(` ${dep}`);
12925
+ }
12926
+ break;
12927
+ }
12928
+ case "add:deps:install-failed":
12929
+ context.spinner?.fail("Failed to install dependencies");
12930
+ console.log(` ${event.message}`);
12931
+ if (event.suggestion) {
12932
+ console.log(` ${event.suggestion}`);
12933
+ }
12934
+ break;
12859
12935
  case "add:complete":
12860
12936
  context.spinner?.succeed(
12861
12937
  `Added ${event.installed} component${event.installed !== 1 ? "s" : ""}`
@@ -12973,6 +13049,127 @@ async function installWithPackageManager(packageManager, dependencies, options)
12973
13049
  }
12974
13050
  }
12975
13051
 
13052
+ // src/utils/install-registry-deps.ts
13053
+ function parseDependency(dep) {
13054
+ const trimmed = dep.trim();
13055
+ if (!trimmed) {
13056
+ return { name: "", version: void 0 };
13057
+ }
13058
+ const versionAt = trimmed.lastIndexOf("@");
13059
+ if (versionAt <= 0) {
13060
+ return { name: trimmed, version: void 0 };
13061
+ }
13062
+ return {
13063
+ name: trimmed.slice(0, versionAt),
13064
+ version: trimmed.slice(versionAt + 1)
13065
+ };
13066
+ }
13067
+ async function readInstalledDeps(targetDir) {
13068
+ let raw;
13069
+ try {
13070
+ raw = await readFile(join(targetDir, "package.json"), "utf-8");
13071
+ } catch {
13072
+ return { packageJsonFound: false, installed: /* @__PURE__ */ new Set() };
13073
+ }
13074
+ try {
13075
+ const pkg = JSON.parse(raw);
13076
+ const installed = /* @__PURE__ */ new Set();
13077
+ const depFields = ["dependencies", "devDependencies", "peerDependencies"];
13078
+ for (const field of depFields) {
13079
+ const section = pkg[field];
13080
+ if (section && typeof section === "object") {
13081
+ for (const name2 of Object.keys(section)) {
13082
+ installed.add(name2);
13083
+ }
13084
+ }
13085
+ }
13086
+ return { packageJsonFound: true, installed };
13087
+ } catch {
13088
+ return { packageJsonFound: true, installed: /* @__PURE__ */ new Set() };
13089
+ }
13090
+ }
13091
+ async function installRegistryDependencies(items, targetDir, options = {}) {
13092
+ const result = {
13093
+ installed: [],
13094
+ skipped: [],
13095
+ devInstalled: [],
13096
+ failed: []
13097
+ };
13098
+ const allDeps = new Set(items.flatMap((item) => item.files.flatMap((file2) => file2.dependencies)));
13099
+ if (allDeps.size === 0) {
13100
+ return result;
13101
+ }
13102
+ const externalDeps = [];
13103
+ for (const dep of allDeps) {
13104
+ if (parseDependency(dep).name.startsWith("@rafters/")) {
13105
+ result.skipped.push(dep);
13106
+ } else {
13107
+ externalDeps.push(dep);
13108
+ }
13109
+ }
13110
+ if (externalDeps.length === 0) {
13111
+ return result;
13112
+ }
13113
+ const { packageJsonFound, installed: installedInProject } = await readInstalledDeps(targetDir);
13114
+ const toInstall = [];
13115
+ if (!packageJsonFound) {
13116
+ log({
13117
+ event: "add:deps:no-package-json",
13118
+ message: "No package.json found. Run npm init or pnpm init first.",
13119
+ targetDir
13120
+ });
13121
+ toInstall.push(...externalDeps);
13122
+ } else {
13123
+ for (const dep of externalDeps) {
13124
+ const { name: name2 } = parseDependency(dep);
13125
+ if (installedInProject.has(name2)) {
13126
+ result.skipped.push(dep);
13127
+ } else {
13128
+ toInstall.push(dep);
13129
+ }
13130
+ }
13131
+ }
13132
+ if (toInstall.length === 0) {
13133
+ return result;
13134
+ }
13135
+ if (options.dryRun) {
13136
+ log({
13137
+ event: "add:deps:dry-run",
13138
+ dependencies: toInstall
13139
+ });
13140
+ return result;
13141
+ }
13142
+ try {
13143
+ await updateDependencies(toInstall, [], {
13144
+ cwd: targetDir,
13145
+ silent: options.silent ?? false
13146
+ });
13147
+ result.installed = toInstall;
13148
+ } catch (err) {
13149
+ result.failed = toInstall;
13150
+ const message = err instanceof Error ? err.message : String(err);
13151
+ log({
13152
+ event: "add:deps:install-failed",
13153
+ message: `Failed to install dependencies: ${message}`,
13154
+ dependencies: toInstall,
13155
+ suggestion: `Manually install: ${toInstall.join(" ")}`
13156
+ });
13157
+ }
13158
+ return result;
13159
+ }
13160
+
13161
+ // src/utils/paths.ts
13162
+ import { join as join2 } from "path";
13163
+ function getRaftersPaths(projectRoot = process.cwd()) {
13164
+ const root = join2(projectRoot, ".rafters");
13165
+ return {
13166
+ root,
13167
+ config: join2(root, "config.rafters.json"),
13168
+ tokens: join2(root, "tokens"),
13169
+ output: join2(root, "output")
13170
+ };
13171
+ }
13172
+
12976
13173
  // src/commands/add.ts
12977
13174
  async function isInitialized(cwd) {
12978
13175
  const paths = getRaftersPaths(cwd);
@@ -12986,7 +13183,7 @@ async function isInitialized(cwd) {
12986
13183
  async function loadConfig(cwd) {
12987
13184
  const paths = getRaftersPaths(cwd);
12988
13185
  try {
12989
- const content = await readFile(paths.config, "utf-8");
13186
+ const content = await readFile2(paths.config, "utf-8");
12990
13187
  return JSON.parse(content);
12991
13188
  } catch (err) {
12992
13189
  if (existsSync(paths.config)) {
@@ -12996,6 +13193,40 @@ async function loadConfig(cwd) {
12996
13193
  return null;
12997
13194
  }
12998
13195
  }
13196
+ async function saveConfig(cwd, config3) {
13197
+ const paths = getRaftersPaths(cwd);
13198
+ await writeFile(paths.config, JSON.stringify(config3, null, 2));
13199
+ }
13200
+ function getInstalledNames(config3) {
13201
+ if (!config3?.installed) return [];
13202
+ const names2 = /* @__PURE__ */ new Set([...config3.installed.components, ...config3.installed.primitives]);
13203
+ return [...names2].sort();
13204
+ }
13205
+ function isAlreadyInstalled(config3, item) {
13206
+ if (!config3?.installed) return false;
13207
+ if (item.type === "registry:ui") {
13208
+ return config3.installed.components.includes(item.name);
13209
+ }
13210
+ return config3.installed.primitives.includes(item.name);
13211
+ }
13212
+ function trackInstalled(config3, items) {
13213
+ if (!config3.installed) {
13214
+ config3.installed = { components: [], primitives: [] };
13215
+ }
13216
+ for (const item of items) {
13217
+ if (item.type === "registry:ui") {
13218
+ if (!config3.installed.components.includes(item.name)) {
13219
+ config3.installed.components.push(item.name);
13220
+ }
13221
+ } else {
13222
+ if (!config3.installed.primitives.includes(item.name)) {
13223
+ config3.installed.primitives.push(item.name);
13224
+ }
13225
+ }
13226
+ }
13227
+ config3.installed.components.sort();
13228
+ config3.installed.primitives.sort();
13229
+ }
12999
13230
  function transformPath(registryPath, config3) {
13000
13231
  if (!config3) return registryPath;
13001
13232
  if (registryPath.startsWith("components/ui/")) {
@@ -13007,7 +13238,7 @@ function transformPath(registryPath, config3) {
13007
13238
  return registryPath;
13008
13239
  }
13009
13240
  function fileExists(cwd, relativePath) {
13010
- return existsSync(join2(cwd, relativePath));
13241
+ return existsSync(join3(cwd, relativePath));
13011
13242
  }
13012
13243
  function transformFileContent(content, config3) {
13013
13244
  let transformed = content;
@@ -13047,7 +13278,7 @@ async function installItem(cwd, item, options, config3) {
13047
13278
  let skipped = false;
13048
13279
  for (const file2 of item.files) {
13049
13280
  const projectPath = transformPath(file2.path, config3);
13050
- const targetPath = join2(cwd, projectPath);
13281
+ const targetPath = join3(cwd, projectPath);
13051
13282
  if (fileExists(cwd, projectPath)) {
13052
13283
  if (!options.overwrite) {
13053
13284
  log({
@@ -13071,23 +13302,9 @@ async function installItem(cwd, item, options, config3) {
13071
13302
  files: installedFiles
13072
13303
  };
13073
13304
  }
13074
- function collectDependencies(items) {
13075
- const deps = /* @__PURE__ */ new Set();
13076
- const devDeps = /* @__PURE__ */ new Set();
13077
- for (const item of items) {
13078
- for (const file2 of item.files) {
13079
- for (const dep of file2.dependencies) {
13080
- deps.add(dep);
13081
- }
13082
- }
13083
- }
13084
- return {
13085
- dependencies: [...deps].sort(),
13086
- devDependencies: [...devDeps].sort()
13087
- };
13088
- }
13089
- async function add(components, options) {
13305
+ async function add(componentArgs, options) {
13090
13306
  setAgentMode(options.agent ?? false);
13307
+ let components = componentArgs;
13091
13308
  const client = new RegistryClient(options.registryUrl);
13092
13309
  if (options.list) {
13093
13310
  const availableComponents = await client.listComponents();
@@ -13109,6 +13326,24 @@ async function add(components, options) {
13109
13326
  return;
13110
13327
  }
13111
13328
  const config3 = await loadConfig(cwd);
13329
+ if (options.update) {
13330
+ options.overwrite = true;
13331
+ }
13332
+ if (options.updateAll) {
13333
+ options.overwrite = true;
13334
+ if (!config3) {
13335
+ error46("No rafters config found. Run 'rafters init' first.");
13336
+ process.exitCode = 1;
13337
+ return;
13338
+ }
13339
+ const installedNames = getInstalledNames(config3);
13340
+ if (installedNames.length === 0) {
13341
+ error46("No installed components found. Use 'rafters add <component>' to install first.");
13342
+ process.exitCode = 1;
13343
+ return;
13344
+ }
13345
+ components = installedNames;
13346
+ }
13112
13347
  if (components.length === 0) {
13113
13348
  error46("No components specified. Usage: rafters add <component...>");
13114
13349
  process.exitCode = 1;
@@ -13138,11 +13373,22 @@ async function add(components, options) {
13138
13373
  }
13139
13374
  const installed = [];
13140
13375
  const skipped = [];
13376
+ const installedItems = [];
13141
13377
  for (const item of allItems) {
13378
+ if (!options.overwrite && isAlreadyInstalled(config3, item)) {
13379
+ log({
13380
+ event: "add:skip",
13381
+ component: item.name,
13382
+ reason: "already installed"
13383
+ });
13384
+ skipped.push(item.name);
13385
+ continue;
13386
+ }
13142
13387
  try {
13143
13388
  const result = await installItem(cwd, item, options, config3);
13144
13389
  if (result.installed) {
13145
13390
  installed.push(item.name);
13391
+ installedItems.push(item);
13146
13392
  log({
13147
13393
  event: "add:installed",
13148
13394
  component: item.name,
@@ -13163,22 +13409,47 @@ async function add(components, options) {
13163
13409
  }
13164
13410
  }
13165
13411
  }
13166
- const { dependencies, devDependencies } = collectDependencies(allItems);
13167
- if (dependencies.length > 0 || devDependencies.length > 0) {
13412
+ const emptyDeps = {
13413
+ installed: [],
13414
+ skipped: [],
13415
+ devInstalled: [],
13416
+ failed: []
13417
+ };
13418
+ let depsResult = emptyDeps;
13419
+ try {
13420
+ depsResult = await installRegistryDependencies(allItems, cwd);
13421
+ } catch (err) {
13422
+ const message = err instanceof Error ? err.message : String(err);
13423
+ log({
13424
+ event: "add:deps:install-failed",
13425
+ message: `Failed to process dependencies: ${message}`,
13426
+ dependencies: [],
13427
+ suggestion: "Check package.json and try installing dependencies manually."
13428
+ });
13429
+ }
13430
+ if (depsResult.installed.length > 0 || depsResult.skipped.length > 0) {
13168
13431
  log({
13169
13432
  event: "add:dependencies",
13170
- dependencies,
13171
- devDependencies
13433
+ dependencies: depsResult.installed,
13434
+ devDependencies: depsResult.devInstalled,
13435
+ skipped: depsResult.skipped
13172
13436
  });
13173
- try {
13174
- await updateDependencies(dependencies, devDependencies, { cwd });
13175
- } catch (err) {
13176
- log({
13177
- event: "add:error",
13178
- message: "Failed to install dependencies",
13179
- error: err instanceof Error ? err.message : String(err)
13180
- });
13181
- }
13437
+ }
13438
+ if (installedItems.length > 0 && config3) {
13439
+ trackInstalled(config3, installedItems);
13440
+ await saveConfig(cwd, config3);
13441
+ } else if (installedItems.length > 0 && !config3) {
13442
+ const newConfig = {
13443
+ framework: "unknown",
13444
+ componentsPath: "components/ui",
13445
+ primitivesPath: "lib/primitives",
13446
+ cssPath: null,
13447
+ shadcn: false,
13448
+ exports: DEFAULT_EXPORTS,
13449
+ installed: { components: [], primitives: [] }
13450
+ };
13451
+ trackInstalled(newConfig, installedItems);
13452
+ await saveConfig(cwd, newConfig);
13182
13453
  }
13183
13454
  log({
13184
13455
  event: "add:complete",
@@ -13189,19 +13460,19 @@ async function add(components, options) {
13189
13460
  if (skipped.length > 0 && installed.length === 0) {
13190
13461
  log({
13191
13462
  event: "add:hint",
13192
- message: "Some components were skipped. Use --overwrite to replace existing files.",
13463
+ message: "Some components were skipped. Use --update to re-fetch, or --update-all to refresh everything.",
13193
13464
  skipped
13194
13465
  });
13195
- error46("Component already exists. Use --overwrite to replace.");
13466
+ error46("Component already exists. Use --update to re-fetch from registry.");
13196
13467
  process.exitCode = 1;
13197
13468
  }
13198
13469
  }
13199
13470
 
13200
13471
  // src/commands/init.ts
13201
13472
  import { existsSync as existsSync2 } from "fs";
13202
- import { copyFile, mkdir as mkdir3, readFile as readFile4, rm, writeFile as writeFile3 } from "fs/promises";
13473
+ import { copyFile, mkdir as mkdir3, readFile as readFile5, rm, writeFile as writeFile3 } from "fs/promises";
13203
13474
  import { createRequire } from "module";
13204
- import { join as join8, relative } from "path";
13475
+ import { join as join9, relative } from "path";
13205
13476
  import { checkbox, confirm } from "@inquirer/prompts";
13206
13477
 
13207
13478
  // ../design-tokens/src/plugins/scale.ts
@@ -24332,7 +24603,7 @@ var require_volume = __commonJS({
24332
24603
  var Dir_1 = require_Dir();
24333
24604
  var resolveCrossPlatform = pathModule.resolve;
24334
24605
  var { O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_TRUNC, O_APPEND, O_DIRECTORY, O_SYMLINK, F_OK, COPYFILE_EXCL, COPYFILE_FICLONE_FORCE } = constants_1.constants;
24335
- var { sep: sep2, relative: relative2, join: join11, dirname: dirname4 } = pathModule.posix ? pathModule.posix : pathModule;
24606
+ var { sep: sep2, relative: relative2, join: join12, dirname: dirname4 } = pathModule.posix ? pathModule.posix : pathModule;
24336
24607
  var kMinPoolSpace = 128;
24337
24608
  var EPERM = "EPERM";
24338
24609
  var ENOENT2 = "ENOENT";
@@ -24398,7 +24669,7 @@ var require_volume = __commonJS({
24398
24669
  function flatten(pathPrefix, node) {
24399
24670
  for (const path2 in node) {
24400
24671
  const contentOrNode = node[path2];
24401
- const joinedPath = join11(pathPrefix, path2);
24672
+ const joinedPath = join12(pathPrefix, path2);
24402
24673
  if (typeof contentOrNode === "string" || contentOrNode instanceof buffer_1.Buffer) {
24403
24674
  flatJSON[joinedPath] = contentOrNode;
24404
24675
  } else if (typeof contentOrNode === "object" && contentOrNode !== null && Object.keys(contentOrNode).length > 0) {
@@ -24555,7 +24826,7 @@ var require_volume = __commonJS({
24555
24826
  return null;
24556
24827
  node = curr === null || curr === void 0 ? void 0 : curr.getNode();
24557
24828
  if (resolveSymlinks && node.isSymlink()) {
24558
- const resolvedPath = pathModule.isAbsolute(node.symlink) ? node.symlink : join11(pathModule.dirname(curr.getPath()), node.symlink);
24829
+ const resolvedPath = pathModule.isAbsolute(node.symlink) ? node.symlink : join12(pathModule.dirname(curr.getPath()), node.symlink);
24559
24830
  steps = filenameToSteps(resolvedPath).concat(steps.slice(i + 1));
24560
24831
  curr = this.root;
24561
24832
  i = 0;
@@ -27479,7 +27750,7 @@ var posix = {
27479
27750
 
27480
27751
  // ../../node_modules/.pnpm/path-unified@0.2.0/node_modules/path-unified/src/posix.js
27481
27752
  var resolve = posResolve;
27482
- var join3 = posJoin;
27753
+ var join4 = posJoin;
27483
27754
 
27484
27755
  // ../../node_modules/.pnpm/@isaacs+balanced-match@4.0.1/node_modules/@isaacs/balanced-match/dist/esm/index.js
27485
27756
  var balanced = (a, b, str) => {
@@ -37378,7 +37649,7 @@ var transforms_default = {
37378
37649
  type: value,
37379
37650
  filter: isAsset,
37380
37651
  transform: function(token, _, options) {
37381
- return join3(process?.cwd() ?? "/", options.usesDtcg ? token.$value : token.value);
37652
+ return join4(process?.cwd() ?? "/", options.usesDtcg ? token.$value : token.value);
37382
37653
  }
37383
37654
  },
37384
37655
  /**
@@ -43578,12 +43849,12 @@ var formats_default = formats2;
43578
43849
  var { androidCopyImages, copyAssets } = actions;
43579
43850
  var { silent } = logVerbosityLevels;
43580
43851
  function getAssetDir(config3) {
43581
- return join3(config3.buildPath ?? "", "android/main/res/drawable-").replace(/\\/g, "/");
43852
+ return join4(config3.buildPath ?? "", "android/main/res/drawable-").replace(/\\/g, "/");
43582
43853
  }
43583
43854
  function getAssetPath(token, imagesDir) {
43584
43855
  const name2 = token.path.slice(2, 4).join("_");
43585
43856
  const dir = `${imagesDir}${token.attributes?.state}`;
43586
- return { file: join3(dir, `${name2}.png`), dir };
43857
+ return { file: join4(dir, `${name2}.png`), dir };
43587
43858
  }
43588
43859
  var actions_default = {
43589
43860
  /**
@@ -43624,7 +43895,7 @@ var actions_default = {
43624
43895
  */
43625
43896
  [copyAssets]: {
43626
43897
  do: async function(_, config3, _options, vol2 = fs2) {
43627
- const assetsPath = join3(config3.buildPath ?? "", "assets");
43898
+ const assetsPath = join4(config3.buildPath ?? "", "assets");
43628
43899
  if (config3.log?.verbosity !== silent) {
43629
43900
  console.log(`Copying assets directory to ${assetsPath}`);
43630
43901
  }
@@ -43638,7 +43909,7 @@ var actions_default = {
43638
43909
  );
43639
43910
  },
43640
43911
  undo: async function(_, config3, _options, vol2 = fs2) {
43641
- const assetsPath = join3(config3.buildPath ?? "", "assets");
43912
+ const assetsPath = join4(config3.buildPath ?? "", "assets");
43642
43913
  if (config3.log?.verbosity !== silent) {
43643
43914
  console.log(`Removing assets directory from ${assetsPath}`);
43644
43915
  }
@@ -44119,8 +44390,14 @@ var DEFAULT_DEPTH_DEFINITIONS = {
44119
44390
  },
44120
44391
  sticky: {
44121
44392
  value: 20,
44122
- meaning: "Sticky elements - headers, navigation",
44123
- contexts: ["sticky-header", "sticky-nav", "floating-actions"],
44393
+ meaning: "Sticky elements - headers, toolbars",
44394
+ contexts: ["sticky-header", "sticky-toolbar", "floating-actions"],
44395
+ stackingContext: true
44396
+ },
44397
+ navigation: {
44398
+ value: 25,
44399
+ meaning: "Navigation panels - sidebars, slide-out nav",
44400
+ contexts: ["sidebar", "navigation-panel", "slide-out-menu"],
44124
44401
  stackingContext: true
44125
44402
  },
44126
44403
  fixed: {
@@ -44146,6 +44423,12 @@ var DEFAULT_DEPTH_DEFINITIONS = {
44146
44423
  meaning: "Tooltips - highest common layer",
44147
44424
  contexts: ["tooltips", "toast-notifications"],
44148
44425
  stackingContext: true
44426
+ },
44427
+ overlay: {
44428
+ value: 70,
44429
+ meaning: "Overlay backdrops - screen-dimming layers behind modals",
44430
+ contexts: ["modal-backdrop", "drawer-backdrop", "sheet-backdrop"],
44431
+ stackingContext: true
44149
44432
  }
44150
44433
  };
44151
44434
  var DEFAULT_SHADOW_DEFINITIONS = {
@@ -46443,6 +46726,78 @@ function generateThemeBlock(groups) {
46443
46726
  lines.push("}");
46444
46727
  return lines.join("\n");
46445
46728
  }
46729
+ var ARTICLE_ELEMENT_STYLES = [
46730
+ // Paragraphs
46731
+ ["p", "leading-relaxed mb-4"],
46732
+ ["p:last-child", "mb-0"],
46733
+ // Headings
46734
+ ["h1", "text-4xl font-bold tracking-tight mb-4 mt-0 text-accent-foreground"],
46735
+ ["h2", "text-3xl font-semibold tracking-tight mb-3 mt-8 text-accent-foreground"],
46736
+ ["h2:first-child", "mt-0"],
46737
+ ["h3", "text-2xl font-semibold mb-2 mt-6 text-accent-foreground"],
46738
+ ["h4", "text-xl font-semibold mb-2 mt-4 text-accent-foreground"],
46739
+ ["h5", "text-lg font-semibold mb-2 mt-4 text-accent-foreground"],
46740
+ ["h6", "text-base font-semibold mb-2 mt-4 text-accent-foreground"],
46741
+ // Lists
46742
+ ["ul", "list-disc pl-6 mb-4"],
46743
+ ["ol", "list-decimal pl-6 mb-4"],
46744
+ ["li", "mb-1"],
46745
+ ["li > ul,\n article li > ol", "mt-1 mb-0"],
46746
+ // Links
46747
+ ["a", "text-primary underline underline-offset-4"],
46748
+ ["a:hover", "text-primary/80"],
46749
+ // Blockquotes
46750
+ ["blockquote", "border-l-4 border-muted pl-4 italic my-4"],
46751
+ // Code
46752
+ ["code", "bg-muted px-1.5 py-0.5 rounded text-sm font-mono"],
46753
+ ["pre", "bg-muted p-4 rounded-lg overflow-x-auto my-4 text-sm font-mono"],
46754
+ ["pre code", "bg-transparent p-0 rounded-none text-[inherit]"],
46755
+ ["kbd", "bg-muted border border-border rounded px-1.5 py-0.5 text-sm font-mono"],
46756
+ // Horizontal rules
46757
+ ["hr", "border-border my-8"],
46758
+ // Media
46759
+ ["img", "rounded-lg my-4 max-w-full h-auto"],
46760
+ ["video", "rounded-lg my-4 max-w-full h-auto"],
46761
+ // Tables
46762
+ ["table", "w-full my-4 border-collapse"],
46763
+ ["caption", "mt-2 text-sm text-muted-foreground text-left"],
46764
+ ["th", "border border-border px-3 py-2 text-left font-semibold"],
46765
+ ["td", "border border-border px-3 py-2"],
46766
+ // Figures
46767
+ ["figure", "my-4"],
46768
+ ["figcaption", "mt-2 text-sm text-muted-foreground"],
46769
+ // Definition lists
46770
+ ["dl", "my-4"],
46771
+ ["dt", "font-semibold mt-2"],
46772
+ ["dd", "pl-4 mb-2"],
46773
+ // Details/Summary
46774
+ ["details", "my-4"],
46775
+ ["summary", "cursor-pointer font-semibold"],
46776
+ // Inline formatting
46777
+ ["strong,\n article b", "font-semibold"],
46778
+ ["mark", "bg-accent text-accent-foreground px-1 rounded"],
46779
+ ["small", "text-sm"],
46780
+ ["sub", "text-xs align-sub"],
46781
+ ["sup", "text-xs align-super"],
46782
+ ["abbr[title]", "underline decoration-dotted underline-offset-4 cursor-help"],
46783
+ ["s,\n article del", "line-through"],
46784
+ ["ins", "underline"]
46785
+ ];
46786
+ function generateArticleBaseLayer() {
46787
+ const lines = [];
46788
+ lines.push("@layer base {");
46789
+ for (const [selector, utilities] of ARTICLE_ELEMENT_STYLES) {
46790
+ if (selector.includes("\n")) {
46791
+ lines.push(` article ${selector} {`);
46792
+ } else {
46793
+ lines.push(` article ${selector} {`);
46794
+ }
46795
+ lines.push(` @apply ${utilities};`);
46796
+ lines.push(" }");
46797
+ }
46798
+ lines.push("}");
46799
+ return lines.join("\n");
46800
+ }
46446
46801
  function generateKeyframes(motionTokens) {
46447
46802
  const keyframeTokens = motionTokens.filter((t2) => t2.name.startsWith("motion-keyframe-"));
46448
46803
  if (keyframeTokens.length === 0) {
@@ -46494,6 +46849,8 @@ function tokensToTailwind(tokens, options = {}) {
46494
46849
  if (keyframes) {
46495
46850
  sections.push(keyframes);
46496
46851
  }
46852
+ sections.push("");
46853
+ sections.push(generateArticleBaseLayer());
46497
46854
  return sections.join("\n");
46498
46855
  }
46499
46856
  function registryToTailwind(registry2, options) {
@@ -46505,7 +46862,7 @@ async function registryToCompiled(registry2, options = {}) {
46505
46862
  const themeCss = registryToTailwind(registry2, { includeImport });
46506
46863
  const { execFileSync } = await import("child_process");
46507
46864
  const { mkdtempSync, writeFileSync, readFileSync, rmSync } = await import("fs");
46508
- const { join: join11, dirname: dirname4 } = await import("path");
46865
+ const { join: join12, dirname: dirname4 } = await import("path");
46509
46866
  const { createRequire: createRequire2 } = await import("module");
46510
46867
  const require2 = createRequire2(import.meta.url);
46511
46868
  let pkgDir;
@@ -46515,10 +46872,10 @@ async function registryToCompiled(registry2, options = {}) {
46515
46872
  } catch {
46516
46873
  throw new Error("Failed to resolve @tailwindcss/cli");
46517
46874
  }
46518
- const binPath = join11(pkgDir, "dist", "index.mjs");
46519
- const tempDir = mkdtempSync(join11(pkgDir, ".tmp-compile-"));
46520
- const tempInput = join11(tempDir, "input.css");
46521
- const tempOutput = join11(tempDir, "output.css");
46875
+ const binPath = join12(pkgDir, "dist", "index.mjs");
46876
+ const tempDir = mkdtempSync(join12(pkgDir, ".tmp-compile-"));
46877
+ const tempInput = join12(tempDir, "input.css");
46878
+ const tempOutput = join12(tempDir, "output.css");
46522
46879
  try {
46523
46880
  writeFileSync(tempInput, themeCss);
46524
46881
  const args = [binPath, "-i", tempInput, "-o", tempOutput];
@@ -46797,7 +47154,7 @@ function tagTokenizer() {
46797
47154
 
46798
47155
  // ../../node_modules/.pnpm/comment-parser@1.4.1/node_modules/comment-parser/es6/parser/tokenizers/type.js
46799
47156
  function typeTokenizer(spacing = "compact") {
46800
- const join11 = getJoiner(spacing);
47157
+ const join12 = getJoiner(spacing);
46801
47158
  return (spec) => {
46802
47159
  let curlies = 0;
46803
47160
  let lines = [];
@@ -46840,7 +47197,7 @@ function typeTokenizer(spacing = "compact") {
46840
47197
  }
46841
47198
  parts[0] = parts[0].slice(1);
46842
47199
  parts[parts.length - 1] = parts[parts.length - 1].slice(0, -1);
46843
- spec.type = join11(parts);
47200
+ spec.type = join12(parts);
46844
47201
  return spec;
46845
47202
  };
46846
47203
  }
@@ -46938,9 +47295,9 @@ function nameTokenizer() {
46938
47295
 
46939
47296
  // ../../node_modules/.pnpm/comment-parser@1.4.1/node_modules/comment-parser/es6/parser/tokenizers/description.js
46940
47297
  function descriptionTokenizer(spacing = "compact", markers = Markers) {
46941
- const join11 = getJoiner2(spacing);
47298
+ const join12 = getJoiner2(spacing);
46942
47299
  return (spec) => {
46943
- spec.description = join11(spec.source, markers);
47300
+ spec.description = join12(spec.source, markers);
46944
47301
  return spec;
46945
47302
  };
46946
47303
  }
@@ -47001,11 +47358,11 @@ function getParser4({ startLine = 0, fence = "```", spacing = "compact", markers
47001
47358
  }
47002
47359
 
47003
47360
  // ../../node_modules/.pnpm/comment-parser@1.4.1/node_modules/comment-parser/es6/stringifier/index.js
47004
- function join4(tokens) {
47361
+ function join5(tokens) {
47005
47362
  return tokens.start + tokens.delimiter + tokens.postDelimiter + tokens.tag + tokens.postTag + tokens.type + tokens.postType + tokens.name + tokens.postName + tokens.description + tokens.end + tokens.lineEnd;
47006
47363
  }
47007
47364
  function getStringifier() {
47008
- return (block) => block.source.map(({ tokens }) => join4(tokens)).join("\n");
47365
+ return (block) => block.source.map(({ tokens }) => join5(tokens)).join("\n");
47009
47366
  }
47010
47367
 
47011
47368
  // ../../node_modules/.pnpm/comment-parser@1.4.1/node_modules/comment-parser/es6/stringifier/inspect.js
@@ -47174,6 +47531,32 @@ function extractPrimitiveDependencies(source) {
47174
47531
  }
47175
47532
  return primitives;
47176
47533
  }
47534
+ var DEP_TAG_MAP = {
47535
+ dependencies: "runtime",
47536
+ devdependencies: "dev",
47537
+ "internal-dependencies": "internal",
47538
+ internaldependencies: "internal"
47539
+ };
47540
+ function extractJSDocDependencies(source) {
47541
+ const result = { runtime: [], dev: [], internal: [] };
47542
+ const blocks = parse3(source);
47543
+ if (blocks.length === 0) return result;
47544
+ for (const block of blocks) {
47545
+ for (const tag of block.tags) {
47546
+ const field = DEP_TAG_MAP[tag.tag.toLowerCase()];
47547
+ if (!field) continue;
47548
+ const value2 = getTagValue(tag).trim();
47549
+ if (value2) {
47550
+ result[field].push(...value2.split(/\s+/).filter(Boolean));
47551
+ }
47552
+ }
47553
+ }
47554
+ return {
47555
+ runtime: [...new Set(result.runtime)],
47556
+ dev: [...new Set(result.dev)],
47557
+ internal: [...new Set(result.internal)]
47558
+ };
47559
+ }
47177
47560
  function toDisplayName(name2) {
47178
47561
  return name2.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
47179
47562
  }
@@ -48263,10 +48646,12 @@ var DEPTH_LEVELS = [
48263
48646
  "base",
48264
48647
  "dropdown",
48265
48648
  "sticky",
48649
+ "navigation",
48266
48650
  "fixed",
48267
48651
  "modal",
48268
48652
  "popover",
48269
- "tooltip"
48653
+ "tooltip",
48654
+ "overlay"
48270
48655
  ];
48271
48656
  var ELEVATION_LEVELS = [
48272
48657
  "surface",
@@ -50325,14 +50710,14 @@ function buildColorSystem(options = {}) {
50325
50710
  }
50326
50711
 
50327
50712
  // ../design-tokens/src/persistence/node-adapter.ts
50328
- import { mkdir as mkdir2, readdir as readdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
50329
- import { join as join5 } from "path";
50713
+ import { mkdir as mkdir2, readdir as readdir2, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
50714
+ import { join as join6 } from "path";
50330
50715
  var SCHEMA_URL = "https://rafters.studio/schemas/namespace-tokens.json";
50331
50716
  var VERSION = "1.0.0";
50332
50717
  var NodePersistenceAdapter = class {
50333
50718
  tokensDir;
50334
50719
  constructor(projectRoot) {
50335
- this.tokensDir = join5(projectRoot, ".rafters", "tokens");
50720
+ this.tokensDir = join6(projectRoot, ".rafters", "tokens");
50336
50721
  }
50337
50722
  async load() {
50338
50723
  let files;
@@ -50344,7 +50729,7 @@ var NodePersistenceAdapter = class {
50344
50729
  const allTokens = [];
50345
50730
  for (const file2 of files) {
50346
50731
  if (!file2.endsWith(".rafters.json")) continue;
50347
- const content = await readFile2(join5(this.tokensDir, file2), "utf-8");
50732
+ const content = await readFile3(join6(this.tokensDir, file2), "utf-8");
50348
50733
  const data = NamespaceFileSchema.parse(JSON.parse(content));
50349
50734
  allTokens.push(...data.tokens);
50350
50735
  }
@@ -50372,7 +50757,7 @@ var NodePersistenceAdapter = class {
50372
50757
  };
50373
50758
  NamespaceFileSchema.parse(data);
50374
50759
  await writeFile2(
50375
- join5(this.tokensDir, `${namespace}.rafters.json`),
50760
+ join6(this.tokensDir, `${namespace}.rafters.json`),
50376
50761
  JSON.stringify(data, null, 2)
50377
50762
  );
50378
50763
  }
@@ -50381,7 +50766,7 @@ var NodePersistenceAdapter = class {
50381
50766
 
50382
50767
  // ../design-tokens/src/rule-engine.ts
50383
50768
  import { promises as fs3 } from "fs";
50384
- import { join as join6 } from "path";
50769
+ import { join as join7 } from "path";
50385
50770
  var RuleResultSchema = external_exports.union([
50386
50771
  external_exports.string(),
50387
50772
  external_exports.object({
@@ -50397,11 +50782,11 @@ var RuleContextSchema = external_exports.object({
50397
50782
  });
50398
50783
 
50399
50784
  // src/utils/detect.ts
50400
- import { readFile as readFile3 } from "fs/promises";
50401
- import { join as join7 } from "path";
50785
+ import { readFile as readFile4 } from "fs/promises";
50786
+ import { join as join8 } from "path";
50402
50787
  async function detectFramework(cwd) {
50403
50788
  try {
50404
- const content = await readFile3(join7(cwd, "package.json"), "utf-8");
50789
+ const content = await readFile4(join8(cwd, "package.json"), "utf-8");
50405
50790
  const pkg = JSON.parse(content);
50406
50791
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
50407
50792
  if (deps.next) {
@@ -50427,7 +50812,7 @@ async function detectFramework(cwd) {
50427
50812
  }
50428
50813
  async function detectTailwindVersion(cwd) {
50429
50814
  try {
50430
- const content = await readFile3(join7(cwd, "package.json"), "utf-8");
50815
+ const content = await readFile4(join8(cwd, "package.json"), "utf-8");
50431
50816
  const pkg = JSON.parse(content);
50432
50817
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
50433
50818
  const tailwindVersion = deps.tailwindcss;
@@ -50448,7 +50833,7 @@ function isTailwindV3(version2) {
50448
50833
  }
50449
50834
  async function detectShadcn(cwd) {
50450
50835
  try {
50451
- const content = await readFile3(join7(cwd, "components.json"), "utf-8");
50836
+ const content = await readFile4(join8(cwd, "components.json"), "utf-8");
50452
50837
  return JSON.parse(content);
50453
50838
  } catch {
50454
50839
  return null;
@@ -50510,60 +50895,6 @@ async function detectProject(cwd) {
50510
50895
  };
50511
50896
  }
50512
50897
 
50513
- // src/utils/exports.ts
50514
- var DEFAULT_EXPORTS = {
50515
- tailwind: true,
50516
- typescript: true,
50517
- dtcg: false,
50518
- compiled: false
50519
- };
50520
- var EXPORT_CHOICES = [
50521
- {
50522
- name: "Tailwind CSS (web projects)",
50523
- value: "tailwind",
50524
- checked: true
50525
- },
50526
- {
50527
- name: "TypeScript (type-safe constants)",
50528
- value: "typescript",
50529
- checked: true
50530
- },
50531
- {
50532
- name: "DTCG JSON (Figma Tokens, Style Dictionary)",
50533
- value: "dtcg",
50534
- checked: false
50535
- },
50536
- {
50537
- name: "Standalone CSS (pre-built, no Tailwind required)",
50538
- value: "compiled",
50539
- checked: false
50540
- }
50541
- ];
50542
- var FUTURE_EXPORTS = [
50543
- {
50544
- name: "iOS (Swift/SwiftUI)",
50545
- value: "tailwind",
50546
- // placeholder
50547
- checked: false,
50548
- disabled: "coming soon"
50549
- },
50550
- {
50551
- name: "Android (Compose)",
50552
- value: "tailwind",
50553
- // placeholder
50554
- checked: false,
50555
- disabled: "coming soon"
50556
- }
50557
- ];
50558
- function selectionsToConfig(selections) {
50559
- return {
50560
- tailwind: selections.includes("tailwind"),
50561
- typescript: selections.includes("typescript"),
50562
- dtcg: selections.includes("dtcg"),
50563
- compiled: selections.includes("compiled")
50564
- };
50565
- }
50566
-
50567
50898
  // src/commands/init.ts
50568
50899
  async function backupCss(cssPath) {
50569
50900
  const backupPath = cssPath.replace(/\.css$/, ".backup.css");
@@ -50589,7 +50920,7 @@ var COMPONENT_PATHS = {
50589
50920
  async function findMainCssFile(cwd, framework) {
50590
50921
  const locations = CSS_LOCATIONS[framework] || CSS_LOCATIONS.unknown;
50591
50922
  for (const location of locations) {
50592
- const fullPath = join8(cwd, location);
50923
+ const fullPath = join9(cwd, location);
50593
50924
  if (existsSync2(fullPath)) {
50594
50925
  return location;
50595
50926
  }
@@ -50597,10 +50928,10 @@ async function findMainCssFile(cwd, framework) {
50597
50928
  return null;
50598
50929
  }
50599
50930
  async function updateMainCss(cwd, cssPath, themePath) {
50600
- const fullCssPath = join8(cwd, cssPath);
50601
- const cssContent = await readFile4(fullCssPath, "utf-8");
50602
- const cssDir = join8(cwd, cssPath, "..");
50603
- const themeFullPath = join8(cwd, themePath);
50931
+ const fullCssPath = join9(cwd, cssPath);
50932
+ const cssContent = await readFile5(fullCssPath, "utf-8");
50933
+ const cssDir = join9(cwd, cssPath, "..");
50934
+ const themeFullPath = join9(cwd, themePath);
50604
50935
  const relativeThemePath = relative(cssDir, themeFullPath);
50605
50936
  if (cssContent.includes(".rafters/output/rafters.css")) {
50606
50937
  log({ event: "init:css_already_imported", cssPath });
@@ -50688,17 +51019,17 @@ async function generateOutputs(cwd, paths, registry2, exports, shadcn) {
50688
51019
  const outputs = [];
50689
51020
  if (exports.tailwind) {
50690
51021
  const tailwindCss = registryToTailwind(registry2, { includeImport: !shadcn });
50691
- await writeFile3(join8(paths.output, "rafters.css"), tailwindCss);
51022
+ await writeFile3(join9(paths.output, "rafters.css"), tailwindCss);
50692
51023
  outputs.push("rafters.css");
50693
51024
  }
50694
51025
  if (exports.typescript) {
50695
51026
  const typescriptSrc = registryToTypeScript(registry2, { includeJSDoc: true });
50696
- await writeFile3(join8(paths.output, "rafters.ts"), typescriptSrc);
51027
+ await writeFile3(join9(paths.output, "rafters.ts"), typescriptSrc);
50697
51028
  outputs.push("rafters.ts");
50698
51029
  }
50699
51030
  if (exports.dtcg) {
50700
51031
  const dtcgJson = toDTCG(registry2.list());
50701
- await writeFile3(join8(paths.output, "rafters.json"), JSON.stringify(dtcgJson, null, 2));
51032
+ await writeFile3(join9(paths.output, "rafters.json"), JSON.stringify(dtcgJson, null, 2));
50702
51033
  outputs.push("rafters.json");
50703
51034
  }
50704
51035
  if (exports.compiled) {
@@ -50707,7 +51038,7 @@ async function generateOutputs(cwd, paths, registry2, exports, shadcn) {
50707
51038
  }
50708
51039
  log({ event: "init:compiling_css" });
50709
51040
  const compiledCss = await registryToCompiled(registry2, { includeImport: !shadcn });
50710
- await writeFile3(join8(paths.output, "rafters.standalone.css"), compiledCss);
51041
+ await writeFile3(join9(paths.output, "rafters.standalone.css"), compiledCss);
50711
51042
  outputs.push("rafters.standalone.css");
50712
51043
  }
50713
51044
  return outputs;
@@ -50716,7 +51047,7 @@ async function regenerateFromExisting(cwd, paths, shadcn, isAgentMode2) {
50716
51047
  log({ event: "init:regenerate", cwd });
50717
51048
  let existingConfig = null;
50718
51049
  try {
50719
- const configContent = await readFile4(paths.config, "utf-8");
51050
+ const configContent = await readFile5(paths.config, "utf-8");
50720
51051
  existingConfig = JSON.parse(configContent);
50721
51052
  } catch {
50722
51053
  }
@@ -50759,7 +51090,7 @@ async function resetToDefaults(cwd, paths, shadcn, isAgentMode2) {
50759
51090
  log({ event: "init:reset", cwd });
50760
51091
  let existingConfig = null;
50761
51092
  try {
50762
- const configContent = await readFile4(paths.config, "utf-8");
51093
+ const configContent = await readFile5(paths.config, "utf-8");
50763
51094
  existingConfig = JSON.parse(configContent);
50764
51095
  } catch {
50765
51096
  }
@@ -50779,7 +51110,7 @@ async function resetToDefaults(cwd, paths, shadcn, isAgentMode2) {
50779
51110
  };
50780
51111
  await mkdir3(paths.output, { recursive: true });
50781
51112
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
50782
- const backupPath = join8(paths.output, `reset-${timestamp}.json`);
51113
+ const backupPath = join9(paths.output, `reset-${timestamp}.json`);
50783
51114
  await writeFile3(backupPath, JSON.stringify(backup, null, 2));
50784
51115
  log({
50785
51116
  event: "init:reset_backup",
@@ -50867,9 +51198,9 @@ async function init(options) {
50867
51198
  }
50868
51199
  let existingColors = null;
50869
51200
  if (shadcn?.tailwind?.css) {
50870
- const cssPath = join8(cwd, shadcn.tailwind.css);
51201
+ const cssPath = join9(cwd, shadcn.tailwind.css);
50871
51202
  try {
50872
- const cssContent = await readFile4(cssPath, "utf-8");
51203
+ const cssContent = await readFile5(cssPath, "utf-8");
50873
51204
  existingColors = parseCssVariables(cssContent);
50874
51205
  const backupPath = await backupCss(cssPath);
50875
51206
  log({
@@ -50975,7 +51306,11 @@ async function init(options) {
50975
51306
  primitivesPath: frameworkPaths.primitives,
50976
51307
  cssPath: detectedCssPath,
50977
51308
  shadcn: !!shadcn,
50978
- exports
51309
+ exports,
51310
+ installed: {
51311
+ components: [],
51312
+ primitives: []
51313
+ }
50979
51314
  };
50980
51315
  await writeFile3(paths.config, JSON.stringify(config3, null, 2));
50981
51316
  log({
@@ -50991,8 +51326,916 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
50991
51326
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
50992
51327
 
50993
51328
  // src/mcp/tools.ts
50994
- import { readdir as readdir3, readFile as readFile5 } from "fs/promises";
50995
- import { basename, join as join9 } from "path";
51329
+ import { existsSync as existsSync3 } from "fs";
51330
+ import { readdir as readdir3, readFile as readFile6 } from "fs/promises";
51331
+ import { basename, join as join10 } from "path";
51332
+
51333
+ // src/mcp/cognitive-load.ts
51334
+ var BUDGET_TIERS = {
51335
+ focused: 15,
51336
+ page: 30,
51337
+ app: 45
51338
+ };
51339
+ var COMPONENT_SCORES = {
51340
+ // Score 0 -- Structural only, no cognitive demand
51341
+ separator: {
51342
+ dimensions: {
51343
+ decisionDemand: 0,
51344
+ informationDensity: 0,
51345
+ interactionComplexity: 0,
51346
+ contextDisruption: 0,
51347
+ learningCurve: 0
51348
+ },
51349
+ primaryCostDriver: "Structural only, no cognitive demand"
51350
+ },
51351
+ container: {
51352
+ dimensions: {
51353
+ decisionDemand: 0,
51354
+ informationDensity: 0,
51355
+ interactionComplexity: 0,
51356
+ contextDisruption: 0,
51357
+ learningCurve: 0
51358
+ },
51359
+ primaryCostDriver: "Structural only, no cognitive demand"
51360
+ },
51361
+ "aspect-ratio": {
51362
+ dimensions: {
51363
+ decisionDemand: 0,
51364
+ informationDensity: 0,
51365
+ interactionComplexity: 0,
51366
+ contextDisruption: 0,
51367
+ learningCurve: 0
51368
+ },
51369
+ primaryCostDriver: "Structural only, no cognitive demand"
51370
+ },
51371
+ // Score 1 -- Display only, minimal information
51372
+ skeleton: {
51373
+ dimensions: {
51374
+ decisionDemand: 0,
51375
+ informationDensity: 1,
51376
+ interactionComplexity: 0,
51377
+ contextDisruption: 0,
51378
+ learningCurve: 0
51379
+ },
51380
+ primaryCostDriver: "Display only, minimal information"
51381
+ },
51382
+ kbd: {
51383
+ dimensions: {
51384
+ decisionDemand: 0,
51385
+ informationDensity: 1,
51386
+ interactionComplexity: 0,
51387
+ contextDisruption: 0,
51388
+ learningCurve: 0
51389
+ },
51390
+ primaryCostDriver: "Display only, minimal information"
51391
+ },
51392
+ // Score 2 -- Simple state or single information piece
51393
+ badge: {
51394
+ dimensions: {
51395
+ decisionDemand: 0,
51396
+ informationDensity: 1,
51397
+ interactionComplexity: 0,
51398
+ contextDisruption: 0,
51399
+ learningCurve: 1
51400
+ },
51401
+ primaryCostDriver: "Simple state or single information piece"
51402
+ },
51403
+ breadcrumb: {
51404
+ dimensions: {
51405
+ decisionDemand: 0,
51406
+ informationDensity: 1,
51407
+ interactionComplexity: 1,
51408
+ contextDisruption: 0,
51409
+ learningCurve: 0
51410
+ },
51411
+ primaryCostDriver: "Simple state or single information piece"
51412
+ },
51413
+ label: {
51414
+ dimensions: {
51415
+ decisionDemand: 0,
51416
+ informationDensity: 1,
51417
+ interactionComplexity: 0,
51418
+ contextDisruption: 0,
51419
+ learningCurve: 1
51420
+ },
51421
+ primaryCostDriver: "Simple state or single information piece"
51422
+ },
51423
+ checkbox: {
51424
+ dimensions: {
51425
+ decisionDemand: 1,
51426
+ informationDensity: 0,
51427
+ interactionComplexity: 1,
51428
+ contextDisruption: 0,
51429
+ learningCurve: 0
51430
+ },
51431
+ primaryCostDriver: "Simple state or single information piece"
51432
+ },
51433
+ switch: {
51434
+ dimensions: {
51435
+ decisionDemand: 1,
51436
+ informationDensity: 0,
51437
+ interactionComplexity: 1,
51438
+ contextDisruption: 0,
51439
+ learningCurve: 0
51440
+ },
51441
+ primaryCostDriver: "Simple state or single information piece"
51442
+ },
51443
+ toggle: {
51444
+ dimensions: {
51445
+ decisionDemand: 1,
51446
+ informationDensity: 0,
51447
+ interactionComplexity: 1,
51448
+ contextDisruption: 0,
51449
+ learningCurve: 0
51450
+ },
51451
+ primaryCostDriver: "Simple state or single information piece"
51452
+ },
51453
+ "button-group": {
51454
+ dimensions: {
51455
+ decisionDemand: 1,
51456
+ informationDensity: 1,
51457
+ interactionComplexity: 0,
51458
+ contextDisruption: 0,
51459
+ learningCurve: 0
51460
+ },
51461
+ primaryCostDriver: "Simple state or single information piece"
51462
+ },
51463
+ card: {
51464
+ dimensions: {
51465
+ decisionDemand: 0,
51466
+ informationDensity: 1,
51467
+ interactionComplexity: 0,
51468
+ contextDisruption: 0,
51469
+ learningCurve: 1
51470
+ },
51471
+ primaryCostDriver: "Simple state or single information piece"
51472
+ },
51473
+ collapsible: {
51474
+ dimensions: {
51475
+ decisionDemand: 1,
51476
+ informationDensity: 0,
51477
+ interactionComplexity: 1,
51478
+ contextDisruption: 0,
51479
+ learningCurve: 0
51480
+ },
51481
+ primaryCostDriver: "Simple state or single information piece"
51482
+ },
51483
+ avatar: {
51484
+ dimensions: {
51485
+ decisionDemand: 0,
51486
+ informationDensity: 1,
51487
+ interactionComplexity: 0,
51488
+ contextDisruption: 0,
51489
+ learningCurve: 1
51490
+ },
51491
+ primaryCostDriver: "Simple state or single information piece"
51492
+ },
51493
+ "scroll-area": {
51494
+ dimensions: {
51495
+ decisionDemand: 0,
51496
+ informationDensity: 1,
51497
+ interactionComplexity: 1,
51498
+ contextDisruption: 0,
51499
+ learningCurve: 0
51500
+ },
51501
+ primaryCostDriver: "Simple state or single information piece"
51502
+ },
51503
+ tooltip: {
51504
+ dimensions: {
51505
+ decisionDemand: 0,
51506
+ informationDensity: 1,
51507
+ interactionComplexity: 0,
51508
+ contextDisruption: 0,
51509
+ learningCurve: 1
51510
+ },
51511
+ primaryCostDriver: "Simple state or single information piece"
51512
+ },
51513
+ typography: {
51514
+ dimensions: {
51515
+ decisionDemand: 0,
51516
+ informationDensity: 1,
51517
+ interactionComplexity: 0,
51518
+ contextDisruption: 0,
51519
+ learningCurve: 1
51520
+ },
51521
+ primaryCostDriver: "Simple state or single information piece"
51522
+ },
51523
+ spinner: {
51524
+ dimensions: {
51525
+ decisionDemand: 0,
51526
+ informationDensity: 1,
51527
+ interactionComplexity: 0,
51528
+ contextDisruption: 0,
51529
+ learningCurve: 1
51530
+ },
51531
+ primaryCostDriver: "Simple state or single information piece"
51532
+ },
51533
+ empty: {
51534
+ dimensions: {
51535
+ decisionDemand: 1,
51536
+ informationDensity: 1,
51537
+ interactionComplexity: 0,
51538
+ contextDisruption: 0,
51539
+ learningCurve: 0
51540
+ },
51541
+ primaryCostDriver: "Simple state or single information piece"
51542
+ },
51543
+ "toggle-group": {
51544
+ dimensions: {
51545
+ decisionDemand: 1,
51546
+ informationDensity: 1,
51547
+ interactionComplexity: 0,
51548
+ contextDisruption: 0,
51549
+ learningCurve: 0
51550
+ },
51551
+ primaryCostDriver: "Simple state or single information piece"
51552
+ },
51553
+ // Score 3 -- One decision + one interaction mode
51554
+ button: {
51555
+ dimensions: {
51556
+ decisionDemand: 1,
51557
+ informationDensity: 1,
51558
+ interactionComplexity: 1,
51559
+ contextDisruption: 0,
51560
+ learningCurve: 0
51561
+ },
51562
+ primaryCostDriver: "One decision + one interaction mode"
51563
+ },
51564
+ alert: {
51565
+ dimensions: {
51566
+ decisionDemand: 0,
51567
+ informationDensity: 1,
51568
+ interactionComplexity: 1,
51569
+ contextDisruption: 0,
51570
+ learningCurve: 1
51571
+ },
51572
+ primaryCostDriver: "One decision + one interaction mode"
51573
+ },
51574
+ sidebar: {
51575
+ dimensions: {
51576
+ decisionDemand: 1,
51577
+ informationDensity: 1,
51578
+ interactionComplexity: 1,
51579
+ contextDisruption: 0,
51580
+ learningCurve: 0
51581
+ },
51582
+ primaryCostDriver: "One decision + one interaction mode"
51583
+ },
51584
+ accordion: {
51585
+ dimensions: {
51586
+ decisionDemand: 1,
51587
+ informationDensity: 1,
51588
+ interactionComplexity: 0,
51589
+ contextDisruption: 0,
51590
+ learningCurve: 1
51591
+ },
51592
+ primaryCostDriver: "One decision + one interaction mode"
51593
+ },
51594
+ slider: {
51595
+ dimensions: {
51596
+ decisionDemand: 1,
51597
+ informationDensity: 0,
51598
+ interactionComplexity: 1,
51599
+ contextDisruption: 0,
51600
+ learningCurve: 1
51601
+ },
51602
+ primaryCostDriver: "One decision + one interaction mode"
51603
+ },
51604
+ "radio-group": {
51605
+ dimensions: {
51606
+ decisionDemand: 1,
51607
+ informationDensity: 1,
51608
+ interactionComplexity: 1,
51609
+ contextDisruption: 0,
51610
+ learningCurve: 0
51611
+ },
51612
+ primaryCostDriver: "One decision + one interaction mode"
51613
+ },
51614
+ table: {
51615
+ dimensions: {
51616
+ decisionDemand: 0,
51617
+ informationDensity: 2,
51618
+ interactionComplexity: 0,
51619
+ contextDisruption: 0,
51620
+ learningCurve: 1
51621
+ },
51622
+ primaryCostDriver: "One decision + one interaction mode"
51623
+ },
51624
+ resizable: {
51625
+ dimensions: {
51626
+ decisionDemand: 1,
51627
+ informationDensity: 0,
51628
+ interactionComplexity: 1,
51629
+ contextDisruption: 0,
51630
+ learningCurve: 1
51631
+ },
51632
+ primaryCostDriver: "One decision + one interaction mode"
51633
+ },
51634
+ field: {
51635
+ dimensions: {
51636
+ decisionDemand: 1,
51637
+ informationDensity: 1,
51638
+ interactionComplexity: 1,
51639
+ contextDisruption: 0,
51640
+ learningCurve: 0
51641
+ },
51642
+ primaryCostDriver: "One decision + one interaction mode"
51643
+ },
51644
+ "hover-card": {
51645
+ dimensions: {
51646
+ decisionDemand: 0,
51647
+ informationDensity: 1,
51648
+ interactionComplexity: 0,
51649
+ contextDisruption: 1,
51650
+ learningCurve: 1
51651
+ },
51652
+ primaryCostDriver: "One decision + one interaction mode"
51653
+ },
51654
+ image: {
51655
+ dimensions: {
51656
+ decisionDemand: 0,
51657
+ informationDensity: 2,
51658
+ interactionComplexity: 0,
51659
+ contextDisruption: 0,
51660
+ learningCurve: 1
51661
+ },
51662
+ primaryCostDriver: "One decision + one interaction mode"
51663
+ },
51664
+ embed: {
51665
+ dimensions: {
51666
+ decisionDemand: 0,
51667
+ informationDensity: 2,
51668
+ interactionComplexity: 0,
51669
+ contextDisruption: 0,
51670
+ learningCurve: 1
51671
+ },
51672
+ primaryCostDriver: "One decision + one interaction mode"
51673
+ },
51674
+ item: {
51675
+ dimensions: {
51676
+ decisionDemand: 1,
51677
+ informationDensity: 1,
51678
+ interactionComplexity: 1,
51679
+ contextDisruption: 0,
51680
+ learningCurve: 0
51681
+ },
51682
+ primaryCostDriver: "One decision + one interaction mode"
51683
+ },
51684
+ // Score 4 -- Data entry or menu scanning
51685
+ input: {
51686
+ dimensions: {
51687
+ decisionDemand: 1,
51688
+ informationDensity: 1,
51689
+ interactionComplexity: 1,
51690
+ contextDisruption: 0,
51691
+ learningCurve: 1
51692
+ },
51693
+ primaryCostDriver: "Data entry or menu scanning"
51694
+ },
51695
+ textarea: {
51696
+ dimensions: {
51697
+ decisionDemand: 1,
51698
+ informationDensity: 1,
51699
+ interactionComplexity: 1,
51700
+ contextDisruption: 0,
51701
+ learningCurve: 1
51702
+ },
51703
+ primaryCostDriver: "Data entry or menu scanning"
51704
+ },
51705
+ "input-group": {
51706
+ dimensions: {
51707
+ decisionDemand: 1,
51708
+ informationDensity: 1,
51709
+ interactionComplexity: 1,
51710
+ contextDisruption: 0,
51711
+ learningCurve: 1
51712
+ },
51713
+ primaryCostDriver: "Data entry or menu scanning"
51714
+ },
51715
+ "input-otp": {
51716
+ dimensions: {
51717
+ decisionDemand: 1,
51718
+ informationDensity: 1,
51719
+ interactionComplexity: 1,
51720
+ contextDisruption: 0,
51721
+ learningCurve: 1
51722
+ },
51723
+ primaryCostDriver: "Data entry or menu scanning"
51724
+ },
51725
+ carousel: {
51726
+ dimensions: {
51727
+ decisionDemand: 1,
51728
+ informationDensity: 1,
51729
+ interactionComplexity: 1,
51730
+ contextDisruption: 0,
51731
+ learningCurve: 1
51732
+ },
51733
+ primaryCostDriver: "Data entry or menu scanning"
51734
+ },
51735
+ tabs: {
51736
+ dimensions: {
51737
+ decisionDemand: 1,
51738
+ informationDensity: 1,
51739
+ interactionComplexity: 1,
51740
+ contextDisruption: 0,
51741
+ learningCurve: 1
51742
+ },
51743
+ primaryCostDriver: "Data entry or menu scanning"
51744
+ },
51745
+ "context-menu": {
51746
+ dimensions: {
51747
+ decisionDemand: 1,
51748
+ informationDensity: 1,
51749
+ interactionComplexity: 1,
51750
+ contextDisruption: 0,
51751
+ learningCurve: 1
51752
+ },
51753
+ primaryCostDriver: "Data entry or menu scanning"
51754
+ },
51755
+ "dropdown-menu": {
51756
+ dimensions: {
51757
+ decisionDemand: 1,
51758
+ informationDensity: 1,
51759
+ interactionComplexity: 1,
51760
+ contextDisruption: 0,
51761
+ learningCurve: 1
51762
+ },
51763
+ primaryCostDriver: "Data entry or menu scanning"
51764
+ },
51765
+ popover: {
51766
+ dimensions: {
51767
+ decisionDemand: 0,
51768
+ informationDensity: 1,
51769
+ interactionComplexity: 1,
51770
+ contextDisruption: 1,
51771
+ learningCurve: 1
51772
+ },
51773
+ primaryCostDriver: "Data entry or menu scanning"
51774
+ },
51775
+ progress: {
51776
+ dimensions: {
51777
+ decisionDemand: 0,
51778
+ informationDensity: 2,
51779
+ interactionComplexity: 0,
51780
+ contextDisruption: 1,
51781
+ learningCurve: 1
51782
+ },
51783
+ primaryCostDriver: "Data entry or menu scanning"
51784
+ },
51785
+ drawer: {
51786
+ dimensions: {
51787
+ decisionDemand: 1,
51788
+ informationDensity: 1,
51789
+ interactionComplexity: 1,
51790
+ contextDisruption: 1,
51791
+ learningCurve: 0
51792
+ },
51793
+ primaryCostDriver: "Data entry or menu scanning"
51794
+ },
51795
+ grid: {
51796
+ dimensions: {
51797
+ decisionDemand: 0,
51798
+ informationDensity: 2,
51799
+ interactionComplexity: 0,
51800
+ contextDisruption: 0,
51801
+ learningCurve: 2
51802
+ },
51803
+ primaryCostDriver: "Data entry or menu scanning"
51804
+ },
51805
+ "block-wrapper": {
51806
+ dimensions: {
51807
+ decisionDemand: 1,
51808
+ informationDensity: 1,
51809
+ interactionComplexity: 1,
51810
+ contextDisruption: 0,
51811
+ learningCurve: 1
51812
+ },
51813
+ primaryCostDriver: "Data entry or menu scanning"
51814
+ },
51815
+ "inline-toolbar": {
51816
+ dimensions: {
51817
+ decisionDemand: 1,
51818
+ informationDensity: 1,
51819
+ interactionComplexity: 1,
51820
+ contextDisruption: 0,
51821
+ learningCurve: 1
51822
+ },
51823
+ primaryCostDriver: "Data entry or menu scanning"
51824
+ },
51825
+ "editor-toolbar": {
51826
+ dimensions: {
51827
+ decisionDemand: 1,
51828
+ informationDensity: 1,
51829
+ interactionComplexity: 1,
51830
+ contextDisruption: 0,
51831
+ learningCurve: 1
51832
+ },
51833
+ primaryCostDriver: "Data entry or menu scanning"
51834
+ },
51835
+ pagination: {
51836
+ dimensions: {
51837
+ decisionDemand: 1,
51838
+ informationDensity: 1,
51839
+ interactionComplexity: 1,
51840
+ contextDisruption: 0,
51841
+ learningCurve: 1
51842
+ },
51843
+ primaryCostDriver: "Data entry or menu scanning"
51844
+ },
51845
+ // Score 5 -- Multi-step interaction or spatial reasoning
51846
+ sheet: {
51847
+ dimensions: {
51848
+ decisionDemand: 1,
51849
+ informationDensity: 1,
51850
+ interactionComplexity: 1,
51851
+ contextDisruption: 1,
51852
+ learningCurve: 1
51853
+ },
51854
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
51855
+ },
51856
+ "date-picker": {
51857
+ dimensions: {
51858
+ decisionDemand: 1,
51859
+ informationDensity: 1,
51860
+ interactionComplexity: 2,
51861
+ contextDisruption: 0,
51862
+ learningCurve: 1
51863
+ },
51864
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
51865
+ },
51866
+ calendar: {
51867
+ dimensions: {
51868
+ decisionDemand: 1,
51869
+ informationDensity: 2,
51870
+ interactionComplexity: 1,
51871
+ contextDisruption: 0,
51872
+ learningCurve: 1
51873
+ },
51874
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
51875
+ },
51876
+ select: {
51877
+ dimensions: {
51878
+ decisionDemand: 2,
51879
+ informationDensity: 1,
51880
+ interactionComplexity: 1,
51881
+ contextDisruption: 0,
51882
+ learningCurve: 1
51883
+ },
51884
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
51885
+ },
51886
+ "navigation-menu": {
51887
+ dimensions: {
51888
+ decisionDemand: 1,
51889
+ informationDensity: 2,
51890
+ interactionComplexity: 1,
51891
+ contextDisruption: 0,
51892
+ learningCurve: 1
51893
+ },
51894
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
51895
+ },
51896
+ menubar: {
51897
+ dimensions: {
51898
+ decisionDemand: 1,
51899
+ informationDensity: 2,
51900
+ interactionComplexity: 1,
51901
+ contextDisruption: 0,
51902
+ learningCurve: 1
51903
+ },
51904
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
51905
+ },
51906
+ "color-picker": {
51907
+ dimensions: {
51908
+ decisionDemand: 1,
51909
+ informationDensity: 1,
51910
+ interactionComplexity: 2,
51911
+ contextDisruption: 0,
51912
+ learningCurve: 1
51913
+ },
51914
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
51915
+ },
51916
+ "block-canvas": {
51917
+ dimensions: {
51918
+ decisionDemand: 1,
51919
+ informationDensity: 1,
51920
+ interactionComplexity: 1,
51921
+ contextDisruption: 1,
51922
+ learningCurve: 1
51923
+ },
51924
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
51925
+ },
51926
+ // Score 6 -- Compound interaction modes or learning curve
51927
+ dialog: {
51928
+ dimensions: {
51929
+ decisionDemand: 1,
51930
+ informationDensity: 1,
51931
+ interactionComplexity: 1,
51932
+ contextDisruption: 2,
51933
+ learningCurve: 1
51934
+ },
51935
+ primaryCostDriver: "Compound interaction modes or learning curve"
51936
+ },
51937
+ command: {
51938
+ dimensions: {
51939
+ decisionDemand: 1,
51940
+ informationDensity: 2,
51941
+ interactionComplexity: 1,
51942
+ contextDisruption: 0,
51943
+ learningCurve: 2
51944
+ },
51945
+ primaryCostDriver: "Compound interaction modes or learning curve"
51946
+ },
51947
+ combobox: {
51948
+ dimensions: {
51949
+ decisionDemand: 2,
51950
+ informationDensity: 2,
51951
+ interactionComplexity: 2,
51952
+ contextDisruption: 0,
51953
+ learningCurve: 0
51954
+ },
51955
+ primaryCostDriver: "Compound interaction modes or learning curve"
51956
+ },
51957
+ // Score 7 -- Full context disruption + consequential decision
51958
+ "alert-dialog": {
51959
+ dimensions: {
51960
+ decisionDemand: 2,
51961
+ informationDensity: 1,
51962
+ interactionComplexity: 1,
51963
+ contextDisruption: 2,
51964
+ learningCurve: 1
51965
+ },
51966
+ primaryCostDriver: "Full context disruption + consequential decision"
51967
+ }
51968
+ };
51969
+ var DIMENSION_NAMES = {
51970
+ decisionDemand: "Decision Demand",
51971
+ informationDensity: "Information Density",
51972
+ interactionComplexity: "Interaction Complexity",
51973
+ contextDisruption: "Context Disruption",
51974
+ learningCurve: "Learning Curve"
51975
+ };
51976
+ var HOTSPOT_SUGGESTIONS = {
51977
+ contextDisruption: "Consider non-modal alternative if the decision is not consequential",
51978
+ informationDensity: "Consider progressive disclosure to reduce simultaneous information",
51979
+ interactionComplexity: "Consider a simpler single-mode alternative",
51980
+ decisionDemand: "Can choices be pre-selected or split across steps?",
51981
+ learningCurve: "Ensure discoverability with inline guidance"
51982
+ };
51983
+ function scoreOf(dimensions) {
51984
+ return dimensions.decisionDemand + dimensions.informationDensity + dimensions.interactionComplexity + dimensions.contextDisruption + dimensions.learningCurve;
51985
+ }
51986
+ function highestDimension(dimensions) {
51987
+ let max = -1;
51988
+ let maxKey = "decisionDemand";
51989
+ for (const key of Object.keys(dimensions)) {
51990
+ if (dimensions[key] > max) {
51991
+ max = dimensions[key];
51992
+ maxKey = key;
51993
+ }
51994
+ }
51995
+ return maxKey;
51996
+ }
51997
+ function evaluateComposition(components, tier, enrichment) {
51998
+ const budget = BUDGET_TIERS[tier];
51999
+ const componentCounts = /* @__PURE__ */ new Map();
52000
+ for (const name2 of components) {
52001
+ componentCounts.set(name2, (componentCounts.get(name2) ?? 0) + 1);
52002
+ }
52003
+ let total = 0;
52004
+ const componentResults = [];
52005
+ const unknownComponents = [];
52006
+ for (const [name2, count] of componentCounts) {
52007
+ const profile = COMPONENT_SCORES[name2];
52008
+ if (!profile) {
52009
+ unknownComponents.push(name2);
52010
+ continue;
52011
+ }
52012
+ const score = scoreOf(profile.dimensions);
52013
+ total += score * count;
52014
+ componentResults.push({
52015
+ name: name2,
52016
+ score,
52017
+ count,
52018
+ dimensions: profile.dimensions,
52019
+ primaryCostDriver: profile.primaryCostDriver
52020
+ });
52021
+ }
52022
+ componentResults.sort((a, b) => b.score - a.score);
52023
+ const withinBudget = total <= budget;
52024
+ const budgetResult = {
52025
+ tier,
52026
+ budget,
52027
+ total,
52028
+ status: withinBudget ? "within-budget" : "over-budget"
52029
+ };
52030
+ if (withinBudget) {
52031
+ budgetResult.headroom = budget - total;
52032
+ } else {
52033
+ budgetResult.overage = total - budget;
52034
+ budgetResult.overagePercent = Math.round((total - budget) / budget * 100);
52035
+ }
52036
+ const attentionConflicts = [];
52037
+ const attentionNotes = [];
52038
+ const fullAttentionCaptures = [];
52039
+ const primaryElements = [];
52040
+ for (const [name2] of componentCounts) {
52041
+ const metadata = enrichment.componentIntelligence.get(name2);
52042
+ if (!metadata?.intelligence?.attentionEconomics) continue;
52043
+ const attn = metadata.intelligence.attentionEconomics.toLowerCase();
52044
+ if (attn.includes("full attention capture") || attn.includes("blocks all other")) {
52045
+ fullAttentionCaptures.push(name2);
52046
+ }
52047
+ if (attn.includes("primary") && attn.includes("maximum 1")) {
52048
+ const count = componentCounts.get(name2) ?? 0;
52049
+ if (count > 1) {
52050
+ primaryElements.push(name2);
52051
+ }
52052
+ }
52053
+ }
52054
+ if (fullAttentionCaptures.length > 1) {
52055
+ attentionConflicts.push(`${fullAttentionCaptures.join(" and ")} both capture full attention`);
52056
+ }
52057
+ const buttonCount = componentCounts.get("button") ?? 0;
52058
+ if (buttonCount > 1) {
52059
+ const buttonMeta = enrichment.componentIntelligence.get("button");
52060
+ if (buttonMeta?.intelligence?.attentionEconomics?.toLowerCase().includes("maximum 1 per section")) {
52061
+ attentionNotes.push(`${buttonCount} buttons present -- ensure maximum 1 primary per section`);
52062
+ }
52063
+ }
52064
+ if (primaryElements.length > 0) {
52065
+ for (const name2 of primaryElements) {
52066
+ attentionNotes.push(`Multiple ${name2} instances -- check attention hierarchy`);
52067
+ }
52068
+ }
52069
+ const trustConsiderations = [];
52070
+ for (const [name2] of componentCounts) {
52071
+ const metadata = enrichment.componentIntelligence.get(name2);
52072
+ if (!metadata?.intelligence?.trustBuilding) continue;
52073
+ const trust = metadata.intelligence.trustBuilding;
52074
+ if (trust.toLowerCase().includes("confirmation") || trust.toLowerCase().includes("destructive") || trust.toLowerCase().includes("consequence") || trust.toLowerCase().includes("cancel")) {
52075
+ trustConsiderations.push(`${name2}: ${trust}`);
52076
+ }
52077
+ }
52078
+ const patternMatches = [];
52079
+ const compositionSet = new Set(componentCounts.keys());
52080
+ for (const [patternKey, pattern] of Object.entries(enrichment.patterns)) {
52081
+ const matched = pattern.components.filter((c) => compositionSet.has(c));
52082
+ if (matched.length >= 2 || matched.length === pattern.components.length) {
52083
+ patternMatches.push({
52084
+ name: pattern.name,
52085
+ matched,
52086
+ suggestion: `Call rafters_pattern('${patternKey}') for full guidance`
52087
+ });
52088
+ }
52089
+ }
52090
+ const designerNotes = [];
52091
+ const compositionNames = [...compositionSet];
52092
+ for (const { token } of enrichment.tokenOverrides) {
52093
+ if (!token.userOverride) continue;
52094
+ const relevantTo = [];
52095
+ if (token.applicableComponents) {
52096
+ for (const comp of token.applicableComponents) {
52097
+ if (compositionSet.has(comp)) {
52098
+ relevantTo.push(comp);
52099
+ }
52100
+ }
52101
+ }
52102
+ if (relevantTo.length === 0) {
52103
+ if (token.namespace === "spacing") {
52104
+ const formComponents = compositionNames.filter(
52105
+ (n) => [
52106
+ "input",
52107
+ "textarea",
52108
+ "field",
52109
+ "label",
52110
+ "button",
52111
+ "select",
52112
+ "checkbox",
52113
+ "radio-group",
52114
+ "switch",
52115
+ "slider",
52116
+ "combobox"
52117
+ ].includes(n)
52118
+ );
52119
+ if (formComponents.length > 0) {
52120
+ relevantTo.push(...formComponents);
52121
+ }
52122
+ } else if (token.namespace === "color") {
52123
+ const variantComponents = compositionNames.filter(
52124
+ (n) => ["button", "badge", "alert", "alert-dialog", "progress"].includes(n)
52125
+ );
52126
+ if (variantComponents.length > 0 && (token.name.includes("destructive") || token.name.includes("primary"))) {
52127
+ relevantTo.push(...variantComponents);
52128
+ }
52129
+ }
52130
+ }
52131
+ if (relevantTo.length > 0) {
52132
+ designerNotes.push({
52133
+ token: token.name,
52134
+ reason: token.userOverride.reason,
52135
+ relevantTo
52136
+ });
52137
+ }
52138
+ }
52139
+ const violations = [];
52140
+ for (const [name2] of componentCounts) {
52141
+ const metadata = enrichment.componentIntelligence.get(name2);
52142
+ if (!metadata?.intelligence?.usagePatterns?.nevers) continue;
52143
+ for (const never2 of metadata.intelligence.usagePatterns.nevers) {
52144
+ const neverLower = never2.toLowerCase();
52145
+ if (neverLower.includes("nest") && neverLower.includes("card") && name2 === "card") {
52146
+ const cardCount = componentCounts.get("card") ?? 0;
52147
+ if (cardCount > 1) {
52148
+ violations.push(
52149
+ `${name2}: "${never2}" -- ${cardCount} cards in composition, verify none are nested`
52150
+ );
52151
+ }
52152
+ }
52153
+ if (neverLower.includes("multiple primary") && name2 === "button") {
52154
+ if (buttonCount > 1) {
52155
+ violations.push(`${name2}: "${never2}"`);
52156
+ }
52157
+ }
52158
+ if (neverLower.includes("stack") && neverLower.includes("dialog")) {
52159
+ if (compositionSet.has("dialog") && (compositionSet.has("alert-dialog") || (componentCounts.get("dialog") ?? 0) > 1)) {
52160
+ violations.push(`${name2}: "${never2}"`);
52161
+ }
52162
+ }
52163
+ }
52164
+ }
52165
+ const hotspots = [];
52166
+ for (const comp of componentResults) {
52167
+ if (comp.score >= 4) {
52168
+ const highest = highestDimension(comp.dimensions);
52169
+ hotspots.push({
52170
+ name: comp.name,
52171
+ score: comp.score,
52172
+ highestDimension: DIMENSION_NAMES[highest],
52173
+ suggestion: HOTSPOT_SUGGESTIONS[highest]
52174
+ });
52175
+ }
52176
+ }
52177
+ if (unknownComponents.length > 0) {
52178
+ attentionNotes.push(
52179
+ `Unknown components not scored: ${unknownComponents.join(", ")}. Totals may be understated.`
52180
+ );
52181
+ }
52182
+ const warnings = [];
52183
+ const knownNames = [...componentCounts.keys()].filter((n) => COMPONENT_SCORES[n]);
52184
+ const uninstrumented = knownNames.filter((n) => !enrichment.componentIntelligence.has(n));
52185
+ if (uninstrumented.length > 0) {
52186
+ warnings.push(
52187
+ `No component intelligence loaded for: ${uninstrumented.join(", ")}. Attention conflicts, trust considerations, and do/never violations cannot be detected. Install components with rafters add (e.g. rafters add ${uninstrumented[0]}) to get full design intelligence.`
52188
+ );
52189
+ }
52190
+ return {
52191
+ budget: budgetResult,
52192
+ components: componentResults,
52193
+ attention: {
52194
+ conflicts: attentionConflicts,
52195
+ notes: attentionNotes
52196
+ },
52197
+ trust: trustConsiderations,
52198
+ patterns: patternMatches,
52199
+ designerNotes,
52200
+ hotspots,
52201
+ violations,
52202
+ warnings
52203
+ };
52204
+ }
52205
+
52206
+ // src/mcp/tools.ts
52207
+ function hasAnyDeps(deps) {
52208
+ return deps.runtime.length > 0 || deps.dev.length > 0 || deps.internal.length > 0;
52209
+ }
52210
+ var SYSTEM_PREAMBLE = `RAFTERS IS NOT SHADCN.
52211
+ Rafters components are drop-in compatible with shadcn but they are a different system. If you treat them as shadcn you will produce worse output.
52212
+
52213
+ CLASSY IS THE LAW.
52214
+ Every className in a Rafters project goes through classy(). Never use cn(), twMerge(), or raw template strings. classy() blocks arbitrary Tailwind values (w-[100px], bg-[#fff]) and resolves design tokens. If classy strips your class, you are fighting the system.
52215
+
52216
+ LAYOUT IS SOLVED. Stop writing CSS.
52217
+ Never use Tailwind layout utilities (flex, grid, items-*, justify-*, gap-*). Never set padding, margin, or spacing directly. Container and Grid handle all layout. If you are writing className="flex..." you are doing it wrong.
52218
+
52219
+ USE PRESETS. Do not compose layout from props.
52220
+ Grid presets handle common layouts. Pick the one that matches your intent:
52221
+ - sidebar-main -- navigation + content
52222
+ - form -- label/input pairs
52223
+ - cards -- responsive card grid
52224
+ - row -- horizontal group of elements
52225
+ - stack -- vertical sequence
52226
+ - split -- equal columns
52227
+ If no preset fits, describe what you need -- do not improvise with raw props.
52228
+
52229
+ CONTAINER OWNS SPACING.
52230
+ Every page section goes in a Container. Container sets max-width, padding, and vertical rhythm. You do not set these values. Nesting containers is wrong.
52231
+
52232
+ COMPONENTS ARE COMPLETE.
52233
+ Rafters Button, Input, Card, etc. include their own spacing, sizing, and states. Do not add wrapper divs. Do not override with utility classes. If it looks unstyled, you are wrapping it wrong, not styling it wrong.
52234
+
52235
+ UTILITIES EXIST FOR EDGE CASES.
52236
+ If no component fits your need, check @/lib/utils for official behavioral utilities. Do not invent your own. If nothing exists there either, ask the human.
52237
+
52238
+ When in doubt: less code, not more. Rafters has already made the design decision.`;
50996
52239
  var DESIGN_PATTERNS = {
50997
52240
  "destructive-action": {
50998
52241
  name: "Destructive Action",
@@ -51340,6 +52583,26 @@ var TOOL_DEFINITIONS = [
51340
52583
  },
51341
52584
  required: ["name"]
51342
52585
  }
52586
+ },
52587
+ {
52588
+ name: "rafters_cognitive_budget",
52589
+ description: "Evaluate composition-level cognitive load. Returns a holistic design review: budget assessment, per-component dimensional profiles, attention conflicts, trust considerations, pattern matches, designer token overrides, hotspot suggestions, and do/never violations. Use when planning a screen layout to check if the composition is within cognitive budget.",
52590
+ inputSchema: {
52591
+ type: "object",
52592
+ properties: {
52593
+ components: {
52594
+ type: "array",
52595
+ items: { type: "string" },
52596
+ description: 'Components visible simultaneously, duplicates allowed (e.g., ["input", "input", "button", "card"])'
52597
+ },
52598
+ tier: {
52599
+ type: "string",
52600
+ enum: ["focused", "page", "app"],
52601
+ description: "Budget tier: focused (15, dialogs/modals), page (30, standard views), app (45, multi-panel). Default: page."
52602
+ }
52603
+ },
52604
+ required: ["components"]
52605
+ }
51343
52606
  }
51344
52607
  ];
51345
52608
  var RaftersToolHandler = class {
@@ -51369,6 +52632,11 @@ var RaftersToolHandler = class {
51369
52632
  return this.getComponent(args.name);
51370
52633
  case "rafters_token":
51371
52634
  return this.getToken(args.name);
52635
+ case "rafters_cognitive_budget":
52636
+ return this.getCognitiveBudget(
52637
+ args.components,
52638
+ args.tier ?? "page"
52639
+ );
51372
52640
  default:
51373
52641
  return {
51374
52642
  content: [{ type: "text", text: `Unknown tool: ${name2}` }],
@@ -51390,12 +52658,12 @@ var RaftersToolHandler = class {
51390
52658
  this.getComponentVocabulary()
51391
52659
  ]);
51392
52660
  const vocabulary = {
52661
+ system: SYSTEM_PREAMBLE,
52662
+ components,
51393
52663
  colors,
51394
52664
  spacing,
51395
52665
  typography,
51396
- components,
51397
- patterns: Object.keys(DESIGN_PATTERNS),
51398
- usage: "Use rafters_pattern for deep guidance on specific patterns, rafters_component for component details"
52666
+ patterns: Object.keys(DESIGN_PATTERNS)
51399
52667
  };
51400
52668
  return {
51401
52669
  content: [
@@ -51482,33 +52750,26 @@ var RaftersToolHandler = class {
51482
52750
  * Extract compact component vocabulary
51483
52751
  */
51484
52752
  async getComponentVocabulary() {
52753
+ let installed = [];
51485
52754
  try {
51486
- const componentsPath = this.getComponentsPath();
51487
- const files = await readdir3(componentsPath);
51488
- const componentFiles = files.filter((f) => f.endsWith(".tsx"));
51489
- const components = [];
51490
- for (const file2 of componentFiles) {
51491
- const name2 = basename(file2, ".tsx");
51492
- const metadata = await this.loadComponentMetadata(name2);
51493
- if (metadata) {
51494
- const item = {
51495
- name: metadata.name,
51496
- category: metadata.category
51497
- };
51498
- if (metadata.intelligence?.cognitiveLoad !== void 0) {
51499
- item.load = metadata.intelligence.cognitiveLoad;
51500
- }
51501
- components.push(item);
51502
- }
52755
+ const paths = getRaftersPaths(this.projectRoot);
52756
+ if (existsSync3(paths.config)) {
52757
+ const content = await readFile6(paths.config, "utf-8");
52758
+ const config3 = JSON.parse(content);
52759
+ installed = config3.installed?.components ?? [];
51503
52760
  }
51504
- components.sort((a, b) => {
51505
- if (a.category !== b.category) return a.category.localeCompare(b.category);
51506
- return a.name.localeCompare(b.name);
51507
- });
51508
- return components;
51509
52761
  } catch {
51510
- return [];
51511
52762
  }
52763
+ let available = [];
52764
+ try {
52765
+ const client = new RegistryClient();
52766
+ const index = await client.fetchIndex();
52767
+ const allComponents = index.components;
52768
+ const installedSet = new Set(installed);
52769
+ available = allComponents.filter((name2) => !installedSet.has(name2));
52770
+ } catch {
52771
+ }
52772
+ return { installed, available };
51512
52773
  }
51513
52774
  // ==================== Tool 2: Pattern ====================
51514
52775
  /**
@@ -51549,7 +52810,7 @@ var RaftersToolHandler = class {
51549
52810
  * Get path to UI components directory
51550
52811
  */
51551
52812
  getComponentsPath() {
51552
- const monorepoPath = join9(this.projectRoot, "packages/ui/src/components/ui");
52813
+ const monorepoPath = join10(this.projectRoot, "packages/ui/src/components/ui");
51553
52814
  return monorepoPath;
51554
52815
  }
51555
52816
  /**
@@ -51557,11 +52818,16 @@ var RaftersToolHandler = class {
51557
52818
  */
51558
52819
  async loadComponentMetadata(name2) {
51559
52820
  const componentsPath = this.getComponentsPath();
51560
- const filePath = join9(componentsPath, `${name2}.tsx`);
52821
+ const filePath = join10(componentsPath, `${name2}.tsx`);
51561
52822
  try {
51562
- const source = await readFile5(filePath, "utf-8");
52823
+ const source = await readFile6(filePath, "utf-8");
51563
52824
  const intelligence = parseJSDocIntelligence(source);
51564
52825
  const description = parseDescription(source);
52826
+ let jsDocDeps = { runtime: [], dev: [], internal: [] };
52827
+ try {
52828
+ jsDocDeps = extractJSDocDependencies(source);
52829
+ } catch {
52830
+ }
51565
52831
  const metadata = {
51566
52832
  name: name2,
51567
52833
  displayName: toDisplayName(name2),
@@ -51572,6 +52838,9 @@ var RaftersToolHandler = class {
51572
52838
  primitives: extractPrimitiveDependencies(source),
51573
52839
  filePath: `packages/ui/src/components/ui/${name2}.tsx`
51574
52840
  };
52841
+ if (hasAnyDeps(jsDocDeps)) {
52842
+ metadata.jsDocDependencies = jsDocDeps;
52843
+ }
51575
52844
  if (description) {
51576
52845
  metadata.description = description;
51577
52846
  }
@@ -51716,6 +52985,9 @@ var RaftersToolHandler = class {
51716
52985
  if (metadata.dependencies.length > 0) {
51717
52986
  formatted.dependencies = metadata.dependencies;
51718
52987
  }
52988
+ if (metadata.jsDocDependencies && hasAnyDeps(metadata.jsDocDependencies)) {
52989
+ formatted.jsDocDependencies = metadata.jsDocDependencies;
52990
+ }
51719
52991
  return {
51720
52992
  content: [
51721
52993
  {
@@ -51831,6 +53103,120 @@ var RaftersToolHandler = class {
51831
53103
  return this.handleError("getToken", error48);
51832
53104
  }
51833
53105
  }
53106
+ // ==================== Tool 5: Cognitive Budget ====================
53107
+ /**
53108
+ * Evaluate composition-level cognitive load
53109
+ * Synthesizes scoring data with component intelligence, token overrides,
53110
+ * and design patterns to produce a holistic composition review
53111
+ */
53112
+ async getCognitiveBudget(components, tier) {
53113
+ try {
53114
+ if (!components || components.length === 0) {
53115
+ return {
53116
+ content: [
53117
+ {
53118
+ type: "text",
53119
+ text: JSON.stringify(
53120
+ {
53121
+ error: "No components provided",
53122
+ suggestion: 'Pass an array of component names visible simultaneously, e.g. ["input", "button", "card"]'
53123
+ },
53124
+ null,
53125
+ 2
53126
+ )
53127
+ }
53128
+ ],
53129
+ isError: true
53130
+ };
53131
+ }
53132
+ if (!BUDGET_TIERS[tier]) {
53133
+ return {
53134
+ content: [
53135
+ {
53136
+ type: "text",
53137
+ text: JSON.stringify(
53138
+ {
53139
+ error: `Invalid tier "${tier}"`,
53140
+ available: Object.keys(BUDGET_TIERS),
53141
+ suggestion: 'Use "focused", "page", or "app"'
53142
+ },
53143
+ null,
53144
+ 2
53145
+ )
53146
+ }
53147
+ ],
53148
+ isError: true
53149
+ };
53150
+ }
53151
+ const uniqueNames = [...new Set(components)];
53152
+ const unknownNames = uniqueNames.filter((n) => !COMPONENT_SCORES[n]);
53153
+ if (unknownNames.length === uniqueNames.length) {
53154
+ return {
53155
+ content: [
53156
+ {
53157
+ type: "text",
53158
+ text: JSON.stringify(
53159
+ {
53160
+ error: `No recognized components in composition: ${unknownNames.join(", ")}`,
53161
+ suggestion: "Use rafters_vocabulary to see available component names"
53162
+ },
53163
+ null,
53164
+ 2
53165
+ )
53166
+ }
53167
+ ],
53168
+ isError: true
53169
+ };
53170
+ }
53171
+ const intelligenceMap = /* @__PURE__ */ new Map();
53172
+ const metadataResults = await Promise.all(
53173
+ uniqueNames.map(async (name2) => {
53174
+ const metadata = await this.loadComponentMetadata(name2);
53175
+ return { name: name2, metadata };
53176
+ })
53177
+ );
53178
+ for (const { name: name2, metadata } of metadataResults) {
53179
+ if (metadata) {
53180
+ intelligenceMap.set(name2, metadata);
53181
+ }
53182
+ }
53183
+ const tokenOverrides = [];
53184
+ const namespaces = ["color", "spacing", "typography"];
53185
+ for (const ns of namespaces) {
53186
+ try {
53187
+ const tokens = await this.loadNamespace(ns);
53188
+ for (const token of tokens) {
53189
+ if (token.userOverride) {
53190
+ tokenOverrides.push({ token, namespace: ns });
53191
+ }
53192
+ }
53193
+ } catch {
53194
+ }
53195
+ }
53196
+ const patternRefs = {};
53197
+ for (const [key, pattern] of Object.entries(DESIGN_PATTERNS)) {
53198
+ patternRefs[key] = {
53199
+ name: pattern.name,
53200
+ components: pattern.components
53201
+ };
53202
+ }
53203
+ const review = evaluateComposition(components, tier, {
53204
+ componentIntelligence: intelligenceMap,
53205
+ tokenOverrides,
53206
+ patterns: patternRefs
53207
+ });
53208
+ return {
53209
+ content: [
53210
+ {
53211
+ type: "text",
53212
+ text: JSON.stringify(review, null, 2)
53213
+ }
53214
+ ]
53215
+ };
53216
+ } catch (error48) {
53217
+ return this.handleError("getCognitiveBudget", error48);
53218
+ }
53219
+ }
51834
53220
  /**
51835
53221
  * Handle errors consistently
51836
53222
  */
@@ -51905,22 +53291,22 @@ async function mcp() {
51905
53291
  }
51906
53292
 
51907
53293
  // src/commands/studio.ts
51908
- import { existsSync as existsSync3 } from "fs";
51909
- import { dirname as dirname3, join as join10 } from "path";
53294
+ import { existsSync as existsSync4 } from "fs";
53295
+ import { dirname as dirname3, join as join11 } from "path";
51910
53296
  import { fileURLToPath as fileURLToPath3 } from "url";
51911
53297
  import { execa as execa2 } from "execa";
51912
53298
  var __dirname2 = dirname3(fileURLToPath3(import.meta.url));
51913
53299
  async function studio() {
51914
53300
  const cwd = process.cwd();
51915
53301
  const paths = getRaftersPaths(cwd);
51916
- if (!existsSync3(paths.root)) {
53302
+ if (!existsSync4(paths.root)) {
51917
53303
  console.error('No .rafters/ directory found. Run "rafters init" first.');
51918
53304
  process.exit(1);
51919
53305
  }
51920
- const devStudioPath = join10(__dirname2, "..", "..", "..", "studio");
51921
- const prodStudioPath = join10(__dirname2, "..", "node_modules", "@rafters", "studio");
51922
- const studioPath = existsSync3(devStudioPath) ? devStudioPath : prodStudioPath;
51923
- if (!existsSync3(studioPath)) {
53306
+ const devStudioPath = join11(__dirname2, "..", "..", "..", "studio");
53307
+ const prodStudioPath = join11(__dirname2, "..", "node_modules", "@rafters", "studio");
53308
+ const studioPath = existsSync4(devStudioPath) ? devStudioPath : prodStudioPath;
53309
+ if (!existsSync4(studioPath)) {
51924
53310
  console.error("Studio package not found. Please reinstall @rafters/cli.");
51925
53311
  process.exit(1);
51926
53312
  }
@@ -51950,7 +53336,7 @@ async function studio() {
51950
53336
  var program = new Command();
51951
53337
  program.name("rafters").description("Design system CLI - scaffold tokens and serve MCP").version("0.0.1");
51952
53338
  program.command("init").description("Initialize .rafters/ with default tokens and config").option("-r, --rebuild", "Regenerate output files from existing tokens").option("--reset", "Re-run generators fresh, replacing persisted tokens").option("--agent", "Output JSON for machine consumption").action(withErrorHandler(init));
51953
- program.command("add").description("Add rafters components to the project").argument("[components...]", "Component names to add").option("--list", "List available components").option("--overwrite", "Overwrite existing component files").option("--registry-url <url>", "Custom registry URL").option("--agent", "Output JSON for machine consumption").action(withErrorHandler(add));
53339
+ program.command("add").description("Add rafters components to the project").argument("[components...]", "Component names to add").option("--list", "List available components").option("--overwrite", "Overwrite existing component files").option("--update", "Re-fetch named components from registry").option("--update-all", "Re-fetch all installed components from registry").option("--registry-url <url>", "Custom registry URL").option("--agent", "Output JSON for machine consumption").action(withErrorHandler(add));
51954
53340
  program.command("mcp").description("Start MCP server for AI agent access (stdio)").action(mcp);
51955
53341
  program.command("studio").description("Open Studio UI for visual token editing").action(studio);
51956
53342
  program.parse();