Skip to main content
Glama

AutoDev Codebase MCP Server

by anrgct
250704-GLOBAL-CONFIG-IMPLEMENTATION.md14.4 kB
# 全局配置功能实现记录 ## 功能概述 为 autodev-codebase 项目实现全局配置文件支持,支持在 `~/.autodev-cache/autodev-config.json` 中设置全局默认配置。 ## 实现目标 建立新的配置优先级系统: ``` 最高优先级 → CLI 参数 (仅当显式提供时) ↓ 项目配置文件 (./autodev-config.json) ↓ 全局配置文件 (~/.autodev-cache/autodev-config.json) ↓ 最低优先级 → DEFAULT_CONFIG ``` ## 实现步骤 ### 1. 扩展配置接口 **文件**: `src/adapters/nodejs/config.ts` **修改内容**: ```typescript export interface NodeConfigOptions { configPath?: string globalConfigPath?: string // 新增 defaultConfig?: Partial<CodeIndexConfig> cliOverrides?: { ollamaUrl?: string model?: string qdrantUrl?: string } } ``` ### 2. 增强 NodeConfigProvider **文件**: `src/adapters/nodejs/config.ts` **关键修改**: 1. 添加 `globalConfigPath` 属性 2. 导入 `os` 模块支持 `os.homedir()` 3. 重写 `loadConfig()` 方法 **新的 loadConfig() 逻辑**: ```typescript async loadConfig(): Promise<CodeIndexConfig> { // 1. 从默认配置开始 this.config = { ...DEFAULT_CONFIG } // 2. 加载全局配置(如果存在) try { if (await this.fileSystem.exists(this.globalConfigPath)) { const globalConfig = JSON.parse(globalText) this.config = { ...this.config, ...globalConfig } } } catch (error) { console.warn(`Failed to load global config: ${error}`) } // 3. 加载项目配置(如果存在) try { if (await this.fileSystem.exists(this.configPath)) { const projectConfig = JSON.parse(projectText) this.config = { ...this.config, ...projectConfig } } } catch (error) { console.warn(`Failed to load project config: ${error}`) } // 4. 应用 CLI 覆盖(最高优先级) if (this.cliOverrides && this.config) { // 应用 CLI 参数覆盖 } return this.config } ``` ### 3. 更新依赖创建函数 **文件**: `src/adapters/nodejs/index.ts` **修改内容**: ```typescript export function createNodeDependencies(options: { workspacePath: string storageOptions?: NodeStorageOptions loggerOptions?: NodeLoggerOptions configOptions?: NodeConfigOptions }) { // 确保全局配置目录存在 const globalConfigDir = path.join(os.homedir(), '.autodev-cache') if (!fs.existsSync(globalConfigDir)) { fs.mkdirSync(globalConfigDir, { recursive: true }) } // 配置全局配置路径 const configOptions = { ...options.configOptions, globalConfigPath: options.configOptions?.globalConfigPath || path.join(globalConfigDir, 'autodev-config.json') } const configProvider = new NodeConfigProvider(fileSystem, eventBus, configOptions) // 返回依赖 } ``` ## 配置文件示例 ### 全局配置文件示例 **路径**: `~/.autodev-cache/autodev-config.json` ```json { "isEnabled": true, "isConfigured": true, "embedder": { "provider": "ollama", "model": "nomic-embed-text", "dimension": 768, "baseUrl": "http://localhost:11434" }, "qdrantUrl": "http://localhost:6333" } ``` ### 项目配置文件示例 **路径**: `./autodev-config.json` ```json { "embedder": { "model": "project-specific-model", "dimension": 1024 }, "qdrantUrl": "http://localhost:6334" } ``` ## 使用方式 ### 1. 设置全局默认配置 ```bash # 编辑全局配置文件 vim ~/.autodev-cache/autodev-config.json ``` ### 2. 项目级配置覆盖 ```bash # 在项目根目录创建配置文件 echo '{"embedder": {"model": "project-model"}}' > autodev-config.json ``` ### 3. CLI 参数覆盖 ```bash # 使用 CLI 参数覆盖配置 npx tsx src/index.ts --model="cli-model" --qdrant-url="http://localhost:7777" ``` ## 向后兼容性 - ✅ 现有项目配置文件继续工作 - ✅ 现有 CLI 参数继续工作 - ✅ 不影响现有默认配置行为 - ✅ 可选功能,不强制使用全局配置 ## 技术细节 ### 配置合并逻辑 使用 JavaScript 对象展开运算符 (`...`) 进行配置合并,后面的配置会覆盖前面的配置: ```typescript this.config = { ...DEFAULT_CONFIG, // 默认配置 ...globalConfig, // 全局配置覆盖 ...projectConfig, // 项目配置覆盖 // CLI 参数通过单独逻辑处理 } ``` ### 目录自动创建 在 `createNodeDependencies` 函数中自动创建 `~/.autodev-cache` 目录: ```typescript const globalConfigDir = path.join(os.homedir(), '.autodev-cache') if (!fs.existsSync(globalConfigDir)) { fs.mkdirSync(globalConfigDir, { recursive: true }) } ``` ### 错误处理 - 全局配置文件不存在时不会报错,继续使用默认配置 - 配置文件解析错误时会输出警告,但不会中断程序 - 配置文件权限错误时会输出警告并继续 ## 最佳实践 ### 1. 全局配置设置 建议在全局配置中设置常用的默认值: ```json { "isEnabled": true, "isConfigured": true, "embedder": { "provider": "ollama", "model": "nomic-embed-text", "dimension": 768, "baseUrl": "http://localhost:11434" }, "qdrantUrl": "http://localhost:6333" } ``` ### 2. 项目配置设置 项目配置只需要设置与全局配置不同的部分: ```json { "embedder": { "model": "project-specific-model" } } ``` ### 3. CLI 参数使用 CLI 参数适合临时测试或特殊情况: ```bash # 临时使用不同的模型 npx tsx src/index.ts --model="test-model" # 连接到不同的 Qdrant 实例 npx tsx src/index.ts --qdrant-url="http://remote-server:6333" ``` ## 调试技巧 ### 1. 查看配置加载日志 运行时会输出配置加载信息: ``` Global config loaded from: ~/.autodev-cache/autodev-config.json Project config loaded from: ./autodev-config.json ``` ### 2. 验证配置合并 可以通过调试模式查看最终的配置: ```bash npx tsx src/index.ts --log-level=debug ``` ### 3. 检查配置文件 验证配置文件语法和内容: ```bash # 检查全局配置 cat ~/.autodev-cache/autodev-config.json | jq . # 检查项目配置 cat ./autodev-config.json | jq . ``` ## 相关文件 - `src/adapters/nodejs/config.ts` - 配置提供者实现 - `src/adapters/nodejs/index.ts` - 依赖创建函数 - `src/code-index/interfaces/config.ts` - 配置接口定义 - `src/cli/tui-runner.ts` - TUI 运行器配置传递 - `src/cli/args-parser.ts` - CLI 参数解析 ## 后续改进建议 1. **配置文件发现**:支持多个配置文件位置的自动发现 2. **环境变量支持**:添加环境变量配置支持 3. **配置验证增强**:增强配置加载后的验证机制 4. **配置文件模板**:提供配置文件模板和生成工具 5. **配置合并可视化**:提供工具显示配置合并结果 ## 经验总结 ### 成功要素 1. **渐进式实现**:逐步添加功能,确保每步都可测试 2. **向后兼容**:保持现有功能不受影响 3. **错误处理**:妥善处理配置文件缺失和解析错误 4. **测试驱动**:编写测试验证功能正确性 ### 技术要点 1. **配置合并**:使用对象展开运算符简化配置合并 2. **路径处理**:使用 `path.join()` 和 `os.homedir()` 处理跨平台路径 3. **目录创建**:自动创建必要的目录结构 4. **类型安全**:扩展 TypeScript 接口保持类型安全 ### 调试经验 1. **日志输出**:在关键位置输出配置加载状态 2. **分步验证**:逐步验证每个配置源的加载情况 3. **优先级测试**:创建测试用例验证配置优先级 4. **实际运行验证**:在实际应用中验证配置加载效果 这次实现为项目提供了灵活的配置管理系统,用户可以根据需要在全局、项目和 CLI 三个层级设置配置,大大提升了使用体验。 ## 后续问题修复:配置多次加载问题 ### 问题发现 在实现全局配置功能后,发现运行应用程序时出现配置重复加载的问题: ```bash Global config loaded from: ~/.autodev-cache/autodev-config.json Project config loaded from: autodev-config.json Global config loaded from: ~/.autodev-cache/autodev-config.json Project config loaded from: autodev-config.json Global config loaded from: ~/.autodev-cache/autodev-config.json Project config loaded from: autodev-config.json # ... 多次重复 ``` ### 问题根因分析 通过代码分析发现,`NodeConfigProvider` 中多个方法都会调用 `loadConfig()`: ```bash $ grep -n "loadConfig" src/adapters/nodejs/config.ts 60: const config = await this.loadConfig() # getEmbedderConfig() 99: const config = await this.loadConfig() # getVectorStoreConfig() 111: const config = await this.loadConfig() # getSearchConfig() 119: return this.loadConfig() # getConfig() 291: const config = await this.loadConfig() # validateConfig() ``` 每次这些方法被调用时,都会重新从文件系统加载配置文件,导致: 1. **性能问题**:重复的文件 I/O 操作 2. **日志污染**:大量重复的配置加载日志 3. **资源浪费**:不必要的文件系统访问 ### 解决方案:配置缓存机制 #### 1. 添加缓存状态管理 **文件**: `src/adapters/nodejs/config.ts` ```typescript export class NodeConfigProvider implements IConfigProvider { private configPath: string private globalConfigPath: string private config: CodeIndexConfig | null = null private configLoaded: boolean = false // 新增:缓存标志 private changeCallbacks: Array<(config: CodeIndexConfig) => void> = [] private cliOverrides: NodeConfigOptions['cliOverrides'] // ... } ``` #### 2. 实现缓存检查机制 ```typescript /** * Ensure configuration is loaded (with caching) */ private async ensureConfigLoaded(): Promise<CodeIndexConfig> { if (!this.configLoaded) { await this.loadConfig() } return this.config! } ``` #### 3. 重构配置访问方法 将所有配置 getter 方法改为使用缓存机制: ```typescript // 修改前 async getEmbedderConfig(): Promise<EmbedderConfig> { const config = await this.loadConfig() // 每次都重新加载 // ... } // 修改后 async getEmbedderConfig(): Promise<EmbedderConfig> { const config = await this.ensureConfigLoaded() // 使用缓存 // ... } ``` 同样的修改应用到: - `getVectorStoreConfig()` - `getSearchConfig()` - `getConfig()` - `validateConfig()` #### 4. 状态管理和强制重载 ```typescript async loadConfig(): Promise<CodeIndexConfig> { // ... 配置加载逻辑 ... // 标记为已加载,启用缓存 this.configLoaded = true return this.config || { ...DEFAULT_CONFIG } } /** * Force reload configuration from files (bypasses cache) */ async reloadConfig(): Promise<CodeIndexConfig> { this.configLoaded = false return this.loadConfig() } async saveConfig(config: Partial<CodeIndexConfig>): Promise<void> { // ... 保存逻辑 ... this.config = newConfig this.configLoaded = true // 标记为已加载 // ... } ``` #### 5. 清理调试日志 为了进一步减少日志噪音,将调试信息注释掉: ```typescript // 修改前 console.log(`Global config loaded from: ${this.globalConfigPath}`) console.log(`Project config loaded from: ${this.configPath}`) // 修改后 // console.log(`Global config loaded from: ${this.globalConfigPath}`) // console.log(`Project config loaded from: ${this.configPath}`) ``` ### 修复验证 #### 测试用例验证 创建测试脚本验证缓存机制: ```typescript // 多次调用不同的配置方法 const config1 = await deps.configProvider.getConfig() const embedderConfig = await deps.configProvider.getEmbedderConfig() const validation = await deps.configProvider.validateConfig() const vectorConfig = await deps.configProvider.getVectorStoreConfig() // 结果:只有第一次调用时加载配置文件 ``` #### 实际运行验证 运行应用程序后,配置加载日志从多次重复变为单次加载: ```bash # 修复前 Global config loaded from: ~/.autodev-cache/autodev-config.json Project config loaded from: autodev-config.json Global config loaded from: ~/.autodev-cache/autodev-config.json Project config loaded from: autodev-config.json # ... 多次重复 # 修复后 [INFO] Loading configuration... [INFO] Configuration validation passed # 干净的日志输出,无重复 ``` ### 性能优化效果 1. **I/O 减少**:配置文件读取从多次减少到单次 2. **日志清洁**:消除重复的配置加载日志 3. **响应速度**:后续配置访问直接使用内存缓存 4. **资源优化**:减少不必要的文件系统访问 ### 缓存机制设计原则 1. **懒加载**:只在需要时加载配置 2. **单次加载**:首次加载后使用缓存 3. **状态一致性**:配置修改时正确更新缓存状态 4. **强制刷新**:提供重新加载机制应对配置文件外部修改 5. **向后兼容**:不改变现有 API 接口 ### 缓存失效策略 配置缓存在以下情况会被重置: 1. **显式重载**:调用 `reloadConfig()` 方法 2. **配置保存**:调用 `saveConfig()` 或相关方法 3. **实例重创建**:NodeConfigProvider 实例重新创建 ### 最佳实践总结 #### 配置访问模式 ```typescript // ✅ 推荐:使用缓存的方法 const config = await configProvider.getConfig() const embedderConfig = await configProvider.getEmbedderConfig() // ❌ 避免:直接调用 loadConfig()(除非明确需要重新加载) const config = await configProvider.loadConfig() ``` #### 强制重新加载场景 ```typescript // 配置文件被外部修改时 await configProvider.reloadConfig() // 或者在需要确保最新配置时 const latestConfig = await configProvider.reloadConfig() ``` ### 经验教训 1. **设计考虑**:在实现配置系统时,应该提前考虑缓存机制 2. **性能监控**:注意观察重复操作,及时发现性能问题 3. **渐进优化**:先实现功能,再优化性能,避免过早优化 4. **测试驱动**:通过测试用例验证缓存机制的正确性 5. **日志管理**:合理控制日志输出,避免信息噪音 这次缓存优化不仅解决了重复加载问题,还为配置系统提供了更好的性能基础,为后续的功能扩展奠定了良好的架构基础。

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/anrgct/autodev-codebase'

If you have feedback or need assistance with the MCP directory API, please join our Discord server