rafters 0.0.49 → 0.0.52

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 +334 -112
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -7100,8 +7100,8 @@ var require_util = __commonJS({
7100
7100
  }
7101
7101
  function fn() {
7102
7102
  var promiseResolve, promiseReject;
7103
- var promise = new Promise(function(resolve7, reject) {
7104
- promiseResolve = resolve7;
7103
+ var promise = new Promise(function(resolve8, reject) {
7104
+ promiseResolve = resolve8;
7105
7105
  promiseReject = reject;
7106
7106
  });
7107
7107
  var args = [];
@@ -10212,8 +10212,8 @@ var require_path = __commonJS({
10212
10212
  var win323 = {};
10213
10213
  function win32SplitPath(filename) {
10214
10214
  var result = splitDeviceRe.exec(filename), device = (result[1] || "") + (result[2] || ""), tail = result[3] || "";
10215
- var result2 = splitTailRe.exec(tail), dir = result2[1], basename3 = result2[2], ext2 = result2[3];
10216
- return [device, dir, basename3, ext2];
10215
+ var result2 = splitTailRe.exec(tail), dir = result2[1], basename5 = result2[2], ext2 = result2[3];
10216
+ return [device, dir, basename5, ext2];
10217
10217
  }
10218
10218
  function win32StatPath(path2) {
10219
10219
  var result = splitDeviceRe.exec(path2), device = result[1] || "", isUnc = !!device && device[1] !== ":";
@@ -11065,7 +11065,7 @@ var require_events = __commonJS({
11065
11065
  return ret;
11066
11066
  }
11067
11067
  function once(emitter, name2) {
11068
- return new Promise(function(resolve7, reject) {
11068
+ return new Promise(function(resolve8, reject) {
11069
11069
  function errorListener(err) {
11070
11070
  emitter.removeListener(name2, resolver);
11071
11071
  reject(err);
@@ -11074,7 +11074,7 @@ var require_events = __commonJS({
11074
11074
  if (typeof emitter.removeListener === "function") {
11075
11075
  emitter.removeListener("error", errorListener);
11076
11076
  }
11077
- resolve7([].slice.call(arguments));
11077
+ resolve8([].slice.call(arguments));
11078
11078
  }
11079
11079
  ;
11080
11080
  eventTargetAgnosticAddListener(emitter, name2, resolver, { once: true });
@@ -11784,11 +11784,11 @@ var require_util3 = __commonJS({
11784
11784
  var queueMicrotask_1 = require_queueMicrotask();
11785
11785
  exports.isWin = process2.platform === "win32";
11786
11786
  function promisify(fs22, fn, getResult = (input) => input) {
11787
- return (...args) => new Promise((resolve7, reject) => {
11787
+ return (...args) => new Promise((resolve8, reject) => {
11788
11788
  fs22[fn].bind(fs22)(...args, (error3, result) => {
11789
11789
  if (error3)
11790
11790
  return reject(error3);
11791
- return resolve7(getResult(result));
11791
+ return resolve8(getResult(result));
11792
11792
  });
11793
11793
  });
11794
11794
  }
@@ -11944,9 +11944,9 @@ var require_util3 = __commonJS({
11944
11944
  }
11945
11945
  function streamToBuffer(stream2) {
11946
11946
  const chunks = [];
11947
- return new Promise((resolve7, reject) => {
11947
+ return new Promise((resolve8, reject) => {
11948
11948
  stream2.on("data", (chunk) => chunks.push(chunk));
11949
- stream2.on("end", () => resolve7(buffer_1.Buffer.concat(chunks)));
11949
+ stream2.on("end", () => resolve8(buffer_1.Buffer.concat(chunks)));
11950
11950
  stream2.on("error", reject);
11951
11951
  });
11952
11952
  }
@@ -12327,11 +12327,11 @@ function __metadata(metadataKey, metadataValue) {
12327
12327
  }
12328
12328
  function __awaiter(thisArg, _arguments, P, generator) {
12329
12329
  function adopt(value2) {
12330
- return value2 instanceof P ? value2 : new P(function(resolve7) {
12331
- resolve7(value2);
12330
+ return value2 instanceof P ? value2 : new P(function(resolve8) {
12331
+ resolve8(value2);
12332
12332
  });
12333
12333
  }
12334
- return new (P || (P = Promise))(function(resolve7, reject) {
12334
+ return new (P || (P = Promise))(function(resolve8, reject) {
12335
12335
  function fulfilled(value2) {
12336
12336
  try {
12337
12337
  step(generator.next(value2));
@@ -12347,7 +12347,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
12347
12347
  }
12348
12348
  }
12349
12349
  function step(result) {
12350
- result.done ? resolve7(result.value) : adopt(result.value).then(fulfilled, rejected);
12350
+ result.done ? resolve8(result.value) : adopt(result.value).then(fulfilled, rejected);
12351
12351
  }
12352
12352
  step((generator = generator.apply(thisArg, _arguments || [])).next());
12353
12353
  });
@@ -12530,14 +12530,14 @@ function __asyncValues(o) {
12530
12530
  }, i);
12531
12531
  function verb(n2) {
12532
12532
  i[n2] = o[n2] && function(v) {
12533
- return new Promise(function(resolve7, reject) {
12534
- v = o[n2](v), settle(resolve7, reject, v.done, v.value);
12533
+ return new Promise(function(resolve8, reject) {
12534
+ v = o[n2](v), settle(resolve8, reject, v.done, v.value);
12535
12535
  });
12536
12536
  };
12537
12537
  }
12538
- function settle(resolve7, reject, d2, v) {
12538
+ function settle(resolve8, reject, d2, v) {
12539
12539
  Promise.resolve(v).then(function(v2) {
12540
- resolve7({ value: v2, done: d2 });
12540
+ resolve8({ value: v2, done: d2 });
12541
12541
  }, reject);
12542
12542
  }
12543
12543
  }
@@ -12756,13 +12756,13 @@ var require_util4 = __commonJS({
12756
12756
  return Object.assign({ separator: "/", syncHandleAllowed: false, mode: "read" }, partial);
12757
12757
  };
12758
12758
  exports.ctx = ctx;
12759
- var basename3 = (path2, separator) => {
12759
+ var basename5 = (path2, separator) => {
12760
12760
  if (path2[path2.length - 1] === separator)
12761
12761
  path2 = path2.slice(0, -1);
12762
12762
  const lastSlashIndex = path2.lastIndexOf(separator);
12763
12763
  return lastSlashIndex === -1 ? path2 : path2.slice(lastSlashIndex + 1);
12764
12764
  };
12765
- exports.basename = basename3;
12765
+ exports.basename = basename5;
12766
12766
  var nameRegex = /^(\.{1,2})$|^(.*([\/\\]).*)$/;
12767
12767
  var assertName = (name2, method, klass) => {
12768
12768
  const isInvalid = !name2 || nameRegex.test(name2);
@@ -12955,12 +12955,12 @@ var require_Dir = __commonJS({
12955
12955
  return typeof x === "function";
12956
12956
  }
12957
12957
  promisify(obj, fn) {
12958
- return (...args) => new Promise((resolve7, reject) => {
12958
+ return (...args) => new Promise((resolve8, reject) => {
12959
12959
  if (this.isFunction(obj[fn])) {
12960
12960
  obj[fn].bind(obj)(...args, (error3, result) => {
12961
12961
  if (error3)
12962
12962
  reject(error3);
12963
- resolve7(result);
12963
+ resolve8(result);
12964
12964
  });
12965
12965
  } else {
12966
12966
  reject("Not a function");
@@ -13084,7 +13084,7 @@ var require_volume = __commonJS({
13084
13084
  var Dir_1 = require_Dir();
13085
13085
  var resolveCrossPlatform = pathModule.resolve;
13086
13086
  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;
13087
- var { sep: sep2, relative: relative4, join: join15, dirname: dirname5 } = pathModule.posix ? pathModule.posix : pathModule;
13087
+ var { sep: sep2, relative: relative4, join: join16, dirname: dirname6 } = pathModule.posix ? pathModule.posix : pathModule;
13088
13088
  var kMinPoolSpace = 128;
13089
13089
  var EPERM = "EPERM";
13090
13090
  var ENOENT2 = "ENOENT";
@@ -13099,13 +13099,13 @@ var require_volume = __commonJS({
13099
13099
  var ENOSYS = "ENOSYS";
13100
13100
  var ERR_FS_EISDIR = "ERR_FS_EISDIR";
13101
13101
  var ERR_OUT_OF_RANGE = "ERR_OUT_OF_RANGE";
13102
- var resolve7 = (filename, base = process_1.default.cwd()) => resolveCrossPlatform(base, filename);
13102
+ var resolve8 = (filename, base = process_1.default.cwd()) => resolveCrossPlatform(base, filename);
13103
13103
  if (util_1.isWin) {
13104
- const _resolve = resolve7;
13105
- resolve7 = (filename, base) => (0, util_1.unixify)(_resolve(filename, base));
13104
+ const _resolve = resolve8;
13105
+ resolve8 = (filename, base) => (0, util_1.unixify)(_resolve(filename, base));
13106
13106
  }
13107
13107
  function filenameToSteps(filename, base) {
13108
- const fullPath = resolve7(filename, base);
13108
+ const fullPath = resolve8(filename, base);
13109
13109
  const fullPathSansSlash = fullPath.substring(1);
13110
13110
  if (!fullPathSansSlash)
13111
13111
  return [];
@@ -13150,7 +13150,7 @@ var require_volume = __commonJS({
13150
13150
  function flatten(pathPrefix, node) {
13151
13151
  for (const path2 in node) {
13152
13152
  const contentOrNode = node[path2];
13153
- const joinedPath = join15(pathPrefix, path2);
13153
+ const joinedPath = join16(pathPrefix, path2);
13154
13154
  if (typeof contentOrNode === "string" || contentOrNode instanceof buffer_1.Buffer) {
13155
13155
  flatJSON[joinedPath] = contentOrNode;
13156
13156
  } else if (typeof contentOrNode === "object" && contentOrNode !== null && Object.keys(contentOrNode).length > 0) {
@@ -13307,7 +13307,7 @@ var require_volume = __commonJS({
13307
13307
  return null;
13308
13308
  node = curr === null || curr === void 0 ? void 0 : curr.getNode();
13309
13309
  if (resolveSymlinks && node.isSymlink()) {
13310
- const resolvedPath = pathModule.isAbsolute(node.symlink) ? node.symlink : join15(pathModule.dirname(curr.getPath()), node.symlink);
13310
+ const resolvedPath = pathModule.isAbsolute(node.symlink) ? node.symlink : join16(pathModule.dirname(curr.getPath()), node.symlink);
13311
13311
  steps2 = filenameToSteps(resolvedPath).concat(steps2.slice(i + 1));
13312
13312
  curr = this.root;
13313
13313
  i = 0;
@@ -13466,9 +13466,9 @@ var require_volume = __commonJS({
13466
13466
  fromJSON(json2, cwd = process_1.default.cwd()) {
13467
13467
  for (let filename in json2) {
13468
13468
  const data = json2[filename];
13469
- filename = resolve7(filename, cwd);
13469
+ filename = resolve8(filename, cwd);
13470
13470
  if (typeof data === "string" || data instanceof buffer_1.Buffer) {
13471
- const dir = dirname5(filename);
13471
+ const dir = dirname6(filename);
13472
13472
  this.mkdirpBase(
13473
13473
  dir,
13474
13474
  511
@@ -19947,10 +19947,10 @@ var Minipass = class extends EventEmitter {
19947
19947
  * Return a void Promise that resolves once the stream ends.
19948
19948
  */
19949
19949
  async promise() {
19950
- return new Promise((resolve7, reject) => {
19950
+ return new Promise((resolve8, reject) => {
19951
19951
  this.on(DESTROYED, () => reject(new Error("stream destroyed")));
19952
19952
  this.on("error", (er) => reject(er));
19953
- this.on("end", () => resolve7());
19953
+ this.on("end", () => resolve8());
19954
19954
  });
19955
19955
  }
19956
19956
  /**
@@ -19974,7 +19974,7 @@ var Minipass = class extends EventEmitter {
19974
19974
  return Promise.resolve({ done: false, value: res });
19975
19975
  if (this[EOF])
19976
19976
  return stop();
19977
- let resolve7;
19977
+ let resolve8;
19978
19978
  let reject;
19979
19979
  const onerr = (er) => {
19980
19980
  this.off("data", ondata);
@@ -19988,19 +19988,19 @@ var Minipass = class extends EventEmitter {
19988
19988
  this.off("end", onend);
19989
19989
  this.off(DESTROYED, ondestroy);
19990
19990
  this.pause();
19991
- resolve7({ value: value2, done: !!this[EOF] });
19991
+ resolve8({ value: value2, done: !!this[EOF] });
19992
19992
  };
19993
19993
  const onend = () => {
19994
19994
  this.off("error", onerr);
19995
19995
  this.off("data", ondata);
19996
19996
  this.off(DESTROYED, ondestroy);
19997
19997
  stop();
19998
- resolve7({ done: true, value: void 0 });
19998
+ resolve8({ done: true, value: void 0 });
19999
19999
  };
20000
20000
  const ondestroy = () => onerr(new Error("stream destroyed"));
20001
20001
  return new Promise((res2, rej) => {
20002
20002
  reject = rej;
20003
- resolve7 = res2;
20003
+ resolve8 = res2;
20004
20004
  this.once(DESTROYED, ondestroy);
20005
20005
  this.once("error", onerr);
20006
20006
  this.once("end", onend);
@@ -20972,9 +20972,9 @@ var PathBase = class {
20972
20972
  if (this.#asyncReaddirInFlight) {
20973
20973
  await this.#asyncReaddirInFlight;
20974
20974
  } else {
20975
- let resolve7 = () => {
20975
+ let resolve8 = () => {
20976
20976
  };
20977
- this.#asyncReaddirInFlight = new Promise((res) => resolve7 = res);
20977
+ this.#asyncReaddirInFlight = new Promise((res) => resolve8 = res);
20978
20978
  try {
20979
20979
  for (const e of await this.#fs.promises.readdir(fullpath, {
20980
20980
  withFileTypes: true
@@ -20987,7 +20987,7 @@ var PathBase = class {
20987
20987
  children.provisional = 0;
20988
20988
  }
20989
20989
  this.#asyncReaddirInFlight = void 0;
20990
- resolve7();
20990
+ resolve8();
20991
20991
  }
20992
20992
  return children.slice(0, children.provisional);
20993
20993
  }
@@ -24242,7 +24242,7 @@ function toBase64(buffer) {
24242
24242
  if (isNode) {
24243
24243
  return buffer.toString("base64");
24244
24244
  } else {
24245
- return new Promise((resolve7, reject) => {
24245
+ return new Promise((resolve8, reject) => {
24246
24246
  const blob = new Blob([buffer], { type: "application/octet-stream" });
24247
24247
  const reader = new FileReader();
24248
24248
  reader.onloadend = () => {
@@ -24250,7 +24250,7 @@ function toBase64(buffer) {
24250
24250
  /** @type {string } */
24251
24251
  reader.result.split(",")[1]
24252
24252
  );
24253
- resolve7(base64String);
24253
+ resolve8(base64String);
24254
24254
  };
24255
24255
  reader.onerror = reject;
24256
24256
  reader.readAsDataURL(blob);
@@ -27863,7 +27863,7 @@ var AESDecryptionStream = class extends TransformStream {
27863
27863
  super({
27864
27864
  start() {
27865
27865
  Object.assign(this, {
27866
- ready: new Promise((resolve7) => this.resolveReady = resolve7),
27866
+ ready: new Promise((resolve8) => this.resolveReady = resolve8),
27867
27867
  password: encodePassword(password2, rawPassword),
27868
27868
  signed,
27869
27869
  strength: encryptionStrength - 1,
@@ -27931,7 +27931,7 @@ var AESEncryptionStream = class extends TransformStream {
27931
27931
  super({
27932
27932
  start() {
27933
27933
  Object.assign(this, {
27934
- ready: new Promise((resolve7) => this.resolveReady = resolve7),
27934
+ ready: new Promise((resolve8) => this.resolveReady = resolve8),
27935
27935
  password: encodePassword(password2, rawPassword),
27936
27936
  strength: encryptionStrength - 1,
27937
27937
  pending: new Uint8Array()
@@ -35929,21 +35929,21 @@ async function registryToCompiled(registry, options = {}) {
35929
35929
  const { minify = true, includeImport = true } = options;
35930
35930
  const themeCss = registryToTailwind(registry, { includeImport });
35931
35931
  const { execFileSync } = await import("child_process");
35932
- const { mkdtempSync, writeFileSync, readFileSync, rmSync } = await import("fs");
35933
- const { join: join15, dirname: dirname5 } = await import("path");
35932
+ const { mkdtempSync, writeFileSync, readFileSync: readFileSync2, rmSync } = await import("fs");
35933
+ const { join: join16, dirname: dirname6 } = await import("path");
35934
35934
  const { createRequire: createRequire2 } = await import("module");
35935
35935
  const require2 = createRequire2(import.meta.url);
35936
35936
  let pkgDir;
35937
35937
  try {
35938
35938
  const pkgJsonPath = require2.resolve("@tailwindcss/cli/package.json");
35939
- pkgDir = dirname5(pkgJsonPath);
35939
+ pkgDir = dirname6(pkgJsonPath);
35940
35940
  } catch {
35941
35941
  throw new Error("Failed to resolve @tailwindcss/cli");
35942
35942
  }
35943
- const binPath = join15(pkgDir, "dist", "index.mjs");
35944
- const tempDir = mkdtempSync(join15(pkgDir, ".tmp-compile-"));
35945
- const tempInput = join15(tempDir, "input.css");
35946
- const tempOutput = join15(tempDir, "output.css");
35943
+ const binPath = join16(pkgDir, "dist", "index.mjs");
35944
+ const tempDir = mkdtempSync(join16(pkgDir, ".tmp-compile-"));
35945
+ const tempInput = join16(tempDir, "input.css");
35946
+ const tempOutput = join16(tempDir, "output.css");
35947
35947
  try {
35948
35948
  writeFileSync(tempInput, themeCss);
35949
35949
  const args = [binPath, "-i", tempInput, "-o", tempOutput];
@@ -35951,7 +35951,7 @@ async function registryToCompiled(registry, options = {}) {
35951
35951
  args.push("--minify");
35952
35952
  }
35953
35953
  execFileSync("node", args, { stdio: "pipe", timeout: 3e4 });
35954
- return readFileSync(tempOutput, "utf-8");
35954
+ return readFileSync2(tempOutput, "utf-8");
35955
35955
  } catch (error3) {
35956
35956
  const message = error3 instanceof Error ? error3.message : String(error3);
35957
35957
  throw new Error(`Failed to compile CSS: ${message}`);
@@ -46867,8 +46867,8 @@ async function maybeOnboardExisting(cwd, importPendingPath) {
46867
46867
  }
46868
46868
 
46869
46869
  // src/commands/mcp.ts
46870
- import { existsSync as existsSync6 } from "fs";
46871
- import { join as join13, resolve as resolve5 } from "path";
46870
+ import { existsSync as existsSync7 } from "fs";
46871
+ import { basename as basename4, join as join14, resolve as resolve6 } from "path";
46872
46872
 
46873
46873
  // src/mcp/server.ts
46874
46874
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -46877,7 +46877,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprot
46877
46877
 
46878
46878
  // src/mcp/tools.ts
46879
46879
  import { readdir as readdir3 } from "fs/promises";
46880
- import { join as join11 } from "path";
46880
+ import { join as join13 } from "path";
46881
46881
 
46882
46882
  // ../composites/src/built-in-rules/email.ts
46883
46883
  var email = external_exports.string().email();
@@ -46989,14 +46989,181 @@ function search(query) {
46989
46989
  }).filter((entry) => entry.score > 0).sort((a2, b2) => b2.score - a2.score).map((entry) => entry.c);
46990
46990
  }
46991
46991
 
46992
+ // src/utils/workspaces.ts
46993
+ import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync, statSync } from "fs";
46994
+ import { basename as basename3, dirname as dirname5, join as join12, resolve as resolve5 } from "path";
46995
+
46996
+ // src/utils/discover.ts
46997
+ import { existsSync as existsSync5 } from "fs";
46998
+ import { dirname as dirname4, join as join11, resolve as resolve4 } from "path";
46999
+ function discoverProjectRoot(startDir) {
47000
+ let current = resolve4(startDir);
47001
+ for (; ; ) {
47002
+ const configPath = join11(current, ".rafters", "config.rafters.json");
47003
+ if (existsSync5(configPath)) {
47004
+ return current;
47005
+ }
47006
+ const parent = dirname4(current);
47007
+ if (parent === current) {
47008
+ return null;
47009
+ }
47010
+ current = parent;
47011
+ }
47012
+ }
47013
+
47014
+ // src/utils/workspaces.ts
47015
+ function findMonorepoRoot(startDir, boundary) {
47016
+ let current = resolve5(startDir);
47017
+ const stopAt = boundary ? resolve5(boundary) : null;
47018
+ for (; ; ) {
47019
+ const pnpmWorkspace = join12(current, "pnpm-workspace.yaml");
47020
+ if (existsSync6(pnpmWorkspace)) {
47021
+ const patterns = parsePnpmWorkspaceYaml(readFileSync(pnpmWorkspace, "utf-8"));
47022
+ if (patterns.length > 0) {
47023
+ return { root: current, patterns };
47024
+ }
47025
+ }
47026
+ const pkgJson = join12(current, "package.json");
47027
+ if (existsSync6(pkgJson)) {
47028
+ const patterns = parsePackageJsonWorkspaces(readFileSync(pkgJson, "utf-8"));
47029
+ if (patterns.length > 0) {
47030
+ return { root: current, patterns };
47031
+ }
47032
+ }
47033
+ if (stopAt && current === stopAt) return null;
47034
+ const parent = dirname5(current);
47035
+ if (parent === current) return null;
47036
+ current = parent;
47037
+ }
47038
+ }
47039
+ function parsePnpmWorkspaceYaml(content) {
47040
+ const lines = content.split("\n");
47041
+ const patterns = [];
47042
+ let inPackages = false;
47043
+ for (const rawLine of lines) {
47044
+ const line = rawLine.replace(/#.*$/, "").trimEnd();
47045
+ if (!line.trim()) continue;
47046
+ if (/^packages\s*:/.test(line)) {
47047
+ inPackages = true;
47048
+ continue;
47049
+ }
47050
+ if (inPackages) {
47051
+ const itemMatch = line.match(/^\s*-\s*(?:["']?)([^"'\s]+)(?:["']?)\s*$/);
47052
+ if (itemMatch?.[1]) {
47053
+ patterns.push(itemMatch[1]);
47054
+ continue;
47055
+ }
47056
+ if (!/^\s/.test(line)) {
47057
+ inPackages = false;
47058
+ }
47059
+ }
47060
+ }
47061
+ return patterns;
47062
+ }
47063
+ function parsePackageJsonWorkspaces(content) {
47064
+ try {
47065
+ const pkg = JSON.parse(content);
47066
+ const ws = pkg.workspaces;
47067
+ if (Array.isArray(ws)) {
47068
+ return ws.filter((p2) => typeof p2 === "string");
47069
+ }
47070
+ if (ws && typeof ws === "object" && Array.isArray(ws.packages)) {
47071
+ return ws.packages.filter((p2) => typeof p2 === "string");
47072
+ }
47073
+ return [];
47074
+ } catch {
47075
+ return [];
47076
+ }
47077
+ }
47078
+ function expandPattern(monorepoRoot, pattern) {
47079
+ const trimmed = pattern.replace(/\/+$/, "");
47080
+ if (trimmed.endsWith("/*")) {
47081
+ const parentRel = trimmed.slice(0, -2);
47082
+ const parent = join12(monorepoRoot, parentRel);
47083
+ if (!existsSync6(parent)) return [];
47084
+ return readdirSync2(parent).map((entry) => join12(parent, entry)).filter((path2) => {
47085
+ try {
47086
+ return statSync(path2).isDirectory();
47087
+ } catch {
47088
+ return false;
47089
+ }
47090
+ });
47091
+ }
47092
+ const literal = join12(monorepoRoot, trimmed);
47093
+ if (existsSync6(literal)) {
47094
+ try {
47095
+ if (statSync(literal).isDirectory()) return [literal];
47096
+ } catch {
47097
+ return [];
47098
+ }
47099
+ }
47100
+ return [];
47101
+ }
47102
+ function discoverWorkspaces(startDir = process.cwd(), options = {}) {
47103
+ const layout = findMonorepoRoot(startDir, options.boundary);
47104
+ if (!layout) {
47105
+ const single = discoverProjectRoot(startDir);
47106
+ if (!single) return [];
47107
+ if (options.boundary && !single.startsWith(resolve5(options.boundary))) return [];
47108
+ return [{ name: basename3(single), root: single }];
47109
+ }
47110
+ const seen = /* @__PURE__ */ new Set();
47111
+ const workspaces = [];
47112
+ for (const pattern of layout.patterns) {
47113
+ for (const dir of expandPattern(layout.root, pattern)) {
47114
+ if (seen.has(dir)) continue;
47115
+ seen.add(dir);
47116
+ const config2 = join12(dir, ".rafters", "config.rafters.json");
47117
+ if (existsSync6(config2)) {
47118
+ workspaces.push({ name: basename3(dir), root: dir });
47119
+ }
47120
+ }
47121
+ }
47122
+ const rootConfig = join12(layout.root, ".rafters", "config.rafters.json");
47123
+ if (existsSync6(rootConfig) && !seen.has(layout.root)) {
47124
+ workspaces.unshift({ name: basename3(layout.root), root: layout.root });
47125
+ }
47126
+ return workspaces;
47127
+ }
47128
+ function pickDefaultWorkspace(workspaces, startDir = process.cwd()) {
47129
+ if (workspaces.length === 0) return null;
47130
+ const cwd = resolve5(startDir);
47131
+ const containing = workspaces.find((ws) => cwd === ws.root || cwd.startsWith(`${ws.root}/`));
47132
+ if (containing) return containing;
47133
+ if (workspaces.length === 1) return workspaces[0] ?? null;
47134
+ return null;
47135
+ }
47136
+ function resolveWorkspace(workspaces, defaultWorkspace, name2) {
47137
+ if (name2) {
47138
+ return workspaces.find((ws) => ws.name === name2) ?? null;
47139
+ }
47140
+ return defaultWorkspace;
47141
+ }
47142
+
46992
47143
  // src/mcp/tools.ts
47144
+ var WORKSPACE_PARAM = {
47145
+ workspace: {
47146
+ type: "string",
47147
+ description: "Workspace name (directory basename). Required when the MCP session has multiple workspaces and none matches cwd. Call rafters_workspaces to list options."
47148
+ }
47149
+ };
46993
47150
  var TOOL_DEFINITIONS = [
47151
+ {
47152
+ name: "rafters_workspaces",
47153
+ description: "List rafters workspaces visible to this MCP session. Returns name, path, and which one is the default for unscoped tool calls. Call this first when the project might be a monorepo.",
47154
+ inputSchema: {
47155
+ type: "object",
47156
+ properties: {},
47157
+ required: []
47158
+ }
47159
+ },
46994
47160
  {
46995
47161
  name: "rafters_composite",
46996
47162
  description: "Query composites by ID, search term, or category. Returns designer intent (solves, appliesWhen, do/never), I/O rules for chaining, and block structure.",
46997
47163
  inputSchema: {
46998
47164
  type: "object",
46999
47165
  properties: {
47166
+ ...WORKSPACE_PARAM,
47000
47167
  id: { type: "string", description: "Get a specific composite by ID" },
47001
47168
  query: { type: "string", description: "Fuzzy search by name/keywords" },
47002
47169
  category: { type: "string", description: "Filter by category" }
@@ -47010,6 +47177,7 @@ var TOOL_DEFINITIONS = [
47010
47177
  inputSchema: {
47011
47178
  type: "object",
47012
47179
  properties: {
47180
+ ...WORKSPACE_PARAM,
47013
47181
  name: { type: "string", description: "Get a specific rule by name" },
47014
47182
  query: { type: "string", description: "Search rules by name/description" },
47015
47183
  create: {
@@ -47032,6 +47200,7 @@ var TOOL_DEFINITIONS = [
47032
47200
  inputSchema: {
47033
47201
  type: "object",
47034
47202
  properties: {
47203
+ ...WORKSPACE_PARAM,
47035
47204
  solves: {
47036
47205
  type: "string",
47037
47206
  description: "What problem the pattern solves (searches composite solves field)"
@@ -47050,6 +47219,7 @@ var TOOL_DEFINITIONS = [
47050
47219
  inputSchema: {
47051
47220
  type: "object",
47052
47221
  properties: {
47222
+ ...WORKSPACE_PARAM,
47053
47223
  name: {
47054
47224
  type: "string",
47055
47225
  description: 'Component name (e.g., "button", "dialog", "card")'
@@ -47060,13 +47230,20 @@ var TOOL_DEFINITIONS = [
47060
47230
  }
47061
47231
  ];
47062
47232
  var RaftersToolHandler = class {
47063
- projectRoot;
47064
- compositesLoaded = false;
47065
- constructor(projectRoot) {
47066
- this.projectRoot = projectRoot;
47233
+ workspaces;
47234
+ defaultWorkspace;
47235
+ /** Tracks per-workspace composite loading so we only read from disk once. */
47236
+ compositesLoadedFor = /* @__PURE__ */ new Set();
47237
+ /** Tracks built-in composite loading separately (loaded once globally). */
47238
+ builtInCompositesLoaded = false;
47239
+ constructor(workspaces, defaultWorkspace) {
47240
+ this.workspaces = workspaces;
47241
+ this.defaultWorkspace = defaultWorkspace;
47067
47242
  }
47068
47243
  async handleToolCall(name2, args) {
47069
47244
  switch (name2) {
47245
+ case "rafters_workspaces":
47246
+ return this.handleWorkspaces();
47070
47247
  case "rafters_composite":
47071
47248
  return this.handleComposite(args);
47072
47249
  case "rafters_rule":
@@ -47076,7 +47253,7 @@ var RaftersToolHandler = class {
47076
47253
  case "rafters_component":
47077
47254
  return this.handleComponent(args.name);
47078
47255
  default: {
47079
- const suggestion = name2 === "rafters_onboard" ? "rafters_onboard was removed. Token import is now a CLI operation: run `rafters init` (auto-detects and prompts) or `rafters import` (standalone)." : "Available tools: rafters_composite, rafters_rule, rafters_pattern, rafters_component.";
47256
+ const suggestion = name2 === "rafters_onboard" ? "rafters_onboard was removed. Token import is now a CLI operation: run `rafters init` (auto-detects and prompts) or `rafters import` (standalone)." : "Available tools: rafters_workspaces, rafters_composite, rafters_rule, rafters_pattern, rafters_component.";
47080
47257
  return {
47081
47258
  content: [
47082
47259
  { type: "text", text: JSON.stringify({ error: `Unknown tool: ${name2}`, suggestion }) }
@@ -47085,6 +47262,54 @@ var RaftersToolHandler = class {
47085
47262
  }
47086
47263
  }
47087
47264
  }
47265
+ /**
47266
+ * Resolve the requested workspace, returning a structured error result when
47267
+ * the agent didn't pick one and there's no default. Returns the default
47268
+ * workspace (which may itself be null when no `.rafters/` exists at all)
47269
+ * for tools that read agent-shipped data and can degrade gracefully.
47270
+ */
47271
+ resolve(name2) {
47272
+ return resolveWorkspace(this.workspaces, this.defaultWorkspace, name2);
47273
+ }
47274
+ /**
47275
+ * Build a structured error response listing the available workspaces.
47276
+ * Use this when a tool requires a workspace and the agent didn't pick one.
47277
+ */
47278
+ workspaceRequiredError() {
47279
+ return {
47280
+ content: [
47281
+ {
47282
+ type: "text",
47283
+ text: JSON.stringify({
47284
+ error: "workspace parameter required",
47285
+ suggestion: "Multiple workspaces are available. Pass `workspace` with one of the names below.",
47286
+ workspaces: this.workspaces.map((w) => ({ name: w.name, root: w.root }))
47287
+ })
47288
+ }
47289
+ ]
47290
+ };
47291
+ }
47292
+ async handleWorkspaces() {
47293
+ return {
47294
+ content: [
47295
+ {
47296
+ type: "text",
47297
+ text: JSON.stringify(
47298
+ {
47299
+ workspaces: this.workspaces.map((w) => ({
47300
+ name: w.name,
47301
+ root: w.root,
47302
+ isDefault: w.name === this.defaultWorkspace?.name
47303
+ })),
47304
+ defaultWorkspace: this.defaultWorkspace?.name ?? null
47305
+ },
47306
+ null,
47307
+ 2
47308
+ )
47309
+ }
47310
+ ]
47311
+ };
47312
+ }
47088
47313
  async loadCompositesFromDir(dir) {
47089
47314
  try {
47090
47315
  const entries = await readdir3(dir);
@@ -47092,7 +47317,7 @@ var RaftersToolHandler = class {
47092
47317
  for (const file of files) {
47093
47318
  try {
47094
47319
  const { readFile: readFile9 } = await import("fs/promises");
47095
- const raw = await readFile9(join11(dir, file), "utf-8");
47320
+ const raw = await readFile9(join13(dir, file), "utf-8");
47096
47321
  const parsed = JSON.parse(raw);
47097
47322
  const result = CompositeFileSchema.safeParse(parsed);
47098
47323
  if (result.success) {
@@ -47107,23 +47332,29 @@ var RaftersToolHandler = class {
47107
47332
  } catch {
47108
47333
  }
47109
47334
  }
47110
- async ensureCompositesLoaded() {
47111
- if (this.compositesLoaded) return;
47112
- const builtInDirs = ["typography"];
47113
- for (const dir of builtInDirs) {
47114
- await this.loadCompositesFromDir(
47115
- join11(process.cwd(), "node_modules/@rafters/composites/src", dir)
47116
- );
47335
+ async ensureCompositesLoaded(workspace) {
47336
+ if (!this.builtInCompositesLoaded) {
47337
+ const builtInDirs = ["typography"];
47338
+ for (const dir of builtInDirs) {
47339
+ await this.loadCompositesFromDir(
47340
+ join13(process.cwd(), "node_modules/@rafters/composites/src", dir)
47341
+ );
47342
+ }
47343
+ this.builtInCompositesLoaded = true;
47117
47344
  }
47118
- if (this.projectRoot) {
47119
- const paths = getRaftersPaths(this.projectRoot);
47120
- await this.loadCompositesFromDir(join11(paths.root, "composites"));
47345
+ if (workspace && !this.compositesLoadedFor.has(workspace.root)) {
47346
+ const paths = getRaftersPaths(workspace.root);
47347
+ await this.loadCompositesFromDir(join13(paths.root, "composites"));
47348
+ this.compositesLoadedFor.add(workspace.root);
47121
47349
  }
47122
- this.compositesLoaded = true;
47123
47350
  }
47124
47351
  async handleComposite(args) {
47125
- await this.ensureCompositesLoaded();
47126
- const { id, query, category } = args;
47352
+ const { id, query, category, workspace } = args;
47353
+ const resolved = this.resolve(workspace);
47354
+ if (workspace && !resolved) {
47355
+ return this.workspaceRequiredError();
47356
+ }
47357
+ await this.ensureCompositesLoaded(resolved);
47127
47358
  let composites2;
47128
47359
  if (id) {
47129
47360
  const c4 = get2(id);
@@ -47190,8 +47421,12 @@ var RaftersToolHandler = class {
47190
47421
  return { content: [{ type: "text", text: JSON.stringify({ rules }, null, 2) }] };
47191
47422
  }
47192
47423
  async handlePattern(args) {
47193
- await this.ensureCompositesLoaded();
47194
- const { solves, query } = args;
47424
+ const { solves, query, workspace } = args;
47425
+ const resolved = this.resolve(workspace);
47426
+ if (workspace && !resolved) {
47427
+ return this.workspaceRequiredError();
47428
+ }
47429
+ await this.ensureCompositesLoaded(resolved);
47195
47430
  let composites2;
47196
47431
  if (solves) {
47197
47432
  const all = getAll2();
@@ -47268,8 +47503,8 @@ var RaftersToolHandler = class {
47268
47503
  };
47269
47504
 
47270
47505
  // src/mcp/server.ts
47271
- async function startMcpServer(projectRoot) {
47272
- const toolHandler = new RaftersToolHandler(projectRoot);
47506
+ async function startMcpServer(workspaces, defaultWorkspace) {
47507
+ const toolHandler = new RaftersToolHandler(workspaces, defaultWorkspace);
47273
47508
  const server = new Server(
47274
47509
  {
47275
47510
  name: "rafters",
@@ -47309,52 +47544,39 @@ async function startMcpServer(projectRoot) {
47309
47544
  });
47310
47545
  }
47311
47546
 
47312
- // src/utils/discover.ts
47313
- import { existsSync as existsSync5 } from "fs";
47314
- import { dirname as dirname4, join as join12, resolve as resolve4 } from "path";
47315
- function discoverProjectRoot(startDir) {
47316
- let current = resolve4(startDir);
47317
- for (; ; ) {
47318
- const configPath = join12(current, ".rafters", "config.rafters.json");
47319
- if (existsSync5(configPath)) {
47320
- return current;
47321
- }
47322
- const parent = dirname4(current);
47323
- if (parent === current) {
47324
- return null;
47325
- }
47326
- current = parent;
47327
- }
47328
- }
47329
-
47330
47547
  // src/commands/mcp.ts
47331
47548
  async function mcp(options) {
47332
- let projectRoot;
47549
+ let workspaces;
47550
+ let defaultWorkspace;
47333
47551
  if (options.projectRoot) {
47334
- const explicit = resolve5(options.projectRoot);
47335
- const configPath = join13(explicit, ".rafters", "config.rafters.json");
47336
- if (!existsSync6(configPath)) {
47552
+ const explicit = resolve6(options.projectRoot);
47553
+ const configPath = join14(explicit, ".rafters", "config.rafters.json");
47554
+ if (!existsSync7(configPath)) {
47337
47555
  process.stderr.write(
47338
47556
  `--project-root ${explicit} does not contain .rafters/config.rafters.json
47339
47557
  `
47340
47558
  );
47341
- projectRoot = null;
47559
+ workspaces = [];
47560
+ defaultWorkspace = null;
47342
47561
  } else {
47343
- projectRoot = explicit;
47562
+ const ws = { name: basename4(explicit), root: explicit };
47563
+ workspaces = [ws];
47564
+ defaultWorkspace = ws;
47344
47565
  }
47345
47566
  } else {
47346
- projectRoot = discoverProjectRoot(process.cwd());
47567
+ workspaces = discoverWorkspaces(process.cwd());
47568
+ defaultWorkspace = pickDefaultWorkspace(workspaces, process.cwd());
47347
47569
  }
47348
- await startMcpServer(projectRoot);
47570
+ await startMcpServer(workspaces, defaultWorkspace);
47349
47571
  }
47350
47572
 
47351
47573
  // src/commands/studio.ts
47352
- import { existsSync as existsSync7 } from "fs";
47353
- import { resolve as resolve6 } from "path";
47574
+ import { existsSync as existsSync8 } from "fs";
47575
+ import { resolve as resolve7 } from "path";
47354
47576
 
47355
47577
  // ../studio/src/api/vite-plugin.ts
47356
47578
  import { writeFile as writeFile5 } from "fs/promises";
47357
- import { join as join14 } from "path";
47579
+ import { join as join15 } from "path";
47358
47580
  var TokenResponseSchema = external_exports.object({
47359
47581
  ok: external_exports.literal(true),
47360
47582
  token: TokenSchema
@@ -47378,7 +47600,7 @@ var ColorBuildOptionsSchema = external_exports.object({
47378
47600
  states: external_exports.record(external_exports.string(), external_exports.string()).optional()
47379
47601
  });
47380
47602
  var projectPath = process.env.RAFTERS_PROJECT_PATH || process.cwd();
47381
- var outputPath = join14(projectPath, ".rafters", "output", "rafters.vars.css");
47603
+ var outputPath = join15(projectPath, ".rafters", "output", "rafters.vars.css");
47382
47604
  var SetTokenMessageSchema = external_exports.object({
47383
47605
  name: external_exports.string().min(1),
47384
47606
  value: external_exports.union([external_exports.string(), ColorValueSchema, ColorReferenceSchema]),
@@ -47497,7 +47719,7 @@ function getNamespacePatchSchema(namespace) {
47497
47719
  }
47498
47720
  var MAX_BODY_SIZE = 1024 * 1024;
47499
47721
  function readJsonBody(req) {
47500
- return new Promise((resolve7, reject) => {
47722
+ return new Promise((resolve8, reject) => {
47501
47723
  let body = "";
47502
47724
  let size = 0;
47503
47725
  req.on("data", (chunk) => {
@@ -47510,7 +47732,7 @@ function readJsonBody(req) {
47510
47732
  });
47511
47733
  req.on("end", () => {
47512
47734
  try {
47513
- resolve7(body ? JSON.parse(body) : {});
47735
+ resolve8(body ? JSON.parse(body) : {});
47514
47736
  } catch {
47515
47737
  reject(new Error("Invalid JSON body"));
47516
47738
  }
@@ -47963,7 +48185,7 @@ import { createServer } from "vite";
47963
48185
  async function studio() {
47964
48186
  const cwd = process.cwd();
47965
48187
  const paths = getRaftersPaths(cwd);
47966
- if (!existsSync7(paths.root)) {
48188
+ if (!existsSync8(paths.root)) {
47967
48189
  console.error('No .rafters/ directory found. Run "rafters init" first.');
47968
48190
  process.exit(1);
47969
48191
  }
@@ -47980,7 +48202,7 @@ async function studio() {
47980
48202
  },
47981
48203
  resolve: {
47982
48204
  alias: {
47983
- "@rafters-output": resolve6(cwd, ".rafters", "output")
48205
+ "@rafters-output": resolve7(cwd, ".rafters", "output")
47984
48206
  }
47985
48207
  }
47986
48208
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rafters",
3
- "version": "0.0.49",
3
+ "version": "0.0.52",
4
4
  "description": "CLI for Rafters design system - scaffold tokens and add components",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -33,7 +33,6 @@
33
33
  "@antfu/ni": "^28.1.0",
34
34
  "@inquirer/prompts": "^8.2.0",
35
35
  "@modelcontextprotocol/sdk": "^1.25.1",
36
- "@rafters/composites": "workspace:*",
37
36
  "commander": "^13.0.0",
38
37
  "css-tree": "^3.2.1",
39
38
  "execa": "^9.6.1",
@@ -43,6 +42,7 @@
43
42
  "devDependencies": {
44
43
  "@playwright/test": "catalog:",
45
44
  "@rafters/color-utils": "workspace:*",
45
+ "@rafters/composites": "workspace:*",
46
46
  "@rafters/design-tokens": "workspace:*",
47
47
  "@rafters/shared": "workspace:*",
48
48
  "@rafters/studio": "workspace:*",