onsystem 0.4.0 → 0.5.0

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.
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { fileURLToPath, pathToFileURL } from "node:url";
3
+ import { pathToFileURL } from "node:url";
4
4
  import * as z from "zod";
5
5
  import { escapePath, glob, globSync } from "tinyglobby";
6
6
  import ts from "typescript";
@@ -10,8 +10,6 @@ import fs$1 from "node:fs/promises";
10
10
  import os from "node:os";
11
11
  import postcss from "postcss";
12
12
  import { stemmer } from "stemmer";
13
- import { McpServer, ResourceTemplate } from "@modelcontextprotocol/server";
14
- import { serveStdio } from "@modelcontextprotocol/server/stdio";
15
13
  //#region src/extract/program.ts
16
14
  const DEFAULT_OPTIONS = {
17
15
  target: ts.ScriptTarget.ES2022,
@@ -756,7 +754,7 @@ function fromComponentsJson(root, dir, json, project, packages, note = ignore) {
756
754
  let uiDir = resolveThroughPaths(ui, project);
757
755
  let imports = [];
758
756
  if (!uiDir) {
759
- const name = packageName$1(ui);
757
+ const name = packageName(ui);
760
758
  const pkgDir = name ? packages.find(name) : void 0;
761
759
  const pkg = pkgDir ? readJson(path.join(pkgDir, "package.json")) : void 0;
762
760
  if (!name || !pkgDir || !pkg) {
@@ -1027,7 +1025,7 @@ function resolveThroughPaths(specifier, project) {
1027
1025
  const middle = star === -1 ? "" : specifier.slice(prefix.length, specifier.length - suffix.length);
1028
1026
  for (const target of targets) {
1029
1027
  const dir = path.resolve(project.pathsBase, target.replace("*", middle));
1030
- if (isDirectory$2(dir)) return dir;
1028
+ if (isDirectory$1(dir)) return dir;
1031
1029
  }
1032
1030
  }
1033
1031
  }
@@ -1038,16 +1036,16 @@ function resolveThroughExports(specifier, imports, root) {
1038
1036
  if (star === -1) continue;
1039
1037
  if (specifier !== mapping.specifier.slice(0, star).replace(/\/$/, "")) continue;
1040
1038
  const dir = path.resolve(root, mapping.target.slice(0, mapping.target.indexOf("*")));
1041
- if (isDirectory$2(dir)) return dir;
1039
+ if (isDirectory$1(dir)) return dir;
1042
1040
  }
1043
1041
  }
1044
1042
  /** `/components` under a package: the folder itself, or under `src`. */
1045
1043
  function subpathDir(pkgDir, subpath) {
1046
1044
  const rel = subpath.replace(/^\//, "");
1047
- return [path.join(pkgDir, rel), path.join(pkgDir, "src", rel)].find(isDirectory$2);
1045
+ return [path.join(pkgDir, rel), path.join(pkgDir, "src", rel)].find(isDirectory$1);
1048
1046
  }
1049
1047
  /** `@scope/name/sub` → `@scope/name`; `name/sub` → `name`; aliases such as `@/x` and `~/x` → undefined. */
1050
- function packageName$1(specifier) {
1048
+ function packageName(specifier) {
1051
1049
  const [first = "", second] = specifier.split("/");
1052
1050
  if (first.startsWith("@")) return first.length > 1 && second ? `${first}/${second}` : void 0;
1053
1051
  return /^[a-z0-9]/i.test(first) ? first : void 0;
@@ -1062,7 +1060,7 @@ function moduleResolver(root) {
1062
1060
  const packages = new PackageFinder(root);
1063
1061
  return (specifier, fromFile) => {
1064
1062
  if (specifier.startsWith(".")) return moduleFile(path.resolve(path.dirname(fromFile), specifier));
1065
- const name = packageName$1(specifier);
1063
+ const name = packageName(specifier);
1066
1064
  const dir = name ? packages.find(name) : void 0;
1067
1065
  if (!name || !dir) return void 0;
1068
1066
  const subpath = specifier.slice(name.length + 1);
@@ -1083,7 +1081,7 @@ function workspaceModule(root) {
1083
1081
  return (specifier, fromFile) => {
1084
1082
  const file = resolve(specifier, fromFile);
1085
1083
  if (file) return { file };
1086
- const name = packageName$1(specifier);
1084
+ const name = packageName(specifier);
1087
1085
  const dir = name ? packages.find(name) : void 0;
1088
1086
  if (!name || !dir) return void 0;
1089
1087
  const source = moduleFile(path.join(dir, "src", specifier.slice(name.length + 1) || "index"));
@@ -1249,7 +1247,7 @@ function isFile(file) {
1249
1247
  return false;
1250
1248
  }
1251
1249
  }
1252
- function isDirectory$2(dir) {
1250
+ function isDirectory$1(dir) {
1253
1251
  try {
1254
1252
  return fs.statSync(dir).isDirectory();
1255
1253
  } catch {
@@ -1841,13 +1839,14 @@ async function loadConfig(options = {}) {
1841
1839
  let raw = {};
1842
1840
  if (configFile) {
1843
1841
  if (!fs.existsSync(configFile)) throw new ConfigError(`Config file not found: ${configFile}`);
1842
+ if (options.jsonConfigOnly && !configFile.endsWith(".json")) throw new ConfigError(`${configFile} was not run: the server runs a JavaScript or TypeScript config only in a project it was started in (its working directory, --root or the client's roots), not in one found from the path a tool call names. Start the server in the project, or use onsystem.config.json.`);
1844
1843
  raw = await readConfigFile(configFile);
1845
1844
  }
1846
1845
  const parsed = configSchema.safeParse(raw);
1847
1846
  if (!parsed.success) throw new ConfigError(`Invalid config in ${configFile ?? "(defaults)"}:\n${z.prettifyError(parsed.error)}`);
1848
1847
  const config = parsed.data;
1849
1848
  const root = options.root ? path.resolve(cwd, options.root) : configFile ? path.dirname(configFile) : cwd;
1850
- if (!isDirectory$1(root)) throw new ConfigError(`Project root not found: ${root}`);
1849
+ if (!isDirectory(root)) throw new ConfigError(`Project root not found: ${root}`);
1851
1850
  const base = options.base ? path.resolve(cwd, options.base) : root;
1852
1851
  const inherited = base !== root;
1853
1852
  const fromBase = (p) => inherited ? toPosix(path.relative(root, path.resolve(base, slashGlob(p)))) || "." : p;
@@ -1963,7 +1962,7 @@ async function readConfigFile(file) {
1963
1962
  throw new ConfigError(`Could not load ${file}: ${message}.${message.includes("@dgesteves/design-system-mcp") ? " The package is now onsystem: import { defineConfig } from 'onsystem'." : /\.m?ts$/.test(file) ? " TypeScript configs need Node.js 22.18+ (type stripping); use JSON or .mjs otherwise." : ""}`);
1964
1963
  }
1965
1964
  }
1966
- function isDirectory$1(dir) {
1965
+ function isDirectory(dir) {
1967
1966
  try {
1968
1967
  return fs.statSync(dir).isDirectory();
1969
1968
  } catch {
@@ -3937,7 +3936,7 @@ function parseDtcgTokens(text, file, options = {}) {
3937
3936
  warnings: [`${file}: invalid JSON (${error.message})`]
3938
3937
  };
3939
3938
  }
3940
- if (!isObject(json)) return {
3939
+ if (!isObject$1(json)) return {
3941
3940
  tokens: [],
3942
3941
  warnings: [`${file}: expected a JSON object`]
3943
3942
  };
@@ -3963,7 +3962,7 @@ function parseDtcgTokens(text, file, options = {}) {
3963
3962
  return value;
3964
3963
  }
3965
3964
  if (Array.isArray(value)) return value.map((v) => resolve(v, seen));
3966
- if (isObject(value)) {
3965
+ if (isObject$1(value)) {
3967
3966
  const ref = value.$ref;
3968
3967
  if (typeof ref === "string" && Object.keys(value).length === 1) {
3969
3968
  const pointer = ref.replace(/^#\//, "").split("/").filter((p) => p !== "$value");
@@ -4028,16 +4027,16 @@ function walk(node, path, inheritedType, out, lines) {
4028
4027
  return;
4029
4028
  }
4030
4029
  for (const [key, child] of Object.entries(node)) {
4031
- if (key.startsWith("$") || !isObject(child)) continue;
4030
+ if (key.startsWith("$") || !isObject$1(child)) continue;
4032
4031
  walk(child, [...path, key], type, out, lines);
4033
4032
  }
4034
4033
  }
4035
4034
  /** Modes are not standardised yet; accept the common `$extensions.mode(s)` shape. */
4036
4035
  function modesOf(extensions) {
4037
- if (!isObject(extensions)) return void 0;
4036
+ if (!isObject$1(extensions)) return void 0;
4038
4037
  for (const key of ["modes", "mode"]) {
4039
4038
  const value = extensions[key];
4040
- if (isObject(value)) return value;
4039
+ if (isObject$1(value)) return value;
4041
4040
  }
4042
4041
  }
4043
4042
  function categorize(type, path) {
@@ -4105,7 +4104,7 @@ function pct255(component) {
4105
4104
  if (component === void 0 || component === "none") return "none";
4106
4105
  return String(Math.round(Number(component) * 255));
4107
4106
  }
4108
- function isObject(value) {
4107
+ function isObject$1(value) {
4109
4108
  return typeof value === "object" && value !== null && !Array.isArray(value);
4110
4109
  }
4111
4110
  /**
@@ -5441,7 +5440,7 @@ const preferDesignSystemComponent = {
5441
5440
  if (!component) continue;
5442
5441
  if (hiddenFromUser(element)) continue;
5443
5442
  if (styledByParent(element, context)) continue;
5444
- if (isBare$1(element)) continue;
5443
+ if (isBare(element)) continue;
5445
5444
  if (styledByOwnVariants(element, context)) continue;
5446
5445
  if (enclosingComponent(element.node) === component.name) continue;
5447
5446
  const dropIn = context.target.isDropIn(component, key);
@@ -5492,7 +5491,7 @@ function styledByOwnVariants(element, context) {
5492
5491
  * `className` or `style`: an element type to render into, not styled UI. Void elements
5493
5492
  * (`<input />`, `<img />`) are always self-closing and stay UI.
5494
5493
  */
5495
- function isBare$1(element) {
5494
+ function isBare(element) {
5496
5495
  return [
5497
5496
  "button",
5498
5497
  "a",
@@ -6449,16 +6448,27 @@ function lengthReplacement(context, category, px, prefix) {
6449
6448
  tailwind: false
6450
6449
  };
6451
6450
  }
6452
- const noHardcodedSpacing = lengthRule("no-hardcoded-spacing", "Padding, margin and gap must use the spacing scale, not arbitrary px/rem values.", {
6453
- category: "spacing",
6454
- prefixes: SPACING_PREFIXES,
6455
- styleProperties: STYLE_SPACING_PROPERTIES
6456
- });
6457
- const noHardcodedRadius = lengthRule("no-hardcoded-radius", "Border radius must use radius tokens, not arbitrary values.", {
6458
- category: "radius",
6459
- prefixes: RADIUS_PREFIXES,
6460
- styleProperties: STYLE_RADIUS_PROPERTIES
6461
- });
6451
+ //#endregion
6452
+ //#region src/lint/rules/index.ts
6453
+ /** Every rule, in the order they run. */
6454
+ const RULES = [
6455
+ noHardcodedColor,
6456
+ lengthRule("no-hardcoded-spacing", "Padding, margin and gap must use the spacing scale, not arbitrary px/rem values.", {
6457
+ category: "spacing",
6458
+ prefixes: SPACING_PREFIXES,
6459
+ styleProperties: STYLE_SPACING_PROPERTIES
6460
+ }),
6461
+ lengthRule("no-hardcoded-radius", "Border radius must use radius tokens, not arbitrary values.", {
6462
+ category: "radius",
6463
+ prefixes: RADIUS_PREFIXES,
6464
+ styleProperties: STYLE_RADIUS_PROPERTIES
6465
+ }),
6466
+ preferDesignSystemComponent,
6467
+ noUnknownComponent,
6468
+ noUnknownProp,
6469
+ noUnknownVariant,
6470
+ iconButtonAccessibleName
6471
+ ];
6462
6472
  //#endregion
6463
6473
  //#region src/lint/suppress.ts
6464
6474
  /**
@@ -6536,6 +6546,265 @@ function insideLiteral(sourceFile, offset) {
6536
6546
  return ts.isStringLiteralLike(node) || ts.isTemplateLiteralToken(node) || ts.isJsxText(node) || ts.isRegularExpressionLiteral(node);
6537
6547
  }
6538
6548
  //#endregion
6549
+ //#region src/baseline.ts
6550
+ const BASELINE_FILE = "onsystem.baseline.json";
6551
+ /** The baseline's name from before the rename to onsystem: still read when the new one is absent. */
6552
+ const LEGACY_BASELINE_FILE = "design-system-mcp.baseline.json";
6553
+ /**
6554
+ * A finding's identity within its file and rule: the offending text with
6555
+ * whitespace collapsed, and without the quotes of a string literal, so a
6556
+ * formatter switching quote style does not make every finding look new.
6557
+ * SARIF fingerprints use it too, so code scanning tracks a finding as a baseline does.
6558
+ */
6559
+ function findingKey(diagnostic) {
6560
+ const text = diagnostic.source.replace(/\s+/g, " ").trim();
6561
+ return (/^(['"`])(.*)\1$/.exec(text)?.[2] ?? text).slice(0, 200);
6562
+ }
6563
+ /** The baseline's entries, or undefined when the file does not exist. */
6564
+ function readBaseline(file) {
6565
+ let text;
6566
+ try {
6567
+ text = fs.readFileSync(file, "utf8");
6568
+ } catch (error) {
6569
+ if (error.code === "ENOENT") return void 0;
6570
+ throw new ConfigError(`Could not read baseline ${file}: ${error.message}`);
6571
+ }
6572
+ const invalid = (why) => new ConfigError(`${file} is not a valid onsystem baseline (${why}). Fix it, or delete it and run check --update-baseline.`);
6573
+ let parsed;
6574
+ try {
6575
+ parsed = JSON.parse(text);
6576
+ } catch (error) {
6577
+ throw invalid(error.message);
6578
+ }
6579
+ if (!isObject(parsed) || parsed.version !== 1) throw invalid("expected \"version\": 1");
6580
+ if (!isObject(parsed.files)) throw invalid("\"files\" must be an object");
6581
+ const entries = /* @__PURE__ */ new Map();
6582
+ for (const [name, rules] of Object.entries(parsed.files)) {
6583
+ if (!isObject(rules)) throw invalid(`"${name}" must map rules to findings`);
6584
+ const byRule = /* @__PURE__ */ new Map();
6585
+ for (const [rule, sources] of Object.entries(rules)) {
6586
+ if (!isObject(sources)) throw invalid(`"${name}" › "${rule}" must map findings to counts`);
6587
+ const counts = /* @__PURE__ */ new Map();
6588
+ for (const [source, count] of Object.entries(sources)) {
6589
+ if (!Number.isInteger(count) || count < 1) throw invalid(`"${name}" › "${rule}" › "${source}" must be a positive whole number`);
6590
+ counts.set(source, count);
6591
+ }
6592
+ byRule.set(rule, counts);
6593
+ }
6594
+ entries.set(name, byRule);
6595
+ }
6596
+ return entries;
6597
+ }
6598
+ /**
6599
+ * Removes the findings the baseline accepts for `file`. Within a file and
6600
+ * rule, each entry accepts that many findings with the same source text, in
6601
+ * order; any beyond the count are new. Entries of `disabled` rules are not
6602
+ * counted as fixed: the rule is off, not satisfied.
6603
+ */
6604
+ function applyBaseline(baseline, file, result, disabled = /* @__PURE__ */ new Set()) {
6605
+ const remaining = /* @__PURE__ */ new Map();
6606
+ for (const [rule, sources] of baseline.get(file) ?? []) {
6607
+ if (disabled.has(rule)) continue;
6608
+ for (const [source, count] of sources) remaining.set(`${rule}\0${source}`, count);
6609
+ }
6610
+ const diagnostics = [];
6611
+ let baselined = 0;
6612
+ for (const diagnostic of result.diagnostics) {
6613
+ const id = `${diagnostic.ruleId}\0${findingKey(diagnostic)}`;
6614
+ const left = remaining.get(id) ?? 0;
6615
+ if (left > 0) {
6616
+ remaining.set(id, left - 1);
6617
+ baselined++;
6618
+ } else diagnostics.push(diagnostic);
6619
+ }
6620
+ let fixed = 0;
6621
+ for (const count of remaining.values()) fixed += count;
6622
+ return {
6623
+ result: {
6624
+ ...result,
6625
+ diagnostics,
6626
+ errorCount: diagnostics.filter((d) => d.severity === "error").length,
6627
+ warningCount: diagnostics.filter((d) => d.severity === "warning").length
6628
+ },
6629
+ baselined,
6630
+ fixed
6631
+ };
6632
+ }
6633
+ /**
6634
+ * The baseline after a run over `checked` (file → its findings): those files
6635
+ * get exactly their current findings, plus their previous entries for
6636
+ * `disabled` rules; other files keep their entries while they still exist
6637
+ * under `root`.
6638
+ */
6639
+ function updateBaseline(previous, checked, root, disabled = /* @__PURE__ */ new Set()) {
6640
+ const disabledIn = typeof disabled === "function" ? disabled : () => disabled;
6641
+ const next = /* @__PURE__ */ new Map();
6642
+ for (const [file, rules] of previous ?? []) if (!checked.has(file) && fs.existsSync(path.resolve(root, file))) next.set(file, rules);
6643
+ for (const [file, diagnostics] of checked) {
6644
+ const rules = /* @__PURE__ */ new Map();
6645
+ const off = disabledIn(file);
6646
+ for (const [rule, sources] of previous?.get(file) ?? []) if (off.has(rule)) rules.set(rule, sources);
6647
+ for (const diagnostic of diagnostics) {
6648
+ let sources = rules.get(diagnostic.ruleId);
6649
+ if (!sources) rules.set(diagnostic.ruleId, sources = /* @__PURE__ */ new Map());
6650
+ const id = findingKey(diagnostic);
6651
+ sources.set(id, (sources.get(id) ?? 0) + 1);
6652
+ }
6653
+ if (rules.size) next.set(file, rules);
6654
+ }
6655
+ return next;
6656
+ }
6657
+ function countBaseline(entries) {
6658
+ let findings = 0;
6659
+ for (const rules of entries.values()) for (const sources of rules.values()) for (const count of sources.values()) findings += count;
6660
+ return {
6661
+ findings,
6662
+ files: entries.size
6663
+ };
6664
+ }
6665
+ /** Writes the baseline sorted at every level, so it diffs cleanly in review. */
6666
+ function writeBaseline(file, entries) {
6667
+ const sorted = (map) => [...map].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
6668
+ const files = {};
6669
+ for (const [name, rules] of sorted(entries)) {
6670
+ const byRule = {};
6671
+ for (const [rule, sources] of sorted(rules)) byRule[rule] = Object.fromEntries(sorted(sources));
6672
+ Object.defineProperty(files, name, {
6673
+ value: byRule,
6674
+ enumerable: true,
6675
+ writable: true
6676
+ });
6677
+ }
6678
+ const baseline = {
6679
+ version: 1,
6680
+ files
6681
+ };
6682
+ try {
6683
+ fs.writeFileSync(file, `${JSON.stringify(baseline, null, 2)}\n`);
6684
+ } catch (error) {
6685
+ throw new ConfigError(`Could not write baseline ${file}: ${error.message}`);
6686
+ }
6687
+ }
6688
+ function isObject(value) {
6689
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6690
+ }
6691
+ //#endregion
6692
+ //#region package.json
6693
+ var name = "onsystem";
6694
+ //#endregion
6695
+ //#region src/version.ts
6696
+ const VERSION = "0.5.0";
6697
+ const NAME = name;
6698
+ //#endregion
6699
+ //#region src/lint/sarif.ts
6700
+ const SCHEMA = "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json";
6701
+ const REPO = "https://github.com/dgesteves/onsystem";
6702
+ const RULES_PAGE = "https://onsystem.vercel.app/rules";
6703
+ /** The rules, and the two kinds of finding that are not a rule of their own. */
6704
+ function descriptors() {
6705
+ return [
6706
+ ...RULES.map((rule) => ({
6707
+ id: rule.id,
6708
+ description: rule.description,
6709
+ level: DEFAULT_SEVERITY[rule.id] === "warn" ? "warning" : "error",
6710
+ anchor: rule.id
6711
+ })),
6712
+ {
6713
+ id: "syntax",
6714
+ description: "The file must parse. A syntax error, or code nested too deeply to parse, makes the other findings in the file unreliable.",
6715
+ level: "error",
6716
+ anchor: "fixes"
6717
+ },
6718
+ {
6719
+ id: "suppression",
6720
+ description: "Suppression comments must name known rules: a misspelled rule name suppresses nothing.",
6721
+ level: "warning",
6722
+ anchor: "suppressing-findings"
6723
+ }
6724
+ ];
6725
+ }
6726
+ /** A path relative to the repository root as a URI reference: `app/blog/%5Bslug%5D/page.tsx`. */
6727
+ function toUri(file) {
6728
+ return file.replaceAll("\\", "/").split("/").map((segment) => segment === "." || segment === ".." ? segment : encodeURIComponent(segment)).join("/");
6729
+ }
6730
+ /**
6731
+ * Check results as a SARIF 2.1.0 log, for GitHub code scanning
6732
+ * (`github/codeql-action/upload-sarif`) and other SARIF consumers.
6733
+ *
6734
+ * `uri` maps a result's file to its path relative to the repository root, which code scanning
6735
+ * resolves locations against. Each result carries a fingerprint built like a baseline entry
6736
+ * (file, rule, the offending text and its occurrence), so an alert keeps its identity when
6737
+ * lines above it move.
6738
+ */
6739
+ function formatSarif(results, uri = (file) => file) {
6740
+ const rules = descriptors();
6741
+ const index = new Map(rules.map((rule, i) => [rule.id, i]));
6742
+ const sarifResults = results.flatMap((result) => {
6743
+ const location = toUri(uri(result.file));
6744
+ const seen = /* @__PURE__ */ new Map();
6745
+ return result.diagnostics.map((d) => {
6746
+ const identity = `${d.ruleId}\0${findingKey(d)}`;
6747
+ const occurrence = seen.get(identity) ?? 0;
6748
+ seen.set(identity, occurrence + 1);
6749
+ return sarifResult(d, location, index.get(d.ruleId), fingerprint$1(location, identity, occurrence));
6750
+ });
6751
+ });
6752
+ const log = {
6753
+ $schema: SCHEMA,
6754
+ version: "2.1.0",
6755
+ runs: [{
6756
+ tool: { driver: {
6757
+ name: NAME,
6758
+ version: VERSION,
6759
+ semanticVersion: VERSION,
6760
+ informationUri: REPO,
6761
+ rules: rules.map((rule) => {
6762
+ const helpUri = `${RULES_PAGE}#${rule.anchor}`;
6763
+ return {
6764
+ id: rule.id,
6765
+ shortDescription: { text: rule.description },
6766
+ fullDescription: { text: rule.description },
6767
+ helpUri,
6768
+ help: {
6769
+ text: `${rule.description} See ${helpUri}`,
6770
+ markdown: `${rule.description}\n\n[${rule.id} in the rules catalog](${helpUri})`
6771
+ },
6772
+ defaultConfiguration: { level: rule.level }
6773
+ };
6774
+ })
6775
+ } },
6776
+ columnKind: "utf16CodeUnits",
6777
+ results: sarifResults
6778
+ }]
6779
+ };
6780
+ return JSON.stringify(log, null, 2);
6781
+ }
6782
+ function sarifResult(d, uri, ruleIndex, fingerprint) {
6783
+ return {
6784
+ ruleId: d.ruleId,
6785
+ ...ruleIndex === void 0 ? {} : { ruleIndex },
6786
+ level: d.severity,
6787
+ message: { text: d.message },
6788
+ locations: [{ physicalLocation: {
6789
+ artifactLocation: {
6790
+ uri,
6791
+ uriBaseId: "%SRCROOT%"
6792
+ },
6793
+ region: {
6794
+ startLine: d.line,
6795
+ startColumn: d.column,
6796
+ endLine: d.endLine,
6797
+ endColumn: d.endColumn,
6798
+ ...d.source ? { snippet: { text: d.source } } : {}
6799
+ }
6800
+ } }],
6801
+ partialFingerprints: { [`${NAME}/v1`]: fingerprint }
6802
+ };
6803
+ }
6804
+ function fingerprint$1(uri, identity, occurrence) {
6805
+ return createHash("sha256").update(`${uri}\0${identity}\0${String(occurrence)}`).digest("hex");
6806
+ }
6807
+ //#endregion
6539
6808
  //#region src/lint/format.ts
6540
6809
  const color = (code) => (text, enabled) => enabled ? `\u001b[${code}m${text}\u001b[39m` : text;
6541
6810
  const red = color(31);
@@ -6546,15 +6815,20 @@ const bold = (text, enabled) => enabled ? `\u001b[1m${text}\u001b[22m` : text;
6546
6815
  * Renders check results:
6547
6816
  * - `pretty`: grouped by file, for terminals and agents,
6548
6817
  * - `json`: the raw results,
6549
- * - `github`: workflow commands that GitHub Actions turns into PR annotations.
6818
+ * - `github`: workflow commands that GitHub Actions turns into PR annotations,
6819
+ * - `sarif`: a SARIF 2.1.0 log, for GitHub code scanning and other SARIF consumers.
6550
6820
  */
6551
6821
  function formatDiagnostics(results, format, options = {}) {
6552
6822
  const { fixedHint } = options;
6553
6823
  if (format === "json") return JSON.stringify(results, null, 2);
6824
+ if (format === "sarif") return formatSarif(results, options.uri);
6554
6825
  if (format === "github") {
6555
- const lines = results.flatMap((result) => result.diagnostics.map((d) => {
6556
- return `::${d.severity === "error" ? "error" : "warning"} ${`file=${escapeProperty(result.file)},line=${d.line},col=${d.column},endLine=${d.endLine},endColumn=${d.endColumn},title=${escapeProperty(d.ruleId)}`}::${escapeData(d.message)}`;
6557
- }));
6826
+ const lines = results.flatMap((result) => {
6827
+ const file = options.uri ? options.uri(result.file) : result.file;
6828
+ return result.diagnostics.map((d) => {
6829
+ return `::${d.severity === "error" ? "error" : "warning"} ${`file=${escapeProperty(file)},line=${d.line},col=${d.column},endLine=${d.endLine},endColumn=${d.endColumn},title=${escapeProperty(d.ruleId)}`}::${escapeData(d.message)}`;
6830
+ });
6831
+ });
6558
6832
  if (fixedHint) lines.push(`::notice title=onsystem baseline::${escapeData(fixedHint)}`);
6559
6833
  return lines.join("\n");
6560
6834
  }
@@ -6591,16 +6865,6 @@ function escapeProperty(value) {
6591
6865
  }
6592
6866
  //#endregion
6593
6867
  //#region src/lint/index.ts
6594
- const RULES = [
6595
- noHardcodedColor,
6596
- noHardcodedSpacing,
6597
- noHardcodedRadius,
6598
- preferDesignSystemComponent,
6599
- noUnknownComponent,
6600
- noUnknownProp,
6601
- noUnknownVariant,
6602
- iconButtonAccessibleName
6603
- ];
6604
6868
  /**
6605
6869
  * Lints a TSX/JSX snippet or file against the design system. Purely
6606
6870
  * syntactic: no type-checking of the snippet, so it works on fragments an
@@ -7108,13 +7372,6 @@ const silentLogger = {
7108
7372
  error: () => void 0
7109
7373
  };
7110
7374
  //#endregion
7111
- //#region package.json
7112
- var name = "onsystem";
7113
- //#endregion
7114
- //#region src/version.ts
7115
- const VERSION = "0.4.0";
7116
- const NAME = name;
7117
- //#endregion
7118
7375
  //#region src/design-system.ts
7119
7376
  /** A loaded design system: the model plus the indexes the tools query. */
7120
7377
  var DesignSystem = class {
@@ -7202,7 +7459,7 @@ var DesignSystem = class {
7202
7459
  });
7203
7460
  }
7204
7461
  };
7205
- const CONFIGURATION_DOCS = "https://design-system-mcp-demo.vercel.app/docs/configuration";
7462
+ const CONFIGURATION_DOCS = "https://onsystem.vercel.app/docs/configuration";
7206
7463
  /** `border-ring` → token `ring`, for color utilities a token's usage list does not spell out. */
7207
7464
  function matchUtility(base, tokens) {
7208
7465
  const key = COLOR_UTILITY.exec(base)?.[1];
@@ -7581,1481 +7838,4 @@ var DesignSystemHost = class {
7581
7838
  }
7582
7839
  };
7583
7840
  //#endregion
7584
- //#region src/types.ts
7585
- /**
7586
- * The design-system model the server exposes. Everything here is plain JSON so
7587
- * it can be cached on disk and returned as MCP structured content.
7588
- */
7589
- const TOKEN_CATEGORIES = [
7590
- "color",
7591
- "spacing",
7592
- "radius",
7593
- "typography",
7594
- "shadow",
7595
- "other"
7596
- ];
7597
- //#endregion
7598
- //#region src/server/render.ts
7599
- /**
7600
- * Tool results are read by models, so they are rendered as compact Markdown:
7601
- * fewer tokens than JSON, and the same information. The JSON is still sent
7602
- * as structured content.
7603
- */
7604
- function firstSentence(text, max = 140) {
7605
- if (!text) return "";
7606
- return truncate((/^[\s\S]*?[.!?](?=\s+[A-Z`"(]|\s*$)/.exec(text.trim())?.[0] ?? text.trim()).replace(/\s+/g, " "), max);
7607
- }
7608
- function importStatement(component) {
7609
- return `import { ${component.exportName.split(".")[0] ?? component.name} } from "${component.importPath}"`;
7610
- }
7611
- function variantSummary(component) {
7612
- return component.variants.map((v) => `${v.name}: ${v.values.join(" | ")}${v.default ? ` (default ${v.default})` : ""}`);
7613
- }
7614
- /** `project`: the folder of a workspace root's project the list is for. */
7615
- function renderComponentList(ds, project) {
7616
- const roots = ds.roots();
7617
- if (!roots.length) return `No components found${project ? ` in ${project}` : ""}. Check the "components" globs in the config (root: ${ds.root}).`;
7618
- const parts = ds.components.length - roots.length;
7619
- const lines = [`${plural$1(roots.length, "component")}${parts ? ` (+${plural$1(parts, "part")})` : ""}${project ? ` in ${project}` : ""}. Call get_component before using one.`, ""];
7620
- for (const component of roots) lines.push(...listItem(component));
7621
- return lines.join("\n");
7622
- }
7623
- function listItem(component, note = "") {
7624
- const element = component.element ? ` <${component.element}>` : "";
7625
- const lines = [`${component.name}${element}${note} — ${firstSentence(component.description) || "No description."}`];
7626
- for (const variant of variantSummary(component)) lines.push(` ${variant}`);
7627
- if (component.subcomponents.length) lines.push(` parts: ${component.subcomponents.join(", ")}`);
7628
- lines.push(` ${importStatement(component)}`);
7629
- return lines;
7630
- }
7631
- /** `apps/web`, `apps/web and packages/ui`, `a, b, c and 4 more`. */
7632
- function listOf$1(items, max = 3) {
7633
- if (items.length <= 1) return items[0] ?? "";
7634
- if (items.length <= max) return `${items.slice(0, -1).join(", ")} and ${items.at(-1) ?? ""}`;
7635
- return `${items.slice(0, max).join(", ")} and ${items.length - max} more`;
7636
- }
7637
- const PICK_PROJECT = "This is a monorepo root: pass `path`, the file you are editing, to get_component, get_tokens and check_ui so they answer for its project.";
7638
- /** Every project's components at a workspace root, grouped by the package they come from. */
7639
- function renderCatalogList(catalog) {
7640
- const roots = catalog.roots();
7641
- if (!roots.length) return `No components found in any project of this monorepo (root: ${catalog.root}). Run \`onsystem inspect\` there to see what was looked at.`;
7642
- const byPackage = /* @__PURE__ */ new Map();
7643
- for (const entry of roots) byPackage.set(entry.package, [...byPackage.get(entry.package) ?? [], entry]);
7644
- const parts = catalog.entries.length - roots.length;
7645
- const lines = [`${plural$1(roots.length, "component")}${parts ? ` (+${plural$1(parts, "part")})` : ""} from ${plural$1(byPackage.size, "package")}, used by ${plural$1(catalog.projects.length, "project")}. ${PICK_PROJECT}`];
7646
- for (const [pkg, entries] of byPackage) {
7647
- const projects = [...new Set(entries.flatMap((e) => e.projects))];
7648
- lines.push("", `## ${pkg} (used by ${listOf$1(projects)})`);
7649
- for (const entry of entries) {
7650
- const others = entry.ambiguous ? catalog.roots().filter((e) => e !== entry && e.component.name === entry.component.name) : [];
7651
- const note = others.length ? ` (also in ${[...new Set(others.map((e) => e.package))].join(", ")})` : "";
7652
- lines.push(...listItem(entry.component, note));
7653
- }
7654
- }
7655
- return lines.join("\n");
7656
- }
7657
- /** Several components under one name at a workspace root, and how to pick one. */
7658
- function renderAmbiguous(name, entries) {
7659
- const lines = [`"${name}" is the name of ${entries.length} components in this monorepo:`];
7660
- for (const entry of entries) lines.push(`- ${entry.package}: ${entry.component.source.file}, used by ${listOf$1(entry.projects)} (${importStatement(entry.component)})`);
7661
- const dirs = [...new Set(entries.flatMap((e) => e.projects))];
7662
- lines.push("", `Pass \`path\`: the file you are editing, or its project folder (${dirs.join(", ")}), to get the one that project uses.`);
7663
- return lines.join("\n");
7664
- }
7665
- /** Inherited attributes worth naming, per element; the rest are summarised. */
7666
- const NOTABLE_INHERITED = {
7667
- button: [
7668
- "onClick",
7669
- "type",
7670
- "disabled",
7671
- "form"
7672
- ],
7673
- input: [
7674
- "type",
7675
- "value",
7676
- "defaultValue",
7677
- "onChange",
7678
- "placeholder",
7679
- "disabled",
7680
- "name",
7681
- "required"
7682
- ],
7683
- textarea: [
7684
- "value",
7685
- "defaultValue",
7686
- "onChange",
7687
- "placeholder",
7688
- "disabled",
7689
- "name",
7690
- "rows"
7691
- ],
7692
- select: [
7693
- "value",
7694
- "defaultValue",
7695
- "onChange",
7696
- "disabled",
7697
- "name"
7698
- ],
7699
- a: [
7700
- "href",
7701
- "target",
7702
- "rel"
7703
- ],
7704
- img: [
7705
- "src",
7706
- "alt",
7707
- "width",
7708
- "height"
7709
- ],
7710
- label: ["htmlFor"],
7711
- default: [
7712
- "onClick",
7713
- "id",
7714
- "role",
7715
- "children"
7716
- ]
7717
- };
7718
- /** What callers of a React Aria component most often need, from its inherited props. */
7719
- const REACT_ARIA_NOTABLE = [
7720
- "onPress",
7721
- "onChange",
7722
- "onAction",
7723
- "onSelectionChange",
7724
- "onOpenChange",
7725
- "value",
7726
- "defaultValue",
7727
- "isDisabled",
7728
- "isRequired",
7729
- "isInvalid",
7730
- "isReadOnly",
7731
- "autoFocus"
7732
- ];
7733
- function renderProp(prop) {
7734
- const optional = prop.required ? "" : "?";
7735
- const fallback = prop.default !== void 0 ? ` = ${prop.default}` : "";
7736
- const deprecated = prop.deprecated ? ` DEPRECATED${prop.deprecated === true ? "" : `: ${prop.deprecated}`}.` : "";
7737
- const description = prop.description ? ` — ${prop.description.replace(/\s*\n\s*/g, " ")}` : "";
7738
- return `- ${prop.name}${optional}: ${prop.type}${fallback}${description}${deprecated}`;
7739
- }
7740
- /** `origin`: at a workspace root, the package the component comes from and who uses it. */
7741
- function renderComponent(ds, component, origin) {
7742
- const lines = [`# ${component.name}`];
7743
- if (component.deprecated) lines.push(`DEPRECATED${component.deprecated === true ? "" : `: ${component.deprecated}`}`);
7744
- if (component.description) lines.push(component.description);
7745
- lines.push("", importStatement(component));
7746
- const facts = [
7747
- component.element ? `Renders <${component.element}>` : void 0,
7748
- component.parent ? `Part of ${component.parent}` : void 0,
7749
- component.aliases.length ? `Also available as ${component.aliases.join(", ")}` : void 0,
7750
- `${component.source.file}:${component.source.line}`,
7751
- component.docs ? `docs: ${component.docs.file}` : void 0
7752
- ].filter(Boolean);
7753
- lines.push(facts.join(" · "));
7754
- if (origin) lines.push(`From ${origin.package}, used by ${listOf$1(origin.projects)}.`);
7755
- lines.push("", "## Props");
7756
- if (!component.props.length && !component.inherits.length) lines.push("No props.");
7757
- for (const prop of component.props) lines.push(renderProp(prop));
7758
- for (const inherited of component.inherits) {
7759
- const names = ds.model.propSets[inherited.set] ?? [];
7760
- const notable = (names.some((n) => n === "onPress" || n === "onFocusChange") ? REACT_ARIA_NOTABLE : NOTABLE_INHERITED[component.element ?? ""] ?? NOTABLE_INHERITED.default ?? []).filter((n) => names.includes(n) && !inherited.deprecated?.includes(n));
7761
- lines.push(`- …plus ${inherited.count} props from ${inherited.from}` + (notable.length ? ` (${notable.join(", ")}, aria-*, data-*, …)` : ""));
7762
- }
7763
- if (component.openProps) lines.push("- Part of the props type could not be resolved; extra props may be accepted.");
7764
- if (component.variants.length) {
7765
- lines.push("", "## Variants");
7766
- for (const variant of component.variants) {
7767
- lines.push(`${variant.name}${variant.default ? ` (default "${variant.default}")` : ""}`);
7768
- const width = Math.max(...variant.values.map((v) => v.length));
7769
- for (const value of variant.values) lines.push(` ${value.padEnd(width)} ${variant.classes[value] ?? ""}`.trimEnd());
7770
- }
7771
- for (const compound of component.compoundVariants) {
7772
- const when = Object.entries(compound.when).map(([k, v]) => `${k}=${Array.isArray(v) ? v.join("|") : v}`).join(" & ");
7773
- lines.push(` when ${when}: ${compound.classes}`);
7774
- }
7775
- }
7776
- if (component.subcomponents.length) {
7777
- lines.push("", "## Parts");
7778
- for (const name of component.subcomponents) {
7779
- const part = ds.getComponent(name);
7780
- lines.push(`- ${name}${part?.description ? ` — ${firstSentence(part.description)}` : ""}`);
7781
- }
7782
- }
7783
- const tokens = ds.relatedTokens(component);
7784
- if (tokens.length) {
7785
- lines.push("", "## Design tokens used");
7786
- lines.push(tokens.map((t) => `${t.name} (${t.usage[0] ?? t.value})`).join(", "));
7787
- }
7788
- if (component.docs?.sections.length) {
7789
- lines.push("", `## Docs (${component.docs.file})`);
7790
- for (const section of component.docs.sections) {
7791
- if (/^examples?$/i.test(section.heading) && !section.body) continue;
7792
- lines.push(`### ${section.heading}`);
7793
- if (section.body) lines.push(section.body);
7794
- }
7795
- }
7796
- if (component.examples.length) {
7797
- lines.push("", "## Examples");
7798
- for (const example of component.examples) {
7799
- if (example.title) lines.push(`### ${example.title}`);
7800
- lines.push(`\`\`\`${example.lang}`, example.code, "```");
7801
- }
7802
- }
7803
- return lines.join("\n");
7804
- }
7805
- function renderSearch(query, hits) {
7806
- if (!hits.length) return `No components match "${query}". Try other words, or list_components to browse.`;
7807
- const lines = [`Components for "${query}", best first:`, ""];
7808
- hits.forEach((hit, i) => {
7809
- const c = hit.component;
7810
- const parent = c.parent ? ` (part of ${c.parent})` : "";
7811
- lines.push(`${i + 1}. ${c.name}${parent} — ${firstSentence(c.description) || "No description."}`);
7812
- lines.push(` score ${hit.score} · matched: ${hit.matched.join(", ")} · ${importStatement(c)}`);
7813
- });
7814
- lines.push("", "Call get_component for props, variants and examples.");
7815
- return lines.join("\n");
7816
- }
7817
- /** Search hits at a workspace root, each with the package it comes from. */
7818
- function renderCatalogSearch(query, hits) {
7819
- if (!hits.length) return `No components match "${query}" in any project. Try other words, or list_components to browse.`;
7820
- const lines = [`Components for "${query}" across the monorepo's projects, best first:`, ""];
7821
- hits.forEach((hit, i) => {
7822
- const c = hit.component;
7823
- const parent = c.parent ? ` (part of ${c.parent})` : "";
7824
- lines.push(`${i + 1}. ${c.name}${parent} from ${hit.entry.package} — ${firstSentence(c.description) || "No description."}`);
7825
- lines.push(` score ${hit.score} · matched: ${hit.matched.join(", ")} · used by ${listOf$1(hit.entry.projects)} · ${importStatement(c)}`);
7826
- });
7827
- lines.push("", `Call get_component for props, variants and examples. ${PICK_PROJECT}`);
7828
- return lines.join("\n");
7829
- }
7830
- /** Tokens at a workspace root, grouped by category, each with the package that defines it. */
7831
- function renderCatalogTokens(catalog, tokens, filter = {}) {
7832
- if (!catalog.tokens.length) return `No design tokens found in any project of this monorepo (root: ${catalog.root}). Set "tokens" in a project's config: https://design-system-mcp-demo.vercel.app/docs/configuration`;
7833
- if (!tokens.length) {
7834
- const what = [filter.category, filter.query && `"${filter.query}"`].filter(Boolean).join(" ");
7835
- return `No tokens match${what ? ` ${what}` : ""} in any project; call get_tokens without filters to see them.`;
7836
- }
7837
- const groups = /* @__PURE__ */ new Map();
7838
- for (const t of tokens) groups.set(t.token.category, [...groups.get(t.token.category) ?? [], t]);
7839
- const lines = [PICK_PROJECT, ""];
7840
- for (const [category, list] of groups) {
7841
- lines.push(`## ${category} (${list.length})`);
7842
- for (const { token, package: pkg } of list) {
7843
- const usage = token.usage.length ? ` → ${token.usage.join(", ")}` : "";
7844
- lines.push(`- ${token.name}: ${token.value}${usage} (${pkg})`);
7845
- }
7846
- lines.push("");
7847
- }
7848
- return lines.join("\n").trimEnd();
7849
- }
7850
- function renderTokens(ds, tokens, filter = {}) {
7851
- if (!ds.tokens.length) return `No design tokens found in this project (root: ${ds.root}): no stylesheet or *.tokens.json defines any. Set "tokens" in the config: https://design-system-mcp-demo.vercel.app/docs/configuration`;
7852
- if (!tokens.length) {
7853
- const counts = /* @__PURE__ */ new Map();
7854
- for (const token of ds.tokens) counts.set(token.category, (counts.get(token.category) ?? 0) + 1);
7855
- const categories = [...counts].map(([category, n]) => `${category} (${n})`).join(", ");
7856
- const what = [filter.category, filter.query && `"${filter.query}"`].filter(Boolean).join(" ");
7857
- return `No tokens match${what ? ` ${what}` : ""}. The design system has ${categories}; call get_tokens without filters to see them.`;
7858
- }
7859
- const groups = /* @__PURE__ */ new Map();
7860
- for (const token of tokens) groups.set(token.category, [...groups.get(token.category) ?? [], token]);
7861
- const lines = [];
7862
- for (const [category, list] of groups) {
7863
- lines.push(`## ${category} (${list.length})`);
7864
- for (const token of list) {
7865
- const px = token.category === "spacing" || token.category === "radius" ? ds.tokenIndex.toPx(token.value) : void 0;
7866
- const value = px !== void 0 && !/^-?[\d.]+px$/.test(token.value) ? `${token.value} = ${formatPx(px)}` : token.value;
7867
- const modes = token.modes ? ` (${Object.entries(token.modes).map(([mode, v]) => `${mode}: ${v}`).join(", ")})` : "";
7868
- const usage = token.usage.length ? ` → ${token.usage.join(", ")}` : "";
7869
- const description = token.description ? ` — ${token.description}` : "";
7870
- const deprecated = token.deprecated ? " DEPRECATED" : "";
7871
- lines.push(`- ${token.name}: ${value}${modes}${usage}${description}${deprecated}`);
7872
- }
7873
- lines.push("");
7874
- }
7875
- return lines.join("\n").trimEnd();
7876
- }
7877
- function capDiagnostics(result, limit) {
7878
- const ordered = [...result.diagnostics.filter((d) => d.severity === "error"), ...result.diagnostics.filter((d) => d.severity !== "error")];
7879
- const byRule = {};
7880
- for (const d of result.diagnostics) {
7881
- const counts = byRule[d.ruleId] ??= {
7882
- errors: 0,
7883
- warnings: 0
7884
- };
7885
- if (d.severity === "error") counts.errors++;
7886
- else counts.warnings++;
7887
- }
7888
- const shown = ordered.slice(0, limit);
7889
- return {
7890
- shown,
7891
- omitted: ordered.length - shown.length,
7892
- byRule
7893
- };
7894
- }
7895
- function renderCheck(result, capped = capDiagnostics(result, Infinity)) {
7896
- if (result.skipped) return `${result.file}: not checked. ${result.skipped}`;
7897
- if (!result.diagnostics.length) return `${result.file}: no design-system problems found.`;
7898
- const lines = [`${result.file}: ${plural$1(result.errorCount, "error")}, ${plural$1(result.warningCount, "warning")}`, ""];
7899
- for (const d of capped.shown) lines.push(`${d.line}:${d.column} ${d.severity} [${d.ruleId}] ${d.message}`);
7900
- if (capped.omitted) {
7901
- const rules = Object.entries(capped.byRule).sort(([, a], [, b]) => b.errors + b.warnings - (a.errors + a.warnings)).map(([rule, c]) => `${rule} ${(c.errors + c.warnings).toLocaleString("en-US")}`).join(", ");
7902
- lines.push("", `Showing ${capped.shown.length} of ${result.diagnostics.length.toLocaleString("en-US")} (errors first). By rule: ${rules}.`, `${capped.omitted.toLocaleString("en-US")} more not shown: fix these, then run check_ui again, or pass a higher \`limit\`.`);
7903
- }
7904
- lines.push("", result.errorCount ? "Fix every error, then run check_ui again." : "Only warnings: fix them if the design system has an equivalent.");
7905
- return lines.join("\n");
7906
- }
7907
- //#endregion
7908
- //#region src/server/index.ts
7909
- const INSTRUCTIONS = `This server describes the project's design system: its React components (props, variants, parts), design tokens and usage docs, and lints UI code against it.
7910
- When writing or changing UI in this project:
7911
- 1. Find components with search_components (by intent) or list_components.
7912
- 2. Read get_component before using a component: use only the props and variant values it lists.
7913
- 3. Style with tokens from get_tokens, never with hex/rgb colors, arbitrary px values or Tailwind's default palette.
7914
- 4. Run check_ui on every snippet or file you write and fix all errors before you finish.
7915
- In a monorepo served from its root, pass \`path\` (the file you are editing) to list_components, get_component, search_components and get_tokens, and \`path\` or \`filename\` to check_ui, so each answer comes from that file's own project.`;
7916
- const READ_ONLY = {
7917
- readOnlyHint: true,
7918
- idempotentHint: true,
7919
- openWorldHint: false
7920
- };
7921
- /** What a client may cache: what never changes while the server runs, and what does. */
7922
- const STATIC = {
7923
- ttlMs: 36e5,
7924
- cacheScope: "private"
7925
- };
7926
- const FRESH = {
7927
- ttlMs: 0,
7928
- cacheScope: "private"
7929
- };
7930
- /** Input limits, so one oversized request cannot stall the server. */
7931
- const MAX_NAME = 256;
7932
- const MAX_QUERY = 1e3;
7933
- const MAX_PATH = 4096;
7934
- /** Larger than any hand-written source file; generated bundles are not UI to check. */
7935
- const MAX_CODE = 1e6;
7936
- /** Diagnostics check_ui returns by default: a 3,000-line file can have thousands, more than a context holds. */
7937
- const DEFAULT_LIMIT = 50;
7938
- const MAX_LIMIT = 1e3;
7939
- /**
7940
- * An integer, without the ±9007199254740991 bounds zod adds to the JSON Schema,
7941
- * which every client would otherwise read in each tool definition.
7942
- */
7943
- const int = () => z.number().int().meta({
7944
- minimum: void 0,
7945
- maximum: void 0
7946
- });
7947
- /** At a workspace root, where a component comes from. */
7948
- const originShape = {
7949
- package: z.string().optional().describe("At a workspace root: the package its source belongs to, e.g. \"@acme/ui\"."),
7950
- projects: z.array(z.string()).optional().describe("At a workspace root: the folders of the projects that use it.")
7951
- };
7952
- const diagnosticSchema = z.object({
7953
- ruleId: z.string(),
7954
- severity: z.enum(["error", "warning"]),
7955
- message: z.string(),
7956
- line: int(),
7957
- column: int(),
7958
- endLine: int(),
7959
- endColumn: int(),
7960
- source: z.string(),
7961
- suggestion: z.string().optional(),
7962
- fix: z.array(z.object({
7963
- range: z.array(int()).length(2),
7964
- text: z.string()
7965
- })).optional().describe("Edits (0-based offsets into the checked code) that apply the suggestion.")
7966
- });
7967
- const deprecatedSchema = z.union([z.string(), z.literal(true)]).optional();
7968
- const componentSchema = {
7969
- name: z.string().describe("How the component is written in JSX: Button, CardHeader, Tabs.List."),
7970
- aliases: z.array(z.string()),
7971
- description: z.string().optional(),
7972
- deprecated: deprecatedSchema,
7973
- importPath: z.string(),
7974
- exportName: z.string(),
7975
- source: z.object({
7976
- file: z.string(),
7977
- line: int()
7978
- }),
7979
- parent: z.string().optional(),
7980
- subcomponents: z.array(z.string()),
7981
- element: z.string().optional().describe("The native element it renders or wraps."),
7982
- props: z.array(z.object({
7983
- name: z.string(),
7984
- type: z.string(),
7985
- required: z.boolean(),
7986
- default: z.string().optional(),
7987
- description: z.string().optional(),
7988
- values: z.array(z.string()).optional(),
7989
- deprecated: deprecatedSchema,
7990
- kind: z.enum(["prop", "variant"])
7991
- })),
7992
- inherits: z.array(z.object({
7993
- from: z.string(),
7994
- count: int(),
7995
- set: z.string(),
7996
- deprecated: z.array(z.string()).optional()
7997
- })),
7998
- openProps: z.boolean().describe("Part of the props type did not resolve; extra props may be accepted."),
7999
- variants: z.array(z.object({
8000
- name: z.string(),
8001
- values: z.array(z.string()),
8002
- default: z.string().optional(),
8003
- classes: z.record(z.string(), z.string())
8004
- })),
8005
- compoundVariants: z.array(z.object({
8006
- when: z.record(z.string(), z.union([z.string(), z.array(z.string())])),
8007
- classes: z.string()
8008
- })),
8009
- examples: z.array(z.object({
8010
- title: z.string().optional(),
8011
- code: z.string(),
8012
- lang: z.string(),
8013
- source: z.enum(["docs", "jsdoc"])
8014
- })),
8015
- docs: z.object({
8016
- file: z.string(),
8017
- description: z.string().optional(),
8018
- sections: z.array(z.object({
8019
- heading: z.string(),
8020
- body: z.string()
8021
- })),
8022
- frontmatter: z.record(z.string(), z.union([z.string(), z.array(z.string())]))
8023
- }).optional(),
8024
- classNames: z.array(z.string()),
8025
- cssVars: z.array(z.string()),
8026
- import: z.string().describe("The import statement to use."),
8027
- tokens: z.array(z.string()).describe("Names of the design tokens its classes use."),
8028
- ...originShape
8029
- };
8030
- const componentSummarySchema = z.object({
8031
- name: z.string(),
8032
- description: z.string().optional(),
8033
- importPath: z.string(),
8034
- element: z.string().optional(),
8035
- variants: z.record(z.string(), z.array(z.string())),
8036
- subcomponents: z.array(z.string()),
8037
- ...originShape
8038
- });
8039
- const tokenSchema = z.object({
8040
- name: z.string(),
8041
- category: z.enum(TOKEN_CATEGORIES),
8042
- value: z.string(),
8043
- modes: z.record(z.string(), z.string()).optional(),
8044
- cssVar: z.string().optional(),
8045
- tailwind: z.string().optional(),
8046
- usage: z.array(z.string()),
8047
- description: z.string().optional(),
8048
- ...originShape
8049
- });
8050
- /** The optional `path` that picks a project at a workspace root. */
8051
- const projectPath = z.string().max(MAX_PATH).optional().describe("In a monorepo served from its root: the file you are editing, or a project folder, relative to the root, to answer from that project. Without it, every project is listed together. Ignored in a single project.");
8052
- /** A component's source path relative to `root`, the workspace root the client knows. */
8053
- function fromRoot(ds, component, root) {
8054
- if (!root || root === ds.root) return component;
8055
- const file = relativePath(root, path.resolve(ds.root, component.source.file));
8056
- return {
8057
- ...component,
8058
- source: {
8059
- ...component.source,
8060
- file
8061
- }
8062
- };
8063
- }
8064
- /** `a, b and c`, `a, b, c and 4 more`. */
8065
- function listOf(items, max) {
8066
- if (items.length <= 1) return items[0] ?? "";
8067
- if (items.length <= max) return `${items.slice(0, -1).join(", ")} and ${items.at(-1) ?? ""}`;
8068
- return `${items.slice(0, max).join(", ")} and ${items.length - max} more`;
8069
- }
8070
- function errorResult(text) {
8071
- return {
8072
- isError: true,
8073
- content: [{
8074
- type: "text",
8075
- text
8076
- }]
8077
- };
8078
- }
8079
- function origin(entry) {
8080
- return {
8081
- package: entry.package,
8082
- projects: entry.projects
8083
- };
8084
- }
8085
- function summary(c) {
8086
- return {
8087
- name: c.name,
8088
- ...c.description ? { description: c.description } : {},
8089
- importPath: c.importPath,
8090
- ...c.element ? { element: c.element } : {},
8091
- variants: Object.fromEntries(c.variants.map((v) => [v.name, v.values])),
8092
- subcomponents: c.subcomponents
8093
- };
8094
- }
8095
- /** Builds the MCP server: five tools, two resources and one prompt. Transport-agnostic. */
8096
- function createServer({ getDesignSystem, getProjects }) {
8097
- const server = new McpServer({
8098
- name: NAME,
8099
- title: "onsystem",
8100
- version: VERSION
8101
- }, {
8102
- instructions: INSTRUCTIONS,
8103
- capabilities: { resources: { listChanged: true } },
8104
- cacheHints: {
8105
- "server/discover": STATIC,
8106
- "tools/list": STATIC,
8107
- "prompts/list": STATIC,
8108
- "resources/templates/list": STATIC,
8109
- "resources/list": FRESH,
8110
- "resources/read": FRESH
8111
- }
8112
- });
8113
- /** The design system for `target` at a workspace root, or the merged catalog without one. */
8114
- const scope = async (target) => {
8115
- const set = await getProjects?.();
8116
- if (!set) return { ds: await getDesignSystem() };
8117
- if (target !== void 0 && target !== "") {
8118
- const project = await set.forPath(target);
8119
- return {
8120
- ds: project.ds,
8121
- project,
8122
- root: set.root
8123
- };
8124
- }
8125
- return {
8126
- set,
8127
- catalog: await set.catalog()
8128
- };
8129
- };
8130
- /** `scope`, with a path outside the workspace as an error result instead of a throw. */
8131
- const scoped = async (target) => {
8132
- try {
8133
- return await scope(target);
8134
- } catch (error) {
8135
- return errorResult(error.message);
8136
- }
8137
- };
8138
- const isResult = (value) => "content" in value;
8139
- server.registerTool("list_components", {
8140
- title: "List design-system components",
8141
- description: "List every component in the project design system with a one-line description, the native element it renders, its variant values, its parts and its import. Call this before writing UI in this project. At a monorepo root, pass `path` for the project of the file you are editing; without it, every project is listed, each component with its package.",
8142
- inputSchema: z.object({ path: projectPath }),
8143
- outputSchema: z.object({ components: z.array(componentSummarySchema) }),
8144
- annotations: READ_ONLY
8145
- }, async ({ path: target }) => {
8146
- const found = await scoped(target);
8147
- if (isResult(found)) return found;
8148
- if ("catalog" in found) {
8149
- const { catalog } = found;
8150
- return {
8151
- content: [{
8152
- type: "text",
8153
- text: renderCatalogList(catalog)
8154
- }],
8155
- structuredContent: { components: catalog.roots().map((entry) => ({
8156
- ...summary(entry.component),
8157
- ...origin(entry)
8158
- })) }
8159
- };
8160
- }
8161
- const { ds } = found;
8162
- return {
8163
- content: [{
8164
- type: "text",
8165
- text: renderComponentList(ds, found.project?.dir)
8166
- }],
8167
- structuredContent: { components: ds.roots().map(summary) }
8168
- };
8169
- });
8170
- server.registerTool("get_component", {
8171
- title: "Get a component contract",
8172
- description: "Get the full contract of one component: import, props with types and defaults, cva variants and the classes each applies, parts (sub-components), design tokens it uses, usage guidelines and examples from its docs. Call it before using a component. Accepts \"Button\", \"CardHeader\" or \"Card.Header\". At a monorepo root, pass `path` (the file you are editing) to get the component its project uses.",
8173
- inputSchema: z.object({
8174
- name: z.string().min(1).max(MAX_NAME).describe("Component name, e.g. \"Button\" or \"CardHeader\"."),
8175
- path: projectPath
8176
- }),
8177
- outputSchema: z.object(componentSchema),
8178
- annotations: READ_ONLY
8179
- }, async ({ name, path: target }) => {
8180
- const found = await scoped(target);
8181
- if (isResult(found)) return found;
8182
- if ("catalog" in found) {
8183
- const { catalog } = found;
8184
- const matches = catalog.find(name);
8185
- const [entry] = matches;
8186
- if (!entry) {
8187
- const guess = catalog.suggest(name);
8188
- const names = [...new Set(catalog.roots().map((e) => e.component.name))];
8189
- return errorResult(`No component named "${name}" in any project.` + (guess ? ` Did you mean "${guess}"?` : "") + ` Available: ${names.join(", ")}.`);
8190
- }
8191
- if (matches.length > 1) return errorResult(renderAmbiguous(name, matches));
8192
- return {
8193
- content: [{
8194
- type: "text",
8195
- text: renderComponent(entry.ds, entry.component, entry)
8196
- }],
8197
- structuredContent: {
8198
- ...entry.component,
8199
- import: importStatement(entry.component),
8200
- tokens: entry.ds.relatedTokens(entry.component).map((t) => t.name),
8201
- ...origin(entry)
8202
- }
8203
- };
8204
- }
8205
- const { ds } = found;
8206
- const component = ds.getComponent(name);
8207
- if (!component) {
8208
- const guess = ds.suggestComponent(name);
8209
- return {
8210
- isError: true,
8211
- content: [{
8212
- type: "text",
8213
- text: `No component named "${name}".` + (guess ? ` Did you mean "${guess}"?` : "") + ` Available: ${ds.roots().map((c) => c.name).join(", ")}.`
8214
- }]
8215
- };
8216
- }
8217
- const shown = fromRoot(ds, component, found.root);
8218
- return {
8219
- content: [{
8220
- type: "text",
8221
- text: renderComponent(ds, shown)
8222
- }],
8223
- structuredContent: {
8224
- ...shown,
8225
- import: importStatement(component),
8226
- tokens: ds.relatedTokens(component).map((t) => t.name)
8227
- }
8228
- };
8229
- });
8230
- server.registerTool("search_components", {
8231
- title: "Search components by intent",
8232
- description: "Find components by what you want to build, e.g. \"confirm a destructive action\", \"status label\", \"text field with validation\". Ranks names, descriptions, docs, props and variant values with BM25 (local, no API key). Use it when you do not know which component fits.",
8233
- inputSchema: z.object({
8234
- query: z.string().min(1).max(MAX_QUERY).describe("What the UI should do, in plain words."),
8235
- limit: z.number().int().min(1).max(20).default(5).describe("Maximum results."),
8236
- path: projectPath
8237
- }),
8238
- outputSchema: z.object({ results: z.array(z.object({
8239
- name: z.string(),
8240
- parent: z.string().optional(),
8241
- description: z.string().optional(),
8242
- importPath: z.string(),
8243
- score: z.number(),
8244
- matched: z.array(z.string()),
8245
- ...originShape
8246
- })) }),
8247
- annotations: READ_ONLY
8248
- }, async ({ query, limit, path: target }) => {
8249
- const found = await scoped(target);
8250
- if (isResult(found)) return found;
8251
- if ("catalog" in found) {
8252
- const hits = found.catalog.search(query, limit);
8253
- return {
8254
- content: [{
8255
- type: "text",
8256
- text: renderCatalogSearch(query, hits)
8257
- }],
8258
- structuredContent: { results: hits.map((h) => ({
8259
- name: h.component.name,
8260
- ...h.component.parent ? { parent: h.component.parent } : {},
8261
- ...h.component.description ? { description: h.component.description } : {},
8262
- importPath: h.component.importPath,
8263
- score: h.score,
8264
- matched: h.matched,
8265
- ...origin(h.entry)
8266
- })) }
8267
- };
8268
- }
8269
- const { ds } = found;
8270
- const hits = ds.search(query, limit);
8271
- return {
8272
- content: [{
8273
- type: "text",
8274
- text: renderSearch(query, hits)
8275
- }],
8276
- structuredContent: { results: hits.map((h) => ({
8277
- name: h.component.name,
8278
- ...h.component.parent ? { parent: h.component.parent } : {},
8279
- ...h.component.description ? { description: h.component.description } : {},
8280
- importPath: h.component.importPath,
8281
- score: h.score,
8282
- matched: h.matched
8283
- })) }
8284
- };
8285
- });
8286
- server.registerTool("get_tokens", {
8287
- title: "Get design tokens",
8288
- description: "List design tokens with resolved values, dark-mode values and how to use each (Tailwind class or CSS variable). Filter by category (color, spacing, radius, typography, shadow, other) or a substring. Use these instead of hex codes, px values or Tailwind default palette classes.",
8289
- inputSchema: z.object({
8290
- category: z.enum(TOKEN_CATEGORIES).optional().describe("Only tokens of this category."),
8291
- query: z.string().max(MAX_QUERY).optional().describe("Substring to match in names, usages or descriptions."),
8292
- path: projectPath
8293
- }),
8294
- outputSchema: z.object({ tokens: z.array(tokenSchema) }),
8295
- annotations: READ_ONLY
8296
- }, async ({ category, query, path: target }) => {
8297
- const found = await scoped(target);
8298
- if (isResult(found)) return found;
8299
- if ("catalog" in found) {
8300
- const tokens = found.catalog.getTokens({
8301
- category,
8302
- query
8303
- });
8304
- return {
8305
- content: [{
8306
- type: "text",
8307
- text: renderCatalogTokens(found.catalog, tokens, {
8308
- category,
8309
- query
8310
- })
8311
- }],
8312
- structuredContent: { tokens: tokens.map(({ token: t, package: pkg, projects }) => ({
8313
- name: t.name,
8314
- category: t.category,
8315
- value: t.value,
8316
- ...t.modes ? { modes: t.modes } : {},
8317
- ...t.cssVar ? { cssVar: t.cssVar } : {},
8318
- ...t.tailwind !== void 0 ? { tailwind: t.tailwind } : {},
8319
- usage: t.usage,
8320
- ...t.description ? { description: t.description } : {},
8321
- package: pkg,
8322
- projects
8323
- })) }
8324
- };
8325
- }
8326
- const { ds } = found;
8327
- const tokens = ds.getTokens({
8328
- category,
8329
- query
8330
- });
8331
- return {
8332
- content: [{
8333
- type: "text",
8334
- text: renderTokens(ds, tokens, {
8335
- category,
8336
- query
8337
- })
8338
- }],
8339
- structuredContent: { tokens: tokens.map((t) => ({
8340
- name: t.name,
8341
- category: t.category,
8342
- value: t.value,
8343
- ...t.modes ? { modes: t.modes } : {},
8344
- ...t.cssVar ? { cssVar: t.cssVar } : {},
8345
- ...t.tailwind !== void 0 ? { tailwind: t.tailwind } : {},
8346
- usage: t.usage,
8347
- ...t.description ? { description: t.description } : {}
8348
- })) }
8349
- };
8350
- });
8351
- server.registerTool("check_ui", {
8352
- title: "Check UI code against the design system",
8353
- description: "Lint TSX/JSX against the design system. Reports hardcoded colors, spacing and radius (with the nearest token), native elements that have a design-system component, unknown components, props and variant values, and icon-only buttons without an accessible name. Each diagnostic has line/column, a rule id, a message and a suggested fix. Run it on every snippet or file you write and fix all errors. Pass `code` for unsaved code, or `path` for a file in the project.",
8354
- inputSchema: z.object({
8355
- code: z.string().max(MAX_CODE).optional().describe(`TSX/JSX source to check (up to ${MAX_CODE.toLocaleString("en")} characters).`),
8356
- path: z.string().max(MAX_PATH).optional().describe("Path of a file to check, relative to the project root (the monorepo root, when served from there)."),
8357
- filename: z.string().max(MAX_PATH).optional().describe("Name to report `code` under; a .jsx extension parses it as JSX. At a monorepo root, the path the code will be saved under, which picks the project whose design system checks it."),
8358
- limit: z.number().int().min(1).max(MAX_LIMIT).default(DEFAULT_LIMIT).describe(`Most diagnostics to return, errors first (default ${DEFAULT_LIMIT}). The counts and byRule cover all of them.`)
8359
- }),
8360
- outputSchema: z.object({
8361
- file: z.string(),
8362
- ok: z.boolean().describe("True when there are no errors."),
8363
- errorCount: int(),
8364
- warningCount: int(),
8365
- diagnostics: z.array(diagnosticSchema).describe("Up to `limit` diagnostics, errors first; `omitted` says how many more there are."),
8366
- omitted: int().describe("Diagnostics left out by `limit`."),
8367
- byRule: z.record(z.string(), z.object({
8368
- errors: int(),
8369
- warnings: int()
8370
- })).describe("Every finding per rule, shown or not."),
8371
- skipped: z.string().optional().describe("Why the file was not checked: it renders an image (next/og) or an email."),
8372
- unchecked: z.object({
8373
- names: z.array(z.string()),
8374
- modules: z.array(z.string())
8375
- }).optional().describe("Components imported from modules the model does not include, not checked."),
8376
- notice: z.string().optional().describe("Set when no components or no color tokens were found, so rules did not run, or when components came from modules the model does not include.")
8377
- }),
8378
- annotations: READ_ONLY
8379
- }, async ({ code, path: filePath, filename, limit }) => {
8380
- if (code === void 0 && !filePath) return errorResult("Pass either `code` or `path`.");
8381
- const set = await getProjects?.();
8382
- const routeBy = code === void 0 ? filePath : filename ?? void 0;
8383
- let ds;
8384
- if (!set) ds = await getDesignSystem();
8385
- else if (routeBy) {
8386
- const found = await scoped(routeBy);
8387
- if (isResult(found)) return found;
8388
- if (!("ds" in found)) return errorResult(`Cannot find the project of ${routeBy}.`);
8389
- ds = found.ds;
8390
- } else if (set.dirs.length <= 1) ds = (await set.forPath(set.dirs[0] ?? ".")).ds;
8391
- else return errorResult(`This is a monorepo root with ${set.dirs.length} projects (${listOf(set.dirs, 6)}). Pass \`filename\`, the path the code will be saved under relative to the root, so the right project's design system checks it.`);
8392
- const base = set?.root ?? ds.root;
8393
- let source = code;
8394
- let file = filename ?? "snippet.tsx";
8395
- let shown = file;
8396
- if (set && filename) file = relativePath(ds.root, path.resolve(base, filename));
8397
- if (source === void 0) {
8398
- if (!filePath) return errorResult("Pass either `code` or `path`.");
8399
- const absolute = path.resolve(base, filePath);
8400
- const outside = errorResult(`${filePath} is outside the project root (${base}).`);
8401
- if (!isInside(base, absolute)) return outside;
8402
- try {
8403
- const [root, real] = await Promise.all([fs$1.realpath(base), fs$1.realpath(absolute)]);
8404
- if (!isInside(root, real)) return outside;
8405
- if ((await fs$1.stat(real)).size > MAX_CODE) return {
8406
- isError: true,
8407
- content: [{
8408
- type: "text",
8409
- text: `${filePath} is too large to check.`
8410
- }]
8411
- };
8412
- source = await fs$1.readFile(real, "utf8");
8413
- } catch {
8414
- return {
8415
- isError: true,
8416
- content: [{
8417
- type: "text",
8418
- text: `Cannot read ${filePath}.`
8419
- }]
8420
- };
8421
- }
8422
- file = relativePath(ds.root, absolute);
8423
- shown = relativePath(base, absolute);
8424
- }
8425
- const checked = ds.check(source, file);
8426
- const result = set ? {
8427
- ...checked,
8428
- file: shown
8429
- } : checked;
8430
- const capped = capDiagnostics(result, limit);
8431
- const notice = result.skipped ? void 0 : [ds.notice(), uncheckedNotice([result])].filter(Boolean).join("\n\n") || void 0;
8432
- return {
8433
- content: [{
8434
- type: "text",
8435
- text: renderCheck(result, capped) + (notice ? `\n\n${notice}` : "")
8436
- }],
8437
- structuredContent: {
8438
- ...result,
8439
- diagnostics: capped.shown,
8440
- omitted: capped.omitted,
8441
- byRule: capped.byRule,
8442
- ok: result.errorCount === 0,
8443
- ...notice ? { notice } : {}
8444
- }
8445
- };
8446
- });
8447
- server.registerResource("component", new ResourceTemplate("ds://components/{name}", {
8448
- list: async () => {
8449
- const found = await scope();
8450
- if ("catalog" in found) return { resources: found.catalog.entries.map(({ component: c, ambiguous, projects }) => ({
8451
- uri: `ds://components/${c.name}${ambiguous ? `?path=${encodeURIComponent(projects[0] ?? ".")}` : ""}`,
8452
- name: c.name,
8453
- title: c.name,
8454
- ...c.description ? { description: c.description } : {},
8455
- mimeType: "text/markdown"
8456
- })) };
8457
- return { resources: found.ds.components.map((c) => ({
8458
- uri: `ds://components/${c.name}`,
8459
- name: c.name,
8460
- title: c.name,
8461
- ...c.description ? { description: c.description } : {},
8462
- mimeType: "text/markdown"
8463
- })) };
8464
- },
8465
- complete: { name: async (value) => {
8466
- const found = await scope();
8467
- const lower = value.toLowerCase();
8468
- const names = "catalog" in found ? found.catalog.entries.map((e) => e.component.name) : found.ds.components.map((c) => c.name);
8469
- return [...new Set(names)].filter((n) => n.toLowerCase().startsWith(lower));
8470
- } }
8471
- }), {
8472
- title: "Design-system component",
8473
- description: "Props, variants, parts, tokens, guidelines and examples for one component.",
8474
- mimeType: "text/markdown"
8475
- }, async (uri, { name }) => {
8476
- const wanted = decodeURIComponent(String(name).split("?")[0] ?? "");
8477
- const found = await scope(uri.searchParams.get("path") ?? void 0);
8478
- if ("catalog" in found) {
8479
- const matches = found.catalog.find(wanted);
8480
- const [entry] = matches;
8481
- if (!entry) throw new Error(`No component named "${wanted}".`);
8482
- if (matches.length > 1) throw new Error(renderAmbiguous(wanted, matches));
8483
- return { contents: [{
8484
- uri: uri.href,
8485
- mimeType: "text/markdown",
8486
- text: renderComponent(entry.ds, entry.component, entry)
8487
- }] };
8488
- }
8489
- const { ds } = found;
8490
- const component = ds.getComponent(wanted);
8491
- if (!component) throw new Error(`No component named "${wanted}".`);
8492
- const text = renderComponent(ds, fromRoot(ds, component, found.root));
8493
- return { contents: [{
8494
- uri: uri.href,
8495
- mimeType: "text/markdown",
8496
- text
8497
- }] };
8498
- });
8499
- server.registerResource("tokens", "ds://tokens", {
8500
- title: "Design tokens",
8501
- description: "Every design token with resolved values, modes and usages, as JSON.",
8502
- mimeType: "application/json"
8503
- }, async (uri) => {
8504
- const found = await scope();
8505
- const tokens = "catalog" in found ? found.catalog.getTokens().map(({ token, package: pkg, projects }) => ({
8506
- ...token,
8507
- package: pkg,
8508
- projects
8509
- })) : found.ds.getTokens();
8510
- return { contents: [{
8511
- uri: uri.href,
8512
- mimeType: "application/json",
8513
- text: JSON.stringify(tokens, null, 2)
8514
- }] };
8515
- });
8516
- server.registerPrompt("build-with-design-system", {
8517
- title: "Build UI with the design system",
8518
- description: "Build a piece of UI using only design-system components and tokens, then verify it with check_ui.",
8519
- argsSchema: z.object({ task: z.string().max(MAX_QUERY * 4).describe("What to build, e.g. \"a settings card that lets owners delete the workspace\".") })
8520
- }, async ({ task }) => {
8521
- const found = await scope();
8522
- const roots = "catalog" in found ? [...new Map(found.catalog.roots().map((e) => [e.component.name, e.component])).values()] : found.ds.roots();
8523
- const allTokens = "catalog" in found ? found.catalog.getTokens().map((t) => t.token) : found.ds.tokens;
8524
- const components = roots.map((c) => {
8525
- const extras = [...c.variants.map((v) => v.name), ...c.subcomponents.length ? [`+${c.subcomponents.length} parts`] : []];
8526
- return `${c.name}${extras.length ? ` (${extras.join(", ")})` : ""}`;
8527
- }).join(", ");
8528
- const counts = /* @__PURE__ */ new Map();
8529
- for (const token of allTokens) counts.set(token.category, (counts.get(token.category) ?? 0) + 1);
8530
- const tokens = [...counts].map(([category, n]) => `${n} ${category}`).join(", ");
8531
- return {
8532
- description: "Build UI with the design system",
8533
- messages: [{
8534
- role: "user",
8535
- content: {
8536
- type: "text",
8537
- text: `Build this UI with the project's design system: ${task}
8538
-
8539
- Work like this:
8540
- 1. Find components: search_components for each piece of the UI (for example "confirm a destructive action"), or list_components for an overview.
8541
- 2. Read the contract: get_component for every component you use. Use only the props and variant values it lists, and compose parts the way its examples do.
8542
- 3. Style with tokens: get_tokens when you need a color, spacing or radius. Never write hex/rgb colors, arbitrary px values or Tailwind default palette classes such as bg-blue-500.
8543
- 4. Use design-system components instead of native elements (<Button>, not <button>).
8544
- 5. Verify: run check_ui on the code you wrote, fix every error, and run it again until it reports none.
8545
-
8546
- Design system: ${components || "no components found"}.
8547
- Tokens: ${tokens || "none"}.${"catalog" in found ? `\n\nThis is a monorepo root with ${found.set.dirs.length} projects (${found.set.dirs.join(", ")}): pass \`path\` with the file you are writing to get_component, get_tokens and check_ui, so they answer for its project.` : ""}`
8548
- }
8549
- }]
8550
- };
8551
- });
8552
- return server;
8553
- }
8554
- //#endregion
8555
- //#region src/workspace.ts
8556
- /**
8557
- * What marks the folder a file belongs to: the nearest one at or above it with a config,
8558
- * a components.json, a package.json or an Nx project.json. The Claude Code hook finds a
8559
- * file's project the same way, so `check` from a workspace root and the hook agree on
8560
- * every file.
8561
- */
8562
- const PROJECT_MARKERS = [
8563
- ...CONFIG_FILES,
8564
- ...LEGACY_CONFIG_FILES,
8565
- "components.json",
8566
- "package.json",
8567
- "project.json"
8568
- ];
8569
- /**
8570
- * A monorepo seen from its root: every workspace package with a design system (an app
8571
- * that uses one, or the design-system package itself), and how to find the project of
8572
- * any file in it. Without a design system at the root, `check`, `inspect` and the MCP
8573
- * server work from here, each file checked by its own project's design system.
8574
- */
8575
- var Workspace = class {
8576
- root;
8577
- source;
8578
- projects;
8579
- others;
8580
- configFile;
8581
- options;
8582
- rootConfig;
8583
- bare = /* @__PURE__ */ new Map();
8584
- constructor(root, source, projects, others, configFile, options, rootConfig) {
8585
- this.root = root;
8586
- this.source = source;
8587
- this.projects = projects;
8588
- this.others = others;
8589
- this.configFile = configFile;
8590
- this.options = options;
8591
- this.rootConfig = rootConfig;
8592
- }
8593
- /**
8594
- * The project `target` (a file or a folder) belongs to: the nearest folder at or above
8595
- * it with one of the `PROJECT_MARKERS`, as the hook finds it, up to the workspace root. A folder that holds no design system still gets
8596
- * its config, so its files are checked as `check` from that folder would check them.
8597
- */
8598
- async projectFor(target) {
8599
- const dir = projectDirOf(path.resolve(this.root, target), this.root);
8600
- const known = this.projects.find((p) => p.root === dir);
8601
- if (known) return known;
8602
- let project = this.bare.get(dir);
8603
- if (!project) {
8604
- project = dir === this.root ? Promise.resolve(this.rootProject()) : this.load(dir);
8605
- this.bare.set(dir, project);
8606
- }
8607
- return project;
8608
- }
8609
- rootProject() {
8610
- return {
8611
- root: this.root,
8612
- dir: ".",
8613
- name: packageName(this.root),
8614
- config: this.rootConfig
8615
- };
8616
- }
8617
- /** A project's config read again, as `check` from its folder would read it. */
8618
- configFor(project) {
8619
- return project.root === this.root ? loadConfig(this.options) : loadProjectConfig(project.root, this.root, this.configFile, this.options);
8620
- }
8621
- async load(dir) {
8622
- return {
8623
- root: dir,
8624
- dir: relativePath(this.root, dir) || ".",
8625
- name: packageName(dir),
8626
- config: await loadProjectConfig(dir, this.root, this.configFile, this.options)
8627
- };
8628
- }
8629
- };
8630
- /**
8631
- * The config at `root`, or a `Workspace` when `root` is a monorepo root with no design
8632
- * system of its own. The root is one project, as before, when a config file or
8633
- * `--components` sets the components, when `--config`, `--tokens` or `--docs` is given,
8634
- * or when zero config finds a design system there.
8635
- */
8636
- async function loadTarget(options) {
8637
- const config = await loadConfig(options);
8638
- if (options.config !== void 0 || Boolean(options.components?.length) || Boolean(options.tokens?.length) || Boolean(options.docs?.length) || config.configured || !isWorkspaceRoot(config.root)) return { config };
8639
- if (config.detected !== void 0 || (await componentFiles(config)).length) return { config };
8640
- return {
8641
- config,
8642
- workspace: await discoverWorkspace(config, options)
8643
- };
8644
- }
8645
- /** Every workspace package with a design system, and the rest by folder. */
8646
- async function discoverWorkspace(rootConfig, options) {
8647
- const { root } = rootConfig;
8648
- const source = [
8649
- workspacePatterns(root).source,
8650
- fs.existsSync(path.join(root, "nx.json")) ? "nx.json" : "",
8651
- fs.existsSync(path.join(root, "turbo.json")) ? "turbo.json" : ""
8652
- ].filter(Boolean).join(", ");
8653
- const projects = [];
8654
- const others = [];
8655
- for (const dir of workspacePackageDirs(root)) {
8656
- const config = await loadProjectConfig(dir, root, rootConfig.configFile, options);
8657
- const files = (await componentFiles(config)).length;
8658
- const own = config.configFile !== void 0 && config.configFile !== rootConfig.configFile;
8659
- if (files || own) projects.push({
8660
- root: dir,
8661
- dir: relativePath(root, dir),
8662
- name: packageName(dir),
8663
- config,
8664
- componentFiles: files
8665
- });
8666
- else others.push(relativePath(root, dir));
8667
- }
8668
- return new Workspace(root, source, projects, others, rootConfig.configFile, options, rootConfig);
8669
- }
8670
- /**
8671
- * A project's config: its own config file, or the workspace root's, with paths relative to
8672
- * the workspace root, or zero config. CLI flags that apply to every project carry over.
8673
- */
8674
- function loadProjectConfig(dir, workspaceRoot, rootConfigFile, options) {
8675
- const shared = {
8676
- cwd: options.cwd,
8677
- includeDesignSystem: options.includeDesignSystem
8678
- };
8679
- if ([...CONFIG_FILES, ...LEGACY_CONFIG_FILES].some((name) => fs.existsSync(path.join(dir, name))) || !rootConfigFile) return loadConfig({
8680
- ...shared,
8681
- root: dir
8682
- });
8683
- return loadConfig({
8684
- ...shared,
8685
- root: dir,
8686
- config: rootConfigFile,
8687
- base: workspaceRoot
8688
- });
8689
- }
8690
- /** The nearest folder at or above `target` (a file or folder) with a project marker, up to `root`. */
8691
- function projectDirOf(target, root) {
8692
- let dir = isDirectory(target) ? target : path.dirname(target);
8693
- while (isInside(root, dir) && dir !== root) {
8694
- if (PROJECT_MARKERS.some((name) => fs.existsSync(path.join(dir, name)))) return dir;
8695
- dir = path.dirname(dir);
8696
- }
8697
- return root;
8698
- }
8699
- function packageName(dir) {
8700
- try {
8701
- const name = JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf8")).name;
8702
- return typeof name === "string" ? name : void 0;
8703
- } catch {
8704
- return;
8705
- }
8706
- }
8707
- function isDirectory(target) {
8708
- try {
8709
- return fs.statSync(target).isDirectory();
8710
- } catch {
8711
- return false;
8712
- }
8713
- }
8714
- //#endregion
8715
- //#region src/catalog.ts
8716
- /**
8717
- * Every project's components and tokens in one list, for a workspace root when no path
8718
- * picks a project. A component used by several projects (a design-system package) is
8719
- * listed once, tagged with its package; two different components under one name are both
8720
- * listed, each with its package, and a lookup by that name returns both.
8721
- */
8722
- var Catalog = class {
8723
- root;
8724
- projects;
8725
- entries;
8726
- tokens;
8727
- index;
8728
- constructor(root, projects) {
8729
- this.root = root;
8730
- this.projects = projects;
8731
- const byKey = /* @__PURE__ */ new Map();
8732
- const owner = packageOwner(root);
8733
- for (const { ds, dir } of projects) for (const component of ds.components) {
8734
- const file = path.resolve(ds.root, component.source.file);
8735
- const key = `${file}\0${component.name}`;
8736
- const known = byKey.get(key);
8737
- if (known) {
8738
- if (!known.projects.includes(dir)) known.projects.push(dir);
8739
- if (isBare(component.importPath) && !isBare(known.component.importPath)) {
8740
- known.component = rebase(component, root, file);
8741
- known.ds = ds;
8742
- }
8743
- continue;
8744
- }
8745
- byKey.set(key, {
8746
- component: rebase(component, root, file),
8747
- ds,
8748
- package: owner(file),
8749
- projects: [dir],
8750
- ambiguous: false
8751
- });
8752
- }
8753
- this.entries = [...byKey.values()];
8754
- const names = /* @__PURE__ */ new Map();
8755
- for (const entry of this.entries) names.set(entry.component.name, (names.get(entry.component.name) ?? 0) + 1);
8756
- for (const entry of this.entries) entry.ambiguous = (names.get(entry.component.name) ?? 0) > 1;
8757
- this.index = buildSearchIndex(this.entries.map((e) => e.component));
8758
- const tokens = /* @__PURE__ */ new Map();
8759
- for (const { ds, dir } of projects) for (const token of ds.getTokens()) {
8760
- const file = path.resolve(ds.root, token.source.file);
8761
- const key = `${file}\0${token.name}\0${token.value}`;
8762
- const known = tokens.get(key);
8763
- if (known) {
8764
- if (!known.projects.includes(dir)) known.projects.push(dir);
8765
- continue;
8766
- }
8767
- tokens.set(key, {
8768
- token: {
8769
- ...token,
8770
- source: {
8771
- ...token.source,
8772
- file: relativePath(root, file)
8773
- }
8774
- },
8775
- ds,
8776
- package: owner(file),
8777
- projects: [dir]
8778
- });
8779
- }
8780
- this.tokens = [...tokens.values()];
8781
- }
8782
- /** Top-level components, not parts of another. */
8783
- roots() {
8784
- return this.entries.filter((e) => !e.component.parent);
8785
- }
8786
- /** Every distinct component a name resolves to, through each project's lookup. */
8787
- find(name) {
8788
- const found = [];
8789
- for (const { ds } of this.projects) {
8790
- const component = ds.getComponent(name);
8791
- if (!component) continue;
8792
- const file = path.resolve(ds.root, component.source.file);
8793
- const entry = this.entries.find((e) => e.component.name === component.name && path.resolve(this.root, e.component.source.file) === file);
8794
- if (entry && !found.includes(entry)) found.push(entry);
8795
- }
8796
- return found;
8797
- }
8798
- suggest(name) {
8799
- return closest(name, unique(this.entries.map((e) => e.component.name)), .5);
8800
- }
8801
- search(query, limit = 5) {
8802
- return searchComponents(this.index, query, limit).flatMap((hit) => {
8803
- const entry = this.entries.find((e) => e.component === hit.component);
8804
- return entry ? [{
8805
- ...hit,
8806
- entry
8807
- }] : [];
8808
- });
8809
- }
8810
- /** Tokens as `get_tokens` filters them, core ones before scoped families. */
8811
- getTokens(filter = {}) {
8812
- const query = filter.query?.toLowerCase();
8813
- const scoped = (t) => Number(t.ds.tokenIndex.scopedFamilyOf(t.token) !== void 0);
8814
- return this.tokens.filter(({ token: t }) => (!filter.category || t.category === filter.category) && (!query || t.name.toLowerCase().includes(query) || (t.description ?? "").toLowerCase().includes(query) || t.usage.some((u) => u.toLowerCase().includes(query)))).sort((a, b) => scoped(a) - scoped(b));
8815
- }
8816
- };
8817
- /** A specifier apps import by package name, not a relative path or a tsconfig alias. */
8818
- function isBare(specifier) {
8819
- return !/^(?:\.|@\/|~\/|#)/.test(specifier);
8820
- }
8821
- function rebase(component, root, file) {
8822
- return {
8823
- ...component,
8824
- source: {
8825
- ...component.source,
8826
- file: relativePath(root, file)
8827
- }
8828
- };
8829
- }
8830
- /** The name of the package a file belongs to (its nearest package.json), cached per folder. */
8831
- function packageOwner(root) {
8832
- const names = /* @__PURE__ */ new Map();
8833
- return (file) => {
8834
- const visited = [];
8835
- let name;
8836
- for (let dir = path.dirname(file);; dir = path.dirname(dir)) {
8837
- const known = names.get(dir);
8838
- if (known !== void 0) {
8839
- name = known;
8840
- break;
8841
- }
8842
- visited.push(dir);
8843
- try {
8844
- const pkg = JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf8"));
8845
- name = typeof pkg.name === "string" ? pkg.name : relativePath(root, dir) || ".";
8846
- break;
8847
- } catch {}
8848
- if (dir === root || dir === path.dirname(dir)) {
8849
- name = relativePath(root, dir) || ".";
8850
- break;
8851
- }
8852
- }
8853
- for (const dir of visited) names.set(dir, name);
8854
- return name;
8855
- };
8856
- }
8857
- //#endregion
8858
- //#region src/server/projects.ts
8859
- /**
8860
- * A workspace root's projects for the MCP server: one `DesignSystemHost` per project,
8861
- * created on first use and watched from then on, and the merged catalog, rebuilt when a
8862
- * project reloads.
8863
- */
8864
- var WorkspaceProjects = class {
8865
- workspace;
8866
- options;
8867
- hosts = /* @__PURE__ */ new Map();
8868
- merged;
8869
- constructor(workspace, options) {
8870
- this.workspace = workspace;
8871
- this.options = options;
8872
- }
8873
- get root() {
8874
- return this.workspace.root;
8875
- }
8876
- get dirs() {
8877
- return this.workspace.projects.map((p) => p.dir);
8878
- }
8879
- async forPath(target) {
8880
- const absolute = path.resolve(this.root, target);
8881
- if (!isInside(this.root, absolute)) throw new Error(`${target} is outside the workspace root (${this.root}).`);
8882
- const project = await this.workspace.projectFor(absolute);
8883
- return {
8884
- ds: await this.host(project).get(),
8885
- dir: project.dir,
8886
- name: project.name
8887
- };
8888
- }
8889
- async all() {
8890
- const served = [];
8891
- for (const project of this.workspace.projects) served.push({
8892
- ds: await this.host(project).get(),
8893
- dir: project.dir,
8894
- name: project.name
8895
- });
8896
- return served;
8897
- }
8898
- async catalog() {
8899
- const all = await this.all();
8900
- const from = all.map((p) => p.ds);
8901
- if (this.merged?.from.length !== from.length || this.merged.from.some((ds, i) => ds !== from[i])) this.merged = {
8902
- from,
8903
- catalog: new Catalog(this.root, all)
8904
- };
8905
- return this.merged.catalog;
8906
- }
8907
- close() {
8908
- for (const host of this.hosts.values()) host.close();
8909
- this.hosts.clear();
8910
- }
8911
- host(project) {
8912
- let host = this.hosts.get(project.root);
8913
- if (host) return host;
8914
- host = new DesignSystemHost(project.config, {
8915
- cache: this.options.cache,
8916
- logger: this.options.logger,
8917
- loadConfig: () => this.workspace.configFor(project)
8918
- });
8919
- host.onChange(() => this.options.onChange?.());
8920
- if (this.options.watch) host.watch();
8921
- this.hosts.set(project.root, host);
8922
- return host;
8923
- }
8924
- };
8925
- //#endregion
8926
- //#region src/server/stdio.ts
8927
- /**
8928
- * Starts the server (on stdio unless another transport is given), for clients on
8929
- * either protocol era: a 2026-07-28 client (stateless, `server/discover`) and a
8930
- * 2025-era one (the `initialize` handshake) are both served, the era chosen by the
8931
- * client's first message.
8932
- *
8933
- * The project root comes from `--root` or `--config`, a config file in the working
8934
- * directory, or the working directory itself when it holds a project (a
8935
- * package.json, a components.json). MCP roots, which the 2026-07-28 revision
8936
- * deprecates, are the fallback for a 2025-era client that starts servers somewhere
8937
- * else (Claude Desktop starts them in `/`): its first `file://` root. At a monorepo
8938
- * root with no design system of its own, every project in it is served.
8939
- */
8940
- function serveStdio$1(options) {
8941
- const { logger } = options;
8942
- let resolveHost = () => void 0;
8943
- let rejectHost = () => void 0;
8944
- const hostReady = new Promise((resolve, reject) => {
8945
- resolveHost = resolve;
8946
- rejectHost = reject;
8947
- });
8948
- hostReady.catch(() => void 0);
8949
- const instances = /* @__PURE__ */ new Set();
8950
- const notify = () => {
8951
- for (const server of instances) {
8952
- if (!server.isConnected()) continue;
8953
- server.server.sendResourceListChanged().catch((error) => {
8954
- logger.warn(`could not notify the client: ${error.message}`);
8955
- });
8956
- }
8957
- };
8958
- const closers = [];
8959
- let started = false;
8960
- let askingRoots = false;
8961
- const start = async (root) => {
8962
- if (started) return;
8963
- started = true;
8964
- try {
8965
- const { config, workspace } = await loadTarget({
8966
- ...options,
8967
- root
8968
- });
8969
- for (const text of config.deprecations ?? []) logger.warn(text);
8970
- if (workspace) {
8971
- const projects = new WorkspaceProjects(workspace, {
8972
- cache: options.cache,
8973
- watch: options.watch,
8974
- logger,
8975
- onChange: notify
8976
- });
8977
- logger.info(`monorepo root: ${workspace.projects.length} of ${workspace.projects.length + workspace.others.length} workspace packages have a design system${workspace.projects.length ? ` (${workspace.projects.map((p) => p.dir).join(", ")})` : ""}`);
8978
- closers.push(() => {
8979
- projects.close();
8980
- });
8981
- resolveHost({ projects });
8982
- return;
8983
- }
8984
- if (config.detected) logger.info(`found the design system through ${config.detected}`);
8985
- const host = new DesignSystemHost(config, {
8986
- cache: options.cache,
8987
- logger,
8988
- loadConfig: () => loadConfig({
8989
- ...options,
8990
- root
8991
- })
8992
- });
8993
- host.onChange(notify);
8994
- if (options.watch) host.watch();
8995
- host.get().catch((error) => {
8996
- logger.error(error.message);
8997
- });
8998
- closers.push(() => {
8999
- host.close();
9000
- });
9001
- resolveHost({ host });
9002
- } catch (error) {
9003
- logger.error(error.message);
9004
- rejectHost(error);
9005
- }
9006
- };
9007
- const configured = options.root !== void 0 || options.config !== void 0 || [...CONFIG_FILES, ...LEGACY_CONFIG_FILES].some((file) => fs.existsSync(path.join(options.cwd, file)));
9008
- const cwdIsProject = PROJECT_MARKERS.some((file) => fs.existsSync(path.join(options.cwd, file)));
9009
- if (configured || cwdIsProject) start(options.root);
9010
- /** The working directory, when nothing else has said where the project is by the first request. */
9011
- const ready = async () => {
9012
- if (!started && !askingRoots) start(void 0);
9013
- return hostReady;
9014
- };
9015
- const factory = ({ era }) => {
9016
- const server = createServer({
9017
- getDesignSystem: async () => {
9018
- const served = await ready();
9019
- if ("host" in served) return served.host.get();
9020
- throw new Error("This server serves a workspace root: pass a path.");
9021
- },
9022
- getProjects: async () => {
9023
- const served = await ready();
9024
- return "projects" in served ? served.projects : void 0;
9025
- }
9026
- });
9027
- instances.add(server);
9028
- if (!started && era === "legacy") server.server.oninitialized = () => {
9029
- askingRoots = true;
9030
- clientRoot(server).then((root) => {
9031
- if (root && root !== options.cwd) logger.info(`using client root ${root}`);
9032
- return start(root);
9033
- });
9034
- };
9035
- return server;
9036
- };
9037
- const handle = serveStdio(factory, {
9038
- ...options.transport ? { transport: options.transport } : {},
9039
- onerror: (error) => {
9040
- logger.warn(error.message);
9041
- }
9042
- });
9043
- logger.info(options.transport ? "ready" : "ready on stdio");
9044
- return Promise.resolve({ close: async () => {
9045
- for (const close of closers.splice(0)) close();
9046
- await handle.close();
9047
- } });
9048
- }
9049
- /** First `file://` root the client reports, if it supports roots (deprecated in 2026-07-28). */
9050
- async function clientRoot(server) {
9051
- if (!server.server.getClientCapabilities()?.roots) return void 0;
9052
- try {
9053
- const { roots } = await server.server.listRoots(void 0, { timeout: 3e3 });
9054
- const uri = roots.find((r) => r.uri.startsWith("file://"))?.uri;
9055
- return uri ? fileURLToPath(uri) : void 0;
9056
- } catch {
9057
- return;
9058
- }
9059
- }
9060
- //#endregion
9061
- export { CONFIG_FILES as A, loadConfig as B, loadTokens as C, attachDocs as D, parseCssTokens as E, DEFAULT_TEST_FOLDERS as F, unique as G, findVariantDefinitions as H, LEGACY_CONFIG_FILES as I, toPosix as J, matchesGlob as K, RULE_IDS as L, DEFAULT_DOCS as M, DEFAULT_SEVERITY as N, parseDoc as O, DEFAULT_TEST_EXCLUDE as P, configJsonSchema as R, TokenIndex as S, parseDtcgTokens as T, detectProject as U, rulesFor as V, plural$1 as W, RULES as _, DesignSystem as a, uncheckedNotice as b, componentFiles as c, tokenFiles as d, NAME as f, searchComponents as g, stderrLogger as h, createServer as i, ConfigError as j, extractComponents as k, fingerprint as l, silentLogger as m, loadTarget as n, DesignSystemHost as o, VERSION as p, relativePath as q, INSTRUCTIONS as r, buildModel as s, serveStdio$1 as t, loadDesignSystem as u, applyFixes as v, mergeTokens as w, formatDiagnostics as x, checkSource as y, defineConfig as z };
7841
+ export { plural$1 as $, parseDtcgTokens as A, DEFAULT_TEST_FOLDERS as B, updateBaseline as C, loadTokens as D, TokenIndex as E, CONFIG_FILES as F, loadConfig as G, RULE_IDS as H, ConfigError as I, detectProject as J, rulesFor as K, DEFAULT_DOCS as L, attachDocs as M, parseDoc as N, mergeTokens as O, extractComponents as P, closest as Q, DEFAULT_SEVERITY as R, readBaseline as S, RULES as T, configJsonSchema as U, LEGACY_CONFIG_FILES as V, defineConfig as W, workspacePackageDirs as X, isWorkspaceRoot as Y, workspacePatterns as Z, VERSION as _, fingerprint as a, toPosix as at, applyBaseline as b, silentLogger as c, searchComponents as d, truncate as et, applyFixes as f, NAME as g, formatDiagnostics as h, componentFiles as i, relativePath as it, parseCssTokens as j, formatPx as k, stderrLogger as l, uncheckedNotice as m, DesignSystemHost as n, isInside as nt, loadDesignSystem as o, checkSource as p, findVariantDefinitions as q, buildModel as r, matchesGlob as rt, tokenFiles as s, DesignSystem as t, unique as tt, buildSearchIndex as u, BASELINE_FILE as v, writeBaseline as w, countBaseline as x, LEGACY_BASELINE_FILE as y, DEFAULT_TEST_EXCLUDE as z };