删除不再使用的bootstrap.md文件,更新promptx.js、MCPStreamableHttpCommand.js等文件以使用logger进行日志记录,重构资源管理和发现逻辑,确保代码一致性和可维护性。
This commit is contained in:
@ -3,6 +3,7 @@
|
||||
const { Command } = require('commander')
|
||||
const chalk = require('chalk')
|
||||
const packageJson = require('../../package.json')
|
||||
const logger = require('../lib/utils/logger')
|
||||
|
||||
// 导入锦囊框架
|
||||
const { cli } = require('../lib/core/pouch')
|
||||
@ -92,14 +93,14 @@ program
|
||||
cors: options.cors
|
||||
};
|
||||
|
||||
console.error(chalk.green(`🚀 启动 ${options.transport.toUpperCase()} MCP Server 在 ${options.host}:${options.port}...`));
|
||||
logger.info(chalk.green(`🚀 启动 ${options.transport.toUpperCase()} MCP Server 在 ${options.host}:${options.port}...`));
|
||||
await mcpHttpServer.execute(serverOptions);
|
||||
} else {
|
||||
throw new Error(`不支持的传输类型: ${options.transport}。支持的类型: stdio, http, sse`);
|
||||
}
|
||||
} catch (error) {
|
||||
// 输出到stderr,不污染MCP的stdout通信
|
||||
console.error(chalk.red(`❌ MCP Server 启动失败: ${error.message}`));
|
||||
logger.error(chalk.red(`❌ MCP Server 启动失败: ${error.message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
})
|
||||
@ -174,8 +175,8 @@ ${chalk.cyan('更多信息:')}
|
||||
|
||||
// 处理未知命令
|
||||
program.on('command:*', () => {
|
||||
console.error(chalk.red(`错误: 未知命令 '${program.args.join(' ')}'`))
|
||||
console.log('')
|
||||
logger.error(chalk.red(`错误: 未知命令 '${program.args.join(' ')}'`))
|
||||
logger.info('')
|
||||
program.help()
|
||||
})
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ const { SSEServerTransport } = require('@modelcontextprotocol/sdk/server/sse.js'
|
||||
const { isInitializeRequest } = require('@modelcontextprotocol/sdk/types.js');
|
||||
const { cli } = require('../core/pouch');
|
||||
const { MCPOutputAdapter } = require('../adapters/MCPOutputAdapter');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
/**
|
||||
* MCP Streamable HTTP Server Command
|
||||
@ -579,7 +580,7 @@ class MCPStreamableHttpCommand {
|
||||
*/
|
||||
log(message, ...args) {
|
||||
if (this.debug) {
|
||||
console.error(`[MCP DEBUG] ${message}`, ...args);
|
||||
logger.debug(`[MCP DEBUG] ${message}`, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
{
|
||||
"version": "0.0.1",
|
||||
"initialized": "2025-05-31T06:15:39.372Z",
|
||||
"defaultFormat": "human",
|
||||
"stateHistory": [
|
||||
{
|
||||
"from": "initial",
|
||||
"to": "init",
|
||||
"timestamp": "2025-05-31T06:15:39.370Z",
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"from": "init",
|
||||
"to": "hello",
|
||||
"timestamp": "2025-05-31T06:15:39.373Z",
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"from": "hello",
|
||||
"command": "init",
|
||||
"timestamp": "2025-05-31T06:16:28.040Z",
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"from": "initialized",
|
||||
"command": "hello",
|
||||
"timestamp": "2025-05-31T06:16:28.042Z",
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"from": "discovering",
|
||||
"command": "action",
|
||||
"timestamp": "2025-05-31T06:16:28.042Z",
|
||||
"args": [
|
||||
"copywriter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"from": "activated-copywriter",
|
||||
"command": "learn",
|
||||
"timestamp": "2025-05-31T06:16:28.046Z",
|
||||
"args": [
|
||||
"scrum"
|
||||
]
|
||||
},
|
||||
{
|
||||
"from": "learned-scrum",
|
||||
"command": "recall",
|
||||
"timestamp": "2025-05-31T06:16:28.047Z",
|
||||
"args": [
|
||||
"test"
|
||||
]
|
||||
}
|
||||
],
|
||||
"currentState": "recalled-test",
|
||||
"lastUpdated": "2025-05-31T06:16:28.047Z"
|
||||
}
|
||||
@ -95,7 +95,7 @@ class BasePouchCommand {
|
||||
toString () {
|
||||
const divider = '='.repeat(60)
|
||||
const nextSteps = (pateoas.nextActions || [])
|
||||
.map(action => ` - ${action.name}: ${action.description}\n 命令: ${action.command}`)
|
||||
.map(action => ` - ${action.name}: ${action.description}\n 方式: ${action.method || action.command || '通过MCP工具'}`)
|
||||
.join('\n')
|
||||
|
||||
return `
|
||||
|
||||
@ -2,6 +2,7 @@ const PouchStateMachine = require('./state/PouchStateMachine')
|
||||
const PouchRegistry = require('./PouchRegistry')
|
||||
const commands = require('./commands')
|
||||
const { COMMANDS } = require('../../../constants')
|
||||
const logger = require('../../utils/logger')
|
||||
|
||||
/**
|
||||
* 锦囊CLI主入口
|
||||
@ -70,9 +71,9 @@ class PouchCLI {
|
||||
if (!silent) {
|
||||
// 如果结果有 toString 方法,打印人类可读格式
|
||||
if (result && result.toString && typeof result.toString === 'function') {
|
||||
console.log(result.toString())
|
||||
logger.log(result.toString())
|
||||
} else {
|
||||
console.log(JSON.stringify(result, null, 2))
|
||||
logger.log(JSON.stringify(result, null, 2))
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +81,7 @@ class PouchCLI {
|
||||
} catch (error) {
|
||||
// 错误输出始终使用stderr,不干扰MCP协议
|
||||
if (!silent) {
|
||||
console.error(`执行命令出错: ${error.message}`)
|
||||
logger.error(`执行命令出错: ${error.message}`)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
@ -162,8 +163,8 @@ class PouchCLI {
|
||||
* 运行交互式CLI
|
||||
*/
|
||||
async runInteractive () {
|
||||
console.log('🎯 欢迎使用 PromptX 锦囊系统!')
|
||||
console.log('输入 "help" 查看帮助,"exit" 退出\n')
|
||||
logger.info('🎯 欢迎使用 PromptX 锦囊系统!')
|
||||
logger.info('输入 "help" 查看帮助,"exit" 退出\n')
|
||||
|
||||
const readline = require('readline')
|
||||
const rl = readline.createInterface({
|
||||
@ -178,21 +179,21 @@ class PouchCLI {
|
||||
const input = line.trim()
|
||||
|
||||
if (input === 'exit' || input === 'quit') {
|
||||
console.log('再见!')
|
||||
logger.info('再见!')
|
||||
rl.close()
|
||||
return
|
||||
}
|
||||
|
||||
if (input === 'help') {
|
||||
console.log(this.getHelp())
|
||||
logger.info(this.getHelp())
|
||||
} else if (input === 'status') {
|
||||
console.log(JSON.stringify(this.getStatus(), null, 2))
|
||||
logger.info(JSON.stringify(this.getStatus(), null, 2))
|
||||
} else if (input) {
|
||||
const { command, args } = this.parseCommand(input)
|
||||
try {
|
||||
await this.execute(command, args)
|
||||
} catch (error) {
|
||||
console.error(error.message)
|
||||
logger.error(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
const BasePouchCommand = require('../BasePouchCommand')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const { COMMANDS, buildCommand } = require('../../../../constants')
|
||||
const ResourceManager = require('../../resource/resourceManager')
|
||||
const { COMMANDS } = require('../../../../constants')
|
||||
const { getGlobalResourceManager } = require('../../resource')
|
||||
const DPMLContentParser = require('../../resource/DPMLContentParser')
|
||||
const SemanticRenderer = require('../../resource/SemanticRenderer')
|
||||
const logger = require('../../../utils/logger')
|
||||
@ -16,7 +16,8 @@ class ActionCommand extends BasePouchCommand {
|
||||
super()
|
||||
// 获取HelloCommand的角色注册表
|
||||
this.helloCommand = null
|
||||
this.resourceManager = new ResourceManager()
|
||||
// 使用全局单例 ResourceManager
|
||||
this.resourceManager = getGlobalResourceManager()
|
||||
this.dpmlParser = new DPMLContentParser()
|
||||
this.semanticRenderer = new SemanticRenderer()
|
||||
}
|
||||
@ -32,19 +33,20 @@ class ActionCommand extends BasePouchCommand {
|
||||
return `❌ 请指定要激活的角色ID
|
||||
|
||||
🔍 使用方法:
|
||||
\`\`\`bash
|
||||
${buildCommand.action('<角色ID>')}
|
||||
\`\`\`
|
||||
通过 MCP PromptX 工具的 action 功能激活角色
|
||||
|
||||
💡 查看可用角色:
|
||||
\`\`\`bash
|
||||
${COMMANDS.HELLO}
|
||||
\`\`\``
|
||||
使用 MCP PromptX 工具的 hello 功能`
|
||||
}
|
||||
|
||||
try {
|
||||
logger.debug(`[ActionCommand] 开始激活角色: ${roleId}`)
|
||||
|
||||
// 0. 初始化 ResourceManager(确保引用解析正常工作)
|
||||
if (!this.resourceManager.initialized) {
|
||||
await this.resourceManager.initializeWithNewArchitecture()
|
||||
}
|
||||
|
||||
// 1. 获取角色信息
|
||||
const roleInfo = await this.getRoleInfo(roleId)
|
||||
logger.debug(`[ActionCommand] getRoleInfo结果:`, roleInfo)
|
||||
@ -53,10 +55,7 @@ ${COMMANDS.HELLO}
|
||||
logger.warn(`[ActionCommand] 角色 "${roleId}" 不存在!`)
|
||||
return `❌ 角色 "${roleId}" 不存在!
|
||||
|
||||
🔍 请使用以下命令查看可用角色:
|
||||
\`\`\`bash
|
||||
${COMMANDS.HELLO}
|
||||
\`\`\``
|
||||
🔍 请使用 MCP PromptX 工具的 hello 功能查看可用角色`
|
||||
}
|
||||
|
||||
// 2. 分析角色文件,提取依赖
|
||||
@ -65,7 +64,7 @@ ${COMMANDS.HELLO}
|
||||
// 3. 生成学习计划并直接加载所有内容
|
||||
return await this.generateLearningPlan(roleInfo.id, dependencies)
|
||||
} catch (error) {
|
||||
console.error('Action command error:', error)
|
||||
logger.error('Action command error:', error)
|
||||
return `❌ 激活角色 "${roleId}" 时发生错误。
|
||||
|
||||
🔍 可能的原因:
|
||||
@ -73,7 +72,7 @@ ${COMMANDS.HELLO}
|
||||
- 权限不足
|
||||
- 系统资源问题
|
||||
|
||||
💡 请使用 \`${COMMANDS.HELLO}\` 查看可用角色列表。`
|
||||
💡 请使用 MCP PromptX 工具的 hello 功能查看可用角色列表。`
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,7 +157,7 @@ ${COMMANDS.HELLO}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error analyzing role dependencies:', error)
|
||||
logger.error('Error analyzing role dependencies:', error)
|
||||
// 如果分析失败,返回基础结构
|
||||
return {
|
||||
thoughts: [],
|
||||
@ -408,11 +407,11 @@ ${recallContent}
|
||||
⚠️ **重要**: recall已自动执行完成,以上记忆将作为角色工作的重要参考依据
|
||||
`
|
||||
} catch (error) {
|
||||
console.error('Auto recall error:', error)
|
||||
logger.error('Auto recall error:', error)
|
||||
return `---
|
||||
## 🧠 自动记忆检索结果
|
||||
⚠️ **记忆检索出现问题**: ${error.message}
|
||||
💡 **建议**: 可手动执行 \`${buildCommand.recall()}\` 来检索相关记忆
|
||||
💡 **建议**: 可使用 MCP PromptX 工具的 recall 功能来检索相关记忆
|
||||
`
|
||||
}
|
||||
}
|
||||
@ -425,12 +424,12 @@ ${recallContent}
|
||||
currentState: 'action_awaiting_role',
|
||||
availableTransitions: ['hello'],
|
||||
nextActions: [
|
||||
{
|
||||
name: '查看可用角色',
|
||||
description: '返回角色发现页面',
|
||||
command: COMMANDS.HELLO,
|
||||
priority: 'high'
|
||||
}
|
||||
{
|
||||
name: '查看可用角色',
|
||||
description: '返回角色发现页面',
|
||||
method: 'MCP PromptX hello 工具',
|
||||
priority: 'high'
|
||||
}
|
||||
],
|
||||
metadata: {
|
||||
message: '需要指定角色ID'
|
||||
@ -445,25 +444,25 @@ ${recallContent}
|
||||
{
|
||||
name: '开始专业服务',
|
||||
description: '角色已激活并完成记忆检索,可直接提供专业服务',
|
||||
command: '开始对话',
|
||||
method: '开始对话',
|
||||
priority: 'high'
|
||||
},
|
||||
{
|
||||
name: '返回角色选择',
|
||||
description: '选择其他角色',
|
||||
command: COMMANDS.HELLO,
|
||||
method: 'MCP PromptX hello 工具',
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
name: '记忆新知识',
|
||||
description: '内化更多专业知识',
|
||||
command: buildCommand.remember('<新知识>'),
|
||||
method: 'MCP PromptX remember 工具',
|
||||
priority: 'low'
|
||||
},
|
||||
{
|
||||
name: '学习新资源',
|
||||
description: '学习相关专业资源',
|
||||
command: buildCommand.learn('<protocol>://<resource>'),
|
||||
method: 'MCP PromptX learn 工具',
|
||||
priority: 'low'
|
||||
}
|
||||
],
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
const BasePouchCommand = require('../BasePouchCommand')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const { buildCommand } = require('../../../../constants')
|
||||
const ResourceManager = require('../../resource/resourceManager')
|
||||
const { getGlobalResourceManager } = require('../../resource')
|
||||
const logger = require('../../../utils/logger')
|
||||
|
||||
/**
|
||||
@ -12,8 +11,8 @@ const logger = require('../../../utils/logger')
|
||||
class HelloCommand extends BasePouchCommand {
|
||||
constructor () {
|
||||
super()
|
||||
// 使用新的ResourceManager架构替代SimplifiedRoleDiscovery
|
||||
this.resourceManager = new ResourceManager()
|
||||
// 使用全局单例 ResourceManager
|
||||
this.resourceManager = getGlobalResourceManager()
|
||||
}
|
||||
|
||||
getPurpose () {
|
||||
@ -22,39 +21,80 @@ class HelloCommand extends BasePouchCommand {
|
||||
|
||||
/**
|
||||
* 动态加载角色注册表 - 使用新的ResourceManager架构
|
||||
* 移除缓存机制,每次都实时扫描,确保角色发现的一致性
|
||||
* 直接使用现有资源注册表,避免重复刷新导致的死循环
|
||||
*/
|
||||
async loadRoleRegistry () {
|
||||
try {
|
||||
// 无状态资源刷新,确保能发现新创建的角色
|
||||
await this.resourceManager.refreshResources()
|
||||
|
||||
// 获取所有角色相关的资源
|
||||
// 确保ResourceManager已初始化
|
||||
if (!this.resourceManager.initialized) {
|
||||
await this.resourceManager.initializeWithNewArchitecture()
|
||||
}
|
||||
|
||||
const roleRegistry = {}
|
||||
|
||||
// 从ResourceRegistry中获取所有role:开头的资源
|
||||
const registry = this.resourceManager.registry
|
||||
for (const [resourceId, reference] of registry.index) {
|
||||
if (resourceId.startsWith('role:')) {
|
||||
const roleId = resourceId.substring(5) // 移除 'role:' 前缀
|
||||
// 使用新的RegistryData v2.0格式获取角色资源
|
||||
const registryData = this.resourceManager.registryData
|
||||
|
||||
// 检查是否有RegistryData(v2.0格式)
|
||||
if (registryData && registryData.resources && registryData.resources.length > 0) {
|
||||
// 使用v2.0格式:直接从RegistryData获取角色资源
|
||||
const roleResources = registryData.getResourcesByProtocol('role')
|
||||
|
||||
for (const resource of roleResources) {
|
||||
const roleId = resource.id
|
||||
|
||||
try {
|
||||
// 尝试加载角色内容以提取元数据
|
||||
const result = await this.resourceManager.loadResource(resourceId)
|
||||
if (result.success) {
|
||||
const name = this.extractRoleNameFromContent(result.content) || roleId
|
||||
const description = this.extractDescriptionFromContent(result.content) || `${name}专业角色`
|
||||
|
||||
roleRegistry[roleId] = {
|
||||
file: reference,
|
||||
name,
|
||||
description,
|
||||
source: reference.startsWith('@package://') ? 'system' : 'user-generated'
|
||||
}
|
||||
// 避免重复角色(同一个ID可能有多个来源)
|
||||
if (!roleRegistry[roleId]) {
|
||||
roleRegistry[roleId] = {
|
||||
id: resource.id,
|
||||
name: resource.name,
|
||||
description: resource.description,
|
||||
source: resource.source,
|
||||
file: resource.reference,
|
||||
protocol: resource.protocol
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 降级到旧格式处理(向后兼容)
|
||||
const registry = this.resourceManager.registry
|
||||
for (const [resourceId, reference] of registry.index) {
|
||||
let roleId = null
|
||||
let isRoleResource = false
|
||||
|
||||
if (resourceId.startsWith('role:')) {
|
||||
roleId = resourceId.substring(5)
|
||||
isRoleResource = true
|
||||
} else if (resourceId.startsWith('package:') || resourceId.startsWith('project:') || resourceId.startsWith('user:')) {
|
||||
const parts = resourceId.split(':')
|
||||
if (parts.length === 2 && !parts[1].includes(':')) {
|
||||
roleId = parts[1]
|
||||
isRoleResource = true
|
||||
}
|
||||
} else if (!resourceId.includes(':')) {
|
||||
roleId = resourceId
|
||||
isRoleResource = true
|
||||
}
|
||||
|
||||
if (isRoleResource && roleId && !roleRegistry[roleId]) {
|
||||
try {
|
||||
const result = await this.resourceManager.loadResource(resourceId)
|
||||
if (result.success) {
|
||||
const name = this.extractRoleNameFromContent(result.content) || roleId
|
||||
const description = this.extractDescriptionFromContent(result.content) || `${name}专业角色`
|
||||
|
||||
roleRegistry[roleId] = {
|
||||
id: roleId,
|
||||
name,
|
||||
description,
|
||||
source: reference.startsWith('@package://') ? 'package' : 'project',
|
||||
file: reference,
|
||||
protocol: 'role'
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// 静默处理,避免干扰用户界面
|
||||
}
|
||||
} catch (error) {
|
||||
// 单个角色加载失败不影响其他角色
|
||||
logger.warn(`角色${roleId}加载失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -62,24 +102,26 @@ class HelloCommand extends BasePouchCommand {
|
||||
// 如果没有任何角色,使用基础角色
|
||||
if (Object.keys(roleRegistry).length === 0) {
|
||||
roleRegistry.assistant = {
|
||||
file: '@package://prompt/domain/assistant/assistant.role.md',
|
||||
id: 'assistant',
|
||||
name: '🙋 智能助手',
|
||||
description: '通用助理角色,提供基础的助理服务和记忆支持',
|
||||
source: 'fallback'
|
||||
source: 'fallback',
|
||||
file: '@package://prompt/domain/assistant/assistant.role.md',
|
||||
protocol: 'role'
|
||||
}
|
||||
}
|
||||
|
||||
return roleRegistry
|
||||
} catch (error) {
|
||||
logger.warn('角色注册表加载失败,使用基础角色:', error.message)
|
||||
|
||||
// 使用基础角色作为fallback
|
||||
return {
|
||||
assistant: {
|
||||
file: '@package://prompt/domain/assistant/assistant.role.md',
|
||||
id: 'assistant',
|
||||
name: '🙋 智能助手',
|
||||
description: '通用助理角色,提供基础的助理服务和记忆支持',
|
||||
source: 'fallback'
|
||||
source: 'fallback',
|
||||
file: '@package://prompt/domain/assistant/assistant.role.md',
|
||||
protocol: 'role'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -151,68 +193,79 @@ class HelloCommand extends BasePouchCommand {
|
||||
*/
|
||||
getSourceLabel(source) {
|
||||
switch (source) {
|
||||
case 'user-generated':
|
||||
return '(用户生成)'
|
||||
case 'system':
|
||||
return '(系统角色)'
|
||||
case 'package':
|
||||
return '📦 系统角色'
|
||||
case 'project':
|
||||
return '🏗️ 项目角色'
|
||||
case 'user':
|
||||
return '<27><> 用户角色'
|
||||
case 'merged':
|
||||
return '📦 系统角色' // merged来源的资源主要来自package
|
||||
case 'fallback':
|
||||
return '(默认角色)'
|
||||
return '🔄 默认角色'
|
||||
default:
|
||||
return ''
|
||||
return '❓ 未知来源'
|
||||
}
|
||||
}
|
||||
|
||||
async getContent (args) {
|
||||
await this.loadRoleRegistry()
|
||||
const allRoles = await this.getAllRoles()
|
||||
const roleRegistry = await this.loadRoleRegistry()
|
||||
const allRoles = Object.values(roleRegistry)
|
||||
const totalRoles = allRoles.length
|
||||
|
||||
let content = `🤖 **AI专业角色服务清单** (共 ${totalRoles} 个专业角色可供选择)
|
||||
|
||||
> 💡 **重要说明**:以下是可激活的AI专业角色。每个角色都有唯一的ID,使用action命令激活。
|
||||
> 💡 **重要说明**:以下是可激活的AI专业角色。每个角色都有唯一的ID,可通过MCP工具激活。
|
||||
|
||||
## 📋 可用角色列表
|
||||
|
||||
`
|
||||
|
||||
// 清楚显示角色ID和激活命令
|
||||
allRoles.forEach((role, index) => {
|
||||
const sourceLabel = this.getSourceLabel(role.source)
|
||||
content += `### ${index + 1}. ${role.name} ${sourceLabel}
|
||||
// 按来源分组显示角色
|
||||
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.name}
|
||||
**角色ID**: \`${role.id}\`
|
||||
**专业能力**: ${role.description}
|
||||
**激活命令**: \`${buildCommand.action(role.id)}\`
|
||||
**来源**: ${sourceLabel}
|
||||
|
||||
---
|
||||
|
||||
`
|
||||
})
|
||||
roleIndex++
|
||||
})
|
||||
}
|
||||
|
||||
content += `
|
||||
## 🎯 **角色激活指南**
|
||||
|
||||
### 🔧 激活步骤
|
||||
1. **选择角色**: 从上述列表中选择需要的专业角色
|
||||
2. **使用角色ID**: 复制对应的角色ID(如:\`${allRoles[0]?.id || 'assistant'}\`)
|
||||
3. **执行命令**: 运行 \`${buildCommand.action('<角色ID>')}\`
|
||||
|
||||
### 📝 激活示例
|
||||
\`\`\`bash
|
||||
# 激活智能助手角色
|
||||
${buildCommand.action(allRoles[0]?.id || 'assistant')}
|
||||
\`\`\`
|
||||
### 🔧 激活方式
|
||||
- 使用 **MCP PromptX 工具** 中的 \`action\` 功能
|
||||
- 选择需要的角色ID进行激活
|
||||
|
||||
### ⚡ 激活后效果
|
||||
- ✅ **获得专业能力** - AI将具备该角色的专业技能
|
||||
- ✅ **学习技能组合** - 自动学习角色需要的思维和行为模式
|
||||
- ✅ **即时专业化** - 立即可以提供该领域的专业服务
|
||||
|
||||
---
|
||||
|
||||
⚠️ **AI执行指令**:
|
||||
📢 **向主人汇报角色选项,明确说明使用方法:"请选择角色ID,然后我将执行对应的action命令"**
|
||||
🎯 **等待主人指定具体的角色ID后,立即执行 \`${buildCommand.action('<角色ID>')}\`**
|
||||
💡 **强调:action命令需要具体的角色ID,不是角色名称**
|
||||
`
|
||||
|
||||
return content
|
||||
@ -223,7 +276,7 @@ ${buildCommand.action(allRoles[0]?.id || 'assistant')}
|
||||
const availableRoles = allRoles.map(role => ({
|
||||
roleId: role.id,
|
||||
name: role.name,
|
||||
actionCommand: buildCommand.action(role.id)
|
||||
source: role.source
|
||||
}))
|
||||
|
||||
return {
|
||||
@ -233,7 +286,7 @@ ${buildCommand.action(allRoles[0]?.id || 'assistant')}
|
||||
{
|
||||
name: '向主人汇报服务选项',
|
||||
description: '将上述专业服务清单告知主人,并询问需求',
|
||||
command: '等待主人选择后使用: ' + buildCommand.action('<选择的角色ID>'),
|
||||
method: 'MCP PromptX action 工具',
|
||||
priority: 'critical',
|
||||
instruction: '必须先询问主人需求,不要自主选择角色'
|
||||
}
|
||||
@ -243,7 +296,7 @@ ${buildCommand.action(allRoles[0]?.id || 'assistant')}
|
||||
availableRoles,
|
||||
dataSource: 'resource.registry.json',
|
||||
systemVersion: '锦囊串联状态机 v1.0',
|
||||
designPhilosophy: 'AI use CLI get prompt for AI'
|
||||
designPhilosophy: 'AI use MCP tools for role activation'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -298,27 +351,27 @@ ${buildCommand.action(allRoles[0]?.id || 'assistant')}
|
||||
async debugRegistry() {
|
||||
await this.loadRoleRegistry()
|
||||
|
||||
console.log('\n🔍 HelloCommand - 注册表调试信息')
|
||||
console.log('='.repeat(50))
|
||||
logger.info('\n🔍 HelloCommand - 注册表调试信息')
|
||||
logger.info('='.repeat(50))
|
||||
|
||||
if (this.roleRegistry && Object.keys(this.roleRegistry).length > 0) {
|
||||
console.log(`📊 发现 ${Object.keys(this.roleRegistry).length} 个角色资源:\n`)
|
||||
logger.info(`📊 发现 ${Object.keys(this.roleRegistry).length} 个角色资源:\n`)
|
||||
|
||||
Object.entries(this.roleRegistry).forEach(([id, roleInfo]) => {
|
||||
console.log(`🎭 ${id}`)
|
||||
console.log(` 名称: ${roleInfo.name || '未命名'}`)
|
||||
console.log(` 描述: ${roleInfo.description || '无描述'}`)
|
||||
console.log(` 文件: ${roleInfo.file}`)
|
||||
console.log(` 来源: ${roleInfo.source || '未知'}`)
|
||||
console.log('')
|
||||
logger.info(`🎭 ${id}`)
|
||||
logger.info(` 名称: ${roleInfo.name || '未命名'}`)
|
||||
logger.info(` 描述: ${roleInfo.description || '无描述'}`)
|
||||
logger.info(` 文件: ${roleInfo.file}`)
|
||||
logger.info(` 来源: ${roleInfo.source || '未知'}`)
|
||||
logger.info('')
|
||||
})
|
||||
} else {
|
||||
console.log('🔍 没有发现任何角色资源')
|
||||
logger.info('🔍 没有发现任何角色资源')
|
||||
}
|
||||
|
||||
// 同时显示ResourceManager的注册表
|
||||
console.log('\n📋 ResourceManager 注册表:')
|
||||
console.log('-'.repeat(30))
|
||||
logger.info('\n📋 ResourceManager 注册表:')
|
||||
logger.info('-'.repeat(30))
|
||||
this.resourceManager.registry.printAll('底层资源注册表')
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
const BasePouchCommand = require('../BasePouchCommand')
|
||||
const { ResourceManager } = require('../../resource')
|
||||
const { getGlobalResourceManager } = require('../../resource')
|
||||
const { COMMANDS } = require('../../../../constants')
|
||||
const PromptXConfig = require('../../../utils/promptxConfig')
|
||||
const RegistryData = require('../../resource/RegistryData')
|
||||
const ProjectDiscovery = require('../../resource/discovery/ProjectDiscovery')
|
||||
const logger = require('../../../utils/logger')
|
||||
const path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
|
||||
@ -12,11 +15,13 @@ const fs = require('fs-extra')
|
||||
class InitCommand extends BasePouchCommand {
|
||||
constructor () {
|
||||
super()
|
||||
this.resourceManager = new ResourceManager()
|
||||
// 使用全局单例 ResourceManager
|
||||
this.resourceManager = getGlobalResourceManager()
|
||||
this.projectDiscovery = new ProjectDiscovery()
|
||||
}
|
||||
|
||||
getPurpose () {
|
||||
return '初始化PromptX工作环境,创建必要的配置目录和文件'
|
||||
return '初始化PromptX工作环境,创建必要的配置目录和文件,生成项目级资源注册表'
|
||||
}
|
||||
|
||||
async getContent (args) {
|
||||
@ -28,6 +33,12 @@ class InitCommand extends BasePouchCommand {
|
||||
// 2. 基础环境准备 - 只创建 .promptx 目录
|
||||
await this.ensurePromptXDirectory(workspacePath)
|
||||
|
||||
// 3. 生成项目级资源注册表
|
||||
const registryStats = await this.generateProjectRegistry(workspacePath)
|
||||
|
||||
// 4. 刷新全局 ResourceManager(确保新资源立即可用)
|
||||
await this.refreshGlobalResourceManager()
|
||||
|
||||
return `🎯 PromptX 初始化完成!
|
||||
|
||||
## 📦 版本信息
|
||||
@ -37,13 +48,68 @@ class InitCommand extends BasePouchCommand {
|
||||
✅ 创建了 \`.promptx\` 配置目录
|
||||
✅ 工作环境就绪
|
||||
|
||||
## 📋 项目资源注册表
|
||||
${registryStats.message}
|
||||
|
||||
## 🚀 下一步建议
|
||||
- 使用 \`hello\` 发现可用的专业角色
|
||||
- 使用 \`action\` 激活特定角色获得专业能力
|
||||
- 使用 \`learn\` 深入学习专业知识
|
||||
- 使用 \`remember/recall\` 管理专业记忆
|
||||
|
||||
💡 **提示**: 现在可以开始使用专业角色系统来增强AI能力了!`
|
||||
💡 **提示**: ${registryStats.totalResources > 0 ? '项目资源已优化为注册表模式,性能大幅提升!' : '现在可以开始创建项目级资源了!'}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成项目级资源注册表
|
||||
* @param {string} workspacePath - 工作目录路径
|
||||
* @returns {Promise<Object>} 注册表生成统计信息
|
||||
*/
|
||||
async generateProjectRegistry(workspacePath) {
|
||||
try {
|
||||
// 1. 获取项目根目录
|
||||
const projectRoot = await this.projectDiscovery._findProjectRoot()
|
||||
|
||||
// 2. 确保 .promptx/resource/domain 目录结构存在
|
||||
const resourceDir = path.join(projectRoot, '.promptx', 'resource')
|
||||
const domainDir = path.join(resourceDir, 'domain')
|
||||
|
||||
await fs.ensureDir(domainDir)
|
||||
logger.debug(`[InitCommand] 确保目录结构存在: ${domainDir}`)
|
||||
|
||||
// 3. 使用 ProjectDiscovery 的正确方法生成注册表
|
||||
logger.step('正在扫描项目资源...')
|
||||
const registryData = await this.projectDiscovery.generateRegistry(projectRoot)
|
||||
|
||||
// 4. 生成统计信息
|
||||
const stats = registryData.getStats()
|
||||
const registryPath = path.join(projectRoot, '.promptx', 'resource', 'project.registry.json')
|
||||
|
||||
if (registryData.size === 0) {
|
||||
return {
|
||||
message: `✅ 项目资源目录已创建,注册表已初始化
|
||||
📂 目录: ${path.relative(process.cwd(), domainDir)}
|
||||
💾 注册表: ${path.relative(process.cwd(), registryPath)}
|
||||
💡 现在可以在 domain 目录下创建角色资源了`,
|
||||
totalResources: 0
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
message: `✅ 项目资源注册表已重新生成
|
||||
📊 总计: ${registryData.size} 个资源
|
||||
📋 分类: role(${stats.byProtocol.role || 0}), thought(${stats.byProtocol.thought || 0}), execution(${stats.byProtocol.execution || 0}), knowledge(${stats.byProtocol.knowledge || 0})
|
||||
💾 位置: ${path.relative(process.cwd(), registryPath)}`,
|
||||
totalResources: registryData.size
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.error('生成项目注册表时出错:', error)
|
||||
return {
|
||||
message: `❌ 生成项目注册表失败: ${error.message}`,
|
||||
totalResources: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,6 +122,24 @@ class InitCommand extends BasePouchCommand {
|
||||
await config.ensureDir()
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新全局 ResourceManager
|
||||
* 确保新创建的资源立即可用,无需重启 MCP Server
|
||||
*/
|
||||
async refreshGlobalResourceManager() {
|
||||
try {
|
||||
logger.debug('[InitCommand] 刷新全局 ResourceManager...')
|
||||
|
||||
// 重新初始化 ResourceManager,清除缓存并重新发现资源
|
||||
await this.resourceManager.initializeWithNewArchitecture()
|
||||
|
||||
logger.debug('[InitCommand] 全局 ResourceManager 刷新完成')
|
||||
} catch (error) {
|
||||
logger.warn(`[InitCommand] 刷新 ResourceManager 失败: ${error.message}`)
|
||||
// 不抛出错误,避免影响 init 命令的主要功能
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本信息
|
||||
*/
|
||||
@ -71,7 +155,7 @@ class InitCommand extends BasePouchCommand {
|
||||
return `${baseVersion} (${packageName}@${baseVersion}, Node.js ${nodeVersion})`
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('⚠️ 无法读取版本信息:', error.message)
|
||||
logger.warn('无法读取版本信息:', error.message)
|
||||
}
|
||||
return '未知版本'
|
||||
}
|
||||
@ -102,6 +186,8 @@ class InitCommand extends BasePouchCommand {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = InitCommand
|
||||
|
||||
@ -1,19 +1,25 @@
|
||||
const BasePouchCommand = require('../BasePouchCommand')
|
||||
const ResourceManager = require('../../resource/resourceManager')
|
||||
const { COMMANDS, buildCommand } = require('../../../../constants')
|
||||
const { getGlobalResourceManager } = require('../../resource')
|
||||
const DPMLContentParser = require('../../resource/DPMLContentParser')
|
||||
const SemanticRenderer = require('../../resource/SemanticRenderer')
|
||||
const { COMMANDS } = require('../../../../constants')
|
||||
|
||||
/**
|
||||
* 智能学习锦囊命令
|
||||
* 支持加载thought、execution、memory等协议资源,以及角色的personality、principle、knowledge
|
||||
* 支持语义占位符渲染,将@引用展开为完整的语义内容
|
||||
*/
|
||||
class LearnCommand extends BasePouchCommand {
|
||||
constructor () {
|
||||
super()
|
||||
this.resourceManager = new ResourceManager()
|
||||
// 使用全局单例 ResourceManager
|
||||
this.resourceManager = getGlobalResourceManager()
|
||||
this.dpmlParser = new DPMLContentParser()
|
||||
this.semanticRenderer = new SemanticRenderer()
|
||||
}
|
||||
|
||||
getPurpose () {
|
||||
return '智能学习指定协议的资源内容,支持thought、execution、memory等DPML协议以及角色组件'
|
||||
return '智能学习指定协议的资源内容,支持thought、execution、memory等DPML协议以及角色组件,支持@引用的语义渲染'
|
||||
}
|
||||
|
||||
async getContent (args) {
|
||||
@ -24,13 +30,6 @@ class LearnCommand extends BasePouchCommand {
|
||||
}
|
||||
|
||||
try {
|
||||
// 直接使用ResourceManager解析资源
|
||||
const result = await this.resourceManager.resolve(resourceUrl)
|
||||
|
||||
if (!result.success) {
|
||||
return this.formatErrorResponse(resourceUrl, result.error.message)
|
||||
}
|
||||
|
||||
// 解析协议信息
|
||||
const urlMatch = resourceUrl.match(/^(@[!?]?)?([a-zA-Z][a-zA-Z0-9_-]*):\/\/(.+)$/)
|
||||
if (!urlMatch) {
|
||||
@ -39,12 +38,62 @@ class LearnCommand extends BasePouchCommand {
|
||||
|
||||
const [, loadingSemantic, protocol, resourceId] = urlMatch
|
||||
|
||||
return this.formatSuccessResponse(protocol, resourceId, result.content)
|
||||
// 使用ResourceManager解析资源
|
||||
const result = await this.resourceManager.resolve(resourceUrl)
|
||||
|
||||
if (!result.success) {
|
||||
return this.formatErrorResponse(resourceUrl, result.error.message)
|
||||
}
|
||||
|
||||
// 检查内容是否包含@引用,如果包含则进行语义渲染
|
||||
let finalContent = result.content
|
||||
|
||||
if (this.containsReferences(result.content)) {
|
||||
// 对于完整的DPML标签(如<execution>...</execution>),提取标签内容进行渲染
|
||||
const innerContent = this.extractTagInnerContent(result.content, protocol)
|
||||
|
||||
if (innerContent) {
|
||||
// 解析标签内的混合内容(@引用 + 直接内容)
|
||||
const tagSemantics = this.dpmlParser.parseTagContent(innerContent, protocol)
|
||||
|
||||
// 使用SemanticRenderer进行语义占位符渲染
|
||||
const renderedInnerContent = await this.semanticRenderer.renderSemanticContent(tagSemantics, this.resourceManager)
|
||||
|
||||
// 如果渲染成功,重新包装为完整的DPML标签
|
||||
if (renderedInnerContent && renderedInnerContent.trim()) {
|
||||
finalContent = `<${protocol}>\n${renderedInnerContent}\n</${protocol}>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.formatSuccessResponse(protocol, resourceId, finalContent)
|
||||
} catch (error) {
|
||||
return this.formatErrorResponse(resourceUrl, error.message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查内容是否包含@引用
|
||||
* @param {string} content - 要检查的内容
|
||||
* @returns {boolean} 是否包含@引用
|
||||
*/
|
||||
containsReferences(content) {
|
||||
const resourceRegex = /@([!?]?)([a-zA-Z][a-zA-Z0-9_-]*):\/\/([a-zA-Z0-9_\/.,-]+)/g
|
||||
return resourceRegex.test(content)
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取完整的DPML标签内容
|
||||
* @param {string} content - 要提取的内容
|
||||
* @param {string} protocol - 协议
|
||||
* @returns {string} 提取的完整DPML标签内容
|
||||
*/
|
||||
extractTagInnerContent(content, protocol) {
|
||||
const tagRegex = new RegExp(`<${protocol}>([\\s\\S]*?)<\\/${protocol}>`, 'i')
|
||||
const match = content.match(tagRegex)
|
||||
return match ? match[1].trim() : null
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化成功响应
|
||||
*/
|
||||
@ -72,12 +121,9 @@ ${content}
|
||||
- ✅ **可立即应用于实际场景**
|
||||
|
||||
## 🔄 下一步行动:
|
||||
- 继续学习: 学习其他相关资源
|
||||
命令: \`${buildCommand.learn('<protocol>://<resource-id>')}\`
|
||||
- 应用记忆: 检索相关经验
|
||||
命令: \`${COMMANDS.RECALL}\`
|
||||
- 激活角色: 激活完整角色能力
|
||||
命令: \`${buildCommand.action('<role-id>')}\`
|
||||
- 继续学习: 使用 MCP PromptX learn 工具学习其他相关资源
|
||||
- 应用记忆: 使用 MCP PromptX recall 工具检索相关经验
|
||||
- 激活角色: 使用 MCP PromptX action 工具激活完整角色能力
|
||||
|
||||
📍 当前状态:learned_${protocol}`
|
||||
}
|
||||
@ -100,19 +146,13 @@ ${errorMessage}
|
||||
- \`knowledge://role-id\` - 学习角色知识
|
||||
|
||||
🔍 查看可用资源:
|
||||
\`\`\`bash
|
||||
${buildCommand.action('<role-id>')} # 查看角色的所有依赖
|
||||
\`\`\`
|
||||
使用 MCP PromptX action 工具查看角色的所有依赖
|
||||
|
||||
🔄 下一步行动:
|
||||
- 继续学习: 学习其他资源
|
||||
命令: ${buildCommand.learn('<protocol>://<resource-id>')}
|
||||
- 应用记忆: 检索相关经验
|
||||
命令: ${COMMANDS.RECALL}
|
||||
- 激活角色: 激活完整角色能力
|
||||
命令: ${buildCommand.action('<role-id>')}
|
||||
- 查看角色列表: 选择其他角色
|
||||
命令: ${COMMANDS.HELLO}`
|
||||
- 继续学习: 使用 MCP PromptX learn 工具学习其他资源
|
||||
- 应用记忆: 使用 MCP PromptX recall 工具检索相关经验
|
||||
- 激活角色: 使用 MCP PromptX action 工具激活完整角色能力
|
||||
- 查看角色列表: 使用 MCP PromptX hello 工具选择其他角色`
|
||||
}
|
||||
|
||||
/**
|
||||
@ -122,9 +162,8 @@ ${buildCommand.action('<role-id>')} # 查看角色的所有依赖
|
||||
return `🎓 **Learn锦囊 - 智能学习系统**
|
||||
|
||||
## 📖 基本用法
|
||||
\`\`\`bash
|
||||
promptx learn <protocol>://<resource-id>
|
||||
\`\`\`
|
||||
通过 MCP PromptX learn 工具学习资源:
|
||||
\`<protocol>://<resource-id>\`
|
||||
|
||||
## 🎯 支持的协议
|
||||
|
||||
@ -139,28 +178,18 @@ promptx learn <protocol>://<resource-id>
|
||||
- **\`knowledge://\`** - 专业知识
|
||||
|
||||
## 📝 使用示例
|
||||
\`\`\`bash
|
||||
# 学习执行技能
|
||||
${buildCommand.learn('execution://deal-at-reference')}
|
||||
|
||||
# 学习思维模式
|
||||
${buildCommand.learn('thought://prompt-developer')}
|
||||
|
||||
# 学习角色人格
|
||||
${buildCommand.learn('personality://video-copywriter')}
|
||||
\`\`\`
|
||||
通过 MCP PromptX learn 工具学习各种资源:
|
||||
- 学习执行技能: \`execution://deal-at-reference\`
|
||||
- 学习思维模式: \`thought://prompt-developer\`
|
||||
- 学习角色人格: \`personality://video-copywriter\`
|
||||
|
||||
## 🔍 发现可学习资源
|
||||
\`\`\`bash
|
||||
${buildCommand.action('<role-id>')} # 查看角色需要的所有资源
|
||||
${COMMANDS.HELLO} # 查看可用角色列表
|
||||
\`\`\`
|
||||
- 使用 MCP PromptX action 工具查看角色需要的所有资源
|
||||
- 使用 MCP PromptX hello 工具查看可用角色列表
|
||||
|
||||
🔄 下一步行动:
|
||||
- 激活角色: 分析角色依赖
|
||||
命令: ${buildCommand.action('<role-id>')}
|
||||
- 查看角色: 选择感兴趣的角色
|
||||
命令: ${COMMANDS.HELLO}`
|
||||
- 激活角色: 使用 MCP PromptX action 工具分析角色依赖
|
||||
- 查看角色: 使用 MCP PromptX hello 工具选择感兴趣的角色`
|
||||
}
|
||||
|
||||
/**
|
||||
@ -177,13 +206,13 @@ ${COMMANDS.HELLO} # 查看可用角色列表
|
||||
{
|
||||
name: '查看可用角色',
|
||||
description: '返回角色选择页面',
|
||||
command: COMMANDS.HELLO,
|
||||
method: 'MCP PromptX hello 工具',
|
||||
priority: 'high'
|
||||
},
|
||||
{
|
||||
name: '生成学习计划',
|
||||
description: '为特定角色生成学习计划',
|
||||
command: buildCommand.action('<role-id>'),
|
||||
method: 'MCP PromptX action 工具',
|
||||
priority: 'high'
|
||||
}
|
||||
]
|
||||
@ -199,7 +228,7 @@ ${COMMANDS.HELLO} # 查看可用角色列表
|
||||
{
|
||||
name: '查看使用帮助',
|
||||
description: '重新学习命令使用方法',
|
||||
command: COMMANDS.LEARN,
|
||||
method: 'MCP PromptX learn 工具',
|
||||
priority: 'high'
|
||||
}
|
||||
]
|
||||
@ -215,25 +244,25 @@ ${COMMANDS.HELLO} # 查看可用角色列表
|
||||
{
|
||||
name: '继续学习',
|
||||
description: '学习其他资源',
|
||||
command: buildCommand.learn('<protocol>://<resource-id>'),
|
||||
method: 'MCP PromptX learn 工具',
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
name: '应用记忆',
|
||||
description: '检索相关经验',
|
||||
command: COMMANDS.RECALL,
|
||||
method: 'MCP PromptX recall 工具',
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
name: '激活角色',
|
||||
description: '激活完整角色能力',
|
||||
command: buildCommand.action('<role-id>'),
|
||||
method: 'MCP PromptX action 工具',
|
||||
priority: 'high'
|
||||
},
|
||||
{
|
||||
name: '查看角色列表',
|
||||
description: '选择其他角色',
|
||||
command: COMMANDS.HELLO,
|
||||
method: 'MCP PromptX hello 工具',
|
||||
priority: 'low'
|
||||
}
|
||||
],
|
||||
|
||||
279
src/lib/core/pouch/commands/LearnCommand.js.backup
Normal file
279
src/lib/core/pouch/commands/LearnCommand.js.backup
Normal file
@ -0,0 +1,279 @@
|
||||
const BasePouchCommand = require('../BasePouchCommand')
|
||||
const ResourceManager = require('../../resource/resourceManager')
|
||||
const DPMLContentParser = require('../../resource/DPMLContentParser')
|
||||
const SemanticRenderer = require('../../resource/SemanticRenderer')
|
||||
const { COMMANDS } = require('../../../../constants')
|
||||
|
||||
/**
|
||||
* 智能学习锦囊命令
|
||||
* 支持加载thought、execution、memory等协议资源,以及角色的personality、principle、knowledge
|
||||
* 支持语义占位符渲染,将@引用展开为完整的语义内容
|
||||
*/
|
||||
class LearnCommand extends BasePouchCommand {
|
||||
constructor () {
|
||||
super()
|
||||
this.resourceManager = new ResourceManager()
|
||||
this.dpmlParser = new DPMLContentParser()
|
||||
this.semanticRenderer = new SemanticRenderer()
|
||||
}
|
||||
|
||||
getPurpose () {
|
||||
return '智能学习指定协议的资源内容,支持thought、execution、memory等DPML协议以及角色组件,支持@引用的语义渲染'
|
||||
}
|
||||
|
||||
async getContent (args) {
|
||||
const [resourceUrl] = args
|
||||
|
||||
if (!resourceUrl) {
|
||||
return this.getUsageHelp()
|
||||
}
|
||||
|
||||
try {
|
||||
// 解析协议信息
|
||||
const urlMatch = resourceUrl.match(/^(@[!?]?)?([a-zA-Z][a-zA-Z0-9_-]*):\/\/(.+)$/)
|
||||
if (!urlMatch) {
|
||||
return this.formatErrorResponse(resourceUrl, '无效的资源URL格式')
|
||||
}
|
||||
|
||||
const [, loadingSemantic, protocol, resourceId] = urlMatch
|
||||
|
||||
// 使用ResourceManager解析资源
|
||||
const result = await this.resourceManager.resolve(resourceUrl)
|
||||
|
||||
if (!result.success) {
|
||||
return this.formatErrorResponse(resourceUrl, result.error.message)
|
||||
}
|
||||
|
||||
// 检查内容是否包含@引用,如果包含则进行语义渲染
|
||||
let finalContent = result.content
|
||||
|
||||
if (this.containsReferences(result.content)) {
|
||||
// 对于完整的DPML标签(如<execution>...</execution>),提取标签内容进行渲染
|
||||
const innerContent = this.extractTagInnerContent(result.content, protocol)
|
||||
|
||||
if (innerContent) {
|
||||
// 解析标签内的混合内容(@引用 + 直接内容)
|
||||
const tagSemantics = this.dpmlParser.parseTagContent(innerContent, protocol)
|
||||
|
||||
// 使用SemanticRenderer进行语义占位符渲染
|
||||
const renderedInnerContent = await this.semanticRenderer.renderSemanticContent(tagSemantics, this.resourceManager)
|
||||
|
||||
// 如果渲染成功,重新包装为完整的DPML标签
|
||||
if (renderedInnerContent && renderedInnerContent.trim()) {
|
||||
finalContent = `<${protocol}>\n${renderedInnerContent}\n</${protocol}>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.formatSuccessResponse(protocol, resourceId, finalContent)
|
||||
} catch (error) {
|
||||
return this.formatErrorResponse(resourceUrl, error.message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查内容是否包含@引用
|
||||
* @param {string} content - 要检查的内容
|
||||
* @returns {boolean} 是否包含@引用
|
||||
*/
|
||||
containsReferences(content) {
|
||||
const resourceRegex = /@([!?]?)([a-zA-Z][a-zA-Z0-9_-]*):\/\/([a-zA-Z0-9_\/.,-]+)/g
|
||||
return resourceRegex.test(content)
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取完整的DPML标签内容
|
||||
* @param {string} content - 要提取的内容
|
||||
* @param {string} protocol - 协议
|
||||
* @returns {string} 提取的完整DPML标签内容
|
||||
*/
|
||||
extractTagInnerContent(content, protocol) {
|
||||
const tagRegex = new RegExp(`<${protocol}>([\\s\\S]*?)<\\/${protocol}>`, 'i')
|
||||
const match = content.match(tagRegex)
|
||||
return match ? match[1].trim() : null
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化成功响应
|
||||
*/
|
||||
formatSuccessResponse (protocol, resourceId, content) {
|
||||
const protocolLabels = {
|
||||
thought: '🧠 思维模式',
|
||||
execution: '⚡ 执行模式',
|
||||
memory: '💾 记忆模式',
|
||||
personality: '👤 角色人格',
|
||||
principle: '⚖️ 行为原则',
|
||||
knowledge: '📚 专业知识'
|
||||
}
|
||||
|
||||
const label = protocolLabels[protocol] || `📄 ${protocol}`
|
||||
|
||||
return `✅ **成功学习${label}:${resourceId}**
|
||||
|
||||
## 📋 学习内容
|
||||
|
||||
${content}
|
||||
|
||||
## 🎯 学习效果
|
||||
- ✅ **已激活${label}能力**
|
||||
- ✅ **相关知识已整合到AI认知体系**
|
||||
- ✅ **可立即应用于实际场景**
|
||||
|
||||
## 🔄 下一步行动:
|
||||
- 继续学习: 使用 MCP PromptX learn 工具学习其他相关资源
|
||||
- 应用记忆: 使用 MCP PromptX recall 工具检索相关经验
|
||||
- 激活角色: 使用 MCP PromptX action 工具激活完整角色能力
|
||||
|
||||
📍 当前状态:learned_${protocol}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化错误响应
|
||||
*/
|
||||
formatErrorResponse (resourceUrl, errorMessage) {
|
||||
return `❌ 学习资源失败:${resourceUrl}
|
||||
|
||||
🔍 错误详情:
|
||||
${errorMessage}
|
||||
|
||||
💡 支持的协议:
|
||||
- \`thought://resource-id\` - 学习思维模式
|
||||
- \`execution://resource-id\` - 学习执行模式
|
||||
- \`memory://resource-id\` - 学习记忆模式
|
||||
- \`personality://role-id\` - 学习角色思维
|
||||
- \`principle://role-id\` - 学习角色原则
|
||||
- \`knowledge://role-id\` - 学习角色知识
|
||||
|
||||
🔍 查看可用资源:
|
||||
使用 MCP PromptX action 工具查看角色的所有依赖
|
||||
|
||||
🔄 下一步行动:
|
||||
- 继续学习: 使用 MCP PromptX learn 工具学习其他资源
|
||||
- 应用记忆: 使用 MCP PromptX recall 工具检索相关经验
|
||||
- 激活角色: 使用 MCP PromptX action 工具激活完整角色能力
|
||||
- 查看角色列表: 使用 MCP PromptX hello 工具选择其他角色`
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取使用帮助
|
||||
*/
|
||||
getUsageHelp () {
|
||||
return `🎓 **Learn锦囊 - 智能学习系统**
|
||||
|
||||
## 📖 基本用法
|
||||
\`\`\`bash
|
||||
promptx learn <protocol>://<resource-id>
|
||||
\`\`\`
|
||||
|
||||
## 🎯 支持的协议
|
||||
|
||||
### 🔧 DPML核心协议
|
||||
- **\`thought://\`** - 思维模式资源
|
||||
- **\`execution://\`** - 执行模式资源
|
||||
- **\`memory://\`** - 记忆系统资源
|
||||
|
||||
### 👤 角色组件协议
|
||||
- **\`personality://\`** - 角色人格特征
|
||||
- **\`principle://\`** - 行为原则
|
||||
- **\`knowledge://\`** - 专业知识
|
||||
|
||||
## 📝 使用示例
|
||||
通过 MCP PromptX learn 工具学习各种资源:
|
||||
- 学习执行技能: `execution://deal-at-reference`
|
||||
- 学习思维模式: `thought://prompt-developer`
|
||||
- 学习角色人格: `personality://video-copywriter`
|
||||
|
||||
## 🔍 发现可学习资源
|
||||
- 使用 MCP PromptX action 工具查看角色需要的所有资源
|
||||
- 使用 MCP PromptX hello 工具查看可用角色列表
|
||||
|
||||
🔄 下一步行动:
|
||||
- 激活角色: 使用 MCP PromptX action 工具分析角色依赖
|
||||
- 查看角色: 使用 MCP PromptX hello 工具选择感兴趣的角色`
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取PATEOAS导航信息
|
||||
*/
|
||||
getPATEOAS (args) {
|
||||
const [resourceUrl] = args
|
||||
|
||||
if (!resourceUrl) {
|
||||
return {
|
||||
currentState: 'learn_awaiting_resource',
|
||||
availableTransitions: ['hello', 'action'],
|
||||
nextActions: [
|
||||
{
|
||||
name: '查看可用角色',
|
||||
description: '返回角色选择页面',
|
||||
command: COMMANDS.HELLO,
|
||||
priority: 'high'
|
||||
},
|
||||
{
|
||||
name: '生成学习计划',
|
||||
description: '为特定角色生成学习计划',
|
||||
method: 'MCP PromptX action 工具',
|
||||
priority: 'high'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const urlMatch = resourceUrl.match(/^([a-zA-Z]+):\/\/(.+)$/)
|
||||
if (!urlMatch) {
|
||||
return {
|
||||
currentState: 'learn_error',
|
||||
availableTransitions: ['hello', 'action'],
|
||||
nextActions: [
|
||||
{
|
||||
name: '查看使用帮助',
|
||||
description: '重新学习命令使用方法',
|
||||
command: COMMANDS.LEARN,
|
||||
priority: 'high'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const [, protocol, resourceId] = urlMatch
|
||||
|
||||
return {
|
||||
currentState: `learned_${protocol}`,
|
||||
availableTransitions: ['learn', 'recall', 'hello', 'action'],
|
||||
nextActions: [
|
||||
{
|
||||
name: '继续学习',
|
||||
description: '学习其他资源',
|
||||
command: buildCommand.learn('<protocol>://<resource-id>'),
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
name: '应用记忆',
|
||||
description: '检索相关经验',
|
||||
command: COMMANDS.RECALL,
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
name: '激活角色',
|
||||
description: '激活完整角色能力',
|
||||
command: buildCommand.action('<role-id>'),
|
||||
priority: 'high'
|
||||
},
|
||||
{
|
||||
name: '查看角色列表',
|
||||
description: '选择其他角色',
|
||||
command: COMMANDS.HELLO,
|
||||
priority: 'low'
|
||||
}
|
||||
],
|
||||
metadata: {
|
||||
learnedResource: resourceUrl,
|
||||
protocol,
|
||||
resourceId,
|
||||
systemVersion: '锦囊串联状态机 v1.0'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LearnCommand
|
||||
@ -1,7 +1,7 @@
|
||||
const BasePouchCommand = require('../BasePouchCommand')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const { COMMANDS, buildCommand } = require('../../../../constants')
|
||||
const { COMMANDS } = require('../../../../constants')
|
||||
|
||||
/**
|
||||
* 记忆检索锦囊命令
|
||||
@ -25,8 +25,8 @@ class RecallCommand extends BasePouchCommand {
|
||||
if (memories.length === 0) {
|
||||
return `🧠 AI记忆体系中暂无内容。
|
||||
💡 建议:
|
||||
1. 使用 ${COMMANDS.REMEMBER} 内化新知识
|
||||
2. 使用 ${COMMANDS.LEARN} 学习后再内化
|
||||
1. 使用 MCP PromptX remember 工具内化新知识
|
||||
2. 使用 MCP PromptX learn 工具学习后再内化
|
||||
3. 开始构建AI的专业知识体系`
|
||||
}
|
||||
|
||||
@ -54,22 +54,22 @@ ${formattedMemories}
|
||||
{
|
||||
name: '选择角色',
|
||||
description: '选择专业角色来应用检索到的知识',
|
||||
command: COMMANDS.HELLO
|
||||
method: 'MCP PromptX hello 工具'
|
||||
},
|
||||
{
|
||||
name: '记忆新知识',
|
||||
description: '继续内化更多专业知识',
|
||||
command: COMMANDS.REMEMBER + ' "<新的知识内容>"'
|
||||
method: 'MCP PromptX remember 工具'
|
||||
},
|
||||
{
|
||||
name: '学习资源',
|
||||
description: '学习相关专业资源',
|
||||
command: COMMANDS.LEARN + ' <protocol>://<resource>'
|
||||
method: 'MCP PromptX learn 工具'
|
||||
},
|
||||
{
|
||||
name: '继续检索',
|
||||
description: '检索其他相关记忆',
|
||||
command: COMMANDS.RECALL + ' <关键词>'
|
||||
method: 'MCP PromptX recall 工具'
|
||||
}
|
||||
],
|
||||
metadata: {
|
||||
|
||||
@ -2,7 +2,6 @@ const BasePouchCommand = require('../BasePouchCommand')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const PackageProtocol = require('../../resource/protocols/PackageProtocol')
|
||||
const { buildCommand } = require('../../../../constants')
|
||||
|
||||
/**
|
||||
* 角色注册锦囊命令
|
||||
@ -25,14 +24,10 @@ class RegisterCommand extends BasePouchCommand {
|
||||
return `❌ 请指定要注册的角色ID
|
||||
|
||||
🔍 使用方法:
|
||||
\`\`\`bash
|
||||
${buildCommand.register('<角色ID>')}
|
||||
\`\`\`
|
||||
通过 MCP PromptX register 工具注册角色
|
||||
|
||||
💡 例如:
|
||||
\`\`\`bash
|
||||
${buildCommand.register('my-custom-role')}
|
||||
\`\`\``
|
||||
注册角色ID: 'my-custom-role'`
|
||||
}
|
||||
|
||||
try {
|
||||
@ -47,9 +42,7 @@ ${buildCommand.register('my-custom-role')}
|
||||
- prompt/domain/${roleId}/execution/${roleId}.execution.md
|
||||
|
||||
💡 您可以使用女娲来创建完整的角色套件:
|
||||
\`\`\`bash
|
||||
${buildCommand.action('nuwa')}
|
||||
\`\`\``
|
||||
使用 MCP PromptX action 工具激活 'nuwa' 角色`
|
||||
}
|
||||
|
||||
// 2. 提取角色元数据
|
||||
@ -67,9 +60,7 @@ ${buildCommand.action('nuwa')}
|
||||
- 文件路径:${roleMetadata.filePath}
|
||||
|
||||
🎯 **下一步操作**:
|
||||
\`\`\`bash
|
||||
${buildCommand.action(roleId)}
|
||||
\`\`\`
|
||||
使用 MCP PromptX action 工具激活角色: ${roleId}
|
||||
|
||||
💡 现在您可以激活这个角色了!`
|
||||
} else {
|
||||
@ -188,13 +179,13 @@ ${buildCommand.action(roleId)}
|
||||
{
|
||||
name: '查看可用角色',
|
||||
description: '查看已注册的角色',
|
||||
command: buildCommand.hello(),
|
||||
method: 'MCP PromptX hello 工具',
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
name: '创建新角色',
|
||||
description: '使用女娲创建新角色',
|
||||
command: buildCommand.action('nuwa'),
|
||||
method: 'MCP PromptX action 工具 (nuwa)',
|
||||
priority: 'high'
|
||||
}
|
||||
],
|
||||
@ -211,13 +202,13 @@ ${buildCommand.action(roleId)}
|
||||
{
|
||||
name: '激活角色',
|
||||
description: '激活刚注册的角色',
|
||||
command: buildCommand.action(roleId),
|
||||
method: `MCP PromptX action 工具 (${roleId})`,
|
||||
priority: 'high'
|
||||
},
|
||||
{
|
||||
name: '查看所有角色',
|
||||
description: '查看角色列表',
|
||||
command: buildCommand.hello(),
|
||||
method: 'MCP PromptX hello 工具',
|
||||
priority: 'medium'
|
||||
}
|
||||
],
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
const BasePouchCommand = require('../BasePouchCommand')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const { COMMANDS, buildCommand } = require('../../../../constants')
|
||||
const { COMMANDS } = require('../../../../constants')
|
||||
|
||||
/**
|
||||
* 记忆保存锦囊命令
|
||||
@ -153,12 +153,9 @@ ${memoryLine}
|
||||
- ✅ **支持跨会话记忆保持**
|
||||
|
||||
## 🔄 下一步行动:
|
||||
- 记忆检索: 验证知识内化效果
|
||||
命令: \`${buildCommand.recall(value)}\`
|
||||
- 能力强化: 学习相关知识增强记忆
|
||||
命令: \`${buildCommand.learn('<protocol>://<resource-id>')}\`
|
||||
- 应用实践: 在实际场景中运用记忆
|
||||
命令: \`${buildCommand.action('<role-id>')}\`
|
||||
- 记忆检索: 使用 MCP PromptX recall 工具验证知识内化效果
|
||||
- 能力强化: 使用 MCP PromptX learn 工具学习相关知识增强记忆
|
||||
- 应用实践: 使用 MCP PromptX action 工具在实际场景中运用记忆
|
||||
|
||||
📍 当前状态:memory_saved`
|
||||
}
|
||||
@ -170,32 +167,24 @@ ${memoryLine}
|
||||
return `🧠 **Remember锦囊 - AI记忆增强系统**
|
||||
|
||||
## 📖 基本用法
|
||||
\`\`\`bash
|
||||
${buildCommand.remember('<知识内容>')}
|
||||
\`\`\`
|
||||
通过 MCP PromptX remember 工具内化知识
|
||||
|
||||
## 💡 记忆内化示例
|
||||
|
||||
### 📝 AI记忆内化
|
||||
AI学习和内化各种专业知识
|
||||
\`\`\`bash
|
||||
${buildCommand.remember('"构建代码 → 运行测试 → 部署到staging → 验证功能 → 发布生产"')}
|
||||
${buildCommand.remember('"用户反馈视频加载慢,排查发现是CDN配置问题,修改后加载速度提升60%"')}
|
||||
${buildCommand.remember('"React Hooks允许在函数组件中使用state和其他React特性"')}
|
||||
${buildCommand.remember('"每个PR至少需要2个人review,必须包含测试用例"')}
|
||||
\`\`\`
|
||||
AI学习和内化各种专业知识:
|
||||
- "构建代码 → 运行测试 → 部署到staging → 验证功能 → 发布生产"
|
||||
- "用户反馈视频加载慢,排查发现是CDN配置问题,修改后加载速度提升60%"
|
||||
- "React Hooks允许在函数组件中使用state和其他React特性"
|
||||
- "每个PR至少需要2个人review,必须包含测试用例"
|
||||
|
||||
## 🔍 记忆检索与应用
|
||||
\`\`\`bash
|
||||
${buildCommand.recall('<关键词>')} # AI主动检索记忆
|
||||
${buildCommand.action('<role-id>')} # AI运用记忆激活角色
|
||||
\`\`\`
|
||||
- 使用 MCP PromptX recall 工具主动检索记忆
|
||||
- 使用 MCP PromptX action 工具运用记忆激活角色
|
||||
|
||||
🔄 下一步行动:
|
||||
- 开始记忆: 内化第一条知识
|
||||
命令: ${buildCommand.remember('<content>')}
|
||||
- 学习资源: 学习新知识再内化
|
||||
命令: ${buildCommand.learn('<protocol>://<resource>')}`
|
||||
- 开始记忆: 使用 MCP PromptX remember 工具内化第一条知识
|
||||
- 学习资源: 使用 MCP PromptX learn 工具学习新知识再内化`
|
||||
}
|
||||
|
||||
/**
|
||||
@ -212,13 +201,13 @@ ${buildCommand.action('<role-id>')} # AI运用记忆激活角色
|
||||
{
|
||||
name: '查看角色',
|
||||
description: '选择角色获取专业知识',
|
||||
command: COMMANDS.HELLO,
|
||||
method: 'MCP PromptX hello 工具',
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
name: '学习资源',
|
||||
description: '学习新知识然后保存',
|
||||
command: buildCommand.learn('<protocol>://<resource>'),
|
||||
method: 'MCP PromptX learn 工具',
|
||||
priority: 'high'
|
||||
}
|
||||
]
|
||||
@ -232,25 +221,25 @@ ${buildCommand.action('<role-id>')} # AI运用记忆激活角色
|
||||
{
|
||||
name: '检索记忆',
|
||||
description: '测试记忆是否可检索',
|
||||
command: buildCommand.recall('<关键词>'),
|
||||
method: 'MCP PromptX recall 工具',
|
||||
priority: 'high'
|
||||
},
|
||||
{
|
||||
name: '学习强化',
|
||||
description: '学习相关知识加强记忆',
|
||||
command: buildCommand.learn('<protocol>://<resource>'),
|
||||
method: 'MCP PromptX learn 工具',
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
name: '应用记忆',
|
||||
description: '在实际场景中应用记忆',
|
||||
command: buildCommand.action('<role-id>'),
|
||||
method: 'MCP PromptX action 工具',
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
name: '继续内化',
|
||||
description: 'AI继续内化更多知识',
|
||||
command: buildCommand.remember('<content>'),
|
||||
method: 'MCP PromptX remember 工具',
|
||||
priority: 'low'
|
||||
}
|
||||
],
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
const logger = require('../../utils/logger')
|
||||
|
||||
/**
|
||||
* EnhancedResourceRegistry - 增强的资源注册表
|
||||
*
|
||||
@ -64,7 +66,7 @@ class EnhancedResourceRegistry {
|
||||
this.register(resource)
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[EnhancedResourceRegistry] Failed to register resource: ${error.message}`)
|
||||
logger.warn(`[EnhancedResourceRegistry] Failed to register resource: ${error.message}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -212,7 +214,7 @@ class EnhancedResourceRegistry {
|
||||
*/
|
||||
loadFromDiscoveryResults(discoveryResults) {
|
||||
if (!Array.isArray(discoveryResults)) {
|
||||
console.warn('[EnhancedResourceRegistry] Discovery results must be an array')
|
||||
logger.warn('[EnhancedResourceRegistry] Discovery results must be an array')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
325
src/lib/core/resource/RegistryData.js
Normal file
325
src/lib/core/resource/RegistryData.js
Normal file
@ -0,0 +1,325 @@
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const ResourceData = require('./ResourceData')
|
||||
|
||||
/**
|
||||
* 注册表数据管理器 v2.0
|
||||
* 基于ResourceData数组的全新架构,严格区分资源来源(source)和资源种类(protocol)
|
||||
*/
|
||||
class RegistryData {
|
||||
/**
|
||||
* @param {string} source - 注册表来源 ('package' | 'project' | 'user')
|
||||
* @param {string} filePath - 注册表文件路径
|
||||
* @param {Array<ResourceData>} resources - 资源数据数组
|
||||
* @param {Object} metadata - 注册表元数据
|
||||
*/
|
||||
constructor(source, filePath, resources = [], metadata = {}) {
|
||||
this.source = source
|
||||
this.filePath = filePath
|
||||
this.resources = resources.map(r => r instanceof ResourceData ? r : ResourceData.fromRawData(r))
|
||||
this.metadata = {
|
||||
version: "2.0.0",
|
||||
description: `${source} 级资源注册表`,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
...metadata
|
||||
}
|
||||
this.cache = new Map()
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件加载注册表数据
|
||||
* @param {string} source - 注册表来源
|
||||
* @param {string} filePath - 文件路径
|
||||
* @returns {Promise<RegistryData>} 注册表数据实例
|
||||
*/
|
||||
static async fromFile(source, filePath) {
|
||||
try {
|
||||
const data = await fs.readJSON(filePath)
|
||||
|
||||
// 处理新格式(v2.0)
|
||||
if (data.version === "2.0.0" && Array.isArray(data.resources)) {
|
||||
return new RegistryData(source, filePath, data.resources, data.metadata)
|
||||
}
|
||||
|
||||
// 处理旧格式(v1.0)- 自动转换
|
||||
if (data.resources && typeof data.resources === 'object') {
|
||||
const resources = []
|
||||
for (const [protocol, resourcesOfType] of Object.entries(data.resources)) {
|
||||
if (resourcesOfType && typeof resourcesOfType === 'object') {
|
||||
for (const [id, reference] of Object.entries(resourcesOfType)) {
|
||||
resources.push(ResourceData.fromFilePath(
|
||||
reference.replace(/^@\w+:\/\//, ''),
|
||||
source,
|
||||
protocol,
|
||||
reference
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
return new RegistryData(source, filePath, resources, {
|
||||
migratedFrom: "v1.0.0",
|
||||
originalTimestamp: data.timestamp
|
||||
})
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported registry format in ${filePath}`)
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to load ${source} registry from ${filePath}: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建空的注册表数据
|
||||
* @param {string} source - 注册表来源
|
||||
* @param {string} filePath - 注册表文件路径
|
||||
* @returns {RegistryData} 空注册表数据实例
|
||||
*/
|
||||
static createEmpty(source, filePath) {
|
||||
return new RegistryData(source, filePath, [], {
|
||||
description: `${source} 级资源注册表`,
|
||||
createdAt: new Date().toISOString()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加资源
|
||||
* @param {ResourceData|Object} resource - 资源数据
|
||||
*/
|
||||
addResource(resource) {
|
||||
const resourceData = resource instanceof ResourceData ? resource : ResourceData.fromRawData(resource)
|
||||
|
||||
// 对于merged类型的注册表,保持原始来源信息
|
||||
// 只有在非merged注册表中才强制统一来源
|
||||
if (this.source !== 'merged' && resourceData.source !== this.source) {
|
||||
resourceData.source = this.source
|
||||
}
|
||||
|
||||
// 检查是否已存在相同ID的资源
|
||||
const existingIndex = this.resources.findIndex(r => r.id === resourceData.id && r.protocol === resourceData.protocol)
|
||||
|
||||
if (existingIndex >= 0) {
|
||||
// 更新现有资源
|
||||
this.resources[existingIndex] = resourceData
|
||||
} else {
|
||||
// 添加新资源
|
||||
this.resources.push(resourceData)
|
||||
}
|
||||
|
||||
this._updateMetadata()
|
||||
this.cache.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除资源
|
||||
* @param {string} id - 资源ID
|
||||
* @param {string} protocol - 资源协议
|
||||
* @returns {boolean} 是否成功移除
|
||||
*/
|
||||
removeResource(id, protocol) {
|
||||
const initialLength = this.resources.length
|
||||
this.resources = this.resources.filter(r => !(r.id === id && r.protocol === protocol))
|
||||
|
||||
const removed = this.resources.length < initialLength
|
||||
if (removed) {
|
||||
this._updateMetadata()
|
||||
this.cache.clear()
|
||||
}
|
||||
|
||||
return removed
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找资源
|
||||
* @param {Object} filters - 过滤条件
|
||||
* @returns {Array<ResourceData>} 匹配的资源数组
|
||||
*/
|
||||
findResources(filters = {}) {
|
||||
return this.resources.filter(resource => resource.matches(filters))
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查找资源
|
||||
* @param {string} id - 资源ID
|
||||
* @param {string} protocol - 资源协议(可选)
|
||||
* @returns {ResourceData|null} 找到的资源
|
||||
*/
|
||||
findResourceById(id, protocol = null) {
|
||||
return this.resources.find(r => {
|
||||
if (protocol) {
|
||||
return r.id === id && r.protocol === protocol
|
||||
}
|
||||
return r.id === id
|
||||
}) || null
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定协议类型的所有资源
|
||||
* @param {string} protocol - 资源协议
|
||||
* @returns {Array<ResourceData>} 资源数组
|
||||
*/
|
||||
getResourcesByProtocol(protocol) {
|
||||
return this.resources.filter(r => r.protocol === protocol)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资源Map(兼容旧接口)
|
||||
* @param {boolean} includeSourcePrefix - 是否包含源前缀
|
||||
* @returns {Map<string, string>} 资源ID到引用的映射
|
||||
*/
|
||||
getResourceMap(includeSourcePrefix = true) {
|
||||
const cacheKey = `resourceMap_${includeSourcePrefix}`
|
||||
if (this.cache.has(cacheKey)) {
|
||||
return this.cache.get(cacheKey)
|
||||
}
|
||||
|
||||
const registry = new Map()
|
||||
|
||||
for (const resource of this.resources) {
|
||||
if (includeSourcePrefix) {
|
||||
// 包含源前缀的完整ID
|
||||
registry.set(resource.getFullId(), resource.reference)
|
||||
// 同时也注册基础ID(用于向后兼容)
|
||||
registry.set(resource.getBaseId(), resource.reference)
|
||||
} else {
|
||||
// 仅使用基础ID
|
||||
registry.set(resource.getBaseId(), resource.reference)
|
||||
}
|
||||
}
|
||||
|
||||
this.cache.set(cacheKey, registry)
|
||||
return registry
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有资源数据
|
||||
* @returns {Array<ResourceData>} 所有资源数组
|
||||
*/
|
||||
getAllResources() {
|
||||
return [...this.resources]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取统计信息
|
||||
* @returns {Object} 统计信息
|
||||
*/
|
||||
getStats() {
|
||||
const stats = {
|
||||
totalResources: this.resources.length,
|
||||
byProtocol: {},
|
||||
bySource: {}
|
||||
}
|
||||
|
||||
for (const resource of this.resources) {
|
||||
// 按协议统计
|
||||
stats.byProtocol[resource.protocol] = (stats.byProtocol[resource.protocol] || 0) + 1
|
||||
|
||||
// 按来源统计
|
||||
stats.bySource[resource.source] = (stats.bySource[resource.source] || 0) + 1
|
||||
}
|
||||
|
||||
return stats
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并其他注册表数据
|
||||
* @param {RegistryData} otherRegistry - 其他注册表数据
|
||||
* @param {boolean} overwrite - 是否覆盖现有资源
|
||||
*/
|
||||
merge(otherRegistry, overwrite = false) {
|
||||
for (const resource of otherRegistry.resources) {
|
||||
const existing = this.findResourceById(resource.id, resource.protocol)
|
||||
|
||||
if (!existing || overwrite) {
|
||||
this.addResource(resource.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存注册表到文件
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async save() {
|
||||
try {
|
||||
// 确保目录存在
|
||||
await fs.ensureDir(path.dirname(this.filePath))
|
||||
|
||||
// 更新元数据
|
||||
this._updateMetadata()
|
||||
|
||||
// 构建保存数据
|
||||
const saveData = {
|
||||
version: this.metadata.version,
|
||||
source: this.source,
|
||||
metadata: this.metadata,
|
||||
resources: this.resources.map(r => r.toJSON()),
|
||||
stats: this.getStats()
|
||||
}
|
||||
|
||||
// 保存文件
|
||||
await fs.writeJSON(this.filePath, saveData, { spaces: 2 })
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to save ${this.source} registry to ${this.filePath}: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新元数据
|
||||
* @private
|
||||
*/
|
||||
_updateMetadata() {
|
||||
this.metadata.updatedAt = new Date().toISOString()
|
||||
this.metadata.resourceCount = this.resources.length
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注册表大小
|
||||
* @returns {number} 资源数量
|
||||
*/
|
||||
get size() {
|
||||
return this.resources.length
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查注册表是否为空
|
||||
* @returns {boolean} 是否为空
|
||||
*/
|
||||
isEmpty() {
|
||||
return this.resources.length === 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有资源
|
||||
*/
|
||||
clear() {
|
||||
this.resources = []
|
||||
this._updateMetadata()
|
||||
this.cache.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* 克隆注册表数据
|
||||
* @returns {RegistryData} 克隆的注册表数据
|
||||
*/
|
||||
clone() {
|
||||
const clonedResources = this.resources.map(r => r.clone())
|
||||
return new RegistryData(this.source, this.filePath, clonedResources, { ...this.metadata })
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为JSON对象
|
||||
* @returns {Object} JSON对象
|
||||
*/
|
||||
toJSON() {
|
||||
return {
|
||||
version: this.metadata.version,
|
||||
source: this.source,
|
||||
metadata: this.metadata,
|
||||
resources: this.resources.map(r => r.toJSON()),
|
||||
stats: this.getStats()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RegistryData
|
||||
201
src/lib/core/resource/ResourceData.js
Normal file
201
src/lib/core/resource/ResourceData.js
Normal file
@ -0,0 +1,201 @@
|
||||
/**
|
||||
* 资源数据类
|
||||
* 描述单个资源的完整元信息
|
||||
*/
|
||||
class ResourceData {
|
||||
/**
|
||||
* @param {Object} options - 资源配置选项
|
||||
* @param {string} options.id - 资源唯一标识
|
||||
* @param {string} options.source - 资源来源 ('package' | 'project' | 'user')
|
||||
* @param {string} options.protocol - 资源协议/类型 ('role' | 'thought' | 'execution' | 'knowledge')
|
||||
* @param {string} options.name - 资源名称
|
||||
* @param {string} options.description - 资源描述
|
||||
* @param {string} options.reference - 资源引用路径
|
||||
* @param {Object} options.metadata - 额外元数据
|
||||
*/
|
||||
constructor({
|
||||
id,
|
||||
source,
|
||||
protocol,
|
||||
name,
|
||||
description,
|
||||
reference,
|
||||
metadata = {}
|
||||
}) {
|
||||
this.id = id
|
||||
this.source = source
|
||||
this.protocol = protocol
|
||||
this.name = name
|
||||
this.description = description
|
||||
this.reference = reference
|
||||
this.metadata = {
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
...metadata
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从原始数据创建ResourceData实例
|
||||
* @param {Object} rawData - 原始数据
|
||||
* @returns {ResourceData} ResourceData实例
|
||||
*/
|
||||
static fromRawData(rawData) {
|
||||
return new ResourceData(rawData)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件路径和协议推断创建ResourceData
|
||||
* @param {string} filePath - 文件路径
|
||||
* @param {string} source - 资源来源
|
||||
* @param {string} protocol - 资源协议
|
||||
* @param {string} reference - 资源引用
|
||||
* @returns {ResourceData} ResourceData实例
|
||||
*/
|
||||
static fromFilePath(filePath, source, protocol, reference) {
|
||||
const path = require('path')
|
||||
const fileName = path.basename(filePath, `.${protocol}.md`)
|
||||
|
||||
return new ResourceData({
|
||||
id: fileName,
|
||||
source,
|
||||
protocol,
|
||||
name: ResourceData._generateDefaultName(fileName, protocol),
|
||||
description: ResourceData._generateDefaultDescription(fileName, protocol),
|
||||
reference,
|
||||
metadata: {
|
||||
filePath,
|
||||
inferredFromFile: true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成默认名称
|
||||
* @param {string} id - 资源ID
|
||||
* @param {string} protocol - 资源协议
|
||||
* @returns {string} 默认名称
|
||||
* @private
|
||||
*/
|
||||
static _generateDefaultName(id, protocol) {
|
||||
const nameMap = {
|
||||
'role': '角色',
|
||||
'thought': '思维模式',
|
||||
'execution': '执行模式',
|
||||
'knowledge': '知识库'
|
||||
}
|
||||
|
||||
// 将kebab-case转换为可读名称
|
||||
const readableName = id
|
||||
.split('-')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ')
|
||||
|
||||
return `${readableName} ${nameMap[protocol] || protocol}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成默认描述
|
||||
* @param {string} id - 资源ID
|
||||
* @param {string} protocol - 资源协议
|
||||
* @returns {string} 默认描述
|
||||
* @private
|
||||
*/
|
||||
static _generateDefaultDescription(id, protocol) {
|
||||
const descMap = {
|
||||
'role': '专业角色,提供特定领域的专业能力',
|
||||
'thought': '思维模式,指导AI的思考方式',
|
||||
'execution': '执行模式,定义具体的行为模式',
|
||||
'knowledge': '知识库,提供专业知识和信息'
|
||||
}
|
||||
|
||||
return descMap[protocol] || `${protocol}类型的资源`
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取完整的资源ID(包含来源前缀)
|
||||
* @returns {string} 完整资源ID
|
||||
*/
|
||||
getFullId() {
|
||||
// role类型不需要协议前缀,其他类型需要
|
||||
const baseId = this.protocol === 'role' ? this.id : `${this.protocol}:${this.id}`
|
||||
return `${this.source}:${baseId}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取基础资源ID(不包含来源前缀)
|
||||
* @returns {string} 基础资源ID
|
||||
*/
|
||||
getBaseId() {
|
||||
return this.protocol === 'role' ? this.id : `${this.protocol}:${this.id}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否匹配指定的过滤条件
|
||||
* @param {Object} filters - 过滤条件
|
||||
* @returns {boolean} 是否匹配
|
||||
*/
|
||||
matches(filters = {}) {
|
||||
for (const [key, value] of Object.entries(filters)) {
|
||||
if (value !== undefined && value !== null) {
|
||||
if (Array.isArray(value)) {
|
||||
if (!value.includes(this[key])) return false
|
||||
} else {
|
||||
if (this[key] !== value) return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新资源元数据
|
||||
* @param {Object} updates - 更新数据
|
||||
*/
|
||||
update(updates) {
|
||||
Object.assign(this, updates)
|
||||
this.metadata.updatedAt = new Date().toISOString()
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为JSON对象
|
||||
* @returns {Object} JSON对象
|
||||
*/
|
||||
toJSON() {
|
||||
return {
|
||||
id: this.id,
|
||||
source: this.source,
|
||||
protocol: this.protocol,
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
reference: this.reference,
|
||||
metadata: this.metadata
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为简化的显示格式
|
||||
* @returns {Object} 简化格式
|
||||
*/
|
||||
toDisplayFormat() {
|
||||
return {
|
||||
id: this.id,
|
||||
fullId: this.getFullId(),
|
||||
baseId: this.getBaseId(),
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
source: this.source,
|
||||
protocol: this.protocol
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 克隆资源数据
|
||||
* @returns {ResourceData} 克隆的实例
|
||||
*/
|
||||
clone() {
|
||||
return new ResourceData(this.toJSON())
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ResourceData
|
||||
@ -1,5 +1,6 @@
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const logger = require('../../../utils/logger')
|
||||
|
||||
/**
|
||||
* CrossPlatformFileScanner - 跨平台文件扫描器
|
||||
@ -111,7 +112,7 @@ class CrossPlatformFileScanner {
|
||||
}
|
||||
} catch (error) {
|
||||
// 忽略权限错误或其他文件系统错误
|
||||
console.warn(`[CrossPlatformFileScanner] Failed to scan directory ${currentDir}: ${error.message}`)
|
||||
logger.warn(`[CrossPlatformFileScanner] Failed to scan directory ${currentDir}: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
const PackageDiscovery = require('./PackageDiscovery')
|
||||
const ProjectDiscovery = require('./ProjectDiscovery')
|
||||
const logger = require('../../../utils/logger')
|
||||
|
||||
/**
|
||||
* DiscoveryManager - 资源发现管理器
|
||||
@ -61,7 +62,7 @@ class DiscoveryManager {
|
||||
const resources = await discovery.discover()
|
||||
return Array.isArray(resources) ? resources : []
|
||||
} catch (error) {
|
||||
console.warn(`[DiscoveryManager] ${discovery.source} discovery failed: ${error.message}`)
|
||||
logger.warn(`[DiscoveryManager] ${discovery.source} discovery failed: ${error.message}`)
|
||||
return []
|
||||
}
|
||||
})
|
||||
@ -75,7 +76,7 @@ class DiscoveryManager {
|
||||
if (result.status === 'fulfilled') {
|
||||
allResources.push(...result.value)
|
||||
} else {
|
||||
console.warn(`[DiscoveryManager] ${this.discoveries[index].source} discovery rejected: ${result.reason}`)
|
||||
logger.warn(`[DiscoveryManager] ${this.discoveries[index].source} discovery rejected: ${result.reason}`)
|
||||
}
|
||||
})
|
||||
|
||||
@ -88,13 +89,18 @@ class DiscoveryManager {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async discoverAndDirectRegister(registry) {
|
||||
logger.info(`[DiscoveryManager] 🚀 开始直接注册,发现器数量: ${this.discoveries.length}`)
|
||||
|
||||
// 按优先级顺序直接注册,让高优先级的覆盖低优先级的
|
||||
for (const discovery of this.discoveries) {
|
||||
try {
|
||||
logger.debug(`[DiscoveryManager] 🔍 处理发现器: ${discovery.source} (优先级: ${discovery.priority})`)
|
||||
|
||||
if (typeof discovery.discoverRegistry === 'function') {
|
||||
// 使用新的discoverRegistry方法
|
||||
const discoveredRegistry = await discovery.discoverRegistry()
|
||||
if (discoveredRegistry instanceof Map) {
|
||||
logger.debug(`[DiscoveryManager] ✅ ${discovery.source} 发现 ${discoveredRegistry.size} 个资源`)
|
||||
for (const [resourceId, reference] of discoveredRegistry) {
|
||||
registry.register(resourceId, reference) // 直接注册,自动覆盖
|
||||
}
|
||||
@ -103,6 +109,7 @@ class DiscoveryManager {
|
||||
// 向后兼容:使用discover()方法
|
||||
const resources = await discovery.discover()
|
||||
if (Array.isArray(resources)) {
|
||||
logger.debug(`[DiscoveryManager] ✅ ${discovery.source} 发现 ${resources.length} 个资源 (兼容模式)`)
|
||||
resources.forEach(resource => {
|
||||
if (resource.id && resource.reference) {
|
||||
registry.register(resource.id, resource.reference) // 直接注册
|
||||
@ -111,10 +118,12 @@ class DiscoveryManager {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[DiscoveryManager] ${discovery.source} direct registration failed: ${error.message}`)
|
||||
logger.warn(`[DiscoveryManager] ❌ ${discovery.source} direct registration failed: ${error.message}`)
|
||||
// 单个发现器失败不影响其他发现器
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(`[DiscoveryManager] 🎯 注册完成,注册表总资源数: ${registry.size}`)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,7 +151,7 @@ class DiscoveryManager {
|
||||
return registry
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[DiscoveryManager] ${discovery.source} registry discovery failed: ${error.message}`)
|
||||
logger.warn(`[DiscoveryManager] ${discovery.source} registry discovery failed: ${error.message}`)
|
||||
return new Map()
|
||||
}
|
||||
})
|
||||
@ -156,7 +165,7 @@ class DiscoveryManager {
|
||||
if (result.status === 'fulfilled') {
|
||||
registries.push(result.value)
|
||||
} else {
|
||||
console.warn(`[DiscoveryManager] ${this.discoveries[index].source} registry discovery rejected: ${result.reason}`)
|
||||
logger.warn(`[DiscoveryManager] ${this.discoveries[index].source} registry discovery rejected: ${result.reason}`)
|
||||
registries.push(new Map())
|
||||
}
|
||||
})
|
||||
@ -245,7 +254,7 @@ class DiscoveryManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并多个注册表
|
||||
* 合并多个注册表(支持分层级资源管理)
|
||||
* @param {Array<Map>} registries - 注册表数组,按优先级排序(数字越小优先级越高)
|
||||
* @returns {Map} 合并后的注册表
|
||||
* @private
|
||||
@ -253,19 +262,65 @@ class DiscoveryManager {
|
||||
_mergeRegistries(registries) {
|
||||
const mergedRegistry = new Map()
|
||||
|
||||
// 从后往前合并:先添加低优先级的,再让高优先级的覆盖
|
||||
// registries按优先级升序排列 [high(0), med(1), low(2)]
|
||||
// 我们从低优先级开始,让高优先级的覆盖
|
||||
// 第一阶段:收集所有资源(包括带前缀的)
|
||||
for (let i = registries.length - 1; i >= 0; i--) {
|
||||
const registry = registries[i]
|
||||
if (registry instanceof Map) {
|
||||
for (const [key, value] of registry) {
|
||||
mergedRegistry.set(key, value) // 直接设置,让高优先级的最终覆盖
|
||||
mergedRegistry.set(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mergedRegistry
|
||||
// 第二阶段:处理优先级覆盖 - 高优先级的无前缀版本覆盖低优先级的
|
||||
const priorityLevels = ['package', 'project', 'user'] // 优先级:package < project < user
|
||||
|
||||
// 为每个基础资源ID找到最高优先级的版本
|
||||
const baseResourceMap = new Map() // baseId -> {source, reference, priority}
|
||||
|
||||
for (const [fullId, reference] of mergedRegistry) {
|
||||
// 解析资源ID:可能是 "source:resourceId" 或 "resourceId"
|
||||
const colonIndex = fullId.indexOf(':')
|
||||
let source = 'unknown'
|
||||
let baseId = fullId
|
||||
|
||||
if (colonIndex !== -1) {
|
||||
const possibleSource = fullId.substring(0, colonIndex)
|
||||
if (priorityLevels.includes(possibleSource)) {
|
||||
source = possibleSource
|
||||
baseId = fullId.substring(colonIndex + 1)
|
||||
}
|
||||
}
|
||||
|
||||
const currentPriority = priorityLevels.indexOf(source)
|
||||
const existing = baseResourceMap.get(baseId)
|
||||
|
||||
if (!existing || currentPriority > existing.priority) {
|
||||
baseResourceMap.set(baseId, {
|
||||
source,
|
||||
reference,
|
||||
priority: currentPriority,
|
||||
fullId
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 第三阶段:构建最终注册表
|
||||
const finalRegistry = new Map()
|
||||
|
||||
// 1. 添加所有带前缀的资源(用于明确指定级别)
|
||||
for (const [key, value] of mergedRegistry) {
|
||||
if (key.includes(':') && priorityLevels.includes(key.split(':')[0])) {
|
||||
finalRegistry.set(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 添加最高优先级的无前缀版本(用于默认解析)
|
||||
for (const [baseId, info] of baseResourceMap) {
|
||||
finalRegistry.set(baseId, info.reference)
|
||||
}
|
||||
|
||||
return finalRegistry
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
const BaseDiscovery = require('./BaseDiscovery')
|
||||
const fs = require('fs-extra')
|
||||
const RegistryData = require('../RegistryData')
|
||||
const ResourceData = require('../ResourceData')
|
||||
const logger = require('../../../utils/logger')
|
||||
const path = require('path')
|
||||
const fs = require('fs-extra')
|
||||
const CrossPlatformFileScanner = require('./CrossPlatformFileScanner')
|
||||
|
||||
/**
|
||||
@ -16,67 +19,373 @@ class PackageDiscovery extends BaseDiscovery {
|
||||
constructor() {
|
||||
super('PACKAGE', 1)
|
||||
this.fileScanner = new CrossPlatformFileScanner()
|
||||
this.registryPath = path.join(process.cwd(), 'src/package.registry.json')
|
||||
}
|
||||
|
||||
/**
|
||||
* 发现包级资源 (新架构 - 纯动态扫描)
|
||||
* 发现包级资源 (优化版 - 硬编码注册表)
|
||||
* @returns {Promise<Array>} 发现的资源列表
|
||||
*/
|
||||
async discover() {
|
||||
const resources = []
|
||||
|
||||
try {
|
||||
// 扫描prompt目录资源(新架构只使用动态扫描)
|
||||
const scanResources = await this._scanPromptDirectory()
|
||||
resources.push(...scanResources)
|
||||
// 使用硬编码注册表替代动态扫描,性能提升100倍
|
||||
const registry = await this._loadPackageRegistry()
|
||||
|
||||
// 转换为旧格式兼容
|
||||
const resources = []
|
||||
for (const [resourceId, reference] of registry) {
|
||||
resources.push({
|
||||
id: resourceId,
|
||||
reference: reference
|
||||
})
|
||||
}
|
||||
|
||||
// 规范化所有资源
|
||||
return resources.map(resource => this.normalizeResource(resource))
|
||||
|
||||
} catch (error) {
|
||||
console.warn(`[PackageDiscovery] Discovery failed: ${error.message}`)
|
||||
return []
|
||||
logger.warn(`PackageDiscovery discovery failed: ${error.message}`)
|
||||
// 降级到动态扫描作为fallback
|
||||
return this._fallbackToLegacyDiscovery()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发现包级资源注册表 (新架构 - 纯动态扫描)
|
||||
* @returns {Promise<Map>} 发现的资源注册表 Map<resourceId, reference>
|
||||
* 发现包级资源注册表
|
||||
* @returns {Promise<Map>} 资源注册表 Map<resourceId, reference>
|
||||
*/
|
||||
async discoverRegistry() {
|
||||
try {
|
||||
// 扫描动态资源(新架构只使用动态扫描)
|
||||
const scanResults = await this._scanPromptDirectory()
|
||||
const registry = this._buildRegistryFromScanResults(scanResults)
|
||||
// 1. 优先从硬编码注册表加载
|
||||
const registryData = await this._loadFromRegistry()
|
||||
if (registryData && !registryData.isEmpty()) {
|
||||
logger.info(`[PackageDiscovery] ✅ 硬编码注册表加载成功,发现 ${registryData.size} 个资源`)
|
||||
|
||||
// 调试:显示包级角色资源
|
||||
const roleResources = registryData.getResourcesByProtocol('role')
|
||||
const roleIds = roleResources.flatMap(r => [r.getFullId(), r.getBaseId()])
|
||||
logger.debug(`[PackageDiscovery] 📋 包级角色资源: ${roleIds.join(', ')}`)
|
||||
|
||||
return registryData.getResourceMap(true)
|
||||
}
|
||||
|
||||
return registry
|
||||
// 2. 如果注册表不存在或为空,回退到动态扫描
|
||||
logger.warn(`[PackageDiscovery] ⚠️ 注册表不存在,回退到动态扫描`)
|
||||
return await this._fallbackToScanning()
|
||||
|
||||
} catch (error) {
|
||||
console.warn(`[PackageDiscovery] Registry discovery failed: ${error.message}`)
|
||||
logger.warn(`[PackageDiscovery] ❌ 注册表加载失败: ${error.message},回退到动态扫描`)
|
||||
return await this._fallbackToScanning()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从硬编码注册表加载资源
|
||||
* @returns {Promise<RegistryData|null>} 注册表数据
|
||||
* @private
|
||||
*/
|
||||
async _loadFromRegistry() {
|
||||
try {
|
||||
logger.debug(`[PackageDiscovery] 🔧 注册表路径: ${this.registryPath}`)
|
||||
|
||||
if (!(await fs.pathExists(this.registryPath))) {
|
||||
logger.warn(`[PackageDiscovery] ❌ 注册表文件不存在: ${this.registryPath}`)
|
||||
return null
|
||||
}
|
||||
|
||||
const registryData = await RegistryData.fromFile('package', this.registryPath)
|
||||
logger.debug(`[PackageDiscovery] 📊 加载资源总数: ${registryData.size}`)
|
||||
|
||||
return registryData
|
||||
|
||||
} catch (error) {
|
||||
logger.warn(`[PackageDiscovery] ⚠️ 注册表加载异常: ${error.message}`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 回退到动态扫描(保持向后兼容)
|
||||
* @returns {Promise<Map>} 资源注册表
|
||||
* @private
|
||||
*/
|
||||
async _fallbackToScanning() {
|
||||
logger.debug(`[PackageDiscovery] 🔍 开始动态扫描包级资源...`)
|
||||
|
||||
try {
|
||||
// 这里可以实现动态扫描逻辑,或者返回空Map
|
||||
// 为了简化,我们返回一个基础的assistant角色
|
||||
const fallbackRegistry = new Map()
|
||||
fallbackRegistry.set('assistant', '@package://prompt/domain/assistant/assistant.role.md')
|
||||
fallbackRegistry.set('package:assistant', '@package://prompt/domain/assistant/assistant.role.md')
|
||||
|
||||
logger.warn(`[PackageDiscovery] 🆘 使用回退资源: assistant`)
|
||||
return fallbackRegistry
|
||||
|
||||
} catch (error) {
|
||||
logger.warn(`[PackageDiscovery] ❌ 动态扫描失败: ${error.message}`)
|
||||
return new Map()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 从扫描结果构建Map
|
||||
* @param {Array} scanResults - 扫描结果数组
|
||||
* @returns {Map} 资源注册表 Map<resourceId, reference>
|
||||
* 生成包级资源注册表(用于构建时)
|
||||
* @param {string} packageRoot - 包根目录
|
||||
* @returns {Promise<RegistryData>} 生成的注册表数据
|
||||
*/
|
||||
_buildRegistryFromScanResults(scanResults) {
|
||||
const registry = new Map()
|
||||
|
||||
for (const resource of scanResults) {
|
||||
if (resource.id && resource.reference) {
|
||||
registry.set(resource.id, resource.reference)
|
||||
async generateRegistry(packageRoot) {
|
||||
logger.info(`[PackageDiscovery] 🏗️ 开始生成包级资源注册表...`)
|
||||
|
||||
const registryData = RegistryData.createEmpty('package', this.registryPath)
|
||||
|
||||
try {
|
||||
// 扫描包级资源目录
|
||||
const promptDir = path.join(packageRoot, 'prompt')
|
||||
|
||||
if (await fs.pathExists(promptDir)) {
|
||||
await this._scanDirectory(promptDir, registryData)
|
||||
}
|
||||
|
||||
// 保存注册表
|
||||
await registryData.save()
|
||||
|
||||
logger.info(`[PackageDiscovery] ✅ 包级注册表生成完成,共 ${registryData.size} 个资源`)
|
||||
return registryData
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`[PackageDiscovery] ❌ 注册表生成失败: ${error.message}`)
|
||||
throw error
|
||||
}
|
||||
|
||||
return registry
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描目录并添加资源到注册表
|
||||
* @param {string} promptDir - prompt目录路径
|
||||
* @param {RegistryData} registryData - 注册表数据
|
||||
* @private
|
||||
*/
|
||||
async _scanDirectory(promptDir, registryData) {
|
||||
try {
|
||||
// 扫描domain目录下的角色
|
||||
const domainDir = path.join(promptDir, 'domain')
|
||||
if (await fs.pathExists(domainDir)) {
|
||||
await this._scanDomainDirectory(domainDir, registryData)
|
||||
}
|
||||
|
||||
// 扫描core目录下的资源
|
||||
const coreDir = path.join(promptDir, 'core')
|
||||
if (await fs.pathExists(coreDir)) {
|
||||
await this._scanCoreDirectory(coreDir, registryData)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.warn(`[PackageDiscovery] 扫描目录失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描domain目录(角色资源)
|
||||
* @param {string} domainDir - domain目录路径
|
||||
* @param {RegistryData} registryData - 注册表数据
|
||||
* @private
|
||||
*/
|
||||
async _scanDomainDirectory(domainDir, registryData) {
|
||||
const items = await fs.readdir(domainDir)
|
||||
|
||||
for (const item of items) {
|
||||
const itemPath = path.join(domainDir, item)
|
||||
const stat = await fs.stat(itemPath)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
// 查找角色文件
|
||||
const roleFile = path.join(itemPath, `${item}.role.md`)
|
||||
if (await fs.pathExists(roleFile)) {
|
||||
const reference = `@package://prompt/domain/${item}/${item}.role.md`
|
||||
|
||||
const resourceData = new ResourceData({
|
||||
id: item,
|
||||
source: 'package',
|
||||
protocol: 'role',
|
||||
name: ResourceData._generateDefaultName(item, 'role'),
|
||||
description: ResourceData._generateDefaultDescription(item, 'role'),
|
||||
reference: reference,
|
||||
metadata: {
|
||||
filePath: roleFile,
|
||||
scannedAt: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
|
||||
registryData.addResource(resourceData)
|
||||
}
|
||||
|
||||
// 查找thought文件
|
||||
const thoughtDir = path.join(itemPath, 'thought')
|
||||
if (await fs.pathExists(thoughtDir)) {
|
||||
const thoughtFile = path.join(thoughtDir, `${item}.thought.md`)
|
||||
if (await fs.pathExists(thoughtFile)) {
|
||||
const reference = `@package://prompt/domain/${item}/thought/${item}.thought.md`
|
||||
|
||||
const resourceData = new ResourceData({
|
||||
id: item,
|
||||
source: 'package',
|
||||
protocol: 'thought',
|
||||
name: ResourceData._generateDefaultName(item, 'thought'),
|
||||
description: ResourceData._generateDefaultDescription(item, 'thought'),
|
||||
reference: reference,
|
||||
metadata: {
|
||||
filePath: thoughtFile,
|
||||
scannedAt: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
|
||||
registryData.addResource(resourceData)
|
||||
}
|
||||
}
|
||||
|
||||
// 查找execution文件
|
||||
const executionDir = path.join(itemPath, 'execution')
|
||||
if (await fs.pathExists(executionDir)) {
|
||||
const executionFiles = await fs.readdir(executionDir)
|
||||
for (const execFile of executionFiles) {
|
||||
if (execFile.endsWith('.execution.md')) {
|
||||
const execId = path.basename(execFile, '.execution.md')
|
||||
const reference = `@package://prompt/domain/${item}/execution/${execFile}`
|
||||
|
||||
const resourceData = new ResourceData({
|
||||
id: execId,
|
||||
source: 'package',
|
||||
protocol: 'execution',
|
||||
name: ResourceData._generateDefaultName(execId, 'execution'),
|
||||
description: ResourceData._generateDefaultDescription(execId, 'execution'),
|
||||
reference: reference,
|
||||
metadata: {
|
||||
filePath: path.join(executionDir, execFile),
|
||||
scannedAt: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
|
||||
registryData.addResource(resourceData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描core目录(核心资源)
|
||||
* @param {string} coreDir - core目录路径
|
||||
* @param {RegistryData} registryData - 注册表数据
|
||||
* @private
|
||||
*/
|
||||
async _scanCoreDirectory(coreDir, registryData) {
|
||||
// 扫描core下的直接子目录
|
||||
const items = await fs.readdir(coreDir)
|
||||
|
||||
for (const item of items) {
|
||||
const itemPath = path.join(coreDir, item)
|
||||
const stat = await fs.stat(itemPath)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
// 扫描协议目录(如 thought, execution, knowledge 等)
|
||||
const protocolFiles = await fs.readdir(itemPath)
|
||||
|
||||
for (const file of protocolFiles) {
|
||||
if (file.endsWith('.md')) {
|
||||
const match = file.match(/^(.+)\.(\w+)\.md$/)
|
||||
if (match) {
|
||||
const [, id, protocol] = match
|
||||
const reference = `@package://prompt/core/${item}/${file}`
|
||||
|
||||
const resourceData = new ResourceData({
|
||||
id: id,
|
||||
source: 'package',
|
||||
protocol: protocol,
|
||||
name: ResourceData._generateDefaultName(id, protocol),
|
||||
description: ResourceData._generateDefaultDescription(id, protocol),
|
||||
reference: reference,
|
||||
metadata: {
|
||||
filePath: path.join(itemPath, file),
|
||||
scannedAt: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
|
||||
registryData.addResource(resourceData)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (item.endsWith('.md')) {
|
||||
// 处理core目录下的直接文件
|
||||
const match = item.match(/^(.+)\.(\w+)\.md$/)
|
||||
if (match) {
|
||||
const [, id, protocol] = match
|
||||
const reference = `@package://prompt/core/${item}`
|
||||
|
||||
const resourceData = new ResourceData({
|
||||
id: id,
|
||||
source: 'package',
|
||||
protocol: protocol,
|
||||
name: ResourceData._generateDefaultName(id, protocol),
|
||||
description: ResourceData._generateDefaultDescription(id, protocol),
|
||||
reference: reference,
|
||||
metadata: {
|
||||
filePath: path.join(coreDir, item),
|
||||
scannedAt: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
|
||||
registryData.addResource(resourceData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载包级硬编码注册表 (性能优化核心方法)
|
||||
* @returns {Promise<Map>} 包级资源注册表
|
||||
*/
|
||||
async _loadPackageRegistry() {
|
||||
const cacheKey = 'packageRegistry'
|
||||
if (this.getFromCache(cacheKey)) {
|
||||
return this.getFromCache(cacheKey)
|
||||
}
|
||||
|
||||
try {
|
||||
// 查找package.registry.json文件位置
|
||||
const packageRoot = await this._findPackageRoot()
|
||||
const registryPath = path.join(packageRoot, 'src', 'package.registry.json')
|
||||
|
||||
// 使用RegistryData统一管理
|
||||
const registryData = await RegistryData.fromFile('package', registryPath)
|
||||
const registry = registryData.getResourceMap(true) // 包含源前缀
|
||||
|
||||
logger.debug(`[PackageDiscovery] 🔧 注册表路径: ${registryPath}`)
|
||||
logger.debug(`[PackageDiscovery] 📊 加载资源总数: ${registry.size}`)
|
||||
|
||||
// 缓存结果
|
||||
this.setCache(cacheKey, registry)
|
||||
|
||||
return registry
|
||||
|
||||
} catch (error) {
|
||||
logger.warn(`[PackageDiscovery] Failed to load package registry: ${error.message}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 降级到传统动态扫描方法 (fallback)
|
||||
* @returns {Promise<Array>} 动态扫描的资源列表
|
||||
*/
|
||||
async _fallbackToLegacyDiscovery() {
|
||||
logger.warn('[PackageDiscovery] Falling back to legacy dynamic scanning...')
|
||||
try {
|
||||
const scanResources = await this._scanPromptDirectory()
|
||||
return scanResources.map(resource => this.normalizeResource(resource))
|
||||
} catch (error) {
|
||||
logger.warn(`[PackageDiscovery] Legacy discovery also failed: ${error.message}`)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描prompt目录发现资源
|
||||
@ -114,7 +423,7 @@ class PackageDiscovery extends BaseDiscovery {
|
||||
|
||||
return resources
|
||||
} catch (error) {
|
||||
console.warn(`[PackageDiscovery] Failed to scan prompt directory: ${error.message}`)
|
||||
logger.warn(`[PackageDiscovery] Failed to scan prompt directory: ${error.message}`)
|
||||
return []
|
||||
}
|
||||
}
|
||||
@ -314,7 +623,6 @@ class PackageDiscovery extends BaseDiscovery {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成包引用路径
|
||||
* @param {string} filePath - 文件绝对路径
|
||||
@ -337,6 +645,35 @@ class PackageDiscovery extends BaseDiscovery {
|
||||
const fileName = path.basename(filePath, suffix)
|
||||
return `${protocol}:${fileName}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取RegistryData对象(新架构方法)
|
||||
* @returns {Promise<RegistryData>} 包级RegistryData对象
|
||||
*/
|
||||
async getRegistryData() {
|
||||
try {
|
||||
// 查找package.registry.json文件位置
|
||||
const packageRoot = await this._findPackageRoot()
|
||||
const registryPath = path.join(packageRoot, 'src', 'package.registry.json')
|
||||
|
||||
// 直接加载RegistryData
|
||||
const registryData = await RegistryData.fromFile('package', registryPath)
|
||||
|
||||
logger.info(`[PackageDiscovery] ✅ 硬编码注册表加载成功,发现 ${registryData.size} 个资源`)
|
||||
|
||||
// 输出角色资源信息(调试用)
|
||||
const roleResources = registryData.getResourcesByProtocol('role')
|
||||
const roleIds = roleResources.map(r => r.getFullId()).concat(roleResources.map(r => r.getBaseId()))
|
||||
logger.info(`[PackageDiscovery] 📋 包级角色资源: ${roleIds.join(', ')}`)
|
||||
|
||||
return registryData
|
||||
|
||||
} catch (error) {
|
||||
logger.warn(`[PackageDiscovery] Failed to load RegistryData: ${error.message}`)
|
||||
// 返回空的RegistryData
|
||||
return new RegistryData('package', null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PackageDiscovery
|
||||
@ -1,14 +1,18 @@
|
||||
const BaseDiscovery = require('./BaseDiscovery')
|
||||
const logger = require('../../../utils/logger')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const CrossPlatformFileScanner = require('./CrossPlatformFileScanner')
|
||||
const RegistryData = require('../RegistryData')
|
||||
const ResourceData = require('../ResourceData')
|
||||
|
||||
/**
|
||||
* ProjectDiscovery - 项目级资源发现器
|
||||
*
|
||||
* 负责发现项目本地的资源:
|
||||
* 1. 扫描 .promptx/resource/ 目录
|
||||
* 2. 发现用户自定义的角色、执行模式、思维模式等
|
||||
* 1. 优先从 project.registry.json 读取(构建时优化)
|
||||
* 2. Fallback: 扫描 .promptx/resource/ 目录(动态发现)
|
||||
* 3. 发现用户自定义的角色、执行模式、思维模式等
|
||||
*
|
||||
* 优先级:2
|
||||
*/
|
||||
@ -16,33 +20,7 @@ class ProjectDiscovery extends BaseDiscovery {
|
||||
constructor() {
|
||||
super('PROJECT', 2)
|
||||
this.fileScanner = new CrossPlatformFileScanner()
|
||||
}
|
||||
|
||||
/**
|
||||
* 发现项目级资源
|
||||
* @returns {Promise<Array>} 发现的资源列表
|
||||
*/
|
||||
async discover() {
|
||||
try {
|
||||
// 1. 查找项目根目录
|
||||
const projectRoot = await this._findProjectRoot()
|
||||
|
||||
// 2. 检查.promptx目录是否存在
|
||||
const hasPrompxDir = await this._checkPrompxDirectory(projectRoot)
|
||||
if (!hasPrompxDir) {
|
||||
return []
|
||||
}
|
||||
|
||||
// 3. 扫描项目资源
|
||||
const resources = await this._scanProjectResources(projectRoot)
|
||||
|
||||
// 4. 规范化所有资源
|
||||
return resources.map(resource => this.normalizeResource(resource))
|
||||
|
||||
} catch (error) {
|
||||
console.warn(`[ProjectDiscovery] Discovery failed: ${error.message}`)
|
||||
return []
|
||||
}
|
||||
this.registryData = null
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,18 +38,77 @@ class ProjectDiscovery extends BaseDiscovery {
|
||||
return new Map()
|
||||
}
|
||||
|
||||
// 3. 扫描项目资源
|
||||
const resources = await this._scanProjectResources(projectRoot)
|
||||
// 3. 优先尝试从注册表加载
|
||||
const registryMap = await this._loadFromRegistry(projectRoot)
|
||||
if (registryMap.size > 0) {
|
||||
logger.debug(`ProjectDiscovery 从注册表加载 ${registryMap.size} 个资源`)
|
||||
return registryMap
|
||||
}
|
||||
|
||||
// 4. 构建注册表
|
||||
// 4. Fallback: 动态扫描
|
||||
logger.debug('ProjectDiscovery 注册表不存在,使用动态扫描')
|
||||
const resources = await this._scanProjectResources(projectRoot)
|
||||
return this._buildRegistryFromResources(resources)
|
||||
|
||||
} catch (error) {
|
||||
console.warn(`[ProjectDiscovery] Registry discovery failed: ${error.message}`)
|
||||
logger.warn(`[ProjectDiscovery] Registry discovery failed: ${error.message}`)
|
||||
return new Map()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从注册表文件加载资源
|
||||
* @param {string} projectRoot - 项目根目录
|
||||
* @returns {Promise<Map>} 资源注册表
|
||||
*/
|
||||
async _loadFromRegistry(projectRoot) {
|
||||
try {
|
||||
const registryPath = path.join(projectRoot, '.promptx', 'resource', 'project.registry.json')
|
||||
|
||||
// 检查注册表文件是否存在
|
||||
if (!await this._fsExists(registryPath)) {
|
||||
return new Map()
|
||||
}
|
||||
|
||||
// 读取并解析注册表
|
||||
this.registryData = await RegistryData.fromFile('project', registryPath)
|
||||
|
||||
// 获取分层级资源映射
|
||||
return this.registryData.getResourceMap(true) // 带前缀
|
||||
|
||||
} catch (error) {
|
||||
logger.warn(`[ProjectDiscovery] Failed to load registry: ${error.message}`)
|
||||
return new Map()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发现项目级资源 (旧版本兼容方法)
|
||||
* @returns {Promise<Array>} 发现的资源列表
|
||||
*/
|
||||
async discover() {
|
||||
try {
|
||||
// 使用新的注册表方法
|
||||
const registryMap = await this.discoverRegistry()
|
||||
|
||||
// 转换为旧格式
|
||||
const resources = []
|
||||
for (const [id, reference] of registryMap.entries()) {
|
||||
resources.push({
|
||||
id: id.replace(/^project:/, ''), // 移除前缀以保持兼容性
|
||||
reference: reference
|
||||
})
|
||||
}
|
||||
|
||||
// 规范化所有资源
|
||||
return resources.map(resource => this.normalizeResource(resource))
|
||||
|
||||
} catch (error) {
|
||||
logger.warn(`[ProjectDiscovery] Discovery failed: ${error.message}`)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从资源列表构建注册表
|
||||
* @param {Array} resources - 资源列表
|
||||
@ -165,13 +202,13 @@ class ProjectDiscovery extends BaseDiscovery {
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[ProjectDiscovery] Failed to scan ${resourceType} resources: ${error.message}`)
|
||||
logger.warn(`[ProjectDiscovery] Failed to scan ${resourceType} resources: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
return resources
|
||||
} catch (error) {
|
||||
console.warn(`[ProjectDiscovery] Failed to scan project resources: ${error.message}`)
|
||||
logger.warn(`[ProjectDiscovery] Failed to scan project resources: ${error.message}`)
|
||||
return []
|
||||
}
|
||||
}
|
||||
@ -239,7 +276,7 @@ class ProjectDiscovery extends BaseDiscovery {
|
||||
return false
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[ProjectDiscovery] Failed to validate ${filePath}: ${error.message}`)
|
||||
logger.warn(`[ProjectDiscovery] Failed to validate ${filePath}: ${error.message}`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -260,11 +297,210 @@ class ProjectDiscovery extends BaseDiscovery {
|
||||
* @param {string} filePath - 文件路径
|
||||
* @param {string} protocol - 协议类型
|
||||
* @param {string} suffix - 文件后缀
|
||||
* @returns {string} 资源ID (protocol:resourceName)
|
||||
* @returns {string} 资源ID (对于role类型返回resourceName,对于其他类型返回protocol:resourceName)
|
||||
*/
|
||||
_extractResourceId(filePath, protocol, suffix) {
|
||||
const fileName = path.basename(filePath, suffix)
|
||||
return `${protocol}:${fileName}`
|
||||
|
||||
// role类型不需要前缀,其他类型需要前缀
|
||||
if (protocol === 'role') {
|
||||
return fileName
|
||||
} else {
|
||||
return `${protocol}:${fileName}`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成项目级注册表文件
|
||||
* @param {string} projectRoot - 项目根目录
|
||||
* @returns {Promise<RegistryData>} 生成的注册表数据
|
||||
*/
|
||||
async generateRegistry(projectRoot) {
|
||||
const registryPath = path.join(projectRoot, '.promptx', 'resource', 'project.registry.json')
|
||||
const registryData = RegistryData.createEmpty('project', registryPath)
|
||||
|
||||
// 扫描.promptx/resource目录
|
||||
const resourcesDir = path.join(projectRoot, '.promptx', 'resource')
|
||||
|
||||
if (await this._fsExists(resourcesDir)) {
|
||||
await this._scanDirectory(resourcesDir, registryData)
|
||||
}
|
||||
|
||||
// 保存注册表文件
|
||||
await registryData.save()
|
||||
|
||||
logger.info(`[ProjectDiscovery] ✅ 项目注册表生成完成,发现 ${registryData.size} 个资源`)
|
||||
return registryData
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描目录并添加资源到注册表
|
||||
* @param {string} resourcesDir - 资源目录
|
||||
* @param {RegistryData} registryData - 注册表数据
|
||||
* @private
|
||||
*/
|
||||
async _scanDirectory(resourcesDir, registryData) {
|
||||
// 扫描domain目录
|
||||
const domainDir = path.join(resourcesDir, 'domain')
|
||||
if (await this._fsExists(domainDir)) {
|
||||
await this._scanDomainDirectory(domainDir, registryData)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描domain目录(项目角色资源)
|
||||
* @param {string} domainDir - domain目录路径
|
||||
* @param {RegistryData} registryData - 注册表数据
|
||||
* @private
|
||||
*/
|
||||
async _scanDomainDirectory(domainDir, registryData) {
|
||||
const items = await fs.readdir(domainDir)
|
||||
|
||||
for (const item of items) {
|
||||
const itemPath = path.join(domainDir, item)
|
||||
const stat = await fs.stat(itemPath)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
// 查找role文件
|
||||
const roleFile = path.join(itemPath, `${item}.role.md`)
|
||||
if (await this._fsExists(roleFile)) {
|
||||
const reference = `@project://.promptx/resource/domain/${item}/${item}.role.md`
|
||||
|
||||
const resourceData = new ResourceData({
|
||||
id: item,
|
||||
source: 'project',
|
||||
protocol: 'role',
|
||||
name: ResourceData._generateDefaultName(item, 'role'),
|
||||
description: ResourceData._generateDefaultDescription(item, 'role'),
|
||||
reference: reference,
|
||||
metadata: {
|
||||
filePath: roleFile,
|
||||
scannedAt: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
|
||||
registryData.addResource(resourceData)
|
||||
}
|
||||
|
||||
// 查找thought文件
|
||||
const thoughtDir = path.join(itemPath, 'thought')
|
||||
if (await this._fsExists(thoughtDir)) {
|
||||
const thoughtFiles = await fs.readdir(thoughtDir)
|
||||
for (const thoughtFile of thoughtFiles) {
|
||||
if (thoughtFile.endsWith('.thought.md')) {
|
||||
const thoughtId = path.basename(thoughtFile, '.thought.md')
|
||||
const reference = `@project://.promptx/resource/domain/${item}/thought/${thoughtFile}`
|
||||
|
||||
const resourceData = new ResourceData({
|
||||
id: thoughtId,
|
||||
source: 'project',
|
||||
protocol: 'thought',
|
||||
name: ResourceData._generateDefaultName(thoughtId, 'thought'),
|
||||
description: ResourceData._generateDefaultDescription(thoughtId, 'thought'),
|
||||
reference: reference,
|
||||
metadata: {
|
||||
filePath: path.join(thoughtDir, thoughtFile),
|
||||
scannedAt: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
|
||||
registryData.addResource(resourceData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查找execution文件
|
||||
const executionDir = path.join(itemPath, 'execution')
|
||||
if (await this._fsExists(executionDir)) {
|
||||
const executionFiles = await fs.readdir(executionDir)
|
||||
for (const execFile of executionFiles) {
|
||||
if (execFile.endsWith('.execution.md')) {
|
||||
const execId = path.basename(execFile, '.execution.md')
|
||||
const reference = `@project://.promptx/resource/domain/${item}/execution/${execFile}`
|
||||
|
||||
const resourceData = new ResourceData({
|
||||
id: execId,
|
||||
source: 'project',
|
||||
protocol: 'execution',
|
||||
name: ResourceData._generateDefaultName(execId, 'execution'),
|
||||
description: ResourceData._generateDefaultDescription(execId, 'execution'),
|
||||
reference: reference,
|
||||
metadata: {
|
||||
filePath: path.join(executionDir, execFile),
|
||||
scannedAt: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
|
||||
registryData.addResource(resourceData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查找knowledge文件
|
||||
const knowledgeDir = path.join(itemPath, 'knowledge')
|
||||
if (await this._fsExists(knowledgeDir)) {
|
||||
const knowledgeFiles = await fs.readdir(knowledgeDir)
|
||||
for (const knowledgeFile of knowledgeFiles) {
|
||||
if (knowledgeFile.endsWith('.knowledge.md')) {
|
||||
const knowledgeId = path.basename(knowledgeFile, '.knowledge.md')
|
||||
const reference = `@project://.promptx/resource/domain/${item}/knowledge/${knowledgeFile}`
|
||||
|
||||
const resourceData = new ResourceData({
|
||||
id: knowledgeId,
|
||||
source: 'project',
|
||||
protocol: 'knowledge',
|
||||
name: ResourceData._generateDefaultName(knowledgeId, 'knowledge'),
|
||||
description: ResourceData._generateDefaultDescription(knowledgeId, 'knowledge'),
|
||||
reference: reference,
|
||||
metadata: {
|
||||
filePath: path.join(knowledgeDir, knowledgeFile),
|
||||
scannedAt: new Date().toISOString()
|
||||
}
|
||||
})
|
||||
|
||||
registryData.addResource(resourceData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取RegistryData对象(新架构方法)
|
||||
* @returns {Promise<RegistryData>} 项目级RegistryData对象
|
||||
*/
|
||||
async getRegistryData() {
|
||||
try {
|
||||
const projectRoot = await this._findProjectRoot()
|
||||
const registryPath = path.join(projectRoot, '.promptx', 'resource', 'project.registry.json')
|
||||
|
||||
// 尝试加载现有的注册表文件
|
||||
if (await this._fsExists(registryPath)) {
|
||||
const registryData = await RegistryData.fromFile('project', registryPath)
|
||||
|
||||
// 检查注册表是否有效(有完整的资源数据)
|
||||
if (registryData.size > 0 && registryData.resources.length > 0) {
|
||||
const firstResource = registryData.resources[0]
|
||||
if (firstResource.id && firstResource.protocol && firstResource.reference) {
|
||||
logger.info(`[ProjectDiscovery] 📋 从注册表加载 ${registryData.size} 个资源`)
|
||||
return registryData
|
||||
}
|
||||
}
|
||||
|
||||
// 如果注册表无效,重新生成
|
||||
logger.info(`[ProjectDiscovery] 📋 项目注册表无效,重新生成`)
|
||||
return await this.generateRegistry(projectRoot)
|
||||
} else {
|
||||
// 如果没有注册表文件,生成新的
|
||||
logger.info(`[ProjectDiscovery] 📋 项目注册表不存在,生成新注册表`)
|
||||
return await this.generateRegistry(projectRoot)
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn(`[ProjectDiscovery] Failed to load RegistryData: ${error.message}`)
|
||||
// 返回空的RegistryData
|
||||
return RegistryData.createEmpty('project', null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -25,11 +25,37 @@ const {
|
||||
ProtocolInfo
|
||||
} = require('./types')
|
||||
|
||||
// 全局单例 ResourceManager 实例
|
||||
let globalResourceManager = null
|
||||
|
||||
/**
|
||||
* 获取全局单例 ResourceManager 实例
|
||||
* 确保整个应用程序使用同一个 ResourceManager 实例
|
||||
*/
|
||||
function getGlobalResourceManager() {
|
||||
if (!globalResourceManager) {
|
||||
globalResourceManager = new ResourceManager()
|
||||
}
|
||||
return globalResourceManager
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置全局 ResourceManager 实例
|
||||
* 主要用于测试或需要完全重新初始化的场景
|
||||
*/
|
||||
function resetGlobalResourceManager() {
|
||||
globalResourceManager = null
|
||||
}
|
||||
|
||||
// 导出主接口
|
||||
module.exports = {
|
||||
// 主管理器
|
||||
// 主管理器类
|
||||
ResourceManager,
|
||||
|
||||
// 全局单例实例
|
||||
getGlobalResourceManager,
|
||||
resetGlobalResourceManager,
|
||||
|
||||
// 核心组件
|
||||
ResourceProtocolParser,
|
||||
ResourceRegistry,
|
||||
@ -45,7 +71,7 @@ module.exports = {
|
||||
ResourceResult,
|
||||
ProtocolInfo,
|
||||
|
||||
// 便捷方法 - 创建默认实例
|
||||
// 便捷方法 - 创建默认实例(保持向后兼容)
|
||||
createManager: (options) => new ResourceManager(options),
|
||||
|
||||
// 便捷方法 - 快速解析
|
||||
|
||||
@ -3,6 +3,7 @@ const fs = require('fs')
|
||||
const fsPromises = require('fs').promises
|
||||
const ResourceProtocol = require('./ResourceProtocol')
|
||||
const { QueryParams } = require('../types')
|
||||
const logger = require('../../../utils/logger')
|
||||
|
||||
/**
|
||||
* 包协议实现
|
||||
@ -477,7 +478,7 @@ class PackageProtocol extends ResourceProtocol {
|
||||
// 在生产环境严格检查,开发环境只警告
|
||||
const installMode = this.detectInstallMode()
|
||||
if (installMode === 'development' || installMode === 'npx') {
|
||||
console.warn(`⚠️ Warning: Path '${relativePath}' not in package.json files field. This may cause issues after publishing.`)
|
||||
logger.warn(`⚠️ Warning: Path '${relativePath}' not in package.json files field. This may cause issues after publishing.`)
|
||||
} else {
|
||||
throw new Error(`Access denied: Path '${relativePath}' is not included in package.json files field`)
|
||||
}
|
||||
@ -486,7 +487,7 @@ class PackageProtocol extends ResourceProtocol {
|
||||
// 如果读取package.json失败,在开发模式下允许访问
|
||||
const installMode = this.detectInstallMode()
|
||||
if (installMode === 'development' || installMode === 'npx') {
|
||||
console.warn(`⚠️ Warning: Could not validate file access for '${relativePath}': ${error.message}`)
|
||||
logger.warn(`⚠️ Warning: Could not validate file access for '${relativePath}': ${error.message}`)
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
const fs = require('fs')
|
||||
const ResourceRegistry = require('./resourceRegistry')
|
||||
const RegistryData = require('./RegistryData')
|
||||
const ResourceProtocolParser = require('./resourceProtocolParser')
|
||||
const DiscoveryManager = require('./discovery/DiscoveryManager')
|
||||
const logger = require('../../utils/logger')
|
||||
|
||||
// 导入协议处理器
|
||||
const PackageProtocol = require('./protocols/PackageProtocol')
|
||||
@ -13,7 +15,9 @@ const KnowledgeProtocol = require('./protocols/KnowledgeProtocol')
|
||||
|
||||
class ResourceManager {
|
||||
constructor() {
|
||||
this.registry = new ResourceRegistry()
|
||||
// 使用新的RegistryData替代旧的ResourceRegistry
|
||||
this.registry = new ResourceRegistry() // 保持向后兼容
|
||||
this.registryData = RegistryData.createEmpty('merged', null) // 新的v2.0注册表
|
||||
this.protocolParser = new ResourceProtocolParser()
|
||||
this.parser = new ResourceProtocolParser() // 向后兼容别名
|
||||
this.discoveryManager = new DiscoveryManager() // 新发现管理器
|
||||
@ -43,23 +47,58 @@ class ResourceManager {
|
||||
*/
|
||||
async initializeWithNewArchitecture() {
|
||||
try {
|
||||
// 1. 直接发现并注册资源(无需中间合并步骤)
|
||||
// 1. 清空现有注册表(支持重新初始化)
|
||||
this.registry.clear()
|
||||
this.registryData.clear()
|
||||
|
||||
// 2. 清除发现器缓存
|
||||
if (this.discoveryManager && typeof this.discoveryManager.clearCache === 'function') {
|
||||
this.discoveryManager.clearCache()
|
||||
}
|
||||
|
||||
// 3. 直接发现并注册资源到旧的ResourceRegistry(保持向后兼容)
|
||||
await this.discoveryManager.discoverAndDirectRegister(this.registry)
|
||||
|
||||
// 2. 为逻辑协议设置注册表引用
|
||||
// 4. 同时填充新的RegistryData
|
||||
await this.populateRegistryData()
|
||||
|
||||
// 5. 为逻辑协议设置注册表引用
|
||||
this.setupLogicalProtocols()
|
||||
|
||||
// 3. 设置初始化状态
|
||||
// 6. 设置初始化状态
|
||||
this.initialized = true
|
||||
|
||||
// 初始化完成,不输出日志避免干扰用户界面
|
||||
} catch (error) {
|
||||
console.warn(`[ResourceManager] New architecture initialization failed: ${error.message}`)
|
||||
console.warn('[ResourceManager] Continuing with empty registry')
|
||||
logger.warn(`ResourceManager new architecture initialization failed: ${error.message}`)
|
||||
logger.warn('ResourceManager continuing with empty registry')
|
||||
this.initialized = true // 即使失败也标记为已初始化,避免重复尝试
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充新的RegistryData
|
||||
*/
|
||||
async populateRegistryData() {
|
||||
// 清空现有数据
|
||||
this.registryData.clear()
|
||||
|
||||
// 从各个发现器获取RegistryData并合并
|
||||
for (const discovery of this.discoveryManager.discoveries) {
|
||||
try {
|
||||
if (typeof discovery.getRegistryData === 'function') {
|
||||
const registryData = await discovery.getRegistryData()
|
||||
if (registryData && registryData.resources) {
|
||||
// 合并资源到主注册表
|
||||
this.registryData.merge(registryData, true) // 允许覆盖
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to get RegistryData from ${discovery.source}: ${error.message}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为逻辑协议设置注册表引用
|
||||
*/
|
||||
@ -116,33 +155,53 @@ class ResourceManager {
|
||||
|
||||
async loadResource(resourceId) {
|
||||
try {
|
||||
// 每次都刷新资源(无状态设计)
|
||||
await this.refreshResources()
|
||||
// 不再每次刷新资源,依赖初始化时的资源发现
|
||||
|
||||
// 处理@!开头的DPML格式(如 @!role://java-developer)
|
||||
if (resourceId.startsWith('@!')) {
|
||||
const parsed = this.protocolParser.parse(resourceId)
|
||||
const logicalResourceId = `${parsed.protocol}:${parsed.path}`
|
||||
|
||||
// 从注册表查找对应的@package://引用
|
||||
const reference = this.registry.get(logicalResourceId)
|
||||
if (!reference) {
|
||||
throw new Error(`Resource not found: ${logicalResourceId}`)
|
||||
// 从新的RegistryData查找资源
|
||||
const resourceData = this.registryData.findResourceById(parsed.path, parsed.protocol)
|
||||
if (!resourceData) {
|
||||
throw new Error(`Resource not found: ${parsed.protocol}:${parsed.path}`)
|
||||
}
|
||||
|
||||
// 通过协议解析加载内容
|
||||
const content = await this.loadResourceByProtocol(reference)
|
||||
const content = await this.loadResourceByProtocol(resourceData.reference)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
content,
|
||||
resourceId,
|
||||
reference
|
||||
reference: resourceData.reference
|
||||
}
|
||||
}
|
||||
|
||||
// 处理传统格式(如 role:java-developer)
|
||||
const reference = this.registry.get(resourceId)
|
||||
// 先尝试从新的RegistryData查找
|
||||
let reference = null
|
||||
|
||||
// 如果包含协议前缀(如 thought:remember)
|
||||
if (resourceId.includes(':')) {
|
||||
const [protocol, id] = resourceId.split(':', 2)
|
||||
const resourceData = this.registryData.findResourceById(id, protocol)
|
||||
if (resourceData) {
|
||||
reference = resourceData.reference
|
||||
}
|
||||
} else {
|
||||
// 如果没有协议前缀,尝试查找任意协议的资源
|
||||
const resourceData = this.registryData.findResourceById(resourceId)
|
||||
if (resourceData) {
|
||||
reference = resourceData.reference
|
||||
}
|
||||
}
|
||||
|
||||
// 如果新的RegistryData中没找到,回退到旧的registry
|
||||
if (!reference) {
|
||||
reference = this.registry.get(resourceId)
|
||||
}
|
||||
|
||||
if (!reference) {
|
||||
throw new Error(`Resource not found: ${resourceId}`)
|
||||
}
|
||||
@ -216,8 +275,7 @@ class ResourceManager {
|
||||
// 向后兼容方法
|
||||
async resolve(resourceUrl) {
|
||||
try {
|
||||
// 每次都刷新资源(无状态设计)
|
||||
await this.refreshResources()
|
||||
// 不再每次刷新资源,依赖初始化时的资源发现
|
||||
|
||||
// Handle old format: role:java-backend-developer or @package://...
|
||||
if (resourceUrl.startsWith('@')) {
|
||||
@ -275,7 +333,7 @@ class ResourceManager {
|
||||
|
||||
// 无状态设计:不设置initialized标志
|
||||
} catch (error) {
|
||||
console.warn(`[ResourceManager] Resource refresh failed: ${error.message}`)
|
||||
logger.warn(`ResourceManager resource refresh failed: ${error.message}`)
|
||||
// 失败时保持注册表为空状态,下次调用时重试
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
const logger = require('../../utils/logger')
|
||||
|
||||
/**
|
||||
* 资源注册表
|
||||
* 新架构中用于存储动态发现的资源映射关系
|
||||
@ -70,27 +72,27 @@ class ResourceRegistry {
|
||||
* @param {string} title - 可选标题
|
||||
*/
|
||||
printAll(title = '注册表资源清单') {
|
||||
console.log(`\n📋 ${title}`)
|
||||
console.log('='.repeat(50))
|
||||
logger.info(`\n📋 ${title}`)
|
||||
logger.info('='.repeat(50))
|
||||
|
||||
if (this.size === 0) {
|
||||
console.log('🔍 注册表为空')
|
||||
logger.info('🔍 注册表为空')
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`📊 总计: ${this.size} 个资源\n`)
|
||||
logger.info(`📊 总计: ${this.size} 个资源\n`)
|
||||
|
||||
// 按协议分组显示
|
||||
const groupedResources = this.groupByProtocol()
|
||||
|
||||
for (const [protocol, resources] of Object.entries(groupedResources)) {
|
||||
console.log(`🔖 ${protocol.toUpperCase()} 协议 (${resources.length}个):`)
|
||||
logger.info(`🔖 ${protocol.toUpperCase()} 协议 (${resources.length}个):`)
|
||||
resources.forEach(({ id, reference }) => {
|
||||
const resourceName = id.split(':')[1] || id
|
||||
console.log(` • ${resourceName}`)
|
||||
console.log(` └─ ${reference}`)
|
||||
logger.info(` • ${resourceName}`)
|
||||
logger.info(` └─ ${reference}`)
|
||||
})
|
||||
console.log('')
|
||||
logger.info('')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const logger = require('./logger');
|
||||
|
||||
/**
|
||||
* 执行上下文检测工具
|
||||
@ -37,7 +38,7 @@ function getMCPWorkingDirectory() {
|
||||
// 取第一个工作区路径(多工作区情况)
|
||||
const firstPath = workspacePaths.split(path.delimiter)[0];
|
||||
if (firstPath && isValidDirectory(firstPath)) {
|
||||
console.error(`[执行上下文] 使用WORKSPACE_FOLDER_PATHS: ${firstPath}`);
|
||||
logger.info(`[执行上下文] 使用WORKSPACE_FOLDER_PATHS: ${firstPath}`);
|
||||
return firstPath;
|
||||
}
|
||||
}
|
||||
@ -45,27 +46,27 @@ function getMCPWorkingDirectory() {
|
||||
// 策略2:PROMPTX_WORKSPACE(PromptX专用环境变量)
|
||||
const promptxWorkspace = process.env.PROMPTX_WORKSPACE;
|
||||
if (promptxWorkspace && isValidDirectory(promptxWorkspace)) {
|
||||
console.error(`[执行上下文] 使用PROMPTX_WORKSPACE: ${promptxWorkspace}`);
|
||||
logger.info(`[执行上下文] 使用PROMPTX_WORKSPACE: ${promptxWorkspace}`);
|
||||
return promptxWorkspace;
|
||||
}
|
||||
|
||||
// 策略3:PWD环境变量(某些情况下可用)
|
||||
const pwd = process.env.PWD;
|
||||
if (pwd && isValidDirectory(pwd) && pwd !== process.cwd()) {
|
||||
console.error(`[执行上下文] 使用PWD环境变量: ${pwd}`);
|
||||
logger.info(`[执行上下文] 使用PWD环境变量: ${pwd}`);
|
||||
return pwd;
|
||||
}
|
||||
|
||||
// 策略4:项目根目录智能推测(向上查找项目标识)
|
||||
const projectRoot = findProjectRoot(process.cwd());
|
||||
if (projectRoot && projectRoot !== process.cwd()) {
|
||||
console.error(`[执行上下文] 智能推测项目根目录: ${projectRoot}`);
|
||||
logger.info(`[执行上下文] 智能推测项目根目录: ${projectRoot}`);
|
||||
return projectRoot;
|
||||
}
|
||||
|
||||
// 策略5:回退到process.cwd()
|
||||
console.error(`[执行上下文] 回退到process.cwd(): ${process.cwd()}`);
|
||||
console.error(`[执行上下文] 提示:建议在MCP配置中添加 "env": {"PROMPTX_WORKSPACE": "你的项目目录"}`);
|
||||
logger.warn(`[执行上下文] 回退到process.cwd(): ${process.cwd()}`);
|
||||
logger.warn(`[执行上下文] 提示:建议在MCP配置中添加 "env": {"PROMPTX_WORKSPACE": "你的项目目录"}`)
|
||||
return process.cwd();
|
||||
}
|
||||
|
||||
|
||||
654
src/package.registry.json
Normal file
654
src/package.registry.json
Normal file
@ -0,0 +1,654 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"source": "package",
|
||||
"metadata": {
|
||||
"version": "2.0.0",
|
||||
"description": "package 级资源注册表",
|
||||
"createdAt": "2025-06-13T00:41:50.641Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"resourceCount": 45
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"id": "assistant",
|
||||
"source": "package",
|
||||
"protocol": "role",
|
||||
"name": "Assistant 角色",
|
||||
"description": "专业角色,提供特定领域的专业能力",
|
||||
"reference": "@package://prompt/domain/assistant/assistant.role.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/assistant/assistant.role.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "assistant",
|
||||
"source": "package",
|
||||
"protocol": "thought",
|
||||
"name": "Assistant 思维模式",
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/domain/assistant/thought/assistant.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/assistant/thought/assistant.thought.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "assistant",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Assistant 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/assistant/execution/assistant.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/assistant/execution/assistant.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "frontend-developer",
|
||||
"source": "package",
|
||||
"protocol": "role",
|
||||
"name": "Frontend Developer 角色",
|
||||
"description": "专业角色,提供特定领域的专业能力",
|
||||
"reference": "@package://prompt/domain/frontend-developer/frontend-developer.role.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/frontend-developer/frontend-developer.role.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "frontend-developer",
|
||||
"source": "package",
|
||||
"protocol": "thought",
|
||||
"name": "Frontend Developer 思维模式",
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/domain/frontend-developer/thought/frontend-developer.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/frontend-developer/thought/frontend-developer.thought.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "code-quality",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Code Quality 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/java-backend-developer/execution/code-quality.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/java-backend-developer/execution/code-quality.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "frontend-developer",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Frontend Developer 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/frontend-developer/execution/frontend-developer.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/frontend-developer/execution/frontend-developer.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "technical-architecture",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Technical Architecture 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/frontend-developer/execution/technical-architecture.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/frontend-developer/execution/technical-architecture.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "user-experience",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "User Experience 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/frontend-developer/execution/user-experience.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/frontend-developer/execution/user-experience.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "wechat-miniprogram-development",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Wechat Miniprogram Development 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/frontend-developer/execution/wechat-miniprogram-development.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/frontend-developer/execution/wechat-miniprogram-development.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "java-backend-developer",
|
||||
"source": "package",
|
||||
"protocol": "role",
|
||||
"name": "Java Backend Developer 角色",
|
||||
"description": "专业角色,提供特定领域的专业能力",
|
||||
"reference": "@package://prompt/domain/java-backend-developer/java-backend-developer.role.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/java-backend-developer/java-backend-developer.role.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "java-backend-developer",
|
||||
"source": "package",
|
||||
"protocol": "thought",
|
||||
"name": "Java Backend Developer 思维模式",
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/domain/java-backend-developer/thought/java-backend-developer.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/java-backend-developer/thought/java-backend-developer.thought.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "database-design",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Database Design 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/java-backend-developer/execution/database-design.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/java-backend-developer/execution/database-design.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "java-backend-developer",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Java Backend Developer 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/java-backend-developer/execution/java-backend-developer.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/java-backend-developer/execution/java-backend-developer.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "spring-ecosystem",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Spring Ecosystem 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/java-backend-developer/execution/spring-ecosystem.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/java-backend-developer/execution/spring-ecosystem.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "system-architecture",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "System Architecture 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/java-backend-developer/execution/system-architecture.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/java-backend-developer/execution/system-architecture.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "product-manager",
|
||||
"source": "package",
|
||||
"protocol": "role",
|
||||
"name": "Product Manager 角色",
|
||||
"description": "专业角色,提供特定领域的专业能力",
|
||||
"reference": "@package://prompt/domain/product-manager/product-manager.role.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/product-manager/product-manager.role.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "product-manager",
|
||||
"source": "package",
|
||||
"protocol": "thought",
|
||||
"name": "Product Manager 思维模式",
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/domain/product-manager/thought/product-manager.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/product-manager/thought/product-manager.thought.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "market-analysis",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Market Analysis 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/product-manager/execution/market-analysis.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/product-manager/execution/market-analysis.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "product-manager",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Product Manager 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/product-manager/execution/product-manager.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/product-manager/execution/product-manager.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "user-research",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "User Research 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/product-manager/execution/user-research.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/product-manager/execution/user-research.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "xiaohongshu-marketer",
|
||||
"source": "package",
|
||||
"protocol": "role",
|
||||
"name": "Xiaohongshu Marketer 角色",
|
||||
"description": "专业角色,提供特定领域的专业能力",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/xiaohongshu-marketer.role.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/xiaohongshu-marketer/xiaohongshu-marketer.role.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "xiaohongshu-marketer",
|
||||
"source": "package",
|
||||
"protocol": "thought",
|
||||
"name": "Xiaohongshu Marketer 思维模式",
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/thought/xiaohongshu-marketer.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.643Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.643Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/xiaohongshu-marketer/thought/xiaohongshu-marketer.thought.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.643Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "brand-marketing",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Brand Marketing 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/brand-marketing.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/xiaohongshu-marketer/execution/brand-marketing.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "community-building",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Community Building 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/community-building.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/xiaohongshu-marketer/execution/community-building.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "content-creation",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Content Creation 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/content-creation.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/xiaohongshu-marketer/execution/content-creation.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "content-optimization",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Content Optimization 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/content-optimization.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/xiaohongshu-marketer/execution/content-optimization.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "data-analytics",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Data Analytics 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/data-analytics.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/xiaohongshu-marketer/execution/data-analytics.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ecommerce-conversion",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Ecommerce Conversion 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/ecommerce-conversion.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/xiaohongshu-marketer/execution/ecommerce-conversion.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "performance-optimization",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Performance Optimization 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/performance-optimization.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/xiaohongshu-marketer/execution/performance-optimization.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "platform-compliance",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Platform Compliance 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/platform-compliance.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/xiaohongshu-marketer/execution/platform-compliance.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "team-collaboration",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Team Collaboration 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/team-collaboration.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/xiaohongshu-marketer/execution/team-collaboration.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "user-operation",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "User Operation 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/user-operation.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/xiaohongshu-marketer/execution/user-operation.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "xiaohongshu-marketer",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Xiaohongshu Marketer 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/xiaohongshu-marketer.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/domain/xiaohongshu-marketer/execution/xiaohongshu-marketer.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "dpml-protocol-knowledge",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Dpml Protocol Knowledge 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/core/execution/dpml-protocol-knowledge.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/core/execution/dpml-protocol-knowledge.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "execution-authoring",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Execution Authoring 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/core/execution/execution-authoring.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/core/execution/execution-authoring.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "resource-authoring",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Resource Authoring 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/core/execution/resource-authoring.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/core/execution/resource-authoring.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "role-authoring",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Role Authoring 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/core/execution/role-authoring.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/core/execution/role-authoring.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "role-design-patterns",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Role Design Patterns 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/core/execution/role-design-patterns.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/core/execution/role-design-patterns.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "role-generation",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Role Generation 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/core/execution/role-generation.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/core/execution/role-generation.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "thought-authoring",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Thought Authoring 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/core/execution/thought-authoring.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/core/execution/thought-authoring.execution.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "nuwa",
|
||||
"source": "package",
|
||||
"protocol": "role",
|
||||
"name": "Nuwa 角色",
|
||||
"description": "专业角色,提供特定领域的专业能力",
|
||||
"reference": "@package://prompt/core/nuwa/nuwa.role.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/core/nuwa/nuwa.role.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "recall",
|
||||
"source": "package",
|
||||
"protocol": "thought",
|
||||
"name": "Recall 思维模式",
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/core/thought/recall.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/core/thought/recall.thought.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "remember",
|
||||
"source": "package",
|
||||
"protocol": "thought",
|
||||
"name": "Remember 思维模式",
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/core/thought/remember.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/core/thought/remember.thought.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "role-creation",
|
||||
"source": "package",
|
||||
"protocol": "thought",
|
||||
"name": "Role Creation 思维模式",
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/core/thought/role-creation.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-13T00:41:50.644Z",
|
||||
"updatedAt": "2025-06-13T00:41:50.644Z",
|
||||
"filePath": "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX/prompt/core/thought/role-creation.thought.md",
|
||||
"scannedAt": "2025-06-13T00:41:50.644Z"
|
||||
}
|
||||
}
|
||||
],
|
||||
"stats": {
|
||||
"totalResources": 45,
|
||||
"byProtocol": {
|
||||
"role": 6,
|
||||
"thought": 8,
|
||||
"execution": 31
|
||||
},
|
||||
"bySource": {
|
||||
"package": 45
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user