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,195 @@
1
+ /**
2
+ * 缺陷路由模块
3
+ * 处理代码缺陷查询和管理
4
+ */
5
+
6
+ const express = require('express');
7
+ const router = express.Router();
8
+ const { getDatabase } = require('../utils/database');
9
+ const { success, error, paginate } = require('../utils/response');
10
+ const { logger } = require('../utils/logger');
11
+
12
+ /**
13
+ * 获取缺陷列表
14
+ */
15
+ router.get('/', (req, res) => {
16
+ try {
17
+ const db = getDatabase();
18
+
19
+ const page = parseInt(req.query.page) || 1;
20
+ const pageSize = parseInt(req.query.pageSize) || 20;
21
+ const taskId = req.query.taskId;
22
+ const projectId = req.query.projectId;
23
+ const severity = req.query.severity;
24
+ const issueType = req.query.issueType;
25
+ const isFixed = req.query.isFixed;
26
+
27
+ // 构建查询条件
28
+ let whereClause = 'WHERE 1=1';
29
+ const params = [];
30
+
31
+ if (taskId) {
32
+ whereClause += ' AND task_id = ?';
33
+ params.push(taskId);
34
+ }
35
+
36
+ if (projectId) {
37
+ whereClause += ' AND project_id = ?';
38
+ params.push(projectId);
39
+ }
40
+
41
+ if (severity) {
42
+ whereClause += ' AND severity = ?';
43
+ params.push(severity);
44
+ }
45
+
46
+ if (issueType) {
47
+ whereClause += ' AND issue_type = ?';
48
+ params.push(issueType);
49
+ }
50
+
51
+ if (isFixed) {
52
+ whereClause += ' AND is_fixed = ?';
53
+ params.push(isFixed === 'true' ? 1 : 0);
54
+ }
55
+
56
+ // 查询总数
57
+ const countStmt = db.prepare(`SELECT COUNT(*) as total FROM code_issue ${whereClause}`);
58
+ const countResult = countStmt.get(...params);
59
+ const total = countResult.total;
60
+
61
+ // 查询数据
62
+ const offset = (page - 1) * pageSize;
63
+ const dataStmt = db.prepare(`
64
+ SELECT * FROM code_issue ${whereClause}
65
+ ORDER BY created_at DESC
66
+ LIMIT ? OFFSET ?
67
+ `);
68
+
69
+ const issues = dataStmt.all(...params, pageSize, offset);
70
+
71
+ return res.json(paginate(issues, total, page, pageSize));
72
+ } catch (err) {
73
+ logger.error('获取缺陷列表失败:', err);
74
+ return res.status(500).json(error(err.message));
75
+ }
76
+ });
77
+
78
+ /**
79
+ * 获取缺陷详情
80
+ */
81
+ router.get('/:id', (req, res) => {
82
+ try {
83
+ const db = getDatabase();
84
+ const { id } = req.params;
85
+
86
+ const stmt = db.prepare('SELECT * FROM code_issue WHERE id = ?');
87
+ const issue = stmt.get(id);
88
+
89
+ if (!issue) {
90
+ return res.status(404).json(error('缺陷未找到', 404));
91
+ }
92
+
93
+ // 获取相关的AI优化记录
94
+ const optimizeStmt = db.prepare('SELECT * FROM ai_optimize_record WHERE issue_id = ?');
95
+ const optimizations = optimizeStmt.all(id);
96
+
97
+ return res.json(success({
98
+ issue,
99
+ optimizations
100
+ }));
101
+ } catch (err) {
102
+ logger.error('获取缺陷详情失败:', err);
103
+ return res.status(500).json(error(err.message));
104
+ }
105
+ });
106
+
107
+ /**
108
+ * 更新缺陷状态(标记为已修复)
109
+ */
110
+ router.put('/:id/fix', (req, res) => {
111
+ try {
112
+ const db = getDatabase();
113
+ const { id } = req.params;
114
+ const { userId } = req.body;
115
+
116
+ const stmt = db.prepare(`
117
+ UPDATE code_issue
118
+ SET is_fixed = 1, fixed_at = CURRENT_TIMESTAMP, fixed_by_user_id = ?
119
+ WHERE id = ?
120
+ `);
121
+
122
+ const result = stmt.run(userId || null, id);
123
+
124
+ if (result.changes === 0) {
125
+ return res.status(404).json(error('缺陷未找到', 404));
126
+ }
127
+
128
+ logger.info(`缺陷已标记为修复: ${id}`);
129
+
130
+ return res.json(success({
131
+ id,
132
+ isFixed: true
133
+ }));
134
+ } catch (err) {
135
+ logger.error('更新缺陷状态失败:', err);
136
+ return res.status(500).json(error(err.message));
137
+ }
138
+ });
139
+
140
+ /**
141
+ * 获取缺陷统计信息
142
+ */
143
+ router.get('/stats', (req, res) => {
144
+ try {
145
+ const db = getDatabase();
146
+
147
+ // 按类型统计
148
+ const typeStmt = db.prepare(`
149
+ SELECT issue_type, COUNT(*) as count
150
+ FROM code_issue
151
+ GROUP BY issue_type
152
+ ORDER BY count DESC
153
+ `);
154
+ const typeStats = typeStmt.all();
155
+
156
+ // 按严重程度统计
157
+ const severityStmt = db.prepare(`
158
+ SELECT severity, COUNT(*) as count
159
+ FROM code_issue
160
+ GROUP BY severity
161
+ `);
162
+ const severityStats = severityStmt.all();
163
+
164
+ // 按语言统计
165
+ const languageStmt = db.prepare(`
166
+ SELECT language, COUNT(*) as count
167
+ FROM code_issue
168
+ GROUP BY language
169
+ ORDER BY count DESC
170
+ `);
171
+ const languageStats = languageStmt.all();
172
+
173
+ // 总数统计
174
+ const totalStmt = db.prepare('SELECT COUNT(*) as total FROM code_issue');
175
+ const totalResult = totalStmt.get();
176
+
177
+ // 已修复统计
178
+ const fixedStmt = db.prepare('SELECT COUNT(*) as fixed FROM code_issue WHERE is_fixed = 1');
179
+ const fixedResult = fixedStmt.get();
180
+
181
+ return res.json(success({
182
+ total: totalResult.total,
183
+ fixed: fixedResult.fixed,
184
+ unfixed: totalResult.total - fixedResult.fixed,
185
+ typeStats,
186
+ severityStats,
187
+ languageStats
188
+ }));
189
+ } catch (err) {
190
+ logger.error('获取缺陷统计失败:', err);
191
+ return res.status(500).json(error(err.message));
192
+ }
193
+ });
194
+
195
+ module.exports = router;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * 项目路由模块
3
+ */
4
+
5
+ const express = require('express');
6
+ const router = express.Router();
7
+ const { getDatabase } = require('../utils/database');
8
+ const { success, error, paginate } = require('../utils/response');
9
+ const { logger } = require('../utils/logger');
10
+
11
+ router.get('/', (req, res) => {
12
+ try {
13
+ const db = getDatabase();
14
+ const page = parseInt(req.query.page) || 1;
15
+ const pageSize = parseInt(req.query.pageSize) || 20;
16
+
17
+ const countStmt = db.prepare('SELECT COUNT(*) as total FROM scan_project WHERE status != "deleted"');
18
+ const countResult = countStmt.get();
19
+
20
+ const offset = (page - 1) * pageSize;
21
+ const dataStmt = db.prepare(`SELECT * FROM scan_project WHERE status != "deleted" ORDER BY created_at DESC LIMIT ? OFFSET ?`);
22
+ const projects = dataStmt.all(pageSize, offset);
23
+
24
+ return res.json(paginate(projects, countResult.total, page, pageSize));
25
+ } catch (err) {
26
+ return res.status(500).json(error(err.message));
27
+ }
28
+ });
29
+
30
+ router.get('/:id', (req, res) => {
31
+ try {
32
+ const db = getDatabase();
33
+ const stmt = db.prepare('SELECT * FROM scan_project WHERE id = ?');
34
+ const project = stmt.get(req.params.id);
35
+
36
+ if (!project) {
37
+ return res.status(404).json(error('项目未找到', 404));
38
+ }
39
+
40
+ return res.json(success(project));
41
+ } catch (err) {
42
+ return res.status(500).json(error(err.message));
43
+ }
44
+ });
45
+
46
+ module.exports = router;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * 报告路由模块
3
+ */
4
+
5
+ const express = require('express');
6
+ const router = express.Router();
7
+ const { getDatabase } = require('../utils/database');
8
+ const { success, error } = require('../utils/response');
9
+ const { generateUUID } = require('../utils/helpers');
10
+
11
+ router.post('/generate', (req, res) => {
12
+ try {
13
+ const { taskId, format, includeAiSuggestions } = req.body;
14
+
15
+ const reportId = generateUUID();
16
+ const reportUrl = `http://localhost:3000/reports/${reportId}.${format || 'pdf'}`;
17
+
18
+ return res.json(success({
19
+ reportId,
20
+ url: reportUrl,
21
+ format: format || 'pdf'
22
+ }));
23
+ } catch (err) {
24
+ return res.status(500).json(error(err.message));
25
+ }
26
+ });
27
+
28
+ router.get('/', (req, res) => {
29
+ try {
30
+ const db = getDatabase();
31
+ const stmt = db.prepare('SELECT * FROM code_report ORDER BY created_at DESC LIMIT 20');
32
+ const reports = stmt.all();
33
+
34
+ return res.json(success(reports));
35
+ } catch (err) {
36
+ return res.status(500).json(error(err.message));
37
+ }
38
+ });
39
+
40
+ module.exports = router;
@@ -0,0 +1,245 @@
1
+ /**
2
+ * 扫描路由模块
3
+ * 处理代码扫描相关请求
4
+ */
5
+
6
+ const express = require('express');
7
+ const router = express.Router();
8
+ const { detectIssues, batchDetect, saveDetectionResults } = require('../services/detection/detector');
9
+ const { optimizeWithRAG } = require('../services/rag/agent');
10
+ const { getDatabase } = require('../utils/database');
11
+ const { success, error } = require('../utils/response');
12
+ const { logger } = require('../utils/logger');
13
+ const { generateUUID, getFileLanguage } = require('../utils/helpers');
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+
17
+ /**
18
+ * 扫描单个文件
19
+ */
20
+ router.post('/file', async (req, res) => {
21
+ const startTime = Date.now();
22
+
23
+ try {
24
+ const { filePath, sourceCode, mode } = req.body;
25
+
26
+ if (!filePath || !sourceCode) {
27
+ return res.status(400).json(error('缺少必要参数', 400));
28
+ }
29
+
30
+ // 执行AST检测
31
+ const detectionResult = await detectIssues(sourceCode, filePath);
32
+
33
+ if (!detectionResult.success) {
34
+ return res.json(error(detectionResult.message));
35
+ }
36
+
37
+ // 如果为在线模式,执行AI优化
38
+ if (mode === 'online' && detectionResult.issues.length > 0) {
39
+ const optimizationResults = [];
40
+
41
+ for (const issue of detectionResult.issues.slice(0, 5)) { // 限制优化数量
42
+ const optimization = await optimizeWithRAG(issue, {
43
+ language: getFileLanguage(filePath),
44
+ issueType: issue.issueType,
45
+ message: issue.message,
46
+ taskId: null
47
+ });
48
+
49
+ optimizationResults.push(optimization);
50
+ }
51
+
52
+ detectionResult.optimizations = optimizationResults;
53
+ }
54
+
55
+ logger.info(`扫描文件完成: ${filePath}`);
56
+
57
+ return res.json(success({
58
+ filePath,
59
+ language: detectionResult.language,
60
+ totalIssues: detectionResult.totalIssues,
61
+ issueCounts: detectionResult.issueCounts,
62
+ issues: detectionResult.issues,
63
+ optimizations: detectionResult.optimizations || [],
64
+ durationMs: Date.now() - startTime
65
+ }));
66
+ } catch (err) {
67
+ logger.error('扫描文件失败:', err);
68
+ return res.status(500).json(error(err.message));
69
+ }
70
+ });
71
+
72
+ /**
73
+ * 扫描整个项目
74
+ */
75
+ router.post('/project', async (req, res) => {
76
+ const startTime = Date.now();
77
+
78
+ try {
79
+ const { projectPath, mode } = req.body;
80
+
81
+ if (!projectPath) {
82
+ return res.status(400).json(error('缺少项目路径', 400));
83
+ }
84
+
85
+ // 收集所有待扫描的文件
86
+ const filesToScan = collectFiles(projectPath);
87
+
88
+ if (filesToScan.length === 0) {
89
+ return res.json(success({
90
+ totalFiles: 0,
91
+ scannedFiles: 0,
92
+ totalIssues: 0,
93
+ durationMs: Date.now() - startTime
94
+ }));
95
+ }
96
+
97
+ // 执行批量扫描
98
+ const scanResults = await batchDetect(filesToScan);
99
+
100
+ // 创建扫描任务记录
101
+ const taskId = generateUUID();
102
+ const projectId = await createProjectRecord(projectPath);
103
+
104
+ await createTaskRecord(taskId, projectId, {
105
+ scanMode: mode,
106
+ scanType: 'full_project',
107
+ targetPath: projectPath,
108
+ fileCount: filesToScan.length,
109
+ scannedFiles: scanResults.scannedFiles,
110
+ issueCount: scanResults.totalIssues,
111
+ durationMs: Date.now() - startTime
112
+ });
113
+
114
+ // 存储检测结果
115
+ await saveDetectionResults(taskId, projectId, scanResults.results);
116
+
117
+ logger.info(`项目扫描完成: ${projectPath}`);
118
+
119
+ return res.json(success({
120
+ taskId,
121
+ projectId,
122
+ projectPath,
123
+ totalFiles: filesToScan.length,
124
+ scannedFiles: scanResults.scannedFiles,
125
+ failedFiles: scanResults.failedFiles,
126
+ totalIssues: scanResults.totalIssues,
127
+ results: scanResults.results,
128
+ durationMs: Date.now() - startTime
129
+ }));
130
+ } catch (err) {
131
+ logger.error('项目扫描失败:', err);
132
+ return res.status(500).json(error(err.message));
133
+ }
134
+ });
135
+
136
+ /**
137
+ * 批量扫描指定文件列表
138
+ */
139
+ router.post('/batch', async (req, res) => {
140
+ const startTime = Date.now();
141
+
142
+ try {
143
+ const { filePaths, mode } = req.body;
144
+
145
+ if (!filePaths || !Array.isArray(filePaths) || filePaths.length === 0) {
146
+ return res.status(400).json(error('缺少文件列表', 400));
147
+ }
148
+
149
+ const scanResults = await batchDetect(filePaths);
150
+
151
+ return res.json(success({
152
+ totalFiles: filePaths.length,
153
+ scannedFiles: scanResults.scannedFiles,
154
+ failedFiles: scanResults.failedFiles,
155
+ totalIssues: scanResults.totalIssues,
156
+ results: scanResults.results,
157
+ durationMs: Date.now() - startTime
158
+ }));
159
+ } catch (err) {
160
+ logger.error('批量扫描失败:', err);
161
+ return res.status(500).json(error(err.message));
162
+ }
163
+ });
164
+
165
+ /**
166
+ * 收集待扫描的文件
167
+ */
168
+ function collectFiles(projectPath, extensions = ['.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.go']) {
169
+ const files = [];
170
+ const excludeDirs = ['node_modules', 'dist', 'build', 'out', '.git', 'coverage', 'vendor'];
171
+
172
+ function walkDir(dir) {
173
+ try {
174
+ const items = fs.readdirSync(dir);
175
+
176
+ for (const item of items) {
177
+ const fullPath = path.join(dir, item);
178
+
179
+ if (excludeDirs.includes(item)) {
180
+ continue;
181
+ }
182
+
183
+ const stat = fs.statSync(fullPath);
184
+
185
+ if (stat.isDirectory()) {
186
+ walkDir(fullPath);
187
+ } else if (stat.isFile()) {
188
+ const ext = path.extname(fullPath);
189
+ if (extensions.includes(ext)) {
190
+ files.push(fullPath);
191
+ }
192
+ }
193
+ }
194
+ } catch (err) {
195
+ logger.warn(`无法访问目录: ${dir}`);
196
+ }
197
+ }
198
+
199
+ walkDir(projectPath);
200
+ return files;
201
+ }
202
+
203
+ /**
204
+ * 创建项目记录
205
+ */
206
+ async function createProjectRecord(projectPath) {
207
+ const db = getDatabase();
208
+ const projectName = path.basename(projectPath);
209
+
210
+ const stmt = db.prepare(`
211
+ INSERT INTO scan_project (project_name, project_path, total_files)
212
+ VALUES (?, ?, ?)
213
+ `);
214
+
215
+ const result = stmt.run(projectName, projectPath, 0);
216
+ return result.lastInsertRowid;
217
+ }
218
+
219
+ /**
220
+ * 创建任务记录
221
+ */
222
+ async function createTaskRecord(taskId, projectId, data) {
223
+ const db = getDatabase();
224
+
225
+ const stmt = db.prepare(`
226
+ INSERT INTO scan_task (id, project_id, scan_mode, scan_type, target_path,
227
+ file_count, scanned_files, issue_count, duration_ms, status)
228
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
229
+ `);
230
+
231
+ stmt.run(
232
+ taskId,
233
+ projectId,
234
+ data.scanMode,
235
+ data.scanType,
236
+ data.targetPath,
237
+ data.fileCount,
238
+ data.scannedFiles,
239
+ data.issueCount,
240
+ data.durationMs,
241
+ 'completed'
242
+ );
243
+ }
244
+
245
+ module.exports = router;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * 用户路由模块
3
+ */
4
+
5
+ const express = require('express');
6
+ const router = express.Router();
7
+ const { getDatabase } = require('../utils/database');
8
+ const { success, error, paginate } = require('../utils/response');
9
+
10
+ router.get('/', (req, res) => {
11
+ try {
12
+ const db = getDatabase();
13
+ const page = parseInt(req.query.page) || 1;
14
+ const pageSize = parseInt(req.query.pageSize) || 20;
15
+
16
+ const countStmt = db.prepare('SELECT COUNT(*) as total FROM sys_user');
17
+ const countResult = countStmt.get();
18
+
19
+ const offset = (page - 1) * pageSize;
20
+ const dataStmt = db.prepare('SELECT id, username, email, role, status, created_at FROM sys_user ORDER BY created_at DESC LIMIT ? OFFSET ?');
21
+ const users = dataStmt.all(pageSize, offset);
22
+
23
+ return res.json(paginate(users, countResult.total, page, pageSize));
24
+ } catch (err) {
25
+ return res.status(500).json(error(err.message));
26
+ }
27
+ });
28
+
29
+ router.post('/login', (req, res) => {
30
+ try {
31
+ const db = getDatabase();
32
+ const { username, password } = req.body;
33
+
34
+ const stmt = db.prepare('SELECT * FROM sys_user WHERE username = ? AND password_hash = ?');
35
+ const user = stmt.get(username, password);
36
+
37
+ if (!user) {
38
+ return res.status(401).json(error('用户名或密码错误', 401));
39
+ }
40
+
41
+ return res.json(success({ id: user.id, username: user.username, role: user.role }));
42
+ } catch (err) {
43
+ return res.status(500).json(error(err.message));
44
+ }
45
+ });
46
+
47
+ module.exports = router;