make-laten 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -35,29 +35,145 @@ function isKnownPattern(content) {
35
35
  return patterns.some((p) => p.test(content));
36
36
  }
37
37
 
38
+ // src/compress/strip.ts
39
+ function stripImports(content) {
40
+ return content.replace(/^import\s+[\s\S]*?from\s+['"][^'"]+['"]\s*;?\s*$/gm, "").replace(/^import\s+['"][^'"]+['"]\s*;?\s*$/gm, "").replace(/^import\s+\{[^}]*\}\s+from\s+['"][^'"]+['"]\s*;?\s*$/gm, "").split("\n").filter((l) => l.trim() !== "").join("\n");
41
+ }
42
+ function stripComments(content) {
43
+ let result = content.replace(/(?<!["'`])\/\/.*$/gm, "");
44
+ result = result.replace(/\/\*[\s\S]*?\*\//g, "");
45
+ return result.split("\n").filter((l) => l.trim() !== "").join("\n");
46
+ }
47
+ function stripBlankLines(content) {
48
+ return content.replace(/\n{3,}/g, "\n\n");
49
+ }
50
+ function stripWhitespace(content) {
51
+ return content.replace(/[ \t]+$/gm, "");
52
+ }
53
+ function stripAll(content) {
54
+ let result = content;
55
+ result = stripImports(result);
56
+ result = stripComments(result);
57
+ result = stripBlankLines(result);
58
+ result = stripWhitespace(result);
59
+ return result.trim();
60
+ }
61
+
62
+ // src/compress/structure.ts
63
+ function extractExports(content) {
64
+ const lines = content.split("\n");
65
+ const exports = [];
66
+ for (const line of lines) {
67
+ const trimmed = line.trim();
68
+ if (/^export\s+(default\s+)?/.test(trimmed)) {
69
+ if (trimmed.includes("{") && !trimmed.includes("}")) {
70
+ exports.push(trimmed.replace(/\{[\s\S]*$/, "{ ... }"));
71
+ } else {
72
+ exports.push(trimmed);
73
+ }
74
+ }
75
+ }
76
+ return exports.join("\n");
77
+ }
78
+ function extractClassMethods(content) {
79
+ const classRegex = /class\s+\w+[^{]*\{([\s\S]*?)\n\}/g;
80
+ const methods = [];
81
+ let match;
82
+ while ((match = classRegex.exec(content)) !== null) {
83
+ const classBody = match[1];
84
+ const methodRegex = /(^\s*(?:public|private|protected|static|async|abstract|\s)*\w+\s*\([^)]*\)(?:\s*:\s*[^{]+)?)\s*\{/gm;
85
+ let methodMatch;
86
+ while ((methodMatch = methodRegex.exec(classBody)) !== null) {
87
+ methods.push(methodMatch[1].trim());
88
+ }
89
+ }
90
+ return methods.join("\n");
91
+ }
92
+ function extractFunctionSignatures(content) {
93
+ const funcRegex = /(export\s+)?(async\s+)?function\s+\w+\s*\([^)]*\)(?:\s*:\s*[^{]+)?\s*\{/g;
94
+ const signatures = [];
95
+ let match;
96
+ while ((match = funcRegex.exec(content)) !== null) {
97
+ signatures.push(match[0].replace(/\{$/, ""));
98
+ }
99
+ return signatures.join("\n");
100
+ }
101
+ function extractTypeDefinitions(content) {
102
+ const typeRegex = /(export\s+)?(interface|type)\s+\w+[^{]*\{[^}]*\}|(export\s+)?type\s+\w+\s*=[^;]+;?/g;
103
+ const types = [];
104
+ let match;
105
+ while ((match = typeRegex.exec(content)) !== null) {
106
+ types.push(match[0].trim());
107
+ }
108
+ return types.join("\n");
109
+ }
110
+
111
+ // src/compress/truncate.ts
112
+ function smartTruncate(content, maxTokens) {
113
+ const lines = content.split("\n");
114
+ const estimatedTokens = Math.ceil(content.length / 4);
115
+ if (estimatedTokens <= maxTokens) return content;
116
+ const targetLines = Math.round(maxTokens * 4 / (content.length / lines.length));
117
+ const headerLines = Math.floor(targetLines * 0.3);
118
+ const footerLines = Math.floor(targetLines * 0.3);
119
+ const omitted = lines.length - headerLines - footerLines;
120
+ const header = lines.slice(0, headerLines);
121
+ const footer = lines.slice(-footerLines);
122
+ return [...header, `
123
+ ... [${omitted} lines omitted] ...
124
+ `, ...footer].join("\n");
125
+ }
126
+
38
127
  // src/compress/file-read.ts
39
128
  var FileReadCompressor = class {
40
129
  async compress(input) {
41
130
  const { content, filePath, language } = input;
42
- const signatures = this.extractSignatures(content, language);
43
- const exports = this.extractExports(content);
44
- const lines = [
131
+ const lines = content.split("\n").length;
132
+ const tokens = Math.ceil(content.length / 4);
133
+ if (tokens < 200) {
134
+ return {
135
+ content,
136
+ original: content,
137
+ confidence: 1,
138
+ metadata: {
139
+ strategy: "passthrough",
140
+ originalLines: lines,
141
+ compressedLines: lines,
142
+ savings: 0
143
+ }
144
+ };
145
+ }
146
+ const stripped = stripAll(content);
147
+ const exports = extractExports(stripped);
148
+ const classes = extractClassMethods(stripped);
149
+ const functions = extractFunctionSignatures(stripped);
150
+ const types = extractTypeDefinitions(stripped);
151
+ const sections = [
45
152
  `// ${filePath} (${content.split("\n").length} lines)`,
46
153
  "",
47
- ...signatures,
48
- "",
49
154
  "// Exports:",
50
- ...exports
51
- ];
52
- const compressed = lines.join("\n");
155
+ exports,
156
+ "",
157
+ "// Classes:",
158
+ classes,
159
+ "",
160
+ "// Functions:",
161
+ functions,
162
+ "",
163
+ "// Types:",
164
+ types
165
+ ].filter((l) => l !== "" && !l.endsWith(":"));
166
+ let compressed = sections.join("\n");
167
+ const maxTokens = 2e3;
168
+ compressed = smartTruncate(compressed, maxTokens);
53
169
  return {
54
170
  content: compressed,
55
171
  original: content,
56
172
  confidence: calculateConfidence(content, compressed),
57
173
  metadata: {
58
- strategy: "signatures",
174
+ strategy: "hybrid",
59
175
  originalLines: content.split("\n").length,
60
- compressedLines: lines.length,
176
+ compressedLines: compressed.split("\n").length,
61
177
  savings: 1 - compressed.length / content.length
62
178
  }
63
179
  };
@@ -65,32 +181,6 @@ var FileReadCompressor = class {
65
181
  async decompress(compressed) {
66
182
  return compressed.original;
67
183
  }
68
- extractSignatures(content, language) {
69
- const signatures = [];
70
- const lines = content.split("\n");
71
- for (const line of lines) {
72
- const trimmed = line.trim();
73
- if (/^(export\s+)?(async\s+)?function\s+\w+/.test(trimmed)) {
74
- signatures.push(trimmed.replace(/\{[\s\S]*$/, "{ ... }"));
75
- }
76
- if (/^(export\s+)?class\s+\w+/.test(trimmed)) {
77
- signatures.push(trimmed);
78
- }
79
- if (/^(export\s+)?(type|interface)\s+\w+/.test(trimmed)) {
80
- signatures.push(trimmed);
81
- }
82
- }
83
- return signatures;
84
- }
85
- extractExports(content) {
86
- const exports = [];
87
- const exportRegex = /export\s+(default\s+)?(function|class|const|let|var|type|interface)\s+(\w+)/g;
88
- let match;
89
- while ((match = exportRegex.exec(content)) !== null) {
90
- exports.push(` - ${match[3]} (${match[2]})`);
91
- }
92
- return exports;
93
- }
94
184
  };
95
185
 
96
186
  // src/cli/commands/read.ts
@@ -249,18 +339,21 @@ import { promisify as promisify2 } from "util";
249
339
  var GitDiffCompressor = class {
250
340
  async compress(input) {
251
341
  const { diff } = input;
252
- const hunks = this.parseDiff(diff);
253
- const condensed = hunks.map((hunk) => ({
254
- file: hunk.file,
255
- changes: this.condenseHunk(hunk),
256
- stats: { additions: hunk.additions, deletions: hunk.deletions }
257
- }));
342
+ const files = this.parseDiff(diff);
258
343
  const lines = [];
259
- for (const hunk of condensed) {
260
- if (hunk.changes.length > 0) {
261
- lines.push(`${hunk.file} (+${hunk.stats.additions} -${hunk.stats.deletions})`);
262
- lines.push(...hunk.changes);
344
+ const totalAdd = files.reduce((s, f) => s + f.additions, 0);
345
+ const totalDel = files.reduce((s, f) => s + f.deletions, 0);
346
+ lines.push(`# ${files.length} files changed: +${totalAdd} -${totalDel}`);
347
+ lines.push("");
348
+ for (const file of files) {
349
+ lines.push(`${file.path} (+${file.additions} -${file.deletions})`);
350
+ const changes = file.lines.filter((l) => l.type === "add" || l.type === "del").map((l) => `${l.type === "add" ? "+" : "-"}${l.content}`);
351
+ const limited = changes.slice(0, 20);
352
+ lines.push(...limited);
353
+ if (changes.length > 20) {
354
+ lines.push(`... [${changes.length - 20} more changes]`);
263
355
  }
356
+ lines.push("");
264
357
  }
265
358
  const compressed = lines.join("\n");
266
359
  return {
@@ -269,7 +362,7 @@ var GitDiffCompressor = class {
269
362
  confidence: calculateConfidence(diff, compressed),
270
363
  metadata: {
271
364
  strategy: "condensed",
272
- filesChanged: condensed.length,
365
+ filesChanged: files.length,
273
366
  savings: 1 - compressed.length / diff.length
274
367
  }
275
368
  };
@@ -278,34 +371,31 @@ var GitDiffCompressor = class {
278
371
  return compressed.original;
279
372
  }
280
373
  parseDiff(diff) {
281
- const hunks = [];
374
+ const files = [];
282
375
  const lines = diff.split("\n");
283
- let currentHunk = null;
376
+ let current = null;
284
377
  for (const line of lines) {
285
378
  if (line.startsWith("diff --git")) {
286
- if (currentHunk) hunks.push(currentHunk);
287
- const fileMatch = line.match(/b\/(.+)/);
288
- currentHunk = {
289
- file: fileMatch?.[1] || "unknown",
290
- lines: [],
379
+ if (current) files.push(current);
380
+ const match = line.match(/b\/(.+)/);
381
+ current = {
382
+ path: match?.[1] || "unknown",
291
383
  additions: 0,
292
- deletions: 0
384
+ deletions: 0,
385
+ lines: []
293
386
  };
294
- } else if (currentHunk) {
387
+ } else if (current) {
295
388
  if (line.startsWith("+")) {
296
- currentHunk.additions++;
297
- currentHunk.lines.push({ type: "addition", content: line.slice(1) });
389
+ current.additions++;
390
+ current.lines.push({ type: "add", content: line.slice(1) });
298
391
  } else if (line.startsWith("-")) {
299
- currentHunk.deletions++;
300
- currentHunk.lines.push({ type: "deletion", content: line.slice(1) });
392
+ current.deletions++;
393
+ current.lines.push({ type: "del", content: line.slice(1) });
301
394
  }
302
395
  }
303
396
  }
304
- if (currentHunk) hunks.push(currentHunk);
305
- return hunks;
306
- }
307
- condenseHunk(hunk) {
308
- return hunk.lines.filter((l) => l.type === "addition" || l.type === "deletion").map((l) => `${l.type === "addition" ? "+" : "-"} ${l.content}`);
397
+ if (current) files.push(current);
398
+ return files;
309
399
  }
310
400
  };
311
401
 
@@ -1490,6 +1580,156 @@ async function initCommand(options) {
1490
1580
  console.log("");
1491
1581
  }
1492
1582
 
1583
+ // src/compress/git-status.ts
1584
+ var GitStatusCompressor = class {
1585
+ async compress(input) {
1586
+ const { status } = input;
1587
+ const statusLines = status.split("\n").filter(Boolean).length;
1588
+ const tokens = Math.ceil(status.length / 4);
1589
+ if (tokens < 10) {
1590
+ return {
1591
+ content: status,
1592
+ original: status,
1593
+ confidence: 1,
1594
+ metadata: {
1595
+ strategy: "passthrough",
1596
+ totalFiles: statusLines,
1597
+ savings: 0
1598
+ }
1599
+ };
1600
+ }
1601
+ const files = this.parseStatus(status);
1602
+ const grouped = this.groupByStatus(files);
1603
+ const lines = [];
1604
+ for (const [statusType, statusFiles] of grouped) {
1605
+ const label = this.statusLabel(statusType);
1606
+ lines.push(`${label} (${statusFiles.length})`);
1607
+ const compressed2 = this.compressPaths(statusFiles.map((f) => f.path));
1608
+ lines.push(...compressed2);
1609
+ lines.push("");
1610
+ }
1611
+ const compressed = lines.join("\n");
1612
+ return {
1613
+ content: compressed,
1614
+ original: status,
1615
+ confidence: calculateConfidence(status, compressed),
1616
+ metadata: {
1617
+ strategy: "grouped",
1618
+ totalFiles: files.length,
1619
+ savings: 1 - compressed.length / status.length
1620
+ }
1621
+ };
1622
+ }
1623
+ async decompress(compressed) {
1624
+ return compressed.original;
1625
+ }
1626
+ parseStatus(status) {
1627
+ return status.split("\n").filter(Boolean).map((line) => {
1628
+ const match = line.match(/^(\?\?|[MADRC]+\s+)(.+)$/);
1629
+ if (match) {
1630
+ return { status: match[1].trim(), path: match[2] };
1631
+ }
1632
+ return { status: line[0], path: line.slice(3) };
1633
+ });
1634
+ }
1635
+ groupByStatus(files) {
1636
+ const grouped = /* @__PURE__ */ new Map();
1637
+ for (const file of files) {
1638
+ const existing = grouped.get(file.status) || [];
1639
+ existing.push(file);
1640
+ grouped.set(file.status, existing);
1641
+ }
1642
+ return grouped;
1643
+ }
1644
+ statusLabel(status) {
1645
+ const labels = {
1646
+ "M": "Modified",
1647
+ "A": "Added",
1648
+ "D": "Deleted",
1649
+ "??": "Untracked",
1650
+ "R": "Renamed"
1651
+ };
1652
+ return labels[status] || `Status ${status}`;
1653
+ }
1654
+ compressPaths(paths) {
1655
+ if (paths.length <= 3) return paths.map((p) => ` ${p}`);
1656
+ const parts = paths.map((p) => p.split("/"));
1657
+ const minLength = Math.min(...parts.map((p) => p.length));
1658
+ let commonIndex = 0;
1659
+ for (let i = 0; i < minLength; i++) {
1660
+ if (parts.every((p) => p[i] === parts[0][i])) {
1661
+ commonIndex = i;
1662
+ } else {
1663
+ break;
1664
+ }
1665
+ }
1666
+ if (commonIndex > 0) {
1667
+ const prefix = parts[0].slice(0, commonIndex).join("/");
1668
+ const suffixes = paths.map((p) => p.slice(prefix.length + 1));
1669
+ return [` ${prefix}/`, ...suffixes.map((s) => ` ${s}`)];
1670
+ }
1671
+ return paths.map((p) => ` ${p}`);
1672
+ }
1673
+ };
1674
+
1675
+ // src/cli/commands/benchmark.ts
1676
+ import fs4 from "fs";
1677
+ import path3 from "path";
1678
+ import { execSync } from "child_process";
1679
+ async function benchmark() {
1680
+ const fileCompressor = new FileReadCompressor();
1681
+ const diffCompressor = new GitDiffCompressor();
1682
+ const statusCompressor = new GitStatusCompressor();
1683
+ const projectPath = process.cwd();
1684
+ console.log("");
1685
+ console.log(" make-laten benchmark");
1686
+ console.log(" ===================");
1687
+ console.log("");
1688
+ const results = [];
1689
+ const testFiles = ["src/index.ts", "package.json", "README.md", "src/mcp/server.ts"];
1690
+ for (const file of testFiles) {
1691
+ const full = path3.join(projectPath, file);
1692
+ if (!fs4.existsSync(full)) continue;
1693
+ const content = fs4.readFileSync(full, "utf-8");
1694
+ const raw = Math.round(content.length / 4);
1695
+ const result = await fileCompressor.compress({ content, filePath: file });
1696
+ const compressed = Math.round(result.content.length / 4);
1697
+ results.push({ name: `read ${file}`, raw, compressed, savings: Math.round((1 - compressed / raw) * 100) });
1698
+ }
1699
+ try {
1700
+ const diff = execSync('git diff HEAD~1 2>/dev/null || echo ""', { cwd: projectPath, encoding: "utf-8" }).trim();
1701
+ if (diff) {
1702
+ const raw = Math.round(diff.length / 4);
1703
+ const result = await diffCompressor.compress({ diff });
1704
+ const compressed = Math.round(result.content.length / 4);
1705
+ results.push({ name: "git-diff", raw, compressed, savings: Math.round((1 - compressed / raw) * 100) });
1706
+ }
1707
+ } catch {
1708
+ }
1709
+ try {
1710
+ const status = execSync('git status --porcelain 2>/dev/null || echo ""', { cwd: projectPath, encoding: "utf-8" }).trim();
1711
+ if (status) {
1712
+ const raw = Math.round(status.length / 4);
1713
+ const result = await statusCompressor.compress({ status });
1714
+ const compressed = Math.round(result.content.length / 4);
1715
+ results.push({ name: "git-status", raw, compressed, savings: Math.round((1 - compressed / raw) * 100) });
1716
+ }
1717
+ } catch {
1718
+ }
1719
+ console.log(" Command".padEnd(28) + "Raw".padEnd(10) + "Compressed".padEnd(12) + "Savings");
1720
+ console.log(" " + "\u2500".repeat(60));
1721
+ for (const r of results) {
1722
+ console.log(` ${r.name}`.padEnd(28) + `${r.raw}`.padEnd(10) + `${r.compressed}`.padEnd(12) + `${r.savings}%`);
1723
+ }
1724
+ const totalRaw = results.reduce((s, r) => s + r.raw, 0);
1725
+ const totalCompressed = results.reduce((s, r) => s + r.compressed, 0);
1726
+ const savings = Math.round((1 - totalCompressed / totalRaw) * 100);
1727
+ console.log("");
1728
+ console.log(` Total: ${totalRaw} \u2192 ${totalCompressed} tokens (${savings}% saved)`);
1729
+ console.log("");
1730
+ }
1731
+ benchmark();
1732
+
1493
1733
  // src/cli/index.ts
1494
1734
  var program = new Command();
1495
1735
  program.name("make-laten").description("Universal efficiency toolkit for AI coding agents").version("1.0.3");
@@ -1505,5 +1745,6 @@ program.command("search").description("Search the web").argument("<query>", "Sea
1505
1745
  program.command("fetch").description("Fetch and compress web content").argument("<url>", "URL to fetch").option("--no-compress", "Disable compression").option("--no-extract", "Disable semantic extraction").action(fetchCommand);
1506
1746
  program.command("install").description("Install make-laten across all detected platforms").option("-u, --uninstall", "Remove adapters").option("-s, --status", "Show installation status").action(installCommand);
1507
1747
  program.command("init").description("Interactive setup wizard \u2014 detect agents & configure MCP").option("-a, --all", "Configure all detected agents (no prompts)").option("-p, --project", "Create .mcp.json in current directory only").action(initCommand);
1748
+ program.command("benchmark").description("Compare raw vs compressed output").action(void 0);
1508
1749
  program.parse();
1509
1750
  //# sourceMappingURL=index.mjs.map