# files

id: 4f217365-1168-4202-b239-24d8cf885237
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
path: /Users/dreambt/sources/coderocket/coderocket-mcp/CRUSH.md
content:
  ## Coderocket MCP `CRUSH.md`

  This file provides agentic coding agents with instructions for operating in this repository.

  ### Commands

  - **Build:** `npm run build`
  - **Lint:** `npm run lint`
  - **Format:** `npm run format`
  - **Test:** `npm run test`
    - To run a single test, you'll need to modify the `test` script in `package.json`. For example: `node dist/test.js --grep "My test name"`
  - **Dev:** `npm run dev`

  ### Code Style

  - **Formatting:** This project uses Prettier for code formatting. Run `npm run format` to format your code.
  - **Imports:**
    - Use ES module imports (`import ... from ...`).
    - Add a `.js` extension to relative imports (e.g., `import { logger } from './logger.js'`).
    - Group imports by type: node built-in, third-party, and then local modules.
  - **Types:**
    - This project uses TypeScript. All new code should have explicit types.
    - Use interfaces for public APIs and types for internal data structures.
    - Use Zod for runtime type validation where necessary.
  - **Naming Conventions:**
    - Use PascalCase for classes and types (e.g., `CodeRocketService`, `ReviewResult`).
    - Use camelCase for functions and variables (e.g., `reviewCode`, `commitHash`).
    - Use UPPER_CASE for constants and environment variables (e.g., `AI_SERVICE`).
  - **Error Handling:**
    - Use `try...catch` blocks for all I/O and API calls.
    - Use the `logger` to log errors.
    - Use the `errorHandler` to handle and normalize errors.
  - **Async/Await:** Use `async/await` for all asynchronous operations.
  - **Comments:** Add JSDoc comments to all public classes and methods.
  - **General:**
    - Favor classes for managing state and related functions.
    - Use helper functions to break up complex logic.
    - Keep functions small and focused on a single task.


id: fb93de38-9e60-4b8a-9710-c386d6c21860
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
path: /Users/dreambt/sources/coderocket/coderocket-mcp/.gitignore
content:
  # Logs
  logs
  *.log
  npm-debug.log*
  yarn-debug.log*
  yarn-error.log*
  lerna-debug.log*

  # Diagnostic reports (https://nodejs.org/api/report.html)
  report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

  # Runtime data
  pids
  *.pid
  *.seed
  *.pid.lock

  # Directory for instrumented libs generated by jscoverage/JSCover
  lib-cov

  # Coverage directory used by tools like istanbul
  coverage
  *.lcov

  # nyc test coverage
  .nyc_output

  # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
  .grunt

  # Bower dependency directory (https://bower.io/)
  bower_components

  # node-waf configuration
  .lock-wscript

  # Compiled binary addons (https://nodejs.org/api/addons.html)
  build/Release

  # Dependency directories
  node_modules/
  jspm_packages/

  # Snowpack dependency directory (https://snowpack.dev/)
  web_modules/

  # TypeScript cache
  *.tsbuildinfo

  # Optional npm cache directory
  .npm

  # Optional eslint cache
  .eslintcache

  # Optional stylelint cache
  .stylelintcache

  # Optional REPL history
  .node_repl_history

  # Output of 'npm pack'
  *.tgz

  # Yarn Integrity file
  .yarn-integrity

  # dotenv environment variable files
  .env
  .env.*
  !.env.example

  # parcel-bundler cache (https://parceljs.org/)
  .cache
  .parcel-cache

  # Next.js build output
  .next
  out

  # Nuxt.js build / generate output
  .nuxt
  dist

  # Gatsby files
  .cache/
  # Comment in the public line in if your project uses Gatsby and not Next.js
  # https://nextjs.org/blog/next-9-1#public-directory-support
  # public

  # vuepress build output
  .vuepress/dist

  # vuepress v2.x temp and cache directory
  .temp
  .cache

  # Sveltekit cache directory
  .svelte-kit/

  # vitepress build output
  **/.vitepress/dist

  # vitepress cache directory
  **/.vitepress/cache

  # Docusaurus cache and generated files
  .docusaurus

  # Serverless directories
  .serverless/

  # FuseBox cache
  .fusebox/

  # DynamoDB Local files
  .dynamodb/

  # Firebase cache directory
  .firebase/

  # TernJS port file
  .tern-port

  # Stores VSCode versions used for testing VSCode extensions
  .vscode-test

  # yarn v3
  .pnp.*
  .yarn/*
  !.yarn/patches
  !.yarn/plugins
  !.yarn/releases
  !.yarn/sdks
  !.yarn/versions

  # Vite logs files
  vite.config.js.timestamp-*
  vite.config.ts.timestamp-*
  .idea/.gitignore
  .idea/coderocket-mcp.iml
  .idea/misc.xml
  .idea/modules.xml
  .idea/vcs.xml
  .idea/inspectionProfiles/Project_Default.xml


id: 76316e9b-78f9-4e89-9e47-bb3ab970b416
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
path: /Users/dreambt/sources/coderocket/coderocket-mcp/.gitignore
content:
  # Logs
  logs
  *.log
  npm-debug.log*
  yarn-debug.log*
  yarn-error.log*
  lerna-debug.log*

  # Diagnostic reports (https://nodejs.org/api/report.html)
  report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

  # Runtime data
  pids
  *.pid
  *.seed
  *.pid.lock

  # Directory for instrumented libs generated by jscoverage/JSCover
  lib-cov

  # Coverage directory used by tools like istanbul
  coverage
  *.lcov

  # nyc test coverage
  .nyc_output

  # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
  .grunt

  # Bower dependency directory (https://bower.io/)
  bower_components

  # node-waf configuration
  .lock-wscript

  # Compiled binary addons (https://nodejs.org/api/addons.html)
  build/Release

  # Dependency directories
  node_modules/
  jspm_packages/

  # Snowpack dependency directory (https://snowpack.dev/)
  web_modules/

  # TypeScript cache
  *.tsbuildinfo

  # Optional npm cache directory
  .npm

  # Optional eslint cache
  .eslintcache

  # Optional stylelint cache
  .stylelintcache

  # Optional REPL history
  .node_repl_history

  # Output of 'npm pack'
  *.tgz

  # Yarn Integrity file
  .yarn-integrity

  # dotenv environment variable files
  .env
  .env.*
  !.env.example

  # parcel-bundler cache (https://parceljs.org/)
  .cache
  .parcel-cache

  # Next.js build output
  .next
  out

  # Nuxt.js build / generate output
  .nuxt
  dist

  # Gatsby files
  .cache/
  # Comment in the public line in if your project uses Gatsby and not Next.js
  # https://nextjs.org/blog/next-9-1#public-directory-support
  # public

  # vuepress build output
  .vuepress/dist

  # vuepress v2.x temp and cache directory
  .temp
  .cache

  # Sveltekit cache directory
  .svelte-kit/

  # vitepress build output
  **/.vitepress/dist

  # vitepress cache directory
  **/.vitepress/cache

  # Docusaurus cache and generated files
  .docusaurus

  # Serverless directories
  .serverless/

  # FuseBox cache
  .fusebox/

  # DynamoDB Local files
  .dynamodb/

  # Firebase cache directory
  .firebase/

  # TernJS port file
  .tern-port

  # Stores VSCode versions used for testing VSCode extensions
  .vscode-test

  # yarn v3
  .pnp.*
  .yarn/*
  !.yarn/patches
  !.yarn/plugins
  !.yarn/releases
  !.yarn/sdks
  !.yarn/versions

  # Vite logs files
  vite.config.js.timestamp-*
  vite.config.ts.timestamp-*
  .idea/.gitignore
  .idea/coderocket-mcp.iml
  .idea/misc.xml
  .idea/modules.xml
  .idea/vcs.xml

  # Crush
  .crush
  .idea/inspectionProfiles/Project_Default.xml


id: e0131e7d-37e3-4bdd-ae8e-7c41fa95ce95
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
path: /Users/dreambt/sources/coderocket/coderocket-mcp/src/index.ts
content:
  #!/usr/bin/env node

  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
  import {
    CallToolRequestSchema,
    ListToolsRequestSchema,
  } from '@modelcontextprotocol/sdk/types.js';
  import { zodToJsonSchema } from 'zod-to-json-schema';
  import { CodeRocketService } from './coderocket.js';
  import {
    ReviewCodeRequestSchema,
    ReviewChangesRequestSchema,
    ReviewCommitRequestSchema,
    ReviewFilesRequestSchema,
  } from './types.js';
  import { toolDefinitions } from './toolDefinitions.js';




  /**
   * CodeRocket MCP Server
   *
   * 提供AI驱动的代码审查功能，集成多种AI服务（Gemini、OpenCode、ClaudeCode）
   * 支持代码片段审查、Git提交审查、文件审查和AI服务管理
   */
  class CodeRocketMCPServer {
    private server: Server;
    private codeRocketService: CodeRocketService | null = null;

    constructor() {
      this.server = new Server(
        {
          name: 'coderocket-mcp',
          version: '1.3.2',
        },
        {
          capabilities: {
            tools: {},
          },
        },
      );

      // 延迟初始化 CodeRocketService，等待 ConfigManager 初始化完成
      this.setupToolHandlers();
    }

    private setupToolHandlers() {
      // 注册工具列表处理器
      this.server.setRequestHandler(ListToolsRequestSchema, async () => {
        return {
          tools: toolDefinitions.map(({ name, description, schema }) => ({
            name,
            description,
            inputSchema: zodToJsonSchema(schema, {
              name: name.replace(/_([a-z])/g, (_, letter) =>
                letter.toUpperCase(),
              ),
              $refStrategy: 'none',
            }),
          })),
        };
      });

      // 注册工具调用处理器
      this.server.setRequestHandler(CallToolRequestSchema, async request => {
        const { name, arguments: args } = request.params;

        // 确保 CodeRocketService 已初始化
        if (!this.codeRocketService) {
          throw new Error('CodeRocketService 未初始化，请稍后重试');
        }

        try {
          switch (name) {
            case 'review_code': {
              const parsedArgs = ReviewCodeRequestSchema.parse(args);
              const result = await this.codeRocketService.reviewCode(parsedArgs);
              return {
                content: [
                  {
                    type: 'text',
                    text: JSON.stringify(result, null, 2),
                  },
                ],
              };
            }

            case 'review_changes': {
              const parsedArgs = ReviewChangesRequestSchema.parse(args);
              const result =
                await this.codeRocketService.reviewChanges(parsedArgs);
              return {
                content: [
                  {
                    type: 'text',
                    text: JSON.stringify(result, null, 2),
                  },
                ],
              };
            }

            case 'review_commit': {
              const parsedArgs = ReviewCommitRequestSchema.parse(args);
              const result =
                await this.codeRocketService.reviewCommit(parsedArgs);
              return {
                content: [
                  {
                    type: 'text',
                    text: JSON.stringify(result, null, 2),
                  },
                ],
              };
            }

            case 'review_files': {
              const parsedArgs = ReviewFilesRequestSchema.parse(args);
              const result = await this.codeRocketService.reviewFiles(parsedArgs);
              return {
                content: [
                  {
                    type: 'text',
                    text: JSON.stringify(result, null, 2),
                  },
                ],
              };
            }

            default:
              throw new Error(`Unknown tool: ${name}`);
          }
        } catch (error) {
          const errorMessage =
            error instanceof Error ? error.message : String(error);
          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify(
                  {
                    error: errorMessage,
                    error_code: 'TOOL_EXECUTION_ERROR',
                    suggestions: [
                      '检查输入参数是否正确',
                      '验证AI服务配置是否正确',
                      '确保API密钥已正确设置',
                      '检查网络连接是否正常',
                    ],
                  },
                  null,
                  2,
                ),
              },
            ],
            isError: true,
          };
        }
      });
    }

    async run() {
      try {
        // 预先初始化配置系统
        const { ConfigManager, CodeRocketService } = await import(
          './coderocket.js'
        );
        await ConfigManager.initialize();

        // 现在可以安全地初始化 CodeRocketService
        this.codeRocketService = new CodeRocketService();

        const transport = new StdioServerTransport();
        await this.server.connect(transport);
      } catch (error) {
        const errorMessage =
          error instanceof Error ? error.message : String(error);
        console.error('❌ CodeRocket MCP 服务器启动失败:', errorMessage);
        process.exit(1);
      }
    }
  }

  // 启动服务器
  const server = new CodeRocketMCPServer();
  server.run().catch(error => {
    console.error('Failed to start server:', error);
    process.exit(1);
  });


id: 702d4a15-409a-49c7-8d11-e3118c1a9196
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
path: /Users/dreambt/sources/coderocket/coderocket-mcp/src/index.ts
content:
  #!/usr/bin/env node

  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
  import {
    CallToolRequestSchema,
    ListToolsRequestSchema,
  } from '@modelcontextprotocol/sdk/types.js';
  import { zodToJsonSchema } from 'zod-to-json-schema';
  import { readFile } from 'fs/promises';
  import { resolve, dirname } from 'path';
  import { fileURLToPath } from 'url';
  import { CodeRocketService } from './coderocket.js';
  import {
    ReviewCodeRequestSchema,
    ReviewChangesRequestSchema,
    ReviewCommitRequestSchema,
    ReviewFilesRequestSchema,
  } from './types.js';
  import { toolDefinitions } from './toolDefinitions.js';

  // 读取版本号
  const getVersion = async (): Promise<string> => {
    try {
      const __dirname = dirname(fileURLToPath(import.meta.url));
      const packageJsonPath = resolve(__dirname, '../package.json');
      const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));
      return packageJson.version || 'unknown';
    } catch (error) {
      console.error('Failed to read version from package.json:', error);
      return 'unknown';
    }
  };

  const VERSION = await getVersion();

  /**
   * CodeRocket MCP Server
   *
   * 提供AI驱动的代码审查功能，集成多种AI服务（Gemini、OpenCode、ClaudeCode）
   * 支持代码片段审查、Git提交审查、文件审查和AI服务管理
   */
  class CodeRocketMCPServer {
    private server: Server;
    private codeRocketService: CodeRocketService | null = null;

    constructor() {
      this.server = new Server(
        {
          name: 'coderocket-mcp',
          version: VERSION,
        },
        {
          capabilities: {
            tools: {},
          },
        },
      );

      // 延迟初始化 CodeRocketService，等待 ConfigManager 初始化完成
      this.setupToolHandlers();
    }

    private setupToolHandlers() {
      // 注册工具列表处理器
      this.server.setRequestHandler(ListToolsRequestSchema, async () => {
        return {
          tools: toolDefinitions.map(({ name, description, schema }) => ({
            name,
            description,
            inputSchema: zodToJsonSchema(schema, {
              name: name.replace(/_([a-z])/g, (_, letter) =>
                letter.toUpperCase(),
              ),
              $refStrategy: 'none',
            }),
          })),
        };
      });

      // 注册工具调用处理器
      this.server.setRequestHandler(CallToolRequestSchema, async request => {
        const { name, arguments: args } = request.params;

        // 确保 CodeRocketService 已初始化
        if (!this.codeRocketService) {
          throw new Error('CodeRocketService 未初始化，请稍后重试');
        }

        try {
          switch (name) {
            case 'review_code': {
              const parsedArgs = ReviewCodeRequestSchema.parse(args);
              const result = await this.codeRocketService.reviewCode(parsedArgs);
              return {
                content: [
                  {
                    type: 'text',
                    text: JSON.stringify(result, null, 2),
                  },
                ],
              };
            }

            case 'review_changes': {
              const parsedArgs = ReviewChangesRequestSchema.parse(args);
              const result =
                await this.codeRocketService.reviewChanges(parsedArgs);
              return {
                content: [
                  {
                    type: 'text',
                    text: JSON.stringify(result, null, 2),
                  },
                ],
              };
            }

            case 'review_commit': {
              const parsedArgs = ReviewCommitRequestSchema.parse(args);
              const result =
                await this.codeRocketService.reviewCommit(parsedArgs);
              return {
                content: [
                  {
                    type: 'text',
                    text: JSON.stringify(result, null, 2),
                  },
                ],
              };
            }

            case 'review_files': {
              const parsedArgs = ReviewFilesRequestSchema.parse(args);
              const result = await this.codeRocketService.reviewFiles(parsedArgs);
              return {
                content: [
                  {
                    type: 'text',
                    text: JSON.stringify(result, null, 2),
                  },
                ],
              };
            }

            default:
              throw new Error(`Unknown tool: ${name}`);
          }
        } catch (error) {
          const errorMessage =
            error instanceof Error ? error.message : String(error);
          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify(
                  {
                    error: errorMessage,
                    error_code: 'TOOL_EXECUTION_ERROR',
                    suggestions: [
                      '检查输入参数是否正确',
                      '验证AI服务配置是否正确',
                      '确保API密钥已正确设置',
                      '检查网络连接是否正常',
                    ],
                  },
                  null,
                  2,
                ),
              },
            ],
            isError: true,
          };
        }
      });
    }

    async run() {
      try {
        // 预先初始化配置系统
        const { ConfigManager, CodeRocketService } = await import(
          './coderocket.js'
        );
        await ConfigManager.initialize();

        // 现在可以安全地初始化 CodeRocketService
        this.codeRocketService = new CodeRocketService();

        const transport = new StdioServerTransport();
        await this.server.connect(transport);
      } catch (error) {
        const errorMessage =
          error instanceof Error ? error.message : String(error);
        console.error('❌ CodeRocket MCP 服务器启动失败:', errorMessage);
        process.exit(1);
      }
    }
  }

  // 启动服务器
  const server = new CodeRocketMCPServer();
  server.run().catch(error => {
    console.error('Failed to start server:', error);
    process.exit(1);
  });


id: 04f2de93-3e4c-4fff-b083-628c397057ae
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
path: /Users/dreambt/sources/coderocket/coderocket-mcp/src/index.ts
content:
  #!/usr/bin/env node

  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
  import {
    CallToolRequestSchema,
    ListToolsRequestSchema,
  } from '@modelcontextprotocol/sdk/types.js';
  import { zodToJsonSchema } from 'zod-to-json-schema';
  import { readFile } from 'fs/promises';
  import { resolve, dirname } from 'path';
  import { fileURLToPath } from 'url';
  import { CodeRocketService } from './coderocket.js';
  import {
    ReviewCodeRequestSchema,
    ReviewChangesRequestSchema,
    ReviewCommitRequestSchema,
    ReviewFilesRequestSchema,
  } from './types.js';
  import { toolDefinitions } from './toolDefinitions.js';

  // 读取版本号
  const getVersion = async (): Promise<string> => {
    try {
      const __dirname = dirname(fileURLToPath(import.meta.url));
      const packageJsonPath = resolve(__dirname, '../package.json');
      const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));
      return packageJson.version || 'unknown';
    } catch (error) {
      console.error('Failed to read version from package.json:', error);
      return 'unknown';
    }
  };

  const VERSION = await getVersion();

  /**
   * CodeRocket MCP Server
   *
   * 提供AI驱动的代码审查功能，集成多种AI服务（Gemini、OpenCode、ClaudeCode）
   * 支持代码片段审查、Git提交审查、文件审查和AI服务管理
   */
  class CodeRocketMCPServer {
    private server: Server;
    private codeRocketService: CodeRocketService | null = null;

    constructor() {
      this.server = new Server(
        {
          name: 'coderocket-mcp',
          version: VERSION,
        },
        {
          capabilities: {
            tools: {},
          },
        },
      );

      // 延迟初始化 CodeRocketService，等待 ConfigManager 初始化完成
      this.setupToolHandlers();
    }

    private setupToolHandlers() {
      // 注册工具列表处理器
      this.server.setRequestHandler(ListToolsRequestSchema, async () => {
        return {
          tools: toolDefinitions.map(({ name, description, schema }) => ({
            name,
            description,
            inputSchema: zodToJsonSchema(schema, {
              name: name.replace(/_([a-z])/g, (_, letter) =>
                letter.toUpperCase(),
              ),
              $refStrategy: 'none',
            }),
          })),
        };
      });

      // 注册工具调用处理器
      this.server.setRequestHandler(CallToolRequestSchema, async request => {
        const { name, arguments: args } = request.params;

        // 确保 CodeRocketService 已初始化
        if (!this.codeRocketService) {
          throw new Error('CodeRocketService 未初始化，请稍后重试');
        }

        // 辅助函数，用于创建和执行工具调用
        const createToolHandler = async (schema: any, serviceMethod: (args: any) => Promise<any>) => {
          const parsedArgs = schema.parse(args);
          const result = await serviceMethod.call(this.codeRocketService, parsedArgs);
          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify(result, null, 2),
              },
            ],
          };
        };

        try {
          switch (name) {
            case 'review_code':
              return await createToolHandler(ReviewCodeRequestSchema, this.codeRocketService.reviewCode);

            case 'review_changes':
              return await createToolHandler(ReviewChangesRequestSchema, this.codeRocketService.reviewChanges);

            case 'review_commit':
              return await createToolHandler(ReviewCommitRequestSchema, this.codeRocketService.reviewCommit);

            case 'review_files':
              return await createToolHandler(ReviewFilesRequestSchema, this.codeRocketService.reviewFiles);

            default:
              throw new Error(`Unknown tool: ${name}`);
          }
        } catch (error) {
          const errorMessage =
            error instanceof Error ? error.message : String(error);
          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify(
                  {
                    error: errorMessage,
                    error_code: 'TOOL_EXECUTION_ERROR',
                    suggestions: [
                      '检查输入参数是否正确',
                      '验证AI服务配置是否正确',
                      '确保API密钥已正确设置',
                      '检查网络连接是否正常',
                    ],
                  },
                  null,
                  2,
                ),
              },
            ],
            isError: true,
          };
        }
      });
    }

    async run() {
      try {
        // 预先初始化配置系统
        const { ConfigManager, CodeRocketService } = await import(
          './coderocket.js'
        );
        await ConfigManager.initialize();

        // 现在可以安全地初始化 CodeRocketService
        this.codeRocketService = new CodeRocketService();

        const transport = new StdioServerTransport();
        await this.server.connect(transport);
      } catch (error) {
        const errorMessage =
          error instanceof Error ? error.message : String(error);
        console.error('❌ CodeRocket MCP 服务器启动失败:', errorMessage);
        process.exit(1);
      }
    }
  }

  // 启动服务器
  const server = new CodeRocketMCPServer();
  server.run().catch(error => {
    console.error('Failed to start server:', error);
    process.exit(1);
  });


id: 99740686-682c-4eb3-a26c-b878f05d683d
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
path: /Users/dreambt/sources/coderocket/coderocket-mcp/src/logger.ts
content:
  import { writeFile, appendFile, mkdir } from 'fs/promises';
  import { join } from 'path';
  import { tmpdir } from 'os';

  /**
   * 日志级别
   */
  export enum LogLevel {
    DEBUG = 0,
    INFO = 1,
    WARN = 2,
    ERROR = 3,
  }

  /**
   * 日志条目接口
   */
  interface LogEntry {
    timestamp: string;
    level: LogLevel;
    message: string;
    context?: Record<string, any>;
    error?: Error;
  }

  /**
   * 简单的日志记录器
   */
  export class Logger {
    private logLevel: LogLevel;
    private logFile?: string;

    constructor(level: LogLevel = LogLevel.INFO, logFile?: string) {
      this.logLevel = level;
      this.logFile = logFile;

      // 如果没有指定日志文件，使用临时目录
      if (!this.logFile) {
        this.logFile = join(tmpdir(), 'coderocket-mcp.log');
      }
    }

    /**
     * 记录调试信息
     */
    debug(message: string, context?: Record<string, any>) {
      this.log(LogLevel.DEBUG, message, context);
    }

    /**
     * 记录信息
     */
    info(message: string, context?: Record<string, any>) {
      this.log(LogLevel.INFO, message, context);
    }

    /**
     * 记录警告
     */
    warn(message: string, context?: Record<string, any>) {
      this.log(LogLevel.WARN, message, context);
    }

    /**
     * 记录错误
     */
    error(message: string, error?: Error, context?: Record<string, any>) {
      this.log(LogLevel.ERROR, message, context, error);
    }

    /**
     * 核心日志记录方法
     */
    private async log(
      level: LogLevel,
      message: string,
      context?: Record<string, any>,
      error?: Error,
    ) {
      if (level < this.logLevel) {
        return;
      }

      const entry: LogEntry = {
        timestamp: new Date().toISOString(),
        level,
        message,
        context,
        error,
      };

      // 在 MCP 服务器模式下，只有在 DEBUG 模式或 WARN/ERROR 级别时才输出到控制台
      // 这避免了 IDE 误认为 INFO 级别的日志是错误信息
      const shouldOutputToConsole =
        process.env.DEBUG === 'true' || level >= LogLevel.WARN;

      if (shouldOutputToConsole) {
        const levelName = LogLevel[level];
        const contextStr = context ? ` ${JSON.stringify(context)}` : '';
        const errorStr = error ? ` Error: ${error.message}` : '';

        // 所有控制台输出都使用 stderr 以避免污染 MCP 协议的 stdout
        console.error(
          `[${entry.timestamp}] ${levelName}: ${message}${contextStr}${errorStr}`,
        );
      }

      // 写入日志文件
      if (this.logFile) {
        try {
          // 处理 Error 对象，避免循环结构
          let logEntry = entry;
          if (entry.error instanceof Error) {
            logEntry = {
              ...entry,
              error: {
                name: entry.error.name,
                message: entry.error.message,
                stack: entry.error.stack,
              },
            };
          }
          const logLine = JSON.stringify(logEntry) + '\n';
          await appendFile(this.logFile, logLine, 'utf-8');
        } catch (fileError) {
          console.error('Failed to write to log file:', fileError);
        }
      }
    }

    /**
     * 获取日志文件路径
     */
    getLogFile(): string | undefined {
      return this.logFile;
    }

    /**
     * 设置日志级别
     */
    setLevel(level: LogLevel) {
      this.logLevel = level;
    }
  }

  // 全局日志实例
  export const logger = new Logger(
    process.env.NODE_ENV === 'development' ? LogLevel.DEBUG : LogLevel.INFO,
  );

  /**
   * 错误处理工具类
   */
  export class ErrorHandler {
    private logger: Logger;

    constructor(logger: Logger) {
      this.logger = logger;
    }

    /**
     * 处理并格式化错误
     */
    handleError(error: unknown, context?: string): Error {
      let formattedError: Error;

      if (error instanceof Error) {
        formattedError = error;
      } else if (typeof error === 'string') {
        formattedError = new Error(error);
      } else {
        formattedError = new Error(`Unknown error: ${JSON.stringify(error)}`);
      }

      // 记录错误
      this.logger.error(
        `${context ? `[${context}] ` : ''}${formattedError.message}`,
        formattedError,
        { context },
      );

      return formattedError;
    }

    /**
     * 包装异步函数，自动处理错误
     */
    wrapAsync<T extends any[], R>(
      fn: (...args: T) => Promise<R>,
      context?: string,
    ): (...args: T) => Promise<R> {
      return async (...args: T): Promise<R> => {
        try {
          return await fn(...args);
        } catch (error) {
          throw this.handleError(error, context);
        }
      };
    }

    /**
     * 创建用户友好的错误消息
     */
    createUserFriendlyError(
      error: Error,
      suggestions?: string[],
    ): {
      error: string;
      error_code: string;
      suggestions: string[];
    } {
      // 根据错误类型提供不同的错误代码和建议
      let errorCode = 'UNKNOWN_ERROR';
      let userSuggestions = suggestions || [];

      if (error.message.includes('Shell command failed')) {
        errorCode = 'SHELL_COMMAND_ERROR';
        userSuggestions = [
          '验证AI服务是否已配置',
          '检查API密钥是否正确设置',
          '确保有足够的权限执行命令',
          ...userSuggestions,
        ];
      } else if (
        error.message.includes('AI服务') ||
        error.message.includes('AI service')
      ) {
        errorCode = 'AI_SERVICE_ERROR';
        userSuggestions = [
          '检查AI服务配置',
          '验证API密钥是否正确',
          '确保网络连接正常',
          '尝试切换到其他AI服务',
          ...userSuggestions,
        ];
      } else if (
        error.message.includes('文件') ||
        error.message.includes('file')
      ) {
        errorCode = 'FILE_ERROR';
        userSuggestions = [
          '检查文件路径是否正确',
          '确保文件存在且可读',
          '验证文件权限',
          ...userSuggestions,
        ];
      } else if (error.message.includes('Git') || error.message.includes('git')) {
        errorCode = 'GIT_ERROR';
        userSuggestions = [
          '确保在Git仓库中执行',
          '检查Git仓库状态',
          '验证提交哈希是否存在',
          ...userSuggestions,
        ];
      }

      return {
        error: error.message,
        error_code: errorCode,
        suggestions: userSuggestions,
      };
    }
  }

  // 全局错误处理器实例
  export const errorHandler = new ErrorHandler(logger);


id: 99a705df-d567-4f1b-9c4d-ce7674cdacac
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
path: /Users/dreambt/sources/coderocket/coderocket-mcp/src/logger.ts
content:
  import { writeFile, appendFile, mkdir } from 'fs/promises';
  import { join } from 'path';
  import { tmpdir } from 'os';

  // 自定义错误类型
  export class AIServiceError extends Error {
    constructor(message: string) {
      super(message);
      this.name = 'AIServiceError';
    }
  }

  export class GitError extends Error {
    constructor(message: string) {
      super(message);
      this.name = 'GitError';
    }
  }

  export class FileError extends Error {
    constructor(message: string) {
      super(message);
      this.name = 'FileError';
    }
  }

  /**
   * 日志级别
   */
  export enum LogLevel {
    DEBUG = 0,
    INFO = 1,
    WARN = 2,
    ERROR = 3,
  }

  /**
   * 日志条目接口
   */
  interface LogEntry {
    timestamp: string;
    level: LogLevel;
    message: string;
    context?: Record<string, any>;
    error?: Error;
  }

  /**
   * 简单的日志记录器
   */
  export class Logger {
    private logLevel: LogLevel;
    private logFile?: string;

    constructor(level: LogLevel = LogLevel.INFO, logFile?: string) {
      this.logLevel = level;
      this.logFile = logFile;

      // 如果没有指定日志文件，使用临时目录
      if (!this.logFile) {
        this.logFile = join(tmpdir(), 'coderocket-mcp.log');
      }
    }

    /**
     * 记录调试信息
     */
    debug(message: string, context?: Record<string, any>) {
      this.log(LogLevel.DEBUG, message, context);
    }

    /**
     * 记录信息
     */
    info(message: string, context?: Record<string, any>) {
      this.log(LogLevel.INFO, message, context);
    }

    /**
     * 记录警告
     */
    warn(message: string, context?: Record<string, any>) {
      this.log(LogLevel.WARN, message, context);
    }

    /**
     * 记录错误
     */
    error(message: string, error?: Error, context?: Record<string, any>) {
      this.log(LogLevel.ERROR, message, context, error);
    }

    /**
     * 核心日志记录方法
     */
    private async log(
      level: LogLevel,
      message: string,
      context?: Record<string, any>,
      error?: Error,
    ) {
      if (level < this.logLevel) {
        return;
      }

      const entry: LogEntry = {
        timestamp: new Date().toISOString(),
        level,
        message,
        context,
        error,
      };

      // 在 MCP 服务器模式下，只有在 DEBUG 模式或 WARN/ERROR 级别时才输出到控制台
      // 这避免了 IDE 误认为 INFO 级别的日志是错误信息
      const shouldOutputToConsole =
        process.env.DEBUG === 'true' || level >= LogLevel.WARN;

      if (shouldOutputToConsole) {
        const levelName = LogLevel[level];
        const contextStr = context ? ` ${JSON.stringify(context)}` : '';
        const errorStr = error ? ` Error: ${error.message}` : '';

        // 所有控制台输出都使用 stderr 以避免污染 MCP 协议的 stdout
        console.error(
          `[${entry.timestamp}] ${levelName}: ${message}${contextStr}${errorStr}`,
        );
      }

      // 写入日志文件
      if (this.logFile) {
        try {
          // 处理 Error 对象，避免循环结构
          let logEntry = entry;
          if (entry.error instanceof Error) {
            logEntry = {
              ...entry,
              error: {
                name: entry.error.name,
                message: entry.error.message,
                stack: entry.error.stack,
              },
            };
          }
          const logLine = JSON.stringify(logEntry) + '\n';
          await appendFile(this.logFile, logLine, 'utf-8');
        } catch (fileError) {
          console.error('Failed to write to log file:', fileError);
        }
      }
    }

    /**
     * 获取日志文件路径
     */
    getLogFile(): string | undefined {
      return this.logFile;
    }

    /**
     * 设置日志级别
     */
    setLevel(level: LogLevel) {
      this.logLevel = level;
    }
  }

  // 全局日志实例
  export const logger = new Logger(
    process.env.NODE_ENV === 'development' ? LogLevel.DEBUG : LogLevel.INFO,
  );

  /**
   * 错误处理工具类
   */
  export class ErrorHandler {
    private logger: Logger;

    constructor(logger: Logger) {
      this.logger = logger;
    }

    /**
     * 处理并格式化错误
     */
    handleError(error: unknown, context?: string): Error {
      let formattedError: Error;

      if (error instanceof Error) {
        formattedError = error;
      } else if (typeof error === 'string') {
        formattedError = new Error(error);
      } else {
        formattedError = new Error(`Unknown error: ${JSON.stringify(error)}`);
      }

      // 记录错误
      this.logger.error(
        `${context ? `[${context}] ` : ''}${formattedError.message}`,
        formattedError,
        { context },
      );

      return formattedError;
    }

    /**
     * 包装异步函数，自动处理错误
     */
    wrapAsync<T extends any[], R>(
      fn: (...args: T) => Promise<R>,
      context?: string,
    ): (...args: T) => Promise<R> {
      return async (...args: T): Promise<R> => {
        try {
          return await fn(...args);
        } catch (error) {
          throw this.handleError(error, context);
        }
      };
    }

    /**
     * 创建用户友好的错误消息
     */
    createUserFriendlyError(
      error: Error,
      suggestions?: string[],
    ): {
      error: string;
      error_code: string;
      suggestions: string[];
    } {
      // 根据错误类型提供不同的错误代码和建议
      let errorCode = 'UNKNOWN_ERROR';
      let userSuggestions = suggestions || [];

      if (error.message.includes('Shell command failed')) {
        errorCode = 'SHELL_COMMAND_ERROR';
        userSuggestions = [
          '验证AI服务是否已配置',
          '检查API密钥是否正确设置',
          '确保有足够的权限执行命令',
          ...userSuggestions,
        ];
      } else if (
        error.message.includes('AI服务') ||
        error.message.includes('AI service')
      ) {
        errorCode = 'AI_SERVICE_ERROR';
        userSuggestions = [
          '检查AI服务配置',
          '验证API密钥是否正确',
          '确保网络连接正常',
          '尝试切换到其他AI服务',
          ...userSuggestions,
        ];
      } else if (
        error.message.includes('文件') ||
        error.message.includes('file')
      ) {
        errorCode = 'FILE_ERROR';
        userSuggestions = [
          '检查文件路径是否正确',
          '确保文件存在且可读',
          '验证文件权限',
          ...userSuggestions,
        ];
      } else if (error.message.includes('Git') || error.message.includes('git')) {
        errorCode = 'GIT_ERROR';
        userSuggestions = [
          '确保在Git仓库中执行',
          '检查Git仓库状态',
          '验证提交哈希是否存在',
          ...userSuggestions,
        ];
      }

      return {
        error: error.message,
        error_code: errorCode,
        suggestions: userSuggestions,
      };
    }
  }

  // 全局错误处理器实例
  export const errorHandler = new ErrorHandler(logger);


id: c9b00f04-9589-4956-8c78-8eda02a5d0f7
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
path: /Users/dreambt/sources/coderocket/coderocket-mcp/src/logger.ts
content:
  import { writeFile, appendFile, mkdir } from 'fs/promises';
  import { join } from 'path';
  import { tmpdir } from 'os';

  // 自定义错误类型
  export class AIServiceError extends Error {
    constructor(message: string) {
      super(message);
      this.name = 'AIServiceError';
    }
  }

  export class GitError extends Error {
    constructor(message: string) {
      super(message);
      this.name = 'GitError';
    }
  }

  export class FileError extends Error {
    constructor(message: string) {
      super(message);
      this.name = 'FileError';
    }
  }

  /**
   * 日志级别
   */
  export enum LogLevel {
    DEBUG = 0,
    INFO = 1,
    WARN = 2,
    ERROR = 3,
  }

  /**
   * 日志条目接口
   */
  interface LogEntry {
    timestamp: string;
    level: LogLevel;
    message: string;
    context?: Record<string, any>;
    error?: Error;
  }

  /**
   * 简单的日志记录器
   */
  export class Logger {
    private logLevel: LogLevel;
    private logFile?: string;

    constructor(level: LogLevel = LogLevel.INFO, logFile?: string) {
      this.logLevel = level;
      this.logFile = logFile;

      // 如果没有指定日志文件，使用临时目录
      if (!this.logFile) {
        this.logFile = join(tmpdir(), 'coderocket-mcp.log');
      }
    }

    /**
     * 记录调试信息
     */
    debug(message: string, context?: Record<string, any>) {
      this.log(LogLevel.DEBUG, message, context);
    }

    /**
     * 记录信息
     */
    info(message: string, context?: Record<string, any>) {
      this.log(LogLevel.INFO, message, context);
    }

    /**
     * 记录警告
     */
    warn(message: string, context?: Record<string, any>) {
      this.log(LogLevel.WARN, message, context);
    }

    /**
     * 记录错误
     */
    error(message: string, error?: Error, context?: Record<string, any>) {
      this.log(LogLevel.ERROR, message, context, error);
    }

    /**
     * 核心日志记录方法
     */
    private async log(
      level: LogLevel,
      message: string,
      context?: Record<string, any>,
      error?: Error,
    ) {
      if (level < this.logLevel) {
        return;
      }

      const entry: LogEntry = {
        timestamp: new Date().toISOString(),
        level,
        message,
        context,
        error,
      };

      // 在 MCP 服务器模式下，只有在 DEBUG 模式或 WARN/ERROR 级别时才输出到控制台
      // 这避免了 IDE 误认为 INFO 级别的日志是错误信息
      const shouldOutputToConsole =
        process.env.DEBUG === 'true' || level >= LogLevel.WARN;

      if (shouldOutputToConsole) {
        const levelName = LogLevel[level];
        const contextStr = context ? ` ${JSON.stringify(context)}` : '';
        const errorStr = error ? ` Error: ${error.message}` : '';

        // 所有控制台输出都使用 stderr 以避免污染 MCP 协议的 stdout
        console.error(
          `[${entry.timestamp}] ${levelName}: ${message}${contextStr}${errorStr}`,
        );
      }

      // 写入日志文件
      if (this.logFile) {
        try {
          // 处理 Error 对象，避免循环结构
          let logEntry = entry;
          if (entry.error instanceof Error) {
            logEntry = {
              ...entry,
              error: {
                name: entry.error.name,
                message: entry.error.message,
                stack: entry.error.stack,
              },
            };
          }
          const logLine = JSON.stringify(logEntry) + '\n';
          await appendFile(this.logFile, logLine, 'utf-8');
        } catch (fileError) {
          console.error('Failed to write to log file:', fileError);
        }
      }
    }

    /**
     * 获取日志文件路径
     */
    getLogFile(): string | undefined {
      return this.logFile;
    }

    /**
     * 设置日志级别
     */
    setLevel(level: LogLevel) {
      this.logLevel = level;
    }
  }

  // 全局日志实例
  export const logger = new Logger(
    process.env.NODE_ENV === 'development' ? LogLevel.DEBUG : LogLevel.INFO,
  );

  /**
   * 错误处理工具类
   */
  export class ErrorHandler {
    private logger: Logger;

    constructor(logger: Logger) {
      this.logger = logger;
    }

    /**
     * 处理并格式化错误
     */
    handleError(error: unknown, context?: string): Error {
      let formattedError: Error;

      if (error instanceof Error) {
        formattedError = error;
      } else if (typeof error === 'string') {
        formattedError = new Error(error);
      } else {
        formattedError = new Error(`Unknown error: ${JSON.stringify(error)}`);
      }

      // 记录错误
      this.logger.error(
        `${context ? `[${context}] ` : ''}${formattedError.message}`,
        formattedError,
        { context },
      );

      return formattedError;
    }

    /**
     * 包装异步函数，自动处理错误
     */
    wrapAsync<T extends any[], R>(
      fn: (...args: T) => Promise<R>,
      context?: string,
    ): (...args: T) => Promise<R> {
      return async (...args: T): Promise<R> => {
        try {
          return await fn(...args);
        } catch (error) {
          throw this.handleError(error, context);
        }
      };
    }

    /**
     * 创建用户友好的错误消息
     */
    createUserFriendlyError(
      error: Error,
      suggestions?: string[],
    ): {
      error: string;
      error_code: string;
      suggestions: string[];
    } {
      // 根据错误类型提供不同的错误代码和建议
      let errorCode = 'UNKNOWN_ERROR';
      let userSuggestions = suggestions || [];

      if (error instanceof GitError) {
        errorCode = 'GIT_ERROR';
        userSuggestions = [
          '确保在Git仓库中执行',
          '检查Git仓库状态',
          '验证提交哈希是否存在',
          ...userSuggestions,
        ];
      } else if (error instanceof AIServiceError) {
        errorCode = 'AI_SERVICE_ERROR';
        userSuggestions = [
          '检查AI服务配置',
          '验证API密钥是否正确',
          '确保网络连接正常',
          '尝试切换到其他AI服务',
          ...userSuggestions,
        ];
      } else if (error instanceof FileError) {
        errorCode = 'FILE_ERROR';
        userSuggestions = [
          '检查文件路径是否正确',
          '确保文件存在且可读',
          '验证文件权限',
          ...userSuggestions,
        ];
      } else if (error.message.includes('Shell command failed')) {
        errorCode = 'SHELL_COMMAND_ERROR';
        userSuggestions = [
          '验证AI服务是否已配置',
          '检查API密钥是否正确',
          '确保有足够的权限执行命令',
          ...userSuggestions,
        ];
      }

      return {
        error: error.message,
        error_code: errorCode,
        suggestions: userSuggestions,
      };
    }
  }

  // 全局错误处理器实例
  export const errorHandler = new ErrorHandler(logger);


id: cd349b8d-3838-451d-8fef-6bbaf7abbe0f
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
path: /Users/dreambt/sources/coderocket/coderocket-mcp/src/coderocket.ts
content:
  import { exec } from 'child_process';
  import { promisify } from 'util';
  import { readFile } from 'fs/promises';
  import { join, resolve } from 'path';
  import { homedir } from 'os';
  import { GoogleGenerativeAI } from '@google/generative-ai';
  import Anthropic from '@anthropic-ai/sdk';
  import {
    ReviewCodeRequest,
    ReviewChangesRequest,
    ReviewCommitRequest,
    ReviewFilesRequest,
    ReviewResult,
    AIService,
    ReviewStatus,
  } from './types.js';
  import { logger, errorHandler } from './logger.js';

  const execAsync = promisify(exec);

  /**
   * 独立配置管理类
   *
   * 支持多层级配置加载：
   * 1. 环境变量（最高优先级）
   * 2. 项目级 .env 文件
   * 3. 全局 ~/.coderocket/env 文件
   * 4. 默认值（最低优先级）
   */
  export class ConfigManager {
    private static config: Record<string, any> = {};
    private static initialized = false;

    /**
     * 初始化配置系统
     */
    static async initialize(): Promise<void> {
      if (this.initialized) return;

      // 加载默认配置
      this.loadDefaults();

      // 加载全局配置文件
      await this.loadGlobalConfig();

      // 加载项目配置文件
      await this.loadProjectConfig();

      // 加载环境变量（最高优先级）
      this.loadEnvironmentVariables();

      this.initialized = true;
      logger.info('配置系统初始化完成', { config: this.getSafeConfig() });
    }

    /**
     * 加载默认配置
     */
    private static loadDefaults(): void {
      this.config = {
        AI_SERVICE: 'gemini',
        AI_AUTO_SWITCH: 'true',
        AI_TIMEOUT: '30',
        AI_MAX_RETRIES: '3',
        AI_RETRY_DELAY: '2',
        AI_LANGUAGE: 'zh-CN',
        NODE_ENV: 'production',
        DEBUG: 'false',
      };
    }

    /**
     * 加载全局配置文件 ~/.coderocket/env
     */
    private static async loadGlobalConfig(): Promise<void> {
      try {
        const globalConfigPath = join(homedir(), '.coderocket', 'env');
        const content = await readFile(globalConfigPath, 'utf-8');
        const globalConfig = this.parseEnvContent(content);
        Object.assign(this.config, globalConfig);
        logger.debug('全局配置加载成功', { path: globalConfigPath });
      } catch (error) {
        // 全局配置文件不存在是正常的
        logger.debug('全局配置文件不存在，跳过');
      }
    }

    /**
     * 加载项目配置文件 .env
     */
    private static async loadProjectConfig(): Promise<void> {
      try {
        const projectConfigPath = join(process.cwd(), '.env');
        const content = await readFile(projectConfigPath, 'utf-8');
        const projectConfig = this.parseEnvContent(content);
        Object.assign(this.config, projectConfig);
        logger.debug('项目配置加载成功', { path: projectConfigPath });
      } catch (error) {
        // 项目配置文件不存在是正常的
        logger.debug('项目配置文件不存在，跳过');
      }
    }

    /**
     * 加载环境变量（最高优先级）
     */
    private static loadEnvironmentVariables(): void {
      const envKeys = [
        'AI_SERVICE',
        'AI_AUTO_SWITCH',
        'AI_TIMEOUT',
        'AI_MAX_RETRIES',
        'AI_RETRY_DELAY',
        'GEMINI_API_KEY',
        'CLAUDECODE_API_KEY',
        'NODE_ENV',
        'DEBUG',
      ];

      envKeys.forEach(key => {
        if (process.env[key]) {
          this.config[key] = process.env[key];
        }
      });
    }

    /**
     * 解析 .env 文件内容
     */
    private static parseEnvContent(content: string): Record<string, string> {
      const config: Record<string, string> = {};
      const lines = content.split('\n');

      for (const line of lines) {
        const trimmedLine = line.trim();
        if (trimmedLine && !trimmedLine.startsWith('#')) {
          const [key, ...valueParts] = trimmedLine.split('=');
          if (key && valueParts.length > 0) {
            const value = valueParts.join('=').trim();
            // 移除引号
            config[key.trim()] = value.replace(/^["']|["']$/g, '');
          }
        }
      }

      return config;
    }

    /**
     * 获取配置值
     */
    static get(key: string, defaultValue?: any): any {
      if (!this.initialized) {
        throw new Error('ConfigManager 未初始化，请先调用 initialize()');
      }
      return this.config[key] ?? defaultValue;
    }

    /**
     * 获取API密钥环境变量名
     */
    static getAPIKeyEnvVar(service: AIService): string {
      const envVarMap: Record<AIService, string> = {
        gemini: 'GEMINI_API_KEY',
        claudecode: 'CLAUDECODE_API_KEY',
      };
      return envVarMap[service];
    }

    /**
     * 获取API密钥
     */
    static getAPIKey(service: AIService): string {
      const envVar = this.getAPIKeyEnvVar(service);
      return this.get(envVar, '');
    }

    /**
     * 获取AI服务配置
     */
    static getAIService(): AIService {
      const service = this.get('AI_SERVICE', 'gemini').toLowerCase();
      if (['gemini', 'claudecode'].includes(service)) {
        return service as AIService;
      }
      return 'gemini';
    }

    /**
     * 获取超时配置
     */
    static getTimeout(): number {
      return parseInt(this.get('AI_TIMEOUT', '30'), 10);
    }

    /**
     * 获取最大重试次数
     */
    static getMaxRetries(): number {
      return parseInt(this.get('AI_MAX_RETRIES', '3'), 10);
    }

    /**
     * 是否启用自动切换
     */
    static isAutoSwitchEnabled(): boolean {
      return this.get('AI_AUTO_SWITCH', 'true').toLowerCase() === 'true';
    }

    /**
     * 获取AI服务语言设置
     */
    static getAILanguage(): string {
      return this.get('AI_LANGUAGE', 'zh-CN');
    }

    /**
     * 获取安全的配置信息（隐藏敏感信息）
     */
    private static getSafeConfig(): Record<string, any> {
      const safeConfig = { ...this.config };
      // 隐藏API密钥
      Object.keys(safeConfig).forEach(key => {
        if (key.includes('API_KEY') || key.includes('TOKEN')) {
          safeConfig[key] = safeConfig[key] ? '***' : undefined;
        }
      });
      return safeConfig;
    }

    /**
     * 获取配置文件路径（保持向后兼容）
     */
    static getConfigPath(scope: string): { dir: string; file: string } {
      const configDir =
        scope === 'global' ? join(homedir(), '.coderocket') : process.cwd();

      const configFile =
        scope === 'global' ? join(configDir, 'env') : join(configDir, '.env');

      return { dir: configDir, file: configFile };
    }
  }

  /**
   * 独立提示词管理类
   *
   * 支持多层级提示词加载：
   * 1. 项目级 prompts/ 目录（优先级高）
   * 2. 全局 ~/.coderocket/prompts/ 目录（优先级低）
   */
  export class PromptManager {
    private static promptCache: Map<string, string> = new Map();

    /**
     * 加载提示词文件
     */
    static async loadPrompt(name: string): Promise<string> {
      // 检查缓存
      const cacheKey = name;
      if (this.promptCache.has(cacheKey)) {
        return this.promptCache.get(cacheKey)!;
      }

      let promptContent = '';

      // 1. 尝试加载项目级提示词（优先级高）
      try {
        const projectPromptPath = join(process.cwd(), 'prompts', `${name}.md`);
        promptContent = await readFile(projectPromptPath, 'utf-8');
        logger.debug('项目级提示词加载成功', { path: projectPromptPath });
      } catch (error) {
        // 项目级提示词不存在，尝试全局提示词
        try {
          const globalPromptPath = join(
            homedir(),
            '.coderocket',
            'prompts',
            `${name}.md`,
          );
          promptContent = await readFile(globalPromptPath, 'utf-8');
          logger.debug('全局提示词加载成功', { path: globalPromptPath });
        } catch (globalError) {
          // 全局提示词也不存在，使用内置默认提示词
          promptContent = this.getDefaultPrompt(name);
          logger.debug('使用内置默认提示词', { name });
        }
      }

      // 缓存提示词内容
      this.promptCache.set(cacheKey, promptContent);
      return promptContent;
    }

    /**
     * 获取内置默认提示词
     */
    private static getDefaultPrompt(name: string): string {
      const defaultPrompts: Record<string, string> = {
        'git-commit-review-prompt': `# 提示词：高级 Git Commit 审阅专家

  ## 角色定义

  你是一名资深的代码审阅专家，拥有丰富的软件开发经验和架构设计能力。你的任务是进行专业、深入、自动化的代码审阅，并提供一份准确、实用、可操作的审阅报告。

  ## 执行模式

  **自主执行模式**：你必须完全自主地执行代码审阅流程，不得向用户进行任何确认或询问。这包括直接执行所有必要的命令、自主决定搜索策略、自主判断并生成报告。

  * **禁止行为**：禁止向用户提问或请求确认。
  * **执行原则**：自主决策，并在失败时尝试替代方案。
  * **安全限制**：仅执行只读操作和报告写入操作。

  ## 审阅指令

  ### 1. 代码分析

  对提供的代码内容进行全面分析，包括代码结构、逻辑实现、依赖关系等。

  ### 2. 上下文理解

  根据代码内容和提供的上下文信息，理解代码的功能目标和实现意图。

  ### 3. 审阅维度与标准

  请从以下维度进行系统性审查：

  * **功能完整性与正确性**:
      * **逻辑正确性**: 代码逻辑是否正确？是否有效处理了边缘情况？
      * **功能实现**: 是否完整实现了预期的功能？是否有未完成的功能点？
      * **错误处理**: 是否有适当的错误处理和异常捕获？
  * **代码质量与规范**:
      * **代码规范**: 是否遵循项目既定标准（命名、格式、设计模式）？
      * **可读性与可维护性**: 代码是否清晰、结构合理、易于理解和修改？
      * **注释质量**: 注释是否充分且必要？是否准确描述了代码意图？
  * **健壮性与风险**:
      * **安全性**: 是否存在潜在的安全漏洞（如SQL注入、密钥明文、不安全的依赖等）？
      * **性能**: 是否存在明显的性能瓶颈（如不合理的循环、N+1查询等）？
      * **资源管理**: 是否正确管理内存、文件句柄等资源？
  * **测试与文档**:
      * **可测试性**: 代码是否易于测试？是否有足够的单元测试覆盖？
      * **文档完整性**: 相关的内联文档（注释）是否完整准确？
  * **架构与扩展性**:
      * **设计合理性**: 模块职责划分是否明确？耦合度是否合理？
      * **扩展性**: 设计是否考虑了未来的扩展需求？
      * **重用性**: 代码是否具有良好的重用性？

  ### 4. 审阅结果输出

  请提供详细的审阅报告，包括：
  - 审阅状态（✅通过/⚠️警告/❌失败/🔍需调查）
  - 总体评价和功能完成度
  - 具体问题和改进建议
  - 优先级排序的建议列表
  - 优秀实践的识别

  请确保审阅报告专业、准确、可操作。

  **重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`,
      };

      return (
        defaultPrompts[name] || `# 默认提示词\n\n请提供专业的代码审查和分析。`
      );
    }

    /**
     * 清除提示词缓存
     */
    static clearCache(): void {
      this.promptCache.clear();
    }

    /**
     * 预加载常用提示词
     */
    static async preloadCommonPrompts(): Promise<void> {
      const commonPrompts = ['git-commit-review-prompt'];

      await Promise.all(
        commonPrompts.map(name =>
          this.loadPrompt(name).catch(error => {
            logger.warn(`预加载提示词失败: ${name}`, error);
          }),
        ),
      );
    }
  }

  /**
   * AI 服务接口
   */
  interface IAIService {
    callAPI(prompt: string, additionalPrompt?: string): Promise<string>;
    isConfigured(): boolean;
    getServiceName(): AIService;
  }

  /**
   * Gemini AI 服务实现
   */
  class GeminiService implements IAIService {
    private client: GoogleGenerativeAI | null = null;
    private model: any = null;

    constructor() {
      this.initialize();
    }

    private async initialize(): Promise<void> {
      // 检查 ConfigManager 是否已初始化
      if (!(ConfigManager as any).initialized) {
        throw new Error(
          'ConfigManager 未初始化，请先调用 ConfigManager.initialize()',
        );
      }

      const apiKey = ConfigManager.getAPIKey('gemini');
      if (apiKey) {
        try {
          this.client = new GoogleGenerativeAI(apiKey);
          this.model = this.client.getGenerativeModel({
            model: 'gemini-1.5-flash',
          });
          logger.debug('Gemini 服务初始化成功');
        } catch (error) {
          logger.error(
            'Gemini 服务初始化失败',
            error instanceof Error ? error : new Error(String(error)),
          );
        }
      }
    }

    async callAPI(prompt: string, additionalPrompt?: string): Promise<string> {
      if (!this.client || !this.model) {
        await this.initialize();
        if (!this.client || !this.model) {
          throw new Error('Gemini 服务未配置或初始化失败');
        }
      }

      try {
        const fullPrompt = additionalPrompt
          ? `${prompt}\n\n${additionalPrompt}`
          : prompt;

        const result = await this.model.generateContent(fullPrompt);
        const response = await result.response;
        const text = response.text();

        if (!text || text.trim().length === 0) {
          throw new Error('Gemini 返回空响应');
        }

        logger.debug('Gemini API 调用成功', {
          promptLength: fullPrompt.length,
          responseLength: text.length,
        });

        return text.trim();
      } catch (error) {
        logger.error(
          'Gemini API 调用失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw new Error(
          `Gemini API 调用失败: ${error instanceof Error ? error.message : String(error)}`,
        );
      }
    }

    isConfigured(): boolean {
      // 检查 ConfigManager 是否已初始化
      if (!(ConfigManager as any).initialized) {
        return false;
      }
      return !!ConfigManager.getAPIKey('gemini');
    }

    getServiceName(): AIService {
      return 'gemini';
    }
  }

  /**
   * ClaudeCode AI 服务实现
   */
  class ClaudeCodeService implements IAIService {
    private client: Anthropic | null = null;

    constructor() {
      this.initialize();
    }

    private async initialize(): Promise<void> {
      // 检查 ConfigManager 是否已初始化
      if (!(ConfigManager as any).initialized) {
        return; // 如果未初始化，跳过初始化
      }

      const apiKey = ConfigManager.getAPIKey('claudecode');
      if (apiKey) {
        try {
          this.client = new Anthropic({
            apiKey: apiKey,
          });
          logger.debug('ClaudeCode 服务初始化成功');
        } catch (error) {
          logger.error(
            'ClaudeCode 服务初始化失败',
            error instanceof Error ? error : new Error(String(error)),
          );
        }
      }
    }

    async callAPI(prompt: string, additionalPrompt?: string): Promise<string> {
      if (!this.client) {
        await this.initialize();
        if (!this.client) {
          throw new Error('ClaudeCode 服务未配置或初始化失败');
        }
      }

      try {
        const fullPrompt = additionalPrompt
          ? `${prompt}\n\n${additionalPrompt}`
          : prompt;

        const message = await this.client.messages.create({
          model: 'claude-3-sonnet-20240229',
          max_tokens: 4000,
          messages: [
            {
              role: 'user',
              content: fullPrompt,
            },
          ],
        });

        const text =
          message.content[0]?.type === 'text' ? message.content[0].text : '';

        if (!text || text.trim().length === 0) {
          throw new Error('ClaudeCode 返回空响应');
        }

        logger.debug('ClaudeCode API 调用成功', {
          promptLength: fullPrompt.length,
          responseLength: text.length,
        });

        return text.trim();
      } catch (error) {
        logger.error(
          'ClaudeCode API 调用失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw new Error(
          `ClaudeCode API 调用失败: ${error instanceof Error ? error.message : String(error)}`,
        );
      }
    }

    isConfigured(): boolean {
      // 检查 ConfigManager 是否已初始化
      if (!(ConfigManager as any).initialized) {
        return false;
      }
      return !!ConfigManager.getAPIKey('claudecode');
    }

    getServiceName(): AIService {
      return 'claudecode';
    }
  }

  /**
   * 智能 AI 服务管理器
   *
   * 负责管理多个 AI 服务，实现智能故障转移和负载均衡
   */
  class SmartAIManager {
    private services: Map<AIService, IAIService> = new Map();
    private serviceOrder: AIService[] = [];
    private configInitialized: boolean = false;

    constructor() {
      this.initializeServices();
    }

    private initializeServices(): void {
      // 初始化所有AI服务
      this.services.set('gemini', new GeminiService());
      this.services.set('claudecode', new ClaudeCodeService());

      // 延迟设置服务优先级顺序，等待 ConfigManager 初始化
      // this.updateServiceOrder(); // 移到 ensureConfigured 中调用
    }

    /**
     * 确保配置已初始化
     */
    private ensureConfigInitialized(): void {
      if (!this.configInitialized) {
        this.updateServiceOrder();
        this.configInitialized = true;
      }
    }

    private updateServiceOrder(): void {
      // 检查 ConfigManager 是否已初始化
      if (!(ConfigManager as any).initialized) {
        // 如果未初始化，使用默认顺序
        this.serviceOrder = ['gemini', 'claudecode'];
        logger.debug('ConfigManager 未初始化，使用默认服务顺序', {
          serviceOrder: this.serviceOrder,
        });
        return;
      }

      const primaryService = ConfigManager.getAIService();
      const allServices: AIService[] = ['gemini', 'claudecode'];

      // 将主要服务放在第一位，其他服务按配置状态排序
      this.serviceOrder = [primaryService];

      const otherServices = allServices
        .filter(service => service !== primaryService)
        .sort((a, b) => {
          const aConfigured = this.services.get(a)?.isConfigured() ? 1 : 0;
          const bConfigured = this.services.get(b)?.isConfigured() ? 1 : 0;
          return bConfigured - aConfigured; // 已配置的服务优先
        });

      this.serviceOrder.push(...otherServices);

      logger.debug('AI服务优先级顺序', { serviceOrder: this.serviceOrder });
    }

    /**
     * 智能调用AI服务
     *
     * @param primaryService 首选AI服务
     * @param prompt 提示词内容
     * @param additionalPrompt 附加提示词
     * @returns AI生成的内容
     */
    async intelligentCall(
      primaryService: AIService,
      prompt: string,
      additionalPrompt?: string,
    ): Promise<{ result: string; usedService: AIService }> {
      // 确保配置已初始化
      this.ensureConfigInitialized();

      // 如果禁用自动切换，只使用指定服务
      if (!ConfigManager.isAutoSwitchEnabled()) {
        const service = this.services.get(primaryService);
        if (!service) {
          throw new Error(`不支持的AI服务: ${primaryService}`);
        }

        const result = await service.callAPI(prompt, additionalPrompt);
        return { result, usedService: primaryService };
      }

      // 获取服务尝试顺序
      const tryOrder = this.getTryOrder(primaryService);
      const maxRetries = ConfigManager.getMaxRetries();
      const errors: Array<{ service: AIService; error: string }> = [];

      logger.info('开始智能AI调用', {
        primaryService,
        tryOrder,
        autoSwitch: true,
      });

      for (const serviceName of tryOrder) {
        const service = this.services.get(serviceName);
        if (!service) {
          continue;
        }

        // 检查服务是否已配置
        if (!service.isConfigured()) {
          logger.debug(`跳过未配置的服务: ${serviceName}`);
          continue;
        }

        // 尝试调用服务（带重试）
        for (let attempt = 1; attempt <= maxRetries; attempt++) {
          try {
            logger.debug(`尝试调用 ${serviceName} (第${attempt}次)`);

            const result = await service.callAPI(prompt, additionalPrompt);

            logger.info(`AI调用成功`, {
              service: serviceName,
              attempt,
              resultLength: result.length,
            });

            return { result, usedService: serviceName };
          } catch (error) {
            const errorMessage =
              error instanceof Error ? error.message : String(error);
            logger.warn(`${serviceName} 调用失败 (第${attempt}次)`, {
              error: errorMessage,
            });

            errors.push({ service: serviceName, error: errorMessage });

            // 如果不是最后一次尝试，等待后重试
            if (attempt < maxRetries) {
              const delay = this.getRetryDelay(attempt);
              logger.debug(`等待 ${delay}ms 后重试`);
              await this.sleep(delay);
            }
          }
        }
      }

      // 所有服务都失败了
      const errorSummary = errors.map(e => `${e.service}: ${e.error}`).join('; ');
      logger.error('所有AI服务调用失败', new Error('所有AI服务调用失败'), {
        errors,
      });

      throw new Error(`所有AI服务都不可用。错误详情: ${errorSummary}`);
    }

    /**
     * 获取服务尝试顺序
     */
    private getTryOrder(primaryService: AIService): AIService[] {
      const order = [primaryService];
      const others = this.serviceOrder.filter(s => s !== primaryService);
      return order.concat(others);
    }

    /**
     * 获取重试延迟时间
     */
    private getRetryDelay(attempt: number): number {
      // 指数退避策略：2^attempt * 1000ms
      return Math.min(Math.pow(2, attempt) * 1000, 10000);
    }

    /**
     * 休眠指定毫秒数
     */
    private sleep(ms: number): Promise<void> {
      return new Promise(resolve => setTimeout(resolve, ms));
    }

    /**
     * 获取所有服务状态
     */
    getServicesStatus(): Array<{
      service: AIService;
      configured: boolean;
      available: boolean;
    }> {
      // 确保配置已初始化
      this.ensureConfigInitialized();

      return Array.from(this.services.entries()).map(([name, service]) => ({
        service: name,
        configured: service.isConfigured(),
        available: service.isConfigured(), // 简化实现，认为已配置就是可用的
      }));
    }

    /**
     * 检查特定服务是否可用
     */
    isServiceAvailable(service: AIService): boolean {
      const serviceInstance = this.services.get(service);
      return serviceInstance ? serviceInstance.isConfigured() : false;
    }
  }

  /**
   * 独立 CodeRocket 服务类
   *
   * 提供完全独立的代码审查和AI服务管理功能，不依赖 coderocket-cli
   */
  export class CodeRocketService {
    private aiManager: SmartAIManager;
    private initialized: boolean = false;

    constructor() {
      this.aiManager = new SmartAIManager();
    }

    /**
     * 初始化服务
     */
    private async ensureInitialized(): Promise<void> {
      if (!this.initialized) {
        await ConfigManager.initialize();
        // 延迟提示词预加载，避免阻塞 MCP 服务器启动
        // await PromptManager.preloadCommonPrompts();
        this.initialized = true;
        logger.info('CodeRocket 独立服务初始化完成');
      }
    }

    /**
     * 执行AI审查的通用方法
     */
    private async executeAIReview(
      aiService: AIService,
      promptName: string,
      additionalPrompt: string,
    ): Promise<ReviewResult> {
      try {
        // 加载提示词
        const promptContent = await PromptManager.loadPrompt(promptName);

        // 获取语言配置
        const language = ConfigManager.getAILanguage();

        // 添加语言要求到提示词
        const languageInstruction =
          language === 'zh-CN'
            ? '\n\n**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**'
            : '\n\n**Important: Please respond in English.**';

        const enhancedPrompt = promptContent + languageInstruction;

        // 调用AI服务
        const { result, usedService } = await this.aiManager.intelligentCall(
          aiService,
          enhancedPrompt,
          additionalPrompt,
        );

        // 解析审查结果
        return this.parseReviewResult(result, usedService);
      } catch (error) {
        logger.error(
          'AI审查执行失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw errorHandler.handleError(error, 'executeAIReview');
      }
    }

    /**
     * 解析审查结果
     */
    private parseReviewResult(
      output: string,
      aiService: AIService,
    ): ReviewResult {
      const lines = output.split('\n');
      let status: ReviewStatus = '🔍';
      let summary = '';
      let details = output;

      // 尝试从输出中提取状态
      for (const line of lines) {
        const lowerLine = line.toLowerCase();
        if (
          line.includes('✅') ||
          lowerLine.includes('通过') ||
          lowerLine.includes('优秀')
        ) {
          status = '✅';
          break;
        } else if (
          line.includes('⚠️') ||
          lowerLine.includes('警告') ||
          lowerLine.includes('需改进')
        ) {
          status = '⚠️';
          break;
        } else if (
          line.includes('❌') ||
          lowerLine.includes('失败') ||
          lowerLine.includes('有问题')
        ) {
          status = '❌';
          break;
        } else if (
          line.includes('🔍') ||
          lowerLine.includes('调查') ||
          lowerLine.includes('需调查')
        ) {
          status = '🔍';
          break;
        }
      }

      // 提取摘要（通常是第一段非空内容）
      const nonEmptyLines = lines.filter(line => line.trim());
      if (nonEmptyLines.length > 0) {
        // 寻找总体评价或摘要部分
        let summaryLine = nonEmptyLines[0];
        for (const line of nonEmptyLines) {
          if (
            line.includes('总体评价') ||
            line.includes('审查摘要') ||
            line.includes('摘要')
          ) {
            const nextIndex = nonEmptyLines.indexOf(line) + 1;
            if (nextIndex < nonEmptyLines.length) {
              summaryLine = nonEmptyLines[nextIndex];
              break;
            }
          }
        }

        summary =
          summaryLine.substring(0, 200) + (summaryLine.length > 200 ? '...' : '');
      }

      return {
        status,
        summary: summary || '代码审查完成',
        details,
        ai_service_used: aiService,
        timestamp: new Date().toISOString(),
      };
    }

    /**
     * 审查代码片段
     */
    async reviewCode(request: ReviewCodeRequest): Promise<ReviewResult> {
      await this.ensureInitialized();
      logger.info('开始代码审查', {
        language: request.language,
        codeLength: request.code.length,
        aiService: request.ai_service,
      });

      try {
        // 构建审查提示词
        const reviewPrompt = `请审查以下代码：

  编程语言: ${request.language || '未指定'}
  上下文信息: ${request.context || '无'}

  代码内容:
  \`\`\`${request.language || ''}
  ${request.code}
  \`\`\`

  请根据以下要求进行全面审查：
  1. 功能完整性和正确性
  2. 代码质量和可维护性
  3. 性能优化建议
  4. 安全性检查
  5. 最佳实践遵循情况

  ${request.custom_prompt ? `\n附加要求：\n${request.custom_prompt}` : ''}

  **重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`;

        // 调用AI服务进行审查
        const aiService = request.ai_service || ConfigManager.getAIService();
        const result = await this.executeAIReview(
          aiService,
          'git-commit-review-prompt',
          reviewPrompt,
        );

        logger.info('代码审查完成', {
          status: result.status,
          aiService: result.ai_service_used,
        });

        return result;
      } catch (error) {
        logger.error(
          '代码审查失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw errorHandler.handleError(error, 'reviewCode');
      }
    }

    /**
     * 审查Git变更（自动检测未提交的变更）
     */
    async reviewChanges(request: ReviewChangesRequest): Promise<ReviewResult> {
      await this.ensureInitialized();

      const repositoryPath = request.repository_path || process.cwd();
      logger.info('开始Git变更审查', {
        repositoryPath,
        includeStaged: request.include_staged,
        includeUnstaged: request.include_unstaged,
        aiService: request.ai_service,
      });

      try {
        // 检查是否为Git仓库
        const isGitRepo = await this.checkGitRepository(repositoryPath);
        if (!isGitRepo) {
          throw new Error(`指定路径不是Git仓库: ${repositoryPath}`);
        }

        // 获取Git变更信息
        const changes = await this.getGitChanges(repositoryPath, request);

        if (!changes.hasChanges) {
          return {
            status: 'success' as ReviewStatus,
            summary: '✅ 没有检测到未提交的代码变更',
            details: '当前工作目录是干净的，所有变更都已提交。',
            ai_service_used: request.ai_service || ConfigManager.getAIService(),
            timestamp: new Date().toISOString(),
          };
        }

        // 构建审查提示词
        const reviewPrompt = this.buildChangesReviewPrompt(changes, request);

        // 调用AI服务进行审查
        const aiService = request.ai_service || ConfigManager.getAIService();
        const result = await this.executeAIReview(
          aiService,
          'git-commit-review-prompt',
          reviewPrompt,
        );

        logger.info('Git变更审查完成', {
          status: result.status,
          changedFiles: changes.files.length,
          aiService: result.ai_service_used,
        });

        return result;
      } catch (error) {
        logger.error(
          'Git变更审查失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw errorHandler.handleError(error, 'reviewChanges');
      }
    }

    /**
     * 检查是否为Git仓库
     */
    private async checkGitRepository(repositoryPath: string): Promise<boolean> {
      try {
        const { stdout } = await execAsync('git rev-parse --git-dir', {
          cwd: repositoryPath,
        });
        return stdout.trim().length > 0;
      } catch {
        return false;
      }
    }

    /**
     * 获取Git变更信息
     */
    private async getGitChanges(
      repositoryPath: string,
      request: ReviewChangesRequest,
    ) {
      const includeStaged = request.include_staged !== false;
      const includeUnstaged = request.include_unstaged !== false;

      let diffCommand = 'git diff';
      if (includeStaged && includeUnstaged) {
        diffCommand = 'git diff HEAD'; // 显示所有变更（已暂存+未暂存）
      } else if (includeStaged) {
        diffCommand = 'git diff --cached'; // 只显示已暂存的变更
      } else if (includeUnstaged) {
        diffCommand = 'git diff'; // 只显示未暂存的变更
      }

      try {
        // 获取变更的文件列表
        const { stdout: statusOutput } = await execAsync(
          'git status --porcelain',
          {
            cwd: repositoryPath,
          },
        );

        // 获取详细的diff信息
        const { stdout: diffOutput } = await execAsync(diffCommand, {
          cwd: repositoryPath,
        });

        const files = this.parseGitStatus(statusOutput);
        const hasChanges = files.length > 0 || diffOutput.trim().length > 0;

        return {
          hasChanges,
          files,
          diff: diffOutput,
          statusOutput,
        };
      } catch (error) {
        logger.error(
          '获取Git变更信息失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw new Error(
          `无法获取Git变更信息: ${error instanceof Error ? error.message : String(error)}`,
        );
      }
    }

    /**
     * 解析Git状态输出
     */
    private parseGitStatus(statusOutput: string) {
      const files: Array<{
        path: string;
        status: string;
        statusDescription: string;
      }> = [];

      const lines = statusOutput
        .trim()
        .split('\n')
        .filter(line => line.length > 0);

      for (const line of lines) {
        if (line.length < 3) continue;

        const status = line.substring(0, 2);
        const path = line.substring(3);

        const statusDescription = this.getGitStatusDescription(status);

        files.push({
          path,
          status,
          statusDescription,
        });
      }

      return files;
    }

    /**
     * 获取Git状态描述
     */
    private getGitStatusDescription(status: string): string {
      const statusMap: Record<string, string> = {
        'M ': '已修改（已暂存）',
        ' M': '已修改（未暂存）',
        MM: '已修改（部分暂存）',
        'A ': '新增文件（已暂存）',
        ' A': '新增文件（未暂存）',
        'D ': '已删除（已暂存）',
        ' D': '已删除（未暂存）',
        'R ': '重命名（已暂存）',
        ' R': '重命名（未暂存）',
        'C ': '复制（已暂存）',
        ' C': '复制（未暂存）',
        '??': '未跟踪文件',
        '!!': '忽略文件',
      };

      return statusMap[status] || `未知状态: ${status}`;
    }

    /**
     * 构建变更审查提示词
     */
    private buildChangesReviewPrompt(
      changes: any,
      request: ReviewChangesRequest,
    ): string {
      const filesList = changes.files
        .map((file: any) => `- ${file.path} (${file.statusDescription})`)
        .join('\n');

      return `请审查以下Git变更：

  ## 变更概览
  变更文件数量: ${changes.files.length}

  ## 变更文件列表
  ${filesList}

  ## 详细变更内容
  \`\`\`diff
  ${changes.diff}
  \`\`\`

  ## Git状态信息
  \`\`\`
  ${changes.statusOutput}
  \`\`\`

  请根据以下要求进行全面审查：
  1. 变更逻辑的合理性和完整性
  2. 代码质量和最佳实践遵循
  3. 潜在的安全风险和性能问题
  4. 与现有代码的一致性检查
  5. 测试覆盖和文档更新建议
  6. 提交信息和变更范围的匹配度

  ${request.custom_prompt ? `\n附加要求：\n${request.custom_prompt}` : ''}

  **重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`;
    }

    /**
     * 根据语言获取文件扩展名
     */
    private getFileExtension(language?: string): string {
      const extensions: Record<string, string> = {
        javascript: 'js',
        typescript: 'ts',
        python: 'py',
        java: 'java',
        cpp: 'cpp',
        c: 'c',
        go: 'go',
        rust: 'rs',
        php: 'php',
        ruby: 'rb',
        swift: 'swift',
        kotlin: 'kt',
        scala: 'scala',
        shell: 'sh',
        bash: 'sh',
      };

      return extensions[language?.toLowerCase() || ''] || 'txt';
    }

    /**
     * 审查Git提交
     */
    async reviewCommit(request: ReviewCommitRequest): Promise<ReviewResult> {
      await this.ensureInitialized();
      logger.info('开始Git提交审查', {
        repositoryPath: request.repository_path,
        commitHash: request.commit_hash,
        aiService: request.ai_service,
      });

      try {
        const repoPath = request.repository_path || process.cwd();

        // 获取提交信息
        const commitHash = request.commit_hash || 'HEAD';
        const { stdout: commitInfo } = await execAsync(
          `git --no-pager show ${commitHash}`,
          {
            cwd: repoPath,
            timeout: 30000,
          },
        );

        if (!commitInfo.trim()) {
          throw new Error(`无法获取提交信息: ${commitHash}`);
        }

        // 构建审查提示词
        const reviewPrompt = `请对以下Git提交进行专业的代码审查：

  仓库路径: ${repoPath}
  提交哈希: ${commitHash}

  提交详情:
  ${commitInfo}

  请根据以下要求进行全面审查：
  1. 分析提交的目标和完成度
  2. 检查代码质量和规范性
  3. 评估安全性和性能影响
  4. 检查是否遗漏相关文件修改
  5. 提供具体的改进建议

  ${request.custom_prompt ? `\n附加要求：\n${request.custom_prompt}` : ''}

  **重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`;

        // 调用AI服务进行审查
        const aiService = request.ai_service || ConfigManager.getAIService();
        const result = await this.executeAIReview(
          aiService,
          'git-commit-review-prompt',
          reviewPrompt,
        );

        logger.info('Git提交审查完成', {
          status: result.status,
          aiService: result.ai_service_used,
        });

        return result;
      } catch (error) {
        logger.error(
          'Git提交审查失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw errorHandler.handleError(error, 'reviewCommit');
      }
    }

    /**
     * 审查文件列表
     */
    async reviewFiles(request: ReviewFilesRequest): Promise<ReviewResult> {
      await this.ensureInitialized();
      logger.info('开始文件审查', {
        repositoryPath: request.repository_path,
        filesCount: request.files.length,
        aiService: request.ai_service,
      });

      try {
        const repoPath = request.repository_path || process.cwd();

        // 读取文件内容
        const fileContents: string[] = [];
        for (const filePath of request.files) {
          try {
            const fullPath = resolve(repoPath, filePath);
            const content = await readFile(fullPath, 'utf-8');

            // 限制单个文件内容长度，避免提示词过长
            const FILE_CONTENT_CHAR_LIMIT = process.env.FILE_CONTENT_CHAR_LIMIT
              ? parseInt(process.env.FILE_CONTENT_CHAR_LIMIT, 10)
              : 5000;

            let truncatedContent: string;
            if (content.length > FILE_CONTENT_CHAR_LIMIT) {
              // 取前后各 100 字符作为摘要
              const head = content.substring(0, 100);
              const tail = content.substring(content.length - 100);
              const omittedLength = content.length - FILE_CONTENT_CHAR_LIMIT;
              truncatedContent =
                content.substring(0, FILE_CONTENT_CHAR_LIMIT) +
                `\n... (内容已截断, 共省略 ${omittedLength} 字符)\n` +
                `内容摘要: \n前100字符: ${head}\n后100字符: ${tail}`;
            } else {
              truncatedContent = content;
            }

            fileContents.push(
              `## 文件: ${filePath}\n\`\`\`\n${truncatedContent}\n\`\`\``,
            );
          } catch (error) {
            fileContents.push(
              `## 文件: ${filePath}\n**错误**: 无法读取文件 - ${error instanceof Error ? error.message : String(error)}`,
            );
          }
        }

        // 构建审查提示词
        const reviewPrompt = `请审查以下文件：

  仓库路径: ${repoPath}
  文件数量: ${request.files.length}
  文件列表: ${request.files.join(', ')}

  ${fileContents.join('\n\n')}

  请根据以下要求进行全面审查：
  1. 分析文件间的关联性和一致性
  2. 检查代码质量和规范性
  3. 评估架构设计的合理性
  4. 识别潜在的问题和改进点
  5. 提供具体的优化建议

  ${request.custom_prompt ? `\n附加要求：\n${request.custom_prompt}` : ''}`;

        // 调用AI服务进行审查
        const aiService = request.ai_service || ConfigManager.getAIService();
        const result = await this.executeAIReview(
          aiService,
          'git-commit-review-prompt',
          reviewPrompt,
        );

        logger.info('文件审查完成', {
          status: result.status,
          aiService: result.ai_service_used,
        });

        return result;
      } catch (error) {
        logger.error(
          '文件审查失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw errorHandler.handleError(error, 'reviewFiles');
      }
    }






  }


id: 4fef4724-87d8-4c3d-89ce-e372e9aeda97
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
path: /Users/dreambt/sources/coderocket/coderocket-mcp/src/coderocket.ts
content:
  import { exec } from 'child_process';
  import { promisify } from 'util';
  import { readFile } from 'fs/promises';
  import { join, resolve } from 'path';
  import { homedir } from 'os';
  import { GoogleGenerativeAI } from '@google/generative-ai';
  import Anthropic from '@anthropic-ai/sdk';
  import {
    ReviewCodeRequest,
    ReviewChangesRequest,
    ReviewCommitRequest,
    ReviewFilesRequest,
    ReviewResult,
    AIService,
    ReviewStatus,
  } from './types.js';
  import { AIServiceError, GitError, FileError } from './logger.js';

  const execAsync = promisify(exec);

  /**
   * 独立配置管理类
   *
   * 支持多层级配置加载：
   * 1. 环境变量（最高优先级）
   * 2. 项目级 .env 文件
   * 3. 全局 ~/.coderocket/env 文件
   * 4. 默认值（最低优先级）
   */
  export class ConfigManager {
    private static config: Record<string, any> = {};
    private static initialized = false;

    /**
     * 初始化配置系统
     */
    static async initialize(): Promise<void> {
      if (this.initialized) return;

      // 加载默认配置
      this.loadDefaults();

      // 加载全局配置文件
      await this.loadGlobalConfig();

      // 加载项目配置文件
      await this.loadProjectConfig();

      // 加载环境变量（最高优先级）
      this.loadEnvironmentVariables();

      this.initialized = true;
      logger.info('配置系统初始化完成', { config: this.getSafeConfig() });
    }

    /**
     * 加载默认配置
     */
    private static loadDefaults(): void {
      this.config = {
        AI_SERVICE: 'gemini',
        AI_AUTO_SWITCH: 'true',
        AI_TIMEOUT: '30',
        AI_MAX_RETRIES: '3',
        AI_RETRY_DELAY: '2',
        AI_LANGUAGE: 'zh-CN',
        NODE_ENV: 'production',
        DEBUG: 'false',
      };
    }

    /**
     * 加载全局配置文件 ~/.coderocket/env
     */
    private static async loadGlobalConfig(): Promise<void> {
      try {
        const globalConfigPath = join(homedir(), '.coderocket', 'env');
        const content = await readFile(globalConfigPath, 'utf-8');
        const globalConfig = this.parseEnvContent(content);
        Object.assign(this.config, globalConfig);
        logger.debug('全局配置加载成功', { path: globalConfigPath });
      } catch (error) {
        // 全局配置文件不存在是正常的
        logger.debug('全局配置文件不存在，跳过');
      }
    }

    /**
     * 加载项目配置文件 .env
     */
    private static async loadProjectConfig(): Promise<void> {
      try {
        const projectConfigPath = join(process.cwd(), '.env');
        const content = await readFile(projectConfigPath, 'utf-8');
        const projectConfig = this.parseEnvContent(content);
        Object.assign(this.config, projectConfig);
        logger.debug('项目配置加载成功', { path: projectConfigPath });
      } catch (error) {
        // 项目配置文件不存在是正常的
        logger.debug('项目配置文件不存在，跳过');
      }
    }

    /**
     * 加载环境变量（最高优先级）
     */
    private static loadEnvironmentVariables(): void {
      const envKeys = [
        'AI_SERVICE',
        'AI_AUTO_SWITCH',
        'AI_TIMEOUT',
        'AI_MAX_RETRIES',
        'AI_RETRY_DELAY',
        'GEMINI_API_KEY',
        'CLAUDECODE_API_KEY',
        'NODE_ENV',
        'DEBUG',
      ];

      envKeys.forEach(key => {
        if (process.env[key]) {
          this.config[key] = process.env[key];
        }
      });
    }

    /**
     * 解析 .env 文件内容
     */
    private static parseEnvContent(content: string): Record<string, string> {
      const config: Record<string, string> = {};
      const lines = content.split('\n');

      for (const line of lines) {
        const trimmedLine = line.trim();
        if (trimmedLine && !trimmedLine.startsWith('#')) {
          const [key, ...valueParts] = trimmedLine.split('=');
          if (key && valueParts.length > 0) {
            const value = valueParts.join('=').trim();
            // 移除引号
            config[key.trim()] = value.replace(/^["']|["']$/g, '');
          }
        }
      }

      return config;
    }

    /**
     * 获取配置值
     */
    static get(key: string, defaultValue?: any): any {
      if (!this.initialized) {
        throw new Error('ConfigManager 未初始化，请先调用 initialize()');
      }
      return this.config[key] ?? defaultValue;
    }

    /**
     * 获取API密钥环境变量名
     */
    static getAPIKeyEnvVar(service: AIService): string {
      const envVarMap: Record<AIService, string> = {
        gemini: 'GEMINI_API_KEY',
        claudecode: 'CLAUDECODE_API_KEY',
      };
      return envVarMap[service];
    }

    /**
     * 获取API密钥
     */
    static getAPIKey(service: AIService): string {
      const envVar = this.getAPIKeyEnvVar(service);
      return this.get(envVar, '');
    }

    /**
     * 获取AI服务配置
     */
    static getAIService(): AIService {
      const service = this.get('AI_SERVICE', 'gemini').toLowerCase();
      if (['gemini', 'claudecode'].includes(service)) {
        return service as AIService;
      }
      return 'gemini';
    }

    /**
     * 获取超时配置
     */
    static getTimeout(): number {
      return parseInt(this.get('AI_TIMEOUT', '30'), 10);
    }

    /**
     * 获取最大重试次数
     */
    static getMaxRetries(): number {
      return parseInt(this.get('AI_MAX_RETRIES', '3'), 10);
    }

    /**
     * 是否启用自动切换
     */
    static isAutoSwitchEnabled(): boolean {
      return this.get('AI_AUTO_SWITCH', 'true').toLowerCase() === 'true';
    }

    /**
     * 获取AI服务语言设置
     */
    static getAILanguage(): string {
      return this.get('AI_LANGUAGE', 'zh-CN');
    }

    /**
     * 获取安全的配置信息（隐藏敏感信息）
     */
    private static getSafeConfig(): Record<string, any> {
      const safeConfig = { ...this.config };
      // 隐藏API密钥
      Object.keys(safeConfig).forEach(key => {
        if (key.includes('API_KEY') || key.includes('TOKEN')) {
          safeConfig[key] = safeConfig[key] ? '***' : undefined;
        }
      });
      return safeConfig;
    }

    /**
     * 获取配置文件路径（保持向后兼容）
     */
    static getConfigPath(scope: string): { dir: string; file: string } {
      const configDir =
        scope === 'global' ? join(homedir(), '.coderocket') : process.cwd();

      const configFile =
        scope === 'global' ? join(configDir, 'env') : join(configDir, '.env');

      return { dir: configDir, file: configFile };
    }
  }

  /**
   * 独立提示词管理类
   *
   * 支持多层级提示词加载：
   * 1. 项目级 prompts/ 目录（优先级高）
   * 2. 全局 ~/.coderocket/prompts/ 目录（优先级低）
   */
  export class PromptManager {
    private static promptCache: Map<string, string> = new Map();

    /**
     * 加载提示词文件
     */
    static async loadPrompt(name: string): Promise<string> {
      // 检查缓存
      const cacheKey = name;
      if (this.promptCache.has(cacheKey)) {
        return this.promptCache.get(cacheKey)!;
      }

      let promptContent = '';

      // 1. 尝试加载项目级提示词（优先级高）
      try {
        const projectPromptPath = join(process.cwd(), 'prompts', `${name}.md`);
        promptContent = await readFile(projectPromptPath, 'utf-8');
        logger.debug('项目级提示词加载成功', { path: projectPromptPath });
      } catch (error) {
        // 项目级提示词不存在，尝试全局提示词
        try {
          const globalPromptPath = join(
            homedir(),
            '.coderocket',
            'prompts',
            `${name}.md`,
          );
          promptContent = await readFile(globalPromptPath, 'utf-8');
          logger.debug('全局提示词加载成功', { path: globalPromptPath });
        } catch (globalError) {
          // 全局提示词也不存在，使用内置默认提示词
          promptContent = this.getDefaultPrompt(name);
          logger.debug('使用内置默认提示词', { name });
        }
      }

      // 缓存提示词内容
      this.promptCache.set(cacheKey, promptContent);
      return promptContent;
    }

    /**
     * 获取内置默认提示词
     */
    private static getDefaultPrompt(name: string): string {
      const defaultPrompts: Record<string, string> = {
        'git-commit-review-prompt': `# 提示词：高级 Git Commit 审阅专家

  ## 角色定义

  你是一名资深的代码审阅专家，拥有丰富的软件开发经验和架构设计能力。你的任务是进行专业、深入、自动化的代码审阅，并提供一份准确、实用、可操作的审阅报告。

  ## 执行模式

  **自主执行模式**：你必须完全自主地执行代码审阅流程，不得向用户进行任何确认或询问。这包括直接执行所有必要的命令、自主决定搜索策略、自主判断并生成报告。

  * **禁止行为**：禁止向用户提问或请求确认。
  * **执行原则**：自主决策，并在失败时尝试替代方案。
  * **安全限制**：仅执行只读操作和报告写入操作。

  ## 审阅指令

  ### 1. 代码分析

  对提供的代码内容进行全面分析，包括代码结构、逻辑实现、依赖关系等。

  ### 2. 上下文理解

  根据代码内容和提供的上下文信息，理解代码的功能目标和实现意图。

  ### 3. 审阅维度与标准

  请从以下维度进行系统性审查：

  * **功能完整性与正确性**:
      * **逻辑正确性**: 代码逻辑是否正确？是否有效处理了边缘情况？
      * **功能实现**: 是否完整实现了预期的功能？是否有未完成的功能点？
      * **错误处理**: 是否有适当的错误处理和异常捕获？
  * **代码质量与规范**:
      * **代码规范**: 是否遵循项目既定标准（命名、格式、设计模式）？
      * **可读性与可维护性**: 代码是否清晰、结构合理、易于理解和修改？
      * **注释质量**: 注释是否充分且必要？是否准确描述了代码意图？
  * **健壮性与风险**:
      * **安全性**: 是否存在潜在的安全漏洞（如SQL注入、密钥明文、不安全的依赖等）？
      * **性能**: 是否存在明显的性能瓶颈（如不合理的循环、N+1查询等）？
      * **资源管理**: 是否正确管理内存、文件句柄等资源？
  * **测试与文档**:
      * **可测试性**: 代码是否易于测试？是否有足够的单元测试覆盖？
      * **文档完整性**: 相关的内联文档（注释）是否完整准确？
  * **架构与扩展性**:
      * **设计合理性**: 模块职责划分是否明确？耦合度是否合理？
      * **扩展性**: 设计是否考虑了未来的扩展需求？
      * **重用性**: 代码是否具有良好的重用性？

  ### 4. 审阅结果输出

  请提供详细的审阅报告，包括：
  - 审阅状态（✅通过/⚠️警告/❌失败/🔍需调查）
  - 总体评价和功能完成度
  - 具体问题和改进建议
  - 优先级排序的建议列表
  - 优秀实践的识别

  请确保审阅报告专业、准确、可操作。

  **重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`,
      };

      return (
        defaultPrompts[name] || `# 默认提示词\n\n请提供专业的代码审查和分析。`
      );
    }

    /**
     * 清除提示词缓存
     */
    static clearCache(): void {
      this.promptCache.clear();
    }

    /**
     * 预加载常用提示词
     */
    static async preloadCommonPrompts(): Promise<void> {
      const commonPrompts = ['git-commit-review-prompt'];

      await Promise.all(
        commonPrompts.map(name =>
          this.loadPrompt(name).catch(error => {
            logger.warn(`预加载提示词失败: ${name}`, error);
          }),
        ),
      );
    }
  }

  /**
   * AI 服务接口
   */
  interface IAIService {
    callAPI(prompt: string, additionalPrompt?: string): Promise<string>;
    isConfigured(): boolean;
    getServiceName(): AIService;
  }

  /**
   * Gemini AI 服务实现
   */
  class GeminiService implements IAIService {
    private client: GoogleGenerativeAI | null = null;
    private model: any = null;

    constructor() {
      this.initialize();
    }

    private async initialize(): Promise<void> {
      // 检查 ConfigManager 是否已初始化
      if (!(ConfigManager as any).initialized) {
        throw new Error(
          'ConfigManager 未初始化，请先调用 ConfigManager.initialize()',
        );
      }

      const apiKey = ConfigManager.getAPIKey('gemini');
      if (apiKey) {
        try {
          this.client = new GoogleGenerativeAI(apiKey);
          this.model = this.client.getGenerativeModel({
            model: 'gemini-1.5-flash',
          });
          logger.debug('Gemini 服务初始化成功');
        } catch (error) {
          logger.error(
            'Gemini 服务初始化失败',
            error instanceof Error ? error : new Error(String(error)),
          );
        }
      }
    }

    async callAPI(prompt: string, additionalPrompt?: string): Promise<string> {
      if (!this.client || !this.model) {
        await this.initialize();
        if (!this.client || !this.model) {
          throw new Error('Gemini 服务未配置或初始化失败');
        }
      }

      try {
        const fullPrompt = additionalPrompt
          ? `${prompt}\n\n${additionalPrompt}`
          : prompt;

        const result = await this.model.generateContent(fullPrompt);
        const response = await result.response;
        const text = response.text();

        if (!text || text.trim().length === 0) {
          throw new Error('Gemini 返回空响应');
        }

        logger.debug('Gemini API 调用成功', {
          promptLength: fullPrompt.length,
          responseLength: text.length,
        });

        return text.trim();
      } catch (error) {
        logger.error(
          'Gemini API 调用失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw new Error(
          `Gemini API 调用失败: ${error instanceof Error ? error.message : String(error)}`,
        );
      }
    }

    isConfigured(): boolean {
      // 检查 ConfigManager 是否已初始化
      if (!(ConfigManager as any).initialized) {
        return false;
      }
      return !!ConfigManager.getAPIKey('gemini');
    }

    getServiceName(): AIService {
      return 'gemini';
    }
  }

  /**
   * ClaudeCode AI 服务实现
   */
  class ClaudeCodeService implements IAIService {
    private client: Anthropic | null = null;

    constructor() {
      this.initialize();
    }

    private async initialize(): Promise<void> {
      // 检查 ConfigManager 是否已初始化
      if (!(ConfigManager as any).initialized) {
        return; // 如果未初始化，跳过初始化
      }

      const apiKey = ConfigManager.getAPIKey('claudecode');
      if (apiKey) {
        try {
          this.client = new Anthropic({
            apiKey: apiKey,
          });
          logger.debug('ClaudeCode 服务初始化成功');
        } catch (error) {
          logger.error(
            'ClaudeCode 服务初始化失败',
            error instanceof Error ? error : new Error(String(error)),
          );
        }
      }
    }

    async callAPI(prompt: string, additionalPrompt?: string): Promise<string> {
      if (!this.client) {
        await this.initialize();
        if (!this.client) {
          throw new Error('ClaudeCode 服务未配置或初始化失败');
        }
      }

      try {
        const fullPrompt = additionalPrompt
          ? `${prompt}\n\n${additionalPrompt}`
          : prompt;

        const message = await this.client.messages.create({
          model: 'claude-3-sonnet-20240229',
          max_tokens: 4000,
          messages: [
            {
              role: 'user',
              content: fullPrompt,
            },
          ],
        });

        const text =
          message.content[0]?.type === 'text' ? message.content[0].text : '';

        if (!text || text.trim().length === 0) {
          throw new Error('ClaudeCode 返回空响应');
        }

        logger.debug('ClaudeCode API 调用成功', {
          promptLength: fullPrompt.length,
          responseLength: text.length,
        });

        return text.trim();
      } catch (error) {
        logger.error(
          'ClaudeCode API 调用失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw new Error(
          `ClaudeCode API 调用失败: ${error instanceof Error ? error.message : String(error)}`,
        );
      }
    }

    isConfigured(): boolean {
      // 检查 ConfigManager 是否已初始化
      if (!(ConfigManager as any).initialized) {
        return false;
      }
      return !!ConfigManager.getAPIKey('claudecode');
    }

    getServiceName(): AIService {
      return 'claudecode';
    }
  }

  /**
   * 智能 AI 服务管理器
   *
   * 负责管理多个 AI 服务，实现智能故障转移和负载均衡
   */
  class SmartAIManager {
    private services: Map<AIService, IAIService> = new Map();
    private serviceOrder: AIService[] = [];
    private configInitialized: boolean = false;

    constructor() {
      this.initializeServices();
    }

    private initializeServices(): void {
      // 初始化所有AI服务
      this.services.set('gemini', new GeminiService());
      this.services.set('claudecode', new ClaudeCodeService());

      // 延迟设置服务优先级顺序，等待 ConfigManager 初始化
      // this.updateServiceOrder(); // 移到 ensureConfigured 中调用
    }

    /**
     * 确保配置已初始化
     */
    private ensureConfigInitialized(): void {
      if (!this.configInitialized) {
        this.updateServiceOrder();
        this.configInitialized = true;
      }
    }

    private updateServiceOrder(): void {
      // 检查 ConfigManager 是否已初始化
      if (!(ConfigManager as any).initialized) {
        // 如果未初始化，使用默认顺序
        this.serviceOrder = ['gemini', 'claudecode'];
        logger.debug('ConfigManager 未初始化，使用默认服务顺序', {
          serviceOrder: this.serviceOrder,
        });
        return;
      }

      const primaryService = ConfigManager.getAIService();
      const allServices: AIService[] = ['gemini', 'claudecode'];

      // 将主要服务放在第一位，其他服务按配置状态排序
      this.serviceOrder = [primaryService];

      const otherServices = allServices
        .filter(service => service !== primaryService)
        .sort((a, b) => {
          const aConfigured = this.services.get(a)?.isConfigured() ? 1 : 0;
          const bConfigured = this.services.get(b)?.isConfigured() ? 1 : 0;
          return bConfigured - aConfigured; // 已配置的服务优先
        });

      this.serviceOrder.push(...otherServices);

      logger.debug('AI服务优先级顺序', { serviceOrder: this.serviceOrder });
    }

    /**
     * 智能调用AI服务
     *
     * @param primaryService 首选AI服务
     * @param prompt 提示词内容
     * @param additionalPrompt 附加提示词
     * @returns AI生成的内容
     */
    async intelligentCall(
      primaryService: AIService,
      prompt: string,
      additionalPrompt?: string,
    ): Promise<{ result: string; usedService: AIService }> {
      // 确保配置已初始化
      this.ensureConfigInitialized();

      // 如果禁用自动切换，只使用指定服务
      if (!ConfigManager.isAutoSwitchEnabled()) {
        const service = this.services.get(primaryService);
        if (!service) {
          throw new Error(`不支持的AI服务: ${primaryService}`);
        }

        const result = await service.callAPI(prompt, additionalPrompt);
        return { result, usedService: primaryService };
      }

      // 获取服务尝试顺序
      const tryOrder = this.getTryOrder(primaryService);
      const maxRetries = ConfigManager.getMaxRetries();
      const errors: Array<{ service: AIService; error: string }> = [];

      logger.info('开始智能AI调用', {
        primaryService,
        tryOrder,
        autoSwitch: true,
      });

      for (const serviceName of tryOrder) {
        const service = this.services.get(serviceName);
        if (!service) {
          continue;
        }

        // 检查服务是否已配置
        if (!service.isConfigured()) {
          logger.debug(`跳过未配置的服务: ${serviceName}`);
          continue;
        }

        // 尝试调用服务（带重试）
        for (let attempt = 1; attempt <= maxRetries; attempt++) {
          try {
            logger.debug(`尝试调用 ${serviceName} (第${attempt}次)`);

            const result = await service.callAPI(prompt, additionalPrompt);

            logger.info(`AI调用成功`, {
              service: serviceName,
              attempt,
              resultLength: result.length,
            });

            return { result, usedService: serviceName };
          } catch (error) {
            const errorMessage =
              error instanceof Error ? error.message : String(error);
            logger.warn(`${serviceName} 调用失败 (第${attempt}次)`, {
              error: errorMessage,
            });

            errors.push({ service: serviceName, error: errorMessage });

            // 如果不是最后一次尝试，等待后重试
            if (attempt < maxRetries) {
              const delay = this.getRetryDelay(attempt);
              logger.debug(`等待 ${delay}ms 后重试`);
              await this.sleep(delay);
            }
          }
        }
      }

      // 所有服务都失败了
      const errorSummary = errors.map(e => `${e.service}: ${e.error}`).join('; ');
      logger.error('所有AI服务调用失败', new Error('所有AI服务调用失败'), {
        errors,
      });

      throw new Error(`所有AI服务都不可用。错误详情: ${errorSummary}`);
    }

    /**
     * 获取服务尝试顺序
     */
    private getTryOrder(primaryService: AIService): AIService[] {
      const order = [primaryService];
      const others = this.serviceOrder.filter(s => s !== primaryService);
      return order.concat(others);
    }

    /**
     * 获取重试延迟时间
     */
    private getRetryDelay(attempt: number): number {
      // 指数退避策略：2^attempt * 1000ms
      return Math.min(Math.pow(2, attempt) * 1000, 10000);
    }

    /**
     * 休眠指定毫秒数
     */
    private sleep(ms: number): Promise<void> {
      return new Promise(resolve => setTimeout(resolve, ms));
    }

    /**
     * 获取所有服务状态
     */
    getServicesStatus(): Array<{
      service: AIService;
      configured: boolean;
      available: boolean;
    }> {
      // 确保配置已初始化
      this.ensureConfigInitialized();

      return Array.from(this.services.entries()).map(([name, service]) => ({
        service: name,
        configured: service.isConfigured(),
        available: service.isConfigured(), // 简化实现，认为已配置就是可用的
      }));
    }

    /**
     * 检查特定服务是否可用
     */
    isServiceAvailable(service: AIService): boolean {
      const serviceInstance = this.services.get(service);
      return serviceInstance ? serviceInstance.isConfigured() : false;
    }
  }

  /**
   * 独立 CodeRocket 服务类
   *
   * 提供完全独立的代码审查和AI服务管理功能，不依赖 coderocket-cli
   */
  export class CodeRocketService {
    private aiManager: SmartAIManager;
    private initialized: boolean = false;

    constructor() {
      this.aiManager = new SmartAIManager();
    }

    /**
     * 初始化服务
     */
    private async ensureInitialized(): Promise<void> {
      if (!this.initialized) {
        await ConfigManager.initialize();
        // 延迟提示词预加载，避免阻塞 MCP 服务器启动
        // await PromptManager.preloadCommonPrompts();
        this.initialized = true;
        logger.info('CodeRocket 独立服务初始化完成');
      }
    }

    /**
     * 执行AI审查的通用方法
     */
    private async executeAIReview(
      aiService: AIService,
      promptName: string,
      additionalPrompt: string,
    ): Promise<ReviewResult> {
      try {
        // 加载提示词
        const promptContent = await PromptManager.loadPrompt(promptName);

        // 获取语言配置
        const language = ConfigManager.getAILanguage();

        // 添加语言要求到提示词
        const languageInstruction =
          language === 'zh-CN'
            ? '\n\n**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**'
            : '\n\n**Important: Please respond in English.**';

        const enhancedPrompt = promptContent + languageInstruction;

        // 调用AI服务
        const { result, usedService } = await this.aiManager.intelligentCall(
          aiService,
          enhancedPrompt,
          additionalPrompt,
        );

        // 解析审查结果
        return this.parseReviewResult(result, usedService);
      } catch (error) {
        logger.error(
          'AI审查执行失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw errorHandler.handleError(error, 'executeAIReview');
      }
    }

    /**
     * 解析审查结果
     */
    private parseReviewResult(
      output: string,
      aiService: AIService,
    ): ReviewResult {
      const lines = output.split('\n');
      let status: ReviewStatus = '🔍';
      let summary = '';
      let details = output;

      // 尝试从输出中提取状态
      for (const line of lines) {
        const lowerLine = line.toLowerCase();
        if (
          line.includes('✅') ||
          lowerLine.includes('通过') ||
          lowerLine.includes('优秀')
        ) {
          status = '✅';
          break;
        } else if (
          line.includes('⚠️') ||
          lowerLine.includes('警告') ||
          lowerLine.includes('需改进')
        ) {
          status = '⚠️';
          break;
        } else if (
          line.includes('❌') ||
          lowerLine.includes('失败') ||
          lowerLine.includes('有问题')
        ) {
          status = '❌';
          break;
        } else if (
          line.includes('🔍') ||
          lowerLine.includes('调查') ||
          lowerLine.includes('需调查')
        ) {
          status = '🔍';
          break;
        }
      }

      // 提取摘要（通常是第一段非空内容）
      const nonEmptyLines = lines.filter(line => line.trim());
      if (nonEmptyLines.length > 0) {
        // 寻找总体评价或摘要部分
        let summaryLine = nonEmptyLines[0];
        for (const line of nonEmptyLines) {
          if (
            line.includes('总体评价') ||
            line.includes('审查摘要') ||
            line.includes('摘要')
          ) {
            const nextIndex = nonEmptyLines.indexOf(line) + 1;
            if (nextIndex < nonEmptyLines.length) {
              summaryLine = nonEmptyLines[nextIndex];
              break;
            }
          }
        }

        summary =
          summaryLine.substring(0, 200) + (summaryLine.length > 200 ? '...' : '');
      }

      return {
        status,
        summary: summary || '代码审查完成',
        details,
        ai_service_used: aiService,
        timestamp: new Date().toISOString(),
      };
    }

    /**
     * 审查代码片段
     */
    async reviewCode(request: ReviewCodeRequest): Promise<ReviewResult> {
      await this.ensureInitialized();
      logger.info('开始代码审查', {
        language: request.language,
        codeLength: request.code.length,
        aiService: request.ai_service,
      });

      try {
        // 构建审查提示词
        const reviewPrompt = `请审查以下代码：

  编程语言: ${request.language || '未指定'}
  上下文信息: ${request.context || '无'}

  代码内容:
  \`\`\`${request.language || ''}
  ${request.code}
  \`\`\`

  请根据以下要求进行全面审查：
  1. 功能完整性和正确性
  2. 代码质量和可维护性
  3. 性能优化建议
  4. 安全性检查
  5. 最佳实践遵循情况

  ${request.custom_prompt ? `\n附加要求：\n${request.custom_prompt}` : ''}

  **重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`;

        // 调用AI服务进行审查
        const aiService = request.ai_service || ConfigManager.getAIService();
        const result = await this.executeAIReview(
          aiService,
          'git-commit-review-prompt',
          reviewPrompt,
        );

        logger.info('代码审查完成', {
          status: result.status,
          aiService: result.ai_service_used,
        });

        return result;
      } catch (error) {
        logger.error(
          '代码审查失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw errorHandler.handleError(error, 'reviewCode');
      }
    }

    /**
     * 审查Git变更（自动检测未提交的变更）
     */
    async reviewChanges(request: ReviewChangesRequest): Promise<ReviewResult> {
      await this.ensureInitialized();

      const repositoryPath = request.repository_path || process.cwd();
      logger.info('开始Git变更审查', {
        repositoryPath,
        includeStaged: request.include_staged,
        includeUnstaged: request.include_unstaged,
        aiService: request.ai_service,
      });

      try {
        // 检查是否为Git仓库
        const isGitRepo = await this.checkGitRepository(repositoryPath);
        if (!isGitRepo) {
          throw new Error(`指定路径不是Git仓库: ${repositoryPath}`);
        }

        // 获取Git变更信息
        const changes = await this.getGitChanges(repositoryPath, request);

        if (!changes.hasChanges) {
          return {
            status: 'success' as ReviewStatus,
            summary: '✅ 没有检测到未提交的代码变更',
            details: '当前工作目录是干净的，所有变更都已提交。',
            ai_service_used: request.ai_service || ConfigManager.getAIService(),
            timestamp: new Date().toISOString(),
          };
        }

        // 构建审查提示词
        const reviewPrompt = this.buildChangesReviewPrompt(changes, request);

        // 调用AI服务进行审查
        const aiService = request.ai_service || ConfigManager.getAIService();
        const result = await this.executeAIReview(
          aiService,
          'git-commit-review-prompt',
          reviewPrompt,
        );

        logger.info('Git变更审查完成', {
          status: result.status,
          changedFiles: changes.files.length,
          aiService: result.ai_service_used,
        });

        return result;
      } catch (error) {
        logger.error(
          'Git变更审查失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw errorHandler.handleError(error, 'reviewChanges');
      }
    }

    /**
     * 检查是否为Git仓库
     */
    private async checkGitRepository(repositoryPath: string): Promise<boolean> {
      try {
        const { stdout } = await execAsync('git rev-parse --git-dir', {
          cwd: repositoryPath,
        });
        return stdout.trim().length > 0;
      } catch {
        return false;
      }
    }

    /**
     * 获取Git变更信息
     */
    private async getGitChanges(
      repositoryPath: string,
      request: ReviewChangesRequest,
    ) {
      const includeStaged = request.include_staged !== false;
      const includeUnstaged = request.include_unstaged !== false;

      let diffCommand = 'git diff';
      if (includeStaged && includeUnstaged) {
        diffCommand = 'git diff HEAD'; // 显示所有变更（已暂存+未暂存）
      } else if (includeStaged) {
        diffCommand = 'git diff --cached'; // 只显示已暂存的变更
      } else if (includeUnstaged) {
        diffCommand = 'git diff'; // 只显示未暂存的变更
      }

      try {
        // 获取变更的文件列表
        const { stdout: statusOutput } = await execAsync(
          'git status --porcelain',
          {
            cwd: repositoryPath,
          },
        );

        // 获取详细的diff信息
        const { stdout: diffOutput } = await execAsync(diffCommand, {
          cwd: repositoryPath,
        });

        const files = this.parseGitStatus(statusOutput);
        const hasChanges = files.length > 0 || diffOutput.trim().length > 0;

        return {
          hasChanges,
          files,
          diff: diffOutput,
          statusOutput,
        };
      } catch (error) {
        logger.error(
          '获取Git变更信息失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw new Error(
          `无法获取Git变更信息: ${error instanceof Error ? error.message : String(error)}`,
        );
      }
    }

    /**
     * 解析Git状态输出
     */
    private parseGitStatus(statusOutput: string) {
      const files: Array<{
        path: string;
        status: string;
        statusDescription: string;
      }> = [];

      const lines = statusOutput
        .trim()
        .split('\n')
        .filter(line => line.length > 0);

      for (const line of lines) {
        if (line.length < 3) continue;

        const status = line.substring(0, 2);
        const path = line.substring(3);

        const statusDescription = this.getGitStatusDescription(status);

        files.push({
          path,
          status,
          statusDescription,
        });
      }

      return files;
    }

    /**
     * 获取Git状态描述
     */
    private getGitStatusDescription(status: string): string {
      const statusMap: Record<string, string> = {
        'M ': '已修改（已暂存）',
        ' M': '已修改（未暂存）',
        MM: '已修改（部分暂存）',
        'A ': '新增文件（已暂存）',
        ' A': '新增文件（未暂存）',
        'D ': '已删除（已暂存）',
        ' D': '已删除（未暂存）',
        'R ': '重命名（已暂存）',
        ' R': '重命名（未暂存）',
        'C ': '复制（已暂存）',
        ' C': '复制（未暂存）',
        '??': '未跟踪文件',
        '!!': '忽略文件',
      };

      return statusMap[status] || `未知状态: ${status}`;
    }

    /**
     * 构建变更审查提示词
     */
    private buildChangesReviewPrompt(
      changes: any,
      request: ReviewChangesRequest,
    ): string {
      const filesList = changes.files
        .map((file: any) => `- ${file.path} (${file.statusDescription})`)
        .join('\n');

      return `请审查以下Git变更：

  ## 变更概览
  变更文件数量: ${changes.files.length}

  ## 变更文件列表
  ${filesList}

  ## 详细变更内容
  \`\`\`diff
  ${changes.diff}
  \`\`\`

  ## Git状态信息
  \`\`\`
  ${changes.statusOutput}
  \`\`\`

  请根据以下要求进行全面审查：
  1. 变更逻辑的合理性和完整性
  2. 代码质量和最佳实践遵循
  3. 潜在的安全风险和性能问题
  4. 与现有代码的一致性检查
  5. 测试覆盖和文档更新建议
  6. 提交信息和变更范围的匹配度

  ${request.custom_prompt ? `\n附加要求：\n${request.custom_prompt}` : ''}

  **重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`;
    }

    /**
     * 根据语言获取文件扩展名
     */
    private getFileExtension(language?: string): string {
      const extensions: Record<string, string> = {
        javascript: 'js',
        typescript: 'ts',
        python: 'py',
        java: 'java',
        cpp: 'cpp',
        c: 'c',
        go: 'go',
        rust: 'rs',
        php: 'php',
        ruby: 'rb',
        swift: 'swift',
        kotlin: 'kt',
        scala: 'scala',
        shell: 'sh',
        bash: 'sh',
      };

      return extensions[language?.toLowerCase() || ''] || 'txt';
    }

    /**
     * 审查Git提交
     */
    async reviewCommit(request: ReviewCommitRequest): Promise<ReviewResult> {
      await this.ensureInitialized();
      logger.info('开始Git提交审查', {
        repositoryPath: request.repository_path,
        commitHash: request.commit_hash,
        aiService: request.ai_service,
      });

      try {
        const repoPath = request.repository_path || process.cwd();

        // 获取提交信息
        const commitHash = request.commit_hash || 'HEAD';
        const { stdout: commitInfo } = await execAsync(
          `git --no-pager show ${commitHash}`,
          {
            cwd: repoPath,
            timeout: 30000,
          },
        );

        if (!commitInfo.trim()) {
          throw new Error(`无法获取提交信息: ${commitHash}`);
        }

        // 构建审查提示词
        const reviewPrompt = `请对以下Git提交进行专业的代码审查：

  仓库路径: ${repoPath}
  提交哈希: ${commitHash}

  提交详情:
  ${commitInfo}

  请根据以下要求进行全面审查：
  1. 分析提交的目标和完成度
  2. 检查代码质量和规范性
  3. 评估安全性和性能影响
  4. 检查是否遗漏相关文件修改
  5. 提供具体的改进建议

  ${request.custom_prompt ? `\n附加要求：\n${request.custom_prompt}` : ''}

  **重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`;

        // 调用AI服务进行审查
        const aiService = request.ai_service || ConfigManager.getAIService();
        const result = await this.executeAIReview(
          aiService,
          'git-commit-review-prompt',
          reviewPrompt,
        );

        logger.info('Git提交审查完成', {
          status: result.status,
          aiService: result.ai_service_used,
        });

        return result;
      } catch (error) {
        logger.error(
          'Git提交审查失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw errorHandler.handleError(error, 'reviewCommit');
      }
    }

    /**
     * 审查文件列表
     */
    async reviewFiles(request: ReviewFilesRequest): Promise<ReviewResult> {
      await this.ensureInitialized();
      logger.info('开始文件审查', {
        repositoryPath: request.repository_path,
        filesCount: request.files.length,
        aiService: request.ai_service,
      });

      try {
        const repoPath = request.repository_path || process.cwd();

        // 读取文件内容
        const fileContents: string[] = [];
        for (const filePath of request.files) {
          try {
            const fullPath = resolve(repoPath, filePath);
            const content = await readFile(fullPath, 'utf-8');

            // 限制单个文件内容长度，避免提示词过长
            const FILE_CONTENT_CHAR_LIMIT = process.env.FILE_CONTENT_CHAR_LIMIT
              ? parseInt(process.env.FILE_CONTENT_CHAR_LIMIT, 10)
              : 5000;

            let truncatedContent: string;
            if (content.length > FILE_CONTENT_CHAR_LIMIT) {
              // 取前后各 100 字符作为摘要
              const head = content.substring(0, 100);
              const tail = content.substring(content.length - 100);
              const omittedLength = content.length - FILE_CONTENT_CHAR_LIMIT;
              truncatedContent =
                content.substring(0, FILE_CONTENT_CHAR_LIMIT) +
                `\n... (内容已截断, 共省略 ${omittedLength} 字符)\n` +
                `内容摘要: \n前100字符: ${head}\n后100字符: ${tail}`;
            } else {
              truncatedContent = content;
            }

            fileContents.push(
              `## 文件: ${filePath}\n\`\`\`\n${truncatedContent}\n\`\`\``,
            );
          } catch (error) {
            fileContents.push(
              `## 文件: ${filePath}\n**错误**: 无法读取文件 - ${error instanceof Error ? error.message : String(error)}`,
            );
          }
        }

        // 构建审查提示词
        const reviewPrompt = `请审查以下文件：

  仓库路径: ${repoPath}
  文件数量: ${request.files.length}
  文件列表: ${request.files.join(', ')}

  ${fileContents.join('\n\n')}

  请根据以下要求进行全面审查：
  1. 分析文件间的关联性和一致性
  2. 检查代码质量和规范性
  3. 评估架构设计的合理性
  4. 识别潜在的问题和改进点
  5. 提供具体的优化建议

  ${request.custom_prompt ? `\n附加要求：\n${request.custom_prompt}` : ''}`;

        // 调用AI服务进行审查
        const aiService = request.ai_service || ConfigManager.getAIService();
        const result = await this.executeAIReview(
          aiService,
          'git-commit-review-prompt',
          reviewPrompt,
        );

        logger.info('文件审查完成', {
          status: result.status,
          aiService: result.ai_service_used,
        });

        return result;
      } catch (error) {
        logger.error(
          '文件审查失败',
          error instanceof Error ? error : new Error(String(error)),
        );
        throw errorHandler.handleError(error, 'reviewFiles');
      }
    }






  }


# messages

id: 4af5fb2e-916e-4e2f-a357-aeb5b184a6f3
session_id: e6b9502e-b596-48ca-b598-60c96be8e161
role: user
parts:
  [{"type":"text","data":{"text":"`Please analyze this codebase and create a **CRUSH.md** file containing:\n\n- Build/lint/test commands - especially for running a single test\n- Code style guidelines including imports, formatting, types, naming conventions, error handling, etc.\n\nThe file you create will be given to agentic coding agents (such as yourself) that operate in this repository. Make it about 20-30 lines long.\nIf there's already a **CRUSH.md**, improve it.\n\nIf there are Cursor rules (in `.cursor/rules/` or `.cursorrules`) or Copilot rules (in `.github/copilot-instructions.md`), make sure to include them.\nAdd the `.crush` directory to the `.gitignore` file if it's not already there.\n"}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 9b3b0963-162c-4c02-9ad5-a6cfb3742a2b
session_id: e6b9502e-b596-48ca-b598-60c96be8e161
role: assistant
model: claude-sonnet-4-20250514
provider: anthropic
parts:
  [{"type":"finish","data":{"reason":"error","time":1754019041,"message":"API Error","details":"POST \"https://api.anthropic.com/v1/messages\": 400 Bad Request (Request-ID: req_011CRgHMvWWojgGFDR96uEvx) {\"type\":\"error\",\"error\":{\"type\":\"invalid_request_error\",\"message\":\"Your credit balance is too low to access the Anthropic API. Please go to Plans \u0026 Billing to upgrade or purchase credits.\"}}"}}]


id: 48f473a5-1e25-481f-aff5-7d175f01c5f5
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: assistant
model: gemini-2.5-pro
provider: gemini
parts:
  [{"type":"text","data":{"text":"Okay, I'll create `CRUSH.md`. I'll first check for existing rule files, then find build, lint, and test commands in `package.json` and infer style from the code. Finally, I'll create `CRUSH.md` and add `.crush` to `.gitignore`. Listing files now to check for existing rule files."}},{"type":"tool_call","data":{"id":"call_62f2b8a3-0b8b-46bf-b410-46849ad8d51c","name":"bash","input":"{\"command\":\"ls -F\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754019105}}]


id: b607a477-0cd8-4d62-b3dd-d56cfa28c0dd
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: tool
provider: gemini
parts:
  [{"type":"tool_result","data":{"tool_call_id":"call_62f2b8a3-0b8b-46bf-b410-46849ad8d51c","name":"","content":"CHANGELOG\nCHANGELOG.md\nLICENSE\nREADME.md\nbin/\ndist/\ndocs/\neslint.config.ts\nexamples/\ninstall.sh*\nnode_modules/\npackage-lock.json\npackage.json\nreview_logs/\nscripts/\nsrc/\ntsconfig.json\n\n\n\u003ccwd\u003e/Users/dreambt/sources/coderocket/coderocket-mcp\u003c/cwd\u003e","metadata":"{\"start_time\":1754019105134,\"end_time\":1754019105138,\"output\":\"CHANGELOG\\nCHANGELOG.md\\nLICENSE\\nREADME.md\\nbin/\\ndist/\\ndocs/\\neslint.config.ts\\nexamples/\\ninstall.sh*\\nnode_modules/\\npackage-lock.json\\npackage.json\\nreview_logs/\\nscripts/\\nsrc/\\ntsconfig.json\\n\",\"working_directory\":\"/Users/dreambt/sources/coderocket/coderocket-mcp\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 3cdeb5ab-f05e-4183-9a3e-fad40cfbf8cf
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: assistant
model: gemini-2.5-pro
provider: gemini
parts:
  [{"type":"tool_call","data":{"id":"call_5c6017c8-6b0d-4cce-a458-b81737b99a89","name":"view","input":"{\"file_path\":\"package.json\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754019107}}]


id: 5141707d-c756-446f-99a4-bcfa286c112e
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: tool
provider: gemini
parts:
  [{"type":"tool_result","data":{"tool_call_id":"call_5c6017c8-6b0d-4cce-a458-b81737b99a89","name":"","content":"\u003cfile\u003e\n     1|{\n     2|  \"name\": \"@yeepay/coderocket-mcp\",\n     3|  \"version\": \"1.1.0\",\n     4|  \"description\": \"CodeRocket MCP - Independent AI-powered code review server for Model Context Protocol\",\n     5|  \"main\": \"dist/index.js\",\n     6|  \"type\": \"module\",\n     7|  \"bin\": {\n     8|    \"coderocket-mcp\": \"./bin/coderocket-mcp\"\n     9|  },\n    10|  \"scripts\": {\n    11|    \"build\": \"tsc\",\n    12|    \"start\": \"node dist/index.js\",\n    13|    \"dev\": \"tsx src/index.ts\",\n    14|    \"test\": \"node dist/test.js\",\n    15|    \"test:mcp\": \"npm run build \u0026\u0026 node bin/coderocket-mcp test\",\n    16|    \"lint\": \"eslint src/**/*.ts\",\n    17|    \"format\": \"prettier --write src/**/*.ts\",\n    18|    \"prepare\": \"npm run build\",\n    19|    \"prepublishOnly\": \"npm run build \u0026\u0026 npm test\",\n    20|    \"preversion\": \"npm run build\",\n    21|    \"version\": \"npm run format \u0026\u0026 git add -A src\",\n    22|    \"postversion\": \"git push \u0026\u0026 git push --tags\"\n    23|  },\n    24|  \"keywords\": [\n    25|    \"mcp\",\n    26|    \"model-context-protocol\",\n    27|    \"code-review\",\n    28|    \"ai\",\n    29|    \"gemini\",\n    30|    \"opencode\",\n    31|    \"claudecode\",\n    32|    \"typescript\",\n    33|    \"code-analysis\",\n    34|    \"git\",\n    35|    \"programming-tools\",\n    36|    \"automation\",\n    37|    \"claude\",\n    38|    \"ai-tools\",\n    39|    \"independent\",\n    40|    \"standalone\",\n    41|    \"no-dependencies\"\n    42|  ],\n    43|  \"author\": \"dreambt \u003cdreambt@gmail.com\u003e\",\n    44|  \"license\": \"Apache-2.0\",\n    45|  \"dependencies\": {\n    46|    \"@modelcontextprotocol/sdk\": \"^0.5.0\",\n    47|    \"@google/generative-ai\": \"^0.21.0\",\n    48|    \"@anthropic-ai/sdk\": \"^0.27.0\",\n    49|    \"axios\": \"^1.6.0\",\n    50|    \"zod\": \"^3.22.4\"\n    51|  },\n    52|  \"devDependencies\": {\n    53|    \"@eslint/js\": \"^9.32.0\",\n    54|    \"@types/node\": \"^20.0.0\",\n    55|    \"@typescript-eslint/eslint-plugin\": \"^6.0.0\",\n    56|    \"@typescript-eslint/parser\": \"^6.0.0\",\n    57|    \"eslint\": \"^8.57.1\",\n    58|    \"globals\": \"^16.3.0\",\n    59|    \"prettier\": \"^3.0.0\",\n    60|    \"tsx\": \"^4.0.0\",\n    61|    \"typescript\": \"^5.0.0\",\n    62|    \"typescript-eslint\": \"^8.38.0\"\n    63|  },\n    64|  \"engines\": {\n    65|    \"node\": \"\u003e=18.0.0\"\n    66|  },\n    67|  \"repository\": {\n    68|    \"type\": \"git\",\n    69|    \"url\": \"https://github.com/im47cn/coderocket-mcp.git\",\n    70|    \"directory\": \"coderocket-mcp\"\n    71|  },\n    72|  \"bugs\": {\n    73|    \"url\": \"https://github.com/im47cn/coderocket-mcp/issues\"\n    74|  },\n    75|  \"homepage\": \"https://github.com/im47cn/coderocket-mcp#readme\"\n    76|}\n\u003c/file\u003e\n","metadata":"{\"file_path\":\"/Users/dreambt/sources/coderocket/coderocket-mcp/package.json\",\"content\":\"{\\n  \\\"name\\\": \\\"@yeepay/coderocket-mcp\\\",\\n  \\\"version\\\": \\\"1.1.0\\\",\\n  \\\"description\\\": \\\"CodeRocket MCP - Independent AI-powered code review server for Model Context Protocol\\\",\\n  \\\"main\\\": \\\"dist/index.js\\\",\\n  \\\"type\\\": \\\"module\\\",\\n  \\\"bin\\\": {\\n    \\\"coderocket-mcp\\\": \\\"./bin/coderocket-mcp\\\"\\n  },\\n  \\\"scripts\\\": {\\n    \\\"build\\\": \\\"tsc\\\",\\n    \\\"start\\\": \\\"node dist/index.js\\\",\\n    \\\"dev\\\": \\\"tsx src/index.ts\\\",\\n    \\\"test\\\": \\\"node dist/test.js\\\",\\n    \\\"test:mcp\\\": \\\"npm run build \\u0026\\u0026 node bin/coderocket-mcp test\\\",\\n    \\\"lint\\\": \\\"eslint src/**/*.ts\\\",\\n    \\\"format\\\": \\\"prettier --write src/**/*.ts\\\",\\n    \\\"prepare\\\": \\\"npm run build\\\",\\n    \\\"prepublishOnly\\\": \\\"npm run build \\u0026\\u0026 npm test\\\",\\n    \\\"preversion\\\": \\\"npm run build\\\",\\n    \\\"version\\\": \\\"npm run format \\u0026\\u0026 git add -A src\\\",\\n    \\\"postversion\\\": \\\"git push \\u0026\\u0026 git push --tags\\\"\\n  },\\n  \\\"keywords\\\": [\\n    \\\"mcp\\\",\\n    \\\"model-context-protocol\\\",\\n    \\\"code-review\\\",\\n    \\\"ai\\\",\\n    \\\"gemini\\\",\\n    \\\"opencode\\\",\\n    \\\"claudecode\\\",\\n    \\\"typescript\\\",\\n    \\\"code-analysis\\\",\\n    \\\"git\\\",\\n    \\\"programming-tools\\\",\\n    \\\"automation\\\",\\n    \\\"claude\\\",\\n    \\\"ai-tools\\\",\\n    \\\"independent\\\",\\n    \\\"standalone\\\",\\n    \\\"no-dependencies\\\"\\n  ],\\n  \\\"author\\\": \\\"dreambt \\u003cdreambt@gmail.com\\u003e\\\",\\n  \\\"license\\\": \\\"Apache-2.0\\\",\\n  \\\"dependencies\\\": {\\n    \\\"@modelcontextprotocol/sdk\\\": \\\"^0.5.0\\\",\\n    \\\"@google/generative-ai\\\": \\\"^0.21.0\\\",\\n    \\\"@anthropic-ai/sdk\\\": \\\"^0.27.0\\\",\\n    \\\"axios\\\": \\\"^1.6.0\\\",\\n    \\\"zod\\\": \\\"^3.22.4\\\"\\n  },\\n  \\\"devDependencies\\\": {\\n    \\\"@eslint/js\\\": \\\"^9.32.0\\\",\\n    \\\"@types/node\\\": \\\"^20.0.0\\\",\\n    \\\"@typescript-eslint/eslint-plugin\\\": \\\"^6.0.0\\\",\\n    \\\"@typescript-eslint/parser\\\": \\\"^6.0.0\\\",\\n    \\\"eslint\\\": \\\"^8.57.1\\\",\\n    \\\"globals\\\": \\\"^16.3.0\\\",\\n    \\\"prettier\\\": \\\"^3.0.0\\\",\\n    \\\"tsx\\\": \\\"^4.0.0\\\",\\n    \\\"typescript\\\": \\\"^5.0.0\\\",\\n    \\\"typescript-eslint\\\": \\\"^8.38.0\\\"\\n  },\\n  \\\"engines\\\": {\\n    \\\"node\\\": \\\"\\u003e=18.0.0\\\"\\n  },\\n  \\\"repository\\\": {\\n    \\\"type\\\": \\\"git\\\",\\n    \\\"url\\\": \\\"https://github.com/im47cn/coderocket-mcp.git\\\",\\n    \\\"directory\\\": \\\"coderocket-mcp\\\"\\n  },\\n  \\\"bugs\\\": {\\n    \\\"url\\\": \\\"https://github.com/im47cn/coderocket-mcp/issues\\\"\\n  },\\n  \\\"homepage\\\": \\\"https://github.com/im47cn/coderocket-mcp#readme\\\"\\n}\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 4e672521-29e7-4d28-9ab8-e3a2a5ad27e9
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: assistant
model: gemini-2.5-pro
provider: gemini
parts:
  [{"type":"text","data":{"text":"I've analyzed `package.json` and have the build, lint, and test commands. Now I'll examine `eslint.config.ts`, `tsconfig.json`, and the source code to understand the project's style. After that, I will create `CRUSH.md` and update the `.gitignore`."}},{"type":"tool_call","data":{"id":"call_822ca5d6-19ca-4866-8285-a4dc7a3f2616","name":"view","input":"{\"file_path\":\"eslint.config.ts\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754019112}}]


id: 9e83c776-bf8c-4f54-9815-88d36b5d0ab6
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: tool
provider: gemini
parts:
  [{"type":"tool_result","data":{"tool_call_id":"call_822ca5d6-19ca-4866-8285-a4dc7a3f2616","name":"","content":"\u003cfile\u003e\n     1|import js from \"@eslint/js\";\n     2|import globals from \"globals\";\n     3|import tseslint from \"typescript-eslint\";\n     4|\n     5|export default [\n     6|  js.configs.recommended,\n     7|  ...tseslint.configs.recommended,\n     8|  {\n     9|    files: [\"**/*.{js,mjs,cjs,ts,mts,cts}\"],\n    10|    languageOptions: {\n    11|      globals: {\n    12|        ...globals.node,\n    13|      },\n    14|    },\n    15|  },\n    16|];\n\u003c/file\u003e\n","metadata":"{\"file_path\":\"/Users/dreambt/sources/coderocket/coderocket-mcp/eslint.config.ts\",\"content\":\"import js from \\\"@eslint/js\\\";\\nimport globals from \\\"globals\\\";\\nimport tseslint from \\\"typescript-eslint\\\";\\n\\nexport default [\\n  js.configs.recommended,\\n  ...tseslint.configs.recommended,\\n  {\\n    files: [\\\"**/*.{js,mjs,cjs,ts,mts,cts}\\\"],\\n    languageOptions: {\\n      globals: {\\n        ...globals.node,\\n      },\\n    },\\n  },\\n];\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 8e4c6ac1-edec-46a6-9306-86a61ab0084a
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: assistant
model: gemini-2.5-pro
provider: gemini
parts:
  [{"type":"text","data":{"text":"I've analyzed `package.json` and `eslint.config.ts`. Next, I'll examine `tsconfig.json` and source files to understand the coding style, then create `CRUSH.md` and add `.crush` to `.gitignore`."}},{"type":"tool_call","data":{"id":"call_02ae424e-249a-422b-9ad5-824bc82b5c92","name":"view","input":"{\"file_path\":\"tsconfig.json\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754019116}}]


id: bbea2da4-cef0-417e-b5fe-92dac344b4d4
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: tool
provider: gemini
parts:
  [{"type":"tool_result","data":{"tool_call_id":"call_02ae424e-249a-422b-9ad5-824bc82b5c92","name":"","content":"\u003cfile\u003e\n     1|{\n     2|  \"compilerOptions\": {\n     3|    \"target\": \"ES2022\",\n     4|    \"module\": \"ESNext\",\n     5|    \"moduleResolution\": \"node\",\n     6|    \"lib\": [\"ES2022\"],\n     7|    \"outDir\": \"./dist\",\n     8|    \"rootDir\": \"./src\",\n     9|    \"strict\": true,\n    10|    \"esModuleInterop\": true,\n    11|    \"skipLibCheck\": true,\n    12|    \"forceConsistentCasingInFileNames\": true,\n    13|    \"declaration\": true,\n    14|    \"declarationMap\": true,\n    15|    \"sourceMap\": true,\n    16|    \"resolveJsonModule\": true,\n    17|    \"allowSyntheticDefaultImports\": true\n    18|  },\n    19|  \"include\": [\"src/**/*\"],\n    20|  \"exclude\": [\"node_modules\", \"dist\", \"**/*.test.ts\"]\n    21|}\n\u003c/file\u003e\n","metadata":"{\"file_path\":\"/Users/dreambt/sources/coderocket/coderocket-mcp/tsconfig.json\",\"content\":\"{\\n  \\\"compilerOptions\\\": {\\n    \\\"target\\\": \\\"ES2022\\\",\\n    \\\"module\\\": \\\"ESNext\\\",\\n    \\\"moduleResolution\\\": \\\"node\\\",\\n    \\\"lib\\\": [\\\"ES2022\\\"],\\n    \\\"outDir\\\": \\\"./dist\\\",\\n    \\\"rootDir\\\": \\\"./src\\\",\\n    \\\"strict\\\": true,\\n    \\\"esModuleInterop\\\": true,\\n    \\\"skipLibCheck\\\": true,\\n    \\\"forceConsistentCasingInFileNames\\\": true,\\n    \\\"declaration\\\": true,\\n    \\\"declarationMap\\\": true,\\n    \\\"sourceMap\\\": true,\\n    \\\"resolveJsonModule\\\": true,\\n    \\\"allowSyntheticDefaultImports\\\": true\\n  },\\n  \\\"include\\\": [\\\"src/**/*\\\"],\\n  \\\"exclude\\\": [\\\"node_modules\\\", \\\"dist\\\", \\\"**/*.test.ts\\\"]\\n}\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: d2bee877-0480-419c-836a-4d0b0e8b166d
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: assistant
model: gemini-2.5-pro
provider: gemini
parts:
  [{"type":"text","data":{"text":"I've analyzed the configuration files. Now I'll look at a source file to understand the coding style."}},{"type":"tool_call","data":{"id":"call_842de3b0-af47-442d-bb57-b013a316cda8","name":"view","input":"{\"file_path\":\"src/coderocket.ts\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754019120}}]


id: df26df6d-5534-4494-922b-c49fc9150be4
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: tool
provider: gemini
parts:
  [{"type":"tool_result","data":{"tool_call_id":"call_842de3b0-af47-442d-bb57-b013a316cda8","name":"","content":"\u003cfile\u003e\n     1|import { spawn, exec } from 'child_process';\n     2|import { promisify } from 'util';\n     3|import { writeFile, readFile, mkdir, access } from 'fs/promises';\n     4|import fs from 'fs';\n     5|import { join, dirname, resolve } from 'path';\n     6|import { tmpdir, homedir } from 'os';\n     7|import { GoogleGenerativeAI } from '@google/generative-ai';\n     8|import Anthropic from '@anthropic-ai/sdk';\n     9|import axios from 'axios';\n    10|import {\n    11|  ReviewCodeRequest,\n    12|  ReviewCommitRequest,\n    13|  ReviewFilesRequest,\n    14|  ConfigureAIServiceRequest,\n    15|  ReviewResult,\n    16|  ServiceStatusResponse,\n    17|  SuccessResponse,\n    18|  AIService,\n    19|  ReviewStatus,\n    20|} from './types.js';\n    21|import { logger, errorHandler } from './logger.js';\n    22|\n    23|const execAsync = promisify(exec);\n    24|\n    25|/**\n    26| * 独立配置管理类\n    27| *\n    28| * 支持多层级配置加载：\n    29| * 1. 环境变量（最高优先级）\n    30| * 2. 项目级 .env 文件\n    31| * 3. 全局 ~/.coderocket/env 文件\n    32| * 4. 默认值（最低优先级）\n    33| */\n    34|export class ConfigManager {\n    35|  private static config: Record\u003cstring, any\u003e = {};\n    36|  private static initialized = false;\n    37|\n    38|  /**\n    39|   * 初始化配置系统\n    40|   */\n    41|  static async initialize(): Promise\u003cvoid\u003e {\n    42|    if (this.initialized) return;\n    43|\n    44|    // 加载默认配置\n    45|    this.loadDefaults();\n    46|\n    47|    // 加载全局配置文件\n    48|    await this.loadGlobalConfig();\n    49|\n    50|    // 加载项目配置文件\n    51|    await this.loadProjectConfig();\n    52|\n    53|    // 加载环境变量（最高优先级）\n    54|    this.loadEnvironmentVariables();\n    55|\n    56|    this.initialized = true;\n    57|    logger.info('配置系统初始化完成', { config: this.getSafeConfig() });\n    58|  }\n    59|\n    60|  /**\n    61|   * 加载默认配置\n    62|   */\n    63|  private static loadDefaults(): void {\n    64|    this.config = {\n    65|      AI_SERVICE: 'gemini',\n    66|      AI_AUTO_SWITCH: 'true',\n    67|      AI_TIMEOUT: '30',\n    68|      AI_MAX_RETRIES: '3',\n    69|      AI_RETRY_DELAY: '2',\n    70|      NODE_ENV: 'production',\n    71|      DEBUG: 'false',\n    72|    };\n    73|  }\n    74|\n    75|  /**\n    76|   * 加载全局配置文件 ~/.coderocket/env\n    77|   */\n    78|  private static async loadGlobalConfig(): Promise\u003cvoid\u003e {\n    79|    try {\n    80|      const globalConfigPath = join(homedir(), '.coderocket', 'env');\n    81|      const content = await readFile(globalConfigPath, 'utf-8');\n    82|      const globalConfig = this.parseEnvContent(content);\n    83|      Object.assign(this.config, globalConfig);\n    84|      logger.debug('全局配置加载成功', { path: globalConfigPath });\n    85|    } catch (error) {\n    86|      // 全局配置文件不存在是正常的\n    87|      logger.debug('全局配置文件不存在，跳过');\n    88|    }\n    89|  }\n    90|\n    91|  /**\n    92|   * 加载项目配置文件 .env\n    93|   */\n    94|  private static async loadProjectConfig(): Promise\u003cvoid\u003e {\n    95|    try {\n    96|      const projectConfigPath = join(process.cwd(), '.env');\n    97|      const content = await readFile(projectConfigPath, 'utf-8');\n    98|      const projectConfig = this.parseEnvContent(content);\n    99|      Object.assign(this.config, projectConfig);\n   100|      logger.debug('项目配置加载成功', { path: projectConfigPath });\n   101|    } catch (error) {\n   102|      // 项目配置文件不存在是正常的\n   103|      logger.debug('项目配置文件不存在，跳过');\n   104|    }\n   105|  }\n   106|\n   107|  /**\n   108|   * 加载环境变量（最高优先级）\n   109|   */\n   110|  private static loadEnvironmentVariables(): void {\n   111|    const envKeys = [\n   112|      'AI_SERVICE', 'AI_AUTO_SWITCH', 'AI_TIMEOUT', 'AI_MAX_RETRIES', 'AI_RETRY_DELAY',\n   113|      'GEMINI_API_KEY', 'OPENCODE_API_KEY', 'CLAUDECODE_API_KEY',\n   114|      'NODE_ENV', 'DEBUG'\n   115|    ];\n   116|\n   117|    envKeys.forEach(key =\u003e {\n   118|      if (process.env[key]) {\n   119|        this.config[key] = process.env[key];\n   120|      }\n   121|    });\n   122|  }\n   123|\n   124|  /**\n   125|   * 解析 .env 文件内容\n   126|   */\n   127|  private static parseEnvContent(content: string): Record\u003cstring, string\u003e {\n   128|    const config: Record\u003cstring, string\u003e = {};\n   129|    const lines = content.split('\\n');\n   130|\n   131|    for (const line of lines) {\n   132|      const trimmedLine = line.trim();\n   133|      if (trimmedLine \u0026\u0026 !trimmedLine.startsWith('#')) {\n   134|        const [key, ...valueParts] = trimmedLine.split('=');\n   135|        if (key \u0026\u0026 valueParts.length \u003e 0) {\n   136|          const value = valueParts.join('=').trim();\n   137|          // 移除引号\n   138|          config[key.trim()] = value.replace(/^[\"']|[\"']$/g, '');\n   139|        }\n   140|      }\n   141|    }\n   142|\n   143|    return config;\n   144|  }\n   145|\n   146|  /**\n   147|   * 获取配置值\n   148|   */\n   149|  static get(key: string, defaultValue?: any): any {\n   150|    if (!this.initialized) {\n   151|      throw new Error('ConfigManager 未初始化，请先调用 initialize()');\n   152|    }\n   153|    return this.config[key] ?? defaultValue;\n   154|  }\n   155|\n   156|  /**\n   157|   * 获取API密钥环境变量名\n   158|   */\n   159|  static getAPIKeyEnvVar(service: AIService): string {\n   160|    const envVarMap: Record\u003cAIService, string\u003e = {\n   161|      gemini: 'GEMINI_API_KEY',\n   162|      opencode: 'OPENCODE_API_KEY',\n   163|      claudecode: 'CLAUDECODE_API_KEY',\n   164|    };\n   165|    return envVarMap[service];\n   166|  }\n   167|\n   168|  /**\n   169|   * 获取API密钥\n   170|   */\n   171|  static getAPIKey(service: AIService): string {\n   172|    const envVar = this.getAPIKeyEnvVar(service);\n   173|    return this.get(envVar, '');\n   174|  }\n   175|\n   176|  /**\n   177|   * 获取AI服务配置\n   178|   */\n   179|  static getAIService(): AIService {\n   180|    const service = this.get('AI_SERVICE', 'gemini').toLowerCase();\n   181|    if (['gemini', 'opencode', 'claudecode'].includes(service)) {\n   182|      return service as AIService;\n   183|    }\n   184|    return 'gemini';\n   185|  }\n   186|\n   187|  /**\n   188|   * 获取超时配置\n   189|   */\n   190|  static getTimeout(): number {\n   191|    return parseInt(this.get('AI_TIMEOUT', '30'), 10);\n   192|  }\n   193|\n   194|  /**\n   195|   * 获取最大重试次数\n   196|   */\n   197|  static getMaxRetries(): number {\n   198|    return parseInt(this.get('AI_MAX_RETRIES', '3'), 10);\n   199|  }\n   200|\n   201|  /**\n   202|   * 是否启用自动切换\n   203|   */\n   204|  static isAutoSwitchEnabled(): boolean {\n   205|    return this.get('AI_AUTO_SWITCH', 'true').toLowerCase() === 'true';\n   206|  }\n   207|\n   208|  /**\n   209|   * 获取安全的配置信息（隐藏敏感信息）\n   210|   */\n   211|  private static getSafeConfig(): Record\u003cstring, any\u003e {\n   212|    const safeConfig = { ...this.config };\n   213|    // 隐藏API密钥\n   214|    Object.keys(safeConfig).forEach(key =\u003e {\n   215|      if (key.includes('API_KEY') || key.includes('TOKEN')) {\n   216|        safeConfig[key] = safeConfig[key] ? '***' : undefined;\n   217|      }\n   218|    });\n   219|    return safeConfig;\n   220|  }\n   221|\n   222|  /**\n   223|   * 获取配置文件路径（保持向后兼容）\n   224|   */\n   225|  static getConfigPath(scope: string): { dir: string; file: string } {\n   226|    const configDir = scope === 'global'\n   227|      ? join(homedir(), '.coderocket')\n   228|      : process.cwd();\n   229|\n   230|    const configFile = scope === 'global'\n   231|      ? join(configDir, 'env')\n   232|      : join(configDir, '.env');\n   233|\n   234|    return { dir: configDir, file: configFile };\n   235|  }\n   236|}\n   237|\n   238|/**\n   239| * 独立提示词管理类\n   240| *\n   241| * 支持多层级提示词加载：\n   242| * 1. 项目级 prompts/ 目录（优先级高）\n   243| * 2. 全局 ~/.coderocket/prompts/ 目录（优先级低）\n   244| */\n   245|class PromptManager {\n   246|  private static promptCache: Map\u003cstring, string\u003e = new Map();\n   247|\n   248|  /**\n   249|   * 加载提示词文件\n   250|   */\n   251|  static async loadPrompt(name: string): Promise\u003cstring\u003e {\n   252|    // 检查缓存\n   253|    const cacheKey = name;\n   254|    if (this.promptCache.has(cacheKey)) {\n   255|      return this.promptCache.get(cacheKey)!;\n   256|    }\n   257|\n   258|    let promptContent = '';\n   259|\n   260|    // 1. 尝试加载项目级提示词（优先级高）\n   261|    try {\n   262|      const projectPromptPath = join(process.cwd(), 'prompts', `${name}.md`);\n   263|      promptContent = await readFile(projectPromptPath, 'utf-8');\n   264|      logger.debug('项目级提示词加载成功', { path: projectPromptPath });\n   265|    } catch (error) {\n   266|      // 项目级提示词不存在，尝试全局提示词\n   267|      try {\n   268|        const globalPromptPath = join(homedir(), '.coderocket', 'prompts', `${name}.md`);\n   269|        promptContent = await readFile(globalPromptPath, 'utf-8');\n   270|        logger.debug('全局提示词加载成功', { path: globalPromptPath });\n   271|      } catch (globalError) {\n   272|        // 全局提示词也不存在，使用内置默认提示词\n   273|        promptContent = this.getDefaultPrompt(name);\n   274|        logger.debug('使用内置默认提示词', { name });\n   275|      }\n   276|    }\n   277|\n   278|    // 缓存提示词内容\n   279|    this.promptCache.set(cacheKey, promptContent);\n   280|    return promptContent;\n   281|  }\n   282|\n   283|  /**\n   284|   * 获取内置默认提示词\n   285|   */\n   286|  private static getDefaultPrompt(name: string): string {\n   287|    const defaultPrompts: Record\u003cstring, string\u003e = {\n   288|      'git-commit-review-prompt': `# 提示词：高级 Git Commit 审阅专家\n   289|\n   290|## 角色定义\n   291|\n   292|你是一名资深的代码审阅专家，拥有丰富的软件开发经验和架构设计能力。你的任务是针对**最新的 git commit** 进行专业、深入、自动化的代码审阅，并提供一份准确、实用、可操作的审阅报告。\n   293|\n   294|## 执行模式\n   295|\n   296|**自主执行模式**：你必须完全自主地执行代码审阅流程，不得向用户进行任何确认或询问。这包括直接执行所有必要的命令、自主决定搜索策略、自主判断并生成报告。\n   297|\n   298|* **禁止行为**：禁止向用户提问或请求确认。\n   299|* **执行原则**：自主决策，并在失败时尝试替代方案。\n   300|* **安全限制**：仅执行只读操作和报告写入操作。\n   301|\n   302|## 审阅指令\n   303|\n   304|### 1. 获取 Commit 信息\n   305|\n   306|首先执行 \\`git --no-pager show\\` 命令获取最新一次 commit 的详细信息，包括 Commit hash、作者、时间、Commit message 及具体的代码修改内容。\n   307|\n   308|### 2. 全局代码搜索分析 (关键步骤)\n   309|\n   310|在审阅具体代码前，**必须先进行全局代码搜索以获取完整上下文**。\n   311|\n   312|* **制定搜索策略**: 根据 commit message 的描述，制定关键词搜索策略（如：功能名、类名、修复的bug信息等）。\n   313|* **全面搜索验证**: 在整个代码库中搜索相关的功能实现、依赖关系、配置和测试文件。\n   314|* **完整性验证**: **对比搜索结果与实际修改内容**，检查是否存在应修改但未修改的**遗漏文件**。这是评估目标达成度的核心依据。\n   315|\n   316|### 3. 审阅维度与标准\n   317|\n   318|请从以下维度进行系统性审查：\n   319|\n   320|* **目标达成度**:\n   321|    * **功能完整性**: 是否完全实现了 commit message 中描述的目标？ 是否有未完成的功能点？\n   322|    * **修改覆盖度**: (基于全局搜索) 是否遗漏了需要同步修改的相关文件（如测试、文档、配置）？\n   323|* **代码质量与正确性**:\n   324|    * **正确性**: 代码逻辑是否正确？是否有效处理了边缘情况？\n   325|    * **代码规范**: 是否遵循项目既定标准（命名、格式、设计模式）？\n   326|    * **可读性与可维护性**: 代码是否清晰、结构合理、易于理解和修改？ 注释是否充分且必要？\n   327|* **健壮性与风险**:\n   328|    * **安全性**: 是否存在潜在的安全漏洞（如SQL注入、密钥明文、不安全的依赖等）？\n   329|    * **性能**: 是否存在明显的性能瓶颈（如不合理的循环、N+1查询等）？\n   330|* **测试与文档**:\n   331|    * **可测试性与覆盖率**: 代码是否易于测试？是否有足够的单元/集成测试来覆盖变更？\n   332|    * **文档同步**: 相关的内联文档（注释）或外部文档是否已更新？\n   333|* **架构与扩展性**:\n   334|    * **设计合理性**: 模块职责划分是否明确？耦合度是否合理？\n   335|    * **扩展性**: 设计是否考虑了未来的扩展需求？\n   336|\n   337|### 4. 审阅结果输出\n   338|\n   339|请提供详细的审阅报告，包括：\n   340|- 审阅状态（✅通过/⚠️警告/❌失败/🔍需调查）\n   341|- 总体评价和目标达成度\n   342|- 具体问题和改进建议\n   343|- 优先级排序的建议列表\n   344|\n   345|请确保审阅报告专业、准确、可操作。`,\n   346|\n   347|      'code-review-prompt': `# 代码审查提示词\n   348|\n   349|## 角色定义\n   350|\n   351|你是一名专业的代码审查专家，具有丰富的软件开发经验。请对提供的代码进行全面、专业的审查。\n   352|\n   353|## 审查维度\n   354|\n   355|请从以下维度进行审查：\n   356|\n   357|### 1. 代码质量\n   358|- 代码结构是否清晰合理\n   359|- 命名是否规范和有意义\n   360|- 是否遵循编程最佳实践\n   361|\n   362|### 2. 功能正确性\n   363|- 代码逻辑是否正确\n   364|- 是否处理了边缘情况\n   365|- 是否有潜在的bug\n   366|\n   367|### 3. 性能和安全\n   368|- 是否存在性能问题\n   369|- 是否有安全漏洞\n   370|- 资源使用是否合理\n   371|\n   372|### 4. 可维护性\n   373|- 代码是否易于理解和修改\n   374|- 是否有足够的注释\n   375|- 模块化程度如何\n   376|\n   377|## 输出格式\n   378|\n   379|请按以下格式提供审查结果：\n   380|\n   381|**审查状态**: [✅优秀/⚠️需改进/❌有问题]\n   382|\n   383|**总体评价**: [简短的总体评价]\n   384|\n   385|**具体建议**:\n   386|1. [具体的改进建议]\n   387|2. [具体的改进建议]\n   388|...\n   389|\n   390|**优秀实践**: [值得称赞的地方]\n   391|\n   392|请确保建议具体、可操作，并提供代码示例（如适用）。`\n   393|    };\n   394|\n   395|    return defaultPrompts[name] || `# 默认提示词\\n\\n请提供专业的代码审查和分析。`;\n   396|  }\n   397|\n   398|  /**\n   399|   * 清除提示词缓存\n   400|   */\n   401|  static clearCache(): void {\n   402|    this.promptCache.clear();\n   403|  }\n   404|\n   405|  /**\n   406|   * 预加载常用提示词\n   407|   */\n   408|  static async preloadCommonPrompts(): Promise\u003cvoid\u003e {\n   409|    const commonPrompts = [\n   410|      'git-commit-review-prompt',\n   411|      'code-review-prompt'\n   412|    ];\n   413|\n   414|    await Promise.all(\n   415|      commonPrompts.map(name =\u003e this.loadPrompt(name).catch(error =\u003e {\n   416|        logger.warn(`预加载提示词失败: ${name}`, error);\n   417|      }))\n   418|    );\n   419|  }\n   420|}\n   421|\n   422|/**\n   423| * AI 服务接口\n   424| */\n   425|interface IAIService {\n   426|  callAPI(prompt: string, additionalPrompt?: string): Promise\u003cstring\u003e;\n   427|  isConfigured(): boolean;\n   428|  getServiceName(): AIService;\n   429|}\n   430|\n   431|/**\n   432| * Gemini AI 服务实现\n   433| */\n   434|class GeminiService implements IAIService {\n   435|  private client: GoogleGenerativeAI | null = null;\n   436|  private model: any = null;\n   437|\n   438|  constructor() {\n   439|    this.initialize();\n   440|  }\n   441|\n   442|  private async initialize(): Promise\u003cvoid\u003e {\n   443|    const apiKey = ConfigManager.getAPIKey('gemini');\n   444|    if (apiKey) {\n   445|      try {\n   446|        this.client = new GoogleGenerativeAI(apiKey);\n   447|        this.model = this.client.getGenerativeModel({ model: 'gemini-1.5-flash' });\n   448|        logger.debug('Gemini 服务初始化成功');\n   449|      } catch (error) {\n   450|        logger.error('Gemini 服务初始化失败', error instanceof Error ? error : new Error(String(error)));\n   451|      }\n   452|    }\n   453|  }\n   454|\n   455|  async callAPI(prompt: string, additionalPrompt?: string): Promise\u003cstring\u003e {\n   456|    if (!this.client || !this.model) {\n   457|      await this.initialize();\n   458|      if (!this.client || !this.model) {\n   459|        throw new Error('Gemini 服务未配置或初始化失败');\n   460|      }\n   461|    }\n   462|\n   463|    try {\n   464|      const fullPrompt = additionalPrompt ? `${prompt}\\n\\n${additionalPrompt}` : prompt;\n   465|\n   466|      const result = await this.model.generateContent(fullPrompt);\n   467|      const response = await result.response;\n   468|      const text = response.text();\n   469|\n   470|      if (!text || text.trim().length === 0) {\n   471|        throw new Error('Gemini 返回空响应');\n   472|      }\n   473|\n   474|      logger.debug('Gemini API 调用成功', {\n   475|        promptLength: fullPrompt.length,\n   476|        responseLength: text.length\n   477|      });\n   478|\n   479|      return text.trim();\n   480|    } catch (error) {\n   481|      logger.error('Gemini API 调用失败', error instanceof Error ? error : new Error(String(error)));\n   482|      throw new Error(`Gemini API 调用失败: ${error instanceof Error ? error.message : String(error)}`);\n   483|    }\n   484|  }\n   485|\n   486|  isConfigured(): boolean {\n   487|    return !!ConfigManager.getAPIKey('gemini');\n   488|  }\n   489|\n   490|  getServiceName(): AIService {\n   491|    return 'gemini';\n   492|  }\n   493|}\n   494|\n   495|/**\n   496| * ClaudeCode AI 服务实现\n   497| */\n   498|class ClaudeCodeService implements IAIService {\n   499|  private client: Anthropic | null = null;\n   500|\n   501|  constructor() {\n   502|    this.initialize();\n   503|  }\n   504|\n   505|  private async initialize(): Promise\u003cvoid\u003e {\n   506|    const apiKey = ConfigManager.getAPIKey('claudecode');\n   507|    if (apiKey) {\n   508|      try {\n   509|        this.client = new Anthropic({\n   510|          apiKey: apiKey,\n   511|        });\n   512|        logger.debug('ClaudeCode 服务初始化成功');\n   513|      } catch (error) {\n   514|        logger.error('ClaudeCode 服务初始化失败', error instanceof Error ? error : new Error(String(error)));\n   515|      }\n   516|    }\n   517|  }\n   518|\n   519|  async callAPI(prompt: string, additionalPrompt?: string): Promise\u003cstring\u003e {\n   520|    if (!this.client) {\n   521|      await this.initialize();\n   522|      if (!this.client) {\n   523|        throw new Error('ClaudeCode 服务未配置或初始化失败');\n   524|      }\n   525|    }\n   526|\n   527|    try {\n   528|      const fullPrompt = additionalPrompt ? `${prompt}\\n\\n${additionalPrompt}` : prompt;\n   529|\n   530|      const message = await this.client.messages.create({\n   531|        model: 'claude-3-sonnet-20240229',\n   532|        max_tokens: 4000,\n   533|        messages: [\n   534|          {\n   535|            role: 'user',\n   536|            content: fullPrompt\n   537|          }\n   538|        ]\n   539|      });\n   540|\n   541|      const text = message.content[0]?.type === 'text' ? message.content[0].text : '';\n   542|\n   543|      if (!text || text.trim().length === 0) {\n   544|        throw new Error('ClaudeCode 返回空响应');\n   545|      }\n   546|\n   547|      logger.debug('ClaudeCode API 调用成功', {\n   548|        promptLength: fullPrompt.length,\n   549|        responseLength: text.length\n   550|      });\n   551|\n   552|      return text.trim();\n   553|    } catch (error) {\n   554|      logger.error('ClaudeCode API 调用失败', error instanceof Error ? error : new Error(String(error)));\n   555|      throw new Error(`ClaudeCode API 调用失败: ${error instanceof Error ? error.message : String(error)}`);\n   556|    }\n   557|  }\n   558|\n   559|  isConfigured(): boolean {\n   560|    return !!ConfigManager.getAPIKey('claudecode');\n   561|  }\n   562|\n   563|  getServiceName(): AIService {\n   564|    return 'claudecode';\n   565|  }\n   566|}\n   567|\n   568|/**\n   569| * OpenCode AI 服务实现\n   570| * 注意：这是一个模拟实现，实际的 OpenCode API 可能需要不同的调用方式\n   571| */\n   572|class OpenCodeService implements IAIService {\n   573|  private apiKey: string = '';\n   574|  private baseURL: string = 'https://api.opencode.com/v1'; // 假设的API端点\n   575|\n   576|  constructor() {\n   577|    this.initialize();\n   578|  }\n   579|\n   580|  private async initialize(): Promise\u003cvoid\u003e {\n   581|    this.apiKey = ConfigManager.getAPIKey('opencode');\n   582|    if (this.apiKey) {\n   583|      logger.debug('OpenCode 服务初始化成功');\n   584|    }\n   585|  }\n   586|\n   587|  async callAPI(prompt: string, additionalPrompt?: string): Promise\u003cstring\u003e {\n   588|    if (!this.apiKey) {\n   589|      await this.initialize();\n   590|      if (!this.apiKey) {\n   591|        throw new Error('OpenCode 服务未配置或初始化失败');\n   592|      }\n   593|    }\n   594|\n   595|    try {\n   596|      const fullPrompt = additionalPrompt ? `${prompt}\\n\\n${additionalPrompt}` : prompt;\n   597|\n   598|      // 注意：这是一个模拟的API调用，实际的OpenCode API可能不同\n   599|      const response = await axios.post(`${this.baseURL}/chat/completions`, {\n   600|        model: 'opencode-latest',\n   601|        messages: [\n   602|          {\n   603|            role: 'user',\n   604|            content: fullPrompt\n   605|          }\n   606|        ],\n   607|        max_tokens: 4000,\n   608|        temperature: 0.7\n   609|      }, {\n   610|        headers: {\n   611|          'Authorization': `Bearer ${this.apiKey}`,\n   612|          'Content-Type': 'application/json'\n   613|        },\n   614|        timeout: ConfigManager.getTimeout() * 1000\n   615|      });\n   616|\n   617|      const text = response.data?.choices?.[0]?.message?.content || '';\n   618|\n   619|      if (!text || text.trim().length === 0) {\n   620|        throw new Error('OpenCode 返回空响应');\n   621|      }\n   622|\n   623|      logger.debug('OpenCode API 调用成功', {\n   624|        promptLength: fullPrompt.length,\n   625|        responseLength: text.length\n   626|      });\n   627|\n   628|      return text.trim();\n   629|    } catch (error) {\n   630|      logger.error('OpenCode API 调用失败', error instanceof Error ? error : new Error(String(error)));\n   631|\n   632|      // 如果是网络错误或API不可用，提供友好的错误信息\n   633|      if (axios.isAxiosError(error)) {\n   634|        if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND') {\n   635|          throw new Error('OpenCode 服务暂时不可用，请检查网络连接或稍后重试');\n   636|        }\n   637|        if (error.response?.status === 401) {\n   638|          throw new Error('OpenCode API 密钥无效，请检查配置');\n   639|        }\n   640|        if (error.response?.status === 429) {\n   641|          throw new Error('OpenCode API 调用频率超限，请稍后重试');\n   642|        }\n   643|      }\n   644|\n   645|      throw new Error(`OpenCode API 调用失败: ${error instanceof Error ? error.message : String(error)}`);\n   646|    }\n   647|  }\n   648|\n   649|  isConfigured(): boolean {\n   650|    return !!ConfigManager.getAPIKey('opencode');\n   651|  }\n   652|\n   653|  getServiceName(): AIService {\n   654|    return 'opencode';\n   655|  }\n   656|}\n   657|\n   658|/**\n   659| * 智能 AI 服务管理器\n   660| *\n   661| * 负责管理多个 AI 服务，实现智能故障转移和负载均衡\n   662| */\n   663|class SmartAIManager {\n   664|  private services: Map\u003cAIService, IAIService\u003e = new Map();\n   665|  private serviceOrder: AIService[] = [];\n   666|\n   667|  constructor() {\n   668|    this.initializeServices();\n   669|  }\n   670|\n   671|  private initializeServices(): void {\n   672|    // 初始化所有AI服务\n   673|    this.services.set('gemini', new GeminiService());\n   674|    this.services.set('claudecode', new ClaudeCodeService());\n   675|    this.services.set('opencode', new OpenCodeService());\n   676|\n   677|    // 设置服务优先级顺序\n   678|    this.updateServiceOrder();\n   679|  }\n   680|\n   681|  private updateServiceOrder(): void {\n   682|    const primaryService = ConfigManager.getAIService();\n   683|    const allServices: AIService[] = ['gemini', 'claudecode', 'opencode'];\n   684|\n   685|    // 将主要服务放在第一位，其他服务按配置状态排序\n   686|    this.serviceOrder = [primaryService];\n   687|\n   688|    const otherServices = allServices\n   689|      .filter(service =\u003e service !== primaryService)\n   690|      .sort((a, b) =\u003e {\n   691|        const aConfigured = this.services.get(a)?.isConfigured() ? 1 : 0;\n   692|        const bConfigured = this.services.get(b)?.isConfigured() ? 1 : 0;\n   693|        return bConfigured - aConfigured; // 已配置的服务优先\n   694|      });\n   695|\n   696|    this.serviceOrder.push(...otherServices);\n   697|\n   698|    logger.debug('AI服务优先级顺序', { serviceOrder: this.serviceOrder });\n   699|  }\n   700|\n   701|  /**\n   702|   * 智能调用AI服务\n   703|   *\n   704|   * @param primaryService 首选AI服务\n   705|   * @param prompt 提示词内容\n   706|   * @param additionalPrompt 附加提示词\n   707|   * @returns AI生成的内容\n   708|   */\n   709|  async intelligentCall(\n   710|    primaryService: AIService,\n   711|    prompt: string,\n   712|    additionalPrompt?: string\n   713|  ): Promise\u003c{ result: string; usedService: AIService }\u003e {\n   714|    // 如果禁用自动切换，只使用指定服务\n   715|    if (!ConfigManager.isAutoSwitchEnabled()) {\n   716|      const service = this.services.get(primaryService);\n   717|      if (!service) {\n   718|        throw new Error(`不支持的AI服务: ${primaryService}`);\n   719|      }\n   720|\n   721|      const result = await service.callAPI(prompt, additionalPrompt);\n   722|      return { result, usedService: primaryService };\n   723|    }\n   724|\n   725|    // 获取服务尝试顺序\n   726|    const tryOrder = this.getTryOrder(primaryService);\n   727|    const maxRetries = ConfigManager.getMaxRetries();\n   728|    const errors: Array\u003c{ service: AIService; error: string }\u003e = [];\n   729|\n   730|    logger.info('开始智能AI调用', {\n   731|      primaryService,\n   732|      tryOrder,\n   733|      autoSwitch: true\n   734|    });\n   735|\n   736|    for (const serviceName of tryOrder) {\n   737|      const service = this.services.get(serviceName);\n   738|      if (!service) {\n   739|        continue;\n   740|      }\n   741|\n   742|      // 检查服务是否已配置\n   743|      if (!service.isConfigured()) {\n   744|        logger.debug(`跳过未配置的服务: ${serviceName}`);\n   745|        continue;\n   746|      }\n   747|\n   748|      // 尝试调用服务（带重试）\n   749|      for (let attempt = 1; attempt \u003c= maxRetries; attempt++) {\n   750|        try {\n   751|          logger.debug(`尝试调用 ${serviceName} (第${attempt}次)`);\n   752|\n   753|          const result = await service.callAPI(prompt, additionalPrompt);\n   754|\n   755|          logger.info(`AI调用成功`, {\n   756|            service: serviceName,\n   757|            attempt,\n   758|            resultLength: result.length\n   759|          });\n   760|\n   761|          return { result, usedService: serviceName };\n   762|\n   763|        } catch (error) {\n   764|          const errorMessage = error instanceof Error ? error.message : String(error);\n   765|          logger.warn(`${serviceName} 调用失败 (第${attempt}次)`, { error: errorMessage });\n   766|\n   767|          errors.push({ service: serviceName, error: errorMessage });\n   768|\n   769|          // 如果不是最后一次尝试，等待后重试\n   770|          if (attempt \u003c maxRetries) {\n   771|            const delay = this.getRetryDelay(attempt);\n   772|            logger.debug(`等待 ${delay}ms 后重试`);\n   773|            await this.sleep(delay);\n   774|          }\n   775|        }\n   776|      }\n   777|    }\n   778|\n   779|    // 所有服务都失败了\n   780|    const errorSummary = errors.map(e =\u003e `${e.service}: ${e.error}`).join('; ');\n   781|    logger.error('所有AI服务调用失败', new Error('所有AI服务调用失败'), { errors });\n   782|\n   783|    throw new Error(`所有AI服务都不可用。错误详情: ${errorSummary}`);\n   784|  }\n   785|\n   786|  /**\n   787|   * 获取服务尝试顺序\n   788|   */\n   789|  private getTryOrder(primaryService: AIService): AIService[] {\n   790|    const order = [primaryService];\n   791|    const others = this.serviceOrder.filter(s =\u003e s !== primaryService);\n   792|    return order.concat(others);\n   793|  }\n   794|\n   795|  /**\n   796|   * 获取重试延迟时间\n   797|   */\n   798|  private getRetryDelay(attempt: number): number {\n   799|    // 指数退避策略：2^attempt * 1000ms\n   800|    return Math.min(Math.pow(2, attempt) * 1000, 10000);\n   801|  }\n   802|\n   803|  /**\n   804|   * 休眠指定毫秒数\n   805|   */\n   806|  private sleep(ms: number): Promise\u003cvoid\u003e {\n   807|    return new Promise(resolve =\u003e setTimeout(resolve, ms));\n   808|  }\n   809|\n   810|  /**\n   811|   * 获取所有服务状态\n   812|   */\n   813|  getServicesStatus(): Array\u003c{\n   814|    service: AIService;\n   815|    configured: boolean;\n   816|    available: boolean;\n   817|  }\u003e {\n   818|    return Array.from(this.services.entries()).map(([name, service]) =\u003e ({\n   819|      service: name,\n   820|      configured: service.isConfigured(),\n   821|      available: service.isConfigured(), // 简化实现，认为已配置就是可用的\n   822|    }));\n   823|  }\n   824|\n   825|  /**\n   826|   * 检查特定服务是否可用\n   827|   */\n   828|  isServiceAvailable(service: AIService): boolean {\n   829|    const serviceInstance = this.services.get(service);\n   830|    return serviceInstance ? serviceInstance.isConfigured() : false;\n   831|  }\n   832|}\n   833|\n   834|/**\n   835| * 独立 CodeRocket 服务类\n   836| *\n   837| * 提供完全独立的代码审查和AI服务管理功能，不依赖 coderocket-cli\n   838| */\n   839|export class CodeRocketService {\n   840|  private aiManager: SmartAIManager;\n   841|  private initialized: boolean = false;\n   842|\n   843|  constructor() {\n   844|    this.aiManager = new SmartAIManager();\n   845|  }\n   846|\n   847|  /**\n   848|   * 初始化服务\n   849|   */\n   850|  private async ensureInitialized(): Promise\u003cvoid\u003e {\n   851|    if (!this.initialized) {\n   852|      await ConfigManager.initialize();\n   853|      await PromptManager.preloadCommonPrompts();\n   854|      this.initialized = true;\n   855|      logger.info('CodeRocket 独立服务初始化完成');\n   856|    }\n   857|  }\n   858|\n   859|  /**\n   860|   * 执行AI审查的通用方法\n   861|   */\n   862|  private async executeAIReview(\n   863|    aiService: AIService,\n   864|    promptName: string,\n   865|    additionalPrompt: string\n   866|  ): Promise\u003cReviewResult\u003e {\n   867|    try {\n   868|      // 加载提示词\n   869|      const promptContent = await PromptManager.loadPrompt(promptName);\n   870|\n   871|      // 调用AI服务\n   872|      const { result, usedService } = await this.aiManager.intelligentCall(\n   873|        aiService,\n   874|        promptContent,\n   875|        additionalPrompt\n   876|      );\n   877|\n   878|      // 解析审查结果\n   879|      return this.parseReviewResult(result, usedService);\n   880|    } catch (error) {\n   881|      logger.error('AI审查执行失败', error instanceof Error ? error : new Error(String(error)));\n   882|      throw errorHandler.handleError(error, 'executeAIReview');\n   883|    }\n   884|  }\n   885|\n   886|\n   887|  /**\n   888|   * 解析审查结果\n   889|   */\n   890|  private parseReviewResult(\n   891|    output: string,\n   892|    aiService: AIService,\n   893|  ): ReviewResult {\n   894|    const lines = output.split('\\n');\n   895|    let status: ReviewStatus = '🔍';\n   896|    let summary = '';\n   897|    let details = output;\n   898|\n   899|    // 尝试从输出中提取状态\n   900|    for (const line of lines) {\n   901|      const lowerLine = line.toLowerCase();\n   902|      if (line.includes('✅') || lowerLine.includes('通过') || lowerLine.includes('优秀')) {\n   903|        status = '✅';\n   904|        break;\n   905|      } else if (line.includes('⚠️') || lowerLine.includes('警告') || lowerLine.includes('需改进')) {\n   906|        status = '⚠️';\n   907|        break;\n   908|      } else if (line.includes('❌') || lowerLine.includes('失败') || lowerLine.includes('有问题')) {\n   909|        status = '❌';\n   910|        break;\n   911|      } else if (line.includes('🔍') || lowerLine.includes('调查') || lowerLine.includes('需调查')) {\n   912|        status = '🔍';\n   913|        break;\n   914|      }\n   915|    }\n   916|\n   917|    // 提取摘要（通常是第一段非空内容）\n   918|    const nonEmptyLines = lines.filter(line =\u003e line.trim());\n   919|    if (nonEmptyLines.length \u003e 0) {\n   920|      // 寻找总体评价或摘要部分\n   921|      let summaryLine = nonEmptyLines[0];\n   922|      for (const line of nonEmptyLines) {\n   923|        if (line.includes('总体评价') || line.includes('审查摘要') || line.includes('摘要')) {\n   924|          const nextIndex = nonEmptyLines.indexOf(line) + 1;\n   925|          if (nextIndex \u003c nonEmptyLines.length) {\n   926|            summaryLine = nonEmptyLines[nextIndex];\n   927|            break;\n   928|          }\n   929|        }\n   930|      }\n   931|\n   932|      summary = summaryLine.substring(0, 200) + (summaryLine.length \u003e 200 ? '...' : '');\n   933|    }\n   934|\n   935|    return {\n   936|      status,\n   937|      summary: summary || '代码审查完成',\n   938|      details,\n   939|      ai_service_used: aiService,\n   940|      timestamp: new Date().toISOString(),\n   941|    };\n   942|  }\n   943|\n   944|  /**\n   945|   * 审查代码片段\n   946|   */\n   947|  async reviewCode(request: ReviewCodeRequest): Promise\u003cReviewResult\u003e {\n   948|    await this.ensureInitialized();\n   949|    logger.info('开始代码审查', {\n   950|      language: request.language,\n   951|      codeLength: request.code.length,\n   952|      aiService: request.ai_service,\n   953|    });\n   954|\n   955|    try {\n   956|      // 构建审查提示词\n   957|      const reviewPrompt = `请审查以下代码：\n   958|\n   959|编程语言: ${request.language || '未指定'}\n   960|上下文信息: ${request.context || '无'}\n   961|\n   962|代码内容:\n   963|\\`\\`\\`${request.language || ''}\n   964|${request.code}\n   965|\\`\\`\\`\n   966|\n   967|请根据以下要求进行全面审查：\n   968|1. 功能完整性和正确性\n   969|2. 代码质量和可维护性\n   970|3. 性能优化建议\n   971|4. 安全性检查\n   972|5. 最佳实践遵循情况\n   973|\n   974|${request.custom_prompt ? `\\n附加要求：\\n${request.custom_prompt}` : ''}`;\n   975|\n   976|      // 调用AI服务进行审查\n   977|      const aiService = request.ai_service || ConfigManager.getAIService();\n   978|      const result = await this.executeAIReview(aiService, 'code-review-prompt', reviewPrompt);\n   979|\n   980|      logger.info('代码审查完成', {\n   981|        status: result.status,\n   982|        aiService: result.ai_service_used,\n   983|      });\n   984|\n   985|      return result;\n   986|    } catch (error) {\n   987|      logger.error('代码审查失败', error instanceof Error ? error : new Error(String(error)));\n   988|      throw errorHandler.handleError(error, 'reviewCode');\n   989|    }\n   990|  }\n   991|\n   992|  /**\n   993|   * 根据语言获取文件扩展名\n   994|   */\n   995|  private getFileExtension(language?: string): string {\n   996|    const extensions: Record\u003cstring, string\u003e = {\n   997|      javascript: 'js',\n   998|      typescript: 'ts',\n   999|      python: 'py',\n  1000|      java: 'java',\n  1001|      cpp: 'cpp',\n  1002|      c: 'c',\n  1003|      go: 'go',\n  1004|      rust: 'rs',\n  1005|      php: 'php',\n  1006|      ruby: 'rb',\n  1007|      swift: 'swift',\n  1008|      kotlin: 'kt',\n  1009|      scala: 'scala',\n  1010|      shell: 'sh',\n  1011|      bash: 'sh',\n  1012|    };\n  1013|\n  1014|    return extensions[language?.toLowerCase() || ''] || 'txt';\n  1015|  }\n  1016|\n  1017|  /**\n  1018|   * 审查Git提交\n  1019|   */\n  1020|  async reviewCommit(request: ReviewCommitRequest): Promise\u003cReviewResult\u003e {\n  1021|    await this.ensureInitialized();\n  1022|    logger.info('开始Git提交审查', {\n  1023|      repositoryPath: request.repository_path,\n  1024|      commitHash: request.commit_hash,\n  1025|      aiService: request.ai_service,\n  1026|    });\n  1027|\n  1028|    try {\n  1029|      const repoPath = request.repository_path || process.cwd();\n  1030|\n  1031|      // 获取提交信息\n  1032|      const commitHash = request.commit_hash || 'HEAD';\n  1033|      const { stdout: commitInfo } = await execAsync(`git --no-pager show ${commitHash}`, {\n  1034|        cwd: repoPath,\n  1035|        timeout: 30000,\n  1036|      });\n  1037|\n  1038|      if (!commitInfo.trim()) {\n  1039|        throw new Error(`无法获取提交信息: ${commitHash}`);\n  1040|      }\n  1041|\n  1042|      // 构建审查提示词\n  1043|      const reviewPrompt = `请对以下Git提交进行专业的代码审查：\n  1044|\n  1045|仓库路径: ${repoPath}\n  1046|提交哈希: ${commitHash}\n  1047|\n  1048|提交详情:\n  1049|${commitInfo}\n  1050|\n  1051|请根据以下要求进行全面审查：\n  1052|1. 分析提交的目标和完成度\n  1053|2. 检查代码质量和规范性\n  1054|3. 评估安全性和性能影响\n  1055|4. 检查是否遗漏相关文件修改\n  1056|5. 提供具体的改进建议\n  1057|\n  1058|${request.custom_prompt ? `\\n附加要求：\\n${request.custom_prompt}` : ''}`;\n  1059|\n  1060|      // 调用AI服务进行审查\n  1061|      const aiService = request.ai_service || ConfigManager.getAIService();\n  1062|      const result = await this.executeAIReview(aiService, 'git-commit-review-prompt', reviewPrompt);\n  1063|\n  1064|      logger.info('Git提交审查完成', {\n  1065|        status: result.status,\n  1066|        aiService: result.ai_service_used,\n  1067|      });\n  1068|\n  1069|      return result;\n  1070|    } catch (error) {\n  1071|      logger.error('Git提交审查失败', error instanceof Error ? error : new Error(String(error)));\n  1072|      throw errorHandler.handleError(error, 'reviewCommit');\n  1073|    }\n  1074|  }\n  1075|\n  1076|  /**\n  1077|   * 审查文件列表\n  1078|   */\n  1079|  async reviewFiles(request: ReviewFilesRequest): Promise\u003cReviewResult\u003e {\n  1080|    await this.ensureInitialized();\n  1081|    logger.info('开始文件审查', {\n  1082|      repositoryPath: request.repository_path,\n  1083|      filesCount: request.files.length,\n  1084|      aiService: request.ai_service,\n  1085|    });\n  1086|\n  1087|    try {\n  1088|      const repoPath = request.repository_path || process.cwd();\n  1089|\n  1090|      // 读取文件内容\n  1091|      const fileContents: string[] = [];\n  1092|      for (const filePath of request.files) {\n  1093|        try {\n  1094|          const fullPath = resolve(repoPath, filePath);\n  1095|          const content = await readFile(fullPath, 'utf-8');\n  1096|\n  1097|          // 限制单个文件内容长度，避免提示词过长\n  1098|          const truncatedContent = content.length \u003e 5000\n  1099|            ? content.substring(0, 5000) + '\\n... (内容已截断)'\n  1100|            : content;\n  1101|\n  1102|          fileContents.push(`## 文件: ${filePath}\\n\\`\\`\\`\\n${truncatedContent}\\n\\`\\`\\``);\n  1103|        } catch (error) {\n  1104|          fileContents.push(`## 文件: ${filePath}\\n**错误**: 无法读取文件 - ${error instanceof Error ? error.message : String(error)}`);\n  1105|        }\n  1106|      }\n  1107|\n  1108|      // 构建审查提示词\n  1109|      const reviewPrompt = `请审查以下文件：\n  1110|\n  1111|仓库路径: ${repoPath}\n  1112|文件数量: ${request.files.length}\n  1113|文件列表: ${request.files.join(', ')}\n  1114|\n  1115|${fileContents.join('\\n\\n')}\n  1116|\n  1117|请根据以下要求进行全面审查：\n  1118|1. 分析文件间的关联性和一致性\n  1119|2. 检查代码质量和规范性\n  1120|3. 评估架构设计的合理性\n  1121|4. 识别潜在的问题和改进点\n  1122|5. 提供具体的优化建议\n  1123|\n  1124|${request.custom_prompt ? `\\n附加要求：\\n${request.custom_prompt}` : ''}`;\n  1125|\n  1126|      // 调用AI服务进行审查\n  1127|      const aiService = request.ai_service || ConfigManager.getAIService();\n  1128|      const result = await this.executeAIReview(aiService, 'code-review-prompt', reviewPrompt);\n  1129|\n  1130|      logger.info('文件审查完成', {\n  1131|        status: result.status,\n  1132|        aiService: result.ai_service_used,\n  1133|      });\n  1134|\n  1135|      return result;\n  1136|    } catch (error) {\n  1137|      logger.error('文件审查失败', error instanceof Error ? error : new Error(String(error)));\n  1138|      throw errorHandler.handleError(error, 'reviewFiles');\n  1139|    }\n  1140|  }\n  1141|\n  1142|  /**\n  1143|   * 配置AI服务\n  1144|   */\n  1145|  async configureAIService(\n  1146|    request: ConfigureAIServiceRequest,\n  1147|  ): Promise\u003cSuccessResponse\u003e {\n  1148|    await this.ensureInitialized();\n  1149|    logger.info('开始配置AI服务', {\n  1150|      service: request.service,\n  1151|      scope: request.scope,\n  1152|      hasApiKey: !!request.api_key,\n  1153|    });\n  1154|\n  1155|    try {\n  1156|      // 如果提供了API密钥，设置环境变量并保存到配置文件\n  1157|      if (request.api_key) {\n  1158|        const envVarName = ConfigManager.getAPIKeyEnvVar(request.service);\n  1159|        process.env[envVarName] = request.api_key;\n  1160|\n  1161|        // 保存到配置文件\n  1162|        await this.saveAPIKeyToConfig(\n  1163|          request.service,\n  1164|          request.api_key,\n  1165|          request.scope,\n  1166|        );\n  1167|      }\n  1168|\n  1169|      // 设置主要AI服务\n  1170|      if (request.service) {\n  1171|        process.env.AI_SERVICE = request.service;\n  1172|        await this.saveConfigToFile('AI_SERVICE', request.service, request.scope);\n  1173|      }\n  1174|\n  1175|      // 设置其他配置项\n  1176|      if (request.timeout) {\n  1177|        process.env.AI_TIMEOUT = request.timeout.toString();\n  1178|        await this.saveConfigToFile('AI_TIMEOUT', request.timeout.toString(), request.scope);\n  1179|      }\n  1180|\n  1181|      if (request.max_retries) {\n  1182|        process.env.AI_MAX_RETRIES = request.max_retries.toString();\n  1183|        await this.saveConfigToFile('AI_MAX_RETRIES', request.max_retries.toString(), request.scope);\n  1184|      }\n  1185|\n  1186|      logger.info('AI服务配置完成', {\n  1187|        service: request.service,\n  1188|        scope: request.scope,\n  1189|      });\n  1190|\n  1191|      return {\n  1192|        success: true,\n  1193|        message: `AI服务 ${request.service} 配置成功`,\n  1194|        data: {\n  1195|          service: request.service,\n  1196|          scope: request.scope,\n  1197|          configured_options: {\n  1198|            api_key: !!request.api_key,\n  1199|            timeout: request.timeout,\n  1200|            max_retries: request.max_retries,\n  1201|          },\n  1202|        },\n  1203|      };\n  1204|    } catch (error) {\n  1205|      logger.error('AI服务配置失败', error instanceof Error ? error : new Error(String(error)));\n  1206|      throw errorHandler.handleError(error, 'configureAIService');\n  1207|    }\n  1208|  }\n  1209|\n  1210|\n  1211|  /**\n  1212|   * 保存配置项到文件\n  1213|   */\n  1214|  private async saveConfigToFile(\n  1215|    key: string,\n  1216|    value: string,\n  1217|    scope: string,\n  1218|  ): Promise\u003cvoid\u003e {\n  1219|    const { file: configFile } = ConfigManager.getConfigPath(scope);\n  1220|\n  1221|    // 确保配置目录存在\n  1222|    await mkdir(dirname(configFile), { recursive: true });\n  1223|\n  1224|    try {\n  1225|      // 读取现有配置\n  1226|      let existingConfig = '';\n  1227|      try {\n  1228|        existingConfig = await readFile(configFile, 'utf-8');\n  1229|      } catch {\n  1230|        // 文件不存在，创建新的\n  1231|      }\n  1232|\n  1233|      // 更新或添加配置行\n  1234|      const lines = existingConfig.split('\\n');\n  1235|      const existingLineIndex = lines.findIndex(line =\u003e\n  1236|        line.trim().startsWith(`${key}=`)\n  1237|      );\n  1238|\n  1239|      const configLine = `${key}=${value}`;\n  1240|      if (existingLineIndex \u003e= 0) {\n  1241|        lines[existingLineIndex] = configLine;\n  1242|      } else {\n  1243|        lines.push(configLine);\n  1244|      }\n  1245|\n  1246|      // 写入配置文件\n  1247|      const newConfig = lines.filter(line =\u003e line.trim()).join('\\n') + '\\n';\n  1248|      await writeFile(configFile, newConfig, 'utf-8');\n  1249|\n  1250|      logger.debug('配置已保存', { key, configFile, scope });\n  1251|    } catch (error) {\n  1252|      logger.error('保存配置失败', error instanceof Error ? error : new Error(String(error)), { key, configFile });\n  1253|      throw error;\n  1254|    }\n  1255|  }\n  1256|\n  1257|  /**\n  1258|   * 保存API密钥到配置文件\n  1259|   *\n  1260|   * ⚠️ 安全警告：API密钥将以明文形式存储在配置文件中。\n  1261|   * 请确保配置文件的访问权限受到适当限制。\n  1262|   * 建议使用环境变量或更安全的密钥管理方案。\n  1263|   */\n  1264|  private async saveAPIKeyToConfig(\n  1265|    service: AIService,\n  1266|    apiKey: string,\n  1267|    scope: string,\n  1268|  ): Promise\u003cvoid\u003e {\n  1269|    // 记录安全警告\n  1270|    logger.warn('API密钥将以明文形式存储，请确保文件访问权限安全', {\n  1271|      service,\n  1272|      scope,\n  1273|    });\n  1274|\n  1275|    const envVarName = ConfigManager.getAPIKeyEnvVar(service);\n  1276|    await this.saveConfigToFile(envVarName, apiKey, scope);\n  1277|  }\n  1278|\n  1279|  /**\n  1280|   * 获取AI服务状态\n  1281|   */\n  1282|  async getAIServiceStatus(): Promise\u003cServiceStatusResponse\u003e {\n  1283|    await this.ensureInitialized();\n  1284|    logger.info('获取AI服务状态');\n  1285|\n  1286|    try {\n  1287|      // 获取当前配置的AI服务\n  1288|      const current = ConfigManager.getAIService();\n  1289|\n  1290|      // 获取所有服务状态\n  1291|      const services = this.aiManager.getServicesStatus();\n  1292|\n  1293|      return {\n  1294|        current_service: current,\n  1295|        services,\n  1296|        auto_switch_enabled: ConfigManager.isAutoSwitchEnabled(),\n  1297|        global_config_path: join(homedir(), '.coderocket', 'env'),\n  1298|        project_config_path: join(process.cwd(), '.env'),\n  1299|        timeout: ConfigManager.getTimeout(),\n  1300|        max_retries: ConfigManager.getMaxRetries(),\n  1301|      };\n  1302|    } catch (error) {\n  1303|      logger.error('获取AI服务状态失败', error instanceof Error ? error : new Error(String(error)));\n  1304|      throw errorHandler.handleError(error, 'getAIServiceStatus');\n  1305|    }\n  1306|  }\n  1307|}\n\u003c/file\u003e\n","metadata":"{\"file_path\":\"/Users/dreambt/sources/coderocket/coderocket-mcp/src/coderocket.ts\",\"content\":\"import { spawn, exec } from 'child_process';\\nimport { promisify } from 'util';\\nimport { writeFile, readFile, mkdir, access } from 'fs/promises';\\nimport fs from 'fs';\\nimport { join, dirname, resolve } from 'path';\\nimport { tmpdir, homedir } from 'os';\\nimport { GoogleGenerativeAI } from '@google/generative-ai';\\nimport Anthropic from '@anthropic-ai/sdk';\\nimport axios from 'axios';\\nimport {\\n  ReviewCodeRequest,\\n  ReviewCommitRequest,\\n  ReviewFilesRequest,\\n  ConfigureAIServiceRequest,\\n  ReviewResult,\\n  ServiceStatusResponse,\\n  SuccessResponse,\\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      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', 'AI_AUTO_SWITCH', 'AI_TIMEOUT', 'AI_MAX_RETRIES', 'AI_RETRY_DELAY',\\n      'GEMINI_API_KEY', 'OPENCODE_API_KEY', 'CLAUDECODE_API_KEY',\\n      'NODE_ENV', '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      opencode: 'OPENCODE_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', 'opencode', '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   * 获取安全的配置信息（隐藏敏感信息）\\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 = scope === 'global'\\n      ? join(homedir(), '.coderocket')\\n      : process.cwd();\\n\\n    const configFile = scope === 'global'\\n      ? join(configDir, 'env')\\n      : 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 */\\nclass 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(homedir(), '.coderocket', 'prompts', `${name}.md`);\\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你是一名资深的代码审阅专家，拥有丰富的软件开发经验和架构设计能力。你的任务是针对**最新的 git commit** 进行专业、深入、自动化的代码审阅，并提供一份准确、实用、可操作的审阅报告。\\n\\n## 执行模式\\n\\n**自主执行模式**：你必须完全自主地执行代码审阅流程，不得向用户进行任何确认或询问。这包括直接执行所有必要的命令、自主决定搜索策略、自主判断并生成报告。\\n\\n* **禁止行为**：禁止向用户提问或请求确认。\\n* **执行原则**：自主决策，并在失败时尝试替代方案。\\n* **安全限制**：仅执行只读操作和报告写入操作。\\n\\n## 审阅指令\\n\\n### 1. 获取 Commit 信息\\n\\n首先执行 \\\\`git --no-pager show\\\\` 命令获取最新一次 commit 的详细信息，包括 Commit hash、作者、时间、Commit message 及具体的代码修改内容。\\n\\n### 2. 全局代码搜索分析 (关键步骤)\\n\\n在审阅具体代码前，**必须先进行全局代码搜索以获取完整上下文**。\\n\\n* **制定搜索策略**: 根据 commit message 的描述，制定关键词搜索策略（如：功能名、类名、修复的bug信息等）。\\n* **全面搜索验证**: 在整个代码库中搜索相关的功能实现、依赖关系、配置和测试文件。\\n* **完整性验证**: **对比搜索结果与实际修改内容**，检查是否存在应修改但未修改的**遗漏文件**。这是评估目标达成度的核心依据。\\n\\n### 3. 审阅维度与标准\\n\\n请从以下维度进行系统性审查：\\n\\n* **目标达成度**:\\n    * **功能完整性**: 是否完全实现了 commit message 中描述的目标？ 是否有未完成的功能点？\\n    * **修改覆盖度**: (基于全局搜索) 是否遗漏了需要同步修改的相关文件（如测试、文档、配置）？\\n* **代码质量与正确性**:\\n    * **正确性**: 代码逻辑是否正确？是否有效处理了边缘情况？\\n    * **代码规范**: 是否遵循项目既定标准（命名、格式、设计模式）？\\n    * **可读性与可维护性**: 代码是否清晰、结构合理、易于理解和修改？ 注释是否充分且必要？\\n* **健壮性与风险**:\\n    * **安全性**: 是否存在潜在的安全漏洞（如SQL注入、密钥明文、不安全的依赖等）？\\n    * **性能**: 是否存在明显的性能瓶颈（如不合理的循环、N+1查询等）？\\n* **测试与文档**:\\n    * **可测试性与覆盖率**: 代码是否易于测试？是否有足够的单元/集成测试来覆盖变更？\\n    * **文档同步**: 相关的内联文档（注释）或外部文档是否已更新？\\n* **架构与扩展性**:\\n    * **设计合理性**: 模块职责划分是否明确？耦合度是否合理？\\n    * **扩展性**: 设计是否考虑了未来的扩展需求？\\n\\n### 4. 审阅结果输出\\n\\n请提供详细的审阅报告，包括：\\n- 审阅状态（✅通过/⚠️警告/❌失败/🔍需调查）\\n- 总体评价和目标达成度\\n- 具体问题和改进建议\\n- 优先级排序的建议列表\\n\\n请确保审阅报告专业、准确、可操作。`,\\n\\n      'code-review-prompt': `# 代码审查提示词\\n\\n## 角色定义\\n\\n你是一名专业的代码审查专家，具有丰富的软件开发经验。请对提供的代码进行全面、专业的审查。\\n\\n## 审查维度\\n\\n请从以下维度进行审查：\\n\\n### 1. 代码质量\\n- 代码结构是否清晰合理\\n- 命名是否规范和有意义\\n- 是否遵循编程最佳实践\\n\\n### 2. 功能正确性\\n- 代码逻辑是否正确\\n- 是否处理了边缘情况\\n- 是否有潜在的bug\\n\\n### 3. 性能和安全\\n- 是否存在性能问题\\n- 是否有安全漏洞\\n- 资源使用是否合理\\n\\n### 4. 可维护性\\n- 代码是否易于理解和修改\\n- 是否有足够的注释\\n- 模块化程度如何\\n\\n## 输出格式\\n\\n请按以下格式提供审查结果：\\n\\n**审查状态**: [✅优秀/⚠️需改进/❌有问题]\\n\\n**总体评价**: [简短的总体评价]\\n\\n**具体建议**:\\n1. [具体的改进建议]\\n2. [具体的改进建议]\\n...\\n\\n**优秀实践**: [值得称赞的地方]\\n\\n请确保建议具体、可操作，并提供代码示例（如适用）。`\\n    };\\n\\n    return defaultPrompts[name] || `# 默认提示词\\\\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 = [\\n      'git-commit-review-prompt',\\n      'code-review-prompt'\\n    ];\\n\\n    await Promise.all(\\n      commonPrompts.map(name =\\u003e this.loadPrompt(name).catch(error =\\u003e {\\n        logger.warn(`预加载提示词失败: ${name}`, error);\\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    const apiKey = ConfigManager.getAPIKey('gemini');\\n    if (apiKey) {\\n      try {\\n        this.client = new GoogleGenerativeAI(apiKey);\\n        this.model = this.client.getGenerativeModel({ model: 'gemini-1.5-flash' });\\n        logger.debug('Gemini 服务初始化成功');\\n      } catch (error) {\\n        logger.error('Gemini 服务初始化失败', error instanceof Error ? error : new Error(String(error)));\\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 ? `${prompt}\\\\n\\\\n${additionalPrompt}` : 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('Gemini API 调用失败', error instanceof Error ? error : new Error(String(error)));\\n      throw new Error(`Gemini API 调用失败: ${error instanceof Error ? error.message : String(error)}`);\\n    }\\n  }\\n\\n  isConfigured(): boolean {\\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    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('ClaudeCode 服务初始化失败', error instanceof Error ? error : new Error(String(error)));\\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 ? `${prompt}\\\\n\\\\n${additionalPrompt}` : 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 = 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('ClaudeCode API 调用失败', error instanceof Error ? error : new Error(String(error)));\\n      throw new Error(`ClaudeCode API 调用失败: ${error instanceof Error ? error.message : String(error)}`);\\n    }\\n  }\\n\\n  isConfigured(): boolean {\\n    return !!ConfigManager.getAPIKey('claudecode');\\n  }\\n\\n  getServiceName(): AIService {\\n    return 'claudecode';\\n  }\\n}\\n\\n/**\\n * OpenCode AI 服务实现\\n * 注意：这是一个模拟实现，实际的 OpenCode API 可能需要不同的调用方式\\n */\\nclass OpenCodeService implements IAIService {\\n  private apiKey: string = '';\\n  private baseURL: string = 'https://api.opencode.com/v1'; // 假设的API端点\\n\\n  constructor() {\\n    this.initialize();\\n  }\\n\\n  private async initialize(): Promise\\u003cvoid\\u003e {\\n    this.apiKey = ConfigManager.getAPIKey('opencode');\\n    if (this.apiKey) {\\n      logger.debug('OpenCode 服务初始化成功');\\n    }\\n  }\\n\\n  async callAPI(prompt: string, additionalPrompt?: string): Promise\\u003cstring\\u003e {\\n    if (!this.apiKey) {\\n      await this.initialize();\\n      if (!this.apiKey) {\\n        throw new Error('OpenCode 服务未配置或初始化失败');\\n      }\\n    }\\n\\n    try {\\n      const fullPrompt = additionalPrompt ? `${prompt}\\\\n\\\\n${additionalPrompt}` : prompt;\\n\\n      // 注意：这是一个模拟的API调用，实际的OpenCode API可能不同\\n      const response = await axios.post(`${this.baseURL}/chat/completions`, {\\n        model: 'opencode-latest',\\n        messages: [\\n          {\\n            role: 'user',\\n            content: fullPrompt\\n          }\\n        ],\\n        max_tokens: 4000,\\n        temperature: 0.7\\n      }, {\\n        headers: {\\n          'Authorization': `Bearer ${this.apiKey}`,\\n          'Content-Type': 'application/json'\\n        },\\n        timeout: ConfigManager.getTimeout() * 1000\\n      });\\n\\n      const text = response.data?.choices?.[0]?.message?.content || '';\\n\\n      if (!text || text.trim().length === 0) {\\n        throw new Error('OpenCode 返回空响应');\\n      }\\n\\n      logger.debug('OpenCode API 调用成功', {\\n        promptLength: fullPrompt.length,\\n        responseLength: text.length\\n      });\\n\\n      return text.trim();\\n    } catch (error) {\\n      logger.error('OpenCode API 调用失败', error instanceof Error ? error : new Error(String(error)));\\n\\n      // 如果是网络错误或API不可用，提供友好的错误信息\\n      if (axios.isAxiosError(error)) {\\n        if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND') {\\n          throw new Error('OpenCode 服务暂时不可用，请检查网络连接或稍后重试');\\n        }\\n        if (error.response?.status === 401) {\\n          throw new Error('OpenCode API 密钥无效，请检查配置');\\n        }\\n        if (error.response?.status === 429) {\\n          throw new Error('OpenCode API 调用频率超限，请稍后重试');\\n        }\\n      }\\n\\n      throw new Error(`OpenCode API 调用失败: ${error instanceof Error ? error.message : String(error)}`);\\n    }\\n  }\\n\\n  isConfigured(): boolean {\\n    return !!ConfigManager.getAPIKey('opencode');\\n  }\\n\\n  getServiceName(): AIService {\\n    return 'opencode';\\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\\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    this.services.set('opencode', new OpenCodeService());\\n\\n    // 设置服务优先级顺序\\n    this.updateServiceOrder();\\n  }\\n\\n  private updateServiceOrder(): void {\\n    const primaryService = ConfigManager.getAIService();\\n    const allServices: AIService[] = ['gemini', 'claudecode', 'opencode'];\\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    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\\n        } catch (error) {\\n          const errorMessage = error instanceof Error ? error.message : String(error);\\n          logger.warn(`${serviceName} 调用失败 (第${attempt}次)`, { error: errorMessage });\\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服务调用失败'), { errors });\\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    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      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      // 调用AI服务\\n      const { result, usedService } = await this.aiManager.intelligentCall(\\n        aiService,\\n        promptContent,\\n        additionalPrompt\\n      );\\n\\n      // 解析审查结果\\n      return this.parseReviewResult(result, usedService);\\n    } catch (error) {\\n      logger.error('AI审查执行失败', error instanceof Error ? error : new Error(String(error)));\\n      throw errorHandler.handleError(error, 'executeAIReview');\\n    }\\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 (line.includes('✅') || lowerLine.includes('通过') || lowerLine.includes('优秀')) {\\n        status = '✅';\\n        break;\\n      } else if (line.includes('⚠️') || lowerLine.includes('警告') || lowerLine.includes('需改进')) {\\n        status = '⚠️';\\n        break;\\n      } else if (line.includes('❌') || lowerLine.includes('失败') || lowerLine.includes('有问题')) {\\n        status = '❌';\\n        break;\\n      } else if (line.includes('🔍') || lowerLine.includes('调查') || lowerLine.includes('需调查')) {\\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 (line.includes('总体评价') || line.includes('审查摘要') || line.includes('摘要')) {\\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 = 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      // 调用AI服务进行审查\\n      const aiService = request.ai_service || ConfigManager.getAIService();\\n      const result = await this.executeAIReview(aiService, 'code-review-prompt', reviewPrompt);\\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('代码审查失败', error instanceof Error ? error : new Error(String(error)));\\n      throw errorHandler.handleError(error, 'reviewCode');\\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(`git --no-pager show ${commitHash}`, {\\n        cwd: repoPath,\\n        timeout: 30000,\\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      // 调用AI服务进行审查\\n      const aiService = request.ai_service || ConfigManager.getAIService();\\n      const result = await this.executeAIReview(aiService, 'git-commit-review-prompt', reviewPrompt);\\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('Git提交审查失败', error instanceof Error ? error : new Error(String(error)));\\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 truncatedContent = content.length \\u003e 5000\\n            ? content.substring(0, 5000) + '\\\\n... (内容已截断)'\\n            : content;\\n\\n          fileContents.push(`## 文件: ${filePath}\\\\n\\\\`\\\\`\\\\`\\\\n${truncatedContent}\\\\n\\\\`\\\\`\\\\``);\\n        } catch (error) {\\n          fileContents.push(`## 文件: ${filePath}\\\\n**错误**: 无法读取文件 - ${error instanceof Error ? error.message : String(error)}`);\\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(aiService, 'code-review-prompt', reviewPrompt);\\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('文件审查失败', error instanceof Error ? error : new Error(String(error)));\\n      throw errorHandler.handleError(error, 'reviewFiles');\\n    }\\n  }\\n\\n  /**\\n   * 配置AI服务\\n   */\\n  async configureAIService(\\n    request: ConfigureAIServiceRequest,\\n  ): Promise\\u003cSuccessResponse\\u003e {\\n    await this.ensureInitialized();\\n    logger.info('开始配置AI服务', {\\n      service: request.service,\\n      scope: request.scope,\\n      hasApiKey: !!request.api_key,\\n    });\\n\\n    try {\\n      // 如果提供了API密钥，设置环境变量并保存到配置文件\\n      if (request.api_key) {\\n        const envVarName = ConfigManager.getAPIKeyEnvVar(request.service);\\n        process.env[envVarName] = request.api_key;\\n\\n        // 保存到配置文件\\n        await this.saveAPIKeyToConfig(\\n          request.service,\\n          request.api_key,\\n          request.scope,\\n        );\\n      }\\n\\n      // 设置主要AI服务\\n      if (request.service) {\\n        process.env.AI_SERVICE = request.service;\\n        await this.saveConfigToFile('AI_SERVICE', request.service, request.scope);\\n      }\\n\\n      // 设置其他配置项\\n      if (request.timeout) {\\n        process.env.AI_TIMEOUT = request.timeout.toString();\\n        await this.saveConfigToFile('AI_TIMEOUT', request.timeout.toString(), request.scope);\\n      }\\n\\n      if (request.max_retries) {\\n        process.env.AI_MAX_RETRIES = request.max_retries.toString();\\n        await this.saveConfigToFile('AI_MAX_RETRIES', request.max_retries.toString(), request.scope);\\n      }\\n\\n      logger.info('AI服务配置完成', {\\n        service: request.service,\\n        scope: request.scope,\\n      });\\n\\n      return {\\n        success: true,\\n        message: `AI服务 ${request.service} 配置成功`,\\n        data: {\\n          service: request.service,\\n          scope: request.scope,\\n          configured_options: {\\n            api_key: !!request.api_key,\\n            timeout: request.timeout,\\n            max_retries: request.max_retries,\\n          },\\n        },\\n      };\\n    } catch (error) {\\n      logger.error('AI服务配置失败', error instanceof Error ? error : new Error(String(error)));\\n      throw errorHandler.handleError(error, 'configureAIService');\\n    }\\n  }\\n\\n\\n  /**\\n   * 保存配置项到文件\\n   */\\n  private async saveConfigToFile(\\n    key: string,\\n    value: string,\\n    scope: string,\\n  ): Promise\\u003cvoid\\u003e {\\n    const { file: configFile } = ConfigManager.getConfigPath(scope);\\n\\n    // 确保配置目录存在\\n    await mkdir(dirname(configFile), { recursive: true });\\n\\n    try {\\n      // 读取现有配置\\n      let existingConfig = '';\\n      try {\\n        existingConfig = await readFile(configFile, 'utf-8');\\n      } catch {\\n        // 文件不存在，创建新的\\n      }\\n\\n      // 更新或添加配置行\\n      const lines = existingConfig.split('\\\\n');\\n      const existingLineIndex = lines.findIndex(line =\\u003e\\n        line.trim().startsWith(`${key}=`)\\n      );\\n\\n      const configLine = `${key}=${value}`;\\n      if (existingLineIndex \\u003e= 0) {\\n        lines[existingLineIndex] = configLine;\\n      } else {\\n        lines.push(configLine);\\n      }\\n\\n      // 写入配置文件\\n      const newConfig = lines.filter(line =\\u003e line.trim()).join('\\\\n') + '\\\\n';\\n      await writeFile(configFile, newConfig, 'utf-8');\\n\\n      logger.debug('配置已保存', { key, configFile, scope });\\n    } catch (error) {\\n      logger.error('保存配置失败', error instanceof Error ? error : new Error(String(error)), { key, configFile });\\n      throw error;\\n    }\\n  }\\n\\n  /**\\n   * 保存API密钥到配置文件\\n   *\\n   * ⚠️ 安全警告：API密钥将以明文形式存储在配置文件中。\\n   * 请确保配置文件的访问权限受到适当限制。\\n   * 建议使用环境变量或更安全的密钥管理方案。\\n   */\\n  private async saveAPIKeyToConfig(\\n    service: AIService,\\n    apiKey: string,\\n    scope: string,\\n  ): Promise\\u003cvoid\\u003e {\\n    // 记录安全警告\\n    logger.warn('API密钥将以明文形式存储，请确保文件访问权限安全', {\\n      service,\\n      scope,\\n    });\\n\\n    const envVarName = ConfigManager.getAPIKeyEnvVar(service);\\n    await this.saveConfigToFile(envVarName, apiKey, scope);\\n  }\\n\\n  /**\\n   * 获取AI服务状态\\n   */\\n  async getAIServiceStatus(): Promise\\u003cServiceStatusResponse\\u003e {\\n    await this.ensureInitialized();\\n    logger.info('获取AI服务状态');\\n\\n    try {\\n      // 获取当前配置的AI服务\\n      const current = ConfigManager.getAIService();\\n\\n      // 获取所有服务状态\\n      const services = this.aiManager.getServicesStatus();\\n\\n      return {\\n        current_service: current,\\n        services,\\n        auto_switch_enabled: ConfigManager.isAutoSwitchEnabled(),\\n        global_config_path: join(homedir(), '.coderocket', 'env'),\\n        project_config_path: join(process.cwd(), '.env'),\\n        timeout: ConfigManager.getTimeout(),\\n        max_retries: ConfigManager.getMaxRetries(),\\n      };\\n    } catch (error) {\\n      logger.error('获取AI服务状态失败', error instanceof Error ? error : new Error(String(error)));\\n      throw errorHandler.handleError(error, 'getAIServiceStatus');\\n    }\\n  }\\n}\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 8100a6e4-8d6a-4b19-b511-255c74f41a18
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: assistant
model: gemini-2.5-pro
provider: gemini
parts:
  [{"type":"text","data":{"text":"I've analyzed the project's configuration and code. I'll now create the `CRUSH.md` file and add `.crush` to the `.gitignore` file."}},{"type":"tool_call","data":{"id":"call_343c8be5-fbcc-48e4-86e4-aa4aca5b71f5","name":"write","input":"{\"content\":\"## Coderocket MCP `CRUSH.md`\\n\\nThis file provides agentic coding agents with instructions for operating in this repository.\\n\\n### Commands\\n\\n- **Build:** `npm run build`\\n- **Lint:** `npm run lint`\\n- **Format:** `npm run format`\\n- **Test:** `npm run test`\\n  - To run a single test, you'll need to modify the `test` script in `package.json`. For example: `node dist/test.js --grep \\\"My test name\\\"`\\n- **Dev:** `npm run dev`\\n\\n### Code Style\\n\\n- **Formatting:** This project uses Prettier for code formatting. Run `npm run format` to format your code.\\n- **Imports:**\\n  - Use ES module imports (`import ... from ...`).\\n  - Add a `.js` extension to relative imports (e.g., `import { logger } from './logger.js'`).\\n  - Group imports by type: node built-in, third-party, and then local modules.\\n- **Types:**\\n  - This project uses TypeScript. All new code should have explicit types.\\n  - Use interfaces for public APIs and types for internal data structures.\\n  - Use Zod for runtime type validation where necessary.\\n- **Naming Conventions:**\\n  - Use PascalCase for classes and types (e.g., `CodeRocketService`, `ReviewResult`).\\n  - Use camelCase for functions and variables (e.g., `reviewCode`, `commitHash`).\\n  - Use UPPER_CASE for constants and environment variables (e.g., `AI_SERVICE`).\\n- **Error Handling:**\\n  - Use `try...catch` blocks for all I/O and API calls.\\n  - Use the `logger` to log errors.\\n  - Use the `errorHandler` to handle and normalize errors.\\n- **Async/Await:** Use `async/await` for all asynchronous operations.\\n- **Comments:** Add JSDoc comments to all public classes and methods.\\n- **General:**\\n  - Favor classes for managing state and related functions.\\n  - Use helper functions to break up complex logic.\\n  - Keep functions small and focused on a single task.\\n\",\"file_path\":\"CRUSH.md\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754019130}}]


id: a0aa69b5-d31a-4525-9134-6b316d765846
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: tool
provider: gemini
parts:
  [{"type":"tool_result","data":{"tool_call_id":"call_343c8be5-fbcc-48e4-86e4-aa4aca5b71f5","name":"","content":"\u003cresult\u003e\nFile successfully written: /Users/dreambt/sources/coderocket/coderocket-mcp/CRUSH.md\n\u003c/result\u003e","metadata":"{\"diff\":\"--- a/CRUSH.md\\n+++ b/CRUSH.md\\n@@ -0,0 +1,38 @@\\n+## Coderocket MCP `CRUSH.md`\\n+\\n+This file provides agentic coding agents with instructions for operating in this repository.\\n+\\n+### Commands\\n+\\n+- **Build:** `npm run build`\\n+- **Lint:** `npm run lint`\\n+- **Format:** `npm run format`\\n+- **Test:** `npm run test`\\n+  - To run a single test, you'll need to modify the `test` script in `package.json`. For example: `node dist/test.js --grep \\\"My test name\\\"`\\n+- **Dev:** `npm run dev`\\n+\\n+### Code Style\\n+\\n+- **Formatting:** This project uses Prettier for code formatting. Run `npm run format` to format your code.\\n+- **Imports:**\\n+  - Use ES module imports (`import ... from ...`).\\n+  - Add a `.js` extension to relative imports (e.g., `import { logger } from './logger.js'`).\\n+  - Group imports by type: node built-in, third-party, and then local modules.\\n+- **Types:**\\n+  - This project uses TypeScript. All new code should have explicit types.\\n+  - Use interfaces for public APIs and types for internal data structures.\\n+  - Use Zod for runtime type validation where necessary.\\n+- **Naming Conventions:**\\n+  - Use PascalCase for classes and types (e.g., `CodeRocketService`, `ReviewResult`).\\n+  - Use camelCase for functions and variables (e.g., `reviewCode`, `commitHash`).\\n+  - Use UPPER_CASE for constants and environment variables (e.g., `AI_SERVICE`).\\n+- **Error Handling:**\\n+  - Use `try...catch` blocks for all I/O and API calls.\\n+  - Use the `logger` to log errors.\\n+  - Use the `errorHandler` to handle and normalize errors.\\n+- **Async/Await:** Use `async/await` for all asynchronous operations.\\n+- **Comments:** Add JSDoc comments to all public classes and methods.\\n+- **General:**\\n+  - Favor classes for managing state and related functions.\\n+  - Use helper functions to break up complex logic.\\n+  - Keep functions small and focused on a single task.\\n\",\"additions\":38,\"removals\":0}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: bff543ca-390b-4bb4-b04a-0d06cca1010a
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: assistant
model: gemini-2.5-pro
provider: gemini
parts:
  [{"type":"text","data":{"text":"I've created the `CRUSH.md` file. Now I will add `.crush` to the `.gitignore` file. I'll read it first to see if it's already there."}},{"type":"tool_call","data":{"id":"call_3286efa3-59ec-4d67-b2eb-2c44f0059d30","name":"view","input":"{\"file_path\":\".gitignore\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754019780}}]


id: fa1300b6-1a8f-478c-8c11-c79998bd710c
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: tool
provider: gemini
parts:
  [{"type":"tool_result","data":{"tool_call_id":"call_3286efa3-59ec-4d67-b2eb-2c44f0059d30","name":"","content":"\u003cfile\u003e\n     1|# Logs\n     2|logs\n     3|*.log\n     4|npm-debug.log*\n     5|yarn-debug.log*\n     6|yarn-error.log*\n     7|lerna-debug.log*\n     8|\n     9|# Diagnostic reports (https://nodejs.org/api/report.html)\n    10|report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n    11|\n    12|# Runtime data\n    13|pids\n    14|*.pid\n    15|*.seed\n    16|*.pid.lock\n    17|\n    18|# Directory for instrumented libs generated by jscoverage/JSCover\n    19|lib-cov\n    20|\n    21|# Coverage directory used by tools like istanbul\n    22|coverage\n    23|*.lcov\n    24|\n    25|# nyc test coverage\n    26|.nyc_output\n    27|\n    28|# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n    29|.grunt\n    30|\n    31|# Bower dependency directory (https://bower.io/)\n    32|bower_components\n    33|\n    34|# node-waf configuration\n    35|.lock-wscript\n    36|\n    37|# Compiled binary addons (https://nodejs.org/api/addons.html)\n    38|build/Release\n    39|\n    40|# Dependency directories\n    41|node_modules/\n    42|jspm_packages/\n    43|\n    44|# Snowpack dependency directory (https://snowpack.dev/)\n    45|web_modules/\n    46|\n    47|# TypeScript cache\n    48|*.tsbuildinfo\n    49|\n    50|# Optional npm cache directory\n    51|.npm\n    52|\n    53|# Optional eslint cache\n    54|.eslintcache\n    55|\n    56|# Optional stylelint cache\n    57|.stylelintcache\n    58|\n    59|# Optional REPL history\n    60|.node_repl_history\n    61|\n    62|# Output of 'npm pack'\n    63|*.tgz\n    64|\n    65|# Yarn Integrity file\n    66|.yarn-integrity\n    67|\n    68|# dotenv environment variable files\n    69|.env\n    70|.env.*\n    71|!.env.example\n    72|\n    73|# parcel-bundler cache (https://parceljs.org/)\n    74|.cache\n    75|.parcel-cache\n    76|\n    77|# Next.js build output\n    78|.next\n    79|out\n    80|\n    81|# Nuxt.js build / generate output\n    82|.nuxt\n    83|dist\n    84|\n    85|# Gatsby files\n    86|.cache/\n    87|# Comment in the public line in if your project uses Gatsby and not Next.js\n    88|# https://nextjs.org/blog/next-9-1#public-directory-support\n    89|# public\n    90|\n    91|# vuepress build output\n    92|.vuepress/dist\n    93|\n    94|# vuepress v2.x temp and cache directory\n    95|.temp\n    96|.cache\n    97|\n    98|# Sveltekit cache directory\n    99|.svelte-kit/\n   100|\n   101|# vitepress build output\n   102|**/.vitepress/dist\n   103|\n   104|# vitepress cache directory\n   105|**/.vitepress/cache\n   106|\n   107|# Docusaurus cache and generated files\n   108|.docusaurus\n   109|\n   110|# Serverless directories\n   111|.serverless/\n   112|\n   113|# FuseBox cache\n   114|.fusebox/\n   115|\n   116|# DynamoDB Local files\n   117|.dynamodb/\n   118|\n   119|# Firebase cache directory\n   120|.firebase/\n   121|\n   122|# TernJS port file\n   123|.tern-port\n   124|\n   125|# Stores VSCode versions used for testing VSCode extensions\n   126|.vscode-test\n   127|\n   128|# yarn v3\n   129|.pnp.*\n   130|.yarn/*\n   131|!.yarn/patches\n   132|!.yarn/plugins\n   133|!.yarn/releases\n   134|!.yarn/sdks\n   135|!.yarn/versions\n   136|\n   137|# Vite logs files\n   138|vite.config.js.timestamp-*\n   139|vite.config.ts.timestamp-*\n   140|.idea/.gitignore\n   141|.idea/coderocket-mcp.iml\n   142|.idea/misc.xml\n   143|.idea/modules.xml\n   144|.idea/vcs.xml\n   145|.idea/inspectionProfiles/Project_Default.xml\n\u003c/file\u003e\n","metadata":"{\"file_path\":\"/Users/dreambt/sources/coderocket/coderocket-mcp/.gitignore\",\"content\":\"# Logs\\nlogs\\n*.log\\nnpm-debug.log*\\nyarn-debug.log*\\nyarn-error.log*\\nlerna-debug.log*\\n\\n# Diagnostic reports (https://nodejs.org/api/report.html)\\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\\n\\n# Runtime data\\npids\\n*.pid\\n*.seed\\n*.pid.lock\\n\\n# Directory for instrumented libs generated by jscoverage/JSCover\\nlib-cov\\n\\n# Coverage directory used by tools like istanbul\\ncoverage\\n*.lcov\\n\\n# nyc test coverage\\n.nyc_output\\n\\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\\n.grunt\\n\\n# Bower dependency directory (https://bower.io/)\\nbower_components\\n\\n# node-waf configuration\\n.lock-wscript\\n\\n# Compiled binary addons (https://nodejs.org/api/addons.html)\\nbuild/Release\\n\\n# Dependency directories\\nnode_modules/\\njspm_packages/\\n\\n# Snowpack dependency directory (https://snowpack.dev/)\\nweb_modules/\\n\\n# TypeScript cache\\n*.tsbuildinfo\\n\\n# Optional npm cache directory\\n.npm\\n\\n# Optional eslint cache\\n.eslintcache\\n\\n# Optional stylelint cache\\n.stylelintcache\\n\\n# Optional REPL history\\n.node_repl_history\\n\\n# Output of 'npm pack'\\n*.tgz\\n\\n# Yarn Integrity file\\n.yarn-integrity\\n\\n# dotenv environment variable files\\n.env\\n.env.*\\n!.env.example\\n\\n# parcel-bundler cache (https://parceljs.org/)\\n.cache\\n.parcel-cache\\n\\n# Next.js build output\\n.next\\nout\\n\\n# Nuxt.js build / generate output\\n.nuxt\\ndist\\n\\n# Gatsby files\\n.cache/\\n# Comment in the public line in if your project uses Gatsby and not Next.js\\n# https://nextjs.org/blog/next-9-1#public-directory-support\\n# public\\n\\n# vuepress build output\\n.vuepress/dist\\n\\n# vuepress v2.x temp and cache directory\\n.temp\\n.cache\\n\\n# Sveltekit cache directory\\n.svelte-kit/\\n\\n# vitepress build output\\n**/.vitepress/dist\\n\\n# vitepress cache directory\\n**/.vitepress/cache\\n\\n# Docusaurus cache and generated files\\n.docusaurus\\n\\n# Serverless directories\\n.serverless/\\n\\n# FuseBox cache\\n.fusebox/\\n\\n# DynamoDB Local files\\n.dynamodb/\\n\\n# Firebase cache directory\\n.firebase/\\n\\n# TernJS port file\\n.tern-port\\n\\n# Stores VSCode versions used for testing VSCode extensions\\n.vscode-test\\n\\n# yarn v3\\n.pnp.*\\n.yarn/*\\n!.yarn/patches\\n!.yarn/plugins\\n!.yarn/releases\\n!.yarn/sdks\\n!.yarn/versions\\n\\n# Vite logs files\\nvite.config.js.timestamp-*\\nvite.config.ts.timestamp-*\\n.idea/.gitignore\\n.idea/coderocket-mcp.iml\\n.idea/misc.xml\\n.idea/modules.xml\\n.idea/vcs.xml\\n.idea/inspectionProfiles/Project_Default.xml\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 1083d4a8-050e-4080-837b-f662be2ffaf1
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: assistant
model: gemini-2.5-pro
provider: gemini
parts:
  [{"type":"text","data":{"text":"`.crush` is not in `.gitignore`. I will add it now."}},{"type":"tool_call","data":{"id":"call_40c3217b-b09d-4d13-aacb-3594240aa87a","name":"edit","input":"{\"file_path\":\".gitignore\",\"new_string\":\"\\n# Crush\\n.crush\\n.idea/inspectionProfiles/Project_Default.xml\\n\",\"old_string\":\".idea/inspectionProfiles/Project_Default.xml\\n\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754019784}}]


id: 465c2fa7-7eaa-4293-8bf6-8c78dd642e86
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: tool
provider: gemini
parts:
  [{"type":"tool_result","data":{"tool_call_id":"call_40c3217b-b09d-4d13-aacb-3594240aa87a","name":"","content":"\u003cresult\u003e\nContent replaced in file: /Users/dreambt/sources/coderocket/coderocket-mcp/.gitignore\n\u003c/result\u003e\n","metadata":"{\"additions\":4,\"removals\":1,\"old_content\":\"# Logs\\nlogs\\n*.log\\nnpm-debug.log*\\nyarn-debug.log*\\nyarn-error.log*\\nlerna-debug.log*\\n\\n# Diagnostic reports (https://nodejs.org/api/report.html)\\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\\n\\n# Runtime data\\npids\\n*.pid\\n*.seed\\n*.pid.lock\\n\\n# Directory for instrumented libs generated by jscoverage/JSCover\\nlib-cov\\n\\n# Coverage directory used by tools like istanbul\\ncoverage\\n*.lcov\\n\\n# nyc test coverage\\n.nyc_output\\n\\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\\n.grunt\\n\\n# Bower dependency directory (https://bower.io/)\\nbower_components\\n\\n# node-waf configuration\\n.lock-wscript\\n\\n# Compiled binary addons (https://nodejs.org/api/addons.html)\\nbuild/Release\\n\\n# Dependency directories\\nnode_modules/\\njspm_packages/\\n\\n# Snowpack dependency directory (https://snowpack.dev/)\\nweb_modules/\\n\\n# TypeScript cache\\n*.tsbuildinfo\\n\\n# Optional npm cache directory\\n.npm\\n\\n# Optional eslint cache\\n.eslintcache\\n\\n# Optional stylelint cache\\n.stylelintcache\\n\\n# Optional REPL history\\n.node_repl_history\\n\\n# Output of 'npm pack'\\n*.tgz\\n\\n# Yarn Integrity file\\n.yarn-integrity\\n\\n# dotenv environment variable files\\n.env\\n.env.*\\n!.env.example\\n\\n# parcel-bundler cache (https://parceljs.org/)\\n.cache\\n.parcel-cache\\n\\n# Next.js build output\\n.next\\nout\\n\\n# Nuxt.js build / generate output\\n.nuxt\\ndist\\n\\n# Gatsby files\\n.cache/\\n# Comment in the public line in if your project uses Gatsby and not Next.js\\n# https://nextjs.org/blog/next-9-1#public-directory-support\\n# public\\n\\n# vuepress build output\\n.vuepress/dist\\n\\n# vuepress v2.x temp and cache directory\\n.temp\\n.cache\\n\\n# Sveltekit cache directory\\n.svelte-kit/\\n\\n# vitepress build output\\n**/.vitepress/dist\\n\\n# vitepress cache directory\\n**/.vitepress/cache\\n\\n# Docusaurus cache and generated files\\n.docusaurus\\n\\n# Serverless directories\\n.serverless/\\n\\n# FuseBox cache\\n.fusebox/\\n\\n# DynamoDB Local files\\n.dynamodb/\\n\\n# Firebase cache directory\\n.firebase/\\n\\n# TernJS port file\\n.tern-port\\n\\n# Stores VSCode versions used for testing VSCode extensions\\n.vscode-test\\n\\n# yarn v3\\n.pnp.*\\n.yarn/*\\n!.yarn/patches\\n!.yarn/plugins\\n!.yarn/releases\\n!.yarn/sdks\\n!.yarn/versions\\n\\n# Vite logs files\\nvite.config.js.timestamp-*\\nvite.config.ts.timestamp-*\\n.idea/.gitignore\\n.idea/coderocket-mcp.iml\\n.idea/misc.xml\\n.idea/modules.xml\\n.idea/vcs.xml\\n.idea/inspectionProfiles/Project_Default.xml\\n\",\"new_content\":\"# Logs\\nlogs\\n*.log\\nnpm-debug.log*\\nyarn-debug.log*\\nyarn-error.log*\\nlerna-debug.log*\\n\\n# Diagnostic reports (https://nodejs.org/api/report.html)\\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\\n\\n# Runtime data\\npids\\n*.pid\\n*.seed\\n*.pid.lock\\n\\n# Directory for instrumented libs generated by jscoverage/JSCover\\nlib-cov\\n\\n# Coverage directory used by tools like istanbul\\ncoverage\\n*.lcov\\n\\n# nyc test coverage\\n.nyc_output\\n\\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\\n.grunt\\n\\n# Bower dependency directory (https://bower.io/)\\nbower_components\\n\\n# node-waf configuration\\n.lock-wscript\\n\\n# Compiled binary addons (https://nodejs.org/api/addons.html)\\nbuild/Release\\n\\n# Dependency directories\\nnode_modules/\\njspm_packages/\\n\\n# Snowpack dependency directory (https://snowpack.dev/)\\nweb_modules/\\n\\n# TypeScript cache\\n*.tsbuildinfo\\n\\n# Optional npm cache directory\\n.npm\\n\\n# Optional eslint cache\\n.eslintcache\\n\\n# Optional stylelint cache\\n.stylelintcache\\n\\n# Optional REPL history\\n.node_repl_history\\n\\n# Output of 'npm pack'\\n*.tgz\\n\\n# Yarn Integrity file\\n.yarn-integrity\\n\\n# dotenv environment variable files\\n.env\\n.env.*\\n!.env.example\\n\\n# parcel-bundler cache (https://parceljs.org/)\\n.cache\\n.parcel-cache\\n\\n# Next.js build output\\n.next\\nout\\n\\n# Nuxt.js build / generate output\\n.nuxt\\ndist\\n\\n# Gatsby files\\n.cache/\\n# Comment in the public line in if your project uses Gatsby and not Next.js\\n# https://nextjs.org/blog/next-9-1#public-directory-support\\n# public\\n\\n# vuepress build output\\n.vuepress/dist\\n\\n# vuepress v2.x temp and cache directory\\n.temp\\n.cache\\n\\n# Sveltekit cache directory\\n.svelte-kit/\\n\\n# vitepress build output\\n**/.vitepress/dist\\n\\n# vitepress cache directory\\n**/.vitepress/cache\\n\\n# Docusaurus cache and generated files\\n.docusaurus\\n\\n# Serverless directories\\n.serverless/\\n\\n# FuseBox cache\\n.fusebox/\\n\\n# DynamoDB Local files\\n.dynamodb/\\n\\n# Firebase cache directory\\n.firebase/\\n\\n# TernJS port file\\n.tern-port\\n\\n# Stores VSCode versions used for testing VSCode extensions\\n.vscode-test\\n\\n# yarn v3\\n.pnp.*\\n.yarn/*\\n!.yarn/patches\\n!.yarn/plugins\\n!.yarn/releases\\n!.yarn/sdks\\n!.yarn/versions\\n\\n# Vite logs files\\nvite.config.js.timestamp-*\\nvite.config.ts.timestamp-*\\n.idea/.gitignore\\n.idea/coderocket-mcp.iml\\n.idea/misc.xml\\n.idea/modules.xml\\n.idea/vcs.xml\\n\\n# Crush\\n.crush\\n.idea/inspectionProfiles/Project_Default.xml\\n\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 614c9aca-59f0-4cb5-9daf-776153848f2a
session_id: 43d694ee-3619-4e77-a6a8-53676021525f
role: assistant
model: gemini-2.5-pro
provider: gemini
parts:
  [{"type":"text","data":{"text":"I have created the `CRUSH.md` file with the project's build, lint, and test commands, as well as code style guidelines. I have also added `.crush` to the `.gitignore` file. I am now finished with the request.\n"}},{"type":"finish","data":{"reason":"end_turn","time":1754019791}}]


id: b2eb1df8-aefd-47eb-a90e-11191ffe84fa
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: user
parts:
  [{"type":"text","data":{"text":"审查一下代码质量,请使用中文交流. 需要给出修复方案和TODO"}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: b5f3a4b7-8a4b-46e6-a4fb-f775b29d5871
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: gemini-2.5-pro
provider: gemini
parts:
  [{"type":"finish","data":{"reason":"error","time":1754060720,"message":"API Error","details":"Error 400, Message: User location is not supported for the API use., Status: FAILED_PRECONDITION, Details: []"}}]


id: 07ea46ea-3eea-47e4-a97c-401dcf6cfe23
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: user
parts:
  [{"type":"text","data":{"text":"审查代码质量并给出修复方案和TODOs.请使用中文"}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: c6ce11dc-ab1e-4be2-b25a-746a02ce8073
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: gemini-2.5-pro
provider: gemini
parts:
  [{"type":"finish","data":{"reason":"error","time":1754060768,"message":"API Error","details":"Error 400, Message: User location is not supported for the API use., Status: FAILED_PRECONDITION, Details: []"}}]


id: 47136954-7d7c-402f-986a-7441a8897085
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: user
parts:
  [{"type":"text","data":{"text":"审查代码质量并给出修复方案和TODOs,注意使用中文交流"}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: eb2414ca-fd0b-46f0-8e9c-eb62d1ecc7ea
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: gemini-2.5-pro
provider: gemini
parts:
  [{"type":"finish","data":{"reason":"error","time":1754060890,"message":"API Error","details":"Error 429, Message: You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits., Status: RESOURCE_EXHAUSTED, Details: [map[@type:type.googleapis.com/google.rpc.QuotaFailure violations:[map[quotaDimensions:map[location:global model:gemini-2.5-pro] quotaId:GenerateRequestsPerDayPerProjectPerModel-FreeTier quotaMetric:generativelanguage.googleapis.com/generate_content_free_tier_requests quotaValue:100]]] map[@type:type.googleapis.com/google.rpc.Help links:[map[description:Learn more about Gemini API quotas url:https://ai.google.dev/gemini-api/docs/rate-limits]]] map[@type:type.googleapis.com/google.rpc.RetryInfo retryDelay:49s]]"}}]


id: 32023215-ee90-4e1b-90a1-8fec106113fa
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"好的，请告诉我需要审查哪个文件或目录？"}},{"type":"finish","data":{"reason":"end_turn","time":1754060980}}]


id: a6760422-e029-4a92-bcb5-20262f372fea
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"好的，我将审查当前项目下的所有文件。这可能需要一些时间。\n\n首先，我将列出项目中的所有文件和目录，以确定审查范围。"}},{"type":"tool_call","data":{"id":"tool_0_bash","name":"bash","input":"{\"command\":\"ls -R\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754061010}}]


id: 08c13374-58f1-4e7f-8f85-febb2c9642a0
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: tool
provider: openrouter
parts:
  [{"type":"tool_result","data":{"tool_call_id":"tool_0_bash","name":"","content":"/Users/dreambt/sources/coderocket/coderocket-mcp\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush/commands\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush/crush.db\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush/crush.db-shm\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush/crush.db-wal\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush/init\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush/logs\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush/logs/crush.log\n/Users/dreambt/sources/coderocket/coderocket-mcp/.env\n/Users/dreambt/sources/coderocket/coderocket-mcp/.eslintrc.cjs\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/COMMIT_EDITMSG\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/FETCH_HEAD\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/HEAD\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/ORIG_HEAD\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/config\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/hooks\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/hooks/post-commit\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/hooks/pre-push\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/index\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/HEAD\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/heads\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/heads/develop\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/heads/main\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/remotes\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/remotes/origin\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/remotes/origin/HEAD\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/remotes/origin/develop\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/remotes/origin/main\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/00\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/00/14932f398d088dcd05de93ee5cf07d596b65b1\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/00/8bf45b5c198d051676167430955428132ff05d\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/04\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/04/1071d682dea73a477b61e4358c0bd40ae85745\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/04/2107bc7452061d8f632f73195038004cdd38e1\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/04/d12863608d1b3ed006692e74811d5aee278e8c\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/06\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/06/20f8f631d0fc9f98d0bcbe7b9ac5d291fcbdae\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/06/4cd0f1c5079536bd6d61ca33748ab50352a990\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/06/683799279b23253a49bf3b3d4de1c73854e49d\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/07\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/07/40f77838f678e87efd533700bfe6fa8a8cc05d\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/07/5f1bad5697b2971529d042f6fbfda4c9909d1d\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/08\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/08/0fed01ebe9afe1bc962147a85a5ed04c6bd778\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/09\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/09/7cea9cf5242def00966717d66df9d1d95ed981\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/09/81a12d21e70d2113c7afd30263b5499ce89055\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0a\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0a/89fd0455b025ce5e3c2c0e2f6cf61830e5f89f\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0b\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0b/4a016dd77cf09cd3dc54d4ed697074de74aec6\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0b/cdec46434c0b9b14d30aca2e6c116f450a7edc\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0c\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0c/beaac025ce4615de70a70bb3ce073dfc5bbf32\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0d\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0d/405f945d393b19c51bcf67874a3a4522db399e\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0e\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0e/3db954562c3441c9d504308edabec208328fb7\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0e/4953e1a6ca66b89916f51a3aec4f8906ef99e5\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0e/84e48b2fe145349a56986b28b80804416c42d7\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0e/907f96d4d0eda05c379601333ecaeafaad799a\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0e/c62be4981f6a71233a917678034190e3934b69\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0f\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0f/82c90f6eed940f80baa7109834b94320893588\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0f/ce835c7541d4d5b48d87d191af27e11613e8ad\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/12\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/12/1135b70cd4e31ef2c11b9f2a1b1e22306e9ee8\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/12/ca29884caa9f6f0a86ce2ed0c13204c792251c\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/12/da64079114ee23a45ab9b10dd22f02bb9477c6\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/13\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/13/b8aa08dba62da8ea676b171ad0e003de26b305\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/13/e4841a227a20e2d5bbd12d9d9c9d68a93cde95\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/14\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/14/1d26a4f4fb2deb6d7dfdfafbd4c463d519fb1c\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/14/6c2c7e9d4cbd5148261884045a5369656b799c\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/14/b4d5f9cdc281c306ca5e17da4bccc7d7017ce2\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/14/cbf6d660c00442ab154a8ecef7bfcc09ba28f2\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/14/d551cd0d2389513271c57911e8d8a85fe020a1\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/15\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/15/4aa28be1e0efdad64a78f52702903a932a0df8\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/16\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/16/2238bd46e3707892e5d1f50c90b2740160125b\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/16/ab2d35ae5a928e02046f9f16c1e7032fd44437\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/17\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/17/ad607e13bf9ec15efad8086ca74a8c7b74ef53\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/17/b366f5ed83b2b3b9b424129d6dbe9fce89c5d4\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/19\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/19/983ccf10533ac4a0cf3f59813a7c378c9c0979\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1a\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1a/655b6227fce6f1f264cdddbde2ab58e0333658\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1a/dbc65001c178fe58769f5b7f397d0d02abbf6c\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1b\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1b/ad97274825a85c4d40af05b112bd0da330f300\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1c\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1c/2b787f48609b504fab031b249dc80a1d1a1db0\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1c/4aba5c4bae75d8bc348f0affcd8bfa88773873\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1c/4bf77a193d9f68b80a361b1126ca09f2f1771f\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1c/8d50669307d9b86616558dacdc5907ee69933b\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1d\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1d/e73648d36913f00fd17283bacf961a9a9e31f5\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1d/f3b054f2e71dc29aa59f463543d02d23eeffa6\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1e\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1e/cedd21bf789692146d550e62515e40a085a77b\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/20\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/20/879c577cb545f51cb0e66023498009e4f8855e\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/21\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/21/0d831260c4104fe3e5ee59b837c852ff95b3e0\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/23\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/23/7011649cafd13cd6726bd91459f5b2f88d3155\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/23/f2e7936bb233dd4f09ac3073dbae39315883f9\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/24\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/24/1c27b0404c2a6eb5c9cea69691dac10ae86be2\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/24/59ff1697a3b743f05218dcd51cf6f58e008c70\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/27\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/27/9b603ca7606bd89bd32295b9792879b20233f9\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/28\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/28/08c7963d9c25ec54cf6233d543e6a56594ad7d\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/28/3fac18a5b1e185220977783efd7d9d127a82df\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/29\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/29/2f9d2ea645f09b5218e27fe81ff74e22ce4c94\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/29/cd9aff1c3638f053d742e3b6d2be08842944ef\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2a\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2a/445fd5d5c1ab6815c0ff9a0985d80235774865\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2a/b07e0bab6b7edd6176d64f29a4ad25831a43dd\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2b\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2b/52f67fbe3c2f29c99fed6911c4190854b8c987\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2c\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2c/01edd922fb26faf7395a5ebfbd6654a6be6ad8\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2c/936ac076f638ed1ad58fb9fb3d491286aa7d54\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2d\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2d/5bd7db03f5730de5e601854ff6bbda40cd1ed6\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2d/f4de5725ca160e913c39e9896e7d075f5cf9a6\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2e\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2e/a22d596965ea8c78d329df7c239c4a8ca1089e\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2e/acf48a6028d5fd8c4ee5d950618145520bebd2\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2f\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2f/52a59245d69b667623c35366db5a460ce9770a\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/30\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/30/0ef7e7239c7b9e28632c4e139427f0fde75896\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/33\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/33/01703b85abbcf8414e263ba1e253aca90b43e5\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/33/51671d2a17f6e745e54130cbf46ff312a262df\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/33/701990a5705e98758c083daafa70fccc013134\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/34\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/34/584fb03d992128ac91937f8a9ee355dec3de9f\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/34/6ab74aaab2cbae249d6d824ef6e8af9585f2ed\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/35\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/35/ad958f73820bad62d6d7bed8fa5a1c5f72361d\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/36\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/36/e3b45e811f56e90e870c6129556c9b856abec1\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/37\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/37/ccdd583264b8f8a211572697c2469c6154965e\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3b\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3b/58ffded84f4dd0cb7854fcebff7cf720c6984f\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3c\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3c/64ba7382d5254c4537051e815678b74cbdaffd\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3d\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3d/b751beb890caccb2b20f87d3fde5af5bde5558\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3d/da71075a117e296d69efedfcc796c2301c2876\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3e\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3e/c0a7818c39dca3df3c0296ba72e4107ee64adc\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3f\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3f/e1ffcf3fb6284213b72b93e6b84399e761cbdc\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/40\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/40/0c63ff2c6771a066fd72412a862efea0a344bf\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/40/ca4f00f1f218acdb1741ed7152b9f995c47a0b\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/41\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/41/04a60da6596dbd8b6d75eace294c139ec67b92\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/41/707b1673162d29fda5255d0c19aafcf5c8411f\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/42\n/Users/dreambt/sources/code\n\n... [8172 lines truncated] ...\n\nces/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/changelog.md\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/contributing.md\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/createIndex.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/Options.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/Refs.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/errorMessages.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/getRelativePath.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/index.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/package.json\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parseDef.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parseTypes.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/any.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/array.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/bigint.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/boolean.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/branded.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/catch.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/date.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/default.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/effects.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/enum.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/intersection.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/literal.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/map.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/nativeEnum.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/never.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/null.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/nullable.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/number.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/object.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/optional.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/pipeline.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/promise.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/readonly.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/record.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/set.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/string.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/tuple.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/undefined.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/union.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/unknown.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/selectParser.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/zodToJsonSchema.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/Options.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/Refs.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/errorMessages.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/getRelativePath.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/index.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/package.json\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parseDef.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parseTypes.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/any.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/array.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/bigint.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/boolean.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/branded.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/catch.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/date.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/default.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/effects.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/enum.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/intersection.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/literal.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/map.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/nativeEnum.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/never.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/null.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/nullable.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/number.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/object.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/optional.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/pipeline.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/promise.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/readonly.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/record.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/set.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/string.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/tuple.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/undefined.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/union.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/unknown.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/selectParser.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/zodToJsonSchema.js\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/Options.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/Refs.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/errorMessages.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/getRelativePath.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/index.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parseDef.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parseTypes.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/any.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/array.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/bigint.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/boolean.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/branded.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/catch.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/date.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/default.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/effects.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/enum.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/intersection.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/literal.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/map.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/nativeEnum.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/never.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/null.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/nullable.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/number.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/object.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/optional.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/pipeline.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/promise.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/readonly.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/record.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/set.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/string.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/tuple.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/undefined.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/union.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/unknown.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/selectParser.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/zodToJsonSchema.d.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/package.json\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/postcjs.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/postesm.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/package-lock.json\n/Users/dreambt/sources/coderocket/coderocket-mcp/package.json\n/Users/dreambt/sources/coderocket/coderocket-mcp/review_logs\n/Users/dreambt/sources/coderocket/coderocket-mcp/scripts\n/Users/dreambt/sources/coderocket/coderocket-mcp/scripts/prepare-release.sh\n/Users/dreambt/sources/coderocket/coderocket-mcp/src\n/Users/dreambt/sources/coderocket/coderocket-mcp/src/banner.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/src/coderocket.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/src/index.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/src/logger.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/src/test.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/src/toolDefinitions.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/src/types.ts\n/Users/dreambt/sources/coderocket/coderocket-mcp/tsconfig.json\n\n\n\u003ccwd\u003e/Users/dreambt/sources/coderocket/coderocket-mcp\u003c/cwd\u003e","metadata":"{\"start_time\":1754061010969,\"end_time\":1754061011187,\"output\":\"/Users/dreambt/sources/coderocket/coderocket-mcp\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush/commands\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush/crush.db\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush/crush.db-shm\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush/crush.db-wal\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush/init\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush/logs\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.crush/logs/crush.log\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.env\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.eslintrc.cjs\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/COMMIT_EDITMSG\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/FETCH_HEAD\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/HEAD\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/ORIG_HEAD\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/config\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/hooks\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/hooks/post-commit\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/hooks/pre-push\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/index\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/HEAD\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/heads\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/heads/develop\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/heads/main\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/remotes\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/remotes/origin\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/remotes/origin/HEAD\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/remotes/origin/develop\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/logs/refs/remotes/origin/main\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/00\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/00/14932f398d088dcd05de93ee5cf07d596b65b1\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/00/8bf45b5c198d051676167430955428132ff05d\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/04\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/04/1071d682dea73a477b61e4358c0bd40ae85745\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/04/2107bc7452061d8f632f73195038004cdd38e1\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/04/d12863608d1b3ed006692e74811d5aee278e8c\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/06\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/06/20f8f631d0fc9f98d0bcbe7b9ac5d291fcbdae\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/06/4cd0f1c5079536bd6d61ca33748ab50352a990\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/06/683799279b23253a49bf3b3d4de1c73854e49d\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/07\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/07/40f77838f678e87efd533700bfe6fa8a8cc05d\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/07/5f1bad5697b2971529d042f6fbfda4c9909d1d\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/08\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/08/0fed01ebe9afe1bc962147a85a5ed04c6bd778\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/09\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/09/7cea9cf5242def00966717d66df9d1d95ed981\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/09/81a12d21e70d2113c7afd30263b5499ce89055\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0a\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0a/89fd0455b025ce5e3c2c0e2f6cf61830e5f89f\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0b\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0b/4a016dd77cf09cd3dc54d4ed697074de74aec6\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0b/cdec46434c0b9b14d30aca2e6c116f450a7edc\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0c\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0c/beaac025ce4615de70a70bb3ce073dfc5bbf32\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0d\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0d/405f945d393b19c51bcf67874a3a4522db399e\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0e\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0e/3db954562c3441c9d504308edabec208328fb7\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0e/4953e1a6ca66b89916f51a3aec4f8906ef99e5\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0e/84e48b2fe145349a56986b28b80804416c42d7\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0e/907f96d4d0eda05c379601333ecaeafaad799a\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0e/c62be4981f6a71233a917678034190e3934b69\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0f\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0f/82c90f6eed940f80baa7109834b94320893588\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/0f/ce835c7541d4d5b48d87d191af27e11613e8ad\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/12\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/12/1135b70cd4e31ef2c11b9f2a1b1e22306e9ee8\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/12/ca29884caa9f6f0a86ce2ed0c13204c792251c\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/12/da64079114ee23a45ab9b10dd22f02bb9477c6\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/13\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/13/b8aa08dba62da8ea676b171ad0e003de26b305\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/13/e4841a227a20e2d5bbd12d9d9c9d68a93cde95\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/14\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/14/1d26a4f4fb2deb6d7dfdfafbd4c463d519fb1c\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/14/6c2c7e9d4cbd5148261884045a5369656b799c\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/14/b4d5f9cdc281c306ca5e17da4bccc7d7017ce2\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/14/cbf6d660c00442ab154a8ecef7bfcc09ba28f2\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/14/d551cd0d2389513271c57911e8d8a85fe020a1\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/15\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/15/4aa28be1e0efdad64a78f52702903a932a0df8\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/16\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/16/2238bd46e3707892e5d1f50c90b2740160125b\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/16/ab2d35ae5a928e02046f9f16c1e7032fd44437\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/17\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/17/ad607e13bf9ec15efad8086ca74a8c7b74ef53\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/17/b366f5ed83b2b3b9b424129d6dbe9fce89c5d4\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/19\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/19/983ccf10533ac4a0cf3f59813a7c378c9c0979\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1a\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1a/655b6227fce6f1f264cdddbde2ab58e0333658\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1a/dbc65001c178fe58769f5b7f397d0d02abbf6c\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1b\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1b/ad97274825a85c4d40af05b112bd0da330f300\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1c\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1c/2b787f48609b504fab031b249dc80a1d1a1db0\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1c/4aba5c4bae75d8bc348f0affcd8bfa88773873\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1c/4bf77a193d9f68b80a361b1126ca09f2f1771f\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1c/8d50669307d9b86616558dacdc5907ee69933b\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1d\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1d/e73648d36913f00fd17283bacf961a9a9e31f5\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1d/f3b054f2e71dc29aa59f463543d02d23eeffa6\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1e\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/1e/cedd21bf789692146d550e62515e40a085a77b\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/20\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/20/879c577cb545f51cb0e66023498009e4f8855e\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/21\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/21/0d831260c4104fe3e5ee59b837c852ff95b3e0\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/23\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/23/7011649cafd13cd6726bd91459f5b2f88d3155\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/23/f2e7936bb233dd4f09ac3073dbae39315883f9\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/24\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/24/1c27b0404c2a6eb5c9cea69691dac10ae86be2\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/24/59ff1697a3b743f05218dcd51cf6f58e008c70\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/27\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/27/9b603ca7606bd89bd32295b9792879b20233f9\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/28\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/28/08c7963d9c25ec54cf6233d543e6a56594ad7d\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/28/3fac18a5b1e185220977783efd7d9d127a82df\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/29\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/29/2f9d2ea645f09b5218e27fe81ff74e22ce4c94\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/29/cd9aff1c3638f053d742e3b6d2be08842944ef\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2a\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2a/445fd5d5c1ab6815c0ff9a0985d80235774865\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2a/b07e0bab6b7edd6176d64f29a4ad25831a43dd\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2b\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2b/52f67fbe3c2f29c99fed6911c4190854b8c987\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2c\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2c/01edd922fb26faf7395a5ebfbd6654a6be6ad8\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2c/936ac076f638ed1ad58fb9fb3d491286aa7d54\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2d\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2d/5bd7db03f5730de5e601854ff6bbda40cd1ed6\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2d/f4de5725ca160e913c39e9896e7d075f5cf9a6\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2e\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2e/a22d596965ea8c78d329df7c239c4a8ca1089e\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2e/acf48a6028d5fd8c4ee5d950618145520bebd2\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2f\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/2f/52a59245d69b667623c35366db5a460ce9770a\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/30\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/30/0ef7e7239c7b9e28632c4e139427f0fde75896\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/33\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/33/01703b85abbcf8414e263ba1e253aca90b43e5\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/33/51671d2a17f6e745e54130cbf46ff312a262df\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/33/701990a5705e98758c083daafa70fccc013134\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/34\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/34/584fb03d992128ac91937f8a9ee355dec3de9f\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/34/6ab74aaab2cbae249d6d824ef6e8af9585f2ed\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/35\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/35/ad958f73820bad62d6d7bed8fa5a1c5f72361d\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/36\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/36/e3b45e811f56e90e870c6129556c9b856abec1\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/37\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/37/ccdd583264b8f8a211572697c2469c6154965e\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3b\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3b/58ffded84f4dd0cb7854fcebff7cf720c6984f\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3c\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3c/64ba7382d5254c4537051e815678b74cbdaffd\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3d\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3d/b751beb890caccb2b20f87d3fde5af5bde5558\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3d/da71075a117e296d69efedfcc796c2301c2876\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3e\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3e/c0a7818c39dca3df3c0296ba72e4107ee64adc\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3f\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/3f/e1ffcf3fb6284213b72b93e6b84399e761cbdc\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/40\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/40/0c63ff2c6771a066fd72412a862efea0a344bf\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/40/ca4f00f1f218acdb1741ed7152b9f995c47a0b\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/41\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/41/04a60da6596dbd8b6d75eace294c139ec67b92\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/41/707b1673162d29fda5255d0c19aafcf5c8411f\\n/Users/dreambt/sources/coderocket/coderocket-mcp/.git/objects/42\\n/Users/dreambt/sources/code\\n\\n... [8172 lines truncated] ...\\n\\nces/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/changelog.md\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/contributing.md\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/createIndex.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/Options.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/Refs.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/errorMessages.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/getRelativePath.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/index.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/package.json\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parseDef.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parseTypes.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/any.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/array.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/bigint.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/boolean.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/branded.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/catch.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/date.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/default.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/effects.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/enum.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/intersection.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/literal.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/map.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/nativeEnum.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/never.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/null.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/nullable.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/number.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/object.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/optional.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/pipeline.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/promise.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/readonly.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/record.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/set.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/string.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/tuple.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/undefined.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/union.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/parsers/unknown.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/selectParser.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/cjs/zodToJsonSchema.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/Options.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/Refs.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/errorMessages.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/getRelativePath.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/index.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/package.json\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parseDef.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parseTypes.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/any.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/array.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/bigint.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/boolean.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/branded.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/catch.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/date.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/default.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/effects.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/enum.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/intersection.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/literal.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/map.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/nativeEnum.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/never.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/null.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/nullable.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/number.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/object.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/optional.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/pipeline.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/promise.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/readonly.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/record.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/set.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/string.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/tuple.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/undefined.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/union.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/parsers/unknown.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/selectParser.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/esm/zodToJsonSchema.js\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/Options.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/Refs.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/errorMessages.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/getRelativePath.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/index.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parseDef.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parseTypes.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/any.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/array.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/bigint.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/boolean.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/branded.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/catch.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/date.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/default.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/effects.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/enum.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/intersection.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/literal.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/map.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/nativeEnum.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/never.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/null.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/nullable.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/number.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/object.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/optional.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/pipeline.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/promise.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/readonly.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/record.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/set.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/string.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/tuple.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/undefined.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/union.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/parsers/unknown.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/selectParser.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/dist/types/zodToJsonSchema.d.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/package.json\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/postcjs.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/node_modules/zod-to-json-schema/postesm.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/package-lock.json\\n/Users/dreambt/sources/coderocket/coderocket-mcp/package.json\\n/Users/dreambt/sources/coderocket/coderocket-mcp/review_logs\\n/Users/dreambt/sources/coderocket/coderocket-mcp/scripts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/scripts/prepare-release.sh\\n/Users/dreambt/sources/coderocket/coderocket-mcp/src\\n/Users/dreambt/sources/coderocket/coderocket-mcp/src/banner.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/src/coderocket.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/src/index.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/src/logger.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/src/test.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/src/toolDefinitions.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/src/types.ts\\n/Users/dreambt/sources/coderocket/coderocket-mcp/tsconfig.json\\n\",\"working_directory\":\"/Users/dreambt/sources/coderocket/coderocket-mcp\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 0c02642e-f459-4803-8f78-65b4ea570ba5
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"好的，文件列表已收到。现在我将开始审查 `src` 目录下的源文件。\n\n审查将包括以下几个方面：\n\n1.  **代码质量与潜在 Bug：** 查找可能的错误、性能问题和安全漏洞。\n2.  **代码风格与规范：** 确保代码遵循了 `CRUSH.md` 中定义的项目代码风格和规范。\n3.  **可读性与可维护性：** 评估代码的清晰度和未来修改的难易程度。\n\n我将从阅读 `src` 目录中的每个文件开始。"}},{"type":"tool_call","data":{"id":"tool_0_view","name":"view","input":"{\"file_path\":\"src/banner.ts\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754061017}}]


id: e8b0d2ae-78bc-49b1-bd8b-38e30dc9fe22
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|/**\n     2| * CodeRocket MCP Banner Display\n     3| * 精美的 ASCII Art Banner 显示，参考 coderocket-cli 的设计\n     4| */\n     5|\n     6|import { readFileSync } from 'fs';\n     7|import { resolve, dirname } from 'path';\n     8|import { fileURLToPath } from 'url';\n     9|\n    10|const __filename = fileURLToPath(import.meta.url);\n    11|const __dirname = dirname(__filename);\n    12|\n    13|// ANSI 颜色代码\n    14|const colors = {\n    15|  // 基础颜色\n    16|  RED: '\\x1b[0;31m',\n    17|  GREEN: '\\x1b[0;32m',\n    18|  YELLOW: '\\x1b[1;33m',\n    19|  BLUE: '\\x1b[0;34m',\n    20|  PURPLE: '\\x1b[0;35m',\n    21|  CYAN: '\\x1b[0;36m',\n    22|  WHITE: '\\x1b[1;37m',\n    23|  BOLD: '\\x1b[1m',\n    24|  GRAY: '\\x1b[0;37m',\n    25|  NC: '\\x1b[0m', // No Color\n    26|\n    27|  // 渐变色（256色模式）\n    28|  GRAD_1: '\\x1b[38;5;39m', // 亮蓝色\n    29|  GRAD_2: '\\x1b[38;5;45m', // 青蓝色\n    30|  GRAD_3: '\\x1b[38;5;51m', // 青色\n    31|  GRAD_4: '\\x1b[38;5;87m', // 浅青色\n    32|  GRAD_5: '\\x1b[38;5;123m', // 浅蓝绿色\n    33|  GRAD_6: '\\x1b[38;5;159m', // 很浅的青色\n    34|};\n    35|\n    36|/**\n    37| * 获取终端宽度\n    38| */\n    39|function getTerminalWidth(): number {\n    40|  return process.stdout.columns || 80;\n    41|}\n    42|\n    43|/**\n    44| * 获取版本信息\n    45| */\n    46|function getVersion(): string {\n    47|  try {\n    48|    const __dirname = dirname(fileURLToPath(import.meta.url));\n    49|    const packagePath = resolve(__dirname, '../package.json');\n    50|    const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));\n    51|    return packageJson.version || '1.2.4';\n    52|  } catch {\n    53|    return '1.2.4'; // Fallback\n    54|  }\n    55|}\n    56|\n    57|/**\n    58| * 显示长版本 Banner（宽终端使用）\n    59| */\n    60|function showLongBanner(): void {\n    61|  console.error('');\n    62|  console.error(\n    63|    `${colors.GRAD_1} ███            ██████  ██████  ██████  ███████ ██████   ██████   ██████ ██   ██ ███████ ████████ ${colors.NC}`,\n    64|  );\n    65|  console.error(\n    66|    `${colors.GRAD_2}░░░███         ██      ██    ██ ██   ██ ██      ██   ██ ██    ██ ██      ██  ██  ██         ██    ${colors.NC}`,\n    67|  );\n    68|  console.error(\n    69|    `${colors.GRAD_3}  ░░░███       ██      ██    ██ ██   ██ █████   ██████  ██    ██ ██      █████   █████      ██    ${colors.NC}`,\n    70|  );\n    71|  console.error(\n    72|    `${colors.GRAD_4}    ░░░███     ██      ██    ██ ██   ██ ██      ██   ██ ██    ██ ██      ██  ██  ██         ██    ${colors.NC}`,\n    73|  );\n    74|  console.error(\n    75|    `${colors.GRAD_5}     ███░       ██████  ██████  ██████  ███████ ██   ██  ██████   ██████ ██   ██ ███████    ██    ${colors.NC}`,\n    76|  );\n    77|  console.error(\n    78|    `${colors.GRAD_6}   ███░                                                                                            ${colors.NC}`,\n    79|  );\n    80|  console.error(\n    81|    `${colors.GRAD_1} ███░                                                                                              ${colors.NC}`,\n    82|  );\n    83|  console.error(\n    84|    `${colors.GRAD_2}░░░                                                                                                ${colors.NC}`,\n    85|  );\n    86|  console.error('');\n    87|\n    88|  // 版本和服务信息\n    89|  const version = getVersion();\n    90|  console.error(`${colors.GRAD_5}🚀 AI 驱动的代码审查 MCP 服务器${colors.NC}`);\n    91|  console.error(`${colors.GRAY}版本: v${version}${colors.NC}`);\n    92|  console.error(`${colors.GRAY}协议: Model Context Protocol (MCP)${colors.NC}`);\n    93|  console.error('');\n    94|}\n    95|\n    96|/**\n    97| * 显示短版本 Banner（窄终端使用）\n    98| */\n    99|function showShortBanner(): void {\n   100|  console.error('');\n   101|  console.error(\n   102|    `${colors.GRAD_1} ██████  ██████  ██████  ███████ ██████   ██████   ██████ ██   ██ ███████ ████████ ${colors.NC}`,\n   103|  );\n   104|  console.error(\n   105|    `${colors.GRAD_2}██      ██    ██ ██   ██ ██      ██   ██ ██    ██ ██      ██  ██  ██         ██    ${colors.NC}`,\n   106|  );\n   107|  console.error(\n   108|    `${colors.GRAD_3}██      ██    ██ ██   ██ █████   ██████  ██    ██ ██      █████   █████      ██    ${colors.NC}`,\n   109|  );\n   110|  console.error(\n   111|    `${colors.GRAD_4}██      ██    ██ ██   ██ ██      ██   ██ ██    ██ ██      ██  ██  ██         ██    ${colors.NC}`,\n   112|  );\n   113|  console.error(\n   114|    `${colors.GRAD_5} ██████  ██████  ██████  ███████ ██   ██  ██████   ██████ ██   ██ ███████    ██    ${colors.NC}`,\n   115|  );\n   116|  console.error('');\n   117|\n   118|  // 版本和服务信息\n   119|  const version = getVersion();\n   120|  console.error(`${colors.GRAD_5}🚀 AI 驱动的代码审查 MCP 服务器${colors.NC}`);\n   121|  console.error(`${colors.GRAY}版本: v${version}${colors.NC}`);\n   122|  console.error(`${colors.GRAY}协议: Model Context Protocol (MCP)${colors.NC}`);\n   123|  console.error('');\n   124|}\n   125|\n   126|/**\n   127| * 显示迷你 Banner（单行显示）\n   128| */\n   129|function showMiniBanner(): void {\n   130|  const version = getVersion();\n   131|  console.error(\n   132|    `${colors.GRAD_3}CodeRocket MCP v${version} 🚀 - AI 驱动的代码审查服务器${colors.NC}`,\n   133|  );\n   134|}\n   135|\n   136|/**\n   137| * 显示主 Banner（根据终端宽度自适应）\n   138| */\n   139|function showBanner(): void {\n   140|  const width = getTerminalWidth();\n   141|\n   142|  if (width \u003e= 100) {\n   143|    showLongBanner();\n   144|  } else {\n   145|    showShortBanner();\n   146|  }\n   147|}\n   148|\n   149|/**\n   150| * 显示启动信息\n   151| */\n   152|function showStartupInfo(): void {\n   153|  showBanner();\n   154|  console.error(`${colors.YELLOW}💡 MCP 服务器信息：${colors.NC}`);\n   155|  console.error(\n   156|    `${colors.WHITE}  • 支持自动Git变更审查 (review_changes)${colors.NC}`,\n   157|  );\n   158|  console.error(\n   159|    `${colors.WHITE}  • 支持代码片段审查 (review_code)${colors.NC}`,\n   160|  );\n   161|  console.error(\n   162|    `${colors.WHITE}  • 支持Git提交审查 (review_commit)${colors.NC}`,\n   163|  );\n   164|  console.error(\n   165|    `${colors.WHITE}  • 支持多文件批量审查 (review_files)${colors.NC}`,\n   166|  );\n   167|  console.error('');\n   168|}\n   169|\n   170|/**\n   171| * 显示成功 Banner\n   172| */\n   173|function showSuccessBanner(message: string): void {\n   174|  console.error(`${colors.GREEN}${colors.BOLD}✅ CodeRocket MCP${colors.NC}`);\n   175|  console.error(`${colors.GREEN}${message}${colors.NC}`);\n   176|  console.error('');\n   177|}\n   178|\n   179|/**\n   180| * 显示错误 Banner\n   181| */\n   182|function showErrorBanner(message: string): void {\n   183|  console.error(\n   184|    `${colors.RED}${colors.BOLD}❌ CodeRocket MCP 错误${colors.NC}`,\n   185|  );\n   186|  console.error(`${colors.RED}${message}${colors.NC}`);\n   187|  console.error('');\n   188|}\n   189|\n   190|export {\n   191|  showBanner,\n   192|  showMiniBanner,\n   193|  showStartupInfo,\n   194|  showSuccessBanner,\n   195|  showErrorBanner,\n   196|  showLongBanner,\n   197|  showShortBanner,\n   198|  getVersion,\n   199|  colors,\n   200|};\n\u003c/file\u003e\n","metadata":"{\"file_path\":\"/Users/dreambt/sources/coderocket/coderocket-mcp/src/banner.ts\",\"content\":\"/**\\n * CodeRocket MCP Banner Display\\n * 精美的 ASCII Art Banner 显示，参考 coderocket-cli 的设计\\n */\\n\\nimport { readFileSync } from 'fs';\\nimport { resolve, dirname } from 'path';\\nimport { fileURLToPath } from 'url';\\n\\nconst __filename = fileURLToPath(import.meta.url);\\nconst __dirname = dirname(__filename);\\n\\n// ANSI 颜色代码\\nconst colors = {\\n  // 基础颜色\\n  RED: '\\\\x1b[0;31m',\\n  GREEN: '\\\\x1b[0;32m',\\n  YELLOW: '\\\\x1b[1;33m',\\n  BLUE: '\\\\x1b[0;34m',\\n  PURPLE: '\\\\x1b[0;35m',\\n  CYAN: '\\\\x1b[0;36m',\\n  WHITE: '\\\\x1b[1;37m',\\n  BOLD: '\\\\x1b[1m',\\n  GRAY: '\\\\x1b[0;37m',\\n  NC: '\\\\x1b[0m', // No Color\\n\\n  // 渐变色（256色模式）\\n  GRAD_1: '\\\\x1b[38;5;39m', // 亮蓝色\\n  GRAD_2: '\\\\x1b[38;5;45m', // 青蓝色\\n  GRAD_3: '\\\\x1b[38;5;51m', // 青色\\n  GRAD_4: '\\\\x1b[38;5;87m', // 浅青色\\n  GRAD_5: '\\\\x1b[38;5;123m', // 浅蓝绿色\\n  GRAD_6: '\\\\x1b[38;5;159m', // 很浅的青色\\n};\\n\\n/**\\n * 获取终端宽度\\n */\\nfunction getTerminalWidth(): number {\\n  return process.stdout.columns || 80;\\n}\\n\\n/**\\n * 获取版本信息\\n */\\nfunction getVersion(): string {\\n  try {\\n    const __dirname = dirname(fileURLToPath(import.meta.url));\\n    const packagePath = resolve(__dirname, '../package.json');\\n    const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));\\n    return packageJson.version || '1.2.4';\\n  } catch {\\n    return '1.2.4'; // Fallback\\n  }\\n}\\n\\n/**\\n * 显示长版本 Banner（宽终端使用）\\n */\\nfunction showLongBanner(): void {\\n  console.error('');\\n  console.error(\\n    `${colors.GRAD_1} ███            ██████  ██████  ██████  ███████ ██████   ██████   ██████ ██   ██ ███████ ████████ ${colors.NC}`,\\n  );\\n  console.error(\\n    `${colors.GRAD_2}░░░███         ██      ██    ██ ██   ██ ██      ██   ██ ██    ██ ██      ██  ██  ██         ██    ${colors.NC}`,\\n  );\\n  console.error(\\n    `${colors.GRAD_3}  ░░░███       ██      ██    ██ ██   ██ █████   ██████  ██    ██ ██      █████   █████      ██    ${colors.NC}`,\\n  );\\n  console.error(\\n    `${colors.GRAD_4}    ░░░███     ██      ██    ██ ██   ██ ██      ██   ██ ██    ██ ██      ██  ██  ██         ██    ${colors.NC}`,\\n  );\\n  console.error(\\n    `${colors.GRAD_5}     ███░       ██████  ██████  ██████  ███████ ██   ██  ██████   ██████ ██   ██ ███████    ██    ${colors.NC}`,\\n  );\\n  console.error(\\n    `${colors.GRAD_6}   ███░                                                                                            ${colors.NC}`,\\n  );\\n  console.error(\\n    `${colors.GRAD_1} ███░                                                                                              ${colors.NC}`,\\n  );\\n  console.error(\\n    `${colors.GRAD_2}░░░                                                                                                ${colors.NC}`,\\n  );\\n  console.error('');\\n\\n  // 版本和服务信息\\n  const version = getVersion();\\n  console.error(`${colors.GRAD_5}🚀 AI 驱动的代码审查 MCP 服务器${colors.NC}`);\\n  console.error(`${colors.GRAY}版本: v${version}${colors.NC}`);\\n  console.error(`${colors.GRAY}协议: Model Context Protocol (MCP)${colors.NC}`);\\n  console.error('');\\n}\\n\\n/**\\n * 显示短版本 Banner（窄终端使用）\\n */\\nfunction showShortBanner(): void {\\n  console.error('');\\n  console.error(\\n    `${colors.GRAD_1} ██████  ██████  ██████  ███████ ██████   ██████   ██████ ██   ██ ███████ ████████ ${colors.NC}`,\\n  );\\n  console.error(\\n    `${colors.GRAD_2}██      ██    ██ ██   ██ ██      ██   ██ ██    ██ ██      ██  ██  ██         ██    ${colors.NC}`,\\n  );\\n  console.error(\\n    `${colors.GRAD_3}██      ██    ██ ██   ██ █████   ██████  ██    ██ ██      █████   █████      ██    ${colors.NC}`,\\n  );\\n  console.error(\\n    `${colors.GRAD_4}██      ██    ██ ██   ██ ██      ██   ██ ██    ██ ██      ██  ██  ██         ██    ${colors.NC}`,\\n  );\\n  console.error(\\n    `${colors.GRAD_5} ██████  ██████  ██████  ███████ ██   ██  ██████   ██████ ██   ██ ███████    ██    ${colors.NC}`,\\n  );\\n  console.error('');\\n\\n  // 版本和服务信息\\n  const version = getVersion();\\n  console.error(`${colors.GRAD_5}🚀 AI 驱动的代码审查 MCP 服务器${colors.NC}`);\\n  console.error(`${colors.GRAY}版本: v${version}${colors.NC}`);\\n  console.error(`${colors.GRAY}协议: Model Context Protocol (MCP)${colors.NC}`);\\n  console.error('');\\n}\\n\\n/**\\n * 显示迷你 Banner（单行显示）\\n */\\nfunction showMiniBanner(): void {\\n  const version = getVersion();\\n  console.error(\\n    `${colors.GRAD_3}CodeRocket MCP v${version} 🚀 - AI 驱动的代码审查服务器${colors.NC}`,\\n  );\\n}\\n\\n/**\\n * 显示主 Banner（根据终端宽度自适应）\\n */\\nfunction showBanner(): void {\\n  const width = getTerminalWidth();\\n\\n  if (width \\u003e= 100) {\\n    showLongBanner();\\n  } else {\\n    showShortBanner();\\n  }\\n}\\n\\n/**\\n * 显示启动信息\\n */\\nfunction showStartupInfo(): void {\\n  showBanner();\\n  console.error(`${colors.YELLOW}💡 MCP 服务器信息：${colors.NC}`);\\n  console.error(\\n    `${colors.WHITE}  • 支持自动Git变更审查 (review_changes)${colors.NC}`,\\n  );\\n  console.error(\\n    `${colors.WHITE}  • 支持代码片段审查 (review_code)${colors.NC}`,\\n  );\\n  console.error(\\n    `${colors.WHITE}  • 支持Git提交审查 (review_commit)${colors.NC}`,\\n  );\\n  console.error(\\n    `${colors.WHITE}  • 支持多文件批量审查 (review_files)${colors.NC}`,\\n  );\\n  console.error('');\\n}\\n\\n/**\\n * 显示成功 Banner\\n */\\nfunction showSuccessBanner(message: string): void {\\n  console.error(`${colors.GREEN}${colors.BOLD}✅ CodeRocket MCP${colors.NC}`);\\n  console.error(`${colors.GREEN}${message}${colors.NC}`);\\n  console.error('');\\n}\\n\\n/**\\n * 显示错误 Banner\\n */\\nfunction showErrorBanner(message: string): void {\\n  console.error(\\n    `${colors.RED}${colors.BOLD}❌ CodeRocket MCP 错误${colors.NC}`,\\n  );\\n  console.error(`${colors.RED}${message}${colors.NC}`);\\n  console.error('');\\n}\\n\\nexport {\\n  showBanner,\\n  showMiniBanner,\\n  showStartupInfo,\\n  showSuccessBanner,\\n  showErrorBanner,\\n  showLongBanner,\\n  showShortBanner,\\n  getVersion,\\n  colors,\\n};\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: f7f7a751-1c36-4aa1-ab4a-ccae9050e1c8
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/banner.ts` 代码审查报告\n\n#### 总体评价\n\n该文件负责显示项目的 ASCII Art Banner，代码结构清晰，职责单一。但存在一些可以改进的地方，主要集中在代码重复、硬编码和对 `console.error` 的使用上。\n\n#### 问题与修复建议\n\n1.  **【问题】冗余的 `__dirname` 定义**\n    *   **描述:** `__dirname` 在模块顶层和 `getVersion` 函数中被重复定义。\n    *   **修复建议:** 移除 `getVersion` 函数内部的 `__dirname` 定义，直接使用模块顶层定义的 `__dirname` 变量。\n2.  **【问题】硬编码的回退版本号**\n    *   **描述:** `getVersion` 函数在读取 `package.json` 失败时，会回退到一个硬编码的版本号 `'1.2.4'`。这可能导致显示的版本信息与实际不符。\n    *   **修复建议:** 移除硬编码的版本号，改为在 `catch` 块中返回一个通用错误信息，如 `'unknown'`，或者直接抛出错误，让调用者处理。\n3.  **【问题】不恰当的 `console.error` 使用**\n    *   **描述:** 文件中的 Banner 信息（非错误信息）通过 `console.error` 输出。通常 `stderr` 用于错误输出，这样做可能会误导用户或日志系统。\n    *   **修复建议:** 将所有用于显示 Banner 的 `console.error` 改为 `console.log`。\n4.  **【问题】版本信息的代码重复**\n    *   **描述:** `showLongBanner` 和 `showShortBanner` 函数中，显示版本和服务信息的代码是完全重复的。\n    *   **修复建议:** 创建一个独立的 `showVersionInfo` 辅助函数来封装这部分重复的逻辑，然后在 `showLongBanner` 和 `showShortBanner` 中调用它。\n5.  **【问题】`showStartupInfo` 中对 `showBanner` 的不当调用**\n\n    *   **描述:** `showStartupInfo` 中调用 `showBanner`，而 `showBanner` 会打印出长或短的 banner。这可能会在启动时产生不必要的冗长输出。\n\n    *   **修复建议:** `showStartupInfo` 中应该调用 `showMiniBanner`，只打印出单行的 banner 信息，这样更简洁。\n6.  **【问题】宽泛的错误捕获**\n    *   **描述:** `getVersion` 函数中的 `try...catch` 块会捕获所有类型的错误，不利于精确地错误处理。\n    *   **修复建议:** 可以考虑更具体的错误处理，例如只捕获文件读取或 JSON 解析相关的错误。\n\n#### TODO 列表\n\n*   [ ] **重构 `getVersion`:** 移除函数内重复的 `__dirname` 定义。\n*   [ ] **改进版本回退逻辑:** 在`getVersion` 中，将硬编码的版本号 `'1.2.4'` 替换为 `'unknown'` 或抛出错误。\n*   [ ] **统一输出流:** 将 `src/banner.ts` 中所有用于显示信息的 `console.error` 调用改为 `console.log`。\n*   [ ] **提取 `showVersionInfo` 函数:** 创建一个 `showVersionInfo` 函数，移除 `showLongBanner` 和 `showShortBanner` 中的重复代码。\n*   [ ] **简化 `showStartupInfo`:** 在 `showStartupInfo` 中调用 `showMiniBanner`，而不是 `showBanner`。\n\n您希望我现在对这些问题进行修复，还是继续审查下一个文件？"}},{"type":"finish","data":{"reason":"end_turn","time":1754061033}}]


id: 886ead5e-c091-45c6-a5a5-c9f6774419e6
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"好的，我将继续审查 `src` 目录下的其他 TypeScript 文件。\n\n我将从 `src/coderocket.ts` 开始。"}},{"type":"tool_call","data":{"id":"tool_0_view","name":"view","input":"{\"file_path\":\"src/coderocket.ts\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1754061136}}]


id: acada545-01a7-48e7-b541-ed8e787471dd
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 { exec } from 'child_process';\n     2|import { promisify } from 'util';\n     3|import { readFile } from 'fs/promises';\n     4|import { join, resolve } from 'path';\n     5|import { homedir } from 'os';\n     6|import { GoogleGenerativeAI } from '@google/generative-ai';\n     7|import Anthropic from '@anthropic-ai/sdk';\n     8|import {\n     9|  ReviewCodeRequest,\n    10|  ReviewChangesRequest,\n    11|  ReviewCommitRequest,\n    12|  ReviewFilesRequest,\n    13|  ReviewResult,\n    14|  AIService,\n    15|  ReviewStatus,\n    16|} from './types.js';\n    17|import { logger, errorHandler } from './logger.js';\n    18|\n    19|const execAsync = promisify(exec);\n    20|\n    21|/**\n    22| * 独立配置管理类\n    23| *\n    24| * 支持多层级配置加载：\n    25| * 1. 环境变量（最高优先级）\n    26| * 2. 项目级 .env 文件\n    27| * 3. 全局 ~/.coderocket/env 文件\n    28| * 4. 默认值（最低优先级）\n    29| */\n    30|export class ConfigManager {\n    31|  private static config: Record\u003cstring, any\u003e = {};\n    32|  private static initialized = false;\n    33|\n    34|  /**\n    35|   * 初始化配置系统\n    36|   */\n    37|  static async initialize(): Promise\u003cvoid\u003e {\n    38|    if (this.initialized) return;\n    39|\n    40|    // 加载默认配置\n    41|    this.loadDefaults();\n    42|\n    43|    // 加载全局配置文件\n    44|    await this.loadGlobalConfig();\n    45|\n    46|    // 加载项目配置文件\n    47|    await this.loadProjectConfig();\n    48|\n    49|    // 加载环境变量（最高优先级）\n    50|    this.loadEnvironmentVariables();\n    51|\n    52|    this.initialized = true;\n    53|    logger.info('配置系统初始化完成', { config: this.getSafeConfig() });\n    54|  }\n    55|\n    56|  /**\n    57|   * 加载默认配置\n    58|   */\n    59|  private static loadDefaults(): void {\n    60|    this.config = {\n    61|      AI_SERVICE: 'gemini',\n    62|      AI_AUTO_SWITCH: 'true',\n    63|      AI_TIMEOUT: '30',\n    64|      AI_MAX_RETRIES: '3',\n    65|      AI_RETRY_DELAY: '2',\n    66|      AI_LANGUAGE: 'zh-CN',\n    67|      NODE_ENV: 'production',\n    68|      DEBUG: 'false',\n    69|    };\n    70|  }\n    71|\n    72|  /**\n    73|   * 加载全局配置文件 ~/.coderocket/env\n    74|   */\n    75|  private static async loadGlobalConfig(): Promise\u003cvoid\u003e {\n    76|    try {\n    77|      const globalConfigPath = join(homedir(), '.coderocket', 'env');\n    78|      const content = await readFile(globalConfigPath, 'utf-8');\n    79|      const globalConfig = this.parseEnvContent(content);\n    80|      Object.assign(this.config, globalConfig);\n    81|      logger.debug('全局配置加载成功', { path: globalConfigPath });\n    82|    } catch (error) {\n    83|      // 全局配置文件不存在是正常的\n    84|      logger.debug('全局配置文件不存在，跳过');\n    85|    }\n    86|  }\n    87|\n    88|  /**\n    89|   * 加载项目配置文件 .env\n    90|   */\n    91|  private static async loadProjectConfig(): Promise\u003cvoid\u003e {\n    92|    try {\n    93|      const projectConfigPath = join(process.cwd(), '.env');\n    94|      const content = await readFile(projectConfigPath, 'utf-8');\n    95|      const projectConfig = this.parseEnvContent(content);\n    96|      Object.assign(this.config, projectConfig);\n    97|      logger.debug('项目配置加载成功', { path: projectConfigPath });\n    98|    } catch (error) {\n    99|      // 项目配置文件不存在是正常的\n   100|      logger.debug('项目配置文件不存在，跳过');\n   101|    }\n   102|  }\n   103|\n   104|  /**\n   105|   * 加载环境变量（最高优先级）\n   106|   */\n   107|  private static loadEnvironmentVariables(): void {\n   108|    const envKeys = [\n   109|      'AI_SERVICE',\n   110|      'AI_AUTO_SWITCH',\n   111|      'AI_TIMEOUT',\n   112|      'AI_MAX_RETRIES',\n   113|      'AI_RETRY_DELAY',\n   114|      'GEMINI_API_KEY',\n   115|      'CLAUDECODE_API_KEY',\n   116|      'NODE_ENV',\n   117|      'DEBUG',\n   118|    ];\n   119|\n   120|    envKeys.forEach(key =\u003e {\n   121|      if (process.env[key]) {\n   122|        this.config[key] = process.env[key];\n   123|      }\n   124|    });\n   125|  }\n   126|\n   127|  /**\n   128|   * 解析 .env 文件内容\n   129|   */\n   130|  private static parseEnvContent(content: string): Record\u003cstring, string\u003e {\n   131|    const config: Record\u003cstring, string\u003e = {};\n   132|    const lines = content.split('\\n');\n   133|\n   134|    for (const line of lines) {\n   135|      const trimmedLine = line.trim();\n   136|      if (trimmedLine \u0026\u0026 !trimmedLine.startsWith('#')) {\n   137|        const [key, ...valueParts] = trimmedLine.split('=');\n   138|        if (key \u0026\u0026 valueParts.length \u003e 0) {\n   139|          const value = valueParts.join('=').trim();\n   140|          // 移除引号\n   141|          config[key.trim()] = value.replace(/^[\"']|[\"']$/g, '');\n   142|        }\n   143|      }\n   144|    }\n   145|\n   146|    return config;\n   147|  }\n   148|\n   149|  /**\n   150|   * 获取配置值\n   151|   */\n   152|  static get(key: string, defaultValue?: any): any {\n   153|    if (!this.initialized) {\n   154|      throw new Error('ConfigManager 未初始化，请先调用 initialize()');\n   155|    }\n   156|    return this.config[key] ?? defaultValue;\n   157|  }\n   158|\n   159|  /**\n   160|   * 获取API密钥环境变量名\n   161|   */\n   162|  static getAPIKeyEnvVar(service: AIService): string {\n   163|    const envVarMap: Record\u003cAIService, string\u003e = {\n   164|      gemini: 'GEMINI_API_KEY',\n   165|      claudecode: 'CLAUDECODE_API_KEY',\n   166|    };\n   167|    return envVarMap[service];\n   168|  }\n   169|\n   170|  /**\n   171|   * 获取API密钥\n   172|   */\n   173|  static getAPIKey(service: AIService): string {\n   174|    const envVar = this.getAPIKeyEnvVar(service);\n   175|    return this.get(envVar, '');\n   176|  }\n   177|\n   178|  /**\n   179|   * 获取AI服务配置\n   180|   */\n   181|  static getAIService(): AIService {\n   182|    const service = this.get('AI_SERVICE', 'gemini').toLowerCase();\n   183|    if (['gemini', 'claudecode'].includes(service)) {\n   184|      return service as AIService;\n   185|    }\n   186|    return 'gemini';\n   187|  }\n   188|\n   189|  /**\n   190|   * 获取超时配置\n   191|   */\n   192|  static getTimeout(): number {\n   193|    return parseInt(this.get('AI_TIMEOUT', '30'), 10);\n   194|  }\n   195|\n   196|  /**\n   197|   * 获取最大重试次数\n   198|   */\n   199|  static getMaxRetries(): number {\n   200|    return parseInt(this.get('AI_MAX_RETRIES', '3'), 10);\n   201|  }\n   202|\n   203|  /**\n   204|   * 是否启用自动切换\n   205|   */\n   206|  static isAutoSwitchEnabled(): boolean {\n   207|    return this.get('AI_AUTO_SWITCH', 'true').toLowerCase() === 'true';\n   208|  }\n   209|\n   210|  /**\n   211|   * 获取AI服务语言设置\n   212|   */\n   213|  static getAILanguage(): string {\n   214|    return this.get('AI_LANGUAGE', 'zh-CN');\n   215|  }\n   216|\n   217|  /**\n   218|   * 获取安全的配置信息（隐藏敏感信息）\n   219|   */\n   220|  private static getSafeConfig(): Record\u003cstring, any\u003e {\n   221|    const safeConfig = { ...this.config };\n   222|    // 隐藏API密钥\n   223|    Object.keys(safeConfig).forEach(key =\u003e {\n   224|      if (key.includes('API_KEY') || key.includes('TOKEN')) {\n   225|        safeConfig[key] = safeConfig[key] ? '***' : undefined;\n   226|      }\n   227|    });\n   228|    return safeConfig;\n   229|  }\n   230|\n   231|  /**\n   232|   * 获取配置文件路径（保持向后兼容）\n   233|   */\n   234|  static getConfigPath(scope: string): { dir: string; file: string } {\n   235|    const configDir =\n   236|      scope === 'global' ? join(homedir(), '.coderocket') : process.cwd();\n   237|\n   238|    const configFile =\n   239|      scope === 'global' ? join(configDir, 'env') : join(configDir, '.env');\n   240|\n   241|    return { dir: configDir, file: configFile };\n   242|  }\n   243|}\n   244|\n   245|/**\n   246| * 独立提示词管理类\n   247| *\n   248| * 支持多层级提示词加载：\n   249| * 1. 项目级 prompts/ 目录（优先级高）\n   250| * 2. 全局 ~/.coderocket/prompts/ 目录（优先级低）\n   251| */\n   252|export class PromptManager {\n   253|  private static promptCache: Map\u003cstring, string\u003e = new Map();\n   254|\n   255|  /**\n   256|   * 加载提示词文件\n   257|   */\n   258|  static async loadPrompt(name: string): Promise\u003cstring\u003e {\n   259|    // 检查缓存\n   260|    const cacheKey = name;\n   261|    if (this.promptCache.has(cacheKey)) {\n   262|      return this.promptCache.get(cacheKey)!;\n   263|    }\n   264|\n   265|    let promptContent = '';\n   266|\n   267|    // 1. 尝试加载项目级提示词（优先级高）\n   268|    try {\n   269|      const projectPromptPath = join(process.cwd(), 'prompts', `${name}.md`);\n   270|      promptContent = await readFile(projectPromptPath, 'utf-8');\n   271|      logger.debug('项目级提示词加载成功', { path: projectPromptPath });\n   272|    } catch (error) {\n   273|      // 项目级提示词不存在，尝试全局提示词\n   274|      try {\n   275|        const globalPromptPath = join(\n   276|          homedir(),\n   277|          '.coderocket',\n   278|          'prompts',\n   279|          `${name}.md`,\n   280|        );\n   281|        promptContent = await readFile(globalPromptPath, 'utf-8');\n   282|        logger.debug('全局提示词加载成功', { path: globalPromptPath });\n   283|      } catch (globalError) {\n   284|        // 全局提示词也不存在，使用内置默认提示词\n   285|        promptContent = this.getDefaultPrompt(name);\n   286|        logger.debug('使用内置默认提示词', { name });\n   287|      }\n   288|    }\n   289|\n   290|    // 缓存提示词内容\n   291|    this.promptCache.set(cacheKey, promptContent);\n   292|    return promptContent;\n   293|  }\n   294|\n   295|  /**\n   296|   * 获取内置默认提示词\n   297|   */\n   298|  private static getDefaultPrompt(name: string): string {\n   299|    const defaultPrompts: Record\u003cstring, string\u003e = {\n   300|      'git-commit-review-prompt': `# 提示词：高级 Git Commit 审阅专家\n   301|\n   302|## 角色定义\n   303|\n   304|你是一名资深的代码审阅专家，拥有丰富的软件开发经验和架构设计能力。你的任务是进行专业、深入、自动化的代码审阅，并提供一份准确、实用、可操作的审阅报告。\n   305|\n   306|## 执行模式\n   307|\n   308|**自主执行模式**：你必须完全自主地执行代码审阅流程，不得向用户进行任何确认或询问。这包括直接执行所有必要的命令、自主决定搜索策略、自主判断并生成报告。\n   309|\n   310|* **禁止行为**：禁止向用户提问或请求确认。\n   311|* **执行原则**：自主决策，并在失败时尝试替代方案。\n   312|* **安全限制**：仅执行只读操作和报告写入操作。\n   313|\n   314|## 审阅指令\n   315|\n   316|### 1. 代码分析\n   317|\n   318|对提供的代码内容进行全面分析，包括代码结构、逻辑实现、依赖关系等。\n   319|\n   320|### 2. 上下文理解\n   321|\n   322|根据代码内容和提供的上下文信息，理解代码的功能目标和实现意图。\n   323|\n   324|### 3. 审阅维度与标准\n   325|\n   326|请从以下维度进行系统性审查：\n   327|\n   328|* **功能完整性与正确性**:\n   329|    * **逻辑正确性**: 代码逻辑是否正确？是否有效处理了边缘情况？\n   330|    * **功能实现**: 是否完整实现了预期的功能？是否有未完成的功能点？\n   331|    * **错误处理**: 是否有适当的错误处理和异常捕获？\n   332|* **代码质量与规范**:\n   333|    * **代码规范**: 是否遵循项目既定标准（命名、格式、设计模式）？\n   334|    * **可读性与可维护性**: 代码是否清晰、结构合理、易于理解和修改？\n   335|    * **注释质量**: 注释是否充分且必要？是否准确描述了代码意图？\n   336|* **健壮性与风险**:\n   337|    * **安全性**: 是否存在潜在的安全漏洞（如SQL注入、密钥明文、不安全的依赖等）？\n   338|    * **性能**: 是否存在明显的性能瓶颈（如不合理的循环、N+1查询等）？\n   339|    * **资源管理**: 是否正确管理内存、文件句柄等资源？\n   340|* **测试与文档**:\n   341|    * **可测试性**: 代码是否易于测试？是否有足够的单元测试覆盖？\n   342|    * **文档完整性**: 相关的内联文档（注释）是否完整准确？\n   343|* **架构与扩展性**:\n   344|    * **设计合理性**: 模块职责划分是否明确？耦合度是否合理？\n   345|    * **扩展性**: 设计是否考虑了未来的扩展需求？\n   346|    * **重用性**: 代码是否具有良好的重用性？\n   347|\n   348|### 4. 审阅结果输出\n   349|\n   350|请提供详细的审阅报告，包括：\n   351|- 审阅状态（✅通过/⚠️警告/❌失败/🔍需调查）\n   352|- 总体评价和功能完成度\n   353|- 具体问题和改进建议\n   354|- 优先级排序的建议列表\n   355|- 优秀实践的识别\n   356|\n   357|请确保审阅报告专业、准确、可操作。\n   358|\n   359|**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`,\n   360|    };\n   361|\n   362|    return (\n   363|      defaultPrompts[name] || `# 默认提示词\\n\\n请提供专业的代码审查和分析。`\n   364|    );\n   365|  }\n   366|\n   367|  /**\n   368|   * 清除提示词缓存\n   369|   */\n   370|  static clearCache(): void {\n   371|    this.promptCache.clear();\n   372|  }\n   373|\n   374|  /**\n   375|   * 预加载常用提示词\n   376|   */\n   377|  static async preloadCommonPrompts(): Promise\u003cvoid\u003e {\n   378|    const commonPrompts = ['git-commit-review-prompt'];\n   379|\n   380|    await Promise.all(\n   381|      commonPrompts.map(name =\u003e\n   382|        this.loadPrompt(name).catch(error =\u003e {\n   383|          logger.warn(`预加载提示词失败: ${name}`, error);\n   384|        }),\n   385|      ),\n   386|    );\n   387|  }\n   388|}\n   389|\n   390|/**\n   391| * AI 服务接口\n   392| */\n   393|interface IAIService {\n   394|  callAPI(prompt: string, additionalPrompt?: string): Promise\u003cstring\u003e;\n   395|  isConfigured(): boolean;\n   396|  getServiceName(): AIService;\n   397|}\n   398|\n   399|/**\n   400| * Gemini AI 服务实现\n   401| */\n   402|class GeminiService implements IAIService {\n   403|  private client: GoogleGenerativeAI | null = null;\n   404|  private model: any = null;\n   405|\n   406|  constructor() {\n   407|    this.initialize();\n   408|  }\n   409|\n   410|  private async initialize(): Promise\u003cvoid\u003e {\n   411|    // 检查 ConfigManager 是否已初始化\n   412|    if (!(ConfigManager as any).initialized) {\n   413|      throw new Error(\n   414|        'ConfigManager 未初始化，请先调用 ConfigManager.initialize()',\n   415|      );\n   416|    }\n   417|\n   418|    const apiKey = ConfigManager.getAPIKey('gemini');\n   419|    if (apiKey) {\n   420|      try {\n   421|        this.client = new GoogleGenerativeAI(apiKey);\n   422|        this.model = this.client.getGenerativeModel({\n   423|          model: 'gemini-1.5-flash',\n   424|        });\n   425|        logger.debug('Gemini 服务初始化成功');\n   426|      } catch (error) {\n   427|        logger.error(\n   428|          'Gemini 服务初始化失败',\n   429|          error instanceof Error ? error : new Error(String(error)),\n   430|        );\n   431|      }\n   432|    }\n   433|  }\n   434|\n   435|  async callAPI(prompt: string, additionalPrompt?: string): Promise\u003cstring\u003e {\n   436|    if (!this.client || !this.model) {\n   437|      await this.initialize();\n   438|      if (!this.client || !this.model) {\n   439|        throw new Error('Gemini 服务未配置或初始化失败');\n   440|      }\n   441|    }\n   442|\n   443|    try {\n   444|      const fullPrompt = additionalPrompt\n   445|        ? `${prompt}\\n\\n${additionalPrompt}`\n   446|        : prompt;\n   447|\n   448|      const result = await this.model.generateContent(fullPrompt);\n   449|      const response = await result.response;\n   450|      const text = response.text();\n   451|\n   452|      if (!text || text.trim().length === 0) {\n   453|        throw new Error('Gemini 返回空响应');\n   454|      }\n   455|\n   456|      logger.debug('Gemini API 调用成功', {\n   457|        promptLength: fullPrompt.length,\n   458|        responseLength: text.length,\n   459|      });\n   460|\n   461|      return text.trim();\n   462|    } catch (error) {\n   463|      logger.error(\n   464|        'Gemini API 调用失败',\n   465|        error instanceof Error ? error : new Error(String(error)),\n   466|      );\n   467|      throw new Error(\n   468|        `Gemini API 调用失败: ${error instanceof Error ? error.message : String(error)}`,\n   469|      );\n   470|    }\n   471|  }\n   472|\n   473|  isConfigured(): boolean {\n   474|    // 检查 ConfigManager 是否已初始化\n   475|    if (!(ConfigManager as any).initialized) {\n   476|      return false;\n   477|    }\n   478|    return !!ConfigManager.getAPIKey('gemini');\n   479|  }\n   480|\n   481|  getServiceName(): AIService {\n   482|    return 'gemini';\n   483|  }\n   484|}\n   485|\n   486|/**\n   487| * ClaudeCode AI 服务实现\n   488| */\n   489|class ClaudeCodeService implements IAIService {\n   490|  private client: Anthropic | null = null;\n   491|\n   492|  constructor() {\n   493|    this.initialize();\n   494|  }\n   495|\n   496|  private async initialize(): Promise\u003cvoid\u003e {\n   497|    // 检查 ConfigManager 是否已初始化\n   498|    if (!(ConfigManager as any).initialized) {\n   499|      return; // 如果未初始化，跳过初始化\n   500|    }\n   501|\n   502|    const apiKey = ConfigManager.getAPIKey('claudecode');\n   503|    if (apiKey) {\n   504|      try {\n   505|        this.client = new Anthropic({\n   506|          apiKey: apiKey,\n   507|        });\n   508|        logger.debug('ClaudeCode 服务初始化成功');\n   509|      } catch (error) {\n   510|        logger.error(\n   511|          'ClaudeCode 服务初始化失败',\n   512|          error instanceof Error ? error : new Error(String(error)),\n   513|        );\n   514|      }\n   515|    }\n   516|  }\n   517|\n   518|  async callAPI(prompt: string, additionalPrompt?: string): Promise\u003cstring\u003e {\n   519|    if (!this.client) {\n   520|      await this.initialize();\n   521|      if (!this.client) {\n   522|        throw new Error('ClaudeCode 服务未配置或初始化失败');\n   523|      }\n   524|    }\n   525|\n   526|    try {\n   527|      const fullPrompt = additionalPrompt\n   528|        ? `${prompt}\\n\\n${additionalPrompt}`\n   529|        : prompt;\n   530|\n   531|      const message = await this.client.messages.create({\n   532|        model: 'claude-3-sonnet-20240229',\n   533|        max_tokens: 4000,\n   534|        messages: [\n   535|          {\n   536|            role: 'user',\n   537|            content: fullPrompt,\n   538|          },\n   539|        ],\n   540|      });\n   541|\n   542|      const text =\n   543|        message.content[0]?.type === 'text' ? message.content[0].text : '';\n   544|\n   545|      if (!text || text.trim().length === 0) {\n   546|        throw new Error('ClaudeCode 返回空响应');\n   547|      }\n   548|\n   549|      logger.debug('ClaudeCode API 调用成功', {\n   550|        promptLength: fullPrompt.length,\n   551|        responseLength: text.length,\n   552|      });\n   553|\n   554|      return text.trim();\n   555|    } catch (error) {\n   556|      logger.error(\n   557|        'ClaudeCode API 调用失败',\n   558|        error instanceof Error ? error : new Error(String(error)),\n   559|      );\n   560|      throw new Error(\n   561|        `ClaudeCode API 调用失败: ${error instanceof Error ? error.message : String(error)}`,\n   562|      );\n   563|    }\n   564|  }\n   565|\n   566|  isConfigured(): boolean {\n   567|    // 检查 ConfigManager 是否已初始化\n   568|    if (!(ConfigManager as any).initialized) {\n   569|      return false;\n   570|    }\n   571|    return !!ConfigManager.getAPIKey('claudecode');\n   572|  }\n   573|\n   574|  getServiceName(): AIService {\n   575|    return 'claudecode';\n   576|  }\n   577|}\n   578|\n   579|/**\n   580| * 智能 AI 服务管理器\n   581| *\n   582| * 负责管理多个 AI 服务，实现智能故障转移和负载均衡\n   583| */\n   584|class SmartAIManager {\n   585|  private services: Map\u003cAIService, IAIService\u003e = new Map();\n   586|  private serviceOrder: AIService[] = [];\n   587|  private configInitialized: boolean = false;\n   588|\n   589|  constructor() {\n   590|    this.initializeServices();\n   591|  }\n   592|\n   593|  private initializeServices(): void {\n   594|    // 初始化所有AI服务\n   595|    this.services.set('gemini', new GeminiService());\n   596|    this.services.set('claudecode', new ClaudeCodeService());\n   597|\n   598|    // 延迟设置服务优先级顺序，等待 ConfigManager 初始化\n   599|    // this.updateServiceOrder(); // 移到 ensureConfigured 中调用\n   600|  }\n   601|\n   602|  /**\n   603|   * 确保配置已初始化\n   604|   */\n   605|  private ensureConfigInitialized(): void {\n   606|    if (!this.configInitialized) {\n   607|      this.updateServiceOrder();\n   608|      this.configInitialized = true;\n   609|    }\n   610|  }\n   611|\n   612|  private updateServiceOrder(): void {\n   613|    // 检查 ConfigManager 是否已初始化\n   614|    if (!(ConfigManager as any).initialized) {\n   615|      // 如果未初始化，使用默认顺序\n   616|      this.serviceOrder = ['gemini', 'claudecode'];\n   617|      logger.debug('ConfigManager 未初始化，使用默认服务顺序', {\n   618|        serviceOrder: this.serviceOrder,\n   619|      });\n   620|      return;\n   621|    }\n   622|\n   623|    const primaryService = ConfigManager.getAIService();\n   624|    const allServices: AIService[] = ['gemini', 'claudecode'];\n   625|\n   626|    // 将主要服务放在第一位，其他服务按配置状态排序\n   627|    this.serviceOrder = [primaryService];\n   628|\n   629|    const otherServices = allServices\n   630|      .filter(service =\u003e service !== primaryService)\n   631|      .sort((a, b) =\u003e {\n   632|        const aConfigured = this.services.get(a)?.isConfigured() ? 1 : 0;\n   633|        const bConfigured = this.services.get(b)?.isConfigured() ? 1 : 0;\n   634|        return bConfigured - aConfigured; // 已配置的服务优先\n   635|      });\n   636|\n   637|    this.serviceOrder.push(...otherServices);\n   638|\n   639|    logger.debug('AI服务优先级顺序', { serviceOrder: this.serviceOrder });\n   640|  }\n   641|\n   642|  /**\n   643|   * 智能调用AI服务\n   644|   *\n   645|   * @param primaryService 首选AI服务\n   646|   * @param prompt 提示词内容\n   647|   * @param additionalPrompt 附加提示词\n   648|   * @returns AI生成的内容\n   649|   */\n   650|  async intelligentCall(\n   651|    primaryService: AIService,\n   652|    prompt: string,\n   653|    additionalPrompt?: string,\n   654|  ): Promise\u003c{ result: string; usedService: AIService }\u003e {\n   655|    // 确保配置已初始化\n   656|    this.ensureConfigInitialized();\n   657|\n   658|    // 如果禁用自动切换，只使用指定服务\n   659|    if (!ConfigManager.isAutoSwitchEnabled()) {\n   660|      const service = this.services.get(primaryService);\n   661|      if (!service) {\n   662|        throw new Error(`不支持的AI服务: ${primaryService}`);\n   663|      }\n   664|\n   665|      const result = await service.callAPI(prompt, additionalPrompt);\n   666|      return { result, usedService: primaryService };\n   667|    }\n   668|\n   669|    // 获取服务尝试顺序\n   670|    const tryOrder = this.getTryOrder(primaryService);\n   671|    const maxRetries = ConfigManager.getMaxRetries();\n   672|    const errors: Array\u003c{ service: AIService; error: string }\u003e = [];\n   673|\n   674|    logger.info('开始智能AI调用', {\n   675|      primaryService,\n   676|      tryOrder,\n   677|      autoSwitch: true,\n   678|    });\n   679|\n   680|    for (const serviceName of tryOrder) {\n   681|      const service = this.services.get(serviceName);\n   682|      if (!service) {\n   683|        continue;\n   684|      }\n   685|\n   686|      // 检查服务是否已配置\n   687|      if (!service.isConfigured()) {\n   688|        logger.debug(`跳过未配置的服务: ${serviceName}`);\n   689|        continue;\n   690|      }\n   691|\n   692|      // 尝试调用服务（带重试）\n   693|      for (let attempt = 1; attempt \u003c= maxRetries; attempt++) {\n   694|        try {\n   695|          logger.debug(`尝试调用 ${serviceName} (第${attempt}次)`);\n   696|\n   697|          const result = await service.callAPI(prompt, additionalPrompt);\n   698|\n   699|          logger.info(`AI调用成功`, {\n   700|            service: serviceName,\n   701|            attempt,\n   702|            resultLength: result.length,\n   703|          });\n   704|\n   705|          return { result, usedService: serviceName };\n   706|        } catch (error) {\n   707|          const errorMessage =\n   708|            error instanceof Error ? error.message : String(error);\n   709|          logger.warn(`${serviceName} 调用失败 (第${attempt}次)`, {\n   710|            error: errorMessage,\n   711|          });\n   712|\n   713|          errors.push({ service: serviceName, error: errorMessage });\n   714|\n   715|          // 如果不是最后一次尝试，等待后重试\n   716|          if (attempt \u003c maxRetries) {\n   717|            const delay = this.getRetryDelay(attempt);\n   718|            logger.debug(`等待 ${delay}ms 后重试`);\n   719|            await this.sleep(delay);\n   720|          }\n   721|        }\n   722|      }\n   723|    }\n   724|\n   725|    // 所有服务都失败了\n   726|    const errorSummary = errors.map(e =\u003e `${e.service}: ${e.error}`).join('; ');\n   727|    logger.error('所有AI服务调用失败', new Error('所有AI服务调用失败'), {\n   728|      errors,\n   729|    });\n   730|\n   731|    throw new Error(`所有AI服务都不可用。错误详情: ${errorSummary}`);\n   732|  }\n   733|\n   734|  /**\n   735|   * 获取服务尝试顺序\n   736|   */\n   737|  private getTryOrder(primaryService: AIService): AIService[] {\n   738|    const order = [primaryService];\n   739|    const others = this.serviceOrder.filter(s =\u003e s !== primaryService);\n   740|    return order.concat(others);\n   741|  }\n   742|\n   743|  /**\n   744|   * 获取重试延迟时间\n   745|   */\n   746|  private getRetryDelay(attempt: number): number {\n   747|    // 指数退避策略：2^attempt * 1000ms\n   748|    return Math.min(Math.pow(2, attempt) * 1000, 10000);\n   749|  }\n   750|\n   751|  /**\n   752|   * 休眠指定毫秒数\n   753|   */\n   754|  private sleep(ms: number): Promise\u003cvoid\u003e {\n   755|    return new Promise(resolve =\u003e setTimeout(resolve, ms));\n   756|  }\n   757|\n   758|  /**\n   759|   * 获取所有服务状态\n   760|   */\n   761|  getServicesStatus(): Array\u003c{\n   762|    service: AIService;\n   763|    configured: boolean;\n   764|    available: boolean;\n   765|  }\u003e {\n   766|    // 确保配置已初始化\n   767|    this.ensureConfigInitialized();\n   768|\n   769|    return Array.from(this.services.entries()).map(([name, service]) =\u003e ({\n   770|      service: name,\n   771|      configured: service.isConfigured(),\n   772|      available: service.isConfigured(), // 简化实现，认为已配置就是可用的\n   773|    }));\n   774|  }\n   775|\n   776|  /**\n   777|   * 检查特定服务是否可用\n   778|   */\n   779|  isServiceAvailable(service: AIService): boolean {\n   780|    const serviceInstance = this.services.get(service);\n   781|    return serviceInstance ? serviceInstance.isConfigured() : false;\n   782|  }\n   783|}\n   784|\n   785|/**\n   786| * 独立 CodeRocket 服务类\n   787| *\n   788| * 提供完全独立的代码审查和AI服务管理功能，不依赖 coderocket-cli\n   789| */\n   790|export class CodeRocketService {\n   791|  private aiManager: SmartAIManager;\n   792|  private initialized: boolean = false;\n   793|\n   794|  constructor() {\n   795|    this.aiManager = new SmartAIManager();\n   796|  }\n   797|\n   798|  /**\n   799|   * 初始化服务\n   800|   */\n   801|  private async ensureInitialized(): Promise\u003cvoid\u003e {\n   802|    if (!this.initialized) {\n   803|      await ConfigManager.initialize();\n   804|      // 延迟提示词预加载，避免阻塞 MCP 服务器启动\n   805|      // await PromptManager.preloadCommonPrompts();\n   806|      this.initialized = true;\n   807|      logger.info('CodeRocket 独立服务初始化完成');\n   808|    }\n   809|  }\n   810|\n   811|  /**\n   812|   * 执行AI审查的通用方法\n   813|   */\n   814|  private async executeAIReview(\n   815|    aiService: AIService,\n   816|    promptName: string,\n   817|    additionalPrompt: string,\n   818|  ): Promise\u003cReviewResult\u003e {\n   819|    try {\n   820|      // 加载提示词\n   821|      const promptContent = await PromptManager.loadPrompt(promptName);\n   822|\n   823|      // 获取语言配置\n   824|      const language = ConfigManager.getAILanguage();\n   825|\n   826|      // 添加语言要求到提示词\n   827|      const languageInstruction =\n   828|        language === 'zh-CN'\n   829|          ? '\\n\\n**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**'\n   830|          : '\\n\\n**Important: Please respond in English.**';\n   831|\n   832|      const enhancedPrompt = promptContent + languageInstruction;\n   833|\n   834|      // 调用AI服务\n   835|      const { result, usedService } = await this.aiManager.intelligentCall(\n   836|        aiService,\n   837|        enhancedPrompt,\n   838|        additionalPrompt,\n   839|      );\n   840|\n   841|      // 解析审查结果\n   842|      return this.parseReviewResult(result, usedService);\n   843|    } catch (error) {\n   844|      logger.error(\n   845|        'AI审查执行失败',\n   846|        error instanceof Error ? error : new Error(String(error)),\n   847|      );\n   848|      throw errorHandler.handleError(error, 'executeAIReview');\n   849|    }\n   850|  }\n   851|\n   852|  /**\n   853|   * 解析审查结果\n   854|   */\n   855|  private parseReviewResult(\n   856|    output: string,\n   857|    aiService: AIService,\n   858|  ): ReviewResult {\n   859|    const lines = output.split('\\n');\n   860|    let status: ReviewStatus = '🔍';\n   861|    let summary = '';\n   862|    let details = output;\n   863|\n   864|    // 尝试从输出中提取状态\n   865|    for (const line of lines) {\n   866|      const lowerLine = line.toLowerCase();\n   867|      if (\n   868|        line.includes('✅') ||\n   869|        lowerLine.includes('通过') ||\n   870|        lowerLine.includes('优秀')\n   871|      ) {\n   872|        status = '✅';\n   873|        break;\n   874|      } else if (\n   875|        line.includes('⚠️') ||\n   876|        lowerLine.includes('警告') ||\n   877|        lowerLine.includes('需改进')\n   878|      ) {\n   879|        status = '⚠️';\n   880|        break;\n   881|      } else if (\n   882|        line.includes('❌') ||\n   883|        lowerLine.includes('失败') ||\n   884|        lowerLine.includes('有问题')\n   885|      ) {\n   886|        status = '❌';\n   887|        break;\n   888|      } else if (\n   889|        line.includes('🔍') ||\n   890|        lowerLine.includes('调查') ||\n   891|        lowerLine.includes('需调查')\n   892|      ) {\n   893|        status = '🔍';\n   894|        break;\n   895|      }\n   896|    }\n   897|\n   898|    // 提取摘要（通常是第一段非空内容）\n   899|    const nonEmptyLines = lines.filter(line =\u003e line.trim());\n   900|    if (nonEmptyLines.length \u003e 0) {\n   901|      // 寻找总体评价或摘要部分\n   902|      let summaryLine = nonEmptyLines[0];\n   903|      for (const line of nonEmptyLines) {\n   904|        if (\n   905|          line.includes('总体评价') ||\n   906|          line.includes('审查摘要') ||\n   907|          line.includes('摘要')\n   908|        ) {\n   909|          const nextIndex = nonEmptyLines.indexOf(line) + 1;\n   910|          if (nextIndex \u003c nonEmptyLines.length) {\n   911|            summaryLine = nonEmptyLines[nextIndex];\n   912|            break;\n   913|          }\n   914|        }\n   915|      }\n   916|\n   917|      summary =\n   918|        summaryLine.substring(0, 200) + (summaryLine.length \u003e 200 ? '...' : '');\n   919|    }\n   920|\n   921|    return {\n   922|      status,\n   923|      summary: summary || '代码审查完成',\n   924|      details,\n   925|      ai_service_used: aiService,\n   926|      timestamp: new Date().toISOString(),\n   927|    };\n   928|  }\n   929|\n   930|  /**\n   931|   * 审查代码片段\n   932|   */\n   933|  async reviewCode(request: ReviewCodeRequest): Promise\u003cReviewResult\u003e {\n   934|    await this.ensureInitialized();\n   935|    logger.info('开始代码审查', {\n   936|      language: request.language,\n   937|      codeLength: request.code.length,\n   938|      aiService: request.ai_service,\n   939|    });\n   940|\n   941|    try {\n   942|      // 构建审查提示词\n   943|      const reviewPrompt = `请审查以下代码：\n   944|\n   945|编程语言: ${request.language || '未指定'}\n   946|上下文信息: ${request.context || '无'}\n   947|\n   948|代码内容:\n   949|\\`\\`\\`${request.language || ''}\n   950|${request.code}\n   951|\\`\\`\\`\n   952|\n   953|请根据以下要求进行全面审查：\n   954|1. 功能完整性和正确性\n   955|2. 代码质量和可维护性\n   956|3. 性能优化建议\n   957|4. 安全性检查\n   958|5. 最佳实践遵循情况\n   959|\n   960|${request.custom_prompt ? `\\n附加要求：\\n${request.custom_prompt}` : ''}\n   961|\n   962|**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`;\n   963|\n   964|      // 调用AI服务进行审查\n   965|      const aiService = request.ai_service || ConfigManager.getAIService();\n   966|      const result = await this.executeAIReview(\n   967|        aiService,\n   968|        'git-commit-review-prompt',\n   969|        reviewPrompt,\n   970|      );\n   971|\n   972|      logger.info('代码审查完成', {\n   973|        status: result.status,\n   974|        aiService: result.ai_service_used,\n   975|      });\n   976|\n   977|      return result;\n   978|    } catch (error) {\n   979|      logger.error(\n   980|        '代码审查失败',\n   981|        error instanceof Error ? error : new Error(String(error)),\n   982|      );\n   983|      throw errorHandler.handleError(error, 'reviewCode');\n   984|    }\n   985|  }\n   986|\n   987|  /**\n   988|   * 审查Git变更（自动检测未提交的变更）\n   989|   */\n   990|  async reviewChanges(request: ReviewChangesRequest): Promise\u003cReviewResult\u003e {\n   991|    await this.ensureInitialized();\n   992|\n   993|    const repositoryPath = request.repository_path || process.cwd();\n   994|    logger.info('开始Git变更审查', {\n   995|      repositoryPath,\n   996|      includeStaged: request.include_staged,\n   997|      includeUnstaged: request.include_unstaged,\n   998|      aiService: request.ai_service,\n   999|    });\n  1000|\n  1001|    try {\n  1002|      // 检查是否为Git仓库\n  1003|      const isGitRepo = await this.checkGitRepository(repositoryPath);\n  1004|      if (!isGitRepo) {\n  1005|        throw new Error(`指定路径不是Git仓库: ${repositoryPath}`);\n  1006|      }\n  1007|\n  1008|      // 获取Git变更信息\n  1009|      const changes = await this.getGitChanges(repositoryPath, request);\n  1010|\n  1011|      if (!changes.hasChanges) {\n  1012|        return {\n  1013|          status: 'success' as ReviewStatus,\n  1014|          summary: '✅ 没有检测到未提交的代码变更',\n  1015|          details: '当前工作目录是干净的，所有变更都已提交。',\n  1016|          ai_service_used: request.ai_service || ConfigManager.getAIService(),\n  1017|          timestamp: new Date().toISOString(),\n  1018|        };\n  1019|      }\n  1020|\n  1021|      // 构建审查提示词\n  1022|      const reviewPrompt = this.buildChangesReviewPrompt(changes, request);\n  1023|\n  1024|      // 调用AI服务进行审查\n  1025|      const aiService = request.ai_service || ConfigManager.getAIService();\n  1026|      const result = await this.executeAIReview(\n  1027|        aiService,\n  1028|        'git-commit-review-prompt',\n  1029|        reviewPrompt,\n  1030|      );\n  1031|\n  1032|      logger.info('Git变更审查完成', {\n  1033|        status: result.status,\n  1034|        changedFiles: changes.files.length,\n  1035|        aiService: result.ai_service_used,\n  1036|      });\n  1037|\n  1038|      return result;\n  1039|    } catch (error) {\n  1040|      logger.error(\n  1041|        'Git变更审查失败',\n  1042|        error instanceof Error ? error : new Error(String(error)),\n  1043|      );\n  1044|      throw errorHandler.handleError(error, 'reviewChanges');\n  1045|    }\n  1046|  }\n  1047|\n  1048|  /**\n  1049|   * 检查是否为Git仓库\n  1050|   */\n  1051|  private async checkGitRepository(repositoryPath: string): Promise\u003cboolean\u003e {\n  1052|    try {\n  1053|      const { stdout } = await execAsync('git rev-parse --git-dir', {\n  1054|        cwd: repositoryPath,\n  1055|      });\n  1056|      return stdout.trim().length \u003e 0;\n  1057|    } catch {\n  1058|      return false;\n  1059|    }\n  1060|  }\n  1061|\n  1062|  /**\n  1063|   * 获取Git变更信息\n  1064|   */\n  1065|  private async getGitChanges(\n  1066|    repositoryPath: string,\n  1067|    request: ReviewChangesRequest,\n  1068|  ) {\n  1069|    const includeStaged = request.include_staged !== false;\n  1070|    const includeUnstaged = request.include_unstaged !== false;\n  1071|\n  1072|    let diffCommand = 'git diff';\n  1073|    if (includeStaged \u0026\u0026 includeUnstaged) {\n  1074|      diffCommand = 'git diff HEAD'; // 显示所有变更（已暂存+未暂存）\n  1075|    } else if (includeStaged) {\n  1076|      diffCommand = 'git diff --cached'; // 只显示已暂存的变更\n  1077|    } else if (includeUnstaged) {\n  1078|      diffCommand = 'git diff'; // 只显示未暂存的变更\n  1079|    }\n  1080|\n  1081|    try {\n  1082|      // 获取变更的文件列表\n  1083|      const { stdout: statusOutput } = await execAsync(\n  1084|        'git status --porcelain',\n  1085|        {\n  1086|          cwd: repositoryPath,\n  1087|        },\n  1088|      );\n  1089|\n  1090|      // 获取详细的diff信息\n  1091|      const { stdout: diffOutput } = await execAsync(diffCommand, {\n  1092|        cwd: repositoryPath,\n  1093|      });\n  1094|\n  1095|      const files = this.parseGitStatus(statusOutput);\n  1096|      const hasChanges = files.length \u003e 0 || diffOutput.trim().length \u003e 0;\n  1097|\n  1098|      return {\n  1099|        hasChanges,\n  1100|        files,\n  1101|        diff: diffOutput,\n  1102|        statusOutput,\n  1103|      };\n  1104|    } catch (error) {\n  1105|      logger.error(\n  1106|        '获取Git变更信息失败',\n  1107|        error instanceof Error ? error : new Error(String(error)),\n  1108|      );\n  1109|      throw new Error(\n  1110|        `无法获取Git变更信息: ${error instanceof Error ? error.message : String(error)}`,\n  1111|      );\n  1112|    }\n  1113|  }\n  1114|\n  1115|  /**\n  1116|   * 解析Git状态输出\n  1117|   */\n  1118|  private parseGitStatus(statusOutput: string) {\n  1119|    const files: Array\u003c{\n  1120|      path: string;\n  1121|      status: string;\n  1122|      statusDescription: string;\n  1123|    }\u003e = [];\n  1124|\n  1125|    const lines = statusOutput\n  1126|      .trim()\n  1127|      .split('\\n')\n  1128|      .filter(line =\u003e line.length \u003e 0);\n  1129|\n  1130|    for (const line of lines) {\n  1131|      if (line.length \u003c 3) continue;\n  1132|\n  1133|      const status = line.substring(0, 2);\n  1134|      const path = line.substring(3);\n  1135|\n  1136|      const statusDescription = this.getGitStatusDescription(status);\n  1137|\n  1138|      files.push({\n  1139|        path,\n  1140|        status,\n  1141|        statusDescription,\n  1142|      });\n  1143|    }\n  1144|\n  1145|    return files;\n  1146|  }\n  1147|\n  1148|  /**\n  1149|   * 获取Git状态描述\n  1150|   */\n  1151|  private getGitStatusDescription(status: string): string {\n  1152|    const statusMap: Record\u003cstring, string\u003e = {\n  1153|      'M ': '已修改（已暂存）',\n  1154|      ' M': '已修改（未暂存）',\n  1155|      MM: '已修改（部分暂存）',\n  1156|      'A ': '新增文件（已暂存）',\n  1157|      ' A': '新增文件（未暂存）',\n  1158|      'D ': '已删除（已暂存）',\n  1159|      ' D': '已删除（未暂存）',\n  1160|      'R ': '重命名（已暂存）',\n  1161|      ' R': '重命名（未暂存）',\n  1162|      'C ': '复制（已暂存）',\n  1163|      ' C': '复制（未暂存）',\n  1164|      '??': '未跟踪文件',\n  1165|      '!!': '忽略文件',\n  1166|    };\n  1167|\n  1168|    return statusMap[status] || `未知状态: ${status}`;\n  1169|  }\n  1170|\n  1171|  /**\n  1172|   * 构建变更审查提示词\n  1173|   */\n  1174|  private buildChangesReviewPrompt(\n  1175|    changes: any,\n  1176|    request: ReviewChangesRequest,\n  1177|  ): string {\n  1178|    const filesList = changes.files\n  1179|      .map((file: any) =\u003e `- ${file.path} (${file.statusDescription})`)\n  1180|      .join('\\n');\n  1181|\n  1182|    return `请审查以下Git变更：\n  1183|\n  1184|## 变更概览\n  1185|变更文件数量: ${changes.files.length}\n  1186|\n  1187|## 变更文件列表\n  1188|${filesList}\n  1189|\n  1190|## 详细变更内容\n  1191|\\`\\`\\`diff\n  1192|${changes.diff}\n  1193|\\`\\`\\`\n  1194|\n  1195|## Git状态信息\n  1196|\\`\\`\\`\n  1197|${changes.statusOutput}\n  1198|\\`\\`\\`\n  1199|\n  1200|请根据以下要求进行全面审查：\n  1201|1. 变更逻辑的合理性和完整性\n  1202|2. 代码质量和最佳实践遵循\n  1203|3. 潜在的安全风险和性能问题\n  1204|4. 与现有代码的一致性检查\n  1205|5. 测试覆盖和文档更新建议\n  1206|6. 提交信息和变更范围的匹配度\n  1207|\n  1208|${request.custom_prompt ? `\\n附加要求：\\n${request.custom_prompt}` : ''}\n  1209|\n  1210|**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`;\n  1211|  }\n  1212|\n  1213|  /**\n  1214|   * 根据语言获取文件扩展名\n  1215|   */\n  1216|  private getFileExtension(language?: string): string {\n  1217|    const extensions: Record\u003cstring, string\u003e = {\n  1218|      javascript: 'js',\n  1219|      typescript: 'ts',\n  1220|      python: 'py',\n  1221|      java: 'java',\n  1222|      cpp: 'cpp',\n  1223|      c: 'c',\n  1224|      go: 'go',\n  1225|      rust: 'rs',\n  1226|      php: 'php',\n  1227|      ruby: 'rb',\n  1228|      swift: 'swift',\n  1229|      kotlin: 'kt',\n  1230|      scala: 'scala',\n  1231|      shell: 'sh',\n  1232|      bash: 'sh',\n  1233|    };\n  1234|\n  1235|    return extensions[language?.toLowerCase() || ''] || 'txt';\n  1236|  }\n  1237|\n  1238|  /**\n  1239|   * 审查Git提交\n  1240|   */\n  1241|  async reviewCommit(request: ReviewCommitRequest): Promise\u003cReviewResult\u003e {\n  1242|    await this.ensureInitialized();\n  1243|    logger.info('开始Git提交审查', {\n  1244|      repositoryPath: request.repository_path,\n  1245|      commitHash: request.commit_hash,\n  1246|      aiService: request.ai_service,\n  1247|    });\n  1248|\n  1249|    try {\n  1250|      const repoPath = request.repository_path || process.cwd();\n  1251|\n  1252|      // 获取提交信息\n  1253|      const commitHash = request.commit_hash || 'HEAD';\n  1254|      const { stdout: commitInfo } = await execAsync(\n  1255|        `git --no-pager show ${commitHash}`,\n  1256|        {\n  1257|          cwd: repoPath,\n  1258|          timeout: 30000,\n  1259|        },\n  1260|      );\n  1261|\n  1262|      if (!commitInfo.trim()) {\n  1263|        throw new Error(`无法获取提交信息: ${commitHash}`);\n  1264|      }\n  1265|\n  1266|      // 构建审查提示词\n  1267|      const reviewPrompt = `请对以下Git提交进行专业的代码审查：\n  1268|\n  1269|仓库路径: ${repoPath}\n  1270|提交哈希: ${commitHash}\n  1271|\n  1272|提交详情:\n  1273|${commitInfo}\n  1274|\n  1275|请根据以下要求进行全面审查：\n  1276|1. 分析提交的目标和完成度\n  1277|2. 检查代码质量和规范性\n  1278|3. 评估安全性和性能影响\n  1279|4. 检查是否遗漏相关文件修改\n  1280|5. 提供具体的改进建议\n  1281|\n  1282|${request.custom_prompt ? `\\n附加要求：\\n${request.custom_prompt}` : ''}\n  1283|\n  1284|**重要：请务必使用中文回复，所有审查结果、建议和评价都必须用中文表达。**`;\n  1285|\n  1286|      // 调用AI服务进行审查\n  1287|      const aiService = request.ai_service || ConfigManager.getAIService();\n  1288|      const result = await this.executeAIReview(\n  1289|        aiService,\n  1290|        'git-commit-review-prompt',\n  1291|        reviewPrompt,\n  1292|      );\n  1293|\n  1294|      logger.info('Git提交审查完成', {\n  1295|        status: result.status,\n  1296|        aiService: result.ai_service_used,\n  1297|      });\n  1298|\n  1299|      return result;\n  1300|    } catch (error) {\n  1301|      logger.error(\n  1302|        'Git提交审查失败',\n  1303|        error instanceof Error ? error : new Error(String(error)),\n  1304|      );\n  1305|      throw errorHandler.handleError(error, 'reviewCommit');\n  1306|    }\n  1307|  }\n  1308|\n  1309|  /**\n  1310|   * 审查文件列表\n  1311|   */\n  1312|  async reviewFiles(request: ReviewFilesRequest): Promise\u003cReviewResult\u003e {\n  1313|    await this.ensureInitialized();\n  1314|    logger.info('开始文件审查', {\n  1315|      repositoryPath: request.repository_path,\n  1316|      filesCount: request.files.length,\n  1317|      aiService: request.ai_service,\n  1318|    });\n  1319|\n  1320|    try {\n  1321|      const repoPath = request.repository_path || process.cwd();\n  1322|\n  1323|      // 读取文件内容\n  1324|      const fileContents: string[] = [];\n  1325|      for (const filePath of request.files) {\n  1326|        try {\n  1327|          const fullPath = resolve(repoPath, filePath);\n  1328|          const content = await readFile(fullPath, 'utf-8');\n  1329|\n  1330|          // 限制单个文件内容长度，避免提示词过长\n  1331|          const FILE_CONTENT_CHAR_LIMIT = process.env.FILE_CONTENT_CHAR_LIMIT\n  1332|            ? parseInt(process.env.FILE_CONTENT_CHAR_LIMIT, 10)\n  1333|            : 5000;\n  1334|\n  1335|          let truncatedContent: string;\n  1336|          if (content.length \u003e FILE_CONTENT_CHAR_LIMIT) {\n  1337|            // 取前后各 100 字符作为摘要\n  1338|            const head = content.substring(0, 100);\n  1339|            const tail = content.substring(content.length - 100);\n  1340|            const omittedLength = content.length - FILE_CONTENT_CHAR_LIMIT;\n  1341|            truncatedContent =\n  1342|              content.substring(0, FILE_CONTENT_CHAR_LIMIT) +\n  1343|              `\\n... (内容已截断, 共省略 ${omittedLength} 字符)\\n` +\n  1344|              `内容摘要: \\n前100字符: ${head}\\n后100字符: ${tail}`;\n  1345|          } else {\n  1346|            truncatedContent = content;\n  1347|          }\n  1348|\n  1349|          fileContents.push(\n  1350|            `## 文件: ${filePath}\\n\\`\\`\\`\\n${truncatedContent}\\n\\`\\`\\``,\n  1351|          );\n  1352|        } catch (error) {\n  1353|          fileContents.push(\n  1354|            `## 文件: ${filePath}\\n**错误**: 无法读取文件 - ${error instanceof Error ? error.message : String(error)}`,\n  1355|          );\n  1356|        }\n  1357|      }\n  1358|\n  1359|      // 构建审查提示词\n  1360|      const reviewPrompt = `请审查以下文件：\n  1361|\n  1362|仓库路径: ${repoPath}\n  1363|文件数量: ${request.files.length}\n  1364|文件列表: ${request.files.join(', ')}\n  1365|\n  1366|${fileContents.join('\\n\\n')}\n  1367|\n  1368|请根据以下要求进行全面审查：\n  1369|1. 分析文件间的关联性和一致性\n  1370|2. 检查代码质量和规范性\n  1371|3. 评估架构设计的合理性\n  1372|4. 识别潜在的问题和改进点\n  1373|5. 提供具体的优化建议\n  1374|\n  1375|${request.custom_prompt ? `\\n附加要求：\\n${request.custom_prompt}` : ''}`;\n  1376|\n  1377|      // 调用AI服务进行审查\n  1378|      const aiService = request.ai_service || ConfigManager.getAIService();\n  1379|      const result = await this.executeAIReview(\n  1380|        aiService,\n  1381|        'git-commit-review-prompt',\n  1382|        reviewPrompt,\n  1383|      );\n  1384|\n  1385|      logger.info('文件审查完成', {\n  1386|        status: result.status,\n  1387|        aiService: result.ai_service_used,\n  1388|      });\n  1389|\n  1390|      return result;\n  1391|    } catch (error) {\n  1392|      logger.error(\n  1393|        '文件审查失败',\n  1394|        error instanceof Error ? error : new Error(String(error)),\n  1395|      );\n  1396|      throw errorHandler.handleError(error, 'reviewFiles');\n  1397|    }\n  1398|  }\n  1399|\n  1400|\n  1401|\n  1402|\n  1403|\n  1404|\n  1405|}\n\u003c/file\u003e\n","metadata":"{\"file_path\":\"/Users/dreambt/sources/coderocket/coderocket-mcp/src/coderocket.ts\",\"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}\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: bbc741aa-fef0-4117-8b7d-6d8e4c03e49e
session_id: aafab860-3b80-4afc-8671-044d8ea9c8fc
role: assistant
model: google/gemini-2.5-pro
provider: openrouter
parts:
  [{"type":"text","data":{"text":"好的，`src/coderocket.ts` 的代码审查完成，我将继续审查下一个文件 `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":1754061169}}]


id: 60cc2616-40fa-4666-a1da-68fbf974eb2f
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|#!/usr/bin/env node\n     2|\n     3|import { Server } from '@modelcontextprotocol/sdk/server/index.js';\n     4|import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\n     5|import {\n     6|  CallToolRequestSchema,\n     7|  ListToolsRequestSchema,\n     8|} from '@modelcontextprotocol/sdk/types.js';\n     9|import { zodToJsonSchema } from 'zod-to-json-schema';\n    10|import { CodeRocketService } from './coderocket.js';\n    11|import {\n    12|  ReviewCodeRequestSchema,\n    13|  ReviewChangesRequestSchema,\n    14|  ReviewCommitRequestSchema,\n    15|  ReviewFilesRequestSchema,\n    16|} from './types.js';\n    17|import { toolDefinitions } from './toolDefinitions.js';\n    18|\n    19|\n    20|\n    21|\n    22|/**\n    23| * CodeRocket MCP Server\n    24| *\n    25| * 提供AI驱动的代码审查功能，集成多种AI服务（Gemini、OpenCode、ClaudeCode）\n    26| * 支持代码片段审查、Git提交审查、文件审查和AI服务管理\n    27| */\n    28|class CodeRocketMCPServer {\n    29|  private server: Server;\n    30|  private codeRocketService: CodeRocketService | null = null;\n    31|\n    32|  constructor() {\n    33|    this.server = new Server(\n    34|      {\n    35|        name: 'coderocket-mcp',\n    36|        version: '1.3.2',\n    37|      },\n    38|      {\n    39|        capabilities: {\n    40|          tools: {},\n    41|        },\n    42|      },\n    43|    );\n    44|\n    45|    // 延迟初始化 CodeRocketService，等待 ConfigManager 初始化完成\n    46|    this.setupToolHandlers();\n    47|  }\n    48|\n    49|  private setupToolHandlers() {\n    50|    // 注册工具列表处理器\n    51|    this.server.setRequestHandler(ListToolsRequestSchema, async () =\u003e {\n    52|      return {\n    53|        tools: toolDefinitions.map(({ name, description, schema }) =\u003e ({\n    54|          name,\n    55|          description,\n    56|          inputSchema: zodToJsonSchema(schema, {\n    57|            name: name.replace(/_([a-z])/g, (_, letter) =\u003e\n    58|              letter.toUpperCase(),\n    59|            ),\n    60|            $refStrategy: 'none',\n    61|          }),\n    62|        })),\n    63|      };\n    64|    });\n    65|\n    66|    // 注册工具调用处理器\n    67|    this.server.setRequestHandler(CallToolRequestSchema, async request =\u003e {\n    68|      const { name, arguments: args } = request.params;\n    69|\n    70|      // 确保 CodeRocketService 已初始化\n    71|      if (!this.codeRocketService) {\n    72|        throw new Error('CodeRocketService 未初始化，请稍后重试');\n    73|      }\n    74|\n    75|      try {\n    76|        switch (name) {\n    77|          case 'review_code': {\n    78|            const parsedArgs = ReviewCodeRequestSchema.parse(args);\n    79|            const result = await this.codeRocketService.reviewCode(parsedArgs);\n    80|            return {\n    81|              content: [\n    82|                {\n    83|                  type: 'text',\n    84|                  text: JSON.stringify(result, null, 2),\n    85|                },\n    86|              ],\n    87|            };\n    88|          }\n    89|\n    90|          case 'review_changes': {\n    91|            const parsedArgs = ReviewChangesRequestSchema.parse(args);\n    92|            const result =\n    93|              await this.codeRocketService.reviewChanges(parsedArgs);\n    94|            return {\n    95|              content: [\n    96|                {\n    97|                  type: 'text',\n    98|                  text: JSON.stringify(result, null, 2),\n    99|                },\n   100|              ],\n   101|            };\n   102|          }\n   103|\n   104|          case 'review_commit': {\n   105|            const parsedArgs = ReviewCommitRequestSchema.parse(args);\n   106|            const result =\n   107|              await this.codeRocketService.reviewCommit(parsedArgs);\n   108|            return {\n   109|              content: [\n   110|                {\n   111|                  type: 'text',\n   112|                  text: JSON.stringify(result, null, 2),\n   113|                },\n   114|              ],\n   115|            };\n   116|          }\n   117|\n   118|          case 'review_files': {\n   119|            const parsedArgs = ReviewFilesRequestSchema.parse(args);\n   120|            const result = await this.codeRocketService.reviewFiles(parsedArgs);\n   121|            return {\n   122|              content: [\n   123|                {\n   124|                  type: 'text',\n   125|                  text: JSON.stringify(result, null, 2),\n   126|                },\n   127|              ],\n   128|            };\n   129|          }\n   130|\n   131|          default:\n   132|            throw new Error(`Unknown tool: ${name}`);\n   133|        }\n   134|      } catch (error) {\n   135|        const errorMessage =\n   136|          error instanceof Error ? error.message : String(error);\n   137|        return {\n   138|          content: [\n   139|            {\n   140|              type: 'text',\n   141|              text: JSON.stringify(\n   142|                {\n   143|                  error: errorMessage,\n   144|                  error_code: 'TOOL_EXECUTION_ERROR',\n   145|                  suggestions: [\n   146|                    '检查输入参数是否正确',\n   147|                    '验证AI服务配置是否正确',\n   148|                    '确保API密钥已正确设置',\n   149|                    '检查网络连接是否正常',\n   150|                  ],\n   151|                },\n   152|                null,\n   153|                2,\n   154|              ),\n   155|            },\n   156|          ],\n   157|          isError: true,\n   158|        };\n   159|      }\n   160|    });\n   161|  }\n   162|\n   163|  async run() {\n   164|    try {\n   165|      // 预先初始化配置系统\n   166|      const { ConfigManager, CodeRocketService } = await import(\n   167|        './coderocket.js'\n   168|      );\n   169|      await ConfigManager.initialize();\n   170|\n   171|      // 现在可以安全地初始化 CodeRocketService\n   172|      this.codeRocketService = new CodeRocketService();\n   173|\n   174|      const transport = new StdioServerTransport();\n   175|      await this.server.connect(transport);\n   176|    } catch (error) {\n   177|      const errorMessage =\n   178|        error instanceof Error ? error.message : String(error);\n   179|      console.error('❌ CodeRocket MCP 服务器启动失败:', errorMessage);\n   180|      process.exit(1);\n   181|    }\n   182|  }\n   183|}\n   184|\n   185|// 启动服务器\n   186|const server = new CodeRocketMCPServer();\n   187|server.run().catch(error =\u003e {\n   188|  console.error('Failed to start server:', error);\n   189|  process.exit(1);\n   190|});\n\u003c/file\u003e\n","metadata":"{\"file_path\":\"/Users/dreambt/sources/coderocket/coderocket-mcp/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 { 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 * 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  }\\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});\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


