Merge pull request #5 from Cen-Yaozu/feature/local-role-discovery
feat: 实现本地角色动态发现机制 - 双重角色发现机制:同时支持npm仓库角色和本地项目角色 - 智能环境检测:自动适配开发、npx、…
This commit is contained in:
@ -31,10 +31,22 @@ class HelloCommand extends BasePouchCommand {
|
||||
const resourceManager = new ResourceManager()
|
||||
await resourceManager.initialize() // 确保初始化完成
|
||||
|
||||
let registeredRoles = {}
|
||||
if (resourceManager.registry && resourceManager.registry.protocols && resourceManager.registry.protocols.role && resourceManager.registry.protocols.role.registry) {
|
||||
this.roleRegistry = resourceManager.registry.protocols.role.registry
|
||||
} else {
|
||||
// 备用:如果资源系统不可用,使用基础角色
|
||||
registeredRoles = resourceManager.registry.protocols.role.registry
|
||||
}
|
||||
|
||||
// 动态发现本地角色并合并
|
||||
const discoveredRoles = await this.discoverLocalRoles()
|
||||
|
||||
// 合并注册表中的角色和动态发现的角色
|
||||
this.roleRegistry = {
|
||||
...registeredRoles,
|
||||
...discoveredRoles
|
||||
}
|
||||
|
||||
// 如果没有任何角色,使用基础角色
|
||||
if (Object.keys(this.roleRegistry).length === 0) {
|
||||
this.roleRegistry = {
|
||||
assistant: {
|
||||
file: '@package://prompt/domain/assistant/assistant.role.md',
|
||||
@ -44,12 +56,26 @@ class HelloCommand extends BasePouchCommand {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('角色注册表加载失败,使用基础角色:', error.message)
|
||||
this.roleRegistry = {
|
||||
assistant: {
|
||||
file: '@package://prompt/domain/assistant/assistant.role.md',
|
||||
name: '🙋 智能助手',
|
||||
description: '通用助理角色,提供基础的助理服务和记忆支持'
|
||||
console.warn('角色注册表加载失败,尝试动态发现:', error.message)
|
||||
|
||||
// fallback到动态发现
|
||||
try {
|
||||
const discoveredRoles = await this.discoverLocalRoles()
|
||||
this.roleRegistry = Object.keys(discoveredRoles).length > 0 ? discoveredRoles : {
|
||||
assistant: {
|
||||
file: '@package://prompt/domain/assistant/assistant.role.md',
|
||||
name: '🙋 智能助手',
|
||||
description: '通用助理角色,提供基础的助理服务和记忆支持'
|
||||
}
|
||||
}
|
||||
} catch (discoveryError) {
|
||||
console.warn('动态角色发现也失败了:', discoveryError.message)
|
||||
this.roleRegistry = {
|
||||
assistant: {
|
||||
file: '@package://prompt/domain/assistant/assistant.role.md',
|
||||
name: '🙋 智能助手',
|
||||
description: '通用助理角色,提供基础的助理服务和记忆支持'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -183,6 +209,64 @@ ${buildCommand.action(allRoles[0]?.id || 'assistant')}
|
||||
const allRoles = await this.getAllRoles()
|
||||
return allRoles.map(role => role.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态发现本地角色文件
|
||||
*/
|
||||
async discoverLocalRoles () {
|
||||
const PackageProtocol = require('../../resource/protocols/PackageProtocol')
|
||||
const packageProtocol = new PackageProtocol()
|
||||
const glob = require('glob')
|
||||
const path = require('path')
|
||||
|
||||
try {
|
||||
const packageRoot = await packageProtocol.getPackageRoot()
|
||||
const domainPath = path.join(packageRoot, 'prompt', 'domain')
|
||||
|
||||
// 扫描所有角色目录
|
||||
const rolePattern = path.join(domainPath, '*', '*.role.md')
|
||||
const roleFiles = glob.sync(rolePattern)
|
||||
|
||||
const discoveredRoles = {}
|
||||
|
||||
for (const roleFile of roleFiles) {
|
||||
try {
|
||||
const content = await fs.readFile(roleFile, 'utf-8')
|
||||
const relativePath = path.relative(packageRoot, roleFile)
|
||||
const roleName = path.basename(roleFile, '.role.md')
|
||||
|
||||
// 尝试从文件内容中提取角色信息
|
||||
let description = '本地发现的角色'
|
||||
let name = `🎭 ${roleName}`
|
||||
|
||||
// 简单的元数据提取(支持多行)
|
||||
const descMatch = content.match(/description:\s*(.+?)(?:\n|$)/i)
|
||||
if (descMatch) {
|
||||
description = descMatch[1].trim()
|
||||
}
|
||||
|
||||
const nameMatch = content.match(/name:\s*(.+?)(?:\n|$)/i)
|
||||
if (nameMatch) {
|
||||
name = nameMatch[1].trim()
|
||||
}
|
||||
|
||||
discoveredRoles[roleName] = {
|
||||
file: `@package://${relativePath}`,
|
||||
name,
|
||||
description,
|
||||
source: 'local-discovery'
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`跳过无效的角色文件: ${roleFile}`, error.message)
|
||||
}
|
||||
}
|
||||
|
||||
return discoveredRoles
|
||||
} catch (error) {
|
||||
console.warn('动态角色发现失败:', error.message)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HelloCommand
|
||||
|
||||
232
src/lib/core/pouch/commands/RegisterCommand.js
Normal file
232
src/lib/core/pouch/commands/RegisterCommand.js
Normal file
@ -0,0 +1,232 @@
|
||||
const BasePouchCommand = require('../BasePouchCommand')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const PackageProtocol = require('../../resource/protocols/PackageProtocol')
|
||||
const { buildCommand } = require('../../../../constants')
|
||||
|
||||
/**
|
||||
* 角色注册锦囊命令
|
||||
* 负责将新创建的角色注册到系统中
|
||||
*/
|
||||
class RegisterCommand extends BasePouchCommand {
|
||||
constructor () {
|
||||
super()
|
||||
this.packageProtocol = new PackageProtocol()
|
||||
}
|
||||
|
||||
getPurpose () {
|
||||
return '注册新创建的角色到系统中,使其可以被发现和激活'
|
||||
}
|
||||
|
||||
async getContent (args) {
|
||||
const [roleId] = args
|
||||
|
||||
if (!roleId) {
|
||||
return `❌ 请指定要注册的角色ID
|
||||
|
||||
🔍 使用方法:
|
||||
\`\`\`bash
|
||||
${buildCommand.register('<角色ID>')}
|
||||
\`\`\`
|
||||
|
||||
💡 例如:
|
||||
\`\`\`bash
|
||||
${buildCommand.register('my-custom-role')}
|
||||
\`\`\``
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. 检查角色文件是否存在
|
||||
const roleExists = await this.checkRoleExists(roleId)
|
||||
if (!roleExists) {
|
||||
return `❌ 角色文件不存在!
|
||||
|
||||
请确保以下文件存在:
|
||||
- prompt/domain/${roleId}/${roleId}.role.md
|
||||
- prompt/domain/${roleId}/thought/${roleId}.thought.md
|
||||
- prompt/domain/${roleId}/execution/${roleId}.execution.md
|
||||
|
||||
💡 您可以使用角色设计师来创建完整的角色套件:
|
||||
\`\`\`bash
|
||||
${buildCommand.action('role-designer')}
|
||||
\`\`\``
|
||||
}
|
||||
|
||||
// 2. 提取角色元数据
|
||||
const roleMetadata = await this.extractRoleMetadata(roleId)
|
||||
|
||||
// 3. 注册角色到系统
|
||||
const registrationResult = await this.registerRole(roleId, roleMetadata)
|
||||
|
||||
if (registrationResult.success) {
|
||||
return `✅ 角色 "${roleId}" 注册成功!
|
||||
|
||||
📋 **注册信息**:
|
||||
- 名称:${roleMetadata.name}
|
||||
- 描述:${roleMetadata.description}
|
||||
- 文件路径:${roleMetadata.filePath}
|
||||
|
||||
🎯 **下一步操作**:
|
||||
\`\`\`bash
|
||||
${buildCommand.action(roleId)}
|
||||
\`\`\`
|
||||
|
||||
💡 现在您可以激活这个角色了!`
|
||||
} else {
|
||||
return `❌ 角色注册失败:${registrationResult.error}
|
||||
|
||||
🔍 请检查:
|
||||
- 角色文件格式是否正确
|
||||
- 是否有写入权限
|
||||
- 注册表文件是否可访问`
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Register command error:', error)
|
||||
return `❌ 注册角色 "${roleId}" 时发生错误:${error.message}
|
||||
|
||||
💡 请确保角色文件存在且格式正确。`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查角色文件是否存在
|
||||
*/
|
||||
async checkRoleExists (roleId) {
|
||||
try {
|
||||
const packageRoot = await this.packageProtocol.getPackageRoot()
|
||||
const roleFile = path.join(packageRoot, 'prompt', 'domain', roleId, `${roleId}.role.md`)
|
||||
|
||||
return await fs.pathExists(roleFile)
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取角色元数据
|
||||
*/
|
||||
async extractRoleMetadata (roleId) {
|
||||
const packageRoot = await this.packageProtocol.getPackageRoot()
|
||||
const roleFile = path.join(packageRoot, 'prompt', 'domain', roleId, `${roleId}.role.md`)
|
||||
|
||||
const content = await fs.readFile(roleFile, 'utf-8')
|
||||
const relativePath = path.relative(packageRoot, roleFile)
|
||||
|
||||
// 提取元数据
|
||||
let name = `🎭 ${roleId}`
|
||||
let description = '用户自定义角色'
|
||||
|
||||
// 从注释中提取元数据(支持多行)
|
||||
const nameMatch = content.match(/name:\s*(.+?)(?:\n|$)/i)
|
||||
if (nameMatch) {
|
||||
name = nameMatch[1].trim()
|
||||
}
|
||||
|
||||
const descMatch = content.match(/description:\s*(.+?)(?:\n|$)/i)
|
||||
if (descMatch) {
|
||||
description = descMatch[1].trim()
|
||||
}
|
||||
|
||||
// 如果没有找到注释,尝试从文件内容推断
|
||||
if (name === `🎭 ${roleId}` && description === '用户自定义角色') {
|
||||
// 可以根据角色内容进行更智能的推断
|
||||
if (content.includes('产品')) {
|
||||
name = `📊 ${roleId}`
|
||||
} else if (content.includes('开发') || content.includes('代码')) {
|
||||
name = `💻 ${roleId}`
|
||||
} else if (content.includes('设计')) {
|
||||
name = `🎨 ${roleId}`
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
description,
|
||||
filePath: `@package://${relativePath}`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册角色到系统
|
||||
*/
|
||||
async registerRole (roleId, metadata) {
|
||||
try {
|
||||
const packageRoot = await this.packageProtocol.getPackageRoot()
|
||||
const registryPath = path.join(packageRoot, 'src', 'resource.registry.json')
|
||||
|
||||
// 读取当前注册表
|
||||
const registry = await fs.readJson(registryPath)
|
||||
|
||||
// 添加新角色
|
||||
if (!registry.protocols.role.registry) {
|
||||
registry.protocols.role.registry = {}
|
||||
}
|
||||
|
||||
registry.protocols.role.registry[roleId] = {
|
||||
file: metadata.filePath,
|
||||
name: metadata.name,
|
||||
description: metadata.description
|
||||
}
|
||||
|
||||
// 写回注册表
|
||||
await fs.writeJson(registryPath, registry, { spaces: 2 })
|
||||
|
||||
return { success: true }
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
getPATEOAS (args) {
|
||||
const [roleId] = args
|
||||
|
||||
if (!roleId) {
|
||||
return {
|
||||
currentState: 'register_awaiting_role',
|
||||
availableTransitions: ['hello', 'action'],
|
||||
nextActions: [
|
||||
{
|
||||
name: '查看可用角色',
|
||||
description: '查看已注册的角色',
|
||||
command: buildCommand.hello(),
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
name: '创建新角色',
|
||||
description: '使用角色设计师创建新角色',
|
||||
command: buildCommand.action('role-designer'),
|
||||
priority: 'high'
|
||||
}
|
||||
],
|
||||
metadata: {
|
||||
message: '需要指定角色ID'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
currentState: 'register_completed',
|
||||
availableTransitions: ['action', 'hello'],
|
||||
nextActions: [
|
||||
{
|
||||
name: '激活角色',
|
||||
description: '激活刚注册的角色',
|
||||
command: buildCommand.action(roleId),
|
||||
priority: 'high'
|
||||
},
|
||||
{
|
||||
name: '查看所有角色',
|
||||
description: '查看角色列表',
|
||||
command: buildCommand.hello(),
|
||||
priority: 'medium'
|
||||
}
|
||||
],
|
||||
metadata: {
|
||||
registeredRole: roleId,
|
||||
systemVersion: '锦囊串联状态机 v1.0'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RegisterCommand
|
||||
Reference in New Issue
Block a user