mr-sliy 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 (50) hide show
  1. package/.env.example +145 -0
  2. package/database/schema.sql +187 -0
  3. package/package.json +74 -0
  4. package/scripts/download-tree-sitter.js +171 -0
  5. package/scripts/postinstall.js +134 -0
  6. package/src/agent/agent.js +563 -0
  7. package/src/agent.js +87 -0
  8. package/src/cli/index.js +1643 -0
  9. package/src/config/index.js +232 -0
  10. package/src/engine/dualModeEngine.js +486 -0
  11. package/src/index.js +165 -0
  12. package/src/middlewares/errorHandler.js +166 -0
  13. package/src/middlewares/index.js +23 -0
  14. package/src/routes/aiRoutes.js +117 -0
  15. package/src/routes/configRoutes.js +31 -0
  16. package/src/routes/index.js +75 -0
  17. package/src/routes/issueRoutes.js +195 -0
  18. package/src/routes/projectRoutes.js +46 -0
  19. package/src/routes/reportRoutes.js +40 -0
  20. package/src/routes/scanRoutes.js +245 -0
  21. package/src/routes/userRoutes.js +47 -0
  22. package/src/services/ast/parser.js +503 -0
  23. package/src/services/detection/detector.js +934 -0
  24. package/src/services/llm/providers.js +1107 -0
  25. package/src/services/rag/agent.js +375 -0
  26. package/src/services/vector/knowledgeBase.js +863 -0
  27. package/src/skills/Skill.js +38 -0
  28. package/src/skills/code-analysis/index.js +272 -0
  29. package/src/skills/code-detection/index.js +166 -0
  30. package/src/skills/code-detection/rules/console-log.js +45 -0
  31. package/src/skills/code-detection/rules/deep-nesting.js +76 -0
  32. package/src/skills/code-detection/rules/duplicate-code.js +57 -0
  33. package/src/skills/code-detection/rules/high-complexity.js +109 -0
  34. package/src/skills/code-detection/rules/index.js +59 -0
  35. package/src/skills/code-detection/rules/long-functions.js +54 -0
  36. package/src/skills/code-detection/rules/magic-numbers.js +48 -0
  37. package/src/skills/code-detection/rules/missing-comment.js +64 -0
  38. package/src/skills/code-detection/rules/null-check.js +71 -0
  39. package/src/skills/code-detection/rules/unnecessary-else.js +46 -0
  40. package/src/skills/code-detection/rules/unused-functions.js +57 -0
  41. package/src/skills/code-detection/rules/unused-imports.js +57 -0
  42. package/src/skills/code-detection/rules/unused-variables.js +54 -0
  43. package/src/skills/code-optimization/index.js +319 -0
  44. package/src/skills/index.js +152 -0
  45. package/src/utils/crypto.js +212 -0
  46. package/src/utils/database.js +125 -0
  47. package/src/utils/helpers.js +226 -0
  48. package/src/utils/logger.js +202 -0
  49. package/src/utils/mysql.js +198 -0
  50. package/src/utils/response.js +124 -0
@@ -0,0 +1,232 @@
1
+ /**
2
+ * 配置管理模块
3
+ * 统一管理应用配置
4
+ */
5
+
6
+ require('dotenv').config();
7
+
8
+ const config = {
9
+ // 服务配置
10
+ server: {
11
+ port: parseInt(process.env.PORT) || 3000,
12
+ nodeEnv: process.env.NODE_ENV || 'development',
13
+ host: process.env.HOST || 'localhost'
14
+ },
15
+
16
+ // 数据库配置
17
+ database: {
18
+ path: process.env.DB_PATH || './database/code_optimizer.db',
19
+ // SQLite不需要连接池,但保留配置以备后用
20
+ pool: {
21
+ max: parseInt(process.env.DB_POOL_MAX) || 10,
22
+ min: parseInt(process.env.DB_POOL_MIN) || 2
23
+ }
24
+ },
25
+
26
+ // MySQL配置(云端同步,可选)
27
+ mysql: {
28
+ enabled: process.env.MYSQL_ENABLED === 'true' || false,
29
+ host: process.env.MYSQL_HOST || '',
30
+ port: parseInt(process.env.MYSQL_PORT) || 3306,
31
+ user: process.env.MYSQL_USER || '',
32
+ password: process.env.MYSQL_PASSWORD || '',
33
+ database: process.env.MYSQL_DATABASE || 'code_optimizer',
34
+ connectionLimit: parseInt(process.env.MYSQL_CONNECTION_LIMIT) || 10,
35
+ syncOnStartup: process.env.MYSQL_SYNC_ON_STARTUP !== 'false'
36
+ },
37
+
38
+ // AI配置
39
+ ai: {
40
+ apiKey: process.env.AI_API_KEY || '',
41
+ apiUrl: process.env.AI_API_URL || 'https://api.openai.com/v1',
42
+ model: process.env.AI_MODEL || 'gpt-4',
43
+ timeout: parseInt(process.env.AI_TIMEOUT) || 30000,
44
+ maxTokens: parseInt(process.env.AI_MAX_TOKENS) || 2000,
45
+ temperature: parseFloat(process.env.AI_TEMPERATURE) || 0.7
46
+ },
47
+
48
+ // LLM提供商配置
49
+ llm: {
50
+ // OpenAI
51
+ openai: {
52
+ apiKey: process.env.OPENAI_API_KEY || '',
53
+ baseURL: process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1',
54
+ model: process.env.OPENAI_MODEL || 'gpt-4'
55
+ },
56
+ // Claude
57
+ claude: {
58
+ apiKey: process.env.CLAUDE_API_KEY || '',
59
+ model: process.env.CLAUDE_MODEL || 'claude-3-sonnet-20240229'
60
+ },
61
+ // Azure OpenAI
62
+ azure: {
63
+ apiKey: process.env.AZURE_OPENAI_KEY || '',
64
+ endpoint: process.env.AZURE_OPENAI_ENDPOINT || '',
65
+ deploymentName: process.env.AZURE_DEPLOYMENT_NAME || 'gpt-4'
66
+ },
67
+ // Google Gemini
68
+ gemini: {
69
+ apiKey: process.env.GEMINI_API_KEY || '',
70
+ model: process.env.GEMINI_MODEL || 'gemini-1.5-pro'
71
+ },
72
+ // 阿里通义千问
73
+ tongyi: {
74
+ apiKey: process.env.TONGYI_API_KEY || '',
75
+ model: process.env.TONGYI_MODEL || 'qwen-plus'
76
+ },
77
+ // 字节豆包
78
+ doubao: {
79
+ apiKey: process.env.DOUBAO_API_KEY || '',
80
+ model: process.env.DOUBAO_MODEL || 'Doubao-7B'
81
+ },
82
+ // 百度文心一言
83
+ wenxin: {
84
+ apiKey: process.env.WENXIN_API_KEY || '',
85
+ secretKey: process.env.WENXIN_SECRET_KEY || '',
86
+ model: process.env.WENXIN_MODEL || 'ernie-3.5'
87
+ },
88
+ // DeepSeek
89
+ deepseek: {
90
+ apiKey: process.env.DEEPSEEK_API_KEY || '',
91
+ baseURL: process.env.DEEPSEEK_BASE_URL || 'https://api.deepseek.com/v1',
92
+ model: process.env.DEEPSEEK_MODEL || 'deepseek-chat'
93
+ },
94
+ // 智谱AI
95
+ zhipu: {
96
+ apiKey: process.env.ZHIPU_API_KEY || '',
97
+ baseURL: process.env.ZHIPU_BASE_URL || 'https://open.bigmodel.cn/api/paas/v4',
98
+ model: process.env.ZHIPU_MODEL || 'glm-4'
99
+ },
100
+ // Moonshot AI
101
+ moonshot: {
102
+ apiKey: process.env.MOONSHOT_API_KEY || '',
103
+ baseURL: process.env.MOONSHOT_BASE_URL || 'https://api.moonshot.cn/v1',
104
+ model: process.env.MOONSHOT_MODEL || 'moonshot-v1-8k'
105
+ },
106
+ // Ollama (本地)
107
+ ollama: {
108
+ baseURL: process.env.OLLAMA_URL || 'http://localhost:11434',
109
+ model: process.env.OLLAMA_MODEL || 'codellama'
110
+ }
111
+ },
112
+
113
+ // AST扫描配置
114
+ scan: {
115
+ maxFileSize: parseInt(process.env.MAX_FILE_SIZE) || 1048576, // 1MB
116
+ timeout: parseInt(process.env.SCAN_TIMEOUT) || 60000, // 60秒
117
+ enableParallel: process.env.ENABLE_PARALLEL_SCAN !== 'false',
118
+ maxParallelJobs: parseInt(process.env.MAX_PARALLEL_JOBS) || 4,
119
+ extensions: (process.env.SCAN_EXTENSIONS || '.js,.ts,.jsx,.tsx,.py,.java,.go,.rs')
120
+ .split(',')
121
+ .map(ext => ext.trim()),
122
+ excludeDirs: (process.env.EXCLUDE_DIRS || 'node_modules,dist,build,out,.git,coverage')
123
+ .split(',')
124
+ .map(dir => dir.trim()),
125
+ excludeFiles: (process.env.EXCLUDE_FILES || 'min.js,min.css,.d.ts')
126
+ .split(',')
127
+ .map(file => file.trim())
128
+ },
129
+
130
+ // 检测规则配置
131
+ detection: {
132
+ unusedVariables: process.env.DETECT_UNUSED_VARIABLES !== 'false',
133
+ unusedImports: process.env.DETECT_UNUSED_IMPORTS !== 'false',
134
+ unusedFunctions: process.env.DETECT_UNUSED_FUNCTIONS !== 'false',
135
+ magicNumbers: process.env.DETECT_MAGIC_NUMBERS !== 'false',
136
+ maxFunctionLines: parseInt(process.env.MAX_FUNCTION_LINES) || 50,
137
+ maxCyclomaticComplexity: parseInt(process.env.MAX_CYCLOMATIC_COMPLEXITY) || 10,
138
+ maxNestingDepth: parseInt(process.env.MAX_NESTING_DEPTH) || 4,
139
+ enableDeepNestingCheck: process.env.ENABLE_DEEP_NESTING_CHECK !== 'false',
140
+ enableNullCheck: process.env.ENABLE_NULL_CHECK !== 'false',
141
+ enableConsoleLogCheck: process.env.ENABLE_CONSOLE_LOG_CHECK !== 'false',
142
+ enableDuplicateCodeCheck: process.env.ENABLE_DUPLICATE_CODE_CHECK !== 'false',
143
+ enableCommentCheck: process.env.ENABLE_COMMENT_CHECK !== 'false'
144
+ },
145
+
146
+ // 日志配置
147
+ logging: {
148
+ level: process.env.LOG_LEVEL || 'info',
149
+ file: process.env.LOG_FILE || './logs/app.log',
150
+ maxSize: parseInt(process.env.LOG_MAX_SIZE) || 5242880, // 5MB
151
+ maxFiles: parseInt(process.env.LOG_MAX_FILES) || 5
152
+ },
153
+
154
+ // CORS配置
155
+ cors: {
156
+ origins: (process.env.CORS_ORIGINS || 'http://localhost:5173,http://localhost:3000,http://localhost:1420')
157
+ .split(',')
158
+ .map(origin => origin.trim()),
159
+ credentials: true
160
+ },
161
+
162
+ // 速率限制配置
163
+ rateLimit: {
164
+ windowMs: 15 * 60 * 1000, // 15分钟
165
+ max: parseInt(process.env.RATE_LIMIT_MAX) || 100
166
+ },
167
+
168
+ // 默认运行模式
169
+ defaultMode: process.env.DEFAULT_MODE || 'offline' // offline | online
170
+ };
171
+
172
+ /**
173
+ * 获取配置值
174
+ */
175
+ function get(key, defaultValue = null) {
176
+ const keys = key.split('.');
177
+ let value = config;
178
+
179
+ for (const k of keys) {
180
+ if (value && typeof value === 'object' && k in value) {
181
+ value = value[k];
182
+ } else {
183
+ return defaultValue;
184
+ }
185
+ }
186
+
187
+ return value;
188
+ }
189
+
190
+ /**
191
+ * 检查是否为在线模式
192
+ */
193
+ function isOnlineMode() {
194
+ return config.defaultMode === 'online' && config.ai.apiKey && config.ai.apiKey.length > 0;
195
+ }
196
+
197
+ /**
198
+ * 检查是否为离线模式
199
+ */
200
+ function isOfflineMode() {
201
+ return !isOnlineMode();
202
+ }
203
+
204
+ /**
205
+ * 验证必要配置
206
+ */
207
+ function validate() {
208
+ const errors = [];
209
+
210
+ if (!config.server.port) {
211
+ errors.push('服务端口未配置');
212
+ }
213
+
214
+ if (!config.database.path) {
215
+ errors.push('数据库路径未配置');
216
+ }
217
+
218
+ if (errors.length > 0) {
219
+ console.error('配置验证失败:', errors);
220
+ return false;
221
+ }
222
+
223
+ return true;
224
+ }
225
+
226
+ module.exports = {
227
+ config,
228
+ get,
229
+ isOnlineMode,
230
+ isOfflineMode,
231
+ validate
232
+ };
@@ -0,0 +1,486 @@
1
+ /**
2
+ * 双模工作引擎
3
+ * 核心特色:离线AST + 本地RAG知识库 / 在线AST + 云端大模型
4
+ * 自适应切换,AI为扩展功能,不依赖核心检测业务
5
+ */
6
+
7
+ const { detectIssues, batchDetect } = require('../services/detection/detector');
8
+ const { knowledgeBase } = require('../services/vector/knowledgeBase');
9
+ const { providerManager } = require('../services/llm/providers');
10
+ const { logger } = require('../utils/logger');
11
+ const { generateUUID, getFileLanguage } = require('../utils/helpers');
12
+ const { isOnlineMode } = require('../config');
13
+ const fs = require('fs');
14
+
15
+ /**
16
+ * 双模引擎类
17
+ */
18
+ class DualModeEngine {
19
+ constructor() {
20
+ this.mode = 'auto'; // 'offline', 'online', 'auto'
21
+ this.initialized = false;
22
+ }
23
+
24
+ /**
25
+ * 初始化引擎
26
+ */
27
+ init() {
28
+ if (this.initialized) return;
29
+
30
+ // 初始化本地知识库
31
+ knowledgeBase.init();
32
+ knowledgeBase.seedDefaultKnowledge();
33
+
34
+ this.initialized = true;
35
+ logger.info('双模引擎初始化完成');
36
+ }
37
+
38
+ /**
39
+ * 设置工作模式
40
+ */
41
+ setMode(mode) {
42
+ const validModes = ['offline', 'online', 'auto'];
43
+ if (!validModes.includes(mode)) {
44
+ throw new Error(`无效模式: ${mode},可选: ${validModes.join(', ')}`);
45
+ }
46
+ this.mode = mode;
47
+ logger.info(`工作模式切换为: ${mode}`);
48
+ }
49
+
50
+ /**
51
+ * 获取当前实际工作模式
52
+ */
53
+ getActualMode() {
54
+ if (this.mode === 'auto') {
55
+ // 自动判断:有可用云端提供商则在线,否则离线
56
+ const availableProviders = providerManager.getAvailableProviders();
57
+ const hasOnlineProvider = availableProviders.some(p => p.available && p.name !== 'ollama');
58
+ return hasOnlineProvider ? 'online' : 'offline';
59
+ }
60
+ return this.mode;
61
+ }
62
+
63
+ /**
64
+ * 检测并优化单个文件(双模入口)
65
+ */
66
+ async analyzeFile(filePath, options = {}) {
67
+ this.init();
68
+ const startTime = Date.now();
69
+ const actualMode = this.getActualMode();
70
+
71
+ logger.info(`开始分析文件: ${filePath} [模式: ${actualMode}]`);
72
+
73
+ try {
74
+ // 1. 读取文件
75
+ const sourceCode = fs.readFileSync(filePath, 'utf-8');
76
+ const language = getFileLanguage(filePath);
77
+
78
+ // 2. AST静态检测(离线核心,永远执行)
79
+ const detectionResult = await detectIssues(sourceCode, filePath);
80
+
81
+ if (!detectionResult.success) {
82
+ return {
83
+ success: false,
84
+ message: detectionResult.message,
85
+ filePath,
86
+ mode: actualMode
87
+ };
88
+ }
89
+
90
+ // 3. 根据模式进行优化
91
+ const optimizedIssues = [];
92
+
93
+ for (const issue of detectionResult.issues) {
94
+ const optimization = await this.optimizeIssue(issue, sourceCode, actualMode);
95
+ optimizedIssues.push({
96
+ ...issue,
97
+ optimization
98
+ });
99
+ }
100
+
101
+ const result = {
102
+ success: true,
103
+ filePath,
104
+ language,
105
+ mode: actualMode,
106
+ totalIssues: detectionResult.totalIssues,
107
+ issueCounts: detectionResult.issueCounts,
108
+ issues: optimizedIssues,
109
+ durationMs: Date.now() - startTime
110
+ };
111
+
112
+ logger.info(`文件分析完成: ${filePath}, 发现 ${detectionResult.totalIssues} 个问题`);
113
+ return result;
114
+
115
+ } catch (error) {
116
+ logger.error(`分析文件失败: ${filePath}`, error);
117
+ return {
118
+ success: false,
119
+ message: error.message,
120
+ filePath,
121
+ mode: actualMode
122
+ };
123
+ }
124
+ }
125
+
126
+ /**
127
+ * 分析代码片段(用于交互式使用)
128
+ */
129
+ async analyzeSnippet(codeSnippet, language = 'javascript', options = {}) {
130
+ this.init();
131
+ const startTime = Date.now();
132
+ const actualMode = this.getActualMode();
133
+
134
+ logger.info(`开始分析代码片段 [模式: ${actualMode}]`);
135
+
136
+ try {
137
+ // 1. AST检测
138
+ const detectionResult = await detectIssues(codeSnippet, `snippet.${language === 'javascript' ? 'js' : language}`);
139
+
140
+ // 2. 优化每个问题
141
+ const optimizedIssues = [];
142
+
143
+ for (const issue of detectionResult.issues) {
144
+ const optimization = await this.optimizeIssue(issue, codeSnippet, actualMode);
145
+ optimizedIssues.push({
146
+ ...issue,
147
+ optimization
148
+ });
149
+ }
150
+
151
+ // 3. 如果无问题但有优化需求,进行一般性优化
152
+ if (detectionResult.issues.length === 0 && options.generalOptimize) {
153
+ const generalOptimization = await this.optimizeIssue(
154
+ { codeSnippet, issueType: 'general', message: '一般性优化' },
155
+ codeSnippet,
156
+ actualMode
157
+ );
158
+ optimizedIssues.push({
159
+ id: generateUUID(),
160
+ issueType: 'general_optimization',
161
+ severity: 'low',
162
+ message: '建议进行一般性代码优化',
163
+ codeSnippet,
164
+ optimization: generalOptimization
165
+ });
166
+ }
167
+
168
+ return {
169
+ success: true,
170
+ mode: actualMode,
171
+ totalIssues: optimizedIssues.length,
172
+ issues: optimizedIssues,
173
+ durationMs: Date.now() - startTime
174
+ };
175
+
176
+ } catch (error) {
177
+ logger.error('分析代码片段失败:', error);
178
+ return {
179
+ success: false,
180
+ message: error.message,
181
+ mode: actualMode
182
+ };
183
+ }
184
+ }
185
+
186
+ /**
187
+ * 批量分析项目
188
+ */
189
+ async analyzeProject(projectPath, options = {}) {
190
+ this.init();
191
+ const startTime = Date.now();
192
+ const actualMode = this.getActualMode();
193
+
194
+ logger.info(`开始分析项目: ${projectPath} [模式: ${actualMode}]`);
195
+
196
+ // 收集文件
197
+ const files = this.collectProjectFiles(projectPath, options.extensions);
198
+
199
+ if (files.length === 0) {
200
+ return {
201
+ success: true,
202
+ projectPath,
203
+ mode: actualMode,
204
+ totalFiles: 0,
205
+ totalIssues: 0,
206
+ results: [],
207
+ durationMs: 0
208
+ };
209
+ }
210
+
211
+ // 批量检测
212
+ const results = [];
213
+ for (const file of files) {
214
+ const result = await this.analyzeFile(file, options);
215
+ results.push(result);
216
+ }
217
+
218
+ const totalIssues = results.reduce((sum, r) => sum + (r.totalIssues || 0), 0);
219
+ const successCount = results.filter(r => r.success).length;
220
+
221
+ return {
222
+ success: true,
223
+ projectPath,
224
+ mode: actualMode,
225
+ totalFiles: files.length,
226
+ scannedFiles: successCount,
227
+ failedFiles: files.length - successCount,
228
+ totalIssues,
229
+ results,
230
+ durationMs: Date.now() - startTime
231
+ };
232
+ }
233
+
234
+ /**
235
+ * 优化单个问题(双模核心)
236
+ * 离线模式:本地知识库RAG检索 + 规则匹配
237
+ * 在线模式:云端大模型 + RAG增强
238
+ */
239
+ async optimizeIssue(issue, fullCode, mode) {
240
+ const context = {
241
+ language: issue.language || 'javascript',
242
+ issueType: issue.issueType,
243
+ message: issue.message,
244
+ codeSnippet: issue.codeSnippet
245
+ };
246
+
247
+ if (mode === 'offline') {
248
+ return this.optimizeOffline(issue, context);
249
+ } else {
250
+ return this.optimizeOnline(issue, context);
251
+ }
252
+ }
253
+
254
+ /**
255
+ * 离线优化:本地知识库RAG
256
+ */
257
+ async optimizeOffline(issue, context) {
258
+ try {
259
+ // 1. 检索相似案例
260
+ const similarCases = knowledgeBase.findSimilarCases(issue.codeSnippet, {
261
+ language: context.language,
262
+ issueType: context.issueType,
263
+ topK: 3
264
+ });
265
+
266
+ // 2. 检索相关知识
267
+ const relatedKnowledge = knowledgeBase.searchEntries(
268
+ `${context.issueType} ${context.message}`,
269
+ {
270
+ language: context.language,
271
+ topK: 3
272
+ }
273
+ );
274
+
275
+ // 3. 构建离线优化建议
276
+ let optimizedCode = issue.codeSnippet;
277
+ let explanation = '';
278
+ const suggestions = [];
279
+
280
+ if (similarCases.length > 0 && similarCases[0].similarity > 0.5) {
281
+ const bestCase = similarCases[0];
282
+ optimizedCode = bestCase.optimizedCode;
283
+ explanation = bestCase.explanation;
284
+ suggestions.push(`参考相似案例(ID: ${bestCase.id})`);
285
+
286
+ // 更新案例使用次数
287
+ knowledgeBase.updateCaseUsage(bestCase.id, 5);
288
+ } else {
289
+ // 基于知识库生成建议
290
+ optimizedCode = this.applyOfflineRules(issue);
291
+ explanation = this.generateOfflineExplanation(issue, relatedKnowledge);
292
+ }
293
+
294
+ relatedKnowledge.forEach(k => {
295
+ if (k.similarity > 0.3) {
296
+ suggestions.push(k.content);
297
+ }
298
+ });
299
+
300
+ return {
301
+ success: true,
302
+ mode: 'offline',
303
+ optimizedCode,
304
+ explanation: explanation || '基于本地知识库规则的建议',
305
+ suggestions: suggestions.length > 0 ? suggestions : ['建议参考编码规范进行优化'],
306
+ similarCases: similarCases.slice(0, 3),
307
+ knowledgeSources: relatedKnowledge.slice(0, 3)
308
+ };
309
+
310
+ } catch (error) {
311
+ logger.error('离线优化失败:', error);
312
+ return {
313
+ success: false,
314
+ mode: 'offline',
315
+ message: error.message,
316
+ optimizedCode: issue.codeSnippet,
317
+ explanation: '离线优化失败,建议切换在线模式',
318
+ suggestions: []
319
+ };
320
+ }
321
+ }
322
+
323
+ /**
324
+ * 在线优化:云端大模型 + RAG增强
325
+ */
326
+ async optimizeOnline(issue, context) {
327
+ try {
328
+ // 1. 先进行RAG检索(增强上下文)
329
+ const similarCases = knowledgeBase.findSimilarCases(issue.codeSnippet, {
330
+ language: context.language,
331
+ issueType: context.issueType,
332
+ topK: 3
333
+ });
334
+
335
+ const relatedKnowledge = knowledgeBase.searchEntries(
336
+ `${context.issueType} ${context.message}`,
337
+ {
338
+ language: context.language,
339
+ topK: 3
340
+ }
341
+ );
342
+
343
+ // 2. 构建增强提示
344
+ const enhancedContext = {
345
+ ...context,
346
+ similarCases: similarCases.filter(c => c.similarity > 0.3),
347
+ relatedKnowledge: relatedKnowledge.filter(k => k.similarity > 0.3)
348
+ };
349
+
350
+ // 3. 调用云端大模型
351
+ const llmResult = await providerManager.optimizeCode(issue.codeSnippet, enhancedContext);
352
+
353
+ // 4. 将优化结果存入知识库(持续学习)
354
+ if (llmResult.content && llmResult.content.optimizedCode) {
355
+ knowledgeBase.addCase(
356
+ issue.codeSnippet,
357
+ llmResult.content.optimizedCode,
358
+ llmResult.content.explanation || 'AI优化建议',
359
+ {
360
+ language: context.language,
361
+ issueType: context.issueType
362
+ }
363
+ );
364
+ }
365
+
366
+ return {
367
+ success: true,
368
+ mode: 'online',
369
+ optimizedCode: llmResult.content?.optimizedCode || issue.codeSnippet,
370
+ explanation: llmResult.content?.explanation || 'AI优化建议',
371
+ suggestions: llmResult.content?.suggestions || [],
372
+ rawResponse: llmResult.rawContent,
373
+ tokensUsed: llmResult.tokensUsed,
374
+ model: llmResult.model,
375
+ similarCases: similarCases.slice(0, 3)
376
+ };
377
+
378
+ } catch (error) {
379
+ logger.error('在线优化失败,回退到离线模式:', error);
380
+ // 在线失败时自动回退到离线模式
381
+ return this.optimizeOffline(issue, context);
382
+ }
383
+ }
384
+
385
+ /**
386
+ * 应用离线规则生成优化代码
387
+ */
388
+ applyOfflineRules(issue) {
389
+ const code = issue.codeSnippet;
390
+
391
+ switch (issue.issueType) {
392
+ case 'unused_variable':
393
+ return `// 建议删除未使用的变量\n// ${code}`;
394
+
395
+ case 'unused_import':
396
+ return `// 建议删除未使用的导入\n// ${code}`;
397
+
398
+ case 'magic_number': {
399
+ const match = code.match(/(\d+)/);
400
+ const num = match ? match[1] : 'NUMBER';
401
+ return code.replace(new RegExp(num, 'g'), `CONSTANT_${num}`);
402
+ }
403
+
404
+ case 'long_function':
405
+ return `// 建议将函数拆分为多个小函数\n// ${code.substring(0, 50)}...`;
406
+
407
+ default:
408
+ return code;
409
+ }
410
+ }
411
+
412
+ /**
413
+ * 生成离线优化说明
414
+ */
415
+ generateOfflineExplanation(issue, knowledge) {
416
+ let explanation = issue.suggestion || '';
417
+
418
+ if (knowledge.length > 0) {
419
+ explanation += '\n\n参考知识:';
420
+ knowledge.slice(0, 2).forEach(k => {
421
+ explanation += `\n- ${k.content}`;
422
+ });
423
+ }
424
+
425
+ return explanation;
426
+ }
427
+
428
+ /**
429
+ * 收集项目文件
430
+ */
431
+ collectProjectFiles(projectPath, extensions = ['.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.go']) {
432
+ const files = [];
433
+ const excludeDirs = ['node_modules', 'dist', 'build', 'out', '.git', 'coverage', 'vendor', '__pycache__'];
434
+
435
+ const walk = (dir) => {
436
+ try {
437
+ const items = fs.readdirSync(dir);
438
+ for (const item of items) {
439
+ const fullPath = require('path').join(dir, item);
440
+ if (excludeDirs.includes(item)) continue;
441
+
442
+ const stat = fs.statSync(fullPath);
443
+ if (stat.isDirectory()) {
444
+ walk(fullPath);
445
+ } else if (extensions.some(ext => item.endsWith(ext))) {
446
+ files.push(fullPath);
447
+ }
448
+ }
449
+ } catch (e) {
450
+ // 忽略无权限访问的目录
451
+ }
452
+ };
453
+
454
+ walk(projectPath);
455
+ return files;
456
+ }
457
+
458
+ /**
459
+ * 获取引擎状态
460
+ */
461
+ getStatus() {
462
+ const actualMode = this.getActualMode();
463
+ const providers = providerManager.getAvailableProviders();
464
+ const kbStats = knowledgeBase.getStats();
465
+
466
+ return {
467
+ mode: this.mode,
468
+ actualMode,
469
+ initialized: this.initialized,
470
+ providers: providers.map(p => ({
471
+ name: p.name,
472
+ available: p.available,
473
+ model: p.model
474
+ })),
475
+ knowledgeBase: kbStats
476
+ };
477
+ }
478
+ }
479
+
480
+ // 单例实例
481
+ const engine = new DualModeEngine();
482
+
483
+ module.exports = {
484
+ DualModeEngine,
485
+ engine
486
+ };