@rely-ai/caliber 1.48.2 → 1.49.1

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 +314 -162
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -184,6 +184,7 @@ var init_config = __esm({
184
184
  // src/lib/resolve-caliber.ts
185
185
  var resolve_caliber_exports = {};
186
186
  __export(resolve_caliber_exports, {
187
+ displayCaliberName: () => displayCaliberName,
187
188
  isCaliberCommand: () => isCaliberCommand,
188
189
  isNpxResolution: () => isNpxResolution,
189
190
  pickExecutable: () => pickExecutable,
@@ -254,6 +255,9 @@ function isNpxResolution() {
254
255
  if (r === "npx --yes @rely-ai/caliber") return true;
255
256
  return NPX_RESOLUTION_RE.test(r);
256
257
  }
258
+ function displayCaliberName() {
259
+ return isNpxResolution() ? "npx @rely-ai/caliber" : "caliber";
260
+ }
257
261
  function resetResolvedCaliber() {
258
262
  _resolved = null;
259
263
  }
@@ -344,7 +348,7 @@ npx @rely-ai/caliber refresh
344
348
  }
345
349
  }
346
350
  function getPreCommitBlock(platform = "claude") {
347
- const bin = resolveCaliber();
351
+ const bin = displayCaliberName();
348
352
  return `${BLOCK_START}
349
353
  ## Before Committing
350
354
 
@@ -369,7 +373,7 @@ ${getCaliberNotFoundFallback(platform, bin)}
369
373
  ${BLOCK_END}`;
370
374
  }
371
375
  function getCursorRuleContent() {
372
- const bin = resolveCaliber();
376
+ const bin = displayCaliberName();
373
377
  return `---
374
378
  description: Run Caliber sync before git commits to keep agent configs up to date
375
379
  alwaysApply: true
@@ -443,7 +447,7 @@ git add ${MANAGED_DOC_PATHS} 2>/dev/null
443
447
  }
444
448
  }
445
449
  function getSyncBlock(platform = "claude") {
446
- const bin = resolveCaliber();
450
+ const bin = displayCaliberName();
447
451
  return `${SYNC_BLOCK_START}
448
452
  ## Context Sync
449
453
 
@@ -467,7 +471,7 @@ function appendManagedBlocks(content, platform = "claude") {
467
471
  );
468
472
  }
469
473
  function getCursorSyncContent() {
470
- const bin = resolveCaliber();
474
+ const bin = displayCaliberName();
471
475
  return `---
472
476
  description: This project uses Caliber for automatic AI agent context sync
473
477
  alwaysApply: true
@@ -574,7 +578,7 @@ description: ${skill.description}
574
578
  return frontmatter + skill.content;
575
579
  }
576
580
  function getFindSkillsContent() {
577
- const bin = resolveCaliber();
581
+ const bin = displayCaliberName();
578
582
  return `# Find Skills
579
583
 
580
584
  Search the public skill registry for community-contributed skills
@@ -627,7 +631,7 @@ User: "I need to write tests for this Python ML pipeline"
627
631
  `;
628
632
  }
629
633
  function getSaveLearningContent() {
630
- const bin = resolveCaliber();
634
+ const bin = displayCaliberName();
631
635
  return `# Save Learning
632
636
 
633
637
  Save a user's instruction or preference as a persistent learning that
@@ -2583,10 +2587,19 @@ import os3 from "os";
2583
2587
  // src/llm/seat-based-errors.ts
2584
2588
  init_resolve_caliber();
2585
2589
  var ERROR_PATTERNS = [
2586
- { pattern: /not logged in|not authenticated|login required|unauthorized/i, message: "Not logged in. Run the login command for your provider to re-authenticate." },
2590
+ {
2591
+ pattern: /not logged in|not authenticated|login required|unauthorized/i,
2592
+ message: "Not logged in. Run the login command for your provider to re-authenticate."
2593
+ },
2587
2594
  { pattern: /rate limit|too many requests|429/i, message: "Rate limit exceeded. Retrying..." },
2588
- { pattern: /usage limit|out of usage|budget.*limit|limit.*reached/i, message: () => `Usage limit reached. Run \`${resolveCaliber()} config\` to switch models (e.g. auto or composer-1.5).` },
2589
- { pattern: /model.*not found|invalid model|model.*unavailable/i, message: () => `The requested model is not available. Run \`${resolveCaliber()} config\` to select a different model.` }
2595
+ {
2596
+ pattern: /usage limit|out of usage|budget.*limit|limit.*reached/i,
2597
+ message: () => `Usage limit reached. Run \`${displayCaliberName()} config\` to switch models (e.g. auto or composer-1.5).`
2598
+ },
2599
+ {
2600
+ pattern: /model.*not found|invalid model|model.*unavailable/i,
2601
+ message: () => `The requested model is not available. Run \`${displayCaliberName()} config\` to select a different model.`
2602
+ }
2590
2603
  ];
2591
2604
  function parseSeatBasedError(stderr, exitCode) {
2592
2605
  if (!stderr && exitCode === 0) return null;
@@ -3038,12 +3051,17 @@ function resolveClaudeBin() {
3038
3051
  _claudeBin = "claude";
3039
3052
  return _claudeBin;
3040
3053
  }
3054
+ var ANTI_RECURSION_ENV_VARS = /* @__PURE__ */ new Set([
3055
+ "CLAUDECODE",
3056
+ "CLAUDE_CODE_SIMPLE",
3057
+ "CLAUDE_CODE_SESSION_ID",
3058
+ "CLAUDE_CODE_ENTRYPOINT",
3059
+ "CLAUDE_CODE_EXECPATH"
3060
+ ]);
3041
3061
  function cleanClaudeEnv() {
3042
3062
  const env = { ...process.env };
3043
- for (const key of Object.keys(env)) {
3044
- if (key === "CLAUDE_CODE_SIMPLE" || key === "CLAUDECODE" || key.startsWith("CLAUDE_CODE_")) {
3045
- delete env[key];
3046
- }
3063
+ for (const key of ANTI_RECURSION_ENV_VARS) {
3064
+ delete env[key];
3047
3065
  }
3048
3066
  return env;
3049
3067
  }
@@ -3505,7 +3523,7 @@ async function handleModelNotAvailable(failedModel, provider, config) {
3505
3523
  if (!process.stdin.isTTY) {
3506
3524
  console.error(
3507
3525
  chalk.red(
3508
- `Model "${failedModel}" is not available. Run \`${resolveCaliber()} config\` to select a different model.`
3526
+ `Model "${failedModel}" is not available. Run \`${displayCaliberName()} config\` to select a different model.`
3509
3527
  )
3510
3528
  );
3511
3529
  return null;
@@ -3531,7 +3549,7 @@ async function handleModelNotAvailable(failedModel, provider, config) {
3531
3549
  if (models.length === 0) {
3532
3550
  console.log(
3533
3551
  chalk.red(
3534
- ` No alternative models found. Run \`${resolveCaliber()} config\` to configure manually.`
3552
+ ` No alternative models found. Run \`${displayCaliberName()} config\` to configure manually.`
3535
3553
  )
3536
3554
  );
3537
3555
  return null;
@@ -3623,7 +3641,7 @@ function getProvider() {
3623
3641
  const config = loadConfig();
3624
3642
  if (!config) {
3625
3643
  throw new Error(
3626
- `No LLM provider configured. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, MINIMAX_API_KEY, or VERTEX_PROJECT_ID; or run \`${resolveCaliber()} config\` and choose Cursor, Claude Code, or OpenCode; or set CALIBER_USE_CURSOR_SEAT=1 / CALIBER_USE_CLAUDE_CLI=1 / CALIBER_USE_OPENCODE=1.`
3644
+ `No LLM provider configured. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, MINIMAX_API_KEY, or VERTEX_PROJECT_ID; or run \`${displayCaliberName()} config\` and choose Cursor, Claude Code, or OpenCode; or set CALIBER_USE_CURSOR_SEAT=1 / CALIBER_USE_CLAUDE_CLI=1 / CALIBER_USE_OPENCODE=1.`
3627
3645
  );
3628
3646
  }
3629
3647
  cachedConfig = config;
@@ -4816,8 +4834,11 @@ var notificationHook = createScriptHook({
4816
4834
  var isNotificationHookInstalled = notificationHook.isInstalled;
4817
4835
  var installNotificationHook = notificationHook.install;
4818
4836
  var removeNotificationHook = notificationHook.remove;
4819
- var PRECOMMIT_START = "# caliber:pre-commit:start";
4820
- var PRECOMMIT_END = "# caliber:pre-commit:end";
4837
+ var HOOK_BLOCK_VERSION = "v2";
4838
+ var PRECOMMIT_START = `# caliber:pre-commit:${HOOK_BLOCK_VERSION}:start`;
4839
+ var PRECOMMIT_END = `# caliber:pre-commit:${HOOK_BLOCK_VERSION}:end`;
4840
+ var PRECOMMIT_ANY_VERSION_START_RE = /^#\s*caliber:pre-commit:(?:[a-zA-Z0-9_.-]+:)?start\s*$/m;
4841
+ var PRECOMMIT_ANY_VERSION_BLOCK_RE = /\n?#\s*caliber:pre-commit:(?:[a-zA-Z0-9_.-]+:)?start[\s\S]*?#\s*caliber:pre-commit:(?:[a-zA-Z0-9_.-]+:)?end\n?/g;
4821
4842
  function tryWindowsDirectNodeInvocation(cmd) {
4822
4843
  if (process.platform !== "win32") return null;
4823
4844
  if (!/\.cmd$/i.test(cmd)) return null;
@@ -4875,7 +4896,7 @@ function getPrecommitBlock() {
4875
4896
  if ${guard}; then
4876
4897
  mkdir -p .caliber
4877
4898
  echo "\\033[2mcaliber: refreshing docs...\\033[0m"
4878
- ${invoke} refresh --quiet 2>.caliber/refresh-hook.log || true
4899
+ ${invoke} refresh --quiet 2>.caliber/refresh-hook.log || echo "\\033[33mcaliber: refresh skipped \u2014 see .caliber/refresh-hook.log\\033[0m" >&2
4879
4900
  ${invoke} learn finalize 2>>.caliber/refresh-hook.log || true
4880
4901
  git diff --name-only -- CLAUDE.md .claude/ .cursor/ AGENTS.md CALIBER_LEARNINGS.md .github/ .agents/ .opencode/ 2>/dev/null | xargs git add 2>/dev/null || true
4881
4902
  fi
@@ -4900,19 +4921,29 @@ function isPreCommitHookInstalled() {
4900
4921
  const hookPath = getPreCommitPath();
4901
4922
  if (!hookPath || !fs11.existsSync(hookPath)) return false;
4902
4923
  const content = fs11.readFileSync(hookPath, "utf-8");
4903
- return content.includes(PRECOMMIT_START);
4924
+ return PRECOMMIT_ANY_VERSION_START_RE.test(content);
4904
4925
  }
4905
4926
  function installPreCommitHook() {
4906
- if (isPreCommitHookInstalled()) {
4907
- return { installed: false, alreadyInstalled: true };
4908
- }
4909
4927
  const hookPath = getPreCommitPath();
4910
- if (!hookPath) return { installed: false, alreadyInstalled: false };
4928
+ if (!hookPath) {
4929
+ return { installed: false, alreadyInstalled: false, upgraded: false };
4930
+ }
4911
4931
  const hooksDir = path10.dirname(hookPath);
4912
4932
  if (!fs11.existsSync(hooksDir)) fs11.mkdirSync(hooksDir, { recursive: true });
4913
- let content = "";
4914
- if (fs11.existsSync(hookPath)) {
4915
- content = fs11.readFileSync(hookPath, "utf-8");
4933
+ const exists = fs11.existsSync(hookPath);
4934
+ let content = exists ? fs11.readFileSync(hookPath, "utf-8") : "";
4935
+ if (PRECOMMIT_ANY_VERSION_START_RE.test(content)) {
4936
+ if (content.includes(PRECOMMIT_START)) {
4937
+ return { installed: false, alreadyInstalled: true, upgraded: false };
4938
+ }
4939
+ content = content.replace(PRECOMMIT_ANY_VERSION_BLOCK_RE, "\n").replace(/\n{3,}/g, "\n\n");
4940
+ if (!content.endsWith("\n")) content += "\n";
4941
+ content += "\n" + getPrecommitBlock() + "\n";
4942
+ fs11.writeFileSync(hookPath, content);
4943
+ fs11.chmodSync(hookPath, 493);
4944
+ return { installed: false, alreadyInstalled: false, upgraded: true };
4945
+ }
4946
+ if (exists) {
4916
4947
  if (!content.endsWith("\n")) content += "\n";
4917
4948
  content += "\n" + getPrecommitBlock() + "\n";
4918
4949
  } else {
@@ -4920,7 +4951,7 @@ function installPreCommitHook() {
4920
4951
  }
4921
4952
  fs11.writeFileSync(hookPath, content);
4922
4953
  fs11.chmodSync(hookPath, 493);
4923
- return { installed: true, alreadyInstalled: false };
4954
+ return { installed: true, alreadyInstalled: false, upgraded: false };
4924
4955
  }
4925
4956
  function removePreCommitHook() {
4926
4957
  const hookPath = getPreCommitPath();
@@ -4928,11 +4959,10 @@ function removePreCommitHook() {
4928
4959
  return { removed: false, notFound: true };
4929
4960
  }
4930
4961
  let content = fs11.readFileSync(hookPath, "utf-8");
4931
- if (!content.includes(PRECOMMIT_START)) {
4962
+ if (!PRECOMMIT_ANY_VERSION_START_RE.test(content)) {
4932
4963
  return { removed: false, notFound: true };
4933
4964
  }
4934
- const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
4935
- content = content.replace(regex, "\n");
4965
+ content = content.replace(PRECOMMIT_ANY_VERSION_BLOCK_RE, "\n").replace(/\n{3,}/g, "\n\n");
4936
4966
  if (content.trim() === "#!/bin/sh" || content.trim() === "") {
4937
4967
  fs11.unlinkSync(hookPath);
4938
4968
  } else {
@@ -7745,10 +7775,11 @@ function detectGitDrift(dir) {
7745
7775
  }
7746
7776
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".cursor/rules"];
7747
7777
  try {
7748
- const headTimestamp = execSync12(
7749
- "git log -1 --format=%ct HEAD",
7750
- { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7751
- ).trim();
7778
+ const headTimestamp = execSync12("git log -1 --format=%ct HEAD", {
7779
+ cwd: dir,
7780
+ encoding: "utf-8",
7781
+ stdio: ["pipe", "pipe", "pipe"]
7782
+ }).trim();
7752
7783
  const headTime = parseInt(headTimestamp, 10) * 1e3;
7753
7784
  for (const file of configFiles) {
7754
7785
  const filePath = join5(dir, file);
@@ -7756,7 +7787,11 @@ function detectGitDrift(dir) {
7756
7787
  try {
7757
7788
  const mtime = statSync(filePath).mtime.getTime();
7758
7789
  if (mtime > headTime) {
7759
- return { commitsSinceConfigUpdate: 0, lastConfigCommit: "uncommitted (recently modified)", isGitRepo: true };
7790
+ return {
7791
+ commitsSinceConfigUpdate: 0,
7792
+ lastConfigCommit: "uncommitted (recently modified)",
7793
+ isGitRepo: true
7794
+ };
7760
7795
  }
7761
7796
  } catch {
7762
7797
  }
@@ -7766,19 +7801,20 @@ function detectGitDrift(dir) {
7766
7801
  let latestConfigCommitHash = null;
7767
7802
  for (const file of configFiles) {
7768
7803
  try {
7769
- const hash = execSync12(
7770
- `git log -1 --format=%H -- "${file}"`,
7771
- { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7772
- ).trim();
7804
+ const hash = execSync12(`git log -1 --format=%H -- "${file}"`, {
7805
+ cwd: dir,
7806
+ encoding: "utf-8",
7807
+ stdio: ["pipe", "pipe", "pipe"]
7808
+ }).trim();
7773
7809
  if (!hash) continue;
7774
7810
  if (!latestConfigCommitHash) {
7775
7811
  latestConfigCommitHash = hash;
7776
7812
  } else {
7777
7813
  try {
7778
- execSync12(
7779
- `git merge-base --is-ancestor ${latestConfigCommitHash} ${hash}`,
7780
- { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }
7781
- );
7814
+ execSync12(`git merge-base --is-ancestor ${latestConfigCommitHash} ${hash}`, {
7815
+ cwd: dir,
7816
+ stdio: ["pipe", "pipe", "pipe"]
7817
+ });
7782
7818
  latestConfigCommitHash = hash;
7783
7819
  } catch {
7784
7820
  }
@@ -7790,22 +7826,28 @@ function detectGitDrift(dir) {
7790
7826
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: true };
7791
7827
  }
7792
7828
  try {
7793
- const countStr = execSync12(
7794
- `git rev-list --count ${latestConfigCommitHash}..HEAD`,
7795
- { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7796
- ).trim();
7829
+ const countStr = execSync12(`git rev-list --count ${latestConfigCommitHash}..HEAD`, {
7830
+ cwd: dir,
7831
+ encoding: "utf-8",
7832
+ stdio: ["pipe", "pipe", "pipe"]
7833
+ }).trim();
7797
7834
  const commitsSince = parseInt(countStr, 10) || 0;
7798
- const lastDate = execSync12(
7799
- `git log -1 --format=%ci ${latestConfigCommitHash}`,
7800
- { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7801
- ).trim();
7835
+ const lastDate = execSync12(`git log -1 --format=%ci ${latestConfigCommitHash}`, {
7836
+ cwd: dir,
7837
+ encoding: "utf-8",
7838
+ stdio: ["pipe", "pipe", "pipe"]
7839
+ }).trim();
7802
7840
  return {
7803
7841
  commitsSinceConfigUpdate: commitsSince,
7804
7842
  lastConfigCommit: lastDate,
7805
7843
  isGitRepo: true
7806
7844
  };
7807
7845
  } catch {
7808
- return { commitsSinceConfigUpdate: 0, lastConfigCommit: latestConfigCommitHash, isGitRepo: true };
7846
+ return {
7847
+ commitsSinceConfigUpdate: 0,
7848
+ lastConfigCommit: latestConfigCommitHash,
7849
+ isGitRepo: true
7850
+ };
7809
7851
  }
7810
7852
  }
7811
7853
  function checkAccuracy(dir) {
@@ -7855,10 +7897,13 @@ function checkAccuracy(dir) {
7855
7897
  earnedPoints: driftPoints,
7856
7898
  passed: drift.commitsSinceConfigUpdate <= 15 || !drift.isGitRepo,
7857
7899
  detail: !drift.isGitRepo ? "Not a git repository \u2014 skipping drift check" : !drift.lastConfigCommit ? "Config files not tracked in git" : drift.commitsSinceConfigUpdate === 0 ? "Config is up to date with latest commits" : `${drift.commitsSinceConfigUpdate} commit${drift.commitsSinceConfigUpdate === 1 ? "" : "s"} since last config update`,
7858
- suggestion: drift.commitsSinceConfigUpdate > 15 ? `Code has had ${drift.commitsSinceConfigUpdate} commits since last config update \u2014 run \`${resolveCaliber()} refresh\` to sync` : void 0,
7900
+ suggestion: drift.commitsSinceConfigUpdate > 15 ? `Code has had ${drift.commitsSinceConfigUpdate} commits since last config update \u2014 run \`${displayCaliberName()} refresh\` to sync` : void 0,
7859
7901
  fix: drift.commitsSinceConfigUpdate > 15 ? {
7860
7902
  action: "refresh_config",
7861
- data: { commitsSince: drift.commitsSinceConfigUpdate, lastConfigCommit: drift.lastConfigCommit },
7903
+ data: {
7904
+ commitsSince: drift.commitsSinceConfigUpdate,
7905
+ lastConfigCommit: drift.lastConfigCommit
7906
+ },
7862
7907
  instruction: `Config is ${drift.commitsSinceConfigUpdate} commits behind. Review recent changes and update config accordingly.`
7863
7908
  } : void 0
7864
7909
  });
@@ -7873,10 +7918,11 @@ import { join as join6 } from "path";
7873
7918
  function getCommitsSinceConfigUpdate(dir) {
7874
7919
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules"];
7875
7920
  try {
7876
- const headTimestamp = execSync13(
7877
- "git log -1 --format=%ct HEAD",
7878
- { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7879
- ).trim();
7921
+ const headTimestamp = execSync13("git log -1 --format=%ct HEAD", {
7922
+ cwd: dir,
7923
+ encoding: "utf-8",
7924
+ stdio: ["pipe", "pipe", "pipe"]
7925
+ }).trim();
7880
7926
  const headTime = parseInt(headTimestamp, 10) * 1e3;
7881
7927
  for (const file of configFiles) {
7882
7928
  const filePath = join6(dir, file);
@@ -7893,15 +7939,17 @@ function getCommitsSinceConfigUpdate(dir) {
7893
7939
  }
7894
7940
  for (const file of configFiles) {
7895
7941
  try {
7896
- const hash = execSync13(
7897
- `git log -1 --format=%H -- "${file}"`,
7898
- { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7899
- ).trim();
7942
+ const hash = execSync13(`git log -1 --format=%H -- "${file}"`, {
7943
+ cwd: dir,
7944
+ encoding: "utf-8",
7945
+ stdio: ["pipe", "pipe", "pipe"]
7946
+ }).trim();
7900
7947
  if (hash) {
7901
- const countStr = execSync13(
7902
- `git rev-list --count ${hash}..HEAD`,
7903
- { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7904
- ).trim();
7948
+ const countStr = execSync13(`git rev-list --count ${hash}..HEAD`, {
7949
+ cwd: dir,
7950
+ encoding: "utf-8",
7951
+ stdio: ["pipe", "pipe", "pipe"]
7952
+ }).trim();
7905
7953
  return parseInt(countStr, 10) || 0;
7906
7954
  }
7907
7955
  } catch {
@@ -7930,7 +7978,7 @@ function checkFreshness(dir) {
7930
7978
  earnedPoints: freshnessPoints,
7931
7979
  passed: freshnessPoints >= 3,
7932
7980
  detail: freshnessDetail,
7933
- suggestion: commitsSince !== null && freshnessPoints < 3 ? `Config is ${commitsSince} commits behind \u2014 run \`${resolveCaliber()} refresh\` to update it` : void 0,
7981
+ suggestion: commitsSince !== null && freshnessPoints < 3 ? `Config is ${commitsSince} commits behind \u2014 run \`${displayCaliberName()} refresh\` to update it` : void 0,
7934
7982
  fix: commitsSince !== null && freshnessPoints < 3 ? {
7935
7983
  action: "refresh_config",
7936
7984
  data: { commitsSince },
@@ -8082,11 +8130,11 @@ function checkBonus(dir) {
8082
8130
  earnedPoints: hasHooks ? POINTS_HOOKS : 0,
8083
8131
  passed: hasHooks,
8084
8132
  detail: hasHooks ? hookSources.join(", ") : "No hooks configured",
8085
- suggestion: hasHooks ? void 0 : `Hooks auto-sync your agent config on every commit so it stays fresh. Run \`${resolveCaliber()} init\` to set up`,
8133
+ suggestion: hasHooks ? void 0 : `Hooks auto-sync your agent config on every commit so it stays fresh. Run \`${displayCaliberName()} init\` to set up`,
8086
8134
  fix: hasHooks ? void 0 : {
8087
8135
  action: "install_hooks",
8088
8136
  data: {},
8089
- instruction: `Run ${resolveCaliber()} init to add pre-commit refresh instructions to config files.`
8137
+ instruction: `Run ${displayCaliberName()} init to add pre-commit refresh instructions to config files.`
8090
8138
  }
8091
8139
  });
8092
8140
  const agentsMdExists = existsSync6(join7(dir, "AGENTS.md"));
@@ -8151,7 +8199,7 @@ function checkBonus(dir) {
8151
8199
  earnedPoints: hasLearned ? POINTS_LEARNED_CONTENT : 0,
8152
8200
  passed: hasLearned,
8153
8201
  detail: hasLearned ? "Session learnings found in CALIBER_LEARNINGS.md" : "No learned content",
8154
- suggestion: hasLearned ? void 0 : `Session learnings capture patterns from your coding sessions so the agent improves over time. Run \`${resolveCaliber()} learn install\``
8202
+ suggestion: hasLearned ? void 0 : `Session learnings capture patterns from your coding sessions so the agent improves over time. Run \`${displayCaliberName()} learn install\``
8155
8203
  });
8156
8204
  const configContent = (() => {
8157
8205
  const parts = [];
@@ -8198,7 +8246,7 @@ function checkSources(dir) {
8198
8246
  earnedPoints: hasSources ? POINTS_SOURCES_CONFIGURED : 0,
8199
8247
  passed: hasSources,
8200
8248
  detail: hasSources ? `${configSources.length} source${configSources.length === 1 ? "" : "s"} configured` : "No external sources configured",
8201
- suggestion: hasSources ? void 0 : `Run \`${resolveCaliber()} sources add <path>\` to add related repos or docs`
8249
+ suggestion: hasSources ? void 0 : `Run \`${displayCaliberName()} sources add <path>\` to add related repos or docs`
8202
8250
  });
8203
8251
  if (hasSources) {
8204
8252
  const claudeMd = readFileOrNull(join8(dir, "CLAUDE.md"));
@@ -8216,7 +8264,7 @@ function checkSources(dir) {
8216
8264
  earnedPoints: referenced ? POINTS_SOURCES_REFERENCED : 0,
8217
8265
  passed: referenced,
8218
8266
  detail: referenced ? "At least one source is referenced in CLAUDE.md" : "No configured sources are mentioned in CLAUDE.md",
8219
- suggestion: referenced ? void 0 : `Regenerate with \`${resolveCaliber()} init\` to include source context in your config`
8267
+ suggestion: referenced ? void 0 : `Regenerate with \`${displayCaliberName()} init\` to include source context in your config`
8220
8268
  });
8221
8269
  }
8222
8270
  return checks;
@@ -8327,7 +8375,14 @@ var CATEGORY_LABELS = {
8327
8375
  freshness: { icon: "\u{1F6E1}\uFE0F", label: "FRESHNESS & SAFETY" },
8328
8376
  bonus: { icon: "\u2B50", label: "BONUS" }
8329
8377
  };
8330
- var CATEGORY_ORDER = ["existence", "quality", "grounding", "accuracy", "freshness", "bonus"];
8378
+ var CATEGORY_ORDER = [
8379
+ "existence",
8380
+ "quality",
8381
+ "grounding",
8382
+ "accuracy",
8383
+ "freshness",
8384
+ "bonus"
8385
+ ];
8331
8386
  function gradeColor(grade) {
8332
8387
  switch (grade) {
8333
8388
  case "A":
@@ -8396,7 +8451,9 @@ function displayScore(result) {
8396
8451
  console.log("");
8397
8452
  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"));
8398
8453
  console.log("");
8399
- console.log(` ${chalk3.bold("Agent Config Score")} ${gc(chalk3.bold(`${result.score} / ${result.maxScore}`))} Grade ${gc(chalk3.bold(result.grade))}`);
8454
+ console.log(
8455
+ ` ${chalk3.bold("Agent Config Score")} ${gc(chalk3.bold(`${result.score} / ${result.maxScore}`))} Grade ${gc(chalk3.bold(result.grade))}`
8456
+ );
8400
8457
  console.log(` ${progressBar(result.score, result.maxScore)}`);
8401
8458
  console.log(chalk3.dim(` Target: ${agentLabel}`));
8402
8459
  console.log("");
@@ -8419,7 +8476,11 @@ function displayScore(result) {
8419
8476
  formatTopImprovements(result.checks);
8420
8477
  }
8421
8478
  function formatTopImprovements(checks) {
8422
- const improvable = checks.filter((c) => c.earnedPoints < c.maxPoints).map((c) => ({ name: c.name, potential: c.maxPoints - c.earnedPoints, suggestion: c.suggestion })).sort((a, b) => b.potential - a.potential).slice(0, 5);
8479
+ const improvable = checks.filter((c) => c.earnedPoints < c.maxPoints).map((c) => ({
8480
+ name: c.name,
8481
+ potential: c.maxPoints - c.earnedPoints,
8482
+ suggestion: c.suggestion
8483
+ })).sort((a, b) => b.potential - a.potential).slice(0, 5);
8423
8484
  if (improvable.length === 0) return;
8424
8485
  console.log(chalk3.gray(" \u2500 TOP IMPROVEMENTS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
8425
8486
  console.log("");
@@ -8450,8 +8511,12 @@ function displayScoreSummary(result) {
8450
8511
  }
8451
8512
  const remaining = failing.length - shown.length;
8452
8513
  const moreText = remaining > 0 ? ` (+${remaining} more)` : "";
8453
- console.log(chalk3.dim(`
8454
- Run ${chalk3.hex("#83D1EB")(`${resolveCaliber()} score`)} for details.${moreText}`));
8514
+ console.log(
8515
+ chalk3.dim(
8516
+ `
8517
+ Run ${chalk3.hex("#83D1EB")(`${displayCaliberName()} score`)} for details.${moreText}`
8518
+ )
8519
+ );
8455
8520
  }
8456
8521
  console.log("");
8457
8522
  }
@@ -8467,7 +8532,9 @@ function displayScoreDelta(before, after) {
8467
8532
  console.log(
8468
8533
  ` Score: ${beforeGc(`${before.score}`)} ${chalk3.gray("\u2192")} ${afterGc(`${after.score}`)} ${deltaColor(deltaStr + " pts")} ${beforeGc(before.grade)} ${chalk3.gray("\u2192")} ${afterGc(after.grade)}`
8469
8534
  );
8470
- console.log(` ${progressBar(before.score, before.maxScore, 19)} ${chalk3.gray("\u2192")} ${progressBar(after.score, after.maxScore, 19)}`);
8535
+ console.log(
8536
+ ` ${progressBar(before.score, before.maxScore, 19)} ${chalk3.gray("\u2192")} ${progressBar(after.score, after.maxScore, 19)}`
8537
+ );
8471
8538
  console.log("");
8472
8539
  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"));
8473
8540
  console.log("");
@@ -9149,7 +9216,7 @@ async function querySkills(query) {
9149
9216
  console.log("");
9150
9217
  console.log(
9151
9218
  chalk5.dim(
9152
- ` Install with: ${resolveCaliber()} skills --install ${available.map((r) => r.slug).join(",")}`
9219
+ ` Install with: ${displayCaliberName()} skills --install ${available.map((r) => r.slug).join(",")}`
9153
9220
  )
9154
9221
  );
9155
9222
  console.log("");
@@ -11137,7 +11204,7 @@ function log(verbose, ...args) {
11137
11204
  async function initCommand(options) {
11138
11205
  const brand = chalk14.hex("#EB9D83");
11139
11206
  const title = chalk14.hex("#83D1EB");
11140
- const bin = resolveCaliber();
11207
+ const bin = displayCaliberName();
11141
11208
  const firstRun = isFirstRun(process.cwd());
11142
11209
  if (firstRun) {
11143
11210
  console.log(
@@ -11278,6 +11345,8 @@ async function initCommand(options) {
11278
11345
  const hookResult = installPreCommitHook();
11279
11346
  if (hookResult.installed) {
11280
11347
  console.log(` ${chalk14.green("\u2713")} Pre-commit hook installed \u2014 configs sync on every commit`);
11348
+ } else if (hookResult.upgraded) {
11349
+ console.log(` ${chalk14.green("\u2713")} Pre-commit hook \u2014 upgraded to latest version`);
11281
11350
  } else if (hookResult.alreadyInstalled) {
11282
11351
  console.log(` ${chalk14.green("\u2713")} Pre-commit hook \u2014 active`);
11283
11352
  }
@@ -11977,24 +12046,34 @@ async function statusCommand(options) {
11977
12046
  const config = loadConfig();
11978
12047
  const manifest = readManifest();
11979
12048
  if (options.json) {
11980
- console.log(JSON.stringify({
11981
- configured: !!config,
11982
- provider: config?.provider,
11983
- model: config?.model,
11984
- manifest
11985
- }, null, 2));
12049
+ console.log(
12050
+ JSON.stringify(
12051
+ {
12052
+ configured: !!config,
12053
+ provider: config?.provider,
12054
+ model: config?.model,
12055
+ manifest
12056
+ },
12057
+ null,
12058
+ 2
12059
+ )
12060
+ );
11986
12061
  return;
11987
12062
  }
11988
12063
  console.log(chalk16.bold("\nCaliber Status\n"));
11989
12064
  if (config) {
11990
12065
  console.log(` LLM: ${chalk16.green(config.provider)} (${config.model})`);
11991
12066
  } else {
11992
- const bin = resolveCaliber();
11993
- console.log(` LLM: ${chalk16.yellow("Not configured")} \u2014 run ${chalk16.hex("#83D1EB")(`${bin} config`)}`);
12067
+ const bin = displayCaliberName();
12068
+ console.log(
12069
+ ` LLM: ${chalk16.yellow("Not configured")} \u2014 run ${chalk16.hex("#83D1EB")(`${bin} config`)}`
12070
+ );
11994
12071
  }
11995
12072
  if (!manifest) {
11996
12073
  console.log(` Config: ${chalk16.dim("No config applied")}`);
11997
- console.log(chalk16.dim("\n Run ") + chalk16.hex("#83D1EB")(`${resolveCaliber()} init`) + chalk16.dim(" to get started.\n"));
12074
+ console.log(
12075
+ chalk16.dim("\n Run ") + chalk16.hex("#83D1EB")(`${displayCaliberName()} init`) + chalk16.dim(" to get started.\n")
12076
+ );
11998
12077
  return;
11999
12078
  }
12000
12079
  console.log(` Files managed: ${chalk16.cyan(manifest.entries.length.toString())}`);
@@ -12014,15 +12093,19 @@ init_review();
12014
12093
  init_config();
12015
12094
  init_resolve_caliber();
12016
12095
  async function regenerateCommand(options) {
12017
- const bin = resolveCaliber();
12096
+ const bin = displayCaliberName();
12018
12097
  const config = loadConfig();
12019
12098
  if (!config) {
12020
- console.log(chalk17.red("No LLM provider configured. Run ") + chalk17.hex("#83D1EB")(`${bin} config`) + chalk17.red(" first."));
12099
+ console.log(
12100
+ chalk17.red("No LLM provider configured. Run ") + chalk17.hex("#83D1EB")(`${bin} config`) + chalk17.red(" first.")
12101
+ );
12021
12102
  throw new Error("__exit__");
12022
12103
  }
12023
12104
  const manifest = readManifest();
12024
12105
  if (!manifest) {
12025
- console.log(chalk17.yellow("No existing config found. Run ") + chalk17.hex("#83D1EB")(`${bin} init`) + chalk17.yellow(" first."));
12106
+ console.log(
12107
+ chalk17.yellow("No existing config found. Run ") + chalk17.hex("#83D1EB")(`${bin} init`) + chalk17.yellow(" first.")
12108
+ );
12026
12109
  throw new Error("__exit__");
12027
12110
  }
12028
12111
  const targetAgent = readState()?.targetAgent ?? ["claude", "cursor"];
@@ -12037,27 +12120,24 @@ async function regenerateCommand(options) {
12037
12120
  return;
12038
12121
  }
12039
12122
  const genSpinner = ora5("Regenerating config...").start();
12040
- const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, { showElapsedTime: true });
12123
+ const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, {
12124
+ showElapsedTime: true
12125
+ });
12041
12126
  genMessages.start();
12042
12127
  let generatedSetup = null;
12043
12128
  try {
12044
- const result = await generateSetup(
12045
- fingerprint,
12046
- targetAgent,
12047
- void 0,
12048
- {
12049
- onStatus: (status) => {
12050
- genMessages.handleServerStatus(status);
12051
- },
12052
- onComplete: (setup) => {
12053
- generatedSetup = setup;
12054
- },
12055
- onError: (error) => {
12056
- genMessages.stop();
12057
- genSpinner.fail(`Generation error: ${error}`);
12058
- }
12129
+ const result = await generateSetup(fingerprint, targetAgent, void 0, {
12130
+ onStatus: (status) => {
12131
+ genMessages.handleServerStatus(status);
12132
+ },
12133
+ onComplete: (setup) => {
12134
+ generatedSetup = setup;
12135
+ },
12136
+ onError: (error) => {
12137
+ genMessages.stop();
12138
+ genSpinner.fail(`Generation error: ${error}`);
12059
12139
  }
12060
- );
12140
+ });
12061
12141
  if (!generatedSetup) generatedSetup = result.setup;
12062
12142
  } catch (err) {
12063
12143
  genMessages.stop();
@@ -12075,9 +12155,13 @@ async function regenerateCommand(options) {
12075
12155
  const setupFiles = collectSetupFiles(generatedSetup, targetAgent);
12076
12156
  const staged = stageFiles(setupFiles, process.cwd());
12077
12157
  const totalChanges = staged.newFiles + staged.modifiedFiles;
12078
- console.log(chalk17.dim(`
12158
+ console.log(
12159
+ chalk17.dim(
12160
+ `
12079
12161
  ${chalk17.green(`${staged.newFiles} new`)} / ${chalk17.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}
12080
- `));
12162
+ `
12163
+ )
12164
+ );
12081
12165
  if (totalChanges === 0) {
12082
12166
  console.log(chalk17.dim(" No changes needed \u2014 your configs are already up to date.\n"));
12083
12167
  cleanupStaging();
@@ -12138,21 +12222,33 @@ async function regenerateCommand(options) {
12138
12222
  const afterScore = computeLocalScore(process.cwd(), targetAgent);
12139
12223
  if (afterScore.score < baselineScore.score) {
12140
12224
  console.log("");
12141
- console.log(chalk17.yellow(` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`));
12225
+ console.log(
12226
+ chalk17.yellow(
12227
+ ` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`
12228
+ )
12229
+ );
12142
12230
  try {
12143
12231
  const { restored, removed } = undoSetup();
12144
12232
  if (restored.length > 0 || removed.length > 0) {
12145
- console.log(chalk17.dim(` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`));
12233
+ console.log(
12234
+ chalk17.dim(
12235
+ ` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`
12236
+ )
12237
+ );
12146
12238
  }
12147
12239
  } catch {
12148
12240
  }
12149
- console.log(chalk17.dim(" Run ") + chalk17.hex("#83D1EB")(`${bin} init --force`) + chalk17.dim(" to override.\n"));
12241
+ console.log(
12242
+ chalk17.dim(" Run ") + chalk17.hex("#83D1EB")(`${bin} init --force`) + chalk17.dim(" to override.\n")
12243
+ );
12150
12244
  return;
12151
12245
  }
12152
12246
  displayScoreDelta(baselineScore, afterScore);
12153
12247
  trackRegenerateCompleted(action, Date.now());
12154
12248
  console.log(chalk17.bold.green(" Regeneration complete!"));
12155
- console.log(chalk17.dim(" Run ") + chalk17.hex("#83D1EB")(`${bin} undo`) + chalk17.dim(" to revert changes.\n"));
12249
+ console.log(
12250
+ chalk17.dim(" Run ") + chalk17.hex("#83D1EB")(`${bin} undo`) + chalk17.dim(" to revert changes.\n")
12251
+ );
12156
12252
  }
12157
12253
 
12158
12254
  // src/commands/score.ts
@@ -12267,7 +12363,7 @@ async function scoreCommand(options) {
12267
12363
  displayScore(result);
12268
12364
  const separator = chalk18.gray(" " + "\u2500".repeat(53));
12269
12365
  console.log(separator);
12270
- const bin = resolveCaliber();
12366
+ const bin = displayCaliberName();
12271
12367
  const failing = result.checks.filter((c) => !c.passed && c.maxPoints > 0).sort((a, b) => b.maxPoints - b.earnedPoints - (a.maxPoints - a.earnedPoints));
12272
12368
  if (result.score < 70 && failing.length > 0) {
12273
12369
  const topFix = failing[0];
@@ -13228,7 +13324,7 @@ async function refreshCommand(options) {
13228
13324
  if (!config) {
13229
13325
  if (quiet) return;
13230
13326
  console.log(
13231
- chalk19.red("No LLM provider configured. Run ") + chalk19.hex("#83D1EB")(`${resolveCaliber()} config`) + chalk19.red(" (e.g. choose Cursor) or set an API key.")
13327
+ chalk19.red("No LLM provider configured. Run ") + chalk19.hex("#83D1EB")(`${displayCaliberName()} config`) + chalk19.red(" (e.g. choose Cursor) or set an API key.")
13232
13328
  );
13233
13329
  throw new Error("__exit__");
13234
13330
  }
@@ -13336,7 +13432,9 @@ async function hooksCommand(options) {
13336
13432
  if (options.install) {
13337
13433
  for (const hook of HOOKS) {
13338
13434
  const result = hook.install();
13339
- if (result.alreadyInstalled) {
13435
+ if (result.upgraded) {
13436
+ console.log(chalk20.green(" \u2713") + ` ${hook.label} upgraded to latest version`);
13437
+ } else if (result.alreadyInstalled) {
13340
13438
  console.log(chalk20.dim(` ${hook.label} already enabled.`));
13341
13439
  } else {
13342
13440
  console.log(chalk20.green(" \u2713") + ` ${hook.label} enabled`);
@@ -14276,7 +14374,7 @@ async function learnFinalizeCommand(options) {
14276
14374
  if (isAuto) return;
14277
14375
  console.log(
14278
14376
  chalk23.yellow(
14279
- `caliber: no LLM provider configured \u2014 run \`${resolveCaliber()} config\` first`
14377
+ `caliber: no LLM provider configured \u2014 run \`${displayCaliberName()} config\` first`
14280
14378
  )
14281
14379
  );
14282
14380
  clearSession();
@@ -14442,7 +14540,7 @@ async function learnFinalizeCommand(options) {
14442
14540
  if (staleLearnings.length > 0 && !isAuto) {
14443
14541
  console.log(
14444
14542
  chalk23.yellow(
14445
- `caliber: ${staleLearnings.length} learning${staleLearnings.length === 1 ? "" : "s"} never activated \u2014 run \`${resolveCaliber()} learn list --verbose\` to review`
14543
+ `caliber: ${staleLearnings.length} learning${staleLearnings.length === 1 ? "" : "s"} never activated \u2014 run \`${displayCaliberName()} learn list --verbose\` to review`
14446
14544
  )
14447
14545
  );
14448
14546
  }
@@ -14499,7 +14597,7 @@ async function learnInstallCommand() {
14499
14597
  if (!fs48.existsSync(".claude") && !fs48.existsSync(".cursor")) {
14500
14598
  console.log(chalk23.yellow("No .claude/ or .cursor/ directory found."));
14501
14599
  console.log(
14502
- chalk23.dim(` Run \`${resolveCaliber()} init\` first, or create the directory manually.`)
14600
+ chalk23.dim(` Run \`${displayCaliberName()} init\` first, or create the directory manually.`)
14503
14601
  );
14504
14602
  return;
14505
14603
  }
@@ -14547,7 +14645,7 @@ async function learnStatusCommand() {
14547
14645
  }
14548
14646
  if (!claudeInstalled && !cursorInstalled) {
14549
14647
  console.log(
14550
- chalk23.dim(` Run \`${resolveCaliber()} learn install\` to enable session learning.`)
14648
+ chalk23.dim(` Run \`${displayCaliberName()} learn install\` to enable session learning.`)
14551
14649
  );
14552
14650
  }
14553
14651
  console.log();
@@ -14603,7 +14701,9 @@ function getAllLearnings() {
14603
14701
  async function learnListCommand(options) {
14604
14702
  const items = getAllLearnings();
14605
14703
  if (items.length === 0) {
14606
- console.log(chalk23.dim(`No learnings yet. Run \`${resolveCaliber()} learn install\` to start.`));
14704
+ console.log(
14705
+ chalk23.dim(`No learnings yet. Run \`${displayCaliberName()} learn install\` to start.`)
14706
+ );
14607
14707
  return;
14608
14708
  }
14609
14709
  const roiStats = options?.verbose ? readROIStats() : null;
@@ -14634,7 +14734,7 @@ async function learnDeleteCommand(indexStr) {
14634
14734
  if (isNaN(index) || index < 1) {
14635
14735
  console.log(
14636
14736
  chalk23.red(
14637
- `Invalid index: "${indexStr}". Use a number from \`${resolveCaliber()} learn list\`.`
14737
+ `Invalid index: "${indexStr}". Use a number from \`${displayCaliberName()} learn list\`.`
14638
14738
  )
14639
14739
  );
14640
14740
  return;
@@ -14745,13 +14845,23 @@ function displayColdStart(score) {
14745
14845
  const hooksInstalled = areLearningHooksInstalled() || areCursorLearningHooksInstalled();
14746
14846
  if (!hooksInstalled) {
14747
14847
  console.log(chalk24.yellow(" Learning hooks not installed."));
14748
- console.log(chalk24.dim(" Session learning captures patterns from your AI coding sessions \u2014 what"));
14749
- console.log(chalk24.dim(" fails, what works, corrections you make \u2014 so your agents improve over time.\n"));
14750
- console.log(chalk24.dim(" Run ") + chalk24.cyan(`${resolveCaliber()} learn install`) + chalk24.dim(" to enable."));
14848
+ console.log(
14849
+ chalk24.dim(" Session learning captures patterns from your AI coding sessions \u2014 what")
14850
+ );
14851
+ console.log(
14852
+ chalk24.dim(" fails, what works, corrections you make \u2014 so your agents improve over time.\n")
14853
+ );
14854
+ console.log(
14855
+ chalk24.dim(" Run ") + chalk24.cyan(`${displayCaliberName()} learn install`) + chalk24.dim(" to enable.")
14856
+ );
14751
14857
  } else {
14752
14858
  console.log(chalk24.dim(" Learning hooks are active. Use your AI agent and insights"));
14753
14859
  console.log(chalk24.dim(" will appear automatically after each session.\n"));
14754
- console.log(chalk24.dim(` Progress: 0/${MIN_SESSIONS_FULL} sessions \u2014 full insights unlock at ${MIN_SESSIONS_FULL}`));
14860
+ console.log(
14861
+ chalk24.dim(
14862
+ ` Progress: 0/${MIN_SESSIONS_FULL} sessions \u2014 full insights unlock at ${MIN_SESSIONS_FULL}`
14863
+ )
14864
+ );
14755
14865
  }
14756
14866
  console.log(chalk24.dim(`
14757
14867
  Config score: ${score.score}/100 (${score.grade})`));
@@ -14760,20 +14870,32 @@ function displayColdStart(score) {
14760
14870
  function displayEarlyData(data, score) {
14761
14871
  console.log(chalk24.bold("\n Agent Insights") + chalk24.yellow(" (early data)\n"));
14762
14872
  const remaining = MIN_SESSIONS_FULL - data.totalSessions;
14763
- console.log(chalk24.dim(` ${data.totalSessions}/${MIN_SESSIONS_FULL} sessions tracked \u2014 ${remaining} more for full insights.
14764
- `));
14873
+ console.log(
14874
+ chalk24.dim(
14875
+ ` ${data.totalSessions}/${MIN_SESSIONS_FULL} sessions tracked \u2014 ${remaining} more for full insights.
14876
+ `
14877
+ )
14878
+ );
14765
14879
  console.log(` Sessions tracked: ${chalk24.cyan(String(data.totalSessions))}`);
14766
14880
  console.log(` Learnings accumulated: ${chalk24.cyan(String(data.learningCount))}`);
14767
14881
  if (data.totalWasteTokens > 0) {
14768
- console.log(` Waste captured: ${chalk24.cyan(data.totalWasteTokens.toLocaleString())} tokens`);
14882
+ console.log(
14883
+ ` Waste captured: ${chalk24.cyan(data.totalWasteTokens.toLocaleString())} tokens`
14884
+ );
14769
14885
  }
14770
14886
  if (data.failureRateImprovement !== null && data.failureRateImprovement > 0) {
14771
- console.log(` Failure rate trend: ${chalk24.green(`${data.failureRateImprovement}% fewer`)} failures with learnings ${chalk24.dim("(early signal)")}`);
14887
+ console.log(
14888
+ ` Failure rate trend: ${chalk24.green(`${data.failureRateImprovement}% fewer`)} failures with learnings ${chalk24.dim("(early signal)")}`
14889
+ );
14772
14890
  } else if (data.totalSessions > 0 && data.failureRateImprovement === null) {
14773
- console.log(` Failure rate trend: ${chalk24.dim("collecting data (need 3+ sessions in each group)")}`);
14891
+ console.log(
14892
+ ` Failure rate trend: ${chalk24.dim("collecting data (need 3+ sessions in each group)")}`
14893
+ );
14774
14894
  }
14775
14895
  if (data.taskSuccessRate !== null) {
14776
- console.log(` Task success rate: ${chalk24.cyan(`${data.taskSuccessRate}%`)} ${chalk24.dim(`(${data.taskCount} tasks)`)}`);
14896
+ console.log(
14897
+ ` Task success rate: ${chalk24.cyan(`${data.taskSuccessRate}%`)} ${chalk24.dim(`(${data.taskCount} tasks)`)}`
14898
+ );
14777
14899
  }
14778
14900
  console.log(` Config score: ${chalk24.cyan(`${score.score}/100`)} (${score.grade})`);
14779
14901
  console.log("");
@@ -14783,32 +14905,48 @@ function displayFullInsights(data, score) {
14783
14905
  console.log(chalk24.bold(" Agent Health"));
14784
14906
  if (data.taskSuccessRate !== null) {
14785
14907
  const color = data.taskSuccessRate >= 80 ? chalk24.green : data.taskSuccessRate >= 60 ? chalk24.yellow : chalk24.red;
14786
- console.log(` Task success rate: ${color(`${data.taskSuccessRate}%`)} across ${data.taskCount} tasks`);
14908
+ console.log(
14909
+ ` Task success rate: ${color(`${data.taskSuccessRate}%`)} across ${data.taskCount} tasks`
14910
+ );
14787
14911
  if (data.taskCorrectionCount > 0) {
14788
- console.log(` Corrections needed: ${chalk24.yellow(String(data.taskCorrectionCount))} tasks required user correction`);
14912
+ console.log(
14913
+ ` Corrections needed: ${chalk24.yellow(String(data.taskCorrectionCount))} tasks required user correction`
14914
+ );
14789
14915
  }
14790
14916
  }
14791
14917
  console.log(` Sessions tracked: ${chalk24.cyan(String(data.totalSessions))}`);
14792
14918
  console.log(chalk24.bold("\n Learning Impact"));
14793
14919
  console.log(` Learnings active: ${chalk24.cyan(String(data.learningCount))}`);
14794
14920
  if (data.failureRateWith !== null && data.failureRateWithout !== null) {
14795
- console.log(` Failure rate: ${chalk24.red(data.failureRateWithout.toFixed(1))}/session ${chalk24.dim("\u2192")} ${chalk24.green(data.failureRateWith.toFixed(1))}/session with learnings`);
14921
+ console.log(
14922
+ ` Failure rate: ${chalk24.red(data.failureRateWithout.toFixed(1))}/session ${chalk24.dim("\u2192")} ${chalk24.green(data.failureRateWith.toFixed(1))}/session with learnings`
14923
+ );
14796
14924
  if (data.failureRateImprovement !== null && data.failureRateImprovement > 0) {
14797
- console.log(` Improvement: ${chalk24.green(`${data.failureRateImprovement}%`)} fewer failures`);
14925
+ console.log(
14926
+ ` Improvement: ${chalk24.green(`${data.failureRateImprovement}%`)} fewer failures`
14927
+ );
14798
14928
  } else if (data.failureRateImprovement === null) {
14799
- console.log(` Improvement: ${chalk24.dim("collecting data (need 3+ sessions in each group)")}`);
14929
+ console.log(
14930
+ ` Improvement: ${chalk24.dim("collecting data (need 3+ sessions in each group)")}`
14931
+ );
14800
14932
  }
14801
14933
  }
14802
14934
  if (data.totalWasteTokens > 0 || data.estimatedSavingsTokens > 0) {
14803
14935
  console.log(chalk24.bold("\n Efficiency"));
14804
14936
  if (data.totalWasteTokens > 0) {
14805
- console.log(` Waste captured: ${chalk24.cyan(data.totalWasteTokens.toLocaleString())} tokens`);
14937
+ console.log(
14938
+ ` Waste captured: ${chalk24.cyan(data.totalWasteTokens.toLocaleString())} tokens`
14939
+ );
14806
14940
  }
14807
14941
  if (data.estimatedSavingsTokens > 0) {
14808
- console.log(` Estimated savings: ~${chalk24.green(data.estimatedSavingsTokens.toLocaleString())} tokens`);
14942
+ console.log(
14943
+ ` Estimated savings: ~${chalk24.green(data.estimatedSavingsTokens.toLocaleString())} tokens`
14944
+ );
14809
14945
  }
14810
14946
  if (data.estimatedSavingsSeconds > 0) {
14811
- console.log(` Time saved: ~${chalk24.green(formatDuration(data.estimatedSavingsSeconds))}`);
14947
+ console.log(
14948
+ ` Time saved: ~${chalk24.green(formatDuration(data.estimatedSavingsSeconds))}`
14949
+ );
14812
14950
  }
14813
14951
  }
14814
14952
  console.log(chalk24.bold("\n Config Quality"));
@@ -14819,7 +14957,9 @@ function displayFullInsights(data, score) {
14819
14957
  const trendColor = trend.direction === "up" ? chalk24.green : trend.direction === "down" ? chalk24.red : chalk24.gray;
14820
14958
  const arrow = trend.direction === "up" ? "\u2191" : trend.direction === "down" ? "\u2193" : "\u2192";
14821
14959
  const sign = trend.delta > 0 ? "+" : "";
14822
- console.log(` Trend: ${trendColor(`${arrow} ${sign}${trend.delta} pts`)} ${chalk24.dim(`over ${trend.entries} checks`)}`);
14960
+ console.log(
14961
+ ` Trend: ${trendColor(`${arrow} ${sign}${trend.delta} pts`)} ${chalk24.dim(`over ${trend.entries} checks`)}`
14962
+ );
14823
14963
  }
14824
14964
  console.log("");
14825
14965
  }
@@ -14829,12 +14969,18 @@ async function insightsCommand(options) {
14829
14969
  const score = computeLocalScore(process.cwd(), readState()?.targetAgent);
14830
14970
  trackInsightsViewed(data.totalSessions, data.learningCount);
14831
14971
  if (options.json) {
14832
- console.log(JSON.stringify({
14833
- ...data,
14834
- tier: data.totalSessions === 0 ? "cold-start" : data.totalSessions < MIN_SESSIONS_FULL ? "early" : "full",
14835
- configScore: score.score,
14836
- configGrade: score.grade
14837
- }, null, 2));
14972
+ console.log(
14973
+ JSON.stringify(
14974
+ {
14975
+ ...data,
14976
+ tier: data.totalSessions === 0 ? "cold-start" : data.totalSessions < MIN_SESSIONS_FULL ? "early" : "full",
14977
+ configScore: score.score,
14978
+ configGrade: score.grade
14979
+ },
14980
+ null,
14981
+ 2
14982
+ )
14983
+ );
14838
14984
  return;
14839
14985
  }
14840
14986
  if (data.totalSessions === 0) {
@@ -14857,7 +15003,9 @@ async function sourcesListCommand() {
14857
15003
  const workspaces = getDetectedWorkspaces(dir);
14858
15004
  if (configSources.length === 0 && workspaces.length === 0) {
14859
15005
  console.log(chalk25.dim("\n No sources configured.\n"));
14860
- console.log(chalk25.dim(" Add a source: ") + chalk25.hex("#83D1EB")(`${resolveCaliber()} sources add <path>`));
15006
+ console.log(
15007
+ chalk25.dim(" Add a source: ") + chalk25.hex("#83D1EB")(`${displayCaliberName()} sources add <path>`)
15008
+ );
14861
15009
  console.log(chalk25.dim(" Or add to .caliber/sources.json manually.\n"));
14862
15010
  return;
14863
15011
  }
@@ -14869,7 +15017,9 @@ async function sourcesListCommand() {
14869
15017
  const status = exists ? chalk25.green("reachable") : chalk25.red("not found");
14870
15018
  const hasSummary = source.path && fs49.existsSync(path40.join(path40.resolve(dir, source.path), ".caliber", "summary.json"));
14871
15019
  console.log(` ${chalk25.bold(source.role || source.type)} ${chalk25.dim(sourcePath)}`);
14872
- console.log(` Type: ${source.type} Status: ${status}${hasSummary ? " " + chalk25.cyan("has summary.json") : ""}`);
15020
+ console.log(
15021
+ ` Type: ${source.type} Status: ${status}${hasSummary ? " " + chalk25.cyan("has summary.json") : ""}`
15022
+ );
14873
15023
  if (source.description) console.log(` ${chalk25.dim(source.description)}`);
14874
15024
  console.log("");
14875
15025
  }
@@ -14900,9 +15050,7 @@ async function sourcesAddCommand(sourcePath) {
14900
15050
  throw new Error("__exit__");
14901
15051
  }
14902
15052
  const existing = loadSourcesConfig(dir);
14903
- const alreadyConfigured = existing.some(
14904
- (s) => s.path && path40.resolve(dir, s.path) === absPath
14905
- );
15053
+ const alreadyConfigured = existing.some((s) => s.path && path40.resolve(dir, s.path) === absPath);
14906
15054
  if (alreadyConfigured) {
14907
15055
  console.log(chalk25.yellow(`
14908
15056
  Already configured: ${sourcePath}
@@ -14928,9 +15076,7 @@ async function sourcesAddCommand(sourcePath) {
14928
15076
  async function sourcesRemoveCommand(name) {
14929
15077
  const dir = process.cwd();
14930
15078
  const existing = loadSourcesConfig(dir);
14931
- const idx = existing.findIndex(
14932
- (s) => s.path?.includes(name) || s.role === name
14933
- );
15079
+ const idx = existing.findIndex((s) => s.path?.includes(name) || s.role === name);
14934
15080
  if (idx === -1) {
14935
15081
  console.log(chalk25.red(`
14936
15082
  Source not found: ${name}
@@ -14943,9 +15089,11 @@ async function sourcesRemoveCommand(name) {
14943
15089
  }
14944
15090
  const removed = existing.splice(idx, 1)[0];
14945
15091
  writeSourcesConfig(dir, existing);
14946
- console.log(chalk25.green(`
15092
+ console.log(
15093
+ chalk25.green(`
14947
15094
  \u2713 Removed ${removed.path || removed.url} (${removed.role || removed.type})
14948
- `));
15095
+ `)
15096
+ );
14949
15097
  }
14950
15098
 
14951
15099
  // src/commands/publish.ts
@@ -14959,7 +15107,9 @@ async function publishCommand() {
14959
15107
  const dir = process.cwd();
14960
15108
  const config = loadConfig();
14961
15109
  if (!config) {
14962
- console.log(chalk26.red("No LLM provider configured. Run ") + chalk26.hex("#83D1EB")(`${resolveCaliber()} config`) + chalk26.red(" first."));
15110
+ console.log(
15111
+ chalk26.red("No LLM provider configured. Run ") + chalk26.hex("#83D1EB")(`${displayCaliberName()} config`) + chalk26.red(" first.")
15112
+ );
14963
15113
  throw new Error("__exit__");
14964
15114
  }
14965
15115
  const spinner = ora7("Generating project summary...").start();
@@ -15002,7 +15152,9 @@ async function publishCommand() {
15002
15152
  spinner.succeed("Project summary published");
15003
15153
  console.log(` ${chalk26.green("\u2713")} ${path41.relative(dir, outputPath)}`);
15004
15154
  console.log(chalk26.dim("\n Other projects can now reference this repo as a source."));
15005
- console.log(chalk26.dim(" When they run `caliber init`, they'll read this summary automatically.\n"));
15155
+ console.log(
15156
+ chalk26.dim(" When they run `caliber init`, they'll read this summary automatically.\n")
15157
+ );
15006
15158
  } catch (err) {
15007
15159
  spinner.fail("Failed to generate summary");
15008
15160
  if (err instanceof Error && err.message === "__exit__") throw err;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rely-ai/caliber",
3
- "version": "1.48.2",
3
+ "version": "1.49.1",
4
4
  "description": "AI context infrastructure for coding agents — keeps CLAUDE.md, Cursor rules, and skills in sync as your codebase evolves",
5
5
  "type": "module",
6
6
  "bin": {