@rely-ai/caliber 0.5.2 → 0.6.0

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 +162 -63
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -3555,10 +3555,10 @@ function checkBonus(dir) {
3555
3555
  if (settingsContent) {
3556
3556
  try {
3557
3557
  const settings = JSON.parse(settingsContent);
3558
- const hooks2 = settings.hooks;
3559
- if (hooks2 && Object.keys(hooks2).length > 0) {
3558
+ const hooks = settings.hooks;
3559
+ if (hooks && Object.keys(hooks).length > 0) {
3560
3560
  hasClaudeHooks = true;
3561
- hookSources.push(`Claude Code: ${Object.keys(hooks2).join(", ")}`);
3561
+ hookSources.push(`Claude Code: ${Object.keys(hooks).join(", ")}`);
3562
3562
  }
3563
3563
  } catch {
3564
3564
  }
@@ -3796,7 +3796,9 @@ function displayScoreDelta(before, after) {
3796
3796
 
3797
3797
  // src/commands/init.ts
3798
3798
  async function initCommand(options) {
3799
- console.log(chalk4.bold.hex("#6366f1")(`
3799
+ const brand = chalk4.hex("#EB9D83");
3800
+ const title = chalk4.hex("#83D1EB");
3801
+ console.log(brand.bold(`
3800
3802
  \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
3801
3803
  \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
3802
3804
  \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
@@ -3805,17 +3807,17 @@ async function initCommand(options) {
3805
3807
  \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
3806
3808
  `));
3807
3809
  console.log(chalk4.dim(" Configure your coding agent environment\n"));
3808
- console.log(chalk4.bold(" What is Caliber?\n"));
3810
+ console.log(title.bold(" What is Caliber?\n"));
3809
3811
  console.log(chalk4.dim(" Caliber audits your AI agent configurations and suggests targeted"));
3810
3812
  console.log(chalk4.dim(" improvements. It analyzes CLAUDE.md, .cursorrules, and skills"));
3811
3813
  console.log(chalk4.dim(" against your actual codebase \u2014 keeping what works, fixing"));
3812
3814
  console.log(chalk4.dim(" what's stale, and adding what's missing.\n"));
3813
- console.log(chalk4.bold(" How it works:\n"));
3815
+ console.log(title.bold(" How it works:\n"));
3814
3816
  console.log(chalk4.dim(" 1. Scan Analyze your code, dependencies, and existing configs"));
3815
3817
  console.log(chalk4.dim(" 2. Generate AI creates or improves config files for your project"));
3816
3818
  console.log(chalk4.dim(" 3. Review You accept, refine, or decline the proposed changes"));
3817
3819
  console.log(chalk4.dim(" 4. Apply Config files are written with backups\n"));
3818
- console.log(chalk4.hex("#6366f1").bold(" Step 1/4 \u2014 How do you want to use Caliber?\n"));
3820
+ console.log(title.bold(" Step 1/4 \u2014 How do you want to use Caliber?\n"));
3819
3821
  let config = loadConfig();
3820
3822
  if (!config) {
3821
3823
  console.log(chalk4.dim(" No LLM provider set yet. Choose how to run Caliber:\n"));
@@ -3837,7 +3839,7 @@ async function initCommand(options) {
3837
3839
  const displayModel = config.model === "default" && config.provider === "claude-cli" ? process.env.ANTHROPIC_MODEL || "default (inherited from Claude Code)" : config.model;
3838
3840
  console.log(chalk4.dim(` Provider: ${config.provider} | Model: ${displayModel}
3839
3841
  `));
3840
- console.log(chalk4.hex("#6366f1").bold(" Step 2/4 \u2014 Scan project\n"));
3842
+ console.log(title.bold(" Step 2/4 \u2014 Scan project\n"));
3841
3843
  console.log(chalk4.dim(" Detecting languages, dependencies, file structure, and existing configs.\n"));
3842
3844
  const spinner = ora("Analyzing project...").start();
3843
3845
  const fingerprint = collectFingerprint(process.cwd());
@@ -3848,7 +3850,7 @@ async function initCommand(options) {
3848
3850
  `));
3849
3851
  const targetAgent = options.agent || await promptAgent();
3850
3852
  const baselineScore = computeLocalScore(process.cwd(), targetAgent);
3851
- console.log(chalk4.hex("#6366f1").bold(" Current project score\n"));
3853
+ console.log(title.bold(" Current project score\n"));
3852
3854
  displayScore(baselineScore);
3853
3855
  const hasExistingConfig = !!(fingerprint.existingConfigs.claudeMd || fingerprint.existingConfigs.claudeSettings || fingerprint.existingConfigs.claudeSkills?.length || fingerprint.existingConfigs.cursorrules || fingerprint.existingConfigs.cursorRules?.length);
3854
3856
  if (hasExistingConfig && baselineScore.score === 100) {
@@ -3868,7 +3870,7 @@ async function initCommand(options) {
3868
3870
  passingChecks = baselineScore.checks.filter((c) => c.passed).map((c) => ({ name: c.name }));
3869
3871
  currentScore = baselineScore.score;
3870
3872
  if (failingChecks.length > 0) {
3871
- console.log(chalk4.hex("#6366f1").bold(" Step 3/4 \u2014 Targeted fixes\n"));
3873
+ console.log(title.bold(" Step 3/4 \u2014 Targeted fixes\n"));
3872
3874
  console.log(chalk4.dim(` Score is ${baselineScore.score}/100 \u2014 only fixing ${failingChecks.length} remaining issue${failingChecks.length === 1 ? "" : "s"}:
3873
3875
  `));
3874
3876
  for (const check of failingChecks) {
@@ -3877,11 +3879,11 @@ async function initCommand(options) {
3877
3879
  console.log("");
3878
3880
  }
3879
3881
  } else if (hasExistingConfig) {
3880
- console.log(chalk4.hex("#6366f1").bold(" Step 3/4 \u2014 Auditing your configs\n"));
3882
+ console.log(title.bold(" Step 3/4 \u2014 Auditing your configs\n"));
3881
3883
  console.log(chalk4.dim(" AI is reviewing your existing configs against your codebase"));
3882
3884
  console.log(chalk4.dim(" and suggesting improvements.\n"));
3883
3885
  } else {
3884
- console.log(chalk4.hex("#6366f1").bold(" Step 3/4 \u2014 Generating configs\n"));
3886
+ console.log(title.bold(" Step 3/4 \u2014 Generating configs\n"));
3885
3887
  console.log(chalk4.dim(" AI is creating agent config files tailored to your project.\n"));
3886
3888
  }
3887
3889
  console.log(chalk4.dim(" This can take a couple of minutes depending on your model and provider.\n"));
@@ -3937,7 +3939,7 @@ async function initCommand(options) {
3937
3939
  const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
3938
3940
  genSpinner.succeed(`Setup generated ${chalk4.dim(`in ${timeStr}`)}`);
3939
3941
  printSetupSummary(generatedSetup);
3940
- console.log(chalk4.hex("#6366f1").bold(" Step 4/4 \u2014 Review\n"));
3942
+ console.log(title.bold(" Step 4/4 \u2014 Review\n"));
3941
3943
  const setupFiles = collectSetupFiles(generatedSetup);
3942
3944
  const staged = stageFiles(setupFiles, process.cwd());
3943
3945
  console.log(chalk4.dim(` ${chalk4.green(`${staged.newFiles} new`)} / ${chalk4.yellow(`${staged.modifiedFiles} modified`)} file${staged.newFiles + staged.modifiedFiles !== 1 ? "s" : ""}
@@ -4061,7 +4063,7 @@ async function initCommand(options) {
4061
4063
  console.log(chalk4.bold.green(" Setup complete! Your coding agent is now configured."));
4062
4064
  console.log(chalk4.dim(" Run `caliber undo` to revert changes.\n"));
4063
4065
  console.log(chalk4.bold(" Next steps:\n"));
4064
- console.log(` ${chalk4.hex("#6366f1")("caliber undo")} Revert all changes from this run`);
4066
+ console.log(` ${title("caliber undo")} Revert all changes from this run`);
4065
4067
  console.log("");
4066
4068
  }
4067
4069
  async function refineLoop(currentSetup, _targetAgent) {
@@ -5465,60 +5467,162 @@ async function refreshCommand(options) {
5465
5467
 
5466
5468
  // src/commands/hooks.ts
5467
5469
  import chalk11 from "chalk";
5468
- async function hooksInstallCommand() {
5469
- const result = installHook();
5470
- if (result.alreadyInstalled) {
5471
- console.log(chalk11.dim("Claude Code hook already installed."));
5472
- return;
5470
+ var HOOKS = [
5471
+ {
5472
+ id: "session-end",
5473
+ label: "Claude Code SessionEnd",
5474
+ description: "Auto-refresh CLAUDE.md when a Claude Code session ends",
5475
+ isInstalled: isHookInstalled,
5476
+ install: installHook,
5477
+ remove: removeHook
5478
+ },
5479
+ {
5480
+ id: "pre-commit",
5481
+ label: "Git pre-commit",
5482
+ description: "Auto-refresh CLAUDE.md before each git commit",
5483
+ isInstalled: isPreCommitHookInstalled,
5484
+ install: installPreCommitHook,
5485
+ remove: removePreCommitHook
5473
5486
  }
5474
- console.log(chalk11.green("\u2713") + " SessionEnd hook installed in .claude/settings.json");
5475
- console.log(chalk11.dim(" Docs will auto-refresh when Claude Code sessions end."));
5476
- }
5477
- async function hooksRemoveCommand() {
5478
- const result = removeHook();
5479
- if (result.notFound) {
5480
- console.log(chalk11.dim("Claude Code hook not found."));
5481
- return;
5487
+ ];
5488
+ function printStatus() {
5489
+ console.log(chalk11.bold("\n Hooks\n"));
5490
+ for (const hook of HOOKS) {
5491
+ const installed = hook.isInstalled();
5492
+ const icon = installed ? chalk11.green("\u2713") : chalk11.dim("\u2717");
5493
+ const state = installed ? chalk11.green("enabled") : chalk11.dim("disabled");
5494
+ console.log(` ${icon} ${hook.label.padEnd(26)} ${state}`);
5495
+ console.log(chalk11.dim(` ${hook.description}`));
5482
5496
  }
5483
- console.log(chalk11.green("\u2713") + " SessionEnd hook removed from .claude/settings.json");
5497
+ console.log("");
5484
5498
  }
5485
- async function hooksInstallPrecommitCommand() {
5486
- const result = installPreCommitHook();
5487
- if (result.alreadyInstalled) {
5488
- console.log(chalk11.dim("Pre-commit hook already installed."));
5499
+ async function hooksCommand(options) {
5500
+ if (options.install) {
5501
+ for (const hook of HOOKS) {
5502
+ const result = hook.install();
5503
+ if (result.alreadyInstalled) {
5504
+ console.log(chalk11.dim(` ${hook.label} already enabled.`));
5505
+ } else {
5506
+ console.log(chalk11.green(" \u2713") + ` ${hook.label} enabled`);
5507
+ }
5508
+ }
5489
5509
  return;
5490
5510
  }
5491
- if (!result.installed) {
5492
- console.log(chalk11.red("Failed to install pre-commit hook (not a git repository?)."));
5511
+ if (options.remove) {
5512
+ for (const hook of HOOKS) {
5513
+ const result = hook.remove();
5514
+ if (result.notFound) {
5515
+ console.log(chalk11.dim(` ${hook.label} already disabled.`));
5516
+ } else {
5517
+ console.log(chalk11.green(" \u2713") + ` ${hook.label} removed`);
5518
+ }
5519
+ }
5493
5520
  return;
5494
5521
  }
5495
- console.log(chalk11.green("\u2713") + " Pre-commit hook installed in .git/hooks/pre-commit");
5496
- console.log(chalk11.dim(" Docs will auto-refresh before each commit via LLM."));
5497
- }
5498
- async function hooksRemovePrecommitCommand() {
5499
- const result = removePreCommitHook();
5500
- if (result.notFound) {
5501
- console.log(chalk11.dim("Pre-commit hook not found."));
5522
+ if (!process.stdin.isTTY) {
5523
+ printStatus();
5502
5524
  return;
5503
5525
  }
5504
- console.log(chalk11.green("\u2713") + " Pre-commit hook removed from .git/hooks/pre-commit");
5505
- }
5506
- async function hooksStatusCommand() {
5507
- const claudeInstalled = isHookInstalled();
5508
- const precommitInstalled = isPreCommitHookInstalled();
5509
- if (claudeInstalled) {
5510
- console.log(chalk11.green("\u2713") + " Claude Code hook is " + chalk11.green("installed"));
5511
- } else {
5512
- console.log(chalk11.dim("\u2717") + " Claude Code hook is " + chalk11.yellow("not installed"));
5513
- }
5514
- if (precommitInstalled) {
5515
- console.log(chalk11.green("\u2713") + " Pre-commit hook is " + chalk11.green("installed"));
5516
- } else {
5517
- console.log(chalk11.dim("\u2717") + " Pre-commit hook is " + chalk11.yellow("not installed"));
5526
+ const { stdin, stdout } = process;
5527
+ let cursor = 0;
5528
+ let lineCount = 0;
5529
+ const states = HOOKS.map((h) => h.isInstalled());
5530
+ function render() {
5531
+ const lines = [];
5532
+ lines.push(chalk11.bold(" Hooks"));
5533
+ lines.push("");
5534
+ for (let i = 0; i < HOOKS.length; i++) {
5535
+ const hook = HOOKS[i];
5536
+ const enabled = states[i];
5537
+ const toggle = enabled ? chalk11.green("[on] ") : chalk11.dim("[off]");
5538
+ const ptr = i === cursor ? chalk11.cyan(">") : " ";
5539
+ lines.push(` ${ptr} ${toggle} ${hook.label}`);
5540
+ lines.push(chalk11.dim(` ${hook.description}`));
5541
+ }
5542
+ lines.push("");
5543
+ lines.push(chalk11.dim(" \u2191\u2193 navigate \u23B5 toggle a all on n all off \u23CE apply q cancel"));
5544
+ return lines.join("\n");
5518
5545
  }
5519
- if (!claudeInstalled && !precommitInstalled) {
5520
- console.log(chalk11.dim("\n Run `caliber hooks install` or `caliber hooks install-precommit` to enable auto-refresh."));
5546
+ function draw(initial) {
5547
+ if (!initial && lineCount > 0) {
5548
+ stdout.write(`\x1B[${lineCount}A`);
5549
+ }
5550
+ stdout.write("\x1B[0J");
5551
+ const output = render();
5552
+ stdout.write(output + "\n");
5553
+ lineCount = output.split("\n").length;
5521
5554
  }
5555
+ return new Promise((resolve2) => {
5556
+ console.log("");
5557
+ draw(true);
5558
+ stdin.setRawMode(true);
5559
+ stdin.resume();
5560
+ stdin.setEncoding("utf8");
5561
+ function cleanup() {
5562
+ stdin.removeListener("data", onData);
5563
+ stdin.setRawMode(false);
5564
+ stdin.pause();
5565
+ }
5566
+ function apply() {
5567
+ let changed = 0;
5568
+ for (let i = 0; i < HOOKS.length; i++) {
5569
+ const hook = HOOKS[i];
5570
+ const wasInstalled = hook.isInstalled();
5571
+ const wantEnabled = states[i];
5572
+ if (wantEnabled && !wasInstalled) {
5573
+ hook.install();
5574
+ console.log(chalk11.green(" \u2713") + ` ${hook.label} enabled`);
5575
+ changed++;
5576
+ } else if (!wantEnabled && wasInstalled) {
5577
+ hook.remove();
5578
+ console.log(chalk11.green(" \u2713") + ` ${hook.label} disabled`);
5579
+ changed++;
5580
+ }
5581
+ }
5582
+ if (changed === 0) {
5583
+ console.log(chalk11.dim(" No changes."));
5584
+ }
5585
+ console.log("");
5586
+ }
5587
+ function onData(key) {
5588
+ switch (key) {
5589
+ case "\x1B[A":
5590
+ cursor = (cursor - 1 + HOOKS.length) % HOOKS.length;
5591
+ draw(false);
5592
+ break;
5593
+ case "\x1B[B":
5594
+ cursor = (cursor + 1) % HOOKS.length;
5595
+ draw(false);
5596
+ break;
5597
+ case " ":
5598
+ states[cursor] = !states[cursor];
5599
+ draw(false);
5600
+ break;
5601
+ case "a":
5602
+ states.fill(true);
5603
+ draw(false);
5604
+ break;
5605
+ case "n":
5606
+ states.fill(false);
5607
+ draw(false);
5608
+ break;
5609
+ case "\r":
5610
+ case "\n":
5611
+ cleanup();
5612
+ apply();
5613
+ resolve2();
5614
+ break;
5615
+ case "q":
5616
+ case "\x1B":
5617
+ case "":
5618
+ cleanup();
5619
+ console.log(chalk11.dim("\n Cancelled.\n"));
5620
+ resolve2();
5621
+ break;
5622
+ }
5623
+ }
5624
+ stdin.on("data", onData);
5625
+ });
5522
5626
  }
5523
5627
 
5524
5628
  // src/commands/config.ts
@@ -5918,12 +6022,7 @@ program.command("config").description("Configure LLM provider, API key, and mode
5918
6022
  program.command("recommend").description("Discover and install skill recommendations").option("--generate", "Force fresh recommendation search").action(recommendCommand);
5919
6023
  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, or both").action(scoreCommand);
5920
6024
  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);
5921
- var hooks = program.command("hooks").description("Manage auto-refresh hooks (Claude Code and git pre-commit)");
5922
- hooks.command("install").description("Install Claude Code SessionEnd auto-refresh hook").action(hooksInstallCommand);
5923
- hooks.command("remove").description("Remove Claude Code SessionEnd auto-refresh hook").action(hooksRemoveCommand);
5924
- hooks.command("install-precommit").description("Install git pre-commit hook for auto-refresh").action(hooksInstallPrecommitCommand);
5925
- hooks.command("remove-precommit").description("Remove git pre-commit hook").action(hooksRemovePrecommitCommand);
5926
- hooks.command("status").description("Check installed hooks status").action(hooksStatusCommand);
6025
+ 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);
5927
6026
  var learn = program.command("learn").description("Session learning \u2014 observe tool usage and extract reusable instructions");
5928
6027
  learn.command("observe").description("Record a tool event from stdin (called by hooks)").option("--failure", "Mark event as a tool failure").action(learnObserveCommand);
5929
6028
  learn.command("finalize").description("Analyze session events and update CLAUDE.md (called on SessionEnd)").action(learnFinalizeCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rely-ai/caliber",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
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": {