From 192eb2a0dfec475b669acbe60064d79d0ab17123 Mon Sep 17 00:00:00 2001 From: sean Date: Tue, 10 Jun 2025 23:58:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=B5=84=E6=BA=90=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=99=A8=E5=92=8C=E5=91=BD=E4=BB=A4=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=9A=E6=96=B0=E5=A2=9E=E8=A7=92=E8=89=B2=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E5=92=8C=E7=94=9F=E6=88=90=E7=9B=B8=E5=85=B3=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E8=B5=84=E6=BA=90=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=EF=BC=8C=E6=94=AF=E6=8C=81=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E8=B5=84=E6=BA=90=E7=9A=84=E5=8F=91?= =?UTF-8?q?=E7=8E=B0=E4=B8=8E=E5=90=88=E5=B9=B6=EF=BC=8C=E5=90=8C=E6=97=B6?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86=E5=92=8C?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=E6=8F=90=E5=8F=96=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E6=8F=90=E5=8D=87=E7=B3=BB=E7=BB=9F=E7=9A=84=E7=81=B5=E6=B4=BB?= =?UTF-8?q?=E6=80=A7=E5=92=8C=E7=94=A8=E6=88=B7=E4=BD=93=E9=AA=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/user-role-creation-system.md | 465 ++++++++++++++++ .../execution-authoring.execution.md | 148 +++++ .../execution/resource-authoring.execution.md | 223 ++++++++ .../execution/role-authoring.execution.md | 241 ++++++++ .../execution/role-generation.execution.md | 138 +++++ .../execution/thought-authoring.execution.md | 183 ++++++ prompt/core/nuwa/nuwa.role.md | 19 + prompt/core/thought/role-creation.thought.md | 63 +++ ...echat-miniprogram-development.execution.md | 244 ++++++++ scripts/start-mcp.sh | 1 + src/lib/core/pouch/commands/ActionCommand.js | 8 +- src/lib/core/pouch/commands/HelloCommand.js | 99 ++-- src/lib/core/resource/resourceManager.js | 274 ++++++++- src/resource.registry.json | 15 +- .../CrossPlatformDiscovery.unit.test.js | 221 ++++++++ .../commands/HelloCommand.integration.test.js | 231 ++++++++ src/tests/commands/HelloCommand.unit.test.js | 323 +++++++++++ .../UserRoleDiscovery.integration.test.js | 525 ++++++++++++++++++ .../resource/ResourceManager.unit.test.js | 220 ++++++++ 19 files changed, 3596 insertions(+), 45 deletions(-) create mode 100644 docs/user-role-creation-system.md create mode 100644 prompt/core/execution/execution-authoring.execution.md create mode 100644 prompt/core/execution/resource-authoring.execution.md create mode 100644 prompt/core/execution/role-authoring.execution.md create mode 100644 prompt/core/execution/role-generation.execution.md create mode 100644 prompt/core/execution/thought-authoring.execution.md create mode 100644 prompt/core/nuwa/nuwa.role.md create mode 100644 prompt/core/thought/role-creation.thought.md create mode 100644 prompt/domain/frontend-developer/execution/wechat-miniprogram-development.execution.md create mode 100644 scripts/start-mcp.sh create mode 100644 src/tests/commands/CrossPlatformDiscovery.unit.test.js create mode 100644 src/tests/commands/HelloCommand.integration.test.js create mode 100644 src/tests/commands/HelloCommand.unit.test.js create mode 100644 src/tests/commands/UserRoleDiscovery.integration.test.js create mode 100644 src/tests/core/resource/ResourceManager.unit.test.js diff --git a/docs/user-role-creation-system.md b/docs/user-role-creation-system.md new file mode 100644 index 0000000..1a998fe --- /dev/null +++ b/docs/user-role-creation-system.md @@ -0,0 +1,465 @@ +# 用户资源发现系统设计 + +## 📋 概述 + +基于ResourceManager的用户资源发现机制,支持用户创建自定义角色、执行流程、思维模式等资源,实现即创即用的体验。 + +## 🎯 核心问题 + +**现状分析**: +- 系统资源已在 `src/resource.registry.json` 静态注册 +- 当前 `HelloCommand.discoverLocalRoles()` 错误扫描系统路径,造成重复处理 +- 用户需要在项目级别创建和管理自定义资源 + +**解决目标**: +- 仅发现用户资源,不重复处理系统资源 +- 支持多种资源类型:角色、执行流程、思维模式 +- 实现用户资源覆盖系统资源的能力 + +## 🏗️ 架构设计 + +### 资源分层 + +``` +系统资源 (静态注册) +├── src/resource.registry.json # 系统资源注册表 +└── prompt/domain/{role}/ # 系统资源文件 + +用户资源 (动态发现) +└── .promptx/resource/domain/{role}/ # 用户资源文件 +``` + +### 目录结构规范 + +#### 用户资源目录 +``` +.promptx/ +├── resource/ +│ └── domain/ +│ └── {role-id}/ +│ ├── {role-id}.role.md +│ ├── thought/ +│ │ └── {name}.thought.md +│ └── execution/ +│ └── {name}.execution.md +└── memory/ # 现有目录 +``` + +**设计原则**: +- **镜像结构**:用户目录结构镜像系统结构,降低认知负载 +- **资源聚合**:角色相关资源统一管理在角色目录下 +- **类型支持**:支持 role、thought、execution 等多种资源类型 + +### 发现机制重构 + +#### ResourceManager 扩展 +```javascript +// src/lib/core/resource/resourceManager.js +class ResourceManager { + async discoverUserResources() { + const userResourcePath = path.join(packageRoot, '.promptx', 'resource', 'domain') + return await this.scanResourceDirectory(userResourcePath) + } + + async scanResourceDirectory(basePath) { + // 使用 Node.js 原生 API,移除 glob 依赖 + // 支持 role、thought、execution 等多种资源类型 + } + + async loadUnifiedRegistry() { + const systemResources = await this.loadSystemRegistry() + const userResources = await this.discoverUserResources() + + // 用户资源覆盖系统资源 + return { ...systemResources, ...userResources } + } +} +``` + +#### HelloCommand 简化 +```javascript +// src/lib/core/pouch/commands/HelloCommand.js +class HelloCommand { + async loadRoleRegistry() { + // 移除错误的本地发现逻辑 + // 直接从 ResourceManager 获取统一注册表 + return await this.resourceManager.loadUnifiedRegistry() + } +} +``` + +## 🤖 nuwa 角色设计 + +### 核心职责 +- **需求理解**:通过自然对话收集用户场景需求 +- **资源生成**:基于 DPML 协议生成角色文件 +- **文件管理**:将生成内容保存到正确的用户资源路径 + +### 对话策略 +``` +收集目标信息: +├── scenario: 用户工作场景 +├── painPoint: 主要痛点 +└── expectation: 期望结果 + +生成时机:三项信息齐全即可生成 +``` + +### 生成模板 +```xml + + + [基于场景的思维模式] + + + + [基于痛点的行为原则] + + + + [基于期望的知识体系] + + +``` + +## 🔧 技术实现 + +### 实现优先级 + +#### Phase 1: 核心功能 +1. **ResourceManager 扩展** + - 实现 `discoverUserResources()` 方法 + - 使用 Node.js 原生 API 替代 glob + - 支持多种资源类型扫描 + +2. **HelloCommand 重构** + - 移除错误的系统路径扫描 + - 集成 ResourceManager 统一注册表 + +3. **nuwa 角色实现** + - DPML 协议掌握和文件生成 + - 用户资源路径文件保存 + +#### Phase 2: 完善功能 +1. **错误处理**:跨平台兼容性和容错机制 +2. **性能优化**:资源发现缓存机制 +3. **用户体验**:更智能的对话策略 + +### 关键技术要点 + +#### 1. 跨平台路径处理 +```javascript +// 使用 Node.js 原生 API,避免 glob 兼容性问题 + const fs = require('fs-extra') + const path = require('path') + +async function discoverUserResources() { + const userResourcePath = path.join( + await packageProtocol.getPackageRoot(), + '.promptx', 'resource', 'domain' + ) + + if (!await fs.pathExists(userResourcePath)) { + return {} + } + + // 使用原生 readdir 和 stat 扫描 +} +``` + +#### 2. 资源覆盖机制 +```javascript +// 用户资源优先级高于系统资源 +const unifiedRegistry = { + ...systemResources, // 系统资源作为基础 + ...userResources // 用户资源覆盖同名项 +} +``` + +#### 3. DPML 元数据提取 +```javascript +function extractRoleMetadata(content, roleId) { + // 从 DPML 标签中提取角色元信息 + // 用于角色发现和展示 +} +``` + +## 🧪 测试策略与设计 + +### 测试架构分层 + +``` +单元测试层 (Unit Tests) +├── ResourceManager.unit.test.js # 资源管理器核心逻辑测试 +├── HelloCommand.unit.test.js # 命令行接口测试 +├── UserResourceDiscovery.unit.test.js # 用户资源发现测试 +└── DPMLParser.unit.test.js # DPML格式解析测试 + +集成测试层 (Integration Tests) +├── ResourceDiscovery.integration.test.js # 资源发现完整流程测试 +├── NuwaRoleGeneration.integration.test.js # nuwa角色生成端到端测试 +└── CrossPlatform.integration.test.js # 跨平台兼容性测试 + +端到端测试层 (E2E Tests) +└── UserWorkflow.e2e.test.js # 完整用户工作流程测试 +``` + +### 核心测试组件 + +#### 1. ResourceManager 单元测试 +```javascript +// src/tests/core/resource/ResourceManager.unit.test.js +describe('ResourceManager', () => { + describe('discoverUserResources', () => { + it('应该正确扫描用户资源目录', async () => { + // 模拟用户资源文件结构 + // 验证发现结果的正确性 + }) + + it('应该支持多种资源类型', async () => { + // 测试 role、thought、execution 类型 + }) + + it('应该处理不存在的目录', async () => { + // 测试容错机制 + }) + }) + + describe('loadUnifiedRegistry', () => { + it('应该正确合并系统和用户资源', async () => { + // 验证用户资源覆盖系统资源 + }) + }) +}) +``` + +#### 2. HelloCommand 重构测试 +```javascript +// src/tests/commands/HelloCommand.unit.test.js +describe('HelloCommand - 重构后', () => { + it('应该移除错误的系统路径扫描', async () => { + // 验证不再扫描 prompt/domain/ 路径 + }) + + it('应该集成ResourceManager统一注册表', async () => { + // 验证使用ResourceManager.loadUnifiedRegistry() + }) + + it('应该正确显示用户自定义角色', async () => { + // 验证用户角色在hello命令中的展示 + }) +}) +``` + +#### 3. 用户资源发现集成测试 +```javascript +// src/tests/integration/UserResourceDiscovery.integration.test.js +describe('用户资源发现机制', () => { + beforeEach(async () => { + // 创建测试用的用户资源结构 + await createTestUserResourceStructure() + }) + + it('应该发现用户创建的角色', async () => { + // 创建测试角色文件 + // 验证ResourceManager能正确发现 + }) + + it('应该支持资源类型扩展', async () => { + // 测试thought、execution文件的发现 + }) + + it('应该处理DPML格式验证', async () => { + // 测试格式错误的文件处理 + }) +}) +``` + +#### 4. nuwa 角色生成端到端测试 +```javascript +// src/tests/integration/NuwaRoleGeneration.integration.test.js +describe('nuwa 角色生成完整流程', () => { + it('应该根据用户需求生成角色文件', async () => { + // 模拟用户输入 + // 验证生成的文件内容和位置 + }) + + it('应该生成符合DPML规范的角色', async () => { + // 验证生成文件的DPML格式正确性 + }) + + it('应该创建正确的目录结构', async () => { + // 验证镜像系统结构的目录创建 + }) +}) +``` + +#### 5. 跨平台兼容性测试 +```javascript +// src/tests/integration/CrossPlatform.integration.test.js +describe('跨平台兼容性', () => { + it('应该在Windows上正确处理路径', () => { + // 模拟Windows路径分隔符 + // 验证路径处理的正确性 + }) + + it('应该在macOS/Linux上正确处理路径', () => { + // 验证Unix风格路径处理 + }) + + it('应该使用Node.js原生API替代glob', () => { + // 验证不使用glob库的实现 + }) +}) +``` + +### 测试数据和环境 + +#### 测试数据结构 +``` +src/tests/fixtures/ +├── user-resources/ +│ └── domain/ +│ ├── test-role/ +│ │ ├── test-role.role.md # 标准DPML格式 +│ │ ├── thought/ +│ │ │ └── test.thought.md +│ │ └── execution/ +│ │ └── test.execution.md +│ ├── invalid-role/ +│ │ └── invalid.role.md # 格式错误的文件 +│ └── sales-analyst/ +│ └── sales-analyst.role.md # nuwa生成测试样例 +├── system-resources/ +│ └── mock-registry.json # 模拟系统注册表 +└── dpml-samples/ + ├── valid-role.md # 有效DPML样例 + └── invalid-role.md # 无效DPML样例 +``` + +#### 测试环境配置 +```javascript +// src/tests/setup/testEnvironment.js +export class TestEnvironment { + async setup() { + // 创建临时测试目录 + this.testDir = await createTempTestDirectory() + + // 模拟 .promptx 结构 + await this.createMockUserResourceStructure() + + // 设置环境变量 + process.env.PROMPTX_TEST_MODE = 'true' + } + + async teardown() { + // 清理测试文件 + await fs.remove(this.testDir) + } +} +``` + +### 测试覆盖率要求 + +#### 覆盖率目标 +- **整体代码覆盖率**: ≥ 85% +- **ResourceManager核心逻辑**: ≥ 95% +- **HelloCommand重构部分**: ≥ 90% +- **DPML解析逻辑**: ≥ 95% +- **跨平台路径处理**: 100% + +#### 关键测试场景 +``` +✅ 用户资源发现功能 +✅ 系统资源静态加载 +✅ 资源覆盖机制 +✅ DPML格式验证 +✅ 跨平台路径处理 +✅ 错误处理和容错 +✅ nuwa角色生成流程 +✅ 文件系统操作安全性 +✅ 缓存机制有效性 +✅ CLI集成正确性 +``` + +### 测试执行策略 + +#### 测试运行配置 +```json +// package.json scripts +{ + "test": "jest", + "test:unit": "jest --testPathPattern=unit", + "test:integration": "jest --testPathPattern=integration", + "test:e2e": "jest --testPathPattern=e2e", + "test:coverage": "jest --coverage", + "test:watch": "jest --watch" +} +``` + +#### CI/CD 集成 +```yaml +# .github/workflows/test.yml +name: Test Suite +on: [push, pull_request] +jobs: + test: + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + node: [16, 18, 20] + steps: + - name: Run Unit Tests + run: npm run test:unit + - name: Run Integration Tests + run: npm run test:integration + - name: Check Coverage + run: npm run test:coverage +``` + +## 📊 用户体验流程 + +```bash +# 1. 创建角色 +npx promptx action nuwa +# 对话生成: .promptx/resource/domain/sales-analyst/sales-analyst.role.md + +# 2. 立即可用(自动发现) +npx promptx hello +# 显示新角色: sales-analyst + +# 3. 直接使用 +npx promptx action sales-analyst +``` + +## 🔄 设计决策 + +### 为什么选择 .promptx/resource/domain 结构? +- **镜像一致性**:与系统 `prompt/domain` 结构保持一致 +- **类型扩展性**:未来可支持 thought、execution 等资源类型 +- **认知简单性**:用户理解成本最低 + +### 为什么移除 HelloCommand 的发现逻辑? +- **职责单一**:ResourceManager 专门负责资源管理 +- **避免重复**:系统资源已静态注册,无需重复发现 +- **架构清晰**:分层明确,便于维护 + +### 为什么使用 Node.js 原生 API? +- **兼容性**:完全跨平台,无第三方库依赖问题 +- **性能**:原生 API 性能更优 +- **维护性**:减少依赖复杂度 + +## 📚 相关文档 + +- [DPML协议](../prompt/protocol/dpml.protocol.md) +- [ResourceManager 架构](../src/lib/core/resource/) +- [角色标签规范](../prompt/protocol/tag/role.tag.md) + +--- + +**实现要点**: +1. ResourceManager 统一资源发现 +2. 用户资源镜像系统结构 +3. nuwa 基于 DPML 生成角色 +4. 即创即用的无缝体验 +5. 完整测试覆盖和质量保证 \ No newline at end of file diff --git a/prompt/core/execution/execution-authoring.execution.md b/prompt/core/execution/execution-authoring.execution.md new file mode 100644 index 0000000..a97d5f1 --- /dev/null +++ b/prompt/core/execution/execution-authoring.execution.md @@ -0,0 +1,148 @@ + + + ## 客观技术限制 + - **DPML语法约束**:必须遵循EBNF定义的execution语法结构 + - **XML格式要求**:标签必须正确闭合,属性值必须用双引号包围 + - **Markdown兼容性**:内容部分必须是有效的Markdown格式 + - **文件编码**:必须使用UTF-8编码 + - **优先级固化**:五个子标签的优先级关系不可改变 + + + + ## 强制性编写规则 + - **纯XML结构**:execution文件必须从``标签开始,不得包含任何XML结构外的内容(如Markdown标题、注释等) + - **根标签强制**:文件必须使用``作为根标签包装全部内容 + - **子标签命名**:只能使用规范定义的五个子标签:constraint, rule, guideline, process, criteria + - **优先级顺序**:子标签必须按constraint → rule → guideline → process → criteria顺序排列 + - **内容完整性**:每个子标签都必须包含实质性内容,不得为空 + - **语义一致性**:子标签内容必须与其语义定义保持一致 + - **文件纯净性**:除了``标签结构外,不得包含任何其他内容 + + + + ## 编写指导原则 + - **语义明确性**:每个子标签的内容应清晰表达其特定语义 + - **内容层次化**:使用Markdown的标题、列表等结构组织内容 + - **实用性导向**:内容应具有实际操作指导价值 + - **简洁性原则**:避免冗长表述,保持核心要点突出 + - **一致性维护**:在整个文件中保持术语和表达方式的一致性 + + + + ## 编写执行流程 + + ### Phase 1: 分析需求和上下文 + 1. **明确execution目的**:确定这个execution要解决什么问题 + 2. **识别客观限制**:分析技术、环境、资源等客观约束条件 + 3. **定义强制要求**:确定必须遵守的规则和底线要求 + 4. **收集最佳实践**:整理相关领域的指导原则和建议 + + ### Phase 2: 内容规划和结构设计 + 1. **约束条件梳理**: + - 列出所有客观存在的限制条件 + - 按重要性和影响程度排序 + - 确保约束条件的客观性和不可变性 + + 2. **规则定义设计**: + - 识别必须严格遵守的行为准则 + - 明确违反规则的后果和风险 + - 确保规则与约束条件不冲突 + + 3. **指导原则制定**: + - 提供灵活性建议和最佳实践 + - 允许根据具体情况调整 + - 确保不违反已定义的规则和约束 + + 4. **流程步骤设计**: + - 在约束和规则框架内设计执行路径 + - 包含正常流程和异常处理 + - 确保步骤的可操作性和逻辑性 + + 5. **评价标准确立**: + - 定义成功完成的判断依据 + - 考虑所有优先级更高元素的要求 + - 提供可量化的评估方法 + + ### Phase 3: DPML结构实现 + + **关键要求:文件必须从``标签直接开始** + + ```xml + + + [客观限制条件内容] + + + + [强制性规则内容] + + + + [指导原则内容] + + + + [具体执行步骤] + + + + [评价标准内容] + + + ``` + + **错误示例(禁止):** + ```markdown + # 标题 + 这是描述内容... + + + ... + + ``` + + ### Phase 4: 质量检查和优化 + 1. **语法验证**:确保DPML语法正确性 + 2. **语义一致性检查**:验证各部分逻辑关系 + 3. **优先级关系验证**:确认无冲突和矛盾 + 4. **实用性测试**:验证内容的可操作性 + 5. **完整性审核**:确保覆盖所有必要方面 + 6. **纯净性检查**:确认文件从``标签开始,无多余内容 + + + + ## 质量评价标准 + + ### 格式合规性 + - ✅ 文件从``标签直接开始,无额外内容 + - ✅ 使用正确的DPML execution标签结构 + - ✅ 五个子标签按规定顺序排列 + - ✅ XML语法正确,标签正确闭合 + - ✅ Markdown格式规范,层次清晰 + + ### 内容完整性 + - ✅ 每个子标签都包含实质性内容 + - ✅ 约束条件体现客观性和不可变性 + - ✅ 规则体现强制性和明确性 + - ✅ 指导原则体现建议性和灵活性 + - ✅ 流程步骤具有可操作性和逻辑性 + - ✅ 评价标准具有可验证性 + + ### 语义一致性 + - ✅ 各子标签内容与其语义定义匹配 + - ✅ 优先级关系得到正确体现 + - ✅ 不存在逻辑冲突和矛盾 + - ✅ 术语使用保持一致性 + + ### 实用价值 + - ✅ 内容具有实际指导意义 + - ✅ 步骤和标准可以实际执行 + - ✅ 能够解决实际问题 + - ✅ 适用于目标场景和用户 + + ### 文件纯净性 + - ✅ 文件结构完全符合DPML execution规范 + - ✅ 无任何XML结构外的多余内容 + - ✅ 体现execution文件的标准格式 + + \ No newline at end of file diff --git a/prompt/core/execution/resource-authoring.execution.md b/prompt/core/execution/resource-authoring.execution.md new file mode 100644 index 0000000..67d7c25 --- /dev/null +++ b/prompt/core/execution/resource-authoring.execution.md @@ -0,0 +1,223 @@ + + + ## 客观技术限制 + - **DPML语法约束**:必须遵循EBNF定义的resource语法结构 + - **XML格式要求**:标签必须正确闭合,属性值必须用双引号包围 + - **protocol属性强制**:resource标签必须包含protocol属性指定协议名 + - **文件编码**:必须使用UTF-8编码 + - **代码实现约束**:必须与ResourceManager、ResourceProtocol基类兼容 + - **注册表集成**:必须与resource.registry.json统一注册表集成 + - **查询参数限制**:查询参数必须符合URL标准格式 + + + + ## 强制性编写规则 + - **纯XML结构**:resource文件必须从``标签开始,不得包含任何XML结构外的内容 + - **根标签强制**:文件必须使用``作为根标签 + - **三组件完整**:必须包含location、params、registry三个子标签 + - **组件顺序固定**:子标签必须按location → params → registry顺序排列 + - **protocol属性必需**:根标签必须包含protocol属性且值唯一 + - **文件纯净性**:除了``标签结构外,不得包含任何其他内容 + - **EBNF规范性**:location标签内容必须使用EBNF语法定义路径格式 + + + + ## 编写指导原则 + - **协议名称清晰**:protocol属性值应简洁明了,符合kebab-case命名规范 + - **路径格式标准化**:使用EBNF语法精确定义资源路径结构 + - **参数文档完整**:详细说明所有支持的查询参数及其类型和用途 + - **注册表合理性**:注册表映射应体现抽象性和实用性的平衡 + - **兼容性考虑**:确保与PromptX资源管理系统的无缝集成 + - **示例丰富性**:提供足够的使用示例帮助理解协议用法 + + + + ## 编写执行流程 + + ### Phase 1: 协议概念设计 + 1. **确定协议用途**:明确这个资源协议要解决什么资源访问问题 + 2. **分析资源特征**:识别目标资源的组织方式、访问模式和参数需求 + 3. **设计协议名称**:选择简洁清晰的协议标识符 + 4. **评估系统集成**:确认与PromptX现有协议的兼容性和差异性 + + ### Phase 2: 路径格式设计(location组件) + 1. **路径结构分析**: + - 确定资源的层次结构和定位方式 + - 分析是否需要支持参数化路径 + - 设计路径的语义表达 + + 2. **EBNF语法定义**: + ```ebnf + location ::= protocol_name '://' path_structure + path_structure ::= segment {'/' segment} + segment ::= literal | parameter + parameter ::= '{' parameter_name '}' + ``` + + 3. **路径规范示例**: + - 简单路径:`protocol://resource_id` + - 参数化路径:`protocol://{category}/{id}` + - 复杂路径:`protocol://{domain}/{namespace}/{resource}` + + ### Phase 3: 查询参数设计(params组件) + 1. **参数分类规划**: + - **格式控制参数**:如format、encoding等 + - **行为控制参数**:如cache、timeout等 + - **过滤参数**:如line、type等 + - **特定功能参数**:协议专有的参数 + + 2. **参数文档格式**: + ```markdown + | 参数名 | 类型 | 描述 | 默认值 | 示例 | + |-------|------|------|--------|------| + | format | string | 输出格式 | text | json, xml | + | cache | boolean | 是否缓存 | true | true, false | + ``` + + 3. **参数验证考虑**: + - 参数类型验证 + - 参数值范围限制 + - 参数组合逻辑 + + ### Phase 4: 注册表设计(registry组件) + 1. **注册表策略选择**: + - **有注册表协议**:需要ID到路径的映射(如thought, execution) + - **无注册表协议**:直接使用路径(如file, http) + + 2. **映射关系设计**(适用于有注册表协议): + ```markdown + | 资源ID | 实际路径 | 描述 | + |--------|----------|------| + | resource-id | @package://path/to/file.md | 资源描述 | + ``` + + 3. **路径引用规范**: + - 支持@package://前缀引用包资源 + - 支持@project://前缀引用项目资源 + - 支持@file://前缀引用文件系统资源 + - 支持嵌套协议引用 + + ### Phase 5: DPML结构实现 + + **关键要求:文件必须从``标签直接开始** + + **有注册表协议示例:** + ```xml + + + ```ebnf + location ::= custom-protocol://{resource_id} + resource_id ::= [a-zA-Z][a-zA-Z0-9_-]* + ``` + + + + | 参数名 | 类型 | 描述 | 默认值 | + |-------|------|------|--------| + | format | string | 输出格式(text\|json\|xml) | text | + | cache | boolean | 是否缓存结果 | true | + | encoding | string | 文件编码 | utf8 | + + + + | 资源ID | 文件路径 | + |--------|----------| + | example-resource | @package://path/to/example.md | + | another-resource | @project://config/another.md | + + + ``` + + **无注册表协议示例:** + ```xml + + + ```ebnf + location ::= direct-access://{path} + path ::= absolute_path | relative_path + absolute_path ::= '/' path_segment {'/' path_segment} + relative_path ::= path_segment {'/' path_segment} + path_segment ::= [^/]+ + ``` + + + + | 参数名 | 类型 | 描述 | 默认值 | + |-------|------|------|--------| + | encoding | string | 文件编码 | utf8 | + | line | string | 行范围(如"1-10") | - | + + + + + + + ``` + + ### Phase 6: 系统集成验证 + 1. **注册表集成**:确保协议定义与resource.registry.json格式兼容 + 2. **代码实现检查**:验证是否需要创建对应的Protocol类文件 + 3. **ResourceManager集成**:确认协议能被ResourceManager正确加载 + 4. **加载语义支持**:验证@、@!、@?前缀的正确处理 + 5. **查询参数解析**:确保参数能被正确解析和应用 + + ### Phase 7: 质量检查和测试 + 1. **语法验证**:确保DPML resource语法正确性 + 2. **EBNF验证**:验证location部分的EBNF语法正确性 + 3. **参数完整性**:确认所有参数都有清晰的类型和描述 + 4. **注册表一致性**:验证注册表映射的逻辑正确性 + 5. **纯净性检查**:确认文件从``标签开始,无多余内容 + + + + ## 质量评价标准 + + ### 格式合规性 + - ✅ 文件从``标签直接开始 + - ✅ 使用正确的DPML resource标签结构 + - ✅ 三个子标签按location → params → registry顺序排列 + - ✅ XML语法正确,标签正确闭合 + - ✅ protocol属性值符合命名规范 + + ### 路径格式规范性 + - ✅ location部分使用正确的EBNF语法 + - ✅ 路径格式清晰明确,无歧义 + - ✅ 参数化路径使用`{parameter}`格式 + - ✅ 路径结构与协议用途匹配 + - ✅ 支持协议的典型使用场景 + + ### 参数文档完整性 + - ✅ 所有参数都有清晰的类型定义 + - ✅ 参数描述详细且准确 + - ✅ 提供了合理的默认值 + - ✅ 参数示例有助于理解 + - ✅ 参数组合逻辑合理 + + ### 注册表设计合理性 + - ✅ 注册表策略与协议特性匹配 + - ✅ 映射关系清晰且实用 + - ✅ 路径引用符合PromptX规范 + - ✅ 抽象性和具体性平衡适当 + - ✅ 支持嵌套协议引用 + + ### 系统集成性 + - ✅ 与ResourceManager兼容 + - ✅ 与resource.registry.json格式一致 + - ✅ 支持标准加载语义(@、@!、@?) + - ✅ 查询参数能被正确解析 + - ✅ 与现有协议生态协调 + + ### 实用价值 + - ✅ 解决了实际的资源访问需求 + - ✅ 路径格式简洁易用 + - ✅ 参数设计灵活且必要 + - ✅ 注册表提供了实际价值 + - ✅ 整体设计具有可扩展性 + + ### 文件纯净性 + - ✅ 文件结构完全符合DPML resource规范 + - ✅ 无任何XML结构外的多余内容 + - ✅ 体现resource协议定义的标准格式 + - ✅ 三组件内容充实且相互配合 + + \ No newline at end of file diff --git a/prompt/core/execution/role-authoring.execution.md b/prompt/core/execution/role-authoring.execution.md new file mode 100644 index 0000000..385c801 --- /dev/null +++ b/prompt/core/execution/role-authoring.execution.md @@ -0,0 +1,241 @@ + + + ## 客观技术限制 + - **DPML语法约束**:必须遵循EBNF定义的role语法结构 + - **XML格式要求**:标签必须正确闭合,属性值必须用双引号包围 + - **三组件架构固化**:personality、principle、knowledge三组件的语义边界固定 + - **文件编码**:必须使用UTF-8编码 + - **引用协议约束**:@!引用必须指向实际存在的资源 + - **PromptX系统集成**:必须与promptx命令行工具和ResourceManager兼容 + + + + ## 强制性编写规则 + - **纯XML结构**:role文件必须从``标签开始,不得包含任何XML结构外的内容 + - **根标签强制**:文件必须使用``作为根标签包装全部内容 + - **三组件完整**:必须包含personality、principle、knowledge三个子标签 + - **组件顺序固定**:子标签必须按personality → principle → knowledge顺序排列 + - **文件纯净性**:除了``标签结构外,不得包含任何其他内容 + - **引用规范性**:使用@!引用时必须遵循resource协议语法 + - **镜像结构约束**:用户资源必须遵循`.promptx/resource/domain/`结构,镜像系统`prompt/domain/` + + + + ## 编写指导原则 + - **编排优先**:role文件主要职责是编排组合,推荐使用@!引用机制而非直接内容 + - **简洁性原则**:保持role文件的简洁和清晰,避免冗长的直接内容 + - **模块化思维**:将具体内容抽离到独立的thought、execution、knowledge文件中 + - **引用一致性**:在同一role文件中保持引用风格的一致性 + - **可维护性**:通过引用机制实现内容的独立维护和复用 + - **灵活性保留**:允许在引用和直接内容之间选择,但推荐引用 + - **镜像一致性**:用户资源结构与系统资源保持一致,降低认知负载 + + + + ## 编写执行流程 + + ### Phase 1: 角色概念设计 + 1. **明确角色定位**:确定AI角色的核心身份和专业领域 + 2. **分析能力需求**:识别角色需要的思维特征、行为原则和专业知识 + 3. **规划组件结构**:决定三个组件的具体内容来源和组织方式 + 4. **选择编排策略**:决定使用引用机制还是直接内容 + + ### Phase 2: 资源组织规划 + + #### 用户资源目录结构(镜像系统结构): + ``` + .promptx/resource/domain/{roleId}/ + ├── {roleId}.role.md # 主角色文件 + ├── thought/ # 思维模式目录 + │ └── {name}.thought.md # 专业思维模式 + └── execution/ # 执行流程目录 + └── {name}.execution.md # 专业执行流程 + ``` + + #### 内容来源规划: + 1. **思维模式来源**(personality组件): + - 核心引用:`@!thought://remember`(记忆能力) + - 核心引用:`@!thought://recall`(回忆能力) + - 专业引用:`@!thought://[role-specific]`(角色特定思维) + - 或直接定义角色的思维特征和认知偏好 + + 2. **行为原则来源**(principle组件): + - 专业引用:`@!execution://[role-specific]`(角色特定执行原则) + - 或直接定义角色的行为准则和工作流程 + + 3. **专业知识来源**(knowledge组件): + - 领域引用:`@!knowledge://[domain-specific]`(领域专业知识) + - 或直接定义角色的知识体系和技能框架 + + ### Phase 3: DPML结构实现 + + **关键要求:文件必须从``标签直接开始** + + **推荐编排风格(引用优先):** + ```xml + + + @!thought://remember + @!thought://recall + @!thought://[role-specific-thought] + + + + @!execution://[role-specific-execution] + + + + @!knowledge://[domain-specific-knowledge] + + + ``` + + **示例:助手角色(参考assistant.role.md)** + ```xml + + + @!thought://remember + @!thought://recall + @!thought://assistant + + + + @!execution://assistant + + + + @!knowledge://general-assistant + + + ``` + + **用户资源示例(自定义销售分析师):** + ```xml + + + @!thought://remember + @!thought://recall + @!thought://sales-analyst + + + + @!execution://sales-data-analysis + + + + @!knowledge://business-intelligence + + + ``` + + **混合风格(引用+直接内容):** + ```xml + + + @!thought://remember + @!thought://recall + + ## 角色特定思维特征 + - **用户导向思维**:始终以用户需求为中心 + - **解决方案思维**:专注于提供实用的解决方案 + + + + @!execution://assistant + + ## 补充行为原则 + - 保持耐心和友善的交互风格 + - 承认不确定性,不臆测答案 + + + + @!knowledge://general-assistant + + + ``` + + **纯直接内容风格(不推荐但允许):** + ```xml + + + # 角色思维模式 + ## 核心思维特征 + - **特征1**:描述 + - **特征2**:描述 + + + + # 角色行为原则 + ## 核心原则 + - **原则1**:描述 + - **原则2**:描述 + + + + # 角色专业知识 + ## 知识领域 + - **领域1**:描述 + - **领域2**:描述 + + + ``` + + ### Phase 4: 质量检查和集成验证 + 1. **结构验证**:确保DPML role语法正确性 + 2. **引用检查**:验证所有@!引用的资源实际存在 + 3. **三组件完整性**:确认personality、principle、knowledge都有实质内容 + 4. **系统集成测试**:验证与promptx命令和ResourceManager的兼容性 + 5. **纯净性检查**:确认文件从``标签开始,无多余内容 + 6. **镜像结构验证**:确认用户资源目录结构符合镜像规范 + + + + ## 质量评价标准 + + ### 格式合规性 + - ✅ 文件从``标签直接开始,无额外内容 + - ✅ 使用正确的DPML role标签结构 + - ✅ 三个子标签按personality → principle → knowledge顺序排列 + - ✅ XML语法正确,标签正确闭合 + - ✅ Markdown格式规范(如有直接内容) + + ### 编排质量 + - ✅ 体现role文件的编排组合职责 + - ✅ 合理使用@!引用机制实现模块化 + - ✅ 保持文件的简洁性和可读性 + - ✅ 引用风格在文件内保持一致 + - ✅ 避免不必要的冗长直接内容 + + ### 三组件完整性 + - ✅ personality组件包含思维特征定义或引用 + - ✅ principle组件包含行为原则定义或引用 + - ✅ knowledge组件包含专业知识定义或引用 + - ✅ 三组件逻辑一致,共同构建完整角色 + - ✅ 组件内容与角色定位匹配 + + ### 引用有效性 + - ✅ 所有@!引用遵循resource协议语法 + - ✅ 引用的资源路径正确且存在 + - ✅ 引用内容与组件语义匹配 + - ✅ 引用关系清晰,无循环依赖 + + ### 系统集成性 + - ✅ 与PromptX锦囊串联系统兼容 + - ✅ 支持promptx action命令激活 + - ✅ 角色定义可被AI系统正确解析 + - ✅ 实现角色的即时专家化能力 + - ✅ ResourceManager可正确发现和加载 + + ### 文件纯净性 + - ✅ 文件结构完全符合DPML role规范 + - ✅ 无任何XML结构外的多余内容 + - ✅ 体现role文件的标准编排格式 + - ✅ 维持role文件的简洁优雅特性 + + ### 架构合规性 + - ✅ 用户资源目录结构镜像系统结构 + - ✅ 文件组织符合`.promptx/resource/domain/`规范 + - ✅ 与系统资源结构保持一致性 + - ✅ 降低用户认知负载和学习成本 + + \ No newline at end of file diff --git a/prompt/core/execution/role-generation.execution.md b/prompt/core/execution/role-generation.execution.md new file mode 100644 index 0000000..af19a96 --- /dev/null +++ b/prompt/core/execution/role-generation.execution.md @@ -0,0 +1,138 @@ + + + ## 客观技术限制 + - **DPML协议约束**:生成的角色必须严格遵循DPML ``标签框架和三组件架构 + - **文件格式要求**:生成的角色文件必须是有效的Markdown格式并符合XML语法 + - **系统集成约束**:生成的角色必须与PromptX系统兼容,支持ResourceManager发现机制 + - **快速生成要求**:整个创建过程应在1-2分钟内完成 + - **目录结构约束**:用户资源必须创建在`.promptx/resource/domain/{roleId}/`目录,镜像系统结构 + - **文件组织约束**:角色相关的所有文件(execution、thought等)必须统一存放在角色目录下 + + + + ## 强制性执行规则 + - **三组件完整性**:每个生成的角色必须包含personality、principle、knowledge三个完整组件 + - **DPML语法严格性**:生成内容必须使用正确的XML标签语法,标签必须正确闭合 + - **领域识别准确性**:必须准确识别用户需求的专业领域 + - **模板化生成**:基于标准模板快速生成,避免复杂的定制化过程 + - **一次性交付**:生成后直接交付,避免反复确认和修改 + - **镜像结构强制**:用户资源必须创建在`.promptx/resource/domain/{roleId}/`,镜像系统`prompt/domain/`结构 + - **文件统一管理**:角色的execution、thought等扩展文件必须放在同一角色目录下,便于统一管理 + - **引用路径准确**:使用@!引用时必须指向正确的文件路径,确保引用关系有效 + + + + ## 执行指导原则 + - **简洁高效**:优先速度和效率,避免冗长对话 + - **标准化优先**:使用领域标准能力,而非深度定制 + - **即用原则**:生成的角色应立即可用,无需额外配置 + - **用户友好**:保持简单明了的交互体验 + - **镜像一致**:与系统结构保持一致,降低认知负载 + + + + ## 极简3步生成流程 + + ### Step 1: 领域快速识别 (30秒内) + ``` + 目标:从用户描述中快速提取领域关键词 + + 识别策略: + - 提取技术栈关键词(如:微信小程序、React、Python等) + - 识别职业角色关键词(如:产品经理、设计师、运营等) + - 理解功能需求关键词(如:开发、分析、营销等) + + 快速确认: + "明白了!您需要一个【X领域】的专业AI助手,对吗?" + + 处理原则: + - 最多1次确认,用户确认后立即进入生成 + - 如果领域明确,跳过确认直接生成 + ``` + + ### Step 2: 模板化角色生成 (60秒内) + ``` + 基于识别的领域,调用标准模板: + + 模板选择逻辑: + - 微信小程序 → 小程序开发专家模板 + - 前端开发 → 前端工程师模板 + - 产品管理 → 产品经理模板 + - 数据分析 → 数据分析师模板 + - 更多领域... → 对应专业模板 + + 文件组织结构(镜像系统结构): + .promptx/resource/domain/{roleId}/ + ├── {roleId}.role.md # 主角色文件 + ├── thought/ # 思维模式目录(需要时创建) + │ └── {specific}.thought.md # 专业思维模式 + └── execution/ # 执行模式目录(需要时创建) + └── {specific}.execution.md # 专业执行流程 + + 三组件自动填充: + personality: 引用该领域的标准思维模式(remember + recall + 专业思维) + principle: 引用该领域的标准执行流程(可独立创建execution文件) + knowledge: 引用该领域的专业知识体系(或直接定义) + + 质量检查: + ☐ DPML格式正确 + ☐ 三组件完整 + ☐ 引用资源有效 + ☐ 目录结构规范(镜像系统结构) + ☐ 文件路径正确 + ☐ ResourceManager可发现 + ``` + + ### Step 3: 结果直接交付 (30秒内) + ``` + 呈现格式: + 1. 角色价值简述 + 2. 文件创建确认(正确目录结构) + 3. 激活命令说明 + 4. 使用建议(可选) + + 目录结构展示(镜像系统结构): + .promptx/resource/domain/{roleId}/ + ├── {roleId}.role.md # ✅ 已创建 + └── [其他扩展文件] # ✅ 按需创建 + + 交付策略: + - 确认角色已正确创建在用户资源目录 + - 提供立即可用的激活命令 + - 说明文件组织规范(与系统结构一致) + - 说明ResourceManager自动发现机制 + + 后续支持: + - 如果用户满意,直接结束 + - 如果需要扩展功能,指导创建execution/thought文件 + ``` + + + + ## 质量评价标准 + + ### 效率指标 + - ✅ 总用时 ≤ 2分钟 + - ✅ 对话轮次 ≤ 3轮 + - ✅ 一次性生成成功率 ≥ 90% + - ✅ 用户满意度 ≥ 85% + + ### 角色质量 + - ✅ DPML协议完全合规 + - ✅ 三组件内容实用 + - ✅ 角色定位准确 + - ✅ 立即可激活使用 + + ### 架构合规 + - ✅ 目录结构镜像系统结构 + - ✅ ResourceManager可发现 + - ✅ 用户资源路径正确 + - ✅ 引用关系有效 + + ### 用户体验 + - ✅ 交互流程简洁 + - ✅ 生成结果清晰 + - ✅ 激活方法明确 + - ✅ 学习成本极低 + + \ No newline at end of file diff --git a/prompt/core/execution/thought-authoring.execution.md b/prompt/core/execution/thought-authoring.execution.md new file mode 100644 index 0000000..55e5975 --- /dev/null +++ b/prompt/core/execution/thought-authoring.execution.md @@ -0,0 +1,183 @@ + + + ## 客观技术限制 + - **DPML语法约束**:必须遵循EBNF定义的thought语法结构 + - **XML格式要求**:标签必须正确闭合,属性值必须用双引号包围 + - **Markdown兼容性**:内容部分必须是有效的Markdown格式,支持Mermaid图表 + - **文件编码**:必须使用UTF-8编码 + - **思维模式固化**:四种思维模式的语义特征不可改变 + - **可视化限制**:Mermaid图表必须符合语法规范 + + + + ## 强制性编写规则 + - **纯XML结构**:thought文件必须从``标签开始,不得包含任何XML结构外的内容 + - **根标签强制**:文件必须使用``作为根标签包装全部内容 + - **子标签命名**:只能使用规范定义的四个思维模式子标签:exploration, reasoning, plan, challenge + - **语义一致性**:每个子标签内容必须与其思维模式语义定义保持一致 + - **文件纯净性**:除了``标签结构外,不得包含任何其他内容 + - **内容实质性**:包含的子标签都必须有实质性思考内容,不得为空 + + + + ## 编写指导原则 + - **思维模式清晰性**:每个子标签的内容应明确体现对应的思维特征 + - **推荐思考顺序**:建议按exploration → challenge → reasoning → plan顺序组织思考 + - **可视化优先**:优先使用Mermaid图表表达复杂的思维关系和逻辑结构 + - **内容层次化**:使用Markdown的标题、列表等结构组织思考内容 + - **思维完整性**:确保思考覆盖问题的关键维度,避免思维盲区 + - **灵活组合**:根据实际需求选择合适的思维模式组合,无需全部使用 + + + + ## 编写执行流程 + + ### Phase 1: 思考需求分析 + 1. **明确思考目标**:确定这个thought要解决什么问题或分析什么议题 + 2. **识别思考复杂度**:判断问题是否需要多维度思考 + 3. **选择思维模式**:根据问题特点选择合适的思维模式组合 + 4. **确定思考深度**:决定每个思维模式需要的分析深度 + + ### Phase 2: 思维模式规划 + 1. **探索思维规划**: + - 确定需要发散思考的维度 + - 列出要探索的可能性和创新点 + - 规划关联性分析的范围 + + 2. **挑战思维规划**: + - 识别需要质疑的假设和观点 + - 列出潜在风险和问题点 + - 规划批判性分析的角度 + + 3. **推理思维规划**: + - 确定需要验证的逻辑链条 + - 规划因果关系分析路径 + - 设计系统性推理结构 + + 4. **计划思维规划**: + - 明确需要设计的行动方案 + - 规划决策路径和组织结构 + - 确定系统架构的层次 + + ### Phase 3: DPML结构实现 + + **关键要求:文件必须从``标签直接开始** + + ```xml + + + # 探索思维:发散性思考 + [跳跃思考、生成可能性、寻找创新点和关联性] + + + + # 挑战思维:批判性思考 + [逆向思考、质疑假设、识别风险和问题点] + + + + # 推理思维:系统性思考 + [连续推理、验证逻辑、分析因果关系] + + + + # 计划思维:结构性思考 + [设计方案、决策路径、组织架构] + + + ``` + + **推荐思考顺序示例:** + ```xml + + + + ## 可能性探索 + - 方向A:... + - 方向B:... + + ## 关联性分析 + ```mermaid + mindmap + root)问题核心( + 分支1 + 分支2 + 分支3 + ``` + + + + + ## 假设质疑 + - 对方向A的质疑:... + - 对方向B的质疑:... + + ## 风险识别 + - 潜在风险1:... + - 潜在风险2:... + + + + + ## 逻辑验证 + ```mermaid + flowchart TD + A[前提] --> B[推理] + B --> C[结论] + ``` + + + + + ## 行动方案 + 1. 步骤一:... + 2. 步骤二:... + + + ``` + + ### Phase 4: 思维质量检查 + 1. **思维模式验证**:确保每个子标签体现正确的思维特征 + 2. **逻辑一致性检查**:验证不同思维模式间的逻辑关系 + 3. **思考完整性审核**:确认思考覆盖问题的关键维度 + 4. **可视化检查**:验证Mermaid图表语法正确性和表达清晰性 + 5. **纯净性检查**:确认文件从``标签开始,无多余内容 + + + + ## 质量评价标准 + + ### 格式合规性 + - ✅ 文件从``标签直接开始,无额外内容 + - ✅ 使用正确的DPML thought标签结构 + - ✅ 子标签命名符合四种思维模式规范 + - ✅ XML语法正确,标签正确闭合 + - ✅ Markdown格式规范,Mermaid图表有效 + + ### 思维模式准确性 + - ✅ exploration体现发散性、跳跃性思考特征 + - ✅ challenge体现批判性、逆向思考特征 + - ✅ reasoning体现系统性、连续性推理特征 + - ✅ plan体现结构性、秩序性思考特征 + - ✅ 各思维模式语义边界清晰,不混淆 + + ### 思考质量 + - ✅ 思考内容具有实质性和深度 + - ✅ 逻辑关系清晰,推理链条完整 + - ✅ 覆盖问题的关键维度,无明显盲区 + - ✅ 思维过程系统化,层次分明 + - ✅ 结论或方案具有可操作性 + + ### 可视化效果 + - ✅ 恰当使用Mermaid图表表达复杂关系 + - ✅ 图表类型选择合适(mindmap, flowchart, graph等) + - ✅ 可视化内容与文字描述相互补充 + - ✅ 图表简洁清晰,易于理解 + + ### 文件纯净性 + - ✅ 文件结构完全符合DPML thought规范 + - ✅ 无任何XML结构外的多余内容 + - ✅ 体现thought文件的标准格式 + - ✅ 思维模式组合合理,符合实际需求 + + \ No newline at end of file diff --git a/prompt/core/nuwa/nuwa.role.md b/prompt/core/nuwa/nuwa.role.md new file mode 100644 index 0000000..7d289a5 --- /dev/null +++ b/prompt/core/nuwa/nuwa.role.md @@ -0,0 +1,19 @@ + + + @!thought://remember + @!thought://recall + @!thought://role-creation + + + + @!execution://role-generation + @!execution://role-authoring + @!execution://thought-authoring + @!execution://execution-authoring + @!execution://resource-authoring + + + + + + \ No newline at end of file diff --git a/prompt/core/thought/role-creation.thought.md b/prompt/core/thought/role-creation.thought.md new file mode 100644 index 0000000..976acf3 --- /dev/null +++ b/prompt/core/thought/role-creation.thought.md @@ -0,0 +1,63 @@ + + + ## 领域快速识别 + + ### 从用户描述中提取核心信息 + - **领域关键词**:用户提到的技术栈、职业、业务领域 + - **功能期望**:用户希望AI助手具备的核心能力 + - **应用场景**:主要的使用场景和工作环境 + + ### 领域标准化映射 + - **技术领域**:前端开发、后端开发、移动开发、数据分析等 + - **业务领域**:产品管理、市场营销、设计创意、运营管理等 + - **综合领域**:项目管理、技术架构、创业咨询、教育培训等 + + ### 快速能力框架识别 + - 该领域的核心技能需求 + - 该领域的典型工作流程 + - 该领域的专业知识体系 + + + + ## 基于ResourceManager的资源生成逻辑 + + ### 架构驱动的生成策略 + ``` + 用户描述 → 领域识别 → 资源规划 → 文件生成 → ResourceManager发现 + ``` + + ### 镜像结构思维模式 + - **结构一致性**:用户资源目录镜像系统`prompt/domain/`结构 + - **认知负载最小化**:与系统结构保持一致,降低学习成本 + - **资源聚合原则**:角色相关的所有文件统一管理在角色目录下 + + ### 三组件标准化填充策略 + - **Personality设计**: + - 基于领域的通用思维特征 + - 该领域专业人士的认知偏好 + - 高效协作的交互风格 + + - **Principle设计**: + - 该领域的标准工作流程 + - 通用的质量标准和最佳实践 + - 常见问题的处理原则 + + - **Knowledge设计**: + - 该领域的核心技能栈 + - 必备的专业知识体系 + - 常用工具和方法论 + + ### 文件组织优化思维 + - **目录结构规划**:`.promptx/resource/domain/{roleId}/` + - **扩展文件支持**:thought/、execution/子目录按需创建 + - **引用关系设计**:优先使用@!引用机制,实现模块化 + - **发现机制适配**:确保ResourceManager能正确发现和加载 + + ### 质量保证机制 + - 确保三组件逻辑一致 + - 验证角色定位清晰准确 + - 保证实用性和可操作性 + - 符合DPML协议规范 + - 满足ResourceManager发现要求 + + \ No newline at end of file diff --git a/prompt/domain/frontend-developer/execution/wechat-miniprogram-development.execution.md b/prompt/domain/frontend-developer/execution/wechat-miniprogram-development.execution.md new file mode 100644 index 0000000..efd1ceb --- /dev/null +++ b/prompt/domain/frontend-developer/execution/wechat-miniprogram-development.execution.md @@ -0,0 +1,244 @@ + + + ## 微信平台技术约束 + - **包大小限制**:主包不超过2MB,总包不超过20MB + - **代码包限制**:分包不超过2MB,最多使用20个分包 + - **API调用限制**:网络请求并发限制,部分API有调用频次限制 + - **性能要求**:页面渲染时间不超过2秒,交互响应时间不超过300ms + - **平台兼容性**:需兼容微信不同版本和iOS/Android双平台 + - **审核规范**:必须遵守微信小程序平台规则和内容规范 + - **开发工具依赖**:必须使用微信开发者工具进行开发和调试 + + + + ## 强制性开发规则 + - **代码规范强制**:必须遵循微信小程序开发规范和ESLint配置 + - **文件结构固定**:页面必须包含.js/.wxml/.wxss/.json四个文件 + - **生命周期规范**:严格按照小程序生命周期进行开发,避免内存泄漏 + - **API使用规范**:必须进行用户授权检查,合规使用敏感API + - **安全要求**:敏感数据必须加密传输,用户隐私信息严格保护 + - **性能底线**:setData调用频次控制,避免频繁的数据更新 + - **审核合规**:代码和内容必须符合微信审核要求,避免违规操作 + + + + ## 开发最佳实践指导 + - **组件化优先**:优先封装可复用组件,提高开发效率和代码质量 + - **性能优化导向**:关注首屏加载时间、内存使用、用户体验流畅度 + - **用户体验优先**:遵循微信设计规范,保持界面一致性和易用性 + - **渐进增强**:优雅降级处理,确保在低版本微信中基本功能可用 + - **错误处理友好**:提供清晰的错误提示和用户引导 + - **测试覆盖全面**:真机测试、兼容性测试、性能测试并重 + - **版本管理规范**:使用语义化版本控制,合理规划版本发布 + + + + ## 微信小程序开发执行流程 + + ### Phase 1: 项目规划与准备 + ``` + 需求分析: + 1. 明确功能需求和用户场景 + 2. 分析技术可行性和平台限制 + 3. 确定MVP功能范围和迭代计划 + 4. 评估开发周期和资源需求 + + 技术选型: + 1. 框架选择:原生/uni-app/Taro/WePY + 2. UI组件库:WeUI/Vant Weapp/ColorUI + 3. 状态管理:原生/MobX/Redux + 4. 后端服务:传统后端/云开发 + + 项目初始化: + 1. 创建小程序项目,配置基础信息 + 2. 搭建项目目录结构 + 3. 配置开发环境和构建工具 + 4. 设置代码规范和Git工作流 + ``` + + ### Phase 2: 架构设计与环境配置 + ``` + 目录结构设计: + ├── pages/ # 页面文件 + │ ├── index/ # 首页 + │ └── detail/ # 详情页 + ├── components/ # 公共组件 + ├── utils/ # 工具函数 + ├── api/ # 接口封装 + ├── store/ # 状态管理 + ├── styles/ # 公共样式 + ├── static/ # 静态资源 + └── app.js/wxss/json # 全局配置 + + 架构设计原则: + 1. 模块化:功能模块独立,便于维护 + 2. 组件化:UI组件可复用,提高效率 + 3. 服务化:API接口统一封装管理 + 4. 配置化:可配置参数集中管理 + ``` + + ### Phase 3: UI开发与组件封装 + ``` + 页面开发流程: + 1. 分析设计稿,拆解页面结构 + 2. 使用WXML构建页面骨架 + 3. 使用WXSS实现样式效果 + 4. 处理响应式布局和适配 + + 组件开发策略: + 1. 识别可复用的UI模块 + 2. 封装自定义组件 + 3. 定义组件属性和事件 + 4. 编写组件文档和使用示例 + + 样式开发规范: + 1. 使用rpx单位适配不同屏幕 + 2. 遵循BEM命名规范 + 3. 使用CSS变量管理主题色彩 + 4. 优化CSS性能,避免复杂选择器 + ``` + + ### Phase 4: 功能开发与API集成 + ``` + 业务逻辑开发: + 1. 实现页面交互逻辑 + 2. 处理用户输入和表单验证 + 3. 实现路由跳转和参数传递 + 4. 处理页面状态管理 + + API集成开发: + 1. 封装网络请求模块 + 2. 实现接口调用和数据处理 + 3. 添加请求拦截器和错误处理 + 4. 实现数据缓存和同步策略 + + 数据管理: + 1. 设计数据流向和状态管理 + 2. 实现本地存储和缓存策略 + 3. 处理数据同步和更新 + 4. 优化setData性能 + ``` + + ### Phase 5: 性能优化与调试 + ``` + 性能优化策略: + 1. 代码分包:合理拆分主包和分包 + 2. 懒加载:按需加载页面和组件 + 3. 图片优化:压缩图片,使用WebP格式 + 4. 缓存策略:合理使用缓存减少请求 + + 调试与测试: + 1. 开发者工具调试 + 2. 真机预览和调试 + 3. 性能分析和优化 + 4. 兼容性测试 + + 代码质量保证: + 1. ESLint代码检查 + 2. 单元测试编写 + 3. 代码审查 + 4. 性能监控 + ``` + + ### Phase 6: 测试发布与上线 + ``` + 测试阶段: + 1. 功能测试:验证所有功能正常 + 2. 兼容性测试:测试不同设备和版本 + 3. 性能测试:检查加载速度和流畅度 + 4. 安全测试:验证数据安全和权限控制 + + 发布准备: + 1. 准备小程序基础信息和资料 + 2. 配置服务器域名和业务域名 + 3. 设置用户隐私保护指引 + 4. 准备审核说明和测试账号 + + 审核发布: + 1. 提交代码审核 + 2. 响应审核反馈 + 3. 发布正式版本 + 4. 监控线上表现 + ``` + + ### Phase 7: 运营维护与迭代 + ``` + 上线监控: + 1. 监控小程序性能指标 + 2. 收集用户反馈和错误日志 + 3. 分析用户行为数据 + 4. 优化用户体验 + + 迭代优化: + 1. 根据数据分析优化功能 + 2. 修复发现的Bug + 3. 新功能开发和上线 + 4. 持续性能优化 + + 版本管理: + 1. 制定版本发布计划 + 2. 管理版本兼容性 + 3. 处理版本回滚 + 4. 维护发布文档 + ``` + + + + ## 质量评价标准 + + ### 代码质量指标 + - ✅ **规范性检查**:通过ESLint检查,无语法错误 + - ✅ **结构清晰**:目录结构合理,文件命名规范 + - ✅ **组件化程度**:公共组件复用率≥60% + - ✅ **代码注释**:关键业务逻辑注释覆盖率≥80% + - ✅ **函数复杂度**:单个函数行数不超过50行 + + ### 性能质量指标 + - ✅ **首屏加载**:首屏渲染时间≤2秒 + - ✅ **包大小控制**:主包大小≤1.5MB,分包合理使用 + - ✅ **内存使用**:页面内存占用≤50MB + - ✅ **网络请求**:接口响应时间≤1秒 + - ✅ **用户体验**:页面切换流畅,无明显卡顿 + + ### 功能质量指标 + - ✅ **功能完整性**:核心功能100%实现 + - ✅ **交互友好性**:操作响应及时,反馈明确 + - ✅ **兼容性**:支持微信6.6.3以上版本 + - ✅ **错误处理**:异常情况有友好提示 + - ✅ **权限管理**:合规申请和使用用户权限 + + ### 安全合规指标 + - ✅ **数据安全**:敏感数据加密传输和存储 + - ✅ **隐私保护**:用户隐私信息保护到位 + - ✅ **内容合规**:内容符合平台规范 + - ✅ **API合规**:API使用符合官方要求 + - ✅ **审核通过**:能够顺利通过微信审核 + + ### 用户体验指标 + - ✅ **界面美观**:UI设计符合微信规范 + - ✅ **操作便捷**:用户操作路径简洁明了 + - ✅ **信息架构**:信息层次清晰,导航明确 + - ✅ **反馈及时**:操作反馈及时准确 + - ✅ **错误容错**:用户误操作有友好处理 + + ## 持续改进标准 + + ### 技术债务管理 + - 定期代码重构,消除技术债务 + - 升级依赖库版本,保持技术栈新鲜度 + - 优化陈旧代码,提高维护效率 + - 完善文档,降低维护成本 + + ### 性能持续优化 + - 建立性能监控体系 + - 定期性能分析和优化 + - 关注新技术和最佳实践 + - 优化用户体验细节 + + ### 团队协作效率 + - 完善开发规范和流程 + - 建立代码审查机制 + - 提升自动化程度 + - 知识分享和技能提升 + + \ No newline at end of file diff --git a/scripts/start-mcp.sh b/scripts/start-mcp.sh new file mode 100644 index 0000000..8c4b562 --- /dev/null +++ b/scripts/start-mcp.sh @@ -0,0 +1 @@ +cd /Users/sean/WorkSpaces/DeepracticeProjects/PromptX && pnpm start mcp-server \ No newline at end of file diff --git a/src/lib/core/pouch/commands/ActionCommand.js b/src/lib/core/pouch/commands/ActionCommand.js index 0799c44..f5a0955 100644 --- a/src/lib/core/pouch/commands/ActionCommand.js +++ b/src/lib/core/pouch/commands/ActionCommand.js @@ -85,10 +85,16 @@ ${COMMANDS.HELLO} */ async analyzeRoleDependencies (roleInfo) { try { - // 处理文件路径,将@package://前缀替换为实际路径 + // 处理文件路径,将@package://和@project://前缀替换为实际路径 let filePath = roleInfo.file if (filePath.startsWith('@package://')) { filePath = filePath.replace('@package://', '') + } else if (filePath.startsWith('@project://')) { + // 对于@project://路径,使用当前工作目录作为基础路径 + const ProjectProtocol = require('../../resource/protocols/ProjectProtocol') + const projectProtocol = new ProjectProtocol() + const relativePath = filePath.replace('@project://', '') + filePath = path.join(process.cwd(), relativePath) } // 读取角色文件内容 diff --git a/src/lib/core/pouch/commands/HelloCommand.js b/src/lib/core/pouch/commands/HelloCommand.js index 373ea77..1ae217e 100644 --- a/src/lib/core/pouch/commands/HelloCommand.js +++ b/src/lib/core/pouch/commands/HelloCommand.js @@ -26,23 +26,25 @@ class HelloCommand extends BasePouchCommand { } try { - // 从ResourceManager获取统一注册表 + // 使用新的ResourceManager架构 const ResourceManager = require('../../resource/resourceManager') const resourceManager = new ResourceManager() - await resourceManager.initialize() // 确保初始化完成 - - let registeredRoles = {} - if (resourceManager.registry && resourceManager.registry.protocols && resourceManager.registry.protocols.role && resourceManager.registry.protocols.role.registry) { - registeredRoles = resourceManager.registry.protocols.role.registry - } - - // 动态发现本地角色并合并 - const discoveredRoles = await this.discoverLocalRoles() - // 合并注册表中的角色和动态发现的角色 - this.roleRegistry = { - ...registeredRoles, - ...discoveredRoles + // 加载统一注册表(包含系统+用户资源) + const unifiedRegistry = await resourceManager.loadUnifiedRegistry() + + // 提取角色数据 + const roleData = unifiedRegistry.role || {} + + // 转换为HelloCommand期望的格式 + this.roleRegistry = {} + for (const [roleId, roleInfo] of Object.entries(roleData)) { + this.roleRegistry[roleId] = { + file: roleInfo.file, + name: roleInfo.name || roleId, + description: this.extractDescription(roleInfo) || `${roleInfo.name || roleId}专业角色`, + source: roleInfo.source || 'unknown' + } } // 如果没有任何角色,使用基础角色 @@ -51,31 +53,21 @@ class HelloCommand extends BasePouchCommand { assistant: { file: '@package://prompt/domain/assistant/assistant.role.md', name: '🙋 智能助手', - description: '通用助理角色,提供基础的助理服务和记忆支持' + description: '通用助理角色,提供基础的助理服务和记忆支持', + source: 'fallback' } } } } catch (error) { - console.warn('角色注册表加载失败,尝试动态发现:', error.message) + 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: '通用助理角色,提供基础的助理服务和记忆支持' - } + // 使用基础角色作为fallback + this.roleRegistry = { + assistant: { + file: '@package://prompt/domain/assistant/assistant.role.md', + name: '🙋 智能助手', + description: '通用助理角色,提供基础的助理服务和记忆支持', + source: 'fallback' } } } @@ -83,6 +75,21 @@ class HelloCommand extends BasePouchCommand { return this.roleRegistry } + /** + * 从角色信息中提取描述 + * @param {Object} roleInfo - 角色信息对象 + * @returns {string} 角色描述 + */ + extractDescription(roleInfo) { + // 尝试从不同字段提取描述 + if (roleInfo.description) { + return roleInfo.description + } + + // 如果有更多元数据,可以在这里扩展提取逻辑 + return null + } + /** * 获取所有角色列表(转换为数组格式) */ @@ -92,10 +99,29 @@ class HelloCommand extends BasePouchCommand { id, name: roleInfo.name, description: roleInfo.description, - file: roleInfo.file + file: roleInfo.file, + source: roleInfo.source })) } + /** + * 获取来源标签 + * @param {string} source - 资源来源 + * @returns {string} 来源标签 + */ + getSourceLabel(source) { + switch (source) { + case 'user-generated': + return '(用户生成)' + case 'system': + return '(系统角色)' + case 'fallback': + return '(默认角色)' + default: + return '' + } + } + async getContent (args) { await this.loadRoleRegistry() const allRoles = await this.getAllRoles() @@ -111,7 +137,8 @@ class HelloCommand extends BasePouchCommand { // 清楚显示角色ID和激活命令 allRoles.forEach((role, index) => { - content += `### ${index + 1}. ${role.name} + const sourceLabel = this.getSourceLabel(role.source) + content += `### ${index + 1}. ${role.name} ${sourceLabel} **角色ID**: \`${role.id}\` **专业能力**: ${role.description} **激活命令**: \`${buildCommand.action(role.id)}\` diff --git a/src/lib/core/resource/resourceManager.js b/src/lib/core/resource/resourceManager.js index d883917..8f2b0c5 100644 --- a/src/lib/core/resource/resourceManager.js +++ b/src/lib/core/resource/resourceManager.js @@ -11,6 +11,16 @@ const ProjectProtocol = require('./protocols/ProjectProtocol') const UserProtocol = require('./protocols/UserProtocol') const PromptProtocol = require('./protocols/PromptProtocol') +// 常量定义 +const USER_RESOURCE_DIR = '.promptx' +const RESOURCE_DOMAIN_PATH = ['resource', 'domain'] +const SUPPORTED_RESOURCE_TYPES = ['role', 'thought', 'execution'] +const DPML_TAGS = { + role: { start: '', end: '' }, + thought: { start: '', end: '' }, + execution: { start: '', end: '' } +} + /** * 资源管理器 - 统一管理各种协议的资源加载 */ @@ -41,17 +51,95 @@ class ResourceManager { } /** - * 加载统一资源注册表 + * 加载统一资源注册表(合并系统和用户资源) */ async loadUnifiedRegistry () { - const registryPath = path.resolve(__dirname, '../../../resource.registry.json') + try { + // 加载系统资源注册表 + const registryPath = path.resolve(__dirname, '../../../resource.registry.json') - if (!await fs.pathExists(registryPath)) { - throw new Error(`统一资源注册表文件不存在: ${registryPath}`) + if (!await fs.pathExists(registryPath)) { + throw new Error(`统一资源注册表文件不存在: ${registryPath}`) + } + + const systemRegistry = await fs.readJSON(registryPath) + + // 发现用户资源 + const userResources = await this.discoverUserResources() + + // 从系统注册表中提取资源数据 + const extractedSystemResources = {} + for (const resourceType of SUPPORTED_RESOURCE_TYPES) { + const protocolConfig = systemRegistry.protocols[resourceType] + if (protocolConfig && protocolConfig.registry) { + extractedSystemResources[resourceType] = protocolConfig.registry + } + } + + // 合并资源,用户资源覆盖系统资源 + const mergedRegistry = { ...systemRegistry } + + // 合并各种资源类型 + for (const resourceType of SUPPORTED_RESOURCE_TYPES) { + // 确保有基础结构 + if (!mergedRegistry[resourceType]) { + mergedRegistry[resourceType] = {} + } + + // 先添加系统资源 + if (extractedSystemResources[resourceType]) { + if (!mergedRegistry[resourceType]) mergedRegistry[resourceType] = {} + for (const [id, resourceInfo] of Object.entries(extractedSystemResources[resourceType])) { + mergedRegistry[resourceType][id] = { + ...resourceInfo, + source: 'system' + } + } + } + + // 再添加用户资源(覆盖同名的系统资源) + if (userResources[resourceType]) { + for (const [id, resourceInfo] of Object.entries(userResources[resourceType])) { + let filePath = resourceInfo.file || resourceInfo + + // 将绝对路径转换为@project://相对路径格式 + if (path.isAbsolute(filePath)) { + // 简单的路径转换:去掉项目根目录前缀 + const projectRoot = process.cwd() + if (filePath.startsWith(projectRoot)) { + const relativePath = path.relative(projectRoot, filePath) + filePath = `@project://${relativePath}` + } + } + + // 对于role资源类型,需要保持对象格式以包含name和description + if (resourceType === 'role') { + mergedRegistry[resourceType][id] = { + file: filePath, + name: resourceInfo.name || id, + description: resourceInfo.description || `${resourceInfo.name || id}专业角色`, + source: 'user-generated', + format: resourceInfo.format, + type: resourceInfo.type + } + } else { + // 对于thought和execution,协议处理器期望的是文件路径字符串 + if (!mergedRegistry[resourceType]) mergedRegistry[resourceType] = {} + mergedRegistry[resourceType][id] = filePath + } + } + } + } + + this.registry = mergedRegistry + return mergedRegistry + } catch (error) { + // 如果加载失败,至少返回一个基本结构 + logger.warn(`加载统一注册表失败: ${error.message}`) + const fallbackRegistry = { role: {} } + this.registry = fallbackRegistry + return fallbackRegistry } - - const registryContent = await fs.readJSON(registryPath) - this.registry = registryContent } /** @@ -204,6 +292,178 @@ class ResourceManager { return null } + + /** + * 发现用户资源 + * @returns {Promise} 用户资源注册表 + */ + async discoverUserResources() { + try { + const PackageProtocol = require('./protocols/PackageProtocol') + const packageProtocol = new PackageProtocol() + const packageRoot = await packageProtocol.getPackageRoot() + + const userResourcePath = path.join(packageRoot, USER_RESOURCE_DIR, ...RESOURCE_DOMAIN_PATH) + + // 检查用户资源目录是否存在 + if (!await fs.pathExists(userResourcePath)) { + return {} + } + + return await this.scanResourceDirectory(userResourcePath) + } catch (error) { + // 出错时返回空对象,不抛出异常 + logger.warn(`用户资源发现失败: ${error.message}`) + return {} + } + } + + /** + * 扫描资源目录 + * @param {string} basePath - 基础路径 + * @returns {Promise} 发现的资源 + */ + async scanResourceDirectory(basePath) { + const resources = {} + + try { + const directories = await fs.readdir(basePath) + + for (const roleDir of directories) { + const rolePath = path.join(basePath, roleDir) + + try { + const stat = await fs.stat(rolePath) + + if (stat.isDirectory()) { + // 扫描角色文件 + await this.scanRoleResources(rolePath, roleDir, resources) + + // 扫描其他资源类型(thought, execution) + await this.scanOtherResources(rolePath, roleDir, resources) + } + } catch (dirError) { + // 跳过无法访问的目录 + logger.debug(`跳过目录 ${roleDir}: ${dirError.message}`) + } + } + } catch (error) { + logger.warn(`扫描资源目录失败 ${basePath}: ${error.message}`) + } + + return resources + } + + /** + * 扫描角色资源 + * @param {string} rolePath - 角色目录路径 + * @param {string} roleId - 角色ID + * @param {Object} resources - 资源容器 + */ + async scanRoleResources(rolePath, roleId, resources) { + const roleFile = path.join(rolePath, `${roleId}.role.md`) + + if (await fs.pathExists(roleFile)) { + try { + const content = await fs.readFile(roleFile, 'utf8') + + // 验证DPML格式 + if (this.validateDPMLFormat(content, 'role')) { + const name = this.extractRoleName(content) + + if (!resources.role) resources.role = {} + resources.role[roleId] = { + file: roleFile, + name: name || roleId, + source: 'user-generated', + format: 'dpml', + type: 'role' + } + } + } catch (error) { + // 忽略单个文件的错误 + } + } + } + + /** + * 扫描其他资源类型 + * @param {string} rolePath - 角色目录路径 + * @param {string} roleId - 角色ID + * @param {Object} resources - 资源容器 + */ + async scanOtherResources(rolePath, roleId, resources) { + for (const resourceType of SUPPORTED_RESOURCE_TYPES.filter(type => type !== 'role')) { + const resourceDir = path.join(rolePath, resourceType) + + if (await fs.pathExists(resourceDir)) { + try { + const files = await fs.readdir(resourceDir) + + for (const file of files) { + if (file.endsWith(`.${resourceType}.md`)) { + const resourceName = file.replace(`.${resourceType}.md`, '') + const filePath = path.join(resourceDir, file) + const content = await fs.readFile(filePath, 'utf8') + + if (this.validateDPMLFormat(content, resourceType)) { + if (!resources[resourceType]) resources[resourceType] = {} + resources[resourceType][resourceName] = { + file: filePath, + name: resourceName, + source: 'user-generated', + format: 'dpml', + type: resourceType + } + } + } + } + } catch (error) { + logger.debug(`扫描${resourceType}资源失败: ${error.message}`) + } + } + } + } + + /** + * 验证DPML格式 + * @param {string} content - 文件内容 + * @param {string} type - 资源类型 + * @returns {boolean} 是否为有效格式 + */ + validateDPMLFormat(content, type) { + const tags = DPML_TAGS[type] + if (!tags) { + return false + } + + return content.includes(tags.start) && content.includes(tags.end) + } + + /** + * 从角色内容中提取名称 + * @param {string} content - 角色文件内容 + * @returns {string} 角色名称 + */ + extractRoleName(content) { + // 简单的名称提取逻辑 + const match = content.match(/#\s*([^\n]+)/) + return match ? match[1].trim() : null + } + + /** + * 加载系统资源注册表(兼容现有方法) + * @returns {Promise} 系统资源注册表 + */ + async loadSystemRegistry() { + const registryPath = path.resolve(__dirname, '../../../resource.registry.json') + + if (!await fs.pathExists(registryPath)) { + throw new Error(`统一资源注册表文件不存在: ${registryPath}`) + } + + return await fs.readJSON(registryPath) + } } module.exports = ResourceManager diff --git a/src/resource.registry.json b/src/resource.registry.json index 186bb01..12f07c8 100644 --- a/src/resource.registry.json +++ b/src/resource.registry.json @@ -13,6 +13,7 @@ "assistant": "@package://prompt/domain/assistant/thought/assistant.thought.md", "remember": "@package://prompt/core/thought/remember.thought.md", "recall": "@package://prompt/core/thought/recall.thought.md", + "role-creation": "@package://prompt/core/thought/role-creation.thought.md", "product-manager": "@package://prompt/domain/product-manager/thought/product-manager.thought.md", "java-backend-developer": "@package://prompt/domain/java-backend-developer/thought/java-backend-developer.thought.md" } @@ -33,7 +34,14 @@ "system-architecture": "@package://prompt/domain/java-backend-developer/execution/system-architecture.execution.md", "spring-ecosystem": "@package://prompt/domain/java-backend-developer/execution/spring-ecosystem.execution.md", "code-quality": "@package://prompt/domain/java-backend-developer/execution/code-quality.execution.md", - "database-design": "@package://prompt/domain/java-backend-developer/execution/database-design.execution.md" + "database-design": "@package://prompt/domain/java-backend-developer/execution/database-design.execution.md", + "role-generation": "@package://prompt/core/execution/role-generation.execution.md", + "execution-authoring": "@package://prompt/core/execution/execution-authoring.execution.md", + "thought-authoring": "@package://prompt/core/execution/thought-authoring.execution.md", + "role-authoring": "@package://prompt/core/execution/role-authoring.execution.md", + "resource-authoring": "@package://prompt/core/execution/resource-authoring.execution.md", + + "wechat-miniprogram-development": "@package://prompt/domain/frontend-developer/execution/wechat-miniprogram-development.execution.md" } }, "memory": { @@ -78,6 +86,11 @@ "name": "☕ Java后端开发者", "description": "专业Java后端开发专家,精通Spring生态系统、微服务架构和系统设计" }, + "nuwa": { + "file": "@package://prompt/core/nuwa/nuwa.role.md", + "name": "🎨 女娲", + "description": "专业角色创造顾问,通过对话收集需求,为用户量身定制AI助手角色" + }, "test-role": { "file": "@package://prompt/domain/test-role/test-role.role.md", "name": "🧪 测试角色", diff --git a/src/tests/commands/CrossPlatformDiscovery.unit.test.js b/src/tests/commands/CrossPlatformDiscovery.unit.test.js new file mode 100644 index 0000000..a436c10 --- /dev/null +++ b/src/tests/commands/CrossPlatformDiscovery.unit.test.js @@ -0,0 +1,221 @@ +const path = require('path') +const fs = require('fs-extra') +const os = require('os') + +describe('跨平台角色发现兼容性测试', () => { + let tempDir + let projectDir + + beforeEach(async () => { + tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'cross-platform-test-')) + projectDir = path.join(tempDir, 'test-project') + + await fs.ensureDir(path.join(projectDir, 'prompt', 'domain')) + await fs.ensureDir(path.join(projectDir, '.promptx', 'user-roles')) + }) + + afterEach(async () => { + if (tempDir) { + await fs.remove(tempDir) + } + }) + + describe('Node.js 原生 API 替代 glob', () => { + test('应该能使用 fs.readdir 代替 glob.sync', async () => { + // 创建测试角色文件 + const roleDir = path.join(projectDir, 'prompt', 'domain', 'test-role') + await fs.ensureDir(roleDir) + await fs.writeFile( + path.join(roleDir, 'test-role.role.md'), + '测试' + ) + + // 使用Node.js原生API实现角色发现(替代glob) + async function discoverRolesWithNativeAPI(scanPath) { + const discoveredRoles = {} + + try { + if (await fs.pathExists(scanPath)) { + const domains = await fs.readdir(scanPath) + + for (const domain of domains) { + const domainDir = path.join(scanPath, domain) + const stat = await fs.stat(domainDir) + + if (stat.isDirectory()) { + const roleFile = path.join(domainDir, `${domain}.role.md`) + if (await fs.pathExists(roleFile)) { + const content = await fs.readFile(roleFile, 'utf-8') + + discoveredRoles[domain] = { + file: roleFile, + name: `🎭 ${domain}`, + description: '原生API发现的角色', + source: 'native-api' + } + } + } + } + } + + return discoveredRoles + } catch (error) { + console.warn('原生API角色发现失败:', error.message) + return {} + } + } + + const domainPath = path.join(projectDir, 'prompt', 'domain') + const discoveredRoles = await discoverRolesWithNativeAPI(domainPath) + + expect(discoveredRoles).toHaveProperty('test-role') + expect(discoveredRoles['test-role'].source).toBe('native-api') + }) + + test('应该能处理不同平台的路径分隔符', () => { + const unixPath = 'prompt/domain/role/role.role.md' + const windowsPath = 'prompt\\domain\\role\\role.role.md' + + // 使用path.join确保跨平台兼容性 + const normalizedPath = path.join('prompt', 'domain', 'role', 'role.role.md') + + // 在当前平台上验证路径处理 + if (process.platform === 'win32') { + expect(normalizedPath).toContain('\\') + } else { + expect(normalizedPath).toContain('/') + } + + // path.relative应该也能正常工作 + const relativePath = path.relative(projectDir, path.join(projectDir, normalizedPath)) + expect(relativePath).toBe(normalizedPath) + }) + + test('应该处理路径中的特殊字符', async () => { + // 创建包含特殊字符的角色名(但符合文件系统要求) + const specialRoleName = 'role-with_special.chars' + const roleDir = path.join(projectDir, 'prompt', 'domain', specialRoleName) + await fs.ensureDir(roleDir) + + const roleFile = path.join(roleDir, `${specialRoleName}.role.md`) + await fs.writeFile(roleFile, '特殊角色') + + // 验证能正确处理特殊字符的文件名 + expect(await fs.pathExists(roleFile)).toBe(true) + + const content = await fs.readFile(roleFile, 'utf-8') + expect(content).toContain('特殊角色') + }) + }) + + describe('文件系统权限处理', () => { + test('应该优雅处理无权限访问的目录', async () => { + if (process.platform === 'win32') { + // Windows权限测试较为复杂,跳过 + expect(true).toBe(true) + return + } + + const restrictedDir = path.join(projectDir, 'restricted') + await fs.ensureDir(restrictedDir) + + // 移除读权限 + await fs.chmod(restrictedDir, 0o000) + + // 角色发现应该不会因为权限问题而崩溃 + async function safeDiscoverRoles(scanPath) { + try { + if (await fs.pathExists(scanPath)) { + const domains = await fs.readdir(scanPath) + return domains + } + return [] + } catch (error) { + // 应该优雅处理权限错误 + console.warn('权限不足,跳过目录:', scanPath) + return [] + } + } + + const result = await safeDiscoverRoles(restrictedDir) + expect(Array.isArray(result)).toBe(true) + + // 恢复权限以便清理 + await fs.chmod(restrictedDir, 0o755) + }) + }) + + describe('错误恢复机制', () => { + test('应该在部分文件失败时继续处理其他文件', async () => { + // 创建多个角色,其中一个有问题 + const goodRoleDir = path.join(projectDir, 'prompt', 'domain', 'good-role') + await fs.ensureDir(goodRoleDir) + await fs.writeFile( + path.join(goodRoleDir, 'good-role.role.md'), + '正常角色' + ) + + const badRoleDir = path.join(projectDir, 'prompt', 'domain', 'bad-role') + await fs.ensureDir(badRoleDir) + await fs.writeFile( + path.join(badRoleDir, 'bad-role.role.md'), + '无效内容' + ) + + // 模拟容错的角色发现实现 + async function resilientDiscoverRoles(scanPath) { + const discoveredRoles = {} + const errors = [] + + try { + if (await fs.pathExists(scanPath)) { + const domains = await fs.readdir(scanPath) + + for (const domain of domains) { + try { + const domainDir = path.join(scanPath, domain) + const stat = await fs.stat(domainDir) + + if (stat.isDirectory()) { + const roleFile = path.join(domainDir, `${domain}.role.md`) + if (await fs.pathExists(roleFile)) { + const content = await fs.readFile(roleFile, 'utf-8') + + // 简单验证内容 + if (content.includes('')) { + discoveredRoles[domain] = { + file: roleFile, + name: `🎭 ${domain}`, + description: '容错发现的角色', + source: 'resilient-discovery' + } + } else { + throw new Error('无效的角色文件格式') + } + } + } + } catch (error) { + // 记录错误但继续处理其他文件 + errors.push({ domain, error: error.message }) + console.warn(`跳过无效角色 ${domain}:`, error.message) + } + } + } + } catch (error) { + console.warn('角色发现过程中出错:', error.message) + } + + return { discoveredRoles, errors } + } + + const domainPath = path.join(projectDir, 'prompt', 'domain') + const result = await resilientDiscoverRoles(domainPath) + + // 应该发现正常角色,跳过问题角色 + expect(result.discoveredRoles).toHaveProperty('good-role') + expect(result.discoveredRoles).not.toHaveProperty('bad-role') + expect(result.errors).toHaveLength(1) + expect(result.errors[0].domain).toBe('bad-role') + }) + }) +}) \ No newline at end of file diff --git a/src/tests/commands/HelloCommand.integration.test.js b/src/tests/commands/HelloCommand.integration.test.js new file mode 100644 index 0000000..6db167e --- /dev/null +++ b/src/tests/commands/HelloCommand.integration.test.js @@ -0,0 +1,231 @@ +const HelloCommand = require('../../lib/core/pouch/commands/HelloCommand') +const ResourceManager = require('../../lib/core/resource/resourceManager') +const fs = require('fs-extra') +const path = require('path') +const os = require('os') + +describe('HelloCommand - ResourceManager集成', () => { + let helloCommand + let tempDir + let mockPackageRoot + + beforeEach(async () => { + // 创建临时测试目录 + tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'promptx-hello-test-')) + mockPackageRoot = tempDir + + // 模拟用户资源目录结构 + await fs.ensureDir(path.join(tempDir, '.promptx', 'resource', 'domain')) + + helloCommand = new HelloCommand() + }) + + afterEach(async () => { + // 清理临时目录 + await fs.remove(tempDir) + jest.restoreAllMocks() + }) + + describe('用户角色发现集成', () => { + it('应该显示用户创建的角色', async () => { + // 创建测试用户角色 + const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'sales-expert') + await fs.ensureDir(roleDir) + + const roleContent = ` + + # 销售专家思维模式 + ## 核心特征 + - **客户导向思维**:始终以客户需求为出发点 + + + + # 销售专家行为原则 + ## 核心原则 + - **诚信为本**:建立长期客户关系 + + + + # 销售专业知识体系 + ## 销售技巧 + - **需求挖掘**:深度了解客户真实需求 + +` + + await fs.writeFile(path.join(roleDir, 'sales-expert.role.md'), roleContent) + + // Mock ResourceManager的loadUnifiedRegistry方法 + jest.spyOn(ResourceManager.prototype, 'loadUnifiedRegistry') + .mockResolvedValue({ + role: { + 'assistant': { + file: '@package://prompt/domain/assistant/assistant.role.md', + name: '🙋 智能助手', + source: 'system', + format: 'dpml', + type: 'role' + }, + 'sales-expert': { + file: path.join(roleDir, 'sales-expert.role.md'), + name: '销售专家思维模式', + source: 'user-generated', + format: 'dpml', + type: 'role' + } + } + }) + + // 模拟执行hello命令 + const result = await helloCommand.execute([]) + + // 验证用户角色在输出中显示 + const allOutput = result.content || '' + + expect(allOutput).toContain('sales-expert') + expect(allOutput).toContain('销售专家') + expect(allOutput).toContain('(用户生成)') + }) + + it('应该允许用户角色覆盖系统角色', async () => { + // 创建与系统角色同名的用户角色 + const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'assistant') + await fs.ensureDir(roleDir) + + const customAssistantContent = ` + + # 定制智能助手 + ## 个性化特征 + - **专业导向**:专注于技术问题解决 + + + + # 定制助手原则 + ## 核心原则 + - **精准回答**:提供准确的技术解决方案 + + + + # 定制助手知识体系 + ## 技术领域 + - **编程语言**:多种编程语言的深度理解 + +` + + await fs.writeFile(path.join(roleDir, 'assistant.role.md'), customAssistantContent) + + // Mock ResourceManager返回用户覆盖的角色 + jest.spyOn(ResourceManager.prototype, 'loadUnifiedRegistry') + .mockResolvedValue({ + role: { + 'assistant': { + file: path.join(roleDir, 'assistant.role.md'), + name: '定制智能助手', + source: 'user-generated', + format: 'dpml', + type: 'role' + } + } + }) + + const result = await helloCommand.execute([]) + + const allOutput = result.content || '' + + // 验证显示的是用户版本 + expect(allOutput).toContain('定制智能助手') + expect(allOutput).toContain('(用户生成)') + expect(allOutput).not.toContain('🙋 智能助手') + }) + + it('应该同时显示系统角色和用户角色', async () => { + // 创建用户角色 + const userRoleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'data-analyst') + await fs.ensureDir(userRoleDir) + + const userRoleContent = ` + + # 数据分析师 + ## 分析思维 + - **逻辑思维**:系统性分析数据模式 + + + + # 分析原则 + ## 核心原则 + - **数据驱动**:基于数据做决策 + + + + # 分析知识 + ## 统计学 + - **描述统计**:数据的基本特征分析 + +` + + await fs.writeFile(path.join(userRoleDir, 'data-analyst.role.md'), userRoleContent) + + // Mock ResourceManager返回系统和用户角色 + jest.spyOn(ResourceManager.prototype, 'loadUnifiedRegistry') + .mockResolvedValue({ + role: { + 'assistant': { + file: '@package://prompt/domain/assistant/assistant.role.md', + name: '🙋 智能助手', + source: 'system', + format: 'dpml', + type: 'role' + }, + 'java-backend-developer': { + file: '@package://prompt/domain/java-backend-developer/java-backend-developer.role.md', + name: '☕ Java后端开发专家', + source: 'system', + format: 'dpml', + type: 'role' + }, + 'data-analyst': { + file: path.join(userRoleDir, 'data-analyst.role.md'), + name: '数据分析师', + source: 'user-generated', + format: 'dpml', + type: 'role' + } + } + }) + + const result = await helloCommand.execute([]) + + const allOutput = result.content || '' + + // 验证系统角色和用户角色都显示 + expect(allOutput).toContain('智能助手') + expect(allOutput).toContain('Java后端开发专家') + expect(allOutput).toContain('数据分析师') + expect(allOutput).toContain('data-analyst') + }) + }) + + describe('错误处理', () => { + it('应该优雅处理资源发现失败', async () => { + // 模拟ResourceManager错误 + jest.spyOn(ResourceManager.prototype, 'loadUnifiedRegistry') + .mockRejectedValue(new Error('资源发现失败')) + + // 应该不抛出异常 + const result = await helloCommand.execute([]) + + // 应该显示基础角色(fallback) + expect(result.content).toContain('智能助手') + }) + + it('应该处理空的资源注册表', async () => { + // Mock空的资源注册表 + jest.spyOn(ResourceManager.prototype, 'loadUnifiedRegistry') + .mockResolvedValue({ role: {} }) + + const result = await helloCommand.execute([]) + + // 应该显示基础角色(fallback) + expect(result.content).toContain('智能助手') + }) + }) +}) \ No newline at end of file diff --git a/src/tests/commands/HelloCommand.unit.test.js b/src/tests/commands/HelloCommand.unit.test.js new file mode 100644 index 0000000..ca28163 --- /dev/null +++ b/src/tests/commands/HelloCommand.unit.test.js @@ -0,0 +1,323 @@ +const path = require('path') +const fs = require('fs-extra') +const os = require('os') +const HelloCommand = require('../../lib/core/pouch/commands/HelloCommand') + +describe('HelloCommand 单元测试', () => { + let helloCommand + let tempDir + let tempProjectDir + + beforeEach(async () => { + helloCommand = new HelloCommand() + + // 创建临时目录模拟项目结构 + tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'hello-command-test-')) + tempProjectDir = path.join(tempDir, 'test-project') + + // 创建基础目录结构 + await fs.ensureDir(path.join(tempProjectDir, 'prompt', 'domain')) + await fs.ensureDir(path.join(tempProjectDir, '.promptx', 'user-roles')) + }) + + afterEach(async () => { + if (tempDir) { + await fs.remove(tempDir) + } + // 清理缓存 + if (helloCommand.roleRegistry) { + helloCommand.roleRegistry = null + } + }) + + describe('基础功能测试', () => { + test('应该能实例化HelloCommand', () => { + expect(helloCommand).toBeInstanceOf(HelloCommand) + expect(typeof helloCommand.discoverLocalRoles).toBe('function') + expect(typeof helloCommand.loadRoleRegistry).toBe('function') + }) + + test('getPurpose应该返回正确的目的描述', () => { + const purpose = helloCommand.getPurpose() + expect(purpose).toContain('AI') + expect(purpose).toContain('角色') + }) + }) + + describe('discoverLocalRoles 功能测试', () => { + test('应该能发现系统内置角色', async () => { + // 创建模拟的系统角色文件 + const assistantDir = path.join(tempProjectDir, 'prompt', 'domain', 'assistant') + await fs.ensureDir(assistantDir) + + const roleFileContent = ` + + + + @!thought://remember + @!thought://recall + @!thought://assistant + + + + @!execution://assistant + +` + + await fs.writeFile( + path.join(assistantDir, 'assistant.role.md'), + roleFileContent + ) + + // Mock PackageProtocol.getPackageRoot 返回临时目录 + const originalRequire = require + jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => { + return class MockPackageProtocol { + async getPackageRoot() { + return tempProjectDir + } + } + }) + + // 重新加载HelloCommand使用mock + delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')] + const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand') + const mockedCommand = new MockedHelloCommand() + + const discoveredRoles = await mockedCommand.discoverLocalRoles() + + expect(discoveredRoles).toHaveProperty('assistant') + expect(discoveredRoles.assistant.name).toContain('智能助手') + expect(discoveredRoles.assistant.description).toContain('通用助理角色') + expect(discoveredRoles.assistant.source).toBe('local-discovery') + + // 恢复原始require + jest.unmock('../../lib/core/resource/protocols/PackageProtocol') + }) + + test('应该处理空的角色目录', async () => { + // Mock PackageProtocol.getPackageRoot 返回空目录 + jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => { + return class MockPackageProtocol { + async getPackageRoot() { + return tempProjectDir + } + } + }) + + delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')] + const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand') + const mockedCommand = new MockedHelloCommand() + + const discoveredRoles = await mockedCommand.discoverLocalRoles() + expect(discoveredRoles).toEqual({}) + + jest.unmock('../../lib/core/resource/protocols/PackageProtocol') + }) + + test('应该优雅处理文件读取错误', async () => { + // 创建无效的角色文件(权限问题) + const invalidRoleDir = path.join(tempProjectDir, 'prompt', 'domain', 'invalid') + await fs.ensureDir(invalidRoleDir) + + const invalidRoleFile = path.join(invalidRoleDir, 'invalid.role.md') + await fs.writeFile(invalidRoleFile, 'invalid content') + + // 修改文件权限使其不可读(仅在Unix系统上有效) + if (process.platform !== 'win32') { + await fs.chmod(invalidRoleFile, 0o000) + } + + jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => { + return class MockPackageProtocol { + async getPackageRoot() { + return tempProjectDir + } + } + }) + + delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')] + const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand') + const mockedCommand = new MockedHelloCommand() + + // 应该不抛出异常,而是记录警告并跳过无效文件 + const discoveredRoles = await mockedCommand.discoverLocalRoles() + expect(typeof discoveredRoles).toBe('object') + + // 恢复文件权限 + if (process.platform !== 'win32') { + await fs.chmod(invalidRoleFile, 0o644) + } + + jest.unmock('../../lib/core/resource/protocols/PackageProtocol') + }) + }) + + describe('元数据提取测试', () => { + test('应该正确提取角色名称和描述', async () => { + const testRoleDir = path.join(tempProjectDir, 'prompt', 'domain', 'test-role') + await fs.ensureDir(testRoleDir) + + const roleContent = ` + + + + 测试思维模式 + + + + 测试行为原则 + +` + + await fs.writeFile( + path.join(testRoleDir, 'test-role.role.md'), + roleContent + ) + + jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => { + return class MockPackageProtocol { + async getPackageRoot() { + return tempProjectDir + } + } + }) + + delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')] + const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand') + const mockedCommand = new MockedHelloCommand() + + const discoveredRoles = await mockedCommand.discoverLocalRoles() + + expect(discoveredRoles).toHaveProperty('test-role') + expect(discoveredRoles['test-role'].name).toBe('🧪 测试角色') + expect(discoveredRoles['test-role'].description).toBe('这是一个测试用的角色') + + jest.unmock('../../lib/core/resource/protocols/PackageProtocol') + }) + + test('应该处理缺少元数据的角色文件', async () => { + const testRoleDir = path.join(tempProjectDir, 'prompt', 'domain', 'no-meta') + await fs.ensureDir(testRoleDir) + + const roleContent = ` + + 基础角色内容 + +` + + await fs.writeFile( + path.join(testRoleDir, 'no-meta.role.md'), + roleContent + ) + + jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => { + return class MockPackageProtocol { + async getPackageRoot() { + return tempProjectDir + } + } + }) + + delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')] + const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand') + const mockedCommand = new MockedHelloCommand() + + const discoveredRoles = await mockedCommand.discoverLocalRoles() + + expect(discoveredRoles).toHaveProperty('no-meta') + expect(discoveredRoles['no-meta'].name).toBe('🎭 no-meta') // 默认格式 + expect(discoveredRoles['no-meta'].description).toBe('本地发现的角色') // 默认描述 + + jest.unmock('../../lib/core/resource/protocols/PackageProtocol') + }) + }) + + describe('角色注册表加载测试', () => { + test('应该能加载角色注册表', async () => { + const result = await helloCommand.loadRoleRegistry() + + expect(typeof result).toBe('object') + expect(helloCommand.roleRegistry).toBe(result) + }) + + test('应该在失败时返回默认assistant角色', async () => { + // Mock ResourceManager抛出异常 + jest.doMock('../../lib/core/resource/resourceManager', () => { + return class MockResourceManager { + async initialize() { + throw new Error('Mock initialization failure') + } + } + }) + + // Mock discoverLocalRoles也失败 + jest.spyOn(helloCommand, 'discoverLocalRoles').mockRejectedValue(new Error('Mock discovery failure')) + + delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')] + const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand') + const mockedCommand = new MockedHelloCommand() + + const result = await mockedCommand.loadRoleRegistry() + + expect(result).toHaveProperty('assistant') + expect(result.assistant.name).toContain('智能助手') + + jest.unmock('../../lib/core/resource/resourceManager') + helloCommand.discoverLocalRoles.mockRestore() + }) + }) + + describe('角色信息获取测试', () => { + test('getRoleInfo应该返回正确的角色信息', async () => { + // Mock注册表 + helloCommand.roleRegistry = { + 'test-role': { + name: '测试角色', + description: '测试描述', + file: '@package://test/path' + } + } + + const roleInfo = await helloCommand.getRoleInfo('test-role') + + expect(roleInfo).toEqual({ + id: 'test-role', + name: '测试角色', + description: '测试描述', + file: '@package://test/path' + }) + }) + + test('getRoleInfo对不存在的角色应该返回null', async () => { + helloCommand.roleRegistry = {} + + const roleInfo = await helloCommand.getRoleInfo('non-existent') + expect(roleInfo).toBeNull() + }) + }) + + describe('getAllRoles测试', () => { + test('应该返回角色数组格式', async () => { + helloCommand.roleRegistry = { + 'role1': { name: '角色1', description: '描述1', file: 'file1' }, + 'role2': { name: '角色2', description: '描述2', file: 'file2' } + } + + const allRoles = await helloCommand.getAllRoles() + + expect(Array.isArray(allRoles)).toBe(true) + expect(allRoles).toHaveLength(2) + expect(allRoles[0]).toHaveProperty('id') + expect(allRoles[0]).toHaveProperty('name') + expect(allRoles[0]).toHaveProperty('description') + expect(allRoles[0]).toHaveProperty('file') + }) + }) +}) \ No newline at end of file diff --git a/src/tests/commands/UserRoleDiscovery.integration.test.js b/src/tests/commands/UserRoleDiscovery.integration.test.js new file mode 100644 index 0000000..1df145b --- /dev/null +++ b/src/tests/commands/UserRoleDiscovery.integration.test.js @@ -0,0 +1,525 @@ +const path = require('path') +const fs = require('fs-extra') +const os = require('os') +const HelloCommand = require('../../lib/core/pouch/commands/HelloCommand') + +describe('用户角色发现机制 集成测试', () => { + let tempDir + let projectDir + let helloCommand + + beforeEach(async () => { + // 创建临时项目目录 + tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'user-role-discovery-')) + projectDir = path.join(tempDir, 'test-project') + + // 创建完整的项目结构 + await fs.ensureDir(path.join(projectDir, 'prompt', 'domain')) + await fs.ensureDir(path.join(projectDir, '.promptx', 'user-roles')) + + helloCommand = new HelloCommand() + }) + + afterEach(async () => { + if (tempDir) { + await fs.remove(tempDir) + } + if (helloCommand.roleRegistry) { + helloCommand.roleRegistry = null + } + }) + + describe('用户角色路径扫描', () => { + test('应该能扫描 .promptx/user-roles 目录', async () => { + // 创建用户自定义角色 + const userRoleDir = path.join(projectDir, '.promptx', 'user-roles', 'custom-analyst') + await fs.ensureDir(userRoleDir) + + const userRoleContent = ` + + + + # 数据分析思维 + 我是一个专注于数据洞察的分析师,善于从复杂数据中发现业务价值。 + + + + # 分析原则 + - 数据驱动决策 + - 业务价值导向 + - 简洁清晰表达 + + + + # 专业技能 + - 统计分析方法 + - 数据可视化技能 + - 业务理解能力 + +` + + await fs.writeFile( + path.join(userRoleDir, 'custom-analyst.role.md'), + userRoleContent + ) + + // 这个测试假设我们已经实现了用户角色发现功能 + // 实际实现时,discoverLocalRoles会被扩展以支持用户角色路径 + + // 验证文件创建成功 + expect(await fs.pathExists(path.join(userRoleDir, 'custom-analyst.role.md'))).toBe(true) + }) + + test('应该同时支持系统角色和用户角色', async () => { + // 创建系统角色 + const systemRoleDir = path.join(projectDir, 'prompt', 'domain', 'assistant') + await fs.ensureDir(systemRoleDir) + + await fs.writeFile( + path.join(systemRoleDir, 'assistant.role.md'), + ` + + + 系统助手思维 +` + ) + + // 创建用户角色 + const userRoleDir = path.join(projectDir, '.promptx', 'user-roles', 'my-role') + await fs.ensureDir(userRoleDir) + + await fs.writeFile( + path.join(userRoleDir, 'my-role.role.md'), + ` + + + 用户自定义思维 +` + ) + + jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => { + return class MockPackageProtocol { + async getPackageRoot() { + return projectDir + } + } + }) + + delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')] + const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand') + const mockedCommand = new MockedHelloCommand() + + // 模拟双路径扫描实现 + mockedCommand.discoverLocalRoles = async function() { + const PackageProtocol = require('../../lib/core/resource/protocols/PackageProtocol') + const packageProtocol = new PackageProtocol() + const glob = require('glob') + const discoveredRoles = {} + + try { + const packageRoot = await packageProtocol.getPackageRoot() + + // 扫描路径:系统角色 + 用户角色 + const scanPaths = [ + path.join(packageRoot, 'prompt', 'domain'), // 系统角色 + path.join(packageRoot, '.promptx', 'user-roles') // 用户角色 + ] + + for (const scanPath of scanPaths) { + if (await fs.pathExists(scanPath)) { + const domains = await fs.readdir(scanPath) + + for (const domain of domains) { + const domainDir = path.join(scanPath, domain) + const stat = await fs.stat(domainDir) + + if (stat.isDirectory()) { + const roleFile = path.join(domainDir, `${domain}.role.md`) + if (await fs.pathExists(roleFile)) { + const content = await fs.readFile(roleFile, 'utf-8') + const relativePath = path.relative(packageRoot, roleFile) + + let name = `🎭 ${domain}` + let description = '本地发现的角色' + let source = 'local-discovery' + + // 区分系统角色和用户角色 + if (scanPath.includes('.promptx')) { + source = 'user-generated' + 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() + } + + discoveredRoles[domain] = { + file: scanPath.includes('.promptx') + ? `@project://${relativePath}` + : `@package://${relativePath}`, + name, + description, + source + } + } + } + } + } + } + + return discoveredRoles + } catch (error) { + console.warn('角色发现失败:', error.message) + return {} + } + } + + const discoveredRoles = await mockedCommand.discoverLocalRoles() + + // 验证同时发现了系统角色和用户角色 + expect(discoveredRoles).toHaveProperty('assistant') + expect(discoveredRoles).toHaveProperty('my-role') + + expect(discoveredRoles.assistant.source).toBe('local-discovery') + expect(discoveredRoles.assistant.file).toContain('@package://') + + expect(discoveredRoles['my-role'].source).toBe('user-generated') + expect(discoveredRoles['my-role'].file).toContain('@project://') + + jest.unmock('../../lib/core/resource/protocols/PackageProtocol') + }) + }) + + describe('DPML格式元数据提取', () => { + test('应该能从DPML格式中提取元数据', async () => { + const userRoleDir = path.join(projectDir, '.promptx', 'user-roles', 'dpml-role') + await fs.ensureDir(userRoleDir) + + // DPML格式的角色文件(根据文档设计的格式) + const dpmlRoleContent = ` + + # 数据分析师思维模式 + + ## 核心思维特征 + - **数据敏感性思维**:善于从数字中发现故事和趋势模式 + - **逻辑分析思维**:系统性地分解复杂数据问题,追求因果关系 + - **结果导向思维**:专注于为业务决策提供可行洞察和建议 + + + + # 数据分析师行为原则 + + ## 核心工作原则 + - **数据驱动决策**:所有分析建议必须有可靠数据支撑 + - **简洁清晰表达**:复杂分析结果要用简单易懂的方式呈现 + - **业务价值优先**:分析要紧密围绕业务目标和价值创造 + + + + # 数据分析专业知识体系 + + ## 数据处理技能 + - **数据清洗方法**:缺失值处理、异常值识别、数据标准化 + - **数据整合技巧**:多源数据合并、关联分析、数据建模 + - **质量控制流程**:数据校验、一致性检查、完整性验证 + + ## 分析方法论 + - **描述性分析**:趋势分析、对比分析、分布分析 + - **诊断性分析**:钻取分析、根因分析、相关性分析 + +` + + await fs.writeFile( + path.join(userRoleDir, 'dpml-role.role.md'), + dpmlRoleContent + ) + + jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => { + return class MockPackageProtocol { + async getPackageRoot() { + return projectDir + } + } + }) + + delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')] + const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand') + const mockedCommand = new MockedHelloCommand() + + // 实现DPML元数据提取逻辑(这是我们要实现的功能) + function extractDPMLMetadata(content, roleId) { + // 从标签中提取角色名称 + const personalityMatch = content.match(/]*>([\s\S]*?)<\/personality>/i) + const roleNameFromPersonality = personalityMatch + ? personalityMatch[1].split('\n')[0].replace(/^#\s*/, '').trim() + : null + + // 从标签中提取专业能力描述 + const knowledgeMatch = content.match(/]*>([\s\S]*?)<\/knowledge>/i) + const roleDescription = knowledgeMatch + ? knowledgeMatch[1].split('\n').slice(0, 3).join(' ').replace(/[#\-\*]/g, '').trim() + : null + + return { + file: `@project://.promptx/user-roles/${roleId}/${roleId}.role.md`, + name: roleNameFromPersonality || `🎭 ${roleId}`, + description: roleDescription || '用户自定义DPML角色', + source: 'user-generated', + format: 'dpml' + } + } + + mockedCommand.discoverLocalRoles = async function() { + const PackageProtocol = require('../../lib/core/resource/protocols/PackageProtocol') + const packageProtocol = new PackageProtocol() + const discoveredRoles = {} + + try { + const packageRoot = await packageProtocol.getPackageRoot() + const userRolesPath = path.join(packageRoot, '.promptx', 'user-roles') + + if (await fs.pathExists(userRolesPath)) { + const userRoleDirs = await fs.readdir(userRolesPath) + + for (const roleId of userRoleDirs) { + const roleDir = path.join(userRolesPath, roleId) + const stat = await fs.stat(roleDir) + + if (stat.isDirectory()) { + const roleFile = path.join(roleDir, `${roleId}.role.md`) + if (await fs.pathExists(roleFile)) { + const content = await fs.readFile(roleFile, 'utf-8') + + // 使用DPML元数据提取 + const roleInfo = extractDPMLMetadata(content, roleId) + discoveredRoles[roleId] = roleInfo + } + } + } + } + + return discoveredRoles + } catch (error) { + console.warn('DPML角色发现失败:', error.message) + return {} + } + } + + const discoveredRoles = await mockedCommand.discoverLocalRoles() + + // 验证DPML元数据提取 + expect(discoveredRoles).toHaveProperty('dpml-role') + expect(discoveredRoles['dpml-role'].name).toBe('数据分析师思维模式') + expect(discoveredRoles['dpml-role'].description).toContain('数据分析专业知识体系') + expect(discoveredRoles['dpml-role'].format).toBe('dpml') + expect(discoveredRoles['dpml-role'].source).toBe('user-generated') + + jest.unmock('../../lib/core/resource/protocols/PackageProtocol') + }) + }) + + describe('错误处理和边界情况', () => { + test('应该处理不存在的用户角色目录', async () => { + // 只创建系统角色目录,不创建用户角色目录 + const systemRoleDir = path.join(projectDir, 'prompt', 'domain', 'assistant') + await fs.ensureDir(systemRoleDir) + + await fs.writeFile( + path.join(systemRoleDir, 'assistant.role.md'), + `助手` + ) + + jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => { + return class MockPackageProtocol { + async getPackageRoot() { + return projectDir + } + } + }) + + delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')] + const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand') + const mockedCommand = new MockedHelloCommand() + + // 模拟处理不存在目录的逻辑 + mockedCommand.discoverLocalRoles = async function() { + const PackageProtocol = require('../../lib/core/resource/protocols/PackageProtocol') + const packageProtocol = new PackageProtocol() + const discoveredRoles = {} + + try { + const packageRoot = await packageProtocol.getPackageRoot() + + const scanPaths = [ + { path: path.join(packageRoot, 'prompt', 'domain'), prefix: '@package://' }, + { path: path.join(packageRoot, '.promptx', 'user-roles'), prefix: '@project://' } + ] + + for (const { path: scanPath, prefix } of scanPaths) { + if (await fs.pathExists(scanPath)) { + const domains = await fs.readdir(scanPath) + + for (const domain of domains) { + const domainDir = path.join(scanPath, domain) + const stat = await fs.stat(domainDir) + + if (stat.isDirectory()) { + const roleFile = path.join(domainDir, `${domain}.role.md`) + if (await fs.pathExists(roleFile)) { + const content = await fs.readFile(roleFile, 'utf-8') + const relativePath = path.relative(packageRoot, roleFile) + + discoveredRoles[domain] = { + file: `${prefix}${relativePath}`, + name: `🎭 ${domain}`, + description: '本地发现的角色', + source: prefix.includes('project') ? 'user-generated' : 'local-discovery' + } + } + } + } + } + } + + return discoveredRoles + } catch (error) { + return {} + } + } + + const discoveredRoles = await mockedCommand.discoverLocalRoles() + + // 应该只发现系统角色,不会因为用户角色目录不存在而出错 + expect(discoveredRoles).toHaveProperty('assistant') + expect(Object.keys(discoveredRoles)).toHaveLength(1) + + jest.unmock('../../lib/core/resource/protocols/PackageProtocol') + }) + + test('应该处理用户角色ID冲突', async () => { + // 创建同名的系统角色和用户角色 + const systemRoleDir = path.join(projectDir, 'prompt', 'domain', 'analyst') + await fs.ensureDir(systemRoleDir) + + await fs.writeFile( + path.join(systemRoleDir, 'analyst.role.md'), + ` +系统分析师` + ) + + const userRoleDir = path.join(projectDir, '.promptx', 'user-roles', 'analyst') + await fs.ensureDir(userRoleDir) + + await fs.writeFile( + path.join(userRoleDir, 'analyst.role.md'), + ` +用户分析师` + ) + + jest.doMock('../../lib/core/resource/protocols/PackageProtocol', () => { + return class MockPackageProtocol { + async getPackageRoot() { + return projectDir + } + } + }) + + delete require.cache[require.resolve('../../lib/core/pouch/commands/HelloCommand')] + const MockedHelloCommand = require('../../lib/core/pouch/commands/HelloCommand') + const mockedCommand = new MockedHelloCommand() + + // 模拟冲突处理逻辑(用户角色优先) + mockedCommand.discoverLocalRoles = async function() { + const PackageProtocol = require('../../lib/core/resource/protocols/PackageProtocol') + const packageProtocol = new PackageProtocol() + const discoveredRoles = {} + + try { + const packageRoot = await packageProtocol.getPackageRoot() + + // 先扫描系统角色,再扫描用户角色(用户角色会覆盖同名系统角色) + const scanPaths = [ + { path: path.join(packageRoot, 'prompt', 'domain'), prefix: '@package://', source: 'local-discovery' }, + { path: path.join(packageRoot, '.promptx', 'user-roles'), prefix: '@project://', source: 'user-generated' } + ] + + for (const { path: scanPath, prefix, source } of scanPaths) { + if (await fs.pathExists(scanPath)) { + const domains = await fs.readdir(scanPath) + + for (const domain of domains) { + const domainDir = path.join(scanPath, domain) + const stat = await fs.stat(domainDir) + + if (stat.isDirectory()) { + const roleFile = path.join(domainDir, `${domain}.role.md`) + if (await fs.pathExists(roleFile)) { + const content = await fs.readFile(roleFile, 'utf-8') + const relativePath = path.relative(packageRoot, roleFile) + + let name = `🎭 ${domain}` + 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() + } + + // 用户角色会覆盖系统角色 + discoveredRoles[domain] = { + file: `${prefix}${relativePath}`, + name, + description, + source + } + } + } + } + } + } + + return discoveredRoles + } catch (error) { + return {} + } + } + + const discoveredRoles = await mockedCommand.discoverLocalRoles() + + // 验证用户角色优先级更高 + expect(discoveredRoles).toHaveProperty('analyst') + expect(discoveredRoles.analyst.name).toBe('👤 用户分析师') + expect(discoveredRoles.analyst.description).toBe('用户自定义分析师') + expect(discoveredRoles.analyst.source).toBe('user-generated') + expect(discoveredRoles.analyst.file).toContain('@project://') + + jest.unmock('../../lib/core/resource/protocols/PackageProtocol') + }) + }) +}) \ No newline at end of file diff --git a/src/tests/core/resource/ResourceManager.unit.test.js b/src/tests/core/resource/ResourceManager.unit.test.js new file mode 100644 index 0000000..b3562a3 --- /dev/null +++ b/src/tests/core/resource/ResourceManager.unit.test.js @@ -0,0 +1,220 @@ +const ResourceManager = require('../../../lib/core/resource/resourceManager') +const fs = require('fs-extra') +const path = require('path') +const os = require('os') + +describe('ResourceManager - 用户资源发现', () => { + let resourceManager + let tempDir + let mockPackageRoot + + beforeEach(async () => { + // 创建临时测试目录 + tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'promptx-test-')) + mockPackageRoot = tempDir + + // 模拟用户资源目录结构 + await fs.ensureDir(path.join(tempDir, '.promptx', 'resource', 'domain')) + + resourceManager = new ResourceManager() + + // Mock packageProtocol module + jest.doMock('../../../lib/core/resource/protocols/PackageProtocol', () => { + return class MockPackageProtocol { + async getPackageRoot() { + return mockPackageRoot + } + } + }) + }) + + afterEach(async () => { + // 清理临时目录 + await fs.remove(tempDir) + jest.restoreAllMocks() + }) + + describe('discoverUserResources', () => { + it('应该返回空对象当用户资源目录不存在时', async () => { + // 删除用户资源目录 + await fs.remove(path.join(tempDir, '.promptx')) + + const result = await resourceManager.discoverUserResources() + + expect(result).toEqual({}) + }) + + it('应该发现用户创建的角色文件', async () => { + // 创建测试角色文件 + const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'test-sales-analyst') + await fs.ensureDir(roleDir) + + const roleContent = ` + + # 销售数据分析师思维模式 + ## 核心思维特征 + - **数据敏感性思维**:善于从数字中发现故事和趋势模式 + + + + # 销售数据分析师行为原则 + ## 核心工作原则 + - **数据驱动决策**:所有分析建议必须有可靠数据支撑 + + + + # 销售数据分析专业知识体系 + ## 数据处理技能 + - **数据清洗方法**:缺失值处理、异常值识别 + +` + + await fs.writeFile(path.join(roleDir, 'test-sales-analyst.role.md'), roleContent) + + const result = await resourceManager.discoverUserResources() + + expect(result).toHaveProperty('role') + expect(result.role).toHaveProperty('test-sales-analyst') + expect(result.role['test-sales-analyst']).toMatchObject({ + file: expect.stringContaining('test-sales-analyst.role.md'), + name: expect.stringContaining('销售数据分析师'), + source: 'user-generated', + format: 'dpml', + type: 'role' + }) + }) + + it('应该支持多种资源类型发现', async () => { + // 创建角色和相关资源 + const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'test-role') + await fs.ensureDir(roleDir) + await fs.ensureDir(path.join(roleDir, 'thought')) + await fs.ensureDir(path.join(roleDir, 'execution')) + + // 创建角色文件 + await fs.writeFile(path.join(roleDir, 'test-role.role.md'), 'TestTestTest') + + // 创建思维文件 + await fs.writeFile(path.join(roleDir, 'thought', 'test.thought.md'), 'Test explorationTest reasoning') + + // 创建执行文件 + await fs.writeFile(path.join(roleDir, 'execution', 'test.execution.md'), 'Test constraint') + + const result = await resourceManager.discoverUserResources() + + expect(result).toHaveProperty('role') + expect(result).toHaveProperty('thought') + expect(result).toHaveProperty('execution') + expect(result.role).toHaveProperty('test-role') + expect(result.thought).toHaveProperty('test') + expect(result.execution).toHaveProperty('test') + }) + + it('应该处理DPML格式错误的文件', async () => { + // 创建格式错误的角色文件 + const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'invalid-role') + await fs.ensureDir(roleDir) + + const invalidContent = `这不是有效的DPML格式` + await fs.writeFile(path.join(roleDir, 'invalid-role.role.md'), invalidContent) + + const result = await resourceManager.discoverUserResources() + + // 应该跳过格式错误的文件,但不应该抛出错误 + expect(result.role || {}).not.toHaveProperty('invalid-role') + }) + + it('应该跨平台正确处理路径', async () => { + // 在不同平台上创建角色文件 + const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'cross-platform-role') + await fs.ensureDir(roleDir) + + const roleContent = 'TestTestTest' + await fs.writeFile(path.join(roleDir, 'cross-platform-role.role.md'), roleContent) + + const result = await resourceManager.discoverUserResources() + + expect(result.role).toHaveProperty('cross-platform-role') + + // 验证文件路径使用正确的分隔符 + const roleInfo = result.role['cross-platform-role'] + expect(roleInfo.file).toBe(path.normalize(roleInfo.file)) + }) + }) + + describe('loadUnifiedRegistry', () => { + it('应该合并系统资源和用户资源', async () => { + // 模拟系统资源 + const mockSystemResources = { + role: { + 'assistant': { + file: '@package://prompt/domain/assistant/assistant.role.md', + name: '🙋 智能助手', + source: 'system', + format: 'dpml', + type: 'role' + } + } + } + + // Mock fs.readJSON for system registry + jest.spyOn(fs, 'readJSON') + .mockImplementation((filePath) => { + if (filePath.includes('resource.registry.json')) { + return Promise.resolve(mockSystemResources) + } + return Promise.resolve({}) + }) + + // 创建用户资源 + const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'user-role') + await fs.ensureDir(roleDir) + await fs.writeFile( + path.join(roleDir, 'user-role.role.md'), + 'UserUserUser' + ) + + const result = await resourceManager.loadUnifiedRegistry() + + expect(result.role).toHaveProperty('assistant') // 系统资源 + expect(result.role).toHaveProperty('user-role') // 用户资源 + }) + + it('应该让用户资源覆盖同名系统资源', async () => { + // 模拟系统资源 + const mockSystemResources = { + role: { + 'assistant': { + file: '@package://prompt/domain/assistant/assistant.role.md', + name: '🙋 智能助手', + source: 'system', + format: 'dpml', + type: 'role' + } + } + } + + // Mock fs.readJSON for system registry + jest.spyOn(fs, 'readJSON') + .mockImplementation((filePath) => { + if (filePath.includes('resource.registry.json')) { + return Promise.resolve(mockSystemResources) + } + return Promise.resolve({}) + }) + + // 创建同名的用户资源 + const roleDir = path.join(tempDir, '.promptx', 'resource', 'domain', 'assistant') + await fs.ensureDir(roleDir) + await fs.writeFile( + path.join(roleDir, 'assistant.role.md'), + '# 自定义助手\n用户定制的助手CustomCustom' + ) + + const result = await resourceManager.loadUnifiedRegistry() + + expect(result.role.assistant.source).toBe('user-generated') + expect(result.role.assistant.name).toContain('自定义助手') + }) + }) +}) \ No newline at end of file