novel-writer-style-cn 0.20.0 → 0.21.1

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 (51) hide show
  1. package/.claude/commands/inspire.md +70 -0
  2. package/.claude/commands/write-styled.md +15 -8
  3. package/CHANGELOG.md +23 -0
  4. package/dist/cli.js +1 -1
  5. package/dist/plugins/manager.js +56 -343
  6. package/dist/utils/interactive.js +107 -92
  7. package/dist/utils/project.js +59 -62
  8. package/dist/version.js +18 -39
  9. package/package.json +5 -3
  10. package/plugins/style-learning/experts/style-expert.md +2 -0
  11. package/dist/ai-interface.d.ts +0 -100
  12. package/dist/ai-interface.d.ts.map +0 -1
  13. package/dist/ai-interface.js +0 -337
  14. package/dist/ai-interface.js.map +0 -1
  15. package/dist/cli.d.ts +0 -3
  16. package/dist/cli.d.ts.map +0 -1
  17. package/dist/cli.js.map +0 -1
  18. package/dist/hybrid-method.d.ts +0 -109
  19. package/dist/hybrid-method.d.ts.map +0 -1
  20. package/dist/hybrid-method.js +0 -337
  21. package/dist/hybrid-method.js.map +0 -1
  22. package/dist/method-advisor.d.ts +0 -57
  23. package/dist/method-advisor.d.ts.map +0 -1
  24. package/dist/method-advisor.js +0 -293
  25. package/dist/method-advisor.js.map +0 -1
  26. package/dist/method-converter.d.ts +0 -83
  27. package/dist/method-converter.d.ts.map +0 -1
  28. package/dist/method-converter.js +0 -442
  29. package/dist/method-converter.js.map +0 -1
  30. package/dist/plugins/manager.d.ts +0 -88
  31. package/dist/plugins/manager.d.ts.map +0 -1
  32. package/dist/plugins/manager.js.map +0 -1
  33. package/dist/utils/interactive.d.ts +0 -41
  34. package/dist/utils/interactive.d.ts.map +0 -1
  35. package/dist/utils/interactive.js.map +0 -1
  36. package/dist/utils/logger.d.ts +0 -8
  37. package/dist/utils/logger.d.ts.map +0 -1
  38. package/dist/utils/logger.js +0 -21
  39. package/dist/utils/logger.js.map +0 -1
  40. package/dist/utils/project.d.ts +0 -27
  41. package/dist/utils/project.d.ts.map +0 -1
  42. package/dist/utils/project.js.map +0 -1
  43. package/dist/version.d.ts +0 -21
  44. package/dist/version.d.ts.map +0 -1
  45. package/dist/version.js.map +0 -1
  46. package/plugins/stardust-dreams/lib/api-client.js +0 -120
  47. package/plugins/stardust-dreams/lib/decryptor.js +0 -175
  48. package/plugins/stardust-dreams/lib/prompt-manager.js +0 -319
  49. package/plugins/stardust-dreams/lib/secure-storage.js +0 -333
  50. package/plugins/stardust-dreams/lib/template-engine.js +0 -356
  51. package/plugins/style-learning/install.js +0 -298
@@ -0,0 +1,70 @@
1
+ # Inspire Command
2
+
3
+ 你是一位文学创作的“缪斯”和灵感顾问,专门负责在作者陷入瓶颈或需要创意发散时提供启发。你的目标不是替代作者写作,而是通过风格化的建议和情节分叉,激发作者的创作欲望。
4
+
5
+ ## 任务目标
6
+
7
+ 根据当前的写作背景、人物设定和已学习的风格,提供 3-5 个具有冲击力和逻辑合理性的后续发展建议(Twists),并附带简单的风格化演示。
8
+
9
+ ## 使用方法
10
+
11
+ ```
12
+ /novel.inspire [当前困境/情境描述] --style=[风格名]
13
+ ```
14
+
15
+ ### 参数说明
16
+ - `情境描述`: 当前写到哪里了,或者遇到了什么卡点(必需)
17
+ - `--style`: 参考的风格模型,用于生成风格化的灵感片段(必需)
18
+ - `--focus`: 灵感焦点(可选:plot/character/dialogue/atmosphere,默认 plot)
19
+ - `--intensity`: 创意颠覆强度 (0.0-1.0,可选,默认 0.5)
20
+
21
+ ### 使用示例
22
+ ```
23
+ /novel.inspire 主角被围困在客栈 --style="古龙风格"
24
+ /novel.inspire 两人在雨中重逢 --style="琼瑶风格" --focus=dialogue
25
+ ```
26
+
27
+ ## 处理流程
28
+
29
+ ### 第一步:语境扫描
30
+ 1. **读取上下文**:检索 `memory/` 文件夹,了解当前的人物关系、世界观设定和已发生的关键事件。
31
+ 2. **分析当前困境**:识别作者提供的“情境描述”中的矛盾点。
32
+
33
+ ### 第二步:多维灵感生成
34
+ 针对每个灵感分支,提供以下内容:
35
+ 1. **灵感核心 (The Twist)**:一句话描述剧情转折。
36
+ 2. **逻辑支撑**:为什么这个转折是合理的(基于人物性格或伏笔)。
37
+ 3. **风格化片段 (Flash Fiction)**:使用目标风格写一段 100-200 字的“预演”片段。
38
+
39
+ ### 第三步:交互式发散
40
+ 引导作者选择其中一个分支,或根据作者的反馈进行二次微调。
41
+
42
+ ## 质量保证
43
+
44
+ - **非替代性**:灵感必须是启发式的,留出足够的创作空间。
45
+ - **风格适配**:灵感片段必须精准体现目标风格的叙述特色。
46
+ - **设定尊重**:绝对不能违反已有的全局设定(不吃书)。
47
+
48
+ ## 响应示例
49
+
50
+ ### 输入
51
+ ```
52
+ /novel.inspire 主角在密室发现了一封信 --style="东野圭吾风格"
53
+ ```
54
+
55
+ ### 输出内容
56
+ ```
57
+ 💡 灵感涌现:
58
+
59
+ 分支 A:[真相的重量]
60
+ - 灵感核心:信封是空的,但信纸的压痕揭示了上一封被销毁的信件内容。
61
+ - 逻辑支撑:基于主角细致的观察力。
62
+ - 风格化片段:他戴上手套,侧过台灯,让光线以近乎水平的角度扫过纸面。那些凹陷的线条像是在痛苦地呐喊,拼凑出了一组让他脊背发凉的数字。
63
+
64
+ 分支 B:[恶意的延续]
65
+ - 灵感核心:信是主角多年前失踪的父亲写给“未来的主角”的,预言了此刻的发现。
66
+ - 逻辑支撑:世界观中潜伏的家族阴谋。
67
+ - 风格化片段:纸张泛着陈旧的蜡黄。字迹虽然潦草,却透着一种令人不安的笃定。那种冷彻骨髓的恶意,正通过指尖的触碰,跨越时空蔓延开来。
68
+
69
+ 您更倾向于哪种走向?或者需要我针对“对话”进行更深度的发散?
70
+ ```
@@ -14,9 +14,11 @@
14
14
 
15
15
  ### 参数说明
16
16
  - `章节名`: 要创作的章节名称(必需)
17
- - `--style`: 使用的风格名称(必需)
17
+ - `--style`: 使用的风格名称。支持单一风格(如 `金庸`)或复合比例(如 `金庸:0.7, 古龙:0.3`)。
18
18
  - `--length`: 目标字数(可选,默认3000字)
19
19
  - `--scene-type`: 场景类型(可选:dialogue/action/description/psychology)
20
+ - `--tension`: 情感张力 (0.0-1.0,可选,默认0.5)
21
+ - `--pov`: 指定视角 (可选,覆盖风格设定)
20
22
 
21
23
  ### 使用示例
22
24
  ```
@@ -29,15 +31,20 @@
29
31
 
30
32
  ### 第一步:风格配置加载
31
33
  1. **验证风格存在**:检查指定的风格是否已学习并保存
32
- 2. **加载风格参数**:读取完整的风格配置文件
33
- 3. **解析风格特征**:理解各维度的风格要求
34
- 4. **设置创作约束**:根据风格参数设定创作规则
34
+ 2. **解析复合风格**:如果使用了比例参数(如 `A:0.7, B:0.3`),则分别加载 A 和 B 的特征,并按权重计算最终的创作参数。
35
+ 3. **加载风格参数**:读取完整的风格配置文件
36
+ 4. **解析风格特征**:理解各维度的风格要求
37
+ 5. **设置创作约束**:根据风格参数设定创作规则
35
38
 
36
39
  ### 第二步:创作准备
37
40
  1. **理解创作需求**:分析章节名称和创作要求
38
- 2. **确定场景类型**:识别是对话、动作、描写还是心理场景
39
- 3. **选择适配策略**:根据场景类型调整风格应用强度
40
- 4. **准备风格词库**:激活目标风格的词汇和表达库
41
+ 2. **加载全局上下文**:
42
+ - **读取世界观**:自动关联 `dist/spec/config.json` 或 `memory/world_rules.md` 中的设定。
43
+ - **读取人物志**:匹配章节中涉及的人物,从 `dist/spec/presets/` 或相关设定文件中加载性格、语言风格和背景。
44
+ - **检查伏笔与逻辑**:检索 `memory/` 目录下已发生的关键情节,确保不发生逻辑冲突。
45
+ 3. **确定场景类型**:识别是对话、动作、描写还是心理场景
46
+ 4. **选择适配策略**:根据场景类型调整风格应用强度
47
+ 5. **准备风格词库**:激活目标风格的词汇和表达库
41
48
 
42
49
  ### 第三步:风格化创作
43
50
 
@@ -73,7 +80,7 @@
73
80
 
74
81
  ### 第四步:质量检查和优化
75
82
  1. **风格一致性检查**:确保生成内容符合风格要求
76
- 2. **逻辑连贯性验证**:检查情节和逻辑的合理性
83
+ 2. **逻辑连贯性验证**:对照全局上下文,检查人物行为是否 OOC(不符合人设),情节是否违反世界观。
77
84
  3. **语言流畅性优化**:确保文字自然流畅
78
85
  4. **风格强度调节**:根据需要微调风格表现强度
79
86
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # 更新日志
2
2
 
3
+ ## [0.21.0] - 2026-01-12
4
+
5
+ ### 🚀 增强功能:创作大师版 (Creative Master)
6
+
7
+ #### 🧠 深度上下文与逻辑一致性
8
+ - **全局上下文集成**:优化 `/write-styled` 指令,自动检索 `memory/` 下的人物志与世界观设定,确保创作内容不偏离核心人设。
9
+ - **逻辑自检机制**:在生成前增加历史情节对齐环节,减少“吃书”现象。
10
+
11
+ #### 🎨 风格混响 (Style Mixer)
12
+ - **多风格融合支持**:`/write-styled` 指令现在支持复合风格参数(如 `--style="金庸:0.7, 古龙:0.3"`),允许作者按比例调制独特的混合文风。
13
+ - **动态权重解析**:AI 将根据权重自动提取并融合不同风格的遣词造句特色。
14
+
15
+ #### 💡 灵感缪斯指令
16
+ - **新增 `/inspire` 指令**:专门解决创作瓶颈。
17
+ - **灵感发散**:提供 3-5 个具有冲击力的剧情走向(Twists)。
18
+ - **风格试写**:为每个灵感分支提供 100-200 字的风格化“预演”片段。
19
+ - **逻辑支撑**:解释每个转折点与已有设定的契合度。
20
+
21
+ #### 🛠️ 系统优化
22
+ - **跨平台兼容性修复**:全面重构 `package.json` 脚本,使用 Node.js 原生 API 替代 Unix 命令(如 `rm -rf`, `chmod`),确保在 Windows 环境下完美运行。
23
+ - **项目引用一致性**:更新 `bun.lock` 及各插件文档中的项目名称引用为 `novel-writer-style-cn`。
24
+ - **风格专家模型升级**:更新 `style-expert.md`,新增风格融合指导与人设一致性检查模块。
25
+
3
26
  ## [0.20.0] - 2026-01-12
4
27
 
5
28
  ### 🎨 重大更新:AI风格学习系统
package/dist/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  import { Command } from '@commander-js/extra-typings';
3
3
  import chalk from 'chalk';
4
4
  import path from 'path';
@@ -1,373 +1,86 @@
1
1
  import fs from 'fs-extra';
2
2
  import path from 'path';
3
- import yaml from 'js-yaml';
4
- import { logger } from '../utils/logger.js';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ /**
9
+ * 插件管理器
10
+ */
5
11
  export class PluginManager {
6
- pluginsDir;
7
- commandsDirs;
8
- expertsDir;
9
- constructor(projectRoot) {
10
- this.pluginsDir = path.join(projectRoot, 'plugins');
11
- this.commandsDirs = {
12
- claude: path.join(projectRoot, '.claude', 'commands'),
13
- cursor: path.join(projectRoot, '.cursor', 'commands'),
14
- gemini: path.join(projectRoot, '.gemini', 'commands'),
15
- windsurf: path.join(projectRoot, '.windsurf', 'workflows'),
16
- roocode: path.join(projectRoot, '.roo', 'commands')
17
- };
18
- this.expertsDir = path.join(projectRoot, 'experts');
19
- }
20
- /**
21
- * 扫描并加载所有插件
22
- */
23
- async loadPlugins() {
24
- try {
25
- // 确保插件目录存在
26
- await fs.ensureDir(this.pluginsDir);
27
- // 扫描插件目录
28
- const plugins = await this.scanPlugins();
29
- if (plugins.length === 0) {
30
- logger.info('没有发现插件');
31
- return;
32
- }
33
- logger.info(`发现 ${plugins.length} 个插件`);
34
- // 加载每个插件
35
- for (const pluginName of plugins) {
36
- await this.loadPlugin(pluginName);
37
- }
38
- logger.success('所有插件加载完成');
39
- }
40
- catch (error) {
41
- logger.error('加载插件失败:', error);
42
- }
12
+ constructor() {
13
+ this.plugins = new Map();
14
+ this.loadPlugins();
43
15
  }
16
+
44
17
  /**
45
- * 扫描插件目录,返回所有插件名称
18
+ * 加载插件
46
19
  */
47
- async scanPlugins() {
20
+ loadPlugins() {
48
21
  try {
49
- // 检查插件目录是否存在
50
- if (!await fs.pathExists(this.pluginsDir)) {
51
- return [];
52
- }
53
- const entries = await fs.promises.readdir(this.pluginsDir, { withFileTypes: true });
54
- // 过滤出目录,并且包含config.yaml的
55
- const plugins = [];
56
- for (const entry of entries) {
57
- if (entry.isDirectory()) {
58
- const configPath = path.join(this.pluginsDir, entry.name, 'config.yaml');
59
- if (await fs.pathExists(configPath)) {
60
- plugins.push(entry.name);
61
- }
22
+ const pluginsDir = path.join(__dirname, '..', '..', 'plugins');
23
+ if (fs.existsSync(pluginsDir)) {
24
+ const pluginDirs = fs.readdirSync(pluginsDir, { withFileTypes: true })
25
+ .filter(dirent => dirent.isDirectory())
26
+ .map(dirent => dirent.name);
27
+
28
+ for (const pluginName of pluginDirs) {
29
+ this.loadPlugin(pluginName);
62
30
  }
63
31
  }
64
- return plugins;
65
- }
66
- catch (error) {
67
- logger.error('扫描插件目录失败:', error);
68
- return [];
32
+ } catch (error) {
33
+ console.warn('Failed to load plugins:', error.message);
69
34
  }
70
35
  }
36
+
71
37
  /**
72
38
  * 加载单个插件
73
39
  */
74
- async loadPlugin(pluginName) {
75
- try {
76
- logger.info(`加载插件: ${pluginName}`);
77
- // 读取插件配置
78
- const configPath = path.join(this.pluginsDir, pluginName, 'config.yaml');
79
- const config = await this.loadConfig(configPath);
80
- if (!config) {
81
- logger.warn(`插件 ${pluginName} 配置无效`);
82
- return;
83
- }
84
- // 检查依赖
85
- if (!this.checkDependencies(config)) {
86
- logger.warn(`插件 ${pluginName} 依赖不满足`);
87
- return;
88
- }
89
- // 注入命令
90
- if (config.commands && config.commands.length > 0) {
91
- await this.injectCommands(pluginName, config.commands);
92
- }
93
- // 注册专家
94
- if (config.experts && config.experts.length > 0) {
95
- await this.registerExperts(pluginName, config.experts);
96
- }
97
- logger.success(`插件 ${pluginName} 加载成功`);
98
- // 显示安装信息
99
- if (config.installation?.message) {
100
- console.log(config.installation.message);
101
- }
102
- }
103
- catch (error) {
104
- logger.error(`加载插件 ${pluginName} 失败:`, error);
105
- }
106
- }
107
- /**
108
- * 读取并解析插件配置
109
- */
110
- async loadConfig(configPath) {
40
+ loadPlugin(pluginName) {
111
41
  try {
112
- const content = await fs.readFile(configPath, 'utf-8');
113
- const config = yaml.load(content);
114
- // 验证必要字段
115
- if (!config.name || !config.version) {
116
- return null;
117
- }
118
- return config;
119
- }
120
- catch (error) {
121
- logger.error(`读取配置文件失败: ${configPath}`, error);
122
- return null;
123
- }
124
- }
125
- /**
126
- * 检查插件依赖
127
- */
128
- checkDependencies(config) {
129
- if (!config.dependencies) {
130
- return true;
131
- }
132
- // 检查核心版本依赖
133
- if (config.dependencies.core) {
134
- // 这里简化处理,实际应该比较版本号
135
- // 可以使用 semver 库进行版本比较
136
- const requiredVersion = config.dependencies.core;
137
- logger.debug(`需要核心版本: ${requiredVersion}`);
138
- // TODO: 实现版本比较逻辑
139
- }
140
- return true;
141
- }
142
- /**
143
- * 检测项目支持的 AI 类型
144
- */
145
- async detectSupportedAIs() {
146
- return {
147
- claude: await fs.pathExists(this.commandsDirs.claude),
148
- cursor: await fs.pathExists(this.commandsDirs.cursor),
149
- gemini: await fs.pathExists(this.commandsDirs.gemini),
150
- windsurf: await fs.pathExists(this.commandsDirs.windsurf),
151
- roocode: await fs.pathExists(this.commandsDirs.roocode)
152
- };
153
- }
154
- /**
155
- * 注入插件命令到对应的 AI 目录
156
- */
157
- async injectCommands(pluginName, commands) {
158
- if (!commands)
159
- return;
160
- // 检测项目支持哪些 AI
161
- const supportedAIs = await this.detectSupportedAIs();
162
- for (const cmd of commands) {
163
- try {
164
- // 处理 Markdown 格式(Claude、Cursor、Windsurf)
165
- const sourcePath = path.join(this.pluginsDir, pluginName, cmd.file);
166
- if (supportedAIs.claude) {
167
- const destPath = path.join(this.commandsDirs.claude, `${cmd.id}.md`);
168
- await fs.ensureDir(this.commandsDirs.claude);
169
- await fs.copy(sourcePath, destPath);
170
- logger.debug(`注入命令到 Claude: /${cmd.id}`);
171
- }
172
- if (supportedAIs.cursor) {
173
- const destPath = path.join(this.commandsDirs.cursor, `${cmd.id}.md`);
174
- await fs.ensureDir(this.commandsDirs.cursor);
175
- await fs.copy(sourcePath, destPath);
176
- logger.debug(`注入命令到 Cursor: /${cmd.id}`);
177
- }
178
- if (supportedAIs.windsurf) {
179
- const destPath = path.join(this.commandsDirs.windsurf, `${cmd.id}.md`);
180
- await fs.ensureDir(this.commandsDirs.windsurf);
181
- await fs.copy(sourcePath, destPath);
182
- logger.debug(`注入命令到 Windsurf: /${cmd.id}`);
183
- }
184
- if (supportedAIs.roocode) {
185
- const destPath = path.join(this.commandsDirs.roocode, `${cmd.id}.md`);
186
- await fs.ensureDir(this.commandsDirs.roocode);
187
- await fs.copy(sourcePath, destPath);
188
- logger.debug(`注入命令到 Roo Code: /${cmd.id}`);
189
- }
190
- // 处理 TOML 格式(Gemini)
191
- if (supportedAIs.gemini) {
192
- // 检查是否有预定义的 TOML 版本
193
- const cmdId = path.basename(cmd.id, path.extname(cmd.id));
194
- const tomlSourcePath = path.join(this.pluginsDir, pluginName, 'commands-gemini', `${cmdId}.toml`);
195
- if (await fs.pathExists(tomlSourcePath)) {
196
- const destPath = path.join(this.commandsDirs.gemini, `${cmdId}.toml`);
197
- await fs.ensureDir(this.commandsDirs.gemini);
198
- await fs.copy(tomlSourcePath, destPath);
199
- logger.debug(`注入命令到 Gemini: /${cmdId} (TOML)`);
200
- }
201
- else {
202
- // 如果没有预定义的 TOML,尝试从 Markdown 转换
203
- try {
204
- const mdContent = await fs.readFile(sourcePath, 'utf-8');
205
- const tomlContent = this.convertMarkdownToToml(mdContent, cmd);
206
- if (tomlContent) {
207
- const destPath = path.join(this.commandsDirs.gemini, `${cmdId}.toml`);
208
- await fs.ensureDir(this.commandsDirs.gemini);
209
- await fs.writeFile(destPath, tomlContent);
210
- logger.debug(`自动转换并注入命令到 Gemini: /${cmdId}`);
211
- }
212
- else {
213
- logger.debug(`插件 ${pluginName} 命令 ${cmdId} 无法转换为 TOML`);
214
- }
215
- }
216
- catch (err) {
217
- logger.debug(`插件 ${pluginName} 命令 ${cmdId} 转换失败: ${err}`);
218
- }
219
- }
220
- }
221
- }
222
- catch (error) {
223
- logger.error(`注入命令 ${cmd.id} 失败:`, error);
224
- }
225
- }
226
- }
227
- /**
228
- * 注册插件专家
229
- */
230
- async registerExperts(pluginName, experts) {
231
- if (!experts)
232
- return;
233
- const pluginExpertsDir = path.join(this.expertsDir, 'plugins', pluginName);
234
- await fs.ensureDir(pluginExpertsDir);
235
- for (const expert of experts) {
236
- try {
237
- const sourcePath = path.join(this.pluginsDir, pluginName, expert.file);
238
- const destPath = path.join(pluginExpertsDir, `${expert.id}.md`);
239
- // 复制专家文件
240
- await fs.copy(sourcePath, destPath);
241
- logger.debug(`注册专家: ${expert.title} (${expert.id})`);
242
- }
243
- catch (error) {
244
- logger.error(`注册专家 ${expert.id} 失败:`, error);
245
- }
42
+ const pluginPath = path.join(__dirname, '..', '..', 'plugins', pluginName);
43
+ const packagePath = path.join(pluginPath, 'package.json');
44
+
45
+ if (fs.existsSync(packagePath)) {
46
+ const packageJson = fs.readJsonSync(packagePath);
47
+ this.plugins.set(pluginName, {
48
+ name: pluginName,
49
+ version: packageJson.version || '1.0.0',
50
+ description: packageJson.description || '',
51
+ path: pluginPath
52
+ });
53
+ }
54
+ } catch (error) {
55
+ console.warn(`Failed to load plugin ${pluginName}:`, error.message);
246
56
  }
247
57
  }
58
+
248
59
  /**
249
- * 列出所有已安装的插件
60
+ * 获取所有插件
250
61
  */
251
- async listPlugins() {
252
- const plugins = await this.scanPlugins();
253
- const configs = [];
254
- for (const pluginName of plugins) {
255
- const configPath = path.join(this.pluginsDir, pluginName, 'config.yaml');
256
- const config = await this.loadConfig(configPath);
257
- if (config) {
258
- configs.push(config);
259
- }
260
- }
261
- return configs;
62
+ getPlugins() {
63
+ return Array.from(this.plugins.values());
262
64
  }
65
+
263
66
  /**
264
- * 安装插件(从模板或远程)
67
+ * 获取插件
265
68
  */
266
- async installPlugin(pluginName, source) {
267
- try {
268
- logger.info(`安装插件: ${pluginName}`);
269
- // 如果提供了源路径,从源复制
270
- if (source) {
271
- const destPath = path.join(this.pluginsDir, pluginName);
272
- await fs.copy(source, destPath);
273
- }
274
- else {
275
- // TODO: 实现从远程仓库或注册中心安装
276
- logger.warn('远程安装功能尚未实现');
277
- return;
278
- }
279
- // 加载新安装的插件
280
- await this.loadPlugin(pluginName);
281
- logger.success(`插件 ${pluginName} 安装成功`);
282
- }
283
- catch (error) {
284
- logger.error(`安装插件 ${pluginName} 失败:`, error);
285
- }
69
+ getPlugin(name) {
70
+ return this.plugins.get(name);
286
71
  }
72
+
287
73
  /**
288
- * 移除插件
74
+ * 检查插件是否存在
289
75
  */
290
- async removePlugin(pluginName) {
291
- try {
292
- logger.info(`移除插件: ${pluginName}`);
293
- // 删除插件目录
294
- const pluginPath = path.join(this.pluginsDir, pluginName);
295
- await fs.remove(pluginPath);
296
- // 删除注入的命令(从所有 AI 目录)
297
- const supportedAIs = await this.detectSupportedAIs();
298
- if (supportedAIs.claude && await fs.pathExists(this.commandsDirs.claude)) {
299
- const commandFiles = await fs.promises.readdir(this.commandsDirs.claude);
300
- for (const file of commandFiles) {
301
- if (file.startsWith(`plugin-${pluginName}-`)) {
302
- await fs.remove(path.join(this.commandsDirs.claude, file));
303
- logger.debug(`移除命令文件: ${file}`);
304
- }
305
- }
306
- }
307
- // 对其他 AI 目录做同样的清理
308
- for (const [aiType, dir] of Object.entries(this.commandsDirs)) {
309
- if (aiType !== 'claude' && await fs.pathExists(dir)) {
310
- const commandFiles = await fs.promises.readdir(dir);
311
- for (const file of commandFiles) {
312
- if (file.startsWith(`plugin-${pluginName}-`)) {
313
- await fs.remove(path.join(dir, file));
314
- logger.debug(`移除 ${aiType} 命令文件: ${file}`);
315
- }
316
- }
317
- }
318
- }
319
- // 删除注册的专家
320
- const pluginExpertsDir = path.join(this.expertsDir, 'plugins', pluginName);
321
- if (await fs.pathExists(pluginExpertsDir)) {
322
- await fs.remove(pluginExpertsDir);
323
- logger.debug(`移除专家目录: ${pluginExpertsDir}`);
324
- }
325
- logger.success(`插件 ${pluginName} 移除成功`);
326
- }
327
- catch (error) {
328
- logger.error(`移除插件 ${pluginName} 失败:`, error);
329
- }
76
+ hasPlugin(name) {
77
+ return this.plugins.has(name);
330
78
  }
331
- /**
332
- * 将 Markdown 命令转换为 TOML 格式
333
- */
334
- convertMarkdownToToml(mdContent, cmd) {
335
- try {
336
- // 提取 frontmatter
337
- const frontmatterMatch = mdContent.match(/^---\n([\s\S]*?)\n---/);
338
- let description = cmd.description || '';
339
- if (frontmatterMatch) {
340
- const yamlContent = frontmatterMatch[1];
341
- const descMatch = yamlContent.match(/description:\s*(.+)/);
342
- if (descMatch) {
343
- description = descMatch[1].trim().replace(/^['"]|['"]$/g, '');
344
- }
345
- }
346
- // 提取内容(去除 frontmatter)
347
- const content = mdContent.replace(/^---\n[\s\S]*?\n---\n/, '');
348
- // 构建 TOML 内容
349
- const tomlContent = `description = "${description}"
350
-
351
- prompt = """
352
- ${content}
353
79
 
354
- 用户输入:{{args}}
355
- """`;
356
- return tomlContent;
357
- }
358
- catch (error) {
359
- return null;
360
- }
361
- }
362
80
  /**
363
- * 更新插件
81
+ * 列出所有插件
364
82
  */
365
- async updatePlugin(pluginName, source) {
366
- logger.info(`更新插件: ${pluginName}`);
367
- // 先移除旧版本
368
- await this.removePlugin(pluginName);
369
- // 安装新版本
370
- await this.installPlugin(pluginName, source);
83
+ listPlugins() {
84
+ return this.getPlugins();
371
85
  }
372
- }
373
- //# sourceMappingURL=manager.js.map
86
+ }