rafters 0.0.12 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +204 -43
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -13290,7 +13290,7 @@ function transformPath(registryPath, config3) {
13290
13290
  function fileExists(cwd, relativePath) {
13291
13291
  return existsSync(join3(cwd, relativePath));
13292
13292
  }
13293
- function transformFileContent(content, config3) {
13293
+ function transformFileContent(content, config3, fileType = "component") {
13294
13294
  let transformed = content;
13295
13295
  const componentsPath = config3?.componentsPath ?? "components/ui";
13296
13296
  const primitivesPath = config3?.primitivesPath ?? "lib/primitives";
@@ -13302,10 +13302,8 @@ function transformFileContent(content, config3) {
13302
13302
  /from\s+['"]\.\.\/primitives\/([^'"]+)['"]/g,
13303
13303
  `from '@/${primitivesPath}/$1'`
13304
13304
  );
13305
- transformed = transformed.replace(
13306
- /from\s+['"]\.\/([^'"]+)['"]/g,
13307
- `from '@/${componentsPath}/$1'`
13308
- );
13305
+ const siblingPath = fileType === "primitive" ? primitivesPath : componentsPath;
13306
+ transformed = transformed.replace(/from\s+['"]\.\/([^'"]+)['"]/g, `from '@/${siblingPath}/$1'`);
13309
13307
  const libPath = dirname(primitivesPath);
13310
13308
  transformed = transformed.replace(
13311
13309
  /from\s+['"]\.\.\/lib\/([^'"]+)['"]/g,
@@ -13342,7 +13340,8 @@ async function installItem(cwd, item, options, config3) {
13342
13340
  }
13343
13341
  }
13344
13342
  await mkdir(dirname(targetPath), { recursive: true });
13345
- const transformedContent = transformFileContent(file2.content, config3);
13343
+ const fileType = item.type === "primitive" ? "primitive" : "component";
13344
+ const transformedContent = transformFileContent(file2.content, config3, fileType);
13346
13345
  await writeFile(targetPath, transformedContent, "utf-8");
13347
13346
  installedFiles.push(projectPath);
13348
13347
  }
@@ -24715,7 +24714,7 @@ var require_volume = __commonJS({
24715
24714
  var Dir_1 = require_Dir();
24716
24715
  var resolveCrossPlatform = pathModule.resolve;
24717
24716
  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;
24718
- var { sep: sep2, relative: relative2, join: join12, dirname: dirname4 } = pathModule.posix ? pathModule.posix : pathModule;
24717
+ var { sep: sep2, relative: relative3, join: join12, dirname: dirname4 } = pathModule.posix ? pathModule.posix : pathModule;
24719
24718
  var kMinPoolSpace = 128;
24720
24719
  var EPERM = "EPERM";
24721
24720
  var ENOENT2 = "ENOENT";
@@ -25058,7 +25057,7 @@ var require_volume = __commonJS({
25058
25057
  if (node.isFile()) {
25059
25058
  let filename = child.getPath();
25060
25059
  if (path2)
25061
- filename = relative2(path2, filename);
25060
+ filename = relative3(path2, filename);
25062
25061
  json3[filename] = asBuffer ? node.getBuffer() : node.getString();
25063
25062
  } else if (node.isDirectory()) {
25064
25063
  this._toJSON(child, json3, path2, asBuffer);
@@ -25066,7 +25065,7 @@ var require_volume = __commonJS({
25066
25065
  }
25067
25066
  let dirPath = link.getPath();
25068
25067
  if (path2)
25069
- dirPath = relative2(path2, dirPath);
25068
+ dirPath = relative3(path2, dirPath);
25070
25069
  if (dirPath && isEmpty) {
25071
25070
  json3[dirPath] = null;
25072
25071
  }
@@ -26544,7 +26543,7 @@ var require_volume = __commonJS({
26544
26543
  const filepath = link.getPath();
26545
26544
  const node = link.getNode();
26546
26545
  const onNodeChange = () => {
26547
- let filename = relative2(this._filename, filepath);
26546
+ let filename = relative3(this._filename, filepath);
26548
26547
  if (!filename) {
26549
26548
  filename = this._getName();
26550
26549
  }
@@ -26559,7 +26558,7 @@ var require_volume = __commonJS({
26559
26558
  var _a;
26560
26559
  const node = link.getNode();
26561
26560
  const onLinkChildAdd = (l) => {
26562
- this.emit("change", "rename", relative2(this._filename, l.getPath()));
26561
+ this.emit("change", "rename", relative3(this._filename, l.getPath()));
26563
26562
  setTimeout(() => {
26564
26563
  watchLinkNodeChanged(l);
26565
26564
  watchLinkChildrenChanged(l);
@@ -26580,7 +26579,7 @@ var require_volume = __commonJS({
26580
26579
  }
26581
26580
  };
26582
26581
  removeLinkNodeListeners(l);
26583
- this.emit("change", "rename", relative2(this._filename, l.getPath()));
26582
+ this.emit("change", "rename", relative3(this._filename, l.getPath()));
26584
26583
  };
26585
26584
  for (const [name2, childLink] of link.children.entries()) {
26586
26585
  if (childLink && name2 !== "." && name2 !== "..") {
@@ -33680,10 +33679,10 @@ var Ignore = class {
33680
33679
  ignored(p) {
33681
33680
  const fullpath = p.fullpath();
33682
33681
  const fullpaths = `${fullpath}/`;
33683
- const relative2 = p.relative() || ".";
33684
- const relatives = `${relative2}/`;
33682
+ const relative3 = p.relative() || ".";
33683
+ const relatives = `${relative3}/`;
33685
33684
  for (const m of this.relative) {
33686
- if (m.match(relative2) || m.match(relatives))
33685
+ if (m.match(relative3) || m.match(relatives))
33687
33686
  return true;
33688
33687
  }
33689
33688
  for (const m of this.absolute) {
@@ -33694,9 +33693,9 @@ var Ignore = class {
33694
33693
  }
33695
33694
  childrenIgnored(p) {
33696
33695
  const fullpath = p.fullpath() + "/";
33697
- const relative2 = (p.relative() || ".") + "/";
33696
+ const relative3 = (p.relative() || ".") + "/";
33698
33697
  for (const m of this.relativeChildren) {
33699
- if (m.match(relative2))
33698
+ if (m.match(relative3))
33700
33699
  return true;
33701
33700
  }
33702
33701
  for (const m of this.absoluteChildren) {
@@ -46629,6 +46628,9 @@ function getSemanticMappingsFromTokens(semanticTokens) {
46629
46628
  function tokenValueToCSS(token) {
46630
46629
  const { value: value2 } = token;
46631
46630
  if (typeof value2 === "string") {
46631
+ if (value2.startsWith("{") || value2.startsWith("[")) {
46632
+ return null;
46633
+ }
46632
46634
  return value2;
46633
46635
  }
46634
46636
  if (typeof value2 === "object" && value2 !== null) {
@@ -46754,6 +46756,7 @@ function generateThemeBlock(groups) {
46754
46756
  if (groups.color.length > 0) {
46755
46757
  for (const token of groups.color) {
46756
46758
  const value2 = tokenValueToCSS(token);
46759
+ if (value2 === null) continue;
46757
46760
  lines.push(` --color-${token.name}: ${value2};`);
46758
46761
  }
46759
46762
  lines.push("");
@@ -46761,6 +46764,7 @@ function generateThemeBlock(groups) {
46761
46764
  if (groups.spacing.length > 0) {
46762
46765
  for (const token of groups.spacing) {
46763
46766
  const value2 = tokenValueToCSS(token);
46767
+ if (value2 === null) continue;
46764
46768
  lines.push(` --spacing-${token.name}: ${value2};`);
46765
46769
  }
46766
46770
  lines.push("");
@@ -46768,6 +46772,7 @@ function generateThemeBlock(groups) {
46768
46772
  if (groups.typography.length > 0) {
46769
46773
  for (const token of groups.typography) {
46770
46774
  const value2 = tokenValueToCSS(token);
46775
+ if (value2 === null) continue;
46771
46776
  lines.push(` --${token.name}: ${value2};`);
46772
46777
  if (token.lineHeight) {
46773
46778
  lines.push(` --${token.name}--line-height: ${token.lineHeight};`);
@@ -46778,6 +46783,7 @@ function generateThemeBlock(groups) {
46778
46783
  if (groups.radius.length > 0) {
46779
46784
  for (const token of groups.radius) {
46780
46785
  const value2 = tokenValueToCSS(token);
46786
+ if (value2 === null) continue;
46781
46787
  lines.push(` --radius-${token.name}: ${value2};`);
46782
46788
  }
46783
46789
  lines.push("");
@@ -46785,6 +46791,7 @@ function generateThemeBlock(groups) {
46785
46791
  if (groups.shadow.length > 0) {
46786
46792
  for (const token of groups.shadow) {
46787
46793
  const value2 = tokenValueToCSS(token);
46794
+ if (value2 === null) continue;
46788
46795
  lines.push(` --shadow-${token.name}: ${value2};`);
46789
46796
  }
46790
46797
  lines.push("");
@@ -46792,6 +46799,7 @@ function generateThemeBlock(groups) {
46792
46799
  if (groups.depth.length > 0) {
46793
46800
  for (const token of groups.depth) {
46794
46801
  const value2 = tokenValueToCSS(token);
46802
+ if (value2 === null) continue;
46795
46803
  lines.push(` --${token.name}: ${value2};`);
46796
46804
  }
46797
46805
  lines.push("");
@@ -46799,6 +46807,7 @@ function generateThemeBlock(groups) {
46799
46807
  if (groups.motion.length > 0) {
46800
46808
  for (const token of groups.motion) {
46801
46809
  const value2 = tokenValueToCSS(token);
46810
+ if (value2 === null) continue;
46802
46811
  lines.push(` --${token.name}: ${value2};`);
46803
46812
  }
46804
46813
  lines.push("");
@@ -46806,6 +46815,7 @@ function generateThemeBlock(groups) {
46806
46815
  if (groups.breakpoint.length > 0) {
46807
46816
  for (const token of groups.breakpoint) {
46808
46817
  const value2 = tokenValueToCSS(token);
46818
+ if (value2 === null) continue;
46809
46819
  lines.push(` --${token.name}: ${value2};`);
46810
46820
  }
46811
46821
  lines.push("");
@@ -46813,6 +46823,7 @@ function generateThemeBlock(groups) {
46813
46823
  if (groups.elevation.length > 0) {
46814
46824
  for (const token of groups.elevation) {
46815
46825
  const value2 = tokenValueToCSS(token);
46826
+ if (value2 === null) continue;
46816
46827
  lines.push(` --${token.name}: ${value2};`);
46817
46828
  }
46818
46829
  lines.push("");
@@ -46820,6 +46831,7 @@ function generateThemeBlock(groups) {
46820
46831
  if (groups.focus.length > 0) {
46821
46832
  for (const token of groups.focus) {
46822
46833
  const value2 = tokenValueToCSS(token);
46834
+ if (value2 === null) continue;
46823
46835
  lines.push(` --${token.name}: ${value2};`);
46824
46836
  }
46825
46837
  lines.push("");
@@ -46827,6 +46839,7 @@ function generateThemeBlock(groups) {
46827
46839
  if (groups.other.length > 0) {
46828
46840
  for (const token of groups.other) {
46829
46841
  const value2 = tokenValueToCSS(token);
46842
+ if (value2 === null) continue;
46830
46843
  lines.push(` --${token.name}: ${value2};`);
46831
46844
  }
46832
46845
  lines.push("");
@@ -47008,6 +47021,9 @@ async function registryToCompiled(registry2, options = {}) {
47008
47021
  function tokenValueToTS(token) {
47009
47022
  const { value: value2 } = token;
47010
47023
  if (typeof value2 === "string") {
47024
+ if (value2.startsWith("{") || value2.startsWith("[")) {
47025
+ return value2;
47026
+ }
47011
47027
  return escapeStringValue(value2);
47012
47028
  }
47013
47029
  if (typeof value2 === "object" && value2 !== null) {
@@ -51494,7 +51510,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprot
51494
51510
  // src/mcp/tools.ts
51495
51511
  import { existsSync as existsSync4 } from "fs";
51496
51512
  import { readdir as readdir3, readFile as readFile6 } from "fs/promises";
51497
- import { basename, join as join10 } from "path";
51513
+ import { basename, join as join10, relative as relative2 } from "path";
51498
51514
 
51499
51515
  // src/mcp/cognitive-load.ts
51500
51516
  var BUDGET_TIERS = {
@@ -52401,7 +52417,53 @@ Rafters Button, Input, Card, etc. include their own spacing, sizing, and states.
52401
52417
  UTILITIES EXIST FOR EDGE CASES.
52402
52418
  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
52419
 
52420
+ COLORS ARE TAILWIND CLASSES.
52421
+ Write border-l-primary, bg-success, text-info-foreground. Do not create color constants, mapping objects, or reference palette names (silver-true-sky-500, neutral-400, etc). The designer already decided what each token looks like. Palette families are internal -- never use them in consumer code.
52422
+
52423
+ SYSTEM COLOR TOKENS (use these as Tailwind class fragments):
52424
+ - primary / primary-foreground -- Main brand, CTA buttons, links
52425
+ - secondary / secondary-foreground -- Less prominent actions
52426
+ - accent / accent-foreground -- Hover highlights, emphasis
52427
+ - muted / muted-foreground -- Subdued backgrounds, disabled text
52428
+ - destructive / destructive-foreground -- Delete, remove, errors
52429
+ - success / success-foreground -- Confirmations, positive feedback
52430
+ - warning / warning-foreground -- Caution, important notices
52431
+ - info / info-foreground -- Tips, help, neutral information
52432
+ - chart-1 through chart-5 -- Categorical distinction (guaranteed harmonious)
52433
+ - card / card-foreground -- Card surfaces
52434
+ - popover / popover-foreground -- Overlay surfaces
52435
+ - border, input, ring -- Structural tokens
52436
+
52437
+ For categorical color coding (e.g. field types), use border-l-{token} on a container with bg-card. Example Tailwind classes: "border-l-4 border-l-primary", "border-l-4 border-l-info", "border-l-4 border-l-success".
52438
+
52404
52439
  When in doubt: less code, not more. Rafters has already made the design decision.`;
52440
+ var CONSUMER_QUICKSTART = {
52441
+ rule1: "Components are pre-styled. Import and render. Do not add className for visual styling -- only for layout context (e.g. border-l-4 for accent). Input knows its focus ring. Label knows its weight. You arrange, you do not style.",
52442
+ rule2: "Colors are Tailwind classes. Write border-l-primary, bg-success, text-info-foreground. Do not create color constants, mapping objects, or reference palette internals.",
52443
+ rule3: "Layout uses Container and Grid presets (sidebar-main, form, cards, row, stack, split). Do not write flex, grid, gap-* directly.",
52444
+ antiPatterns: [
52445
+ "Do NOT reference palette families (silver-true-sky-500, neutral-400). Those are internal.",
52446
+ "Do NOT add className to Input/Label/Select for styling. They handle their own styles.",
52447
+ "Do NOT create wrapper components that add styling to Rafters components.",
52448
+ "Do NOT define focus states, hover states, or error states. Components include these.",
52449
+ "Do NOT create color mapping objects. The Tailwind class name IS the mapping."
52450
+ ],
52451
+ colorTokens: {
52452
+ semantic: [
52453
+ "primary -- Main brand, CTA buttons, links",
52454
+ "secondary -- Less prominent actions",
52455
+ "accent -- Hover highlights, emphasis",
52456
+ "muted -- Subdued backgrounds, disabled text",
52457
+ "destructive -- Delete, remove, errors",
52458
+ "success -- Confirmations, positive feedback",
52459
+ "warning -- Caution, important notices",
52460
+ "info -- Tips, help, neutral information"
52461
+ ],
52462
+ categorical: "chart-1 through chart-5 -- For data viz and categorical distinction (guaranteed harmonious)",
52463
+ structural: "card, popover, border, input, ring -- Surface and boundary tokens",
52464
+ usage: "Use as Tailwind class fragments: bg-primary, text-info-foreground, border-l-success. Each token has a -foreground variant for text on that background."
52465
+ }
52466
+ };
52405
52467
  var DESIGN_PATTERNS = {
52406
52468
  "destructive-action": {
52407
52469
  name: "Destructive Action",
@@ -52701,7 +52763,7 @@ var DESIGN_PATTERNS = {
52701
52763
  var TOOL_DEFINITIONS = [
52702
52764
  {
52703
52765
  name: "rafters_vocabulary",
52704
- description: "Get compact design system vocabulary: color palette names, spacing scale, type scale, and component list with cognitive loads. Use this first to understand what you can compose with.",
52766
+ description: "Get design system vocabulary with consumer quickstart guide. Returns: system rules, onboarding guidance (what to do and what NOT to do), semantic color tokens, spacing scale, type scale, layout presets, and component list. ALWAYS call this first before building with Rafters.",
52705
52767
  inputSchema: {
52706
52768
  type: "object",
52707
52769
  properties: {},
@@ -52813,7 +52875,7 @@ var RaftersToolHandler = class {
52813
52875
  // ==================== Tool 1: Vocabulary ====================
52814
52876
  /**
52815
52877
  * Get compact design system vocabulary
52816
- * Returns: color palettes, spacing scale, type scale, component list with loads
52878
+ * Returns: system rules, consumer quickstart, color tokens, spacing, type scale, components
52817
52879
  */
52818
52880
  async getVocabulary() {
52819
52881
  try {
@@ -52825,6 +52887,7 @@ var RaftersToolHandler = class {
52825
52887
  ]);
52826
52888
  const vocabulary = {
52827
52889
  system: SYSTEM_PREAMBLE,
52890
+ quickstart: CONSUMER_QUICKSTART,
52828
52891
  components,
52829
52892
  colors,
52830
52893
  spacing,
@@ -52844,16 +52907,50 @@ var RaftersToolHandler = class {
52844
52907
  }
52845
52908
  }
52846
52909
  /**
52847
- * Extract compact color vocabulary
52910
+ * Extract compact color vocabulary.
52911
+ * Always includes the known semantic token list so consumers get guidance
52912
+ * even when dynamic token loading fails or returns empty.
52848
52913
  */
52849
52914
  async getColorVocabulary() {
52915
+ const knownSemantic = [
52916
+ "primary",
52917
+ "primary-foreground",
52918
+ "secondary",
52919
+ "secondary-foreground",
52920
+ "accent",
52921
+ "accent-foreground",
52922
+ "muted",
52923
+ "muted-foreground",
52924
+ "destructive",
52925
+ "destructive-foreground",
52926
+ "success",
52927
+ "success-foreground",
52928
+ "warning",
52929
+ "warning-foreground",
52930
+ "info",
52931
+ "info-foreground",
52932
+ "card",
52933
+ "card-foreground",
52934
+ "popover",
52935
+ "popover-foreground",
52936
+ "background",
52937
+ "foreground",
52938
+ "border",
52939
+ "input",
52940
+ "ring",
52941
+ "chart-1",
52942
+ "chart-2",
52943
+ "chart-3",
52944
+ "chart-4",
52945
+ "chart-5"
52946
+ ];
52850
52947
  try {
52851
52948
  const tokens = await this.loadNamespace("color");
52852
- const semantic = [];
52949
+ const dynamicSemantic = [];
52853
52950
  const palettes = /* @__PURE__ */ new Map();
52854
52951
  for (const token of tokens) {
52855
52952
  if (typeof token.value === "object" && "family" in token.value) {
52856
- semantic.push(token.name);
52953
+ dynamicSemantic.push(token.name);
52857
52954
  } else if (typeof token.value === "object" && "scale" in token.value) {
52858
52955
  const colorValue = token.value;
52859
52956
  palettes.set(
@@ -52862,19 +52959,26 @@ var RaftersToolHandler = class {
52862
52959
  );
52863
52960
  }
52864
52961
  }
52962
+ const allSemantic = [.../* @__PURE__ */ new Set([...knownSemantic, ...dynamicSemantic])];
52865
52963
  return {
52866
- semantic,
52964
+ semantic: allSemantic,
52867
52965
  palettes: [...palettes.entries()].map(([name2, positions]) => ({
52868
52966
  name: name2,
52869
52967
  positions: [...positions]
52870
- }))
52968
+ })),
52969
+ usage: "Use as Tailwind classes: bg-primary, text-info-foreground, border-l-success. Palette families are internal -- never reference them in consumer code."
52871
52970
  };
52872
52971
  } catch {
52873
- return { semantic: [], palettes: [] };
52972
+ return {
52973
+ semantic: knownSemantic,
52974
+ palettes: [],
52975
+ usage: "Use as Tailwind classes: bg-primary, text-info-foreground, border-l-success. Palette families are internal -- never reference them in consumer code."
52976
+ };
52874
52977
  }
52875
52978
  }
52876
52979
  /**
52877
- * Extract compact spacing vocabulary
52980
+ * Extract compact spacing vocabulary.
52981
+ * Includes static scale names so consumers always get guidance.
52878
52982
  */
52879
52983
  async getSpacingVocabulary() {
52880
52984
  try {
@@ -52885,13 +52989,32 @@ var RaftersToolHandler = class {
52885
52989
  scale2[token.name] = token.value;
52886
52990
  }
52887
52991
  }
52888
- return { scale: scale2 };
52992
+ if (Object.keys(scale2).length > 0) {
52993
+ return {
52994
+ scale: scale2,
52995
+ usage: "Container and Grid handle spacing. Do not use gap-*, p-*, m-* directly."
52996
+ };
52997
+ }
52889
52998
  } catch {
52890
- return { scale: {} };
52891
52999
  }
53000
+ return {
53001
+ scale: {
53002
+ "spacing-0": "0rem",
53003
+ "spacing-1": "0.25rem",
53004
+ "spacing-2": "0.5rem",
53005
+ "spacing-3": "0.75rem",
53006
+ "spacing-4": "1rem",
53007
+ "spacing-6": "1.5rem",
53008
+ "spacing-8": "2rem",
53009
+ "spacing-12": "3rem",
53010
+ "spacing-16": "4rem"
53011
+ },
53012
+ usage: "Container and Grid handle spacing. Do not use gap-*, p-*, m-* directly."
53013
+ };
52892
53014
  }
52893
53015
  /**
52894
- * Extract compact typography vocabulary
53016
+ * Extract compact typography vocabulary.
53017
+ * Includes static fallbacks so consumers always get guidance.
52895
53018
  */
52896
53019
  async getTypographyVocabulary() {
52897
53020
  try {
@@ -52907,9 +53030,44 @@ var RaftersToolHandler = class {
52907
53030
  }
52908
53031
  }
52909
53032
  }
52910
- return { sizes, weights: [...weights] };
53033
+ if (Object.keys(sizes).length > 0) {
53034
+ return {
53035
+ sizes,
53036
+ weights: [...weights],
53037
+ usage: "Typography components (H1-H4, P, Label) handle sizing. Do not set font sizes directly."
53038
+ };
53039
+ }
52911
53040
  } catch {
52912
- return { sizes: {}, weights: [] };
53041
+ }
53042
+ return {
53043
+ sizes: {
53044
+ "font-size-xs": "0.75rem",
53045
+ "font-size-sm": "0.875rem",
53046
+ "font-size-base": "1rem",
53047
+ "font-size-lg": "1.125rem",
53048
+ "font-size-xl": "1.25rem",
53049
+ "font-size-2xl": "1.5rem",
53050
+ "font-size-3xl": "1.875rem",
53051
+ "font-size-4xl": "2.25rem"
53052
+ },
53053
+ weights: ["normal", "medium", "semibold", "bold"],
53054
+ usage: "Typography components (H1-H4, P, Label) handle sizing. Do not set font sizes directly."
53055
+ };
53056
+ }
53057
+ /**
53058
+ * Load and cache the project's Rafters config from .rafters/config.rafters.json.
53059
+ * Returns null when no config exists or when it cannot be parsed.
53060
+ */
53061
+ async loadConfig() {
53062
+ try {
53063
+ const paths = getRaftersPaths(this.projectRoot);
53064
+ if (!existsSync4(paths.config)) {
53065
+ return null;
53066
+ }
53067
+ const content = await readFile6(paths.config, "utf-8");
53068
+ return JSON.parse(content);
53069
+ } catch {
53070
+ return null;
52913
53071
  }
52914
53072
  }
52915
53073
  /**
@@ -52918,10 +53076,8 @@ var RaftersToolHandler = class {
52918
53076
  async getComponentVocabulary() {
52919
53077
  let installed = [];
52920
53078
  try {
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);
53079
+ const config3 = await this.loadConfig();
53080
+ if (config3) {
52925
53081
  installed = config3.installed?.components ?? [];
52926
53082
  }
52927
53083
  } catch {
@@ -52973,17 +53129,22 @@ var RaftersToolHandler = class {
52973
53129
  }
52974
53130
  // ==================== Tool 3: Component ====================
52975
53131
  /**
52976
- * Get path to UI components directory
53132
+ * Get path to UI components directory.
53133
+ * Reads componentsPath from .rafters/config.rafters.json when available,
53134
+ * falls back to the monorepo layout for local development.
52977
53135
  */
52978
- getComponentsPath() {
52979
- const monorepoPath = join10(this.projectRoot, "packages/ui/src/components/ui");
52980
- return monorepoPath;
53136
+ async getComponentsPath() {
53137
+ const config3 = await this.loadConfig();
53138
+ if (config3?.componentsPath) {
53139
+ return join10(this.projectRoot, config3.componentsPath);
53140
+ }
53141
+ return join10(this.projectRoot, "packages/ui/src/components/ui");
52981
53142
  }
52982
53143
  /**
52983
53144
  * Load component metadata from source file
52984
53145
  */
52985
53146
  async loadComponentMetadata(name2) {
52986
- const componentsPath = this.getComponentsPath();
53147
+ const componentsPath = await this.getComponentsPath();
52987
53148
  const filePath = join10(componentsPath, `${name2}.tsx`);
52988
53149
  try {
52989
53150
  const source = await readFile6(filePath, "utf-8");
@@ -53002,7 +53163,7 @@ var RaftersToolHandler = class {
53002
53163
  sizes: extractSizes(source),
53003
53164
  dependencies: extractDependencies(source),
53004
53165
  primitives: extractPrimitiveDependencies(source),
53005
- filePath: `packages/ui/src/components/ui/${name2}.tsx`
53166
+ filePath: relative2(this.projectRoot, join10(componentsPath, `${name2}.tsx`))
53006
53167
  };
53007
53168
  if (hasAnyDeps(jsDocDeps)) {
53008
53169
  metadata.jsDocDependencies = jsDocDeps;
@@ -53094,7 +53255,7 @@ var RaftersToolHandler = class {
53094
53255
  try {
53095
53256
  const metadata = await this.loadComponentMetadata(name2);
53096
53257
  if (!metadata) {
53097
- const componentsPath = this.getComponentsPath();
53258
+ const componentsPath = await this.getComponentsPath();
53098
53259
  let available = [];
53099
53260
  try {
53100
53261
  const files = await readdir3(componentsPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rafters",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "description": "CLI for Rafters design system - scaffold tokens and add components",
5
5
  "license": "MIT",
6
6
  "type": "module",