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.
@@ -29,29 +29,145 @@ function isKnownPattern(content) {
29
29
  return patterns.some((p) => p.test(content));
30
30
  }
31
31
 
32
+ // src/compress/strip.ts
33
+ function stripImports(content) {
34
+ 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");
35
+ }
36
+ function stripComments(content) {
37
+ let result = content.replace(/(?<!["'`])\/\/.*$/gm, "");
38
+ result = result.replace(/\/\*[\s\S]*?\*\//g, "");
39
+ return result.split("\n").filter((l) => l.trim() !== "").join("\n");
40
+ }
41
+ function stripBlankLines(content) {
42
+ return content.replace(/\n{3,}/g, "\n\n");
43
+ }
44
+ function stripWhitespace(content) {
45
+ return content.replace(/[ \t]+$/gm, "");
46
+ }
47
+ function stripAll(content) {
48
+ let result = content;
49
+ result = stripImports(result);
50
+ result = stripComments(result);
51
+ result = stripBlankLines(result);
52
+ result = stripWhitespace(result);
53
+ return result.trim();
54
+ }
55
+
56
+ // src/compress/structure.ts
57
+ function extractExports(content) {
58
+ const lines = content.split("\n");
59
+ const exports = [];
60
+ for (const line of lines) {
61
+ const trimmed = line.trim();
62
+ if (/^export\s+(default\s+)?/.test(trimmed)) {
63
+ if (trimmed.includes("{") && !trimmed.includes("}")) {
64
+ exports.push(trimmed.replace(/\{[\s\S]*$/, "{ ... }"));
65
+ } else {
66
+ exports.push(trimmed);
67
+ }
68
+ }
69
+ }
70
+ return exports.join("\n");
71
+ }
72
+ function extractClassMethods(content) {
73
+ const classRegex = /class\s+\w+[^{]*\{([\s\S]*?)\n\}/g;
74
+ const methods = [];
75
+ let match;
76
+ while ((match = classRegex.exec(content)) !== null) {
77
+ const classBody = match[1];
78
+ const methodRegex = /(^\s*(?:public|private|protected|static|async|abstract|\s)*\w+\s*\([^)]*\)(?:\s*:\s*[^{]+)?)\s*\{/gm;
79
+ let methodMatch;
80
+ while ((methodMatch = methodRegex.exec(classBody)) !== null) {
81
+ methods.push(methodMatch[1].trim());
82
+ }
83
+ }
84
+ return methods.join("\n");
85
+ }
86
+ function extractFunctionSignatures(content) {
87
+ const funcRegex = /(export\s+)?(async\s+)?function\s+\w+\s*\([^)]*\)(?:\s*:\s*[^{]+)?\s*\{/g;
88
+ const signatures = [];
89
+ let match;
90
+ while ((match = funcRegex.exec(content)) !== null) {
91
+ signatures.push(match[0].replace(/\{$/, ""));
92
+ }
93
+ return signatures.join("\n");
94
+ }
95
+ function extractTypeDefinitions(content) {
96
+ const typeRegex = /(export\s+)?(interface|type)\s+\w+[^{]*\{[^}]*\}|(export\s+)?type\s+\w+\s*=[^;]+;?/g;
97
+ const types = [];
98
+ let match;
99
+ while ((match = typeRegex.exec(content)) !== null) {
100
+ types.push(match[0].trim());
101
+ }
102
+ return types.join("\n");
103
+ }
104
+
105
+ // src/compress/truncate.ts
106
+ function smartTruncate(content, maxTokens) {
107
+ const lines = content.split("\n");
108
+ const estimatedTokens = Math.ceil(content.length / 4);
109
+ if (estimatedTokens <= maxTokens) return content;
110
+ const targetLines = Math.round(maxTokens * 4 / (content.length / lines.length));
111
+ const headerLines = Math.floor(targetLines * 0.3);
112
+ const footerLines = Math.floor(targetLines * 0.3);
113
+ const omitted = lines.length - headerLines - footerLines;
114
+ const header = lines.slice(0, headerLines);
115
+ const footer = lines.slice(-footerLines);
116
+ return [...header, `
117
+ ... [${omitted} lines omitted] ...
118
+ `, ...footer].join("\n");
119
+ }
120
+
32
121
  // src/compress/file-read.ts
33
122
  var FileReadCompressor = class {
34
123
  async compress(input) {
35
124
  const { content, filePath, language } = input;
36
- const signatures = this.extractSignatures(content, language);
37
- const exports = this.extractExports(content);
38
- const lines = [
125
+ const lines = content.split("\n").length;
126
+ const tokens = Math.ceil(content.length / 4);
127
+ if (tokens < 200) {
128
+ return {
129
+ content,
130
+ original: content,
131
+ confidence: 1,
132
+ metadata: {
133
+ strategy: "passthrough",
134
+ originalLines: lines,
135
+ compressedLines: lines,
136
+ savings: 0
137
+ }
138
+ };
139
+ }
140
+ const stripped = stripAll(content);
141
+ const exports = extractExports(stripped);
142
+ const classes = extractClassMethods(stripped);
143
+ const functions = extractFunctionSignatures(stripped);
144
+ const types = extractTypeDefinitions(stripped);
145
+ const sections = [
39
146
  `// ${filePath} (${content.split("\n").length} lines)`,
40
147
  "",
41
- ...signatures,
42
- "",
43
148
  "// Exports:",
44
- ...exports
45
- ];
46
- const compressed = lines.join("\n");
149
+ exports,
150
+ "",
151
+ "// Classes:",
152
+ classes,
153
+ "",
154
+ "// Functions:",
155
+ functions,
156
+ "",
157
+ "// Types:",
158
+ types
159
+ ].filter((l) => l !== "" && !l.endsWith(":"));
160
+ let compressed = sections.join("\n");
161
+ const maxTokens = 2e3;
162
+ compressed = smartTruncate(compressed, maxTokens);
47
163
  return {
48
164
  content: compressed,
49
165
  original: content,
50
166
  confidence: calculateConfidence(content, compressed),
51
167
  metadata: {
52
- strategy: "signatures",
168
+ strategy: "hybrid",
53
169
  originalLines: content.split("\n").length,
54
- compressedLines: lines.length,
170
+ compressedLines: compressed.split("\n").length,
55
171
  savings: 1 - compressed.length / content.length
56
172
  }
57
173
  };
@@ -59,32 +175,6 @@ var FileReadCompressor = class {
59
175
  async decompress(compressed) {
60
176
  return compressed.original;
61
177
  }
62
- extractSignatures(content, language) {
63
- const signatures = [];
64
- const lines = content.split("\n");
65
- for (const line of lines) {
66
- const trimmed = line.trim();
67
- if (/^(export\s+)?(async\s+)?function\s+\w+/.test(trimmed)) {
68
- signatures.push(trimmed.replace(/\{[\s\S]*$/, "{ ... }"));
69
- }
70
- if (/^(export\s+)?class\s+\w+/.test(trimmed)) {
71
- signatures.push(trimmed);
72
- }
73
- if (/^(export\s+)?(type|interface)\s+\w+/.test(trimmed)) {
74
- signatures.push(trimmed);
75
- }
76
- }
77
- return signatures;
78
- }
79
- extractExports(content) {
80
- const exports = [];
81
- const exportRegex = /export\s+(default\s+)?(function|class|const|let|var|type|interface)\s+(\w+)/g;
82
- let match;
83
- while ((match = exportRegex.exec(content)) !== null) {
84
- exports.push(` - ${match[3]} (${match[2]})`);
85
- }
86
- return exports;
87
- }
88
178
  };
89
179
 
90
180
  // src/compress/grep.ts
@@ -141,18 +231,21 @@ var GrepCompressor = class {
141
231
  var GitDiffCompressor = class {
142
232
  async compress(input) {
143
233
  const { diff } = input;
144
- const hunks = this.parseDiff(diff);
145
- const condensed = hunks.map((hunk) => ({
146
- file: hunk.file,
147
- changes: this.condenseHunk(hunk),
148
- stats: { additions: hunk.additions, deletions: hunk.deletions }
149
- }));
234
+ const files = this.parseDiff(diff);
150
235
  const lines = [];
151
- for (const hunk of condensed) {
152
- if (hunk.changes.length > 0) {
153
- lines.push(`${hunk.file} (+${hunk.stats.additions} -${hunk.stats.deletions})`);
154
- lines.push(...hunk.changes);
236
+ const totalAdd = files.reduce((s, f) => s + f.additions, 0);
237
+ const totalDel = files.reduce((s, f) => s + f.deletions, 0);
238
+ lines.push(`# ${files.length} files changed: +${totalAdd} -${totalDel}`);
239
+ lines.push("");
240
+ for (const file of files) {
241
+ lines.push(`${file.path} (+${file.additions} -${file.deletions})`);
242
+ const changes = file.lines.filter((l) => l.type === "add" || l.type === "del").map((l) => `${l.type === "add" ? "+" : "-"}${l.content}`);
243
+ const limited = changes.slice(0, 20);
244
+ lines.push(...limited);
245
+ if (changes.length > 20) {
246
+ lines.push(`... [${changes.length - 20} more changes]`);
155
247
  }
248
+ lines.push("");
156
249
  }
157
250
  const compressed = lines.join("\n");
158
251
  return {
@@ -161,7 +254,7 @@ var GitDiffCompressor = class {
161
254
  confidence: calculateConfidence(diff, compressed),
162
255
  metadata: {
163
256
  strategy: "condensed",
164
- filesChanged: condensed.length,
257
+ filesChanged: files.length,
165
258
  savings: 1 - compressed.length / diff.length
166
259
  }
167
260
  };
@@ -170,46 +263,716 @@ var GitDiffCompressor = class {
170
263
  return compressed.original;
171
264
  }
172
265
  parseDiff(diff) {
173
- const hunks = [];
266
+ const files = [];
174
267
  const lines = diff.split("\n");
175
- let currentHunk = null;
268
+ let current = null;
176
269
  for (const line of lines) {
177
270
  if (line.startsWith("diff --git")) {
178
- if (currentHunk) hunks.push(currentHunk);
179
- const fileMatch = line.match(/b\/(.+)/);
180
- currentHunk = {
181
- file: fileMatch?.[1] || "unknown",
182
- lines: [],
271
+ if (current) files.push(current);
272
+ const match = line.match(/b\/(.+)/);
273
+ current = {
274
+ path: match?.[1] || "unknown",
183
275
  additions: 0,
184
- deletions: 0
276
+ deletions: 0,
277
+ lines: []
185
278
  };
186
- } else if (currentHunk) {
279
+ } else if (current) {
187
280
  if (line.startsWith("+")) {
188
- currentHunk.additions++;
189
- currentHunk.lines.push({ type: "addition", content: line.slice(1) });
281
+ current.additions++;
282
+ current.lines.push({ type: "add", content: line.slice(1) });
190
283
  } else if (line.startsWith("-")) {
191
- currentHunk.deletions++;
192
- currentHunk.lines.push({ type: "deletion", content: line.slice(1) });
284
+ current.deletions++;
285
+ current.lines.push({ type: "del", content: line.slice(1) });
193
286
  }
194
287
  }
195
288
  }
196
- if (currentHunk) hunks.push(currentHunk);
197
- return hunks;
289
+ if (current) files.push(current);
290
+ return files;
291
+ }
292
+ };
293
+
294
+ // src/compress/git-status.ts
295
+ var GitStatusCompressor = class {
296
+ async compress(input) {
297
+ const { status } = input;
298
+ const statusLines = status.split("\n").filter(Boolean).length;
299
+ const tokens = Math.ceil(status.length / 4);
300
+ if (tokens < 10) {
301
+ return {
302
+ content: status,
303
+ original: status,
304
+ confidence: 1,
305
+ metadata: {
306
+ strategy: "passthrough",
307
+ totalFiles: statusLines,
308
+ savings: 0
309
+ }
310
+ };
311
+ }
312
+ const files = this.parseStatus(status);
313
+ const grouped = this.groupByStatus(files);
314
+ const lines = [];
315
+ for (const [statusType, statusFiles] of grouped) {
316
+ const label = this.statusLabel(statusType);
317
+ lines.push(`${label} (${statusFiles.length})`);
318
+ const compressed2 = this.compressPaths(statusFiles.map((f) => f.path));
319
+ lines.push(...compressed2);
320
+ lines.push("");
321
+ }
322
+ const compressed = lines.join("\n");
323
+ return {
324
+ content: compressed,
325
+ original: status,
326
+ confidence: calculateConfidence(status, compressed),
327
+ metadata: {
328
+ strategy: "grouped",
329
+ totalFiles: files.length,
330
+ savings: 1 - compressed.length / status.length
331
+ }
332
+ };
333
+ }
334
+ async decompress(compressed) {
335
+ return compressed.original;
336
+ }
337
+ parseStatus(status) {
338
+ return status.split("\n").filter(Boolean).map((line) => {
339
+ const match = line.match(/^(\?\?|[MADRC]+\s+)(.+)$/);
340
+ if (match) {
341
+ return { status: match[1].trim(), path: match[2] };
342
+ }
343
+ return { status: line[0], path: line.slice(3) };
344
+ });
345
+ }
346
+ groupByStatus(files) {
347
+ const grouped = /* @__PURE__ */ new Map();
348
+ for (const file of files) {
349
+ const existing = grouped.get(file.status) || [];
350
+ existing.push(file);
351
+ grouped.set(file.status, existing);
352
+ }
353
+ return grouped;
354
+ }
355
+ statusLabel(status) {
356
+ const labels = {
357
+ "M": "Modified",
358
+ "A": "Added",
359
+ "D": "Deleted",
360
+ "??": "Untracked",
361
+ "R": "Renamed"
362
+ };
363
+ return labels[status] || `Status ${status}`;
364
+ }
365
+ compressPaths(paths) {
366
+ if (paths.length <= 3) return paths.map((p) => ` ${p}`);
367
+ const parts = paths.map((p) => p.split("/"));
368
+ const minLength = Math.min(...parts.map((p) => p.length));
369
+ let commonIndex = 0;
370
+ for (let i = 0; i < minLength; i++) {
371
+ if (parts.every((p) => p[i] === parts[0][i])) {
372
+ commonIndex = i;
373
+ } else {
374
+ break;
375
+ }
376
+ }
377
+ if (commonIndex > 0) {
378
+ const prefix = parts[0].slice(0, commonIndex).join("/");
379
+ const suffixes = paths.map((p) => p.slice(prefix.length + 1));
380
+ return [` ${prefix}/`, ...suffixes.map((s) => ` ${s}`)];
381
+ }
382
+ return paths.map((p) => ` ${p}`);
383
+ }
384
+ };
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;
198
679
  }
199
- condenseHunk(hunk) {
200
- return hunk.lines.filter((l) => l.type === "addition" || l.type === "deletion").map((l) => `${l.type === "addition" ? "+" : "-"} ${l.content}`);
680
+ stripTags(html) {
681
+ return html.replace(/<[^>]+>/g, "").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&#x27;/g, "'").replace(/&quot;/g, '"');
201
682
  }
202
683
  };
203
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(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/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
+
204
959
  // src/mcp/server.ts
205
960
  import { exec } from "child_process";
206
961
  import { promisify } from "util";
207
962
  import fs from "fs/promises";
208
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();
209
971
  var TOOLS = [
972
+ // Compress Layer
210
973
  {
211
974
  name: "make-laten-read",
212
- description: "Read and compress a file (60-90% token savings)",
975
+ description: "Read and compress a file (65-92% token savings)",
213
976
  inputSchema: {
214
977
  type: "object",
215
978
  properties: {
@@ -245,10 +1008,127 @@ var TOOLS = [
245
1008
  description: "Compressed git status",
246
1009
  inputSchema: { type: "object", properties: {} }
247
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
248
1036
  {
249
1037
  name: "make-laten-cache-stats",
250
1038
  description: "Show cache performance stats",
251
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: {} }
252
1132
  }
253
1133
  ];
254
1134
  function detectLanguage(filePath) {
@@ -278,9 +1158,9 @@ async function handleRead(params) {
278
1158
  filePath: params.file_path,
279
1159
  language: detectLanguage(params.file_path)
280
1160
  });
281
- return {
282
- content: [{ type: "text", text: JSON.stringify({ compressed: result.content, savings: result.metadata.savings, confidence: result.confidence }) }]
283
- };
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 }) }] };
284
1164
  }
285
1165
  async function handleGrep(params) {
286
1166
  const dir = params.path || ".";
@@ -294,6 +1174,7 @@ async function handleGrep(params) {
294
1174
  });
295
1175
  const compressor = new GrepCompressor();
296
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 });
297
1178
  return { content: [{ type: "text", text: JSON.stringify({ compressed: result.content, total: matches.length, savings: result.metadata.savings }) }] };
298
1179
  }
299
1180
  async function handleGitDiff(params) {
@@ -304,23 +1185,98 @@ async function handleGitDiff(params) {
304
1185
  }
305
1186
  const compressor = new GitDiffCompressor();
306
1187
  const result = await compressor.compress({ diff: stdout });
1188
+ patternMiner.record({ type: "git-diff", input: { staged: params.staged }, success: true });
307
1189
  return { content: [{ type: "text", text: JSON.stringify({ compressed: result.content, savings: result.metadata.savings }) }] };
308
1190
  }
309
1191
  async function handleGitStatus() {
310
1192
  const { stdout } = await execAsync("git status --porcelain");
311
- const lines = stdout.split("\n").filter(Boolean);
312
- const files = lines.map((l) => ({ status: l[0], path: l.slice(3) }));
313
- return { content: [{ type: "text", text: JSON.stringify({ total: files.length, files }) }] };
1193
+ const compressor = new GitStatusCompressor();
1194
+ const result = await compressor.compress({ status: stdout });
1195
+ return { content: [{ type: "text", text: JSON.stringify({ compressed: result.content, savings: result.metadata.savings }) }] };
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) }] };
314
1207
  }
315
1208
  async function handleCacheStats() {
316
- return { content: [{ type: "text", text: JSON.stringify({ message: "Cache stats \u2014 use CLI: make-laten cache stats" }) }] };
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 }) }] };
317
1261
  }
318
1262
  var handlers = {
319
1263
  "make-laten-read": handleRead,
320
1264
  "make-laten-grep": handleGrep,
321
1265
  "make-laten-git-diff": handleGitDiff,
322
1266
  "make-laten-git-status": handleGitStatus,
323
- "make-laten-cache-stats": handleCacheStats
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
324
1280
  };
325
1281
  function sendResponse(id, result) {
326
1282
  const response = { jsonrpc: "2.0", id, result };
@@ -337,7 +1293,7 @@ async function handleLine(line) {
337
1293
  sendResponse(request.id, {
338
1294
  protocolVersion: "2024-11-05",
339
1295
  capabilities: { tools: {} },
340
- serverInfo: { name: "make-laten", version: "1.0.0" }
1296
+ serverInfo: { name: "make-laten", version: "1.2.0" }
341
1297
  });
342
1298
  } else if (request.method === "notifications/initialized") {
343
1299
  } else if (request.method === "tools/list") {
@@ -353,6 +1309,7 @@ async function handleLine(line) {
353
1309
  const result = await handler(params || {});
354
1310
  sendResponse(request.id, result);
355
1311
  } catch (error) {
1312
+ failureLearner.record({ type: name, error: error.message, success: false });
356
1313
  sendError(request.id, -32e3, error.message);
357
1314
  }
358
1315
  } else {
@@ -375,5 +1332,5 @@ process.stdin.on("data", async (chunk) => {
375
1332
  }
376
1333
  }
377
1334
  });
378
- process.stderr.write("make-laten MCP server started\n");
1335
+ process.stderr.write("make-laten MCP server started (17 tools)\n");
379
1336
  //# sourceMappingURL=server.mjs.map