mcp-probe-kit 3.0.19 → 3.0.22

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.
Files changed (81) hide show
  1. package/README.md +12 -5
  2. package/build/index.js +3 -1
  3. package/build/lib/__tests__/agents-md-template.unit.test.js +2 -0
  4. package/build/lib/__tests__/memory-config.unit.test.js +9 -0
  5. package/build/lib/__tests__/memory-injection.unit.test.d.ts +1 -0
  6. package/build/lib/__tests__/memory-injection.unit.test.js +51 -0
  7. package/build/lib/__tests__/memory-orchestration.unit.test.d.ts +1 -0
  8. package/build/lib/__tests__/memory-orchestration.unit.test.js +84 -0
  9. package/build/lib/__tests__/memory-payload.unit.test.d.ts +1 -0
  10. package/build/lib/__tests__/memory-payload.unit.test.js +35 -0
  11. package/build/lib/agents-md-template.js +7 -5
  12. package/build/lib/memory-client.d.ts +8 -1
  13. package/build/lib/memory-client.js +53 -44
  14. package/build/lib/memory-config.d.ts +8 -0
  15. package/build/lib/memory-config.js +19 -0
  16. package/build/lib/memory-orchestration.d.ts +7 -2
  17. package/build/lib/memory-orchestration.js +81 -8
  18. package/build/lib/memory-payload.d.ts +21 -0
  19. package/build/lib/memory-payload.js +65 -0
  20. package/build/lib/shadcn-ui.d.ts +11 -0
  21. package/build/lib/shadcn-ui.js +78 -0
  22. package/build/resources/ui-ux-data/guidelines/vercel-web-interface.json +1632 -0
  23. package/build/resources/ui-ux-data/metadata.json +27 -3
  24. package/build/resources/ui-ux-data/shadcn/blocks.json +2541 -0
  25. package/build/resources/ui-ux-data/shadcn/components.json +997 -0
  26. package/build/resources/ui-ux-data/themes/presets.json +483 -0
  27. package/build/schemas/index.d.ts +38 -9
  28. package/build/schemas/memory-tools.d.ts +38 -9
  29. package/build/schemas/memory-tools.js +24 -9
  30. package/build/schemas/output/ui-ux-tools.d.ts +16 -0
  31. package/build/schemas/output/ui-ux-tools.js +4 -0
  32. package/build/schemas/ui-ux-schemas.js +3 -3
  33. package/build/tools/__tests__/start_ui.property.test.js +4 -3
  34. package/build/tools/index.d.ts +1 -0
  35. package/build/tools/index.js +1 -0
  36. package/build/tools/memorize_asset.js +12 -0
  37. package/build/tools/scan_and_extract_patterns.js +7 -7
  38. package/build/tools/search_memory.d.ts +7 -0
  39. package/build/tools/search_memory.js +57 -0
  40. package/build/tools/start_bugfix.js +3 -3
  41. package/build/tools/start_feature.js +3 -3
  42. package/build/tools/start_ui.js +33 -6
  43. package/build/tools/ui-ux-tools.js +322 -244
  44. package/build/utils/__tests__/shadcn-sync.unit.test.d.ts +1 -0
  45. package/build/utils/__tests__/shadcn-sync.unit.test.js +49 -0
  46. package/build/utils/__tests__/theme-pick.unit.test.d.ts +1 -0
  47. package/build/utils/__tests__/theme-pick.unit.test.js +9 -0
  48. package/build/utils/__tests__/themes-sync.unit.test.d.ts +1 -0
  49. package/build/utils/__tests__/themes-sync.unit.test.js +21 -0
  50. package/build/utils/__tests__/ui-metadata.unit.test.d.ts +1 -0
  51. package/build/utils/__tests__/ui-metadata.unit.test.js +35 -0
  52. package/build/utils/__tests__/vercel-guidelines-sync.unit.test.d.ts +1 -0
  53. package/build/utils/__tests__/vercel-guidelines-sync.unit.test.js +34 -0
  54. package/build/utils/bm25.d.ts +2 -1
  55. package/build/utils/bm25.js +17 -5
  56. package/build/utils/shadcn-sync.d.ts +55 -0
  57. package/build/utils/shadcn-sync.js +146 -0
  58. package/build/utils/themes-sync.d.ts +32 -0
  59. package/build/utils/themes-sync.js +201 -0
  60. package/build/utils/ui-data-loader.js +13 -2
  61. package/build/utils/ui-metadata.d.ts +27 -0
  62. package/build/utils/ui-metadata.js +39 -0
  63. package/build/utils/ui-search-engine.d.ts +1 -0
  64. package/build/utils/ui-search-engine.js +20 -6
  65. package/build/utils/ui-sync.d.ts +24 -2
  66. package/build/utils/ui-sync.js +152 -86
  67. package/build/utils/vercel-guidelines-sync.d.ts +30 -0
  68. package/build/utils/vercel-guidelines-sync.js +133 -0
  69. package/docs/data/tools.js +18 -0
  70. package/docs/i18n/all-tools/en.json +6 -1
  71. package/docs/i18n/all-tools/ja.json +2 -1
  72. package/docs/i18n/all-tools/ko.json +2 -1
  73. package/docs/i18n/all-tools/zh-CN.json +7 -2
  74. package/docs/i18n/en.json +5 -5
  75. package/docs/i18n/ja.json +2 -2
  76. package/docs/i18n/ko.json +2 -2
  77. package/docs/i18n/zh-CN.json +7 -7
  78. package/docs/memory-local-setup.md +1 -1
  79. package/docs/memory-local-setup.zh-CN.md +5 -2
  80. package/docs/pages/getting-started.html +3 -2
  81. package/package.json +2 -2
@@ -150,9 +150,20 @@ export class UIDataLoader {
150
150
  * 提取类别名称
151
151
  */
152
152
  extractCategory(filename) {
153
- // 移除扩展名
154
153
  let category = filename.replace(/\.json$/, '');
155
- // 处理子目录(如 stacks/react.json -> react)
154
+ // themes/presets.json -> ui-themes
155
+ if (category.startsWith('themes/')) {
156
+ return 'ui-themes';
157
+ }
158
+ // guidelines/vercel-web-interface.json -> ui-guidelines-vercel
159
+ if (category.startsWith('guidelines/')) {
160
+ return 'ui-guidelines-vercel';
161
+ }
162
+ // shadcn/blocks.json -> shadcn-blocks
163
+ if (category.startsWith('shadcn/')) {
164
+ return category.replace('/', '-');
165
+ }
166
+ // stacks/react.json -> react
156
167
  if (category.includes('/')) {
157
168
  const parts = category.split('/');
158
169
  category = parts[parts.length - 1];
@@ -0,0 +1,27 @@
1
+ /**
2
+ * UI/UX 多源同步元数据
3
+ */
4
+ export interface UISourceMetadata {
5
+ version: string;
6
+ syncedAt: string;
7
+ checksum?: string;
8
+ style?: string;
9
+ blocks?: number;
10
+ components?: number;
11
+ }
12
+ export interface UISyncMetadata {
13
+ /** @deprecated 兼容旧版:等同 uipro-cli version */
14
+ version: string;
15
+ syncedAt: string;
16
+ /** @deprecated 兼容旧版 */
17
+ source: string;
18
+ format: 'json';
19
+ sources: {
20
+ 'uipro-cli'?: UISourceMetadata;
21
+ shadcn?: UISourceMetadata;
22
+ themes?: UISourceMetadata;
23
+ vercel?: UISourceMetadata;
24
+ };
25
+ }
26
+ export declare function readUISyncMetadata(outputDir: string): UISyncMetadata | null;
27
+ export declare function writeUISyncMetadata(outputDir: string, metadata: UISyncMetadata): void;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * UI/UX 多源同步元数据
3
+ */
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ export function readUISyncMetadata(outputDir) {
7
+ const metadataPath = path.join(outputDir, 'metadata.json');
8
+ if (!fs.existsSync(metadataPath)) {
9
+ return null;
10
+ }
11
+ try {
12
+ const raw = JSON.parse(fs.readFileSync(metadataPath, 'utf-8'));
13
+ if (!raw.sources) {
14
+ return {
15
+ version: raw.version || 'unknown',
16
+ syncedAt: raw.syncedAt || new Date(0).toISOString(),
17
+ source: raw.source || 'uipro-cli',
18
+ format: 'json',
19
+ sources: {
20
+ 'uipro-cli': {
21
+ version: raw.version || 'unknown',
22
+ syncedAt: raw.syncedAt || new Date(0).toISOString(),
23
+ },
24
+ },
25
+ };
26
+ }
27
+ return raw;
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ export function writeUISyncMetadata(outputDir, metadata) {
34
+ if (!fs.existsSync(outputDir)) {
35
+ fs.mkdirSync(outputDir, { recursive: true });
36
+ }
37
+ const metadataPath = path.join(outputDir, 'metadata.json');
38
+ fs.writeFileSync(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`, 'utf-8');
39
+ }
@@ -42,6 +42,7 @@ export declare class UISearchEngine {
42
42
  * 搜索 UI/UX 数据
43
43
  */
44
44
  search(query: string, options?: UISearchOptions): UISearchResult[];
45
+ private matchesStack;
45
46
  /**
46
47
  * 获取所有类别
47
48
  */
@@ -72,16 +72,13 @@ export class UISearchEngine {
72
72
  * 搜索 UI/UX 数据
73
73
  */
74
74
  search(query, options = {}) {
75
- const { category, limit = 10, minScore = 0, } = options;
76
- // 执行 BM25 搜索
77
- const bm25Results = this.bm25.search(query, limit * 2);
78
- // 转换结果
75
+ const { category, stack, limit = 10, minScore = 0, } = options;
76
+ const bm25Results = this.bm25.search(query, limit * 3, category ? (metadata) => metadata?.category === category : undefined);
79
77
  const results = [];
80
78
  for (const result of bm25Results) {
81
79
  if (result.score < minScore)
82
80
  continue;
83
81
  const { category: resultCategory, index } = result.metadata;
84
- // 过滤类别
85
82
  if (category && resultCategory !== category) {
86
83
  continue;
87
84
  }
@@ -89,11 +86,15 @@ export class UISearchEngine {
89
86
  if (!dataset || index >= dataset.length) {
90
87
  continue;
91
88
  }
89
+ const item = dataset[index];
90
+ if (stack && !this.matchesStack(item, stack)) {
91
+ continue;
92
+ }
92
93
  results.push({
93
94
  id: result.id,
94
95
  score: result.score,
95
96
  category: resultCategory,
96
- data: dataset[index],
97
+ data: item,
97
98
  });
98
99
  if (results.length >= limit) {
99
100
  break;
@@ -101,6 +102,19 @@ export class UISearchEngine {
101
102
  }
102
103
  return results;
103
104
  }
105
+ matchesStack(item, stack) {
106
+ const normalized = stack.toLowerCase();
107
+ const itemStack = String(item.stack || '').toLowerCase();
108
+ if (itemStack && itemStack !== normalized) {
109
+ if (!(normalized.includes('next') && itemStack === 'react')) {
110
+ return false;
111
+ }
112
+ }
113
+ if (normalized.includes('shadcn') || normalized.includes('react') || normalized.includes('next')) {
114
+ return item.category?.startsWith('shadcn-') || itemStack === 'react' || !itemStack;
115
+ }
116
+ return true;
117
+ }
104
118
  /**
105
119
  * 获取所有类别
106
120
  */
@@ -1,16 +1,38 @@
1
1
  /**
2
2
  * UI/UX 数据同步工具
3
3
  *
4
- * 从 npm 包 uipro-cli 同步数据到缓存目录
4
+ * 多源同步:
5
+ * - uipro-cli(设计词典)
6
+ * - shadcn/ui registry(blocks + components 索引)
7
+ * - ui-themes(shadcn 兼容 CSS 变量主题预设)
8
+ * - Vercel Web Interface Guidelines(可搜索规范条文)
5
9
  */
10
+ import { type UISyncMetadata } from './ui-metadata.js';
6
11
  export interface SyncRuntimeOptions {
7
12
  signal?: AbortSignal;
8
13
  onProgress?: (progress: number, message: string) => Promise<void> | void;
14
+ force?: boolean;
9
15
  }
16
+ export interface UISourceUpdateStatus {
17
+ current?: string;
18
+ latest: string;
19
+ upToDate: boolean;
20
+ }
21
+ export interface UIUpdateCheckResult {
22
+ hasUpdate: boolean;
23
+ uipro: UISourceUpdateStatus;
24
+ shadcn: UISourceUpdateStatus;
25
+ themes: UISourceUpdateStatus;
26
+ vercel: UISourceUpdateStatus;
27
+ }
28
+ export declare function checkUISourcesUpdate(outputDir: string, signal?: AbortSignal): Promise<UIUpdateCheckResult>;
10
29
  /**
11
30
  * 同步 UI/UX 数据到指定目录(通用函数)
12
31
  */
13
- export declare function syncUIDataTo(outputDir: string, verbose?: boolean, options?: SyncRuntimeOptions): Promise<void>;
32
+ export declare function syncUIDataTo(outputDir: string, verbose?: boolean, options?: SyncRuntimeOptions): Promise<{
33
+ skipped: boolean;
34
+ metadata: UISyncMetadata;
35
+ }>;
14
36
  /**
15
37
  * 同步 UI/UX 数据到缓存
16
38
  */
@@ -1,7 +1,11 @@
1
1
  /**
2
2
  * UI/UX 数据同步工具
3
3
  *
4
- * 从 npm 包 uipro-cli 同步数据到缓存目录
4
+ * 多源同步:
5
+ * - uipro-cli(设计词典)
6
+ * - shadcn/ui registry(blocks + components 索引)
7
+ * - ui-themes(shadcn 兼容 CSS 变量主题预设)
8
+ * - Vercel Web Interface Guidelines(可搜索规范条文)
5
9
  */
6
10
  import * as fs from 'fs';
7
11
  import * as path from 'path';
@@ -10,6 +14,10 @@ import * as https from 'https';
10
14
  import * as tar from 'tar';
11
15
  import { createWriteStream } from 'fs';
12
16
  import { parse as parseCSV } from 'csv-parse/sync';
17
+ import { readUISyncMetadata, writeUISyncMetadata } from './ui-metadata.js';
18
+ import { computeRegistryChecksum, fetchShadcnRegistry, syncShadcnTo, } from './shadcn-sync.js';
19
+ import { computeThemesChecksum, syncThemesTo } from './themes-sync.js';
20
+ import { getVercelGuidelinesChecksum, syncVercelGuidelinesTo } from './vercel-guidelines-sync.js';
13
21
  function throwIfAborted(signal, message) {
14
22
  if (!signal?.aborted) {
15
23
  return;
@@ -24,9 +32,6 @@ async function emitProgress(options, progress, message) {
24
32
  }
25
33
  await options.onProgress(progress, message);
26
34
  }
27
- /**
28
- * 获取 npm 包的最新版本号
29
- */
30
35
  async function getLatestVersion(packageName, signal) {
31
36
  throwIfAborted(signal, 'Sync cancelled before fetching latest version');
32
37
  return new Promise((resolve, reject) => {
@@ -68,9 +73,6 @@ async function getLatestVersion(packageName, signal) {
68
73
  });
69
74
  });
70
75
  }
71
- /**
72
- * 下载文件
73
- */
74
76
  async function downloadFile(url, outputPath, signal) {
75
77
  throwIfAborted(signal, 'Sync cancelled before download');
76
78
  return new Promise((resolve, reject) => {
@@ -122,9 +124,6 @@ async function downloadFile(url, outputPath, signal) {
122
124
  });
123
125
  });
124
126
  }
125
- /**
126
- * 解压 tarball 并提取指定目录
127
- */
128
127
  async function extractTarball(tarballPath, extractPath, targetDir, signal) {
129
128
  throwIfAborted(signal, 'Sync cancelled before extract');
130
129
  const tempDir = path.join(extractPath, '.temp');
@@ -142,9 +141,6 @@ async function extractTarball(tarballPath, extractPath, targetDir, signal) {
142
141
  }
143
142
  return sourceDir;
144
143
  }
145
- /**
146
- * 转换 CSV 到 JSON
147
- */
148
144
  function convertCSVToJSON(csvContent, filename) {
149
145
  try {
150
146
  const records = parseCSV(csvContent, {
@@ -164,9 +160,6 @@ function convertCSVToJSON(csvContent, filename) {
164
160
  return [];
165
161
  }
166
162
  }
167
- /**
168
- * 收集目录下所有 CSV 文件(递归)
169
- */
170
163
  function collectCsvFiles(rootDir) {
171
164
  const files = [];
172
165
  const entries = fs.readdirSync(rootDir, { withFileTypes: true });
@@ -182,9 +175,6 @@ function collectCsvFiles(rootDir) {
182
175
  }
183
176
  return files;
184
177
  }
185
- /**
186
- * 处理数据文件
187
- */
188
178
  async function processDataFiles(sourceDir, outputDir, verbose, options) {
189
179
  throwIfAborted(options?.signal, 'Sync cancelled before processing files');
190
180
  if (!fs.existsSync(outputDir)) {
@@ -218,7 +208,7 @@ async function processDataFiles(sourceDir, outputDir, verbose, options) {
218
208
  fs.mkdirSync(outputSubDir, { recursive: true });
219
209
  }
220
210
  const outputPath = path.join(outputSubDir, outputFile);
221
- fs.writeFileSync(outputPath, JSON.stringify(jsonData, null, 2), 'utf-8');
211
+ fs.writeFileSync(outputPath, `${JSON.stringify(jsonData, null, 2)}\n`, 'utf-8');
222
212
  if (verbose) {
223
213
  console.log(` → ${outputFile} (${jsonData.length} records)`);
224
214
  }
@@ -226,104 +216,181 @@ async function processDataFiles(sourceDir, outputDir, verbose, options) {
226
216
  await emitProgress(options, fileProgress, `Processed ${index + 1}/${csvFiles.length}: ${relativePath}`);
227
217
  }
228
218
  }
229
- /**
230
- * 写入元数据
231
- */
232
- function writeMetadata(outputDir, metadata) {
233
- const metadataPath = path.join(outputDir, 'metadata.json');
234
- fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), 'utf-8');
235
- }
236
- /**
237
- * 清理临时文件
238
- */
239
219
  function cleanup(tempDir) {
240
220
  if (fs.existsSync(tempDir)) {
241
221
  fs.rmSync(tempDir, { recursive: true, force: true });
242
222
  }
243
223
  }
224
+ export async function checkUISourcesUpdate(outputDir, signal) {
225
+ const existing = readUISyncMetadata(outputDir);
226
+ const latestUipro = await getLatestVersion('uipro-cli', signal);
227
+ const registry = await fetchShadcnRegistry(signal);
228
+ const latestShadcn = computeRegistryChecksum(registry);
229
+ const uiproCurrent = existing?.sources['uipro-cli']?.version || existing?.version;
230
+ const shadcnCurrent = existing?.sources.shadcn?.checksum || existing?.sources.shadcn?.version;
231
+ const themesCurrent = existing?.sources.themes?.checksum || existing?.sources.themes?.version;
232
+ const vercelCurrent = existing?.sources.vercel?.checksum || existing?.sources.vercel?.version;
233
+ const latestThemes = computeThemesChecksum();
234
+ const latestVercel = await getVercelGuidelinesChecksum(signal);
235
+ const uiproUpToDate = uiproCurrent === latestUipro;
236
+ const shadcnUpToDate = shadcnCurrent === latestShadcn;
237
+ const themesUpToDate = themesCurrent === latestThemes;
238
+ const vercelUpToDate = vercelCurrent === latestVercel;
239
+ return {
240
+ hasUpdate: !uiproUpToDate || !shadcnUpToDate || !themesUpToDate || !vercelUpToDate,
241
+ uipro: {
242
+ current: uiproCurrent,
243
+ latest: latestUipro,
244
+ upToDate: uiproUpToDate,
245
+ },
246
+ shadcn: {
247
+ current: shadcnCurrent,
248
+ latest: latestShadcn,
249
+ upToDate: shadcnUpToDate,
250
+ },
251
+ themes: {
252
+ current: themesCurrent,
253
+ latest: latestThemes,
254
+ upToDate: themesUpToDate,
255
+ },
256
+ vercel: {
257
+ current: vercelCurrent,
258
+ latest: latestVercel,
259
+ upToDate: vercelUpToDate,
260
+ },
261
+ };
262
+ }
263
+ async function syncUiproPackage(outputDir, latestVersion, verbose, options) {
264
+ const packageName = 'uipro-cli';
265
+ const tarballUrl = `https://registry.npmjs.org/${packageName}/-/${packageName}-${latestVersion}.tgz`;
266
+ const tempDir = path.join(os.tmpdir(), '.mcp-ui-sync');
267
+ const tarballPath = path.join(tempDir, 'package.tgz');
268
+ if (!fs.existsSync(tempDir)) {
269
+ fs.mkdirSync(tempDir, { recursive: true });
270
+ }
271
+ if (verbose) {
272
+ console.log('Downloading uipro-cli tarball...');
273
+ }
274
+ await downloadFile(tarballUrl, tarballPath, options?.signal);
275
+ await emitProgress(options, 40, 'Downloaded uipro-cli tarball');
276
+ const extractedDataDir = await extractTarball(tarballPath, tempDir, 'package/assets/data', options?.signal);
277
+ await emitProgress(options, 50, 'Extracted uipro-cli data');
278
+ await processDataFiles(extractedDataDir, outputDir, verbose, {
279
+ signal: options?.signal,
280
+ onProgress: async (progress, message) => {
281
+ const normalized = 50 + Math.round(progress * 0.25);
282
+ await emitProgress(options, normalized, message);
283
+ },
284
+ });
285
+ cleanup(tempDir);
286
+ return {
287
+ version: latestVersion,
288
+ syncedAt: new Date().toISOString(),
289
+ };
290
+ }
244
291
  /**
245
292
  * 同步 UI/UX 数据到指定目录(通用函数)
246
293
  */
247
294
  export async function syncUIDataTo(outputDir, verbose = false, options) {
248
- const packageName = 'uipro-cli';
295
+ const force = options?.force ?? false;
249
296
  throwIfAborted(options?.signal, 'Sync cancelled before start');
250
297
  await emitProgress(options, 5, 'Initializing sync');
251
298
  if (verbose) {
252
299
  console.log('🚀 Starting UI/UX data sync...\n');
253
300
  }
254
- try {
255
- // 1. 获取最新版本
256
- if (verbose) {
257
- console.log(`Fetching latest version of ${packageName}...`);
258
- }
259
- const latestVersion = await getLatestVersion(packageName, options?.signal);
260
- await emitProgress(options, 15, `Fetched latest version: ${latestVersion}`);
301
+ const existing = readUISyncMetadata(outputDir);
302
+ const updateCheck = await checkUISourcesUpdate(outputDir, options?.signal);
303
+ await emitProgress(options, 15, 'Checked upstream versions');
304
+ if (!force && !updateCheck.hasUpdate && existing) {
305
+ await emitProgress(options, 100, 'All UI sources up to date');
261
306
  if (verbose) {
262
- console.log(`✓ Latest version: ${latestVersion}\n`);
263
- }
264
- // 2. 下载 tarball
265
- const tarballUrl = `https://registry.npmjs.org/${packageName}/-/${packageName}-${latestVersion}.tgz`;
266
- const tempDir = path.join(os.tmpdir(), '.mcp-ui-sync');
267
- const tarballPath = path.join(tempDir, 'package.tgz');
268
- if (!fs.existsSync(tempDir)) {
269
- fs.mkdirSync(tempDir, { recursive: true });
307
+ console.log('✅ All UI sources up to date, skipping sync.');
308
+ console.log(` uipro-cli: ${updateCheck.uipro.latest}`);
309
+ console.log(` shadcn: ${updateCheck.shadcn.latest}`);
310
+ console.log(` themes: ${updateCheck.themes.latest}`);
311
+ console.log(` vercel: ${updateCheck.vercel.latest}`);
270
312
  }
271
- if (verbose) {
272
- console.log(`Downloading tarball...`);
313
+ return { skipped: true, metadata: existing };
314
+ }
315
+ const sources = {
316
+ ...(existing?.sources || {}),
317
+ };
318
+ try {
319
+ if (force || !updateCheck.uipro.upToDate) {
320
+ if (verbose) {
321
+ console.log(`Syncing uipro-cli ${updateCheck.uipro.latest}...`);
322
+ }
323
+ const uiproMeta = await syncUiproPackage(outputDir, updateCheck.uipro.latest, verbose, options);
324
+ sources['uipro-cli'] = {
325
+ version: uiproMeta.version,
326
+ syncedAt: uiproMeta.syncedAt,
327
+ };
273
328
  }
274
- await downloadFile(tarballUrl, tarballPath, options?.signal);
275
- await emitProgress(options, 35, 'Downloaded package tarball');
276
- if (verbose) {
277
- console.log('✓ Downloaded tarball\n');
329
+ await emitProgress(options, 78, 'Syncing shadcn/ui registry');
330
+ const shadcnResult = await syncShadcnTo(outputDir, {
331
+ signal: options?.signal,
332
+ force,
333
+ existingChecksum: updateCheck.shadcn.current,
334
+ });
335
+ if (shadcnResult) {
336
+ sources.shadcn = shadcnResult.metadata;
337
+ if (verbose) {
338
+ console.log(`✓ shadcn registry synced (${shadcnResult.blocks} blocks, ${shadcnResult.components} components)`);
339
+ }
278
340
  }
279
- // 3. 解压并提取数据
280
- if (verbose) {
281
- console.log('Extracting data files...');
341
+ else if (verbose) {
342
+ console.log('✓ shadcn registry unchanged');
282
343
  }
283
- const extractedDataDir = await extractTarball(tarballPath, tempDir, 'package/assets/data', options?.signal);
284
- await emitProgress(options, 50, 'Extracted package data files');
285
- if (verbose) {
286
- console.log('✓ Extracted data files\n');
344
+ await emitProgress(options, 85, 'Syncing UI theme presets');
345
+ const themesResult = syncThemesTo(outputDir, {
346
+ force,
347
+ existingChecksum: updateCheck.themes.current,
348
+ });
349
+ if (themesResult) {
350
+ sources.themes = themesResult.metadata;
351
+ if (verbose) {
352
+ console.log(`✓ UI themes synced (${themesResult.count} presets)`);
353
+ }
287
354
  }
288
- // 4. 处理数据文件
289
- if (verbose) {
290
- console.log('Processing data files...');
355
+ else if (verbose) {
356
+ console.log('✓ UI themes unchanged');
291
357
  }
292
- await processDataFiles(extractedDataDir, outputDir, verbose, {
358
+ await emitProgress(options, 92, 'Syncing Vercel Web Interface Guidelines');
359
+ const vercelResult = await syncVercelGuidelinesTo(outputDir, {
293
360
  signal: options?.signal,
294
- onProgress: async (progress, message) => {
295
- const normalized = 50 + Math.round(progress * 0.35);
296
- await emitProgress(options, normalized, message);
297
- },
361
+ force,
362
+ existingChecksum: updateCheck.vercel.current,
298
363
  });
299
- if (verbose) {
300
- console.log('✓ Processed all data files\n');
364
+ if (vercelResult) {
365
+ sources.vercel = vercelResult.metadata;
366
+ if (verbose) {
367
+ console.log(`✓ Vercel guidelines synced (${vercelResult.count} rules)`);
368
+ }
369
+ }
370
+ else if (verbose) {
371
+ console.log('✓ Vercel guidelines unchanged');
301
372
  }
302
- // 5. 写入元数据
303
373
  const metadata = {
304
- version: latestVersion,
374
+ version: sources['uipro-cli']?.version || updateCheck.uipro.latest,
305
375
  syncedAt: new Date().toISOString(),
306
- source: packageName,
376
+ source: 'uipro-cli',
307
377
  format: 'json',
378
+ sources,
308
379
  };
309
- writeMetadata(outputDir, metadata);
310
- await emitProgress(options, 90, 'Wrote metadata');
311
- if (verbose) {
312
- console.log('✓ Written metadata\n');
313
- }
314
- // 6. 清理临时文件
315
- cleanup(tempDir);
380
+ writeUISyncMetadata(outputDir, metadata);
316
381
  await emitProgress(options, 100, 'Sync completed');
317
382
  if (verbose) {
318
- console.log(' Cleaned up temporary files\n');
319
- console.log('✅ Sync completed successfully!');
320
- console.log(` Version: ${latestVersion}`);
383
+ console.log('\n✅ Sync completed successfully!');
384
+ console.log(` uipro-cli: ${metadata.sources['uipro-cli']?.version}`);
385
+ console.log(` shadcn: ${metadata.sources.shadcn?.blocks || 0} blocks, ${metadata.sources.shadcn?.components || 0} components`);
386
+ console.log(` themes: ${sources.themes?.version || 'n/a'} (${sources.themes?.checksum?.slice(0, 8) || '—'})`);
387
+ console.log(` vercel: ${sources.vercel?.checksum?.slice(0, 8) || 'n/a'} rules checksum`);
321
388
  console.log(` Output: ${outputDir}`);
322
389
  }
390
+ return { skipped: false, metadata };
323
391
  }
324
392
  catch (error) {
325
- const tempDir = path.join(os.tmpdir(), '.mcp-ui-sync');
326
- cleanup(tempDir);
393
+ cleanup(path.join(os.tmpdir(), '.mcp-ui-sync'));
327
394
  throw error;
328
395
  }
329
396
  }
@@ -332,6 +399,5 @@ export async function syncUIDataTo(outputDir, verbose = false, options) {
332
399
  */
333
400
  export async function syncUIDataToCache(force = false, verbose = false, options) {
334
401
  const cacheDir = path.join(os.homedir(), '.mcp-probe-kit', 'ui-ux-data');
335
- void force;
336
- await syncUIDataTo(cacheDir, verbose, options);
402
+ await syncUIDataTo(cacheDir, verbose, { ...options, force });
337
403
  }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Vercel Web Interface Guidelines → 可搜索 JSON
3
+ * @see https://github.com/vercel-labs/web-interface-guidelines
4
+ */
5
+ import type { UISourceMetadata } from './ui-metadata.js';
6
+ export declare const VERCEL_GUIDELINES_URL = "https://raw.githubusercontent.com/vercel-labs/web-interface-guidelines/main/AGENTS.md";
7
+ export declare const VERCEL_GUIDELINES_VERSION = "2026-04-06";
8
+ export interface VercelGuidelineRecord {
9
+ id: string;
10
+ title: string;
11
+ section: string;
12
+ subsection: string;
13
+ level: 'MUST' | 'SHOULD' | 'NEVER' | 'INFO';
14
+ rule: string;
15
+ description: string;
16
+ category: string;
17
+ source: string;
18
+ tags: string[];
19
+ }
20
+ export interface VercelGuidelinesSyncResult {
21
+ metadata: UISourceMetadata;
22
+ count: number;
23
+ }
24
+ export declare function parseVercelGuidelinesMarkdown(markdown: string): VercelGuidelineRecord[];
25
+ export declare function syncVercelGuidelinesTo(outputDir: string, options?: {
26
+ signal?: AbortSignal;
27
+ force?: boolean;
28
+ existingChecksum?: string;
29
+ }): Promise<VercelGuidelinesSyncResult | null>;
30
+ export declare function getVercelGuidelinesChecksum(signal?: AbortSignal): Promise<string>;