UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

124 lines (123 loc) 4.71 kB
import fs from 'fs/promises'; import path from 'path'; import logger from '../../../logger.js'; import { FileChangeDetector } from './fileChangeDetector.js'; import { getCacheDirectory } from '../directoryUtils.js'; export class IncrementalProcessor { fileChangeDetector; config; baseDir; previousFilesListPath; static DEFAULT_CONFIG = { useFileHashes: true, useFileMetadata: true, maxCachedHashes: 10000, maxHashAge: 24 * 60 * 60 * 1000, previousFilesListPath: '', saveProcessedFilesList: true }; constructor(fileContentManager, config) { this.config = { ...IncrementalProcessor.DEFAULT_CONFIG, ...config.processing?.incrementalConfig }; this.fileChangeDetector = new FileChangeDetector(fileContentManager, { useFileHashes: this.config.useFileHashes, useFileMetadata: this.config.useFileMetadata, maxCachedHashes: this.config.maxCachedHashes, maxHashAge: this.config.maxHashAge }); this.baseDir = config.allowedMappingDirectory; if (this.config.previousFilesListPath) { this.previousFilesListPath = this.config.previousFilesListPath; } else { const cacheDir = getCacheDirectory(config); this.previousFilesListPath = path.join(cacheDir, 'processed-files.json'); } logger.debug(`IncrementalProcessor created with config: ${JSON.stringify(this.config)}`); } async processIncrementally(filePaths) { const previousFiles = await this.loadPreviousFiles(); if (previousFiles.length === 0) { logger.info('No previously processed files found, processing all files'); if (this.config.saveProcessedFilesList) { await this.savePreviousFiles(filePaths); } return { changedFiles: filePaths, unchangedFiles: [], totalFiles: filePaths.length, changePercentage: 100 }; } this.fileChangeDetector.setProcessedFiles(previousFiles); const changedFiles = []; const unchangedFiles = []; for (const filePath of filePaths) { const wasProcessed = this.fileChangeDetector.wasFileProcessed(filePath); if (!wasProcessed) { changedFiles.push(filePath); continue; } const result = await this.fileChangeDetector.detectChange(filePath, this.baseDir); if (result.changed) { changedFiles.push(filePath); } else { unchangedFiles.push(filePath); } } const totalFiles = filePaths.length; const changePercentage = totalFiles > 0 ? (changedFiles.length / totalFiles) * 100 : 0; logger.info(`Incremental processing: ${changedFiles.length} changed files, ${unchangedFiles.length} unchanged files (${changePercentage.toFixed(2)}% changed)`); if (this.config.saveProcessedFilesList) { await this.savePreviousFiles(filePaths); } return { changedFiles, unchangedFiles, totalFiles, changePercentage }; } async loadPreviousFiles() { try { try { await fs.access(this.previousFilesListPath); } catch { return []; } const content = await fs.readFile(this.previousFilesListPath, 'utf-8'); const data = JSON.parse(content); if (Array.isArray(data)) { logger.debug(`Loaded ${data.length} previously processed files`); return data; } logger.warn('Invalid format for previously processed files list'); return []; } catch (error) { logger.error({ err: error }, 'Error loading previously processed files'); return []; } } async savePreviousFiles(filePaths) { try { const dir = path.dirname(this.previousFilesListPath); await fs.mkdir(dir, { recursive: true }); await fs.writeFile(this.previousFilesListPath, JSON.stringify(filePaths), 'utf-8'); logger.debug(`Saved ${filePaths.length} processed files for the next run`); } catch (error) { logger.error({ err: error }, 'Error saving processed files list'); } } getFileChangeDetector() { return this.fileChangeDetector; } clearCache() { this.fileChangeDetector.clearCache(); } }