@rely-ai/caliber 1.25.0-dev.1773915517 → 1.25.0-dev.1773925407

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 +409 -440
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -271,20 +271,20 @@ var init_resolve_caliber = __esm({
271
271
  });
272
272
 
273
273
  // src/utils/editor.ts
274
- import { execSync as execSync13, spawn as spawn3 } from "child_process";
275
- import fs26 from "fs";
276
- import path22 from "path";
274
+ import { execSync as execSync12, spawn as spawn3 } from "child_process";
275
+ import fs25 from "fs";
276
+ import path21 from "path";
277
277
  import os5 from "os";
278
278
  function getEmptyFilePath(proposedPath) {
279
- fs26.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
280
- const tempPath = path22.join(DIFF_TEMP_DIR, path22.basename(proposedPath));
281
- fs26.writeFileSync(tempPath, "");
279
+ fs25.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
280
+ const tempPath = path21.join(DIFF_TEMP_DIR, path21.basename(proposedPath));
281
+ fs25.writeFileSync(tempPath, "");
282
282
  return tempPath;
283
283
  }
284
284
  function commandExists(cmd) {
285
285
  try {
286
286
  const check = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
287
- execSync13(check, { stdio: "ignore" });
287
+ execSync12(check, { stdio: "ignore" });
288
288
  return true;
289
289
  } catch {
290
290
  return false;
@@ -318,7 +318,7 @@ var init_editor = __esm({
318
318
  "src/utils/editor.ts"() {
319
319
  "use strict";
320
320
  IS_WINDOWS3 = process.platform === "win32";
321
- DIFF_TEMP_DIR = path22.join(os5.tmpdir(), "caliber-diff");
321
+ DIFF_TEMP_DIR = path21.join(os5.tmpdir(), "caliber-diff");
322
322
  }
323
323
  });
324
324
 
@@ -330,7 +330,7 @@ __export(review_exports, {
330
330
  promptWantsReview: () => promptWantsReview
331
331
  });
332
332
  import chalk10 from "chalk";
333
- import fs27 from "fs";
333
+ import fs26 from "fs";
334
334
  import select4 from "@inquirer/select";
335
335
  import { createTwoFilesPatch } from "diff";
336
336
  async function promptWantsReview() {
@@ -367,8 +367,8 @@ async function openReview(method, stagedFiles) {
367
367
  return;
368
368
  }
369
369
  const fileInfos = stagedFiles.map((file) => {
370
- const proposed = fs27.readFileSync(file.proposedPath, "utf-8");
371
- const current = file.currentPath ? fs27.readFileSync(file.currentPath, "utf-8") : "";
370
+ const proposed = fs26.readFileSync(file.proposedPath, "utf-8");
371
+ const current = file.currentPath ? fs26.readFileSync(file.currentPath, "utf-8") : "";
372
372
  const patch = createTwoFilesPatch(
373
373
  file.isNew ? "/dev/null" : file.relativePath,
374
374
  file.relativePath,
@@ -543,13 +543,13 @@ __export(lock_exports, {
543
543
  isCaliberRunning: () => isCaliberRunning,
544
544
  releaseLock: () => releaseLock
545
545
  });
546
- import fs36 from "fs";
547
- import path28 from "path";
546
+ import fs35 from "fs";
547
+ import path27 from "path";
548
548
  import os7 from "os";
549
549
  function isCaliberRunning() {
550
550
  try {
551
- if (!fs36.existsSync(LOCK_FILE)) return false;
552
- const raw = fs36.readFileSync(LOCK_FILE, "utf-8").trim();
551
+ if (!fs35.existsSync(LOCK_FILE)) return false;
552
+ const raw = fs35.readFileSync(LOCK_FILE, "utf-8").trim();
553
553
  const { pid, ts } = JSON.parse(raw);
554
554
  if (Date.now() - ts > STALE_MS) return false;
555
555
  try {
@@ -564,13 +564,13 @@ function isCaliberRunning() {
564
564
  }
565
565
  function acquireLock() {
566
566
  try {
567
- fs36.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
567
+ fs35.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
568
568
  } catch {
569
569
  }
570
570
  }
571
571
  function releaseLock() {
572
572
  try {
573
- if (fs36.existsSync(LOCK_FILE)) fs36.unlinkSync(LOCK_FILE);
573
+ if (fs35.existsSync(LOCK_FILE)) fs35.unlinkSync(LOCK_FILE);
574
574
  } catch {
575
575
  }
576
576
  }
@@ -578,7 +578,7 @@ var LOCK_FILE, STALE_MS;
578
578
  var init_lock = __esm({
579
579
  "src/lib/lock.ts"() {
580
580
  "use strict";
581
- LOCK_FILE = path28.join(os7.tmpdir(), ".caliber.lock");
581
+ LOCK_FILE = path27.join(os7.tmpdir(), ".caliber.lock");
582
582
  STALE_MS = 10 * 60 * 1e3;
583
583
  }
584
584
  });
@@ -590,9 +590,9 @@ import path35 from "path";
590
590
  import { fileURLToPath } from "url";
591
591
 
592
592
  // src/commands/init.ts
593
- import path24 from "path";
593
+ import path23 from "path";
594
594
  import chalk14 from "chalk";
595
- import fs31 from "fs";
595
+ import fs30 from "fs";
596
596
 
597
597
  // src/fingerprint/index.ts
598
598
  import fs7 from "fs";
@@ -4012,9 +4012,45 @@ import fs15 from "fs";
4012
4012
  // src/writers/claude/index.ts
4013
4013
  import fs9 from "fs";
4014
4014
  import path9 from "path";
4015
+
4016
+ // src/writers/pre-commit-block.ts
4017
+ var BLOCK_START = "<!-- caliber:managed:pre-commit -->";
4018
+ var BLOCK_END = "<!-- /caliber:managed:pre-commit -->";
4019
+ var MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .github/copilot-instructions.md AGENTS.md CALIBER_LEARNINGS.md";
4020
+ var PRE_COMMIT_BLOCK = `${BLOCK_START}
4021
+ ## Before Committing
4022
+
4023
+ Run \`caliber refresh\` before creating git commits to keep docs in sync with code changes.
4024
+ After it completes, stage any modified doc files before committing:
4025
+
4026
+ \`\`\`bash
4027
+ caliber refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null
4028
+ \`\`\`
4029
+ ${BLOCK_END}`;
4030
+ var CURSOR_RULE_FILENAME = "caliber-pre-commit.mdc";
4031
+ var CURSOR_RULE_CONTENT = `---
4032
+ description: Run caliber refresh before git commits to keep docs in sync
4033
+ alwaysApply: true
4034
+ ---
4035
+ Before creating git commits, run \`caliber refresh\` to update docs based on code changes,
4036
+ then stage modified doc files (${MANAGED_DOC_PATHS}).
4037
+ `;
4038
+ function hasPreCommitBlock(content) {
4039
+ return content.includes(BLOCK_START);
4040
+ }
4041
+ function appendPreCommitBlock(content) {
4042
+ if (hasPreCommitBlock(content)) return content;
4043
+ const trimmed = content.trimEnd();
4044
+ return trimmed + "\n\n" + PRE_COMMIT_BLOCK + "\n";
4045
+ }
4046
+ function getCursorPreCommitRule() {
4047
+ return { filename: CURSOR_RULE_FILENAME, content: CURSOR_RULE_CONTENT };
4048
+ }
4049
+
4050
+ // src/writers/claude/index.ts
4015
4051
  function writeClaudeConfig(config) {
4016
4052
  const written = [];
4017
- fs9.writeFileSync("CLAUDE.md", config.claudeMd);
4053
+ fs9.writeFileSync("CLAUDE.md", appendPreCommitBlock(config.claudeMd));
4018
4054
  written.push("CLAUDE.md");
4019
4055
  if (config.skills?.length) {
4020
4056
  for (const skill of config.skills) {
@@ -4057,14 +4093,14 @@ function writeCursorConfig(config) {
4057
4093
  fs10.writeFileSync(".cursorrules", config.cursorrules);
4058
4094
  written.push(".cursorrules");
4059
4095
  }
4060
- if (config.rules?.length) {
4061
- const rulesDir = path10.join(".cursor", "rules");
4062
- if (!fs10.existsSync(rulesDir)) fs10.mkdirSync(rulesDir, { recursive: true });
4063
- for (const rule of config.rules) {
4064
- const rulePath = path10.join(rulesDir, rule.filename);
4065
- fs10.writeFileSync(rulePath, rule.content);
4066
- written.push(rulePath);
4067
- }
4096
+ const preCommitRule = getCursorPreCommitRule();
4097
+ const allRules = [...config.rules || [], preCommitRule];
4098
+ const rulesDir = path10.join(".cursor", "rules");
4099
+ if (!fs10.existsSync(rulesDir)) fs10.mkdirSync(rulesDir, { recursive: true });
4100
+ for (const rule of allRules) {
4101
+ const rulePath = path10.join(rulesDir, rule.filename);
4102
+ fs10.writeFileSync(rulePath, rule.content);
4103
+ written.push(rulePath);
4068
4104
  }
4069
4105
  if (config.skills?.length) {
4070
4106
  for (const skill of config.skills) {
@@ -4106,7 +4142,7 @@ import fs11 from "fs";
4106
4142
  import path11 from "path";
4107
4143
  function writeCodexConfig(config) {
4108
4144
  const written = [];
4109
- fs11.writeFileSync("AGENTS.md", config.agentsMd);
4145
+ fs11.writeFileSync("AGENTS.md", appendPreCommitBlock(config.agentsMd));
4110
4146
  written.push("AGENTS.md");
4111
4147
  if (config.skills?.length) {
4112
4148
  for (const skill of config.skills) {
@@ -4134,7 +4170,7 @@ function writeGithubCopilotConfig(config) {
4134
4170
  const written = [];
4135
4171
  if (config.instructions) {
4136
4172
  fs12.mkdirSync(".github", { recursive: true });
4137
- fs12.writeFileSync(path12.join(".github", "copilot-instructions.md"), config.instructions);
4173
+ fs12.writeFileSync(path12.join(".github", "copilot-instructions.md"), appendPreCommitBlock(config.instructions));
4138
4174
  written.push(".github/copilot-instructions.md");
4139
4175
  }
4140
4176
  if (config.instructionFiles?.length) {
@@ -4438,150 +4474,11 @@ ${agentRefs.join(" ")}
4438
4474
  return files;
4439
4475
  }
4440
4476
 
4441
- // src/lib/hooks.ts
4477
+ // src/lib/learning-hooks.ts
4442
4478
  init_resolve_caliber();
4443
4479
  import fs19 from "fs";
4444
4480
  import path15 from "path";
4445
- import { execSync as execSync7 } from "child_process";
4446
4481
  var SETTINGS_PATH = path15.join(".claude", "settings.json");
4447
- var REFRESH_TAIL = "refresh --quiet";
4448
- var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
4449
- function getHookCommand() {
4450
- return `${resolveCaliber()} ${REFRESH_TAIL}`;
4451
- }
4452
- function readSettings() {
4453
- if (!fs19.existsSync(SETTINGS_PATH)) return {};
4454
- try {
4455
- return JSON.parse(fs19.readFileSync(SETTINGS_PATH, "utf-8"));
4456
- } catch {
4457
- return {};
4458
- }
4459
- }
4460
- function writeSettings(settings) {
4461
- const dir = path15.dirname(SETTINGS_PATH);
4462
- if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
4463
- fs19.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
4464
- }
4465
- function findHookIndex(sessionEnd) {
4466
- return sessionEnd.findIndex(
4467
- (entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, REFRESH_TAIL))
4468
- );
4469
- }
4470
- function isHookInstalled() {
4471
- const settings = readSettings();
4472
- const sessionEnd = settings.hooks?.SessionEnd;
4473
- if (!Array.isArray(sessionEnd)) return false;
4474
- return findHookIndex(sessionEnd) !== -1;
4475
- }
4476
- function installHook() {
4477
- const settings = readSettings();
4478
- if (!settings.hooks) settings.hooks = {};
4479
- if (!Array.isArray(settings.hooks.SessionEnd)) settings.hooks.SessionEnd = [];
4480
- if (findHookIndex(settings.hooks.SessionEnd) !== -1) {
4481
- return { installed: false, alreadyInstalled: true };
4482
- }
4483
- settings.hooks.SessionEnd.push({
4484
- matcher: "",
4485
- hooks: [{ type: "command", command: getHookCommand(), description: HOOK_DESCRIPTION }]
4486
- });
4487
- writeSettings(settings);
4488
- return { installed: true, alreadyInstalled: false };
4489
- }
4490
- function removeHook() {
4491
- const settings = readSettings();
4492
- const sessionEnd = settings.hooks?.SessionEnd;
4493
- if (!Array.isArray(sessionEnd)) {
4494
- return { removed: false, notFound: true };
4495
- }
4496
- const idx = findHookIndex(sessionEnd);
4497
- if (idx === -1) {
4498
- return { removed: false, notFound: true };
4499
- }
4500
- sessionEnd.splice(idx, 1);
4501
- if (sessionEnd.length === 0) {
4502
- delete settings.hooks.SessionEnd;
4503
- }
4504
- if (settings.hooks && Object.keys(settings.hooks).length === 0) {
4505
- delete settings.hooks;
4506
- }
4507
- writeSettings(settings);
4508
- return { removed: true, notFound: false };
4509
- }
4510
- var PRECOMMIT_START = "# caliber:pre-commit:start";
4511
- var PRECOMMIT_END = "# caliber:pre-commit:end";
4512
- function getPrecommitBlock() {
4513
- const bin = resolveCaliber();
4514
- return `${PRECOMMIT_START}
4515
- if [ -x "${bin}" ] || command -v "${bin}" >/dev/null 2>&1; then
4516
- echo "\\033[2mcaliber: refreshing docs...\\033[0m"
4517
- "${bin}" refresh 2>/dev/null || true
4518
- "${bin}" learn finalize 2>/dev/null || true
4519
- git diff --name-only -- CLAUDE.md .claude/ .cursor/ AGENTS.md CALIBER_LEARNINGS.md 2>/dev/null | xargs git add 2>/dev/null || true
4520
- fi
4521
- ${PRECOMMIT_END}`;
4522
- }
4523
- function getGitHooksDir() {
4524
- try {
4525
- const gitDir = execSync7("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
4526
- return path15.join(gitDir, "hooks");
4527
- } catch {
4528
- return null;
4529
- }
4530
- }
4531
- function getPreCommitPath() {
4532
- const hooksDir = getGitHooksDir();
4533
- return hooksDir ? path15.join(hooksDir, "pre-commit") : null;
4534
- }
4535
- function isPreCommitHookInstalled() {
4536
- const hookPath = getPreCommitPath();
4537
- if (!hookPath || !fs19.existsSync(hookPath)) return false;
4538
- const content = fs19.readFileSync(hookPath, "utf-8");
4539
- return content.includes(PRECOMMIT_START);
4540
- }
4541
- function installPreCommitHook() {
4542
- if (isPreCommitHookInstalled()) {
4543
- return { installed: false, alreadyInstalled: true };
4544
- }
4545
- const hookPath = getPreCommitPath();
4546
- if (!hookPath) return { installed: false, alreadyInstalled: false };
4547
- const hooksDir = path15.dirname(hookPath);
4548
- if (!fs19.existsSync(hooksDir)) fs19.mkdirSync(hooksDir, { recursive: true });
4549
- let content = "";
4550
- if (fs19.existsSync(hookPath)) {
4551
- content = fs19.readFileSync(hookPath, "utf-8");
4552
- if (!content.endsWith("\n")) content += "\n";
4553
- content += "\n" + getPrecommitBlock() + "\n";
4554
- } else {
4555
- content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
4556
- }
4557
- fs19.writeFileSync(hookPath, content);
4558
- fs19.chmodSync(hookPath, 493);
4559
- return { installed: true, alreadyInstalled: false };
4560
- }
4561
- function removePreCommitHook() {
4562
- const hookPath = getPreCommitPath();
4563
- if (!hookPath || !fs19.existsSync(hookPath)) {
4564
- return { removed: false, notFound: true };
4565
- }
4566
- let content = fs19.readFileSync(hookPath, "utf-8");
4567
- if (!content.includes(PRECOMMIT_START)) {
4568
- return { removed: false, notFound: true };
4569
- }
4570
- const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
4571
- content = content.replace(regex, "\n");
4572
- if (content.trim() === "#!/bin/sh" || content.trim() === "") {
4573
- fs19.unlinkSync(hookPath);
4574
- } else {
4575
- fs19.writeFileSync(hookPath, content);
4576
- }
4577
- return { removed: true, notFound: false };
4578
- }
4579
-
4580
- // src/lib/learning-hooks.ts
4581
- init_resolve_caliber();
4582
- import fs20 from "fs";
4583
- import path16 from "path";
4584
- var SETTINGS_PATH2 = path16.join(".claude", "settings.json");
4585
4482
  var HOOK_TAILS = [
4586
4483
  { event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
4587
4484
  { event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
@@ -4597,24 +4494,24 @@ function getHookConfigs() {
4597
4494
  description
4598
4495
  }));
4599
4496
  }
4600
- function readSettings2() {
4601
- if (!fs20.existsSync(SETTINGS_PATH2)) return {};
4497
+ function readSettings() {
4498
+ if (!fs19.existsSync(SETTINGS_PATH)) return {};
4602
4499
  try {
4603
- return JSON.parse(fs20.readFileSync(SETTINGS_PATH2, "utf-8"));
4500
+ return JSON.parse(fs19.readFileSync(SETTINGS_PATH, "utf-8"));
4604
4501
  } catch {
4605
4502
  return {};
4606
4503
  }
4607
4504
  }
4608
- function writeSettings2(settings) {
4609
- const dir = path16.dirname(SETTINGS_PATH2);
4610
- if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
4611
- fs20.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
4505
+ function writeSettings(settings) {
4506
+ const dir = path15.dirname(SETTINGS_PATH);
4507
+ if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
4508
+ fs19.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
4612
4509
  }
4613
4510
  function hasLearningHook(matchers, tail) {
4614
4511
  return matchers.some((entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, tail)));
4615
4512
  }
4616
4513
  function areLearningHooksInstalled() {
4617
- const settings = readSettings2();
4514
+ const settings = readSettings();
4618
4515
  if (!settings.hooks) return false;
4619
4516
  return HOOK_TAILS.every((cfg) => {
4620
4517
  const matchers = settings.hooks[cfg.event];
@@ -4625,7 +4522,7 @@ function installLearningHooks() {
4625
4522
  if (areLearningHooksInstalled()) {
4626
4523
  return { installed: false, alreadyInstalled: true };
4627
4524
  }
4628
- const settings = readSettings2();
4525
+ const settings = readSettings();
4629
4526
  if (!settings.hooks) settings.hooks = {};
4630
4527
  const configs = getHookConfigs();
4631
4528
  for (const cfg of configs) {
@@ -4639,10 +4536,10 @@ function installLearningHooks() {
4639
4536
  });
4640
4537
  }
4641
4538
  }
4642
- writeSettings2(settings);
4539
+ writeSettings(settings);
4643
4540
  return { installed: true, alreadyInstalled: false };
4644
4541
  }
4645
- var CURSOR_HOOKS_PATH = path16.join(".cursor", "hooks.json");
4542
+ var CURSOR_HOOKS_PATH = path15.join(".cursor", "hooks.json");
4646
4543
  var CURSOR_HOOK_EVENTS = [
4647
4544
  { event: "postToolUse", tail: "learn observe" },
4648
4545
  { event: "postToolUseFailure", tail: "learn observe --failure" },
@@ -4650,17 +4547,17 @@ var CURSOR_HOOK_EVENTS = [
4650
4547
  { event: "sessionEnd", tail: "learn finalize --auto" }
4651
4548
  ];
4652
4549
  function readCursorHooks() {
4653
- if (!fs20.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
4550
+ if (!fs19.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
4654
4551
  try {
4655
- return JSON.parse(fs20.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
4552
+ return JSON.parse(fs19.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
4656
4553
  } catch {
4657
4554
  return { version: 1, hooks: {} };
4658
4555
  }
4659
4556
  }
4660
4557
  function writeCursorHooks(config) {
4661
- const dir = path16.dirname(CURSOR_HOOKS_PATH);
4662
- if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
4663
- fs20.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
4558
+ const dir = path15.dirname(CURSOR_HOOKS_PATH);
4559
+ if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
4560
+ fs19.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
4664
4561
  }
4665
4562
  function hasCursorHook(entries, tail) {
4666
4563
  return entries.some((e) => isCaliberCommand(e.command, tail));
@@ -4707,7 +4604,7 @@ function removeCursorLearningHooks() {
4707
4604
  return { removed: true, notFound: false };
4708
4605
  }
4709
4606
  function removeLearningHooks() {
4710
- const settings = readSettings2();
4607
+ const settings = readSettings();
4711
4608
  if (!settings.hooks) return { removed: false, notFound: true };
4712
4609
  let removedAny = false;
4713
4610
  for (const cfg of HOOK_TAILS) {
@@ -4724,16 +4621,16 @@ function removeLearningHooks() {
4724
4621
  delete settings.hooks;
4725
4622
  }
4726
4623
  if (!removedAny) return { removed: false, notFound: true };
4727
- writeSettings2(settings);
4624
+ writeSettings(settings);
4728
4625
  return { removed: true, notFound: false };
4729
4626
  }
4730
4627
 
4731
4628
  // src/lib/state.ts
4732
4629
  init_constants();
4733
- import fs21 from "fs";
4734
- import path17 from "path";
4735
- import { execSync as execSync8 } from "child_process";
4736
- var STATE_FILE = path17.join(CALIBER_DIR, ".caliber-state.json");
4630
+ import fs20 from "fs";
4631
+ import path16 from "path";
4632
+ import { execSync as execSync7 } from "child_process";
4633
+ var STATE_FILE = path16.join(CALIBER_DIR, ".caliber-state.json");
4737
4634
  function normalizeTargetAgent(value) {
4738
4635
  if (Array.isArray(value)) return value;
4739
4636
  if (typeof value === "string") {
@@ -4744,8 +4641,8 @@ function normalizeTargetAgent(value) {
4744
4641
  }
4745
4642
  function readState() {
4746
4643
  try {
4747
- if (!fs21.existsSync(STATE_FILE)) return null;
4748
- const raw = JSON.parse(fs21.readFileSync(STATE_FILE, "utf-8"));
4644
+ if (!fs20.existsSync(STATE_FILE)) return null;
4645
+ const raw = JSON.parse(fs20.readFileSync(STATE_FILE, "utf-8"));
4749
4646
  if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
4750
4647
  return raw;
4751
4648
  } catch {
@@ -4753,14 +4650,14 @@ function readState() {
4753
4650
  }
4754
4651
  }
4755
4652
  function writeState(state) {
4756
- if (!fs21.existsSync(CALIBER_DIR)) {
4757
- fs21.mkdirSync(CALIBER_DIR, { recursive: true });
4653
+ if (!fs20.existsSync(CALIBER_DIR)) {
4654
+ fs20.mkdirSync(CALIBER_DIR, { recursive: true });
4758
4655
  }
4759
- fs21.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
4656
+ fs20.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
4760
4657
  }
4761
4658
  function getCurrentHeadSha() {
4762
4659
  try {
4763
- return execSync8("git rev-parse HEAD", {
4660
+ return execSync7("git rev-parse HEAD", {
4764
4661
  encoding: "utf-8",
4765
4662
  stdio: ["pipe", "pipe", "pipe"]
4766
4663
  }).trim();
@@ -5369,7 +5266,7 @@ function checkGrounding(dir) {
5369
5266
 
5370
5267
  // src/scoring/checks/accuracy.ts
5371
5268
  import { existsSync as existsSync4, statSync as statSync2 } from "fs";
5372
- import { execSync as execSync9 } from "child_process";
5269
+ import { execSync as execSync8 } from "child_process";
5373
5270
  import { join as join5 } from "path";
5374
5271
  function validateReferences(dir) {
5375
5272
  const configContent = collectPrimaryConfigContent(dir);
@@ -5378,13 +5275,13 @@ function validateReferences(dir) {
5378
5275
  }
5379
5276
  function detectGitDrift(dir) {
5380
5277
  try {
5381
- execSync9("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
5278
+ execSync8("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
5382
5279
  } catch {
5383
5280
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: false };
5384
5281
  }
5385
5282
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".cursor/rules"];
5386
5283
  try {
5387
- const headTimestamp = execSync9(
5284
+ const headTimestamp = execSync8(
5388
5285
  "git log -1 --format=%ct HEAD",
5389
5286
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5390
5287
  ).trim();
@@ -5405,7 +5302,7 @@ function detectGitDrift(dir) {
5405
5302
  let latestConfigCommitHash = null;
5406
5303
  for (const file of configFiles) {
5407
5304
  try {
5408
- const hash = execSync9(
5305
+ const hash = execSync8(
5409
5306
  `git log -1 --format=%H -- "${file}"`,
5410
5307
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5411
5308
  ).trim();
@@ -5414,7 +5311,7 @@ function detectGitDrift(dir) {
5414
5311
  latestConfigCommitHash = hash;
5415
5312
  } else {
5416
5313
  try {
5417
- execSync9(
5314
+ execSync8(
5418
5315
  `git merge-base --is-ancestor ${latestConfigCommitHash} ${hash}`,
5419
5316
  { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }
5420
5317
  );
@@ -5429,12 +5326,12 @@ function detectGitDrift(dir) {
5429
5326
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: true };
5430
5327
  }
5431
5328
  try {
5432
- const countStr = execSync9(
5329
+ const countStr = execSync8(
5433
5330
  `git rev-list --count ${latestConfigCommitHash}..HEAD`,
5434
5331
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5435
5332
  ).trim();
5436
5333
  const commitsSince = parseInt(countStr, 10) || 0;
5437
- const lastDate = execSync9(
5334
+ const lastDate = execSync8(
5438
5335
  `git log -1 --format=%ci ${latestConfigCommitHash}`,
5439
5336
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5440
5337
  ).trim();
@@ -5506,12 +5403,12 @@ function checkAccuracy(dir) {
5506
5403
 
5507
5404
  // src/scoring/checks/freshness.ts
5508
5405
  import { existsSync as existsSync5, statSync as statSync3 } from "fs";
5509
- import { execSync as execSync10 } from "child_process";
5406
+ import { execSync as execSync9 } from "child_process";
5510
5407
  import { join as join6 } from "path";
5511
5408
  function getCommitsSinceConfigUpdate(dir) {
5512
5409
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules"];
5513
5410
  try {
5514
- const headTimestamp = execSync10(
5411
+ const headTimestamp = execSync9(
5515
5412
  "git log -1 --format=%ct HEAD",
5516
5413
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5517
5414
  ).trim();
@@ -5531,12 +5428,12 @@ function getCommitsSinceConfigUpdate(dir) {
5531
5428
  }
5532
5429
  for (const file of configFiles) {
5533
5430
  try {
5534
- const hash = execSync10(
5431
+ const hash = execSync9(
5535
5432
  `git log -1 --format=%H -- "${file}"`,
5536
5433
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5537
5434
  ).trim();
5538
5435
  if (hash) {
5539
- const countStr = execSync10(
5436
+ const countStr = execSync9(
5540
5437
  `git rev-list --count ${hash}..HEAD`,
5541
5438
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5542
5439
  ).trim();
@@ -5654,11 +5551,11 @@ function checkFreshness(dir) {
5654
5551
 
5655
5552
  // src/scoring/checks/bonus.ts
5656
5553
  import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
5657
- import { execSync as execSync11 } from "child_process";
5554
+ import { execSync as execSync10 } from "child_process";
5658
5555
  import { join as join7 } from "path";
5659
5556
  function hasPreCommitHook(dir) {
5660
5557
  try {
5661
- const gitDir = execSync11("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
5558
+ const gitDir = execSync10("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
5662
5559
  const hookPath = join7(gitDir, "hooks", "pre-commit");
5663
5560
  const content = readFileOrNull(hookPath);
5664
5561
  return content ? content.includes("caliber") : false;
@@ -5687,7 +5584,12 @@ function checkBonus(dir) {
5687
5584
  if (hasPrecommit) {
5688
5585
  hookSources.push("git pre-commit");
5689
5586
  }
5690
- const hasHooks = hasClaudeHooks || hasPrecommit;
5587
+ const claudeMd = readFileOrNull(join7(dir, "CLAUDE.md"));
5588
+ const hasPreCommitBlock2 = claudeMd ? hasPreCommitBlock(claudeMd) : false;
5589
+ if (hasPreCommitBlock2) {
5590
+ hookSources.push("config pre-commit instruction");
5591
+ }
5592
+ const hasHooks = hasClaudeHooks || hasPrecommit || hasPreCommitBlock2;
5691
5593
  checks.push({
5692
5594
  id: "hooks_configured",
5693
5595
  name: "Hooks configured",
@@ -5696,11 +5598,11 @@ function checkBonus(dir) {
5696
5598
  earnedPoints: hasHooks ? POINTS_HOOKS : 0,
5697
5599
  passed: hasHooks,
5698
5600
  detail: hasHooks ? hookSources.join(", ") : "No hooks configured",
5699
- suggestion: hasHooks ? void 0 : "Run `caliber hooks --install` for auto-refresh",
5601
+ suggestion: hasHooks ? void 0 : "Run `caliber init` to add pre-commit instructions",
5700
5602
  fix: hasHooks ? void 0 : {
5701
5603
  action: "install_hooks",
5702
5604
  data: {},
5703
- instruction: "Install caliber hooks for automatic config refresh on commits."
5605
+ instruction: "Run caliber init to add pre-commit refresh instructions to config files."
5704
5606
  }
5705
5607
  });
5706
5608
  const agentsMdExists = existsSync6(join7(dir, "AGENTS.md"));
@@ -5810,22 +5712,22 @@ function checkSources(dir) {
5810
5712
 
5811
5713
  // src/scoring/dismissed.ts
5812
5714
  init_constants();
5813
- import fs22 from "fs";
5814
- import path18 from "path";
5815
- var DISMISSED_FILE = path18.join(CALIBER_DIR, "dismissed-checks.json");
5715
+ import fs21 from "fs";
5716
+ import path17 from "path";
5717
+ var DISMISSED_FILE = path17.join(CALIBER_DIR, "dismissed-checks.json");
5816
5718
  function readDismissedChecks() {
5817
5719
  try {
5818
- if (!fs22.existsSync(DISMISSED_FILE)) return [];
5819
- return JSON.parse(fs22.readFileSync(DISMISSED_FILE, "utf-8"));
5720
+ if (!fs21.existsSync(DISMISSED_FILE)) return [];
5721
+ return JSON.parse(fs21.readFileSync(DISMISSED_FILE, "utf-8"));
5820
5722
  } catch {
5821
5723
  return [];
5822
5724
  }
5823
5725
  }
5824
5726
  function writeDismissedChecks(checks) {
5825
- if (!fs22.existsSync(CALIBER_DIR)) {
5826
- fs22.mkdirSync(CALIBER_DIR, { recursive: true });
5727
+ if (!fs21.existsSync(CALIBER_DIR)) {
5728
+ fs21.mkdirSync(CALIBER_DIR, { recursive: true });
5827
5729
  }
5828
- fs22.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
5730
+ fs21.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
5829
5731
  }
5830
5732
  function getDismissedIds() {
5831
5733
  return new Set(readDismissedChecks().map((c) => c.id));
@@ -6073,13 +5975,13 @@ import { mkdirSync, readFileSync as readFileSync4, readdirSync as readdirSync4,
6073
5975
  import { join as join10, dirname as dirname2 } from "path";
6074
5976
 
6075
5977
  // src/scanner/index.ts
6076
- import fs23 from "fs";
6077
- import path19 from "path";
5978
+ import fs22 from "fs";
5979
+ import path18 from "path";
6078
5980
  import crypto3 from "crypto";
6079
5981
  function scanLocalState(dir) {
6080
5982
  const items = [];
6081
- const claudeMdPath = path19.join(dir, "CLAUDE.md");
6082
- if (fs23.existsSync(claudeMdPath)) {
5983
+ const claudeMdPath = path18.join(dir, "CLAUDE.md");
5984
+ if (fs22.existsSync(claudeMdPath)) {
6083
5985
  items.push({
6084
5986
  type: "rule",
6085
5987
  platform: "claude",
@@ -6088,10 +5990,10 @@ function scanLocalState(dir) {
6088
5990
  path: claudeMdPath
6089
5991
  });
6090
5992
  }
6091
- const skillsDir = path19.join(dir, ".claude", "skills");
6092
- if (fs23.existsSync(skillsDir)) {
6093
- for (const file of fs23.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
6094
- const filePath = path19.join(skillsDir, file);
5993
+ const skillsDir = path18.join(dir, ".claude", "skills");
5994
+ if (fs22.existsSync(skillsDir)) {
5995
+ for (const file of fs22.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
5996
+ const filePath = path18.join(skillsDir, file);
6095
5997
  items.push({
6096
5998
  type: "skill",
6097
5999
  platform: "claude",
@@ -6101,10 +6003,10 @@ function scanLocalState(dir) {
6101
6003
  });
6102
6004
  }
6103
6005
  }
6104
- const mcpJsonPath = path19.join(dir, ".mcp.json");
6105
- if (fs23.existsSync(mcpJsonPath)) {
6006
+ const mcpJsonPath = path18.join(dir, ".mcp.json");
6007
+ if (fs22.existsSync(mcpJsonPath)) {
6106
6008
  try {
6107
- const mcpJson = JSON.parse(fs23.readFileSync(mcpJsonPath, "utf-8"));
6009
+ const mcpJson = JSON.parse(fs22.readFileSync(mcpJsonPath, "utf-8"));
6108
6010
  if (mcpJson.mcpServers) {
6109
6011
  for (const name of Object.keys(mcpJson.mcpServers)) {
6110
6012
  items.push({
@@ -6119,8 +6021,8 @@ function scanLocalState(dir) {
6119
6021
  } catch {
6120
6022
  }
6121
6023
  }
6122
- const agentsMdPath = path19.join(dir, "AGENTS.md");
6123
- if (fs23.existsSync(agentsMdPath)) {
6024
+ const agentsMdPath = path18.join(dir, "AGENTS.md");
6025
+ if (fs22.existsSync(agentsMdPath)) {
6124
6026
  items.push({
6125
6027
  type: "rule",
6126
6028
  platform: "codex",
@@ -6129,12 +6031,12 @@ function scanLocalState(dir) {
6129
6031
  path: agentsMdPath
6130
6032
  });
6131
6033
  }
6132
- const codexSkillsDir = path19.join(dir, ".agents", "skills");
6133
- if (fs23.existsSync(codexSkillsDir)) {
6034
+ const codexSkillsDir = path18.join(dir, ".agents", "skills");
6035
+ if (fs22.existsSync(codexSkillsDir)) {
6134
6036
  try {
6135
- for (const name of fs23.readdirSync(codexSkillsDir)) {
6136
- const skillFile = path19.join(codexSkillsDir, name, "SKILL.md");
6137
- if (fs23.existsSync(skillFile)) {
6037
+ for (const name of fs22.readdirSync(codexSkillsDir)) {
6038
+ const skillFile = path18.join(codexSkillsDir, name, "SKILL.md");
6039
+ if (fs22.existsSync(skillFile)) {
6138
6040
  items.push({
6139
6041
  type: "skill",
6140
6042
  platform: "codex",
@@ -6147,8 +6049,8 @@ function scanLocalState(dir) {
6147
6049
  } catch {
6148
6050
  }
6149
6051
  }
6150
- const cursorrulesPath = path19.join(dir, ".cursorrules");
6151
- if (fs23.existsSync(cursorrulesPath)) {
6052
+ const cursorrulesPath = path18.join(dir, ".cursorrules");
6053
+ if (fs22.existsSync(cursorrulesPath)) {
6152
6054
  items.push({
6153
6055
  type: "rule",
6154
6056
  platform: "cursor",
@@ -6157,10 +6059,10 @@ function scanLocalState(dir) {
6157
6059
  path: cursorrulesPath
6158
6060
  });
6159
6061
  }
6160
- const cursorRulesDir = path19.join(dir, ".cursor", "rules");
6161
- if (fs23.existsSync(cursorRulesDir)) {
6162
- for (const file of fs23.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
6163
- const filePath = path19.join(cursorRulesDir, file);
6062
+ const cursorRulesDir = path18.join(dir, ".cursor", "rules");
6063
+ if (fs22.existsSync(cursorRulesDir)) {
6064
+ for (const file of fs22.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
6065
+ const filePath = path18.join(cursorRulesDir, file);
6164
6066
  items.push({
6165
6067
  type: "rule",
6166
6068
  platform: "cursor",
@@ -6170,12 +6072,12 @@ function scanLocalState(dir) {
6170
6072
  });
6171
6073
  }
6172
6074
  }
6173
- const cursorSkillsDir = path19.join(dir, ".cursor", "skills");
6174
- if (fs23.existsSync(cursorSkillsDir)) {
6075
+ const cursorSkillsDir = path18.join(dir, ".cursor", "skills");
6076
+ if (fs22.existsSync(cursorSkillsDir)) {
6175
6077
  try {
6176
- for (const name of fs23.readdirSync(cursorSkillsDir)) {
6177
- const skillFile = path19.join(cursorSkillsDir, name, "SKILL.md");
6178
- if (fs23.existsSync(skillFile)) {
6078
+ for (const name of fs22.readdirSync(cursorSkillsDir)) {
6079
+ const skillFile = path18.join(cursorSkillsDir, name, "SKILL.md");
6080
+ if (fs22.existsSync(skillFile)) {
6179
6081
  items.push({
6180
6082
  type: "skill",
6181
6083
  platform: "cursor",
@@ -6188,10 +6090,10 @@ function scanLocalState(dir) {
6188
6090
  } catch {
6189
6091
  }
6190
6092
  }
6191
- const cursorMcpPath = path19.join(dir, ".cursor", "mcp.json");
6192
- if (fs23.existsSync(cursorMcpPath)) {
6093
+ const cursorMcpPath = path18.join(dir, ".cursor", "mcp.json");
6094
+ if (fs22.existsSync(cursorMcpPath)) {
6193
6095
  try {
6194
- const mcpJson = JSON.parse(fs23.readFileSync(cursorMcpPath, "utf-8"));
6096
+ const mcpJson = JSON.parse(fs22.readFileSync(cursorMcpPath, "utf-8"));
6195
6097
  if (mcpJson.mcpServers) {
6196
6098
  for (const name of Object.keys(mcpJson.mcpServers)) {
6197
6099
  items.push({
@@ -6209,7 +6111,7 @@ function scanLocalState(dir) {
6209
6111
  return items;
6210
6112
  }
6211
6113
  function hashFile(filePath) {
6212
- const text = fs23.readFileSync(filePath, "utf-8");
6114
+ const text = fs22.readFileSync(filePath, "utf-8");
6213
6115
  return crypto3.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
6214
6116
  }
6215
6117
  function hashJson(obj) {
@@ -6224,27 +6126,27 @@ import { PostHog } from "posthog-node";
6224
6126
  import chalk5 from "chalk";
6225
6127
 
6226
6128
  // src/telemetry/config.ts
6227
- import fs24 from "fs";
6228
- import path20 from "path";
6129
+ import fs23 from "fs";
6130
+ import path19 from "path";
6229
6131
  import os4 from "os";
6230
6132
  import crypto4 from "crypto";
6231
- import { execSync as execSync12 } from "child_process";
6232
- var CONFIG_DIR2 = path20.join(os4.homedir(), ".caliber");
6233
- var CONFIG_FILE2 = path20.join(CONFIG_DIR2, "config.json");
6133
+ import { execSync as execSync11 } from "child_process";
6134
+ var CONFIG_DIR2 = path19.join(os4.homedir(), ".caliber");
6135
+ var CONFIG_FILE2 = path19.join(CONFIG_DIR2, "config.json");
6234
6136
  var runtimeDisabled = false;
6235
6137
  function readConfig() {
6236
6138
  try {
6237
- if (!fs24.existsSync(CONFIG_FILE2)) return {};
6238
- return JSON.parse(fs24.readFileSync(CONFIG_FILE2, "utf-8"));
6139
+ if (!fs23.existsSync(CONFIG_FILE2)) return {};
6140
+ return JSON.parse(fs23.readFileSync(CONFIG_FILE2, "utf-8"));
6239
6141
  } catch {
6240
6142
  return {};
6241
6143
  }
6242
6144
  }
6243
6145
  function writeConfig(config) {
6244
- if (!fs24.existsSync(CONFIG_DIR2)) {
6245
- fs24.mkdirSync(CONFIG_DIR2, { recursive: true });
6146
+ if (!fs23.existsSync(CONFIG_DIR2)) {
6147
+ fs23.mkdirSync(CONFIG_DIR2, { recursive: true });
6246
6148
  }
6247
- fs24.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
6149
+ fs23.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
6248
6150
  }
6249
6151
  function getMachineId() {
6250
6152
  const config = readConfig();
@@ -6255,7 +6157,7 @@ function getMachineId() {
6255
6157
  }
6256
6158
  function getGitEmailHash() {
6257
6159
  try {
6258
- const email = execSync12("git config user.email", { encoding: "utf-8" }).trim();
6160
+ const email = execSync11("git config user.email", { encoding: "utf-8" }).trim();
6259
6161
  if (!email) return void 0;
6260
6162
  return crypto4.createHash("sha256").update(email).digest("hex");
6261
6163
  } catch {
@@ -7346,8 +7248,8 @@ async function runScoreRefineWithSpinner(setup, dir, sessionHistory) {
7346
7248
  }
7347
7249
 
7348
7250
  // src/lib/debug-report.ts
7349
- import fs25 from "fs";
7350
- import path21 from "path";
7251
+ import fs24 from "fs";
7252
+ import path20 from "path";
7351
7253
  var DebugReport = class {
7352
7254
  sections = [];
7353
7255
  startTime;
@@ -7416,11 +7318,11 @@ var DebugReport = class {
7416
7318
  lines.push(`| **Total** | **${formatMs(totalMs)}** |`);
7417
7319
  lines.push("");
7418
7320
  }
7419
- const dir = path21.dirname(outputPath);
7420
- if (!fs25.existsSync(dir)) {
7421
- fs25.mkdirSync(dir, { recursive: true });
7321
+ const dir = path20.dirname(outputPath);
7322
+ if (!fs24.existsSync(dir)) {
7323
+ fs24.mkdirSync(dir, { recursive: true });
7422
7324
  }
7423
- fs25.writeFileSync(outputPath, lines.join("\n"));
7325
+ fs24.writeFileSync(outputPath, lines.join("\n"));
7424
7326
  }
7425
7327
  };
7426
7328
  function formatMs(ms) {
@@ -7821,7 +7723,7 @@ import chalk11 from "chalk";
7821
7723
  import ora3 from "ora";
7822
7724
  import select5 from "@inquirer/select";
7823
7725
  import checkbox from "@inquirer/checkbox";
7824
- import fs28 from "fs";
7726
+ import fs27 from "fs";
7825
7727
 
7826
7728
  // src/ai/refine.ts
7827
7729
  async function refineSetup(currentSetup, message, conversationHistory, callbacks) {
@@ -7962,10 +7864,10 @@ init_config();
7962
7864
  init_review();
7963
7865
  function detectAgents(dir) {
7964
7866
  const agents = [];
7965
- if (fs28.existsSync(`${dir}/.claude`)) agents.push("claude");
7966
- if (fs28.existsSync(`${dir}/.cursor`)) agents.push("cursor");
7967
- if (fs28.existsSync(`${dir}/.agents`) || fs28.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
7968
- if (fs28.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
7867
+ if (fs27.existsSync(`${dir}/.claude`)) agents.push("claude");
7868
+ if (fs27.existsSync(`${dir}/.cursor`)) agents.push("cursor");
7869
+ if (fs27.existsSync(`${dir}/.agents`) || fs27.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
7870
+ if (fs27.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
7969
7871
  return agents;
7970
7872
  }
7971
7873
  async function promptAgent(detected) {
@@ -7987,20 +7889,6 @@ async function promptAgent(detected) {
7987
7889
  });
7988
7890
  return selected;
7989
7891
  }
7990
- async function promptHookType(targetAgent) {
7991
- const choices = [];
7992
- const hasClaude = targetAgent.includes("claude");
7993
- choices.push({ name: "Git pre-commit hook \u2014 refresh before each commit (recommended)", value: "precommit" });
7994
- if (hasClaude) {
7995
- choices.push({ name: "Claude Code hook \u2014 auto-refresh on session end", value: "claude" });
7996
- choices.push({ name: "Both (pre-commit + Claude Code)", value: "both" });
7997
- }
7998
- choices.push({ name: "Skip for now", value: "skip" });
7999
- return select5({
8000
- message: "Keep your AI docs & skills in sync as your code evolves?",
8001
- choices
8002
- });
8003
- }
8004
7892
  async function promptLearnInstall(targetAgent) {
8005
7893
  const hasClaude = targetAgent.includes("claude");
8006
7894
  const hasCursor = targetAgent.includes("cursor");
@@ -8101,18 +7989,18 @@ async function refineLoop(currentSetup, sessionHistory, summarizeSetup2, printSu
8101
7989
 
8102
7990
  // src/commands/init-display.ts
8103
7991
  import chalk12 from "chalk";
8104
- import fs29 from "fs";
7992
+ import fs28 from "fs";
8105
7993
  function formatWhatChanged(setup) {
8106
7994
  const lines = [];
8107
7995
  const claude = setup.claude;
8108
7996
  const codex = setup.codex;
8109
7997
  const cursor = setup.cursor;
8110
7998
  if (claude?.claudeMd) {
8111
- const action = fs29.existsSync("CLAUDE.md") ? "Updated" : "Created";
7999
+ const action = fs28.existsSync("CLAUDE.md") ? "Updated" : "Created";
8112
8000
  lines.push(`${action} CLAUDE.md`);
8113
8001
  }
8114
8002
  if (codex?.agentsMd) {
8115
- const action = fs29.existsSync("AGENTS.md") ? "Updated" : "Created";
8003
+ const action = fs28.existsSync("AGENTS.md") ? "Updated" : "Created";
8116
8004
  lines.push(`${action} AGENTS.md`);
8117
8005
  }
8118
8006
  const allSkills = [];
@@ -8149,7 +8037,7 @@ function printSetupSummary(setup) {
8149
8037
  };
8150
8038
  if (claude) {
8151
8039
  if (claude.claudeMd) {
8152
- const icon = fs29.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
8040
+ const icon = fs28.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
8153
8041
  const desc = getDescription("CLAUDE.md");
8154
8042
  console.log(` ${icon} ${chalk12.bold("CLAUDE.md")}`);
8155
8043
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -8159,7 +8047,7 @@ function printSetupSummary(setup) {
8159
8047
  if (Array.isArray(skills) && skills.length > 0) {
8160
8048
  for (const skill of skills) {
8161
8049
  const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
8162
- const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8050
+ const icon = fs28.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8163
8051
  const desc = getDescription(skillPath);
8164
8052
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
8165
8053
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -8170,7 +8058,7 @@ function printSetupSummary(setup) {
8170
8058
  const codex = setup.codex;
8171
8059
  if (codex) {
8172
8060
  if (codex.agentsMd) {
8173
- const icon = fs29.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
8061
+ const icon = fs28.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
8174
8062
  const desc = getDescription("AGENTS.md");
8175
8063
  console.log(` ${icon} ${chalk12.bold("AGENTS.md")}`);
8176
8064
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -8180,7 +8068,7 @@ function printSetupSummary(setup) {
8180
8068
  if (Array.isArray(codexSkills) && codexSkills.length > 0) {
8181
8069
  for (const skill of codexSkills) {
8182
8070
  const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
8183
- const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8071
+ const icon = fs28.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8184
8072
  const desc = getDescription(skillPath);
8185
8073
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
8186
8074
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -8190,7 +8078,7 @@ function printSetupSummary(setup) {
8190
8078
  }
8191
8079
  if (cursor) {
8192
8080
  if (cursor.cursorrules) {
8193
- const icon = fs29.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
8081
+ const icon = fs28.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
8194
8082
  const desc = getDescription(".cursorrules");
8195
8083
  console.log(` ${icon} ${chalk12.bold(".cursorrules")}`);
8196
8084
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -8200,7 +8088,7 @@ function printSetupSummary(setup) {
8200
8088
  if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
8201
8089
  for (const skill of cursorSkills) {
8202
8090
  const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
8203
- const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8091
+ const icon = fs28.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8204
8092
  const desc = getDescription(skillPath);
8205
8093
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
8206
8094
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -8211,7 +8099,7 @@ function printSetupSummary(setup) {
8211
8099
  if (Array.isArray(rulesArr) && rulesArr.length > 0) {
8212
8100
  for (const rule of rulesArr) {
8213
8101
  const rulePath = `.cursor/rules/${rule.filename}`;
8214
- const icon = fs29.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
8102
+ const icon = fs28.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
8215
8103
  const desc = getDescription(rulePath);
8216
8104
  console.log(` ${icon} ${chalk12.bold(rulePath)}`);
8217
8105
  if (desc) {
@@ -8258,12 +8146,12 @@ function displayTokenUsage() {
8258
8146
  // src/commands/init-helpers.ts
8259
8147
  init_config();
8260
8148
  import chalk13 from "chalk";
8261
- import fs30 from "fs";
8262
- import path23 from "path";
8149
+ import fs29 from "fs";
8150
+ import path22 from "path";
8263
8151
  function isFirstRun(dir) {
8264
- const caliberDir = path23.join(dir, ".caliber");
8152
+ const caliberDir = path22.join(dir, ".caliber");
8265
8153
  try {
8266
- const stat = fs30.statSync(caliberDir);
8154
+ const stat = fs29.statSync(caliberDir);
8267
8155
  return !stat.isDirectory();
8268
8156
  } catch {
8269
8157
  return true;
@@ -8316,8 +8204,8 @@ function ensurePermissions(fingerprint) {
8316
8204
  const settingsPath = ".claude/settings.json";
8317
8205
  let settings = {};
8318
8206
  try {
8319
- if (fs30.existsSync(settingsPath)) {
8320
- settings = JSON.parse(fs30.readFileSync(settingsPath, "utf-8"));
8207
+ if (fs29.existsSync(settingsPath)) {
8208
+ settings = JSON.parse(fs29.readFileSync(settingsPath, "utf-8"));
8321
8209
  }
8322
8210
  } catch {
8323
8211
  }
@@ -8326,12 +8214,12 @@ function ensurePermissions(fingerprint) {
8326
8214
  if (Array.isArray(allow) && allow.length > 0) return;
8327
8215
  permissions.allow = derivePermissions(fingerprint);
8328
8216
  settings.permissions = permissions;
8329
- if (!fs30.existsSync(".claude")) fs30.mkdirSync(".claude", { recursive: true });
8330
- fs30.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
8217
+ if (!fs29.existsSync(".claude")) fs29.mkdirSync(".claude", { recursive: true });
8218
+ fs29.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
8331
8219
  }
8332
8220
  function writeErrorLog(config, rawOutput, error, stopReason) {
8333
8221
  try {
8334
- const logPath = path23.join(process.cwd(), ".caliber", "error-log.md");
8222
+ const logPath = path22.join(process.cwd(), ".caliber", "error-log.md");
8335
8223
  const lines = [
8336
8224
  `# Generation Error \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
8337
8225
  "",
@@ -8344,8 +8232,8 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
8344
8232
  lines.push("## Error", "```", error, "```", "");
8345
8233
  }
8346
8234
  lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
8347
- fs30.mkdirSync(path23.join(process.cwd(), ".caliber"), { recursive: true });
8348
- fs30.writeFileSync(logPath, lines.join("\n"));
8235
+ fs29.mkdirSync(path22.join(process.cwd(), ".caliber"), { recursive: true });
8236
+ fs29.writeFileSync(logPath, lines.join("\n"));
8349
8237
  console.log(chalk13.dim(`
8350
8238
  Error log written to .caliber/error-log.md`));
8351
8239
  } catch {
@@ -8783,7 +8671,7 @@ async function initCommand(options) {
8783
8671
  const { default: ora9 } = await import("ora");
8784
8672
  const writeSpinner = ora9("Writing config files...").start();
8785
8673
  try {
8786
- if (targetAgent.includes("codex") && !fs31.existsSync("AGENTS.md") && !generatedSetup.codex) {
8674
+ if (targetAgent.includes("codex") && !fs30.existsSync("AGENTS.md") && !generatedSetup.codex) {
8787
8675
  const claude = generatedSetup.claude;
8788
8676
  const cursor = generatedSetup.cursor;
8789
8677
  const agentRefs = [];
@@ -8873,66 +8761,8 @@ ${agentRefs.join(" ")}
8873
8761
  }
8874
8762
  }
8875
8763
  console.log("");
8876
- let hookChoice;
8877
- if (options.autoApprove) {
8878
- hookChoice = "skip";
8879
- log(options.verbose, "Auto-approve: skipping hook installation");
8880
- } else if (firstRun) {
8881
- const precommitResult = installPreCommitHook();
8882
- if (precommitResult.installed) {
8883
- console.log(` ${chalk14.green("\u2713")} Pre-commit hook installed \u2014 docs refresh before each commit`);
8884
- console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")("caliber hooks --remove") + chalk14.dim(" to disable"));
8885
- hookChoice = "precommit";
8886
- } else if (precommitResult.alreadyInstalled) {
8887
- console.log(chalk14.dim(" Pre-commit hook already installed"));
8888
- hookChoice = "precommit";
8889
- } else {
8890
- hookChoice = "skip";
8891
- }
8892
- if (targetAgent.includes("claude")) {
8893
- const { default: select7 } = await import("@inquirer/select");
8894
- const wantsClaude = await select7({
8895
- message: "Also install Claude Code session hook? (auto-refresh on session end)",
8896
- choices: [
8897
- { name: "Yes", value: true },
8898
- { name: "No", value: false }
8899
- ]
8900
- });
8901
- if (wantsClaude) {
8902
- const hookResult = installHook();
8903
- if (hookResult.installed) {
8904
- console.log(` ${chalk14.green("\u2713")} Claude Code hook installed`);
8905
- }
8906
- hookChoice = hookChoice === "precommit" ? "both" : "claude";
8907
- }
8908
- }
8909
- } else {
8910
- hookChoice = await promptHookType(targetAgent);
8911
- if (hookChoice === "claude" || hookChoice === "both") {
8912
- const hookResult = installHook();
8913
- if (hookResult.installed) {
8914
- console.log(` ${chalk14.green("\u2713")} Claude Code hook installed \u2014 docs update on session end`);
8915
- console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")("caliber hooks --remove") + chalk14.dim(" to disable"));
8916
- } else if (hookResult.alreadyInstalled) {
8917
- console.log(chalk14.dim(" Claude Code hook already installed"));
8918
- }
8919
- }
8920
- if (hookChoice === "precommit" || hookChoice === "both") {
8921
- const precommitResult = installPreCommitHook();
8922
- if (precommitResult.installed) {
8923
- console.log(` ${chalk14.green("\u2713")} Pre-commit hook installed \u2014 docs refresh before each commit`);
8924
- console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")("caliber hooks --remove") + chalk14.dim(" to disable"));
8925
- } else if (precommitResult.alreadyInstalled) {
8926
- console.log(chalk14.dim(" Pre-commit hook already installed"));
8927
- } else {
8928
- console.log(chalk14.yellow(" Could not install pre-commit hook (not a git repository?)"));
8929
- }
8930
- }
8931
- if (hookChoice === "skip") {
8932
- console.log(chalk14.dim(" Skipped auto-sync hooks. Run ") + chalk14.hex("#83D1EB")("caliber hooks --install") + chalk14.dim(" later to enable."));
8933
- }
8934
- }
8935
- trackInitHookSelected(hookChoice);
8764
+ console.log(` ${chalk14.green("\u2713")} Docs auto-refresh ${chalk14.dim("agents run caliber refresh before commits")}`);
8765
+ trackInitHookSelected("config-instructions");
8936
8766
  const hasLearnableAgent = targetAgent.includes("claude") || targetAgent.includes("cursor");
8937
8767
  let enableLearn = false;
8938
8768
  if (hasLearnableAgent) {
@@ -8967,13 +8797,7 @@ ${agentRefs.join(" ")}
8967
8797
  const skip = chalk14.dim("\u2013");
8968
8798
  console.log(chalk14.bold(" What was set up:\n"));
8969
8799
  console.log(` ${done} Config generated ${title("caliber score")} ${chalk14.dim("for full breakdown")}`);
8970
- const hooksInstalled = hookChoice !== "skip";
8971
- if (hooksInstalled) {
8972
- const hookLabel = hookChoice === "both" ? "pre-commit + Claude Code" : hookChoice === "precommit" ? "pre-commit" : "Claude Code";
8973
- console.log(` ${done} Auto-sync hooks ${chalk14.dim(hookLabel + " \u2014 docs stay fresh automatically")}`);
8974
- } else {
8975
- console.log(` ${skip} Auto-sync hooks ${title("caliber hooks --install")} to enable later`);
8976
- }
8800
+ console.log(` ${done} Docs auto-refresh ${chalk14.dim("agents run caliber refresh before commits")}`);
8977
8801
  if (hasLearnableAgent) {
8978
8802
  if (enableLearn) {
8979
8803
  console.log(` ${done} Session learning ${chalk14.dim("agent learns from your feedback")}`);
@@ -8996,9 +8820,9 @@ ${agentRefs.join(" ")}
8996
8820
  }
8997
8821
  if (report) {
8998
8822
  report.markStep("Finished");
8999
- const reportPath = path24.join(process.cwd(), ".caliber", "debug-report.md");
8823
+ const reportPath = path23.join(process.cwd(), ".caliber", "debug-report.md");
9000
8824
  report.write(reportPath);
9001
- console.log(chalk14.dim(` Debug report written to ${path24.relative(process.cwd(), reportPath)}
8825
+ console.log(chalk14.dim(` Debug report written to ${path23.relative(process.cwd(), reportPath)}
9002
8826
  `));
9003
8827
  }
9004
8828
  }
@@ -9037,7 +8861,7 @@ function undoCommand() {
9037
8861
 
9038
8862
  // src/commands/status.ts
9039
8863
  import chalk16 from "chalk";
9040
- import fs32 from "fs";
8864
+ import fs31 from "fs";
9041
8865
  init_config();
9042
8866
  async function statusCommand(options) {
9043
8867
  const config = loadConfig();
@@ -9064,7 +8888,7 @@ async function statusCommand(options) {
9064
8888
  }
9065
8889
  console.log(` Files managed: ${chalk16.cyan(manifest.entries.length.toString())}`);
9066
8890
  for (const entry of manifest.entries) {
9067
- const exists = fs32.existsSync(entry.path);
8891
+ const exists = fs31.existsSync(entry.path);
9068
8892
  const icon = exists ? chalk16.green("\u2713") : chalk16.red("\u2717");
9069
8893
  console.log(` ${icon} ${entry.path} (${entry.action})`);
9070
8894
  }
@@ -9219,21 +9043,21 @@ async function regenerateCommand(options) {
9219
9043
  }
9220
9044
 
9221
9045
  // src/commands/score.ts
9222
- import fs33 from "fs";
9046
+ import fs32 from "fs";
9223
9047
  import os6 from "os";
9224
- import path25 from "path";
9048
+ import path24 from "path";
9225
9049
  import { execFileSync } from "child_process";
9226
9050
  import chalk18 from "chalk";
9227
9051
  var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".cursorrules", "CALIBER_LEARNINGS.md"];
9228
9052
  var CONFIG_DIRS = [".claude", ".cursor"];
9229
9053
  function scoreBaseRef(ref, target) {
9230
9054
  if (!/^[\w.\-\/~^@{}]+$/.test(ref)) return null;
9231
- const tmpDir = fs33.mkdtempSync(path25.join(os6.tmpdir(), "caliber-compare-"));
9055
+ const tmpDir = fs32.mkdtempSync(path24.join(os6.tmpdir(), "caliber-compare-"));
9232
9056
  try {
9233
9057
  for (const file of CONFIG_FILES) {
9234
9058
  try {
9235
9059
  const content = execFileSync("git", ["show", `${ref}:${file}`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
9236
- fs33.writeFileSync(path25.join(tmpDir, file), content);
9060
+ fs32.writeFileSync(path24.join(tmpDir, file), content);
9237
9061
  } catch {
9238
9062
  }
9239
9063
  }
@@ -9241,10 +9065,10 @@ function scoreBaseRef(ref, target) {
9241
9065
  try {
9242
9066
  const files = execFileSync("git", ["ls-tree", "-r", "--name-only", ref, `${dir}/`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim().split("\n").filter(Boolean);
9243
9067
  for (const file of files) {
9244
- const filePath = path25.join(tmpDir, file);
9245
- fs33.mkdirSync(path25.dirname(filePath), { recursive: true });
9068
+ const filePath = path24.join(tmpDir, file);
9069
+ fs32.mkdirSync(path24.dirname(filePath), { recursive: true });
9246
9070
  const content = execFileSync("git", ["show", `${ref}:${file}`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
9247
- fs33.writeFileSync(filePath, content);
9071
+ fs32.writeFileSync(filePath, content);
9248
9072
  }
9249
9073
  } catch {
9250
9074
  }
@@ -9254,7 +9078,7 @@ function scoreBaseRef(ref, target) {
9254
9078
  } catch {
9255
9079
  return null;
9256
9080
  } finally {
9257
- fs33.rmSync(tmpDir, { recursive: true, force: true });
9081
+ fs32.rmSync(tmpDir, { recursive: true, force: true });
9258
9082
  }
9259
9083
  }
9260
9084
  async function scoreCommand(options) {
@@ -9314,13 +9138,13 @@ async function scoreCommand(options) {
9314
9138
  }
9315
9139
 
9316
9140
  // src/commands/refresh.ts
9317
- import fs37 from "fs";
9318
- import path29 from "path";
9141
+ import fs36 from "fs";
9142
+ import path28 from "path";
9319
9143
  import chalk19 from "chalk";
9320
9144
  import ora6 from "ora";
9321
9145
 
9322
9146
  // src/lib/git-diff.ts
9323
- import { execSync as execSync14 } from "child_process";
9147
+ import { execSync as execSync13 } from "child_process";
9324
9148
  var MAX_DIFF_BYTES = 1e5;
9325
9149
  var DOC_PATTERNS = [
9326
9150
  "CLAUDE.md",
@@ -9335,7 +9159,7 @@ function excludeArgs() {
9335
9159
  }
9336
9160
  function safeExec(cmd) {
9337
9161
  try {
9338
- return execSync14(cmd, {
9162
+ return execSync13(cmd, {
9339
9163
  encoding: "utf-8",
9340
9164
  stdio: ["pipe", "pipe", "pipe"],
9341
9165
  maxBuffer: 10 * 1024 * 1024
@@ -9393,48 +9217,48 @@ function collectDiff(lastSha) {
9393
9217
  }
9394
9218
 
9395
9219
  // src/writers/refresh.ts
9396
- import fs34 from "fs";
9397
- import path26 from "path";
9220
+ import fs33 from "fs";
9221
+ import path25 from "path";
9398
9222
  function writeRefreshDocs(docs) {
9399
9223
  const written = [];
9400
9224
  if (docs.claudeMd) {
9401
- fs34.writeFileSync("CLAUDE.md", docs.claudeMd);
9225
+ fs33.writeFileSync("CLAUDE.md", appendPreCommitBlock(docs.claudeMd));
9402
9226
  written.push("CLAUDE.md");
9403
9227
  }
9404
9228
  if (docs.readmeMd) {
9405
- fs34.writeFileSync("README.md", docs.readmeMd);
9229
+ fs33.writeFileSync("README.md", docs.readmeMd);
9406
9230
  written.push("README.md");
9407
9231
  }
9408
9232
  if (docs.cursorrules) {
9409
- fs34.writeFileSync(".cursorrules", docs.cursorrules);
9233
+ fs33.writeFileSync(".cursorrules", docs.cursorrules);
9410
9234
  written.push(".cursorrules");
9411
9235
  }
9412
9236
  if (docs.cursorRules) {
9413
- const rulesDir = path26.join(".cursor", "rules");
9414
- if (!fs34.existsSync(rulesDir)) fs34.mkdirSync(rulesDir, { recursive: true });
9237
+ const rulesDir = path25.join(".cursor", "rules");
9238
+ if (!fs33.existsSync(rulesDir)) fs33.mkdirSync(rulesDir, { recursive: true });
9415
9239
  for (const rule of docs.cursorRules) {
9416
- fs34.writeFileSync(path26.join(rulesDir, rule.filename), rule.content);
9240
+ fs33.writeFileSync(path25.join(rulesDir, rule.filename), rule.content);
9417
9241
  written.push(`.cursor/rules/${rule.filename}`);
9418
9242
  }
9419
9243
  }
9420
9244
  if (docs.claudeSkills) {
9421
- const skillsDir = path26.join(".claude", "skills");
9422
- if (!fs34.existsSync(skillsDir)) fs34.mkdirSync(skillsDir, { recursive: true });
9245
+ const skillsDir = path25.join(".claude", "skills");
9246
+ if (!fs33.existsSync(skillsDir)) fs33.mkdirSync(skillsDir, { recursive: true });
9423
9247
  for (const skill of docs.claudeSkills) {
9424
- fs34.writeFileSync(path26.join(skillsDir, skill.filename), skill.content);
9248
+ fs33.writeFileSync(path25.join(skillsDir, skill.filename), skill.content);
9425
9249
  written.push(`.claude/skills/${skill.filename}`);
9426
9250
  }
9427
9251
  }
9428
9252
  if (docs.copilotInstructions) {
9429
- fs34.mkdirSync(".github", { recursive: true });
9430
- fs34.writeFileSync(path26.join(".github", "copilot-instructions.md"), docs.copilotInstructions);
9253
+ fs33.mkdirSync(".github", { recursive: true });
9254
+ fs33.writeFileSync(path25.join(".github", "copilot-instructions.md"), appendPreCommitBlock(docs.copilotInstructions));
9431
9255
  written.push(".github/copilot-instructions.md");
9432
9256
  }
9433
9257
  if (docs.copilotInstructionFiles) {
9434
- const instructionsDir = path26.join(".github", "instructions");
9435
- fs34.mkdirSync(instructionsDir, { recursive: true });
9258
+ const instructionsDir = path25.join(".github", "instructions");
9259
+ fs33.mkdirSync(instructionsDir, { recursive: true });
9436
9260
  for (const file of docs.copilotInstructionFiles) {
9437
- fs34.writeFileSync(path26.join(instructionsDir, file.filename), file.content);
9261
+ fs33.writeFileSync(path25.join(instructionsDir, file.filename), file.content);
9438
9262
  written.push(`.github/instructions/${file.filename}`);
9439
9263
  }
9440
9264
  }
@@ -9514,8 +9338,8 @@ Changed files: ${diff.changedFiles.join(", ")}`);
9514
9338
  }
9515
9339
 
9516
9340
  // src/learner/writer.ts
9517
- import fs35 from "fs";
9518
- import path27 from "path";
9341
+ import fs34 from "fs";
9342
+ import path26 from "path";
9519
9343
 
9520
9344
  // src/learner/utils.ts
9521
9345
  var TYPE_PREFIX_RE = /^\*\*\[[^\]]+\]\*\*\s*/;
@@ -9633,20 +9457,20 @@ function deduplicateLearnedItems(existing, incoming) {
9633
9457
  }
9634
9458
  function writeLearnedSectionTo(filePath, header, existing, incoming, mode) {
9635
9459
  const { merged, newCount, newItems } = deduplicateLearnedItems(existing, incoming);
9636
- fs35.writeFileSync(filePath, header + merged + "\n");
9637
- if (mode) fs35.chmodSync(filePath, mode);
9460
+ fs34.writeFileSync(filePath, header + merged + "\n");
9461
+ if (mode) fs34.chmodSync(filePath, mode);
9638
9462
  return { newCount, newItems };
9639
9463
  }
9640
9464
  function writeLearnedSection(content) {
9641
9465
  return writeLearnedSectionTo(LEARNINGS_FILE, LEARNINGS_HEADER, readLearnedSection(), content);
9642
9466
  }
9643
9467
  function writeLearnedSkill(skill) {
9644
- const skillDir = path27.join(".claude", "skills", skill.name);
9645
- if (!fs35.existsSync(skillDir)) fs35.mkdirSync(skillDir, { recursive: true });
9646
- const skillPath = path27.join(skillDir, "SKILL.md");
9647
- if (!skill.isNew && fs35.existsSync(skillPath)) {
9648
- const existing = fs35.readFileSync(skillPath, "utf-8");
9649
- fs35.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
9468
+ const skillDir = path26.join(".claude", "skills", skill.name);
9469
+ if (!fs34.existsSync(skillDir)) fs34.mkdirSync(skillDir, { recursive: true });
9470
+ const skillPath = path26.join(skillDir, "SKILL.md");
9471
+ if (!skill.isNew && fs34.existsSync(skillPath)) {
9472
+ const existing = fs34.readFileSync(skillPath, "utf-8");
9473
+ fs34.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
9650
9474
  } else {
9651
9475
  const frontmatter = [
9652
9476
  "---",
@@ -9655,47 +9479,47 @@ function writeLearnedSkill(skill) {
9655
9479
  "---",
9656
9480
  ""
9657
9481
  ].join("\n");
9658
- fs35.writeFileSync(skillPath, frontmatter + skill.content);
9482
+ fs34.writeFileSync(skillPath, frontmatter + skill.content);
9659
9483
  }
9660
9484
  return skillPath;
9661
9485
  }
9662
9486
  function writePersonalLearnedSection(content) {
9663
- if (!fs35.existsSync(AUTH_DIR)) fs35.mkdirSync(AUTH_DIR, { recursive: true });
9487
+ if (!fs34.existsSync(AUTH_DIR)) fs34.mkdirSync(AUTH_DIR, { recursive: true });
9664
9488
  return writeLearnedSectionTo(PERSONAL_LEARNINGS_FILE, PERSONAL_LEARNINGS_HEADER, readPersonalLearnings(), content, 384);
9665
9489
  }
9666
9490
  function readPersonalLearnings() {
9667
- if (!fs35.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
9668
- const content = fs35.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
9491
+ if (!fs34.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
9492
+ const content = fs34.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
9669
9493
  const bullets = content.split("\n").filter((l) => l.startsWith("- ")).join("\n");
9670
9494
  return bullets || null;
9671
9495
  }
9672
9496
  function readLearnedSection() {
9673
- if (fs35.existsSync(LEARNINGS_FILE)) {
9674
- const content2 = fs35.readFileSync(LEARNINGS_FILE, "utf-8");
9497
+ if (fs34.existsSync(LEARNINGS_FILE)) {
9498
+ const content2 = fs34.readFileSync(LEARNINGS_FILE, "utf-8");
9675
9499
  const bullets = content2.split("\n").filter((l) => l.startsWith("- ")).join("\n");
9676
9500
  return bullets || null;
9677
9501
  }
9678
9502
  const claudeMdPath = "CLAUDE.md";
9679
- if (!fs35.existsSync(claudeMdPath)) return null;
9680
- const content = fs35.readFileSync(claudeMdPath, "utf-8");
9503
+ if (!fs34.existsSync(claudeMdPath)) return null;
9504
+ const content = fs34.readFileSync(claudeMdPath, "utf-8");
9681
9505
  const startIdx = content.indexOf(LEARNED_START);
9682
9506
  const endIdx = content.indexOf(LEARNED_END);
9683
9507
  if (startIdx === -1 || endIdx === -1) return null;
9684
9508
  return content.slice(startIdx + LEARNED_START.length, endIdx).trim() || null;
9685
9509
  }
9686
9510
  function migrateInlineLearnings() {
9687
- if (fs35.existsSync(LEARNINGS_FILE)) return false;
9511
+ if (fs34.existsSync(LEARNINGS_FILE)) return false;
9688
9512
  const claudeMdPath = "CLAUDE.md";
9689
- if (!fs35.existsSync(claudeMdPath)) return false;
9690
- const content = fs35.readFileSync(claudeMdPath, "utf-8");
9513
+ if (!fs34.existsSync(claudeMdPath)) return false;
9514
+ const content = fs34.readFileSync(claudeMdPath, "utf-8");
9691
9515
  const startIdx = content.indexOf(LEARNED_START);
9692
9516
  const endIdx = content.indexOf(LEARNED_END);
9693
9517
  if (startIdx === -1 || endIdx === -1) return false;
9694
9518
  const section = content.slice(startIdx + LEARNED_START.length, endIdx).trim();
9695
9519
  if (!section) return false;
9696
- fs35.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
9520
+ fs34.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
9697
9521
  const cleaned = content.slice(0, startIdx) + content.slice(endIdx + LEARNED_END.length);
9698
- fs35.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
9522
+ fs34.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
9699
9523
  return true;
9700
9524
  }
9701
9525
 
@@ -9707,11 +9531,11 @@ function log2(quiet, ...args) {
9707
9531
  function discoverGitRepos(parentDir) {
9708
9532
  const repos = [];
9709
9533
  try {
9710
- const entries = fs37.readdirSync(parentDir, { withFileTypes: true });
9534
+ const entries = fs36.readdirSync(parentDir, { withFileTypes: true });
9711
9535
  for (const entry of entries) {
9712
9536
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
9713
- const childPath = path29.join(parentDir, entry.name);
9714
- if (fs37.existsSync(path29.join(childPath, ".git"))) {
9537
+ const childPath = path28.join(parentDir, entry.name);
9538
+ if (fs36.existsSync(path28.join(childPath, ".git"))) {
9715
9539
  repos.push(childPath);
9716
9540
  }
9717
9541
  }
@@ -9817,7 +9641,7 @@ async function refreshCommand(options) {
9817
9641
  `));
9818
9642
  const originalDir = process.cwd();
9819
9643
  for (const repo of repos) {
9820
- const repoName = path29.basename(repo);
9644
+ const repoName = path28.basename(repo);
9821
9645
  try {
9822
9646
  process.chdir(repo);
9823
9647
  await refreshSingleRepo(repo, { ...options, label: repoName });
@@ -9839,6 +9663,147 @@ async function refreshCommand(options) {
9839
9663
  // src/commands/hooks.ts
9840
9664
  import chalk20 from "chalk";
9841
9665
  import fs38 from "fs";
9666
+
9667
+ // src/lib/hooks.ts
9668
+ init_resolve_caliber();
9669
+ import fs37 from "fs";
9670
+ import path29 from "path";
9671
+ import { execSync as execSync14 } from "child_process";
9672
+ var SETTINGS_PATH2 = path29.join(".claude", "settings.json");
9673
+ var REFRESH_TAIL = "refresh --quiet";
9674
+ var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
9675
+ function getHookCommand() {
9676
+ return `${resolveCaliber()} ${REFRESH_TAIL}`;
9677
+ }
9678
+ function readSettings2() {
9679
+ if (!fs37.existsSync(SETTINGS_PATH2)) return {};
9680
+ try {
9681
+ return JSON.parse(fs37.readFileSync(SETTINGS_PATH2, "utf-8"));
9682
+ } catch {
9683
+ return {};
9684
+ }
9685
+ }
9686
+ function writeSettings2(settings) {
9687
+ const dir = path29.dirname(SETTINGS_PATH2);
9688
+ if (!fs37.existsSync(dir)) fs37.mkdirSync(dir, { recursive: true });
9689
+ fs37.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
9690
+ }
9691
+ function findHookIndex(sessionEnd) {
9692
+ return sessionEnd.findIndex(
9693
+ (entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, REFRESH_TAIL))
9694
+ );
9695
+ }
9696
+ function isHookInstalled() {
9697
+ const settings = readSettings2();
9698
+ const sessionEnd = settings.hooks?.SessionEnd;
9699
+ if (!Array.isArray(sessionEnd)) return false;
9700
+ return findHookIndex(sessionEnd) !== -1;
9701
+ }
9702
+ function installHook() {
9703
+ const settings = readSettings2();
9704
+ if (!settings.hooks) settings.hooks = {};
9705
+ if (!Array.isArray(settings.hooks.SessionEnd)) settings.hooks.SessionEnd = [];
9706
+ if (findHookIndex(settings.hooks.SessionEnd) !== -1) {
9707
+ return { installed: false, alreadyInstalled: true };
9708
+ }
9709
+ settings.hooks.SessionEnd.push({
9710
+ matcher: "",
9711
+ hooks: [{ type: "command", command: getHookCommand(), description: HOOK_DESCRIPTION }]
9712
+ });
9713
+ writeSettings2(settings);
9714
+ return { installed: true, alreadyInstalled: false };
9715
+ }
9716
+ function removeHook() {
9717
+ const settings = readSettings2();
9718
+ const sessionEnd = settings.hooks?.SessionEnd;
9719
+ if (!Array.isArray(sessionEnd)) {
9720
+ return { removed: false, notFound: true };
9721
+ }
9722
+ const idx = findHookIndex(sessionEnd);
9723
+ if (idx === -1) {
9724
+ return { removed: false, notFound: true };
9725
+ }
9726
+ sessionEnd.splice(idx, 1);
9727
+ if (sessionEnd.length === 0) {
9728
+ delete settings.hooks.SessionEnd;
9729
+ }
9730
+ if (settings.hooks && Object.keys(settings.hooks).length === 0) {
9731
+ delete settings.hooks;
9732
+ }
9733
+ writeSettings2(settings);
9734
+ return { removed: true, notFound: false };
9735
+ }
9736
+ var PRECOMMIT_START = "# caliber:pre-commit:start";
9737
+ var PRECOMMIT_END = "# caliber:pre-commit:end";
9738
+ function getPrecommitBlock() {
9739
+ const bin = resolveCaliber();
9740
+ return `${PRECOMMIT_START}
9741
+ if [ -x "${bin}" ] || command -v "${bin}" >/dev/null 2>&1; then
9742
+ echo "\\033[2mcaliber: refreshing docs...\\033[0m"
9743
+ "${bin}" refresh 2>/dev/null || true
9744
+ "${bin}" learn finalize 2>/dev/null || true
9745
+ git diff --name-only -- CLAUDE.md .claude/ .cursor/ AGENTS.md CALIBER_LEARNINGS.md 2>/dev/null | xargs git add 2>/dev/null || true
9746
+ fi
9747
+ ${PRECOMMIT_END}`;
9748
+ }
9749
+ function getGitHooksDir() {
9750
+ try {
9751
+ const gitDir = execSync14("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
9752
+ return path29.join(gitDir, "hooks");
9753
+ } catch {
9754
+ return null;
9755
+ }
9756
+ }
9757
+ function getPreCommitPath() {
9758
+ const hooksDir = getGitHooksDir();
9759
+ return hooksDir ? path29.join(hooksDir, "pre-commit") : null;
9760
+ }
9761
+ function isPreCommitHookInstalled() {
9762
+ const hookPath = getPreCommitPath();
9763
+ if (!hookPath || !fs37.existsSync(hookPath)) return false;
9764
+ const content = fs37.readFileSync(hookPath, "utf-8");
9765
+ return content.includes(PRECOMMIT_START);
9766
+ }
9767
+ function installPreCommitHook() {
9768
+ if (isPreCommitHookInstalled()) {
9769
+ return { installed: false, alreadyInstalled: true };
9770
+ }
9771
+ const hookPath = getPreCommitPath();
9772
+ if (!hookPath) return { installed: false, alreadyInstalled: false };
9773
+ const hooksDir = path29.dirname(hookPath);
9774
+ if (!fs37.existsSync(hooksDir)) fs37.mkdirSync(hooksDir, { recursive: true });
9775
+ let content = "";
9776
+ if (fs37.existsSync(hookPath)) {
9777
+ content = fs37.readFileSync(hookPath, "utf-8");
9778
+ if (!content.endsWith("\n")) content += "\n";
9779
+ content += "\n" + getPrecommitBlock() + "\n";
9780
+ } else {
9781
+ content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
9782
+ }
9783
+ fs37.writeFileSync(hookPath, content);
9784
+ fs37.chmodSync(hookPath, 493);
9785
+ return { installed: true, alreadyInstalled: false };
9786
+ }
9787
+ function removePreCommitHook() {
9788
+ const hookPath = getPreCommitPath();
9789
+ if (!hookPath || !fs37.existsSync(hookPath)) {
9790
+ return { removed: false, notFound: true };
9791
+ }
9792
+ let content = fs37.readFileSync(hookPath, "utf-8");
9793
+ if (!content.includes(PRECOMMIT_START)) {
9794
+ return { removed: false, notFound: true };
9795
+ }
9796
+ const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
9797
+ content = content.replace(regex, "\n");
9798
+ if (content.trim() === "#!/bin/sh" || content.trim() === "") {
9799
+ fs37.unlinkSync(hookPath);
9800
+ } else {
9801
+ fs37.writeFileSync(hookPath, content);
9802
+ }
9803
+ return { removed: true, notFound: false };
9804
+ }
9805
+
9806
+ // src/commands/hooks.ts
9842
9807
  var HOOKS = [
9843
9808
  {
9844
9809
  id: "session-end",
@@ -9869,6 +9834,10 @@ function printStatus() {
9869
9834
  console.log("");
9870
9835
  }
9871
9836
  async function hooksCommand(options) {
9837
+ if (!options.install && !options.remove) {
9838
+ console.log(chalk20.dim("\n Note: caliber now adds refresh instructions directly to config files."));
9839
+ console.log(chalk20.dim(" These hooks are available for non-agent workflows (manual commits).\n"));
9840
+ }
9872
9841
  if (options.install) {
9873
9842
  for (const hook of HOOKS) {
9874
9843
  const result = hook.install();