make-laten 1.2.0 → 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/dist/mcp/server.js +783 -8
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/server.mjs +783 -8
- package/dist/mcp/server.mjs.map +1 -1
- package/package.json +1 -1
package/dist/mcp/server.mjs
CHANGED
|
@@ -383,15 +383,596 @@ var GitStatusCompressor = class {
|
|
|
383
383
|
}
|
|
384
384
|
};
|
|
385
385
|
|
|
386
|
+
// src/route/tool-router.ts
|
|
387
|
+
var ToolRouter = class {
|
|
388
|
+
rules = [
|
|
389
|
+
{
|
|
390
|
+
matcher: (input) => input.type === "file",
|
|
391
|
+
compressor: "file-read",
|
|
392
|
+
confidence: 0.95,
|
|
393
|
+
reason: "File read detected"
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
matcher: (input) => input.type === "grep",
|
|
397
|
+
compressor: "grep",
|
|
398
|
+
confidence: 0.95,
|
|
399
|
+
reason: "Grep results detected"
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
matcher: (input) => input.type === "git-diff",
|
|
403
|
+
compressor: "git-diff",
|
|
404
|
+
confidence: 0.95,
|
|
405
|
+
reason: "Git diff detected"
|
|
406
|
+
}
|
|
407
|
+
];
|
|
408
|
+
route(input) {
|
|
409
|
+
for (const rule of this.rules) {
|
|
410
|
+
if (rule.matcher(input)) {
|
|
411
|
+
return {
|
|
412
|
+
tool: "compress",
|
|
413
|
+
compressor: rule.compressor,
|
|
414
|
+
confidence: rule.confidence,
|
|
415
|
+
reason: rule.reason
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return {
|
|
420
|
+
tool: "compress",
|
|
421
|
+
confidence: 0.1,
|
|
422
|
+
reason: "No matching route found"
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
addRule(rule) {
|
|
426
|
+
this.rules.push(rule);
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
// src/route/strategy-router.ts
|
|
431
|
+
var StrategyRouter = class {
|
|
432
|
+
thresholds = {
|
|
433
|
+
small: 500,
|
|
434
|
+
large: 5e3
|
|
435
|
+
};
|
|
436
|
+
select(context) {
|
|
437
|
+
if (context.userPreference) {
|
|
438
|
+
return {
|
|
439
|
+
strategy: context.userPreference,
|
|
440
|
+
reason: `User preference: ${context.userPreference}`,
|
|
441
|
+
savings: this.estimateSavings(context.userPreference)
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
const fileSize = context.fileSize || 0;
|
|
445
|
+
if (fileSize >= this.thresholds.large) {
|
|
446
|
+
return {
|
|
447
|
+
strategy: "aggressive",
|
|
448
|
+
reason: `large file (${fileSize} bytes)`,
|
|
449
|
+
savings: 0.6
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
if (fileSize <= this.thresholds.small) {
|
|
453
|
+
return {
|
|
454
|
+
strategy: "conservative",
|
|
455
|
+
reason: `small file (${fileSize} bytes)`,
|
|
456
|
+
savings: 0.2
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
strategy: "balanced",
|
|
461
|
+
reason: `Medium file (${fileSize} bytes)`,
|
|
462
|
+
savings: 0.4
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
estimateSavings(strategy) {
|
|
466
|
+
switch (strategy) {
|
|
467
|
+
case "aggressive":
|
|
468
|
+
return 0.6;
|
|
469
|
+
case "balanced":
|
|
470
|
+
return 0.4;
|
|
471
|
+
case "conservative":
|
|
472
|
+
return 0.2;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// src/learn/pattern-miner.ts
|
|
478
|
+
var PatternMiner = class {
|
|
479
|
+
operations = [];
|
|
480
|
+
patterns = /* @__PURE__ */ new Map();
|
|
481
|
+
record(operation) {
|
|
482
|
+
this.operations.push(operation);
|
|
483
|
+
if (operation.success) {
|
|
484
|
+
this.updatePatterns(operation);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
getPatterns() {
|
|
488
|
+
return Array.from(this.patterns.values());
|
|
489
|
+
}
|
|
490
|
+
getPatternsByType(type) {
|
|
491
|
+
return this.getPatterns().filter((p) => p.type === type);
|
|
492
|
+
}
|
|
493
|
+
updatePatterns(operation) {
|
|
494
|
+
const key = operation.type;
|
|
495
|
+
const existing = this.patterns.get(key);
|
|
496
|
+
if (existing) {
|
|
497
|
+
existing.count++;
|
|
498
|
+
existing.confidence = Math.min(0.99, existing.confidence + 0.05);
|
|
499
|
+
} else {
|
|
500
|
+
this.patterns.set(key, {
|
|
501
|
+
id: `p${this.patterns.size + 1}`,
|
|
502
|
+
type: operation.type,
|
|
503
|
+
pattern: JSON.stringify(operation.input),
|
|
504
|
+
confidence: 0.5,
|
|
505
|
+
count: 1
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
// src/learn/failure-learner.ts
|
|
512
|
+
var FailureLearner = class {
|
|
513
|
+
failures = /* @__PURE__ */ new Map();
|
|
514
|
+
record(failure) {
|
|
515
|
+
const key = `${failure.type}:${failure.error}`;
|
|
516
|
+
const existing = this.failures.get(key);
|
|
517
|
+
if (existing) {
|
|
518
|
+
existing.count++;
|
|
519
|
+
} else {
|
|
520
|
+
this.failures.set(key, {
|
|
521
|
+
id: `f${this.failures.size + 1}`,
|
|
522
|
+
...failure,
|
|
523
|
+
timestamp: Date.now(),
|
|
524
|
+
count: 1
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
getFailures() {
|
|
529
|
+
return Array.from(this.failures.values());
|
|
530
|
+
}
|
|
531
|
+
getFailuresByType(type) {
|
|
532
|
+
return this.getFailures().filter((f) => f.type === type);
|
|
533
|
+
}
|
|
534
|
+
getSuggestions(type) {
|
|
535
|
+
const failures = Array.from(this.failures.values()).filter((f) => f.type === type);
|
|
536
|
+
const suggestions = [];
|
|
537
|
+
for (const failure of failures) {
|
|
538
|
+
if (failure.error.includes("not found")) {
|
|
539
|
+
suggestions.push("check if file exists before processing");
|
|
540
|
+
}
|
|
541
|
+
if (failure.error.includes("permission")) {
|
|
542
|
+
suggestions.push("verify file permissions");
|
|
543
|
+
}
|
|
544
|
+
if (failure.count > 2) {
|
|
545
|
+
suggestions.push(`recurring issue: ${failure.error}`);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return [...new Set(suggestions)];
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
// src/correct/auto-correct.ts
|
|
553
|
+
var AutoCorrect = class {
|
|
554
|
+
rules = [];
|
|
555
|
+
corrections = [];
|
|
556
|
+
appliedCount = 0;
|
|
557
|
+
addRule(rule) {
|
|
558
|
+
this.rules.push(rule);
|
|
559
|
+
}
|
|
560
|
+
getRules() {
|
|
561
|
+
return this.rules;
|
|
562
|
+
}
|
|
563
|
+
correct(input) {
|
|
564
|
+
let output = input;
|
|
565
|
+
for (const rule of this.rules) {
|
|
566
|
+
if (typeof rule.pattern === "string") {
|
|
567
|
+
if (output.includes(rule.pattern)) {
|
|
568
|
+
output = output.split(rule.pattern).join(rule.replacement);
|
|
569
|
+
this.recordCorrection(input, output, rule.name);
|
|
570
|
+
}
|
|
571
|
+
} else {
|
|
572
|
+
const matches = output.match(rule.pattern);
|
|
573
|
+
if (matches) {
|
|
574
|
+
output = output.replace(rule.pattern, rule.replacement);
|
|
575
|
+
this.recordCorrection(input, output, rule.name);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return output;
|
|
580
|
+
}
|
|
581
|
+
getStats() {
|
|
582
|
+
return {
|
|
583
|
+
applied: this.appliedCount,
|
|
584
|
+
corrections: this.corrections
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
recordCorrection(original, corrected, ruleName) {
|
|
588
|
+
this.appliedCount++;
|
|
589
|
+
this.corrections.push({
|
|
590
|
+
id: `c${this.corrections.length + 1}`,
|
|
591
|
+
original,
|
|
592
|
+
corrected,
|
|
593
|
+
rule: ruleName,
|
|
594
|
+
confidence: 0.9
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
// src/cache/l1-session.ts
|
|
600
|
+
var SessionCache = class {
|
|
601
|
+
store = /* @__PURE__ */ new Map();
|
|
602
|
+
hits = 0;
|
|
603
|
+
misses = 0;
|
|
604
|
+
get(key) {
|
|
605
|
+
const entry = this.store.get(key);
|
|
606
|
+
if (entry) {
|
|
607
|
+
this.hits++;
|
|
608
|
+
return entry;
|
|
609
|
+
}
|
|
610
|
+
this.misses++;
|
|
611
|
+
return null;
|
|
612
|
+
}
|
|
613
|
+
set(key, value) {
|
|
614
|
+
this.store.set(key, value);
|
|
615
|
+
}
|
|
616
|
+
clear() {
|
|
617
|
+
this.store.clear();
|
|
618
|
+
this.hits = 0;
|
|
619
|
+
this.misses = 0;
|
|
620
|
+
}
|
|
621
|
+
stats() {
|
|
622
|
+
const total = this.hits + this.misses;
|
|
623
|
+
return {
|
|
624
|
+
hits: this.hits,
|
|
625
|
+
misses: this.misses,
|
|
626
|
+
size: this.store.size,
|
|
627
|
+
hitRate: total > 0 ? this.hits / total : 0
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
// src/web/backends/duckduckgo.ts
|
|
633
|
+
var DuckDuckGoBackend = class {
|
|
634
|
+
name = "duckduckgo";
|
|
635
|
+
isAvailable() {
|
|
636
|
+
return true;
|
|
637
|
+
}
|
|
638
|
+
async search(query, options) {
|
|
639
|
+
const maxResults = options?.maxResults || 5;
|
|
640
|
+
const url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
|
|
641
|
+
const response = await fetch(url, {
|
|
642
|
+
headers: {
|
|
643
|
+
"User-Agent": "Mozilla/5.0 (compatible; make-laten/0.1.0)"
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
const html = await response.text();
|
|
647
|
+
return this.parseResults(html, maxResults);
|
|
648
|
+
}
|
|
649
|
+
parseResults(html, maxResults) {
|
|
650
|
+
const results = [];
|
|
651
|
+
const resultRegex = /<a[^>]*class="result__a"[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
652
|
+
const snippetRegex = /<a[^>]*class="result__snippet"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
653
|
+
let match;
|
|
654
|
+
const urls = [];
|
|
655
|
+
const titles = [];
|
|
656
|
+
while ((match = resultRegex.exec(html)) !== null && urls.length < maxResults) {
|
|
657
|
+
const rawUrl = match[1];
|
|
658
|
+
const urlMatch = rawUrl.match(/uddg=([^&]+)/);
|
|
659
|
+
const url = urlMatch ? decodeURIComponent(urlMatch[1]) : rawUrl;
|
|
660
|
+
const title = this.stripTags(match[2]).trim();
|
|
661
|
+
if (url && title) {
|
|
662
|
+
urls.push(url);
|
|
663
|
+
titles.push(title);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
const snippets = [];
|
|
667
|
+
while ((match = snippetRegex.exec(html)) !== null && snippets.length < maxResults) {
|
|
668
|
+
snippets.push(this.stripTags(match[1]).trim());
|
|
669
|
+
}
|
|
670
|
+
for (let i = 0; i < urls.length; i++) {
|
|
671
|
+
results.push({
|
|
672
|
+
title: titles[i],
|
|
673
|
+
url: urls[i],
|
|
674
|
+
snippet: snippets[i] || "",
|
|
675
|
+
score: 1 - i * 0.1
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
return results;
|
|
679
|
+
}
|
|
680
|
+
stripTags(html) {
|
|
681
|
+
return html.replace(/<[^>]+>/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/'/g, "'").replace(/"/g, '"');
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
// src/web/backends/index.ts
|
|
686
|
+
function selectBackend(preferred) {
|
|
687
|
+
const backends = [
|
|
688
|
+
new DuckDuckGoBackend()
|
|
689
|
+
];
|
|
690
|
+
if (preferred) {
|
|
691
|
+
const found = backends.find((b) => b.name === preferred);
|
|
692
|
+
if (found) return found;
|
|
693
|
+
}
|
|
694
|
+
return backends.find((b) => b.isAvailable()) || backends[0];
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// src/web/extractor.ts
|
|
698
|
+
function stripTags(html) {
|
|
699
|
+
return html.replace(/<[^>]+>/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'");
|
|
700
|
+
}
|
|
701
|
+
function extractTitle(html) {
|
|
702
|
+
const titleMatch = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
703
|
+
if (titleMatch) return stripTags(titleMatch[1]).trim();
|
|
704
|
+
const h1Match = html.match(/<h1[^>]*>([\s\S]*?)<\/h1>/i);
|
|
705
|
+
if (h1Match) return stripTags(h1Match[1]).trim();
|
|
706
|
+
return "Untitled";
|
|
707
|
+
}
|
|
708
|
+
function extractSections(html) {
|
|
709
|
+
const sections = [];
|
|
710
|
+
const headingRegex = /<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi;
|
|
711
|
+
let match;
|
|
712
|
+
while ((match = headingRegex.exec(html)) !== null) {
|
|
713
|
+
const level = parseInt(match[1]);
|
|
714
|
+
const heading = stripTags(match[2]).trim();
|
|
715
|
+
const startPos = match.index + match[0].length;
|
|
716
|
+
const nextHeading = html.substring(startPos).match(/<h[1-6]/i);
|
|
717
|
+
const endPos = nextHeading ? startPos + nextHeading.index : html.length;
|
|
718
|
+
const content = stripTags(html.substring(startPos, endPos)).trim();
|
|
719
|
+
if (!content) continue;
|
|
720
|
+
const importance = level <= 2 ? "primary" : level <= 4 ? "secondary" : "tertiary";
|
|
721
|
+
sections.push({ heading, level, content, importance });
|
|
722
|
+
}
|
|
723
|
+
return sections;
|
|
724
|
+
}
|
|
725
|
+
function extractCodeExamples(html) {
|
|
726
|
+
const examples = [];
|
|
727
|
+
const codeRegex = /<(?:pre|code)[^>]*>[\s\S]*?<(?:code|\/pre)[^>]*>/gi;
|
|
728
|
+
const blockRegex = /<pre[^>]*><code[^>]*class="[^"]*language-(\w+)[^"]*"[^>]*>([\s\S]*?)<\/code><\/pre>/gi;
|
|
729
|
+
let match;
|
|
730
|
+
while ((match = blockRegex.exec(html)) !== null) {
|
|
731
|
+
const language = match[1];
|
|
732
|
+
const code = stripTags(match[2]).trim();
|
|
733
|
+
if (code.length > 10) {
|
|
734
|
+
examples.push({ language, code });
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
const inlineRegex = /<code[^>]*class="[^"]*language-(\w+)[^"]*"[^>]*>([\s\S]*?)<\/code>/gi;
|
|
738
|
+
while ((match = inlineRegex.exec(html)) !== null) {
|
|
739
|
+
const language = match[1];
|
|
740
|
+
const code = stripTags(match[2]).trim();
|
|
741
|
+
if (code.length > 20 && !examples.some((e) => e.code === code)) {
|
|
742
|
+
examples.push({ language, code });
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return examples.slice(0, 10);
|
|
746
|
+
}
|
|
747
|
+
function extractKeyPoints(sections) {
|
|
748
|
+
const keyPoints = [];
|
|
749
|
+
for (const section of sections.filter((s) => s.importance === "primary")) {
|
|
750
|
+
const sentences = section.content.split(/[.!?\n]+/).filter((s) => s.trim().length > 10);
|
|
751
|
+
if (sentences.length > 0) {
|
|
752
|
+
keyPoints.push(sentences[0].trim());
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
return keyPoints.slice(0, 5);
|
|
756
|
+
}
|
|
757
|
+
function classifyPurpose(html, sections) {
|
|
758
|
+
const text = (html + " " + sections.map((s) => s.heading).join(" ")).toLowerCase();
|
|
759
|
+
if (text.includes("api reference") || text.includes("endpoint") || text.includes("parameters")) return "reference";
|
|
760
|
+
if (text.includes("tutorial") || text.includes("step by step") || text.includes("getting started")) return "tutorial";
|
|
761
|
+
if (text.includes("blog") || text.includes("published") || text.includes("author")) return "blog";
|
|
762
|
+
if (text.includes("install") || text.includes("quickstart") || text.includes("overview")) return "documentation";
|
|
763
|
+
return "documentation";
|
|
764
|
+
}
|
|
765
|
+
function extractSemantic(html, url) {
|
|
766
|
+
const title = extractTitle(html);
|
|
767
|
+
const sections = extractSections(html);
|
|
768
|
+
const codeExamples = extractCodeExamples(html);
|
|
769
|
+
const keyPoints = extractKeyPoints(sections);
|
|
770
|
+
const purpose = classifyPurpose(html, sections);
|
|
771
|
+
return {
|
|
772
|
+
type: "webpage",
|
|
773
|
+
url,
|
|
774
|
+
title,
|
|
775
|
+
purpose,
|
|
776
|
+
sections,
|
|
777
|
+
keyPoints,
|
|
778
|
+
codeExamples,
|
|
779
|
+
metadata: {
|
|
780
|
+
language: html.match(/lang="(\w+)"/)?.[1] || "en",
|
|
781
|
+
author: html.match(/<meta[^>]*name="author"[^>]*content="([^"]+)"/i)?.[1],
|
|
782
|
+
tags: html.match(/<meta[^>]*name="keywords"[^>]*content="([^"]+)"/i)?.[1]?.split(",").map((t) => t.trim())
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// src/web/compressor.ts
|
|
788
|
+
function compressWebContent(semantic, options = {}) {
|
|
789
|
+
const { maxTokens = 1e3, includeCode = true, includeSections = "all" } = options;
|
|
790
|
+
const lines = [];
|
|
791
|
+
lines.push(`# ${semantic.title}`);
|
|
792
|
+
lines.push(`URL: ${semantic.url}`);
|
|
793
|
+
lines.push(`Purpose: ${semantic.purpose}`);
|
|
794
|
+
lines.push("");
|
|
795
|
+
if (semantic.keyPoints.length > 0) {
|
|
796
|
+
lines.push("## Key Points");
|
|
797
|
+
for (const point of semantic.keyPoints) {
|
|
798
|
+
lines.push(`- ${point}`);
|
|
799
|
+
}
|
|
800
|
+
lines.push("");
|
|
801
|
+
}
|
|
802
|
+
const filteredSections = filterSections(semantic.sections, includeSections);
|
|
803
|
+
for (const section of filteredSections) {
|
|
804
|
+
const content = truncateContent(section.content, 500);
|
|
805
|
+
lines.push(`## ${section.heading}`);
|
|
806
|
+
lines.push(content);
|
|
807
|
+
lines.push("");
|
|
808
|
+
}
|
|
809
|
+
if (includeCode && semantic.codeExamples.length > 0) {
|
|
810
|
+
const topExamples = semantic.codeExamples.slice(0, 3);
|
|
811
|
+
lines.push("## Code Examples");
|
|
812
|
+
for (const example of topExamples) {
|
|
813
|
+
lines.push(`\`\`\`${example.language}`);
|
|
814
|
+
lines.push(truncateContent(example.code, 300));
|
|
815
|
+
lines.push("```");
|
|
816
|
+
lines.push("");
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
let result = lines.join("\n");
|
|
820
|
+
const estimatedTokens = estimateTokens(result);
|
|
821
|
+
if (estimatedTokens > maxTokens) {
|
|
822
|
+
result = truncateByTokens(result, maxTokens);
|
|
823
|
+
}
|
|
824
|
+
return result;
|
|
825
|
+
}
|
|
826
|
+
function filterSections(sections, mode) {
|
|
827
|
+
if (mode === "none") return [];
|
|
828
|
+
if (mode === "primary") return sections.filter((s) => s.importance === "primary");
|
|
829
|
+
return sections;
|
|
830
|
+
}
|
|
831
|
+
function truncateContent(content, maxLength) {
|
|
832
|
+
if (content.length <= maxLength) return content;
|
|
833
|
+
return content.slice(0, maxLength) + "...";
|
|
834
|
+
}
|
|
835
|
+
function estimateTokens(text) {
|
|
836
|
+
return Math.ceil(text.length / 4);
|
|
837
|
+
}
|
|
838
|
+
function truncateByTokens(text, maxTokens) {
|
|
839
|
+
const maxChars = maxTokens * 4;
|
|
840
|
+
if (text.length <= maxChars) return text;
|
|
841
|
+
const truncated = text.slice(0, maxChars);
|
|
842
|
+
const lastNewline = truncated.lastIndexOf("\n");
|
|
843
|
+
return truncated.slice(0, lastNewline > 0 ? lastNewline : maxChars) + "\n\n... (truncated)";
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// src/web/router.ts
|
|
847
|
+
var WebRouter = class {
|
|
848
|
+
fetchCache = /* @__PURE__ */ new Map();
|
|
849
|
+
searchCache = /* @__PURE__ */ new Map();
|
|
850
|
+
cacheTtl;
|
|
851
|
+
constructor(options) {
|
|
852
|
+
this.cacheTtl = options?.cacheTtlMs || 60 * 60 * 1e3;
|
|
853
|
+
}
|
|
854
|
+
async search(query, options) {
|
|
855
|
+
const cacheKey = `search:${query}:${JSON.stringify(options || {})}`;
|
|
856
|
+
const cached = this.searchCache.get(cacheKey);
|
|
857
|
+
if (cached && this.isFresh(cacheKey)) {
|
|
858
|
+
return cached;
|
|
859
|
+
}
|
|
860
|
+
const backend = selectBackend(options?.backend);
|
|
861
|
+
const results = await backend.search(query, options);
|
|
862
|
+
this.searchCache.set(cacheKey, results);
|
|
863
|
+
this.storeFreshness(cacheKey);
|
|
864
|
+
return results;
|
|
865
|
+
}
|
|
866
|
+
async fetch(url, options) {
|
|
867
|
+
const cacheKey = `fetch:${url}:${options?.format || "text"}`;
|
|
868
|
+
if (options?.cache !== false) {
|
|
869
|
+
const cached = this.fetchCache.get(cacheKey);
|
|
870
|
+
if (cached && this.isFresh(cacheKey)) {
|
|
871
|
+
return cached;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
const startTime = Date.now();
|
|
875
|
+
const response = await fetch(url);
|
|
876
|
+
const html = await response.text();
|
|
877
|
+
const originalSize = html.length;
|
|
878
|
+
let content;
|
|
879
|
+
let semantic = void 0;
|
|
880
|
+
if (options?.extract !== false) {
|
|
881
|
+
semantic = extractSemantic(html, url);
|
|
882
|
+
content = compressWebContent(semantic, {
|
|
883
|
+
includeCode: true,
|
|
884
|
+
includeSections: options?.format === "html" ? "all" : "primary"
|
|
885
|
+
});
|
|
886
|
+
} else {
|
|
887
|
+
content = html;
|
|
888
|
+
}
|
|
889
|
+
const compressedSize = content.length;
|
|
890
|
+
const result = {
|
|
891
|
+
url,
|
|
892
|
+
title: semantic?.title || extractSimpleTitle(html),
|
|
893
|
+
content,
|
|
894
|
+
semantic,
|
|
895
|
+
metadata: {
|
|
896
|
+
fetchTime: Date.now() - startTime,
|
|
897
|
+
originalSize,
|
|
898
|
+
compressedSize,
|
|
899
|
+
savings: 1 - compressedSize / originalSize
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
if (options?.cache !== false) {
|
|
903
|
+
this.fetchCache.set(cacheKey, result);
|
|
904
|
+
this.storeFreshness(cacheKey);
|
|
905
|
+
}
|
|
906
|
+
return result;
|
|
907
|
+
}
|
|
908
|
+
async extract(url) {
|
|
909
|
+
return this.fetch(url, { extract: true, compress: true });
|
|
910
|
+
}
|
|
911
|
+
async summarize(content, options) {
|
|
912
|
+
const lines = content.split("\n");
|
|
913
|
+
const summaryLines = [];
|
|
914
|
+
let tokenCount = 0;
|
|
915
|
+
const maxTokens = options?.maxTokens || 200;
|
|
916
|
+
for (const line of lines) {
|
|
917
|
+
const lineTokens = Math.ceil(line.length / 4);
|
|
918
|
+
if (tokenCount + lineTokens > maxTokens) break;
|
|
919
|
+
if (line.startsWith("#") || line.startsWith("-") || line.startsWith("*")) {
|
|
920
|
+
summaryLines.push(line);
|
|
921
|
+
tokenCount += lineTokens;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
if (summaryLines.length === 0 && lines.length > 0) {
|
|
925
|
+
for (const line of lines.slice(0, 5)) {
|
|
926
|
+
const lineTokens = Math.ceil(line.length / 4);
|
|
927
|
+
if (tokenCount + lineTokens > maxTokens) break;
|
|
928
|
+
summaryLines.push(line);
|
|
929
|
+
tokenCount += lineTokens;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return summaryLines.join("\n");
|
|
933
|
+
}
|
|
934
|
+
clearCache() {
|
|
935
|
+
this.fetchCache.clear();
|
|
936
|
+
this.searchCache.clear();
|
|
937
|
+
}
|
|
938
|
+
getCacheStats() {
|
|
939
|
+
return {
|
|
940
|
+
fetchSize: this.fetchCache.size,
|
|
941
|
+
searchSize: this.searchCache.size
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
isFresh(key) {
|
|
945
|
+
const timestamp = this.freshness.get(key);
|
|
946
|
+
if (!timestamp) return false;
|
|
947
|
+
return Date.now() - timestamp < this.cacheTtl;
|
|
948
|
+
}
|
|
949
|
+
storeFreshness(key) {
|
|
950
|
+
this.freshness.set(key, Date.now());
|
|
951
|
+
}
|
|
952
|
+
freshness = /* @__PURE__ */ new Map();
|
|
953
|
+
};
|
|
954
|
+
function extractSimpleTitle(html) {
|
|
955
|
+
const titleMatch = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
956
|
+
return titleMatch ? titleMatch[1].replace(/<[^>]+>/g, "").trim() : "Untitled";
|
|
957
|
+
}
|
|
958
|
+
|
|
386
959
|
// src/mcp/server.ts
|
|
387
960
|
import { exec } from "child_process";
|
|
388
961
|
import { promisify } from "util";
|
|
389
962
|
import fs from "fs/promises";
|
|
390
963
|
var execAsync = promisify(exec);
|
|
964
|
+
var sessionCache = new SessionCache();
|
|
965
|
+
var toolRouter = new ToolRouter();
|
|
966
|
+
var strategyRouter = new StrategyRouter();
|
|
967
|
+
var patternMiner = new PatternMiner();
|
|
968
|
+
var failureLearner = new FailureLearner();
|
|
969
|
+
var autoCorrect = new AutoCorrect();
|
|
970
|
+
var webRouter = new WebRouter();
|
|
391
971
|
var TOOLS = [
|
|
972
|
+
// Compress Layer
|
|
392
973
|
{
|
|
393
974
|
name: "make-laten-read",
|
|
394
|
-
description: "Read and compress a file (65-
|
|
975
|
+
description: "Read and compress a file (65-92% token savings)",
|
|
395
976
|
inputSchema: {
|
|
396
977
|
type: "object",
|
|
397
978
|
properties: {
|
|
@@ -427,10 +1008,127 @@ var TOOLS = [
|
|
|
427
1008
|
description: "Compressed git status",
|
|
428
1009
|
inputSchema: { type: "object", properties: {} }
|
|
429
1010
|
},
|
|
1011
|
+
// Route Layer
|
|
1012
|
+
{
|
|
1013
|
+
name: "make-laten-route",
|
|
1014
|
+
description: "Route input to correct compressor",
|
|
1015
|
+
inputSchema: {
|
|
1016
|
+
type: "object",
|
|
1017
|
+
properties: {
|
|
1018
|
+
type: { type: "string", enum: ["file", "grep", "git-diff", "unknown"], description: "Input type" },
|
|
1019
|
+
content: { type: "string", description: "Input content" }
|
|
1020
|
+
},
|
|
1021
|
+
required: ["type", "content"]
|
|
1022
|
+
}
|
|
1023
|
+
},
|
|
1024
|
+
{
|
|
1025
|
+
name: "make-laten-strategy",
|
|
1026
|
+
description: "Select compression strategy based on context",
|
|
1027
|
+
inputSchema: {
|
|
1028
|
+
type: "object",
|
|
1029
|
+
properties: {
|
|
1030
|
+
file_size: { type: "number", description: "File size in bytes" },
|
|
1031
|
+
preference: { type: "string", enum: ["aggressive", "balanced", "conservative"], description: "Strategy preference" }
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
},
|
|
1035
|
+
// Cache Layer
|
|
430
1036
|
{
|
|
431
1037
|
name: "make-laten-cache-stats",
|
|
432
1038
|
description: "Show cache performance stats",
|
|
433
1039
|
inputSchema: { type: "object", properties: {} }
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
name: "make-laten-cache-get",
|
|
1043
|
+
description: "Get value from session cache",
|
|
1044
|
+
inputSchema: {
|
|
1045
|
+
type: "object",
|
|
1046
|
+
properties: {
|
|
1047
|
+
key: { type: "string", description: "Cache key" }
|
|
1048
|
+
},
|
|
1049
|
+
required: ["key"]
|
|
1050
|
+
}
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
name: "make-laten-cache-set",
|
|
1054
|
+
description: "Set value in session cache",
|
|
1055
|
+
inputSchema: {
|
|
1056
|
+
type: "object",
|
|
1057
|
+
properties: {
|
|
1058
|
+
key: { type: "string", description: "Cache key" },
|
|
1059
|
+
value: { type: "string", description: "Value to cache" }
|
|
1060
|
+
},
|
|
1061
|
+
required: ["key", "value"]
|
|
1062
|
+
}
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
name: "make-laten-cache-clear",
|
|
1066
|
+
description: "Clear session cache",
|
|
1067
|
+
inputSchema: { type: "object", properties: {} }
|
|
1068
|
+
},
|
|
1069
|
+
// Learn Layer
|
|
1070
|
+
{
|
|
1071
|
+
name: "make-laten-patterns",
|
|
1072
|
+
description: "Get learned patterns from usage",
|
|
1073
|
+
inputSchema: { type: "object", properties: {} }
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
name: "make-laten-failures",
|
|
1077
|
+
description: "Get failure records and suggestions",
|
|
1078
|
+
inputSchema: { type: "object", properties: {} }
|
|
1079
|
+
},
|
|
1080
|
+
{
|
|
1081
|
+
name: "make-laten-suggestions",
|
|
1082
|
+
description: "Get suggestions based on learned patterns",
|
|
1083
|
+
inputSchema: {
|
|
1084
|
+
type: "object",
|
|
1085
|
+
properties: {
|
|
1086
|
+
context: { type: "string", description: "Context for suggestions" }
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
},
|
|
1090
|
+
// Correct Layer
|
|
1091
|
+
{
|
|
1092
|
+
name: "make-laten-correct",
|
|
1093
|
+
description: "Apply auto-corrections to text",
|
|
1094
|
+
inputSchema: {
|
|
1095
|
+
type: "object",
|
|
1096
|
+
properties: {
|
|
1097
|
+
text: { type: "string", description: "Text to correct" }
|
|
1098
|
+
},
|
|
1099
|
+
required: ["text"]
|
|
1100
|
+
}
|
|
1101
|
+
},
|
|
1102
|
+
// Web Layer
|
|
1103
|
+
{
|
|
1104
|
+
name: "make-laten-search",
|
|
1105
|
+
description: "Search the web with semantic understanding",
|
|
1106
|
+
inputSchema: {
|
|
1107
|
+
type: "object",
|
|
1108
|
+
properties: {
|
|
1109
|
+
query: { type: "string", description: "Search query" },
|
|
1110
|
+
backend: { type: "string", description: "Search backend (duckduckgo, exa, tavily)" }
|
|
1111
|
+
},
|
|
1112
|
+
required: ["query"]
|
|
1113
|
+
}
|
|
1114
|
+
},
|
|
1115
|
+
{
|
|
1116
|
+
name: "make-laten-fetch",
|
|
1117
|
+
description: "Fetch and compress web content",
|
|
1118
|
+
inputSchema: {
|
|
1119
|
+
type: "object",
|
|
1120
|
+
properties: {
|
|
1121
|
+
url: { type: "string", description: "URL to fetch" },
|
|
1122
|
+
extract: { type: "boolean", description: "Extract semantic content", default: true }
|
|
1123
|
+
},
|
|
1124
|
+
required: ["url"]
|
|
1125
|
+
}
|
|
1126
|
+
},
|
|
1127
|
+
// Tool Layer
|
|
1128
|
+
{
|
|
1129
|
+
name: "make-laten-tools",
|
|
1130
|
+
description: "List available make-laten tools",
|
|
1131
|
+
inputSchema: { type: "object", properties: {} }
|
|
434
1132
|
}
|
|
435
1133
|
];
|
|
436
1134
|
function detectLanguage(filePath) {
|
|
@@ -460,9 +1158,9 @@ async function handleRead(params) {
|
|
|
460
1158
|
filePath: params.file_path,
|
|
461
1159
|
language: detectLanguage(params.file_path)
|
|
462
1160
|
});
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
};
|
|
1161
|
+
patternMiner.record({ type: "file-read", input: { file: params.file_path }, success: true });
|
|
1162
|
+
sessionCache.set(`file:${params.file_path}`, { content: result.content, metadata: result.metadata });
|
|
1163
|
+
return { content: [{ type: "text", text: JSON.stringify({ compressed: result.content, savings: result.metadata.savings, confidence: result.confidence }) }] };
|
|
466
1164
|
}
|
|
467
1165
|
async function handleGrep(params) {
|
|
468
1166
|
const dir = params.path || ".";
|
|
@@ -476,6 +1174,7 @@ async function handleGrep(params) {
|
|
|
476
1174
|
});
|
|
477
1175
|
const compressor = new GrepCompressor();
|
|
478
1176
|
const result = await compressor.compress({ results: matches, pattern: params.pattern, directory: dir });
|
|
1177
|
+
patternMiner.record({ type: "grep", input: { pattern: params.pattern, dir }, success: true });
|
|
479
1178
|
return { content: [{ type: "text", text: JSON.stringify({ compressed: result.content, total: matches.length, savings: result.metadata.savings }) }] };
|
|
480
1179
|
}
|
|
481
1180
|
async function handleGitDiff(params) {
|
|
@@ -486,6 +1185,7 @@ async function handleGitDiff(params) {
|
|
|
486
1185
|
}
|
|
487
1186
|
const compressor = new GitDiffCompressor();
|
|
488
1187
|
const result = await compressor.compress({ diff: stdout });
|
|
1188
|
+
patternMiner.record({ type: "git-diff", input: { staged: params.staged }, success: true });
|
|
489
1189
|
return { content: [{ type: "text", text: JSON.stringify({ compressed: result.content, savings: result.metadata.savings }) }] };
|
|
490
1190
|
}
|
|
491
1191
|
async function handleGitStatus() {
|
|
@@ -494,15 +1194,89 @@ async function handleGitStatus() {
|
|
|
494
1194
|
const result = await compressor.compress({ status: stdout });
|
|
495
1195
|
return { content: [{ type: "text", text: JSON.stringify({ compressed: result.content, savings: result.metadata.savings }) }] };
|
|
496
1196
|
}
|
|
1197
|
+
async function handleRoute(params) {
|
|
1198
|
+
const route = toolRouter.route({ type: params.type, content: params.content });
|
|
1199
|
+
return { content: [{ type: "text", text: JSON.stringify(route) }] };
|
|
1200
|
+
}
|
|
1201
|
+
async function handleStrategy(params) {
|
|
1202
|
+
const strategy = strategyRouter.select({
|
|
1203
|
+
fileSize: params.file_size,
|
|
1204
|
+
userPreference: params.preference
|
|
1205
|
+
});
|
|
1206
|
+
return { content: [{ type: "text", text: JSON.stringify(strategy) }] };
|
|
1207
|
+
}
|
|
497
1208
|
async function handleCacheStats() {
|
|
498
|
-
|
|
1209
|
+
const stats = sessionCache.stats();
|
|
1210
|
+
return { content: [{ type: "text", text: JSON.stringify(stats) }] };
|
|
1211
|
+
}
|
|
1212
|
+
async function handleCacheGet(params) {
|
|
1213
|
+
const entry = sessionCache.get(params.key);
|
|
1214
|
+
return { content: [{ type: "text", text: JSON.stringify({ found: !!entry, entry }) }] };
|
|
1215
|
+
}
|
|
1216
|
+
async function handleCacheSet(params) {
|
|
1217
|
+
sessionCache.set(params.key, { content: params.value, metadata: { setAt: Date.now() } });
|
|
1218
|
+
return { content: [{ type: "text", text: JSON.stringify({ success: true }) }] };
|
|
1219
|
+
}
|
|
1220
|
+
async function handleCacheClear() {
|
|
1221
|
+
sessionCache.clear();
|
|
1222
|
+
return { content: [{ type: "text", text: JSON.stringify({ success: true }) }] };
|
|
1223
|
+
}
|
|
1224
|
+
async function handlePatterns() {
|
|
1225
|
+
const patterns = patternMiner.getPatterns();
|
|
1226
|
+
return { content: [{ type: "text", text: JSON.stringify({ patterns, total: patterns.length }) }] };
|
|
1227
|
+
}
|
|
1228
|
+
async function handleFailures() {
|
|
1229
|
+
const failures = failureLearner.getFailures();
|
|
1230
|
+
return { content: [{ type: "text", text: JSON.stringify({ failures, total: failures.length }) }] };
|
|
1231
|
+
}
|
|
1232
|
+
async function handleSuggestions(params) {
|
|
1233
|
+
const patterns = patternMiner.getPatterns();
|
|
1234
|
+
const failures = failureLearner.getFailures();
|
|
1235
|
+
const suggestions = [];
|
|
1236
|
+
for (const pattern of patterns.slice(0, 5)) {
|
|
1237
|
+
suggestions.push(`Pattern: ${pattern.type} (confidence: ${pattern.confidence})`);
|
|
1238
|
+
}
|
|
1239
|
+
for (const failure of failures.slice(0, 5)) {
|
|
1240
|
+
const failureSuggestions = failureLearner.getSuggestions(failure.type);
|
|
1241
|
+
suggestions.push(...failureSuggestions);
|
|
1242
|
+
}
|
|
1243
|
+
return { content: [{ type: "text", text: JSON.stringify({ suggestions: [...new Set(suggestions)] }) }] };
|
|
1244
|
+
}
|
|
1245
|
+
async function handleCorrect(params) {
|
|
1246
|
+
const corrected = autoCorrect.correct(params.text);
|
|
1247
|
+
const stats = autoCorrect.getStats();
|
|
1248
|
+
return { content: [{ type: "text", text: JSON.stringify({ original: params.text, corrected, corrections: stats.applied }) }] };
|
|
1249
|
+
}
|
|
1250
|
+
async function handleSearch(params) {
|
|
1251
|
+
const results = await webRouter.search(params.query, { backend: params.backend });
|
|
1252
|
+
return { content: [{ type: "text", text: JSON.stringify({ results, total: results.length }) }] };
|
|
1253
|
+
}
|
|
1254
|
+
async function handleFetch(params) {
|
|
1255
|
+
const result = await webRouter.fetch(params.url, { extract: params.extract !== false });
|
|
1256
|
+
return { content: [{ type: "text", text: JSON.stringify({ url: result.url, title: result.title, content: result.content, savings: result.metadata.savings }) }] };
|
|
1257
|
+
}
|
|
1258
|
+
async function handleTools() {
|
|
1259
|
+
const tools = TOOLS.map((t) => ({ name: t.name, description: t.description }));
|
|
1260
|
+
return { content: [{ type: "text", text: JSON.stringify({ tools, total: tools.length }) }] };
|
|
499
1261
|
}
|
|
500
1262
|
var handlers = {
|
|
501
1263
|
"make-laten-read": handleRead,
|
|
502
1264
|
"make-laten-grep": handleGrep,
|
|
503
1265
|
"make-laten-git-diff": handleGitDiff,
|
|
504
1266
|
"make-laten-git-status": handleGitStatus,
|
|
505
|
-
"make-laten-
|
|
1267
|
+
"make-laten-route": handleRoute,
|
|
1268
|
+
"make-laten-strategy": handleStrategy,
|
|
1269
|
+
"make-laten-cache-stats": handleCacheStats,
|
|
1270
|
+
"make-laten-cache-get": handleCacheGet,
|
|
1271
|
+
"make-laten-cache-set": handleCacheSet,
|
|
1272
|
+
"make-laten-cache-clear": handleCacheClear,
|
|
1273
|
+
"make-laten-patterns": handlePatterns,
|
|
1274
|
+
"make-laten-failures": handleFailures,
|
|
1275
|
+
"make-laten-suggestions": handleSuggestions,
|
|
1276
|
+
"make-laten-correct": handleCorrect,
|
|
1277
|
+
"make-laten-search": handleSearch,
|
|
1278
|
+
"make-laten-fetch": handleFetch,
|
|
1279
|
+
"make-laten-tools": handleTools
|
|
506
1280
|
};
|
|
507
1281
|
function sendResponse(id, result) {
|
|
508
1282
|
const response = { jsonrpc: "2.0", id, result };
|
|
@@ -519,7 +1293,7 @@ async function handleLine(line) {
|
|
|
519
1293
|
sendResponse(request.id, {
|
|
520
1294
|
protocolVersion: "2024-11-05",
|
|
521
1295
|
capabilities: { tools: {} },
|
|
522
|
-
serverInfo: { name: "make-laten", version: "1.
|
|
1296
|
+
serverInfo: { name: "make-laten", version: "1.2.0" }
|
|
523
1297
|
});
|
|
524
1298
|
} else if (request.method === "notifications/initialized") {
|
|
525
1299
|
} else if (request.method === "tools/list") {
|
|
@@ -535,6 +1309,7 @@ async function handleLine(line) {
|
|
|
535
1309
|
const result = await handler(params || {});
|
|
536
1310
|
sendResponse(request.id, result);
|
|
537
1311
|
} catch (error) {
|
|
1312
|
+
failureLearner.record({ type: name, error: error.message, success: false });
|
|
538
1313
|
sendError(request.id, -32e3, error.message);
|
|
539
1314
|
}
|
|
540
1315
|
} else {
|
|
@@ -557,5 +1332,5 @@ process.stdin.on("data", async (chunk) => {
|
|
|
557
1332
|
}
|
|
558
1333
|
}
|
|
559
1334
|
});
|
|
560
|
-
process.stderr.write("make-laten MCP server started\n");
|
|
1335
|
+
process.stderr.write("make-laten MCP server started (17 tools)\n");
|
|
561
1336
|
//# sourceMappingURL=server.mjs.map
|