@rely-ai/caliber 1.40.0 → 1.40.2

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 (3) hide show
  1. package/README.md +13 -1
  2. package/dist/bin.js +73 -20
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -51,7 +51,7 @@ Requires **Node.js >= 20**.
51
51
  npx @rely-ai/caliber bootstrap
52
52
  ```
53
53
 
54
- Then, in your next Claude Code or Cursor chat session, type:
54
+ Then, in your terminal (not the IDE chat), start a Claude Code or Cursor CLI session and type:
55
55
 
56
56
  > **/setup-caliber**
57
57
 
@@ -61,6 +61,18 @@ Your agent detects your stack, generates tailored configs for every platform you
61
61
 
62
62
  > **Your code stays on your machine.** Bootstrap is 100% local — no LLM calls, no code sent anywhere. Generation uses your own AI subscription or API key. Caliber never sees your code.
63
63
 
64
+ <details>
65
+ <summary><strong>Windows Users</strong></summary>
66
+
67
+ Caliber works on Windows with a few notes:
68
+
69
+ - **Run from your terminal** (PowerShell, CMD, or Git Bash) — not from inside an IDE chat window. Open a terminal, `cd` into your project folder, then run `npx @rely-ai/caliber bootstrap`.
70
+ - **Git Bash is recommended.** Caliber's pre-commit hooks and auto-sync scripts use shell syntax. Git for Windows includes Git Bash, which handles this automatically. If you only use PowerShell, hooks may be skipped silently.
71
+ - **Cursor Agent CLI:** If prompted to install it, download from [cursor.com/downloads](https://www.cursor.com/downloads) instead of the `curl | bash` command shown on macOS/Linux. Then run `agent login` in your terminal to authenticate.
72
+ - **One terminal at a time.** Avoid running Caliber from multiple terminals simultaneously — this can cause conflicting state and unexpected provider detection.
73
+
74
+ </details>
75
+
64
76
  ## Audits first, writes second
65
77
 
66
78
  Caliber never overwrites your existing configs without asking. The workflow mirrors code review:
package/dist/bin.js CHANGED
@@ -858,7 +858,7 @@ function openDiffsInEditor(editor, files) {
858
858
  for (const file of files) {
859
859
  try {
860
860
  const leftPath = file.originalPath ?? getEmptyFilePath(file.proposedPath);
861
- if (IS_WINDOWS3) {
861
+ if (IS_WINDOWS4) {
862
862
  const quote = (s) => `"${s}"`;
863
863
  spawn3([cmd, "--diff", quote(leftPath), quote(file.proposedPath)].join(" "), { shell: true, stdio: "ignore", detached: true }).unref();
864
864
  } else {
@@ -869,11 +869,11 @@ function openDiffsInEditor(editor, files) {
869
869
  }
870
870
  }
871
871
  }
872
- var IS_WINDOWS3, DIFF_TEMP_DIR;
872
+ var IS_WINDOWS4, DIFF_TEMP_DIR;
873
873
  var init_editor = __esm({
874
874
  "src/utils/editor.ts"() {
875
875
  "use strict";
876
- IS_WINDOWS3 = process.platform === "win32";
876
+ IS_WINDOWS4 = process.platform === "win32";
877
877
  DIFF_TEMP_DIR = path25.join(os6.tmpdir(), "caliber-diff");
878
878
  }
879
879
  });
@@ -2363,7 +2363,7 @@ import os3 from "os";
2363
2363
  // src/llm/seat-based-errors.ts
2364
2364
  init_resolve_caliber();
2365
2365
  var ERROR_PATTERNS = [
2366
- { pattern: /not logged in|not authenticated|login required|unauthorized/i, message: "Authentication required. Run the login command for your provider to re-authenticate." },
2366
+ { pattern: /not logged in|not authenticated|login required|unauthorized/i, message: "Not logged in. Run the login command for your provider to re-authenticate." },
2367
2367
  { pattern: /rate limit|too many requests|429/i, message: "Rate limit exceeded. Retrying..." },
2368
2368
  { 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).` },
2369
2369
  { pattern: /model.*not found|invalid model|model.*unavailable/i, message: () => `The requested model is not available. Run \`${resolveCaliber()} config\` to select a different model.` }
@@ -2886,6 +2886,26 @@ function isClaudeCliAvailable() {
2886
2886
  return false;
2887
2887
  }
2888
2888
  }
2889
+ var cachedLoggedIn = null;
2890
+ function isClaudeCliLoggedIn() {
2891
+ if (cachedLoggedIn !== null) return cachedLoggedIn;
2892
+ try {
2893
+ const result = execSync6(`${CLAUDE_CLI_BIN} auth status`, {
2894
+ stdio: ["ignore", "pipe", "pipe"],
2895
+ timeout: 5e3
2896
+ });
2897
+ const output = result.toString().trim();
2898
+ try {
2899
+ const status = JSON.parse(output);
2900
+ cachedLoggedIn = status.loggedIn === true;
2901
+ } catch {
2902
+ cachedLoggedIn = !output.toLowerCase().includes("not logged in");
2903
+ }
2904
+ } catch {
2905
+ cachedLoggedIn = false;
2906
+ }
2907
+ return cachedLoggedIn;
2908
+ }
2889
2909
 
2890
2910
  // src/llm/model-recovery.ts
2891
2911
  init_config();
@@ -3017,6 +3037,11 @@ function createProvider(config) {
3017
3037
  "Cursor provider requires the Cursor Agent CLI. Install it from https://cursor.com/install then run `agent login`. Alternatively set ANTHROPIC_API_KEY or another provider."
3018
3038
  );
3019
3039
  }
3040
+ if (!isCursorLoggedIn()) {
3041
+ throw new Error(
3042
+ "Cursor Agent CLI is installed but not logged in. Run `agent login` in your terminal to authenticate, then retry."
3043
+ );
3044
+ }
3020
3045
  return new CursorAcpProvider(config);
3021
3046
  }
3022
3047
  case "claude-cli": {
@@ -3025,6 +3050,11 @@ function createProvider(config) {
3025
3050
  "Claude Code provider requires the Claude Code CLI. Install it from https://claude.ai/install (or run `claude` once and log in). Alternatively set ANTHROPIC_API_KEY or choose another provider."
3026
3051
  );
3027
3052
  }
3053
+ if (!isClaudeCliLoggedIn()) {
3054
+ throw new Error(
3055
+ "Claude Code CLI is installed but not logged in. Run `claude` in your terminal to log in, then retry."
3056
+ );
3057
+ }
3028
3058
  return new ClaudeCliProvider(config);
3029
3059
  }
3030
3060
  default:
@@ -3078,6 +3108,10 @@ async function llmCall(options) {
3078
3108
  await new Promise((r) => setTimeout(r, 1e3 * Math.pow(2, attempt - 1)));
3079
3109
  continue;
3080
3110
  }
3111
+ if (isRateLimitError(error.message) && attempt < MAX_RETRIES) {
3112
+ await new Promise((r) => setTimeout(r, 2e3 * Math.pow(2, attempt - 1)));
3113
+ continue;
3114
+ }
3081
3115
  if (isTransientError(error) && attempt < MAX_RETRIES) {
3082
3116
  await new Promise((r) => setTimeout(r, 1e3 * Math.pow(2, attempt - 1)));
3083
3117
  continue;
@@ -6274,6 +6308,7 @@ init_config();
6274
6308
  import chalk3 from "chalk";
6275
6309
  import select2 from "@inquirer/select";
6276
6310
  import confirm from "@inquirer/confirm";
6311
+ var IS_WINDOWS3 = process.platform === "win32";
6277
6312
  var PROVIDER_CHOICES = [
6278
6313
  { name: "Claude Code \u2014 use your existing subscription (no API key)", value: "claude-cli" },
6279
6314
  { name: "Cursor \u2014 use your existing subscription (no API key)", value: "cursor" },
@@ -6297,6 +6332,11 @@ async function runInteractiveProviderSetup(options) {
6297
6332
  console.log(chalk3.dim(" Then run ") + chalk3.hex("#83D1EB")("claude") + chalk3.dim(" once to log in.\n"));
6298
6333
  const proceed = await confirm({ message: "Continue anyway?" });
6299
6334
  if (!proceed) throw new Error("__exit__");
6335
+ } else if (!isClaudeCliLoggedIn()) {
6336
+ console.log(chalk3.yellow("\n Claude Code CLI found but not logged in."));
6337
+ console.log(chalk3.dim(" Run ") + chalk3.hex("#83D1EB")("claude") + chalk3.dim(" once to log in.\n"));
6338
+ const proceed = await confirm({ message: "Continue anyway?" });
6339
+ if (!proceed) throw new Error("__exit__");
6300
6340
  } else {
6301
6341
  console.log(chalk3.dim(" Run `claude` once and log in with your Pro/Max/Team account if you haven't."));
6302
6342
  }
@@ -6305,8 +6345,13 @@ async function runInteractiveProviderSetup(options) {
6305
6345
  case "cursor": {
6306
6346
  if (!isCursorAgentAvailable()) {
6307
6347
  console.log(chalk3.yellow("\n Cursor Agent CLI not found."));
6308
- console.log(chalk3.dim(" Install it: ") + chalk3.hex("#83D1EB")("curl https://cursor.com/install -fsS | bash"));
6309
- console.log(chalk3.dim(" Then run ") + chalk3.hex("#83D1EB")("agent login") + chalk3.dim(" to authenticate.\n"));
6348
+ if (IS_WINDOWS3) {
6349
+ console.log(chalk3.dim(" Install it from: ") + chalk3.hex("#83D1EB")("https://www.cursor.com/downloads"));
6350
+ console.log(chalk3.dim(" Then run ") + chalk3.hex("#83D1EB")("agent login") + chalk3.dim(" in PowerShell to authenticate.\n"));
6351
+ } else {
6352
+ console.log(chalk3.dim(" Install it: ") + chalk3.hex("#83D1EB")("curl https://cursor.com/install -fsS | bash"));
6353
+ console.log(chalk3.dim(" Then run ") + chalk3.hex("#83D1EB")("agent login") + chalk3.dim(" to authenticate.\n"));
6354
+ }
6310
6355
  const proceed = await confirm({ message: "Continue anyway?" });
6311
6356
  if (!proceed) throw new Error("__exit__");
6312
6357
  } else if (!isCursorLoggedIn()) {
@@ -6611,7 +6656,7 @@ function checkExistence(dir) {
6611
6656
  earnedPoints: Math.min(skillPoints, maxSkillPoints),
6612
6657
  passed: skillCount >= 1,
6613
6658
  detail: skillCount === 0 ? "No skills found" : `${skillCount} skill${skillCount === 1 ? "" : "s"} found`,
6614
- suggestion: skillCount === 0 ? "Add .claude/skills/ with project-specific workflows" : skillCount < 3 ? "Optimal is 2-3 focused skills" : void 0,
6659
+ suggestion: skillCount === 0 ? "Skills are reusable agent workflows (e.g., deploy, test, review). Add 2-3 for your most common tasks" : skillCount < 3 ? "Optimal is 2-3 focused skills" : void 0,
6615
6660
  fix: skillCount === 0 ? {
6616
6661
  action: "create_skills",
6617
6662
  data: { currentCount: 0 },
@@ -6628,7 +6673,7 @@ function checkExistence(dir) {
6628
6673
  earnedPoints: mdcCount >= 1 ? POINTS_CURSOR_MDC_RULES : 0,
6629
6674
  passed: mdcCount >= 1,
6630
6675
  detail: mdcCount === 0 ? "No .mdc rule files" : `${mdcCount} .mdc rule${mdcCount === 1 ? "" : "s"} found`,
6631
- suggestion: mdcCount === 0 ? "Add .cursor/rules/*.mdc with frontmatter for Cursor" : void 0,
6676
+ suggestion: mdcCount === 0 ? "Cursor .mdc rules use frontmatter to scope rules to specific files/paths. Add them for more targeted Cursor behavior" : void 0,
6632
6677
  fix: mdcCount === 0 ? {
6633
6678
  action: "create_mdc_rules",
6634
6679
  data: {},
@@ -6644,7 +6689,7 @@ function checkExistence(dir) {
6644
6689
  earnedPoints: mcp.count >= 1 ? POINTS_MCP_SERVERS : 0,
6645
6690
  passed: mcp.count >= 1,
6646
6691
  detail: mcp.count > 0 ? `${mcp.count} server${mcp.count === 1 ? "" : "s"} in ${mcp.sources.join(", ")}` : "No MCP servers configured",
6647
- suggestion: mcp.count === 0 ? "Configure MCP servers in .mcp.json for external service access" : void 0,
6692
+ suggestion: mcp.count === 0 ? "MCP servers connect your agent to external tools (databases, Slack, Linear, etc). Add if your team uses external services" : void 0,
6648
6693
  fix: mcp.count === 0 ? {
6649
6694
  action: "configure_mcp",
6650
6695
  data: {},
@@ -6713,7 +6758,7 @@ function checkQuality(dir) {
6713
6758
  earnedPoints: tokenPoints,
6714
6759
  passed: tokenPoints >= 4,
6715
6760
  detail: totalContent.length === 0 ? "No config files to measure" : `~${totalTokens} tokens total across all config files`,
6716
- suggestion: tokenPoints < 4 && totalContent.length > 0 ? `Total config is ~${totalTokens} tokens \u2014 reduce to under 5000 for better agent performance` : void 0,
6761
+ suggestion: tokenPoints < 4 && totalContent.length > 0 ? `Config is ~${totalTokens} tokens. Agents work best under ~5000 tokens (~4 pages of text) \u2014 trim verbose sections` : void 0,
6717
6762
  fix: tokenPoints < 4 && totalContent.length > 0 ? {
6718
6763
  action: "reduce_size",
6719
6764
  data: { currentTokens: totalTokens, targetTokens: 5e3 },
@@ -7157,7 +7202,7 @@ function checkFreshness(dir) {
7157
7202
  earnedPoints: hasPermissions ? POINTS_PERMISSIONS : 0,
7158
7203
  passed: hasPermissions,
7159
7204
  detail: permissionDetail,
7160
- suggestion: hasPermissions ? void 0 : "Add permissions.allow to .claude/settings.json for safer agent execution",
7205
+ suggestion: hasPermissions ? void 0 : "Permissions control which shell commands the agent can run without asking. Adds a safety layer for your team",
7161
7206
  fix: hasPermissions ? void 0 : {
7162
7207
  action: "add_permissions",
7163
7208
  data: {},
@@ -7222,7 +7267,7 @@ function checkBonus(dir) {
7222
7267
  earnedPoints: hasHooks ? POINTS_HOOKS : 0,
7223
7268
  passed: hasHooks,
7224
7269
  detail: hasHooks ? hookSources.join(", ") : "No hooks configured",
7225
- suggestion: hasHooks ? void 0 : `Run \`${resolveCaliber()} init\` to add pre-commit instructions`,
7270
+ suggestion: hasHooks ? void 0 : `Hooks auto-sync your agent config on every commit so it stays fresh. Run \`${resolveCaliber()} init\` to set up`,
7226
7271
  fix: hasHooks ? void 0 : {
7227
7272
  action: "install_hooks",
7228
7273
  data: {},
@@ -7238,7 +7283,7 @@ function checkBonus(dir) {
7238
7283
  earnedPoints: agentsMdExists ? POINTS_AGENTS_MD : 0,
7239
7284
  passed: agentsMdExists,
7240
7285
  detail: agentsMdExists ? "Found at project root" : "Not found",
7241
- suggestion: agentsMdExists ? void 0 : "Add AGENTS.md \u2014 the emerging cross-agent standard",
7286
+ suggestion: agentsMdExists ? void 0 : "AGENTS.md provides project context to Codex, Copilot, and other agents. Works alongside CLAUDE.md",
7242
7287
  fix: agentsMdExists ? void 0 : {
7243
7288
  action: "create_file",
7244
7289
  data: { file: "AGENTS.md" },
@@ -7274,7 +7319,7 @@ function checkBonus(dir) {
7274
7319
  earnedPoints: allOpenSkills ? POINTS_OPEN_SKILLS_FORMAT : 0,
7275
7320
  passed: allOpenSkills || totalSkillFiles === 0,
7276
7321
  detail: totalSkillFiles === 0 ? "No skills to check" : allOpenSkills ? `All ${totalSkillFiles} skill${totalSkillFiles === 1 ? "" : "s"} use SKILL.md with frontmatter` : `${openSkillsCount}/${totalSkillFiles} use OpenSkills format`,
7277
- suggestion: totalSkillFiles > 0 && !allOpenSkills ? "Migrate skills to .claude/skills/{name}/SKILL.md with YAML frontmatter" : void 0,
7322
+ suggestion: totalSkillFiles > 0 && !allOpenSkills ? "OpenSkills format (SKILL.md with YAML header) makes skills portable across agents. Migrate for cross-tool compatibility" : void 0,
7278
7323
  fix: totalSkillFiles > 0 && !allOpenSkills ? {
7279
7324
  action: "migrate_skills",
7280
7325
  data: { openSkills: openSkillsCount, total: totalSkillFiles },
@@ -7291,7 +7336,7 @@ function checkBonus(dir) {
7291
7336
  earnedPoints: hasLearned ? POINTS_LEARNED_CONTENT : 0,
7292
7337
  passed: hasLearned,
7293
7338
  detail: hasLearned ? "Session learnings found in CALIBER_LEARNINGS.md" : "No learned content",
7294
- suggestion: hasLearned ? void 0 : `Install learning hooks: \`${resolveCaliber()} learn install\``
7339
+ suggestion: hasLearned ? void 0 : `Session learnings capture patterns from your coding sessions so the agent improves over time. Run \`${resolveCaliber()} learn install\``
7295
7340
  });
7296
7341
  return checks;
7297
7342
  }
@@ -7532,7 +7577,7 @@ function displayScore(result) {
7532
7577
  formatTopImprovements(result.checks);
7533
7578
  }
7534
7579
  function formatTopImprovements(checks) {
7535
- const improvable = checks.filter((c) => c.earnedPoints < c.maxPoints).map((c) => ({ name: c.name, potential: c.maxPoints - c.earnedPoints })).sort((a, b) => b.potential - a.potential).slice(0, 5);
7580
+ 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);
7536
7581
  if (improvable.length === 0) return;
7537
7582
  console.log(chalk4.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"));
7538
7583
  console.log("");
@@ -7542,6 +7587,9 @@ function formatTopImprovements(checks) {
7542
7587
  const label = chalk4.white(item.name.padEnd(42));
7543
7588
  const pts = chalk4.yellow(`+${item.potential} pts`);
7544
7589
  console.log(` ${num} ${label}${pts}`);
7590
+ if (item.suggestion) {
7591
+ console.log(chalk4.gray(` ${item.suggestion}`));
7592
+ }
7545
7593
  }
7546
7594
  console.log("");
7547
7595
  }
@@ -10097,6 +10145,7 @@ function getScoreTrend(entries) {
10097
10145
  }
10098
10146
 
10099
10147
  // src/commands/init.ts
10148
+ var IS_WINDOWS5 = process.platform === "win32";
10100
10149
  function log(verbose, ...args) {
10101
10150
  if (verbose) console.log(chalk14.dim(` [verbose] ${args.map(String).join(" ")}`));
10102
10151
  }
@@ -10141,7 +10190,7 @@ async function initCommand(options) {
10141
10190
  console.log(title.bold(" Step 1/3 \u2014 Connect\n"));
10142
10191
  let config = loadConfig();
10143
10192
  if (!config && !options.autoApprove) {
10144
- if (isClaudeCliAvailable()) {
10193
+ if (isClaudeCliAvailable() && isClaudeCliLoggedIn()) {
10145
10194
  console.log(chalk14.dim(" Detected: Claude Code CLI (uses your Pro/Max/Team subscription)\n"));
10146
10195
  const useIt = await confirm2({ message: "Use Claude Code as your LLM provider?" });
10147
10196
  if (useIt) {
@@ -10161,7 +10210,7 @@ async function initCommand(options) {
10161
10210
  }
10162
10211
  if (!config) {
10163
10212
  if (options.autoApprove) {
10164
- if (isClaudeCliAvailable()) {
10213
+ if (isClaudeCliAvailable() && isClaudeCliLoggedIn()) {
10165
10214
  const autoConfig = { provider: "claude-cli", model: "default" };
10166
10215
  writeConfigFile(autoConfig);
10167
10216
  config = autoConfig;
@@ -10232,6 +10281,10 @@ async function initCommand(options) {
10232
10281
  console.log(` ${chalk14.green("\u2713")} Onboarding hook \u2014 nudges new team members to set up`);
10233
10282
  installSessionStartHook();
10234
10283
  console.log(` ${chalk14.green("\u2713")} Freshness hook \u2014 warns when configs are stale`);
10284
+ if (IS_WINDOWS5) {
10285
+ console.log(chalk14.yellow("\n Note: hooks use shell syntax and require Git Bash (included with Git for Windows)."));
10286
+ console.log(chalk14.dim(" If hooks don't run, ensure Git for Windows is installed and git is using its bundled sh."));
10287
+ }
10235
10288
  const { ensureBuiltinSkills: ensureBuiltinSkills2 } = await Promise.resolve().then(() => (init_builtin_skills(), builtin_skills_exports));
10236
10289
  for (const agent of targetAgent) {
10237
10290
  if (agent === "claude" && !fs34.existsSync(".claude"))
@@ -11209,12 +11262,12 @@ async function scoreCommand(options) {
11209
11262
  chalk18.gray(" Biggest gain: ") + chalk18.yellow(`+${pts} pts`) + chalk18.gray(` from "${topFix.name}"`) + (topFix.suggestion ? chalk18.gray(` \u2014 ${topFix.suggestion}`) : "")
11210
11263
  );
11211
11264
  console.log(
11212
- chalk18.gray(" Run ") + chalk18.hex("#83D1EB")(`${bin} init`) + chalk18.gray(" to auto-fix these.")
11265
+ chalk18.gray(" Run ") + chalk18.hex("#83D1EB")(`${bin} init`) + chalk18.gray(" to generate or update your agent config files.")
11213
11266
  );
11214
11267
  } else if (failing.length > 0) {
11215
11268
  console.log(
11216
11269
  chalk18.green(" Looking good!") + chalk18.gray(
11217
- ` ${failing.length} check${failing.length === 1 ? "" : "s"} can still be improved.`
11270
+ ` ${failing.length} optional improvement${failing.length === 1 ? "" : "s"} available.`
11218
11271
  )
11219
11272
  );
11220
11273
  console.log(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rely-ai/caliber",
3
- "version": "1.40.0",
3
+ "version": "1.40.2",
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": {