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,357 @@
1
+ /**
2
+ * PDD Visual Manager - 总览屏幕 (VM-C020)
3
+ *
4
+ * OverviewScreen 展示项目整体状态:
5
+ * - 项目基本信息和进度
6
+ * - 核心指标卡片(功能点数、通过率、平均分、Token消耗、迭代次数、缓存命中率)
7
+ * - Pipeline 流水线可视化(各阶段分布)
8
+ * - 最近活动时间线
9
+ *
10
+ * 布局结构:
11
+ * ┌──────────────────────────────────────────────┐
12
+ * │ 顶栏: 项目名 | 进度条 | 时间 │
13
+ * ├──────────┬──────────┬────────────────────────┤
14
+ * │ 3张汇总卡│ 3张汇总卡 │ │
15
+ * ├──────────┴──────────┴────────────────────────┤
16
+ * │ Pipeline: PRD→Ext→Spec→Imp→Ver→Done │
17
+ * ├──────────────────────────────────────────────┤
18
+ * │ 最近活动时间线 │
19
+ * └──────────────────────────────────────────────┘
20
+ */
21
+
22
+ import { ANSI } from '../renderer.js';
23
+ import Card from '../components/card.js';
24
+ import ProgressBar from '../components/progress-bar.js';
25
+ import Table from '../components/table.js';
26
+
27
+ /**
28
+ * OverviewScreen - 总览屏
29
+ *
30
+ * 静态方法类,接收 provider 和 renderer 参数进行渲染。
31
+ */
32
+ class OverviewScreen {
33
+ /**
34
+ * 渲染总览屏幕
35
+ * @param {PDDDataProvider} provider - 数据提供者
36
+ * @param {Renderer} renderer - 渲染器实例
37
+ * @returns {string} 完整的屏幕内容字符串
38
+ */
39
+ static render(provider, renderer) {
40
+ const r = renderer;
41
+ const w = r.width;
42
+ const h = r.height;
43
+
44
+ // 获取数据
45
+ const summary = provider.getSummary() || {};
46
+ const features = provider.getFeatures() || [];
47
+ const qualityMatrix = provider.getQualityMatrix() || {};
48
+ const tokenStats = provider.getTokenStats() || {};
49
+ const cacheStats = provider.getCacheStats() || {};
50
+
51
+ // 计算派生值
52
+ const totalFeatures = summary.totalFeatures || features.length || 0;
53
+ const progress = summary.overallProgress || 0;
54
+ const avgScore = summary.avgQualityScore || 0;
55
+ const avgIterations = summary.avgIterations || 0;
56
+ const stageDist = summary.stageDistribution || {};
57
+ const totalTokens = tokenStats.used || summary.totalTokens || 0;
58
+ const tokenLimit = tokenStats.total || 60000;
59
+
60
+ // 通过率计算
61
+ let passRate = 0;
62
+ if (totalFeatures > 0) {
63
+ const doneCount = (stageDist['done'] || 0) + (stageDist['verifying'] || 0);
64
+ passRate = Math.round((doneCount / totalFeatures) * 100);
65
+ }
66
+
67
+ // 缓存命中率
68
+ const cacheHitRate = cacheStats.hitRate || 0;
69
+
70
+ let output = '';
71
+
72
+ // ========== 1. 顶栏 ==========
73
+ output += _renderTopBar(r, summary, progress, w);
74
+
75
+ output += '\n';
76
+
77
+ // ========== 2. 指标卡片区域 (3列布局) ==========
78
+ output += _renderMetricCards(r, {
79
+ totalFeatures,
80
+ passRate,
81
+ avgScore,
82
+ totalTokens,
83
+ tokenLimit,
84
+ avgIterations,
85
+ cacheHitRate
86
+ }, w);
87
+
88
+ output += '\n';
89
+
90
+ // ========== 3. Pipeline 流水线 ==========
91
+ output += _renderPipeline(r, stageDist, totalFeatures, w);
92
+
93
+ output += '\n';
94
+
95
+ // ========== 4. 最近活动 ==========
96
+ output += _renderActivityTimeline(r, features, h);
97
+
98
+ return output;
99
+ }
100
+ }
101
+
102
+ // ============================================================
103
+ // 私有渲染函数
104
+ // ============================================================
105
+
106
+ /**
107
+ * 渲染顶栏
108
+ */
109
+ function _renderTopBar(r, summary, progress, w) {
110
+ const projectName = summary.name || 'PDD Project';
111
+ const version = summary.version || '';
112
+ const now = new Date().toLocaleTimeString('zh-CN', { hour12: false });
113
+
114
+ // 项目名称 + 版本
115
+ const title = r.bold(r.cyan(`📊 ${projectName}`)) +
116
+ (version ? r.dim(` v${version}`) : '');
117
+
118
+ // 进度条
119
+ const pb = new ProgressBar(progress, 100, 20, {
120
+ showLabel: false,
121
+ showPercent: true
122
+ });
123
+ const progressBarStr = pb.render();
124
+
125
+ // 组合顶栏
126
+ const leftPart = `${title} 进度: ${progressBarStr}`;
127
+ const rightPart = r.dim(now);
128
+
129
+ const leftWidth = r.strWidth(leftPart);
130
+ const rightWidth = r.strWidth(rightPart);
131
+ const middlePad = Math.max(2, w - leftWidth - rightWidth);
132
+
133
+ return leftPart + ' '.repeat(middlePad) + rightPart;
134
+ }
135
+
136
+ /**
137
+ * 渲染指标卡片区域
138
+ */
139
+ function _renderMetricCards(r, metrics, w) {
140
+ const { totalFeatures, passRate, avgScore, totalTokens, tokenLimit,
141
+ avgIterations, cacheHitRate } = metrics;
142
+
143
+ // 定义6个指标卡片
144
+ const cardData = [
145
+ {
146
+ label: '功能点',
147
+ value: String(totalFeatures),
148
+ sub: `通过率: ${passRate}%`,
149
+ color: ANSI.CYAN
150
+ },
151
+ {
152
+ label: '平均分',
153
+ value: String(avgScore),
154
+ sub: `迭代: ${avgIterations}`,
155
+ color: ANSI.YELLOW
156
+ },
157
+ {
158
+ label: 'Token',
159
+ value: _formatTokens(totalTokens),
160
+ sub: `/${_formatTokens(tokenLimit)}`,
161
+ color: ANSI.MAGENTA
162
+ },
163
+ {
164
+ label: '缓存',
165
+ value: `${cacheHitRate.toFixed(1)}%`,
166
+ sub: '命中率',
167
+ color: ANSI.GREEN
168
+ },
169
+ {
170
+ label: '质量',
171
+ value: _gradeFromScore(avgScore),
172
+ sub: `${avgScore}/100`,
173
+ color: _gradeColor(avgScore)
174
+ },
175
+ {
176
+ label: '活跃',
177
+ value: String(Math.round(avgIterations * 10) / 10),
178
+ sub: '平均迭代',
179
+ color: ANSI.BLUE
180
+ }
181
+ ];
182
+
183
+ // 将卡片分为两行,每行3个
184
+ const cardWidth = Math.floor((w - 8) / 3); // 减去间距
185
+
186
+ let output = '';
187
+
188
+ for (let row = 0; row < 2; row++) {
189
+ const rowCards = [];
190
+ for (let col = 0; col < 3; col++) {
191
+ const idx = row * 3 + col;
192
+ const data = cardData[idx];
193
+ const cardContent = [
194
+ '',
195
+ ` ${data.color}${r.bold(data.value)}${ANSI.RESET}`,
196
+ ` ${r.dim(data.sub)}`,
197
+ ''
198
+ ];
199
+ const card = new Card(data.label, cardContent, {
200
+ width: cardWidth,
201
+ borderStyle: 'rounded',
202
+ padding: { x: 1, y: 0 }
203
+ });
204
+ rowCards.push(card.render());
205
+ }
206
+ output += rowCards.join(' ') + '\n';
207
+ }
208
+
209
+ return output.trimEnd();
210
+ }
211
+
212
+ /**
213
+ * 渲染 Pipeline 流水线
214
+ */
215
+ function _renderPipeline(r, stageDist, totalFeatures, w) {
216
+ const stages = [
217
+ { key: 'prd', label: 'PRD', short: 'PRD' },
218
+ { key: 'extracted', label: 'Extract', short: 'Ext' },
219
+ { key: 'spec', label: 'Spec', short: 'Spec' },
220
+ { key: 'implementing', label: 'Implement', short: 'Imp' },
221
+ { key: 'verifying', label: 'Verify', short: 'Ver' },
222
+ { key: 'done', label: 'Done', short: 'Done' }
223
+ ];
224
+
225
+ // 构建流水线文本
226
+ let pipelineText = 'Pipeline: ';
227
+ for (const stage of stages) {
228
+ const count = stageDist[stage.key] || 0;
229
+ pipelineText += `${stage.short}(${count})`;
230
+ if (stage.key !== 'done') pipelineText += ' → ';
231
+ }
232
+
233
+ output = r.bold(pipelineText) + '\n\n';
234
+
235
+ // 绘制进度条形式的流水线
236
+ const barChars = ['░', '▒', '▓', '█'];
237
+ let barLine = '';
238
+ const totalStages = stages.length;
239
+
240
+ for (let i = 0; i < totalStages; i++) {
241
+ const stage = stages[i];
242
+ const count = stageDist[stage.key] || 0;
243
+ const maxInStage = Math.max(1, ...stages.map(s => stageDist[s.key] || 0));
244
+ const ratio = count / maxInStage;
245
+
246
+ // 每个阶段占用一定宽度
247
+ const segWidth = Math.max(2, Math.floor((w - 10) / totalStages));
248
+ const filled = Math.ceil(segWidth * ratio);
249
+
250
+ // 根据阶段选择颜色
251
+ const stageColors = [ANSI.RED, ANSI.YELLOW, ANSI.BRIGHT_YELLOW, ANSI.BRIGHT_CYAN, ANSI.GREEN, ANSI.BRIGHT_GREEN];
252
+ const color = stageColors[i] || ANSI.DIM;
253
+
254
+ barLine += color + barChars[3].repeat(filled) + barChars[0].repeat(segWidth - filled) + ANSI.RESET;
255
+ if (i < totalStages - 1) barLine += ' ';
256
+ }
257
+
258
+ output += barLine;
259
+
260
+ return output;
261
+ }
262
+
263
+ /**
264
+ * 渲染最近活动时间线
265
+ */
266
+ function _renderActivityTimeline(r, features, screenHeight) {
267
+ const maxItems = Math.min(8, Math.floor(screenHeight / 2) - 5);
268
+
269
+ // 模拟最近活动(从功能点的时间线中提取)
270
+ const activities = [];
271
+
272
+ // 收集各功能点的最新活动
273
+ const sortedFeatures = [...features]
274
+ .filter(f => f.updatedAt || f.createdAt)
275
+ .sort((a, b) => (b.updatedAt || b.createdAt || 0) - (a.updatedAt || a.createdAt || 0))
276
+ .slice(0, maxItems);
277
+
278
+ for (const f of sortedFeatures) {
279
+ const ts = f.updatedAt || f.createdAt || 0;
280
+ const timeStr = new Date(ts).toLocaleTimeString('zh-CN', {
281
+ hour12: false,
282
+ hour: '2-digit',
283
+ minute: '2-digit',
284
+ second: '2-digit'
285
+ });
286
+
287
+ // 根据阶段决定图标
288
+ const stageIcons = {
289
+ prd: '📋',
290
+ extracted: '🔍',
291
+ spec: '📝',
292
+ implementing: '🔨',
293
+ verifying: '✅',
294
+ done: '✨'
295
+ };
296
+ const icon = stageIcons[f.stage] || '📌';
297
+
298
+ activities.push({
299
+ icon,
300
+ name: f.name || f.id || '(unnamed)',
301
+ stage: f.stage,
302
+ time: timeStr
303
+ });
304
+ }
305
+
306
+ let output = '';
307
+
308
+ // 分隔线
309
+ output += r.separator('─', r.width);
310
+ output += '\n';
311
+
312
+ // 标题
313
+ output += r.bold(' 最近活动') + '\n';
314
+ output += r.separator('─', r.width);
315
+ output += '\n';
316
+
317
+ if (activities.length === 0) {
318
+ output += r.dim(' 暂无活动记录\n');
319
+ } else {
320
+ for (const act of activities) {
321
+ const nameTruncated = r.truncate(act.name, r.width - 20);
322
+ output += ` ${act.icon} ${nameTruncated}`;
323
+ output += r.dim(` ${act.time}\n`);
324
+ }
325
+ }
326
+
327
+ return output;
328
+ }
329
+
330
+ // ============================================================
331
+ // 工具函数
332
+ // ============================================================
333
+
334
+ function _formatTokens(n) {
335
+ if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
336
+ if (n >= 1000) return (n / 1000).toFixed(0) + 'K';
337
+ return String(n);
338
+ }
339
+
340
+ function _gradeFromScore(score) {
341
+ if (score >= 95) return 'S';
342
+ if (score >= 85) return 'A';
343
+ if (score >= 70) return 'B';
344
+ if (score >= 55) return 'C';
345
+ if (score >= 40) return 'D';
346
+ return 'F';
347
+ }
348
+
349
+ function _gradeColor(score) {
350
+ if (score >= 85) return ANSI.GREEN;
351
+ if (score >= 70) return ANSI.BRIGHT_GREEN;
352
+ if (score >= 55) return ANSI.YELLOW;
353
+ if (score >= 40) return ANSI.BRIGHT_RED;
354
+ return ANSI.RED;
355
+ }
356
+
357
+ export default OverviewScreen;
@@ -0,0 +1,336 @@
1
+ /**
2
+ * PDD Visual Manager - 质量屏幕 (VM-C022)
3
+ *
4
+ * QualityScreen 展示项目和功能点的质量信息:
5
+ * - Token 使用统计与配额
6
+ * - 缓存命中率和各级别统计
7
+ * - 质量矩阵(可读性、可维护性、健壮性、性能、安全性)
8
+ * - Top 问题列表
9
+ * - 评分等级分布图
10
+ *
11
+ * 布局:
12
+ * ╭────────────────────┬────────────────────╮
13
+ * │ 💰 Token 使用 │ 🧠 缓存状态 │
14
+ * │ ████████░░ 75% │ L1:98% L2:91% L3:88%│
15
+ * ├────────────────────┼────────────────────┤
16
+ * │ 📊 质量矩阵 │ ⚠️ Top Issues │
17
+ * │ 可读性: 85 ████████░░ │ 1. Error 12 │
18
+ * │ 可维护: 72 ███████░░ │ 2. Warning 8 │
19
+ * ├────────────────────┼────────────────────┤
20
+ * │ 评分分布: S A B C D F │
21
+ * ╰─────────────────────────────────────────╯
22
+ */
23
+
24
+ import { ANSI } from '../renderer.js';
25
+ import Card from '../components/card.js';
26
+ import ProgressBar from '../components/progress-bar.js';
27
+ import Table from '../components/table.js';
28
+ import Sparkline from '../components/sparkline.js';
29
+
30
+ /**
31
+ * QualityScreen - 质量屏
32
+ *
33
+ * 静态方法类,展示全面的质量分析数据。
34
+ */
35
+ class QualityScreen {
36
+ /**
37
+ * 渲染质量屏幕
38
+ * @param {PDDDataProvider} provider - 数据提供者
39
+ * @param {Renderer} renderer - 渲染器
40
+ * @returns {string} 完整的屏幕内容
41
+ */
42
+ static render(provider, renderer) {
43
+ const r = renderer;
44
+ const w = r.width;
45
+ const h = r.height;
46
+
47
+ // 获取数据
48
+ const qualityMatrix = provider.getQualityMatrix() || {};
49
+ const tokenStats = provider.getTokenStats() || {};
50
+ const cacheStats = provider.getCacheStats() || {};
51
+ const systemHealth = provider.getSystemHealth() || {};
52
+ const features = provider.getFeatures() || [];
53
+ const summary = provider.getSummary() || {};
54
+
55
+ let output = '';
56
+
57
+ // ========== 1. 标题 ==========
58
+ output += r.bold(r.magenta('◈ Quality Dashboard')) + '\n';
59
+ output += r.separator('─', w) + '\n\n';
60
+
61
+ // ========== 2. 上半区: Token + 缓存 (双卡片) ==========
62
+ output += _renderTokenAndCacheSection(r, tokenStats, cacheStats, w);
63
+ output += '\n\n';
64
+
65
+ // ========== 3. 中间区: 质量矩阵 + Top Issues ==========
66
+ output += _renderMatrixAndIssues(r, qualityMatrix, features, w);
67
+ output += '\n\n';
68
+
69
+ // ========== 4. 底部区: 评分分布 ==========
70
+ output += _renderGradeDistribution(r, features, summary, w);
71
+
72
+ return output;
73
+ }
74
+ }
75
+
76
+ // ============================================================
77
+ // 区域渲染函数
78
+ // ============================================================
79
+
80
+ /**
81
+ * 渲染 Token 和缓存状态区域
82
+ */
83
+ function _renderTokenAndCacheSection(r, tokenStats, cacheStats, w) {
84
+ const halfW = Math.floor((w - 4) / 2);
85
+
86
+ // --- Token 卡片 ---
87
+ const used = tokenStats.used || 0;
88
+ const total = tokenStats.total || 60000;
89
+ const remaining = tokenStats.remaining || Math.max(0, total - used);
90
+ const pct = total > 0 ? Math.round((used / total) * 100) : 0;
91
+
92
+ const tokenPb = new ProgressBar(used, total, halfW - 10, {
93
+ showPercent: true,
94
+ showLabel: false
95
+ });
96
+
97
+ const tokenContent = [
98
+ '',
99
+ ` ${r.bold(r.cyan(_fmtTokens(used)))}/${_fmtTokens(total)}`,
100
+ ` ${tokenPb.render()}`,
101
+ ` ${r.dim(`剩余: ${_fmtTokens(remaining)}`)}`,
102
+ ''
103
+ ];
104
+
105
+ const tokenCard = new Card('💰 Token Usage', tokenContent, {
106
+ width: halfW,
107
+ borderStyle: 'rounded'
108
+ });
109
+
110
+ // --- 缓存卡片 ---
111
+ const hitRate = cacheStats.hitRate || 0;
112
+ const l1 = cacheStats.l1HitRate ?? 95;
113
+ const l2 = cacheStats.l2HitRate ?? 88;
114
+ const l3 = cacheStats.l3HitRate ?? 80;
115
+ const size = cacheStats.sizeKB || 0;
116
+
117
+ const cacheContent = [
118
+ '',
119
+ ` ${r.bold(r.green(`${hitRate.toFixed(1)}%`))} ${r.dim('命中率')}`,
120
+ ``,
121
+ ` L1: ${_rateBar(l1)} ${l1.toFixed(0)}%`,
122
+ ` L2: ${_rateBar(l2)} ${l2.toFixed(0)}%`,
123
+ ` L3: ${_rateBar(l3)} ${l3.toFixed(0)}%`,
124
+ ` ${r.dim(`Size: ${(size / 1024).toFixed(1)}MB`)}`,
125
+ ''
126
+ ];
127
+
128
+ const cacheCard = new Card('🧠 Cache Status', cacheContent, {
129
+ width: halfW,
130
+ borderStyle: 'rounded'
131
+ });
132
+
133
+ return Card.sideBySide(tokenCard, cacheCard, 4);
134
+ }
135
+
136
+ /**
137
+ * 渲染质量矩阵和问题列表区域
138
+ */
139
+ function _renderMatrixAndIssues(r, qualityMatrix, features, w) {
140
+ const halfW = Math.floor((w - 4) / 2);
141
+
142
+ // --- 质量矩阵 ---
143
+ const dimensions = [
144
+ { key: 'readability', label: '可读性', icon: '👁' },
145
+ { key: 'maintainability', label: '可维护', icon: '🔧' },
146
+ { key: 'robustness', label: '健壮性', icon: '🛡' },
147
+ { key: 'performance', label: '性能', icon: '⚡' },
148
+ { key: 'security', label: '安全性', icon: '🔒' }
149
+ ];
150
+
151
+ const matrixLines = ['', ''];
152
+
153
+ for (const dim of dimensions) {
154
+ const score = qualityMatrix[dim.key] ?? _estimateDimension(features, dim.key);
155
+ const pb = new ProgressBar(score, 100, 12, { showPercent: false });
156
+ matrixLines.push(
157
+ ` ${dim.icon} ${r.pad(dim.label, 8)} ${pb.render()} ${r.bold(String(score))}`
158
+ );
159
+ }
160
+ matrixLines.push('');
161
+
162
+ const matrixCard = new Card('📊 Quality Matrix', matrixLines, {
163
+ width: halfW,
164
+ borderStyle: 'rounded'
165
+ });
166
+
167
+ // --- Top Issues ---
168
+ const issues = _collectIssues(features);
169
+ const issueLines = [''];
170
+
171
+ if (issues.length === 0) {
172
+ issueLines.push(` ${r.green('✓ No issues found')}`);
173
+ issueLines.push('');
174
+ } else {
175
+ const topIssues = issues.slice(0, 8);
176
+ for (let i = 0; i < topIssues.length; i++) {
177
+ const iss = topIssues[i];
178
+ const severityIcon = _severityIcon(iss.severity);
179
+ issueLines.push(
180
+ ` ${r.bold(String(i + 1).padStart(2))}. ${severityIcon} ${r.pad(iss.message || iss.type || '-', 18)} ${iss.count || ''}`
181
+ );
182
+ }
183
+ issueLines.push('');
184
+ }
185
+
186
+ const issuesCard = new Card('⚠️ Top Issues', issueLines, {
187
+ width: halfW,
188
+ borderStyle: 'rounded'
189
+ });
190
+
191
+ return Card.sideBySide(matrixCard, issuesCard, 4);
192
+ }
193
+
194
+ /**
195
+ * 渲染评分分布区域
196
+ */
197
+ function _renderGradeDistribution(r, features, summary, w) {
198
+ // 统计各等级数量
199
+ const grades = { S: 0, A: 0, B: 0, C: 0, D: 0, F: 0 };
200
+
201
+ for (const f of features) {
202
+ if (f.quality && f.quality.grade) {
203
+ const g = f.quality.grade.toUpperCase();
204
+ if (grades.hasOwnProperty(g)) {
205
+ grades[g]++;
206
+ }
207
+ }
208
+ }
209
+
210
+ // 如果没有质量数据,基于平均分估算
211
+ const totalGraded = Object.values(grades).reduce((s, v) => s + v, 0);
212
+ if (totalGraded === 0 && summary.avgQualityScore > 0) {
213
+ _estimateGrades(grades, summary.avgQualityScore, features.length);
214
+ }
215
+
216
+ // 构建分布字符串
217
+ const gradeOrder = ['S', 'A', 'B', 'C', 'D', 'F'];
218
+ const gradeColors = {
219
+ S: ANSI.BRIGHT_GREEN, A: ANSI.GREEN, B: ANSI.BRIGHT_YELLOW,
220
+ C: ANSI.YELLOW, D: ANSI.BRIGHT_RED, F: ANSI.RED
221
+ };
222
+
223
+ let distStr = ' Distribution: ';
224
+ for (const g of gradeOrder) {
225
+ distStr += `${gradeColors[g]}${g}(${grades[g]})${ANSI.RESET} `;
226
+ }
227
+
228
+ // ASCII 分布条形图
229
+ const maxCount = Math.max(...Object.values(grades), 1);
230
+ const barParts = gradeOrder.map(g => {
231
+ const count = grades[g];
232
+ const barLen = Math.round((count / maxCount) * 20);
233
+ return `${gradeColors[g]}${'█'.repeat(barLen)}${ANSI.RESET}`;
234
+ });
235
+
236
+ let output = '';
237
+ output += r.separator('─', w) + '\n';
238
+ output += distStr + '\n';
239
+ output += ' ' + barParts.join(' ') + '\n';
240
+
241
+ return output;
242
+ }
243
+
244
+ // ============================================================
245
+ // 工具函数
246
+ // ============================================================
247
+
248
+ function _fmtTokens(n) {
249
+ if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
250
+ if (n >= 1000) return (n / 1000).toFixed(0) + 'K';
251
+ return String(n);
252
+ }
253
+
254
+ function _rateBar(pct) {
255
+ if (pct >= 90) return `${ANSI.GREEN}████${ANSI.RESET}`;
256
+ if (pct >= 70) return `${ANSI.BRIGHT_GREEN}███░${ANSI.RESET}`;
257
+ if (pct >= 50) return `${ANSI.YELLOW}██░░${ANSI.RESET}`;
258
+ if (pct >= 30) return `${ANSI.BRIGHT_YELLOW}█░░░${ANSI.RESET}`;
259
+ return `${ANSI.RED}░░░░${ANSI.RESET}`;
260
+ }
261
+
262
+ function _severityIcon(severity) {
263
+ switch ((severity || '').toLowerCase()) {
264
+ case 'error': return r => r.brightRed('✖');
265
+ case 'warning': return r => r.brightYellow('⚠');
266
+ case 'style': return r => r.yellow('◎');
267
+ case 'security': return r => r.red('⛨');
268
+ case 'performance': return r => r.cyan('⚡');
269
+ default: return r => r.dim('•');
270
+ }
271
+ }
272
+
273
+ function _collectIssues(features) {
274
+ const issueMap = {};
275
+
276
+ for (const f of features) {
277
+ if (!f.quality || !f.quality.issues) continue;
278
+ for (const issue of f.quality.issues) {
279
+ const type = issue.type || issue.severity || 'unknown';
280
+ const msg = issue.message || issue.type || 'Unknown';
281
+ const key = `${type}:${msg}`;
282
+
283
+ if (!issueMap[key]) {
284
+ issueMap[key] = { type, message: msg, severity: issue.severity || type, count: 0 };
285
+ }
286
+ issueMap[key].count++;
287
+ }
288
+ }
289
+
290
+ // 按数量排序
291
+ return Object.values(issueMap).sort((a, b) => b.count - a.count);
292
+ }
293
+
294
+ function _estimateDimension(features, dimKey) {
295
+ // 从功能点的质量指标中估算维度分数
296
+ let total = 0;
297
+ let count = 0;
298
+
299
+ for (const f of features) {
300
+ if (f.quality && typeof f.quality.score === 'number') {
301
+ total += f.quality.score;
302
+ count++;
303
+ }
304
+ }
305
+
306
+ // 基于不同维度添加一些变化
307
+ const baseAvg = count > 0 ? total / count : 65;
308
+ const variance = { readability: 5, maintainability: -3, robustness: 8, performance: -10, security: 2 };
309
+ const adjustment = variance[dimKey] || 0;
310
+
311
+ return Math.max(0, Math.min(100, Math.round(baseAvg + adjustment)));
312
+ }
313
+
314
+ function _estimateGrades(grades, avgScore, total) {
315
+ // 基于正态分布估算
316
+ const base = total > 0 ? total : 10;
317
+ if (avgScore >= 90) {
318
+ grades.S = Math.round(base * 0.15); grades.A = Math.round(base * 0.35);
319
+ grades.B = Math.round(base * 0.30); grades.C = Math.round(base * 0.15);
320
+ grades.D = Math.round(base * 0.04); grades.F = Math.round(base * 0.01);
321
+ } else if (avgScore >= 75) {
322
+ grades.S = Math.round(base * 0.05); grades.A = Math.round(base * 0.25);
323
+ grades.B = Math.round(base * 0.35); grades.C = Math.round(base * 0.25);
324
+ grades.D = Math.round(base * 0.08); grades.F = Math.round(base * 0.02);
325
+ } else if (avgScore >= 60) {
326
+ grades.S = 0; grades.A = Math.round(base * 0.10);
327
+ grades.B = Math.round(base * 0.30); grades.C = Math.round(base * 0.35);
328
+ grades.D = Math.round(base * 0.18); grades.F = Math.round(base * 0.07);
329
+ } else {
330
+ grades.S = 0; grades.A = Math.round(base * 0.03);
331
+ grades.B = Math.round(base * 0.12); grades.C = Math.round(base * 0.28);
332
+ grades.D = Math.round(base * 0.32); grades.F = Math.round(base * 0.25);
333
+ }
334
+ }
335
+
336
+ export default QualityScreen;