@rely-ai/caliber 0.5.1 → 0.5.3

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 +169 -64
  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
  }
@@ -4935,9 +4935,24 @@ async function recommendCommand(options) {
4935
4935
  } else {
4936
4936
  results = newCandidates.slice(0, 20);
4937
4937
  }
4938
- const selected = await interactiveSelect(results);
4938
+ const fetchSpinner = ora4("Verifying skill availability...").start();
4939
+ const contentMap = /* @__PURE__ */ new Map();
4940
+ await Promise.all(results.map(async (rec) => {
4941
+ const content = await fetchSkillContent(rec);
4942
+ if (content) contentMap.set(rec.slug, content);
4943
+ }));
4944
+ const available = results.filter((r) => contentMap.has(r.slug));
4945
+ if (!available.length) {
4946
+ fetchSpinner.fail("No installable skills found \u2014 content could not be fetched.");
4947
+ return;
4948
+ }
4949
+ const unavailableCount = results.length - available.length;
4950
+ fetchSpinner.succeed(
4951
+ `${available.length} installable skill${available.length > 1 ? "s" : ""}` + (unavailableCount > 0 ? chalk8.dim(` (${unavailableCount} unavailable)`) : "")
4952
+ );
4953
+ const selected = await interactiveSelect(available);
4939
4954
  if (selected?.length) {
4940
- await installSkills(selected, platforms);
4955
+ await installSkills(selected, platforms, contentMap);
4941
4956
  }
4942
4957
  }
4943
4958
  async function interactiveSelect(recs) {
@@ -5076,16 +5091,12 @@ async function fetchSkillContent(rec) {
5076
5091
  }
5077
5092
  return null;
5078
5093
  }
5079
- async function installSkills(recs, platforms) {
5094
+ async function installSkills(recs, platforms, contentMap) {
5080
5095
  const spinner = ora4(`Installing ${recs.length} skill${recs.length > 1 ? "s" : ""}...`).start();
5081
5096
  const installed = [];
5082
- const warnings = [];
5083
5097
  for (const rec of recs) {
5084
- const content = await fetchSkillContent(rec);
5085
- if (!content) {
5086
- warnings.push(`No content available for ${rec.name}`);
5087
- continue;
5088
- }
5098
+ const content = contentMap.get(rec.slug);
5099
+ if (!content) continue;
5089
5100
  for (const platform of platforms) {
5090
5101
  const skillPath = getSkillPath(platform, rec.slug);
5091
5102
  const fullPath = join8(process.cwd(), skillPath);
@@ -5102,9 +5113,6 @@ async function installSkills(recs, platforms) {
5102
5113
  } else {
5103
5114
  spinner.fail("No skills were installed");
5104
5115
  }
5105
- for (const w of warnings) {
5106
- console.log(chalk8.yellow(` \u26A0 ${w}`));
5107
- }
5108
5116
  console.log("");
5109
5117
  }
5110
5118
  function printRecommendations(recs) {
@@ -5457,60 +5465,162 @@ async function refreshCommand(options) {
5457
5465
 
5458
5466
  // src/commands/hooks.ts
5459
5467
  import chalk11 from "chalk";
5460
- async function hooksInstallCommand() {
5461
- const result = installHook();
5462
- if (result.alreadyInstalled) {
5463
- console.log(chalk11.dim("Claude Code hook already installed."));
5464
- return;
5468
+ var HOOKS = [
5469
+ {
5470
+ id: "session-end",
5471
+ label: "Claude Code SessionEnd",
5472
+ description: "Auto-refresh CLAUDE.md when a Claude Code session ends",
5473
+ isInstalled: isHookInstalled,
5474
+ install: installHook,
5475
+ remove: removeHook
5476
+ },
5477
+ {
5478
+ id: "pre-commit",
5479
+ label: "Git pre-commit",
5480
+ description: "Auto-refresh CLAUDE.md before each git commit",
5481
+ isInstalled: isPreCommitHookInstalled,
5482
+ install: installPreCommitHook,
5483
+ remove: removePreCommitHook
5465
5484
  }
5466
- console.log(chalk11.green("\u2713") + " SessionEnd hook installed in .claude/settings.json");
5467
- console.log(chalk11.dim(" Docs will auto-refresh when Claude Code sessions end."));
5468
- }
5469
- async function hooksRemoveCommand() {
5470
- const result = removeHook();
5471
- if (result.notFound) {
5472
- console.log(chalk11.dim("Claude Code hook not found."));
5473
- return;
5485
+ ];
5486
+ function printStatus() {
5487
+ console.log(chalk11.bold("\n Hooks\n"));
5488
+ for (const hook of HOOKS) {
5489
+ const installed = hook.isInstalled();
5490
+ const icon = installed ? chalk11.green("\u2713") : chalk11.dim("\u2717");
5491
+ const state = installed ? chalk11.green("enabled") : chalk11.dim("disabled");
5492
+ console.log(` ${icon} ${hook.label.padEnd(26)} ${state}`);
5493
+ console.log(chalk11.dim(` ${hook.description}`));
5474
5494
  }
5475
- console.log(chalk11.green("\u2713") + " SessionEnd hook removed from .claude/settings.json");
5495
+ console.log("");
5476
5496
  }
5477
- async function hooksInstallPrecommitCommand() {
5478
- const result = installPreCommitHook();
5479
- if (result.alreadyInstalled) {
5480
- console.log(chalk11.dim("Pre-commit hook already installed."));
5497
+ async function hooksCommand(options) {
5498
+ if (options.install) {
5499
+ for (const hook of HOOKS) {
5500
+ const result = hook.install();
5501
+ if (result.alreadyInstalled) {
5502
+ console.log(chalk11.dim(` ${hook.label} already enabled.`));
5503
+ } else {
5504
+ console.log(chalk11.green(" \u2713") + ` ${hook.label} enabled`);
5505
+ }
5506
+ }
5481
5507
  return;
5482
5508
  }
5483
- if (!result.installed) {
5484
- console.log(chalk11.red("Failed to install pre-commit hook (not a git repository?)."));
5509
+ if (options.remove) {
5510
+ for (const hook of HOOKS) {
5511
+ const result = hook.remove();
5512
+ if (result.notFound) {
5513
+ console.log(chalk11.dim(` ${hook.label} already disabled.`));
5514
+ } else {
5515
+ console.log(chalk11.green(" \u2713") + ` ${hook.label} removed`);
5516
+ }
5517
+ }
5485
5518
  return;
5486
5519
  }
5487
- console.log(chalk11.green("\u2713") + " Pre-commit hook installed in .git/hooks/pre-commit");
5488
- console.log(chalk11.dim(" Docs will auto-refresh before each commit via LLM."));
5489
- }
5490
- async function hooksRemovePrecommitCommand() {
5491
- const result = removePreCommitHook();
5492
- if (result.notFound) {
5493
- console.log(chalk11.dim("Pre-commit hook not found."));
5520
+ if (!process.stdin.isTTY) {
5521
+ printStatus();
5494
5522
  return;
5495
5523
  }
5496
- console.log(chalk11.green("\u2713") + " Pre-commit hook removed from .git/hooks/pre-commit");
5497
- }
5498
- async function hooksStatusCommand() {
5499
- const claudeInstalled = isHookInstalled();
5500
- const precommitInstalled = isPreCommitHookInstalled();
5501
- if (claudeInstalled) {
5502
- console.log(chalk11.green("\u2713") + " Claude Code hook is " + chalk11.green("installed"));
5503
- } else {
5504
- console.log(chalk11.dim("\u2717") + " Claude Code hook is " + chalk11.yellow("not installed"));
5505
- }
5506
- if (precommitInstalled) {
5507
- console.log(chalk11.green("\u2713") + " Pre-commit hook is " + chalk11.green("installed"));
5508
- } else {
5509
- console.log(chalk11.dim("\u2717") + " Pre-commit hook is " + chalk11.yellow("not installed"));
5524
+ const { stdin, stdout } = process;
5525
+ let cursor = 0;
5526
+ let lineCount = 0;
5527
+ const states = HOOKS.map((h) => h.isInstalled());
5528
+ function render() {
5529
+ const lines = [];
5530
+ lines.push(chalk11.bold(" Hooks"));
5531
+ lines.push("");
5532
+ for (let i = 0; i < HOOKS.length; i++) {
5533
+ const hook = HOOKS[i];
5534
+ const enabled = states[i];
5535
+ const toggle = enabled ? chalk11.green("[on] ") : chalk11.dim("[off]");
5536
+ const ptr = i === cursor ? chalk11.cyan(">") : " ";
5537
+ lines.push(` ${ptr} ${toggle} ${hook.label}`);
5538
+ lines.push(chalk11.dim(` ${hook.description}`));
5539
+ }
5540
+ lines.push("");
5541
+ lines.push(chalk11.dim(" \u2191\u2193 navigate \u23B5 toggle a all on n all off \u23CE apply q cancel"));
5542
+ return lines.join("\n");
5510
5543
  }
5511
- if (!claudeInstalled && !precommitInstalled) {
5512
- console.log(chalk11.dim("\n Run `caliber hooks install` or `caliber hooks install-precommit` to enable auto-refresh."));
5544
+ function draw(initial) {
5545
+ if (!initial && lineCount > 0) {
5546
+ stdout.write(`\x1B[${lineCount}A`);
5547
+ }
5548
+ stdout.write("\x1B[0J");
5549
+ const output = render();
5550
+ stdout.write(output + "\n");
5551
+ lineCount = output.split("\n").length;
5513
5552
  }
5553
+ return new Promise((resolve2) => {
5554
+ console.log("");
5555
+ draw(true);
5556
+ stdin.setRawMode(true);
5557
+ stdin.resume();
5558
+ stdin.setEncoding("utf8");
5559
+ function cleanup() {
5560
+ stdin.removeListener("data", onData);
5561
+ stdin.setRawMode(false);
5562
+ stdin.pause();
5563
+ }
5564
+ function apply() {
5565
+ let changed = 0;
5566
+ for (let i = 0; i < HOOKS.length; i++) {
5567
+ const hook = HOOKS[i];
5568
+ const wasInstalled = hook.isInstalled();
5569
+ const wantEnabled = states[i];
5570
+ if (wantEnabled && !wasInstalled) {
5571
+ hook.install();
5572
+ console.log(chalk11.green(" \u2713") + ` ${hook.label} enabled`);
5573
+ changed++;
5574
+ } else if (!wantEnabled && wasInstalled) {
5575
+ hook.remove();
5576
+ console.log(chalk11.green(" \u2713") + ` ${hook.label} disabled`);
5577
+ changed++;
5578
+ }
5579
+ }
5580
+ if (changed === 0) {
5581
+ console.log(chalk11.dim(" No changes."));
5582
+ }
5583
+ console.log("");
5584
+ }
5585
+ function onData(key) {
5586
+ switch (key) {
5587
+ case "\x1B[A":
5588
+ cursor = (cursor - 1 + HOOKS.length) % HOOKS.length;
5589
+ draw(false);
5590
+ break;
5591
+ case "\x1B[B":
5592
+ cursor = (cursor + 1) % HOOKS.length;
5593
+ draw(false);
5594
+ break;
5595
+ case " ":
5596
+ states[cursor] = !states[cursor];
5597
+ draw(false);
5598
+ break;
5599
+ case "a":
5600
+ states.fill(true);
5601
+ draw(false);
5602
+ break;
5603
+ case "n":
5604
+ states.fill(false);
5605
+ draw(false);
5606
+ break;
5607
+ case "\r":
5608
+ case "\n":
5609
+ cleanup();
5610
+ apply();
5611
+ resolve2();
5612
+ break;
5613
+ case "q":
5614
+ case "\x1B":
5615
+ case "":
5616
+ cleanup();
5617
+ console.log(chalk11.dim("\n Cancelled.\n"));
5618
+ resolve2();
5619
+ break;
5620
+ }
5621
+ }
5622
+ stdin.on("data", onData);
5623
+ });
5514
5624
  }
5515
5625
 
5516
5626
  // src/commands/config.ts
@@ -5910,12 +6020,7 @@ program.command("config").description("Configure LLM provider, API key, and mode
5910
6020
  program.command("recommend").description("Discover and install skill recommendations").option("--generate", "Force fresh recommendation search").action(recommendCommand);
5911
6021
  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);
5912
6022
  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);
5913
- var hooks = program.command("hooks").description("Manage auto-refresh hooks (Claude Code and git pre-commit)");
5914
- hooks.command("install").description("Install Claude Code SessionEnd auto-refresh hook").action(hooksInstallCommand);
5915
- hooks.command("remove").description("Remove Claude Code SessionEnd auto-refresh hook").action(hooksRemoveCommand);
5916
- hooks.command("install-precommit").description("Install git pre-commit hook for auto-refresh").action(hooksInstallPrecommitCommand);
5917
- hooks.command("remove-precommit").description("Remove git pre-commit hook").action(hooksRemovePrecommitCommand);
5918
- hooks.command("status").description("Check installed hooks status").action(hooksStatusCommand);
6023
+ 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);
5919
6024
  var learn = program.command("learn").description("Session learning \u2014 observe tool usage and extract reusable instructions");
5920
6025
  learn.command("observe").description("Record a tool event from stdin (called by hooks)").option("--failure", "Mark event as a tool failure").action(learnObserveCommand);
5921
6026
  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.1",
3
+ "version": "0.5.3",
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": {