@becrafter/prompt-manager 0.0.18 → 0.0.19

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 (50) hide show
  1. package/IFLOW.md +175 -0
  2. package/README.md +1 -1
  3. package/app/desktop/assets/icons/icon.icns +0 -0
  4. package/app/desktop/assets/icons/icon.ico +0 -0
  5. package/app/desktop/assets/icons/icon_1024x1024.png +0 -0
  6. package/app/desktop/assets/icons/icon_128x128.png +0 -0
  7. package/app/desktop/assets/icons/icon_16x16.png +0 -0
  8. package/app/desktop/assets/icons/icon_24x24.png +0 -0
  9. package/app/desktop/assets/icons/icon_256x256.png +0 -0
  10. package/app/desktop/assets/icons/icon_32x32.png +0 -0
  11. package/app/desktop/assets/icons/icon_48x48.png +0 -0
  12. package/app/desktop/assets/icons/icon_512x512.png +0 -0
  13. package/app/desktop/assets/icons/icon_64x64.png +0 -0
  14. package/app/desktop/assets/icons/icon_96x96.png +0 -0
  15. package/app/desktop/assets/templates/about.html +147 -0
  16. package/app/desktop/main.js +160 -732
  17. package/app/desktop/package-lock.json +567 -534
  18. package/app/desktop/package.json +45 -10
  19. package/app/desktop/preload.js +7 -0
  20. package/app/desktop/src/core/error-handler.js +108 -0
  21. package/app/desktop/src/core/event-emitter.js +84 -0
  22. package/app/desktop/src/core/logger.js +108 -0
  23. package/app/desktop/src/core/state-manager.js +125 -0
  24. package/app/desktop/src/services/module-loader.js +193 -0
  25. package/app/desktop/src/services/runtime-manager.js +152 -0
  26. package/app/desktop/src/services/service-manager.js +169 -0
  27. package/app/desktop/src/services/update-manager.js +268 -0
  28. package/app/desktop/src/ui/about-dialog-manager.js +208 -0
  29. package/app/desktop/src/ui/tray-manager.js +202 -0
  30. package/app/desktop/src/utils/icon-manager.js +141 -0
  31. package/app/desktop/src/utils/path-utils.js +58 -0
  32. package/app/desktop/src/utils/resource-paths.js +72 -0
  33. package/app/desktop/src/utils/template-renderer.js +284 -0
  34. package/app/desktop/src/utils/version-utils.js +59 -0
  35. package/examples/prompts/engineer/engineer-professional.yaml +92 -0
  36. package/examples/prompts/engineer/laowang-engineer.yaml +132 -0
  37. package/examples/prompts/engineer/nekomata-engineer.yaml +123 -0
  38. package/examples/prompts/engineer/ojousama-engineer.yaml +124 -0
  39. package/examples/prompts/workflow/sixstep-workflow.yaml +192 -0
  40. package/package.json +9 -3
  41. package/packages/admin-ui/admin.html +1 -1
  42. package/packages/resources/tools/filesystem/filesystem.tool.js +184 -0
  43. package/packages/resources/tools/index.js +16 -0
  44. package/packages/server/mcp/mcp.handler.js +108 -9
  45. package/packages/server/mcp/mcp.server.js +126 -27
  46. package/packages/server/mcp/toolx.handler.js +131 -0
  47. package/packages/server/utils/config.js +1 -1
  48. package/scripts/build-icons.js +105 -0
  49. package/scripts/icns-builder/package.json +12 -0
  50. /package/app/desktop/assets/{icon.png → icons/icon.png} +0 -0
@@ -0,0 +1,184 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * 文件系统工具
6
+ * @param {Object} params - 工具参数
7
+ * @param {string} mode - 操作模式
8
+ * @returns {Promise<Object>} 执行结果
9
+ */
10
+ export default async function filesystem(params, mode = 'execute') {
11
+ // 根据模式执行不同的操作
12
+ switch (mode) {
13
+ case 'manual':
14
+ // 生成 Markdown 格式的手册
15
+ return generateManual();
16
+
17
+ case 'execute':
18
+ // 执行模式 - 实际执行操作
19
+ const { action, path: filePath, content } = params;
20
+
21
+ if (!action) {
22
+ throw new Error('缺少必需参数: action');
23
+ }
24
+
25
+ if (!filePath) {
26
+ throw new Error('缺少必需参数: path');
27
+ }
28
+
29
+ switch (action) {
30
+ case 'read':
31
+ try {
32
+ const fileContent = await fs.readFile(filePath, 'utf8');
33
+ return {
34
+ success: true,
35
+ action: 'read',
36
+ path: filePath,
37
+ content: fileContent
38
+ };
39
+ } catch (error) {
40
+ throw new Error(`读取文件失败: ${error.message}`);
41
+ }
42
+
43
+ case 'write':
44
+ if (content === undefined) {
45
+ throw new Error('写入文件需要提供 content 参数');
46
+ }
47
+ try {
48
+ await fs.writeFile(filePath, content, 'utf8');
49
+ return {
50
+ success: true,
51
+ action: 'write',
52
+ path: filePath,
53
+ message: '文件写入成功'
54
+ };
55
+ } catch (error) {
56
+ throw new Error(`写入文件失败: ${error.message}`);
57
+ }
58
+
59
+ case 'list':
60
+ try {
61
+ const items = await fs.readdir(filePath);
62
+ return {
63
+ success: true,
64
+ action: 'list',
65
+ path: filePath,
66
+ items: items
67
+ };
68
+ } catch (error) {
69
+ throw new Error(`列出目录内容失败: ${error.message}`);
70
+ }
71
+
72
+ case 'delete':
73
+ try {
74
+ const stats = await fs.stat(filePath);
75
+ if (stats.isDirectory()) {
76
+ await fs.rm(filePath, { recursive: true });
77
+ return {
78
+ success: true,
79
+ action: 'delete',
80
+ path: filePath,
81
+ type: 'directory',
82
+ message: '目录删除成功'
83
+ };
84
+ } else {
85
+ await fs.unlink(filePath);
86
+ return {
87
+ success: true,
88
+ action: 'delete',
89
+ path: filePath,
90
+ type: 'file',
91
+ message: '文件删除成功'
92
+ };
93
+ }
94
+ } catch (error) {
95
+ throw new Error(`删除文件或目录失败: ${error.message}`);
96
+ }
97
+
98
+ default:
99
+ throw new Error(`不支持的操作类型: ${action}`);
100
+ }
101
+
102
+ default:
103
+ throw new Error(`不支持的模式: ${mode}`);
104
+ }
105
+ }
106
+
107
+ /**
108
+ * 生成 Markdown 格式的手册
109
+ * @returns {string} Markdown 格式的手册
110
+ */
111
+ function generateManual() {
112
+ return `# 🔧 filesystem
113
+
114
+ > 文件系统工具 - 用于文件操作
115
+
116
+ ## 📋 基础信息
117
+
118
+ - **标识**: \`tool://filesystem\`
119
+ - **分类**: 系统工具
120
+
121
+ ## ✅ 适用场景
122
+
123
+ - 读取文件内容进行分析
124
+ - 写入文件内容保存数据
125
+ - 列出目录内容查看文件结构
126
+ - 删除文件或目录进行清理
127
+
128
+ ## 📝 参数定义
129
+
130
+ ### execute 模式参数
131
+
132
+ | 参数 | 类型 | 必需 | 描述 | 默认值 |
133
+ |------|------|------|------|--------|
134
+ | action | string (read|write|list|delete) | ✅ | 操作类型 | - |
135
+ | path | string | ✅ | 文件或目录路径 | - |
136
+ | content | string | ❌ | 写入的文件内容 (仅在action为write时需要) | - |
137
+
138
+ ## 💻 使用示例
139
+
140
+ 通过 toolx 调用,使用 YAML 格式:
141
+
142
+ \`\`\`yaml
143
+ # 读取文件内容
144
+ tool: tool://filesystem
145
+ mode: execute
146
+ parameters:
147
+ action: read
148
+ path: /path/to/file.txt
149
+
150
+ # 写入文件内容
151
+ tool: tool://filesystem
152
+ mode: execute
153
+ parameters:
154
+ action: write
155
+ path: /path/to/file.txt
156
+ content: "Hello, World!"
157
+
158
+ # 列出目录内容
159
+ tool: tool://filesystem
160
+ mode: execute
161
+ parameters:
162
+ action: list
163
+ path: /path/to/directory
164
+
165
+ # 删除文件或目录
166
+ tool: tool://filesystem
167
+ mode: execute
168
+ parameters:
169
+ action: delete
170
+ path: /path/to/file-or-directory
171
+ \`\`\`
172
+
173
+ ## 🚨 业务错误
174
+
175
+ | 错误码 | 描述 | 解决方案 | 可重试 |
176
+ |--------|------|----------|--------|
177
+ | MISSING_ACTION | 缺少必需参数: action | 提供 action 参数 | ❌ |
178
+ | MISSING_PATH | 缺少必需参数: path | 提供 path 参数 | ❌ |
179
+ | READ_FAILED | 读取文件失败 | 检查文件路径是否存在且可读 | ✅ |
180
+ | WRITE_FAILED | 写入文件失败 | 检查文件路径是否可写 | ✅ |
181
+ | LIST_FAILED | 列出目录内容失败 | 检查目录路径是否存在且可读 | ✅ |
182
+ | DELETE_FAILED | 删除文件或目录失败 | 检查文件或目录是否存在且可删除 | ✅ |
183
+ `;
184
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * ToolX 工具系统 - 提供各种服务内部工具的统一调用接口
3
+ *
4
+ * 工具目录结构:
5
+ * - 所有工具都位于 /tools 目录下
6
+ * - 每个工具是一个独立的 JS 文件
7
+ * - 工具导出一个默认函数,接受参数和模式作为输入
8
+ */
9
+
10
+ // 导出所有可用的工具
11
+ export { default as filesystem } from './filesystem/filesystem.tool.js';
12
+
13
+ // 工具列表
14
+ export const availableTools = [
15
+ 'filesystem'
16
+ ];
@@ -9,7 +9,7 @@ export async function handleGetPrompt(args) {
9
9
  const promptId = args.prompt_id || args.name;
10
10
 
11
11
  if (!promptId) {
12
- throw new Error("缺少必需参数: prompt_id 或 name");
12
+ throw new Error("缺少必需参数: prompt_id");
13
13
  }
14
14
 
15
15
  const promptManager = await util.getPromptManager();
@@ -39,7 +39,7 @@ export async function handleGetPrompt(args) {
39
39
  return convertToText({
40
40
  success: true,
41
41
  prompt: promptInfo
42
- });
42
+ }, 'detail');
43
43
  }
44
44
 
45
45
  // 处理 search_prompts 工具调用
@@ -60,7 +60,7 @@ export async function handleSearchPrompts(args) {
60
60
  query: searchTerm || '',
61
61
  count: simplifiedPrompts.length,
62
62
  results: simplifiedPrompts
63
- });
63
+ }, 'list');
64
64
  }
65
65
 
66
66
  // 实现相似度匹配算法
@@ -95,7 +95,7 @@ export async function handleSearchPrompts(args) {
95
95
  }
96
96
  }
97
97
 
98
- return convertToText(result);
98
+ return convertToText(result, 'list');
99
99
  }
100
100
 
101
101
  /**
@@ -150,17 +150,116 @@ function formatResults(results = []) {
150
150
  }
151
151
 
152
152
  /**
153
- * 将对象转换为text类型
154
- * @param {*} object
153
+ * 处理列表格式输出
154
+ * @param {*} result
155
+ * @returns
156
+ */
157
+ function formatListOutput(result) {
158
+ // 生成当前时间戳
159
+ const now = new Date();
160
+ const formattedDate = `${now.getFullYear()}/${(now.getMonth() + 1).toString().padStart(2, '0')}/${now.getDate().toString().padStart(2, '0')} ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
161
+
162
+ // 构建新的输出格式
163
+ let output = "";
164
+ output += "[PROMPT_HEADER_AREA]\n";
165
+ output += "🎭 **PromptManager 提示词清单**\n";
166
+ output += `📅 ${formattedDate}\n\n`;
167
+ output += "--------------------------------------------------\n";
168
+ output += "[PROMPT_LIST_AREA]\n\n";
169
+ output += `📦 **提示词列表** (${result.count}个)\n`;
170
+
171
+ // 添加提示词列表
172
+ if (result.results && Array.isArray(result.results) && result.results.length > 0) {
173
+ result.results.forEach(prompt => {
174
+ output += `- [${prompt.id}] ${prompt.name}\n`;
175
+ output += ` - ${prompt.description}\n`;
176
+ });
177
+ } else {
178
+ output += "(无提示词)\n";
179
+ }
180
+
181
+ output += "\n--------------------------------------------------\n";
182
+ output += "[STATE_AREA]\n";
183
+ output += "📍 **当前状态**:prompts_completed\n";
184
+
185
+ // 返回格式化文本
186
+ return output;
187
+ }
188
+
189
+ /**
190
+ * 处理详情格式输出
191
+ * @param {*} result
192
+ * @returns string
193
+ */
194
+ function formatDetailOutput(result) {
195
+ const prompt = result.prompt;
196
+
197
+ // 生成当前时间戳
198
+ const now = new Date();
199
+ const formattedDate = `${now.getFullYear()}/${(now.getMonth() + 1).toString().padStart(2, '0')}/${now.getDate().toString().padStart(2, '0')} ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
200
+
201
+ // 构建新的输出格式
202
+ let output = "";
203
+ output += "--------------------------------------------------\n";
204
+ output += "[PROMPT_HEADER_AREA]\n";
205
+ output += `- id: ${prompt.id}\n`;
206
+ output += `- name: ${prompt.name}\n`;
207
+ output += `- description: ${prompt.description}\n`;
208
+ output += `- filepath: ${prompt.filePath}\n\n`;
209
+ output += "--------------------------------------------------\n";
210
+ output += "[PROMPT_PARAMS_AREA]\n";
211
+
212
+ // 添加参数信息
213
+ if (prompt.arguments && Array.isArray(prompt.arguments) && prompt.arguments.length > 0) {
214
+ prompt.arguments.forEach(param => {
215
+ const requiredText = param.required ? "必填" : "非必填";
216
+ output += `- ${param.name}: ${param.type}; ${requiredText}; ${param.description}\n`;
217
+ });
218
+ } else {
219
+ output += "(无参数)\n";
220
+ }
221
+
222
+ output += "\n--------------------------------------------------\n";
223
+ output += "[PROMPT_CONTENT_AREA]\n";
224
+
225
+ // 添加消息内容
226
+ if (prompt.messages && Array.isArray(prompt.messages)) {
227
+ const userMessages = prompt.messages.filter(msg => msg.role !== "");
228
+ if (userMessages.length > 0 && userMessages[0].content && userMessages[0].content.text) {
229
+ output += userMessages[0].content.text + "\n";
230
+ }
231
+ }
232
+
233
+ output += "\n[STATE_AREA]\n";
234
+ output += "📍 **当前状态**:prompt_loaded\n";
235
+
236
+ return output;
237
+ }
238
+
239
+ /**
240
+ * 将对象转换为格式化的text类型输出
241
+ * @param {*} result
242
+ * @param {string} format - 输出格式类型: 'list' 或 'detail'
155
243
  * @returns
156
244
  */
157
- function convertToText(result) {
245
+ function convertToText(result, format) {
246
+ let ret = ""
247
+ switch (format) {
248
+ case 'list':
249
+ ret = formatListOutput(result);
250
+ break;
251
+ case 'detail':
252
+ ret = formatDetailOutput(result);
253
+ break;
254
+ default:
255
+ ret =JSON.stringify(result, null, 2);
256
+ }
158
257
  return {
159
258
  content: [
160
259
  {
161
260
  type: "text",
162
- text: JSON.stringify(result, null, 2)
261
+ text: ret
163
262
  }
164
263
  ]
165
264
  };
166
- }
265
+ }
@@ -1,10 +1,12 @@
1
1
  import { config } from '../utils/config.js';
2
+ import { z } from 'zod';
2
3
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
4
  import {
4
5
  handleGetPrompt,
5
6
  handleSearchPrompts,
6
7
  handleReloadPrompts
7
8
  } from './mcp.handler.js';
9
+ import { handleToolX } from './toolx.handler.js';
8
10
 
9
11
  class Server {
10
12
  constructor() {
@@ -19,7 +21,13 @@ class Server {
19
21
 
20
22
  registerTools(tools) {
21
23
  for (const tool of tools) {
22
- this.server.tool(tool.name, tool.description, tool.inputSchema, tool.handler);
24
+ this.server.registerTool(tool.name,
25
+ {
26
+ description: tool.description,
27
+ inputSchema: tool.inputSchema,
28
+ },
29
+ tool.handler
30
+ );
23
31
  }
24
32
  }
25
33
 
@@ -31,48 +39,139 @@ class Server {
31
39
  export const getMcpServer = () => {
32
40
  const mcpServer = new Server();
33
41
  mcpServer.registerTools([
42
+ {
43
+ name: 'search_prompts',
44
+ description: `功能:智能检索提示词库,匹配用户需求\n描述:根据用户输入内容(可为空)搜索匹配的提示词,返回候选提示词的 ID、名称、简短描述。若输入为空则返回全部提示词列表。帮助用户快速定位适合的提示词,无需记忆具体名称。\n\n示例:\n- 用户:"我想写一首诗" → 工具返回:[ID:001, 名称:诗歌创作, 描述:生成古典/现代风格诗歌]\n- 用户:"(无输入)" → 工具返回:完整提示词库概览`,
45
+ inputSchema: {
46
+ name: z.string().optional().describe('提示词名称或关键词,用于搜索匹配提示词'),
47
+ },
48
+ handler: async (args) => {
49
+ return handleSearchPrompts(args);
50
+ }
51
+ },
34
52
  {
35
53
  name: 'get_prompt',
36
- description: 'Retrieve the complete content of a specific prompt by its ID, including all messages, arguments, and metadata.',
54
+ description: `功能:精准获取并应用提示词详情\n描述:根据提示词 ID 或名称 调用具体内容,自动将其嵌入当前对话上下文,无需用户手动复制。支持通过 search_prompts 返回的 ID/名称直接获取。\n\n示例:\n- 用户:"使用 ID 001" 工具自动加载诗歌创作提示词并生成内容\n- 用户:"调用'营销文案生成'" 工具匹配名称后应用对应提示词`,
37
55
  inputSchema: {
38
- type: 'object',
39
- properties: {
40
- prompt_id: {
41
- type: 'string',
42
- description: 'the unique identifier of the prompt to retrieve'
43
- }
44
- },
45
- required: ['prompt_id']
56
+ prompt_id: z.string().describe('提示词的唯一标识 ID/名称'),
46
57
  },
47
58
  handler: async (args) => {
48
59
  return handleGetPrompt(args);
49
60
  }
50
61
  },
51
62
  {
52
- name: 'search_prompts',
53
- description: 'For keyword search matching, retrieve or return all prompts. When a corresponding prompt word is matched, utilize the prompt ID to invoke the get_prompt tool to query the complete content of the prompt word.',
63
+ name: 'toolx',
64
+ description: `ToolX is the Prompt Manager tool runtime for loading and executing various tools.
65
+
66
+ ## Why ToolX Exists
67
+
68
+ ToolX is your gateway to the Prompt Manager tool ecosystem. Think of it as:
69
+ - A **universal remote control** for all Prompt Manager tools
70
+ - Your **interface** to specialized capabilities (file operations, etc.)
71
+ - The **bridge** between you (AI agent) and powerful system tools
72
+
73
+ Without ToolX, you cannot access any Prompt Manager ecosystem tools.
74
+
75
+ ## When to Use ToolX
76
+
77
+ ### Common Scenarios (IF-THEN rules):
78
+ - IF user needs file operations → USE tool://filesystem via toolx
79
+ - IF you see tool:// in any context → USE toolx to call it
80
+
81
+ ### First Time Using Any Tool
82
+ ⚠️ **MUST run mode: manual first** to read the tool's documentation
83
+ ⚠️ Example: toolx with mode: manual for tool://filesystem
84
+
85
+ ## How to Use ToolX (Copy These Patterns)
86
+
87
+ ### Pattern 1: Read Tool Manual (First Time)
88
+
89
+ **Exact code to use:**
90
+ \`\`\`javascript
91
+ // Call the toolx function with this exact structure:
92
+ toolx({
93
+ yaml: \`tool: tool://filesystem\nmode: manual\`
94
+ })
95
+ \`\`\`
96
+
97
+ **What this does:** Shows you how to use the filesystem tool
98
+
99
+ ### Pattern 2: Execute Tool with Parameters
100
+
101
+ **Exact code to use:**
102
+ \`\`\`javascript
103
+ toolx({
104
+ yaml: \`tool: tool://filesystem\nmode: execute\nparameters:\n path: /path/to/file.txt\n action: read\`
105
+ })
106
+ \`\`\`
107
+
108
+ **What this does:** Reads a file at the specified path
109
+
110
+ ### Pattern 3: Configure Tool Environment
111
+
112
+ **Exact code to use:**
113
+ \`\`\`javascript
114
+ toolx({
115
+ yaml: \`tool: tool://my-tool\nmode: configure\nparameters:\n API_KEY: sk-xxx123\n TIMEOUT: 30000\`
116
+ })
117
+ \`\`\`
118
+
119
+ **What this does:** Sets environment variables for the tool
120
+
121
+ ## Critical Rules (Must Follow)
122
+
123
+ ### ✅ Correct Format
124
+ The yaml parameter MUST be a complete YAML document:
125
+ - Start with \`tool: tool://tool-name\`
126
+ - Add \`mode: execute\` (or manual/configure)
127
+ - If needed, add \`parameters:\` section with proper indentation
128
+
129
+ ### ❌ Common Mistakes to Avoid
130
+ - DON'T pass just "tool://filesystem" (missing YAML structure)
131
+ - DON'T add @ prefix like "@tool://filesystem" (system handles it)
132
+ - DON'T forget "tool://" prefix (not "tool: filesystem")
133
+ - DON'T forget to read manual first for new tools
134
+
135
+ ## Available System Tools
136
+
137
+ Quick reference of built-in tools:
138
+ - **tool://filesystem** - File operations (read/write/list/delete)
139
+
140
+ To see all available tools: check the tools directory
141
+
142
+ ## Step-by-Step Workflow
143
+
144
+ ### Step 1: Discover Available Tools
145
+ Check the tools directory to see what tools exist
146
+
147
+ ### Step 2: Read Tool Manual
148
+ \`\`\`javascript
149
+ toolx({
150
+ yaml: \`tool: tool://TOOLNAME\nmode: manual\`
151
+ })
152
+ \`\`\`
153
+
154
+ ### Step 3: Execute Tool
155
+ Copy the example from manual, modify parameters for your needs
156
+
157
+ ### Step 4: Handle Errors
158
+ If execution fails, check:
159
+ - Is the tool name correct?
160
+ - Are parameters properly indented?
161
+ - Did you read the manual first?
162
+
163
+ `,
54
164
  inputSchema: {
55
- type: 'object',
56
- properties: {
57
- name: {
58
- type: 'string',
59
- description: 'Optional name filter for prompts'
60
- }
61
- },
62
- required: []
165
+ yaml: z.string().describe('YAML 格式的工具调用配置')
63
166
  },
64
167
  handler: async (args) => {
65
- return handleSearchPrompts(args);
168
+ return handleToolX(args);
66
169
  }
67
- },
170
+ }
68
171
  // {
69
172
  // name: 'reload_prompts',
70
173
  // description: 'Force a reload of all preset prompts to overwrite the cache.',
71
- // inputSchema: {
72
- // type: 'object',
73
- // properties: {},
74
- // required: []
75
- // },
174
+ // inputSchema: {},
76
175
  // handler: async (args) => {
77
176
  // return handleReloadPrompts(args);
78
177
  // }
@@ -0,0 +1,131 @@
1
+ // 导入自定义模块
2
+ import { config } from '../utils/config.js';
3
+ import { logger } from '../utils/logger.js';
4
+ import { util } from '../utils/util.js';
5
+ import fs from 'fs-extra';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+ import YAML from 'yaml';
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+
13
+ // 处理 toolx 工具调用
14
+ export async function handleToolX(args) {
15
+ const { yaml: yamlInput } = args;
16
+
17
+ if (!yamlInput) {
18
+ throw new Error("缺少必需参数: yaml");
19
+ }
20
+
21
+ try {
22
+ // Auto-correct common AI mistakes
23
+ let yamlContent = yamlInput.trim();
24
+
25
+ // Case 1: Just a plain URL string like "tool://filesystem" or "@tool://filesystem"
26
+ if (yamlContent.match(/^@?tool:\/\/[\w-]+$/)) {
27
+ const toolName = yamlContent.replace(/^@?tool:\/\//, '');
28
+ yamlContent = `tool: tool://${toolName}\nmode: execute`;
29
+ }
30
+
31
+ // Case 2: Handle escaped backslashes and quotes: tool: \"@tool://xxx\"
32
+ // This happens when AI generates YAML in a JSON string
33
+ yamlContent = yamlContent.replace(/\\\\/g, '\\').replace(/\\"/g, '"');
34
+
35
+ // Case 3: Remove @ prefix from tool URLs: @tool://xxx -> tool://xxx
36
+ yamlContent = yamlContent.replace(/@tool:\/\//g, 'tool://');
37
+
38
+ // Case 4: Remove quotes around tool URLs: tool: "tool://xxx" -> tool: tool://xxx
39
+ yamlContent = yamlContent.replace(/(tool|toolUrl|url):\s*"(tool:\/\/[^\"]+)"/g, '$1: $2');
40
+
41
+ // YAML → JSON conversion
42
+ const configObj = YAML.parse(yamlContent);
43
+
44
+ // Normalize field names (support aliases for AI-friendliness)
45
+ // Priority: tool > toolUrl > url
46
+ const toolIdentifier = configObj.tool || configObj.toolUrl || configObj.url;
47
+
48
+ // Priority: mode > operation
49
+ const operationMode = configObj.mode || configObj.operation;
50
+
51
+ // Validate required fields
52
+ if (!toolIdentifier) {
53
+ throw new Error('Missing required field: tool\nExample: tool: tool://filesystem\nAliases supported: tool / toolUrl / url');
54
+ }
55
+
56
+ // Validate URL format
57
+ if (!toolIdentifier.startsWith('tool://')) {
58
+ throw new Error(`Invalid tool format: ${toolIdentifier}\nMust start with tool://`);
59
+ }
60
+
61
+ // Get tool name
62
+ const toolName = toolIdentifier.replace('tool://', '');
63
+
64
+ // Check if tool exists in our local tools directory
65
+ const rootDir = path.join(__dirname, '..', '..', '..');
66
+ const toolsDir = path.join(rootDir, 'packages', 'resources', 'tools');
67
+ const toolPath = path.join(toolsDir, `${toolName}`, `${toolName}.tool.js`);
68
+
69
+ if (!fs.existsSync(toolPath)) {
70
+ // List available tools
71
+ const availableTools = await fs.readdir(toolsDir);
72
+ const toolList = availableTools.filter(file => file.endsWith('.js')).map(file => path.basename(file, '.js'));
73
+
74
+ throw new Error(`Tool '${toolName}' not found\nAvailable tools: ${toolList.join(', ')}\nTools are located in: ${toolsDir}`);
75
+ }
76
+
77
+ // Load and execute the tool
78
+ const toolModule = await import(toolPath);
79
+ const toolFunction = toolModule.default || toolModule.execute || toolModule.run;
80
+
81
+ if (typeof toolFunction !== 'function') {
82
+ throw new Error(`Tool '${toolName}' does not export a valid function`);
83
+ }
84
+
85
+ // Execute the tool with provided parameters
86
+ const result = await toolFunction(configObj.parameters || {}, operationMode || 'execute');
87
+
88
+ // 如果是 manual 模式,直接返回 Markdown 格式的手册
89
+ if (operationMode === 'manual') {
90
+ return {
91
+ content: [
92
+ {
93
+ type: "text",
94
+ text: result
95
+ }
96
+ ]
97
+ };
98
+ }
99
+
100
+ // 其他模式返回 JSON 格式的结果
101
+ return {
102
+ content: [
103
+ {
104
+ type: "text",
105
+ text: JSON.stringify({
106
+ success: true,
107
+ tool: toolName,
108
+ mode: operationMode || 'execute',
109
+ result: result
110
+ }, null, 2)
111
+ }
112
+ ]
113
+ };
114
+ } catch (error) {
115
+ // YAML parsing errors
116
+ if (error.name === 'YAMLException') {
117
+ // Check for multiline string issues
118
+ if (error.message.includes('bad indentation') || error.message.includes('mapping entry')) {
119
+ throw new Error(`YAML format error: ${error.message}\n\nMultiline content requires | symbol, example:\ncontent: |\n First line\n Second line\n\nNote: Newline after |, indent content with 2 spaces`);
120
+ }
121
+ throw new Error(`YAML format error: ${error.message}\nCheck indentation (use spaces) and syntax`);
122
+ }
123
+
124
+ // Tool not found
125
+ if (error.message?.includes('Tool not found')) {
126
+ throw new Error(error.message);
127
+ }
128
+
129
+ throw error;
130
+ }
131
+ }
@@ -95,7 +95,7 @@ export class Config {
95
95
 
96
96
  // 其他配置
97
97
  this.serverName = process.env.MCP_SERVER_NAME || 'prompt-manager';
98
- this.serverVersion = process.env.MCP_SERVER_VERSION || '0.0.18';
98
+ this.serverVersion = process.env.MCP_SERVER_VERSION || '0.0.19';
99
99
  this.logLevel = process.env.LOG_LEVEL || 'info';
100
100
  this.maxPrompts = parseInt(process.env.MAX_PROMPTS) || 1000;
101
101
  this.recursiveScan = process.env.RECURSIVE_SCAN !== 'false'; // 默认启用递归扫描