@rely-ai/caliber 1.48.1 → 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 +347 -167
  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,32 @@ 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;
4842
+ function tryWindowsDirectNodeInvocation(cmd) {
4843
+ if (process.platform !== "win32") return null;
4844
+ if (!/\.cmd$/i.test(cmd)) return null;
4845
+ const npmDir = path10.dirname(cmd);
4846
+ const binJs = path10.join(npmDir, "node_modules", "@rely-ai", "caliber", "dist", "bin.js");
4847
+ if (!fs11.existsSync(binJs)) return null;
4848
+ let nodePath;
4849
+ try {
4850
+ const out = execSync10("where node", {
4851
+ encoding: "utf-8",
4852
+ stdio: ["pipe", "pipe", "pipe"]
4853
+ }).trim();
4854
+ nodePath = pickExecutable(out);
4855
+ if (!nodePath) return null;
4856
+ } catch {
4857
+ return null;
4858
+ }
4859
+ const fwdNode = nodePath.replace(/\\/g, "/");
4860
+ const fwdBin = binJs.replace(/\\/g, "/");
4861
+ return `"${fwdNode}" "${fwdBin}"`;
4862
+ }
4821
4863
  function getPrecommitBlock() {
4822
4864
  const cmd = resolveCaliber();
4823
4865
  const npx = isNpxResolution();
@@ -4835,19 +4877,26 @@ function getPrecommitBlock() {
4835
4877
  invoke = cmd;
4836
4878
  }
4837
4879
  } else {
4838
- const cmdBash = bashPath(cmd);
4839
- if (path10.isAbsolute(cmd)) {
4840
- guard = `[ -x "${cmdBash}" ]`;
4880
+ const directNode = tryWindowsDirectNodeInvocation(cmd);
4881
+ if (directNode) {
4882
+ const nodeBin = directNode.match(/^"([^"]+)"/)?.[1] ?? "";
4883
+ guard = `[ -x "${nodeBin}" ]`;
4884
+ invoke = directNode;
4841
4885
  } else {
4842
- guard = `[ -x "${cmdBash}" ] || command -v "${cmdBash}" >/dev/null 2>&1`;
4886
+ const cmdBash = bashPath(cmd);
4887
+ if (path10.isAbsolute(cmd)) {
4888
+ guard = `[ -x "${cmdBash}" ]`;
4889
+ } else {
4890
+ guard = `[ -x "${cmdBash}" ] || command -v "${cmdBash}" >/dev/null 2>&1`;
4891
+ }
4892
+ invoke = `"${cmdBash}"`;
4843
4893
  }
4844
- invoke = `"${cmdBash}"`;
4845
4894
  }
4846
4895
  return `${PRECOMMIT_START}
4847
4896
  if ${guard}; then
4848
4897
  mkdir -p .caliber
4849
4898
  echo "\\033[2mcaliber: refreshing docs...\\033[0m"
4850
- ${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
4851
4900
  ${invoke} learn finalize 2>>.caliber/refresh-hook.log || true
4852
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
4853
4902
  fi
@@ -4872,19 +4921,29 @@ function isPreCommitHookInstalled() {
4872
4921
  const hookPath = getPreCommitPath();
4873
4922
  if (!hookPath || !fs11.existsSync(hookPath)) return false;
4874
4923
  const content = fs11.readFileSync(hookPath, "utf-8");
4875
- return content.includes(PRECOMMIT_START);
4924
+ return PRECOMMIT_ANY_VERSION_START_RE.test(content);
4876
4925
  }
4877
4926
  function installPreCommitHook() {
4878
- if (isPreCommitHookInstalled()) {
4879
- return { installed: false, alreadyInstalled: true };
4880
- }
4881
4927
  const hookPath = getPreCommitPath();
4882
- if (!hookPath) return { installed: false, alreadyInstalled: false };
4928
+ if (!hookPath) {
4929
+ return { installed: false, alreadyInstalled: false, upgraded: false };
4930
+ }
4883
4931
  const hooksDir = path10.dirname(hookPath);
4884
4932
  if (!fs11.existsSync(hooksDir)) fs11.mkdirSync(hooksDir, { recursive: true });
4885
- let content = "";
4886
- if (fs11.existsSync(hookPath)) {
4887
- 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) {
4888
4947
  if (!content.endsWith("\n")) content += "\n";
4889
4948
  content += "\n" + getPrecommitBlock() + "\n";
4890
4949
  } else {
@@ -4892,7 +4951,7 @@ function installPreCommitHook() {
4892
4951
  }
4893
4952
  fs11.writeFileSync(hookPath, content);
4894
4953
  fs11.chmodSync(hookPath, 493);
4895
- return { installed: true, alreadyInstalled: false };
4954
+ return { installed: true, alreadyInstalled: false, upgraded: false };
4896
4955
  }
4897
4956
  function removePreCommitHook() {
4898
4957
  const hookPath = getPreCommitPath();
@@ -4900,11 +4959,10 @@ function removePreCommitHook() {
4900
4959
  return { removed: false, notFound: true };
4901
4960
  }
4902
4961
  let content = fs11.readFileSync(hookPath, "utf-8");
4903
- if (!content.includes(PRECOMMIT_START)) {
4962
+ if (!PRECOMMIT_ANY_VERSION_START_RE.test(content)) {
4904
4963
  return { removed: false, notFound: true };
4905
4964
  }
4906
- const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
4907
- content = content.replace(regex, "\n");
4965
+ content = content.replace(PRECOMMIT_ANY_VERSION_BLOCK_RE, "\n").replace(/\n{3,}/g, "\n\n");
4908
4966
  if (content.trim() === "#!/bin/sh" || content.trim() === "") {
4909
4967
  fs11.unlinkSync(hookPath);
4910
4968
  } else {
@@ -7717,10 +7775,11 @@ function detectGitDrift(dir) {
7717
7775
  }
7718
7776
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".cursor/rules"];
7719
7777
  try {
7720
- const headTimestamp = execSync12(
7721
- "git log -1 --format=%ct HEAD",
7722
- { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7723
- ).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();
7724
7783
  const headTime = parseInt(headTimestamp, 10) * 1e3;
7725
7784
  for (const file of configFiles) {
7726
7785
  const filePath = join5(dir, file);
@@ -7728,7 +7787,11 @@ function detectGitDrift(dir) {
7728
7787
  try {
7729
7788
  const mtime = statSync(filePath).mtime.getTime();
7730
7789
  if (mtime > headTime) {
7731
- return { commitsSinceConfigUpdate: 0, lastConfigCommit: "uncommitted (recently modified)", isGitRepo: true };
7790
+ return {
7791
+ commitsSinceConfigUpdate: 0,
7792
+ lastConfigCommit: "uncommitted (recently modified)",
7793
+ isGitRepo: true
7794
+ };
7732
7795
  }
7733
7796
  } catch {
7734
7797
  }
@@ -7738,19 +7801,20 @@ function detectGitDrift(dir) {
7738
7801
  let latestConfigCommitHash = null;
7739
7802
  for (const file of configFiles) {
7740
7803
  try {
7741
- const hash = execSync12(
7742
- `git log -1 --format=%H -- "${file}"`,
7743
- { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7744
- ).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();
7745
7809
  if (!hash) continue;
7746
7810
  if (!latestConfigCommitHash) {
7747
7811
  latestConfigCommitHash = hash;
7748
7812
  } else {
7749
7813
  try {
7750
- execSync12(
7751
- `git merge-base --is-ancestor ${latestConfigCommitHash} ${hash}`,
7752
- { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }
7753
- );
7814
+ execSync12(`git merge-base --is-ancestor ${latestConfigCommitHash} ${hash}`, {
7815
+ cwd: dir,
7816
+ stdio: ["pipe", "pipe", "pipe"]
7817
+ });
7754
7818
  latestConfigCommitHash = hash;
7755
7819
  } catch {
7756
7820
  }
@@ -7762,22 +7826,28 @@ function detectGitDrift(dir) {
7762
7826
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: true };
7763
7827
  }
7764
7828
  try {
7765
- const countStr = execSync12(
7766
- `git rev-list --count ${latestConfigCommitHash}..HEAD`,
7767
- { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7768
- ).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();
7769
7834
  const commitsSince = parseInt(countStr, 10) || 0;
7770
- const lastDate = execSync12(
7771
- `git log -1 --format=%ci ${latestConfigCommitHash}`,
7772
- { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7773
- ).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();
7774
7840
  return {
7775
7841
  commitsSinceConfigUpdate: commitsSince,
7776
7842
  lastConfigCommit: lastDate,
7777
7843
  isGitRepo: true
7778
7844
  };
7779
7845
  } catch {
7780
- return { commitsSinceConfigUpdate: 0, lastConfigCommit: latestConfigCommitHash, isGitRepo: true };
7846
+ return {
7847
+ commitsSinceConfigUpdate: 0,
7848
+ lastConfigCommit: latestConfigCommitHash,
7849
+ isGitRepo: true
7850
+ };
7781
7851
  }
7782
7852
  }
7783
7853
  function checkAccuracy(dir) {
@@ -7827,10 +7897,13 @@ function checkAccuracy(dir) {
7827
7897
  earnedPoints: driftPoints,
7828
7898
  passed: drift.commitsSinceConfigUpdate <= 15 || !drift.isGitRepo,
7829
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`,
7830
- 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,
7831
7901
  fix: drift.commitsSinceConfigUpdate > 15 ? {
7832
7902
  action: "refresh_config",
7833
- data: { commitsSince: drift.commitsSinceConfigUpdate, lastConfigCommit: drift.lastConfigCommit },
7903
+ data: {
7904
+ commitsSince: drift.commitsSinceConfigUpdate,
7905
+ lastConfigCommit: drift.lastConfigCommit
7906
+ },
7834
7907
  instruction: `Config is ${drift.commitsSinceConfigUpdate} commits behind. Review recent changes and update config accordingly.`
7835
7908
  } : void 0
7836
7909
  });
@@ -7845,10 +7918,11 @@ import { join as join6 } from "path";
7845
7918
  function getCommitsSinceConfigUpdate(dir) {
7846
7919
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules"];
7847
7920
  try {
7848
- const headTimestamp = execSync13(
7849
- "git log -1 --format=%ct HEAD",
7850
- { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7851
- ).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();
7852
7926
  const headTime = parseInt(headTimestamp, 10) * 1e3;
7853
7927
  for (const file of configFiles) {
7854
7928
  const filePath = join6(dir, file);
@@ -7865,15 +7939,17 @@ function getCommitsSinceConfigUpdate(dir) {
7865
7939
  }
7866
7940
  for (const file of configFiles) {
7867
7941
  try {
7868
- const hash = execSync13(
7869
- `git log -1 --format=%H -- "${file}"`,
7870
- { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7871
- ).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();
7872
7947
  if (hash) {
7873
- const countStr = execSync13(
7874
- `git rev-list --count ${hash}..HEAD`,
7875
- { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
7876
- ).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();
7877
7953
  return parseInt(countStr, 10) || 0;
7878
7954
  }
7879
7955
  } catch {
@@ -7902,7 +7978,7 @@ function checkFreshness(dir) {
7902
7978
  earnedPoints: freshnessPoints,
7903
7979
  passed: freshnessPoints >= 3,
7904
7980
  detail: freshnessDetail,
7905
- 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,
7906
7982
  fix: commitsSince !== null && freshnessPoints < 3 ? {
7907
7983
  action: "refresh_config",
7908
7984
  data: { commitsSince },
@@ -8054,11 +8130,11 @@ function checkBonus(dir) {
8054
8130
  earnedPoints: hasHooks ? POINTS_HOOKS : 0,
8055
8131
  passed: hasHooks,
8056
8132
  detail: hasHooks ? hookSources.join(", ") : "No hooks configured",
8057
- 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`,
8058
8134
  fix: hasHooks ? void 0 : {
8059
8135
  action: "install_hooks",
8060
8136
  data: {},
8061
- 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.`
8062
8138
  }
8063
8139
  });
8064
8140
  const agentsMdExists = existsSync6(join7(dir, "AGENTS.md"));
@@ -8123,7 +8199,7 @@ function checkBonus(dir) {
8123
8199
  earnedPoints: hasLearned ? POINTS_LEARNED_CONTENT : 0,
8124
8200
  passed: hasLearned,
8125
8201
  detail: hasLearned ? "Session learnings found in CALIBER_LEARNINGS.md" : "No learned content",
8126
- 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\``
8127
8203
  });
8128
8204
  const configContent = (() => {
8129
8205
  const parts = [];
@@ -8170,7 +8246,7 @@ function checkSources(dir) {
8170
8246
  earnedPoints: hasSources ? POINTS_SOURCES_CONFIGURED : 0,
8171
8247
  passed: hasSources,
8172
8248
  detail: hasSources ? `${configSources.length} source${configSources.length === 1 ? "" : "s"} configured` : "No external sources configured",
8173
- 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`
8174
8250
  });
8175
8251
  if (hasSources) {
8176
8252
  const claudeMd = readFileOrNull(join8(dir, "CLAUDE.md"));
@@ -8188,7 +8264,7 @@ function checkSources(dir) {
8188
8264
  earnedPoints: referenced ? POINTS_SOURCES_REFERENCED : 0,
8189
8265
  passed: referenced,
8190
8266
  detail: referenced ? "At least one source is referenced in CLAUDE.md" : "No configured sources are mentioned in CLAUDE.md",
8191
- 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`
8192
8268
  });
8193
8269
  }
8194
8270
  return checks;
@@ -8299,7 +8375,14 @@ var CATEGORY_LABELS = {
8299
8375
  freshness: { icon: "\u{1F6E1}\uFE0F", label: "FRESHNESS & SAFETY" },
8300
8376
  bonus: { icon: "\u2B50", label: "BONUS" }
8301
8377
  };
8302
- 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
+ ];
8303
8386
  function gradeColor(grade) {
8304
8387
  switch (grade) {
8305
8388
  case "A":
@@ -8368,7 +8451,9 @@ function displayScore(result) {
8368
8451
  console.log("");
8369
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"));
8370
8453
  console.log("");
8371
- 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
+ );
8372
8457
  console.log(` ${progressBar(result.score, result.maxScore)}`);
8373
8458
  console.log(chalk3.dim(` Target: ${agentLabel}`));
8374
8459
  console.log("");
@@ -8391,7 +8476,11 @@ function displayScore(result) {
8391
8476
  formatTopImprovements(result.checks);
8392
8477
  }
8393
8478
  function formatTopImprovements(checks) {
8394
- 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);
8395
8484
  if (improvable.length === 0) return;
8396
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"));
8397
8486
  console.log("");
@@ -8422,8 +8511,12 @@ function displayScoreSummary(result) {
8422
8511
  }
8423
8512
  const remaining = failing.length - shown.length;
8424
8513
  const moreText = remaining > 0 ? ` (+${remaining} more)` : "";
8425
- console.log(chalk3.dim(`
8426
- 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
+ );
8427
8520
  }
8428
8521
  console.log("");
8429
8522
  }
@@ -8439,7 +8532,9 @@ function displayScoreDelta(before, after) {
8439
8532
  console.log(
8440
8533
  ` Score: ${beforeGc(`${before.score}`)} ${chalk3.gray("\u2192")} ${afterGc(`${after.score}`)} ${deltaColor(deltaStr + " pts")} ${beforeGc(before.grade)} ${chalk3.gray("\u2192")} ${afterGc(after.grade)}`
8441
8534
  );
8442
- 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
+ );
8443
8538
  console.log("");
8444
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"));
8445
8540
  console.log("");
@@ -9121,7 +9216,7 @@ async function querySkills(query) {
9121
9216
  console.log("");
9122
9217
  console.log(
9123
9218
  chalk5.dim(
9124
- ` Install with: ${resolveCaliber()} skills --install ${available.map((r) => r.slug).join(",")}`
9219
+ ` Install with: ${displayCaliberName()} skills --install ${available.map((r) => r.slug).join(",")}`
9125
9220
  )
9126
9221
  );
9127
9222
  console.log("");
@@ -11109,7 +11204,7 @@ function log(verbose, ...args) {
11109
11204
  async function initCommand(options) {
11110
11205
  const brand = chalk14.hex("#EB9D83");
11111
11206
  const title = chalk14.hex("#83D1EB");
11112
- const bin = resolveCaliber();
11207
+ const bin = displayCaliberName();
11113
11208
  const firstRun = isFirstRun(process.cwd());
11114
11209
  if (firstRun) {
11115
11210
  console.log(
@@ -11250,6 +11345,8 @@ async function initCommand(options) {
11250
11345
  const hookResult = installPreCommitHook();
11251
11346
  if (hookResult.installed) {
11252
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`);
11253
11350
  } else if (hookResult.alreadyInstalled) {
11254
11351
  console.log(` ${chalk14.green("\u2713")} Pre-commit hook \u2014 active`);
11255
11352
  }
@@ -11949,24 +12046,34 @@ async function statusCommand(options) {
11949
12046
  const config = loadConfig();
11950
12047
  const manifest = readManifest();
11951
12048
  if (options.json) {
11952
- console.log(JSON.stringify({
11953
- configured: !!config,
11954
- provider: config?.provider,
11955
- model: config?.model,
11956
- manifest
11957
- }, 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
+ );
11958
12061
  return;
11959
12062
  }
11960
12063
  console.log(chalk16.bold("\nCaliber Status\n"));
11961
12064
  if (config) {
11962
12065
  console.log(` LLM: ${chalk16.green(config.provider)} (${config.model})`);
11963
12066
  } else {
11964
- const bin = resolveCaliber();
11965
- 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
+ );
11966
12071
  }
11967
12072
  if (!manifest) {
11968
12073
  console.log(` Config: ${chalk16.dim("No config applied")}`);
11969
- 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
+ );
11970
12077
  return;
11971
12078
  }
11972
12079
  console.log(` Files managed: ${chalk16.cyan(manifest.entries.length.toString())}`);
@@ -11986,15 +12093,19 @@ init_review();
11986
12093
  init_config();
11987
12094
  init_resolve_caliber();
11988
12095
  async function regenerateCommand(options) {
11989
- const bin = resolveCaliber();
12096
+ const bin = displayCaliberName();
11990
12097
  const config = loadConfig();
11991
12098
  if (!config) {
11992
- 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
+ );
11993
12102
  throw new Error("__exit__");
11994
12103
  }
11995
12104
  const manifest = readManifest();
11996
12105
  if (!manifest) {
11997
- 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
+ );
11998
12109
  throw new Error("__exit__");
11999
12110
  }
12000
12111
  const targetAgent = readState()?.targetAgent ?? ["claude", "cursor"];
@@ -12009,27 +12120,24 @@ async function regenerateCommand(options) {
12009
12120
  return;
12010
12121
  }
12011
12122
  const genSpinner = ora5("Regenerating config...").start();
12012
- const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, { showElapsedTime: true });
12123
+ const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, {
12124
+ showElapsedTime: true
12125
+ });
12013
12126
  genMessages.start();
12014
12127
  let generatedSetup = null;
12015
12128
  try {
12016
- const result = await generateSetup(
12017
- fingerprint,
12018
- targetAgent,
12019
- void 0,
12020
- {
12021
- onStatus: (status) => {
12022
- genMessages.handleServerStatus(status);
12023
- },
12024
- onComplete: (setup) => {
12025
- generatedSetup = setup;
12026
- },
12027
- onError: (error) => {
12028
- genMessages.stop();
12029
- genSpinner.fail(`Generation error: ${error}`);
12030
- }
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}`);
12031
12139
  }
12032
- );
12140
+ });
12033
12141
  if (!generatedSetup) generatedSetup = result.setup;
12034
12142
  } catch (err) {
12035
12143
  genMessages.stop();
@@ -12047,9 +12155,13 @@ async function regenerateCommand(options) {
12047
12155
  const setupFiles = collectSetupFiles(generatedSetup, targetAgent);
12048
12156
  const staged = stageFiles(setupFiles, process.cwd());
12049
12157
  const totalChanges = staged.newFiles + staged.modifiedFiles;
12050
- console.log(chalk17.dim(`
12158
+ console.log(
12159
+ chalk17.dim(
12160
+ `
12051
12161
  ${chalk17.green(`${staged.newFiles} new`)} / ${chalk17.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}
12052
- `));
12162
+ `
12163
+ )
12164
+ );
12053
12165
  if (totalChanges === 0) {
12054
12166
  console.log(chalk17.dim(" No changes needed \u2014 your configs are already up to date.\n"));
12055
12167
  cleanupStaging();
@@ -12110,21 +12222,33 @@ async function regenerateCommand(options) {
12110
12222
  const afterScore = computeLocalScore(process.cwd(), targetAgent);
12111
12223
  if (afterScore.score < baselineScore.score) {
12112
12224
  console.log("");
12113
- 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
+ );
12114
12230
  try {
12115
12231
  const { restored, removed } = undoSetup();
12116
12232
  if (restored.length > 0 || removed.length > 0) {
12117
- 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
+ );
12118
12238
  }
12119
12239
  } catch {
12120
12240
  }
12121
- 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
+ );
12122
12244
  return;
12123
12245
  }
12124
12246
  displayScoreDelta(baselineScore, afterScore);
12125
12247
  trackRegenerateCompleted(action, Date.now());
12126
12248
  console.log(chalk17.bold.green(" Regeneration complete!"));
12127
- 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
+ );
12128
12252
  }
12129
12253
 
12130
12254
  // src/commands/score.ts
@@ -12239,7 +12363,7 @@ async function scoreCommand(options) {
12239
12363
  displayScore(result);
12240
12364
  const separator = chalk18.gray(" " + "\u2500".repeat(53));
12241
12365
  console.log(separator);
12242
- const bin = resolveCaliber();
12366
+ const bin = displayCaliberName();
12243
12367
  const failing = result.checks.filter((c) => !c.passed && c.maxPoints > 0).sort((a, b) => b.maxPoints - b.earnedPoints - (a.maxPoints - a.earnedPoints));
12244
12368
  if (result.score < 70 && failing.length > 0) {
12245
12369
  const topFix = failing[0];
@@ -13200,7 +13324,7 @@ async function refreshCommand(options) {
13200
13324
  if (!config) {
13201
13325
  if (quiet) return;
13202
13326
  console.log(
13203
- 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.")
13204
13328
  );
13205
13329
  throw new Error("__exit__");
13206
13330
  }
@@ -13308,7 +13432,9 @@ async function hooksCommand(options) {
13308
13432
  if (options.install) {
13309
13433
  for (const hook of HOOKS) {
13310
13434
  const result = hook.install();
13311
- if (result.alreadyInstalled) {
13435
+ if (result.upgraded) {
13436
+ console.log(chalk20.green(" \u2713") + ` ${hook.label} upgraded to latest version`);
13437
+ } else if (result.alreadyInstalled) {
13312
13438
  console.log(chalk20.dim(` ${hook.label} already enabled.`));
13313
13439
  } else {
13314
13440
  console.log(chalk20.green(" \u2713") + ` ${hook.label} enabled`);
@@ -14248,7 +14374,7 @@ async function learnFinalizeCommand(options) {
14248
14374
  if (isAuto) return;
14249
14375
  console.log(
14250
14376
  chalk23.yellow(
14251
- `caliber: no LLM provider configured \u2014 run \`${resolveCaliber()} config\` first`
14377
+ `caliber: no LLM provider configured \u2014 run \`${displayCaliberName()} config\` first`
14252
14378
  )
14253
14379
  );
14254
14380
  clearSession();
@@ -14414,7 +14540,7 @@ async function learnFinalizeCommand(options) {
14414
14540
  if (staleLearnings.length > 0 && !isAuto) {
14415
14541
  console.log(
14416
14542
  chalk23.yellow(
14417
- `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`
14418
14544
  )
14419
14545
  );
14420
14546
  }
@@ -14471,7 +14597,7 @@ async function learnInstallCommand() {
14471
14597
  if (!fs48.existsSync(".claude") && !fs48.existsSync(".cursor")) {
14472
14598
  console.log(chalk23.yellow("No .claude/ or .cursor/ directory found."));
14473
14599
  console.log(
14474
- chalk23.dim(` Run \`${resolveCaliber()} init\` first, or create the directory manually.`)
14600
+ chalk23.dim(` Run \`${displayCaliberName()} init\` first, or create the directory manually.`)
14475
14601
  );
14476
14602
  return;
14477
14603
  }
@@ -14519,7 +14645,7 @@ async function learnStatusCommand() {
14519
14645
  }
14520
14646
  if (!claudeInstalled && !cursorInstalled) {
14521
14647
  console.log(
14522
- chalk23.dim(` Run \`${resolveCaliber()} learn install\` to enable session learning.`)
14648
+ chalk23.dim(` Run \`${displayCaliberName()} learn install\` to enable session learning.`)
14523
14649
  );
14524
14650
  }
14525
14651
  console.log();
@@ -14575,7 +14701,9 @@ function getAllLearnings() {
14575
14701
  async function learnListCommand(options) {
14576
14702
  const items = getAllLearnings();
14577
14703
  if (items.length === 0) {
14578
- 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
+ );
14579
14707
  return;
14580
14708
  }
14581
14709
  const roiStats = options?.verbose ? readROIStats() : null;
@@ -14606,7 +14734,7 @@ async function learnDeleteCommand(indexStr) {
14606
14734
  if (isNaN(index) || index < 1) {
14607
14735
  console.log(
14608
14736
  chalk23.red(
14609
- `Invalid index: "${indexStr}". Use a number from \`${resolveCaliber()} learn list\`.`
14737
+ `Invalid index: "${indexStr}". Use a number from \`${displayCaliberName()} learn list\`.`
14610
14738
  )
14611
14739
  );
14612
14740
  return;
@@ -14717,13 +14845,23 @@ function displayColdStart(score) {
14717
14845
  const hooksInstalled = areLearningHooksInstalled() || areCursorLearningHooksInstalled();
14718
14846
  if (!hooksInstalled) {
14719
14847
  console.log(chalk24.yellow(" Learning hooks not installed."));
14720
- console.log(chalk24.dim(" Session learning captures patterns from your AI coding sessions \u2014 what"));
14721
- console.log(chalk24.dim(" fails, what works, corrections you make \u2014 so your agents improve over time.\n"));
14722
- 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
+ );
14723
14857
  } else {
14724
14858
  console.log(chalk24.dim(" Learning hooks are active. Use your AI agent and insights"));
14725
14859
  console.log(chalk24.dim(" will appear automatically after each session.\n"));
14726
- 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
+ );
14727
14865
  }
14728
14866
  console.log(chalk24.dim(`
14729
14867
  Config score: ${score.score}/100 (${score.grade})`));
@@ -14732,20 +14870,32 @@ function displayColdStart(score) {
14732
14870
  function displayEarlyData(data, score) {
14733
14871
  console.log(chalk24.bold("\n Agent Insights") + chalk24.yellow(" (early data)\n"));
14734
14872
  const remaining = MIN_SESSIONS_FULL - data.totalSessions;
14735
- console.log(chalk24.dim(` ${data.totalSessions}/${MIN_SESSIONS_FULL} sessions tracked \u2014 ${remaining} more for full insights.
14736
- `));
14873
+ console.log(
14874
+ chalk24.dim(
14875
+ ` ${data.totalSessions}/${MIN_SESSIONS_FULL} sessions tracked \u2014 ${remaining} more for full insights.
14876
+ `
14877
+ )
14878
+ );
14737
14879
  console.log(` Sessions tracked: ${chalk24.cyan(String(data.totalSessions))}`);
14738
14880
  console.log(` Learnings accumulated: ${chalk24.cyan(String(data.learningCount))}`);
14739
14881
  if (data.totalWasteTokens > 0) {
14740
- console.log(` Waste captured: ${chalk24.cyan(data.totalWasteTokens.toLocaleString())} tokens`);
14882
+ console.log(
14883
+ ` Waste captured: ${chalk24.cyan(data.totalWasteTokens.toLocaleString())} tokens`
14884
+ );
14741
14885
  }
14742
14886
  if (data.failureRateImprovement !== null && data.failureRateImprovement > 0) {
14743
- 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
+ );
14744
14890
  } else if (data.totalSessions > 0 && data.failureRateImprovement === null) {
14745
- 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
+ );
14746
14894
  }
14747
14895
  if (data.taskSuccessRate !== null) {
14748
- 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
+ );
14749
14899
  }
14750
14900
  console.log(` Config score: ${chalk24.cyan(`${score.score}/100`)} (${score.grade})`);
14751
14901
  console.log("");
@@ -14755,32 +14905,48 @@ function displayFullInsights(data, score) {
14755
14905
  console.log(chalk24.bold(" Agent Health"));
14756
14906
  if (data.taskSuccessRate !== null) {
14757
14907
  const color = data.taskSuccessRate >= 80 ? chalk24.green : data.taskSuccessRate >= 60 ? chalk24.yellow : chalk24.red;
14758
- 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
+ );
14759
14911
  if (data.taskCorrectionCount > 0) {
14760
- 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
+ );
14761
14915
  }
14762
14916
  }
14763
14917
  console.log(` Sessions tracked: ${chalk24.cyan(String(data.totalSessions))}`);
14764
14918
  console.log(chalk24.bold("\n Learning Impact"));
14765
14919
  console.log(` Learnings active: ${chalk24.cyan(String(data.learningCount))}`);
14766
14920
  if (data.failureRateWith !== null && data.failureRateWithout !== null) {
14767
- 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
+ );
14768
14924
  if (data.failureRateImprovement !== null && data.failureRateImprovement > 0) {
14769
- console.log(` Improvement: ${chalk24.green(`${data.failureRateImprovement}%`)} fewer failures`);
14925
+ console.log(
14926
+ ` Improvement: ${chalk24.green(`${data.failureRateImprovement}%`)} fewer failures`
14927
+ );
14770
14928
  } else if (data.failureRateImprovement === null) {
14771
- 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
+ );
14772
14932
  }
14773
14933
  }
14774
14934
  if (data.totalWasteTokens > 0 || data.estimatedSavingsTokens > 0) {
14775
14935
  console.log(chalk24.bold("\n Efficiency"));
14776
14936
  if (data.totalWasteTokens > 0) {
14777
- console.log(` Waste captured: ${chalk24.cyan(data.totalWasteTokens.toLocaleString())} tokens`);
14937
+ console.log(
14938
+ ` Waste captured: ${chalk24.cyan(data.totalWasteTokens.toLocaleString())} tokens`
14939
+ );
14778
14940
  }
14779
14941
  if (data.estimatedSavingsTokens > 0) {
14780
- console.log(` Estimated savings: ~${chalk24.green(data.estimatedSavingsTokens.toLocaleString())} tokens`);
14942
+ console.log(
14943
+ ` Estimated savings: ~${chalk24.green(data.estimatedSavingsTokens.toLocaleString())} tokens`
14944
+ );
14781
14945
  }
14782
14946
  if (data.estimatedSavingsSeconds > 0) {
14783
- console.log(` Time saved: ~${chalk24.green(formatDuration(data.estimatedSavingsSeconds))}`);
14947
+ console.log(
14948
+ ` Time saved: ~${chalk24.green(formatDuration(data.estimatedSavingsSeconds))}`
14949
+ );
14784
14950
  }
14785
14951
  }
14786
14952
  console.log(chalk24.bold("\n Config Quality"));
@@ -14791,7 +14957,9 @@ function displayFullInsights(data, score) {
14791
14957
  const trendColor = trend.direction === "up" ? chalk24.green : trend.direction === "down" ? chalk24.red : chalk24.gray;
14792
14958
  const arrow = trend.direction === "up" ? "\u2191" : trend.direction === "down" ? "\u2193" : "\u2192";
14793
14959
  const sign = trend.delta > 0 ? "+" : "";
14794
- 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
+ );
14795
14963
  }
14796
14964
  console.log("");
14797
14965
  }
@@ -14801,12 +14969,18 @@ async function insightsCommand(options) {
14801
14969
  const score = computeLocalScore(process.cwd(), readState()?.targetAgent);
14802
14970
  trackInsightsViewed(data.totalSessions, data.learningCount);
14803
14971
  if (options.json) {
14804
- console.log(JSON.stringify({
14805
- ...data,
14806
- tier: data.totalSessions === 0 ? "cold-start" : data.totalSessions < MIN_SESSIONS_FULL ? "early" : "full",
14807
- configScore: score.score,
14808
- configGrade: score.grade
14809
- }, 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
+ );
14810
14984
  return;
14811
14985
  }
14812
14986
  if (data.totalSessions === 0) {
@@ -14829,7 +15003,9 @@ async function sourcesListCommand() {
14829
15003
  const workspaces = getDetectedWorkspaces(dir);
14830
15004
  if (configSources.length === 0 && workspaces.length === 0) {
14831
15005
  console.log(chalk25.dim("\n No sources configured.\n"));
14832
- 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
+ );
14833
15009
  console.log(chalk25.dim(" Or add to .caliber/sources.json manually.\n"));
14834
15010
  return;
14835
15011
  }
@@ -14841,7 +15017,9 @@ async function sourcesListCommand() {
14841
15017
  const status = exists ? chalk25.green("reachable") : chalk25.red("not found");
14842
15018
  const hasSummary = source.path && fs49.existsSync(path40.join(path40.resolve(dir, source.path), ".caliber", "summary.json"));
14843
15019
  console.log(` ${chalk25.bold(source.role || source.type)} ${chalk25.dim(sourcePath)}`);
14844
- 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
+ );
14845
15023
  if (source.description) console.log(` ${chalk25.dim(source.description)}`);
14846
15024
  console.log("");
14847
15025
  }
@@ -14872,9 +15050,7 @@ async function sourcesAddCommand(sourcePath) {
14872
15050
  throw new Error("__exit__");
14873
15051
  }
14874
15052
  const existing = loadSourcesConfig(dir);
14875
- const alreadyConfigured = existing.some(
14876
- (s) => s.path && path40.resolve(dir, s.path) === absPath
14877
- );
15053
+ const alreadyConfigured = existing.some((s) => s.path && path40.resolve(dir, s.path) === absPath);
14878
15054
  if (alreadyConfigured) {
14879
15055
  console.log(chalk25.yellow(`
14880
15056
  Already configured: ${sourcePath}
@@ -14900,9 +15076,7 @@ async function sourcesAddCommand(sourcePath) {
14900
15076
  async function sourcesRemoveCommand(name) {
14901
15077
  const dir = process.cwd();
14902
15078
  const existing = loadSourcesConfig(dir);
14903
- const idx = existing.findIndex(
14904
- (s) => s.path?.includes(name) || s.role === name
14905
- );
15079
+ const idx = existing.findIndex((s) => s.path?.includes(name) || s.role === name);
14906
15080
  if (idx === -1) {
14907
15081
  console.log(chalk25.red(`
14908
15082
  Source not found: ${name}
@@ -14915,9 +15089,11 @@ async function sourcesRemoveCommand(name) {
14915
15089
  }
14916
15090
  const removed = existing.splice(idx, 1)[0];
14917
15091
  writeSourcesConfig(dir, existing);
14918
- console.log(chalk25.green(`
15092
+ console.log(
15093
+ chalk25.green(`
14919
15094
  \u2713 Removed ${removed.path || removed.url} (${removed.role || removed.type})
14920
- `));
15095
+ `)
15096
+ );
14921
15097
  }
14922
15098
 
14923
15099
  // src/commands/publish.ts
@@ -14931,7 +15107,9 @@ async function publishCommand() {
14931
15107
  const dir = process.cwd();
14932
15108
  const config = loadConfig();
14933
15109
  if (!config) {
14934
- 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
+ );
14935
15113
  throw new Error("__exit__");
14936
15114
  }
14937
15115
  const spinner = ora7("Generating project summary...").start();
@@ -14974,7 +15152,9 @@ async function publishCommand() {
14974
15152
  spinner.succeed("Project summary published");
14975
15153
  console.log(` ${chalk26.green("\u2713")} ${path41.relative(dir, outputPath)}`);
14976
15154
  console.log(chalk26.dim("\n Other projects can now reference this repo as a source."));
14977
- 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
+ );
14978
15158
  } catch (err) {
14979
15159
  spinner.fail("Failed to generate summary");
14980
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.1",
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": {