make-laten 1.1.0 → 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.
- package/README.md +198 -51
- package/dist/cli/index.js +376 -175
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +376 -175
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.js +288 -68
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +274 -67
- package/dist/index.mjs.map +1 -1
- package/dist/mcp/server.js +250 -68
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/server.mjs +250 -68
- package/dist/mcp/server.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -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
|
|
43
|
-
const
|
|
44
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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: "
|
|
174
|
+
strategy: "hybrid",
|
|
59
175
|
originalLines: content.split("\n").length,
|
|
60
|
-
compressedLines:
|
|
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
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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:
|
|
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
|
|
374
|
+
const files = [];
|
|
282
375
|
const lines = diff.split("\n");
|
|
283
|
-
let
|
|
376
|
+
let current = null;
|
|
284
377
|
for (const line of lines) {
|
|
285
378
|
if (line.startsWith("diff --git")) {
|
|
286
|
-
if (
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
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 (
|
|
387
|
+
} else if (current) {
|
|
295
388
|
if (line.startsWith("+")) {
|
|
296
|
-
|
|
297
|
-
|
|
389
|
+
current.additions++;
|
|
390
|
+
current.lines.push({ type: "add", content: line.slice(1) });
|
|
298
391
|
} else if (line.startsWith("-")) {
|
|
299
|
-
|
|
300
|
-
|
|
392
|
+
current.deletions++;
|
|
393
|
+
current.lines.push({ type: "del", content: line.slice(1) });
|
|
301
394
|
}
|
|
302
395
|
}
|
|
303
396
|
}
|
|
304
|
-
if (
|
|
305
|
-
return
|
|
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
|
|
|
@@ -1326,125 +1416,102 @@ async function directoryExists(dirPath) {
|
|
|
1326
1416
|
return false;
|
|
1327
1417
|
}
|
|
1328
1418
|
}
|
|
1419
|
+
async function readJSON(filePath) {
|
|
1420
|
+
try {
|
|
1421
|
+
const raw = await fs3.readFile(filePath, "utf-8");
|
|
1422
|
+
return JSON.parse(raw);
|
|
1423
|
+
} catch {
|
|
1424
|
+
return {};
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
async function writeJSON(filePath, data) {
|
|
1428
|
+
try {
|
|
1429
|
+
const dir = path2.dirname(filePath);
|
|
1430
|
+
await fs3.mkdir(dir, { recursive: true });
|
|
1431
|
+
await fs3.writeFile(filePath, JSON.stringify(data, null, 2));
|
|
1432
|
+
return true;
|
|
1433
|
+
} catch {
|
|
1434
|
+
return false;
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
var MCP_CMD = ["npx", "-y", "make-laten-mcp", "server"];
|
|
1329
1438
|
async function detectAllAgents() {
|
|
1330
1439
|
const home = getHome2();
|
|
1331
1440
|
const agents2 = [];
|
|
1332
|
-
const claudeConfig = path2.join(home, ".claude", "mcp.json");
|
|
1333
|
-
const claudeDir = path2.join(home, ".claude");
|
|
1334
1441
|
agents2.push({
|
|
1335
1442
|
name: "Claude Code",
|
|
1336
|
-
detected: await commandExists2("claude") || await directoryExists(
|
|
1337
|
-
configPath:
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1443
|
+
detected: await commandExists2("claude") || await directoryExists(path2.join(home, ".claude")),
|
|
1444
|
+
configPath: path2.join(home, ".claude", "mcp.json"),
|
|
1445
|
+
version: await commandExists2("claude") ? await execAsync4("claude --version", { timeout: 3e3 }).then((r) => r.stdout.trim().split("\n")[0]).catch(() => "unknown") : void 0,
|
|
1446
|
+
writeConfig: async (p) => {
|
|
1447
|
+
const data = await readJSON(p);
|
|
1448
|
+
data.mcpServers = { ...data.mcpServers || {}, "make-laten": { command: "npx", args: ["-y", "make-laten-mcp", "server"] } };
|
|
1449
|
+
return writeJSON(p, data);
|
|
1450
|
+
}
|
|
1344
1451
|
});
|
|
1345
|
-
const cursorMcp = path2.join(home, ".cursor", "mcp.json");
|
|
1346
|
-
const cursorDir = path2.join(home, ".cursor");
|
|
1347
1452
|
agents2.push({
|
|
1348
1453
|
name: "Cursor",
|
|
1349
|
-
detected: await directoryExists(
|
|
1350
|
-
configPath:
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1454
|
+
detected: await directoryExists(path2.join(home, ".cursor")),
|
|
1455
|
+
configPath: path2.join(home, ".cursor", "mcp.json"),
|
|
1456
|
+
writeConfig: async (p) => {
|
|
1457
|
+
const data = await readJSON(p);
|
|
1458
|
+
data.mcpServers = { ...data.mcpServers || {}, "make-laten": { command: "npx", args: ["-y", "make-laten-mcp", "server"] } };
|
|
1459
|
+
return writeJSON(p, data);
|
|
1355
1460
|
}
|
|
1356
1461
|
});
|
|
1357
|
-
const codexMcp = path2.join(home, ".codex", "config.json");
|
|
1358
|
-
const codexDir = path2.join(home, ".codex");
|
|
1359
1462
|
agents2.push({
|
|
1360
1463
|
name: "Codex",
|
|
1361
|
-
detected: await commandExists2("codex") || await directoryExists(
|
|
1362
|
-
configPath:
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1464
|
+
detected: await commandExists2("codex") || await directoryExists(path2.join(home, ".codex")),
|
|
1465
|
+
configPath: path2.join(home, ".codex", "config.json"),
|
|
1466
|
+
writeConfig: async (p) => {
|
|
1467
|
+
const data = await readJSON(p);
|
|
1468
|
+
data.mcpServers = { ...data.mcpServers || {}, "make-laten": { command: "npx", args: ["-y", "make-laten-mcp", "server"] } };
|
|
1469
|
+
return writeJSON(p, data);
|
|
1367
1470
|
}
|
|
1368
1471
|
});
|
|
1369
|
-
const windsurfMcp = path2.join(home, ".codeium", "windsurf", "mcp_config.json");
|
|
1370
|
-
const windsurfDir = path2.join(home, ".codeium", "windsurf");
|
|
1371
1472
|
agents2.push({
|
|
1372
|
-
name: "
|
|
1373
|
-
detected: await directoryExists(
|
|
1374
|
-
configPath:
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1473
|
+
name: "OpenCode",
|
|
1474
|
+
detected: await directoryExists(path2.join(home, ".config", "opencode")),
|
|
1475
|
+
configPath: path2.join(home, ".config", "opencode", "opencode.json"),
|
|
1476
|
+
writeConfig: async (p) => {
|
|
1477
|
+
const data = await readJSON(p);
|
|
1478
|
+
data.mcp = { ...data.mcp || {}, "make-laten": { type: "local", command: MCP_CMD, enabled: true } };
|
|
1479
|
+
delete data.mcpServers;
|
|
1480
|
+
return writeJSON(p, data);
|
|
1379
1481
|
}
|
|
1380
1482
|
});
|
|
1381
|
-
const openCodeConfig = path2.join(home, ".config", "opencode", "opencode.json");
|
|
1382
|
-
const openCodeDir = path2.join(home, ".config", "opencode");
|
|
1383
1483
|
agents2.push({
|
|
1384
|
-
name: "
|
|
1385
|
-
detected: await directoryExists(
|
|
1386
|
-
configPath:
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1484
|
+
name: "Windsurf",
|
|
1485
|
+
detected: await directoryExists(path2.join(home, ".codeium", "windsurf")),
|
|
1486
|
+
configPath: path2.join(home, ".codeium", "windsurf", "mcp_config.json"),
|
|
1487
|
+
writeConfig: async (p) => {
|
|
1488
|
+
const data = await readJSON(p);
|
|
1489
|
+
data.mcpServers = { ...data.mcpServers || {}, "make-laten": { command: "npx", args: ["-y", "make-laten-mcp", "server"] } };
|
|
1490
|
+
return writeJSON(p, data);
|
|
1391
1491
|
}
|
|
1392
1492
|
});
|
|
1393
|
-
const clineDir = path2.join(home, ".cline");
|
|
1394
|
-
const clineMcp = path2.join(home, ".cline", "mcp_settings.json");
|
|
1395
1493
|
agents2.push({
|
|
1396
1494
|
name: "Cline",
|
|
1397
|
-
detected: await directoryExists(
|
|
1398
|
-
configPath:
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1495
|
+
detected: await directoryExists(path2.join(home, ".cline")),
|
|
1496
|
+
configPath: path2.join(home, ".cline", "mcp_settings.json"),
|
|
1497
|
+
writeConfig: async (p) => {
|
|
1498
|
+
const data = await readJSON(p);
|
|
1499
|
+
data.mcpServers = { ...data.mcpServers || {}, "make-laten": { command: "npx", args: ["-y", "make-laten-mcp", "server"] } };
|
|
1500
|
+
return writeJSON(p, data);
|
|
1403
1501
|
}
|
|
1404
1502
|
});
|
|
1405
|
-
const vscodeDir = path2.join(home, "Library", "Application Support", "Code", "User", "globalStorage");
|
|
1406
|
-
agents2.push({
|
|
1407
|
-
name: "GitHub Copilot (VS Code)",
|
|
1408
|
-
detected: await directoryExists(vscodeDir),
|
|
1409
|
-
configPath: "",
|
|
1410
|
-
mcpConfig: {}
|
|
1411
|
-
});
|
|
1412
|
-
const geminiDir = path2.join(home, ".gemini");
|
|
1413
1503
|
agents2.push({
|
|
1414
1504
|
name: "Gemini CLI",
|
|
1415
|
-
detected: await commandExists2("gemini") || await directoryExists(
|
|
1505
|
+
detected: await commandExists2("gemini") || await directoryExists(path2.join(home, ".gemini")),
|
|
1416
1506
|
configPath: path2.join(home, ".gemini", "settings.json"),
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1507
|
+
writeConfig: async (p) => {
|
|
1508
|
+
const data = await readJSON(p);
|
|
1509
|
+
data.mcpServers = { ...data.mcpServers || {}, "make-laten": { command: "npx", args: ["-y", "make-laten-mcp", "server"] } };
|
|
1510
|
+
return writeJSON(p, data);
|
|
1421
1511
|
}
|
|
1422
1512
|
});
|
|
1423
1513
|
return agents2;
|
|
1424
1514
|
}
|
|
1425
|
-
async function writeMCPConfig(agent) {
|
|
1426
|
-
try {
|
|
1427
|
-
const dir = path2.dirname(agent.configPath);
|
|
1428
|
-
await fs3.mkdir(dir, { recursive: true });
|
|
1429
|
-
let existing = {};
|
|
1430
|
-
try {
|
|
1431
|
-
const raw = await fs3.readFile(agent.configPath, "utf-8");
|
|
1432
|
-
existing = JSON.parse(raw);
|
|
1433
|
-
} catch {
|
|
1434
|
-
}
|
|
1435
|
-
const merged = {
|
|
1436
|
-
...existing,
|
|
1437
|
-
mcpServers: {
|
|
1438
|
-
...existing.mcpServers || {},
|
|
1439
|
-
...agent.mcpConfig.mcpServers
|
|
1440
|
-
}
|
|
1441
|
-
};
|
|
1442
|
-
await fs3.writeFile(agent.configPath, JSON.stringify(merged, null, 2));
|
|
1443
|
-
return true;
|
|
1444
|
-
} catch {
|
|
1445
|
-
return false;
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
1515
|
function askQuestion(rl, question) {
|
|
1449
1516
|
return new Promise((resolve) => rl.question(question, resolve));
|
|
1450
1517
|
}
|
|
@@ -1471,42 +1538,25 @@ async function initCommand(options) {
|
|
|
1471
1538
|
console.log("");
|
|
1472
1539
|
if (options.all) {
|
|
1473
1540
|
for (const agent of detected) {
|
|
1474
|
-
const success = await
|
|
1541
|
+
const success = await agent.writeConfig(agent.configPath);
|
|
1475
1542
|
if (success) {
|
|
1476
1543
|
console.log(` \u2713 ${agent.name} \u2192 MCP configured`);
|
|
1477
1544
|
} else {
|
|
1478
|
-
console.log(` \u2717 ${agent.name} \u2192 failed`);
|
|
1545
|
+
console.log(` \u2717 ${agent.name} \u2192 failed to write config`);
|
|
1479
1546
|
}
|
|
1480
1547
|
}
|
|
1481
1548
|
} else if (options.project) {
|
|
1482
1549
|
const cwd = process.cwd();
|
|
1483
|
-
const mcpConfig = {
|
|
1484
|
-
mcpServers: {
|
|
1485
|
-
"make-laten": { command: "npx", args: ["-y", "make-laten-mcp", "server"] }
|
|
1486
|
-
}
|
|
1487
|
-
};
|
|
1488
1550
|
const configPath = path2.join(cwd, ".mcp.json");
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
const raw = await fs3.readFile(configPath, "utf-8");
|
|
1492
|
-
existing = JSON.parse(raw);
|
|
1493
|
-
} catch {
|
|
1494
|
-
}
|
|
1495
|
-
const merged = {
|
|
1496
|
-
...existing,
|
|
1497
|
-
mcpServers: {
|
|
1498
|
-
...existing.mcpServers || {},
|
|
1499
|
-
...mcpConfig.mcpServers
|
|
1500
|
-
}
|
|
1501
|
-
};
|
|
1502
|
-
await fs3.writeFile(configPath, JSON.stringify(merged, null, 2));
|
|
1551
|
+
const data = { mcpServers: { "make-laten": { command: "npx", args: ["-y", "make-laten-mcp", "server"] } } };
|
|
1552
|
+
await writeJSON(configPath, data);
|
|
1503
1553
|
console.log(` \u2713 .mcp.json created in ${cwd}`);
|
|
1504
1554
|
} else {
|
|
1505
1555
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1506
1556
|
for (const agent of detected) {
|
|
1507
1557
|
const answer = await askQuestion(rl, ` Configure ${agent.name}? (Y/n) `);
|
|
1508
1558
|
if (answer.toLowerCase() !== "n") {
|
|
1509
|
-
const success = await
|
|
1559
|
+
const success = await agent.writeConfig(agent.configPath);
|
|
1510
1560
|
if (success) {
|
|
1511
1561
|
console.log(` \u2713 ${agent.name} \u2192 MCP configured`);
|
|
1512
1562
|
} else {
|
|
@@ -1530,6 +1580,156 @@ async function initCommand(options) {
|
|
|
1530
1580
|
console.log("");
|
|
1531
1581
|
}
|
|
1532
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
|
+
|
|
1533
1733
|
// src/cli/index.ts
|
|
1534
1734
|
var program = new Command();
|
|
1535
1735
|
program.name("make-laten").description("Universal efficiency toolkit for AI coding agents").version("1.0.3");
|
|
@@ -1545,5 +1745,6 @@ program.command("search").description("Search the web").argument("<query>", "Sea
|
|
|
1545
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);
|
|
1546
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);
|
|
1547
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);
|
|
1548
1749
|
program.parse();
|
|
1549
1750
|
//# sourceMappingURL=index.mjs.map
|