From 920a41ec5a0a633fa75f802c29bbf45dc7662d98 Mon Sep 17 00:00:00 2001 From: sean Date: Sun, 15 Jun 2025 21:33:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0.gitignore=E4=BB=A5=E6=8E=92?= =?UTF-8?q?=E9=99=A4.kilocode=E7=9B=AE=E5=BD=95=EF=BC=9B=E5=9C=A8Directory?= =?UTF-8?q?Locator.js=E4=B8=AD=E5=A2=9E=E5=BC=BA=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E6=9F=A5=E6=89=BE=E7=AD=96=E7=95=A5=E7=9A=84=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=AF=B9IDE=E7=8E=AF=E5=A2=83?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E7=9A=84=E6=A3=80=E6=B5=8B=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E8=B7=AF=E5=BE=84=E5=9B=9E=E9=80=80?= =?UTF-8?q?=E7=AD=96=E7=95=A5=EF=BC=9B=E5=9C=A8DirectoryService.js?= =?UTF-8?q?=E4=B8=AD=E6=B7=BB=E5=8A=A0IDE=E6=A3=80=E6=B5=8B=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E5=92=8C=E8=B7=AF=E5=BE=84=E9=85=8D=E7=BD=AE=E5=BB=BA?= =?UTF-8?q?=E8=AE=AE=EF=BC=8C=E6=8F=90=E5=8D=87=E7=94=A8=E6=88=B7=E4=BD=93?= =?UTF-8?q?=E9=AA=8C=E5=92=8C=E7=8E=AF=E5=A2=83=E9=85=8D=E7=BD=AE=E7=9A=84?= =?UTF-8?q?=E7=81=B5=E6=B4=BB=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + src/lib/utils/DirectoryLocator.js | 202 ++++++++++++++++++++++++++---- src/lib/utils/DirectoryService.js | 51 +++++++- 3 files changed, 228 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 4b6843e..6127e21 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,4 @@ temp/ *.orig .husky/_ +.kilocode/ diff --git a/src/lib/utils/DirectoryLocator.js b/src/lib/utils/DirectoryLocator.js index 093ef14..37eb16c 100644 --- a/src/lib/utils/DirectoryLocator.js +++ b/src/lib/utils/DirectoryLocator.js @@ -90,13 +90,13 @@ class ProjectRootLocator extends DirectoryLocator { constructor(options = {}) { super(options) - // 可配置的查找策略优先级 + // 可配置的查找策略优先级(按可靠性和准确性排序) this.strategies = options.strategies || [ - 'existingPromptxDirectory', - 'currentWorkingDirectoryIfHasMarkers', - 'packageJsonDirectory', - 'gitRootDirectory', - 'currentWorkingDirectory' + 'existingPromptxDirectory', // 1. 现有.promptx目录(最可靠的项目标识) + 'packageJsonDirectory', // 2. 向上查找项目标识文件(最准确的项目边界) + 'gitRootDirectory', // 3. Git根目录(通用可靠) + 'currentWorkingDirectoryIfHasMarkers', // 4. 当前目录项目标识(降级策略) + 'currentWorkingDirectory' // 5. 纯当前目录(最后回退) ] // 项目标识文件 @@ -277,26 +277,26 @@ class PromptXWorkspaceLocator extends DirectoryLocator { */ async locate(context = {}) { const cacheKey = `promptxWorkspace:${JSON.stringify(context)}` - + // 检查缓存 const cached = this.getCached(cacheKey) if (cached) { return cached } - // 策略1:IDE环境变量 + // 策略1:IDE环境变量(最高优先级 - 用户/IDE明确指定) const workspaceFromIDE = await this._fromIDEEnvironment() if (workspaceFromIDE) { return this.setCached(cacheKey, workspaceFromIDE) } - // 策略2:PromptX专用环境变量 + // 策略2:PromptX专用环境变量(用户手动配置) const workspaceFromEnv = await this._fromPromptXEnvironment() if (workspaceFromEnv) { return this.setCached(cacheKey, workspaceFromEnv) } - // 策略3:如果上下文指定了特定策略(如init命令),直接使用项目根目录 + // 策略3:特定上下文策略(如init命令的强制指定) if (context.strategies) { const workspaceFromProject = await this._fromProjectRoot(context) if (workspaceFromProject) { @@ -304,40 +304,103 @@ class PromptXWorkspaceLocator extends DirectoryLocator { } } - // 策略4:现有.promptx目录 + // 策略4:现有.promptx目录(已初始化的项目) const workspaceFromExisting = await this._fromExistingDirectory(context.startDir) if (workspaceFromExisting) { return this.setCached(cacheKey, workspaceFromExisting) } - // 策略5:项目根目录 + // 策略5:项目根目录(基于项目结构推断) const workspaceFromProject = await this._fromProjectRoot(context) if (workspaceFromProject) { return this.setCached(cacheKey, workspaceFromProject) } - // 策略6:回退到当前目录 - return this.setCached(cacheKey, context.startDir || process.cwd()) + // 策略6:智能回退策略(兜底方案) + return this.setCached(cacheKey, await this._getSmartFallback(context)) } /** - * 从IDE环境变量获取 + * 从IDE环境变量获取(支持多种IDE) */ async _fromIDEEnvironment() { - const workspaceFolders = process.env.WORKSPACE_FOLDER_PATHS - if (workspaceFolders) { - try { - const folders = JSON.parse(workspaceFolders) - if (Array.isArray(folders) && folders.length > 0) { - const firstFolder = folders[0] - if (await this.isValidDirectory(firstFolder)) { - return firstFolder + // IDE环境变量检测策略(按优先级排序) + const ideStrategies = [ + // Claude IDE (现有格式) + { + name: 'Claude IDE', + vars: ['WORKSPACE_FOLDER_PATHS'], + parse: (value) => { + try { + const folders = JSON.parse(value) + return Array.isArray(folders) && folders.length > 0 ? folders[0] : null + } catch { + return null + } + } + }, + + // VSCode + { + name: 'VSCode', + vars: ['VSCODE_WORKSPACE_FOLDER', 'VSCODE_CWD'], + parse: (value) => value + }, + + // IntelliJ IDEA / WebStorm / PhpStorm + { + name: 'JetBrains IDEs', + vars: ['PROJECT_ROOT', 'IDEA_INITIAL_DIRECTORY', 'WEBSTORM_PROJECT_PATH'], + parse: (value) => value + }, + + // Sublime Text + { + name: 'Sublime Text', + vars: ['SUBLIME_PROJECT_PATH', 'SUBL_PROJECT_DIR'], + parse: (value) => value + }, + + // Atom + { + name: 'Atom', + vars: ['ATOM_PROJECT_PATH', 'ATOM_HOME_PROJECT'], + parse: (value) => value + }, + + // Vim/Neovim + { + name: 'Vim/Neovim', + vars: ['VIM_PROJECT_ROOT', 'NVIM_PROJECT_ROOT'], + parse: (value) => value + }, + + // 通用工作目录 + { + name: 'Generic', + vars: ['WORKSPACE_ROOT', 'PROJECT_DIR', 'WORKING_DIRECTORY'], + parse: (value) => value + } + ] + + // 按策略逐一检测 + for (const strategy of ideStrategies) { + for (const varName of strategy.vars) { + const envValue = process.env[varName] + if (envValue && envValue.trim() !== '') { + const parsedPath = strategy.parse(envValue.trim()) + if (parsedPath) { + const normalizedPath = this.normalizePath(this.expandHome(parsedPath)) + if (normalizedPath && await this.isValidDirectory(normalizedPath)) { + // 记录检测到的IDE类型(用于调试) + this._detectedIDE = strategy.name + return normalizedPath + } } } - } catch { - // 忽略解析错误 } } + return null } @@ -370,6 +433,95 @@ class PromptXWorkspaceLocator extends DirectoryLocator { const projectRoot = await this.projectRootLocator.locate(context) return projectRoot } + + /** + * 智能回退策略 + */ + async _getSmartFallback(context) { + // 1. 尝试从命令行参数推断 + const argPath = await this._fromProcessArguments() + if (argPath && await this.isValidDirectory(argPath)) { + return argPath + } + + // 2. 尝试从进程的工作目录 + const processCwd = process.cwd() + if (await this.isValidDirectory(processCwd)) { + return processCwd + } + + // 3. 最后回退到用户主目录 + return os.homedir() + } + + /** + * 从进程参数推断项目路径 + */ + async _fromProcessArguments() { + const args = process.argv + + // 查找可能的路径参数 + for (let i = 0; i < args.length; i++) { + const arg = args[i] + + // 查找 --project-path 或类似参数 + if (arg.startsWith('--project-path=')) { + return arg.split('=')[1] + } + + if (arg === '--project-path' && i + 1 < args.length) { + return args[i + 1] + } + + // 查找 --cwd 参数 + if (arg.startsWith('--cwd=')) { + return arg.split('=')[1] + } + + if (arg === '--cwd' && i + 1 < args.length) { + return args[i + 1] + } + } + + return null + } + + /** + * 获取检测调试信息 + */ + getDetectionInfo() { + return { + detectedIDE: this._detectedIDE || 'Unknown', + availableEnvVars: this._getAvailableEnvVars(), + platform: process.platform, + cwd: process.cwd(), + args: process.argv + } + } + + /** + * 获取可用的环境变量 + */ + _getAvailableEnvVars() { + const relevantVars = [ + 'WORKSPACE_FOLDER_PATHS', 'VSCODE_WORKSPACE_FOLDER', 'VSCODE_CWD', + 'PROJECT_ROOT', 'IDEA_INITIAL_DIRECTORY', 'WEBSTORM_PROJECT_PATH', + 'SUBLIME_PROJECT_PATH', 'SUBL_PROJECT_DIR', + 'ATOM_PROJECT_PATH', 'ATOM_HOME_PROJECT', + 'VIM_PROJECT_ROOT', 'NVIM_PROJECT_ROOT', + 'WORKSPACE_ROOT', 'PROJECT_DIR', 'WORKING_DIRECTORY', + 'PROMPTX_WORKSPACE', 'PWD' + ] + + const available = {} + for (const varName of relevantVars) { + if (process.env[varName]) { + available[varName] = process.env[varName] + } + } + + return available + } } /** diff --git a/src/lib/utils/DirectoryService.js b/src/lib/utils/DirectoryService.js index df22e5b..fac40db 100644 --- a/src/lib/utils/DirectoryService.js +++ b/src/lib/utils/DirectoryService.js @@ -150,15 +150,30 @@ class DirectoryService { const workspace = await this.getWorkspace(context) const promptxDir = await this.getPromptXDirectory(context) + // 获取IDE检测信息 + const ideDetectionInfo = this.workspaceLocator?.getDetectionInfo() || {} + return { platform: process.platform, projectRoot, workspace, promptxDirectory: promptxDir, isSame: projectRoot === workspace, + ideDetection: { + detectedIDE: ideDetectionInfo.detectedIDE, + availableEnvVars: ideDetectionInfo.availableEnvVars, + cwd: process.cwd(), + args: process.argv.slice(2) // 隐藏node和脚本路径 + }, environment: { + // 主要IDE环境变量 WORKSPACE_FOLDER_PATHS: process.env.WORKSPACE_FOLDER_PATHS, + VSCODE_WORKSPACE_FOLDER: process.env.VSCODE_WORKSPACE_FOLDER, + PROJECT_ROOT: process.env.PROJECT_ROOT, + SUBLIME_PROJECT_PATH: process.env.SUBLIME_PROJECT_PATH, + // PromptX专用 PROMPTX_WORKSPACE: process.env.PROMPTX_WORKSPACE, + // 系统环境 PWD: process.env.PWD, NODE_ENV: process.env.NODE_ENV }, @@ -166,10 +181,44 @@ class DirectoryService { cache: { projectRootCacheSize: this.projectRootLocator?.cache.size || 0, workspaceCacheSize: this.workspaceLocator?.cache.size || 0 - } + }, + recommendations: this._getPathRecommendations(ideDetectionInfo) } } + /** + * 获取路径配置建议 + */ + _getPathRecommendations(ideDetectionInfo = {}) { + const recommendations = [] + + if (!ideDetectionInfo.detectedIDE || ideDetectionInfo.detectedIDE === 'Unknown') { + recommendations.push({ + type: 'env_var', + message: '未检测到IDE环境变量,建议设置项目路径环境变量', + suggestions: [ + 'export PROMPTX_WORKSPACE="/path/to/your/project"', + 'export PROJECT_ROOT="/path/to/your/project"', + 'export WORKSPACE_ROOT="/path/to/your/project"' + ] + }) + } + + if (!ideDetectionInfo.availableEnvVars || Object.keys(ideDetectionInfo.availableEnvVars).length === 0) { + recommendations.push({ + type: 'manual_config', + message: '建议在IDE中配置MCP工作目录', + suggestions: [ + 'VSCode: 在settings.json中设置workspace.folders', + 'IntelliJ: 在Run Configuration中设置Working directory', + 'Claude IDE: 确保workspace路径正确传递' + ] + }) + } + + return recommendations + } + /** * 确保服务已初始化 */