@qwe8652591/prompt-ai
Version:
DPML-powered AI prompt framework - Revolutionary AI-First CLI system based on Deepractice Prompt Markup Language. Build sophisticated AI agents with structured prompts, memory systems, and execution frameworks.
291 lines (256 loc) • 8.12 kB
JavaScript
const fs = require('fs');
const path = require('path');
const os = require('os');
const logger = require('./logger');
const { getDirectoryService } = require('./DirectoryService');
/**
* 执行上下文检测工具 (已重构)
*
* 现在使用统一的DirectoryService提供路径解析
* 保持向后兼容的API,但内部使用新的架构
*
* @deprecated 推荐直接使用 DirectoryService
*
* 注意:此文件主要保留向后兼容的同步API
* 新代码请直接使用 DirectoryService 的异步API
*/
/**
* 获取执行上下文信息
* @returns {Object} 包含模式和工作目录的上下文对象
*/
function getExecutionContext() {
const args = process.argv;
const command = args[2]; // 第一个命令参数
const isMCPMode = command === 'mcp-server';
return {
mode: isMCPMode ? 'MCP' : 'CLI',
command: command,
workingDirectory: isMCPMode ? getMCPWorkingDirectory() : process.cwd(),
originalCwd: process.cwd()
};
}
/**
* MCP模式下获取工作目录
* 使用新的DirectoryService进行路径解析
* @returns {string} 工作目录路径
*/
function getMCPWorkingDirectory() {
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) {
// 策略1:IDE环境变量
const workspacePaths = process.env.WORKSPACE_FOLDER_PATHS;
if (workspacePaths) {
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;
}
}
}
// 策略2:PromptX专用环境变量
const promptxWorkspaceEnv = process.env.PROMPTX_WORKSPACE;
if (promptxWorkspaceEnv && promptxWorkspaceEnv.trim() !== '') {
const promptxWorkspace = normalizePath(expandHome(promptxWorkspaceEnv));
if (isValidDirectory(promptxWorkspace)) {
console.error(`[执行上下文] 使用PROMPTX_WORKSPACE: ${promptxWorkspace}`);
return promptxWorkspace;
}
}
// 策略3:现有.promptx目录
const existingPrompxRoot = findExistingPromptxDirectory(context.startDir);
if (existingPrompxRoot) {
console.error(`[执行上下文] 发现现有.promptx目录: ${existingPrompxRoot}`);
return existingPrompxRoot;
}
// 策略4:PWD环境变量
const pwd = process.env.PWD;
if (pwd && isValidDirectory(pwd) && pwd !== process.cwd()) {
console.error(`[执行上下文] 使用PWD环境变量: ${pwd}`);
return pwd;
}
// 策略5:项目根目录
const projectRoot = findProjectRoot(context.startDir);
if (projectRoot && projectRoot !== process.cwd()) {
console.error(`[执行上下文] 智能推测项目根目录: ${projectRoot}`);
return projectRoot;
}
// 策略6:回退到当前目录
console.error(`[执行上下文] 回退到process.cwd(): ${process.cwd()}`);
console.error(`[执行上下文] 提示:建议在MCP配置中添加 "env": {"PROMPTX_WORKSPACE": "你的项目目录"}`);
return process.cwd();
}
/**
* 旧版MCP工作目录获取逻辑(兼容性备用)
* @deprecated
*/
function getMCPWorkingDirectoryLegacy() {
// 保留原始的同步逻辑作为备份
return process.cwd();
}
/**
* 向上查找现有的.promptx目录
* @param {string} startDir 开始查找的目录
* @returns {string|null} 包含.promptx目录的父目录路径或null
*/
function findExistingPromptxDirectory(startDir) {
let currentDir = path.resolve(startDir);
const root = path.parse(currentDir).root;
while (currentDir !== root) {
// 检查当前目录是否包含.promptx目录
const promptxPath = path.join(currentDir, '.promptx');
if (fs.existsSync(promptxPath)) {
try {
const stat = fs.statSync(promptxPath);
if (stat.isDirectory()) {
return currentDir;
}
} catch {
// 忽略权限错误等,继续查找
}
}
// 向上一级目录
const parentDir = path.dirname(currentDir);
if (parentDir === currentDir) break; // 防止无限循环
currentDir = parentDir;
}
return null;
}
/**
* 向上查找项目根目录
* @param {string} startDir 开始查找的目录
* @returns {string|null} 项目根目录或null
*/
function findProjectRoot(startDir) {
const projectMarkers = [
'package.json',
'.git',
'pyproject.toml',
'Cargo.toml',
'go.mod',
'pom.xml',
'build.gradle',
'.gitignore'
];
let currentDir = path.resolve(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);
if (fs.existsSync(markerPath)) {
return currentDir;
}
}
// 向上一级目录
const parentDir = path.dirname(currentDir);
if (parentDir === currentDir) break; // 防止无限循环
currentDir = parentDir;
}
return null;
}
/**
* 验证目录是否有效
* @param {string} dir 要验证的目录路径
* @returns {boolean} 目录是否有效
*/
function isValidDirectory(dir) {
try {
if (!dir || typeof dir !== 'string') {
return false;
}
const resolvedDir = path.resolve(dir);
const stat = fs.statSync(resolvedDir);
return stat.isDirectory();
} catch {
return false;
}
}
/**
* 获取调试信息
* @returns {Object} 调试信息对象
*/
function getDebugInfo() {
const context = getExecutionContext();
return {
processArgv: process.argv,
processCwd: process.cwd(),
detectedMode: context.mode,
detectedWorkingDirectory: context.workingDirectory,
environmentVariables: {
WORKSPACE_FOLDER_PATHS: process.env.WORKSPACE_FOLDER_PATHS || 'undefined',
PROMPTX_WORKSPACE: process.env.PROMPTX_WORKSPACE || 'undefined',
PWD: process.env.PWD || 'undefined'
},
nodeVersion: process.version,
platform: process.platform
};
}
/**
* 规范化路径
*/
function normalizePath(p) {
return path.normalize(p);
}
/**
* 展开家目录路径
*/
function expandHome(filepath) {
if (!filepath || typeof filepath !== 'string') {
return '';
}
if (filepath.startsWith('~/') || filepath === '~') {
return path.join(os.homedir(), filepath.slice(2));
}
return filepath;
}
module.exports = {
getExecutionContext,
isValidDirectory,
getDebugInfo,
findExistingPromptxDirectory,
findProjectRoot
};