@becrafter/prompt-manager 0.1.1 → 0.1.8

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 (110) hide show
  1. package/README.md +304 -121
  2. package/app/cli/commands/start.js +28 -4
  3. package/app/cli/support/argv.js +6 -0
  4. package/env.example +32 -0
  5. package/package.json +36 -6
  6. package/packages/server/api/admin.routes.js +409 -1
  7. package/packages/server/api/open.routes.js +7 -2
  8. package/packages/server/api/tool.routes.js +479 -0
  9. package/packages/server/app.js +97 -25
  10. package/packages/server/configs/models/built-in/bigmodel.yaml +6 -0
  11. package/packages/server/configs/models/providers.yaml +50 -0
  12. package/packages/server/configs/templates/built-in/general-iteration.yaml +60 -0
  13. package/packages/server/configs/templates/built-in/general-optimize.yaml +63 -0
  14. package/packages/server/configs/templates/built-in/output-format-optimize.yaml +95 -0
  15. package/packages/server/mcp/heartbeat-patch.js +73 -0
  16. package/packages/server/mcp/mcp.server.js +63 -314
  17. package/packages/server/mcp/prompt.handler.js +26 -0
  18. package/packages/server/mcp/thinking-toolkit.handler.js +380 -0
  19. package/packages/server/package.json +35 -3
  20. package/packages/server/server.js +114 -12
  21. package/packages/server/services/TerminalService.js +498 -0
  22. package/packages/server/services/WebSocketService.js +484 -0
  23. package/packages/server/services/manager.js +38 -7
  24. package/packages/server/services/model.service.js +473 -0
  25. package/packages/server/services/optimization.service.js +457 -0
  26. package/packages/server/services/template.service.js +333 -0
  27. package/packages/server/toolm/tool-description-generator-optimized.service.js +5 -2
  28. package/packages/server/toolm/tool-sync.service.js +47 -3
  29. package/packages/server/utils/config.js +8 -1
  30. package/packages/server/utils/port-checker.js +63 -0
  31. package/packages/server/utils/util.js +27 -0
  32. package/IFLOW.md +0 -175
  33. package/app/desktop/assets/app.1.png +0 -0
  34. package/app/desktop/assets/app.png +0 -0
  35. package/app/desktop/assets/icons/icon.icns +0 -0
  36. package/app/desktop/assets/icons/icon.ico +0 -0
  37. package/app/desktop/assets/icons/icon.png +0 -0
  38. package/app/desktop/assets/icons/tray.png +0 -0
  39. package/app/desktop/assets/templates/about.html +0 -147
  40. package/app/desktop/assets/tray.1.png +0 -0
  41. package/app/desktop/assets/tray.png +0 -0
  42. package/app/desktop/main.js +0 -241
  43. package/app/desktop/package-lock.json +0 -4997
  44. package/app/desktop/package.json +0 -100
  45. package/app/desktop/preload.js +0 -7
  46. package/app/desktop/src/core/error-handler.js +0 -108
  47. package/app/desktop/src/core/event-emitter.js +0 -84
  48. package/app/desktop/src/core/logger.js +0 -108
  49. package/app/desktop/src/core/state-manager.js +0 -125
  50. package/app/desktop/src/services/module-loader.js +0 -214
  51. package/app/desktop/src/services/runtime-manager.js +0 -301
  52. package/app/desktop/src/services/service-manager.js +0 -169
  53. package/app/desktop/src/services/update-manager.js +0 -268
  54. package/app/desktop/src/ui/about-dialog-manager.js +0 -208
  55. package/app/desktop/src/ui/admin-window-manager.js +0 -757
  56. package/app/desktop/src/ui/splash-manager.js +0 -253
  57. package/app/desktop/src/ui/tray-manager.js +0 -186
  58. package/app/desktop/src/utils/icon-manager.js +0 -133
  59. package/app/desktop/src/utils/path-utils.js +0 -58
  60. package/app/desktop/src/utils/resource-paths.js +0 -49
  61. package/app/desktop/src/utils/resource-sync.js +0 -260
  62. package/app/desktop/src/utils/runtime-sync.js +0 -241
  63. package/app/desktop/src/utils/template-renderer.js +0 -284
  64. package/app/desktop/src/utils/version-utils.js +0 -59
  65. package/examples/prompts/developer/code-review.yaml +0 -32
  66. package/examples/prompts/developer/code_refactoring.yaml +0 -31
  67. package/examples/prompts/developer/doc-generator.yaml +0 -36
  68. package/examples/prompts/developer/error-code-fixer.yaml +0 -35
  69. package/examples/prompts/engineer/engineer-professional.yaml +0 -92
  70. package/examples/prompts/engineer/laowang-engineer.yaml +0 -132
  71. package/examples/prompts/engineer/nekomata-engineer.yaml +0 -123
  72. package/examples/prompts/engineer/ojousama-engineer.yaml +0 -124
  73. package/examples/prompts/generator/gen_3d_edu_webpage_html.yaml +0 -117
  74. package/examples/prompts/generator/gen_3d_webpage_html.yaml +0 -75
  75. package/examples/prompts/generator/gen_bento_grid_html.yaml +0 -112
  76. package/examples/prompts/generator/gen_html_web_page.yaml +0 -88
  77. package/examples/prompts/generator/gen_knowledge_card_html.yaml +0 -83
  78. package/examples/prompts/generator/gen_magazine_card_html.yaml +0 -82
  79. package/examples/prompts/generator/gen_mimeng_headline_title.yaml +0 -71
  80. package/examples/prompts/generator/gen_podcast_script.yaml +0 -69
  81. package/examples/prompts/generator/gen_prd_prototype_html.yaml +0 -175
  82. package/examples/prompts/generator/gen_summarize.yaml +0 -157
  83. package/examples/prompts/generator/gen_title.yaml +0 -119
  84. package/examples/prompts/generator/others/api_documentation.yaml +0 -32
  85. package/examples/prompts/generator/others/build_mcp_server.yaml +0 -26
  86. package/examples/prompts/generator/others/project_architecture.yaml +0 -31
  87. package/examples/prompts/generator/others/test_case_generator.yaml +0 -30
  88. package/examples/prompts/generator/others/writing_assistant.yaml +0 -72
  89. package/examples/prompts/recommend/human_3-0_growth_diagnostic_coach_prompt.yaml +0 -105
  90. package/examples/prompts/workflow/sixstep-workflow.yaml +0 -192
  91. package/packages/admin-ui/.babelrc +0 -3
  92. package/packages/admin-ui/admin.html +0 -412
  93. package/packages/admin-ui/css/codemirror-theme_xq-light.css +0 -43
  94. package/packages/admin-ui/css/codemirror.css +0 -344
  95. package/packages/admin-ui/css/main.css +0 -2592
  96. package/packages/admin-ui/css/recommended-prompts.css +0 -610
  97. package/packages/admin-ui/package-lock.json +0 -6981
  98. package/packages/admin-ui/package.json +0 -36
  99. package/packages/admin-ui/src/codemirror.js +0 -53
  100. package/packages/admin-ui/src/index.js +0 -3188
  101. package/packages/admin-ui/webpack.config.js +0 -76
  102. package/packages/server/toolm/test-tools.js +0 -264
  103. package/scripts/build-icons.js +0 -135
  104. package/scripts/build.sh +0 -57
  105. package/scripts/postinstall.js +0 -34
  106. package/scripts/surge/CNAME +0 -1
  107. package/scripts/surge/README.md +0 -47
  108. package/scripts/surge/package-lock.json +0 -34
  109. package/scripts/surge/package.json +0 -20
  110. package/scripts/surge/sync-to-surge.js +0 -151
@@ -0,0 +1,484 @@
1
+ /**
2
+ * WebSocketService - WebSocket服务管理类
3
+ *
4
+ * 提供实时双向通信服务,支持终端会话的实时数据传输
5
+ * 处理客户端连接、消息路由和会话管理
6
+ */
7
+
8
+ import { WebSocketServer } from 'ws';
9
+ import { randomUUID } from 'crypto';
10
+ import { logger } from '../utils/logger.js';
11
+ import { terminalService } from './TerminalService.js';
12
+
13
+ /**
14
+ * WebSocket连接类
15
+ */
16
+ class WebSocketConnection {
17
+ constructor(ws, clientId) {
18
+ this.ws = ws;
19
+ this.clientId = clientId;
20
+ this.connectedAt = new Date();
21
+ this.lastActivity = new Date();
22
+ this.sessionId = null;
23
+ this.isAuthenticated = false;
24
+ this.userInfo = null;
25
+
26
+ this.setupWebSocketEvents();
27
+ }
28
+
29
+ /**
30
+ * 设置WebSocket事件监听
31
+ */
32
+ setupWebSocketEvents() {
33
+ this.ws.on('message', (data) => {
34
+ this.handleMessage(data);
35
+ });
36
+
37
+ this.ws.on('close', (code, reason) => {
38
+ this.handleClose(code, reason);
39
+ });
40
+
41
+ this.ws.on('error', (error) => {
42
+ this.handleError(error);
43
+ });
44
+
45
+ this.ws.on('pong', () => {
46
+ this.lastActivity = new Date();
47
+ });
48
+ }
49
+
50
+ /**
51
+ * 处理消息
52
+ */
53
+ async handleMessage(data) {
54
+ try {
55
+ this.lastActivity = new Date();
56
+ const message = JSON.parse(data.toString());
57
+
58
+ logger.debug(`Received message from client ${this.clientId}:`, message.type);
59
+
60
+ // 根据消息类型处理
61
+ switch (message.type) {
62
+ case 'terminal.create':
63
+ await this.handleTerminalCreate(message);
64
+ break;
65
+ case 'terminal.data':
66
+ await this.handleTerminalData(message);
67
+ break;
68
+ case 'terminal.resize':
69
+ await this.handleTerminalResize(message);
70
+ break;
71
+ case 'terminal.close':
72
+ await this.handleTerminalClose(message);
73
+ break;
74
+ case 'ping':
75
+ this.handlePing(message);
76
+ break;
77
+ default:
78
+ this.sendError('Unknown message type', message.type);
79
+ }
80
+ } catch (error) {
81
+ logger.error(`Error handling message from client ${this.clientId}:`, error);
82
+ this.sendError('Message processing error', error.message);
83
+ }
84
+ }
85
+
86
+ /**
87
+ * 处理终端创建
88
+ */
89
+ async handleTerminalCreate(message) {
90
+ try {
91
+ const options = {
92
+ id: message.sessionId,
93
+ workingDirectory: message.workingDirectory,
94
+ size: message.size || { cols: 80, rows: 24 },
95
+ shell: message.shell,
96
+ environment: message.environment
97
+ };
98
+
99
+ const session = await terminalService.createSession(options);
100
+ this.sessionId = session.id;
101
+
102
+ // 监听会话数据
103
+ session.on('data', (data) => {
104
+ this.send('terminal.data', {
105
+ sessionId: session.id,
106
+ data: data
107
+ });
108
+ });
109
+
110
+ session.on('exit', (info) => {
111
+ this.send('terminal.exit', {
112
+ sessionId: session.id,
113
+ exitCode: info.exitCode,
114
+ signal: info.signal
115
+ });
116
+ });
117
+
118
+ this.send('terminal.created', {
119
+ sessionId: session.id,
120
+ info: session.getInfo()
121
+ });
122
+
123
+ logger.info(`Terminal session created for client ${this.clientId}: ${session.id}`);
124
+ } catch (error) {
125
+ this.sendError('Failed to create terminal session', error.message);
126
+ }
127
+ }
128
+
129
+ /**
130
+ * 处理终端数据
131
+ */
132
+ async handleTerminalData(message) {
133
+ if (!this.sessionId) {
134
+ this.sendError('No active terminal session', 'terminal.data');
135
+ return;
136
+ }
137
+
138
+ try {
139
+ const session = terminalService.getSession(this.sessionId);
140
+ if (session && session.isActive) {
141
+ session.write(message.data);
142
+ } else {
143
+ this.sendError('Terminal session not found or inactive', 'terminal.data');
144
+ }
145
+ } catch (error) {
146
+ this.sendError('Failed to write to terminal', error.message);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * 处理终端大小调整
152
+ */
153
+ async handleTerminalResize(message) {
154
+ if (!this.sessionId) {
155
+ this.sendError('No active terminal session', 'terminal.resize');
156
+ return;
157
+ }
158
+
159
+ try {
160
+ const session = terminalService.getSession(this.sessionId);
161
+ if (session && session.isActive) {
162
+ session.resize(message.cols, message.rows);
163
+ this.send('terminal.resized', {
164
+ sessionId: this.sessionId,
165
+ size: { cols: message.cols, rows: message.rows }
166
+ });
167
+ } else {
168
+ this.sendError('Terminal session not found or inactive', 'terminal.resize');
169
+ }
170
+ } catch (error) {
171
+ this.sendError('Failed to resize terminal', error.message);
172
+ }
173
+ }
174
+
175
+ /**
176
+ * 处理终端关闭
177
+ */
178
+ async handleTerminalClose(message) {
179
+ if (this.sessionId) {
180
+ try {
181
+ await terminalService.removeSession(this.sessionId);
182
+ this.send('terminal.closed', {
183
+ sessionId: this.sessionId
184
+ });
185
+ this.sessionId = null;
186
+ } catch (error) {
187
+ this.sendError('Failed to close terminal session', error.message);
188
+ }
189
+ }
190
+ }
191
+
192
+ /**
193
+ * 处理ping
194
+ */
195
+ handlePing(message) {
196
+ this.send('pong', {
197
+ timestamp: Date.now(),
198
+ clientId: this.clientId
199
+ });
200
+ }
201
+
202
+ /**
203
+ * 处理连接关闭
204
+ */
205
+ handleClose(code, reason) {
206
+ logger.info(`Client ${this.clientId} disconnected: ${code} - ${reason}`);
207
+
208
+ // 清理关联的终端会话
209
+ if (this.sessionId) {
210
+ terminalService.removeSession(this.sessionId);
211
+ }
212
+ }
213
+
214
+ /**
215
+ * 处理错误
216
+ */
217
+ handleError(error) {
218
+ logger.error(`WebSocket error for client ${this.clientId}:`, error);
219
+ }
220
+
221
+ /**
222
+ * 发送消息
223
+ */
224
+ send(type, data) {
225
+ if (this.ws.readyState === this.ws.OPEN) {
226
+ const message = {
227
+ type: type,
228
+ timestamp: Date.now(),
229
+ clientId: this.clientId,
230
+ ...data
231
+ };
232
+
233
+ this.ws.send(JSON.stringify(message));
234
+ }
235
+ }
236
+
237
+ /**
238
+ * 发送错误消息
239
+ */
240
+ sendError(message, details = null) {
241
+ this.send('error', {
242
+ message: message,
243
+ details: details
244
+ });
245
+ }
246
+
247
+ /**
248
+ * 获取连接信息
249
+ */
250
+ getInfo() {
251
+ return {
252
+ clientId: this.clientId,
253
+ connectedAt: this.connectedAt,
254
+ lastActivity: this.lastActivity,
255
+ sessionId: this.sessionId,
256
+ isAuthenticated: this.isAuthenticated,
257
+ readyState: this.ws.readyState
258
+ };
259
+ }
260
+
261
+ /**
262
+ * 关闭连接
263
+ */
264
+ close(code = 1000, reason = 'Normal closure') {
265
+ if (this.ws.readyState === this.ws.OPEN) {
266
+ this.ws.close(code, reason);
267
+ }
268
+ }
269
+ }
270
+
271
+ /**
272
+ * WebSocketService 主类
273
+ */
274
+ export class WebSocketService {
275
+ constructor(options = {}) {
276
+ this.options = {
277
+ port: 5622,
278
+ host: '0.0.0.0',
279
+ maxConnections: 100,
280
+ heartbeatInterval: 30000, // 30秒心跳
281
+ connectionTimeout: 300000, // 5分钟超时
282
+ ...options
283
+ };
284
+
285
+ this.wss = null;
286
+ this.connections = new Map();
287
+ this.isRunning = false;
288
+
289
+ logger.info('WebSocketService initialized');
290
+ }
291
+
292
+ /**
293
+ * 启动WebSocket服务
294
+ */
295
+ async start() {
296
+ if (this.isRunning) {
297
+ throw new Error('WebSocket service is already running');
298
+ }
299
+
300
+ return new Promise((resolve, reject) => {
301
+ try {
302
+ this.wss = new WebSocketServer({
303
+ port: this.options.port,
304
+ host: this.options.host,
305
+ maxConnections: this.options.maxConnections
306
+ });
307
+
308
+ this.wss.on('connection', (ws, request) => {
309
+ this.handleConnection(ws, request);
310
+ });
311
+
312
+ this.wss.on('error', (error) => {
313
+ logger.error('WebSocket server error:', error);
314
+ if (!this.isRunning) {
315
+ reject(error);
316
+ }
317
+ });
318
+
319
+ this.wss.on('listening', () => {
320
+ this.isRunning = true;
321
+ logger.info(`WebSocket server listening on ${this.options.host}:${this.options.port}`);
322
+
323
+ // 启动心跳检测
324
+ this.startHeartbeat();
325
+
326
+ resolve();
327
+ });
328
+
329
+ } catch (error) {
330
+ reject(error);
331
+ }
332
+ });
333
+ }
334
+
335
+ /**
336
+ * 停止WebSocket服务
337
+ */
338
+ async stop() {
339
+ if (!this.isRunning) {
340
+ return;
341
+ }
342
+
343
+ // 关闭所有连接
344
+ for (const [clientId, connection] of this.connections) {
345
+ connection.close(1001, 'Server shutdown');
346
+ }
347
+ this.connections.clear();
348
+
349
+ // 停止心跳检测
350
+ if (this.heartbeatInterval) {
351
+ clearInterval(this.heartbeatInterval);
352
+ }
353
+
354
+ // 关闭服务器
355
+ if (this.wss) {
356
+ this.wss.close();
357
+ this.wss = null;
358
+ }
359
+
360
+ this.isRunning = false;
361
+ logger.info('WebSocket service stopped');
362
+ }
363
+
364
+ /**
365
+ * 处理新连接
366
+ */
367
+ handleConnection(ws, request) {
368
+ const clientId = this.generateClientId();
369
+
370
+ // 检查连接数限制
371
+ if (this.connections.size >= this.options.maxConnections) {
372
+ ws.close(1013, 'Server overload');
373
+ return;
374
+ }
375
+
376
+ try {
377
+ const connection = new WebSocketConnection(ws, clientId);
378
+ this.connections.set(clientId, connection);
379
+
380
+ logger.info(`New client connected: ${clientId} from ${request.socket.remoteAddress}`);
381
+
382
+ // 发送欢迎消息
383
+ connection.send('welcome', {
384
+ clientId: clientId,
385
+ serverInfo: {
386
+ version: '1.0.0',
387
+ platform: process.platform,
388
+ nodeVersion: process.version
389
+ }
390
+ });
391
+
392
+ // 监听连接关闭
393
+ ws.on('close', () => {
394
+ this.connections.delete(clientId);
395
+ });
396
+
397
+ } catch (error) {
398
+ logger.error(`Failed to handle connection for client ${clientId}:`, error);
399
+ ws.close(1011, 'Internal error');
400
+ }
401
+ }
402
+
403
+ /**
404
+ * 生成客户端ID
405
+ */
406
+ generateClientId() {
407
+ return randomUUID();
408
+ }
409
+
410
+ /**
411
+ * 启动心跳检测
412
+ */
413
+ startHeartbeat() {
414
+ this.heartbeatInterval = setInterval(() => {
415
+ const now = Date.now();
416
+
417
+ for (const [clientId, connection] of this.connections) {
418
+ // 检查连接超时
419
+ if (now - connection.lastActivity > this.options.connectionTimeout) {
420
+ logger.info(`Client ${clientId} timeout, disconnecting`);
421
+ connection.close(1000, 'Connection timeout');
422
+ this.connections.delete(clientId);
423
+ continue;
424
+ }
425
+
426
+ // 发送ping
427
+ if (connection.ws.readyState === connection.ws.OPEN) {
428
+ connection.ws.ping();
429
+ } else {
430
+ // 连接已关闭,清理
431
+ this.connections.delete(clientId);
432
+ }
433
+ }
434
+ }, this.options.heartbeatInterval);
435
+ }
436
+
437
+ /**
438
+ * 广播消息给所有客户端
439
+ */
440
+ broadcast(type, data) {
441
+ for (const connection of this.connections.values()) {
442
+ connection.send(type, data);
443
+ }
444
+ }
445
+
446
+ /**
447
+ * 获取连接
448
+ */
449
+ getConnection(clientId) {
450
+ return this.connections.get(clientId);
451
+ }
452
+
453
+ /**
454
+ * 获取所有连接
455
+ */
456
+ getAllConnections() {
457
+ return Array.from(this.connections.values());
458
+ }
459
+
460
+ /**
461
+ * 获取服务状态
462
+ */
463
+ getStatus() {
464
+ const activeConnections = this.getAllConnections().filter(
465
+ conn => conn.ws.readyState === conn.ws.OPEN
466
+ );
467
+
468
+ return {
469
+ isRunning: this.isRunning,
470
+ port: this.options.port,
471
+ host: this.options.host,
472
+ totalConnections: this.connections.size,
473
+ activeConnections: activeConnections.length,
474
+ maxConnections: this.options.maxConnections,
475
+ uptime: process.uptime()
476
+ };
477
+ }
478
+ }
479
+
480
+ // 创建单例实例
481
+ export const webSocketService = new WebSocketService();
482
+
483
+ // 导出类型定义
484
+ export { WebSocketConnection };
@@ -210,17 +210,22 @@ class PromptManager {
210
210
  *
211
211
  * @param {string} currentPath - 当前目录路径
212
212
  * @param {string} relativePath - 相对于当前目录的路径
213
+ * @param {boolean} inheritedEnabled - 从父目录继承的启用状态
213
214
  * @returns {Array} 所有prompt文件
214
215
  */
215
- async scanPromptFiles(currentPath, relativePath = '') {
216
+ async scanPromptFiles(currentPath, relativePath = '', inheritedEnabled = true) {
216
217
  const promptFiles = [];
218
+ let currentEnabled = inheritedEnabled;
217
219
 
218
220
  if (relativePath) {
219
221
  // console.log('读取组元数据:', "{relativePath:", relativePath, ", currentPath:", currentPath, "}");
220
222
  const meta = this.readGroupMeta(path.join(currentPath, relativePath));
221
- if (meta.enabled === false) {
222
- return [];
223
- }
223
+ currentEnabled = currentEnabled && (meta.enabled !== false);
224
+ }
225
+
226
+ // 如果当前目录被禁用,直接返回空数组
227
+ if (!currentEnabled) {
228
+ return [];
224
229
  }
225
230
 
226
231
  try {
@@ -232,8 +237,8 @@ class PromptManager {
232
237
  const itemRelativePath = relativePath ? path.join(relativePath, item.name) : item.name; // 文件的相对路径,相对于当前目录,如:xxx/xxx.yaml
233
238
 
234
239
  if (item.isDirectory()) {
235
- // 递归扫描子目录
236
- const subFiles = await this.scanPromptFiles(itemPath, itemRelativePath);
240
+ // 递归扫描子目录,传递当前目录的启用状态
241
+ const subFiles = await this.scanPromptFiles(itemPath, itemRelativePath, currentEnabled);
237
242
  promptFiles.push(...subFiles);
238
243
  } else if (item.isFile()) {
239
244
  // 检查文件扩展名
@@ -361,10 +366,33 @@ class PromptManager {
361
366
  promptData = YAML.parse(content);
362
367
  }
363
368
 
369
+ // 检查提示词本身是否启用
364
370
  if (promptData.enabled !== true) {
365
371
  logger.debug(`skipped loading prompt: ${promptData.name} -> disabled`);
366
372
  return null;
367
373
  }
374
+
375
+ // 检查目录启用状态
376
+ const fileDir = path.dirname(filePath);
377
+ const relativeDir = path.dirname(relativePath);
378
+ if (relativeDir && relativeDir !== '.') {
379
+ // 检查从根目录到当前文件路径的所有目录的启用状态
380
+ const dirParts = relativeDir.split(path.sep);
381
+ let currentPath = this.promptsDir;
382
+ let inheritedEnabled = true;
383
+
384
+ for (const dirPart of dirParts) {
385
+ currentPath = path.join(currentPath, dirPart);
386
+ const meta = this.readGroupMeta(currentPath);
387
+ inheritedEnabled = inheritedEnabled && (meta.enabled !== false);
388
+
389
+ if (!inheritedEnabled) {
390
+ logger.debug(`skipped loading prompt: ${promptData.name} -> parent directory disabled`);
391
+ return null;
392
+ }
393
+ }
394
+ }
395
+
368
396
  logger.debug(`loaded prompt: ${promptData.name} -> ${filePath}`);
369
397
 
370
398
  // 验证prompt数据结构
@@ -478,4 +506,7 @@ class PromptManager {
478
506
  }
479
507
 
480
508
  // 创建全局PromptManager实例
481
- export const promptManager = new PromptManager(config.getPromptsDir());
509
+ export const promptManager = new PromptManager(config.getPromptsDir());
510
+
511
+ // 导出PromptManager类供测试使用
512
+ export { PromptManager };