重构:引入统一的DirectoryService以优化目录管理

- 在InitCommand、RecallCommand、RememberCommand和PouchStateMachine中替换了直接路径处理逻辑,改为使用DirectoryService进行目录解析。
- 更新了ProjectDiscovery以使用新的getProjectRoot方法,标记旧方法为已弃用。
- 在executionContext中重构了工作目录获取逻辑,增强了兼容性和可维护性。
- 确保了对用户主目录的避免处理,提升了目录定位的智能性和可靠性。

此改动旨在提升代码的可读性和一致性,同时为未来的扩展打下基础。
This commit is contained in:
sean
2025-06-15 11:23:19 +08:00
parent 2d90a7089e
commit 041ece9af1
10 changed files with 1198 additions and 68 deletions

View File

@ -2,11 +2,15 @@ const fs = require('fs');
const path = require('path');
const os = require('os');
const logger = require('./logger');
const { getDirectoryService } = require('./DirectoryService');
/**
* 执行上下文检测工具
* 根据命令入口自动判断执行模式CLI vs MCP并获取正确的工作目录
* 基于MCP社区标准实践通过环境变量解决cwd获取问题
* 执行上下文检测工具 (已重构)
*
* 现在使用统一的DirectoryService提供路径解析
* 保持向后兼容的API但内部使用新的架构
*
* @deprecated 推荐直接使用 DirectoryService
*/
/**
@ -29,22 +33,61 @@ function getExecutionContext() {
/**
* MCP模式下获取工作目录
* 基于社区标准实践,优先从环境变量获取配置的工作目录
* 使用新的DirectoryService进行路径解析
* @returns {string} 工作目录路径
*/
function getMCPWorkingDirectory() {
// 策略1WORKSPACE_FOLDER_PATHSVS Code/Cursor标准环境变量
try {
const directoryService = getDirectoryService();
// 使用新的统一路径解析服务
// 注意这是异步操作但为了保持API兼容性我们需要同步处理
// 在实际使用中,建议迁移到异步版本
const context = {
startDir: process.cwd(),
platform: process.platform,
avoidUserHome: true
};
// 同步获取工作空间目录
// TODO: 在后续版本中迁移到异步API
return getWorkspaceSynchronous(context);
} catch (error) {
logger.warn('[executionContext] 使用新服务失败,回退到旧逻辑:', error.message);
return getMCPWorkingDirectoryLegacy();
}
}
/**
* 同步获取工作空间(临时解决方案)
* @param {Object} context - 查找上下文
* @returns {string} 工作空间路径
*/
function getWorkspaceSynchronous(context) {
// 策略1IDE环境变量
const workspacePaths = process.env.WORKSPACE_FOLDER_PATHS;
if (workspacePaths) {
// 取第一个工作区路径(多工作区情况)
const firstPath = workspacePaths.split(path.delimiter)[0];
if (firstPath && isValidDirectory(firstPath)) {
console.error(`[执行上下文] 使用WORKSPACE_FOLDER_PATHS: ${firstPath}`);
return firstPath;
try {
const folders = JSON.parse(workspacePaths);
if (Array.isArray(folders) && folders.length > 0) {
const firstFolder = folders[0];
if (isValidDirectory(firstFolder)) {
console.error(`[执行上下文] 使用WORKSPACE_FOLDER_PATHS: ${firstFolder}`);
return firstFolder;
}
}
} catch {
// 忽略解析错误,尝试直接使用
const firstPath = workspacePaths.split(path.delimiter)[0];
if (firstPath && isValidDirectory(firstPath)) {
console.error(`[执行上下文] 使用WORKSPACE_FOLDER_PATHS: ${firstPath}`);
return firstPath;
}
}
}
// 策略2PROMPTX_WORKSPACEPromptX专用环境变量仅当明确配置且非空时使用
// 策略2PromptX专用环境变量
const promptxWorkspaceEnv = process.env.PROMPTX_WORKSPACE;
if (promptxWorkspaceEnv && promptxWorkspaceEnv.trim() !== '') {
const promptxWorkspace = normalizePath(expandHome(promptxWorkspaceEnv));
@ -54,30 +97,39 @@ function getMCPWorkingDirectory() {
}
}
// 策略3向上查找现有.promptx目录(复用现有项目配置)
const existingPrompxRoot = findExistingPromptxDirectory(process.cwd());
// 策略3现有.promptx目录
const existingPrompxRoot = findExistingPromptxDirectory(context.startDir);
if (existingPrompxRoot) {
console.error(`[执行上下文] 发现现有.promptx目录: ${existingPrompxRoot}`);
return existingPrompxRoot;
}
// 策略4PWD环境变量(某些情况下可用)
// 策略4PWD环境变量
const pwd = process.env.PWD;
if (pwd && isValidDirectory(pwd) && pwd !== process.cwd()) {
console.error(`[执行上下文] 使用PWD环境变量: ${pwd}`);
return pwd;
}
// 策略5项目根目录智能推测(向上查找项目标识)
const projectRoot = findProjectRoot(process.cwd());
// 策略5项目根目录
const projectRoot = findProjectRoot(context.startDir);
if (projectRoot && projectRoot !== process.cwd()) {
console.error(`[执行上下文] 智能推测项目根目录: ${projectRoot}`);
return projectRoot;
}
// 策略6回退到process.cwd()
// 策略6回退到当前目录
console.error(`[执行上下文] 回退到process.cwd(): ${process.cwd()}`);
console.error(`[执行上下文] 提示建议在MCP配置中添加 "env": {"PROMPTX_WORKSPACE": "你的项目目录"}`)
console.error(`[执行上下文] 提示建议在MCP配置中添加 "env": {"PROMPTX_WORKSPACE": "你的项目目录"}`);
return process.cwd();
}
/**
* 旧版MCP工作目录获取逻辑兼容性备用
* @deprecated
*/
function getMCPWorkingDirectoryLegacy() {
// 保留原始的同步逻辑作为备份
return process.cwd();
}
@ -134,6 +186,18 @@ function findProjectRoot(startDir) {
const root = path.parse(currentDir).root;
while (currentDir !== root) {
// Windows特有避免用户家目录
if (process.platform === 'win32') {
const homeDir = os.homedir();
if (path.resolve(currentDir) === path.resolve(homeDir)) {
console.error(`[executionContext] 跳过用户家目录: ${currentDir}`);
const parentDir = path.dirname(currentDir);
if (parentDir === currentDir) break;
currentDir = parentDir;
continue;
}
}
// 检查是否包含项目标识文件
for (const marker of projectMarkers) {
const markerPath = path.join(currentDir, marker);
@ -193,15 +257,25 @@ function getDebugInfo() {
};
}
/**
* 规范化路径
*/
function normalizePath(p) {
return path.normalize(p);
}
/**
* 展开家目录路径
*/
function expandHome(filepath) {
if (filepath.startsWith('~/') || filepath === '~') {
return path.join(os.homedir(), filepath.slice(1));
if (!filepath || typeof filepath !== 'string') {
return '';
}
if (filepath.startsWith('~/') || filepath === '~') {
return path.join(os.homedir(), filepath.slice(2));
}
return filepath;
}