mcp-probe-kit 3.0.5 → 3.0.7
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 +458 -431
- package/build/index.js +875 -141
- package/build/lib/gitnexus-bridge.d.ts +58 -0
- package/build/lib/gitnexus-bridge.js +379 -0
- package/build/lib/template-loader.js +317 -317
- package/build/lib/tool-execution-context.d.ts +8 -0
- package/build/lib/tool-execution-context.js +20 -0
- package/build/lib/toolset-manager.d.ts +1 -1
- package/build/lib/toolset-manager.js +7 -5
- package/build/schemas/code-analysis-tools.d.ts +46 -0
- package/build/schemas/code-analysis-tools.js +47 -0
- package/build/schemas/git-tools.js +16 -16
- package/build/schemas/index.d.ts +46 -0
- package/build/tools/__tests__/code_insight.unit.test.d.ts +1 -0
- package/build/tools/__tests__/code_insight.unit.test.js +35 -0
- package/build/tools/__tests__/start_bugfix.unit.test.js +14 -14
- package/build/tools/__tests__/start_ui.unit.test.js +11 -11
- package/build/tools/add_feature.js +79 -79
- package/build/tools/ask_user.js +5 -5
- package/build/tools/code_insight.d.ts +8 -0
- package/build/tools/code_insight.js +129 -0
- package/build/tools/index.d.ts +1 -0
- package/build/tools/index.js +1 -0
- package/build/tools/interview.js +9 -9
- package/build/tools/start_bugfix.d.ts +2 -1
- package/build/tools/start_bugfix.js +170 -126
- package/build/tools/start_feature.d.ts +2 -1
- package/build/tools/start_feature.js +156 -112
- package/build/tools/start_onboard.d.ts +2 -1
- package/build/tools/start_onboard.js +57 -51
- package/build/tools/start_product.d.ts +2 -1
- package/build/tools/start_product.js +9 -1
- package/build/tools/start_ralph.d.ts +2 -1
- package/build/tools/start_ralph.js +9 -3
- package/build/tools/start_ui.d.ts +2 -1
- package/build/tools/start_ui.js +102 -88
- package/build/tools/ui-ux-tools.d.ts +2 -1
- package/build/tools/ui-ux-tools.js +19 -3
- package/build/utils/ui-sync.d.ts +6 -2
- package/build/utils/ui-sync.js +125 -29
- package/docs/assets/font/MaterialSymbolsOutlined.codepoints +4102 -0
- package/docs/assets/font/MaterialSymbolsOutlined.ttf +0 -0
- package/docs/assets/font/noto-sans-sc-400.ttf +0 -0
- package/docs/assets/font/noto-sans-sc-700.ttf +0 -0
- package/docs/assets/font/noto-sans-sc-900.ttf +0 -0
- package/docs/assets/js/i18n.js +122 -21
- package/docs/assets/js/tailwind.js +83 -83
- package/docs/data/tools.js +419 -399
- package/docs/debug-i18n.html +163 -0
- package/docs/i18n/all-tools/en.json +157 -0
- package/docs/i18n/all-tools/ja.json +157 -0
- package/docs/i18n/all-tools/ko.json +157 -0
- package/docs/i18n/all-tools/zh-CN.json +157 -0
- package/docs/pages/all-tools.html +514 -352
- package/docs/pages/examples.html +689 -689
- package/docs/pages/getting-started.html +589 -589
- package/docs/pages/migration.html +298 -298
- package/docs/specs/user-auth/design.md +82 -0
- package/docs/specs/user-auth/requirements.md +52 -0
- package/docs/specs/user-auth/tasks.md +55 -0
- package/package.json +5 -5
- package/docs/project-context/architecture.md +0 -0
- package/docs/project-context/how-to-develop.md +0 -313
- package/docs/project-context/how-to-test.md +0 -457
- package/docs/project-context/tech-stack.md +0 -96
- package/docs/project-context.md +0 -53
- package/docs/specs/git-work-report/design.md +0 -568
- package/docs/specs/git-work-report/requirements.md +0 -131
- package/docs/specs/git-work-report/tasks.md +0 -197
package/build/utils/ui-sync.js
CHANGED
|
@@ -10,19 +10,39 @@ import * as https from 'https';
|
|
|
10
10
|
import * as tar from 'tar';
|
|
11
11
|
import { createWriteStream } from 'fs';
|
|
12
12
|
import { parse as parseCSV } from 'csv-parse/sync';
|
|
13
|
+
function throwIfAborted(signal, message) {
|
|
14
|
+
if (!signal?.aborted) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const err = new Error(message);
|
|
18
|
+
err.name = 'AbortError';
|
|
19
|
+
throw err;
|
|
20
|
+
}
|
|
21
|
+
async function emitProgress(options, progress, message) {
|
|
22
|
+
if (!options?.onProgress) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
await options.onProgress(progress, message);
|
|
26
|
+
}
|
|
13
27
|
/**
|
|
14
28
|
* 获取 npm 包的最新版本号
|
|
15
29
|
*/
|
|
16
|
-
async function getLatestVersion(packageName) {
|
|
30
|
+
async function getLatestVersion(packageName, signal) {
|
|
31
|
+
throwIfAborted(signal, 'Sync cancelled before fetching latest version');
|
|
17
32
|
return new Promise((resolve, reject) => {
|
|
18
33
|
const url = `https://registry.npmjs.org/${packageName}/latest`;
|
|
19
|
-
https.get(url, (res) => {
|
|
34
|
+
const req = https.get(url, (res) => {
|
|
20
35
|
let data = '';
|
|
21
36
|
res.on('data', (chunk) => {
|
|
37
|
+
if (signal?.aborted) {
|
|
38
|
+
req.destroy(new Error('Sync cancelled while reading package metadata'));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
22
41
|
data += chunk;
|
|
23
42
|
});
|
|
24
43
|
res.on('end', () => {
|
|
25
44
|
try {
|
|
45
|
+
throwIfAborted(signal, 'Sync cancelled after reading package metadata');
|
|
26
46
|
const pkg = JSON.parse(data);
|
|
27
47
|
resolve(pkg.version);
|
|
28
48
|
}
|
|
@@ -30,20 +50,34 @@ async function getLatestVersion(packageName) {
|
|
|
30
50
|
reject(new Error(`Failed to parse package metadata: ${error}`));
|
|
31
51
|
}
|
|
32
52
|
});
|
|
33
|
-
})
|
|
53
|
+
});
|
|
54
|
+
const onAbort = () => req.destroy(new Error('Sync cancelled'));
|
|
55
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
56
|
+
req.on('error', (error) => {
|
|
57
|
+
signal?.removeEventListener('abort', onAbort);
|
|
58
|
+
if (signal?.aborted) {
|
|
59
|
+
const abortError = new Error('Sync cancelled');
|
|
60
|
+
abortError.name = 'AbortError';
|
|
61
|
+
reject(abortError);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
34
64
|
reject(new Error(`Failed to fetch package metadata: ${error}`));
|
|
35
65
|
});
|
|
66
|
+
req.on('close', () => {
|
|
67
|
+
signal?.removeEventListener('abort', onAbort);
|
|
68
|
+
});
|
|
36
69
|
});
|
|
37
70
|
}
|
|
38
71
|
/**
|
|
39
72
|
* 下载文件
|
|
40
73
|
*/
|
|
41
|
-
async function downloadFile(url, outputPath) {
|
|
74
|
+
async function downloadFile(url, outputPath, signal) {
|
|
75
|
+
throwIfAborted(signal, 'Sync cancelled before download');
|
|
42
76
|
return new Promise((resolve, reject) => {
|
|
43
|
-
https.get(url, (res) => {
|
|
77
|
+
const req = https.get(url, (res) => {
|
|
44
78
|
if (res.statusCode === 302 || res.statusCode === 301) {
|
|
45
79
|
if (res.headers.location) {
|
|
46
|
-
downloadFile(res.headers.location, outputPath).then(resolve).catch(reject);
|
|
80
|
+
downloadFile(res.headers.location, outputPath, signal).then(resolve).catch(reject);
|
|
47
81
|
return;
|
|
48
82
|
}
|
|
49
83
|
}
|
|
@@ -54,6 +88,7 @@ async function downloadFile(url, outputPath) {
|
|
|
54
88
|
const fileStream = createWriteStream(outputPath);
|
|
55
89
|
res.pipe(fileStream);
|
|
56
90
|
fileStream.on('finish', () => {
|
|
91
|
+
throwIfAborted(signal, 'Sync cancelled during download');
|
|
57
92
|
fileStream.close();
|
|
58
93
|
resolve();
|
|
59
94
|
});
|
|
@@ -61,15 +96,37 @@ async function downloadFile(url, outputPath) {
|
|
|
61
96
|
fs.unlinkSync(outputPath);
|
|
62
97
|
reject(error);
|
|
63
98
|
});
|
|
64
|
-
})
|
|
99
|
+
});
|
|
100
|
+
const onAbort = () => req.destroy(new Error('Sync cancelled'));
|
|
101
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
102
|
+
req.on('error', (error) => {
|
|
103
|
+
signal?.removeEventListener('abort', onAbort);
|
|
104
|
+
if (signal?.aborted) {
|
|
105
|
+
try {
|
|
106
|
+
if (fs.existsSync(outputPath)) {
|
|
107
|
+
fs.unlinkSync(outputPath);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// ignore cleanup error
|
|
112
|
+
}
|
|
113
|
+
const abortError = new Error('Sync cancelled');
|
|
114
|
+
abortError.name = 'AbortError';
|
|
115
|
+
reject(abortError);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
65
118
|
reject(error);
|
|
66
119
|
});
|
|
120
|
+
req.on('close', () => {
|
|
121
|
+
signal?.removeEventListener('abort', onAbort);
|
|
122
|
+
});
|
|
67
123
|
});
|
|
68
124
|
}
|
|
69
125
|
/**
|
|
70
126
|
* 解压 tarball 并提取指定目录
|
|
71
127
|
*/
|
|
72
|
-
async function extractTarball(tarballPath, extractPath, targetDir) {
|
|
128
|
+
async function extractTarball(tarballPath, extractPath, targetDir, signal) {
|
|
129
|
+
throwIfAborted(signal, 'Sync cancelled before extract');
|
|
73
130
|
const tempDir = path.join(extractPath, '.temp');
|
|
74
131
|
if (!fs.existsSync(tempDir)) {
|
|
75
132
|
fs.mkdirSync(tempDir, { recursive: true });
|
|
@@ -78,6 +135,7 @@ async function extractTarball(tarballPath, extractPath, targetDir) {
|
|
|
78
135
|
file: tarballPath,
|
|
79
136
|
cwd: tempDir,
|
|
80
137
|
});
|
|
138
|
+
throwIfAborted(signal, 'Sync cancelled after extract');
|
|
81
139
|
const sourceDir = path.join(tempDir, targetDir);
|
|
82
140
|
if (!fs.existsSync(sourceDir)) {
|
|
83
141
|
throw new Error(`Target directory not found in tarball: ${targetDir}`);
|
|
@@ -106,25 +164,41 @@ function convertCSVToJSON(csvContent, filename) {
|
|
|
106
164
|
return [];
|
|
107
165
|
}
|
|
108
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* 收集目录下所有 CSV 文件(递归)
|
|
169
|
+
*/
|
|
170
|
+
function collectCsvFiles(rootDir) {
|
|
171
|
+
const files = [];
|
|
172
|
+
const entries = fs.readdirSync(rootDir, { withFileTypes: true });
|
|
173
|
+
for (const entry of entries) {
|
|
174
|
+
const fullPath = path.join(rootDir, entry.name);
|
|
175
|
+
if (entry.isDirectory()) {
|
|
176
|
+
files.push(...collectCsvFiles(fullPath));
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (entry.isFile() && entry.name.endsWith('.csv')) {
|
|
180
|
+
files.push(fullPath);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return files;
|
|
184
|
+
}
|
|
109
185
|
/**
|
|
110
186
|
* 处理数据文件
|
|
111
187
|
*/
|
|
112
|
-
async function processDataFiles(sourceDir, outputDir, verbose) {
|
|
188
|
+
async function processDataFiles(sourceDir, outputDir, verbose, options) {
|
|
189
|
+
throwIfAborted(options?.signal, 'Sync cancelled before processing files');
|
|
113
190
|
if (!fs.existsSync(outputDir)) {
|
|
114
191
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
115
192
|
}
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (!file.endsWith('.csv')) {
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
193
|
+
const csvFiles = collectCsvFiles(sourceDir);
|
|
194
|
+
if (csvFiles.length === 0) {
|
|
195
|
+
await emitProgress(options, 100, 'No CSV files found');
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
for (let index = 0; index < csvFiles.length; index++) {
|
|
199
|
+
throwIfAborted(options?.signal, 'Sync cancelled while processing data files');
|
|
200
|
+
const sourcePath = csvFiles[index];
|
|
201
|
+
const file = path.basename(sourcePath);
|
|
128
202
|
if (verbose) {
|
|
129
203
|
console.log(`Processing: ${file}`);
|
|
130
204
|
}
|
|
@@ -137,11 +211,19 @@ async function processDataFiles(sourceDir, outputDir, verbose) {
|
|
|
137
211
|
continue;
|
|
138
212
|
}
|
|
139
213
|
const outputFile = file.replace('.csv', '.json');
|
|
140
|
-
const
|
|
214
|
+
const relativePath = path.relative(sourceDir, sourcePath);
|
|
215
|
+
const relativeDir = path.dirname(relativePath);
|
|
216
|
+
const outputSubDir = relativeDir === '.' ? outputDir : path.join(outputDir, relativeDir);
|
|
217
|
+
if (!fs.existsSync(outputSubDir)) {
|
|
218
|
+
fs.mkdirSync(outputSubDir, { recursive: true });
|
|
219
|
+
}
|
|
220
|
+
const outputPath = path.join(outputSubDir, outputFile);
|
|
141
221
|
fs.writeFileSync(outputPath, JSON.stringify(jsonData, null, 2), 'utf-8');
|
|
142
222
|
if (verbose) {
|
|
143
223
|
console.log(` → ${outputFile} (${jsonData.length} records)`);
|
|
144
224
|
}
|
|
225
|
+
const fileProgress = Math.round(((index + 1) / csvFiles.length) * 100);
|
|
226
|
+
await emitProgress(options, fileProgress, `Processed ${index + 1}/${csvFiles.length}: ${relativePath}`);
|
|
145
227
|
}
|
|
146
228
|
}
|
|
147
229
|
/**
|
|
@@ -162,8 +244,10 @@ function cleanup(tempDir) {
|
|
|
162
244
|
/**
|
|
163
245
|
* 同步 UI/UX 数据到指定目录(通用函数)
|
|
164
246
|
*/
|
|
165
|
-
export async function syncUIDataTo(outputDir, verbose = false) {
|
|
247
|
+
export async function syncUIDataTo(outputDir, verbose = false, options) {
|
|
166
248
|
const packageName = 'uipro-cli';
|
|
249
|
+
throwIfAborted(options?.signal, 'Sync cancelled before start');
|
|
250
|
+
await emitProgress(options, 5, 'Initializing sync');
|
|
167
251
|
if (verbose) {
|
|
168
252
|
console.log('🚀 Starting UI/UX data sync...\n');
|
|
169
253
|
}
|
|
@@ -172,7 +256,8 @@ export async function syncUIDataTo(outputDir, verbose = false) {
|
|
|
172
256
|
if (verbose) {
|
|
173
257
|
console.log(`Fetching latest version of ${packageName}...`);
|
|
174
258
|
}
|
|
175
|
-
const latestVersion = await getLatestVersion(packageName);
|
|
259
|
+
const latestVersion = await getLatestVersion(packageName, options?.signal);
|
|
260
|
+
await emitProgress(options, 15, `Fetched latest version: ${latestVersion}`);
|
|
176
261
|
if (verbose) {
|
|
177
262
|
console.log(`✓ Latest version: ${latestVersion}\n`);
|
|
178
263
|
}
|
|
@@ -186,7 +271,8 @@ export async function syncUIDataTo(outputDir, verbose = false) {
|
|
|
186
271
|
if (verbose) {
|
|
187
272
|
console.log(`Downloading tarball...`);
|
|
188
273
|
}
|
|
189
|
-
await downloadFile(tarballUrl, tarballPath);
|
|
274
|
+
await downloadFile(tarballUrl, tarballPath, options?.signal);
|
|
275
|
+
await emitProgress(options, 35, 'Downloaded package tarball');
|
|
190
276
|
if (verbose) {
|
|
191
277
|
console.log('✓ Downloaded tarball\n');
|
|
192
278
|
}
|
|
@@ -194,7 +280,8 @@ export async function syncUIDataTo(outputDir, verbose = false) {
|
|
|
194
280
|
if (verbose) {
|
|
195
281
|
console.log('Extracting data files...');
|
|
196
282
|
}
|
|
197
|
-
const extractedDataDir = await extractTarball(tarballPath, tempDir, 'package/assets/data');
|
|
283
|
+
const extractedDataDir = await extractTarball(tarballPath, tempDir, 'package/assets/data', options?.signal);
|
|
284
|
+
await emitProgress(options, 50, 'Extracted package data files');
|
|
198
285
|
if (verbose) {
|
|
199
286
|
console.log('✓ Extracted data files\n');
|
|
200
287
|
}
|
|
@@ -202,7 +289,13 @@ export async function syncUIDataTo(outputDir, verbose = false) {
|
|
|
202
289
|
if (verbose) {
|
|
203
290
|
console.log('Processing data files...');
|
|
204
291
|
}
|
|
205
|
-
await processDataFiles(extractedDataDir, outputDir, verbose
|
|
292
|
+
await processDataFiles(extractedDataDir, outputDir, verbose, {
|
|
293
|
+
signal: options?.signal,
|
|
294
|
+
onProgress: async (progress, message) => {
|
|
295
|
+
const normalized = 50 + Math.round(progress * 0.35);
|
|
296
|
+
await emitProgress(options, normalized, message);
|
|
297
|
+
},
|
|
298
|
+
});
|
|
206
299
|
if (verbose) {
|
|
207
300
|
console.log('✓ Processed all data files\n');
|
|
208
301
|
}
|
|
@@ -214,11 +307,13 @@ export async function syncUIDataTo(outputDir, verbose = false) {
|
|
|
214
307
|
format: 'json',
|
|
215
308
|
};
|
|
216
309
|
writeMetadata(outputDir, metadata);
|
|
310
|
+
await emitProgress(options, 90, 'Wrote metadata');
|
|
217
311
|
if (verbose) {
|
|
218
312
|
console.log('✓ Written metadata\n');
|
|
219
313
|
}
|
|
220
314
|
// 6. 清理临时文件
|
|
221
315
|
cleanup(tempDir);
|
|
316
|
+
await emitProgress(options, 100, 'Sync completed');
|
|
222
317
|
if (verbose) {
|
|
223
318
|
console.log('✓ Cleaned up temporary files\n');
|
|
224
319
|
console.log('✅ Sync completed successfully!');
|
|
@@ -235,7 +330,8 @@ export async function syncUIDataTo(outputDir, verbose = false) {
|
|
|
235
330
|
/**
|
|
236
331
|
* 同步 UI/UX 数据到缓存
|
|
237
332
|
*/
|
|
238
|
-
export async function syncUIDataToCache(force = false, verbose = false) {
|
|
333
|
+
export async function syncUIDataToCache(force = false, verbose = false, options) {
|
|
239
334
|
const cacheDir = path.join(os.homedir(), '.mcp-probe-kit', 'ui-ux-data');
|
|
240
|
-
|
|
335
|
+
void force;
|
|
336
|
+
await syncUIDataTo(cacheDir, verbose, options);
|
|
241
337
|
}
|