@rely-ai/caliber 1.17.0 → 1.18.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 +340 -174
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -396,6 +396,7 @@ function readExistingConfigs(dir) {
396
396
  // src/fingerprint/code-analysis.ts
397
397
  import fs3 from "fs";
398
398
  import path3 from "path";
399
+ import { execSync as execSync2 } from "child_process";
399
400
  var IGNORE_DIRS2 = /* @__PURE__ */ new Set([
400
401
  "node_modules",
401
402
  ".git",
@@ -503,59 +504,78 @@ var SKIP_PATTERNS = [
503
504
  /\.generated\./,
504
505
  /\.snap$/
505
506
  ];
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*$/
507
+ var COMMENT_LINE = {
508
+ "c": /^\s*\/\//,
509
+ "h": /^\s*#/,
510
+ "x": /^\s*<!--.*-->\s*$/
512
511
  };
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"
512
+ var EXT_COMMENT = {
513
+ ".ts": "c",
514
+ ".tsx": "c",
515
+ ".js": "c",
516
+ ".jsx": "c",
517
+ ".mjs": "c",
518
+ ".cjs": "c",
519
+ ".go": "c",
520
+ ".rs": "c",
521
+ ".java": "c",
522
+ ".kt": "c",
523
+ ".scala": "c",
524
+ ".cs": "c",
525
+ ".c": "c",
526
+ ".cpp": "c",
527
+ ".h": "c",
528
+ ".hpp": "c",
529
+ ".swift": "c",
530
+ ".php": "c",
531
+ ".py": "h",
532
+ ".pyw": "h",
533
+ ".rb": "h",
534
+ ".sh": "h",
535
+ ".bash": "h",
536
+ ".zsh": "h",
537
+ ".fish": "h",
538
+ ".r": "h",
539
+ ".tf": "h",
540
+ ".hcl": "h",
541
+ ".yaml": "h",
542
+ ".yml": "h",
543
+ ".toml": "h",
544
+ ".ini": "h",
545
+ ".cfg": "h",
546
+ ".env": "h",
547
+ ".html": "x",
548
+ ".xml": "x",
549
+ ".vue": "x",
550
+ ".svelte": "x"
553
551
  };
552
+ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
553
+ ".ts",
554
+ ".tsx",
555
+ ".js",
556
+ ".jsx",
557
+ ".mjs",
558
+ ".cjs",
559
+ ".py",
560
+ ".pyw",
561
+ ".go",
562
+ ".rs",
563
+ ".rb",
564
+ ".java",
565
+ ".kt",
566
+ ".scala",
567
+ ".cs",
568
+ ".c",
569
+ ".cpp",
570
+ ".h",
571
+ ".hpp",
572
+ ".swift",
573
+ ".php"
574
+ ]);
554
575
  var TOKEN_BUDGET = 18e4;
555
576
  var CHAR_BUDGET = TOKEN_BUDGET * 4;
556
577
  function compressContent(content, ext) {
557
- const commentStyle = EXT_TO_COMMENT_STYLE[ext];
558
- const commentPattern = commentStyle ? COMMENT_LINE_PATTERNS[commentStyle] : null;
578
+ const cp = EXT_COMMENT[ext] ? COMMENT_LINE[EXT_COMMENT[ext]] : null;
559
579
  const lines = content.split("\n");
560
580
  const result = [];
561
581
  let prevBlank = false;
@@ -577,12 +597,11 @@ function compressContent(content, ext) {
577
597
  continue;
578
598
  }
579
599
  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());
600
+ if (cp && cp.test(trimmed)) continue;
601
+ const leading = line.match(/^(\s*)/);
602
+ if (leading) {
603
+ const spaces = leading[1].replace(/\t/g, " ").length;
604
+ result.push(" ".repeat(Math.floor(spaces / 2) * 2) + line.trimStart().trimEnd());
586
605
  } else {
587
606
  result.push(trimmed);
588
607
  }
@@ -590,99 +609,254 @@ function compressContent(content, ext) {
590
609
  while (result.length > 0 && result[result.length - 1] === "") result.pop();
591
610
  return result.join("\n");
592
611
  }
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);
612
+ function extractSkeleton(content, ext) {
613
+ if (!SOURCE_EXTENSIONS.has(ext)) return content;
614
+ const lines = content.split("\n");
615
+ const result = [];
616
+ let braceDepth = 0;
617
+ let inSignature = false;
618
+ let skipBody = false;
619
+ if ([".py", ".pyw", ".rb"].includes(ext)) {
620
+ return extractSkeletonIndentBased(lines, ext);
621
+ }
622
+ for (let i = 0; i < lines.length; i++) {
623
+ const line = lines[i];
624
+ const trimmed = line.trim();
625
+ if (/^\s*(import |from |require\(|use |package |module )/.test(line)) {
626
+ result.push(line);
627
+ continue;
628
+ }
629
+ if (/^\s*(export\s+)?(interface|type|enum)\s/.test(trimmed)) {
630
+ result.push(line);
631
+ let depth = 0;
632
+ for (let j = i; j < lines.length; j++) {
633
+ if (j > i) result.push(lines[j]);
634
+ depth += (lines[j].match(/{/g) || []).length;
635
+ depth -= (lines[j].match(/}/g) || []).length;
636
+ if (depth <= 0 && j > i) {
637
+ i = j;
638
+ break;
639
+ }
640
+ }
641
+ continue;
642
+ }
643
+ const isFnOrClass = /^\s*(export\s+)?(default\s+)?(async\s+)?(function|class|const\s+\w+\s*=\s*(async\s*)?\(|pub\s+fn|fn|func)\s/.test(trimmed) || /^\s*(def|func|fn|pub fn|pub async fn)\s/.test(trimmed);
644
+ if (isFnOrClass && braceDepth === 0) {
645
+ result.push(line);
646
+ const opens = (line.match(/{/g) || []).length;
647
+ const closes = (line.match(/}/g) || []).length;
648
+ if (opens > closes) {
649
+ skipBody = true;
650
+ braceDepth = opens - closes;
651
+ result.push(" ".repeat(line.search(/\S/)) + " // ...");
652
+ }
653
+ continue;
654
+ }
655
+ if (skipBody) {
656
+ braceDepth += (line.match(/{/g) || []).length;
657
+ braceDepth -= (line.match(/}/g) || []).length;
658
+ if (braceDepth <= 0) {
659
+ result.push(line);
660
+ skipBody = false;
661
+ braceDepth = 0;
662
+ }
663
+ continue;
664
+ }
665
+ if (braceDepth === 0) {
666
+ result.push(line);
667
+ }
610
668
  }
669
+ return result.join("\n");
670
+ }
671
+ function extractSkeletonIndentBased(lines, ext) {
611
672
  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
- });
673
+ let skipIndent = -1;
674
+ for (const line of lines) {
675
+ const trimmed = line.trim();
676
+ if (trimmed.length === 0) continue;
677
+ const indent = line.search(/\S/);
678
+ if (/^(import |from )/.test(trimmed)) {
679
+ result.push(line);
680
+ skipIndent = -1;
681
+ continue;
682
+ }
683
+ if (/^(class |def |async def )/.test(trimmed)) {
684
+ result.push(line);
685
+ skipIndent = indent;
686
+ continue;
687
+ }
688
+ if (trimmed.startsWith("@")) {
689
+ result.push(line);
690
+ skipIndent = -1;
691
+ continue;
692
+ }
693
+ if (skipIndent >= 0 && indent > skipIndent) {
694
+ continue;
695
+ }
696
+ skipIndent = -1;
697
+ result.push(line);
698
+ }
699
+ return result.join("\n");
700
+ }
701
+ function extractImports(content, filePath) {
702
+ const imports = [];
703
+ const dir = path3.dirname(filePath);
704
+ for (const line of content.split("\n")) {
705
+ const trimmed = line.trim();
706
+ const jsMatch = trimmed.match(/(?:from|require\()\s*['"]([^'"]+)['"]/);
707
+ if (jsMatch && jsMatch[1].startsWith(".")) {
708
+ imports.push(path3.normalize(path3.join(dir, jsMatch[1])));
709
+ continue;
710
+ }
711
+ const pyMatch = trimmed.match(/^from\s+(\.[.\w]*)\s+import/);
712
+ if (pyMatch) {
713
+ const modulePath = pyMatch[1].replace(/\./g, "/");
714
+ imports.push(path3.normalize(path3.join(dir, modulePath)));
715
+ continue;
716
+ }
717
+ const goMatch = trimmed.match(/^\s*"([^"]+)"/);
718
+ if (goMatch && !goMatch[1].includes(".")) {
719
+ imports.push(goMatch[1]);
720
+ }
721
+ }
722
+ return imports;
723
+ }
724
+ function buildImportCounts(files) {
725
+ const counts = /* @__PURE__ */ new Map();
726
+ for (const [filePath, content] of files) {
727
+ const ext = path3.extname(filePath).toLowerCase();
728
+ if (!SOURCE_EXTENSIONS.has(ext)) continue;
729
+ const imports = extractImports(content, filePath);
730
+ for (const imp of imports) {
731
+ const candidates = [imp, imp + ".ts", imp + ".js", imp + ".tsx", imp + ".jsx", imp + "/index.ts", imp + "/index.js", imp + ".py"];
732
+ for (const candidate of candidates) {
733
+ const normalized = candidate.replace(/\\/g, "/");
734
+ if (files.has(normalized)) {
735
+ counts.set(normalized, (counts.get(normalized) || 0) + 1);
736
+ break;
737
+ }
738
+ }
627
739
  }
628
740
  }
629
- return result;
741
+ return counts;
742
+ }
743
+ function getGitFrequency(dir) {
744
+ const freq = /* @__PURE__ */ new Map();
745
+ try {
746
+ const output = execSync2(
747
+ 'git log --since="6 months ago" --format="" --name-only --diff-filter=ACMR 2>/dev/null | head -10000',
748
+ { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 5e3 }
749
+ );
750
+ for (const line of output.split("\n")) {
751
+ const trimmed = line.trim();
752
+ if (trimmed) freq.set(trimmed, (freq.get(trimmed) || 0) + 1);
753
+ }
754
+ } catch {
755
+ }
756
+ return freq;
757
+ }
758
+ function groupByDirectory(files) {
759
+ const groups = /* @__PURE__ */ new Map();
760
+ for (const f of files) {
761
+ const dir = path3.dirname(f.path);
762
+ const group = groups.get(dir) || [];
763
+ group.push(f);
764
+ groups.set(dir, group);
765
+ }
766
+ return groups;
767
+ }
768
+ function structuralFingerprint(content, ext) {
769
+ const lines = content.split("\n").filter((l) => l.trim().length > 0);
770
+ const bucket = Math.floor(lines.length / 10) * 10;
771
+ const first = (lines[0] || "").trim().slice(0, 50);
772
+ const imports = lines.filter((l) => /^\s*(import |from |require\(|use )/.test(l)).length;
773
+ const fns = lines.filter((l) => /^\s*(export\s+)?(async\s+)?(function |def |func |fn |pub fn |class )/.test(l)).length;
774
+ return `${ext}:${bucket}:${imports}:${fns}:${first}`;
630
775
  }
631
776
  function analyzeCode(dir) {
632
- const allFiles = [];
633
- walkDir(dir, "", 0, 10, allFiles);
634
- sortByPriority(allFiles);
777
+ const allPaths = [];
778
+ walkDir(dir, "", 0, 10, allPaths);
635
779
  let totalChars = 0;
636
- for (const relPath of allFiles) {
780
+ for (const relPath of allPaths) {
637
781
  try {
638
- const stat = fs3.statSync(path3.join(dir, relPath));
639
- totalChars += stat.size;
782
+ totalChars += fs3.statSync(path3.join(dir, relPath)).size;
640
783
  } catch {
641
784
  }
642
785
  }
643
- const readFiles = [];
644
- let compressedChars = 0;
645
- for (const relPath of allFiles) {
646
- const fullPath = path3.join(dir, relPath);
647
- let rawContent;
786
+ const fileContents = /* @__PURE__ */ new Map();
787
+ for (const relPath of allPaths) {
648
788
  try {
649
- rawContent = fs3.readFileSync(fullPath, "utf-8");
789
+ const content = fs3.readFileSync(path3.join(dir, relPath), "utf-8");
790
+ if (content.split("\n").length <= 500) fileContents.set(relPath, content);
650
791
  } catch {
651
- continue;
652
792
  }
653
- if (rawContent.split("\n").length > 500) continue;
793
+ }
794
+ const importCounts = buildImportCounts(fileContents);
795
+ const gitFreq = getGitFrequency(dir);
796
+ const scored = [];
797
+ let compressedChars = 0;
798
+ for (const [relPath, rawContent] of fileContents) {
654
799
  const ext = path3.extname(relPath).toLowerCase();
655
800
  const compressed = compressContent(rawContent, ext);
801
+ const skeleton = extractSkeleton(compressed, ext);
656
802
  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;
803
+ const priorityScore = filePriority(relPath);
804
+ const importScore = Math.min(importCounts.get(relPath) || 0, 20) * 2;
805
+ const gitScore = Math.min(gitFreq.get(relPath) || 0, 10) * 3;
806
+ const score = priorityScore + importScore + gitScore;
807
+ scored.push({ path: relPath, rawContent, compressed, skeleton, ext, score });
808
+ }
809
+ scored.sort((a, b) => b.score - a.score);
810
+ const dirGroups = groupByDirectory(scored);
811
+ const result = [];
666
812
  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;
671
- if (includedChars + entrySize > CHAR_BUDGET) {
672
- truncated = true;
673
- continue;
813
+ let dupGroups = 0;
814
+ for (const [dirPath, group] of dirGroups) {
815
+ if (group.length === 0) continue;
816
+ const rep = group[0];
817
+ const repFP = structuralFingerprint(rep.compressed, rep.ext);
818
+ const similar = group.slice(1).filter((f) => structuralFingerprint(f.compressed, f.ext) === repFP);
819
+ const unique = group.slice(1).filter((f) => structuralFingerprint(f.compressed, f.ext) !== repFP);
820
+ const repEntry = { path: rep.path, content: rep.compressed, size: rep.compressed.length };
821
+ const repSize = rep.path.length + rep.compressed.length + 10;
822
+ if (includedChars + repSize <= CHAR_BUDGET) {
823
+ result.push(repEntry);
824
+ includedChars += repSize;
825
+ }
826
+ if (similar.length > 0) {
827
+ dupGroups++;
828
+ const names = similar.map((f) => path3.basename(f.path));
829
+ const summary = `(${similar.length} similar file${similar.length === 1 ? "" : "s"} in ${dirPath}/: ${names.join(", ")})`;
830
+ const summarySize = summary.length + 30;
831
+ if (includedChars + summarySize <= CHAR_BUDGET) {
832
+ result.push({ path: `[similar to ${rep.path}]`, content: summary, size: summary.length });
833
+ includedChars += summarySize;
834
+ }
835
+ }
836
+ for (const f of unique) {
837
+ const skeletonSize = f.path.length + f.skeleton.length + 10;
838
+ if (includedChars + skeletonSize <= CHAR_BUDGET) {
839
+ result.push({ path: f.path, content: f.skeleton, size: f.skeleton.length });
840
+ includedChars += skeletonSize;
841
+ }
674
842
  }
675
- files.push(file);
676
- includedChars += entrySize;
843
+ }
844
+ const includedPaths = new Set(result.map((f) => f.path));
845
+ for (const f of scored) {
846
+ if (includedPaths.has(f.path)) continue;
847
+ const skeletonSize = f.path.length + f.skeleton.length + 10;
848
+ if (includedChars + skeletonSize > CHAR_BUDGET) continue;
849
+ result.push({ path: f.path, content: f.skeleton, size: f.skeleton.length });
850
+ includedChars += skeletonSize;
677
851
  }
678
852
  return {
679
- files,
680
- truncated,
853
+ files: result,
854
+ truncated: includedChars >= CHAR_BUDGET * 0.95,
681
855
  totalProjectTokens: Math.ceil(totalChars / 4),
682
856
  compressedTokens: Math.ceil(compressedChars / 4),
683
857
  includedTokens: Math.ceil(includedChars / 4),
684
- filesAnalyzed: readFiles.length,
685
- filesIncluded: files.length,
858
+ filesAnalyzed: fileContents.size,
859
+ filesIncluded: result.length,
686
860
  duplicateGroups: dupGroups
687
861
  };
688
862
  }
@@ -711,8 +885,9 @@ function walkDir(base, rel, depth, maxDepth, files) {
711
885
  }
712
886
  }
713
887
  }
714
- function sortByPriority(files) {
715
- const entryPointNames = /* @__PURE__ */ new Set([
888
+ function filePriority(filePath) {
889
+ const base = path3.basename(filePath);
890
+ const entryPoints = /* @__PURE__ */ new Set([
716
891
  "index.ts",
717
892
  "index.js",
718
893
  "index.tsx",
@@ -729,22 +904,13 @@ function sortByPriority(files) {
729
904
  "mod.rs",
730
905
  "lib.rs"
731
906
  ]);
732
- const configPattern = /\.(json|ya?ml|toml|ini|cfg|env)$|config\.|Makefile|Dockerfile/i;
733
- const routePattern = /(route|api|controller|endpoint|handler)/i;
734
- const schemaPattern = /(types|schema|models|entities|migration)/i;
735
- const servicePattern = /(service|lib|utils|helper|middleware)/i;
736
- const testPattern = /(test|spec|__tests__|_test\.|\.test\.)/i;
737
- function priority(filePath) {
738
- const base = path3.basename(filePath);
739
- if (entryPointNames.has(base)) return 0;
740
- if (configPattern.test(filePath)) return 1;
741
- if (routePattern.test(filePath)) return 2;
742
- if (schemaPattern.test(filePath)) return 3;
743
- if (servicePattern.test(filePath)) return 4;
744
- if (testPattern.test(filePath)) return 6;
745
- return 5;
746
- }
747
- files.sort((a, b) => priority(a) - priority(b));
907
+ if (entryPoints.has(base)) return 40;
908
+ if (/\.(json|ya?ml|toml|ini|cfg|env)$|config\.|Makefile|Dockerfile/i.test(filePath)) return 35;
909
+ if (/(route|api|controller|endpoint|handler)/i.test(filePath)) return 30;
910
+ if (/(types|schema|models|entities|migration)/i.test(filePath)) return 25;
911
+ if (/(service|lib|utils|helper|middleware)/i.test(filePath)) return 20;
912
+ if (/(test|spec|__tests__|_test\.|\.test\.)/i.test(filePath)) return 5;
913
+ return 15;
748
914
  }
749
915
 
750
916
  // src/llm/index.ts
@@ -1026,7 +1192,7 @@ var OpenAICompatProvider = class {
1026
1192
  };
1027
1193
 
1028
1194
  // src/llm/cursor-acp.ts
1029
- import { spawn, execSync as execSync2 } from "child_process";
1195
+ import { spawn, execSync as execSync3 } from "child_process";
1030
1196
  import readline from "readline";
1031
1197
  var ACP_AGENT_BIN = "agent";
1032
1198
  var CursorAcpProvider = class {
@@ -1176,7 +1342,7 @@ ${msg.content}
1176
1342
  };
1177
1343
  function isCursorAgentAvailable() {
1178
1344
  try {
1179
- execSync2(`which ${ACP_AGENT_BIN}`, { stdio: "ignore" });
1345
+ execSync3(`which ${ACP_AGENT_BIN}`, { stdio: "ignore" });
1180
1346
  return true;
1181
1347
  } catch {
1182
1348
  return false;
@@ -1184,7 +1350,7 @@ function isCursorAgentAvailable() {
1184
1350
  }
1185
1351
 
1186
1352
  // src/llm/claude-cli.ts
1187
- import { spawn as spawn2, execSync as execSync3 } from "child_process";
1353
+ import { spawn as spawn2, execSync as execSync4 } from "child_process";
1188
1354
  var CLAUDE_CLI_BIN = "claude";
1189
1355
  var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
1190
1356
  var ClaudeCliProvider = class {
@@ -1303,7 +1469,7 @@ ${msg.content}
1303
1469
  function isClaudeCliAvailable() {
1304
1470
  try {
1305
1471
  const cmd = process.platform === "win32" ? `where ${CLAUDE_CLI_BIN}` : `which ${CLAUDE_CLI_BIN}`;
1306
- execSync3(cmd, { stdio: "ignore" });
1472
+ execSync4(cmd, { stdio: "ignore" });
1307
1473
  return true;
1308
1474
  } catch {
1309
1475
  return false;
@@ -3037,10 +3203,10 @@ import select2 from "@inquirer/select";
3037
3203
  import { createTwoFilesPatch } from "diff";
3038
3204
 
3039
3205
  // src/utils/editor.ts
3040
- import { execSync as execSync4, spawn as spawn3 } from "child_process";
3206
+ import { execSync as execSync5, spawn as spawn3 } from "child_process";
3041
3207
  function commandExists(cmd) {
3042
3208
  try {
3043
- execSync4(`which ${cmd}`, { stdio: "ignore" });
3209
+ execSync5(`which ${cmd}`, { stdio: "ignore" });
3044
3210
  return true;
3045
3211
  } catch {
3046
3212
  return false;
@@ -3340,11 +3506,11 @@ ${agentRefs.join(" ")}
3340
3506
  // src/lib/hooks.ts
3341
3507
  import fs17 from "fs";
3342
3508
  import path12 from "path";
3343
- import { execSync as execSync6 } from "child_process";
3509
+ import { execSync as execSync7 } from "child_process";
3344
3510
 
3345
3511
  // src/lib/resolve-caliber.ts
3346
3512
  import fs16 from "fs";
3347
- import { execSync as execSync5 } from "child_process";
3513
+ import { execSync as execSync6 } from "child_process";
3348
3514
  var _resolved = null;
3349
3515
  function resolveCaliber() {
3350
3516
  if (_resolved) return _resolved;
@@ -3354,7 +3520,7 @@ function resolveCaliber() {
3354
3520
  return _resolved;
3355
3521
  }
3356
3522
  try {
3357
- const found = execSync5("which caliber", {
3523
+ const found = execSync6("which caliber", {
3358
3524
  encoding: "utf-8",
3359
3525
  stdio: ["pipe", "pipe", "pipe"]
3360
3526
  }).trim();
@@ -3459,7 +3625,7 @@ ${PRECOMMIT_END}`;
3459
3625
  }
3460
3626
  function getGitHooksDir() {
3461
3627
  try {
3462
- const gitDir = execSync6("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3628
+ const gitDir = execSync7("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3463
3629
  return path12.join(gitDir, "hooks");
3464
3630
  } catch {
3465
3631
  return null;
@@ -3603,7 +3769,7 @@ function removeLearningHooks() {
3603
3769
  init_constants();
3604
3770
  import fs19 from "fs";
3605
3771
  import path14 from "path";
3606
- import { execSync as execSync7 } from "child_process";
3772
+ import { execSync as execSync8 } from "child_process";
3607
3773
  var STATE_FILE = path14.join(CALIBER_DIR, ".caliber-state.json");
3608
3774
  function normalizeTargetAgent(value) {
3609
3775
  if (Array.isArray(value)) return value;
@@ -3631,7 +3797,7 @@ function writeState(state) {
3631
3797
  }
3632
3798
  function getCurrentHeadSha() {
3633
3799
  try {
3634
- return execSync7("git rev-parse HEAD", {
3800
+ return execSync8("git rev-parse HEAD", {
3635
3801
  encoding: "utf-8",
3636
3802
  stdio: ["pipe", "pipe", "pipe"]
3637
3803
  }).trim();
@@ -4554,7 +4720,7 @@ function checkGrounding(dir) {
4554
4720
 
4555
4721
  // src/scoring/checks/accuracy.ts
4556
4722
  import { existsSync as existsSync4, statSync as statSync2 } from "fs";
4557
- import { execSync as execSync8 } from "child_process";
4723
+ import { execSync as execSync9 } from "child_process";
4558
4724
  import { join as join5 } from "path";
4559
4725
  function validateReferences(dir) {
4560
4726
  const configContent = collectPrimaryConfigContent(dir);
@@ -4586,13 +4752,13 @@ function validateReferences(dir) {
4586
4752
  }
4587
4753
  function detectGitDrift(dir) {
4588
4754
  try {
4589
- execSync8("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
4755
+ execSync9("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
4590
4756
  } catch {
4591
4757
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: false };
4592
4758
  }
4593
4759
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".cursor/rules"];
4594
4760
  try {
4595
- const headTimestamp = execSync8(
4761
+ const headTimestamp = execSync9(
4596
4762
  "git log -1 --format=%ct HEAD",
4597
4763
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4598
4764
  ).trim();
@@ -4613,7 +4779,7 @@ function detectGitDrift(dir) {
4613
4779
  let latestConfigCommitHash = null;
4614
4780
  for (const file of configFiles) {
4615
4781
  try {
4616
- const hash = execSync8(
4782
+ const hash = execSync9(
4617
4783
  `git log -1 --format=%H -- "${file}"`,
4618
4784
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4619
4785
  ).trim();
@@ -4622,7 +4788,7 @@ function detectGitDrift(dir) {
4622
4788
  latestConfigCommitHash = hash;
4623
4789
  } else {
4624
4790
  try {
4625
- execSync8(
4791
+ execSync9(
4626
4792
  `git merge-base --is-ancestor ${latestConfigCommitHash} ${hash}`,
4627
4793
  { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }
4628
4794
  );
@@ -4637,12 +4803,12 @@ function detectGitDrift(dir) {
4637
4803
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: true };
4638
4804
  }
4639
4805
  try {
4640
- const countStr = execSync8(
4806
+ const countStr = execSync9(
4641
4807
  `git rev-list --count ${latestConfigCommitHash}..HEAD`,
4642
4808
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4643
4809
  ).trim();
4644
4810
  const commitsSince = parseInt(countStr, 10) || 0;
4645
- const lastDate = execSync8(
4811
+ const lastDate = execSync9(
4646
4812
  `git log -1 --format=%ci ${latestConfigCommitHash}`,
4647
4813
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4648
4814
  ).trim();
@@ -4714,12 +4880,12 @@ function checkAccuracy(dir) {
4714
4880
 
4715
4881
  // src/scoring/checks/freshness.ts
4716
4882
  import { existsSync as existsSync5, statSync as statSync3 } from "fs";
4717
- import { execSync as execSync9 } from "child_process";
4883
+ import { execSync as execSync10 } from "child_process";
4718
4884
  import { join as join6 } from "path";
4719
4885
  function getCommitsSinceConfigUpdate(dir) {
4720
4886
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules"];
4721
4887
  try {
4722
- const headTimestamp = execSync9(
4888
+ const headTimestamp = execSync10(
4723
4889
  "git log -1 --format=%ct HEAD",
4724
4890
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4725
4891
  ).trim();
@@ -4739,12 +4905,12 @@ function getCommitsSinceConfigUpdate(dir) {
4739
4905
  }
4740
4906
  for (const file of configFiles) {
4741
4907
  try {
4742
- const hash = execSync9(
4908
+ const hash = execSync10(
4743
4909
  `git log -1 --format=%H -- "${file}"`,
4744
4910
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4745
4911
  ).trim();
4746
4912
  if (hash) {
4747
- const countStr = execSync9(
4913
+ const countStr = execSync10(
4748
4914
  `git rev-list --count ${hash}..HEAD`,
4749
4915
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4750
4916
  ).trim();
@@ -4862,11 +5028,11 @@ function checkFreshness(dir) {
4862
5028
 
4863
5029
  // src/scoring/checks/bonus.ts
4864
5030
  import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
4865
- import { execSync as execSync10 } from "child_process";
5031
+ import { execSync as execSync11 } from "child_process";
4866
5032
  import { join as join7 } from "path";
4867
5033
  function hasPreCommitHook(dir) {
4868
5034
  try {
4869
- const gitDir = execSync10("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
5035
+ const gitDir = execSync11("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
4870
5036
  const hookPath = join7(gitDir, "hooks", "pre-commit");
4871
5037
  const content = readFileOrNull2(hookPath);
4872
5038
  return content ? content.includes("caliber") : false;
@@ -5334,7 +5500,7 @@ import fs22 from "fs";
5334
5500
  import path17 from "path";
5335
5501
  import os3 from "os";
5336
5502
  import crypto3 from "crypto";
5337
- import { execSync as execSync11 } from "child_process";
5503
+ import { execSync as execSync12 } from "child_process";
5338
5504
  var CONFIG_DIR2 = path17.join(os3.homedir(), ".caliber");
5339
5505
  var CONFIG_FILE2 = path17.join(CONFIG_DIR2, "config.json");
5340
5506
  var runtimeDisabled = false;
@@ -5361,7 +5527,7 @@ function getMachineId() {
5361
5527
  }
5362
5528
  function getGitEmailHash() {
5363
5529
  try {
5364
- const email = execSync11("git config user.email", { encoding: "utf-8" }).trim();
5530
+ const email = execSync12("git config user.email", { encoding: "utf-8" }).trim();
5365
5531
  if (!email) return void 0;
5366
5532
  return crypto3.createHash("sha256").update(email).digest("hex");
5367
5533
  } catch {
@@ -7169,7 +7335,7 @@ import chalk13 from "chalk";
7169
7335
  import ora5 from "ora";
7170
7336
 
7171
7337
  // src/lib/git-diff.ts
7172
- import { execSync as execSync12 } from "child_process";
7338
+ import { execSync as execSync13 } from "child_process";
7173
7339
  var MAX_DIFF_BYTES = 1e5;
7174
7340
  var DOC_PATTERNS = [
7175
7341
  "CLAUDE.md",
@@ -7183,7 +7349,7 @@ function excludeArgs() {
7183
7349
  }
7184
7350
  function safeExec(cmd) {
7185
7351
  try {
7186
- return execSync12(cmd, {
7352
+ return execSync13(cmd, {
7187
7353
  encoding: "utf-8",
7188
7354
  stdio: ["pipe", "pipe", "pipe"],
7189
7355
  maxBuffer: 10 * 1024 * 1024
@@ -8110,7 +8276,7 @@ learn.command("status").description("Show learning system status").action(tracke
8110
8276
  import fs32 from "fs";
8111
8277
  import path26 from "path";
8112
8278
  import { fileURLToPath as fileURLToPath2 } from "url";
8113
- import { execSync as execSync13 } from "child_process";
8279
+ import { execSync as execSync14 } from "child_process";
8114
8280
  import chalk17 from "chalk";
8115
8281
  import ora6 from "ora";
8116
8282
  import confirm2 from "@inquirer/confirm";
@@ -8120,7 +8286,7 @@ var pkg2 = JSON.parse(
8120
8286
  );
8121
8287
  function getInstalledVersion() {
8122
8288
  try {
8123
- const globalRoot = execSync13("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
8289
+ const globalRoot = execSync14("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
8124
8290
  const pkgPath = path26.join(globalRoot, "@rely-ai", "caliber", "package.json");
8125
8291
  return JSON.parse(fs32.readFileSync(pkgPath, "utf-8")).version;
8126
8292
  } catch {
@@ -8165,7 +8331,7 @@ Update available: ${current} -> ${latest}`)
8165
8331
  }
8166
8332
  const spinner = ora6("Updating caliber...").start();
8167
8333
  try {
8168
- execSync13(`npm install -g @rely-ai/caliber@${latest}`, {
8334
+ execSync14(`npm install -g @rely-ai/caliber@${latest}`, {
8169
8335
  stdio: "pipe",
8170
8336
  timeout: 12e4,
8171
8337
  env: { ...process.env, npm_config_fund: "false", npm_config_audit: "false" }
@@ -8182,7 +8348,7 @@ Update available: ${current} -> ${latest}`)
8182
8348
  console.log(chalk17.dim(`
8183
8349
  Restarting: caliber ${args.join(" ")}
8184
8350
  `));
8185
- execSync13(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
8351
+ execSync14(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
8186
8352
  stdio: "inherit",
8187
8353
  env: { ...process.env, CALIBER_SKIP_UPDATE_CHECK: "1" }
8188
8354
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rely-ai/caliber",
3
- "version": "1.17.0",
3
+ "version": "1.18.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": {