@atomixstudio/mcp 1.0.8 → 1.0.9
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/README.md +38 -9
- package/dist/index.js +265 -1225
- package/dist/index.js.map +1 -1
- package/package.json +1 -51
package/dist/index.js
CHANGED
|
@@ -11,1134 +11,29 @@ import {
|
|
|
11
11
|
ListPromptsRequestSchema,
|
|
12
12
|
GetPromptRequestSchema
|
|
13
13
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
]);
|
|
36
|
-
for (const foundation of allFoundationKeys) {
|
|
37
|
-
const cachedRules = cachedCategories[foundation] || [];
|
|
38
|
-
const freshRules = freshCategories[foundation] || [];
|
|
39
|
-
const cachedRulesStr2 = JSON.stringify(cachedRules.sort());
|
|
40
|
-
const freshRulesStr2 = JSON.stringify(freshRules.sort());
|
|
41
|
-
if (cachedRulesStr2 !== freshRulesStr2) {
|
|
42
|
-
changes.push(foundation);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return changes;
|
|
46
|
-
}
|
|
47
|
-
async function fetchDesignSystem(options) {
|
|
48
|
-
const { dsId: dsId2, apiKey: apiKey2, apiBase: apiBase2 = "https://atomixstudio.eu", etag, forceRefresh = false } = options;
|
|
49
|
-
if (!dsId2) {
|
|
50
|
-
throw new Error("Missing dsId. Usage: fetchDesignSystem({ dsId: '...' })");
|
|
51
|
-
}
|
|
52
|
-
const url = `${apiBase2}/api/ds/${dsId2}/tokens?format=export`;
|
|
53
|
-
const headers = {
|
|
54
|
-
"Content-Type": "application/json"
|
|
55
|
-
};
|
|
56
|
-
if (apiKey2) {
|
|
57
|
-
headers["x-api-key"] = apiKey2;
|
|
58
|
-
}
|
|
59
|
-
if (etag) {
|
|
60
|
-
headers["if-none-match"] = etag;
|
|
61
|
-
}
|
|
62
|
-
const response = await fetch(url, { headers });
|
|
63
|
-
if (response.status === 304) {
|
|
64
|
-
return {
|
|
65
|
-
data: null,
|
|
66
|
-
etag,
|
|
67
|
-
status: 304
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
if (!response.ok) {
|
|
71
|
-
const text = await response.text();
|
|
72
|
-
throw new Error(`Failed to fetch design system: ${response.status} ${text}`);
|
|
73
|
-
}
|
|
74
|
-
const data = await response.json();
|
|
75
|
-
const responseETag = response.headers.get("etag");
|
|
76
|
-
const finalETag = responseETag || generateETag(data.meta);
|
|
77
|
-
const designSystemData = {
|
|
78
|
-
tokens: data.tokens,
|
|
79
|
-
cssVariables: data.cssVariables,
|
|
80
|
-
governance: data.governance,
|
|
81
|
-
meta: data.meta
|
|
82
|
-
};
|
|
83
|
-
return {
|
|
84
|
-
data: designSystemData,
|
|
85
|
-
etag: finalETag,
|
|
86
|
-
status: 200
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// ../atomix-sync-core/src/diff.ts
|
|
91
|
-
function diffTokens(oldContent, newCssVars, format, newDarkVars) {
|
|
92
|
-
const added = [];
|
|
93
|
-
const modified = [];
|
|
94
|
-
const removed = [];
|
|
95
|
-
const addedDark = [];
|
|
96
|
-
const modifiedDark = [];
|
|
97
|
-
const removedDark = [];
|
|
98
|
-
if (format === "css" || format === "scss" || format === "less") {
|
|
99
|
-
const rootMatch = oldContent.match(/:root\s*\{([^}]*)\}/);
|
|
100
|
-
const darkMatch = oldContent.match(/\.dark[^{]*\{([^}]*)\}/);
|
|
101
|
-
const oldLightVars = {};
|
|
102
|
-
if (rootMatch) {
|
|
103
|
-
const rootContent = rootMatch[1];
|
|
104
|
-
const varRegex = /(--[\w-]+):\s*([^;]+);/g;
|
|
105
|
-
let match;
|
|
106
|
-
while ((match = varRegex.exec(rootContent)) !== null) {
|
|
107
|
-
oldLightVars[match[1]] = match[2].trim();
|
|
108
|
-
}
|
|
109
|
-
} else {
|
|
110
|
-
const varRegex = /(--[\w-]+):\s*([^;]+);/g;
|
|
111
|
-
let match;
|
|
112
|
-
while ((match = varRegex.exec(oldContent)) !== null) {
|
|
113
|
-
if (!(match[1] in oldLightVars)) {
|
|
114
|
-
oldLightVars[match[1]] = match[2].trim();
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
const oldDarkVars = {};
|
|
119
|
-
if (darkMatch) {
|
|
120
|
-
const darkContent = darkMatch[1];
|
|
121
|
-
const varRegex = /(--[\w-]+):\s*([^;]+);/g;
|
|
122
|
-
let match;
|
|
123
|
-
while ((match = varRegex.exec(darkContent)) !== null) {
|
|
124
|
-
oldDarkVars[match[1]] = match[2].trim();
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
for (const [key, value] of Object.entries(newCssVars)) {
|
|
128
|
-
if (!(key in oldLightVars)) {
|
|
129
|
-
added.push(key);
|
|
130
|
-
} else if (oldLightVars[key] !== value) {
|
|
131
|
-
modified.push({ key, old: oldLightVars[key], new: value });
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
for (const key of Object.keys(oldLightVars)) {
|
|
135
|
-
if (!(key in newCssVars)) {
|
|
136
|
-
removed.push(key);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (newDarkVars && Object.keys(newDarkVars).length > 0) {
|
|
140
|
-
const firstKey = Object.keys(newCssVars)[0] || "--atmx-";
|
|
141
|
-
const prefixMatch = firstKey.match(/^(--[a-z]+-)/);
|
|
142
|
-
const prefix = prefixMatch ? prefixMatch[1] : "--atmx-";
|
|
143
|
-
for (const [key, value] of Object.entries(newDarkVars)) {
|
|
144
|
-
const cssVarName = `${prefix}color-${key}`;
|
|
145
|
-
if (!(cssVarName in oldDarkVars)) {
|
|
146
|
-
addedDark.push(cssVarName);
|
|
147
|
-
} else if (oldDarkVars[cssVarName] !== value) {
|
|
148
|
-
modifiedDark.push({ key: cssVarName, old: oldDarkVars[cssVarName], new: value });
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
for (const key of Object.keys(oldDarkVars)) {
|
|
152
|
-
const shortKey = key.replace(new RegExp(`^${prefix}color-`), "");
|
|
153
|
-
if (!(shortKey in newDarkVars)) {
|
|
154
|
-
removedDark.push(key);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return { added, modified, removed, addedDark, modifiedDark, removedDark };
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// ../atomix-sync-core/src/rules.ts
|
|
163
|
-
import * as fs from "fs";
|
|
14
|
+
import {
|
|
15
|
+
fetchDesignSystem,
|
|
16
|
+
getTokenByPath,
|
|
17
|
+
flattenTokens,
|
|
18
|
+
searchTokens,
|
|
19
|
+
getTokenStats,
|
|
20
|
+
compareDesignSystems,
|
|
21
|
+
generateCSSOutput,
|
|
22
|
+
generateSCSSOutput,
|
|
23
|
+
generateLessOutput,
|
|
24
|
+
generateJSONOutput,
|
|
25
|
+
generateTSOutput,
|
|
26
|
+
generateJSOutput,
|
|
27
|
+
generateSwiftOutput,
|
|
28
|
+
generateKotlinOutput,
|
|
29
|
+
generateDartOutput,
|
|
30
|
+
diffTokens,
|
|
31
|
+
formatSyncResponse,
|
|
32
|
+
detectGovernanceChangesByFoundation,
|
|
33
|
+
syncRulesFiles
|
|
34
|
+
} from "@atomixstudio/sync-core";
|
|
164
35
|
import * as path from "path";
|
|
165
|
-
|
|
166
|
-
const { dsId: dsId2, apiKey: apiKey2, apiBase: apiBase2 = "https://atomixstudio.eu", rulesDir = process.cwd() } = options;
|
|
167
|
-
const rulesDirResolved = path.resolve(process.cwd(), rulesDir);
|
|
168
|
-
const toolsToSync = [
|
|
169
|
-
{ tool: "cursor", filename: ".cursorrules" },
|
|
170
|
-
{ tool: "windsurf", filename: ".windsurfrules" },
|
|
171
|
-
{ tool: "cline", filename: ".clinerules" },
|
|
172
|
-
{ tool: "continue", filename: ".continuerules" },
|
|
173
|
-
{ tool: "copilot", filename: "copilot-instructions.md", dir: ".github" },
|
|
174
|
-
{ tool: "generic", filename: "AI_GUIDELINES.md" }
|
|
175
|
-
];
|
|
176
|
-
const existingTools = toolsToSync.filter((t) => {
|
|
177
|
-
const filePath = t.dir ? path.join(rulesDirResolved, t.dir, t.filename) : path.join(rulesDirResolved, t.filename);
|
|
178
|
-
return fs.existsSync(filePath);
|
|
179
|
-
});
|
|
180
|
-
const toolsToWrite = existingTools.length > 0 ? existingTools : [{ tool: "cursor", filename: ".cursorrules" }];
|
|
181
|
-
const results = [];
|
|
182
|
-
for (const { tool, filename, dir } of toolsToWrite) {
|
|
183
|
-
try {
|
|
184
|
-
const rulesUrl = `${apiBase2}/api/ds/${dsId2}/rules?format=${tool}`;
|
|
185
|
-
const headers = { "Content-Type": "application/json" };
|
|
186
|
-
if (apiKey2) headers["x-api-key"] = apiKey2;
|
|
187
|
-
const response = await fetch(rulesUrl, { headers });
|
|
188
|
-
if (!response.ok) {
|
|
189
|
-
results.push({
|
|
190
|
-
tool,
|
|
191
|
-
filename,
|
|
192
|
-
path: dir ? `${dir}/${filename}` : filename,
|
|
193
|
-
success: false,
|
|
194
|
-
error: `Failed to fetch ${tool} rules: ${response.status}`
|
|
195
|
-
});
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
const rulesData = await response.json();
|
|
199
|
-
if (!rulesData.content) {
|
|
200
|
-
results.push({
|
|
201
|
-
tool,
|
|
202
|
-
filename,
|
|
203
|
-
path: dir ? `${dir}/${filename}` : filename,
|
|
204
|
-
success: false,
|
|
205
|
-
error: `No content for ${tool} rules`
|
|
206
|
-
});
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
const targetDir = dir ? path.join(rulesDirResolved, dir) : rulesDirResolved;
|
|
210
|
-
if (!fs.existsSync(targetDir)) {
|
|
211
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
212
|
-
}
|
|
213
|
-
const filePath = path.join(targetDir, filename);
|
|
214
|
-
fs.writeFileSync(filePath, rulesData.content);
|
|
215
|
-
results.push({
|
|
216
|
-
tool,
|
|
217
|
-
filename,
|
|
218
|
-
path: dir ? `${dir}/${filename}` : filename,
|
|
219
|
-
success: true
|
|
220
|
-
});
|
|
221
|
-
} catch (error) {
|
|
222
|
-
results.push({
|
|
223
|
-
tool,
|
|
224
|
-
filename,
|
|
225
|
-
path: dir ? `${dir}/${filename}` : filename,
|
|
226
|
-
success: false,
|
|
227
|
-
error: error instanceof Error ? error.message : String(error)
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
return results;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// ../atomix-sync-core/src/formats/css.ts
|
|
235
|
-
function generateCSSOutput(cssVariables, darkModeColors, deprecatedTokens = /* @__PURE__ */ new Map()) {
|
|
236
|
-
const lines = [
|
|
237
|
-
"/* Atomix Design System Tokens",
|
|
238
|
-
" * Auto-generated - do not edit manually",
|
|
239
|
-
` * Synced: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
240
|
-
" *",
|
|
241
|
-
" * WARNING: This file is completely rewritten on each sync.",
|
|
242
|
-
" * Only CSS custom properties (variables) are preserved.",
|
|
243
|
-
" * Custom CSS rules (selectors, classes, etc.) will be lost.",
|
|
244
|
-
" * Keep custom CSS in a separate file.",
|
|
245
|
-
" */",
|
|
246
|
-
"",
|
|
247
|
-
"/* Light mode (default) */",
|
|
248
|
-
":root {"
|
|
249
|
-
];
|
|
250
|
-
const allVars = /* @__PURE__ */ new Map();
|
|
251
|
-
for (const [key, value] of Object.entries(cssVariables)) {
|
|
252
|
-
allVars.set(key, { value, deprecated: false });
|
|
253
|
-
}
|
|
254
|
-
for (const [key, value] of deprecatedTokens.entries()) {
|
|
255
|
-
allVars.set(key, { value, deprecated: true });
|
|
256
|
-
}
|
|
257
|
-
const sortedKeys = Array.from(allVars.keys()).sort();
|
|
258
|
-
for (const key of sortedKeys) {
|
|
259
|
-
const { value, deprecated } = allVars.get(key);
|
|
260
|
-
if (deprecated) {
|
|
261
|
-
lines.push(` /* DEPRECATED */ ${key}: ${value};`);
|
|
262
|
-
} else {
|
|
263
|
-
lines.push(` ${key}: ${value};`);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
lines.push("}");
|
|
267
|
-
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
268
|
-
lines.push("");
|
|
269
|
-
lines.push("/* Dark mode */");
|
|
270
|
-
lines.push(".dark,");
|
|
271
|
-
lines.push('[data-theme="dark"] {');
|
|
272
|
-
const firstKey = sortedKeys[0] || "--atmx-";
|
|
273
|
-
const prefixMatch = firstKey.match(/^(--[a-z]+-)/);
|
|
274
|
-
const prefix = prefixMatch ? prefixMatch[1] : "--atmx-";
|
|
275
|
-
const sortedDarkKeys = Object.keys(darkModeColors).sort();
|
|
276
|
-
for (const key of sortedDarkKeys) {
|
|
277
|
-
lines.push(` ${prefix}color-${key}: ${darkModeColors[key]};`);
|
|
278
|
-
}
|
|
279
|
-
lines.push("}");
|
|
280
|
-
}
|
|
281
|
-
lines.push("");
|
|
282
|
-
return lines.join("\n");
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// ../atomix-sync-core/src/formats/scss.ts
|
|
286
|
-
function generateSCSSOutput(cssVariables, darkModeColors, deprecatedTokens = /* @__PURE__ */ new Map()) {
|
|
287
|
-
const lines = [
|
|
288
|
-
"// Atomix Design System Tokens",
|
|
289
|
-
"// Auto-generated - do not edit manually",
|
|
290
|
-
`// Synced: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
291
|
-
"",
|
|
292
|
-
"// CSS Custom Properties (for use in CSS/HTML)",
|
|
293
|
-
"// Light mode (default)",
|
|
294
|
-
":root {"
|
|
295
|
-
];
|
|
296
|
-
const allVars = /* @__PURE__ */ new Map();
|
|
297
|
-
for (const [key, value] of Object.entries(cssVariables)) {
|
|
298
|
-
allVars.set(key, { value, deprecated: false });
|
|
299
|
-
}
|
|
300
|
-
for (const [key, value] of deprecatedTokens.entries()) {
|
|
301
|
-
allVars.set(key, { value, deprecated: true });
|
|
302
|
-
}
|
|
303
|
-
const sortedKeys = Array.from(allVars.keys()).sort();
|
|
304
|
-
for (const key of sortedKeys) {
|
|
305
|
-
const { value, deprecated } = allVars.get(key);
|
|
306
|
-
if (deprecated) {
|
|
307
|
-
lines.push(` // DEPRECATED ${key}: ${value};`);
|
|
308
|
-
} else {
|
|
309
|
-
lines.push(` ${key}: ${value};`);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
lines.push("}");
|
|
313
|
-
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
314
|
-
lines.push("");
|
|
315
|
-
lines.push("// Dark mode");
|
|
316
|
-
lines.push(".dark,");
|
|
317
|
-
lines.push('[data-theme="dark"] {');
|
|
318
|
-
const firstKey = sortedKeys[0] || "--atmx-";
|
|
319
|
-
const prefixMatch = firstKey.match(/^(--[a-z]+-)/);
|
|
320
|
-
const prefix = prefixMatch ? prefixMatch[1] : "--atmx-";
|
|
321
|
-
for (const [key, value] of Object.entries(darkModeColors)) {
|
|
322
|
-
lines.push(` ${prefix}color-${key}: ${value};`);
|
|
323
|
-
}
|
|
324
|
-
lines.push("}");
|
|
325
|
-
}
|
|
326
|
-
lines.push("");
|
|
327
|
-
lines.push("// SCSS Variables (light mode values)");
|
|
328
|
-
for (const key of sortedKeys) {
|
|
329
|
-
const { value, deprecated } = allVars.get(key);
|
|
330
|
-
const scssVar = "$" + key.replace(/^--/, "");
|
|
331
|
-
if (deprecated) {
|
|
332
|
-
lines.push(`// DEPRECATED ${scssVar}: ${value};`);
|
|
333
|
-
} else {
|
|
334
|
-
lines.push(`${scssVar}: ${value};`);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
338
|
-
lines.push("");
|
|
339
|
-
lines.push("// SCSS Variables (dark mode values)");
|
|
340
|
-
const firstKey = Object.keys(cssVariables)[0] || "--atmx-";
|
|
341
|
-
const prefixMatch = firstKey.match(/^--([a-z]+)-/);
|
|
342
|
-
const prefix = prefixMatch ? prefixMatch[1] : "atmx";
|
|
343
|
-
for (const [key, value] of Object.entries(darkModeColors)) {
|
|
344
|
-
const scssVar = `$${prefix}-color-${key}-dark`;
|
|
345
|
-
lines.push(`${scssVar}: ${value};`);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
lines.push("");
|
|
349
|
-
return lines.join("\n");
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// ../atomix-sync-core/src/formats/less.ts
|
|
353
|
-
function generateLessOutput(cssVariables, darkModeColors, deprecatedTokens = /* @__PURE__ */ new Map()) {
|
|
354
|
-
const lines = [
|
|
355
|
-
"// Atomix Design System Tokens",
|
|
356
|
-
"// Auto-generated - do not edit manually",
|
|
357
|
-
`// Synced: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
358
|
-
"",
|
|
359
|
-
"// CSS Custom Properties (for use in CSS/HTML)",
|
|
360
|
-
"// Light mode (default)",
|
|
361
|
-
":root {"
|
|
362
|
-
];
|
|
363
|
-
const allVars = /* @__PURE__ */ new Map();
|
|
364
|
-
for (const [key, value] of Object.entries(cssVariables)) {
|
|
365
|
-
allVars.set(key, { value, deprecated: false });
|
|
366
|
-
}
|
|
367
|
-
for (const [key, value] of deprecatedTokens.entries()) {
|
|
368
|
-
allVars.set(key, { value, deprecated: true });
|
|
369
|
-
}
|
|
370
|
-
const sortedKeys = Array.from(allVars.keys()).sort();
|
|
371
|
-
for (const key of sortedKeys) {
|
|
372
|
-
const { value, deprecated } = allVars.get(key);
|
|
373
|
-
if (deprecated) {
|
|
374
|
-
lines.push(` // DEPRECATED ${key}: ${value};`);
|
|
375
|
-
} else {
|
|
376
|
-
lines.push(` ${key}: ${value};`);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
lines.push("}");
|
|
380
|
-
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
381
|
-
lines.push("");
|
|
382
|
-
lines.push("// Dark mode");
|
|
383
|
-
lines.push(".dark,");
|
|
384
|
-
lines.push('[data-theme="dark"] {');
|
|
385
|
-
const firstKey = Object.keys(cssVariables)[0] || "--atmx-";
|
|
386
|
-
const prefixMatch = firstKey.match(/^(--[a-z]+-)/);
|
|
387
|
-
const prefix = prefixMatch ? prefixMatch[1] : "--atmx-";
|
|
388
|
-
for (const [key, value] of Object.entries(darkModeColors)) {
|
|
389
|
-
lines.push(` ${prefix}color-${key}: ${value};`);
|
|
390
|
-
}
|
|
391
|
-
lines.push("}");
|
|
392
|
-
}
|
|
393
|
-
lines.push("");
|
|
394
|
-
lines.push("// Less Variables (light mode values)");
|
|
395
|
-
for (const [key, value] of Object.entries(cssVariables)) {
|
|
396
|
-
const lessVar = "@" + key.replace(/^--/, "");
|
|
397
|
-
lines.push(`${lessVar}: ${value};`);
|
|
398
|
-
}
|
|
399
|
-
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
400
|
-
lines.push("");
|
|
401
|
-
lines.push("// Less Variables (dark mode values)");
|
|
402
|
-
const firstKey = Object.keys(cssVariables)[0] || "--atmx-";
|
|
403
|
-
const prefixMatch = firstKey.match(/^--([a-z]+)-/);
|
|
404
|
-
const prefix = prefixMatch ? prefixMatch[1] : "atmx";
|
|
405
|
-
for (const [key, value] of Object.entries(darkModeColors)) {
|
|
406
|
-
const lessVar = `@${prefix}-color-${key}-dark`;
|
|
407
|
-
lines.push(`${lessVar}: ${value};`);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
lines.push("");
|
|
411
|
-
return lines.join("\n");
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// ../atomix-sync-core/src/formats/json.ts
|
|
415
|
-
function generateJSONOutput(tokens) {
|
|
416
|
-
return JSON.stringify(tokens, null, 2);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// ../atomix-sync-core/src/formats/ts.ts
|
|
420
|
-
function generateTSOutput(tokens) {
|
|
421
|
-
return `// Atomix Design System Tokens
|
|
422
|
-
// Auto-generated - do not edit manually
|
|
423
|
-
// Synced: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
424
|
-
|
|
425
|
-
export const tokens = ${JSON.stringify(tokens, null, 2)} as const;
|
|
426
|
-
|
|
427
|
-
export type Tokens = typeof tokens;
|
|
428
|
-
`;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// ../atomix-sync-core/src/formats/js.ts
|
|
432
|
-
function generateJSOutput(tokens) {
|
|
433
|
-
return `// Atomix Design System Tokens
|
|
434
|
-
// Auto-generated - do not edit manually
|
|
435
|
-
// Synced: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
436
|
-
|
|
437
|
-
export const tokens = ${JSON.stringify(tokens, null, 2)};
|
|
438
|
-
`;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// ../atomix-sync-core/src/formats/swift.ts
|
|
442
|
-
function toSwiftName(cssVar) {
|
|
443
|
-
return cssVar.replace(/^--atmx-/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
444
|
-
}
|
|
445
|
-
function isColorValue(value) {
|
|
446
|
-
return /^#[0-9A-Fa-f]{3,8}$/.test(value) || /^rgb/.test(value) || /^hsl/.test(value);
|
|
447
|
-
}
|
|
448
|
-
function hexToSwiftColor(hex) {
|
|
449
|
-
const clean = hex.replace("#", "");
|
|
450
|
-
if (clean.length === 3) {
|
|
451
|
-
const r = clean[0], g = clean[1], b = clean[2];
|
|
452
|
-
return `Color(hex: 0x${r}${r}${g}${g}${b}${b})`;
|
|
453
|
-
}
|
|
454
|
-
if (clean.length === 6) {
|
|
455
|
-
return `Color(hex: 0x${clean})`;
|
|
456
|
-
}
|
|
457
|
-
if (clean.length === 8) {
|
|
458
|
-
const rgb = clean.substring(0, 6);
|
|
459
|
-
const alpha = parseInt(clean.substring(6, 8), 16) / 255;
|
|
460
|
-
return `Color(hex: 0x${rgb}).opacity(${alpha.toFixed(2)})`;
|
|
461
|
-
}
|
|
462
|
-
return `Color.clear // Could not parse: ${hex}`;
|
|
463
|
-
}
|
|
464
|
-
function generateSwiftOutput(cssVariables, darkModeColors, deprecatedTokens = /* @__PURE__ */ new Map()) {
|
|
465
|
-
const lines = [
|
|
466
|
-
"// Atomix Design System Tokens",
|
|
467
|
-
"// Auto-generated - do not edit manually",
|
|
468
|
-
`// Synced: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
469
|
-
"",
|
|
470
|
-
"import SwiftUI",
|
|
471
|
-
"",
|
|
472
|
-
"// MARK: - Color Extension for Hex",
|
|
473
|
-
"extension Color {",
|
|
474
|
-
" init(hex: UInt, alpha: Double = 1.0) {",
|
|
475
|
-
" self.init(",
|
|
476
|
-
" .sRGB,",
|
|
477
|
-
" red: Double((hex >> 16) & 0xFF) / 255.0,",
|
|
478
|
-
" green: Double((hex >> 8) & 0xFF) / 255.0,",
|
|
479
|
-
" blue: Double(hex & 0xFF) / 255.0,",
|
|
480
|
-
" opacity: alpha",
|
|
481
|
-
" )",
|
|
482
|
-
" }",
|
|
483
|
-
"}",
|
|
484
|
-
"",
|
|
485
|
-
"// MARK: - Design Tokens",
|
|
486
|
-
"enum DesignTokens {",
|
|
487
|
-
"",
|
|
488
|
-
" // MARK: Colors (Light Mode)",
|
|
489
|
-
" enum Colors {"
|
|
490
|
-
];
|
|
491
|
-
const colors = [];
|
|
492
|
-
const spacing = [];
|
|
493
|
-
const typography = [];
|
|
494
|
-
const other = [];
|
|
495
|
-
for (const [key, value] of Object.entries(cssVariables)) {
|
|
496
|
-
const isDeprecated = false;
|
|
497
|
-
if (key.includes("-color-")) colors.push([key, value, isDeprecated]);
|
|
498
|
-
else if (key.includes("-spacing-")) spacing.push([key, value, isDeprecated]);
|
|
499
|
-
else if (key.includes("-typography-") || key.includes("-font-")) typography.push([key, value, isDeprecated]);
|
|
500
|
-
else other.push([key, value, isDeprecated]);
|
|
501
|
-
}
|
|
502
|
-
for (const [key, value] of deprecatedTokens.entries()) {
|
|
503
|
-
const isDeprecated = true;
|
|
504
|
-
if (key.includes("-color-")) colors.push([key, value, isDeprecated]);
|
|
505
|
-
else if (key.includes("-spacing-")) spacing.push([key, value, isDeprecated]);
|
|
506
|
-
else if (key.includes("-typography-") || key.includes("-font-")) typography.push([key, value, isDeprecated]);
|
|
507
|
-
else other.push([key, value, isDeprecated]);
|
|
508
|
-
}
|
|
509
|
-
colors.sort((a, b) => a[0].localeCompare(b[0]));
|
|
510
|
-
spacing.sort((a, b) => a[0].localeCompare(b[0]));
|
|
511
|
-
typography.sort((a, b) => a[0].localeCompare(b[0]));
|
|
512
|
-
other.sort((a, b) => a[0].localeCompare(b[0]));
|
|
513
|
-
for (const [key, value, isDeprecated] of colors) {
|
|
514
|
-
const name = toSwiftName(key);
|
|
515
|
-
if (isColorValue(value)) {
|
|
516
|
-
if (isDeprecated) {
|
|
517
|
-
lines.push(` @available(*, deprecated, message: "This token has been removed from the design system")`);
|
|
518
|
-
lines.push(` static let ${name} = ${hexToSwiftColor(value)}`);
|
|
519
|
-
} else {
|
|
520
|
-
lines.push(` static let ${name} = ${hexToSwiftColor(value)}`);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
lines.push(" }");
|
|
525
|
-
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
526
|
-
lines.push("");
|
|
527
|
-
lines.push(" // MARK: Colors (Dark Mode)");
|
|
528
|
-
lines.push(" enum ColorsDark {");
|
|
529
|
-
for (const [key, value] of Object.entries(darkModeColors)) {
|
|
530
|
-
const name = `color${key.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("")}`;
|
|
531
|
-
if (isColorValue(value)) {
|
|
532
|
-
lines.push(` static let ${name} = ${hexToSwiftColor(value)}`);
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
lines.push(" }");
|
|
536
|
-
}
|
|
537
|
-
lines.push("");
|
|
538
|
-
lines.push(" // MARK: Spacing");
|
|
539
|
-
lines.push(" enum Spacing {");
|
|
540
|
-
for (const [key, value, isDeprecated] of spacing) {
|
|
541
|
-
const name = toSwiftName(key);
|
|
542
|
-
const numValue = parseFloat(value);
|
|
543
|
-
if (!isNaN(numValue)) {
|
|
544
|
-
if (isDeprecated) {
|
|
545
|
-
lines.push(` @available(*, deprecated, message: "This token has been removed from the design system")`);
|
|
546
|
-
lines.push(` static let ${name}: CGFloat = ${numValue}`);
|
|
547
|
-
} else {
|
|
548
|
-
lines.push(` static let ${name}: CGFloat = ${numValue}`);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
lines.push(" }");
|
|
553
|
-
lines.push("");
|
|
554
|
-
lines.push(" // MARK: Typography");
|
|
555
|
-
lines.push(" enum Typography {");
|
|
556
|
-
for (const [key, value, isDeprecated] of typography) {
|
|
557
|
-
const name = toSwiftName(key);
|
|
558
|
-
if (isDeprecated) {
|
|
559
|
-
lines.push(` @available(*, deprecated, message: "This token has been removed from the design system")`);
|
|
560
|
-
}
|
|
561
|
-
if (key.includes("size")) {
|
|
562
|
-
const numValue = parseFloat(value);
|
|
563
|
-
if (!isNaN(numValue)) {
|
|
564
|
-
lines.push(` static let ${name}: CGFloat = ${numValue}`);
|
|
565
|
-
}
|
|
566
|
-
} else if (key.includes("weight")) {
|
|
567
|
-
lines.push(` static let ${name} = "${value}"`);
|
|
568
|
-
} else if (key.includes("family")) {
|
|
569
|
-
lines.push(` static let ${name} = "${value}"`);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
lines.push(" }");
|
|
573
|
-
lines.push("");
|
|
574
|
-
lines.push(" // MARK: Other");
|
|
575
|
-
lines.push(" enum Other {");
|
|
576
|
-
for (const [key, value, isDeprecated] of other) {
|
|
577
|
-
const name = toSwiftName(key);
|
|
578
|
-
if (isDeprecated) {
|
|
579
|
-
lines.push(` @available(*, deprecated, message: "This token has been removed from the design system")`);
|
|
580
|
-
}
|
|
581
|
-
if (isColorValue(value)) {
|
|
582
|
-
lines.push(` static let ${name} = ${hexToSwiftColor(value)}`);
|
|
583
|
-
} else {
|
|
584
|
-
const numValue = parseFloat(value);
|
|
585
|
-
if (!isNaN(numValue)) {
|
|
586
|
-
lines.push(` static let ${name}: CGFloat = ${numValue}`);
|
|
587
|
-
} else {
|
|
588
|
-
lines.push(` static let ${name} = "${value}"`);
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
lines.push(" }");
|
|
593
|
-
lines.push("}");
|
|
594
|
-
lines.push("");
|
|
595
|
-
return lines.join("\n");
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
// ../atomix-sync-core/src/formats/kotlin.ts
|
|
599
|
-
function toSwiftName2(cssVar) {
|
|
600
|
-
return cssVar.replace(/^--atmx-/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
601
|
-
}
|
|
602
|
-
function toKotlinName(cssVar) {
|
|
603
|
-
const camel = toSwiftName2(cssVar);
|
|
604
|
-
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
605
|
-
}
|
|
606
|
-
function isColorValue2(value) {
|
|
607
|
-
return /^#[0-9A-Fa-f]{3,8}$/.test(value) || /^rgb/.test(value) || /^hsl/.test(value);
|
|
608
|
-
}
|
|
609
|
-
function hexToKotlinColor(hex) {
|
|
610
|
-
const clean = hex.replace("#", "").toUpperCase();
|
|
611
|
-
if (clean.length === 3) {
|
|
612
|
-
const r = clean[0], g = clean[1], b = clean[2];
|
|
613
|
-
return `Color(0xFF${r}${r}${g}${g}${b}${b})`;
|
|
614
|
-
}
|
|
615
|
-
if (clean.length === 6) {
|
|
616
|
-
return `Color(0xFF${clean})`;
|
|
617
|
-
}
|
|
618
|
-
if (clean.length === 8) {
|
|
619
|
-
const rgb = clean.substring(0, 6);
|
|
620
|
-
const alpha = clean.substring(6, 8);
|
|
621
|
-
return `Color(0x${alpha}${rgb})`;
|
|
622
|
-
}
|
|
623
|
-
return `Color.Transparent // Could not parse: ${hex}`;
|
|
624
|
-
}
|
|
625
|
-
function generateKotlinOutput(cssVariables, darkModeColors, deprecatedTokens = /* @__PURE__ */ new Map()) {
|
|
626
|
-
const lines = [
|
|
627
|
-
"// Atomix Design System Tokens",
|
|
628
|
-
"// Auto-generated - do not edit manually",
|
|
629
|
-
`// Synced: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
630
|
-
"",
|
|
631
|
-
"package com.atomix.design",
|
|
632
|
-
"",
|
|
633
|
-
"import androidx.compose.ui.graphics.Color",
|
|
634
|
-
"import androidx.compose.ui.unit.dp",
|
|
635
|
-
"import androidx.compose.ui.unit.sp",
|
|
636
|
-
"",
|
|
637
|
-
"object DesignTokens {",
|
|
638
|
-
"",
|
|
639
|
-
" // Light mode colors",
|
|
640
|
-
" object Colors {"
|
|
641
|
-
];
|
|
642
|
-
const colors = [];
|
|
643
|
-
const spacing = [];
|
|
644
|
-
const typography = [];
|
|
645
|
-
const other = [];
|
|
646
|
-
for (const [key, value] of Object.entries(cssVariables)) {
|
|
647
|
-
const isDeprecated = false;
|
|
648
|
-
if (key.includes("-color-")) colors.push([key, value, isDeprecated]);
|
|
649
|
-
else if (key.includes("-spacing-")) spacing.push([key, value, isDeprecated]);
|
|
650
|
-
else if (key.includes("-typography-") || key.includes("-font-")) typography.push([key, value, isDeprecated]);
|
|
651
|
-
else other.push([key, value, isDeprecated]);
|
|
652
|
-
}
|
|
653
|
-
for (const [key, value] of deprecatedTokens.entries()) {
|
|
654
|
-
const isDeprecated = true;
|
|
655
|
-
if (key.includes("-color-")) colors.push([key, value, isDeprecated]);
|
|
656
|
-
else if (key.includes("-spacing-")) spacing.push([key, value, isDeprecated]);
|
|
657
|
-
else if (key.includes("-typography-") || key.includes("-font-")) typography.push([key, value, isDeprecated]);
|
|
658
|
-
else other.push([key, value, isDeprecated]);
|
|
659
|
-
}
|
|
660
|
-
colors.sort((a, b) => a[0].localeCompare(b[0]));
|
|
661
|
-
spacing.sort((a, b) => a[0].localeCompare(b[0]));
|
|
662
|
-
typography.sort((a, b) => a[0].localeCompare(b[0]));
|
|
663
|
-
other.sort((a, b) => a[0].localeCompare(b[0]));
|
|
664
|
-
for (const [key, value, isDeprecated] of colors) {
|
|
665
|
-
const name = toKotlinName(key);
|
|
666
|
-
if (isColorValue2(value)) {
|
|
667
|
-
if (isDeprecated) {
|
|
668
|
-
lines.push(` @Deprecated("This token has been removed from the design system")`);
|
|
669
|
-
lines.push(` val ${name} = ${hexToKotlinColor(value)}`);
|
|
670
|
-
} else {
|
|
671
|
-
lines.push(` val ${name} = ${hexToKotlinColor(value)}`);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
lines.push(" }");
|
|
676
|
-
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
677
|
-
lines.push("");
|
|
678
|
-
lines.push(" // Dark mode colors");
|
|
679
|
-
lines.push(" object ColorsDark {");
|
|
680
|
-
for (const [key, value] of Object.entries(darkModeColors)) {
|
|
681
|
-
const name = `Color${key.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("")}`;
|
|
682
|
-
if (isColorValue2(value)) {
|
|
683
|
-
lines.push(` val ${name} = ${hexToKotlinColor(value)}`);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
lines.push(" }");
|
|
687
|
-
}
|
|
688
|
-
lines.push("");
|
|
689
|
-
lines.push(" object Spacing {");
|
|
690
|
-
for (const [key, value, isDeprecated] of spacing) {
|
|
691
|
-
const name = toKotlinName(key);
|
|
692
|
-
const numValue = parseFloat(value);
|
|
693
|
-
if (!isNaN(numValue)) {
|
|
694
|
-
if (isDeprecated) {
|
|
695
|
-
lines.push(` @Deprecated("This token has been removed from the design system")`);
|
|
696
|
-
lines.push(` val ${name} = ${numValue}.dp`);
|
|
697
|
-
} else {
|
|
698
|
-
lines.push(` val ${name} = ${numValue}.dp`);
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
lines.push(" }");
|
|
703
|
-
lines.push("");
|
|
704
|
-
lines.push(" object Typography {");
|
|
705
|
-
for (const [key, value, isDeprecated] of typography) {
|
|
706
|
-
const name = toKotlinName(key);
|
|
707
|
-
if (isDeprecated) {
|
|
708
|
-
lines.push(` @Deprecated("This token has been removed from the design system")`);
|
|
709
|
-
}
|
|
710
|
-
if (key.includes("size")) {
|
|
711
|
-
const numValue = parseFloat(value);
|
|
712
|
-
if (!isNaN(numValue)) {
|
|
713
|
-
lines.push(` val ${name} = ${numValue}.sp`);
|
|
714
|
-
}
|
|
715
|
-
} else {
|
|
716
|
-
lines.push(` const val ${name} = "${value}"`);
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
lines.push(" }");
|
|
720
|
-
lines.push("");
|
|
721
|
-
lines.push(" object Other {");
|
|
722
|
-
for (const [key, value, isDeprecated] of other) {
|
|
723
|
-
const name = toKotlinName(key);
|
|
724
|
-
if (isDeprecated) {
|
|
725
|
-
lines.push(` @Deprecated("This token has been removed from the design system")`);
|
|
726
|
-
}
|
|
727
|
-
if (isColorValue2(value)) {
|
|
728
|
-
lines.push(` val ${name} = ${hexToKotlinColor(value)}`);
|
|
729
|
-
} else {
|
|
730
|
-
const numValue = parseFloat(value);
|
|
731
|
-
if (!isNaN(numValue)) {
|
|
732
|
-
lines.push(` val ${name} = ${numValue}.dp`);
|
|
733
|
-
} else {
|
|
734
|
-
lines.push(` const val ${name} = "${value}"`);
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
lines.push(" }");
|
|
739
|
-
lines.push("}");
|
|
740
|
-
lines.push("");
|
|
741
|
-
return lines.join("\n");
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
// ../atomix-sync-core/src/formats/dart.ts
|
|
745
|
-
function toSwiftName3(cssVar) {
|
|
746
|
-
return cssVar.replace(/^--atmx-/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
747
|
-
}
|
|
748
|
-
function toDartName(cssVar) {
|
|
749
|
-
return toSwiftName3(cssVar);
|
|
750
|
-
}
|
|
751
|
-
function isColorValue3(value) {
|
|
752
|
-
return /^#[0-9A-Fa-f]{3,8}$/.test(value) || /^rgb/.test(value) || /^hsl/.test(value);
|
|
753
|
-
}
|
|
754
|
-
function hexToDartColor(hex) {
|
|
755
|
-
const clean = hex.replace("#", "").toUpperCase();
|
|
756
|
-
if (clean.length === 3) {
|
|
757
|
-
const r = clean[0], g = clean[1], b = clean[2];
|
|
758
|
-
return `Color(0xFF${r}${r}${g}${g}${b}${b})`;
|
|
759
|
-
}
|
|
760
|
-
if (clean.length === 6) {
|
|
761
|
-
return `Color(0xFF${clean})`;
|
|
762
|
-
}
|
|
763
|
-
if (clean.length === 8) {
|
|
764
|
-
const rgb = clean.substring(0, 6);
|
|
765
|
-
const alpha = clean.substring(6, 8);
|
|
766
|
-
return `Color(0x${alpha}${rgb})`;
|
|
767
|
-
}
|
|
768
|
-
return `Colors.transparent // Could not parse: ${hex}`;
|
|
769
|
-
}
|
|
770
|
-
function generateDartOutput(cssVariables, darkModeColors, deprecatedTokens = /* @__PURE__ */ new Map()) {
|
|
771
|
-
const lines = [
|
|
772
|
-
"// Atomix Design System Tokens",
|
|
773
|
-
"// Auto-generated - do not edit manually",
|
|
774
|
-
`// Synced: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
775
|
-
"",
|
|
776
|
-
"import 'package:flutter/material.dart';",
|
|
777
|
-
"",
|
|
778
|
-
"class DesignTokens {",
|
|
779
|
-
" DesignTokens._();",
|
|
780
|
-
"",
|
|
781
|
-
" // Colors (Light Mode)"
|
|
782
|
-
];
|
|
783
|
-
const colors = [];
|
|
784
|
-
const spacing = [];
|
|
785
|
-
const typography = [];
|
|
786
|
-
const other = [];
|
|
787
|
-
for (const [key, value] of Object.entries(cssVariables)) {
|
|
788
|
-
const isDeprecated = false;
|
|
789
|
-
if (key.includes("-color-")) colors.push([key, value, isDeprecated]);
|
|
790
|
-
else if (key.includes("-spacing-")) spacing.push([key, value, isDeprecated]);
|
|
791
|
-
else if (key.includes("-typography-") || key.includes("-font-")) typography.push([key, value, isDeprecated]);
|
|
792
|
-
else other.push([key, value, isDeprecated]);
|
|
793
|
-
}
|
|
794
|
-
for (const [key, value] of deprecatedTokens.entries()) {
|
|
795
|
-
const isDeprecated = true;
|
|
796
|
-
if (key.includes("-color-")) colors.push([key, value, isDeprecated]);
|
|
797
|
-
else if (key.includes("-spacing-")) spacing.push([key, value, isDeprecated]);
|
|
798
|
-
else if (key.includes("-typography-") || key.includes("-font-")) typography.push([key, value, isDeprecated]);
|
|
799
|
-
else other.push([key, value, isDeprecated]);
|
|
800
|
-
}
|
|
801
|
-
colors.sort((a, b) => a[0].localeCompare(b[0]));
|
|
802
|
-
spacing.sort((a, b) => a[0].localeCompare(b[0]));
|
|
803
|
-
typography.sort((a, b) => a[0].localeCompare(b[0]));
|
|
804
|
-
other.sort((a, b) => a[0].localeCompare(b[0]));
|
|
805
|
-
for (const [key, value, isDeprecated] of colors) {
|
|
806
|
-
const name = toDartName(key);
|
|
807
|
-
if (isColorValue3(value)) {
|
|
808
|
-
if (isDeprecated) {
|
|
809
|
-
lines.push(` @Deprecated('This token has been removed from the design system')`);
|
|
810
|
-
lines.push(` static const ${name} = ${hexToDartColor(value)};`);
|
|
811
|
-
} else {
|
|
812
|
-
lines.push(` static const ${name} = ${hexToDartColor(value)};`);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
if (darkModeColors && Object.keys(darkModeColors).length > 0) {
|
|
817
|
-
lines.push("");
|
|
818
|
-
lines.push(" // Colors (Dark Mode)");
|
|
819
|
-
for (const [key, value] of Object.entries(darkModeColors)) {
|
|
820
|
-
const name = `color${key.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("")}Dark`;
|
|
821
|
-
if (isColorValue3(value)) {
|
|
822
|
-
lines.push(` static const ${name} = ${hexToDartColor(value)};`);
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
lines.push("");
|
|
827
|
-
lines.push(" // Spacing");
|
|
828
|
-
for (const [key, value, isDeprecated] of spacing) {
|
|
829
|
-
const name = toDartName(key);
|
|
830
|
-
const numValue = parseFloat(value);
|
|
831
|
-
if (!isNaN(numValue)) {
|
|
832
|
-
if (isDeprecated) {
|
|
833
|
-
lines.push(` @Deprecated('This token has been removed from the design system')`);
|
|
834
|
-
lines.push(` static const double ${name} = ${numValue};`);
|
|
835
|
-
} else {
|
|
836
|
-
lines.push(` static const double ${name} = ${numValue};`);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
lines.push("");
|
|
841
|
-
lines.push(" // Typography");
|
|
842
|
-
for (const [key, value, isDeprecated] of typography) {
|
|
843
|
-
const name = toDartName(key);
|
|
844
|
-
if (isDeprecated) {
|
|
845
|
-
lines.push(` @Deprecated('This token has been removed from the design system')`);
|
|
846
|
-
}
|
|
847
|
-
if (key.includes("size")) {
|
|
848
|
-
const numValue = parseFloat(value);
|
|
849
|
-
if (!isNaN(numValue)) {
|
|
850
|
-
lines.push(` static const double ${name} = ${numValue};`);
|
|
851
|
-
}
|
|
852
|
-
} else {
|
|
853
|
-
lines.push(` static const String ${name} = '${value}';`);
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
lines.push("");
|
|
857
|
-
lines.push(" // Other");
|
|
858
|
-
for (const [key, value, isDeprecated] of other) {
|
|
859
|
-
const name = toDartName(key);
|
|
860
|
-
if (isDeprecated) {
|
|
861
|
-
lines.push(` @Deprecated('This token has been removed from the design system')`);
|
|
862
|
-
}
|
|
863
|
-
if (isColorValue3(value)) {
|
|
864
|
-
lines.push(` static const ${name} = ${hexToDartColor(value)};`);
|
|
865
|
-
} else {
|
|
866
|
-
const numValue = parseFloat(value);
|
|
867
|
-
if (!isNaN(numValue)) {
|
|
868
|
-
lines.push(` static const double ${name} = ${numValue};`);
|
|
869
|
-
} else {
|
|
870
|
-
lines.push(` static const String ${name} = '${value}';`);
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
lines.push("}");
|
|
875
|
-
lines.push("");
|
|
876
|
-
return lines.join("\n");
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
// ../atomix-sync-core/src/response.ts
|
|
880
|
-
import * as path2 from "path";
|
|
881
|
-
function formatSyncResponse(options) {
|
|
882
|
-
const {
|
|
883
|
-
data,
|
|
884
|
-
output,
|
|
885
|
-
format,
|
|
886
|
-
dsTokenCount,
|
|
887
|
-
deprecatedCount,
|
|
888
|
-
deprecatedTokens,
|
|
889
|
-
diff,
|
|
890
|
-
changes = [],
|
|
891
|
-
fileExists,
|
|
892
|
-
rulesResults = [],
|
|
893
|
-
governanceChanges = [],
|
|
894
|
-
changeSummary,
|
|
895
|
-
hasRefactorRecommendation = false,
|
|
896
|
-
deprecatedTokenCount = 0
|
|
897
|
-
} = options;
|
|
898
|
-
const lastUpdated = data.meta.exportedAt ? new Date(data.meta.exportedAt).toLocaleString() : "N/A";
|
|
899
|
-
let response = `\u2713 Synced ${dsTokenCount} tokens to ${output}
|
|
900
|
-
`;
|
|
901
|
-
response += `Format: ${format}
|
|
902
|
-
`;
|
|
903
|
-
response += `Design System: ${data.meta.name} (v${data.meta.version})
|
|
904
|
-
`;
|
|
905
|
-
if (deprecatedCount > 0) {
|
|
906
|
-
response += `Tokens: ${dsTokenCount} active, ${deprecatedCount} deprecated (run /refactor to migrate)
|
|
907
|
-
`;
|
|
908
|
-
} else {
|
|
909
|
-
response += `Tokens: ${dsTokenCount}
|
|
910
|
-
`;
|
|
911
|
-
}
|
|
912
|
-
response += `Version: ${data.meta.version}
|
|
913
|
-
`;
|
|
914
|
-
response += `Last updated: ${lastUpdated}
|
|
915
|
-
`;
|
|
916
|
-
if (deprecatedCount > 0) {
|
|
917
|
-
response += `
|
|
918
|
-
\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
|
|
919
|
-
`;
|
|
920
|
-
response += `\u{1F4CB} DEPRECATED TOKENS (${deprecatedCount})
|
|
921
|
-
`;
|
|
922
|
-
response += `\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
|
|
923
|
-
|
|
924
|
-
`;
|
|
925
|
-
response += `These tokens are no longer in the design system but are preserved in your file for backward compatibility.
|
|
926
|
-
`;
|
|
927
|
-
response += `Run \`/refactor\` to find and migrate usage in your codebase.
|
|
928
|
-
|
|
929
|
-
`;
|
|
930
|
-
const deprecatedTokenList = Array.from(deprecatedTokens.keys()).sort();
|
|
931
|
-
deprecatedTokenList.forEach((token, index) => {
|
|
932
|
-
const value = deprecatedTokens.get(token);
|
|
933
|
-
response += ` ${index + 1}. ${token}`;
|
|
934
|
-
if (value) {
|
|
935
|
-
response += ` = ${value}`;
|
|
936
|
-
}
|
|
937
|
-
response += `
|
|
938
|
-
`;
|
|
939
|
-
});
|
|
940
|
-
}
|
|
941
|
-
const newTokenList = [];
|
|
942
|
-
if (diff) {
|
|
943
|
-
newTokenList.push(...diff.added, ...diff.addedDark);
|
|
944
|
-
}
|
|
945
|
-
if (changeSummary && changeSummary.includes("\u2795 Added:")) {
|
|
946
|
-
const addedSection = changeSummary.match(/➕ Added: \d+ token\(s\)([\s\S]*?)(?=➖|🔄|📝|$)/);
|
|
947
|
-
if (addedSection) {
|
|
948
|
-
const addedLines = addedSection[0].match(/ - (--[^\n]+)/g) || [];
|
|
949
|
-
addedLines.forEach((line) => {
|
|
950
|
-
const token = line.replace(/ - /, "").trim();
|
|
951
|
-
if (token && !newTokenList.includes(token)) {
|
|
952
|
-
newTokenList.push(token);
|
|
953
|
-
}
|
|
954
|
-
});
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
if (newTokenList.length > 0) {
|
|
958
|
-
response += `
|
|
959
|
-
\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
|
|
960
|
-
`;
|
|
961
|
-
response += `\u2728 NEW TOKENS (${newTokenList.length})
|
|
962
|
-
`;
|
|
963
|
-
response += `\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
|
|
964
|
-
|
|
965
|
-
`;
|
|
966
|
-
newTokenList.sort().forEach((token, index) => {
|
|
967
|
-
response += ` ${index + 1}. ${token}
|
|
968
|
-
`;
|
|
969
|
-
});
|
|
970
|
-
}
|
|
971
|
-
const modifiedFiles = [output];
|
|
972
|
-
rulesResults.forEach((result) => {
|
|
973
|
-
if (result.success) {
|
|
974
|
-
const fullPath = path2.resolve(process.cwd(), result.path);
|
|
975
|
-
if (!modifiedFiles.includes(fullPath)) {
|
|
976
|
-
modifiedFiles.push(fullPath);
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
});
|
|
980
|
-
response += `
|
|
981
|
-
\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
|
|
982
|
-
`;
|
|
983
|
-
response += `\u{1F4C1} MODIFIED FILES (${modifiedFiles.length})
|
|
984
|
-
`;
|
|
985
|
-
response += `\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
|
|
986
|
-
|
|
987
|
-
`;
|
|
988
|
-
modifiedFiles.forEach((file, index) => {
|
|
989
|
-
const relativePath = path2.relative(process.cwd(), file);
|
|
990
|
-
response += ` ${index + 1}. ${relativePath}
|
|
991
|
-
`;
|
|
992
|
-
});
|
|
993
|
-
if (governanceChanges.length > 0) {
|
|
994
|
-
response += `
|
|
995
|
-
\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
|
|
996
|
-
`;
|
|
997
|
-
response += `\u{1F4DD} AI RULES CHANGES
|
|
998
|
-
`;
|
|
999
|
-
response += `\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
|
|
1000
|
-
|
|
1001
|
-
`;
|
|
1002
|
-
response += `The following AI guidance sections have been updated:
|
|
1003
|
-
|
|
1004
|
-
`;
|
|
1005
|
-
const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
|
|
1006
|
-
governanceChanges.forEach((foundation, index) => {
|
|
1007
|
-
const displayName = foundation === "general" ? "General Rules" : capitalize(foundation);
|
|
1008
|
-
response += ` ${index + 1}. ${displayName}
|
|
1009
|
-
`;
|
|
1010
|
-
});
|
|
1011
|
-
response += `
|
|
1012
|
-
These changes are reflected in your AI tool rules files (e.g., .cursorrules).
|
|
1013
|
-
`;
|
|
1014
|
-
response += `Review the updated rules files to see the new guidance.
|
|
1015
|
-
`;
|
|
1016
|
-
}
|
|
1017
|
-
if (diff) {
|
|
1018
|
-
const lightChanges = diff.added.length + diff.modified.length;
|
|
1019
|
-
const darkChanges = diff.addedDark.length + diff.modifiedDark.length;
|
|
1020
|
-
if (lightChanges > 0 || darkChanges > 0 || deprecatedCount > 0) {
|
|
1021
|
-
response += `
|
|
1022
|
-
\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
|
|
1023
|
-
`;
|
|
1024
|
-
response += `\u{1F4CA} DETAILED CHANGES
|
|
1025
|
-
`;
|
|
1026
|
-
response += `\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
|
|
1027
|
-
|
|
1028
|
-
`;
|
|
1029
|
-
const lightSummary = lightChanges > 0 ? `Light: ${diff.modified.length} modified, ${diff.added.length} added` : "";
|
|
1030
|
-
const darkSummary = darkChanges > 0 ? `Dark: ${diff.modifiedDark.length} modified, ${diff.addedDark.length} added` : "";
|
|
1031
|
-
const deprecatedSummary = deprecatedCount > 0 ? `${deprecatedCount} deprecated (use /refactor)` : "";
|
|
1032
|
-
const diffSummary = [lightSummary, darkSummary, deprecatedSummary].filter(Boolean).join(" | ");
|
|
1033
|
-
response += `${diffSummary}
|
|
1034
|
-
`;
|
|
1035
|
-
if (changes.length > 0 && changes.length <= 10) {
|
|
1036
|
-
response += "\nModified tokens:\n";
|
|
1037
|
-
for (const { key, old: oldVal, new: newVal } of changes) {
|
|
1038
|
-
response += ` ${key}: ${oldVal} \u2192 ${newVal}
|
|
1039
|
-
`;
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
} else if (!fileExists) {
|
|
1044
|
-
response += `
|
|
1045
|
-
(New file created)
|
|
1046
|
-
`;
|
|
1047
|
-
}
|
|
1048
|
-
if (hasRefactorRecommendation && deprecatedTokenCount > 0) {
|
|
1049
|
-
response += `
|
|
1050
|
-
\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
|
|
1051
|
-
`;
|
|
1052
|
-
response += `\u{1F4A1} NEXT STEP
|
|
1053
|
-
`;
|
|
1054
|
-
response += `\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
|
|
1055
|
-
|
|
1056
|
-
`;
|
|
1057
|
-
response += `${deprecatedTokenCount} token(s) have been deprecated.
|
|
1058
|
-
`;
|
|
1059
|
-
response += `Your codebase may still reference these deprecated tokens.
|
|
1060
|
-
|
|
1061
|
-
`;
|
|
1062
|
-
response += `Run \`/refactor\` to:
|
|
1063
|
-
`;
|
|
1064
|
-
response += ` \u2022 Scan your codebase for deprecated token usage
|
|
1065
|
-
`;
|
|
1066
|
-
response += ` \u2022 See which files need updates
|
|
1067
|
-
`;
|
|
1068
|
-
response += ` \u2022 Review and apply changes with your confirmation
|
|
1069
|
-
`;
|
|
1070
|
-
}
|
|
1071
|
-
return response;
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
// ../atomix-sync-core/src/tokens.ts
|
|
1075
|
-
function getTokenByPath(tokens, path4) {
|
|
1076
|
-
const parts = path4.split(".");
|
|
1077
|
-
let current = tokens;
|
|
1078
|
-
for (const part of parts) {
|
|
1079
|
-
if (current && typeof current === "object" && part in current) {
|
|
1080
|
-
current = current[part];
|
|
1081
|
-
} else {
|
|
1082
|
-
return void 0;
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
return current;
|
|
1086
|
-
}
|
|
1087
|
-
function flattenTokens(obj, prefix = "") {
|
|
1088
|
-
const results = [];
|
|
1089
|
-
if (obj && typeof obj === "object" && !Array.isArray(obj)) {
|
|
1090
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
1091
|
-
const newPath = prefix ? `${prefix}.${key}` : key;
|
|
1092
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1093
|
-
results.push(...flattenTokens(value, newPath));
|
|
1094
|
-
} else {
|
|
1095
|
-
results.push({ path: newPath, value });
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
return results;
|
|
1100
|
-
}
|
|
1101
|
-
function searchTokens(tokens, query) {
|
|
1102
|
-
const flat = flattenTokens(tokens);
|
|
1103
|
-
const lowerQuery = query.toLowerCase();
|
|
1104
|
-
return flat.filter(({ path: path4, value }) => {
|
|
1105
|
-
const pathMatch = path4.toLowerCase().includes(lowerQuery);
|
|
1106
|
-
const valueMatch = String(value).toLowerCase().includes(lowerQuery);
|
|
1107
|
-
return pathMatch || valueMatch;
|
|
1108
|
-
});
|
|
1109
|
-
}
|
|
1110
|
-
function countTokens(tokens, prefix = "") {
|
|
1111
|
-
let count = 0;
|
|
1112
|
-
for (const [key, value] of Object.entries(tokens)) {
|
|
1113
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1114
|
-
count += countTokens(value, `${prefix}${key}.`);
|
|
1115
|
-
} else if (value !== void 0 && value !== null) {
|
|
1116
|
-
count++;
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
return count;
|
|
1120
|
-
}
|
|
1121
|
-
function getTokenStats(data) {
|
|
1122
|
-
const byCategory = {};
|
|
1123
|
-
let total = 0;
|
|
1124
|
-
for (const [category, value] of Object.entries(data.tokens)) {
|
|
1125
|
-
if (value && typeof value === "object") {
|
|
1126
|
-
const count = countTokens(value);
|
|
1127
|
-
byCategory[category] = count;
|
|
1128
|
-
total += count;
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
return {
|
|
1132
|
-
total,
|
|
1133
|
-
byCategory,
|
|
1134
|
-
cssVariables: Object.keys(data.cssVariables).length,
|
|
1135
|
-
governanceRules: data.governance.rules.length
|
|
1136
|
-
};
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
// src/index.ts
|
|
1140
|
-
import * as path3 from "path";
|
|
1141
|
-
import * as fs2 from "fs";
|
|
36
|
+
import * as fs from "fs";
|
|
1142
37
|
function parseArgs() {
|
|
1143
38
|
const args = process.argv.slice(2);
|
|
1144
39
|
let dsId2 = null;
|
|
@@ -1165,35 +60,40 @@ var cachedData = null;
|
|
|
1165
60
|
var cachedETag = null;
|
|
1166
61
|
var lastChangeSummary = null;
|
|
1167
62
|
var lastSyncAffectedTokens = null;
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
63
|
+
function getLastChangeSummary() {
|
|
64
|
+
return lastChangeSummary;
|
|
65
|
+
}
|
|
66
|
+
async function updateChangeSummary(freshData) {
|
|
67
|
+
if (cachedData) {
|
|
68
|
+
const changes = compareDesignSystems(cachedData, freshData);
|
|
69
|
+
lastChangeSummary = changes.summary;
|
|
70
|
+
if (changes.hasChanges) {
|
|
71
|
+
console.error(`[mcp-user] Design system changes detected:
|
|
72
|
+
${changes.summary}`);
|
|
73
|
+
}
|
|
1171
74
|
}
|
|
75
|
+
}
|
|
76
|
+
async function fetchDesignSystemForMCP(forceRefresh = false) {
|
|
77
|
+
if (!dsId) throw new Error("Missing --ds-id. Usage: npx @atomixstudio/mcp --ds-id <id>");
|
|
1172
78
|
const result = await fetchDesignSystem({
|
|
1173
79
|
dsId,
|
|
1174
80
|
apiKey: apiKey ?? void 0,
|
|
1175
|
-
apiBase,
|
|
81
|
+
apiBase: apiBase ?? void 0,
|
|
1176
82
|
etag: forceRefresh ? void 0 : cachedETag ?? void 0,
|
|
1177
83
|
forceRefresh
|
|
1178
84
|
});
|
|
1179
|
-
if (result.status === 304 && cachedData)
|
|
1180
|
-
|
|
1181
|
-
}
|
|
1182
|
-
if (!result.data) {
|
|
1183
|
-
throw new Error("Failed to fetch design system: no data returned");
|
|
1184
|
-
}
|
|
85
|
+
if (result.status === 304 && cachedData) return cachedData;
|
|
86
|
+
if (result.status === 304 || !result.data) throw new Error("No design system data (304 or null)");
|
|
1185
87
|
cachedData = result.data;
|
|
1186
88
|
cachedETag = result.etag;
|
|
89
|
+
await updateChangeSummary(result.data);
|
|
1187
90
|
return result.data;
|
|
1188
91
|
}
|
|
1189
|
-
function getLastChangeSummary() {
|
|
1190
|
-
return lastChangeSummary;
|
|
1191
|
-
}
|
|
1192
92
|
var TOKEN_CATEGORIES = ["colors", "typography", "spacing", "sizing", "shadows", "radius", "borders", "motion", "zIndex"];
|
|
1193
93
|
var server = new Server(
|
|
1194
94
|
{
|
|
1195
95
|
name: "atomix-mcp-user",
|
|
1196
|
-
version: "1.0.
|
|
96
|
+
version: "1.0.9"
|
|
1197
97
|
},
|
|
1198
98
|
{
|
|
1199
99
|
capabilities: {
|
|
@@ -1335,6 +235,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
1335
235
|
},
|
|
1336
236
|
required: ["output"]
|
|
1337
237
|
}
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: "getDependencies",
|
|
241
|
+
description: "Get suggested dependencies for this design system (icon package, fonts, SKILL.md, MCP config, token files). Use with atomix/install. Optional platform and stack for tailored suggestions.",
|
|
242
|
+
inputSchema: {
|
|
243
|
+
type: "object",
|
|
244
|
+
properties: {
|
|
245
|
+
platform: {
|
|
246
|
+
type: "string",
|
|
247
|
+
enum: ["web", "ios", "android"],
|
|
248
|
+
description: "Target platform (web, ios, android). Optional."
|
|
249
|
+
},
|
|
250
|
+
stack: {
|
|
251
|
+
type: "string",
|
|
252
|
+
description: "Stack or framework (e.g. react, vue, next, swift, kotlin). Optional."
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
required: []
|
|
256
|
+
}
|
|
1338
257
|
}
|
|
1339
258
|
]
|
|
1340
259
|
};
|
|
@@ -1346,27 +265,27 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1346
265
|
const data = await fetchDesignSystemForMCP(shouldForceRefresh);
|
|
1347
266
|
switch (name) {
|
|
1348
267
|
case "getToken": {
|
|
1349
|
-
const
|
|
1350
|
-
const value = getTokenByPath(data.tokens,
|
|
268
|
+
const path2 = args?.path;
|
|
269
|
+
const value = getTokenByPath(data.tokens, path2);
|
|
1351
270
|
if (value === void 0) {
|
|
1352
271
|
return {
|
|
1353
272
|
content: [{
|
|
1354
273
|
type: "text",
|
|
1355
274
|
text: JSON.stringify({
|
|
1356
|
-
error: `Token not found: ${
|
|
275
|
+
error: `Token not found: ${path2}`,
|
|
1357
276
|
suggestion: "Use listTokens or searchTokens to find available tokens.",
|
|
1358
277
|
availableCategories: TOKEN_CATEGORIES
|
|
1359
278
|
}, null, 2)
|
|
1360
279
|
}]
|
|
1361
280
|
};
|
|
1362
281
|
}
|
|
1363
|
-
const cssVarKey = `--atmx-${
|
|
282
|
+
const cssVarKey = `--atmx-${path2.replace(/\./g, "-")}`;
|
|
1364
283
|
const cssVar = data.cssVariables[cssVarKey];
|
|
1365
284
|
return {
|
|
1366
285
|
content: [{
|
|
1367
286
|
type: "text",
|
|
1368
287
|
text: JSON.stringify({
|
|
1369
|
-
path:
|
|
288
|
+
path: path2,
|
|
1370
289
|
value,
|
|
1371
290
|
cssVariable: cssVar || `var(${cssVarKey})`,
|
|
1372
291
|
usage: `style={{ property: "var(${cssVarKey})" }}`
|
|
@@ -1393,13 +312,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1393
312
|
};
|
|
1394
313
|
}
|
|
1395
314
|
const flat = flattenTokens(tokensToList);
|
|
1396
|
-
const tokensWithCssVars = flat.map(({ path:
|
|
1397
|
-
const fullPath = subcategory ? `${category}.${subcategory}.${
|
|
315
|
+
const tokensWithCssVars = flat.map(({ path: path2, value }) => {
|
|
316
|
+
const fullPath = subcategory ? `${category}.${subcategory}.${path2}` : `${category}.${path2}`;
|
|
1398
317
|
let cssVar;
|
|
1399
318
|
if (category === "colors" && subcategory === "static.brand") {
|
|
1400
|
-
cssVar = data.cssVariables[`--atmx-color-brand-${
|
|
319
|
+
cssVar = data.cssVariables[`--atmx-color-brand-${path2}`];
|
|
1401
320
|
} else if (category === "colors" && subcategory?.startsWith("modes.")) {
|
|
1402
|
-
cssVar = data.cssVariables[`--atmx-color-${
|
|
321
|
+
cssVar = data.cssVariables[`--atmx-color-${path2}`];
|
|
1403
322
|
} else {
|
|
1404
323
|
const cssVarKey = `--atmx-${fullPath.replace(/\./g, "-")}`;
|
|
1405
324
|
cssVar = data.cssVariables[cssVarKey];
|
|
@@ -1590,12 +509,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1590
509
|
- \u274C **Lost**: Regular CSS rules (selectors, classes, media queries, etc.)
|
|
1591
510
|
|
|
1592
511
|
**Best Practice**: Keep \`tokens.css\` separate from your custom CSS. Use a separate file (e.g., \`custom.css\`) for custom styles.`,
|
|
1593
|
-
copilot: `#
|
|
512
|
+
copilot: `# Copilot Setup
|
|
1594
513
|
|
|
1595
|
-
1. Create \`.github/copilot-instructions.md\`
|
|
514
|
+
1. Create a Copilot instructions file in your project (e.g. \`.github/copilot-instructions.md\`)
|
|
1596
515
|
2. Use getAIToolRules({ tool: "copilot" }) to get the content
|
|
1597
|
-
3. Enable custom instructions in
|
|
1598
|
-
"github.copilot.chat.codeGeneration.useInstructionFiles": true
|
|
516
|
+
3. Enable custom instructions in your editor (e.g. \`github.copilot.chat.codeGeneration.useInstructionFiles\`: true in settings)
|
|
1599
517
|
|
|
1600
518
|
## File Structure
|
|
1601
519
|
|
|
@@ -1720,12 +638,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1720
638
|
}]
|
|
1721
639
|
};
|
|
1722
640
|
}
|
|
1723
|
-
const outputPath =
|
|
1724
|
-
const fileExists =
|
|
641
|
+
const outputPath = path.resolve(process.cwd(), output);
|
|
642
|
+
const fileExists = fs.existsSync(outputPath);
|
|
1725
643
|
const deprecatedTokens = /* @__PURE__ */ new Map();
|
|
1726
644
|
const existingTokens = /* @__PURE__ */ new Map();
|
|
1727
645
|
if (fileExists && ["css", "scss", "less"].includes(format)) {
|
|
1728
|
-
const oldContent =
|
|
646
|
+
const oldContent = fs.readFileSync(outputPath, "utf-8");
|
|
1729
647
|
const oldVarPattern = /(?:^|\n)\s*(?:\/\*[^*]*\*+(?:[^/*][^*]*\*+)*\/\s*)?(--[a-zA-Z0-9-]+):\s*([^;]+);/gm;
|
|
1730
648
|
let match;
|
|
1731
649
|
while ((match = oldVarPattern.exec(oldContent)) !== null) {
|
|
@@ -1778,7 +696,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1778
696
|
let changes = [];
|
|
1779
697
|
let diff;
|
|
1780
698
|
if (fileExists && ["css", "scss", "less"].includes(format)) {
|
|
1781
|
-
const oldContent =
|
|
699
|
+
const oldContent = fs.readFileSync(outputPath, "utf-8");
|
|
1782
700
|
diff = diffTokens(oldContent, mergedCssVariables, format, darkModeColors?.dark);
|
|
1783
701
|
const lightChanges = diff.added.length + diff.modified.length;
|
|
1784
702
|
const darkChanges = diff.addedDark.length + diff.modifiedDark.length;
|
|
@@ -1818,17 +736,17 @@ Last updated: ${lastUpdated}`
|
|
|
1818
736
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1819
737
|
};
|
|
1820
738
|
}
|
|
1821
|
-
const outputDir =
|
|
1822
|
-
if (!
|
|
1823
|
-
|
|
739
|
+
const outputDir = path.dirname(outputPath);
|
|
740
|
+
if (!fs.existsSync(outputDir)) {
|
|
741
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
1824
742
|
}
|
|
1825
|
-
|
|
743
|
+
fs.writeFileSync(outputPath, newContent);
|
|
1826
744
|
let rulesResults = [];
|
|
1827
745
|
try {
|
|
1828
746
|
rulesResults = await syncRulesFiles({
|
|
1829
747
|
dsId,
|
|
1830
748
|
apiKey: apiKey ?? void 0,
|
|
1831
|
-
apiBase,
|
|
749
|
+
apiBase: apiBase ?? void 0,
|
|
1832
750
|
rulesDir: process.cwd()
|
|
1833
751
|
});
|
|
1834
752
|
} catch (error) {
|
|
@@ -1858,13 +776,74 @@ Last updated: ${lastUpdated}`
|
|
|
1858
776
|
}]
|
|
1859
777
|
};
|
|
1860
778
|
}
|
|
779
|
+
case "getDependencies": {
|
|
780
|
+
const platform = args?.platform;
|
|
781
|
+
const stack = args?.stack;
|
|
782
|
+
const tokens = data.tokens;
|
|
783
|
+
const typography = tokens?.typography;
|
|
784
|
+
const fontFamily = typography?.fontFamily;
|
|
785
|
+
const fontNames = [];
|
|
786
|
+
if (fontFamily) {
|
|
787
|
+
for (const key of ["display", "heading", "body"]) {
|
|
788
|
+
const v = fontFamily[key];
|
|
789
|
+
if (typeof v === "string" && v && !fontNames.includes(v)) fontNames.push(v);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
const icons = tokens?.icons;
|
|
793
|
+
const ICON_PACKAGES = {
|
|
794
|
+
lucide: { web: "lucide-react", native: "lucide-react-native" },
|
|
795
|
+
heroicons: { web: "@heroicons/react", native: "heroicons-react-native" },
|
|
796
|
+
phosphor: { web: "phosphor-react", native: "phosphor-react-native" }
|
|
797
|
+
};
|
|
798
|
+
const lib = icons?.library || "lucide";
|
|
799
|
+
const iconPkgs = ICON_PACKAGES[lib] || ICON_PACKAGES.lucide;
|
|
800
|
+
const serverName = data.meta.name.toLowerCase().replace(/[^a-z0-9]/g, "-");
|
|
801
|
+
const npxArgs = ["@atomixstudio/mcp@latest"];
|
|
802
|
+
if (dsId) npxArgs.push("--ds-id", dsId);
|
|
803
|
+
if (apiKey) npxArgs.push("--api-key", apiKey);
|
|
804
|
+
const mcpConfig = {
|
|
805
|
+
path: ".cursor/mcp.json",
|
|
806
|
+
content: JSON.stringify({
|
|
807
|
+
mcpServers: {
|
|
808
|
+
[serverName]: { command: "npx", args: npxArgs }
|
|
809
|
+
}
|
|
810
|
+
}, null, 2)
|
|
811
|
+
};
|
|
812
|
+
const payload = {
|
|
813
|
+
iconLibrary: {
|
|
814
|
+
package: iconPkgs.web,
|
|
815
|
+
nativePackage: iconPkgs.native,
|
|
816
|
+
performanceHint: "Use individual SVG imports for tree-shaking; avoid loading entire icon libraries or icon fonts."
|
|
817
|
+
},
|
|
818
|
+
fonts: {
|
|
819
|
+
families: fontNames,
|
|
820
|
+
performanceHint: "Implement self-hosted fonts and local @font-face declarations; avoid external CDN links; use font-display: swap."
|
|
821
|
+
},
|
|
822
|
+
skill: {
|
|
823
|
+
path: ".cursor/skills/atomix-ds/SKILL.md",
|
|
824
|
+
content: GENERIC_SKILL_MD
|
|
825
|
+
},
|
|
826
|
+
mcpConfig,
|
|
827
|
+
tokenFiles: {
|
|
828
|
+
files: ["tokens.css", "tokens.json"],
|
|
829
|
+
copyInstructions: "Copy tokens.css and tokens.json from the design system export (or use syncTokens) into your project."
|
|
830
|
+
},
|
|
831
|
+
meta: { dsName: data.meta.name, platform: platform ?? void 0, stack: stack ?? void 0 }
|
|
832
|
+
};
|
|
833
|
+
return {
|
|
834
|
+
content: [{
|
|
835
|
+
type: "text",
|
|
836
|
+
text: JSON.stringify(payload, null, 2)
|
|
837
|
+
}]
|
|
838
|
+
};
|
|
839
|
+
}
|
|
1861
840
|
default:
|
|
1862
841
|
return {
|
|
1863
842
|
content: [{
|
|
1864
843
|
type: "text",
|
|
1865
844
|
text: JSON.stringify({
|
|
1866
845
|
error: `Unknown tool: ${name}`,
|
|
1867
|
-
availableTools: ["getToken", "listTokens", "searchTokens", "validateUsage", "getAIToolRules", "exportMCPConfig", "getSetupInstructions", "syncTokens"]
|
|
846
|
+
availableTools: ["getToken", "listTokens", "searchTokens", "validateUsage", "getAIToolRules", "exportMCPConfig", "getSetupInstructions", "syncTokens", "getDependencies"]
|
|
1868
847
|
}, null, 2)
|
|
1869
848
|
}]
|
|
1870
849
|
};
|
|
@@ -1906,6 +885,38 @@ Last updated: ${lastUpdated}`
|
|
|
1906
885
|
};
|
|
1907
886
|
}
|
|
1908
887
|
});
|
|
888
|
+
var GENERIC_SKILL_MD = `---
|
|
889
|
+
name: atomix-ds
|
|
890
|
+
description: Use this design system when editing UI, design system files, or when the user asks to follow the project's design system. Fetch rules dynamically via MCP.
|
|
891
|
+
---
|
|
892
|
+
|
|
893
|
+
# Atomix Design System
|
|
894
|
+
|
|
895
|
+
## When to use
|
|
896
|
+
|
|
897
|
+
- Editing UI components, pages, or styles
|
|
898
|
+
- Working in design system or token files
|
|
899
|
+
- User asks to follow the project's design system
|
|
900
|
+
|
|
901
|
+
## How to use
|
|
902
|
+
|
|
903
|
+
1. Call MCP tool **getAIToolRules** with the tool id that matches your **current AI coding environment**:
|
|
904
|
+
- **cursor** (Cursor IDE)
|
|
905
|
+
- **windsurf** (Windsurf)
|
|
906
|
+
- **copilot** (Copilot)
|
|
907
|
+
- **cline** (Cline)
|
|
908
|
+
- **continue** (Continue)
|
|
909
|
+
- **zed** (Zed)
|
|
910
|
+
- **generic** (other tools)
|
|
911
|
+
|
|
912
|
+
Example: \`getAIToolRules({ tool: "cursor" })\` when in Cursor.
|
|
913
|
+
|
|
914
|
+
2. Alternatively use the **design-system-rules** prompt or the resource \`atomix://rules/<tool>\` with the same tool id.
|
|
915
|
+
|
|
916
|
+
3. Apply the returned rules (tokens, governance) when generating or editing code.
|
|
917
|
+
|
|
918
|
+
4. Ensure Atomix MCP is configured with the project's \`--ds-id\` (config path varies by platform: e.g. \`.cursor/mcp.json\`, \`.windsurf/mcp.json\`).
|
|
919
|
+
`;
|
|
1909
920
|
var AI_TOOLS = ["cursor", "copilot", "windsurf", "cline", "continue", "zed", "generic"];
|
|
1910
921
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
1911
922
|
try {
|
|
@@ -1913,7 +924,7 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
|
1913
924
|
const stats = getTokenStats(data);
|
|
1914
925
|
const resources = [
|
|
1915
926
|
{
|
|
1916
|
-
uri: "atomix://
|
|
927
|
+
uri: "atomix://hello",
|
|
1917
928
|
name: `Welcome to ${data.meta.name}`,
|
|
1918
929
|
description: `Design system overview with ${stats.total} tokens and ${stats.governanceRules} governance rules`,
|
|
1919
930
|
mimeType: "text/markdown"
|
|
@@ -1968,7 +979,7 @@ Get your DS ID from: https://atomixstudio.eu/ds/[your-ds-id]`
|
|
|
1968
979
|
}
|
|
1969
980
|
const data = await fetchDesignSystemForMCP();
|
|
1970
981
|
const stats = getTokenStats(data);
|
|
1971
|
-
if (uri === "atomix://
|
|
982
|
+
if (uri === "atomix://hello") {
|
|
1972
983
|
const welcome = generateWelcomeMessage(data, stats);
|
|
1973
984
|
return {
|
|
1974
985
|
contents: [{
|
|
@@ -2005,7 +1016,7 @@ Get your DS ID from: https://atomixstudio.eu/ds/[your-ds-id]`
|
|
|
2005
1016
|
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
2006
1017
|
const prompts = [
|
|
2007
1018
|
{
|
|
2008
|
-
name: "
|
|
1019
|
+
name: "hello",
|
|
2009
1020
|
description: "Get started with this design system - shows overview, available tokens, and tools. Run this first!"
|
|
2010
1021
|
},
|
|
2011
1022
|
{
|
|
@@ -2058,6 +1069,14 @@ server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
|
2058
1069
|
{
|
|
2059
1070
|
name: "refactor",
|
|
2060
1071
|
description: "Migrate deprecated tokens in codebase. Scans for deprecated tokens marked by /sync and suggests replacements. Run after /sync to update code."
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
name: "install",
|
|
1075
|
+
description: "Suggest dependencies for this design system (icons, fonts, SKILL.md, MCP config, tokens). Asks before installing. Run getDependencies then follow phased instructions.",
|
|
1076
|
+
arguments: [
|
|
1077
|
+
{ name: "platform", description: "Target platform (web, ios, android). Optional; ask user if unknown.", required: false },
|
|
1078
|
+
{ name: "stack", description: "Stack or framework (e.g. react, vue, next, swift, kotlin). Optional.", required: false }
|
|
1079
|
+
]
|
|
2061
1080
|
}
|
|
2062
1081
|
];
|
|
2063
1082
|
return { prompts };
|
|
@@ -2170,10 +1189,10 @@ Configure the MCP server in your Cursor settings, then restart Cursor.`
|
|
|
2170
1189
|
return lines.join("\n");
|
|
2171
1190
|
};
|
|
2172
1191
|
switch (name) {
|
|
2173
|
-
case "
|
|
1192
|
+
case "hello": {
|
|
2174
1193
|
const welcome = generateWelcomeMessage(data, stats);
|
|
2175
1194
|
return {
|
|
2176
|
-
description: `
|
|
1195
|
+
description: `Hello \u2014 ${data.meta.name} Design System`,
|
|
2177
1196
|
messages: [
|
|
2178
1197
|
{
|
|
2179
1198
|
role: "user",
|
|
@@ -2492,29 +1511,71 @@ Use \`/color\`, \`/spacing\`, \`/radius\`, \`/typography\`, \`/shadow\`, \`/bord
|
|
|
2492
1511
|
]
|
|
2493
1512
|
};
|
|
2494
1513
|
}
|
|
1514
|
+
case "install": {
|
|
1515
|
+
const installInstructions = `You are running **atomix/install**. Follow these phases in order; do not skip any phase.
|
|
1516
|
+
|
|
1517
|
+
## Phase 1 \u2013 Resolve platform and stack
|
|
1518
|
+
|
|
1519
|
+
- If the project clearly indicates platform (e.g. package.json + web deps, or build.gradle / Android, or Xcode / iOS), infer \`platform\` (e.g. web, ios, android) and optionally \`stack\` (e.g. react, next, vue, swift, kotlin).
|
|
1520
|
+
- If the project gives **no hint** (blank repo, empty folder, or ambiguous): **Ask the user:** "Which platform are you building for? (e.g. web, Android, iOS)" and, if relevant, "Which stack or framework? (e.g. React, Vue, Next, Swift, Kotlin)." Do **not** assume web or any default.
|
|
1521
|
+
- Proceed only once platform (and optionally stack) is known or confirmed.
|
|
1522
|
+
|
|
1523
|
+
## Phase 2 \u2013 Get suggested dependencies
|
|
1524
|
+
|
|
1525
|
+
- Call MCP tool **getDependencies** with the resolved \`platform\` (and optional \`stack\`). Use the parameter names the tool expects (e.g. platform, stack).
|
|
1526
|
+
- If the call fails (e.g. MCP not connected or no ds-id), tell the user: "Atomix MCP is not connected or design system ID is missing. Configure MCP with --ds-id and try again."
|
|
1527
|
+
|
|
1528
|
+
## Phase 3 \u2013 Scan codebase and build suggestion list
|
|
1529
|
+
|
|
1530
|
+
- Scan the repo for: package.json (or equivalent), existing skill path (e.g. .cursor/skills), presence of tokens.css or project token file, font imports/config, icon package usage.
|
|
1531
|
+
- Build two lists: **Suggested dependencies** (from getDependencies, minus what is already present) and **Already present**. Include: icon package, fonts, SKILL path, MCP config, token files.
|
|
1532
|
+
- Do **not** install, copy, or write any file in this phase.
|
|
1533
|
+
|
|
1534
|
+
## Phase 4 \u2013 Present list and ask before install
|
|
1535
|
+
|
|
1536
|
+
- Reply with: "Suggested dependencies: \u2026" and "Already present: \u2026" and state: "Do not install or copy anything until you confirm. Would you like me to install or add these?"
|
|
1537
|
+
- If the user says yes, perform only the steps they approved. If no or only part, perform only that.
|
|
1538
|
+
|
|
1539
|
+
## Phase 5 \u2013 Optional: suggest global styles (if missing)
|
|
1540
|
+
|
|
1541
|
+
- After presenting the dependency list (Phase 4), or after the user has approved install, check whether the project has **global styles that use the design system tokens** (e.g. typography scale, semantic color classes, or theme file referencing DS tokens). If **not** (e.g. no globals.css using tokens, or blank project), ask the user **verbatim**:
|
|
1542
|
+
|
|
1543
|
+
"Your project doesn't appear to have global styles that use the design system tokens (e.g. semantic typography scale or semantic color classes). Would you like me to suggest or generate a minimal set (e.g. typography scale + semantic utilities) so you can develop consistently with the DS?"
|
|
1544
|
+
|
|
1545
|
+
- If the user says yes, suggest or generate a minimal set; do not overwrite existing global styles without confirmation.
|
|
1546
|
+
|
|
1547
|
+
## Phase 6 \u2013 Report what was created
|
|
1548
|
+
|
|
1549
|
+
- After any install or copy steps (and optional global-styles step), list **what was created or updated** (e.g. "Installed: lucide-react. Added: .cursor/skills/atomix-ds/SKILL.md. Updated: .cursor/mcp.json. Copied: tokens.css."). If global styles were added, include that.
|
|
1550
|
+
|
|
1551
|
+
---
|
|
1552
|
+
|
|
1553
|
+
Execute Phase 1 now (resolve or ask platform/stack), then Phase 2 (call getDependencies), then continue through Phase 6.`;
|
|
1554
|
+
return {
|
|
1555
|
+
description: "Suggest dependencies for this design system (atomix/install)",
|
|
1556
|
+
messages: [
|
|
1557
|
+
{
|
|
1558
|
+
role: "user",
|
|
1559
|
+
content: {
|
|
1560
|
+
type: "text",
|
|
1561
|
+
text: installInstructions
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
]
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
2495
1567
|
default:
|
|
2496
1568
|
throw new Error(`Unknown prompt: ${name}`);
|
|
2497
1569
|
}
|
|
2498
1570
|
});
|
|
2499
1571
|
function generateWelcomeMessage(data, stats) {
|
|
2500
|
-
const
|
|
2501
|
-
"getToken - Get a specific token by path",
|
|
2502
|
-
"listTokens - List all tokens in a category",
|
|
2503
|
-
"searchTokens - Search tokens by name or value",
|
|
2504
|
-
"validateUsage - Check if a CSS value follows the design system",
|
|
2505
|
-
"getAIToolRules - Generate AI coding rules for any tool",
|
|
2506
|
-
"exportMCPConfig - Generate MCP config for AI tools",
|
|
2507
|
-
"getSetupInstructions - Get setup guide for specific tools",
|
|
2508
|
-
"syncTokens - Sync tokens to a local file (css, scss, swift, kotlin, dart, etc.)"
|
|
2509
|
-
];
|
|
2510
|
-
const categoryBreakdown = Object.entries(stats.byCategory).map(([cat, count]) => ` - ${cat}: ${count} tokens`).join("\n");
|
|
1572
|
+
const tokenSummary = Object.entries(stats.byCategory).map(([cat, count]) => `${cat}: ${count}`).join(", ");
|
|
2511
1573
|
const asciiArt = `
|
|
2512
1574
|
\`\`\`
|
|
2513
1575
|
\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198
|
|
2514
1576
|
\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198
|
|
2515
1577
|
\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198
|
|
2516
1578
|
\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198
|
|
2517
|
-
\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198
|
|
2518
1579
|
\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198
|
|
2519
1580
|
\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198
|
|
2520
1581
|
\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198
|
|
@@ -2532,7 +1593,6 @@ function generateWelcomeMessage(data, stats) {
|
|
|
2532
1593
|
\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198
|
|
2533
1594
|
\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198
|
|
2534
1595
|
\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198
|
|
2535
|
-
\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198
|
|
2536
1596
|
\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198
|
|
2537
1597
|
\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198
|
|
2538
1598
|
\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198\u2198
|
|
@@ -2541,8 +1601,6 @@ function generateWelcomeMessage(data, stats) {
|
|
|
2541
1601
|
return `${asciiArt}
|
|
2542
1602
|
# Welcome to ${data.meta.name}
|
|
2543
1603
|
|
|
2544
|
-
Your design system is connected and ready to use.
|
|
2545
|
-
|
|
2546
1604
|
## Design System Overview
|
|
2547
1605
|
|
|
2548
1606
|
| Metric | Value |
|
|
@@ -2554,57 +1612,39 @@ Your design system is connected and ready to use.
|
|
|
2554
1612
|
| Governance Rules | ${stats.governanceRules} |
|
|
2555
1613
|
| Version | ${data.meta.version || 1} |
|
|
2556
1614
|
|
|
2557
|
-
### Token
|
|
2558
|
-
|
|
2559
|
-
${categoryBreakdown}
|
|
1615
|
+
### Token breakdown
|
|
2560
1616
|
|
|
2561
|
-
|
|
1617
|
+
${tokenSummary}
|
|
2562
1618
|
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
## Quick Start
|
|
2566
|
-
|
|
2567
|
-
### Get a Token
|
|
2568
|
-
\`\`\`
|
|
2569
|
-
Use getToken with path "colors.static.brand.primary"
|
|
2570
|
-
\`\`\`
|
|
2571
|
-
|
|
2572
|
-
### List All Spacing Tokens
|
|
2573
|
-
\`\`\`
|
|
2574
|
-
Use listTokens with category "spacing"
|
|
2575
|
-
\`\`\`
|
|
2576
|
-
|
|
2577
|
-
### Validate a Hardcoded Value
|
|
2578
|
-
\`\`\`
|
|
2579
|
-
Use validateUsage with value "#ff0000" and context "color"
|
|
2580
|
-
\`\`\`
|
|
2581
|
-
|
|
2582
|
-
## Resources Available
|
|
1619
|
+
---
|
|
2583
1620
|
|
|
2584
|
-
|
|
1621
|
+
## Essential commands
|
|
2585
1622
|
|
|
2586
|
-
|
|
|
2587
|
-
|
|
2588
|
-
|
|
|
2589
|
-
|
|
|
2590
|
-
|
|
|
2591
|
-
| \`atomix://rules/windsurf\` | .windsurfrules file content |
|
|
2592
|
-
| \`atomix://rules/cline\` | .clinerules file content |
|
|
2593
|
-
| \`atomix://rules/continue\` | Continue rules |
|
|
2594
|
-
| \`atomix://rules/zed\` | Zed assistant rules |
|
|
2595
|
-
| \`atomix://rules/generic\` | Generic AI guidelines |
|
|
1623
|
+
| Command | What to expect |
|
|
1624
|
+
|---------|----------------|
|
|
1625
|
+
| **install** | Scans your repo to set up global styles (if none), icons and fonts. Safe: Won't install until you confirm. |
|
|
1626
|
+
| **sync** | Syncs tokens to a local file. Adds new, updates existing, marks deprecated. Safe: No changes until you confirm. |
|
|
1627
|
+
| **refactor** | Scans codebase for deprecated tokens (after sync), suggests replacements. Run after sync to migrate code. |
|
|
2596
1628
|
|
|
2597
|
-
##
|
|
1629
|
+
## Helper commands
|
|
2598
1630
|
|
|
2599
|
-
|
|
1631
|
+
| Command | What to expect |
|
|
1632
|
+
|---------|----------------|
|
|
1633
|
+
| **design-system-rules** | Returns governance rules for your AI tools. |
|
|
1634
|
+
| **spacing** | Table of all spacing tokens and values. |
|
|
1635
|
+
| **color** | Table of color tokens with light/dark values. |
|
|
1636
|
+
| **typography** | Table of typography tokens and values. |
|
|
1637
|
+
| **radius** | Table of border radius tokens. |
|
|
1638
|
+
| **shadow** | Table of shadow/elevation tokens. |
|
|
1639
|
+
| **border** | Table of border width tokens. |
|
|
1640
|
+
| **sizing** | Table of height and icon size tokens. |
|
|
1641
|
+
| **motion** | Table of duration and easing tokens. |
|
|
2600
1642
|
|
|
2601
|
-
|
|
2602
|
-
2. **Refresh tokens**: Restart the MCP server to fetch latest tokens
|
|
2603
|
-
3. **View changes**: Visit https://atomixstudio.eu/ds/${dsId} to see recent changes
|
|
1643
|
+
**Suggested next step:** Run **install** to set up global styles, icons, fonts, and token files; the AI will list options and ask before adding anything.
|
|
2604
1644
|
|
|
2605
1645
|
---
|
|
2606
1646
|
|
|
2607
|
-
*
|
|
1647
|
+
*Atomix Studio \u2014 https://atomixstudio.eu | DS: https://atomixstudio.eu/ds/${dsId}*
|
|
2608
1648
|
`;
|
|
2609
1649
|
}
|
|
2610
1650
|
async function startServer() {
|