@atomixstudio/mcp 1.0.22 → 1.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1480,15 +1480,6 @@ function figmaColorNameWithGroup(key) {
1480
1480
  const groupDisplay = group.charAt(0).toUpperCase() + group.slice(1).toLowerCase();
1481
1481
  return `${groupDisplay} / ${name}`;
1482
1482
  }
1483
- var FIGMA_COLOR_GROUP_ORDER = {
1484
- background: 0,
1485
- text: 1,
1486
- border: 2,
1487
- icon: 3,
1488
- brand: 4,
1489
- action: 5,
1490
- feedback: 6
1491
- };
1492
1483
  var FIGMA_SHADOW_ORDER = {
1493
1484
  none: 0,
1494
1485
  xs: 1,
@@ -1582,8 +1573,11 @@ function buildFigmaPayloadsFromDS(data) {
1582
1573
  if (Object.keys(light).length > 0) modes.push("Light");
1583
1574
  if (Object.keys(dark).length > 0 && !modes.includes("Dark")) modes.push("Dark");
1584
1575
  if (modes.length === 0) modes.push("Light");
1585
- const allKeys = /* @__PURE__ */ new Set([...Object.keys(light), ...Object.keys(dark)]);
1586
- for (const key of allKeys) {
1576
+ const orderedKeys = [...Object.keys(light)];
1577
+ for (const k of Object.keys(dark)) {
1578
+ if (!orderedKeys.includes(k)) orderedKeys.push(k);
1579
+ }
1580
+ for (const key of orderedKeys) {
1587
1581
  const lightHex = light[key];
1588
1582
  const darkHex = dark[key];
1589
1583
  if (typeof lightHex === "string" && hexRe.test(lightHex)) {
@@ -1612,26 +1606,8 @@ function buildFigmaPayloadsFromDS(data) {
1612
1606
  }
1613
1607
  }
1614
1608
  if (variables.length === 0 && modes.length === 0) modes.push("Light");
1615
- const colorSortKey = (figmaName) => {
1616
- const slash = figmaName.indexOf(" / ");
1617
- const group = slash >= 0 ? figmaName.slice(0, slash).toLowerCase() : "";
1618
- const name = slash >= 0 ? figmaName.slice(slash + 3) : figmaName;
1619
- const order = FIGMA_COLOR_GROUP_ORDER[group] ?? 999;
1620
- return [order, name];
1621
- };
1622
- variables.sort((a, b) => {
1623
- const [oA, nA] = colorSortKey(a.name);
1624
- const [oB, nB] = colorSortKey(b.name);
1625
- if (oA !== oB) return oA - oB;
1626
- return nA.localeCompare(nB);
1627
- });
1628
- paintStyles.sort((a, b) => {
1629
- const [oA, nA] = colorSortKey(a.name);
1630
- const [oB, nB] = colorSortKey(b.name);
1631
- if (oA !== oB) return oA - oB;
1632
- return nA.localeCompare(nB);
1633
- });
1634
- const collectionName = data.meta?.name ? `${data.meta.name} Colors` : "Atomix Colors";
1609
+ const dsName = data.meta?.name;
1610
+ const collectionName = dsName ? `${dsName} Foundations` : "Foundations";
1635
1611
  const textStyles = [];
1636
1612
  const sizeToPx = (val, basePx = 16) => {
1637
1613
  if (typeof val === "number") return Math.round(val);
@@ -1735,7 +1711,6 @@ function buildFigmaPayloadsFromDS(data) {
1735
1711
  if (a.fontSize !== b.fontSize) return a.fontSize - b.fontSize;
1736
1712
  return (a.name || "").localeCompare(b.name || "");
1737
1713
  });
1738
- const dsName = data.meta?.name ?? "Atomix";
1739
1714
  const numberVariableCollections = [];
1740
1715
  const spacing = tokens?.spacing;
1741
1716
  if (spacing?.scale && typeof spacing.scale === "object") {
@@ -1745,7 +1720,7 @@ function buildFigmaPayloadsFromDS(data) {
1745
1720
  if (n >= 0) variables2.push({ name: key, value: n });
1746
1721
  }
1747
1722
  variables2.sort((a, b) => a.value - b.value);
1748
- if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${dsName} Spacing`, categoryKey: "Spacing", variables: variables2, scopes: ["GAP"] });
1723
+ if (variables2.length > 0) numberVariableCollections.push({ collectionName, categoryKey: "Spacing", variables: variables2, scopes: ["GAP"] });
1749
1724
  }
1750
1725
  const radius = tokens?.radius;
1751
1726
  if (radius?.scale && typeof radius.scale === "object") {
@@ -1755,7 +1730,7 @@ function buildFigmaPayloadsFromDS(data) {
1755
1730
  if (n >= 0) variables2.push({ name: key, value: n });
1756
1731
  }
1757
1732
  variables2.sort((a, b) => a.value - b.value);
1758
- if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${dsName} Radius`, categoryKey: "Radius", variables: variables2, scopes: ["CORNER_RADIUS"] });
1733
+ if (variables2.length > 0) numberVariableCollections.push({ collectionName, categoryKey: "Radius", variables: variables2, scopes: ["CORNER_RADIUS"] });
1759
1734
  }
1760
1735
  const borders = tokens?.borders;
1761
1736
  if (borders?.width && typeof borders.width === "object") {
@@ -1765,7 +1740,7 @@ function buildFigmaPayloadsFromDS(data) {
1765
1740
  if (n >= 0) variables2.push({ name: key, value: n });
1766
1741
  }
1767
1742
  variables2.sort((a, b) => a.value - b.value);
1768
- if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${dsName} Borders`, categoryKey: "Borders", variables: variables2, scopes: ["STROKE_FLOAT"] });
1743
+ if (variables2.length > 0) numberVariableCollections.push({ collectionName, categoryKey: "Borders", variables: variables2, scopes: ["STROKE_FLOAT"] });
1769
1744
  }
1770
1745
  const sizing = tokens?.sizing;
1771
1746
  if (sizing?.height && typeof sizing.height === "object") {
@@ -1775,7 +1750,7 @@ function buildFigmaPayloadsFromDS(data) {
1775
1750
  if (n >= 0) variables2.push({ name: key, value: n });
1776
1751
  }
1777
1752
  variables2.sort((a, b) => a.value - b.value);
1778
- if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${dsName} Height`, categoryKey: "Height", variables: variables2, scopes: ["WIDTH_HEIGHT"] });
1753
+ if (variables2.length > 0) numberVariableCollections.push({ collectionName, categoryKey: "Height", variables: variables2, scopes: ["WIDTH_HEIGHT"] });
1779
1754
  }
1780
1755
  if (sizing?.icon && typeof sizing.icon === "object") {
1781
1756
  const variables2 = [];
@@ -1784,7 +1759,7 @@ function buildFigmaPayloadsFromDS(data) {
1784
1759
  if (n >= 0) variables2.push({ name: key, value: n });
1785
1760
  }
1786
1761
  variables2.sort((a, b) => a.value - b.value);
1787
- if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${dsName} Icon`, categoryKey: "Icon", variables: variables2, scopes: ["WIDTH_HEIGHT"] });
1762
+ if (variables2.length > 0) numberVariableCollections.push({ collectionName, categoryKey: "Icon", variables: variables2, scopes: ["WIDTH_HEIGHT"] });
1788
1763
  }
1789
1764
  const layout = tokens?.layout;
1790
1765
  if (layout?.breakpoint && typeof layout.breakpoint === "object") {
@@ -1794,7 +1769,7 @@ function buildFigmaPayloadsFromDS(data) {
1794
1769
  if (n >= 0) variables2.push({ name: key, value: n });
1795
1770
  }
1796
1771
  variables2.sort((a, b) => a.value - b.value);
1797
- if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${dsName} Breakpoint`, categoryKey: "Breakpoint", variables: variables2, scopes: ["WIDTH_HEIGHT"] });
1772
+ if (variables2.length > 0) numberVariableCollections.push({ collectionName, categoryKey: "Breakpoint", variables: variables2, scopes: ["WIDTH_HEIGHT"] });
1798
1773
  }
1799
1774
  const effectStyles = [];
1800
1775
  const shadows = tokens?.shadows;
@@ -1909,6 +1884,43 @@ ${changes.summary}`);
1909
1884
  }
1910
1885
  }
1911
1886
  }
1887
+ function validateTokenFileAfterWrite(outputPath, format, expectedMinVariables) {
1888
+ if (!fs2.existsSync(outputPath)) {
1889
+ return { path: outputPath, status: "FAIL", detail: "File not found after write." };
1890
+ }
1891
+ const content = fs2.readFileSync(outputPath, "utf-8");
1892
+ if (!content || content.trim().length === 0) {
1893
+ return { path: outputPath, status: "FAIL", detail: "File is empty after write." };
1894
+ }
1895
+ if (["css", "scss", "less"].includes(format)) {
1896
+ const varPattern = /(--[a-zA-Z0-9-]+):\s*[^;]+;/g;
1897
+ let count = 0;
1898
+ let m;
1899
+ while ((m = varPattern.exec(content)) !== null) count++;
1900
+ if (count < expectedMinVariables) {
1901
+ return {
1902
+ path: outputPath,
1903
+ status: "FAIL",
1904
+ detail: `Expected at least ${expectedMinVariables} variables; found ${count}.`
1905
+ };
1906
+ }
1907
+ return { path: outputPath, status: "OK", detail: `${count} variables written.` };
1908
+ }
1909
+ return { path: outputPath, status: "OK", detail: "File written (non-CSS format)." };
1910
+ }
1911
+ function formatValidationBlock(entries) {
1912
+ if (entries.length === 0) return "";
1913
+ const displayPath = (p) => p.startsWith("(") ? p : path2.relative(process.cwd(), p);
1914
+ const lines = [
1915
+ "",
1916
+ "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
1917
+ "VALIDATION (confirm sync succeeded)",
1918
+ "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
1919
+ ...entries.map((e) => ` ${displayPath(e.path)}: ${e.status} \u2014 ${e.detail}`),
1920
+ ""
1921
+ ];
1922
+ return lines.join("\n");
1923
+ }
1912
1924
  async function fetchDesignSystemForMCP(forceRefresh = false) {
1913
1925
  if (!dsId) throw new Error("Missing --ds-id. Usage: npx @atomixstudio/mcp --ds-id <id> --atomix-token <token>");
1914
1926
  if (!accessToken) throw new Error("Missing --atomix-token. Get your token from the Export modal or Settings.");
@@ -1931,7 +1943,7 @@ var TOKEN_CATEGORIES = ["colors", "typography", "spacing", "sizing", "shadows",
1931
1943
  var server = new Server(
1932
1944
  {
1933
1945
  name: "atomix-mcp-user",
1934
- version: "1.0.22"
1946
+ version: "1.0.24"
1935
1947
  },
1936
1948
  {
1937
1949
  capabilities: {
@@ -2083,7 +2095,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
2083
2095
  },
2084
2096
  {
2085
2097
  name: "syncAll",
2086
- description: "Sync tokens, AI rules, skills files (SKILL.md, design-in-figma.md), and atomix-dependencies.json. One tool for full project sync. Use /--sync prompt or call when the user wants to sync. Optional: output (default ./tokens.css), format (default css), skipTokens (if true, only writes skills and manifest).",
2098
+ description: "Sync tokens, AI rules, skills (SKILL.md, design-in-figma.md), and atomix-dependencies.json. Use dryRun: true first to report what would change without writing; then dryRun: false to apply. Response includes a VALIDATION section\u2014agent must check it to confirm success. Optional: output (default ./tokens.css), format (default css), skipTokens (if true, only skills and manifest), dryRun (if true, report only; no files written).",
2087
2099
  inputSchema: {
2088
2100
  type: "object",
2089
2101
  properties: {
@@ -2099,6 +2111,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
2099
2111
  skipTokens: {
2100
2112
  type: "boolean",
2101
2113
  description: "If true, skip token file and rules sync; only write skills and dependencies manifest. Default: false."
2114
+ },
2115
+ dryRun: {
2116
+ type: "boolean",
2117
+ description: "If true, compute and return what would be written (paths, token counts, diff summary) but do NOT write any files. Call with dryRun: false to apply. Default: false."
2102
2118
  }
2103
2119
  },
2104
2120
  required: []
@@ -2311,7 +2327,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2311
2327
  isError: true
2312
2328
  };
2313
2329
  }
2314
- async function performTokenSyncAndRules(designSystemData, tokenOutput, tokenFormat) {
2330
+ async function performTokenSyncAndRules(designSystemData, tokenOutput, tokenFormat, dryRun) {
2315
2331
  const output = tokenOutput;
2316
2332
  const format = tokenFormat;
2317
2333
  const outputPath = path2.resolve(process.cwd(), output);
@@ -2374,7 +2390,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2374
2390
  const lightChanges = diff.added.length + diff.modified.length;
2375
2391
  const darkChanges = diff.addedDark.length + diff.modifiedDark.length;
2376
2392
  const totalChanges = lightChanges + darkChanges + deprecatedCount;
2377
- if (totalChanges === 0) {
2393
+ if (totalChanges === 0 && !dryRun) {
2378
2394
  const lastUpdated = designSystemData.meta.exportedAt ? new Date(designSystemData.meta.exportedAt).toLocaleString() : "N/A";
2379
2395
  return {
2380
2396
  responseText: `\u2713 Already up to date!
@@ -2383,7 +2399,19 @@ File: ${output}
2383
2399
  Tokens: ${tokenCount}
2384
2400
  Version: ${designSystemData.meta.version}
2385
2401
  Last updated: ${lastUpdated}`,
2386
- rulesResults: []
2402
+ rulesResults: [],
2403
+ validation: [{ path: outputPath, status: "OK", detail: "No changes needed; file already up to date." }]
2404
+ };
2405
+ }
2406
+ if (totalChanges === 0 && dryRun) {
2407
+ return {
2408
+ responseText: `[DRY RUN] Already up to date. No changes would be written.
2409
+
2410
+ File: ${output}
2411
+ Tokens: ${tokenCount}
2412
+ Version: ${designSystemData.meta.version}`,
2413
+ rulesResults: [],
2414
+ validation: [{ path: "(dry run)", status: "OK", detail: "No files written." }]
2387
2415
  };
2388
2416
  }
2389
2417
  changes = [...diff.modified, ...diff.modifiedDark];
@@ -2397,9 +2425,34 @@ Last updated: ${lastUpdated}`,
2397
2425
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2398
2426
  };
2399
2427
  }
2428
+ if (dryRun) {
2429
+ const added = diff ? diff.added.length + diff.addedDark.length : 0;
2430
+ const modified = diff ? diff.modified.length + diff.modifiedDark.length : 0;
2431
+ const changeLine = fileExists && diff ? ` Changes: ${added} added, ${modified} modified` : !fileExists ? ` New file would be created with ${tokenCount} tokens.` : "";
2432
+ const report = [
2433
+ "[DRY RUN] No files were written. The following would be written if you run syncAll with dryRun: false.",
2434
+ "",
2435
+ "Token file:",
2436
+ ` Path: ${output}`,
2437
+ ` Format: ${format}`,
2438
+ ` Tokens: ${tokenCount} (${deprecatedCount} deprecated preserved)`,
2439
+ changeLine,
2440
+ "",
2441
+ "Rules: .cursorrules (or existing rules files in project)",
2442
+ "",
2443
+ "Call syncAll again with dryRun: false to apply."
2444
+ ].filter(Boolean).join("\n");
2445
+ return {
2446
+ responseText: report,
2447
+ rulesResults: [],
2448
+ validation: [{ path: "(dry run)", status: "OK", detail: "No files written." }]
2449
+ };
2450
+ }
2400
2451
  const outputDir = path2.dirname(outputPath);
2401
2452
  if (!fs2.existsSync(outputDir)) fs2.mkdirSync(outputDir, { recursive: true });
2402
2453
  fs2.writeFileSync(outputPath, newContent);
2454
+ const validation = [];
2455
+ validation.push(validateTokenFileAfterWrite(outputPath, format, tokenCount));
2403
2456
  let rulesResults = [];
2404
2457
  try {
2405
2458
  rulesResults = await syncRulesFiles({
@@ -2408,6 +2461,14 @@ Last updated: ${lastUpdated}`,
2408
2461
  apiBase: apiBase ?? void 0,
2409
2462
  rulesDir: process.cwd()
2410
2463
  });
2464
+ for (const r of rulesResults) {
2465
+ const fullPath = path2.resolve(process.cwd(), r.path);
2466
+ validation.push({
2467
+ path: fullPath,
2468
+ status: r.success && fs2.existsSync(fullPath) ? "OK" : "FAIL",
2469
+ detail: r.success ? "Written." : r.error || "Write failed."
2470
+ });
2471
+ }
2411
2472
  } catch (error) {
2412
2473
  console.error(`[syncAll] Failed to sync rules: ${error}`);
2413
2474
  }
@@ -2428,7 +2489,7 @@ Last updated: ${lastUpdated}`,
2428
2489
  hasRefactorRecommendation: !!lastSyncAffectedTokens?.removed.length,
2429
2490
  deprecatedTokenCount: lastSyncAffectedTokens?.removed.length || 0
2430
2491
  });
2431
- return { responseText: response, rulesResults };
2492
+ return { responseText: response, rulesResults, validation };
2432
2493
  }
2433
2494
  switch (name) {
2434
2495
  case "getToken": {
@@ -2796,26 +2857,50 @@ Last updated: ${lastUpdated}`,
2796
2857
  }
2797
2858
  case "syncAll": {
2798
2859
  const skipTokens = args?.skipTokens === true;
2860
+ const dryRun = args?.dryRun === true;
2799
2861
  const output = args?.output || "./tokens.css";
2800
2862
  const format = args?.format || "css";
2801
- const parts = ["\u2713 syncAll complete."];
2863
+ const parts = [dryRun ? "[DRY RUN] syncAll report (no files written)." : "\u2713 syncAll complete."];
2802
2864
  let tokenResponseText = "";
2865
+ const allValidation = [];
2803
2866
  if (!skipTokens) {
2804
- const { responseText, rulesResults } = await performTokenSyncAndRules(data, output, format);
2805
- tokenResponseText = responseText;
2806
- parts.push(`Tokens: ${output} (${format})`);
2807
- if (rulesResults.length > 0) {
2808
- parts.push(`Rules: ${rulesResults.map((r) => r.path).join(", ")}`);
2867
+ const result = await performTokenSyncAndRules(data, output, format, dryRun);
2868
+ tokenResponseText = result.responseText;
2869
+ allValidation.push(...result.validation);
2870
+ if (!dryRun && result.rulesResults.length > 0) {
2871
+ parts.push(`Rules: ${result.rulesResults.map((r) => r.path).join(", ")}`);
2872
+ }
2873
+ if (dryRun) {
2874
+ parts.push(`Would write tokens: ${output} (${format})`);
2875
+ } else {
2876
+ parts.push(`Tokens: ${output} (${format})`);
2809
2877
  }
2810
2878
  }
2811
- const skillsDir = path2.resolve(process.cwd(), ".cursor/skills/atomix-ds");
2812
- if (!fs2.existsSync(skillsDir)) fs2.mkdirSync(skillsDir, { recursive: true });
2813
2879
  const dsVersion = String(data.meta.version ?? "1.0.0");
2814
2880
  const dsExportedAt = data.meta.exportedAt;
2881
+ const skillsDir = path2.resolve(process.cwd(), ".cursor/skills/atomix-ds");
2882
+ const skillPath1 = path2.join(skillsDir, "SKILL.md");
2883
+ const skillPath2 = path2.join(skillsDir, "design-in-figma.md");
2884
+ const manifestPath = path2.resolve(process.cwd(), "atomix-dependencies.json");
2885
+ if (dryRun) {
2886
+ parts.push("Would write skills: .cursor/skills/atomix-ds/SKILL.md, .cursor/skills/atomix-ds/design-in-figma.md");
2887
+ parts.push("Would write manifest: atomix-dependencies.json");
2888
+ const reportText = [parts.join("\n"), tokenResponseText].filter(Boolean).join("\n\n---\n\n");
2889
+ return {
2890
+ content: [{ type: "text", text: `syncAllResult: DRY_RUN (no files written)
2891
+
2892
+ ${reportText}` }]
2893
+ };
2894
+ }
2895
+ if (!fs2.existsSync(skillsDir)) fs2.mkdirSync(skillsDir, { recursive: true });
2815
2896
  const genericWithVersion = injectSkillVersion(GENERIC_SKILL_MD, dsVersion, dsExportedAt);
2816
2897
  const figmaWithVersion = injectSkillVersion(FIGMA_DESIGN_SKILL_MD, dsVersion, dsExportedAt);
2817
- fs2.writeFileSync(path2.join(skillsDir, "SKILL.md"), genericWithVersion);
2818
- fs2.writeFileSync(path2.join(skillsDir, "design-in-figma.md"), figmaWithVersion);
2898
+ fs2.writeFileSync(skillPath1, genericWithVersion);
2899
+ fs2.writeFileSync(skillPath2, figmaWithVersion);
2900
+ allValidation.push(
2901
+ { path: skillPath1, status: fs2.existsSync(skillPath1) ? "OK" : "FAIL", detail: "Written." },
2902
+ { path: skillPath2, status: fs2.existsSync(skillPath2) ? "OK" : "FAIL", detail: "Written." }
2903
+ );
2819
2904
  parts.push("Skills: .cursor/skills/atomix-ds/SKILL.md, .cursor/skills/atomix-ds/design-in-figma.md (synced at DS v" + dsVersion + ")");
2820
2905
  const tokens = data.tokens;
2821
2906
  const typography = tokens?.typography;
@@ -2851,15 +2936,18 @@ Last updated: ${lastUpdated}`,
2851
2936
  syncedAtVersion: data.meta.version ?? "1.0.0"
2852
2937
  }
2853
2938
  };
2854
- const manifestPath = path2.resolve(process.cwd(), "atomix-dependencies.json");
2855
2939
  fs2.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
2940
+ allValidation.push({ path: manifestPath, status: fs2.existsSync(manifestPath) ? "OK" : "FAIL", detail: "Written." });
2856
2941
  parts.push("Manifest: atomix-dependencies.json (icons, fonts, skill paths)");
2857
2942
  const summary = parts.join("\n");
2858
- const fullText = tokenResponseText ? `${summary}
2943
+ const validationBlock = formatValidationBlock(allValidation);
2944
+ const hasFailure = allValidation.some((e) => e.status === "FAIL");
2945
+ const resultLine = hasFailure ? "syncAllResult: FAIL \u2014 Check VALIDATION section below. Do not report success to the user.\n\n" : "syncAllResult: OK\n\n";
2946
+ const fullText = resultLine + (tokenResponseText ? `${summary}
2859
2947
 
2860
2948
  ---
2861
2949
 
2862
- ${tokenResponseText}` : summary;
2950
+ ${tokenResponseText}${validationBlock}` : `${summary}${validationBlock}`);
2863
2951
  return {
2864
2952
  content: [{ type: "text", text: fullText }]
2865
2953
  };
@@ -2971,13 +3059,14 @@ ${tokenResponseText}` : summary;
2971
3059
  if (payloads.numberVariableCollections.length > 0) {
2972
3060
  const numberResults = [];
2973
3061
  try {
3062
+ const foundationsName = payloads.colorVariables.collectionName;
2974
3063
  for (const coll of payloads.numberVariableCollections) {
2975
3064
  const result = await sendBridgeRequest("create_number_variables", {
2976
- collectionName: coll.collectionName,
2977
- variables: coll.variables,
3065
+ collectionName: foundationsName,
3066
+ variables: coll.variables.map((v) => ({ name: `${coll.categoryKey} / ${v.name}`, value: v.value })),
2978
3067
  scopes: coll.scopes
2979
3068
  });
2980
- numberResults.push({ collectionName: coll.collectionName, result });
3069
+ numberResults.push({ categoryKey: coll.categoryKey, result });
2981
3070
  }
2982
3071
  out.numberVariables = numberResults;
2983
3072
  } catch (e) {