make-laten 1.1.1 → 1.2.1
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 +3 -3
- package/dist/cli/index.js +306 -65
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +306 -65
- 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 +1032 -75
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/server.mjs +1032 -75
- package/dist/mcp/server.mjs.map +1 -1
- package/package.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -52,29 +52,145 @@ function isKnownPattern(content) {
|
|
|
52
52
|
return patterns.some((p) => p.test(content));
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
// src/compress/strip.ts
|
|
56
|
+
function stripImports(content) {
|
|
57
|
+
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");
|
|
58
|
+
}
|
|
59
|
+
function stripComments(content) {
|
|
60
|
+
let result = content.replace(/(?<!["'`])\/\/.*$/gm, "");
|
|
61
|
+
result = result.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
62
|
+
return result.split("\n").filter((l) => l.trim() !== "").join("\n");
|
|
63
|
+
}
|
|
64
|
+
function stripBlankLines(content) {
|
|
65
|
+
return content.replace(/\n{3,}/g, "\n\n");
|
|
66
|
+
}
|
|
67
|
+
function stripWhitespace(content) {
|
|
68
|
+
return content.replace(/[ \t]+$/gm, "");
|
|
69
|
+
}
|
|
70
|
+
function stripAll(content) {
|
|
71
|
+
let result = content;
|
|
72
|
+
result = stripImports(result);
|
|
73
|
+
result = stripComments(result);
|
|
74
|
+
result = stripBlankLines(result);
|
|
75
|
+
result = stripWhitespace(result);
|
|
76
|
+
return result.trim();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/compress/structure.ts
|
|
80
|
+
function extractExports(content) {
|
|
81
|
+
const lines = content.split("\n");
|
|
82
|
+
const exports2 = [];
|
|
83
|
+
for (const line of lines) {
|
|
84
|
+
const trimmed = line.trim();
|
|
85
|
+
if (/^export\s+(default\s+)?/.test(trimmed)) {
|
|
86
|
+
if (trimmed.includes("{") && !trimmed.includes("}")) {
|
|
87
|
+
exports2.push(trimmed.replace(/\{[\s\S]*$/, "{ ... }"));
|
|
88
|
+
} else {
|
|
89
|
+
exports2.push(trimmed);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return exports2.join("\n");
|
|
94
|
+
}
|
|
95
|
+
function extractClassMethods(content) {
|
|
96
|
+
const classRegex = /class\s+\w+[^{]*\{([\s\S]*?)\n\}/g;
|
|
97
|
+
const methods = [];
|
|
98
|
+
let match;
|
|
99
|
+
while ((match = classRegex.exec(content)) !== null) {
|
|
100
|
+
const classBody = match[1];
|
|
101
|
+
const methodRegex = /(^\s*(?:public|private|protected|static|async|abstract|\s)*\w+\s*\([^)]*\)(?:\s*:\s*[^{]+)?)\s*\{/gm;
|
|
102
|
+
let methodMatch;
|
|
103
|
+
while ((methodMatch = methodRegex.exec(classBody)) !== null) {
|
|
104
|
+
methods.push(methodMatch[1].trim());
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return methods.join("\n");
|
|
108
|
+
}
|
|
109
|
+
function extractFunctionSignatures(content) {
|
|
110
|
+
const funcRegex = /(export\s+)?(async\s+)?function\s+\w+\s*\([^)]*\)(?:\s*:\s*[^{]+)?\s*\{/g;
|
|
111
|
+
const signatures = [];
|
|
112
|
+
let match;
|
|
113
|
+
while ((match = funcRegex.exec(content)) !== null) {
|
|
114
|
+
signatures.push(match[0].replace(/\{$/, ""));
|
|
115
|
+
}
|
|
116
|
+
return signatures.join("\n");
|
|
117
|
+
}
|
|
118
|
+
function extractTypeDefinitions(content) {
|
|
119
|
+
const typeRegex = /(export\s+)?(interface|type)\s+\w+[^{]*\{[^}]*\}|(export\s+)?type\s+\w+\s*=[^;]+;?/g;
|
|
120
|
+
const types = [];
|
|
121
|
+
let match;
|
|
122
|
+
while ((match = typeRegex.exec(content)) !== null) {
|
|
123
|
+
types.push(match[0].trim());
|
|
124
|
+
}
|
|
125
|
+
return types.join("\n");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/compress/truncate.ts
|
|
129
|
+
function smartTruncate(content, maxTokens) {
|
|
130
|
+
const lines = content.split("\n");
|
|
131
|
+
const estimatedTokens = Math.ceil(content.length / 4);
|
|
132
|
+
if (estimatedTokens <= maxTokens) return content;
|
|
133
|
+
const targetLines = Math.round(maxTokens * 4 / (content.length / lines.length));
|
|
134
|
+
const headerLines = Math.floor(targetLines * 0.3);
|
|
135
|
+
const footerLines = Math.floor(targetLines * 0.3);
|
|
136
|
+
const omitted = lines.length - headerLines - footerLines;
|
|
137
|
+
const header = lines.slice(0, headerLines);
|
|
138
|
+
const footer = lines.slice(-footerLines);
|
|
139
|
+
return [...header, `
|
|
140
|
+
... [${omitted} lines omitted] ...
|
|
141
|
+
`, ...footer].join("\n");
|
|
142
|
+
}
|
|
143
|
+
|
|
55
144
|
// src/compress/file-read.ts
|
|
56
145
|
var FileReadCompressor = class {
|
|
57
146
|
async compress(input) {
|
|
58
147
|
const { content, filePath, language } = input;
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
148
|
+
const lines = content.split("\n").length;
|
|
149
|
+
const tokens = Math.ceil(content.length / 4);
|
|
150
|
+
if (tokens < 200) {
|
|
151
|
+
return {
|
|
152
|
+
content,
|
|
153
|
+
original: content,
|
|
154
|
+
confidence: 1,
|
|
155
|
+
metadata: {
|
|
156
|
+
strategy: "passthrough",
|
|
157
|
+
originalLines: lines,
|
|
158
|
+
compressedLines: lines,
|
|
159
|
+
savings: 0
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const stripped = stripAll(content);
|
|
164
|
+
const exports2 = extractExports(stripped);
|
|
165
|
+
const classes = extractClassMethods(stripped);
|
|
166
|
+
const functions = extractFunctionSignatures(stripped);
|
|
167
|
+
const types = extractTypeDefinitions(stripped);
|
|
168
|
+
const sections = [
|
|
62
169
|
`// ${filePath} (${content.split("\n").length} lines)`,
|
|
63
170
|
"",
|
|
64
|
-
...signatures,
|
|
65
|
-
"",
|
|
66
171
|
"// Exports:",
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
172
|
+
exports2,
|
|
173
|
+
"",
|
|
174
|
+
"// Classes:",
|
|
175
|
+
classes,
|
|
176
|
+
"",
|
|
177
|
+
"// Functions:",
|
|
178
|
+
functions,
|
|
179
|
+
"",
|
|
180
|
+
"// Types:",
|
|
181
|
+
types
|
|
182
|
+
].filter((l) => l !== "" && !l.endsWith(":"));
|
|
183
|
+
let compressed = sections.join("\n");
|
|
184
|
+
const maxTokens = 2e3;
|
|
185
|
+
compressed = smartTruncate(compressed, maxTokens);
|
|
70
186
|
return {
|
|
71
187
|
content: compressed,
|
|
72
188
|
original: content,
|
|
73
189
|
confidence: calculateConfidence(content, compressed),
|
|
74
190
|
metadata: {
|
|
75
|
-
strategy: "
|
|
191
|
+
strategy: "hybrid",
|
|
76
192
|
originalLines: content.split("\n").length,
|
|
77
|
-
compressedLines:
|
|
193
|
+
compressedLines: compressed.split("\n").length,
|
|
78
194
|
savings: 1 - compressed.length / content.length
|
|
79
195
|
}
|
|
80
196
|
};
|
|
@@ -82,32 +198,6 @@ var FileReadCompressor = class {
|
|
|
82
198
|
async decompress(compressed) {
|
|
83
199
|
return compressed.original;
|
|
84
200
|
}
|
|
85
|
-
extractSignatures(content, language) {
|
|
86
|
-
const signatures = [];
|
|
87
|
-
const lines = content.split("\n");
|
|
88
|
-
for (const line of lines) {
|
|
89
|
-
const trimmed = line.trim();
|
|
90
|
-
if (/^(export\s+)?(async\s+)?function\s+\w+/.test(trimmed)) {
|
|
91
|
-
signatures.push(trimmed.replace(/\{[\s\S]*$/, "{ ... }"));
|
|
92
|
-
}
|
|
93
|
-
if (/^(export\s+)?class\s+\w+/.test(trimmed)) {
|
|
94
|
-
signatures.push(trimmed);
|
|
95
|
-
}
|
|
96
|
-
if (/^(export\s+)?(type|interface)\s+\w+/.test(trimmed)) {
|
|
97
|
-
signatures.push(trimmed);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return signatures;
|
|
101
|
-
}
|
|
102
|
-
extractExports(content) {
|
|
103
|
-
const exports2 = [];
|
|
104
|
-
const exportRegex = /export\s+(default\s+)?(function|class|const|let|var|type|interface)\s+(\w+)/g;
|
|
105
|
-
let match;
|
|
106
|
-
while ((match = exportRegex.exec(content)) !== null) {
|
|
107
|
-
exports2.push(` - ${match[3]} (${match[2]})`);
|
|
108
|
-
}
|
|
109
|
-
return exports2;
|
|
110
|
-
}
|
|
111
201
|
};
|
|
112
202
|
|
|
113
203
|
// src/compress/grep.ts
|
|
@@ -164,18 +254,21 @@ var GrepCompressor = class {
|
|
|
164
254
|
var GitDiffCompressor = class {
|
|
165
255
|
async compress(input) {
|
|
166
256
|
const { diff } = input;
|
|
167
|
-
const
|
|
168
|
-
const condensed = hunks.map((hunk) => ({
|
|
169
|
-
file: hunk.file,
|
|
170
|
-
changes: this.condenseHunk(hunk),
|
|
171
|
-
stats: { additions: hunk.additions, deletions: hunk.deletions }
|
|
172
|
-
}));
|
|
257
|
+
const files = this.parseDiff(diff);
|
|
173
258
|
const lines = [];
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
259
|
+
const totalAdd = files.reduce((s, f) => s + f.additions, 0);
|
|
260
|
+
const totalDel = files.reduce((s, f) => s + f.deletions, 0);
|
|
261
|
+
lines.push(`# ${files.length} files changed: +${totalAdd} -${totalDel}`);
|
|
262
|
+
lines.push("");
|
|
263
|
+
for (const file of files) {
|
|
264
|
+
lines.push(`${file.path} (+${file.additions} -${file.deletions})`);
|
|
265
|
+
const changes = file.lines.filter((l) => l.type === "add" || l.type === "del").map((l) => `${l.type === "add" ? "+" : "-"}${l.content}`);
|
|
266
|
+
const limited = changes.slice(0, 20);
|
|
267
|
+
lines.push(...limited);
|
|
268
|
+
if (changes.length > 20) {
|
|
269
|
+
lines.push(`... [${changes.length - 20} more changes]`);
|
|
178
270
|
}
|
|
271
|
+
lines.push("");
|
|
179
272
|
}
|
|
180
273
|
const compressed = lines.join("\n");
|
|
181
274
|
return {
|
|
@@ -184,7 +277,7 @@ var GitDiffCompressor = class {
|
|
|
184
277
|
confidence: calculateConfidence(diff, compressed),
|
|
185
278
|
metadata: {
|
|
186
279
|
strategy: "condensed",
|
|
187
|
-
filesChanged:
|
|
280
|
+
filesChanged: files.length,
|
|
188
281
|
savings: 1 - compressed.length / diff.length
|
|
189
282
|
}
|
|
190
283
|
};
|
|
@@ -193,46 +286,716 @@ var GitDiffCompressor = class {
|
|
|
193
286
|
return compressed.original;
|
|
194
287
|
}
|
|
195
288
|
parseDiff(diff) {
|
|
196
|
-
const
|
|
289
|
+
const files = [];
|
|
197
290
|
const lines = diff.split("\n");
|
|
198
|
-
let
|
|
291
|
+
let current = null;
|
|
199
292
|
for (const line of lines) {
|
|
200
293
|
if (line.startsWith("diff --git")) {
|
|
201
|
-
if (
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
lines: [],
|
|
294
|
+
if (current) files.push(current);
|
|
295
|
+
const match = line.match(/b\/(.+)/);
|
|
296
|
+
current = {
|
|
297
|
+
path: match?.[1] || "unknown",
|
|
206
298
|
additions: 0,
|
|
207
|
-
deletions: 0
|
|
299
|
+
deletions: 0,
|
|
300
|
+
lines: []
|
|
208
301
|
};
|
|
209
|
-
} else if (
|
|
302
|
+
} else if (current) {
|
|
210
303
|
if (line.startsWith("+")) {
|
|
211
|
-
|
|
212
|
-
|
|
304
|
+
current.additions++;
|
|
305
|
+
current.lines.push({ type: "add", content: line.slice(1) });
|
|
213
306
|
} else if (line.startsWith("-")) {
|
|
214
|
-
|
|
215
|
-
|
|
307
|
+
current.deletions++;
|
|
308
|
+
current.lines.push({ type: "del", content: line.slice(1) });
|
|
216
309
|
}
|
|
217
310
|
}
|
|
218
311
|
}
|
|
219
|
-
if (
|
|
220
|
-
return
|
|
312
|
+
if (current) files.push(current);
|
|
313
|
+
return files;
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// src/compress/git-status.ts
|
|
318
|
+
var GitStatusCompressor = class {
|
|
319
|
+
async compress(input) {
|
|
320
|
+
const { status } = input;
|
|
321
|
+
const statusLines = status.split("\n").filter(Boolean).length;
|
|
322
|
+
const tokens = Math.ceil(status.length / 4);
|
|
323
|
+
if (tokens < 10) {
|
|
324
|
+
return {
|
|
325
|
+
content: status,
|
|
326
|
+
original: status,
|
|
327
|
+
confidence: 1,
|
|
328
|
+
metadata: {
|
|
329
|
+
strategy: "passthrough",
|
|
330
|
+
totalFiles: statusLines,
|
|
331
|
+
savings: 0
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
const files = this.parseStatus(status);
|
|
336
|
+
const grouped = this.groupByStatus(files);
|
|
337
|
+
const lines = [];
|
|
338
|
+
for (const [statusType, statusFiles] of grouped) {
|
|
339
|
+
const label = this.statusLabel(statusType);
|
|
340
|
+
lines.push(`${label} (${statusFiles.length})`);
|
|
341
|
+
const compressed2 = this.compressPaths(statusFiles.map((f) => f.path));
|
|
342
|
+
lines.push(...compressed2);
|
|
343
|
+
lines.push("");
|
|
344
|
+
}
|
|
345
|
+
const compressed = lines.join("\n");
|
|
346
|
+
return {
|
|
347
|
+
content: compressed,
|
|
348
|
+
original: status,
|
|
349
|
+
confidence: calculateConfidence(status, compressed),
|
|
350
|
+
metadata: {
|
|
351
|
+
strategy: "grouped",
|
|
352
|
+
totalFiles: files.length,
|
|
353
|
+
savings: 1 - compressed.length / status.length
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
async decompress(compressed) {
|
|
358
|
+
return compressed.original;
|
|
359
|
+
}
|
|
360
|
+
parseStatus(status) {
|
|
361
|
+
return status.split("\n").filter(Boolean).map((line) => {
|
|
362
|
+
const match = line.match(/^(\?\?|[MADRC]+\s+)(.+)$/);
|
|
363
|
+
if (match) {
|
|
364
|
+
return { status: match[1].trim(), path: match[2] };
|
|
365
|
+
}
|
|
366
|
+
return { status: line[0], path: line.slice(3) };
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
groupByStatus(files) {
|
|
370
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
371
|
+
for (const file of files) {
|
|
372
|
+
const existing = grouped.get(file.status) || [];
|
|
373
|
+
existing.push(file);
|
|
374
|
+
grouped.set(file.status, existing);
|
|
375
|
+
}
|
|
376
|
+
return grouped;
|
|
377
|
+
}
|
|
378
|
+
statusLabel(status) {
|
|
379
|
+
const labels = {
|
|
380
|
+
"M": "Modified",
|
|
381
|
+
"A": "Added",
|
|
382
|
+
"D": "Deleted",
|
|
383
|
+
"??": "Untracked",
|
|
384
|
+
"R": "Renamed"
|
|
385
|
+
};
|
|
386
|
+
return labels[status] || `Status ${status}`;
|
|
387
|
+
}
|
|
388
|
+
compressPaths(paths) {
|
|
389
|
+
if (paths.length <= 3) return paths.map((p) => ` ${p}`);
|
|
390
|
+
const parts = paths.map((p) => p.split("/"));
|
|
391
|
+
const minLength = Math.min(...parts.map((p) => p.length));
|
|
392
|
+
let commonIndex = 0;
|
|
393
|
+
for (let i = 0; i < minLength; i++) {
|
|
394
|
+
if (parts.every((p) => p[i] === parts[0][i])) {
|
|
395
|
+
commonIndex = i;
|
|
396
|
+
} else {
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (commonIndex > 0) {
|
|
401
|
+
const prefix = parts[0].slice(0, commonIndex).join("/");
|
|
402
|
+
const suffixes = paths.map((p) => p.slice(prefix.length + 1));
|
|
403
|
+
return [` ${prefix}/`, ...suffixes.map((s) => ` ${s}`)];
|
|
404
|
+
}
|
|
405
|
+
return paths.map((p) => ` ${p}`);
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
// src/route/tool-router.ts
|
|
410
|
+
var ToolRouter = class {
|
|
411
|
+
rules = [
|
|
412
|
+
{
|
|
413
|
+
matcher: (input) => input.type === "file",
|
|
414
|
+
compressor: "file-read",
|
|
415
|
+
confidence: 0.95,
|
|
416
|
+
reason: "File read detected"
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
matcher: (input) => input.type === "grep",
|
|
420
|
+
compressor: "grep",
|
|
421
|
+
confidence: 0.95,
|
|
422
|
+
reason: "Grep results detected"
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
matcher: (input) => input.type === "git-diff",
|
|
426
|
+
compressor: "git-diff",
|
|
427
|
+
confidence: 0.95,
|
|
428
|
+
reason: "Git diff detected"
|
|
429
|
+
}
|
|
430
|
+
];
|
|
431
|
+
route(input) {
|
|
432
|
+
for (const rule of this.rules) {
|
|
433
|
+
if (rule.matcher(input)) {
|
|
434
|
+
return {
|
|
435
|
+
tool: "compress",
|
|
436
|
+
compressor: rule.compressor,
|
|
437
|
+
confidence: rule.confidence,
|
|
438
|
+
reason: rule.reason
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return {
|
|
443
|
+
tool: "compress",
|
|
444
|
+
confidence: 0.1,
|
|
445
|
+
reason: "No matching route found"
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
addRule(rule) {
|
|
449
|
+
this.rules.push(rule);
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
// src/route/strategy-router.ts
|
|
454
|
+
var StrategyRouter = class {
|
|
455
|
+
thresholds = {
|
|
456
|
+
small: 500,
|
|
457
|
+
large: 5e3
|
|
458
|
+
};
|
|
459
|
+
select(context) {
|
|
460
|
+
if (context.userPreference) {
|
|
461
|
+
return {
|
|
462
|
+
strategy: context.userPreference,
|
|
463
|
+
reason: `User preference: ${context.userPreference}`,
|
|
464
|
+
savings: this.estimateSavings(context.userPreference)
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
const fileSize = context.fileSize || 0;
|
|
468
|
+
if (fileSize >= this.thresholds.large) {
|
|
469
|
+
return {
|
|
470
|
+
strategy: "aggressive",
|
|
471
|
+
reason: `large file (${fileSize} bytes)`,
|
|
472
|
+
savings: 0.6
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
if (fileSize <= this.thresholds.small) {
|
|
476
|
+
return {
|
|
477
|
+
strategy: "conservative",
|
|
478
|
+
reason: `small file (${fileSize} bytes)`,
|
|
479
|
+
savings: 0.2
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
return {
|
|
483
|
+
strategy: "balanced",
|
|
484
|
+
reason: `Medium file (${fileSize} bytes)`,
|
|
485
|
+
savings: 0.4
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
estimateSavings(strategy) {
|
|
489
|
+
switch (strategy) {
|
|
490
|
+
case "aggressive":
|
|
491
|
+
return 0.6;
|
|
492
|
+
case "balanced":
|
|
493
|
+
return 0.4;
|
|
494
|
+
case "conservative":
|
|
495
|
+
return 0.2;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
// src/learn/pattern-miner.ts
|
|
501
|
+
var PatternMiner = class {
|
|
502
|
+
operations = [];
|
|
503
|
+
patterns = /* @__PURE__ */ new Map();
|
|
504
|
+
record(operation) {
|
|
505
|
+
this.operations.push(operation);
|
|
506
|
+
if (operation.success) {
|
|
507
|
+
this.updatePatterns(operation);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
getPatterns() {
|
|
511
|
+
return Array.from(this.patterns.values());
|
|
512
|
+
}
|
|
513
|
+
getPatternsByType(type) {
|
|
514
|
+
return this.getPatterns().filter((p) => p.type === type);
|
|
515
|
+
}
|
|
516
|
+
updatePatterns(operation) {
|
|
517
|
+
const key = operation.type;
|
|
518
|
+
const existing = this.patterns.get(key);
|
|
519
|
+
if (existing) {
|
|
520
|
+
existing.count++;
|
|
521
|
+
existing.confidence = Math.min(0.99, existing.confidence + 0.05);
|
|
522
|
+
} else {
|
|
523
|
+
this.patterns.set(key, {
|
|
524
|
+
id: `p${this.patterns.size + 1}`,
|
|
525
|
+
type: operation.type,
|
|
526
|
+
pattern: JSON.stringify(operation.input),
|
|
527
|
+
confidence: 0.5,
|
|
528
|
+
count: 1
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
// src/learn/failure-learner.ts
|
|
535
|
+
var FailureLearner = class {
|
|
536
|
+
failures = /* @__PURE__ */ new Map();
|
|
537
|
+
record(failure) {
|
|
538
|
+
const key = `${failure.type}:${failure.error}`;
|
|
539
|
+
const existing = this.failures.get(key);
|
|
540
|
+
if (existing) {
|
|
541
|
+
existing.count++;
|
|
542
|
+
} else {
|
|
543
|
+
this.failures.set(key, {
|
|
544
|
+
id: `f${this.failures.size + 1}`,
|
|
545
|
+
...failure,
|
|
546
|
+
timestamp: Date.now(),
|
|
547
|
+
count: 1
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
getFailures() {
|
|
552
|
+
return Array.from(this.failures.values());
|
|
553
|
+
}
|
|
554
|
+
getFailuresByType(type) {
|
|
555
|
+
return this.getFailures().filter((f) => f.type === type);
|
|
556
|
+
}
|
|
557
|
+
getSuggestions(type) {
|
|
558
|
+
const failures = Array.from(this.failures.values()).filter((f) => f.type === type);
|
|
559
|
+
const suggestions = [];
|
|
560
|
+
for (const failure of failures) {
|
|
561
|
+
if (failure.error.includes("not found")) {
|
|
562
|
+
suggestions.push("check if file exists before processing");
|
|
563
|
+
}
|
|
564
|
+
if (failure.error.includes("permission")) {
|
|
565
|
+
suggestions.push("verify file permissions");
|
|
566
|
+
}
|
|
567
|
+
if (failure.count > 2) {
|
|
568
|
+
suggestions.push(`recurring issue: ${failure.error}`);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return [...new Set(suggestions)];
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
// src/correct/auto-correct.ts
|
|
576
|
+
var AutoCorrect = class {
|
|
577
|
+
rules = [];
|
|
578
|
+
corrections = [];
|
|
579
|
+
appliedCount = 0;
|
|
580
|
+
addRule(rule) {
|
|
581
|
+
this.rules.push(rule);
|
|
582
|
+
}
|
|
583
|
+
getRules() {
|
|
584
|
+
return this.rules;
|
|
585
|
+
}
|
|
586
|
+
correct(input) {
|
|
587
|
+
let output = input;
|
|
588
|
+
for (const rule of this.rules) {
|
|
589
|
+
if (typeof rule.pattern === "string") {
|
|
590
|
+
if (output.includes(rule.pattern)) {
|
|
591
|
+
output = output.split(rule.pattern).join(rule.replacement);
|
|
592
|
+
this.recordCorrection(input, output, rule.name);
|
|
593
|
+
}
|
|
594
|
+
} else {
|
|
595
|
+
const matches = output.match(rule.pattern);
|
|
596
|
+
if (matches) {
|
|
597
|
+
output = output.replace(rule.pattern, rule.replacement);
|
|
598
|
+
this.recordCorrection(input, output, rule.name);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return output;
|
|
603
|
+
}
|
|
604
|
+
getStats() {
|
|
605
|
+
return {
|
|
606
|
+
applied: this.appliedCount,
|
|
607
|
+
corrections: this.corrections
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
recordCorrection(original, corrected, ruleName) {
|
|
611
|
+
this.appliedCount++;
|
|
612
|
+
this.corrections.push({
|
|
613
|
+
id: `c${this.corrections.length + 1}`,
|
|
614
|
+
original,
|
|
615
|
+
corrected,
|
|
616
|
+
rule: ruleName,
|
|
617
|
+
confidence: 0.9
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
// src/cache/l1-session.ts
|
|
623
|
+
var SessionCache = class {
|
|
624
|
+
store = /* @__PURE__ */ new Map();
|
|
625
|
+
hits = 0;
|
|
626
|
+
misses = 0;
|
|
627
|
+
get(key) {
|
|
628
|
+
const entry = this.store.get(key);
|
|
629
|
+
if (entry) {
|
|
630
|
+
this.hits++;
|
|
631
|
+
return entry;
|
|
632
|
+
}
|
|
633
|
+
this.misses++;
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
set(key, value) {
|
|
637
|
+
this.store.set(key, value);
|
|
638
|
+
}
|
|
639
|
+
clear() {
|
|
640
|
+
this.store.clear();
|
|
641
|
+
this.hits = 0;
|
|
642
|
+
this.misses = 0;
|
|
643
|
+
}
|
|
644
|
+
stats() {
|
|
645
|
+
const total = this.hits + this.misses;
|
|
646
|
+
return {
|
|
647
|
+
hits: this.hits,
|
|
648
|
+
misses: this.misses,
|
|
649
|
+
size: this.store.size,
|
|
650
|
+
hitRate: total > 0 ? this.hits / total : 0
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
// src/web/backends/duckduckgo.ts
|
|
656
|
+
var DuckDuckGoBackend = class {
|
|
657
|
+
name = "duckduckgo";
|
|
658
|
+
isAvailable() {
|
|
659
|
+
return true;
|
|
660
|
+
}
|
|
661
|
+
async search(query, options) {
|
|
662
|
+
const maxResults = options?.maxResults || 5;
|
|
663
|
+
const url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
|
|
664
|
+
const response = await fetch(url, {
|
|
665
|
+
headers: {
|
|
666
|
+
"User-Agent": "Mozilla/5.0 (compatible; make-laten/0.1.0)"
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
const html = await response.text();
|
|
670
|
+
return this.parseResults(html, maxResults);
|
|
671
|
+
}
|
|
672
|
+
parseResults(html, maxResults) {
|
|
673
|
+
const results = [];
|
|
674
|
+
const resultRegex = /<a[^>]*class="result__a"[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
675
|
+
const snippetRegex = /<a[^>]*class="result__snippet"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
676
|
+
let match;
|
|
677
|
+
const urls = [];
|
|
678
|
+
const titles = [];
|
|
679
|
+
while ((match = resultRegex.exec(html)) !== null && urls.length < maxResults) {
|
|
680
|
+
const rawUrl = match[1];
|
|
681
|
+
const urlMatch = rawUrl.match(/uddg=([^&]+)/);
|
|
682
|
+
const url = urlMatch ? decodeURIComponent(urlMatch[1]) : rawUrl;
|
|
683
|
+
const title = this.stripTags(match[2]).trim();
|
|
684
|
+
if (url && title) {
|
|
685
|
+
urls.push(url);
|
|
686
|
+
titles.push(title);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
const snippets = [];
|
|
690
|
+
while ((match = snippetRegex.exec(html)) !== null && snippets.length < maxResults) {
|
|
691
|
+
snippets.push(this.stripTags(match[1]).trim());
|
|
692
|
+
}
|
|
693
|
+
for (let i = 0; i < urls.length; i++) {
|
|
694
|
+
results.push({
|
|
695
|
+
title: titles[i],
|
|
696
|
+
url: urls[i],
|
|
697
|
+
snippet: snippets[i] || "",
|
|
698
|
+
score: 1 - i * 0.1
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
return results;
|
|
221
702
|
}
|
|
222
|
-
|
|
223
|
-
return
|
|
703
|
+
stripTags(html) {
|
|
704
|
+
return html.replace(/<[^>]+>/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/'/g, "'").replace(/"/g, '"');
|
|
224
705
|
}
|
|
225
706
|
};
|
|
226
707
|
|
|
708
|
+
// src/web/backends/index.ts
|
|
709
|
+
function selectBackend(preferred) {
|
|
710
|
+
const backends = [
|
|
711
|
+
new DuckDuckGoBackend()
|
|
712
|
+
];
|
|
713
|
+
if (preferred) {
|
|
714
|
+
const found = backends.find((b) => b.name === preferred);
|
|
715
|
+
if (found) return found;
|
|
716
|
+
}
|
|
717
|
+
return backends.find((b) => b.isAvailable()) || backends[0];
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// src/web/extractor.ts
|
|
721
|
+
function stripTags(html) {
|
|
722
|
+
return html.replace(/<[^>]+>/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'");
|
|
723
|
+
}
|
|
724
|
+
function extractTitle(html) {
|
|
725
|
+
const titleMatch = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
726
|
+
if (titleMatch) return stripTags(titleMatch[1]).trim();
|
|
727
|
+
const h1Match = html.match(/<h1[^>]*>([\s\S]*?)<\/h1>/i);
|
|
728
|
+
if (h1Match) return stripTags(h1Match[1]).trim();
|
|
729
|
+
return "Untitled";
|
|
730
|
+
}
|
|
731
|
+
function extractSections(html) {
|
|
732
|
+
const sections = [];
|
|
733
|
+
const headingRegex = /<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi;
|
|
734
|
+
let match;
|
|
735
|
+
while ((match = headingRegex.exec(html)) !== null) {
|
|
736
|
+
const level = parseInt(match[1]);
|
|
737
|
+
const heading = stripTags(match[2]).trim();
|
|
738
|
+
const startPos = match.index + match[0].length;
|
|
739
|
+
const nextHeading = html.substring(startPos).match(/<h[1-6]/i);
|
|
740
|
+
const endPos = nextHeading ? startPos + nextHeading.index : html.length;
|
|
741
|
+
const content = stripTags(html.substring(startPos, endPos)).trim();
|
|
742
|
+
if (!content) continue;
|
|
743
|
+
const importance = level <= 2 ? "primary" : level <= 4 ? "secondary" : "tertiary";
|
|
744
|
+
sections.push({ heading, level, content, importance });
|
|
745
|
+
}
|
|
746
|
+
return sections;
|
|
747
|
+
}
|
|
748
|
+
function extractCodeExamples(html) {
|
|
749
|
+
const examples = [];
|
|
750
|
+
const codeRegex = /<(?:pre|code)[^>]*>[\s\S]*?<(?:code|\/pre)[^>]*>/gi;
|
|
751
|
+
const blockRegex = /<pre[^>]*><code[^>]*class="[^"]*language-(\w+)[^"]*"[^>]*>([\s\S]*?)<\/code><\/pre>/gi;
|
|
752
|
+
let match;
|
|
753
|
+
while ((match = blockRegex.exec(html)) !== null) {
|
|
754
|
+
const language = match[1];
|
|
755
|
+
const code = stripTags(match[2]).trim();
|
|
756
|
+
if (code.length > 10) {
|
|
757
|
+
examples.push({ language, code });
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
const inlineRegex = /<code[^>]*class="[^"]*language-(\w+)[^"]*"[^>]*>([\s\S]*?)<\/code>/gi;
|
|
761
|
+
while ((match = inlineRegex.exec(html)) !== null) {
|
|
762
|
+
const language = match[1];
|
|
763
|
+
const code = stripTags(match[2]).trim();
|
|
764
|
+
if (code.length > 20 && !examples.some((e) => e.code === code)) {
|
|
765
|
+
examples.push({ language, code });
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
return examples.slice(0, 10);
|
|
769
|
+
}
|
|
770
|
+
function extractKeyPoints(sections) {
|
|
771
|
+
const keyPoints = [];
|
|
772
|
+
for (const section of sections.filter((s) => s.importance === "primary")) {
|
|
773
|
+
const sentences = section.content.split(/[.!?\n]+/).filter((s) => s.trim().length > 10);
|
|
774
|
+
if (sentences.length > 0) {
|
|
775
|
+
keyPoints.push(sentences[0].trim());
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return keyPoints.slice(0, 5);
|
|
779
|
+
}
|
|
780
|
+
function classifyPurpose(html, sections) {
|
|
781
|
+
const text = (html + " " + sections.map((s) => s.heading).join(" ")).toLowerCase();
|
|
782
|
+
if (text.includes("api reference") || text.includes("endpoint") || text.includes("parameters")) return "reference";
|
|
783
|
+
if (text.includes("tutorial") || text.includes("step by step") || text.includes("getting started")) return "tutorial";
|
|
784
|
+
if (text.includes("blog") || text.includes("published") || text.includes("author")) return "blog";
|
|
785
|
+
if (text.includes("install") || text.includes("quickstart") || text.includes("overview")) return "documentation";
|
|
786
|
+
return "documentation";
|
|
787
|
+
}
|
|
788
|
+
function extractSemantic(html, url) {
|
|
789
|
+
const title = extractTitle(html);
|
|
790
|
+
const sections = extractSections(html);
|
|
791
|
+
const codeExamples = extractCodeExamples(html);
|
|
792
|
+
const keyPoints = extractKeyPoints(sections);
|
|
793
|
+
const purpose = classifyPurpose(html, sections);
|
|
794
|
+
return {
|
|
795
|
+
type: "webpage",
|
|
796
|
+
url,
|
|
797
|
+
title,
|
|
798
|
+
purpose,
|
|
799
|
+
sections,
|
|
800
|
+
keyPoints,
|
|
801
|
+
codeExamples,
|
|
802
|
+
metadata: {
|
|
803
|
+
language: html.match(/lang="(\w+)"/)?.[1] || "en",
|
|
804
|
+
author: html.match(/<meta[^>]*name="author"[^>]*content="([^"]+)"/i)?.[1],
|
|
805
|
+
tags: html.match(/<meta[^>]*name="keywords"[^>]*content="([^"]+)"/i)?.[1]?.split(",").map((t) => t.trim())
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// src/web/compressor.ts
|
|
811
|
+
function compressWebContent(semantic, options = {}) {
|
|
812
|
+
const { maxTokens = 1e3, includeCode = true, includeSections = "all" } = options;
|
|
813
|
+
const lines = [];
|
|
814
|
+
lines.push(`# ${semantic.title}`);
|
|
815
|
+
lines.push(`URL: ${semantic.url}`);
|
|
816
|
+
lines.push(`Purpose: ${semantic.purpose}`);
|
|
817
|
+
lines.push("");
|
|
818
|
+
if (semantic.keyPoints.length > 0) {
|
|
819
|
+
lines.push("## Key Points");
|
|
820
|
+
for (const point of semantic.keyPoints) {
|
|
821
|
+
lines.push(`- ${point}`);
|
|
822
|
+
}
|
|
823
|
+
lines.push("");
|
|
824
|
+
}
|
|
825
|
+
const filteredSections = filterSections(semantic.sections, includeSections);
|
|
826
|
+
for (const section of filteredSections) {
|
|
827
|
+
const content = truncateContent(section.content, 500);
|
|
828
|
+
lines.push(`## ${section.heading}`);
|
|
829
|
+
lines.push(content);
|
|
830
|
+
lines.push("");
|
|
831
|
+
}
|
|
832
|
+
if (includeCode && semantic.codeExamples.length > 0) {
|
|
833
|
+
const topExamples = semantic.codeExamples.slice(0, 3);
|
|
834
|
+
lines.push("## Code Examples");
|
|
835
|
+
for (const example of topExamples) {
|
|
836
|
+
lines.push(`\`\`\`${example.language}`);
|
|
837
|
+
lines.push(truncateContent(example.code, 300));
|
|
838
|
+
lines.push("```");
|
|
839
|
+
lines.push("");
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
let result = lines.join("\n");
|
|
843
|
+
const estimatedTokens = estimateTokens(result);
|
|
844
|
+
if (estimatedTokens > maxTokens) {
|
|
845
|
+
result = truncateByTokens(result, maxTokens);
|
|
846
|
+
}
|
|
847
|
+
return result;
|
|
848
|
+
}
|
|
849
|
+
function filterSections(sections, mode) {
|
|
850
|
+
if (mode === "none") return [];
|
|
851
|
+
if (mode === "primary") return sections.filter((s) => s.importance === "primary");
|
|
852
|
+
return sections;
|
|
853
|
+
}
|
|
854
|
+
function truncateContent(content, maxLength) {
|
|
855
|
+
if (content.length <= maxLength) return content;
|
|
856
|
+
return content.slice(0, maxLength) + "...";
|
|
857
|
+
}
|
|
858
|
+
function estimateTokens(text) {
|
|
859
|
+
return Math.ceil(text.length / 4);
|
|
860
|
+
}
|
|
861
|
+
function truncateByTokens(text, maxTokens) {
|
|
862
|
+
const maxChars = maxTokens * 4;
|
|
863
|
+
if (text.length <= maxChars) return text;
|
|
864
|
+
const truncated = text.slice(0, maxChars);
|
|
865
|
+
const lastNewline = truncated.lastIndexOf("\n");
|
|
866
|
+
return truncated.slice(0, lastNewline > 0 ? lastNewline : maxChars) + "\n\n... (truncated)";
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// src/web/router.ts
|
|
870
|
+
var WebRouter = class {
|
|
871
|
+
fetchCache = /* @__PURE__ */ new Map();
|
|
872
|
+
searchCache = /* @__PURE__ */ new Map();
|
|
873
|
+
cacheTtl;
|
|
874
|
+
constructor(options) {
|
|
875
|
+
this.cacheTtl = options?.cacheTtlMs || 60 * 60 * 1e3;
|
|
876
|
+
}
|
|
877
|
+
async search(query, options) {
|
|
878
|
+
const cacheKey = `search:${query}:${JSON.stringify(options || {})}`;
|
|
879
|
+
const cached = this.searchCache.get(cacheKey);
|
|
880
|
+
if (cached && this.isFresh(cacheKey)) {
|
|
881
|
+
return cached;
|
|
882
|
+
}
|
|
883
|
+
const backend = selectBackend(options?.backend);
|
|
884
|
+
const results = await backend.search(query, options);
|
|
885
|
+
this.searchCache.set(cacheKey, results);
|
|
886
|
+
this.storeFreshness(cacheKey);
|
|
887
|
+
return results;
|
|
888
|
+
}
|
|
889
|
+
async fetch(url, options) {
|
|
890
|
+
const cacheKey = `fetch:${url}:${options?.format || "text"}`;
|
|
891
|
+
if (options?.cache !== false) {
|
|
892
|
+
const cached = this.fetchCache.get(cacheKey);
|
|
893
|
+
if (cached && this.isFresh(cacheKey)) {
|
|
894
|
+
return cached;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
const startTime = Date.now();
|
|
898
|
+
const response = await fetch(url);
|
|
899
|
+
const html = await response.text();
|
|
900
|
+
const originalSize = html.length;
|
|
901
|
+
let content;
|
|
902
|
+
let semantic = void 0;
|
|
903
|
+
if (options?.extract !== false) {
|
|
904
|
+
semantic = extractSemantic(html, url);
|
|
905
|
+
content = compressWebContent(semantic, {
|
|
906
|
+
includeCode: true,
|
|
907
|
+
includeSections: options?.format === "html" ? "all" : "primary"
|
|
908
|
+
});
|
|
909
|
+
} else {
|
|
910
|
+
content = html;
|
|
911
|
+
}
|
|
912
|
+
const compressedSize = content.length;
|
|
913
|
+
const result = {
|
|
914
|
+
url,
|
|
915
|
+
title: semantic?.title || extractSimpleTitle(html),
|
|
916
|
+
content,
|
|
917
|
+
semantic,
|
|
918
|
+
metadata: {
|
|
919
|
+
fetchTime: Date.now() - startTime,
|
|
920
|
+
originalSize,
|
|
921
|
+
compressedSize,
|
|
922
|
+
savings: 1 - compressedSize / originalSize
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
if (options?.cache !== false) {
|
|
926
|
+
this.fetchCache.set(cacheKey, result);
|
|
927
|
+
this.storeFreshness(cacheKey);
|
|
928
|
+
}
|
|
929
|
+
return result;
|
|
930
|
+
}
|
|
931
|
+
async extract(url) {
|
|
932
|
+
return this.fetch(url, { extract: true, compress: true });
|
|
933
|
+
}
|
|
934
|
+
async summarize(content, options) {
|
|
935
|
+
const lines = content.split("\n");
|
|
936
|
+
const summaryLines = [];
|
|
937
|
+
let tokenCount = 0;
|
|
938
|
+
const maxTokens = options?.maxTokens || 200;
|
|
939
|
+
for (const line of lines) {
|
|
940
|
+
const lineTokens = Math.ceil(line.length / 4);
|
|
941
|
+
if (tokenCount + lineTokens > maxTokens) break;
|
|
942
|
+
if (line.startsWith("#") || line.startsWith("-") || line.startsWith("*")) {
|
|
943
|
+
summaryLines.push(line);
|
|
944
|
+
tokenCount += lineTokens;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
if (summaryLines.length === 0 && lines.length > 0) {
|
|
948
|
+
for (const line of lines.slice(0, 5)) {
|
|
949
|
+
const lineTokens = Math.ceil(line.length / 4);
|
|
950
|
+
if (tokenCount + lineTokens > maxTokens) break;
|
|
951
|
+
summaryLines.push(line);
|
|
952
|
+
tokenCount += lineTokens;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
return summaryLines.join("\n");
|
|
956
|
+
}
|
|
957
|
+
clearCache() {
|
|
958
|
+
this.fetchCache.clear();
|
|
959
|
+
this.searchCache.clear();
|
|
960
|
+
}
|
|
961
|
+
getCacheStats() {
|
|
962
|
+
return {
|
|
963
|
+
fetchSize: this.fetchCache.size,
|
|
964
|
+
searchSize: this.searchCache.size
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
isFresh(key) {
|
|
968
|
+
const timestamp = this.freshness.get(key);
|
|
969
|
+
if (!timestamp) return false;
|
|
970
|
+
return Date.now() - timestamp < this.cacheTtl;
|
|
971
|
+
}
|
|
972
|
+
storeFreshness(key) {
|
|
973
|
+
this.freshness.set(key, Date.now());
|
|
974
|
+
}
|
|
975
|
+
freshness = /* @__PURE__ */ new Map();
|
|
976
|
+
};
|
|
977
|
+
function extractSimpleTitle(html) {
|
|
978
|
+
const titleMatch = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
979
|
+
return titleMatch ? titleMatch[1].replace(/<[^>]+>/g, "").trim() : "Untitled";
|
|
980
|
+
}
|
|
981
|
+
|
|
227
982
|
// src/mcp/server.ts
|
|
228
983
|
var import_child_process = require("child_process");
|
|
229
984
|
var import_util = require("util");
|
|
230
985
|
var import_promises = __toESM(require("fs/promises"));
|
|
231
986
|
var execAsync = (0, import_util.promisify)(import_child_process.exec);
|
|
987
|
+
var sessionCache = new SessionCache();
|
|
988
|
+
var toolRouter = new ToolRouter();
|
|
989
|
+
var strategyRouter = new StrategyRouter();
|
|
990
|
+
var patternMiner = new PatternMiner();
|
|
991
|
+
var failureLearner = new FailureLearner();
|
|
992
|
+
var autoCorrect = new AutoCorrect();
|
|
993
|
+
var webRouter = new WebRouter();
|
|
232
994
|
var TOOLS = [
|
|
995
|
+
// Compress Layer
|
|
233
996
|
{
|
|
234
997
|
name: "make-laten-read",
|
|
235
|
-
description: "Read and compress a file (
|
|
998
|
+
description: "Read and compress a file (65-92% token savings)",
|
|
236
999
|
inputSchema: {
|
|
237
1000
|
type: "object",
|
|
238
1001
|
properties: {
|
|
@@ -268,10 +1031,127 @@ var TOOLS = [
|
|
|
268
1031
|
description: "Compressed git status",
|
|
269
1032
|
inputSchema: { type: "object", properties: {} }
|
|
270
1033
|
},
|
|
1034
|
+
// Route Layer
|
|
1035
|
+
{
|
|
1036
|
+
name: "make-laten-route",
|
|
1037
|
+
description: "Route input to correct compressor",
|
|
1038
|
+
inputSchema: {
|
|
1039
|
+
type: "object",
|
|
1040
|
+
properties: {
|
|
1041
|
+
type: { type: "string", enum: ["file", "grep", "git-diff", "unknown"], description: "Input type" },
|
|
1042
|
+
content: { type: "string", description: "Input content" }
|
|
1043
|
+
},
|
|
1044
|
+
required: ["type", "content"]
|
|
1045
|
+
}
|
|
1046
|
+
},
|
|
1047
|
+
{
|
|
1048
|
+
name: "make-laten-strategy",
|
|
1049
|
+
description: "Select compression strategy based on context",
|
|
1050
|
+
inputSchema: {
|
|
1051
|
+
type: "object",
|
|
1052
|
+
properties: {
|
|
1053
|
+
file_size: { type: "number", description: "File size in bytes" },
|
|
1054
|
+
preference: { type: "string", enum: ["aggressive", "balanced", "conservative"], description: "Strategy preference" }
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
},
|
|
1058
|
+
// Cache Layer
|
|
271
1059
|
{
|
|
272
1060
|
name: "make-laten-cache-stats",
|
|
273
1061
|
description: "Show cache performance stats",
|
|
274
1062
|
inputSchema: { type: "object", properties: {} }
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
name: "make-laten-cache-get",
|
|
1066
|
+
description: "Get value from session cache",
|
|
1067
|
+
inputSchema: {
|
|
1068
|
+
type: "object",
|
|
1069
|
+
properties: {
|
|
1070
|
+
key: { type: "string", description: "Cache key" }
|
|
1071
|
+
},
|
|
1072
|
+
required: ["key"]
|
|
1073
|
+
}
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
name: "make-laten-cache-set",
|
|
1077
|
+
description: "Set value in session cache",
|
|
1078
|
+
inputSchema: {
|
|
1079
|
+
type: "object",
|
|
1080
|
+
properties: {
|
|
1081
|
+
key: { type: "string", description: "Cache key" },
|
|
1082
|
+
value: { type: "string", description: "Value to cache" }
|
|
1083
|
+
},
|
|
1084
|
+
required: ["key", "value"]
|
|
1085
|
+
}
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
name: "make-laten-cache-clear",
|
|
1089
|
+
description: "Clear session cache",
|
|
1090
|
+
inputSchema: { type: "object", properties: {} }
|
|
1091
|
+
},
|
|
1092
|
+
// Learn Layer
|
|
1093
|
+
{
|
|
1094
|
+
name: "make-laten-patterns",
|
|
1095
|
+
description: "Get learned patterns from usage",
|
|
1096
|
+
inputSchema: { type: "object", properties: {} }
|
|
1097
|
+
},
|
|
1098
|
+
{
|
|
1099
|
+
name: "make-laten-failures",
|
|
1100
|
+
description: "Get failure records and suggestions",
|
|
1101
|
+
inputSchema: { type: "object", properties: {} }
|
|
1102
|
+
},
|
|
1103
|
+
{
|
|
1104
|
+
name: "make-laten-suggestions",
|
|
1105
|
+
description: "Get suggestions based on learned patterns",
|
|
1106
|
+
inputSchema: {
|
|
1107
|
+
type: "object",
|
|
1108
|
+
properties: {
|
|
1109
|
+
context: { type: "string", description: "Context for suggestions" }
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
},
|
|
1113
|
+
// Correct Layer
|
|
1114
|
+
{
|
|
1115
|
+
name: "make-laten-correct",
|
|
1116
|
+
description: "Apply auto-corrections to text",
|
|
1117
|
+
inputSchema: {
|
|
1118
|
+
type: "object",
|
|
1119
|
+
properties: {
|
|
1120
|
+
text: { type: "string", description: "Text to correct" }
|
|
1121
|
+
},
|
|
1122
|
+
required: ["text"]
|
|
1123
|
+
}
|
|
1124
|
+
},
|
|
1125
|
+
// Web Layer
|
|
1126
|
+
{
|
|
1127
|
+
name: "make-laten-search",
|
|
1128
|
+
description: "Search the web with semantic understanding",
|
|
1129
|
+
inputSchema: {
|
|
1130
|
+
type: "object",
|
|
1131
|
+
properties: {
|
|
1132
|
+
query: { type: "string", description: "Search query" },
|
|
1133
|
+
backend: { type: "string", description: "Search backend (duckduckgo, exa, tavily)" }
|
|
1134
|
+
},
|
|
1135
|
+
required: ["query"]
|
|
1136
|
+
}
|
|
1137
|
+
},
|
|
1138
|
+
{
|
|
1139
|
+
name: "make-laten-fetch",
|
|
1140
|
+
description: "Fetch and compress web content",
|
|
1141
|
+
inputSchema: {
|
|
1142
|
+
type: "object",
|
|
1143
|
+
properties: {
|
|
1144
|
+
url: { type: "string", description: "URL to fetch" },
|
|
1145
|
+
extract: { type: "boolean", description: "Extract semantic content", default: true }
|
|
1146
|
+
},
|
|
1147
|
+
required: ["url"]
|
|
1148
|
+
}
|
|
1149
|
+
},
|
|
1150
|
+
// Tool Layer
|
|
1151
|
+
{
|
|
1152
|
+
name: "make-laten-tools",
|
|
1153
|
+
description: "List available make-laten tools",
|
|
1154
|
+
inputSchema: { type: "object", properties: {} }
|
|
275
1155
|
}
|
|
276
1156
|
];
|
|
277
1157
|
function detectLanguage(filePath) {
|
|
@@ -301,9 +1181,9 @@ async function handleRead(params) {
|
|
|
301
1181
|
filePath: params.file_path,
|
|
302
1182
|
language: detectLanguage(params.file_path)
|
|
303
1183
|
});
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
};
|
|
1184
|
+
patternMiner.record({ type: "file-read", input: { file: params.file_path }, success: true });
|
|
1185
|
+
sessionCache.set(`file:${params.file_path}`, { content: result.content, metadata: result.metadata });
|
|
1186
|
+
return { content: [{ type: "text", text: JSON.stringify({ compressed: result.content, savings: result.metadata.savings, confidence: result.confidence }) }] };
|
|
307
1187
|
}
|
|
308
1188
|
async function handleGrep(params) {
|
|
309
1189
|
const dir = params.path || ".";
|
|
@@ -317,6 +1197,7 @@ async function handleGrep(params) {
|
|
|
317
1197
|
});
|
|
318
1198
|
const compressor = new GrepCompressor();
|
|
319
1199
|
const result = await compressor.compress({ results: matches, pattern: params.pattern, directory: dir });
|
|
1200
|
+
patternMiner.record({ type: "grep", input: { pattern: params.pattern, dir }, success: true });
|
|
320
1201
|
return { content: [{ type: "text", text: JSON.stringify({ compressed: result.content, total: matches.length, savings: result.metadata.savings }) }] };
|
|
321
1202
|
}
|
|
322
1203
|
async function handleGitDiff(params) {
|
|
@@ -327,23 +1208,98 @@ async function handleGitDiff(params) {
|
|
|
327
1208
|
}
|
|
328
1209
|
const compressor = new GitDiffCompressor();
|
|
329
1210
|
const result = await compressor.compress({ diff: stdout });
|
|
1211
|
+
patternMiner.record({ type: "git-diff", input: { staged: params.staged }, success: true });
|
|
330
1212
|
return { content: [{ type: "text", text: JSON.stringify({ compressed: result.content, savings: result.metadata.savings }) }] };
|
|
331
1213
|
}
|
|
332
1214
|
async function handleGitStatus() {
|
|
333
1215
|
const { stdout } = await execAsync("git status --porcelain");
|
|
334
|
-
const
|
|
335
|
-
const
|
|
336
|
-
return { content: [{ type: "text", text: JSON.stringify({
|
|
1216
|
+
const compressor = new GitStatusCompressor();
|
|
1217
|
+
const result = await compressor.compress({ status: stdout });
|
|
1218
|
+
return { content: [{ type: "text", text: JSON.stringify({ compressed: result.content, savings: result.metadata.savings }) }] };
|
|
1219
|
+
}
|
|
1220
|
+
async function handleRoute(params) {
|
|
1221
|
+
const route = toolRouter.route({ type: params.type, content: params.content });
|
|
1222
|
+
return { content: [{ type: "text", text: JSON.stringify(route) }] };
|
|
1223
|
+
}
|
|
1224
|
+
async function handleStrategy(params) {
|
|
1225
|
+
const strategy = strategyRouter.select({
|
|
1226
|
+
fileSize: params.file_size,
|
|
1227
|
+
userPreference: params.preference
|
|
1228
|
+
});
|
|
1229
|
+
return { content: [{ type: "text", text: JSON.stringify(strategy) }] };
|
|
337
1230
|
}
|
|
338
1231
|
async function handleCacheStats() {
|
|
339
|
-
|
|
1232
|
+
const stats = sessionCache.stats();
|
|
1233
|
+
return { content: [{ type: "text", text: JSON.stringify(stats) }] };
|
|
1234
|
+
}
|
|
1235
|
+
async function handleCacheGet(params) {
|
|
1236
|
+
const entry = sessionCache.get(params.key);
|
|
1237
|
+
return { content: [{ type: "text", text: JSON.stringify({ found: !!entry, entry }) }] };
|
|
1238
|
+
}
|
|
1239
|
+
async function handleCacheSet(params) {
|
|
1240
|
+
sessionCache.set(params.key, { content: params.value, metadata: { setAt: Date.now() } });
|
|
1241
|
+
return { content: [{ type: "text", text: JSON.stringify({ success: true }) }] };
|
|
1242
|
+
}
|
|
1243
|
+
async function handleCacheClear() {
|
|
1244
|
+
sessionCache.clear();
|
|
1245
|
+
return { content: [{ type: "text", text: JSON.stringify({ success: true }) }] };
|
|
1246
|
+
}
|
|
1247
|
+
async function handlePatterns() {
|
|
1248
|
+
const patterns = patternMiner.getPatterns();
|
|
1249
|
+
return { content: [{ type: "text", text: JSON.stringify({ patterns, total: patterns.length }) }] };
|
|
1250
|
+
}
|
|
1251
|
+
async function handleFailures() {
|
|
1252
|
+
const failures = failureLearner.getFailures();
|
|
1253
|
+
return { content: [{ type: "text", text: JSON.stringify({ failures, total: failures.length }) }] };
|
|
1254
|
+
}
|
|
1255
|
+
async function handleSuggestions(params) {
|
|
1256
|
+
const patterns = patternMiner.getPatterns();
|
|
1257
|
+
const failures = failureLearner.getFailures();
|
|
1258
|
+
const suggestions = [];
|
|
1259
|
+
for (const pattern of patterns.slice(0, 5)) {
|
|
1260
|
+
suggestions.push(`Pattern: ${pattern.type} (confidence: ${pattern.confidence})`);
|
|
1261
|
+
}
|
|
1262
|
+
for (const failure of failures.slice(0, 5)) {
|
|
1263
|
+
const failureSuggestions = failureLearner.getSuggestions(failure.type);
|
|
1264
|
+
suggestions.push(...failureSuggestions);
|
|
1265
|
+
}
|
|
1266
|
+
return { content: [{ type: "text", text: JSON.stringify({ suggestions: [...new Set(suggestions)] }) }] };
|
|
1267
|
+
}
|
|
1268
|
+
async function handleCorrect(params) {
|
|
1269
|
+
const corrected = autoCorrect.correct(params.text);
|
|
1270
|
+
const stats = autoCorrect.getStats();
|
|
1271
|
+
return { content: [{ type: "text", text: JSON.stringify({ original: params.text, corrected, corrections: stats.applied }) }] };
|
|
1272
|
+
}
|
|
1273
|
+
async function handleSearch(params) {
|
|
1274
|
+
const results = await webRouter.search(params.query, { backend: params.backend });
|
|
1275
|
+
return { content: [{ type: "text", text: JSON.stringify({ results, total: results.length }) }] };
|
|
1276
|
+
}
|
|
1277
|
+
async function handleFetch(params) {
|
|
1278
|
+
const result = await webRouter.fetch(params.url, { extract: params.extract !== false });
|
|
1279
|
+
return { content: [{ type: "text", text: JSON.stringify({ url: result.url, title: result.title, content: result.content, savings: result.metadata.savings }) }] };
|
|
1280
|
+
}
|
|
1281
|
+
async function handleTools() {
|
|
1282
|
+
const tools = TOOLS.map((t) => ({ name: t.name, description: t.description }));
|
|
1283
|
+
return { content: [{ type: "text", text: JSON.stringify({ tools, total: tools.length }) }] };
|
|
340
1284
|
}
|
|
341
1285
|
var handlers = {
|
|
342
1286
|
"make-laten-read": handleRead,
|
|
343
1287
|
"make-laten-grep": handleGrep,
|
|
344
1288
|
"make-laten-git-diff": handleGitDiff,
|
|
345
1289
|
"make-laten-git-status": handleGitStatus,
|
|
346
|
-
"make-laten-
|
|
1290
|
+
"make-laten-route": handleRoute,
|
|
1291
|
+
"make-laten-strategy": handleStrategy,
|
|
1292
|
+
"make-laten-cache-stats": handleCacheStats,
|
|
1293
|
+
"make-laten-cache-get": handleCacheGet,
|
|
1294
|
+
"make-laten-cache-set": handleCacheSet,
|
|
1295
|
+
"make-laten-cache-clear": handleCacheClear,
|
|
1296
|
+
"make-laten-patterns": handlePatterns,
|
|
1297
|
+
"make-laten-failures": handleFailures,
|
|
1298
|
+
"make-laten-suggestions": handleSuggestions,
|
|
1299
|
+
"make-laten-correct": handleCorrect,
|
|
1300
|
+
"make-laten-search": handleSearch,
|
|
1301
|
+
"make-laten-fetch": handleFetch,
|
|
1302
|
+
"make-laten-tools": handleTools
|
|
347
1303
|
};
|
|
348
1304
|
function sendResponse(id, result) {
|
|
349
1305
|
const response = { jsonrpc: "2.0", id, result };
|
|
@@ -360,7 +1316,7 @@ async function handleLine(line) {
|
|
|
360
1316
|
sendResponse(request.id, {
|
|
361
1317
|
protocolVersion: "2024-11-05",
|
|
362
1318
|
capabilities: { tools: {} },
|
|
363
|
-
serverInfo: { name: "make-laten", version: "1.
|
|
1319
|
+
serverInfo: { name: "make-laten", version: "1.2.0" }
|
|
364
1320
|
});
|
|
365
1321
|
} else if (request.method === "notifications/initialized") {
|
|
366
1322
|
} else if (request.method === "tools/list") {
|
|
@@ -376,6 +1332,7 @@ async function handleLine(line) {
|
|
|
376
1332
|
const result = await handler(params || {});
|
|
377
1333
|
sendResponse(request.id, result);
|
|
378
1334
|
} catch (error) {
|
|
1335
|
+
failureLearner.record({ type: name, error: error.message, success: false });
|
|
379
1336
|
sendError(request.id, -32e3, error.message);
|
|
380
1337
|
}
|
|
381
1338
|
} else {
|
|
@@ -398,5 +1355,5 @@ process.stdin.on("data", async (chunk) => {
|
|
|
398
1355
|
}
|
|
399
1356
|
}
|
|
400
1357
|
});
|
|
401
|
-
process.stderr.write("make-laten MCP server started\n");
|
|
1358
|
+
process.stderr.write("make-laten MCP server started (17 tools)\n");
|
|
402
1359
|
//# sourceMappingURL=server.js.map
|