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,655 @@
1
+ /**
2
+ * PDD Plugin Manager - 插件生命周期管理器
3
+ *
4
+ * 负责插件的发现、加载、激活、停用、卸载等全生命周期管理。
5
+ * 提供依赖解析、事件系统、状态机等核心能力。
6
+ * 一个插件失败不影响其他插件的运行。
7
+ *
8
+ * @module plugin-manager
9
+ * @author PDD Team
10
+ * @version 1.0.0
11
+ * @license MIT
12
+ */
13
+
14
+ import { readFile, readdir, stat, access } from 'node:fs/promises';
15
+ import { join, extname, basename, dirname } from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+ import { EventEmitter } from 'node:events';
18
+
19
+ import {
20
+ PluginBase,
21
+ PluginContext,
22
+ PluginManifest,
23
+ PluginStatus,
24
+ createPlugin,
25
+ matchVersionRange,
26
+ } from './plugin-sdk.js';
27
+
28
+ // ==================== 事件常量 ====================
29
+
30
+ /**
31
+ * 插件管理器事件类型
32
+ * @enum {string}
33
+ */
34
+ export const PluginEvents = {
35
+ /** 插件加载完成 */
36
+ LOADED: 'plugin-loaded',
37
+ /** 插件激活完成 */
38
+ ACTIVATED: 'plugin-activated',
39
+ /** 插件停用完成 */
40
+ DEACTIVATED: 'plugin-deactivated',
41
+ /** 插件卸载完成 */
42
+ UNINSTALLED: 'plugin-uninstalled',
43
+ /** 插件出错 */
44
+ ERROR: 'plugin-error',
45
+ /** 插件发现完成(批量扫描结束时触发) */
46
+ DISCOVERED: 'plugins-discovered',
47
+ };
48
+
49
+ // ==================== PluginManager 主类 ====================
50
+
51
+ /**
52
+ * 插件生命周期管理器
53
+ *
54
+ * 管理所有已安装插件的状态转换和资源分配。
55
+ * 实现插件隔离:每个插件拥有独立的上下文和作用域。
56
+ *
57
+ * 状态机流转:
58
+ * installed → loaded → activated → deactivated → uninstalled
59
+ * ↘ ↗
60
+ * error (任意阶段)
61
+ *
62
+ * @class PluginManager
63
+ * @extends EventEmitter
64
+ *
65
+ * @example
66
+ * import { PluginManager } from './plugin-manager.js';
67
+ *
68
+ * const manager = new PluginManager({ pluginsDir: './plugins' });
69
+ * await manager.discover();
70
+ * await manager.activate('hello-world');
71
+ */
72
+ export class PluginManager extends EventEmitter {
73
+ /**
74
+ * 创建插件管理器实例
75
+ * @param {Object} options - 管理器配置选项
76
+ * @param {string} options.pluginsDir - 插件根目录路径
77
+ * @param {Object} [options.globalConfig={}] - 全局配置对象
78
+ * @param {Object} [options.logger] - 日志记录器
79
+ * @param {Object} [options.api={}] - PDD 核心 API
80
+ * @param {string} [options.pddVersion='1.0.0'] - 当前 PDD 版本号
81
+ * @param {boolean} [options.autoActivate=true] - 加载后是否自动激活
82
+ */
83
+ constructor({
84
+ pluginsDir,
85
+ globalConfig = {},
86
+ logger = null,
87
+ api = {},
88
+ pddVersion = '1.0.0',
89
+ autoActivate = true,
90
+ }) {
91
+ super();
92
+
93
+ if (!pluginsDir) {
94
+ throw new Error('PluginManager 需要指定 pluginsDir 参数');
95
+ }
96
+
97
+ /** @type {string} 插件根目录 */
98
+ this.pluginsDir = pluginsDir;
99
+
100
+ /** @type {string} 当前 PDD 版本 */
101
+ this.pddVersion = pddVersion;
102
+
103
+ /** @type {boolean} 是否自动激活 */
104
+ this.autoActivate = autoActivate;
105
+
106
+ /** @type {Map<string, Object>} 插件注册表 { name → { instance, manifest, path, status, context } } */
107
+ this._registry = new Map();
108
+
109
+ /** @type {Map<string, string[]>} 依赖图 { name → [depNames] } */
110
+ this._dependencyGraph = new Map();
111
+
112
+ /** @type {Object} 全局配置 */
113
+ this._globalConfig = globalConfig;
114
+
115
+ /** @type {Object|null} 日志记录器 */
116
+ this._logger = logger || this._createDefaultLogger();
117
+
118
+ /** @type {Object} PDD 核心 API */
119
+ this._api = api;
120
+
121
+ /** @type {Set<string>} 正在处理的插件名称集合(防止循环) */
122
+ this._processing = new Set();
123
+ }
124
+
125
+ /**
126
+ * 创建默认日志记录器
127
+ * @returns {Object}
128
+ * @private
129
+ */
130
+ _createDefaultLogger() {
131
+ return {
132
+ info: (...args) => console.log(`[PluginManager] [INFO]`, ...args),
133
+ warn: (...args) => console.warn(`[PluginManager] [WARN]`, ...args),
134
+ error: (...args) => console.error(`[PluginManager] [ERROR]`, ...args),
135
+ debug: (...args) => process.env.DEBUG && console.log(`[PluginManager] [DEBUG]`, ...args),
136
+ };
137
+ }
138
+
139
+ // ==================== 插件发现 ====================
140
+
141
+ /**
142
+ * 扫描插件目录,发现所有可用插件
143
+ * 查找每个子目录中的 plugin.json 清单文件
144
+ *
145
+ * @returns {Promise<string[]>} 发现的插件名称列表
146
+ */
147
+ async discover() {
148
+ this._logger.info(`开始扫描插件目录: ${this.pluginsDir}`);
149
+
150
+ let entries;
151
+ try {
152
+ entries = await readdir(this.pluginsDir, { withFileTypes: true });
153
+ } catch (err) {
154
+ this._logger.error(`无法读取插件目录: ${err.message}`);
155
+ throw new Error(`插件目录不存在或不可读: ${this.pluginsDir}`);
156
+ }
157
+
158
+ const discovered = [];
159
+
160
+ for (const entry of entries) {
161
+ if (!entry.isDirectory()) continue;
162
+
163
+ const pluginPath = join(this.pluginsDir, entry.name);
164
+ const manifestPath = join(pluginPath, 'plugin.json');
165
+
166
+ try {
167
+ // 检查 plugin.json 是否存在
168
+ await access(manifestPath);
169
+ discovered.push(entry.name);
170
+ this._logger.debug(`发现插件: ${entry.name}`);
171
+ } catch {
172
+ // 没有 plugin.json 的目录跳过
173
+ this._logger.debug(`跳过非插件目录: ${entry.name}`);
174
+ }
175
+ }
176
+
177
+ this._logger.info(`扫描完成,发现 ${discovered.length} 个插件`);
178
+ this.emit(PluginEvents.DISCOVERED, { plugins: discovered });
179
+
180
+ return discovered;
181
+ }
182
+
183
+ // ==================== 插件加载 ====================
184
+
185
+ /**
186
+ * 加载单个插件
187
+ * 1. 读取并验证 plugin.json
188
+ * 2. 动态 import 入口文件
189
+ * 3. 创建插件实例
190
+ * 4. 注册到管理器
191
+ *
192
+ * @param {string} nameOrPath - 插件名称或绝对路径
193
+ * @returns {Promise<PluginBase>} 已加载的插件实例
194
+ * @throws {Error} 加载失败时抛出异常
195
+ */
196
+ async load(nameOrPath) {
197
+ // 判断是名称还是路径
198
+ let pluginPath;
199
+ let pluginName;
200
+
201
+ if (nameOrPath.includes('/') || nameOrPath.includes('\\')) {
202
+ pluginPath = nameOrPath;
203
+ pluginName = basename(nameOrPath);
204
+ } else {
205
+ pluginName = nameOrPath;
206
+ pluginPath = join(this.pluginsDir, pluginName);
207
+ }
208
+
209
+ // 防止重复加载
210
+ if (this._registry.has(pluginName)) {
211
+ const existing = this._registry.get(pluginName);
212
+ if (existing.status === PluginStatus.LOADED || existing.status === PluginStatus.ACTIVATED) {
213
+ this._logger.warn(`插件 "${pluginName}" 已加载`);
214
+ return existing.instance;
215
+ }
216
+ }
217
+
218
+ this._processing.add(pluginName);
219
+
220
+ try {
221
+ // 步骤 1: 读取清单
222
+ const manifestPath = join(pluginPath, 'plugin.json');
223
+ const manifest = await PluginManifest.load(manifestPath);
224
+ const validation = manifest.validate();
225
+
226
+ if (!validation.valid) {
227
+ throw new Error(`插件清单验证失败:\n ${validation.errors.join('\n ')}`);
228
+ }
229
+ if (validation.warnings.length > 0) {
230
+ validation.warnings.forEach(w => this._logger.warn(`[${pluginName}] ${w}`));
231
+ }
232
+
233
+ // 步骤 2: 版本兼容性检查
234
+ if (!matchVersionRange(manifest.raw.pdd, this.pddVersion)) {
235
+ throw new Error(
236
+ `PDD 版本不兼容: 需要 ${manifest.raw.pdd},当前 ${this.pddVersion}`
237
+ );
238
+ }
239
+
240
+ // 步骤 3: 动态导入入口文件
241
+ const entryFile = join(pluginPath, manifest.raw.main);
242
+ const module = await import(entryFile);
243
+
244
+ // 获取默认导出或 named export
245
+ const PluginClass = module.default || module;
246
+ if (typeof PluginClass !== 'function') {
247
+ throw new Error(
248
+ `插件入口文件 "${manifest.raw.main}" 必须导出一个继承自 PluginBase 的类`
249
+ );
250
+ }
251
+
252
+ // 步骤 4: 创建实例
253
+ const instance = createPlugin(PluginClass);
254
+
255
+ // 步骤 5: 创建上下文
256
+ const context = new PluginContext({
257
+ config: { ...this._globalConfig, ...(manifest.raw.config || {}) },
258
+ logger: this._logger,
259
+ cache: new Map(),
260
+ api: this._api,
261
+ pluginDir: pluginPath,
262
+ });
263
+
264
+ // 步骤 6: 注册到管理器
265
+ this._registry.set(pluginName, {
266
+ instance,
267
+ manifest: manifest.raw,
268
+ path: pluginPath,
269
+ status: PluginStatus.LOADED,
270
+ context,
271
+ loadedAt: Date.now(),
272
+ });
273
+
274
+ // 构建依赖图
275
+ if (manifest.raw.dependencies || manifest.raw.peerDependencies) {
276
+ const deps = [
277
+ ...(Object.keys(manifest.raw.dependencies || {})),
278
+ ...(Object.keys(manifest.raw.peerDependencies || {})),
279
+ ];
280
+ this._dependencyGraph.set(pluginName, deps);
281
+ }
282
+
283
+ this._logger.info(`插件 "${pluginName}" v${instance.version} 加载成功`);
284
+
285
+ // 触发 onInstall 生命周期
286
+ try {
287
+ await instance.onInstall(context);
288
+ } catch (err) {
289
+ this._logger.warn(`[${pluginName}] onInstall 回调失败: ${err.message}`);
290
+ }
291
+
292
+ this.emit(PluginEvents.LOADED, { name: pluginName, instance });
293
+
294
+ // 自动激活
295
+ if (this.autoActivate) {
296
+ await this.activate(pluginName);
297
+ }
298
+
299
+ return instance;
300
+ } catch (err) {
301
+ this._logger.error(`加载插件 "${pluginName}" 失败: ${err.message}`);
302
+
303
+ // 记录错误状态
304
+ this._registry.set(pluginName, {
305
+ instance: null,
306
+ manifest: null,
307
+ path: pluginPath,
308
+ status: PluginStatus.ERROR,
309
+ context: null,
310
+ error: err.message,
311
+ });
312
+
313
+ this.emit(PluginEvents.ERROR, { name: pluginName, error: err.message });
314
+ throw err;
315
+ } finally {
316
+ this._processing.delete(pluginName);
317
+ }
318
+ }
319
+
320
+ // ==================== 批量加载 ====================
321
+
322
+ /**
323
+ * 加载所有已发现的插件
324
+ * 自动解析依赖顺序后按序加载
325
+ *
326
+ * @returns {Promise<PluginBase[]>} 所有成功加载的插件实例数组
327
+ */
328
+ async loadAll() {
329
+ const discovered = await this.discover();
330
+ const loadOrder = this.resolveDependencies(discovered);
331
+
332
+ this._logger.info(`按依赖顺序加载 ${loadOrder.length} 个插件`);
333
+
334
+ const results = [];
335
+ for (const name of loadOrder) {
336
+ try {
337
+ const instance = await this.load(name);
338
+ results.push(instance);
339
+ } catch (err) {
340
+ this._logger.error(`加载 "${name}" 失败(继续加载其他插件): ${err.message}`);
341
+ // 单个插件失败不影响其他插件
342
+ }
343
+ }
344
+
345
+ return results;
346
+ }
347
+
348
+ // ==================== 激活 / 停用 ====================
349
+
350
+ /**
351
+ * 激活插件
352
+ * 调用插件的 onActivate 方法,使其进入可用状态
353
+ *
354
+ * @param {string} name - 插件名称
355
+ * @returns {Promise<void>}
356
+ * @throws {Error} 如果插件未加载或激活失败
357
+ */
358
+ async activate(name) {
359
+ const entry = this._registry.get(name);
360
+ if (!entry) {
361
+ throw new Error(`插件 "${name}" 未注册,请先调用 load()`);
362
+ }
363
+ if (entry.status === PluginStatus.ACTIVATED) {
364
+ this._logger.warn(`插件 "${name}" 已经处于激活状态`);
365
+ return;
366
+ }
367
+ if (entry.status === PluginStatus.ERROR) {
368
+ throw new Error(`插件 "${name}" 处于错误状态,无法激活: ${entry.error}`);
369
+ }
370
+
371
+ try {
372
+ this._processing.add(name);
373
+
374
+ // 先激活依赖项
375
+ const deps = this._dependencyGraph.get(name) || [];
376
+ for (const dep of deps) {
377
+ if (this._registry.has(dep)) {
378
+ const depEntry = this._registry.get(dep);
379
+ if (depEntry.status !== PluginStatus.ACTIVATED) {
380
+ await this.activate(dep);
381
+ }
382
+ }
383
+ }
384
+
385
+ entry.instance._setStatus(PluginStatus.ACTIVATED);
386
+ await entry.instance.onActivate(entry.context);
387
+
388
+ entry.status = PluginStatus.ACTIVATED;
389
+ entry.activatedAt = Date.now();
390
+
391
+ this._logger.info(`插件 "${name}" 激活成功`);
392
+ this.emit(PluginEvents.ACTIVATED, { name, instance: entry.instance });
393
+ } catch (err) {
394
+ entry.status = PluginStatus.ERROR;
395
+ entry.error = err.message;
396
+ entry.instance._setStatus(PluginStatus.ERROR);
397
+
398
+ this._logger.error(`激活插件 "${name}" 失败: ${err.message}`);
399
+ this.emit(PluginEvents.ERROR, { name, error: err.message });
400
+ throw err;
401
+ } finally {
402
+ this._processing.delete(name);
403
+ }
404
+ }
405
+
406
+ /**
407
+ * 停用插件
408
+ * 调用插件的 onDeactivate 方法,释放其占用的资源
409
+ *
410
+ * @param {string} name - 插件名称
411
+ * @returns {Promise<void>}
412
+ * @throws {Error} 如果插件未激活
413
+ */
414
+ async deactivate(name) {
415
+ const entry = this._registry.get(name);
416
+ if (!entry) {
417
+ throw new Error(`插件 "${name}" 不存在`);
418
+ }
419
+ if (entry.status !== PluginStatus.ACTIVATED) {
420
+ throw new Error(`插件 "${name}" 当前未激活(状态: ${entry.status})`);
421
+ }
422
+
423
+ try {
424
+ await entry.instance.onDeactivate(entry.context);
425
+
426
+ entry.status = PluginStatus.DEACTIVATED;
427
+ entry.instance._setStatus(PluginStatus.DEACTIVATED);
428
+
429
+ this._logger.info(`插件 "${name}" 已停用`);
430
+ this.emit(PluginEvents.DEACTIVATED, { name });
431
+ } catch (err) {
432
+ this._logger.error(`停用插件 "${name}" 时出错: ${err.message}`);
433
+ this.emit(PluginEvents.ERROR, { name, error: err.message });
434
+ throw err;
435
+ }
436
+ }
437
+
438
+ /**
439
+ * 卸载插件
440
+ * 先停用,再调用 onUninstall,最后从注册表移除
441
+ *
442
+ * @param {string} name - 插件名称
443
+ * @returns {Promise<void>}
444
+ */
445
+ async unload(name) {
446
+ const entry = this._registry.get(name);
447
+ if (!entry) {
448
+ this._logger.warn(`插件 "${name}" 未注册,无需卸载`);
449
+ return;
450
+ }
451
+
452
+ try {
453
+ // 如果处于激活状态,先停用
454
+ if (entry.status === PluginStatus.ACTIVATED) {
455
+ await this.deactivate(name);
456
+ // 重新获取(deactivate 后引用可能变化)
457
+ const refreshed = this._registry.get(name);
458
+ if (refreshed?.instance) {
459
+ await refreshed.instance.onUninstall(refreshed.context);
460
+ }
461
+ } else if (entry.instance) {
462
+ await entry.instance.onUninstall(entry.context);
463
+ }
464
+
465
+ this._registry.delete(name);
466
+ this._dependencyGraph.delete(name);
467
+
468
+ this._logger.info(`插件 "${name}" 已卸载`);
469
+ this.emit(PluginEvents.UNINSTALLED, { name });
470
+ } catch (err) {
471
+ this._logger.error(`卸载插件 "${name}" 时出错: ${err.message}`);
472
+ this.emit(PluginEvents.ERROR, { name, error: err.message });
473
+ throw err;
474
+ }
475
+ }
476
+
477
+ // ==================== 查询 API ====================
478
+
479
+ /**
480
+ * 列出所有已注册的插件及其状态
481
+ * @returns {Array<{name: string, version: string, status: string, description: string}>}
482
+ */
483
+ listPlugins() {
484
+ const result = [];
485
+ for (const [name, entry] of this._registry) {
486
+ result.push({
487
+ name,
488
+ version: entry.instance?.version ?? 'unknown',
489
+ status: entry.status,
490
+ description: entry.instance?.description ?? '',
491
+ path: entry.path,
492
+ loadedAt: entry.loadedAt,
493
+ activatedAt: entry.activatedAt,
494
+ });
495
+ }
496
+ return result;
497
+ }
498
+
499
+ /**
500
+ * 根据名称获取插件实例
501
+ * @param {string} name - 插件名称
502
+ * @returns {PluginBase|undefined}
503
+ */
504
+ getPlugin(name) {
505
+ const entry = this._registry.get(name);
506
+ return entry?.instance;
507
+ }
508
+
509
+ /**
510
+ * 获取插件上下文
511
+ * @param {string} name - 插件名称
512
+ * @returns {PluginContext|undefined}
513
+ */
514
+ getContext(name) {
515
+ const entry = this._registry.get(name);
516
+ return entry?.context;
517
+ }
518
+
519
+ /**
520
+ * 获取所有已注册的命令(跨插件聚合)
521
+ * @returns {Map<string, Object>} { commandName → { handler, options, pluginName } }
522
+ */
523
+ getAllCommands() {
524
+ const allCommands = new Map();
525
+ for (const [, entry] of this._registry) {
526
+ if (entry.instance && entry.status === PluginStatus.ACTIVATED) {
527
+ const commands = entry.instance.getCommands();
528
+ for (const [cmdName, cmdDef] of commands) {
529
+ allCommands.set(cmdName, cmdDef);
530
+ }
531
+ }
532
+ }
533
+ return allCommands;
534
+ }
535
+
536
+ /**
537
+ * 获取所有已注册的钩子(跨插件聚合)
538
+ * @returns {Map<string, Object>} { hookKey → { name, handler, priority, pluginName } }
539
+ */
540
+ getAllHooks() {
541
+ const allHooks = new Map();
542
+ for (const [, entry] of this._registry) {
543
+ if (entry.instance && entry.status === PluginStatus.ACTIVATED) {
544
+ const hooks = entry.instance.getHooks();
545
+ for (const [hookKey, hookDef] of hooks) {
546
+ allHooks.set(hookKey, hookDef);
547
+ }
548
+ }
549
+ }
550
+ return allHooks;
551
+ }
552
+
553
+ // ==================== 依赖解析 ====================
554
+
555
+ /**
556
+ * 解析插件依赖顺序(拓扑排序)
557
+ * 确保被依赖的插件优先加载/激活
558
+ *
559
+ * @param {string[]} pluginNames - 待排序的插件名称列表
560
+ * @returns {string[]} 按依赖顺序排列的插件名称列表
561
+ * @throws {Error} 存在循环依赖时抛出异常
562
+ */
563
+ resolveDependencies(pluginNames) {
564
+ const visited = new Set();
565
+ const visiting = new Set(); // 用于检测环
566
+ const order = [];
567
+
568
+ /**
569
+ * 深度优先遍历
570
+ * @param {string} name
571
+ */
572
+ const visit = (name) => {
573
+ if (visited.has(name)) return;
574
+ if (visiting.has(name)) {
575
+ throw new Error(`检测到循环依赖,涉及插件: ${name}`);
576
+ }
577
+
578
+ visiting.add(name);
579
+
580
+ const deps = this._dependencyGraph.get(name) || [];
581
+ for (const dep of deps) {
582
+ // 只处理在当前待排序列表中的依赖
583
+ if (pluginNames.includes(dep)) {
584
+ visit(dep);
585
+ }
586
+ }
587
+
588
+ visiting.delete(name);
589
+ visited.add(name);
590
+ order.push(name);
591
+ };
592
+
593
+ for (const name of pluginNames) {
594
+ visit(name);
595
+ }
596
+
597
+ return order;
598
+ }
599
+
600
+ // ==================== 配置变更通知 ====================
601
+
602
+ /**
603
+ * 通知插件配置发生变更
604
+ * @param {string} name - 插件名称
605
+ * @param {Object} changedKeys - 变更的配置键值对
606
+ * @returns {Promise<void>}
607
+ */
608
+ async notifyConfigChange(name, changedKeys) {
609
+ const entry = this._registry.get(name);
610
+ if (!entry || !entry.instance || entry.status !== PluginStatus.ACTIVATED) {
611
+ return;
612
+ }
613
+
614
+ try {
615
+ // 更新上下文中的配置
616
+ Object.assign(entry.context.config, changedKeys);
617
+ await entry.instance.onConfigChange(entry.context, changedKeys);
618
+ this._logger.debug(`已通知插件 "${name}" 配置变更`);
619
+ } catch (err) {
620
+ this._logger.error(`通知插件 "${name}" 配置变更失败: ${err.message}`);
621
+ this.emit(PluginEvents.ERROR, { name, error: err.message });
622
+ }
623
+ }
624
+
625
+ // ==================== 销毁 ====================
626
+
627
+ /**
628
+ * 卸载所有插件并释放资源
629
+ * 应用于程序退出前的清理工作
630
+ *
631
+ * @returns {Promise<void>}
632
+ */
633
+ async destroy() {
634
+ this._logger.info('正在销毁插件管理器...');
635
+
636
+ const names = [...this._registry.keys()];
637
+ for (const name of names) {
638
+ try {
639
+ await this.unload(name);
640
+ } catch (err) {
641
+ this._logger.error(`卸载 "${name}" 时出错(忽略): ${err.message}`);
642
+ }
643
+ }
644
+
645
+ this._registry.clear();
646
+ this._dependencyGraph.clear();
647
+ this.removeAllListeners();
648
+
649
+ this._logger.info('插件管理器已销毁');
650
+ }
651
+ }
652
+
653
+ // ==================== 导出 ====================
654
+
655
+ export default PluginManager;