@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 +149 -60
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
1586
|
-
for (const
|
|
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
|
|
1616
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
2805
|
-
tokenResponseText = responseText;
|
|
2806
|
-
|
|
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(
|
|
2818
|
-
fs2.writeFileSync(
|
|
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
|
|
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:
|
|
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({
|
|
3069
|
+
numberResults.push({ categoryKey: coll.categoryKey, result });
|
|
2981
3070
|
}
|
|
2982
3071
|
out.numberVariables = numberResults;
|
|
2983
3072
|
} catch (e) {
|