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,397 @@
1
+ /**
2
+ * PDD Visual Manager - 看板屏幕 (VM-C021)
3
+ *
4
+ * KanbanScreen 展示功能点看板视图:
5
+ * - 6列紧凑看板 (PRD → Extracted → Spec → Implementing → Verifying → Done)
6
+ * - 每个功能点显示为一张小卡片(名称+优先级+状态)
7
+ * - 支持键盘导航选择 (j/k 或 方向键)
8
+ * - Enter 打开详情覆盖层
9
+ * - 搜索过滤集成 (VM-C025)
10
+ * - Feature Detail Overlay (VM-C024)
11
+ *
12
+ * 布局:
13
+ * ┌─PRD(2)─┐┌Ext(5)──┐┌Spec(8)─┐┌Imp(4)──┐┌Ver(3)──┐┌Done(1)┐
14
+ * │┌───────┐│┌──────┐│┌──────┐│┌──────┐│┌──────┐│┌──────┐│
15
+ * ││auth │││order │││cart │││paymt │││auth │││repo ││
16
+ * ││ P0 A │││ P1 B│││ P2 │││ P1 │││ S✓ │││ S✓ ││
17
+ * │└───────┘│└──────┘│└──────┘│└──────┘│└──────┘│└──────┘│
18
+ * └─────────┴────────┴────────┴────────┴────────┴────────┘
19
+ * ↑ j/k 选择 Enter 查看 ← → 翻页
20
+ */
21
+
22
+ import { ANSI } from '../renderer.js';
23
+ import { StageEnum, STAGE_ORDER, STAGE_VALUES } from '../../models.js';
24
+ import StatusLight from '../components/status-light.js';
25
+
26
+ /** 看板列定义 */
27
+ const KANBAN_COLUMNS = [
28
+ { key: StageEnum.PRD, label: 'PRD', color: ANSI.RED },
29
+ { key: StageEnum.EXTRACTED, label: 'Ext', color: ANSI.YELLOW },
30
+ { key: StageEnum.SPEC, label: 'Spec', color: ANSI.BRIGHT_YELLOW },
31
+ { key: StageEnum.IMPLEMENTING, label: 'Imp', color: ANSI.CYAN },
32
+ { key: StageEnum.VERIFYING, label: 'Ver', color: ANSI.GREEN },
33
+ { key: StageEnum.DONE, label: 'Done', color: ANSI.BRIGHT_GREEN }
34
+ ];
35
+
36
+ /** 单张功能点卡片尺寸 */
37
+ const CARD_INNER_W = 10; // 卡片内部宽度
38
+ const CARD_HEIGHT = 3; // 卡片高度(不含边框)
39
+ const COL_SPACING = 1; // 列间距
40
+
41
+ /**
42
+ * KanbanScreen - 看板屏
43
+ *
44
+ * 静态方法类,支持功能点浏览、选择和详情查看。
45
+ */
46
+ class KanbanScreen {
47
+ /**
48
+ * 渲染看板屏幕
49
+ * @param {PDDDataProvider} provider - 数据提供者
50
+ * @param {Renderer} renderer - 渲染器
51
+ * @param {number} selectedIndex - 选中的功能点索引 (-1=无选中)
52
+ * @param {Array} filteredFeatures - 过滤后的功能点列表
53
+ * @returns {string} 完整的屏幕内容
54
+ */
55
+ static render(provider, renderer, selectedIndex = -1, filteredFeatures = null) {
56
+ const r = renderer;
57
+ const w = r.width;
58
+ const h = r.height;
59
+
60
+ // 获取功能点列表
61
+ const allFeatures = provider.getFeatures() || [];
62
+ const displayFeatures = filteredFeatures || allFeatures;
63
+
64
+ // 按阶段分组
65
+ const grouped = _groupByStage(displayFeatures);
66
+
67
+ // 计算布局参数
68
+ const layout = _calcLayout(w, KANBAN_COLUMNS.length);
69
+
70
+ let output = '';
71
+
72
+ // ========== 1. 标题栏 ==========
73
+ output += _renderHeader(r, displayFeatures.length, allFeatures.length, w);
74
+ output += '\n\n';
75
+
76
+ // ========== 2. 看板列 ==========
77
+ output += _renderColumns(r, grouped, selectedIndex, layout, displayFeatures);
78
+ output += '\n';
79
+
80
+ // ========== 3. 底部操作提示 ==========
81
+ output += _renderFooter(r, selectedIndex >= 0, w);
82
+
83
+ return output;
84
+ }
85
+
86
+ /**
87
+ * 渲染 Feature Detail Overlay (VM-C024)
88
+ * @param {Feature} feature - 选中的功能点
89
+ * @param {Renderer} renderer - 渲染器
90
+ * @param {number} index - 当前索引
91
+ * @param {number} total - 总数
92
+ * @returns {string} 详情覆盖层内容
93
+ */
94
+ static renderDetail(feature, renderer, index = 0, total = 1) {
95
+ const r = renderer;
96
+ const w = r.width;
97
+ const h = r.height;
98
+
99
+ if (!feature) {
100
+ return r.red('错误: 功能点数据不可用');
101
+ }
102
+
103
+ const overlayW = Math.min(w - 4, 70);
104
+ const overlayH = Math.min(h - 4, 20);
105
+
106
+ let output = '';
107
+
108
+ // 覆盖层边框
109
+ const lines = [];
110
+ const B = { t: '\u255a', tr: '\u2557', bl: '\u255d', br: '\u2559', h: '\u2550', v: '\u2551', lt: '\u2560', rt: '\u2563', bt: '\u2566', bb: '\u2569' };
111
+
112
+ // 顶部标题
113
+ const title = ' Feature Detail: ' + (feature.name || feature.id) + ' ';
114
+ lines.push(B.t + B.h.repeat(overlayW - 2) + B.tr);
115
+ lines.push(B.v + ' ' + r.bold(r.cyan(title.padEnd(overlayW - 4))) + ' ' + B.v);
116
+
117
+ // 分隔线
118
+ lines.push(B.lt + B.h.repeat(overlayW - 2) + B.rt);
119
+
120
+ // 基本信息
121
+ const infoRows = [
122
+ ['ID', feature.id || '(none)'],
123
+ ['Name', feature.name || '(unnamed)'],
124
+ ['Description', feature.description || '-'],
125
+ ['Stage', _formatStage(feature.stage)],
126
+ ['Priority', _formatPriority(feature.priority)],
127
+ ['Progress', (feature.progress ? feature.progress() : _calcProgress(feature.stage)) + '%'],
128
+ ['Tags', (feature.tags || []).join(', ') || '-'],
129
+ ['Created', feature.createdAt ? new Date(feature.createdAt).toLocaleString() : '-'],
130
+ ['Updated', feature.updatedAt ? new Date(feature.updatedAt).toLocaleString() : '-']
131
+ ];
132
+
133
+ for (const [label, value] of infoRows) {
134
+ const line = B.v + ' ' + r.bold(label.padEnd(12)) + ' ' + r.truncate(String(value), overlayW - 20) + ' ';
135
+ lines.push(line.padEnd(overlayW - 1) + B.v);
136
+ }
137
+
138
+ // 分隔线
139
+ lines.push(B.lt + B.h.repeat(overlayW - 2) + B.rt);
140
+
141
+ // 质量信息
142
+ if (feature.quality) {
143
+ const q = feature.quality;
144
+ lines.push(B.v + ' ' + r.bold('Quality') + ' '.repeat(28) + B.v);
145
+ lines.push((B.v + ' Score: ' + q.score + ' (' + q.grade + ') Coverage: ' + q.coverage + '% Pass: ' + q.passRate + '%').padEnd(overlayW - 2) + B.v);
146
+ }
147
+
148
+ // Token 信息
149
+ if (feature.tokens) {
150
+ const t = feature.tokens;
151
+ lines.push(B.v + ' ' + r.bold('Tokens') + ' '.repeat(28) + B.v);
152
+ lines.push((B.v + ' Used: ' + t.used.toLocaleString() + ' / Total: ' + t.total.toLocaleString()).padEnd(overlayW - 2) + B.v);
153
+ }
154
+
155
+ // 迭代信息
156
+ if (feature.iterations && feature.iterations.length > 0) {
157
+ lines.push(B.lt + B.h.repeat(overlayW - 2) + B.rt);
158
+ lines.push((B.v + ' ' + r.bold('Iterations (' + feature.iterations.length + ')')).padEnd(overlayW - 2) + B.v);
159
+ const recentIters = feature.iterations.slice(-3);
160
+ for (const iter of recentIters) {
161
+ lines.push((B.v + ' #' + iter.round + ': score=' + iter.score + ', fixed=' + iter.issuesFixed + ', tokens=' + iter.tokenUsed).padEnd(overlayW - 2) + B.v);
162
+ }
163
+ }
164
+
165
+ // 制品信息
166
+ if (feature.artifacts && feature.artifacts.length > 0) {
167
+ lines.push(B.lt + B.h.repeat(overlayW - 2) + B.rt);
168
+ lines.push((B.v + ' ' + r.bold('Artifacts (' + feature.artifacts.length + ')')).padEnd(overlayW - 2) + B.v);
169
+ for (const art of feature.artifacts.slice(0, 3)) {
170
+ lines.push((B.v + ' [' + art.type + '] ' + art.path).padEnd(overlayW - 2) + B.v);
171
+ }
172
+ }
173
+
174
+ // 底部导航提示
175
+ lines.push(B.lt + B.h.repeat(overlayW - 2) + B.rt);
176
+ const navHint = '[' + (index + 1) + '/' + total + '] j/k: navigate Esc/q/E: close';
177
+ lines.push(B.v + ' ' + r.dim(navHint.padEnd(overlayW - 4)) + ' ' + B.v);
178
+ lines.push(B.bl + B.h.repeat(overlayW - 2) + B.br);
179
+
180
+ output = lines.join('\n');
181
+
182
+ return output;
183
+ }
184
+ }
185
+
186
+ // ============================================================
187
+ // 私有渲染函数
188
+ // ============================================================
189
+
190
+ /**
191
+ * 渲染标题栏
192
+ */
193
+ function _renderHeader(r, filteredCount, totalCount, w) {
194
+ const searchHint = filteredCount !== totalCount
195
+ ? r.yellow(` (filtered: ${filteredCount}/${totalCount})`)
196
+ : '';
197
+
198
+ const header = r.bold(r.brightCyan('▦ Kanban Board')) +
199
+ r.dim(searchHint);
200
+
201
+ return header;
202
+ }
203
+
204
+ /**
205
+ * 计算看板布局参数
206
+ */
207
+ function _calcLayout(totalWidth, numCols) {
208
+ // 可用宽度 = 总宽 - 列间间距
209
+ const availableWidth = totalWidth - (numCols - 1) * COL_SPACING;
210
+ const colWidth = Math.max(CARD_INNER_W + 4, Math.floor(availableWidth / numCols)); // +4 for borders and padding
211
+ const cardInnerWidth = colWidth - 4; // 减去两边边框和内边距
212
+
213
+ return {
214
+ colWidth,
215
+ cardInnerWidth: Math.max(6, cardInnerWidth),
216
+ numCols
217
+ };
218
+ }
219
+
220
+ /**
221
+ * 渲染所有看板列
222
+ */
223
+ function _renderColumns(r, grouped, selectedIndex, layout, flatList) {
224
+ const { colWidth, cardInnerWidth, numCols } = layout;
225
+ const cols = KANBAN_COLUMNS;
226
+
227
+ // 找出最高的列
228
+ let maxRows = 0;
229
+ for (const col of cols) {
230
+ const items = grouped[col.key] || [];
231
+ const rowsNeeded = items.length * (CARD_HEIGHT + 1); // 卡片高度 + 间距
232
+ if (rowsNeeded > maxRows) maxRows = rowsNeeded;
233
+ }
234
+ maxRows = Math.max(maxRows, 3); // 最少3行
235
+
236
+ // 为每列构建内容数组
237
+ const columnContents = [];
238
+ let globalIndex = 0;
239
+
240
+ for (let ci = 0; ci < cols.length; ci++) {
241
+ const col = cols[ci];
242
+ const items = grouped[col.key] || [];
243
+ const colLines = [];
244
+
245
+ // 列标题
246
+ const countTag = `(${items.length})`;
247
+ const header = `${col.color}${col.label}${ANSI.RESET} ${countTag}`;
248
+ colLines.push(header);
249
+
250
+ // 列分隔线
251
+ colLines.push('─'.repeat(colWidth));
252
+
253
+ // 功能点卡片
254
+ for (let ii = 0; ii < items.length; ii++) {
255
+ const feat = items[ii];
256
+ const isSelected = globalIndex === selectedIndex;
257
+ const cardLines = _renderFeatureCard(r, feat, cardInnerWidth, isSelected);
258
+ colLines.push(...cardLines);
259
+ globalIndex++;
260
+
261
+ // 卡片间距
262
+ if (ii < items.length - 1) {
263
+ colLines.push('');
264
+ }
265
+ }
266
+
267
+ // 空列占位
268
+ if (items.length === 0) {
269
+ colLines.push(r.dim(' (empty)'));
270
+ }
271
+
272
+ columnContents.push(colLines);
273
+ }
274
+
275
+ // 合并所有列为最终输出
276
+ return _mergeColumns(r, columnContents, colWidth, COL_SPACING);
277
+ }
278
+
279
+ /**
280
+ * 渲染单张功能点卡片
281
+ */
282
+ function _renderFeatureCard(r, feature, innerWidth, selected) {
283
+ const name = r.truncate(feature.name || feature.id || '?', innerWidth);
284
+ const priority = feature.priority || 'P2';
285
+ const hasQuality = feature.quality && feature.quality.grade;
286
+ const gradeChar = hasQuality ? feature.quality.grade : '';
287
+ const converged = typeof feature.isConverged === 'function' ? feature.isConverged() : false;
288
+ const statusMark = feature.stage === 'done' ? '✓' : (converged ? '~' : '');
289
+
290
+ const line1 = selected
291
+ ? `${ANSI.REVERSE}${name}${ANSI.RESET}`
292
+ : name;
293
+ const line2 = ` ${priority} ${gradeChar}${statusMark}`;
294
+
295
+ // 边框盒子
296
+ const border = selected ? ANSI.REVERSE : '';
297
+ const lines = [
298
+ `${border}┌${'─'.repeat(innerWidth)}┐${ANSI.RESET}`,
299
+ `${border}│${r.pad(line1, innerWidth)}│${ANSI.RESET}`,
300
+ `${border}│${r.pad(line2, innerWidth)}│${ANSI.RESET}`,
301
+ `${border}└${'─'.repeat(innerWidth)}┘${ANSI.RESET}`
302
+ ];
303
+
304
+ return lines;
305
+ }
306
+
307
+ /**
308
+ * 合并多列为并排输出
309
+ */
310
+ function _mergeColumns(r, columns, colWidth, spacing) {
311
+ const maxRows = Math.max(...columns.map(col => col.length));
312
+ const result = [];
313
+
314
+ for (let row = 0; row < maxRows; row++) {
315
+ const parts = [];
316
+ for (let c = 0; c < columns.length; c++) {
317
+ const line = row < columns[c].length ? columns[c][row] : '';
318
+ parts.push(r.pad(line, colWidth));
319
+ }
320
+ result.push(parts.join(' '.repeat(spacing)));
321
+ }
322
+
323
+ return result.join('\n');
324
+ }
325
+
326
+ /**
327
+ * 渲染底部操作提示
328
+ */
329
+ function _renderFooter(r, hasSelection, w) {
330
+ const hints = [];
331
+ hints.push('j/k or ↑↓: select');
332
+ if (hasSelection) hints.push('Enter: detail');
333
+ hints.push('/: search');
334
+ hints.push('Esc: back');
335
+
336
+ return r.separator('─', w) + '\n' + r.dim(' ' + hints.join(' | '));
337
+ }
338
+
339
+ // ============================================================
340
+ // 数据处理工具函数
341
+ // ============================================================
342
+
343
+ /**
344
+ * 按阶段对功能点分组
345
+ */
346
+ function _groupByStage(features) {
347
+ const groups = {};
348
+ for (const stage of STAGE_VALUES) {
349
+ groups[stage] = [];
350
+ }
351
+ for (const f of features) {
352
+ const stage = f.stage || StageEnum.PRD;
353
+ if (!groups[stage]) groups[stage] = [];
354
+ groups[stage].push(f);
355
+ }
356
+ return groups;
357
+ }
358
+
359
+ /**
360
+ * 格式化阶段显示
361
+ */
362
+ function _formatStage(stage) {
363
+ const icons = {
364
+ prd: '📋 PRD',
365
+ extracted: '🔍 Extracted',
366
+ spec: '📝 Spec',
367
+ implementing: '🔨 Implementing',
368
+ verifying: '✅ Verifying',
369
+ done: '✨ Done'
370
+ };
371
+ return icons[stage] || stage;
372
+ }
373
+
374
+ /**
375
+ * 格式化优先级
376
+ */
377
+ function _formatPriority(priority) {
378
+ const colors = {
379
+ P0: ANSI.BRIGHT_RED,
380
+ P1: ANSI.YELLOW,
381
+ P2: ANSI.DIM,
382
+ P3: ANSI.DIM
383
+ };
384
+ const color = colors[priority] || ANSI.DIM;
385
+ return `${color}${priority}${ANSI.RESET}`;
386
+ }
387
+
388
+ /**
389
+ * 计算进度百分比
390
+ */
391
+ function _calcProgress(stage) {
392
+ const order = STAGE_ORDER[stage];
393
+ if (order === undefined) return 0;
394
+ return Math.round((order / (Object.keys(STAGE_ORDER).length - 1)) * 100);
395
+ }
396
+
397
+ export default KanbanScreen;