mcp-probe-kit 3.0.16 → 3.0.17

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 (74) hide show
  1. package/README.md +601 -399
  2. package/build/index.js +13 -1
  3. package/build/lib/__tests__/memory-client.unit.test.d.ts +1 -0
  4. package/build/lib/__tests__/memory-client.unit.test.js +83 -0
  5. package/build/lib/__tests__/memory-config.unit.test.d.ts +1 -0
  6. package/build/lib/__tests__/memory-config.unit.test.js +33 -0
  7. package/build/lib/cursor-history-client.d.ts +54 -0
  8. package/build/lib/cursor-history-client.js +240 -0
  9. package/build/lib/gitnexus-bridge.js +6 -8
  10. package/build/lib/memory-client.d.ts +61 -0
  11. package/build/lib/memory-client.js +293 -0
  12. package/build/lib/memory-config.d.ts +14 -0
  13. package/build/lib/memory-config.js +31 -0
  14. package/build/lib/memory-orchestration.d.ts +26 -0
  15. package/build/lib/memory-orchestration.js +65 -0
  16. package/build/lib/project-detector.js +6 -4
  17. package/build/lib/workspace-root.d.ts +12 -0
  18. package/build/lib/workspace-root.js +153 -0
  19. package/build/resources/ui-ux-data/metadata.json +1 -1
  20. package/build/schemas/code-analysis-tools.d.ts +1 -1
  21. package/build/schemas/code-analysis-tools.js +1 -1
  22. package/build/schemas/index.d.ts +198 -4
  23. package/build/schemas/index.js +2 -0
  24. package/build/schemas/memory-tools.d.ts +191 -0
  25. package/build/schemas/memory-tools.js +106 -0
  26. package/build/schemas/orchestration-tools.d.ts +3 -3
  27. package/build/schemas/orchestration-tools.js +3 -3
  28. package/build/schemas/ui-ux-schemas.d.ts +8 -0
  29. package/build/schemas/ui-ux-schemas.js +4 -0
  30. package/build/tools/__tests__/cursor-history.unit.test.d.ts +1 -0
  31. package/build/tools/__tests__/cursor-history.unit.test.js +87 -0
  32. package/build/tools/__tests__/memorize_asset.unit.test.d.ts +1 -0
  33. package/build/tools/__tests__/memorize_asset.unit.test.js +68 -0
  34. package/build/tools/code_insight.d.ts +20 -0
  35. package/build/tools/code_insight.js +15 -0
  36. package/build/tools/cursor_list_conversations.d.ts +7 -0
  37. package/build/tools/cursor_list_conversations.js +35 -0
  38. package/build/tools/cursor_read_conversation.d.ts +7 -0
  39. package/build/tools/cursor_read_conversation.js +36 -0
  40. package/build/tools/cursor_search_conversations.d.ts +7 -0
  41. package/build/tools/cursor_search_conversations.js +36 -0
  42. package/build/tools/index.d.ts +6 -0
  43. package/build/tools/index.js +7 -0
  44. package/build/tools/init_project_context.d.ts +20 -1
  45. package/build/tools/init_project_context.js +114 -99
  46. package/build/tools/memorize_asset.d.ts +7 -0
  47. package/build/tools/memorize_asset.js +66 -0
  48. package/build/tools/read_memory_asset.d.ts +7 -0
  49. package/build/tools/read_memory_asset.js +26 -0
  50. package/build/tools/scan_and_extract_patterns.d.ts +27 -0
  51. package/build/tools/scan_and_extract_patterns.js +346 -0
  52. package/build/tools/start_bugfix.d.ts +20 -0
  53. package/build/tools/start_bugfix.js +97 -69
  54. package/build/tools/start_feature.d.ts +20 -0
  55. package/build/tools/start_feature.js +61 -31
  56. package/build/tools/start_onboard.d.ts +20 -0
  57. package/build/tools/start_onboard.js +15 -0
  58. package/build/tools/start_ui.d.ts +20 -0
  59. package/build/tools/start_ui.js +66 -32
  60. package/docs/data/tools.js +472 -373
  61. package/docs/i18n/all-tools/en.json +38 -5
  62. package/docs/i18n/all-tools/ja.json +14 -4
  63. package/docs/i18n/all-tools/ko.json +13 -3
  64. package/docs/i18n/all-tools/zh-CN.json +38 -5
  65. package/docs/i18n/en.json +48 -10
  66. package/docs/i18n/ja.json +47 -9
  67. package/docs/i18n/ko.json +47 -9
  68. package/docs/i18n/zh-CN.json +48 -10
  69. package/docs/pages/all-tools.html +515 -515
  70. package/docs/pages/examples.html +661 -661
  71. package/docs/pages/getting-started.html +673 -582
  72. package/docs/pages/migration.html +291 -291
  73. package/package.json +83 -82
  74. package/docs/debug-i18n.html +0 -163
@@ -0,0 +1,346 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { parseArgs, getString, getNumber } from '../utils/parseArgs.js';
4
+ import { okStructured } from '../lib/response.js';
5
+ import { handleToolError } from '../utils/error-handler.js';
6
+ import { resolveWorkspaceRoot } from '../lib/workspace-root.js';
7
+ const DEFAULT_INCLUDE_EXTENSIONS = new Set([
8
+ '.ts',
9
+ '.tsx',
10
+ '.js',
11
+ '.jsx',
12
+ '.mjs',
13
+ '.cjs',
14
+ '.py',
15
+ '.go',
16
+ '.rs',
17
+ '.java',
18
+ '.kt',
19
+ '.swift',
20
+ '.vue',
21
+ '.svelte',
22
+ ]);
23
+ const DEFAULT_IGNORE_DIRS = new Set([
24
+ '.git',
25
+ 'node_modules',
26
+ 'dist',
27
+ 'build',
28
+ '.next',
29
+ '.nuxt',
30
+ '.turbo',
31
+ 'coverage',
32
+ '.cursor-local-assistant',
33
+ ]);
34
+ function inferTags(content, filePath = '') {
35
+ const tags = new Set();
36
+ const lower = `${filePath}\n${content}`.toLowerCase();
37
+ if (lower.includes('react'))
38
+ tags.add('react');
39
+ if (lower.includes('vue'))
40
+ tags.add('vue');
41
+ if (lower.includes('svelte'))
42
+ tags.add('svelte');
43
+ if (lower.includes('typescript') || filePath.endsWith('.ts') || filePath.endsWith('.tsx'))
44
+ tags.add('typescript');
45
+ if (lower.includes('auth'))
46
+ tags.add('auth');
47
+ if (lower.includes('api'))
48
+ tags.add('api');
49
+ if (lower.includes('hook'))
50
+ tags.add('hook');
51
+ if (lower.includes('component'))
52
+ tags.add('component');
53
+ if (lower.includes('test(') || lower.includes('describe(') || /\.test\.|\.spec\./.test(filePath))
54
+ tags.add('test');
55
+ if (lower.includes('debounce'))
56
+ tags.add('debounce');
57
+ if (lower.includes('fetch(') || lower.includes('axios'))
58
+ tags.add('http');
59
+ return [...tags];
60
+ }
61
+ function detectPatternType(content, filePath = '') {
62
+ const lower = content.toLowerCase();
63
+ if (/export\s+function\s+use[A-Z]/.test(content) || /function\s+use[A-Z]/.test(content)) {
64
+ return 'hook';
65
+ }
66
+ if (/export\s+(default\s+)?function\s+[A-Z]/.test(content) || /export\s+const\s+[A-Z][\w$]*\s*=/.test(content)) {
67
+ return 'component';
68
+ }
69
+ if (lower.includes('class ') && lower.includes('service')) {
70
+ return 'service';
71
+ }
72
+ if (lower.includes('interface ') || lower.includes('type ')) {
73
+ return 'type-definition';
74
+ }
75
+ if (/\.test\.|\.spec\./.test(filePath)) {
76
+ return 'test-pattern';
77
+ }
78
+ return 'code-pattern';
79
+ }
80
+ function collectCandidateNames(content) {
81
+ const names = new Set();
82
+ const patterns = [
83
+ /export\s+function\s+([A-Za-z_$][\w$]*)/g,
84
+ /function\s+([A-Za-z_$][\w$]*)\s*\(/g,
85
+ /export\s+const\s+([A-Za-z_$][\w$]*)\s*=/g,
86
+ /const\s+([A-Za-z_$][\w$]*)\s*=\s*\(/g,
87
+ /class\s+([A-Za-z_$][\w$]*)/g,
88
+ ];
89
+ for (const pattern of patterns) {
90
+ for (const match of content.matchAll(pattern)) {
91
+ const name = match[1]?.trim();
92
+ if (name && name.length > 2) {
93
+ names.add(name);
94
+ }
95
+ if (names.size >= 4) {
96
+ return [...names];
97
+ }
98
+ }
99
+ }
100
+ return [...names];
101
+ }
102
+ function summarizeContent(content, maxChars = 200) {
103
+ return content
104
+ .split(/\r?\n/)
105
+ .slice(0, 8)
106
+ .join(' ')
107
+ .replace(/\s+/g, ' ')
108
+ .trim()
109
+ .slice(0, maxChars);
110
+ }
111
+ function buildPattern(content, filePath, projectName) {
112
+ const normalized = content.trim();
113
+ const candidateNames = collectCandidateNames(normalized);
114
+ const primaryName = candidateNames[0] || (filePath ? path.basename(filePath) : 'snippet');
115
+ const type = detectPatternType(normalized, filePath);
116
+ const summary = summarizeContent(normalized);
117
+ return {
118
+ name: primaryName,
119
+ type,
120
+ description: filePath ? `从 ${filePath} 提取的可复用模式` : '从代码片段提取的可复用模式',
121
+ summary,
122
+ content: normalized,
123
+ tags: inferTags(normalized, filePath),
124
+ sourcePath: filePath || undefined,
125
+ sourceProject: projectName || undefined,
126
+ confidence: filePath ? 0.62 : 0.55,
127
+ candidateNames,
128
+ };
129
+ }
130
+ function shouldIncludeFile(filePath, extensions) {
131
+ return extensions.has(path.extname(filePath).toLowerCase());
132
+ }
133
+ function walkDirectory(rootDir, extensions, maxFiles) {
134
+ const collected = [];
135
+ function walk(currentDir) {
136
+ if (collected.length >= maxFiles) {
137
+ return;
138
+ }
139
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
140
+ for (const entry of entries) {
141
+ if (collected.length >= maxFiles) {
142
+ return;
143
+ }
144
+ const fullPath = path.join(currentDir, entry.name);
145
+ if (entry.isDirectory()) {
146
+ if (DEFAULT_IGNORE_DIRS.has(entry.name)) {
147
+ continue;
148
+ }
149
+ walk(fullPath);
150
+ continue;
151
+ }
152
+ if (entry.isFile() && shouldIncludeFile(fullPath, extensions)) {
153
+ collected.push(fullPath);
154
+ }
155
+ }
156
+ }
157
+ walk(rootDir);
158
+ return collected;
159
+ }
160
+ function resolveTargetDirectory(projectRoot, directoryPath) {
161
+ if (!directoryPath) {
162
+ return { resolvedPath: projectRoot, attemptedRoots: [projectRoot] };
163
+ }
164
+ if (path.isAbsolute(directoryPath)) {
165
+ return {
166
+ resolvedPath: path.resolve(directoryPath),
167
+ attemptedRoots: [path.resolve(directoryPath)],
168
+ };
169
+ }
170
+ const attemptedRoots = Array.from(new Set([
171
+ projectRoot,
172
+ resolveWorkspaceRoot(),
173
+ process.cwd(),
174
+ ].filter(Boolean)));
175
+ for (const root of attemptedRoots) {
176
+ const candidate = path.resolve(root, directoryPath);
177
+ try {
178
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
179
+ return { resolvedPath: candidate, attemptedRoots };
180
+ }
181
+ }
182
+ catch {
183
+ // ignore and continue probing next root
184
+ }
185
+ }
186
+ return {
187
+ resolvedPath: path.resolve(attemptedRoots[0] || projectRoot, directoryPath),
188
+ attemptedRoots,
189
+ };
190
+ }
191
+ function isLikelyProjectNamedRelativePath(directoryPath) {
192
+ if (!directoryPath || path.isAbsolute(directoryPath)) {
193
+ return false;
194
+ }
195
+ const normalized = directoryPath.replace(/\\/g, '/').replace(/^\.\//, '').trim();
196
+ if (!normalized || normalized.startsWith('../')) {
197
+ return false;
198
+ }
199
+ const segments = normalized.split('/').filter(Boolean);
200
+ if (segments.length < 2) {
201
+ return false;
202
+ }
203
+ const [firstSegment, secondSegment] = segments;
204
+ const commonRootDirs = new Set([
205
+ 'src',
206
+ 'app',
207
+ 'lib',
208
+ 'test',
209
+ 'tests',
210
+ 'spec',
211
+ 'specs',
212
+ 'docs',
213
+ 'scripts',
214
+ 'packages',
215
+ 'services',
216
+ 'server',
217
+ 'client',
218
+ 'components',
219
+ 'utils',
220
+ 'bin',
221
+ 'config',
222
+ 'examples',
223
+ ]);
224
+ if (commonRootDirs.has(firstSegment.toLowerCase())) {
225
+ return false;
226
+ }
227
+ if (!/^[a-z0-9._-]+$/i.test(firstSegment)) {
228
+ return false;
229
+ }
230
+ return commonRootDirs.has(secondSegment.toLowerCase()) || firstSegment.includes('-');
231
+ }
232
+ function buildRetryHint(directoryPath) {
233
+ const normalized = directoryPath.replace(/\\/g, '/');
234
+ const withoutFirstSegment = normalized.split('/').slice(1).join('/') || 'app/utils';
235
+ return {
236
+ preferred: {
237
+ project_root: 'C:/path/to/your/project',
238
+ directory_path: withoutFirstSegment,
239
+ },
240
+ fallback: {
241
+ directory_path: 'C:/path/to/your/project/' + withoutFirstSegment,
242
+ },
243
+ };
244
+ }
245
+ function toRelativePath(targetPath, rootDir) {
246
+ const relative = path.relative(rootDir, targetPath);
247
+ return relative || path.basename(targetPath);
248
+ }
249
+ export async function scanAndExtractPatterns(args) {
250
+ try {
251
+ const parsed = parseArgs(args, {
252
+ defaultValues: {
253
+ content: '',
254
+ file_path: '',
255
+ project_name: '',
256
+ directory_path: '',
257
+ project_root: '',
258
+ max_files: 30,
259
+ max_patterns: 20,
260
+ },
261
+ fieldAliases: {
262
+ content: ['code', 'snippet'],
263
+ file_path: ['path'],
264
+ project_name: ['project'],
265
+ directory_path: ['dir', 'directory'],
266
+ project_root: ['root'],
267
+ max_files: ['limit_files'],
268
+ max_patterns: ['limit_patterns'],
269
+ include_extensions: ['extensions', 'exts'],
270
+ },
271
+ });
272
+ const content = getString(parsed.content);
273
+ const filePath = getString(parsed.file_path);
274
+ const projectName = getString(parsed.project_name);
275
+ const directoryPath = getString(parsed.directory_path);
276
+ const projectRoot = resolveWorkspaceRoot(getString(parsed.project_root));
277
+ const maxFiles = Math.max(1, Math.min(200, getNumber(parsed.max_files, 30)));
278
+ const maxPatterns = Math.max(1, Math.min(100, getNumber(parsed.max_patterns, 20)));
279
+ const includeExtensions = new Set(Array.isArray(parsed.include_extensions)
280
+ ? parsed.include_extensions
281
+ .filter((item) => typeof item === 'string')
282
+ .map((item) => item.startsWith('.') ? item.toLowerCase() : `.${item.toLowerCase()}`)
283
+ : DEFAULT_INCLUDE_EXTENSIONS);
284
+ if (content) {
285
+ const pattern = buildPattern(content, filePath, projectName || undefined);
286
+ return okStructured(`已提取 1 个候选模式${filePath ? `: ${filePath}` : ''}`, {
287
+ mode: 'single',
288
+ patterns: [pattern],
289
+ });
290
+ }
291
+ if (!getString(parsed.project_root) && isLikelyProjectNamedRelativePath(directoryPath)) {
292
+ return {
293
+ content: [{
294
+ type: 'text',
295
+ text: `拒绝执行目录扫描:directory_path 不能传带项目名的半相对路径,例如 ${directoryPath}。请改为传 project_root + 相对目录路径,或直接传目录绝对路径。`,
296
+ }],
297
+ isError: true,
298
+ structuredContent: {
299
+ error_code: 'INVALID_DIRECTORY_PATH',
300
+ mode: 'directory',
301
+ rejected_directory_path: directoryPath,
302
+ retry_hint: buildRetryHint(directoryPath),
303
+ },
304
+ };
305
+ }
306
+ const { resolvedPath: targetDir, attemptedRoots } = resolveTargetDirectory(projectRoot, directoryPath);
307
+ if (!fs.existsSync(targetDir) || !fs.statSync(targetDir).isDirectory()) {
308
+ const attempted = attemptedRoots.map((root) => path.resolve(root, directoryPath).replace(/\\/g, '/'));
309
+ return okStructured(`目录不存在或不可访问: ${targetDir.replace(/\\/g, '/')}。相对路径解析依赖工作区根目录;请显式传入 project_root,或让客户端传递 MCP_PROJECT_ROOT。`, {
310
+ mode: 'directory',
311
+ scannedFiles: 0,
312
+ patterns: [],
313
+ attemptedPaths: attempted,
314
+ });
315
+ }
316
+ const files = walkDirectory(targetDir, includeExtensions, maxFiles);
317
+ const patterns = [];
318
+ for (const absoluteFile of files) {
319
+ if (patterns.length >= maxPatterns) {
320
+ break;
321
+ }
322
+ let raw = '';
323
+ try {
324
+ raw = fs.readFileSync(absoluteFile, 'utf-8');
325
+ }
326
+ catch {
327
+ continue;
328
+ }
329
+ const normalized = raw.trim();
330
+ if (!normalized || normalized.length < 40) {
331
+ continue;
332
+ }
333
+ const relativePath = toRelativePath(absoluteFile, projectRoot).replace(/\\/g, '/');
334
+ patterns.push(buildPattern(normalized, relativePath, projectName || path.basename(projectRoot)));
335
+ }
336
+ return okStructured(`已扫描 ${files.length} 个文件,提取 ${patterns.length} 个候选模式`, {
337
+ mode: 'directory',
338
+ scannedRoot: targetDir.replace(/\\/g, '/'),
339
+ scannedFiles: files.length,
340
+ patterns,
341
+ });
342
+ }
343
+ catch (error) {
344
+ return handleToolError(error, 'scan_and_extract_patterns');
345
+ }
346
+ }
@@ -5,4 +5,24 @@ export declare function startBugfix(args: any, context?: ToolExecutionContext):
5
5
  text: string;
6
6
  }[];
7
7
  isError: boolean;
8
+ structuredContent: {
9
+ error_code: string;
10
+ rejected_project_root: string;
11
+ retry_hint: {
12
+ preferred: {
13
+ project_root: string;
14
+ path: string;
15
+ };
16
+ fallback: {
17
+ project_root: string;
18
+ };
19
+ };
20
+ };
21
+ } | {
22
+ content: {
23
+ type: string;
24
+ text: string;
25
+ }[];
26
+ isError: boolean;
27
+ structuredContent?: undefined;
8
28
  }>;