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.
Files changed (69) hide show
  1. package/README.md +458 -431
  2. package/build/index.js +875 -141
  3. package/build/lib/gitnexus-bridge.d.ts +58 -0
  4. package/build/lib/gitnexus-bridge.js +379 -0
  5. package/build/lib/template-loader.js +317 -317
  6. package/build/lib/tool-execution-context.d.ts +8 -0
  7. package/build/lib/tool-execution-context.js +20 -0
  8. package/build/lib/toolset-manager.d.ts +1 -1
  9. package/build/lib/toolset-manager.js +7 -5
  10. package/build/schemas/code-analysis-tools.d.ts +46 -0
  11. package/build/schemas/code-analysis-tools.js +47 -0
  12. package/build/schemas/git-tools.js +16 -16
  13. package/build/schemas/index.d.ts +46 -0
  14. package/build/tools/__tests__/code_insight.unit.test.d.ts +1 -0
  15. package/build/tools/__tests__/code_insight.unit.test.js +35 -0
  16. package/build/tools/__tests__/start_bugfix.unit.test.js +14 -14
  17. package/build/tools/__tests__/start_ui.unit.test.js +11 -11
  18. package/build/tools/add_feature.js +79 -79
  19. package/build/tools/ask_user.js +5 -5
  20. package/build/tools/code_insight.d.ts +8 -0
  21. package/build/tools/code_insight.js +129 -0
  22. package/build/tools/index.d.ts +1 -0
  23. package/build/tools/index.js +1 -0
  24. package/build/tools/interview.js +9 -9
  25. package/build/tools/start_bugfix.d.ts +2 -1
  26. package/build/tools/start_bugfix.js +170 -126
  27. package/build/tools/start_feature.d.ts +2 -1
  28. package/build/tools/start_feature.js +156 -112
  29. package/build/tools/start_onboard.d.ts +2 -1
  30. package/build/tools/start_onboard.js +57 -51
  31. package/build/tools/start_product.d.ts +2 -1
  32. package/build/tools/start_product.js +9 -1
  33. package/build/tools/start_ralph.d.ts +2 -1
  34. package/build/tools/start_ralph.js +9 -3
  35. package/build/tools/start_ui.d.ts +2 -1
  36. package/build/tools/start_ui.js +102 -88
  37. package/build/tools/ui-ux-tools.d.ts +2 -1
  38. package/build/tools/ui-ux-tools.js +19 -3
  39. package/build/utils/ui-sync.d.ts +6 -2
  40. package/build/utils/ui-sync.js +125 -29
  41. package/docs/assets/font/MaterialSymbolsOutlined.codepoints +4102 -0
  42. package/docs/assets/font/MaterialSymbolsOutlined.ttf +0 -0
  43. package/docs/assets/font/noto-sans-sc-400.ttf +0 -0
  44. package/docs/assets/font/noto-sans-sc-700.ttf +0 -0
  45. package/docs/assets/font/noto-sans-sc-900.ttf +0 -0
  46. package/docs/assets/js/i18n.js +122 -21
  47. package/docs/assets/js/tailwind.js +83 -83
  48. package/docs/data/tools.js +419 -399
  49. package/docs/debug-i18n.html +163 -0
  50. package/docs/i18n/all-tools/en.json +157 -0
  51. package/docs/i18n/all-tools/ja.json +157 -0
  52. package/docs/i18n/all-tools/ko.json +157 -0
  53. package/docs/i18n/all-tools/zh-CN.json +157 -0
  54. package/docs/pages/all-tools.html +514 -352
  55. package/docs/pages/examples.html +689 -689
  56. package/docs/pages/getting-started.html +589 -589
  57. package/docs/pages/migration.html +298 -298
  58. package/docs/specs/user-auth/design.md +82 -0
  59. package/docs/specs/user-auth/requirements.md +52 -0
  60. package/docs/specs/user-auth/tasks.md +55 -0
  61. package/package.json +5 -5
  62. package/docs/project-context/architecture.md +0 -0
  63. package/docs/project-context/how-to-develop.md +0 -313
  64. package/docs/project-context/how-to-test.md +0 -457
  65. package/docs/project-context/tech-stack.md +0 -96
  66. package/docs/project-context.md +0 -53
  67. package/docs/specs/git-work-report/design.md +0 -568
  68. package/docs/specs/git-work-report/requirements.md +0 -131
  69. package/docs/specs/git-work-report/tasks.md +0 -197
@@ -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
- }).on('error', (error) => {
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
- }).on('error', (error) => {
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 files = fs.readdirSync(sourceDir);
117
- for (const file of files) {
118
- const sourcePath = path.join(sourceDir, file);
119
- const stat = fs.statSync(sourcePath);
120
- if (stat.isDirectory()) {
121
- const subOutputDir = path.join(outputDir, file);
122
- await processDataFiles(sourcePath, subOutputDir, verbose);
123
- continue;
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 outputPath = path.join(outputDir, outputFile);
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
- await syncUIDataTo(cacheDir, verbose);
335
+ void force;
336
+ await syncUIDataTo(cacheDir, verbose, options);
241
337
  }