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>
This commit is contained in:
130
dist/compressors/read.js
vendored
Normal file
130
dist/compressors/read.js
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* 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}%`,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user