@rely-ai/caliber 0.2.0 → 0.2.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 (2) hide show
  1. package/dist/bin.js +70 -36
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -1002,12 +1002,12 @@ SCORING CRITERIA \u2014 your output is scored deterministically. Optimize for 10
1002
1002
 
1003
1003
  Existence (25 pts):
1004
1004
  - CLAUDE.md exists (6 pts) \u2014 always generate
1005
- - Skills configured (8 pts) \u2014 2-3 focused skills is optimal
1005
+ - Skills configured (8 pts) \u2014 generate exactly 3 focused skills for full points (6 base + 1 per extra, cap 2). Two skills = 7 pts, three = 8 pts.
1006
1006
  - MCP servers mentioned (3 pts) \u2014 reference detected MCP integrations
1007
1007
  - For "both" target: .cursorrules/.cursor/rules/ exist (3+3 pts), cross-platform parity (2 pts)
1008
1008
 
1009
1009
  Quality (25 pts):
1010
- - Build/test/lint commands documented (8 pts) \u2014 include actual npm/make/cargo commands
1010
+ - Build/test/lint commands documented (8 pts) \u2014 include actual commands from the project
1011
1011
  - Concise context files (6 pts) \u2014 keep CLAUDE.md under 100 lines for full points (200=4pts, 300=3pts, 500+=0pts)
1012
1012
  - No vague instructions (4 pts) \u2014 avoid "follow best practices", "write clean code", "ensure quality"
1013
1013
  - No directory tree listings (3 pts) \u2014 do NOT include tree-style file listings in code blocks
@@ -1018,8 +1018,12 @@ Coverage (20 pts):
1018
1018
  - Service/MCP coverage (6 pts) \u2014 reference detected services (DB, cloud, etc.)
1019
1019
  - MCP completeness (4 pts) \u2014 full points if no external services detected
1020
1020
 
1021
- Accuracy (15 pts):
1022
- - Documented commands exist (6 pts) \u2014 ONLY reference commands that actually exist in package.json scripts. Do NOT invent commands. Check the provided package.json scripts section carefully.
1021
+ Accuracy (15 pts) \u2014 THIS IS CRITICAL, READ CAREFULLY:
1022
+ - Documented commands exist (6 pts) \u2014 the scoring system validates EVERY command you write against the project's actual package.json scripts, Makefile targets, or Cargo.toml. If you write "yarn build" but there is no "build" script in package.json, you LOSE points. Rules:
1023
+ * Look at the "scripts" section in the provided package.json. ONLY reference scripts that exist there.
1024
+ * If a project uses Makefiles, only reference targets that exist in the Makefile.
1025
+ * If there are no build/test scripts, do NOT invent them. Document what actually exists.
1026
+ * Use the exact package manager the project uses (npm/yarn/pnpm/bun) \u2014 check the lockfile.
1023
1027
  - Documented paths exist (4 pts) \u2014 ONLY reference file paths from the provided file tree. Never guess paths.
1024
1028
  - Config freshness (5 pts) \u2014 config must match current code state
1025
1029
 
@@ -1032,7 +1036,7 @@ Bonus (5 pts):
1032
1036
 
1033
1037
  OUTPUT SIZE CONSTRAINTS \u2014 these are critical:
1034
1038
  - CLAUDE.md: MUST be under 100 lines for maximum score. Aim for 70-90 lines. Be extremely concise \u2014 only commands, architecture overview, and key conventions. Use bullet points and tables, not prose.
1035
- - Skills: max 5 skills total (across claude + cursor). Only generate skills for the most important frameworks.
1039
+ - Skills: generate exactly 3 skills per target platform. Only go above 3 for large multi-framework projects.
1036
1040
  - Each skill content: max 150 lines. Focus on patterns and examples, not exhaustive docs.
1037
1041
  - Cursor rules: max 5 .mdc files.
1038
1042
  - If the project is large, prioritize depth on the 3-4 most critical tools over breadth across everything.`;
@@ -3103,6 +3107,7 @@ function checkFreshness(dir) {
3103
3107
 
3104
3108
  // src/scoring/checks/bonus.ts
3105
3109
  import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "fs";
3110
+ import { execSync as execSync5 } from "child_process";
3106
3111
  import { join as join6 } from "path";
3107
3112
  function readFileOrNull5(path23) {
3108
3113
  try {
@@ -3111,27 +3116,39 @@ function readFileOrNull5(path23) {
3111
3116
  return null;
3112
3117
  }
3113
3118
  }
3119
+ function hasPreCommitHook(dir) {
3120
+ try {
3121
+ const gitDir = execSync5("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3122
+ const hookPath = join6(gitDir, "hooks", "pre-commit");
3123
+ const content = readFileOrNull5(hookPath);
3124
+ return content ? content.includes("caliber") : false;
3125
+ } catch {
3126
+ return false;
3127
+ }
3128
+ }
3114
3129
  function checkBonus(dir) {
3115
3130
  const checks = [];
3116
- let hasHooks = false;
3117
- let hookDetail = "";
3131
+ let hasClaudeHooks = false;
3132
+ let hasPrecommit = false;
3133
+ const hookSources = [];
3118
3134
  const settingsContent = readFileOrNull5(join6(dir, ".claude", "settings.json"));
3119
3135
  if (settingsContent) {
3120
3136
  try {
3121
3137
  const settings = JSON.parse(settingsContent);
3122
3138
  const hooks2 = settings.hooks;
3123
3139
  if (hooks2 && Object.keys(hooks2).length > 0) {
3124
- hasHooks = true;
3125
- hookDetail = `${Object.keys(hooks2).length} hook${Object.keys(hooks2).length === 1 ? "" : "s"}: ${Object.keys(hooks2).join(", ")}`;
3126
- } else {
3127
- hookDetail = "No hooks in settings.json";
3140
+ hasClaudeHooks = true;
3141
+ hookSources.push(`Claude Code: ${Object.keys(hooks2).join(", ")}`);
3128
3142
  }
3129
3143
  } catch {
3130
- hookDetail = "settings.json is not valid JSON";
3131
3144
  }
3132
- } else {
3133
- hookDetail = "No .claude/settings.json";
3134
3145
  }
3146
+ hasPrecommit = hasPreCommitHook(dir);
3147
+ if (hasPrecommit) {
3148
+ hookSources.push("git pre-commit");
3149
+ }
3150
+ const hasHooks = hasClaudeHooks || hasPrecommit;
3151
+ const hookDetail = hasHooks ? hookSources.join(", ") : settingsContent ? "No hooks in settings.json" : "No hooks configured";
3135
3152
  checks.push({
3136
3153
  id: "hooks_configured",
3137
3154
  name: "Hooks configured",
@@ -3140,7 +3157,7 @@ function checkBonus(dir) {
3140
3157
  earnedPoints: hasHooks ? POINTS_HOOKS : 0,
3141
3158
  passed: hasHooks,
3142
3159
  detail: hookDetail,
3143
- suggestion: hasHooks ? void 0 : "Add hooks (e.g., SessionEnd for auto-refresh) to .claude/settings.json"
3160
+ suggestion: hasHooks ? void 0 : "Run `caliber hooks install` or `caliber hooks install-precommit` for auto-refresh"
3144
3161
  });
3145
3162
  const agentsMdExists = existsSync7(join6(dir, "AGENTS.md"));
3146
3163
  checks.push({
@@ -3374,10 +3391,10 @@ async function initCommand(options) {
3374
3391
  console.log(chalk3.dim(" against your actual codebase \u2014 keeping what works, fixing"));
3375
3392
  console.log(chalk3.dim(" what's stale, and adding what's missing.\n"));
3376
3393
  console.log(chalk3.bold(" How it works:\n"));
3377
- console.log(chalk3.dim(" 1. Scan Analyze your code, dependencies, and file structure"));
3378
- console.log(chalk3.dim(" 2. Generate AI creates config files tailored to your project"));
3379
- console.log(chalk3.dim(" 3. Review You accept, refine, or decline the generated setup"));
3380
- console.log(chalk3.dim(" 4. Apply Config files are written to your project\n"));
3394
+ console.log(chalk3.dim(" 1. Scan Analyze your code, dependencies, and existing configs"));
3395
+ console.log(chalk3.dim(" 2. Generate AI creates or improves config files for your project"));
3396
+ console.log(chalk3.dim(" 3. Review You accept, refine, or decline the proposed changes"));
3397
+ console.log(chalk3.dim(" 4. Apply Config files are written with backups\n"));
3381
3398
  console.log(chalk3.hex("#6366f1").bold(" Step 1/4 \u2014 Check LLM provider\n"));
3382
3399
  const config = loadConfig();
3383
3400
  if (!config) {
@@ -3392,7 +3409,7 @@ async function initCommand(options) {
3392
3409
  console.log(chalk3.dim(` Provider: ${config.provider} | Model: ${config.model}
3393
3410
  `));
3394
3411
  console.log(chalk3.hex("#6366f1").bold(" Step 2/4 \u2014 Scan project\n"));
3395
- console.log(chalk3.dim(" Detecting languages, frameworks, file structure, and existing configs.\n"));
3412
+ console.log(chalk3.dim(" Detecting languages, dependencies, file structure, and existing configs.\n"));
3396
3413
  const spinner = ora("Analyzing project...").start();
3397
3414
  const fingerprint = collectFingerprint(process.cwd());
3398
3415
  await enrichFingerprintWithLLM(fingerprint, process.cwd());
@@ -3406,11 +3423,16 @@ async function initCommand(options) {
3406
3423
  if (isEmpty) {
3407
3424
  fingerprint.description = await promptInput("What will you build in this project?");
3408
3425
  }
3409
- console.log(chalk3.hex("#6366f1").bold(" Step 3/4 \u2014 Auditing your configs\n"));
3410
- console.log(chalk3.dim(" AI is auditing your CLAUDE.md, skills, and rules against your"));
3411
- console.log(chalk3.dim(" project's actual codebase and conventions.\n"));
3412
- console.log(chalk3.dim(" This usually takes 1\u20133 minutes on first run.\n"));
3413
3426
  const hasExistingConfig = !!(fingerprint.existingConfigs.claudeMd || fingerprint.existingConfigs.claudeSettings || fingerprint.existingConfigs.claudeSkills?.length || fingerprint.existingConfigs.cursorrules || fingerprint.existingConfigs.cursorRules?.length);
3427
+ if (hasExistingConfig) {
3428
+ console.log(chalk3.hex("#6366f1").bold(" Step 3/4 \u2014 Auditing your configs\n"));
3429
+ console.log(chalk3.dim(" AI is reviewing your existing configs against your codebase"));
3430
+ console.log(chalk3.dim(" and suggesting improvements.\n"));
3431
+ } else {
3432
+ console.log(chalk3.hex("#6366f1").bold(" Step 3/4 \u2014 Generating configs\n"));
3433
+ console.log(chalk3.dim(" AI is creating agent config files tailored to your project.\n"));
3434
+ }
3435
+ console.log(chalk3.dim(" This usually takes 1\u20133 minutes.\n"));
3414
3436
  const genStartTime = Date.now();
3415
3437
  const genSpinner = ora("Generating setup...").start();
3416
3438
  const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, { showElapsedTime: true });
@@ -3931,9 +3953,10 @@ async function regenerateCommand(options) {
3931
3953
  genMessages.start();
3932
3954
  let generatedSetup = null;
3933
3955
  try {
3956
+ const targetAgent = readState()?.targetAgent ?? "both";
3934
3957
  const result2 = await generateSetup(
3935
3958
  fingerprint,
3936
- "both",
3959
+ targetAgent,
3937
3960
  void 0,
3938
3961
  {
3939
3962
  onStatus: (status) => {
@@ -3983,7 +4006,7 @@ async function regenerateCommand(options) {
3983
4006
  // src/commands/recommend.ts
3984
4007
  import chalk7 from "chalk";
3985
4008
  import ora4 from "ora";
3986
- import { mkdirSync, writeFileSync } from "fs";
4009
+ import { mkdirSync, readFileSync as readFileSync7, existsSync as existsSync9, writeFileSync } from "fs";
3987
4010
  import { join as join8, dirname as dirname2 } from "path";
3988
4011
 
3989
4012
  // src/scanner/index.ts
@@ -4145,15 +4168,26 @@ async function searchSkills(technologies) {
4145
4168
  }
4146
4169
  return results;
4147
4170
  }
4171
+ function extractTopDeps() {
4172
+ const pkgPath = join8(process.cwd(), "package.json");
4173
+ if (!existsSync9(pkgPath)) return [];
4174
+ try {
4175
+ const pkg3 = JSON.parse(readFileSync7(pkgPath, "utf-8"));
4176
+ return Object.keys(pkg3.dependencies ?? {});
4177
+ } catch {
4178
+ return [];
4179
+ }
4180
+ }
4148
4181
  async function recommendCommand(options) {
4149
4182
  const fingerprint = collectFingerprint(process.cwd());
4150
4183
  const platforms = detectLocalPlatforms();
4151
- const technologies = [
4184
+ const technologies = [...new Set([
4152
4185
  ...fingerprint.languages,
4153
- ...fingerprint.frameworks
4154
- ].filter(Boolean);
4186
+ ...fingerprint.frameworks,
4187
+ ...extractTopDeps()
4188
+ ].filter(Boolean))];
4155
4189
  if (technologies.length === 0) {
4156
- console.log(chalk7.yellow("Could not detect any languages or frameworks. Try running from a project root."));
4190
+ console.log(chalk7.yellow("Could not detect any languages or dependencies. Try running from a project root."));
4157
4191
  throw new Error("__exit__");
4158
4192
  }
4159
4193
  const spinner = ora4("Searching for skills...").start();
@@ -4350,7 +4384,7 @@ import chalk9 from "chalk";
4350
4384
  import ora5 from "ora";
4351
4385
 
4352
4386
  // src/lib/git-diff.ts
4353
- import { execSync as execSync5 } from "child_process";
4387
+ import { execSync as execSync6 } from "child_process";
4354
4388
  var MAX_DIFF_BYTES = 1e5;
4355
4389
  var DOC_PATTERNS = [
4356
4390
  "CLAUDE.md",
@@ -4364,7 +4398,7 @@ function excludeArgs() {
4364
4398
  }
4365
4399
  function safeExec(cmd) {
4366
4400
  try {
4367
- return execSync5(cmd, {
4401
+ return execSync6(cmd, {
4368
4402
  encoding: "utf-8",
4369
4403
  stdio: ["pipe", "pipe", "pipe"],
4370
4404
  maxBuffer: 10 * 1024 * 1024
@@ -5163,7 +5197,7 @@ learn.command("status").description("Show learning system status").action(learnS
5163
5197
  import fs25 from "fs";
5164
5198
  import path22 from "path";
5165
5199
  import { fileURLToPath as fileURLToPath2 } from "url";
5166
- import { execSync as execSync6 } from "child_process";
5200
+ import { execSync as execSync7 } from "child_process";
5167
5201
  import chalk13 from "chalk";
5168
5202
  import ora6 from "ora";
5169
5203
  import confirm2 from "@inquirer/confirm";
@@ -5173,7 +5207,7 @@ var pkg2 = JSON.parse(
5173
5207
  );
5174
5208
  function getInstalledVersion() {
5175
5209
  try {
5176
- const globalRoot = execSync6("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
5210
+ const globalRoot = execSync7("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
5177
5211
  const pkgPath = path22.join(globalRoot, "@rely-ai", "caliber", "package.json");
5178
5212
  return JSON.parse(fs25.readFileSync(pkgPath, "utf-8")).version;
5179
5213
  } catch {
@@ -5218,7 +5252,7 @@ Update available: ${current} -> ${latest}`)
5218
5252
  }
5219
5253
  const spinner = ora6("Updating caliber...").start();
5220
5254
  try {
5221
- execSync6(`npm install -g @rely-ai/caliber@${latest} --prefer-online`, { stdio: "pipe", timeout: 6e4 });
5255
+ execSync7(`npm install -g @rely-ai/caliber@${latest} --prefer-online`, { stdio: "pipe", timeout: 6e4 });
5222
5256
  const installed = getInstalledVersion();
5223
5257
  if (installed !== latest) {
5224
5258
  spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
@@ -5231,7 +5265,7 @@ Update available: ${current} -> ${latest}`)
5231
5265
  console.log(chalk13.dim(`
5232
5266
  Restarting: caliber ${args.join(" ")}
5233
5267
  `));
5234
- execSync6(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
5268
+ execSync7(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
5235
5269
  stdio: "inherit",
5236
5270
  env: { ...process.env, CALIBER_SKIP_UPDATE_CHECK: "1" }
5237
5271
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rely-ai/caliber",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Open-source CLI for configuring coding agent environments (CLAUDE.md, .cursorrules, skills). Bring your own LLM.",
5
5
  "type": "module",
6
6
  "bin": {