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
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render Verify - 渲染验证脚本
|
|
3
|
+
* 三项校验:语法校验 + 目录层级校验 + 优先级合规校验
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Promise化的fs方法
|
|
11
|
+
*/
|
|
12
|
+
const _fsPromises = {
|
|
13
|
+
pathExists: (p) => fs.promises.access(p).then(() => true).catch(() => false),
|
|
14
|
+
readdir: (p) => fs.promises.readdir(p),
|
|
15
|
+
stat: (p) => fs.promises.stat(p),
|
|
16
|
+
readFile: (p, encoding) => fs.promises.readFile(p, encoding)
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
class RenderVerify {
|
|
20
|
+
constructor() {
|
|
21
|
+
this.results = [];
|
|
22
|
+
this.passed = 0;
|
|
23
|
+
this.failed = 0;
|
|
24
|
+
this.baseDir = process.cwd();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 记录测试结果
|
|
29
|
+
*/
|
|
30
|
+
log(type, message, status) {
|
|
31
|
+
const icon = status === 'PASS' ? '✅' : '❌';
|
|
32
|
+
const log = `${icon} [${type}] ${message}`;
|
|
33
|
+
this.results.push({ type, message, status });
|
|
34
|
+
console.log(log);
|
|
35
|
+
if (status === 'PASS') this.passed++;
|
|
36
|
+
else this.failed++;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 简单的YAML语法检查
|
|
41
|
+
*/
|
|
42
|
+
validateYamlSyntax(content) {
|
|
43
|
+
// 基本YAML语法检查
|
|
44
|
+
const lines = content.split('\n');
|
|
45
|
+
let inMultilineString = false;
|
|
46
|
+
|
|
47
|
+
for (let i = 0; i < lines.length; i++) {
|
|
48
|
+
const line = lines[i];
|
|
49
|
+
|
|
50
|
+
// 处理多行字符串
|
|
51
|
+
if (line.includes('|-')) {
|
|
52
|
+
inMultilineString = true;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (inMultilineString && line.match(/^\s+\S/)) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
inMultilineString = false;
|
|
59
|
+
|
|
60
|
+
// 检查基本YAML语法
|
|
61
|
+
const trimmed = line.trim();
|
|
62
|
+
if (trimmed === '' || trimmed.startsWith('#')) continue;
|
|
63
|
+
|
|
64
|
+
// 检查键值对格式
|
|
65
|
+
if (trimmed.includes(':')) {
|
|
66
|
+
const key = trimmed.split(':')[0].trim();
|
|
67
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(key)) {
|
|
68
|
+
// 可能是带引号的键
|
|
69
|
+
if (!key.startsWith('"') && !key.startsWith("'")) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 校验1:语法校验
|
|
80
|
+
*/
|
|
81
|
+
async verifySyntax() {
|
|
82
|
+
console.log('\n========== 校验1: 语法校验 ==========');
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
// 检查YAML语法(只检查渲染引擎新生成的文件:L1-、L2-、L3-开头)
|
|
86
|
+
const rulesDir = path.join(this.baseDir, '.trae', 'rules');
|
|
87
|
+
const generatedPrefixes = ['L1-', 'L2-', 'L3-'];
|
|
88
|
+
|
|
89
|
+
if (await _fsPromises.pathExists(rulesDir)) {
|
|
90
|
+
// 递归检查所有YAML文件
|
|
91
|
+
const checkDir = async (dirPath, relativePath = '') => {
|
|
92
|
+
const files = await _fsPromises.readdir(dirPath);
|
|
93
|
+
for (const file of files) {
|
|
94
|
+
const fullPath = path.join(dirPath, file);
|
|
95
|
+
const stat = await _fsPromises.stat(fullPath);
|
|
96
|
+
if (stat.isDirectory()) {
|
|
97
|
+
await checkDir(fullPath, `${relativePath}${file}/`);
|
|
98
|
+
} else if (file.endsWith('.yml') || file.endsWith('.yaml')) {
|
|
99
|
+
// 只检查渲染引擎生成的文件
|
|
100
|
+
if (generatedPrefixes.some(p => file.startsWith(p))) {
|
|
101
|
+
const content = await _fsPromises.readFile(fullPath, 'utf-8');
|
|
102
|
+
if (this.validateYamlSyntax(content)) {
|
|
103
|
+
this.log('SYNTAX', `YAML语法正确: ${relativePath}${file}`, 'PASS');
|
|
104
|
+
} else {
|
|
105
|
+
this.log('SYNTAX', `YAML语法错误: ${relativePath}${file}`, 'FAIL');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
await checkDir(rulesDir);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 检查Markdown语法(基本检查)
|
|
115
|
+
const agentsPath = path.join(this.baseDir, 'AGENTS.md');
|
|
116
|
+
const claudePath = path.join(this.baseDir, 'CLAUDE.md');
|
|
117
|
+
|
|
118
|
+
if (await _fsPromises.pathExists(agentsPath)) {
|
|
119
|
+
const content = await _fsPromises.readFile(agentsPath, 'utf-8');
|
|
120
|
+
if (content.includes('# AGENTS.md') && content.length > 100) {
|
|
121
|
+
this.log('SYNTAX', 'AGENTS.md 格式正确', 'PASS');
|
|
122
|
+
} else {
|
|
123
|
+
this.log('SYNTAX', 'AGENTS.md 格式异常', 'FAIL');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (await _fsPromises.pathExists(claudePath)) {
|
|
128
|
+
const content = await _fsPromises.readFile(claudePath, 'utf-8');
|
|
129
|
+
if (content.includes('# CLAUDE.md') && content.length > 100) {
|
|
130
|
+
this.log('SYNTAX', 'CLAUDE.md 格式正确', 'PASS');
|
|
131
|
+
} else {
|
|
132
|
+
this.log('SYNTAX', 'CLAUDE.md 格式异常', 'FAIL');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
} catch (e) {
|
|
137
|
+
this.log('SYNTAX', `语法校验失败: ${e.message}`, 'FAIL');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 校验2:目录层级校验
|
|
143
|
+
*/
|
|
144
|
+
async verifyDirectoryHierarchy() {
|
|
145
|
+
console.log('\n========== 校验2: 目录层级校验 ==========');
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const rulesDir = path.join(this.baseDir, '.trae', 'rules');
|
|
149
|
+
|
|
150
|
+
// 检查顶级目录
|
|
151
|
+
if (await _fsPromises.pathExists(rulesDir)) {
|
|
152
|
+
this.log('HIERARCHY', '.trae/rules 目录存在', 'PASS');
|
|
153
|
+
} else {
|
|
154
|
+
this.log('HIERARCHY', '.trae/rules 目录不存在', 'FAIL');
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 检查L1/L2/L3层级
|
|
159
|
+
const l1Dir = path.join(rulesDir, 'L1');
|
|
160
|
+
const l2Dir = path.join(rulesDir, 'L2');
|
|
161
|
+
const l3Dir = path.join(rulesDir, 'L3');
|
|
162
|
+
|
|
163
|
+
if (await _fsPromises.pathExists(l1Dir)) {
|
|
164
|
+
this.log('HIERARCHY', 'L1 目录存在', 'PASS');
|
|
165
|
+
} else {
|
|
166
|
+
this.log('HIERARCHY', 'L1 目录不存在', 'FAIL');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (await _fsPromises.pathExists(l2Dir)) {
|
|
170
|
+
this.log('HIERARCHY', 'L2 目录存在', 'PASS');
|
|
171
|
+
} else {
|
|
172
|
+
this.log('HIERARCHY', 'L2 目录不存在', 'FAIL');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (await _fsPromises.pathExists(l3Dir)) {
|
|
176
|
+
this.log('HIERARCHY', 'L3 目录存在', 'PASS');
|
|
177
|
+
} else {
|
|
178
|
+
this.log('HIERARCHY', 'L3 目录不存在', 'FAIL');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 检查L3子目录递归(如RZero模块目录)
|
|
182
|
+
const l3Contents = await _fsPromises.readdir(l3Dir);
|
|
183
|
+
for (const item of l3Contents) {
|
|
184
|
+
const itemPath = path.join(l3Dir, item);
|
|
185
|
+
const stat = await _fsPromises.stat(itemPath);
|
|
186
|
+
if (stat.isDirectory()) {
|
|
187
|
+
this.log('HIERARCHY', `L3 子目录存在: ${item}`, 'PASS');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
} catch (e) {
|
|
192
|
+
this.log('HIERARCHY', `目录层级校验失败: ${e.message}`, 'FAIL');
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* 校验3:优先级合规校验
|
|
198
|
+
*/
|
|
199
|
+
async verifyPriorityCompliance() {
|
|
200
|
+
console.log('\n========== 校验3: 优先级合规校验 ==========');
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
// 检查优先级:AGENTS.md > CLAUDE.md > .trae/rules
|
|
204
|
+
const agentsPath = path.join(this.baseDir, 'AGENTS.md');
|
|
205
|
+
const claudePath = path.join(this.baseDir, 'CLAUDE.md');
|
|
206
|
+
const rulesDir = path.join(this.baseDir, '.trae', 'rules');
|
|
207
|
+
|
|
208
|
+
const agentsExists = await _fsPromises.pathExists(agentsPath);
|
|
209
|
+
const claudeExists = await _fsPromises.pathExists(claudePath);
|
|
210
|
+
const rulesExists = await _fsPromises.pathExists(rulesDir);
|
|
211
|
+
|
|
212
|
+
// 校验1:AGENTS.md必须存在(最高优先级)
|
|
213
|
+
if (agentsExists) {
|
|
214
|
+
this.log('PRIORITY', 'AGENTS.md 存在(最高优先级)', 'PASS');
|
|
215
|
+
} else {
|
|
216
|
+
this.log('PRIORITY', 'AGENTS.md 不存在(违反最高优先级要求)', 'FAIL');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// 校验2:CLAUDE.md必须存在
|
|
220
|
+
if (claudeExists) {
|
|
221
|
+
this.log('PRIORITY', 'CLAUDE.md 存在(次优先级)', 'PASS');
|
|
222
|
+
} else {
|
|
223
|
+
this.log('PRIORITY', 'CLAUDE.md 不存在', 'FAIL');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// 校验3:.trae/rules目录必须存在(最低优先级)
|
|
227
|
+
if (rulesExists) {
|
|
228
|
+
this.log('PRIORITY', '.trae/rules 存在(最低优先级)', 'PASS');
|
|
229
|
+
} else {
|
|
230
|
+
this.log('PRIORITY', '.trae/rules 不存在', 'FAIL');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// 校验4:优先级顺序文档必须在CLAUDE.md中体现
|
|
234
|
+
if (claudeExists) {
|
|
235
|
+
const claudeContent = await _fsPromises.readFile(claudePath, 'utf-8');
|
|
236
|
+
if (claudeContent.includes('AGENTS.md') &&
|
|
237
|
+
claudeContent.includes('CLAUDE.md') &&
|
|
238
|
+
claudeContent.includes('.trae/rules')) {
|
|
239
|
+
this.log('PRIORITY', 'CLAUDE.md 包含优先级顺序说明', 'PASS');
|
|
240
|
+
} else {
|
|
241
|
+
this.log('PRIORITY', 'CLAUDE.md 缺少优先级顺序说明', 'FAIL');
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
} catch (e) {
|
|
246
|
+
this.log('PRIORITY', `优先级校验失败: ${e.message}`, 'FAIL');
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* 执行全部校验
|
|
252
|
+
*/
|
|
253
|
+
async verifyAll() {
|
|
254
|
+
console.log('===========================================');
|
|
255
|
+
console.log(' Render Engine Verification');
|
|
256
|
+
console.log('===========================================');
|
|
257
|
+
|
|
258
|
+
await this.verifySyntax();
|
|
259
|
+
await this.verifyDirectoryHierarchy();
|
|
260
|
+
await this.verifyPriorityCompliance();
|
|
261
|
+
|
|
262
|
+
console.log('\n===========================================');
|
|
263
|
+
console.log(` 校验结果: ${this.passed} 通过, ${this.failed} 失败`);
|
|
264
|
+
console.log('===========================================');
|
|
265
|
+
|
|
266
|
+
if (this.failed > 0) {
|
|
267
|
+
console.log('\n❌ 渲染验证未全部通过,请检查上述失败项');
|
|
268
|
+
process.exit(1);
|
|
269
|
+
} else {
|
|
270
|
+
console.log('\n✅ 渲染验证全部通过!');
|
|
271
|
+
process.exit(0);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// CLI入口
|
|
277
|
+
if (require.main === module) {
|
|
278
|
+
const verifier = new RenderVerify();
|
|
279
|
+
verifier.verifyAll();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
module.exports = { RenderVerify };
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule Render - 规则文件渲染器
|
|
3
|
+
* 生成标准YAML格式规则文件
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class RuleRender {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.indent = ' ';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 渲染单条规则为YAML
|
|
13
|
+
*/
|
|
14
|
+
renderRule(rule, level) {
|
|
15
|
+
const doc = {
|
|
16
|
+
id: rule.id || `rule-${Date.now()}`,
|
|
17
|
+
name: rule.name || 'Unnamed Rule',
|
|
18
|
+
level: level,
|
|
19
|
+
version: '1.0.0',
|
|
20
|
+
enabled: true,
|
|
21
|
+
description: rule.description || `Generated rule: ${rule.name}`,
|
|
22
|
+
priority: this.getPriorityByLevel(level),
|
|
23
|
+
rules: this.generateRuleContent(rule, level)
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// 根据level添加特定配置
|
|
27
|
+
if (level === 'L1') {
|
|
28
|
+
doc.scope = 'global';
|
|
29
|
+
doc.appliesTo = ['all'];
|
|
30
|
+
} else if (level === 'L2') {
|
|
31
|
+
doc.scope = 'project';
|
|
32
|
+
doc.appliesTo = [rule.projectPattern || '*'];
|
|
33
|
+
} else if (level === 'L3') {
|
|
34
|
+
doc.scope = 'module';
|
|
35
|
+
doc.module = rule.module || 'default';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return this.toYaml(doc, 0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 简单YAML生成器
|
|
43
|
+
*/
|
|
44
|
+
toYaml(obj, depth) {
|
|
45
|
+
const indentStr = this.indent.repeat(depth);
|
|
46
|
+
let result = '';
|
|
47
|
+
|
|
48
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
49
|
+
if (value === null || value === undefined) continue;
|
|
50
|
+
|
|
51
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
52
|
+
result += `${indentStr}${key}:\n`;
|
|
53
|
+
result += this.toYaml(value, depth + 1);
|
|
54
|
+
} else if (Array.isArray(value)) {
|
|
55
|
+
if (value.length === 0) {
|
|
56
|
+
result += `${indentStr}${key}: []\n`;
|
|
57
|
+
} else {
|
|
58
|
+
result += `${indentStr}${key}:\n`;
|
|
59
|
+
for (const item of value) {
|
|
60
|
+
if (typeof item === 'object') {
|
|
61
|
+
result += `${indentStr}${this.indent}- \n`;
|
|
62
|
+
result += this.toYaml(item, depth + 2);
|
|
63
|
+
} else {
|
|
64
|
+
result += `${indentStr}${this.indent}- ${item}\n`;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} else if (typeof value === 'string') {
|
|
69
|
+
// 处理可能包含特殊字符的字符串
|
|
70
|
+
if (value.includes('\n') || value.includes(':') || value.includes('#')) {
|
|
71
|
+
result += `${indentStr}${key}: |-\n`;
|
|
72
|
+
for (const line of value.split('\n')) {
|
|
73
|
+
result += `${indentStr}${this.indent}${line}\n`;
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
result += `${indentStr}${key}: ${value}\n`;
|
|
77
|
+
}
|
|
78
|
+
} else if (typeof value === 'boolean') {
|
|
79
|
+
result += `${indentStr}${key}: ${value}\n`;
|
|
80
|
+
} else if (typeof value === 'number') {
|
|
81
|
+
result += `${indentStr}${key}: ${value}\n`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 根据level获取优先级
|
|
90
|
+
*/
|
|
91
|
+
getPriorityByLevel(level) {
|
|
92
|
+
const priorities = {
|
|
93
|
+
'L1': 100, // 最高优先级
|
|
94
|
+
'L2': 80,
|
|
95
|
+
'L3': 60,
|
|
96
|
+
'custom': 40
|
|
97
|
+
};
|
|
98
|
+
return priorities[level] || 50;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 生成规则内容
|
|
103
|
+
*/
|
|
104
|
+
generateRuleContent(rule, level) {
|
|
105
|
+
const baseRules = [];
|
|
106
|
+
|
|
107
|
+
// 基础规则
|
|
108
|
+
baseRules.push({
|
|
109
|
+
name: 'base-rule',
|
|
110
|
+
description: rule.description || `Base rule for ${rule.name}`,
|
|
111
|
+
action: 'enforce',
|
|
112
|
+
conditions: this.generateConditions(rule),
|
|
113
|
+
effects: this.generateEffects(rule)
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// 根据level添加特定规则
|
|
117
|
+
if (level === 'L1') {
|
|
118
|
+
baseRules.push(this.generateGlobalConstraints(rule));
|
|
119
|
+
} else if (level === 'L2') {
|
|
120
|
+
baseRules.push(this.generateProjectConstraints(rule));
|
|
121
|
+
} else if (level === 'L3') {
|
|
122
|
+
baseRules.push(this.generateModuleConstraints(rule));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return baseRules;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 生成条件
|
|
130
|
+
*/
|
|
131
|
+
generateConditions(rule) {
|
|
132
|
+
return {
|
|
133
|
+
fileTypes: rule.techStackMatch || ['*'],
|
|
134
|
+
projectSize: rule.complexity || 'medium',
|
|
135
|
+
dependencies: rule.dependencies || []
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 生成效果
|
|
141
|
+
*/
|
|
142
|
+
generateEffects(rule) {
|
|
143
|
+
return {
|
|
144
|
+
output: 'enforced',
|
|
145
|
+
logging: true,
|
|
146
|
+
blocking: rule.level === 'L1' // L1规则阻塞执行
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* 生成全局约束(L1)
|
|
152
|
+
*/
|
|
153
|
+
generateGlobalConstraints(rule) {
|
|
154
|
+
return {
|
|
155
|
+
name: 'global-constraints',
|
|
156
|
+
description: '全局行为约束',
|
|
157
|
+
action: 'enforce',
|
|
158
|
+
constraints: {
|
|
159
|
+
codeStyle: {
|
|
160
|
+
formatter: 'prettier',
|
|
161
|
+
eslint: 'strict'
|
|
162
|
+
},
|
|
163
|
+
security: {
|
|
164
|
+
noHardcodedCredentials: true,
|
|
165
|
+
useEnvironmentVariables: true
|
|
166
|
+
},
|
|
167
|
+
quality: {
|
|
168
|
+
maxComplexity: 15,
|
|
169
|
+
minTestCoverage: 0.8
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 生成项目约束(L2)
|
|
177
|
+
*/
|
|
178
|
+
generateProjectConstraints(rule) {
|
|
179
|
+
return {
|
|
180
|
+
name: 'project-constraints',
|
|
181
|
+
description: '项目特定约束',
|
|
182
|
+
action: 'enforce',
|
|
183
|
+
constraints: {
|
|
184
|
+
projectStructure: {
|
|
185
|
+
srcDirectory: 'src',
|
|
186
|
+
testDirectory: 'test',
|
|
187
|
+
docsDirectory: 'docs'
|
|
188
|
+
},
|
|
189
|
+
naming: {
|
|
190
|
+
files: 'kebab-case',
|
|
191
|
+
classes: 'PascalCase',
|
|
192
|
+
functions: 'camelCase'
|
|
193
|
+
},
|
|
194
|
+
techStack: rule.techStackMatch || []
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* 生成模块约束(L3)
|
|
201
|
+
*/
|
|
202
|
+
generateModuleConstraints(rule) {
|
|
203
|
+
return {
|
|
204
|
+
name: 'module-constraints',
|
|
205
|
+
description: `模块 ${rule.module || 'default'} 特定约束`,
|
|
206
|
+
action: 'enforce',
|
|
207
|
+
constraints: {
|
|
208
|
+
module: rule.module || 'default',
|
|
209
|
+
dependencies: rule.dependencies || [],
|
|
210
|
+
patterns: rule.patterns || []
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* 批量渲染规则
|
|
217
|
+
*/
|
|
218
|
+
renderRulesBatch(rules, level) {
|
|
219
|
+
const results = [];
|
|
220
|
+
for (const rule of rules) {
|
|
221
|
+
results.push({
|
|
222
|
+
level,
|
|
223
|
+
id: rule.id,
|
|
224
|
+
content: this.renderRule(rule, level)
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return results;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
module.exports = { RuleRender };
|