MCP server providing compressed versions of Read/Grep/Glob: - compressed_read: removes comments, blanks, collapses imports - compressed_grep: groups by file, dedupes adjacent matches - compressed_glob: collapses directories, shows type distribution Test results: 66.7% compression on sample file 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
131 lines
4.0 KiB
JavaScript
131 lines
4.0 KiB
JavaScript
/**
|
|
* Read Compressor - Compress file content while preserving essential information
|
|
*
|
|
* Strategies:
|
|
* - Remove blank lines (configurable)
|
|
* - Remove comment-only lines (language-aware)
|
|
* - Collapse import blocks
|
|
* - Preserve line numbers for reference
|
|
*/
|
|
// Language-specific comment patterns
|
|
const COMMENT_PATTERNS = {
|
|
// Single-line comments
|
|
javascript: [/^\s*\/\/.*$/, /^\s*\/\*.*\*\/\s*$/],
|
|
typescript: [/^\s*\/\/.*$/, /^\s*\/\*.*\*\/\s*$/],
|
|
python: [/^\s*#.*$/],
|
|
ruby: [/^\s*#.*$/],
|
|
bash: [/^\s*#.*$/],
|
|
swift: [/^\s*\/\/.*$/],
|
|
go: [/^\s*\/\/.*$/],
|
|
rust: [/^\s*\/\/.*$/],
|
|
php: [/^\s*\/\/.*$/, /^\s*#.*$/, /^\s*\/\*.*\*\/\s*$/],
|
|
};
|
|
// Import patterns by language
|
|
const IMPORT_PATTERNS = {
|
|
javascript: /^(import|export)\s+/,
|
|
typescript: /^(import|export)\s+/,
|
|
python: /^(import|from)\s+/,
|
|
swift: /^import\s+/,
|
|
go: /^import\s+/,
|
|
rust: /^use\s+/,
|
|
php: /^(use|require|include)/,
|
|
};
|
|
function detectLanguage(filename) {
|
|
const ext = filename.split('.').pop()?.toLowerCase() || '';
|
|
const langMap = {
|
|
js: 'javascript',
|
|
jsx: 'javascript',
|
|
ts: 'typescript',
|
|
tsx: 'typescript',
|
|
py: 'python',
|
|
rb: 'ruby',
|
|
sh: 'bash',
|
|
bash: 'bash',
|
|
swift: 'swift',
|
|
go: 'go',
|
|
rs: 'rust',
|
|
php: 'php',
|
|
};
|
|
return langMap[ext] || 'unknown';
|
|
}
|
|
function isCommentLine(line, language) {
|
|
const patterns = COMMENT_PATTERNS[language];
|
|
if (!patterns)
|
|
return false;
|
|
return patterns.some(pattern => pattern.test(line));
|
|
}
|
|
function isImportLine(line, language) {
|
|
const pattern = IMPORT_PATTERNS[language];
|
|
if (!pattern)
|
|
return false;
|
|
return pattern.test(line.trim());
|
|
}
|
|
export function compressRead(content, filename, options = {}) {
|
|
const { removeBlankLines = true, removeComments = true, collapseImports = true, maxLines = 500, } = options;
|
|
const language = detectLanguage(filename);
|
|
const lines = content.split('\n');
|
|
const originalLines = lines.length;
|
|
const result = [];
|
|
let importBlock = [];
|
|
let inImportBlock = false;
|
|
let lineNumber = 0;
|
|
for (const line of lines) {
|
|
lineNumber++;
|
|
// Skip blank lines if configured
|
|
if (removeBlankLines && line.trim() === '') {
|
|
continue;
|
|
}
|
|
// Skip comment lines if configured
|
|
if (removeComments && isCommentLine(line, language)) {
|
|
continue;
|
|
}
|
|
// Handle import collapsing
|
|
if (collapseImports && isImportLine(line, language)) {
|
|
if (!inImportBlock) {
|
|
inImportBlock = true;
|
|
importBlock = [];
|
|
}
|
|
importBlock.push(line.trim());
|
|
continue;
|
|
}
|
|
else if (inImportBlock) {
|
|
// End of import block - collapse it
|
|
if (importBlock.length > 3) {
|
|
result.push(`// [${importBlock.length} imports collapsed]`);
|
|
}
|
|
else {
|
|
result.push(...importBlock);
|
|
}
|
|
importBlock = [];
|
|
inImportBlock = false;
|
|
}
|
|
// Add line with number prefix for reference
|
|
result.push(`${lineNumber}: ${line}`);
|
|
}
|
|
// Handle remaining imports at end of file
|
|
if (importBlock.length > 0) {
|
|
if (importBlock.length > 3) {
|
|
result.push(`// [${importBlock.length} imports collapsed]`);
|
|
}
|
|
else {
|
|
result.push(...importBlock);
|
|
}
|
|
}
|
|
// Truncate if too long
|
|
let compressed = result;
|
|
let truncated = false;
|
|
if (compressed.length > maxLines) {
|
|
compressed = compressed.slice(0, maxLines);
|
|
compressed.push(`\n... [${result.length - maxLines} more lines truncated]`);
|
|
truncated = true;
|
|
}
|
|
const compressedLines = compressed.length;
|
|
const savings = ((1 - compressedLines / originalLines) * 100).toFixed(1);
|
|
return {
|
|
content: compressed.join('\n'),
|
|
originalLines,
|
|
compressedLines,
|
|
savings: `${savings}%`,
|
|
};
|
|
}
|