/** * marketplace-tools.js — 市场技能搜索 + 动态调用工具 * * 为 Master Agent 提供搜索和动态调用 27,000+ 市场技能的能力。 * 核心系统 agent 直接列在 prompt 中;市场技能通过 搜索 → 选择 → 调用 的方式使用。 */ const fs = require('fs'); const path = require('path'); /** * search_marketplace_skills — 搜索市场技能 */ function createSearchSkillsTool() { return { name: 'search_marketplace_skills', description: `在 ArenaSK 市场中搜索技能(27,000+ 可用)。返回匹配的技能列表(名称、描述、ELO 评分等)。 使用场景: - 用户需要某个特定领域的技能时,先搜索找到最合适的 - 不确定是否存在某个技能时,先搜索确认 - 需要对比多个同类技能时 搜索后,如果找到合适的技能,可以用 call_marketplace_skill 工具动态调用它。`, parameters: { type: 'object', required: ['query'], properties: { query: { type: 'string', description: '搜索关键词(支持技能名、描述、标签等)', }, limit: { type: 'number', description: '返回结果数量上限(默认 10,最大 20)', }, }, }, async execute(params) { const { query, limit = 10 } = params; const skillDb = require('../../services/skill-db'); const maxLimit = Math.min(limit || 10, 20); const results = skillDb.searchSkills(query, { limit: maxLimit }); return { query, count: results.length, skills: results.map(s => ({ name: s.skillName, description: (s.description || '').substring(0, 200), domain: s.domain || '', keywords: s.keywords || '', downloadCount: s.downloadCount || 0, })), hint: results.length > 0 ? '找到匹配技能。使用 call_marketplace_skill 工具可以动态调用其中一个。' : '未找到匹配技能。尝试不同的搜索关键词。', }; }, }; } /** * call_marketplace_skill — 动态调用市场技能 */ function createCallMarketplaceSkillTool() { return { name: 'call_marketplace_skill', description: `动态调用一个市场技能(先用 search_marketplace_skills 搜索找到技能名,再用此工具调用)。 系统会自动加载该技能的 SKILL.md,创建临时 Claude CLI session 执行任务。 使用流程: 1. 先用 search_marketplace_skills 搜索技能 2. 从搜索结果中选择合适的技能 3. 用此工具调用该技能,传入任务描述`, parameters: { type: 'object', required: ['skillName', 'task'], properties: { skillName: { type: 'string', description: '要调用的技能名称(从搜索结果中获取)', }, task: { type: 'string', description: '给该技能的任务描述 / 用户输入', }, }, }, async execute(params, context) { const { skillName, task } = params; const { agentManager, onSubAgentData } = context; const skillDb = require('../../services/skill-db'); const { resolveSkillDir } = require('../../services/skill-dir-manager'); // 1. 验证技能存在 const skill = skillDb.getSkill(skillName); if (!skill) { throw new Error(`技能 "${skillName}" 不存在。请先用 search_marketplace_skills 搜索。`); } // 2. 查找 SKILL.md const skillDir = resolveSkillDir(skillName) || skill.directory; if (!skillDir) { throw new Error(`技能 "${skillName}" 的目录未找到。`); } const skillMdPath = path.join(skillDir, 'SKILL.md'); if (!fs.existsSync(skillMdPath)) { throw new Error(`技能 "${skillName}" 的 SKILL.md 文件不存在: ${skillMdPath}`); } // 3. 读取 SKILL.md 作为 system prompt const skillMd = fs.readFileSync(skillMdPath, 'utf-8'); // 限制 SKILL.md 大小(避免超大文件) const MAX_SKILL_SIZE = 100000; // 100KB if (skillMd.length > MAX_SKILL_SIZE) { throw new Error(`技能 "${skillName}" 的 SKILL.md 太大 (${skillMd.length} chars),超过 ${MAX_SKILL_SIZE} 限制。`); } // 4. 构建完整 prompt: SKILL.md + 用户任务 const fullPrompt = `${skillMd}\n\n---\n\n## 用户任务\n\n${task}`; // 5. 检查 agentManager 中是否已有该 agent 配置 // 如果没有,临时注册到 config 中 if (!agentManager) { throw new Error('call_marketplace_skill 需要 agentManager'); } let agentName = skillName; try { agentManager.getAgentConfig(agentName); } catch { // 临时注册该 agent 到 config const config = context.config; if (config && config.agents) { config.agents[agentName] = { type: 'executor', protocol: 'claude-cli', transport: 'pipe', launcher: 'claude-local', client: 'claude-local', prompt: skillMdPath, description: skill.description || '', }; } } // 6. 调用 agent try { const result = await agentManager.callAgent(agentName, fullPrompt, { onData: (chunk) => { if (onSubAgentData && chunk && chunk.trim()) { onSubAgentData(agentName, chunk); } }, }); return { skillName, status: 'success', output: result.output || '', }; } catch (err) { return { skillName, status: 'error', error: err.message, }; } }, }; } module.exports = { createSearchSkillsTool, createCallMarketplaceSkillTool };