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,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Config Render - Agent配置渲染器
|
|
3
|
+
* 生成AGENTS.md和CLAUDE.md标准配置
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class AgentConfigRender {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.template = {
|
|
9
|
+
agents: this.getAgentsTemplate(),
|
|
10
|
+
claude: this.getClaudeTemplate()
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 渲染AGENTS.md
|
|
16
|
+
*/
|
|
17
|
+
renderAgentsMd(decisionResult) {
|
|
18
|
+
const recommendedAgent = decisionResult.decision?.recommendedAgent;
|
|
19
|
+
const recommendedMcp = decisionResult.decision?.recommendedMcp || [];
|
|
20
|
+
|
|
21
|
+
let content = `# AGENTS.md - Agent Configuration
|
|
22
|
+
|
|
23
|
+
## Agent Identity
|
|
24
|
+
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
if (recommendedAgent) {
|
|
28
|
+
content += `### ${recommendedAgent.name}
|
|
29
|
+
|
|
30
|
+
`;
|
|
31
|
+
content += `**Type**: ${recommendedAgent.type || 'general-purpose'}\n\n`;
|
|
32
|
+
content += `**Version**: 1.0.0\n\n`;
|
|
33
|
+
content += `**Description**: ${recommendedAgent.description || `AI agent configured for optimal project support`}\n\n`;
|
|
34
|
+
|
|
35
|
+
if (recommendedAgent.recommendedMcp) {
|
|
36
|
+
content += `### MCP Services\n\n`;
|
|
37
|
+
content += `Recommended MCP services for this agent:\n\n`;
|
|
38
|
+
for (const mcp of recommendedAgent.recommendedMcp) {
|
|
39
|
+
content += `- \`${mcp}\`\n`;
|
|
40
|
+
}
|
|
41
|
+
content += `\n`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (recommendedAgent.recommendedRules) {
|
|
45
|
+
content += `### Rule Levels\n\n`;
|
|
46
|
+
content += `Applicable rule levels:\n\n`;
|
|
47
|
+
for (const ruleLevel of recommendedAgent.recommendedRules) {
|
|
48
|
+
content += `- **${ruleLevel}**: `;
|
|
49
|
+
switch (ruleLevel) {
|
|
50
|
+
case 'L1': content += `Global behavior constraints\n`; break;
|
|
51
|
+
case 'L2': content += `Project-specific rules\n`; break;
|
|
52
|
+
case 'L3': content += `Module-level rules\n`; break;
|
|
53
|
+
default: content += `Custom rules\n`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
content += `\n`;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
content += `## Agent Capabilities
|
|
61
|
+
|
|
62
|
+
### Core Capabilities
|
|
63
|
+
|
|
64
|
+
- **Code Generation**: Generate TypeScript, JavaScript, React components
|
|
65
|
+
- **Code Refactoring**: Improve code quality and structure
|
|
66
|
+
- **Bug Detection**: Identify and fix common bugs
|
|
67
|
+
- **Debugging**: Analyze and resolve issues
|
|
68
|
+
- **Documentation**: Generate API docs, README, code comments
|
|
69
|
+
- **Testing**: Write unit tests, integration tests
|
|
70
|
+
- **Requirements Analysis**: Parse and understand user requirements
|
|
71
|
+
|
|
72
|
+
### Behavior Constraints
|
|
73
|
+
|
|
74
|
+
1. Follow AGENTS.md > CLAUDE.md > .trae/rules priority
|
|
75
|
+
2. Use TypeScript strict mode
|
|
76
|
+
3. Follow ESLint rules
|
|
77
|
+
4. Use Prettier for formatting
|
|
78
|
+
5. No hardcoded credentials
|
|
79
|
+
6. No console.log statements in production code
|
|
80
|
+
|
|
81
|
+
### MCP Integration
|
|
82
|
+
|
|
83
|
+
`;
|
|
84
|
+
if (recommendedMcp.length > 0) {
|
|
85
|
+
content += `Configured MCP services:\n\n`;
|
|
86
|
+
for (const mcp of recommendedMcp) {
|
|
87
|
+
content += `- **${mcp.name}** (${mcp.type}): ${mcp.id}\n`;
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
content += `No additional MCP services required.\n`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
content += `
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
*Generated by TRAIDE Project Configuration System*
|
|
97
|
+
`;
|
|
98
|
+
|
|
99
|
+
return content;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 渲染CLAUDE.md
|
|
104
|
+
*/
|
|
105
|
+
renderClaudeMd(decisionResult) {
|
|
106
|
+
const recommendedAgent = decisionResult.decision?.recommendedAgent;
|
|
107
|
+
const recommendedRules = decisionResult.decision?.recommendedRules || [];
|
|
108
|
+
const scoreBreakdown = decisionResult.scoreBreakdown || {};
|
|
109
|
+
|
|
110
|
+
let content = `# CLAUDE.md - Project Configuration
|
|
111
|
+
|
|
112
|
+
## Project Overview
|
|
113
|
+
|
|
114
|
+
`;
|
|
115
|
+
if (recommendedAgent) {
|
|
116
|
+
content += `**Agent**: ${recommendedAgent.name}\n`;
|
|
117
|
+
}
|
|
118
|
+
content += `**Generated**: ${new Date().toISOString()}\n`;
|
|
119
|
+
content += `**Confidence**: ${(decisionResult.decision?.confidence * 100 || 0).toFixed(1)}%\n\n`;
|
|
120
|
+
|
|
121
|
+
content += `## Score Breakdown\n\n`;
|
|
122
|
+
content += `| Metric | Score |\n`;
|
|
123
|
+
content += `|--------|-------|\n`;
|
|
124
|
+
content += `| Tech Stack Match | ${scoreBreakdown.techStackMatch || 0}/100 |\n`;
|
|
125
|
+
content += `| Complexity Fit | ${scoreBreakdown.complexityFit || 0}/100 |\n`;
|
|
126
|
+
content += `| Dependency Score | ${scoreBreakdown.dependencyScore || 0}/100 |\n`;
|
|
127
|
+
content += `| Reuse Score | ${scoreBreakdown.reuseScore || 0}/100 |\n\n`;
|
|
128
|
+
|
|
129
|
+
content += `## Configuration Priority\n\n`;
|
|
130
|
+
content += `**IMPORTANT**: Configuration files are loaded in the following order:\n\n`;
|
|
131
|
+
content += `1. **AGENTS.md** (highest priority) - Agent-specific configuration\n`;
|
|
132
|
+
content += `2. **CLAUDE.md** (project level) - Project-specific settings\n`;
|
|
133
|
+
content += `3. **.trae/rules/** (lowest priority) - Rule files\n\n`;
|
|
134
|
+
|
|
135
|
+
content += `## Rule Levels\n\n`;
|
|
136
|
+
content += `### L1 - Global Rules\n`;
|
|
137
|
+
content += `Global behavior constraints that apply to all operations.\n\n`;
|
|
138
|
+
content += `### L2 - Project Rules\n`;
|
|
139
|
+
content += `Project-specific rules for this workspace.\n\n`;
|
|
140
|
+
content += `### L3 - Module Rules\n`;
|
|
141
|
+
content += `Module-level rules organized by functionality.\n\n`;
|
|
142
|
+
|
|
143
|
+
if (recommendedRules.length > 0) {
|
|
144
|
+
content += `## Recommended Rules\n\n`;
|
|
145
|
+
for (const rule of recommendedRules) {
|
|
146
|
+
content += `- **${rule.name}** [${rule.level}]: ${rule.description || 'No description'}\n`;
|
|
147
|
+
}
|
|
148
|
+
content += `\n`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
content += `## Conflicts & Warnings\n\n`;
|
|
152
|
+
if (decisionResult.conflicts && decisionResult.conflicts.length > 0) {
|
|
153
|
+
for (const conflict of decisionResult.conflicts) {
|
|
154
|
+
const icon = conflict.severity === 'error' ? '❌' :
|
|
155
|
+
conflict.severity === 'warning' ? '⚠️' : 'ℹ️';
|
|
156
|
+
content += `${icon} ${conflict.message}\n`;
|
|
157
|
+
if (conflict.suggestion) {
|
|
158
|
+
content += ` → ${conflict.suggestion}\n`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
content += `No conflicts detected.\n`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
content += `
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
*Generated by TRAIDE Decision Engine*
|
|
169
|
+
`;
|
|
170
|
+
|
|
171
|
+
return content;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 获取AGENTS.md模板
|
|
176
|
+
*/
|
|
177
|
+
getAgentsTemplate() {
|
|
178
|
+
return {
|
|
179
|
+
header: '# AGENTS.md - Agent Configuration',
|
|
180
|
+
sections: ['identity', 'capabilities', 'constraints', 'mcp', 'rules']
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 获取CLAUDE.md模板
|
|
186
|
+
*/
|
|
187
|
+
getClaudeTemplate() {
|
|
188
|
+
return {
|
|
189
|
+
header: '# CLAUDE.md - Project Configuration',
|
|
190
|
+
sections: ['overview', 'priority', 'rules', 'conflicts']
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
module.exports = { AgentConfigRender };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render Engine - 渲染引擎入口
|
|
3
|
+
* 对接decision-engine决策结果,一键渲染全套配置
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { RenderCore } = require('./render-core.js');
|
|
8
|
+
|
|
9
|
+
class RenderEngine {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.outputDir = options.outputDir || process.cwd();
|
|
12
|
+
this.renderCore = new RenderCore({ outputDir: this.outputDir });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 执行完整渲染流程
|
|
17
|
+
* 接收decision-engine的决策结果作为唯一数据源
|
|
18
|
+
*/
|
|
19
|
+
async render(decisionResult) {
|
|
20
|
+
console.log('\n[RenderEngine] ========== 配置渲染开始 ==========\n');
|
|
21
|
+
|
|
22
|
+
// 数据源验证:必须来自decision-engine
|
|
23
|
+
if (!decisionResult || !decisionResult.decision) {
|
|
24
|
+
throw new Error('无效的决策结果,数据源必须来自decision-engine');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 执行渲染
|
|
28
|
+
const result = await this.renderCore.render(decisionResult);
|
|
29
|
+
|
|
30
|
+
// 输出结果
|
|
31
|
+
if (result.success) {
|
|
32
|
+
console.log(`[RenderEngine] ✅ 渲染成功,共生成 ${result.files.length} 个文件:\n`);
|
|
33
|
+
for (const file of result.files) {
|
|
34
|
+
console.log(` - ${file.path} [${file.type || file.level || 'file'}]`);
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
console.log(`[RenderEngine] ❌ 渲染失败:\n`);
|
|
38
|
+
for (const error of result.errors) {
|
|
39
|
+
console.log(` - ${error}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log('\n[RenderEngine] ========== 渲染完成 ==========\n');
|
|
44
|
+
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 打印渲染摘要
|
|
50
|
+
*/
|
|
51
|
+
printSummary() {
|
|
52
|
+
const summary = this.renderCore.getSummary();
|
|
53
|
+
console.log('\n========== 渲染摘要 ==========');
|
|
54
|
+
console.log(`优先级: ${summary.priority}`);
|
|
55
|
+
console.log(`文件总数: ${summary.totalFiles}`);
|
|
56
|
+
console.log('================================\n');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 获取引擎信息
|
|
61
|
+
*/
|
|
62
|
+
getInfo() {
|
|
63
|
+
return {
|
|
64
|
+
name: 'RenderEngine',
|
|
65
|
+
version: '1.0.0',
|
|
66
|
+
priority: 'AGENTS.md > CLAUDE.md > .trae/rules',
|
|
67
|
+
formats: ['AGENTS.md (Markdown)', 'CLAUDE.md (Markdown)', '.trae/rules/*.yml (YAML)']
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = { RenderEngine };
|
|
73
|
+
|
|
74
|
+
// CLI入口
|
|
75
|
+
if (require.main === module) {
|
|
76
|
+
const path = require('path');
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
// 加载decision-engine结果(模拟)
|
|
80
|
+
console.log('[RenderEngine] CLI模式示例');
|
|
81
|
+
console.log('实际使用时,decision-result应从decision-engine获取\n');
|
|
82
|
+
|
|
83
|
+
// 示例决策结果
|
|
84
|
+
const mockDecisionResult = {
|
|
85
|
+
decision: {
|
|
86
|
+
recommendedMcp: [
|
|
87
|
+
{ id: 'context7', name: 'Context7', type: 'context-management' }
|
|
88
|
+
],
|
|
89
|
+
recommendedRules: [
|
|
90
|
+
{ id: 'user-rules', name: 'User Rules', level: 'L1', description: '用户全局行为约束' },
|
|
91
|
+
{ id: 'project-rules', name: 'Project Rules', level: 'L2', description: '项目特定规则' },
|
|
92
|
+
{ id: 'rzero-agent', name: 'RZero Agent Configuration', level: 'L3', module: 'RZero', description: 'RZero Agent配置' }
|
|
93
|
+
],
|
|
94
|
+
recommendedAgent: {
|
|
95
|
+
id: 'fullstack-developer',
|
|
96
|
+
name: 'Full Stack Developer',
|
|
97
|
+
type: 'general-purpose',
|
|
98
|
+
recommendedMcp: ['rules-enforcer', 'context7'],
|
|
99
|
+
recommendedRules: ['L1', 'L2']
|
|
100
|
+
},
|
|
101
|
+
confidence: 0.94
|
|
102
|
+
},
|
|
103
|
+
scoreBreakdown: {
|
|
104
|
+
techStackMatch: 93,
|
|
105
|
+
complexityFit: 100,
|
|
106
|
+
dependencyScore: 88,
|
|
107
|
+
reuseScore: 100
|
|
108
|
+
},
|
|
109
|
+
conflicts: [],
|
|
110
|
+
alternatives: []
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const engine = new RenderEngine({ outputDir: process.cwd() });
|
|
114
|
+
engine.render(mockDecisionResult).then(result => {
|
|
115
|
+
engine.printSummary();
|
|
116
|
+
console.log('完整渲染结果:');
|
|
117
|
+
console.log(JSON.stringify(result, null, 2));
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
} catch (e) {
|
|
121
|
+
console.error('[RenderEngine] 执行失败:', e.message);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render Core - 核心渲染引擎
|
|
3
|
+
* 接收decision-engine决策结果,调度各渲染器生成配置
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { RuleRender } = require('./rule-render.js');
|
|
9
|
+
const { AgentConfigRender } = require('./ag-config-render.js');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Promise化的fs方法
|
|
13
|
+
*/
|
|
14
|
+
const fsPromises = {
|
|
15
|
+
writeFile: (path, content, encoding) => fs.promises.writeFile(path, content, encoding),
|
|
16
|
+
readFile: (path, encoding) => fs.promises.readFile(path, encoding),
|
|
17
|
+
ensureDir: async (dirPath) => {
|
|
18
|
+
try {
|
|
19
|
+
await fs.promises.mkdir(dirPath, { recursive: true });
|
|
20
|
+
} catch (e) {
|
|
21
|
+
if (e.code !== 'EEXIST') throw e;
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
pathExists: (path) => fs.promises.access(path).then(() => true).catch(() => false),
|
|
25
|
+
readdir: (path, options) => fs.promises.readdir(path, options)
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
class RenderCore {
|
|
29
|
+
constructor(options = {}) {
|
|
30
|
+
this.outputDir = options.outputDir || process.cwd();
|
|
31
|
+
this.ruleRender = new RuleRender();
|
|
32
|
+
this.agentConfigRender = new AgentConfigRender();
|
|
33
|
+
this.renderedFiles = [];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 执行完整渲染流程
|
|
38
|
+
*/
|
|
39
|
+
async render(decisionResult) {
|
|
40
|
+
const results = {
|
|
41
|
+
success: true,
|
|
42
|
+
files: [],
|
|
43
|
+
errors: []
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// 1. 渲染Agent配置(最高优先级)
|
|
48
|
+
const agentResult = await this.renderAgentConfigs(decisionResult);
|
|
49
|
+
results.files.push(...agentResult.files);
|
|
50
|
+
if (agentResult.error) results.errors.push(agentResult.error);
|
|
51
|
+
|
|
52
|
+
// 2. 渲染规则文件(次优先级)
|
|
53
|
+
const rulesResult = await this.renderRules(decisionResult);
|
|
54
|
+
results.files.push(...rulesResult.files);
|
|
55
|
+
if (rulesResult.error) results.errors.push(rulesResult.error);
|
|
56
|
+
|
|
57
|
+
results.success = results.errors.length === 0;
|
|
58
|
+
this.renderedFiles = results.files;
|
|
59
|
+
|
|
60
|
+
} catch (e) {
|
|
61
|
+
results.success = false;
|
|
62
|
+
results.errors.push(`渲染核心错误: ${e.message}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return results;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 渲染Agent配置文件
|
|
70
|
+
*/
|
|
71
|
+
async renderAgentConfigs(decisionResult) {
|
|
72
|
+
const result = { files: [], error: null };
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
// 渲染AGENTS.md
|
|
76
|
+
const agentsContent = this.agentConfigRender.renderAgentsMd(decisionResult);
|
|
77
|
+
const agentsPath = path.join(this.outputDir, 'AGENTS.md');
|
|
78
|
+
await fsPromises.writeFile(agentsPath, agentsContent, 'utf-8');
|
|
79
|
+
result.files.push({ path: agentsPath, type: 'agents-md' });
|
|
80
|
+
|
|
81
|
+
// 渲染CLAUDE.md
|
|
82
|
+
const claudeContent = this.agentConfigRender.renderClaudeMd(decisionResult);
|
|
83
|
+
const claudePath = path.join(this.outputDir, 'CLAUDE.md');
|
|
84
|
+
await fsPromises.writeFile(claudePath, claudeContent, 'utf-8');
|
|
85
|
+
result.files.push({ path: claudePath, type: 'claude-md' });
|
|
86
|
+
|
|
87
|
+
} catch (e) {
|
|
88
|
+
result.error = `Agent配置渲染失败: ${e.message}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 渲染规则文件
|
|
96
|
+
*/
|
|
97
|
+
async renderRules(decisionResult) {
|
|
98
|
+
const result = { files: [], error: null };
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
// 确保.trae/rules目录存在
|
|
102
|
+
const rulesDir = path.join(this.outputDir, '.trae', 'rules');
|
|
103
|
+
await fsPromises.ensureDir(rulesDir);
|
|
104
|
+
|
|
105
|
+
// 按优先级渲染规则:L1 > L2 > L3
|
|
106
|
+
const rulesByLevel = this.groupRulesByLevel(decisionResult);
|
|
107
|
+
|
|
108
|
+
// 渲染L1规则
|
|
109
|
+
if (rulesByLevel.L1 && rulesByLevel.L1.length > 0) {
|
|
110
|
+
const l1Dir = path.join(rulesDir, 'L1');
|
|
111
|
+
await fsPromises.ensureDir(l1Dir);
|
|
112
|
+
for (const rule of rulesByLevel.L1) {
|
|
113
|
+
const filePath = path.join(l1Dir, `${rule.id}.yml`);
|
|
114
|
+
const content = this.ruleRender.renderRule(rule, 'L1');
|
|
115
|
+
await fsPromises.writeFile(filePath, content, 'utf-8');
|
|
116
|
+
result.files.push({ path: filePath, type: 'rule-L1', level: 'L1' });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 渲染L2规则
|
|
121
|
+
if (rulesByLevel.L2 && rulesByLevel.L2.length > 0) {
|
|
122
|
+
const l2Dir = path.join(rulesDir, 'L2');
|
|
123
|
+
await fsPromises.ensureDir(l2Dir);
|
|
124
|
+
for (const rule of rulesByLevel.L2) {
|
|
125
|
+
const filePath = path.join(l2Dir, `${rule.id}.yml`);
|
|
126
|
+
const content = this.ruleRender.renderRule(rule, 'L2');
|
|
127
|
+
await fsPromises.writeFile(filePath, content, 'utf-8');
|
|
128
|
+
result.files.push({ path: filePath, type: 'rule-L2', level: 'L2' });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 渲染L3规则
|
|
133
|
+
if (rulesByLevel.L3 && rulesByLevel.L3.length > 0) {
|
|
134
|
+
// 按模块分组创建子目录
|
|
135
|
+
const l3Dir = path.join(rulesDir, 'L3');
|
|
136
|
+
await fsPromises.ensureDir(l3Dir);
|
|
137
|
+
|
|
138
|
+
for (const rule of rulesByLevel.L3) {
|
|
139
|
+
const module = rule.module || 'default';
|
|
140
|
+
const moduleDir = path.join(l3Dir, module);
|
|
141
|
+
await fsPromises.ensureDir(moduleDir);
|
|
142
|
+
|
|
143
|
+
const filePath = path.join(moduleDir, `${rule.id}.yml`);
|
|
144
|
+
const content = this.ruleRender.renderRule(rule, 'L3');
|
|
145
|
+
await fsPromises.writeFile(filePath, content, 'utf-8');
|
|
146
|
+
result.files.push({ path: filePath, type: 'rule-L3', level: 'L3', module });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 渲染custom规则
|
|
151
|
+
if (rulesByLevel.custom && rulesByLevel.custom.length > 0) {
|
|
152
|
+
const customDir = path.join(rulesDir, 'custom');
|
|
153
|
+
await fsPromises.ensureDir(customDir);
|
|
154
|
+
for (const rule of rulesByLevel.custom) {
|
|
155
|
+
const filePath = path.join(customDir, `${rule.id}.yml`);
|
|
156
|
+
const content = this.ruleRender.renderRule(rule, 'custom');
|
|
157
|
+
await fsPromises.writeFile(filePath, content, 'utf-8');
|
|
158
|
+
result.files.push({ path: filePath, type: 'rule-custom', level: 'custom' });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
} catch (e) {
|
|
163
|
+
result.error = `规则渲染失败: ${e.message}`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 按优先级分组规则
|
|
171
|
+
*/
|
|
172
|
+
groupRulesByLevel(decisionResult) {
|
|
173
|
+
const rules = decisionResult.decision?.recommendedRules || [];
|
|
174
|
+
const groups = { L1: [], L2: [], L3: [], custom: [] };
|
|
175
|
+
|
|
176
|
+
for (const rule of rules) {
|
|
177
|
+
const level = rule.level || 'L3';
|
|
178
|
+
if (groups[level]) {
|
|
179
|
+
groups[level].push(rule);
|
|
180
|
+
} else {
|
|
181
|
+
groups.custom.push(rule);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return groups;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* 获取渲染结果摘要
|
|
190
|
+
*/
|
|
191
|
+
getSummary() {
|
|
192
|
+
return {
|
|
193
|
+
totalFiles: this.renderedFiles.length,
|
|
194
|
+
files: this.renderedFiles,
|
|
195
|
+
priority: 'AGENTS.md > CLAUDE.md > .trae/rules'
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
module.exports = { RenderCore };
|