vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
198 lines (197 loc) • 8.28 kB
JavaScript
import { TokenEstimator } from './token-estimator.js';
import { readFileSecure } from '../../code-map-generator/fsUtils.js';
export class FileContentProcessor {
static DEFAULT_LOC_THRESHOLD = 1000;
static DEFAULT_MAX_CONTENT_LENGTH = 25;
static async processFileContent(filePath, fileContent, options) {
const startTime = Date.now();
const lines = fileContent.split('\n');
const totalLines = lines.length;
const locThreshold = options.locThreshold || this.DEFAULT_LOC_THRESHOLD;
const contentType = this.detectContentType(filePath);
const originalTokenEstimate = TokenEstimator.estimateFileTokens(filePath, fileContent, this.mapToTokenEstimatorType(contentType));
const processingMetadata = {
filePath,
fileSize: fileContent.length,
processingTime: 0,
optimizationApplied: false,
contentType,
encoding: 'utf-8'
};
if (totalLines <= locThreshold) {
const contentSection = {
type: 'full',
startLine: 1,
endLine: totalLines,
content: fileContent,
tokenCount: originalTokenEstimate.totalTokens,
description: `Complete file content (${totalLines} lines)`
};
processingMetadata.processingTime = Date.now() - startTime;
return {
content: fileContent,
isOptimized: false,
totalLines,
fullContentLines: totalLines,
tokenEstimate: originalTokenEstimate.totalTokens,
contentSections: [contentSection],
processingMetadata
};
}
return this.processLargeFile(filePath, fileContent, lines, options, processingMetadata, startTime);
}
static async processLargeFile(filePath, fileContent, lines, options, processingMetadata, startTime) {
const totalLines = lines.length;
const locThreshold = options.locThreshold || this.DEFAULT_LOC_THRESHOLD;
const fullContentLines = lines.slice(0, locThreshold);
const optimizationLines = lines.slice(locThreshold);
const fullContent = fullContentLines.join('\n');
const optimizationContent = optimizationLines.join('\n');
const optimizedContent = await this.optimizeContent(optimizationContent, options);
const fullContentTokens = TokenEstimator.estimateTokens(fullContent);
const optimizedContentTokens = TokenEstimator.estimateTokens(optimizedContent);
const totalTokens = fullContentTokens + optimizedContentTokens;
const contentSections = [
{
type: 'full',
startLine: 1,
endLine: locThreshold,
content: fullContent,
tokenCount: fullContentTokens,
description: `Unoptimized content (lines 1-${locThreshold})`
},
{
type: 'optimized',
startLine: locThreshold + 1,
endLine: totalLines,
content: optimizedContent,
tokenCount: optimizedContentTokens,
description: `Optimized content (lines ${locThreshold + 1}-${totalLines})`
}
];
const combinedContent = this.combineContentWithMarkers(fullContent, optimizedContent, locThreshold);
const originalOptimizationTokens = TokenEstimator.estimateTokens(optimizationContent);
const optimizationRatio = optimizedContentTokens / originalOptimizationTokens;
processingMetadata.processingTime = Date.now() - startTime;
processingMetadata.optimizationApplied = true;
processingMetadata.optimizationRatio = optimizationRatio;
return {
content: combinedContent,
isOptimized: true,
totalLines,
fullContentLines: locThreshold,
optimizedLines: totalLines - locThreshold,
tokenEstimate: totalTokens,
contentSections,
processingMetadata
};
}
static async optimizeContent(content, options) {
try {
const maxLength = options.maxContentLength || this.DEFAULT_MAX_CONTENT_LENGTH;
const preserveComments = options.preserveComments ?? true;
let optimized = (content || '')
.split('\n')
.map(line => (line || '').trim())
.filter(line => {
if (line.length === 0)
return false;
if (!preserveComments && (line.startsWith('//') || line.startsWith('#') || line.startsWith('/*'))) {
return false;
}
return true;
})
.join('\n');
if (optimized.length > maxLength * 100) {
const lines = optimized.split('\n');
const targetLines = Math.floor(lines.length * 0.7);
optimized = lines.slice(0, targetLines).join('\n') + '\n// ... (content optimized for token efficiency)';
}
return optimized;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown optimization error';
return `// [OPTIMIZATION WARNING: ${errorMessage}]\n${content}`;
}
}
static combineContentWithMarkers(fullContent, optimizedContent, threshold) {
const marker = `\n// ===== OPTIMIZATION BOUNDARY (Line ${threshold}) =====\n// Content below this line has been optimized for token efficiency\n// Original structure and semantics are preserved\n\n`;
return fullContent + marker + optimizedContent;
}
static detectContentType(filePath) {
const extension = filePath.split('.').pop()?.toLowerCase();
const typeMap = {
'js': 'javascript',
'ts': 'typescript',
'jsx': 'javascript',
'tsx': 'typescript',
'py': 'python',
'java': 'java',
'cpp': 'cpp',
'c': 'c',
'cs': 'csharp',
'php': 'php',
'rb': 'ruby',
'go': 'go',
'rs': 'rust',
'swift': 'swift',
'kt': 'kotlin',
'json': 'json',
'xml': 'xml',
'html': 'html',
'css': 'css',
'md': 'markdown',
'yml': 'yaml',
'yaml': 'yaml'
};
return typeMap[extension || ''] || 'text';
}
static mapToTokenEstimatorType(contentType) {
switch (contentType) {
case 'xml':
case 'html':
return 'xml';
case 'json':
return 'json';
case 'markdown':
return 'markdown';
case 'javascript':
case 'typescript':
case 'python':
case 'java':
case 'cpp':
case 'c':
case 'csharp':
case 'php':
case 'ruby':
case 'go':
case 'rust':
case 'swift':
case 'kotlin':
return 'code';
default:
return 'plain';
}
}
static async readAndProcessFile(filePath, options) {
try {
const fileContent = await readFileSecure(filePath, options.allowedDirectory);
return this.processFileContent(filePath, fileContent, options);
}
catch (error) {
throw new Error(`Failed to read and process file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
static getProcessingStats(result) {
const tokenEfficiency = result.isOptimized && result.processingMetadata.optimizationRatio
? (1 - result.processingMetadata.optimizationRatio) * 100
: 0;
return {
totalLines: result.totalLines,
optimizationApplied: result.isOptimized,
tokenEfficiency: Math.round(tokenEfficiency * 100) / 100,
processingTime: result.processingMetadata.processingTime,
contentSectionCount: result.contentSections.length
};
}
}