Files
Christian Gick 0879633faf feat: Tool Compression MCP server for Phase 8
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>
2026-01-08 11:05:27 +02:00

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}%`,
};
}