@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.
Files changed (2) hide show
  1. package/dist/bin.js +281 -231
  2. 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 fs26 from "fs";
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 fs19 from "fs";
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/lib/hooks.ts
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 (!fs14.existsSync(SETTINGS_PATH)) return {};
2282
+ if (!fs15.existsSync(SETTINGS_PATH)) return {};
2216
2283
  try {
2217
- return JSON.parse(fs14.readFileSync(SETTINGS_PATH, "utf-8"));
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 (!fs14.existsSync(dir)) fs14.mkdirSync(dir, { recursive: true });
2225
- fs14.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
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 || !fs14.existsSync(hookPath)) return false;
2295
- const content = fs14.readFileSync(hookPath, "utf-8");
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 (!fs14.existsSync(hooksDir)) fs14.mkdirSync(hooksDir, { recursive: true });
2372
+ if (!fs15.existsSync(hooksDir)) fs15.mkdirSync(hooksDir, { recursive: true });
2306
2373
  let content = "";
2307
- if (fs14.existsSync(hookPath)) {
2308
- content = fs14.readFileSync(hookPath, "utf-8");
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
- fs14.writeFileSync(hookPath, content);
2315
- fs14.chmodSync(hookPath, 493);
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 || !fs14.existsSync(hookPath)) {
2387
+ if (!hookPath || !fs15.existsSync(hookPath)) {
2321
2388
  return { removed: false, notFound: true };
2322
2389
  }
2323
- let content = fs14.readFileSync(hookPath, "utf-8");
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
- fs14.unlinkSync(hookPath);
2397
+ fs15.unlinkSync(hookPath);
2331
2398
  } else {
2332
- fs14.writeFileSync(hookPath, content);
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 fs15 from "fs";
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 (!fs15.existsSync(SETTINGS_PATH2)) return {};
2426
+ if (!fs16.existsSync(SETTINGS_PATH2)) return {};
2360
2427
  try {
2361
- return JSON.parse(fs15.readFileSync(SETTINGS_PATH2, "utf-8"));
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 (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
2369
- fs15.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
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 fs16 from "fs";
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 (!fs16.existsSync(STATE_FILE)) return null;
2441
- const raw = JSON.parse(fs16.readFileSync(STATE_FILE, "utf-8"));
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 (!fs16.existsSync(CALIBER_DIR)) {
2450
- fs16.mkdirSync(CALIBER_DIR, { recursive: true });
2516
+ if (!fs17.existsSync(CALIBER_DIR)) {
2517
+ fs17.mkdirSync(CALIBER_DIR, { recursive: true });
2451
2518
  }
2452
- fs16.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
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 fs17 from "fs";
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 (!fs17.existsSync(DISMISSED_FILE)) return [];
3679
- return JSON.parse(fs17.readFileSync(DISMISSED_FILE, "utf-8"));
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 (!fs17.existsSync(CALIBER_DIR)) {
3686
- fs17.mkdirSync(CALIBER_DIR, { recursive: true });
3752
+ if (!fs18.existsSync(CALIBER_DIR)) {
3753
+ fs18.mkdirSync(CALIBER_DIR, { recursive: true });
3687
3754
  }
3688
- fs17.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
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(" \u256D\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\u256E"));
3803
- console.log(chalk3.gray(" \u2502") + " " + chalk3.gray("\u2502"));
3804
- console.log(
3805
- chalk3.gray(" \u2502") + " Agent Config Score" + gc(` ${String(result.score).padStart(3)} / ${result.maxScore}`) + " Grade " + gc(result.grade) + " " + chalk3.gray("\u2502")
3806
- );
3807
- console.log(chalk3.gray(" \u2502") + ` ${progressBar(result.score, result.maxScore)} ` + chalk3.gray("\u2502"));
3808
- console.log(chalk3.gray(" \u2502") + chalk3.dim(` Target: ${agentLabel}`) + " ".repeat(Math.max(1, 40 - agentLabel.length)) + chalk3.gray("\u2502"));
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(" \u256D" + "\u2500".repeat(BOX_INNER) + "\u256E"));
3865
- console.log(chalk3.gray(" \u2502") + " ".repeat(BOX_INNER) + chalk3.gray("\u2502"));
3866
- console.log(chalk3.gray(" \u2502") + scoreLineFormatted + " ".repeat(trailingPad) + chalk3.gray("\u2502"));
3867
- console.log(chalk3.gray(" \u2502") + barLine + chalk3.gray("\u2502"));
3868
- console.log(chalk3.gray(" \u2502") + " ".repeat(BOX_INNER) + chalk3.gray("\u2502"));
3869
- console.log(chalk3.gray(" \u2570" + "\u2500".repeat(BOX_INNER) + "\u256F"));
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 fs18 from "fs";
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 (!fs18.existsSync(cursorDir)) fs18.mkdirSync(cursorDir, { recursive: true });
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 (fs18.existsSync(filePath)) {
4254
- const parsed = JSON.parse(fs18.readFileSync(filePath, "utf-8"));
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
- fs18.writeFileSync(filePath, JSON.stringify({ mcpServers: merged }, null, 2) + "\n");
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 = fs19.readFileSync(file.proposedPath, "utf-8");
4876
- const current = file.currentPath ? fs19.readFileSync(file.currentPath, "utf-8") : "";
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 = fs19.existsSync("CLAUDE.md") ? chalk5.yellow("~") : chalk5.green("+");
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 = fs19.existsSync(skillPath) ? chalk5.yellow("~") : chalk5.green("+");
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 = fs19.existsSync("AGENTS.md") ? chalk5.yellow("~") : chalk5.green("+");
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 = fs19.existsSync(skillPath) ? chalk5.yellow("~") : chalk5.green("+");
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 = fs19.existsSync(".cursorrules") ? chalk5.yellow("~") : chalk5.green("+");
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 = fs19.existsSync(skillPath) ? chalk5.yellow("~") : chalk5.green("+");
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 = fs19.existsSync(rulePath) ? chalk5.yellow("~") : chalk5.green("+");
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 && !fs19.existsSync("AGENTS.md")) {
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 (fs19.existsSync(settingsPath)) {
5163
- settings = JSON.parse(fs19.readFileSync(settingsPath, "utf-8"));
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 (!fs19.existsSync(".claude")) fs19.mkdirSync(".claude", { recursive: true });
5178
- fs19.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
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 fs20 from "fs";
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 = fs20.existsSync(entry.path);
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 confirm from "@inquirer/confirm";
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(" (e.g. choose Cursor) or set ANTHROPIC_API_KEY."));
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 spinner = ora4("Re-analyzing project...").start();
5307
+ const targetAgent = readState()?.targetAgent ?? ["claude", "cursor"];
5308
+ const spinner = ora4("Analyzing project...").start();
5319
5309
  const fingerprint = collectFingerprint(process.cwd());
5320
- spinner.succeed("Project re-analyzed");
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 targetAgent = readState()?.targetAgent ?? ["claude", "cursor"];
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 = result2.setup;
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("\n[Dry run] Would write:"));
5359
- console.log(JSON.stringify(generatedSetup, null, 2));
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 shouldApply = await confirm({ message: "Apply regenerated setup?", default: true });
5363
- if (!shouldApply) {
5364
- console.log(chalk8.dim("Regeneration cancelled."));
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("Updating config files...").start();
5368
- const result = writeSetup(generatedSetup);
5369
- writeSpinner.succeed("Config files updated");
5370
- for (const file of result.written) {
5371
- console.log(` ${chalk8.green("\u2713")} ${file}`);
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
- console.log("");
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 fs21 from "fs";
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 (fs21.existsSync(claudeMdPath)) {
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 (fs21.existsSync(skillsDir)) {
5400
- for (const file of fs21.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
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 (fs21.existsSync(mcpJsonPath)) {
5462
+ if (fs22.existsSync(mcpJsonPath)) {
5413
5463
  try {
5414
- const mcpJson = JSON.parse(fs21.readFileSync(mcpJsonPath, "utf-8"));
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 (fs21.existsSync(agentsMdPath)) {
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 (fs21.existsSync(codexSkillsDir)) {
5490
+ if (fs22.existsSync(codexSkillsDir)) {
5441
5491
  try {
5442
- for (const name of fs21.readdirSync(codexSkillsDir)) {
5492
+ for (const name of fs22.readdirSync(codexSkillsDir)) {
5443
5493
  const skillFile = path17.join(codexSkillsDir, name, "SKILL.md");
5444
- if (fs21.existsSync(skillFile)) {
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 (fs21.existsSync(cursorrulesPath)) {
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 (fs21.existsSync(cursorRulesDir)) {
5469
- for (const file of fs21.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
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 (fs21.existsSync(cursorSkillsDir)) {
5531
+ if (fs22.existsSync(cursorSkillsDir)) {
5482
5532
  try {
5483
- for (const name of fs21.readdirSync(cursorSkillsDir)) {
5533
+ for (const name of fs22.readdirSync(cursorSkillsDir)) {
5484
5534
  const skillFile = path17.join(cursorSkillsDir, name, "SKILL.md");
5485
- if (fs21.existsSync(skillFile)) {
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 (fs21.existsSync(cursorMcpPath)) {
5549
+ if (fs22.existsSync(cursorMcpPath)) {
5500
5550
  try {
5501
- const mcpJson = JSON.parse(fs21.readFileSync(cursorMcpPath, "utf-8"));
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 = fs21.readFileSync(filePath, "utf-8");
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 update") + chalk10.gray(" to keep it fresh."));
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 fs23 from "fs";
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 fs22 from "fs";
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
- fs22.writeFileSync("CLAUDE.md", docs.claudeMd);
6233
+ fs23.writeFileSync("CLAUDE.md", docs.claudeMd);
6184
6234
  written.push("CLAUDE.md");
6185
6235
  }
6186
6236
  if (docs.readmeMd) {
6187
- fs22.writeFileSync("README.md", docs.readmeMd);
6237
+ fs23.writeFileSync("README.md", docs.readmeMd);
6188
6238
  written.push("README.md");
6189
6239
  }
6190
6240
  if (docs.cursorrules) {
6191
- fs22.writeFileSync(".cursorrules", docs.cursorrules);
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 (!fs22.existsSync(rulesDir)) fs22.mkdirSync(rulesDir, { recursive: true });
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
- fs22.writeFileSync(filePath, rule.content);
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 (!fs22.existsSync(skillsDir)) fs22.mkdirSync(skillsDir, { recursive: true });
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
- fs22.writeFileSync(filePath, skill.content);
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 = fs23.readdirSync(parentDir, { withFileTypes: true });
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 (fs23.existsSync(path19.join(childPath, ".git"))) {
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 fs24 from "fs";
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 (!fs24.existsSync(LEARNING_DIR)) {
6646
- fs24.mkdirSync(LEARNING_DIR, { recursive: true });
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
- fs24.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
6714
+ fs25.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
6665
6715
  const count = getEventCount();
6666
6716
  if (count > LEARNING_MAX_EVENTS) {
6667
- const lines = fs24.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
6717
+ const lines = fs25.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
6668
6718
  const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
6669
- fs24.writeFileSync(filePath, kept.join("\n") + "\n");
6719
+ fs25.writeFileSync(filePath, kept.join("\n") + "\n");
6670
6720
  }
6671
6721
  }
6672
6722
  function readAllEvents() {
6673
6723
  const filePath = sessionFilePath();
6674
- if (!fs24.existsSync(filePath)) return [];
6675
- const lines = fs24.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
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 (!fs24.existsSync(filePath)) return 0;
6681
- const content = fs24.readFileSync(filePath, "utf-8");
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 (fs24.existsSync(filePath)) fs24.unlinkSync(filePath);
6736
+ if (fs25.existsSync(filePath)) fs25.unlinkSync(filePath);
6687
6737
  }
6688
6738
  function readState2() {
6689
6739
  const filePath = stateFilePath();
6690
- if (!fs24.existsSync(filePath)) return { ...DEFAULT_STATE };
6740
+ if (!fs25.existsSync(filePath)) return { ...DEFAULT_STATE };
6691
6741
  try {
6692
- return JSON.parse(fs24.readFileSync(filePath, "utf-8"));
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
- fs24.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
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 fs25 from "fs";
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 (fs25.existsSync(claudeMdPath)) {
6728
- existing = fs25.readFileSync(claudeMdPath, "utf-8");
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
- fs25.writeFileSync(claudeMdPath, updated);
6792
+ fs26.writeFileSync(claudeMdPath, updated);
6743
6793
  }
6744
6794
  function writeLearnedSkill(skill) {
6745
6795
  const skillDir = path21.join(".claude", "skills", skill.name);
6746
- if (!fs25.existsSync(skillDir)) fs25.mkdirSync(skillDir, { recursive: true });
6796
+ if (!fs26.existsSync(skillDir)) fs26.mkdirSync(skillDir, { recursive: true });
6747
6797
  const skillPath = path21.join(skillDir, "SKILL.md");
6748
- if (!skill.isNew && fs25.existsSync(skillPath)) {
6749
- const existing = fs25.readFileSync(skillPath, "utf-8");
6750
- fs25.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
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
- fs25.writeFileSync(skillPath, frontmatter + skill.content);
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 (!fs25.existsSync(claudeMdPath)) return null;
6766
- const content = fs25.readFileSync(claudeMdPath, "utf-8");
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
- fs26.readFileSync(path22.resolve(__dirname, "..", "package.json"), "utf-8")
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").alias("update").description("Re-analyze project and regenerate setup").option("--dry-run", "Preview changes without writing files").action(regenerateCommand);
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 fs27 from "fs";
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 confirm2 from "@inquirer/confirm";
7041
+ import confirm from "@inquirer/confirm";
6992
7042
  var __dirname_vc = path23.dirname(fileURLToPath2(import.meta.url));
6993
7043
  var pkg2 = JSON.parse(
6994
- fs27.readFileSync(path23.resolve(__dirname_vc, "..", "package.json"), "utf-8")
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(fs27.readFileSync(pkgPath, "utf-8")).version;
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 confirm2({ message: "Would you like to update now? (Y/n)", default: true });
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rely-ai/caliber",
3
- "version": "1.3.2",
3
+ "version": "1.4.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": {