@rely-ai/caliber 1.15.3 → 1.17.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.
Files changed (2) hide show
  1. package/dist/bin.js +164 -14
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -503,47 +503,187 @@ var SKIP_PATTERNS = [
503
503
  /\.generated\./,
504
504
  /\.snap$/
505
505
  ];
506
+ var COMMENT_LINE_PATTERNS = {
507
+ "c-style": /^\s*\/\//,
508
+ // ts, js, go, rs, java, etc.
509
+ "hash": /^\s*#/,
510
+ // py, sh, yaml, tf, etc.
511
+ "html": /^\s*<!--.*-->\s*$/
512
+ };
513
+ var EXT_TO_COMMENT_STYLE = {
514
+ ".ts": "c-style",
515
+ ".tsx": "c-style",
516
+ ".js": "c-style",
517
+ ".jsx": "c-style",
518
+ ".mjs": "c-style",
519
+ ".cjs": "c-style",
520
+ ".go": "c-style",
521
+ ".rs": "c-style",
522
+ ".java": "c-style",
523
+ ".kt": "c-style",
524
+ ".scala": "c-style",
525
+ ".cs": "c-style",
526
+ ".c": "c-style",
527
+ ".cpp": "c-style",
528
+ ".h": "c-style",
529
+ ".hpp": "c-style",
530
+ ".swift": "c-style",
531
+ ".php": "c-style",
532
+ ".py": "hash",
533
+ ".pyw": "hash",
534
+ ".rb": "hash",
535
+ ".sh": "hash",
536
+ ".bash": "hash",
537
+ ".zsh": "hash",
538
+ ".fish": "hash",
539
+ ".r": "hash",
540
+ ".tf": "hash",
541
+ ".hcl": "hash",
542
+ ".yaml": "hash",
543
+ ".yml": "hash",
544
+ ".toml": "hash",
545
+ ".ini": "hash",
546
+ ".cfg": "hash",
547
+ ".env": "hash",
548
+ ".html": "html",
549
+ ".xml": "html",
550
+ ".svg": "html",
551
+ ".vue": "html",
552
+ ".svelte": "html"
553
+ };
506
554
  var TOKEN_BUDGET = 18e4;
507
555
  var CHAR_BUDGET = TOKEN_BUDGET * 4;
556
+ function compressContent(content, ext) {
557
+ const commentStyle = EXT_TO_COMMENT_STYLE[ext];
558
+ const commentPattern = commentStyle ? COMMENT_LINE_PATTERNS[commentStyle] : null;
559
+ const lines = content.split("\n");
560
+ const result = [];
561
+ let prevBlank = false;
562
+ let inBlockComment = false;
563
+ for (const line of lines) {
564
+ const trimmed = line.trimEnd();
565
+ if (!inBlockComment && /^\s*\/\*/.test(trimmed) && !trimmed.includes("*/")) {
566
+ inBlockComment = true;
567
+ continue;
568
+ }
569
+ if (inBlockComment) {
570
+ if (trimmed.includes("*/")) inBlockComment = false;
571
+ continue;
572
+ }
573
+ if (/^\s*\/\*.*\*\/\s*$/.test(trimmed)) continue;
574
+ if (trimmed.length === 0) {
575
+ if (!prevBlank) result.push("");
576
+ prevBlank = true;
577
+ continue;
578
+ }
579
+ prevBlank = false;
580
+ if (commentPattern && commentPattern.test(trimmed)) continue;
581
+ const leadingMatch = line.match(/^(\s*)/);
582
+ if (leadingMatch) {
583
+ const spaces = leadingMatch[1].replace(/\t/g, " ").length;
584
+ const normalizedIndent = " ".repeat(Math.floor(spaces / 2) * 2);
585
+ result.push(normalizedIndent + line.trimStart().trimEnd());
586
+ } else {
587
+ result.push(trimmed);
588
+ }
589
+ }
590
+ while (result.length > 0 && result[result.length - 1] === "") result.pop();
591
+ return result.join("\n");
592
+ }
593
+ function structuralFingerprint(content, ext) {
594
+ const lines = content.split("\n").filter((l) => l.trim().length > 0);
595
+ const lineCount = lines.length;
596
+ const sizeBucket = Math.floor(lineCount / 10) * 10;
597
+ const firstLine = lines[0]?.trim().slice(0, 60) || "";
598
+ const imports = lines.filter((l) => /^\s*(import |from |require\(|use )/.test(l)).length;
599
+ const exports = lines.filter((l) => /^\s*export /.test(l)).length;
600
+ const functions = lines.filter((l) => /^\s*(function |def |func |fn |pub fn )/.test(l)).length;
601
+ return `${ext}:${sizeBucket}:${imports}:${exports}:${functions}:${firstLine}`;
602
+ }
603
+ function deduplicateFiles(files) {
604
+ const groups = /* @__PURE__ */ new Map();
605
+ for (const f of files) {
606
+ const fp = structuralFingerprint(f.content, f.ext);
607
+ const group = groups.get(fp) || [];
608
+ group.push({ path: f.path, content: f.content });
609
+ groups.set(fp, group);
610
+ }
611
+ const result = [];
612
+ for (const [, group] of groups) {
613
+ const representative = group[0];
614
+ result.push({
615
+ path: representative.path,
616
+ content: representative.content,
617
+ size: representative.content.length
618
+ });
619
+ if (group.length > 1) {
620
+ const similarPaths = group.slice(1).map((f) => f.path);
621
+ const summary = `(${similarPaths.length} similar file${similarPaths.length === 1 ? "" : "s"}: ${similarPaths.join(", ")})`;
622
+ result.push({
623
+ path: `[similar to ${representative.path}]`,
624
+ content: summary,
625
+ size: summary.length
626
+ });
627
+ }
628
+ }
629
+ return result;
630
+ }
508
631
  function analyzeCode(dir) {
509
632
  const allFiles = [];
510
633
  walkDir(dir, "", 0, 10, allFiles);
511
634
  sortByPriority(allFiles);
512
635
  let totalChars = 0;
513
- let includedChars = 0;
514
- let truncated = false;
515
- const files = [];
516
- const fileSizes = /* @__PURE__ */ new Map();
517
636
  for (const relPath of allFiles) {
518
637
  try {
519
638
  const stat = fs3.statSync(path3.join(dir, relPath));
520
- fileSizes.set(relPath, stat.size);
521
639
  totalChars += stat.size;
522
640
  } catch {
523
641
  }
524
642
  }
643
+ const readFiles = [];
644
+ let compressedChars = 0;
525
645
  for (const relPath of allFiles) {
526
646
  const fullPath = path3.join(dir, relPath);
527
- let content;
647
+ let rawContent;
528
648
  try {
529
- content = fs3.readFileSync(fullPath, "utf-8");
649
+ rawContent = fs3.readFileSync(fullPath, "utf-8");
530
650
  } catch {
531
651
  continue;
532
652
  }
533
- if (content.split("\n").length > 500) continue;
534
- const entrySize = relPath.length + content.length + 10;
653
+ if (rawContent.split("\n").length > 500) continue;
654
+ const ext = path3.extname(relPath).toLowerCase();
655
+ const compressed = compressContent(rawContent, ext);
656
+ compressedChars += compressed.length;
657
+ readFiles.push({
658
+ path: relPath,
659
+ content: compressed,
660
+ ext,
661
+ rawSize: rawContent.length
662
+ });
663
+ }
664
+ const deduped = deduplicateFiles(readFiles);
665
+ const dupGroups = deduped.filter((f) => f.path.startsWith("[similar")).length;
666
+ let includedChars = 0;
667
+ let truncated = false;
668
+ const files = [];
669
+ for (const file of deduped) {
670
+ const entrySize = file.path.length + file.content.length + 10;
535
671
  if (includedChars + entrySize > CHAR_BUDGET) {
536
672
  truncated = true;
537
673
  continue;
538
674
  }
539
- files.push({ path: relPath, content, size: content.length });
675
+ files.push(file);
540
676
  includedChars += entrySize;
541
677
  }
542
678
  return {
543
679
  files,
544
680
  truncated,
545
681
  totalProjectTokens: Math.ceil(totalChars / 4),
546
- includedTokens: Math.ceil(includedChars / 4)
682
+ compressedTokens: Math.ceil(compressedChars / 4),
683
+ includedTokens: Math.ceil(includedChars / 4),
684
+ filesAnalyzed: readFiles.length,
685
+ filesIncluded: files.length,
686
+ duplicateGroups: dupGroups
547
687
  };
548
688
  }
549
689
  function walkDir(base, rel, depth, maxDepth, files) {
@@ -6036,15 +6176,25 @@ async function initCommand(options) {
6036
6176
  spinner.succeed("Project analyzed");
6037
6177
  log(options.verbose, `Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`);
6038
6178
  if (options.verbose && fingerprint.codeAnalysis) {
6039
- log(options.verbose, `Code analysis: ${fingerprint.codeAnalysis.files.length} files, ~${fingerprint.codeAnalysis.includedTokens.toLocaleString()} tokens${fingerprint.codeAnalysis.truncated ? ` (trimmed from ~${fingerprint.codeAnalysis.totalProjectTokens.toLocaleString()})` : ""}`);
6179
+ log(options.verbose, `Code analysis: ${fingerprint.codeAnalysis.filesIncluded}/${fingerprint.codeAnalysis.filesAnalyzed} files, ~${fingerprint.codeAnalysis.includedTokens.toLocaleString()} tokens, ${fingerprint.codeAnalysis.duplicateGroups} dedup groups`);
6040
6180
  }
6041
6181
  trackInitProjectDiscovered(fingerprint.languages.length, fingerprint.frameworks.length, fingerprint.fileTree.length);
6042
6182
  console.log(chalk8.dim(` Languages: ${fingerprint.languages.join(", ") || "none detected"}`));
6043
6183
  console.log(chalk8.dim(` Files: ${fingerprint.fileTree.length} found`));
6044
6184
  if (fingerprint.codeAnalysis) {
6045
6185
  const ca = fingerprint.codeAnalysis;
6046
- const contextInfo = ca.truncated ? `Context: ~${ca.includedTokens.toLocaleString()} tokens (${Math.round(ca.includedTokens / ca.totalProjectTokens * 100)}% of ${ca.totalProjectTokens.toLocaleString()} total)` : `Context: ~${ca.includedTokens.toLocaleString()} tokens`;
6047
- console.log(chalk8.dim(` ${contextInfo}`));
6186
+ const compressionPct = ca.totalProjectTokens > 0 ? Math.round((1 - ca.compressedTokens / ca.totalProjectTokens) * 100) : 0;
6187
+ const parts = [`Context: ~${ca.includedTokens.toLocaleString()} tokens sent`];
6188
+ if (ca.truncated) {
6189
+ parts.push(`(${Math.round(ca.includedTokens / ca.totalProjectTokens * 100)}% of ${ca.totalProjectTokens.toLocaleString()} total)`);
6190
+ }
6191
+ if (compressionPct > 5) {
6192
+ parts.push(`compressed ${compressionPct}%`);
6193
+ }
6194
+ if (ca.duplicateGroups > 0) {
6195
+ parts.push(`${ca.duplicateGroups} duplicate group${ca.duplicateGroups === 1 ? "" : "s"} merged`);
6196
+ }
6197
+ console.log(chalk8.dim(` ${parts.join(" \xB7 ")}`));
6048
6198
  }
6049
6199
  console.log("");
6050
6200
  if (report) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rely-ai/caliber",
3
- "version": "1.15.3",
3
+ "version": "1.17.0",
4
4
  "description": "Analyze your codebase and generate optimized AI agent configs (CLAUDE.md, .cursorrules, skills) — no API key needed",
5
5
  "type": "module",
6
6
  "bin": {