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>
122 lines
4.0 KiB
JavaScript
122 lines
4.0 KiB
JavaScript
/**
|
|
* Grep Compressor - Compress search results while preserving essential matches
|
|
*
|
|
* Strategies:
|
|
* - Group by file
|
|
* - Show first N matches per file + count
|
|
* - Dedupe similar/adjacent matches
|
|
* - Prioritize exact matches
|
|
*/
|
|
function parseGrepOutput(output) {
|
|
const matches = [];
|
|
const lines = output.split('\n').filter(l => l.trim());
|
|
for (const line of lines) {
|
|
// Parse format: file:line:content or file:line-content
|
|
const match = line.match(/^(.+?):(\d+)[:-](.*)$/);
|
|
if (match) {
|
|
matches.push({
|
|
file: match[1],
|
|
line: parseInt(match[2]),
|
|
content: match[3],
|
|
});
|
|
}
|
|
}
|
|
return matches;
|
|
}
|
|
function groupByFile(matches) {
|
|
const grouped = new Map();
|
|
for (const match of matches) {
|
|
const existing = grouped.get(match.file) || [];
|
|
existing.push(match);
|
|
grouped.set(match.file, existing);
|
|
}
|
|
return grouped;
|
|
}
|
|
function dedupeAdjacent(matches, threshold = 3) {
|
|
if (matches.length <= 1)
|
|
return matches;
|
|
const result = [matches[0]];
|
|
let skipped = 0;
|
|
for (let i = 1; i < matches.length; i++) {
|
|
const prev = result[result.length - 1];
|
|
const curr = matches[i];
|
|
// Skip if within threshold lines of previous match
|
|
if (curr.line - prev.line <= threshold) {
|
|
skipped++;
|
|
continue;
|
|
}
|
|
result.push(curr);
|
|
}
|
|
// Add note about skipped adjacent matches
|
|
if (skipped > 0 && result.length > 0) {
|
|
const last = result[result.length - 1];
|
|
result.push({
|
|
file: last.file,
|
|
line: -1,
|
|
content: `[${skipped} adjacent matches omitted]`,
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
export function compressGrep(output, options = {}) {
|
|
const { maxMatchesPerFile = 3, maxTotalMatches = 20, dedupeAdjacent: shouldDedupe = true, showCounts = true, } = options;
|
|
const matches = parseGrepOutput(output);
|
|
const originalMatches = matches.length;
|
|
if (originalMatches === 0) {
|
|
return {
|
|
content: 'No matches found.',
|
|
originalMatches: 0,
|
|
compressedMatches: 0,
|
|
filesMatched: 0,
|
|
savings: '0%',
|
|
};
|
|
}
|
|
const grouped = groupByFile(matches);
|
|
const filesMatched = grouped.size;
|
|
const result = [];
|
|
let totalShown = 0;
|
|
// Sort files by match count (most matches first)
|
|
const sortedFiles = Array.from(grouped.entries()).sort((a, b) => b[1].length - a[1].length);
|
|
for (const [file, fileMatches] of sortedFiles) {
|
|
if (totalShown >= maxTotalMatches) {
|
|
const remaining = sortedFiles.length - result.filter(l => l.startsWith('## ')).length;
|
|
if (remaining > 0) {
|
|
result.push(`\n... [${remaining} more files with matches]`);
|
|
}
|
|
break;
|
|
}
|
|
// Dedupe adjacent matches if configured
|
|
let processed = shouldDedupe ? dedupeAdjacent(fileMatches) : fileMatches;
|
|
// Limit matches per file
|
|
const totalInFile = fileMatches.length;
|
|
const shown = processed.slice(0, maxMatchesPerFile);
|
|
const omitted = totalInFile - shown.length;
|
|
result.push(`## ${file}`);
|
|
if (showCounts && totalInFile > maxMatchesPerFile) {
|
|
result.push(`(${totalInFile} matches, showing ${shown.length})`);
|
|
}
|
|
for (const match of shown) {
|
|
if (match.line === -1) {
|
|
result.push(` ${match.content}`);
|
|
}
|
|
else {
|
|
result.push(` ${match.line}: ${match.content.trim()}`);
|
|
totalShown++;
|
|
}
|
|
}
|
|
if (omitted > 0) {
|
|
result.push(` ... [${omitted} more matches in this file]`);
|
|
}
|
|
result.push('');
|
|
}
|
|
const compressedMatches = totalShown;
|
|
const savings = ((1 - compressedMatches / originalMatches) * 100).toFixed(1);
|
|
return {
|
|
content: result.join('\n').trim(),
|
|
originalMatches,
|
|
compressedMatches,
|
|
filesMatched,
|
|
savings: `${savings}%`,
|
|
};
|
|
}
|