# messages

id: d184c722-4eab-4da8-9f8a-0da1caa0e711
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"我已审查 `src/index.ts` 文件。现在我将审查最后一个 TypeScript 文件：`src/logger.ts`。"}},{"type":"tool_call","data":{"id":"tool_0_view","name":"view","input":"{\"file_path\":\"src/logger.ts\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754061172}}]


id: ed3f94f8-77fe-4967-a07e-e4df9754b475
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: tool
provider: openrouter
parts:
  [{"type":"tool_result","data":{"tool_call_id":"tool_0_view","name":"","content":"\u003cfile\u003e\n     1|import { writeFile, appendFile, mkdir } from 'fs/promises';\n     2|import { join } from 'path';\n     3|import { tmpdir } from 'os';\n     4|\n     5|/**\n     6| * 日志级别\n     7| */\n     8|export enum LogLevel {\n     9|  DEBUG = 0,\n    10|  INFO = 1,\n    11|  WARN = 2,\n    12|  ERROR = 3,\n    13|}\n    14|\n    15|/**\n    16| * 日志条目接口\n    17| */\n    18|interface LogEntry {\n    19|  timestamp: string;\n    20|  level: LogLevel;\n    21|  message: string;\n    22|  context?: Record\u003cstring, any\u003e;\n    23|  error?: Error;\n    24|}\n    25|\n    26|/**\n    27| * 简单的日志记录器\n    28| */\n    29|export class Logger {\n    30|  private logLevel: LogLevel;\n    31|  private logFile?: string;\n    32|\n    33|  constructor(level: LogLevel = LogLevel.INFO, logFile?: string) {\n    34|    this.logLevel = level;\n    35|    this.logFile = logFile;\n    36|\n    37|    // 如果没有指定日志文件，使用临时目录\n    38|    if (!this.logFile) {\n    39|      this.logFile = join(tmpdir(), 'coderocket-mcp.log');\n    40|    }\n    41|  }\n    42|\n    43|  /**\n    44|   * 记录调试信息\n    45|   */\n    46|  debug(message: string, context?: Record\u003cstring, any\u003e) {\n    47|    this.log(LogLevel.DEBUG, message, context);\n    48|  }\n    49|\n    50|  /**\n    51|   * 记录信息\n    52|   */\n    53|  info(message: string, context?: Record\u003cstring, any\u003e) {\n    54|    this.log(LogLevel.INFO, message, context);\n    55|  }\n    56|\n    57|  /**\n    58|   * 记录警告\n    59|   */\n    60|  warn(message: string, context?: Record\u003cstring, any\u003e) {\n    61|    this.log(LogLevel.WARN, message, context);\n    62|  }\n    63|\n    64|  /**\n    65|   * 记录错误\n    66|   */\n    67|  error(message: string, error?: Error, context?: Record\u003cstring, any\u003e) {\n    68|    this.log(LogLevel.ERROR, message, context, error);\n    69|  }\n    70|\n    71|  /**\n    72|   * 核心日志记录方法\n    73|   */\n    74|  private async log(\n    75|    level: LogLevel,\n    76|    message: string,\n    77|    context?: Record\u003cstring, any\u003e,\n    78|    error?: Error,\n    79|  ) {\n    80|    if (level \u003c this.logLevel) {\n    81|      return;\n    82|    }\n    83|\n    84|    const entry: LogEntry = {\n    85|      timestamp: new Date().toISOString(),\n    86|      level,\n    87|      message,\n    88|      context,\n    89|      error,\n    90|    };\n    91|\n    92|    // 在 MCP 服务器模式下，只有在 DEBUG 模式或 WARN/ERROR 级别时才输出到控制台\n    93|    // 这避免了 IDE 误认为 INFO 级别的日志是错误信息\n    94|    const shouldOutputToConsole =\n    95|      process.env.DEBUG === 'true' || level \u003e= LogLevel.WARN;\n    96|\n    97|    if (shouldOutputToConsole) {\n    98|      const levelName = LogLevel[level];\n    99|      const contextStr = context ? ` ${JSON.stringify(context)}` : '';\n   100|      const errorStr = error ? ` Error: ${error.message}` : '';\n   101|\n   102|      // 所有控制台输出都使用 stderr 以避免污染 MCP 协议的 stdout\n   103|      console.error(\n   104|        `[${entry.timestamp}] ${levelName}: ${message}${contextStr}${errorStr}`,\n   105|      );\n   106|    }\n   107|\n   108|    // 写入日志文件\n   109|    if (this.logFile) {\n   110|      try {\n   111|        // 处理 Error 对象，避免循环结构\n   112|        let logEntry = entry;\n   113|        if (entry.error instanceof Error) {\n   114|          logEntry = {\n   115|            ...entry,\n   116|            error: {\n   117|              name: entry.error.name,\n   118|              message: entry.error.message,\n   119|              stack: entry.error.stack,\n   120|            },\n   121|          };\n   122|        }\n   123|        const logLine = JSON.stringify(logEntry) + '\\n';\n   124|        await appendFile(this.logFile, logLine, 'utf-8');\n   125|      } catch (fileError) {\n   126|        console.error('Failed to write to log file:', fileError);\n   127|      }\n   128|    }\n   129|  }\n   130|\n   131|  /**\n   132|   * 获取日志文件路径\n   133|   */\n   134|  getLogFile(): string | undefined {\n   135|    return this.logFile;\n   136|  }\n   137|\n   138|  /**\n   139|   * 设置日志级别\n   140|   */\n   141|  setLevel(level: LogLevel) {\n   142|    this.logLevel = level;\n   143|  }\n   144|}\n   145|\n   146|// 全局日志实例\n   147|export const logger = new Logger(\n   148|  process.env.NODE_ENV === 'development' ? LogLevel.DEBUG : LogLevel.INFO,\n   149|);\n   150|\n   151|/**\n   152| * 错误处理工具类\n   153| */\n   154|export class ErrorHandler {\n   155|  private logger: Logger;\n   156|\n   157|  constructor(logger: Logger) {\n   158|    this.logger = logger;\n   159|  }\n   160|\n   161|  /**\n   162|   * 处理并格式化错误\n   163|   */\n   164|  handleError(error: unknown, context?: string): Error {\n   165|    let formattedError: Error;\n   166|\n   167|    if (error instanceof Error) {\n   168|      formattedError = error;\n   169|    } else if (typeof error === 'string') {\n   170|      formattedError = new Error(error);\n   171|    } else {\n   172|      formattedError = new Error(`Unknown error: ${JSON.stringify(error)}`);\n   173|    }\n   174|\n   175|    // 记录错误\n   176|    this.logger.error(\n   177|      `${context ? `[${context}] ` : ''}${formattedError.message}`,\n   178|      formattedError,\n   179|      { context },\n   180|    );\n   181|\n   182|    return formattedError;\n   183|  }\n   184|\n   185|  /**\n   186|   * 包装异步函数，自动处理错误\n   187|   */\n   188|  wrapAsync\u003cT extends any[], R\u003e(\n   189|    fn: (...args: T) =\u003e Promise\u003cR\u003e,\n   190|    context?: string,\n   191|  ): (...args: T) =\u003e Promise\u003cR\u003e {\n   192|    return async (...args: T): Promise\u003cR\u003e =\u003e {\n   193|      try {\n   194|        return await fn(...args);\n   195|      } catch (error) {\n   196|        throw this.handleError(error, context);\n   197|      }\n   198|    };\n   199|  }\n   200|\n   201|  /**\n   202|   * 创建用户友好的错误消息\n   203|   */\n   204|  createUserFriendlyError(\n   205|    error: Error,\n   206|    suggestions?: string[],\n   207|  ): {\n   208|    error: string;\n   209|    error_code: string;\n   210|    suggestions: string[];\n   211|  } {\n   212|    // 根据错误类型提供不同的错误代码和建议\n   213|    let errorCode = 'UNKNOWN_ERROR';\n   214|    let userSuggestions = suggestions || [];\n   215|\n   216|    if (error.message.includes('Shell command failed')) {\n   217|      errorCode = 'SHELL_COMMAND_ERROR';\n   218|      userSuggestions = [\n   219|        '验证AI服务是否已配置',\n   220|        '检查API密钥是否正确设置',\n   221|        '确保有足够的权限执行命令',\n   222|        ...userSuggestions,\n   223|      ];\n   224|    } else if (\n   225|      error.message.includes('AI服务') ||\n   226|      error.message.includes('AI service')\n   227|    ) {\n   228|      errorCode = 'AI_SERVICE_ERROR';\n   229|      userSuggestions = [\n   230|        '检查AI服务配置',\n   231|        '验证API密钥是否正确',\n   232|        '确保网络连接正常',\n   233|        '尝试切换到其他AI服务',\n   234|        ...userSuggestions,\n   235|      ];\n   236|    } else if (\n   237|      error.message.includes('文件') ||\n   238|      error.message.includes('file')\n   239|    ) {\n   240|      errorCode = 'FILE_ERROR';\n   241|      userSuggestions = [\n   242|        '检查文件路径是否正确',\n   243|        '确保文件存在且可读',\n   244|        '验证文件权限',\n   245|        ...userSuggestions,\n   246|      ];\n   247|    } else if (error.message.includes('Git') || error.message.includes('git')) {\n   248|      errorCode = 'GIT_ERROR';\n   249|      userSuggestions = [\n   250|        '确保在Git仓库中执行',\n   251|        '检查Git仓库状态',\n   252|        '验证提交哈希是否存在',\n   253|        ...userSuggestions,\n   254|      ];\n   255|    }\n   256|\n   257|    return {\n   258|      error: error.message,\n   259|      error_code: errorCode,\n   260|      suggestions: userSuggestions,\n   261|    };\n   262|  }\n   263|}\n   264|\n   265|// 全局错误处理器实例\n   266|export const errorHandler = new ErrorHandler(logger);\n\u003c/file\u003e\n","metadata":"{\"file_path\":\"/Users/dreambt/sources/coderocket/coderocket-mcp/src/logger.ts\",\"content\":\"import { writeFile, appendFile, mkdir } from 'fs/promises';\\nimport { join } from 'path';\\nimport { tmpdir } from 'os';\\n\\n/**\\n * 日志级别\\n */\\nexport enum LogLevel {\\n  DEBUG = 0,\\n  INFO = 1,\\n  WARN = 2,\\n  ERROR = 3,\\n}\\n\\n/**\\n * 日志条目接口\\n */\\ninterface LogEntry {\\n  timestamp: string;\\n  level: LogLevel;\\n  message: string;\\n  context?: Record\\u003cstring, any\\u003e;\\n  error?: Error;\\n}\\n\\n/**\\n * 简单的日志记录器\\n */\\nexport class Logger {\\n  private logLevel: LogLevel;\\n  private logFile?: string;\\n\\n  constructor(level: LogLevel = LogLevel.INFO, logFile?: string) {\\n    this.logLevel = level;\\n    this.logFile = logFile;\\n\\n    // 如果没有指定日志文件，使用临时目录\\n    if (!this.logFile) {\\n      this.logFile = join(tmpdir(), 'coderocket-mcp.log');\\n    }\\n  }\\n\\n  /**\\n   * 记录调试信息\\n   */\\n  debug(message: string, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.DEBUG, message, context);\\n  }\\n\\n  /**\\n   * 记录信息\\n   */\\n  info(message: string, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.INFO, message, context);\\n  }\\n\\n  /**\\n   * 记录警告\\n   */\\n  warn(message: string, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.WARN, message, context);\\n  }\\n\\n  /**\\n   * 记录错误\\n   */\\n  error(message: string, error?: Error, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.ERROR, message, context, error);\\n  }\\n\\n  /**\\n   * 核心日志记录方法\\n   */\\n  private async log(\\n    level: LogLevel,\\n    message: string,\\n    context?: Record\\u003cstring, any\\u003e,\\n    error?: Error,\\n  ) {\\n    if (level \\u003c this.logLevel) {\\n      return;\\n    }\\n\\n    const entry: LogEntry = {\\n      timestamp: new Date().toISOString(),\\n      level,\\n      message,\\n      context,\\n      error,\\n    };\\n\\n    // 在 MCP 服务器模式下，只有在 DEBUG 模式或 WARN/ERROR 级别时才输出到控制台\\n    // 这避免了 IDE 误认为 INFO 级别的日志是错误信息\\n    const shouldOutputToConsole =\\n      process.env.DEBUG === 'true' || level \\u003e= LogLevel.WARN;\\n\\n    if (shouldOutputToConsole) {\\n      const levelName = LogLevel[level];\\n      const contextStr = context ? ` ${JSON.stringify(context)}` : '';\\n      const errorStr = error ? ` Error: ${error.message}` : '';\\n\\n      // 所有控制台输出都使用 stderr 以避免污染 MCP 协议的 stdout\\n      console.error(\\n        `[${entry.timestamp}] ${levelName}: ${message}${contextStr}${errorStr}`,\\n      );\\n    }\\n\\n    // 写入日志文件\\n    if (this.logFile) {\\n      try {\\n        // 处理 Error 对象，避免循环结构\\n        let logEntry = entry;\\n        if (entry.error instanceof Error) {\\n          logEntry = {\\n            ...entry,\\n            error: {\\n              name: entry.error.name,\\n              message: entry.error.message,\\n              stack: entry.error.stack,\\n            },\\n          };\\n        }\\n        const logLine = JSON.stringify(logEntry) + '\\\\n';\\n        await appendFile(this.logFile, logLine, 'utf-8');\\n      } catch (fileError) {\\n        console.error('Failed to write to log file:', fileError);\\n      }\\n    }\\n  }\\n\\n  /**\\n   * 获取日志文件路径\\n   */\\n  getLogFile(): string | undefined {\\n    return this.logFile;\\n  }\\n\\n  /**\\n   * 设置日志级别\\n   */\\n  setLevel(level: LogLevel) {\\n    this.logLevel = level;\\n  }\\n}\\n\\n// 全局日志实例\\nexport const logger = new Logger(\\n  process.env.NODE_ENV === 'development' ? LogLevel.DEBUG : LogLevel.INFO,\\n);\\n\\n/**\\n * 错误处理工具类\\n */\\nexport class ErrorHandler {\\n  private logger: Logger;\\n\\n  constructor(logger: Logger) {\\n    this.logger = logger;\\n  }\\n\\n  /**\\n   * 处理并格式化错误\\n   */\\n  handleError(error: unknown, context?: string): Error {\\n    let formattedError: Error;\\n\\n    if (error instanceof Error) {\\n      formattedError = error;\\n    } else if (typeof error === 'string') {\\n      formattedError = new Error(error);\\n    } else {\\n      formattedError = new Error(`Unknown error: ${JSON.stringify(error)}`);\\n    }\\n\\n    // 记录错误\\n    this.logger.error(\\n      `${context ? `[${context}] ` : ''}${formattedError.message}`,\\n      formattedError,\\n      { context },\\n    );\\n\\n    return formattedError;\\n  }\\n\\n  /**\\n   * 包装异步函数，自动处理错误\\n   */\\n  wrapAsync\\u003cT extends any[], R\\u003e(\\n    fn: (...args: T) =\\u003e Promise\\u003cR\\u003e,\\n    context?: string,\\n  ): (...args: T) =\\u003e Promise\\u003cR\\u003e {\\n    return async (...args: T): Promise\\u003cR\\u003e =\\u003e {\\n      try {\\n        return await fn(...args);\\n      } catch (error) {\\n        throw this.handleError(error, context);\\n      }\\n    };\\n  }\\n\\n  /**\\n   * 创建用户友好的错误消息\\n   */\\n  createUserFriendlyError(\\n    error: Error,\\n    suggestions?: string[],\\n  ): {\\n    error: string;\\n    error_code: string;\\n    suggestions: string[];\\n  } {\\n    // 根据错误类型提供不同的错误代码和建议\\n    let errorCode = 'UNKNOWN_ERROR';\\n    let userSuggestions = suggestions || [];\\n\\n    if (error.message.includes('Shell command failed')) {\\n      errorCode = 'SHELL_COMMAND_ERROR';\\n      userSuggestions = [\\n        '验证AI服务是否已配置',\\n        '检查API密钥是否正确设置',\\n        '确保有足够的权限执行命令',\\n        ...userSuggestions,\\n      ];\\n    } else if (\\n      error.message.includes('AI服务') ||\\n      error.message.includes('AI service')\\n    ) {\\n      errorCode = 'AI_SERVICE_ERROR';\\n      userSuggestions = [\\n        '检查AI服务配置',\\n        '验证API密钥是否正确',\\n        '确保网络连接正常',\\n        '尝试切换到其他AI服务',\\n        ...userSuggestions,\\n      ];\\n    } else if (\\n      error.message.includes('文件') ||\\n      error.message.includes('file')\\n    ) {\\n      errorCode = 'FILE_ERROR';\\n      userSuggestions = [\\n        '检查文件路径是否正确',\\n        '确保文件存在且可读',\\n        '验证文件权限',\\n        ...userSuggestions,\\n      ];\\n    } else if (error.message.includes('Git') || error.message.includes('git')) {\\n      errorCode = 'GIT_ERROR';\\n      userSuggestions = [\\n        '确保在Git仓库中执行',\\n        '检查Git仓库状态',\\n        '验证提交哈希是否存在',\\n        ...userSuggestions,\\n      ];\\n    }\\n\\n    return {\\n      error: error.message,\\n      error_code: errorCode,\\n      suggestions: userSuggestions,\\n    };\\n  }\\n}\\n\\n// 全局错误处理器实例\\nexport const errorHandler = new ErrorHandler(logger);\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: d18306c4-ceb3-4a4a-9f0e-e88267100c46
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"好的，我已经完成了对 `src` 目录下的所有 TypeScript 文件的审查。以下是详细的代码质量报告、修复方案和 TODO 列表。\n\n### 整体代码质量审查报告\n\n#### 总体评价\n\n项目代码结构清晰，模块化程度较好，特别是在配置管理 (`ConfigManager`)、提示词管理 (`PromptManager`) 和 AI 服务管理 (`SmartAIManager`) 方面，职责划分明确。代码的可读性和可维护性都处于良好水平，并且注释覆盖率较高。\n\n然而，项目中也存在一些可以改进的地方，主要集中在以下几个方面：\n\n*   **硬编码问题：** 多处存在硬编码的字符串、路径和配置项，降低了代码的灵活性和可维护性。\n*   **代码重复：** 在不同模块和函数间存在一些重复的逻辑，可以进行抽象和重构。\n*   **错误处理：** 错误处理机制虽然存在，但在某些地方可以更具体、更健壮，以提高系统的稳定性。\n*   **配置和初始化：** 模块间的初始化顺序存在隐式依赖，可以通过更明确的依赖注入或初始化流程来优化。\n\n接下来是每个文件的详细分析和修复建议。\n\n---\n\n### 文件审查详情\n\n#### `src/coderocket.ts`\n\n**评价：** 这是项目的核心文件，包含了配置、提示词和 AI 服务的核心逻辑。代码结构复杂但组织良好，是整个系统的枢纽。\n\n**问题与修复建议：**\n\n1.  **【问题】硬编码的配置和模型名称**\n    *   **描述：** `ConfigManager` 中的默认配置（如超时时间 `AI_TIMEOUT`）、`GeminiService` 和 `ClaudeCodeService` 中的 AI 模型名称（如 `'gemini-1.5-flash'`），以及 `reviewFiles` 函数中的文件内容截断长度都是硬编码的。\n    *   **修复建议：** 将这些值移入 `ConfigManager` 的默认配置中，使其可以通过配置文件或环境变量进行覆盖，提高灵活性。\n2.  **【问题】脆弱的审查结果解析逻辑**\n    *   **描述：** `parseReviewResult` 函数通过匹配关键字（如 \"✅\", \"通过\"）来判断审查状态，这种方法非常脆弱，一旦 AI 的输出格式稍有变化，解析就会失败。\n    *   **修复建议：** 在 AI 提示词中要求以更结构化的格式（如 JSON）返回审查结果，让解析过程更稳定、更可靠。\n3.  **【问题】重复的提示词构建逻辑**\n    *   **描述：** 各个 `review...` 方法中构建审查提示词的逻辑相似，特别是语言要求的部分（“请务必使用中文回复”）在多处重复。\n    *   **修复建议：** 创建一个 `PromptBuilder` 类或辅助函数，用于统一生成不同审查场景下的提示词，封装重复的逻辑（如添加语言指令）。\n4.  **【问题】脆弱的初始化顺序依赖**\n    *   **描述：** 多个类（如 `GeminiService`, `ClaudeCodeService`）都依赖 `ConfigManager` 首先被初始化。代码中通过 `(ConfigManager as any).initialized` 这种非类型安全的方式进行检查，是一种“代码异味”。\n    *   **修复建议：** 采用依赖注入（DI）的方式，在创建 `CodeRocketService` 实例时，将已初始化的 `ConfigManager` 实例传递进去。或者设计一个更明确的、集中的应用初始化流程。\n5.  **【问题】安全风险：暴露原始错误信息**\n    *   **描述：** `SmartAIManager` 在所有服务调用失败后，会将所有底层的错误信息拼接成一个新的错误抛出。这可能在日志中无意间泄露 API Key 等敏感信息。\n    *   **修复建议：** 抛出更通用的错误信息，并将详细的底层错误记录在安全日志中，而不是直接暴露给上层调用者。\n\n#### `src/index.ts`\n\n**评价：** 作为项目的入口和 MCP 服务器的实现，该文件清晰地定义了工具和请求处理器，但存在一些重复代码和硬编码。\n\n**问题与修复建议：**\n\n1.  **【问题】硬编码的服务器版本号**\n    *   **描述：** 服务器版本号 `'1.3.2'` 是硬编码的。\n    *   **修复建议：** 从 `package.json` 文件中动态读取版本号，确保与项目版本同步。\n2.  **【问题】重复的工具调用处理逻辑**\n    *   **描述：** `switch` 语句中处理每个 `review_...` 工具的逻辑非常相似（解析参数、调用服务、返回结果）。\n    *   **修复建议：** 创建一个高阶函数或工具注册表，将这些重复的逻辑抽象出来，简化工具处理器的代码。\n3.  **【问题】通用的错误返回**\n    *   **描述：** 工具执行失败时返回的错误信息是固定的 JSON，没有充分利用 `logger.ts` 中 `ErrorHandler` 提供的更具体的错误分类能力。\n    *   **修复建议：** 集成 `errorHandler.createUserFriendlyError` 方法，根据错误的类型生成更具体、更有帮助的错误响应。\n\n#### `src/logger.ts`\n\n**评价：** 提供了一个简单实用的日志系统和错误处理工具，但在持久化和健壮性方面有待加强。\n\n**问题与修复建议：**\n\n1.  **【问题】缺乏日志轮转机制**\n    *   **描述：** 日志持续写入单个文件，可能导致该文件无限增大。\n    *   **修复建议：** 引入一个简单的日志轮转策略，例如在启动时检查日志文件大小，如果超过阈值（如 10MB），则将旧日志重命名为备份文件。\n2.  **【问题】脆弱的错误消息分类**\n    *   **描述：** `createUserFriendlyError` 方法通过检查错误消息中的字符串（如 'AI服务'）来分类错误，这种方式不稳定。\n    *   **修复建议：** 定义一系列自定义错误类（如 `AIServiceError`, `GitError`），在业务逻辑中抛出这些特定类型的错误。然后在 `createUserFriendlyError` 中使用 `instanceof` 进行类型判断，使其更健壮。\n3.  **【问题】日志写入的“即发即忘”**\n    *   **描述：** `log` 方法是异步的，但调用它的 `info`, `warn` 等方法是同步的，这意味着日志写入失败或应用在写入完成前退出时，日志消息可能会丢失。\n    *   **修复建议：** 对于关键错误日志，可以考虑提供一种机制来确保日志被成功写入。或者在应用退出前，增加一个 `logger.flush()` 方法来处理所有待处理的日志写入操作。\n\n#### `src/banner.ts`\n\n**评价：** 代码简单直接，但存在一些小的重复和不规范之处。\n\n**问题与修复建议：**\n\n1.  **【问题】不恰当的 `console.error` 使用**\n    *   **描述：** 使用 `console.error` 来打印非错误信息的 Banner，这会污染 `stderr` 输出流，可能会被其他工具误判为错误。\n    *   **修复建议：** 将所有用于显示 Banner 的 `console.error` 改为 `console.log`。\n2.  **【问题】重复的版本信息显示代码**\n    *   **描述：** `showLongBanner` 和 `showShortBanner` 中显示版本号的代码完全相同。\n    *   **修复建议：** 提取一个独立的 `showVersionInfo` 辅助函数来消除重复。\n\n---\n\n### 综合修复 TODO 列表\n\n根据以上分析，这是一个建议的、按优先级排序的修复任务列表：\n\n*   [ ] **重构 `coderocket.ts` 中的错误处理：** 避免在 `SmartAIManager` 中暴露原始错误信息，改用通用的错误提示。\n*   [ ] **重构 `logger.ts` 中的错误分类：** 引入自定义错误类（如 `AIServiceError`, `GitError`），替换基于字符串匹配的脆弱实现。\n*   [ ] **重构 `coderocket.ts` 中的结果解析：** 修改 AI 提示词以返回 JSON 格式，并更新 `parseReviewResult` 方法以增强稳定性。\n*   [ ] **移除各文件中的硬编码：**\n    *   将 `coderocket.ts` 中的 AI 模型名称、超时等值移入配置。\n    *   在 `index.ts` 中从 `package.json` 动态读取版本号。\n*   [ ] **重构 `coderocket.ts` 中的初始化逻辑：** 考虑使用依赖注入或更明确的初始化流程，移除 `(ConfigManager as any).initialized` 检查。\n*   [ ] **抽象化 `index.ts` 中的工具处理逻辑：** 创建一个辅助函数或注册表，减少 `switch` 语句中的重复代码。\n*   [ ] **统一 `banner.ts` 中的输出流：** 将所有 `console.error` 调用改为 `console.log`，并提取重复的版本信息显示代码。\n*   [ ] **为 `logger.ts` 增加日志轮转功能：** 添加一个简单的文件大小检查和备份机制。\n\n您可以选择希望我优先处理的任务，或者我可以按照此列表的顺序开始修复。"}},{"type":"finish","data":{"reason":"end_turn","time":1754061208}}]


id: 454d2769-f3d7-4b6d-8ae8-87286434ca38
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"好的，我将按照之前生成的 TODO 列表开始修复。\n\n我将从 `banner.ts` 和 `index.ts` 中比较直接的问题开始，包括消除重复代码和移除硬编码的版本号。\n\n首先，我将重构 `src/banner.ts`，提取一个共享的 `showVersionInfo` 函数来消除重复，并调整启动 Banner 的显示逻辑，使其更简洁。"}},{"type":"finish","data":{"reason":"end_turn","time":1754061328}}]


id: 9c967720-5cd1-44ab-aac4-8919c428cf52
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"好的，继续修复。\n\n我将从 `src/banner.ts` 开始，解决之前发现的**代码重复**和**不恰当的 `console.error` 使用**问题。\n\n我将执行以下操作：\n1.  创建一个新的 `showVersionInfo` 函数，用于显示版本和服务信息，以消除 `showLongBanner` 和 `showShortBanner` 中的重复代码。\n2.  将所有用于显示普通 Banner 信息的 `console.error` 调用替换为 `console.log`，以确保只有真正的错误信息才输出到 `stderr`。\n3.  在 `showStartupInfo` 中调用更简洁的 `showMiniBanner`，以优化启动信息的显示。\n\n现在，我将应用这些修改。"}},{"type":"finish","data":{"reason":"end_turn","time":1754061399}}]


id: b8861ddf-6dd2-4406-b1a6-c148f7af5687
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: user
parts:
  [{"type":"text","data":{"text":"修复所有问题并在修复完每个问题后报告进度,没有修复完毕前不要暂停"}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: fad9f617-17e1-4efd-993e-ad1e8cf7c730
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"好的，我将开始修复所有已发现的问题，并在完成每个文件的修复后向您报告进度。\n\n### 进度1/4：修复 `src/banner.ts`\n\n我将首先处理 `src/banner.ts` 文件中的问题。\n\n**修复操作：**\n1.  **提取 `showVersionInfo` 函数：** 创建一个独立的 `showVersionInfo` 函数，以消除 `showLongBanner` 和 `showShortBanner` 之间的重复代码。\n2.  **纠正输出流：** 将所有用于显示 Banner 的 `console.error` 调用更改为 `console.log`，以避免污染标准错误流。\n3.  **优化启动显示：** 更新 `showStartupInfo` 函数，使其调用更简洁的 `showMiniBanner` 而不是完整的 `showBanner`。\n4.  **移除冗余代码：** 删除 `getVersion` 函数中重复的 `__dirname` 声明。\n5.  **改进版本回退：** 将 `getVersion` 中的硬编码回退版本 `'1.2.4'` 更改为 `'unknown'`。\n\n我将通过一次操作完成以上所有修复。"}},{"type":"finish","data":{"reason":"end_turn","time":1754061450}}]


id: efd5e5a4-d2d7-4b19-a73c-0f2beacd7cc5
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: user
parts:
  [{"type":"text","data":{"text":"跳过src/banner.ts,因为这个文件被我删掉了"}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 34719bc1-9ecf-4219-827c-f5fa5e8013a6
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"好的，我已跳过 `src/banner.ts`。\n\n现在我将开始修复 `src/index.ts`。\n\n### 进度 2/4：修复 `src/index.ts` (第 1 部分)\n\n我的第一个操作是解决**硬编码的版本号**问题。我将修改文件，使其从 `package.json` 动态读取版本号，确保服务器版本始终与项目版本同步。\n\n**修复操作：**\n1.  **添加必要的模块导入：** 导入 `fs/promises`、`path` 和 `url` 模块，用于读取文件。\n2.  **实现 `getVersion` 函数：** 创建一个异步函数，用于安全地读取和解析 `package.json` 文件以获取版本号。\n3.  **动态设置版本：** 在服务器实例化时，使用动态获取的版本号替换硬编码的 `'1.3.2'`。\n\n正在应用此修复。"}},{"type":"tool_call","data":{"id":"tool_0_edit","name":"edit","input":"{\"new_string\":\"#!/usr/bin/env node\\n\\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\\nimport {\\n  CallToolRequestSchema,\\n  ListToolsRequestSchema,\\n} from '@modelcontextprotocol/sdk/types.js';\\nimport { zodToJsonSchema } from 'zod-to-json-schema';\\nimport { readFile } from 'fs/promises';\\nimport { resolve, dirname } from 'path';\\nimport { fileURLToPath } from 'url';\\nimport { CodeRocketService } from './coderocket.js';\\nimport {\\n  ReviewCodeRequestSchema,\\n  ReviewChangesRequestSchema,\\n  ReviewCommitRequestSchema,\\n  ReviewFilesRequestSchema,\\n} from './types.js';\\nimport { toolDefinitions } from './toolDefinitions.js';\\n\\n// 读取版本号\\nconst getVersion = async (): Promise\u003cstring\u003e =\u003e {\\n  try {\\n    const __dirname = dirname(fileURLToPath(import.meta.url));\\n    const packageJsonPath = resolve(__dirname, '../package.json');\\n    const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));\\n    return packageJson.version || 'unknown';\\n  } catch (error) {\\n    console.error('Failed to read version from package.json:', error);\\n    return 'unknown';\\n  }\\n};\\n\\nconst VERSION = await getVersion();\\n\\n/**\\n * CodeRocket MCP Server\\n *\\n * 提供AI驱动的代码审查功能，集成多种AI服务（Gemini、OpenCode、ClaudeCode）\\n * 支持代码片段审查、Git提交审查、文件审查和AI服务管理\\n */\\nclass CodeRocketMCPServer {\\n  private server: Server;\\n  private codeRocketService: CodeRocketService | null = null;\\n\\n  constructor() {\\n    this.server = new Server(\\n      {\\n        name: 'coderocket-mcp',\\n        version: VERSION,\\n      },\\n      {\\n        capabilities: {\\n          tools: {},\\n        },\\n      },\\n    );\\n\\n    // 延迟初始化 CodeRocketService，等待 ConfigManager 初始化完成\\n    this.setupToolHandlers();\\n  }\",\"file_path\":\"src/index.ts\",\"old_string\":\"#!/usr/bin/env node\\n\\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\\nimport {\\n  CallToolRequestSchema,\\n  ListToolsRequestSchema,\\n} from '@modelcontextprotocol/sdk/types.js';\\nimport { zodToJsonSchema } from 'zod-to-json-schema';\\nimport { CodeRocketService } from './coderocket.js';\\nimport {\\n  ReviewCodeRequestSchema,\\n  ReviewChangesRequestSchema,\\n  ReviewCommitRequestSchema,\\n  ReviewFilesRequestSchema,\\n} from './types.js';\\nimport { toolDefinitions } from './toolDefinitions.js';\\n\\n/**\\n * CodeRocket MCP Server\\n *\\n * 提供AI驱动的代码审查功能，集成多种AI服务（Gemini、OpenCode、ClaudeCode）\\n * 支持代码片段审查、Git提交审查、文件审查和AI服务管理\\n */\\nclass CodeRocketMCPServer {\\n  private server: Server;\\n  private codeRocketService: CodeRocketService | null = null;\\n\\n  constructor() {\\n    this.server = new Server(\\n      {\\n        name: 'coderocket-mcp',\\n        version: '1.3.2',\\n      },\\n      {\\n        capabilities: {\\n          tools: {},\\n        },\\n      },\\n    );\\n\\n    // 延迟初始化 CodeRocketService，等待 ConfigManager 初始化完成\\n    this.setupToolHandlers();\\n  }\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754061558}}]


id: 24faaf95-7afb-4977-9a42-21c63c5bfd64
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: tool
provider: openrouter
parts:
  [{"type":"tool_result","data":{"tool_call_id":"tool_0_edit","name":"","content":"old_string not found in file. Make sure it matches exactly, including whitespace and line breaks","metadata":"","is_error":true}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 52954f01-dd96-414b-9e2e-531715d8a28e
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"我注意到 `edit` 命令失败了，因为原始字符串不匹配。这很可能是由于自上次检查以来文件已被修改。\n\n为了解决这个问题，我将首先读取 `src/index.ts` 的当前内容，然后使用更新后的内容再次尝试修复。"}},{"type":"tool_call","data":{"id":"tool_0_view","name":"view","input":"{\"file_path\":\"src/index.ts\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754061561}}]


id: cdcc05a0-15cf-4002-8253-f7d5cd9f52f1
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"好的，我已经获取了 `src/index.ts` 的最新内容。\n\n我现在将重新尝试修复**硬编码的版本号**问题。\n\n**修复操作：**\n1.  **添加 `fs/promises`、`path` 和 `url` 的导入。**\n2.  **实现 `getVersion` 函数** 以从 `package.json` 动态读取版本。\n3.  **在服务器实例化时使用动态版本**，替换硬编码的 `'1.3.2'`。\n\n正在应用修复。"}},{"type":"tool_call","data":{"id":"tool_0_edit","name":"edit","input":"{\"old_string\":\"#!/usr/bin/env node\\n\\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\\nimport {\\n  CallToolRequestSchema,\\n  ListToolsRequestSchema,\\n} from '@modelcontextprotocol/sdk/types.js';\\nimport { zodToJsonSchema } from 'zod-to-json-schema';\\nimport { CodeRocketService } from './coderocket.js';\\nimport {\\n  ReviewCodeRequestSchema,\\n  ReviewChangesRequestSchema,\\n  ReviewCommitRequestSchema,\\n  ReviewFilesRequestSchema,\\n} from './types.js';\\nimport { toolDefinitions } from './toolDefinitions.js';\\n\\n\\n\\n\\n\\n/**\\n * CodeRocket MCP Server\\n *\\n * 提供AI驱动的代码审查功能，集成多种AI服务（Gemini、OpenCode、ClaudeCode）\\n * 支持代码片段审查、Git提交审查、文件审查和AI服务管理\\n */\\nclass CodeRocketMCPServer {\\n  private server: Server;\\n  private codeRocketService: CodeRocketService | null = null;\\n\\n  constructor() {\\n    this.server = new Server(\\n      {\\n        name: 'coderocket-mcp',\\n        version: '1.3.2',\\n      },\\n      {\\n        capabilities: {\\n          tools: {},\\n        },\\n      },\\n    );\\n\\n    // 延迟初始化 CodeRocketService，等待 ConfigManager 初始化完成\\n    this.setupToolHandlers();\\n  }\",\"file_path\":\"src/index.ts\",\"new_string\":\"#!/usr/bin/env node\\n\\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\\nimport {\\n  CallToolRequestSchema,\\n  ListToolsRequestSchema,\\n} from '@modelcontextprotocol/sdk/types.js';\\nimport { zodToJsonSchema } from 'zod-to-json-schema';\\nimport { readFile } from 'fs/promises';\\nimport { resolve, dirname } from 'path';\\nimport { fileURLToPath } from 'url';\\nimport { CodeRocketService } from './coderocket.js';\\nimport {\\n  ReviewCodeRequestSchema,\\n  ReviewChangesRequestSchema,\\n  ReviewCommitRequestSchema,\\n  ReviewFilesRequestSchema,\\n} from './types.js';\\nimport { toolDefinitions } from './toolDefinitions.js';\\n\\n// 读取版本号\\nconst getVersion = async (): Promise\u003cstring\u003e =\u003e {\\n  try {\\n    const __dirname = dirname(fileURLToPath(import.meta.url));\\n    const packageJsonPath = resolve(__dirname, '../package.json');\\n    const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));\\n    return packageJson.version || 'unknown';\\n  } catch (error) {\\n    console.error('Failed to read version from package.json:', error);\\n    return 'unknown';\\n  }\\n};\\n\\nconst VERSION = await getVersion();\\n\\n/**\\n * CodeRocket MCP Server\\n *\\n * 提供AI驱动的代码审查功能，集成多种AI服务（Gemini、OpenCode、ClaudeCode）\\n * 支持代码片段审查、Git提交审查、文件审查和AI服务管理\\n */\\nclass CodeRocketMCPServer {\\n  private server: Server;\\n  private codeRocketService: CodeRocketService | null = null;\\n\\n  constructor() {\\n    this.server = new Server(\\n      {\\n        name: 'coderocket-mcp',\\n        version: VERSION,\\n      },\\n      {\\n        capabilities: {\\n          tools: {},\\n        },\\n      },\\n    );\\n\\n    // 延迟初始化 CodeRocketService，等待 ConfigManager 初始化完成\\n    this.setupToolHandlers();\\n  }\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754061572}}]


id: ff2df4a8-2b21-4e9e-883a-408a5fd019d5
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"`edit` 命令再次失败。很可能是 `old_string` 和文件实际内容之间仍存在细微的空白差异。\n\n为了确保成功，我将采取一个更直接的策略：\n1.  读取 `src/index.ts` 的全部内容。\n2.  使用 `write` 命令，用包含动态版本读取逻辑的新内容完全覆盖原文件。\n\n这将绕过 `edit` 工具的字符串匹配问题，直接应用修复。"}},{"type":"tool_call","data":{"id":"tool_0_view","name":"view","input":"{\"file_path\":\"src/index.ts\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754061577}}]


id: ba152fba-dd87-4fd3-ae52-895b5b211015
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"tool_call","data":{"id":"tool_0_write","name":"write","input":"{\"file_path\":\"src/index.ts\",\"content\":\"#!/usr/bin/env node\\n\\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\\nimport {\\n  CallToolRequestSchema,\\n  ListToolsRequestSchema,\\n} from '@modelcontextprotocol/sdk/types.js';\\nimport { zodToJsonSchema } from 'zod-to-json-schema';\\nimport { readFile } from 'fs/promises';\\nimport { resolve, dirname } from 'path';\\nimport { fileURLToPath } from 'url';\\nimport { CodeRocketService } from './coderocket.js';\\nimport {\\n  ReviewCodeRequestSchema,\\n  ReviewChangesRequestSchema,\\n  ReviewCommitRequestSchema,\\n  ReviewFilesRequestSchema,\\n} from './types.js';\\nimport { toolDefinitions } from './toolDefinitions.js';\\n\\n// 读取版本号\\nconst getVersion = async (): Promise\u003cstring\u003e =\u003e {\\n  try {\\n    const __dirname = dirname(fileURLToPath(import.meta.url));\\n    const packageJsonPath = resolve(__dirname, '../package.json');\\n    const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));\\n    return packageJson.version || 'unknown';\\n  } catch (error) {\\n    console.error('Failed to read version from package.json:', error);\\n    return 'unknown';\\n  }\\n};\\n\\nconst VERSION = await getVersion();\\n\\n/**\\n * CodeRocket MCP Server\\n *\\n * 提供AI驱动的代码审查功能，集成多种AI服务（Gemini、OpenCode、ClaudeCode）\\n * 支持代码片段审查、Git提交审查、文件审查和AI服务管理\\n */\\nclass CodeRocketMCPServer {\\n  private server: Server;\\n  private codeRocketService: CodeRocketService | null = null;\\n\\n  constructor() {\\n    this.server = new Server(\\n      {\\n        name: 'coderocket-mcp',\\n        version: VERSION,\\n      },\\n      {\\n        capabilities: {\\n          tools: {},\\n        },\\n      },\\n    );\\n\\n    // 延迟初始化 CodeRocketService，等待 ConfigManager 初始化完成\\n    this.setupToolHandlers();\\n  }\\n\\n  private setupToolHandlers() {\\n    // 注册工具列表处理器\\n    this.server.setRequestHandler(ListToolsRequestSchema, async () =\u003e {\\n      return {\\n        tools: toolDefinitions.map(({ name, description, schema }) =\u003e ({\\n          name,\\n          description,\\n          inputSchema: zodToJsonSchema(schema, {\\n            name: name.replace(/_([a-z])/g, (_, letter) =\u003e\\n              letter.toUpperCase(),\\n            ),\\n            $refStrategy: 'none',\\n          }),\\n        })),\\n      };\\n    });\\n\\n    // 注册工具调用处理器\\n    this.server.setRequestHandler(CallToolRequestSchema, async request =\u003e {\\n      const { name, arguments: args } = request.params;\\n\\n      // 确保 CodeRocketService 已初始化\\n      if (!this.codeRocketService) {\\n        throw new Error('CodeRocketService 未初始化，请稍后重试');\\n      }\\n\\n      try {\\n        switch (name) {\\n          case 'review_code': {\\n            const parsedArgs = ReviewCodeRequestSchema.parse(args);\\n            const result = await this.codeRocketService.reviewCode(parsedArgs);\\n            return {\\n              content: [\\n                {\\n                  type: 'text',\\n                  text: JSON.stringify(result, null, 2),\\n                },\\n              ],\\n            };\\n          }\\n\\n          case 'review_changes': {\\n            const parsedArgs = ReviewChangesRequestSchema.parse(args);\\n            const result =\\n              await this.codeRocketService.reviewChanges(parsedArgs);\\n            return {\\n              content: [\\n                {\\n                  type: 'text',\\n                  text: JSON.stringify(result, null, 2),\\n                },\\n              ],\\n            };\\n          }\\n\\n          case 'review_commit': {\\n            const parsedArgs = ReviewCommitRequestSchema.parse(args);\\n            const result =\\n              await this.codeRocketService.reviewCommit(parsedArgs);\\n            return {\\n              content: [\\n                {\\n                  type: 'text',\\n                  text: JSON.stringify(result, null, 2),\\n                },\\n              ],\\n            };\\n          }\\n\\n          case 'review_files': {\\n            const parsedArgs = ReviewFilesRequestSchema.parse(args);\\n            const result = await this.codeRocketService.reviewFiles(parsedArgs);\\n            return {\\n              content: [\\n                {\\n                  type: 'text',\\n                  text: JSON.stringify(result, null, 2),\\n                },\\n              ],\\n            };\\n          }\\n\\n          default:\\n            throw new Error(`Unknown tool: ${name}`);\\n        }\\n      } catch (error) {\\n        const errorMessage =\\n          error instanceof Error ? error.message : String(error);\\n        return {\\n          content: [\\n            {\\n              type: 'text',\\n              text: JSON.stringify(\\n                {\\n                  error: errorMessage,\\n                  error_code: 'TOOL_EXECUTION_ERROR',\\n                  suggestions: [\\n                    '检查输入参数是否正确',\\n                    '验证AI服务配置是否正确',\\n                    '确保API密钥已正确设置',\\n                    '检查网络连接是否正常',\\n                  ],\\n                },\\n                null,\\n                2,\\n              ),\\n            },\\n          ],\\n          isError: true,\\n        };\\n      }\\n    });\\n  }\\n\\n  async run() {\\n    try {\\n      // 预先初始化配置系统\\n      const { ConfigManager, CodeRocketService } = await import(\\n        './coderocket.js'\\n      );\\n      await ConfigManager.initialize();\\n\\n      // 现在可以安全地初始化 CodeRocketService\\n      this.codeRocketService = new CodeRocketService();\\n\\n      const transport = new StdioServerTransport();\\n      await this.server.connect(transport);\\n    } catch (error) {\\n      const errorMessage =\\n        error instanceof Error ? error.message : String(error);\\n      console.error('❌ CodeRocket MCP 服务器启动失败:', errorMessage);\\n      process.exit(1);\\n    }\\n  }\\n}\\n\\n// 启动服务器\\nconst server = new CodeRocketMCPServer();\\nserver.run().catch(error =\u003e {\\n  console.error('Failed to start server:', error);\\n  process.exit(1);\\n});\\n\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754061592}}]


id: 09d4e66b-76b8-48da-95ac-abf24dcf6879
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: tool
provider: openrouter
parts:
  [{"type":"tool_result","data":{"tool_call_id":"tool_0_write","name":"","content":"\u003cresult\u003e\nFile successfully written: /Users/dreambt/sources/coderocket/coderocket-mcp/src/index.ts\n\u003c/result\u003e","metadata":"{\"diff\":\"--- a/src/index.ts\\n+++ b/src/index.ts\\n@@ -7,17 +7,32 @@\\n   ListToolsRequestSchema,\\n } from '@modelcontextprotocol/sdk/types.js';\\n import { zodToJsonSchema } from 'zod-to-json-schema';\\n-import { CodeRocketService } from './coderocket.js';\\n+import { readFile } from 'fs/promises';\\n-import {\\n-  ReviewCodeRequestSchema,\\n-  ReviewChangesRequestSchema,\\n-  ReviewCommitRequestSchema,\\n-  ReviewFilesRequestSchema,\\n-} from './types.js';\\n-import { toolDefinitions } from './toolDefinitions.js';\\n-\\n-\\n-\\n+import { resolve, dirname } from 'path';\\n+import { fileURLToPath } from 'url';\\n+import { CodeRocketService } from './coderocket.js';\\n+import {\\n+  ReviewCodeRequestSchema,\\n+  ReviewChangesRequestSchema,\\n+  ReviewCommitRequestSchema,\\n+  ReviewFilesRequestSchema,\\n+} from './types.js';\\n+import { toolDefinitions } from './toolDefinitions.js';\\n+\\n+// 读取版本号\\n+const getVersion = async (): Promise\\u003cstring\\u003e =\\u003e {\\n+  try {\\n+    const __dirname = dirname(fileURLToPath(import.meta.url));\\n+    const packageJsonPath = resolve(__dirname, '../package.json');\\n+    const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));\\n+    return packageJson.version || 'unknown';\\n+  } catch (error) {\\n+    console.error('Failed to read version from package.json:', error);\\n+    return 'unknown';\\n+  }\\n+};\\n+\\n+const VERSION = await getVersion();\\n \\n /**\\n  * CodeRocket MCP Server\\n@@ -33,7 +48,7 @@\\n     this.server = new Server(\\n       {\\n         name: 'coderocket-mcp',\\n-        version: '1.3.2',\\n+        version: VERSION,\\n       },\\n       {\\n         capabilities: {\\n\",\"additions\":27,\"removals\":12}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 7bd4a011-085e-4f7c-956d-0883924fed69
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"`src/index.ts` 的版本号问题已修复。现在我将继续处理该文件中的下一个问题：**重复的工具调用处理逻辑**。\n\n### 进度 2/4：修复 `src/index.ts` (第 2 部分)\n\n我将重构 `setupToolHandlers` 方法，通过创建一个辅助函数来处理所有 `review_*` 相关的工具调用，以消除 `switch` 语句中的重复代码。\n\n**修复操作：**\n1.  **创建 `createToolHandler` 辅助函数：** 此函数将封装参数解析、服务调用和结果格式化的通用逻辑。\n2.  **简化 `switch` 语句：** 使用 `createToolHandler` 来简化每个工具的处理分支，使代码更简洁、更易于维护。\n\n正在应用此修复。"}},{"type":"tool_call","data":{"id":"tool_0_edit","name":"edit","input":"{\"old_string\":\"      try {\\n        switch (name) {\\n          case 'review_code': {\\n            const parsedArgs = ReviewCodeRequestSchema.parse(args);\\n            const result = await this.codeRocketService.reviewCode(parsedArgs);\\n            return {\\n              content: [\\n                {\\n                  type: 'text',\\n                  text: JSON.stringify(result, null, 2),\\n                },\\n              ],\\n            };\\n          }\\n\\n          case 'review_changes': {\\n            const parsedArgs = ReviewChangesRequestSchema.parse(args);\\n            const result =\\n              await this.codeRocketService.reviewChanges(parsedArgs);\\n            return {\\n              content: [\\n                {\\n                  type: 'text',\\n                  text: JSON.stringify(result, null, 2),\\n                },\\n              ],\\n            };\\n          }\\n\\n          case 'review_commit': {\\n            const parsedArgs = ReviewCommitRequestSchema.parse(args);\\n            const result =\\n              await this.codeRocketService.reviewCommit(parsedArgs);\\n            return {\\n              content: [\\n                {\\n                  type: 'text',\\n                  text: JSON.stringify(result, null, 2),\\n                },\\n              ],\\n            };\\n          }\\n\\n          case 'review_files': {\\n            const parsedArgs = ReviewFilesRequestSchema.parse(args);\\n            const result = await this.codeRocketService.reviewFiles(parsedArgs);\\n            return {\\n              content: [\\n                {\\n                  type: 'text',\\n                  text: JSON.stringify(result, null, 2),\\n                },\\n              ],\\n            };\\n          }\\n\\n          default:\\n            throw new Error(`Unknown tool: ${name}`);\\n        }\\n      } catch (error) {\",\"file_path\":\"src/index.ts\",\"new_string\":\"      // 辅助函数，用于创建和执行工具调用\\n      const createToolHandler = async (schema: any, serviceMethod: (args: any) =\u003e Promise\u003cany\u003e) =\u003e {\\n        const parsedArgs = schema.parse(args);\\n        const result = await serviceMethod.call(this.codeRocketService, parsedArgs);\\n        return {\\n          content: [\\n            {\\n              type: 'text',\\n              text: JSON.stringify(result, null, 2),\\n            },\\n          ],\\n        };\\n      };\\n\\n      try {\\n        switch (name) {\\n          case 'review_code':\\n            return await createToolHandler(ReviewCodeRequestSchema, this.codeRocketService.reviewCode);\\n\\n          case 'review_changes':\\n            return await createToolHandler(ReviewChangesRequestSchema, this.codeRocketService.reviewChanges);\\n\\n          case 'review_commit':\\n            return await createToolHandler(ReviewCommitRequestSchema, this.codeRocketService.reviewCommit);\\n\\n          case 'review_files':\\n            return await createToolHandler(ReviewFilesRequestSchema, this.codeRocketService.reviewFiles);\\n\\n          default:\\n            throw new Error(`Unknown tool: ${name}`);\\n        }\\n      } catch (error) {\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754061614}}]


id: 2cb4fee3-74e4-41f4-b79c-f6a8890992f4
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: tool
provider: openrouter
parts:
  [{"type":"tool_result","data":{"tool_call_id":"tool_0_edit","name":"","content":"\u003cresult\u003e\nContent replaced in file: /Users/dreambt/sources/coderocket/coderocket-mcp/src/index.ts\n\u003c/result\u003e\n","metadata":"{\"additions\":27,\"removals\":55,\"old_content\":\"#!/usr/bin/env node\\n\\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\\nimport {\\n  CallToolRequestSchema,\\n  ListToolsRequestSchema,\\n} from '@modelcontextprotocol/sdk/types.js';\\nimport { zodToJsonSchema } from 'zod-to-json-schema';\\nimport { readFile } from 'fs/promises';\\nimport { resolve, dirname } from 'path';\\nimport { fileURLToPath } from 'url';\\nimport { CodeRocketService } from './coderocket.js';\\nimport {\\n  ReviewCodeRequestSchema,\\n  ReviewChangesRequestSchema,\\n  ReviewCommitRequestSchema,\\n  ReviewFilesRequestSchema,\\n} from './types.js';\\nimport { toolDefinitions } from './toolDefinitions.js';\\n\\n// 读取版本号\\nconst getVersion = async (): Promise\\u003cstring\\u003e =\\u003e {\\n  try {\\n    const __dirname = dirname(fileURLToPath(import.meta.url));\\n    const packageJsonPath = resolve(__dirname, '../package.json');\\n    const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));\\n    return packageJson.version || 'unknown';\\n  } catch (error) {\\n    console.error('Failed to read version from package.json:', error);\\n    return 'unknown';\\n  }\\n};\\n\\nconst VERSION = await getVersion();\\n\\n/**\\n * CodeRocket MCP Server\\n *\\n * 提供AI驱动的代码审查功能，集成多种AI服务（Gemini、OpenCode、ClaudeCode）\\n * 支持代码片段审查、Git提交审查、文件审查和AI服务管理\\n */\\nclass CodeRocketMCPServer {\\n  private server: Server;\\n  private codeRocketService: CodeRocketService | null = null;\\n\\n  constructor() {\\n    this.server = new Server(\\n      {\\n        name: 'coderocket-mcp',\\n        version: VERSION,\\n      },\\n      {\\n        capabilities: {\\n          tools: {},\\n        },\\n      },\\n    );\\n\\n    // 延迟初始化 CodeRocketService，等待 ConfigManager 初始化完成\\n    this.setupToolHandlers();\\n  }\\n\\n  private setupToolHandlers() {\\n    // 注册工具列表处理器\\n    this.server.setRequestHandler(ListToolsRequestSchema, async () =\\u003e {\\n      return {\\n        tools: toolDefinitions.map(({ name, description, schema }) =\\u003e ({\\n          name,\\n          description,\\n          inputSchema: zodToJsonSchema(schema, {\\n            name: name.replace(/_([a-z])/g, (_, letter) =\\u003e\\n              letter.toUpperCase(),\\n            ),\\n            $refStrategy: 'none',\\n          }),\\n        })),\\n      };\\n    });\\n\\n    // 注册工具调用处理器\\n    this.server.setRequestHandler(CallToolRequestSchema, async request =\\u003e {\\n      const { name, arguments: args } = request.params;\\n\\n      // 确保 CodeRocketService 已初始化\\n      if (!this.codeRocketService) {\\n        throw new Error('CodeRocketService 未初始化，请稍后重试');\\n      }\\n\\n      try {\\n        switch (name) {\\n          case 'review_code': {\\n            const parsedArgs = ReviewCodeRequestSchema.parse(args);\\n            const result = await this.codeRocketService.reviewCode(parsedArgs);\\n            return {\\n              content: [\\n                {\\n                  type: 'text',\\n                  text: JSON.stringify(result, null, 2),\\n                },\\n              ],\\n            };\\n          }\\n\\n          case 'review_changes': {\\n            const parsedArgs = ReviewChangesRequestSchema.parse(args);\\n            const result =\\n              await this.codeRocketService.reviewChanges(parsedArgs);\\n            return {\\n              content: [\\n                {\\n                  type: 'text',\\n                  text: JSON.stringify(result, null, 2),\\n                },\\n              ],\\n            };\\n          }\\n\\n          case 'review_commit': {\\n            const parsedArgs = ReviewCommitRequestSchema.parse(args);\\n            const result =\\n              await this.codeRocketService.reviewCommit(parsedArgs);\\n            return {\\n              content: [\\n                {\\n                  type: 'text',\\n                  text: JSON.stringify(result, null, 2),\\n                },\\n              ],\\n            };\\n          }\\n\\n          case 'review_files': {\\n            const parsedArgs = ReviewFilesRequestSchema.parse(args);\\n            const result = await this.codeRocketService.reviewFiles(parsedArgs);\\n            return {\\n              content: [\\n                {\\n                  type: 'text',\\n                  text: JSON.stringify(result, null, 2),\\n                },\\n              ],\\n            };\\n          }\\n\\n          default:\\n            throw new Error(`Unknown tool: ${name}`);\\n        }\\n      } catch (error) {\\n        const errorMessage =\\n          error instanceof Error ? error.message : String(error);\\n        return {\\n          content: [\\n            {\\n              type: 'text',\\n              text: JSON.stringify(\\n                {\\n                  error: errorMessage,\\n                  error_code: 'TOOL_EXECUTION_ERROR',\\n                  suggestions: [\\n                    '检查输入参数是否正确',\\n                    '验证AI服务配置是否正确',\\n                    '确保API密钥已正确设置',\\n                    '检查网络连接是否正常',\\n                  ],\\n                },\\n                null,\\n                2,\\n              ),\\n            },\\n          ],\\n          isError: true,\\n        };\\n      }\\n    });\\n  }\\n\\n  async run() {\\n    try {\\n      // 预先初始化配置系统\\n      const { ConfigManager, CodeRocketService } = await import(\\n        './coderocket.js'\\n      );\\n      await ConfigManager.initialize();\\n\\n      // 现在可以安全地初始化 CodeRocketService\\n      this.codeRocketService = new CodeRocketService();\\n\\n      const transport = new StdioServerTransport();\\n      await this.server.connect(transport);\\n    } catch (error) {\\n      const errorMessage =\\n        error instanceof Error ? error.message : String(error);\\n      console.error('❌ CodeRocket MCP 服务器启动失败:', errorMessage);\\n      process.exit(1);\\n    }\\n  }\\n}\\n\\n// 启动服务器\\nconst server = new CodeRocketMCPServer();\\nserver.run().catch(error =\\u003e {\\n  console.error('Failed to start server:', error);\\n  process.exit(1);\\n});\\n\",\"new_content\":\"#!/usr/bin/env node\\n\\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\\nimport {\\n  CallToolRequestSchema,\\n  ListToolsRequestSchema,\\n} from '@modelcontextprotocol/sdk/types.js';\\nimport { zodToJsonSchema } from 'zod-to-json-schema';\\nimport { readFile } from 'fs/promises';\\nimport { resolve, dirname } from 'path';\\nimport { fileURLToPath } from 'url';\\nimport { CodeRocketService } from './coderocket.js';\\nimport {\\n  ReviewCodeRequestSchema,\\n  ReviewChangesRequestSchema,\\n  ReviewCommitRequestSchema,\\n  ReviewFilesRequestSchema,\\n} from './types.js';\\nimport { toolDefinitions } from './toolDefinitions.js';\\n\\n// 读取版本号\\nconst getVersion = async (): Promise\\u003cstring\\u003e =\\u003e {\\n  try {\\n    const __dirname = dirname(fileURLToPath(import.meta.url));\\n    const packageJsonPath = resolve(__dirname, '../package.json');\\n    const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));\\n    return packageJson.version || 'unknown';\\n  } catch (error) {\\n    console.error('Failed to read version from package.json:', error);\\n    return 'unknown';\\n  }\\n};\\n\\nconst VERSION = await getVersion();\\n\\n/**\\n * CodeRocket MCP Server\\n *\\n * 提供AI驱动的代码审查功能，集成多种AI服务（Gemini、OpenCode、ClaudeCode）\\n * 支持代码片段审查、Git提交审查、文件审查和AI服务管理\\n */\\nclass CodeRocketMCPServer {\\n  private server: Server;\\n  private codeRocketService: CodeRocketService | null = null;\\n\\n  constructor() {\\n    this.server = new Server(\\n      {\\n        name: 'coderocket-mcp',\\n        version: VERSION,\\n      },\\n      {\\n        capabilities: {\\n          tools: {},\\n        },\\n      },\\n    );\\n\\n    // 延迟初始化 CodeRocketService，等待 ConfigManager 初始化完成\\n    this.setupToolHandlers();\\n  }\\n\\n  private setupToolHandlers() {\\n    // 注册工具列表处理器\\n    this.server.setRequestHandler(ListToolsRequestSchema, async () =\\u003e {\\n      return {\\n        tools: toolDefinitions.map(({ name, description, schema }) =\\u003e ({\\n          name,\\n          description,\\n          inputSchema: zodToJsonSchema(schema, {\\n            name: name.replace(/_([a-z])/g, (_, letter) =\\u003e\\n              letter.toUpperCase(),\\n            ),\\n            $refStrategy: 'none',\\n          }),\\n        })),\\n      };\\n    });\\n\\n    // 注册工具调用处理器\\n    this.server.setRequestHandler(CallToolRequestSchema, async request =\\u003e {\\n      const { name, arguments: args } = request.params;\\n\\n      // 确保 CodeRocketService 已初始化\\n      if (!this.codeRocketService) {\\n        throw new Error('CodeRocketService 未初始化，请稍后重试');\\n      }\\n\\n      // 辅助函数，用于创建和执行工具调用\\n      const createToolHandler = async (schema: any, serviceMethod: (args: any) =\\u003e Promise\\u003cany\\u003e) =\\u003e {\\n        const parsedArgs = schema.parse(args);\\n        const result = await serviceMethod.call(this.codeRocketService, parsedArgs);\\n        return {\\n          content: [\\n            {\\n              type: 'text',\\n              text: JSON.stringify(result, null, 2),\\n            },\\n          ],\\n        };\\n      };\\n\\n      try {\\n        switch (name) {\\n          case 'review_code':\\n            return await createToolHandler(ReviewCodeRequestSchema, this.codeRocketService.reviewCode);\\n\\n          case 'review_changes':\\n            return await createToolHandler(ReviewChangesRequestSchema, this.codeRocketService.reviewChanges);\\n\\n          case 'review_commit':\\n            return await createToolHandler(ReviewCommitRequestSchema, this.codeRocketService.reviewCommit);\\n\\n          case 'review_files':\\n            return await createToolHandler(ReviewFilesRequestSchema, this.codeRocketService.reviewFiles);\\n\\n          default:\\n            throw new Error(`Unknown tool: ${name}`);\\n        }\\n      } catch (error) {\\n        const errorMessage =\\n          error instanceof Error ? error.message : String(error);\\n        return {\\n          content: [\\n            {\\n              type: 'text',\\n              text: JSON.stringify(\\n                {\\n                  error: errorMessage,\\n                  error_code: 'TOOL_EXECUTION_ERROR',\\n                  suggestions: [\\n                    '检查输入参数是否正确',\\n                    '验证AI服务配置是否正确',\\n                    '确保API密钥已正确设置',\\n                    '检查网络连接是否正常',\\n                  ],\\n                },\\n                null,\\n                2,\\n              ),\\n            },\\n          ],\\n          isError: true,\\n        };\\n      }\\n    });\\n  }\\n\\n  async run() {\\n    try {\\n      // 预先初始化配置系统\\n      const { ConfigManager, CodeRocketService } = await import(\\n        './coderocket.js'\\n      );\\n      await ConfigManager.initialize();\\n\\n      // 现在可以安全地初始化 CodeRocketService\\n      this.codeRocketService = new CodeRocketService();\\n\\n      const transport = new StdioServerTransport();\\n      await this.server.connect(transport);\\n    } catch (error) {\\n      const errorMessage =\\n        error instanceof Error ? error.message : String(error);\\n      console.error('❌ CodeRocket MCP 服务器启动失败:', errorMessage);\\n      process.exit(1);\\n    }\\n  }\\n}\\n\\n// 启动服务器\\nconst server = new CodeRocketMCPServer();\\nserver.run().catch(error =\\u003e {\\n  console.error('Failed to start server:', error);\\n  process.exit(1);\\n});\\n\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 30018a78-c8c7-456e-acc0-05927cacb06f
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"`src/index.ts` 中的重复代码问题已解决。现在，该文件中的所有主要问题都已修复。\n\n接下来，我将开始修复 `src/logger.ts` 文件。\n\n### 进度 3/4：修复 `src/logger.ts`\n\n我将首先解决**脆弱的错误消息分类**问题，用更健壮的自定义错误类替换基于字符串匹配的逻辑。\n\n**修复操作：**\n1.  **定义自定义错误类：** 在 `logger.ts` 的顶部，我将定义 `AIServiceError`、`GitError` 和 `FileError` 等继承自 `Error` 的自定义错误类。\n2.  **更新 `createUserFriendlyError` 方法：** 修改此方法，使用 `instanceof` 来检查错误类型，并据此分配错误代码和建议，使其更加可靠。\n3.  **在 `coderocket.ts` 中应用新的错误类型：** 我将在 `coderocket.ts` 的适当位置抛出这些新的自定义错误，以确保 `logger.ts` 中的新逻辑能够被触发（这将在下一步骤中完成）。\n\n现在，我将应用对 `src/logger.ts` 的第一部分修复。"}},{"type":"tool_call","data":{"id":"tool_0_edit","name":"edit","input":"{\"new_string\":\"import { writeFile, appendFile, mkdir } from 'fs/promises';\\nimport { join } from 'path';\\nimport { tmpdir } from 'os';\\n\\n// 自定义错误类型\\nexport class AIServiceError extends Error {\\n  constructor(message: string) {\\n    super(message);\\n    this.name = 'AIServiceError';\\n  }\\n}\\n\\nexport class GitError extends Error {\\n  constructor(message: string) {\\n    super(message);\\n    this.name = 'GitError';\\n  }\\n}\\n\\nexport class FileError extends Error {\\n  constructor(message: string) {\\n    super(message);\\n    this.name = 'FileError';\\n  }\\n}\\n\\n/**\\n * 日志级别\\n */\",\"old_string\":\"import { writeFile, appendFile, mkdir } from 'fs/promises';\\nimport { join } from 'path';\\nimport { tmpdir } from 'os';\\n\\n/**\\n * 日志级别\\n */\",\"file_path\":\"src/logger.ts\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754061622}}]


id: 694bf86a-1442-4f9f-a294-efc1692201bd
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: tool
provider: openrouter
parts:
  [{"type":"tool_result","data":{"tool_call_id":"tool_0_edit","name":"","content":"\u003cresult\u003e\nContent replaced in file: /Users/dreambt/sources/coderocket/coderocket-mcp/src/logger.ts\n\u003c/result\u003e\n","metadata":"{\"additions\":23,\"removals\":1,\"old_content\":\"import { writeFile, appendFile, mkdir } from 'fs/promises';\\nimport { join } from 'path';\\nimport { tmpdir } from 'os';\\n\\n/**\\n * 日志级别\\n */\\nexport enum LogLevel {\\n  DEBUG = 0,\\n  INFO = 1,\\n  WARN = 2,\\n  ERROR = 3,\\n}\\n\\n/**\\n * 日志条目接口\\n */\\ninterface LogEntry {\\n  timestamp: string;\\n  level: LogLevel;\\n  message: string;\\n  context?: Record\\u003cstring, any\\u003e;\\n  error?: Error;\\n}\\n\\n/**\\n * 简单的日志记录器\\n */\\nexport class Logger {\\n  private logLevel: LogLevel;\\n  private logFile?: string;\\n\\n  constructor(level: LogLevel = LogLevel.INFO, logFile?: string) {\\n    this.logLevel = level;\\n    this.logFile = logFile;\\n\\n    // 如果没有指定日志文件，使用临时目录\\n    if (!this.logFile) {\\n      this.logFile = join(tmpdir(), 'coderocket-mcp.log');\\n    }\\n  }\\n\\n  /**\\n   * 记录调试信息\\n   */\\n  debug(message: string, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.DEBUG, message, context);\\n  }\\n\\n  /**\\n   * 记录信息\\n   */\\n  info(message: string, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.INFO, message, context);\\n  }\\n\\n  /**\\n   * 记录警告\\n   */\\n  warn(message: string, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.WARN, message, context);\\n  }\\n\\n  /**\\n   * 记录错误\\n   */\\n  error(message: string, error?: Error, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.ERROR, message, context, error);\\n  }\\n\\n  /**\\n   * 核心日志记录方法\\n   */\\n  private async log(\\n    level: LogLevel,\\n    message: string,\\n    context?: Record\\u003cstring, any\\u003e,\\n    error?: Error,\\n  ) {\\n    if (level \\u003c this.logLevel) {\\n      return;\\n    }\\n\\n    const entry: LogEntry = {\\n      timestamp: new Date().toISOString(),\\n      level,\\n      message,\\n      context,\\n      error,\\n    };\\n\\n    // 在 MCP 服务器模式下，只有在 DEBUG 模式或 WARN/ERROR 级别时才输出到控制台\\n    // 这避免了 IDE 误认为 INFO 级别的日志是错误信息\\n    const shouldOutputToConsole =\\n      process.env.DEBUG === 'true' || level \\u003e= LogLevel.WARN;\\n\\n    if (shouldOutputToConsole) {\\n      const levelName = LogLevel[level];\\n      const contextStr = context ? ` ${JSON.stringify(context)}` : '';\\n      const errorStr = error ? ` Error: ${error.message}` : '';\\n\\n      // 所有控制台输出都使用 stderr 以避免污染 MCP 协议的 stdout\\n      console.error(\\n        `[${entry.timestamp}] ${levelName}: ${message}${contextStr}${errorStr}`,\\n      );\\n    }\\n\\n    // 写入日志文件\\n    if (this.logFile) {\\n      try {\\n        // 处理 Error 对象，避免循环结构\\n        let logEntry = entry;\\n        if (entry.error instanceof Error) {\\n          logEntry = {\\n            ...entry,\\n            error: {\\n              name: entry.error.name,\\n              message: entry.error.message,\\n              stack: entry.error.stack,\\n            },\\n          };\\n        }\\n        const logLine = JSON.stringify(logEntry) + '\\\\n';\\n        await appendFile(this.logFile, logLine, 'utf-8');\\n      } catch (fileError) {\\n        console.error('Failed to write to log file:', fileError);\\n      }\\n    }\\n  }\\n\\n  /**\\n   * 获取日志文件路径\\n   */\\n  getLogFile(): string | undefined {\\n    return this.logFile;\\n  }\\n\\n  /**\\n   * 设置日志级别\\n   */\\n  setLevel(level: LogLevel) {\\n    this.logLevel = level;\\n  }\\n}\\n\\n// 全局日志实例\\nexport const logger = new Logger(\\n  process.env.NODE_ENV === 'development' ? LogLevel.DEBUG : LogLevel.INFO,\\n);\\n\\n/**\\n * 错误处理工具类\\n */\\nexport class ErrorHandler {\\n  private logger: Logger;\\n\\n  constructor(logger: Logger) {\\n    this.logger = logger;\\n  }\\n\\n  /**\\n   * 处理并格式化错误\\n   */\\n  handleError(error: unknown, context?: string): Error {\\n    let formattedError: Error;\\n\\n    if (error instanceof Error) {\\n      formattedError = error;\\n    } else if (typeof error === 'string') {\\n      formattedError = new Error(error);\\n    } else {\\n      formattedError = new Error(`Unknown error: ${JSON.stringify(error)}`);\\n    }\\n\\n    // 记录错误\\n    this.logger.error(\\n      `${context ? `[${context}] ` : ''}${formattedError.message}`,\\n      formattedError,\\n      { context },\\n    );\\n\\n    return formattedError;\\n  }\\n\\n  /**\\n   * 包装异步函数，自动处理错误\\n   */\\n  wrapAsync\\u003cT extends any[], R\\u003e(\\n    fn: (...args: T) =\\u003e Promise\\u003cR\\u003e,\\n    context?: string,\\n  ): (...args: T) =\\u003e Promise\\u003cR\\u003e {\\n    return async (...args: T): Promise\\u003cR\\u003e =\\u003e {\\n      try {\\n        return await fn(...args);\\n      } catch (error) {\\n        throw this.handleError(error, context);\\n      }\\n    };\\n  }\\n\\n  /**\\n   * 创建用户友好的错误消息\\n   */\\n  createUserFriendlyError(\\n    error: Error,\\n    suggestions?: string[],\\n  ): {\\n    error: string;\\n    error_code: string;\\n    suggestions: string[];\\n  } {\\n    // 根据错误类型提供不同的错误代码和建议\\n    let errorCode = 'UNKNOWN_ERROR';\\n    let userSuggestions = suggestions || [];\\n\\n    if (error.message.includes('Shell command failed')) {\\n      errorCode = 'SHELL_COMMAND_ERROR';\\n      userSuggestions = [\\n        '验证AI服务是否已配置',\\n        '检查API密钥是否正确设置',\\n        '确保有足够的权限执行命令',\\n        ...userSuggestions,\\n      ];\\n    } else if (\\n      error.message.includes('AI服务') ||\\n      error.message.includes('AI service')\\n    ) {\\n      errorCode = 'AI_SERVICE_ERROR';\\n      userSuggestions = [\\n        '检查AI服务配置',\\n        '验证API密钥是否正确',\\n        '确保网络连接正常',\\n        '尝试切换到其他AI服务',\\n        ...userSuggestions,\\n      ];\\n    } else if (\\n      error.message.includes('文件') ||\\n      error.message.includes('file')\\n    ) {\\n      errorCode = 'FILE_ERROR';\\n      userSuggestions = [\\n        '检查文件路径是否正确',\\n        '确保文件存在且可读',\\n        '验证文件权限',\\n        ...userSuggestions,\\n      ];\\n    } else if (error.message.includes('Git') || error.message.includes('git')) {\\n      errorCode = 'GIT_ERROR';\\n      userSuggestions = [\\n        '确保在Git仓库中执行',\\n        '检查Git仓库状态',\\n        '验证提交哈希是否存在',\\n        ...userSuggestions,\\n      ];\\n    }\\n\\n    return {\\n      error: error.message,\\n      error_code: errorCode,\\n      suggestions: userSuggestions,\\n    };\\n  }\\n}\\n\\n// 全局错误处理器实例\\nexport const errorHandler = new ErrorHandler(logger);\\n\",\"new_content\":\"import { writeFile, appendFile, mkdir } from 'fs/promises';\\nimport { join } from 'path';\\nimport { tmpdir } from 'os';\\n\\n// 自定义错误类型\\nexport class AIServiceError extends Error {\\n  constructor(message: string) {\\n    super(message);\\n    this.name = 'AIServiceError';\\n  }\\n}\\n\\nexport class GitError extends Error {\\n  constructor(message: string) {\\n    super(message);\\n    this.name = 'GitError';\\n  }\\n}\\n\\nexport class FileError extends Error {\\n  constructor(message: string) {\\n    super(message);\\n    this.name = 'FileError';\\n  }\\n}\\n\\n/**\\n * 日志级别\\n */\\nexport enum LogLevel {\\n  DEBUG = 0,\\n  INFO = 1,\\n  WARN = 2,\\n  ERROR = 3,\\n}\\n\\n/**\\n * 日志条目接口\\n */\\ninterface LogEntry {\\n  timestamp: string;\\n  level: LogLevel;\\n  message: string;\\n  context?: Record\\u003cstring, any\\u003e;\\n  error?: Error;\\n}\\n\\n/**\\n * 简单的日志记录器\\n */\\nexport class Logger {\\n  private logLevel: LogLevel;\\n  private logFile?: string;\\n\\n  constructor(level: LogLevel = LogLevel.INFO, logFile?: string) {\\n    this.logLevel = level;\\n    this.logFile = logFile;\\n\\n    // 如果没有指定日志文件，使用临时目录\\n    if (!this.logFile) {\\n      this.logFile = join(tmpdir(), 'coderocket-mcp.log');\\n    }\\n  }\\n\\n  /**\\n   * 记录调试信息\\n   */\\n  debug(message: string, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.DEBUG, message, context);\\n  }\\n\\n  /**\\n   * 记录信息\\n   */\\n  info(message: string, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.INFO, message, context);\\n  }\\n\\n  /**\\n   * 记录警告\\n   */\\n  warn(message: string, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.WARN, message, context);\\n  }\\n\\n  /**\\n   * 记录错误\\n   */\\n  error(message: string, error?: Error, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.ERROR, message, context, error);\\n  }\\n\\n  /**\\n   * 核心日志记录方法\\n   */\\n  private async log(\\n    level: LogLevel,\\n    message: string,\\n    context?: Record\\u003cstring, any\\u003e,\\n    error?: Error,\\n  ) {\\n    if (level \\u003c this.logLevel) {\\n      return;\\n    }\\n\\n    const entry: LogEntry = {\\n      timestamp: new Date().toISOString(),\\n      level,\\n      message,\\n      context,\\n      error,\\n    };\\n\\n    // 在 MCP 服务器模式下，只有在 DEBUG 模式或 WARN/ERROR 级别时才输出到控制台\\n    // 这避免了 IDE 误认为 INFO 级别的日志是错误信息\\n    const shouldOutputToConsole =\\n      process.env.DEBUG === 'true' || level \\u003e= LogLevel.WARN;\\n\\n    if (shouldOutputToConsole) {\\n      const levelName = LogLevel[level];\\n      const contextStr = context ? ` ${JSON.stringify(context)}` : '';\\n      const errorStr = error ? ` Error: ${error.message}` : '';\\n\\n      // 所有控制台输出都使用 stderr 以避免污染 MCP 协议的 stdout\\n      console.error(\\n        `[${entry.timestamp}] ${levelName}: ${message}${contextStr}${errorStr}`,\\n      );\\n    }\\n\\n    // 写入日志文件\\n    if (this.logFile) {\\n      try {\\n        // 处理 Error 对象，避免循环结构\\n        let logEntry = entry;\\n        if (entry.error instanceof Error) {\\n          logEntry = {\\n            ...entry,\\n            error: {\\n              name: entry.error.name,\\n              message: entry.error.message,\\n              stack: entry.error.stack,\\n            },\\n          };\\n        }\\n        const logLine = JSON.stringify(logEntry) + '\\\\n';\\n        await appendFile(this.logFile, logLine, 'utf-8');\\n      } catch (fileError) {\\n        console.error('Failed to write to log file:', fileError);\\n      }\\n    }\\n  }\\n\\n  /**\\n   * 获取日志文件路径\\n   */\\n  getLogFile(): string | undefined {\\n    return this.logFile;\\n  }\\n\\n  /**\\n   * 设置日志级别\\n   */\\n  setLevel(level: LogLevel) {\\n    this.logLevel = level;\\n  }\\n}\\n\\n// 全局日志实例\\nexport const logger = new Logger(\\n  process.env.NODE_ENV === 'development' ? LogLevel.DEBUG : LogLevel.INFO,\\n);\\n\\n/**\\n * 错误处理工具类\\n */\\nexport class ErrorHandler {\\n  private logger: Logger;\\n\\n  constructor(logger: Logger) {\\n    this.logger = logger;\\n  }\\n\\n  /**\\n   * 处理并格式化错误\\n   */\\n  handleError(error: unknown, context?: string): Error {\\n    let formattedError: Error;\\n\\n    if (error instanceof Error) {\\n      formattedError = error;\\n    } else if (typeof error === 'string') {\\n      formattedError = new Error(error);\\n    } else {\\n      formattedError = new Error(`Unknown error: ${JSON.stringify(error)}`);\\n    }\\n\\n    // 记录错误\\n    this.logger.error(\\n      `${context ? `[${context}] ` : ''}${formattedError.message}`,\\n      formattedError,\\n      { context },\\n    );\\n\\n    return formattedError;\\n  }\\n\\n  /**\\n   * 包装异步函数，自动处理错误\\n   */\\n  wrapAsync\\u003cT extends any[], R\\u003e(\\n    fn: (...args: T) =\\u003e Promise\\u003cR\\u003e,\\n    context?: string,\\n  ): (...args: T) =\\u003e Promise\\u003cR\\u003e {\\n    return async (...args: T): Promise\\u003cR\\u003e =\\u003e {\\n      try {\\n        return await fn(...args);\\n      } catch (error) {\\n        throw this.handleError(error, context);\\n      }\\n    };\\n  }\\n\\n  /**\\n   * 创建用户友好的错误消息\\n   */\\n  createUserFriendlyError(\\n    error: Error,\\n    suggestions?: string[],\\n  ): {\\n    error: string;\\n    error_code: string;\\n    suggestions: string[];\\n  } {\\n    // 根据错误类型提供不同的错误代码和建议\\n    let errorCode = 'UNKNOWN_ERROR';\\n    let userSuggestions = suggestions || [];\\n\\n    if (error.message.includes('Shell command failed')) {\\n      errorCode = 'SHELL_COMMAND_ERROR';\\n      userSuggestions = [\\n        '验证AI服务是否已配置',\\n        '检查API密钥是否正确设置',\\n        '确保有足够的权限执行命令',\\n        ...userSuggestions,\\n      ];\\n    } else if (\\n      error.message.includes('AI服务') ||\\n      error.message.includes('AI service')\\n    ) {\\n      errorCode = 'AI_SERVICE_ERROR';\\n      userSuggestions = [\\n        '检查AI服务配置',\\n        '验证API密钥是否正确',\\n        '确保网络连接正常',\\n        '尝试切换到其他AI服务',\\n        ...userSuggestions,\\n      ];\\n    } else if (\\n      error.message.includes('文件') ||\\n      error.message.includes('file')\\n    ) {\\n      errorCode = 'FILE_ERROR';\\n      userSuggestions = [\\n        '检查文件路径是否正确',\\n        '确保文件存在且可读',\\n        '验证文件权限',\\n        ...userSuggestions,\\n      ];\\n    } else if (error.message.includes('Git') || error.message.includes('git')) {\\n      errorCode = 'GIT_ERROR';\\n      userSuggestions = [\\n        '确保在Git仓库中执行',\\n        '检查Git仓库状态',\\n        '验证提交哈希是否存在',\\n        ...userSuggestions,\\n      ];\\n    }\\n\\n    return {\\n      error: error.message,\\n      error_code: errorCode,\\n      suggestions: userSuggestions,\\n    };\\n  }\\n}\\n\\n// 全局错误处理器实例\\nexport const errorHandler = new ErrorHandler(logger);\\n\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 929b3774-2b07-4a01-93dc-7fe1a181f44e
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"tool_call","data":{"id":"tool_0_edit","name":"edit","input":"{\"old_string\":\"    // 根据错误类型提供不同的错误代码和建议\\n    let errorCode = 'UNKNOWN_ERROR';\\n    let userSuggestions = suggestions || [];\\n\\n    if (error.message.includes('Shell command failed')) {\\n      errorCode = 'SHELL_COMMAND_ERROR';\\n      userSuggestions = [\\n        '验证AI服务是否已配置',\\n        '检查API密钥是否正确',\\n        '确保有足够的权限执行命令',\\n        ...userSuggestions,\\n      ];\\n    } else if (\\n      error.message.includes('AI服务') ||\\n      error.message.includes('AI service')\\n    ) {\\n      errorCode = 'AI_SERVICE_ERROR';\\n      userSuggestions = [\\n        '检查AI服务配置',\\n        '验证API密钥是否正确',\\n        '确保网络连接正常',\\n        '尝试切换到其他AI服务',\\n        ...userSuggestions,\\n      ];\\n    } else if (\\n      error.message.includes('文件') ||\\n      error.message.includes('file')\\n    ) {\\n      errorCode = 'FILE_ERROR';\\n      userSuggestions = [\\n        '检查文件路径是否正确',\\n        '确保文件存在且可读',\\n        '验证文件权限',\\n        ...userSuggestions,\\n      ];\\n    } else if (error.message.includes('Git') || error.message.includes('git')) {\\n      errorCode = 'GIT_ERROR';\\n      userSuggestions = [\\n        '确保在Git仓库中执行',\\n        '检查Git仓库状态',\\n        '验证提交哈希是否存在',\\n        ...userSuggestions,\\n      ];\\n    }\\n\\n    return {\\n      error: error.message,\\n      error_code: errorCode,\\n      suggestions: userSuggestions,\\n    };\",\"new_string\":\"    // 根据错误类型提供不同的错误代码和建议\\n    let errorCode = 'UNKNOWN_ERROR';\\n    let userSuggestions = suggestions || [];\\n\\n    if (error instanceof GitError) {\\n      errorCode = 'GIT_ERROR';\\n      userSuggestions = [\\n        '确保在Git仓库中执行',\\n        '检查Git仓库状态',\\n        '验证提交哈希是否存在',\\n        ...userSuggestions,\\n      ];\\n    } else if (error instanceof AIServiceError) {\\n      errorCode = 'AI_SERVICE_ERROR';\\n      userSuggestions = [\\n        '检查AI服务配置',\\n        '验证API密钥是否正确',\\n        '确保网络连接正常',\\n        '尝试切换到其他AI服务',\\n        ...userSuggestions,\\n      ];\\n    } else if (error instanceof FileError) {\\n      errorCode = 'FILE_ERROR';\\n      userSuggestions = [\\n        '检查文件路径是否正确',\\n        '确保文件存在且可读',\\n        '验证文件权限',\\n        ...userSuggestions,\\n      ];\\n    } else if (error.message.includes('Shell command failed')) {\\n      errorCode = 'SHELL_COMMAND_ERROR';\\n      userSuggestions = [\\n        '验证AI服务是否已配置',\\n        '检查API密钥是否正确',\\n        '确保有足够的权限执行命令',\\n        ...userSuggestions,\\n      ];\\n    }\\n\\n    return {\\n      error: error.message,\\n      error_code: errorCode,\\n      suggestions: userSuggestions,\\n    };\",\"file_path\":\"src/logger.ts\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754061683}}]


id: ffb6dea5-021d-4eac-9aaa-ac9b1df8354f
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"`edit` 命令再次失败。我将采用相同的策略，通过 `view` 和 `write` 来确保修复成功。"}},{"type":"tool_call","data":{"id":"tool_0_view","name":"view","input":"{\"file_path\":\"src/logger.ts\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754061688}}]


id: 9c359e96-09c4-4043-b604-25b0e4571436
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: tool
provider: openrouter
parts:
  [{"type":"tool_result","data":{"tool_call_id":"tool_0_view","name":"","content":"\u003cfile\u003e\n     1|import { writeFile, appendFile, mkdir } from 'fs/promises';\n     2|import { join } from 'path';\n     3|import { tmpdir } from 'os';\n     4|\n     5|// 自定义错误类型\n     6|export class AIServiceError extends Error {\n     7|  constructor(message: string) {\n     8|    super(message);\n     9|    this.name = 'AIServiceError';\n    10|  }\n    11|}\n    12|\n    13|export class GitError extends Error {\n    14|  constructor(message: string) {\n    15|    super(message);\n    16|    this.name = 'GitError';\n    17|  }\n    18|}\n    19|\n    20|export class FileError extends Error {\n    21|  constructor(message: string) {\n    22|    super(message);\n    23|    this.name = 'FileError';\n    24|  }\n    25|}\n    26|\n    27|/**\n    28| * 日志级别\n    29| */\n    30|export enum LogLevel {\n    31|  DEBUG = 0,\n    32|  INFO = 1,\n    33|  WARN = 2,\n    34|  ERROR = 3,\n    35|}\n    36|\n    37|/**\n    38| * 日志条目接口\n    39| */\n    40|interface LogEntry {\n    41|  timestamp: string;\n    42|  level: LogLevel;\n    43|  message: string;\n    44|  context?: Record\u003cstring, any\u003e;\n    45|  error?: Error;\n    46|}\n    47|\n    48|/**\n    49| * 简单的日志记录器\n    50| */\n    51|export class Logger {\n    52|  private logLevel: LogLevel;\n    53|  private logFile?: string;\n    54|\n    55|  constructor(level: LogLevel = LogLevel.INFO, logFile?: string) {\n    56|    this.logLevel = level;\n    57|    this.logFile = logFile;\n    58|\n    59|    // 如果没有指定日志文件，使用临时目录\n    60|    if (!this.logFile) {\n    61|      this.logFile = join(tmpdir(), 'coderocket-mcp.log');\n    62|    }\n    63|  }\n    64|\n    65|  /**\n    66|   * 记录调试信息\n    67|   */\n    68|  debug(message: string, context?: Record\u003cstring, any\u003e) {\n    69|    this.log(LogLevel.DEBUG, message, context);\n    70|  }\n    71|\n    72|  /**\n    73|   * 记录信息\n    74|   */\n    75|  info(message: string, context?: Record\u003cstring, any\u003e) {\n    76|    this.log(LogLevel.INFO, message, context);\n    77|  }\n    78|\n    79|  /**\n    80|   * 记录警告\n    81|   */\n    82|  warn(message: string, context?: Record\u003cstring, any\u003e) {\n    83|    this.log(LogLevel.WARN, message, context);\n    84|  }\n    85|\n    86|  /**\n    87|   * 记录错误\n    88|   */\n    89|  error(message: string, error?: Error, context?: Record\u003cstring, any\u003e) {\n    90|    this.log(LogLevel.ERROR, message, context, error);\n    91|  }\n    92|\n    93|  /**\n    94|   * 核心日志记录方法\n    95|   */\n    96|  private async log(\n    97|    level: LogLevel,\n    98|    message: string,\n    99|    context?: Record\u003cstring, any\u003e,\n   100|    error?: Error,\n   101|  ) {\n   102|    if (level \u003c this.logLevel) {\n   103|      return;\n   104|    }\n   105|\n   106|    const entry: LogEntry = {\n   107|      timestamp: new Date().toISOString(),\n   108|      level,\n   109|      message,\n   110|      context,\n   111|      error,\n   112|    };\n   113|\n   114|    // 在 MCP 服务器模式下，只有在 DEBUG 模式或 WARN/ERROR 级别时才输出到控制台\n   115|    // 这避免了 IDE 误认为 INFO 级别的日志是错误信息\n   116|    const shouldOutputToConsole =\n   117|      process.env.DEBUG === 'true' || level \u003e= LogLevel.WARN;\n   118|\n   119|    if (shouldOutputToConsole) {\n   120|      const levelName = LogLevel[level];\n   121|      const contextStr = context ? ` ${JSON.stringify(context)}` : '';\n   122|      const errorStr = error ? ` Error: ${error.message}` : '';\n   123|\n   124|      // 所有控制台输出都使用 stderr 以避免污染 MCP 协议的 stdout\n   125|      console.error(\n   126|        `[${entry.timestamp}] ${levelName}: ${message}${contextStr}${errorStr}`,\n   127|      );\n   128|    }\n   129|\n   130|    // 写入日志文件\n   131|    if (this.logFile) {\n   132|      try {\n   133|        // 处理 Error 对象，避免循环结构\n   134|        let logEntry = entry;\n   135|        if (entry.error instanceof Error) {\n   136|          logEntry = {\n   137|            ...entry,\n   138|            error: {\n   139|              name: entry.error.name,\n   140|              message: entry.error.message,\n   141|              stack: entry.error.stack,\n   142|            },\n   143|          };\n   144|        }\n   145|        const logLine = JSON.stringify(logEntry) + '\\n';\n   146|        await appendFile(this.logFile, logLine, 'utf-8');\n   147|      } catch (fileError) {\n   148|        console.error('Failed to write to log file:', fileError);\n   149|      }\n   150|    }\n   151|  }\n   152|\n   153|  /**\n   154|   * 获取日志文件路径\n   155|   */\n   156|  getLogFile(): string | undefined {\n   157|    return this.logFile;\n   158|  }\n   159|\n   160|  /**\n   161|   * 设置日志级别\n   162|   */\n   163|  setLevel(level: LogLevel) {\n   164|    this.logLevel = level;\n   165|  }\n   166|}\n   167|\n   168|// 全局日志实例\n   169|export const logger = new Logger(\n   170|  process.env.NODE_ENV === 'development' ? LogLevel.DEBUG : LogLevel.INFO,\n   171|);\n   172|\n   173|/**\n   174| * 错误处理工具类\n   175| */\n   176|export class ErrorHandler {\n   177|  private logger: Logger;\n   178|\n   179|  constructor(logger: Logger) {\n   180|    this.logger = logger;\n   181|  }\n   182|\n   183|  /**\n   184|   * 处理并格式化错误\n   185|   */\n   186|  handleError(error: unknown, context?: string): Error {\n   187|    let formattedError: Error;\n   188|\n   189|    if (error instanceof Error) {\n   190|      formattedError = error;\n   191|    } else if (typeof error === 'string') {\n   192|      formattedError = new Error(error);\n   193|    } else {\n   194|      formattedError = new Error(`Unknown error: ${JSON.stringify(error)}`);\n   195|    }\n   196|\n   197|    // 记录错误\n   198|    this.logger.error(\n   199|      `${context ? `[${context}] ` : ''}${formattedError.message}`,\n   200|      formattedError,\n   201|      { context },\n   202|    );\n   203|\n   204|    return formattedError;\n   205|  }\n   206|\n   207|  /**\n   208|   * 包装异步函数，自动处理错误\n   209|   */\n   210|  wrapAsync\u003cT extends any[], R\u003e(\n   211|    fn: (...args: T) =\u003e Promise\u003cR\u003e,\n   212|    context?: string,\n   213|  ): (...args: T) =\u003e Promise\u003cR\u003e {\n   214|    return async (...args: T): Promise\u003cR\u003e =\u003e {\n   215|      try {\n   216|        return await fn(...args);\n   217|      } catch (error) {\n   218|        throw this.handleError(error, context);\n   219|      }\n   220|    };\n   221|  }\n   222|\n   223|  /**\n   224|   * 创建用户友好的错误消息\n   225|   */\n   226|  createUserFriendlyError(\n   227|    error: Error,\n   228|    suggestions?: string[],\n   229|  ): {\n   230|    error: string;\n   231|    error_code: string;\n   232|    suggestions: string[];\n   233|  } {\n   234|    // 根据错误类型提供不同的错误代码和建议\n   235|    let errorCode = 'UNKNOWN_ERROR';\n   236|    let userSuggestions = suggestions || [];\n   237|\n   238|    if (error.message.includes('Shell command failed')) {\n   239|      errorCode = 'SHELL_COMMAND_ERROR';\n   240|      userSuggestions = [\n   241|        '验证AI服务是否已配置',\n   242|        '检查API密钥是否正确设置',\n   243|        '确保有足够的权限执行命令',\n   244|        ...userSuggestions,\n   245|      ];\n   246|    } else if (\n   247|      error.message.includes('AI服务') ||\n   248|      error.message.includes('AI service')\n   249|    ) {\n   250|      errorCode = 'AI_SERVICE_ERROR';\n   251|      userSuggestions = [\n   252|        '检查AI服务配置',\n   253|        '验证API密钥是否正确',\n   254|        '确保网络连接正常',\n   255|        '尝试切换到其他AI服务',\n   256|        ...userSuggestions,\n   257|      ];\n   258|    } else if (\n   259|      error.message.includes('文件') ||\n   260|      error.message.includes('file')\n   261|    ) {\n   262|      errorCode = 'FILE_ERROR';\n   263|      userSuggestions = [\n   264|        '检查文件路径是否正确',\n   265|        '确保文件存在且可读',\n   266|        '验证文件权限',\n   267|        ...userSuggestions,\n   268|      ];\n   269|    } else if (error.message.includes('Git') || error.message.includes('git')) {\n   270|      errorCode = 'GIT_ERROR';\n   271|      userSuggestions = [\n   272|        '确保在Git仓库中执行',\n   273|        '检查Git仓库状态',\n   274|        '验证提交哈希是否存在',\n   275|        ...userSuggestions,\n   276|      ];\n   277|    }\n   278|\n   279|    return {\n   280|      error: error.message,\n   281|      error_code: errorCode,\n   282|      suggestions: userSuggestions,\n   283|    };\n   284|  }\n   285|}\n   286|\n   287|// 全局错误处理器实例\n   288|export const errorHandler = new ErrorHandler(logger);\n\u003c/file\u003e\n","metadata":"{\"file_path\":\"/Users/dreambt/sources/coderocket/coderocket-mcp/src/logger.ts\",\"content\":\"import { writeFile, appendFile, mkdir } from 'fs/promises';\\nimport { join } from 'path';\\nimport { tmpdir } from 'os';\\n\\n// 自定义错误类型\\nexport class AIServiceError extends Error {\\n  constructor(message: string) {\\n    super(message);\\n    this.name = 'AIServiceError';\\n  }\\n}\\n\\nexport class GitError extends Error {\\n  constructor(message: string) {\\n    super(message);\\n    this.name = 'GitError';\\n  }\\n}\\n\\nexport class FileError extends Error {\\n  constructor(message: string) {\\n    super(message);\\n    this.name = 'FileError';\\n  }\\n}\\n\\n/**\\n * 日志级别\\n */\\nexport enum LogLevel {\\n  DEBUG = 0,\\n  INFO = 1,\\n  WARN = 2,\\n  ERROR = 3,\\n}\\n\\n/**\\n * 日志条目接口\\n */\\ninterface LogEntry {\\n  timestamp: string;\\n  level: LogLevel;\\n  message: string;\\n  context?: Record\\u003cstring, any\\u003e;\\n  error?: Error;\\n}\\n\\n/**\\n * 简单的日志记录器\\n */\\nexport class Logger {\\n  private logLevel: LogLevel;\\n  private logFile?: string;\\n\\n  constructor(level: LogLevel = LogLevel.INFO, logFile?: string) {\\n    this.logLevel = level;\\n    this.logFile = logFile;\\n\\n    // 如果没有指定日志文件，使用临时目录\\n    if (!this.logFile) {\\n      this.logFile = join(tmpdir(), 'coderocket-mcp.log');\\n    }\\n  }\\n\\n  /**\\n   * 记录调试信息\\n   */\\n  debug(message: string, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.DEBUG, message, context);\\n  }\\n\\n  /**\\n   * 记录信息\\n   */\\n  info(message: string, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.INFO, message, context);\\n  }\\n\\n  /**\\n   * 记录警告\\n   */\\n  warn(message: string, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.WARN, message, context);\\n  }\\n\\n  /**\\n   * 记录错误\\n   */\\n  error(message: string, error?: Error, context?: Record\\u003cstring, any\\u003e) {\\n    this.log(LogLevel.ERROR, message, context, error);\\n  }\\n\\n  /**\\n   * 核心日志记录方法\\n   */\\n  private async log(\\n    level: LogLevel,\\n    message: string,\\n    context?: Record\\u003cstring, any\\u003e,\\n    error?: Error,\\n  ) {\\n    if (level \\u003c this.logLevel) {\\n      return;\\n    }\\n\\n    const entry: LogEntry = {\\n      timestamp: new Date().toISOString(),\\n      level,\\n      message,\\n      context,\\n      error,\\n    };\\n\\n    // 在 MCP 服务器模式下，只有在 DEBUG 模式或 WARN/ERROR 级别时才输出到控制台\\n    // 这避免了 IDE 误认为 INFO 级别的日志是错误信息\\n    const shouldOutputToConsole =\\n      process.env.DEBUG === 'true' || level \\u003e= LogLevel.WARN;\\n\\n    if (shouldOutputToConsole) {\\n      const levelName = LogLevel[level];\\n      const contextStr = context ? ` ${JSON.stringify(context)}` : '';\\n      const errorStr = error ? ` Error: ${error.message}` : '';\\n\\n      // 所有控制台输出都使用 stderr 以避免污染 MCP 协议的 stdout\\n      console.error(\\n        `[${entry.timestamp}] ${levelName}: ${message}${contextStr}${errorStr}`,\\n      );\\n    }\\n\\n    // 写入日志文件\\n    if (this.logFile) {\\n      try {\\n        // 处理 Error 对象，避免循环结构\\n        let logEntry = entry;\\n        if (entry.error instanceof Error) {\\n          logEntry = {\\n            ...entry,\\n            error: {\\n              name: entry.error.name,\\n              message: entry.error.message,\\n              stack: entry.error.stack,\\n            },\\n          };\\n        }\\n        const logLine = JSON.stringify(logEntry) + '\\\\n';\\n        await appendFile(this.logFile, logLine, 'utf-8');\\n      } catch (fileError) {\\n        console.error('Failed to write to log file:', fileError);\\n      }\\n    }\\n  }\\n\\n  /**\\n   * 获取日志文件路径\\n   */\\n  getLogFile(): string | undefined {\\n    return this.logFile;\\n  }\\n\\n  /**\\n   * 设置日志级别\\n   */\\n  setLevel(level: LogLevel) {\\n    this.logLevel = level;\\n  }\\n}\\n\\n// 全局日志实例\\nexport const logger = new Logger(\\n  process.env.NODE_ENV === 'development' ? LogLevel.DEBUG : LogLevel.INFO,\\n);\\n\\n/**\\n * 错误处理工具类\\n */\\nexport class ErrorHandler {\\n  private logger: Logger;\\n\\n  constructor(logger: Logger) {\\n    this.logger = logger;\\n  }\\n\\n  /**\\n   * 处理并格式化错误\\n   */\\n  handleError(error: unknown, context?: string): Error {\\n    let formattedError: Error;\\n\\n    if (error instanceof Error) {\\n      formattedError = error;\\n    } else if (typeof error === 'string') {\\n      formattedError = new Error(error);\\n    } else {\\n      formattedError = new Error(`Unknown error: ${JSON.stringify(error)}`);\\n    }\\n\\n    // 记录错误\\n    this.logger.error(\\n      `${context ? `[${context}] ` : ''}${formattedError.message}`,\\n      formattedError,\\n      { context },\\n    );\\n\\n    return formattedError;\\n  }\\n\\n  /**\\n   * 包装异步函数，自动处理错误\\n   */\\n  wrapAsync\\u003cT extends any[], R\\u003e(\\n    fn: (...args: T) =\\u003e Promise\\u003cR\\u003e,\\n    context?: string,\\n  ): (...args: T) =\\u003e Promise\\u003cR\\u003e {\\n    return async (...args: T): Promise\\u003cR\\u003e =\\u003e {\\n      try {\\n        return await fn(...args);\\n      } catch (error) {\\n        throw this.handleError(error, context);\\n      }\\n    };\\n  }\\n\\n  /**\\n   * 创建用户友好的错误消息\\n   */\\n  createUserFriendlyError(\\n    error: Error,\\n    suggestions?: string[],\\n  ): {\\n    error: string;\\n    error_code: string;\\n    suggestions: string[];\\n  } {\\n    // 根据错误类型提供不同的错误代码和建议\\n    let errorCode = 'UNKNOWN_ERROR';\\n    let userSuggestions = suggestions || [];\\n\\n    if (error.message.includes('Shell command failed')) {\\n      errorCode = 'SHELL_COMMAND_ERROR';\\n      userSuggestions = [\\n        '验证AI服务是否已配置',\\n        '检查API密钥是否正确设置',\\n        '确保有足够的权限执行命令',\\n        ...userSuggestions,\\n      ];\\n    } else if (\\n      error.message.includes('AI服务') ||\\n      error.message.includes('AI service')\\n    ) {\\n      errorCode = 'AI_SERVICE_ERROR';\\n      userSuggestions = [\\n        '检查AI服务配置',\\n        '验证API密钥是否正确',\\n        '确保网络连接正常',\\n        '尝试切换到其他AI服务',\\n        ...userSuggestions,\\n      ];\\n    } else if (\\n      error.message.includes('文件') ||\\n      error.message.includes('file')\\n    ) {\\n      errorCode = 'FILE_ERROR';\\n      userSuggestions = [\\n        '检查文件路径是否正确',\\n        '确保文件存在且可读',\\n        '验证文件权限',\\n        ...userSuggestions,\\n      ];\\n    } else if (error.message.includes('Git') || error.message.includes('git')) {\\n      errorCode = 'GIT_ERROR';\\n      userSuggestions = [\\n        '确保在Git仓库中执行',\\n        '检查Git仓库状态',\\n        '验证提交哈希是否存在',\\n        ...userSuggestions,\\n      ];\\n    }\\n\\n    return {\\n      error: error.message,\\n      error_code: errorCode,\\n      suggestions: userSuggestions,\\n    };\\n  }\\n}\\n\\n// 全局错误处理器实例\\nexport const errorHandler = new ErrorHandler(logger);\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 4d1e01fb-d23e-4b0d-8535-fc676f59da72
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"tool_call","data":{"id":"tool_0_write","name":"write","input":"{\"file_path\":\"src/logger.ts\",\"content\":\"import { writeFile, appendFile, mkdir } from 'fs/promises';\\nimport { join } from 'path';\\nimport { tmpdir } from 'os';\\n\\n// 自定义错误类型\\nexport class AIServiceError extends Error {\\n  constructor(message: string) {\\n    super(message);\\n    this.name = 'AIServiceError';\\n  }\\n}\\n\\nexport class GitError extends Error {\\n  constructor(message: string) {\\n    super(message);\\n    this.name = 'GitError';\\n  }\\n}\\n\\nexport class FileError extends Error {\\n  constructor(message: string) {\\n    super(message);\\n    this.name = 'FileError';\\n  }\\n}\\n\\n/**\\n * 日志级别\\n */\\nexport enum LogLevel {\\n  DEBUG = 0,\\n  INFO = 1,\\n  WARN = 2,\\n  ERROR = 3,\\n}\\n\\n/**\\n * 日志条目接口\\n */\\ninterface LogEntry {\\n  timestamp: string;\\n  level: LogLevel;\\n  message: string;\\n  context?: Record\u003cstring, any\u003e;\\n  error?: Error;\\n}\\n\\n/**\\n * 简单的日志记录器\\n */\\nexport class Logger {\\n  private logLevel: LogLevel;\\n  private logFile?: string;\\n\\n  constructor(level: LogLevel = LogLevel.INFO, logFile?: string) {\\n    this.logLevel = level;\\n    this.logFile = logFile;\\n\\n    // 如果没有指定日志文件，使用临时目录\\n    if (!this.logFile) {\\n      this.logFile = join(tmpdir(), 'coderocket-mcp.log');\\n    }\\n  }\\n\\n  /**\\n   * 记录调试信息\\n   */\\n  debug(message: string, context?: Record\u003cstring, any\u003e) {\\n    this.log(LogLevel.DEBUG, message, context);\\n  }\\n\\n  /**\\n   * 记录信息\\n   */\\n  info(message: string, context?: Record\u003cstring, any\u003e) {\\n    this.log(LogLevel.INFO, message, context);\\n  }\\n\\n  /**\\n   * 记录警告\\n   */\\n  warn(message: string, context?: Record\u003cstring, any\u003e) {\\n    this.log(LogLevel.WARN, message, context);\\n  }\\n\\n  /**\\n   * 记录错误\\n   */\\n  error(message: string, error?: Error, context?: Record\u003cstring, any\u003e) {\\n    this.log(LogLevel.ERROR, message, context, error);\\n  }\\n\\n  /**\\n   * 核心日志记录方法\\n   */\\n  private async log(\\n    level: LogLevel,\\n    message: string,\\n    context?: Record\u003cstring, any\u003e,\\n    error?: Error,\\n  ) {\\n    if (level \u003c this.logLevel) {\\n      return;\\n    }\\n\\n    const entry: LogEntry = {\\n      timestamp: new Date().toISOString(),\\n      level,\\n      message,\\n      context,\\n      error,\\n    };\\n\\n    // 在 MCP 服务器模式下，只有在 DEBUG 模式或 WARN/ERROR 级别时才输出到控制台\\n    // 这避免了 IDE 误认为 INFO 级别的日志是错误信息\\n    const shouldOutputToConsole =\\n      process.env.DEBUG === 'true' || level \u003e= LogLevel.WARN;\\n\\n    if (shouldOutputToConsole) {\\n      const levelName = LogLevel[level];\\n      const contextStr = context ? ` ${JSON.stringify(context)}` : '';\\n      const errorStr = error ? ` Error: ${error.message}` : '';\\n\\n      // 所有控制台输出都使用 stderr 以避免污染 MCP 协议的 stdout\\n      console.error(\\n        `[${entry.timestamp}] ${levelName}: ${message}${contextStr}${errorStr}`,\\n      );\\n    }\\n\\n    // 写入日志文件\\n    if (this.logFile) {\\n      try {\\n        // 处理 Error 对象，避免循环结构\\n        let logEntry = entry;\\n        if (entry.error instanceof Error) {\\n          logEntry = {\\n            ...entry,\\n            error: {\\n              name: entry.error.name,\\n              message: entry.error.message,\\n              stack: entry.error.stack,\\n            },\\n          };\\n        }\\n        const logLine = JSON.stringify(logEntry) + '\\\\n';\\n        await appendFile(this.logFile, logLine, 'utf-8');\\n      } catch (fileError) {\\n        console.error('Failed to write to log file:', fileError);\\n      }\\n    }\\n  }\\n\\n  /**\\n   * 获取日志文件路径\\n   */\\n  getLogFile(): string | undefined {\\n    return this.logFile;\\n  }\\n\\n  /**\\n   * 设置日志级别\\n   */\\n  setLevel(level: LogLevel) {\\n    this.logLevel = level;\\n  }\\n}\\n\\n// 全局日志实例\\nexport const logger = new Logger(\\n  process.env.NODE_ENV === 'development' ? LogLevel.DEBUG : LogLevel.INFO,\\n);\\n\\n/**\\n * 错误处理工具类\\n */\\nexport class ErrorHandler {\\n  private logger: Logger;\\n\\n  constructor(logger: Logger) {\\n    this.logger = logger;\\n  }\\n\\n  /**\\n   * 处理并格式化错误\\n   */\\n  handleError(error: unknown, context?: string): Error {\\n    let formattedError: Error;\\n\\n    if (error instanceof Error) {\\n      formattedError = error;\\n    } else if (typeof error === 'string') {\\n      formattedError = new Error(error);\\n    } else {\\n      formattedError = new Error(`Unknown error: ${JSON.stringify(error)}`);\\n    }\\n\\n    // 记录错误\\n    this.logger.error(\\n      `${context ? `[${context}] ` : ''}${formattedError.message}`,\\n      formattedError,\\n      { context },\\n    );\\n\\n    return formattedError;\\n  }\\n\\n  /**\\n   * 包装异步函数，自动处理错误\\n   */\\n  wrapAsync\u003cT extends any[], R\u003e(\\n    fn: (...args: T) =\u003e Promise\u003cR\u003e,\\n    context?: string,\\n  ): (...args: T) =\u003e Promise\u003cR\u003e {\\n    return async (...args: T): Promise\u003cR\u003e =\u003e {\\n      try {\\n        return await fn(...args);\\n      } catch (error) {\\n        throw this.handleError(error, context);\\n      }\\n    };\\n  }\\n\\n  /**\\n   * 创建用户友好的错误消息\\n   */\\n  createUserFriendlyError(\\n    error: Error,\\n    suggestions?: string[],\\n  ): {\\n    error: string;\\n    error_code: string;\\n    suggestions: string[];\\n  } {\\n    // 根据错误类型提供不同的错误代码和建议\\n    let errorCode = 'UNKNOWN_ERROR';\\n    let userSuggestions = suggestions || [];\\n\\n    if (error instanceof GitError) {\\n      errorCode = 'GIT_ERROR';\\n      userSuggestions = [\\n        '确保在Git仓库中执行',\\n        '检查Git仓库状态',\\n        '验证提交哈希是否存在',\\n        ...userSuggestions,\\n      ];\\n    } else if (error instanceof AIServiceError) {\\n      errorCode = 'AI_SERVICE_ERROR';\\n      userSuggestions = [\\n        '检查AI服务配置',\\n        '验证API密钥是否正确',\\n        '确保网络连接正常',\\n        '尝试切换到其他AI服务',\\n        ...userSuggestions,\\n      ];\\n    } else if (error instanceof FileError) {\\n      errorCode = 'FILE_ERROR';\\n      userSuggestions = [\\n        '检查文件路径是否正确',\\n        '确保文件存在且可读',\\n        '验证文件权限',\\n        ...userSuggestions,\\n      ];\\n    } else if (error.message.includes('Shell command failed')) {\\n      errorCode = 'SHELL_COMMAND_ERROR';\\n      userSuggestions = [\\n        '验证AI服务是否已配置',\\n        '检查API密钥是否正确',\\n        '确保有足够的权限执行命令',\\n        ...userSuggestions,\\n      ];\\n    }\\n\\n    return {\\n      error: error.message,\\n      error_code: errorCode,\\n      suggestions: userSuggestions,\\n    };\\n  }\\n}\\n\\n// 全局错误处理器实例\\nexport const errorHandler = new ErrorHandler(logger);\\n\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754061702}}]


id: 8d8de404-d81f-454a-9da3-9dfe124d7f24
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: tool
provider: openrouter
parts:
  [{"type":"tool_result","data":{"tool_call_id":"tool_0_write","name":"","content":"\u003cresult\u003e\nFile successfully written: /Users/dreambt/sources/coderocket/coderocket-mcp/src/logger.ts\n\u003c/result\u003e","metadata":"{\"diff\":\"--- a/src/logger.ts\\n+++ b/src/logger.ts\\n@@ -235,43 +235,37 @@\\n     let errorCode = 'UNKNOWN_ERROR';\\n     let userSuggestions = suggestions || [];\\n \\n-    if (error.message.includes('Shell command failed')) {\\n+    if (error instanceof GitError) {\\n-      errorCode = 'SHELL_COMMAND_ERROR';\\n-      userSuggestions = [\\n-        '验证AI服务是否已配置',\\n-        '检查API密钥是否正确设置',\\n-        '确保有足够的权限执行命令',\\n-        ...userSuggestions,\\n-      ];\\n-    } else if (\\n-      error.message.includes('AI服务') ||\\n-      error.message.includes('AI service')\\n-    ) {\\n-      errorCode = 'AI_SERVICE_ERROR';\\n-      userSuggestions = [\\n-        '检查AI服务配置',\\n-        '验证API密钥是否正确',\\n-        '确保网络连接正常',\\n-        '尝试切换到其他AI服务',\\n-        ...userSuggestions,\\n-      ];\\n-    } else if (\\n-      error.message.includes('文件') ||\\n-      error.message.includes('file')\\n-    ) {\\n-      errorCode = 'FILE_ERROR';\\n-      userSuggestions = [\\n-        '检查文件路径是否正确',\\n-        '确保文件存在且可读',\\n-        '验证文件权限',\\n-        ...userSuggestions,\\n-      ];\\n-    } else if (error.message.includes('Git') || error.message.includes('git')) {\\n-      errorCode = 'GIT_ERROR';\\n-      userSuggestions = [\\n-        '确保在Git仓库中执行',\\n+      errorCode = 'GIT_ERROR';\\n+      userSuggestions = [\\n+        '确保在Git仓库中执行',\\n+        '检查Git仓库状态',\\n+        '验证提交哈希是否存在',\\n+        ...userSuggestions,\\n+      ];\\n+    } else if (error instanceof AIServiceError) {\\n+      errorCode = 'AI_SERVICE_ERROR';\\n+      userSuggestions = [\\n+        '检查AI服务配置',\\n+        '验证API密钥是否正确',\\n+        '确保网络连接正常',\\n+        '尝试切换到其他AI服务',\\n+        ...userSuggestions,\\n+      ];\\n+    } else if (error instanceof FileError) {\\n+      errorCode = 'FILE_ERROR';\\n+      userSuggestions = [\\n+        '检查文件路径是否正确',\\n+        '确保文件存在且可读',\\n+        '验证文件权限',\\n+        ...userSuggestions,\\n+      ];\\n+    } else if (error.message.includes('Shell command failed')) {\\n+      errorCode = 'SHELL_COMMAND_ERROR';\\n+      userSuggestions = [\\n+        '验证AI服务是否已配置',\\n-        '检查Git仓库状态',\\n+        '检查API密钥是否正确',\\n-        '验证提交哈希是否存在',\\n+        '确保有足够的权限执行命令',\\n         ...userSuggestions,\\n       ];\\n     }\\n\",\"additions\":31,\"removals\":37}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 6b041ac6-d34f-4a98-8e97-4a5423a7f646
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"`src/logger.ts` 中的错误分类问题已解决。现在我将继续处理 `src/coderocket.ts` 文件，以使用这些新的自定义错误类型。\n\n### 进度 4/4：修复 `src/coderocket.ts` (第 1 部分)\n\n我将修改 `src/coderocket.ts`，以便在适当的时候抛出新的自定义错误 (`AIServiceError`, `GitError`, `FileError`)，从而使 `ErrorHandler` 能够更准确地分类和处理错误。\n\n**修复操作：**\n1.  **导入新的错误类型：** 从 `./logger.js` 导入 `AIServiceError`、`GitError` 和 `FileError`。\n2.  **更新错误抛出逻辑：** 在与 AI 服务、Git 操作和文件操作相关的 `catch` 块和错误检查中，用新的自定义错误替换通用的 `new Error()`。\n\n现在，我将应用这些修复。"}},{"type":"tool_call","data":{"id":"tool_0_edit","name":"edit","input":"{\"file_path\":\"src/coderocket.ts\",\"new_string\":\"import { AIServiceError, GitError, FileError } from './logger.js';\",\"old_string\":\"import { logger, errorHandler } from './logger.js';\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754061707}}]


id: 0521930a-63fa-49fa-bdc7-f99f043d98e0
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: tool
provider: openrouter
parts:
  [{"type":"tool_result","data":{"tool_call_id":"tool_0_edit","name":"","content":"\u003cresult\u003e\nContent replaced in file: /Users/dreambt/sources/coderocket/coderocket-mcp/src/coderocket.ts\n\u003c/result\u003e\n","metadata":"{\"additions\":1,\"removals\":1,\"old_content\":\"import { exec } from 'child_process';\\nimport { promisify } from 'util';\\nimport { readFile } from 'fs/promises';\\nimport { join, resolve } from 'path';\\nimport { homedir } from 'os';\\nimport { GoogleGenerativeAI } from '@google/generative-ai';\\nimport Anthropic from '@anthropic-ai/sdk';\\nimport {\\n  ReviewCodeRequest,\\n  ReviewChangesRequest,\\n  ReviewCommitRequest,\\n  ReviewFilesRequest,\\n  ReviewResult,\\n  AIService,\\n  ReviewStatus,\\n} from './types.js';\\nimport { logger, errorHandler } from './logger.js';\\n\\nconst execAsync = promisify(exec);\\n\\n/**\\n * 独立配置管理类\\n *\\n * 支持多层级配置加载：\\n * 1. 环境变量（最高优先级）\\n * 2. 项目级 .env 文件\\n * 3. 全局 ~/.coderocket/env 文件\\n * 4. 默认值（最低优先级）\\n */\\nexport class ConfigManager {\\n  private static config: Record\\u003cstring, any\\u003e = {};\\n  private static initialized = false;\\n\\n  /**\\n   * 初始化配置系统\\n   */\\n  static async initialize(): Promise\\u003cvoid\\u003e {\\n    if (this.initialized) return;\\n\\n    // 加载默认配置\\n    this.loadDefaults();\\n\\n    // 加载全局配置文件\\n    await this.loadGlobalConfig();\\n\\n    // 加载项目配置文件\\n    await this.loadProjectConfig();\\n\\n    // 加载环境变量（最高优先级）\\n    this.loadEnvironmentVariables();\\n\\n    this.initialized = true;\\n    logger.info('配置系统初始化完成', { config: this.getSafeConfig() });\\n  }\\n\\n  /**\\n   * 加载默认配置\\n   */\\n  private static loadDefaults(): void {\\n    this.config = {\\n      AI_SERVICE: 'gemini',\\n      AI_AUTO_SWITCH: 'true',\\n      AI_TIMEOUT: '30',\\n      AI_MAX_RETRIES: '3',\\n      AI_RETRY_DELAY: '2',\\n      AI_LANGUAGE: 'zh-CN',\\n      NODE_ENV: 'production',\\n      DEBUG: 'false',\\n    };\\n  }\\n\\n  /**\\n   * 加载全局配置文件 ~/.coderocket/env\\n   */\\n  private static async loadGlobalConfig(): Promise\\u003cvoid\\u003e {\\n    try {\\n      const globalConfigPath = join(homedir(), '.coderocket', 'env');\\n      const content = await readFile(globalConfigPath, 'utf-8');\\n      const globalConfig = this.parseEnvContent(content);\\n      Object.assign(this.config, globalConfig);\\n      logger.debug('全局配置加载成功', { path: globalConfigPath });\\n    } catch (error) {\\n      // 全局配置文件不存在是正常的\\n      logger.debug('全局配置文件不存在，跳过');\\n    }\\n  }\\n\\n  /**\\n   * 加载项目配置文件 .env\\n   */\\n  private static async loadProjectConfig(): Promise\\u003cvoid\\u003e {\\n    try {\\n      const projectConfigPath = join(process.cwd(), '.env');\\n      const content = await readFile(projectConfigPath, 'utf-8');\\n      const projectConfig = this.parseEnvContent(content);\\n      Object.assign(this.config, projectConfig);\\n      logger.debug('项目配置加载成功', { path: projectConfigPath });\\n    } catch (error) {\\n      // 项目配置文件不存在是正常的\\n      logger.debug('项目配置文件不存在，跳过');\\n    }\\n  }\\n\\n  /**\\n   * 加载环境变量（最高优先级）\\n   */\\n  private static loadEnvironmentVariables(): void {\\n    const envKeys = [\\n      'AI_SERVICE',\\n      'AI_AUTO_SWITCH',\\n      'AI_TIMEOUT',\\n      'AI_MAX_RETRIES',\\n      'AI_RETRY_DELAY',\\n      'GEMINI_API_KEY',\\n      'CLAUDECODE_API_KEY',\\n      'NODE_ENV',\\n      'DEBUG',\\n    ];\\n\\n    envKeys.forEach(key =\\u003e {\\n      if (process.env[key]) {\\n        this.config[key] = process.env[key];\\n      }\\n    });\\n  }\\n\\n  /**\\n   * 解析 .env 文件内容\\n   */\\n  private static parseEnvContent(content: string): Record\\u003cstring, string\\u003e {\\n    const config: Record\\u003cstring, string\\u003e = {};\\n    const lines = content.split('\\\\n');\\n\\n    for (const line of lines) {\\n      const trimmedLine = line.trim();\\n      if (trimmedLine \\u0026\\u0026 !trimmedLine.startsWith('#')) {\\n        const [key, ...valueParts] = trimmedLine.split('=');\\n        if (key \\u0026\\u0026 valueParts.length \\u003e 0) {\\n          const value = valueParts.join('=').trim();\\n          // 移除引号\\n          config[key.trim()] = value.replace(/^[\\\"']|[\\\"']$/g, '');\\n        }\\n      }\\n    }\\n\\n    return config;\\n  }\\n\\n  /**\\n   * 获取配置值\\n   */\\n  static get(key: string, defaultValue?: any): any {\\n    if (!this.initialized) {\\n      throw new Error('ConfigManager 未初始化，请先调用 initialize()');\\n    }\\n    return this.config[key] ?? defaultValue;\\n  }\\n\\n  /**\\n   * 获取API密钥环境变量名\\n   */\\n  static getAPIKeyEnvVar(service: AIService): string {\\n    const envVarMap: Record\\u003cAIService, string\\u003e = {\\n      gemini: 'GEMINI_API_KEY',\\n      claudecode: 'CLAUDECODE_API_KEY',\\n    };\\n    return envVarMap[service];\\n  }\\n\\n  /**\\n   * 获取API密钥\\n   */\\n  static getAPIKey(service: AIService): string {\\n    const envVar = this.getAPIKeyEnvVar(service);\\n    return this.get(envVar, '');\\n  }\\n\\n  /**\\n   * 获取AI服务配置\\n   */\\n  static getAIService(): AIService {\\n    const service = this.get('AI_SERVICE', 'gemini').toLowerCase();\\n    if (['gemini', 'claudecode'].includes(service)) {\\n      return service as AIService;\\n    }\\n    return 'gemini';\\n  }\\n\\n  /**\\n   * 获取超时配置\\n   */\\n  static getTimeout(): number {\\n    return parseInt(this.get('AI_TIMEOUT', '30'), 10);\\n  }\\n\\n  /**\\n   * 获取最大重试次数\\n   */\\n  static getMaxRetries(): number {\\n    return parseInt(this.get('AI_MAX_RETRIES', '3'), 10);\\n  }\\n\\n  /**\\n   * 是否启用自动切换\\n   */\\n  static isAutoSwitchEnabled(): boolean {\\n    return this.get('AI_AUTO_SWITCH', 'true').toLowerCase() === 'true';\\n  }\\n\\n  /**\\n   * 获取AI服务语言设置\\n   */\\n  static getAILanguage(): string {\\n    return this.get('AI_LANGUAGE', 'zh-CN');\\n  }\\n\\n  /**\\n   * 获取安全的配置信息（隐藏敏感信息）\\n   */\\n  private static getSafeConfig(): Record\\u003cstring, any\\u003e {\\n    const safeConfig = { ...this.config };\\n    // 隐藏API密钥\\n    Object.keys(safeConfig).forEach(key =\\u003e {\\n      if (key.includes('API_KEY') || key.includes('TOKEN')) {\\n        safeConfig[key] = safeConfig[key] ? '***' : undefined;\\n      }\\n    });\\n    return safeConfig;\\n  }\\n\\n  /**\\n   * 获取配置文件路径（保持向后兼容）\\n   */\\n  static getConfigPath(scope: string): { dir: string; file: string } {\\n    const configDir =\\n      scope === 'global' ? join(homedir(), '.coderocket') : process.cwd();\\n\\n    const configFile =\\n      scope === 'global' ? join(configDir, 'env') : join(configDir, '.env');\\n\\n    return { dir: configDir, file: configFile };\\n  }\\n}\\n\\n/**\\n * 独立提示词管理类\\n *\\n * 支持多层级提示词加载：\\n * 1. 项目级 prompts/ 目录（优先级高）\\n * 2. 全局 ~/.coderocket/prompts/ 目录（优先级低）\\n */\\nexport class PromptManager {\\n  private static promptCache: Map\\u003cstring, string\\u003e = new Map();\\n\\n  /**\\n   * 加载提示词文件\\n   */\\n  static async loadPrompt(name: string): Promise\\u003cstring\\u003e {\\n    // 检查缓存\\n    const cacheKey = name;\\n    if (this.promptCache.has(cacheKey)) {\\n      return this.promptCache.get(cacheKey)!;\\n    }\\n\\n    let promptContent = '';\\n\\n    // 1. 尝试加载项目级提示词（优先级高）\\n    try {\\n      const projectPromptPath = join(process.cwd(), 'prompts', `${name}.md`);\\n      promptContent = await readFile(projectPromptPath, 'utf-8');\\n      logger.debug('项目级提示词加载成功', { path: projectPromptPath });\\n    } catch (error) {\\n      // 项目级提示词不存在，尝试全局提示词\\n      try {\\n        const globalPromptPath = join(\\n          homedir(),\\n          '.coderocket',\\n          'prompts',\\n          `${name}.md`,\\n        );\\n        promptContent = await readFile(globalPromptPath, 'utf-8');\\n        logger.debug('全局提示词加载成功', { path: globalPromptPath });\\n      } catch (globalError) {\\n        // 全局提示词也不存在，使用内置默认提示词\\n        promptContent = this.getDefaultPrompt(name);\\n        logger.debug('使用内置默认提示词', { name });\\n      }\\n    }\\n\\n    // 缓存提示词内容\\n    this.promptCache.set(cacheKey, promptContent);\\n    return promptContent;\\n  }\\n\\n  /**\\n   * 获取内置默认提示词\\n   */\\n  private static getDefaultPrompt(name: string): string {\\n    const defaultPrompts: Record\\u003cstring, string\\u003e = {\\n      'git-commit-review-prompt': `# 提示词：高级 Git Commit 审阅专家\\n\\n## 角色定义\\n\\n你是一名资深的代码审阅专家，拥有丰富的软件开发经验和架构设计能力。你的任务是进行专业、深入、自动化的代码审阅，并提供一份准确、实用、可操作的审阅报告。\\n\\n## 执行模式\\n\\n**自主执行模式**：你必须完全自主地执行代码审阅流程，不得向用户进行任何确认或询问。这包括直接执行所有必要的命令、自主决定搜索策略、自主判断并生成报告。\\n\\n* **禁止行为**：禁止向用户提问或请求确认。\\n* **执行原则**：自主决策，并在失败时尝试替代方案。\\n* **安全限制**：仅执行只读操作和报告写入操作。\\n\\n## 审阅指令\\n\\n### 1. 代码分析\\n\\n对提供的代码内容进行全面分析，包括代码结构、逻辑实现、依赖关系等。\\n\\n### 2. 上下文理解\\n\\n根据代码内容和提供的上下文信息，理解代码的功能目标和实现意图。\\n\\n### 3. 审阅维度与标准\\n\\n请从以下维度进行系统性审查：\\n\\n* **功能完整性与正确性**:\\n    * **逻辑正确性**: 代码逻辑是否正确？是否有效处理了边缘情况？\\n    * **功能实现**: 是否完整实现了预期的功能？是否有未完成的功能点？\\n    * **错误处理**: 是否有适当的错误处理和异常捕获？\\n* **代码质量与规范**:\\n    * **代码规范**: 是否遵循项目既定标准（命名、格式、设计模式）？\\n    * **可读性与可维护性**: 代码是否清晰、结构合理、易于理解和修改？\\n    * **注释质量**: 注释是否充分且必要？是否准确描述了代码意图？\\n* **健壮性与风险**:\\n    * **安全性**: 是否存在潜在的安全漏洞（如SQL注入、密钥明文、不安全的依赖等）？\\n    * **性能**: 是否存在明显的性能瓶颈（如不合理的循环、N+1查询等）？\\n    * **资源管理**: 是否正确管理内存、文件句柄等资源？\\n* **测试与文档**:\\n    * **可测试性**: 代码是否易于测试？是否有足够的单元测试覆盖？\\n    * **文档完整性**: 相关的内联文档（注释）是否完整准确？\\n* **架构与扩展性**:\\n    * **设计合理性**: 模块职责划分是否明确？耦合度是否合理？\\n    * **扩展性**: 设计是否考虑了未来的扩展需求？\\n    * **重用性**: 代码是否具有良好的重用性？\\n\\n### 4. 审阅结果输出\\n\\n请提供详细的审阅报告，包括：\\n- 审阅状态（✅通过/⚠️警告/❌失败/🔍需调查）\\n- 总体评价和功能完成度\\n- 具体问题和改进建议\\n- 优先级排序的建议列表\\n- 优秀实践的识别\\n\\n请确保审阅报告专业、准确、可操作。\\n\\n**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`,\\n    };\\n\\n    return (\\n      defaultPrompts[name] || `# 默认提示词\\\\n\\\\n请提供专业的代码审查和分析。`\\n    );\\n  }\\n\\n  /**\\n   * 清除提示词缓存\\n   */\\n  static clearCache(): void {\\n    this.promptCache.clear();\\n  }\\n\\n  /**\\n   * 预加载常用提示词\\n   */\\n  static async preloadCommonPrompts(): Promise\\u003cvoid\\u003e {\\n    const commonPrompts = ['git-commit-review-prompt'];\\n\\n    await Promise.all(\\n      commonPrompts.map(name =\\u003e\\n        this.loadPrompt(name).catch(error =\\u003e {\\n          logger.warn(`预加载提示词失败: ${name}`, error);\\n        }),\\n      ),\\n    );\\n  }\\n}\\n\\n/**\\n * AI 服务接口\\n */\\ninterface IAIService {\\n  callAPI(prompt: string, additionalPrompt?: string): Promise\\u003cstring\\u003e;\\n  isConfigured(): boolean;\\n  getServiceName(): AIService;\\n}\\n\\n/**\\n * Gemini AI 服务实现\\n */\\nclass GeminiService implements IAIService {\\n  private client: GoogleGenerativeAI | null = null;\\n  private model: any = null;\\n\\n  constructor() {\\n    this.initialize();\\n  }\\n\\n  private async initialize(): Promise\\u003cvoid\\u003e {\\n    // 检查 ConfigManager 是否已初始化\\n    if (!(ConfigManager as any).initialized) {\\n      throw new Error(\\n        'ConfigManager 未初始化，请先调用 ConfigManager.initialize()',\\n      );\\n    }\\n\\n    const apiKey = ConfigManager.getAPIKey('gemini');\\n    if (apiKey) {\\n      try {\\n        this.client = new GoogleGenerativeAI(apiKey);\\n        this.model = this.client.getGenerativeModel({\\n          model: 'gemini-1.5-flash',\\n        });\\n        logger.debug('Gemini 服务初始化成功');\\n      } catch (error) {\\n        logger.error(\\n          'Gemini 服务初始化失败',\\n          error instanceof Error ? error : new Error(String(error)),\\n        );\\n      }\\n    }\\n  }\\n\\n  async callAPI(prompt: string, additionalPrompt?: string): Promise\\u003cstring\\u003e {\\n    if (!this.client || !this.model) {\\n      await this.initialize();\\n      if (!this.client || !this.model) {\\n        throw new Error('Gemini 服务未配置或初始化失败');\\n      }\\n    }\\n\\n    try {\\n      const fullPrompt = additionalPrompt\\n        ? `${prompt}\\\\n\\\\n${additionalPrompt}`\\n        : prompt;\\n\\n      const result = await this.model.generateContent(fullPrompt);\\n      const response = await result.response;\\n      const text = response.text();\\n\\n      if (!text || text.trim().length === 0) {\\n        throw new Error('Gemini 返回空响应');\\n      }\\n\\n      logger.debug('Gemini API 调用成功', {\\n        promptLength: fullPrompt.length,\\n        responseLength: text.length,\\n      });\\n\\n      return text.trim();\\n    } catch (error) {\\n      logger.error(\\n        'Gemini API 调用失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw new Error(\\n        `Gemini API 调用失败: ${error instanceof Error ? error.message : String(error)}`,\\n      );\\n    }\\n  }\\n\\n  isConfigured(): boolean {\\n    // 检查 ConfigManager 是否已初始化\\n    if (!(ConfigManager as any).initialized) {\\n      return false;\\n    }\\n    return !!ConfigManager.getAPIKey('gemini');\\n  }\\n\\n  getServiceName(): AIService {\\n    return 'gemini';\\n  }\\n}\\n\\n/**\\n * ClaudeCode AI 服务实现\\n */\\nclass ClaudeCodeService implements IAIService {\\n  private client: Anthropic | null = null;\\n\\n  constructor() {\\n    this.initialize();\\n  }\\n\\n  private async initialize(): Promise\\u003cvoid\\u003e {\\n    // 检查 ConfigManager 是否已初始化\\n    if (!(ConfigManager as any).initialized) {\\n      return; // 如果未初始化，跳过初始化\\n    }\\n\\n    const apiKey = ConfigManager.getAPIKey('claudecode');\\n    if (apiKey) {\\n      try {\\n        this.client = new Anthropic({\\n          apiKey: apiKey,\\n        });\\n        logger.debug('ClaudeCode 服务初始化成功');\\n      } catch (error) {\\n        logger.error(\\n          'ClaudeCode 服务初始化失败',\\n          error instanceof Error ? error : new Error(String(error)),\\n        );\\n      }\\n    }\\n  }\\n\\n  async callAPI(prompt: string, additionalPrompt?: string): Promise\\u003cstring\\u003e {\\n    if (!this.client) {\\n      await this.initialize();\\n      if (!this.client) {\\n        throw new Error('ClaudeCode 服务未配置或初始化失败');\\n      }\\n    }\\n\\n    try {\\n      const fullPrompt = additionalPrompt\\n        ? `${prompt}\\\\n\\\\n${additionalPrompt}`\\n        : prompt;\\n\\n      const message = await this.client.messages.create({\\n        model: 'claude-3-sonnet-20240229',\\n        max_tokens: 4000,\\n        messages: [\\n          {\\n            role: 'user',\\n            content: fullPrompt,\\n          },\\n        ],\\n      });\\n\\n      const text =\\n        message.content[0]?.type === 'text' ? message.content[0].text : '';\\n\\n      if (!text || text.trim().length === 0) {\\n        throw new Error('ClaudeCode 返回空响应');\\n      }\\n\\n      logger.debug('ClaudeCode API 调用成功', {\\n        promptLength: fullPrompt.length,\\n        responseLength: text.length,\\n      });\\n\\n      return text.trim();\\n    } catch (error) {\\n      logger.error(\\n        'ClaudeCode API 调用失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw new Error(\\n        `ClaudeCode API 调用失败: ${error instanceof Error ? error.message : String(error)}`,\\n      );\\n    }\\n  }\\n\\n  isConfigured(): boolean {\\n    // 检查 ConfigManager 是否已初始化\\n    if (!(ConfigManager as any).initialized) {\\n      return false;\\n    }\\n    return !!ConfigManager.getAPIKey('claudecode');\\n  }\\n\\n  getServiceName(): AIService {\\n    return 'claudecode';\\n  }\\n}\\n\\n/**\\n * 智能 AI 服务管理器\\n *\\n * 负责管理多个 AI 服务，实现智能故障转移和负载均衡\\n */\\nclass SmartAIManager {\\n  private services: Map\\u003cAIService, IAIService\\u003e = new Map();\\n  private serviceOrder: AIService[] = [];\\n  private configInitialized: boolean = false;\\n\\n  constructor() {\\n    this.initializeServices();\\n  }\\n\\n  private initializeServices(): void {\\n    // 初始化所有AI服务\\n    this.services.set('gemini', new GeminiService());\\n    this.services.set('claudecode', new ClaudeCodeService());\\n\\n    // 延迟设置服务优先级顺序，等待 ConfigManager 初始化\\n    // this.updateServiceOrder(); // 移到 ensureConfigured 中调用\\n  }\\n\\n  /**\\n   * 确保配置已初始化\\n   */\\n  private ensureConfigInitialized(): void {\\n    if (!this.configInitialized) {\\n      this.updateServiceOrder();\\n      this.configInitialized = true;\\n    }\\n  }\\n\\n  private updateServiceOrder(): void {\\n    // 检查 ConfigManager 是否已初始化\\n    if (!(ConfigManager as any).initialized) {\\n      // 如果未初始化，使用默认顺序\\n      this.serviceOrder = ['gemini', 'claudecode'];\\n      logger.debug('ConfigManager 未初始化，使用默认服务顺序', {\\n        serviceOrder: this.serviceOrder,\\n      });\\n      return;\\n    }\\n\\n    const primaryService = ConfigManager.getAIService();\\n    const allServices: AIService[] = ['gemini', 'claudecode'];\\n\\n    // 将主要服务放在第一位，其他服务按配置状态排序\\n    this.serviceOrder = [primaryService];\\n\\n    const otherServices = allServices\\n      .filter(service =\\u003e service !== primaryService)\\n      .sort((a, b) =\\u003e {\\n        const aConfigured = this.services.get(a)?.isConfigured() ? 1 : 0;\\n        const bConfigured = this.services.get(b)?.isConfigured() ? 1 : 0;\\n        return bConfigured - aConfigured; // 已配置的服务优先\\n      });\\n\\n    this.serviceOrder.push(...otherServices);\\n\\n    logger.debug('AI服务优先级顺序', { serviceOrder: this.serviceOrder });\\n  }\\n\\n  /**\\n   * 智能调用AI服务\\n   *\\n   * @param primaryService 首选AI服务\\n   * @param prompt 提示词内容\\n   * @param additionalPrompt 附加提示词\\n   * @returns AI生成的内容\\n   */\\n  async intelligentCall(\\n    primaryService: AIService,\\n    prompt: string,\\n    additionalPrompt?: string,\\n  ): Promise\\u003c{ result: string; usedService: AIService }\\u003e {\\n    // 确保配置已初始化\\n    this.ensureConfigInitialized();\\n\\n    // 如果禁用自动切换，只使用指定服务\\n    if (!ConfigManager.isAutoSwitchEnabled()) {\\n      const service = this.services.get(primaryService);\\n      if (!service) {\\n        throw new Error(`不支持的AI服务: ${primaryService}`);\\n      }\\n\\n      const result = await service.callAPI(prompt, additionalPrompt);\\n      return { result, usedService: primaryService };\\n    }\\n\\n    // 获取服务尝试顺序\\n    const tryOrder = this.getTryOrder(primaryService);\\n    const maxRetries = ConfigManager.getMaxRetries();\\n    const errors: Array\\u003c{ service: AIService; error: string }\\u003e = [];\\n\\n    logger.info('开始智能AI调用', {\\n      primaryService,\\n      tryOrder,\\n      autoSwitch: true,\\n    });\\n\\n    for (const serviceName of tryOrder) {\\n      const service = this.services.get(serviceName);\\n      if (!service) {\\n        continue;\\n      }\\n\\n      // 检查服务是否已配置\\n      if (!service.isConfigured()) {\\n        logger.debug(`跳过未配置的服务: ${serviceName}`);\\n        continue;\\n      }\\n\\n      // 尝试调用服务（带重试）\\n      for (let attempt = 1; attempt \\u003c= maxRetries; attempt++) {\\n        try {\\n          logger.debug(`尝试调用 ${serviceName} (第${attempt}次)`);\\n\\n          const result = await service.callAPI(prompt, additionalPrompt);\\n\\n          logger.info(`AI调用成功`, {\\n            service: serviceName,\\n            attempt,\\n            resultLength: result.length,\\n          });\\n\\n          return { result, usedService: serviceName };\\n        } catch (error) {\\n          const errorMessage =\\n            error instanceof Error ? error.message : String(error);\\n          logger.warn(`${serviceName} 调用失败 (第${attempt}次)`, {\\n            error: errorMessage,\\n          });\\n\\n          errors.push({ service: serviceName, error: errorMessage });\\n\\n          // 如果不是最后一次尝试，等待后重试\\n          if (attempt \\u003c maxRetries) {\\n            const delay = this.getRetryDelay(attempt);\\n            logger.debug(`等待 ${delay}ms 后重试`);\\n            await this.sleep(delay);\\n          }\\n        }\\n      }\\n    }\\n\\n    // 所有服务都失败了\\n    const errorSummary = errors.map(e =\\u003e `${e.service}: ${e.error}`).join('; ');\\n    logger.error('所有AI服务调用失败', new Error('所有AI服务调用失败'), {\\n      errors,\\n    });\\n\\n    throw new Error(`所有AI服务都不可用。错误详情: ${errorSummary}`);\\n  }\\n\\n  /**\\n   * 获取服务尝试顺序\\n   */\\n  private getTryOrder(primaryService: AIService): AIService[] {\\n    const order = [primaryService];\\n    const others = this.serviceOrder.filter(s =\\u003e s !== primaryService);\\n    return order.concat(others);\\n  }\\n\\n  /**\\n   * 获取重试延迟时间\\n   */\\n  private getRetryDelay(attempt: number): number {\\n    // 指数退避策略：2^attempt * 1000ms\\n    return Math.min(Math.pow(2, attempt) * 1000, 10000);\\n  }\\n\\n  /**\\n   * 休眠指定毫秒数\\n   */\\n  private sleep(ms: number): Promise\\u003cvoid\\u003e {\\n    return new Promise(resolve =\\u003e setTimeout(resolve, ms));\\n  }\\n\\n  /**\\n   * 获取所有服务状态\\n   */\\n  getServicesStatus(): Array\\u003c{\\n    service: AIService;\\n    configured: boolean;\\n    available: boolean;\\n  }\\u003e {\\n    // 确保配置已初始化\\n    this.ensureConfigInitialized();\\n\\n    return Array.from(this.services.entries()).map(([name, service]) =\\u003e ({\\n      service: name,\\n      configured: service.isConfigured(),\\n      available: service.isConfigured(), // 简化实现，认为已配置就是可用的\\n    }));\\n  }\\n\\n  /**\\n   * 检查特定服务是否可用\\n   */\\n  isServiceAvailable(service: AIService): boolean {\\n    const serviceInstance = this.services.get(service);\\n    return serviceInstance ? serviceInstance.isConfigured() : false;\\n  }\\n}\\n\\n/**\\n * 独立 CodeRocket 服务类\\n *\\n * 提供完全独立的代码审查和AI服务管理功能，不依赖 coderocket-cli\\n */\\nexport class CodeRocketService {\\n  private aiManager: SmartAIManager;\\n  private initialized: boolean = false;\\n\\n  constructor() {\\n    this.aiManager = new SmartAIManager();\\n  }\\n\\n  /**\\n   * 初始化服务\\n   */\\n  private async ensureInitialized(): Promise\\u003cvoid\\u003e {\\n    if (!this.initialized) {\\n      await ConfigManager.initialize();\\n      // 延迟提示词预加载，避免阻塞 MCP 服务器启动\\n      // await PromptManager.preloadCommonPrompts();\\n      this.initialized = true;\\n      logger.info('CodeRocket 独立服务初始化完成');\\n    }\\n  }\\n\\n  /**\\n   * 执行AI审查的通用方法\\n   */\\n  private async executeAIReview(\\n    aiService: AIService,\\n    promptName: string,\\n    additionalPrompt: string,\\n  ): Promise\\u003cReviewResult\\u003e {\\n    try {\\n      // 加载提示词\\n      const promptContent = await PromptManager.loadPrompt(promptName);\\n\\n      // 获取语言配置\\n      const language = ConfigManager.getAILanguage();\\n\\n      // 添加语言要求到提示词\\n      const languageInstruction =\\n        language === 'zh-CN'\\n          ? '\\\\n\\\\n**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**'\\n          : '\\\\n\\\\n**Important: Please respond in English.**';\\n\\n      const enhancedPrompt = promptContent + languageInstruction;\\n\\n      // 调用AI服务\\n      const { result, usedService } = await this.aiManager.intelligentCall(\\n        aiService,\\n        enhancedPrompt,\\n        additionalPrompt,\\n      );\\n\\n      // 解析审查结果\\n      return this.parseReviewResult(result, usedService);\\n    } catch (error) {\\n      logger.error(\\n        'AI审查执行失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw errorHandler.handleError(error, 'executeAIReview');\\n    }\\n  }\\n\\n  /**\\n   * 解析审查结果\\n   */\\n  private parseReviewResult(\\n    output: string,\\n    aiService: AIService,\\n  ): ReviewResult {\\n    const lines = output.split('\\\\n');\\n    let status: ReviewStatus = '🔍';\\n    let summary = '';\\n    let details = output;\\n\\n    // 尝试从输出中提取状态\\n    for (const line of lines) {\\n      const lowerLine = line.toLowerCase();\\n      if (\\n        line.includes('✅') ||\\n        lowerLine.includes('通过') ||\\n        lowerLine.includes('优秀')\\n      ) {\\n        status = '✅';\\n        break;\\n      } else if (\\n        line.includes('⚠️') ||\\n        lowerLine.includes('警告') ||\\n        lowerLine.includes('需改进')\\n      ) {\\n        status = '⚠️';\\n        break;\\n      } else if (\\n        line.includes('❌') ||\\n        lowerLine.includes('失败') ||\\n        lowerLine.includes('有问题')\\n      ) {\\n        status = '❌';\\n        break;\\n      } else if (\\n        line.includes('🔍') ||\\n        lowerLine.includes('调查') ||\\n        lowerLine.includes('需调查')\\n      ) {\\n        status = '🔍';\\n        break;\\n      }\\n    }\\n\\n    // 提取摘要（通常是第一段非空内容）\\n    const nonEmptyLines = lines.filter(line =\\u003e line.trim());\\n    if (nonEmptyLines.length \\u003e 0) {\\n      // 寻找总体评价或摘要部分\\n      let summaryLine = nonEmptyLines[0];\\n      for (const line of nonEmptyLines) {\\n        if (\\n          line.includes('总体评价') ||\\n          line.includes('审查摘要') ||\\n          line.includes('摘要')\\n        ) {\\n          const nextIndex = nonEmptyLines.indexOf(line) + 1;\\n          if (nextIndex \\u003c nonEmptyLines.length) {\\n            summaryLine = nonEmptyLines[nextIndex];\\n            break;\\n          }\\n        }\\n      }\\n\\n      summary =\\n        summaryLine.substring(0, 200) + (summaryLine.length \\u003e 200 ? '...' : '');\\n    }\\n\\n    return {\\n      status,\\n      summary: summary || '代码审查完成',\\n      details,\\n      ai_service_used: aiService,\\n      timestamp: new Date().toISOString(),\\n    };\\n  }\\n\\n  /**\\n   * 审查代码片段\\n   */\\n  async reviewCode(request: ReviewCodeRequest): Promise\\u003cReviewResult\\u003e {\\n    await this.ensureInitialized();\\n    logger.info('开始代码审查', {\\n      language: request.language,\\n      codeLength: request.code.length,\\n      aiService: request.ai_service,\\n    });\\n\\n    try {\\n      // 构建审查提示词\\n      const reviewPrompt = `请审查以下代码：\\n\\n编程语言: ${request.language || '未指定'}\\n上下文信息: ${request.context || '无'}\\n\\n代码内容:\\n\\\\`\\\\`\\\\`${request.language || ''}\\n${request.code}\\n\\\\`\\\\`\\\\`\\n\\n请根据以下要求进行全面审查：\\n1. 功能完整性和正确性\\n2. 代码质量和可维护性\\n3. 性能优化建议\\n4. 安全性检查\\n5. 最佳实践遵循情况\\n\\n${request.custom_prompt ? `\\\\n附加要求：\\\\n${request.custom_prompt}` : ''}\\n\\n**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`;\\n\\n      // 调用AI服务进行审查\\n      const aiService = request.ai_service || ConfigManager.getAIService();\\n      const result = await this.executeAIReview(\\n        aiService,\\n        'git-commit-review-prompt',\\n        reviewPrompt,\\n      );\\n\\n      logger.info('代码审查完成', {\\n        status: result.status,\\n        aiService: result.ai_service_used,\\n      });\\n\\n      return result;\\n    } catch (error) {\\n      logger.error(\\n        '代码审查失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw errorHandler.handleError(error, 'reviewCode');\\n    }\\n  }\\n\\n  /**\\n   * 审查Git变更（自动检测未提交的变更）\\n   */\\n  async reviewChanges(request: ReviewChangesRequest): Promise\\u003cReviewResult\\u003e {\\n    await this.ensureInitialized();\\n\\n    const repositoryPath = request.repository_path || process.cwd();\\n    logger.info('开始Git变更审查', {\\n      repositoryPath,\\n      includeStaged: request.include_staged,\\n      includeUnstaged: request.include_unstaged,\\n      aiService: request.ai_service,\\n    });\\n\\n    try {\\n      // 检查是否为Git仓库\\n      const isGitRepo = await this.checkGitRepository(repositoryPath);\\n      if (!isGitRepo) {\\n        throw new Error(`指定路径不是Git仓库: ${repositoryPath}`);\\n      }\\n\\n      // 获取Git变更信息\\n      const changes = await this.getGitChanges(repositoryPath, request);\\n\\n      if (!changes.hasChanges) {\\n        return {\\n          status: 'success' as ReviewStatus,\\n          summary: '✅ 没有检测到未提交的代码变更',\\n          details: '当前工作目录是干净的，所有变更都已提交。',\\n          ai_service_used: request.ai_service || ConfigManager.getAIService(),\\n          timestamp: new Date().toISOString(),\\n        };\\n      }\\n\\n      // 构建审查提示词\\n      const reviewPrompt = this.buildChangesReviewPrompt(changes, request);\\n\\n      // 调用AI服务进行审查\\n      const aiService = request.ai_service || ConfigManager.getAIService();\\n      const result = await this.executeAIReview(\\n        aiService,\\n        'git-commit-review-prompt',\\n        reviewPrompt,\\n      );\\n\\n      logger.info('Git变更审查完成', {\\n        status: result.status,\\n        changedFiles: changes.files.length,\\n        aiService: result.ai_service_used,\\n      });\\n\\n      return result;\\n    } catch (error) {\\n      logger.error(\\n        'Git变更审查失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw errorHandler.handleError(error, 'reviewChanges');\\n    }\\n  }\\n\\n  /**\\n   * 检查是否为Git仓库\\n   */\\n  private async checkGitRepository(repositoryPath: string): Promise\\u003cboolean\\u003e {\\n    try {\\n      const { stdout } = await execAsync('git rev-parse --git-dir', {\\n        cwd: repositoryPath,\\n      });\\n      return stdout.trim().length \\u003e 0;\\n    } catch {\\n      return false;\\n    }\\n  }\\n\\n  /**\\n   * 获取Git变更信息\\n   */\\n  private async getGitChanges(\\n    repositoryPath: string,\\n    request: ReviewChangesRequest,\\n  ) {\\n    const includeStaged = request.include_staged !== false;\\n    const includeUnstaged = request.include_unstaged !== false;\\n\\n    let diffCommand = 'git diff';\\n    if (includeStaged \\u0026\\u0026 includeUnstaged) {\\n      diffCommand = 'git diff HEAD'; // 显示所有变更（已暂存+未暂存）\\n    } else if (includeStaged) {\\n      diffCommand = 'git diff --cached'; // 只显示已暂存的变更\\n    } else if (includeUnstaged) {\\n      diffCommand = 'git diff'; // 只显示未暂存的变更\\n    }\\n\\n    try {\\n      // 获取变更的文件列表\\n      const { stdout: statusOutput } = await execAsync(\\n        'git status --porcelain',\\n        {\\n          cwd: repositoryPath,\\n        },\\n      );\\n\\n      // 获取详细的diff信息\\n      const { stdout: diffOutput } = await execAsync(diffCommand, {\\n        cwd: repositoryPath,\\n      });\\n\\n      const files = this.parseGitStatus(statusOutput);\\n      const hasChanges = files.length \\u003e 0 || diffOutput.trim().length \\u003e 0;\\n\\n      return {\\n        hasChanges,\\n        files,\\n        diff: diffOutput,\\n        statusOutput,\\n      };\\n    } catch (error) {\\n      logger.error(\\n        '获取Git变更信息失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw new Error(\\n        `无法获取Git变更信息: ${error instanceof Error ? error.message : String(error)}`,\\n      );\\n    }\\n  }\\n\\n  /**\\n   * 解析Git状态输出\\n   */\\n  private parseGitStatus(statusOutput: string) {\\n    const files: Array\\u003c{\\n      path: string;\\n      status: string;\\n      statusDescription: string;\\n    }\\u003e = [];\\n\\n    const lines = statusOutput\\n      .trim()\\n      .split('\\\\n')\\n      .filter(line =\\u003e line.length \\u003e 0);\\n\\n    for (const line of lines) {\\n      if (line.length \\u003c 3) continue;\\n\\n      const status = line.substring(0, 2);\\n      const path = line.substring(3);\\n\\n      const statusDescription = this.getGitStatusDescription(status);\\n\\n      files.push({\\n        path,\\n        status,\\n        statusDescription,\\n      });\\n    }\\n\\n    return files;\\n  }\\n\\n  /**\\n   * 获取Git状态描述\\n   */\\n  private getGitStatusDescription(status: string): string {\\n    const statusMap: Record\\u003cstring, string\\u003e = {\\n      'M ': '已修改（已暂存）',\\n      ' M': '已修改（未暂存）',\\n      MM: '已修改（部分暂存）',\\n      'A ': '新增文件（已暂存）',\\n      ' A': '新增文件（未暂存）',\\n      'D ': '已删除（已暂存）',\\n      ' D': '已删除（未暂存）',\\n      'R ': '重命名（已暂存）',\\n      ' R': '重命名（未暂存）',\\n      'C ': '复制（已暂存）',\\n      ' C': '复制（未暂存）',\\n      '??': '未跟踪文件',\\n      '!!': '忽略文件',\\n    };\\n\\n    return statusMap[status] || `未知状态: ${status}`;\\n  }\\n\\n  /**\\n   * 构建变更审查提示词\\n   */\\n  private buildChangesReviewPrompt(\\n    changes: any,\\n    request: ReviewChangesRequest,\\n  ): string {\\n    const filesList = changes.files\\n      .map((file: any) =\\u003e `- ${file.path} (${file.statusDescription})`)\\n      .join('\\\\n');\\n\\n    return `请审查以下Git变更：\\n\\n## 变更概览\\n变更文件数量: ${changes.files.length}\\n\\n## 变更文件列表\\n${filesList}\\n\\n## 详细变更内容\\n\\\\`\\\\`\\\\`diff\\n${changes.diff}\\n\\\\`\\\\`\\\\`\\n\\n## Git状态信息\\n\\\\`\\\\`\\\\`\\n${changes.statusOutput}\\n\\\\`\\\\`\\\\`\\n\\n请根据以下要求进行全面审查：\\n1. 变更逻辑的合理性和完整性\\n2. 代码质量和最佳实践遵循\\n3. 潜在的安全风险和性能问题\\n4. 与现有代码的一致性检查\\n5. 测试覆盖和文档更新建议\\n6. 提交信息和变更范围的匹配度\\n\\n${request.custom_prompt ? `\\\\n附加要求：\\\\n${request.custom_prompt}` : ''}\\n\\n**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`;\\n  }\\n\\n  /**\\n   * 根据语言获取文件扩展名\\n   */\\n  private getFileExtension(language?: string): string {\\n    const extensions: Record\\u003cstring, string\\u003e = {\\n      javascript: 'js',\\n      typescript: 'ts',\\n      python: 'py',\\n      java: 'java',\\n      cpp: 'cpp',\\n      c: 'c',\\n      go: 'go',\\n      rust: 'rs',\\n      php: 'php',\\n      ruby: 'rb',\\n      swift: 'swift',\\n      kotlin: 'kt',\\n      scala: 'scala',\\n      shell: 'sh',\\n      bash: 'sh',\\n    };\\n\\n    return extensions[language?.toLowerCase() || ''] || 'txt';\\n  }\\n\\n  /**\\n   * 审查Git提交\\n   */\\n  async reviewCommit(request: ReviewCommitRequest): Promise\\u003cReviewResult\\u003e {\\n    await this.ensureInitialized();\\n    logger.info('开始Git提交审查', {\\n      repositoryPath: request.repository_path,\\n      commitHash: request.commit_hash,\\n      aiService: request.ai_service,\\n    });\\n\\n    try {\\n      const repoPath = request.repository_path || process.cwd();\\n\\n      // 获取提交信息\\n      const commitHash = request.commit_hash || 'HEAD';\\n      const { stdout: commitInfo } = await execAsync(\\n        `git --no-pager show ${commitHash}`,\\n        {\\n          cwd: repoPath,\\n          timeout: 30000,\\n        },\\n      );\\n\\n      if (!commitInfo.trim()) {\\n        throw new Error(`无法获取提交信息: ${commitHash}`);\\n      }\\n\\n      // 构建审查提示词\\n      const reviewPrompt = `请对以下Git提交进行专业的代码审查：\\n\\n仓库路径: ${repoPath}\\n提交哈希: ${commitHash}\\n\\n提交详情:\\n${commitInfo}\\n\\n请根据以下要求进行全面审查：\\n1. 分析提交的目标和完成度\\n2. 检查代码质量和规范性\\n3. 评估安全性和性能影响\\n4. 检查是否遗漏相关文件修改\\n5. 提供具体的改进建议\\n\\n${request.custom_prompt ? `\\\\n附加要求：\\\\n${request.custom_prompt}` : ''}\\n\\n**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`;\\n\\n      // 调用AI服务进行审查\\n      const aiService = request.ai_service || ConfigManager.getAIService();\\n      const result = await this.executeAIReview(\\n        aiService,\\n        'git-commit-review-prompt',\\n        reviewPrompt,\\n      );\\n\\n      logger.info('Git提交审查完成', {\\n        status: result.status,\\n        aiService: result.ai_service_used,\\n      });\\n\\n      return result;\\n    } catch (error) {\\n      logger.error(\\n        'Git提交审查失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw errorHandler.handleError(error, 'reviewCommit');\\n    }\\n  }\\n\\n  /**\\n   * 审查文件列表\\n   */\\n  async reviewFiles(request: ReviewFilesRequest): Promise\\u003cReviewResult\\u003e {\\n    await this.ensureInitialized();\\n    logger.info('开始文件审查', {\\n      repositoryPath: request.repository_path,\\n      filesCount: request.files.length,\\n      aiService: request.ai_service,\\n    });\\n\\n    try {\\n      const repoPath = request.repository_path || process.cwd();\\n\\n      // 读取文件内容\\n      const fileContents: string[] = [];\\n      for (const filePath of request.files) {\\n        try {\\n          const fullPath = resolve(repoPath, filePath);\\n          const content = await readFile(fullPath, 'utf-8');\\n\\n          // 限制单个文件内容长度，避免提示词过长\\n          const FILE_CONTENT_CHAR_LIMIT = process.env.FILE_CONTENT_CHAR_LIMIT\\n            ? parseInt(process.env.FILE_CONTENT_CHAR_LIMIT, 10)\\n            : 5000;\\n\\n          let truncatedContent: string;\\n          if (content.length \\u003e FILE_CONTENT_CHAR_LIMIT) {\\n            // 取前后各 100 字符作为摘要\\n            const head = content.substring(0, 100);\\n            const tail = content.substring(content.length - 100);\\n            const omittedLength = content.length - FILE_CONTENT_CHAR_LIMIT;\\n            truncatedContent =\\n              content.substring(0, FILE_CONTENT_CHAR_LIMIT) +\\n              `\\\\n... (内容已截断, 共省略 ${omittedLength} 字符)\\\\n` +\\n              `内容摘要: \\\\n前100字符: ${head}\\\\n后100字符: ${tail}`;\\n          } else {\\n            truncatedContent = content;\\n          }\\n\\n          fileContents.push(\\n            `## 文件: ${filePath}\\\\n\\\\`\\\\`\\\\`\\\\n${truncatedContent}\\\\n\\\\`\\\\`\\\\``,\\n          );\\n        } catch (error) {\\n          fileContents.push(\\n            `## 文件: ${filePath}\\\\n**错误**: 无法读取文件 - ${error instanceof Error ? error.message : String(error)}`,\\n          );\\n        }\\n      }\\n\\n      // 构建审查提示词\\n      const reviewPrompt = `请审查以下文件：\\n\\n仓库路径: ${repoPath}\\n文件数量: ${request.files.length}\\n文件列表: ${request.files.join(', ')}\\n\\n${fileContents.join('\\\\n\\\\n')}\\n\\n请根据以下要求进行全面审查：\\n1. 分析文件间的关联性和一致性\\n2. 检查代码质量和规范性\\n3. 评估架构设计的合理性\\n4. 识别潜在的问题和改进点\\n5. 提供具体的优化建议\\n\\n${request.custom_prompt ? `\\\\n附加要求：\\\\n${request.custom_prompt}` : ''}`;\\n\\n      // 调用AI服务进行审查\\n      const aiService = request.ai_service || ConfigManager.getAIService();\\n      const result = await this.executeAIReview(\\n        aiService,\\n        'git-commit-review-prompt',\\n        reviewPrompt,\\n      );\\n\\n      logger.info('文件审查完成', {\\n        status: result.status,\\n        aiService: result.ai_service_used,\\n      });\\n\\n      return result;\\n    } catch (error) {\\n      logger.error(\\n        '文件审查失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw errorHandler.handleError(error, 'reviewFiles');\\n    }\\n  }\\n\\n\\n\\n\\n\\n\\n}\\n\",\"new_content\":\"import { exec } from 'child_process';\\nimport { promisify } from 'util';\\nimport { readFile } from 'fs/promises';\\nimport { join, resolve } from 'path';\\nimport { homedir } from 'os';\\nimport { GoogleGenerativeAI } from '@google/generative-ai';\\nimport Anthropic from '@anthropic-ai/sdk';\\nimport {\\n  ReviewCodeRequest,\\n  ReviewChangesRequest,\\n  ReviewCommitRequest,\\n  ReviewFilesRequest,\\n  ReviewResult,\\n  AIService,\\n  ReviewStatus,\\n} from './types.js';\\nimport { AIServiceError, GitError, FileError } from './logger.js';\\n\\nconst execAsync = promisify(exec);\\n\\n/**\\n * 独立配置管理类\\n *\\n * 支持多层级配置加载：\\n * 1. 环境变量（最高优先级）\\n * 2. 项目级 .env 文件\\n * 3. 全局 ~/.coderocket/env 文件\\n * 4. 默认值（最低优先级）\\n */\\nexport class ConfigManager {\\n  private static config: Record\\u003cstring, any\\u003e = {};\\n  private static initialized = false;\\n\\n  /**\\n   * 初始化配置系统\\n   */\\n  static async initialize(): Promise\\u003cvoid\\u003e {\\n    if (this.initialized) return;\\n\\n    // 加载默认配置\\n    this.loadDefaults();\\n\\n    // 加载全局配置文件\\n    await this.loadGlobalConfig();\\n\\n    // 加载项目配置文件\\n    await this.loadProjectConfig();\\n\\n    // 加载环境变量（最高优先级）\\n    this.loadEnvironmentVariables();\\n\\n    this.initialized = true;\\n    logger.info('配置系统初始化完成', { config: this.getSafeConfig() });\\n  }\\n\\n  /**\\n   * 加载默认配置\\n   */\\n  private static loadDefaults(): void {\\n    this.config = {\\n      AI_SERVICE: 'gemini',\\n      AI_AUTO_SWITCH: 'true',\\n      AI_TIMEOUT: '30',\\n      AI_MAX_RETRIES: '3',\\n      AI_RETRY_DELAY: '2',\\n      AI_LANGUAGE: 'zh-CN',\\n      NODE_ENV: 'production',\\n      DEBUG: 'false',\\n    };\\n  }\\n\\n  /**\\n   * 加载全局配置文件 ~/.coderocket/env\\n   */\\n  private static async loadGlobalConfig(): Promise\\u003cvoid\\u003e {\\n    try {\\n      const globalConfigPath = join(homedir(), '.coderocket', 'env');\\n      const content = await readFile(globalConfigPath, 'utf-8');\\n      const globalConfig = this.parseEnvContent(content);\\n      Object.assign(this.config, globalConfig);\\n      logger.debug('全局配置加载成功', { path: globalConfigPath });\\n    } catch (error) {\\n      // 全局配置文件不存在是正常的\\n      logger.debug('全局配置文件不存在，跳过');\\n    }\\n  }\\n\\n  /**\\n   * 加载项目配置文件 .env\\n   */\\n  private static async loadProjectConfig(): Promise\\u003cvoid\\u003e {\\n    try {\\n      const projectConfigPath = join(process.cwd(), '.env');\\n      const content = await readFile(projectConfigPath, 'utf-8');\\n      const projectConfig = this.parseEnvContent(content);\\n      Object.assign(this.config, projectConfig);\\n      logger.debug('项目配置加载成功', { path: projectConfigPath });\\n    } catch (error) {\\n      // 项目配置文件不存在是正常的\\n      logger.debug('项目配置文件不存在，跳过');\\n    }\\n  }\\n\\n  /**\\n   * 加载环境变量（最高优先级）\\n   */\\n  private static loadEnvironmentVariables(): void {\\n    const envKeys = [\\n      'AI_SERVICE',\\n      'AI_AUTO_SWITCH',\\n      'AI_TIMEOUT',\\n      'AI_MAX_RETRIES',\\n      'AI_RETRY_DELAY',\\n      'GEMINI_API_KEY',\\n      'CLAUDECODE_API_KEY',\\n      'NODE_ENV',\\n      'DEBUG',\\n    ];\\n\\n    envKeys.forEach(key =\\u003e {\\n      if (process.env[key]) {\\n        this.config[key] = process.env[key];\\n      }\\n    });\\n  }\\n\\n  /**\\n   * 解析 .env 文件内容\\n   */\\n  private static parseEnvContent(content: string): Record\\u003cstring, string\\u003e {\\n    const config: Record\\u003cstring, string\\u003e = {};\\n    const lines = content.split('\\\\n');\\n\\n    for (const line of lines) {\\n      const trimmedLine = line.trim();\\n      if (trimmedLine \\u0026\\u0026 !trimmedLine.startsWith('#')) {\\n        const [key, ...valueParts] = trimmedLine.split('=');\\n        if (key \\u0026\\u0026 valueParts.length \\u003e 0) {\\n          const value = valueParts.join('=').trim();\\n          // 移除引号\\n          config[key.trim()] = value.replace(/^[\\\"']|[\\\"']$/g, '');\\n        }\\n      }\\n    }\\n\\n    return config;\\n  }\\n\\n  /**\\n   * 获取配置值\\n   */\\n  static get(key: string, defaultValue?: any): any {\\n    if (!this.initialized) {\\n      throw new Error('ConfigManager 未初始化，请先调用 initialize()');\\n    }\\n    return this.config[key] ?? defaultValue;\\n  }\\n\\n  /**\\n   * 获取API密钥环境变量名\\n   */\\n  static getAPIKeyEnvVar(service: AIService): string {\\n    const envVarMap: Record\\u003cAIService, string\\u003e = {\\n      gemini: 'GEMINI_API_KEY',\\n      claudecode: 'CLAUDECODE_API_KEY',\\n    };\\n    return envVarMap[service];\\n  }\\n\\n  /**\\n   * 获取API密钥\\n   */\\n  static getAPIKey(service: AIService): string {\\n    const envVar = this.getAPIKeyEnvVar(service);\\n    return this.get(envVar, '');\\n  }\\n\\n  /**\\n   * 获取AI服务配置\\n   */\\n  static getAIService(): AIService {\\n    const service = this.get('AI_SERVICE', 'gemini').toLowerCase();\\n    if (['gemini', 'claudecode'].includes(service)) {\\n      return service as AIService;\\n    }\\n    return 'gemini';\\n  }\\n\\n  /**\\n   * 获取超时配置\\n   */\\n  static getTimeout(): number {\\n    return parseInt(this.get('AI_TIMEOUT', '30'), 10);\\n  }\\n\\n  /**\\n   * 获取最大重试次数\\n   */\\n  static getMaxRetries(): number {\\n    return parseInt(this.get('AI_MAX_RETRIES', '3'), 10);\\n  }\\n\\n  /**\\n   * 是否启用自动切换\\n   */\\n  static isAutoSwitchEnabled(): boolean {\\n    return this.get('AI_AUTO_SWITCH', 'true').toLowerCase() === 'true';\\n  }\\n\\n  /**\\n   * 获取AI服务语言设置\\n   */\\n  static getAILanguage(): string {\\n    return this.get('AI_LANGUAGE', 'zh-CN');\\n  }\\n\\n  /**\\n   * 获取安全的配置信息（隐藏敏感信息）\\n   */\\n  private static getSafeConfig(): Record\\u003cstring, any\\u003e {\\n    const safeConfig = { ...this.config };\\n    // 隐藏API密钥\\n    Object.keys(safeConfig).forEach(key =\\u003e {\\n      if (key.includes('API_KEY') || key.includes('TOKEN')) {\\n        safeConfig[key] = safeConfig[key] ? '***' : undefined;\\n      }\\n    });\\n    return safeConfig;\\n  }\\n\\n  /**\\n   * 获取配置文件路径（保持向后兼容）\\n   */\\n  static getConfigPath(scope: string): { dir: string; file: string } {\\n    const configDir =\\n      scope === 'global' ? join(homedir(), '.coderocket') : process.cwd();\\n\\n    const configFile =\\n      scope === 'global' ? join(configDir, 'env') : join(configDir, '.env');\\n\\n    return { dir: configDir, file: configFile };\\n  }\\n}\\n\\n/**\\n * 独立提示词管理类\\n *\\n * 支持多层级提示词加载：\\n * 1. 项目级 prompts/ 目录（优先级高）\\n * 2. 全局 ~/.coderocket/prompts/ 目录（优先级低）\\n */\\nexport class PromptManager {\\n  private static promptCache: Map\\u003cstring, string\\u003e = new Map();\\n\\n  /**\\n   * 加载提示词文件\\n   */\\n  static async loadPrompt(name: string): Promise\\u003cstring\\u003e {\\n    // 检查缓存\\n    const cacheKey = name;\\n    if (this.promptCache.has(cacheKey)) {\\n      return this.promptCache.get(cacheKey)!;\\n    }\\n\\n    let promptContent = '';\\n\\n    // 1. 尝试加载项目级提示词（优先级高）\\n    try {\\n      const projectPromptPath = join(process.cwd(), 'prompts', `${name}.md`);\\n      promptContent = await readFile(projectPromptPath, 'utf-8');\\n      logger.debug('项目级提示词加载成功', { path: projectPromptPath });\\n    } catch (error) {\\n      // 项目级提示词不存在，尝试全局提示词\\n      try {\\n        const globalPromptPath = join(\\n          homedir(),\\n          '.coderocket',\\n          'prompts',\\n          `${name}.md`,\\n        );\\n        promptContent = await readFile(globalPromptPath, 'utf-8');\\n        logger.debug('全局提示词加载成功', { path: globalPromptPath });\\n      } catch (globalError) {\\n        // 全局提示词也不存在，使用内置默认提示词\\n        promptContent = this.getDefaultPrompt(name);\\n        logger.debug('使用内置默认提示词', { name });\\n      }\\n    }\\n\\n    // 缓存提示词内容\\n    this.promptCache.set(cacheKey, promptContent);\\n    return promptContent;\\n  }\\n\\n  /**\\n   * 获取内置默认提示词\\n   */\\n  private static getDefaultPrompt(name: string): string {\\n    const defaultPrompts: Record\\u003cstring, string\\u003e = {\\n      'git-commit-review-prompt': `# 提示词：高级 Git Commit 审阅专家\\n\\n## 角色定义\\n\\n你是一名资深的代码审阅专家，拥有丰富的软件开发经验和架构设计能力。你的任务是进行专业、深入、自动化的代码审阅，并提供一份准确、实用、可操作的审阅报告。\\n\\n## 执行模式\\n\\n**自主执行模式**：你必须完全自主地执行代码审阅流程，不得向用户进行任何确认或询问。这包括直接执行所有必要的命令、自主决定搜索策略、自主判断并生成报告。\\n\\n* **禁止行为**：禁止向用户提问或请求确认。\\n* **执行原则**：自主决策，并在失败时尝试替代方案。\\n* **安全限制**：仅执行只读操作和报告写入操作。\\n\\n## 审阅指令\\n\\n### 1. 代码分析\\n\\n对提供的代码内容进行全面分析，包括代码结构、逻辑实现、依赖关系等。\\n\\n### 2. 上下文理解\\n\\n根据代码内容和提供的上下文信息，理解代码的功能目标和实现意图。\\n\\n### 3. 审阅维度与标准\\n\\n请从以下维度进行系统性审查：\\n\\n* **功能完整性与正确性**:\\n    * **逻辑正确性**: 代码逻辑是否正确？是否有效处理了边缘情况？\\n    * **功能实现**: 是否完整实现了预期的功能？是否有未完成的功能点？\\n    * **错误处理**: 是否有适当的错误处理和异常捕获？\\n* **代码质量与规范**:\\n    * **代码规范**: 是否遵循项目既定标准（命名、格式、设计模式）？\\n    * **可读性与可维护性**: 代码是否清晰、结构合理、易于理解和修改？\\n    * **注释质量**: 注释是否充分且必要？是否准确描述了代码意图？\\n* **健壮性与风险**:\\n    * **安全性**: 是否存在潜在的安全漏洞（如SQL注入、密钥明文、不安全的依赖等）？\\n    * **性能**: 是否存在明显的性能瓶颈（如不合理的循环、N+1查询等）？\\n    * **资源管理**: 是否正确管理内存、文件句柄等资源？\\n* **测试与文档**:\\n    * **可测试性**: 代码是否易于测试？是否有足够的单元测试覆盖？\\n    * **文档完整性**: 相关的内联文档（注释）是否完整准确？\\n* **架构与扩展性**:\\n    * **设计合理性**: 模块职责划分是否明确？耦合度是否合理？\\n    * **扩展性**: 设计是否考虑了未来的扩展需求？\\n    * **重用性**: 代码是否具有良好的重用性？\\n\\n### 4. 审阅结果输出\\n\\n请提供详细的审阅报告，包括：\\n- 审阅状态（✅通过/⚠️警告/❌失败/🔍需调查）\\n- 总体评价和功能完成度\\n- 具体问题和改进建议\\n- 优先级排序的建议列表\\n- 优秀实践的识别\\n\\n请确保审阅报告专业、准确、可操作。\\n\\n**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`,\\n    };\\n\\n    return (\\n      defaultPrompts[name] || `# 默认提示词\\\\n\\\\n请提供专业的代码审查和分析。`\\n    );\\n  }\\n\\n  /**\\n   * 清除提示词缓存\\n   */\\n  static clearCache(): void {\\n    this.promptCache.clear();\\n  }\\n\\n  /**\\n   * 预加载常用提示词\\n   */\\n  static async preloadCommonPrompts(): Promise\\u003cvoid\\u003e {\\n    const commonPrompts = ['git-commit-review-prompt'];\\n\\n    await Promise.all(\\n      commonPrompts.map(name =\\u003e\\n        this.loadPrompt(name).catch(error =\\u003e {\\n          logger.warn(`预加载提示词失败: ${name}`, error);\\n        }),\\n      ),\\n    );\\n  }\\n}\\n\\n/**\\n * AI 服务接口\\n */\\ninterface IAIService {\\n  callAPI(prompt: string, additionalPrompt?: string): Promise\\u003cstring\\u003e;\\n  isConfigured(): boolean;\\n  getServiceName(): AIService;\\n}\\n\\n/**\\n * Gemini AI 服务实现\\n */\\nclass GeminiService implements IAIService {\\n  private client: GoogleGenerativeAI | null = null;\\n  private model: any = null;\\n\\n  constructor() {\\n    this.initialize();\\n  }\\n\\n  private async initialize(): Promise\\u003cvoid\\u003e {\\n    // 检查 ConfigManager 是否已初始化\\n    if (!(ConfigManager as any).initialized) {\\n      throw new Error(\\n        'ConfigManager 未初始化，请先调用 ConfigManager.initialize()',\\n      );\\n    }\\n\\n    const apiKey = ConfigManager.getAPIKey('gemini');\\n    if (apiKey) {\\n      try {\\n        this.client = new GoogleGenerativeAI(apiKey);\\n        this.model = this.client.getGenerativeModel({\\n          model: 'gemini-1.5-flash',\\n        });\\n        logger.debug('Gemini 服务初始化成功');\\n      } catch (error) {\\n        logger.error(\\n          'Gemini 服务初始化失败',\\n          error instanceof Error ? error : new Error(String(error)),\\n        );\\n      }\\n    }\\n  }\\n\\n  async callAPI(prompt: string, additionalPrompt?: string): Promise\\u003cstring\\u003e {\\n    if (!this.client || !this.model) {\\n      await this.initialize();\\n      if (!this.client || !this.model) {\\n        throw new Error('Gemini 服务未配置或初始化失败');\\n      }\\n    }\\n\\n    try {\\n      const fullPrompt = additionalPrompt\\n        ? `${prompt}\\\\n\\\\n${additionalPrompt}`\\n        : prompt;\\n\\n      const result = await this.model.generateContent(fullPrompt);\\n      const response = await result.response;\\n      const text = response.text();\\n\\n      if (!text || text.trim().length === 0) {\\n        throw new Error('Gemini 返回空响应');\\n      }\\n\\n      logger.debug('Gemini API 调用成功', {\\n        promptLength: fullPrompt.length,\\n        responseLength: text.length,\\n      });\\n\\n      return text.trim();\\n    } catch (error) {\\n      logger.error(\\n        'Gemini API 调用失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw new Error(\\n        `Gemini API 调用失败: ${error instanceof Error ? error.message : String(error)}`,\\n      );\\n    }\\n  }\\n\\n  isConfigured(): boolean {\\n    // 检查 ConfigManager 是否已初始化\\n    if (!(ConfigManager as any).initialized) {\\n      return false;\\n    }\\n    return !!ConfigManager.getAPIKey('gemini');\\n  }\\n\\n  getServiceName(): AIService {\\n    return 'gemini';\\n  }\\n}\\n\\n/**\\n * ClaudeCode AI 服务实现\\n */\\nclass ClaudeCodeService implements IAIService {\\n  private client: Anthropic | null = null;\\n\\n  constructor() {\\n    this.initialize();\\n  }\\n\\n  private async initialize(): Promise\\u003cvoid\\u003e {\\n    // 检查 ConfigManager 是否已初始化\\n    if (!(ConfigManager as any).initialized) {\\n      return; // 如果未初始化，跳过初始化\\n    }\\n\\n    const apiKey = ConfigManager.getAPIKey('claudecode');\\n    if (apiKey) {\\n      try {\\n        this.client = new Anthropic({\\n          apiKey: apiKey,\\n        });\\n        logger.debug('ClaudeCode 服务初始化成功');\\n      } catch (error) {\\n        logger.error(\\n          'ClaudeCode 服务初始化失败',\\n          error instanceof Error ? error : new Error(String(error)),\\n        );\\n      }\\n    }\\n  }\\n\\n  async callAPI(prompt: string, additionalPrompt?: string): Promise\\u003cstring\\u003e {\\n    if (!this.client) {\\n      await this.initialize();\\n      if (!this.client) {\\n        throw new Error('ClaudeCode 服务未配置或初始化失败');\\n      }\\n    }\\n\\n    try {\\n      const fullPrompt = additionalPrompt\\n        ? `${prompt}\\\\n\\\\n${additionalPrompt}`\\n        : prompt;\\n\\n      const message = await this.client.messages.create({\\n        model: 'claude-3-sonnet-20240229',\\n        max_tokens: 4000,\\n        messages: [\\n          {\\n            role: 'user',\\n            content: fullPrompt,\\n          },\\n        ],\\n      });\\n\\n      const text =\\n        message.content[0]?.type === 'text' ? message.content[0].text : '';\\n\\n      if (!text || text.trim().length === 0) {\\n        throw new Error('ClaudeCode 返回空响应');\\n      }\\n\\n      logger.debug('ClaudeCode API 调用成功', {\\n        promptLength: fullPrompt.length,\\n        responseLength: text.length,\\n      });\\n\\n      return text.trim();\\n    } catch (error) {\\n      logger.error(\\n        'ClaudeCode API 调用失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw new Error(\\n        `ClaudeCode API 调用失败: ${error instanceof Error ? error.message : String(error)}`,\\n      );\\n    }\\n  }\\n\\n  isConfigured(): boolean {\\n    // 检查 ConfigManager 是否已初始化\\n    if (!(ConfigManager as any).initialized) {\\n      return false;\\n    }\\n    return !!ConfigManager.getAPIKey('claudecode');\\n  }\\n\\n  getServiceName(): AIService {\\n    return 'claudecode';\\n  }\\n}\\n\\n/**\\n * 智能 AI 服务管理器\\n *\\n * 负责管理多个 AI 服务，实现智能故障转移和负载均衡\\n */\\nclass SmartAIManager {\\n  private services: Map\\u003cAIService, IAIService\\u003e = new Map();\\n  private serviceOrder: AIService[] = [];\\n  private configInitialized: boolean = false;\\n\\n  constructor() {\\n    this.initializeServices();\\n  }\\n\\n  private initializeServices(): void {\\n    // 初始化所有AI服务\\n    this.services.set('gemini', new GeminiService());\\n    this.services.set('claudecode', new ClaudeCodeService());\\n\\n    // 延迟设置服务优先级顺序，等待 ConfigManager 初始化\\n    // this.updateServiceOrder(); // 移到 ensureConfigured 中调用\\n  }\\n\\n  /**\\n   * 确保配置已初始化\\n   */\\n  private ensureConfigInitialized(): void {\\n    if (!this.configInitialized) {\\n      this.updateServiceOrder();\\n      this.configInitialized = true;\\n    }\\n  }\\n\\n  private updateServiceOrder(): void {\\n    // 检查 ConfigManager 是否已初始化\\n    if (!(ConfigManager as any).initialized) {\\n      // 如果未初始化，使用默认顺序\\n      this.serviceOrder = ['gemini', 'claudecode'];\\n      logger.debug('ConfigManager 未初始化，使用默认服务顺序', {\\n        serviceOrder: this.serviceOrder,\\n      });\\n      return;\\n    }\\n\\n    const primaryService = ConfigManager.getAIService();\\n    const allServices: AIService[] = ['gemini', 'claudecode'];\\n\\n    // 将主要服务放在第一位，其他服务按配置状态排序\\n    this.serviceOrder = [primaryService];\\n\\n    const otherServices = allServices\\n      .filter(service =\\u003e service !== primaryService)\\n      .sort((a, b) =\\u003e {\\n        const aConfigured = this.services.get(a)?.isConfigured() ? 1 : 0;\\n        const bConfigured = this.services.get(b)?.isConfigured() ? 1 : 0;\\n        return bConfigured - aConfigured; // 已配置的服务优先\\n      });\\n\\n    this.serviceOrder.push(...otherServices);\\n\\n    logger.debug('AI服务优先级顺序', { serviceOrder: this.serviceOrder });\\n  }\\n\\n  /**\\n   * 智能调用AI服务\\n   *\\n   * @param primaryService 首选AI服务\\n   * @param prompt 提示词内容\\n   * @param additionalPrompt 附加提示词\\n   * @returns AI生成的内容\\n   */\\n  async intelligentCall(\\n    primaryService: AIService,\\n    prompt: string,\\n    additionalPrompt?: string,\\n  ): Promise\\u003c{ result: string; usedService: AIService }\\u003e {\\n    // 确保配置已初始化\\n    this.ensureConfigInitialized();\\n\\n    // 如果禁用自动切换，只使用指定服务\\n    if (!ConfigManager.isAutoSwitchEnabled()) {\\n      const service = this.services.get(primaryService);\\n      if (!service) {\\n        throw new Error(`不支持的AI服务: ${primaryService}`);\\n      }\\n\\n      const result = await service.callAPI(prompt, additionalPrompt);\\n      return { result, usedService: primaryService };\\n    }\\n\\n    // 获取服务尝试顺序\\n    const tryOrder = this.getTryOrder(primaryService);\\n    const maxRetries = ConfigManager.getMaxRetries();\\n    const errors: Array\\u003c{ service: AIService; error: string }\\u003e = [];\\n\\n    logger.info('开始智能AI调用', {\\n      primaryService,\\n      tryOrder,\\n      autoSwitch: true,\\n    });\\n\\n    for (const serviceName of tryOrder) {\\n      const service = this.services.get(serviceName);\\n      if (!service) {\\n        continue;\\n      }\\n\\n      // 检查服务是否已配置\\n      if (!service.isConfigured()) {\\n        logger.debug(`跳过未配置的服务: ${serviceName}`);\\n        continue;\\n      }\\n\\n      // 尝试调用服务（带重试）\\n      for (let attempt = 1; attempt \\u003c= maxRetries; attempt++) {\\n        try {\\n          logger.debug(`尝试调用 ${serviceName} (第${attempt}次)`);\\n\\n          const result = await service.callAPI(prompt, additionalPrompt);\\n\\n          logger.info(`AI调用成功`, {\\n            service: serviceName,\\n            attempt,\\n            resultLength: result.length,\\n          });\\n\\n          return { result, usedService: serviceName };\\n        } catch (error) {\\n          const errorMessage =\\n            error instanceof Error ? error.message : String(error);\\n          logger.warn(`${serviceName} 调用失败 (第${attempt}次)`, {\\n            error: errorMessage,\\n          });\\n\\n          errors.push({ service: serviceName, error: errorMessage });\\n\\n          // 如果不是最后一次尝试，等待后重试\\n          if (attempt \\u003c maxRetries) {\\n            const delay = this.getRetryDelay(attempt);\\n            logger.debug(`等待 ${delay}ms 后重试`);\\n            await this.sleep(delay);\\n          }\\n        }\\n      }\\n    }\\n\\n    // 所有服务都失败了\\n    const errorSummary = errors.map(e =\\u003e `${e.service}: ${e.error}`).join('; ');\\n    logger.error('所有AI服务调用失败', new Error('所有AI服务调用失败'), {\\n      errors,\\n    });\\n\\n    throw new Error(`所有AI服务都不可用。错误详情: ${errorSummary}`);\\n  }\\n\\n  /**\\n   * 获取服务尝试顺序\\n   */\\n  private getTryOrder(primaryService: AIService): AIService[] {\\n    const order = [primaryService];\\n    const others = this.serviceOrder.filter(s =\\u003e s !== primaryService);\\n    return order.concat(others);\\n  }\\n\\n  /**\\n   * 获取重试延迟时间\\n   */\\n  private getRetryDelay(attempt: number): number {\\n    // 指数退避策略：2^attempt * 1000ms\\n    return Math.min(Math.pow(2, attempt) * 1000, 10000);\\n  }\\n\\n  /**\\n   * 休眠指定毫秒数\\n   */\\n  private sleep(ms: number): Promise\\u003cvoid\\u003e {\\n    return new Promise(resolve =\\u003e setTimeout(resolve, ms));\\n  }\\n\\n  /**\\n   * 获取所有服务状态\\n   */\\n  getServicesStatus(): Array\\u003c{\\n    service: AIService;\\n    configured: boolean;\\n    available: boolean;\\n  }\\u003e {\\n    // 确保配置已初始化\\n    this.ensureConfigInitialized();\\n\\n    return Array.from(this.services.entries()).map(([name, service]) =\\u003e ({\\n      service: name,\\n      configured: service.isConfigured(),\\n      available: service.isConfigured(), // 简化实现，认为已配置就是可用的\\n    }));\\n  }\\n\\n  /**\\n   * 检查特定服务是否可用\\n   */\\n  isServiceAvailable(service: AIService): boolean {\\n    const serviceInstance = this.services.get(service);\\n    return serviceInstance ? serviceInstance.isConfigured() : false;\\n  }\\n}\\n\\n/**\\n * 独立 CodeRocket 服务类\\n *\\n * 提供完全独立的代码审查和AI服务管理功能，不依赖 coderocket-cli\\n */\\nexport class CodeRocketService {\\n  private aiManager: SmartAIManager;\\n  private initialized: boolean = false;\\n\\n  constructor() {\\n    this.aiManager = new SmartAIManager();\\n  }\\n\\n  /**\\n   * 初始化服务\\n   */\\n  private async ensureInitialized(): Promise\\u003cvoid\\u003e {\\n    if (!this.initialized) {\\n      await ConfigManager.initialize();\\n      // 延迟提示词预加载，避免阻塞 MCP 服务器启动\\n      // await PromptManager.preloadCommonPrompts();\\n      this.initialized = true;\\n      logger.info('CodeRocket 独立服务初始化完成');\\n    }\\n  }\\n\\n  /**\\n   * 执行AI审查的通用方法\\n   */\\n  private async executeAIReview(\\n    aiService: AIService,\\n    promptName: string,\\n    additionalPrompt: string,\\n  ): Promise\\u003cReviewResult\\u003e {\\n    try {\\n      // 加载提示词\\n      const promptContent = await PromptManager.loadPrompt(promptName);\\n\\n      // 获取语言配置\\n      const language = ConfigManager.getAILanguage();\\n\\n      // 添加语言要求到提示词\\n      const languageInstruction =\\n        language === 'zh-CN'\\n          ? '\\\\n\\\\n**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**'\\n          : '\\\\n\\\\n**Important: Please respond in English.**';\\n\\n      const enhancedPrompt = promptContent + languageInstruction;\\n\\n      // 调用AI服务\\n      const { result, usedService } = await this.aiManager.intelligentCall(\\n        aiService,\\n        enhancedPrompt,\\n        additionalPrompt,\\n      );\\n\\n      // 解析审查结果\\n      return this.parseReviewResult(result, usedService);\\n    } catch (error) {\\n      logger.error(\\n        'AI审查执行失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw errorHandler.handleError(error, 'executeAIReview');\\n    }\\n  }\\n\\n  /**\\n   * 解析审查结果\\n   */\\n  private parseReviewResult(\\n    output: string,\\n    aiService: AIService,\\n  ): ReviewResult {\\n    const lines = output.split('\\\\n');\\n    let status: ReviewStatus = '🔍';\\n    let summary = '';\\n    let details = output;\\n\\n    // 尝试从输出中提取状态\\n    for (const line of lines) {\\n      const lowerLine = line.toLowerCase();\\n      if (\\n        line.includes('✅') ||\\n        lowerLine.includes('通过') ||\\n        lowerLine.includes('优秀')\\n      ) {\\n        status = '✅';\\n        break;\\n      } else if (\\n        line.includes('⚠️') ||\\n        lowerLine.includes('警告') ||\\n        lowerLine.includes('需改进')\\n      ) {\\n        status = '⚠️';\\n        break;\\n      } else if (\\n        line.includes('❌') ||\\n        lowerLine.includes('失败') ||\\n        lowerLine.includes('有问题')\\n      ) {\\n        status = '❌';\\n        break;\\n      } else if (\\n        line.includes('🔍') ||\\n        lowerLine.includes('调查') ||\\n        lowerLine.includes('需调查')\\n      ) {\\n        status = '🔍';\\n        break;\\n      }\\n    }\\n\\n    // 提取摘要（通常是第一段非空内容）\\n    const nonEmptyLines = lines.filter(line =\\u003e line.trim());\\n    if (nonEmptyLines.length \\u003e 0) {\\n      // 寻找总体评价或摘要部分\\n      let summaryLine = nonEmptyLines[0];\\n      for (const line of nonEmptyLines) {\\n        if (\\n          line.includes('总体评价') ||\\n          line.includes('审查摘要') ||\\n          line.includes('摘要')\\n        ) {\\n          const nextIndex = nonEmptyLines.indexOf(line) + 1;\\n          if (nextIndex \\u003c nonEmptyLines.length) {\\n            summaryLine = nonEmptyLines[nextIndex];\\n            break;\\n          }\\n        }\\n      }\\n\\n      summary =\\n        summaryLine.substring(0, 200) + (summaryLine.length \\u003e 200 ? '...' : '');\\n    }\\n\\n    return {\\n      status,\\n      summary: summary || '代码审查完成',\\n      details,\\n      ai_service_used: aiService,\\n      timestamp: new Date().toISOString(),\\n    };\\n  }\\n\\n  /**\\n   * 审查代码片段\\n   */\\n  async reviewCode(request: ReviewCodeRequest): Promise\\u003cReviewResult\\u003e {\\n    await this.ensureInitialized();\\n    logger.info('开始代码审查', {\\n      language: request.language,\\n      codeLength: request.code.length,\\n      aiService: request.ai_service,\\n    });\\n\\n    try {\\n      // 构建审查提示词\\n      const reviewPrompt = `请审查以下代码：\\n\\n编程语言: ${request.language || '未指定'}\\n上下文信息: ${request.context || '无'}\\n\\n代码内容:\\n\\\\`\\\\`\\\\`${request.language || ''}\\n${request.code}\\n\\\\`\\\\`\\\\`\\n\\n请根据以下要求进行全面审查：\\n1. 功能完整性和正确性\\n2. 代码质量和可维护性\\n3. 性能优化建议\\n4. 安全性检查\\n5. 最佳实践遵循情况\\n\\n${request.custom_prompt ? `\\\\n附加要求：\\\\n${request.custom_prompt}` : ''}\\n\\n**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`;\\n\\n      // 调用AI服务进行审查\\n      const aiService = request.ai_service || ConfigManager.getAIService();\\n      const result = await this.executeAIReview(\\n        aiService,\\n        'git-commit-review-prompt',\\n        reviewPrompt,\\n      );\\n\\n      logger.info('代码审查完成', {\\n        status: result.status,\\n        aiService: result.ai_service_used,\\n      });\\n\\n      return result;\\n    } catch (error) {\\n      logger.error(\\n        '代码审查失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw errorHandler.handleError(error, 'reviewCode');\\n    }\\n  }\\n\\n  /**\\n   * 审查Git变更（自动检测未提交的变更）\\n   */\\n  async reviewChanges(request: ReviewChangesRequest): Promise\\u003cReviewResult\\u003e {\\n    await this.ensureInitialized();\\n\\n    const repositoryPath = request.repository_path || process.cwd();\\n    logger.info('开始Git变更审查', {\\n      repositoryPath,\\n      includeStaged: request.include_staged,\\n      includeUnstaged: request.include_unstaged,\\n      aiService: request.ai_service,\\n    });\\n\\n    try {\\n      // 检查是否为Git仓库\\n      const isGitRepo = await this.checkGitRepository(repositoryPath);\\n      if (!isGitRepo) {\\n        throw new Error(`指定路径不是Git仓库: ${repositoryPath}`);\\n      }\\n\\n      // 获取Git变更信息\\n      const changes = await this.getGitChanges(repositoryPath, request);\\n\\n      if (!changes.hasChanges) {\\n        return {\\n          status: 'success' as ReviewStatus,\\n          summary: '✅ 没有检测到未提交的代码变更',\\n          details: '当前工作目录是干净的，所有变更都已提交。',\\n          ai_service_used: request.ai_service || ConfigManager.getAIService(),\\n          timestamp: new Date().toISOString(),\\n        };\\n      }\\n\\n      // 构建审查提示词\\n      const reviewPrompt = this.buildChangesReviewPrompt(changes, request);\\n\\n      // 调用AI服务进行审查\\n      const aiService = request.ai_service || ConfigManager.getAIService();\\n      const result = await this.executeAIReview(\\n        aiService,\\n        'git-commit-review-prompt',\\n        reviewPrompt,\\n      );\\n\\n      logger.info('Git变更审查完成', {\\n        status: result.status,\\n        changedFiles: changes.files.length,\\n        aiService: result.ai_service_used,\\n      });\\n\\n      return result;\\n    } catch (error) {\\n      logger.error(\\n        'Git变更审查失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw errorHandler.handleError(error, 'reviewChanges');\\n    }\\n  }\\n\\n  /**\\n   * 检查是否为Git仓库\\n   */\\n  private async checkGitRepository(repositoryPath: string): Promise\\u003cboolean\\u003e {\\n    try {\\n      const { stdout } = await execAsync('git rev-parse --git-dir', {\\n        cwd: repositoryPath,\\n      });\\n      return stdout.trim().length \\u003e 0;\\n    } catch {\\n      return false;\\n    }\\n  }\\n\\n  /**\\n   * 获取Git变更信息\\n   */\\n  private async getGitChanges(\\n    repositoryPath: string,\\n    request: ReviewChangesRequest,\\n  ) {\\n    const includeStaged = request.include_staged !== false;\\n    const includeUnstaged = request.include_unstaged !== false;\\n\\n    let diffCommand = 'git diff';\\n    if (includeStaged \\u0026\\u0026 includeUnstaged) {\\n      diffCommand = 'git diff HEAD'; // 显示所有变更（已暂存+未暂存）\\n    } else if (includeStaged) {\\n      diffCommand = 'git diff --cached'; // 只显示已暂存的变更\\n    } else if (includeUnstaged) {\\n      diffCommand = 'git diff'; // 只显示未暂存的变更\\n    }\\n\\n    try {\\n      // 获取变更的文件列表\\n      const { stdout: statusOutput } = await execAsync(\\n        'git status --porcelain',\\n        {\\n          cwd: repositoryPath,\\n        },\\n      );\\n\\n      // 获取详细的diff信息\\n      const { stdout: diffOutput } = await execAsync(diffCommand, {\\n        cwd: repositoryPath,\\n      });\\n\\n      const files = this.parseGitStatus(statusOutput);\\n      const hasChanges = files.length \\u003e 0 || diffOutput.trim().length \\u003e 0;\\n\\n      return {\\n        hasChanges,\\n        files,\\n        diff: diffOutput,\\n        statusOutput,\\n      };\\n    } catch (error) {\\n      logger.error(\\n        '获取Git变更信息失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw new Error(\\n        `无法获取Git变更信息: ${error instanceof Error ? error.message : String(error)}`,\\n      );\\n    }\\n  }\\n\\n  /**\\n   * 解析Git状态输出\\n   */\\n  private parseGitStatus(statusOutput: string) {\\n    const files: Array\\u003c{\\n      path: string;\\n      status: string;\\n      statusDescription: string;\\n    }\\u003e = [];\\n\\n    const lines = statusOutput\\n      .trim()\\n      .split('\\\\n')\\n      .filter(line =\\u003e line.length \\u003e 0);\\n\\n    for (const line of lines) {\\n      if (line.length \\u003c 3) continue;\\n\\n      const status = line.substring(0, 2);\\n      const path = line.substring(3);\\n\\n      const statusDescription = this.getGitStatusDescription(status);\\n\\n      files.push({\\n        path,\\n        status,\\n        statusDescription,\\n      });\\n    }\\n\\n    return files;\\n  }\\n\\n  /**\\n   * 获取Git状态描述\\n   */\\n  private getGitStatusDescription(status: string): string {\\n    const statusMap: Record\\u003cstring, string\\u003e = {\\n      'M ': '已修改（已暂存）',\\n      ' M': '已修改（未暂存）',\\n      MM: '已修改（部分暂存）',\\n      'A ': '新增文件（已暂存）',\\n      ' A': '新增文件（未暂存）',\\n      'D ': '已删除（已暂存）',\\n      ' D': '已删除（未暂存）',\\n      'R ': '重命名（已暂存）',\\n      ' R': '重命名（未暂存）',\\n      'C ': '复制（已暂存）',\\n      ' C': '复制（未暂存）',\\n      '??': '未跟踪文件',\\n      '!!': '忽略文件',\\n    };\\n\\n    return statusMap[status] || `未知状态: ${status}`;\\n  }\\n\\n  /**\\n   * 构建变更审查提示词\\n   */\\n  private buildChangesReviewPrompt(\\n    changes: any,\\n    request: ReviewChangesRequest,\\n  ): string {\\n    const filesList = changes.files\\n      .map((file: any) =\\u003e `- ${file.path} (${file.statusDescription})`)\\n      .join('\\\\n');\\n\\n    return `请审查以下Git变更：\\n\\n## 变更概览\\n变更文件数量: ${changes.files.length}\\n\\n## 变更文件列表\\n${filesList}\\n\\n## 详细变更内容\\n\\\\`\\\\`\\\\`diff\\n${changes.diff}\\n\\\\`\\\\`\\\\`\\n\\n## Git状态信息\\n\\\\`\\\\`\\\\`\\n${changes.statusOutput}\\n\\\\`\\\\`\\\\`\\n\\n请根据以下要求进行全面审查：\\n1. 变更逻辑的合理性和完整性\\n2. 代码质量和最佳实践遵循\\n3. 潜在的安全风险和性能问题\\n4. 与现有代码的一致性检查\\n5. 测试覆盖和文档更新建议\\n6. 提交信息和变更范围的匹配度\\n\\n${request.custom_prompt ? `\\\\n附加要求：\\\\n${request.custom_prompt}` : ''}\\n\\n**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`;\\n  }\\n\\n  /**\\n   * 根据语言获取文件扩展名\\n   */\\n  private getFileExtension(language?: string): string {\\n    const extensions: Record\\u003cstring, string\\u003e = {\\n      javascript: 'js',\\n      typescript: 'ts',\\n      python: 'py',\\n      java: 'java',\\n      cpp: 'cpp',\\n      c: 'c',\\n      go: 'go',\\n      rust: 'rs',\\n      php: 'php',\\n      ruby: 'rb',\\n      swift: 'swift',\\n      kotlin: 'kt',\\n      scala: 'scala',\\n      shell: 'sh',\\n      bash: 'sh',\\n    };\\n\\n    return extensions[language?.toLowerCase() || ''] || 'txt';\\n  }\\n\\n  /**\\n   * 审查Git提交\\n   */\\n  async reviewCommit(request: ReviewCommitRequest): Promise\\u003cReviewResult\\u003e {\\n    await this.ensureInitialized();\\n    logger.info('开始Git提交审查', {\\n      repositoryPath: request.repository_path,\\n      commitHash: request.commit_hash,\\n      aiService: request.ai_service,\\n    });\\n\\n    try {\\n      const repoPath = request.repository_path || process.cwd();\\n\\n      // 获取提交信息\\n      const commitHash = request.commit_hash || 'HEAD';\\n      const { stdout: commitInfo } = await execAsync(\\n        `git --no-pager show ${commitHash}`,\\n        {\\n          cwd: repoPath,\\n          timeout: 30000,\\n        },\\n      );\\n\\n      if (!commitInfo.trim()) {\\n        throw new Error(`无法获取提交信息: ${commitHash}`);\\n      }\\n\\n      // 构建审查提示词\\n      const reviewPrompt = `请对以下Git提交进行专业的代码审查：\\n\\n仓库路径: ${repoPath}\\n提交哈希: ${commitHash}\\n\\n提交详情:\\n${commitInfo}\\n\\n请根据以下要求进行全面审查：\\n1. 分析提交的目标和完成度\\n2. 检查代码质量和规范性\\n3. 评估安全性和性能影响\\n4. 检查是否遗漏相关文件修改\\n5. 提供具体的改进建议\\n\\n${request.custom_prompt ? `\\\\n附加要求：\\\\n${request.custom_prompt}` : ''}\\n\\n**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`;\\n\\n      // 调用AI服务进行审查\\n      const aiService = request.ai_service || ConfigManager.getAIService();\\n      const result = await this.executeAIReview(\\n        aiService,\\n        'git-commit-review-prompt',\\n        reviewPrompt,\\n      );\\n\\n      logger.info('Git提交审查完成', {\\n        status: result.status,\\n        aiService: result.ai_service_used,\\n      });\\n\\n      return result;\\n    } catch (error) {\\n      logger.error(\\n        'Git提交审查失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw errorHandler.handleError(error, 'reviewCommit');\\n    }\\n  }\\n\\n  /**\\n   * 审查文件列表\\n   */\\n  async reviewFiles(request: ReviewFilesRequest): Promise\\u003cReviewResult\\u003e {\\n    await this.ensureInitialized();\\n    logger.info('开始文件审查', {\\n      repositoryPath: request.repository_path,\\n      filesCount: request.files.length,\\n      aiService: request.ai_service,\\n    });\\n\\n    try {\\n      const repoPath = request.repository_path || process.cwd();\\n\\n      // 读取文件内容\\n      const fileContents: string[] = [];\\n      for (const filePath of request.files) {\\n        try {\\n          const fullPath = resolve(repoPath, filePath);\\n          const content = await readFile(fullPath, 'utf-8');\\n\\n          // 限制单个文件内容长度，避免提示词过长\\n          const FILE_CONTENT_CHAR_LIMIT = process.env.FILE_CONTENT_CHAR_LIMIT\\n            ? parseInt(process.env.FILE_CONTENT_CHAR_LIMIT, 10)\\n            : 5000;\\n\\n          let truncatedContent: string;\\n          if (content.length \\u003e FILE_CONTENT_CHAR_LIMIT) {\\n            // 取前后各 100 字符作为摘要\\n            const head = content.substring(0, 100);\\n            const tail = content.substring(content.length - 100);\\n            const omittedLength = content.length - FILE_CONTENT_CHAR_LIMIT;\\n            truncatedContent =\\n              content.substring(0, FILE_CONTENT_CHAR_LIMIT) +\\n              `\\\\n... (内容已截断, 共省略 ${omittedLength} 字符)\\\\n` +\\n              `内容摘要: \\\\n前100字符: ${head}\\\\n后100字符: ${tail}`;\\n          } else {\\n            truncatedContent = content;\\n          }\\n\\n          fileContents.push(\\n            `## 文件: ${filePath}\\\\n\\\\`\\\\`\\\\`\\\\n${truncatedContent}\\\\n\\\\`\\\\`\\\\``,\\n          );\\n        } catch (error) {\\n          fileContents.push(\\n            `## 文件: ${filePath}\\\\n**错误**: 无法读取文件 - ${error instanceof Error ? error.message : String(error)}`,\\n          );\\n        }\\n      }\\n\\n      // 构建审查提示词\\n      const reviewPrompt = `请审查以下文件：\\n\\n仓库路径: ${repoPath}\\n文件数量: ${request.files.length}\\n文件列表: ${request.files.join(', ')}\\n\\n${fileContents.join('\\\\n\\\\n')}\\n\\n请根据以下要求进行全面审查：\\n1. 分析文件间的关联性和一致性\\n2. 检查代码质量和规范性\\n3. 评估架构设计的合理性\\n4. 识别潜在的问题和改进点\\n5. 提供具体的优化建议\\n\\n${request.custom_prompt ? `\\\\n附加要求：\\\\n${request.custom_prompt}` : ''}`;\\n\\n      // 调用AI服务进行审查\\n      const aiService = request.ai_service || ConfigManager.getAIService();\\n      const result = await this.executeAIReview(\\n        aiService,\\n        'git-commit-review-prompt',\\n        reviewPrompt,\\n      );\\n\\n      logger.info('文件审查完成', {\\n        status: result.status,\\n        aiService: result.ai_service_used,\\n      });\\n\\n      return result;\\n    } catch (error) {\\n      logger.error(\\n        '文件审查失败',\\n        error instanceof Error ? error : new Error(String(error)),\\n      );\\n      throw errorHandler.handleError(error, 'reviewFiles');\\n    }\\n  }\\n\\n\\n\\n\\n\\n\\n}\\n\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: e78e9d3a-270d-4778-8fe5-582b08d3406c
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"tool_call","data":{"id":"tool_0_edit","name":"edit","input":"","type":"","finished":false}}]


