make-laten 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -180,29 +180,157 @@ function isKnownPattern(content) {
180
180
  return patterns.some((p) => p.test(content));
181
181
  }
182
182
 
183
+ // src/compress/strip.ts
184
+ function stripImports(content) {
185
+ 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");
186
+ }
187
+ function stripComments(content) {
188
+ let result = content.replace(/(?<!["'`])\/\/.*$/gm, "");
189
+ result = result.replace(/\/\*[\s\S]*?\*\//g, "");
190
+ return result.split("\n").filter((l) => l.trim() !== "").join("\n");
191
+ }
192
+ function stripBlankLines(content) {
193
+ return content.replace(/\n{3,}/g, "\n\n");
194
+ }
195
+ function stripWhitespace(content) {
196
+ return content.replace(/[ \t]+$/gm, "");
197
+ }
198
+ function stripAll(content) {
199
+ let result = content;
200
+ result = stripImports(result);
201
+ result = stripComments(result);
202
+ result = stripBlankLines(result);
203
+ result = stripWhitespace(result);
204
+ return result.trim();
205
+ }
206
+
207
+ // src/compress/structure.ts
208
+ function extractExports(content) {
209
+ const lines = content.split("\n");
210
+ const exports = [];
211
+ for (const line of lines) {
212
+ const trimmed = line.trim();
213
+ if (/^export\s+(default\s+)?/.test(trimmed)) {
214
+ if (trimmed.includes("{") && !trimmed.includes("}")) {
215
+ exports.push(trimmed.replace(/\{[\s\S]*$/, "{ ... }"));
216
+ } else {
217
+ exports.push(trimmed);
218
+ }
219
+ }
220
+ }
221
+ return exports.join("\n");
222
+ }
223
+ function extractClassMethods(content) {
224
+ const classRegex = /class\s+\w+[^{]*\{([\s\S]*?)\n\}/g;
225
+ const methods = [];
226
+ let match;
227
+ while ((match = classRegex.exec(content)) !== null) {
228
+ const classBody = match[1];
229
+ const methodRegex = /(^\s*(?:public|private|protected|static|async|abstract|\s)*\w+\s*\([^)]*\)(?:\s*:\s*[^{]+)?)\s*\{/gm;
230
+ let methodMatch;
231
+ while ((methodMatch = methodRegex.exec(classBody)) !== null) {
232
+ methods.push(methodMatch[1].trim());
233
+ }
234
+ }
235
+ return methods.join("\n");
236
+ }
237
+ function extractFunctionSignatures(content) {
238
+ const funcRegex = /(export\s+)?(async\s+)?function\s+\w+\s*\([^)]*\)(?:\s*:\s*[^{]+)?\s*\{/g;
239
+ const signatures = [];
240
+ let match;
241
+ while ((match = funcRegex.exec(content)) !== null) {
242
+ signatures.push(match[0].replace(/\{$/, ""));
243
+ }
244
+ return signatures.join("\n");
245
+ }
246
+ function extractTypeDefinitions(content) {
247
+ const typeRegex = /(export\s+)?(interface|type)\s+\w+[^{]*\{[^}]*\}|(export\s+)?type\s+\w+\s*=[^;]+;?/g;
248
+ const types = [];
249
+ let match;
250
+ while ((match = typeRegex.exec(content)) !== null) {
251
+ types.push(match[0].trim());
252
+ }
253
+ return types.join("\n");
254
+ }
255
+
256
+ // src/compress/truncate.ts
257
+ function smartTruncate(content, maxTokens) {
258
+ const lines = content.split("\n");
259
+ const estimatedTokens = Math.ceil(content.length / 4);
260
+ if (estimatedTokens <= maxTokens) return content;
261
+ const targetLines = Math.round(maxTokens * 4 / (content.length / lines.length));
262
+ const headerLines = Math.floor(targetLines * 0.3);
263
+ const footerLines = Math.floor(targetLines * 0.3);
264
+ const omitted = lines.length - headerLines - footerLines;
265
+ const header = lines.slice(0, headerLines);
266
+ const footer = lines.slice(-footerLines);
267
+ return [...header, `
268
+ ... [${omitted} lines omitted] ...
269
+ `, ...footer].join("\n");
270
+ }
271
+ function truncateByTokens(text, maxTokens) {
272
+ const maxChars = maxTokens * 4;
273
+ if (text.length <= maxChars) return text;
274
+ const truncated = text.slice(0, maxChars);
275
+ const lastNewline = truncated.lastIndexOf("\n");
276
+ return truncated.slice(0, lastNewline > 0 ? lastNewline : maxChars) + "\n\n... (truncated)";
277
+ }
278
+ function preserveContext(lines, targetIndex, before, after) {
279
+ const start = Math.max(0, targetIndex - before);
280
+ const end = Math.min(lines.length, targetIndex + after + 1);
281
+ return lines.slice(start, end);
282
+ }
283
+
183
284
  // src/compress/file-read.ts
184
285
  var FileReadCompressor = class {
185
286
  async compress(input) {
186
287
  const { content, filePath, language } = input;
187
- const signatures = this.extractSignatures(content, language);
188
- const exports = this.extractExports(content);
189
- const lines = [
288
+ const lines = content.split("\n").length;
289
+ const tokens = Math.ceil(content.length / 4);
290
+ if (tokens < 200) {
291
+ return {
292
+ content,
293
+ original: content,
294
+ confidence: 1,
295
+ metadata: {
296
+ strategy: "passthrough",
297
+ originalLines: lines,
298
+ compressedLines: lines,
299
+ savings: 0
300
+ }
301
+ };
302
+ }
303
+ const stripped = stripAll(content);
304
+ const exports = extractExports(stripped);
305
+ const classes = extractClassMethods(stripped);
306
+ const functions = extractFunctionSignatures(stripped);
307
+ const types = extractTypeDefinitions(stripped);
308
+ const sections = [
190
309
  `// ${filePath} (${content.split("\n").length} lines)`,
191
310
  "",
192
- ...signatures,
193
- "",
194
311
  "// Exports:",
195
- ...exports
196
- ];
197
- const compressed = lines.join("\n");
312
+ exports,
313
+ "",
314
+ "// Classes:",
315
+ classes,
316
+ "",
317
+ "// Functions:",
318
+ functions,
319
+ "",
320
+ "// Types:",
321
+ types
322
+ ].filter((l) => l !== "" && !l.endsWith(":"));
323
+ let compressed = sections.join("\n");
324
+ const maxTokens = 2e3;
325
+ compressed = smartTruncate(compressed, maxTokens);
198
326
  return {
199
327
  content: compressed,
200
328
  original: content,
201
329
  confidence: calculateConfidence(content, compressed),
202
330
  metadata: {
203
- strategy: "signatures",
331
+ strategy: "hybrid",
204
332
  originalLines: content.split("\n").length,
205
- compressedLines: lines.length,
333
+ compressedLines: compressed.split("\n").length,
206
334
  savings: 1 - compressed.length / content.length
207
335
  }
208
336
  };
@@ -210,50 +338,27 @@ var FileReadCompressor = class {
210
338
  async decompress(compressed) {
211
339
  return compressed.original;
212
340
  }
213
- extractSignatures(content, language) {
214
- const signatures = [];
215
- const lines = content.split("\n");
216
- for (const line of lines) {
217
- const trimmed = line.trim();
218
- if (/^(export\s+)?(async\s+)?function\s+\w+/.test(trimmed)) {
219
- signatures.push(trimmed.replace(/\{[\s\S]*$/, "{ ... }"));
220
- }
221
- if (/^(export\s+)?class\s+\w+/.test(trimmed)) {
222
- signatures.push(trimmed);
223
- }
224
- if (/^(export\s+)?(type|interface)\s+\w+/.test(trimmed)) {
225
- signatures.push(trimmed);
226
- }
227
- }
228
- return signatures;
229
- }
230
- extractExports(content) {
231
- const exports = [];
232
- const exportRegex = /export\s+(default\s+)?(function|class|const|let|var|type|interface)\s+(\w+)/g;
233
- let match;
234
- while ((match = exportRegex.exec(content)) !== null) {
235
- exports.push(` - ${match[3]} (${match[2]})`);
236
- }
237
- return exports;
238
- }
239
341
  };
240
342
 
241
343
  // src/compress/git-diff.ts
242
344
  var GitDiffCompressor = class {
243
345
  async compress(input) {
244
346
  const { diff } = input;
245
- const hunks = this.parseDiff(diff);
246
- const condensed = hunks.map((hunk) => ({
247
- file: hunk.file,
248
- changes: this.condenseHunk(hunk),
249
- stats: { additions: hunk.additions, deletions: hunk.deletions }
250
- }));
347
+ const files = this.parseDiff(diff);
251
348
  const lines = [];
252
- for (const hunk of condensed) {
253
- if (hunk.changes.length > 0) {
254
- lines.push(`${hunk.file} (+${hunk.stats.additions} -${hunk.stats.deletions})`);
255
- lines.push(...hunk.changes);
349
+ const totalAdd = files.reduce((s, f) => s + f.additions, 0);
350
+ const totalDel = files.reduce((s, f) => s + f.deletions, 0);
351
+ lines.push(`# ${files.length} files changed: +${totalAdd} -${totalDel}`);
352
+ lines.push("");
353
+ for (const file of files) {
354
+ lines.push(`${file.path} (+${file.additions} -${file.deletions})`);
355
+ const changes = file.lines.filter((l) => l.type === "add" || l.type === "del").map((l) => `${l.type === "add" ? "+" : "-"}${l.content}`);
356
+ const limited = changes.slice(0, 20);
357
+ lines.push(...limited);
358
+ if (changes.length > 20) {
359
+ lines.push(`... [${changes.length - 20} more changes]`);
256
360
  }
361
+ lines.push("");
257
362
  }
258
363
  const compressed = lines.join("\n");
259
364
  return {
@@ -262,7 +367,7 @@ var GitDiffCompressor = class {
262
367
  confidence: calculateConfidence(diff, compressed),
263
368
  metadata: {
264
369
  strategy: "condensed",
265
- filesChanged: condensed.length,
370
+ filesChanged: files.length,
266
371
  savings: 1 - compressed.length / diff.length
267
372
  }
268
373
  };
@@ -271,34 +376,123 @@ var GitDiffCompressor = class {
271
376
  return compressed.original;
272
377
  }
273
378
  parseDiff(diff) {
274
- const hunks = [];
379
+ const files = [];
275
380
  const lines = diff.split("\n");
276
- let currentHunk = null;
381
+ let current = null;
277
382
  for (const line of lines) {
278
383
  if (line.startsWith("diff --git")) {
279
- if (currentHunk) hunks.push(currentHunk);
280
- const fileMatch = line.match(/b\/(.+)/);
281
- currentHunk = {
282
- file: fileMatch?.[1] || "unknown",
283
- lines: [],
384
+ if (current) files.push(current);
385
+ const match = line.match(/b\/(.+)/);
386
+ current = {
387
+ path: match?.[1] || "unknown",
284
388
  additions: 0,
285
- deletions: 0
389
+ deletions: 0,
390
+ lines: []
286
391
  };
287
- } else if (currentHunk) {
392
+ } else if (current) {
288
393
  if (line.startsWith("+")) {
289
- currentHunk.additions++;
290
- currentHunk.lines.push({ type: "addition", content: line.slice(1) });
394
+ current.additions++;
395
+ current.lines.push({ type: "add", content: line.slice(1) });
291
396
  } else if (line.startsWith("-")) {
292
- currentHunk.deletions++;
293
- currentHunk.lines.push({ type: "deletion", content: line.slice(1) });
397
+ current.deletions++;
398
+ current.lines.push({ type: "del", content: line.slice(1) });
294
399
  }
295
400
  }
296
401
  }
297
- if (currentHunk) hunks.push(currentHunk);
298
- return hunks;
402
+ if (current) files.push(current);
403
+ return files;
299
404
  }
300
- condenseHunk(hunk) {
301
- return hunk.lines.filter((l) => l.type === "addition" || l.type === "deletion").map((l) => `${l.type === "addition" ? "+" : "-"} ${l.content}`);
405
+ };
406
+
407
+ // src/compress/git-status.ts
408
+ var GitStatusCompressor = class {
409
+ async compress(input) {
410
+ const { status } = input;
411
+ const statusLines = status.split("\n").filter(Boolean).length;
412
+ const tokens = Math.ceil(status.length / 4);
413
+ if (tokens < 10) {
414
+ return {
415
+ content: status,
416
+ original: status,
417
+ confidence: 1,
418
+ metadata: {
419
+ strategy: "passthrough",
420
+ totalFiles: statusLines,
421
+ savings: 0
422
+ }
423
+ };
424
+ }
425
+ const files = this.parseStatus(status);
426
+ const grouped = this.groupByStatus(files);
427
+ const lines = [];
428
+ for (const [statusType, statusFiles] of grouped) {
429
+ const label = this.statusLabel(statusType);
430
+ lines.push(`${label} (${statusFiles.length})`);
431
+ const compressed2 = this.compressPaths(statusFiles.map((f) => f.path));
432
+ lines.push(...compressed2);
433
+ lines.push("");
434
+ }
435
+ const compressed = lines.join("\n");
436
+ return {
437
+ content: compressed,
438
+ original: status,
439
+ confidence: calculateConfidence(status, compressed),
440
+ metadata: {
441
+ strategy: "grouped",
442
+ totalFiles: files.length,
443
+ savings: 1 - compressed.length / status.length
444
+ }
445
+ };
446
+ }
447
+ async decompress(compressed) {
448
+ return compressed.original;
449
+ }
450
+ parseStatus(status) {
451
+ return status.split("\n").filter(Boolean).map((line) => {
452
+ const match = line.match(/^(\?\?|[MADRC]+\s+)(.+)$/);
453
+ if (match) {
454
+ return { status: match[1].trim(), path: match[2] };
455
+ }
456
+ return { status: line[0], path: line.slice(3) };
457
+ });
458
+ }
459
+ groupByStatus(files) {
460
+ const grouped = /* @__PURE__ */ new Map();
461
+ for (const file of files) {
462
+ const existing = grouped.get(file.status) || [];
463
+ existing.push(file);
464
+ grouped.set(file.status, existing);
465
+ }
466
+ return grouped;
467
+ }
468
+ statusLabel(status) {
469
+ const labels = {
470
+ "M": "Modified",
471
+ "A": "Added",
472
+ "D": "Deleted",
473
+ "??": "Untracked",
474
+ "R": "Renamed"
475
+ };
476
+ return labels[status] || `Status ${status}`;
477
+ }
478
+ compressPaths(paths) {
479
+ if (paths.length <= 3) return paths.map((p) => ` ${p}`);
480
+ const parts = paths.map((p) => p.split("/"));
481
+ const minLength = Math.min(...parts.map((p) => p.length));
482
+ let commonIndex = 0;
483
+ for (let i = 0; i < minLength; i++) {
484
+ if (parts.every((p) => p[i] === parts[0][i])) {
485
+ commonIndex = i;
486
+ } else {
487
+ break;
488
+ }
489
+ }
490
+ if (commonIndex > 0) {
491
+ const prefix = parts[0].slice(0, commonIndex).join("/");
492
+ const suffixes = paths.map((p) => p.slice(prefix.length + 1));
493
+ return [` ${prefix}/`, ...suffixes.map((s) => ` ${s}`)];
494
+ }
495
+ return paths.map((p) => ` ${p}`);
302
496
  }
303
497
  };
304
498
 
@@ -2031,7 +2225,7 @@ function compressWebContent(semantic, options = {}) {
2031
2225
  let result = lines.join("\n");
2032
2226
  const estimatedTokens = estimateTokens(result);
2033
2227
  if (estimatedTokens > maxTokens) {
2034
- result = truncateByTokens(result, maxTokens);
2228
+ result = truncateByTokens2(result, maxTokens);
2035
2229
  }
2036
2230
  return result;
2037
2231
  }
@@ -2047,7 +2241,7 @@ function truncateContent(content, maxLength) {
2047
2241
  function estimateTokens(text) {
2048
2242
  return Math.ceil(text.length / 4);
2049
2243
  }
2050
- function truncateByTokens(text, maxTokens) {
2244
+ function truncateByTokens2(text, maxTokens) {
2051
2245
  const maxChars = maxTokens * 4;
2052
2246
  if (text.length <= maxChars) return text;
2053
2247
  const truncated = text.slice(0, maxChars);
@@ -2184,6 +2378,7 @@ export {
2184
2378
  FileReadCompressor,
2185
2379
  GeminiCliAdapter,
2186
2380
  GitDiffCompressor,
2381
+ GitStatusCompressor,
2187
2382
  GrepCompressor,
2188
2383
  HookAdapter,
2189
2384
  Installer,
@@ -2218,12 +2413,24 @@ export {
2218
2413
  createRouter,
2219
2414
  createRulesAdapter,
2220
2415
  createToolRegistry,
2416
+ extractClassMethods,
2417
+ extractExports,
2418
+ extractFunctionSignatures,
2221
2419
  extractSemantic,
2420
+ extractTypeDefinitions,
2222
2421
  getAdapter,
2223
2422
  getAdapters,
2224
2423
  getSemanticTools,
2225
2424
  makeLatenCache,
2425
+ preserveContext,
2226
2426
  registerAdapter,
2227
- selectBackend
2427
+ selectBackend,
2428
+ smartTruncate,
2429
+ stripAll,
2430
+ stripBlankLines,
2431
+ stripComments,
2432
+ stripImports,
2433
+ stripWhitespace,
2434
+ truncateByTokens
2228
2435
  };
2229
2436
  //# sourceMappingURL=index.mjs.map