@rely-ai/caliber 1.3.2 → 1.4.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 +281 -231
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -51,7 +51,7 @@ var init_constants = __esm({
|
|
|
51
51
|
|
|
52
52
|
// src/cli.ts
|
|
53
53
|
import { Command } from "commander";
|
|
54
|
-
import
|
|
54
|
+
import fs27 from "fs";
|
|
55
55
|
import path22 from "path";
|
|
56
56
|
import { fileURLToPath } from "url";
|
|
57
57
|
|
|
@@ -61,7 +61,7 @@ import ora2 from "ora";
|
|
|
61
61
|
import readline4 from "readline";
|
|
62
62
|
import select2 from "@inquirer/select";
|
|
63
63
|
import checkbox from "@inquirer/checkbox";
|
|
64
|
-
import
|
|
64
|
+
import fs20 from "fs";
|
|
65
65
|
|
|
66
66
|
// src/fingerprint/index.ts
|
|
67
67
|
import fs6 from "fs";
|
|
@@ -2204,25 +2204,92 @@ function openDiffsInEditor(editor, files) {
|
|
|
2204
2204
|
// src/commands/onboard.ts
|
|
2205
2205
|
import { createTwoFilesPatch } from "diff";
|
|
2206
2206
|
|
|
2207
|
-
// src/
|
|
2207
|
+
// src/commands/setup-files.ts
|
|
2208
2208
|
import fs14 from "fs";
|
|
2209
|
+
function buildSkillContent(skill) {
|
|
2210
|
+
const frontmatter = `---
|
|
2211
|
+
name: ${skill.name}
|
|
2212
|
+
description: ${skill.description}
|
|
2213
|
+
---
|
|
2214
|
+
|
|
2215
|
+
`;
|
|
2216
|
+
return frontmatter + skill.content;
|
|
2217
|
+
}
|
|
2218
|
+
function collectSetupFiles(setup) {
|
|
2219
|
+
const files = [];
|
|
2220
|
+
const claude = setup.claude;
|
|
2221
|
+
const cursor = setup.cursor;
|
|
2222
|
+
const codex = setup.codex;
|
|
2223
|
+
if (claude) {
|
|
2224
|
+
if (claude.claudeMd) files.push({ path: "CLAUDE.md", content: claude.claudeMd });
|
|
2225
|
+
const skills = claude.skills;
|
|
2226
|
+
if (Array.isArray(skills)) {
|
|
2227
|
+
for (const skill of skills) {
|
|
2228
|
+
files.push({ path: `.claude/skills/${skill.name}/SKILL.md`, content: buildSkillContent(skill) });
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
if (codex) {
|
|
2233
|
+
if (codex.agentsMd) files.push({ path: "AGENTS.md", content: codex.agentsMd });
|
|
2234
|
+
const codexSkills = codex.skills;
|
|
2235
|
+
if (Array.isArray(codexSkills)) {
|
|
2236
|
+
for (const skill of codexSkills) {
|
|
2237
|
+
files.push({ path: `.agents/skills/${skill.name}/SKILL.md`, content: buildSkillContent(skill) });
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
if (cursor) {
|
|
2242
|
+
if (cursor.cursorrules) files.push({ path: ".cursorrules", content: cursor.cursorrules });
|
|
2243
|
+
const cursorSkills = cursor.skills;
|
|
2244
|
+
if (Array.isArray(cursorSkills)) {
|
|
2245
|
+
for (const skill of cursorSkills) {
|
|
2246
|
+
files.push({ path: `.cursor/skills/${skill.name}/SKILL.md`, content: buildSkillContent(skill) });
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
const rules = cursor.rules;
|
|
2250
|
+
if (Array.isArray(rules)) {
|
|
2251
|
+
for (const rule of rules) {
|
|
2252
|
+
files.push({ path: `.cursor/rules/${rule.filename}`, content: rule.content });
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
if (!fs14.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
|
|
2257
|
+
const agentRefs = [];
|
|
2258
|
+
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
2259
|
+
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
2260
|
+
if (agentRefs.length === 0) agentRefs.push("See CLAUDE.md and .cursor/rules/ for agent configurations.");
|
|
2261
|
+
files.push({
|
|
2262
|
+
path: "AGENTS.md",
|
|
2263
|
+
content: `# AGENTS.md
|
|
2264
|
+
|
|
2265
|
+
This project uses AI coding agents configured by [Caliber](https://github.com/rely-ai-org/caliber).
|
|
2266
|
+
|
|
2267
|
+
${agentRefs.join(" ")}
|
|
2268
|
+
`
|
|
2269
|
+
});
|
|
2270
|
+
}
|
|
2271
|
+
return files;
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
// src/lib/hooks.ts
|
|
2275
|
+
import fs15 from "fs";
|
|
2209
2276
|
import path12 from "path";
|
|
2210
2277
|
import { execSync as execSync5 } from "child_process";
|
|
2211
2278
|
var SETTINGS_PATH = path12.join(".claude", "settings.json");
|
|
2212
2279
|
var HOOK_COMMAND = "caliber refresh --quiet";
|
|
2213
2280
|
var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
|
|
2214
2281
|
function readSettings() {
|
|
2215
|
-
if (!
|
|
2282
|
+
if (!fs15.existsSync(SETTINGS_PATH)) return {};
|
|
2216
2283
|
try {
|
|
2217
|
-
return JSON.parse(
|
|
2284
|
+
return JSON.parse(fs15.readFileSync(SETTINGS_PATH, "utf-8"));
|
|
2218
2285
|
} catch {
|
|
2219
2286
|
return {};
|
|
2220
2287
|
}
|
|
2221
2288
|
}
|
|
2222
2289
|
function writeSettings(settings) {
|
|
2223
2290
|
const dir = path12.dirname(SETTINGS_PATH);
|
|
2224
|
-
if (!
|
|
2225
|
-
|
|
2291
|
+
if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
|
|
2292
|
+
fs15.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
2226
2293
|
}
|
|
2227
2294
|
function findHookIndex(sessionEnd) {
|
|
2228
2295
|
return sessionEnd.findIndex(
|
|
@@ -2291,8 +2358,8 @@ function getPreCommitPath() {
|
|
|
2291
2358
|
}
|
|
2292
2359
|
function isPreCommitHookInstalled() {
|
|
2293
2360
|
const hookPath = getPreCommitPath();
|
|
2294
|
-
if (!hookPath || !
|
|
2295
|
-
const content =
|
|
2361
|
+
if (!hookPath || !fs15.existsSync(hookPath)) return false;
|
|
2362
|
+
const content = fs15.readFileSync(hookPath, "utf-8");
|
|
2296
2363
|
return content.includes(PRECOMMIT_START);
|
|
2297
2364
|
}
|
|
2298
2365
|
function installPreCommitHook() {
|
|
@@ -2302,40 +2369,40 @@ function installPreCommitHook() {
|
|
|
2302
2369
|
const hookPath = getPreCommitPath();
|
|
2303
2370
|
if (!hookPath) return { installed: false, alreadyInstalled: false };
|
|
2304
2371
|
const hooksDir = path12.dirname(hookPath);
|
|
2305
|
-
if (!
|
|
2372
|
+
if (!fs15.existsSync(hooksDir)) fs15.mkdirSync(hooksDir, { recursive: true });
|
|
2306
2373
|
let content = "";
|
|
2307
|
-
if (
|
|
2308
|
-
content =
|
|
2374
|
+
if (fs15.existsSync(hookPath)) {
|
|
2375
|
+
content = fs15.readFileSync(hookPath, "utf-8");
|
|
2309
2376
|
if (!content.endsWith("\n")) content += "\n";
|
|
2310
2377
|
content += "\n" + PRECOMMIT_BLOCK + "\n";
|
|
2311
2378
|
} else {
|
|
2312
2379
|
content = "#!/bin/sh\n\n" + PRECOMMIT_BLOCK + "\n";
|
|
2313
2380
|
}
|
|
2314
|
-
|
|
2315
|
-
|
|
2381
|
+
fs15.writeFileSync(hookPath, content);
|
|
2382
|
+
fs15.chmodSync(hookPath, 493);
|
|
2316
2383
|
return { installed: true, alreadyInstalled: false };
|
|
2317
2384
|
}
|
|
2318
2385
|
function removePreCommitHook() {
|
|
2319
2386
|
const hookPath = getPreCommitPath();
|
|
2320
|
-
if (!hookPath || !
|
|
2387
|
+
if (!hookPath || !fs15.existsSync(hookPath)) {
|
|
2321
2388
|
return { removed: false, notFound: true };
|
|
2322
2389
|
}
|
|
2323
|
-
let content =
|
|
2390
|
+
let content = fs15.readFileSync(hookPath, "utf-8");
|
|
2324
2391
|
if (!content.includes(PRECOMMIT_START)) {
|
|
2325
2392
|
return { removed: false, notFound: true };
|
|
2326
2393
|
}
|
|
2327
2394
|
const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
|
|
2328
2395
|
content = content.replace(regex, "\n");
|
|
2329
2396
|
if (content.trim() === "#!/bin/sh" || content.trim() === "") {
|
|
2330
|
-
|
|
2397
|
+
fs15.unlinkSync(hookPath);
|
|
2331
2398
|
} else {
|
|
2332
|
-
|
|
2399
|
+
fs15.writeFileSync(hookPath, content);
|
|
2333
2400
|
}
|
|
2334
2401
|
return { removed: true, notFound: false };
|
|
2335
2402
|
}
|
|
2336
2403
|
|
|
2337
2404
|
// src/lib/learning-hooks.ts
|
|
2338
|
-
import
|
|
2405
|
+
import fs16 from "fs";
|
|
2339
2406
|
import path13 from "path";
|
|
2340
2407
|
var SETTINGS_PATH2 = path13.join(".claude", "settings.json");
|
|
2341
2408
|
var HOOK_CONFIGS = [
|
|
@@ -2356,17 +2423,17 @@ var HOOK_CONFIGS = [
|
|
|
2356
2423
|
}
|
|
2357
2424
|
];
|
|
2358
2425
|
function readSettings2() {
|
|
2359
|
-
if (!
|
|
2426
|
+
if (!fs16.existsSync(SETTINGS_PATH2)) return {};
|
|
2360
2427
|
try {
|
|
2361
|
-
return JSON.parse(
|
|
2428
|
+
return JSON.parse(fs16.readFileSync(SETTINGS_PATH2, "utf-8"));
|
|
2362
2429
|
} catch {
|
|
2363
2430
|
return {};
|
|
2364
2431
|
}
|
|
2365
2432
|
}
|
|
2366
2433
|
function writeSettings2(settings) {
|
|
2367
2434
|
const dir = path13.dirname(SETTINGS_PATH2);
|
|
2368
|
-
if (!
|
|
2369
|
-
|
|
2435
|
+
if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
|
|
2436
|
+
fs16.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
|
|
2370
2437
|
}
|
|
2371
2438
|
function hasLearningHook(matchers, command) {
|
|
2372
2439
|
return matchers.some((entry) => entry.hooks?.some((h) => h.command === command));
|
|
@@ -2423,7 +2490,7 @@ function removeLearningHooks() {
|
|
|
2423
2490
|
|
|
2424
2491
|
// src/lib/state.ts
|
|
2425
2492
|
init_constants();
|
|
2426
|
-
import
|
|
2493
|
+
import fs17 from "fs";
|
|
2427
2494
|
import path14 from "path";
|
|
2428
2495
|
import { execSync as execSync6 } from "child_process";
|
|
2429
2496
|
var STATE_FILE = path14.join(CALIBER_DIR, ".caliber-state.json");
|
|
@@ -2437,8 +2504,8 @@ function normalizeTargetAgent(value) {
|
|
|
2437
2504
|
}
|
|
2438
2505
|
function readState() {
|
|
2439
2506
|
try {
|
|
2440
|
-
if (!
|
|
2441
|
-
const raw = JSON.parse(
|
|
2507
|
+
if (!fs17.existsSync(STATE_FILE)) return null;
|
|
2508
|
+
const raw = JSON.parse(fs17.readFileSync(STATE_FILE, "utf-8"));
|
|
2442
2509
|
if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
|
|
2443
2510
|
return raw;
|
|
2444
2511
|
} catch {
|
|
@@ -2446,10 +2513,10 @@ function readState() {
|
|
|
2446
2513
|
}
|
|
2447
2514
|
}
|
|
2448
2515
|
function writeState(state) {
|
|
2449
|
-
if (!
|
|
2450
|
-
|
|
2516
|
+
if (!fs17.existsSync(CALIBER_DIR)) {
|
|
2517
|
+
fs17.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
2451
2518
|
}
|
|
2452
|
-
|
|
2519
|
+
fs17.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
|
|
2453
2520
|
}
|
|
2454
2521
|
function getCurrentHeadSha() {
|
|
2455
2522
|
try {
|
|
@@ -3670,22 +3737,22 @@ function checkBonus(dir) {
|
|
|
3670
3737
|
|
|
3671
3738
|
// src/scoring/dismissed.ts
|
|
3672
3739
|
init_constants();
|
|
3673
|
-
import
|
|
3740
|
+
import fs18 from "fs";
|
|
3674
3741
|
import path15 from "path";
|
|
3675
3742
|
var DISMISSED_FILE = path15.join(CALIBER_DIR, "dismissed-checks.json");
|
|
3676
3743
|
function readDismissedChecks() {
|
|
3677
3744
|
try {
|
|
3678
|
-
if (!
|
|
3679
|
-
return JSON.parse(
|
|
3745
|
+
if (!fs18.existsSync(DISMISSED_FILE)) return [];
|
|
3746
|
+
return JSON.parse(fs18.readFileSync(DISMISSED_FILE, "utf-8"));
|
|
3680
3747
|
} catch {
|
|
3681
3748
|
return [];
|
|
3682
3749
|
}
|
|
3683
3750
|
}
|
|
3684
3751
|
function writeDismissedChecks(checks) {
|
|
3685
|
-
if (!
|
|
3686
|
-
|
|
3752
|
+
if (!fs18.existsSync(CALIBER_DIR)) {
|
|
3753
|
+
fs18.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
3687
3754
|
}
|
|
3688
|
-
|
|
3755
|
+
fs18.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
|
|
3689
3756
|
}
|
|
3690
3757
|
function getDismissedIds() {
|
|
3691
3758
|
return new Set(readDismissedChecks().map((c) => c.id));
|
|
@@ -3799,15 +3866,13 @@ function displayScore(result) {
|
|
|
3799
3866
|
const gc = gradeColor(result.grade);
|
|
3800
3867
|
const agentLabel = result.targetAgent.map((a) => AGENT_DISPLAY_NAMES[a] || a).join(" + ");
|
|
3801
3868
|
console.log("");
|
|
3802
|
-
console.log(chalk3.gray(" \
|
|
3803
|
-
console.log(
|
|
3804
|
-
console.log(
|
|
3805
|
-
|
|
3806
|
-
);
|
|
3807
|
-
console.log(
|
|
3808
|
-
console.log(chalk3.gray(" \
|
|
3809
|
-
console.log(chalk3.gray(" \u2502") + " " + chalk3.gray("\u2502"));
|
|
3810
|
-
console.log(chalk3.gray(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
|
|
3869
|
+
console.log(chalk3.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
3870
|
+
console.log("");
|
|
3871
|
+
console.log(` ${chalk3.bold("Agent Config Score")} ${gc(chalk3.bold(`${result.score} / ${result.maxScore}`))} Grade ${gc(chalk3.bold(result.grade))}`);
|
|
3872
|
+
console.log(` ${progressBar(result.score, result.maxScore)}`);
|
|
3873
|
+
console.log(chalk3.dim(` Target: ${agentLabel}`));
|
|
3874
|
+
console.log("");
|
|
3875
|
+
console.log(chalk3.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
3811
3876
|
console.log("");
|
|
3812
3877
|
for (const category of CATEGORY_ORDER) {
|
|
3813
3878
|
const summary = result.categories[category];
|
|
@@ -3847,26 +3912,15 @@ function displayScoreDelta(before, after) {
|
|
|
3847
3912
|
const deltaColor = delta >= 0 ? chalk3.green : chalk3.red;
|
|
3848
3913
|
const beforeGc = gradeColor(before.grade);
|
|
3849
3914
|
const afterGc = gradeColor(after.grade);
|
|
3850
|
-
const BOX_INNER = 51;
|
|
3851
|
-
const scorePart = `Score: ${before.score} > ${after.score}`;
|
|
3852
|
-
const deltaPart = `${deltaStr} pts`;
|
|
3853
|
-
const gradePart = `${before.grade} > ${after.grade}`;
|
|
3854
|
-
const contentLen = 3 + scorePart.length + deltaPart.length + gradePart.length + 8;
|
|
3855
|
-
const totalPad = BOX_INNER - contentLen;
|
|
3856
|
-
const pad1 = Math.max(2, Math.ceil(totalPad / 2));
|
|
3857
|
-
const pad2 = Math.max(1, totalPad - pad1);
|
|
3858
|
-
const scoreLineFormatted = " Score: " + beforeGc(`${before.score}`) + chalk3.gray(" \u2192 ") + afterGc(`${after.score}`) + " ".repeat(pad1) + deltaColor(deltaPart) + " ".repeat(pad2) + beforeGc(before.grade) + chalk3.gray(" \u2192 ") + afterGc(after.grade);
|
|
3859
|
-
const visibleLen = 3 + scorePart.length + pad1 + deltaPart.length + pad2 + gradePart.length;
|
|
3860
|
-
const trailingPad = Math.max(0, BOX_INNER - visibleLen);
|
|
3861
|
-
const barWidth = Math.floor((BOX_INNER - 12) / 2);
|
|
3862
|
-
const barLine = ` ${progressBar(before.score, before.maxScore, barWidth)}` + chalk3.gray(" \u2192 ") + progressBar(after.score, after.maxScore, barWidth) + " ";
|
|
3863
3915
|
console.log("");
|
|
3864
|
-
console.log(chalk3.gray(" \
|
|
3865
|
-
console.log(
|
|
3866
|
-
console.log(
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
console.log(chalk3.gray("
|
|
3916
|
+
console.log(chalk3.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
3917
|
+
console.log("");
|
|
3918
|
+
console.log(
|
|
3919
|
+
` Score: ${beforeGc(`${before.score}`)} ${chalk3.gray("\u2192")} ${afterGc(`${after.score}`)} ${deltaColor(deltaStr + " pts")} ${beforeGc(before.grade)} ${chalk3.gray("\u2192")} ${afterGc(after.grade)}`
|
|
3920
|
+
);
|
|
3921
|
+
console.log(` ${progressBar(before.score, before.maxScore, 19)} ${chalk3.gray("\u2192")} ${progressBar(after.score, after.maxScore, 19)}`);
|
|
3922
|
+
console.log("");
|
|
3923
|
+
console.log(chalk3.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
3870
3924
|
console.log("");
|
|
3871
3925
|
const improved = after.checks.filter((ac) => {
|
|
3872
3926
|
const bc = before.checks.find((b) => b.id === ac.id);
|
|
@@ -3889,7 +3943,7 @@ function displayScoreDelta(before, after) {
|
|
|
3889
3943
|
import chalk4 from "chalk";
|
|
3890
3944
|
import ora from "ora";
|
|
3891
3945
|
import readline3 from "readline";
|
|
3892
|
-
import
|
|
3946
|
+
import fs19 from "fs";
|
|
3893
3947
|
import path16 from "path";
|
|
3894
3948
|
|
|
3895
3949
|
// src/mcp/search.ts
|
|
@@ -4242,7 +4296,7 @@ async function discoverAndInstallMcps(targetAgent, fingerprint, dir) {
|
|
|
4242
4296
|
}
|
|
4243
4297
|
if (targetAgent.includes("cursor")) {
|
|
4244
4298
|
const cursorDir = path16.join(dir, ".cursor");
|
|
4245
|
-
if (!
|
|
4299
|
+
if (!fs19.existsSync(cursorDir)) fs19.mkdirSync(cursorDir, { recursive: true });
|
|
4246
4300
|
writeMcpJson(path16.join(cursorDir, "mcp.json"), mcpServers);
|
|
4247
4301
|
}
|
|
4248
4302
|
return { installed: installedNames.length, names: installedNames };
|
|
@@ -4250,14 +4304,14 @@ async function discoverAndInstallMcps(targetAgent, fingerprint, dir) {
|
|
|
4250
4304
|
function writeMcpJson(filePath, mcpServers) {
|
|
4251
4305
|
let existing = {};
|
|
4252
4306
|
try {
|
|
4253
|
-
if (
|
|
4254
|
-
const parsed = JSON.parse(
|
|
4307
|
+
if (fs19.existsSync(filePath)) {
|
|
4308
|
+
const parsed = JSON.parse(fs19.readFileSync(filePath, "utf-8"));
|
|
4255
4309
|
if (parsed.mcpServers) existing = parsed.mcpServers;
|
|
4256
4310
|
}
|
|
4257
4311
|
} catch {
|
|
4258
4312
|
}
|
|
4259
4313
|
const merged = { ...existing, ...mcpServers };
|
|
4260
|
-
|
|
4314
|
+
fs19.writeFileSync(filePath, JSON.stringify({ mcpServers: merged }, null, 2) + "\n");
|
|
4261
4315
|
}
|
|
4262
4316
|
function getExistingMcpNames(fingerprint, targetAgent) {
|
|
4263
4317
|
const names = [];
|
|
@@ -4872,8 +4926,8 @@ async function openReview(method, stagedFiles) {
|
|
|
4872
4926
|
return;
|
|
4873
4927
|
}
|
|
4874
4928
|
const fileInfos = stagedFiles.map((file) => {
|
|
4875
|
-
const proposed =
|
|
4876
|
-
const current = file.currentPath ?
|
|
4929
|
+
const proposed = fs20.readFileSync(file.proposedPath, "utf-8");
|
|
4930
|
+
const current = file.currentPath ? fs20.readFileSync(file.currentPath, "utf-8") : "";
|
|
4877
4931
|
const patch = createTwoFilesPatch(
|
|
4878
4932
|
file.isNew ? "/dev/null" : file.relativePath,
|
|
4879
4933
|
file.relativePath,
|
|
@@ -5056,7 +5110,7 @@ function printSetupSummary(setup) {
|
|
|
5056
5110
|
};
|
|
5057
5111
|
if (claude) {
|
|
5058
5112
|
if (claude.claudeMd) {
|
|
5059
|
-
const icon =
|
|
5113
|
+
const icon = fs20.existsSync("CLAUDE.md") ? chalk5.yellow("~") : chalk5.green("+");
|
|
5060
5114
|
const desc = getDescription("CLAUDE.md");
|
|
5061
5115
|
console.log(` ${icon} ${chalk5.bold("CLAUDE.md")}`);
|
|
5062
5116
|
if (desc) console.log(chalk5.dim(` ${desc}`));
|
|
@@ -5066,7 +5120,7 @@ function printSetupSummary(setup) {
|
|
|
5066
5120
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
5067
5121
|
for (const skill of skills) {
|
|
5068
5122
|
const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
|
|
5069
|
-
const icon =
|
|
5123
|
+
const icon = fs20.existsSync(skillPath) ? chalk5.yellow("~") : chalk5.green("+");
|
|
5070
5124
|
const desc = getDescription(skillPath);
|
|
5071
5125
|
console.log(` ${icon} ${chalk5.bold(skillPath)}`);
|
|
5072
5126
|
console.log(chalk5.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -5077,7 +5131,7 @@ function printSetupSummary(setup) {
|
|
|
5077
5131
|
const codex = setup.codex;
|
|
5078
5132
|
if (codex) {
|
|
5079
5133
|
if (codex.agentsMd) {
|
|
5080
|
-
const icon =
|
|
5134
|
+
const icon = fs20.existsSync("AGENTS.md") ? chalk5.yellow("~") : chalk5.green("+");
|
|
5081
5135
|
const desc = getDescription("AGENTS.md");
|
|
5082
5136
|
console.log(` ${icon} ${chalk5.bold("AGENTS.md")}`);
|
|
5083
5137
|
if (desc) console.log(chalk5.dim(` ${desc}`));
|
|
@@ -5087,7 +5141,7 @@ function printSetupSummary(setup) {
|
|
|
5087
5141
|
if (Array.isArray(codexSkills) && codexSkills.length > 0) {
|
|
5088
5142
|
for (const skill of codexSkills) {
|
|
5089
5143
|
const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
|
|
5090
|
-
const icon =
|
|
5144
|
+
const icon = fs20.existsSync(skillPath) ? chalk5.yellow("~") : chalk5.green("+");
|
|
5091
5145
|
const desc = getDescription(skillPath);
|
|
5092
5146
|
console.log(` ${icon} ${chalk5.bold(skillPath)}`);
|
|
5093
5147
|
console.log(chalk5.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -5097,7 +5151,7 @@ function printSetupSummary(setup) {
|
|
|
5097
5151
|
}
|
|
5098
5152
|
if (cursor) {
|
|
5099
5153
|
if (cursor.cursorrules) {
|
|
5100
|
-
const icon =
|
|
5154
|
+
const icon = fs20.existsSync(".cursorrules") ? chalk5.yellow("~") : chalk5.green("+");
|
|
5101
5155
|
const desc = getDescription(".cursorrules");
|
|
5102
5156
|
console.log(` ${icon} ${chalk5.bold(".cursorrules")}`);
|
|
5103
5157
|
if (desc) console.log(chalk5.dim(` ${desc}`));
|
|
@@ -5107,7 +5161,7 @@ function printSetupSummary(setup) {
|
|
|
5107
5161
|
if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
|
|
5108
5162
|
for (const skill of cursorSkills) {
|
|
5109
5163
|
const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
|
|
5110
|
-
const icon =
|
|
5164
|
+
const icon = fs20.existsSync(skillPath) ? chalk5.yellow("~") : chalk5.green("+");
|
|
5111
5165
|
const desc = getDescription(skillPath);
|
|
5112
5166
|
console.log(` ${icon} ${chalk5.bold(skillPath)}`);
|
|
5113
5167
|
console.log(chalk5.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -5118,7 +5172,7 @@ function printSetupSummary(setup) {
|
|
|
5118
5172
|
if (Array.isArray(rules) && rules.length > 0) {
|
|
5119
5173
|
for (const rule of rules) {
|
|
5120
5174
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
5121
|
-
const icon =
|
|
5175
|
+
const icon = fs20.existsSync(rulePath) ? chalk5.yellow("~") : chalk5.green("+");
|
|
5122
5176
|
const desc = getDescription(rulePath);
|
|
5123
5177
|
console.log(` ${icon} ${chalk5.bold(rulePath)}`);
|
|
5124
5178
|
if (desc) {
|
|
@@ -5131,7 +5185,7 @@ function printSetupSummary(setup) {
|
|
|
5131
5185
|
}
|
|
5132
5186
|
}
|
|
5133
5187
|
}
|
|
5134
|
-
if (!codex && !
|
|
5188
|
+
if (!codex && !fs20.existsSync("AGENTS.md")) {
|
|
5135
5189
|
console.log(` ${chalk5.green("+")} ${chalk5.bold("AGENTS.md")}`);
|
|
5136
5190
|
console.log(chalk5.dim(" Cross-agent coordination file"));
|
|
5137
5191
|
console.log("");
|
|
@@ -5146,21 +5200,12 @@ function printSetupSummary(setup) {
|
|
|
5146
5200
|
console.log(` ${chalk5.green("+")} ${chalk5.dim("new")} ${chalk5.yellow("~")} ${chalk5.dim("modified")} ${chalk5.red("-")} ${chalk5.dim("removed")}`);
|
|
5147
5201
|
console.log("");
|
|
5148
5202
|
}
|
|
5149
|
-
function buildSkillContent(skill) {
|
|
5150
|
-
const frontmatter = `---
|
|
5151
|
-
name: ${skill.name}
|
|
5152
|
-
description: ${skill.description}
|
|
5153
|
-
---
|
|
5154
|
-
|
|
5155
|
-
`;
|
|
5156
|
-
return frontmatter + skill.content;
|
|
5157
|
-
}
|
|
5158
5203
|
function ensurePermissions() {
|
|
5159
5204
|
const settingsPath = ".claude/settings.json";
|
|
5160
5205
|
let settings = {};
|
|
5161
5206
|
try {
|
|
5162
|
-
if (
|
|
5163
|
-
settings = JSON.parse(
|
|
5207
|
+
if (fs20.existsSync(settingsPath)) {
|
|
5208
|
+
settings = JSON.parse(fs20.readFileSync(settingsPath, "utf-8"));
|
|
5164
5209
|
}
|
|
5165
5210
|
} catch {
|
|
5166
5211
|
}
|
|
@@ -5174,64 +5219,8 @@ function ensurePermissions() {
|
|
|
5174
5219
|
"Bash(git *)"
|
|
5175
5220
|
];
|
|
5176
5221
|
settings.permissions = permissions;
|
|
5177
|
-
if (!
|
|
5178
|
-
|
|
5179
|
-
}
|
|
5180
|
-
function collectSetupFiles(setup) {
|
|
5181
|
-
const files = [];
|
|
5182
|
-
const claude = setup.claude;
|
|
5183
|
-
const cursor = setup.cursor;
|
|
5184
|
-
const codex = setup.codex;
|
|
5185
|
-
if (claude) {
|
|
5186
|
-
if (claude.claudeMd) files.push({ path: "CLAUDE.md", content: claude.claudeMd });
|
|
5187
|
-
const skills = claude.skills;
|
|
5188
|
-
if (Array.isArray(skills)) {
|
|
5189
|
-
for (const skill of skills) {
|
|
5190
|
-
files.push({ path: `.claude/skills/${skill.name}/SKILL.md`, content: buildSkillContent(skill) });
|
|
5191
|
-
}
|
|
5192
|
-
}
|
|
5193
|
-
}
|
|
5194
|
-
if (codex) {
|
|
5195
|
-
if (codex.agentsMd) files.push({ path: "AGENTS.md", content: codex.agentsMd });
|
|
5196
|
-
const codexSkills = codex.skills;
|
|
5197
|
-
if (Array.isArray(codexSkills)) {
|
|
5198
|
-
for (const skill of codexSkills) {
|
|
5199
|
-
files.push({ path: `.agents/skills/${skill.name}/SKILL.md`, content: buildSkillContent(skill) });
|
|
5200
|
-
}
|
|
5201
|
-
}
|
|
5202
|
-
}
|
|
5203
|
-
if (cursor) {
|
|
5204
|
-
if (cursor.cursorrules) files.push({ path: ".cursorrules", content: cursor.cursorrules });
|
|
5205
|
-
const cursorSkills = cursor.skills;
|
|
5206
|
-
if (Array.isArray(cursorSkills)) {
|
|
5207
|
-
for (const skill of cursorSkills) {
|
|
5208
|
-
files.push({ path: `.cursor/skills/${skill.name}/SKILL.md`, content: buildSkillContent(skill) });
|
|
5209
|
-
}
|
|
5210
|
-
}
|
|
5211
|
-
const rules = cursor.rules;
|
|
5212
|
-
if (Array.isArray(rules)) {
|
|
5213
|
-
for (const rule of rules) {
|
|
5214
|
-
files.push({ path: `.cursor/rules/${rule.filename}`, content: rule.content });
|
|
5215
|
-
}
|
|
5216
|
-
}
|
|
5217
|
-
}
|
|
5218
|
-
const hasCodexAgentsMd = codex && codex.agentsMd;
|
|
5219
|
-
if (!fs19.existsSync("AGENTS.md") && !hasCodexAgentsMd) {
|
|
5220
|
-
const agentRefs = [];
|
|
5221
|
-
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
5222
|
-
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
5223
|
-
if (agentRefs.length === 0) agentRefs.push("See CLAUDE.md and .cursor/rules/ for agent configurations.");
|
|
5224
|
-
files.push({
|
|
5225
|
-
path: "AGENTS.md",
|
|
5226
|
-
content: `# AGENTS.md
|
|
5227
|
-
|
|
5228
|
-
This project uses AI coding agents configured by [Caliber](https://github.com/rely-ai-org/caliber).
|
|
5229
|
-
|
|
5230
|
-
${agentRefs.join(" ")}
|
|
5231
|
-
`
|
|
5232
|
-
});
|
|
5233
|
-
}
|
|
5234
|
-
return files;
|
|
5222
|
+
if (!fs20.existsSync(".claude")) fs20.mkdirSync(".claude", { recursive: true });
|
|
5223
|
+
fs20.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
5235
5224
|
}
|
|
5236
5225
|
|
|
5237
5226
|
// src/commands/undo.ts
|
|
@@ -5267,7 +5256,7 @@ function undoCommand() {
|
|
|
5267
5256
|
|
|
5268
5257
|
// src/commands/status.ts
|
|
5269
5258
|
import chalk7 from "chalk";
|
|
5270
|
-
import
|
|
5259
|
+
import fs21 from "fs";
|
|
5271
5260
|
async function statusCommand(options) {
|
|
5272
5261
|
const config = loadConfig();
|
|
5273
5262
|
const manifest = readManifest();
|
|
@@ -5293,7 +5282,7 @@ async function statusCommand(options) {
|
|
|
5293
5282
|
}
|
|
5294
5283
|
console.log(` Files managed: ${chalk7.cyan(manifest.entries.length.toString())}`);
|
|
5295
5284
|
for (const entry of manifest.entries) {
|
|
5296
|
-
const exists =
|
|
5285
|
+
const exists = fs21.existsSync(entry.path);
|
|
5297
5286
|
const icon = exists ? chalk7.green("\u2713") : chalk7.red("\u2717");
|
|
5298
5287
|
console.log(` ${icon} ${entry.path} (${entry.action})`);
|
|
5299
5288
|
}
|
|
@@ -5303,11 +5292,11 @@ async function statusCommand(options) {
|
|
|
5303
5292
|
// src/commands/regenerate.ts
|
|
5304
5293
|
import chalk8 from "chalk";
|
|
5305
5294
|
import ora4 from "ora";
|
|
5306
|
-
import
|
|
5295
|
+
import select3 from "@inquirer/select";
|
|
5307
5296
|
async function regenerateCommand(options) {
|
|
5308
5297
|
const config = loadConfig();
|
|
5309
5298
|
if (!config) {
|
|
5310
|
-
console.log(chalk8.red("No LLM provider configured. Run ") + chalk8.hex("#83D1EB")("caliber config") + chalk8.red("
|
|
5299
|
+
console.log(chalk8.red("No LLM provider configured. Run ") + chalk8.hex("#83D1EB")("caliber config") + chalk8.red(" first."));
|
|
5311
5300
|
throw new Error("__exit__");
|
|
5312
5301
|
}
|
|
5313
5302
|
const manifest = readManifest();
|
|
@@ -5315,16 +5304,19 @@ async function regenerateCommand(options) {
|
|
|
5315
5304
|
console.log(chalk8.yellow("No existing setup found. Run ") + chalk8.hex("#83D1EB")("caliber onboard") + chalk8.yellow(" first."));
|
|
5316
5305
|
throw new Error("__exit__");
|
|
5317
5306
|
}
|
|
5318
|
-
const
|
|
5307
|
+
const targetAgent = readState()?.targetAgent ?? ["claude", "cursor"];
|
|
5308
|
+
const spinner = ora4("Analyzing project...").start();
|
|
5319
5309
|
const fingerprint = collectFingerprint(process.cwd());
|
|
5320
|
-
|
|
5310
|
+
await enrichFingerprintWithLLM(fingerprint, process.cwd());
|
|
5311
|
+
spinner.succeed("Project analyzed");
|
|
5312
|
+
const baselineScore = computeLocalScore(process.cwd(), targetAgent);
|
|
5313
|
+
displayScoreSummary(baselineScore);
|
|
5321
5314
|
const genSpinner = ora4("Regenerating setup...").start();
|
|
5322
|
-
const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES);
|
|
5315
|
+
const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, { showElapsedTime: true });
|
|
5323
5316
|
genMessages.start();
|
|
5324
5317
|
let generatedSetup = null;
|
|
5325
5318
|
try {
|
|
5326
|
-
const
|
|
5327
|
-
const result2 = await generateSetup(
|
|
5319
|
+
const result = await generateSetup(
|
|
5328
5320
|
fingerprint,
|
|
5329
5321
|
targetAgent,
|
|
5330
5322
|
void 0,
|
|
@@ -5341,7 +5333,7 @@ async function regenerateCommand(options) {
|
|
|
5341
5333
|
}
|
|
5342
5334
|
}
|
|
5343
5335
|
);
|
|
5344
|
-
if (!generatedSetup) generatedSetup =
|
|
5336
|
+
if (!generatedSetup) generatedSetup = result.setup;
|
|
5345
5337
|
} catch (err) {
|
|
5346
5338
|
genMessages.stop();
|
|
5347
5339
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -5354,23 +5346,81 @@ async function regenerateCommand(options) {
|
|
|
5354
5346
|
throw new Error("__exit__");
|
|
5355
5347
|
}
|
|
5356
5348
|
genSpinner.succeed("Setup regenerated");
|
|
5349
|
+
const setupFiles = collectSetupFiles(generatedSetup);
|
|
5350
|
+
const staged = stageFiles(setupFiles, process.cwd());
|
|
5351
|
+
const totalChanges = staged.newFiles + staged.modifiedFiles;
|
|
5352
|
+
console.log(chalk8.dim(`
|
|
5353
|
+
${chalk8.green(`${staged.newFiles} new`)} / ${chalk8.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}
|
|
5354
|
+
`));
|
|
5355
|
+
if (totalChanges === 0) {
|
|
5356
|
+
console.log(chalk8.dim(" No changes needed \u2014 your configs are already up to date.\n"));
|
|
5357
|
+
cleanupStaging();
|
|
5358
|
+
return;
|
|
5359
|
+
}
|
|
5357
5360
|
if (options.dryRun) {
|
|
5358
|
-
console.log(chalk8.yellow("
|
|
5359
|
-
|
|
5361
|
+
console.log(chalk8.yellow("[Dry run] Would write:"));
|
|
5362
|
+
for (const f of staged.stagedFiles) {
|
|
5363
|
+
console.log(` ${f.isNew ? chalk8.green("+") : chalk8.yellow("~")} ${f.relativePath}`);
|
|
5364
|
+
}
|
|
5365
|
+
cleanupStaging();
|
|
5360
5366
|
return;
|
|
5361
5367
|
}
|
|
5362
|
-
const
|
|
5363
|
-
|
|
5364
|
-
|
|
5368
|
+
const action = await select3({
|
|
5369
|
+
message: "Apply regenerated setup?",
|
|
5370
|
+
choices: [
|
|
5371
|
+
{ name: "Accept and apply", value: "accept" },
|
|
5372
|
+
{ name: "Decline", value: "decline" }
|
|
5373
|
+
]
|
|
5374
|
+
});
|
|
5375
|
+
cleanupStaging();
|
|
5376
|
+
if (action === "decline") {
|
|
5377
|
+
console.log(chalk8.dim("Regeneration cancelled. No files were modified."));
|
|
5365
5378
|
return;
|
|
5366
5379
|
}
|
|
5367
|
-
const writeSpinner = ora4("
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5380
|
+
const writeSpinner = ora4("Writing config files...").start();
|
|
5381
|
+
try {
|
|
5382
|
+
const result = writeSetup(generatedSetup);
|
|
5383
|
+
writeSpinner.succeed("Config files written");
|
|
5384
|
+
for (const file of result.written) {
|
|
5385
|
+
console.log(` ${chalk8.green("\u2713")} ${file}`);
|
|
5386
|
+
}
|
|
5387
|
+
if (result.deleted.length > 0) {
|
|
5388
|
+
for (const file of result.deleted) {
|
|
5389
|
+
console.log(` ${chalk8.red("\u2717")} ${file}`);
|
|
5390
|
+
}
|
|
5391
|
+
}
|
|
5392
|
+
if (result.backupDir) {
|
|
5393
|
+
console.log(chalk8.dim(`
|
|
5394
|
+
Backups saved to ${result.backupDir}`));
|
|
5395
|
+
}
|
|
5396
|
+
} catch (err) {
|
|
5397
|
+
writeSpinner.fail("Failed to write files");
|
|
5398
|
+
console.error(chalk8.red(err instanceof Error ? err.message : "Unknown error"));
|
|
5399
|
+
throw new Error("__exit__");
|
|
5372
5400
|
}
|
|
5373
|
-
|
|
5401
|
+
const sha = getCurrentHeadSha();
|
|
5402
|
+
writeState({
|
|
5403
|
+
lastRefreshSha: sha ?? "",
|
|
5404
|
+
lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5405
|
+
targetAgent
|
|
5406
|
+
});
|
|
5407
|
+
const afterScore = computeLocalScore(process.cwd(), targetAgent);
|
|
5408
|
+
if (afterScore.score < baselineScore.score) {
|
|
5409
|
+
console.log("");
|
|
5410
|
+
console.log(chalk8.yellow(` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`));
|
|
5411
|
+
try {
|
|
5412
|
+
const { restored, removed } = undoSetup();
|
|
5413
|
+
if (restored.length > 0 || removed.length > 0) {
|
|
5414
|
+
console.log(chalk8.dim(` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`));
|
|
5415
|
+
}
|
|
5416
|
+
} catch {
|
|
5417
|
+
}
|
|
5418
|
+
console.log(chalk8.dim(" Run ") + chalk8.hex("#83D1EB")("caliber onboard --force") + chalk8.dim(" to override.\n"));
|
|
5419
|
+
return;
|
|
5420
|
+
}
|
|
5421
|
+
displayScoreDelta(baselineScore, afterScore);
|
|
5422
|
+
console.log(chalk8.bold.green(" Regeneration complete!"));
|
|
5423
|
+
console.log(chalk8.dim(" Run ") + chalk8.hex("#83D1EB")("caliber undo") + chalk8.dim(" to revert changes.\n"));
|
|
5374
5424
|
}
|
|
5375
5425
|
|
|
5376
5426
|
// src/commands/recommend.ts
|
|
@@ -5380,13 +5430,13 @@ import { mkdirSync, readFileSync as readFileSync7, readdirSync as readdirSync5,
|
|
|
5380
5430
|
import { join as join8, dirname as dirname2 } from "path";
|
|
5381
5431
|
|
|
5382
5432
|
// src/scanner/index.ts
|
|
5383
|
-
import
|
|
5433
|
+
import fs22 from "fs";
|
|
5384
5434
|
import path17 from "path";
|
|
5385
5435
|
import crypto2 from "crypto";
|
|
5386
5436
|
function scanLocalState(dir) {
|
|
5387
5437
|
const items = [];
|
|
5388
5438
|
const claudeMdPath = path17.join(dir, "CLAUDE.md");
|
|
5389
|
-
if (
|
|
5439
|
+
if (fs22.existsSync(claudeMdPath)) {
|
|
5390
5440
|
items.push({
|
|
5391
5441
|
type: "rule",
|
|
5392
5442
|
platform: "claude",
|
|
@@ -5396,8 +5446,8 @@ function scanLocalState(dir) {
|
|
|
5396
5446
|
});
|
|
5397
5447
|
}
|
|
5398
5448
|
const skillsDir = path17.join(dir, ".claude", "skills");
|
|
5399
|
-
if (
|
|
5400
|
-
for (const file of
|
|
5449
|
+
if (fs22.existsSync(skillsDir)) {
|
|
5450
|
+
for (const file of fs22.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
5401
5451
|
const filePath = path17.join(skillsDir, file);
|
|
5402
5452
|
items.push({
|
|
5403
5453
|
type: "skill",
|
|
@@ -5409,9 +5459,9 @@ function scanLocalState(dir) {
|
|
|
5409
5459
|
}
|
|
5410
5460
|
}
|
|
5411
5461
|
const mcpJsonPath = path17.join(dir, ".mcp.json");
|
|
5412
|
-
if (
|
|
5462
|
+
if (fs22.existsSync(mcpJsonPath)) {
|
|
5413
5463
|
try {
|
|
5414
|
-
const mcpJson = JSON.parse(
|
|
5464
|
+
const mcpJson = JSON.parse(fs22.readFileSync(mcpJsonPath, "utf-8"));
|
|
5415
5465
|
if (mcpJson.mcpServers) {
|
|
5416
5466
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
5417
5467
|
items.push({
|
|
@@ -5427,7 +5477,7 @@ function scanLocalState(dir) {
|
|
|
5427
5477
|
}
|
|
5428
5478
|
}
|
|
5429
5479
|
const agentsMdPath = path17.join(dir, "AGENTS.md");
|
|
5430
|
-
if (
|
|
5480
|
+
if (fs22.existsSync(agentsMdPath)) {
|
|
5431
5481
|
items.push({
|
|
5432
5482
|
type: "rule",
|
|
5433
5483
|
platform: "codex",
|
|
@@ -5437,11 +5487,11 @@ function scanLocalState(dir) {
|
|
|
5437
5487
|
});
|
|
5438
5488
|
}
|
|
5439
5489
|
const codexSkillsDir = path17.join(dir, ".agents", "skills");
|
|
5440
|
-
if (
|
|
5490
|
+
if (fs22.existsSync(codexSkillsDir)) {
|
|
5441
5491
|
try {
|
|
5442
|
-
for (const name of
|
|
5492
|
+
for (const name of fs22.readdirSync(codexSkillsDir)) {
|
|
5443
5493
|
const skillFile = path17.join(codexSkillsDir, name, "SKILL.md");
|
|
5444
|
-
if (
|
|
5494
|
+
if (fs22.existsSync(skillFile)) {
|
|
5445
5495
|
items.push({
|
|
5446
5496
|
type: "skill",
|
|
5447
5497
|
platform: "codex",
|
|
@@ -5455,7 +5505,7 @@ function scanLocalState(dir) {
|
|
|
5455
5505
|
}
|
|
5456
5506
|
}
|
|
5457
5507
|
const cursorrulesPath = path17.join(dir, ".cursorrules");
|
|
5458
|
-
if (
|
|
5508
|
+
if (fs22.existsSync(cursorrulesPath)) {
|
|
5459
5509
|
items.push({
|
|
5460
5510
|
type: "rule",
|
|
5461
5511
|
platform: "cursor",
|
|
@@ -5465,8 +5515,8 @@ function scanLocalState(dir) {
|
|
|
5465
5515
|
});
|
|
5466
5516
|
}
|
|
5467
5517
|
const cursorRulesDir = path17.join(dir, ".cursor", "rules");
|
|
5468
|
-
if (
|
|
5469
|
-
for (const file of
|
|
5518
|
+
if (fs22.existsSync(cursorRulesDir)) {
|
|
5519
|
+
for (const file of fs22.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
5470
5520
|
const filePath = path17.join(cursorRulesDir, file);
|
|
5471
5521
|
items.push({
|
|
5472
5522
|
type: "rule",
|
|
@@ -5478,11 +5528,11 @@ function scanLocalState(dir) {
|
|
|
5478
5528
|
}
|
|
5479
5529
|
}
|
|
5480
5530
|
const cursorSkillsDir = path17.join(dir, ".cursor", "skills");
|
|
5481
|
-
if (
|
|
5531
|
+
if (fs22.existsSync(cursorSkillsDir)) {
|
|
5482
5532
|
try {
|
|
5483
|
-
for (const name of
|
|
5533
|
+
for (const name of fs22.readdirSync(cursorSkillsDir)) {
|
|
5484
5534
|
const skillFile = path17.join(cursorSkillsDir, name, "SKILL.md");
|
|
5485
|
-
if (
|
|
5535
|
+
if (fs22.existsSync(skillFile)) {
|
|
5486
5536
|
items.push({
|
|
5487
5537
|
type: "skill",
|
|
5488
5538
|
platform: "cursor",
|
|
@@ -5496,9 +5546,9 @@ function scanLocalState(dir) {
|
|
|
5496
5546
|
}
|
|
5497
5547
|
}
|
|
5498
5548
|
const cursorMcpPath = path17.join(dir, ".cursor", "mcp.json");
|
|
5499
|
-
if (
|
|
5549
|
+
if (fs22.existsSync(cursorMcpPath)) {
|
|
5500
5550
|
try {
|
|
5501
|
-
const mcpJson = JSON.parse(
|
|
5551
|
+
const mcpJson = JSON.parse(fs22.readFileSync(cursorMcpPath, "utf-8"));
|
|
5502
5552
|
if (mcpJson.mcpServers) {
|
|
5503
5553
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
5504
5554
|
items.push({
|
|
@@ -5516,7 +5566,7 @@ function scanLocalState(dir) {
|
|
|
5516
5566
|
return items;
|
|
5517
5567
|
}
|
|
5518
5568
|
function hashFile(filePath) {
|
|
5519
|
-
const text =
|
|
5569
|
+
const text = fs22.readFileSync(filePath, "utf-8");
|
|
5520
5570
|
return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
5521
5571
|
}
|
|
5522
5572
|
function hashJson(obj) {
|
|
@@ -6091,13 +6141,13 @@ async function scoreCommand(options) {
|
|
|
6091
6141
|
} else if (result.score < 70) {
|
|
6092
6142
|
console.log(chalk10.gray(" Run ") + chalk10.hex("#83D1EB")("caliber onboard") + chalk10.gray(" to improve your setup."));
|
|
6093
6143
|
} else {
|
|
6094
|
-
console.log(chalk10.green(" Looking good!") + chalk10.gray(" Run ") + chalk10.hex("#83D1EB")("caliber
|
|
6144
|
+
console.log(chalk10.green(" Looking good!") + chalk10.gray(" Run ") + chalk10.hex("#83D1EB")("caliber regenerate") + chalk10.gray(" to rebuild from scratch."));
|
|
6095
6145
|
}
|
|
6096
6146
|
console.log("");
|
|
6097
6147
|
}
|
|
6098
6148
|
|
|
6099
6149
|
// src/commands/refresh.ts
|
|
6100
|
-
import
|
|
6150
|
+
import fs24 from "fs";
|
|
6101
6151
|
import path19 from "path";
|
|
6102
6152
|
import chalk11 from "chalk";
|
|
6103
6153
|
import ora6 from "ora";
|
|
@@ -6175,37 +6225,37 @@ function collectDiff(lastSha) {
|
|
|
6175
6225
|
}
|
|
6176
6226
|
|
|
6177
6227
|
// src/writers/refresh.ts
|
|
6178
|
-
import
|
|
6228
|
+
import fs23 from "fs";
|
|
6179
6229
|
import path18 from "path";
|
|
6180
6230
|
function writeRefreshDocs(docs) {
|
|
6181
6231
|
const written = [];
|
|
6182
6232
|
if (docs.claudeMd) {
|
|
6183
|
-
|
|
6233
|
+
fs23.writeFileSync("CLAUDE.md", docs.claudeMd);
|
|
6184
6234
|
written.push("CLAUDE.md");
|
|
6185
6235
|
}
|
|
6186
6236
|
if (docs.readmeMd) {
|
|
6187
|
-
|
|
6237
|
+
fs23.writeFileSync("README.md", docs.readmeMd);
|
|
6188
6238
|
written.push("README.md");
|
|
6189
6239
|
}
|
|
6190
6240
|
if (docs.cursorrules) {
|
|
6191
|
-
|
|
6241
|
+
fs23.writeFileSync(".cursorrules", docs.cursorrules);
|
|
6192
6242
|
written.push(".cursorrules");
|
|
6193
6243
|
}
|
|
6194
6244
|
if (docs.cursorRules) {
|
|
6195
6245
|
const rulesDir = path18.join(".cursor", "rules");
|
|
6196
|
-
if (!
|
|
6246
|
+
if (!fs23.existsSync(rulesDir)) fs23.mkdirSync(rulesDir, { recursive: true });
|
|
6197
6247
|
for (const rule of docs.cursorRules) {
|
|
6198
6248
|
const filePath = path18.join(rulesDir, rule.filename);
|
|
6199
|
-
|
|
6249
|
+
fs23.writeFileSync(filePath, rule.content);
|
|
6200
6250
|
written.push(filePath);
|
|
6201
6251
|
}
|
|
6202
6252
|
}
|
|
6203
6253
|
if (docs.claudeSkills) {
|
|
6204
6254
|
const skillsDir = path18.join(".claude", "skills");
|
|
6205
|
-
if (!
|
|
6255
|
+
if (!fs23.existsSync(skillsDir)) fs23.mkdirSync(skillsDir, { recursive: true });
|
|
6206
6256
|
for (const skill of docs.claudeSkills) {
|
|
6207
6257
|
const filePath = path18.join(skillsDir, skill.filename);
|
|
6208
|
-
|
|
6258
|
+
fs23.writeFileSync(filePath, skill.content);
|
|
6209
6259
|
written.push(filePath);
|
|
6210
6260
|
}
|
|
6211
6261
|
}
|
|
@@ -6282,11 +6332,11 @@ function log(quiet, ...args) {
|
|
|
6282
6332
|
function discoverGitRepos(parentDir) {
|
|
6283
6333
|
const repos = [];
|
|
6284
6334
|
try {
|
|
6285
|
-
const entries =
|
|
6335
|
+
const entries = fs24.readdirSync(parentDir, { withFileTypes: true });
|
|
6286
6336
|
for (const entry of entries) {
|
|
6287
6337
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
6288
6338
|
const childPath = path19.join(parentDir, entry.name);
|
|
6289
|
-
if (
|
|
6339
|
+
if (fs24.existsSync(path19.join(childPath, ".git"))) {
|
|
6290
6340
|
repos.push(childPath);
|
|
6291
6341
|
}
|
|
6292
6342
|
}
|
|
@@ -6633,7 +6683,7 @@ function readStdin() {
|
|
|
6633
6683
|
|
|
6634
6684
|
// src/learner/storage.ts
|
|
6635
6685
|
init_constants();
|
|
6636
|
-
import
|
|
6686
|
+
import fs25 from "fs";
|
|
6637
6687
|
import path20 from "path";
|
|
6638
6688
|
var MAX_RESPONSE_LENGTH = 2e3;
|
|
6639
6689
|
var DEFAULT_STATE = {
|
|
@@ -6642,8 +6692,8 @@ var DEFAULT_STATE = {
|
|
|
6642
6692
|
lastAnalysisTimestamp: null
|
|
6643
6693
|
};
|
|
6644
6694
|
function ensureLearningDir() {
|
|
6645
|
-
if (!
|
|
6646
|
-
|
|
6695
|
+
if (!fs25.existsSync(LEARNING_DIR)) {
|
|
6696
|
+
fs25.mkdirSync(LEARNING_DIR, { recursive: true });
|
|
6647
6697
|
}
|
|
6648
6698
|
}
|
|
6649
6699
|
function sessionFilePath() {
|
|
@@ -6661,49 +6711,49 @@ function appendEvent(event) {
|
|
|
6661
6711
|
ensureLearningDir();
|
|
6662
6712
|
const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
|
|
6663
6713
|
const filePath = sessionFilePath();
|
|
6664
|
-
|
|
6714
|
+
fs25.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
|
|
6665
6715
|
const count = getEventCount();
|
|
6666
6716
|
if (count > LEARNING_MAX_EVENTS) {
|
|
6667
|
-
const lines =
|
|
6717
|
+
const lines = fs25.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
6668
6718
|
const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
|
|
6669
|
-
|
|
6719
|
+
fs25.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
6670
6720
|
}
|
|
6671
6721
|
}
|
|
6672
6722
|
function readAllEvents() {
|
|
6673
6723
|
const filePath = sessionFilePath();
|
|
6674
|
-
if (!
|
|
6675
|
-
const lines =
|
|
6724
|
+
if (!fs25.existsSync(filePath)) return [];
|
|
6725
|
+
const lines = fs25.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
6676
6726
|
return lines.map((line) => JSON.parse(line));
|
|
6677
6727
|
}
|
|
6678
6728
|
function getEventCount() {
|
|
6679
6729
|
const filePath = sessionFilePath();
|
|
6680
|
-
if (!
|
|
6681
|
-
const content =
|
|
6730
|
+
if (!fs25.existsSync(filePath)) return 0;
|
|
6731
|
+
const content = fs25.readFileSync(filePath, "utf-8");
|
|
6682
6732
|
return content.split("\n").filter(Boolean).length;
|
|
6683
6733
|
}
|
|
6684
6734
|
function clearSession() {
|
|
6685
6735
|
const filePath = sessionFilePath();
|
|
6686
|
-
if (
|
|
6736
|
+
if (fs25.existsSync(filePath)) fs25.unlinkSync(filePath);
|
|
6687
6737
|
}
|
|
6688
6738
|
function readState2() {
|
|
6689
6739
|
const filePath = stateFilePath();
|
|
6690
|
-
if (!
|
|
6740
|
+
if (!fs25.existsSync(filePath)) return { ...DEFAULT_STATE };
|
|
6691
6741
|
try {
|
|
6692
|
-
return JSON.parse(
|
|
6742
|
+
return JSON.parse(fs25.readFileSync(filePath, "utf-8"));
|
|
6693
6743
|
} catch {
|
|
6694
6744
|
return { ...DEFAULT_STATE };
|
|
6695
6745
|
}
|
|
6696
6746
|
}
|
|
6697
6747
|
function writeState2(state) {
|
|
6698
6748
|
ensureLearningDir();
|
|
6699
|
-
|
|
6749
|
+
fs25.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
|
|
6700
6750
|
}
|
|
6701
6751
|
function resetState() {
|
|
6702
6752
|
writeState2({ ...DEFAULT_STATE });
|
|
6703
6753
|
}
|
|
6704
6754
|
|
|
6705
6755
|
// src/learner/writer.ts
|
|
6706
|
-
import
|
|
6756
|
+
import fs26 from "fs";
|
|
6707
6757
|
import path21 from "path";
|
|
6708
6758
|
var LEARNED_START = "<!-- caliber:learned -->";
|
|
6709
6759
|
var LEARNED_END = "<!-- /caliber:learned -->";
|
|
@@ -6724,8 +6774,8 @@ function writeLearnedContent(update) {
|
|
|
6724
6774
|
function writeLearnedSection(content) {
|
|
6725
6775
|
const claudeMdPath = "CLAUDE.md";
|
|
6726
6776
|
let existing = "";
|
|
6727
|
-
if (
|
|
6728
|
-
existing =
|
|
6777
|
+
if (fs26.existsSync(claudeMdPath)) {
|
|
6778
|
+
existing = fs26.readFileSync(claudeMdPath, "utf-8");
|
|
6729
6779
|
}
|
|
6730
6780
|
const section = `${LEARNED_START}
|
|
6731
6781
|
${content}
|
|
@@ -6739,15 +6789,15 @@ ${LEARNED_END}`;
|
|
|
6739
6789
|
const separator = existing.endsWith("\n") || existing === "" ? "" : "\n";
|
|
6740
6790
|
updated = existing + separator + "\n" + section + "\n";
|
|
6741
6791
|
}
|
|
6742
|
-
|
|
6792
|
+
fs26.writeFileSync(claudeMdPath, updated);
|
|
6743
6793
|
}
|
|
6744
6794
|
function writeLearnedSkill(skill) {
|
|
6745
6795
|
const skillDir = path21.join(".claude", "skills", skill.name);
|
|
6746
|
-
if (!
|
|
6796
|
+
if (!fs26.existsSync(skillDir)) fs26.mkdirSync(skillDir, { recursive: true });
|
|
6747
6797
|
const skillPath = path21.join(skillDir, "SKILL.md");
|
|
6748
|
-
if (!skill.isNew &&
|
|
6749
|
-
const existing =
|
|
6750
|
-
|
|
6798
|
+
if (!skill.isNew && fs26.existsSync(skillPath)) {
|
|
6799
|
+
const existing = fs26.readFileSync(skillPath, "utf-8");
|
|
6800
|
+
fs26.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
|
|
6751
6801
|
} else {
|
|
6752
6802
|
const frontmatter = [
|
|
6753
6803
|
"---",
|
|
@@ -6756,14 +6806,14 @@ function writeLearnedSkill(skill) {
|
|
|
6756
6806
|
"---",
|
|
6757
6807
|
""
|
|
6758
6808
|
].join("\n");
|
|
6759
|
-
|
|
6809
|
+
fs26.writeFileSync(skillPath, frontmatter + skill.content);
|
|
6760
6810
|
}
|
|
6761
6811
|
return skillPath;
|
|
6762
6812
|
}
|
|
6763
6813
|
function readLearnedSection() {
|
|
6764
6814
|
const claudeMdPath = "CLAUDE.md";
|
|
6765
|
-
if (!
|
|
6766
|
-
const content =
|
|
6815
|
+
if (!fs26.existsSync(claudeMdPath)) return null;
|
|
6816
|
+
const content = fs26.readFileSync(claudeMdPath, "utf-8");
|
|
6767
6817
|
const startIdx = content.indexOf(LEARNED_START);
|
|
6768
6818
|
const endIdx = content.indexOf(LEARNED_END);
|
|
6769
6819
|
if (startIdx === -1 || endIdx === -1) return null;
|
|
@@ -6949,7 +6999,7 @@ Learned items in CLAUDE.md: ${chalk14.cyan(String(lineCount))}`);
|
|
|
6949
6999
|
// src/cli.ts
|
|
6950
7000
|
var __dirname = path22.dirname(fileURLToPath(import.meta.url));
|
|
6951
7001
|
var pkg = JSON.parse(
|
|
6952
|
-
|
|
7002
|
+
fs27.readFileSync(path22.resolve(__dirname, "..", "package.json"), "utf-8")
|
|
6953
7003
|
);
|
|
6954
7004
|
var program = new Command();
|
|
6955
7005
|
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
|
|
@@ -6968,7 +7018,7 @@ function parseAgentOption(value) {
|
|
|
6968
7018
|
program.command("onboard").alias("init").description("Onboard your project for AI-assisted development").option("--agent <type>", "Target agents (comma-separated): claude, cursor, codex", parseAgentOption).option("--dry-run", "Preview changes without writing files").option("--force", "Overwrite existing setup without prompting").action(initCommand);
|
|
6969
7019
|
program.command("undo").description("Revert all config changes made by Caliber").action(undoCommand);
|
|
6970
7020
|
program.command("status").description("Show current Caliber setup status").option("--json", "Output as JSON").action(statusCommand);
|
|
6971
|
-
program.command("regenerate").alias("regen").alias("re").
|
|
7021
|
+
program.command("regenerate").alias("regen").alias("re").description("Re-analyze project and regenerate setup").option("--dry-run", "Preview changes without writing files").action(regenerateCommand);
|
|
6972
7022
|
program.command("config").description("Configure LLM provider, API key, and model").action(configCommand);
|
|
6973
7023
|
program.command("recommend").description("Discover and install skill recommendations").option("--generate", "Force fresh recommendation search").action(recommendCommand);
|
|
6974
7024
|
program.command("score").description("Score your current agent config setup (deterministic, no network)").option("--json", "Output as JSON").option("--quiet", "One-line output for scripts/hooks").option("--agent <type>", "Target agents (comma-separated): claude, cursor, codex", parseAgentOption).action(scoreCommand);
|
|
@@ -6982,22 +7032,22 @@ learn.command("remove").description("Remove learning hooks from .claude/settings
|
|
|
6982
7032
|
learn.command("status").description("Show learning system status").action(learnStatusCommand);
|
|
6983
7033
|
|
|
6984
7034
|
// src/utils/version-check.ts
|
|
6985
|
-
import
|
|
7035
|
+
import fs28 from "fs";
|
|
6986
7036
|
import path23 from "path";
|
|
6987
7037
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6988
7038
|
import { execSync as execSync9 } from "child_process";
|
|
6989
7039
|
import chalk15 from "chalk";
|
|
6990
7040
|
import ora7 from "ora";
|
|
6991
|
-
import
|
|
7041
|
+
import confirm from "@inquirer/confirm";
|
|
6992
7042
|
var __dirname_vc = path23.dirname(fileURLToPath2(import.meta.url));
|
|
6993
7043
|
var pkg2 = JSON.parse(
|
|
6994
|
-
|
|
7044
|
+
fs28.readFileSync(path23.resolve(__dirname_vc, "..", "package.json"), "utf-8")
|
|
6995
7045
|
);
|
|
6996
7046
|
function getInstalledVersion() {
|
|
6997
7047
|
try {
|
|
6998
7048
|
const globalRoot = execSync9("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
6999
7049
|
const pkgPath = path23.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
7000
|
-
return JSON.parse(
|
|
7050
|
+
return JSON.parse(fs28.readFileSync(pkgPath, "utf-8")).version;
|
|
7001
7051
|
} catch {
|
|
7002
7052
|
return null;
|
|
7003
7053
|
}
|
|
@@ -7033,7 +7083,7 @@ Run ${chalk15.bold("npm install -g @rely-ai/caliber")} to upgrade.
|
|
|
7033
7083
|
chalk15.yellow(`
|
|
7034
7084
|
Update available: ${current} -> ${latest}`)
|
|
7035
7085
|
);
|
|
7036
|
-
const shouldUpdate = await
|
|
7086
|
+
const shouldUpdate = await confirm({ message: "Would you like to update now? (Y/n)", default: true });
|
|
7037
7087
|
if (!shouldUpdate) {
|
|
7038
7088
|
console.log();
|
|
7039
7089
|
return;
|
package/package.json
CHANGED