rules-enforcer 1.0.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.
Files changed (33) hide show
  1. package/README.md +58 -0
  2. package/detector/README.md +212 -0
  3. package/detector/decision-engine/README.md +203 -0
  4. package/detector/decision-engine/conflict-resolver.js +336 -0
  5. package/detector/decision-engine/de-verify.js +461 -0
  6. package/detector/decision-engine/index.js +204 -0
  7. package/detector/decision-engine/optimizer.js +325 -0
  8. package/detector/decision-engine/scorer.js +359 -0
  9. package/detector/knowledge-base/README.md +140 -0
  10. package/detector/knowledge-base/agent-knowledge.json +62 -0
  11. package/detector/knowledge-base/index.js +332 -0
  12. package/detector/knowledge-base/kb-verify.js +287 -0
  13. package/detector/knowledge-base/mcp-knowledge.json +135 -0
  14. package/detector/knowledge-base/rules-knowledge.json +184 -0
  15. package/detector/mcp-server.js +157 -0
  16. package/detector/mcp-service.js +118 -0
  17. package/detector/package.json +13 -0
  18. package/detector/plugin.json +122 -0
  19. package/detector/project-detector.js +710 -0
  20. package/detector/render-engine/ag-config-render.js +195 -0
  21. package/detector/render-engine/index.js +124 -0
  22. package/detector/render-engine/render-core.js +200 -0
  23. package/detector/render-engine/render-verify.js +282 -0
  24. package/detector/render-engine/rule-render.js +231 -0
  25. package/detector/test-exceptions.js +366 -0
  26. package/detector/verify-plugin.js +233 -0
  27. package/hooks/chain-invoker.js +98 -0
  28. package/hooks/custom-hook-server.js +312 -0
  29. package/hooks/mcp-hooks.js +153 -0
  30. package/hooks/validate-chain.js +147 -0
  31. package/package.json +35 -0
  32. package/rules-server.js +350 -0
  33. package/test/test-mcp-full.js +193 -0
@@ -0,0 +1,325 @@
1
+ /**
2
+ * Optimizer - 优化算法
3
+ * 基于评分结果生成最优配置组合
4
+ * 权重固定:MCP=40%, 规则=35%, Agent=25%
5
+ */
6
+
7
+ const { Scorer } = require('./scorer.js');
8
+
9
+ class Optimizer {
10
+ constructor() {
11
+ this.scorer = new Scorer();
12
+ this.weights = this.scorer.getWeights();
13
+ }
14
+
15
+ /**
16
+ * 优化MCP配置
17
+ */
18
+ optimizeMcp(mcpKnowledge, projectInfo) {
19
+ const scoredComponents = [];
20
+ const services = mcpKnowledge.mcpServices || {};
21
+
22
+ for (const [id, service] of Object.entries(services)) {
23
+ const score = this.scorer.scoreMcpComponent(service, projectInfo);
24
+ scoredComponents.push(score);
25
+ }
26
+
27
+ // 按总分降序排序
28
+ scoredComponents.sort((a, b) => b.totalScore - a.totalScore);
29
+
30
+ // 过滤已存在的组件
31
+ const existingMcp = projectInfo.existingComponents?.mcp || [];
32
+ const newComponents = scoredComponents.filter(c =>
33
+ !existingMcp.includes(c.id)
34
+ );
35
+
36
+ // 选择高分组件(最多推荐3个)
37
+ const recommended = newComponents.slice(0, 3);
38
+
39
+ return {
40
+ all: scoredComponents,
41
+ recommended,
42
+ existing: scoredComponents.filter(c =>
43
+ existingMcp.includes(c.id)
44
+ )
45
+ };
46
+ }
47
+
48
+ /**
49
+ * 优化规则配置
50
+ */
51
+ optimizeRules(rulesKnowledge, projectInfo) {
52
+ const scoredComponents = [];
53
+ const rules = rulesKnowledge.rules || {};
54
+
55
+ for (const [id, rule] of Object.entries(rules)) {
56
+ const score = this.scorer.scoreRuleComponent(rule, projectInfo);
57
+ scoredComponents.push(score);
58
+ }
59
+
60
+ // 按优先级和总分排序
61
+ scoredComponents.sort((a, b) => {
62
+ // 先按level排序:L1 > L2 > L3 > custom
63
+ const levelOrder = { 'L1': 1, 'L2': 2, 'L3': 3, 'custom': 4 };
64
+ const levelDiff = (levelOrder[a.level] || 5) - (levelOrder[b.level] || 5);
65
+ if (levelDiff !== 0) return levelDiff;
66
+
67
+ // 同级别按总分降序
68
+ return b.totalScore - a.totalScore;
69
+ });
70
+
71
+ // 过滤已存在的组件
72
+ const existingRules = (projectInfo.existingComponents?.rules || []).map(r => r.name || r);
73
+ const newComponents = scoredComponents.filter(c =>
74
+ !existingRules.includes(c.id) && !existingRules.includes(c.name)
75
+ );
76
+
77
+ // 选择高分组件
78
+ const recommended = newComponents.slice(0, 5);
79
+
80
+ return {
81
+ all: scoredComponents,
82
+ recommended,
83
+ existing: scoredComponents.filter(c =>
84
+ existingRules.includes(c.id) || existingRules.includes(c.name)
85
+ )
86
+ };
87
+ }
88
+
89
+ /**
90
+ * 优化Agent配置
91
+ */
92
+ optimizeAgent(agentKnowledge, projectInfo) {
93
+ const scoredComponents = [];
94
+ const profiles = agentKnowledge.agentProfiles || {};
95
+
96
+ for (const [id, profile] of Object.entries(profiles)) {
97
+ const score = this.scorer.scoreAgentComponent({
98
+ id,
99
+ ...profile
100
+ }, projectInfo);
101
+ scoredComponents.push(score);
102
+ }
103
+
104
+ // 按总分降序排序
105
+ scoredComponents.sort((a, b) => b.totalScore - a.totalScore);
106
+
107
+ // 选择最高分
108
+ const recommended = scoredComponents[0] || null;
109
+
110
+ return {
111
+ all: scoredComponents,
112
+ recommended
113
+ };
114
+ }
115
+
116
+ /**
117
+ * 计算综合置信度
118
+ * 基于技术栈匹配度、复杂度适配度、依赖兼容度三项加权
119
+ * 复用分单独计算,不计入置信度
120
+ */
121
+ calculateConfidence(mcpResult, rulesResult, agentResult) {
122
+ // 提取推荐组件的分项得分
123
+ const extractScores = (components) => {
124
+ if (!components || components.length === 0) {
125
+ return { techStackMatch: 50, complexityFit: 50, dependency: 50 };
126
+ }
127
+ const avg = (arr) => arr.reduce((sum, v) => sum + v, 0) / arr.length;
128
+ return {
129
+ techStackMatch: avg(components.map(c => c.scores.techStackMatch)),
130
+ complexityFit: avg(components.map(c => c.scores.complexityFit)),
131
+ dependency: avg(components.map(c => c.scores.dependency))
132
+ };
133
+ };
134
+
135
+ const mcpScores = extractScores(mcpResult.recommended);
136
+ const rulesScores = extractScores(rulesResult.recommended);
137
+ const agentScores = agentResult.recommended
138
+ ? {
139
+ techStackMatch: agentResult.recommended.scores.techStackMatch,
140
+ complexityFit: agentResult.recommended.scores.complexityFit,
141
+ dependency: agentResult.recommended.scores.dependency
142
+ }
143
+ : { techStackMatch: 50, complexityFit: 50, dependency: 50 };
144
+
145
+ // 按固定权重计算综合置信度:技术栈40%、复杂度35%、依赖25%
146
+ const techStackAvg = mcpScores.techStackMatch * this.weights.mcp +
147
+ rulesScores.techStackMatch * this.weights.rules +
148
+ agentScores.techStackMatch * this.weights.agent;
149
+
150
+ const complexityAvg = mcpScores.complexityFit * this.weights.mcp +
151
+ rulesScores.complexityFit * this.weights.rules +
152
+ agentScores.complexityFit * this.weights.agent;
153
+
154
+ const dependencyAvg = mcpScores.dependency * this.weights.mcp +
155
+ rulesScores.dependency * this.weights.rules +
156
+ agentScores.dependency * this.weights.agent;
157
+
158
+ // 置信度 = 技术栈*0.4 + 复杂度*0.35 + 依赖*0.25
159
+ const confidence = techStackAvg * this.weights.mcp +
160
+ complexityAvg * this.weights.rules +
161
+ dependencyAvg * this.weights.agent;
162
+
163
+ return Math.round(confidence) / 100; // 转换为0~1区间
164
+ }
165
+
166
+ /**
167
+ * 计算复用得分(基于已有组件被推荐组件复用的程度)
168
+ */
169
+ calculateReuseScore(mcpResult, rulesResult, agentResult) {
170
+ let reuseCount = 0;
171
+ let totalCount = 0;
172
+
173
+ // MCP复用
174
+ if (mcpResult.existing && mcpResult.existing.length > 0) {
175
+ reuseCount += mcpResult.existing.reduce((sum, c) => sum + c.scores.reuse, 0);
176
+ totalCount += mcpResult.existing.length;
177
+ }
178
+
179
+ // Rules复用
180
+ if (rulesResult.existing && rulesResult.existing.length > 0) {
181
+ reuseCount += rulesResult.existing.reduce((sum, c) => sum + c.scores.reuse, 0);
182
+ totalCount += rulesResult.existing.length;
183
+ }
184
+
185
+ // 如果没有已有组件(全是新增),复用分为0是正常的
186
+ // 如果有已有组件但复用分为0,说明评分逻辑有问题
187
+ if (totalCount === 0) {
188
+ return 0; // 全部是新增组件,复用分为0
189
+ }
190
+
191
+ return Math.round(reuseCount / totalCount);
192
+ }
193
+
194
+ /**
195
+ * 计算分项得分明细
196
+ */
197
+ calculateScoreBreakdown(mcpResult, rulesResult, agentResult) {
198
+ const extractScores = (components) => {
199
+ if (!components || components.length === 0) {
200
+ return { techStackMatch: 50, complexityFit: 50, dependency: 50, reuse: 50 };
201
+ }
202
+
203
+ const avg = (arr) => arr.reduce((sum, v) => sum + v, 0) / arr.length;
204
+
205
+ return {
206
+ techStackMatch: avg(components.map(c => c.scores.techStackMatch)),
207
+ complexityFit: avg(components.map(c => c.scores.complexityFit)),
208
+ dependency: avg(components.map(c => c.scores.dependency)),
209
+ reuse: avg(components.map(c => c.scores.reuse))
210
+ };
211
+ };
212
+
213
+ const mcpScores = extractScores(mcpResult.recommended);
214
+ const rulesScores = extractScores(rulesResult.recommended);
215
+ const agentScores = agentResult.recommended
216
+ ? agentResult.recommended.scores
217
+ : { techStackMatch: 50, complexityFit: 50, dependency: 50, reuse: 50 };
218
+
219
+ // 综合计算(复用分从已有组件中单独计算)
220
+ const reuseScore = this.calculateReuseScore(mcpResult, rulesResult, agentResult);
221
+
222
+ return {
223
+ techStackMatch: Math.round(
224
+ mcpScores.techStackMatch * this.weights.mcp +
225
+ rulesScores.techStackMatch * this.weights.rules +
226
+ agentScores.techStackMatch * this.weights.agent
227
+ ),
228
+ complexityFit: Math.round(
229
+ mcpScores.complexityFit * this.weights.mcp +
230
+ rulesScores.complexityFit * this.weights.rules +
231
+ agentScores.complexityFit * this.weights.agent
232
+ ),
233
+ dependencyScore: Math.round(
234
+ mcpScores.dependency * this.weights.mcp +
235
+ rulesScores.dependency * this.weights.rules +
236
+ agentScores.dependency * this.weights.agent
237
+ ),
238
+ reuseScore
239
+ };
240
+ }
241
+
242
+ /**
243
+ * 生成备选方案
244
+ */
245
+ generateAlternatives(mcpResult, rulesResult, agentResult) {
246
+ const alternatives = [];
247
+
248
+ // MCP备选方案
249
+ if (mcpResult.all.length > 1) {
250
+ const mcpAlt = mcpResult.all.slice(1, 3).map(c => ({
251
+ type: 'mcp',
252
+ id: c.id,
253
+ name: c.name,
254
+ score: c.totalScore
255
+ }));
256
+ alternatives.push(...mcpAlt);
257
+ }
258
+
259
+ // 规则备选方案
260
+ if (rulesResult.all.length > 1) {
261
+ const rulesAlt = rulesResult.all.slice(1, 4).map(c => ({
262
+ type: 'rule',
263
+ id: c.id,
264
+ name: c.name,
265
+ level: c.level,
266
+ score: c.totalScore
267
+ }));
268
+ alternatives.push(...rulesAlt);
269
+ }
270
+
271
+ // Agent备选方案
272
+ if (agentResult.all.length > 1) {
273
+ const agentAlt = agentResult.all.slice(1, 2).map(c => ({
274
+ type: 'agent',
275
+ id: c.id,
276
+ name: c.name,
277
+ score: c.totalScore
278
+ }));
279
+ alternatives.push(...agentAlt);
280
+ }
281
+
282
+ return alternatives;
283
+ }
284
+
285
+ /**
286
+ * 执行完整优化
287
+ */
288
+ optimize(knowledgeBase, projectInfo) {
289
+ // 优化各组件
290
+ const mcpResult = this.optimizeMcp(
291
+ knowledgeBase.mcpKnowledge,
292
+ projectInfo
293
+ );
294
+
295
+ const rulesResult = this.optimizeRules(
296
+ knowledgeBase.rulesKnowledge,
297
+ projectInfo
298
+ );
299
+
300
+ const agentResult = this.optimizeAgent(
301
+ knowledgeBase.agentKnowledge,
302
+ projectInfo
303
+ );
304
+
305
+ // 计算置信度
306
+ const confidence = this.calculateConfidence(mcpResult, rulesResult, agentResult);
307
+
308
+ // 计算分项得分
309
+ const scoreBreakdown = this.calculateScoreBreakdown(mcpResult, rulesResult, agentResult);
310
+
311
+ // 生成备选方案
312
+ const alternatives = this.generateAlternatives(mcpResult, rulesResult, agentResult);
313
+
314
+ return {
315
+ mcpResult,
316
+ rulesResult,
317
+ agentResult,
318
+ confidence,
319
+ scoreBreakdown,
320
+ alternatives
321
+ };
322
+ }
323
+ }
324
+
325
+ module.exports = { Optimizer };
@@ -0,0 +1,359 @@
1
+ /**
2
+ * Scorer - 评分引擎
3
+ * 四项打分维度:技术栈匹配/复杂度适配/依赖兼容/已有组件复用
4
+ * 分值区间:0~100
5
+ */
6
+
7
+ class Scorer {
8
+ constructor() {
9
+ // 固定权重配置(预留外置接口)
10
+ this.weights = {
11
+ mcp: 0.40, // MCP权重 40%
12
+ rules: 0.35, // 规则权重 35%
13
+ agent: 0.25 // Agent权重 25%
14
+ };
15
+ }
16
+
17
+ /**
18
+ * 计算技术栈匹配分 (0~100)
19
+ */
20
+ calculateTechStackMatchScore(projectTechStack, componentTechStack) {
21
+ if (!projectTechStack || projectTechStack.length === 0) {
22
+ return 50; // 无技术栈信息,返回中等分数
23
+ }
24
+
25
+ if (!componentTechStack || componentTechStack.length === 0) {
26
+ return 50; // 组件无技术栈限制,返回中等分数
27
+ }
28
+
29
+ // 检查是否包含"any"通配符
30
+ if (componentTechStack.includes('any')) {
31
+ return 90; // 通用组件,高分
32
+ }
33
+
34
+ let matchCount = 0;
35
+ let totalScore = 0;
36
+
37
+ for (const tech of projectTechStack) {
38
+ const normalizedTech = tech.toLowerCase();
39
+ for (const pattern of componentTechStack) {
40
+ const normalizedPattern = pattern.toLowerCase();
41
+
42
+ if (normalizedTech === normalizedPattern) {
43
+ totalScore += 100; // 完全匹配
44
+ matchCount++;
45
+ } else if (normalizedTech.includes(normalizedPattern)) {
46
+ totalScore += 80; // 包含匹配
47
+ matchCount++;
48
+ } else if (normalizedPattern.includes(normalizedTech)) {
49
+ totalScore += 70; // 被包含匹配
50
+ matchCount++;
51
+ }
52
+ }
53
+ }
54
+
55
+ if (matchCount === 0) {
56
+ return 20; // 无匹配,低分
57
+ }
58
+
59
+ return Math.min(100, Math.round(totalScore / matchCount));
60
+ }
61
+
62
+ /**
63
+ * 计算复杂度适配分 (0~100)
64
+ */
65
+ calculateComplexityFitScore(projectComplexity, componentSizeRange) {
66
+ if (!projectComplexity || !componentSizeRange) {
67
+ return 50;
68
+ }
69
+
70
+ const complexityMap = {
71
+ 'simple': 1,
72
+ 'medium': 2,
73
+ 'complex': 3
74
+ };
75
+
76
+ const projectLevel = complexityMap[projectComplexity] || 2;
77
+
78
+ // 检查组件是否支持该项目规模
79
+ if (componentSizeRange.includes(projectComplexity)) {
80
+ return 100; // 完全支持
81
+ }
82
+
83
+ // 检查相邻规模支持
84
+ const supportedLevels = componentSizeRange.map(s => complexityMap[s] || 2);
85
+ const minLevel = Math.min(...supportedLevels);
86
+ const maxLevel = Math.max(...supportedLevels);
87
+
88
+ if (projectLevel >= minLevel && projectLevel <= maxLevel) {
89
+ return 90; // 范围内支持
90
+ }
91
+
92
+ // 计算距离分数
93
+ const distance = Math.min(
94
+ Math.abs(projectLevel - minLevel),
95
+ Math.abs(projectLevel - maxLevel)
96
+ );
97
+
98
+ return Math.max(30, 100 - distance * 25);
99
+ }
100
+
101
+ /**
102
+ * 计算依赖兼容分 (0~100)
103
+ */
104
+ calculateDependencyScore(componentDependencies, existingComponents) {
105
+ if (!componentDependencies || componentDependencies.length === 0) {
106
+ return 100; // 无依赖,满分
107
+ }
108
+
109
+ if (!existingComponents) {
110
+ existingComponents = { mcp: [], agents: [], rules: [] };
111
+ }
112
+
113
+ let satisfiedCount = 0;
114
+ const allExisting = [
115
+ ...existingComponents.mcp,
116
+ ...existingComponents.agents.map(a => a.name || a),
117
+ ...existingComponents.rules.map(r => r.name || r)
118
+ ];
119
+
120
+ for (const dep of componentDependencies) {
121
+ if (allExisting.some(e =>
122
+ (e.id && e.id === dep) ||
123
+ (e.name && e.name === dep) ||
124
+ (typeof e === 'string' && e === dep)
125
+ )) {
126
+ satisfiedCount++;
127
+ }
128
+ }
129
+
130
+ if (satisfiedCount === componentDependencies.length) {
131
+ return 100; // 全部依赖满足
132
+ }
133
+
134
+ return Math.round((satisfiedCount / componentDependencies.length) * 100);
135
+ }
136
+
137
+ /**
138
+ * 计算复用得分(基于项目已有组件)
139
+ */
140
+ calculateReuseScore(componentId, existingComponents) {
141
+ if (!existingComponents || !componentId) {
142
+ return 0;
143
+ }
144
+
145
+ // 收集所有已有组件
146
+ const allExisting = [];
147
+
148
+ // MCP组件(直接是字符串ID数组)
149
+ if (Array.isArray(existingComponents.mcp)) {
150
+ allExisting.push(...existingComponents.mcp);
151
+ }
152
+
153
+ // Agent组件(可能是对象数组)
154
+ if (Array.isArray(existingComponents.agents)) {
155
+ for (const agent of existingComponents.agents) {
156
+ if (typeof agent === 'string') {
157
+ allExisting.push(agent);
158
+ } else if (agent.name) {
159
+ allExisting.push(agent.name);
160
+ } else if (agent.id) {
161
+ allExisting.push(agent.id);
162
+ }
163
+ }
164
+ }
165
+
166
+ // Rule组件(可能是对象数组)
167
+ if (Array.isArray(existingComponents.rules)) {
168
+ for (const rule of existingComponents.rules) {
169
+ if (typeof rule === 'string') {
170
+ allExisting.push(rule);
171
+ } else if (rule.name) {
172
+ allExisting.push(rule.name);
173
+ } else if (rule.id) {
174
+ allExisting.push(rule.id);
175
+ }
176
+ }
177
+ }
178
+
179
+ // 精确匹配或ID部分匹配
180
+ const normalizedId = componentId.toLowerCase();
181
+ for (const existing of allExisting) {
182
+ if (typeof existing === 'string') {
183
+ const normalizedExisting = existing.toLowerCase();
184
+ // 精确匹配
185
+ if (normalizedExisting === normalizedId) {
186
+ return 100;
187
+ }
188
+ // 包含匹配(如 'rules-enforcer' 匹配 'rules-enforcer-mcp')
189
+ if (normalizedId.includes(normalizedExisting) || normalizedExisting.includes(normalizedId)) {
190
+ return 100;
191
+ }
192
+ }
193
+ }
194
+
195
+ return 0; // 不存在,0分
196
+ }
197
+
198
+ /**
199
+ * 计算MCP组件综合分
200
+ */
201
+ scoreMcpComponent(mcpComponent, projectInfo) {
202
+ const techStackScore = this.calculateTechStackMatchScore(
203
+ projectInfo.techStack,
204
+ mcpComponent.techStackMatch
205
+ );
206
+
207
+ const complexityScore = this.calculateComplexityFitScore(
208
+ projectInfo.complexity,
209
+ mcpComponent.projectSizeRange
210
+ );
211
+
212
+ const dependencyScore = this.calculateDependencyScore(
213
+ mcpComponent.dependencies,
214
+ projectInfo.existingComponents
215
+ );
216
+
217
+ const reuseScore = this.calculateReuseScore(
218
+ mcpComponent.id,
219
+ projectInfo.existingComponents
220
+ );
221
+
222
+ // 综合分 = (技术栈*0.4 + 复杂度*0.3 + 依赖*0.2 + 复用*0.1)
223
+ const totalScore = Math.round(
224
+ techStackScore * 0.4 +
225
+ complexityScore * 0.3 +
226
+ dependencyScore * 0.2 +
227
+ reuseScore * 0.1
228
+ );
229
+
230
+ return {
231
+ id: mcpComponent.id,
232
+ name: mcpComponent.name,
233
+ type: mcpComponent.type,
234
+ scores: {
235
+ techStackMatch: techStackScore,
236
+ complexityFit: complexityScore,
237
+ dependency: dependencyScore,
238
+ reuse: reuseScore
239
+ },
240
+ totalScore,
241
+ weight: this.weights.mcp
242
+ };
243
+ }
244
+
245
+ /**
246
+ * 计算规则组件综合分
247
+ */
248
+ scoreRuleComponent(ruleComponent, projectInfo) {
249
+ const techStackScore = this.calculateTechStackMatchScore(
250
+ projectInfo.techStack,
251
+ ruleComponent.techStackMatch
252
+ );
253
+
254
+ const complexityScore = this.calculateComplexityFitScore(
255
+ projectInfo.complexity,
256
+ ruleComponent.projectSizeRange
257
+ );
258
+
259
+ const reuseScore = this.calculateReuseScore(
260
+ ruleComponent.id,
261
+ projectInfo.existingComponents
262
+ );
263
+
264
+ // 规则通常无依赖
265
+ const dependencyScore = 100;
266
+
267
+ // 综合分 = (技术栈*0.4 + 复杂度*0.3 + 依赖*0.1 + 复用*0.2)
268
+ const totalScore = Math.round(
269
+ techStackScore * 0.4 +
270
+ complexityScore * 0.3 +
271
+ dependencyScore * 0.1 +
272
+ reuseScore * 0.2
273
+ );
274
+
275
+ return {
276
+ id: ruleComponent.id,
277
+ name: ruleComponent.name,
278
+ level: ruleComponent.level,
279
+ module: ruleComponent.module,
280
+ scores: {
281
+ techStackMatch: techStackScore,
282
+ complexityFit: complexityScore,
283
+ dependency: dependencyScore,
284
+ reuse: reuseScore
285
+ },
286
+ totalScore,
287
+ weight: this.weights.rules
288
+ };
289
+ }
290
+
291
+ /**
292
+ * 计算Agent组件综合分
293
+ */
294
+ scoreAgentComponent(agentComponent, projectInfo) {
295
+ const techStackScore = this.calculateTechStackMatchScore(
296
+ projectInfo.techStack,
297
+ agentComponent.techStack
298
+ );
299
+
300
+ const complexityScore = this.calculateComplexityFitScore(
301
+ projectInfo.complexity,
302
+ agentComponent.projectSizeRange || ['small', 'medium', 'large']
303
+ );
304
+
305
+ const dependencyScore = this.calculateDependencyScore(
306
+ agentComponent.recommendedMcp,
307
+ projectInfo.existingComponents
308
+ );
309
+
310
+ const reuseScore = this.calculateReuseScore(
311
+ agentComponent.id,
312
+ projectInfo.existingComponents
313
+ );
314
+
315
+ // 综合分 = (技术栈*0.5 + 复杂度*0.2 + 依赖*0.2 + 复用*0.1)
316
+ const totalScore = Math.round(
317
+ techStackScore * 0.5 +
318
+ complexityScore * 0.2 +
319
+ dependencyScore * 0.2 +
320
+ reuseScore * 0.1
321
+ );
322
+
323
+ return {
324
+ id: agentComponent.id,
325
+ name: agentComponent.name,
326
+ scores: {
327
+ techStackMatch: techStackScore,
328
+ complexityFit: complexityScore,
329
+ dependency: dependencyScore,
330
+ reuse: reuseScore
331
+ },
332
+ totalScore,
333
+ weight: this.weights.agent
334
+ };
335
+ }
336
+
337
+ /**
338
+ * 获取当前权重配置
339
+ */
340
+ getWeights() {
341
+ return { ...this.weights };
342
+ }
343
+
344
+ /**
345
+ * 设置权重配置(预留外置接口)
346
+ */
347
+ setWeights(newWeights) {
348
+ // 验证权重总和为1
349
+ const sum = (newWeights.mcp || 0) + (newWeights.rules || 0) + (newWeights.agent || 0);
350
+ if (Math.abs(sum - 1) > 0.001) {
351
+ throw new Error(`权重总和必须为1,当前总和: ${sum}`);
352
+ }
353
+
354
+ this.weights = { ...newWeights };
355
+ return this.weights;
356
+ }
357
+ }
358
+
359
+ module.exports = { Scorer };