UNPKG

dpml-prompt

Version:

DPML-powered AI prompt framework - Revolutionary AI-First CLI system based on Deepractice Prompt Markup Language. Build sophisticated AI agents with structured prompts, memory systems, and execution frameworks.

490 lines (412 loc) 14.2 kB
const BasePouchCommand = require('../BasePouchCommand') const fs = require('fs-extra') const path = require('path') const { getGlobalResourceManager } = require('../../resource') const ProjectManager = require('../../../utils/ProjectManager') const { getGlobalProjectManager } = require('../../../utils/ProjectManager') const { getGlobalServerEnvironment } = require('../../../utils/ServerEnvironment') const logger = require('../../../utils/logger') /** * 角色欢迎锦囊命令 * 负责展示可用的AI角色和领域专家 */ class WelcomeCommand extends BasePouchCommand { constructor () { super() // 使用全局单例 ResourceManager this.resourceManager = getGlobalResourceManager() this.projectManager = getGlobalProjectManager() } getPurpose () { return '为AI提供可用角色和工具信息,以便AI向主人汇报专业服务选项' } /** * 动态加载角色注册表 - 使用新的RegistryData架构 */ async loadRoleRegistry () { // 确保ResourceManager已初始化 if (!this.resourceManager.initialized) { await this.resourceManager.initializeWithNewArchitecture() } // 直接使用ResourceManager的注册表,无需重复处理 return this.resourceManager.registryData.getResourcesByProtocol('role') } /** * 动态加载工具注册表 */ async loadToolRegistry () { // 确保ResourceManager已初始化 if (!this.resourceManager.initialized) { await this.resourceManager.initializeWithNewArchitecture() } // 获取tool和manual资源 const tools = this.resourceManager.registryData.getResourcesByProtocol('tool') const manuals = this.resourceManager.registryData.getResourcesByProtocol('manual') // 将工具和手册关联起来,保留source信息 const toolsWithManuals = {} tools.forEach(tool => { const manual = manuals.find(m => m.id === tool.id && m.source === tool.source) toolsWithManuals[tool.id] = { id: tool.id, name: tool.name || tool.id, description: tool.description || '工具功能描述', source: tool.source || 'unknown', reference: tool.reference, manual: manual ? `@manual://${manual.id}` : null } }) return toolsWithManuals } /** * 从角色内容中提取角色名称 * @param {string} content - 角色文件内容 * @returns {string|null} 角色名称 */ extractRoleNameFromContent(content) { if (!content || typeof content !== 'string') { return null } // 提取Markdown标题 const match = content.match(/^#\s*(.+)$/m) return match ? match[1].trim() : null } /** * 从角色内容中提取描述 * @param {string} content - 角色文件内容 * @returns {string|null} 角色描述 */ extractDescriptionFromContent(content) { if (!content || typeof content !== 'string') { return null } // 提取Markdown引用(描述) const match = content.match(/^>\s*(.+)$/m) return match ? match[1].trim() : null } /** * 从角色信息中提取描述(保持向后兼容) * @param {Object} roleInfo - 角色信息对象 * @returns {string} 角色描述 */ extractDescription(roleInfo) { // 尝试从不同字段提取描述 if (roleInfo.description) { return roleInfo.description } // 如果有更多元数据,可以在这里扩展提取逻辑 return null } /** * 获取所有角色列表(转换为数组格式) */ async getAllRoles () { const registry = await this.loadRoleRegistry() return Object.entries(registry).map(([id, roleInfo]) => ({ id, name: roleInfo.name, description: roleInfo.description, file: roleInfo.file, source: roleInfo.source })) } /** * 获取来源标签 * @param {string} source - 资源来源 * @param {string} type - 资源类型 ('role' 或 'tool') * @returns {string} 来源标签 */ getSourceLabel(source, type = 'role') { if (type === 'tool') { switch (source) { case 'package': return '📦 系统工具' case 'project': return '🏗️ 项目工具' case 'user': return '👤 用户工具' case 'merged': return '📦 系统工具' default: return '❓ 未知来源' } } // 角色标签 switch (source) { case 'package': return '📦 系统角色' case 'project': return '🏗️ 项目角色' case 'user': return '👤 用户角色' case 'merged': return '📦 系统角色' // merged来源的资源主要来自package case 'fallback': return '🔄 默认角色' default: return '❓ 未知来源' } } async getContent (args) { const roleRegistry = await this.loadRoleRegistry() const toolRegistry = await this.loadToolRegistry() const allRoles = Object.values(roleRegistry) const allTools = Object.values(toolRegistry) const totalRoles = allRoles.length const totalTools = allTools.length let content = `🤖 **AI专业服务清单** (共 ${totalRoles} 个专业角色 + ${totalTools} 个工具可供使用) > 💡 **使用说明**:以下是可激活的AI专业角色和可调用的工具。每个都有唯一的ID,可通过MCP工具使用。 ## 📋 可用角色列表 ` // 按来源分组显示角色 const rolesBySource = {} allRoles.forEach(role => { const source = role.source || 'unknown' if (!rolesBySource[source]) { rolesBySource[source] = [] } rolesBySource[source].push(role) }) let roleIndex = 1 // 优先显示系统角色 const sourceOrder = ['package', 'merged', 'project', 'user', 'fallback', 'unknown'] for (const source of sourceOrder) { if (!rolesBySource[source] || rolesBySource[source].length === 0) continue const sourceLabel = this.getSourceLabel(source) content += `### ${sourceLabel}\n\n` rolesBySource[source].forEach(role => { content += `#### ${roleIndex}. \`${role.id}\` - ${role.name} **专业能力**: ${role.description} **来源**: ${sourceLabel} --- ` roleIndex++ }) } // 添加工具列表 content += ` ## 🔧 可用工具列表 ` // 按来源分组显示工具 const toolsBySource = {} allTools.forEach(tool => { const source = tool.source || 'unknown' if (!toolsBySource[source]) { toolsBySource[source] = [] } toolsBySource[source].push(tool) }) let toolIndex = 1 for (const source of sourceOrder) { if (!toolsBySource[source] || toolsBySource[source].length === 0) continue const sourceLabel = this.getSourceLabel(source, 'tool') content += `### ${sourceLabel}\n\n` toolsBySource[source].forEach(tool => { content += `#### ${toolIndex}. \`${tool.id}\` - ${tool.name} **功能描述**: ${tool.description} **使用手册**: ${tool.manual || '暂无手册'} **来源**: ${sourceLabel} --- ` toolIndex++ }) } content += ` ## 🎯 **使用指南** ### 📋 角色激活 - 使用 **MCP PromptX 工具** 中的 \`action\` 功能 - 选择需要的角色ID进行激活 - 激活后AI将具备该角色的专业技能 ### 🔧 工具使用 - **第一步**:通过 \`@manual://tool-name\` 查看工具手册 - **第二步**:理解工具功能和参数要求 - **第三步**:使用 \`promptx_tool\` 执行工具 - **重要**:禁止在未阅读手册的情况下使用工具! ### ⚡ 效果说明 - ✅ **角色激活** - 获得专业思维和技能 - ✅ **工具调用** - 执行具体的功能操作 - ✅ **安全使用** - 先读手册,再用工具 ` return content } async getPATEOAS (args) { const allRoles = await this.getAllRoles() const toolRegistry = await this.loadToolRegistry() const allTools = Object.values(toolRegistry) const availableRoles = allRoles.map(role => ({ roleId: role.id, name: role.name, source: role.source })) const availableTools = allTools.map(tool => ({ toolId: tool.id, name: tool.name, source: tool.source, manual: tool.manual })) return { currentState: 'service_discovery', availableTransitions: ['action', 'learn', 'init', 'recall', 'tool'], nextActions: [ { name: '向主人汇报服务选项', description: '将上述专业角色和工具清单告知主人,并询问需求', method: 'MCP PromptX action/tool 工具', priority: 'critical', instruction: '必须先询问主人需求,不要自主选择角色或工具' }, { name: '工具使用流程', description: '如需使用工具,必须先查看manual手册', method: '1. 查看@manual://tool-name 2. 使用promptx_tool', priority: 'high', instruction: '严格遵循先读手册后使用的原则' } ], metadata: { totalRoles: allRoles.length, totalTools: allTools.length, availableRoles, availableTools, dataSource: 'RegistryData v2.0', systemVersion: '锦囊串联状态机 v1.0', designPhilosophy: 'AI use MCP tools for role activation and tool execution' } } } /** * 获取角色信息(提供给其他命令使用) */ async getRoleInfo (roleId) { logger.debug(`[WelcomeCommand] getRoleInfo调用,角色ID: ${roleId}`) const registry = await this.loadRoleRegistry() logger.debug(`[WelcomeCommand] 注册表加载完成,包含角色:`, Object.keys(registry)) const roleData = registry[roleId] logger.debug(`[WelcomeCommand] 查找角色${roleId}结果:`, roleData ? '找到' : '未找到') if (!roleData) { logger.debug(`[WelcomeCommand] 角色${roleId}在注册表中不存在`) return null } const result = { id: roleId, name: roleData.name, description: roleData.description, file: roleData.file } logger.debug(`[WelcomeCommand] 返回角色信息:`, result) return result } /** * 未来扩展:动态角色发现 * TODO: 实现真正的文件扫描和解析 */ async discoverAvailableDomains () { // 现在基于注册表返回角色ID列表 const allRoles = await this.getAllRoles() return allRoles.map(role => role.id) } /** * 注意:原来的discoverLocalRoles方法已被移除 * 现在使用SimplifiedRoleDiscovery.discoverAllRoles()替代 * 这避免了glob依赖和跨平台兼容性问题 */ /** * 调试方法:打印所有注册的资源 */ async debugRegistry() { await this.loadRoleRegistry() logger.info('\n🔍 WelcomeCommand - 注册表调试信息') logger.info('='.repeat(50)) if (this.roleRegistry && Object.keys(this.roleRegistry).length > 0) { logger.info(`📊 发现 ${Object.keys(this.roleRegistry).length} 个角色资源:\n`) Object.entries(this.roleRegistry).forEach(([id, roleInfo]) => { logger.info(`🎭 ${id}`) logger.info(` 名称: ${roleInfo.name || '未命名'}`) logger.info(` 描述: ${roleInfo.description || '无描述'}`) logger.info(` 文件: ${roleInfo.file}`) logger.info(` 来源: ${roleInfo.source || '未知'}`) logger.info('') }) } else { logger.info('🔍 没有发现任何角色资源') } // 显示RegistryData统计信息 logger.info('\n📋 RegistryData 统计信息:') if (this.resourceManager && this.resourceManager.registryData) { const stats = this.resourceManager.registryData.getStats() logger.info(`总资源数: ${stats.totalResources}`) logger.info(`按协议分布: ${JSON.stringify(stats.byProtocol, null, 2)}`) logger.info(`按来源分布: ${JSON.stringify(stats.bySource, null, 2)}`) } else { logger.info('❌ RegistryData 不可用') } } /** * 重写execute方法以添加多项目状态检查 */ async execute (args = []) { // 从执行上下文获取MCP信息 const mcpId = this.detectMcpId() const ideType = await this.detectIdeType() // 获取多项目状态提示 const projectPrompt = await this.projectManager.generateTopLevelProjectPrompt('list', mcpId, ideType) const purpose = this.getPurpose() const content = await this.getContent(args) const pateoas = await this.getPATEOAS(args) return this.formatOutputWithProjectCheck(purpose, content, pateoas, projectPrompt) } /** * 检测MCP进程ID */ detectMcpId() { const serverEnv = getGlobalServerEnvironment() if (serverEnv.isInitialized()) { return serverEnv.getMcpId() } return ProjectManager.generateMcpId() } /** * 检测IDE类型 - 从配置文件读取,移除环境变量检测 */ async detectIdeType() { const mcpId = this.detectMcpId() return await this.projectManager.getIdeType(mcpId) } /** * 格式化带有项目检查的输出 */ formatOutputWithProjectCheck(purpose, content, pateoas, projectPrompt) { const output = { purpose, content, pateoas, context: this.context, format: this.outputFormat, projectPrompt } if (this.outputFormat === 'json') { return output } // 人类可读格式 return { ...output, toString () { const divider = '='.repeat(60) const nextSteps = (pateoas.nextActions || []) .map(action => ` - ${action.name}: ${action.description}\n 方式: ${action.method || action.command || '通过MCP工具'}`) .join('\n') return `${projectPrompt} ${divider} 🎯 锦囊目的:${purpose} ${divider} 📜 锦囊内容: ${content} 🔄 下一步行动: ${nextSteps} 📍 当前状态:${pateoas.currentState} ${divider} ` } } } } module.exports = WelcomeCommand