@dereekb/dbx-components-mcp 13.10.6 → 13.10.7

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.
@@ -5,7 +5,7 @@ const require = __createRequire(import.meta.url);
5
5
  // packages/dbx-components-mcp/package.json
6
6
  var package_default = {
7
7
  name: "@dereekb/dbx-components-mcp",
8
- version: "13.10.6",
8
+ version: "13.10.7",
9
9
  type: "module",
10
10
  bin: {
11
11
  "dbx-components-mcp": "dbx-components-mcp.js"
@@ -1987,13 +1987,13 @@ function parseDecoratorInput(property) {
1987
1987
  }
1988
1988
  }
1989
1989
  const required = requiredFromDecorator ?? !property.hasQuestionToken();
1990
- const type65 = property.getTypeNode()?.getText() ?? "unknown";
1990
+ const type68 = property.getTypeNode()?.getText() ?? "unknown";
1991
1991
  const initializer = property.getInitializer();
1992
1992
  const defaultValue = initializer === void 0 ? void 0 : initializer.getText();
1993
1993
  return {
1994
1994
  propertyName,
1995
1995
  alias: alias ?? propertyName,
1996
- type: type65,
1996
+ type: type68,
1997
1997
  required,
1998
1998
  description: readPropertyDescription(property),
1999
1999
  ...defaultValue === void 0 ? {} : { defaultValue }
@@ -2059,22 +2059,22 @@ function parseDecoratorOutput(property) {
2059
2059
  alias = decoratorArg.getLiteralText();
2060
2060
  }
2061
2061
  const initializer = property.getInitializer();
2062
- let type65 = "unknown";
2062
+ let type68 = "unknown";
2063
2063
  if (initializer !== void 0 && Node4.isNewExpression(initializer)) {
2064
2064
  const typeArgs = initializer.getTypeArguments();
2065
2065
  if (typeArgs.length > 0) {
2066
- type65 = typeArgs[0].getText();
2066
+ type68 = typeArgs[0].getText();
2067
2067
  }
2068
2068
  } else {
2069
2069
  const explicit = property.getTypeNode()?.getText();
2070
2070
  if (explicit !== void 0) {
2071
- type65 = explicit;
2071
+ type68 = explicit;
2072
2072
  }
2073
2073
  }
2074
2074
  return {
2075
2075
  propertyName,
2076
2076
  name: alias ?? propertyName,
2077
- type: type65,
2077
+ type: type68,
2078
2078
  description: readPropertyDescription(property)
2079
2079
  };
2080
2080
  }
@@ -2089,7 +2089,7 @@ function parseSignalOutput(property) {
2089
2089
  }
2090
2090
  const propertyName = property.getName();
2091
2091
  const typeArgs = initializer.getTypeArguments();
2092
- const type65 = typeArgs.length > 0 ? typeArgs[0].getText() : "void";
2092
+ const type68 = typeArgs.length > 0 ? typeArgs[0].getText() : "void";
2093
2093
  const args = initializer.getArguments();
2094
2094
  let alias;
2095
2095
  if (args.length > 0 && Node4.isObjectLiteralExpression(args[0])) {
@@ -2098,7 +2098,7 @@ function parseSignalOutput(property) {
2098
2098
  return {
2099
2099
  propertyName,
2100
2100
  name: alias ?? propertyName,
2101
- type: type65,
2101
+ type: type68,
2102
2102
  description: readPropertyDescription(property)
2103
2103
  };
2104
2104
  }
@@ -2704,7 +2704,13 @@ var DbxMcpConfig = type13({
2704
2704
  "forgeFields?": ClusterSection,
2705
2705
  "pipes?": ClusterSection,
2706
2706
  "actions?": ClusterSection,
2707
- "filters?": ClusterSection
2707
+ "filters?": ClusterSection,
2708
+ "tokens?": ClusterSection,
2709
+ "uiSmellCheck?": {
2710
+ "projectConventions?": {
2711
+ "cardWrapperClasses?": "string[]"
2712
+ }
2713
+ }
2708
2714
  });
2709
2715
 
2710
2716
  // packages/dbx-components-mcp/src/config/load-config.ts
@@ -4140,11 +4146,241 @@ async function loadSemanticTypeRegistry(input) {
4140
4146
  return result;
4141
4147
  }
4142
4148
 
4143
- // packages/dbx-components-mcp/src/manifest/load-ui-components-registry.ts
4149
+ // packages/dbx-components-mcp/src/manifest/load-tokens-registry.ts
4144
4150
  import { existsSync as existsSync6 } from "node:fs";
4145
4151
  import { dirname as dirname6, isAbsolute as isAbsolute6, resolve as resolve12 } from "node:path";
4146
4152
  import { fileURLToPath as fileURLToPath6 } from "node:url";
4147
4153
 
4154
+ // packages/dbx-components-mcp/src/registry/tokens-runtime.ts
4155
+ function createTokenRegistry(loaded) {
4156
+ const entries = Array.from(loaded.entries.values()).sort((a, b) => a.cssVariable.localeCompare(b.cssVariable));
4157
+ return createTokenRegistryFromEntries({ entries, loadedSources: loaded.loadedSources });
4158
+ }
4159
+ function createTokenRegistryFromEntries(input) {
4160
+ const all = [...input.entries].sort((a, b) => a.cssVariable.localeCompare(b.cssVariable));
4161
+ const bySource = /* @__PURE__ */ new Map();
4162
+ const byRole = /* @__PURE__ */ new Map();
4163
+ const byCssVariable = /* @__PURE__ */ new Map();
4164
+ const byScssVariable = /* @__PURE__ */ new Map();
4165
+ const byComponent = /* @__PURE__ */ new Map();
4166
+ for (const entry of all) {
4167
+ pushInto6(bySource, entry.source, entry);
4168
+ pushInto6(byRole, entry.role, entry);
4169
+ if (!byCssVariable.has(entry.cssVariable)) {
4170
+ byCssVariable.set(entry.cssVariable, entry);
4171
+ }
4172
+ if (entry.scssVariable !== void 0 && !byScssVariable.has(entry.scssVariable)) {
4173
+ byScssVariable.set(entry.scssVariable, entry);
4174
+ }
4175
+ if (entry.componentScope !== void 0) {
4176
+ pushInto6(byComponent, entry.componentScope, entry);
4177
+ }
4178
+ }
4179
+ const bySourceImmutable = freezeBuckets(bySource);
4180
+ const byRoleImmutable = freezeBuckets(byRole);
4181
+ const registry = {
4182
+ all,
4183
+ loadedSources: [...input.loadedSources],
4184
+ bySource: bySourceImmutable,
4185
+ byRole: byRoleImmutable,
4186
+ findByCssVariable(name) {
4187
+ return byCssVariable.get(name);
4188
+ },
4189
+ findByScssVariable(name) {
4190
+ return byScssVariable.get(name);
4191
+ },
4192
+ findByIntent(query, role) {
4193
+ const trimmed = query.trim().toLowerCase();
4194
+ let matches;
4195
+ if (trimmed.length === 0) {
4196
+ matches = [];
4197
+ } else {
4198
+ const candidates = role !== void 0 ? byRoleImmutable.get(role) ?? [] : all;
4199
+ matches = [];
4200
+ for (const entry of candidates) {
4201
+ let best = 0;
4202
+ for (const intent of entry.intents) {
4203
+ const lower = intent.toLowerCase();
4204
+ let score = 0;
4205
+ if (lower === trimmed) {
4206
+ score = 10;
4207
+ } else if (lower.includes(trimmed)) {
4208
+ score = 5;
4209
+ } else if (trimmed.includes(lower) && lower.length > 0) {
4210
+ score = 3;
4211
+ }
4212
+ if (score > best) {
4213
+ best = score;
4214
+ }
4215
+ }
4216
+ if (best > 0) {
4217
+ matches.push({ entry, score: best });
4218
+ }
4219
+ }
4220
+ matches.sort((a, b) => b.score - a.score);
4221
+ }
4222
+ return matches;
4223
+ },
4224
+ findByValue(value, role) {
4225
+ const trimmed = value.trim();
4226
+ let matches;
4227
+ if (trimmed.length === 0) {
4228
+ matches = [];
4229
+ } else {
4230
+ const candidates = role !== void 0 ? byRoleImmutable.get(role) ?? [] : all;
4231
+ matches = [];
4232
+ for (const entry of candidates) {
4233
+ let score = 0;
4234
+ if (entry.defaults.light !== void 0 && entry.defaults.light === trimmed) score = 10;
4235
+ if (entry.defaults.dark !== void 0 && entry.defaults.dark === trimmed && score < 10) score = 9;
4236
+ if (score === 0 && entry.defaults.light !== void 0 && entry.defaults.light.includes(trimmed)) score = 4;
4237
+ if (score === 0 && entry.defaults.dark !== void 0 && entry.defaults.dark.includes(trimmed)) score = 3;
4238
+ if (score > 0) {
4239
+ matches.push({ entry, score });
4240
+ }
4241
+ }
4242
+ matches.sort((a, b) => b.score - a.score);
4243
+ }
4244
+ return matches;
4245
+ },
4246
+ findByComponent(component) {
4247
+ return byComponent.get(component) ?? [];
4248
+ }
4249
+ };
4250
+ return registry;
4251
+ }
4252
+ var EMPTY_TOKEN_REGISTRY = createTokenRegistryFromEntries({ entries: [], loadedSources: [] });
4253
+ function pushInto6(map, key, entry) {
4254
+ const existing = map.get(key);
4255
+ if (existing === void 0) {
4256
+ map.set(key, [entry]);
4257
+ } else {
4258
+ existing.push(entry);
4259
+ }
4260
+ }
4261
+ function freezeBuckets(map) {
4262
+ const out = /* @__PURE__ */ new Map();
4263
+ for (const [key, list] of map) {
4264
+ out.set(key, [...list]);
4265
+ }
4266
+ return out;
4267
+ }
4268
+
4269
+ // packages/dbx-components-mcp/src/manifest/tokens-schema.ts
4270
+ import { type as type20 } from "arktype";
4271
+ var TOKEN_ROLES = ["color", "text-color", "surface", "spacing", "radius", "elevation", "shadow", "typography", "motion", "state-layer", "size", "breakpoint", "misc"];
4272
+ var TOKEN_SOURCES = ["dbx-web", "mat-sys", "mdc", "app"];
4273
+ var TokenDefaults = type20({
4274
+ "light?": "string",
4275
+ "dark?": "string"
4276
+ });
4277
+ var TokenSourceLocation = type20({
4278
+ file: "string",
4279
+ line: "number"
4280
+ });
4281
+ var TokenEntry = type20({
4282
+ cssVariable: "string",
4283
+ "scssVariable?": "string",
4284
+ source: '"dbx-web" | "mat-sys" | "mdc" | "app"',
4285
+ role: '"color" | "text-color" | "surface" | "spacing" | "radius" | "elevation" | "shadow" | "typography" | "motion" | "state-layer" | "size" | "breakpoint" | "misc"',
4286
+ intents: "string[]",
4287
+ description: "string",
4288
+ "antiUseNotes?": "string",
4289
+ defaults: TokenDefaults,
4290
+ "utilityClasses?": "string[]",
4291
+ "componentScope?": "string",
4292
+ "recommendedPrimitive?": "string",
4293
+ "seeAlso?": "string[]",
4294
+ "sourceLocation?": TokenSourceLocation
4295
+ });
4296
+ var TokenManifest = type20({
4297
+ version: "1",
4298
+ source: "string",
4299
+ module: "string",
4300
+ generatedAt: "string",
4301
+ generator: "string",
4302
+ entries: TokenEntry.array()
4303
+ });
4304
+
4305
+ // packages/dbx-components-mcp/src/manifest/tokens-loader.ts
4306
+ async function loadTokenManifests(input) {
4307
+ const { entries, indexMap, warnings, loadedSources } = await loadManifestsBase(input, {
4308
+ name: "loadTokenManifests",
4309
+ schema: TokenManifest,
4310
+ extractIndexValue: (entry) => entry.role,
4311
+ buildEntryKey: (_manifest, entry) => `${entry.source}::${entry.cssVariable}`
4312
+ });
4313
+ return { entries, roleIndex: indexMap, warnings, loadedSources };
4314
+ }
4315
+
4316
+ // packages/dbx-components-mcp/src/manifest/load-tokens-registry.ts
4317
+ var DEFAULT_BUNDLED_FILENAMES6 = ["dereekb-dbx-web.tokens.mcp.generated.json", "angular-material-m3.tokens.mcp.generated.json", "angular-material-mdc.tokens.mcp.generated.json"];
4318
+ function findPackageRoot6(startUrl) {
4319
+ const startPath = fileURLToPath6(startUrl);
4320
+ let dir = dirname6(startPath);
4321
+ let result;
4322
+ while (result === void 0) {
4323
+ if (existsSync6(resolve12(dir, "package.json"))) {
4324
+ result = dir;
4325
+ } else {
4326
+ const parent = dirname6(dir);
4327
+ if (parent === dir) {
4328
+ throw new Error(`findPackageRoot: no package.json found above ${startPath}`);
4329
+ }
4330
+ dir = parent;
4331
+ }
4332
+ }
4333
+ return result;
4334
+ }
4335
+ var DEFAULT_BUNDLED_PATHS6 = () => {
4336
+ const packageRoot = findPackageRoot6(import.meta.url);
4337
+ return DEFAULT_BUNDLED_FILENAMES6.map((name) => resolve12(packageRoot, "generated", name));
4338
+ };
4339
+ async function loadTokenRegistry(input) {
4340
+ const { cwd, bundledManifestPaths = DEFAULT_BUNDLED_PATHS6, readFile: readFile20 } = input;
4341
+ const bundledSources = bundledManifestPaths().map((path) => ({ origin: "bundled", path }));
4342
+ const configResult = await findAndLoadConfig({ cwd, readFile: readFile20 });
4343
+ const externalSources = [];
4344
+ if (configResult.config !== null && configResult.configPath !== null) {
4345
+ const baseDir = dirname6(configResult.configPath);
4346
+ const tokensCluster = configResult.config.tokens;
4347
+ const declaredSources = tokensCluster?.sources ?? [];
4348
+ for (const source of declaredSources) {
4349
+ const absolute = isAbsolute6(source) ? source : resolve12(baseDir, source);
4350
+ externalSources.push({ origin: "external", path: absolute });
4351
+ }
4352
+ const declaredScans = tokensCluster?.scan ?? [];
4353
+ for (const scan of declaredScans) {
4354
+ const out = scan.out;
4355
+ if (typeof out === "string" && out.length > 0) {
4356
+ const absolute = isAbsolute6(out) ? out : resolve12(baseDir, out);
4357
+ externalSources.push({ origin: "external", path: absolute });
4358
+ }
4359
+ }
4360
+ }
4361
+ let registry = EMPTY_TOKEN_REGISTRY;
4362
+ let loaderWarnings = [];
4363
+ const sources = [...bundledSources, ...externalSources];
4364
+ if (sources.length > 0) {
4365
+ const loaded = await loadTokenManifests({ sources, readFile: readFile20 });
4366
+ registry = createTokenRegistry(loaded);
4367
+ loaderWarnings = loaded.warnings;
4368
+ }
4369
+ const result = {
4370
+ registry,
4371
+ configPath: configResult.configPath,
4372
+ configWarnings: configResult.warnings,
4373
+ loaderWarnings,
4374
+ externalSourceCount: externalSources.length
4375
+ };
4376
+ return result;
4377
+ }
4378
+
4379
+ // packages/dbx-components-mcp/src/manifest/load-ui-components-registry.ts
4380
+ import { existsSync as existsSync7 } from "node:fs";
4381
+ import { dirname as dirname7, isAbsolute as isAbsolute7, resolve as resolve13 } from "node:path";
4382
+ import { fileURLToPath as fileURLToPath7 } from "node:url";
4383
+
4148
4384
  // packages/dbx-components-mcp/src/registry/ui-components-runtime.ts
4149
4385
  function createUiComponentRegistry(loaded) {
4150
4386
  const entries = Array.from(loaded.entries.values()).sort((a, b) => a.slug.localeCompare(b.slug));
@@ -4159,10 +4395,10 @@ function createUiComponentRegistryFromEntries(input) {
4159
4395
  const byClassName = /* @__PURE__ */ new Map();
4160
4396
  const bySelectorToken = /* @__PURE__ */ new Map();
4161
4397
  for (const entry of all) {
4162
- pushInto6(bySlug, entry.slug, entry);
4163
- pushInto6(byCategory, entry.category, entry);
4164
- pushInto6(byKind, entry.kind, entry);
4165
- pushInto6(byModule, entry.module, entry);
4398
+ pushInto7(bySlug, entry.slug, entry);
4399
+ pushInto7(byCategory, entry.category, entry);
4400
+ pushInto7(byKind, entry.kind, entry);
4401
+ pushInto7(byModule, entry.module, entry);
4166
4402
  byClassName.set(entry.className.toLowerCase(), entry);
4167
4403
  for (const token of splitSelectorTokens(entry.selector)) {
4168
4404
  if (!bySelectorToken.has(token)) {
@@ -4214,7 +4450,7 @@ ${entry.description}`.toLowerCase();
4214
4450
  return registry;
4215
4451
  }
4216
4452
  var EMPTY_UI_COMPONENT_REGISTRY = createUiComponentRegistryFromEntries({ entries: [], loadedSources: [] });
4217
- function pushInto6(map, key, entry) {
4453
+ function pushInto7(map, key, entry) {
4218
4454
  const existing = map.get(key);
4219
4455
  if (existing === void 0) {
4220
4456
  map.set(key, [entry]);
@@ -4248,16 +4484,16 @@ async function loadUiComponentManifests(input) {
4248
4484
  }
4249
4485
 
4250
4486
  // packages/dbx-components-mcp/src/manifest/load-ui-components-registry.ts
4251
- var DEFAULT_BUNDLED_FILENAMES6 = ["dereekb-dbx-web.ui-components.mcp.generated.json"];
4252
- function findPackageRoot6(startUrl) {
4253
- const startPath = fileURLToPath6(startUrl);
4254
- let dir = dirname6(startPath);
4487
+ var DEFAULT_BUNDLED_FILENAMES7 = ["dereekb-dbx-web.ui-components.mcp.generated.json"];
4488
+ function findPackageRoot7(startUrl) {
4489
+ const startPath = fileURLToPath7(startUrl);
4490
+ let dir = dirname7(startPath);
4255
4491
  let result;
4256
4492
  while (result === void 0) {
4257
- if (existsSync6(resolve12(dir, "package.json"))) {
4493
+ if (existsSync7(resolve13(dir, "package.json"))) {
4258
4494
  result = dir;
4259
4495
  } else {
4260
- const parent = dirname6(dir);
4496
+ const parent = dirname7(dir);
4261
4497
  if (parent === dir) {
4262
4498
  throw new Error(`findPackageRoot: no package.json found above ${startPath}`);
4263
4499
  }
@@ -4266,20 +4502,20 @@ function findPackageRoot6(startUrl) {
4266
4502
  }
4267
4503
  return result;
4268
4504
  }
4269
- var DEFAULT_BUNDLED_PATHS6 = () => {
4270
- const packageRoot = findPackageRoot6(import.meta.url);
4271
- return DEFAULT_BUNDLED_FILENAMES6.map((name) => resolve12(packageRoot, "generated", name));
4505
+ var DEFAULT_BUNDLED_PATHS7 = () => {
4506
+ const packageRoot = findPackageRoot7(import.meta.url);
4507
+ return DEFAULT_BUNDLED_FILENAMES7.map((name) => resolve13(packageRoot, "generated", name));
4272
4508
  };
4273
4509
  async function loadUiComponentRegistry(input) {
4274
- const { cwd, bundledManifestPaths = DEFAULT_BUNDLED_PATHS6, readFile: readFile20 } = input;
4510
+ const { cwd, bundledManifestPaths = DEFAULT_BUNDLED_PATHS7, readFile: readFile20 } = input;
4275
4511
  const bundledSources = bundledManifestPaths().map((path) => ({ origin: "bundled", path }));
4276
4512
  const configResult = await findAndLoadConfig({ cwd, readFile: readFile20 });
4277
4513
  const externalSources = [];
4278
4514
  if (configResult.config !== null && configResult.configPath !== null) {
4279
- const baseDir = dirname6(configResult.configPath);
4515
+ const baseDir = dirname7(configResult.configPath);
4280
4516
  const declared = configResult.config.uiComponents?.sources ?? [];
4281
4517
  for (const source of declared) {
4282
- const absolute = isAbsolute6(source) ? source : resolve12(baseDir, source);
4518
+ const absolute = isAbsolute7(source) ? source : resolve13(baseDir, source);
4283
4519
  externalSources.push({ origin: "external", path: absolute });
4284
4520
  }
4285
4521
  }
@@ -5294,11 +5530,11 @@ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
5294
5530
  var FORM_TIER_ORDER = ["field-factory", "field-derivative", "composite-builder", "template-builder", "primitive"];
5295
5531
 
5296
5532
  // packages/dbx-components-mcp/src/registry/downstream-models-runtime.ts
5297
- import { resolve as resolve14 } from "node:path";
5533
+ import { resolve as resolve15 } from "node:path";
5298
5534
 
5299
5535
  // packages/dbx-components-mcp/src/scan/discover-firebase-packages.ts
5300
5536
  import { glob as fsGlob2, readFile, stat } from "node:fs/promises";
5301
- import { basename, join, resolve as resolve13, sep } from "node:path";
5537
+ import { basename, join, resolve as resolve14, sep } from "node:path";
5302
5538
  var COMPONENTS_GLOB = "components/*-firebase";
5303
5539
  var MODEL_SUBPATH = "src/lib/model";
5304
5540
  var PACKAGE_JSON = "package.json";
@@ -5309,7 +5545,7 @@ async function discoverDownstreamFirebasePackages(workspaceRoot) {
5309
5545
  const componentDir = match.split(sep).join("/");
5310
5546
  if (seen.has(componentDir)) continue;
5311
5547
  seen.add(componentDir);
5312
- const componentAbs = resolve13(workspaceRoot, componentDir);
5548
+ const componentAbs = resolve14(workspaceRoot, componentDir);
5313
5549
  const pkg = await inspectPackage(componentAbs, componentDir);
5314
5550
  if (pkg) out.push(pkg);
5315
5551
  }
@@ -5323,7 +5559,7 @@ async function resolveExplicitFirebasePackages(workspaceRoot, componentDirs) {
5323
5559
  const componentDir = raw.split(sep).join("/").replace(/\/+$/, "");
5324
5560
  if (seen.has(componentDir) || componentDir.length === 0) continue;
5325
5561
  seen.add(componentDir);
5326
- const componentAbs = resolve13(workspaceRoot, componentDir);
5562
+ const componentAbs = resolve14(workspaceRoot, componentDir);
5327
5563
  const pkg = await inspectPackage(componentAbs, componentDir);
5328
5564
  if (pkg) out.push(pkg);
5329
5565
  }
@@ -5956,13 +6192,13 @@ function getDownstreamCatalog(input) {
5956
6192
  return pending;
5957
6193
  }
5958
6194
  function cacheKey(input) {
5959
- const root = resolve14(input.workspaceRoot);
6195
+ const root = resolve15(input.workspaceRoot);
5960
6196
  const dirs = input.componentDirs;
5961
6197
  const dirKey = dirs ? [...dirs].slice().sort((a, b) => a.localeCompare(b)).join(",") : "*";
5962
6198
  return `${root}|${dirKey}`;
5963
6199
  }
5964
6200
  async function buildCatalog(input) {
5965
- const workspaceRoot = resolve14(input.workspaceRoot);
6201
+ const workspaceRoot = resolve15(input.workspaceRoot);
5966
6202
  const discoveryUsed = input.componentDirs === void 0;
5967
6203
  const packages = discoveryUsed ? await discoverDownstreamFirebasePackages(workspaceRoot) : await resolveExplicitFirebasePackages(workspaceRoot, input.componentDirs ?? []);
5968
6204
  const errors = [];
@@ -5972,7 +6208,7 @@ async function buildCatalog(input) {
5972
6208
  packages.map(async (pkg) => {
5973
6209
  try {
5974
6210
  const result = await extractModels({
5975
- rootDir: resolve14(workspaceRoot, pkg.modelDir),
6211
+ rootDir: resolve15(workspaceRoot, pkg.modelDir),
5976
6212
  sourcePackage: pkg.packageName,
5977
6213
  workspaceRoot,
5978
6214
  skipReservedFolders: RESERVED_FOLDER_NAMES
@@ -6942,6 +7178,141 @@ function registerSemanticTypesResource(server, config) {
6942
7178
  );
6943
7179
  }
6944
7180
 
7181
+ // packages/dbx-components-mcp/src/resources/tokens.resource.ts
7182
+ import { ResourceTemplate as ResourceTemplate8 } from "@modelcontextprotocol/sdk/server/mcp.js";
7183
+ var TOKENS_URI = "dbx://token/entries";
7184
+ var TOKENS_BY_SOURCE_TEMPLATE = "dbx://token/source/{source}";
7185
+ var TOKENS_BY_ROLE_TEMPLATE = "dbx://token/role/{role}";
7186
+ var TOKENS_BY_VAR_TEMPLATE = "dbx://token/css-var/{cssVariable}";
7187
+ function registerTokensResource(server, options) {
7188
+ const { registry } = options;
7189
+ server.registerResource(
7190
+ "dbx-components Tokens",
7191
+ TOKENS_URI,
7192
+ {
7193
+ title: "Design Tokens",
7194
+ description: "Catalog of dbx-web + Angular Material design tokens (system, MDC, dbx-web aliases).",
7195
+ mimeType: "application/json"
7196
+ },
7197
+ async () => {
7198
+ const payload = {
7199
+ description: "Every registered design token.",
7200
+ sourceOrder: TOKEN_SOURCES,
7201
+ roleOrder: TOKEN_ROLES,
7202
+ tokens: registry.all.map((t) => ({
7203
+ cssVariable: t.cssVariable,
7204
+ source: t.source,
7205
+ role: t.role,
7206
+ description: t.description
7207
+ }))
7208
+ };
7209
+ return {
7210
+ contents: [
7211
+ {
7212
+ uri: TOKENS_URI,
7213
+ mimeType: "application/json",
7214
+ text: JSON.stringify(payload, null, 2)
7215
+ }
7216
+ ]
7217
+ };
7218
+ }
7219
+ );
7220
+ server.registerResource(
7221
+ "dbx-components Tokens by Source",
7222
+ new ResourceTemplate8(TOKENS_BY_SOURCE_TEMPLATE, { list: void 0 }),
7223
+ {
7224
+ title: "Tokens by Source",
7225
+ description: "Tokens filtered by origin: dbx-web, mat-sys, mdc, app.",
7226
+ mimeType: "application/json"
7227
+ },
7228
+ async (uri, variables) => {
7229
+ const rawSource = variables.source;
7230
+ const source = Array.isArray(rawSource) ? rawSource[0] : rawSource;
7231
+ const valid = source !== void 0 && TOKEN_SOURCES.includes(source);
7232
+ let text;
7233
+ if (valid && source !== void 0) {
7234
+ const tokens = registry.bySource.get(source) ?? [];
7235
+ text = JSON.stringify({ source, tokens }, null, 2);
7236
+ } else {
7237
+ text = `Invalid source. Valid values: ${TOKEN_SOURCES.join(", ")}`;
7238
+ }
7239
+ return {
7240
+ contents: [
7241
+ {
7242
+ uri: uri.href,
7243
+ mimeType: valid ? "application/json" : "text/plain",
7244
+ text
7245
+ }
7246
+ ]
7247
+ };
7248
+ }
7249
+ );
7250
+ server.registerResource(
7251
+ "dbx-components Tokens by Role",
7252
+ new ResourceTemplate8(TOKENS_BY_ROLE_TEMPLATE, { list: void 0 }),
7253
+ {
7254
+ title: "Tokens by Role",
7255
+ description: "Tokens filtered by role (color, surface, spacing, radius, ...).",
7256
+ mimeType: "application/json"
7257
+ },
7258
+ async (uri, variables) => {
7259
+ const rawRole = variables.role;
7260
+ const role = Array.isArray(rawRole) ? rawRole[0] : rawRole;
7261
+ const valid = role !== void 0 && TOKEN_ROLES.includes(role);
7262
+ let text;
7263
+ if (valid && role !== void 0) {
7264
+ const tokens = registry.byRole.get(role) ?? [];
7265
+ text = JSON.stringify({ role, tokens }, null, 2);
7266
+ } else {
7267
+ text = `Invalid role. Valid values: ${TOKEN_ROLES.join(", ")}`;
7268
+ }
7269
+ return {
7270
+ contents: [
7271
+ {
7272
+ uri: uri.href,
7273
+ mimeType: valid ? "application/json" : "text/plain",
7274
+ text
7275
+ }
7276
+ ]
7277
+ };
7278
+ }
7279
+ );
7280
+ server.registerResource(
7281
+ "dbx-components Token Details",
7282
+ new ResourceTemplate8(TOKENS_BY_VAR_TEMPLATE, { list: void 0 }),
7283
+ {
7284
+ title: "Token Details",
7285
+ description: "Full metadata for a single token by CSS variable name.",
7286
+ mimeType: "application/json"
7287
+ },
7288
+ async (uri, variables) => {
7289
+ const rawVar = variables.cssVariable;
7290
+ const cssVariable = Array.isArray(rawVar) ? rawVar[0] : rawVar;
7291
+ let text;
7292
+ let entry;
7293
+ if (cssVariable !== void 0) {
7294
+ entry = registry.findByCssVariable(cssVariable) ?? registry.findByScssVariable(cssVariable);
7295
+ }
7296
+ if (entry !== void 0) {
7297
+ text = JSON.stringify(entry, null, 2);
7298
+ } else if (cssVariable !== void 0) {
7299
+ text = `Token '${cssVariable}' not found.`;
7300
+ } else {
7301
+ text = "No cssVariable provided.";
7302
+ }
7303
+ return {
7304
+ contents: [
7305
+ {
7306
+ uri: uri.href,
7307
+ mimeType: entry !== void 0 ? "application/json" : "text/plain",
7308
+ text
7309
+ }
7310
+ ]
7311
+ };
7312
+ }
7313
+ );
7314
+ }
7315
+
6945
7316
  // packages/dbx-components-mcp/src/resources/index.ts
6946
7317
  function registerResources(server, options = {}) {
6947
7318
  if (options.forgeFieldRegistry !== void 0) {
@@ -6963,13 +7334,16 @@ function registerResources(server, options = {}) {
6963
7334
  if (options.semanticTypeRegistry !== void 0) {
6964
7335
  registerSemanticTypesResource(server, { registry: options.semanticTypeRegistry });
6965
7336
  }
7337
+ if (options.tokenRegistry !== void 0) {
7338
+ registerTokensResource(server, { registry: options.tokenRegistry });
7339
+ }
6966
7340
  }
6967
7341
 
6968
7342
  // packages/dbx-components-mcp/src/tools/index.ts
6969
7343
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
6970
7344
 
6971
7345
  // packages/dbx-components-mcp/src/tools/lookup-form.tool.ts
6972
- import { type as type20 } from "arktype";
7346
+ import { type as type21 } from "arktype";
6973
7347
 
6974
7348
  // packages/dbx-components-mcp/src/tools/form-alias-resolver.ts
6975
7349
  var ALIASES = {
@@ -7206,13 +7580,13 @@ var DBX_FORM_LOOKUP_TOOL = {
7206
7580
  required: ["topic"]
7207
7581
  }
7208
7582
  };
7209
- var LookupFormArgsType = type20({
7583
+ var LookupFormArgsType = type21({
7210
7584
  topic: "string",
7211
7585
  "depth?": "'brief' | 'full'"
7212
7586
  });
7213
7587
  function parseLookupFormArgs(raw) {
7214
7588
  const parsed = LookupFormArgsType(raw);
7215
- if (parsed instanceof type20.errors) {
7589
+ if (parsed instanceof type21.errors) {
7216
7590
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
7217
7591
  }
7218
7592
  return {
@@ -7334,14 +7708,14 @@ function createLookupFormTool(config) {
7334
7708
  }
7335
7709
 
7336
7710
  // packages/dbx-components-mcp/src/tools/_search/score.ts
7337
- import { type as type21 } from "arktype";
7338
- var SEARCH_ARGS_TYPE = type21({
7711
+ import { type as type22 } from "arktype";
7712
+ var SEARCH_ARGS_TYPE = type22({
7339
7713
  query: "string",
7340
7714
  "limit?": "number"
7341
7715
  });
7342
7716
  function parseSearchArgs(raw, config) {
7343
7717
  const parsed = SEARCH_ARGS_TYPE(raw);
7344
- if (parsed instanceof type21.errors) {
7718
+ if (parsed instanceof type22.errors) {
7345
7719
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
7346
7720
  }
7347
7721
  const rawLimit = parsed.limit ?? config.defaultLimit;
@@ -7539,7 +7913,7 @@ function createSearchFormTool(config) {
7539
7913
  }
7540
7914
 
7541
7915
  // packages/dbx-components-mcp/src/tools/form-examples.tool.ts
7542
- import { type as type22 } from "arktype";
7916
+ import { type as type23 } from "arktype";
7543
7917
 
7544
7918
  // packages/dbx-components-mcp/src/tools/data/patterns/forms/form.address-form.pattern.ts
7545
7919
  var FORM_PATTERN_ADDRESS_FORM = {
@@ -7828,13 +8202,13 @@ var DBX_FORM_EXAMPLES_TOOL = {
7828
8202
  required: ["pattern"]
7829
8203
  }
7830
8204
  };
7831
- var ExamplesArgsType = type22({
8205
+ var ExamplesArgsType = type23({
7832
8206
  pattern: "string",
7833
8207
  "depth?": "'minimal' | 'brief' | 'full'"
7834
8208
  });
7835
8209
  function parseExamplesArgs(raw) {
7836
8210
  const parsed = ExamplesArgsType(raw);
7837
- if (parsed instanceof type22.errors) {
8211
+ if (parsed instanceof type23.errors) {
7838
8212
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
7839
8213
  }
7840
8214
  const result = {
@@ -7895,7 +8269,7 @@ var formExamplesTool = {
7895
8269
  };
7896
8270
 
7897
8271
  // packages/dbx-components-mcp/src/tools/form-scaffold.tool.ts
7898
- import { type as type23 } from "arktype";
8272
+ import { type as type24 } from "arktype";
7899
8273
  var DEFAULT_VALUE_TYPE_NAME = "ScaffoldedFormValue";
7900
8274
  var DBX_FORM_SCAFFOLD_TOOL = {
7901
8275
  name: "dbx_form_scaffold",
@@ -7933,14 +8307,14 @@ var DBX_FORM_SCAFFOLD_TOOL = {
7933
8307
  required: ["fields"]
7934
8308
  }
7935
8309
  };
7936
- var ScaffoldArgsType = type23({
8310
+ var ScaffoldArgsType = type24({
7937
8311
  fields: "string[]>0",
7938
8312
  "valueTypeName?": "string",
7939
8313
  "wrapInSection?": "boolean"
7940
8314
  });
7941
8315
  function parseScaffoldArgs(raw) {
7942
8316
  const parsed = ScaffoldArgsType(raw);
7943
- if (parsed instanceof type23.errors) {
8317
+ if (parsed instanceof type24.errors) {
7944
8318
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
7945
8319
  }
7946
8320
  const result = {
@@ -8096,7 +8470,7 @@ ${code11}
8096
8470
  }
8097
8471
 
8098
8472
  // packages/dbx-components-mcp/src/tools/lookup-ui.tool.ts
8099
- import { type as type24 } from "arktype";
8473
+ import { type as type25 } from "arktype";
8100
8474
  var DBX_UI_LOOKUP_TOOL = {
8101
8475
  name: "dbx_ui_lookup",
8102
8476
  description: [
@@ -8126,13 +8500,13 @@ var DBX_UI_LOOKUP_TOOL = {
8126
8500
  required: ["topic"]
8127
8501
  }
8128
8502
  };
8129
- var LookupUiArgsType = type24({
8503
+ var LookupUiArgsType = type25({
8130
8504
  topic: "string",
8131
8505
  "depth?": "'brief' | 'full'"
8132
8506
  });
8133
8507
  function parseLookupUiArgs(raw) {
8134
8508
  const parsed = LookupUiArgsType(raw);
8135
- if (parsed instanceof type24.errors) {
8509
+ if (parsed instanceof type25.errors) {
8136
8510
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
8137
8511
  }
8138
8512
  const result = {
@@ -8382,7 +8756,7 @@ function createLookupUiTool(input) {
8382
8756
  }
8383
8757
 
8384
8758
  // packages/dbx-components-mcp/src/tools/search-ui.tool.ts
8385
- import { type as type25 } from "arktype";
8759
+ import { type as type26 } from "arktype";
8386
8760
  var DEFAULT_LIMIT2 = 10;
8387
8761
  var MAX_LIMIT2 = 25;
8388
8762
  var DBX_UI_SEARCH_TOOL = {
@@ -8419,14 +8793,14 @@ var DBX_UI_SEARCH_TOOL = {
8419
8793
  required: ["query"]
8420
8794
  }
8421
8795
  };
8422
- var SearchUiArgsType = type25({
8796
+ var SearchUiArgsType = type26({
8423
8797
  query: "string",
8424
8798
  "category?": "'layout' | 'list' | 'button' | 'card' | 'feedback' | 'overlay' | 'navigation' | 'text' | 'screen' | 'action' | 'router' | 'misc'",
8425
8799
  "limit?": "number"
8426
8800
  });
8427
8801
  function parseSearchUiArgs(raw) {
8428
8802
  const parsed = SearchUiArgsType(raw);
8429
- if (parsed instanceof type25.errors) {
8803
+ if (parsed instanceof type26.errors) {
8430
8804
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
8431
8805
  }
8432
8806
  const rawLimit = parsed.limit ?? DEFAULT_LIMIT2;
@@ -8579,7 +8953,7 @@ function createSearchUiTool(input) {
8579
8953
  }
8580
8954
 
8581
8955
  // packages/dbx-components-mcp/src/tools/ui-examples.tool.ts
8582
- import { type as type26 } from "arktype";
8956
+ import { type as type27 } from "arktype";
8583
8957
 
8584
8958
  // packages/dbx-components-mcp/src/tools/data/patterns/ui/ui.card-with-action.pattern.ts
8585
8959
  var UI_PATTERN_CARD_WITH_ACTION = {
@@ -8985,13 +9359,13 @@ var DBX_UI_EXAMPLES_TOOL = {
8985
9359
  required: ["pattern"]
8986
9360
  }
8987
9361
  };
8988
- var UiExamplesArgsType = type26({
9362
+ var UiExamplesArgsType = type27({
8989
9363
  pattern: "string",
8990
9364
  "depth?": "'minimal' | 'brief' | 'full'"
8991
9365
  });
8992
9366
  function parseUiExamplesArgs(raw) {
8993
9367
  const parsed = UiExamplesArgsType(raw);
8994
- if (parsed instanceof type26.errors) {
9368
+ if (parsed instanceof type27.errors) {
8995
9369
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
8996
9370
  }
8997
9371
  const result = {
@@ -9052,7 +9426,7 @@ var uiExamplesTool = {
9052
9426
  };
9053
9427
 
9054
9428
  // packages/dbx-components-mcp/src/tools/lookup-model.tool.ts
9055
- import { type as type27 } from "arktype";
9429
+ import { type as type28 } from "arktype";
9056
9430
 
9057
9431
  // packages/dbx-components-mcp/src/tools/firebase-lookup.formatter.ts
9058
9432
  function applyFieldsFilter(model, filter) {
@@ -9266,10 +9640,10 @@ function formatFirebaseStoreShapeTaxonomy() {
9266
9640
 
9267
9641
  // packages/dbx-components-mcp/src/tools/validate-input.ts
9268
9642
  import { glob as fsGlob3, readFile as readFile3, stat as stat3 } from "node:fs/promises";
9269
- import { resolve as resolve15, sep as sep3 } from "node:path";
9643
+ import { resolve as resolve16, sep as sep3 } from "node:path";
9270
9644
  function ensurePathInsideCwd(relative17, cwd) {
9271
9645
  const cwdPrefix = cwd.endsWith(sep3) ? cwd : cwd + sep3;
9272
- const absolute = resolve15(cwd, relative17);
9646
+ const absolute = resolve16(cwd, relative17);
9273
9647
  if (!absolute.startsWith(cwdPrefix) && absolute !== cwd) {
9274
9648
  throw new Error(`Path \`${relative17}\` resolves outside the server cwd and is not allowed.`);
9275
9649
  }
@@ -9291,7 +9665,7 @@ async function resolveFolderPaths(config) {
9291
9665
  }
9292
9666
  if (glob) {
9293
9667
  for await (const match of fsGlob3(glob, { cwd })) {
9294
- const absolute = resolve15(cwd, match);
9668
+ const absolute = resolve16(cwd, match);
9295
9669
  try {
9296
9670
  const stats = await stat3(absolute);
9297
9671
  if (!stats.isDirectory()) continue;
@@ -9328,7 +9702,7 @@ async function resolveValidatorSources(config) {
9328
9702
  for (const relative17 of pathList) {
9329
9703
  if (seenNames.has(relative17)) continue;
9330
9704
  ensurePathInsideCwd(relative17, cwd);
9331
- const absolute = resolve15(cwd, relative17);
9705
+ const absolute = resolve16(cwd, relative17);
9332
9706
  const text = await readFile3(absolute, "utf8");
9333
9707
  seenNames.add(relative17);
9334
9708
  collected.push({ name: relative17, text });
@@ -9388,7 +9762,7 @@ var DBX_MODEL_LOOKUP_TOOL = {
9388
9762
  required: ["topic"]
9389
9763
  }
9390
9764
  };
9391
- var LookupModelArgsType = type27({
9765
+ var LookupModelArgsType = type28({
9392
9766
  topic: "string",
9393
9767
  "depth?": "'brief' | 'full'",
9394
9768
  "scope?": "'all' | 'upstream' | 'downstream'",
@@ -9415,7 +9789,7 @@ function normalizeFieldsFilter(raw) {
9415
9789
  }
9416
9790
  function parseLookupModelArgs(raw) {
9417
9791
  const parsed = LookupModelArgsType(raw);
9418
- if (parsed instanceof type27.errors) {
9792
+ if (parsed instanceof type28.errors) {
9419
9793
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
9420
9794
  }
9421
9795
  return {
@@ -9532,10 +9906,10 @@ var lookupModelTool = {
9532
9906
  };
9533
9907
 
9534
9908
  // packages/dbx-components-mcp/src/tools/search-model.tool.ts
9535
- import { type as type28 } from "arktype";
9909
+ import { type as type29 } from "arktype";
9536
9910
  var DEFAULT_LIMIT3 = 10;
9537
9911
  var MAX_LIMIT3 = 25;
9538
- var SearchArgsType = type28({
9912
+ var SearchArgsType = type29({
9539
9913
  query: "string",
9540
9914
  "limit?": "number",
9541
9915
  "scope?": "'all' | 'upstream' | 'downstream'",
@@ -9679,7 +10053,7 @@ function formatSourceLine(scope, upstreamCount, downstreamPackages) {
9679
10053
  }
9680
10054
  function parseArgs2(rawArgs) {
9681
10055
  const parsed = SearchArgsType(rawArgs);
9682
- if (parsed instanceof type28.errors) {
10056
+ if (parsed instanceof type29.errors) {
9683
10057
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
9684
10058
  }
9685
10059
  return {
@@ -9740,7 +10114,7 @@ var searchModelTool = {
9740
10114
  };
9741
10115
 
9742
10116
  // packages/dbx-components-mcp/src/tools/model-decode.tool.ts
9743
- import { type as type29 } from "arktype";
10117
+ import { type as type30 } from "arktype";
9744
10118
 
9745
10119
  // packages/dbx-components-mcp/src/tools/decode.formatter.ts
9746
10120
  function formatDecode(context) {
@@ -9900,13 +10274,13 @@ var DBX_MODEL_DECODE_TOOL = {
9900
10274
  required: ["data"]
9901
10275
  }
9902
10276
  };
9903
- var DecodeArgsType = type29({
10277
+ var DecodeArgsType = type30({
9904
10278
  data: "string | object",
9905
10279
  "model?": "string"
9906
10280
  });
9907
10281
  function parseDecodeArgs(raw) {
9908
10282
  const parsed = DecodeArgsType(raw);
9909
- if (parsed instanceof type29.errors) {
10283
+ if (parsed instanceof type30.errors) {
9910
10284
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
9911
10285
  }
9912
10286
  const result = {
@@ -10046,9 +10420,9 @@ var modelDecodeTool = {
10046
10420
  };
10047
10421
 
10048
10422
  // packages/dbx-components-mcp/src/tools/validate-tool.ts
10049
- import { resolve as resolve16 } from "node:path";
10050
- import { type as type30 } from "arktype";
10051
- var FolderValidateArgsType = type30({
10423
+ import { resolve as resolve17 } from "node:path";
10424
+ import { type as type31 } from "arktype";
10425
+ var FolderValidateArgsType = type31({
10052
10426
  "paths?": "string[]",
10053
10427
  "glob?": "string"
10054
10428
  });
@@ -10056,7 +10430,7 @@ function createFolderValidateTool(config) {
10056
10430
  const { definition, inspectFolder: inspectFolder3, validate, format } = config;
10057
10431
  async function run15(rawArgs) {
10058
10432
  const parsed = FolderValidateArgsType(rawArgs);
10059
- if (parsed instanceof type30.errors) {
10433
+ if (parsed instanceof type31.errors) {
10060
10434
  return toolError(`Invalid arguments: ${parsed.summary}`);
10061
10435
  }
10062
10436
  const hasAny = parsed.paths && parsed.paths.length > 0 || parsed.glob;
@@ -10077,7 +10451,7 @@ function createFolderValidateTool(config) {
10077
10451
  const inspections = [];
10078
10452
  try {
10079
10453
  for (const relative17 of paths) {
10080
- const absolute = resolve16(cwd, relative17);
10454
+ const absolute = resolve17(cwd, relative17);
10081
10455
  const inspection = await inspectFolder3(absolute);
10082
10456
  const relativized = { ...inspection, path: relative17 };
10083
10457
  inspections.push(relativized);
@@ -10097,8 +10471,8 @@ function createFolderValidateTool(config) {
10097
10471
  const tool = { definition, run: run15 };
10098
10472
  return tool;
10099
10473
  }
10100
- var SourceValidateArgsType = type30({
10101
- "sources?": type30({ name: "string", text: "string" }).array(),
10474
+ var SourceValidateArgsType = type31({
10475
+ "sources?": type31({ name: "string", text: "string" }).array(),
10102
10476
  "paths?": "string[]",
10103
10477
  "glob?": "string"
10104
10478
  });
@@ -10106,7 +10480,7 @@ function createSourceValidateTool(config) {
10106
10480
  const { definition, validate, format } = config;
10107
10481
  async function run15(rawArgs) {
10108
10482
  const parsed = SourceValidateArgsType(rawArgs);
10109
- if (parsed instanceof type30.errors) {
10483
+ if (parsed instanceof type31.errors) {
10110
10484
  return toolError(`Invalid arguments: ${parsed.summary}`);
10111
10485
  }
10112
10486
  const hasAny = parsed.sources && parsed.sources.length > 0 || parsed.paths && parsed.paths.length > 0 || parsed.glob;
@@ -10134,7 +10508,7 @@ function createSourceValidateTool(config) {
10134
10508
  const tool = { definition, run: run15 };
10135
10509
  return tool;
10136
10510
  }
10137
- var TwoSideValidateArgsType = type30({
10511
+ var TwoSideValidateArgsType = type31({
10138
10512
  componentDir: "string",
10139
10513
  apiDir: "string"
10140
10514
  });
@@ -10150,9 +10524,9 @@ function resolveTwoSideInput(parsed) {
10150
10524
  return toolError(message);
10151
10525
  }
10152
10526
  return {
10153
- componentAbs: resolve16(cwd, componentRel),
10527
+ componentAbs: resolve17(cwd, componentRel),
10154
10528
  componentRel,
10155
- apiAbs: resolve16(cwd, apiRel),
10529
+ apiAbs: resolve17(cwd, apiRel),
10156
10530
  apiRel
10157
10531
  };
10158
10532
  }
@@ -10160,7 +10534,7 @@ function createTwoSideValidateTool(config) {
10160
10534
  const { definition, inspectAndValidate, format } = config;
10161
10535
  async function run15(rawArgs) {
10162
10536
  const parsed = TwoSideValidateArgsType(rawArgs);
10163
- if (parsed instanceof type30.errors) {
10537
+ if (parsed instanceof type31.errors) {
10164
10538
  return toolError(`Invalid arguments: ${parsed.summary}`);
10165
10539
  }
10166
10540
  const resolved = resolveTwoSideInput(parsed);
@@ -10178,7 +10552,7 @@ function createTwoSideValidateTool(config) {
10178
10552
  const tool = { definition, run: run15 };
10179
10553
  return tool;
10180
10554
  }
10181
- var ListAppArgsType = type30({
10555
+ var ListAppArgsType = type31({
10182
10556
  componentDir: "string",
10183
10557
  apiDir: "string",
10184
10558
  "format?": "'markdown' | 'json'"
@@ -10187,7 +10561,7 @@ function createListAppTool(config) {
10187
10561
  const { definition, inspectAndList, formatMarkdown: formatMarkdown2, formatJson: formatJson2 } = config;
10188
10562
  async function run15(rawArgs) {
10189
10563
  const parsed = ListAppArgsType(rawArgs);
10190
- if (parsed instanceof type30.errors) {
10564
+ if (parsed instanceof type31.errors) {
10191
10565
  return toolError(`Invalid arguments: ${parsed.summary}`);
10192
10566
  }
10193
10567
  const resolved = resolveTwoSideInput(parsed);
@@ -10684,9 +11058,9 @@ var RULE_CATALOG = [
10684
11058
  severity: "error",
10685
11059
  title: "A model is missing one of Fixture / Instance / Params / factory",
10686
11060
  whatItFlags: "A model is missing one of Fixture / Instance / Params / factory\ncall / singleton \u2014 the five-anchor triplet definition is incomplete.",
10687
- whenItApplies: "Every model that has at least one of the anchors.",
10688
- whenItDoesNotApply: "Models intentionally not yet wired (uncommon \u2014 usually a forgotten scaffold step).",
10689
- canonicalFix: "Run `dbx_model_fixture_scaffold apiDir=<apiDir> model=<Model>` to generate the missing pieces.",
11061
+ whenItApplies: "Every Firestore-model fixture (`kind === 'firestore-model'`) that has at least one of the anchors.",
11062
+ whenItDoesNotApply: "Skipped for non-Firestore-model fixture pairs: AuthorizedUser-style fixtures (extending `AuthorizedUserTestContextFixture`/`Instance` or registered via `authorizedUserContextFactory`) and pairs tagged with `@dbxFixtureNotModel`.",
11063
+ canonicalFix: "Run `dbx_model_fixture_scaffold apiDir=<apiDir> model=<Model>` to generate the missing pieces; or, if the pair is intentionally not a Firestore model, ensure it extends a recognized framework base or carries `@dbxFixtureNotModel`.",
10690
11064
  seeAlso: [
10691
11065
  {
10692
11066
  kind: "tool",
@@ -14722,8 +15096,8 @@ var modelValidateApiTool = createSourceValidateTool({
14722
15096
  });
14723
15097
 
14724
15098
  // packages/dbx-components-mcp/src/tools/model-api-list-app.tool.ts
14725
- import { resolve as resolve17 } from "node:path";
14726
- import { type as type31 } from "arktype";
15099
+ import { resolve as resolve18 } from "node:path";
15100
+ import { type as type32 } from "arktype";
14727
15101
 
14728
15102
  // packages/dbx-components-mcp/src/tools/model-api-list-app/extract.ts
14729
15103
  import { readdir as readdir2, readFile as readFile4, stat as stat4 } from "node:fs/promises";
@@ -15065,7 +15439,7 @@ async function listAppModelApi(componentAbs, options) {
15065
15439
  }
15066
15440
 
15067
15441
  // packages/dbx-components-mcp/src/tools/model-api-list-app.tool.ts
15068
- var ListArgsType = type31({
15442
+ var ListArgsType = type32({
15069
15443
  componentDir: "string",
15070
15444
  "model?": "string",
15071
15445
  "identity?": "string",
@@ -15103,7 +15477,7 @@ var TOOL = {
15103
15477
  };
15104
15478
  async function run(rawArgs) {
15105
15479
  const parsed = ListArgsType(rawArgs);
15106
- if (parsed instanceof type31.errors) {
15480
+ if (parsed instanceof type32.errors) {
15107
15481
  return toolError(`Invalid arguments: ${parsed.summary}`);
15108
15482
  }
15109
15483
  const cwd = process.cwd();
@@ -15112,7 +15486,7 @@ async function run(rawArgs) {
15112
15486
  } catch (err) {
15113
15487
  return toolError(err instanceof Error ? err.message : String(err));
15114
15488
  }
15115
- const componentAbs = resolve17(cwd, parsed.componentDir);
15489
+ const componentAbs = resolve18(cwd, parsed.componentDir);
15116
15490
  const modelFilter = parsed.model ?? (parsed.identity ? identityToModel(parsed.identity) : void 0);
15117
15491
  let report;
15118
15492
  try {
@@ -15132,8 +15506,8 @@ function identityToModel(identity) {
15132
15506
  var modelApiListAppTool = { definition: TOOL, run };
15133
15507
 
15134
15508
  // packages/dbx-components-mcp/src/tools/model-api-lookup.tool.ts
15135
- import { resolve as resolve18 } from "node:path";
15136
- import { type as type32 } from "arktype";
15509
+ import { resolve as resolve19 } from "node:path";
15510
+ import { type as type33 } from "arktype";
15137
15511
 
15138
15512
  // packages/dbx-components-mcp/src/tools/model-api-lookup/extract.ts
15139
15513
  import { readdir as readdir4, readFile as readFile6, stat as stat6 } from "node:fs/promises";
@@ -15555,7 +15929,7 @@ async function lookupModelApi(options) {
15555
15929
  }
15556
15930
 
15557
15931
  // packages/dbx-components-mcp/src/tools/model-api-lookup.tool.ts
15558
- var LookupArgsType = type32({
15932
+ var LookupArgsType = type33({
15559
15933
  componentDir: "string",
15560
15934
  "apiDir?": "string",
15561
15935
  "model?": "string",
@@ -15593,7 +15967,7 @@ var TOOL2 = {
15593
15967
  };
15594
15968
  async function run2(rawArgs) {
15595
15969
  const parsed = LookupArgsType(rawArgs);
15596
- if (parsed instanceof type32.errors) {
15970
+ if (parsed instanceof type33.errors) {
15597
15971
  return toolError(`Invalid arguments: ${parsed.summary}`);
15598
15972
  }
15599
15973
  if (!parsed.model && !parsed.identity) {
@@ -15608,8 +15982,8 @@ async function run2(rawArgs) {
15608
15982
  } catch (err) {
15609
15983
  return toolError(err instanceof Error ? err.message : String(err));
15610
15984
  }
15611
- const componentAbs = resolve18(cwd, parsed.componentDir);
15612
- const apiAbs = parsed.apiDir ? resolve18(cwd, parsed.apiDir) : void 0;
15985
+ const componentAbs = resolve19(cwd, parsed.componentDir);
15986
+ const apiAbs = parsed.apiDir ? resolve19(cwd, parsed.apiDir) : void 0;
15613
15987
  const modelFilter = parsed.model ?? identityToModel2(parsed.identity);
15614
15988
  let report;
15615
15989
  try {
@@ -15638,8 +16012,8 @@ function identityToModel2(identity) {
15638
16012
  var modelApiLookupTool = { definition: TOOL2, run: run2 };
15639
16013
 
15640
16014
  // packages/dbx-components-mcp/src/tools/model-api-validate-app.tool.ts
15641
- import { resolve as resolve19 } from "node:path";
15642
- import { type as type33 } from "arktype";
16015
+ import { resolve as resolve20 } from "node:path";
16016
+ import { type as type34 } from "arktype";
15643
16017
 
15644
16018
  // packages/dbx-components-mcp/src/tools/model-api-validate-app/extract-declarations.ts
15645
16019
  import { readdir as readdir5, readFile as readFile7, stat as stat7 } from "node:fs/promises";
@@ -15937,7 +16311,7 @@ async function validateAppModelApi(options) {
15937
16311
  }
15938
16312
 
15939
16313
  // packages/dbx-components-mcp/src/tools/model-api-validate-app.tool.ts
15940
- var ValidateArgsType = type33({
16314
+ var ValidateArgsType = type34({
15941
16315
  componentDir: "string",
15942
16316
  apiDir: "string",
15943
16317
  "model?": "string",
@@ -15978,7 +16352,7 @@ var TOOL3 = {
15978
16352
  };
15979
16353
  async function run3(rawArgs) {
15980
16354
  const parsed = ValidateArgsType(rawArgs);
15981
- if (parsed instanceof type33.errors) {
16355
+ if (parsed instanceof type34.errors) {
15982
16356
  return toolError(`Invalid arguments: ${parsed.summary}`);
15983
16357
  }
15984
16358
  const cwd = process.cwd();
@@ -15988,8 +16362,8 @@ async function run3(rawArgs) {
15988
16362
  } catch (err) {
15989
16363
  return toolError(err instanceof Error ? err.message : String(err));
15990
16364
  }
15991
- const componentAbs = resolve19(cwd, parsed.componentDir);
15992
- const apiAbs = resolve19(cwd, parsed.apiDir);
16365
+ const componentAbs = resolve20(cwd, parsed.componentDir);
16366
+ const apiAbs = resolve20(cwd, parsed.apiDir);
15993
16367
  const modelFilter = parsed.model ?? (parsed.identity ? identityToModel3(parsed.identity) : void 0);
15994
16368
  let report;
15995
16369
  try {
@@ -16207,7 +16581,7 @@ var modelValidateFolderTool = createFolderValidateTool({
16207
16581
  });
16208
16582
 
16209
16583
  // packages/dbx-components-mcp/src/tools/model-store-scaffold.tool.ts
16210
- import { type as type34 } from "arktype";
16584
+ import { type as type35 } from "arktype";
16211
16585
  var STORE_SHAPES = ["root", "root-singleton", "sub-collection", "singleton-sub", "system-state"];
16212
16586
  var SHAPE_PROFILES = {
16213
16587
  root: { defaultSurfaces: ["document", "collection"], allowedSurfaces: ["document", "collection"], hasParent: false, emitsDirectives: true, requiresCollectionsClass: true, requiresSystemStateTypeConst: false },
@@ -16303,7 +16677,7 @@ var DBX_MODEL_STORE_SCAFFOLD_TOOL = {
16303
16677
  required: ["model_name", "app_prefix", "firebase_package"]
16304
16678
  }
16305
16679
  };
16306
- var ScaffoldArgsType2 = type34({
16680
+ var ScaffoldArgsType2 = type35({
16307
16681
  model_name: "string",
16308
16682
  app_prefix: "string",
16309
16683
  firebase_package: "string",
@@ -16323,7 +16697,7 @@ var ScaffoldArgsType2 = type34({
16323
16697
  name: "string",
16324
16698
  functions_path: "string"
16325
16699
  },
16326
- "crud_functions?": type34({
16700
+ "crud_functions?": type35({
16327
16701
  name: "string",
16328
16702
  kind: "'create' | 'update' | 'crud'",
16329
16703
  functions_path: "string"
@@ -16343,7 +16717,7 @@ function requireString(value, label) {
16343
16717
  }
16344
16718
  function parseArgs3(raw) {
16345
16719
  const parsed = ScaffoldArgsType2(raw);
16346
- if (parsed instanceof type34.errors) {
16720
+ if (parsed instanceof type35.errors) {
16347
16721
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
16348
16722
  }
16349
16723
  const modelName = requireString(parsed.model_name, "model_name");
@@ -16735,8 +17109,8 @@ var modelStoreScaffoldTool = {
16735
17109
  };
16736
17110
 
16737
17111
  // packages/dbx-components-mcp/src/tools/model-fixture-list-app.tool.ts
16738
- import { resolve as resolve20 } from "node:path";
16739
- import { type as type35 } from "arktype";
17112
+ import { resolve as resolve21 } from "node:path";
17113
+ import { type as type36 } from "arktype";
16740
17114
 
16741
17115
  // packages/dbx-components-mcp/src/tools/model-fixture-shared/archetype.ts
16742
17116
  function classifyFixtureArchetype(input) {
@@ -16766,6 +17140,27 @@ function paramsHasFixtureDependencies(params) {
16766
17140
 
16767
17141
  // packages/dbx-components-mcp/src/tools/model-fixture-shared/extract.ts
16768
17142
  import { Node as Node13, Project as Project11, SyntaxKind as SyntaxKind7 } from "ts-morph";
17143
+
17144
+ // packages/dbx-components-mcp/src/tools/model-fixture-shared/framework-fixtures.ts
17145
+ var KNOWN_NON_MODEL_FIXTURE_FAMILIES = [
17146
+ {
17147
+ kind: "authorized-user",
17148
+ factoryName: "authorizedUserContextFactory",
17149
+ baseFixtureClass: "AuthorizedUserTestContextFixture",
17150
+ baseInstanceClass: "AuthorizedUserTestContextInstance",
17151
+ module: "@dereekb/firebase-server/test",
17152
+ description: "Authorized Firebase auth user (with custom claims). Not backed by a Firestore document."
17153
+ }
17154
+ ];
17155
+ var NON_MODEL_JSDOC_TAG = "dbxFixtureNotModel";
17156
+ function findFamilyByFactoryName(factoryCallName) {
17157
+ return KNOWN_NON_MODEL_FIXTURE_FAMILIES.find((f) => f.factoryName === factoryCallName);
17158
+ }
17159
+ function findFamilyByBaseClass(baseClassName) {
17160
+ return KNOWN_NON_MODEL_FIXTURE_FAMILIES.find((f) => f.baseFixtureClass === baseClassName || f.baseInstanceClass === baseClassName);
17161
+ }
17162
+
17163
+ // packages/dbx-components-mcp/src/tools/model-fixture-shared/extract.ts
16769
17164
  var FIXTURE_SUFFIX = "TestContextFixture";
16770
17165
  var INSTANCE_SUFFIX = "TestContextInstance";
16771
17166
  var PARAMS_SUFFIX = "TestContextParams";
@@ -16782,7 +17177,7 @@ function extractAppFixturesFromText(input) {
16782
17177
  const unrecognized = [];
16783
17178
  collectClasses(sourceFile, prefix, { fixtures: fixtureClasses, instances: instanceClasses, unrecognized });
16784
17179
  const paramsTypes = collectParamsTypes(sourceFile, prefix);
16785
- const factoriesByModel = collectFactories(sourceFile, prefix);
17180
+ const { factoriesByModel, nonModelFamiliesByModel } = collectFactories(sourceFile, prefix);
16786
17181
  const singletonsByFactory = collectSingletonsByFactory(sourceFile);
16787
17182
  const entries = [];
16788
17183
  const seenModels = /* @__PURE__ */ new Set();
@@ -16806,10 +17201,13 @@ function extractAppFixturesFromText(input) {
16806
17201
  const archetype = classifyFixtureArchetype({ factory, params });
16807
17202
  const fixtureMethods = readClassMethods(fixtureClass);
16808
17203
  const instanceMethods = readClassMethods(instanceClass);
17204
+ const { kind, nonModelFamily } = classifyFixtureKind({ fixtureClass, instanceClass, nonModelFamily: nonModelFamiliesByModel.get(model) });
16809
17205
  const entry = {
16810
17206
  model,
16811
17207
  prefix: prefix ?? "",
16812
17208
  archetype,
17209
+ kind,
17210
+ nonModelFamily,
16813
17211
  fixtureClassName: fixtureClass.getName() ?? "",
16814
17212
  instanceClassName: instanceClass.getName() ?? "",
16815
17213
  paramsTypeName: params?.name ?? `${prefix ?? ""}${model}${PARAMS_SUFFIX}`,
@@ -16961,19 +17359,23 @@ function resolveFixtureFieldModel(typeText, prefix) {
16961
17359
  return model.length > 0 ? model : void 0;
16962
17360
  }
16963
17361
  function collectFactories(sourceFile, prefix) {
16964
- const out = /* @__PURE__ */ new Map();
17362
+ const factoriesByModel = /* @__PURE__ */ new Map();
17363
+ const nonModelFamiliesByModel = /* @__PURE__ */ new Map();
16965
17364
  for (const stmt of sourceFile.getVariableStatements()) {
16966
17365
  for (const decl of stmt.getDeclarations()) {
16967
- const factory = readModelTestContextFactory(decl, prefix);
16968
- if (!factory) continue;
16969
- const model = factoryNameToModel(factory.factoryName, prefix);
17366
+ const parsed = readContextFactory(decl);
17367
+ if (!parsed) continue;
17368
+ const model = factoryNameToModel(parsed.factory.factoryName, prefix);
16970
17369
  if (model === void 0) continue;
16971
- out.set(model, factory);
17370
+ factoriesByModel.set(model, parsed.factory);
17371
+ if (parsed.family !== void 0) {
17372
+ nonModelFamiliesByModel.set(model, parsed.family);
17373
+ }
16972
17374
  }
16973
17375
  }
16974
- return out;
17376
+ return { factoriesByModel, nonModelFamiliesByModel };
16975
17377
  }
16976
- function readModelTestContextFactory(decl, _prefix) {
17378
+ function readContextFactory(decl) {
16977
17379
  const initializer = decl.getInitializer();
16978
17380
  if (!initializer) return void 0;
16979
17381
  let bodyExpr;
@@ -16984,7 +17386,13 @@ function readModelTestContextFactory(decl, _prefix) {
16984
17386
  }
16985
17387
  if (!bodyExpr || !Node13.isCallExpression(bodyExpr)) return void 0;
16986
17388
  const callee = bodyExpr.getExpression();
16987
- if (!Node13.isIdentifier(callee) || callee.getText() !== MODEL_FACTORY_NAME) return void 0;
17389
+ if (!Node13.isIdentifier(callee)) return void 0;
17390
+ const calleeName = callee.getText();
17391
+ let family;
17392
+ if (calleeName !== MODEL_FACTORY_NAME) {
17393
+ family = findFamilyByFactoryName(calleeName);
17394
+ if (family === void 0) return void 0;
17395
+ }
16988
17396
  const generics = bodyExpr.getTypeArguments().map((t) => t.getText());
16989
17397
  const args = bodyExpr.getArguments();
16990
17398
  let hasParamsGetCollection = false;
@@ -17011,7 +17419,7 @@ function readModelTestContextFactory(decl, _prefix) {
17011
17419
  }
17012
17420
  }
17013
17421
  }
17014
- const result = {
17422
+ const factory = {
17015
17423
  factoryName: decl.getName(),
17016
17424
  genericArgs: generics,
17017
17425
  hasParamsGetCollection,
@@ -17020,7 +17428,41 @@ function readModelTestContextFactory(decl, _prefix) {
17020
17428
  parentFixtureFieldFromGetCollection,
17021
17429
  line: decl.getStartLineNumber()
17022
17430
  };
17023
- return result;
17431
+ return { factory, family };
17432
+ }
17433
+ function classifyFixtureKind(input) {
17434
+ if (hasNonModelJsDoc(input.fixtureClass) || hasNonModelJsDoc(input.instanceClass)) {
17435
+ return { kind: "non-model", nonModelFamily: "jsdoc-tag" };
17436
+ }
17437
+ const familyFromInheritance = familyFromExtends(input.fixtureClass) ?? familyFromExtends(input.instanceClass);
17438
+ const family = familyFromInheritance ?? input.nonModelFamily;
17439
+ if (family !== void 0) {
17440
+ return { kind: family.kind, nonModelFamily: family.kind };
17441
+ }
17442
+ return { kind: "firestore-model" };
17443
+ }
17444
+ function hasNonModelJsDoc(cls) {
17445
+ for (const doc of cls.getJsDocs()) {
17446
+ for (const tag of doc.getTags()) {
17447
+ if (tag.getTagName() === NON_MODEL_JSDOC_TAG) return true;
17448
+ }
17449
+ }
17450
+ return false;
17451
+ }
17452
+ function familyFromExtends(cls) {
17453
+ const ext = cls.getExtends();
17454
+ if (!ext) return void 0;
17455
+ const expr = ext.getExpression();
17456
+ let baseName;
17457
+ if (Node13.isIdentifier(expr)) {
17458
+ baseName = expr.getText();
17459
+ } else {
17460
+ const text = ext.getText();
17461
+ const match = /^([A-Za-z_$][\w$]*)/.exec(text);
17462
+ baseName = match ? match[1] : void 0;
17463
+ }
17464
+ if (baseName === void 0) return void 0;
17465
+ return findFamilyByBaseClass(baseName);
17024
17466
  }
17025
17467
  function readSingleReturnExpression(fn) {
17026
17468
  const body = fn.getBody();
@@ -17176,12 +17618,15 @@ async function inspectAppFixtures(apiAbs, apiRel) {
17176
17618
  function validateAppFixtures(extraction, options = {}) {
17177
17619
  const diagnostics = [];
17178
17620
  for (const entry of extraction.entries) {
17179
- pushTripletCompletenessDiagnostics(entry, diagnostics);
17621
+ const isFirestoreModel = entry.kind === "firestore-model";
17622
+ if (isFirestoreModel) {
17623
+ pushTripletCompletenessDiagnostics(entry, diagnostics);
17624
+ pushArchetypeConsistencyDiagnostics(entry, diagnostics);
17625
+ pushParentFieldNamingDiagnostics(entry, options.registry, diagnostics);
17626
+ pushRegistryDiagnostics(entry, options.registry, diagnostics);
17627
+ }
17180
17628
  pushForwardingDiagnostics(entry, diagnostics);
17181
- pushGenericAlignmentDiagnostics(entry, diagnostics);
17182
- pushArchetypeConsistencyDiagnostics(entry, diagnostics);
17183
- pushParentFieldNamingDiagnostics(entry, options.registry, diagnostics);
17184
- pushRegistryDiagnostics(entry, options.registry, diagnostics);
17629
+ pushGenericAlignmentDiagnostics(entry, diagnostics, { skipFactoryComparison: !isFirestoreModel });
17185
17630
  }
17186
17631
  pushOrphanFixtureDiagnostics(extraction, diagnostics);
17187
17632
  pushModelsWithoutFixtureDiagnostics(extraction, options.registry, diagnostics);
@@ -17257,7 +17702,7 @@ function pushForwardingDiagnostics(entry, out) {
17257
17702
  }
17258
17703
  }
17259
17704
  }
17260
- function pushGenericAlignmentDiagnostics(entry, out) {
17705
+ function pushGenericAlignmentDiagnostics(entry, out, options = { skipFactoryComparison: false }) {
17261
17706
  const fixGenerics = entry.fixtureExtendsGenerics;
17262
17707
  const instGenerics = entry.instanceExtendsGenerics;
17263
17708
  const factoryGenerics = entry.factory?.genericArgs ?? [];
@@ -17285,6 +17730,7 @@ function pushGenericAlignmentDiagnostics(entry, out) {
17285
17730
  line: entry.fixtureLine
17286
17731
  });
17287
17732
  }
17733
+ if (options.skipFactoryComparison) return;
17288
17734
  if (factoryModel !== void 0 && fixModel !== void 0 && factoryModel !== fixModel) {
17289
17735
  out.push({
17290
17736
  code: "generics-misaligned",
@@ -17419,6 +17865,8 @@ function summarizeEntry(entry, full = false) {
17419
17865
  const summary = {
17420
17866
  model: entry.model,
17421
17867
  prefix: entry.prefix,
17868
+ kind: entry.kind,
17869
+ nonModelFamily: entry.nonModelFamily,
17422
17870
  archetype: entry.archetype,
17423
17871
  fixtureClassName: entry.fixtureClassName,
17424
17872
  instanceClassName: entry.instanceClassName,
@@ -17452,9 +17900,16 @@ function formatListAsMarkdown(extraction) {
17452
17900
  if (extraction.entries.length === 0) {
17453
17901
  lines.push("", "_No model fixtures found._");
17454
17902
  } else {
17455
- lines.push("", "| Model | Archetype | Fixture | Instance | Params | Factory | Singleton | Methods (F/I) | Lines |", "|---|---|---|---|---|---|---|---|---|");
17903
+ lines.push("", "| Model | Kind | Archetype | Fixture | Instance | Params | Factory | Singleton | Methods (F/I) | Lines |", "|---|---|---|---|---|---|---|---|---|---|");
17456
17904
  for (const e of extraction.entries) {
17457
- lines.push("| " + [e.model, e.archetype, code3(e.fixtureClassName), code3(e.instanceClassName), code3(e.paramsTypeName), code3(e.factoryName ?? "\u2014"), code3(e.singletonName ?? "\u2014"), `${e.fixtureMethods.length}/${e.instanceMethods.length}`, `${e.fixtureLine}-${e.instanceEndLine}`].join(" | ") + " |");
17905
+ lines.push("| " + [e.model, e.kind, e.archetype, code3(e.fixtureClassName), code3(e.instanceClassName), code3(e.paramsTypeName), code3(e.factoryName ?? "\u2014"), code3(e.singletonName ?? "\u2014"), `${e.fixtureMethods.length}/${e.instanceMethods.length}`, `${e.fixtureLine}-${e.instanceEndLine}`].join(" | ") + " |");
17906
+ }
17907
+ const nonModelEntries = extraction.entries.filter((e) => e.kind !== "firestore-model");
17908
+ if (nonModelEntries.length > 0) {
17909
+ lines.push("", "### Non-model fixtures");
17910
+ for (const e of nonModelEntries) {
17911
+ lines.push("", `- **${e.model}** \u2014 ${nonModelDescription(e)}`);
17912
+ }
17458
17913
  }
17459
17914
  }
17460
17915
  if (extraction.unrecognizedClassNames.length > 0) {
@@ -17481,7 +17936,21 @@ function formatLookupAsMarkdown2(extraction, entry) {
17481
17936
  return lines.join("\n");
17482
17937
  }
17483
17938
  function appendLookupHeader(lines, extraction, entry) {
17484
- lines.push(`# Fixture: \`${entry.prefix}${entry.model}\``, "", `Source: \`${extraction.fixturePath}\``, `Archetype: \`${entry.archetype}\``, "");
17939
+ lines.push(`# Fixture: \`${entry.prefix}${entry.model}\``, "", `Source: \`${extraction.fixturePath}\``, `Kind: \`${entry.kind}\``, `Archetype: \`${entry.archetype}\``);
17940
+ if (entry.kind !== "firestore-model") {
17941
+ lines.push("", `> ${nonModelDescription(entry)}`);
17942
+ }
17943
+ lines.push("");
17944
+ }
17945
+ function nonModelDescription(entry) {
17946
+ if (entry.nonModelFamily === "jsdoc-tag") {
17947
+ return "Tagged `@dbxFixtureNotModel` \u2014 explicitly opted out of model validation.";
17948
+ }
17949
+ const family = KNOWN_NON_MODEL_FIXTURE_FAMILIES.find((f) => f.kind === entry.kind);
17950
+ if (family) {
17951
+ return `${family.description} Registered via \`${family.factoryName}\` (\`${family.module}\`).`;
17952
+ }
17953
+ return "Non-Firestore-model fixture.";
17485
17954
  }
17486
17955
  function appendLookupTriplet(lines, entry) {
17487
17956
  lines.push("## Triplet", "", `- Fixture: \`${entry.fixtureClassName}\` (lines ${entry.fixtureLine}-${entry.fixtureEndLine})`, `- Instance: \`${entry.instanceClassName}\` (lines ${entry.instanceLine}-${entry.instanceEndLine})`, `- Params: \`${entry.paramsTypeName}\``, `- Factory: \`${entry.factoryName ?? "\u2014"}\``, `- Singleton: \`${entry.singletonName ?? "\u2014"}\``);
@@ -17845,7 +18314,7 @@ function topLevelColonIndex(text) {
17845
18314
  }
17846
18315
 
17847
18316
  // packages/dbx-components-mcp/src/tools/model-fixture-list-app.tool.ts
17848
- var ListArgsType2 = type35({
18317
+ var ListArgsType2 = type36({
17849
18318
  apiDir: "string",
17850
18319
  "format?": "'markdown' | 'json'"
17851
18320
  });
@@ -17873,7 +18342,7 @@ var TOOL4 = {
17873
18342
  };
17874
18343
  async function run4(rawArgs) {
17875
18344
  const parsed = ListArgsType2(rawArgs);
17876
- if (parsed instanceof type35.errors) {
18345
+ if (parsed instanceof type36.errors) {
17877
18346
  return toolError(`Invalid arguments: ${parsed.summary}`);
17878
18347
  }
17879
18348
  const cwd = process.cwd();
@@ -17882,7 +18351,7 @@ async function run4(rawArgs) {
17882
18351
  } catch (err) {
17883
18352
  return toolError(err instanceof Error ? err.message : String(err));
17884
18353
  }
17885
- const apiAbs = resolve20(cwd, parsed.apiDir);
18354
+ const apiAbs = resolve21(cwd, parsed.apiDir);
17886
18355
  let extraction;
17887
18356
  try {
17888
18357
  extraction = await inspectAppFixtures(apiAbs, parsed.apiDir);
@@ -17896,9 +18365,9 @@ async function run4(rawArgs) {
17896
18365
  var modelFixtureListAppTool = { definition: TOOL4, run: run4 };
17897
18366
 
17898
18367
  // packages/dbx-components-mcp/src/tools/model-fixture-lookup.tool.ts
17899
- import { resolve as resolve21 } from "node:path";
17900
- import { type as type36 } from "arktype";
17901
- var LookupArgsType2 = type36({
18368
+ import { resolve as resolve22 } from "node:path";
18369
+ import { type as type37 } from "arktype";
18370
+ var LookupArgsType2 = type37({
17902
18371
  apiDir: "string",
17903
18372
  "model?": "string",
17904
18373
  "identity?": "string",
@@ -17930,7 +18399,7 @@ var TOOL5 = {
17930
18399
  };
17931
18400
  async function run5(rawArgs) {
17932
18401
  const parsed = LookupArgsType2(rawArgs);
17933
- if (parsed instanceof type36.errors) {
18402
+ if (parsed instanceof type37.errors) {
17934
18403
  return toolError(`Invalid arguments: ${parsed.summary}`);
17935
18404
  }
17936
18405
  if (!parsed.model && !parsed.identity) {
@@ -17942,7 +18411,7 @@ async function run5(rawArgs) {
17942
18411
  } catch (err) {
17943
18412
  return toolError(err instanceof Error ? err.message : String(err));
17944
18413
  }
17945
- const apiAbs = resolve21(cwd, parsed.apiDir);
18414
+ const apiAbs = resolve22(cwd, parsed.apiDir);
17946
18415
  let extraction;
17947
18416
  try {
17948
18417
  extraction = await inspectAppFixtures(apiAbs, parsed.apiDir);
@@ -17968,9 +18437,9 @@ function identityToModel4(identity) {
17968
18437
  var modelFixtureLookupTool = { definition: TOOL5, run: run5 };
17969
18438
 
17970
18439
  // packages/dbx-components-mcp/src/tools/model-fixture-validate-app.tool.ts
17971
- import { resolve as resolve22 } from "node:path";
17972
- import { type as type37 } from "arktype";
17973
- var ValidateArgsType2 = type37({
18440
+ import { resolve as resolve23 } from "node:path";
18441
+ import { type as type38 } from "arktype";
18442
+ var ValidateArgsType2 = type38({
17974
18443
  apiDir: "string",
17975
18444
  "format?": "'markdown' | 'json'"
17976
18445
  });
@@ -18003,7 +18472,7 @@ var TOOL6 = {
18003
18472
  function createModelFixtureValidateAppTool(config = {}) {
18004
18473
  async function run15(rawArgs) {
18005
18474
  const parsed = ValidateArgsType2(rawArgs);
18006
- if (parsed instanceof type37.errors) {
18475
+ if (parsed instanceof type38.errors) {
18007
18476
  return toolError(`Invalid arguments: ${parsed.summary}`);
18008
18477
  }
18009
18478
  const cwd = process.cwd();
@@ -18012,7 +18481,7 @@ function createModelFixtureValidateAppTool(config = {}) {
18012
18481
  } catch (err) {
18013
18482
  return toolError(err instanceof Error ? err.message : String(err));
18014
18483
  }
18015
- const apiAbs = resolve22(cwd, parsed.apiDir);
18484
+ const apiAbs = resolve23(cwd, parsed.apiDir);
18016
18485
  let extraction;
18017
18486
  try {
18018
18487
  extraction = await inspectAppFixtures(apiAbs, parsed.apiDir);
@@ -18033,16 +18502,16 @@ function createModelFixtureValidateAppTool(config = {}) {
18033
18502
 
18034
18503
  // packages/dbx-components-mcp/src/tools/model-fixture-scaffold.tool.ts
18035
18504
  import { readFile as readFile11, writeFile } from "node:fs/promises";
18036
- import { join as join10, resolve as resolve23 } from "node:path";
18037
- import { type as type38 } from "arktype";
18038
- var ScaffoldArgsType3 = type38({
18505
+ import { join as join10, resolve as resolve24 } from "node:path";
18506
+ import { type as type39 } from "arktype";
18507
+ var ScaffoldArgsType3 = type39({
18039
18508
  apiDir: "string",
18040
18509
  model: "string",
18041
18510
  archetype: "'top-level-simple' | 'top-level-with-deps' | 'sub-collection' | 'sub-collection-traversal'",
18042
18511
  "prefix?": "string",
18043
18512
  "parentFixture?": "string",
18044
18513
  "parentFixtureField?": "string",
18045
- "paramsDependsOn?": type38({
18514
+ "paramsDependsOn?": type39({
18046
18515
  field: "string",
18047
18516
  fixtureModel: "string",
18048
18517
  "optional?": "boolean",
@@ -18103,7 +18572,7 @@ var TOOL7 = {
18103
18572
  };
18104
18573
  async function run6(rawArgs) {
18105
18574
  const parsed = ScaffoldArgsType3(rawArgs);
18106
- if (parsed instanceof type38.errors) {
18575
+ if (parsed instanceof type39.errors) {
18107
18576
  return toolError(`Invalid arguments: ${parsed.summary}`);
18108
18577
  }
18109
18578
  const cwd = process.cwd();
@@ -18112,7 +18581,7 @@ async function run6(rawArgs) {
18112
18581
  } catch (err) {
18113
18582
  return toolError(err instanceof Error ? err.message : String(err));
18114
18583
  }
18115
- const apiAbs = resolve23(cwd, parsed.apiDir);
18584
+ const apiAbs = resolve24(cwd, parsed.apiDir);
18116
18585
  let extraction;
18117
18586
  try {
18118
18587
  extraction = await inspectAppFixtures(apiAbs, parsed.apiDir);
@@ -18203,10 +18672,10 @@ function collectConflicts(extraction, rendered) {
18203
18672
  var modelFixtureScaffoldTool = { definition: TOOL7, run: run6 };
18204
18673
 
18205
18674
  // packages/dbx-components-mcp/src/tools/model-fixture-forward.tool.ts
18206
- import { join as join11, resolve as resolve24 } from "node:path";
18207
- import { type as type39 } from "arktype";
18675
+ import { join as join11, resolve as resolve25 } from "node:path";
18676
+ import { type as type40 } from "arktype";
18208
18677
  import { Project as Project12 } from "ts-morph";
18209
- var ForwardArgsType = type39({
18678
+ var ForwardArgsType = type40({
18210
18679
  apiDir: "string",
18211
18680
  model: "string",
18212
18681
  "methods?": "string[]"
@@ -18230,7 +18699,7 @@ var TOOL8 = {
18230
18699
  };
18231
18700
  async function run7(rawArgs) {
18232
18701
  const parsed = ForwardArgsType(rawArgs);
18233
- if (parsed instanceof type39.errors) {
18702
+ if (parsed instanceof type40.errors) {
18234
18703
  return toolError(`Invalid arguments: ${parsed.summary}`);
18235
18704
  }
18236
18705
  const cwd = process.cwd();
@@ -18239,7 +18708,7 @@ async function run7(rawArgs) {
18239
18708
  } catch (err) {
18240
18709
  return toolError(toErrorMessage(err));
18241
18710
  }
18242
- const apiAbs = resolve24(cwd, parsed.apiDir);
18711
+ const apiAbs = resolve25(cwd, parsed.apiDir);
18243
18712
  let extraction;
18244
18713
  try {
18245
18714
  extraction = await inspectAppFixtures(apiAbs, parsed.apiDir);
@@ -18313,8 +18782,8 @@ function appendListSection(lines, heading, names) {
18313
18782
  var modelFixtureForwardTool = { definition: TOOL8, run: run7 };
18314
18783
 
18315
18784
  // packages/dbx-components-mcp/src/tools/model-test-tree.tool.ts
18316
- import { resolve as resolve25 } from "node:path";
18317
- import { type as type40 } from "arktype";
18785
+ import { resolve as resolve26 } from "node:path";
18786
+ import { type as type41 } from "arktype";
18318
18787
 
18319
18788
  // packages/dbx-components-mcp/src/tools/model-test-shared/extract.ts
18320
18789
  import { Node as Node14, Project as Project13, SyntaxKind as SyntaxKind8 } from "ts-morph";
@@ -19059,7 +19528,7 @@ function parseDescribePath2(value) {
19059
19528
  }
19060
19529
 
19061
19530
  // packages/dbx-components-mcp/src/tools/model-test-tree.tool.ts
19062
- var TreeArgsType = type40({
19531
+ var TreeArgsType = type41({
19063
19532
  specFile: "string",
19064
19533
  "apiDir?": "string",
19065
19534
  "view?": "'all' | 'describes' | 'fixtures' | 'its' | 'helpers'",
@@ -19097,7 +19566,7 @@ var TOOL9 = {
19097
19566
  };
19098
19567
  async function run8(rawArgs) {
19099
19568
  const parsed = TreeArgsType(rawArgs);
19100
- if (parsed instanceof type40.errors) {
19569
+ if (parsed instanceof type41.errors) {
19101
19570
  return toolError(`Invalid arguments: ${parsed.summary}`);
19102
19571
  }
19103
19572
  const cwd = process.cwd();
@@ -19107,8 +19576,8 @@ async function run8(rawArgs) {
19107
19576
  } catch (err) {
19108
19577
  return toolError(err instanceof Error ? err.message : String(err));
19109
19578
  }
19110
- const specAbs = resolve25(cwd, parsed.specFile);
19111
- const apiAbs = parsed.apiDir !== void 0 ? resolve25(cwd, parsed.apiDir) : void 0;
19579
+ const specAbs = resolve26(cwd, parsed.specFile);
19580
+ const apiAbs = parsed.apiDir !== void 0 ? resolve26(cwd, parsed.apiDir) : void 0;
19112
19581
  let tree;
19113
19582
  try {
19114
19583
  tree = await inspectSpecFile({ specAbs, specRel: parsed.specFile, apiAbs, apiRel: parsed.apiDir });
@@ -19124,9 +19593,9 @@ async function run8(rawArgs) {
19124
19593
  var modelTestTreeTool = { definition: TOOL9, run: run8 };
19125
19594
 
19126
19595
  // packages/dbx-components-mcp/src/tools/model-test-search.tool.ts
19127
- import { resolve as resolve26 } from "node:path";
19128
- import { type as type41 } from "arktype";
19129
- var SearchArgsType2 = type41({
19596
+ import { resolve as resolve27 } from "node:path";
19597
+ import { type as type42 } from "arktype";
19598
+ var SearchArgsType2 = type42({
19130
19599
  specFile: "string",
19131
19600
  "apiDir?": "string",
19132
19601
  "model?": "string",
@@ -19167,7 +19636,7 @@ var TOOL10 = {
19167
19636
  };
19168
19637
  async function run9(rawArgs) {
19169
19638
  const parsed = SearchArgsType2(rawArgs);
19170
- if (parsed instanceof type41.errors) {
19639
+ if (parsed instanceof type42.errors) {
19171
19640
  return toolError(`Invalid arguments: ${parsed.summary}`);
19172
19641
  }
19173
19642
  const queryFields = [
@@ -19190,8 +19659,8 @@ async function run9(rawArgs) {
19190
19659
  } catch (err) {
19191
19660
  return toolError(err instanceof Error ? err.message : String(err));
19192
19661
  }
19193
- const specAbs = resolve26(cwd, parsed.specFile);
19194
- const apiAbs = parsed.apiDir !== void 0 ? resolve26(cwd, parsed.apiDir) : void 0;
19662
+ const specAbs = resolve27(cwd, parsed.specFile);
19663
+ const apiAbs = parsed.apiDir !== void 0 ? resolve27(cwd, parsed.apiDir) : void 0;
19195
19664
  let tree;
19196
19665
  try {
19197
19666
  tree = await inspectSpecFile({ specAbs, specRel: parsed.specFile, apiAbs, apiRel: parsed.apiDir });
@@ -19618,8 +20087,8 @@ function getFunctionType(x) {
19618
20087
  return typeof x === "function" ? x.prototype ? Object.getOwnPropertyDescriptor(x, "prototype")?.writable ? "function" : "class" : x.constructor.name === "AsyncFunction" ? "function" : "arrow" : null;
19619
20088
  }
19620
20089
  function isNonClassFunction(x) {
19621
- const type65 = getFunctionType(x);
19622
- return type65 != null && type65 !== "class";
20090
+ const type68 = getFunctionType(x);
20091
+ return type68 != null && type68 !== "class";
19623
20092
  }
19624
20093
 
19625
20094
  // packages/util/src/lib/getter/getter.ts
@@ -19696,9 +20165,9 @@ function asNumber(input) {
19696
20165
  }
19697
20166
 
19698
20167
  // packages/util/src/lib/number/round.ts
19699
- function roundingFunction(type65) {
20168
+ function roundingFunction(type68) {
19700
20169
  let fn;
19701
- switch (type65) {
20170
+ switch (type68) {
19702
20171
  case "floor":
19703
20172
  fn = Math.floor;
19704
20173
  break;
@@ -19951,9 +20420,9 @@ function filterKeyValueTuplesInputToFilter(input) {
19951
20420
  }
19952
20421
  function filterKeyValueTupleFunction(inputFilter) {
19953
20422
  const filter = filterKeyValueTuplesInputToFilter(inputFilter);
19954
- const { valueFilter: type65 = 1 /* UNDEFINED */, invertFilter: inverseFilter = false, keysFilter } = filter;
20423
+ const { valueFilter: type68 = 1 /* UNDEFINED */, invertFilter: inverseFilter = false, keysFilter } = filter;
19955
20424
  let filterFn;
19956
- switch (type65) {
20425
+ switch (type68) {
19957
20426
  case 1 /* UNDEFINED */:
19958
20427
  filterFn = ([, x]) => x !== void 0;
19959
20428
  break;
@@ -20212,7 +20681,7 @@ var NUMBER_STRING_DENCODER_64_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcd
20212
20681
  var NUMBER_STRING_DENCODER_64_DEFAULT_NEGATIVE_PREFIX = "!";
20213
20682
  function numberStringDencoder(config) {
20214
20683
  const { negativePrefix, digits } = config;
20215
- const type65 = negativePrefix ? "integer" : "positive_integer";
20684
+ const type68 = negativePrefix ? "integer" : "positive_integer";
20216
20685
  const log2OfDigits = Math.min(6, Math.floor(Math.log2(digits.length)));
20217
20686
  const bitDepth = Math.pow(2, log2OfDigits);
20218
20687
  const bitMask = bitDepth - 1;
@@ -20249,7 +20718,7 @@ function numberStringDencoder(config) {
20249
20718
  return result;
20250
20719
  }
20251
20720
  return {
20252
- type: type65,
20721
+ type: type68,
20253
20722
  digits,
20254
20723
  bitDepth,
20255
20724
  negativePrefix,
@@ -20320,9 +20789,9 @@ function readableStreamToStringFunction(encoding) {
20320
20789
  var readableStreamToBase64 = readableStreamToStringFunction("base64");
20321
20790
  function readableStreamToBuffer(stream) {
20322
20791
  const chunks = [];
20323
- return new Promise((resolve39, reject) => {
20792
+ return new Promise((resolve41, reject) => {
20324
20793
  stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
20325
- stream.on("end", () => resolve39(Buffer.concat(chunks)));
20794
+ stream.on("end", () => resolve41(Buffer.concat(chunks)));
20326
20795
  stream.on("error", reject);
20327
20796
  });
20328
20797
  }
@@ -24106,7 +24575,7 @@ var systemMValidateFolderTool = createFolderValidateTool({
24106
24575
  });
24107
24576
 
24108
24577
  // packages/dbx-components-mcp/src/tools/lookup-action.tool.ts
24109
- import { type as type42 } from "arktype";
24578
+ import { type as type43 } from "arktype";
24110
24579
  var DBX_ACTION_LOOKUP_TOOL = {
24111
24580
  name: "dbx_action_lookup",
24112
24581
  description: [
@@ -24139,13 +24608,13 @@ var DBX_ACTION_LOOKUP_TOOL = {
24139
24608
  required: ["topic"]
24140
24609
  }
24141
24610
  };
24142
- var LookupActionArgsType = type42({
24611
+ var LookupActionArgsType = type43({
24143
24612
  topic: "string",
24144
24613
  "depth?": "'brief' | 'full'"
24145
24614
  });
24146
24615
  function parseLookupActionArgs(raw) {
24147
24616
  const parsed = LookupActionArgsType(raw);
24148
- if (parsed instanceof type42.errors) {
24617
+ if (parsed instanceof type43.errors) {
24149
24618
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
24150
24619
  }
24151
24620
  const result = {
@@ -24443,7 +24912,7 @@ function createLookupActionTool(input) {
24443
24912
  }
24444
24913
 
24445
24914
  // packages/dbx-components-mcp/src/tools/action-examples.tool.ts
24446
- import { type as type43 } from "arktype";
24915
+ import { type as type44 } from "arktype";
24447
24916
 
24448
24917
  // packages/dbx-components-mcp/src/tools/data/patterns/actions/action.auto-trigger-on-modify.pattern.ts
24449
24918
  var ACTION_PATTERN_AUTO_TRIGGER_ON_MODIFY = {
@@ -24803,13 +25272,13 @@ var DBX_ACTION_EXAMPLES_TOOL = {
24803
25272
  required: ["pattern"]
24804
25273
  }
24805
25274
  };
24806
- var ActionExamplesArgsType = type43({
25275
+ var ActionExamplesArgsType = type44({
24807
25276
  pattern: "string",
24808
25277
  "depth?": "'minimal' | 'brief' | 'full'"
24809
25278
  });
24810
25279
  function parseActionExamplesArgs(raw) {
24811
25280
  const parsed = ActionExamplesArgsType(raw);
24812
- if (parsed instanceof type43.errors) {
25281
+ if (parsed instanceof type44.errors) {
24813
25282
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
24814
25283
  }
24815
25284
  const result = {
@@ -24871,7 +25340,7 @@ var actionExamplesTool = {
24871
25340
  };
24872
25341
 
24873
25342
  // packages/dbx-components-mcp/src/tools/action-scaffold.tool.ts
24874
- import { type as type44 } from "arktype";
25343
+ import { type as type45 } from "arktype";
24875
25344
  var TRIGGER_VALUES = ["button", "form", "auto-modify"];
24876
25345
  var SUCCESS_FEEDBACK_VALUES = ["snackbar", "redirect", "none"];
24877
25346
  var CONTEXT_PROVIDER_VALUES = ["inline", "parent", "host-component"];
@@ -24905,7 +25374,7 @@ var DBX_ACTION_SCAFFOLD_TOOL = {
24905
25374
  required: ["use_case", "trigger", "value_type"]
24906
25375
  }
24907
25376
  };
24908
- var ScaffoldArgsType4 = type44({
25377
+ var ScaffoldArgsType4 = type45({
24909
25378
  use_case: "string",
24910
25379
  trigger: "'button' | 'form' | 'auto-modify'",
24911
25380
  "confirm?": "boolean",
@@ -24916,7 +25385,7 @@ var ScaffoldArgsType4 = type44({
24916
25385
  });
24917
25386
  function parseScaffoldArgs2(raw) {
24918
25387
  const parsed = ScaffoldArgsType4(raw);
24919
- if (parsed instanceof type44.errors) {
25388
+ if (parsed instanceof type45.errors) {
24920
25389
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
24921
25390
  }
24922
25391
  const useCase = parsed.use_case.trim();
@@ -25122,11 +25591,11 @@ var actionScaffoldTool = {
25122
25591
  };
25123
25592
 
25124
25593
  // packages/dbx-components-mcp/src/tools/route-tree.tool.ts
25125
- import { type as type45 } from "arktype";
25594
+ import { type as type46 } from "arktype";
25126
25595
 
25127
25596
  // packages/dbx-components-mcp/src/tools/route/load-sources.ts
25128
25597
  import { glob as fsGlob4, readFile as readFile16, stat as stat12 } from "node:fs/promises";
25129
- import { dirname as dirname7, isAbsolute as isAbsolute7, resolve as resolve27, sep as sep9 } from "node:path";
25598
+ import { dirname as dirname8, isAbsolute as isAbsolute8, resolve as resolve28, sep as sep9 } from "node:path";
25130
25599
 
25131
25600
  // packages/dbx-components-mcp/src/tools/route/extract.ts
25132
25601
  import { Node as Node19, Project as Project16, SyntaxKind as SyntaxKind12 } from "ts-morph";
@@ -25441,7 +25910,7 @@ function computeRelativeSpecifiers(source) {
25441
25910
  // packages/dbx-components-mcp/src/tools/route/load-sources.ts
25442
25911
  var TRY_EXTENSIONS = [".ts", ".tsx", "/index.ts", "/index.tsx"];
25443
25912
  async function loadRouteSources(args) {
25444
- const cwdResolved = args.cwd ? resolve27(process.cwd(), args.cwd) : process.cwd();
25913
+ const cwdResolved = args.cwd ? resolve28(process.cwd(), args.cwd) : process.cwd();
25445
25914
  guardCwdInsideServer(args.cwd, cwdResolved);
25446
25915
  const collected = /* @__PURE__ */ new Map();
25447
25916
  if (args.sources) {
@@ -25513,7 +25982,7 @@ async function loadOne(relative17, cwd, into) {
25513
25982
  if (into.has(relative17)) {
25514
25983
  return;
25515
25984
  }
25516
- const absolute = resolve27(cwd, relative17);
25985
+ const absolute = resolve28(cwd, relative17);
25517
25986
  const cwdPrefix = cwd.endsWith(sep9) ? cwd : cwd + sep9;
25518
25987
  if (!absolute.startsWith(cwdPrefix) && absolute !== cwd) {
25519
25988
  throw new Error(`Path \`${relative17}\` resolves outside the server cwd and is not allowed.`);
@@ -25525,9 +25994,9 @@ async function resolveRelativeImport(fromFile, specifier, cwd) {
25525
25994
  if (!specifier.startsWith(".")) {
25526
25995
  return void 0;
25527
25996
  }
25528
- const fromAbsolute = isAbsolute7(fromFile) ? fromFile : resolve27(cwd, fromFile);
25529
- const fromDir = dirname7(fromAbsolute);
25530
- const baseAbsolute = resolve27(fromDir, specifier);
25997
+ const fromAbsolute = isAbsolute8(fromFile) ? fromFile : resolve28(cwd, fromFile);
25998
+ const fromDir = dirname8(fromAbsolute);
25999
+ const baseAbsolute = resolve28(fromDir, specifier);
25531
26000
  const cwdPrefix = cwd.endsWith(sep9) ? cwd : cwd + sep9;
25532
26001
  for (const ext of TRY_EXTENSIONS) {
25533
26002
  const candidateAbsolute = baseAbsolute + ext;
@@ -25906,8 +26375,8 @@ var DBX_ROUTE_TREE_TOOL = {
25906
26375
  }
25907
26376
  }
25908
26377
  };
25909
- var TreeArgsType2 = type45({
25910
- "sources?": type45({ name: "string", text: "string" }).array(),
26378
+ var TreeArgsType2 = type46({
26379
+ "sources?": type46({ name: "string", text: "string" }).array(),
25911
26380
  "paths?": "string[]",
25912
26381
  "glob?": "string",
25913
26382
  "format?": "'markdown' | 'json' | 'flat'",
@@ -25916,7 +26385,7 @@ var TreeArgsType2 = type45({
25916
26385
  });
25917
26386
  function parseArgs4(raw) {
25918
26387
  const parsed = TreeArgsType2(raw);
25919
- if (parsed instanceof type45.errors) {
26388
+ if (parsed instanceof type46.errors) {
25920
26389
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
25921
26390
  }
25922
26391
  const result = {
@@ -25966,7 +26435,7 @@ var routeTreeTool = {
25966
26435
  };
25967
26436
 
25968
26437
  // packages/dbx-components-mcp/src/tools/route-lookup.tool.ts
25969
- import { type as type46 } from "arktype";
26438
+ import { type as type47 } from "arktype";
25970
26439
  var DBX_ROUTE_LOOKUP_TOOL = {
25971
26440
  name: "dbx_route_lookup",
25972
26441
  description: ["Look up a single UIRouter state by name, full URL, or component class name. Returns the state's URL, component, parent chain, params, resolves, siblings, and children.", "", "Provide at least one of `sources` / `paths` / `glob` to point the tool at the source set, plus a `topic`.", "", "Resolution order: exact state name \u2192 URL path \u2192 component class name \u2192 fuzzy substring (returns up to 5 candidates)."].join("\n"),
@@ -25991,9 +26460,9 @@ var DBX_ROUTE_LOOKUP_TOOL = {
25991
26460
  required: ["topic"]
25992
26461
  }
25993
26462
  };
25994
- var LookupArgsType3 = type46({
26463
+ var LookupArgsType3 = type47({
25995
26464
  topic: "string",
25996
- "sources?": type46({ name: "string", text: "string" }).array(),
26465
+ "sources?": type47({ name: "string", text: "string" }).array(),
25997
26466
  "paths?": "string[]",
25998
26467
  "glob?": "string",
25999
26468
  "depth?": "'brief' | 'full'",
@@ -26001,7 +26470,7 @@ var LookupArgsType3 = type46({
26001
26470
  });
26002
26471
  function parseArgs5(raw) {
26003
26472
  const parsed = LookupArgsType3(raw);
26004
- if (parsed instanceof type46.errors) {
26473
+ if (parsed instanceof type47.errors) {
26005
26474
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
26006
26475
  }
26007
26476
  const result = {
@@ -26194,7 +26663,7 @@ var routeLookupTool = {
26194
26663
  };
26195
26664
 
26196
26665
  // packages/dbx-components-mcp/src/tools/route-search.tool.ts
26197
- import { type as type47 } from "arktype";
26666
+ import { type as type48 } from "arktype";
26198
26667
  var DBX_ROUTE_SEARCH_TOOL = {
26199
26668
  name: "dbx_route_search",
26200
26669
  description: ["Search the UIRouter state tree of a dbx-components app. Matches against state names, full URLs, component class names, resolve keys, and source-file paths. Returns the top 10 results with state name, URL, component, and source location.", "", "Provide at least one of `sources` / `paths` / `glob` plus a `query`. `scope` narrows to a single axis (`name`, `url`, `component`, `resolve`)."].join("\n"),
@@ -26218,9 +26687,9 @@ var DBX_ROUTE_SEARCH_TOOL = {
26218
26687
  required: ["query"]
26219
26688
  }
26220
26689
  };
26221
- var SearchArgsType3 = type47({
26690
+ var SearchArgsType3 = type48({
26222
26691
  query: "string",
26223
- "sources?": type47({ name: "string", text: "string" }).array(),
26692
+ "sources?": type48({ name: "string", text: "string" }).array(),
26224
26693
  "paths?": "string[]",
26225
26694
  "glob?": "string",
26226
26695
  "scope?": "'name' | 'url' | 'component' | 'resolve' | 'all'",
@@ -26228,7 +26697,7 @@ var SearchArgsType3 = type47({
26228
26697
  });
26229
26698
  function parseArgs6(raw) {
26230
26699
  const parsed = SearchArgsType3(raw);
26231
- if (parsed instanceof type47.errors) {
26700
+ if (parsed instanceof type48.errors) {
26232
26701
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
26233
26702
  }
26234
26703
  const result = {
@@ -26321,14 +26790,14 @@ var routeSearchTool = {
26321
26790
  };
26322
26791
 
26323
26792
  // packages/dbx-components-mcp/src/tools/_lookup.factory.ts
26324
- import { type as type48 } from "arktype";
26325
- var LOOKUP_ARGS_TYPE = type48({
26793
+ import { type as type49 } from "arktype";
26794
+ var LOOKUP_ARGS_TYPE = type49({
26326
26795
  topic: "string",
26327
26796
  "depth?": "'brief' | 'full'"
26328
26797
  });
26329
26798
  function parseLookupArgs(raw) {
26330
26799
  const parsed = LOOKUP_ARGS_TYPE(raw);
26331
- if (parsed instanceof type48.errors) {
26800
+ if (parsed instanceof type49.errors) {
26332
26801
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
26333
26802
  }
26334
26803
  const result = {
@@ -26530,7 +26999,7 @@ function createLookupFilterTool(input) {
26530
26999
  }
26531
27000
 
26532
27001
  // packages/dbx-components-mcp/src/tools/filter-scaffold.tool.ts
26533
- import { type as type49 } from "arktype";
27002
+ import { type as type50 } from "arktype";
26534
27003
  var DBX_FILTER_SCAFFOLD_TOOL = {
26535
27004
  name: "dbx_filter_scaffold",
26536
27005
  description: [
@@ -26555,7 +27024,7 @@ var DBX_FILTER_SCAFFOLD_TOOL = {
26555
27024
  required: ["model_name", "filter_type"]
26556
27025
  }
26557
27026
  };
26558
- var ScaffoldArgsType5 = type49({
27027
+ var ScaffoldArgsType5 = type50({
26559
27028
  model_name: "string",
26560
27029
  filter_type: "string",
26561
27030
  "preset_keys?": "string[]",
@@ -26563,7 +27032,7 @@ var ScaffoldArgsType5 = type49({
26563
27032
  });
26564
27033
  function parseArgs7(raw) {
26565
27034
  const parsed = ScaffoldArgsType5(raw);
26566
- if (parsed instanceof type49.errors) {
27035
+ if (parsed instanceof type50.errors) {
26567
27036
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
26568
27037
  }
26569
27038
  const modelName = parsed.model_name.trim();
@@ -26803,8 +27272,8 @@ function createLookupPipeTool(input) {
26803
27272
 
26804
27273
  // packages/dbx-components-mcp/src/tools/artifact-scaffold.tool.ts
26805
27274
  import { access } from "node:fs/promises";
26806
- import { resolve as resolve28, sep as sep10 } from "node:path";
26807
- import { type as type50 } from "arktype";
27275
+ import { resolve as resolve29, sep as sep10 } from "node:path";
27276
+ import { type as type51 } from "arktype";
26808
27277
 
26809
27278
  // packages/dbx-components-mcp/src/tools/artifact-scaffold/templates.ts
26810
27279
  function splitWords(input) {
@@ -27352,7 +27821,7 @@ var DBX_ARTIFACT_SCAFFOLD_TOOL = {
27352
27821
  required: ["artifact", "name", "componentDir", "apiDir"]
27353
27822
  }
27354
27823
  };
27355
- var ScaffoldArtifactArgsType = type50({
27824
+ var ScaffoldArtifactArgsType = type51({
27356
27825
  artifact: ARTIFACT_KIND_LITERAL_UNION,
27357
27826
  name: "string",
27358
27827
  componentDir: "string",
@@ -27365,7 +27834,7 @@ var ScaffoldArtifactArgsType = type50({
27365
27834
  });
27366
27835
  function parseArgs8(raw) {
27367
27836
  const parsed = ScaffoldArtifactArgsType(raw);
27368
- if (parsed instanceof type50.errors) {
27837
+ if (parsed instanceof type51.errors) {
27369
27838
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
27370
27839
  }
27371
27840
  const result = {
@@ -27378,7 +27847,7 @@ function parseArgs8(raw) {
27378
27847
  return result;
27379
27848
  }
27380
27849
  function ensureInsideCwd(relativePath, cwd) {
27381
- const absolute = resolve28(cwd, relativePath);
27850
+ const absolute = resolve29(cwd, relativePath);
27382
27851
  const cwdPrefix = cwd.endsWith(sep10) ? cwd : cwd + sep10;
27383
27852
  if (!absolute.startsWith(cwdPrefix) && absolute !== cwd) {
27384
27853
  throw new Error(`Path \`${relativePath}\` resolves outside the server cwd and is not allowed.`);
@@ -27418,7 +27887,7 @@ async function runArtifactScaffold(rawArgs) {
27418
27887
  const message = err instanceof Error ? err.message : String(err);
27419
27888
  return toolError(`Scaffold failed: ${message}`);
27420
27889
  }
27421
- const checked = await applyIdempotency(raw, (relativePath) => fileExists2(resolve28(cwd, relativePath)));
27890
+ const checked = await applyIdempotency(raw, (relativePath) => fileExists2(resolve29(cwd, relativePath)));
27422
27891
  const text = formatResult9(checked);
27423
27892
  const result = {
27424
27893
  content: [{ type: "text", text }]
@@ -27431,7 +27900,7 @@ var artifactScaffoldTool = {
27431
27900
  };
27432
27901
 
27433
27902
  // packages/dbx-components-mcp/src/tools/artifact-file-convention.tool.ts
27434
- import { type as type51 } from "arktype";
27903
+ import { type as type52 } from "arktype";
27435
27904
 
27436
27905
  // packages/dbx-components-mcp/src/tools/artifact-file-convention/spec.ts
27437
27906
  var FIRESTORE_MODEL = {
@@ -27867,7 +28336,7 @@ var DBX_ARTIFACT_FILE_CONVENTION_TOOL = {
27867
28336
  required: ["artifact"]
27868
28337
  }
27869
28338
  };
27870
- var FileConventionArgsType = type51({
28339
+ var FileConventionArgsType = type52({
27871
28340
  artifact: ARTIFACT_KIND_LITERAL_UNION2,
27872
28341
  "componentDir?": "string",
27873
28342
  "apiDir?": "string",
@@ -27875,7 +28344,7 @@ var FileConventionArgsType = type51({
27875
28344
  });
27876
28345
  function parseArgs9(raw) {
27877
28346
  const parsed = FileConventionArgsType(raw);
27878
- if (parsed instanceof type51.errors) {
28347
+ if (parsed instanceof type52.errors) {
27879
28348
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
27880
28349
  }
27881
28350
  const result = {
@@ -27908,8 +28377,8 @@ var artifactFileConventionTool = {
27908
28377
  };
27909
28378
 
27910
28379
  // packages/dbx-components-mcp/src/tools/explain-rule.tool.ts
27911
- import { type as type52 } from "arktype";
27912
- var ExplainRuleArgsType = type52({
28380
+ import { type as type53 } from "arktype";
28381
+ var ExplainRuleArgsType = type53({
27913
28382
  code: "string",
27914
28383
  "format?": "'markdown' | 'json'"
27915
28384
  });
@@ -27938,7 +28407,7 @@ var TOOL11 = {
27938
28407
  };
27939
28408
  async function run10(rawArgs) {
27940
28409
  const parsed = ExplainRuleArgsType(rawArgs);
27941
- if (parsed instanceof type52.errors) {
28410
+ if (parsed instanceof type53.errors) {
27942
28411
  return toolError(`Invalid arguments: ${parsed.summary}`);
27943
28412
  }
27944
28413
  const exact = findRule(parsed.code);
@@ -27963,11 +28432,11 @@ async function run10(rawArgs) {
27963
28432
  var explainRuleTool = { definition: TOOL11, run: run10 };
27964
28433
 
27965
28434
  // packages/dbx-components-mcp/src/tools/app-validate.tool.ts
27966
- import { resolve as resolve29, join as join15 } from "node:path";
28435
+ import { resolve as resolve30, join as join15 } from "node:path";
27967
28436
  import { readdir as readdir10, stat as stat13 } from "node:fs/promises";
27968
- import { type as type53 } from "arktype";
28437
+ import { type as type54 } from "arktype";
27969
28438
  var Cluster = "'storagefile_m' | 'notification_m' | 'model_folder' | 'system_m' | 'fixture'";
27970
- var AppValidateArgsType = type53({
28439
+ var AppValidateArgsType = type54({
27971
28440
  componentDir: "string",
27972
28441
  apiDir: "string",
27973
28442
  "format?": "'markdown' | 'json'",
@@ -28007,7 +28476,7 @@ var TOOL12 = {
28007
28476
  };
28008
28477
  async function run11(rawArgs) {
28009
28478
  const parsed = AppValidateArgsType(rawArgs);
28010
- if (parsed instanceof type53.errors) {
28479
+ if (parsed instanceof type54.errors) {
28011
28480
  return toolError(`Invalid arguments: ${parsed.summary}`);
28012
28481
  }
28013
28482
  const cwd = process.cwd();
@@ -28017,8 +28486,8 @@ async function run11(rawArgs) {
28017
28486
  } catch (err) {
28018
28487
  return toolError(err instanceof Error ? err.message : String(err));
28019
28488
  }
28020
- const componentAbs = resolve29(cwd, parsed.componentDir);
28021
- const apiAbs = resolve29(cwd, parsed.apiDir);
28489
+ const componentAbs = resolve30(cwd, parsed.componentDir);
28490
+ const apiAbs = resolve30(cwd, parsed.apiDir);
28022
28491
  const skip = new Set(parsed.skip ?? []);
28023
28492
  const findings = [];
28024
28493
  const skipped = [];
@@ -28216,8 +28685,8 @@ function formatJson(report) {
28216
28685
  var appValidateTool = { definition: TOOL12, run: run11 };
28217
28686
 
28218
28687
  // packages/dbx-components-mcp/src/tools/model-list-component.tool.ts
28219
- import { resolve as resolve30 } from "node:path";
28220
- import { type as type54 } from "arktype";
28688
+ import { resolve as resolve31 } from "node:path";
28689
+ import { type as type55 } from "arktype";
28221
28690
 
28222
28691
  // packages/dbx-components-mcp/src/tools/model-list-component/extract.ts
28223
28692
  import { readdir as readdir11, readFile as readFile17, stat as stat14 } from "node:fs/promises";
@@ -28485,7 +28954,7 @@ function stripCoverage(model) {
28485
28954
  }
28486
28955
 
28487
28956
  // packages/dbx-components-mcp/src/tools/model-list-component.tool.ts
28488
- var ListArgsType3 = type54({
28957
+ var ListArgsType3 = type55({
28489
28958
  componentDir: "string",
28490
28959
  "apiDir?": "string",
28491
28960
  "format?": "'markdown' | 'json'"
@@ -28516,7 +28985,7 @@ var TOOL13 = {
28516
28985
  };
28517
28986
  async function run12(rawArgs) {
28518
28987
  const parsed = ListArgsType3(rawArgs);
28519
- if (parsed instanceof type54.errors) {
28988
+ if (parsed instanceof type55.errors) {
28520
28989
  return toolError(`Invalid arguments: ${parsed.summary}`);
28521
28990
  }
28522
28991
  const cwd = process.cwd();
@@ -28526,8 +28995,8 @@ async function run12(rawArgs) {
28526
28995
  } catch (err) {
28527
28996
  return toolError(err instanceof Error ? err.message : String(err));
28528
28997
  }
28529
- const componentAbs = resolve30(cwd, parsed.componentDir);
28530
- const apiAbs = parsed.apiDir ? resolve30(cwd, parsed.apiDir) : void 0;
28998
+ const componentAbs = resolve31(cwd, parsed.componentDir);
28999
+ const apiAbs = parsed.apiDir ? resolve31(cwd, parsed.apiDir) : void 0;
28531
29000
  let report;
28532
29001
  try {
28533
29002
  report = await listComponentModels(componentAbs, { componentDir: parsed.componentDir, apiDir: parsed.apiDir, apiAbs });
@@ -28541,12 +29010,12 @@ async function run12(rawArgs) {
28541
29010
  var modelListComponentTool = { definition: TOOL13, run: run12 };
28542
29011
 
28543
29012
  // packages/dbx-components-mcp/src/tools/server-actions-list-app.tool.ts
28544
- import { resolve as resolve31 } from "node:path";
28545
- import { type as type55 } from "arktype";
29013
+ import { resolve as resolve32 } from "node:path";
29014
+ import { type as type56 } from "arktype";
28546
29015
 
28547
29016
  // packages/dbx-components-mcp/src/tools/server-actions-list-app/extract.ts
28548
29017
  import { readdir as readdir12, readFile as readFile18, stat as stat15 } from "node:fs/promises";
28549
- import { dirname as dirname8, join as join17, relative as relative11, sep as sep12 } from "node:path";
29018
+ import { dirname as dirname9, join as join17, relative as relative11, sep as sep12 } from "node:path";
28550
29019
  import { Node as Node21, Project as Project18 } from "ts-morph";
28551
29020
  var ACTION_SERVER_SUFFIX2 = ".action.server.ts";
28552
29021
  var COMMON_MODEL_SUBPATH2 = "src/app/common/model";
@@ -28654,7 +29123,7 @@ async function inspectSiblingModule(actionFilePath, className) {
28654
29123
  return { modulePath: moduleCandidates[0], providedByModule: false, exportedByModule: false };
28655
29124
  }
28656
29125
  async function findModuleCandidates(actionFilePath) {
28657
- const dir = dirname8(actionFilePath);
29126
+ const dir = dirname9(actionFilePath);
28658
29127
  const out = [];
28659
29128
  try {
28660
29129
  const entries = await readdir12(dir, { withFileTypes: true });
@@ -28841,7 +29310,7 @@ async function listAppServerActions(apiAbs, options) {
28841
29310
  }
28842
29311
 
28843
29312
  // packages/dbx-components-mcp/src/tools/server-actions-list-app.tool.ts
28844
- var ListArgsType4 = type55({
29313
+ var ListArgsType4 = type56({
28845
29314
  apiDir: "string",
28846
29315
  "format?": "'markdown' | 'json'"
28847
29316
  });
@@ -28870,7 +29339,7 @@ var TOOL14 = {
28870
29339
  };
28871
29340
  async function run13(rawArgs) {
28872
29341
  const parsed = ListArgsType4(rawArgs);
28873
- if (parsed instanceof type55.errors) {
29342
+ if (parsed instanceof type56.errors) {
28874
29343
  return toolError(`Invalid arguments: ${parsed.summary}`);
28875
29344
  }
28876
29345
  const cwd = process.cwd();
@@ -28879,7 +29348,7 @@ async function run13(rawArgs) {
28879
29348
  } catch (err) {
28880
29349
  return toolError(err instanceof Error ? err.message : String(err));
28881
29350
  }
28882
- const apiAbs = resolve31(cwd, parsed.apiDir);
29351
+ const apiAbs = resolve32(cwd, parsed.apiDir);
28883
29352
  let report;
28884
29353
  try {
28885
29354
  report = await listAppServerActions(apiAbs, { apiDir: parsed.apiDir });
@@ -28893,16 +29362,16 @@ async function run13(rawArgs) {
28893
29362
  var serverActionsListAppTool = { definition: TOOL14, run: run13 };
28894
29363
 
28895
29364
  // packages/dbx-components-mcp/src/tools/mcp-config.tool.ts
28896
- import { type as type62 } from "arktype";
28897
- import { resolve as resolve38 } from "node:path";
29365
+ import { type as type63 } from "arktype";
29366
+ import { resolve as resolve39 } from "node:path";
28898
29367
 
28899
29368
  // packages/dbx-components-mcp/src/tools/mcp-config/snapshot.ts
28900
- import { dirname as dirname9, isAbsolute as isAbsolute8, relative as relative12, resolve as resolve33 } from "node:path";
29369
+ import { dirname as dirname10, isAbsolute as isAbsolute9, relative as relative12, resolve as resolve34 } from "node:path";
28901
29370
  import { stat as stat17 } from "node:fs/promises";
28902
29371
 
28903
29372
  // packages/dbx-components-mcp/src/scan/discover-downstream-packages.ts
28904
29373
  import { glob as fsGlob5, readFile as readFile19, stat as stat16 } from "node:fs/promises";
28905
- import { basename as basename7, join as join18, resolve as resolve32, sep as sep13 } from "node:path";
29374
+ import { basename as basename7, join as join18, resolve as resolve33, sep as sep13 } from "node:path";
28906
29375
  var COMPONENT_GLOBS = ["components/*-firebase", "components/*-shared", "components/*-web", "components/*-core"];
28907
29376
  var APPS_GLOB = "apps/*";
28908
29377
  var SCAN_CONFIG_FILENAME2 = "dbx-mcp.scan.json";
@@ -28947,7 +29416,7 @@ function normaliseExplicitDirs(dirs) {
28947
29416
  return result;
28948
29417
  }
28949
29418
  async function inspectPackage2(workspaceRoot, relDir) {
28950
- const absDir = resolve32(workspaceRoot, relDir);
29419
+ const absDir = resolve33(workspaceRoot, relDir);
28951
29420
  const isDir = await isDirectory2(absDir);
28952
29421
  if (!isDir) return void 0;
28953
29422
  const packageName = await readPackageName2(absDir, relDir);
@@ -29053,7 +29522,7 @@ function mergeClusters(...lists) {
29053
29522
  async function buildSnapshot(input) {
29054
29523
  const { workspaceRoot, explicitDirs } = input;
29055
29524
  const configResult = await findAndLoadConfig({ cwd: workspaceRoot });
29056
- const configBaseDir = configResult.configPath !== null ? dirname9(configResult.configPath) : workspaceRoot;
29525
+ const configBaseDir = configResult.configPath !== null ? dirname10(configResult.configPath) : workspaceRoot;
29057
29526
  const registeredSources = await collectRegisteredSources(configResult.config, configBaseDir, workspaceRoot);
29058
29527
  const packages = await discoverDownstreamPackages({ workspaceRoot, explicitDirs });
29059
29528
  const packageSnapshots = packages.map((pkg) => ({
@@ -29075,7 +29544,7 @@ async function collectRegisteredSources(config, configBaseDir, workspaceRoot) {
29075
29544
  for (const cluster of DOWNSTREAM_CLUSTERS) {
29076
29545
  const declared = config[cluster]?.sources ?? [];
29077
29546
  for (const declaredPath of declared) {
29078
- const absolutePath = isAbsolute8(declaredPath) ? declaredPath : resolve33(configBaseDir, declaredPath);
29547
+ const absolutePath = isAbsolute9(declaredPath) ? declaredPath : resolve34(configBaseDir, declaredPath);
29079
29548
  const exists = await fileExists4(absolutePath);
29080
29549
  const relativePath = relativeOrAbsolute(workspaceRoot, absolutePath);
29081
29550
  out.push({ cluster, declaredPath, absolutePath, relativePath, exists });
@@ -29113,8 +29582,19 @@ function relativeOrAbsolute(from, target) {
29113
29582
 
29114
29583
  // packages/dbx-components-mcp/src/tools/mcp-config/init.ts
29115
29584
  import { writeFile as nodeWriteFile3, mkdir as nodeMkdir, readFile as nodeReadFile7 } from "node:fs/promises";
29116
- import { dirname as dirname10, relative as relative13, resolve as resolve34 } from "node:path";
29585
+ import { dirname as dirname11, relative as relative13, resolve as resolve35 } from "node:path";
29117
29586
  var HEURISTIC_CLUSTERS = ["uiComponents", "forgeFields", "pipes", "actions", "filters"];
29587
+ var CLUSTER_FILENAME_SEGMENT = {
29588
+ semanticTypes: "semantic",
29589
+ uiComponents: "ui",
29590
+ forgeFields: "forge",
29591
+ pipes: "pipes",
29592
+ actions: "actions",
29593
+ filters: "filters"
29594
+ };
29595
+ function clusterFilenameSegment(cluster) {
29596
+ return CLUSTER_FILENAME_SEGMENT[cluster];
29597
+ }
29118
29598
  var DEFAULT_INCLUDE = ["src/**/*.ts"];
29119
29599
  var DEFAULT_EXCLUDE = ["**/*.spec.ts", "**/*.test.ts", "**/*.stories.ts"];
29120
29600
  async function buildInitPlan(input) {
@@ -29136,14 +29616,14 @@ async function applyInitPlan(plan, opts) {
29136
29616
  const mkdir = opts?.mkdir ?? defaultMkdir;
29137
29617
  for (const change of plan.changes) {
29138
29618
  if (change.reason === "unchanged") continue;
29139
- await mkdir(dirname10(change.absolutePath));
29619
+ await mkdir(dirname11(change.absolutePath));
29140
29620
  await writeFile2(change.absolutePath, change.after);
29141
29621
  }
29142
29622
  }
29143
29623
  async function planPackageScanConfig(input) {
29144
29624
  const { snapshot, packageSnapshot, readFile: readFile20 } = input;
29145
29625
  const { pkg } = packageSnapshot;
29146
- const scanConfigPath = resolve34(pkg.absDir, "dbx-mcp.scan.json");
29626
+ const scanConfigPath = resolve35(pkg.absDir, "dbx-mcp.scan.json");
29147
29627
  const before = await tryRead(scanConfigPath, readFile20);
29148
29628
  const parsed = parseExisting(before);
29149
29629
  const targetClusters = HEURISTIC_CLUSTERS.filter((c) => pkg.candidateClusters.includes(c));
@@ -29154,7 +29634,7 @@ async function planPackageScanConfig(input) {
29154
29634
  if (next.version !== 1) next.version = 1;
29155
29635
  const sourcesToRegister = [];
29156
29636
  if (typeof next.topicNamespace === "string" && Array.isArray(next.include)) {
29157
- const outValue = typeof next.out === "string" && next.out.length > 0 ? next.out : `${pkg.slug}.semantic-types.json`;
29637
+ const outValue = typeof next.out === "string" && next.out.length > 0 ? next.out : `${pkg.slug}.${clusterFilenameSegment("semanticTypes")}.json`;
29158
29638
  const declaredOut = resolveScanOut(scanConfigPath, outValue);
29159
29639
  sourcesToRegister.push({ cluster: "semanticTypes", relPath: relativeFromConfig(snapshot, declaredOut) });
29160
29640
  }
@@ -29187,7 +29667,7 @@ async function planPackageScanConfig(input) {
29187
29667
  }
29188
29668
  async function planRootConfig(input) {
29189
29669
  const { snapshot, sourcesToRegister, readFile: readFile20 } = input;
29190
- const configPath = snapshot.configPath ?? resolve34(snapshot.workspaceRoot, "dbx-mcp.config.json");
29670
+ const configPath = snapshot.configPath ?? resolve35(snapshot.workspaceRoot, "dbx-mcp.config.json");
29191
29671
  const before = snapshot.configPath !== null ? await tryRead(configPath, readFile20) : null;
29192
29672
  const baseConfig = snapshot.config !== null ? cloneConfig(snapshot.config) : { version: 1 };
29193
29673
  if (baseConfig.version !== 1) baseConfig.version = 1;
@@ -29216,14 +29696,14 @@ async function planRootConfig(input) {
29216
29696
  };
29217
29697
  }
29218
29698
  function defaultOutFor(input) {
29219
- const targetAbs = resolve34(input.snapshot.workspaceRoot, ".tmp/dbx-mcp", `${input.slug}.${input.cluster}.json`);
29220
- return relative13(dirname10(input.scanConfigPath), targetAbs).split("\\").join("/");
29699
+ const targetAbs = resolve35(input.snapshot.workspaceRoot, ".tmp/dbx-mcp", `${input.slug}.${clusterFilenameSegment(input.cluster)}.json`);
29700
+ return relative13(dirname11(input.scanConfigPath), targetAbs).split("\\").join("/");
29221
29701
  }
29222
29702
  function resolveScanOut(scanConfigPath, out) {
29223
- return resolve34(dirname10(scanConfigPath), out);
29703
+ return resolve35(dirname11(scanConfigPath), out);
29224
29704
  }
29225
29705
  function relativeFromConfig(snapshot, absolute) {
29226
- const configDir = snapshot.configPath !== null ? dirname10(snapshot.configPath) : snapshot.workspaceRoot;
29706
+ const configDir = snapshot.configPath !== null ? dirname11(snapshot.configPath) : snapshot.workspaceRoot;
29227
29707
  return relative13(configDir, absolute).split("\\").join("/");
29228
29708
  }
29229
29709
  function workspaceRel(workspaceRoot, absolute) {
@@ -29267,8 +29747,8 @@ var defaultMkdir = async (dir) => {
29267
29747
  };
29268
29748
 
29269
29749
  // packages/dbx-components-mcp/src/scan/actions-build-manifest.ts
29270
- import { relative as relative14, resolve as resolve35 } from "node:path";
29271
- import { type as type57 } from "arktype";
29750
+ import { relative as relative14, resolve as resolve36 } from "node:path";
29751
+ import { type as type58 } from "arktype";
29272
29752
  import { Project as Project19 } from "ts-morph";
29273
29753
 
29274
29754
  // packages/dbx-components-mcp/src/scan/actions-extract.ts
@@ -29571,15 +30051,15 @@ function readMethodDescription(method) {
29571
30051
  }
29572
30052
 
29573
30053
  // packages/dbx-components-mcp/src/scan/actions-scan-config-schema.ts
29574
- import { type as type56 } from "arktype";
29575
- var ActionsScanSection = type56({
30054
+ import { type as type57 } from "arktype";
30055
+ var ActionsScanSection = type57({
29576
30056
  "source?": "string",
29577
30057
  "module?": "string",
29578
30058
  include: "string[] >= 1",
29579
30059
  "exclude?": "string[]",
29580
30060
  "out?": "string"
29581
30061
  });
29582
- var ActionsScanConfig = type56({
30062
+ var ActionsScanConfig = type57({
29583
30063
  version: "1",
29584
30064
  actions: ActionsScanSection
29585
30065
  });
@@ -29591,8 +30071,8 @@ var DEFAULT_READ_FILE9 = defaultReadFile;
29591
30071
  var DEFAULT_GLOBBER4 = defaultGlobber;
29592
30072
  async function buildActionsManifest(input) {
29593
30073
  const { projectRoot, generator, readFile: readFile20 = DEFAULT_READ_FILE9, globber = DEFAULT_GLOBBER4, now = () => /* @__PURE__ */ new Date() } = input;
29594
- const configPath = resolve35(projectRoot, ACTIONS_SCAN_CONFIG_FILENAME);
29595
- const packagePath = resolve35(projectRoot, "package.json");
30074
+ const configPath = resolve36(projectRoot, ACTIONS_SCAN_CONFIG_FILENAME);
30075
+ const packagePath = resolve36(projectRoot, "package.json");
29596
30076
  const configOutcome = await loadScanConfig4(configPath, readFile20);
29597
30077
  if (configOutcome.kind !== "ok") return configOutcome.outcome;
29598
30078
  const scanSection = configOutcome.section;
@@ -29602,7 +30082,7 @@ async function buildActionsManifest(input) {
29602
30082
  const filePaths = await globber({ projectRoot, include: scanSection.include, exclude: scanSection.exclude ?? [] });
29603
30083
  const project = new Project19({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
29604
30084
  for (const relPath of filePaths) {
29605
- const absolute = resolve35(projectRoot, relPath);
30085
+ const absolute = resolve36(projectRoot, relPath);
29606
30086
  const text = await readFile20(absolute);
29607
30087
  project.createSourceFile(absolute, text, { overwrite: true });
29608
30088
  }
@@ -29620,10 +30100,10 @@ async function buildActionsManifest(input) {
29620
30100
  };
29621
30101
  const validated = ActionManifest(manifest);
29622
30102
  let outcome;
29623
- if (validated instanceof type57.errors) {
30103
+ if (validated instanceof type58.errors) {
29624
30104
  outcome = { kind: "invalid-manifest", error: validated.summary };
29625
30105
  } else {
29626
- const outPath = resolve35(projectRoot, scanSection.out ?? DEFAULT_ACTIONS_SCAN_OUT_PATH);
30106
+ const outPath = resolve36(projectRoot, scanSection.out ?? DEFAULT_ACTIONS_SCAN_OUT_PATH);
29627
30107
  outcome = {
29628
30108
  kind: "success",
29629
30109
  manifest: validated,
@@ -29654,7 +30134,7 @@ async function loadScanConfig4(configPath, readFile20) {
29654
30134
  }
29655
30135
  if (parseError === null) {
29656
30136
  const validated = ActionsScanConfig(parsed);
29657
- if (validated instanceof type57.errors) {
30137
+ if (validated instanceof type58.errors) {
29658
30138
  result = { kind: "fail", outcome: { kind: "invalid-scan-config", configPath, error: validated.summary } };
29659
30139
  } else {
29660
30140
  result = { kind: "ok", section: validated.actions };
@@ -29785,8 +30265,8 @@ function formatExtractWarning3(warning) {
29785
30265
  }
29786
30266
 
29787
30267
  // packages/dbx-components-mcp/src/scan/filters-build-manifest.ts
29788
- import { relative as relative15, resolve as resolve36 } from "node:path";
29789
- import { type as type59 } from "arktype";
30268
+ import { relative as relative15, resolve as resolve37 } from "node:path";
30269
+ import { type as type60 } from "arktype";
29790
30270
  import { Project as Project20 } from "ts-morph";
29791
30271
 
29792
30272
  // packages/dbx-components-mcp/src/scan/filters-extract.ts
@@ -29979,15 +30459,15 @@ function extractOutputs3(decl) {
29979
30459
  }
29980
30460
 
29981
30461
  // packages/dbx-components-mcp/src/scan/filters-scan-config-schema.ts
29982
- import { type as type58 } from "arktype";
29983
- var FiltersScanSection = type58({
30462
+ import { type as type59 } from "arktype";
30463
+ var FiltersScanSection = type59({
29984
30464
  "source?": "string",
29985
30465
  "module?": "string",
29986
30466
  include: "string[] >= 1",
29987
30467
  "exclude?": "string[]",
29988
30468
  "out?": "string"
29989
30469
  });
29990
- var FiltersScanConfig = type58({
30470
+ var FiltersScanConfig = type59({
29991
30471
  version: "1",
29992
30472
  filters: FiltersScanSection
29993
30473
  });
@@ -29999,8 +30479,8 @@ var DEFAULT_READ_FILE10 = defaultReadFile;
29999
30479
  var DEFAULT_GLOBBER5 = defaultGlobber;
30000
30480
  async function buildFiltersManifest(input) {
30001
30481
  const { projectRoot, generator, readFile: readFile20 = DEFAULT_READ_FILE10, globber = DEFAULT_GLOBBER5, now = () => /* @__PURE__ */ new Date() } = input;
30002
- const configPath = resolve36(projectRoot, FILTERS_SCAN_CONFIG_FILENAME);
30003
- const packagePath = resolve36(projectRoot, "package.json");
30482
+ const configPath = resolve37(projectRoot, FILTERS_SCAN_CONFIG_FILENAME);
30483
+ const packagePath = resolve37(projectRoot, "package.json");
30004
30484
  const configOutcome = await loadScanConfig5(configPath, readFile20);
30005
30485
  if (configOutcome.kind !== "ok") {
30006
30486
  return configOutcome.outcome;
@@ -30018,7 +30498,7 @@ async function buildFiltersManifest(input) {
30018
30498
  });
30019
30499
  const project = new Project20({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
30020
30500
  for (const relPath of filePaths) {
30021
- const absolute = resolve36(projectRoot, relPath);
30501
+ const absolute = resolve37(projectRoot, relPath);
30022
30502
  const text = await readFile20(absolute);
30023
30503
  project.createSourceFile(absolute, text, { overwrite: true });
30024
30504
  }
@@ -30036,10 +30516,10 @@ async function buildFiltersManifest(input) {
30036
30516
  };
30037
30517
  const validated = FilterManifest(manifest);
30038
30518
  let outcome;
30039
- if (validated instanceof type59.errors) {
30519
+ if (validated instanceof type60.errors) {
30040
30520
  outcome = { kind: "invalid-manifest", error: validated.summary };
30041
30521
  } else {
30042
- const outPath = resolve36(projectRoot, scanSection.out ?? DEFAULT_FILTERS_SCAN_OUT_PATH);
30522
+ const outPath = resolve37(projectRoot, scanSection.out ?? DEFAULT_FILTERS_SCAN_OUT_PATH);
30043
30523
  outcome = {
30044
30524
  kind: "success",
30045
30525
  manifest: validated,
@@ -30070,7 +30550,7 @@ async function loadScanConfig5(configPath, readFile20) {
30070
30550
  }
30071
30551
  if (parseError === null) {
30072
30552
  const validated = FiltersScanConfig(parsed);
30073
- if (validated instanceof type59.errors) {
30553
+ if (validated instanceof type60.errors) {
30074
30554
  result = { kind: "fail", outcome: { kind: "invalid-scan-config", configPath, error: validated.summary } };
30075
30555
  } else {
30076
30556
  result = { kind: "ok", section: validated.filters };
@@ -30163,8 +30643,8 @@ function formatExtractWarning4(warning) {
30163
30643
  }
30164
30644
 
30165
30645
  // packages/dbx-components-mcp/src/scan/pipes-build-manifest.ts
30166
- import { relative as relative16, resolve as resolve37 } from "node:path";
30167
- import { type as type61 } from "arktype";
30646
+ import { relative as relative16, resolve as resolve38 } from "node:path";
30647
+ import { type as type62 } from "arktype";
30168
30648
  import { Project as Project21 } from "ts-morph";
30169
30649
 
30170
30650
  // packages/dbx-components-mcp/src/scan/pipes-extract.ts
@@ -30400,10 +30880,10 @@ function readTransformSignature(method, fallbackParamDescriptions) {
30400
30880
  for (let i = 1; i < params.length; i += 1) {
30401
30881
  const param = params[i];
30402
30882
  const name = param.getName();
30403
- const type65 = readParamType(param);
30883
+ const type68 = readParamType(param);
30404
30884
  const description = methodParamDescriptions.get(name) ?? fallbackParamDescriptions.get(name) ?? "";
30405
30885
  const required = !param.isOptional() && !param.hasInitializer();
30406
- args.push({ name, type: type65, description, required });
30886
+ args.push({ name, type: type68, description, required });
30407
30887
  }
30408
30888
  return { inputType, outputType, args };
30409
30889
  }
@@ -30440,15 +30920,15 @@ function extractParamName(rawTag) {
30440
30920
  }
30441
30921
 
30442
30922
  // packages/dbx-components-mcp/src/scan/pipes-scan-config-schema.ts
30443
- import { type as type60 } from "arktype";
30444
- var PipesScanSection = type60({
30923
+ import { type as type61 } from "arktype";
30924
+ var PipesScanSection = type61({
30445
30925
  "source?": "string",
30446
30926
  "module?": "string",
30447
30927
  include: "string[] >= 1",
30448
30928
  "exclude?": "string[]",
30449
30929
  "out?": "string"
30450
30930
  });
30451
- var PipesScanConfig = type60({
30931
+ var PipesScanConfig = type61({
30452
30932
  version: "1",
30453
30933
  pipes: PipesScanSection
30454
30934
  });
@@ -30460,8 +30940,8 @@ var DEFAULT_READ_FILE11 = defaultReadFile;
30460
30940
  var DEFAULT_GLOBBER6 = defaultGlobber;
30461
30941
  async function buildPipesManifest(input) {
30462
30942
  const { projectRoot, generator, readFile: readFile20 = DEFAULT_READ_FILE11, globber = DEFAULT_GLOBBER6, now = () => /* @__PURE__ */ new Date() } = input;
30463
- const configPath = resolve37(projectRoot, PIPES_SCAN_CONFIG_FILENAME);
30464
- const packagePath = resolve37(projectRoot, "package.json");
30943
+ const configPath = resolve38(projectRoot, PIPES_SCAN_CONFIG_FILENAME);
30944
+ const packagePath = resolve38(projectRoot, "package.json");
30465
30945
  const configOutcome = await loadScanConfig6(configPath, readFile20);
30466
30946
  if (configOutcome.kind !== "ok") {
30467
30947
  return configOutcome.outcome;
@@ -30479,7 +30959,7 @@ async function buildPipesManifest(input) {
30479
30959
  });
30480
30960
  const project = new Project21({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
30481
30961
  for (const relPath of filePaths) {
30482
- const absolute = resolve37(projectRoot, relPath);
30962
+ const absolute = resolve38(projectRoot, relPath);
30483
30963
  const text = await readFile20(absolute);
30484
30964
  project.createSourceFile(absolute, text, { overwrite: true });
30485
30965
  }
@@ -30497,10 +30977,10 @@ async function buildPipesManifest(input) {
30497
30977
  };
30498
30978
  const validated = PipeManifest(manifest);
30499
30979
  let outcome;
30500
- if (validated instanceof type61.errors) {
30980
+ if (validated instanceof type62.errors) {
30501
30981
  outcome = { kind: "invalid-manifest", error: validated.summary };
30502
30982
  } else {
30503
- const outPath = resolve37(projectRoot, scanSection.out ?? DEFAULT_PIPES_SCAN_OUT_PATH);
30983
+ const outPath = resolve38(projectRoot, scanSection.out ?? DEFAULT_PIPES_SCAN_OUT_PATH);
30504
30984
  outcome = {
30505
30985
  kind: "success",
30506
30986
  manifest: validated,
@@ -30531,7 +31011,7 @@ async function loadScanConfig6(configPath, readFile20) {
30531
31011
  }
30532
31012
  if (parseError === null) {
30533
31013
  const validated = PipesScanConfig(parsed);
30534
- if (validated instanceof type61.errors) {
31014
+ if (validated instanceof type62.errors) {
30535
31015
  result = { kind: "fail", outcome: { kind: "invalid-scan-config", configPath, error: validated.summary } };
30536
31016
  } else {
30537
31017
  result = { kind: "ok", section: validated.pipes };
@@ -30880,7 +31360,7 @@ var TOOL15 = {
30880
31360
  required: ["op"]
30881
31361
  }
30882
31362
  };
30883
- var McpConfigArgsType = type62({
31363
+ var McpConfigArgsType = type63({
30884
31364
  op: "'status' | 'validate' | 'init' | 'refresh'",
30885
31365
  "cwd?": "string",
30886
31366
  "dryRun?": "boolean",
@@ -30888,12 +31368,12 @@ var McpConfigArgsType = type62({
30888
31368
  });
30889
31369
  function parseArgs10(rawArgs, defaultCwd) {
30890
31370
  const parsed = McpConfigArgsType(rawArgs);
30891
- if (parsed instanceof type62.errors) {
31371
+ if (parsed instanceof type63.errors) {
30892
31372
  return `Invalid arguments: ${parsed.summary}`;
30893
31373
  }
30894
31374
  let workspaceRoot = defaultCwd;
30895
31375
  if (parsed.cwd !== void 0 && parsed.cwd.length > 0) {
30896
- workspaceRoot = resolve38(defaultCwd, parsed.cwd);
31376
+ workspaceRoot = resolve39(defaultCwd, parsed.cwd);
30897
31377
  }
30898
31378
  if (parsed.explicitDirs !== void 0) {
30899
31379
  for (const dir of parsed.explicitDirs) {
@@ -30945,7 +31425,7 @@ var mcpConfigTool = {
30945
31425
  };
30946
31426
 
30947
31427
  // packages/dbx-components-mcp/src/tools/lookup-semantic-type.tool.ts
30948
- import { type as type63 } from "arktype";
31428
+ import { type as type64 } from "arktype";
30949
31429
 
30950
31430
  // packages/dbx-components-mcp/src/tools/semantic-type-lookup.formatter.ts
30951
31431
  function formatSemanticTypeEntry(entry, depth) {
@@ -31126,13 +31606,13 @@ var DBX_SEMANTIC_TYPE_LOOKUP_TOOL = {
31126
31606
  required: ["name"]
31127
31607
  }
31128
31608
  };
31129
- var LookupSemanticTypeArgsType = type63({
31609
+ var LookupSemanticTypeArgsType = type64({
31130
31610
  name: "string",
31131
31611
  "depth?": "'brief' | 'full'"
31132
31612
  });
31133
31613
  function parseLookupArgs2(raw) {
31134
31614
  const parsed = LookupSemanticTypeArgsType(raw);
31135
- if (parsed instanceof type63.errors) {
31615
+ if (parsed instanceof type64.errors) {
31136
31616
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
31137
31617
  }
31138
31618
  const result = {
@@ -31194,7 +31674,7 @@ function formatNotFound11(name, registry) {
31194
31674
  }
31195
31675
 
31196
31676
  // packages/dbx-components-mcp/src/tools/search-semantic-type.tool.ts
31197
- import { type as type64 } from "arktype";
31677
+ import { type as type65 } from "arktype";
31198
31678
  var DEFAULT_LIMIT4 = 25;
31199
31679
  var MAX_LIMIT4 = 100;
31200
31680
  var DBX_SEMANTIC_TYPE_SEARCH_TOOL = {
@@ -31227,7 +31707,7 @@ var DBX_SEMANTIC_TYPE_SEARCH_TOOL = {
31227
31707
  }
31228
31708
  }
31229
31709
  };
31230
- var SearchSemanticTypeArgsType = type64({
31710
+ var SearchSemanticTypeArgsType = type65({
31231
31711
  "topic?": "string",
31232
31712
  "baseType?": "string",
31233
31713
  "package?": "string",
@@ -31236,7 +31716,7 @@ var SearchSemanticTypeArgsType = type64({
31236
31716
  });
31237
31717
  function parseSearchArgs2(raw) {
31238
31718
  const parsed = SearchSemanticTypeArgsType(raw);
31239
- if (parsed instanceof type64.errors) {
31719
+ if (parsed instanceof type65.errors) {
31240
31720
  throw new TypeError(`Invalid arguments: ${parsed.summary}`);
31241
31721
  }
31242
31722
  const rawLimit = parsed.limit ?? DEFAULT_LIMIT4;
@@ -31347,56 +31827,1273 @@ function describeFilters(args) {
31347
31827
  return parts.join(" \xB7 ");
31348
31828
  }
31349
31829
 
31350
- // packages/dbx-components-mcp/src/tools/index.ts
31351
- var DBX_TOOLS = [
31352
- // form
31353
- formExamplesTool,
31354
- // ui
31355
- uiExamplesTool,
31356
- // model
31357
- lookupModelTool,
31358
- searchModelTool,
31359
- modelDecodeTool,
31360
- modelValidateApiTool,
31361
- modelApiListAppTool,
31362
- modelApiLookupTool,
31363
- modelApiValidateAppTool,
31364
- modelValidateFolderTool,
31365
- modelStoreScaffoldTool,
31366
- modelFixtureListAppTool,
31367
- modelFixtureLookupTool,
31368
- modelFixtureScaffoldTool,
31369
- modelFixtureForwardTool,
31370
- modelTestTreeTool,
31371
- modelTestSearchTool,
31372
- // storagefile_m (model extension)
31373
- storageFileMValidateAppTool,
31374
- storageFileMListAppTool,
31375
- storageFileMValidateFolderTool,
31376
- // notification_m (model extension)
31377
- notificationMValidateAppTool,
31378
- notificationMListAppTool,
31379
- notificationMValidateFolderTool,
31380
- // system_m (model extension)
31381
- systemMValidateFolderTool,
31382
- // action
31383
- actionExamplesTool,
31384
- actionScaffoldTool,
31385
- // route
31386
- routeTreeTool,
31387
- routeLookupTool,
31388
- routeSearchTool,
31389
- // filter
31390
- filterScaffoldTool,
31391
- // artifact (cross-domain dispatchers)
31392
- artifactScaffoldTool,
31393
- artifactFileConventionTool,
31394
- // rule catalog (cross-domain reference)
31395
- explainRuleTool,
31396
- // aggregate orchestrator
31397
- appValidateTool,
31398
- // downstream component introspection
31399
- modelListComponentTool,
31830
+ // packages/dbx-components-mcp/src/tools/css-token-lookup.tool.ts
31831
+ import { type as type66 } from "arktype";
31832
+
31833
+ // packages/dbx-components-mcp/src/tools/css-token-lookup/synonyms.ts
31834
+ var INTENT_SYNONYMS = /* @__PURE__ */ new Map([
31835
+ ["subtitle", ["hint", "secondary text", "caption", "muted text"]],
31836
+ ["hint", ["secondary text", "subtitle", "caption", "muted text"]],
31837
+ ["caption", ["hint", "subtitle", "supporting text"]],
31838
+ ["muted", ["hint", "secondary", "subtitle"]],
31839
+ ["card", ["surface", "container", "content-box"]],
31840
+ ["card surface", ["surface", "container", "card background"]],
31841
+ ["card background", ["surface", "container"]],
31842
+ ["section gap", ["default padding", "12px", "padding 3", "card padding"]],
31843
+ ["gap", ["padding", "spacing"]],
31844
+ ["margin", ["spacing"]],
31845
+ ["radius", ["corner", "rounding"]],
31846
+ ["corner", ["radius", "rounding"]],
31847
+ ["shadow", ["elevation"]],
31848
+ ["error", ["warn", "destructive", "danger"]],
31849
+ ["warn", ["error", "destructive"]],
31850
+ ["accent", ["tertiary", "highlight"]],
31851
+ ["outline", ["border", "divider", "stroke"]],
31852
+ ["border", ["outline", "divider"]],
31853
+ ["divider", ["outline", "border"]]
31854
+ ]);
31855
+ function expandIntentQuery(query) {
31856
+ const trimmed = query.trim().toLowerCase();
31857
+ let result;
31858
+ if (trimmed.length === 0) {
31859
+ result = [];
31860
+ } else {
31861
+ const set = /* @__PURE__ */ new Set([trimmed]);
31862
+ const direct = INTENT_SYNONYMS.get(trimmed);
31863
+ if (direct !== void 0) {
31864
+ for (const value of direct) {
31865
+ set.add(value.toLowerCase());
31866
+ }
31867
+ }
31868
+ for (const [key, synonyms] of INTENT_SYNONYMS) {
31869
+ if (trimmed.includes(key)) {
31870
+ for (const value of synonyms) {
31871
+ set.add(value.toLowerCase());
31872
+ }
31873
+ }
31874
+ }
31875
+ result = [...set];
31876
+ }
31877
+ return result;
31878
+ }
31879
+
31880
+ // packages/dbx-components-mcp/src/tools/css-token-lookup/resolve.ts
31881
+ var CONFIDENT_THRESHOLD = 7;
31882
+ var MAX_MATCHES = 3;
31883
+ var SOURCE_RANK = { "mat-sys": 4, mdc: 3, "dbx-web": 2, app: 1 };
31884
+ function resolveToken(registry, input) {
31885
+ let candidates = registry.all;
31886
+ if (input.category !== void 0 && input.category !== "list") {
31887
+ candidates = candidates.filter((c) => c.source === input.category);
31888
+ }
31889
+ if (input.role !== void 0) {
31890
+ candidates = candidates.filter((c) => c.role === input.role);
31891
+ }
31892
+ if (input.component !== void 0) {
31893
+ candidates = candidates.filter((c) => c.componentScope === input.component);
31894
+ }
31895
+ const scored = /* @__PURE__ */ new Map();
31896
+ function add(entry, score) {
31897
+ if (score <= 0) return;
31898
+ const key = `${entry.source}::${entry.cssVariable}`;
31899
+ const previous = scored.get(key);
31900
+ if (previous === void 0 || previous < score) {
31901
+ scored.set(key, score);
31902
+ }
31903
+ }
31904
+ if (input.intent !== void 0 && input.intent.trim().length > 0) {
31905
+ const expanded = expandIntentQuery(input.intent);
31906
+ for (const entry of candidates) {
31907
+ const score = scoreIntent(entry, expanded);
31908
+ if (score > 0) add(entry, score);
31909
+ }
31910
+ }
31911
+ if (input.value !== void 0 && input.value.trim().length > 0) {
31912
+ const trimmed = input.value.trim();
31913
+ const parsedColor = parseColor(trimmed);
31914
+ const parsedLength = parseLength(trimmed);
31915
+ const parsedShadow = parseShadow(trimmed);
31916
+ const legacyHint = isLegacyHintColor(parsedColor);
31917
+ for (const entry of candidates) {
31918
+ let score = 0;
31919
+ if (parsedColor !== null && (entry.role === "color" || entry.role === "text-color" || entry.role === "surface" || entry.role === "shadow")) {
31920
+ score = Math.max(score, scoreColor(entry, parsedColor));
31921
+ }
31922
+ if (parsedLength !== null && (entry.role === "spacing" || entry.role === "radius" || entry.role === "size")) {
31923
+ score = Math.max(score, scoreLength(entry, parsedLength));
31924
+ }
31925
+ if (parsedShadow !== null && (entry.role === "elevation" || entry.role === "shadow")) {
31926
+ score = Math.max(score, scoreShadow(entry, trimmed, parsedShadow));
31927
+ }
31928
+ if (legacyHint && entry.cssVariable === "--mat-sys-on-surface-variant") {
31929
+ score = Math.max(score, 9);
31930
+ }
31931
+ if (score === 0) {
31932
+ score = scoreValueLiteral(entry, trimmed);
31933
+ }
31934
+ if (score > 0) add(entry, score);
31935
+ }
31936
+ }
31937
+ if (input.component !== void 0 && input.intent === void 0 && input.value === void 0) {
31938
+ for (const entry of candidates) {
31939
+ add(entry, 5);
31940
+ }
31941
+ }
31942
+ if (input.category !== void 0 && scored.size === 0 && input.intent === void 0 && input.value === void 0) {
31943
+ for (const entry of candidates) {
31944
+ add(entry, 1);
31945
+ }
31946
+ }
31947
+ const matches = [];
31948
+ for (const [key, score] of scored) {
31949
+ const [source, cssVariable] = splitKey(key);
31950
+ const entry = registry.all.find((e) => e.source === source && e.cssVariable === cssVariable);
31951
+ if (entry !== void 0) {
31952
+ matches.push({ entry, score });
31953
+ }
31954
+ }
31955
+ matches.sort((a, b) => {
31956
+ if (b.score !== a.score) return b.score - a.score;
31957
+ const rankDiff = (SOURCE_RANK[b.entry.source] ?? 0) - (SOURCE_RANK[a.entry.source] ?? 0);
31958
+ if (rankDiff !== 0) return rankDiff;
31959
+ return a.entry.cssVariable.localeCompare(b.entry.cssVariable);
31960
+ });
31961
+ const truncated = matches.slice(0, MAX_MATCHES);
31962
+ const confident = truncated.length > 0 && truncated[0].score >= CONFIDENT_THRESHOLD;
31963
+ return { matches: truncated, confident, category: input.category };
31964
+ }
31965
+ function scoreIntent(entry, expanded) {
31966
+ let best = 0;
31967
+ for (const intent of entry.intents) {
31968
+ const lower = intent.toLowerCase();
31969
+ for (const term of expanded) {
31970
+ let score = 0;
31971
+ if (lower === term) {
31972
+ score = 12;
31973
+ } else if (lower.includes(term)) {
31974
+ score = 7;
31975
+ } else if (term.length >= 3 && term.includes(lower)) {
31976
+ score = 4;
31977
+ }
31978
+ if (score > best) best = score;
31979
+ }
31980
+ }
31981
+ return best;
31982
+ }
31983
+ var RGB_RE = /rgba?\(\s*([0-9.]+)\s*,?\s*([0-9.]+)\s*,?\s*([0-9.]+)(?:[\s,/]+([0-9.]+%?))?\s*\)/i;
31984
+ var RGB_NEW_RE = /rgba?\(\s*([0-9.]+)\s+([0-9.]+)\s+([0-9.]+)(?:\s*\/\s*([0-9.]+%?))?\s*\)/i;
31985
+ var HEX_RE = /^#([0-9a-f]{3,8})$/i;
31986
+ function parseColor(raw) {
31987
+ const trimmed = raw.trim();
31988
+ if (trimmed === "transparent") return { r: 0, g: 0, b: 0, a: 0 };
31989
+ if (trimmed.toLowerCase() === "white") return { r: 255, g: 255, b: 255, a: 1 };
31990
+ if (trimmed.toLowerCase() === "black") return { r: 0, g: 0, b: 0, a: 1 };
31991
+ let match = RGB_RE.exec(trimmed);
31992
+ let result = null;
31993
+ if (match !== null) {
31994
+ result = {
31995
+ r: clamp(parseFloat(match[1]), 0, 255),
31996
+ g: clamp(parseFloat(match[2]), 0, 255),
31997
+ b: clamp(parseFloat(match[3]), 0, 255),
31998
+ a: parseAlpha(match[4])
31999
+ };
32000
+ }
32001
+ if (result === null) {
32002
+ match = RGB_NEW_RE.exec(trimmed);
32003
+ if (match !== null) {
32004
+ result = {
32005
+ r: clamp(parseFloat(match[1]), 0, 255),
32006
+ g: clamp(parseFloat(match[2]), 0, 255),
32007
+ b: clamp(parseFloat(match[3]), 0, 255),
32008
+ a: parseAlpha(match[4])
32009
+ };
32010
+ }
32011
+ }
32012
+ if (result === null) {
32013
+ const hexMatch = HEX_RE.exec(trimmed);
32014
+ if (hexMatch !== null) {
32015
+ result = parseHex(hexMatch[1]);
32016
+ }
32017
+ }
32018
+ return result;
32019
+ }
32020
+ function parseAlpha(value) {
32021
+ let alpha = 1;
32022
+ if (value !== void 0) {
32023
+ if (value.endsWith("%")) {
32024
+ alpha = clamp(parseFloat(value.slice(0, -1)) / 100, 0, 1);
32025
+ } else {
32026
+ alpha = clamp(parseFloat(value), 0, 1);
32027
+ }
32028
+ }
32029
+ return alpha;
32030
+ }
32031
+ function parseHex(hex) {
32032
+ let r = 0;
32033
+ let g = 0;
32034
+ let b = 0;
32035
+ let a = 1;
32036
+ if (hex.length === 3) {
32037
+ r = parseInt(hex[0] + hex[0], 16);
32038
+ g = parseInt(hex[1] + hex[1], 16);
32039
+ b = parseInt(hex[2] + hex[2], 16);
32040
+ } else if (hex.length === 4) {
32041
+ r = parseInt(hex[0] + hex[0], 16);
32042
+ g = parseInt(hex[1] + hex[1], 16);
32043
+ b = parseInt(hex[2] + hex[2], 16);
32044
+ a = parseInt(hex[3] + hex[3], 16) / 255;
32045
+ } else if (hex.length === 6) {
32046
+ r = parseInt(hex.slice(0, 2), 16);
32047
+ g = parseInt(hex.slice(2, 4), 16);
32048
+ b = parseInt(hex.slice(4, 6), 16);
32049
+ } else if (hex.length === 8) {
32050
+ r = parseInt(hex.slice(0, 2), 16);
32051
+ g = parseInt(hex.slice(2, 4), 16);
32052
+ b = parseInt(hex.slice(4, 6), 16);
32053
+ a = parseInt(hex.slice(6, 8), 16) / 255;
32054
+ }
32055
+ return { r, g, b, a };
32056
+ }
32057
+ function clamp(value, min, max) {
32058
+ return Math.max(min, Math.min(max, value));
32059
+ }
32060
+ function srgbToLinear(channel) {
32061
+ const v = channel / 255;
32062
+ return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
32063
+ }
32064
+ function rgbToOkLab(rgb) {
32065
+ const r = srgbToLinear(rgb.r);
32066
+ const g = srgbToLinear(rgb.g);
32067
+ const b = srgbToLinear(rgb.b);
32068
+ const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
32069
+ const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
32070
+ const s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
32071
+ const lCbrt = Math.cbrt(l);
32072
+ const mCbrt = Math.cbrt(m);
32073
+ const sCbrt = Math.cbrt(s);
32074
+ return {
32075
+ L: 0.2104542553 * lCbrt + 0.793617785 * mCbrt - 0.0040720468 * sCbrt,
32076
+ a: 1.9779984951 * lCbrt - 2.428592205 * mCbrt + 0.4505937099 * sCbrt,
32077
+ b: 0.0259040371 * lCbrt + 0.7827717662 * mCbrt - 0.808675766 * sCbrt,
32078
+ alpha: rgb.a
32079
+ };
32080
+ }
32081
+ function colorDistance(a, b) {
32082
+ const lab1 = rgbToOkLab(a);
32083
+ const lab2 = rgbToOkLab(b);
32084
+ const dL = lab1.L - lab2.L;
32085
+ const dA = lab1.a - lab2.a;
32086
+ const dB = lab1.b - lab2.b;
32087
+ const dAlpha = (lab1.alpha - lab2.alpha) * 0.5;
32088
+ return Math.sqrt(dL * dL + dA * dA + dB * dB + dAlpha * dAlpha);
32089
+ }
32090
+ function scoreColor(entry, target) {
32091
+ let best = 0;
32092
+ const candidates = [entry.defaults.light, entry.defaults.dark].filter((v) => v !== void 0);
32093
+ for (const value of candidates) {
32094
+ const parsed = parseColor(value);
32095
+ if (parsed !== null) {
32096
+ const distance = colorDistance(target, parsed);
32097
+ let score = 0;
32098
+ if (distance <= 5e-3) score = 11;
32099
+ else if (distance <= 0.02) score = 9;
32100
+ else if (distance <= 0.04) score = 5;
32101
+ else if (distance <= 0.08) score = 3;
32102
+ if (score > best) best = score;
32103
+ }
32104
+ }
32105
+ return best;
32106
+ }
32107
+ function isLegacyHintColor(parsed) {
32108
+ let result = false;
32109
+ if (parsed !== null) {
32110
+ const isBlack = parsed.r < 5 && parsed.g < 5 && parsed.b < 5;
32111
+ const isWhite = parsed.r > 250 && parsed.g > 250 && parsed.b > 250;
32112
+ if (isBlack && parsed.a >= 0.5 && parsed.a <= 0.74 || isWhite && parsed.a >= 0.6 && parsed.a <= 0.85) {
32113
+ result = true;
32114
+ }
32115
+ }
32116
+ return result;
32117
+ }
32118
+ var LENGTH_RE = /^([0-9.]+)\s*(px|rem|em|%|vh|vw)?$/i;
32119
+ function parseLength(raw) {
32120
+ const match = LENGTH_RE.exec(raw.trim());
32121
+ let result = null;
32122
+ if (match !== null) {
32123
+ result = {
32124
+ value: parseFloat(match[1]),
32125
+ unit: (match[2] ?? "").toLowerCase()
32126
+ };
32127
+ }
32128
+ return result;
32129
+ }
32130
+ function scoreLength(entry, target) {
32131
+ let best = 0;
32132
+ const candidates = [entry.defaults.light, entry.defaults.dark].filter((v) => v !== void 0);
32133
+ for (const value of candidates) {
32134
+ const parsed = parseLength(value);
32135
+ if (parsed !== null && parsed.unit === target.unit) {
32136
+ const diff = Math.abs(parsed.value - target.value);
32137
+ let score = 0;
32138
+ if (diff < 0.01) score = 11;
32139
+ else if (diff <= 1 && (entry.role === "spacing" || entry.role === "radius")) score = 7;
32140
+ else if (diff <= 2 && entry.role === "spacing") score = 4;
32141
+ if (score > best) best = score;
32142
+ }
32143
+ }
32144
+ return best;
32145
+ }
32146
+ function parseShadow(raw) {
32147
+ const trimmed = raw.trim();
32148
+ if (!trimmed.includes("px") && !trimmed.startsWith("rgba(") && !trimmed.includes(" ")) return null;
32149
+ const layers = [];
32150
+ for (const layerRaw of splitShadowLayers(trimmed)) {
32151
+ const parsed = parseShadowLayer(layerRaw);
32152
+ if (parsed !== null) layers.push(parsed);
32153
+ }
32154
+ return layers.length > 0 ? layers : null;
32155
+ }
32156
+ function splitShadowLayers(raw) {
32157
+ const out = [];
32158
+ let depth = 0;
32159
+ let buf = "";
32160
+ for (const ch of raw) {
32161
+ if (ch === "(") depth += 1;
32162
+ else if (ch === ")") depth -= 1;
32163
+ if (ch === "," && depth === 0) {
32164
+ if (buf.trim().length > 0) out.push(buf.trim());
32165
+ buf = "";
32166
+ } else {
32167
+ buf += ch;
32168
+ }
32169
+ }
32170
+ if (buf.trim().length > 0) out.push(buf.trim());
32171
+ return out;
32172
+ }
32173
+ function parseShadowLayer(raw) {
32174
+ const colorMatch = /(rgba?\([^)]*\)|#[0-9a-f]+)/i.exec(raw);
32175
+ let color = null;
32176
+ let withoutColor = raw;
32177
+ if (colorMatch !== null) {
32178
+ color = parseColor(colorMatch[1]);
32179
+ withoutColor = (raw.slice(0, colorMatch.index) + raw.slice(colorMatch.index + colorMatch[0].length)).trim();
32180
+ }
32181
+ const parts = withoutColor.split(/\s+/).filter((p) => p.length > 0);
32182
+ let result = null;
32183
+ if (parts.length >= 2) {
32184
+ const ox = parseLength(parts[0]);
32185
+ const oy = parseLength(parts[1]);
32186
+ const blur = parts.length >= 3 ? parseLength(parts[2]) : { value: 0, unit: "px" };
32187
+ const spread = parts.length >= 4 ? parseLength(parts[3]) : { value: 0, unit: "px" };
32188
+ if (ox !== null && oy !== null && blur !== null && spread !== null) {
32189
+ result = {
32190
+ offsetX: ox.value,
32191
+ offsetY: oy.value,
32192
+ blur: blur.value,
32193
+ spread: spread.value,
32194
+ color
32195
+ };
32196
+ }
32197
+ }
32198
+ return result;
32199
+ }
32200
+ function scoreShadow(entry, raw, target) {
32201
+ let best = 0;
32202
+ for (const value of [entry.defaults.light, entry.defaults.dark].filter((v) => v !== void 0)) {
32203
+ const parsed = parseShadow(value);
32204
+ if (parsed !== null) {
32205
+ let layerHits = 0;
32206
+ for (const t of target) {
32207
+ const found = parsed.find((p) => Math.abs(p.offsetX - t.offsetX) <= 1 && Math.abs(p.offsetY - t.offsetY) <= 1 && Math.abs(p.blur - t.blur) <= 2);
32208
+ if (found !== void 0) layerHits += 1;
32209
+ }
32210
+ let score = 0;
32211
+ if (layerHits === target.length) score = 9;
32212
+ else if (layerHits > 0) score = 5;
32213
+ if (score > best) best = score;
32214
+ }
32215
+ if (value === raw) best = Math.max(best, 11);
32216
+ }
32217
+ return best;
32218
+ }
32219
+ function scoreValueLiteral(entry, raw) {
32220
+ let best = 0;
32221
+ for (const value of [entry.defaults.light, entry.defaults.dark].filter((v) => v !== void 0)) {
32222
+ if (value === raw) best = Math.max(best, 11);
32223
+ else if (value.includes(raw)) best = Math.max(best, 4);
32224
+ }
32225
+ return best;
32226
+ }
32227
+ function splitKey(key) {
32228
+ const idx = key.indexOf("::");
32229
+ let result;
32230
+ if (idx < 0) {
32231
+ result = ["", key];
32232
+ } else {
32233
+ result = [key.slice(0, idx), key.slice(idx + 2)];
32234
+ }
32235
+ return result;
32236
+ }
32237
+
32238
+ // packages/dbx-components-mcp/src/tools/css-token-lookup/format.ts
32239
+ function formatCssTokenLookup(registry, input, result) {
32240
+ let text;
32241
+ if (input.category !== void 0 && input.category !== "list" || input.intent === void 0 && input.value === void 0 && input.component === void 0 && input.role === void 0) {
32242
+ if (input.category !== void 0) {
32243
+ text = formatBrowse(registry, input.category, result.matches);
32244
+ } else if (input.role !== void 0) {
32245
+ text = formatBrowse(registry, void 0, result.matches);
32246
+ } else {
32247
+ text = formatNoQuery();
32248
+ }
32249
+ } else if (result.matches.length === 0) {
32250
+ text = formatNoMatch(input);
32251
+ } else if (!result.confident && result.matches.length > 1) {
32252
+ text = formatAmbiguous(input, result.matches);
32253
+ } else {
32254
+ text = formatSingle2(result.matches[0], input, result.matches.slice(1));
32255
+ }
32256
+ return text;
32257
+ }
32258
+ function formatSingle2(match, input, otherMatches) {
32259
+ const entry = match.entry;
32260
+ const lines = [`# \`${entry.cssVariable}\``, "", formatHeaderLine(entry), "", `**Use:** \`var(${entry.cssVariable})\``];
32261
+ if (entry.scssVariable !== void 0) {
32262
+ lines.push(`**SCSS:** \`${entry.scssVariable}\``);
32263
+ }
32264
+ lines.push("");
32265
+ lines.push(entry.description);
32266
+ if (entry.defaults.light !== void 0 || entry.defaults.dark !== void 0) {
32267
+ lines.push("", "## Default value");
32268
+ if (entry.defaults.light !== void 0) {
32269
+ lines.push(`- light: \`${entry.defaults.light}\``);
32270
+ }
32271
+ if (entry.defaults.dark !== void 0) {
32272
+ lines.push(`- dark: \`${entry.defaults.dark}\``);
32273
+ }
32274
+ }
32275
+ if (entry.antiUseNotes !== void 0 && entry.antiUseNotes.length > 0) {
32276
+ lines.push("", "## Don't use this when", "", entry.antiUseNotes);
32277
+ }
32278
+ if (entry.utilityClasses !== void 0 && entry.utilityClasses.length > 0) {
32279
+ const classes = entry.utilityClasses.map((c) => `\`${c}\``).join(", ");
32280
+ lines.push("", `## Utility classes`, "", classes);
32281
+ }
32282
+ if (entry.recommendedPrimitive !== void 0) {
32283
+ lines.push("", "## Recommended primitive", "", `Reach for \`${entry.recommendedPrimitive}\` when you'd otherwise hand-roll this token. See \`dbx_ui_lookup topic="${entry.recommendedPrimitive}"\`.`);
32284
+ }
32285
+ if (entry.seeAlso !== void 0 && entry.seeAlso.length > 0) {
32286
+ const seeAlso = entry.seeAlso.map((s) => `\`${s}\``).join(", ");
32287
+ lines.push("", "## See also", "", seeAlso);
32288
+ }
32289
+ if (otherMatches.length > 0) {
32290
+ lines.push("", "## Other candidates", "");
32291
+ for (const candidate of otherMatches) {
32292
+ lines.push(`- \`${candidate.entry.cssVariable}\` \xB7 ${candidate.entry.role} \xB7 score ${candidate.score}`);
32293
+ }
32294
+ }
32295
+ return lines.join("\n");
32296
+ }
32297
+ function formatHeaderLine(entry) {
32298
+ const parts = [`**source:** \`${entry.source}\``, `**role:** \`${entry.role}\``];
32299
+ if (entry.componentScope !== void 0) parts.push(`**component:** \`${entry.componentScope}\``);
32300
+ return parts.join(" \xB7 ");
32301
+ }
32302
+ function formatAmbiguous(input, matches) {
32303
+ const lines = [`# Top candidates`, "", `Multiple tokens look relevant for the supplied query (${describeQuery(input)}). The candidates are sorted by confidence:`, ""];
32304
+ for (const match of matches) {
32305
+ lines.push(`## \`${match.entry.cssVariable}\` \xB7 score ${match.score}`);
32306
+ lines.push("", `${match.entry.description}`, "");
32307
+ if (match.entry.defaults.light !== void 0) {
32308
+ lines.push(`- light default: \`${match.entry.defaults.light}\``);
32309
+ }
32310
+ if (match.entry.defaults.dark !== void 0) {
32311
+ lines.push(`- dark default: \`${match.entry.defaults.dark}\``);
32312
+ }
32313
+ if (match.entry.antiUseNotes !== void 0 && match.entry.antiUseNotes.length > 0) {
32314
+ lines.push("", match.entry.antiUseNotes);
32315
+ }
32316
+ lines.push("");
32317
+ }
32318
+ return lines.join("\n").trimEnd();
32319
+ }
32320
+ function formatBrowse(registry, category, matches) {
32321
+ const list = matches.length > 0 ? matches.map((m) => m.entry) : registry.all;
32322
+ const title = category !== void 0 ? `# Tokens \xB7 category \`${category}\`` : `# Tokens`;
32323
+ const lines = [title, ""];
32324
+ if (list.length === 0) {
32325
+ lines.push("_No tokens matched._");
32326
+ } else {
32327
+ lines.push("| CSS variable | Role | Source | Description |");
32328
+ lines.push("| --- | --- | --- | --- |");
32329
+ for (const entry of list) {
32330
+ const desc = entry.description.replaceAll("|", String.raw`\|`).split("\n")[0];
32331
+ lines.push(`| \`${entry.cssVariable}\` | ${entry.role} | ${entry.source} | ${desc} |`);
32332
+ }
32333
+ }
32334
+ return lines.join("\n");
32335
+ }
32336
+ function formatNoQuery() {
32337
+ return ["# `dbx_css_token_lookup`", "", 'Provide at least one of `intent`, `value`, `role`, `component`, or `category` (use `category="list"` to browse the full catalog).'].join("\n");
32338
+ }
32339
+ function formatNoMatch(input) {
32340
+ const lines = ["# No confident match", "", `No token confidently matched the query (${describeQuery(input)}).`, "", "If this looks like a project-specific brand chrome value, define a project-local SCSS variable in your app's `styles.scss` instead of reaching for a system token."];
32341
+ return lines.join("\n");
32342
+ }
32343
+ function describeQuery(input) {
32344
+ const parts = [];
32345
+ if (input.intent !== void 0) parts.push(`intent="${input.intent}"`);
32346
+ if (input.value !== void 0) parts.push(`value="${input.value}"`);
32347
+ if (input.role !== void 0) parts.push(`role="${input.role}"`);
32348
+ if (input.component !== void 0) parts.push(`component="${input.component}"`);
32349
+ if (input.category !== void 0) parts.push(`category="${input.category}"`);
32350
+ return parts.join(", ") || "(none)";
32351
+ }
32352
+
32353
+ // packages/dbx-components-mcp/src/tools/css-token-lookup.tool.ts
32354
+ var DBX_CSS_TOKEN_LOOKUP_TOOL = {
32355
+ name: "dbx_css_token_lookup",
32356
+ description: [
32357
+ "Forward-lookup over the dbx-web + Angular Material design-token catalog.",
32358
+ "",
32359
+ "Pass any combination of:",
32360
+ ' \u2022 `intent` \u2014 plain English ("hint text color", "card radius", "section gap", "primary button color");',
32361
+ " \u2022 `value` \u2014 raw CSS value to reverse-lookup (`rgba(0,0,0,0.6)`, `12px`, `#26353f`, `0 1px 2px rgba(0,0,0,.06)`);",
32362
+ " \u2022 `role` \u2014 narrow the candidate pool (color, text-color, surface, spacing, radius, elevation, shadow, typography, motion, state-layer, size, breakpoint);",
32363
+ ' \u2022 `component` \u2014 Angular Material component slug ("mat-progress-bar", "mat-button", ...) to surface the relevant MDC tokens;',
32364
+ ' \u2022 `category` \u2014 `"list"` for the full catalog, or `"dbx-web"|"mat-sys"|"mdc"|"app"` to browse one source.',
32365
+ "",
32366
+ "Returns the recommended `var(--\u2026)` plus light/dark defaults, anti-use notes, the dbx-web utility class or primitive that wraps the underlying token, and any see-also references."
32367
+ ].join("\n"),
32368
+ inputSchema: {
32369
+ type: "object",
32370
+ properties: {
32371
+ intent: { type: "string", description: "Plain-English intent \u2014 what the value is for." },
32372
+ value: { type: "string", description: "Raw CSS value to reverse-lookup." },
32373
+ role: { type: "string", enum: [...TOKEN_ROLES], description: "Token-role filter." },
32374
+ component: { type: "string", description: "Angular Material component slug \u2014 narrows the lookup to MDC tokens scoped to that component." },
32375
+ category: { type: "string", description: '"list" for the full catalog, or "dbx-web"|"mat-sys"|"mdc"|"app" to browse one source.' }
32376
+ }
32377
+ }
32378
+ };
32379
+ var CssTokenLookupArgs = type66({
32380
+ "intent?": "string",
32381
+ "value?": "string",
32382
+ "role?": "string",
32383
+ "component?": "string",
32384
+ "category?": "string"
32385
+ });
32386
+ function parseArgs11(raw) {
32387
+ const parsed = CssTokenLookupArgs(raw);
32388
+ if (parsed instanceof type66.errors) {
32389
+ throw new TypeError(`Invalid arguments: ${parsed.summary}`);
32390
+ }
32391
+ const result = {
32392
+ intent: parsed.intent,
32393
+ value: parsed.value,
32394
+ role: parsed.role,
32395
+ component: parsed.component,
32396
+ category: parsed.category
32397
+ };
32398
+ return result;
32399
+ }
32400
+ function createCssTokenLookupTool(input) {
32401
+ const { registry } = input;
32402
+ const run15 = (rawArgs) => {
32403
+ let args;
32404
+ try {
32405
+ args = parseArgs11(rawArgs);
32406
+ } catch (error) {
32407
+ const message = error instanceof Error ? error.message : String(error);
32408
+ return toolError(message);
32409
+ }
32410
+ const result = resolveToken(registry, args);
32411
+ const text = formatCssTokenLookup(registry, args, result);
32412
+ const tool = { content: [{ type: "text", text }] };
32413
+ return tool;
32414
+ };
32415
+ return { definition: DBX_CSS_TOKEN_LOOKUP_TOOL, run: run15 };
32416
+ }
32417
+
32418
+ // packages/dbx-components-mcp/src/tools/ui-smell-check.tool.ts
32419
+ import { readFile as nodeReadFile8 } from "node:fs/promises";
32420
+ import { isAbsolute as isAbsolute10, resolve as resolve40 } from "node:path";
32421
+ import { type as type67 } from "arktype";
32422
+
32423
+ // packages/dbx-components-mcp/src/tools/ui-smell-check/smells.ts
32424
+ function captureSnippet(haystack, index, length) {
32425
+ const start = Math.max(0, haystack.lastIndexOf("\n", index) + 1);
32426
+ const end = haystack.indexOf("\n", index + length);
32427
+ let stop;
32428
+ if (end < 0) {
32429
+ stop = Math.min(haystack.length, index + length + 80);
32430
+ } else {
32431
+ stop = end;
32432
+ }
32433
+ return haystack.slice(start, stop).trimEnd();
32434
+ }
32435
+ function isInsideScssVarDeclaration(scss, colonIndex) {
32436
+ const lineStart = scss.lastIndexOf("\n", colonIndex - 1) + 1;
32437
+ const prefix = scss.slice(lineStart, colonIndex);
32438
+ return /^\s*\$[\w-]+\s*$/.test(prefix);
32439
+ }
32440
+ function findCardSurface(scss) {
32441
+ let result = null;
32442
+ let depth = 0;
32443
+ let blockStart = -1;
32444
+ for (let i = 0; i < scss.length; i += 1) {
32445
+ const ch = scss[i];
32446
+ if (ch === "{") {
32447
+ if (depth === 0) blockStart = i + 1;
32448
+ depth += 1;
32449
+ } else if (ch === "}") {
32450
+ depth -= 1;
32451
+ if (depth === 0 && blockStart >= 0) {
32452
+ const block = scss.slice(blockStart, i);
32453
+ if (looksLikeCardSurface(block)) {
32454
+ result = { index: blockStart, length: block.length, snippet: block.trim() };
32455
+ break;
32456
+ }
32457
+ blockStart = -1;
32458
+ }
32459
+ }
32460
+ }
32461
+ return result;
32462
+ }
32463
+ function looksLikeCardSurface(block) {
32464
+ const hasPadding = /\bpadding\s*:\s*[0-9]/i.test(block);
32465
+ const hasWhiteBg = /background\s*:\s*(#fff|#ffffff|white|rgb\(\s*255\s*,\s*255\s*,\s*255\s*\))/i.test(block);
32466
+ const hasRadius = /\bborder-radius\s*:\s*[0-9]/i.test(block);
32467
+ return hasPadding && hasWhiteBg && hasRadius;
32468
+ }
32469
+ var cardSurfaceHandrolled = (input) => {
32470
+ const matches = [];
32471
+ const hit = findCardSurface(input.scss);
32472
+ if (hit !== null) {
32473
+ const wrapperHint = input.conventions.cardWrapperClasses && input.conventions.cardWrapperClasses.length > 0 ? ` Or wrap the projection in your project-local class: \`${input.conventions.cardWrapperClasses.join("`, `")}\`.` : "";
32474
+ matches.push({
32475
+ id: "card-surface-handrolled",
32476
+ severity: "warn",
32477
+ title: "Hand-rolled card surface",
32478
+ snippet: hit.snippet,
32479
+ fix: ['This block re-implements `<dbx-content-box>`. Replace the wrapper element with `<dbx-content-box>` (it provides padding, surface color, and corner radius automatically) and project the section content via `<dbx-section header="..." hint="...">` for the title/subtitle stack.' + wrapperHint, "", "Replacement HTML:", "```html", "<dbx-content-box>", ' <dbx-section header="\u2026" hint="\u2026">', " <!-- projected content -->", " </dbx-section>", "</dbx-content-box>", "```"].join("\n"),
32480
+ seeAlsoSlugs: ["content-box", "section"],
32481
+ seeAlsoTokens: ["--mat-sys-surface-container", "--dbx-padding-3"]
32482
+ });
32483
+ }
32484
+ return matches;
32485
+ };
32486
+ var HARDCODED_RADIUS_RE = /border-radius\s*:\s*([0-9]+(?:\.[0-9]+)?(?:px|rem|em))(?!\s*\))/gi;
32487
+ var hardcodedRadius = (input) => {
32488
+ const matches = [];
32489
+ for (const match of input.scss.matchAll(HARDCODED_RADIUS_RE)) {
32490
+ if (match.index === void 0) continue;
32491
+ const colonIndex = input.scss.indexOf(":", match.index);
32492
+ if (colonIndex !== -1 && isInsideScssVarDeclaration(input.scss, colonIndex)) continue;
32493
+ const raw = match[1];
32494
+ const candidates = input.tokenRegistry.findByValue(raw, "radius");
32495
+ const top = candidates[0];
32496
+ const fix = top !== void 0 ? `Replace \`border-radius: ${raw}\` with \`border-radius: var(${top.entry.cssVariable})\` (matches the design-system corner token).` : `Hardcoded radius \`${raw}\` doesn't match any known design-system token. Either wrap it in a project-local SCSS variable or align it with one of \`--mat-sys-corner-extra-small/-small/-medium/-large/-extra-large/-full\`.`;
32497
+ matches.push({
32498
+ id: "hardcoded-radius",
32499
+ severity: "warn",
32500
+ title: "Hardcoded border-radius",
32501
+ snippet: captureSnippet(input.scss, match.index, match[0].length),
32502
+ fix,
32503
+ seeAlsoSlugs: top?.entry.recommendedPrimitive !== void 0 ? [top.entry.recommendedPrimitive] : [],
32504
+ seeAlsoTokens: top !== void 0 ? [top.entry.cssVariable] : []
32505
+ });
32506
+ }
32507
+ return matches;
32508
+ };
32509
+ var HARDCODED_SHADOW_RE = /box-shadow\s*:\s*([^;]+);/gi;
32510
+ var hardcodedShadow = (input) => {
32511
+ const matches = [];
32512
+ for (const match of input.scss.matchAll(HARDCODED_SHADOW_RE)) {
32513
+ if (match.index === void 0) continue;
32514
+ const raw = match[1].trim();
32515
+ if (raw.startsWith("var(") || raw === "none") continue;
32516
+ const candidates = input.tokenRegistry.findByValue(raw, "elevation");
32517
+ const top = candidates[0];
32518
+ const fix = top !== void 0 ? `Replace this hardcoded shadow with \`box-shadow: var(${top.entry.cssVariable})\` or use \`<dbx-content-box [elevate]="true">\` to apply the elevation through the wrapper component.` : 'Hardcoded `box-shadow:` declarations should map to one of the `--mat-sys-level0..5` elevation tokens, or wrap the surface in `<dbx-content-box [elevate]="true">` so the shadow comes from the design system.';
32519
+ matches.push({
32520
+ id: "hardcoded-shadow",
32521
+ severity: "warn",
32522
+ title: "Hardcoded box-shadow",
32523
+ snippet: captureSnippet(input.scss, match.index, match[0].length),
32524
+ fix,
32525
+ seeAlsoSlugs: ["content-box", "content-elevate"],
32526
+ seeAlsoTokens: top !== void 0 ? [top.entry.cssVariable] : ["--mat-sys-level1"]
32527
+ });
32528
+ }
32529
+ return matches;
32530
+ };
32531
+ var HARDCODED_HINT_COLOR_RE = /color\s*:\s*(rgba?\(\s*0\s*,\s*0\s*,\s*0\s*[,/\s]\s*0\.[5-7][0-9]?\s*\)|rgba?\(\s*255\s*,\s*255\s*,\s*255\s*[,/\s]\s*0\.[6-8][0-9]?\s*\))/gi;
32532
+ var hardcodedHintColor = (input) => {
32533
+ const matches = [];
32534
+ for (const match of input.scss.matchAll(HARDCODED_HINT_COLOR_RE)) {
32535
+ if (match.index === void 0) continue;
32536
+ matches.push({
32537
+ id: "hardcoded-hint-color",
32538
+ severity: "warn",
32539
+ title: "Hardcoded hint text color",
32540
+ snippet: captureSnippet(input.scss, match.index, match[0].length),
32541
+ fix: ["Replace this hardcoded hint color with `color: var(--mat-sys-on-surface-variant)` so it adapts to dark mode and matches Material guidance.", "", 'If this `color:` is on text inside a card, prefer `<dbx-section [header]="\u2026" [hint]="\u2026">`, which already styles the hint slot with the right token.'].join("\n"),
32542
+ seeAlsoSlugs: ["section"],
32543
+ seeAlsoTokens: ["--mat-sys-on-surface-variant"]
32544
+ });
32545
+ }
32546
+ return matches;
32547
+ };
32548
+ var HARDCODED_PADDING_RE = /(?:padding|margin)\s*(?:-(?:top|right|bottom|left))?\s*:\s*([0-9]+(?:\.[0-9]+)?px)(?!\s*\))/gi;
32549
+ var hardcodedPadding = (input) => {
32550
+ const matches = [];
32551
+ for (const match of input.scss.matchAll(HARDCODED_PADDING_RE)) {
32552
+ if (match.index === void 0) continue;
32553
+ const raw = match[1];
32554
+ const candidates = input.tokenRegistry.findByValue(raw, "spacing");
32555
+ const top = candidates[0];
32556
+ if (top !== void 0) {
32557
+ const utility = top.entry.utilityClasses?.[0];
32558
+ const fix = ["Replace `" + match[0].trim() + "` with `var(" + top.entry.cssVariable + ")`" + (utility !== void 0 ? ` (or use the \`${utility}\` utility class on the host element)` : "") + "."].join("\n");
32559
+ matches.push({
32560
+ id: "hardcoded-padding",
32561
+ severity: "warn",
32562
+ title: "Hardcoded padding/margin",
32563
+ snippet: captureSnippet(input.scss, match.index, match[0].length),
32564
+ fix,
32565
+ seeAlsoSlugs: [],
32566
+ seeAlsoTokens: [top.entry.cssVariable]
32567
+ });
32568
+ }
32569
+ }
32570
+ return matches;
32571
+ };
32572
+ var HARDCODED_TYPOGRAPHY_RE = /font-size\s*:\s*([0-9]+(?:\.[0-9]+)?)(rem|px)/gi;
32573
+ var hardcodedTypography = (input) => {
32574
+ const matches = [];
32575
+ for (const match of input.scss.matchAll(HARDCODED_TYPOGRAPHY_RE)) {
32576
+ if (match.index === void 0) continue;
32577
+ const value = parseFloat(match[1]);
32578
+ const unit = match[2];
32579
+ const tooBig = unit === "rem" && value > 1 || unit === "px" && value >= 18;
32580
+ if (tooBig) {
32581
+ matches.push({
32582
+ id: "hardcoded-typography",
32583
+ severity: "info",
32584
+ title: "Hardcoded large font-size",
32585
+ snippet: captureSnippet(input.scss, match.index, match[0].length),
32586
+ fix: "Use the `<dbx-section [header]>` (or `<dbx-section-page>` for page-level headers) primitive \u2014 it carries Material `headline` typography automatically. If you must inline a font-size, reach for one of `--mat-sys-headline-{small|medium|large}` or `--mat-sys-title-large`.",
32587
+ seeAlsoSlugs: ["section", "section-page"],
32588
+ seeAlsoTokens: ["--mat-sys-headline-medium", "--mat-sys-title-large"]
32589
+ });
32590
+ }
32591
+ }
32592
+ return matches;
32593
+ };
32594
+ var PIT_BACKGROUND_RE = /background\s*:\s*(rgba?\(\s*0\s*,\s*0\s*,\s*0\s*[,/\s]\s*0\.0[2-8]\s*\))/gi;
32595
+ var pitInsteadOfTintedBg = (input) => {
32596
+ const matches = [];
32597
+ for (const match of input.scss.matchAll(PIT_BACKGROUND_RE)) {
32598
+ if (match.index === void 0) continue;
32599
+ matches.push({
32600
+ id: "pit-instead-of-tinted-bg",
32601
+ severity: "warn",
32602
+ title: 'Hand-rolled tinted "pit" background',
32603
+ snippet: captureSnippet(input.scss, match.index, match[0].length),
32604
+ fix: "Apply `[dbxContentPit]` to the host element. The directive paints the right tinted surface color and supports the rounded variant via `[dbxContentPitRounded]`.",
32605
+ seeAlsoSlugs: ["content-pit"],
32606
+ seeAlsoTokens: ["--mat-sys-surface-container-low"]
32607
+ });
32608
+ }
32609
+ return matches;
32610
+ };
32611
+ var FLEX_COLUMN_GAP_RE = /display\s*:\s*flex\s*;\s*flex-direction\s*:\s*column\s*;\s*gap\s*:\s*[0-9]+(?:\.[0-9]+)?px\s*;/gi;
32612
+ var flexColumnWithGap = (input) => {
32613
+ const matches = [];
32614
+ for (const match of input.scss.matchAll(FLEX_COLUMN_GAP_RE)) {
32615
+ if (match.index === void 0) continue;
32616
+ matches.push({
32617
+ id: "flex-column-with-gap-as-card-stack",
32618
+ severity: "info",
32619
+ title: "Flex column + gap on a card stack",
32620
+ snippet: captureSnippet(input.scss, match.index, match[0].length),
32621
+ fix: "For stacking dbx-web cards/content-boxes, prefer `[dbxFlexGroup]` with `[breakToColumn]` (it handles wrapping, gaps, and responsive flow) or rely on the default sibling margins emitted by `<dbx-content-box>` siblings.",
32622
+ seeAlsoSlugs: ["flex-group"],
32623
+ seeAlsoTokens: ["--dbx-padding-3"]
32624
+ });
32625
+ }
32626
+ return matches;
32627
+ };
32628
+ var MDC_OVERRIDE_RE = /(--mdc-[a-z0-9-]+)\s*:/gi;
32629
+ var mdcTokenOverride = (input) => {
32630
+ const matches = [];
32631
+ const seen = /* @__PURE__ */ new Set();
32632
+ for (const match of input.scss.matchAll(MDC_OVERRIDE_RE)) {
32633
+ if (match.index === void 0) continue;
32634
+ const tokenName = match[1];
32635
+ if (seen.has(tokenName)) continue;
32636
+ seen.add(tokenName);
32637
+ const tokenEntry = input.tokenRegistry.findByCssVariable(tokenName);
32638
+ const componentScope = tokenEntry?.componentScope;
32639
+ const fix = componentScope !== void 0 ? `Don't override \`${tokenName}\` directly. Set \`color="primary|accent|warn"\` on the host \`<${componentScope}>\` (or wrap in a thin styled component) so the token resolution flows through Material's theme.` : `Don't override \`${tokenName}\` directly. Material component tokens should be set via the host component's \`color\` attribute or by a parent theme \u2014 direct overrides break dark mode and theme switching.`;
32640
+ matches.push({
32641
+ id: "mdc-token-override-instead-of-wrapper",
32642
+ severity: "warn",
32643
+ title: "Direct MDC token override",
32644
+ snippet: captureSnippet(input.scss, match.index, match[0].length),
32645
+ fix,
32646
+ seeAlsoSlugs: componentScope === "mat-button" ? ["button"] : [],
32647
+ seeAlsoTokens: [tokenName]
32648
+ });
32649
+ }
32650
+ return matches;
32651
+ };
32652
+ var HARDCODED_HEX_RE = /:\s*(#[0-9a-fA-F]{3,8})\b(?!\s*(?:px|rem|em))/g;
32653
+ var hardcodedHexBrand = (input) => {
32654
+ const matches = [];
32655
+ const seen = /* @__PURE__ */ new Set();
32656
+ for (const match of input.scss.matchAll(HARDCODED_HEX_RE)) {
32657
+ if (match.index === void 0) continue;
32658
+ if (isInsideScssVarDeclaration(input.scss, match.index)) continue;
32659
+ const hex = match[1];
32660
+ if (seen.has(hex)) continue;
32661
+ seen.add(hex);
32662
+ const candidates = input.tokenRegistry.findByValue(hex);
32663
+ if (candidates.length === 0 || candidates[0].score < 6) {
32664
+ matches.push({
32665
+ id: "hardcoded-hex-brand-color",
32666
+ severity: "info",
32667
+ title: "Hardcoded hex with no matching token",
32668
+ snippet: captureSnippet(input.scss, match.index, match[0].length),
32669
+ fix: `\`${hex}\` doesn't map to a known design-system token. If this is project-local brand chrome, define a project-local SCSS variable in your app's \`styles.scss\` (e.g. \`$myapp-onboarding-bg: ${hex};\`) so the value has one source of truth.`,
32670
+ seeAlsoSlugs: [],
32671
+ seeAlsoTokens: []
32672
+ });
32673
+ }
32674
+ }
32675
+ return matches;
32676
+ };
32677
+ var EMPTY_RULESET_RE = /([^{}\n;]+?)\s*\{\s*\}/g;
32678
+ var emptyRuleset = (input) => {
32679
+ const matches = [];
32680
+ for (const match of input.scss.matchAll(EMPTY_RULESET_RE)) {
32681
+ if (match.index === void 0) continue;
32682
+ const selector = match[1].trim();
32683
+ if (selector.length === 0) continue;
32684
+ if (!/^[.#&*:@\w]/.test(selector)) continue;
32685
+ matches.push({
32686
+ id: "empty-ruleset",
32687
+ severity: "info",
32688
+ title: "Empty rule block",
32689
+ snippet: captureSnippet(input.scss, match.index, match[0].length),
32690
+ fix: "Empty rule blocks add noise. Either add styles, remove the block, or replace it with a `// TODO:` comment if you want to mark a placeholder.",
32691
+ seeAlsoSlugs: [],
32692
+ seeAlsoTokens: []
32693
+ });
32694
+ }
32695
+ return matches;
32696
+ };
32697
+ var SCSS_USE_AS_RE = /@use\s+['"][^'"]+['"]\s+as\s+([\w-]+)\s*;/g;
32698
+ var unusedScssUse = (input) => {
32699
+ const matches = [];
32700
+ for (const match of input.scss.matchAll(SCSS_USE_AS_RE)) {
32701
+ if (match.index === void 0) continue;
32702
+ const ns = match[1];
32703
+ const usagePattern = new RegExp(`(?:^|[^\\w-])${ns}\\.`, "g");
32704
+ let used = false;
32705
+ for (const usageMatch of input.scss.matchAll(usagePattern)) {
32706
+ if (usageMatch.index === void 0) continue;
32707
+ const lineStart = input.scss.lastIndexOf("\n", usageMatch.index) + 1;
32708
+ const lineEndRaw = input.scss.indexOf("\n", usageMatch.index);
32709
+ const lineEnd = lineEndRaw === -1 ? input.scss.length : lineEndRaw;
32710
+ const line = input.scss.slice(lineStart, lineEnd);
32711
+ if (/^\s*@use\b/.test(line)) continue;
32712
+ used = true;
32713
+ break;
32714
+ }
32715
+ if (!used) {
32716
+ matches.push({
32717
+ id: "unused-scss-use",
32718
+ severity: "info",
32719
+ title: `Unused @use namespace \`${ns}\``,
32720
+ snippet: captureSnippet(input.scss, match.index, match[0].length),
32721
+ fix: `\`@use\` declares namespace \`${ns}\` but no \`${ns}.\` reference appears in this file. Remove the import to keep the file lean, or use it.`,
32722
+ seeAlsoSlugs: [],
32723
+ seeAlsoTokens: []
32724
+ });
32725
+ }
32726
+ }
32727
+ return matches;
32728
+ };
32729
+ var RAW_MAT_BUTTON_RE = /<button\b([^>]*?)\b(mat-stroked-button|mat-flat-button|mat-raised-button|mat-button)\b/gi;
32730
+ var rawMatButton = (input) => {
32731
+ const matches = [];
32732
+ for (const match of input.html.matchAll(RAW_MAT_BUTTON_RE)) {
32733
+ if (match.index === void 0) continue;
32734
+ const variant = match[2];
32735
+ const variantAttr = variant === "mat-stroked-button" ? "stroked" : variant === "mat-flat-button" ? "flat" : variant === "mat-raised-button" ? "raised" : "basic";
32736
+ matches.push({
32737
+ id: "raw-mat-button",
32738
+ severity: "warn",
32739
+ title: `Raw ${variant}`,
32740
+ snippet: captureSnippet(input.html, match.index, match[0].length),
32741
+ fix: `Use \`<dbx-button ${variantAttr}>\` instead of raw \`${variant}\`. \`<dbx-button>\` plugs into the action stack, accepts \`text\` / \`icon\` inputs, and inherits dbx-web theming.`,
32742
+ seeAlsoSlugs: ["button"],
32743
+ seeAlsoTokens: []
32744
+ });
32745
+ }
32746
+ return matches;
32747
+ };
32748
+ var HEADER_THEN_HINT_RE = /<(h[1-3])\b[^>]*>([\s\S]*?)<\/\1>\s*<(?:p|span)\b[^>]*>([\s\S]{1,200}?)<\/(?:p|span)>/gi;
32749
+ var customSectionHeader = (input) => {
32750
+ const matches = [];
32751
+ for (const match of input.html.matchAll(HEADER_THEN_HINT_RE)) {
32752
+ if (match.index === void 0) continue;
32753
+ const headerTag = match[1].toLowerCase();
32754
+ const isPageLevel = headerTag === "h1";
32755
+ matches.push({
32756
+ id: "custom-section-header",
32757
+ severity: "info",
32758
+ title: `Custom ${headerTag} + sibling hint text`,
32759
+ snippet: captureSnippet(input.html, match.index, match[0].length),
32760
+ fix: `Replace this header+hint pair with \`<${isPageLevel ? "dbx-section-page" : "dbx-section"} header="\u2026" hint="\u2026">\` \u2014 projected content lives inside, the typography and hint color come from dbx-web.`,
32761
+ seeAlsoSlugs: [isPageLevel ? "section-page" : "section"],
32762
+ seeAlsoTokens: ["--mat-sys-on-surface-variant"]
32763
+ });
32764
+ }
32765
+ return matches;
32766
+ };
32767
+ var TWO_COLUMN_GRID_RE = /grid-template-columns\s*:\s*(?:1fr\s+1fr|repeat\(\s*2\s*,)/gi;
32768
+ var customGridLayout = (input) => {
32769
+ const matches = [];
32770
+ for (const match of input.scss.matchAll(TWO_COLUMN_GRID_RE)) {
32771
+ if (match.index === void 0) continue;
32772
+ matches.push({
32773
+ id: "custom-grid-layout",
32774
+ severity: "info",
32775
+ title: "Custom two-column grid",
32776
+ snippet: captureSnippet(input.scss, match.index, match[0].length),
32777
+ fix: "For two-column layouts that should collapse on narrow viewports, prefer `[dbxFlexGroup] [breakToColumn]` on the parent and `[dbxFlexSize]` on each child. The directives handle responsive wrapping consistently across the app.",
32778
+ seeAlsoSlugs: ["flex-group", "flex-size"],
32779
+ seeAlsoTokens: []
32780
+ });
32781
+ }
32782
+ return matches;
32783
+ };
32784
+ var EYEBROW_TEXT_RE = /<span\b[^>]*?(?:class|style)=[^>]*?(?:text-transform\s*:\s*uppercase|uppercase)/gi;
32785
+ var eyebrowText = (input) => {
32786
+ const matches = [];
32787
+ for (const match of input.html.matchAll(EYEBROW_TEXT_RE)) {
32788
+ if (match.index === void 0) continue;
32789
+ matches.push({
32790
+ id: "eyebrow-text",
32791
+ severity: "info",
32792
+ title: "Eyebrow text (uppercase span above a heading)",
32793
+ snippet: captureSnippet(input.html, match.index, match[0].length),
32794
+ fix: 'No current dbx-web primitive wraps the "eyebrow" pattern (small uppercase label above a heading). Either keep it inline with `<dbx-label-block>` for now, or propose a `dbx-eyebrow` primitive in dbx-web.',
32795
+ seeAlsoSlugs: ["label-block"],
32796
+ seeAlsoTokens: ["--mat-sys-label-medium"]
32797
+ });
32798
+ }
32799
+ return matches;
32800
+ };
32801
+ var UI_SMELLS = [
32802
+ { id: "card-surface-handrolled", detect: cardSurfaceHandrolled },
32803
+ { id: "hardcoded-radius", detect: hardcodedRadius },
32804
+ { id: "hardcoded-shadow", detect: hardcodedShadow },
32805
+ { id: "hardcoded-hint-color", detect: hardcodedHintColor },
32806
+ { id: "hardcoded-padding", detect: hardcodedPadding },
32807
+ { id: "hardcoded-typography", detect: hardcodedTypography },
32808
+ { id: "pit-instead-of-tinted-bg", detect: pitInsteadOfTintedBg },
32809
+ { id: "flex-column-with-gap-as-card-stack", detect: flexColumnWithGap },
32810
+ { id: "mdc-token-override-instead-of-wrapper", detect: mdcTokenOverride },
32811
+ { id: "hardcoded-hex-brand-color", detect: hardcodedHexBrand },
32812
+ { id: "empty-ruleset", detect: emptyRuleset },
32813
+ { id: "unused-scss-use", detect: unusedScssUse },
32814
+ { id: "raw-mat-button", detect: rawMatButton },
32815
+ { id: "custom-section-header", detect: customSectionHeader },
32816
+ { id: "custom-grid-layout", detect: customGridLayout },
32817
+ { id: "eyebrow-text", detect: eyebrowText }
32818
+ ];
32819
+ function detectSmells(input) {
32820
+ const matches = [];
32821
+ for (const smell of UI_SMELLS) {
32822
+ const found = smell.detect(input);
32823
+ for (const match of found) matches.push(match);
32824
+ }
32825
+ return matches;
32826
+ }
32827
+
32828
+ // packages/dbx-components-mcp/src/tools/ui-smell-check/format.ts
32829
+ function formatSmellResult(input, matches, tokenRegistry) {
32830
+ const sections = [];
32831
+ const title = "# UI smell check";
32832
+ sections.push(title);
32833
+ if (input.context !== void 0 && input.context.length > 0) {
32834
+ sections.push(`_Context:_ ${input.context}`);
32835
+ }
32836
+ if (matches.length === 0) {
32837
+ const goodSignals = formatGoodSignals(input.html, input.scss);
32838
+ if (goodSignals !== null) {
32839
+ sections.push("", "No smells detected.", "", goodSignals, "", "For a token recommendation, call `dbx_css_token_lookup` with an `intent` or `value`.");
32840
+ } else {
32841
+ sections.push("", "No smells detected against the v1 catalog. If you still want a token recommendation, call `dbx_css_token_lookup` with an `intent` or `value`.");
32842
+ }
32843
+ } else {
32844
+ sections.push("", "## Detected smells", "");
32845
+ for (const match of matches) {
32846
+ sections.push(formatMatch(match));
32847
+ }
32848
+ const tokenTable = formatTokenTable(matches, tokenRegistry);
32849
+ if (tokenTable !== null) {
32850
+ sections.push("", "## Tokens to use", "", tokenTable);
32851
+ }
32852
+ const infoOnly = matches.filter((m) => m.severity === "info");
32853
+ if (infoOnly.length > 0) {
32854
+ sections.push("", "## No-match notes", "");
32855
+ for (const match of infoOnly) {
32856
+ sections.push(`- **${match.id}** \u2014 ${match.title}`);
32857
+ }
32858
+ }
32859
+ }
32860
+ return sections.join("\n");
32861
+ }
32862
+ function formatMatch(match) {
32863
+ const lines = [`### ${match.id} \xB7 ${match.severity}`, "", match.title, "", "```", match.snippet, "```", "", match.fix];
32864
+ if (match.seeAlsoSlugs.length > 0 || match.seeAlsoTokens.length > 0) {
32865
+ const refs = [];
32866
+ for (const slug of match.seeAlsoSlugs) {
32867
+ refs.push(`\`dbx_ui_lookup topic="${slug}"\``);
32868
+ }
32869
+ for (const token of match.seeAlsoTokens) {
32870
+ refs.push(`\`dbx_css_token_lookup value="${token}"\``);
32871
+ }
32872
+ lines.push("", `See also: ${refs.join(", ")}`);
32873
+ }
32874
+ lines.push("");
32875
+ return lines.join("\n");
32876
+ }
32877
+ function formatGoodSignals(html, scss) {
32878
+ let result = null;
32879
+ if (html.length > 0 || scss.length > 0) {
32880
+ const lines = [];
32881
+ if (html.length > 0) {
32882
+ const elements = /* @__PURE__ */ new Map();
32883
+ for (const match of html.matchAll(/<(dbx-[\w-]+)\b/g)) {
32884
+ const tag = match[1].toLowerCase();
32885
+ elements.set(tag, (elements.get(tag) ?? 0) + 1);
32886
+ }
32887
+ if (elements.size > 0) {
32888
+ const summary = Array.from(elements.entries()).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([tag, n]) => `${tag} \xD7${n}`).join(", ");
32889
+ lines.push(`- HTML uses: ${summary}`);
32890
+ } else {
32891
+ lines.push("- HTML uses: no `dbx-*` elements");
32892
+ }
32893
+ }
32894
+ if (scss.length > 0) {
32895
+ let dbx = 0;
32896
+ let matSys = 0;
32897
+ for (const match of scss.matchAll(/var\(\s*(--[\w-]+)/g)) {
32898
+ const name = match[1];
32899
+ if (name.startsWith("--dbx-")) dbx += 1;
32900
+ else if (name.startsWith("--mat-sys-")) matSys += 1;
32901
+ }
32902
+ lines.push(`- SCSS uses: ${dbx} \`--dbx-*\` reference${dbx === 1 ? "" : "s"}, ${matSys} \`--mat-sys-*\` reference${matSys === 1 ? "" : "s"}`);
32903
+ }
32904
+ if (lines.length > 0) {
32905
+ result = `Good signals:
32906
+ ${lines.join("\n")}`;
32907
+ }
32908
+ }
32909
+ return result;
32910
+ }
32911
+ function formatTokenTable(matches, _tokenRegistry) {
32912
+ const rows = [];
32913
+ const seen = /* @__PURE__ */ new Set();
32914
+ for (const match of matches) {
32915
+ for (const token of match.seeAlsoTokens) {
32916
+ if (seen.has(token)) continue;
32917
+ seen.add(token);
32918
+ rows.push(`| ${match.id} | \`var(${token})\` |`);
32919
+ }
32920
+ }
32921
+ let result;
32922
+ if (rows.length === 0) {
32923
+ result = null;
32924
+ } else {
32925
+ result = ["| Smell | Recommended var |", "| --- | --- |", ...rows].join("\n");
32926
+ }
32927
+ return result;
32928
+ }
32929
+
32930
+ // packages/dbx-components-mcp/src/tools/ui-smell-check.tool.ts
32931
+ var DBX_UI_SMELL_CHECK_TOOL = {
32932
+ name: "dbx_ui_smell_check",
32933
+ description: [
32934
+ "Reverse-lookup: paste component HTML and/or SCSS and get back the dbx-web smells you triggered, plus the canonical fix.",
32935
+ "",
32936
+ "Run this after writing component SCSS \u2014 it flags hand-rolled card surfaces, raw `mat-button` usages, hardcoded paddings/radii/shadows/colors that map to existing tokens, MDC token overrides that should be a host attribute or wrapper, and other patterns that already have a dbx-web primitive.",
32937
+ "",
32938
+ "Pass either `html` / `scss` (inline strings) or `htmlPath` / `scssPath` (read from disk, relative to cwd or absolute). Prefer paths for files larger than ~1KB to keep the tool input small. Optional `context` is a one-liner about what the component is for (informational \u2014 does not change detection).",
32939
+ "",
32940
+ "Project-local conventions (e.g. wrapper class names) can be supplied via `projectConventions` or read from `dbx-mcp.config.json` `uiSmellCheck.projectConventions`."
32941
+ ].join("\n"),
32942
+ inputSchema: {
32943
+ type: "object",
32944
+ properties: {
32945
+ html: { type: "string", description: "Component HTML (optional). Mutually exclusive with `htmlPath`." },
32946
+ scss: { type: "string", description: "Component SCSS (optional). Mutually exclusive with `scssPath`." },
32947
+ htmlPath: { type: "string", description: "Path to a component HTML file. Resolved relative to the MCP cwd, or used as-is when absolute. Mutually exclusive with `html`." },
32948
+ scssPath: { type: "string", description: "Path to a component SCSS/CSS file. Resolved relative to the MCP cwd, or used as-is when absolute. Mutually exclusive with `scss`." },
32949
+ context: { type: "string", description: "Optional one-liner about what the component is for." },
32950
+ projectConventions: {
32951
+ type: "object",
32952
+ description: "Optional project-local convention overrides \u2014 wins over the config-file defaults.",
32953
+ properties: {
32954
+ cardWrapperClasses: { type: "array", items: { type: "string" } }
32955
+ }
32956
+ }
32957
+ }
32958
+ }
32959
+ };
32960
+ var SmellCheckArgs = type67({
32961
+ "html?": "string",
32962
+ "scss?": "string",
32963
+ "htmlPath?": "string",
32964
+ "scssPath?": "string",
32965
+ "context?": "string",
32966
+ "projectConventions?": type67({ "cardWrapperClasses?": "string[]" })
32967
+ });
32968
+ function parseArgs12(raw, defaults) {
32969
+ const parsed = SmellCheckArgs(raw);
32970
+ if (parsed instanceof type67.errors) {
32971
+ throw new TypeError(`Invalid arguments: ${parsed.summary}`);
32972
+ }
32973
+ const html = parsed.html ?? "";
32974
+ const scss = parsed.scss ?? "";
32975
+ const merged = {
32976
+ cardWrapperClasses: parsed.projectConventions?.cardWrapperClasses ?? defaults.cardWrapperClasses
32977
+ };
32978
+ return { html, scss, htmlPath: parsed.htmlPath, scssPath: parsed.scssPath, context: parsed.context, conventions: merged };
32979
+ }
32980
+ var DEFAULT_READ_FILE12 = (path) => nodeReadFile8(path, "utf-8");
32981
+ async function readPathInput(path, cwd, readFile20) {
32982
+ const absolute = isAbsolute10(path) ? path : resolve40(cwd, path);
32983
+ return readFile20(absolute);
32984
+ }
32985
+ function createUiSmellCheckTool(input) {
32986
+ const { tokenRegistry, uiComponentRegistry, cwd, readFile: readFile20 } = input;
32987
+ const run15 = async (rawArgs) => {
32988
+ let conventionsFromConfig = {};
32989
+ try {
32990
+ const configResult = await findAndLoadConfig({ cwd: cwd ?? process.cwd(), readFile: readFile20 });
32991
+ const block = configResult.config?.uiSmellCheck?.projectConventions;
32992
+ if (block !== void 0) {
32993
+ conventionsFromConfig = block;
32994
+ }
32995
+ } catch {
32996
+ conventionsFromConfig = {};
32997
+ }
32998
+ let args;
32999
+ try {
33000
+ args = parseArgs12(rawArgs, conventionsFromConfig);
33001
+ } catch (error) {
33002
+ const message = error instanceof Error ? error.message : String(error);
33003
+ return toolError(message);
33004
+ }
33005
+ if (args.html.length > 0 && args.htmlPath !== void 0) {
33006
+ return toolError("dbx_ui_smell_check: provide either `html` or `htmlPath`, not both.");
33007
+ }
33008
+ if (args.scss.length > 0 && args.scssPath !== void 0) {
33009
+ return toolError("dbx_ui_smell_check: provide either `scss` or `scssPath`, not both.");
33010
+ }
33011
+ const resolvedCwd = cwd ?? process.cwd();
33012
+ const reader = readFile20 ?? DEFAULT_READ_FILE12;
33013
+ let html = args.html;
33014
+ let scss = args.scss;
33015
+ if (args.htmlPath !== void 0) {
33016
+ try {
33017
+ html = await readPathInput(args.htmlPath, resolvedCwd, reader);
33018
+ } catch (error) {
33019
+ const message = error instanceof Error ? error.message : String(error);
33020
+ return toolError(`dbx_ui_smell_check: failed to read htmlPath \`${args.htmlPath}\`: ${message}`);
33021
+ }
33022
+ }
33023
+ if (args.scssPath !== void 0) {
33024
+ try {
33025
+ scss = await readPathInput(args.scssPath, resolvedCwd, reader);
33026
+ } catch (error) {
33027
+ const message = error instanceof Error ? error.message : String(error);
33028
+ return toolError(`dbx_ui_smell_check: failed to read scssPath \`${args.scssPath}\`: ${message}`);
33029
+ }
33030
+ }
33031
+ if (html.length === 0 && scss.length === 0) {
33032
+ return toolError("dbx_ui_smell_check: provide at least one of `html`, `scss`, `htmlPath`, or `scssPath`.");
33033
+ }
33034
+ const matches = detectSmells({
33035
+ html,
33036
+ scss,
33037
+ conventions: args.conventions,
33038
+ tokenRegistry,
33039
+ uiComponentRegistry
33040
+ });
33041
+ const text = formatSmellResult({ html, scss, context: args.context }, matches, tokenRegistry);
33042
+ return { content: [{ type: "text", text }] };
33043
+ };
33044
+ return { definition: DBX_UI_SMELL_CHECK_TOOL, run: run15 };
33045
+ }
33046
+
33047
+ // packages/dbx-components-mcp/src/tools/index.ts
33048
+ var DBX_TOOLS = [
33049
+ // form
33050
+ formExamplesTool,
33051
+ // ui
33052
+ uiExamplesTool,
33053
+ // model
33054
+ lookupModelTool,
33055
+ searchModelTool,
33056
+ modelDecodeTool,
33057
+ modelValidateApiTool,
33058
+ modelApiListAppTool,
33059
+ modelApiLookupTool,
33060
+ modelApiValidateAppTool,
33061
+ modelValidateFolderTool,
33062
+ modelStoreScaffoldTool,
33063
+ modelFixtureListAppTool,
33064
+ modelFixtureLookupTool,
33065
+ modelFixtureScaffoldTool,
33066
+ modelFixtureForwardTool,
33067
+ modelTestTreeTool,
33068
+ modelTestSearchTool,
33069
+ // storagefile_m (model extension)
33070
+ storageFileMValidateAppTool,
33071
+ storageFileMListAppTool,
33072
+ storageFileMValidateFolderTool,
33073
+ // notification_m (model extension)
33074
+ notificationMValidateAppTool,
33075
+ notificationMListAppTool,
33076
+ notificationMValidateFolderTool,
33077
+ // system_m (model extension)
33078
+ systemMValidateFolderTool,
33079
+ // action
33080
+ actionExamplesTool,
33081
+ actionScaffoldTool,
33082
+ // route
33083
+ routeTreeTool,
33084
+ routeLookupTool,
33085
+ routeSearchTool,
33086
+ // filter
33087
+ filterScaffoldTool,
33088
+ // artifact (cross-domain dispatchers)
33089
+ artifactScaffoldTool,
33090
+ artifactFileConventionTool,
33091
+ // rule catalog (cross-domain reference)
33092
+ explainRuleTool,
33093
+ // aggregate orchestrator
33094
+ appValidateTool,
33095
+ // downstream component introspection
33096
+ modelListComponentTool,
31400
33097
  serverActionsListAppTool,
31401
33098
  // workspace config audit / setup
31402
33099
  mcpConfigTool
@@ -31424,6 +33121,12 @@ function registerTools(server, options = {}) {
31424
33121
  if (options.filterRegistry !== void 0) {
31425
33122
  tools.push(createLookupFilterTool({ registry: options.filterRegistry }));
31426
33123
  }
33124
+ if (options.tokenRegistry !== void 0) {
33125
+ tools.push(createCssTokenLookupTool({ registry: options.tokenRegistry }));
33126
+ if (options.uiComponentRegistry !== void 0) {
33127
+ tools.push(createUiSmellCheckTool({ tokenRegistry: options.tokenRegistry, uiComponentRegistry: options.uiComponentRegistry, cwd: options.cwd }));
33128
+ }
33129
+ }
31427
33130
  underlyingServer.setRequestHandler(ListToolsRequestSchema, async () => {
31428
33131
  return { tools: tools.map((t) => t.definition) };
31429
33132
  });
@@ -31464,6 +33167,11 @@ Resource URIs are namespaced by domain:
31464
33167
  - dbx://ui/components[/{slug}|/category/{category}|/kind/{kind}]
31465
33168
  - dbx://pipe/entries[/{slug}|/category/{category}]
31466
33169
  - dbx://filter/entries[/{slug}|/kind/{kind}]
33170
+ - dbx://token/entries[/{cssVariable}|/source/{source}|/role/{role}]
33171
+
33172
+ UI styling reverse/forward lookup:
33173
+ - dbx_css_token_lookup \u2014 forward: intent/value/role/component \u2192 recommended \`var(--\u2026)\` + utility class + dbx-web primitive.
33174
+ - dbx_ui_smell_check \u2014 reverse: paste component HTML/SCSS, get smells + canonical fix. **Run this after writing component SCSS** so hardcoded paddings/radii/shadows/colors get caught before they ship.
31467
33175
 
31468
33176
  Fall back to dbx-components prose skills only for content this server doesn't carry: Firestore security rules, multi-file orchestration walkthroughs, model design phase, naming/tier checklists, and decision/why content beyond catalog lookup.`;
31469
33177
  async function createServer(options = {}) {
@@ -31592,12 +33300,30 @@ async function createServer(options = {}) {
31592
33300
  filterRegistry = void 0;
31593
33301
  }
31594
33302
  }
33303
+ let tokenRegistry = options.tokenRegistry;
33304
+ if (tokenRegistry === void 0) {
33305
+ const cwd = options.cwd ?? process.cwd();
33306
+ try {
33307
+ const tokenLoaderResult = await loadTokenRegistry({ cwd });
33308
+ if (options.onTokenLoaderResult === void 0) {
33309
+ reportTokenLoaderResult(tokenLoaderResult);
33310
+ } else {
33311
+ options.onTokenLoaderResult(tokenLoaderResult);
33312
+ }
33313
+ tokenRegistry = tokenLoaderResult.registry;
33314
+ } catch (error) {
33315
+ const message = error instanceof Error ? error.message : String(error);
33316
+ process.stderr.write(`[dbx-components-mcp] tokens registry unavailable: ${message}
33317
+ `);
33318
+ tokenRegistry = void 0;
33319
+ }
33320
+ }
31595
33321
  const fixtureModelRegistry = {
31596
33322
  entries: FIREBASE_MODELS.map((m) => ({ name: m.name, modelType: m.modelType, collectionPrefix: m.collectionPrefix }))
31597
33323
  };
31598
33324
  await emitDownstreamHints({ cwd: options.cwd ?? process.cwd(), externalCounts, onDownstreamHints: options.onDownstreamHints });
31599
- registerResources(server, { semanticTypeRegistry: registry, forgeFieldRegistry: forgeRegistry, pipeRegistry, uiComponentRegistry: uiRegistry, actionRegistry, filterRegistry });
31600
- registerTools(server, { semanticTypeRegistry: registry, forgeFieldRegistry: forgeRegistry, pipeRegistry, uiComponentRegistry: uiRegistry, actionRegistry, filterRegistry, fixtureModelRegistry, modelValidateRuleOptions });
33325
+ registerResources(server, { semanticTypeRegistry: registry, forgeFieldRegistry: forgeRegistry, pipeRegistry, uiComponentRegistry: uiRegistry, actionRegistry, filterRegistry, tokenRegistry });
33326
+ registerTools(server, { semanticTypeRegistry: registry, forgeFieldRegistry: forgeRegistry, pipeRegistry, uiComponentRegistry: uiRegistry, actionRegistry, filterRegistry, tokenRegistry, fixtureModelRegistry, modelValidateRuleOptions, cwd: options.cwd ?? process.cwd() });
31601
33327
  return server;
31602
33328
  }
31603
33329
  function resolveModelValidateRuleOptions(config) {
@@ -31701,6 +33427,20 @@ function reportFilterLoaderResult(result) {
31701
33427
  `);
31702
33428
  }
31703
33429
  }
33430
+ function reportTokenLoaderResult(result) {
33431
+ const { registry, configPath, configWarnings, loaderWarnings, externalSourceCount } = result;
33432
+ const summary = [`[dbx-components-mcp] tokens registry loaded`, ` sources: ${registry.loadedSources.join(", ") || "(none)"}`, ` external: ${externalSourceCount}`, ` config: ${configPath ?? "(none)"}`, ` entries: ${registry.all.length}`, ` warnings: ${configWarnings.length + loaderWarnings.length}`].join("\n");
33433
+ process.stderr.write(`${summary}
33434
+ `);
33435
+ for (const warning of configWarnings) {
33436
+ process.stderr.write(`[dbx-components-mcp] tokens-config-warning: ${warning.kind} ${warning.path}
33437
+ `);
33438
+ }
33439
+ for (const warning of loaderWarnings) {
33440
+ process.stderr.write(`[dbx-components-mcp] tokens-loader-warning: ${warning.kind}
33441
+ `);
33442
+ }
33443
+ }
31704
33444
  var HINT_CLUSTER_LABEL = {
31705
33445
  semanticTypes: "semantic-types",
31706
33446
  uiComponents: "ui-components",