@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.
- package/dist/bin.js +224 -155
- 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:
|
|
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
|
|
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
|
|
3147
|
-
import { join as
|
|
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
|
|
3349
|
+
import { readFileSync as readFileSync2, readdirSync } from "fs";
|
|
3350
|
+
import { join as join2 } from "path";
|
|
3351
|
+
function readFileOrNull2(path26) {
|
|
3254
3352
|
try {
|
|
3255
|
-
return
|
|
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 =
|
|
3360
|
+
const claudeMd = readFileOrNull2(join2(dir, "CLAUDE.md"));
|
|
3336
3361
|
if (claudeMd) parts.push(claudeMd);
|
|
3337
|
-
const cursorrules =
|
|
3362
|
+
const cursorrules = readFileOrNull2(join2(dir, ".cursorrules"));
|
|
3338
3363
|
if (cursorrules) parts.push(cursorrules);
|
|
3339
|
-
for (const skillsDir of [
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
3545
|
-
const cursorRulesDir = existsSync3(
|
|
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(
|
|
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(
|
|
3569
|
-
const codexSkills = countFiles(
|
|
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(
|
|
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
|
|
3628
|
-
import { join as
|
|
3629
|
-
function
|
|
3652
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
3653
|
+
import { join as join4 } from "path";
|
|
3654
|
+
function readFileOrNull3(path26) {
|
|
3630
3655
|
try {
|
|
3631
|
-
return
|
|
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 =
|
|
3642
|
-
const cursorrules =
|
|
3643
|
-
const agentsMd =
|
|
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
|
|
3783
|
-
import { join as
|
|
3784
|
-
function
|
|
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
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
3842
|
-
const 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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
|
3974
|
-
import { join as
|
|
3975
|
-
function
|
|
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
|
|
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 =
|
|
3995
|
-
const agentsMdPath =
|
|
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 =
|
|
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 =
|
|
4082
|
+
const settingsPath = join6(dir, ".claude", "settings.json");
|
|
4058
4083
|
let hasPermissions = false;
|
|
4059
4084
|
let permissionDetail = "";
|
|
4060
|
-
const settingsContent =
|
|
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
|
|
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
|
|
4091
|
-
function
|
|
4115
|
+
import { join as join7 } from "path";
|
|
4116
|
+
function readFileOrNull6(path26) {
|
|
4092
4117
|
try {
|
|
4093
|
-
return
|
|
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 =
|
|
4102
|
-
const content =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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(
|
|
4229
|
-
if (existsSync8(
|
|
4230
|
-
if (existsSync8(
|
|
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
|
|
4395
|
-
import { join as
|
|
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
|
|
4739
|
+
return join9(".cursor", "skills", slug, "SKILL.md");
|
|
4715
4740
|
}
|
|
4716
4741
|
if (platform === "codex") {
|
|
4717
|
-
return
|
|
4742
|
+
return join9(".agents", "skills", slug, "SKILL.md");
|
|
4718
4743
|
}
|
|
4719
|
-
return
|
|
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
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
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 =
|
|
4952
|
+
const pkgPath = join9(process.cwd(), "package.json");
|
|
4928
4953
|
if (!existsSync9(pkgPath)) return [];
|
|
4929
4954
|
try {
|
|
4930
|
-
const pkg3 = JSON.parse(
|
|
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 =
|
|
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.
|
|
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.
|
|
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([
|
|
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
|
-
|
|
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.
|
|
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