@rely-ai/caliber 1.2.0 → 1.3.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 +70 -46
  2. package/package.json +2 -1
package/dist/bin.js CHANGED
@@ -60,6 +60,7 @@ import chalk5 from "chalk";
60
60
  import ora2 from "ora";
61
61
  import readline4 from "readline";
62
62
  import select2 from "@inquirer/select";
63
+ import checkbox from "@inquirer/checkbox";
63
64
  import fs19 from "fs";
64
65
 
65
66
  // src/fingerprint/index.ts
@@ -1218,7 +1219,7 @@ Omit empty categories. Keep each reason punchy and specific. End with a blank li
1218
1219
 
1219
1220
  AgentSetup schema:
1220
1221
  {
1221
- "targetAgent": "claude" | "cursor" | "codex" | "both",
1222
+ "targetAgent": ["claude", "cursor", "codex"] (array of selected agents),
1222
1223
  "fileDescriptions": {
1223
1224
  "<file-path>": "reason for this change (max 80 chars)"
1224
1225
  },
@@ -1302,7 +1303,7 @@ Apply the requested changes to the setup and return the complete updated AgentSe
1302
1303
 
1303
1304
  AgentSetup schema:
1304
1305
  {
1305
- "targetAgent": "claude" | "cursor" | "codex" | "both",
1306
+ "targetAgent": ["claude", "cursor", "codex"] (array of selected agents),
1306
1307
  "fileDescriptions": {
1307
1308
  "<file-path>": "reason for this change (max 80 chars)"
1308
1309
  },
@@ -2019,13 +2020,13 @@ function writeSetup(setup) {
2019
2020
  ];
2020
2021
  const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
2021
2022
  const written = [];
2022
- if ((setup.targetAgent === "claude" || setup.targetAgent === "both") && setup.claude) {
2023
+ if (setup.targetAgent.includes("claude") && setup.claude) {
2023
2024
  written.push(...writeClaudeConfig(setup.claude));
2024
2025
  }
2025
- if ((setup.targetAgent === "cursor" || setup.targetAgent === "both") && setup.cursor) {
2026
+ if (setup.targetAgent.includes("cursor") && setup.cursor) {
2026
2027
  written.push(...writeCursorConfig(setup.cursor));
2027
2028
  }
2028
- if (setup.targetAgent === "codex" && setup.codex) {
2029
+ if (setup.targetAgent.includes("codex") && setup.codex) {
2029
2030
  written.push(...writeCodexConfig(setup.codex));
2030
2031
  }
2031
2032
  const deleted = [];
@@ -2078,7 +2079,7 @@ function undoSetup() {
2078
2079
  }
2079
2080
  function getFilesToWrite(setup) {
2080
2081
  const files = [];
2081
- if ((setup.targetAgent === "claude" || setup.targetAgent === "both") && setup.claude) {
2082
+ if (setup.targetAgent.includes("claude") && setup.claude) {
2082
2083
  files.push("CLAUDE.md");
2083
2084
  if (setup.claude.mcpServers) files.push(".mcp.json");
2084
2085
  if (setup.claude.skills) {
@@ -2087,7 +2088,7 @@ function getFilesToWrite(setup) {
2087
2088
  }
2088
2089
  }
2089
2090
  }
2090
- if ((setup.targetAgent === "cursor" || setup.targetAgent === "both") && setup.cursor) {
2091
+ if (setup.targetAgent.includes("cursor") && setup.cursor) {
2091
2092
  if (setup.cursor.cursorrules) files.push(".cursorrules");
2092
2093
  if (setup.cursor.rules) {
2093
2094
  for (const r of setup.cursor.rules) files.push(`.cursor/rules/${r.filename}`);
@@ -2097,7 +2098,7 @@ function getFilesToWrite(setup) {
2097
2098
  }
2098
2099
  if (setup.cursor.mcpServers) files.push(".cursor/mcp.json");
2099
2100
  }
2100
- if (setup.targetAgent === "codex" && setup.codex) {
2101
+ if (setup.targetAgent.includes("codex") && setup.codex) {
2101
2102
  files.push("AGENTS.md");
2102
2103
  if (setup.codex.skills) {
2103
2104
  for (const s of setup.codex.skills) files.push(`.agents/skills/${s.name}/SKILL.md`);
@@ -2426,10 +2427,20 @@ import fs16 from "fs";
2426
2427
  import path14 from "path";
2427
2428
  import { execSync as execSync6 } from "child_process";
2428
2429
  var STATE_FILE = path14.join(CALIBER_DIR, ".caliber-state.json");
2430
+ function normalizeTargetAgent(value) {
2431
+ if (Array.isArray(value)) return value;
2432
+ if (typeof value === "string") {
2433
+ if (value === "both") return ["claude", "cursor"];
2434
+ if (["claude", "cursor", "codex"].includes(value)) return [value];
2435
+ }
2436
+ return void 0;
2437
+ }
2429
2438
  function readState() {
2430
2439
  try {
2431
2440
  if (!fs16.existsSync(STATE_FILE)) return null;
2432
- return JSON.parse(fs16.readFileSync(STATE_FILE, "utf-8"));
2441
+ const raw = JSON.parse(fs16.readFileSync(STATE_FILE, "utf-8"));
2442
+ if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
2443
+ return raw;
2433
2444
  } catch {
2434
2445
  return null;
2435
2446
  }
@@ -3690,26 +3701,19 @@ function sumCategory(checks, category) {
3690
3701
  }
3691
3702
  function filterChecksForTarget(checks, target) {
3692
3703
  return checks.filter((c) => {
3693
- if (target === "claude") {
3694
- return !CURSOR_ONLY_CHECKS.has(c.id) && !CODEX_ONLY_CHECKS.has(c.id) && !BOTH_ONLY_CHECKS.has(c.id);
3695
- }
3696
- if (target === "cursor") {
3697
- return !CLAUDE_ONLY_CHECKS.has(c.id) && !CODEX_ONLY_CHECKS.has(c.id) && !BOTH_ONLY_CHECKS.has(c.id);
3698
- }
3699
- if (target === "codex") {
3700
- return !CLAUDE_ONLY_CHECKS.has(c.id) && !CURSOR_ONLY_CHECKS.has(c.id) && !BOTH_ONLY_CHECKS.has(c.id);
3701
- }
3704
+ if (CLAUDE_ONLY_CHECKS.has(c.id)) return target.includes("claude");
3705
+ if (CURSOR_ONLY_CHECKS.has(c.id)) return target.includes("cursor");
3706
+ if (CODEX_ONLY_CHECKS.has(c.id)) return target.includes("codex");
3707
+ if (BOTH_ONLY_CHECKS.has(c.id)) return target.includes("claude") && target.includes("cursor");
3702
3708
  return true;
3703
3709
  });
3704
3710
  }
3705
3711
  function detectTargetAgent(dir) {
3706
- const hasClaude = existsSync8(join7(dir, "CLAUDE.md")) || existsSync8(join7(dir, ".claude", "skills"));
3707
- const hasCursor = existsSync8(join7(dir, ".cursorrules")) || existsSync8(join7(dir, ".cursor", "rules"));
3708
- const hasCodex = existsSync8(join7(dir, ".codex")) || existsSync8(join7(dir, ".agents", "skills"));
3709
- if (hasClaude && hasCursor) return "both";
3710
- if (hasCodex && !hasClaude && !hasCursor) return "codex";
3711
- if (hasCursor) return "cursor";
3712
- return "claude";
3712
+ const agents = [];
3713
+ if (existsSync8(join7(dir, "CLAUDE.md")) || existsSync8(join7(dir, ".claude", "skills"))) agents.push("claude");
3714
+ if (existsSync8(join7(dir, ".cursorrules")) || existsSync8(join7(dir, ".cursor", "rules"))) agents.push("cursor");
3715
+ if (existsSync8(join7(dir, ".codex")) || existsSync8(join7(dir, ".agents", "skills"))) agents.push("codex");
3716
+ return agents.length > 0 ? agents : ["claude"];
3713
3717
  }
3714
3718
  function computeLocalScore(dir, targetAgent) {
3715
3719
  const target = targetAgent ?? detectTargetAgent(dir);
@@ -3746,6 +3750,11 @@ function computeLocalScore(dir, targetAgent) {
3746
3750
 
3747
3751
  // src/scoring/display.ts
3748
3752
  import chalk3 from "chalk";
3753
+ var AGENT_DISPLAY_NAMES = {
3754
+ claude: "Claude Code",
3755
+ cursor: "Cursor",
3756
+ codex: "Codex"
3757
+ };
3749
3758
  var CATEGORY_LABELS = {
3750
3759
  existence: "FILES & SETUP",
3751
3760
  quality: "QUALITY",
@@ -3788,7 +3797,7 @@ function formatCheck(check) {
3788
3797
  }
3789
3798
  function displayScore(result) {
3790
3799
  const gc = gradeColor(result.grade);
3791
- const agentLabel = result.targetAgent === "both" ? "Claude Code + Cursor" : result.targetAgent === "claude" ? "Claude Code" : result.targetAgent === "codex" ? "Codex" : "Cursor";
3800
+ const agentLabel = result.targetAgent.map((a) => AGENT_DISPLAY_NAMES[a] || a).join(" + ");
3792
3801
  console.log("");
3793
3802
  console.log(chalk3.gray(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
3794
3803
  console.log(chalk3.gray(" \u2502") + " " + chalk3.gray("\u2502"));
@@ -3814,7 +3823,7 @@ function displayScore(result) {
3814
3823
  }
3815
3824
  function displayScoreSummary(result) {
3816
3825
  const gc = gradeColor(result.grade);
3817
- const agentLabel = result.targetAgent === "both" ? "Claude Code + Cursor" : result.targetAgent === "claude" ? "Claude Code" : result.targetAgent === "codex" ? "Codex" : "Cursor";
3826
+ const agentLabel = result.targetAgent.map((a) => AGENT_DISPLAY_NAMES[a] || a).join(" + ");
3818
3827
  console.log("");
3819
3828
  console.log(
3820
3829
  chalk3.gray(" ") + gc(`${result.score}/${result.maxScore}`) + chalk3.gray(` (Grade ${result.grade})`) + chalk3.gray(` \xB7 ${agentLabel}`) + chalk3.gray(` \xB7 ${progressBar(result.score, result.maxScore, 20)}`)
@@ -4228,17 +4237,14 @@ async function discoverAndInstallMcps(targetAgent, fingerprint, dir) {
4228
4237
  if (installedNames.length === 0) {
4229
4238
  return { installed: 0, names: [] };
4230
4239
  }
4231
- if (targetAgent === "claude" || targetAgent === "both") {
4240
+ if (targetAgent.includes("claude") || targetAgent.includes("codex")) {
4232
4241
  writeMcpJson(path16.join(dir, ".mcp.json"), mcpServers);
4233
4242
  }
4234
- if (targetAgent === "cursor" || targetAgent === "both") {
4243
+ if (targetAgent.includes("cursor")) {
4235
4244
  const cursorDir = path16.join(dir, ".cursor");
4236
4245
  if (!fs18.existsSync(cursorDir)) fs18.mkdirSync(cursorDir, { recursive: true });
4237
4246
  writeMcpJson(path16.join(cursorDir, "mcp.json"), mcpServers);
4238
4247
  }
4239
- if (targetAgent === "codex") {
4240
- writeMcpJson(path16.join(dir, ".mcp.json"), mcpServers);
4241
- }
4242
4248
  return { installed: installedNames.length, names: installedNames };
4243
4249
  }
4244
4250
  function writeMcpJson(filePath, mcpServers) {
@@ -4255,12 +4261,12 @@ function writeMcpJson(filePath, mcpServers) {
4255
4261
  }
4256
4262
  function getExistingMcpNames(fingerprint, targetAgent) {
4257
4263
  const names = [];
4258
- if (targetAgent === "claude" || targetAgent === "both") {
4264
+ if (targetAgent.includes("claude")) {
4259
4265
  if (fingerprint.existingConfigs.claudeMcpServers) {
4260
4266
  names.push(...Object.keys(fingerprint.existingConfigs.claudeMcpServers).map((k) => k.toLowerCase()));
4261
4267
  }
4262
4268
  }
4263
- if (targetAgent === "cursor" || targetAgent === "both") {
4269
+ if (targetAgent.includes("cursor")) {
4264
4270
  if (fingerprint.existingConfigs.cursorMcpServers) {
4265
4271
  names.push(...Object.keys(fingerprint.existingConfigs.cursorMcpServers).map((k) => k.toLowerCase()));
4266
4272
  }
@@ -4643,7 +4649,7 @@ async function initCommand(options) {
4643
4649
  console.log("");
4644
4650
  console.log(title.bold(" Keep your configs fresh\n"));
4645
4651
  console.log(chalk5.dim(" Caliber can automatically update your agent configs when your code changes.\n"));
4646
- const hookChoice = targetAgent === "codex" ? await promptHookType("cursor") : await promptHookType(targetAgent);
4652
+ const hookChoice = await promptHookType(targetAgent);
4647
4653
  if (hookChoice === "claude" || hookChoice === "both") {
4648
4654
  const hookResult = installHook();
4649
4655
  if (hookResult.installed) {
@@ -4801,23 +4807,28 @@ function promptInput3(question) {
4801
4807
  });
4802
4808
  }
4803
4809
  async function promptAgent() {
4804
- return select2({
4805
- message: "Which coding agent are you using?",
4810
+ const selected = await checkbox({
4811
+ message: "Which coding agents do you use? (toggle with space)",
4806
4812
  choices: [
4807
4813
  { name: "Claude Code", value: "claude" },
4808
4814
  { name: "Cursor", value: "cursor" },
4809
- { name: "Codex (OpenAI)", value: "codex" },
4810
- { name: "Both (Claude + Cursor)", value: "both" }
4811
- ]
4815
+ { name: "Codex (OpenAI)", value: "codex" }
4816
+ ],
4817
+ validate: (items) => {
4818
+ if (items.length === 0) return "At least one agent must be selected";
4819
+ return true;
4820
+ }
4812
4821
  });
4822
+ return selected;
4813
4823
  }
4814
4824
  async function promptHookType(targetAgent) {
4815
4825
  const choices = [];
4816
- if (targetAgent === "claude" || targetAgent === "both") {
4826
+ const hasClaude = targetAgent.includes("claude");
4827
+ if (hasClaude) {
4817
4828
  choices.push({ name: "Claude Code hook (auto-refresh on session end)", value: "claude" });
4818
4829
  }
4819
4830
  choices.push({ name: "Git pre-commit hook (refresh before each commit)", value: "precommit" });
4820
- if (targetAgent === "claude" || targetAgent === "both") {
4831
+ if (hasClaude) {
4821
4832
  choices.push({ name: "Both (Claude Code + pre-commit)", value: "both" });
4822
4833
  }
4823
4834
  choices.push({ name: "Skip for now", value: "skip" });
@@ -5312,7 +5323,7 @@ async function regenerateCommand(options) {
5312
5323
  genMessages.start();
5313
5324
  let generatedSetup = null;
5314
5325
  try {
5315
- const targetAgent = readState()?.targetAgent ?? "both";
5326
+ const targetAgent = readState()?.targetAgent ?? ["claude", "cursor"];
5316
5327
  const result2 = await generateSetup(
5317
5328
  fingerprint,
5318
5329
  targetAgent,
@@ -6204,10 +6215,12 @@ function writeRefreshDocs(docs) {
6204
6215
  // src/ai/refresh.ts
6205
6216
  async function refreshDocs(diff, existingDocs, projectContext) {
6206
6217
  const prompt = buildRefreshPrompt(diff, existingDocs, projectContext);
6218
+ const fastModel = getFastModel();
6207
6219
  const raw = await llmCall({
6208
6220
  system: REFRESH_SYSTEM_PROMPT,
6209
6221
  prompt,
6210
- maxTokens: 16384
6222
+ maxTokens: 16384,
6223
+ ...fastModel ? { model: fastModel } : {}
6211
6224
  });
6212
6225
  return parseJsonResponse(raw);
6213
6226
  }
@@ -6936,13 +6949,24 @@ var pkg = JSON.parse(
6936
6949
  var program = new Command();
6937
6950
  var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
6938
6951
  program.name(process.env.CALIBER_LOCAL ? "caloc" : "caliber").description("Configure your coding agent environment").version(displayVersion);
6939
- program.command("onboard").alias("init").description("Onboard your project for AI-assisted development").option("--agent <type>", "Target agent: claude, cursor, codex, or both").option("--dry-run", "Preview changes without writing files").option("--force", "Overwrite existing setup without prompting").action(initCommand);
6952
+ function parseAgentOption(value) {
6953
+ if (value === "both") return ["claude", "cursor"];
6954
+ if (value === "all") return ["claude", "cursor", "codex"];
6955
+ const valid = ["claude", "cursor", "codex"];
6956
+ const agents = [...new Set(value.split(",").map((s) => s.trim().toLowerCase()).filter((a) => valid.includes(a)))];
6957
+ if (agents.length === 0) {
6958
+ console.error(`Invalid agent "${value}". Choose from: claude, cursor, codex (comma-separated for multiple)`);
6959
+ process.exit(1);
6960
+ }
6961
+ return agents;
6962
+ }
6963
+ program.command("onboard").alias("init").description("Onboard your project for AI-assisted development").option("--agent <type>", "Target agents (comma-separated): claude, cursor, codex", parseAgentOption).option("--dry-run", "Preview changes without writing files").option("--force", "Overwrite existing setup without prompting").action(initCommand);
6940
6964
  program.command("undo").description("Revert all config changes made by Caliber").action(undoCommand);
6941
6965
  program.command("status").description("Show current Caliber setup status").option("--json", "Output as JSON").action(statusCommand);
6942
6966
  program.command("regenerate").alias("regen").alias("re").alias("update").description("Re-analyze project and regenerate setup").option("--dry-run", "Preview changes without writing files").action(regenerateCommand);
6943
6967
  program.command("config").description("Configure LLM provider, API key, and model").action(configCommand);
6944
6968
  program.command("recommend").description("Discover and install skill recommendations").option("--generate", "Force fresh recommendation search").action(recommendCommand);
6945
- program.command("score").description("Score your current agent config setup (deterministic, no network)").option("--json", "Output as JSON").option("--quiet", "One-line output for scripts/hooks").option("--agent <type>", "Target agent: claude, cursor, codex, or both").action(scoreCommand);
6969
+ program.command("score").description("Score your current agent config setup (deterministic, no network)").option("--json", "Output as JSON").option("--quiet", "One-line output for scripts/hooks").option("--agent <type>", "Target agents (comma-separated): claude, cursor, codex", parseAgentOption).action(scoreCommand);
6946
6970
  program.command("refresh").description("Update docs based on recent code changes").option("--quiet", "Suppress output (for use in hooks)").option("--dry-run", "Preview changes without writing files").action(refreshCommand);
6947
6971
  program.command("hooks").description("Manage auto-refresh hooks (toggle interactively)").option("--install", "Enable all hooks non-interactively").option("--remove", "Disable all hooks non-interactively").action(hooksCommand);
6948
6972
  var learn = program.command("learn").description("Session learning \u2014 observe tool usage and extract reusable instructions");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rely-ai/caliber",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Analyze your codebase and generate optimized AI agent configs (CLAUDE.md, .cursorrules, skills) — no API key needed",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,6 +16,7 @@
16
16
  "dependencies": {
17
17
  "@anthropic-ai/sdk": "^0.39.0",
18
18
  "@anthropic-ai/vertex-sdk": "^0.7.0",
19
+ "@inquirer/checkbox": "^5.1.0",
19
20
  "@inquirer/confirm": "^6.0.8",
20
21
  "@inquirer/select": "^5.1.0",
21
22
  "chalk": "^5.4.0",