pdd-skills 3.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 (261) hide show
  1. package/README.md +1478 -0
  2. package/bin/pdd.js +354 -0
  3. package/config/bpmn-rules.yaml +166 -0
  4. package/config/checkstyle.xml +105 -0
  5. package/config/eslint.config.js +48 -0
  6. package/config/pmd.xml +91 -0
  7. package/config/prd-rules.yaml +113 -0
  8. package/config/ruff.toml +45 -0
  9. package/config/sqlfluff.cfg +82 -0
  10. package/hooks/hook-executor.js +332 -0
  11. package/index.js +43 -0
  12. package/lib/api-routes.js +750 -0
  13. package/lib/api-server.js +408 -0
  14. package/lib/cache/cache-config.js +209 -0
  15. package/lib/cache/system-cache.js +852 -0
  16. package/lib/config-manager.js +373 -0
  17. package/lib/generate.js +528 -0
  18. package/lib/grpc/grpc-routes.js +1134 -0
  19. package/lib/grpc/grpc-server.js +912 -0
  20. package/lib/grpc/proto-definitions.js +1033 -0
  21. package/lib/init.js +172 -0
  22. package/lib/iteration/auto-fixer.js +1025 -0
  23. package/lib/iteration/auto-reviewer.js +923 -0
  24. package/lib/iteration/controller.js +577 -0
  25. package/lib/list.js +130 -0
  26. package/lib/mcp-server.js +548 -0
  27. package/lib/openclaw/api-integration.js +535 -0
  28. package/lib/openclaw/cli-integration.js +567 -0
  29. package/lib/openclaw/data-sync.js +845 -0
  30. package/lib/openclaw/openclaw-adapter.js +783 -0
  31. package/lib/plugin/example-plugins/code-stats/index.js +332 -0
  32. package/lib/plugin/example-plugins/code-stats/plugin.json +1 -0
  33. package/lib/plugin/example-plugins/custom-linter/index.js +472 -0
  34. package/lib/plugin/example-plugins/custom-linter/plugin.json +1 -0
  35. package/lib/plugin/example-plugins/hello-world/index.js +86 -0
  36. package/lib/plugin/example-plugins/hello-world/plugin.json +1 -0
  37. package/lib/plugin/plugin-manager.js +655 -0
  38. package/lib/plugin/plugin-sdk.js +565 -0
  39. package/lib/plugin/sandbox.js +627 -0
  40. package/lib/quality/rules/maintainability.js +418 -0
  41. package/lib/quality/rules/performance.js +498 -0
  42. package/lib/quality/rules/readability.js +441 -0
  43. package/lib/quality/rules/robustness.js +504 -0
  44. package/lib/quality/rules/security.js +444 -0
  45. package/lib/quality/scorer.js +576 -0
  46. package/lib/report.js +669 -0
  47. package/lib/sdk-base.js +301 -0
  48. package/lib/sdk-js.js +446 -0
  49. package/lib/sdk-python/README.md +546 -0
  50. package/lib/sdk-python/examples/basic_usage.py +450 -0
  51. package/lib/sdk-python/pdd_sdk/__init__.py +180 -0
  52. package/lib/sdk-python/pdd_sdk/client.py +1170 -0
  53. package/lib/sdk-python/pdd_sdk/events.py +423 -0
  54. package/lib/sdk-python/pdd_sdk/exceptions.py +158 -0
  55. package/lib/sdk-python/pdd_sdk/models.py +518 -0
  56. package/lib/sdk-python/pdd_sdk/utils.py +759 -0
  57. package/lib/token/budget-alert.js +367 -0
  58. package/lib/token/budget-manager.js +485 -0
  59. package/lib/update.js +54 -0
  60. package/lib/utils/logger.js +88 -0
  61. package/lib/verify.js +741 -0
  62. package/lib/version.js +52 -0
  63. package/lib/vm/README.md +102 -0
  64. package/lib/vm/dashboard/api-routes.js +669 -0
  65. package/lib/vm/dashboard/server.js +391 -0
  66. package/lib/vm/dashboard/sse.js +358 -0
  67. package/lib/vm/dashboard/static/css/dashboard.css +1378 -0
  68. package/lib/vm/dashboard/static/index.html +118 -0
  69. package/lib/vm/dashboard/static/js/app.js +949 -0
  70. package/lib/vm/dashboard/static/js/charts.js +913 -0
  71. package/lib/vm/dashboard/static/js/kanban-view.js +1053 -0
  72. package/lib/vm/dashboard/static/js/pipeline-view.js +463 -0
  73. package/lib/vm/dashboard/static/js/quality-view.js +598 -0
  74. package/lib/vm/dashboard/static/js/system-view.js +1021 -0
  75. package/lib/vm/data-provider.js +1191 -0
  76. package/lib/vm/event-bus.js +402 -0
  77. package/lib/vm/hooks/extract-hook.js +307 -0
  78. package/lib/vm/hooks/generate-hook.js +374 -0
  79. package/lib/vm/hooks/hook-interface.js +458 -0
  80. package/lib/vm/hooks/report-hook.js +331 -0
  81. package/lib/vm/hooks/verify-hook.js +454 -0
  82. package/lib/vm/models.js +1003 -0
  83. package/lib/vm/reconciler.js +855 -0
  84. package/lib/vm/scanner.js +988 -0
  85. package/lib/vm/state-schema.js +955 -0
  86. package/lib/vm/state-store.js +733 -0
  87. package/lib/vm/tui/components/card.js +339 -0
  88. package/lib/vm/tui/components/progress-bar.js +368 -0
  89. package/lib/vm/tui/components/sparkline.js +327 -0
  90. package/lib/vm/tui/components/status-light.js +294 -0
  91. package/lib/vm/tui/components/table.js +370 -0
  92. package/lib/vm/tui/input.js +335 -0
  93. package/lib/vm/tui/renderer.js +548 -0
  94. package/lib/vm/tui/screens/kanban-screen.js +397 -0
  95. package/lib/vm/tui/screens/overview-screen.js +357 -0
  96. package/lib/vm/tui/screens/quality-screen.js +336 -0
  97. package/lib/vm/tui/screens/system-screen.js +379 -0
  98. package/lib/vm/tui/tui.js +805 -0
  99. package/package.json +1 -0
  100. package/scripts/cso-analyzer.js +198 -0
  101. package/scripts/eval-runner.js +359 -0
  102. package/scripts/i18n-checker.js +109 -0
  103. package/scripts/linter/activiti-linter.js +272 -0
  104. package/scripts/linter/prd-linter.js +162 -0
  105. package/scripts/linter/report-generator.js +207 -0
  106. package/scripts/linter/run-linters.js +285 -0
  107. package/scripts/linter/sql-linter.js +166 -0
  108. package/scripts/token-analyzer.js +162 -0
  109. package/scripts/vm-test.js +180 -0
  110. package/skills/core/official-doc-writer/LICENSE +21 -0
  111. package/skills/core/official-doc-writer/README.md +232 -0
  112. package/skills/core/official-doc-writer/SKILL.md +475 -0
  113. package/skills/core/official-doc-writer/_meta.json +1 -0
  114. package/skills/core/official-doc-writer/document_generator.py +580 -0
  115. package/skills/core/official-doc-writer/evals/default-evals.json +1 -0
  116. package/skills/core/official-doc-writer/examples.md +150 -0
  117. package/skills/core/official-doc-writer/fonts/FONTS_LIST.md +45 -0
  118. package/skills/core/official-doc-writer/fonts/README.md +141 -0
  119. package/skills/core/official-doc-writer/fonts/SIMFANG.TTF +0 -0
  120. package/skills/core/official-doc-writer/fonts/SIMHEI.TTF +0 -0
  121. package/skills/core/official-doc-writer/fonts/SIMKAI.TTF +0 -0
  122. package/skills/core/official-doc-writer/fonts/SIMSUN.TTC +0 -0
  123. package/skills/core/official-doc-writer/fonts//346/226/271/346/255/243/345/260/217/346/240/207/345/256/213GBK.TTF +0 -0
  124. package/skills/core/official-doc-writer/references/GBT_9704-2012_/345/205/232/346/224/277/346/234/272/345/205/263/345/205/254/346/226/207/346/240/274/345/274/217.md +422 -0
  125. package/skills/core/official-doc-writer/scripts/__pycache__/generate_official_doc.cpython-313.pyc +0 -0
  126. package/skills/core/official-doc-writer/scripts/dialog_manager.py +564 -0
  127. package/skills/core/official-doc-writer/scripts/generate_official_doc.py +252 -0
  128. package/skills/core/official-doc-writer/scripts/install_fonts.py +390 -0
  129. package/skills/core/official-doc-writer/scripts/smart_prompts.py +363 -0
  130. package/skills/core/pdd-ba/SKILL.md +305 -0
  131. package/skills/core/pdd-ba/_meta.json +1 -0
  132. package/skills/core/pdd-ba/evals/default-evals.json +1 -0
  133. package/skills/core/pdd-code-reviewer/SKILL.md +378 -0
  134. package/skills/core/pdd-code-reviewer/_meta.json +1 -0
  135. package/skills/core/pdd-code-reviewer/evals/default-evals.json +1 -0
  136. package/skills/core/pdd-doc-change/SKILL.md +350 -0
  137. package/skills/core/pdd-doc-change/_meta.json +1 -0
  138. package/skills/core/pdd-doc-change/evals/default-evals.json +1 -0
  139. package/skills/core/pdd-doc-gardener/SKILL.md +248 -0
  140. package/skills/core/pdd-doc-gardener/_meta.json +1 -0
  141. package/skills/core/pdd-doc-gardener/evals/default-evals.json +1 -0
  142. package/skills/core/pdd-entropy-reduction/SKILL.md +360 -0
  143. package/skills/core/pdd-entropy-reduction/_meta.json +1 -0
  144. package/skills/core/pdd-entropy-reduction/evals/default-evals.json +1 -0
  145. package/skills/core/pdd-entropy-reduction/references/entropy-report-template.md +287 -0
  146. package/skills/core/pdd-entropy-reduction/references/golden-principles.md +573 -0
  147. package/skills/core/pdd-entropy-reduction/scripts/entropy_scan.py +712 -0
  148. package/skills/core/pdd-extract-features/SKILL.md +320 -0
  149. package/skills/core/pdd-extract-features/_meta.json +1 -0
  150. package/skills/core/pdd-extract-features/evals/default-evals.json +1 -0
  151. package/skills/core/pdd-generate-spec/SKILL.md +418 -0
  152. package/skills/core/pdd-generate-spec/_meta.json +1 -0
  153. package/skills/core/pdd-generate-spec/evals/default-evals.json +1 -0
  154. package/skills/core/pdd-implement-feature/SKILL.md +332 -0
  155. package/skills/core/pdd-implement-feature/_meta.json +1 -0
  156. package/skills/core/pdd-implement-feature/evals/default-evals.json +1 -0
  157. package/skills/core/pdd-main/SKILL.md +540 -0
  158. package/skills/core/pdd-main/_meta.json +1 -0
  159. package/skills/core/pdd-main/evals/default-evals.json +1 -0
  160. package/skills/core/pdd-main/evals/evals.json +215 -0
  161. package/skills/core/pdd-verify-feature/SKILL.md +474 -0
  162. package/skills/core/pdd-verify-feature/_meta.json +1 -0
  163. package/skills/core/pdd-verify-feature/evals/default-evals.json +1 -0
  164. package/skills/core/pdd-vm/evals/default-evals.json +1 -0
  165. package/skills/core/traffic-accident-assessor/LICENSE +29 -0
  166. package/skills/core/traffic-accident-assessor/SKILL.md +439 -0
  167. package/skills/core/traffic-accident-assessor/evals/evals.json +1 -0
  168. package/skills/core/traffic-accident-assessor/references/accident-types.md +369 -0
  169. package/skills/core/traffic-accident-assessor/references/liability-rules.md +287 -0
  170. package/skills/core/traffic-accident-assessor/references/traffic-laws.md +226 -0
  171. package/skills/core/traffic-accident-assessor/references//351/253/230/345/260/224/345/244/253/350/257/264/346/230/216/344/271/246.pdf +32576 -106
  172. package/skills/core/traffic-accident-assessor/scripts/generate_official_statement.py +588 -0
  173. package/skills/core/traffic-accident-assessor/scripts/generate_report.py +495 -0
  174. package/skills/core/traffic-accident-assessor/scripts/generate_statement.py +528 -0
  175. package/skills/core/traffic-accident-assessor.zip +0 -0
  176. package/skills/entropy/expert-arch-enforcer/SKILL.md +292 -0
  177. package/skills/entropy/expert-arch-enforcer/_meta.json +1 -0
  178. package/skills/entropy/expert-arch-enforcer/evals/default-evals.json +1 -0
  179. package/skills/entropy/expert-auto-refactor/SKILL.md +327 -0
  180. package/skills/entropy/expert-auto-refactor/_meta.json +1 -0
  181. package/skills/entropy/expert-auto-refactor/evals/default-evals.json +1 -0
  182. package/skills/entropy/expert-code-quality/SKILL.md +468 -0
  183. package/skills/entropy/expert-code-quality/_meta.json +1 -0
  184. package/skills/entropy/expert-code-quality/evals/default-evals.json +1 -0
  185. package/skills/entropy/expert-code-quality/evals/evals.json +109 -0
  186. package/skills/entropy/expert-code-quality/references/code-smells.md +605 -0
  187. package/skills/entropy/expert-code-quality/references/design-patterns.md +1111 -0
  188. package/skills/entropy/expert-code-quality/references/refactoring-catalog.md +1281 -0
  189. package/skills/entropy/expert-code-quality/references/solid-principles.md +524 -0
  190. package/skills/entropy/expert-entropy-auditor/SKILL.md +276 -0
  191. package/skills/entropy/expert-entropy-auditor/_meta.json +1 -0
  192. package/skills/entropy/expert-entropy-auditor/evals/default-evals.json +1 -0
  193. package/skills/expert/expert-activiti/SKILL.md +497 -0
  194. package/skills/expert/expert-activiti/_meta.json +1 -0
  195. package/skills/expert/expert-mysql/SKILL.md +832 -0
  196. package/skills/expert/expert-mysql/_meta.json +1 -0
  197. package/skills/expert/expert-performance/SKILL.md +379 -0
  198. package/skills/expert/expert-performance/_meta.json +1 -0
  199. package/skills/expert/expert-performance/evals/default-evals.json +1 -0
  200. package/skills/expert/expert-ruoyi/SKILL.md +472 -0
  201. package/skills/expert/expert-ruoyi/_meta.json +1 -0
  202. package/skills/expert/expert-security/SKILL.md +1341 -0
  203. package/skills/expert/expert-security/_meta.json +1 -0
  204. package/skills/expert/expert-security/evals/default-evals.json +1 -0
  205. package/skills/expert/software-architect/SKILL.md +350 -0
  206. package/skills/expert/software-architect/_meta.json +1 -0
  207. package/skills/expert/software-engineer/SKILL.md +437 -0
  208. package/skills/expert/software-engineer/_meta.json +1 -0
  209. package/skills/expert/software-engineer/architecture.md +130 -0
  210. package/skills/expert/software-engineer/patterns.md +151 -0
  211. package/skills/expert/software-engineer/testing.md +135 -0
  212. package/skills/expert/system-architect/SKILL.md +628 -0
  213. package/skills/expert/system-architect/_meta.json +1 -0
  214. package/skills/expert/system-architect/assets/templates/ARCHITECTURE.md +25 -0
  215. package/skills/expert/system-architect/assets/templates/README.md +44 -0
  216. package/skills/expert/system-architect/references/js-ts-standards.md +18 -0
  217. package/skills/expert/system-architect/references/python-standards.md +19 -0
  218. package/skills/expert/system-architect/references/scaffolding.md +61 -0
  219. package/skills/expert/system-architect/references/security-checklist.md +21 -0
  220. package/skills/openspec/openspec-apply-change/SKILL.md +156 -0
  221. package/skills/openspec/openspec-apply-change/_meta.json +1 -0
  222. package/skills/openspec/openspec-archive-change/SKILL.md +114 -0
  223. package/skills/openspec/openspec-archive-change/_meta.json +1 -0
  224. package/skills/openspec/openspec-bulk-archive-change/SKILL.md +246 -0
  225. package/skills/openspec/openspec-bulk-archive-change/_meta.json +1 -0
  226. package/skills/openspec/openspec-continue-change/SKILL.md +118 -0
  227. package/skills/openspec/openspec-continue-change/_meta.json +1 -0
  228. package/skills/openspec/openspec-explore/SKILL.md +288 -0
  229. package/skills/openspec/openspec-explore/_meta.json +1 -0
  230. package/skills/openspec/openspec-ff-change/SKILL.md +101 -0
  231. package/skills/openspec/openspec-ff-change/_meta.json +1 -0
  232. package/skills/openspec/openspec-new-change/SKILL.md +74 -0
  233. package/skills/openspec/openspec-new-change/_meta.json +1 -0
  234. package/skills/openspec/openspec-onboard/SKILL.md +554 -0
  235. package/skills/openspec/openspec-onboard/_meta.json +1 -0
  236. package/skills/openspec/openspec-sync-specs/SKILL.md +138 -0
  237. package/skills/openspec/openspec-sync-specs/_meta.json +1 -0
  238. package/skills/openspec/openspec-verify-change/SKILL.md +168 -0
  239. package/skills/openspec/openspec-verify-change/_meta.json +1 -0
  240. package/skills/pr/pdd-multi-review/SKILL.md +534 -0
  241. package/skills/pr/pdd-multi-review/_meta.json +1 -0
  242. package/skills/pr/pdd-pr-batch/SKILL.md +303 -0
  243. package/skills/pr/pdd-pr-batch/_meta.json +1 -0
  244. package/skills/pr/pdd-pr-create/SKILL.md +344 -0
  245. package/skills/pr/pdd-pr-create/_meta.json +1 -0
  246. package/skills/pr/pdd-pr-merge/SKILL.md +286 -0
  247. package/skills/pr/pdd-pr-merge/_meta.json +1 -0
  248. package/skills/pr/pdd-pr-review/SKILL.md +217 -0
  249. package/skills/pr/pdd-pr-review/_meta.json +1 -0
  250. package/skills/pr/pdd-task-manager/SKILL.md +636 -0
  251. package/skills/pr/pdd-task-manager/_meta.json +1 -0
  252. package/skills/pr/pdd-template-engine/SKILL.md +306 -0
  253. package/skills/pr/pdd-template-engine/_meta.json +1 -0
  254. package/templates/behavior-shaping/iron-law-template.md +87 -0
  255. package/templates/behavior-shaping/rationalization-template.md +62 -0
  256. package/templates/behavior-shaping/red-flags-template.md +70 -0
  257. package/templates/bilingual-template.md +139 -0
  258. package/templates/config/default.yaml +47 -0
  259. package/templates/project/default/README.md +31 -0
  260. package/templates/project/frontend/README.md +46 -0
  261. package/templates/project/java/README.md +48 -0
@@ -0,0 +1,1025 @@
1
+ // lib/iteration/auto-fixer.js - 自动修复模块
2
+ // 根据审查结果生成修复建议和代码patch
3
+ // 安全原则: 只生成建议和patch预览,不直接修改源码
4
+
5
+ import chalk from 'chalk';
6
+ import { Severity } from './auto-reviewer.js';
7
+
8
+ /**
9
+ * 修复类型枚举
10
+ */
11
+ export const FixType = {
12
+ REPLACE: 'replace', // 替换代码片段
13
+ INSERT_BEFORE: 'insert_before', // 在指定位置前插入
14
+ INSERT_AFTER: 'insert_after', // 在指定位置后插入
15
+ DELETE: 'delete', // 删除代码行
16
+ REFACTOR: 'refactor', // 重构建议(需要人工确认)
17
+ CONFIG_CHANGE: 'config_change' // 配置修改建议
18
+ };
19
+
20
+ /**
21
+ * 修复置信度等级
22
+ */
23
+ export const ConfidenceLevel = {
24
+ HIGH: 'high', // 高置信度,可安全自动应用
25
+ MEDIUM: 'medium', // 中等置信度,需要人工审核
26
+ LOW: 'low' // 低置信度,仅作参考
27
+ };
28
+
29
+ /**
30
+ * 单条修复建议
31
+ */
32
+ class FixSuggestion {
33
+ /**
34
+ * @param {Object} params
35
+ * @param {string} params.id - 唯一标识
36
+ * @param {string} params.type - 修复类型 (FixType)
37
+ * @param {string} params.description - 修复描述
38
+ * @param {string} params.issueId - 关联的问题ID
39
+ * @param {number} [params.line] - 目标行号
40
+ * @param {string} [params.originalCode] - 原始代码片段
41
+ * @param {string} [params.fixedCode] - 修复后的代码
42
+ * @param {string} params.confidence - 置信度 (ConfidenceLevel)
43
+ * @param {boolean} params.autoApplicable - 是否可自动应用
44
+ * @param {string} [params.reasoning] - 修复理由
45
+ * @param {string} [params.category] - 修复类别
46
+ * @param {Array<string>} [params.risks] - 潜在风险列表
47
+ */
48
+ constructor(params) {
49
+ this.id = params.id || `fix-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
50
+ this.type = params.type;
51
+ this.description = params.description;
52
+ this.issueId = params.issueId;
53
+ this.line = params.line || null;
54
+ this.originalCode = params.originalCode || null;
55
+ this.fixedCode = params.fixedCode || null;
56
+ this.confidence = params.confidence || ConfidenceLevel.MEDIUM;
57
+ this.autoApplicable = params.autoApplicable !== undefined ? params.autoApplicable : false;
58
+ this.reasoning = params.reasoning || '';
59
+ this.category = params.category || 'general';
60
+ this.risks = params.risks || [];
61
+ this.timestamp = Date.now();
62
+ }
63
+
64
+ toJSON() {
65
+ return {
66
+ id: this.id,
67
+ type: this.type,
68
+ description: this.description,
69
+ issueId: this.issueId,
70
+ line: this.line,
71
+ originalCode: this.originalCode,
72
+ fixedCode: this.fixedCode,
73
+ confidence: this.confidence,
74
+ autoApplicable: this.autoApplicable,
75
+ reasoning: this.reasoning,
76
+ category: this.category,
77
+ risks: this.risks
78
+ };
79
+ }
80
+
81
+ /**
82
+ * 应用此修复到源代码
83
+ * @param {string} source - 源代码
84
+ * @returns {string} 修复后的代码
85
+ */
86
+ apply(source) {
87
+ if (!this.autoApplicable) {
88
+ throw new Error(`修复 ${this.id} 标记为不可自动应用,请手动处理`);
89
+ }
90
+
91
+ const lines = source.split('\n');
92
+
93
+ switch (this.type) {
94
+ case FixType.REPLACE:
95
+ if (this.line && this.fixedCode !== null) {
96
+ lines[this.line - 1] = this.fixedCode;
97
+ }
98
+ break;
99
+
100
+ case FixType.INSERT_BEFORE:
101
+ if (this.line && this.fixedCode !== null) {
102
+ lines.splice(this.line - 1, 0, this.fixedCode);
103
+ }
104
+ break;
105
+
106
+ case FixType.INSERT_AFTER:
107
+ if (this.line && this.fixedCode !== null) {
108
+ lines.splice(this.line, 0, this.fixedCode);
109
+ }
110
+ break;
111
+
112
+ case FixType.DELETE:
113
+ if (this.line) {
114
+ lines.splice(this.line - 1, 1);
115
+ }
116
+ break;
117
+
118
+ default:
119
+ console.warn(chalk.yellow(`不支持的修复类型: ${this.type}`));
120
+ break;
121
+ }
122
+
123
+ return lines.join('\n');
124
+ }
125
+ }
126
+
127
+ /**
128
+ * 修复结果
129
+ */
130
+ class FixResult {
131
+ constructor() {
132
+ this.suggestions = [];
133
+ this.patchedCode = null; // 应用了高置信度修复后的代码预览
134
+ this.appliedCount = 0;
135
+ this.skippedCount = 0;
136
+ this.manualReviewRequired = [];
137
+ this.fixTime = 0;
138
+ }
139
+
140
+ addSuggestion(suggestion) {
141
+ this.suggestions.push(suggestion);
142
+
143
+ if (suggestion.autoApplicable && suggestion.confidence === ConfidenceLevel.HIGH) {
144
+ this.appliedCount++;
145
+ } else {
146
+ this.skippedCount++;
147
+ this.manualReviewRequired.push(suggestion);
148
+ }
149
+ }
150
+
151
+ getSummary() {
152
+ return {
153
+ totalSuggestions: this.suggestions.length,
154
+ autoApplied: this.appliedCount,
155
+ manualReview: this.skippedCount,
156
+ byConfidence: {
157
+ high: this.suggestions.filter(s => s.confidence === ConfidenceLevel.HIGH).length,
158
+ medium: this.suggestions.filter(s => s.confidence === ConfidenceLevel.MEDIUM).length,
159
+ low: this.suggestions.filter(s => s.confidence === ConfidenceLevel.LOW).length
160
+ },
161
+ byCategory: this._groupByCategory()
162
+ };
163
+ }
164
+
165
+ _groupByCategory() {
166
+ const groups = {};
167
+ for (const s of this.suggestions) {
168
+ groups[s.category] = (groups[s.category] || 0) + 1;
169
+ }
170
+ return groups;
171
+ }
172
+ }
173
+
174
+ /**
175
+ * AutoFixer - 自动修复建议生成器
176
+ *
177
+ * 核心职责:
178
+ * 1. 分析审查结果中的问题
179
+ * 2. 为每个问题生成修复策略
180
+ * 3. 评估修复的可行性和风险
181
+ * 4. 生成代码patch预览(不直接修改源码)
182
+ *
183
+ * 安全原则:
184
+ * - 所有修复都是"建议"性质,默认不自动应用
185
+ * - 只有高置信度的简单修复才标记为可自动应用
186
+ * - 复杂重构类修复始终需要人工确认
187
+ * - 提供完整的before/after对比供审核
188
+ *
189
+ * 使用场景:
190
+ * - 与IterationController配合完成多轮迭代
191
+ * - 作为独立工具为开发者提供修复指导
192
+ * - 集成到CI/CD流水线作为代码质量门禁
193
+ */
194
+ export class AutoFixer {
195
+ /**
196
+ * @param {Object} config - 配置选项
197
+ * @param {boolean} config.autoApplyHighConfidence - 是否自动应用高置信度修复 (默认false)
198
+ * @param {boolean} config.generatePatchedCode - 是否生成patch预览代码 (默认true)
199
+ * @param {number} config.maxFixesPerIssue - 每个问题最大修复建议数 (默认3)
200
+ * @param {Array<string>} config.disabledCategories - 禁用的修复类别
201
+ * @param {Object} config.fixStrategies - 自定义修复策略映射
202
+ */
203
+ constructor(config = {}) {
204
+ this.config = {
205
+ autoApplyHighConfidence: config.autoApplyHighConfidence || false,
206
+ generatePatchedCode: config.generatePatchedCode !== false,
207
+ maxFixesPerIssue: config.maxFixesPerIssue || 3,
208
+ disabledCategories: new Set(config.disabledCategories || []),
209
+ fixStrategies: config.fixStrategies || {}
210
+ };
211
+
212
+ // 注册内置修复策略
213
+ this.strategies = this._initStrategies();
214
+
215
+ // 统计信息
216
+ this.stats = {
217
+ totalFixSessions: 0,
218
+ totalSuggestionsGenerated: 0,
219
+ avgSuggestionsPerIssue: 0
220
+ };
221
+ }
222
+
223
+ /**
224
+ * 根据审查结果生成修复建议
225
+ *
226
+ * @param {string} code - 待修复的原始代码
227
+ * @param {Array<Issue>} issues - 审查发现的问题列表
228
+ * @param {Object} spec - 开发规格 (可选)
229
+ * @returns {FixResult} 修复结果
230
+ */
231
+ generateFixes(code, issues, spec = {}) {
232
+ const startTime = Date.now();
233
+ const result = new FixResult();
234
+
235
+ console.log(chalk.blue(' 🔧 AutoFixer 开始分析修复方案...'));
236
+
237
+ try {
238
+ // 为每个问题生成修复建议
239
+ for (const issue of issues) {
240
+ const fixes = this._generateFixForIssue(code, issue, spec);
241
+
242
+ for (const fix of fixes.slice(0, this.config.maxFixesPerIssue)) {
243
+ result.addSuggestion(fix);
244
+ }
245
+ }
246
+
247
+ // 如果启用,生成应用了高置信度修复后的代码预览
248
+ if (this.config.generatePatchedCode && result.appliedCount > 0) {
249
+ result.patchedCode = this._applySafeFixes(code, result.suggestions);
250
+ }
251
+
252
+ // 更新统计
253
+ result.fixTime = Date.now() - startTime;
254
+ this._updateStats(result);
255
+
256
+ const summary = result.getSummary();
257
+ console.log(` ✅ 修复分析完成: ${summary.totalSuggestions} 条建议 (${summary.autoApplied} 可自动应用, ${summary.manualReview} 需人工审核)`);
258
+
259
+ return result;
260
+
261
+ } catch (error) {
262
+ console.error(chalk.red(` ❌ 修复分析出错: ${error.message}`));
263
+ return result;
264
+ }
265
+ }
266
+
267
+ /**
268
+ * 为单个问题生成修复建议
269
+ * @private
270
+ */
271
+ _generateFixForIssue(code, issue, spec) {
272
+ const fixes = [];
273
+
274
+ // 根据问题类别选择修复策略
275
+ const strategyKey = this._getStrategyKey(issue);
276
+ const strategy = this.strategies[strategyKey];
277
+
278
+ if (strategy && typeof strategy.handler === 'function') {
279
+ try {
280
+ const strategyFixes = strategy.handler(code, issue, spec);
281
+ if (Array.isArray(strategyFixes)) {
282
+ fixes.push(...strategyFixes);
283
+ }
284
+ } catch (err) {
285
+ console.error(chalk.yellow(` ⚠️ 策略[${strategyKey}]执行失败: ${err.message}`));
286
+ }
287
+ }
288
+
289
+ // 如果没有找到专用策略,使用通用修复建议
290
+ if (fixes.length === 0) {
291
+ fixes.push(this._generateGenericFix(code, issue));
292
+ }
293
+
294
+ return fixes;
295
+ }
296
+
297
+ /**
298
+ * 获取策略键名
299
+ * @private
300
+ */
301
+ _getStrategyKey(issue) {
302
+ // 优先使用规则名称
303
+ if (issue.rule && this.strategies[issue.rule]) {
304
+ return issue.rule;
305
+ }
306
+ // 其次使用类别
307
+ if (this.strategies[issue.category]) {
308
+ return issue.category;
309
+ }
310
+ // 再次尝试类别+严重级别组合
311
+ const combined = `${issue.category}_${issue.severity}`;
312
+ if (this.strategies[combined]) {
313
+ return combined;
314
+ }
315
+ return 'default';
316
+ }
317
+
318
+ // ==================== 内置修复策略 ====================
319
+
320
+ /**
321
+ * SQL注入修复策略
322
+ */
323
+ _fixSqlInjection(code, issue) {
324
+ const lineNum = issue.line;
325
+ const originalLine = this._getLine(code, lineNum);
326
+
327
+ if (!originalLine) return [];
328
+
329
+ const fixes = [];
330
+
331
+ // 生成参数化查询示例
332
+ const fixedExample = originalLine
333
+ .replace(/\$\{.*?\}/g, '?')
334
+ .replace(/["']\s*\+\s*[^"']+/, '?');
335
+
336
+ fixes.push(new FixSuggestion({
337
+ type: FixType.REPLACE,
338
+ description: `将SQL字符串拼接替换为参数化查询`,
339
+ issueId: issue.id,
340
+ line: lineNum,
341
+ originalCode: originalLine.trim(),
342
+ fixedCode: fixedExample.trim(),
343
+ confidence: ConfidenceLevel.HIGH,
344
+ autoApplicable: true,
345
+ category: 'security',
346
+ reasoning: '使用参数化查询(prepared statement)可以有效防止SQL注入攻击',
347
+ risks: ['需要确保参数绑定顺序正确']
348
+ }));
349
+
350
+ // 备选方案:使用ORM
351
+ fixes.push(new FixSuggestion({
352
+ type: FixType.REFACTOR,
353
+ description: `考虑使用ORM/Query Builder替代原生SQL`,
354
+ issueId: issue.id,
355
+ line: lineNum,
356
+ confidence: ConfidenceLevel.MEDIUM,
357
+ autoApplicable: false,
358
+ category: 'security',
359
+ reasoning: '如Sequelize、TypeORM、Prisma等提供了安全的查询接口',
360
+ risks: ['可能需要较大的架构调整', '需要学习ORM的使用方式']
361
+ }));
362
+
363
+ return fixes;
364
+ }
365
+
366
+ /**
367
+ * XSS修复策略
368
+ */
369
+ _fixXss(code, issue) {
370
+ const lineNum = issue.line;
371
+ const originalLine = this._getLine(code, lineNum);
372
+
373
+ if (!originalLine) return [];
374
+
375
+ const fixes = [];
376
+
377
+ let fixedCode = originalLine;
378
+
379
+ // innerHTML -> textContent
380
+ if (/innerHTML\s*=/.test(originalLine)) {
381
+ fixedCode = originalLine.replace(/innerHTML/g, 'textContent');
382
+ }
383
+
384
+ // document.write -> 安全DOM操作
385
+ if (/document\.write\s*\(/.test(originalLine)) {
386
+ fixedCode = '// 已移除document.write,请使用DOM API安全地插入内容';
387
+ }
388
+
389
+ if (fixedCode !== originalLine) {
390
+ fixes.push(new FixSuggestion({
391
+ type: FixType.REPLACE,
392
+ description: `替换不安全的DOM操作为安全替代方案`,
393
+ issueId: issue.id,
394
+ line: lineNum,
395
+ originalCode: originalLine.trim(),
396
+ fixedCode: fixedCode.trim(),
397
+ confidence: ConfidenceLevel.HIGH,
398
+ autoApplicable: true,
399
+ category: 'security',
400
+ reasoning: 'textContent不会解析HTML标签,可有效防止XSS攻击',
401
+ risks: ['innerHTML设置的样式属性会丢失']
402
+ }));
403
+ }
404
+
405
+ // 建议使用DOMPurify
406
+ fixes.push(new FixSuggestion({
407
+ type: FixType.REFACTOR,
408
+ description: `如需保留HTML格式,使用DOMPurify进行消毒`,
409
+ issueId: issue.id,
410
+ line: lineNum,
411
+ confidence: ConfidenceLevel.MEDIUM,
412
+ autoApplicable: false,
413
+ category: 'security',
414
+ reasoning: 'DOMPurify可以清理HTML中的恶意脚本同时保留合法内容',
415
+ risks: ['需要引入额外依赖', '需确保DOMPurify版本保持更新']
416
+ }));
417
+
418
+ return fixes;
419
+ }
420
+
421
+ /**
422
+ * 敏感信息硬编码修复策略
423
+ */
424
+ _fixHardcodedSecret(code, issue) {
425
+ const lineNum = issue.line;
426
+ const originalLine = this._getLine(code, lineNum);
427
+
428
+ if (!originalLine) return [];
429
+
430
+ const fixes = [];
431
+
432
+ // 提取变量名的正则
433
+ const varMatch = originalLine.match(/(?:password|passwd|pwd|api[_-]?key|apikey|secret|token)\s*[:=]\s*['"]([^'"]+)['"]/i);
434
+ const varName = varMatch ? varMatch[1].substring(0, 3) + '***' : '***';
435
+
436
+ // 生成环境变量引用示例
437
+ const envVarName = (varMatch?.[0]?.match(/[a-zA-Z_]+/)?.[0] || 'SECRET').toUpperCase();
438
+ const fixedCode = originalLine.replace(
439
+ /[:=]\s*['"][^'"]+['"]/,
440
+ `: process.env.${envVarName}`
441
+ );
442
+
443
+ fixes.push(new FixSuggestion({
444
+ type: FixType.REPLACE,
445
+ description: `将硬编码的${varName}替换为环境变量引用`,
446
+ issueId: issue.id,
447
+ line: lineNum,
448
+ originalCode: originalLine.trim(),
449
+ fixedCode: fixedCode.trim(),
450
+ confidence: ConfidenceLevel.HIGH,
451
+ autoApplicable: true,
452
+ category: 'security',
453
+ reasoning: '敏感信息不应硬编码在源码中,应通过环境变量或密钥管理服务注入',
454
+ risks: ['需要在部署环境配置相应的环境变量', '确保.env文件不被提交到版本控制']
455
+ }));
456
+
457
+ // 建议使用配置管理服务
458
+ fixes.push(new FixSuggestion({
459
+ type: FixType.REFACTOR,
460
+ description: `对于生产环境,考虑使用AWS Secrets Manager / HashiCorp Vault等`,
461
+ issueId: issue.id,
462
+ line: lineNum,
463
+ confidence: ConfidenceLevel.LOW,
464
+ autoApplicable: false,
465
+ category: 'security',
466
+ reasoning: '专业的密钥管理服务提供加密存储、审计日志、自动轮换等功能',
467
+ costs: ['增加基础设施复杂度', '可能有额外费用']
468
+ }));
469
+
470
+ return fixes;
471
+ }
472
+
473
+ /**
474
+ * eval()修复策略
475
+ */
476
+ _fixEval(code, issue) {
477
+ const lineNum = issue.line;
478
+ const originalLine = this._getLine(code, lineNum);
479
+
480
+ if (!originalLine) return [];
481
+
482
+ const fixes = [];
483
+
484
+ // 尝试判断eval用途并给出具体建议
485
+ const evalContent = originalLine.match(/eval\s*\(\s*(.+?)\s*\)/)?.[1];
486
+
487
+ let suggestion = '避免使用eval()';
488
+ let fixedCode = '// 请使用更安全的替代方案';
489
+
490
+ if (evalContent) {
491
+ if (/JSON/.test(evalContent)) {
492
+ fixedCode = originalLine.replace(/eval\s*\(\s*/, 'JSON.parse(');
493
+ suggestion = '如果解析JSON,使用JSON.parse()替代eval()';
494
+ } else if (/\[\w+\]/.test(evalContent)) {
495
+ fixedCode = originalLine.replace(/eval\s*\(\s*/, '');
496
+ fixedCode = fixedCode.replace(/\)\s*$/, '');
497
+ suggestion = '如果是动态属性访问,使用obj[prop]语法替代';
498
+ }
499
+ }
500
+
501
+ fixes.push(new FixSuggestion({
502
+ type: FixType.REPLACE,
503
+ description: suggestion,
504
+ issueId: issue.id,
505
+ line: lineNum,
506
+ originalCode: originalLine.trim(),
507
+ fixedCode: fixedCode.trim(),
508
+ confidence: ConfidenceLevel.HIGH,
509
+ autoApplicable: true,
510
+ category: 'security',
511
+ reasoning: 'eval()会执行任意JavaScript代码,存在严重的安全风险和性能问题',
512
+ risks: ['需要验证替换后功能是否正常']
513
+ }));
514
+
515
+ return fixes;
516
+ }
517
+
518
+ /**
519
+ * 性能问题修复策略
520
+ */
521
+ _fixPerformanceIssue(code, issue) {
522
+ const lineNum = issue.line;
523
+ const originalLine = this._getLine(code, lineNum);
524
+
525
+ if (!originalLine) return [];
526
+
527
+ const fixes = [];
528
+
529
+ switch (issue.rule) {
530
+ case 'no-sync-in-loop':
531
+ fixes.push(new FixSuggestion({
532
+ type: FixType.REFACTOR,
533
+ description: `将同步I/O操作改为异步版本或移出循环`,
534
+ issueId: issue.id,
535
+ line: lineNum,
536
+ originalCode: originalLine.trim(),
537
+ fixedCode: originalLine
538
+ .replace(/readFileSync/g, 'readFile')
539
+ .replace(/writeFileSync/g, 'writeFile')
540
+ .replace(/execSync/g, 'exec')
541
+ .trim(),
542
+ confidence: ConfidenceLevel.MEDIUM,
543
+ autoApplicable: false,
544
+ category: 'performance',
545
+ reasoning: '同步I/O会阻塞事件循环,在循环中会导致严重的性能瓶颈',
546
+ risks: ['需要将函数改为async/await模式', '需要正确处理错误回调']
547
+ }));
548
+ break;
549
+
550
+ case 'n-plus-one-query':
551
+ fixes.push(new FixSuggestion({
552
+ type: FixType.REFACTOR,
553
+ description: `使用批量查询(eager loading)或JOIN替代循环中的单独查询`,
554
+ issueId: issue.id,
555
+ line: lineNum,
556
+ confidence: ConfidenceLevel.MEDIUM,
557
+ autoApplicable: false,
558
+ category: 'performance',
559
+ reasoning: 'N+1问题是数据库性能问题的常见原因,可通过一次查询获取所有关联数据来解决',
560
+ risks: ['可能增加单次查询的复杂度', '需要注意内存占用']
561
+ }));
562
+ break;
563
+
564
+ case 'event-listener-leak':
565
+ fixes.push(new FixSuggestion({
566
+ type: FixType.INSERT_BEFORE,
567
+ description: `在组件卸载时添加事件监听器清理逻辑`,
568
+ issueId: issue.id,
569
+ line: lineNum,
570
+ fixedCode: `
571
+ // 清理事件监听器
572
+ element.removeEventListener('eventType', handler);`,
573
+ confidence: ConfidenceLevel.MEDIUM,
574
+ autoApplicable: false,
575
+ category: 'performance',
576
+ reasoning: '未清理的事件监听器会导致内存泄漏,尤其在SPA应用中',
577
+ risks: ['需要确定正确的清理时机', '需要保存handler的引用']
578
+ }));
579
+ break;
580
+
581
+ default:
582
+ fixes.push(this._generateGenericFix(code, issue));
583
+ }
584
+
585
+ return fixes;
586
+ }
587
+
588
+ /**
589
+ * 代码风格修复策略
590
+ */
591
+ _fixStyleIssue(code, issue) {
592
+ const lineNum = issue.line;
593
+ const originalLine = this._getLine(code, lineNum);
594
+
595
+ if (!originalLine) return [];
596
+
597
+ const fixes = [];
598
+
599
+ switch (issue.rule) {
600
+ case 'max-line-length':
601
+ // 自动拆分长行
602
+ const targetLength = 100;
603
+ if (originalLine.length > targetLength) {
604
+ // 简单拆分策略:在逗号、运算符处断开
605
+ let fixedCode = originalLine;
606
+ const splitPoints = [', ', ' + ', ' && ', ' || ', ' ? '];
607
+ for (const point of splitPoints) {
608
+ const idx = fixedCode.indexOf(point, targetLength);
609
+ if (idx > 0) {
610
+ fixedCode = fixedCode.substring(0, idx + point.length) +
611
+ '\n' + ' '.repeat(fixedCode.search(/\S/) + 2) +
612
+ fixedCode.substring(idx + point.length);
613
+ break;
614
+ }
615
+ }
616
+
617
+ if (fixedCode !== originalLine) {
618
+ fixes.push(new FixSuggestion({
619
+ type: FixType.REPLACE,
620
+ description: `拆分过长行为多行(目标长度:${targetLength}字符)`,
621
+ issueId: issue.id,
622
+ line: lineNum,
623
+ originalCode: originalLine.trim(),
624
+ fixedCode: fixedCode.trim(),
625
+ confidence: ConfidenceLevel.HIGH,
626
+ autoApplicable: true,
627
+ category: 'style'
628
+ }));
629
+ }
630
+ }
631
+ break;
632
+
633
+ case 'no-console-log':
634
+ fixes.push(new FixSuggestion({
635
+ type: FixType.REPLACE,
636
+ description: `将console.log替换为条件性日志输出`,
637
+ issueId: issue.id,
638
+ line: lineNum,
639
+ originalCode: originalLine.trim(),
640
+ fixedCode: originalLine.replace(
641
+ /console\.(log|debug|info)\s*\(/g,
642
+ 'logger.debug('
643
+ ).trim(),
644
+ confidence: ConfidenceLevel.HIGH,
645
+ autoApplicable: true,
646
+ category: 'style',
647
+ reasoning: '生产环境应使用结构化日志框架而非console.log',
648
+ risks: ['需要引入logger依赖', '需要配置日志级别']
649
+ }));
650
+ break;
651
+
652
+ case 'no-magic-numbers':
653
+ const magicMatch = issue.codeSnippet?.match(/\d+/);
654
+ if (magicMatch) {
655
+ const num = magicMatch[0];
656
+ const constName = this._suggestConstantName(num, issue);
657
+ fixes.push(new FixSuggestion({
658
+ type: FixType.INSERT_BEFORE,
659
+ description: `将魔法数字 ${num} 提取为命名常量 ${constName}`,
660
+ issueId: issue.id,
661
+ line: lineNum,
662
+ fixedCode: `const ${constName} = ${num}; // TODO: 添加有意义的常量描述`,
663
+ confidence: ConfidenceLevel.MEDIUM,
664
+ autoApplicable: false,
665
+ category: 'style'
666
+ }));
667
+ }
668
+ break;
669
+
670
+ default:
671
+ fixes.push(this._generateGenericFix(code, issue));
672
+ }
673
+
674
+ return fixes;
675
+ }
676
+
677
+ /**
678
+ * 复杂度/逻辑问题修复策略
679
+ */
680
+ _fixLogicIssue(code, issue) {
681
+ const fixes = [];
682
+
683
+ switch (issue.rule) {
684
+ case 'max-nesting-depth':
685
+ fixes.push(new FixSuggestion({
686
+ type: FixType.REFACTOR,
687
+ description: `使用早返回(early return)模式减少嵌套层级`,
688
+ issueId: issue.id,
689
+ line: issue.line,
690
+ confidence: ConfidenceLevel.MEDIUM,
691
+ autoApplicable: false,
692
+ category: 'logic',
693
+ reasoning: '早返回可以让代码逻辑更清晰,减少认知负担',
694
+ example: `
695
+ // Before:
696
+ function process(data) {
697
+ if (data) {
698
+ if (data.items) {
699
+ data.items.forEach(item => { ... });
700
+ }
701
+ }
702
+ }
703
+
704
+ // After:
705
+ function process(data) {
706
+ if (!data) return;
707
+ if (!data.items) return;
708
+ data.items.forEach(item => { ... });
709
+ }`
710
+ }));
711
+ break;
712
+
713
+ case 'max-function-length':
714
+ fixes.push(new FixSuggestion({
715
+ type: FixType.REFACTOR,
716
+ description: `将长函数按职责拆分为多个小函数`,
717
+ issueId: issue.id,
718
+ line: issue.line,
719
+ confidence: ConfidenceLevel.MEDIUM,
720
+ autoApplicable: false,
721
+ category: 'logic',
722
+ reasoning: '单一职责原则要求每个函数只做一件事',
723
+ risks: ['需要识别函数内的不同职责边界', '可能需要传递更多参数']
724
+ }));
725
+ break;
726
+
727
+ case 'no-duplicated-code':
728
+ fixes.push(new FixSuggestion({
729
+ type: FixType.REFACTOR,
730
+ description: `提取重复代码为共享函数或工具方法`,
731
+ issueId: issue.id,
732
+ line: issue.line,
733
+ confidence: ConfidenceLevel.MEDIUM,
734
+ autoApplicable: false,
735
+ category: 'logic',
736
+ reasoning: 'DRY(Don't Repeat Yourself)原则可以减少维护成本和bug风险'
737
+ }));
738
+ break;
739
+
740
+ default:
741
+ fixes.push(this._generateGenericFix(code, issue));
742
+ }
743
+
744
+ return fixes;
745
+ }
746
+
747
+ /**
748
+ * 规格违反修复策略
749
+ */
750
+ _fixSpecViolation(code, issue) {
751
+ const fixes = [];
752
+
753
+ switch (issue.rule) {
754
+ case 'required-function-missing':
755
+ const funcName = issue.message.match(/:\s*(\w+)/)?.[1] || 'requiredFunction';
756
+ fixes.push(new FixSuggestion({
757
+ type: FixType.INSERT_BEFORE,
758
+ description: `添加缺失的必需函数: ${funcName}`,
759
+ issueId: issue.id,
760
+ line: issue.line || 1,
761
+ fixedCode: `
762
+ /**
763
+ * ${funcName} - 按规格要求实现
764
+ * TODO: 完善函数实现
765
+ */
766
+ export function ${funcName}() {
767
+ // Implementation needed
768
+ throw new Error('Not implemented');
769
+ }`,
770
+ confidence: ConfidenceLevel.HIGH,
771
+ autoApplicable: true,
772
+ category: 'spec_compliance'
773
+ }));
774
+ break;
775
+
776
+ case 'forbidden-api-used':
777
+ fixes.push(new FixSuggestion({
778
+ type: FixType.REFACTOR,
779
+ description: `替换禁止使用的API为允许的替代方案`,
780
+ issueId: issue.id,
781
+ line: issue.line,
782
+ confidence: ConfidenceLevel.MEDIUM,
783
+ autoApplicable: false,
784
+ category: 'spec_compliance'
785
+ }));
786
+ break;
787
+
788
+ default:
789
+ fixes.push(this._generateGenericFix(code, issue));
790
+ }
791
+
792
+ return fixes;
793
+ }
794
+
795
+ // ==================== 通用修复生成 ====================
796
+
797
+ /**
798
+ * 通用修复建议(当没有专用策略时)
799
+ * @private
800
+ */
801
+ _generateGenericFix(code, issue) {
802
+ const lineNum = issue.line;
803
+ const originalLine = lineNum ? this._getLine(code, lineNum) : null;
804
+
805
+ return new FixSuggestion({
806
+ type: issue.severity === Severity.CRITICAL ? FixType.REPLACE : FixType.REFACTOR,
807
+ description: issue.suggestion || `修复: ${issue.message}`,
808
+ issueId: issue.id,
809
+ line: lineNum,
810
+ originalCode: originalLine?.trim() || null,
811
+ fixedCode: issue.suggestion ? `// TODO: ${issue.suggestion}` : null,
812
+ confidence: issue.severity === Severity.CRITICAL ? ConfidenceLevel.HIGH :
813
+ issue.severity === Severity.MAJOR ? ConfidenceLevel.MEDIUM :
814
+ ConfidenceLevel.LOW,
815
+ autoApplicable: issue.severity === Severity.CRITICAL,
816
+ category: issue.category,
817
+ reasoning: `基于问题 "${issue.message}" 生成的修复建议`
818
+ });
819
+ }
820
+
821
+ // ==================== 工具方法 ====================
822
+
823
+ /**
824
+ * 获取指定行的内容
825
+ * @private
826
+ */
827
+ _getLine(code, lineNumber) {
828
+ if (!lineNumber || lineNumber < 1) return null;
829
+ const lines = code.split('\n');
830
+ return lines[lineNumber - 1] || null;
831
+ }
832
+
833
+ /**
834
+ * 应用所有安全的高置信度修复,生成patch预览
835
+ * @private
836
+ */
837
+ _applySafeFixes(code, suggestions) {
838
+ let patchedCode = code;
839
+ const appliedFixes = suggestions.filter(
840
+ s => s.autoApplicable && s.confidence === ConfidenceLevel.HIGH
841
+ );
842
+
843
+ // 按行号倒序应用,避免行号偏移问题
844
+ const sortedFixes = [...appliedFixes]
845
+ .filter(f => f.line)
846
+ .sort((a, b) => b.line - a.line);
847
+
848
+ for (const fix of sortedFixes) {
849
+ try {
850
+ patchedCode = fix.apply(patchedCode);
851
+ } catch (e) {
852
+ console.warn(chalk.yellow(` ⚠️ 无法应用修复 ${fix.id}: ${e.message}`));
853
+ }
854
+ }
855
+
856
+ return patchedCode;
857
+ }
858
+
859
+ /**
860
+ * 为魔法数字建议常量名
861
+ * @private
862
+ */
863
+ _suggestConstantName(number, context) {
864
+ const num = parseInt(number);
865
+
866
+ // 常见数字的模式匹配
867
+ const commonPatterns = {
868
+ '60': 'TIMEOUT_SECONDS',
869
+ '100': 'MAX_ITEMS',
870
+ '1000': 'PAGE_SIZE',
871
+ '1024': 'BUFFER_SIZE',
872
+ '2048': 'MAX_LENGTH',
873
+ '3600': 'HOUR_IN_SECONDS',
874
+ '86400': 'DAY_IN_SECONDS',
875
+ '30': 'DEFAULT_PAGE_SIZE',
876
+ '10': 'DEFAULT_LIMIT',
877
+ '5': 'MAX_RETRIES',
878
+ '3': 'DEFAULT_RETRIES',
879
+ '0': 'ZERO',
880
+ '1': 'ONE',
881
+ '-1': 'ERROR_CODE'
882
+ };
883
+
884
+ if (commonPatterns[number]) {
885
+ return commonPatterns[number];
886
+ }
887
+
888
+ // 根据上下文推测
889
+ if (context?.category === 'performance') {
890
+ return `PERFORMANCE_THRESHOLD_${number}`;
891
+ }
892
+ if (context?.rule?.includes('timeout')) {
893
+ return `TIMEOUT_MS_${number}`;
894
+ }
895
+ if (context?.rule?.includes('limit')) {
896
+ return `MAX_LIMIT_${number}`;
897
+ }
898
+
899
+ return `MAGIC_NUMBER_${number}`;
900
+ }
901
+
902
+ /**
903
+ * 初始化内置修复策略
904
+ * @private
905
+ */
906
+ _initStrategies() {
907
+ return {
908
+ // 安全相关
909
+ 'sql-injection-template': { handler: (c, i, s) => this._fixSqlInjection(c, i), priority: 1 },
910
+ 'sql-injection-concat': { handler: (c, i, s) => this._fixSqlInjection(c, i), priority: 1 },
911
+ 'sql-injection-array': { handler: (c, i, s) => this._fixSqlInjection(c, i), priority: 1 },
912
+ 'xss-innerHTML': { handler: (c, i, s) => this._fixXss(c, i), priority: 1 },
913
+ 'xss-document-write': { handler: (c, i, s) => this._fixXss(c, i), priority: 1 },
914
+ 'xss-jquery-html-concat': { handler: (c, i, s) => this._fixXss(c, i), priority: 1 },
915
+ 'hardcoded-password': { handler: (c, i, s) => this._fixHardcodedSecret(c, i), priority: 1 },
916
+ 'hardcoded-api-key': { handler: (c, i, s) => this._fixHardcodedSecret(c, i), priority: 1 },
917
+ 'hardcoded-secret': { handler: (c, i, s) => this._fixHardcodedSecret(c, i), priority: 1 },
918
+ 'hardcoded-connection-string': { handler: (c, i, s) => this._fixHardcodedSecret(c, i), priority: 1 },
919
+ 'no-eval': { handler: (c, i, s) => this._fixEval(c, i), priority: 1 },
920
+
921
+ // 性能相关
922
+ 'no-sync-in-loop': { handler: (c, i, s) => this._fixPerformanceIssue(c, i), priority: 2 },
923
+ 'n-plus-one-query': { handler: (c, i, s) => this._fixPerformanceIssue(c, i), priority: 2 },
924
+ 'event-listener-leak': { handler: (c, i, s) => this._fixPerformanceIssue(c, i), priority: 2 },
925
+ 'large-array-chain': { handler: (c, i, s) => this._fixPerformanceIssue(c, i), priority: 3 },
926
+
927
+ // 风格相关
928
+ 'max-line-length': { handler: (c, i, s) => this._fixStyleIssue(c, i), priority: 3 },
929
+ 'no-console-log': { handler: (c, i, s) => this._fixStyleIssue(c, i), priority: 3 },
930
+ 'no-magic-numbers': { handler: (c, i, s) => this._fixStyleIssue(c, i), priority: 3 },
931
+ 'semi-style': { handler: (c, i, s) => this._fixStyleIssue(c, i), priority: 4 },
932
+
933
+ // 逻辑相关
934
+ 'max-nesting-depth': { handler: (c, i, s) => this._fixLogicIssue(c, i), priority: 2 },
935
+ 'max-function-length': { handler: (c, i, s) => this._fixLogicIssue(c, i), priority: 2 },
936
+ 'no-duplicated-code': { handler: (c, i, s) => this._fixLogicIssue(c, i), priority: 2 },
937
+
938
+ // 规格相关
939
+ 'required-function-missing': { handler: (c, i, s) => this._fixSpecViolation(c, i), priority: 1 },
940
+ 'required-export-missing': { handler: (c, i, s) => this._fixSpecViolation(c, i), priority: 1 },
941
+ 'forbidden-api-used': { handler: (c, i, s) => this._fixSpecViolation(c, i), priority: 1 },
942
+
943
+ // 默认策略
944
+ 'default': { handler: (c, i, s) => [this._generateGenericFix(c, i)], priority: 99 }
945
+ };
946
+ }
947
+
948
+ /**
949
+ * 更新运行统计
950
+ * @private
951
+ */
952
+ _updateStats(result) {
953
+ this.stats.totalFixSessions++;
954
+ this.stats.totalSuggestionsGenerated += result.suggestions.length;
955
+ this.stats.avgSuggestionsPerIssue =
956
+ result.suggestions.length / Math.max(1, result.suggestions.length);
957
+ }
958
+
959
+ /**
960
+ * 获取统计信息
961
+ * @returns {Object}
962
+ */
963
+ getStats() {
964
+ return { ...this.stats };
965
+ }
966
+
967
+ /**
968
+ * 重置统计
969
+ */
970
+ resetStats() {
971
+ this.stats = {
972
+ totalFixSessions: 0,
973
+ totalSuggestionsGenerated: 0,
974
+ avgSuggestionsPerIssue: 0
975
+ };
976
+ }
977
+
978
+ /**
979
+ * 导出修复报告为Markdown格式
980
+ * @param {FixResult} fixResult - 修复结果
981
+ * @returns {string} Markdown文本
982
+ */
983
+ exportMarkdownReport(fixResult) {
984
+ const summary = fixResult.getSummary();
985
+ let md = '# 代码修复建议报告\n\n';
986
+ md += `**生成时间**: ${new Date().toLocaleString()}\n\n`;
987
+ md += `## 概览\n\n`;
988
+ md += `- 总建议数: ${summary.totalSuggestions}\n`;
989
+ md += `- 可自动应用: ${summary.autoApplied}\n`;
990
+ md += `- 需人工审核: ${summary.manualReview}\n\n`;
991
+
992
+ md += `## 详细建议\n\n`;
993
+
994
+ for (const sug of fixResult.suggestions) {
995
+ const icon = sug.confidence === ConfidenceLevel.HIGH ? '🟢' :
996
+ sug.confidence === ConfidenceLevel.MEDIUM ? '🟡' : '🔴';
997
+ md += `### ${icon} ${sug.description}\n\n`;
998
+ md += `- **类型**: ${sug.type}\n`;
999
+ md += `- **置信度**: ${sug.confidence}\n`;
1000
+ md += `- **可自动应用**: ${sug.autoApplicable ? '是' : '否'}\n`;
1001
+ if (sug.line) md += `- **位置**: 第${sug.line}行\n`;
1002
+ if (sug.originalCode) {
1003
+ md += `- **原代码**:\n\`\`\`\n${sug.originalCode}\n\`\`\`\n`;
1004
+ }
1005
+ if (sug.fixedCode) {
1006
+ md += `- **修复后**:\n\`\`\`\n${sug.fixedCode}\n\`\`\`\n`;
1007
+ }
1008
+ if (sug.reasoning) md += `- **理由**: ${sug.reasoning}\n`;
1009
+ if (sug.risks?.length > 0) {
1010
+ md += `- **潜在风险**:\n`;
1011
+ for (const risk of sug.risks) {
1012
+ md += ` - ${risk}\n`;
1013
+ }
1014
+ }
1015
+ md += '\n---\n\n';
1016
+ }
1017
+
1018
+ return md;
1019
+ }
1020
+ }
1021
+
1022
+ /**
1023
+ * 默认导出
1024
+ */
1025
+ export default AutoFixer;