rafters 0.0.8 → 0.0.12

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,22 +12558,28 @@ 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(["ui", "primitive", "composite"]);
12565
12567
  var RegistryItemSchema = external_exports.object({
12566
12568
  name: external_exports.string(),
12567
12569
  type: RegistryItemTypeSchema,
12568
12570
  description: external_exports.string().optional(),
12569
12571
  primitives: external_exports.array(external_exports.string()),
12570
- files: external_exports.array(RegistryFileSchema)
12572
+ files: external_exports.array(RegistryFileSchema),
12573
+ rules: external_exports.array(external_exports.string()).default([]),
12574
+ composites: external_exports.array(external_exports.string()).default([])
12571
12575
  });
12572
12576
  var RegistryIndexSchema = external_exports.object({
12573
12577
  name: external_exports.string(),
12574
12578
  homepage: external_exports.string(),
12575
12579
  components: external_exports.array(external_exports.string()),
12576
- primitives: external_exports.array(external_exports.string())
12580
+ primitives: external_exports.array(external_exports.string()),
12581
+ composites: external_exports.array(external_exports.string()).default([]),
12582
+ rules: external_exports.array(external_exports.string()).default([])
12577
12583
  });
12578
12584
 
12579
12585
  // src/registry/client.ts
@@ -12644,6 +12650,30 @@ var RegistryClient = class {
12644
12650
  this.cache.set(cacheKey, item);
12645
12651
  return item;
12646
12652
  }
12653
+ /**
12654
+ * Fetch a composite by name
12655
+ */
12656
+ async fetchComposite(name2) {
12657
+ const cacheKey = `composite:${name2}`;
12658
+ const cached2 = this.cache.get(cacheKey);
12659
+ if (cached2) {
12660
+ return cached2;
12661
+ }
12662
+ const url2 = `${this.baseUrl}/registry/composites/${name2}.json`;
12663
+ const response = await fetch(url2);
12664
+ if (response.status === 404) {
12665
+ throw new Error(`Composite "${name2}" not found`);
12666
+ }
12667
+ if (!response.ok) {
12668
+ throw new Error(
12669
+ `Failed to fetch composite "${name2}": ${response.status} ${response.statusText}`
12670
+ );
12671
+ }
12672
+ const data = await response.json();
12673
+ const item = RegistryItemSchema.parse(data);
12674
+ this.cache.set(cacheKey, item);
12675
+ return item;
12676
+ }
12647
12677
  /**
12648
12678
  * Fetch a registry item (component or primitive) by name
12649
12679
  * Tries component first, then primitive
@@ -12669,6 +12699,13 @@ var RegistryClient = class {
12669
12699
  const index = await this.fetchIndex();
12670
12700
  return index.components.map((name2) => ({ name: name2 }));
12671
12701
  }
12702
+ /**
12703
+ * List all available composites
12704
+ */
12705
+ async listComposites() {
12706
+ const index = await this.fetchIndex();
12707
+ return index.composites.map((name2) => ({ name: name2 }));
12708
+ }
12672
12709
  /**
12673
12710
  * Check if a component exists in the registry
12674
12711
  */
@@ -12714,18 +12751,64 @@ var RegistryClient = class {
12714
12751
  };
12715
12752
  var registryClient = new RegistryClient();
12716
12753
 
12717
- // src/utils/paths.ts
12718
- import { join } from "path";
12719
- function getRaftersPaths(projectRoot = process.cwd()) {
12720
- const root = join(projectRoot, ".rafters");
12754
+ // src/utils/exports.ts
12755
+ var DEFAULT_EXPORTS = {
12756
+ tailwind: true,
12757
+ typescript: true,
12758
+ dtcg: false,
12759
+ compiled: false
12760
+ };
12761
+ var EXPORT_CHOICES = [
12762
+ {
12763
+ name: "Tailwind CSS (web projects)",
12764
+ value: "tailwind",
12765
+ checked: true
12766
+ },
12767
+ {
12768
+ name: "TypeScript (type-safe constants)",
12769
+ value: "typescript",
12770
+ checked: true
12771
+ },
12772
+ {
12773
+ name: "DTCG JSON (Figma Tokens, Style Dictionary)",
12774
+ value: "dtcg",
12775
+ checked: false
12776
+ },
12777
+ {
12778
+ name: "Standalone CSS (pre-built, no Tailwind required)",
12779
+ value: "compiled",
12780
+ checked: false
12781
+ }
12782
+ ];
12783
+ var FUTURE_EXPORTS = [
12784
+ {
12785
+ name: "iOS (Swift/SwiftUI)",
12786
+ value: "tailwind",
12787
+ // placeholder
12788
+ checked: false,
12789
+ disabled: "coming soon"
12790
+ },
12791
+ {
12792
+ name: "Android (Compose)",
12793
+ value: "tailwind",
12794
+ // placeholder
12795
+ checked: false,
12796
+ disabled: "coming soon"
12797
+ }
12798
+ ];
12799
+ function selectionsToConfig(selections) {
12721
12800
  return {
12722
- root,
12723
- config: join(root, "config.rafters.json"),
12724
- tokens: join(root, "tokens"),
12725
- output: join(root, "output")
12801
+ tailwind: selections.includes("tailwind"),
12802
+ typescript: selections.includes("typescript"),
12803
+ dtcg: selections.includes("dtcg"),
12804
+ compiled: selections.includes("compiled")
12726
12805
  };
12727
12806
  }
12728
12807
 
12808
+ // src/utils/install-registry-deps.ts
12809
+ import { readFile } from "fs/promises";
12810
+ import { join } from "path";
12811
+
12729
12812
  // src/utils/ui.ts
12730
12813
  import ora from "ora";
12731
12814
  var context = {
@@ -12844,8 +12927,9 @@ function log(event) {
12844
12927
  context.spinner?.succeed("Files written");
12845
12928
  const deps = event.dependencies;
12846
12929
  const devDeps = event.devDependencies;
12930
+ const skippedDeps = event.skipped ?? [];
12847
12931
  if (deps.length > 0 || devDeps.length > 0) {
12848
- console.log(" Dependencies to install:");
12932
+ console.log(" Dependencies installed:");
12849
12933
  for (const dep of deps) {
12850
12934
  console.log(` ${dep}`);
12851
12935
  }
@@ -12853,9 +12937,32 @@ function log(event) {
12853
12937
  console.log(` ${dep} (dev)`);
12854
12938
  }
12855
12939
  }
12856
- context.spinner = ora("Installing dependencies...").start();
12940
+ if (skippedDeps.length > 0) {
12941
+ console.log(" Dependencies skipped (already installed or internal):");
12942
+ for (const dep of skippedDeps) {
12943
+ console.log(` ${dep}`);
12944
+ }
12945
+ }
12946
+ break;
12947
+ }
12948
+ case "add:deps:no-package-json":
12949
+ console.warn(` Warning: ${event.message}`);
12950
+ break;
12951
+ case "add:deps:dry-run": {
12952
+ const dryDeps = event.dependencies;
12953
+ console.log(" [dry-run] Would install:");
12954
+ for (const dep of dryDeps) {
12955
+ console.log(` ${dep}`);
12956
+ }
12857
12957
  break;
12858
12958
  }
12959
+ case "add:deps:install-failed":
12960
+ context.spinner?.fail("Failed to install dependencies");
12961
+ console.log(` ${event.message}`);
12962
+ if (event.suggestion) {
12963
+ console.log(` ${event.suggestion}`);
12964
+ }
12965
+ break;
12859
12966
  case "add:complete":
12860
12967
  context.spinner?.succeed(
12861
12968
  `Added ${event.installed} component${event.installed !== 1 ? "s" : ""}`
@@ -12973,6 +13080,127 @@ async function installWithPackageManager(packageManager, dependencies, options)
12973
13080
  }
12974
13081
  }
12975
13082
 
13083
+ // src/utils/install-registry-deps.ts
13084
+ function parseDependency(dep) {
13085
+ const trimmed = dep.trim();
13086
+ if (!trimmed) {
13087
+ return { name: "", version: void 0 };
13088
+ }
13089
+ const versionAt = trimmed.lastIndexOf("@");
13090
+ if (versionAt <= 0) {
13091
+ return { name: trimmed, version: void 0 };
13092
+ }
13093
+ return {
13094
+ name: trimmed.slice(0, versionAt),
13095
+ version: trimmed.slice(versionAt + 1)
13096
+ };
13097
+ }
13098
+ async function readInstalledDeps(targetDir) {
13099
+ let raw;
13100
+ try {
13101
+ raw = await readFile(join(targetDir, "package.json"), "utf-8");
13102
+ } catch {
13103
+ return { packageJsonFound: false, installed: /* @__PURE__ */ new Set() };
13104
+ }
13105
+ try {
13106
+ const pkg = JSON.parse(raw);
13107
+ const installed = /* @__PURE__ */ new Set();
13108
+ const depFields = ["dependencies", "devDependencies", "peerDependencies"];
13109
+ for (const field of depFields) {
13110
+ const section = pkg[field];
13111
+ if (section && typeof section === "object") {
13112
+ for (const name2 of Object.keys(section)) {
13113
+ installed.add(name2);
13114
+ }
13115
+ }
13116
+ }
13117
+ return { packageJsonFound: true, installed };
13118
+ } catch {
13119
+ return { packageJsonFound: true, installed: /* @__PURE__ */ new Set() };
13120
+ }
13121
+ }
13122
+ async function installRegistryDependencies(items, targetDir, options = {}) {
13123
+ const result = {
13124
+ installed: [],
13125
+ skipped: [],
13126
+ devInstalled: [],
13127
+ failed: []
13128
+ };
13129
+ const allDeps = new Set(items.flatMap((item) => item.files.flatMap((file2) => file2.dependencies)));
13130
+ if (allDeps.size === 0) {
13131
+ return result;
13132
+ }
13133
+ const externalDeps = [];
13134
+ for (const dep of allDeps) {
13135
+ if (parseDependency(dep).name.startsWith("@rafters/")) {
13136
+ result.skipped.push(dep);
13137
+ } else {
13138
+ externalDeps.push(dep);
13139
+ }
13140
+ }
13141
+ if (externalDeps.length === 0) {
13142
+ return result;
13143
+ }
13144
+ const { packageJsonFound, installed: installedInProject } = await readInstalledDeps(targetDir);
13145
+ const toInstall = [];
13146
+ if (!packageJsonFound) {
13147
+ log({
13148
+ event: "add:deps:no-package-json",
13149
+ message: "No package.json found. Run npm init or pnpm init first.",
13150
+ targetDir
13151
+ });
13152
+ toInstall.push(...externalDeps);
13153
+ } else {
13154
+ for (const dep of externalDeps) {
13155
+ const { name: name2 } = parseDependency(dep);
13156
+ if (installedInProject.has(name2)) {
13157
+ result.skipped.push(dep);
13158
+ } else {
13159
+ toInstall.push(dep);
13160
+ }
13161
+ }
13162
+ }
13163
+ if (toInstall.length === 0) {
13164
+ return result;
13165
+ }
13166
+ if (options.dryRun) {
13167
+ log({
13168
+ event: "add:deps:dry-run",
13169
+ dependencies: toInstall
13170
+ });
13171
+ return result;
13172
+ }
13173
+ try {
13174
+ await updateDependencies(toInstall, [], {
13175
+ cwd: targetDir,
13176
+ silent: options.silent ?? false
13177
+ });
13178
+ result.installed = toInstall;
13179
+ } catch (err) {
13180
+ result.failed = toInstall;
13181
+ const message = err instanceof Error ? err.message : String(err);
13182
+ log({
13183
+ event: "add:deps:install-failed",
13184
+ message: `Failed to install dependencies: ${message}`,
13185
+ dependencies: toInstall,
13186
+ suggestion: `Manually install: ${toInstall.join(" ")}`
13187
+ });
13188
+ }
13189
+ return result;
13190
+ }
13191
+
13192
+ // src/utils/paths.ts
13193
+ import { join as join2 } from "path";
13194
+ function getRaftersPaths(projectRoot = process.cwd()) {
13195
+ const root = join2(projectRoot, ".rafters");
13196
+ return {
13197
+ root,
13198
+ config: join2(root, "config.rafters.json"),
13199
+ tokens: join2(root, "tokens"),
13200
+ output: join2(root, "output")
13201
+ };
13202
+ }
13203
+
12976
13204
  // src/commands/add.ts
12977
13205
  async function isInitialized(cwd) {
12978
13206
  const paths = getRaftersPaths(cwd);
@@ -12986,7 +13214,7 @@ async function isInitialized(cwd) {
12986
13214
  async function loadConfig(cwd) {
12987
13215
  const paths = getRaftersPaths(cwd);
12988
13216
  try {
12989
- const content = await readFile(paths.config, "utf-8");
13217
+ const content = await readFile2(paths.config, "utf-8");
12990
13218
  return JSON.parse(content);
12991
13219
  } catch (err) {
12992
13220
  if (existsSync(paths.config)) {
@@ -12996,6 +13224,56 @@ async function loadConfig(cwd) {
12996
13224
  return null;
12997
13225
  }
12998
13226
  }
13227
+ async function saveConfig(cwd, config3) {
13228
+ const paths = getRaftersPaths(cwd);
13229
+ await writeFile(paths.config, JSON.stringify(config3, null, 2));
13230
+ }
13231
+ function getInstalledNames(config3) {
13232
+ if (!config3?.installed) return [];
13233
+ const names2 = /* @__PURE__ */ new Set([
13234
+ ...config3.installed.components,
13235
+ ...config3.installed.primitives,
13236
+ ...config3.installed.composites ?? []
13237
+ ]);
13238
+ return [...names2].sort();
13239
+ }
13240
+ var FOLDER_NAMES = /* @__PURE__ */ new Set(["composites"]);
13241
+ function isAlreadyInstalled(config3, item) {
13242
+ if (!config3?.installed) return false;
13243
+ if (item.type === "ui") {
13244
+ return config3.installed.components.includes(item.name);
13245
+ }
13246
+ if (item.type === "composite") {
13247
+ return (config3.installed.composites ?? []).includes(item.name);
13248
+ }
13249
+ return config3.installed.primitives.includes(item.name);
13250
+ }
13251
+ function trackInstalled(config3, items) {
13252
+ if (!config3.installed) {
13253
+ config3.installed = { components: [], primitives: [], composites: [] };
13254
+ }
13255
+ if (!config3.installed.composites) {
13256
+ config3.installed.composites = [];
13257
+ }
13258
+ for (const item of items) {
13259
+ if (item.type === "ui") {
13260
+ if (!config3.installed.components.includes(item.name)) {
13261
+ config3.installed.components.push(item.name);
13262
+ }
13263
+ } else if (item.type === "composite") {
13264
+ if (!config3.installed.composites.includes(item.name)) {
13265
+ config3.installed.composites.push(item.name);
13266
+ }
13267
+ } else {
13268
+ if (!config3.installed.primitives.includes(item.name)) {
13269
+ config3.installed.primitives.push(item.name);
13270
+ }
13271
+ }
13272
+ }
13273
+ config3.installed.components.sort();
13274
+ config3.installed.primitives.sort();
13275
+ config3.installed.composites.sort();
13276
+ }
12999
13277
  function transformPath(registryPath, config3) {
13000
13278
  if (!config3) return registryPath;
13001
13279
  if (registryPath.startsWith("components/ui/")) {
@@ -13004,10 +13282,13 @@ function transformPath(registryPath, config3) {
13004
13282
  if (registryPath.startsWith("lib/primitives/")) {
13005
13283
  return registryPath.replace("lib/primitives/", `${config3.primitivesPath}/`);
13006
13284
  }
13285
+ if (registryPath.startsWith("composites/")) {
13286
+ return registryPath.replace("composites/", `${config3.compositesPath}/`);
13287
+ }
13007
13288
  return registryPath;
13008
13289
  }
13009
13290
  function fileExists(cwd, relativePath) {
13010
- return existsSync(join2(cwd, relativePath));
13291
+ return existsSync(join3(cwd, relativePath));
13011
13292
  }
13012
13293
  function transformFileContent(content, config3) {
13013
13294
  let transformed = content;
@@ -13047,7 +13328,7 @@ async function installItem(cwd, item, options, config3) {
13047
13328
  let skipped = false;
13048
13329
  for (const file2 of item.files) {
13049
13330
  const projectPath = transformPath(file2.path, config3);
13050
- const targetPath = join2(cwd, projectPath);
13331
+ const targetPath = join3(cwd, projectPath);
13051
13332
  if (fileExists(cwd, projectPath)) {
13052
13333
  if (!options.overwrite) {
13053
13334
  log({
@@ -13071,33 +13352,36 @@ async function installItem(cwd, item, options, config3) {
13071
13352
  files: installedFiles
13072
13353
  };
13073
13354
  }
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) {
13355
+ async function add(componentArgs, options) {
13090
13356
  setAgentMode(options.agent ?? false);
13357
+ let components = componentArgs;
13091
13358
  const client = new RegistryClient(options.registryUrl);
13359
+ let folder;
13360
+ const firstArg = components[0];
13361
+ if (firstArg && FOLDER_NAMES.has(firstArg)) {
13362
+ folder = firstArg;
13363
+ components = components.slice(1);
13364
+ }
13092
13365
  if (options.list) {
13093
13366
  const availableComponents = await client.listComponents();
13367
+ const availableComposites = await client.listComposites();
13094
13368
  if (options.agent) {
13095
- log({ event: "add:list", components: availableComponents });
13369
+ log({
13370
+ event: "add:list",
13371
+ components: availableComponents,
13372
+ composites: availableComposites
13373
+ });
13096
13374
  } else {
13097
13375
  console.log("Available components:\n");
13098
13376
  for (const comp of availableComponents) {
13099
13377
  console.log(` ${comp.name} ${comp.description ?? ""}`);
13100
13378
  }
13379
+ if (availableComposites.length > 0) {
13380
+ console.log("\nAvailable composites:\n");
13381
+ for (const comp of availableComposites) {
13382
+ console.log(` ${comp.name} ${comp.description ?? ""}`);
13383
+ }
13384
+ }
13101
13385
  }
13102
13386
  return;
13103
13387
  }
@@ -13109,6 +13393,24 @@ async function add(components, options) {
13109
13393
  return;
13110
13394
  }
13111
13395
  const config3 = await loadConfig(cwd);
13396
+ if (options.update) {
13397
+ options.overwrite = true;
13398
+ }
13399
+ if (options.updateAll) {
13400
+ options.overwrite = true;
13401
+ if (!config3) {
13402
+ error46("No rafters config found. Run 'rafters init' first.");
13403
+ process.exitCode = 1;
13404
+ return;
13405
+ }
13406
+ const installedNames = getInstalledNames(config3);
13407
+ if (installedNames.length === 0) {
13408
+ error46("No installed components found. Use 'rafters add <component>' to install first.");
13409
+ process.exitCode = 1;
13410
+ return;
13411
+ }
13412
+ components = installedNames;
13413
+ }
13112
13414
  if (components.length === 0) {
13113
13415
  error46("No components specified. Usage: rafters add <component...>");
13114
13416
  process.exitCode = 1;
@@ -13122,15 +13424,23 @@ async function add(components, options) {
13122
13424
  });
13123
13425
  const allItems = [];
13124
13426
  const seen = /* @__PURE__ */ new Set();
13125
- for (const componentName of components) {
13427
+ for (const itemName of components) {
13126
13428
  try {
13127
- const items = await client.resolveDependencies(componentName, seen);
13128
- allItems.push(...items);
13429
+ if (folder === "composites") {
13430
+ if (!seen.has(itemName)) {
13431
+ const item = await client.fetchComposite(itemName);
13432
+ seen.add(itemName);
13433
+ allItems.push(item);
13434
+ }
13435
+ } else {
13436
+ const items = await client.resolveDependencies(itemName, seen);
13437
+ allItems.push(...items);
13438
+ }
13129
13439
  } catch (err) {
13130
13440
  if (err instanceof Error) {
13131
13441
  error46(err.message);
13132
13442
  } else {
13133
- error46(`Failed to fetch component "${componentName}"`);
13443
+ error46(`Failed to fetch "${itemName}"`);
13134
13444
  }
13135
13445
  process.exitCode = 1;
13136
13446
  return;
@@ -13138,11 +13448,22 @@ async function add(components, options) {
13138
13448
  }
13139
13449
  const installed = [];
13140
13450
  const skipped = [];
13451
+ const installedItems = [];
13141
13452
  for (const item of allItems) {
13453
+ if (!options.overwrite && isAlreadyInstalled(config3, item)) {
13454
+ log({
13455
+ event: "add:skip",
13456
+ component: item.name,
13457
+ reason: "already installed"
13458
+ });
13459
+ skipped.push(item.name);
13460
+ continue;
13461
+ }
13142
13462
  try {
13143
13463
  const result = await installItem(cwd, item, options, config3);
13144
13464
  if (result.installed) {
13145
13465
  installed.push(item.name);
13466
+ installedItems.push(item);
13146
13467
  log({
13147
13468
  event: "add:installed",
13148
13469
  component: item.name,
@@ -13163,22 +13484,48 @@ async function add(components, options) {
13163
13484
  }
13164
13485
  }
13165
13486
  }
13166
- const { dependencies, devDependencies } = collectDependencies(allItems);
13167
- if (dependencies.length > 0 || devDependencies.length > 0) {
13487
+ const emptyDeps = {
13488
+ installed: [],
13489
+ skipped: [],
13490
+ devInstalled: [],
13491
+ failed: []
13492
+ };
13493
+ let depsResult = emptyDeps;
13494
+ try {
13495
+ depsResult = await installRegistryDependencies(allItems, cwd);
13496
+ } catch (err) {
13497
+ const message = err instanceof Error ? err.message : String(err);
13498
+ log({
13499
+ event: "add:deps:install-failed",
13500
+ message: `Failed to process dependencies: ${message}`,
13501
+ dependencies: [],
13502
+ suggestion: "Check package.json and try installing dependencies manually."
13503
+ });
13504
+ }
13505
+ if (depsResult.installed.length > 0 || depsResult.skipped.length > 0) {
13168
13506
  log({
13169
13507
  event: "add:dependencies",
13170
- dependencies,
13171
- devDependencies
13508
+ dependencies: depsResult.installed,
13509
+ devDependencies: depsResult.devInstalled,
13510
+ skipped: depsResult.skipped
13172
13511
  });
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
- }
13512
+ }
13513
+ if (installedItems.length > 0 && config3) {
13514
+ trackInstalled(config3, installedItems);
13515
+ await saveConfig(cwd, config3);
13516
+ } else if (installedItems.length > 0 && !config3) {
13517
+ const newConfig = {
13518
+ framework: "unknown",
13519
+ componentsPath: "components/ui",
13520
+ primitivesPath: "lib/primitives",
13521
+ compositesPath: "composites",
13522
+ cssPath: null,
13523
+ shadcn: false,
13524
+ exports: DEFAULT_EXPORTS,
13525
+ installed: { components: [], primitives: [], composites: [] }
13526
+ };
13527
+ trackInstalled(newConfig, installedItems);
13528
+ await saveConfig(cwd, newConfig);
13182
13529
  }
13183
13530
  log({
13184
13531
  event: "add:complete",
@@ -13189,19 +13536,19 @@ async function add(components, options) {
13189
13536
  if (skipped.length > 0 && installed.length === 0) {
13190
13537
  log({
13191
13538
  event: "add:hint",
13192
- message: "Some components were skipped. Use --overwrite to replace existing files.",
13539
+ message: "Some components were skipped. Use --update to re-fetch, or --update-all to refresh everything.",
13193
13540
  skipped
13194
13541
  });
13195
- error46("Component already exists. Use --overwrite to replace.");
13542
+ error46("Component already exists. Use --update to re-fetch from registry.");
13196
13543
  process.exitCode = 1;
13197
13544
  }
13198
13545
  }
13199
13546
 
13200
13547
  // src/commands/init.ts
13201
- import { existsSync as existsSync2 } from "fs";
13202
- import { copyFile, mkdir as mkdir3, readFile as readFile4, rm, writeFile as writeFile3 } from "fs/promises";
13548
+ import { existsSync as existsSync3 } from "fs";
13549
+ import { copyFile, mkdir as mkdir3, readFile as readFile5, rm, writeFile as writeFile3 } from "fs/promises";
13203
13550
  import { createRequire } from "module";
13204
- import { join as join8, relative } from "path";
13551
+ import { join as join9, relative } from "path";
13205
13552
  import { checkbox, confirm } from "@inquirer/prompts";
13206
13553
 
13207
13554
  // ../design-tokens/src/plugins/scale.ts
@@ -14126,6 +14473,42 @@ function buildExtensions(token) {
14126
14473
  if (token.customPropertyOnly) {
14127
14474
  extensions.customPropertyOnly = token.customPropertyOnly;
14128
14475
  }
14476
+ if (token.userOverride) {
14477
+ extensions.userOverride = token.userOverride;
14478
+ }
14479
+ if (token.computedValue !== void 0) {
14480
+ extensions.computedValue = convertValue({ ...token, value: token.computedValue });
14481
+ }
14482
+ if (token.generationRule) {
14483
+ extensions.generationRule = token.generationRule;
14484
+ }
14485
+ if (token.pairedWith && token.pairedWith.length > 0) {
14486
+ extensions.pairedWith = token.pairedWith;
14487
+ }
14488
+ if (token.conflictsWith && token.conflictsWith.length > 0) {
14489
+ extensions.conflictsWith = token.conflictsWith;
14490
+ }
14491
+ if (token.applicableComponents && token.applicableComponents.length > 0) {
14492
+ extensions.applicableComponents = token.applicableComponents;
14493
+ }
14494
+ if (token.requiredForComponents && token.requiredForComponents.length > 0) {
14495
+ extensions.requiredForComponents = token.requiredForComponents;
14496
+ }
14497
+ if (token.trustLevel) {
14498
+ extensions.trustLevel = token.trustLevel;
14499
+ }
14500
+ if (token.cognitiveLoad !== void 0) {
14501
+ extensions.cognitiveLoad = token.cognitiveLoad;
14502
+ }
14503
+ if (token.consequence) {
14504
+ extensions.consequence = token.consequence;
14505
+ }
14506
+ if (token.accessibilityLevel) {
14507
+ extensions.accessibilityLevel = token.accessibilityLevel;
14508
+ }
14509
+ if (token.appliesWhen && token.appliesWhen.length > 0) {
14510
+ extensions.appliesWhen = token.appliesWhen;
14511
+ }
14129
14512
  return Object.keys(extensions).length > 0 ? { rafters: extensions } : {};
14130
14513
  }
14131
14514
  function tokenToDTCG(token) {
@@ -24332,7 +24715,7 @@ var require_volume = __commonJS({
24332
24715
  var Dir_1 = require_Dir();
24333
24716
  var resolveCrossPlatform = pathModule.resolve;
24334
24717
  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;
24718
+ var { sep: sep2, relative: relative2, join: join12, dirname: dirname4 } = pathModule.posix ? pathModule.posix : pathModule;
24336
24719
  var kMinPoolSpace = 128;
24337
24720
  var EPERM = "EPERM";
24338
24721
  var ENOENT2 = "ENOENT";
@@ -24398,7 +24781,7 @@ var require_volume = __commonJS({
24398
24781
  function flatten(pathPrefix, node) {
24399
24782
  for (const path2 in node) {
24400
24783
  const contentOrNode = node[path2];
24401
- const joinedPath = join11(pathPrefix, path2);
24784
+ const joinedPath = join12(pathPrefix, path2);
24402
24785
  if (typeof contentOrNode === "string" || contentOrNode instanceof buffer_1.Buffer) {
24403
24786
  flatJSON[joinedPath] = contentOrNode;
24404
24787
  } else if (typeof contentOrNode === "object" && contentOrNode !== null && Object.keys(contentOrNode).length > 0) {
@@ -24555,7 +24938,7 @@ var require_volume = __commonJS({
24555
24938
  return null;
24556
24939
  node = curr === null || curr === void 0 ? void 0 : curr.getNode();
24557
24940
  if (resolveSymlinks && node.isSymlink()) {
24558
- const resolvedPath = pathModule.isAbsolute(node.symlink) ? node.symlink : join11(pathModule.dirname(curr.getPath()), node.symlink);
24941
+ const resolvedPath = pathModule.isAbsolute(node.symlink) ? node.symlink : join12(pathModule.dirname(curr.getPath()), node.symlink);
24559
24942
  steps = filenameToSteps(resolvedPath).concat(steps.slice(i + 1));
24560
24943
  curr = this.root;
24561
24944
  i = 0;
@@ -27479,7 +27862,7 @@ var posix = {
27479
27862
 
27480
27863
  // ../../node_modules/.pnpm/path-unified@0.2.0/node_modules/path-unified/src/posix.js
27481
27864
  var resolve = posResolve;
27482
- var join3 = posJoin;
27865
+ var join4 = posJoin;
27483
27866
 
27484
27867
  // ../../node_modules/.pnpm/@isaacs+balanced-match@4.0.1/node_modules/@isaacs/balanced-match/dist/esm/index.js
27485
27868
  var balanced = (a, b, str) => {
@@ -37378,7 +37761,7 @@ var transforms_default = {
37378
37761
  type: value,
37379
37762
  filter: isAsset,
37380
37763
  transform: function(token, _, options) {
37381
- return join3(process?.cwd() ?? "/", options.usesDtcg ? token.$value : token.value);
37764
+ return join4(process?.cwd() ?? "/", options.usesDtcg ? token.$value : token.value);
37382
37765
  }
37383
37766
  },
37384
37767
  /**
@@ -43578,12 +43961,12 @@ var formats_default = formats2;
43578
43961
  var { androidCopyImages, copyAssets } = actions;
43579
43962
  var { silent } = logVerbosityLevels;
43580
43963
  function getAssetDir(config3) {
43581
- return join3(config3.buildPath ?? "", "android/main/res/drawable-").replace(/\\/g, "/");
43964
+ return join4(config3.buildPath ?? "", "android/main/res/drawable-").replace(/\\/g, "/");
43582
43965
  }
43583
43966
  function getAssetPath(token, imagesDir) {
43584
43967
  const name2 = token.path.slice(2, 4).join("_");
43585
43968
  const dir = `${imagesDir}${token.attributes?.state}`;
43586
- return { file: join3(dir, `${name2}.png`), dir };
43969
+ return { file: join4(dir, `${name2}.png`), dir };
43587
43970
  }
43588
43971
  var actions_default = {
43589
43972
  /**
@@ -43624,7 +44007,7 @@ var actions_default = {
43624
44007
  */
43625
44008
  [copyAssets]: {
43626
44009
  do: async function(_, config3, _options, vol2 = fs2) {
43627
- const assetsPath = join3(config3.buildPath ?? "", "assets");
44010
+ const assetsPath = join4(config3.buildPath ?? "", "assets");
43628
44011
  if (config3.log?.verbosity !== silent) {
43629
44012
  console.log(`Copying assets directory to ${assetsPath}`);
43630
44013
  }
@@ -43638,7 +44021,7 @@ var actions_default = {
43638
44021
  );
43639
44022
  },
43640
44023
  undo: async function(_, config3, _options, vol2 = fs2) {
43641
- const assetsPath = join3(config3.buildPath ?? "", "assets");
44024
+ const assetsPath = join4(config3.buildPath ?? "", "assets");
43642
44025
  if (config3.log?.verbosity !== silent) {
43643
44026
  console.log(`Removing assets directory from ${assetsPath}`);
43644
44027
  }
@@ -44119,8 +44502,14 @@ var DEFAULT_DEPTH_DEFINITIONS = {
44119
44502
  },
44120
44503
  sticky: {
44121
44504
  value: 20,
44122
- meaning: "Sticky elements - headers, navigation",
44123
- contexts: ["sticky-header", "sticky-nav", "floating-actions"],
44505
+ meaning: "Sticky elements - headers, toolbars",
44506
+ contexts: ["sticky-header", "sticky-toolbar", "floating-actions"],
44507
+ stackingContext: true
44508
+ },
44509
+ navigation: {
44510
+ value: 25,
44511
+ meaning: "Navigation panels - sidebars, slide-out nav",
44512
+ contexts: ["sidebar", "navigation-panel", "slide-out-menu"],
44124
44513
  stackingContext: true
44125
44514
  },
44126
44515
  fixed: {
@@ -44146,6 +44535,12 @@ var DEFAULT_DEPTH_DEFINITIONS = {
44146
44535
  meaning: "Tooltips - highest common layer",
44147
44536
  contexts: ["tooltips", "toast-notifications"],
44148
44537
  stackingContext: true
44538
+ },
44539
+ overlay: {
44540
+ value: 70,
44541
+ meaning: "Overlay backdrops - screen-dimming layers behind modals",
44542
+ contexts: ["modal-backdrop", "drawer-backdrop", "sheet-backdrop"],
44543
+ stackingContext: true
44149
44544
  }
44150
44545
  };
44151
44546
  var DEFAULT_SHADOW_DEFINITIONS = {
@@ -46443,6 +46838,78 @@ function generateThemeBlock(groups) {
46443
46838
  lines.push("}");
46444
46839
  return lines.join("\n");
46445
46840
  }
46841
+ var ARTICLE_ELEMENT_STYLES = [
46842
+ // Paragraphs
46843
+ ["p", "leading-relaxed mb-4"],
46844
+ ["p:last-child", "mb-0"],
46845
+ // Headings
46846
+ ["h1", "text-4xl font-bold tracking-tight mb-4 mt-0 text-accent-foreground"],
46847
+ ["h2", "text-3xl font-semibold tracking-tight mb-3 mt-8 text-accent-foreground"],
46848
+ ["h2:first-child", "mt-0"],
46849
+ ["h3", "text-2xl font-semibold mb-2 mt-6 text-accent-foreground"],
46850
+ ["h4", "text-xl font-semibold mb-2 mt-4 text-accent-foreground"],
46851
+ ["h5", "text-lg font-semibold mb-2 mt-4 text-accent-foreground"],
46852
+ ["h6", "text-base font-semibold mb-2 mt-4 text-accent-foreground"],
46853
+ // Lists
46854
+ ["ul", "list-disc pl-6 mb-4"],
46855
+ ["ol", "list-decimal pl-6 mb-4"],
46856
+ ["li", "mb-1"],
46857
+ ["li > ul,\n article li > ol", "mt-1 mb-0"],
46858
+ // Links
46859
+ ["a", "text-primary underline underline-offset-4"],
46860
+ ["a:hover", "text-primary/80"],
46861
+ // Blockquotes
46862
+ ["blockquote", "border-l-4 border-muted pl-4 italic my-4"],
46863
+ // Code
46864
+ ["code", "bg-muted px-1.5 py-0.5 rounded text-sm font-mono"],
46865
+ ["pre", "bg-muted p-4 rounded-lg overflow-x-auto my-4 text-sm font-mono"],
46866
+ ["pre code", "bg-transparent p-0 rounded-none text-[inherit]"],
46867
+ ["kbd", "bg-muted border border-border rounded px-1.5 py-0.5 text-sm font-mono"],
46868
+ // Horizontal rules
46869
+ ["hr", "border-border my-8"],
46870
+ // Media
46871
+ ["img", "rounded-lg my-4 max-w-full h-auto"],
46872
+ ["video", "rounded-lg my-4 max-w-full h-auto"],
46873
+ // Tables
46874
+ ["table", "w-full my-4 border-collapse"],
46875
+ ["caption", "mt-2 text-sm text-muted-foreground text-left"],
46876
+ ["th", "border border-border px-3 py-2 text-left font-semibold"],
46877
+ ["td", "border border-border px-3 py-2"],
46878
+ // Figures
46879
+ ["figure", "my-4"],
46880
+ ["figcaption", "mt-2 text-sm text-muted-foreground"],
46881
+ // Definition lists
46882
+ ["dl", "my-4"],
46883
+ ["dt", "font-semibold mt-2"],
46884
+ ["dd", "pl-4 mb-2"],
46885
+ // Details/Summary
46886
+ ["details", "my-4"],
46887
+ ["summary", "cursor-pointer font-semibold"],
46888
+ // Inline formatting
46889
+ ["strong,\n article b", "font-semibold"],
46890
+ ["mark", "bg-accent text-accent-foreground px-1 rounded"],
46891
+ ["small", "text-sm"],
46892
+ ["sub", "text-xs align-sub"],
46893
+ ["sup", "text-xs align-super"],
46894
+ ["abbr[title]", "underline decoration-dotted underline-offset-4 cursor-help"],
46895
+ ["s,\n article del", "line-through"],
46896
+ ["ins", "underline"]
46897
+ ];
46898
+ function generateArticleBaseLayer() {
46899
+ const lines = [];
46900
+ lines.push("@layer base {");
46901
+ for (const [selector, utilities] of ARTICLE_ELEMENT_STYLES) {
46902
+ if (selector.includes("\n")) {
46903
+ lines.push(` article ${selector} {`);
46904
+ } else {
46905
+ lines.push(` article ${selector} {`);
46906
+ }
46907
+ lines.push(` @apply ${utilities};`);
46908
+ lines.push(" }");
46909
+ }
46910
+ lines.push("}");
46911
+ return lines.join("\n");
46912
+ }
46446
46913
  function generateKeyframes(motionTokens) {
46447
46914
  const keyframeTokens = motionTokens.filter((t2) => t2.name.startsWith("motion-keyframe-"));
46448
46915
  if (keyframeTokens.length === 0) {
@@ -46494,6 +46961,8 @@ function tokensToTailwind(tokens, options = {}) {
46494
46961
  if (keyframes) {
46495
46962
  sections.push(keyframes);
46496
46963
  }
46964
+ sections.push("");
46965
+ sections.push(generateArticleBaseLayer());
46497
46966
  return sections.join("\n");
46498
46967
  }
46499
46968
  function registryToTailwind(registry2, options) {
@@ -46505,7 +46974,7 @@ async function registryToCompiled(registry2, options = {}) {
46505
46974
  const themeCss = registryToTailwind(registry2, { includeImport });
46506
46975
  const { execFileSync } = await import("child_process");
46507
46976
  const { mkdtempSync, writeFileSync, readFileSync, rmSync } = await import("fs");
46508
- const { join: join11, dirname: dirname4 } = await import("path");
46977
+ const { join: join12, dirname: dirname4 } = await import("path");
46509
46978
  const { createRequire: createRequire2 } = await import("module");
46510
46979
  const require2 = createRequire2(import.meta.url);
46511
46980
  let pkgDir;
@@ -46515,10 +46984,10 @@ async function registryToCompiled(registry2, options = {}) {
46515
46984
  } catch {
46516
46985
  throw new Error("Failed to resolve @tailwindcss/cli");
46517
46986
  }
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");
46987
+ const binPath = join12(pkgDir, "dist", "index.mjs");
46988
+ const tempDir = mkdtempSync(join12(pkgDir, ".tmp-compile-"));
46989
+ const tempInput = join12(tempDir, "input.css");
46990
+ const tempOutput = join12(tempDir, "output.css");
46522
46991
  try {
46523
46992
  writeFileSync(tempInput, themeCss);
46524
46993
  const args = [binPath, "-i", tempInput, "-o", tempOutput];
@@ -46797,7 +47266,7 @@ function tagTokenizer() {
46797
47266
 
46798
47267
  // ../../node_modules/.pnpm/comment-parser@1.4.1/node_modules/comment-parser/es6/parser/tokenizers/type.js
46799
47268
  function typeTokenizer(spacing = "compact") {
46800
- const join11 = getJoiner(spacing);
47269
+ const join12 = getJoiner(spacing);
46801
47270
  return (spec) => {
46802
47271
  let curlies = 0;
46803
47272
  let lines = [];
@@ -46840,7 +47309,7 @@ function typeTokenizer(spacing = "compact") {
46840
47309
  }
46841
47310
  parts[0] = parts[0].slice(1);
46842
47311
  parts[parts.length - 1] = parts[parts.length - 1].slice(0, -1);
46843
- spec.type = join11(parts);
47312
+ spec.type = join12(parts);
46844
47313
  return spec;
46845
47314
  };
46846
47315
  }
@@ -46938,9 +47407,9 @@ function nameTokenizer() {
46938
47407
 
46939
47408
  // ../../node_modules/.pnpm/comment-parser@1.4.1/node_modules/comment-parser/es6/parser/tokenizers/description.js
46940
47409
  function descriptionTokenizer(spacing = "compact", markers = Markers) {
46941
- const join11 = getJoiner2(spacing);
47410
+ const join12 = getJoiner2(spacing);
46942
47411
  return (spec) => {
46943
- spec.description = join11(spec.source, markers);
47412
+ spec.description = join12(spec.source, markers);
46944
47413
  return spec;
46945
47414
  };
46946
47415
  }
@@ -47001,11 +47470,11 @@ function getParser4({ startLine = 0, fence = "```", spacing = "compact", markers
47001
47470
  }
47002
47471
 
47003
47472
  // ../../node_modules/.pnpm/comment-parser@1.4.1/node_modules/comment-parser/es6/stringifier/index.js
47004
- function join4(tokens) {
47473
+ function join5(tokens) {
47005
47474
  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
47475
  }
47007
47476
  function getStringifier() {
47008
- return (block) => block.source.map(({ tokens }) => join4(tokens)).join("\n");
47477
+ return (block) => block.source.map(({ tokens }) => join5(tokens)).join("\n");
47009
47478
  }
47010
47479
 
47011
47480
  // ../../node_modules/.pnpm/comment-parser@1.4.1/node_modules/comment-parser/es6/stringifier/inspect.js
@@ -47167,13 +47636,45 @@ function extractPrimitiveDependencies(source) {
47167
47636
  const pkg = match2[1];
47168
47637
  if (pkg && (pkg.includes("/primitives/") || pkg.includes("../primitives/"))) {
47169
47638
  const primitiveName = pkg.split("/").pop()?.replace(/\.(ts|tsx)$/, "");
47170
- if (primitiveName && !primitives.includes(primitiveName) && primitiveName !== "types") {
47639
+ if (primitiveName && !primitives.includes(primitiveName)) {
47171
47640
  primitives.push(primitiveName);
47172
47641
  }
47173
47642
  }
47174
47643
  }
47175
47644
  return primitives;
47176
47645
  }
47646
+ var DEP_TAG_MAP = {
47647
+ dependencies: "runtime",
47648
+ devdependencies: "dev",
47649
+ "internal-dependencies": "internal",
47650
+ internaldependencies: "internal"
47651
+ };
47652
+ function extractJSDocDependencies(source) {
47653
+ const result = { runtime: [], dev: [], internal: [] };
47654
+ const blocks = parse3(source);
47655
+ if (blocks.length === 0) return result;
47656
+ for (const block of blocks) {
47657
+ for (const tag of block.tags) {
47658
+ const field = DEP_TAG_MAP[tag.tag.toLowerCase()];
47659
+ if (!field) continue;
47660
+ const value2 = getTagValue(tag).trim();
47661
+ if (value2) {
47662
+ const tokens = value2.split(/\s+/).filter(Boolean);
47663
+ const validTokens = [];
47664
+ for (const token of tokens) {
47665
+ if (token.startsWith("(")) break;
47666
+ validTokens.push(token);
47667
+ }
47668
+ result[field].push(...validTokens);
47669
+ }
47670
+ }
47671
+ }
47672
+ return {
47673
+ runtime: [...new Set(result.runtime)],
47674
+ dev: [...new Set(result.dev)],
47675
+ internal: [...new Set(result.internal)]
47676
+ };
47677
+ }
47177
47678
  function toDisplayName(name2) {
47178
47679
  return name2.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
47179
47680
  }
@@ -48263,10 +48764,12 @@ var DEPTH_LEVELS = [
48263
48764
  "base",
48264
48765
  "dropdown",
48265
48766
  "sticky",
48767
+ "navigation",
48266
48768
  "fixed",
48267
48769
  "modal",
48268
48770
  "popover",
48269
- "tooltip"
48771
+ "tooltip",
48772
+ "overlay"
48270
48773
  ];
48271
48774
  var ELEVATION_LEVELS = [
48272
48775
  "surface",
@@ -50325,14 +50828,14 @@ function buildColorSystem(options = {}) {
50325
50828
  }
50326
50829
 
50327
50830
  // ../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";
50831
+ import { mkdir as mkdir2, readdir as readdir2, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
50832
+ import { join as join6 } from "path";
50330
50833
  var SCHEMA_URL = "https://rafters.studio/schemas/namespace-tokens.json";
50331
50834
  var VERSION = "1.0.0";
50332
50835
  var NodePersistenceAdapter = class {
50333
50836
  tokensDir;
50334
50837
  constructor(projectRoot) {
50335
- this.tokensDir = join5(projectRoot, ".rafters", "tokens");
50838
+ this.tokensDir = join6(projectRoot, ".rafters", "tokens");
50336
50839
  }
50337
50840
  async load() {
50338
50841
  let files;
@@ -50344,7 +50847,7 @@ var NodePersistenceAdapter = class {
50344
50847
  const allTokens = [];
50345
50848
  for (const file2 of files) {
50346
50849
  if (!file2.endsWith(".rafters.json")) continue;
50347
- const content = await readFile2(join5(this.tokensDir, file2), "utf-8");
50850
+ const content = await readFile3(join6(this.tokensDir, file2), "utf-8");
50348
50851
  const data = NamespaceFileSchema.parse(JSON.parse(content));
50349
50852
  allTokens.push(...data.tokens);
50350
50853
  }
@@ -50372,7 +50875,7 @@ var NodePersistenceAdapter = class {
50372
50875
  };
50373
50876
  NamespaceFileSchema.parse(data);
50374
50877
  await writeFile2(
50375
- join5(this.tokensDir, `${namespace}.rafters.json`),
50878
+ join6(this.tokensDir, `${namespace}.rafters.json`),
50376
50879
  JSON.stringify(data, null, 2)
50377
50880
  );
50378
50881
  }
@@ -50381,7 +50884,7 @@ var NodePersistenceAdapter = class {
50381
50884
 
50382
50885
  // ../design-tokens/src/rule-engine.ts
50383
50886
  import { promises as fs3 } from "fs";
50384
- import { join as join6 } from "path";
50887
+ import { join as join7 } from "path";
50385
50888
  var RuleResultSchema = external_exports.union([
50386
50889
  external_exports.string(),
50387
50890
  external_exports.object({
@@ -50397,11 +50900,18 @@ var RuleContextSchema = external_exports.object({
50397
50900
  });
50398
50901
 
50399
50902
  // src/utils/detect.ts
50400
- import { readFile as readFile3 } from "fs/promises";
50401
- import { join as join7 } from "path";
50903
+ import { existsSync as existsSync2 } from "fs";
50904
+ import { readFile as readFile4 } from "fs/promises";
50905
+ import { join as join8 } from "path";
50906
+ var CONFIG_FILE_FRAMEWORKS = [
50907
+ { files: ["astro.config.mjs", "astro.config.ts", "astro.config.js"], framework: "astro" },
50908
+ { files: ["next.config.mjs", "next.config.ts", "next.config.js"], framework: "next" },
50909
+ { files: ["remix.config.js", "remix.config.ts"], framework: "remix" },
50910
+ { files: ["vite.config.ts", "vite.config.js", "vite.config.mjs"], framework: "vite" }
50911
+ ];
50402
50912
  async function detectFramework(cwd) {
50403
50913
  try {
50404
- const content = await readFile3(join7(cwd, "package.json"), "utf-8");
50914
+ const content = await readFile4(join8(cwd, "package.json"), "utf-8");
50405
50915
  const pkg = JSON.parse(content);
50406
50916
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
50407
50917
  if (deps.next) {
@@ -50420,14 +50930,23 @@ async function detectFramework(cwd) {
50420
50930
  if (deps.vite) {
50421
50931
  return "vite";
50422
50932
  }
50423
- return "unknown";
50424
50933
  } catch {
50425
- return "unknown";
50426
50934
  }
50935
+ return detectFrameworkFromConfigFiles(cwd);
50936
+ }
50937
+ function detectFrameworkFromConfigFiles(cwd) {
50938
+ for (const { files, framework } of CONFIG_FILE_FRAMEWORKS) {
50939
+ for (const file2 of files) {
50940
+ if (existsSync2(join8(cwd, file2))) {
50941
+ return framework;
50942
+ }
50943
+ }
50944
+ }
50945
+ return "unknown";
50427
50946
  }
50428
50947
  async function detectTailwindVersion(cwd) {
50429
50948
  try {
50430
- const content = await readFile3(join7(cwd, "package.json"), "utf-8");
50949
+ const content = await readFile4(join8(cwd, "package.json"), "utf-8");
50431
50950
  const pkg = JSON.parse(content);
50432
50951
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
50433
50952
  const tailwindVersion = deps.tailwindcss;
@@ -50448,7 +50967,7 @@ function isTailwindV3(version2) {
50448
50967
  }
50449
50968
  async function detectShadcn(cwd) {
50450
50969
  try {
50451
- const content = await readFile3(join7(cwd, "components.json"), "utf-8");
50970
+ const content = await readFile4(join8(cwd, "components.json"), "utf-8");
50452
50971
  return JSON.parse(content);
50453
50972
  } catch {
50454
50973
  return null;
@@ -50510,60 +51029,6 @@ async function detectProject(cwd) {
50510
51029
  };
50511
51030
  }
50512
51031
 
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
51032
  // src/commands/init.ts
50568
51033
  async function backupCss(cssPath) {
50569
51034
  const backupPath = cssPath.replace(/\.css$/, ".backup.css");
@@ -50579,28 +51044,44 @@ var CSS_LOCATIONS = {
50579
51044
  unknown: ["src/styles/global.css", "src/index.css", "styles/globals.css"]
50580
51045
  };
50581
51046
  var COMPONENT_PATHS = {
50582
- astro: { components: "src/components/ui", primitives: "src/lib/primitives" },
50583
- next: { components: "components/ui", primitives: "lib/primitives" },
50584
- vite: { components: "src/components/ui", primitives: "src/lib/primitives" },
50585
- remix: { components: "app/components/ui", primitives: "app/lib/primitives" },
50586
- "react-router": { components: "app/components/ui", primitives: "app/lib/primitives" },
50587
- unknown: { components: "components/ui", primitives: "lib/primitives" }
51047
+ astro: {
51048
+ components: "src/components/ui",
51049
+ primitives: "src/lib/primitives",
51050
+ composites: "src/composites"
51051
+ },
51052
+ next: { components: "components/ui", primitives: "lib/primitives", composites: "composites" },
51053
+ vite: {
51054
+ components: "src/components/ui",
51055
+ primitives: "src/lib/primitives",
51056
+ composites: "src/composites"
51057
+ },
51058
+ remix: {
51059
+ components: "app/components/ui",
51060
+ primitives: "app/lib/primitives",
51061
+ composites: "app/composites"
51062
+ },
51063
+ "react-router": {
51064
+ components: "app/components/ui",
51065
+ primitives: "app/lib/primitives",
51066
+ composites: "app/composites"
51067
+ },
51068
+ unknown: { components: "components/ui", primitives: "lib/primitives", composites: "composites" }
50588
51069
  };
50589
51070
  async function findMainCssFile(cwd, framework) {
50590
51071
  const locations = CSS_LOCATIONS[framework] || CSS_LOCATIONS.unknown;
50591
51072
  for (const location of locations) {
50592
- const fullPath = join8(cwd, location);
50593
- if (existsSync2(fullPath)) {
51073
+ const fullPath = join9(cwd, location);
51074
+ if (existsSync3(fullPath)) {
50594
51075
  return location;
50595
51076
  }
50596
51077
  }
50597
51078
  return null;
50598
51079
  }
50599
51080
  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);
51081
+ const fullCssPath = join9(cwd, cssPath);
51082
+ const cssContent = await readFile5(fullCssPath, "utf-8");
51083
+ const cssDir = join9(cwd, cssPath, "..");
51084
+ const themeFullPath = join9(cwd, themePath);
50604
51085
  const relativeThemePath = relative(cssDir, themeFullPath);
50605
51086
  if (cssContent.includes(".rafters/output/rafters.css")) {
50606
51087
  log({ event: "init:css_already_imported", cssPath });
@@ -50688,17 +51169,17 @@ async function generateOutputs(cwd, paths, registry2, exports, shadcn) {
50688
51169
  const outputs = [];
50689
51170
  if (exports.tailwind) {
50690
51171
  const tailwindCss = registryToTailwind(registry2, { includeImport: !shadcn });
50691
- await writeFile3(join8(paths.output, "rafters.css"), tailwindCss);
51172
+ await writeFile3(join9(paths.output, "rafters.css"), tailwindCss);
50692
51173
  outputs.push("rafters.css");
50693
51174
  }
50694
51175
  if (exports.typescript) {
50695
51176
  const typescriptSrc = registryToTypeScript(registry2, { includeJSDoc: true });
50696
- await writeFile3(join8(paths.output, "rafters.ts"), typescriptSrc);
51177
+ await writeFile3(join9(paths.output, "rafters.ts"), typescriptSrc);
50697
51178
  outputs.push("rafters.ts");
50698
51179
  }
50699
51180
  if (exports.dtcg) {
50700
51181
  const dtcgJson = toDTCG(registry2.list());
50701
- await writeFile3(join8(paths.output, "rafters.json"), JSON.stringify(dtcgJson, null, 2));
51182
+ await writeFile3(join9(paths.output, "rafters.json"), JSON.stringify(dtcgJson, null, 2));
50702
51183
  outputs.push("rafters.json");
50703
51184
  }
50704
51185
  if (exports.compiled) {
@@ -50707,19 +51188,26 @@ async function generateOutputs(cwd, paths, registry2, exports, shadcn) {
50707
51188
  }
50708
51189
  log({ event: "init:compiling_css" });
50709
51190
  const compiledCss = await registryToCompiled(registry2, { includeImport: !shadcn });
50710
- await writeFile3(join8(paths.output, "rafters.standalone.css"), compiledCss);
51191
+ await writeFile3(join9(paths.output, "rafters.standalone.css"), compiledCss);
50711
51192
  outputs.push("rafters.standalone.css");
50712
51193
  }
50713
51194
  return outputs;
50714
51195
  }
50715
- async function regenerateFromExisting(cwd, paths, shadcn, isAgentMode2) {
51196
+ async function regenerateFromExisting(cwd, paths, shadcn, isAgentMode2, framework) {
50716
51197
  log({ event: "init:regenerate", cwd });
50717
51198
  let existingConfig = null;
50718
51199
  try {
50719
- const configContent = await readFile4(paths.config, "utf-8");
51200
+ const configContent = await readFile5(paths.config, "utf-8");
50720
51201
  existingConfig = JSON.parse(configContent);
50721
51202
  } catch {
50722
51203
  }
51204
+ if (framework !== "unknown" && existingConfig) {
51205
+ const frameworkPaths = COMPONENT_PATHS[framework] || COMPONENT_PATHS.unknown;
51206
+ existingConfig.framework = framework;
51207
+ existingConfig.componentsPath = frameworkPaths.components;
51208
+ existingConfig.primitivesPath = frameworkPaths.primitives;
51209
+ existingConfig.compositesPath = frameworkPaths.composites;
51210
+ }
50723
51211
  const adapter = new NodePersistenceAdapter(cwd);
50724
51212
  const allTokens = await adapter.load();
50725
51213
  if (allTokens.length === 0) {
@@ -50755,14 +51243,21 @@ async function regenerateFromExisting(cwd, paths, shadcn, isAgentMode2) {
50755
51243
  path: paths.output
50756
51244
  });
50757
51245
  }
50758
- async function resetToDefaults(cwd, paths, shadcn, isAgentMode2) {
51246
+ async function resetToDefaults(cwd, paths, shadcn, isAgentMode2, framework) {
50759
51247
  log({ event: "init:reset", cwd });
50760
51248
  let existingConfig = null;
50761
51249
  try {
50762
- const configContent = await readFile4(paths.config, "utf-8");
51250
+ const configContent = await readFile5(paths.config, "utf-8");
50763
51251
  existingConfig = JSON.parse(configContent);
50764
51252
  } catch {
50765
51253
  }
51254
+ if (framework !== "unknown" && existingConfig) {
51255
+ const frameworkPaths = COMPONENT_PATHS[framework] || COMPONENT_PATHS.unknown;
51256
+ existingConfig.framework = framework;
51257
+ existingConfig.componentsPath = frameworkPaths.components;
51258
+ existingConfig.primitivesPath = frameworkPaths.primitives;
51259
+ existingConfig.compositesPath = frameworkPaths.composites;
51260
+ }
50766
51261
  const adapter = new NodePersistenceAdapter(cwd);
50767
51262
  const existingTokens = await adapter.load();
50768
51263
  const overriddenTokens = existingTokens.filter((t2) => t2.userOverride);
@@ -50779,7 +51274,7 @@ async function resetToDefaults(cwd, paths, shadcn, isAgentMode2) {
50779
51274
  };
50780
51275
  await mkdir3(paths.output, { recursive: true });
50781
51276
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
50782
- const backupPath = join8(paths.output, `reset-${timestamp}.json`);
51277
+ const backupPath = join9(paths.output, `reset-${timestamp}.json`);
50783
51278
  await writeFile3(backupPath, JSON.stringify(backup, null, 2));
50784
51279
  log({
50785
51280
  event: "init:reset_backup",
@@ -50848,12 +51343,12 @@ async function init(options) {
50848
51343
  if (isTailwindV3(tailwindVersion)) {
50849
51344
  throw new Error("Tailwind v3 detected. Rafters requires Tailwind v4.");
50850
51345
  }
50851
- const raftersExists = existsSync2(paths.root);
51346
+ const raftersExists = existsSync3(paths.root);
50852
51347
  if (options.reset && !raftersExists) {
50853
51348
  throw new Error("Nothing to reset. No .rafters/ directory found.");
50854
51349
  }
50855
51350
  if (raftersExists && options.reset) {
50856
- await resetToDefaults(cwd, paths, shadcn, isAgentMode2);
51351
+ await resetToDefaults(cwd, paths, shadcn, isAgentMode2, framework);
50857
51352
  return;
50858
51353
  }
50859
51354
  if (raftersExists && !options.rebuild) {
@@ -50862,14 +51357,14 @@ async function init(options) {
50862
51357
  );
50863
51358
  }
50864
51359
  if (raftersExists && options.rebuild) {
50865
- await regenerateFromExisting(cwd, paths, shadcn, isAgentMode2);
51360
+ await regenerateFromExisting(cwd, paths, shadcn, isAgentMode2, framework);
50866
51361
  return;
50867
51362
  }
50868
51363
  let existingColors = null;
50869
51364
  if (shadcn?.tailwind?.css) {
50870
- const cssPath = join8(cwd, shadcn.tailwind.css);
51365
+ const cssPath = join9(cwd, shadcn.tailwind.css);
50871
51366
  try {
50872
- const cssContent = await readFile4(cssPath, "utf-8");
51367
+ const cssContent = await readFile5(cssPath, "utf-8");
50873
51368
  existingColors = parseCssVariables(cssContent);
50874
51369
  const backupPath = await backupCss(cssPath);
50875
51370
  log({
@@ -50973,9 +51468,15 @@ async function init(options) {
50973
51468
  framework,
50974
51469
  componentsPath: frameworkPaths.components,
50975
51470
  primitivesPath: frameworkPaths.primitives,
51471
+ compositesPath: frameworkPaths.composites,
50976
51472
  cssPath: detectedCssPath,
50977
51473
  shadcn: !!shadcn,
50978
- exports
51474
+ exports,
51475
+ installed: {
51476
+ components: [],
51477
+ primitives: [],
51478
+ composites: []
51479
+ }
50979
51480
  };
50980
51481
  await writeFile3(paths.config, JSON.stringify(config3, null, 2));
50981
51482
  log({
@@ -50991,8 +51492,916 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
50991
51492
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
50992
51493
 
50993
51494
  // src/mcp/tools.ts
50994
- import { readdir as readdir3, readFile as readFile5 } from "fs/promises";
50995
- import { basename, join as join9 } from "path";
51495
+ import { existsSync as existsSync4 } from "fs";
51496
+ import { readdir as readdir3, readFile as readFile6 } from "fs/promises";
51497
+ import { basename, join as join10 } from "path";
51498
+
51499
+ // src/mcp/cognitive-load.ts
51500
+ var BUDGET_TIERS = {
51501
+ focused: 15,
51502
+ page: 30,
51503
+ app: 45
51504
+ };
51505
+ var COMPONENT_SCORES = {
51506
+ // Score 0 -- Structural only, no cognitive demand
51507
+ separator: {
51508
+ dimensions: {
51509
+ decisionDemand: 0,
51510
+ informationDensity: 0,
51511
+ interactionComplexity: 0,
51512
+ contextDisruption: 0,
51513
+ learningCurve: 0
51514
+ },
51515
+ primaryCostDriver: "Structural only, no cognitive demand"
51516
+ },
51517
+ container: {
51518
+ dimensions: {
51519
+ decisionDemand: 0,
51520
+ informationDensity: 0,
51521
+ interactionComplexity: 0,
51522
+ contextDisruption: 0,
51523
+ learningCurve: 0
51524
+ },
51525
+ primaryCostDriver: "Structural only, no cognitive demand"
51526
+ },
51527
+ "aspect-ratio": {
51528
+ dimensions: {
51529
+ decisionDemand: 0,
51530
+ informationDensity: 0,
51531
+ interactionComplexity: 0,
51532
+ contextDisruption: 0,
51533
+ learningCurve: 0
51534
+ },
51535
+ primaryCostDriver: "Structural only, no cognitive demand"
51536
+ },
51537
+ // Score 1 -- Display only, minimal information
51538
+ skeleton: {
51539
+ dimensions: {
51540
+ decisionDemand: 0,
51541
+ informationDensity: 1,
51542
+ interactionComplexity: 0,
51543
+ contextDisruption: 0,
51544
+ learningCurve: 0
51545
+ },
51546
+ primaryCostDriver: "Display only, minimal information"
51547
+ },
51548
+ kbd: {
51549
+ dimensions: {
51550
+ decisionDemand: 0,
51551
+ informationDensity: 1,
51552
+ interactionComplexity: 0,
51553
+ contextDisruption: 0,
51554
+ learningCurve: 0
51555
+ },
51556
+ primaryCostDriver: "Display only, minimal information"
51557
+ },
51558
+ // Score 2 -- Simple state or single information piece
51559
+ badge: {
51560
+ dimensions: {
51561
+ decisionDemand: 0,
51562
+ informationDensity: 1,
51563
+ interactionComplexity: 0,
51564
+ contextDisruption: 0,
51565
+ learningCurve: 1
51566
+ },
51567
+ primaryCostDriver: "Simple state or single information piece"
51568
+ },
51569
+ breadcrumb: {
51570
+ dimensions: {
51571
+ decisionDemand: 0,
51572
+ informationDensity: 1,
51573
+ interactionComplexity: 1,
51574
+ contextDisruption: 0,
51575
+ learningCurve: 0
51576
+ },
51577
+ primaryCostDriver: "Simple state or single information piece"
51578
+ },
51579
+ label: {
51580
+ dimensions: {
51581
+ decisionDemand: 0,
51582
+ informationDensity: 1,
51583
+ interactionComplexity: 0,
51584
+ contextDisruption: 0,
51585
+ learningCurve: 1
51586
+ },
51587
+ primaryCostDriver: "Simple state or single information piece"
51588
+ },
51589
+ checkbox: {
51590
+ dimensions: {
51591
+ decisionDemand: 1,
51592
+ informationDensity: 0,
51593
+ interactionComplexity: 1,
51594
+ contextDisruption: 0,
51595
+ learningCurve: 0
51596
+ },
51597
+ primaryCostDriver: "Simple state or single information piece"
51598
+ },
51599
+ switch: {
51600
+ dimensions: {
51601
+ decisionDemand: 1,
51602
+ informationDensity: 0,
51603
+ interactionComplexity: 1,
51604
+ contextDisruption: 0,
51605
+ learningCurve: 0
51606
+ },
51607
+ primaryCostDriver: "Simple state or single information piece"
51608
+ },
51609
+ toggle: {
51610
+ dimensions: {
51611
+ decisionDemand: 1,
51612
+ informationDensity: 0,
51613
+ interactionComplexity: 1,
51614
+ contextDisruption: 0,
51615
+ learningCurve: 0
51616
+ },
51617
+ primaryCostDriver: "Simple state or single information piece"
51618
+ },
51619
+ "button-group": {
51620
+ dimensions: {
51621
+ decisionDemand: 1,
51622
+ informationDensity: 1,
51623
+ interactionComplexity: 0,
51624
+ contextDisruption: 0,
51625
+ learningCurve: 0
51626
+ },
51627
+ primaryCostDriver: "Simple state or single information piece"
51628
+ },
51629
+ card: {
51630
+ dimensions: {
51631
+ decisionDemand: 0,
51632
+ informationDensity: 1,
51633
+ interactionComplexity: 0,
51634
+ contextDisruption: 0,
51635
+ learningCurve: 1
51636
+ },
51637
+ primaryCostDriver: "Simple state or single information piece"
51638
+ },
51639
+ collapsible: {
51640
+ dimensions: {
51641
+ decisionDemand: 1,
51642
+ informationDensity: 0,
51643
+ interactionComplexity: 1,
51644
+ contextDisruption: 0,
51645
+ learningCurve: 0
51646
+ },
51647
+ primaryCostDriver: "Simple state or single information piece"
51648
+ },
51649
+ avatar: {
51650
+ dimensions: {
51651
+ decisionDemand: 0,
51652
+ informationDensity: 1,
51653
+ interactionComplexity: 0,
51654
+ contextDisruption: 0,
51655
+ learningCurve: 1
51656
+ },
51657
+ primaryCostDriver: "Simple state or single information piece"
51658
+ },
51659
+ "scroll-area": {
51660
+ dimensions: {
51661
+ decisionDemand: 0,
51662
+ informationDensity: 1,
51663
+ interactionComplexity: 1,
51664
+ contextDisruption: 0,
51665
+ learningCurve: 0
51666
+ },
51667
+ primaryCostDriver: "Simple state or single information piece"
51668
+ },
51669
+ tooltip: {
51670
+ dimensions: {
51671
+ decisionDemand: 0,
51672
+ informationDensity: 1,
51673
+ interactionComplexity: 0,
51674
+ contextDisruption: 0,
51675
+ learningCurve: 1
51676
+ },
51677
+ primaryCostDriver: "Simple state or single information piece"
51678
+ },
51679
+ typography: {
51680
+ dimensions: {
51681
+ decisionDemand: 0,
51682
+ informationDensity: 1,
51683
+ interactionComplexity: 0,
51684
+ contextDisruption: 0,
51685
+ learningCurve: 1
51686
+ },
51687
+ primaryCostDriver: "Simple state or single information piece"
51688
+ },
51689
+ spinner: {
51690
+ dimensions: {
51691
+ decisionDemand: 0,
51692
+ informationDensity: 1,
51693
+ interactionComplexity: 0,
51694
+ contextDisruption: 0,
51695
+ learningCurve: 1
51696
+ },
51697
+ primaryCostDriver: "Simple state or single information piece"
51698
+ },
51699
+ empty: {
51700
+ dimensions: {
51701
+ decisionDemand: 1,
51702
+ informationDensity: 1,
51703
+ interactionComplexity: 0,
51704
+ contextDisruption: 0,
51705
+ learningCurve: 0
51706
+ },
51707
+ primaryCostDriver: "Simple state or single information piece"
51708
+ },
51709
+ "toggle-group": {
51710
+ dimensions: {
51711
+ decisionDemand: 1,
51712
+ informationDensity: 1,
51713
+ interactionComplexity: 0,
51714
+ contextDisruption: 0,
51715
+ learningCurve: 0
51716
+ },
51717
+ primaryCostDriver: "Simple state or single information piece"
51718
+ },
51719
+ // Score 3 -- One decision + one interaction mode
51720
+ button: {
51721
+ dimensions: {
51722
+ decisionDemand: 1,
51723
+ informationDensity: 1,
51724
+ interactionComplexity: 1,
51725
+ contextDisruption: 0,
51726
+ learningCurve: 0
51727
+ },
51728
+ primaryCostDriver: "One decision + one interaction mode"
51729
+ },
51730
+ alert: {
51731
+ dimensions: {
51732
+ decisionDemand: 0,
51733
+ informationDensity: 1,
51734
+ interactionComplexity: 1,
51735
+ contextDisruption: 0,
51736
+ learningCurve: 1
51737
+ },
51738
+ primaryCostDriver: "One decision + one interaction mode"
51739
+ },
51740
+ sidebar: {
51741
+ dimensions: {
51742
+ decisionDemand: 1,
51743
+ informationDensity: 1,
51744
+ interactionComplexity: 1,
51745
+ contextDisruption: 0,
51746
+ learningCurve: 0
51747
+ },
51748
+ primaryCostDriver: "One decision + one interaction mode"
51749
+ },
51750
+ accordion: {
51751
+ dimensions: {
51752
+ decisionDemand: 1,
51753
+ informationDensity: 1,
51754
+ interactionComplexity: 0,
51755
+ contextDisruption: 0,
51756
+ learningCurve: 1
51757
+ },
51758
+ primaryCostDriver: "One decision + one interaction mode"
51759
+ },
51760
+ slider: {
51761
+ dimensions: {
51762
+ decisionDemand: 1,
51763
+ informationDensity: 0,
51764
+ interactionComplexity: 1,
51765
+ contextDisruption: 0,
51766
+ learningCurve: 1
51767
+ },
51768
+ primaryCostDriver: "One decision + one interaction mode"
51769
+ },
51770
+ "radio-group": {
51771
+ dimensions: {
51772
+ decisionDemand: 1,
51773
+ informationDensity: 1,
51774
+ interactionComplexity: 1,
51775
+ contextDisruption: 0,
51776
+ learningCurve: 0
51777
+ },
51778
+ primaryCostDriver: "One decision + one interaction mode"
51779
+ },
51780
+ table: {
51781
+ dimensions: {
51782
+ decisionDemand: 0,
51783
+ informationDensity: 2,
51784
+ interactionComplexity: 0,
51785
+ contextDisruption: 0,
51786
+ learningCurve: 1
51787
+ },
51788
+ primaryCostDriver: "One decision + one interaction mode"
51789
+ },
51790
+ resizable: {
51791
+ dimensions: {
51792
+ decisionDemand: 1,
51793
+ informationDensity: 0,
51794
+ interactionComplexity: 1,
51795
+ contextDisruption: 0,
51796
+ learningCurve: 1
51797
+ },
51798
+ primaryCostDriver: "One decision + one interaction mode"
51799
+ },
51800
+ field: {
51801
+ dimensions: {
51802
+ decisionDemand: 1,
51803
+ informationDensity: 1,
51804
+ interactionComplexity: 1,
51805
+ contextDisruption: 0,
51806
+ learningCurve: 0
51807
+ },
51808
+ primaryCostDriver: "One decision + one interaction mode"
51809
+ },
51810
+ "hover-card": {
51811
+ dimensions: {
51812
+ decisionDemand: 0,
51813
+ informationDensity: 1,
51814
+ interactionComplexity: 0,
51815
+ contextDisruption: 1,
51816
+ learningCurve: 1
51817
+ },
51818
+ primaryCostDriver: "One decision + one interaction mode"
51819
+ },
51820
+ image: {
51821
+ dimensions: {
51822
+ decisionDemand: 0,
51823
+ informationDensity: 2,
51824
+ interactionComplexity: 0,
51825
+ contextDisruption: 0,
51826
+ learningCurve: 1
51827
+ },
51828
+ primaryCostDriver: "One decision + one interaction mode"
51829
+ },
51830
+ embed: {
51831
+ dimensions: {
51832
+ decisionDemand: 0,
51833
+ informationDensity: 2,
51834
+ interactionComplexity: 0,
51835
+ contextDisruption: 0,
51836
+ learningCurve: 1
51837
+ },
51838
+ primaryCostDriver: "One decision + one interaction mode"
51839
+ },
51840
+ item: {
51841
+ dimensions: {
51842
+ decisionDemand: 1,
51843
+ informationDensity: 1,
51844
+ interactionComplexity: 1,
51845
+ contextDisruption: 0,
51846
+ learningCurve: 0
51847
+ },
51848
+ primaryCostDriver: "One decision + one interaction mode"
51849
+ },
51850
+ // Score 4 -- Data entry or menu scanning
51851
+ input: {
51852
+ dimensions: {
51853
+ decisionDemand: 1,
51854
+ informationDensity: 1,
51855
+ interactionComplexity: 1,
51856
+ contextDisruption: 0,
51857
+ learningCurve: 1
51858
+ },
51859
+ primaryCostDriver: "Data entry or menu scanning"
51860
+ },
51861
+ textarea: {
51862
+ dimensions: {
51863
+ decisionDemand: 1,
51864
+ informationDensity: 1,
51865
+ interactionComplexity: 1,
51866
+ contextDisruption: 0,
51867
+ learningCurve: 1
51868
+ },
51869
+ primaryCostDriver: "Data entry or menu scanning"
51870
+ },
51871
+ "input-group": {
51872
+ dimensions: {
51873
+ decisionDemand: 1,
51874
+ informationDensity: 1,
51875
+ interactionComplexity: 1,
51876
+ contextDisruption: 0,
51877
+ learningCurve: 1
51878
+ },
51879
+ primaryCostDriver: "Data entry or menu scanning"
51880
+ },
51881
+ "input-otp": {
51882
+ dimensions: {
51883
+ decisionDemand: 1,
51884
+ informationDensity: 1,
51885
+ interactionComplexity: 1,
51886
+ contextDisruption: 0,
51887
+ learningCurve: 1
51888
+ },
51889
+ primaryCostDriver: "Data entry or menu scanning"
51890
+ },
51891
+ carousel: {
51892
+ dimensions: {
51893
+ decisionDemand: 1,
51894
+ informationDensity: 1,
51895
+ interactionComplexity: 1,
51896
+ contextDisruption: 0,
51897
+ learningCurve: 1
51898
+ },
51899
+ primaryCostDriver: "Data entry or menu scanning"
51900
+ },
51901
+ tabs: {
51902
+ dimensions: {
51903
+ decisionDemand: 1,
51904
+ informationDensity: 1,
51905
+ interactionComplexity: 1,
51906
+ contextDisruption: 0,
51907
+ learningCurve: 1
51908
+ },
51909
+ primaryCostDriver: "Data entry or menu scanning"
51910
+ },
51911
+ "context-menu": {
51912
+ dimensions: {
51913
+ decisionDemand: 1,
51914
+ informationDensity: 1,
51915
+ interactionComplexity: 1,
51916
+ contextDisruption: 0,
51917
+ learningCurve: 1
51918
+ },
51919
+ primaryCostDriver: "Data entry or menu scanning"
51920
+ },
51921
+ "dropdown-menu": {
51922
+ dimensions: {
51923
+ decisionDemand: 1,
51924
+ informationDensity: 1,
51925
+ interactionComplexity: 1,
51926
+ contextDisruption: 0,
51927
+ learningCurve: 1
51928
+ },
51929
+ primaryCostDriver: "Data entry or menu scanning"
51930
+ },
51931
+ popover: {
51932
+ dimensions: {
51933
+ decisionDemand: 0,
51934
+ informationDensity: 1,
51935
+ interactionComplexity: 1,
51936
+ contextDisruption: 1,
51937
+ learningCurve: 1
51938
+ },
51939
+ primaryCostDriver: "Data entry or menu scanning"
51940
+ },
51941
+ progress: {
51942
+ dimensions: {
51943
+ decisionDemand: 0,
51944
+ informationDensity: 2,
51945
+ interactionComplexity: 0,
51946
+ contextDisruption: 1,
51947
+ learningCurve: 1
51948
+ },
51949
+ primaryCostDriver: "Data entry or menu scanning"
51950
+ },
51951
+ drawer: {
51952
+ dimensions: {
51953
+ decisionDemand: 1,
51954
+ informationDensity: 1,
51955
+ interactionComplexity: 1,
51956
+ contextDisruption: 1,
51957
+ learningCurve: 0
51958
+ },
51959
+ primaryCostDriver: "Data entry or menu scanning"
51960
+ },
51961
+ grid: {
51962
+ dimensions: {
51963
+ decisionDemand: 0,
51964
+ informationDensity: 2,
51965
+ interactionComplexity: 0,
51966
+ contextDisruption: 0,
51967
+ learningCurve: 2
51968
+ },
51969
+ primaryCostDriver: "Data entry or menu scanning"
51970
+ },
51971
+ "block-wrapper": {
51972
+ dimensions: {
51973
+ decisionDemand: 1,
51974
+ informationDensity: 1,
51975
+ interactionComplexity: 1,
51976
+ contextDisruption: 0,
51977
+ learningCurve: 1
51978
+ },
51979
+ primaryCostDriver: "Data entry or menu scanning"
51980
+ },
51981
+ "inline-toolbar": {
51982
+ dimensions: {
51983
+ decisionDemand: 1,
51984
+ informationDensity: 1,
51985
+ interactionComplexity: 1,
51986
+ contextDisruption: 0,
51987
+ learningCurve: 1
51988
+ },
51989
+ primaryCostDriver: "Data entry or menu scanning"
51990
+ },
51991
+ "editor-toolbar": {
51992
+ dimensions: {
51993
+ decisionDemand: 1,
51994
+ informationDensity: 1,
51995
+ interactionComplexity: 1,
51996
+ contextDisruption: 0,
51997
+ learningCurve: 1
51998
+ },
51999
+ primaryCostDriver: "Data entry or menu scanning"
52000
+ },
52001
+ pagination: {
52002
+ dimensions: {
52003
+ decisionDemand: 1,
52004
+ informationDensity: 1,
52005
+ interactionComplexity: 1,
52006
+ contextDisruption: 0,
52007
+ learningCurve: 1
52008
+ },
52009
+ primaryCostDriver: "Data entry or menu scanning"
52010
+ },
52011
+ // Score 5 -- Multi-step interaction or spatial reasoning
52012
+ sheet: {
52013
+ dimensions: {
52014
+ decisionDemand: 1,
52015
+ informationDensity: 1,
52016
+ interactionComplexity: 1,
52017
+ contextDisruption: 1,
52018
+ learningCurve: 1
52019
+ },
52020
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
52021
+ },
52022
+ "date-picker": {
52023
+ dimensions: {
52024
+ decisionDemand: 1,
52025
+ informationDensity: 1,
52026
+ interactionComplexity: 2,
52027
+ contextDisruption: 0,
52028
+ learningCurve: 1
52029
+ },
52030
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
52031
+ },
52032
+ calendar: {
52033
+ dimensions: {
52034
+ decisionDemand: 1,
52035
+ informationDensity: 2,
52036
+ interactionComplexity: 1,
52037
+ contextDisruption: 0,
52038
+ learningCurve: 1
52039
+ },
52040
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
52041
+ },
52042
+ select: {
52043
+ dimensions: {
52044
+ decisionDemand: 2,
52045
+ informationDensity: 1,
52046
+ interactionComplexity: 1,
52047
+ contextDisruption: 0,
52048
+ learningCurve: 1
52049
+ },
52050
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
52051
+ },
52052
+ "navigation-menu": {
52053
+ dimensions: {
52054
+ decisionDemand: 1,
52055
+ informationDensity: 2,
52056
+ interactionComplexity: 1,
52057
+ contextDisruption: 0,
52058
+ learningCurve: 1
52059
+ },
52060
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
52061
+ },
52062
+ menubar: {
52063
+ dimensions: {
52064
+ decisionDemand: 1,
52065
+ informationDensity: 2,
52066
+ interactionComplexity: 1,
52067
+ contextDisruption: 0,
52068
+ learningCurve: 1
52069
+ },
52070
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
52071
+ },
52072
+ "color-picker": {
52073
+ dimensions: {
52074
+ decisionDemand: 1,
52075
+ informationDensity: 1,
52076
+ interactionComplexity: 2,
52077
+ contextDisruption: 0,
52078
+ learningCurve: 1
52079
+ },
52080
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
52081
+ },
52082
+ "block-canvas": {
52083
+ dimensions: {
52084
+ decisionDemand: 1,
52085
+ informationDensity: 1,
52086
+ interactionComplexity: 1,
52087
+ contextDisruption: 1,
52088
+ learningCurve: 1
52089
+ },
52090
+ primaryCostDriver: "Multi-step interaction or spatial reasoning"
52091
+ },
52092
+ // Score 6 -- Compound interaction modes or learning curve
52093
+ dialog: {
52094
+ dimensions: {
52095
+ decisionDemand: 1,
52096
+ informationDensity: 1,
52097
+ interactionComplexity: 1,
52098
+ contextDisruption: 2,
52099
+ learningCurve: 1
52100
+ },
52101
+ primaryCostDriver: "Compound interaction modes or learning curve"
52102
+ },
52103
+ command: {
52104
+ dimensions: {
52105
+ decisionDemand: 1,
52106
+ informationDensity: 2,
52107
+ interactionComplexity: 1,
52108
+ contextDisruption: 0,
52109
+ learningCurve: 2
52110
+ },
52111
+ primaryCostDriver: "Compound interaction modes or learning curve"
52112
+ },
52113
+ combobox: {
52114
+ dimensions: {
52115
+ decisionDemand: 2,
52116
+ informationDensity: 2,
52117
+ interactionComplexity: 2,
52118
+ contextDisruption: 0,
52119
+ learningCurve: 0
52120
+ },
52121
+ primaryCostDriver: "Compound interaction modes or learning curve"
52122
+ },
52123
+ // Score 7 -- Full context disruption + consequential decision
52124
+ "alert-dialog": {
52125
+ dimensions: {
52126
+ decisionDemand: 2,
52127
+ informationDensity: 1,
52128
+ interactionComplexity: 1,
52129
+ contextDisruption: 2,
52130
+ learningCurve: 1
52131
+ },
52132
+ primaryCostDriver: "Full context disruption + consequential decision"
52133
+ }
52134
+ };
52135
+ var DIMENSION_NAMES = {
52136
+ decisionDemand: "Decision Demand",
52137
+ informationDensity: "Information Density",
52138
+ interactionComplexity: "Interaction Complexity",
52139
+ contextDisruption: "Context Disruption",
52140
+ learningCurve: "Learning Curve"
52141
+ };
52142
+ var HOTSPOT_SUGGESTIONS = {
52143
+ contextDisruption: "Consider non-modal alternative if the decision is not consequential",
52144
+ informationDensity: "Consider progressive disclosure to reduce simultaneous information",
52145
+ interactionComplexity: "Consider a simpler single-mode alternative",
52146
+ decisionDemand: "Can choices be pre-selected or split across steps?",
52147
+ learningCurve: "Ensure discoverability with inline guidance"
52148
+ };
52149
+ function scoreOf(dimensions) {
52150
+ return dimensions.decisionDemand + dimensions.informationDensity + dimensions.interactionComplexity + dimensions.contextDisruption + dimensions.learningCurve;
52151
+ }
52152
+ function highestDimension(dimensions) {
52153
+ let max = -1;
52154
+ let maxKey = "decisionDemand";
52155
+ for (const key of Object.keys(dimensions)) {
52156
+ if (dimensions[key] > max) {
52157
+ max = dimensions[key];
52158
+ maxKey = key;
52159
+ }
52160
+ }
52161
+ return maxKey;
52162
+ }
52163
+ function evaluateComposition(components, tier, enrichment) {
52164
+ const budget = BUDGET_TIERS[tier];
52165
+ const componentCounts = /* @__PURE__ */ new Map();
52166
+ for (const name2 of components) {
52167
+ componentCounts.set(name2, (componentCounts.get(name2) ?? 0) + 1);
52168
+ }
52169
+ let total = 0;
52170
+ const componentResults = [];
52171
+ const unknownComponents = [];
52172
+ for (const [name2, count] of componentCounts) {
52173
+ const profile = COMPONENT_SCORES[name2];
52174
+ if (!profile) {
52175
+ unknownComponents.push(name2);
52176
+ continue;
52177
+ }
52178
+ const score = scoreOf(profile.dimensions);
52179
+ total += score * count;
52180
+ componentResults.push({
52181
+ name: name2,
52182
+ score,
52183
+ count,
52184
+ dimensions: profile.dimensions,
52185
+ primaryCostDriver: profile.primaryCostDriver
52186
+ });
52187
+ }
52188
+ componentResults.sort((a, b) => b.score - a.score);
52189
+ const withinBudget = total <= budget;
52190
+ const budgetResult = {
52191
+ tier,
52192
+ budget,
52193
+ total,
52194
+ status: withinBudget ? "within-budget" : "over-budget"
52195
+ };
52196
+ if (withinBudget) {
52197
+ budgetResult.headroom = budget - total;
52198
+ } else {
52199
+ budgetResult.overage = total - budget;
52200
+ budgetResult.overagePercent = Math.round((total - budget) / budget * 100);
52201
+ }
52202
+ const attentionConflicts = [];
52203
+ const attentionNotes = [];
52204
+ const fullAttentionCaptures = [];
52205
+ const primaryElements = [];
52206
+ for (const [name2] of componentCounts) {
52207
+ const metadata = enrichment.componentIntelligence.get(name2);
52208
+ if (!metadata?.intelligence?.attentionEconomics) continue;
52209
+ const attn = metadata.intelligence.attentionEconomics.toLowerCase();
52210
+ if (attn.includes("full attention capture") || attn.includes("blocks all other")) {
52211
+ fullAttentionCaptures.push(name2);
52212
+ }
52213
+ if (attn.includes("primary") && attn.includes("maximum 1")) {
52214
+ const count = componentCounts.get(name2) ?? 0;
52215
+ if (count > 1) {
52216
+ primaryElements.push(name2);
52217
+ }
52218
+ }
52219
+ }
52220
+ if (fullAttentionCaptures.length > 1) {
52221
+ attentionConflicts.push(`${fullAttentionCaptures.join(" and ")} both capture full attention`);
52222
+ }
52223
+ const buttonCount = componentCounts.get("button") ?? 0;
52224
+ if (buttonCount > 1) {
52225
+ const buttonMeta = enrichment.componentIntelligence.get("button");
52226
+ if (buttonMeta?.intelligence?.attentionEconomics?.toLowerCase().includes("maximum 1 per section")) {
52227
+ attentionNotes.push(`${buttonCount} buttons present -- ensure maximum 1 primary per section`);
52228
+ }
52229
+ }
52230
+ if (primaryElements.length > 0) {
52231
+ for (const name2 of primaryElements) {
52232
+ attentionNotes.push(`Multiple ${name2} instances -- check attention hierarchy`);
52233
+ }
52234
+ }
52235
+ const trustConsiderations = [];
52236
+ for (const [name2] of componentCounts) {
52237
+ const metadata = enrichment.componentIntelligence.get(name2);
52238
+ if (!metadata?.intelligence?.trustBuilding) continue;
52239
+ const trust = metadata.intelligence.trustBuilding;
52240
+ if (trust.toLowerCase().includes("confirmation") || trust.toLowerCase().includes("destructive") || trust.toLowerCase().includes("consequence") || trust.toLowerCase().includes("cancel")) {
52241
+ trustConsiderations.push(`${name2}: ${trust}`);
52242
+ }
52243
+ }
52244
+ const patternMatches = [];
52245
+ const compositionSet = new Set(componentCounts.keys());
52246
+ for (const [patternKey, pattern] of Object.entries(enrichment.patterns)) {
52247
+ const matched = pattern.components.filter((c) => compositionSet.has(c));
52248
+ if (matched.length >= 2 || matched.length === pattern.components.length) {
52249
+ patternMatches.push({
52250
+ name: pattern.name,
52251
+ matched,
52252
+ suggestion: `Call rafters_pattern('${patternKey}') for full guidance`
52253
+ });
52254
+ }
52255
+ }
52256
+ const designerNotes = [];
52257
+ const compositionNames = [...compositionSet];
52258
+ for (const { token } of enrichment.tokenOverrides) {
52259
+ if (!token.userOverride) continue;
52260
+ const relevantTo = [];
52261
+ if (token.applicableComponents) {
52262
+ for (const comp of token.applicableComponents) {
52263
+ if (compositionSet.has(comp)) {
52264
+ relevantTo.push(comp);
52265
+ }
52266
+ }
52267
+ }
52268
+ if (relevantTo.length === 0) {
52269
+ if (token.namespace === "spacing") {
52270
+ const formComponents = compositionNames.filter(
52271
+ (n) => [
52272
+ "input",
52273
+ "textarea",
52274
+ "field",
52275
+ "label",
52276
+ "button",
52277
+ "select",
52278
+ "checkbox",
52279
+ "radio-group",
52280
+ "switch",
52281
+ "slider",
52282
+ "combobox"
52283
+ ].includes(n)
52284
+ );
52285
+ if (formComponents.length > 0) {
52286
+ relevantTo.push(...formComponents);
52287
+ }
52288
+ } else if (token.namespace === "color") {
52289
+ const variantComponents = compositionNames.filter(
52290
+ (n) => ["button", "badge", "alert", "alert-dialog", "progress"].includes(n)
52291
+ );
52292
+ if (variantComponents.length > 0 && (token.name.includes("destructive") || token.name.includes("primary"))) {
52293
+ relevantTo.push(...variantComponents);
52294
+ }
52295
+ }
52296
+ }
52297
+ if (relevantTo.length > 0) {
52298
+ designerNotes.push({
52299
+ token: token.name,
52300
+ reason: token.userOverride.reason,
52301
+ relevantTo
52302
+ });
52303
+ }
52304
+ }
52305
+ const violations = [];
52306
+ for (const [name2] of componentCounts) {
52307
+ const metadata = enrichment.componentIntelligence.get(name2);
52308
+ if (!metadata?.intelligence?.usagePatterns?.nevers) continue;
52309
+ for (const never2 of metadata.intelligence.usagePatterns.nevers) {
52310
+ const neverLower = never2.toLowerCase();
52311
+ if (neverLower.includes("nest") && neverLower.includes("card") && name2 === "card") {
52312
+ const cardCount = componentCounts.get("card") ?? 0;
52313
+ if (cardCount > 1) {
52314
+ violations.push(
52315
+ `${name2}: "${never2}" -- ${cardCount} cards in composition, verify none are nested`
52316
+ );
52317
+ }
52318
+ }
52319
+ if (neverLower.includes("multiple primary") && name2 === "button") {
52320
+ if (buttonCount > 1) {
52321
+ violations.push(`${name2}: "${never2}"`);
52322
+ }
52323
+ }
52324
+ if (neverLower.includes("stack") && neverLower.includes("dialog")) {
52325
+ if (compositionSet.has("dialog") && (compositionSet.has("alert-dialog") || (componentCounts.get("dialog") ?? 0) > 1)) {
52326
+ violations.push(`${name2}: "${never2}"`);
52327
+ }
52328
+ }
52329
+ }
52330
+ }
52331
+ const hotspots = [];
52332
+ for (const comp of componentResults) {
52333
+ if (comp.score >= 4) {
52334
+ const highest = highestDimension(comp.dimensions);
52335
+ hotspots.push({
52336
+ name: comp.name,
52337
+ score: comp.score,
52338
+ highestDimension: DIMENSION_NAMES[highest],
52339
+ suggestion: HOTSPOT_SUGGESTIONS[highest]
52340
+ });
52341
+ }
52342
+ }
52343
+ if (unknownComponents.length > 0) {
52344
+ attentionNotes.push(
52345
+ `Unknown components not scored: ${unknownComponents.join(", ")}. Totals may be understated.`
52346
+ );
52347
+ }
52348
+ const warnings = [];
52349
+ const knownNames = [...componentCounts.keys()].filter((n) => COMPONENT_SCORES[n]);
52350
+ const uninstrumented = knownNames.filter((n) => !enrichment.componentIntelligence.has(n));
52351
+ if (uninstrumented.length > 0) {
52352
+ warnings.push(
52353
+ `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.`
52354
+ );
52355
+ }
52356
+ return {
52357
+ budget: budgetResult,
52358
+ components: componentResults,
52359
+ attention: {
52360
+ conflicts: attentionConflicts,
52361
+ notes: attentionNotes
52362
+ },
52363
+ trust: trustConsiderations,
52364
+ patterns: patternMatches,
52365
+ designerNotes,
52366
+ hotspots,
52367
+ violations,
52368
+ warnings
52369
+ };
52370
+ }
52371
+
52372
+ // src/mcp/tools.ts
52373
+ function hasAnyDeps(deps) {
52374
+ return deps.runtime.length > 0 || deps.dev.length > 0 || deps.internal.length > 0;
52375
+ }
52376
+ var SYSTEM_PREAMBLE = `RAFTERS IS NOT SHADCN.
52377
+ 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.
52378
+
52379
+ CLASSY IS THE LAW.
52380
+ 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.
52381
+
52382
+ LAYOUT IS SOLVED. Stop writing CSS.
52383
+ 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.
52384
+
52385
+ USE PRESETS. Do not compose layout from props.
52386
+ Grid presets handle common layouts. Pick the one that matches your intent:
52387
+ - sidebar-main -- navigation + content
52388
+ - form -- label/input pairs
52389
+ - cards -- responsive card grid
52390
+ - row -- horizontal group of elements
52391
+ - stack -- vertical sequence
52392
+ - split -- equal columns
52393
+ If no preset fits, describe what you need -- do not improvise with raw props.
52394
+
52395
+ CONTAINER OWNS SPACING.
52396
+ 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.
52397
+
52398
+ COMPONENTS ARE COMPLETE.
52399
+ 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.
52400
+
52401
+ UTILITIES EXIST FOR EDGE CASES.
52402
+ 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.
52403
+
52404
+ When in doubt: less code, not more. Rafters has already made the design decision.`;
50996
52405
  var DESIGN_PATTERNS = {
50997
52406
  "destructive-action": {
50998
52407
  name: "Destructive Action",
@@ -51340,6 +52749,26 @@ var TOOL_DEFINITIONS = [
51340
52749
  },
51341
52750
  required: ["name"]
51342
52751
  }
52752
+ },
52753
+ {
52754
+ name: "rafters_cognitive_budget",
52755
+ 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.",
52756
+ inputSchema: {
52757
+ type: "object",
52758
+ properties: {
52759
+ components: {
52760
+ type: "array",
52761
+ items: { type: "string" },
52762
+ description: 'Components visible simultaneously, duplicates allowed (e.g., ["input", "input", "button", "card"])'
52763
+ },
52764
+ tier: {
52765
+ type: "string",
52766
+ enum: ["focused", "page", "app"],
52767
+ description: "Budget tier: focused (15, dialogs/modals), page (30, standard views), app (45, multi-panel). Default: page."
52768
+ }
52769
+ },
52770
+ required: ["components"]
52771
+ }
51343
52772
  }
51344
52773
  ];
51345
52774
  var RaftersToolHandler = class {
@@ -51369,6 +52798,11 @@ var RaftersToolHandler = class {
51369
52798
  return this.getComponent(args.name);
51370
52799
  case "rafters_token":
51371
52800
  return this.getToken(args.name);
52801
+ case "rafters_cognitive_budget":
52802
+ return this.getCognitiveBudget(
52803
+ args.components,
52804
+ args.tier ?? "page"
52805
+ );
51372
52806
  default:
51373
52807
  return {
51374
52808
  content: [{ type: "text", text: `Unknown tool: ${name2}` }],
@@ -51390,12 +52824,12 @@ var RaftersToolHandler = class {
51390
52824
  this.getComponentVocabulary()
51391
52825
  ]);
51392
52826
  const vocabulary = {
52827
+ system: SYSTEM_PREAMBLE,
52828
+ components,
51393
52829
  colors,
51394
52830
  spacing,
51395
52831
  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"
52832
+ patterns: Object.keys(DESIGN_PATTERNS)
51399
52833
  };
51400
52834
  return {
51401
52835
  content: [
@@ -51482,33 +52916,26 @@ var RaftersToolHandler = class {
51482
52916
  * Extract compact component vocabulary
51483
52917
  */
51484
52918
  async getComponentVocabulary() {
52919
+ let installed = [];
51485
52920
  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
- }
52921
+ const paths = getRaftersPaths(this.projectRoot);
52922
+ if (existsSync4(paths.config)) {
52923
+ const content = await readFile6(paths.config, "utf-8");
52924
+ const config3 = JSON.parse(content);
52925
+ installed = config3.installed?.components ?? [];
51503
52926
  }
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
52927
  } catch {
51510
- return [];
51511
52928
  }
52929
+ let available = [];
52930
+ try {
52931
+ const client = new RegistryClient();
52932
+ const index = await client.fetchIndex();
52933
+ const allComponents = index.components;
52934
+ const installedSet = new Set(installed);
52935
+ available = allComponents.filter((name2) => !installedSet.has(name2));
52936
+ } catch {
52937
+ }
52938
+ return { installed, available };
51512
52939
  }
51513
52940
  // ==================== Tool 2: Pattern ====================
51514
52941
  /**
@@ -51549,7 +52976,7 @@ var RaftersToolHandler = class {
51549
52976
  * Get path to UI components directory
51550
52977
  */
51551
52978
  getComponentsPath() {
51552
- const monorepoPath = join9(this.projectRoot, "packages/ui/src/components/ui");
52979
+ const monorepoPath = join10(this.projectRoot, "packages/ui/src/components/ui");
51553
52980
  return monorepoPath;
51554
52981
  }
51555
52982
  /**
@@ -51557,11 +52984,16 @@ var RaftersToolHandler = class {
51557
52984
  */
51558
52985
  async loadComponentMetadata(name2) {
51559
52986
  const componentsPath = this.getComponentsPath();
51560
- const filePath = join9(componentsPath, `${name2}.tsx`);
52987
+ const filePath = join10(componentsPath, `${name2}.tsx`);
51561
52988
  try {
51562
- const source = await readFile5(filePath, "utf-8");
52989
+ const source = await readFile6(filePath, "utf-8");
51563
52990
  const intelligence = parseJSDocIntelligence(source);
51564
52991
  const description = parseDescription(source);
52992
+ let jsDocDeps = { runtime: [], dev: [], internal: [] };
52993
+ try {
52994
+ jsDocDeps = extractJSDocDependencies(source);
52995
+ } catch {
52996
+ }
51565
52997
  const metadata = {
51566
52998
  name: name2,
51567
52999
  displayName: toDisplayName(name2),
@@ -51572,6 +53004,9 @@ var RaftersToolHandler = class {
51572
53004
  primitives: extractPrimitiveDependencies(source),
51573
53005
  filePath: `packages/ui/src/components/ui/${name2}.tsx`
51574
53006
  };
53007
+ if (hasAnyDeps(jsDocDeps)) {
53008
+ metadata.jsDocDependencies = jsDocDeps;
53009
+ }
51575
53010
  if (description) {
51576
53011
  metadata.description = description;
51577
53012
  }
@@ -51716,6 +53151,9 @@ var RaftersToolHandler = class {
51716
53151
  if (metadata.dependencies.length > 0) {
51717
53152
  formatted.dependencies = metadata.dependencies;
51718
53153
  }
53154
+ if (metadata.jsDocDependencies && hasAnyDeps(metadata.jsDocDependencies)) {
53155
+ formatted.jsDocDependencies = metadata.jsDocDependencies;
53156
+ }
51719
53157
  return {
51720
53158
  content: [
51721
53159
  {
@@ -51831,6 +53269,120 @@ var RaftersToolHandler = class {
51831
53269
  return this.handleError("getToken", error48);
51832
53270
  }
51833
53271
  }
53272
+ // ==================== Tool 5: Cognitive Budget ====================
53273
+ /**
53274
+ * Evaluate composition-level cognitive load
53275
+ * Synthesizes scoring data with component intelligence, token overrides,
53276
+ * and design patterns to produce a holistic composition review
53277
+ */
53278
+ async getCognitiveBudget(components, tier) {
53279
+ try {
53280
+ if (!components || components.length === 0) {
53281
+ return {
53282
+ content: [
53283
+ {
53284
+ type: "text",
53285
+ text: JSON.stringify(
53286
+ {
53287
+ error: "No components provided",
53288
+ suggestion: 'Pass an array of component names visible simultaneously, e.g. ["input", "button", "card"]'
53289
+ },
53290
+ null,
53291
+ 2
53292
+ )
53293
+ }
53294
+ ],
53295
+ isError: true
53296
+ };
53297
+ }
53298
+ if (!BUDGET_TIERS[tier]) {
53299
+ return {
53300
+ content: [
53301
+ {
53302
+ type: "text",
53303
+ text: JSON.stringify(
53304
+ {
53305
+ error: `Invalid tier "${tier}"`,
53306
+ available: Object.keys(BUDGET_TIERS),
53307
+ suggestion: 'Use "focused", "page", or "app"'
53308
+ },
53309
+ null,
53310
+ 2
53311
+ )
53312
+ }
53313
+ ],
53314
+ isError: true
53315
+ };
53316
+ }
53317
+ const uniqueNames = [...new Set(components)];
53318
+ const unknownNames = uniqueNames.filter((n) => !COMPONENT_SCORES[n]);
53319
+ if (unknownNames.length === uniqueNames.length) {
53320
+ return {
53321
+ content: [
53322
+ {
53323
+ type: "text",
53324
+ text: JSON.stringify(
53325
+ {
53326
+ error: `No recognized components in composition: ${unknownNames.join(", ")}`,
53327
+ suggestion: "Use rafters_vocabulary to see available component names"
53328
+ },
53329
+ null,
53330
+ 2
53331
+ )
53332
+ }
53333
+ ],
53334
+ isError: true
53335
+ };
53336
+ }
53337
+ const intelligenceMap = /* @__PURE__ */ new Map();
53338
+ const metadataResults = await Promise.all(
53339
+ uniqueNames.map(async (name2) => {
53340
+ const metadata = await this.loadComponentMetadata(name2);
53341
+ return { name: name2, metadata };
53342
+ })
53343
+ );
53344
+ for (const { name: name2, metadata } of metadataResults) {
53345
+ if (metadata) {
53346
+ intelligenceMap.set(name2, metadata);
53347
+ }
53348
+ }
53349
+ const tokenOverrides = [];
53350
+ const namespaces = ["color", "spacing", "typography"];
53351
+ for (const ns of namespaces) {
53352
+ try {
53353
+ const tokens = await this.loadNamespace(ns);
53354
+ for (const token of tokens) {
53355
+ if (token.userOverride) {
53356
+ tokenOverrides.push({ token, namespace: ns });
53357
+ }
53358
+ }
53359
+ } catch {
53360
+ }
53361
+ }
53362
+ const patternRefs = {};
53363
+ for (const [key, pattern] of Object.entries(DESIGN_PATTERNS)) {
53364
+ patternRefs[key] = {
53365
+ name: pattern.name,
53366
+ components: pattern.components
53367
+ };
53368
+ }
53369
+ const review = evaluateComposition(components, tier, {
53370
+ componentIntelligence: intelligenceMap,
53371
+ tokenOverrides,
53372
+ patterns: patternRefs
53373
+ });
53374
+ return {
53375
+ content: [
53376
+ {
53377
+ type: "text",
53378
+ text: JSON.stringify(review, null, 2)
53379
+ }
53380
+ ]
53381
+ };
53382
+ } catch (error48) {
53383
+ return this.handleError("getCognitiveBudget", error48);
53384
+ }
53385
+ }
51834
53386
  /**
51835
53387
  * Handle errors consistently
51836
53388
  */
@@ -51905,22 +53457,22 @@ async function mcp() {
51905
53457
  }
51906
53458
 
51907
53459
  // src/commands/studio.ts
51908
- import { existsSync as existsSync3 } from "fs";
51909
- import { dirname as dirname3, join as join10 } from "path";
53460
+ import { existsSync as existsSync5 } from "fs";
53461
+ import { dirname as dirname3, join as join11 } from "path";
51910
53462
  import { fileURLToPath as fileURLToPath3 } from "url";
51911
53463
  import { execa as execa2 } from "execa";
51912
53464
  var __dirname2 = dirname3(fileURLToPath3(import.meta.url));
51913
53465
  async function studio() {
51914
53466
  const cwd = process.cwd();
51915
53467
  const paths = getRaftersPaths(cwd);
51916
- if (!existsSync3(paths.root)) {
53468
+ if (!existsSync5(paths.root)) {
51917
53469
  console.error('No .rafters/ directory found. Run "rafters init" first.');
51918
53470
  process.exit(1);
51919
53471
  }
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)) {
53472
+ const devStudioPath = join11(__dirname2, "..", "..", "..", "studio");
53473
+ const prodStudioPath = join11(__dirname2, "..", "node_modules", "@rafters", "studio");
53474
+ const studioPath = existsSync5(devStudioPath) ? devStudioPath : prodStudioPath;
53475
+ if (!existsSync5(studioPath)) {
51924
53476
  console.error("Studio package not found. Please reinstall @rafters/cli.");
51925
53477
  process.exit(1);
51926
53478
  }
@@ -51950,7 +53502,7 @@ async function studio() {
51950
53502
  var program = new Command();
51951
53503
  program.name("rafters").description("Design system CLI - scaffold tokens and serve MCP").version("0.0.1");
51952
53504
  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));
53505
+ 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
53506
  program.command("mcp").description("Start MCP server for AI agent access (stdio)").action(mcp);
51955
53507
  program.command("studio").description("Open Studio UI for visual token editing").action(studio);
51956
53508
  program.parse();