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.
- package/README.md +12 -5
- package/build/index.js +3 -1
- package/build/lib/__tests__/agents-md-template.unit.test.js +2 -0
- package/build/lib/__tests__/memory-config.unit.test.js +9 -0
- package/build/lib/__tests__/memory-injection.unit.test.d.ts +1 -0
- package/build/lib/__tests__/memory-injection.unit.test.js +51 -0
- package/build/lib/__tests__/memory-orchestration.unit.test.d.ts +1 -0
- package/build/lib/__tests__/memory-orchestration.unit.test.js +84 -0
- package/build/lib/__tests__/memory-payload.unit.test.d.ts +1 -0
- package/build/lib/__tests__/memory-payload.unit.test.js +35 -0
- package/build/lib/agents-md-template.js +7 -5
- package/build/lib/memory-client.d.ts +8 -1
- package/build/lib/memory-client.js +53 -44
- package/build/lib/memory-config.d.ts +8 -0
- package/build/lib/memory-config.js +19 -0
- package/build/lib/memory-orchestration.d.ts +7 -2
- package/build/lib/memory-orchestration.js +81 -8
- package/build/lib/memory-payload.d.ts +21 -0
- package/build/lib/memory-payload.js +65 -0
- package/build/lib/shadcn-ui.d.ts +11 -0
- package/build/lib/shadcn-ui.js +78 -0
- package/build/resources/ui-ux-data/guidelines/vercel-web-interface.json +1632 -0
- package/build/resources/ui-ux-data/metadata.json +27 -3
- package/build/resources/ui-ux-data/shadcn/blocks.json +2541 -0
- package/build/resources/ui-ux-data/shadcn/components.json +997 -0
- package/build/resources/ui-ux-data/themes/presets.json +483 -0
- package/build/schemas/index.d.ts +38 -9
- package/build/schemas/memory-tools.d.ts +38 -9
- package/build/schemas/memory-tools.js +24 -9
- package/build/schemas/output/ui-ux-tools.d.ts +16 -0
- package/build/schemas/output/ui-ux-tools.js +4 -0
- package/build/schemas/ui-ux-schemas.js +3 -3
- package/build/tools/__tests__/start_ui.property.test.js +4 -3
- package/build/tools/index.d.ts +1 -0
- package/build/tools/index.js +1 -0
- package/build/tools/memorize_asset.js +12 -0
- package/build/tools/scan_and_extract_patterns.js +7 -7
- package/build/tools/search_memory.d.ts +7 -0
- package/build/tools/search_memory.js +57 -0
- package/build/tools/start_bugfix.js +3 -3
- package/build/tools/start_feature.js +3 -3
- package/build/tools/start_ui.js +33 -6
- package/build/tools/ui-ux-tools.js +322 -244
- package/build/utils/__tests__/shadcn-sync.unit.test.d.ts +1 -0
- package/build/utils/__tests__/shadcn-sync.unit.test.js +49 -0
- package/build/utils/__tests__/theme-pick.unit.test.d.ts +1 -0
- package/build/utils/__tests__/theme-pick.unit.test.js +9 -0
- package/build/utils/__tests__/themes-sync.unit.test.d.ts +1 -0
- package/build/utils/__tests__/themes-sync.unit.test.js +21 -0
- package/build/utils/__tests__/ui-metadata.unit.test.d.ts +1 -0
- package/build/utils/__tests__/ui-metadata.unit.test.js +35 -0
- package/build/utils/__tests__/vercel-guidelines-sync.unit.test.d.ts +1 -0
- package/build/utils/__tests__/vercel-guidelines-sync.unit.test.js +34 -0
- package/build/utils/bm25.d.ts +2 -1
- package/build/utils/bm25.js +17 -5
- package/build/utils/shadcn-sync.d.ts +55 -0
- package/build/utils/shadcn-sync.js +146 -0
- package/build/utils/themes-sync.d.ts +32 -0
- package/build/utils/themes-sync.js +201 -0
- package/build/utils/ui-data-loader.js +13 -2
- package/build/utils/ui-metadata.d.ts +27 -0
- package/build/utils/ui-metadata.js +39 -0
- package/build/utils/ui-search-engine.d.ts +1 -0
- package/build/utils/ui-search-engine.js +20 -6
- package/build/utils/ui-sync.d.ts +24 -2
- package/build/utils/ui-sync.js +152 -86
- package/build/utils/vercel-guidelines-sync.d.ts +30 -0
- package/build/utils/vercel-guidelines-sync.js +133 -0
- package/docs/data/tools.js +18 -0
- package/docs/i18n/all-tools/en.json +6 -1
- package/docs/i18n/all-tools/ja.json +2 -1
- package/docs/i18n/all-tools/ko.json +2 -1
- package/docs/i18n/all-tools/zh-CN.json +7 -2
- package/docs/i18n/en.json +5 -5
- package/docs/i18n/ja.json +2 -2
- package/docs/i18n/ko.json +2 -2
- package/docs/i18n/zh-CN.json +7 -7
- package/docs/memory-local-setup.md +1 -1
- package/docs/memory-local-setup.zh-CN.md +5 -2
- package/docs/pages/getting-started.html +3 -2
- 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
|
-
//
|
|
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
|
+
}
|
|
@@ -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
|
-
|
|
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:
|
|
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
|
*/
|
package/build/utils/ui-sync.d.ts
CHANGED
|
@@ -1,16 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* UI/UX 数据同步工具
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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<
|
|
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
|
*/
|
package/build/utils/ui-sync.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* UI/UX 数据同步工具
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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)
|
|
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
|
|
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
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
272
|
-
|
|
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
|
|
275
|
-
await
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
console.log('Extracting data files...');
|
|
341
|
+
else if (verbose) {
|
|
342
|
+
console.log('✓ shadcn registry unchanged');
|
|
282
343
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
console.log('Processing data files...');
|
|
355
|
+
else if (verbose) {
|
|
356
|
+
console.log('✓ UI themes unchanged');
|
|
291
357
|
}
|
|
292
|
-
await
|
|
358
|
+
await emitProgress(options, 92, 'Syncing Vercel Web Interface Guidelines');
|
|
359
|
+
const vercelResult = await syncVercelGuidelinesTo(outputDir, {
|
|
293
360
|
signal: options?.signal,
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
await emitProgress(options, normalized, message);
|
|
297
|
-
},
|
|
361
|
+
force,
|
|
362
|
+
existingChecksum: updateCheck.vercel.current,
|
|
298
363
|
});
|
|
299
|
-
if (
|
|
300
|
-
|
|
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:
|
|
374
|
+
version: sources['uipro-cli']?.version || updateCheck.uipro.latest,
|
|
305
375
|
syncedAt: new Date().toISOString(),
|
|
306
|
-
source:
|
|
376
|
+
source: 'uipro-cli',
|
|
307
377
|
format: 'json',
|
|
378
|
+
sources,
|
|
308
379
|
};
|
|
309
|
-
|
|
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('
|
|
319
|
-
console.log(
|
|
320
|
-
console.log(`
|
|
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
|
-
|
|
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
|
-
|
|
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>;
|