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,598 @@
1
+ /**
2
+ * QualityView - 质量监控视图 (Panel 3)
3
+ * 展示五维雷达图、评分分布、Token仪表盘、缓存效率、问题排行、迭代收敛曲线
4
+ *
5
+ * @module QualityView
6
+ * @version 1.0.0
7
+ * @requires App, Charts (Canvas 图表引擎)
8
+ */
9
+
10
+ const QualityView = {
11
+ name: 'quality',
12
+
13
+ /**
14
+ * 初始化视图
15
+ */
16
+ init() {
17
+ App.registerView(this.name, this);
18
+ console.log('[QualityView] Initialized');
19
+ },
20
+
21
+ /**
22
+ * 视图显示时调用
23
+ */
24
+ onShow() {
25
+ this.render();
26
+ },
27
+
28
+ /**
29
+ * SSE事件处理
30
+ * @param {string} type - 事件类型
31
+ * @param {*} data - 事件数据
32
+ */
33
+ onEvent(type, data) {
34
+ if (type === 'quality_update' || type === 'data_refreshed' || type === 'token_threshold') {
35
+ this.render();
36
+ }
37
+ },
38
+
39
+ /**
40
+ * 主渲染方法(异步,因为需要fetch多个API)
41
+ */
42
+ async render() {
43
+ const container = document.getElementById('view-quality');
44
+ if (!container) return;
45
+
46
+ // 显示加载状态
47
+ container.innerHTML = `
48
+ <div class="loading-state">
49
+ <div class="spinner"></div>
50
+ <p>正在加载质量数据...</p>
51
+ </div>
52
+ `;
53
+
54
+ try {
55
+ // 并行获取所有需要的数据
56
+ const [qualityRes, tokenRes, cacheRes, iterationRes] = await Promise.all([
57
+ fetch('/api/quality').catch(() => null),
58
+ fetch('/api/tokens').catch(() => null),
59
+ fetch('/api/cache').catch(() => null),
60
+ fetch('/api/iterations').catch(() => null)
61
+ ]);
62
+
63
+ // 解析响应
64
+ let qualityMatrix = null;
65
+ let tokenStats = null;
66
+ let cacheStats = null;
67
+ let iterations = [];
68
+
69
+ if (qualityRes && qualityRes.ok) {
70
+ const qData = await qualityRes.json();
71
+ qualityMatrix = qData.matrix || qData;
72
+ }
73
+
74
+ if (tokenRes && tokenRes.ok) {
75
+ const tData = await tokenRes.json();
76
+ tokenStats = tData.stats || tData;
77
+ }
78
+
79
+ if (cacheRes && cacheRes.ok) {
80
+ const cData = await cacheRes.json();
81
+ cacheStats = cData.stats || cData;
82
+ }
83
+
84
+ if (iterationRes && iterationRes.ok) {
85
+ const iData = await iterationRes.json();
86
+ iterations = iData.iterations || [];
87
+ }
88
+
89
+ // 渲染完整视图
90
+ container.innerHTML = `
91
+ <div class="quality-view">
92
+ <div class="grid-2">
93
+ <!-- 左上: 雷达图 -->
94
+ ${this._renderRadarChart(qualityMatrix)}
95
+
96
+ <!-- 右上: 评分分布 -->
97
+ ${this._renderGradeDistribution(qualityMatrix)}
98
+
99
+ <!-- 左中: Token仪表盘 -->
100
+ ${this._renderTokenGauge(tokenStats)}
101
+
102
+ <!-- 右中: 缓存面板 -->
103
+ ${this._renderCachePanel(cacheStats)}
104
+
105
+ <!-- 左下: 问题排行 -->
106
+ ${this._renderTopIssues(qualityMatrix)}
107
+
108
+ <!-- 右下: 迭代收敛曲线 -->
109
+ ${this._renderConvergenceCurve(iterations)}
110
+ </div>
111
+ </div>
112
+ `;
113
+
114
+ // 延迟绘制Canvas图表(确保DOM已渲染)
115
+ setTimeout(() => {
116
+ this._drawCharts(qualityMatrix, tokenStats, iterations);
117
+ }, 100);
118
+
119
+ } catch (error) {
120
+ console.error('[QualityView] Render error:', error);
121
+ container.innerHTML = `
122
+ <div class="error-state">
123
+ <div class="error-icon">⚠️</div>
124
+ <h3>加载质量数据失败</h3>
125
+ <p>${error.message}</p>
126
+ <button class="btn btn-primary" onclick="QualityView.render()">重试</button>
127
+ </div>
128
+ `;
129
+ }
130
+ },
131
+
132
+ // ==================== 子面板渲染方法 ====================
133
+
134
+ /**
135
+ * 渲染雷达图容器
136
+ * @param {Object} matrix - 质量矩阵数据
137
+ * @returns {string} HTML
138
+ */
139
+ _renderRadarChart(matrix) {
140
+ const dimensions = matrix?.dimensions || {};
141
+ const hasData = Object.keys(dimensions).length > 0;
142
+
143
+ return `
144
+ <div class="card radar-card">
145
+ <div class="card-header">
146
+ <h3>🎯 五维质量雷达</h3>
147
+ <span class="text-muted">各维度得分</span>
148
+ </div>
149
+ <div class="card-body canvas-container">
150
+ ${hasData ? '<canvas id="radar-chart" width="400" height="350"></canvas>' : '<div class="empty-panel">暂无数据</div>'}
151
+ </div>
152
+ </div>
153
+ `;
154
+ },
155
+
156
+ /**
157
+ * 渲染评分分布直方图容器
158
+ * @param {Object} matrix - 质量矩阵数据
159
+ * @returns {string} HTML
160
+ */
161
+ _renderGradeDistribution(matrix) {
162
+ const distribution = matrix?.gradeDistribution || matrix?.distribution || {};
163
+ const avgScore = matrix?.avgScore || matrix?.averageScore || 0;
164
+
165
+ return `
166
+ <div class="card grade-dist-card">
167
+ <div class="card-header">
168
+ <h3>📊 评分分布</h3>
169
+ <span class="badge badge-${Charts.getGrade(avgScore).toLowerCase()}">平均: ${avgScore.toFixed(1)}</span>
170
+ </div>
171
+ <div class="card-body canvas-container">
172
+ <canvas id="grade-histogram" width="400" height="300"></canvas>
173
+ </div>
174
+ </div>
175
+ `;
176
+ },
177
+
178
+ /**
179
+ * 渲染Token预算仪表盘容器
180
+ * @param {Object} tokenStats - Token统计
181
+ * @returns {string} HTML
182
+ */
183
+ _renderTokenGauge(tokenStats) {
184
+ const used = tokenStats?.used || tokenStats?.consumed || 0;
185
+ const total = tokenStats?.total || tokenStats?.budget || 100000;
186
+ const percent = total > 0 ? ((used / total) * 100).toFixed(1) : 0;
187
+
188
+ return `
189
+ <div class="card token-gauge-card">
190
+ <div class="card-header">
191
+ <h3>💰 Token 预算</h3>
192
+ <span class="text-muted">${percent}% 已使用</span>
193
+ </div>
194
+ <div class="card-body">
195
+ <div class="gauge-container">
196
+ <canvas id="token-gauge" width="200" height="200"></canvas>
197
+ </div>
198
+ <div class="token-stats-row">
199
+ <div class="ts-item">
200
+ <span class="ts-label">已用</span>
201
+ <span class="ts-value">${this._formatNumber(used)}</span>
202
+ </div>
203
+ <div class="ts-divider">/</div>
204
+ <div class="ts-item">
205
+ <span class="ts-label">预算</span>
206
+ <span class="ts-value">${this._formatNumber(total)}</span>
207
+ </div>
208
+ <div class="ts-divider">/</div>
209
+ <div class="ts-item">
210
+ <span class="ts-label">剩余</span>
211
+ <span class="ts-value" style="color: ${percent > 95 ? '#e74c3c' : percent > 80 ? '#f39c12' : '#27ae60'}">${this._formatNumber(total - used)}</span>
212
+ </div>
213
+ </div>
214
+
215
+ <!-- 各阶段Token消耗柱状图 -->
216
+ ${tokenStats?.byStage ? `
217
+ <div class="stage-token-bars mt-3">
218
+ <canvas id="stage-token-chart" width="380" height="180"></canvas>
219
+ </div>
220
+ ` : ''}
221
+ </div>
222
+ </div>
223
+ `;
224
+ },
225
+
226
+ /**
227
+ * 渲染缓存效率面板
228
+ * @param {Object|null} cacheStats - 缓存统计
229
+ * @returns {string} HTML
230
+ */
231
+ _renderCachePanel(cacheStats) {
232
+ if (!cacheStats) {
233
+ return `
234
+ <div class="card cache-card">
235
+ <div class="card-header">
236
+ <h3>⚡ 缓存效率</h3>
237
+ <span class="badge badge-secondary">N/A</span>
238
+ </div>
239
+ <div class="card-body text-center py-4">
240
+ <div class="empty-icon">🚫</div>
241
+ <p class="text-muted">缓存未启用或无数据</p>
242
+ </div>
243
+ </div>
244
+ `;
245
+ }
246
+
247
+ const l1 = cacheStats.l1?.hitRate || cacheStats.l1HitRate || 0;
248
+ const l2 = cacheStats.l2?.hitRate || cacheStats.l2HitRate || 0;
249
+ const l3 = cacheStats.l3?.hitRate || cacheStats.l3HitRate || 0;
250
+ const overall = cacheStats.overall?.hitRate || cacheStats.overallHitRate ||
251
+ ((l1 + l2 + l3) / 3) || 0;
252
+
253
+ return `
254
+ <div class="card cache-card">
255
+ <div class="card-header">
256
+ <h3>⚡ 缓存效率</h3>
257
+ <span class="text-muted">三层命中率</span>
258
+ </div>
259
+ <div class="card-body">
260
+ <div class="cache-overall">
261
+ <div class="cache-big-number" style="color: ${overall >= 90 ? '#27ae60' : overall >= 70 ? '#f39c12' : '#e74c3c'}">
262
+ ${overall.toFixed(1)}%
263
+ </div>
264
+ <div class="cache-label">总命中率</div>
265
+ </div>
266
+
267
+ <div class="cache-layers">
268
+ <div class="cache-layer">
269
+ <div class="layer-name">L1 缓存</div>
270
+ <div class="layer-bar-container">
271
+ <div class="layer-bar-fill" style="width: ${l1}%; background-color: #27ae60;"></div>
272
+ </div>
273
+ <div class="layer-value">${l1.toFixed(1)}%</div>
274
+ </div>
275
+ <div class="cache-layer">
276
+ <div class="layer-name">L2 缓存</div>
277
+ <div class="layer-bar-container">
278
+ <div class="layer-bar-fill" style="width: ${l2}%; background-color: #3498db;"></div>
279
+ </div>
280
+ <div class="layer-value">${l2.toFixed(1)}%</div>
281
+ </div>
282
+ <div class="cache-layer">
283
+ <div class="layer-name">L3 缓存</div>
284
+ <div class="layer-bar-container">
285
+ <div class="layer-bar-fill" style="width: ${l3}%; background-color: #9b59b6;"></div>
286
+ </div>
287
+ <div class="layer-value">${l3.toFixed(1)}%</div>
288
+ </div>
289
+ </div>
290
+
291
+ ${cacheStats.size ? `<div class="cache-meta text-muted mt-2">缓存大小: ${this._formatBytes(cacheStats.size)}</div>` : ''}
292
+ </div>
293
+ </div>
294
+ `;
295
+ },
296
+
297
+ /**
298
+ * 渲染Top N问题排行容器
299
+ * @param {Object} matrix - 质量矩阵数据
300
+ * @returns {string} HTML
301
+ */
302
+ _renderTopIssues(matrix) {
303
+ const issueTypes = matrix?.issueTypes || matrix?.topIssues || {};
304
+
305
+ return `
306
+ <div class="card issues-rank-card">
307
+ <div class="card-header">
308
+ <h3>🐛 Top 问题排行</h3>
309
+ <span class="text-muted">按类型统计</span>
310
+ </div>
311
+ <div class="card-body canvas-container">
312
+ ${Object.keys(issueTypes).length > 0 ?
313
+ '<canvas id="issues-horizontal-bar" width="400" height="280"></canvas>' :
314
+ '<div class="empty-panel success">✅ 未发现问题</div>'
315
+ }
316
+ </div>
317
+ </div>
318
+ `;
319
+ },
320
+
321
+ /**
322
+ * 渲染迭代收敛曲线容器
323
+ * @param {Array} iterations - 迭代列表
324
+ * @returns {string} HTML
325
+ */
326
+ _renderConvergenceCurve(iterations) {
327
+ const hasIterations = Array.isArray(iterations) && iterations.length > 0;
328
+
329
+ return `
330
+ <div class="card convergence-card">
331
+ <div class="card-header">
332
+ <h3>📈 迭代收敛曲线</h3>
333
+ ${hasIterations ? `<span class="badge badge-success">共 ${iterations.length} 轮</span>` : ''}
334
+ </div>
335
+ <div class="card-body canvas-container">
336
+ ${hasIterations ?
337
+ '<canvas id="convergence-line-chart" width="400" height="300"></canvas>' :
338
+ '<div class="empty-panel">暂无迭代数据</div>'
339
+ }
340
+ </div>
341
+ </div>
342
+ `;
343
+ },
344
+
345
+ // ==================== Canvas 绘制方法 ====================
346
+
347
+ /**
348
+ * 绘制所有Canvas图表
349
+ * @param {Object} matrix - 质量矩阵
350
+ * @param {Object} tokenStats - Token统计
351
+ * @param {Array} iterations - 迭代列表
352
+ */
353
+ _drawCharts(matrix, tokenStats, iterations) {
354
+ try {
355
+ // 1. 绘制雷达图
356
+ this._drawRadarChart(matrix);
357
+
358
+ // 2. 绘制评分分布直方图
359
+ this._drawGradeHistogram(matrix);
360
+
361
+ // 3. 绘制Token仪表盘
362
+ this._drawTokenGauge(tokenStats);
363
+
364
+ // 4. 绘制阶段Token柱状图
365
+ this._drawStageTokenChart(tokenStats);
366
+
367
+ // 5. 绘制问题排行条形图
368
+ this._drawIssuesBar(matrix);
369
+
370
+ // 6. 绘制迭代收敛曲线
371
+ this._drawConvergenceChart(iterations);
372
+
373
+ } catch (error) {
374
+ console.error('[QualityView] Chart drawing error:', error);
375
+ }
376
+ },
377
+
378
+ /**
379
+ * 绘制五维雷达图
380
+ * @param {Object} matrix - 质量矩阵
381
+ */
382
+ _drawRadarChart(matrix) {
383
+ const canvas = document.getElementById('radar-chart');
384
+ if (!canvas || !matrix?.dimensions) return;
385
+
386
+ const dims = matrix.dimensions;
387
+ const labels = Object.keys(dims);
388
+ const values = Object.values(dims);
389
+
390
+ // 确保是五维数据(不足的补全,多余的截取)
391
+ while (labels.length < 5) {
392
+ labels.push(`维度${labels.length + 1}`);
393
+ values.push(0);
394
+ }
395
+
396
+ Charts.radar('radar-chart', {
397
+ labels: labels.slice(0, 5),
398
+ values: values.slice(0, 5)
399
+ }, {
400
+ colors: ['#3498db'],
401
+ fillAlpha: 0.25,
402
+ animate: true,
403
+ maxVal: 100,
404
+ onHover: (index, info) => {
405
+ // 可以在这里添加悬停提示逻辑
406
+ console.log('Radar hover:', info);
407
+ }
408
+ });
409
+ },
410
+
411
+ /**
412
+ * 绘制评分分布直方图
413
+ * @param {Object} matrix - 质量矩阵
414
+ */
415
+ _drawGradeHistogram(matrix) {
416
+ const canvas = document.getElementById('grade-histogram');
417
+ if (!canvas) return;
418
+
419
+ const dist = matrix?.gradeDistribution || matrix?.distribution || {
420
+ S: 0, A: 0, B: 0, C: 0, D: 0, F: 0
421
+ };
422
+
423
+ const grades = ['S', 'A', 'B', 'C', 'D', 'F'];
424
+ const colors = ['#27ae60', '#2980b9', '#f39c12', '#e67e22', '#e74c3c', '#c0392b'];
425
+
426
+ Charts.histogram('grade-histogram', {
427
+ labels: grades,
428
+ values: grades.map(g => dist[g] || 0)
429
+ }, {
430
+ colors: colors,
431
+ label: '数量',
432
+ animate: true,
433
+ onClick: (index, label, value) => {
434
+ if (value > 0 && typeof KanbanView !== 'undefined') {
435
+ // 点击筛选对应等级的功能点
436
+ window.location.hash = '#kanban';
437
+ setTimeout(() => {
438
+ KanbanView.setFilter('search', `grade:${label}`);
439
+ }, 100);
440
+ }
441
+ }
442
+ });
443
+ },
444
+
445
+ /**
446
+ * 绘制Token仪表盘
447
+ * @param {Object} tokenStats - Token统计
448
+ */
449
+ _drawTokenGauge(tokenStats) {
450
+ const canvas = document.getElementById('token-gauge');
451
+ if (!canvas || !tokenStats) return;
452
+
453
+ const used = tokenStats.used || tokenStats.consumed || 0;
454
+ const total = tokenStats.total || tokenStats.budget || 100000;
455
+ const percent = total > 0 ? ((used / total) * 100) : 0;
456
+
457
+ Charts.gauge('token-gauge', percent, {
458
+ color: '#3498db',
459
+ bgColor: '#ecf0f1',
460
+ lineWidth: 14,
461
+ label: 'TOKENS',
462
+ thresholdColors: {
463
+ warning: '#f39c12',
464
+ danger: '#e74c3c',
465
+ warningThreshold: 80,
466
+ dangerThreshold: 95
467
+ },
468
+ animate: true
469
+ });
470
+ },
471
+
472
+ /**
473
+ * 绘制各阶段Token柱状图
474
+ * @param {Object} tokenStats - Token统计
475
+ */
476
+ _drawStageTokenChart(tokenStats) {
477
+ const canvas = document.getElementById('stage-token-chart');
478
+ if (!canvas || !tokenStats?.byStage) return;
479
+
480
+ const byStage = tokenStats.byStage;
481
+ const stages = ['prd', 'extracted', 'spec', 'implementing', 'verifying'];
482
+ const stageLabels = ['PRD', '提取', '规格', '实现', '验证'];
483
+ const stageColors = ['#9b59b6', '#3498db', '#2ecc71', '#f39c12', '#e74c3c'];
484
+
485
+ const values = stages.map(s => byStage[s] || 0);
486
+
487
+ Charts.histogram('stage-token-chart', {
488
+ labels: stageLabels,
489
+ values: values
490
+ }, {
491
+ colors: stageColors,
492
+ label: 'Tokens',
493
+ animate: true
494
+ });
495
+ },
496
+
497
+ /**
498
+ * 绘制问题排行水平条形图
499
+ * @param {Object} matrix - 质量矩阵
500
+ */
501
+ _drawIssuesBar(matrix) {
502
+ const canvas = document.getElementById('issues-horizontal-bar');
503
+ if (!canvas) return;
504
+
505
+ const issueTypes = matrix?.issueTypes || matrix?.topIssues || {};
506
+
507
+ if (Object.keys(issueTypes).length === 0) return;
508
+
509
+ const entries = Object.entries(issueTypes)
510
+ .sort((a, b) => b[1] - a[1])
511
+ .slice(0, 8); // Top 8
512
+
513
+ Charts.horizontalBar('issues-horizontal-bar', {
514
+ labels: entries.map(e => e[0]),
515
+ values: entries.map(e => e[1])
516
+ }, {
517
+ colors: ['#e74c3c', '#f39c12', '#3498db', '#9b59b6', '#1abc9c', '#e67e22', '#34495e', '#95a5a6'],
518
+ animate: true,
519
+ showPercent: true,
520
+ onClick: (index, label, value) => {
521
+ // 可以跳转到看板视图筛选相关问题
522
+ if (typeof KanbanView !== 'undefined') {
523
+ App.showModal(`
524
+ <div class="issue-detail-modal">
525
+ <h4>${label}</h4>
526
+ <p>共有 <strong>${value}</strong> 个此类型的问题</p>
527
+ <p class="text-muted">点击下方按钮查看涉及的功能点</p>
528
+ <button class="btn btn-primary" onclick="App.closeModal(); window.location.hash='#kanban';">
529
+ 查看功能点
530
+ </button>
531
+ </div>
532
+ `);
533
+ }
534
+ }
535
+ });
536
+ },
537
+
538
+ /**
539
+ * 绘制迭代收敛曲线
540
+ * @param {Array} iterations - 迭代列表
541
+ */
542
+ _drawConvergenceChart(iterations) {
543
+ const canvas = document.getElementById('convergence-line-chart');
544
+ if (!canvas || !Array.isArray(iterations) || iterations.length === 0) return;
545
+
546
+ const labels = iterations.map((_, i) => `R${i + 1}`);
547
+ const scores = iterations.map(it => it.score || 0);
548
+
549
+ Charts.lineChart('convergence-line-chart', {
550
+ labels: labels,
551
+ datasets: [{
552
+ label: '评分',
553
+ values: scores,
554
+ color: '#3498db'
555
+ }]
556
+ }, {
557
+ threshold: 90,
558
+ thresholdLabel: '目标线 (90分)',
559
+ animate: true,
560
+ showPoints: true,
561
+ fillArea: true
562
+ });
563
+ },
564
+
565
+ // ==================== 工具方法 ====================
566
+
567
+ /**
568
+ * 格式化数字(千位分隔符)
569
+ * @param {number} num
570
+ * @returns {string}
571
+ */
572
+ _formatNumber(num) {
573
+ if (!num && num !== 0) return '0';
574
+ return Math.round(num).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
575
+ },
576
+
577
+ /**
578
+ * 格式化字节数
579
+ * @param {number} bytes
580
+ * @returns {string}
581
+ */
582
+ _formatBytes(bytes) {
583
+ if (!bytes) return '0 B';
584
+ const units = ['B', 'KB', 'MB', 'GB'];
585
+ let i = 0;
586
+ while (bytes >= 1024 && i < units.length - 1) {
587
+ bytes /= 1024;
588
+ i++;
589
+ }
590
+ return `${bytes.toFixed(i > 0 ? 1 : 0)} ${units[i]}`;
591
+ }
592
+ };
593
+
594
+ // 初始化
595
+ QualityView.init();
596
+
597
+ // 导出全局使用
598
+ window.QualityView = QualityView;