rafters 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -82,7 +82,7 @@ import { Command } from "commander";
82
82
  // src/commands/add.ts
83
83
  import { existsSync as existsSync3 } from "fs";
84
84
  import { access, mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
85
- import { basename, dirname, join as join6 } from "path";
85
+ import { basename, dirname, join as join7 } from "path";
86
86
 
87
87
  // ../../node_modules/.pnpm/apca-w3@0.1.9/node_modules/apca-w3/src/apca-w3.js
88
88
  var SA98G = {
@@ -21054,8 +21054,8 @@ async function registryToCompiled(registry2, options = {}) {
21054
21054
  ${sourceDirectives}
21055
21055
  ${themeBody}`;
21056
21056
  const { execFileSync } = await import("child_process");
21057
- const { mkdtempSync, writeFileSync: writeFileSync2, readFileSync: readFileSync4, rmSync } = await import("fs");
21058
- const { join: join15, dirname: dirname4 } = await import("path");
21057
+ const { mkdtempSync, writeFileSync: writeFileSync2, readFileSync: readFileSync5, rmSync } = await import("fs");
21058
+ const { join: join16, dirname: dirname4 } = await import("path");
21059
21059
  const { createRequire: createRequire2 } = await import("module");
21060
21060
  const require2 = createRequire2(import.meta.url);
21061
21061
  let pkgDir;
@@ -21065,10 +21065,10 @@ ${themeBody}`;
21065
21065
  } catch {
21066
21066
  throw new Error("Failed to resolve @tailwindcss/cli");
21067
21067
  }
21068
- const binPath = join15(pkgDir, "dist", "index.mjs");
21069
- const tempDir = mkdtempSync(join15(pkgDir, ".tmp-compile-"));
21070
- const tempInput = join15(tempDir, "input.css");
21071
- const tempOutput = join15(tempDir, "output.css");
21068
+ const binPath = join16(pkgDir, "dist", "index.mjs");
21069
+ const tempDir = mkdtempSync(join16(pkgDir, ".tmp-compile-"));
21070
+ const tempInput = join16(tempDir, "input.css");
21071
+ const tempOutput = join16(tempDir, "output.css");
21072
21072
  try {
21073
21073
  writeFileSync2(tempInput, input);
21074
21074
  const args = [binPath, "-i", tempInput, "-o", tempOutput];
@@ -21076,7 +21076,7 @@ ${themeBody}`;
21076
21076
  args.push("--minify");
21077
21077
  }
21078
21078
  execFileSync("node", args, { stdio: "pipe", timeout: 3e4, cwd: pkgDir });
21079
- return readFileSync4(tempOutput, "utf-8");
21079
+ return readFileSync5(tempOutput, "utf-8");
21080
21080
  } catch (error47) {
21081
21081
  const message = error47 instanceof Error ? error47.message : String(error47);
21082
21082
  throw new Error(`Failed to compile CSS: ${message}`);
@@ -21252,7 +21252,7 @@ async function registryToDocumentation(registry2, options = {}) {
21252
21252
  const themeBody = registryToTailwind(registry2, { includeImport: false });
21253
21253
  const candidates = deriveCandidates(themeBody);
21254
21254
  const { mkdtempSync, writeFileSync: writeFileSync2, rmSync } = await import("fs");
21255
- const { join: join15, dirname: dirname4 } = await import("path");
21255
+ const { join: join16, dirname: dirname4 } = await import("path");
21256
21256
  const { createRequire: createRequire2 } = await import("module");
21257
21257
  const require2 = createRequire2(import.meta.url);
21258
21258
  let pkgDir;
@@ -21264,8 +21264,8 @@ async function registryToDocumentation(registry2, options = {}) {
21264
21264
  "Failed to resolve @tailwindcss/cli -- install it to generate documentation CSS"
21265
21265
  );
21266
21266
  }
21267
- const tempDir = mkdtempSync(join15(pkgDir, ".tmp-doc-compile-"));
21268
- const candidateFile = join15(tempDir, "candidates.txt");
21267
+ const tempDir = mkdtempSync(join16(pkgDir, ".tmp-doc-compile-"));
21268
+ const candidateFile = join16(tempDir, "candidates.txt");
21269
21269
  writeFileSync2(candidateFile, candidates.join("\n"));
21270
21270
  const sourceDirectives = [
21271
21271
  `@source "${candidateFile}";`,
@@ -21274,17 +21274,17 @@ async function registryToDocumentation(registry2, options = {}) {
21274
21274
  const input = `@import "tailwindcss" source(none);
21275
21275
  ${sourceDirectives}
21276
21276
  ${themeBody}`;
21277
- const tempInput = join15(tempDir, "input.css");
21278
- const tempOutput = join15(tempDir, "output.css");
21277
+ const tempInput = join16(tempDir, "input.css");
21278
+ const tempOutput = join16(tempDir, "output.css");
21279
21279
  try {
21280
21280
  writeFileSync2(tempInput, input);
21281
21281
  const { execFileSync } = await import("child_process");
21282
- const binPath = join15(pkgDir, "dist", "index.mjs");
21282
+ const binPath = join16(pkgDir, "dist", "index.mjs");
21283
21283
  const args = [binPath, "-i", tempInput, "-o", tempOutput];
21284
21284
  if (minify) args.push("--minify");
21285
21285
  execFileSync("node", args, { stdio: "pipe", timeout: 6e4, cwd: pkgDir });
21286
- const { readFileSync: readFileSync4 } = await import("fs");
21287
- const raw = readFileSync4(tempOutput, "utf-8");
21286
+ const { readFileSync: readFileSync5 } = await import("fs");
21287
+ const raw = readFileSync5(tempOutput, "utf-8");
21288
21288
  return postProcessDocSheet(raw);
21289
21289
  } catch (error47) {
21290
21290
  const message = error47 instanceof Error ? error47.message : String(error47);
@@ -27105,6 +27105,32 @@ function generateBaseSystem(config2 = {}) {
27105
27105
  }
27106
27106
  };
27107
27107
  }
27108
+ function generateNamespaces(namespaces, config2 = {}) {
27109
+ const mergedConfig = {
27110
+ ...DEFAULT_SYSTEM_CONFIG,
27111
+ ...config2
27112
+ };
27113
+ const resolvedConfig = resolveConfig(mergedConfig);
27114
+ const byNamespace = /* @__PURE__ */ new Map();
27115
+ const allTokens = [];
27116
+ const generators = createGeneratorDefs(mergedConfig.colorPaletteBases);
27117
+ const requestedGenerators = generators.filter((g2) => namespaces.includes(g2.name));
27118
+ for (const { generate: generate4 } of requestedGenerators) {
27119
+ const result = generate4(resolvedConfig);
27120
+ byNamespace.set(result.namespace, result.tokens);
27121
+ allTokens.push(...result.tokens);
27122
+ }
27123
+ return {
27124
+ byNamespace,
27125
+ allTokens,
27126
+ metadata: {
27127
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
27128
+ config: resolvedConfig,
27129
+ tokenCount: allTokens.length,
27130
+ namespaces: Array.from(byNamespace.keys())
27131
+ }
27132
+ };
27133
+ }
27108
27134
 
27109
27135
  // ../design-tokens/src/importers/adapter.ts
27110
27136
  var adapters = /* @__PURE__ */ new Map();
@@ -28701,6 +28727,21 @@ var PropFieldSchema = external_exports.discriminatedUnion("type", [
28701
28727
  requires: external_exports.object({ prop: external_exports.string() })
28702
28728
  }).optional()
28703
28729
  }),
28730
+ external_exports.object({
28731
+ type: external_exports.literal("boolean"),
28732
+ default: external_exports.boolean().optional(),
28733
+ required: external_exports.boolean().optional()
28734
+ }),
28735
+ external_exports.object({
28736
+ type: external_exports.literal("string"),
28737
+ default: external_exports.string().optional(),
28738
+ required: external_exports.boolean().optional()
28739
+ }),
28740
+ external_exports.object({
28741
+ type: external_exports.literal("number"),
28742
+ default: external_exports.number().optional(),
28743
+ required: external_exports.boolean().optional()
28744
+ }),
28704
28745
  external_exports.object({
28705
28746
  // Matches #2072's PropNode 'grammar' arm exactly, so graph.ts's
28706
28747
  // describe(<id>.props.<name>.vocab) drill has real shape data.
@@ -29493,6 +29534,11 @@ function log(event) {
29493
29534
  console.log(" Skipped import. You can run `rafters import` later.");
29494
29535
  console.log("");
29495
29536
  break;
29537
+ // Motion rebuild events
29538
+ case "motion:override-dropped":
29539
+ context.spinner?.stop();
29540
+ console.warn(` Warning: ${event.message}`);
29541
+ break;
29496
29542
  default:
29497
29543
  if (context.spinner) {
29498
29544
  context.spinner.text = eventType;
@@ -29706,17 +29752,90 @@ async function installRegistryDependencies(items, targetDir, options = {}) {
29706
29752
  return result;
29707
29753
  }
29708
29754
 
29755
+ // src/utils/motion-rebuild.ts
29756
+ import { readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
29757
+ import { join as join5 } from "path";
29758
+ function hasStoredTokens(tokensDir) {
29759
+ try {
29760
+ return readdirSync2(tokensDir).some((entry) => entry.endsWith(".rafters.json"));
29761
+ } catch {
29762
+ return false;
29763
+ }
29764
+ }
29765
+ var MOTION_CELL_PREFIX = "motion-cell-";
29766
+ var MotionCellSpecSchema = external_exports.object({
29767
+ keyframe: external_exports.string(),
29768
+ duration: external_exports.discriminatedUnion("kind", [
29769
+ external_exports.object({ kind: external_exports.literal("tier"), tier: external_exports.string() }),
29770
+ external_exports.object({ kind: external_exports.literal("period"), period: external_exports.string() })
29771
+ ]),
29772
+ curve: external_exports.string().optional()
29773
+ });
29774
+ function isCarryableMotionValue(name, value) {
29775
+ if (!name.startsWith(MOTION_CELL_PREFIX)) return true;
29776
+ if (typeof value !== "string") return false;
29777
+ let parsed;
29778
+ try {
29779
+ parsed = JSON.parse(value);
29780
+ } catch {
29781
+ return true;
29782
+ }
29783
+ if (typeof parsed !== "object" || parsed === null) return true;
29784
+ return MotionCellSpecSchema.safeParse(parsed).success;
29785
+ }
29786
+ var StoredMotionTokenSchema = external_exports.object({
29787
+ name: external_exports.string(),
29788
+ value: TokenSchema.shape.value,
29789
+ userOverride: TokenSchema.shape.userOverride
29790
+ });
29791
+ function readStoredMotionOverrides(motionFile) {
29792
+ const carried = /* @__PURE__ */ new Map();
29793
+ let raw;
29794
+ try {
29795
+ raw = JSON.parse(readFileSync3(motionFile, "utf8"));
29796
+ } catch {
29797
+ return carried;
29798
+ }
29799
+ const file2 = external_exports.object({ tokens: external_exports.array(external_exports.unknown()) }).safeParse(raw);
29800
+ if (!file2.success) return carried;
29801
+ for (const entry of file2.data.tokens) {
29802
+ const parsed = StoredMotionTokenSchema.safeParse(entry);
29803
+ if (!parsed.success) continue;
29804
+ const { name, value, userOverride } = parsed.data;
29805
+ if (!userOverride) continue;
29806
+ carried.set(name, { value, userOverride });
29807
+ }
29808
+ return carried;
29809
+ }
29810
+ function regenerateMotionNamespace(tokensDir, plugins) {
29811
+ const carried = readStoredMotionOverrides(join5(tokensDir, "motion.rafters.json"));
29812
+ const tokens = generateNamespaces(["motion"]).allTokens.map((token) => {
29813
+ const override = carried.get(token.name);
29814
+ if (!override) return token;
29815
+ if (!isCarryableMotionValue(token.name, override.value)) {
29816
+ log({
29817
+ event: "motion:override-dropped",
29818
+ token: token.name,
29819
+ message: `Stored override on "${token.name}" holds a value shape this version no longer emits; the token is rebuilt from the generator and the override is dropped.`
29820
+ });
29821
+ return token;
29822
+ }
29823
+ return { ...token, ...override };
29824
+ });
29825
+ saveRegistryToDir(tokensDir, new TokenRegistry(tokens, plugins));
29826
+ }
29827
+
29709
29828
  // src/utils/paths.ts
29710
29829
  import { realpathSync as realpathSync2 } from "fs";
29711
- import { isAbsolute as isAbsolute2, join as join5, relative, resolve as resolve2 } from "path";
29830
+ import { isAbsolute as isAbsolute2, join as join6, relative, resolve as resolve2 } from "path";
29712
29831
  function getRaftersPaths(projectRoot = process.cwd()) {
29713
- const root = join5(projectRoot, ".rafters");
29832
+ const root = join6(projectRoot, ".rafters");
29714
29833
  return {
29715
29834
  root,
29716
- config: join5(root, "config.rafters.json"),
29717
- tokens: join5(root, "tokens"),
29718
- output: join5(root, "output"),
29719
- importPending: join5(root, "import-pending.json")
29835
+ config: join6(root, "config.rafters.json"),
29836
+ tokens: join6(root, "tokens"),
29837
+ output: join6(root, "output"),
29838
+ importPending: join6(root, "import-pending.json")
29720
29839
  };
29721
29840
  }
29722
29841
  var PathEntrySchema = external_exports.union([
@@ -29778,7 +29897,7 @@ function resolveReadSet(field, cwd, fallback) {
29778
29897
  }
29779
29898
 
29780
29899
  // src/utils/reconcile.ts
29781
- import { readdirSync as readdirSync2 } from "fs";
29900
+ import { readdirSync as readdirSync3 } from "fs";
29782
29901
  var DISCOVERABLE_KINDS = ["components", "primitives", "composites"];
29783
29902
  var KIND_PATHS = {
29784
29903
  components: { field: "componentsPath", fallback: "components/ui" },
@@ -29810,7 +29929,7 @@ function readInstallRoots(cwd, config2) {
29810
29929
  const names = /* @__PURE__ */ new Set();
29811
29930
  for (const dir of resolveReadSet(pathField, cwd, fallback)) {
29812
29931
  try {
29813
- for (const entry of readdirSync2(dir)) names.add(entry);
29932
+ for (const entry of readdirSync3(dir)) names.add(entry);
29814
29933
  } catch {
29815
29934
  }
29816
29935
  }
@@ -29837,14 +29956,11 @@ function migrateConfig(raw) {
29837
29956
  var REGISTRY_PLUGINS = [scalePlugin, contrastPlugin, statePlugin, invertPlugin];
29838
29957
  async function regenerateAfterInstall(cwd, config2) {
29839
29958
  const paths = getRaftersPaths(cwd);
29840
- let registry2;
29841
- try {
29842
- registry2 = loadRegistryFromDir(paths.tokens, REGISTRY_PLUGINS);
29843
- } catch {
29844
- return;
29845
- }
29846
- if (registry2.size() === 0) return;
29959
+ if (!hasStoredTokens(paths.tokens)) return;
29847
29960
  try {
29961
+ regenerateMotionNamespace(paths.tokens, REGISTRY_PLUGINS);
29962
+ const registry2 = loadRegistryFromDir(paths.tokens, REGISTRY_PLUGINS);
29963
+ if (registry2.size() === 0) return;
29848
29964
  await regenerateOutputs(registry2, {
29849
29965
  outputDir: paths.output,
29850
29966
  exports: config2.exports,
@@ -29922,10 +30038,16 @@ function getComponentTarget(config2) {
29922
30038
  return resolveComponentTarget(config2);
29923
30039
  }
29924
30040
  var SHARED_EXTENSIONS = /* @__PURE__ */ new Set([".behavior.ts", ".classes.ts"]);
30041
+ var FRAMEWORK_EXTENSIONS = [".tsx", ".astro", ".vue", ".svelte", ".element.ts"];
29925
30042
  function isSharedFile(path) {
30043
+ if (FRAMEWORK_EXTENSIONS.some((ext) => path.endsWith(ext))) return false;
29926
30044
  for (const ext of SHARED_EXTENSIONS) {
29927
30045
  if (path.endsWith(ext)) return true;
29928
30046
  }
30047
+ if (path.startsWith("components/") && path.endsWith(".ts")) {
30048
+ const afterComponents = path.slice("components/ui/".length);
30049
+ if (afterComponents.includes("/")) return true;
30050
+ }
29929
30051
  return false;
29930
30052
  }
29931
30053
  var REACT_FALLBACK_TARGETS = /* @__PURE__ */ new Set(["astro", "vue", "svelte"]);
@@ -30026,10 +30148,10 @@ function transformPath(registryPath, config2, cwd = process.cwd()) {
30026
30148
  function substrateProjectPath(registryPath, config2, cwd = process.cwd()) {
30027
30149
  const componentsResolved = rootFor(config2?.componentsPath, cwd, "components/ui");
30028
30150
  const sourceRoot = componentsResolved.replace(/\/?components\/ui$/, "");
30029
- return sourceRoot ? join6(sourceRoot, registryPath) : registryPath;
30151
+ return sourceRoot ? join7(sourceRoot, registryPath) : registryPath;
30030
30152
  }
30031
30153
  function fileExists(cwd, relativePath) {
30032
- return existsSync3(join6(cwd, relativePath));
30154
+ return existsSync3(join7(cwd, relativePath));
30033
30155
  }
30034
30156
  function transformFileContent(content, config2, fileType = "component", cwd = process.cwd(), opts = {}) {
30035
30157
  const { substrateKinds = [], installPath } = opts;
@@ -30041,28 +30163,64 @@ function transformFileContent(content, config2, fileType = "component", cwd = pr
30041
30163
  const aliasPrimitives = stripSourceRoot(primitivesPath);
30042
30164
  const toFlatPrimitive = (_match, subpath) => `from '@/${aliasPrimitives}/${basename(subpath)}'`;
30043
30165
  transformed = transformed.replace(
30044
- /from\s+['"]\.\.\/\.\.\/primitives\/([^'"]+)['"]/g,
30166
+ /from\s+['"](?:\.\.\/)+primitives\/([^'"]+)['"]/g,
30045
30167
  toFlatPrimitive
30046
30168
  );
30047
- transformed = transformed.replace(/from\s+['"]\.\.\/primitives\/([^'"]+)['"]/g, toFlatPrimitive);
30048
- const substrateOwnDir = fileType === "substrate" && installPath ? stripSourceRoot(dirname(installPath)) : null;
30049
- const aliasSibling = fileType === "primitive" ? aliasPrimitives : substrateOwnDir ?? aliasComponents;
30050
- transformed = transformed.replace(/from\s+['"]\.\/([^'"]+)['"]/g, `from '@/${aliasSibling}/$1'`);
30169
+ const isSubsystemFile = fileType === "component" && installPath?.startsWith("components/") && installPath.slice("components/ui/".length).includes("/");
30170
+ const SHARED_SUFFIX_BASES = [".behavior", ".classes", ".types", ".constants", ".styles"];
30171
+ function extractComponentName(path) {
30172
+ if (!path.startsWith("components/")) return null;
30173
+ let base = basename(path).replace(/\.(tsx?|jsx?|astro|vue|svelte)$/, "");
30174
+ for (const suffix of SHARED_SUFFIX_BASES) {
30175
+ if (base.endsWith(suffix)) {
30176
+ base = base.slice(0, -suffix.length);
30177
+ break;
30178
+ }
30179
+ }
30180
+ return base || null;
30181
+ }
30182
+ if (isSubsystemFile) {
30183
+ } else if (fileType === "component" && installPath) {
30184
+ const componentName = extractComponentName(installPath);
30185
+ transformed = transformed.replace(
30186
+ /from\s+['"]\.\/([^'"]+)['"]/g,
30187
+ (_match, importPath) => {
30188
+ if (SHARED_SUFFIX_BASES.some((s) => importPath.endsWith(s))) {
30189
+ return `from '@/${aliasComponents}/${importPath}'`;
30190
+ }
30191
+ if (componentName) {
30192
+ return `from '@/${aliasComponents}/${componentName}/${importPath}'`;
30193
+ }
30194
+ return `from '@/${aliasComponents}/${importPath}'`;
30195
+ }
30196
+ );
30197
+ } else {
30198
+ const substrateOwnDir = fileType === "substrate" && installPath ? stripSourceRoot(dirname(installPath)) : null;
30199
+ const aliasSibling = fileType === "primitive" ? aliasPrimitives : substrateOwnDir ?? aliasComponents;
30200
+ transformed = transformed.replace(
30201
+ /from\s+['"]\.\/([^'"]+)['"]/g,
30202
+ `from '@/${aliasSibling}/$1'`
30203
+ );
30204
+ }
30051
30205
  if (substrateKinds.length > 0) {
30052
30206
  const kindAlternation = substrateKinds.join("|");
30053
30207
  transformed = transformed.replace(
30054
- new RegExp(`from\\s+['"](?:\\.\\./){1,2}(${kindAlternation})/([^'"]+)['"]`, "g"),
30208
+ new RegExp(`from\\s+['"](?:\\.\\./)+?(${kindAlternation})/([^'"]+)['"]`, "g"),
30055
30209
  "from '@/$1/$2'"
30056
30210
  );
30057
30211
  }
30058
- transformed = transformed.replace(
30059
- /from\s+['"]\.\.\/([^/'"]+)\/\1([^'"]*)['"]/g,
30060
- `from '@/${aliasComponents}/$1$2'`
30061
- );
30062
- transformed = transformed.replace(
30063
- /from\s+['"]\.\.\/([^'"]+)['"]/g,
30064
- `from '@/${aliasComponents}/$1'`
30065
- );
30212
+ if (!isSubsystemFile) {
30213
+ transformed = transformed.replace(
30214
+ /from\s+['"]\.\.\/([^/'"]+)\/\1([^'"]*)['"]/g,
30215
+ `from '@/${aliasComponents}/$1$2'`
30216
+ );
30217
+ }
30218
+ if (!isSubsystemFile) {
30219
+ transformed = transformed.replace(
30220
+ /from\s+['"]\.\.\/([^'"]+)['"]/g,
30221
+ `from '@/${aliasComponents}/$1'`
30222
+ );
30223
+ }
30066
30224
  return transformed;
30067
30225
  }
30068
30226
  async function installItem(cwd, item, options, config2, substrateKinds = []) {
@@ -30092,7 +30250,7 @@ async function installItem(cwd, item, options, config2, substrateKinds = []) {
30092
30250
  }
30093
30251
  for (const file2 of filesToInstall) {
30094
30252
  const projectPath2 = item.type === "substrate" ? substrateProjectPath(file2.path, config2, cwd) : transformPath(file2.path, config2, cwd);
30095
- const targetPath = join6(cwd, projectPath2);
30253
+ const targetPath = join7(cwd, projectPath2);
30096
30254
  if (fileExists(cwd, projectPath2)) {
30097
30255
  if (!options.overwrite) {
30098
30256
  log({
@@ -30371,11 +30529,11 @@ async function add(componentArgs, options) {
30371
30529
  // src/commands/agents.ts
30372
30530
  import { existsSync as existsSync6 } from "fs";
30373
30531
  import { readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
30374
- import { join as join11 } from "path";
30532
+ import { join as join12 } from "path";
30375
30533
 
30376
30534
  // src/mcp/tools.ts
30377
30535
  import { readdir as readdir2, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
30378
- import { isAbsolute as isAbsolute3, join as join10 } from "path";
30536
+ import { isAbsolute as isAbsolute3, join as join11 } from "path";
30379
30537
 
30380
30538
  // ../composites/src/built-in-rules/email.ts
30381
30539
  var email3 = external_exports.string().email();
@@ -30547,7 +30705,7 @@ var import_escape_html = __toESM(require_escape_html(), 1);
30547
30705
 
30548
30706
  // ../composites/src/discovery-node.ts
30549
30707
  import { readdir, readFile as readFile4 } from "fs/promises";
30550
- import { join as join7 } from "path";
30708
+ import { join as join8 } from "path";
30551
30709
  var COMPOSITE_SUFFIX = ".composite.json";
30552
30710
  async function readDir(directory) {
30553
30711
  const entries = [];
@@ -30558,7 +30716,7 @@ async function readDir(directory) {
30558
30716
  return entries;
30559
30717
  }
30560
30718
  for (const dirent of dirents) {
30561
- const fullPath = join7(directory, dirent.name);
30719
+ const fullPath = join8(directory, dirent.name);
30562
30720
  if (dirent.isDirectory()) {
30563
30721
  entries.push(...await readDir(fullPath));
30564
30722
  continue;
@@ -30585,16 +30743,16 @@ async function discoverFromDirs(...directories) {
30585
30743
  }
30586
30744
 
30587
30745
  // src/utils/workspaces.ts
30588
- import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
30589
- import { basename as basename2, dirname as dirname3, join as join9, resolve as resolve4 } from "path";
30746
+ import { existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
30747
+ import { basename as basename2, dirname as dirname3, join as join10, resolve as resolve4 } from "path";
30590
30748
 
30591
30749
  // src/utils/discover.ts
30592
30750
  import { existsSync as existsSync4 } from "fs";
30593
- import { dirname as dirname2, join as join8, resolve as resolve3 } from "path";
30751
+ import { dirname as dirname2, join as join9, resolve as resolve3 } from "path";
30594
30752
  function discoverProjectRoot(startDir) {
30595
30753
  let current = resolve3(startDir);
30596
30754
  for (; ; ) {
30597
- const configPath2 = join8(current, ".rafters", "config.rafters.json");
30755
+ const configPath2 = join9(current, ".rafters", "config.rafters.json");
30598
30756
  if (existsSync4(configPath2)) {
30599
30757
  return current;
30600
30758
  }
@@ -30611,16 +30769,16 @@ function findMonorepoRoot(startDir, boundary) {
30611
30769
  let current = resolve4(startDir);
30612
30770
  const stopAt = boundary ? resolve4(boundary) : null;
30613
30771
  for (; ; ) {
30614
- const pnpmWorkspace = join9(current, "pnpm-workspace.yaml");
30772
+ const pnpmWorkspace = join10(current, "pnpm-workspace.yaml");
30615
30773
  if (existsSync5(pnpmWorkspace)) {
30616
- const patterns = parsePnpmWorkspaceYaml(readFileSync3(pnpmWorkspace, "utf-8"));
30774
+ const patterns = parsePnpmWorkspaceYaml(readFileSync4(pnpmWorkspace, "utf-8"));
30617
30775
  if (patterns.length > 0) {
30618
30776
  return { root: current, patterns };
30619
30777
  }
30620
30778
  }
30621
- const pkgJson = join9(current, "package.json");
30779
+ const pkgJson = join10(current, "package.json");
30622
30780
  if (existsSync5(pkgJson)) {
30623
- const patterns = parsePackageJsonWorkspaces(readFileSync3(pkgJson, "utf-8"));
30781
+ const patterns = parsePackageJsonWorkspaces(readFileSync4(pkgJson, "utf-8"));
30624
30782
  if (patterns.length > 0) {
30625
30783
  return { root: current, patterns };
30626
30784
  }
@@ -30674,9 +30832,9 @@ function expandPattern(monorepoRoot, pattern) {
30674
30832
  const trimmed = pattern.replace(/\/+$/, "");
30675
30833
  if (trimmed.endsWith("/*")) {
30676
30834
  const parentRel = trimmed.slice(0, -2);
30677
- const parent = join9(monorepoRoot, parentRel);
30835
+ const parent = join10(monorepoRoot, parentRel);
30678
30836
  if (!existsSync5(parent)) return [];
30679
- return readdirSync3(parent).map((entry) => join9(parent, entry)).filter((path) => {
30837
+ return readdirSync4(parent).map((entry) => join10(parent, entry)).filter((path) => {
30680
30838
  try {
30681
30839
  return statSync2(path).isDirectory();
30682
30840
  } catch {
@@ -30684,7 +30842,7 @@ function expandPattern(monorepoRoot, pattern) {
30684
30842
  }
30685
30843
  });
30686
30844
  }
30687
- const literal2 = join9(monorepoRoot, trimmed);
30845
+ const literal2 = join10(monorepoRoot, trimmed);
30688
30846
  if (existsSync5(literal2)) {
30689
30847
  try {
30690
30848
  if (statSync2(literal2).isDirectory()) return [literal2];
@@ -30708,13 +30866,13 @@ function discoverWorkspaces(startDir = process.cwd(), options = {}) {
30708
30866
  for (const dir of expandPattern(layout.root, pattern)) {
30709
30867
  if (seen.has(dir)) continue;
30710
30868
  seen.add(dir);
30711
- const config2 = join9(dir, ".rafters", "config.rafters.json");
30869
+ const config2 = join10(dir, ".rafters", "config.rafters.json");
30712
30870
  if (existsSync5(config2)) {
30713
30871
  workspaces.push({ name: basename2(dir), root: dir });
30714
30872
  }
30715
30873
  }
30716
30874
  }
30717
- const rootConfig = join9(layout.root, ".rafters", "config.rafters.json");
30875
+ const rootConfig = join10(layout.root, ".rafters", "config.rafters.json");
30718
30876
  if (existsSync5(rootConfig) && !seen.has(layout.root)) {
30719
30877
  workspaces.unshift({ name: basename2(layout.root), root: layout.root });
30720
30878
  }
@@ -31231,7 +31389,7 @@ var TOOL_DEFINITIONS = [
31231
31389
  },
31232
31390
  {
31233
31391
  name: "rafters_describe",
31234
- description: `Recursively introspect the component/composite intel graph. describe() returns the installed surface; describe(components)/describe(composites) list the kind roster; describe(button) returns a node -- intel plus type-marked, drillable children; describe(button.props.variant) drills into a prop -- an enum prop returns its literal union members; a grammar prop returns its composition rules, with the token vocabulary deliberately withheld. describe(button.*) expands all props inline in one call (no more drill-per-prop round trips); describe(button.props.color.?) probes safely (null on miss, not an error). A natural-language address (e.g. "what do I use when it needs to be above everything") routes through a separate intent door instead: deterministic keyword matching over a small, curated tag axis, returning a best-match node plus its near-miss counter-example. Below its match threshold it refuses rather than guessing, with a note pointing you at describe(components)/describe(composites) to browse; #2166 (open) is the follow-up that scores it against the intel this tool already owns. A part node carries parent, plus siblings when its parent has other parts; children are typed pointers you feed back in (the prop's own type for props, part for sub-components, edge for composesWith).`,
31392
+ description: `Recursively introspect the component/composite intel graph. describe() returns the installed surface; describe(components)/describe(composites) list the kind roster; describe(button) returns a node -- intel plus type-marked, drillable children; describe(button.props.variant) drills into a prop -- an enum prop returns its literal union members; a boolean, string, or number prop returns its kind with any default and whether it is required, and no value list, because it has no finite one; a grammar prop returns its composition rules, with the token vocabulary deliberately withheld. describe(button.*) expands all props inline in one call (no more drill-per-prop round trips); describe(button.props.color.?) probes safely (null on miss, not an error). A natural-language address (e.g. "what do I use when it needs to be above everything") routes through a separate intent door instead: deterministic keyword matching over a small, curated tag axis, returning a best-match node plus its near-miss counter-example. Below its match threshold it refuses rather than guessing, with a note pointing you at describe(components)/describe(composites) to browse; #2166 (open) is the follow-up that scores it against the intel this tool already owns. A part node carries parent, plus siblings when its parent has other parts; children are typed pointers you feed back in (the prop's own type for props, part for sub-components, edge for composesWith).`,
31235
31393
  inputSchema: {
31236
31394
  type: "object",
31237
31395
  properties: {
@@ -31480,7 +31638,7 @@ var RaftersToolHandler = class {
31480
31638
  async ensureCompositesLoaded(workspace) {
31481
31639
  if (!this.builtInCompositesLoaded) {
31482
31640
  await this.loadCompositesFromDirs(
31483
- join10(process.cwd(), "node_modules/@rafters/composites/src/typography")
31641
+ join11(process.cwd(), "node_modules/@rafters/composites/src/typography")
31484
31642
  );
31485
31643
  this.builtInCompositesLoaded = true;
31486
31644
  }
@@ -31599,7 +31757,7 @@ var RaftersToolHandler = class {
31599
31757
  */
31600
31758
  async scanInstalled(workspaceRoot, paths) {
31601
31759
  const idsUnder = async (field, fallback) => {
31602
- const roots = field ? resolveReadSet(field, workspaceRoot) : [join10(workspaceRoot, fallback)];
31760
+ const roots = field ? resolveReadSet(field, workspaceRoot) : [join11(workspaceRoot, fallback)];
31603
31761
  const ids = /* @__PURE__ */ new Set();
31604
31762
  for (const root of roots) {
31605
31763
  let entries;
@@ -31758,7 +31916,7 @@ var RaftersToolHandler = class {
31758
31916
  const paths = getRaftersPaths(workspaceRoot);
31759
31917
  const config2 = await this.readConfig(workspaceRoot);
31760
31918
  if (!config2?.compositesPath) {
31761
- return [join10(paths.root, "composites")];
31919
+ return [join11(paths.root, "composites")];
31762
31920
  }
31763
31921
  return resolveReadSet(config2.compositesPath, workspaceRoot);
31764
31922
  }
@@ -31973,7 +32131,7 @@ async function agents() {
31973
32131
  `No rafters project found: ${paths.config} is missing. Run \`rafters init\` first.`
31974
32132
  );
31975
32133
  }
31976
- const agentsPath = join11(cwd, "AGENTS.md");
32134
+ const agentsPath = join12(cwd, "AGENTS.md");
31977
32135
  const existing = existsSync6(agentsPath) ? await readFile6(agentsPath, "utf-8") : null;
31978
32136
  const merged = mergeAgentContract(existing, generateAgentContract());
31979
32137
  await writeFile4(agentsPath, merged);
@@ -31983,7 +32141,7 @@ async function agents() {
31983
32141
  import { existsSync as existsSync7 } from "fs";
31984
32142
  import { copyFile, mkdir as mkdir3, readFile as readFile7, rm, writeFile as writeFile5 } from "fs/promises";
31985
32143
  import { createRequire } from "module";
31986
- import { join as join12, relative as relative2 } from "path";
32144
+ import { join as join13, relative as relative2 } from "path";
31987
32145
  import { checkbox, confirm, select } from "@inquirer/prompts";
31988
32146
  var REGISTRY_PLUGINS2 = [scalePlugin, contrastPlugin, statePlugin, invertPlugin];
31989
32147
  async function backupCss(cssPath) {
@@ -32067,7 +32225,7 @@ function cleanSourceCssBlocks(content) {
32067
32225
  }
32068
32226
  async function stripImportedDeclarations(cwd, cssPath, importedNames) {
32069
32227
  if (importedNames.length === 0) return;
32070
- const fullPath = join12(cwd, cssPath);
32228
+ const fullPath = join13(cwd, cssPath);
32071
32229
  const content = await readFile7(fullPath, "utf-8");
32072
32230
  const pattern = new RegExp(importedNames.map((n2) => `^\\s*--${n2}[^;]*;\\s*$`).join("|"), "gm");
32073
32231
  const cleaned = cleanSourceCssBlocks(content.replace(pattern, ""));
@@ -32075,10 +32233,10 @@ async function stripImportedDeclarations(cwd, cssPath, importedNames) {
32075
32233
  await writeFile5(fullPath, collapsed);
32076
32234
  }
32077
32235
  async function updateMainCss(cwd, cssPath, themePath) {
32078
- const fullCssPath = join12(cwd, cssPath);
32236
+ const fullCssPath = join13(cwd, cssPath);
32079
32237
  const cssContent = await readFile7(fullCssPath, "utf-8");
32080
- const cssDir = join12(cwd, cssPath, "..");
32081
- const themeFullPath = join12(cwd, themePath);
32238
+ const cssDir = join13(cwd, cssPath, "..");
32239
+ const themeFullPath = join13(cwd, themePath);
32082
32240
  const relativeThemePath = relative2(cssDir, themeFullPath);
32083
32241
  if (cssContent.includes(".rafters/output/rafters.css")) {
32084
32242
  log({ event: "init:css_already_imported", cssPath });
@@ -32107,7 +32265,7 @@ function isInteractive() {
32107
32265
  }
32108
32266
  function resolveSourceCssPath(cwd, framework, detectedFramework, projectCssPath, shadcnCssPath, emit) {
32109
32267
  if (shadcnCssPath) {
32110
- if (existsSync7(join12(cwd, shadcnCssPath))) return shadcnCssPath;
32268
+ if (existsSync7(join13(cwd, shadcnCssPath))) return shadcnCssPath;
32111
32269
  const fallback = framework === detectedFramework ? projectCssPath : findCssPath(cwd, framework);
32112
32270
  emit({
32113
32271
  event: "init:shadcn_css_missing",
@@ -32242,8 +32400,11 @@ async function regenerateFromExisting(cwd, paths, source, isAgentMode2, framewor
32242
32400
  existingConfig.compositesPath = frameworkPaths.composites;
32243
32401
  existingConfig.rulesPath = frameworkPaths.rules;
32244
32402
  }
32245
- await rm(join12(paths.tokens, "elevation.rafters.json"), { force: true });
32246
- await rm(join12(paths.tokens, "fill.rafters.json"), { force: true });
32403
+ await rm(join13(paths.tokens, "elevation.rafters.json"), { force: true });
32404
+ await rm(join13(paths.tokens, "fill.rafters.json"), { force: true });
32405
+ if (hasStoredTokens(paths.tokens)) {
32406
+ regenerateMotionNamespace(paths.tokens, REGISTRY_PLUGINS2);
32407
+ }
32247
32408
  const registry2 = loadRegistryFromDir(paths.tokens, REGISTRY_PLUGINS2);
32248
32409
  if (registry2.size() === 0) {
32249
32410
  throw new Error("No tokens found. Cannot regenerate without existing tokens.");
@@ -32338,7 +32499,7 @@ async function resetToDefaults(cwd, paths, source, isAgentMode2, framework) {
32338
32499
  };
32339
32500
  await mkdir3(paths.output, { recursive: true });
32340
32501
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
32341
- const backupPath = join12(paths.output, `reset-${timestamp}.json`);
32502
+ const backupPath = join13(paths.output, `reset-${timestamp}.json`);
32342
32503
  await writeFile5(backupPath, JSON.stringify(backup, null, 2));
32343
32504
  log({
32344
32505
  event: "init:reset_backup",
@@ -32476,7 +32637,7 @@ async function init(options) {
32476
32637
  );
32477
32638
  if (sourceCssPathForBase !== null) {
32478
32639
  try {
32479
- const cssForBase = await readFile7(join12(cwd, sourceCssPathForBase), "utf-8");
32640
+ const cssForBase = await readFile7(join13(cwd, sourceCssPathForBase), "utf-8");
32480
32641
  const baseSlots = [
32481
32642
  {
32482
32643
  detect: () => adapter.detectSpacing(cssForBase).base,
@@ -32599,7 +32760,7 @@ async function init(options) {
32599
32760
  if (detectedCssPath) {
32600
32761
  let sourceCss = null;
32601
32762
  try {
32602
- sourceCss = await readFile7(join12(cwd, detectedCssPath), "utf-8");
32763
+ sourceCss = await readFile7(join13(cwd, detectedCssPath), "utf-8");
32603
32764
  } catch (err) {
32604
32765
  if (!(err instanceof Error && "code" in err && err.code === "ENOENT")) throw err;
32605
32766
  }
@@ -32866,7 +33027,7 @@ async function init(options) {
32866
33027
 
32867
33028
  // src/commands/mcp.ts
32868
33029
  import { existsSync as existsSync8 } from "fs";
32869
- import { basename as basename3, join as join13, resolve as resolve5 } from "path";
33030
+ import { basename as basename3, join as join14, resolve as resolve5 } from "path";
32870
33031
 
32871
33032
  // src/mcp/server.ts
32872
33033
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -32919,7 +33080,7 @@ async function mcp(options) {
32919
33080
  let defaultWorkspace;
32920
33081
  if (options.projectRoot) {
32921
33082
  const explicit = resolve5(options.projectRoot);
32922
- const configPath2 = join13(explicit, ".rafters", "config.rafters.json");
33083
+ const configPath2 = join14(explicit, ".rafters", "config.rafters.json");
32923
33084
  if (!existsSync8(configPath2)) {
32924
33085
  process.stderr.write(
32925
33086
  `--project-root ${explicit} does not contain .rafters/config.rafters.json
@@ -32945,7 +33106,7 @@ import { resolve as resolve6 } from "path";
32945
33106
 
32946
33107
  // ../studio/src/api/vite-plugin.ts
32947
33108
  import { readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
32948
- import { isAbsolute as isAbsolute4, join as join14 } from "path";
33109
+ import { isAbsolute as isAbsolute4, join as join15 } from "path";
32949
33110
  var REGISTRY_PLUGINS3 = [scalePlugin, contrastPlugin, statePlugin, invertPlugin];
32950
33111
  var STUDIO_REASON_DEFAULT = "studio interactive edit";
32951
33112
  var TokenResponseSchema = external_exports.object({
@@ -32971,8 +33132,8 @@ var ColorBuildOptionsSchema = external_exports.object({
32971
33132
  states: external_exports.record(external_exports.string(), external_exports.string()).optional()
32972
33133
  });
32973
33134
  var projectPath = process.env.RAFTERS_PROJECT_PATH || process.cwd();
32974
- var outputDir = join14(projectPath, ".rafters", "output");
32975
- var configPath = join14(projectPath, ".rafters", "config.rafters.json");
33135
+ var outputDir = join15(projectPath, ".rafters", "output");
33136
+ var configPath = join15(projectPath, ".rafters", "config.rafters.json");
32976
33137
  var FontsConfigSchema = external_exports.object({
32977
33138
  path: external_exports.union([external_exports.string(), external_exports.null()]).optional(),
32978
33139
  imports: external_exports.array(external_exports.string()).optional()
@@ -33399,7 +33560,7 @@ function studioApiPlugin() {
33399
33560
  return {
33400
33561
  name: "rafters-studio-api",
33401
33562
  async configureServer(server) {
33402
- const tokensDir = join14(projectPath, ".rafters", "tokens");
33563
+ const tokensDir = join15(projectPath, ".rafters", "tokens");
33403
33564
  try {
33404
33565
  registry2 = loadRegistryFromDir(tokensDir, REGISTRY_PLUGINS3);
33405
33566
  initialized = true;
@@ -33462,11 +33623,11 @@ function studioApiPlugin() {
33462
33623
  const classDirs = watchConfig ? resolveContentSources(projectPath, watchConfig) : [];
33463
33624
  const newDirs = classDirs.filter((d2) => !trackedClassDirs.has(d2));
33464
33625
  if (newDirs.length > 0) {
33465
- server.watcher.add(newDirs.map((dir) => join14(dir, "**/*.classes.ts")));
33626
+ server.watcher.add(newDirs.map((dir) => join15(dir, "**/*.classes.ts")));
33466
33627
  for (const d2 of newDirs) trackedClassDirs.add(d2);
33467
33628
  }
33468
33629
  };
33469
- server.watcher.add([join14(tokensDir, "*.rafters.json"), configPath]);
33630
+ server.watcher.add([join15(tokensDir, "*.rafters.json"), configPath]);
33470
33631
  await syncWatchTargets();
33471
33632
  let debounce = null;
33472
33633
  const onChange = (changed) => {
@@ -96,6 +96,18 @@ declare const PropFieldSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
96
96
  prop: z.ZodString;
97
97
  }, z.core.$strip>;
98
98
  }, z.core.$strip>>;
99
+ }, z.core.$strip>, z.ZodObject<{
100
+ type: z.ZodLiteral<"boolean">;
101
+ default: z.ZodOptional<z.ZodBoolean>;
102
+ required: z.ZodOptional<z.ZodBoolean>;
103
+ }, z.core.$strip>, z.ZodObject<{
104
+ type: z.ZodLiteral<"string">;
105
+ default: z.ZodOptional<z.ZodString>;
106
+ required: z.ZodOptional<z.ZodBoolean>;
107
+ }, z.core.$strip>, z.ZodObject<{
108
+ type: z.ZodLiteral<"number">;
109
+ default: z.ZodOptional<z.ZodNumber>;
110
+ required: z.ZodOptional<z.ZodBoolean>;
99
111
  }, z.core.$strip>, z.ZodObject<{
100
112
  type: z.ZodLiteral<"grammar">;
101
113
  grammar: z.ZodArray<z.ZodString>;
@@ -126,6 +138,18 @@ declare const FacetSchema: z.ZodObject<{
126
138
  prop: z.ZodString;
127
139
  }, z.core.$strip>;
128
140
  }, z.core.$strip>>;
141
+ }, z.core.$strip>, z.ZodObject<{
142
+ type: z.ZodLiteral<"boolean">;
143
+ default: z.ZodOptional<z.ZodBoolean>;
144
+ required: z.ZodOptional<z.ZodBoolean>;
145
+ }, z.core.$strip>, z.ZodObject<{
146
+ type: z.ZodLiteral<"string">;
147
+ default: z.ZodOptional<z.ZodString>;
148
+ required: z.ZodOptional<z.ZodBoolean>;
149
+ }, z.core.$strip>, z.ZodObject<{
150
+ type: z.ZodLiteral<"number">;
151
+ default: z.ZodOptional<z.ZodNumber>;
152
+ required: z.ZodOptional<z.ZodBoolean>;
129
153
  }, z.core.$strip>, z.ZodObject<{
130
154
  type: z.ZodLiteral<"grammar">;
131
155
  grammar: z.ZodArray<z.ZodString>;
@@ -195,6 +219,18 @@ declare const RegistryItemSchema: z.ZodObject<{
195
219
  prop: z.ZodString;
196
220
  }, z.core.$strip>;
197
221
  }, z.core.$strip>>;
222
+ }, z.core.$strip>, z.ZodObject<{
223
+ type: z.ZodLiteral<"boolean">;
224
+ default: z.ZodOptional<z.ZodBoolean>;
225
+ required: z.ZodOptional<z.ZodBoolean>;
226
+ }, z.core.$strip>, z.ZodObject<{
227
+ type: z.ZodLiteral<"string">;
228
+ default: z.ZodOptional<z.ZodString>;
229
+ required: z.ZodOptional<z.ZodBoolean>;
230
+ }, z.core.$strip>, z.ZodObject<{
231
+ type: z.ZodLiteral<"number">;
232
+ default: z.ZodOptional<z.ZodNumber>;
233
+ required: z.ZodOptional<z.ZodBoolean>;
198
234
  }, z.core.$strip>, z.ZodObject<{
199
235
  type: z.ZodLiteral<"grammar">;
200
236
  grammar: z.ZodArray<z.ZodString>;
@@ -33,6 +33,21 @@ var PropFieldSchema = z.discriminatedUnion("type", [
33
33
  requires: z.object({ prop: z.string() })
34
34
  }).optional()
35
35
  }),
36
+ z.object({
37
+ type: z.literal("boolean"),
38
+ default: z.boolean().optional(),
39
+ required: z.boolean().optional()
40
+ }),
41
+ z.object({
42
+ type: z.literal("string"),
43
+ default: z.string().optional(),
44
+ required: z.boolean().optional()
45
+ }),
46
+ z.object({
47
+ type: z.literal("number"),
48
+ default: z.number().optional(),
49
+ required: z.boolean().optional()
50
+ }),
36
51
  z.object({
37
52
  // Matches #2072's PropNode 'grammar' arm exactly, so graph.ts's
38
53
  // describe(<id>.props.<name>.vocab) drill has real shape data.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rafters",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Design Intelligence CLI. Scaffold tokens, import existing shadcn/Tailwind v4 sources, add components, and serve an MCP server so AI agents read decisions instead of guessing.",
5
5
  "homepage": "https://rafters.studio",
6
6
  "license": "MIT",