Files
PromptX/src/lib/core/pouch/commands/InitCommand.js
阿祖 71ce235904 修复中文路径URL编码问题 (#100)
- 在InitCommand中添加decodeURIComponent()处理中文路径
- 解决MCP传入URL编码路径导致验证失败的问题
- 支持类似 /home/azu/%E6%A1%8C%E9%9D%A2/PromptX 的编码路径

Co-authored-by: Cen-Yaozu <azu@example.com>
2025-07-01 10:00:41 +08:00

260 lines
9.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const BasePouchCommand = require('../BasePouchCommand')
const { getGlobalResourceManager } = require('../../resource')
const { COMMANDS } = require('../../../../constants')
const { getDirectoryService } = require('../../../utils/DirectoryService')
const RegistryData = require('../../resource/RegistryData')
const ProjectDiscovery = require('../../resource/discovery/ProjectDiscovery')
const CurrentProjectManager = require('../../../utils/CurrentProjectManager')
const logger = require('../../../utils/logger')
const path = require('path')
const fs = require('fs-extra')
/**
* 初始化锦囊命令
* 负责准备工作环境和传达系统协议
*/
class InitCommand extends BasePouchCommand {
constructor () {
super()
// 使用全局单例 ResourceManager
this.resourceManager = getGlobalResourceManager()
this.projectDiscovery = new ProjectDiscovery()
this.directoryService = getDirectoryService()
this.currentProjectManager = new CurrentProjectManager()
}
getPurpose () {
return '初始化PromptX工作环境创建必要的配置目录和文件生成项目级资源注册表'
}
async getContent (args) {
// 获取工作目录参数,支持两种格式:
// 1. 来自MCP的对象格式{ workingDirectory: "path" }
// 2. 来自CLI的字符串格式["path"]
let workingDirectory
if (args && typeof args[0] === 'object' && args[0].workingDirectory) {
// MCP格式
workingDirectory = args[0].workingDirectory
} else if (args && typeof args[0] === 'string') {
// CLI格式
workingDirectory = args[0]
}
// 注意如果args[0]是空对象{}workingDirectory保持undefined走后续的自动检测逻辑
let projectPath
if (workingDirectory) {
// AI提供了工作目录先解码中文路径然后使用
const decodedWorkingDirectory = decodeURIComponent(workingDirectory)
projectPath = path.resolve(decodedWorkingDirectory)
// 验证AI提供的路径是否有效
if (!await this.currentProjectManager.validateProjectPath(projectPath)) {
return `❌ 提供的工作目录无效: ${projectPath}
请确保:
1. 路径存在且为目录
2. 不是用户主目录
3. 具有适当的访问权限
💡 请提供一个有效的项目目录路径。`
}
// 保存AI提供的项目路径
await this.currentProjectManager.setCurrentProject(projectPath)
} else {
// AI没有提供工作目录检查是否已有保存的项目
const savedProject = await this.currentProjectManager.getCurrentProject()
if (savedProject) {
// 使用之前保存的项目路径
projectPath = savedProject
} else {
// 没有保存的项目要求AI提供
return `🎯 PromptX需要知道当前项目的工作目录。
请在调用此工具时提供 workingDirectory 参数,例如:
- workingDirectory: "/Users/sean/WorkSpaces/DeepracticeProjects/PromptX"
💡 你当前工作在哪个项目目录?请提供完整的绝对路径。`
}
}
// 构建统一的查找上下文,使用确定的项目路径
const context = {
startDir: projectPath,
platform: process.platform,
avoidUserHome: true,
// init命令特有AI提供的路径优先级最高然后是当前目录
strategies: [
'aiProvidedProjectPath', // 最高优先级AI提供的项目路径
'currentWorkingDirectoryIfHasMarkers',
'currentWorkingDirectory'
]
}
// 1. 获取版本信息
const version = await this.getVersionInfo()
// 2. 基础环境准备 - 创建 .promptx 目录
await this.ensurePromptXDirectory(context)
// 3. 生成项目级资源注册表
const registryStats = await this.generateProjectRegistry(context)
// 4. 刷新全局 ResourceManager确保新资源立即可用
await this.refreshGlobalResourceManager()
return `🎯 PromptX 初始化完成!
## 📦 版本信息
✅ **PromptX v${version}** - AI专业能力增强框架
## 🏗️ 环境准备
✅ 创建了 \`.promptx\` 配置目录
✅ 工作环境就绪
## 📋 项目资源注册表
${registryStats.message}
## 🚀 下一步建议
- 使用 \`welcome\` 发现可用的专业角色
- 使用 \`action\` 激活特定角色获得专业能力
- 使用 \`learn\` 深入学习专业知识
- 使用 \`remember/recall\` 管理专业记忆
💡 **提示**: ${registryStats.totalResources > 0 ? '项目资源已优化为注册表模式,性能大幅提升!' : '现在可以开始创建项目级资源了!'}`
}
/**
* 生成项目级资源注册表
* @param {Object} context - 查找上下文
* @returns {Promise<Object>} 注册表生成统计信息
*/
async generateProjectRegistry(context) {
try {
// 1. 使用统一的目录服务获取项目根目录
const projectRoot = await this.directoryService.getProjectRoot(context)
const resourceDir = await this.directoryService.getResourceDirectory(context)
// 2. 确保资源目录存在具体子目录由ResourceManager扫描时按需创建
await fs.ensureDir(resourceDir)
logger.debug(`[InitCommand] 确保资源目录存在: ${resourceDir}`)
// 3. 使用 ProjectDiscovery 的正确方法生成注册表
logger.step('正在扫描项目资源...')
const registryData = await this.projectDiscovery.generateRegistry(projectRoot)
// 4. 生成统计信息
const stats = registryData.getStats()
const registryPath = await this.directoryService.getRegistryPath(context)
if (registryData.size === 0) {
return {
message: `✅ 项目资源目录已创建,注册表已初始化
📂 目录: ${path.relative(process.cwd(), resourceDir)}
💾 注册表: ${path.relative(process.cwd(), registryPath)}
💡 现在可以在 domain 目录下创建角色资源了`,
totalResources: 0
}
}
return {
message: `✅ 项目资源注册表已重新生成
📊 总计: ${registryData.size} 个资源
📋 分类: role(${stats.byProtocol.role || 0}), thought(${stats.byProtocol.thought || 0}), execution(${stats.byProtocol.execution || 0}), knowledge(${stats.byProtocol.knowledge || 0})
💾 位置: ${path.relative(process.cwd(), registryPath)}`,
totalResources: registryData.size
}
} catch (error) {
logger.error('生成项目注册表时出错:', error)
return {
message: `❌ 生成项目注册表失败: ${error.message}`,
totalResources: 0
}
}
}
/**
* 确保 .promptx 基础目录存在
* 使用统一的目录服务创建基础环境
*/
async ensurePromptXDirectory (context) {
const promptxDir = await this.directoryService.getPromptXDirectory(context)
await fs.ensureDir(promptxDir)
logger.debug(`[InitCommand] 确保.promptx目录存在: ${promptxDir}`)
}
/**
* 刷新全局 ResourceManager
* 确保新创建的资源立即可用,无需重启 MCP Server
*/
async refreshGlobalResourceManager() {
try {
logger.debug('[InitCommand] 刷新全局 ResourceManager...')
// 重新初始化 ResourceManager清除缓存并重新发现资源
await this.resourceManager.initializeWithNewArchitecture()
logger.debug('[InitCommand] 全局 ResourceManager 刷新完成')
} catch (error) {
logger.warn(`[InitCommand] 刷新 ResourceManager 失败: ${error.message}`)
// 不抛出错误,避免影响 init 命令的主要功能
}
}
/**
* 获取版本信息
*/
async getVersionInfo () {
try {
const packageJsonPath = path.resolve(__dirname, '../../../../../package.json')
if (await fs.pathExists(packageJsonPath)) {
const packageJson = await fs.readJSON(packageJsonPath)
const baseVersion = packageJson.version || '未知版本'
const nodeVersion = process.version
const packageName = packageJson.name || 'dpml-prompt'
return `${baseVersion} (${packageName}@${baseVersion}, Node.js ${nodeVersion})`
}
} catch (error) {
logger.warn('无法读取版本信息:', error.message)
}
return '未知版本'
}
async getPATEOAS (args) {
const version = await this.getVersionInfo()
return {
currentState: 'initialized',
availableTransitions: ['welcome', 'action', 'learn', 'recall', 'remember'],
nextActions: [
{
name: '发现专业角色',
description: '查看所有可用的AI专业角色',
method: 'MCP PromptX welcome 工具',
priority: 'recommended'
},
{
name: '激活专业角色',
description: '直接激活特定专业角色如果已知角色ID',
method: 'MCP PromptX action 工具',
priority: 'optional'
}
],
metadata: {
timestamp: new Date().toISOString(),
version: version,
description: 'PromptX专业能力增强系统已就绪'
}
}
}
}
module.exports = InitCommand