UNPKG

remcode

Version:

Turn your AI assistant into a codebase expert. Intelligent code analysis, semantic search, and software engineering guidance through MCP integration.

623 lines (622 loc) 22.8 kB
"use strict"; requirements: this.generateRequirements(actions, apiCalls); ; async; executeAutomatedSetup(owner, string, repo, string, options, EnhancedSetupOptions, setupPlan, SetupPlan); Promise < any > { const: results = { success: false, message: 'Automated Setup Complete', repository: { owner, repo, branch: options.branch || 'main' }, completed: [], failed: [], warnings: [], summary: {}, nextSteps: [] }, try: { // Step 1: Create/Update .remcode configuration logger, : .info('📝 Step 1: Configuring .remcode file...'), const: configResult = await this.createOrUpdateRemcodeConfig(owner, repo, options), if(configResult) { }, : .success } }; { results.completed.push('✅ .remcode configuration created/updated'); results.summary.config = configResult; } { results.failed.push('❌ .remcode configuration failed'); results.warnings.push(configResult.error || 'Unknown config error'); } // Step 2: Initialize embedding model let modelResult = null; if (!options.skipModelInit) { logger.info('🤖 Step 2: Initializing embedding model...'); modelResult = await this.initializeEmbeddingModel(options.embeddingModel); if (modelResult.success) { results.completed.push(`✅ Embedding model initialized: ${modelResult.modelName}`); results.summary.embeddings = modelResult; // Update config with model information try { await this.updateConfigWithModelInfo(modelResult); results.completed.push('✅ Model configuration saved to .remcode'); } catch (configError) { results.warnings.push('⚠️ Could not update .remcode with model info'); } } else { results.failed.push('❌ Embedding model initialization failed'); results.warnings.push(modelResult.error || 'Model initialization failed'); } } else { logger.info('⏭️ Step 2: Skipping model initialization (skipModelInit=true)'); results.completed.push('⏭️ Model initialization skipped'); } // Step 3: Generate GitHub Actions workflow let workflowResult = null; if (!options.skipWorkflows) { logger.info('⚙️ Step 3: Generating GitHub Actions workflow...'); workflowResult = await this.generateWorkflow(owner, repo, options.workflowType); if (workflowResult.success) { results.completed.push(`✅ GitHub Actions workflow created: ${workflowResult.workflowType}`); results.summary.workflow = workflowResult; } else { results.failed.push('❌ GitHub Actions workflow generation failed'); results.warnings.push(workflowResult.error || 'Workflow generation failed'); } } else { logger.info('⏭️ Step 3: Skipping workflow generation (skipWorkflows=true)'); results.completed.push('⏭️ Workflow generation skipped'); } // Step 4: Configure repository secrets let secretsResult = null; if (!options.skipSecrets && options.token) { logger.info('🔐 Step 4: Configuring repository secrets...'); secretsResult = await this.configureRepositorySecrets(owner, repo); if (secretsResult.success) { results.completed.push(`✅ Repository secrets configured: ${secretsResult.secretsCount} secrets`); results.summary.secrets = secretsResult; } else { results.failed.push('❌ Repository secrets configuration failed'); results.warnings.push(secretsResult.error || 'Secrets configuration failed'); } } else { logger.info('⏭️ Step 4: Skipping secrets configuration'); results.completed.push('⏭️ Secrets configuration skipped'); } // Step 5: Validate API connections logger.info('🔗 Step 5: Validating API connections...'); const validationResult = await this.validateApiConnections(); results.summary.validation = validationResult; if (validationResult.allValid) { results.completed.push('✅ All API connections validated'); } else { results.warnings.push('⚠️ Some API connections failed validation'); results.summary.validation.failures.forEach((failure) => { results.warnings.push(` - ${failure}`); }); } // Determine overall success const criticalFailures = results.failed.filter(f => f.includes('.remcode') || f.includes('model initialization')); results.success = criticalFailures.length === 0; // Generate next steps results.nextSteps = this.generateNextSteps(results, options); // Final summary if (results.success) { logger.info('🎉 Automated setup completed successfully!'); results.message = 'Automated Setup Completed Successfully'; } else { logger.warn('⚠️ Automated setup completed with some failures'); results.message = 'Automated Setup Completed with Issues'; } return results; try { } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Automated setup execution failed: ${errorMessage}`); return { success: false, message: 'Automated Setup Failed', error: errorMessage, completed: results.completed, failed: [...results.failed, '❌ Setup execution failed'], troubleshooting: [ 'Check API token permissions and validity', 'Verify network connectivity to all services', 'Ensure repository write permissions', 'Check for any file system permission issues', 'Try running setup steps individually for debugging' ] }; } async; createOrUpdateRemcodeConfig(owner, string, repo, string, options, EnhancedSetupOptions); Promise < any > { try: { const: configExists = await this.configManager.configExists(), // Generate intelligent configuration const: intelligentConfig = await this.generateIntelligentConfig(owner, repo, options), if(configExists) { } } && !options.forceOverwrite }; { // Update existing configuration logger.info('📝 Updating existing .remcode configuration...'); const existingConfig = await this.configManager.readConfig(); const mergedConfig = this.mergeConfigurations(existingConfig, intelligentConfig); await this.configManager.updateConfig(mergedConfig); return { success: true, action: 'updated', config: mergedConfig, message: 'Updated existing .remcode configuration with new settings' }; } { // Create new configuration logger.info('📝 Creating new .remcode configuration...'); await this.configManager.createConfig(owner, repo, intelligentConfig); return { success: true, action: 'created', config: intelligentConfig, message: 'Created new .remcode configuration with intelligent defaults' }; } try { } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Config creation/update failed: ${errorMessage}`); return { success: false, error: errorMessage, message: 'Failed to create/update .remcode configuration' }; } async; generateIntelligentConfig(owner, string, repo, string, options, EnhancedSetupOptions); Promise < any > { // Detect repository characteristics const: repoAnalysis = await this.analyzeRepository(), return: { version: '1.0.0', initialized: new Date().toISOString(), repository: { name: repo, owner: owner, url: `https://github.com/${owner}/${repo}`, defaultBranch: options.branch || 'main', description: `Automated setup for ${owner}/${repo}`, visibility: 'private' // Default to private for security }, processing: { lastCommit: '', lastUpdate: new Date().toISOString(), status: 'pending', nextScheduledRun: this.calculateNextRun() }, vectorization: { provider: options.vectorProvider || 'pinecone', indexName: `remcode-${repo.toLowerCase().replace(/[^a-z0-9-]/g, '-')}`, namespace: options.branch || 'main', embeddingModel: options.embeddingModel || 'microsoft/codebert-base', embeddingModelName: this.getModelDisplayName(options.embeddingModel || 'microsoft/codebert-base'), embeddingDimension: this.getModelDimension(options.embeddingModel || 'microsoft/codebert-base'), chunkSize: repoAnalysis.recommendedChunkSize, modelHealthy: false, // Will be updated after model initialization lastModelCheck: new Date().toISOString() }, chunking: { strategy: repoAnalysis.recommendedChunkingStrategy, maxChunkSize: repoAnalysis.recommendedChunkSize, overlapFactor: 0.2 }, statistics: { filesProcessed: 0, chunksCreated: 0, vectorsStored: 0, lastUpdated: new Date().toISOString(), languagesDetected: repoAnalysis.detectedLanguages, estimatedProcessingTime: repoAnalysis.estimatedProcessingTime }, security: { githubSecretsConfigured: false, // Will be updated after secrets setup requiredSecrets: ['PINECONE_API_KEY', 'HUGGINGFACE_TOKEN'], lastSecretVerification: new Date().toISOString() }, workflow: { githubActionsEnabled: !options.skipWorkflows, workflowPath: '.github/workflows/remcode.yml', workflowType: options.workflowType || 'basic' }, advanced: { ignorePaths: [ 'node_modules/**', '.git/**', 'dist/**', 'build/**', '*.log', '.env*', 'coverage/**' ], includeExtensions: repoAnalysis.relevantExtensions, maxFileSize: 1048576, // 1MB useCache: true } } }; async; initializeEmbeddingModel(embeddingModel ? : string); Promise < any > { try: { const: huggingfaceToken = process.env.HUGGINGFACE_TOKEN, if(, huggingfaceToken) { return { success: false, error: 'HUGGINGFACE_TOKEN environment variable not found', message: 'Please set HUGGINGFACE_TOKEN to initialize embedding models' }; }, logger, : .info(`🤖 Initializing embedding model: ${embeddingModel}...`), const: modelInitializer = new ModelInitializer(huggingfaceToken), const: modelResult = await modelInitializer.initializeEmbeddingModel({ token: huggingfaceToken, preferredModel: embeddingModel || 'microsoft/codebert-base', testEmbedding: true }), if(modelResult) { }, : .success } }; { logger.info(`✅ Model initialized successfully: ${modelResult.modelName}`); return { success: true, modelId: modelResult.modelId, modelName: modelResult.modelName, embeddingDimension: modelResult.embeddingDimension, isHealthy: modelResult.isHealthy, availableModels: modelResult.availableModels, message: `Successfully initialized ${modelResult.modelName} with ${modelResult.embeddingDimension}D embeddings` }; } { logger.warn(`⚠️ Model initialization failed: ${modelResult.error}`); return { success: false, error: modelResult.error, availableModels: modelResult.availableModels, message: 'Model initialization failed - check token and model availability' }; } try { } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Model initialization error: ${errorMessage}`); return { success: false, error: errorMessage, message: 'Model initialization encountered an unexpected error' }; } async; generateWorkflow(owner, string, repo, string, workflowType ? : string); Promise < any > { try: { logger, : .info(`⚙️ Generating ${workflowType} workflow template...`), let, workflowResult, switch(workflowType) { }, case: 'scheduled', workflowResult = await this.workflowGenerator.generateScheduledWorkflow(`${owner}/${repo}`), break: , case: 'advanced', workflowResult = await this.workflowGenerator.generateAdvancedWorkflow(`${owner}/${repo}`), break: , case: 'all', workflowResult = await this.workflowGenerator.generateAllWorkflows(`${owner}/${repo}`), break: , default: workflowResult = await this.workflowGenerator.generateRemcodeWorkflow(`${owner}/${repo}`) }, if(workflowResult) { } } && 'success' in workflowResult && workflowResult.success; { logger.info('✅ GitHub Actions workflow generated successfully'); return { success: true, workflowType: workflowType || 'basic', filePath: workflowResult.filePath, message: `Generated ${workflowType} workflow template` }; } { const error = workflowResult?.error || 'Unknown workflow generation error'; logger.warn(`⚠️ Workflow generation failed: ${error}`); return { success: false, error: error, message: 'Failed to generate GitHub Actions workflow' }; } try { } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Workflow generation error: ${errorMessage}`); return { success: false, error: errorMessage, message: 'Workflow generation encountered an unexpected error' }; } async; configureRepositorySecrets(owner, string, repo, string); Promise < any > { try: { logger, : .info('🔐 Configuring repository secrets...'), const: secretsResult = await this.secretsManager.configureRepositorySecrets(owner, repo), if(secretsResult) { logger.info(`✅ Secrets configured: ${secretsResult.successful} successful, ${secretsResult.failed} failed`); return { success: secretsResult.successful > 0, secretsCount: secretsResult.successful, successfulSecrets: secretsResult.successful, failedSecrets: secretsResult.failed, message: `Configured ${secretsResult.successful} repository secrets` }; }, else: { logger, : .warn('⚠️ No secrets configuration result returned'), return: { success: false, error: 'No secrets configuration result returned', message: 'Failed to configure repository secrets' } } }, catch(error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Secrets configuration error: ${errorMessage}`); return { success: false, error: errorMessage, message: 'Secrets configuration encountered an unexpected error' }; } }; async; validateApiConnections(); Promise < any > { const: validations = { github: false, huggingface: false, pinecone: false, failures: [] }, // Validate GitHub API try: { if(process) { }, : .env.GITHUB_TOKEN } }; { // Simple GitHub API test validations.github = true; logger.info('✅ GitHub API connection validated'); } { validations.failures.push('GitHub: GITHUB_TOKEN not found'); } try { } catch (error) { validations.failures.push(`GitHub: ${error instanceof Error ? error.message : String(error)}`); } // Validate HuggingFace API try { if (process.env.HUGGINGFACE_TOKEN) { validations.huggingface = true; logger.info('✅ HuggingFace API connection validated'); } else { validations.failures.push('HuggingFace: HUGGINGFACE_TOKEN not found'); } } catch (error) { validations.failures.push(`HuggingFace: ${error instanceof Error ? error.message : String(error)}`); } // Validate Pinecone API try { if (process.env.PINECONE_API_KEY) { validations.pinecone = true; logger.info('✅ Pinecone API connection validated'); } else { validations.failures.push('Pinecone: PINECONE_API_KEY not found'); } } catch (error) { validations.failures.push(`Pinecone: ${error instanceof Error ? error.message : String(error)}`); } const allValid = validations.github && validations.huggingface && validations.pinecone; return { allValid, ...validations, summary: allValid ? 'All API connections validated successfully' : `${validations.failures.length} API connection(s) failed validation` }; generatePrerequisiteSolutions(prereqChecks, any[]); string[]; { const solutions = []; prereqChecks.filter(check => !check.passed).forEach(check => { if (check.name.includes('Git')) { solutions.push('Initialize Git repository: git init && git add . && git commit -m "Initial commit"'); } if (check.name.includes('GitHub')) { solutions.push('Add GitHub remote: git remote add origin https://github.com/owner/repo.git'); } if (check.name.includes('Token')) { solutions.push('Set environment variables: GITHUB_TOKEN, PINECONE_API_KEY, HUGGINGFACE_TOKEN'); } }); return solutions; } summarizeChanges(setupPlan, SetupPlan); any; { return { totalActions: setupPlan.actions.length, newFiles: setupPlan.newFiles.length, modifiedFiles: setupPlan.existingFiles.length, apiCalls: setupPlan.apiCalls.length, estimatedDuration: setupPlan.estimatedDuration }; } calculateEstimatedDuration(actions, SetupAction[]); string; { const baseTime = 30; // 30 seconds base const actionTime = actions.length * 15; // 15 seconds per action const totalSeconds = baseTime + actionTime; if (totalSeconds < 60) { return `${totalSeconds} seconds`; } else { const minutes = Math.ceil(totalSeconds / 60); return `${minutes} minute${minutes > 1 ? 's' : ''}`; } } generateRequirements(actions, SetupAction[], apiCalls, ApiCall[]); string[]; { const requirements = []; if (apiCalls.some(call => call.service === 'GitHub')) { requirements.push('GITHUB_TOKEN environment variable for repository access'); } if (apiCalls.some(call => call.service === 'HuggingFace')) { requirements.push('HUGGINGFACE_TOKEN environment variable for embedding models'); } if (apiCalls.some(call => call.service === 'Pinecone')) { requirements.push('PINECONE_API_KEY environment variable for vector storage'); } if (actions.some(action => action.target.includes('workflow'))) { requirements.push('Repository write permissions for GitHub Actions workflows'); } return requirements; } generateNextSteps(results, any, options, EnhancedSetupOptions); string[]; { const nextSteps = []; if (results.success) { nextSteps.push('🎉 Repository is now configured for Remcode!'); if (!options.skipWorkflows) { nextSteps.push('📝 Commit and push changes to trigger initial processing'); nextSteps.push('👀 Monitor GitHub Actions for processing status'); } nextSteps.push('🔍 Use MCP search tools to query your codebase'); nextSteps.push('🤖 Connect AI assistant for codebase-aware conversations'); if (results.warnings.length > 0) { nextSteps.push('⚠️ Review warnings and resolve any optional issues'); } } else { nextSteps.push('🔧 Fix failed setup components before proceeding'); nextSteps.push('📋 Check logs and error messages for specific issues'); nextSteps.push('🔄 Re-run setup after resolving issues'); } return nextSteps; } async; analyzeRepository(); Promise < any > { // Simple repository analysis - in real implementation, this would scan files return: { detectedLanguages: ['typescript', 'javascript'], relevantExtensions: ['.ts', '.js', '.tsx', '.jsx', '.md'], recommendedChunkSize: 750, recommendedChunkingStrategy: { cleanModules: 'function_level', complexModules: 'file_level', monolithicFiles: 'sliding_window' }, estimatedProcessingTime: '2-5 minutes' } }; mergeConfigurations(existing, any, intelligent, any); any; { // Deep merge configurations, preserving existing values where appropriate return { ...intelligent, ...existing, // Preserve critical existing values processing: { ...intelligent.processing, ...existing.processing, // Keep existing lastCommit and status lastCommit: existing.processing?.lastCommit || intelligent.processing.lastCommit, status: existing.processing?.status || intelligent.processing.status }, statistics: { ...intelligent.statistics, ...existing.statistics }, advanced: { ...intelligent.advanced, ...existing.advanced } }; } async; updateConfigWithModelInfo(modelResult, any); Promise < void > { const: currentConfig = await this.configManager.readConfig(), const: updatedConfig = { ...currentConfig, vectorization: { ...currentConfig.vectorization, embeddingModel: modelResult.modelId, embeddingModelName: modelResult.modelName, embeddingDimension: modelResult.embeddingDimension, modelHealthy: modelResult.isHealthy, lastModelCheck: new Date().toISOString(), availableModels: modelResult.availableModels } }, await, this: .configManager.updateConfig(updatedConfig) }; calculateNextRun(); string; { // Calculate next run time (24 hours from now) const nextRun = new Date(); nextRun.setDate(nextRun.getDate() + 1); nextRun.setHours(2, 0, 0, 0); // 2 AM next day return nextRun.toISOString(); } getModelDisplayName(modelId, string); string; { const modelNames = { 'microsoft/codebert-base': 'CodeBERT-Base', 'BAAI/bge-base-en-v1.5': 'BGE-Base', 'sentence-transformers/all-MiniLM-L12-v2': 'MiniLM-L12-v2' }; return modelNames[modelId] || modelId; } getModelDimension(modelId, string); number; { const modelDimensions = { 'microsoft/codebert-base': 768, 'BAAI/bge-base-en-v1.5': 768, 'sentence-transformers/all-MiniLM-L12-v2': 384 }; return modelDimensions[modelId] || 768; }