@rely-ai/caliber 1.7.9 → 1.8.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 +224 -155
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -1476,7 +1476,7 @@ Quality (25 pts):
1476
1476
  - No contradictions (2 pts) \u2014 consistent tool/style recommendations
1477
1477
 
1478
1478
  Coverage (20 pts):
1479
- - Dependency coverage (10 pts) \u2014 CRITICAL: mention the project's actual dependencies by name in CLAUDE.md or skills. Reference the key packages from package.json/requirements.txt/go.mod. The scoring checks whether each non-trivial dependency name appears somewhere in your output. Aim for >80% coverage.
1479
+ - Dependency coverage (10 pts) \u2014 CRITICAL: the exact dependency list is provided in your input under "DEPENDENCY COVERAGE". Mention AT LEAST 85% of them by name in CLAUDE.md or skills. You get full points at 85%+, proportional below that. Weave them naturally into architecture, key deps, and conventions sections.
1480
1480
  - Service/MCP coverage (6 pts) \u2014 reference detected services (DB, cloud, etc.)
1481
1481
  - MCP completeness (4 pts) \u2014 full points if no external services detected
1482
1482
 
@@ -1737,6 +1737,98 @@ async function enrichWithLLM(fingerprint, dir) {
1737
1737
  }
1738
1738
  }
1739
1739
 
1740
+ // src/utils/dependencies.ts
1741
+ import { readFileSync } from "fs";
1742
+ import { join } from "path";
1743
+ function readFileOrNull(path26) {
1744
+ try {
1745
+ return readFileSync(path26, "utf-8");
1746
+ } catch {
1747
+ return null;
1748
+ }
1749
+ }
1750
+ function readJsonOrNull(path26) {
1751
+ const content = readFileOrNull(path26);
1752
+ if (!content) return null;
1753
+ try {
1754
+ return JSON.parse(content);
1755
+ } catch {
1756
+ return null;
1757
+ }
1758
+ }
1759
+ function extractNpmDeps(dir) {
1760
+ const pkg3 = readJsonOrNull(join(dir, "package.json"));
1761
+ if (!pkg3) return [];
1762
+ const deps = {
1763
+ ...pkg3.dependencies,
1764
+ ...pkg3.devDependencies
1765
+ };
1766
+ const trivial = /* @__PURE__ */ new Set([
1767
+ "typescript",
1768
+ "@types/node",
1769
+ "tslib",
1770
+ "ts-node",
1771
+ "tsx",
1772
+ "prettier",
1773
+ "eslint",
1774
+ "@eslint/js",
1775
+ "rimraf",
1776
+ "cross-env",
1777
+ "dotenv",
1778
+ "nodemon",
1779
+ "husky",
1780
+ "lint-staged",
1781
+ "commitlint",
1782
+ "@commitlint/cli",
1783
+ "@commitlint/config-conventional"
1784
+ ]);
1785
+ const trivialPatterns = [
1786
+ /^@rely-ai\//,
1787
+ /^@caliber-ai\//,
1788
+ /^eslint-/,
1789
+ /^@eslint\//,
1790
+ /^prettier-/,
1791
+ /^@typescript-eslint\//
1792
+ ];
1793
+ return Object.keys(deps).filter((d) => !trivial.has(d) && !d.startsWith("@types/") && !trivialPatterns.some((p) => p.test(d))).slice(0, 30);
1794
+ }
1795
+ function extractPythonDeps(dir) {
1796
+ const reqTxt = readFileOrNull(join(dir, "requirements.txt"));
1797
+ if (reqTxt) {
1798
+ return reqTxt.split("\n").map((l) => l.trim().split(/[=<>!~\[]/)[0].trim()).filter((l) => l && !l.startsWith("#")).slice(0, 30);
1799
+ }
1800
+ const pyproject = readFileOrNull(join(dir, "pyproject.toml"));
1801
+ if (pyproject) {
1802
+ const depMatch = pyproject.match(/dependencies\s*=\s*\[([\s\S]*?)\]/);
1803
+ if (depMatch) {
1804
+ return depMatch[1].split("\n").map((l) => l.trim().replace(/["',]/g, "").split(/[=<>!~\[]/)[0].trim()).filter((l) => l.length > 0).slice(0, 30);
1805
+ }
1806
+ }
1807
+ return [];
1808
+ }
1809
+ function extractGoDeps(dir) {
1810
+ const goMod = readFileOrNull(join(dir, "go.mod"));
1811
+ if (!goMod) return [];
1812
+ const requireBlock = goMod.match(/require\s*\(([\s\S]*?)\)/);
1813
+ if (!requireBlock) return [];
1814
+ return requireBlock[1].split("\n").map((l) => l.trim().split(/\s/)[0]).filter((l) => l && !l.startsWith("//")).map((l) => l.split("/").pop() || l).slice(0, 30);
1815
+ }
1816
+ function extractRustDeps(dir) {
1817
+ const cargo = readFileOrNull(join(dir, "Cargo.toml"));
1818
+ if (!cargo) return [];
1819
+ const depSection = cargo.match(/\[dependencies\]([\s\S]*?)(?:\[|$)/);
1820
+ if (!depSection) return [];
1821
+ return depSection[1].split("\n").map((l) => l.trim().split(/\s*=/)[0].trim()).filter((l) => l.length > 0 && !l.startsWith("#")).slice(0, 30);
1822
+ }
1823
+ function extractAllDeps(dir) {
1824
+ return [
1825
+ ...extractNpmDeps(dir),
1826
+ ...extractPythonDeps(dir),
1827
+ ...extractGoDeps(dir),
1828
+ ...extractRustDeps(dir)
1829
+ ];
1830
+ }
1831
+
1740
1832
  // src/ai/generate.ts
1741
1833
  var GENERATION_MAX_TOKENS = 64e3;
1742
1834
  var MODEL_MAX_OUTPUT_TOKENS = 128e3;
@@ -1983,6 +2075,12 @@ ${truncate(cfg.content, LIMITS.CONFIG_FILE_CHARS)}`);
1983
2075
  parts.push("\n(Code analysis was truncated due to size limits \u2014 not all files are shown.)");
1984
2076
  }
1985
2077
  }
2078
+ const allDeps = extractAllDeps(process.cwd());
2079
+ if (allDeps.length > 0) {
2080
+ parts.push(`
2081
+ DEPENDENCY COVERAGE \u2014 mention at least 85% of these ${allDeps.length} packages by name in CLAUDE.md or skills for full coverage points:`);
2082
+ parts.push(allDeps.join(", "));
2083
+ }
1986
2084
  if (prompt) parts.push(`
1987
2085
  User instructions: ${prompt}`);
1988
2086
  return parts.join("\n");
@@ -3140,11 +3238,11 @@ async function runInteractiveProviderSetup(options) {
3140
3238
 
3141
3239
  // src/scoring/index.ts
3142
3240
  import { existsSync as existsSync8 } from "fs";
3143
- import { join as join7 } from "path";
3241
+ import { join as join8 } from "path";
3144
3242
 
3145
3243
  // src/scoring/checks/existence.ts
3146
- import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync2 } from "fs";
3147
- import { join as join2 } from "path";
3244
+ import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
3245
+ import { join as join3 } from "path";
3148
3246
 
3149
3247
  // src/scoring/constants.ts
3150
3248
  var POINTS_CLAUDE_MD_EXISTS = 6;
@@ -3248,100 +3346,27 @@ function computeGrade(score) {
3248
3346
  }
3249
3347
 
3250
3348
  // src/scoring/checks/coverage.ts
3251
- import { readFileSync, readdirSync } from "fs";
3252
- import { join } from "path";
3253
- function readFileOrNull(path26) {
3349
+ import { readFileSync as readFileSync2, readdirSync } from "fs";
3350
+ import { join as join2 } from "path";
3351
+ function readFileOrNull2(path26) {
3254
3352
  try {
3255
- return readFileSync(path26, "utf-8");
3353
+ return readFileSync2(path26, "utf-8");
3256
3354
  } catch {
3257
3355
  return null;
3258
3356
  }
3259
3357
  }
3260
- function readJsonOrNull(path26) {
3261
- const content = readFileOrNull(path26);
3262
- if (!content) return null;
3263
- try {
3264
- return JSON.parse(content);
3265
- } catch {
3266
- return null;
3267
- }
3268
- }
3269
- function extractNpmDeps(dir) {
3270
- const pkg3 = readJsonOrNull(join(dir, "package.json"));
3271
- if (!pkg3) return [];
3272
- const deps = {
3273
- ...pkg3.dependencies,
3274
- ...pkg3.devDependencies
3275
- };
3276
- const trivial = /* @__PURE__ */ new Set([
3277
- "typescript",
3278
- "@types/node",
3279
- "tslib",
3280
- "ts-node",
3281
- "tsx",
3282
- "prettier",
3283
- "eslint",
3284
- "@eslint/js",
3285
- "rimraf",
3286
- "cross-env",
3287
- "dotenv",
3288
- "nodemon",
3289
- "husky",
3290
- "lint-staged",
3291
- "commitlint",
3292
- "@commitlint/cli",
3293
- "@commitlint/config-conventional"
3294
- ]);
3295
- const trivialPatterns = [
3296
- /^@rely-ai\//,
3297
- /^@caliber-ai\//,
3298
- /^eslint-/,
3299
- /^@eslint\//,
3300
- /^prettier-/,
3301
- /^@typescript-eslint\//
3302
- ];
3303
- return Object.keys(deps).filter((d) => !trivial.has(d) && !d.startsWith("@types/") && !trivialPatterns.some((p) => p.test(d))).slice(0, 30);
3304
- }
3305
- function extractPythonDeps(dir) {
3306
- const reqTxt = readFileOrNull(join(dir, "requirements.txt"));
3307
- if (reqTxt) {
3308
- return reqTxt.split("\n").map((l) => l.trim().split(/[=<>!~\[]/)[0].trim()).filter((l) => l && !l.startsWith("#")).slice(0, 30);
3309
- }
3310
- const pyproject = readFileOrNull(join(dir, "pyproject.toml"));
3311
- if (pyproject) {
3312
- const depMatch = pyproject.match(/dependencies\s*=\s*\[([\s\S]*?)\]/);
3313
- if (depMatch) {
3314
- return depMatch[1].split("\n").map((l) => l.trim().replace(/["',]/g, "").split(/[=<>!~\[]/)[0].trim()).filter((l) => l.length > 0).slice(0, 30);
3315
- }
3316
- }
3317
- return [];
3318
- }
3319
- function extractGoDeps(dir) {
3320
- const goMod = readFileOrNull(join(dir, "go.mod"));
3321
- if (!goMod) return [];
3322
- const requireBlock = goMod.match(/require\s*\(([\s\S]*?)\)/);
3323
- if (!requireBlock) return [];
3324
- return requireBlock[1].split("\n").map((l) => l.trim().split(/\s/)[0]).filter((l) => l && !l.startsWith("//")).map((l) => l.split("/").pop() || l).slice(0, 30);
3325
- }
3326
- function extractRustDeps(dir) {
3327
- const cargo = readFileOrNull(join(dir, "Cargo.toml"));
3328
- if (!cargo) return [];
3329
- const depSection = cargo.match(/\[dependencies\]([\s\S]*?)(?:\[|$)/);
3330
- if (!depSection) return [];
3331
- return depSection[1].split("\n").map((l) => l.trim().split(/\s*=/)[0].trim()).filter((l) => l.length > 0 && !l.startsWith("#")).slice(0, 30);
3332
- }
3333
3358
  function collectAllConfigContent(dir) {
3334
3359
  const parts = [];
3335
- const claudeMd = readFileOrNull(join(dir, "CLAUDE.md"));
3360
+ const claudeMd = readFileOrNull2(join2(dir, "CLAUDE.md"));
3336
3361
  if (claudeMd) parts.push(claudeMd);
3337
- const cursorrules = readFileOrNull(join(dir, ".cursorrules"));
3362
+ const cursorrules = readFileOrNull2(join2(dir, ".cursorrules"));
3338
3363
  if (cursorrules) parts.push(cursorrules);
3339
- for (const skillsDir of [join(dir, ".claude", "skills"), join(dir, ".cursor", "skills")]) {
3364
+ for (const skillsDir of [join2(dir, ".claude", "skills"), join2(dir, ".cursor", "skills")]) {
3340
3365
  try {
3341
3366
  const entries = readdirSync(skillsDir, { withFileTypes: true });
3342
3367
  for (const entry of entries) {
3343
3368
  if (entry.isDirectory()) {
3344
- const skill = readFileOrNull(join(skillsDir, entry.name, "SKILL.md"));
3369
+ const skill = readFileOrNull2(join2(skillsDir, entry.name, "SKILL.md"));
3345
3370
  if (skill) parts.push(skill);
3346
3371
  }
3347
3372
  }
@@ -3349,10 +3374,10 @@ function collectAllConfigContent(dir) {
3349
3374
  }
3350
3375
  }
3351
3376
  try {
3352
- const rulesDir = join(dir, ".cursor", "rules");
3377
+ const rulesDir = join2(dir, ".cursor", "rules");
3353
3378
  const mdcFiles = readdirSync(rulesDir).filter((f) => f.endsWith(".mdc"));
3354
3379
  for (const f of mdcFiles) {
3355
- const content = readFileOrNull(join(rulesDir, f));
3380
+ const content = readFileOrNull2(join2(rulesDir, f));
3356
3381
  if (content) parts.push(content);
3357
3382
  }
3358
3383
  } catch {
@@ -3400,7 +3425,7 @@ function getConfiguredMcpServers(dir) {
3400
3425
  ];
3401
3426
  for (const rel of mcpFiles) {
3402
3427
  try {
3403
- const content = readFileSync(join(dir, rel), "utf-8");
3428
+ const content = readFileSync2(join2(dir, rel), "utf-8");
3404
3429
  const parsed = JSON.parse(content);
3405
3430
  const mcpServers = parsed.mcpServers;
3406
3431
  if (mcpServers) {
@@ -3516,7 +3541,7 @@ function hasMcpServers(dir) {
3516
3541
  ];
3517
3542
  for (const rel of mcpFiles) {
3518
3543
  try {
3519
- const content = readFileSync2(join2(dir, rel), "utf-8");
3544
+ const content = readFileSync3(join3(dir, rel), "utf-8");
3520
3545
  const parsed = JSON.parse(content);
3521
3546
  const servers = parsed.mcpServers;
3522
3547
  if (servers && Object.keys(servers).length > 0) {
@@ -3530,7 +3555,7 @@ function hasMcpServers(dir) {
3530
3555
  }
3531
3556
  function checkExistence(dir) {
3532
3557
  const checks = [];
3533
- const claudeMdExists = existsSync3(join2(dir, "CLAUDE.md"));
3558
+ const claudeMdExists = existsSync3(join3(dir, "CLAUDE.md"));
3534
3559
  checks.push({
3535
3560
  id: "claude_md_exists",
3536
3561
  name: "CLAUDE.md exists",
@@ -3541,8 +3566,8 @@ function checkExistence(dir) {
3541
3566
  detail: claudeMdExists ? "Found at project root" : "Not found",
3542
3567
  suggestion: claudeMdExists ? void 0 : "Create a CLAUDE.md with project context and commands"
3543
3568
  });
3544
- const hasCursorrules = existsSync3(join2(dir, ".cursorrules"));
3545
- const cursorRulesDir = existsSync3(join2(dir, ".cursor", "rules"));
3569
+ const hasCursorrules = existsSync3(join3(dir, ".cursorrules"));
3570
+ const cursorRulesDir = existsSync3(join3(dir, ".cursor", "rules"));
3546
3571
  const cursorRulesExist = hasCursorrules || cursorRulesDir;
3547
3572
  checks.push({
3548
3573
  id: "cursor_rules_exist",
@@ -3554,7 +3579,7 @@ function checkExistence(dir) {
3554
3579
  detail: hasCursorrules ? ".cursorrules found" : cursorRulesDir ? ".cursor/rules/ found" : "No Cursor rules",
3555
3580
  suggestion: cursorRulesExist ? void 0 : "Add .cursor/rules/ for Cursor users on your team"
3556
3581
  });
3557
- const agentsMdExists = existsSync3(join2(dir, "AGENTS.md"));
3582
+ const agentsMdExists = existsSync3(join3(dir, "AGENTS.md"));
3558
3583
  checks.push({
3559
3584
  id: "codex_agents_md_exists",
3560
3585
  name: "AGENTS.md exists",
@@ -3565,8 +3590,8 @@ function checkExistence(dir) {
3565
3590
  detail: agentsMdExists ? "Found at project root" : "Not found",
3566
3591
  suggestion: agentsMdExists ? void 0 : "Create AGENTS.md with project context for Codex"
3567
3592
  });
3568
- const claudeSkills = countFiles(join2(dir, ".claude", "skills"), /\.(md|SKILL\.md)$/);
3569
- const codexSkills = countFiles(join2(dir, ".agents", "skills"), /SKILL\.md$/);
3593
+ const claudeSkills = countFiles(join3(dir, ".claude", "skills"), /\.(md|SKILL\.md)$/);
3594
+ const codexSkills = countFiles(join3(dir, ".agents", "skills"), /SKILL\.md$/);
3570
3595
  const skillCount = claudeSkills.length + codexSkills.length;
3571
3596
  const skillBase = skillCount >= 1 ? POINTS_SKILLS_EXIST : 0;
3572
3597
  const skillBonus = Math.min((skillCount - 1) * POINTS_SKILLS_BONUS_PER_EXTRA, POINTS_SKILLS_BONUS_CAP);
@@ -3582,7 +3607,7 @@ function checkExistence(dir) {
3582
3607
  detail: skillCount === 0 ? "No skills found" : `${skillCount} skill${skillCount === 1 ? "" : "s"} found`,
3583
3608
  suggestion: skillCount === 0 ? "Add .claude/skills/ with project-specific workflows" : skillCount < 3 ? "Optimal is 2-3 focused skills (SkillsBench research)" : void 0
3584
3609
  });
3585
- const mdcFiles = countFiles(join2(dir, ".cursor", "rules"), /\.mdc$/);
3610
+ const mdcFiles = countFiles(join3(dir, ".cursor", "rules"), /\.mdc$/);
3586
3611
  const mdcCount = mdcFiles.length;
3587
3612
  checks.push({
3588
3613
  id: "cursor_mdc_rules",
@@ -3624,11 +3649,11 @@ function checkExistence(dir) {
3624
3649
  }
3625
3650
 
3626
3651
  // src/scoring/checks/quality.ts
3627
- import { readFileSync as readFileSync3 } from "fs";
3628
- import { join as join3 } from "path";
3629
- function readFileOrNull2(path26) {
3652
+ import { readFileSync as readFileSync4 } from "fs";
3653
+ import { join as join4 } from "path";
3654
+ function readFileOrNull3(path26) {
3630
3655
  try {
3631
- return readFileSync3(path26, "utf-8");
3656
+ return readFileSync4(path26, "utf-8");
3632
3657
  } catch {
3633
3658
  return null;
3634
3659
  }
@@ -3638,9 +3663,9 @@ function countLines(content) {
3638
3663
  }
3639
3664
  function checkQuality(dir) {
3640
3665
  const checks = [];
3641
- const claudeMd = readFileOrNull2(join3(dir, "CLAUDE.md"));
3642
- const cursorrules = readFileOrNull2(join3(dir, ".cursorrules"));
3643
- const agentsMd = readFileOrNull2(join3(dir, "AGENTS.md"));
3666
+ const claudeMd = readFileOrNull3(join4(dir, "CLAUDE.md"));
3667
+ const cursorrules = readFileOrNull3(join4(dir, ".cursorrules"));
3668
+ const agentsMd = readFileOrNull3(join4(dir, "AGENTS.md"));
3644
3669
  const allContent = [claudeMd, cursorrules, agentsMd].filter(Boolean);
3645
3670
  const combinedContent = allContent.join("\n");
3646
3671
  const primaryInstructions = claudeMd ?? agentsMd;
@@ -3779,17 +3804,17 @@ function checkQuality(dir) {
3779
3804
  }
3780
3805
 
3781
3806
  // src/scoring/checks/accuracy.ts
3782
- import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync } from "fs";
3783
- import { join as join4 } from "path";
3784
- function readFileOrNull3(path26) {
3807
+ import { existsSync as existsSync5, readFileSync as readFileSync5, readdirSync as readdirSync3, statSync } from "fs";
3808
+ import { join as join5 } from "path";
3809
+ function readFileOrNull4(path26) {
3785
3810
  try {
3786
- return readFileSync4(path26, "utf-8");
3811
+ return readFileSync5(path26, "utf-8");
3787
3812
  } catch {
3788
3813
  return null;
3789
3814
  }
3790
3815
  }
3791
3816
  function readJsonOrNull2(path26) {
3792
- const content = readFileOrNull3(path26);
3817
+ const content = readFileOrNull4(path26);
3793
3818
  if (!content) return null;
3794
3819
  try {
3795
3820
  return JSON.parse(content);
@@ -3798,12 +3823,12 @@ function readJsonOrNull2(path26) {
3798
3823
  }
3799
3824
  }
3800
3825
  function getPackageScripts(dir) {
3801
- const pkg3 = readJsonOrNull2(join4(dir, "package.json"));
3826
+ const pkg3 = readJsonOrNull2(join5(dir, "package.json"));
3802
3827
  if (!pkg3?.scripts) return /* @__PURE__ */ new Set();
3803
3828
  return new Set(Object.keys(pkg3.scripts));
3804
3829
  }
3805
3830
  function validateDocumentedCommands(dir) {
3806
- const claudeMd = readFileOrNull3(join4(dir, "CLAUDE.md"));
3831
+ const claudeMd = readFileOrNull4(join5(dir, "CLAUDE.md"));
3807
3832
  if (!claudeMd) return { valid: [], invalid: [], total: 0 };
3808
3833
  const scripts = getPackageScripts(dir);
3809
3834
  const valid = [];
@@ -3838,8 +3863,8 @@ function validateDocumentedCommands(dir) {
3838
3863
  valid.push(match[0]);
3839
3864
  }
3840
3865
  const makePattern = /make\s+(\S+)/g;
3841
- if (existsSync5(join4(dir, "Makefile"))) {
3842
- const makefile = readFileOrNull3(join4(dir, "Makefile"));
3866
+ if (existsSync5(join5(dir, "Makefile"))) {
3867
+ const makefile = readFileOrNull4(join5(dir, "Makefile"));
3843
3868
  const makeTargets = /* @__PURE__ */ new Set();
3844
3869
  if (makefile) {
3845
3870
  for (const line of makefile.split("\n")) {
@@ -3861,7 +3886,7 @@ function validateDocumentedCommands(dir) {
3861
3886
  return { valid, invalid, total: valid.length + invalid.length };
3862
3887
  }
3863
3888
  function validateDocumentedPaths(dir) {
3864
- const claudeMd = readFileOrNull3(join4(dir, "CLAUDE.md"));
3889
+ const claudeMd = readFileOrNull4(join5(dir, "CLAUDE.md"));
3865
3890
  if (!claudeMd) return { valid: [], invalid: [], total: 0 };
3866
3891
  const valid = [];
3867
3892
  const invalid = [];
@@ -3872,7 +3897,7 @@ function validateDocumentedPaths(dir) {
3872
3897
  const filePath = match[1];
3873
3898
  if (seen.has(filePath)) continue;
3874
3899
  seen.add(filePath);
3875
- if (existsSync5(join4(dir, filePath))) {
3900
+ if (existsSync5(join5(dir, filePath))) {
3876
3901
  valid.push(filePath);
3877
3902
  } else {
3878
3903
  invalid.push(filePath);
@@ -3884,13 +3909,13 @@ function detectConfigDrift(dir) {
3884
3909
  const srcDirs = ["src", "lib", "app", "cmd", "internal", "pages", "components"];
3885
3910
  let latestSrcMtime = 0;
3886
3911
  for (const srcDir of srcDirs) {
3887
- const fullPath = join4(dir, srcDir);
3912
+ const fullPath = join5(dir, srcDir);
3888
3913
  if (!existsSync5(fullPath)) continue;
3889
3914
  try {
3890
3915
  const files = readdirSync3(fullPath, { recursive: true }).map(String).filter((f) => /\.(ts|js|tsx|jsx|py|go|rs|java|rb)$/.test(f));
3891
3916
  for (const file of files.slice(0, 100)) {
3892
3917
  try {
3893
- const stat = statSync(join4(fullPath, file));
3918
+ const stat = statSync(join5(fullPath, file));
3894
3919
  if (stat.mtime.getTime() > latestSrcMtime) {
3895
3920
  latestSrcMtime = stat.mtime.getTime();
3896
3921
  }
@@ -3904,7 +3929,7 @@ function detectConfigDrift(dir) {
3904
3929
  let latestConfigMtime = 0;
3905
3930
  for (const configFile of configFiles) {
3906
3931
  try {
3907
- const stat = statSync(join4(dir, configFile));
3932
+ const stat = statSync(join5(dir, configFile));
3908
3933
  if (stat.mtime.getTime() > latestConfigMtime) {
3909
3934
  latestConfigMtime = stat.mtime.getTime();
3910
3935
  }
@@ -3970,11 +3995,11 @@ function checkAccuracy(dir) {
3970
3995
  }
3971
3996
 
3972
3997
  // src/scoring/checks/freshness.ts
3973
- import { existsSync as existsSync6, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
3974
- import { join as join5 } from "path";
3975
- function readFileOrNull4(path26) {
3998
+ import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync2 } from "fs";
3999
+ import { join as join6 } from "path";
4000
+ function readFileOrNull5(path26) {
3976
4001
  try {
3977
- return readFileSync5(path26, "utf-8");
4002
+ return readFileSync6(path26, "utf-8");
3978
4003
  } catch {
3979
4004
  return null;
3980
4005
  }
@@ -3991,8 +4016,8 @@ function daysSinceModified(filePath) {
3991
4016
  }
3992
4017
  function checkFreshness(dir) {
3993
4018
  const checks = [];
3994
- const claudeMdPath = join5(dir, "CLAUDE.md");
3995
- const agentsMdPath = join5(dir, "AGENTS.md");
4019
+ const claudeMdPath = join6(dir, "CLAUDE.md");
4020
+ const agentsMdPath = join6(dir, "AGENTS.md");
3996
4021
  const primaryPath = existsSync6(claudeMdPath) ? claudeMdPath : agentsMdPath;
3997
4022
  const primaryName = existsSync6(claudeMdPath) ? "CLAUDE.md" : "AGENTS.md";
3998
4023
  const daysOld = daysSinceModified(primaryPath);
@@ -4026,7 +4051,7 @@ function checkFreshness(dir) {
4026
4051
  ];
4027
4052
  const secretFindings = [];
4028
4053
  for (const rel of filesToScan) {
4029
- const content = readFileOrNull4(join5(dir, rel));
4054
+ const content = readFileOrNull5(join6(dir, rel));
4030
4055
  if (!content) continue;
4031
4056
  const lines = content.split("\n");
4032
4057
  for (let i = 0; i < lines.length; i++) {
@@ -4054,10 +4079,10 @@ function checkFreshness(dir) {
4054
4079
  detail: hasSecrets ? `${secretFindings.length} potential secret${secretFindings.length === 1 ? "" : "s"} found in ${secretFindings[0].file}:${secretFindings[0].line}` : "No secrets detected",
4055
4080
  suggestion: hasSecrets ? `Remove secrets from ${secretFindings[0].file}:${secretFindings[0].line} \u2014 use environment variables instead` : void 0
4056
4081
  });
4057
- const settingsPath = join5(dir, ".claude", "settings.json");
4082
+ const settingsPath = join6(dir, ".claude", "settings.json");
4058
4083
  let hasPermissions = false;
4059
4084
  let permissionDetail = "";
4060
- const settingsContent = readFileOrNull4(settingsPath);
4085
+ const settingsContent = readFileOrNull5(settingsPath);
4061
4086
  if (settingsContent) {
4062
4087
  try {
4063
4088
  const settings = JSON.parse(settingsContent);
@@ -4085,12 +4110,12 @@ function checkFreshness(dir) {
4085
4110
  }
4086
4111
 
4087
4112
  // src/scoring/checks/bonus.ts
4088
- import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "fs";
4113
+ import { existsSync as existsSync7, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "fs";
4089
4114
  import { execSync as execSync8 } from "child_process";
4090
- import { join as join6 } from "path";
4091
- function readFileOrNull5(path26) {
4115
+ import { join as join7 } from "path";
4116
+ function readFileOrNull6(path26) {
4092
4117
  try {
4093
- return readFileSync6(path26, "utf-8");
4118
+ return readFileSync7(path26, "utf-8");
4094
4119
  } catch {
4095
4120
  return null;
4096
4121
  }
@@ -4098,8 +4123,8 @@ function readFileOrNull5(path26) {
4098
4123
  function hasPreCommitHook(dir) {
4099
4124
  try {
4100
4125
  const gitDir = execSync8("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
4101
- const hookPath = join6(gitDir, "hooks", "pre-commit");
4102
- const content = readFileOrNull5(hookPath);
4126
+ const hookPath = join7(gitDir, "hooks", "pre-commit");
4127
+ const content = readFileOrNull6(hookPath);
4103
4128
  return content ? content.includes("caliber") : false;
4104
4129
  } catch {
4105
4130
  return false;
@@ -4110,7 +4135,7 @@ function checkBonus(dir) {
4110
4135
  let hasClaudeHooks = false;
4111
4136
  let hasPrecommit = false;
4112
4137
  const hookSources = [];
4113
- const settingsContent = readFileOrNull5(join6(dir, ".claude", "settings.json"));
4138
+ const settingsContent = readFileOrNull6(join7(dir, ".claude", "settings.json"));
4114
4139
  if (settingsContent) {
4115
4140
  try {
4116
4141
  const settings = JSON.parse(settingsContent);
@@ -4138,7 +4163,7 @@ function checkBonus(dir) {
4138
4163
  detail: hookDetail,
4139
4164
  suggestion: hasHooks ? void 0 : "Run `caliber hooks --install` for auto-refresh"
4140
4165
  });
4141
- const agentsMdExists = existsSync7(join6(dir, "AGENTS.md"));
4166
+ const agentsMdExists = existsSync7(join7(dir, "AGENTS.md"));
4142
4167
  checks.push({
4143
4168
  id: "agents_md_exists",
4144
4169
  name: "AGENTS.md exists",
@@ -4149,14 +4174,14 @@ function checkBonus(dir) {
4149
4174
  detail: agentsMdExists ? "Found at project root" : "Not found",
4150
4175
  suggestion: agentsMdExists ? void 0 : "Add AGENTS.md \u2014 the emerging cross-agent standard (60k+ repos)"
4151
4176
  });
4152
- const skillsDir = join6(dir, ".claude", "skills");
4177
+ const skillsDir = join7(dir, ".claude", "skills");
4153
4178
  let openSkillsCount = 0;
4154
4179
  let totalSkillFiles = 0;
4155
4180
  try {
4156
4181
  const entries = readdirSync4(skillsDir, { withFileTypes: true });
4157
4182
  for (const entry of entries) {
4158
4183
  if (entry.isDirectory()) {
4159
- const skillMd = readFileOrNull5(join6(skillsDir, entry.name, "SKILL.md"));
4184
+ const skillMd = readFileOrNull6(join7(skillsDir, entry.name, "SKILL.md"));
4160
4185
  if (skillMd) {
4161
4186
  totalSkillFiles++;
4162
4187
  if (skillMd.trimStart().startsWith("---")) {
@@ -4225,9 +4250,9 @@ function filterChecksForTarget(checks, target) {
4225
4250
  }
4226
4251
  function detectTargetAgent(dir) {
4227
4252
  const agents = [];
4228
- if (existsSync8(join7(dir, "CLAUDE.md")) || existsSync8(join7(dir, ".claude", "skills"))) agents.push("claude");
4229
- if (existsSync8(join7(dir, ".cursorrules")) || existsSync8(join7(dir, ".cursor", "rules"))) agents.push("cursor");
4230
- if (existsSync8(join7(dir, ".codex")) || existsSync8(join7(dir, ".agents", "skills"))) agents.push("codex");
4253
+ if (existsSync8(join8(dir, "CLAUDE.md")) || existsSync8(join8(dir, ".claude", "skills"))) agents.push("claude");
4254
+ if (existsSync8(join8(dir, ".cursorrules")) || existsSync8(join8(dir, ".cursor", "rules"))) agents.push("cursor");
4255
+ if (existsSync8(join8(dir, ".codex")) || existsSync8(join8(dir, ".agents", "skills"))) agents.push("codex");
4231
4256
  return agents.length > 0 ? agents : ["claude"];
4232
4257
  }
4233
4258
  function computeLocalScore(dir, targetAgent) {
@@ -4391,8 +4416,8 @@ function displayScoreDelta(before, after) {
4391
4416
  import chalk7 from "chalk";
4392
4417
  import ora from "ora";
4393
4418
  import select4 from "@inquirer/select";
4394
- import { mkdirSync, readFileSync as readFileSync7, readdirSync as readdirSync5, existsSync as existsSync9, writeFileSync } from "fs";
4395
- import { join as join8, dirname as dirname2 } from "path";
4419
+ import { mkdirSync, readFileSync as readFileSync8, readdirSync as readdirSync5, existsSync as existsSync9, writeFileSync } from "fs";
4420
+ import { join as join9, dirname as dirname2 } from "path";
4396
4421
 
4397
4422
  // src/scanner/index.ts
4398
4423
  import fs21 from "fs";
@@ -4711,19 +4736,19 @@ function detectLocalPlatforms() {
4711
4736
  }
4712
4737
  function getSkillPath(platform, slug) {
4713
4738
  if (platform === "cursor") {
4714
- return join8(".cursor", "skills", slug, "SKILL.md");
4739
+ return join9(".cursor", "skills", slug, "SKILL.md");
4715
4740
  }
4716
4741
  if (platform === "codex") {
4717
- return join8(".agents", "skills", slug, "SKILL.md");
4742
+ return join9(".agents", "skills", slug, "SKILL.md");
4718
4743
  }
4719
- return join8(".claude", "skills", slug, "SKILL.md");
4744
+ return join9(".claude", "skills", slug, "SKILL.md");
4720
4745
  }
4721
4746
  function getInstalledSkills() {
4722
4747
  const installed = /* @__PURE__ */ new Set();
4723
4748
  const dirs = [
4724
- join8(process.cwd(), ".claude", "skills"),
4725
- join8(process.cwd(), ".cursor", "skills"),
4726
- join8(process.cwd(), ".agents", "skills")
4749
+ join9(process.cwd(), ".claude", "skills"),
4750
+ join9(process.cwd(), ".cursor", "skills"),
4751
+ join9(process.cwd(), ".agents", "skills")
4727
4752
  ];
4728
4753
  for (const dir of dirs) {
4729
4754
  try {
@@ -4924,10 +4949,10 @@ Already installed skills: ${Array.from(installed).join(", ")}`);
4924
4949
  return parts.join("\n");
4925
4950
  }
4926
4951
  function extractTopDeps() {
4927
- const pkgPath = join8(process.cwd(), "package.json");
4952
+ const pkgPath = join9(process.cwd(), "package.json");
4928
4953
  if (!existsSync9(pkgPath)) return [];
4929
4954
  try {
4930
- const pkg3 = JSON.parse(readFileSync7(pkgPath, "utf-8"));
4955
+ const pkg3 = JSON.parse(readFileSync8(pkgPath, "utf-8"));
4931
4956
  const deps = Object.keys(pkg3.dependencies ?? {});
4932
4957
  const trivial = /* @__PURE__ */ new Set([
4933
4958
  "typescript",
@@ -5233,7 +5258,7 @@ async function installSkills(recs, platforms, contentMap) {
5233
5258
  if (!content) continue;
5234
5259
  for (const platform of platforms) {
5235
5260
  const skillPath = getSkillPath(platform, rec.slug);
5236
- const fullPath = join8(process.cwd(), skillPath);
5261
+ const fullPath = join9(process.cwd(), skillPath);
5237
5262
  mkdirSync(dirname2(fullPath), { recursive: true });
5238
5263
  writeFileSync(fullPath, content, "utf-8");
5239
5264
  installed.push(`[${platform}] ${skillPath}`);
@@ -5427,7 +5452,7 @@ async function initCommand(options) {
5427
5452
  `));
5428
5453
  if (report) {
5429
5454
  report.markStep("Fingerprint");
5430
- report.addJson("Fingerprint: Git", { remote: fingerprint.remote, packageName: fingerprint.packageName });
5455
+ report.addJson("Fingerprint: Git", { remote: fingerprint.gitRemoteUrl, packageName: fingerprint.packageName });
5431
5456
  report.addCodeBlock("Fingerprint: File Tree", fingerprint.fileTree.join("\n"));
5432
5457
  report.addJson("Fingerprint: Detected Stack", { languages: fingerprint.languages, frameworks: fingerprint.frameworks, tools: fingerprint.tools });
5433
5458
  report.addJson("Fingerprint: Existing Configs", fingerprint.existingConfigs);
@@ -5458,11 +5483,18 @@ async function initCommand(options) {
5458
5483
 
5459
5484
  | Check | Passed | Points | Max |
5460
5485
  |-------|--------|--------|-----|
5461
- ` + baselineScore.checks.map((c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.points} | ${c.maxPoints} |`).join("\n"));
5486
+ ` + baselineScore.checks.map((c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`).join("\n"));
5462
5487
  report.addSection("Generation: Target Agents", targetAgent.join(", "));
5463
5488
  }
5464
5489
  const hasExistingConfig = !!(fingerprint.existingConfigs.claudeMd || fingerprint.existingConfigs.claudeSettings || fingerprint.existingConfigs.claudeSkills?.length || fingerprint.existingConfigs.cursorrules || fingerprint.existingConfigs.cursorRules?.length || fingerprint.existingConfigs.agentsMd);
5465
- const NON_LLM_CHECKS = /* @__PURE__ */ new Set(["hooks_configured", "agents_md_exists", "permissions_configured", "mcp_servers"]);
5490
+ const NON_LLM_CHECKS = /* @__PURE__ */ new Set([
5491
+ "hooks_configured",
5492
+ "agents_md_exists",
5493
+ "permissions_configured",
5494
+ "mcp_servers",
5495
+ "service_coverage",
5496
+ "mcp_completeness"
5497
+ ]);
5466
5498
  if (hasExistingConfig && baselineScore.score === 100) {
5467
5499
  trackInitScoreComputed(baselineScore.score, passingCount, failingCount, true);
5468
5500
  console.log(chalk8.bold.green(" Your setup is already optimal \u2014 nothing to change.\n"));
@@ -5702,7 +5734,44 @@ async function initCommand(options) {
5702
5734
  if (hookChoice === "skip") {
5703
5735
  console.log(chalk8.dim(" Skipped auto-refresh hooks. Run ") + chalk8.hex("#83D1EB")("caliber hooks --install") + chalk8.dim(" later to enable."));
5704
5736
  }
5705
- const afterScore = computeLocalScore(process.cwd(), targetAgent);
5737
+ let afterScore = computeLocalScore(process.cwd(), targetAgent);
5738
+ if (afterScore.score < 100) {
5739
+ const polishFailingChecks = afterScore.checks.filter((c) => !c.passed && c.maxPoints > 0).filter((c) => !NON_LLM_CHECKS.has(c.id));
5740
+ if (polishFailingChecks.length > 0) {
5741
+ console.log("");
5742
+ console.log(chalk8.dim(` Score: ${afterScore.score}/100 \u2014 polishing ${polishFailingChecks.length} remaining check${polishFailingChecks.length === 1 ? "" : "s"}...`));
5743
+ const polishFailing = polishFailingChecks.map((c) => ({
5744
+ name: c.name,
5745
+ suggestion: c.suggestion
5746
+ }));
5747
+ const polishPassing = afterScore.checks.filter((c) => c.passed).map((c) => ({ name: c.name }));
5748
+ try {
5749
+ const polishResult = await generateSetup(
5750
+ fingerprint,
5751
+ targetAgent,
5752
+ void 0,
5753
+ {
5754
+ onStatus: () => {
5755
+ },
5756
+ onComplete: () => {
5757
+ },
5758
+ onError: () => {
5759
+ }
5760
+ },
5761
+ polishFailing,
5762
+ afterScore.score,
5763
+ polishPassing
5764
+ );
5765
+ if (polishResult.setup) {
5766
+ const polishWriteResult = writeSetup(polishResult.setup);
5767
+ if (polishWriteResult.written.length > 0) {
5768
+ afterScore = computeLocalScore(process.cwd(), targetAgent);
5769
+ }
5770
+ }
5771
+ } catch {
5772
+ }
5773
+ }
5774
+ }
5706
5775
  if (afterScore.score < baselineScore.score) {
5707
5776
  trackInitScoreRegression(baselineScore.score, afterScore.score);
5708
5777
  console.log("");
@@ -5723,7 +5792,7 @@ async function initCommand(options) {
5723
5792
 
5724
5793
  | Check | Passed | Points | Max |
5725
5794
  |-------|--------|--------|-----|
5726
- ` + afterScore.checks.map((c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.points} | ${c.maxPoints} |`).join("\n"));
5795
+ ` + afterScore.checks.map((c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`).join("\n"));
5727
5796
  }
5728
5797
  displayScoreDelta(baselineScore, afterScore);
5729
5798
  console.log(title.bold("\n Step 6/6 \u2014 Community skills\n"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rely-ai/caliber",
3
- "version": "1.7.9",
3
+ "version": "1.8.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": {