mcp-probe-kit 3.0.24 → 3.2.0
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 +755 -779
- package/build/index.js +42 -41
- package/build/lib/__tests__/spec-validator.unit.test.js +115 -0
- package/build/lib/agents-md-template.js +32 -32
- package/build/lib/memory-orchestration.js +29 -8
- package/build/lib/skill-bridge.js +12 -12
- package/build/lib/spec-validator.d.ts +36 -0
- package/build/lib/spec-validator.js +103 -0
- package/build/lib/template-loader.js +149 -47
- package/build/lib/tool-annotations.d.ts +30 -0
- package/build/lib/tool-annotations.js +55 -0
- package/build/lib/toolset-manager.js +2 -0
- package/build/resources/index.d.ts +4 -0
- package/build/resources/index.js +4 -0
- package/build/resources/tool-params-guide.d.ts +571 -0
- package/build/resources/tool-params-guide.js +488 -0
- package/build/resources/ui-ux-data/guidelines/vercel-web-interface.json +1632 -1632
- package/build/resources/ui-ux-data/metadata.json +30 -30
- package/build/resources/ui-ux-data/shadcn/blocks.json +2541 -2541
- package/build/resources/ui-ux-data/shadcn/components.json +997 -997
- package/build/resources/ui-ux-data/themes/presets.json +483 -483
- package/build/schemas/index.d.ts +22 -22
- package/build/schemas/memory-tools.d.ts +0 -22
- package/build/schemas/memory-tools.js +0 -14
- package/build/schemas/project-tools.d.ts +22 -0
- package/build/schemas/project-tools.js +23 -0
- package/build/tools/analyze_project.d.ts +1 -0
- package/build/tools/analyze_project.js +527 -0
- package/build/tools/check_deps.d.ts +13 -0
- package/build/tools/check_deps.js +204 -0
- package/build/tools/check_spec.d.ts +7 -0
- package/build/tools/check_spec.js +81 -0
- package/build/tools/code_insight.js +41 -41
- package/build/tools/convert.d.ts +13 -0
- package/build/tools/convert.js +599 -0
- package/build/tools/css_order.d.ts +13 -0
- package/build/tools/css_order.js +81 -0
- package/build/tools/debug.d.ts +13 -0
- package/build/tools/debug.js +131 -0
- package/build/tools/design2code.d.ts +20 -0
- package/build/tools/design2code.js +426 -0
- package/build/tools/detect_shell.d.ts +6 -0
- package/build/tools/detect_shell.js +151 -0
- package/build/tools/explain.d.ts +13 -0
- package/build/tools/explain.js +390 -0
- package/build/tools/fix.d.ts +13 -0
- package/build/tools/fix.js +303 -0
- package/build/tools/fix_bug.js +161 -161
- package/build/tools/gen_mock.d.ts +22 -0
- package/build/tools/gen_mock.js +269 -0
- package/build/tools/gen_skill.d.ts +13 -0
- package/build/tools/gen_skill.js +560 -0
- package/build/tools/genapi.d.ts +13 -0
- package/build/tools/genapi.js +174 -0
- package/build/tools/genchangelog.d.ts +13 -0
- package/build/tools/genchangelog.js +250 -0
- package/build/tools/gencommit.js +60 -60
- package/build/tools/gendoc.d.ts +13 -0
- package/build/tools/gendoc.js +232 -0
- package/build/tools/genpr.d.ts +13 -0
- package/build/tools/genpr.js +194 -0
- package/build/tools/genreadme.d.ts +13 -0
- package/build/tools/genreadme.js +626 -0
- package/build/tools/gensql.d.ts +13 -0
- package/build/tools/gensql.js +320 -0
- package/build/tools/genui.d.ts +13 -0
- package/build/tools/genui.js +803 -0
- package/build/tools/index.d.ts +1 -1
- package/build/tools/index.js +1 -1
- package/build/tools/init_component_catalog.d.ts +22 -0
- package/build/tools/init_component_catalog.js +809 -0
- package/build/tools/init_project_context.js +432 -432
- package/build/tools/init_setting.d.ts +13 -0
- package/build/tools/init_setting.js +47 -0
- package/build/tools/perf.d.ts +13 -0
- package/build/tools/perf.js +409 -0
- package/build/tools/render_ui.d.ts +22 -0
- package/build/tools/render_ui.js +384 -0
- package/build/tools/resolve_conflict.d.ts +13 -0
- package/build/tools/resolve_conflict.js +349 -0
- package/build/tools/security_scan.d.ts +22 -0
- package/build/tools/security_scan.js +323 -0
- package/build/tools/split.d.ts +13 -0
- package/build/tools/split.js +599 -0
- package/build/tools/start_api.d.ts +13 -0
- package/build/tools/start_api.js +193 -0
- package/build/tools/start_bugfix.js +254 -243
- package/build/tools/start_doc.d.ts +13 -0
- package/build/tools/start_doc.js +207 -0
- package/build/tools/start_feature.js +162 -127
- package/build/tools/start_product.js +1 -1
- package/build/tools/start_refactor.d.ts +13 -0
- package/build/tools/start_refactor.js +188 -0
- package/build/tools/start_release.d.ts +13 -0
- package/build/tools/start_release.js +167 -0
- package/build/tools/start_review.d.ts +13 -0
- package/build/tools/start_review.js +175 -0
- package/build/tools/start_ui.js +426 -412
- package/build/tools/ui-ux-tools.js +290 -290
- package/build/utils/__tests__/vercel-guidelines-sync.unit.test.js +12 -12
- package/build/utils/themes-sync.js +8 -8
- package/package.json +81 -83
- package/build/lib/__tests__/memory-orchestration.unit.test.js +0 -88
- package/build/lib/__tests__/memory-payload.unit.test.js +0 -35
- package/build/lib/cursor-history-client.d.ts +0 -54
- package/build/lib/cursor-history-client.js +0 -240
- package/build/tools/__tests__/cursor-history.unit.test.js +0 -38
- package/build/tools/cursor_read_conversation.d.ts +0 -7
- package/build/tools/cursor_read_conversation.js +0 -36
- package/docs/.mcp-probe/layout.json +0 -11
- package/docs/CNAME +0 -1
- package/docs/assets/font/MaterialSymbolsOutlined.codepoints +0 -4102
- 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 +0 -375
- package/docs/assets/js/tailwind.js +0 -83
- package/docs/assets/logo-zh.png +0 -0
- package/docs/assets/logo.png +0 -0
- package/docs/data/tools.js +0 -523
- package/docs/i18n/all-tools/en.json +0 -190
- package/docs/i18n/all-tools/ja.json +0 -171
- package/docs/i18n/all-tools/ko.json +0 -171
- package/docs/i18n/all-tools/zh-CN.json +0 -190
- package/docs/i18n/en.json +0 -626
- package/docs/i18n/ja.json +0 -602
- package/docs/i18n/ko.json +0 -602
- package/docs/i18n/zh-CN.json +0 -626
- package/docs/index.html +0 -327
- package/docs/memory-local-setup.md +0 -315
- package/docs/memory-local-setup.zh-CN.md +0 -283
- package/docs/pages/all-tools.html +0 -515
- package/docs/pages/examples.html +0 -717
- package/docs/pages/getting-started.html +0 -964
- package/docs/pages/migration.html +0 -308
- package/docs/specs/user-auth/design.md +0 -82
- package/docs/specs/user-auth/requirements.md +0 -52
- package/docs/specs/user-auth/tasks.md +0 -55
- /package/build/lib/__tests__/{memory-orchestration.unit.test.d.ts → spec-validator.unit.test.d.ts} +0 -0
- /package/build/{lib/__tests__/memory-payload.unit.test.d.ts → utils/design-docs-generator.d.ts} +0 -0
- /package/build/{tools/__tests__/cursor-history.unit.test.d.ts → utils/design-docs-generator.js} +0 -0
package/build/schemas/index.d.ts
CHANGED
|
@@ -281,6 +281,28 @@ export declare const allToolSchemas: ({
|
|
|
281
281
|
readonly required: readonly [];
|
|
282
282
|
readonly additionalProperties: true;
|
|
283
283
|
};
|
|
284
|
+
} | {
|
|
285
|
+
readonly name: "check_spec";
|
|
286
|
+
readonly description: "校验已落盘的功能规格(docs/specs/<feature_name>/requirements|design|tasks.md)是否完整:检测残留 [填写] 占位、缺失章节、缺 FR/验收标准、FR 未进覆盖矩阵。写完规格后、进入实现前调用;未通过按报告补全后重跑。";
|
|
287
|
+
readonly inputSchema: {
|
|
288
|
+
readonly type: "object";
|
|
289
|
+
readonly properties: {
|
|
290
|
+
readonly feature_name: {
|
|
291
|
+
readonly type: "string";
|
|
292
|
+
readonly description: "要校验的规格目录名,对应 docs/specs/<feature_name>/";
|
|
293
|
+
};
|
|
294
|
+
readonly docs_dir: {
|
|
295
|
+
readonly type: "string";
|
|
296
|
+
readonly description: "文档根目录,默认为 docs";
|
|
297
|
+
};
|
|
298
|
+
readonly project_root: {
|
|
299
|
+
readonly type: "string";
|
|
300
|
+
readonly description: "项目根目录绝对路径。可选,默认自动探测工作区根";
|
|
301
|
+
};
|
|
302
|
+
};
|
|
303
|
+
readonly required: readonly [];
|
|
304
|
+
readonly additionalProperties: true;
|
|
305
|
+
};
|
|
284
306
|
} | {
|
|
285
307
|
readonly name: "start_feature";
|
|
286
308
|
readonly description: "当用户需要完整的新功能开发流程时使用。编排:检查上下文→生成规格→估算工作量。若只需规格文档请用 add_feature";
|
|
@@ -837,26 +859,4 @@ export declare const allToolSchemas: ({
|
|
|
837
859
|
readonly required: readonly [];
|
|
838
860
|
readonly additionalProperties: true;
|
|
839
861
|
};
|
|
840
|
-
} | {
|
|
841
|
-
readonly name: "cursor_read_conversation";
|
|
842
|
-
readonly description: "按 composer_id 读取一条 Cursor 本地会话的消息时间线。";
|
|
843
|
-
readonly inputSchema: {
|
|
844
|
-
readonly type: "object";
|
|
845
|
-
readonly properties: {
|
|
846
|
-
readonly composer_id: {
|
|
847
|
-
readonly type: "string";
|
|
848
|
-
readonly description: "Cursor 会话 ID";
|
|
849
|
-
};
|
|
850
|
-
readonly limit: {
|
|
851
|
-
readonly type: "number";
|
|
852
|
-
readonly description: "最多返回多少条消息,默认 200,最大 2000";
|
|
853
|
-
};
|
|
854
|
-
readonly include_empty: {
|
|
855
|
-
readonly type: "boolean";
|
|
856
|
-
readonly description: "是否包含空文本消息,默认 false";
|
|
857
|
-
};
|
|
858
|
-
};
|
|
859
|
-
readonly required: readonly ["composer_id"];
|
|
860
|
-
readonly additionalProperties: true;
|
|
861
|
-
};
|
|
862
862
|
})[];
|
|
@@ -147,26 +147,4 @@ export declare const memoryToolSchemas: readonly [{
|
|
|
147
147
|
readonly required: readonly [];
|
|
148
148
|
readonly additionalProperties: true;
|
|
149
149
|
};
|
|
150
|
-
}, {
|
|
151
|
-
readonly name: "cursor_read_conversation";
|
|
152
|
-
readonly description: "按 composer_id 读取一条 Cursor 本地会话的消息时间线。";
|
|
153
|
-
readonly inputSchema: {
|
|
154
|
-
readonly type: "object";
|
|
155
|
-
readonly properties: {
|
|
156
|
-
readonly composer_id: {
|
|
157
|
-
readonly type: "string";
|
|
158
|
-
readonly description: "Cursor 会话 ID";
|
|
159
|
-
};
|
|
160
|
-
readonly limit: {
|
|
161
|
-
readonly type: "number";
|
|
162
|
-
readonly description: "最多返回多少条消息,默认 200,最大 2000";
|
|
163
|
-
};
|
|
164
|
-
readonly include_empty: {
|
|
165
|
-
readonly type: "boolean";
|
|
166
|
-
readonly description: "是否包含空文本消息,默认 false";
|
|
167
|
-
};
|
|
168
|
-
};
|
|
169
|
-
readonly required: readonly ["composer_id"];
|
|
170
|
-
readonly additionalProperties: true;
|
|
171
|
-
};
|
|
172
150
|
}];
|
|
@@ -75,18 +75,4 @@ export const memoryToolSchemas = [
|
|
|
75
75
|
additionalProperties: true,
|
|
76
76
|
},
|
|
77
77
|
},
|
|
78
|
-
{
|
|
79
|
-
name: 'cursor_read_conversation',
|
|
80
|
-
description: '按 composer_id 读取一条 Cursor 本地会话的消息时间线。',
|
|
81
|
-
inputSchema: {
|
|
82
|
-
type: 'object',
|
|
83
|
-
properties: {
|
|
84
|
-
composer_id: { type: 'string', description: 'Cursor 会话 ID' },
|
|
85
|
-
limit: { type: 'number', description: '最多返回多少条消息,默认 200,最大 2000' },
|
|
86
|
-
include_empty: { type: 'boolean', description: '是否包含空文本消息,默认 false' },
|
|
87
|
-
},
|
|
88
|
-
required: ['composer_id'],
|
|
89
|
-
additionalProperties: true,
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
78
|
];
|
|
@@ -89,4 +89,26 @@ export declare const projectToolSchemas: readonly [{
|
|
|
89
89
|
readonly required: readonly [];
|
|
90
90
|
readonly additionalProperties: true;
|
|
91
91
|
};
|
|
92
|
+
}, {
|
|
93
|
+
readonly name: "check_spec";
|
|
94
|
+
readonly description: "校验已落盘的功能规格(docs/specs/<feature_name>/requirements|design|tasks.md)是否完整:检测残留 [填写] 占位、缺失章节、缺 FR/验收标准、FR 未进覆盖矩阵。写完规格后、进入实现前调用;未通过按报告补全后重跑。";
|
|
95
|
+
readonly inputSchema: {
|
|
96
|
+
readonly type: "object";
|
|
97
|
+
readonly properties: {
|
|
98
|
+
readonly feature_name: {
|
|
99
|
+
readonly type: "string";
|
|
100
|
+
readonly description: "要校验的规格目录名,对应 docs/specs/<feature_name>/";
|
|
101
|
+
};
|
|
102
|
+
readonly docs_dir: {
|
|
103
|
+
readonly type: "string";
|
|
104
|
+
readonly description: "文档根目录,默认为 docs";
|
|
105
|
+
};
|
|
106
|
+
readonly project_root: {
|
|
107
|
+
readonly type: "string";
|
|
108
|
+
readonly description: "项目根目录绝对路径。可选,默认自动探测工作区根";
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
readonly required: readonly [];
|
|
112
|
+
readonly additionalProperties: true;
|
|
113
|
+
};
|
|
92
114
|
}];
|
|
@@ -93,4 +93,27 @@ export const projectToolSchemas = [
|
|
|
93
93
|
additionalProperties: true,
|
|
94
94
|
},
|
|
95
95
|
},
|
|
96
|
+
{
|
|
97
|
+
name: "check_spec",
|
|
98
|
+
description: "校验已落盘的功能规格(docs/specs/<feature_name>/requirements|design|tasks.md)是否完整:检测残留 [填写] 占位、缺失章节、缺 FR/验收标准、FR 未进覆盖矩阵。写完规格后、进入实现前调用;未通过按报告补全后重跑。",
|
|
99
|
+
inputSchema: {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {
|
|
102
|
+
feature_name: {
|
|
103
|
+
type: "string",
|
|
104
|
+
description: "要校验的规格目录名,对应 docs/specs/<feature_name>/",
|
|
105
|
+
},
|
|
106
|
+
docs_dir: {
|
|
107
|
+
type: "string",
|
|
108
|
+
description: "文档根目录,默认为 docs",
|
|
109
|
+
},
|
|
110
|
+
project_root: {
|
|
111
|
+
type: "string",
|
|
112
|
+
description: "项目根目录绝对路径。可选,默认自动探测工作区根",
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
required: [],
|
|
116
|
+
additionalProperties: true,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
96
119
|
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function analyzeProject(args: any): Promise<any>;
|
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import { parseArgs, getString, getNumber, getBoolean } from "../utils/parseArgs.js";
|
|
2
|
+
import { readFileSync, readdirSync, statSync, existsSync } from 'fs';
|
|
3
|
+
import { join, extname } from 'path';
|
|
4
|
+
import { VERSION } from '../version.js';
|
|
5
|
+
export async function analyzeProject(args) {
|
|
6
|
+
// 智能参数解析,支持自然语言输入
|
|
7
|
+
const parsedArgs = parseArgs(args, {
|
|
8
|
+
defaultValues: {
|
|
9
|
+
project_path: process.cwd(),
|
|
10
|
+
max_depth: 5,
|
|
11
|
+
include_content: true,
|
|
12
|
+
},
|
|
13
|
+
primaryField: "project_path", // 纯文本输入默认映射到 project_path 字段
|
|
14
|
+
fieldAliases: {
|
|
15
|
+
project_path: ["path", "dir", "directory", "路径", "项目路径"],
|
|
16
|
+
max_depth: ["depth", "level", "深度", "层级"],
|
|
17
|
+
include_content: ["content", "with_content", "包含内容"],
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
const projectPath = getString(parsedArgs.project_path) || process.cwd();
|
|
21
|
+
const maxDepth = getNumber(parsedArgs.max_depth, 5);
|
|
22
|
+
const includeContent = getBoolean(parsedArgs.include_content, true);
|
|
23
|
+
try {
|
|
24
|
+
console.error(`开始分析项目: ${projectPath}`);
|
|
25
|
+
const analysis = await performProjectAnalysis(projectPath, maxDepth, includeContent);
|
|
26
|
+
return {
|
|
27
|
+
content: [
|
|
28
|
+
{
|
|
29
|
+
type: "text",
|
|
30
|
+
text: `# 📊 项目分析报告
|
|
31
|
+
|
|
32
|
+
## 🏗️ 项目概览
|
|
33
|
+
- **项目名称**: ${analysis.projectStructure.name}
|
|
34
|
+
- **项目类型**: ${analysis.projectStructure.type}
|
|
35
|
+
- **技术栈**: ${analysis.projectStructure.framework}
|
|
36
|
+
- **主要语言**: ${analysis.projectStructure.language}
|
|
37
|
+
- **包管理器**: ${analysis.projectStructure.packageManager}
|
|
38
|
+
|
|
39
|
+
## 📁 目录结构
|
|
40
|
+
\`\`\`
|
|
41
|
+
${analysis.directoryTree}
|
|
42
|
+
\`\`\`
|
|
43
|
+
|
|
44
|
+
## 🔑 关键文件
|
|
45
|
+
${analysis.keyFiles.map(file => `### ${file.path}
|
|
46
|
+
**用途**: ${file.purpose}
|
|
47
|
+
${includeContent ? `\`\`\`${getFileExtension(file.path)}
|
|
48
|
+
${file.content.substring(0, 500)}${file.content.length > 500 ? '\n...' : ''}
|
|
49
|
+
\`\`\`` : ''}`).join('\n\n')}
|
|
50
|
+
|
|
51
|
+
## 📦 依赖分析
|
|
52
|
+
- **生产依赖**: ${analysis.dependencies.production.length} 个
|
|
53
|
+
- **开发依赖**: ${analysis.dependencies.development.length} 个
|
|
54
|
+
- **总依赖数**: ${analysis.dependencies.total} 个
|
|
55
|
+
|
|
56
|
+
### 主要依赖
|
|
57
|
+
${analysis.dependencies.production.slice(0, 10).map(dep => `- ${dep}`).join('\n')}
|
|
58
|
+
|
|
59
|
+
## 📈 代码指标
|
|
60
|
+
- **总文件数**: ${analysis.codeMetrics.totalFiles}
|
|
61
|
+
- **总行数**: ${analysis.codeMetrics.totalLines}
|
|
62
|
+
${analysis.codeMetrics.skippedFiles > 0 ? `- **跳过文件**: ${analysis.codeMetrics.skippedFiles} 个(过大或无法读取)` : ''}
|
|
63
|
+
- **文件类型分布**:
|
|
64
|
+
${Object.entries(analysis.codeMetrics.fileTypes)
|
|
65
|
+
.sort(([, a], [, b]) => b - a)
|
|
66
|
+
.slice(0, 10)
|
|
67
|
+
.map(([type, count]) => ` - ${type}: ${count} 个文件`)
|
|
68
|
+
.join('\n')}
|
|
69
|
+
${Object.keys(analysis.codeMetrics.fileTypes).length > 10 ? ' - ... (更多类型已省略)' : ''}
|
|
70
|
+
|
|
71
|
+
### 最大文件
|
|
72
|
+
${analysis.codeMetrics.largestFiles.slice(0, 5).map(file => `- ${file.path} (${file.lines} 行)`).join('\n')}
|
|
73
|
+
|
|
74
|
+
## 🏛️ 架构分析
|
|
75
|
+
- **设计模式**: ${analysis.architecture.patterns.join(', ')}
|
|
76
|
+
- **入口文件**: ${analysis.architecture.entryPoints.join(', ')}
|
|
77
|
+
- **核心模块**: ${analysis.architecture.mainModules.join(', ')}
|
|
78
|
+
|
|
79
|
+
## 📋 项目总结
|
|
80
|
+
**项目目的**: ${analysis.summary.purpose}
|
|
81
|
+
**复杂度**: ${analysis.summary.complexity}
|
|
82
|
+
**建议**:
|
|
83
|
+
${analysis.summary.recommendations.map(rec => `- ${rec}`).join('\n')}
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
*分析完成时间: ${new Date().toLocaleString('zh-CN')}*
|
|
87
|
+
*分析工具: MCP Probe Kit v${VERSION}*
|
|
88
|
+
|
|
89
|
+
**分析说明**:
|
|
90
|
+
- 大型项目会自动采样分析,限制最多扫描 5000 个文件
|
|
91
|
+
- 已自动忽略以下目录: \`node_modules\`, \`dist\`, \`build\`, \`.git\`, \`coverage\`, \`.next\`, \`.nuxt\`, \`vendor\` 等
|
|
92
|
+
- 单个文件大小限制: 1MB,超过则跳过`,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
return {
|
|
99
|
+
content: [
|
|
100
|
+
{
|
|
101
|
+
type: "text",
|
|
102
|
+
text: `❌ 项目分析失败: ${error instanceof Error ? error.message : String(error)}`,
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
isError: true,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function performProjectAnalysis(projectPath, maxDepth, includeContent) {
|
|
110
|
+
// 读取 package.json
|
|
111
|
+
const packageJson = await readPackageJson(projectPath);
|
|
112
|
+
// 分析项目结构
|
|
113
|
+
const projectStructure = analyzeProjectStructure(packageJson);
|
|
114
|
+
// 生成目录树
|
|
115
|
+
const directoryTree = generateDirectoryTree(projectPath, maxDepth);
|
|
116
|
+
// 识别关键文件
|
|
117
|
+
const keyFiles = await identifyKeyFiles(projectPath, includeContent);
|
|
118
|
+
// 分析依赖
|
|
119
|
+
const dependencies = analyzeDependencies(packageJson);
|
|
120
|
+
// 代码指标
|
|
121
|
+
const codeMetrics = await calculateCodeMetrics(projectPath);
|
|
122
|
+
// 架构分析
|
|
123
|
+
const architecture = await analyzeArchitecture(projectPath, keyFiles);
|
|
124
|
+
// 生成总结
|
|
125
|
+
const summary = generateSummary(projectStructure, codeMetrics, architecture);
|
|
126
|
+
return {
|
|
127
|
+
projectStructure,
|
|
128
|
+
directoryTree,
|
|
129
|
+
keyFiles,
|
|
130
|
+
dependencies,
|
|
131
|
+
codeMetrics,
|
|
132
|
+
architecture,
|
|
133
|
+
summary,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
async function readPackageJson(projectPath) {
|
|
137
|
+
try {
|
|
138
|
+
const packageJsonPath = join(projectPath, 'package.json');
|
|
139
|
+
const content = readFileSync(packageJsonPath, 'utf-8');
|
|
140
|
+
return JSON.parse(content);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return {};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function analyzeProjectStructure(packageJson) {
|
|
147
|
+
const name = packageJson.name || 'unknown';
|
|
148
|
+
const type = detectProjectType(packageJson);
|
|
149
|
+
const framework = detectFramework(packageJson);
|
|
150
|
+
const language = detectLanguage(packageJson);
|
|
151
|
+
const packageManager = detectPackageManager();
|
|
152
|
+
return { name, type, framework, language, packageManager };
|
|
153
|
+
}
|
|
154
|
+
function detectProjectType(packageJson) {
|
|
155
|
+
if (packageJson.dependencies?.react)
|
|
156
|
+
return 'React 应用';
|
|
157
|
+
if (packageJson.dependencies?.vue)
|
|
158
|
+
return 'Vue 应用';
|
|
159
|
+
if (packageJson.dependencies?.angular)
|
|
160
|
+
return 'Angular 应用';
|
|
161
|
+
if (packageJson.dependencies?.express)
|
|
162
|
+
return 'Node.js 后端';
|
|
163
|
+
if (packageJson.dependencies?.next)
|
|
164
|
+
return 'Next.js 应用';
|
|
165
|
+
if (packageJson.dependencies?.nuxt)
|
|
166
|
+
return 'Nuxt.js 应用';
|
|
167
|
+
if (packageJson.dependencies?.svelte)
|
|
168
|
+
return 'Svelte 应用';
|
|
169
|
+
if (packageJson.dependencies?.electron)
|
|
170
|
+
return 'Electron 应用';
|
|
171
|
+
if (packageJson.dependencies?.jest || packageJson.devDependencies?.jest)
|
|
172
|
+
return '测试项目';
|
|
173
|
+
if (packageJson.dependencies?.webpack || packageJson.devDependencies?.webpack)
|
|
174
|
+
return '构建工具';
|
|
175
|
+
return '通用项目';
|
|
176
|
+
}
|
|
177
|
+
function detectFramework(packageJson) {
|
|
178
|
+
const frameworks = [];
|
|
179
|
+
if (packageJson.dependencies?.react)
|
|
180
|
+
frameworks.push('React');
|
|
181
|
+
if (packageJson.dependencies?.vue)
|
|
182
|
+
frameworks.push('Vue');
|
|
183
|
+
if (packageJson.dependencies?.angular)
|
|
184
|
+
frameworks.push('Angular');
|
|
185
|
+
if (packageJson.dependencies?.express)
|
|
186
|
+
frameworks.push('Express');
|
|
187
|
+
if (packageJson.dependencies?.next)
|
|
188
|
+
frameworks.push('Next.js');
|
|
189
|
+
if (packageJson.dependencies?.nuxt)
|
|
190
|
+
frameworks.push('Nuxt.js');
|
|
191
|
+
if (packageJson.dependencies?.svelte)
|
|
192
|
+
frameworks.push('Svelte');
|
|
193
|
+
if (packageJson.dependencies?.electron)
|
|
194
|
+
frameworks.push('Electron');
|
|
195
|
+
return frameworks.join(', ') || '无框架';
|
|
196
|
+
}
|
|
197
|
+
function detectLanguage(packageJson) {
|
|
198
|
+
if (packageJson.devDependencies?.typescript)
|
|
199
|
+
return 'TypeScript';
|
|
200
|
+
if (packageJson.dependencies?.typescript)
|
|
201
|
+
return 'TypeScript';
|
|
202
|
+
return 'JavaScript';
|
|
203
|
+
}
|
|
204
|
+
function detectPackageManager() {
|
|
205
|
+
if (existsSync('yarn.lock'))
|
|
206
|
+
return 'Yarn';
|
|
207
|
+
if (existsSync('pnpm-lock.yaml'))
|
|
208
|
+
return 'pnpm';
|
|
209
|
+
return 'npm';
|
|
210
|
+
}
|
|
211
|
+
function generateDirectoryTree(projectPath, maxDepth) {
|
|
212
|
+
const ignoreDirs = [
|
|
213
|
+
'node_modules', '.git', 'dist', 'build', '.next', '.nuxt',
|
|
214
|
+
'coverage', '.vscode', '.idea', 'tmp', 'temp', 'out',
|
|
215
|
+
'vendor', '__pycache__', '.cache', '.parcel-cache',
|
|
216
|
+
'bower_components', 'jspm_packages'
|
|
217
|
+
];
|
|
218
|
+
const MAX_ITEMS_PER_DIR = 50; // 每个目录最多显示50项
|
|
219
|
+
function buildTree(dir, prefix = '', depth = 0) {
|
|
220
|
+
if (depth >= maxDepth)
|
|
221
|
+
return '';
|
|
222
|
+
try {
|
|
223
|
+
let items = readdirSync(dir)
|
|
224
|
+
.filter(item => !ignoreDirs.includes(item) && !item.startsWith('.'))
|
|
225
|
+
.map(item => {
|
|
226
|
+
const fullPath = join(dir, item);
|
|
227
|
+
try {
|
|
228
|
+
const stat = statSync(fullPath);
|
|
229
|
+
return { name: item, isDir: stat.isDirectory(), path: fullPath };
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
})
|
|
235
|
+
.filter(item => item !== null);
|
|
236
|
+
// 限制每个目录显示的项目数量
|
|
237
|
+
const hasMore = items.length > MAX_ITEMS_PER_DIR;
|
|
238
|
+
items = items.slice(0, MAX_ITEMS_PER_DIR);
|
|
239
|
+
items.sort((a, b) => {
|
|
240
|
+
if (a.isDir && !b.isDir)
|
|
241
|
+
return -1;
|
|
242
|
+
if (!a.isDir && b.isDir)
|
|
243
|
+
return 1;
|
|
244
|
+
return a.name.localeCompare(b.name);
|
|
245
|
+
});
|
|
246
|
+
let result = '';
|
|
247
|
+
items.forEach((item, index) => {
|
|
248
|
+
const isLast = index === items.length - 1 && !hasMore;
|
|
249
|
+
const currentPrefix = isLast ? '└── ' : '├── ';
|
|
250
|
+
const nextPrefix = isLast ? ' ' : '│ ';
|
|
251
|
+
result += `${prefix}${currentPrefix}${item.name}\n`;
|
|
252
|
+
if (item.isDir) {
|
|
253
|
+
result += buildTree(item.path, prefix + nextPrefix, depth + 1);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
if (hasMore) {
|
|
257
|
+
result += `${prefix}└── ... (更多项目被省略)\n`;
|
|
258
|
+
}
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
return '';
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return buildTree(projectPath);
|
|
266
|
+
}
|
|
267
|
+
async function identifyKeyFiles(projectPath, includeContent) {
|
|
268
|
+
const keyFilePatterns = [
|
|
269
|
+
'package.json', 'README.md', 'index.js', 'index.ts', 'main.js', 'main.ts',
|
|
270
|
+
'app.js', 'app.ts', 'server.js', 'server.ts', 'config.js', 'config.ts',
|
|
271
|
+
'webpack.config.js', 'vite.config.js', 'next.config.js', 'nuxt.config.js',
|
|
272
|
+
'tsconfig.json', 'babel.config.js', '.env', '.env.example'
|
|
273
|
+
];
|
|
274
|
+
const MAX_FILE_SIZE = 100 * 1024; // 100KB
|
|
275
|
+
const MAX_CONTENT_LINES = 100; // 最多显示100行
|
|
276
|
+
const keyFiles = [];
|
|
277
|
+
for (const pattern of keyFilePatterns) {
|
|
278
|
+
try {
|
|
279
|
+
const filePath = join(projectPath, pattern);
|
|
280
|
+
const stat = statSync(filePath);
|
|
281
|
+
// 跳过过大的文件
|
|
282
|
+
if (stat.size > MAX_FILE_SIZE) {
|
|
283
|
+
keyFiles.push({
|
|
284
|
+
path: pattern,
|
|
285
|
+
purpose: getFilePurpose(pattern, ''),
|
|
286
|
+
content: includeContent ? `[文件过大 (${Math.round(stat.size / 1024)}KB),已跳过]` : ''
|
|
287
|
+
});
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
let content = readFileSync(filePath, 'utf-8');
|
|
291
|
+
const purpose = getFilePurpose(pattern, content);
|
|
292
|
+
// 限制内容行数
|
|
293
|
+
if (includeContent && content) {
|
|
294
|
+
const lines = content.split('\n');
|
|
295
|
+
if (lines.length > MAX_CONTENT_LINES) {
|
|
296
|
+
content = lines.slice(0, MAX_CONTENT_LINES).join('\n') + `\n... (省略 ${lines.length - MAX_CONTENT_LINES} 行)`;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
keyFiles.push({
|
|
300
|
+
path: pattern,
|
|
301
|
+
purpose,
|
|
302
|
+
content: includeContent ? content : ''
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
// 文件不存在,跳过
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return keyFiles;
|
|
310
|
+
}
|
|
311
|
+
function getFilePurpose(filename, content) {
|
|
312
|
+
const purposes = {
|
|
313
|
+
'package.json': '项目配置和依赖管理',
|
|
314
|
+
'README.md': '项目说明文档',
|
|
315
|
+
'index.js': '项目入口文件',
|
|
316
|
+
'index.ts': 'TypeScript 项目入口文件',
|
|
317
|
+
'main.js': '主程序文件',
|
|
318
|
+
'main.ts': 'TypeScript 主程序文件',
|
|
319
|
+
'app.js': '应用程序主文件',
|
|
320
|
+
'app.ts': 'TypeScript 应用程序主文件',
|
|
321
|
+
'server.js': '服务器文件',
|
|
322
|
+
'server.ts': 'TypeScript 服务器文件',
|
|
323
|
+
'config.js': '配置文件',
|
|
324
|
+
'config.ts': 'TypeScript 配置文件',
|
|
325
|
+
'webpack.config.js': 'Webpack 构建配置',
|
|
326
|
+
'vite.config.js': 'Vite 构建配置',
|
|
327
|
+
'next.config.js': 'Next.js 配置',
|
|
328
|
+
'nuxt.config.js': 'Nuxt.js 配置',
|
|
329
|
+
'tsconfig.json': 'TypeScript 配置',
|
|
330
|
+
'babel.config.js': 'Babel 配置',
|
|
331
|
+
'.env': '环境变量配置',
|
|
332
|
+
'.env.example': '环境变量示例'
|
|
333
|
+
};
|
|
334
|
+
return purposes[filename] || '配置文件';
|
|
335
|
+
}
|
|
336
|
+
function analyzeDependencies(packageJson) {
|
|
337
|
+
const production = Object.keys(packageJson.dependencies || {});
|
|
338
|
+
const development = Object.keys(packageJson.devDependencies || {});
|
|
339
|
+
return {
|
|
340
|
+
production,
|
|
341
|
+
development,
|
|
342
|
+
total: production.length + development.length
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
async function calculateCodeMetrics(projectPath) {
|
|
346
|
+
const fileTypes = {};
|
|
347
|
+
const largestFiles = [];
|
|
348
|
+
const MAX_FILES_TO_SCAN = 5000; // 最多扫描5000个文件
|
|
349
|
+
const MAX_FILE_SIZE = 1 * 1024 * 1024; // 1MB
|
|
350
|
+
const SAMPLE_LARGE_PROJECTS = true; // 大项目采样
|
|
351
|
+
let totalFiles = 0;
|
|
352
|
+
let totalLines = 0;
|
|
353
|
+
let skippedFiles = 0;
|
|
354
|
+
let scannedFiles = 0;
|
|
355
|
+
function scanDirectory(dir) {
|
|
356
|
+
// 达到文件数量限制
|
|
357
|
+
if (scannedFiles >= MAX_FILES_TO_SCAN) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
try {
|
|
361
|
+
const items = readdirSync(dir);
|
|
362
|
+
for (const item of items) {
|
|
363
|
+
if (scannedFiles >= MAX_FILES_TO_SCAN)
|
|
364
|
+
break;
|
|
365
|
+
const fullPath = join(dir, item);
|
|
366
|
+
let stat;
|
|
367
|
+
try {
|
|
368
|
+
stat = statSync(fullPath);
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
if (stat.isDirectory()) {
|
|
374
|
+
const ignoreDirs = [
|
|
375
|
+
'node_modules', '.git', 'dist', 'build', '.next', '.nuxt',
|
|
376
|
+
'coverage', '.vscode', '.idea', 'tmp', 'temp', 'out',
|
|
377
|
+
'vendor', '__pycache__', '.cache', '.parcel-cache',
|
|
378
|
+
'bower_components', 'jspm_packages', 'target', 'bin', 'obj'
|
|
379
|
+
];
|
|
380
|
+
if (!ignoreDirs.includes(item) && !item.startsWith('.')) {
|
|
381
|
+
scanDirectory(fullPath);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
const ext = extname(item);
|
|
386
|
+
const fileType = ext || 'no-extension';
|
|
387
|
+
fileTypes[fileType] = (fileTypes[fileType] || 0) + 1;
|
|
388
|
+
totalFiles++;
|
|
389
|
+
scannedFiles++;
|
|
390
|
+
// 跳过过大的文件
|
|
391
|
+
if (stat.size > MAX_FILE_SIZE) {
|
|
392
|
+
skippedFiles++;
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
try {
|
|
396
|
+
// 只读取文本文件
|
|
397
|
+
const textExtensions = ['.js', '.ts', '.jsx', '.tsx', '.vue', '.py', '.java', '.go', '.rs', '.php', '.rb', '.cpp', '.c', '.h', '.cs', '.swift', '.kt'];
|
|
398
|
+
if (textExtensions.includes(ext.toLowerCase())) {
|
|
399
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
400
|
+
const lines = content.split('\n').length;
|
|
401
|
+
totalLines += lines;
|
|
402
|
+
// 只保存相对路径
|
|
403
|
+
const relativePath = fullPath.replace(projectPath, '').replace(/\\/g, '/');
|
|
404
|
+
largestFiles.push({ path: relativePath, lines });
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
catch {
|
|
408
|
+
// 忽略无法读取的文件(二进制文件等)
|
|
409
|
+
skippedFiles++;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
catch {
|
|
415
|
+
// 忽略无法访问的目录
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
console.error('开始扫描代码文件...');
|
|
419
|
+
console.error('忽略目录: node_modules, dist, build, .git 等');
|
|
420
|
+
scanDirectory(projectPath);
|
|
421
|
+
console.error(`扫描完成: ${totalFiles} 个文件, ${skippedFiles} 个跳过`);
|
|
422
|
+
// 按行数排序,取前10个
|
|
423
|
+
largestFiles.sort((a, b) => b.lines - a.lines);
|
|
424
|
+
return {
|
|
425
|
+
totalFiles,
|
|
426
|
+
totalLines,
|
|
427
|
+
fileTypes,
|
|
428
|
+
largestFiles: largestFiles.slice(0, 10),
|
|
429
|
+
skippedFiles
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
async function analyzeArchitecture(projectPath, keyFiles) {
|
|
433
|
+
const patterns = [];
|
|
434
|
+
const entryPoints = [];
|
|
435
|
+
const mainModules = [];
|
|
436
|
+
// 分析入口文件
|
|
437
|
+
keyFiles.forEach(file => {
|
|
438
|
+
if (file.path.includes('index') || file.path.includes('main') || file.path.includes('app')) {
|
|
439
|
+
entryPoints.push(file.path);
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
// 检测设计模式
|
|
443
|
+
if (keyFiles.some(f => f.path.includes('component')))
|
|
444
|
+
patterns.push('组件化');
|
|
445
|
+
if (keyFiles.some(f => f.path.includes('service')))
|
|
446
|
+
patterns.push('服务层');
|
|
447
|
+
if (keyFiles.some(f => f.path.includes('controller')))
|
|
448
|
+
patterns.push('MVC');
|
|
449
|
+
if (keyFiles.some(f => f.path.includes('middleware')))
|
|
450
|
+
patterns.push('中间件');
|
|
451
|
+
if (keyFiles.some(f => f.path.includes('hook')))
|
|
452
|
+
patterns.push('Hooks');
|
|
453
|
+
if (keyFiles.some(f => f.path.includes('store')))
|
|
454
|
+
patterns.push('状态管理');
|
|
455
|
+
// 识别核心模块
|
|
456
|
+
const srcDir = join(projectPath, 'src');
|
|
457
|
+
try {
|
|
458
|
+
const srcItems = readdirSync(srcDir);
|
|
459
|
+
mainModules.push(...srcItems.filter(item => statSync(join(srcDir, item)).isDirectory()));
|
|
460
|
+
}
|
|
461
|
+
catch {
|
|
462
|
+
// src 目录不存在
|
|
463
|
+
}
|
|
464
|
+
return { patterns, entryPoints, mainModules };
|
|
465
|
+
}
|
|
466
|
+
function generateSummary(projectStructure, codeMetrics, architecture) {
|
|
467
|
+
let complexity = 'low';
|
|
468
|
+
if (codeMetrics.totalFiles > 100 || codeMetrics.totalLines > 10000) {
|
|
469
|
+
complexity = 'high';
|
|
470
|
+
}
|
|
471
|
+
else if (codeMetrics.totalFiles > 20 || codeMetrics.totalLines > 1000) {
|
|
472
|
+
complexity = 'medium';
|
|
473
|
+
}
|
|
474
|
+
const purpose = `${projectStructure.type},使用 ${projectStructure.framework} 框架,主要语言为 ${projectStructure.language}`;
|
|
475
|
+
const recommendations = [];
|
|
476
|
+
if (codeMetrics.totalFiles > 50) {
|
|
477
|
+
recommendations.push('考虑模块化重构,减少文件数量');
|
|
478
|
+
}
|
|
479
|
+
if (codeMetrics.largestFiles[0]?.lines > 500) {
|
|
480
|
+
recommendations.push('拆分大文件,提高代码可维护性');
|
|
481
|
+
}
|
|
482
|
+
if (architecture.patterns.length === 0) {
|
|
483
|
+
recommendations.push('引入设计模式,提高代码组织性');
|
|
484
|
+
}
|
|
485
|
+
if (!architecture.entryPoints.length) {
|
|
486
|
+
recommendations.push('明确项目入口文件');
|
|
487
|
+
}
|
|
488
|
+
return { purpose, complexity, recommendations };
|
|
489
|
+
}
|
|
490
|
+
function getFileExtension(filename) {
|
|
491
|
+
const ext = extname(filename).slice(1);
|
|
492
|
+
const extMap = {
|
|
493
|
+
'js': 'javascript',
|
|
494
|
+
'ts': 'typescript',
|
|
495
|
+
'jsx': 'jsx',
|
|
496
|
+
'tsx': 'tsx',
|
|
497
|
+
'vue': 'vue',
|
|
498
|
+
'py': 'python',
|
|
499
|
+
'java': 'java',
|
|
500
|
+
'cpp': 'cpp',
|
|
501
|
+
'c': 'c',
|
|
502
|
+
'cs': 'csharp',
|
|
503
|
+
'php': 'php',
|
|
504
|
+
'rb': 'ruby',
|
|
505
|
+
'go': 'go',
|
|
506
|
+
'rs': 'rust',
|
|
507
|
+
'swift': 'swift',
|
|
508
|
+
'kt': 'kotlin',
|
|
509
|
+
'scala': 'scala',
|
|
510
|
+
'html': 'html',
|
|
511
|
+
'css': 'css',
|
|
512
|
+
'scss': 'scss',
|
|
513
|
+
'sass': 'sass',
|
|
514
|
+
'less': 'less',
|
|
515
|
+
'json': 'json',
|
|
516
|
+
'xml': 'xml',
|
|
517
|
+
'yaml': 'yaml',
|
|
518
|
+
'yml': 'yaml',
|
|
519
|
+
'md': 'markdown',
|
|
520
|
+
'txt': 'text',
|
|
521
|
+
'sql': 'sql',
|
|
522
|
+
'sh': 'bash',
|
|
523
|
+
'bat': 'batch',
|
|
524
|
+
'ps1': 'powershell'
|
|
525
|
+
};
|
|
526
|
+
return extMap[ext] || ext || 'text';
|
|
527
|
+
}
|