@rely-ai/caliber 1.41.4 → 1.43.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 +775 -479
  2. package/package.json +2 -1
package/dist/bin.js CHANGED
@@ -169,19 +169,44 @@ import fs6 from "fs";
169
169
  import { execSync as execSync4 } from "child_process";
170
170
  function resolveCaliber() {
171
171
  if (_resolved) return _resolved;
172
+ const whichCmd = process.platform === "win32" ? "where caliber" : "which caliber";
173
+ const whichNpxCmd = process.platform === "win32" ? "where npx" : "which npx";
172
174
  const isNpx = process.argv[1]?.includes("_npx") || process.env.npm_execpath?.includes("npx");
173
175
  if (isNpx) {
176
+ try {
177
+ const out = execSync4(whichCmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
178
+ const caliberPath = out.split("\n")[0].trim();
179
+ if (caliberPath) {
180
+ _resolved = caliberPath;
181
+ return _resolved;
182
+ }
183
+ } catch {
184
+ }
185
+ try {
186
+ const out = execSync4(whichNpxCmd, {
187
+ encoding: "utf-8",
188
+ stdio: ["pipe", "pipe", "pipe"]
189
+ }).trim();
190
+ const npxPath = out.split("\n")[0].trim();
191
+ if (npxPath) {
192
+ _resolved = `${npxPath} --yes @rely-ai/caliber`;
193
+ return _resolved;
194
+ }
195
+ } catch {
196
+ }
174
197
  _resolved = "npx --yes @rely-ai/caliber";
175
198
  return _resolved;
176
199
  }
177
200
  try {
178
- const whichCmd = process.platform === "win32" ? "where caliber" : "which caliber";
179
- execSync4(whichCmd, {
201
+ const out = execSync4(whichCmd, {
180
202
  encoding: "utf-8",
181
203
  stdio: ["pipe", "pipe", "pipe"]
182
- });
183
- _resolved = "caliber";
184
- return _resolved;
204
+ }).trim();
205
+ const caliberPath = out.split("\n")[0].trim();
206
+ if (caliberPath) {
207
+ _resolved = caliberPath;
208
+ return _resolved;
209
+ }
185
210
  } catch {
186
211
  }
187
212
  const binPath = process.argv[1];
@@ -193,7 +218,8 @@ function resolveCaliber() {
193
218
  return _resolved;
194
219
  }
195
220
  function isNpxResolution() {
196
- return resolveCaliber().startsWith("npx ");
221
+ const r = resolveCaliber();
222
+ return r === "npx --yes @rely-ai/caliber" || r.endsWith("/npx --yes @rely-ai/caliber");
197
223
  }
198
224
  function resetResolvedCaliber() {
199
225
  _resolved = null;
@@ -203,6 +229,8 @@ function isCaliberCommand(command, subcommandTail) {
203
229
  if (command.endsWith(`/caliber ${subcommandTail}`)) return true;
204
230
  if (command === `npx --yes @rely-ai/caliber ${subcommandTail}`) return true;
205
231
  if (command === `npx @rely-ai/caliber ${subcommandTail}`) return true;
232
+ if (command.endsWith(`/npx --yes @rely-ai/caliber ${subcommandTail}`)) return true;
233
+ if (command.endsWith(`/npx @rely-ai/caliber ${subcommandTail}`)) return true;
206
234
  return false;
207
235
  }
208
236
  var _resolved;
@@ -234,6 +262,7 @@ var pre_commit_block_exports = {};
234
262
  __export(pre_commit_block_exports, {
235
263
  appendLearningsBlock: () => appendLearningsBlock,
236
264
  appendManagedBlocks: () => appendManagedBlocks,
265
+ appendModelBlock: () => appendModelBlock,
237
266
  appendPreCommitBlock: () => appendPreCommitBlock,
238
267
  appendSyncBlock: () => appendSyncBlock,
239
268
  getCursorLearningsRule: () => getCursorLearningsRule,
@@ -241,6 +270,7 @@ __export(pre_commit_block_exports, {
241
270
  getCursorSetupRule: () => getCursorSetupRule,
242
271
  getCursorSyncRule: () => getCursorSyncRule,
243
272
  hasLearningsBlock: () => hasLearningsBlock,
273
+ hasModelBlock: () => hasModelBlock,
244
274
  hasPreCommitBlock: () => hasPreCommitBlock,
245
275
  hasSyncBlock: () => hasSyncBlock,
246
276
  stripManagedBlocks: () => stripManagedBlocks
@@ -317,6 +347,25 @@ function appendLearningsBlock(content) {
317
347
  function getCursorLearningsRule() {
318
348
  return { filename: CURSOR_LEARNINGS_FILENAME, content: CURSOR_LEARNINGS_CONTENT };
319
349
  }
350
+ function buildManagedModelBlock() {
351
+ const m = DEFAULT_MODELS.anthropic;
352
+ return `${MODEL_BLOCK_START}
353
+ ## Model Configuration
354
+
355
+ Recommended default: \`${m}\` with high effort (stronger reasoning; higher cost and latency than smaller models).
356
+ Smaller/faster models trade quality for speed and cost \u2014 pick what fits the task.
357
+ Pin your choice (\`/model\` in Claude Code, or \`CALIBER_MODEL\` when using Caliber with an API provider) so upstream default changes do not silently change behavior.
358
+
359
+ ${MODEL_BLOCK_END}`;
360
+ }
361
+ function hasModelBlock(content) {
362
+ return content.includes(MODEL_BLOCK_START);
363
+ }
364
+ function appendModelBlock(content) {
365
+ if (hasModelBlock(content)) return content;
366
+ const trimmed = content.trimEnd();
367
+ return trimmed + "\n\n" + buildManagedModelBlock() + "\n";
368
+ }
320
369
  function getSyncSetupInstruction(platform) {
321
370
  switch (platform) {
322
371
  case "claude":
@@ -351,7 +400,10 @@ function appendSyncBlock(content, platform = "claude") {
351
400
  return trimmed + "\n\n" + getSyncBlock(platform) + "\n";
352
401
  }
353
402
  function appendManagedBlocks(content, platform = "claude") {
354
- return appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(content, platform)), platform);
403
+ return appendSyncBlock(
404
+ appendModelBlock(appendLearningsBlock(appendPreCommitBlock(content, platform))),
405
+ platform
406
+ );
355
407
  }
356
408
  function getCursorSyncContent() {
357
409
  const bin = resolveCaliber();
@@ -379,7 +431,7 @@ grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "SYNCED" || echo "NO
379
431
  \`\`\`
380
432
 
381
433
  If SYNCED: proceed normally.
382
- If NOT_SYNCED: Tell the user: "This project uses Caliber to keep agent configs in sync. It's not set up on your machine yet \u2014 want me to set it up? (~30 seconds)". If they agree, read \`.cursor/skills/setup-caliber/SKILL.md\` and follow its instructions.
434
+ If NOT_SYNCED: Tell the user: "This project uses Caliber to keep agent configs in sync \u2014 it is not set up yet. Want me to run /setup-caliber? (~30 seconds)". If they agree, read \`.cursor/skills/setup-caliber/SKILL.md\` and follow its instructions.
383
435
  `;
384
436
  }
385
437
  function getCursorSetupRule() {
@@ -396,11 +448,12 @@ function stripManagedBlocks(content) {
396
448
  }
397
449
  return result.replace(/\n{3,}/g, "\n\n").trim() + "\n";
398
450
  }
399
- var BLOCK_START, BLOCK_END, MANAGED_DOC_PATHS, CURSOR_RULE_FILENAME, LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END, LEARNINGS_BLOCK, CURSOR_LEARNINGS_FILENAME, CURSOR_LEARNINGS_CONTENT, SYNC_BLOCK_START, SYNC_BLOCK_END, CURSOR_SYNC_FILENAME, CURSOR_SETUP_FILENAME, MANAGED_BLOCK_PAIRS;
451
+ var BLOCK_START, BLOCK_END, MANAGED_DOC_PATHS, CURSOR_RULE_FILENAME, LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END, LEARNINGS_BLOCK, CURSOR_LEARNINGS_FILENAME, CURSOR_LEARNINGS_CONTENT, MODEL_BLOCK_START, MODEL_BLOCK_END, SYNC_BLOCK_START, SYNC_BLOCK_END, CURSOR_SYNC_FILENAME, CURSOR_SETUP_FILENAME, MANAGED_BLOCK_PAIRS;
400
452
  var init_pre_commit_block = __esm({
401
453
  "src/writers/pre-commit-block.ts"() {
402
454
  "use strict";
403
455
  init_resolve_caliber();
456
+ init_config();
404
457
  BLOCK_START = "<!-- caliber:managed:pre-commit -->";
405
458
  BLOCK_END = "<!-- /caliber:managed:pre-commit -->";
406
459
  MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .cursorrules .github/copilot-instructions.md .github/instructions/ AGENTS.md CALIBER_LEARNINGS.md .agents/ .opencode/";
@@ -421,6 +474,8 @@ alwaysApply: true
421
474
  Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
422
475
  These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
423
476
  `;
477
+ MODEL_BLOCK_START = "<!-- caliber:managed:model-config -->";
478
+ MODEL_BLOCK_END = "<!-- /caliber:managed:model-config -->";
424
479
  SYNC_BLOCK_START = "<!-- caliber:managed:sync -->";
425
480
  SYNC_BLOCK_END = "<!-- /caliber:managed:sync -->";
426
481
  CURSOR_SYNC_FILENAME = "caliber-sync.mdc";
@@ -428,6 +483,7 @@ These are auto-extracted from real tool usage \u2014 treat them as project-speci
428
483
  MANAGED_BLOCK_PAIRS = [
429
484
  [BLOCK_START, BLOCK_END],
430
485
  [LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END],
486
+ [MODEL_BLOCK_START, MODEL_BLOCK_END],
431
487
  [SYNC_BLOCK_START, SYNC_BLOCK_END]
432
488
  ];
433
489
  }
@@ -445,7 +501,7 @@ __export(builtin_skills_exports, {
445
501
  buildSkillContent: () => buildSkillContent,
446
502
  ensureBuiltinSkills: () => ensureBuiltinSkills
447
503
  });
448
- import fs16 from "fs";
504
+ import fs17 from "fs";
449
505
  import path16 from "path";
450
506
  function buildSkillContent(skill) {
451
507
  const frontmatter = `---
@@ -779,11 +835,11 @@ From now on, every commit keeps all your agent configs in sync automatically.
779
835
  function ensureBuiltinSkills() {
780
836
  const written = [];
781
837
  for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
782
- if (!fs16.existsSync(platformDir)) continue;
838
+ if (!fs17.existsSync(platformDir)) continue;
783
839
  for (const skill of BUILTIN_SKILLS) {
784
840
  const skillPath = path16.join(skillsDir, skill.name, "SKILL.md");
785
- fs16.mkdirSync(path16.dirname(skillPath), { recursive: true });
786
- fs16.writeFileSync(skillPath, buildSkillContent(skill));
841
+ fs17.mkdirSync(path16.dirname(skillPath), { recursive: true });
842
+ fs17.writeFileSync(skillPath, buildSkillContent(skill));
787
843
  written.push(skillPath);
788
844
  }
789
845
  }
@@ -828,13 +884,13 @@ var init_builtin_skills = __esm({
828
884
 
829
885
  // src/utils/editor.ts
830
886
  import { execSync as execSync14, spawn as spawn3 } from "child_process";
831
- import fs28 from "fs";
887
+ import fs29 from "fs";
832
888
  import path25 from "path";
833
889
  import os6 from "os";
834
890
  function getEmptyFilePath(proposedPath) {
835
- fs28.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
891
+ fs29.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
836
892
  const tempPath = path25.join(DIFF_TEMP_DIR, path25.basename(proposedPath));
837
- fs28.writeFileSync(tempPath, "");
893
+ fs29.writeFileSync(tempPath, "");
838
894
  return tempPath;
839
895
  }
840
896
  function commandExists(cmd) {
@@ -886,10 +942,11 @@ __export(review_exports, {
886
942
  promptWantsReview: () => promptWantsReview
887
943
  });
888
944
  import chalk10 from "chalk";
889
- import fs29 from "fs";
945
+ import fs30 from "fs";
890
946
  import select4 from "@inquirer/select";
891
947
  import { createTwoFilesPatch } from "diff";
892
948
  async function promptWantsReview() {
949
+ if (!process.stdin.isTTY) return false;
893
950
  return select4({
894
951
  message: "Would you like to review the diffs before deciding?",
895
952
  choices: [
@@ -901,6 +958,7 @@ async function promptWantsReview() {
901
958
  async function promptReviewMethod() {
902
959
  const available = detectAvailableEditors();
903
960
  if (available.length === 1) return "terminal";
961
+ if (!process.stdin.isTTY) return "terminal";
904
962
  const choices = available.map((method) => {
905
963
  switch (method) {
906
964
  case "cursor":
@@ -915,16 +973,19 @@ async function promptReviewMethod() {
915
973
  }
916
974
  async function openReview(method, stagedFiles) {
917
975
  if (method === "cursor" || method === "vscode") {
918
- openDiffsInEditor(method, stagedFiles.map((f) => ({
919
- originalPath: f.originalPath,
920
- proposedPath: f.proposedPath
921
- })));
976
+ openDiffsInEditor(
977
+ method,
978
+ stagedFiles.map((f) => ({
979
+ originalPath: f.originalPath,
980
+ proposedPath: f.proposedPath
981
+ }))
982
+ );
922
983
  console.log(chalk10.dim(" Diffs opened in your editor.\n"));
923
984
  return;
924
985
  }
925
986
  const fileInfos = stagedFiles.map((file) => {
926
- const proposed = fs29.readFileSync(file.proposedPath, "utf-8");
927
- const current = file.currentPath ? fs29.readFileSync(file.currentPath, "utf-8") : "";
987
+ const proposed = fs30.readFileSync(file.proposedPath, "utf-8");
988
+ const current = file.currentPath ? fs30.readFileSync(file.currentPath, "utf-8") : "";
928
989
  const patch = createTwoFilesPatch(
929
990
  file.isNew ? "/dev/null" : file.relativePath,
930
991
  file.relativePath,
@@ -1099,7 +1160,7 @@ __export(lock_exports, {
1099
1160
  isCaliberRunning: () => isCaliberRunning,
1100
1161
  releaseLock: () => releaseLock
1101
1162
  });
1102
- import fs40 from "fs";
1163
+ import fs41 from "fs";
1103
1164
  import path33 from "path";
1104
1165
  import os8 from "os";
1105
1166
  import crypto5 from "crypto";
@@ -1115,8 +1176,8 @@ function getLockFile() {
1115
1176
  function isCaliberRunning() {
1116
1177
  try {
1117
1178
  const lockFile = buildLockPath();
1118
- if (!fs40.existsSync(lockFile)) return false;
1119
- const raw = fs40.readFileSync(lockFile, "utf-8").trim();
1179
+ if (!fs41.existsSync(lockFile)) return false;
1180
+ const raw = fs41.readFileSync(lockFile, "utf-8").trim();
1120
1181
  const { pid, ts } = JSON.parse(raw);
1121
1182
  if (pid === process.pid) return false;
1122
1183
  if (Date.now() - ts > STALE_MS) return false;
@@ -1132,14 +1193,14 @@ function isCaliberRunning() {
1132
1193
  }
1133
1194
  function acquireLock() {
1134
1195
  try {
1135
- fs40.writeFileSync(getLockFile(), JSON.stringify({ pid: process.pid, ts: Date.now() }));
1196
+ fs41.writeFileSync(getLockFile(), JSON.stringify({ pid: process.pid, ts: Date.now() }));
1136
1197
  } catch {
1137
1198
  }
1138
1199
  }
1139
1200
  function releaseLock() {
1140
1201
  try {
1141
1202
  const lockFile = getLockFile();
1142
- if (fs40.existsSync(lockFile)) fs40.unlinkSync(lockFile);
1203
+ if (fs41.existsSync(lockFile)) fs41.unlinkSync(lockFile);
1143
1204
  } catch {
1144
1205
  }
1145
1206
  }
@@ -1154,17 +1215,17 @@ var init_lock = __esm({
1154
1215
 
1155
1216
  // src/cli.ts
1156
1217
  import { Command } from "commander";
1157
- import fs51 from "fs";
1218
+ import fs52 from "fs";
1158
1219
  import path42 from "path";
1159
1220
  import { fileURLToPath } from "url";
1160
1221
 
1161
1222
  // src/commands/init.ts
1162
1223
  import path28 from "path";
1163
1224
  import chalk14 from "chalk";
1164
- import fs34 from "fs";
1225
+ import fs35 from "fs";
1165
1226
 
1166
1227
  // src/fingerprint/index.ts
1167
- import fs8 from "fs";
1228
+ import fs9 from "fs";
1168
1229
  import path8 from "path";
1169
1230
 
1170
1231
  // src/fingerprint/git.ts
@@ -2358,7 +2419,7 @@ var OpenAICompatProvider = class {
2358
2419
  };
2359
2420
 
2360
2421
  // src/llm/cursor-acp.ts
2361
- import { spawn, execSync as execSync5 } from "child_process";
2422
+ import { spawn, execSync as execSync5, execFileSync } from "child_process";
2362
2423
  import os3 from "os";
2363
2424
 
2364
2425
  // src/llm/seat-based-errors.ts
@@ -2430,8 +2491,23 @@ function estimateTokens(text) {
2430
2491
  }
2431
2492
 
2432
2493
  // src/llm/cursor-acp.ts
2433
- var AGENT_BIN = "agent";
2434
2494
  var IS_WINDOWS = process.platform === "win32";
2495
+ var _agentBin = null;
2496
+ function resolveAgentBin() {
2497
+ if (_agentBin !== null) return _agentBin;
2498
+ try {
2499
+ const whichCmd = IS_WINDOWS ? "where agent" : "which agent";
2500
+ const out = execSync5(whichCmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
2501
+ const p = out.split("\n")[0].trim();
2502
+ if (p) {
2503
+ _agentBin = p;
2504
+ return _agentBin;
2505
+ }
2506
+ } catch {
2507
+ }
2508
+ _agentBin = "agent";
2509
+ return _agentBin;
2510
+ }
2435
2511
  var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
2436
2512
  var SIGKILL_DELAY_MS = 5e3;
2437
2513
  var STDERR_MAX_BYTES = 10 * 1024;
@@ -2489,7 +2565,7 @@ var CursorAcpProvider = class {
2489
2565
  const targetModel = model || this.defaultModel;
2490
2566
  if (this.warmProcess && !this.warmProcess.killed && this.warmModel === targetModel) return;
2491
2567
  const args = this.buildArgs(targetModel, false);
2492
- this.warmProcess = spawn(AGENT_BIN, args, {
2568
+ this.warmProcess = spawn(resolveAgentBin(), args, {
2493
2569
  stdio: ["pipe", "pipe", "pipe"],
2494
2570
  env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
2495
2571
  ...IS_WINDOWS && { shell: true }
@@ -2536,7 +2612,7 @@ var CursorAcpProvider = class {
2536
2612
  return { child: warm, stderrChunks: stderrChunks2 };
2537
2613
  }
2538
2614
  const args = this.buildArgs(model, streaming);
2539
- const child = spawn(AGENT_BIN, args, {
2615
+ const child = spawn(resolveAgentBin(), args, {
2540
2616
  stdio: ["pipe", "pipe", "pipe"],
2541
2617
  env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
2542
2618
  ...IS_WINDOWS && { shell: true }
@@ -2710,9 +2786,9 @@ var CursorAcpProvider = class {
2710
2786
  }
2711
2787
  };
2712
2788
  function isCursorAgentAvailable() {
2789
+ if (resolveAgentBin() !== "agent") return true;
2713
2790
  try {
2714
- const cmd = IS_WINDOWS ? `where ${AGENT_BIN}` : `which ${AGENT_BIN}`;
2715
- execSync5(cmd, { stdio: "ignore" });
2791
+ execSync5(IS_WINDOWS ? "where agent" : "which agent", { stdio: "ignore" });
2716
2792
  return true;
2717
2793
  } catch {
2718
2794
  return false;
@@ -2720,7 +2796,7 @@ function isCursorAgentAvailable() {
2720
2796
  }
2721
2797
  function isCursorLoggedIn() {
2722
2798
  try {
2723
- const result = execSync5(`${AGENT_BIN} status`, {
2799
+ const result = execFileSync(resolveAgentBin(), ["status"], {
2724
2800
  input: "",
2725
2801
  stdio: ["pipe", "pipe", "pipe"],
2726
2802
  timeout: 5e3
@@ -2732,18 +2808,56 @@ function isCursorLoggedIn() {
2732
2808
  }
2733
2809
 
2734
2810
  // src/llm/claude-cli.ts
2735
- import { spawn as spawn2, execSync as execSync6 } from "child_process";
2736
- var CLAUDE_CLI_BIN = "claude";
2811
+ import fs7 from "fs";
2812
+ import { spawn as spawn2, execSync as execSync6, execFileSync as execFileSync2 } from "child_process";
2737
2813
  var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
2738
2814
  var IS_WINDOWS2 = process.platform === "win32";
2815
+ function candidateClaudePaths() {
2816
+ if (IS_WINDOWS2) return [];
2817
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
2818
+ return [
2819
+ `${home}/.local/bin/claude`,
2820
+ // Claude Code default installer path
2821
+ "/usr/local/bin/claude",
2822
+ // Homebrew / manual install
2823
+ "/opt/homebrew/bin/claude"
2824
+ // Apple Silicon Homebrew
2825
+ ].filter(Boolean);
2826
+ }
2827
+ var _claudeBin = null;
2828
+ function resolveClaudeBin() {
2829
+ if (_claudeBin !== null) return _claudeBin;
2830
+ try {
2831
+ const whichCmd = IS_WINDOWS2 ? "where claude" : "which claude";
2832
+ const out = execSync6(whichCmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
2833
+ const p = out.split("\n")[0].trim();
2834
+ if (p) {
2835
+ _claudeBin = p;
2836
+ return _claudeBin;
2837
+ }
2838
+ } catch {
2839
+ }
2840
+ for (const candidate of candidateClaudePaths()) {
2841
+ try {
2842
+ fs7.accessSync(candidate, fs7.constants.X_OK);
2843
+ _claudeBin = candidate;
2844
+ return _claudeBin;
2845
+ } catch {
2846
+ }
2847
+ }
2848
+ _claudeBin = "claude";
2849
+ return _claudeBin;
2850
+ }
2739
2851
  function spawnClaude(args) {
2740
- const env = { ...process.env, CLAUDE_CODE_SIMPLE: "1" };
2741
- return IS_WINDOWS2 ? spawn2([CLAUDE_CLI_BIN, ...args].join(" "), {
2852
+ const bin = resolveClaudeBin();
2853
+ const { CLAUDE_CODE_SIMPLE: _stripped, ...parentEnv } = process.env;
2854
+ const env = parentEnv;
2855
+ return IS_WINDOWS2 ? spawn2([bin, ...args].join(" "), {
2742
2856
  cwd: process.cwd(),
2743
2857
  stdio: ["pipe", "pipe", "pipe"],
2744
2858
  env,
2745
2859
  shell: true
2746
- }) : spawn2(CLAUDE_CLI_BIN, args, {
2860
+ }) : spawn2(bin, args, {
2747
2861
  cwd: process.cwd(),
2748
2862
  stdio: ["pipe", "pipe", "pipe"],
2749
2863
  env
@@ -2818,8 +2932,8 @@ var ClaudeCliProvider = class {
2818
2932
  callbacks.onEnd({ stopReason: "end_turn" });
2819
2933
  } else {
2820
2934
  const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
2821
- const friendly = parseSeatBasedError(stderr, code);
2822
2935
  const stdout = Buffer.concat(chunks).toString("utf-8").trim();
2936
+ const friendly = parseSeatBasedError(stderr || stdout, code);
2823
2937
  const base = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
2824
2938
  const detail = friendly || stderr || (stdout ? stdout.slice(0, 200) : "");
2825
2939
  callbacks.onError(new Error(detail ? `${base}. ${detail}` : base));
@@ -2879,7 +2993,7 @@ var ClaudeCliProvider = class {
2879
2993
  resolve3(stdout);
2880
2994
  } else {
2881
2995
  const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
2882
- const friendly = parseSeatBasedError(stderr, code);
2996
+ const friendly = parseSeatBasedError(stderr || stdout, code);
2883
2997
  const base = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
2884
2998
  const detail = friendly || stderr || (stdout ? stdout.slice(0, 200) : "");
2885
2999
  reject(new Error(detail ? `${base}. ${detail}` : base));
@@ -2889,9 +3003,9 @@ var ClaudeCliProvider = class {
2889
3003
  }
2890
3004
  };
2891
3005
  function isClaudeCliAvailable() {
3006
+ if (resolveClaudeBin() !== "claude") return true;
2892
3007
  try {
2893
- const cmd = process.platform === "win32" ? `where ${CLAUDE_CLI_BIN}` : `which ${CLAUDE_CLI_BIN}`;
2894
- execSync6(cmd, { stdio: "ignore" });
3008
+ execSync6(IS_WINDOWS2 ? "where claude" : "which claude", { stdio: "ignore" });
2895
3009
  return true;
2896
3010
  } catch {
2897
3011
  return false;
@@ -2901,7 +3015,7 @@ var cachedLoggedIn = null;
2901
3015
  function isClaudeCliLoggedIn() {
2902
3016
  if (cachedLoggedIn !== null) return cachedLoggedIn;
2903
3017
  try {
2904
- const result = execSync6(`${CLAUDE_CLI_BIN} auth status`, {
3018
+ const result = execFileSync2(resolveClaudeBin(), ["auth", "status"], {
2905
3019
  input: "",
2906
3020
  stdio: ["pipe", "pipe", "pipe"],
2907
3021
  timeout: 5e3
@@ -3720,7 +3834,7 @@ async function detectProjectStack(fileTree, suffixCounts) {
3720
3834
  init_config();
3721
3835
 
3722
3836
  // src/fingerprint/cache.ts
3723
- import fs7 from "fs";
3837
+ import fs8 from "fs";
3724
3838
  import path7 from "path";
3725
3839
  import crypto from "crypto";
3726
3840
  import { execSync as execSync7 } from "child_process";
@@ -3765,8 +3879,8 @@ function computeTreeSignature(fileTree, dir) {
3765
3879
  function loadFingerprintCache(dir, fileTree) {
3766
3880
  const cachePath = getCachePath(dir);
3767
3881
  try {
3768
- if (!fs7.existsSync(cachePath)) return null;
3769
- const raw = fs7.readFileSync(cachePath, "utf-8");
3882
+ if (!fs8.existsSync(cachePath)) return null;
3883
+ const raw = fs8.readFileSync(cachePath, "utf-8");
3770
3884
  const cache = JSON.parse(raw);
3771
3885
  if (cache.version !== CACHE_VERSION) return null;
3772
3886
  const currentHead = getGitHead(dir);
@@ -3788,8 +3902,8 @@ function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks
3788
3902
  const cachePath = getCachePath(dir);
3789
3903
  try {
3790
3904
  const cacheDir = path7.dirname(cachePath);
3791
- if (!fs7.existsSync(cacheDir)) {
3792
- fs7.mkdirSync(cacheDir, { recursive: true });
3905
+ if (!fs8.existsSync(cacheDir)) {
3906
+ fs8.mkdirSync(cacheDir, { recursive: true });
3793
3907
  }
3794
3908
  const cache = {
3795
3909
  version: CACHE_VERSION,
@@ -3801,15 +3915,15 @@ function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks
3801
3915
  tools,
3802
3916
  workspaces
3803
3917
  };
3804
- fs7.writeFileSync(cachePath, JSON.stringify(cache), "utf-8");
3918
+ fs8.writeFileSync(cachePath, JSON.stringify(cache), "utf-8");
3805
3919
  } catch {
3806
3920
  }
3807
3921
  }
3808
3922
  function getDetectedWorkspaces(dir) {
3809
3923
  const cachePath = getCachePath(dir);
3810
3924
  try {
3811
- if (!fs7.existsSync(cachePath)) return [];
3812
- const raw = fs7.readFileSync(cachePath, "utf-8");
3925
+ if (!fs8.existsSync(cachePath)) return [];
3926
+ const raw = fs8.readFileSync(cachePath, "utf-8");
3813
3927
  const cache = JSON.parse(raw);
3814
3928
  return cache.workspaces ?? [];
3815
3929
  } catch {
@@ -3862,8 +3976,8 @@ async function collectFingerprint(dir) {
3862
3976
  function readPackageName(dir) {
3863
3977
  try {
3864
3978
  const pkgPath = path8.join(dir, "package.json");
3865
- if (!fs8.existsSync(pkgPath)) return void 0;
3866
- const pkg3 = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
3979
+ if (!fs9.existsSync(pkgPath)) return void 0;
3980
+ const pkg3 = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
3867
3981
  return pkg3.name;
3868
3982
  } catch {
3869
3983
  return void 0;
@@ -3893,23 +4007,23 @@ async function enrichWithLLM(fingerprint) {
3893
4007
  }
3894
4008
 
3895
4009
  // src/scanner/index.ts
3896
- import fs9 from "fs";
4010
+ import fs10 from "fs";
3897
4011
  import path9 from "path";
3898
4012
  import crypto2 from "crypto";
3899
4013
  import os4 from "os";
3900
4014
  function detectPlatforms() {
3901
4015
  const home = os4.homedir();
3902
4016
  return {
3903
- claude: fs9.existsSync(path9.join(home, ".claude")),
3904
- cursor: fs9.existsSync(getCursorConfigDir()),
3905
- codex: fs9.existsSync(path9.join(home, ".codex")),
3906
- opencode: fs9.existsSync(path9.join(home, ".config", "opencode"))
4017
+ claude: fs10.existsSync(path9.join(home, ".claude")),
4018
+ cursor: fs10.existsSync(getCursorConfigDir()),
4019
+ codex: fs10.existsSync(path9.join(home, ".codex")),
4020
+ opencode: fs10.existsSync(path9.join(home, ".config", "opencode"))
3907
4021
  };
3908
4022
  }
3909
4023
  function scanLocalState(dir) {
3910
4024
  const items = [];
3911
4025
  const claudeMdPath = path9.join(dir, "CLAUDE.md");
3912
- if (fs9.existsSync(claudeMdPath)) {
4026
+ if (fs10.existsSync(claudeMdPath)) {
3913
4027
  items.push({
3914
4028
  type: "rule",
3915
4029
  platform: "claude",
@@ -3919,8 +4033,8 @@ function scanLocalState(dir) {
3919
4033
  });
3920
4034
  }
3921
4035
  const skillsDir = path9.join(dir, ".claude", "skills");
3922
- if (fs9.existsSync(skillsDir)) {
3923
- for (const file of fs9.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
4036
+ if (fs10.existsSync(skillsDir)) {
4037
+ for (const file of fs10.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
3924
4038
  const filePath = path9.join(skillsDir, file);
3925
4039
  items.push({
3926
4040
  type: "skill",
@@ -3932,9 +4046,9 @@ function scanLocalState(dir) {
3932
4046
  }
3933
4047
  }
3934
4048
  const mcpJsonPath = path9.join(dir, ".mcp.json");
3935
- if (fs9.existsSync(mcpJsonPath)) {
4049
+ if (fs10.existsSync(mcpJsonPath)) {
3936
4050
  try {
3937
- const mcpJson = JSON.parse(fs9.readFileSync(mcpJsonPath, "utf-8"));
4051
+ const mcpJson = JSON.parse(fs10.readFileSync(mcpJsonPath, "utf-8"));
3938
4052
  if (mcpJson.mcpServers) {
3939
4053
  for (const name of Object.keys(mcpJson.mcpServers)) {
3940
4054
  items.push({
@@ -3951,7 +4065,7 @@ function scanLocalState(dir) {
3951
4065
  }
3952
4066
  }
3953
4067
  const agentsMdPath = path9.join(dir, "AGENTS.md");
3954
- if (fs9.existsSync(agentsMdPath)) {
4068
+ if (fs10.existsSync(agentsMdPath)) {
3955
4069
  items.push({
3956
4070
  type: "rule",
3957
4071
  platform: "codex",
@@ -3961,11 +4075,11 @@ function scanLocalState(dir) {
3961
4075
  });
3962
4076
  }
3963
4077
  const codexSkillsDir = path9.join(dir, ".agents", "skills");
3964
- if (fs9.existsSync(codexSkillsDir)) {
4078
+ if (fs10.existsSync(codexSkillsDir)) {
3965
4079
  try {
3966
- for (const name of fs9.readdirSync(codexSkillsDir)) {
4080
+ for (const name of fs10.readdirSync(codexSkillsDir)) {
3967
4081
  const skillFile = path9.join(codexSkillsDir, name, "SKILL.md");
3968
- if (fs9.existsSync(skillFile)) {
4082
+ if (fs10.existsSync(skillFile)) {
3969
4083
  items.push({
3970
4084
  type: "skill",
3971
4085
  platform: "codex",
@@ -3980,11 +4094,11 @@ function scanLocalState(dir) {
3980
4094
  }
3981
4095
  }
3982
4096
  const opencodeSkillsDir = path9.join(dir, ".opencode", "skills");
3983
- if (fs9.existsSync(opencodeSkillsDir)) {
4097
+ if (fs10.existsSync(opencodeSkillsDir)) {
3984
4098
  try {
3985
- for (const name of fs9.readdirSync(opencodeSkillsDir)) {
4099
+ for (const name of fs10.readdirSync(opencodeSkillsDir)) {
3986
4100
  const skillFile = path9.join(opencodeSkillsDir, name, "SKILL.md");
3987
- if (fs9.existsSync(skillFile)) {
4101
+ if (fs10.existsSync(skillFile)) {
3988
4102
  items.push({
3989
4103
  type: "skill",
3990
4104
  platform: "opencode",
@@ -3999,7 +4113,7 @@ function scanLocalState(dir) {
3999
4113
  }
4000
4114
  }
4001
4115
  const cursorrulesPath = path9.join(dir, ".cursorrules");
4002
- if (fs9.existsSync(cursorrulesPath)) {
4116
+ if (fs10.existsSync(cursorrulesPath)) {
4003
4117
  items.push({
4004
4118
  type: "rule",
4005
4119
  platform: "cursor",
@@ -4009,8 +4123,8 @@ function scanLocalState(dir) {
4009
4123
  });
4010
4124
  }
4011
4125
  const cursorRulesDir = path9.join(dir, ".cursor", "rules");
4012
- if (fs9.existsSync(cursorRulesDir)) {
4013
- for (const file of fs9.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
4126
+ if (fs10.existsSync(cursorRulesDir)) {
4127
+ for (const file of fs10.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
4014
4128
  const filePath = path9.join(cursorRulesDir, file);
4015
4129
  items.push({
4016
4130
  type: "rule",
@@ -4022,11 +4136,11 @@ function scanLocalState(dir) {
4022
4136
  }
4023
4137
  }
4024
4138
  const cursorSkillsDir = path9.join(dir, ".cursor", "skills");
4025
- if (fs9.existsSync(cursorSkillsDir)) {
4139
+ if (fs10.existsSync(cursorSkillsDir)) {
4026
4140
  try {
4027
- for (const name of fs9.readdirSync(cursorSkillsDir)) {
4141
+ for (const name of fs10.readdirSync(cursorSkillsDir)) {
4028
4142
  const skillFile = path9.join(cursorSkillsDir, name, "SKILL.md");
4029
- if (fs9.existsSync(skillFile)) {
4143
+ if (fs10.existsSync(skillFile)) {
4030
4144
  items.push({
4031
4145
  type: "skill",
4032
4146
  platform: "cursor",
@@ -4041,9 +4155,9 @@ function scanLocalState(dir) {
4041
4155
  }
4042
4156
  }
4043
4157
  const cursorMcpPath = path9.join(dir, ".cursor", "mcp.json");
4044
- if (fs9.existsSync(cursorMcpPath)) {
4158
+ if (fs10.existsSync(cursorMcpPath)) {
4045
4159
  try {
4046
- const mcpJson = JSON.parse(fs9.readFileSync(cursorMcpPath, "utf-8"));
4160
+ const mcpJson = JSON.parse(fs10.readFileSync(cursorMcpPath, "utf-8"));
4047
4161
  if (mcpJson.mcpServers) {
4048
4162
  for (const name of Object.keys(mcpJson.mcpServers)) {
4049
4163
  items.push({
@@ -4062,7 +4176,7 @@ function scanLocalState(dir) {
4062
4176
  return items;
4063
4177
  }
4064
4178
  function hashFile(filePath) {
4065
- const text = fs9.readFileSync(filePath, "utf-8");
4179
+ const text = fs10.readFileSync(filePath, "utf-8");
4066
4180
  return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
4067
4181
  }
4068
4182
  function hashJson(obj) {
@@ -4085,7 +4199,7 @@ function getCursorConfigDir() {
4085
4199
 
4086
4200
  // src/lib/hooks.ts
4087
4201
  init_resolve_caliber();
4088
- import fs10 from "fs";
4202
+ import fs11 from "fs";
4089
4203
  import path10 from "path";
4090
4204
  import { execSync as execSync8 } from "child_process";
4091
4205
  var SETTINGS_PATH = path10.join(".claude", "settings.json");
@@ -4095,17 +4209,17 @@ function getHookCommand() {
4095
4209
  return `${resolveCaliber()} ${REFRESH_TAIL}`;
4096
4210
  }
4097
4211
  function readSettings() {
4098
- if (!fs10.existsSync(SETTINGS_PATH)) return {};
4212
+ if (!fs11.existsSync(SETTINGS_PATH)) return {};
4099
4213
  try {
4100
- return JSON.parse(fs10.readFileSync(SETTINGS_PATH, "utf-8"));
4214
+ return JSON.parse(fs11.readFileSync(SETTINGS_PATH, "utf-8"));
4101
4215
  } catch {
4102
4216
  return {};
4103
4217
  }
4104
4218
  }
4105
4219
  function writeSettings(settings) {
4106
4220
  const dir = path10.dirname(SETTINGS_PATH);
4107
- if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
4108
- fs10.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
4221
+ if (!fs11.existsSync(dir)) fs11.mkdirSync(dir, { recursive: true });
4222
+ fs11.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
4109
4223
  }
4110
4224
  function findHookIndex(sessionEnd) {
4111
4225
  return sessionEnd.findIndex(
@@ -4153,7 +4267,8 @@ function removeHook() {
4153
4267
  return { removed: true, notFound: false };
4154
4268
  }
4155
4269
  function createScriptHook(config) {
4156
- const { eventName, scriptPath, scriptContent, description } = config;
4270
+ const { eventName, scriptPath, description } = config;
4271
+ const getContent = () => typeof config.scriptContent === "function" ? config.scriptContent() : config.scriptContent;
4157
4272
  const hasHook = (matchers) => matchers.some((entry) => entry.hooks?.some((h) => h.description === description));
4158
4273
  function isInstalled() {
4159
4274
  const settings = readSettings();
@@ -4168,9 +4283,9 @@ function createScriptHook(config) {
4168
4283
  return { installed: false, alreadyInstalled: true };
4169
4284
  }
4170
4285
  const scriptDir = path10.dirname(scriptPath);
4171
- if (!fs10.existsSync(scriptDir)) fs10.mkdirSync(scriptDir, { recursive: true });
4172
- fs10.writeFileSync(scriptPath, scriptContent);
4173
- fs10.chmodSync(scriptPath, 493);
4286
+ if (!fs11.existsSync(scriptDir)) fs11.mkdirSync(scriptDir, { recursive: true });
4287
+ fs11.writeFileSync(scriptPath, getContent());
4288
+ fs11.chmodSync(scriptPath, 493);
4174
4289
  if (!Array.isArray(settings.hooks[eventName])) {
4175
4290
  settings.hooks[eventName] = [];
4176
4291
  }
@@ -4194,7 +4309,7 @@ function createScriptHook(config) {
4194
4309
  if (settings.hooks && Object.keys(settings.hooks).length === 0) delete settings.hooks;
4195
4310
  writeSettings(settings);
4196
4311
  try {
4197
- fs10.unlinkSync(scriptPath);
4312
+ fs11.unlinkSync(scriptPath);
4198
4313
  } catch {
4199
4314
  }
4200
4315
  return { removed: true, notFound: false };
@@ -4221,7 +4336,9 @@ var stopHook = createScriptHook({
4221
4336
  });
4222
4337
  var installStopHook = stopHook.install;
4223
4338
  var removeStopHook = stopHook.remove;
4224
- var FRESHNESS_SCRIPT = `#!/bin/sh
4339
+ function getFreshnessScript() {
4340
+ const bin = resolveCaliber();
4341
+ return `#!/bin/sh
4225
4342
  STATE_FILE=".caliber/.caliber-state.json"
4226
4343
  [ ! -f "$STATE_FILE" ] && exit 0
4227
4344
  LAST_SHA=$(grep -o '"lastRefreshSha":"[^"]*"' "$STATE_FILE" 2>/dev/null | cut -d'"' -f4)
@@ -4230,13 +4347,14 @@ CURRENT_SHA=$(git rev-parse HEAD 2>/dev/null)
4230
4347
  [ "$LAST_SHA" = "$CURRENT_SHA" ] && exit 0
4231
4348
  COMMITS_BEHIND=$(git rev-list --count "$LAST_SHA".."$CURRENT_SHA" 2>/dev/null || echo 0)
4232
4349
  if [ "$COMMITS_BEHIND" -gt 15 ]; then
4233
- printf '{"systemMessage":"Caliber: agent configs are %s commits behind. Run caliber refresh to sync."}' "$COMMITS_BEHIND"
4350
+ printf '{"systemMessage":"Caliber: agent configs are %s commits behind. Run ${bin} refresh to sync."}' "$COMMITS_BEHIND"
4234
4351
  fi
4235
4352
  `;
4353
+ }
4236
4354
  var sessionStartHook = createScriptHook({
4237
4355
  eventName: "SessionStart",
4238
4356
  scriptPath: path10.join(".claude", "hooks", "caliber-session-freshness.sh"),
4239
- scriptContent: FRESHNESS_SCRIPT,
4357
+ scriptContent: getFreshnessScript,
4240
4358
  description: "Caliber: check config freshness on session start"
4241
4359
  });
4242
4360
  var isSessionStartHookInstalled = sessionStartHook.isInstalled;
@@ -4245,7 +4363,7 @@ var removeSessionStartHook = sessionStartHook.remove;
4245
4363
  var notificationHook = createScriptHook({
4246
4364
  eventName: "Notification",
4247
4365
  scriptPath: path10.join(".claude", "hooks", "caliber-freshness-notify.sh"),
4248
- scriptContent: FRESHNESS_SCRIPT,
4366
+ scriptContent: getFreshnessScript,
4249
4367
  description: "Caliber: warn when agent configs are stale"
4250
4368
  });
4251
4369
  var isNotificationHookInstalled = notificationHook.isInstalled;
@@ -4254,10 +4372,28 @@ var removeNotificationHook = notificationHook.remove;
4254
4372
  var PRECOMMIT_START = "# caliber:pre-commit:start";
4255
4373
  var PRECOMMIT_END = "# caliber:pre-commit:end";
4256
4374
  function getPrecommitBlock() {
4257
- const bin = resolveCaliber();
4375
+ const cmd = resolveCaliber();
4258
4376
  const npx = isNpxResolution();
4259
- const guard = npx ? "command -v npx >/dev/null 2>&1" : `[ -x "${bin}" ] || command -v "${bin}" >/dev/null 2>&1`;
4260
- const invoke = npx ? bin : `"${bin}"`;
4377
+ let guard;
4378
+ let invoke;
4379
+ if (npx) {
4380
+ const npxBin = cmd.split(" ")[0];
4381
+ if (npxBin.startsWith("/")) {
4382
+ guard = `[ -x "${npxBin}" ]`;
4383
+ const npxArgs = cmd.slice(npxBin.length);
4384
+ invoke = `"${npxBin}"${npxArgs}`;
4385
+ } else {
4386
+ guard = "command -v npx >/dev/null 2>&1";
4387
+ invoke = cmd;
4388
+ }
4389
+ } else {
4390
+ if (cmd.startsWith("/")) {
4391
+ guard = `[ -x "${cmd}" ]`;
4392
+ } else {
4393
+ guard = `[ -x "${cmd}" ] || command -v "${cmd}" >/dev/null 2>&1`;
4394
+ }
4395
+ invoke = `"${cmd}"`;
4396
+ }
4261
4397
  return `${PRECOMMIT_START}
4262
4398
  if ${guard}; then
4263
4399
  mkdir -p .caliber
@@ -4285,8 +4421,8 @@ function getPreCommitPath() {
4285
4421
  }
4286
4422
  function isPreCommitHookInstalled() {
4287
4423
  const hookPath = getPreCommitPath();
4288
- if (!hookPath || !fs10.existsSync(hookPath)) return false;
4289
- const content = fs10.readFileSync(hookPath, "utf-8");
4424
+ if (!hookPath || !fs11.existsSync(hookPath)) return false;
4425
+ const content = fs11.readFileSync(hookPath, "utf-8");
4290
4426
  return content.includes(PRECOMMIT_START);
4291
4427
  }
4292
4428
  function installPreCommitHook() {
@@ -4296,45 +4432,45 @@ function installPreCommitHook() {
4296
4432
  const hookPath = getPreCommitPath();
4297
4433
  if (!hookPath) return { installed: false, alreadyInstalled: false };
4298
4434
  const hooksDir = path10.dirname(hookPath);
4299
- if (!fs10.existsSync(hooksDir)) fs10.mkdirSync(hooksDir, { recursive: true });
4435
+ if (!fs11.existsSync(hooksDir)) fs11.mkdirSync(hooksDir, { recursive: true });
4300
4436
  let content = "";
4301
- if (fs10.existsSync(hookPath)) {
4302
- content = fs10.readFileSync(hookPath, "utf-8");
4437
+ if (fs11.existsSync(hookPath)) {
4438
+ content = fs11.readFileSync(hookPath, "utf-8");
4303
4439
  if (!content.endsWith("\n")) content += "\n";
4304
4440
  content += "\n" + getPrecommitBlock() + "\n";
4305
4441
  } else {
4306
4442
  content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
4307
4443
  }
4308
- fs10.writeFileSync(hookPath, content);
4309
- fs10.chmodSync(hookPath, 493);
4444
+ fs11.writeFileSync(hookPath, content);
4445
+ fs11.chmodSync(hookPath, 493);
4310
4446
  return { installed: true, alreadyInstalled: false };
4311
4447
  }
4312
4448
  function removePreCommitHook() {
4313
4449
  const hookPath = getPreCommitPath();
4314
- if (!hookPath || !fs10.existsSync(hookPath)) {
4450
+ if (!hookPath || !fs11.existsSync(hookPath)) {
4315
4451
  return { removed: false, notFound: true };
4316
4452
  }
4317
- let content = fs10.readFileSync(hookPath, "utf-8");
4453
+ let content = fs11.readFileSync(hookPath, "utf-8");
4318
4454
  if (!content.includes(PRECOMMIT_START)) {
4319
4455
  return { removed: false, notFound: true };
4320
4456
  }
4321
4457
  const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
4322
4458
  content = content.replace(regex, "\n");
4323
4459
  if (content.trim() === "#!/bin/sh" || content.trim() === "") {
4324
- fs10.unlinkSync(hookPath);
4460
+ fs11.unlinkSync(hookPath);
4325
4461
  } else {
4326
- fs10.writeFileSync(hookPath, content);
4462
+ fs11.writeFileSync(hookPath, content);
4327
4463
  }
4328
4464
  return { removed: true, notFound: false };
4329
4465
  }
4330
4466
 
4331
4467
  // src/fingerprint/sources.ts
4332
- import fs11 from "fs";
4468
+ import fs12 from "fs";
4333
4469
  import path11 from "path";
4334
4470
 
4335
4471
  // src/scoring/utils.ts
4336
4472
  import { existsSync as existsSync2, readFileSync, readdirSync } from "fs";
4337
- import { execFileSync } from "child_process";
4473
+ import { execFileSync as execFileSync3 } from "child_process";
4338
4474
  import { join, relative } from "path";
4339
4475
  function readFileOrNull(filePath) {
4340
4476
  try {
@@ -4384,7 +4520,7 @@ var IGNORED_FILES = /* @__PURE__ */ new Set([
4384
4520
  ]);
4385
4521
  function isGitRepo2(dir) {
4386
4522
  try {
4387
- execFileSync("git", ["rev-parse", "--git-dir"], {
4523
+ execFileSync3("git", ["rev-parse", "--git-dir"], {
4388
4524
  cwd: dir,
4389
4525
  encoding: "utf-8",
4390
4526
  stdio: ["pipe", "pipe", "pipe"]
@@ -4397,7 +4533,7 @@ function isGitRepo2(dir) {
4397
4533
  function checkGitIgnored(dir, paths) {
4398
4534
  if (paths.length === 0) return /* @__PURE__ */ new Set();
4399
4535
  try {
4400
- const result = execFileSync("git", ["check-ignore", ...paths], {
4536
+ const result = execFileSync3("git", ["check-ignore", ...paths], {
4401
4537
  cwd: dir,
4402
4538
  encoding: "utf-8",
4403
4539
  stdio: ["pipe", "pipe", "pipe"]
@@ -4678,15 +4814,15 @@ function loadSourcesConfig(dir) {
4678
4814
  }
4679
4815
  function writeSourcesConfig(dir, sources2) {
4680
4816
  const configDir = path11.join(dir, ".caliber");
4681
- if (!fs11.existsSync(configDir)) {
4682
- fs11.mkdirSync(configDir, { recursive: true });
4817
+ if (!fs12.existsSync(configDir)) {
4818
+ fs12.mkdirSync(configDir, { recursive: true });
4683
4819
  }
4684
4820
  const configPath = path11.join(configDir, "sources.json");
4685
- fs11.writeFileSync(configPath, JSON.stringify({ sources: sources2 }, null, 2) + "\n", "utf-8");
4821
+ fs12.writeFileSync(configPath, JSON.stringify({ sources: sources2 }, null, 2) + "\n", "utf-8");
4686
4822
  }
4687
4823
  function detectSourceType(absPath) {
4688
4824
  try {
4689
- return fs11.statSync(absPath).isDirectory() ? "repo" : "file";
4825
+ return fs12.statSync(absPath).isDirectory() ? "repo" : "file";
4690
4826
  } catch {
4691
4827
  return "file";
4692
4828
  }
@@ -4730,7 +4866,7 @@ function resolveAllSources(dir, cliSources, workspaces) {
4730
4866
  for (const [absPath, resolved] of seen) {
4731
4867
  let stat;
4732
4868
  try {
4733
- stat = fs11.statSync(absPath);
4869
+ stat = fs12.statSync(absPath);
4734
4870
  } catch {
4735
4871
  console.warn(`Source ${resolved.config.path || absPath} not found, skipping`);
4736
4872
  continue;
@@ -4785,7 +4921,7 @@ function collectRepoSummary(resolved, _projectDir) {
4785
4921
  let topLevelDirs;
4786
4922
  let keyFiles;
4787
4923
  try {
4788
- const entries = fs11.readdirSync(absPath, { withFileTypes: true });
4924
+ const entries = fs12.readdirSync(absPath, { withFileTypes: true });
4789
4925
  topLevelDirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name).slice(0, 20);
4790
4926
  keyFiles = entries.filter((e) => e.isFile() && !e.name.startsWith(".")).map((e) => e.name).slice(0, 15);
4791
4927
  } catch {
@@ -5396,6 +5532,13 @@ var LIMITS = {
5396
5532
  SKILL_CHARS: 3e3,
5397
5533
  RULES_MAX: 10
5398
5534
  };
5535
+ var BUILD_GENERATE_PROMPT_MAX_TOKENS = 12e4;
5536
+ var PROJECT_FILES_HEADER_RESERVE_TOKENS = 160;
5537
+ function maxCharsForCodeFileContent(runningJoinedLen, pathLine, budgetTokens) {
5538
+ const maxTotalChars = budgetTokens * 4;
5539
+ const overhead = runningJoinedLen + 1 + pathLine.length + 1;
5540
+ return Math.max(0, maxTotalChars - overhead);
5541
+ }
5399
5542
  function truncate(text, maxChars) {
5400
5543
  if (text.length <= maxChars) return text;
5401
5544
  return text.slice(0, maxChars) + `
@@ -5601,9 +5744,12 @@ User instructions: ${prompt}`);
5601
5744
  if (fingerprint.codeAnalysis) {
5602
5745
  const ca = fingerprint.codeAnalysis;
5603
5746
  const basePrompt = parts.join("\n");
5604
- const maxPromptTokens = getMaxPromptTokens();
5747
+ const effectiveMaxTokens = Math.min(getMaxPromptTokens(), BUILD_GENERATE_PROMPT_MAX_TOKENS);
5605
5748
  const baseTokens = estimateTokens(basePrompt);
5606
- const tokenBudgetForCode = Math.max(0, maxPromptTokens - baseTokens);
5749
+ const tokenBudgetForCode = Math.max(
5750
+ 0,
5751
+ effectiveMaxTokens - baseTokens - PROJECT_FILES_HEADER_RESERVE_TOKENS
5752
+ );
5607
5753
  const codeLines = [];
5608
5754
  let codeChars = 0;
5609
5755
  const introLine = "Study these files to extract patterns for skills. Use the exact code patterns you see here.\n";
@@ -5612,13 +5758,24 @@ User instructions: ${prompt}`);
5612
5758
  const sortedFiles = [...ca.files].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
5613
5759
  let includedFiles = 0;
5614
5760
  for (const f of sortedFiles) {
5615
- const entry = `[${f.path}]
5616
- ${f.content}
5761
+ const pathLine = `[${f.path}]
5762
+ `;
5763
+ const maxContent = maxCharsForCodeFileContent(runningCodeLen, pathLine, tokenBudgetForCode);
5764
+ if (maxContent < 1) {
5765
+ if (includedFiles > 0) break;
5766
+ continue;
5767
+ }
5768
+ const content = f.content.slice(0, Math.min(f.content.length, maxContent));
5769
+ const entry = `${pathLine}${content}
5617
5770
  `;
5618
5771
  const projectedLen = runningCodeLen + 1 + entry.length;
5619
- if (Math.ceil(projectedLen / 4) > tokenBudgetForCode && includedFiles > 0) break;
5772
+ const projectedTokens = Math.ceil(projectedLen / 4);
5773
+ if (projectedTokens > tokenBudgetForCode) {
5774
+ if (includedFiles > 0) break;
5775
+ continue;
5776
+ }
5620
5777
  codeLines.push(entry);
5621
- codeChars += f.content.length;
5778
+ codeChars += content.length;
5622
5779
  runningCodeLen = projectedLen;
5623
5780
  includedFiles++;
5624
5781
  }
@@ -5646,32 +5803,32 @@ ${f.content}
5646
5803
  }
5647
5804
 
5648
5805
  // src/writers/index.ts
5649
- import fs20 from "fs";
5806
+ import fs21 from "fs";
5650
5807
 
5651
5808
  // src/writers/claude/index.ts
5652
5809
  init_pre_commit_block();
5653
- import fs12 from "fs";
5810
+ import fs13 from "fs";
5654
5811
  import path12 from "path";
5655
5812
  function writeClaudeConfig(config) {
5656
5813
  const written = [];
5657
- fs12.writeFileSync(
5814
+ fs13.writeFileSync(
5658
5815
  "CLAUDE.md",
5659
- appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(config.claudeMd)))
5816
+ appendManagedBlocks(config.claudeMd)
5660
5817
  );
5661
5818
  written.push("CLAUDE.md");
5662
5819
  if (config.rules?.length) {
5663
5820
  const rulesDir = path12.join(".claude", "rules");
5664
- if (!fs12.existsSync(rulesDir)) fs12.mkdirSync(rulesDir, { recursive: true });
5821
+ if (!fs13.existsSync(rulesDir)) fs13.mkdirSync(rulesDir, { recursive: true });
5665
5822
  for (const rule of config.rules) {
5666
5823
  const rulePath = path12.join(rulesDir, rule.filename);
5667
- fs12.writeFileSync(rulePath, rule.content);
5824
+ fs13.writeFileSync(rulePath, rule.content);
5668
5825
  written.push(rulePath);
5669
5826
  }
5670
5827
  }
5671
5828
  if (config.skills?.length) {
5672
5829
  for (const skill of config.skills) {
5673
5830
  const skillDir = path12.join(".claude", "skills", skill.name);
5674
- if (!fs12.existsSync(skillDir)) fs12.mkdirSync(skillDir, { recursive: true });
5831
+ if (!fs13.existsSync(skillDir)) fs13.mkdirSync(skillDir, { recursive: true });
5675
5832
  const skillPath = path12.join(skillDir, "SKILL.md");
5676
5833
  const frontmatterLines = ["---", `name: ${skill.name}`, `description: ${skill.description}`];
5677
5834
  if (skill.paths?.length) {
@@ -5682,21 +5839,21 @@ function writeClaudeConfig(config) {
5682
5839
  }
5683
5840
  frontmatterLines.push("---", "");
5684
5841
  const frontmatter = frontmatterLines.join("\n");
5685
- fs12.writeFileSync(skillPath, frontmatter + skill.content);
5842
+ fs13.writeFileSync(skillPath, frontmatter + skill.content);
5686
5843
  written.push(skillPath);
5687
5844
  }
5688
5845
  }
5689
5846
  if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
5690
5847
  let existingServers = {};
5691
5848
  try {
5692
- if (fs12.existsSync(".mcp.json")) {
5693
- const existing = JSON.parse(fs12.readFileSync(".mcp.json", "utf-8"));
5849
+ if (fs13.existsSync(".mcp.json")) {
5850
+ const existing = JSON.parse(fs13.readFileSync(".mcp.json", "utf-8"));
5694
5851
  if (existing.mcpServers) existingServers = existing.mcpServers;
5695
5852
  }
5696
5853
  } catch {
5697
5854
  }
5698
5855
  const mergedServers = { ...existingServers, ...config.mcpServers };
5699
- fs12.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
5856
+ fs13.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
5700
5857
  written.push(".mcp.json");
5701
5858
  }
5702
5859
  return written;
@@ -5704,12 +5861,12 @@ function writeClaudeConfig(config) {
5704
5861
 
5705
5862
  // src/writers/cursor/index.ts
5706
5863
  init_pre_commit_block();
5707
- import fs13 from "fs";
5864
+ import fs14 from "fs";
5708
5865
  import path13 from "path";
5709
5866
  function writeCursorConfig(config) {
5710
5867
  const written = [];
5711
5868
  if (config.cursorrules) {
5712
- fs13.writeFileSync(".cursorrules", config.cursorrules);
5869
+ fs14.writeFileSync(".cursorrules", config.cursorrules);
5713
5870
  written.push(".cursorrules");
5714
5871
  }
5715
5872
  const preCommitRule = getCursorPreCommitRule();
@@ -5717,16 +5874,16 @@ function writeCursorConfig(config) {
5717
5874
  const syncRule = getCursorSyncRule();
5718
5875
  const allRules = [...config.rules || [], preCommitRule, learningsRule, syncRule];
5719
5876
  const rulesDir = path13.join(".cursor", "rules");
5720
- if (!fs13.existsSync(rulesDir)) fs13.mkdirSync(rulesDir, { recursive: true });
5877
+ if (!fs14.existsSync(rulesDir)) fs14.mkdirSync(rulesDir, { recursive: true });
5721
5878
  for (const rule of allRules) {
5722
5879
  const rulePath = path13.join(rulesDir, rule.filename);
5723
- fs13.writeFileSync(rulePath, rule.content);
5880
+ fs14.writeFileSync(rulePath, rule.content);
5724
5881
  written.push(rulePath);
5725
5882
  }
5726
5883
  if (config.skills?.length) {
5727
5884
  for (const skill of config.skills) {
5728
5885
  const skillDir = path13.join(".cursor", "skills", skill.name);
5729
- if (!fs13.existsSync(skillDir)) fs13.mkdirSync(skillDir, { recursive: true });
5886
+ if (!fs14.existsSync(skillDir)) fs14.mkdirSync(skillDir, { recursive: true });
5730
5887
  const skillPath = path13.join(skillDir, "SKILL.md");
5731
5888
  const frontmatter = [
5732
5889
  "---",
@@ -5735,24 +5892,24 @@ function writeCursorConfig(config) {
5735
5892
  "---",
5736
5893
  ""
5737
5894
  ].join("\n");
5738
- fs13.writeFileSync(skillPath, frontmatter + skill.content);
5895
+ fs14.writeFileSync(skillPath, frontmatter + skill.content);
5739
5896
  written.push(skillPath);
5740
5897
  }
5741
5898
  }
5742
5899
  if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
5743
5900
  const cursorDir = ".cursor";
5744
- if (!fs13.existsSync(cursorDir)) fs13.mkdirSync(cursorDir, { recursive: true });
5901
+ if (!fs14.existsSync(cursorDir)) fs14.mkdirSync(cursorDir, { recursive: true });
5745
5902
  const mcpPath = path13.join(cursorDir, "mcp.json");
5746
5903
  let existingServers = {};
5747
5904
  try {
5748
- if (fs13.existsSync(mcpPath)) {
5749
- const existing = JSON.parse(fs13.readFileSync(mcpPath, "utf-8"));
5905
+ if (fs14.existsSync(mcpPath)) {
5906
+ const existing = JSON.parse(fs14.readFileSync(mcpPath, "utf-8"));
5750
5907
  if (existing.mcpServers) existingServers = existing.mcpServers;
5751
5908
  }
5752
5909
  } catch {
5753
5910
  }
5754
5911
  const mergedServers = { ...existingServers, ...config.mcpServers };
5755
- fs13.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
5912
+ fs14.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
5756
5913
  written.push(mcpPath);
5757
5914
  }
5758
5915
  return written;
@@ -5760,19 +5917,19 @@ function writeCursorConfig(config) {
5760
5917
 
5761
5918
  // src/writers/codex/index.ts
5762
5919
  init_pre_commit_block();
5763
- import fs14 from "fs";
5920
+ import fs15 from "fs";
5764
5921
  import path14 from "path";
5765
5922
  function writeCodexConfig(config) {
5766
5923
  const written = [];
5767
- fs14.writeFileSync(
5924
+ fs15.writeFileSync(
5768
5925
  "AGENTS.md",
5769
- appendLearningsBlock(appendPreCommitBlock(config.agentsMd, "codex"))
5926
+ appendManagedBlocks(config.agentsMd, "codex")
5770
5927
  );
5771
5928
  written.push("AGENTS.md");
5772
5929
  if (config.skills?.length) {
5773
5930
  for (const skill of config.skills) {
5774
5931
  const skillDir = path14.join(".agents", "skills", skill.name);
5775
- if (!fs14.existsSync(skillDir)) fs14.mkdirSync(skillDir, { recursive: true });
5932
+ if (!fs15.existsSync(skillDir)) fs15.mkdirSync(skillDir, { recursive: true });
5776
5933
  const skillPath = path14.join(skillDir, "SKILL.md");
5777
5934
  const frontmatter = [
5778
5935
  "---",
@@ -5781,7 +5938,7 @@ function writeCodexConfig(config) {
5781
5938
  "---",
5782
5939
  ""
5783
5940
  ].join("\n");
5784
- fs14.writeFileSync(skillPath, frontmatter + skill.content);
5941
+ fs15.writeFileSync(skillPath, frontmatter + skill.content);
5785
5942
  written.push(skillPath);
5786
5943
  }
5787
5944
  }
@@ -5790,23 +5947,23 @@ function writeCodexConfig(config) {
5790
5947
 
5791
5948
  // src/writers/github-copilot/index.ts
5792
5949
  init_pre_commit_block();
5793
- import fs15 from "fs";
5950
+ import fs16 from "fs";
5794
5951
  import path15 from "path";
5795
5952
  function writeGithubCopilotConfig(config) {
5796
5953
  const written = [];
5797
5954
  if (config.instructions) {
5798
- fs15.mkdirSync(".github", { recursive: true });
5799
- fs15.writeFileSync(
5955
+ fs16.mkdirSync(".github", { recursive: true });
5956
+ fs16.writeFileSync(
5800
5957
  path15.join(".github", "copilot-instructions.md"),
5801
- appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(config.instructions, "copilot")))
5958
+ appendManagedBlocks(config.instructions, "copilot")
5802
5959
  );
5803
5960
  written.push(".github/copilot-instructions.md");
5804
5961
  }
5805
5962
  if (config.instructionFiles?.length) {
5806
5963
  const instructionsDir = path15.join(".github", "instructions");
5807
- fs15.mkdirSync(instructionsDir, { recursive: true });
5964
+ fs16.mkdirSync(instructionsDir, { recursive: true });
5808
5965
  for (const file of config.instructionFiles) {
5809
- fs15.writeFileSync(path15.join(instructionsDir, file.filename), file.content);
5966
+ fs16.writeFileSync(path15.join(instructionsDir, file.filename), file.content);
5810
5967
  written.push(`.github/instructions/${file.filename}`);
5811
5968
  }
5812
5969
  }
@@ -5816,12 +5973,12 @@ function writeGithubCopilotConfig(config) {
5816
5973
  // src/writers/opencode/index.ts
5817
5974
  init_pre_commit_block();
5818
5975
  init_builtin_skills();
5819
- import fs17 from "fs";
5976
+ import fs18 from "fs";
5820
5977
  import path17 from "path";
5821
5978
  function writeOpencodeConfig(config, agentsMdAlreadyWritten = false) {
5822
5979
  const written = [];
5823
5980
  if (!agentsMdAlreadyWritten) {
5824
- fs17.writeFileSync(
5981
+ fs18.writeFileSync(
5825
5982
  "AGENTS.md",
5826
5983
  appendLearningsBlock(appendPreCommitBlock(config.agentsMd, "codex"))
5827
5984
  );
@@ -5830,9 +5987,9 @@ function writeOpencodeConfig(config, agentsMdAlreadyWritten = false) {
5830
5987
  if (config.skills?.length) {
5831
5988
  for (const skill of config.skills) {
5832
5989
  const skillDir = path17.join(".opencode", "skills", skill.name);
5833
- if (!fs17.existsSync(skillDir)) fs17.mkdirSync(skillDir, { recursive: true });
5990
+ if (!fs18.existsSync(skillDir)) fs18.mkdirSync(skillDir, { recursive: true });
5834
5991
  const skillPath = path17.join(skillDir, "SKILL.md");
5835
- fs17.writeFileSync(skillPath, buildSkillContent(skill));
5992
+ fs18.writeFileSync(skillPath, buildSkillContent(skill));
5836
5993
  written.push(skillPath);
5837
5994
  }
5838
5995
  }
@@ -5840,30 +5997,30 @@ function writeOpencodeConfig(config, agentsMdAlreadyWritten = false) {
5840
5997
  }
5841
5998
 
5842
5999
  // src/writers/backup.ts
5843
- import fs18 from "fs";
6000
+ import fs19 from "fs";
5844
6001
  import path18 from "path";
5845
6002
  function createBackup(files) {
5846
6003
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
5847
6004
  const backupDir = path18.join(BACKUPS_DIR, timestamp);
5848
6005
  for (const file of files) {
5849
- if (!fs18.existsSync(file)) continue;
6006
+ if (!fs19.existsSync(file)) continue;
5850
6007
  const dest = path18.join(backupDir, file);
5851
6008
  const destDir = path18.dirname(dest);
5852
- if (!fs18.existsSync(destDir)) {
5853
- fs18.mkdirSync(destDir, { recursive: true });
6009
+ if (!fs19.existsSync(destDir)) {
6010
+ fs19.mkdirSync(destDir, { recursive: true });
5854
6011
  }
5855
- fs18.copyFileSync(file, dest);
6012
+ fs19.copyFileSync(file, dest);
5856
6013
  }
5857
6014
  return backupDir;
5858
6015
  }
5859
6016
  function restoreBackup(backupDir, file) {
5860
6017
  const backupFile = path18.join(backupDir, file);
5861
- if (!fs18.existsSync(backupFile)) return false;
6018
+ if (!fs19.existsSync(backupFile)) return false;
5862
6019
  const destDir = path18.dirname(file);
5863
- if (!fs18.existsSync(destDir)) {
5864
- fs18.mkdirSync(destDir, { recursive: true });
6020
+ if (!fs19.existsSync(destDir)) {
6021
+ fs19.mkdirSync(destDir, { recursive: true });
5865
6022
  }
5866
- fs18.copyFileSync(backupFile, file);
6023
+ fs19.copyFileSync(backupFile, file);
5867
6024
  return true;
5868
6025
  }
5869
6026
 
@@ -5871,32 +6028,32 @@ function restoreBackup(backupDir, file) {
5871
6028
  init_builtin_skills();
5872
6029
 
5873
6030
  // src/writers/manifest.ts
5874
- import fs19 from "fs";
6031
+ import fs20 from "fs";
5875
6032
  import crypto3 from "crypto";
5876
6033
  function readManifest() {
5877
6034
  try {
5878
- if (!fs19.existsSync(MANIFEST_FILE)) return null;
5879
- return JSON.parse(fs19.readFileSync(MANIFEST_FILE, "utf-8"));
6035
+ if (!fs20.existsSync(MANIFEST_FILE)) return null;
6036
+ return JSON.parse(fs20.readFileSync(MANIFEST_FILE, "utf-8"));
5880
6037
  } catch {
5881
6038
  return null;
5882
6039
  }
5883
6040
  }
5884
6041
  function writeManifest(manifest) {
5885
- if (!fs19.existsSync(CALIBER_DIR)) {
5886
- fs19.mkdirSync(CALIBER_DIR, { recursive: true });
6042
+ if (!fs20.existsSync(CALIBER_DIR)) {
6043
+ fs20.mkdirSync(CALIBER_DIR, { recursive: true });
5887
6044
  }
5888
- fs19.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
6045
+ fs20.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
5889
6046
  }
5890
6047
  function fileChecksum(filePath) {
5891
- const content = fs19.readFileSync(filePath);
6048
+ const content = fs20.readFileSync(filePath);
5892
6049
  return crypto3.createHash("sha256").update(content).digest("hex");
5893
6050
  }
5894
6051
 
5895
6052
  // src/writers/index.ts
5896
6053
  function writeSetup(setup) {
5897
6054
  const filesToWrite = getFilesToWrite(setup);
5898
- const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs20.existsSync(f));
5899
- const existingFiles = [...filesToWrite.filter((f) => fs20.existsSync(f)), ...filesToDelete];
6055
+ const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs21.existsSync(f));
6056
+ const existingFiles = [...filesToWrite.filter((f) => fs21.existsSync(f)), ...filesToDelete];
5900
6057
  const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
5901
6058
  const written = [];
5902
6059
  if (setup.targetAgent.includes("claude") && setup.claude) {
@@ -5917,7 +6074,7 @@ function writeSetup(setup) {
5917
6074
  }
5918
6075
  const deleted = [];
5919
6076
  for (const filePath of filesToDelete) {
5920
- fs20.unlinkSync(filePath);
6077
+ fs21.unlinkSync(filePath);
5921
6078
  deleted.push(filePath);
5922
6079
  }
5923
6080
  written.push(...ensureBuiltinSkills());
@@ -5948,8 +6105,8 @@ function undoSetup() {
5948
6105
  const removed = [];
5949
6106
  for (const entry of manifest.entries) {
5950
6107
  if (entry.action === "created") {
5951
- if (fs20.existsSync(entry.path)) {
5952
- fs20.unlinkSync(entry.path);
6108
+ if (fs21.existsSync(entry.path)) {
6109
+ fs21.unlinkSync(entry.path);
5953
6110
  removed.push(entry.path);
5954
6111
  }
5955
6112
  } else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
@@ -5958,8 +6115,8 @@ function undoSetup() {
5958
6115
  }
5959
6116
  }
5960
6117
  }
5961
- if (fs20.existsSync(MANIFEST_FILE)) {
5962
- fs20.unlinkSync(MANIFEST_FILE);
6118
+ if (fs21.existsSync(MANIFEST_FILE)) {
6119
+ fs21.unlinkSync(MANIFEST_FILE);
5963
6120
  }
5964
6121
  return { restored, removed };
5965
6122
  }
@@ -6012,18 +6169,18 @@ function getFilesToWrite(setup) {
6012
6169
  }
6013
6170
  function ensureGitignore() {
6014
6171
  const gitignorePath = ".gitignore";
6015
- if (fs20.existsSync(gitignorePath)) {
6016
- const content = fs20.readFileSync(gitignorePath, "utf-8");
6172
+ if (fs21.existsSync(gitignorePath)) {
6173
+ const content = fs21.readFileSync(gitignorePath, "utf-8");
6017
6174
  if (!content.includes(".caliber/")) {
6018
- fs20.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
6175
+ fs21.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
6019
6176
  }
6020
6177
  } else {
6021
- fs20.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
6178
+ fs21.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
6022
6179
  }
6023
6180
  }
6024
6181
 
6025
6182
  // src/writers/staging.ts
6026
- import fs21 from "fs";
6183
+ import fs22 from "fs";
6027
6184
  import path19 from "path";
6028
6185
  var STAGED_DIR = path19.join(CALIBER_DIR, "staged");
6029
6186
  var PROPOSED_DIR = path19.join(STAGED_DIR, "proposed");
@@ -6039,19 +6196,19 @@ function stageFiles(files, projectDir) {
6039
6196
  for (const file of files) {
6040
6197
  assertPathWithinDir(file.path, projectDir);
6041
6198
  const originalPath = path19.join(projectDir, file.path);
6042
- if (fs21.existsSync(originalPath)) {
6043
- const existing = fs21.readFileSync(originalPath, "utf-8");
6199
+ if (fs22.existsSync(originalPath)) {
6200
+ const existing = fs22.readFileSync(originalPath, "utf-8");
6044
6201
  if (normalizeContent(existing) === normalizeContent(file.content)) {
6045
6202
  continue;
6046
6203
  }
6047
6204
  }
6048
6205
  const proposedPath = path19.join(PROPOSED_DIR, file.path);
6049
- fs21.mkdirSync(path19.dirname(proposedPath), { recursive: true });
6050
- fs21.writeFileSync(proposedPath, file.content);
6051
- if (fs21.existsSync(originalPath)) {
6206
+ fs22.mkdirSync(path19.dirname(proposedPath), { recursive: true });
6207
+ fs22.writeFileSync(proposedPath, file.content);
6208
+ if (fs22.existsSync(originalPath)) {
6052
6209
  const currentPath = path19.join(CURRENT_DIR, file.path);
6053
- fs21.mkdirSync(path19.dirname(currentPath), { recursive: true });
6054
- fs21.copyFileSync(originalPath, currentPath);
6210
+ fs22.mkdirSync(path19.dirname(currentPath), { recursive: true });
6211
+ fs22.copyFileSync(originalPath, currentPath);
6055
6212
  modifiedFiles++;
6056
6213
  stagedFiles.push({
6057
6214
  relativePath: file.path,
@@ -6068,14 +6225,14 @@ function stageFiles(files, projectDir) {
6068
6225
  return { newFiles, modifiedFiles, stagedFiles };
6069
6226
  }
6070
6227
  function cleanupStaging() {
6071
- if (fs21.existsSync(STAGED_DIR)) {
6072
- fs21.rmSync(STAGED_DIR, { recursive: true, force: true });
6228
+ if (fs22.existsSync(STAGED_DIR)) {
6229
+ fs22.rmSync(STAGED_DIR, { recursive: true, force: true });
6073
6230
  }
6074
6231
  }
6075
6232
 
6076
6233
  // src/commands/setup-files.ts
6077
6234
  init_builtin_skills();
6078
- import fs22 from "fs";
6235
+ import fs23 from "fs";
6079
6236
  function collectSetupFiles(setup, targetAgent) {
6080
6237
  const files = [];
6081
6238
  const claude = setup.claude;
@@ -6182,7 +6339,7 @@ function collectSetupFiles(setup, targetAgent) {
6182
6339
  }
6183
6340
  const codexTargeted = targetAgent ? targetAgent.includes("codex") : false;
6184
6341
  const opencodeTargeted = targetAgent ? targetAgent.includes("opencode") : false;
6185
- if ((codexTargeted || opencodeTargeted) && !fs22.existsSync("AGENTS.md") && !(codex && codex.agentsMd) && !(opencode && opencode.agentsMd)) {
6342
+ if ((codexTargeted || opencodeTargeted) && !fs23.existsSync("AGENTS.md") && !(codex && codex.agentsMd) && !(opencode && opencode.agentsMd)) {
6186
6343
  const agentRefs = [];
6187
6344
  if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
6188
6345
  if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
@@ -6201,7 +6358,7 @@ ${agentRefs.join(" ")}
6201
6358
 
6202
6359
  // src/lib/learning-hooks.ts
6203
6360
  init_resolve_caliber();
6204
- import fs23 from "fs";
6361
+ import fs24 from "fs";
6205
6362
  import path20 from "path";
6206
6363
  var SETTINGS_PATH2 = path20.join(".claude", "settings.json");
6207
6364
  var HOOK_TAILS = [
@@ -6220,17 +6377,17 @@ function getHookConfigs() {
6220
6377
  }));
6221
6378
  }
6222
6379
  function readSettings2() {
6223
- if (!fs23.existsSync(SETTINGS_PATH2)) return {};
6380
+ if (!fs24.existsSync(SETTINGS_PATH2)) return {};
6224
6381
  try {
6225
- return JSON.parse(fs23.readFileSync(SETTINGS_PATH2, "utf-8"));
6382
+ return JSON.parse(fs24.readFileSync(SETTINGS_PATH2, "utf-8"));
6226
6383
  } catch {
6227
6384
  return {};
6228
6385
  }
6229
6386
  }
6230
6387
  function writeSettings2(settings) {
6231
6388
  const dir = path20.dirname(SETTINGS_PATH2);
6232
- if (!fs23.existsSync(dir)) fs23.mkdirSync(dir, { recursive: true });
6233
- fs23.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
6389
+ if (!fs24.existsSync(dir)) fs24.mkdirSync(dir, { recursive: true });
6390
+ fs24.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
6234
6391
  }
6235
6392
  function hasLearningHook(matchers, tail) {
6236
6393
  return matchers.some((entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, tail)));
@@ -6272,17 +6429,17 @@ var CURSOR_HOOK_EVENTS = [
6272
6429
  { event: "sessionEnd", tail: "learn finalize --auto" }
6273
6430
  ];
6274
6431
  function readCursorHooks() {
6275
- if (!fs23.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
6432
+ if (!fs24.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
6276
6433
  try {
6277
- return JSON.parse(fs23.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
6434
+ return JSON.parse(fs24.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
6278
6435
  } catch {
6279
6436
  return { version: 1, hooks: {} };
6280
6437
  }
6281
6438
  }
6282
6439
  function writeCursorHooks(config) {
6283
6440
  const dir = path20.dirname(CURSOR_HOOKS_PATH);
6284
- if (!fs23.existsSync(dir)) fs23.mkdirSync(dir, { recursive: true });
6285
- fs23.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
6441
+ if (!fs24.existsSync(dir)) fs24.mkdirSync(dir, { recursive: true });
6442
+ fs24.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
6286
6443
  }
6287
6444
  function hasCursorHook(entries, tail) {
6288
6445
  return entries.some((e) => isCaliberCommand(e.command, tail));
@@ -6354,7 +6511,7 @@ function removeLearningHooks() {
6354
6511
  init_resolve_caliber();
6355
6512
 
6356
6513
  // src/lib/state.ts
6357
- import fs24 from "fs";
6514
+ import fs25 from "fs";
6358
6515
  import path21 from "path";
6359
6516
  import { execSync as execSync9 } from "child_process";
6360
6517
  var STATE_FILE = path21.join(CALIBER_DIR, ".caliber-state.json");
@@ -6368,8 +6525,8 @@ function normalizeTargetAgent(value) {
6368
6525
  }
6369
6526
  function readState() {
6370
6527
  try {
6371
- if (!fs24.existsSync(STATE_FILE)) return null;
6372
- const raw = JSON.parse(fs24.readFileSync(STATE_FILE, "utf-8"));
6528
+ if (!fs25.existsSync(STATE_FILE)) return null;
6529
+ const raw = JSON.parse(fs25.readFileSync(STATE_FILE, "utf-8"));
6373
6530
  if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
6374
6531
  return raw;
6375
6532
  } catch {
@@ -6377,10 +6534,10 @@ function readState() {
6377
6534
  }
6378
6535
  }
6379
6536
  function writeState(state) {
6380
- if (!fs24.existsSync(CALIBER_DIR)) {
6381
- fs24.mkdirSync(CALIBER_DIR, { recursive: true });
6537
+ if (!fs25.existsSync(CALIBER_DIR)) {
6538
+ fs25.mkdirSync(CALIBER_DIR, { recursive: true });
6382
6539
  }
6383
- fs24.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
6540
+ fs25.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
6384
6541
  }
6385
6542
  function getCurrentHeadSha() {
6386
6543
  try {
@@ -6397,6 +6554,7 @@ function getCurrentHeadSha() {
6397
6554
  import chalk2 from "chalk";
6398
6555
  import readline from "readline";
6399
6556
  function promptInput(question) {
6557
+ if (!process.stdin.isTTY) return Promise.resolve("");
6400
6558
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
6401
6559
  return new Promise((resolve3) => {
6402
6560
  rl.question(chalk2.cyan(`${question} `), (answer) => {
@@ -6540,6 +6698,7 @@ var POINTS_FRESHNESS = 4;
6540
6698
  var POINTS_NO_SECRETS = 4;
6541
6699
  var POINTS_PERMISSIONS = 2;
6542
6700
  var POINTS_HOOKS = 2;
6701
+ var POINTS_MODEL_PINNED = 2;
6543
6702
  var POINTS_AGENTS_MD = 1;
6544
6703
  var POINTS_OPEN_SKILLS_FORMAT = 2;
6545
6704
  var POINTS_LEARNED_CONTENT = 2;
@@ -7324,6 +7483,19 @@ import { execSync as execSync12 } from "child_process";
7324
7483
  import { join as join7 } from "path";
7325
7484
  init_resolve_caliber();
7326
7485
  init_pre_commit_block();
7486
+
7487
+ // src/scoring/model-pinning.ts
7488
+ function configContentSuggestsPinnedModel(lower) {
7489
+ if (/\bcaliber_model\b/.test(lower) || /\bcaliber_fast_model\b/.test(lower)) return true;
7490
+ if (/(?:^|[\s`'"\n])\/model(?:[\s`'"\n]|$)/.test(lower)) return true;
7491
+ if (/claude-(sonnet|opus|haiku)([-.@\d]|\b)/.test(lower)) return true;
7492
+ if (/\bgpt-[45]([-._\d]|\b)/.test(lower)) return true;
7493
+ if (/\bsonnet-4\.[\d.]+\b/.test(lower)) return true;
7494
+ if (/\b(high|medium|low)\s+effort\b/.test(lower)) return true;
7495
+ return false;
7496
+ }
7497
+
7498
+ // src/scoring/checks/bonus.ts
7327
7499
  function hasPreCommitHook(dir) {
7328
7500
  try {
7329
7501
  const gitDir = execSync12("git rev-parse --git-dir", {
@@ -7444,6 +7616,33 @@ function checkBonus(dir) {
7444
7616
  detail: hasLearned ? "Session learnings found in CALIBER_LEARNINGS.md" : "No learned content",
7445
7617
  suggestion: hasLearned ? void 0 : `Session learnings capture patterns from your coding sessions so the agent improves over time. Run \`${resolveCaliber()} learn install\``
7446
7618
  });
7619
+ const configContent = (() => {
7620
+ const parts = [];
7621
+ for (const rel of ["CLAUDE.md", "AGENTS.md"]) {
7622
+ const c = readFileOrNull(join7(dir, rel));
7623
+ if (c) parts.push(c);
7624
+ }
7625
+ try {
7626
+ const rulesDir = join7(dir, ".cursor", "rules");
7627
+ for (const f of readdirSync3(rulesDir).filter((x) => x.endsWith(".mdc"))) {
7628
+ const content = readFileOrNull(join7(rulesDir, f));
7629
+ if (content) parts.push(content);
7630
+ }
7631
+ } catch {
7632
+ }
7633
+ return parts.join("\n").toLowerCase();
7634
+ })();
7635
+ const hasModelRef = configContentSuggestsPinnedModel(configContent);
7636
+ checks.push({
7637
+ id: "model_pinned",
7638
+ name: "Model & effort pinned",
7639
+ category: "bonus",
7640
+ maxPoints: POINTS_MODEL_PINNED,
7641
+ earnedPoints: hasModelRef ? POINTS_MODEL_PINNED : 0,
7642
+ passed: hasModelRef,
7643
+ detail: hasModelRef ? "Model or effort level explicitly set in config" : "Config doesn't pin model or effort level \u2014 behavior may change when defaults are updated",
7644
+ suggestion: hasModelRef ? void 0 : "Add model/effort to config: CALIBER_MODEL env var, or /model in Claude Code, or a Model Configuration section in CLAUDE.md"
7645
+ });
7447
7646
  return checks;
7448
7647
  }
7449
7648
 
@@ -7487,7 +7686,7 @@ function checkSources(dir) {
7487
7686
  }
7488
7687
 
7489
7688
  // src/scoring/dismissed.ts
7490
- import fs25 from "fs";
7689
+ import fs26 from "fs";
7491
7690
  import path22 from "path";
7492
7691
  var DISMISSED_FILE = path22.join(CALIBER_DIR, "dismissed-checks.json");
7493
7692
  function dismissedFilePath(dir) {
@@ -7496,17 +7695,17 @@ function dismissedFilePath(dir) {
7496
7695
  function readDismissedChecks(dir) {
7497
7696
  try {
7498
7697
  const filePath = dismissedFilePath(dir);
7499
- if (!fs25.existsSync(filePath)) return [];
7500
- return JSON.parse(fs25.readFileSync(filePath, "utf-8"));
7698
+ if (!fs26.existsSync(filePath)) return [];
7699
+ return JSON.parse(fs26.readFileSync(filePath, "utf-8"));
7501
7700
  } catch {
7502
7701
  return [];
7503
7702
  }
7504
7703
  }
7505
7704
  function writeDismissedChecks(checks) {
7506
- if (!fs25.existsSync(CALIBER_DIR)) {
7507
- fs25.mkdirSync(CALIBER_DIR, { recursive: true });
7705
+ if (!fs26.existsSync(CALIBER_DIR)) {
7706
+ fs26.mkdirSync(CALIBER_DIR, { recursive: true });
7508
7707
  }
7509
- fs25.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
7708
+ fs26.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
7510
7709
  }
7511
7710
  function getDismissedIds(dir) {
7512
7711
  return new Set(readDismissedChecks(dir).map((c) => c.id));
@@ -7765,7 +7964,7 @@ import { PostHog } from "posthog-node";
7765
7964
  import chalk5 from "chalk";
7766
7965
 
7767
7966
  // src/telemetry/config.ts
7768
- import fs26 from "fs";
7967
+ import fs27 from "fs";
7769
7968
  import path23 from "path";
7770
7969
  import os5 from "os";
7771
7970
  import crypto4 from "crypto";
@@ -7775,17 +7974,17 @@ var CONFIG_FILE2 = path23.join(CONFIG_DIR2, "config.json");
7775
7974
  var runtimeDisabled = false;
7776
7975
  function readConfig() {
7777
7976
  try {
7778
- if (!fs26.existsSync(CONFIG_FILE2)) return {};
7779
- return JSON.parse(fs26.readFileSync(CONFIG_FILE2, "utf-8"));
7977
+ if (!fs27.existsSync(CONFIG_FILE2)) return {};
7978
+ return JSON.parse(fs27.readFileSync(CONFIG_FILE2, "utf-8"));
7780
7979
  } catch {
7781
7980
  return {};
7782
7981
  }
7783
7982
  }
7784
7983
  function writeConfig(config) {
7785
- if (!fs26.existsSync(CONFIG_DIR2)) {
7786
- fs26.mkdirSync(CONFIG_DIR2, { recursive: true });
7984
+ if (!fs27.existsSync(CONFIG_DIR2)) {
7985
+ fs27.mkdirSync(CONFIG_DIR2, { recursive: true });
7787
7986
  }
7788
- fs26.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
7987
+ fs27.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
7789
7988
  }
7790
7989
  function getMachineId() {
7791
7990
  const config = readConfig();
@@ -8458,6 +8657,10 @@ async function recommendCommand(options) {
8458
8657
  await querySkills(options.query);
8459
8658
  return;
8460
8659
  }
8660
+ if (!process.stdin.isTTY) {
8661
+ console.log(chalk6.dim(" Skills search requires an interactive terminal."));
8662
+ return;
8663
+ }
8461
8664
  const proceed = await select3({
8462
8665
  message: "Search public repos for relevant skills to add to this project?",
8463
8666
  choices: [
@@ -9125,7 +9328,7 @@ async function runScoreRefineWithSpinner(setup, dir, sessionHistory, options) {
9125
9328
  }
9126
9329
 
9127
9330
  // src/lib/debug-report.ts
9128
- import fs27 from "fs";
9331
+ import fs28 from "fs";
9129
9332
  import path24 from "path";
9130
9333
  var DebugReport = class {
9131
9334
  sections = [];
@@ -9196,10 +9399,10 @@ var DebugReport = class {
9196
9399
  lines.push("");
9197
9400
  }
9198
9401
  const dir = path24.dirname(outputPath);
9199
- if (!fs27.existsSync(dir)) {
9200
- fs27.mkdirSync(dir, { recursive: true });
9402
+ if (!fs28.existsSync(dir)) {
9403
+ fs28.mkdirSync(dir, { recursive: true });
9201
9404
  }
9202
- fs27.writeFileSync(outputPath, lines.join("\n"));
9405
+ fs28.writeFileSync(outputPath, lines.join("\n"));
9203
9406
  }
9204
9407
  };
9205
9408
  function formatMs(ms) {
@@ -9600,7 +9803,7 @@ import chalk11 from "chalk";
9600
9803
  import ora3 from "ora";
9601
9804
  import select5 from "@inquirer/select";
9602
9805
  import checkbox from "@inquirer/checkbox";
9603
- import fs30 from "fs";
9806
+ import fs31 from "fs";
9604
9807
 
9605
9808
  // src/ai/refine.ts
9606
9809
  async function refineSetup(currentSetup, message, conversationHistory, callbacks) {
@@ -9776,11 +9979,11 @@ init_config();
9776
9979
  init_review();
9777
9980
  function detectAgents(dir) {
9778
9981
  const agents = [];
9779
- if (fs30.existsSync(`${dir}/.claude`)) agents.push("claude");
9780
- if (fs30.existsSync(`${dir}/.cursor`)) agents.push("cursor");
9781
- if (fs30.existsSync(`${dir}/.agents`) || fs30.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
9782
- if (fs30.existsSync(`${dir}/.opencode`)) agents.push("opencode");
9783
- if (fs30.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
9982
+ if (fs31.existsSync(`${dir}/.claude`)) agents.push("claude");
9983
+ if (fs31.existsSync(`${dir}/.cursor`)) agents.push("cursor");
9984
+ if (fs31.existsSync(`${dir}/.agents`) || fs31.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
9985
+ if (fs31.existsSync(`${dir}/.opencode`)) agents.push("opencode");
9986
+ if (fs31.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
9784
9987
  return agents;
9785
9988
  }
9786
9989
  async function promptAgent(detected) {
@@ -9884,7 +10087,7 @@ async function refineLoop(currentSetup, sessionHistory, summarizeSetup2, printSu
9884
10087
 
9885
10088
  // src/commands/init-display.ts
9886
10089
  import chalk12 from "chalk";
9887
- import fs31 from "fs";
10090
+ import fs32 from "fs";
9888
10091
  init_types();
9889
10092
  function formatWhatChanged(setup) {
9890
10093
  const lines = [];
@@ -9892,12 +10095,12 @@ function formatWhatChanged(setup) {
9892
10095
  const codex = setup.codex;
9893
10096
  const cursor = setup.cursor;
9894
10097
  if (claude?.claudeMd) {
9895
- const action = fs31.existsSync("CLAUDE.md") ? "Updated" : "Created";
10098
+ const action = fs32.existsSync("CLAUDE.md") ? "Updated" : "Created";
9896
10099
  lines.push(`${action} CLAUDE.md`);
9897
10100
  }
9898
10101
  const opencode = setup.opencode;
9899
10102
  if (codex?.agentsMd || opencode?.agentsMd) {
9900
- const action = fs31.existsSync("AGENTS.md") ? "Updated" : "Created";
10103
+ const action = fs32.existsSync("AGENTS.md") ? "Updated" : "Created";
9901
10104
  lines.push(`${action} AGENTS.md`);
9902
10105
  }
9903
10106
  const allSkills = [];
@@ -9941,7 +10144,7 @@ function printSetupSummary(setup) {
9941
10144
  };
9942
10145
  if (claude) {
9943
10146
  if (claude.claudeMd) {
9944
- const icon = fs31.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
10147
+ const icon = fs32.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
9945
10148
  const desc = getDescription("CLAUDE.md");
9946
10149
  console.log(` ${icon} ${chalk12.bold("CLAUDE.md")}`);
9947
10150
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -9951,7 +10154,7 @@ function printSetupSummary(setup) {
9951
10154
  if (Array.isArray(skills) && skills.length > 0) {
9952
10155
  for (const skill of skills) {
9953
10156
  const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
9954
- const icon = fs31.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
10157
+ const icon = fs32.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
9955
10158
  const desc = getDescription(skillPath);
9956
10159
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
9957
10160
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -9962,7 +10165,7 @@ function printSetupSummary(setup) {
9962
10165
  const codex = setup.codex;
9963
10166
  if (codex) {
9964
10167
  if (codex.agentsMd) {
9965
- const icon = fs31.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
10168
+ const icon = fs32.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
9966
10169
  const desc = getDescription("AGENTS.md");
9967
10170
  console.log(` ${icon} ${chalk12.bold("AGENTS.md")}`);
9968
10171
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -9972,7 +10175,7 @@ function printSetupSummary(setup) {
9972
10175
  if (Array.isArray(codexSkills) && codexSkills.length > 0) {
9973
10176
  for (const skill of codexSkills) {
9974
10177
  const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
9975
- const icon = fs31.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
10178
+ const icon = fs32.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
9976
10179
  const desc = getDescription(skillPath);
9977
10180
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
9978
10181
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -9983,7 +10186,7 @@ function printSetupSummary(setup) {
9983
10186
  const opencode = setup.opencode;
9984
10187
  if (opencode) {
9985
10188
  if (opencode.agentsMd && !codex?.agentsMd) {
9986
- const icon = fs31.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
10189
+ const icon = fs32.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
9987
10190
  const desc = getDescription("AGENTS.md");
9988
10191
  console.log(` ${icon} ${chalk12.bold("AGENTS.md")} ${chalk12.dim("(OpenCode)")}`);
9989
10192
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -9993,7 +10196,7 @@ function printSetupSummary(setup) {
9993
10196
  if (Array.isArray(opencodeSkills) && opencodeSkills.length > 0) {
9994
10197
  for (const skill of opencodeSkills) {
9995
10198
  const skillPath = `.opencode/skills/${skill.name}/SKILL.md`;
9996
- const icon = fs31.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
10199
+ const icon = fs32.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
9997
10200
  const desc = getDescription(skillPath);
9998
10201
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
9999
10202
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -10003,7 +10206,7 @@ function printSetupSummary(setup) {
10003
10206
  }
10004
10207
  if (cursor) {
10005
10208
  if (cursor.cursorrules) {
10006
- const icon = fs31.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
10209
+ const icon = fs32.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
10007
10210
  const desc = getDescription(".cursorrules");
10008
10211
  console.log(` ${icon} ${chalk12.bold(".cursorrules")}`);
10009
10212
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -10013,7 +10216,7 @@ function printSetupSummary(setup) {
10013
10216
  if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
10014
10217
  for (const skill of cursorSkills) {
10015
10218
  const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
10016
- const icon = fs31.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
10219
+ const icon = fs32.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
10017
10220
  const desc = getDescription(skillPath);
10018
10221
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
10019
10222
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -10024,7 +10227,7 @@ function printSetupSummary(setup) {
10024
10227
  if (Array.isArray(rulesArr) && rulesArr.length > 0) {
10025
10228
  for (const rule of rulesArr) {
10026
10229
  const rulePath = `.cursor/rules/${rule.filename}`;
10027
- const icon = fs31.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
10230
+ const icon = fs32.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
10028
10231
  const desc = getDescription(rulePath);
10029
10232
  console.log(` ${icon} ${chalk12.bold(rulePath)}`);
10030
10233
  if (desc) {
@@ -10086,12 +10289,12 @@ function displayTokenUsage() {
10086
10289
  // src/commands/init-helpers.ts
10087
10290
  init_config();
10088
10291
  import chalk13 from "chalk";
10089
- import fs32 from "fs";
10292
+ import fs33 from "fs";
10090
10293
  import path26 from "path";
10091
10294
  function isFirstRun(dir) {
10092
10295
  const caliberDir = path26.join(dir, ".caliber");
10093
10296
  try {
10094
- const stat = fs32.statSync(caliberDir);
10297
+ const stat = fs33.statSync(caliberDir);
10095
10298
  return !stat.isDirectory();
10096
10299
  } catch {
10097
10300
  return true;
@@ -10144,8 +10347,8 @@ function ensurePermissions(fingerprint) {
10144
10347
  const settingsPath = ".claude/settings.json";
10145
10348
  let settings = {};
10146
10349
  try {
10147
- if (fs32.existsSync(settingsPath)) {
10148
- settings = JSON.parse(fs32.readFileSync(settingsPath, "utf-8"));
10350
+ if (fs33.existsSync(settingsPath)) {
10351
+ settings = JSON.parse(fs33.readFileSync(settingsPath, "utf-8"));
10149
10352
  }
10150
10353
  } catch {
10151
10354
  }
@@ -10154,8 +10357,8 @@ function ensurePermissions(fingerprint) {
10154
10357
  if (Array.isArray(allow) && allow.length > 0) return;
10155
10358
  permissions.allow = derivePermissions(fingerprint);
10156
10359
  settings.permissions = permissions;
10157
- if (!fs32.existsSync(".claude")) fs32.mkdirSync(".claude", { recursive: true });
10158
- fs32.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
10360
+ if (!fs33.existsSync(".claude")) fs33.mkdirSync(".claude", { recursive: true });
10361
+ fs33.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
10159
10362
  }
10160
10363
  function writeErrorLog(config, rawOutput, error, stopReason) {
10161
10364
  try {
@@ -10184,8 +10387,8 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
10184
10387
  lines.push("```");
10185
10388
  lines.push("", "If timeouts persist, try a different model.");
10186
10389
  }
10187
- fs32.mkdirSync(path26.join(process.cwd(), ".caliber"), { recursive: true });
10188
- fs32.writeFileSync(logPath, lines.join("\n"));
10390
+ fs33.mkdirSync(path26.join(process.cwd(), ".caliber"), { recursive: true });
10391
+ fs33.writeFileSync(logPath, lines.join("\n"));
10189
10392
  console.log(chalk13.dim(`
10190
10393
  Error log written to .caliber/error-log.md`));
10191
10394
  } catch {
@@ -10238,7 +10441,7 @@ ${JSON.stringify(checkList, null, 2)}`,
10238
10441
  }
10239
10442
 
10240
10443
  // src/scoring/history.ts
10241
- import fs33 from "fs";
10444
+ import fs34 from "fs";
10242
10445
  import path27 from "path";
10243
10446
  var HISTORY_FILE = "score-history.jsonl";
10244
10447
  var MAX_ENTRIES = 500;
@@ -10255,14 +10458,14 @@ function recordScore(result, trigger) {
10255
10458
  trigger
10256
10459
  };
10257
10460
  try {
10258
- fs33.mkdirSync(CALIBER_DIR, { recursive: true });
10461
+ fs34.mkdirSync(CALIBER_DIR, { recursive: true });
10259
10462
  const filePath = historyFilePath();
10260
- fs33.appendFileSync(filePath, JSON.stringify(entry) + "\n");
10261
- const stat = fs33.statSync(filePath);
10463
+ fs34.appendFileSync(filePath, JSON.stringify(entry) + "\n");
10464
+ const stat = fs34.statSync(filePath);
10262
10465
  if (stat.size > TRIM_THRESHOLD * 120) {
10263
- const lines = fs33.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
10466
+ const lines = fs34.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
10264
10467
  if (lines.length > MAX_ENTRIES) {
10265
- fs33.writeFileSync(filePath, lines.slice(-MAX_ENTRIES).join("\n") + "\n");
10468
+ fs34.writeFileSync(filePath, lines.slice(-MAX_ENTRIES).join("\n") + "\n");
10266
10469
  }
10267
10470
  }
10268
10471
  } catch {
@@ -10271,7 +10474,7 @@ function recordScore(result, trigger) {
10271
10474
  function readScoreHistory() {
10272
10475
  const filePath = historyFilePath();
10273
10476
  try {
10274
- const content = fs33.readFileSync(filePath, "utf-8");
10477
+ const content = fs34.readFileSync(filePath, "utf-8");
10275
10478
  const entries = [];
10276
10479
  for (const line of content.split("\n")) {
10277
10480
  if (!line) continue;
@@ -10450,12 +10653,12 @@ async function initCommand(options) {
10450
10653
  }
10451
10654
  const { ensureBuiltinSkills: ensureBuiltinSkills2 } = await Promise.resolve().then(() => (init_builtin_skills(), builtin_skills_exports));
10452
10655
  for (const agent of targetAgent) {
10453
- if (agent === "claude" && !fs34.existsSync(".claude"))
10454
- fs34.mkdirSync(".claude", { recursive: true });
10455
- if (agent === "cursor" && !fs34.existsSync(".cursor"))
10456
- fs34.mkdirSync(".cursor", { recursive: true });
10457
- if (agent === "codex" && !fs34.existsSync(".agents"))
10458
- fs34.mkdirSync(".agents", { recursive: true });
10656
+ if (agent === "claude" && !fs35.existsSync(".claude"))
10657
+ fs35.mkdirSync(".claude", { recursive: true });
10658
+ if (agent === "cursor" && !fs35.existsSync(".cursor"))
10659
+ fs35.mkdirSync(".cursor", { recursive: true });
10660
+ if (agent === "codex" && !fs35.existsSync(".agents"))
10661
+ fs35.mkdirSync(".agents", { recursive: true });
10459
10662
  }
10460
10663
  const skillsWritten = ensureBuiltinSkills2();
10461
10664
  if (skillsWritten.length > 0) {
@@ -10544,7 +10747,7 @@ async function initCommand(options) {
10544
10747
  const claudeMdPath = "CLAUDE.md";
10545
10748
  let claudeContent = "";
10546
10749
  try {
10547
- claudeContent = fs34.readFileSync(claudeMdPath, "utf-8");
10750
+ claudeContent = fs35.readFileSync(claudeMdPath, "utf-8");
10548
10751
  } catch {
10549
10752
  }
10550
10753
  if (!claudeContent) {
@@ -10552,20 +10755,20 @@ async function initCommand(options) {
10552
10755
  `;
10553
10756
  }
10554
10757
  const updatedClaude = appendManagedBlocks2(claudeContent, "claude");
10555
- if (updatedClaude !== claudeContent || !fs34.existsSync(claudeMdPath)) {
10556
- fs34.writeFileSync(claudeMdPath, updatedClaude);
10758
+ if (updatedClaude !== claudeContent || !fs35.existsSync(claudeMdPath)) {
10759
+ fs35.writeFileSync(claudeMdPath, updatedClaude);
10557
10760
  console.log(` ${chalk14.green("\u2713")} CLAUDE.md \u2014 added Caliber sync instructions`);
10558
10761
  }
10559
10762
  if (targetAgent.includes("cursor")) {
10560
10763
  const rulesDir = path28.join(".cursor", "rules");
10561
- if (!fs34.existsSync(rulesDir)) fs34.mkdirSync(rulesDir, { recursive: true });
10764
+ if (!fs35.existsSync(rulesDir)) fs35.mkdirSync(rulesDir, { recursive: true });
10562
10765
  for (const rule of [
10563
10766
  getCursorPreCommitRule2(),
10564
10767
  getCursorLearningsRule2(),
10565
10768
  getCursorSyncRule2(),
10566
10769
  getCursorSetupRule2()
10567
10770
  ]) {
10568
- fs34.writeFileSync(path28.join(rulesDir, rule.filename), rule.content);
10771
+ fs35.writeFileSync(path28.join(rulesDir, rule.filename), rule.content);
10569
10772
  }
10570
10773
  console.log(` ${chalk14.green("\u2713")} Cursor rules \u2014 added Caliber sync rules`);
10571
10774
  }
@@ -10573,17 +10776,17 @@ async function initCommand(options) {
10573
10776
  const copilotPath = path28.join(".github", "copilot-instructions.md");
10574
10777
  let copilotContent = "";
10575
10778
  try {
10576
- copilotContent = fs34.readFileSync(copilotPath, "utf-8");
10779
+ copilotContent = fs35.readFileSync(copilotPath, "utf-8");
10577
10780
  } catch {
10578
10781
  }
10579
10782
  if (!copilotContent) {
10580
- fs34.mkdirSync(".github", { recursive: true });
10783
+ fs35.mkdirSync(".github", { recursive: true });
10581
10784
  copilotContent = `# ${path28.basename(process.cwd())}
10582
10785
  `;
10583
10786
  }
10584
10787
  const updatedCopilot = appendManagedBlocks2(copilotContent, "copilot");
10585
10788
  if (updatedCopilot !== copilotContent) {
10586
- fs34.writeFileSync(copilotPath, updatedCopilot);
10789
+ fs35.writeFileSync(copilotPath, updatedCopilot);
10587
10790
  console.log(` ${chalk14.green("\u2713")} Copilot instructions \u2014 added Caliber sync instructions`);
10588
10791
  }
10589
10792
  }
@@ -10930,7 +11133,7 @@ async function initCommand(options) {
10930
11133
  const { default: ora9 } = await import("ora");
10931
11134
  const writeSpinner = ora9("Writing config files...").start();
10932
11135
  try {
10933
- if (targetAgent.includes("codex") && !fs34.existsSync("AGENTS.md") && !generatedSetup.codex) {
11136
+ if (targetAgent.includes("codex") && !fs35.existsSync("AGENTS.md") && !generatedSetup.codex) {
10934
11137
  const claude = generatedSetup.claude;
10935
11138
  const cursor = generatedSetup.cursor;
10936
11139
  const agentRefs = [];
@@ -11120,7 +11323,7 @@ function undoCommand() {
11120
11323
 
11121
11324
  // src/commands/status.ts
11122
11325
  import chalk16 from "chalk";
11123
- import fs35 from "fs";
11326
+ import fs36 from "fs";
11124
11327
  init_config();
11125
11328
  init_resolve_caliber();
11126
11329
  async function statusCommand(options) {
@@ -11149,7 +11352,7 @@ async function statusCommand(options) {
11149
11352
  }
11150
11353
  console.log(` Files managed: ${chalk16.cyan(manifest.entries.length.toString())}`);
11151
11354
  for (const entry of manifest.entries) {
11152
- const exists = fs35.existsSync(entry.path);
11355
+ const exists = fs36.existsSync(entry.path);
11153
11356
  const icon = exists ? chalk16.green("\u2713") : chalk16.red("\u2717");
11154
11357
  console.log(` ${icon} ${entry.path} (${entry.action})`);
11155
11358
  }
@@ -11306,42 +11509,42 @@ async function regenerateCommand(options) {
11306
11509
  }
11307
11510
 
11308
11511
  // src/commands/score.ts
11309
- import fs36 from "fs";
11512
+ import fs37 from "fs";
11310
11513
  import os7 from "os";
11311
11514
  import path29 from "path";
11312
- import { execFileSync as execFileSync2 } from "child_process";
11515
+ import { execFileSync as execFileSync4 } from "child_process";
11313
11516
  import chalk18 from "chalk";
11314
11517
  init_resolve_caliber();
11315
11518
  var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".cursorrules", "CALIBER_LEARNINGS.md"];
11316
11519
  var CONFIG_DIRS = [".claude", ".cursor"];
11317
11520
  function scoreBaseRef(ref, target) {
11318
11521
  if (!/^[\w.\-/~^@{}]+$/.test(ref)) return null;
11319
- const tmpDir = fs36.mkdtempSync(path29.join(os7.tmpdir(), "caliber-compare-"));
11522
+ const tmpDir = fs37.mkdtempSync(path29.join(os7.tmpdir(), "caliber-compare-"));
11320
11523
  try {
11321
11524
  for (const file of CONFIG_FILES) {
11322
11525
  try {
11323
- const content = execFileSync2("git", ["show", `${ref}:${file}`], {
11526
+ const content = execFileSync4("git", ["show", `${ref}:${file}`], {
11324
11527
  encoding: "utf-8",
11325
11528
  stdio: ["pipe", "pipe", "pipe"]
11326
11529
  });
11327
- fs36.writeFileSync(path29.join(tmpDir, file), content);
11530
+ fs37.writeFileSync(path29.join(tmpDir, file), content);
11328
11531
  } catch {
11329
11532
  }
11330
11533
  }
11331
11534
  for (const dir of CONFIG_DIRS) {
11332
11535
  try {
11333
- const files = execFileSync2("git", ["ls-tree", "-r", "--name-only", ref, `${dir}/`], {
11536
+ const files = execFileSync4("git", ["ls-tree", "-r", "--name-only", ref, `${dir}/`], {
11334
11537
  encoding: "utf-8",
11335
11538
  stdio: ["pipe", "pipe", "pipe"]
11336
11539
  }).trim().split("\n").filter(Boolean);
11337
11540
  for (const file of files) {
11338
11541
  const filePath = path29.join(tmpDir, file);
11339
- fs36.mkdirSync(path29.dirname(filePath), { recursive: true });
11340
- const content = execFileSync2("git", ["show", `${ref}:${file}`], {
11542
+ fs37.mkdirSync(path29.dirname(filePath), { recursive: true });
11543
+ const content = execFileSync4("git", ["show", `${ref}:${file}`], {
11341
11544
  encoding: "utf-8",
11342
11545
  stdio: ["pipe", "pipe", "pipe"]
11343
11546
  });
11344
- fs36.writeFileSync(filePath, content);
11547
+ fs37.writeFileSync(filePath, content);
11345
11548
  }
11346
11549
  } catch {
11347
11550
  }
@@ -11351,7 +11554,7 @@ function scoreBaseRef(ref, target) {
11351
11554
  } catch {
11352
11555
  return null;
11353
11556
  } finally {
11354
- fs36.rmSync(tmpDir, { recursive: true, force: true });
11557
+ fs37.rmSync(tmpDir, { recursive: true, force: true });
11355
11558
  }
11356
11559
  }
11357
11560
  async function scoreCommand(options) {
@@ -11444,10 +11647,11 @@ async function scoreCommand(options) {
11444
11647
  }
11445
11648
 
11446
11649
  // src/commands/refresh.ts
11447
- import fs41 from "fs";
11650
+ import fs42 from "fs";
11448
11651
  import path34 from "path";
11449
11652
  import chalk19 from "chalk";
11450
11653
  import ora6 from "ora";
11654
+ import pLimit from "p-limit";
11451
11655
 
11452
11656
  // src/lib/git-diff.ts
11453
11657
  import { execSync as execSync15 } from "child_process";
@@ -11560,13 +11764,13 @@ function scopeDiffToDir(diff, dir, allConfigDirs) {
11560
11764
 
11561
11765
  // src/writers/refresh.ts
11562
11766
  init_pre_commit_block();
11563
- import fs37 from "fs";
11767
+ import fs38 from "fs";
11564
11768
  import path30 from "path";
11565
11769
  function writeFileGroup(groupDir, files) {
11566
- fs37.mkdirSync(groupDir, { recursive: true });
11770
+ fs38.mkdirSync(groupDir, { recursive: true });
11567
11771
  return files.map((file) => {
11568
11772
  const filePath = path30.join(groupDir, file.filename);
11569
- fs37.writeFileSync(filePath, file.content);
11773
+ fs38.writeFileSync(filePath, file.content);
11570
11774
  return filePath.replace(/\\/g, "/");
11571
11775
  });
11572
11776
  }
@@ -11575,18 +11779,18 @@ function writeRefreshDocs(docs, dir = ".") {
11575
11779
  const p = (relPath) => (dir === "." ? relPath : path30.join(dir, relPath)).replace(/\\/g, "/");
11576
11780
  const ensureParent = (filePath) => {
11577
11781
  const parent = path30.dirname(filePath);
11578
- if (parent !== "." && !fs37.existsSync(parent)) fs37.mkdirSync(parent, { recursive: true });
11782
+ if (parent !== "." && !fs38.existsSync(parent)) fs38.mkdirSync(parent, { recursive: true });
11579
11783
  };
11580
11784
  if (docs.agentsMd) {
11581
11785
  const filePath = p("AGENTS.md");
11582
11786
  ensureParent(filePath);
11583
- fs37.writeFileSync(filePath, appendManagedBlocks(docs.agentsMd, "codex"));
11787
+ fs38.writeFileSync(filePath, appendManagedBlocks(docs.agentsMd, "codex"));
11584
11788
  written.push(filePath);
11585
11789
  }
11586
11790
  if (docs.claudeMd) {
11587
11791
  const filePath = p("CLAUDE.md");
11588
11792
  ensureParent(filePath);
11589
- fs37.writeFileSync(filePath, appendManagedBlocks(docs.claudeMd));
11793
+ fs38.writeFileSync(filePath, appendManagedBlocks(docs.claudeMd));
11590
11794
  written.push(filePath);
11591
11795
  }
11592
11796
  if (docs.claudeRules) {
@@ -11595,13 +11799,13 @@ function writeRefreshDocs(docs, dir = ".") {
11595
11799
  if (docs.readmeMd) {
11596
11800
  const filePath = p("README.md");
11597
11801
  ensureParent(filePath);
11598
- fs37.writeFileSync(filePath, docs.readmeMd);
11802
+ fs38.writeFileSync(filePath, docs.readmeMd);
11599
11803
  written.push(filePath);
11600
11804
  }
11601
11805
  if (docs.cursorrules) {
11602
11806
  const filePath = p(".cursorrules");
11603
11807
  ensureParent(filePath);
11604
- fs37.writeFileSync(filePath, docs.cursorrules);
11808
+ fs38.writeFileSync(filePath, docs.cursorrules);
11605
11809
  written.push(filePath);
11606
11810
  }
11607
11811
  if (docs.cursorRules) {
@@ -11610,7 +11814,7 @@ function writeRefreshDocs(docs, dir = ".") {
11610
11814
  if (docs.copilotInstructions) {
11611
11815
  const filePath = p(path30.join(".github", "copilot-instructions.md"));
11612
11816
  ensureParent(filePath);
11613
- fs37.writeFileSync(filePath, appendManagedBlocks(docs.copilotInstructions, "copilot"));
11817
+ fs38.writeFileSync(filePath, appendManagedBlocks(docs.copilotInstructions, "copilot"));
11614
11818
  written.push(filePath);
11615
11819
  }
11616
11820
  if (docs.copilotInstructionFiles) {
@@ -11752,7 +11956,7 @@ Changed files: ${diff.changedFiles.join(", ")}`);
11752
11956
  }
11753
11957
 
11754
11958
  // src/learner/writer.ts
11755
- import fs38 from "fs";
11959
+ import fs39 from "fs";
11756
11960
  import path31 from "path";
11757
11961
 
11758
11962
  // src/learner/utils.ts
@@ -11870,8 +12074,8 @@ function deduplicateLearnedItems(existing, incoming) {
11870
12074
  }
11871
12075
  function writeLearnedSectionTo(filePath, header, existing, incoming, mode) {
11872
12076
  const { merged, newCount, newItems } = deduplicateLearnedItems(existing, incoming);
11873
- fs38.writeFileSync(filePath, header + merged + "\n");
11874
- if (mode) fs38.chmodSync(filePath, mode);
12077
+ fs39.writeFileSync(filePath, header + merged + "\n");
12078
+ if (mode) fs39.chmodSync(filePath, mode);
11875
12079
  return { newCount, newItems };
11876
12080
  }
11877
12081
  function writeLearnedSection(content) {
@@ -11879,11 +12083,11 @@ function writeLearnedSection(content) {
11879
12083
  }
11880
12084
  function writeLearnedSkill(skill) {
11881
12085
  const skillDir = path31.join(".claude", "skills", skill.name);
11882
- if (!fs38.existsSync(skillDir)) fs38.mkdirSync(skillDir, { recursive: true });
12086
+ if (!fs39.existsSync(skillDir)) fs39.mkdirSync(skillDir, { recursive: true });
11883
12087
  const skillPath = path31.join(skillDir, "SKILL.md");
11884
- if (!skill.isNew && fs38.existsSync(skillPath)) {
11885
- const existing = fs38.readFileSync(skillPath, "utf-8");
11886
- fs38.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
12088
+ if (!skill.isNew && fs39.existsSync(skillPath)) {
12089
+ const existing = fs39.readFileSync(skillPath, "utf-8");
12090
+ fs39.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
11887
12091
  } else {
11888
12092
  const frontmatter = [
11889
12093
  "---",
@@ -11892,12 +12096,12 @@ function writeLearnedSkill(skill) {
11892
12096
  "---",
11893
12097
  ""
11894
12098
  ].join("\n");
11895
- fs38.writeFileSync(skillPath, frontmatter + skill.content);
12099
+ fs39.writeFileSync(skillPath, frontmatter + skill.content);
11896
12100
  }
11897
12101
  return skillPath;
11898
12102
  }
11899
12103
  function writePersonalLearnedSection(content) {
11900
- if (!fs38.existsSync(AUTH_DIR)) fs38.mkdirSync(AUTH_DIR, { recursive: true });
12104
+ if (!fs39.existsSync(AUTH_DIR)) fs39.mkdirSync(AUTH_DIR, { recursive: true });
11901
12105
  return writeLearnedSectionTo(PERSONAL_LEARNINGS_FILE, PERSONAL_LEARNINGS_HEADER, readPersonalLearnings(), content, 384);
11902
12106
  }
11903
12107
  function addLearning(bullet, scope = "project") {
@@ -11910,38 +12114,38 @@ function addLearning(bullet, scope = "project") {
11910
12114
  return { file: LEARNINGS_FILE, added: result.newCount > 0 };
11911
12115
  }
11912
12116
  function readPersonalLearnings() {
11913
- if (!fs38.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
11914
- const content = fs38.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
12117
+ if (!fs39.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
12118
+ const content = fs39.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
11915
12119
  const bullets = content.split("\n").filter((l) => l.startsWith("- ")).join("\n");
11916
12120
  return bullets || null;
11917
12121
  }
11918
12122
  function readLearnedSection() {
11919
- if (fs38.existsSync(LEARNINGS_FILE)) {
11920
- const content2 = fs38.readFileSync(LEARNINGS_FILE, "utf-8");
12123
+ if (fs39.existsSync(LEARNINGS_FILE)) {
12124
+ const content2 = fs39.readFileSync(LEARNINGS_FILE, "utf-8");
11921
12125
  const bullets = content2.split("\n").filter((l) => l.startsWith("- ")).join("\n");
11922
12126
  return bullets || null;
11923
12127
  }
11924
12128
  const claudeMdPath = "CLAUDE.md";
11925
- if (!fs38.existsSync(claudeMdPath)) return null;
11926
- const content = fs38.readFileSync(claudeMdPath, "utf-8");
12129
+ if (!fs39.existsSync(claudeMdPath)) return null;
12130
+ const content = fs39.readFileSync(claudeMdPath, "utf-8");
11927
12131
  const startIdx = content.indexOf(LEARNED_START);
11928
12132
  const endIdx = content.indexOf(LEARNED_END);
11929
12133
  if (startIdx === -1 || endIdx === -1) return null;
11930
12134
  return content.slice(startIdx + LEARNED_START.length, endIdx).trim() || null;
11931
12135
  }
11932
12136
  function migrateInlineLearnings() {
11933
- if (fs38.existsSync(LEARNINGS_FILE)) return false;
12137
+ if (fs39.existsSync(LEARNINGS_FILE)) return false;
11934
12138
  const claudeMdPath = "CLAUDE.md";
11935
- if (!fs38.existsSync(claudeMdPath)) return false;
11936
- const content = fs38.readFileSync(claudeMdPath, "utf-8");
12139
+ if (!fs39.existsSync(claudeMdPath)) return false;
12140
+ const content = fs39.readFileSync(claudeMdPath, "utf-8");
11937
12141
  const startIdx = content.indexOf(LEARNED_START);
11938
12142
  const endIdx = content.indexOf(LEARNED_END);
11939
12143
  if (startIdx === -1 || endIdx === -1) return false;
11940
12144
  const section = content.slice(startIdx + LEARNED_START.length, endIdx).trim();
11941
12145
  if (!section) return false;
11942
- fs38.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
12146
+ fs39.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
11943
12147
  const cleaned = content.slice(0, startIdx) + content.slice(endIdx + LEARNED_END.length);
11944
- fs38.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
12148
+ fs39.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
11945
12149
  return true;
11946
12150
  }
11947
12151
 
@@ -11951,7 +12155,7 @@ init_resolve_caliber();
11951
12155
  init_builtin_skills();
11952
12156
 
11953
12157
  // src/lib/config-discovery.ts
11954
- import fs39 from "fs";
12158
+ import fs40 from "fs";
11955
12159
  import path32 from "path";
11956
12160
  var CONFIG_FILE_MARKERS = [
11957
12161
  "CLAUDE.md",
@@ -11978,11 +12182,11 @@ var IGNORE_DIRS3 = /* @__PURE__ */ new Set([
11978
12182
  var MAX_DEPTH = 4;
11979
12183
  function hasConfigFiles(dir) {
11980
12184
  for (const marker of CONFIG_FILE_MARKERS) {
11981
- if (fs39.existsSync(path32.join(dir, marker))) return true;
12185
+ if (fs40.existsSync(path32.join(dir, marker))) return true;
11982
12186
  }
11983
12187
  for (const marker of CONFIG_DIR_MARKERS) {
11984
12188
  const markerPath = path32.join(dir, marker);
11985
- if (fs39.existsSync(markerPath) && fs39.statSync(markerPath).isDirectory()) return true;
12189
+ if (fs40.existsSync(markerPath) && fs40.statSync(markerPath).isDirectory()) return true;
11986
12190
  }
11987
12191
  return false;
11988
12192
  }
@@ -11999,7 +12203,7 @@ function walkForConfigs(baseDir, currentDir, depth, result) {
11999
12203
  if (depth >= MAX_DEPTH) return;
12000
12204
  let entries;
12001
12205
  try {
12002
- entries = fs39.readdirSync(currentDir, { withFileTypes: true });
12206
+ entries = fs40.readdirSync(currentDir, { withFileTypes: true });
12003
12207
  } catch {
12004
12208
  return;
12005
12209
  }
@@ -12018,8 +12222,8 @@ function walkForConfigs(baseDir, currentDir, depth, result) {
12018
12222
  // src/commands/refresh.ts
12019
12223
  function writeRefreshError(error) {
12020
12224
  try {
12021
- if (!fs41.existsSync(CALIBER_DIR)) fs41.mkdirSync(CALIBER_DIR, { recursive: true });
12022
- fs41.writeFileSync(
12225
+ if (!fs42.existsSync(CALIBER_DIR)) fs42.mkdirSync(CALIBER_DIR, { recursive: true });
12226
+ fs42.writeFileSync(
12023
12227
  REFRESH_LAST_ERROR_FILE,
12024
12228
  JSON.stringify(
12025
12229
  {
@@ -12038,15 +12242,15 @@ function writeRefreshError(error) {
12038
12242
  }
12039
12243
  function readRefreshError() {
12040
12244
  try {
12041
- if (!fs41.existsSync(REFRESH_LAST_ERROR_FILE)) return null;
12042
- return JSON.parse(fs41.readFileSync(REFRESH_LAST_ERROR_FILE, "utf-8"));
12245
+ if (!fs42.existsSync(REFRESH_LAST_ERROR_FILE)) return null;
12246
+ return JSON.parse(fs42.readFileSync(REFRESH_LAST_ERROR_FILE, "utf-8"));
12043
12247
  } catch {
12044
12248
  return null;
12045
12249
  }
12046
12250
  }
12047
12251
  function clearRefreshError() {
12048
12252
  try {
12049
- if (fs41.existsSync(REFRESH_LAST_ERROR_FILE)) fs41.unlinkSync(REFRESH_LAST_ERROR_FILE);
12253
+ if (fs42.existsSync(REFRESH_LAST_ERROR_FILE)) fs42.unlinkSync(REFRESH_LAST_ERROR_FILE);
12050
12254
  } catch {
12051
12255
  }
12052
12256
  }
@@ -12066,11 +12270,11 @@ function log2(quiet, ...args) {
12066
12270
  function discoverGitRepos(parentDir) {
12067
12271
  const repos = [];
12068
12272
  try {
12069
- const entries = fs41.readdirSync(parentDir, { withFileTypes: true });
12273
+ const entries = fs42.readdirSync(parentDir, { withFileTypes: true });
12070
12274
  for (const entry of entries) {
12071
12275
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
12072
12276
  const childPath = path34.join(parentDir, entry.name);
12073
- if (fs41.existsSync(path34.join(childPath, ".git"))) {
12277
+ if (fs42.existsSync(path34.join(childPath, ".git"))) {
12074
12278
  repos.push(childPath);
12075
12279
  }
12076
12280
  }
@@ -12101,12 +12305,15 @@ function collectFilesToWrite(updatedDocs, dir = ".") {
12101
12305
  return files;
12102
12306
  }
12103
12307
  var REFRESH_COOLDOWN_MS = 3e4;
12308
+ var PARALLEL_DIR_CONCURRENCY = 4;
12104
12309
  async function refreshDir(repoDir, dir, diff, options) {
12105
12310
  const quiet = !!options.quiet;
12311
+ const suppress = !!options.suppressSpinner;
12312
+ const effectiveQuiet = quiet || suppress;
12106
12313
  const prefix = options.label ? `${chalk19.bold(options.label)} ` : "";
12107
12314
  const absDir = dir === "." ? repoDir : path34.resolve(repoDir, dir);
12108
12315
  const scope = dir === "." ? void 0 : dir;
12109
- const spinner = quiet ? null : ora6(`${prefix}Analyzing changes...`).start();
12316
+ const spinner = effectiveQuiet ? null : ora6(`${prefix}Analyzing changes...`).start();
12110
12317
  const learnedSection = readLearnedSection();
12111
12318
  const fingerprint = await collectFingerprint(absDir);
12112
12319
  const existingDocs = fingerprint.existingConfigs;
@@ -12155,7 +12362,7 @@ async function refreshDir(repoDir, dir, diff, options) {
12155
12362
  }
12156
12363
  if (!response.docsUpdated || response.docsUpdated.length === 0) {
12157
12364
  spinner?.succeed(`${prefix}No doc updates needed`);
12158
- return { written: [] };
12365
+ return { written: [], fileChanges: [], syncedAgents: [], changesSummary: null };
12159
12366
  }
12160
12367
  if (options.dryRun) {
12161
12368
  spinner?.info(`${prefix}Dry run \u2014 would update:`);
@@ -12166,14 +12373,14 @@ async function refreshDir(repoDir, dir, diff, options) {
12166
12373
  console.log(chalk19.dim(`
12167
12374
  ${response.changesSummary}`));
12168
12375
  }
12169
- return { written: [] };
12376
+ return { written: [], fileChanges: [], syncedAgents: [], changesSummary: null };
12170
12377
  }
12171
12378
  const allFilesToWrite = collectFilesToWrite(response.updatedDocs, dir);
12172
12379
  const preRefreshContents = /* @__PURE__ */ new Map();
12173
12380
  for (const filePath of allFilesToWrite) {
12174
12381
  const fullPath = path34.resolve(repoDir, filePath);
12175
12382
  try {
12176
- preRefreshContents.set(filePath, fs41.readFileSync(fullPath, "utf-8"));
12383
+ preRefreshContents.set(filePath, fs42.readFileSync(fullPath, "utf-8"));
12177
12384
  } catch {
12178
12385
  preRefreshContents.set(filePath, null);
12179
12386
  }
@@ -12192,43 +12399,47 @@ async function refreshDir(repoDir, dir, diff, options) {
12192
12399
  const fullPath = path34.resolve(repoDir, filePath);
12193
12400
  if (content === null) {
12194
12401
  try {
12195
- fs41.unlinkSync(fullPath);
12402
+ fs42.unlinkSync(fullPath);
12196
12403
  } catch {
12197
12404
  }
12198
12405
  } else {
12199
- fs41.writeFileSync(fullPath, content);
12406
+ fs42.writeFileSync(fullPath, content);
12200
12407
  }
12201
12408
  }
12202
12409
  spinner?.warn(
12203
12410
  `${prefix}Refresh reverted \u2014 score would drop from ${preScore.score} to ${postScore.score}`
12204
12411
  );
12205
- log2(quiet, chalk19.dim(` Config quality gate prevented a regression. No files were changed.`));
12206
- return { written: [] };
12412
+ log2(
12413
+ effectiveQuiet,
12414
+ chalk19.dim(` Config quality gate prevented a regression. No files were changed.`)
12415
+ );
12416
+ return { written: [], fileChanges: [], syncedAgents: [], changesSummary: null };
12207
12417
  }
12208
12418
  recordScore(postScore, "refresh");
12209
12419
  }
12210
12420
  spinner?.succeed(`${prefix}Updated ${written.length} doc${written.length === 1 ? "" : "s"}`);
12211
- const fileChangesMap = new Map(
12212
- (response.fileChanges || []).map((fc) => [
12213
- fc.file,
12214
- fc.description
12215
- ])
12216
- );
12217
- for (const file of written) {
12218
- const desc = fileChangesMap.get(file);
12219
- const suffix = desc ? chalk19.dim(` \u2014 ${desc}`) : "";
12220
- log2(quiet, ` ${chalk19.green("\u2713")} ${file}${suffix}`);
12221
- }
12222
- const agents = detectSyncedAgents(written);
12223
- if (agents.length > 1) {
12224
- log2(quiet, chalk19.cyan(`
12225
- ${agents.length} agent formats in sync (${agents.join(", ")})`));
12226
- }
12227
- if (response.changesSummary) {
12228
- log2(quiet, chalk19.dim(`
12421
+ const fileChanges = response.fileChanges || [];
12422
+ const fileChangesMap = new Map(fileChanges.map((fc) => [fc.file, fc.description]));
12423
+ const syncedAgents = detectSyncedAgents(written);
12424
+ if (!suppress) {
12425
+ for (const file of written) {
12426
+ const desc = fileChangesMap.get(file);
12427
+ const suffix = desc ? chalk19.dim(` \u2014 ${desc}`) : "";
12428
+ log2(effectiveQuiet, ` ${chalk19.green("\u2713")} ${file}${suffix}`);
12429
+ }
12430
+ if (syncedAgents.length > 1) {
12431
+ log2(
12432
+ effectiveQuiet,
12433
+ chalk19.cyan(`
12434
+ ${syncedAgents.length} agent formats in sync (${syncedAgents.join(", ")})`)
12435
+ );
12436
+ }
12437
+ if (response.changesSummary) {
12438
+ log2(effectiveQuiet, chalk19.dim(`
12229
12439
  ${response.changesSummary}`));
12440
+ }
12230
12441
  }
12231
- return { written };
12442
+ return { written, fileChanges, syncedAgents, changesSummary: response.changesSummary };
12232
12443
  }
12233
12444
  async function refreshSingleRepo(repoDir, options) {
12234
12445
  const quiet = !!options.quiet;
@@ -12260,21 +12471,57 @@ async function refreshSingleRepo(repoDir, options) {
12260
12471
  } else {
12261
12472
  log2(quiet, chalk19.dim(`${prefix}Found configs in ${configDirs.length} directories
12262
12473
  `));
12474
+ const dirsWithChanges = configDirs.map((dir) => ({ dir, scopedDiff: scopeDiffToDir(diff, dir, configDirs) })).filter(({ scopedDiff }) => scopedDiff.hasChanges);
12475
+ const parallelSpinner = quiet ? null : ora6(
12476
+ `${prefix}Refreshing ${dirsWithChanges.length} director${dirsWithChanges.length === 1 ? "y" : "ies"}...`
12477
+ ).start();
12478
+ const limit = pLimit(PARALLEL_DIR_CONCURRENCY);
12479
+ const results = await Promise.allSettled(
12480
+ dirsWithChanges.map(({ dir, scopedDiff }) => {
12481
+ const dirLabel = dir === "." ? "root" : dir;
12482
+ return limit(
12483
+ () => refreshDir(repoDir, dir, scopedDiff, {
12484
+ ...options,
12485
+ suppressSpinner: true,
12486
+ label: dirLabel
12487
+ })
12488
+ );
12489
+ })
12490
+ );
12491
+ parallelSpinner?.stop();
12263
12492
  let hadFailure = false;
12264
- for (const dir of configDirs) {
12265
- const scopedDiff = scopeDiffToDir(diff, dir, configDirs);
12266
- if (!scopedDiff.hasChanges) continue;
12493
+ for (const [i, result] of results.entries()) {
12494
+ const { dir } = dirsWithChanges[i];
12267
12495
  const dirLabel = dir === "." ? "root" : dir;
12268
- try {
12269
- await refreshDir(repoDir, dir, scopedDiff, { ...options, label: dirLabel });
12270
- } catch (err) {
12496
+ if (result.status === "rejected") {
12271
12497
  hadFailure = true;
12272
12498
  log2(
12273
12499
  quiet,
12274
12500
  chalk19.yellow(
12275
- ` ${dirLabel}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`
12501
+ ` ${dirLabel}: refresh failed \u2014 ${result.reason instanceof Error ? result.reason.message : "unknown error"}`
12276
12502
  )
12277
12503
  );
12504
+ } else {
12505
+ const { written, fileChanges, syncedAgents, changesSummary } = result.value;
12506
+ const fileChangesMap = new Map(fileChanges.map((fc) => [fc.file, fc.description]));
12507
+ for (const file of written) {
12508
+ const desc = fileChangesMap.get(file);
12509
+ const suffix = desc ? chalk19.dim(` \u2014 ${desc}`) : "";
12510
+ log2(quiet, ` ${chalk19.green("\u2713")} ${dirLabel}/${file}${suffix}`);
12511
+ }
12512
+ if (syncedAgents.length > 1) {
12513
+ log2(
12514
+ quiet,
12515
+ chalk19.cyan(
12516
+ `
12517
+ ${syncedAgents.length} agent formats in sync (${syncedAgents.join(", ")})`
12518
+ )
12519
+ );
12520
+ }
12521
+ if (changesSummary) {
12522
+ log2(quiet, chalk19.dim(`
12523
+ ${changesSummary}`));
12524
+ }
12278
12525
  }
12279
12526
  }
12280
12527
  if (hadFailure) {
@@ -12366,7 +12613,7 @@ async function refreshCommand(options) {
12366
12613
 
12367
12614
  // src/commands/hooks.ts
12368
12615
  import chalk20 from "chalk";
12369
- import fs42 from "fs";
12616
+ import fs43 from "fs";
12370
12617
  var HOOKS = [
12371
12618
  {
12372
12619
  id: "session-end",
@@ -12430,11 +12677,11 @@ async function hooksCommand(options) {
12430
12677
  console.log(chalk20.green(" \u2713") + ` ${hook.label} enabled`);
12431
12678
  }
12432
12679
  }
12433
- if (fs42.existsSync(".claude")) {
12680
+ if (fs43.existsSync(".claude")) {
12434
12681
  const r = installLearningHooks();
12435
12682
  if (r.installed) console.log(chalk20.green(" \u2713") + " Claude Code learning hooks enabled");
12436
12683
  }
12437
- if (fs42.existsSync(".cursor")) {
12684
+ if (fs43.existsSync(".cursor")) {
12438
12685
  const r = installCursorLearningHooks();
12439
12686
  if (r.installed) console.log(chalk20.green(" \u2713") + " Cursor learning hooks enabled");
12440
12687
  }
@@ -12602,7 +12849,7 @@ async function configCommand() {
12602
12849
  }
12603
12850
 
12604
12851
  // src/commands/learn.ts
12605
- import fs46 from "fs";
12852
+ import fs47 from "fs";
12606
12853
  import path38 from "path";
12607
12854
  import chalk23 from "chalk";
12608
12855
 
@@ -12634,7 +12881,7 @@ function readStdin() {
12634
12881
  }
12635
12882
 
12636
12883
  // src/learner/storage.ts
12637
- import fs43 from "fs";
12884
+ import fs44 from "fs";
12638
12885
  import path35 from "path";
12639
12886
  var MAX_RESPONSE_LENGTH = 2e3;
12640
12887
  var MAX_PROMPT_LENGTH = 2e3;
@@ -12646,8 +12893,8 @@ var DEFAULT_STATE = {
12646
12893
  lastAnalysisEventCount: 0
12647
12894
  };
12648
12895
  function ensureLearningDir() {
12649
- if (!fs43.existsSync(getLearningDir())) {
12650
- fs43.mkdirSync(getLearningDir(), { recursive: true });
12896
+ if (!fs44.existsSync(getLearningDir())) {
12897
+ fs44.mkdirSync(getLearningDir(), { recursive: true });
12651
12898
  }
12652
12899
  }
12653
12900
  function sessionFilePath() {
@@ -12663,9 +12910,9 @@ function truncateResponse(response) {
12663
12910
  }
12664
12911
  function trimSessionFileIfNeeded(filePath) {
12665
12912
  try {
12666
- const stat = fs43.statSync(filePath);
12913
+ const stat = fs44.statSync(filePath);
12667
12914
  if (stat.size > MAX_SESSION_FILE_BYTES) {
12668
- fs43.writeFileSync(filePath, "");
12915
+ fs44.writeFileSync(filePath, "");
12669
12916
  resetState();
12670
12917
  return;
12671
12918
  }
@@ -12674,10 +12921,10 @@ function trimSessionFileIfNeeded(filePath) {
12674
12921
  }
12675
12922
  const state = readState2();
12676
12923
  if (state.eventCount + 1 > LEARNING_MAX_EVENTS) {
12677
- const lines = fs43.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
12924
+ const lines = fs44.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
12678
12925
  if (lines.length > LEARNING_MAX_EVENTS) {
12679
12926
  const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
12680
- fs43.writeFileSync(filePath, kept.join("\n") + "\n");
12927
+ fs44.writeFileSync(filePath, kept.join("\n") + "\n");
12681
12928
  }
12682
12929
  }
12683
12930
  }
@@ -12685,7 +12932,7 @@ function appendEvent(event) {
12685
12932
  ensureLearningDir();
12686
12933
  const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
12687
12934
  const filePath = sessionFilePath();
12688
- fs43.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
12935
+ fs44.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
12689
12936
  trimSessionFileIfNeeded(filePath);
12690
12937
  }
12691
12938
  function appendPromptEvent(event) {
@@ -12695,22 +12942,22 @@ function appendPromptEvent(event) {
12695
12942
  prompt_content: event.prompt_content.length > MAX_PROMPT_LENGTH ? event.prompt_content.slice(0, MAX_PROMPT_LENGTH) : event.prompt_content
12696
12943
  };
12697
12944
  const filePath = sessionFilePath();
12698
- fs43.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
12945
+ fs44.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
12699
12946
  trimSessionFileIfNeeded(filePath);
12700
12947
  }
12701
12948
  function readAllEvents() {
12702
12949
  const filePath = sessionFilePath();
12703
12950
  try {
12704
- const stat = fs43.statSync(filePath);
12951
+ const stat = fs44.statSync(filePath);
12705
12952
  if (stat.size > MAX_SESSION_FILE_BYTES) {
12706
- fs43.writeFileSync(filePath, "");
12953
+ fs44.writeFileSync(filePath, "");
12707
12954
  resetState();
12708
12955
  return [];
12709
12956
  }
12710
12957
  } catch {
12711
12958
  return [];
12712
12959
  }
12713
- const lines = fs43.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
12960
+ const lines = fs44.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
12714
12961
  const events = [];
12715
12962
  for (const line of lines) {
12716
12963
  try {
@@ -12723,33 +12970,33 @@ function readAllEvents() {
12723
12970
  function getEventCount() {
12724
12971
  const filePath = sessionFilePath();
12725
12972
  try {
12726
- const stat = fs43.statSync(filePath);
12973
+ const stat = fs44.statSync(filePath);
12727
12974
  if (stat.size > MAX_SESSION_FILE_BYTES) return 0;
12728
12975
  } catch {
12729
12976
  return 0;
12730
12977
  }
12731
- const content = fs43.readFileSync(filePath, "utf-8");
12978
+ const content = fs44.readFileSync(filePath, "utf-8");
12732
12979
  return content.split("\n").filter(Boolean).length;
12733
12980
  }
12734
12981
  function clearSession() {
12735
12982
  const filePath = sessionFilePath();
12736
12983
  try {
12737
- fs43.writeFileSync(filePath, "");
12984
+ fs44.writeFileSync(filePath, "");
12738
12985
  } catch {
12739
12986
  }
12740
12987
  }
12741
12988
  function readState2() {
12742
12989
  const filePath = stateFilePath();
12743
- if (!fs43.existsSync(filePath)) return { ...DEFAULT_STATE };
12990
+ if (!fs44.existsSync(filePath)) return { ...DEFAULT_STATE };
12744
12991
  try {
12745
- return JSON.parse(fs43.readFileSync(filePath, "utf-8"));
12992
+ return JSON.parse(fs44.readFileSync(filePath, "utf-8"));
12746
12993
  } catch {
12747
12994
  return { ...DEFAULT_STATE };
12748
12995
  }
12749
12996
  }
12750
12997
  function writeState2(state) {
12751
12998
  ensureLearningDir();
12752
- fs43.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
12999
+ fs44.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
12753
13000
  }
12754
13001
  function resetState() {
12755
13002
  writeState2({ ...DEFAULT_STATE });
@@ -12762,11 +13009,11 @@ function lockFilePath() {
12762
13009
  function acquireFinalizeLock() {
12763
13010
  ensureLearningDir();
12764
13011
  const lockPath = lockFilePath();
12765
- if (fs43.existsSync(lockPath)) {
13012
+ if (fs44.existsSync(lockPath)) {
12766
13013
  try {
12767
- const stat = fs43.statSync(lockPath);
13014
+ const stat = fs44.statSync(lockPath);
12768
13015
  if (Date.now() - stat.mtimeMs < LOCK_STALE_MS) {
12769
- const pid = parseInt(fs43.readFileSync(lockPath, "utf-8").trim(), 10);
13016
+ const pid = parseInt(fs44.readFileSync(lockPath, "utf-8").trim(), 10);
12770
13017
  if (!isNaN(pid) && isProcessAlive(pid)) {
12771
13018
  return false;
12772
13019
  }
@@ -12774,12 +13021,12 @@ function acquireFinalizeLock() {
12774
13021
  } catch {
12775
13022
  }
12776
13023
  try {
12777
- fs43.unlinkSync(lockPath);
13024
+ fs44.unlinkSync(lockPath);
12778
13025
  } catch {
12779
13026
  }
12780
13027
  }
12781
13028
  try {
12782
- fs43.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
13029
+ fs44.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
12783
13030
  return true;
12784
13031
  } catch {
12785
13032
  return false;
@@ -12796,13 +13043,13 @@ function isProcessAlive(pid) {
12796
13043
  function releaseFinalizeLock() {
12797
13044
  const lockPath = lockFilePath();
12798
13045
  try {
12799
- if (fs43.existsSync(lockPath)) fs43.unlinkSync(lockPath);
13046
+ if (fs44.existsSync(lockPath)) fs44.unlinkSync(lockPath);
12800
13047
  } catch {
12801
13048
  }
12802
13049
  }
12803
13050
 
12804
13051
  // src/lib/notifications.ts
12805
- import fs44 from "fs";
13052
+ import fs45 from "fs";
12806
13053
  import path36 from "path";
12807
13054
  import chalk22 from "chalk";
12808
13055
  function notificationFilePath() {
@@ -12811,15 +13058,15 @@ function notificationFilePath() {
12811
13058
  function writeFinalizeSummary(summary) {
12812
13059
  try {
12813
13060
  ensureLearningDir();
12814
- fs44.writeFileSync(notificationFilePath(), JSON.stringify(summary, null, 2));
13061
+ fs45.writeFileSync(notificationFilePath(), JSON.stringify(summary, null, 2));
12815
13062
  } catch {
12816
13063
  }
12817
13064
  }
12818
13065
  function checkPendingNotifications() {
12819
13066
  try {
12820
- if (!fs44.existsSync(notificationFilePath())) return;
12821
- const raw = fs44.readFileSync(notificationFilePath(), "utf-8");
12822
- fs44.unlinkSync(notificationFilePath());
13067
+ if (!fs45.existsSync(notificationFilePath())) return;
13068
+ const raw = fs45.readFileSync(notificationFilePath(), "utf-8");
13069
+ fs45.unlinkSync(notificationFilePath());
12823
13070
  const summary = JSON.parse(raw);
12824
13071
  if (!summary.newItemCount || summary.newItemCount === 0) return;
12825
13072
  const wasteLabel = summary.wasteTokens > 0 ? ` (~${summary.wasteTokens.toLocaleString()} wasted tokens captured)` : "";
@@ -12835,7 +13082,7 @@ function checkPendingNotifications() {
12835
13082
  console.log("");
12836
13083
  } catch {
12837
13084
  try {
12838
- fs44.unlinkSync(notificationFilePath());
13085
+ fs45.unlinkSync(notificationFilePath());
12839
13086
  } catch {
12840
13087
  }
12841
13088
  }
@@ -12987,7 +13234,7 @@ function calculateSessionWaste(events) {
12987
13234
  init_config();
12988
13235
 
12989
13236
  // src/learner/roi.ts
12990
- import fs45 from "fs";
13237
+ import fs46 from "fs";
12991
13238
  import path37 from "path";
12992
13239
  var DEFAULT_TOTALS = {
12993
13240
  totalWasteTokens: 0,
@@ -13006,15 +13253,15 @@ function roiFilePath() {
13006
13253
  }
13007
13254
  function readROIStats() {
13008
13255
  const filePath = roiFilePath();
13009
- if (!fs45.existsSync(filePath)) {
13256
+ if (!fs46.existsSync(filePath)) {
13010
13257
  return { learnings: [], sessions: [], totals: { ...DEFAULT_TOTALS } };
13011
13258
  }
13012
13259
  try {
13013
- return JSON.parse(fs45.readFileSync(filePath, "utf-8"));
13260
+ return JSON.parse(fs46.readFileSync(filePath, "utf-8"));
13014
13261
  } catch {
13015
13262
  try {
13016
13263
  const corruptPath = filePath + ".corrupt";
13017
- fs45.renameSync(filePath, corruptPath);
13264
+ fs46.renameSync(filePath, corruptPath);
13018
13265
  console.error(`caliber: roi-stats.json was corrupt \u2014 renamed to ${corruptPath}`);
13019
13266
  } catch {
13020
13267
  }
@@ -13023,7 +13270,7 @@ function readROIStats() {
13023
13270
  }
13024
13271
  function writeROIStats(stats) {
13025
13272
  ensureLearningDir();
13026
- fs45.writeFileSync(roiFilePath(), JSON.stringify(stats, null, 2));
13273
+ fs46.writeFileSync(roiFilePath(), JSON.stringify(stats, null, 2));
13027
13274
  }
13028
13275
  function recalculateTotals(stats) {
13029
13276
  const totals = stats.totals;
@@ -13233,20 +13480,27 @@ var INCREMENTAL_INTERVAL = 50;
13233
13480
  function writeFinalizeError(message) {
13234
13481
  try {
13235
13482
  const errorPath = path38.join(getLearningDir(), LEARNING_LAST_ERROR_FILE);
13236
- if (!fs46.existsSync(getLearningDir())) fs46.mkdirSync(getLearningDir(), { recursive: true });
13237
- fs46.writeFileSync(errorPath, JSON.stringify({
13238
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
13239
- error: message,
13240
- pid: process.pid
13241
- }, null, 2));
13483
+ if (!fs47.existsSync(getLearningDir())) fs47.mkdirSync(getLearningDir(), { recursive: true });
13484
+ fs47.writeFileSync(
13485
+ errorPath,
13486
+ JSON.stringify(
13487
+ {
13488
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
13489
+ error: message,
13490
+ pid: process.pid
13491
+ },
13492
+ null,
13493
+ 2
13494
+ )
13495
+ );
13242
13496
  } catch {
13243
13497
  }
13244
13498
  }
13245
13499
  function readFinalizeError() {
13246
13500
  try {
13247
13501
  const errorPath = path38.join(getLearningDir(), LEARNING_LAST_ERROR_FILE);
13248
- if (!fs46.existsSync(errorPath)) return null;
13249
- return JSON.parse(fs46.readFileSync(errorPath, "utf-8"));
13502
+ if (!fs47.existsSync(errorPath)) return null;
13503
+ return JSON.parse(fs47.readFileSync(errorPath, "utf-8"));
13250
13504
  } catch {
13251
13505
  return null;
13252
13506
  }
@@ -13294,17 +13548,19 @@ async function learnObserveCommand(options) {
13294
13548
  const eventsSinceLastAnalysis = state.eventCount - (state.lastAnalysisEventCount || 0);
13295
13549
  if (eventsSinceLastAnalysis >= INCREMENTAL_INTERVAL) {
13296
13550
  try {
13297
- const { resolveCaliber: resolveCaliber2 } = await Promise.resolve().then(() => (init_resolve_caliber(), resolve_caliber_exports));
13551
+ const { resolveCaliber: resolveCaliber2, isNpxResolution: isNpxResolution2 } = await Promise.resolve().then(() => (init_resolve_caliber(), resolve_caliber_exports));
13298
13552
  const bin = resolveCaliber2();
13299
13553
  const { spawn: spawn4 } = await import("child_process");
13300
13554
  const logPath = path38.join(getLearningDir(), LEARNING_FINALIZE_LOG);
13301
- if (!fs46.existsSync(getLearningDir())) fs46.mkdirSync(getLearningDir(), { recursive: true });
13302
- const logFd = fs46.openSync(logPath, "a");
13303
- spawn4(bin, ["learn", "finalize", "--auto", "--incremental"], {
13555
+ if (!fs47.existsSync(getLearningDir())) fs47.mkdirSync(getLearningDir(), { recursive: true });
13556
+ const logFd = fs47.openSync(logPath, "a");
13557
+ const NPX_SUFFIX = " --yes @rely-ai/caliber";
13558
+ const [exe, binArgs] = isNpxResolution2() ? [bin.slice(0, -NPX_SUFFIX.length) || "npx", ["--yes", "@rely-ai/caliber"]] : [bin, []];
13559
+ spawn4(exe, [...binArgs, "learn", "finalize", "--auto", "--incremental"], {
13304
13560
  detached: true,
13305
13561
  stdio: ["ignore", logFd, logFd]
13306
13562
  }).unref();
13307
- fs46.closeSync(logFd);
13563
+ fs47.closeSync(logFd);
13308
13564
  } catch {
13309
13565
  }
13310
13566
  }
@@ -13317,7 +13573,8 @@ async function learnFinalizeCommand(options) {
13317
13573
  if (!options?.force && !isAuto) {
13318
13574
  const { isCaliberRunning: isCaliberRunning2 } = await Promise.resolve().then(() => (init_lock(), lock_exports));
13319
13575
  if (isCaliberRunning2()) {
13320
- if (!isAuto) console.log(chalk23.dim("caliber: skipping finalize \u2014 another caliber process is running"));
13576
+ if (!isAuto)
13577
+ console.log(chalk23.dim("caliber: skipping finalize \u2014 another caliber process is running"));
13321
13578
  return;
13322
13579
  }
13323
13580
  }
@@ -13325,7 +13582,8 @@ async function learnFinalizeCommand(options) {
13325
13582
  await new Promise((r) => setTimeout(r, AUTO_SETTLE_MS));
13326
13583
  }
13327
13584
  if (!acquireFinalizeLock()) {
13328
- if (!isAuto) console.log(chalk23.dim("caliber: skipping finalize \u2014 another finalize is in progress"));
13585
+ if (!isAuto)
13586
+ console.log(chalk23.dim("caliber: skipping finalize \u2014 another finalize is in progress"));
13329
13587
  return;
13330
13588
  }
13331
13589
  let analyzed = false;
@@ -13333,7 +13591,11 @@ async function learnFinalizeCommand(options) {
13333
13591
  const config = loadConfig();
13334
13592
  if (!config) {
13335
13593
  if (isAuto) return;
13336
- console.log(chalk23.yellow(`caliber: no LLM provider configured \u2014 run \`${resolveCaliber()} config\` first`));
13594
+ console.log(
13595
+ chalk23.yellow(
13596
+ `caliber: no LLM provider configured \u2014 run \`${resolveCaliber()} config\` first`
13597
+ )
13598
+ );
13337
13599
  clearSession();
13338
13600
  resetState();
13339
13601
  return;
@@ -13341,7 +13603,12 @@ async function learnFinalizeCommand(options) {
13341
13603
  const allEvents = readAllEvents();
13342
13604
  const threshold = isAuto ? MIN_EVENTS_AUTO : MIN_EVENTS_FOR_ANALYSIS;
13343
13605
  if (allEvents.length < threshold) {
13344
- if (!isAuto) console.log(chalk23.dim(`caliber: ${allEvents.length}/${threshold} events recorded \u2014 need more before analysis`));
13606
+ if (!isAuto)
13607
+ console.log(
13608
+ chalk23.dim(
13609
+ `caliber: ${allEvents.length}/${threshold} events recorded \u2014 need more before analysis`
13610
+ )
13611
+ );
13345
13612
  return;
13346
13613
  }
13347
13614
  await validateModel({ fast: true });
@@ -13350,7 +13617,12 @@ async function learnFinalizeCommand(options) {
13350
13617
  const analysisOffset = isIncremental ? state.lastAnalysisEventCount || 0 : 0;
13351
13618
  const events = analysisOffset > 0 ? allEvents.slice(analysisOffset) : allEvents;
13352
13619
  if (events.length < threshold) {
13353
- if (!isAuto) console.log(chalk23.dim(`caliber: ${events.length}/${threshold} new events since last analysis \u2014 need more`));
13620
+ if (!isAuto)
13621
+ console.log(
13622
+ chalk23.dim(
13623
+ `caliber: ${events.length}/${threshold} new events since last analysis \u2014 need more`
13624
+ )
13625
+ );
13354
13626
  return;
13355
13627
  }
13356
13628
  const existingConfigs = readExistingConfigs(process.cwd());
@@ -13386,7 +13658,11 @@ async function learnFinalizeCommand(options) {
13386
13658
  });
13387
13659
  } else {
13388
13660
  const wasteLabel = waste.totalWasteTokens > 0 ? ` (~${waste.totalWasteTokens.toLocaleString()} wasted tokens captured)` : "";
13389
- console.log(chalk23.dim(`caliber: learned ${result.newItemCount} new pattern${result.newItemCount === 1 ? "" : "s"}${wasteLabel}`));
13661
+ console.log(
13662
+ chalk23.dim(
13663
+ `caliber: learned ${result.newItemCount} new pattern${result.newItemCount === 1 ? "" : "s"}${wasteLabel}`
13664
+ )
13665
+ );
13390
13666
  for (const item of result.newItems) {
13391
13667
  console.log(chalk23.dim(` + ${item.replace(/^- /, "").slice(0, 80)}`));
13392
13668
  }
@@ -13481,12 +13757,20 @@ async function learnFinalizeCommand(options) {
13481
13757
  if (!isIncremental) {
13482
13758
  const staleLearnings = findStaleLearnings(roiStats);
13483
13759
  if (staleLearnings.length > 0 && !isAuto) {
13484
- console.log(chalk23.yellow(`caliber: ${staleLearnings.length} learning${staleLearnings.length === 1 ? "" : "s"} never activated \u2014 run \`${resolveCaliber()} learn list --verbose\` to review`));
13760
+ console.log(
13761
+ chalk23.yellow(
13762
+ `caliber: ${staleLearnings.length} learning${staleLearnings.length === 1 ? "" : "s"} never activated \u2014 run \`${resolveCaliber()} learn list --verbose\` to review`
13763
+ )
13764
+ );
13485
13765
  }
13486
13766
  }
13487
13767
  if (!isAuto && t.estimatedSavingsTokens > 0) {
13488
13768
  const totalLearnings = existingLearnedItems + newLearningsProduced;
13489
- console.log(chalk23.dim(`caliber: ${totalLearnings} learnings active \u2014 est. ~${t.estimatedSavingsTokens.toLocaleString()} tokens saved across ${t.totalSessionsWithLearnings} sessions`));
13769
+ console.log(
13770
+ chalk23.dim(
13771
+ `caliber: ${totalLearnings} learnings active \u2014 est. ~${t.estimatedSavingsTokens.toLocaleString()} tokens saved across ${t.totalSessionsWithLearnings} sessions`
13772
+ )
13773
+ );
13490
13774
  }
13491
13775
  } catch (err) {
13492
13776
  const errorMsg = err instanceof Error ? err.message : String(err);
@@ -13511,7 +13795,7 @@ async function learnFinalizeCommand(options) {
13511
13795
  }
13512
13796
  async function learnInstallCommand() {
13513
13797
  let anyInstalled = false;
13514
- if (fs46.existsSync(".claude")) {
13798
+ if (fs47.existsSync(".claude")) {
13515
13799
  const r = installLearningHooks();
13516
13800
  if (r.installed) {
13517
13801
  console.log(chalk23.green("\u2713") + " Claude Code learning hooks installed");
@@ -13520,7 +13804,7 @@ async function learnInstallCommand() {
13520
13804
  console.log(chalk23.dim(" Claude Code hooks already installed"));
13521
13805
  }
13522
13806
  }
13523
- if (fs46.existsSync(".cursor")) {
13807
+ if (fs47.existsSync(".cursor")) {
13524
13808
  const r = installCursorLearningHooks();
13525
13809
  if (r.installed) {
13526
13810
  console.log(chalk23.green("\u2713") + " Cursor learning hooks installed");
@@ -13529,13 +13813,19 @@ async function learnInstallCommand() {
13529
13813
  console.log(chalk23.dim(" Cursor hooks already installed"));
13530
13814
  }
13531
13815
  }
13532
- if (!fs46.existsSync(".claude") && !fs46.existsSync(".cursor")) {
13816
+ if (!fs47.existsSync(".claude") && !fs47.existsSync(".cursor")) {
13533
13817
  console.log(chalk23.yellow("No .claude/ or .cursor/ directory found."));
13534
- console.log(chalk23.dim(` Run \`${resolveCaliber()} init\` first, or create the directory manually.`));
13818
+ console.log(
13819
+ chalk23.dim(` Run \`${resolveCaliber()} init\` first, or create the directory manually.`)
13820
+ );
13535
13821
  return;
13536
13822
  }
13537
13823
  if (anyInstalled) {
13538
- console.log(chalk23.dim(` Tool usage will be recorded and learnings extracted after \u2265${MIN_EVENTS_FOR_ANALYSIS} events.`));
13824
+ console.log(
13825
+ chalk23.dim(
13826
+ ` Tool usage will be recorded and learnings extracted after \u2265${MIN_EVENTS_FOR_ANALYSIS} events.`
13827
+ )
13828
+ );
13539
13829
  console.log(chalk23.dim(" Learnings written to CALIBER_LEARNINGS.md."));
13540
13830
  }
13541
13831
  }
@@ -13573,7 +13863,9 @@ async function learnStatusCommand() {
13573
13863
  console.log(chalk23.dim("\u2717") + " Cursor hooks " + chalk23.dim("not installed"));
13574
13864
  }
13575
13865
  if (!claudeInstalled && !cursorInstalled) {
13576
- console.log(chalk23.dim(` Run \`${resolveCaliber()} learn install\` to enable session learning.`));
13866
+ console.log(
13867
+ chalk23.dim(` Run \`${resolveCaliber()} learn install\` to enable session learning.`)
13868
+ );
13577
13869
  }
13578
13870
  console.log();
13579
13871
  console.log(`Events recorded: ${chalk23.cyan(String(eventCount))}`);
@@ -13588,7 +13880,7 @@ async function learnStatusCommand() {
13588
13880
  console.log(`Last error: ${chalk23.red(lastError.error)}`);
13589
13881
  console.log(chalk23.dim(` at ${lastError.timestamp}`));
13590
13882
  const logPath = path38.join(getLearningDir(), LEARNING_FINALIZE_LOG);
13591
- if (fs46.existsSync(logPath)) {
13883
+ if (fs47.existsSync(logPath)) {
13592
13884
  console.log(chalk23.dim(` Full log: ${logPath}`));
13593
13885
  }
13594
13886
  }
@@ -13657,7 +13949,11 @@ async function learnListCommand(options) {
13657
13949
  async function learnDeleteCommand(indexStr) {
13658
13950
  const index = parseInt(indexStr, 10);
13659
13951
  if (isNaN(index) || index < 1) {
13660
- console.log(chalk23.red(`Invalid index: "${indexStr}". Use a number from \`${resolveCaliber()} learn list\`.`));
13952
+ console.log(
13953
+ chalk23.red(
13954
+ `Invalid index: "${indexStr}". Use a number from \`${resolveCaliber()} learn list\`.`
13955
+ )
13956
+ );
13661
13957
  return;
13662
13958
  }
13663
13959
  const items = getAllLearnings();
@@ -13668,11 +13964,11 @@ async function learnDeleteCommand(indexStr) {
13668
13964
  }
13669
13965
  const item = items[targetIdx];
13670
13966
  const filePath = item.source === "personal" ? PERSONAL_LEARNINGS_FILE : "CALIBER_LEARNINGS.md";
13671
- if (!fs46.existsSync(filePath)) {
13967
+ if (!fs47.existsSync(filePath)) {
13672
13968
  console.log(chalk23.red("Learnings file not found."));
13673
13969
  return;
13674
13970
  }
13675
- const content = fs46.readFileSync(filePath, "utf-8");
13971
+ const content = fs47.readFileSync(filePath, "utf-8");
13676
13972
  const lines = content.split("\n");
13677
13973
  const bulletsOfSource = items.filter((i) => i.source === item.source);
13678
13974
  const posInFile = bulletsOfSource.indexOf(item);
@@ -13693,9 +13989,9 @@ async function learnDeleteCommand(indexStr) {
13693
13989
  }
13694
13990
  const bulletToRemove = lines[lineToRemove];
13695
13991
  const newLines = lines.filter((_, i) => i !== lineToRemove);
13696
- fs46.writeFileSync(filePath, newLines.join("\n"));
13992
+ fs47.writeFileSync(filePath, newLines.join("\n"));
13697
13993
  if (item.source === "personal") {
13698
- fs46.chmodSync(filePath, 384);
13994
+ fs47.chmodSync(filePath, 384);
13699
13995
  }
13700
13996
  const roiStats = readROIStats();
13701
13997
  const cleanText = bulletToRemove.replace(/^- /, "").replace(/^\*\*\[[^\]]+\]\*\*\s*/, "").trim();
@@ -13868,7 +14164,7 @@ async function insightsCommand(options) {
13868
14164
  }
13869
14165
 
13870
14166
  // src/commands/sources.ts
13871
- import fs47 from "fs";
14167
+ import fs48 from "fs";
13872
14168
  import path39 from "path";
13873
14169
  import chalk25 from "chalk";
13874
14170
  init_resolve_caliber();
@@ -13886,9 +14182,9 @@ async function sourcesListCommand() {
13886
14182
  if (configSources.length > 0) {
13887
14183
  for (const source of configSources) {
13888
14184
  const sourcePath = source.path || source.url || "";
13889
- const exists = source.path ? fs47.existsSync(path39.resolve(dir, source.path)) : false;
14185
+ const exists = source.path ? fs48.existsSync(path39.resolve(dir, source.path)) : false;
13890
14186
  const status = exists ? chalk25.green("reachable") : chalk25.red("not found");
13891
- const hasSummary = source.path && fs47.existsSync(path39.join(path39.resolve(dir, source.path), ".caliber", "summary.json"));
14187
+ const hasSummary = source.path && fs48.existsSync(path39.join(path39.resolve(dir, source.path), ".caliber", "summary.json"));
13892
14188
  console.log(` ${chalk25.bold(source.role || source.type)} ${chalk25.dim(sourcePath)}`);
13893
14189
  console.log(` Type: ${source.type} Status: ${status}${hasSummary ? " " + chalk25.cyan("has summary.json") : ""}`);
13894
14190
  if (source.description) console.log(` ${chalk25.dim(source.description)}`);
@@ -13898,7 +14194,7 @@ async function sourcesListCommand() {
13898
14194
  if (workspaces.length > 0) {
13899
14195
  console.log(chalk25.dim(" Auto-detected workspaces:"));
13900
14196
  for (const ws of workspaces) {
13901
- const exists = fs47.existsSync(path39.resolve(dir, ws));
14197
+ const exists = fs48.existsSync(path39.resolve(dir, ws));
13902
14198
  console.log(` ${exists ? chalk25.green("\u25CF") : chalk25.red("\u25CF")} ${ws}`);
13903
14199
  }
13904
14200
  console.log("");
@@ -13907,7 +14203,7 @@ async function sourcesListCommand() {
13907
14203
  async function sourcesAddCommand(sourcePath) {
13908
14204
  const dir = process.cwd();
13909
14205
  const absPath = path39.resolve(dir, sourcePath);
13910
- if (!fs47.existsSync(absPath)) {
14206
+ if (!fs48.existsSync(absPath)) {
13911
14207
  console.log(chalk25.red(`
13912
14208
  Path not found: ${sourcePath}
13913
14209
  `));
@@ -13970,7 +14266,7 @@ async function sourcesRemoveCommand(name) {
13970
14266
  }
13971
14267
 
13972
14268
  // src/commands/publish.ts
13973
- import fs48 from "fs";
14269
+ import fs49 from "fs";
13974
14270
  import path40 from "path";
13975
14271
  import chalk26 from "chalk";
13976
14272
  import ora7 from "ora";
@@ -14015,11 +14311,11 @@ async function publishCommand() {
14015
14311
  } catch {
14016
14312
  }
14017
14313
  const outputDir = path40.join(dir, ".caliber");
14018
- if (!fs48.existsSync(outputDir)) {
14019
- fs48.mkdirSync(outputDir, { recursive: true });
14314
+ if (!fs49.existsSync(outputDir)) {
14315
+ fs49.mkdirSync(outputDir, { recursive: true });
14020
14316
  }
14021
14317
  const outputPath = path40.join(outputDir, "summary.json");
14022
- fs48.writeFileSync(outputPath, JSON.stringify(summary, null, 2) + "\n", "utf-8");
14318
+ fs49.writeFileSync(outputPath, JSON.stringify(summary, null, 2) + "\n", "utf-8");
14023
14319
  spinner.succeed("Project summary published");
14024
14320
  console.log(` ${chalk26.green("\u2713")} ${path40.relative(dir, outputPath)}`);
14025
14321
  console.log(chalk26.dim("\n Other projects can now reference this repo as a source."));
@@ -14034,7 +14330,7 @@ async function publishCommand() {
14034
14330
 
14035
14331
  // src/commands/bootstrap.ts
14036
14332
  init_builtin_skills();
14037
- import fs49 from "fs";
14333
+ import fs50 from "fs";
14038
14334
  import chalk27 from "chalk";
14039
14335
  var PLATFORM_SKILL_DIRS = {
14040
14336
  claude: ".claude/skills",
@@ -14055,8 +14351,8 @@ async function bootstrapCommand() {
14055
14351
  for (const skill of BUILTIN_SKILLS) {
14056
14352
  const skillDir = `${skillsDir}/${skill.name}`;
14057
14353
  const skillPath = `${skillDir}/SKILL.md`;
14058
- fs49.mkdirSync(skillDir, { recursive: true });
14059
- fs49.writeFileSync(skillPath, buildSkillContent(skill));
14354
+ fs50.mkdirSync(skillDir, { recursive: true });
14355
+ fs50.writeFileSync(skillPath, buildSkillContent(skill));
14060
14356
  written.push(skillPath);
14061
14357
  }
14062
14358
  }
@@ -14073,7 +14369,7 @@ async function bootstrapCommand() {
14073
14369
  }
14074
14370
 
14075
14371
  // src/commands/uninstall.ts
14076
- import fs50 from "fs";
14372
+ import fs51 from "fs";
14077
14373
  import path41 from "path";
14078
14374
  import chalk28 from "chalk";
14079
14375
  import confirm3 from "@inquirer/confirm";
@@ -14090,11 +14386,11 @@ var CURSOR_RULES_DIR = path41.join(".cursor", "rules");
14090
14386
  var CLAUDE_RULES_DIR = path41.join(".claude", "rules");
14091
14387
  function removeCaliberManagedFiles(dir, extension) {
14092
14388
  const removed = [];
14093
- if (!fs50.existsSync(dir)) return removed;
14094
- for (const file of fs50.readdirSync(dir)) {
14389
+ if (!fs51.existsSync(dir)) return removed;
14390
+ for (const file of fs51.readdirSync(dir)) {
14095
14391
  if (file.startsWith(CALIBER_MANAGED_PREFIX) && file.endsWith(extension)) {
14096
14392
  const fullPath = path41.join(dir, file);
14097
- fs50.unlinkSync(fullPath);
14393
+ fs51.unlinkSync(fullPath);
14098
14394
  removed.push(fullPath);
14099
14395
  }
14100
14396
  }
@@ -14103,11 +14399,11 @@ function removeCaliberManagedFiles(dir, extension) {
14103
14399
  function removeBuiltinSkills() {
14104
14400
  const removed = [];
14105
14401
  for (const skillsDir of SKILL_DIRS) {
14106
- if (!fs50.existsSync(skillsDir)) continue;
14402
+ if (!fs51.existsSync(skillsDir)) continue;
14107
14403
  for (const name of BUILTIN_SKILL_NAMES) {
14108
14404
  const skillDir = path41.join(skillsDir, name);
14109
- if (fs50.existsSync(skillDir)) {
14110
- fs50.rmSync(skillDir, { recursive: true });
14405
+ if (fs51.existsSync(skillDir)) {
14406
+ fs51.rmSync(skillDir, { recursive: true });
14111
14407
  removed.push(skillDir);
14112
14408
  }
14113
14409
  }
@@ -14117,15 +14413,15 @@ function removeBuiltinSkills() {
14117
14413
  function stripManagedBlocksFromFiles() {
14118
14414
  const modified = [];
14119
14415
  for (const filePath of MANAGED_DOC_FILES) {
14120
- if (!fs50.existsSync(filePath)) continue;
14121
- const original = fs50.readFileSync(filePath, "utf-8");
14416
+ if (!fs51.existsSync(filePath)) continue;
14417
+ const original = fs51.readFileSync(filePath, "utf-8");
14122
14418
  const stripped = stripManagedBlocks(original);
14123
14419
  if (stripped !== original) {
14124
14420
  const trimmed = stripped.trim();
14125
14421
  if (!trimmed || /^#\s*\S*$/.test(trimmed)) {
14126
- fs50.unlinkSync(filePath);
14422
+ fs51.unlinkSync(filePath);
14127
14423
  } else {
14128
- fs50.writeFileSync(filePath, stripped);
14424
+ fs51.writeFileSync(filePath, stripped);
14129
14425
  }
14130
14426
  modified.push(filePath);
14131
14427
  }
@@ -14133,8 +14429,8 @@ function stripManagedBlocksFromFiles() {
14133
14429
  return modified;
14134
14430
  }
14135
14431
  function removeDirectory(dir) {
14136
- if (!fs50.existsSync(dir)) return false;
14137
- fs50.rmSync(dir, { recursive: true });
14432
+ if (!fs51.existsSync(dir)) return false;
14433
+ fs51.rmSync(dir, { recursive: true });
14138
14434
  return true;
14139
14435
  }
14140
14436
  async function uninstallCommand(options) {
@@ -14206,8 +14502,8 @@ async function uninstallCommand(options) {
14206
14502
  console.log(` ${chalk28.red("\u2717")} ${skill}/`);
14207
14503
  }
14208
14504
  if (removedSkills.length > 0) actions.push("builtin skills");
14209
- if (fs50.existsSync("CALIBER_LEARNINGS.md")) {
14210
- fs50.unlinkSync("CALIBER_LEARNINGS.md");
14505
+ if (fs51.existsSync("CALIBER_LEARNINGS.md")) {
14506
+ fs51.unlinkSync("CALIBER_LEARNINGS.md");
14211
14507
  console.log(` ${chalk28.red("\u2717")} CALIBER_LEARNINGS.md`);
14212
14508
  actions.push("learnings file");
14213
14509
  }
@@ -14221,18 +14517,18 @@ async function uninstallCommand(options) {
14221
14517
  }
14222
14518
  trackUninstallExecuted();
14223
14519
  const configPath = getConfigFilePath();
14224
- if (fs50.existsSync(configPath)) {
14520
+ if (fs51.existsSync(configPath)) {
14225
14521
  console.log("");
14226
14522
  const removeConfig = options.force || await confirm3({
14227
14523
  message: `Remove global config (~/.caliber/config.json)? This affects all projects.`
14228
14524
  });
14229
14525
  if (removeConfig) {
14230
- fs50.unlinkSync(configPath);
14526
+ fs51.unlinkSync(configPath);
14231
14527
  console.log(` ${chalk28.red("\u2717")} ${configPath}`);
14232
14528
  const configDir = path41.dirname(configPath);
14233
14529
  try {
14234
- const remaining = fs50.readdirSync(configDir);
14235
- if (remaining.length === 0) fs50.rmdirSync(configDir);
14530
+ const remaining = fs51.readdirSync(configDir);
14531
+ if (remaining.length === 0) fs51.rmdirSync(configDir);
14236
14532
  } catch {
14237
14533
  }
14238
14534
  }
@@ -14244,7 +14540,7 @@ async function uninstallCommand(options) {
14244
14540
 
14245
14541
  // src/cli.ts
14246
14542
  var __dirname = path42.dirname(fileURLToPath(import.meta.url));
14247
- var pkg = JSON.parse(fs51.readFileSync(path42.resolve(__dirname, "..", "package.json"), "utf-8"));
14543
+ var pkg = JSON.parse(fs52.readFileSync(path42.resolve(__dirname, "..", "package.json"), "utf-8"));
14248
14544
  var program = new Command();
14249
14545
  var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
14250
14546
  program.name(process.env.CALIBER_LOCAL ? "caloc" : "caliber").description("AI context infrastructure for coding agents").version(displayVersion).option("--no-traces", "Disable anonymous telemetry for this run");
@@ -14355,15 +14651,15 @@ learn.command("delete <index>").description("Delete a learning by its index numb
14355
14651
  learn.command("add <content>").description("Add a learning directly (used by agent skills)").option("--personal", "Save as a personal learning instead of project-level").action(tracked("learn:add", learnAddCommand));
14356
14652
 
14357
14653
  // src/utils/version-check.ts
14358
- import fs52 from "fs";
14654
+ import fs53 from "fs";
14359
14655
  import path43 from "path";
14360
14656
  import { fileURLToPath as fileURLToPath2 } from "url";
14361
- import { execSync as execSync16, execFileSync as execFileSync3 } from "child_process";
14657
+ import { execSync as execSync16, execFileSync as execFileSync5 } from "child_process";
14362
14658
  import chalk29 from "chalk";
14363
14659
  import ora8 from "ora";
14364
14660
  import confirm4 from "@inquirer/confirm";
14365
14661
  var __dirname_vc = path43.dirname(fileURLToPath2(import.meta.url));
14366
- var pkg2 = JSON.parse(fs52.readFileSync(path43.resolve(__dirname_vc, "..", "package.json"), "utf-8"));
14662
+ var pkg2 = JSON.parse(fs53.readFileSync(path43.resolve(__dirname_vc, "..", "package.json"), "utf-8"));
14367
14663
  function getChannel(version) {
14368
14664
  const match = version.match(/-(dev|next)\./);
14369
14665
  return match ? match[1] : "latest";
@@ -14391,7 +14687,7 @@ function getInstalledVersion() {
14391
14687
  stdio: ["pipe", "pipe", "pipe"]
14392
14688
  }).trim();
14393
14689
  const pkgPath = path43.join(globalRoot, "@rely-ai", "caliber", "package.json");
14394
- return JSON.parse(fs52.readFileSync(pkgPath, "utf-8")).version;
14690
+ return JSON.parse(fs53.readFileSync(pkgPath, "utf-8")).version;
14395
14691
  } catch {
14396
14692
  return null;
14397
14693
  }
@@ -14439,7 +14735,7 @@ Update available: ${current} -> ${latest}`));
14439
14735
  if (!/^[\w.-]+$/.test(tag)) return;
14440
14736
  const spinner = ora8("Updating caliber...").start();
14441
14737
  try {
14442
- execFileSync3("npm", ["install", "-g", `@rely-ai/caliber@${tag}`], {
14738
+ execFileSync5("npm", ["install", "-g", `@rely-ai/caliber@${tag}`], {
14443
14739
  stdio: "pipe",
14444
14740
  timeout: 12e4,
14445
14741
  env: { ...process.env, npm_config_fund: "false", npm_config_audit: "false" }
@@ -14458,7 +14754,7 @@ Update available: ${current} -> ${latest}`));
14458
14754
  console.log(chalk29.dim(`
14459
14755
  Restarting: caliber ${args.join(" ")}
14460
14756
  `));
14461
- execFileSync3("caliber", args, {
14757
+ execFileSync5("caliber", args, {
14462
14758
  stdio: "inherit",
14463
14759
  env: { ...process.env, CALIBER_SKIP_UPDATE_CHECK: "1" }
14464
14760
  });