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.
- package/README.md +58 -0
- package/detector/README.md +212 -0
- package/detector/decision-engine/README.md +203 -0
- package/detector/decision-engine/conflict-resolver.js +336 -0
- package/detector/decision-engine/de-verify.js +461 -0
- package/detector/decision-engine/index.js +204 -0
- package/detector/decision-engine/optimizer.js +325 -0
- package/detector/decision-engine/scorer.js +359 -0
- package/detector/knowledge-base/README.md +140 -0
- package/detector/knowledge-base/agent-knowledge.json +62 -0
- package/detector/knowledge-base/index.js +332 -0
- package/detector/knowledge-base/kb-verify.js +287 -0
- package/detector/knowledge-base/mcp-knowledge.json +135 -0
- package/detector/knowledge-base/rules-knowledge.json +184 -0
- package/detector/mcp-server.js +157 -0
- package/detector/mcp-service.js +118 -0
- package/detector/package.json +13 -0
- package/detector/plugin.json +122 -0
- package/detector/project-detector.js +710 -0
- package/detector/render-engine/ag-config-render.js +195 -0
- package/detector/render-engine/index.js +124 -0
- package/detector/render-engine/render-core.js +200 -0
- package/detector/render-engine/render-verify.js +282 -0
- package/detector/render-engine/rule-render.js +231 -0
- package/detector/test-exceptions.js +366 -0
- package/detector/verify-plugin.js +233 -0
- package/hooks/chain-invoker.js +98 -0
- package/hooks/custom-hook-server.js +312 -0
- package/hooks/mcp-hooks.js +153 -0
- package/hooks/validate-chain.js +147 -0
- package/package.json +35 -0
- package/rules-server.js +350 -0
- package/test/test-mcp-full.js +193 -0
package/rules-server.js
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
const readline = require('readline');
|
|
2
|
+
const { stdin, stdout } = require('process');
|
|
3
|
+
|
|
4
|
+
const l1Rules = {
|
|
5
|
+
codeQuality: {
|
|
6
|
+
name: "代码质量检查",
|
|
7
|
+
description: "TypeScript严格模式、ESLint检查",
|
|
8
|
+
validate: (code) => {
|
|
9
|
+
const issues = [];
|
|
10
|
+
if (code.includes('var ')) {
|
|
11
|
+
issues.push({ rule: 'L1-001', message: '使用var声明变量,应使用const或let', severity: 'warning' });
|
|
12
|
+
}
|
|
13
|
+
if (code.includes('console.log(')) {
|
|
14
|
+
issues.push({ rule: 'L1-002', message: '存在console.log,生产环境应移除', severity: 'info' });
|
|
15
|
+
}
|
|
16
|
+
return issues;
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
simplicity: {
|
|
20
|
+
name: "简约原则检查",
|
|
21
|
+
description: "代码应简洁明了",
|
|
22
|
+
validate: (code) => {
|
|
23
|
+
const issues = [];
|
|
24
|
+
const lines = code.split('\n');
|
|
25
|
+
for (let i = 0; i < lines.length; i++) {
|
|
26
|
+
if (lines[i].length > 120) {
|
|
27
|
+
issues.push({ rule: 'L1-003', message: `第${i + 1}行超过120字符`, severity: 'warning' });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (lines.length > 50) {
|
|
31
|
+
issues.push({ rule: 'L1-004', message: '函数超过50行,建议拆分', severity: 'info' });
|
|
32
|
+
}
|
|
33
|
+
return issues;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const l2Rules = {
|
|
39
|
+
singleResponsibility: {
|
|
40
|
+
name: "单一职责检查",
|
|
41
|
+
description: "每个类/函数只负责一件事",
|
|
42
|
+
validate: (code) => {
|
|
43
|
+
const issues = [];
|
|
44
|
+
const functionMatches = code.match(/function\s+\w+\s*\([^)]*\)\s*\{/g);
|
|
45
|
+
if (functionMatches) {
|
|
46
|
+
functionMatches.forEach((func) => {
|
|
47
|
+
const funcName = func.match(/function\s+(\w+)/)[1];
|
|
48
|
+
if (funcName.includes('And') || funcName.includes('Or')) {
|
|
49
|
+
issues.push({ rule: 'L2-001', message: `函数${funcName}可能违反单一职责原则`, severity: 'warning' });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return issues;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
directoryStructure: {
|
|
57
|
+
name: "目录结构检查",
|
|
58
|
+
description: "符合项目目录结构规范",
|
|
59
|
+
validate: (code, context) => {
|
|
60
|
+
const issues = [];
|
|
61
|
+
if (context && context.filePath) {
|
|
62
|
+
const path = context.filePath.toLowerCase();
|
|
63
|
+
if (path.includes('controllers') && !path.endsWith('.js') && !path.endsWith('.ts')) {
|
|
64
|
+
issues.push({ rule: 'L2-002', message: `控制器文件应使用.js或.ts扩展名`, severity: 'error' });
|
|
65
|
+
}
|
|
66
|
+
if (path.includes('routes') && !path.endsWith('.js') && !path.endsWith('.ts')) {
|
|
67
|
+
issues.push({ rule: 'L2-003', message: `路由文件应使用.js或.ts扩展名`, severity: 'error' });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return issues;
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
namingConvention: {
|
|
74
|
+
name: "命名规范检查",
|
|
75
|
+
description: "符合项目命名规范",
|
|
76
|
+
validate: (code) => {
|
|
77
|
+
const issues = [];
|
|
78
|
+
const camelCaseRegex = /\b[A-Z][a-zA-Z0-9]*\b/g;
|
|
79
|
+
const matches = code.match(camelCaseRegex);
|
|
80
|
+
if (matches) {
|
|
81
|
+
matches.forEach(match => {
|
|
82
|
+
if (match.length > 1 && !['class', 'function', 'const', 'let', 'var', 'if', 'else', 'for', 'while', 'return', 'import', 'export', 'from', 'async', 'await', 'new', 'this', 'try', 'catch', 'throw'].includes(match.toLowerCase())) {
|
|
83
|
+
if (!/^[A-Z]/.test(match)) {
|
|
84
|
+
issues.push({ rule: 'L2-004', message: `类名${match}应使用PascalCase`, severity: 'warning' });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return issues;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const l3Rules = {
|
|
95
|
+
dry: {
|
|
96
|
+
name: "DRY原则检查",
|
|
97
|
+
description: "避免重复代码",
|
|
98
|
+
validate: (code) => {
|
|
99
|
+
const issues = [];
|
|
100
|
+
const lines = code.split('\n');
|
|
101
|
+
const lineCounts = {};
|
|
102
|
+
lines.forEach(line => {
|
|
103
|
+
const trimmed = line.trim();
|
|
104
|
+
if (trimmed && trimmed.length > 20) {
|
|
105
|
+
lineCounts[trimmed] = (lineCounts[trimmed] || 0) + 1;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
Object.entries(lineCounts).forEach(([line, count]) => {
|
|
109
|
+
if (count >= 3) {
|
|
110
|
+
issues.push({ rule: 'L3-001', message: `发现重复代码片段,出现${count}次,应提取为函数`, severity: 'warning' });
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
return issues;
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
solid: {
|
|
117
|
+
name: "SOLID原则检查",
|
|
118
|
+
description: "符合SOLID设计原则",
|
|
119
|
+
validate: (code) => {
|
|
120
|
+
const issues = [];
|
|
121
|
+
if (code.includes('extends') && code.includes('implements')) {
|
|
122
|
+
issues.push({ rule: 'L3-002', message: '同时使用extends和implements,建议检查是否符合里氏替换原则', severity: 'info' });
|
|
123
|
+
}
|
|
124
|
+
return issues;
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
dataStructure: {
|
|
128
|
+
name: "数据结构优先检查",
|
|
129
|
+
description: "使用合适的数据结构",
|
|
130
|
+
validate: (code) => {
|
|
131
|
+
const issues = [];
|
|
132
|
+
if (code.includes('for (let i = 0; i < ') && code.includes('.length')) {
|
|
133
|
+
issues.push({ rule: 'L3-003', message: '使用普通for循环,考虑使用forEach或map', severity: 'info' });
|
|
134
|
+
}
|
|
135
|
+
return issues;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
function validateCode(code, context = {}) {
|
|
141
|
+
const results = [];
|
|
142
|
+
Object.values(l1Rules).forEach(rule => {
|
|
143
|
+
const issues = rule.validate(code, context);
|
|
144
|
+
issues.forEach(issue => {
|
|
145
|
+
results.push({ ...issue, level: 'L1', ruleName: rule.name });
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
Object.values(l2Rules).forEach(rule => {
|
|
149
|
+
const issues = rule.validate(code, context);
|
|
150
|
+
issues.forEach(issue => {
|
|
151
|
+
results.push({ ...issue, level: 'L2', ruleName: rule.name });
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
Object.values(l3Rules).forEach(rule => {
|
|
155
|
+
const issues = rule.validate(code, context);
|
|
156
|
+
issues.forEach(issue => {
|
|
157
|
+
results.push({ ...issue, level: 'L3', ruleName: rule.name });
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
return results;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function getRules() {
|
|
164
|
+
return {
|
|
165
|
+
L1: Object.keys(l1Rules).map(key => ({
|
|
166
|
+
id: key,
|
|
167
|
+
name: l1Rules[key].name,
|
|
168
|
+
description: l1Rules[key].description
|
|
169
|
+
})),
|
|
170
|
+
L2: Object.keys(l2Rules).map(key => ({
|
|
171
|
+
id: key,
|
|
172
|
+
name: l2Rules[key].name,
|
|
173
|
+
description: l2Rules[key].description
|
|
174
|
+
})),
|
|
175
|
+
L3: Object.keys(l3Rules).map(key => ({
|
|
176
|
+
id: key,
|
|
177
|
+
name: l3Rules[key].name,
|
|
178
|
+
description: l3Rules[key].description
|
|
179
|
+
}))
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const rl = readline.createInterface({
|
|
184
|
+
input: stdin,
|
|
185
|
+
output: stdout,
|
|
186
|
+
terminal: false
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
rl.on('line', (input) => {
|
|
190
|
+
try {
|
|
191
|
+
const request = JSON.parse(input);
|
|
192
|
+
const { id, method, params } = request;
|
|
193
|
+
let response;
|
|
194
|
+
|
|
195
|
+
if (method === 'initialize') {
|
|
196
|
+
response = {
|
|
197
|
+
jsonrpc: "2.0",
|
|
198
|
+
id: id,
|
|
199
|
+
result: {
|
|
200
|
+
protocolVersion: "2024-11-05",
|
|
201
|
+
capabilities: {
|
|
202
|
+
tools: {}
|
|
203
|
+
},
|
|
204
|
+
serverInfo: {
|
|
205
|
+
name: "rules-enforcer",
|
|
206
|
+
version: "1.0.0"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
} else if (method === 'notifications/initialized') {
|
|
211
|
+
return;
|
|
212
|
+
} else if (method === 'tools/list') {
|
|
213
|
+
response = {
|
|
214
|
+
jsonrpc: "2.0",
|
|
215
|
+
id: id,
|
|
216
|
+
result: {
|
|
217
|
+
tools: [
|
|
218
|
+
{
|
|
219
|
+
name: "validate",
|
|
220
|
+
description: "验证代码是否符合L1/L2/L3规则",
|
|
221
|
+
inputSchema: {
|
|
222
|
+
type: "object",
|
|
223
|
+
properties: {
|
|
224
|
+
code: { type: "string", description: "要验证的代码" },
|
|
225
|
+
context: { type: "object", description: "上下文信息" }
|
|
226
|
+
},
|
|
227
|
+
required: ["code"]
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: "get_rules",
|
|
232
|
+
description: "获取所有规则定义",
|
|
233
|
+
inputSchema: {
|
|
234
|
+
type: "object",
|
|
235
|
+
properties: {}
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: "enforce",
|
|
240
|
+
description: "强制执行规则验证,不通过则拒绝",
|
|
241
|
+
inputSchema: {
|
|
242
|
+
type: "object",
|
|
243
|
+
properties: {
|
|
244
|
+
code: { type: "string", description: "要验证的代码" },
|
|
245
|
+
context: { type: "object", description: "上下文信息" }
|
|
246
|
+
},
|
|
247
|
+
required: ["code"]
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
} else if (method === 'tools/call') {
|
|
254
|
+
const toolName = params?.name;
|
|
255
|
+
const toolArgs = params?.arguments || {};
|
|
256
|
+
|
|
257
|
+
if (toolName === 'validate') {
|
|
258
|
+
const issues = validateCode(toolArgs.code, toolArgs.context);
|
|
259
|
+
response = {
|
|
260
|
+
jsonrpc: "2.0",
|
|
261
|
+
id: id,
|
|
262
|
+
result: {
|
|
263
|
+
content: [
|
|
264
|
+
{
|
|
265
|
+
type: "text",
|
|
266
|
+
text: JSON.stringify({
|
|
267
|
+
success: true,
|
|
268
|
+
issues,
|
|
269
|
+
summary: {
|
|
270
|
+
total: issues.length,
|
|
271
|
+
errors: issues.filter(i => i.severity === 'error').length,
|
|
272
|
+
warnings: issues.filter(i => i.severity === 'warning').length,
|
|
273
|
+
info: issues.filter(i => i.severity === 'info').length
|
|
274
|
+
}
|
|
275
|
+
})
|
|
276
|
+
}
|
|
277
|
+
]
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
} else if (toolName === 'get_rules') {
|
|
281
|
+
response = {
|
|
282
|
+
jsonrpc: "2.0",
|
|
283
|
+
id: id,
|
|
284
|
+
result: {
|
|
285
|
+
content: [
|
|
286
|
+
{
|
|
287
|
+
type: "text",
|
|
288
|
+
text: JSON.stringify({
|
|
289
|
+
success: true,
|
|
290
|
+
rules: getRules()
|
|
291
|
+
})
|
|
292
|
+
}
|
|
293
|
+
]
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
} else if (toolName === 'enforce') {
|
|
297
|
+
const issues = validateCode(toolArgs.code, toolArgs.context);
|
|
298
|
+
const errors = issues.filter(i => i.severity === 'error');
|
|
299
|
+
const status = errors.length === 0 ? 'accepted' : 'rejected';
|
|
300
|
+
response = {
|
|
301
|
+
jsonrpc: "2.0",
|
|
302
|
+
id: id,
|
|
303
|
+
result: {
|
|
304
|
+
content: [
|
|
305
|
+
{
|
|
306
|
+
type: "text",
|
|
307
|
+
text: JSON.stringify({
|
|
308
|
+
success: true,
|
|
309
|
+
status,
|
|
310
|
+
issues,
|
|
311
|
+
errors
|
|
312
|
+
})
|
|
313
|
+
}
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
} else {
|
|
318
|
+
response = {
|
|
319
|
+
jsonrpc: "2.0",
|
|
320
|
+
id: id,
|
|
321
|
+
error: {
|
|
322
|
+
code: -32601,
|
|
323
|
+
message: 'Tool not found'
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
} else {
|
|
328
|
+
response = {
|
|
329
|
+
jsonrpc: "2.0",
|
|
330
|
+
id: id,
|
|
331
|
+
error: {
|
|
332
|
+
code: -32601,
|
|
333
|
+
message: 'Method not found'
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
stdout.write(JSON.stringify(response) + '\n');
|
|
339
|
+
} catch (error) {
|
|
340
|
+
console.error('Error:', error.message);
|
|
341
|
+
stdout.write(JSON.stringify({
|
|
342
|
+
jsonrpc: "2.0",
|
|
343
|
+
id: null,
|
|
344
|
+
error: {
|
|
345
|
+
code: -32603,
|
|
346
|
+
message: 'Internal error'
|
|
347
|
+
}
|
|
348
|
+
}) + '\n');
|
|
349
|
+
}
|
|
350
|
+
});
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP 全链路测试脚本
|
|
3
|
+
*
|
|
4
|
+
* 用途:
|
|
5
|
+
* 1. 检查关键文件存在性
|
|
6
|
+
* 2. 验证 MCP 配置正确性
|
|
7
|
+
* 3. 验证双规则目录存在
|
|
8
|
+
* 4. 验证 G 记忆五级目录
|
|
9
|
+
* 5. 测试 MCP 协议通信和工具列表
|
|
10
|
+
*
|
|
11
|
+
* 运行方式:
|
|
12
|
+
* cd .trae/mcp/rules-enforcer
|
|
13
|
+
* node test/test-mcp-full.js
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { spawn } = require('child_process');
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
// 路径配置 - 更新为合并后的结构
|
|
21
|
+
const RULES_ENFORCER_DIR = path.join(__dirname, '..'); // rules-enforcer 目录
|
|
22
|
+
const MCP_ROOT = path.join(RULES_ENFORCER_DIR, '..'); // .trae/mcp 目录
|
|
23
|
+
const PROJECT_ROOT = path.join(MCP_ROOT, '..'); // 项目根目录
|
|
24
|
+
const MCP_SERVER = path.join(RULES_ENFORCER_DIR, 'rules-server.js');
|
|
25
|
+
const HOOKS_SERVER = path.join(RULES_ENFORCER_DIR, 'hooks', 'custom-hook-server.js');
|
|
26
|
+
const DETECTOR_SERVER = path.join(RULES_ENFORCER_DIR, 'detector', 'mcp-server.js');
|
|
27
|
+
const DB_PATH = path.join(PROJECT_ROOT, '.trae', 'database', 'rzero-test.db');
|
|
28
|
+
const RULES_DIR = path.join(PROJECT_ROOT, '.trae', 'rules');
|
|
29
|
+
const GMEMORY_DIR = path.join(PROJECT_ROOT, '.trae', 'g_memory');
|
|
30
|
+
|
|
31
|
+
console.log('='.repeat(60));
|
|
32
|
+
console.log('MCP 全链路测试 (rules-enforcer 合并版)');
|
|
33
|
+
console.log('='.repeat(60));
|
|
34
|
+
console.log();
|
|
35
|
+
|
|
36
|
+
console.log('[1] 检查关键目录');
|
|
37
|
+
console.log(' rules-enforcer:', fs.existsSync(RULES_ENFORCER_DIR) ? '✅ 存在' : '❌ 缺失');
|
|
38
|
+
console.log(' hooks 子目录:', fs.existsSync(path.join(RULES_ENFORCER_DIR, 'hooks')) ? '✅ 存在' : '❌ 缺失');
|
|
39
|
+
console.log(' detector 子目录:', fs.existsSync(path.join(RULES_ENFORCER_DIR, 'detector')) ? '✅ 存在' : '❌ 缺失');
|
|
40
|
+
console.log(' test 子目录:', fs.existsSync(path.join(RULES_ENFORCER_DIR, 'test')) ? '✅ 存在' : '❌ 缺失');
|
|
41
|
+
console.log();
|
|
42
|
+
|
|
43
|
+
console.log('[2] MCP 服务入口检查');
|
|
44
|
+
console.log(' rules-server.js:', fs.existsSync(MCP_SERVER) ? '✅ 存在' : '❌ 缺失');
|
|
45
|
+
console.log(' hooks/server.js:', fs.existsSync(HOOKS_SERVER) ? '✅ 存在' : '❌ 缺失');
|
|
46
|
+
console.log(' detector/server.js:', fs.existsSync(DETECTOR_SERVER) ? '✅ 存在' : '❌ 缺失');
|
|
47
|
+
console.log();
|
|
48
|
+
|
|
49
|
+
console.log('[3] SQLite 数据库检查');
|
|
50
|
+
console.log(' 数据库文件:', fs.existsSync(DB_PATH) ? '✅ 存在' : '⚠️ 缺失');
|
|
51
|
+
if (fs.existsSync(DB_PATH)) {
|
|
52
|
+
const stats = fs.statSync(DB_PATH);
|
|
53
|
+
console.log(' 文件大小:', stats.size, 'bytes');
|
|
54
|
+
}
|
|
55
|
+
console.log();
|
|
56
|
+
|
|
57
|
+
console.log('[4] MCP 配置检查');
|
|
58
|
+
const projectMcp = path.join(MCP_ROOT, 'mcp.json');
|
|
59
|
+
console.log(' 项目配置:', fs.existsSync(projectMcp) ? '✅ 存在' : '❌ 缺失');
|
|
60
|
+
|
|
61
|
+
if (fs.existsSync(projectMcp)) {
|
|
62
|
+
try {
|
|
63
|
+
const mcpConfig = JSON.parse(fs.readFileSync(projectMcp, 'utf8'));
|
|
64
|
+
const services = Object.keys(mcpConfig.mcpServers || {});
|
|
65
|
+
console.log(' 注册服务:', services.join(', '));
|
|
66
|
+
|
|
67
|
+
// 检查是否只有一个 rules-enforcer
|
|
68
|
+
if (services.length === 1 && services[0] === 'rules-enforcer') {
|
|
69
|
+
console.log(' ✅ 配置正确: 只注册了 rules-enforcer');
|
|
70
|
+
} else {
|
|
71
|
+
console.log(' ⚠️ 配置异常: 应只注册 rules-enforcer');
|
|
72
|
+
}
|
|
73
|
+
} catch (err) {
|
|
74
|
+
console.warn(' ⚠️ 项目配置解析失败');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
console.log();
|
|
78
|
+
|
|
79
|
+
console.log('[5] 双规则目录检查');
|
|
80
|
+
const rzeroDir = path.join(RULES_DIR, 'L3', 'RZero');
|
|
81
|
+
const customDir = path.join(RULES_DIR, 'custom_config');
|
|
82
|
+
console.log(' RZero 规则:', fs.existsSync(rzeroDir) ? '✅ 存在' : '❌ 缺失');
|
|
83
|
+
console.log(' Custom 规则:', fs.existsSync(customDir) ? '✅ 存在' : '❌ 缺失');
|
|
84
|
+
console.log();
|
|
85
|
+
|
|
86
|
+
console.log('[6] G 记忆五级目录检查');
|
|
87
|
+
const gmemoryDirs = ['短期记忆', '中期记忆', '长期记忆', '明知识', '暗知识'];
|
|
88
|
+
let gmemoryCount = 0;
|
|
89
|
+
gmemoryDirs.forEach(dir => {
|
|
90
|
+
const dirPath = path.join(GMEMORY_DIR, dir);
|
|
91
|
+
const exists = fs.existsSync(dirPath);
|
|
92
|
+
console.log(` ${exists ? '✅' : '⚠️'} ${dir}`);
|
|
93
|
+
if (exists) gmemoryCount++;
|
|
94
|
+
});
|
|
95
|
+
console.log(` 目录存在: ${gmemoryCount}/${gmemoryDirs.length}`);
|
|
96
|
+
console.log();
|
|
97
|
+
|
|
98
|
+
console.log('[7] 测试 rules-server.js MCP 协议');
|
|
99
|
+
return new Promise((resolve, reject) => {
|
|
100
|
+
const mcpProc = spawn('node', [MCP_SERVER], {
|
|
101
|
+
cwd: PROJECT_ROOT,
|
|
102
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
let output = '';
|
|
106
|
+
let errorOutput = '';
|
|
107
|
+
|
|
108
|
+
mcpProc.stdout.on('data', (data) => {
|
|
109
|
+
output += data.toString();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
mcpProc.stderr.on('data', (data) => {
|
|
113
|
+
errorOutput += data.toString();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
mcpProc.on('error', (err) => {
|
|
117
|
+
console.log(' ❌ MCP 服务器启动失败:', err.message);
|
|
118
|
+
reject(err);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// 发送 initialize 请求
|
|
122
|
+
setTimeout(() => {
|
|
123
|
+
const initRequest = JSON.stringify({
|
|
124
|
+
jsonrpc: '2.0',
|
|
125
|
+
id: 1,
|
|
126
|
+
method: 'initialize',
|
|
127
|
+
params: {
|
|
128
|
+
protocolVersion: '2024-11-05',
|
|
129
|
+
capabilities: {},
|
|
130
|
+
clientInfo: { name: 'test', version: '1.0.0' }
|
|
131
|
+
}
|
|
132
|
+
}) + '\n';
|
|
133
|
+
mcpProc.stdin.write(initRequest);
|
|
134
|
+
}, 500);
|
|
135
|
+
|
|
136
|
+
// 发送 tools/list 请求
|
|
137
|
+
setTimeout(() => {
|
|
138
|
+
const listRequest = JSON.stringify({
|
|
139
|
+
jsonrpc: '2.0',
|
|
140
|
+
id: 2,
|
|
141
|
+
method: 'tools/list',
|
|
142
|
+
params: {}
|
|
143
|
+
}) + '\n';
|
|
144
|
+
mcpProc.stdin.write(listRequest);
|
|
145
|
+
}, 1000);
|
|
146
|
+
|
|
147
|
+
// 接收响应
|
|
148
|
+
setTimeout(() => {
|
|
149
|
+
console.log(' MCP 服务器响应:');
|
|
150
|
+
const lines = output.split('\n').filter(l => l.trim());
|
|
151
|
+
|
|
152
|
+
if (lines.length > 0) {
|
|
153
|
+
let hasValidResponse = false;
|
|
154
|
+
lines.forEach(line => {
|
|
155
|
+
try {
|
|
156
|
+
const resp = JSON.parse(line);
|
|
157
|
+
if (resp.result) {
|
|
158
|
+
hasValidResponse = true;
|
|
159
|
+
if (resp.result.tools) {
|
|
160
|
+
const toolNames = resp.result.tools.map(t => t.name);
|
|
161
|
+
console.log(' ✅ MCP 协议通信正常');
|
|
162
|
+
console.log(' 可用工具:', toolNames.join(', '));
|
|
163
|
+
} else if (resp.result.serverInfo) {
|
|
164
|
+
console.log(' ✅ 初始化成功:', resp.result.serverInfo.name);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (resp.error) {
|
|
168
|
+
console.log(' ⚠️ MCP 错误:', resp.error.message);
|
|
169
|
+
}
|
|
170
|
+
} catch (err) {
|
|
171
|
+
// 忽略解析错误
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
if (!hasValidResponse) {
|
|
176
|
+
console.log(' ⚠️ 未收到有效响应');
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
console.log(' ⚠️ 无输出');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (errorOutput) {
|
|
183
|
+
console.log(' stderr:', errorOutput.substring(0, 200));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
mcpProc.kill();
|
|
187
|
+
console.log();
|
|
188
|
+
console.log('='.repeat(60));
|
|
189
|
+
console.log('✅ MCP 全链路检查完成');
|
|
190
|
+
console.log('='.repeat(60));
|
|
191
|
+
resolve();
|
|
192
|
+
}, 2000);
|
|
193
|
+
});
|