Develop (#91)
* 重构ActionCommand和LearnCommand,更新DPMLContentParser和SemanticRenderer的导入路径,确保模块结构一致性。删除不再使用的DPMLContentParser和SemanticRenderer文件,优化代码结构,提升可维护性。 * 重构PromptX资源协议系统,采用极简两层协议架构,删除不必要的语义层,优化路径解析和资源加载流程。引入AI协作优化,支持直接生成完整协议路径,提升系统性能和用户体验。整体架构简化60%,实现零配置启动,显著降低内存占用和启动时间。 * optimize:优化女娲提示词 * Optimize:更新记忆策略文档,增加角色专业记忆的独特价值和工作流程,强调角色记忆与客户端记忆的差异,优化记忆引导话术和决策规则,以提升用户对专业记忆系统的理解和应用。 * feature:增加 Sean 角色 * optimize:优化记忆格式化逻辑,确保完整记忆内容不被截断,同时更新工具定义中的描述,增强用户对记忆回想器的理解和使用指导。 * feat: 添加DACP服务支持,允许通过命令行调用DACP专业服务,增强AI角色的执行能力,同时更新相关依赖和工具定义。 * feat: 在MCPServerCommand和MCPStreamableHttpCommand中添加'promptx_dacp'参数映射,同时在DACPCommand中优化参数处理逻辑,以支持数组参数的正确解析。 * feat: 更新DACP演示服务,重命名服务和描述,简化功能,删除不必要的日历和文档操作,增强演示效果。同时,优化了API接口和README文档,确保用户更易于理解和使用。 * feat: 添加DACP邮件发送功能,支持真实发送与Demo模式,增强邮件发送的配置管理和错误提示,优化用户体验。 * feat: 更新女娲和Sean角色文档,增强角色身份、核心特质和决策框架的描述,优化内容结构,提升用户理解和使用体验。同时,更新产品哲学知识体系,明确矛盾驱动和简洁性原则的应用。 * Add product management submodule * fix: 修复 recall 和 learn 的 bug * refactor: 把 hello 改成 welcome * feat: 添加DACP服务启动脚本和测试命令,更新相关依赖,优化配置文件路径处理 * fix: 更新pnpm-lock.yaml以匹配DACP依赖,解决CI中--frozen-lockfile的错误 * 更新DACP白皮书的更新日期至2025-01-19;在DACPConfigManager中优化配置管理,支持项目级和用户级配置的优先级处理,增强错误提示信息,更新相关方法以支持异步操作。 * Develop (#66) * 重构ActionCommand和LearnCommand,更新DPMLContentParser和SemanticRenderer的导入路径,确保模块结构一致性。删除不再使用的DPMLContentParser和SemanticRenderer文件,优化代码结构,提升可维护性。 * 重构PromptX资源协议系统,采用极简两层协议架构,删除不必要的语义层,优化路径解析和资源加载流程。引入AI协作优化,支持直接生成完整协议路径,提升系统性能和用户体验。整体架构简化60%,实现零配置启动,显著降低内存占用和启动时间。 * optimize:优化女娲提示词 * Optimize:更新记忆策略文档,增加角色专业记忆的独特价值和工作流程,强调角色记忆与客户端记忆的差异,优化记忆引导话术和决策规则,以提升用户对专业记忆系统的理解和应用。 * feature:增加 Sean 角色 * optimize:优化记忆格式化逻辑,确保完整记忆内容不被截断,同时更新工具定义中的描述,增强用户对记忆回想器的理解和使用指导。 * feat: 添加DACP服务支持,允许通过命令行调用DACP专业服务,增强AI角色的执行能力,同时更新相关依赖和工具定义。 * feat: 在MCPServerCommand和MCPStreamableHttpCommand中添加'promptx_dacp'参数映射,同时在DACPCommand中优化参数处理逻辑,以支持数组参数的正确解析。 * feat: 更新DACP演示服务,重命名服务和描述,简化功能,删除不必要的日历和文档操作,增强演示效果。同时,优化了API接口和README文档,确保用户更易于理解和使用。 * feat: 添加DACP邮件发送功能,支持真实发送与Demo模式,增强邮件发送的配置管理和错误提示,优化用户体验。 * feat: 更新女娲和Sean角色文档,增强角色身份、核心特质和决策框架的描述,优化内容结构,提升用户理解和使用体验。同时,更新产品哲学知识体系,明确矛盾驱动和简洁性原则的应用。 * Add product management submodule * fix: 修复 recall 和 learn 的 bug * refactor: 把 hello 改成 welcome * feat: 添加DACP服务启动脚本和测试命令,更新相关依赖,优化配置文件路径处理 * fix: 更新pnpm-lock.yaml以匹配DACP依赖,解决CI中--frozen-lockfile的错误 * 更新DACP白皮书的更新日期至2025-01-19;在DACPConfigManager中优化配置管理,支持项目级和用户级配置的优先级处理,增强错误提示信息,更新相关方法以支持异步操作。 * fix: 统一Pouch命令路径获取机制,解决Issue #69记忆持久化问题 修复多实例MCP环境下的路径不一致问题: - RememberCommand: 使用ResourceManager替代DirectoryService直接调用 - RecallCommand: 使用ResourceManager替代DirectoryService直接调用 - RegisterCommand: 使用ResourceManager+DirectoryService统一路径获取 核心改进: 1. 所有命令现在使用相同的getGlobalResourceManager()初始化 2. 通过resourceManager.initializeWithNewArchitecture()确保路径一致性 3. 实现"要对一起对,要错一起错"的一致性原则 测试验证: - 记忆写入和读取使用相同项目路径 - 多实例环境下路径解析行为完全一致 - 向后兼容,无破坏性变更 Fixes #69 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Develop (#70) * 重构ActionCommand和LearnCommand,更新DPMLContentParser和SemanticRenderer的导入路径,确保模块结构一致性。删除不再使用的DPMLContentParser和SemanticRenderer文件,优化代码结构,提升可维护性。 * 重构PromptX资源协议系统,采用极简两层协议架构,删除不必要的语义层,优化路径解析和资源加载流程。引入AI协作优化,支持直接生成完整协议路径,提升系统性能和用户体验。整体架构简化60%,实现零配置启动,显著降低内存占用和启动时间。 * optimize:优化女娲提示词 * Optimize:更新记忆策略文档,增加角色专业记忆的独特价值和工作流程,强调角色记忆与客户端记忆的差异,优化记忆引导话术和决策规则,以提升用户对专业记忆系统的理解和应用。 * feature:增加 Sean 角色 * optimize:优化记忆格式化逻辑,确保完整记忆内容不被截断,同时更新工具定义中的描述,增强用户对记忆回想器的理解和使用指导。 * feat: 添加DACP服务支持,允许通过命令行调用DACP专业服务,增强AI角色的执行能力,同时更新相关依赖和工具定义。 * feat: 在MCPServerCommand和MCPStreamableHttpCommand中添加'promptx_dacp'参数映射,同时在DACPCommand中优化参数处理逻辑,以支持数组参数的正确解析。 * feat: 更新DACP演示服务,重命名服务和描述,简化功能,删除不必要的日历和文档操作,增强演示效果。同时,优化了API接口和README文档,确保用户更易于理解和使用。 * feat: 添加DACP邮件发送功能,支持真实发送与Demo模式,增强邮件发送的配置管理和错误提示,优化用户体验。 * feat: 更新女娲和Sean角色文档,增强角色身份、核心特质和决策框架的描述,优化内容结构,提升用户理解和使用体验。同时,更新产品哲学知识体系,明确矛盾驱动和简洁性原则的应用。 * Add product management submodule * fix: 修复 recall 和 learn 的 bug * refactor: 把 hello 改成 welcome * feat: 添加DACP服务启动脚本和测试命令,更新相关依赖,优化配置文件路径处理 * fix: 更新pnpm-lock.yaml以匹配DACP依赖,解决CI中--frozen-lockfile的错误 * 更新DACP白皮书的更新日期至2025-01-19;在DACPConfigManager中优化配置管理,支持项目级和用户级配置的优先级处理,增强错误提示信息,更新相关方法以支持异步操作。 * fix: 统一Pouch命令路径获取机制,解决Issue #69记忆持久化问题 修复多实例MCP环境下的路径不一致问题: - RememberCommand: 使用ResourceManager替代DirectoryService直接调用 - RecallCommand: 使用ResourceManager替代DirectoryService直接调用 - RegisterCommand: 使用ResourceManager+DirectoryService统一路径获取 核心改进: 1. 所有命令现在使用相同的getGlobalResourceManager()初始化 2. 通过resourceManager.initializeWithNewArchitecture()确保路径一致性 3. 实现"要对一起对,要错一起错"的一致性原则 测试验证: - 记忆写入和读取使用相同项目路径 - 多实例环境下路径解析行为完全一致 - 向后兼容,无破坏性变更 Fixes #69 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * doc: 新增“女娲”使用心得 (#73) * feat:添加女娲的使用心得 refactor:readme最新版本同步到英文版 * docs: 添加社区教程与案例部分,包含基于PromptX架构的MCP工具开发实践经验 * Staging (#71) * Develop (#66) * 重构ActionCommand和LearnCommand,更新DPMLContentParser和SemanticRenderer的导入路径,确保模块结构一致性。删除不再使用的DPMLContentParser和SemanticRenderer文件,优化代码结构,提升可维护性。 * 重构PromptX资源协议系统,采用极简两层协议架构,删除不必要的语义层,优化路径解析和资源加载流程。引入AI协作优化,支持直接生成完整协议路径,提升系统性能和用户体验。整体架构简化60%,实现零配置启动,显著降低内存占用和启动时间。 * optimize:优化女娲提示词 * Optimize:更新记忆策略文档,增加角色专业记忆的独特价值和工作流程,强调角色记忆与客户端记忆的差异,优化记忆引导话术和决策规则,以提升用户对专业记忆系统的理解和应用。 * feature:增加 Sean 角色 * optimize:优化记忆格式化逻辑,确保完整记忆内容不被截断,同时更新工具定义中的描述,增强用户对记忆回想器的理解和使用指导。 * feat: 添加DACP服务支持,允许通过命令行调用DACP专业服务,增强AI角色的执行能力,同时更新相关依赖和工具定义。 * feat: 在MCPServerCommand和MCPStreamableHttpCommand中添加'promptx_dacp'参数映射,同时在DACPCommand中优化参数处理逻辑,以支持数组参数的正确解析。 * feat: 更新DACP演示服务,重命名服务和描述,简化功能,删除不必要的日历和文档操作,增强演示效果。同时,优化了API接口和README文档,确保用户更易于理解和使用。 * feat: 添加DACP邮件发送功能,支持真实发送与Demo模式,增强邮件发送的配置管理和错误提示,优化用户体验。 * feat: 更新女娲和Sean角色文档,增强角色身份、核心特质和决策框架的描述,优化内容结构,提升用户理解和使用体验。同时,更新产品哲学知识体系,明确矛盾驱动和简洁性原则的应用。 * Add product management submodule * fix: 修复 recall 和 learn 的 bug * refactor: 把 hello 改成 welcome * feat: 添加DACP服务启动脚本和测试命令,更新相关依赖,优化配置文件路径处理 * fix: 更新pnpm-lock.yaml以匹配DACP依赖,解决CI中--frozen-lockfile的错误 * 更新DACP白皮书的更新日期至2025-01-19;在DACPConfigManager中优化配置管理,支持项目级和用户级配置的优先级处理,增强错误提示信息,更新相关方法以支持异步操作。 * Develop (#70) * 重构ActionCommand和LearnCommand,更新DPMLContentParser和SemanticRenderer的导入路径,确保模块结构一致性。删除不再使用的DPMLContentParser和SemanticRenderer文件,优化代码结构,提升可维护性。 * 重构PromptX资源协议系统,采用极简两层协议架构,删除不必要的语义层,优化路径解析和资源加载流程。引入AI协作优化,支持直接生成完整协议路径,提升系统性能和用户体验。整体架构简化60%,实现零配置启动,显著降低内存占用和启动时间。 * optimize:优化女娲提示词 * Optimize:更新记忆策略文档,增加角色专业记忆的独特价值和工作流程,强调角色记忆与客户端记忆的差异,优化记忆引导话术和决策规则,以提升用户对专业记忆系统的理解和应用。 * feature:增加 Sean 角色 * optimize:优化记忆格式化逻辑,确保完整记忆内容不被截断,同时更新工具定义中的描述,增强用户对记忆回想器的理解和使用指导。 * feat: 添加DACP服务支持,允许通过命令行调用DACP专业服务,增强AI角色的执行能力,同时更新相关依赖和工具定义。 * feat: 在MCPServerCommand和MCPStreamableHttpCommand中添加'promptx_dacp'参数映射,同时在DACPCommand中优化参数处理逻辑,以支持数组参数的正确解析。 * feat: 更新DACP演示服务,重命名服务和描述,简化功能,删除不必要的日历和文档操作,增强演示效果。同时,优化了API接口和README文档,确保用户更易于理解和使用。 * feat: 添加DACP邮件发送功能,支持真实发送与Demo模式,增强邮件发送的配置管理和错误提示,优化用户体验。 * feat: 更新女娲和Sean角色文档,增强角色身份、核心特质和决策框架的描述,优化内容结构,提升用户理解和使用体验。同时,更新产品哲学知识体系,明确矛盾驱动和简洁性原则的应用。 * Add product management submodule * fix: 修复 recall 和 learn 的 bug * refactor: 把 hello 改成 welcome * feat: 添加DACP服务启动脚本和测试命令,更新相关依赖,优化配置文件路径处理 * fix: 更新pnpm-lock.yaml以匹配DACP依赖,解决CI中--frozen-lockfile的错误 * 更新DACP白皮书的更新日期至2025-01-19;在DACPConfigManager中优化配置管理,支持项目级和用户级配置的优先级处理,增强错误提示信息,更新相关方法以支持异步操作。 * fix: 统一Pouch命令路径获取机制,解决Issue #69记忆持久化问题 修复多实例MCP环境下的路径不一致问题: - RememberCommand: 使用ResourceManager替代DirectoryService直接调用 - RecallCommand: 使用ResourceManager替代DirectoryService直接调用 - RegisterCommand: 使用ResourceManager+DirectoryService统一路径获取 核心改进: 1. 所有命令现在使用相同的getGlobalResourceManager()初始化 2. 通过resourceManager.initializeWithNewArchitecture()确保路径一致性 3. 实现"要对一起对,要错一起错"的一致性原则 测试验证: - 记忆写入和读取使用相同项目路径 - 多实例环境下路径解析行为完全一致 - 向后兼容,无破坏性变更 Fixes #69 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * 更新README文件,替换女娲创造工坊的logo图片,添加社区教程与案例部分,展示基于PromptX架构的MCP工具开发经验,提升内容的可读性和用户体验。 * 更新README文件,优化“女娲”功能的描述,强调其无需编程知识即可使用的便利性,并介绍其创建“小红书营销”专家的能力,提升内容的清晰度和用户体验。 --------- Co-authored-by: Sean <sean@deepracticex.com> Co-authored-by: coso <wutongci@example.com> Co-authored-by: Claude <noreply@anthropic.com> * refactor: 优化DACP工具提示词,去除诱导性描述 - 将DACP工具描述从功能介绍改为使用条件 - 强调需要专业知识才能正确使用 - 避免AI在不了解服务配置时盲目尝试 - 符合"先学会使用工具再做事"的设计理念 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Optimize:优化女娲提示词,为 Sean 添加 Github Issues 矛盾分析方法论 * fix: 修复 这几个命令使用了废弃的项目路径定位方案 * Optimize: 优化 sean 提示词 * Optimize: 优化 sean 提示词 * 删除产品子项目文件 * 🚀 feat: 记忆系统架构升级 + declarative.dpml命名重构 + MCP边界条件Bug修复 ## 📊 变更概览 - declarative.dpml架构升级:memory.xml → declarative.dpml (认知科学语义精准) - MCP环境边界条件Bug修复:解决空文件导致的记忆保存失败问题 - 跨项目角色发现Bug修复:优化环境检测顺序,MCP环境角色发现从1个→9个 - XML转义处理增强:完整的存储-显示分离架构,数据安全+用户友好 ## 🎯 核心成就 ✅ declarative.dpml升级:100%测试验证通过 ✅ 边界条件修复:三重保护机制,文件状态自动检测修复 ✅ 角色发现修复:环境检测顺序优化,跨项目使用稳定 ✅ 存储分离架构:XML转义安全存储 + AI友好显示 ## 📁 主要文件变更 - RememberCommand.js/RecallCommand.js: declarative.dpml升级 + 边界条件修复 - PackageDiscovery.js: 环境检测顺序优化 - 新增思维模式文件: recall-xml.thought.md, remember-xml.thought.md - 新增测试: memory-dpml-integration.test.js - 完整文档: PR文档 + Bug报告 + 修复总结 🎉 架构升级验证:MCP重启测试100%通过,零中断平滑切换 * fix: 修复记忆时的问题处理合并的问题 * fix: 系统化优化角色输出显示,解决角色名称混淆问题 - 优化WelcomeCommand输出格式,角色列表显示为 `id` - name 格式 - 优化ActionCommand输出格式,激活成功时显示 `id` (name) 格式 - 更新package.registry.json中7个角色的name和description: * nuwa: "Nuwa 角色" → "女娲",描述更新为专业的角色创造能力 * sean: "Sean 角色" → "Sean",描述更新为CEO身份 * product-manager: 更新为"产品经理",描述强调价值平衡能力 * java-backend-developer: 更新为"Java后端开发工程师",描述强调架构能力 * frontend-developer: 更新为"前端开发工程师",描述强调用户体验 * xiaohongshu-marketer: 更新为"小红书营销专家",描述强调平台运营 * assistant: 更新为"总经理秘书",描述强调执行和协调能力 - 解决角色名称显示不一致和描述过于通用的问题 - 提升用户体验,避免角色身份混淆 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: noface角色重命名及file://协议路径转换优化 ## 主要变更 - **角色重命名**: wumian → noface,更符合英文命名规范 - **file://协议优化**: 新增FileProtocol.js支持本地文件访问 - **路径转换修复**: 智能处理Shell反斜杠转义问题 - **ResourceManager增强**: 支持基础协议直接处理 ## 技术改进 - 修复复杂路径格式兼容性(如WeChat路径、中文字符、特殊符号) - 自动清理反斜杠转义符(Application\ Support → Application Support) - 完善错误处理机制和用户提示 ## 文件变更 - 新增: noface角色完整文件结构(role + 2个execution文件) - 新增: FileProtocol.js协议处理器 - 更新: ResourceManager.js基础协议支持 - 更新: package.registry.json角色注册信息 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: 重新定位产品价值主张,强化AI上下文工程概念 核心改进: - 🎯 主标题改为「领先的AI上下文工程平台」蹭技术热点 - ✨ 引入「Chat is all you need」革命性交互理念 - 🚀 前置强力案例数据(11000行代码、40小时→30分钟) - 🎭 重新包装核心能力,突出专业价值 产品战略价值: - 结合AI上下文工程趋势,提升技术定位 - 通过真实数据建立社会证明 - 降低技术理解门槛,扩大目标用户群 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: 重构社区章节和案例展示 核心改进: - 🌟 新增「Deepractice深度实践社区」愿景展示 - 🎯 突出「AI时代Life Style」定位和「君子和而不同」价值观 - 🏗️ 重新设计社区资源架构:开源产品+开发者舞台+商业共建 - 📋 移动案例到最后,重命名为「社区优质案例分享」 - 🤝 简化「加入我们」为纯二维码展示 产品战略价值: - 从工具项目升华为AI时代生活方式社区 - 建立开放包容的商业合作模式(内容换价值) - 为社区可持续发展和商业化提供健康路径 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: 全面优化社区价值体系和README结构 ## 主要变更 - **社区定位升级**:从"AI时代Life Style"优化为"AI原生Life Style社区" - **价值观体系重构**:技术开源·内容分享·社区开放·价值交换四维度 - **社区价值机制**:构建注意力价值交换+商业价值交换双重生态 - **内容展示优化**:社区案例分享格式精简,提升阅读体验 - **精神内核明确**:突出"实践·协作·创新"的社区精神 ## 技术改进 - 移除冗余的传统企业级展示模式 - 优化信息架构,突出价值交换核心理念 - 精简社区案例展示,提升信息密度 - 强化AI原生社区的独特定位 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * feat: 添加安装成功示意图 - 新增 assets/install-success.jpg 用于展示MCP工具安装成功效果 - 完善README中的安装成功确认章节视觉展示 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: AgustD <100462005+lsh2002@users.noreply.github.com> Co-authored-by: coso <wutongci@example.com> Co-authored-by: Cen-Yaozu <80613496+Cen-Yaozu@users.noreply.github.com>
This commit is contained in:
@ -75,7 +75,7 @@ class ActionCommand extends BasePouchCommand {
|
||||
const dependencies = await this.analyzeRoleDependencies(roleInfo)
|
||||
|
||||
// 3. 生成学习计划并直接加载所有内容
|
||||
return await this.generateLearningPlan(roleInfo.id, dependencies)
|
||||
return await this.generateLearningPlan(roleInfo, dependencies)
|
||||
} catch (error) {
|
||||
logger.error('Action command error:', error)
|
||||
return `❌ 激活角色 "${roleId}" 时发生错误。
|
||||
@ -325,10 +325,11 @@ ${result.content}
|
||||
/**
|
||||
* 生成学习计划并直接加载所有内容(包含完整的角色语义)
|
||||
*/
|
||||
async generateLearningPlan (roleId, dependencies) {
|
||||
async generateLearningPlan (roleInfo, dependencies) {
|
||||
const { thoughts, executions, roleSemantics } = dependencies
|
||||
const { id: roleId } = roleInfo
|
||||
|
||||
let content = `🎭 **角色激活完成:${roleId}** - 所有技能已自动加载\n`
|
||||
let content = `🎭 **角色激活完成:\`${roleId}\` (${roleInfo.name})** - 所有技能已自动加载\n`
|
||||
|
||||
// 加载思维模式技能(仅包含独立的thought引用)
|
||||
if (thoughts.size > 0) {
|
||||
@ -388,7 +389,7 @@ ${result.content}
|
||||
|
||||
// 激活总结
|
||||
content += `# 🎯 角色激活总结\n`
|
||||
content += `✅ **${roleId} 角色已完全激活!**\n`
|
||||
content += `✅ **\`${roleId}\` (${roleInfo.name}) 角色已完全激活!**\n`
|
||||
content += `📋 **已获得能力**:\n`
|
||||
if (thoughts.size > 0) content += `- 🧠 思维模式:${Array.from(thoughts).join(', ')}\n`
|
||||
if (executions.size > 0) content += `- ⚡ 执行技能:${Array.from(executions).join(', ')}\n`
|
||||
@ -402,7 +403,7 @@ ${result.content}
|
||||
content += `- 🎭 角色组件:${roleComponents.join(', ')}\n`
|
||||
}
|
||||
|
||||
content += `💡 **现在可以立即开始以 ${roleId} 身份提供专业服务!**\n`
|
||||
content += `💡 **现在可以立即开始以 \`${roleId}\` (${roleInfo.name}) 身份提供专业服务!**\n`
|
||||
|
||||
// 自动执行 recall 命令
|
||||
content += await this.executeRecall(roleId)
|
||||
|
||||
@ -3,31 +3,41 @@ const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const { COMMANDS } = require('../../../../constants')
|
||||
const { getGlobalResourceManager } = require('../../resource')
|
||||
const { getDirectoryService } = require('../../../utils/DirectoryService')
|
||||
const logger = require('../../../utils/logger')
|
||||
|
||||
/**
|
||||
* 记忆检索锦囊命令
|
||||
* 负责从记忆库中检索相关知识和经验
|
||||
* 记忆检索锦囊命令 - 纯XML模式
|
||||
* 负责从XML格式记忆库中检索相关知识和经验
|
||||
* 已升级为统一XML架构,移除Markdown兼容逻辑
|
||||
*/
|
||||
class RecallCommand extends BasePouchCommand {
|
||||
constructor () {
|
||||
super()
|
||||
// 复用ActionCommand的ResourceManager方式
|
||||
this.lastSearchCount = 0
|
||||
this.resourceManager = getGlobalResourceManager()
|
||||
this.directoryService = getDirectoryService()
|
||||
this.FORCE_XML_MODE = true // 🎯 强制XML模式标志
|
||||
}
|
||||
|
||||
getPurpose () {
|
||||
return 'AI主动检索记忆中的专业知识、最佳实践和历史经验'
|
||||
return 'AI主动检索记忆中的专业知识、最佳实践和历史经验(纯XML模式)'
|
||||
}
|
||||
|
||||
async getContent (args) {
|
||||
const [query] = args
|
||||
|
||||
logger.step('🧠 [RecallCommand] 开始记忆检索流程 (纯XML模式)')
|
||||
logger.info(`🔍 [RecallCommand] 查询内容: ${query ? `"${query}"` : '全部记忆'}`)
|
||||
|
||||
try {
|
||||
const memories = await this.getAllMemories(query)
|
||||
const memories = await this.getXMLMemoriesOnly(query)
|
||||
|
||||
logger.success(`✅ [RecallCommand] XML记忆检索完成 - 找到 ${memories.length} 条匹配记忆`)
|
||||
|
||||
if (memories.length === 0) {
|
||||
if (query) {
|
||||
// 针对特定查询的优化提示
|
||||
logger.warn(`⚠️ [RecallCommand] 未找到匹配查询"${query}"的记忆`)
|
||||
return `🔍 记忆检索结果:未找到匹配"${query}"的相关记忆
|
||||
|
||||
💡 优化建议:
|
||||
@ -41,7 +51,7 @@ class RecallCommand extends BasePouchCommand {
|
||||
- 使用 remember 工具记录新的相关知识
|
||||
- 使用 learn 工具学习相关资源后再检索`
|
||||
} else {
|
||||
// 无记忆的情况
|
||||
logger.warn('⚠️ [RecallCommand] 记忆体系为空')
|
||||
return `🧠 AI记忆体系中暂无内容。
|
||||
💡 建议:
|
||||
1. 使用 MCP PromptX remember 工具内化新知识
|
||||
@ -59,7 +69,14 @@ ${formattedMemories}
|
||||
2. 根据实际情况调整和变通
|
||||
3. 持续学习和增强记忆能力`
|
||||
} catch (error) {
|
||||
return `❌ 检索记忆时出错:${error.message}`
|
||||
logger.error(`❌ [RecallCommand] 记忆检索失败: ${error.message}`)
|
||||
logger.debug(`🐛 [RecallCommand] 错误堆栈: ${error.stack}`)
|
||||
return `❌ 检索记忆时出错:${error.message}
|
||||
|
||||
🛡️ **数据安全提示**:
|
||||
- 如果是升级后首次使用,数据在 .promptx/backup/ 目录中有备份
|
||||
- DPML格式记忆文件位置:.promptx/memory/declarative.dpml
|
||||
- 如需帮助,请检查备份数据或重新运行记忆迁移`
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,39 +119,49 @@ ${formattedMemories}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有记忆(支持多行格式,使用ResourceManager路径获取)
|
||||
* 获取XML记忆(纯XML模式,移除Markdown兼容)
|
||||
*/
|
||||
async getAllMemories (query) {
|
||||
async getXMLMemoriesOnly (query) {
|
||||
logger.step('🔧 [RecallCommand] 执行纯XML检索模式')
|
||||
|
||||
this.lastSearchCount = 0
|
||||
const memories = []
|
||||
|
||||
// 确保ResourceManager已初始化(就像ActionCommand那样)
|
||||
logger.debug('🔍 [RecallCommand] 初始化ResourceManager...')
|
||||
|
||||
// 确保ResourceManager已初始化
|
||||
if (!this.resourceManager.initialized) {
|
||||
logger.info('⚙️ [RecallCommand] ResourceManager未初始化,正在初始化...')
|
||||
await this.resourceManager.initializeWithNewArchitecture()
|
||||
logger.success('⚙️ [RecallCommand] ResourceManager初始化完成')
|
||||
}
|
||||
|
||||
// 通过ResourceManager获取项目路径(与ActionCommand一致)
|
||||
const projectPath = await this.getProjectPath()
|
||||
logger.info(`📍 [RecallCommand] 项目根路径: ${projectPath}`)
|
||||
|
||||
const memoryDir = path.join(projectPath, '.promptx', 'memory')
|
||||
const memoryFile = path.join(memoryDir, 'declarative.md')
|
||||
const xmlFile = path.join(memoryDir, 'declarative.dpml')
|
||||
|
||||
logger.info(`📁 [RecallCommand] XML记忆文件路径: ${xmlFile}`)
|
||||
|
||||
try {
|
||||
if (await fs.pathExists(memoryFile)) {
|
||||
const content = await fs.readFile(memoryFile, 'utf-8')
|
||||
const memoryBlocks = this.parseMemoryBlocks(content)
|
||||
|
||||
for (const memoryBlock of memoryBlocks) {
|
||||
const memory = this.parseMemoryBlock(memoryBlock)
|
||||
if (memory && (!query || this.matchesMemory(memory, query))) {
|
||||
memories.push(memory)
|
||||
}
|
||||
}
|
||||
// 🎯 只读取XML格式,不再兼容Markdown
|
||||
if (await fs.pathExists(xmlFile)) {
|
||||
logger.info('📄 [RecallCommand] 读取XML格式记忆文件')
|
||||
const xmlMemories = await this.readXMLMemories(xmlFile, query)
|
||||
memories.push(...xmlMemories)
|
||||
logger.success(`📄 [RecallCommand] XML记忆读取完成 - ${xmlMemories.length} 条记忆`)
|
||||
} else {
|
||||
logger.warn('📄 [RecallCommand] 未找到XML记忆文件,可能需要先创建记忆')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error reading memories:', error)
|
||||
logger.error(`❌ [RecallCommand] 读取XML记忆文件时发生错误: ${error.message}`)
|
||||
logger.debug(`🐛 [RecallCommand] 读取错误堆栈: ${error.stack}`)
|
||||
}
|
||||
|
||||
this.lastSearchCount = memories.length
|
||||
logger.info(`📊 [RecallCommand] XML记忆检索统计 - 总计: ${memories.length} 条`)
|
||||
|
||||
return memories
|
||||
}
|
||||
|
||||
@ -142,147 +169,50 @@ ${formattedMemories}
|
||||
* 获取项目路径(复用ActionCommand逻辑)
|
||||
*/
|
||||
async getProjectPath() {
|
||||
// 使用ResourceManager的项目路径获取逻辑
|
||||
return this.resourceManager.projectPath || process.cwd()
|
||||
logger.debug('📍 [RecallCommand] 获取项目路径...')
|
||||
|
||||
// 🔍 增加详细的路径诊断日志
|
||||
logger.warn('🔍 [RecallCommand-DIAGNOSIS] ===== 路径诊断开始 =====')
|
||||
logger.warn(`🔍 [RecallCommand-DIAGNOSIS] process.cwd(): ${process.cwd()}`)
|
||||
logger.warn(`🔍 [RecallCommand-DIAGNOSIS] process.argv: ${JSON.stringify(process.argv)}`)
|
||||
logger.warn(`🔍 [RecallCommand-DIAGNOSIS] PROMPTX_WORKSPACE: ${process.env.PROMPTX_WORKSPACE || 'undefined'}`)
|
||||
logger.warn(`🔍 [RecallCommand-DIAGNOSIS] WORKSPACE_FOLDER_PATHS: ${process.env.WORKSPACE_FOLDER_PATHS || 'undefined'}`)
|
||||
logger.warn(`🔍 [RecallCommand-DIAGNOSIS] PWD: ${process.env.PWD || 'undefined'}`)
|
||||
|
||||
// 使用DirectoryService统一获取项目路径(与InitCommand保持一致)
|
||||
const context = {
|
||||
startDir: process.cwd(),
|
||||
platform: process.platform,
|
||||
avoidUserHome: true
|
||||
}
|
||||
logger.warn(`🔍 [RecallCommand-DIAGNOSIS] DirectoryService context: ${JSON.stringify(context)}`)
|
||||
|
||||
const projectPath = await this.directoryService.getProjectRoot(context)
|
||||
logger.warn(`🔍 [RecallCommand-DIAGNOSIS] DirectoryService结果: ${projectPath}`)
|
||||
logger.warn('🔍 [RecallCommand-DIAGNOSIS] ===== 路径诊断结束 =====')
|
||||
|
||||
logger.debug(`📍 [RecallCommand] 项目路径解析结果: ${projectPath}`)
|
||||
|
||||
return projectPath
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析记忆块(新多行格式)
|
||||
*/
|
||||
parseMemoryBlocks (content) {
|
||||
const blocks = []
|
||||
const lines = content.split('\n')
|
||||
let currentBlock = []
|
||||
let inBlock = false
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.match(/^- \d{4}\/\d{2}\/\d{2} \d{2}:\d{2} START$/)) {
|
||||
// 开始新的记忆块
|
||||
if (inBlock && currentBlock.length > 0) {
|
||||
blocks.push(currentBlock.join('\n'))
|
||||
}
|
||||
currentBlock = [line]
|
||||
inBlock = true
|
||||
} else if (line === '- END' && inBlock) {
|
||||
// 结束当前记忆块
|
||||
currentBlock.push(line)
|
||||
blocks.push(currentBlock.join('\n'))
|
||||
currentBlock = []
|
||||
inBlock = false
|
||||
} else if (inBlock) {
|
||||
// 记忆块内容
|
||||
currentBlock.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理未结束的块
|
||||
if (inBlock && currentBlock.length > 0) {
|
||||
blocks.push(currentBlock.join('\n'))
|
||||
}
|
||||
|
||||
return blocks
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析单个记忆块
|
||||
*/
|
||||
parseMemoryBlock (blockContent) {
|
||||
const lines = blockContent.split('\n')
|
||||
|
||||
// 解析开始行:- 2025/06/15 15:58 START
|
||||
const startLine = lines[0]
|
||||
const startMatch = startLine.match(/^- (\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}) START$/)
|
||||
if (!startMatch) return null
|
||||
|
||||
const timestamp = startMatch[1]
|
||||
|
||||
// 查找标签行:--tags xxx
|
||||
let tagsLine = ''
|
||||
let contentLines = []
|
||||
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const line = lines[i]
|
||||
if (line.startsWith('--tags ')) {
|
||||
tagsLine = line
|
||||
} else if (line !== '- END') {
|
||||
contentLines.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
// 提取内容(去除空行)
|
||||
const content = contentLines.join('\n').trim()
|
||||
|
||||
// 解析标签
|
||||
let tags = []
|
||||
if (tagsLine) {
|
||||
const tagsContent = tagsLine.replace('--tags ', '')
|
||||
const hashTags = tagsContent.match(/#[^\s]+/g) || []
|
||||
const regularTags = tagsContent.replace(/#[^\s]+/g, '').trim().split(/\s+/).filter(t => t)
|
||||
tags = [...regularTags, ...hashTags]
|
||||
}
|
||||
|
||||
return {
|
||||
timestamp,
|
||||
content,
|
||||
tags,
|
||||
source: 'memory'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析记忆行(向下兼容旧格式)
|
||||
*/
|
||||
parseMemoryLine (line) {
|
||||
// 修复正则表达式,适配实际的记忆格式
|
||||
// 格式:- 2025/05/31 14:30 内容 --tags 标签 ##分类 #评分:8 #有效期:长期
|
||||
const match = line.match(/^- (\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}) (.+)$/)
|
||||
if (!match) return null
|
||||
|
||||
const [, timestamp, contentAndTags] = match
|
||||
|
||||
// 分离内容和标签
|
||||
let content = contentAndTags
|
||||
let tags = []
|
||||
|
||||
// 提取 --tags 后面的内容
|
||||
const tagsMatch = contentAndTags.match(/--tags\s+(.*)/)
|
||||
if (tagsMatch) {
|
||||
const beforeTags = contentAndTags.substring(0, contentAndTags.indexOf('--tags')).trim()
|
||||
content = beforeTags
|
||||
|
||||
// 解析标签部分,包括 --tags 后的内容和 # 开头的标签
|
||||
const tagsContent = tagsMatch[1]
|
||||
const hashTags = tagsContent.match(/#[^\s]+/g) || []
|
||||
const regularTags = tagsContent.replace(/#[^\s]+/g, '').trim().split(/\s+/).filter(t => t)
|
||||
|
||||
tags = [...regularTags, ...hashTags]
|
||||
} else {
|
||||
// 如果没有 --tags,检查是否有直接的 # 标签
|
||||
const hashTags = contentAndTags.match(/#[^\s]+/g) || []
|
||||
if (hashTags.length > 0) {
|
||||
content = contentAndTags.replace(/#[^\s]+/g, '').trim()
|
||||
tags = hashTags
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
timestamp,
|
||||
content,
|
||||
tags,
|
||||
source: 'memory'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查记忆是否匹配查询 - 增强版匹配算法
|
||||
*/
|
||||
matchesMemory (memory, query) {
|
||||
if (!query) return true
|
||||
|
||||
logger.debug(`🎯 [RecallCommand] 开始匹配检查 - 查询: "${query}", 记忆: "${memory.content.substring(0, 30)}..."`)
|
||||
|
||||
const lowerQuery = query.toLowerCase()
|
||||
const lowerContent = memory.content.toLowerCase()
|
||||
|
||||
// 1. 完全匹配 - 最高优先级
|
||||
if (lowerContent.includes(lowerQuery) ||
|
||||
memory.tags.some(tag => tag.toLowerCase().includes(lowerQuery))) {
|
||||
logger.debug(`✅ [RecallCommand] 完全匹配成功`)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -294,7 +224,10 @@ ${formattedMemories}
|
||||
memory.tags.some(tag => tag.toLowerCase().includes(word))
|
||||
)
|
||||
// 如果匹配了一半以上的关键词,认为相关
|
||||
return matchedWords.length >= Math.ceil(queryWords.length / 2)
|
||||
if (matchedWords.length >= Math.ceil(queryWords.length / 2)) {
|
||||
logger.debug(`✅ [RecallCommand] 分词匹配成功 - 匹配词数: ${matchedWords.length}/${queryWords.length}`)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 模糊匹配 - 支持常见同义词和缩写
|
||||
@ -302,10 +235,12 @@ ${formattedMemories}
|
||||
for (const synonym of synonyms) {
|
||||
if (lowerContent.includes(synonym) ||
|
||||
memory.tags.some(tag => tag.toLowerCase().includes(synonym))) {
|
||||
logger.debug(`✅ [RecallCommand] 同义词匹配成功 - 同义词: "${synonym}"`)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug(`❌ [RecallCommand] 无匹配`)
|
||||
return false
|
||||
}
|
||||
|
||||
@ -359,7 +294,7 @@ ${formattedMemories}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化检索到的记忆(支持多行显示)
|
||||
* 格式化检索到的记忆(支持多行显示,确保XML反转义)
|
||||
*/
|
||||
formatRetrievedKnowledge (memories, query) {
|
||||
return memories.map((memory, index) => {
|
||||
@ -367,13 +302,19 @@ ${formattedMemories}
|
||||
// 陈述性记忆的完整性对于系统价值至关重要
|
||||
let content = memory.content
|
||||
|
||||
// 🔧 确保XML转义字符被正确反转义
|
||||
content = this.unescapeXML(content)
|
||||
|
||||
// 只对格式进行优化,但不截断内容
|
||||
// 确保换行符正确显示
|
||||
content = content.trim()
|
||||
|
||||
// 🔧 也要对标签进行反转义处理
|
||||
const unescapedTags = memory.tags.map(tag => this.unescapeXML(tag))
|
||||
|
||||
return `📝 ${index + 1}. **记忆** (${memory.timestamp})
|
||||
${content}
|
||||
${memory.tags.slice(0, 8).join(' ')}
|
||||
${unescapedTags.slice(0, 8).join(' ')}
|
||||
---`
|
||||
}).join('\n')
|
||||
}
|
||||
@ -402,6 +343,116 @@ ${memory.tags.slice(0, 8).join(' ')}
|
||||
|
||||
return query + '-advanced'
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取XML格式记忆
|
||||
*/
|
||||
async readXMLMemories (xmlFile, query) {
|
||||
logger.step('📄 [RecallCommand] 开始读取XML格式记忆')
|
||||
|
||||
const memories = []
|
||||
|
||||
try {
|
||||
const xmlContent = await fs.readFile(xmlFile, 'utf8')
|
||||
logger.info(`📄 [RecallCommand] XML文件读取成功 - 文件大小: ${xmlContent.length} 字符`)
|
||||
|
||||
const xmlMemories = this.parseXMLMemories(xmlContent)
|
||||
logger.info(`📄 [RecallCommand] XML解析完成 - 解析出 ${xmlMemories.length} 条记忆`)
|
||||
|
||||
for (const memory of xmlMemories) {
|
||||
if (!query || this.matchesMemory(memory, query)) {
|
||||
memories.push(memory)
|
||||
if (query) {
|
||||
logger.debug(`🎯 [RecallCommand] 记忆匹配成功: "${memory.content.substring(0, 30)}..."`)
|
||||
}
|
||||
} else if (query) {
|
||||
logger.debug(`❌ [RecallCommand] 记忆不匹配: "${memory.content.substring(0, 30)}..."`)
|
||||
}
|
||||
}
|
||||
|
||||
logger.success(`📄 [RecallCommand] XML记忆筛选完成 - 匹配: ${memories.length}/${xmlMemories.length} 条`)
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`❌ [RecallCommand] XML记忆读取失败: ${error.message}`)
|
||||
logger.debug(`🐛 [RecallCommand] XML读取错误堆栈: ${error.stack}`)
|
||||
}
|
||||
|
||||
return memories
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析XML格式记忆
|
||||
*/
|
||||
parseXMLMemories (xmlContent) {
|
||||
logger.debug('🔍 [RecallCommand] 开始解析XML记忆内容')
|
||||
|
||||
const memories = []
|
||||
|
||||
try {
|
||||
// 简单的XML解析(不依赖外部库)
|
||||
const itemRegex = /<item\s+id="([^"]*?)"\s+time="([^"]*?)">(.*?)<\/item>/gs
|
||||
let match
|
||||
let itemCount = 0
|
||||
|
||||
while ((match = itemRegex.exec(xmlContent)) !== null) {
|
||||
itemCount++
|
||||
const [, id, timestamp, itemContent] = match
|
||||
|
||||
logger.debug(`🔍 [RecallCommand] 解析记忆项 ${itemCount}: ID=${id}, 时间=${timestamp}`)
|
||||
|
||||
// 解析内容和标签
|
||||
const contentMatch = itemContent.match(/<content>(.*?)<\/content>/s)
|
||||
const tagsMatch = itemContent.match(/<tags>(.*?)<\/tags>/s)
|
||||
|
||||
if (contentMatch) {
|
||||
const content = this.unescapeXML(contentMatch[1].trim())
|
||||
const tagsString = tagsMatch ? this.unescapeXML(tagsMatch[1].trim()) : ''
|
||||
const tags = tagsString ? tagsString.split(/\s+/).filter(t => t) : []
|
||||
|
||||
logger.debug(`🔍 [RecallCommand] 记忆项内容: "${content.substring(0, 50)}${content.length > 50 ? '...' : ''}"`)
|
||||
logger.debug(`🔍 [RecallCommand] 记忆项标签: [${tags.join(', ')}]`)
|
||||
|
||||
memories.push({
|
||||
id,
|
||||
timestamp,
|
||||
content,
|
||||
tags,
|
||||
source: 'xml'
|
||||
})
|
||||
} else {
|
||||
logger.warn(`⚠️ [RecallCommand] 记忆项 ${itemCount} 缺少content标签`)
|
||||
}
|
||||
}
|
||||
|
||||
logger.success(`🔍 [RecallCommand] XML解析完成 - 成功解析 ${memories.length} 条记忆`)
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`❌ [RecallCommand] XML解析失败: ${error.message}`)
|
||||
logger.debug(`🐛 [RecallCommand] XML解析错误堆栈: ${error.stack}`)
|
||||
}
|
||||
|
||||
return memories
|
||||
}
|
||||
|
||||
/**
|
||||
* XML反转义函数(增强版,处理所有常见XML转义字符)
|
||||
*/
|
||||
unescapeXML (text) {
|
||||
if (typeof text !== 'string') {
|
||||
return text
|
||||
}
|
||||
return text
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(/'/g, "'")
|
||||
.replace(/'/g, "'")
|
||||
.replace(///g, '/')
|
||||
.replace(///g, '/')
|
||||
.replace(/ /g, ' ')
|
||||
.replace(/&/g, '&')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RecallCommand
|
||||
|
||||
@ -184,8 +184,13 @@ class RegisterCommand extends BasePouchCommand {
|
||||
* 获取项目路径(复用ActionCommand逻辑)
|
||||
*/
|
||||
async getProjectPath() {
|
||||
// 使用ResourceManager的项目路径获取逻辑
|
||||
return this.resourceManager.projectPath || process.cwd()
|
||||
// 使用DirectoryService统一获取项目路径(与InitCommand保持一致)
|
||||
const context = {
|
||||
startDir: process.cwd(),
|
||||
platform: process.platform,
|
||||
avoidUserHome: true
|
||||
}
|
||||
return await this.directoryService.getProjectRoot(context)
|
||||
}
|
||||
|
||||
getPATEOAS (args) {
|
||||
|
||||
@ -3,20 +3,24 @@ const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const { COMMANDS } = require('../../../../constants')
|
||||
const { getGlobalResourceManager } = require('../../resource')
|
||||
const { getDirectoryService } = require('../../../utils/DirectoryService')
|
||||
const logger = require('../../../utils/logger')
|
||||
|
||||
/**
|
||||
* 记忆保存锦囊命令
|
||||
* 负责将知识、经验和最佳实践保存到记忆库中
|
||||
* 记忆保存锦囊命令 - 纯XML模式
|
||||
* 负责将知识、经验和最佳实践保存到XML格式记忆库中
|
||||
* 已升级为统一XML架构,移除Markdown兼容逻辑
|
||||
*/
|
||||
class RememberCommand extends BasePouchCommand {
|
||||
constructor () {
|
||||
super()
|
||||
// 复用ActionCommand的ResourceManager方式
|
||||
this.resourceManager = getGlobalResourceManager()
|
||||
this.directoryService = getDirectoryService()
|
||||
this.FORCE_XML_MODE = true // 🎯 强制XML模式标志
|
||||
}
|
||||
|
||||
getPurpose () {
|
||||
return '增强AI长期记忆能力,主动内化专业知识、最佳实践和项目经验'
|
||||
return '增强AI长期记忆能力,主动内化专业知识、最佳实践和项目经验(纯XML模式)'
|
||||
}
|
||||
|
||||
async getContent (args) {
|
||||
@ -27,62 +31,175 @@ class RememberCommand extends BasePouchCommand {
|
||||
}
|
||||
|
||||
try {
|
||||
const memoryEntry = await this.saveMemory(content)
|
||||
// 🛡️ 升级前自动备份(仅首次)
|
||||
await this.ensureSafetyBackupExists()
|
||||
|
||||
logger.step('🧠 [RememberCommand] 开始记忆保存流程 (纯XML模式)')
|
||||
logger.info(`📝 [RememberCommand] 记忆内容: "${content.substring(0, 50)}${content.length > 50 ? '...' : ''}"`)
|
||||
|
||||
const memoryEntry = await this.saveMemoryXMLOnly(content)
|
||||
|
||||
logger.success(`✅ [RememberCommand] XML记忆保存完成 - 路径: ${memoryEntry.filePath}`)
|
||||
return this.formatSaveResponse(content, memoryEntry)
|
||||
|
||||
} catch (error) {
|
||||
return `❌ 记忆内化失败:${error.message}
|
||||
|
||||
💡 可能的原因:
|
||||
- AI记忆体系目录权限不足
|
||||
- 磁盘空间不够
|
||||
- 记忆内容格式问题
|
||||
|
||||
🔧 解决方案:
|
||||
1. 检查 .promptx 目录权限
|
||||
2. 确保磁盘空间充足
|
||||
3. 检查记忆内容是否包含特殊字符`
|
||||
logger.error(`❌ [RememberCommand] 记忆保存失败: ${error.message}`)
|
||||
logger.debug(`🐛 [RememberCommand] 错误堆栈: ${error.stack}`)
|
||||
|
||||
return this.formatErrorWithRecovery(error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将知识内化到AI记忆体系(紧凑格式)
|
||||
* 🛡️ 确保安全备份存在
|
||||
*/
|
||||
async saveMemory (value) {
|
||||
// 1. 确保AI记忆体系目录存在
|
||||
const memoryDir = await this.ensureMemoryDirectory()
|
||||
|
||||
// 2. 使用单一记忆文件
|
||||
const memoryFile = path.join(memoryDir, 'declarative.md')
|
||||
|
||||
// 3. 格式化为一行记忆
|
||||
const memoryLine = this.formatMemoryLine(value)
|
||||
|
||||
// 4. 追加到记忆文件
|
||||
const action = await this.appendToMemoryFile(memoryFile, memoryLine)
|
||||
|
||||
return {
|
||||
value,
|
||||
filePath: memoryFile,
|
||||
action,
|
||||
timestamp: new Date().toISOString()
|
||||
async ensureSafetyBackupExists() {
|
||||
const projectPath = await this.getProjectPath()
|
||||
const backupMarker = path.join(projectPath, '.promptx', '.xml-upgrade-backup-done')
|
||||
|
||||
if (!await fs.pathExists(backupMarker)) {
|
||||
logger.step('🛡️ [RememberCommand] 执行升级前安全备份...')
|
||||
await this.createSafetyBackup()
|
||||
await fs.writeFile(backupMarker, new Date().toISOString())
|
||||
logger.success('🛡️ [RememberCommand] 安全备份完成')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 🛡️ 创建安全备份
|
||||
*/
|
||||
async createSafetyBackup() {
|
||||
const projectPath = await this.getProjectPath()
|
||||
const memoryDir = path.join(projectPath, '.promptx', 'memory')
|
||||
const backupDir = path.join(projectPath, '.promptx', 'backup', `backup_${Date.now()}`)
|
||||
|
||||
await fs.ensureDir(backupDir)
|
||||
|
||||
// 备份所有现有记忆文件
|
||||
const filesToBackup = ['declarative.dpml', 'declarative.md', 'declarative.md.bak']
|
||||
|
||||
for (const file of filesToBackup) {
|
||||
const source = path.join(memoryDir, file)
|
||||
if (await fs.pathExists(source)) {
|
||||
await fs.copy(source, path.join(backupDir, file))
|
||||
logger.success(`✅ 备份文件: ${file}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 创建备份元数据
|
||||
const backupMeta = {
|
||||
timestamp: new Date().toISOString(),
|
||||
version: 'pre-xml-upgrade',
|
||||
files: filesToBackup.filter(f => fs.pathExistsSync(path.join(memoryDir, f)))
|
||||
}
|
||||
|
||||
await fs.writeJSON(path.join(backupDir, 'backup-meta.json'), backupMeta, {spaces: 2})
|
||||
|
||||
logger.success(`🛡️ 安全备份完成: ${backupDir}`)
|
||||
return backupDir
|
||||
}
|
||||
|
||||
/**
|
||||
* 纯XML记忆保存(移除所有Markdown逻辑)
|
||||
*/
|
||||
async saveMemoryXMLOnly(value) {
|
||||
logger.step('🔧 [RememberCommand] 执行纯XML保存模式')
|
||||
|
||||
const memoryDir = await this.ensureMemoryDirectory()
|
||||
|
||||
// 🔄 保留一次性Legacy迁移(确保老用户数据不丢失)
|
||||
await this.performSafeLegacyMigration(memoryDir)
|
||||
|
||||
// 🎯 纯DPML处理流程
|
||||
const xmlFile = path.join(memoryDir, 'declarative.dpml')
|
||||
const memoryItem = this.formatXMLMemoryItem(value)
|
||||
const action = await this.appendToXMLFile(xmlFile, memoryItem)
|
||||
|
||||
return {
|
||||
value,
|
||||
filePath: xmlFile,
|
||||
action,
|
||||
timestamp: new Date().toISOString(),
|
||||
format: 'xml'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔄 安全的Legacy迁移
|
||||
*/
|
||||
async performSafeLegacyMigration(memoryDir) {
|
||||
const legacyFile = path.join(memoryDir, 'declarative.md')
|
||||
const xmlFile = path.join(memoryDir, 'declarative.dpml')
|
||||
|
||||
if (await fs.pathExists(legacyFile) && !await fs.pathExists(xmlFile)) {
|
||||
logger.step('🔄 [RememberCommand] 检测到Legacy数据,执行安全迁移...')
|
||||
|
||||
try {
|
||||
// 迁移前再次备份
|
||||
const timestamp = Date.now()
|
||||
await fs.copy(legacyFile, `${legacyFile}.pre-migration.${timestamp}`)
|
||||
|
||||
// 执行迁移
|
||||
await this.migrateLegacyMemoriesIfNeeded(memoryDir)
|
||||
|
||||
logger.success('🔄 [RememberCommand] Legacy数据迁移完成')
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`❌ [RememberCommand] Legacy迁移失败: ${error.message}`)
|
||||
logger.debug(`❌ [RememberCommand] 迁移错误堆栈: ${error.stack}`)
|
||||
logger.warn(`⚠️ [RememberCommand] 迁移失败,继续使用新记忆系统,备份文件已保存`)
|
||||
// 静默处理,不向用户抛出错误,宁愿丢失旧记忆也不影响用户体验
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 🚨 错误恢复建议
|
||||
*/
|
||||
formatErrorWithRecovery(error) {
|
||||
return `❌ XML记忆保存失败:${error.message}
|
||||
|
||||
🛡️ **恢复方案**:
|
||||
1. 检查 .promptx/backup/ 目录中的数据备份
|
||||
2. 如需回滚,请联系技术支持
|
||||
3. 备份文件位置:.promptx/backup/backup_*
|
||||
|
||||
🔧 **可能的原因**:
|
||||
- 磁盘空间不足
|
||||
- 文件权限问题
|
||||
- XML格式验证失败
|
||||
|
||||
💡 **建议操作**:
|
||||
1. 检查磁盘空间和权限
|
||||
2. 重试记忆操作
|
||||
3. 如持续失败,查看备份数据`
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 确保AI记忆体系目录存在(使用ResourceManager路径获取)
|
||||
*/
|
||||
async ensureMemoryDirectory () {
|
||||
logger.debug('🔍 [RememberCommand] 初始化ResourceManager...')
|
||||
|
||||
// 确保ResourceManager已初始化(就像ActionCommand那样)
|
||||
if (!this.resourceManager.initialized) {
|
||||
logger.info('⚙️ [RememberCommand] ResourceManager未初始化,正在初始化...')
|
||||
await this.resourceManager.initializeWithNewArchitecture()
|
||||
logger.success('⚙️ [RememberCommand] ResourceManager初始化完成')
|
||||
}
|
||||
|
||||
// 通过ResourceManager获取项目路径(与ActionCommand一致)
|
||||
const projectPath = await this.getProjectPath()
|
||||
logger.info(`📍 [RememberCommand] 项目根路径: ${projectPath}`)
|
||||
|
||||
const memoryDir = path.join(projectPath, '.promptx', 'memory')
|
||||
logger.info(`📁 [RememberCommand] 创建记忆目录: ${memoryDir}`)
|
||||
|
||||
await fs.ensureDir(memoryDir)
|
||||
logger.success(`📁 [RememberCommand] 记忆目录确保完成: ${memoryDir}`)
|
||||
|
||||
return memoryDir
|
||||
}
|
||||
|
||||
@ -90,25 +207,456 @@ class RememberCommand extends BasePouchCommand {
|
||||
* 获取项目路径(复用ActionCommand逻辑)
|
||||
*/
|
||||
async getProjectPath() {
|
||||
// 使用ResourceManager的项目路径获取逻辑
|
||||
return this.resourceManager.projectPath || process.cwd()
|
||||
logger.debug('📍 [RememberCommand] 获取项目路径...')
|
||||
|
||||
// 🔍 增加详细的路径诊断日志
|
||||
logger.warn('🔍 [RememberCommand-DIAGNOSIS] ===== 路径诊断开始 =====')
|
||||
logger.warn(`🔍 [RememberCommand-DIAGNOSIS] process.cwd(): ${process.cwd()}`)
|
||||
logger.warn(`🔍 [RememberCommand-DIAGNOSIS] process.argv: ${JSON.stringify(process.argv)}`)
|
||||
logger.warn(`🔍 [RememberCommand-DIAGNOSIS] PROMPTX_WORKSPACE: ${process.env.PROMPTX_WORKSPACE || 'undefined'}`)
|
||||
logger.warn(`🔍 [RememberCommand-DIAGNOSIS] WORKSPACE_FOLDER_PATHS: ${process.env.WORKSPACE_FOLDER_PATHS || 'undefined'}`)
|
||||
logger.warn(`🔍 [RememberCommand-DIAGNOSIS] PWD: ${process.env.PWD || 'undefined'}`)
|
||||
|
||||
// 使用DirectoryService统一获取项目路径(与InitCommand保持一致)
|
||||
const context = {
|
||||
startDir: process.cwd(),
|
||||
platform: process.platform,
|
||||
avoidUserHome: true
|
||||
}
|
||||
logger.warn(`🔍 [RememberCommand-DIAGNOSIS] DirectoryService context: ${JSON.stringify(context)}`)
|
||||
|
||||
const projectPath = await this.directoryService.getProjectRoot(context)
|
||||
logger.warn(`🔍 [RememberCommand-DIAGNOSIS] DirectoryService结果: ${projectPath}`)
|
||||
logger.warn('🔍 [RememberCommand-DIAGNOSIS] ===== 路径诊断结束 =====')
|
||||
|
||||
logger.debug(`📍 [RememberCommand] 项目路径解析结果: ${projectPath}`)
|
||||
|
||||
return projectPath
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化为多行记忆块(新格式)
|
||||
* 格式化为XML记忆项
|
||||
*/
|
||||
formatMemoryLine (value) {
|
||||
formatXMLMemoryItem (value) {
|
||||
logger.debug('🏷️ [RememberCommand] 开始格式化XML记忆项...')
|
||||
|
||||
const now = new Date()
|
||||
const timestamp = `${now.getFullYear()}/${String(now.getMonth() + 1).padStart(2, '0')}/${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
|
||||
const id = `mem_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
|
||||
logger.debug(`🏷️ [RememberCommand] 生成记忆ID: ${id}`)
|
||||
logger.debug(`🏷️ [RememberCommand] 时间戳: ${timestamp}`)
|
||||
|
||||
// 自动生成标签
|
||||
const tags = this.generateTags(value)
|
||||
logger.debug(`🏷️ [RememberCommand] 自动生成标签: ${tags}`)
|
||||
|
||||
// 使用新的多行格式
|
||||
return `- ${timestamp} START
|
||||
${value}
|
||||
--tags ${tags} #评分:8 #有效期:长期
|
||||
- END`
|
||||
// XML转义
|
||||
const escapedContent = this.escapeXML(value)
|
||||
const escapedTags = this.escapeXML(tags)
|
||||
|
||||
logger.debug(`🏷️ [RememberCommand] XML转义完成 - 内容长度: ${escapedContent.length}`)
|
||||
if (escapedContent !== value) {
|
||||
logger.info('🔄 [RememberCommand] 检测到特殊字符,已进行XML转义')
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
timestamp,
|
||||
content: escapedContent,
|
||||
tags: escapedTags,
|
||||
rawContent: value,
|
||||
rawTags: tags
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* XML转义函数
|
||||
*/
|
||||
escapeXML (text) {
|
||||
if (typeof text !== 'string') {
|
||||
return text
|
||||
}
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化内容缩进(添加适当的缩进让XML更美观)
|
||||
*/
|
||||
formatContentWithIndent (content, indentLevel = 3) {
|
||||
if (typeof content !== 'string') {
|
||||
return content
|
||||
}
|
||||
|
||||
// 基础缩进字符串(每级2个空格)
|
||||
const baseIndent = ' '.repeat(indentLevel)
|
||||
|
||||
// 分割内容为行
|
||||
const lines = content.split('\n')
|
||||
|
||||
// 格式化每一行,添加缩进
|
||||
const formattedLines = lines.map((line, index) => {
|
||||
// 第一行和最后一行特殊处理
|
||||
if (index === 0 && index === lines.length - 1) {
|
||||
// 单行内容
|
||||
return line.trim() ? `\n${baseIndent}${line.trim()}\n ` : line
|
||||
} else if (index === 0) {
|
||||
// 第一行
|
||||
return line.trim() ? `\n${baseIndent}${line.trim()}` : `\n${baseIndent}`
|
||||
} else if (index === lines.length - 1) {
|
||||
// 最后一行
|
||||
return line.trim() ? `${baseIndent}${line.trim()}\n ` : `\n `
|
||||
} else {
|
||||
// 中间行
|
||||
return line.trim() ? `${baseIndent}${line.trim()}` : baseIndent.substring(2) // 空行保持基础缩进
|
||||
}
|
||||
})
|
||||
|
||||
return formattedLines.join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加到XML文件
|
||||
*/
|
||||
async appendToXMLFile (xmlFile, memoryItem) {
|
||||
logger.debug(`💾 [RememberCommand] 检查XML文件是否存在: ${xmlFile}`)
|
||||
|
||||
// 格式化内容缩进
|
||||
const formattedContent = this.formatContentWithIndent(memoryItem.content)
|
||||
|
||||
// 检查文件是否存在以及是否为空
|
||||
const fileExists = await fs.pathExists(xmlFile)
|
||||
let fileIsEmpty = false
|
||||
|
||||
if (fileExists) {
|
||||
const stats = await fs.stat(xmlFile)
|
||||
fileIsEmpty = stats.size === 0
|
||||
logger.debug(`💾 [RememberCommand] XML文件状态检查 - 存在: ${fileExists}, 大小: ${stats.size}字节, 为空: ${fileIsEmpty}`)
|
||||
}
|
||||
|
||||
// 初始化XML文件(如果不存在或为空)
|
||||
if (!fileExists || fileIsEmpty) {
|
||||
if (fileIsEmpty) {
|
||||
logger.info('📄 [RememberCommand] XML文件存在但为空,重新初始化...')
|
||||
} else {
|
||||
logger.info('📄 [RememberCommand] XML文件不存在,创建新文件...')
|
||||
}
|
||||
|
||||
const initialXML = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<memory>
|
||||
<item id="${memoryItem.id}" time="${memoryItem.timestamp}">
|
||||
<content>${formattedContent}</content>
|
||||
<tags>${memoryItem.tags}</tags>
|
||||
</item>
|
||||
</memory>`
|
||||
|
||||
await fs.writeFile(xmlFile, initialXML, 'utf8')
|
||||
logger.success('📄 [RememberCommand] XML文件初始化完成')
|
||||
logger.debug(`📄 [RememberCommand] 初始XML内容长度: ${initialXML.length}字符`)
|
||||
|
||||
return 'created'
|
||||
}
|
||||
|
||||
logger.info('📄 [RememberCommand] XML文件已存在且有内容,追加新记忆项...')
|
||||
|
||||
// 读取现有XML并添加新项
|
||||
const content = await fs.readFile(xmlFile, 'utf8')
|
||||
logger.debug(`📄 [RememberCommand] 读取现有XML文件 - 长度: ${content.length}字符`)
|
||||
|
||||
// 验证XML文件格式
|
||||
if (!content.includes('</memory>')) {
|
||||
logger.warn('📄 [RememberCommand] XML文件格式异常,缺少</memory>标签,重新初始化...')
|
||||
// 重新初始化文件
|
||||
const initialXML = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<memory>
|
||||
<item id="${memoryItem.id}" time="${memoryItem.timestamp}">
|
||||
<content>${formattedContent}</content>
|
||||
<tags>${memoryItem.tags}</tags>
|
||||
</item>
|
||||
</memory>`
|
||||
|
||||
await fs.writeFile(xmlFile, initialXML, 'utf8')
|
||||
logger.success('📄 [RememberCommand] XML文件重新初始化完成')
|
||||
return 'created'
|
||||
}
|
||||
|
||||
// 找到</memory>标签的位置,在它之前插入新的记忆项
|
||||
const newItem = ` <item id="${memoryItem.id}" time="${memoryItem.timestamp}">
|
||||
<content>${formattedContent}</content>
|
||||
<tags>${memoryItem.tags}</tags>
|
||||
</item>`
|
||||
|
||||
const updatedContent = content.replace('</memory>', `${newItem}
|
||||
</memory>`)
|
||||
|
||||
logger.debug(`📄 [RememberCommand] 新XML内容长度: ${updatedContent.length}字符`)
|
||||
logger.debug(`📄 [RememberCommand] 新增记忆项ID: ${memoryItem.id}`)
|
||||
|
||||
await fs.writeFile(xmlFile, updatedContent, 'utf8')
|
||||
logger.success('📄 [RememberCommand] XML文件追加完成')
|
||||
|
||||
return 'created'
|
||||
}
|
||||
|
||||
/**
|
||||
* 从legacy Markdown格式迁移到XML格式
|
||||
*/
|
||||
async migrateLegacyMemoriesIfNeeded (memoryDir) {
|
||||
const legacyFile = path.join(memoryDir, 'declarative.md')
|
||||
const xmlFile = path.join(memoryDir, 'declarative.dpml')
|
||||
const backupFile = path.join(memoryDir, 'declarative.md.bak')
|
||||
|
||||
logger.debug(`🔄 [RememberCommand] 检查迁移需求 - legacy: ${legacyFile}, xml: ${xmlFile}`)
|
||||
|
||||
// 如果XML文件已存在,说明已经迁移过了
|
||||
if (await fs.pathExists(xmlFile)) {
|
||||
logger.debug('🔄 [RememberCommand] XML文件已存在,无需迁移')
|
||||
return
|
||||
}
|
||||
|
||||
// 如果legacy文件不存在,无需迁移
|
||||
if (!await fs.pathExists(legacyFile)) {
|
||||
logger.debug('🔄 [RememberCommand] Legacy文件不存在,无需迁移')
|
||||
return
|
||||
}
|
||||
|
||||
logger.step('🔄 [RememberCommand] 正在迁移记忆数据从Markdown到XML格式...')
|
||||
|
||||
try {
|
||||
// 读取legacy文件
|
||||
const legacyContent = await fs.readFile(legacyFile, 'utf8')
|
||||
logger.info(`🔄 [RememberCommand] 读取legacy文件 - 长度: ${legacyContent.length}字符`)
|
||||
|
||||
// 解析legacy记忆
|
||||
const legacyMemories = this.parseLegacyMemories(legacyContent)
|
||||
logger.info(`🔄 [RememberCommand] 解析到 ${legacyMemories.length} 条legacy记忆`)
|
||||
|
||||
// 创建XML文件
|
||||
let xmlContent = '<?xml version="1.0" encoding="UTF-8"?>\n<memory>\n'
|
||||
|
||||
for (const memory of legacyMemories) {
|
||||
const escapedContent = this.escapeXML(memory.content)
|
||||
const escapedTags = this.escapeXML(memory.tags.join(' '))
|
||||
const id = `legacy_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
|
||||
logger.debug(`🔄 [RememberCommand] 迁移记忆项: ${memory.content.substring(0, 30)}...`)
|
||||
|
||||
xmlContent += ` <item id="${id}" time="${memory.timestamp}">
|
||||
<content>${escapedContent}</content>
|
||||
<tags>${escapedTags}</tags>
|
||||
</item>
|
||||
`
|
||||
}
|
||||
|
||||
xmlContent += '</memory>'
|
||||
|
||||
// 写入XML文件
|
||||
await fs.writeFile(xmlFile, xmlContent, 'utf8')
|
||||
logger.success(`🔄 [RememberCommand] XML文件创建成功 - 长度: ${xmlContent.length}字符`)
|
||||
|
||||
// 备份legacy文件
|
||||
await fs.move(legacyFile, backupFile)
|
||||
logger.success(`🔄 [RememberCommand] Legacy文件备份到: ${backupFile}`)
|
||||
|
||||
logger.success(`🔄 [RememberCommand] 成功迁移${legacyMemories.length}条记忆到XML格式`)
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`🔄 [RememberCommand] 记忆迁移失败: ${error.message}`)
|
||||
logger.debug(`🔄 [RememberCommand] 迁移错误堆栈: ${error.stack}`)
|
||||
throw new Error(`记忆迁移失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析legacy Markdown格式的记忆(支持START-END多行格式)
|
||||
*/
|
||||
parseLegacyMemories (content) {
|
||||
logger.debug('🔍 [RememberCommand] 开始解析Legacy记忆,支持START-END多行格式')
|
||||
|
||||
const memories = []
|
||||
|
||||
// 🎯 首先尝试解析START-END多行格式
|
||||
const multiLineMemories = this.parseMultiLineMemories(content)
|
||||
memories.push(...multiLineMemories)
|
||||
|
||||
// 🎯 只有在没有找到多行格式时才解析单行格式(避免重复)
|
||||
if (multiLineMemories.length === 0) {
|
||||
logger.info('🔍 [RememberCommand] 未找到START-END格式,尝试单行格式解析')
|
||||
const singleLineMemories = this.parseSingleLineMemories(content)
|
||||
memories.push(...singleLineMemories)
|
||||
logger.success(`🔍 [RememberCommand] 单行格式解析完成 - ${singleLineMemories.length} 条记忆`)
|
||||
} else {
|
||||
logger.success(`🔍 [RememberCommand] 多行格式解析完成 - ${multiLineMemories.length} 条记忆,跳过单行解析`)
|
||||
}
|
||||
|
||||
logger.success(`🔍 [RememberCommand] Legacy记忆解析完成 - 总计: ${memories.length} 条`)
|
||||
|
||||
return memories
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析START-END多行格式记忆
|
||||
*/
|
||||
parseMultiLineMemories (content) {
|
||||
logger.debug('📝 [RememberCommand] 解析START-END多行格式记忆')
|
||||
|
||||
const memories = []
|
||||
const blocks = this.parseMemoryBlocks(content)
|
||||
|
||||
for (const block of blocks) {
|
||||
const memory = this.parseMemoryBlock(block)
|
||||
if (memory) {
|
||||
memories.push(memory)
|
||||
logger.debug(`📝 [RememberCommand] 成功解析多行记忆: "${memory.content.substring(0, 30)}..."`)
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug(`📝 [RememberCommand] 多行格式解析完成 - ${memories.length} 条记忆`)
|
||||
return memories
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析记忆块(START-END格式)
|
||||
*/
|
||||
parseMemoryBlocks (content) {
|
||||
const blocks = []
|
||||
const lines = content.split('\n')
|
||||
let currentBlock = []
|
||||
let inBlock = false
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.match(/^- \d{4}\/\d{2}\/\d{2} \d{2}:\d{2} START$/)) {
|
||||
// 开始新的记忆块
|
||||
if (inBlock && currentBlock.length > 0) {
|
||||
blocks.push(currentBlock.join('\n'))
|
||||
}
|
||||
currentBlock = [line]
|
||||
inBlock = true
|
||||
} else if (line === '- END' && inBlock) {
|
||||
// 结束当前记忆块
|
||||
currentBlock.push(line)
|
||||
blocks.push(currentBlock.join('\n'))
|
||||
currentBlock = []
|
||||
inBlock = false
|
||||
} else if (inBlock) {
|
||||
// 记忆块内容
|
||||
currentBlock.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理未结束的块
|
||||
if (inBlock && currentBlock.length > 0) {
|
||||
blocks.push(currentBlock.join('\n'))
|
||||
}
|
||||
|
||||
return blocks
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析单个记忆块
|
||||
*/
|
||||
parseMemoryBlock (blockContent) {
|
||||
const lines = blockContent.split('\n')
|
||||
|
||||
// 解析开始行:- 2025/06/15 15:58 START
|
||||
const startLine = lines[0]
|
||||
const startMatch = startLine.match(/^- (\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}) START$/)
|
||||
if (!startMatch) return null
|
||||
|
||||
const timestamp = startMatch[1]
|
||||
|
||||
// 查找标签行:--tags xxx
|
||||
let tagsLine = ''
|
||||
let contentLines = []
|
||||
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const line = lines[i]
|
||||
if (line.startsWith('--tags ')) {
|
||||
tagsLine = line
|
||||
} else if (line !== '- END') {
|
||||
contentLines.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
// 提取内容(去除空行)
|
||||
const content = contentLines.join('\n').trim()
|
||||
|
||||
// 解析标签
|
||||
let tags = []
|
||||
if (tagsLine) {
|
||||
const tagsContent = tagsLine.replace('--tags ', '')
|
||||
const hashTags = tagsContent.match(/#[^\s]+/g) || []
|
||||
const regularTags = tagsContent.replace(/#[^\s]+/g, '').trim().split(/\s+/).filter(t => t)
|
||||
tags = [...regularTags, ...hashTags]
|
||||
}
|
||||
|
||||
return {
|
||||
timestamp,
|
||||
content,
|
||||
tags
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析单行格式记忆(向后兼容)
|
||||
*/
|
||||
parseSingleLineMemories (content) {
|
||||
logger.debug('📄 [RememberCommand] 解析单行格式记忆(向后兼容)')
|
||||
|
||||
const memories = []
|
||||
const lines = content.split('\n')
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmedLine = line.trim()
|
||||
|
||||
// 跳过START-END格式的行(避免重复解析)
|
||||
if (trimmedLine.includes(' START') || trimmedLine === '- END' || trimmedLine.startsWith('--tags')) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 解析标准单行格式:- 2025/01/15 14:30 内容 #标签 #评分:8 #有效期:长期
|
||||
const match = trimmedLine.match(/^- (\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}) (.+)$/)
|
||||
if (match) {
|
||||
const [, timestamp, contentAndTags] = match
|
||||
|
||||
// 分离内容和标签
|
||||
let content = contentAndTags
|
||||
let tags = []
|
||||
|
||||
// 提取 --tags 后面的内容
|
||||
const tagsMatch = contentAndTags.match(/--tags\s+(.*)/)
|
||||
if (tagsMatch) {
|
||||
content = contentAndTags.substring(0, contentAndTags.indexOf('--tags')).trim()
|
||||
const tagsContent = tagsMatch[1]
|
||||
const hashTags = tagsContent.match(/#[^\s]+/g) || []
|
||||
const regularTags = tagsContent.replace(/#[^\s]+/g, '').trim().split(/\s+/).filter(t => t)
|
||||
tags = [...regularTags, ...hashTags]
|
||||
} else {
|
||||
// 如果没有 --tags,检查是否有直接的 # 标签
|
||||
const hashTags = contentAndTags.match(/#[^\s]+/g) || []
|
||||
if (hashTags.length > 0) {
|
||||
content = contentAndTags.replace(/#[^\s]+/g, '').trim()
|
||||
tags = hashTags
|
||||
}
|
||||
}
|
||||
|
||||
memories.push({
|
||||
timestamp,
|
||||
content,
|
||||
tags
|
||||
})
|
||||
|
||||
logger.debug(`📄 [RememberCommand] 成功解析单行记忆: "${content.substring(0, 30)}..."`)
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug(`📄 [RememberCommand] 单行格式解析完成 - ${memories.length} 条记忆`)
|
||||
return memories
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,68 +675,58 @@ ${value}
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加到记忆文件
|
||||
*/
|
||||
async appendToMemoryFile (memoryFile, memoryBlock) {
|
||||
// 初始化文件(如果不存在)
|
||||
if (!await fs.pathExists(memoryFile)) {
|
||||
await fs.writeFile(memoryFile, `# 陈述性记忆
|
||||
|
||||
## 高价值记忆(评分 ≥ 7)
|
||||
|
||||
${memoryBlock}
|
||||
|
||||
`)
|
||||
return 'created'
|
||||
}
|
||||
|
||||
// 读取现有内容
|
||||
const content = await fs.readFile(memoryFile, 'utf-8')
|
||||
|
||||
// 追加新记忆块(在高价值记忆部分)
|
||||
const updatedContent = content + '\n\n' + memoryBlock
|
||||
await fs.writeFile(memoryFile, updatedContent)
|
||||
return 'created'
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化保存响应(简化版本)
|
||||
* 格式化保存响应(XML版本)
|
||||
*/
|
||||
formatSaveResponse (value, memoryEntry) {
|
||||
const { action, timestamp } = memoryEntry
|
||||
const { action, timestamp, format, filePath } = memoryEntry
|
||||
|
||||
const actionLabels = {
|
||||
created: '✅ AI已内化新记忆'
|
||||
created: '✅ AI已内化新记忆(XML格式)'
|
||||
}
|
||||
|
||||
return `${actionLabels[action]}:${value}
|
||||
|
||||
## 📋 记忆详情
|
||||
- **存储格式**: ${format.toUpperCase()}
|
||||
- **内化时间**: ${timestamp.split('T')[0]}
|
||||
- **存储路径**: ${path.basename(filePath)}
|
||||
- **知识内容**: ${value.length > 100 ? value.substring(0, 100) + '...' : value}
|
||||
|
||||
## 🎯 能力增强效果
|
||||
- ✅ **知识已内化到AI长期记忆**
|
||||
- ✅ **知识已内化到AI长期记忆(XML结构化存储)**
|
||||
- ✅ **支持精确的内容检索和标签搜索**
|
||||
- ✅ **可通过recall命令主动检索**
|
||||
- ✅ **支持跨会话记忆保持**
|
||||
- ✅ **自动从legacy格式迁移**
|
||||
|
||||
## 🔄 下一步行动:
|
||||
- 记忆检索: 使用 MCP PromptX recall 工具验证知识内化效果
|
||||
- 能力强化: 使用 MCP PromptX learn 工具学习相关知识增强记忆
|
||||
- 应用实践: 使用 MCP PromptX action 工具在实际场景中运用记忆
|
||||
|
||||
📍 当前状态:memory_saved`
|
||||
📍 当前状态:memory_saved_xml`
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取使用帮助
|
||||
* 获取使用帮助(纯XML模式)
|
||||
*/
|
||||
getUsageHelp () {
|
||||
return `🧠 **Remember锦囊 - AI记忆增强系统**
|
||||
return `🧠 **Remember锦囊 - AI记忆增强系统(纯XML模式)**
|
||||
|
||||
## 📖 基本用法
|
||||
通过 MCP PromptX remember 工具内化知识
|
||||
|
||||
## 🆕 升级特性
|
||||
- **纯XML存储**: 统一使用XML格式,性能更优
|
||||
- **自动备份**: 升级前自动创建安全备份
|
||||
- **Legacy迁移**: 自动迁移旧格式数据
|
||||
- **数据安全**: 多重备份保护机制
|
||||
|
||||
## 🛡️ 安全保障
|
||||
- 升级前自动备份所有数据
|
||||
- Legacy数据自动迁移到XML格式
|
||||
- 出错时提供恢复建议和备份位置
|
||||
|
||||
## 💡 记忆内化示例
|
||||
|
||||
### 📝 AI记忆内化
|
||||
@ -198,6 +736,12 @@ AI学习和内化各种专业知识:
|
||||
- "React Hooks允许在函数组件中使用state和其他React特性"
|
||||
- "每个PR至少需要2个人review,必须包含测试用例"
|
||||
|
||||
## 🆕 XML记忆模式特性
|
||||
- **结构化存储**: 使用XML格式存储,支持更精确的数据管理
|
||||
- **自动迁移**: 从legacy Markdown格式自动迁移到XML
|
||||
- **XML转义**: 自动处理特殊字符,确保数据完整性
|
||||
- **向后兼容**: 继续支持读取legacy格式记忆
|
||||
|
||||
## 🔍 记忆检索与应用
|
||||
- 使用 MCP PromptX recall 工具主动检索记忆
|
||||
- 使用 MCP PromptX action 工具运用记忆激活角色
|
||||
|
||||
@ -201,10 +201,8 @@ class WelcomeCommand extends BasePouchCommand {
|
||||
content += `### ${sourceLabel}\n\n`
|
||||
|
||||
rolesBySource[source].forEach(role => {
|
||||
content += `#### ${roleIndex}. ${role.name}
|
||||
**角色ID**: \`${role.id}\`
|
||||
content += `#### ${roleIndex}. \`${role.id}\` - ${role.name}
|
||||
**专业能力**: ${role.description}
|
||||
**文件路径**: ${role.file}
|
||||
**来源**: ${sourceLabel}
|
||||
|
||||
---
|
||||
|
||||
617
src/lib/core/pouch/commands/_deprecated/RecallCommand_v1.js
Normal file
617
src/lib/core/pouch/commands/_deprecated/RecallCommand_v1.js
Normal file
@ -0,0 +1,617 @@
|
||||
const BasePouchCommand = require('../BasePouchCommand')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const { COMMANDS } = require('../../../../constants')
|
||||
const { getGlobalResourceManager } = require('../../resource')
|
||||
const { getDirectoryService } = require('../../../utils/DirectoryService')
|
||||
const logger = require('../../../utils/logger')
|
||||
|
||||
/**
|
||||
* 记忆检索锦囊命令
|
||||
* 负责从记忆库中检索相关知识和经验
|
||||
*/
|
||||
class RecallCommand extends BasePouchCommand {
|
||||
constructor () {
|
||||
super()
|
||||
this.lastSearchCount = 0
|
||||
// 复用ActionCommand的ResourceManager方式
|
||||
this.resourceManager = getGlobalResourceManager()
|
||||
this.directoryService = getDirectoryService()
|
||||
}
|
||||
|
||||
getPurpose () {
|
||||
return 'AI主动检索记忆中的专业知识、最佳实践和历史经验'
|
||||
}
|
||||
|
||||
async getContent (args) {
|
||||
const [query] = args
|
||||
|
||||
logger.step('🧠 [RecallCommand] 开始记忆检索流程')
|
||||
logger.info(`🔍 [RecallCommand] 查询内容: ${query ? `"${query}"` : '全部记忆'}`)
|
||||
|
||||
try {
|
||||
const memories = await this.getAllMemories(query)
|
||||
|
||||
logger.success(`✅ [RecallCommand] 记忆检索完成 - 找到 ${memories.length} 条匹配记忆`)
|
||||
|
||||
if (memories.length === 0) {
|
||||
if (query) {
|
||||
logger.warn(`⚠️ [RecallCommand] 未找到匹配查询"${query}"的记忆`)
|
||||
// 针对特定查询的优化提示
|
||||
return `🔍 记忆检索结果:未找到匹配"${query}"的相关记忆
|
||||
|
||||
💡 优化建议:
|
||||
1. **扩大查询范围**:尝试使用更通用的关键词
|
||||
2. **换个角度查询**:尝试相关词汇或概念
|
||||
3. **检查拼写**:确认关键词拼写正确
|
||||
4. **查看全部记忆**:不使用查询参数,浏览所有记忆寻找灵感
|
||||
|
||||
🔄 下一步行动:
|
||||
- 不带参数再次使用 recall 工具查看全部记忆
|
||||
- 使用 remember 工具记录新的相关知识
|
||||
- 使用 learn 工具学习相关资源后再检索`
|
||||
} else {
|
||||
logger.warn('⚠️ [RecallCommand] 记忆体系为空')
|
||||
// 无记忆的情况
|
||||
return `🧠 AI记忆体系中暂无内容。
|
||||
💡 建议:
|
||||
1. 使用 MCP PromptX remember 工具内化新知识
|
||||
2. 使用 MCP PromptX learn 工具学习后再内化
|
||||
3. 开始构建AI的专业知识体系`
|
||||
}
|
||||
}
|
||||
|
||||
const formattedMemories = this.formatRetrievedKnowledge(memories, query)
|
||||
|
||||
return `🧠 AI记忆体系 ${query ? `检索"${query}"` : '全部记忆'} (${memories.length}条):
|
||||
${formattedMemories}
|
||||
💡 记忆运用建议:
|
||||
1. 结合当前任务场景灵活运用
|
||||
2. 根据实际情况调整和变通
|
||||
3. 持续学习和增强记忆能力`
|
||||
} catch (error) {
|
||||
logger.error(`❌ [RecallCommand] 记忆检索失败: ${error.message}`)
|
||||
logger.debug(`🐛 [RecallCommand] 错误堆栈: ${error.stack}`)
|
||||
return `❌ 检索记忆时出错:${error.message}`
|
||||
}
|
||||
}
|
||||
|
||||
getPATEOAS (args) {
|
||||
const [query] = args
|
||||
const currentState = query ? `recalled-${query}` : 'recall-waiting'
|
||||
|
||||
return {
|
||||
currentState,
|
||||
availableTransitions: ['welcome', 'remember', 'learn', 'recall'],
|
||||
nextActions: [
|
||||
{
|
||||
name: '选择角色',
|
||||
description: '选择专业角色来应用检索到的知识',
|
||||
method: 'MCP PromptX welcome 工具'
|
||||
},
|
||||
{
|
||||
name: '记忆新知识',
|
||||
description: '继续内化更多专业知识',
|
||||
method: 'MCP PromptX remember 工具'
|
||||
},
|
||||
{
|
||||
name: '学习资源',
|
||||
description: '学习相关专业资源',
|
||||
method: 'MCP PromptX learn 工具'
|
||||
},
|
||||
{
|
||||
name: '继续检索',
|
||||
description: '检索其他相关记忆',
|
||||
method: 'MCP PromptX recall 工具'
|
||||
}
|
||||
],
|
||||
metadata: {
|
||||
query: query || null,
|
||||
resultCount: this.lastSearchCount || 0,
|
||||
searchTime: new Date().toISOString(),
|
||||
hasResults: (this.lastSearchCount || 0) > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有记忆(支持XML和Markdown格式,优先XML)
|
||||
*/
|
||||
async getAllMemories (query) {
|
||||
logger.step('🔧 [RecallCommand] 执行getAllMemories方法')
|
||||
|
||||
this.lastSearchCount = 0
|
||||
const memories = []
|
||||
|
||||
logger.debug('🔍 [RecallCommand] 初始化ResourceManager...')
|
||||
|
||||
// 确保ResourceManager已初始化(就像ActionCommand那样)
|
||||
if (!this.resourceManager.initialized) {
|
||||
logger.info('⚙️ [RecallCommand] ResourceManager未初始化,正在初始化...')
|
||||
await this.resourceManager.initializeWithNewArchitecture()
|
||||
logger.success('⚙️ [RecallCommand] ResourceManager初始化完成')
|
||||
}
|
||||
|
||||
// 通过ResourceManager获取项目路径(与ActionCommand一致)
|
||||
const projectPath = await this.getProjectPath()
|
||||
logger.info(`📍 [RecallCommand] 项目根路径: ${projectPath}`)
|
||||
|
||||
const memoryDir = path.join(projectPath, '.promptx', 'memory')
|
||||
logger.info(`📁 [RecallCommand] 记忆目录路径: ${memoryDir}`)
|
||||
|
||||
// 优先尝试XML格式
|
||||
const xmlFile = path.join(memoryDir, 'memory.xml')
|
||||
const legacyFile = path.join(memoryDir, 'declarative.md')
|
||||
|
||||
logger.debug(`📄 [RecallCommand] XML文件路径: ${xmlFile}`)
|
||||
logger.debug(`📄 [RecallCommand] Legacy文件路径: ${legacyFile}`)
|
||||
|
||||
try {
|
||||
// 优先读取XML格式
|
||||
if (await fs.pathExists(xmlFile)) {
|
||||
logger.info('📄 [RecallCommand] 检测到XML格式记忆文件,使用XML模式')
|
||||
const xmlMemories = await this.readXMLMemories(xmlFile, query)
|
||||
memories.push(...xmlMemories)
|
||||
logger.success(`📄 [RecallCommand] XML记忆读取完成 - ${xmlMemories.length} 条记忆`)
|
||||
} else if (await fs.pathExists(legacyFile)) {
|
||||
logger.info('📄 [RecallCommand] 检测到Legacy Markdown格式,使用兼容模式')
|
||||
// 向后兼容:读取legacy Markdown格式
|
||||
const legacyMemories = await this.readLegacyMemories(legacyFile, query)
|
||||
memories.push(...legacyMemories)
|
||||
logger.success(`📄 [RecallCommand] Legacy记忆读取完成 - ${legacyMemories.length} 条记忆`)
|
||||
} else {
|
||||
logger.warn('📄 [RecallCommand] 未找到任何记忆文件')
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`❌ [RecallCommand] 读取记忆文件时发生错误: ${error.message}`)
|
||||
logger.debug(`🐛 [RecallCommand] 读取错误堆栈: ${error.stack}`)
|
||||
}
|
||||
|
||||
this.lastSearchCount = memories.length
|
||||
logger.info(`📊 [RecallCommand] 最终记忆检索统计 - 总计: ${memories.length} 条`)
|
||||
|
||||
return memories
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目路径(复用ActionCommand逻辑)
|
||||
*/
|
||||
async getProjectPath() {
|
||||
logger.debug('📍 [RecallCommand] 获取项目路径...')
|
||||
|
||||
// 🔍 增加详细的路径诊断日志
|
||||
logger.warn('🔍 [RecallCommand-DIAGNOSIS] ===== 路径诊断开始 =====')
|
||||
logger.warn(`🔍 [RecallCommand-DIAGNOSIS] process.cwd(): ${process.cwd()}`)
|
||||
logger.warn(`🔍 [RecallCommand-DIAGNOSIS] process.argv: ${JSON.stringify(process.argv)}`)
|
||||
logger.warn(`🔍 [RecallCommand-DIAGNOSIS] PROMPTX_WORKSPACE: ${process.env.PROMPTX_WORKSPACE || 'undefined'}`)
|
||||
logger.warn(`🔍 [RecallCommand-DIAGNOSIS] WORKSPACE_FOLDER_PATHS: ${process.env.WORKSPACE_FOLDER_PATHS || 'undefined'}`)
|
||||
logger.warn(`🔍 [RecallCommand-DIAGNOSIS] PWD: ${process.env.PWD || 'undefined'}`)
|
||||
|
||||
// 使用DirectoryService统一获取项目路径(与InitCommand保持一致)
|
||||
const context = {
|
||||
startDir: process.cwd(),
|
||||
platform: process.platform,
|
||||
avoidUserHome: true
|
||||
}
|
||||
logger.warn(`🔍 [RecallCommand-DIAGNOSIS] DirectoryService context: ${JSON.stringify(context)}`)
|
||||
|
||||
const projectPath = await this.directoryService.getProjectRoot(context)
|
||||
logger.warn(`🔍 [RecallCommand-DIAGNOSIS] DirectoryService结果: ${projectPath}`)
|
||||
logger.warn('🔍 [RecallCommand-DIAGNOSIS] ===== 路径诊断结束 =====')
|
||||
|
||||
logger.debug(`📍 [RecallCommand] 项目路径解析结果: ${projectPath}`)
|
||||
|
||||
return projectPath
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析记忆块(新多行格式)
|
||||
*/
|
||||
parseMemoryBlocks (content) {
|
||||
const blocks = []
|
||||
const lines = content.split('\n')
|
||||
let currentBlock = []
|
||||
let inBlock = false
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.match(/^- \d{4}\/\d{2}\/\d{2} \d{2}:\d{2} START$/)) {
|
||||
// 开始新的记忆块
|
||||
if (inBlock && currentBlock.length > 0) {
|
||||
blocks.push(currentBlock.join('\n'))
|
||||
}
|
||||
currentBlock = [line]
|
||||
inBlock = true
|
||||
} else if (line === '- END' && inBlock) {
|
||||
// 结束当前记忆块
|
||||
currentBlock.push(line)
|
||||
blocks.push(currentBlock.join('\n'))
|
||||
currentBlock = []
|
||||
inBlock = false
|
||||
} else if (inBlock) {
|
||||
// 记忆块内容
|
||||
currentBlock.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理未结束的块
|
||||
if (inBlock && currentBlock.length > 0) {
|
||||
blocks.push(currentBlock.join('\n'))
|
||||
}
|
||||
|
||||
return blocks
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析单个记忆块
|
||||
*/
|
||||
parseMemoryBlock (blockContent) {
|
||||
const lines = blockContent.split('\n')
|
||||
|
||||
// 解析开始行:- 2025/06/15 15:58 START
|
||||
const startLine = lines[0]
|
||||
const startMatch = startLine.match(/^- (\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}) START$/)
|
||||
if (!startMatch) return null
|
||||
|
||||
const timestamp = startMatch[1]
|
||||
|
||||
// 查找标签行:--tags xxx
|
||||
let tagsLine = ''
|
||||
let contentLines = []
|
||||
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const line = lines[i]
|
||||
if (line.startsWith('--tags ')) {
|
||||
tagsLine = line
|
||||
} else if (line !== '- END') {
|
||||
contentLines.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
// 提取内容(去除空行)
|
||||
const content = contentLines.join('\n').trim()
|
||||
|
||||
// 解析标签
|
||||
let tags = []
|
||||
if (tagsLine) {
|
||||
const tagsContent = tagsLine.replace('--tags ', '')
|
||||
const hashTags = tagsContent.match(/#[^\s]+/g) || []
|
||||
const regularTags = tagsContent.replace(/#[^\s]+/g, '').trim().split(/\s+/).filter(t => t)
|
||||
tags = [...regularTags, ...hashTags]
|
||||
}
|
||||
|
||||
return {
|
||||
timestamp,
|
||||
content,
|
||||
tags,
|
||||
source: 'memory'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析记忆行(向下兼容旧格式)
|
||||
*/
|
||||
parseMemoryLine (line) {
|
||||
// 修复正则表达式,适配实际的记忆格式
|
||||
// 格式:- 2025/05/31 14:30 内容 --tags 标签 ##分类 #评分:8 #有效期:长期
|
||||
const match = line.match(/^- (\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}) (.+)$/)
|
||||
if (!match) return null
|
||||
|
||||
const [, timestamp, contentAndTags] = match
|
||||
|
||||
// 分离内容和标签
|
||||
let content = contentAndTags
|
||||
let tags = []
|
||||
|
||||
// 提取 --tags 后面的内容
|
||||
const tagsMatch = contentAndTags.match(/--tags\s+(.*)/)
|
||||
if (tagsMatch) {
|
||||
const beforeTags = contentAndTags.substring(0, contentAndTags.indexOf('--tags')).trim()
|
||||
content = beforeTags
|
||||
|
||||
// 解析标签部分,包括 --tags 后的内容和 # 开头的标签
|
||||
const tagsContent = tagsMatch[1]
|
||||
const hashTags = tagsContent.match(/#[^\s]+/g) || []
|
||||
const regularTags = tagsContent.replace(/#[^\s]+/g, '').trim().split(/\s+/).filter(t => t)
|
||||
|
||||
tags = [...regularTags, ...hashTags]
|
||||
} else {
|
||||
// 如果没有 --tags,检查是否有直接的 # 标签
|
||||
const hashTags = contentAndTags.match(/#[^\s]+/g) || []
|
||||
if (hashTags.length > 0) {
|
||||
content = contentAndTags.replace(/#[^\s]+/g, '').trim()
|
||||
tags = hashTags
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
timestamp,
|
||||
content,
|
||||
tags,
|
||||
source: 'memory'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查记忆是否匹配查询 - 增强版匹配算法
|
||||
*/
|
||||
matchesMemory (memory, query) {
|
||||
if (!query) return true
|
||||
|
||||
logger.debug(`🎯 [RecallCommand] 开始匹配检查 - 查询: "${query}", 记忆: "${memory.content.substring(0, 30)}..."`)
|
||||
|
||||
const lowerQuery = query.toLowerCase()
|
||||
const lowerContent = memory.content.toLowerCase()
|
||||
|
||||
// 1. 完全匹配 - 最高优先级
|
||||
if (lowerContent.includes(lowerQuery) ||
|
||||
memory.tags.some(tag => tag.toLowerCase().includes(lowerQuery))) {
|
||||
logger.debug(`✅ [RecallCommand] 完全匹配成功`)
|
||||
return true
|
||||
}
|
||||
|
||||
// 2. 分词匹配 - 支持多关键词组合查询
|
||||
const queryWords = lowerQuery.split(/\s+/).filter(word => word.length > 1)
|
||||
if (queryWords.length > 1) {
|
||||
const matchedWords = queryWords.filter(word =>
|
||||
lowerContent.includes(word) ||
|
||||
memory.tags.some(tag => tag.toLowerCase().includes(word))
|
||||
)
|
||||
// 如果匹配了一半以上的关键词,认为相关
|
||||
if (matchedWords.length >= Math.ceil(queryWords.length / 2)) {
|
||||
logger.debug(`✅ [RecallCommand] 分词匹配成功 - 匹配词数: ${matchedWords.length}/${queryWords.length}`)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 模糊匹配 - 支持常见同义词和缩写
|
||||
const synonyms = this.getSynonyms(lowerQuery)
|
||||
for (const synonym of synonyms) {
|
||||
if (lowerContent.includes(synonym) ||
|
||||
memory.tags.some(tag => tag.toLowerCase().includes(synonym))) {
|
||||
logger.debug(`✅ [RecallCommand] 同义词匹配成功 - 同义词: "${synonym}"`)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug(`❌ [RecallCommand] 无匹配`)
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询词的同义词和相关词
|
||||
*/
|
||||
getSynonyms (query) {
|
||||
const synonymMap = {
|
||||
'mcp': ['model context protocol', '工具'],
|
||||
'promptx': ['prompt-x', '提示词'],
|
||||
'测试': ['test', 'testing', 'qa', '质量保证'],
|
||||
'工具': ['tool', 'mcp', '功能'],
|
||||
'记忆': ['memory', 'recall', '回忆'],
|
||||
'学习': ['learn', 'study', '学会'],
|
||||
'角色': ['role', 'character', '专家'],
|
||||
'产品': ['product', 'pm', '产品经理'],
|
||||
'开发': ['develop', 'dev', 'coding', '编程'],
|
||||
'前端': ['frontend', 'fe', 'ui'],
|
||||
'后端': ['backend', 'be', 'api', '服务端'],
|
||||
'github': ['git', '代码仓库', '版本控制'],
|
||||
'issue': ['问题', 'bug', '需求'],
|
||||
'敏捷': ['agile', 'scrum', '迭代']
|
||||
}
|
||||
|
||||
const result = [query] // 包含原查询词
|
||||
|
||||
// 查找直接同义词
|
||||
if (synonymMap[query]) {
|
||||
result.push(...synonymMap[query])
|
||||
}
|
||||
|
||||
// 查找包含关系的同义词
|
||||
for (const [key, values] of Object.entries(synonymMap)) {
|
||||
if (key.includes(query) || query.includes(key)) {
|
||||
result.push(key, ...values)
|
||||
}
|
||||
if (values.some(v => v.includes(query) || query.includes(v))) {
|
||||
result.push(key, ...values)
|
||||
}
|
||||
}
|
||||
|
||||
return [...new Set(result)] // 去重
|
||||
}
|
||||
|
||||
matchesQuery (content, query) {
|
||||
const lowerContent = content.toLowerCase()
|
||||
const lowerQuery = query.toLowerCase()
|
||||
const keywords = lowerQuery.split(/\s+/)
|
||||
|
||||
return keywords.some(keyword => lowerContent.includes(keyword))
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化检索到的记忆(支持多行显示)
|
||||
*/
|
||||
formatRetrievedKnowledge (memories, query) {
|
||||
return memories.map((memory, index) => {
|
||||
// 保持完整的记忆内容,不进行截断
|
||||
// 陈述性记忆的完整性对于系统价值至关重要
|
||||
let content = memory.content
|
||||
|
||||
// 只对格式进行优化,但不截断内容
|
||||
// 确保换行符正确显示
|
||||
content = content.trim()
|
||||
|
||||
return `📝 ${index + 1}. **记忆** (${memory.timestamp})
|
||||
${content}
|
||||
${memory.tags.slice(0, 8).join(' ')}
|
||||
---`
|
||||
}).join('\n')
|
||||
}
|
||||
|
||||
extractDomain (query) {
|
||||
const domains = ['copywriter', 'scrum', 'developer', 'test', 'prompt']
|
||||
const lowerQuery = query.toLowerCase()
|
||||
|
||||
return domains.find(domain => lowerQuery.includes(domain)) || null
|
||||
}
|
||||
|
||||
getRelatedQuery (query) {
|
||||
const relatedMap = {
|
||||
copywriter: 'marketing',
|
||||
scrum: 'agile',
|
||||
frontend: 'ui',
|
||||
backend: 'api',
|
||||
test: 'qa'
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(relatedMap)) {
|
||||
if (query.toLowerCase().includes(key)) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
return query + '-advanced'
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取XML格式记忆
|
||||
*/
|
||||
async readXMLMemories (xmlFile, query) {
|
||||
logger.step('📄 [RecallCommand] 开始读取XML格式记忆')
|
||||
|
||||
const memories = []
|
||||
|
||||
try {
|
||||
const xmlContent = await fs.readFile(xmlFile, 'utf8')
|
||||
logger.info(`📄 [RecallCommand] XML文件读取成功 - 文件大小: ${xmlContent.length} 字符`)
|
||||
|
||||
const xmlMemories = this.parseXMLMemories(xmlContent)
|
||||
logger.info(`📄 [RecallCommand] XML解析完成 - 解析出 ${xmlMemories.length} 条记忆`)
|
||||
|
||||
for (const memory of xmlMemories) {
|
||||
if (!query || this.matchesMemory(memory, query)) {
|
||||
memories.push(memory)
|
||||
if (query) {
|
||||
logger.debug(`🎯 [RecallCommand] 记忆匹配成功: "${memory.content.substring(0, 30)}..."`)
|
||||
}
|
||||
} else if (query) {
|
||||
logger.debug(`❌ [RecallCommand] 记忆不匹配: "${memory.content.substring(0, 30)}..."`)
|
||||
}
|
||||
}
|
||||
|
||||
logger.success(`📄 [RecallCommand] XML记忆筛选完成 - 匹配: ${memories.length}/${xmlMemories.length} 条`)
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`❌ [RecallCommand] XML记忆读取失败: ${error.message}`)
|
||||
logger.debug(`🐛 [RecallCommand] XML读取错误堆栈: ${error.stack}`)
|
||||
}
|
||||
|
||||
return memories
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取legacy Markdown格式记忆
|
||||
*/
|
||||
async readLegacyMemories (legacyFile, query) {
|
||||
logger.step('📄 [RecallCommand] 开始读取Legacy Markdown格式记忆')
|
||||
|
||||
const memories = []
|
||||
|
||||
try {
|
||||
const content = await fs.readFile(legacyFile, 'utf-8')
|
||||
logger.info(`📄 [RecallCommand] Legacy文件读取成功 - 文件大小: ${content.length} 字符`)
|
||||
|
||||
const memoryBlocks = this.parseMemoryBlocks(content)
|
||||
logger.info(`📄 [RecallCommand] Legacy解析完成 - 解析出 ${memoryBlocks.length} 个记忆块`)
|
||||
|
||||
for (const memoryBlock of memoryBlocks) {
|
||||
const memory = this.parseMemoryBlock(memoryBlock)
|
||||
if (memory && (!query || this.matchesMemory(memory, query))) {
|
||||
memories.push(memory)
|
||||
if (query) {
|
||||
logger.debug(`🎯 [RecallCommand] Legacy记忆匹配成功: "${memory.content.substring(0, 30)}..."`)
|
||||
}
|
||||
} else if (memory && query) {
|
||||
logger.debug(`❌ [RecallCommand] Legacy记忆不匹配: "${memory.content.substring(0, 30)}..."`)
|
||||
}
|
||||
}
|
||||
|
||||
logger.success(`📄 [RecallCommand] Legacy记忆筛选完成 - 匹配: ${memories.length}/${memoryBlocks.length} 条`)
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`❌ [RecallCommand] Legacy记忆读取失败: ${error.message}`)
|
||||
logger.debug(`🐛 [RecallCommand] Legacy读取错误堆栈: ${error.stack}`)
|
||||
}
|
||||
|
||||
return memories
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析XML格式记忆
|
||||
*/
|
||||
parseXMLMemories (xmlContent) {
|
||||
logger.debug('🔍 [RecallCommand] 开始解析XML记忆内容')
|
||||
|
||||
const memories = []
|
||||
|
||||
try {
|
||||
// 简单的XML解析(不依赖外部库)
|
||||
const itemRegex = /<item\s+id="([^"]*?)"\s+time="([^"]*?)">(.*?)<\/item>/gs
|
||||
let match
|
||||
let itemCount = 0
|
||||
|
||||
while ((match = itemRegex.exec(xmlContent)) !== null) {
|
||||
itemCount++
|
||||
const [, id, timestamp, itemContent] = match
|
||||
|
||||
logger.debug(`🔍 [RecallCommand] 解析记忆项 ${itemCount}: ID=${id}, 时间=${timestamp}`)
|
||||
|
||||
// 解析内容和标签
|
||||
const contentMatch = itemContent.match(/<content>(.*?)<\/content>/s)
|
||||
const tagsMatch = itemContent.match(/<tags>(.*?)<\/tags>/s)
|
||||
|
||||
if (contentMatch) {
|
||||
const content = this.unescapeXML(contentMatch[1].trim())
|
||||
const tagsString = tagsMatch ? this.unescapeXML(tagsMatch[1].trim()) : ''
|
||||
const tags = tagsString ? tagsString.split(/\s+/).filter(t => t) : []
|
||||
|
||||
logger.debug(`🔍 [RecallCommand] 记忆项内容: "${content.substring(0, 50)}${content.length > 50 ? '...' : ''}"`)
|
||||
logger.debug(`🔍 [RecallCommand] 记忆项标签: [${tags.join(', ')}]`)
|
||||
|
||||
memories.push({
|
||||
id,
|
||||
timestamp,
|
||||
content,
|
||||
tags,
|
||||
source: 'xml'
|
||||
})
|
||||
} else {
|
||||
logger.warn(`⚠️ [RecallCommand] 记忆项 ${itemCount} 缺少content标签`)
|
||||
}
|
||||
}
|
||||
|
||||
logger.success(`🔍 [RecallCommand] XML解析完成 - 成功解析 ${memories.length} 条记忆`)
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`❌ [RecallCommand] XML解析失败: ${error.message}`)
|
||||
logger.debug(`🐛 [RecallCommand] XML解析错误堆栈: ${error.stack}`)
|
||||
}
|
||||
|
||||
return memories
|
||||
}
|
||||
|
||||
/**
|
||||
* XML反转义函数
|
||||
*/
|
||||
unescapeXML (text) {
|
||||
if (typeof text !== 'string') {
|
||||
return text
|
||||
}
|
||||
return text
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(/&/g, '&') // 必须最后处理
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RecallCommand
|
||||
587
src/lib/core/pouch/commands/_deprecated/RememberCommand_v1.js
Normal file
587
src/lib/core/pouch/commands/_deprecated/RememberCommand_v1.js
Normal file
@ -0,0 +1,587 @@
|
||||
const BasePouchCommand = require('../BasePouchCommand')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const { COMMANDS } = require('../../../../constants')
|
||||
const { getGlobalResourceManager } = require('../../resource')
|
||||
const { getDirectoryService } = require('../../../utils/DirectoryService')
|
||||
const logger = require('../../../utils/logger')
|
||||
|
||||
/**
|
||||
* 记忆保存锦囊命令
|
||||
* 负责将知识、经验和最佳实践保存到记忆库中
|
||||
* 支持XML格式和Markdown格式,自动迁移legacy数据
|
||||
*/
|
||||
class RememberCommand extends BasePouchCommand {
|
||||
constructor () {
|
||||
super()
|
||||
// 复用ActionCommand的ResourceManager方式
|
||||
this.resourceManager = getGlobalResourceManager()
|
||||
this.directoryService = getDirectoryService()
|
||||
}
|
||||
|
||||
getPurpose () {
|
||||
return '增强AI长期记忆能力,主动内化专业知识、最佳实践和项目经验'
|
||||
}
|
||||
|
||||
async getContent (args) {
|
||||
const content = args.join(' ')
|
||||
|
||||
if (!content) {
|
||||
return this.getUsageHelp()
|
||||
}
|
||||
|
||||
try {
|
||||
logger.step('🧠 [RememberCommand] 开始记忆保存流程')
|
||||
logger.info(`📝 [RememberCommand] 记忆内容: "${content.substring(0, 50)}${content.length > 50 ? '...' : ''}"`)
|
||||
|
||||
const memoryEntry = await this.saveMemory(content)
|
||||
|
||||
logger.success(`✅ [RememberCommand] 记忆保存完成 - 格式: ${memoryEntry.format}, 路径: ${memoryEntry.filePath}`)
|
||||
return this.formatSaveResponse(content, memoryEntry)
|
||||
} catch (error) {
|
||||
logger.error(`❌ [RememberCommand] 记忆保存失败: ${error.message}`)
|
||||
logger.debug(`🐛 [RememberCommand] 错误堆栈: ${error.stack}`)
|
||||
|
||||
return `❌ 记忆内化失败:${error.message}
|
||||
|
||||
💡 可能的原因:
|
||||
- AI记忆体系目录权限不足
|
||||
- 磁盘空间不够
|
||||
- 记忆内容格式问题
|
||||
|
||||
🔧 解决方案:
|
||||
1. 检查 .promptx 目录权限
|
||||
2. 确保磁盘空间充足
|
||||
3. 检查记忆内容是否包含特殊字符`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将知识内化到AI记忆体系(XML格式优先)
|
||||
*/
|
||||
async saveMemory (value) {
|
||||
logger.step('🔧 [RememberCommand] 执行saveMemory方法')
|
||||
|
||||
// 1. 确保AI记忆体系目录存在
|
||||
logger.info('📁 [RememberCommand] 确保记忆目录存在...')
|
||||
const memoryDir = await this.ensureMemoryDirectory()
|
||||
logger.info(`📁 [RememberCommand] 记忆目录路径: ${memoryDir}`)
|
||||
|
||||
// 2. 检查是否需要从legacy格式迁移
|
||||
logger.info('🔄 [RememberCommand] 检查legacy数据迁移需求...')
|
||||
await this.migrateLegacyMemoriesIfNeeded(memoryDir)
|
||||
|
||||
// 3. 使用XML格式保存记忆
|
||||
const xmlFile = path.join(memoryDir, 'memory.xml')
|
||||
logger.info(`📄 [RememberCommand] XML文件路径: ${xmlFile}`)
|
||||
|
||||
// 4. 格式化为XML记忆项
|
||||
logger.info('🏷️ [RememberCommand] 格式化XML记忆项...')
|
||||
const memoryItem = this.formatXMLMemoryItem(value)
|
||||
logger.debug(`🏷️ [RememberCommand] 记忆项ID: ${memoryItem.id}, 时间戳: ${memoryItem.timestamp}`)
|
||||
logger.debug(`🏷️ [RememberCommand] 记忆标签: ${memoryItem.rawTags}`)
|
||||
|
||||
// 5. 追加到XML文件
|
||||
logger.info('💾 [RememberCommand] 保存到XML文件...')
|
||||
const action = await this.appendToXMLFile(xmlFile, memoryItem)
|
||||
logger.success(`💾 [RememberCommand] XML保存操作: ${action}`)
|
||||
|
||||
return {
|
||||
value,
|
||||
filePath: xmlFile,
|
||||
action,
|
||||
timestamp: new Date().toISOString(),
|
||||
format: 'xml'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保AI记忆体系目录存在(使用ResourceManager路径获取)
|
||||
*/
|
||||
async ensureMemoryDirectory () {
|
||||
logger.debug('🔍 [RememberCommand] 初始化ResourceManager...')
|
||||
|
||||
// 确保ResourceManager已初始化(就像ActionCommand那样)
|
||||
if (!this.resourceManager.initialized) {
|
||||
logger.info('⚙️ [RememberCommand] ResourceManager未初始化,正在初始化...')
|
||||
await this.resourceManager.initializeWithNewArchitecture()
|
||||
logger.success('⚙️ [RememberCommand] ResourceManager初始化完成')
|
||||
}
|
||||
|
||||
// 通过ResourceManager获取项目路径(与ActionCommand一致)
|
||||
const projectPath = await this.getProjectPath()
|
||||
logger.info(`📍 [RememberCommand] 项目根路径: ${projectPath}`)
|
||||
|
||||
const memoryDir = path.join(projectPath, '.promptx', 'memory')
|
||||
logger.info(`📁 [RememberCommand] 创建记忆目录: ${memoryDir}`)
|
||||
|
||||
await fs.ensureDir(memoryDir)
|
||||
logger.success(`📁 [RememberCommand] 记忆目录确保完成: ${memoryDir}`)
|
||||
|
||||
return memoryDir
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目路径(复用ActionCommand逻辑)
|
||||
*/
|
||||
async getProjectPath() {
|
||||
logger.debug('📍 [RememberCommand] 获取项目路径...')
|
||||
|
||||
// 🔍 增加详细的路径诊断日志
|
||||
logger.warn('🔍 [RememberCommand-DIAGNOSIS] ===== 路径诊断开始 =====')
|
||||
logger.warn(`🔍 [RememberCommand-DIAGNOSIS] process.cwd(): ${process.cwd()}`)
|
||||
logger.warn(`🔍 [RememberCommand-DIAGNOSIS] process.argv: ${JSON.stringify(process.argv)}`)
|
||||
logger.warn(`🔍 [RememberCommand-DIAGNOSIS] PROMPTX_WORKSPACE: ${process.env.PROMPTX_WORKSPACE || 'undefined'}`)
|
||||
logger.warn(`🔍 [RememberCommand-DIAGNOSIS] WORKSPACE_FOLDER_PATHS: ${process.env.WORKSPACE_FOLDER_PATHS || 'undefined'}`)
|
||||
logger.warn(`🔍 [RememberCommand-DIAGNOSIS] PWD: ${process.env.PWD || 'undefined'}`)
|
||||
|
||||
// 使用DirectoryService统一获取项目路径(与InitCommand保持一致)
|
||||
const context = {
|
||||
startDir: process.cwd(),
|
||||
platform: process.platform,
|
||||
avoidUserHome: true
|
||||
}
|
||||
logger.warn(`🔍 [RememberCommand-DIAGNOSIS] DirectoryService context: ${JSON.stringify(context)}`)
|
||||
|
||||
const projectPath = await this.directoryService.getProjectRoot(context)
|
||||
logger.warn(`🔍 [RememberCommand-DIAGNOSIS] DirectoryService结果: ${projectPath}`)
|
||||
logger.warn('🔍 [RememberCommand-DIAGNOSIS] ===== 路径诊断结束 =====')
|
||||
|
||||
logger.debug(`📍 [RememberCommand] 项目路径解析结果: ${projectPath}`)
|
||||
|
||||
return projectPath
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化为XML记忆项
|
||||
*/
|
||||
formatXMLMemoryItem (value) {
|
||||
logger.debug('🏷️ [RememberCommand] 开始格式化XML记忆项...')
|
||||
|
||||
const now = new Date()
|
||||
const timestamp = `${now.getFullYear()}/${String(now.getMonth() + 1).padStart(2, '0')}/${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
|
||||
const id = `mem_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
|
||||
logger.debug(`🏷️ [RememberCommand] 生成记忆ID: ${id}`)
|
||||
logger.debug(`🏷️ [RememberCommand] 时间戳: ${timestamp}`)
|
||||
|
||||
// 自动生成标签
|
||||
const tags = this.generateTags(value)
|
||||
logger.debug(`🏷️ [RememberCommand] 自动生成标签: ${tags}`)
|
||||
|
||||
// XML转义
|
||||
const escapedContent = this.escapeXML(value)
|
||||
const escapedTags = this.escapeXML(tags)
|
||||
|
||||
logger.debug(`🏷️ [RememberCommand] XML转义完成 - 内容长度: ${escapedContent.length}`)
|
||||
if (escapedContent !== value) {
|
||||
logger.info('🔄 [RememberCommand] 检测到特殊字符,已进行XML转义')
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
timestamp,
|
||||
content: escapedContent,
|
||||
tags: escapedTags,
|
||||
rawContent: value,
|
||||
rawTags: tags
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* XML转义函数
|
||||
*/
|
||||
escapeXML (text) {
|
||||
if (typeof text !== 'string') {
|
||||
return text
|
||||
}
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化内容缩进(添加适当的缩进让XML更美观)
|
||||
*/
|
||||
formatContentWithIndent (content, indentLevel = 3) {
|
||||
if (typeof content !== 'string') {
|
||||
return content
|
||||
}
|
||||
|
||||
// 基础缩进字符串(每级2个空格)
|
||||
const baseIndent = ' '.repeat(indentLevel)
|
||||
|
||||
// 分割内容为行
|
||||
const lines = content.split('\n')
|
||||
|
||||
// 格式化每一行,添加缩进
|
||||
const formattedLines = lines.map((line, index) => {
|
||||
// 第一行和最后一行特殊处理
|
||||
if (index === 0 && index === lines.length - 1) {
|
||||
// 单行内容
|
||||
return line.trim() ? `\n${baseIndent}${line.trim()}\n ` : line
|
||||
} else if (index === 0) {
|
||||
// 第一行
|
||||
return line.trim() ? `\n${baseIndent}${line.trim()}` : `\n${baseIndent}`
|
||||
} else if (index === lines.length - 1) {
|
||||
// 最后一行
|
||||
return line.trim() ? `${baseIndent}${line.trim()}\n ` : `\n `
|
||||
} else {
|
||||
// 中间行
|
||||
return line.trim() ? `${baseIndent}${line.trim()}` : baseIndent.substring(2) // 空行保持基础缩进
|
||||
}
|
||||
})
|
||||
|
||||
return formattedLines.join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加到XML文件
|
||||
*/
|
||||
async appendToXMLFile (xmlFile, memoryItem) {
|
||||
logger.debug(`💾 [RememberCommand] 检查XML文件是否存在: ${xmlFile}`)
|
||||
|
||||
// 格式化内容缩进
|
||||
const formattedContent = this.formatContentWithIndent(memoryItem.content)
|
||||
|
||||
// 检查文件是否存在以及是否为空
|
||||
const fileExists = await fs.pathExists(xmlFile)
|
||||
let fileIsEmpty = false
|
||||
|
||||
if (fileExists) {
|
||||
const stats = await fs.stat(xmlFile)
|
||||
fileIsEmpty = stats.size === 0
|
||||
logger.debug(`💾 [RememberCommand] XML文件状态检查 - 存在: ${fileExists}, 大小: ${stats.size}字节, 为空: ${fileIsEmpty}`)
|
||||
}
|
||||
|
||||
// 初始化XML文件(如果不存在或为空)
|
||||
if (!fileExists || fileIsEmpty) {
|
||||
if (fileIsEmpty) {
|
||||
logger.info('📄 [RememberCommand] XML文件存在但为空,重新初始化...')
|
||||
} else {
|
||||
logger.info('📄 [RememberCommand] XML文件不存在,创建新文件...')
|
||||
}
|
||||
|
||||
const initialXML = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<memory>
|
||||
<item id="${memoryItem.id}" time="${memoryItem.timestamp}">
|
||||
<content>${formattedContent}</content>
|
||||
<tags>${memoryItem.tags}</tags>
|
||||
</item>
|
||||
</memory>`
|
||||
|
||||
await fs.writeFile(xmlFile, initialXML, 'utf8')
|
||||
logger.success('📄 [RememberCommand] XML文件初始化完成')
|
||||
logger.debug(`📄 [RememberCommand] 初始XML内容长度: ${initialXML.length}字符`)
|
||||
|
||||
return 'created'
|
||||
}
|
||||
|
||||
logger.info('📄 [RememberCommand] XML文件已存在且有内容,追加新记忆项...')
|
||||
|
||||
// 读取现有XML并添加新项
|
||||
const content = await fs.readFile(xmlFile, 'utf8')
|
||||
logger.debug(`📄 [RememberCommand] 读取现有XML文件 - 长度: ${content.length}字符`)
|
||||
|
||||
// 验证XML文件格式
|
||||
if (!content.includes('</memory>')) {
|
||||
logger.warn('📄 [RememberCommand] XML文件格式异常,缺少</memory>标签,重新初始化...')
|
||||
// 重新初始化文件
|
||||
const initialXML = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<memory>
|
||||
<item id="${memoryItem.id}" time="${memoryItem.timestamp}">
|
||||
<content>${formattedContent}</content>
|
||||
<tags>${memoryItem.tags}</tags>
|
||||
</item>
|
||||
</memory>`
|
||||
|
||||
await fs.writeFile(xmlFile, initialXML, 'utf8')
|
||||
logger.success('📄 [RememberCommand] XML文件重新初始化完成')
|
||||
return 'created'
|
||||
}
|
||||
|
||||
// 找到</memory>标签的位置,在它之前插入新的记忆项
|
||||
const newItem = ` <item id="${memoryItem.id}" time="${memoryItem.timestamp}">
|
||||
<content>${formattedContent}</content>
|
||||
<tags>${memoryItem.tags}</tags>
|
||||
</item>`
|
||||
|
||||
const updatedContent = content.replace('</memory>', `${newItem}
|
||||
</memory>`)
|
||||
|
||||
logger.debug(`📄 [RememberCommand] 新XML内容长度: ${updatedContent.length}字符`)
|
||||
logger.debug(`📄 [RememberCommand] 新增记忆项ID: ${memoryItem.id}`)
|
||||
|
||||
await fs.writeFile(xmlFile, updatedContent, 'utf8')
|
||||
logger.success('📄 [RememberCommand] XML文件追加完成')
|
||||
|
||||
return 'created'
|
||||
}
|
||||
|
||||
/**
|
||||
* 从legacy Markdown格式迁移到XML格式
|
||||
*/
|
||||
async migrateLegacyMemoriesIfNeeded (memoryDir) {
|
||||
const legacyFile = path.join(memoryDir, 'declarative.md')
|
||||
const xmlFile = path.join(memoryDir, 'memory.xml')
|
||||
const backupFile = path.join(memoryDir, 'declarative.md.bak')
|
||||
|
||||
logger.debug(`🔄 [RememberCommand] 检查迁移需求 - legacy: ${legacyFile}, xml: ${xmlFile}`)
|
||||
|
||||
// 如果XML文件已存在,说明已经迁移过了
|
||||
if (await fs.pathExists(xmlFile)) {
|
||||
logger.debug('🔄 [RememberCommand] XML文件已存在,无需迁移')
|
||||
return
|
||||
}
|
||||
|
||||
// 如果legacy文件不存在,无需迁移
|
||||
if (!await fs.pathExists(legacyFile)) {
|
||||
logger.debug('🔄 [RememberCommand] Legacy文件不存在,无需迁移')
|
||||
return
|
||||
}
|
||||
|
||||
logger.step('🔄 [RememberCommand] 正在迁移记忆数据从Markdown到XML格式...')
|
||||
|
||||
try {
|
||||
// 读取legacy文件
|
||||
const legacyContent = await fs.readFile(legacyFile, 'utf8')
|
||||
logger.info(`🔄 [RememberCommand] 读取legacy文件 - 长度: ${legacyContent.length}字符`)
|
||||
|
||||
// 解析legacy记忆
|
||||
const legacyMemories = this.parseLegacyMemories(legacyContent)
|
||||
logger.info(`🔄 [RememberCommand] 解析到 ${legacyMemories.length} 条legacy记忆`)
|
||||
|
||||
// 创建XML文件
|
||||
let xmlContent = '<?xml version="1.0" encoding="UTF-8"?>\n<memory>\n'
|
||||
|
||||
for (const memory of legacyMemories) {
|
||||
const escapedContent = this.escapeXML(memory.content)
|
||||
const escapedTags = this.escapeXML(memory.tags.join(' '))
|
||||
const id = `legacy_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
|
||||
logger.debug(`🔄 [RememberCommand] 迁移记忆项: ${memory.content.substring(0, 30)}...`)
|
||||
|
||||
xmlContent += ` <item id="${id}" time="${memory.timestamp}">
|
||||
<content>${escapedContent}</content>
|
||||
<tags>${escapedTags}</tags>
|
||||
</item>
|
||||
`
|
||||
}
|
||||
|
||||
xmlContent += '</memory>'
|
||||
|
||||
// 写入XML文件
|
||||
await fs.writeFile(xmlFile, xmlContent, 'utf8')
|
||||
logger.success(`🔄 [RememberCommand] XML文件创建成功 - 长度: ${xmlContent.length}字符`)
|
||||
|
||||
// 备份legacy文件
|
||||
await fs.move(legacyFile, backupFile)
|
||||
logger.success(`🔄 [RememberCommand] Legacy文件备份到: ${backupFile}`)
|
||||
|
||||
logger.success(`🔄 [RememberCommand] 成功迁移${legacyMemories.length}条记忆到XML格式`)
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`🔄 [RememberCommand] 记忆迁移失败: ${error.message}`)
|
||||
logger.debug(`🔄 [RememberCommand] 迁移错误堆栈: ${error.stack}`)
|
||||
throw new Error(`记忆迁移失败: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析legacy Markdown格式的记忆
|
||||
*/
|
||||
parseLegacyMemories (content) {
|
||||
const memories = []
|
||||
const lines = content.split('\n')
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmedLine = line.trim()
|
||||
|
||||
// 解析标准格式:- 2025/01/15 14:30 内容 #标签 #评分:8 #有效期:长期
|
||||
const match = trimmedLine.match(/^- (\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}) (.+)$/)
|
||||
if (match) {
|
||||
const [, timestamp, contentAndTags] = match
|
||||
|
||||
// 分离内容和标签
|
||||
let content = contentAndTags
|
||||
let tags = []
|
||||
|
||||
// 提取 --tags 后面的内容
|
||||
const tagsMatch = contentAndTags.match(/--tags\s+(.*)/)
|
||||
if (tagsMatch) {
|
||||
content = contentAndTags.substring(0, contentAndTags.indexOf('--tags')).trim()
|
||||
const tagsContent = tagsMatch[1]
|
||||
const hashTags = tagsContent.match(/#[^\s]+/g) || []
|
||||
const regularTags = tagsContent.replace(/#[^\s]+/g, '').trim().split(/\s+/).filter(t => t)
|
||||
tags = [...regularTags, ...hashTags]
|
||||
} else {
|
||||
// 如果没有 --tags,检查是否有直接的 # 标签
|
||||
const hashTags = contentAndTags.match(/#[^\s]+/g) || []
|
||||
if (hashTags.length > 0) {
|
||||
content = contentAndTags.replace(/#[^\s]+/g, '').trim()
|
||||
tags = hashTags
|
||||
}
|
||||
}
|
||||
|
||||
memories.push({
|
||||
timestamp,
|
||||
content,
|
||||
tags
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return memories
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动生成标签
|
||||
*/
|
||||
generateTags (value) {
|
||||
const tags = []
|
||||
const lowerValue = value.toLowerCase()
|
||||
|
||||
// 基于value生成标签
|
||||
if (lowerValue.includes('最佳实践') || lowerValue.includes('规则')) tags.push('#最佳实践')
|
||||
if (lowerValue.includes('流程') || lowerValue.includes('步骤')) tags.push('#流程管理')
|
||||
if (lowerValue.includes('命令') || lowerValue.includes('工具')) tags.push('#工具使用')
|
||||
|
||||
return tags.join(' ') || '#其他'
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化保存响应(XML版本)
|
||||
*/
|
||||
formatSaveResponse (value, memoryEntry) {
|
||||
const { action, timestamp, format, filePath } = memoryEntry
|
||||
|
||||
const actionLabels = {
|
||||
created: '✅ AI已内化新记忆(XML格式)'
|
||||
}
|
||||
|
||||
return `${actionLabels[action]}:${value}
|
||||
|
||||
## 📋 记忆详情
|
||||
- **存储格式**: ${format.toUpperCase()}
|
||||
- **内化时间**: ${timestamp.split('T')[0]}
|
||||
- **存储路径**: ${path.basename(filePath)}
|
||||
- **知识内容**: ${value.length > 100 ? value.substring(0, 100) + '...' : value}
|
||||
|
||||
## 🎯 能力增强效果
|
||||
- ✅ **知识已内化到AI长期记忆(XML结构化存储)**
|
||||
- ✅ **支持精确的内容检索和标签搜索**
|
||||
- ✅ **可通过recall命令主动检索**
|
||||
- ✅ **支持跨会话记忆保持**
|
||||
- ✅ **自动从legacy格式迁移**
|
||||
|
||||
## 🔄 下一步行动:
|
||||
- 记忆检索: 使用 MCP PromptX recall 工具验证知识内化效果
|
||||
- 能力强化: 使用 MCP PromptX learn 工具学习相关知识增强记忆
|
||||
- 应用实践: 使用 MCP PromptX action 工具在实际场景中运用记忆
|
||||
|
||||
📍 当前状态:memory_saved_xml`
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取使用帮助
|
||||
*/
|
||||
getUsageHelp () {
|
||||
return `🧠 **Remember锦囊 - AI记忆增强系统(XML版本)**
|
||||
|
||||
## 📖 基本用法
|
||||
通过 MCP PromptX remember 工具内化知识
|
||||
|
||||
## 💡 记忆内化示例
|
||||
|
||||
### 📝 AI记忆内化
|
||||
AI学习和内化各种专业知识:
|
||||
- "构建代码 → 运行测试 → 部署到staging → 验证功能 → 发布生产"
|
||||
- "用户反馈视频加载慢,排查发现是CDN配置问题,修改后加载速度提升60%"
|
||||
- "React Hooks允许在函数组件中使用state和其他React特性"
|
||||
- "每个PR至少需要2个人review,必须包含测试用例"
|
||||
|
||||
## 🆕 XML记忆模式特性
|
||||
- **结构化存储**: 使用XML格式存储,支持更精确的数据管理
|
||||
- **自动迁移**: 从legacy Markdown格式自动迁移到XML
|
||||
- **XML转义**: 自动处理特殊字符,确保数据完整性
|
||||
- **向后兼容**: 继续支持读取legacy格式记忆
|
||||
|
||||
## 🔍 记忆检索与应用
|
||||
- 使用 MCP PromptX recall 工具主动检索记忆
|
||||
- 使用 MCP PromptX action 工具运用记忆激活角色
|
||||
|
||||
🔄 下一步行动:
|
||||
- 开始记忆: 使用 MCP PromptX remember 工具内化第一条知识
|
||||
- 学习资源: 使用 MCP PromptX learn 工具学习新知识再内化`
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取PATEOAS导航信息
|
||||
*/
|
||||
getPATEOAS (args) {
|
||||
const content = args.join(' ')
|
||||
|
||||
if (!content) {
|
||||
return {
|
||||
currentState: 'remember_awaiting_input',
|
||||
availableTransitions: ['welcome', 'learn', 'recall'],
|
||||
nextActions: [
|
||||
{
|
||||
name: '查看角色',
|
||||
description: '选择角色获取专业知识',
|
||||
method: 'MCP PromptX welcome 工具',
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
name: '学习资源',
|
||||
description: '学习新知识然后保存',
|
||||
method: 'MCP PromptX learn 工具',
|
||||
priority: 'high'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
currentState: 'memory_saved',
|
||||
availableTransitions: ['recall', 'learn', 'action', 'remember'],
|
||||
nextActions: [
|
||||
{
|
||||
name: '检索记忆',
|
||||
description: '测试记忆是否可检索',
|
||||
method: 'MCP PromptX recall 工具',
|
||||
priority: 'high'
|
||||
},
|
||||
{
|
||||
name: '学习强化',
|
||||
description: '学习相关知识加强记忆',
|
||||
method: 'MCP PromptX learn 工具',
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
name: '应用记忆',
|
||||
description: '在实际场景中应用记忆',
|
||||
method: 'MCP PromptX action 工具',
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
name: '继续内化',
|
||||
description: 'AI继续内化更多知识',
|
||||
method: 'MCP PromptX remember 工具',
|
||||
priority: 'low'
|
||||
}
|
||||
],
|
||||
metadata: {
|
||||
savedMemory: content.substring(0, 50) + (content.length > 50 ? '...' : ''),
|
||||
memoryLength: content.length,
|
||||
timestamp: new Date().toISOString(),
|
||||
systemVersion: '锦囊串联状态机 v1.0'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RememberCommand
|
||||
@ -471,21 +471,21 @@ class PackageDiscovery extends BaseDiscovery {
|
||||
* @returns {Promise<string>} 环境类型:development, npx, local, unknown
|
||||
*/
|
||||
async _detectExecutionEnvironment() {
|
||||
// 1. 检查是否在开发环境
|
||||
if (await this._isDevelopmentMode()) {
|
||||
return 'development'
|
||||
}
|
||||
|
||||
// 2. 检查是否通过npx执行
|
||||
// 1. 优先检查npx执行(具体环境,避免MCP误判)
|
||||
if (this._isNpxExecution()) {
|
||||
return 'npx'
|
||||
}
|
||||
|
||||
// 3. 检查是否在node_modules中安装
|
||||
// 2. 检查本地安装(具体环境)
|
||||
if (this._isLocalInstallation()) {
|
||||
return 'local'
|
||||
}
|
||||
|
||||
// 3. 最后检查开发环境(通用环境,优先级降低)
|
||||
if (await this._isDevelopmentMode()) {
|
||||
return 'development'
|
||||
}
|
||||
|
||||
return 'unknown'
|
||||
}
|
||||
|
||||
@ -673,11 +673,24 @@ class PackageDiscovery extends BaseDiscovery {
|
||||
*/
|
||||
async _findFallbackRoot() {
|
||||
try {
|
||||
// 优先使用__dirname计算包根目录(更可靠的路径)
|
||||
const packageRoot = path.resolve(__dirname, '../../../../../')
|
||||
|
||||
// 验证是否为有效的dpml-prompt包
|
||||
const packageJsonPath = path.join(packageRoot, 'package.json')
|
||||
if (await fs.pathExists(packageJsonPath)) {
|
||||
const packageJson = await fs.readJSON(packageJsonPath)
|
||||
if (packageJson.name === 'dpml-prompt') {
|
||||
return packageRoot
|
||||
}
|
||||
}
|
||||
|
||||
// 后备方案:使用模块解析(使用__dirname作为basedir)
|
||||
const resolve = require('resolve')
|
||||
const packageJsonPath = resolve.sync('dpml-prompt/package.json', {
|
||||
basedir: process.cwd()
|
||||
const resolvedPackageJsonPath = resolve.sync('dpml-prompt/package.json', {
|
||||
basedir: __dirname
|
||||
})
|
||||
return path.dirname(packageJsonPath)
|
||||
return path.dirname(resolvedPackageJsonPath)
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
|
||||
187
src/lib/core/resource/protocols/FileProtocol.js
Normal file
187
src/lib/core/resource/protocols/FileProtocol.js
Normal file
@ -0,0 +1,187 @@
|
||||
const ResourceProtocol = require('./ResourceProtocol')
|
||||
const path = require('path')
|
||||
const fs = require('fs').promises
|
||||
|
||||
/**
|
||||
* 文件协议实现
|
||||
* 实现@file://协议,用于访问本地文件系统中的文件
|
||||
*/
|
||||
class FileProtocol extends ResourceProtocol {
|
||||
constructor (options = {}) {
|
||||
super('file', options)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置注册表(保持与其他协议的一致性)
|
||||
*/
|
||||
setRegistry (registry) {
|
||||
// File协议不使用注册表,但为了一致性提供此方法
|
||||
this.registry = registry || {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协议信息
|
||||
* @returns {object} 协议信息
|
||||
*/
|
||||
getProtocolInfo () {
|
||||
return {
|
||||
name: 'file',
|
||||
description: '文件系统协议,提供本地文件访问',
|
||||
location: 'file://{path}',
|
||||
examples: [
|
||||
'file://package.json',
|
||||
'file:///absolute/path/to/file.txt',
|
||||
'file://./relative/path/file.md',
|
||||
'file://../parent/file.json'
|
||||
],
|
||||
params: this.getSupportedParams()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 支持的查询参数
|
||||
* @returns {object} 参数说明
|
||||
*/
|
||||
getSupportedParams () {
|
||||
return {
|
||||
...super.getSupportedParams(),
|
||||
encoding: 'string - 文件编码 (utf8, ascii, binary等)',
|
||||
exists: 'boolean - 仅返回存在的文件'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证文件协议路径
|
||||
* @param {string} resourcePath - 资源路径
|
||||
* @returns {boolean} 是否有效
|
||||
*/
|
||||
validatePath (resourcePath) {
|
||||
if (!super.validatePath(resourcePath)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 基本路径验证 - 允许相对路径和绝对路径
|
||||
return typeof resourcePath === 'string' && resourcePath.length > 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析文件路径
|
||||
* @param {string} resourcePath - 原始资源路径
|
||||
* @param {QueryParams} queryParams - 查询参数
|
||||
* @returns {Promise<string>} 解析后的绝对路径
|
||||
*/
|
||||
async resolvePath (resourcePath, queryParams) {
|
||||
let resolvedPath
|
||||
|
||||
if (path.isAbsolute(resourcePath)) {
|
||||
// 绝对路径直接使用
|
||||
resolvedPath = resourcePath
|
||||
} else {
|
||||
// 相对路径相对于当前工作目录解析
|
||||
resolvedPath = path.resolve(process.cwd(), resourcePath)
|
||||
}
|
||||
|
||||
// 规范化路径
|
||||
resolvedPath = path.normalize(resolvedPath)
|
||||
|
||||
return resolvedPath
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载资源内容
|
||||
* @param {string} resolvedPath - 解析后的路径
|
||||
* @param {QueryParams} queryParams - 查询参数
|
||||
* @returns {Promise<string>} 资源内容
|
||||
*/
|
||||
async loadContent (resolvedPath, queryParams) {
|
||||
try {
|
||||
// 检查路径是否存在
|
||||
const stats = await fs.stat(resolvedPath)
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return await this.loadDirectoryContent(resolvedPath, queryParams)
|
||||
} else if (stats.isFile()) {
|
||||
return await this.loadFileContent(resolvedPath, queryParams)
|
||||
} else {
|
||||
throw new Error(`不支持的文件类型: ${resolvedPath}`)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
// 如果设置了exists参数为false,返回空内容而不是错误
|
||||
if (queryParams && queryParams.get('exists') === 'false') {
|
||||
return ''
|
||||
}
|
||||
throw new Error(`文件或目录不存在: ${resolvedPath}`)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载文件内容
|
||||
* @param {string} filePath - 文件路径
|
||||
* @param {QueryParams} queryParams - 查询参数
|
||||
* @returns {Promise<string>} 文件内容
|
||||
*/
|
||||
async loadFileContent (filePath, queryParams) {
|
||||
const encoding = queryParams?.get('encoding') || 'utf8'
|
||||
return await fs.readFile(filePath, encoding)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载目录内容
|
||||
* @param {string} dirPath - 目录路径
|
||||
* @param {QueryParams} queryParams - 查询参数
|
||||
* @returns {Promise<string>} 目录内容列表
|
||||
*/
|
||||
async loadDirectoryContent (dirPath, queryParams) {
|
||||
const entries = await fs.readdir(dirPath, { withFileTypes: true })
|
||||
|
||||
// 应用类型过滤
|
||||
const typeFilter = queryParams?.get('type')
|
||||
let filteredEntries = entries
|
||||
|
||||
if (typeFilter) {
|
||||
filteredEntries = entries.filter(entry => {
|
||||
switch (typeFilter) {
|
||||
case 'file': return entry.isFile()
|
||||
case 'dir': return entry.isDirectory()
|
||||
case 'both': return true
|
||||
default: return true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 格式化输出
|
||||
const format = queryParams?.get('format') || 'list'
|
||||
|
||||
switch (format) {
|
||||
case 'json':
|
||||
return JSON.stringify(
|
||||
filteredEntries.map(entry => ({
|
||||
name: entry.name,
|
||||
type: entry.isDirectory() ? 'directory' : 'file',
|
||||
path: path.join(dirPath, entry.name)
|
||||
})),
|
||||
null,
|
||||
2
|
||||
)
|
||||
|
||||
case 'paths':
|
||||
return filteredEntries
|
||||
.map(entry => path.join(dirPath, entry.name))
|
||||
.join('\n')
|
||||
|
||||
case 'list':
|
||||
default:
|
||||
return filteredEntries
|
||||
.map(entry => {
|
||||
const type = entry.isDirectory() ? '[DIR]' : '[FILE]'
|
||||
return `${type} ${entry.name}`
|
||||
})
|
||||
.join('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FileProtocol
|
||||
@ -11,6 +11,8 @@ const RoleProtocol = require('./protocols/RoleProtocol')
|
||||
const ThoughtProtocol = require('./protocols/ThoughtProtocol')
|
||||
const ExecutionProtocol = require('./protocols/ExecutionProtocol')
|
||||
const KnowledgeProtocol = require('./protocols/KnowledgeProtocol')
|
||||
const UserProtocol = require('./protocols/UserProtocol')
|
||||
const FileProtocol = require('./protocols/FileProtocol')
|
||||
|
||||
class ResourceManager {
|
||||
constructor() {
|
||||
@ -36,6 +38,8 @@ class ResourceManager {
|
||||
// 基础协议 - 直接文件系统映射
|
||||
this.protocols.set('package', new PackageProtocol())
|
||||
this.protocols.set('project', new ProjectProtocol())
|
||||
this.protocols.set('file', new FileProtocol())
|
||||
this.protocols.set('user', new UserProtocol())
|
||||
|
||||
// 逻辑协议 - 需要注册表查询
|
||||
this.protocols.set('role', new RoleProtocol())
|
||||
@ -158,11 +162,23 @@ class ResourceManager {
|
||||
await this.initializeWithNewArchitecture()
|
||||
}
|
||||
|
||||
// 处理@!开头的DPML格式(如 @!role://java-developer)
|
||||
if (resourceId.startsWith('@!')) {
|
||||
// 处理@开头的DPML格式(如 @file://path, @!role://java-developer)
|
||||
if (resourceId.startsWith('@')) {
|
||||
const parsed = this.protocolParser.parse(resourceId)
|
||||
|
||||
// 从RegistryData查找资源
|
||||
// 对于基础协议(file, user, package, project),直接通过协议处理器加载
|
||||
const basicProtocols = ['file', 'user', 'package', 'project']
|
||||
if (basicProtocols.includes(parsed.protocol)) {
|
||||
const content = await this.loadResourceByProtocol(resourceId)
|
||||
return {
|
||||
success: true,
|
||||
content,
|
||||
resourceId,
|
||||
reference: resourceId
|
||||
}
|
||||
}
|
||||
|
||||
// 对于逻辑协议,从RegistryData查找资源
|
||||
const resourceData = this.registryData.findResourceById(parsed.path, parsed.protocol)
|
||||
if (!resourceData) {
|
||||
throw new Error(`Resource not found: ${parsed.protocol}:${parsed.path}`)
|
||||
|
||||
@ -113,7 +113,7 @@ const TOOL_DEFINITIONS = [
|
||||
},
|
||||
{
|
||||
name: 'promptx_dacp',
|
||||
description: '🚀 [DACP专业服务调用器] 让PromptX角色拥有执行能力 - 调用邮件发送、日程管理、文档处理等专业服务,将AI建议转化为实际行动。支持自然语言需求智能路由到合适的DACP服务包。',
|
||||
description: '🚀 [DACP专业服务工具] 专业执行工具 - 需要明确知道如何使用特定DACP服务时调用。工具存在但需要专业知识才能正确使用,不建议在不了解服务配置和参数的情况下尝试。',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
|
||||
@ -46,17 +46,30 @@ class DirectoryService {
|
||||
async getProjectRoot(context = {}) {
|
||||
await this._ensureInitialized()
|
||||
|
||||
// 🔍 增加详细的路径诊断日志
|
||||
console.error('🔍 [DirectoryService-DIAGNOSIS] ===== getProjectRoot 诊断开始 =====')
|
||||
console.error(`🔍 [DirectoryService-DIAGNOSIS] context: ${JSON.stringify(context)}`)
|
||||
console.error(`🔍 [DirectoryService-DIAGNOSIS] process.cwd(): ${process.cwd()}`)
|
||||
|
||||
try {
|
||||
const result = await this.projectRootLocator.locate(context)
|
||||
this._lastProjectRoot = result
|
||||
this._lastContext = context
|
||||
|
||||
console.error(`🔍 [DirectoryService-DIAGNOSIS] ProjectRootLocator结果: ${result}`)
|
||||
console.error('🔍 [DirectoryService-DIAGNOSIS] ===== getProjectRoot 诊断结束 =====')
|
||||
|
||||
logger.debug(`[DirectoryService] 项目根目录: ${result}`)
|
||||
return result
|
||||
} catch (error) {
|
||||
console.error(`🔍 [DirectoryService-DIAGNOSIS] ❌ ProjectRootLocator失败: ${error.message}`)
|
||||
console.error('🔍 [DirectoryService-DIAGNOSIS] ===== getProjectRoot 诊断结束(出错) =====')
|
||||
|
||||
logger.error('[DirectoryService] 获取项目根目录失败:', error)
|
||||
// 回退到当前目录
|
||||
return context.startDir || process.cwd()
|
||||
// 回退到当前工作目录
|
||||
const fallback = process.cwd()
|
||||
console.error(`🔍 [DirectoryService-DIAGNOSIS] 回退到process.cwd(): ${fallback}`)
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -68,14 +68,21 @@ function getMCPWorkingDirectory() {
|
||||
* @returns {string} 工作空间路径
|
||||
*/
|
||||
function getWorkspaceSynchronous(context) {
|
||||
// 🔍 增加详细的路径诊断日志
|
||||
console.error('🔍 [executionContext-DIAGNOSIS] ===== getWorkspaceSynchronous 诊断开始 =====')
|
||||
console.error(`🔍 [executionContext-DIAGNOSIS] context: ${JSON.stringify(context)}`)
|
||||
console.error(`🔍 [executionContext-DIAGNOSIS] process.cwd(): ${process.cwd()}`)
|
||||
|
||||
// 策略1:IDE环境变量
|
||||
const workspacePaths = process.env.WORKSPACE_FOLDER_PATHS;
|
||||
console.error(`🔍 [executionContext-DIAGNOSIS] 策略1 - WORKSPACE_FOLDER_PATHS: ${workspacePaths || 'undefined'}`)
|
||||
if (workspacePaths) {
|
||||
try {
|
||||
const folders = JSON.parse(workspacePaths);
|
||||
if (Array.isArray(folders) && folders.length > 0) {
|
||||
const firstFolder = folders[0];
|
||||
if (isValidDirectory(firstFolder)) {
|
||||
console.error(`🔍 [executionContext-DIAGNOSIS] 策略1成功: ${firstFolder}`)
|
||||
console.error(`[执行上下文] 使用WORKSPACE_FOLDER_PATHS: ${firstFolder}`);
|
||||
return firstFolder;
|
||||
}
|
||||
@ -84,6 +91,7 @@ function getWorkspaceSynchronous(context) {
|
||||
// 忽略解析错误,尝试直接使用
|
||||
const firstPath = workspacePaths.split(path.delimiter)[0];
|
||||
if (firstPath && isValidDirectory(firstPath)) {
|
||||
console.error(`🔍 [executionContext-DIAGNOSIS] 策略1备用成功: ${firstPath}`)
|
||||
console.error(`[执行上下文] 使用WORKSPACE_FOLDER_PATHS: ${firstPath}`);
|
||||
return firstPath;
|
||||
}
|
||||
@ -92,39 +100,51 @@ function getWorkspaceSynchronous(context) {
|
||||
|
||||
// 策略2:PromptX专用环境变量
|
||||
const promptxWorkspaceEnv = process.env.PROMPTX_WORKSPACE;
|
||||
console.error(`🔍 [executionContext-DIAGNOSIS] 策略2 - PROMPTX_WORKSPACE: ${promptxWorkspaceEnv || 'undefined'}`)
|
||||
if (promptxWorkspaceEnv && promptxWorkspaceEnv.trim() !== '') {
|
||||
const promptxWorkspace = normalizePath(expandHome(promptxWorkspaceEnv));
|
||||
if (isValidDirectory(promptxWorkspace)) {
|
||||
console.error(`🔍 [executionContext-DIAGNOSIS] 策略2成功: ${promptxWorkspace}`)
|
||||
console.error(`[执行上下文] 使用PROMPTX_WORKSPACE: ${promptxWorkspace}`);
|
||||
return promptxWorkspace;
|
||||
}
|
||||
}
|
||||
|
||||
// 策略3:现有.promptx目录
|
||||
console.error(`🔍 [executionContext-DIAGNOSIS] 策略3 - 查找现有.promptx目录,起始目录: ${context.startDir}`)
|
||||
const existingPrompxRoot = findExistingPromptxDirectory(context.startDir);
|
||||
console.error(`🔍 [executionContext-DIAGNOSIS] 策略3结果: ${existingPrompxRoot || 'null'}`)
|
||||
if (existingPrompxRoot) {
|
||||
console.error(`🔍 [executionContext-DIAGNOSIS] 策略3成功: ${existingPrompxRoot}`)
|
||||
console.error(`[执行上下文] 发现现有.promptx目录: ${existingPrompxRoot}`);
|
||||
return existingPrompxRoot;
|
||||
}
|
||||
|
||||
// 策略4:PWD环境变量
|
||||
const pwd = process.env.PWD;
|
||||
console.error(`🔍 [executionContext-DIAGNOSIS] 策略4 - PWD: ${pwd || 'undefined'}`)
|
||||
if (pwd && isValidDirectory(pwd) && pwd !== process.cwd()) {
|
||||
console.error(`🔍 [executionContext-DIAGNOSIS] 策略4成功: ${pwd}`)
|
||||
console.error(`[执行上下文] 使用PWD环境变量: ${pwd}`);
|
||||
return pwd;
|
||||
}
|
||||
|
||||
// 策略5:项目根目录
|
||||
const projectRoot = findProjectRoot(context.startDir);
|
||||
console.error(`🔍 [executionContext-DIAGNOSIS] 策略5结果: ${projectRoot || 'null'}`)
|
||||
if (projectRoot && projectRoot !== process.cwd()) {
|
||||
console.error(`🔍 [executionContext-DIAGNOSIS] 策略5成功: ${projectRoot}`)
|
||||
console.error(`[执行上下文] 智能推测项目根目录: ${projectRoot}`);
|
||||
return projectRoot;
|
||||
}
|
||||
|
||||
// 策略6:回退到当前目录
|
||||
console.error(`[执行上下文] 回退到process.cwd(): ${process.cwd()}`);
|
||||
const fallbackPath = process.cwd()
|
||||
console.error(`🔍 [executionContext-DIAGNOSIS] 策略6 - 回退到process.cwd(): ${fallbackPath}`)
|
||||
console.error(`[执行上下文] 回退到process.cwd(): ${fallbackPath}`);
|
||||
console.error(`[执行上下文] 提示:建议在MCP配置中添加 "env": {"PROMPTX_WORKSPACE": "你的项目目录"}`);
|
||||
return process.cwd();
|
||||
console.error('🔍 [executionContext-DIAGNOSIS] ===== getWorkspaceSynchronous 诊断结束 =====')
|
||||
return fallbackPath;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,29 +162,61 @@ function getMCPWorkingDirectoryLegacy() {
|
||||
* @returns {string|null} 包含.promptx目录的父目录路径或null
|
||||
*/
|
||||
function findExistingPromptxDirectory(startDir) {
|
||||
// 🔍 增加详细的路径诊断日志
|
||||
console.error('🔍 [findExistingPromptxDirectory-DIAGNOSIS] ===== 查找.promptx目录诊断开始 =====')
|
||||
console.error(`🔍 [findExistingPromptxDirectory-DIAGNOSIS] 起始目录: ${startDir}`)
|
||||
|
||||
let currentDir = path.resolve(startDir);
|
||||
const root = path.parse(currentDir).root;
|
||||
console.error(`🔍 [findExistingPromptxDirectory-DIAGNOSIS] 解析后起始目录: ${currentDir}`)
|
||||
console.error(`🔍 [findExistingPromptxDirectory-DIAGNOSIS] 文件系统根目录: ${root}`)
|
||||
|
||||
const foundDirectories = []
|
||||
let stepCount = 0
|
||||
|
||||
while (currentDir !== root) {
|
||||
stepCount++
|
||||
console.error(`🔍 [findExistingPromptxDirectory-DIAGNOSIS] 第${stepCount}步 - 检查目录: ${currentDir}`)
|
||||
|
||||
// 检查当前目录是否包含.promptx目录
|
||||
const promptxPath = path.join(currentDir, '.promptx');
|
||||
console.error(`🔍 [findExistingPromptxDirectory-DIAGNOSIS] 检查路径: ${promptxPath}`)
|
||||
|
||||
if (fs.existsSync(promptxPath)) {
|
||||
console.error(`🔍 [findExistingPromptxDirectory-DIAGNOSIS] ✅ 发现.promptx目录: ${promptxPath}`)
|
||||
foundDirectories.push(currentDir)
|
||||
|
||||
try {
|
||||
const stat = fs.statSync(promptxPath);
|
||||
if (stat.isDirectory()) {
|
||||
console.error(`🔍 [findExistingPromptxDirectory-DIAGNOSIS] ✅ 确认为有效目录,返回: ${currentDir}`)
|
||||
console.error(`🔍 [findExistingPromptxDirectory-DIAGNOSIS] 🎯 总共发现${foundDirectories.length}个.promptx目录: ${JSON.stringify(foundDirectories)}`)
|
||||
console.error('🔍 [findExistingPromptxDirectory-DIAGNOSIS] ===== 查找.promptx目录诊断结束 =====')
|
||||
return currentDir;
|
||||
} else {
|
||||
console.error(`🔍 [findExistingPromptxDirectory-DIAGNOSIS] ❌ .promptx存在但不是目录`)
|
||||
}
|
||||
} catch {
|
||||
} catch (error) {
|
||||
console.error(`🔍 [findExistingPromptxDirectory-DIAGNOSIS] ❌ 访问.promptx目录时出错: ${error.message}`)
|
||||
// 忽略权限错误等,继续查找
|
||||
}
|
||||
} else {
|
||||
console.error(`🔍 [findExistingPromptxDirectory-DIAGNOSIS] ❌ 当前目录无.promptx`)
|
||||
}
|
||||
|
||||
// 向上一级目录
|
||||
const parentDir = path.dirname(currentDir);
|
||||
if (parentDir === currentDir) break; // 防止无限循环
|
||||
if (parentDir === currentDir) {
|
||||
console.error(`🔍 [findExistingPromptxDirectory-DIAGNOSIS] 🔚 到达顶级目录,停止搜索`)
|
||||
break; // 防止无限循环
|
||||
}
|
||||
currentDir = parentDir;
|
||||
console.error(`🔍 [findExistingPromptxDirectory-DIAGNOSIS] ⬆️ 向上一级: ${currentDir}`)
|
||||
}
|
||||
|
||||
console.error(`🔍 [findExistingPromptxDirectory-DIAGNOSIS] 🎯 搜索完成,总共发现${foundDirectories.length}个.promptx目录: ${JSON.stringify(foundDirectories)}`)
|
||||
console.error(`🔍 [findExistingPromptxDirectory-DIAGNOSIS] ❌ 未找到有效的.promptx目录`)
|
||||
console.error('🔍 [findExistingPromptxDirectory-DIAGNOSIS] ===== 查找.promptx目录诊断结束 =====')
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
"metadata": {
|
||||
"version": "2.0.0",
|
||||
"description": "package 级资源注册表",
|
||||
"createdAt": "2025-06-18T10:00:59.258Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.263Z",
|
||||
"resourceCount": 47
|
||||
"createdAt": "2025-06-26T08:44:24.849Z",
|
||||
"updatedAt": "2025-06-26T08:44:47.497Z",
|
||||
"resourceCount": 58
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
@ -17,9 +17,9 @@
|
||||
"description": "专业角色,提供特定领域的专业能力",
|
||||
"reference": "@package://prompt/domain/assistant/assistant.role.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.259Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.259Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.259Z"
|
||||
"createdAt": "2025-06-26T08:44:24.853Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.853Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.853Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -30,9 +30,9 @@
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/domain/assistant/thought/assistant.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.260Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.260Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.260Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -43,9 +43,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/assistant/execution/assistant.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.260Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.260Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.260Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -56,9 +56,9 @@
|
||||
"description": "专业角色,提供特定领域的专业能力",
|
||||
"reference": "@package://prompt/domain/frontend-developer/frontend-developer.role.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.260Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.260Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.260Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -69,9 +69,9 @@
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/domain/frontend-developer/thought/frontend-developer.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.260Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.260Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.260Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -82,9 +82,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/java-backend-developer/execution/code-quality.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.260Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.260Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.260Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -95,9 +95,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/frontend-developer/execution/frontend-developer.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.260Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.260Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.260Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -108,9 +108,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/frontend-developer/execution/technical-architecture.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.260Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.260Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.260Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -121,9 +121,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/frontend-developer/execution/user-experience.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.260Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.260Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.260Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -134,9 +134,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/frontend-developer/execution/wechat-miniprogram-development.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.260Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.260Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.260Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -147,9 +147,9 @@
|
||||
"description": "专业角色,提供特定领域的专业能力",
|
||||
"reference": "@package://prompt/domain/java-backend-developer/java-backend-developer.role.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.260Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.260Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.260Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -160,9 +160,9 @@
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/domain/java-backend-developer/thought/java-backend-developer.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.260Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.260Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.260Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -173,9 +173,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/java-backend-developer/execution/database-design.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.260Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.260Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.260Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -186,9 +186,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/java-backend-developer/execution/java-backend-developer.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.260Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.260Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.260Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -199,9 +199,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/java-backend-developer/execution/spring-ecosystem.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.260Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.260Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.260Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -212,9 +212,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/java-backend-developer/execution/system-architecture.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.260Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.260Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.260Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -225,9 +225,9 @@
|
||||
"description": "专业角色,提供特定领域的专业能力",
|
||||
"reference": "@package://prompt/domain/nuwa/nuwa.role.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.261Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.261Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.261Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -238,9 +238,9 @@
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/domain/nuwa/thought/role-creation.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.261Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.261Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.261Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -251,9 +251,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/nuwa/execution/dpml-authoring.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.261Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.261Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.261Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -264,9 +264,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/nuwa/execution/role-design-patterns.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.261Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.261Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.261Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -277,9 +277,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/nuwa/execution/role-generation.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.261Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.261Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.261Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -290,9 +290,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/nuwa/execution/visualization-enhancement.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.261Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.261Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.261Z"
|
||||
"createdAt": "2025-06-26T08:44:24.854Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.854Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.854Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -303,9 +303,9 @@
|
||||
"description": "专业角色,提供特定领域的专业能力",
|
||||
"reference": "@package://prompt/domain/product-manager/product-manager.role.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.261Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.261Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.261Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -316,9 +316,9 @@
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/domain/product-manager/thought/product-manager.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -329,9 +329,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/product-manager/execution/market-analysis.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -342,9 +342,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/product-manager/execution/product-manager.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -355,9 +355,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/product-manager/execution/user-research.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -368,9 +368,22 @@
|
||||
"description": "专业角色,提供特定领域的专业能力",
|
||||
"reference": "@package://prompt/domain/sean/sean.role.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "contradiction-methodology",
|
||||
"source": "package",
|
||||
"protocol": "thought",
|
||||
"name": "Contradiction Methodology 思维模式",
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/domain/sean/thought/contradiction-methodology.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -381,9 +394,35 @@
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/domain/sean/thought/sean.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "contradiction-analysis",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Contradiction Analysis 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/sean/execution/contradiction-analysis.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "contradiction-management-methodology",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Contradiction Management Methodology 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/sean/execution/contradiction-management-methodology.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -394,9 +433,61 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/sean/execution/sean-decision-framework.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "template-adherence",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Template Adherence 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/sean/execution/template-adherence.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "noface",
|
||||
"source": "package",
|
||||
"protocol": "role",
|
||||
"name": "无面",
|
||||
"description": "万能学习助手,通过学习用户提示词获得专业能力",
|
||||
"reference": "@package://prompt/domain/noface/noface.role.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:47.495Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "adaptive-learning",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Adaptive Learning 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/noface/execution/adaptive-learning.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "content-preservation",
|
||||
"source": "package",
|
||||
"protocol": "execution",
|
||||
"name": "Content Preservation 执行模式",
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/noface/execution/content-preservation.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -407,9 +498,9 @@
|
||||
"description": "专业角色,提供特定领域的专业能力",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/xiaohongshu-marketer.role.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -420,9 +511,9 @@
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/thought/xiaohongshu-marketer.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -433,9 +524,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/brand-marketing.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -446,9 +537,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/community-building.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -459,9 +550,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/content-creation.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -472,9 +563,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/content-optimization.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -485,9 +576,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/data-analytics.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -498,9 +589,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/ecommerce-conversion.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -511,9 +602,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/performance-optimization.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -524,9 +615,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/platform-compliance.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -537,9 +628,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/team-collaboration.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -550,9 +641,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/user-operation.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -563,9 +654,35 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/domain/xiaohongshu-marketer/execution/xiaohongshu-marketer.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.262Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.262Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.262Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "recall_v1",
|
||||
"source": "package",
|
||||
"protocol": "thought",
|
||||
"name": "Recall_v1 思维模式",
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/core/_deprecated/recall_v1.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "remember_v1",
|
||||
"source": "package",
|
||||
"protocol": "thought",
|
||||
"name": "Remember_v1 思维模式",
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/core/_deprecated/remember_v1.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -576,9 +693,9 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/core/dacp-email-sending.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.263Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.263Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.263Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -589,9 +706,22 @@
|
||||
"description": "执行模式,定义具体的行为模式",
|
||||
"reference": "@package://prompt/core/dacp-service-calling.execution.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.263Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.263Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.263Z"
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "recall-xml",
|
||||
"source": "package",
|
||||
"protocol": "thought",
|
||||
"name": "Recall Xml 思维模式",
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/core/recall-xml.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-26T08:44:24.855Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.855Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.855Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -602,9 +732,22 @@
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/core/recall.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.263Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.263Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.263Z"
|
||||
"createdAt": "2025-06-26T08:44:24.856Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.856Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.856Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "remember-xml",
|
||||
"source": "package",
|
||||
"protocol": "thought",
|
||||
"name": "Remember Xml 思维模式",
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/core/remember-xml.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-26T08:44:24.856Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.856Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.856Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -615,21 +758,21 @@
|
||||
"description": "思维模式,指导AI的思考方式",
|
||||
"reference": "@package://prompt/core/remember.thought.md",
|
||||
"metadata": {
|
||||
"createdAt": "2025-06-18T10:00:59.263Z",
|
||||
"updatedAt": "2025-06-18T10:00:59.263Z",
|
||||
"scannedAt": "2025-06-18T10:00:59.263Z"
|
||||
"createdAt": "2025-06-26T08:44:24.856Z",
|
||||
"updatedAt": "2025-06-26T08:44:24.856Z",
|
||||
"scannedAt": "2025-06-26T08:44:24.856Z"
|
||||
}
|
||||
}
|
||||
],
|
||||
"stats": {
|
||||
"totalResources": 47,
|
||||
"totalResources": 58,
|
||||
"byProtocol": {
|
||||
"role": 7,
|
||||
"thought": 9,
|
||||
"execution": 31
|
||||
"role": 8,
|
||||
"thought": 14,
|
||||
"execution": 36
|
||||
},
|
||||
"bySource": {
|
||||
"package": 47
|
||||
"package": 58
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
173
src/tests/integration/memory-dpml-integration.test.js
Normal file
173
src/tests/integration/memory-dpml-integration.test.js
Normal file
@ -0,0 +1,173 @@
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const RememberCommand = require('../../lib/core/pouch/commands/RememberCommand')
|
||||
const RecallCommand = require('../../lib/core/pouch/commands/RecallCommand')
|
||||
|
||||
describe('Memory DPML Integration', () => {
|
||||
const testDir = path.join(__dirname, 'test-workspace')
|
||||
const memoryDir = path.join(testDir, '.promptx', 'memory')
|
||||
const xmlFile = path.join(memoryDir, 'declarative.dpml')
|
||||
const legacyFile = path.join(memoryDir, 'declarative.md')
|
||||
const backupFile = path.join(memoryDir, 'declarative.md.bak')
|
||||
|
||||
let originalCwd
|
||||
|
||||
beforeEach(async () => {
|
||||
// 保存原始工作目录
|
||||
originalCwd = process.cwd()
|
||||
|
||||
// 清理测试目录
|
||||
await fs.remove(testDir)
|
||||
await fs.ensureDir(testDir)
|
||||
|
||||
// 切换到测试工作目录
|
||||
process.chdir(testDir)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
// 恢复原始工作目录
|
||||
process.chdir(originalCwd)
|
||||
|
||||
// 清理测试目录
|
||||
await fs.remove(testDir)
|
||||
})
|
||||
|
||||
test('完整的保存和检索流程', async () => {
|
||||
const rememberCmd = new RememberCommand()
|
||||
const recallCmd = new RecallCommand()
|
||||
|
||||
// 保存记忆
|
||||
const result = await rememberCmd.saveMemory('测试记忆内容')
|
||||
expect(result.action).toBe('created')
|
||||
expect(result.value).toBe('测试记忆内容')
|
||||
|
||||
// 检索记忆
|
||||
const memories = await recallCmd.getAllMemories('测试')
|
||||
expect(memories.length).toBe(1)
|
||||
expect(memories[0].content).toBe('测试记忆内容')
|
||||
expect(memories[0].tags).toContain('其他')
|
||||
})
|
||||
|
||||
test('DPML文件格式正确', async () => {
|
||||
const rememberCmd = new RememberCommand()
|
||||
|
||||
// 保存记忆
|
||||
await rememberCmd.saveMemory('测试DPML格式')
|
||||
|
||||
// 检查DPML文件
|
||||
expect(await fs.pathExists(xmlFile)).toBe(true)
|
||||
const xmlContent = await fs.readFile(xmlFile, 'utf8')
|
||||
|
||||
// 验证XML结构
|
||||
expect(xmlContent).toMatch(/<memory>/)
|
||||
expect(xmlContent).toMatch(/<\/memory>/)
|
||||
expect(xmlContent).toMatch(/<item id="[^"]*" time="[^"]*">/)
|
||||
expect(xmlContent).toMatch(/<content>测试DPML格式<\/content>/)
|
||||
expect(xmlContent).toMatch(/<tags>其他<\/tags>/)
|
||||
})
|
||||
|
||||
test('数据迁移功能', async () => {
|
||||
// 确保目录存在
|
||||
await fs.ensureDir(memoryDir)
|
||||
|
||||
// 创建legacy文件
|
||||
const legacyContent = `# 陈述性记忆
|
||||
|
||||
## 高价值记忆(评分 ≥ 7)
|
||||
|
||||
- 2025/01/15 14:30 测试记忆内容 #工具使用 #评分:8 #有效期:长期
|
||||
|
||||
- 2025/01/16 10:20 另一条测试记忆 #流程管理 #评分:9 #有效期:长期`
|
||||
|
||||
await fs.writeFile(legacyFile, legacyContent, 'utf8')
|
||||
|
||||
// 触发迁移
|
||||
const rememberCmd = new RememberCommand()
|
||||
await rememberCmd.saveMemory('新记忆触发迁移')
|
||||
|
||||
// 验证迁移结果
|
||||
expect(await fs.pathExists(xmlFile)).toBe(true)
|
||||
expect(await fs.pathExists(backupFile)).toBe(true)
|
||||
|
||||
// 检查迁移的内容
|
||||
const recallCmd = new RecallCommand()
|
||||
const memories = await recallCmd.getAllMemories()
|
||||
|
||||
// 应该有3条记忆(2条迁移的 + 1条新的)
|
||||
expect(memories.length).toBe(3)
|
||||
|
||||
// 验证迁移的记忆内容
|
||||
const migratedMemories = memories.filter(m =>
|
||||
m.content.includes('测试记忆内容') || m.content.includes('另一条测试记忆')
|
||||
)
|
||||
expect(migratedMemories.length).toBe(2)
|
||||
})
|
||||
|
||||
test('搜索功能正常工作', async () => {
|
||||
const rememberCmd = new RememberCommand()
|
||||
const recallCmd = new RecallCommand()
|
||||
|
||||
// 保存多条记忆
|
||||
await rememberCmd.saveMemory('前端开发最佳实践')
|
||||
await rememberCmd.saveMemory('后端API设计规范')
|
||||
await rememberCmd.saveMemory('测试流程优化')
|
||||
|
||||
// 搜索特定内容
|
||||
const frontendMemories = await recallCmd.getAllMemories('前端')
|
||||
expect(frontendMemories.length).toBe(1)
|
||||
expect(frontendMemories[0].content).toBe('前端开发最佳实践')
|
||||
|
||||
// 搜索标签
|
||||
const flowMemories = await recallCmd.getAllMemories('流程')
|
||||
expect(flowMemories.length).toBe(1)
|
||||
expect(flowMemories[0].content).toBe('测试流程优化')
|
||||
})
|
||||
|
||||
test('XML转义功能正常', async () => {
|
||||
const rememberCmd = new RememberCommand()
|
||||
const recallCmd = new RecallCommand()
|
||||
|
||||
// 保存包含特殊字符的记忆
|
||||
const specialContent = '使用<script>标签时要注意"安全性"问题 & 性能优化'
|
||||
await rememberCmd.saveMemory(specialContent)
|
||||
|
||||
// 检索记忆
|
||||
const memories = await recallCmd.getAllMemories('script')
|
||||
expect(memories.length).toBe(1)
|
||||
expect(memories[0].content).toBe(specialContent)
|
||||
|
||||
// 检查DPML文件中的转义
|
||||
const xmlContent = await fs.readFile(xmlFile, 'utf8')
|
||||
expect(xmlContent).toMatch(/<script>/)
|
||||
expect(xmlContent).toMatch(/"安全性"/)
|
||||
expect(xmlContent).toMatch(/& 性能优化/)
|
||||
})
|
||||
|
||||
test('迁移只执行一次', async () => {
|
||||
// 确保目录存在
|
||||
await fs.ensureDir(memoryDir)
|
||||
|
||||
// 创建legacy文件
|
||||
const legacyContent = '- 2025/01/15 14:30 测试记忆 #其他 #评分:8 #有效期:长期'
|
||||
await fs.writeFile(legacyFile, legacyContent, 'utf8')
|
||||
|
||||
const rememberCmd = new RememberCommand()
|
||||
|
||||
// 第一次触发迁移
|
||||
await rememberCmd.saveMemory('第一次记忆')
|
||||
expect(await fs.pathExists(backupFile)).toBe(true)
|
||||
|
||||
// 记录备份文件的修改时间
|
||||
const firstBackupStats = await fs.stat(backupFile)
|
||||
|
||||
// 等待一小段时间确保时间戳不同
|
||||
await new Promise(resolve => setTimeout(resolve, 10))
|
||||
|
||||
// 第二次保存,不应该再次迁移
|
||||
await rememberCmd.saveMemory('第二次记忆')
|
||||
|
||||
// 备份文件的修改时间应该没有变化
|
||||
const secondBackupStats = await fs.stat(backupFile)
|
||||
expect(secondBackupStats.mtime.getTime()).toBe(firstBackupStats.mtime.getTime())
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user