UNPKG

woaru

Version:

Universal Project Setup Autopilot - Analyze and automatically configure development tools for ANY programming language

882 lines (876 loc) 36.9 kB
// Removed unused fs and path imports import axios from 'axios'; import { safeJsonParse } from '../utils/safeJsonParser.js'; import { PromptManager } from './PromptManager.js'; import { UsageTracker } from './UsageTracker.js'; import { getAILanguageInstruction } from '../config/i18n.js'; import { triggerHook, } from '../core/HookSystem.js'; /** * AIReviewAgent - Multi-LLM powered code review and analysis system * * The AIReviewAgent provides sophisticated AI-powered code review capabilities by leveraging * multiple Large Language Model (LLM) providers simultaneously. It performs comprehensive * code analysis, identifies potential issues, suggests improvements, and provides detailed * findings with actionable recommendations. The agent supports multiple AI providers * including OpenAI, Anthropic, and others, enabling robust and diverse code analysis. * * Key features: * - Multi-LLM analysis for comprehensive code reviews * - Hook system integration for extensible workflows * - Usage tracking and performance monitoring * - Provider-specific configuration and failover * - Internationalization support for multilingual analysis * - Secure API communication with error handling * * Supported analysis types: * - Code quality assessment and best practices * - Bug detection and potential security issues * - Performance optimization recommendations * - Architecture and design pattern suggestions * - Maintainability and readability improvements * - Language-specific idioms and conventions * * @example * ```typescript * // Configure AI review with multiple providers * const config: AIReviewConfig = { * providers: [ * { name: 'openai', enabled: true, apiKey: 'your-openai-key' }, * { name: 'anthropic', enabled: true, apiKey: 'your-anthropic-key' } * ], * maxTokens: 4000, * temperature: 0.1 * }; * * const reviewAgent = new AIReviewAgent(config); * * // Perform code review * const codeContext: CodeContext = { * fileName: 'UserService.ts', * language: 'typescript', * framework: 'express', * projectType: 'web-api' * }; * * const result = await reviewAgent.performMultiLLMReview(sourceCode, codeContext); * * // Process results from multiple providers * result.results.forEach(providerResult => { * console.log(`${providerResult.provider} findings:`); * providerResult.findings.forEach(finding => { * console.log(`- ${finding.type}: ${finding.message}`); * console.log(` Severity: ${finding.severity}`); * if (finding.suggestion) { * console.log(` Suggestion: ${finding.suggestion}`); * } * }); * }); * ``` * * @example * ```typescript * // Advanced usage with custom prompt templates * const customPrompts = { * security: { * name: 'Security Review', * description: 'Focus on security vulnerabilities', * system_prompt: 'You are a security expert reviewing code for vulnerabilities...', * user_prompt: 'Analyze this code for security issues: {{code}}' * } * }; * * const reviewAgent = new AIReviewAgent(config, customPrompts); * * // Perform focused security review * const securityResult = await reviewAgent.performMultiLLMReview( * authenticationCode, * { fileName: 'auth.ts', language: 'typescript', focus: 'security' } * ); * * // Handle review completion with metrics * console.log(`Review completed with ${securityResult.results.length} providers`); * console.log(`Total findings: ${securityResult.totalFindings}`); * console.log(`Success rate: ${securityResult.successRate}%`); * ``` * * @since 1.0.0 */ export class AIReviewAgent { config; promptTemplate; promptTemplates; enabledProviders; constructor(config, promptTemplates) { this.config = config; this.enabledProviders = config.providers?.filter(p => p?.enabled) || []; this.promptTemplates = promptTemplates || {}; this.promptTemplate = this.createDefaultPromptTemplate(); if (this.enabledProviders.length === 0) { console.warn('⚠️ No LLM providers enabled in AI Review configuration'); } } /** * Performs comprehensive multi-LLM code review analysis with hook integration * * Executes parallel code analysis using all enabled LLM providers to generate * comprehensive code review findings. Each provider analyzes the code independently, * and results are aggregated with consensus scoring and conflict resolution. * The method integrates with the WOARU Hook System for extensible analysis workflows * and provides detailed performance metrics. * * Analysis workflow: * 1. **Pre-analysis hooks**: Context preparation and provider validation * 2. **Parallel LLM requests**: Simultaneous analysis across all enabled providers * 3. **Response processing**: JSON parsing, validation, and finding extraction * 4. **Result aggregation**: Consensus building and confidence scoring * 5. **Post-analysis hooks**: Result validation and metrics collection * 6. **Error handling**: Graceful provider failures with partial results * * Features: * - Parallel provider execution for optimal performance * - Automatic retry logic with exponential backoff * - Response validation and malformed data handling * - Usage tracking for cost and performance monitoring * - Internationalization support for multilingual contexts * - Comprehensive error logging and debugging * * 🪝 **Hook Integration**: Seamlessly integrates with WOARU's rule-based AI system * * @param code - Source code content to analyze (supports all major programming languages) * @param context - Contextual information about the code being analyzed * @param context.fileName - Name of the file being analyzed for context-aware suggestions * @param context.language - Programming language for language-specific analysis * @param context.framework - Framework context (e.g., 'react', 'express', 'django') * @param context.projectType - Project type for targeted recommendations * @returns Promise resolving to comprehensive multi-provider review results * * @throws {Error} When no providers are enabled or all providers fail * * @example * ```typescript * const reviewAgent = new AIReviewAgent(config); * * // Analyze a React component * const reactCode = ` * import React, { useState } from 'react'; * * function UserProfile({ userId }) { * const [user, setUser] = useState(null); * // ... component implementation * }`; * * const context: CodeContext = { * fileName: 'UserProfile.tsx', * language: 'typescript', * framework: 'react', * projectType: 'spa' * }; * * const result = await reviewAgent.performMultiLLMReview(reactCode, context); * * // Access aggregated results * console.log(`Total findings: ${result.totalFindings}`); * console.log(`Success rate: ${result.successRate}%`); * console.log(`Analysis duration: ${result.totalDuration}ms`); * * // Process provider-specific findings * result.results.forEach(providerResult => { * if (providerResult.success) { * console.log(`\n${providerResult.provider} Analysis:`); * providerResult.findings.forEach(finding => { * console.log(`- [${finding.severity}] ${finding.type}: ${finding.message}`); * if (finding.lineNumber) console.log(` Line: ${finding.lineNumber}`); * if (finding.suggestion) console.log(` 💡 ${finding.suggestion}`); * }); * } else { * console.warn(`${providerResult.provider} failed: ${providerResult.error}`); * } * }); * ``` * * @example * ```typescript * // Handle analysis with error recovery * try { * const result = await reviewAgent.performMultiLLMReview(complexCode, context); * * // Check if we have usable results despite some failures * if (result.successRate >= 50) { * const highSeverityIssues = result.results * .flatMap(r => r.findings) * .filter(f => f.severity === 'high' || f.severity === 'critical'); * * if (highSeverityIssues.length > 0) { * console.log('🚨 Critical issues found:'); * highSeverityIssues.forEach(issue => { * console.log(`- ${issue.message}`); * }); * } * } else { * console.warn('Analysis quality may be compromised due to provider failures'); * } * } catch (error) { * console.error('Multi-LLM review failed completely:', error.message); * } * ``` * * @since 1.0.0 */ async performMultiLLMReview(code, context) { const startTime = new Date(); const results = {}; const responseTimesMs = {}; const tokensUsed = {}; const estimatedCosts = {}; const errors = {}; // 🪝 HOOK: beforeAnalysis - KI-freundliche Regelwelt const beforeData = { files: [context.filePath], projectPath: '', // CodeContext doesn't have projectPath, using empty string config: { language: context.language, enabledProviders: this.enabledProviders.map(p => p.id), tokenLimit: this.config.tokenLimit, parallelRequests: this.config.parallelRequests, }, timestamp: new Date(), }; try { await triggerHook('beforeAnalysis', beforeData); } catch (hookError) { console.debug(`Hook error (beforeAnalysis AI review): ${hookError}`); } try { // Validate code length if (code.length > this.config.tokenLimit * 4) { // Rough estimate: 1 token ≈ 4 chars throw new Error(`Code too long for analysis (${code.length} chars). Max: ${this.config.tokenLimit * 4}`); } console.log(`🧠 Starting AI Code Review with ${this.enabledProviders.length} LLM providers...`); // Run LLM requests (parallel or sequential based on config) if (this.config.parallelRequests) { const promises = this.enabledProviders.map(provider => this.callLLMProvider(provider, code, context)); const responses = await Promise.allSettled(promises); responses.forEach((result, index) => { const provider = this.enabledProviders[index]; if (result.status === 'fulfilled') { const response = result.value; results[provider.id] = response.findings; responseTimesMs[provider.id] = response.responseTime; tokensUsed[provider.id] = response.tokensUsed || 0; estimatedCosts[provider.id] = response.estimatedCost || 0; errors[provider.id] = response.error || null; } else { results[provider.id] = []; errors[provider.id] = result.reason?.toString() || 'Unknown error'; responseTimesMs[provider.id] = 0; tokensUsed[provider.id] = 0; estimatedCosts[provider.id] = 0; } }); } else { // Sequential execution for (const provider of this.enabledProviders) { try { const response = await this.callLLMProvider(provider, code, context); results[provider.id] = response.findings; responseTimesMs[provider.id] = response.responseTime; tokensUsed[provider.id] = response.tokensUsed || 0; estimatedCosts[provider.id] = response.estimatedCost || 0; errors[provider.id] = response.error || null; } catch (error) { results[provider.id] = []; errors[provider.id] = error instanceof Error ? error.message : 'Unknown error'; responseTimesMs[provider.id] = 0; tokensUsed[provider.id] = 0; estimatedCosts[provider.id] = 0; } } } const endTime = new Date(); // Aggregate results const aggregation = this.aggregateResults(results); const reviewResult = { codeContext: context, results, aggregation, meta: { analysisStartTime: startTime, analysisEndTime: endTime, totalDuration: endTime.getTime() - startTime.getTime(), llmResponseTimes: responseTimesMs, tokensUsed, estimatedCost: estimatedCosts, totalEstimatedCost: Object.values(estimatedCosts).reduce((sum, cost) => sum + cost, 0), llmErrors: errors, }, }; // 🪝 HOOK: afterAnalysis - KI-freundliche Regelwelt const afterData = { files: [context.filePath], results: [ { file: context.filePath, tool: 'ai-review', success: true, issues: Object.values(results).flat(), }, ], duration: endTime.getTime() - startTime.getTime(), success: true, timestamp: new Date(), }; try { await triggerHook('afterAnalysis', afterData); } catch (hookError) { console.debug(`Hook error (afterAnalysis AI review): ${hookError}`); } return reviewResult; } catch (error) { // 🪝 HOOK: onError - KI-freundliche Regelwelt const errorData = { error: error instanceof Error ? error : new Error(String(error)), context: 'ai-review-analysis', filePath: context.filePath, timestamp: new Date(), }; try { await triggerHook('onError', errorData); } catch (hookError) { console.debug(`Hook error (onError AI review): ${hookError}`); } throw error; } } /** * Call a specific LLM provider */ async callLLMProvider(provider, code, context) { const startTime = Date.now(); try { console.log(` 🤖 Calling ${provider.id} (${provider.model})...`); // Build provider-specific prompt const prompt = this.buildPromptForProvider(provider, code, context); // Get API key from environment const apiKey = process.env[provider.apiKeyEnvVar]; if (!apiKey && provider.apiKeyEnvVar) { throw new Error(`API key not found in environment variable: ${provider.apiKeyEnvVar}`); } const validApiKey = apiKey || ''; // Call appropriate method based on provider type let response; switch (provider.providerType) { case 'anthropic': response = await this._callAnthropic(provider, prompt, code, context, validApiKey); break; case 'openai': response = await this._callOpenAI(provider, prompt, code, context, validApiKey); break; case 'azure-openai': response = await this._callAzureOpenAI(provider, prompt, code, context, validApiKey); break; case 'google': response = await this._callGoogle(provider, prompt, code, context, validApiKey); break; case 'custom-ollama': response = await this._callOllama(provider, prompt, code, context); break; default: throw new Error(`Unsupported provider type: ${provider.providerType}`); } response.responseTime = Date.now() - startTime; console.log(` ✅ ${provider.id} completed (${response.responseTime}ms, ${response.findings.length} findings)`); // Track successful request const usageTracker = UsageTracker.getInstance(); await usageTracker.trackRequest(provider.id, response.tokensUsed || 0, response.estimatedCost || 0); return response; } catch (error) { console.error(` ❌ ${provider.id} failed:`, error instanceof Error ? error.message : error); // Track failed request const usageTracker = UsageTracker.getInstance(); await usageTracker.trackError(provider.id); return { success: false, findings: [], responseTime: Date.now() - startTime, error: error instanceof Error ? error.message : 'Unknown error', }; } } /** * Call Anthropic Claude API */ async _callAnthropic(provider, prompt, code, context, apiKey) { const headers = { 'Content-Type': 'application/json', 'x-api-key': apiKey, ...provider.headers, }; const body = this.interpolateTemplate(provider.bodyTemplate, { model: provider.model, prompt, code, language: context.language, systemPrompt: this.promptTemplate.systemPrompt, }); const response = await axios.post(provider.baseUrl, safeJsonParse(body) || {}, { headers, timeout: provider.timeout || 30000, }); const content = response.data.content[0].text; const findings = this.parseAIResponse(content, provider.id, context); return { success: true, findings, rawResponse: content, tokensUsed: response.data.usage?.input_tokens + response.data.usage?.output_tokens || 0, responseTime: 0, // Will be set by caller estimatedCost: this.estimateCost(provider.id, response.data.usage?.input_tokens || 0, response.data.usage?.output_tokens || 0), }; } /** * Call OpenAI GPT API */ async _callOpenAI(provider, prompt, code, context, apiKey) { const headers = { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}`, ...provider.headers, }; const body = this.interpolateTemplate(provider.bodyTemplate, { model: provider.model, prompt, code, language: context.language, systemPrompt: this.promptTemplate.systemPrompt, }); const response = await axios.post(provider.baseUrl, safeJsonParse(body) || {}, { headers, timeout: provider.timeout || 30000, }); const content = response.data.choices[0].message.content; const findings = this.parseAIResponse(content, provider.id, context); return { success: true, findings, rawResponse: content, tokensUsed: response.data.usage?.total_tokens || 0, responseTime: 0, estimatedCost: this.estimateCost(provider.id, response.data.usage?.prompt_tokens || 0, response.data.usage?.completion_tokens || 0), }; } /** * Call Azure OpenAI API */ async _callAzureOpenAI(provider, prompt, code, context, apiKey) { const headers = { 'Content-Type': 'application/json', 'api-key': apiKey, ...provider.headers, }; const body = this.interpolateTemplate(provider.bodyTemplate, { model: provider.model, prompt, code, language: context.language, systemPrompt: this.promptTemplate.systemPrompt, }); const response = await axios.post(provider.baseUrl, safeJsonParse(body) || {}, { headers, timeout: provider.timeout || 30000, }); const content = response.data.choices[0].message.content; const findings = this.parseAIResponse(content, provider.id, context); return { success: true, findings, rawResponse: content, tokensUsed: response.data.usage?.total_tokens || 0, responseTime: 0, estimatedCost: this.estimateCost(provider.id, response.data.usage?.prompt_tokens || 0, response.data.usage?.completion_tokens || 0), }; } /** * Call Google Gemini API */ async _callGoogle(provider, prompt, code, context, apiKey) { const url = provider.baseUrl.replace('{model}', provider.model) + `?key=${apiKey}`; const body = this.interpolateTemplate(provider.bodyTemplate, { model: provider.model, prompt, code, language: context.language, systemPrompt: this.promptTemplate.systemPrompt, }); const requestBody = safeJsonParse(body); if (!requestBody) { throw new Error('Failed to parse request body JSON'); } const response = await axios.post(url, requestBody, { headers: { 'Content-Type': 'application/json', ...provider.headers, }, timeout: provider.timeout || 30000, }); const content = response.data.candidates[0].content.parts[0].text; const findings = this.parseAIResponse(content, provider.id, context); return { success: true, findings, rawResponse: content, tokensUsed: response.data.usageMetadata?.totalTokenCount || 0, responseTime: 0, estimatedCost: this.estimateCost(provider.id, response.data.usageMetadata?.promptTokenCount || 0, response.data.usageMetadata?.candidatesTokenCount || 0), }; } /** * Call local Ollama API */ async _callOllama(provider, prompt, code, context) { const body = this.interpolateTemplate(provider.bodyTemplate, { model: provider.model, prompt, code, language: context.language, systemPrompt: this.promptTemplate.systemPrompt, }); const response = await axios.post(provider.baseUrl, safeJsonParse(body) || {}, { headers: { 'Content-Type': 'application/json', ...provider.headers, }, timeout: provider.timeout || 60000, }); const content = response.data.response; const findings = this.parseAIResponse(content, provider.id, context); return { success: true, findings, rawResponse: content, tokensUsed: 0, // Ollama doesn't always provide token counts responseTime: 0, estimatedCost: 0, // Local models are free }; } /** * Interpolate template placeholders with safe JSON escaping */ interpolateTemplate(template, variables) { let result = template; Object.entries(variables).forEach(([key, value]) => { // Use JSON.stringify to safely escape the value, then remove outer quotes const escapedValue = JSON.stringify(value).slice(1, -1); result = result.replace(new RegExp(`{${key}}`, 'g'), escapedValue); }); return result; } /** * Build the complete prompt for LLM */ /** * Build provider-specific prompt using dynamic templates */ buildPromptForProvider(provider, code, context) { // Check if we have a custom prompt template for this provider const customTemplate = this.promptTemplates[provider.id]; if (customTemplate) { // Use custom template with variable interpolation // PromptManager imported at top level const promptManager = PromptManager.getInstance(); const variables = { file_path: context.filePath, language: context.language, project_name: context.projectContext?.name || 'Unknown Project', framework: context.framework || 'None', code_content: code, total_lines: context.totalLines.toString(), expected_load: 'Standard', architecture_context: context.projectContext?.type || 'Unknown', testing_framework: 'Unknown', coverage_percentage: '0', }; // Interpolate user prompt with variables const userPrompt = promptManager.interpolatePrompt(customTemplate .user_prompt, variables); return userPrompt; } else { // Fall back to default prompt return this.buildDefaultPrompt(code, context); } } /** * Build default prompt (legacy compatibility) */ buildDefaultPrompt(code, context) { let prompt = this.promptTemplate.userPromptTemplate; // Add language instruction at the beginning let languageInstruction = ''; try { languageInstruction = getAILanguageInstruction(); prompt = `${languageInstruction}\n\n${prompt}`; } catch { // If i18n is not initialized, default to English prompt = `Respond exclusively in English.\n\n${prompt}`; } // Add context information if (this.promptTemplate.contextInjection.includeFileMetadata) { prompt += `\n\nFile: ${context.filePath}`; prompt += `\nLanguage: ${context.language}`; if (context.framework) { prompt += `\nFramework: ${context.framework}`; } prompt += `\nTotal Lines: ${context.totalLines}`; } if (this.promptTemplate.contextInjection.includeProjectContext && context.projectContext) { prompt += `\n\nProject Context:`; prompt += `\nProject: ${context.projectContext.name} (${context.projectContext.type})`; if (context.projectContext.dependencies.length > 0) { prompt += `\nKey Dependencies: ${context.projectContext.dependencies.slice(0, 5).join(', ')}`; } } if (this.promptTemplate.contextInjection.includeGitDiff && context.gitDiff) { prompt += `\n\nGit Diff:\n${context.gitDiff}`; } return prompt; } /** * Parse AI response into structured findings */ parseAIResponse(response, llmId, context) { try { // Try to extract JSON from the response const jsonMatch = response.match(/\[[\s\S]*\]/); if (!jsonMatch) { console.warn(`No JSON array found in ${llmId} response`); return []; } const findings = safeJsonParse(jsonMatch[0]); if (!findings || !Array.isArray(findings)) { console.warn('Failed to parse AI findings JSON or result is not an array'); return []; } return findings.map((finding) => ({ llmId, severity: finding.severity || 'medium', category: finding.category || 'code-smell', message: finding.message || 'No message provided', rationale: finding.rationale || finding.reason || 'No rationale provided', suggestion: finding.suggestion || 'No suggestion provided', filePath: context.filePath, lineNumber: finding.lineNumber || finding.line, lineRange: finding.lineRange, codeSnippet: finding.codeSnippet, confidence: finding.confidence || 0.8, tags: finding.tags || [], estimatedFixTime: finding.estimatedFixTime, businessImpact: finding.businessImpact || 'medium', })); } catch (error) { console.error(`Failed to parse ${llmId} response:`, error); return []; } } /** * Aggregate results from multiple LLMs */ aggregateResults(results) { const allFindings = Object.values(results).flat(); const findingsBySeverity = allFindings.reduce((acc, finding) => { acc[finding.severity] = (acc[finding.severity] || 0) + 1; return acc; }, {}); const findingsByCategory = allFindings.reduce((acc, finding) => { acc[finding.category] = (acc[finding.category] || 0) + 1; return acc; }, {}); // Find consensus findings (issues found by multiple LLMs) const consensusFindings = this.findConsensusIssues(results); // Find unique findings per LLM const uniqueFindings = this.findUniqueFindings(results, consensusFindings); // Calculate agreement score const llmAgreementScore = consensusFindings.length / Math.max(allFindings.length, 1); return { totalFindings: allFindings.length, findingsBySeverity, findingsByCategory, consensusFindings, uniqueFindings, llmAgreementScore, }; } /** * Find issues that multiple LLMs agree on */ findConsensusIssues(results) { const consensus = []; const llmIds = Object.keys(results); // For now, simple consensus based on similar messages // TODO: Implement more sophisticated similarity detection // Current implementation uses simple string matching which may miss: // - Similar findings with different wording // - Findings that reference the same code issue from different perspectives // - Semantically equivalent findings across different languages // Consider implementing: // - Embedding-based similarity using sentence transformers // - AST-based comparison for code-related findings // - Fuzzy matching with configurable thresholds // Tracked in issue: #woaru-ai-similarity-enhancement for (const llmId of llmIds) { const findings = results[llmId]; for (const finding of findings) { const similarFindings = llmIds .filter(id => id !== llmId) .map(id => results[id]) .flat() .filter(f => this.areFindingsSimilar(finding, f)); if (similarFindings.length >= this.config.minConsensusCount - 1) { consensus.push(finding); } } } return consensus; } /** * Find unique findings per LLM */ findUniqueFindings(results, consensusFindings) { const unique = {}; Object.entries(results).forEach(([llmId, findings]) => { unique[llmId] = findings.filter(finding => !consensusFindings.some(consensus => this.areFindingsSimilar(finding, consensus))); }); return unique; } /** * Check if two findings are similar (simple implementation) */ areFindingsSimilar(finding1, finding2) { // Simple similarity check based on message and line number const messageSimilarity = this.calculateStringSimilarity(finding1.message, finding2.message); const sameLine = finding1.lineNumber === finding2.lineNumber; const sameCategory = finding1.category === finding2.category; return messageSimilarity > 0.7 || (sameLine && sameCategory); } /** * Calculate string similarity (simple Levenshtein-based) */ calculateStringSimilarity(str1, str2) { const longer = str1.length > str2.length ? str1 : str2; const shorter = str1.length > str2.length ? str2 : str1; if (longer.length === 0) return 1.0; const editDistance = this.levenshteinDistance(longer, shorter); return (longer.length - editDistance) / longer.length; } /** * Calculate Levenshtein distance */ levenshteinDistance(str1, str2) { const matrix = []; for (let i = 0; i <= str2.length; i++) { matrix[i] = [i]; } for (let j = 0; j <= str1.length; j++) { matrix[0][j] = j; } for (let i = 1; i <= str2.length; i++) { for (let j = 1; j <= str1.length; j++) { if (str2.charAt(i - 1) === str1.charAt(j - 1)) { matrix[i][j] = matrix[i - 1][j - 1]; } else { matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1); } } } return matrix[str2.length][str1.length]; } /** * Estimate cost for API calls */ estimateCost(llmId, inputTokens, outputTokens) { // Rough cost estimates (USD per 1k tokens) - update these with current pricing const pricing = { 'anthropic-claude': { input: 0.003, output: 0.015 }, 'openai-gpt4': { input: 0.03, output: 0.06 }, 'google-gemini': { input: 0.00035, output: 0.00105 }, 'azure-gpt4': { input: 0.03, output: 0.06 }, 'local-ollama': { input: 0, output: 0 }, }; const rates = pricing[llmId] || { input: 0.001, output: 0.002 }; return (inputTokens * rates.input + outputTokens * rates.output) / 1000; } /** * Create default prompt template */ createDefaultPromptTemplate() { // Get language instruction for AI responses let languageInstruction = ''; try { languageInstruction = getAILanguageInstruction(); } catch { // If i18n is not initialized, default to English languageInstruction = 'Respond exclusively in English.'; } return { systemPrompt: `You are an experienced, conservative Senior Staff Engineer with a focus on maintainability, security, and scalable architecture. You are extremely critical but fair and always provide well-reasoned explanations. Your expertise areas include: - Code security vulnerabilities and best practices - Performance optimization and scalability concerns - Software architecture and design patterns - Code maintainability and readability - Industry best practices and coding standards You are thorough, detail-oriented, and always explain the business impact of technical issues. IMPORTANT LANGUAGE INSTRUCTION: ${languageInstruction}`, userPromptTemplate: `Analyze the following code for code smells, design principle violations, security risks, and potential performance bottlenecks. For each finding: 1. Provide a clear rationale explaining WHY it's a problem 2. Explain the potential business impact 3. Give a concrete improvement suggestion 4. Estimate the fix complexity/time if possible IMPORTANT: - Be conservative and only flag genuine issues - Use only your internal knowledge, no web search - Focus on issues that have real business impact - Respond ONLY in valid JSON format as an array of objects Required JSON format: [ { "severity": "critical" | "high" | "medium" | "low", "category": "security" | "performance" | "maintainability" | "architecture" | "code-smell" | "best-practice", "message": "Brief description of the issue", "rationale": "Detailed explanation of why this is a problem", "suggestion": "Specific improvement recommendation", "lineNumber": <line_number_if_applicable>, "confidence": <0.0_to_1.0>, "businessImpact": "low" | "medium" | "high", "estimatedFixTime": "5 minutes" | "30 minutes" | "2 hours" | "1 day" | "1 week" } ]`, contextInjection: { includeFileMetadata: true, includeProjectContext: true, includeGitDiff: false, maxCodeLength: 8000, }, }; } } //# sourceMappingURL=AIReviewAgent.js.map