- 在InitCommand、RecallCommand、RememberCommand和PouchStateMachine中替换了直接路径处理逻辑,改为使用DirectoryService进行目录解析。 - 更新了ProjectDiscovery以使用新的getProjectRoot方法,标记旧方法为已弃用。 - 在executionContext中重构了工作目录获取逻辑,增强了兼容性和可维护性。 - 确保了对用户主目录的避免处理,提升了目录定位的智能性和可靠性。 此改动旨在提升代码的可读性和一致性,同时为未来的扩展打下基础。
199 lines
5.3 KiB
JavaScript
199 lines
5.3 KiB
JavaScript
const BasePouchCommand = require('../BasePouchCommand')
|
||
const fs = require('fs-extra')
|
||
const path = require('path')
|
||
const { COMMANDS } = require('../../../../constants')
|
||
|
||
/**
|
||
* 记忆检索锦囊命令
|
||
* 负责从记忆库中检索相关知识和经验
|
||
*/
|
||
class RecallCommand extends BasePouchCommand {
|
||
constructor () {
|
||
super()
|
||
}
|
||
|
||
getPurpose () {
|
||
return 'AI主动检索记忆中的专业知识、最佳实践和历史经验'
|
||
}
|
||
|
||
async getContent (args) {
|
||
const [query] = args
|
||
|
||
try {
|
||
const memories = await this.getAllMemories(query)
|
||
|
||
if (memories.length === 0) {
|
||
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) {
|
||
return `❌ 检索记忆时出错:${error.message}`
|
||
}
|
||
}
|
||
|
||
getPATEOAS (args) {
|
||
const [query] = args
|
||
const currentState = query ? `recalled-${query}` : 'recall-waiting'
|
||
|
||
return {
|
||
currentState,
|
||
availableTransitions: ['hello', 'remember', 'learn', 'recall'],
|
||
nextActions: [
|
||
{
|
||
name: '选择角色',
|
||
description: '选择专业角色来应用检索到的知识',
|
||
method: 'MCP PromptX hello 工具'
|
||
},
|
||
{
|
||
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
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取所有记忆(紧凑格式)
|
||
*/
|
||
async getAllMemories (query) {
|
||
this.lastSearchCount = 0
|
||
const memories = []
|
||
|
||
// 读取单一记忆文件
|
||
const { getDirectoryService } = require('../../../utils/DirectoryService')
|
||
const directoryService = getDirectoryService()
|
||
const memoryDir = await directoryService.getMemoryDirectory()
|
||
const memoryFile = path.join(memoryDir, 'declarative.md')
|
||
|
||
try {
|
||
if (await fs.pathExists(memoryFile)) {
|
||
const content = await fs.readFile(memoryFile, 'utf-8')
|
||
const lines = content.split('\n')
|
||
|
||
for (const line of lines) {
|
||
if (line.startsWith('- ')) {
|
||
// 解析记忆行
|
||
const memory = this.parseMemoryLine(line)
|
||
if (memory && (!query || this.matchesMemory(memory, query))) {
|
||
memories.push(memory)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('Error reading memories:', error)
|
||
}
|
||
|
||
this.lastSearchCount = memories.length
|
||
return memories
|
||
}
|
||
|
||
/**
|
||
* 解析记忆行(紧凑格式)
|
||
*/
|
||
parseMemoryLine (line) {
|
||
// 格式:- 2025/05/31 14:30 内容 #tag1 #tag2 #评分:8 #有效期:长期
|
||
const match = line.match(/^- (\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}) (.*?) (#.*?)$/)
|
||
if (!match) return null
|
||
|
||
const [, timestamp, content, tagsStr] = match
|
||
const tags = tagsStr.split(' ').filter(t => t.startsWith('#'))
|
||
|
||
return {
|
||
timestamp,
|
||
content,
|
||
tags,
|
||
source: 'memory'
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查记忆是否匹配查询
|
||
*/
|
||
matchesMemory (memory, query) {
|
||
const lowerQuery = query.toLowerCase()
|
||
return memory.content.toLowerCase().includes(lowerQuery) ||
|
||
memory.tags.some(tag => tag.toLowerCase().includes(lowerQuery))
|
||
}
|
||
|
||
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) => {
|
||
const content = memory.content.length > 120
|
||
? memory.content.substring(0, 120) + '...'
|
||
: memory.content
|
||
|
||
return `📝 ${index + 1}. **记忆** (${memory.timestamp})
|
||
${content}
|
||
${memory.tags.slice(0, 5).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'
|
||
}
|
||
}
|
||
|
||
module.exports = RecallCommand
|