@rely-ai/caliber 1.22.1 → 1.23.0-dev.1773793983

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 +662 -534
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -25,9 +25,11 @@ var config_exports = {};
25
25
  __export(config_exports, {
26
26
  DEFAULT_FAST_MODELS: () => DEFAULT_FAST_MODELS,
27
27
  DEFAULT_MODELS: () => DEFAULT_MODELS,
28
+ MODEL_CONTEXT_WINDOWS: () => MODEL_CONTEXT_WINDOWS,
28
29
  getConfigFilePath: () => getConfigFilePath,
29
30
  getDisplayModel: () => getDisplayModel,
30
31
  getFastModel: () => getFastModel,
32
+ getMaxPromptTokens: () => getMaxPromptTokens,
31
33
  loadConfig: () => loadConfig,
32
34
  readConfigFile: () => readConfigFile,
33
35
  resolveFromEnv: () => resolveFromEnv,
@@ -36,6 +38,13 @@ __export(config_exports, {
36
38
  import fs4 from "fs";
37
39
  import path4 from "path";
38
40
  import os from "os";
41
+ function getMaxPromptTokens() {
42
+ const config = loadConfig();
43
+ const model = process.env.CALIBER_MODEL || config?.model;
44
+ const contextWindow = model ? MODEL_CONTEXT_WINDOWS[model] ?? DEFAULT_CONTEXT_WINDOW : DEFAULT_CONTEXT_WINDOW;
45
+ const budget = Math.floor(contextWindow * INPUT_BUDGET_FRACTION);
46
+ return Math.max(MIN_PROMPT_TOKENS, Math.min(budget, MAX_PROMPT_TOKENS_CAP));
47
+ }
39
48
  function loadConfig() {
40
49
  const envConfig = resolveFromEnv();
41
50
  if (envConfig) return envConfig;
@@ -123,7 +132,7 @@ function getFastModel() {
123
132
  if (provider) return DEFAULT_FAST_MODELS[provider];
124
133
  return void 0;
125
134
  }
126
- var CONFIG_DIR, CONFIG_FILE, DEFAULT_MODELS, DEFAULT_FAST_MODELS;
135
+ var CONFIG_DIR, CONFIG_FILE, DEFAULT_MODELS, MODEL_CONTEXT_WINDOWS, DEFAULT_CONTEXT_WINDOW, INPUT_BUDGET_FRACTION, MAX_PROMPT_TOKENS_CAP, MIN_PROMPT_TOKENS, DEFAULT_FAST_MODELS;
127
136
  var init_config = __esm({
128
137
  "src/llm/config.ts"() {
129
138
  "use strict";
@@ -136,6 +145,21 @@ var init_config = __esm({
136
145
  cursor: "sonnet-4.6",
137
146
  "claude-cli": "default"
138
147
  };
148
+ MODEL_CONTEXT_WINDOWS = {
149
+ "claude-sonnet-4-6": 2e5,
150
+ "claude-opus-4-6": 2e5,
151
+ "claude-haiku-4-5-20251001": 2e5,
152
+ "claude-sonnet-4-5-20250514": 2e5,
153
+ "gpt-4.1": 1e6,
154
+ "gpt-4.1-mini": 1e6,
155
+ "gpt-4o": 128e3,
156
+ "gpt-4o-mini": 128e3,
157
+ "sonnet-4.6": 2e5
158
+ };
159
+ DEFAULT_CONTEXT_WINDOW = 2e5;
160
+ INPUT_BUDGET_FRACTION = 0.6;
161
+ MAX_PROMPT_TOKENS_CAP = 3e5;
162
+ MIN_PROMPT_TOKENS = 3e4;
139
163
  DEFAULT_FAST_MODELS = {
140
164
  anthropic: "claude-haiku-4-5-20251001",
141
165
  vertex: "claude-haiku-4-5-20251001",
@@ -158,17 +182,17 @@ __export(constants_exports, {
158
182
  LEARNING_STATE_FILE: () => LEARNING_STATE_FILE,
159
183
  MANIFEST_FILE: () => MANIFEST_FILE
160
184
  });
161
- import path9 from "path";
185
+ import path10 from "path";
162
186
  import os3 from "os";
163
187
  var AUTH_DIR, CALIBER_DIR, MANIFEST_FILE, BACKUPS_DIR, LEARNING_DIR, LEARNING_SESSION_FILE, LEARNING_STATE_FILE, LEARNING_MAX_EVENTS, LEARNING_ROI_FILE;
164
188
  var init_constants = __esm({
165
189
  "src/constants.ts"() {
166
190
  "use strict";
167
- AUTH_DIR = path9.join(os3.homedir(), ".caliber");
191
+ AUTH_DIR = path10.join(os3.homedir(), ".caliber");
168
192
  CALIBER_DIR = ".caliber";
169
- MANIFEST_FILE = path9.join(CALIBER_DIR, "manifest.json");
170
- BACKUPS_DIR = path9.join(CALIBER_DIR, "backups");
171
- LEARNING_DIR = path9.join(CALIBER_DIR, "learning");
193
+ MANIFEST_FILE = path10.join(CALIBER_DIR, "manifest.json");
194
+ BACKUPS_DIR = path10.join(CALIBER_DIR, "backups");
195
+ LEARNING_DIR = path10.join(CALIBER_DIR, "learning");
172
196
  LEARNING_SESSION_FILE = "current-session.jsonl";
173
197
  LEARNING_STATE_FILE = "state.json";
174
198
  LEARNING_MAX_EVENTS = 500;
@@ -183,13 +207,13 @@ __export(lock_exports, {
183
207
  isCaliberRunning: () => isCaliberRunning,
184
208
  releaseLock: () => releaseLock
185
209
  });
186
- import fs29 from "fs";
187
- import path23 from "path";
210
+ import fs30 from "fs";
211
+ import path24 from "path";
188
212
  import os6 from "os";
189
213
  function isCaliberRunning() {
190
214
  try {
191
- if (!fs29.existsSync(LOCK_FILE)) return false;
192
- const raw = fs29.readFileSync(LOCK_FILE, "utf-8").trim();
215
+ if (!fs30.existsSync(LOCK_FILE)) return false;
216
+ const raw = fs30.readFileSync(LOCK_FILE, "utf-8").trim();
193
217
  const { pid, ts } = JSON.parse(raw);
194
218
  if (Date.now() - ts > STALE_MS) return false;
195
219
  try {
@@ -204,13 +228,13 @@ function isCaliberRunning() {
204
228
  }
205
229
  function acquireLock() {
206
230
  try {
207
- fs29.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
231
+ fs30.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
208
232
  } catch {
209
233
  }
210
234
  }
211
235
  function releaseLock() {
212
236
  try {
213
- if (fs29.existsSync(LOCK_FILE)) fs29.unlinkSync(LOCK_FILE);
237
+ if (fs30.existsSync(LOCK_FILE)) fs30.unlinkSync(LOCK_FILE);
214
238
  } catch {
215
239
  }
216
240
  }
@@ -218,28 +242,28 @@ var LOCK_FILE, STALE_MS;
218
242
  var init_lock = __esm({
219
243
  "src/lib/lock.ts"() {
220
244
  "use strict";
221
- LOCK_FILE = path23.join(os6.tmpdir(), ".caliber.lock");
245
+ LOCK_FILE = path24.join(os6.tmpdir(), ".caliber.lock");
222
246
  STALE_MS = 10 * 60 * 1e3;
223
247
  }
224
248
  });
225
249
 
226
250
  // src/cli.ts
227
251
  import { Command } from "commander";
228
- import fs34 from "fs";
229
- import path27 from "path";
252
+ import fs35 from "fs";
253
+ import path28 from "path";
230
254
  import { fileURLToPath } from "url";
231
255
 
232
256
  // src/commands/init.ts
233
- import path20 from "path";
257
+ import path21 from "path";
234
258
  import chalk11 from "chalk";
235
259
  import ora3 from "ora";
236
260
  import select5 from "@inquirer/select";
237
261
  import checkbox from "@inquirer/checkbox";
238
- import fs25 from "fs";
262
+ import fs26 from "fs";
239
263
 
240
264
  // src/fingerprint/index.ts
241
- import fs6 from "fs";
242
- import path5 from "path";
265
+ import fs7 from "fs";
266
+ import path6 from "path";
243
267
 
244
268
  // src/fingerprint/git.ts
245
269
  import { execSync } from "child_process";
@@ -292,15 +316,23 @@ function getFileTree(dir, maxDepth = 3) {
292
316
  for (const e of entries) {
293
317
  (e.isDir ? dirs : files).push(e);
294
318
  }
295
- for (const d of dirs) {
296
- const prefix = d.relPath;
297
- let maxChildMtime = d.mtime;
298
- for (const f of files) {
299
- if (f.relPath.startsWith(prefix) && f.mtime > maxChildMtime) {
300
- maxChildMtime = f.mtime;
319
+ const dirMaxMtime = /* @__PURE__ */ new Map();
320
+ for (const d of dirs) dirMaxMtime.set(d.relPath, d.mtime);
321
+ for (const f of files) {
322
+ let remaining = f.relPath;
323
+ while (true) {
324
+ const lastSlash = remaining.lastIndexOf("/");
325
+ if (lastSlash === -1) break;
326
+ const dirPrefix = remaining.slice(0, lastSlash + 1);
327
+ const current = dirMaxMtime.get(dirPrefix);
328
+ if (current !== void 0 && f.mtime > current) {
329
+ dirMaxMtime.set(dirPrefix, f.mtime);
301
330
  }
331
+ remaining = remaining.slice(0, lastSlash);
302
332
  }
303
- d.mtime = maxChildMtime;
333
+ }
334
+ for (const d of dirs) {
335
+ d.mtime = dirMaxMtime.get(d.relPath) ?? d.mtime;
304
336
  }
305
337
  dirs.sort((a, b) => b.mtime - a.mtime);
306
338
  files.sort((a, b) => b.mtime - a.mtime);
@@ -2212,12 +2244,112 @@ async function detectProjectStack(fileTree, suffixCounts) {
2212
2244
 
2213
2245
  // src/fingerprint/index.ts
2214
2246
  init_config();
2247
+
2248
+ // src/fingerprint/cache.ts
2249
+ import fs6 from "fs";
2250
+ import path5 from "path";
2251
+ import crypto from "crypto";
2252
+ import { execSync as execSync5 } from "child_process";
2253
+ var CACHE_VERSION = 1;
2254
+ var CACHE_DIR = ".caliber/cache";
2255
+ var CACHE_FILE = "fingerprint.json";
2256
+ function getCachePath(dir) {
2257
+ return path5.join(dir, CACHE_DIR, CACHE_FILE);
2258
+ }
2259
+ function getGitHead(dir) {
2260
+ try {
2261
+ return execSync5("git rev-parse HEAD", {
2262
+ cwd: dir,
2263
+ encoding: "utf-8",
2264
+ stdio: ["pipe", "pipe", "pipe"],
2265
+ timeout: 3e3
2266
+ }).trim();
2267
+ } catch {
2268
+ return "";
2269
+ }
2270
+ }
2271
+ function getDirtySignature(dir) {
2272
+ try {
2273
+ const output = execSync5("git diff --name-only HEAD", {
2274
+ cwd: dir,
2275
+ encoding: "utf-8",
2276
+ stdio: ["pipe", "pipe", "pipe"],
2277
+ timeout: 3e3
2278
+ }).trim();
2279
+ return output.split("\n").slice(0, 100).join("\n");
2280
+ } catch {
2281
+ return "";
2282
+ }
2283
+ }
2284
+ function computeTreeSignature(fileTree, dir) {
2285
+ const hash = crypto.createHash("sha256");
2286
+ hash.update(fileTree.join("\0"));
2287
+ hash.update("\n");
2288
+ hash.update(getDirtySignature(dir));
2289
+ return hash.digest("hex").slice(0, 16);
2290
+ }
2291
+ function loadFingerprintCache(dir, fileTree) {
2292
+ const cachePath = getCachePath(dir);
2293
+ try {
2294
+ if (!fs6.existsSync(cachePath)) return null;
2295
+ const raw = fs6.readFileSync(cachePath, "utf-8");
2296
+ const cache = JSON.parse(raw);
2297
+ if (cache.version !== CACHE_VERSION) return null;
2298
+ const currentHead = getGitHead(dir);
2299
+ if (currentHead && cache.gitHead !== currentHead) return null;
2300
+ const currentSig = computeTreeSignature(fileTree, dir);
2301
+ if (cache.treeSignature !== currentSig) return null;
2302
+ return {
2303
+ codeAnalysis: cache.codeAnalysis,
2304
+ languages: cache.languages,
2305
+ frameworks: cache.frameworks,
2306
+ tools: cache.tools
2307
+ };
2308
+ } catch {
2309
+ return null;
2310
+ }
2311
+ }
2312
+ function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks, tools) {
2313
+ const cachePath = getCachePath(dir);
2314
+ try {
2315
+ const cacheDir = path5.dirname(cachePath);
2316
+ if (!fs6.existsSync(cacheDir)) {
2317
+ fs6.mkdirSync(cacheDir, { recursive: true });
2318
+ }
2319
+ const cache = {
2320
+ version: CACHE_VERSION,
2321
+ gitHead: getGitHead(dir),
2322
+ treeSignature: computeTreeSignature(fileTree, dir),
2323
+ codeAnalysis,
2324
+ languages,
2325
+ frameworks,
2326
+ tools
2327
+ };
2328
+ fs6.writeFileSync(cachePath, JSON.stringify(cache), "utf-8");
2329
+ } catch {
2330
+ }
2331
+ }
2332
+
2333
+ // src/fingerprint/index.ts
2215
2334
  async function collectFingerprint(dir) {
2216
2335
  const gitRemoteUrl = getGitRemoteUrl();
2217
2336
  const fileTree = getFileTree(dir);
2218
2337
  const existingConfigs = readExistingConfigs(dir);
2219
- const codeAnalysis = analyzeCode(dir);
2220
2338
  const packageName = readPackageName(dir);
2339
+ const cached = loadFingerprintCache(dir, fileTree);
2340
+ if (cached) {
2341
+ return {
2342
+ gitRemoteUrl,
2343
+ packageName,
2344
+ languages: cached.languages,
2345
+ frameworks: cached.frameworks,
2346
+ tools: cached.tools,
2347
+ fileTree,
2348
+ existingConfigs,
2349
+ codeAnalysis: cached.codeAnalysis
2350
+ };
2351
+ }
2352
+ const codeAnalysis = analyzeCode(dir);
2221
2353
  const fingerprint = {
2222
2354
  gitRemoteUrl,
2223
2355
  packageName,
@@ -2229,13 +2361,21 @@ async function collectFingerprint(dir) {
2229
2361
  codeAnalysis
2230
2362
  };
2231
2363
  await enrichWithLLM(fingerprint);
2364
+ saveFingerprintCache(
2365
+ dir,
2366
+ fileTree,
2367
+ codeAnalysis,
2368
+ fingerprint.languages,
2369
+ fingerprint.frameworks,
2370
+ fingerprint.tools
2371
+ );
2232
2372
  return fingerprint;
2233
2373
  }
2234
2374
  function readPackageName(dir) {
2235
2375
  try {
2236
- const pkgPath = path5.join(dir, "package.json");
2237
- if (!fs6.existsSync(pkgPath)) return void 0;
2238
- const pkg3 = JSON.parse(fs6.readFileSync(pkgPath, "utf-8"));
2376
+ const pkgPath = path6.join(dir, "package.json");
2377
+ if (!fs7.existsSync(pkgPath)) return void 0;
2378
+ const pkg3 = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
2239
2379
  return pkg3.name;
2240
2380
  } catch {
2241
2381
  return void 0;
@@ -2249,7 +2389,7 @@ async function enrichWithLLM(fingerprint) {
2249
2389
  const suffixCounts = {};
2250
2390
  for (const entry of fingerprint.fileTree) {
2251
2391
  if (entry.endsWith("/")) continue;
2252
- const ext = path5.extname(entry).toLowerCase();
2392
+ const ext = path6.extname(entry).toLowerCase();
2253
2393
  if (ext) {
2254
2394
  suffixCounts[ext] = (suffixCounts[ext] || 0) + 1;
2255
2395
  }
@@ -2268,15 +2408,15 @@ init_config();
2268
2408
  // src/utils/dependencies.ts
2269
2409
  import { readFileSync } from "fs";
2270
2410
  import { join } from "path";
2271
- function readFileOrNull(path29) {
2411
+ function readFileOrNull(path30) {
2272
2412
  try {
2273
- return readFileSync(path29, "utf-8");
2413
+ return readFileSync(path30, "utf-8");
2274
2414
  } catch {
2275
2415
  return null;
2276
2416
  }
2277
2417
  }
2278
- function readJsonOrNull(path29) {
2279
- const content = readFileOrNull(path29);
2418
+ function readJsonOrNull(path30) {
2419
+ const content = readFileOrNull(path30);
2280
2420
  if (!content) return null;
2281
2421
  try {
2282
2422
  return JSON.parse(content);
@@ -2500,15 +2640,14 @@ Generate the skill content following the instructions in the system prompt.`;
2500
2640
  content
2501
2641
  };
2502
2642
  }
2503
- async function generateCore(fingerprint, targetAgent, prompt, callbacks, failingChecks, currentScore, passingChecks) {
2643
+ async function streamGeneration(config) {
2504
2644
  const provider = getProvider();
2505
- const userMessage = buildGeneratePrompt(fingerprint, targetAgent, prompt, failingChecks, currentScore, passingChecks);
2506
2645
  let attempt = 0;
2507
2646
  const attemptGeneration = async () => {
2508
2647
  attempt++;
2509
2648
  const maxTokensForAttempt = Math.min(
2510
- CORE_MAX_TOKENS + attempt * 8e3,
2511
- GENERATION_MAX_TOKENS
2649
+ config.baseMaxTokens + attempt * config.tokenIncrement,
2650
+ config.maxTokensCap
2512
2651
  );
2513
2652
  return new Promise((resolve2) => {
2514
2653
  let preJsonBuffer = "";
@@ -2518,8 +2657,8 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
2518
2657
  let stopReason = null;
2519
2658
  provider.stream(
2520
2659
  {
2521
- system: CORE_GENERATION_PROMPT,
2522
- prompt: userMessage,
2660
+ system: config.systemPrompt,
2661
+ prompt: config.userMessage,
2523
2662
  maxTokens: maxTokensForAttempt
2524
2663
  },
2525
2664
  {
@@ -2532,7 +2671,7 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
2532
2671
  const trimmed = completedLines[i].trim();
2533
2672
  if (trimmed.startsWith("STATUS:")) {
2534
2673
  const status = trimmed.slice(7).trim();
2535
- if (status && callbacks) callbacks.onStatus(status);
2674
+ if (status && config.callbacks) config.callbacks.onStatus(status);
2536
2675
  }
2537
2676
  }
2538
2677
  sentStatuses = completedLines.length;
@@ -2562,7 +2701,7 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
2562
2701
  } catch {
2563
2702
  }
2564
2703
  if (!setup && stopReason === "max_tokens" && attempt < MAX_RETRIES2) {
2565
- if (callbacks) callbacks.onStatus("Output was truncated, retrying with higher token limit...");
2704
+ if (config.callbacks) config.callbacks.onStatus("Output was truncated, retrying with higher token limit...");
2566
2705
  setTimeout(() => attemptGeneration().then(resolve2), 1e3);
2567
2706
  return;
2568
2707
  }
@@ -2572,7 +2711,7 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
2572
2711
  explanation = explainMatch[1].trim();
2573
2712
  }
2574
2713
  if (setup) {
2575
- if (callbacks) callbacks.onComplete(setup, explanation);
2714
+ if (config.callbacks) config.callbacks.onComplete(setup, explanation);
2576
2715
  resolve2({ setup, explanation, stopReason: stopReason ?? void 0 });
2577
2716
  } else {
2578
2717
  resolve2({ setup: null, explanation, raw: preJsonBuffer, stopReason: stopReason ?? void 0 });
@@ -2580,117 +2719,43 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
2580
2719
  },
2581
2720
  onError: (error) => {
2582
2721
  if (isTransientError2(error) && attempt < MAX_RETRIES2) {
2583
- if (callbacks) callbacks.onStatus("Connection interrupted, retrying...");
2722
+ if (config.callbacks) config.callbacks.onStatus("Connection interrupted, retrying...");
2584
2723
  setTimeout(() => attemptGeneration().then(resolve2), 2e3);
2585
2724
  return;
2586
2725
  }
2587
- if (callbacks) callbacks.onError(error.message);
2726
+ if (config.callbacks) config.callbacks.onError(error.message);
2588
2727
  resolve2({ setup: null, raw: error.message, stopReason: "error" });
2589
2728
  }
2590
2729
  }
2591
2730
  ).catch((error) => {
2592
- if (callbacks) callbacks.onError(error.message);
2731
+ if (config.callbacks) config.callbacks.onError(error.message);
2593
2732
  resolve2({ setup: null, raw: error.message, stopReason: "error" });
2594
2733
  });
2595
2734
  });
2596
2735
  };
2597
2736
  return attemptGeneration();
2598
2737
  }
2738
+ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failingChecks, currentScore, passingChecks) {
2739
+ const userMessage = buildGeneratePrompt(fingerprint, targetAgent, prompt, failingChecks, currentScore, passingChecks);
2740
+ return streamGeneration({
2741
+ systemPrompt: CORE_GENERATION_PROMPT,
2742
+ userMessage,
2743
+ baseMaxTokens: CORE_MAX_TOKENS,
2744
+ tokenIncrement: 8e3,
2745
+ maxTokensCap: GENERATION_MAX_TOKENS,
2746
+ callbacks
2747
+ });
2748
+ }
2599
2749
  async function generateMonolithic(fingerprint, targetAgent, prompt, callbacks, failingChecks, currentScore, passingChecks) {
2600
- const provider = getProvider();
2601
2750
  const userMessage = buildGeneratePrompt(fingerprint, targetAgent, prompt, failingChecks, currentScore, passingChecks);
2602
- let attempt = 0;
2603
- const attemptGeneration = async () => {
2604
- attempt++;
2605
- const maxTokensForAttempt = Math.min(
2606
- GENERATION_MAX_TOKENS + attempt * 16e3,
2607
- MODEL_MAX_OUTPUT_TOKENS
2608
- );
2609
- return new Promise((resolve2) => {
2610
- let preJsonBuffer = "";
2611
- let jsonContent = "";
2612
- let inJson = false;
2613
- let sentStatuses = 0;
2614
- let stopReason = null;
2615
- provider.stream(
2616
- {
2617
- system: GENERATION_SYSTEM_PROMPT,
2618
- prompt: userMessage,
2619
- maxTokens: maxTokensForAttempt
2620
- },
2621
- {
2622
- onText: (text) => {
2623
- if (!inJson) {
2624
- preJsonBuffer += text;
2625
- const lines = preJsonBuffer.split("\n");
2626
- const completedLines = lines.slice(0, -1);
2627
- for (let i = sentStatuses; i < completedLines.length; i++) {
2628
- const trimmed = completedLines[i].trim();
2629
- if (trimmed.startsWith("STATUS:")) {
2630
- const status = trimmed.slice(7).trim();
2631
- if (status && callbacks) callbacks.onStatus(status);
2632
- }
2633
- }
2634
- sentStatuses = completedLines.length;
2635
- const jsonStartMatch = preJsonBuffer.match(/(?:^|\n)\s*(?:```json\s*\n\s*)?\{(?=\s*")/);
2636
- if (jsonStartMatch) {
2637
- const matchIndex = preJsonBuffer.indexOf("{", jsonStartMatch.index);
2638
- inJson = true;
2639
- jsonContent = preJsonBuffer.slice(matchIndex);
2640
- }
2641
- } else {
2642
- jsonContent += text;
2643
- }
2644
- },
2645
- onEnd: (meta) => {
2646
- stopReason = meta?.stopReason ?? null;
2647
- let setup = null;
2648
- let jsonToParse = (jsonContent || preJsonBuffer).replace(/```\s*$/g, "").trim();
2649
- if (!jsonContent && preJsonBuffer) {
2650
- const fallbackMatch = preJsonBuffer.match(/(?:^|\n)\s*(?:```json\s*\n\s*)?\{(?=\s*")/);
2651
- if (fallbackMatch) {
2652
- const matchIndex = preJsonBuffer.indexOf("{", fallbackMatch.index);
2653
- jsonToParse = preJsonBuffer.slice(matchIndex).replace(/```\s*$/g, "").trim();
2654
- }
2655
- }
2656
- try {
2657
- setup = JSON.parse(jsonToParse);
2658
- } catch {
2659
- }
2660
- if (!setup && stopReason === "max_tokens" && attempt < MAX_RETRIES2) {
2661
- if (callbacks) callbacks.onStatus("Output was truncated, retrying with higher token limit...");
2662
- setTimeout(() => attemptGeneration().then(resolve2), 1e3);
2663
- return;
2664
- }
2665
- let explanation;
2666
- const explainMatch = preJsonBuffer.match(/EXPLAIN:\s*\n([\s\S]*?)(?=\n\s*(`{3}|\{))/);
2667
- if (explainMatch) {
2668
- explanation = explainMatch[1].trim();
2669
- }
2670
- if (setup) {
2671
- if (callbacks) callbacks.onComplete(setup, explanation);
2672
- resolve2({ setup, explanation, stopReason: stopReason ?? void 0 });
2673
- } else {
2674
- resolve2({ setup: null, explanation, raw: preJsonBuffer, stopReason: stopReason ?? void 0 });
2675
- }
2676
- },
2677
- onError: (error) => {
2678
- if (isTransientError2(error) && attempt < MAX_RETRIES2) {
2679
- if (callbacks) callbacks.onStatus("Connection interrupted, retrying...");
2680
- setTimeout(() => attemptGeneration().then(resolve2), 2e3);
2681
- return;
2682
- }
2683
- if (callbacks) callbacks.onError(error.message);
2684
- resolve2({ setup: null, raw: error.message, stopReason: "error" });
2685
- }
2686
- }
2687
- ).catch((error) => {
2688
- if (callbacks) callbacks.onError(error.message);
2689
- resolve2({ setup: null, raw: error.message, stopReason: "error" });
2690
- });
2691
- });
2692
- };
2693
- return attemptGeneration();
2751
+ return streamGeneration({
2752
+ systemPrompt: GENERATION_SYSTEM_PROMPT,
2753
+ userMessage,
2754
+ baseMaxTokens: GENERATION_MAX_TOKENS,
2755
+ tokenIncrement: 16e3,
2756
+ maxTokensCap: MODEL_MAX_OUTPUT_TOKENS,
2757
+ callbacks
2758
+ });
2694
2759
  }
2695
2760
  async function generateSkillsForSetup(setup, fingerprint, targetAgent, onStatus) {
2696
2761
  const skillTopics = collectSkillTopics(setup, targetAgent, fingerprint);
@@ -2723,7 +2788,6 @@ async function generateSkillsForSetup(setup, fingerprint, targetAgent, onStatus)
2723
2788
  if (failed > 0) onStatus?.(`${succeeded} generated, ${failed} failed`);
2724
2789
  return succeeded;
2725
2790
  }
2726
- var MAX_PROMPT_TOKENS = 12e4;
2727
2791
  var LIMITS = {
2728
2792
  FILE_TREE_ENTRIES: 500,
2729
2793
  EXISTING_CONFIG_CHARS: 8e3,
@@ -2738,6 +2802,7 @@ function truncate(text, maxChars) {
2738
2802
  }
2739
2803
  function sampleFileTree(fileTree, codeAnalysisPaths, limit) {
2740
2804
  if (fileTree.length <= limit) return fileTree;
2805
+ const fileTreeSet = new Set(fileTree);
2741
2806
  const dirs = [];
2742
2807
  const rootFiles = [];
2743
2808
  const nestedFiles = [];
@@ -2763,7 +2828,7 @@ function sampleFileTree(fileTree, codeAnalysisPaths, limit) {
2763
2828
  for (const f of rootFiles.slice(0, 50)) add(f);
2764
2829
  for (const p of codeAnalysisPaths) {
2765
2830
  if (result.length >= limit) break;
2766
- if (fileTree.includes(p)) add(p);
2831
+ if (fileTreeSet.has(p)) add(p);
2767
2832
  }
2768
2833
  for (const f of nestedFiles) {
2769
2834
  if (result.length >= limit) break;
@@ -2887,20 +2952,25 @@ User instructions: ${prompt}`);
2887
2952
  if (fingerprint.codeAnalysis) {
2888
2953
  const ca = fingerprint.codeAnalysis;
2889
2954
  const basePrompt = parts.join("\n");
2955
+ const maxPromptTokens = getMaxPromptTokens();
2890
2956
  const baseTokens = estimateTokens(basePrompt);
2891
- const tokenBudgetForCode = Math.max(0, MAX_PROMPT_TOKENS - baseTokens);
2957
+ const tokenBudgetForCode = Math.max(0, maxPromptTokens - baseTokens);
2892
2958
  const codeLines = [];
2893
2959
  let codeChars = 0;
2894
- codeLines.push("Study these files to extract patterns for skills. Use the exact code patterns you see here.\n");
2960
+ const introLine = "Study these files to extract patterns for skills. Use the exact code patterns you see here.\n";
2961
+ codeLines.push(introLine);
2962
+ let runningCodeLen = introLine.length;
2895
2963
  const sortedFiles = [...ca.files].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
2896
2964
  let includedFiles = 0;
2897
2965
  for (const f of sortedFiles) {
2898
2966
  const entry = `[${f.path}]
2899
2967
  ${f.content}
2900
2968
  `;
2901
- if (estimateTokens(codeLines.join("\n") + entry) > tokenBudgetForCode && includedFiles > 0) break;
2969
+ const projectedLen = runningCodeLen + 1 + entry.length;
2970
+ if (Math.ceil(projectedLen / 4) > tokenBudgetForCode && includedFiles > 0) break;
2902
2971
  codeLines.push(entry);
2903
2972
  codeChars += f.content.length;
2973
+ runningCodeLen = projectedLen;
2904
2974
  includedFiles++;
2905
2975
  }
2906
2976
  const includedTokens = Math.ceil(codeChars / 4);
@@ -2971,20 +3041,20 @@ Return the complete updated AgentSetup JSON incorporating the user's changes. Re
2971
3041
  }
2972
3042
 
2973
3043
  // src/writers/index.ts
2974
- import fs12 from "fs";
3044
+ import fs13 from "fs";
2975
3045
 
2976
3046
  // src/writers/claude/index.ts
2977
- import fs7 from "fs";
2978
- import path6 from "path";
3047
+ import fs8 from "fs";
3048
+ import path7 from "path";
2979
3049
  function writeClaudeConfig(config) {
2980
3050
  const written = [];
2981
- fs7.writeFileSync("CLAUDE.md", config.claudeMd);
3051
+ fs8.writeFileSync("CLAUDE.md", config.claudeMd);
2982
3052
  written.push("CLAUDE.md");
2983
3053
  if (config.skills?.length) {
2984
3054
  for (const skill of config.skills) {
2985
- const skillDir = path6.join(".claude", "skills", skill.name);
2986
- if (!fs7.existsSync(skillDir)) fs7.mkdirSync(skillDir, { recursive: true });
2987
- const skillPath = path6.join(skillDir, "SKILL.md");
3055
+ const skillDir = path7.join(".claude", "skills", skill.name);
3056
+ if (!fs8.existsSync(skillDir)) fs8.mkdirSync(skillDir, { recursive: true });
3057
+ const skillPath = path7.join(skillDir, "SKILL.md");
2988
3058
  const frontmatter = [
2989
3059
  "---",
2990
3060
  `name: ${skill.name}`,
@@ -2992,49 +3062,49 @@ function writeClaudeConfig(config) {
2992
3062
  "---",
2993
3063
  ""
2994
3064
  ].join("\n");
2995
- fs7.writeFileSync(skillPath, frontmatter + skill.content);
3065
+ fs8.writeFileSync(skillPath, frontmatter + skill.content);
2996
3066
  written.push(skillPath);
2997
3067
  }
2998
3068
  }
2999
3069
  if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
3000
3070
  let existingServers = {};
3001
3071
  try {
3002
- if (fs7.existsSync(".mcp.json")) {
3003
- const existing = JSON.parse(fs7.readFileSync(".mcp.json", "utf-8"));
3072
+ if (fs8.existsSync(".mcp.json")) {
3073
+ const existing = JSON.parse(fs8.readFileSync(".mcp.json", "utf-8"));
3004
3074
  if (existing.mcpServers) existingServers = existing.mcpServers;
3005
3075
  }
3006
3076
  } catch {
3007
3077
  }
3008
3078
  const mergedServers = { ...existingServers, ...config.mcpServers };
3009
- fs7.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
3079
+ fs8.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
3010
3080
  written.push(".mcp.json");
3011
3081
  }
3012
3082
  return written;
3013
3083
  }
3014
3084
 
3015
3085
  // src/writers/cursor/index.ts
3016
- import fs8 from "fs";
3017
- import path7 from "path";
3086
+ import fs9 from "fs";
3087
+ import path8 from "path";
3018
3088
  function writeCursorConfig(config) {
3019
3089
  const written = [];
3020
3090
  if (config.cursorrules) {
3021
- fs8.writeFileSync(".cursorrules", config.cursorrules);
3091
+ fs9.writeFileSync(".cursorrules", config.cursorrules);
3022
3092
  written.push(".cursorrules");
3023
3093
  }
3024
3094
  if (config.rules?.length) {
3025
- const rulesDir = path7.join(".cursor", "rules");
3026
- if (!fs8.existsSync(rulesDir)) fs8.mkdirSync(rulesDir, { recursive: true });
3095
+ const rulesDir = path8.join(".cursor", "rules");
3096
+ if (!fs9.existsSync(rulesDir)) fs9.mkdirSync(rulesDir, { recursive: true });
3027
3097
  for (const rule of config.rules) {
3028
- const rulePath = path7.join(rulesDir, rule.filename);
3029
- fs8.writeFileSync(rulePath, rule.content);
3098
+ const rulePath = path8.join(rulesDir, rule.filename);
3099
+ fs9.writeFileSync(rulePath, rule.content);
3030
3100
  written.push(rulePath);
3031
3101
  }
3032
3102
  }
3033
3103
  if (config.skills?.length) {
3034
3104
  for (const skill of config.skills) {
3035
- const skillDir = path7.join(".cursor", "skills", skill.name);
3036
- if (!fs8.existsSync(skillDir)) fs8.mkdirSync(skillDir, { recursive: true });
3037
- const skillPath = path7.join(skillDir, "SKILL.md");
3105
+ const skillDir = path8.join(".cursor", "skills", skill.name);
3106
+ if (!fs9.existsSync(skillDir)) fs9.mkdirSync(skillDir, { recursive: true });
3107
+ const skillPath = path8.join(skillDir, "SKILL.md");
3038
3108
  const frontmatter = [
3039
3109
  "---",
3040
3110
  `name: ${skill.name}`,
@@ -3042,41 +3112,41 @@ function writeCursorConfig(config) {
3042
3112
  "---",
3043
3113
  ""
3044
3114
  ].join("\n");
3045
- fs8.writeFileSync(skillPath, frontmatter + skill.content);
3115
+ fs9.writeFileSync(skillPath, frontmatter + skill.content);
3046
3116
  written.push(skillPath);
3047
3117
  }
3048
3118
  }
3049
3119
  if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
3050
3120
  const cursorDir = ".cursor";
3051
- if (!fs8.existsSync(cursorDir)) fs8.mkdirSync(cursorDir, { recursive: true });
3052
- const mcpPath = path7.join(cursorDir, "mcp.json");
3121
+ if (!fs9.existsSync(cursorDir)) fs9.mkdirSync(cursorDir, { recursive: true });
3122
+ const mcpPath = path8.join(cursorDir, "mcp.json");
3053
3123
  let existingServers = {};
3054
3124
  try {
3055
- if (fs8.existsSync(mcpPath)) {
3056
- const existing = JSON.parse(fs8.readFileSync(mcpPath, "utf-8"));
3125
+ if (fs9.existsSync(mcpPath)) {
3126
+ const existing = JSON.parse(fs9.readFileSync(mcpPath, "utf-8"));
3057
3127
  if (existing.mcpServers) existingServers = existing.mcpServers;
3058
3128
  }
3059
3129
  } catch {
3060
3130
  }
3061
3131
  const mergedServers = { ...existingServers, ...config.mcpServers };
3062
- fs8.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
3132
+ fs9.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
3063
3133
  written.push(mcpPath);
3064
3134
  }
3065
3135
  return written;
3066
3136
  }
3067
3137
 
3068
3138
  // src/writers/codex/index.ts
3069
- import fs9 from "fs";
3070
- import path8 from "path";
3139
+ import fs10 from "fs";
3140
+ import path9 from "path";
3071
3141
  function writeCodexConfig(config) {
3072
3142
  const written = [];
3073
- fs9.writeFileSync("AGENTS.md", config.agentsMd);
3143
+ fs10.writeFileSync("AGENTS.md", config.agentsMd);
3074
3144
  written.push("AGENTS.md");
3075
3145
  if (config.skills?.length) {
3076
3146
  for (const skill of config.skills) {
3077
- const skillDir = path8.join(".agents", "skills", skill.name);
3078
- if (!fs9.existsSync(skillDir)) fs9.mkdirSync(skillDir, { recursive: true });
3079
- const skillPath = path8.join(skillDir, "SKILL.md");
3147
+ const skillDir = path9.join(".agents", "skills", skill.name);
3148
+ if (!fs10.existsSync(skillDir)) fs10.mkdirSync(skillDir, { recursive: true });
3149
+ const skillPath = path9.join(skillDir, "SKILL.md");
3080
3150
  const frontmatter = [
3081
3151
  "---",
3082
3152
  `name: ${skill.name}`,
@@ -3084,7 +3154,7 @@ function writeCodexConfig(config) {
3084
3154
  "---",
3085
3155
  ""
3086
3156
  ].join("\n");
3087
- fs9.writeFileSync(skillPath, frontmatter + skill.content);
3157
+ fs10.writeFileSync(skillPath, frontmatter + skill.content);
3088
3158
  written.push(skillPath);
3089
3159
  }
3090
3160
  }
@@ -3093,62 +3163,62 @@ function writeCodexConfig(config) {
3093
3163
 
3094
3164
  // src/writers/backup.ts
3095
3165
  init_constants();
3096
- import fs10 from "fs";
3097
- import path10 from "path";
3166
+ import fs11 from "fs";
3167
+ import path11 from "path";
3098
3168
  function createBackup(files) {
3099
3169
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3100
- const backupDir = path10.join(BACKUPS_DIR, timestamp);
3170
+ const backupDir = path11.join(BACKUPS_DIR, timestamp);
3101
3171
  for (const file of files) {
3102
- if (!fs10.existsSync(file)) continue;
3103
- const dest = path10.join(backupDir, file);
3104
- const destDir = path10.dirname(dest);
3105
- if (!fs10.existsSync(destDir)) {
3106
- fs10.mkdirSync(destDir, { recursive: true });
3172
+ if (!fs11.existsSync(file)) continue;
3173
+ const dest = path11.join(backupDir, file);
3174
+ const destDir = path11.dirname(dest);
3175
+ if (!fs11.existsSync(destDir)) {
3176
+ fs11.mkdirSync(destDir, { recursive: true });
3107
3177
  }
3108
- fs10.copyFileSync(file, dest);
3178
+ fs11.copyFileSync(file, dest);
3109
3179
  }
3110
3180
  return backupDir;
3111
3181
  }
3112
3182
  function restoreBackup(backupDir, file) {
3113
- const backupFile = path10.join(backupDir, file);
3114
- if (!fs10.existsSync(backupFile)) return false;
3115
- const destDir = path10.dirname(file);
3116
- if (!fs10.existsSync(destDir)) {
3117
- fs10.mkdirSync(destDir, { recursive: true });
3183
+ const backupFile = path11.join(backupDir, file);
3184
+ if (!fs11.existsSync(backupFile)) return false;
3185
+ const destDir = path11.dirname(file);
3186
+ if (!fs11.existsSync(destDir)) {
3187
+ fs11.mkdirSync(destDir, { recursive: true });
3118
3188
  }
3119
- fs10.copyFileSync(backupFile, file);
3189
+ fs11.copyFileSync(backupFile, file);
3120
3190
  return true;
3121
3191
  }
3122
3192
 
3123
3193
  // src/writers/manifest.ts
3124
3194
  init_constants();
3125
- import fs11 from "fs";
3126
- import crypto from "crypto";
3195
+ import fs12 from "fs";
3196
+ import crypto2 from "crypto";
3127
3197
  function readManifest() {
3128
3198
  try {
3129
- if (!fs11.existsSync(MANIFEST_FILE)) return null;
3130
- return JSON.parse(fs11.readFileSync(MANIFEST_FILE, "utf-8"));
3199
+ if (!fs12.existsSync(MANIFEST_FILE)) return null;
3200
+ return JSON.parse(fs12.readFileSync(MANIFEST_FILE, "utf-8"));
3131
3201
  } catch {
3132
3202
  return null;
3133
3203
  }
3134
3204
  }
3135
3205
  function writeManifest(manifest) {
3136
- if (!fs11.existsSync(CALIBER_DIR)) {
3137
- fs11.mkdirSync(CALIBER_DIR, { recursive: true });
3206
+ if (!fs12.existsSync(CALIBER_DIR)) {
3207
+ fs12.mkdirSync(CALIBER_DIR, { recursive: true });
3138
3208
  }
3139
- fs11.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
3209
+ fs12.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
3140
3210
  }
3141
3211
  function fileChecksum(filePath) {
3142
- const content = fs11.readFileSync(filePath);
3143
- return crypto.createHash("sha256").update(content).digest("hex");
3212
+ const content = fs12.readFileSync(filePath);
3213
+ return crypto2.createHash("sha256").update(content).digest("hex");
3144
3214
  }
3145
3215
 
3146
3216
  // src/writers/index.ts
3147
3217
  function writeSetup(setup) {
3148
3218
  const filesToWrite = getFilesToWrite(setup);
3149
- const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs12.existsSync(f));
3219
+ const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs13.existsSync(f));
3150
3220
  const existingFiles = [
3151
- ...filesToWrite.filter((f) => fs12.existsSync(f)),
3221
+ ...filesToWrite.filter((f) => fs13.existsSync(f)),
3152
3222
  ...filesToDelete
3153
3223
  ];
3154
3224
  const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
@@ -3164,7 +3234,7 @@ function writeSetup(setup) {
3164
3234
  }
3165
3235
  const deleted = [];
3166
3236
  for (const filePath of filesToDelete) {
3167
- fs12.unlinkSync(filePath);
3237
+ fs13.unlinkSync(filePath);
3168
3238
  deleted.push(filePath);
3169
3239
  }
3170
3240
  ensureGitignore();
@@ -3194,8 +3264,8 @@ function undoSetup() {
3194
3264
  const removed = [];
3195
3265
  for (const entry of manifest.entries) {
3196
3266
  if (entry.action === "created") {
3197
- if (fs12.existsSync(entry.path)) {
3198
- fs12.unlinkSync(entry.path);
3267
+ if (fs13.existsSync(entry.path)) {
3268
+ fs13.unlinkSync(entry.path);
3199
3269
  removed.push(entry.path);
3200
3270
  }
3201
3271
  } else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
@@ -3205,8 +3275,8 @@ function undoSetup() {
3205
3275
  }
3206
3276
  }
3207
3277
  const { MANIFEST_FILE: MANIFEST_FILE2 } = (init_constants(), __toCommonJS(constants_exports));
3208
- if (fs12.existsSync(MANIFEST_FILE2)) {
3209
- fs12.unlinkSync(MANIFEST_FILE2);
3278
+ if (fs13.existsSync(MANIFEST_FILE2)) {
3279
+ fs13.unlinkSync(MANIFEST_FILE2);
3210
3280
  }
3211
3281
  return { restored, removed };
3212
3282
  }
@@ -3241,23 +3311,23 @@ function getFilesToWrite(setup) {
3241
3311
  }
3242
3312
  function ensureGitignore() {
3243
3313
  const gitignorePath = ".gitignore";
3244
- if (fs12.existsSync(gitignorePath)) {
3245
- const content = fs12.readFileSync(gitignorePath, "utf-8");
3314
+ if (fs13.existsSync(gitignorePath)) {
3315
+ const content = fs13.readFileSync(gitignorePath, "utf-8");
3246
3316
  if (!content.includes(".caliber/")) {
3247
- fs12.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
3317
+ fs13.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
3248
3318
  }
3249
3319
  } else {
3250
- fs12.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
3320
+ fs13.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
3251
3321
  }
3252
3322
  }
3253
3323
 
3254
3324
  // src/writers/staging.ts
3255
3325
  init_constants();
3256
- import fs13 from "fs";
3257
- import path11 from "path";
3258
- var STAGED_DIR = path11.join(CALIBER_DIR, "staged");
3259
- var PROPOSED_DIR = path11.join(STAGED_DIR, "proposed");
3260
- var CURRENT_DIR = path11.join(STAGED_DIR, "current");
3326
+ import fs14 from "fs";
3327
+ import path12 from "path";
3328
+ var STAGED_DIR = path12.join(CALIBER_DIR, "staged");
3329
+ var PROPOSED_DIR = path12.join(STAGED_DIR, "proposed");
3330
+ var CURRENT_DIR = path12.join(STAGED_DIR, "current");
3261
3331
  function normalizeContent(content) {
3262
3332
  return content.split("\n").map((line) => line.trimEnd()).join("\n").replace(/\n{3,}/g, "\n\n").trim();
3263
3333
  }
@@ -3267,20 +3337,20 @@ function stageFiles(files, projectDir) {
3267
3337
  let modifiedFiles = 0;
3268
3338
  const stagedFiles = [];
3269
3339
  for (const file of files) {
3270
- const originalPath = path11.join(projectDir, file.path);
3271
- if (fs13.existsSync(originalPath)) {
3272
- const existing = fs13.readFileSync(originalPath, "utf-8");
3340
+ const originalPath = path12.join(projectDir, file.path);
3341
+ if (fs14.existsSync(originalPath)) {
3342
+ const existing = fs14.readFileSync(originalPath, "utf-8");
3273
3343
  if (normalizeContent(existing) === normalizeContent(file.content)) {
3274
3344
  continue;
3275
3345
  }
3276
3346
  }
3277
- const proposedPath = path11.join(PROPOSED_DIR, file.path);
3278
- fs13.mkdirSync(path11.dirname(proposedPath), { recursive: true });
3279
- fs13.writeFileSync(proposedPath, file.content);
3280
- if (fs13.existsSync(originalPath)) {
3281
- const currentPath = path11.join(CURRENT_DIR, file.path);
3282
- fs13.mkdirSync(path11.dirname(currentPath), { recursive: true });
3283
- fs13.copyFileSync(originalPath, currentPath);
3347
+ const proposedPath = path12.join(PROPOSED_DIR, file.path);
3348
+ fs14.mkdirSync(path12.dirname(proposedPath), { recursive: true });
3349
+ fs14.writeFileSync(proposedPath, file.content);
3350
+ if (fs14.existsSync(originalPath)) {
3351
+ const currentPath = path12.join(CURRENT_DIR, file.path);
3352
+ fs14.mkdirSync(path12.dirname(currentPath), { recursive: true });
3353
+ fs14.copyFileSync(originalPath, currentPath);
3284
3354
  modifiedFiles++;
3285
3355
  stagedFiles.push({ relativePath: file.path, proposedPath, currentPath, originalPath, isNew: false });
3286
3356
  } else {
@@ -3291,34 +3361,34 @@ function stageFiles(files, projectDir) {
3291
3361
  return { newFiles, modifiedFiles, stagedFiles };
3292
3362
  }
3293
3363
  function cleanupStaging() {
3294
- if (fs13.existsSync(STAGED_DIR)) {
3295
- fs13.rmSync(STAGED_DIR, { recursive: true, force: true });
3364
+ if (fs14.existsSync(STAGED_DIR)) {
3365
+ fs14.rmSync(STAGED_DIR, { recursive: true, force: true });
3296
3366
  }
3297
3367
  }
3298
3368
 
3299
3369
  // src/utils/review.ts
3300
3370
  import chalk2 from "chalk";
3301
- import fs15 from "fs";
3371
+ import fs16 from "fs";
3302
3372
  import select2 from "@inquirer/select";
3303
3373
  import { createTwoFilesPatch } from "diff";
3304
3374
 
3305
3375
  // src/utils/editor.ts
3306
- import { execSync as execSync5, spawn as spawn3 } from "child_process";
3307
- import fs14 from "fs";
3308
- import path12 from "path";
3376
+ import { execSync as execSync6, spawn as spawn3 } from "child_process";
3377
+ import fs15 from "fs";
3378
+ import path13 from "path";
3309
3379
  import os4 from "os";
3310
3380
  var IS_WINDOWS3 = process.platform === "win32";
3311
- var DIFF_TEMP_DIR = path12.join(os4.tmpdir(), "caliber-diff");
3381
+ var DIFF_TEMP_DIR = path13.join(os4.tmpdir(), "caliber-diff");
3312
3382
  function getEmptyFilePath(proposedPath) {
3313
- fs14.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
3314
- const tempPath = path12.join(DIFF_TEMP_DIR, path12.basename(proposedPath));
3315
- fs14.writeFileSync(tempPath, "");
3383
+ fs15.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
3384
+ const tempPath = path13.join(DIFF_TEMP_DIR, path13.basename(proposedPath));
3385
+ fs15.writeFileSync(tempPath, "");
3316
3386
  return tempPath;
3317
3387
  }
3318
3388
  function commandExists(cmd) {
3319
3389
  try {
3320
3390
  const check = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
3321
- execSync5(check, { stdio: "ignore" });
3391
+ execSync6(check, { stdio: "ignore" });
3322
3392
  return true;
3323
3393
  } catch {
3324
3394
  return false;
@@ -3383,8 +3453,8 @@ async function openReview(method, stagedFiles) {
3383
3453
  return;
3384
3454
  }
3385
3455
  const fileInfos = stagedFiles.map((file) => {
3386
- const proposed = fs15.readFileSync(file.proposedPath, "utf-8");
3387
- const current = file.currentPath ? fs15.readFileSync(file.currentPath, "utf-8") : "";
3456
+ const proposed = fs16.readFileSync(file.proposedPath, "utf-8");
3457
+ const current = file.currentPath ? fs16.readFileSync(file.currentPath, "utf-8") : "";
3388
3458
  const patch = createTwoFilesPatch(
3389
3459
  file.isNew ? "/dev/null" : file.relativePath,
3390
3460
  file.relativePath,
@@ -3547,7 +3617,7 @@ async function interactiveDiffExplorer(files) {
3547
3617
  }
3548
3618
 
3549
3619
  // src/commands/setup-files.ts
3550
- import fs16 from "fs";
3620
+ import fs17 from "fs";
3551
3621
  function buildSkillContent(skill) {
3552
3622
  const frontmatter = `---
3553
3623
  name: ${skill.name}
@@ -3596,7 +3666,7 @@ function collectSetupFiles(setup, targetAgent) {
3596
3666
  }
3597
3667
  }
3598
3668
  const codexTargeted = targetAgent ? targetAgent.includes("codex") : false;
3599
- if (codexTargeted && !fs16.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
3669
+ if (codexTargeted && !fs17.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
3600
3670
  const agentRefs = [];
3601
3671
  if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
3602
3672
  if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
@@ -3613,13 +3683,13 @@ ${agentRefs.join(" ")}
3613
3683
  }
3614
3684
 
3615
3685
  // src/lib/hooks.ts
3616
- import fs18 from "fs";
3617
- import path13 from "path";
3618
- import { execSync as execSync7 } from "child_process";
3686
+ import fs19 from "fs";
3687
+ import path14 from "path";
3688
+ import { execSync as execSync8 } from "child_process";
3619
3689
 
3620
3690
  // src/lib/resolve-caliber.ts
3621
- import fs17 from "fs";
3622
- import { execSync as execSync6 } from "child_process";
3691
+ import fs18 from "fs";
3692
+ import { execSync as execSync7 } from "child_process";
3623
3693
  var _resolved = null;
3624
3694
  function resolveCaliber() {
3625
3695
  if (_resolved) return _resolved;
@@ -3630,7 +3700,7 @@ function resolveCaliber() {
3630
3700
  }
3631
3701
  try {
3632
3702
  const whichCmd = process.platform === "win32" ? "where caliber" : "which caliber";
3633
- const found = execSync6(whichCmd, {
3703
+ const found = execSync7(whichCmd, {
3634
3704
  encoding: "utf-8",
3635
3705
  stdio: ["pipe", "pipe", "pipe"]
3636
3706
  }).trim();
@@ -3641,7 +3711,7 @@ function resolveCaliber() {
3641
3711
  } catch {
3642
3712
  }
3643
3713
  const binPath = process.argv[1];
3644
- if (binPath && fs17.existsSync(binPath)) {
3714
+ if (binPath && fs18.existsSync(binPath)) {
3645
3715
  _resolved = binPath;
3646
3716
  return _resolved;
3647
3717
  }
@@ -3657,24 +3727,24 @@ function isCaliberCommand(command, subcommandTail) {
3657
3727
  }
3658
3728
 
3659
3729
  // src/lib/hooks.ts
3660
- var SETTINGS_PATH = path13.join(".claude", "settings.json");
3730
+ var SETTINGS_PATH = path14.join(".claude", "settings.json");
3661
3731
  var REFRESH_TAIL = "refresh --quiet";
3662
3732
  var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
3663
3733
  function getHookCommand() {
3664
3734
  return `${resolveCaliber()} ${REFRESH_TAIL}`;
3665
3735
  }
3666
3736
  function readSettings() {
3667
- if (!fs18.existsSync(SETTINGS_PATH)) return {};
3737
+ if (!fs19.existsSync(SETTINGS_PATH)) return {};
3668
3738
  try {
3669
- return JSON.parse(fs18.readFileSync(SETTINGS_PATH, "utf-8"));
3739
+ return JSON.parse(fs19.readFileSync(SETTINGS_PATH, "utf-8"));
3670
3740
  } catch {
3671
3741
  return {};
3672
3742
  }
3673
3743
  }
3674
3744
  function writeSettings(settings) {
3675
- const dir = path13.dirname(SETTINGS_PATH);
3676
- if (!fs18.existsSync(dir)) fs18.mkdirSync(dir, { recursive: true });
3677
- fs18.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
3745
+ const dir = path14.dirname(SETTINGS_PATH);
3746
+ if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
3747
+ fs19.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
3678
3748
  }
3679
3749
  function findHookIndex(sessionEnd) {
3680
3750
  return sessionEnd.findIndex(
@@ -3736,20 +3806,20 @@ ${PRECOMMIT_END}`;
3736
3806
  }
3737
3807
  function getGitHooksDir() {
3738
3808
  try {
3739
- const gitDir = execSync7("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3740
- return path13.join(gitDir, "hooks");
3809
+ const gitDir = execSync8("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3810
+ return path14.join(gitDir, "hooks");
3741
3811
  } catch {
3742
3812
  return null;
3743
3813
  }
3744
3814
  }
3745
3815
  function getPreCommitPath() {
3746
3816
  const hooksDir = getGitHooksDir();
3747
- return hooksDir ? path13.join(hooksDir, "pre-commit") : null;
3817
+ return hooksDir ? path14.join(hooksDir, "pre-commit") : null;
3748
3818
  }
3749
3819
  function isPreCommitHookInstalled() {
3750
3820
  const hookPath = getPreCommitPath();
3751
- if (!hookPath || !fs18.existsSync(hookPath)) return false;
3752
- const content = fs18.readFileSync(hookPath, "utf-8");
3821
+ if (!hookPath || !fs19.existsSync(hookPath)) return false;
3822
+ const content = fs19.readFileSync(hookPath, "utf-8");
3753
3823
  return content.includes(PRECOMMIT_START);
3754
3824
  }
3755
3825
  function installPreCommitHook() {
@@ -3758,43 +3828,43 @@ function installPreCommitHook() {
3758
3828
  }
3759
3829
  const hookPath = getPreCommitPath();
3760
3830
  if (!hookPath) return { installed: false, alreadyInstalled: false };
3761
- const hooksDir = path13.dirname(hookPath);
3762
- if (!fs18.existsSync(hooksDir)) fs18.mkdirSync(hooksDir, { recursive: true });
3831
+ const hooksDir = path14.dirname(hookPath);
3832
+ if (!fs19.existsSync(hooksDir)) fs19.mkdirSync(hooksDir, { recursive: true });
3763
3833
  let content = "";
3764
- if (fs18.existsSync(hookPath)) {
3765
- content = fs18.readFileSync(hookPath, "utf-8");
3834
+ if (fs19.existsSync(hookPath)) {
3835
+ content = fs19.readFileSync(hookPath, "utf-8");
3766
3836
  if (!content.endsWith("\n")) content += "\n";
3767
3837
  content += "\n" + getPrecommitBlock() + "\n";
3768
3838
  } else {
3769
3839
  content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
3770
3840
  }
3771
- fs18.writeFileSync(hookPath, content);
3772
- fs18.chmodSync(hookPath, 493);
3841
+ fs19.writeFileSync(hookPath, content);
3842
+ fs19.chmodSync(hookPath, 493);
3773
3843
  return { installed: true, alreadyInstalled: false };
3774
3844
  }
3775
3845
  function removePreCommitHook() {
3776
3846
  const hookPath = getPreCommitPath();
3777
- if (!hookPath || !fs18.existsSync(hookPath)) {
3847
+ if (!hookPath || !fs19.existsSync(hookPath)) {
3778
3848
  return { removed: false, notFound: true };
3779
3849
  }
3780
- let content = fs18.readFileSync(hookPath, "utf-8");
3850
+ let content = fs19.readFileSync(hookPath, "utf-8");
3781
3851
  if (!content.includes(PRECOMMIT_START)) {
3782
3852
  return { removed: false, notFound: true };
3783
3853
  }
3784
3854
  const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
3785
3855
  content = content.replace(regex, "\n");
3786
3856
  if (content.trim() === "#!/bin/sh" || content.trim() === "") {
3787
- fs18.unlinkSync(hookPath);
3857
+ fs19.unlinkSync(hookPath);
3788
3858
  } else {
3789
- fs18.writeFileSync(hookPath, content);
3859
+ fs19.writeFileSync(hookPath, content);
3790
3860
  }
3791
3861
  return { removed: true, notFound: false };
3792
3862
  }
3793
3863
 
3794
3864
  // src/lib/learning-hooks.ts
3795
- import fs19 from "fs";
3796
- import path14 from "path";
3797
- var SETTINGS_PATH2 = path14.join(".claude", "settings.json");
3865
+ import fs20 from "fs";
3866
+ import path15 from "path";
3867
+ var SETTINGS_PATH2 = path15.join(".claude", "settings.json");
3798
3868
  var HOOK_TAILS = [
3799
3869
  { event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
3800
3870
  { event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
@@ -3811,17 +3881,17 @@ function getHookConfigs() {
3811
3881
  }));
3812
3882
  }
3813
3883
  function readSettings2() {
3814
- if (!fs19.existsSync(SETTINGS_PATH2)) return {};
3884
+ if (!fs20.existsSync(SETTINGS_PATH2)) return {};
3815
3885
  try {
3816
- return JSON.parse(fs19.readFileSync(SETTINGS_PATH2, "utf-8"));
3886
+ return JSON.parse(fs20.readFileSync(SETTINGS_PATH2, "utf-8"));
3817
3887
  } catch {
3818
3888
  return {};
3819
3889
  }
3820
3890
  }
3821
3891
  function writeSettings2(settings) {
3822
- const dir = path14.dirname(SETTINGS_PATH2);
3823
- if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
3824
- fs19.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
3892
+ const dir = path15.dirname(SETTINGS_PATH2);
3893
+ if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
3894
+ fs20.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
3825
3895
  }
3826
3896
  function hasLearningHook(matchers, tail) {
3827
3897
  return matchers.some((entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, tail)));
@@ -3855,7 +3925,7 @@ function installLearningHooks() {
3855
3925
  writeSettings2(settings);
3856
3926
  return { installed: true, alreadyInstalled: false };
3857
3927
  }
3858
- var CURSOR_HOOKS_PATH = path14.join(".cursor", "hooks.json");
3928
+ var CURSOR_HOOKS_PATH = path15.join(".cursor", "hooks.json");
3859
3929
  var CURSOR_HOOK_EVENTS = [
3860
3930
  { event: "postToolUse", tail: "learn observe" },
3861
3931
  { event: "postToolUseFailure", tail: "learn observe --failure" },
@@ -3863,17 +3933,17 @@ var CURSOR_HOOK_EVENTS = [
3863
3933
  { event: "sessionEnd", tail: "learn finalize" }
3864
3934
  ];
3865
3935
  function readCursorHooks() {
3866
- if (!fs19.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
3936
+ if (!fs20.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
3867
3937
  try {
3868
- return JSON.parse(fs19.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
3938
+ return JSON.parse(fs20.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
3869
3939
  } catch {
3870
3940
  return { version: 1, hooks: {} };
3871
3941
  }
3872
3942
  }
3873
3943
  function writeCursorHooks(config) {
3874
- const dir = path14.dirname(CURSOR_HOOKS_PATH);
3875
- if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
3876
- fs19.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
3944
+ const dir = path15.dirname(CURSOR_HOOKS_PATH);
3945
+ if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
3946
+ fs20.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
3877
3947
  }
3878
3948
  function hasCursorHook(entries, tail) {
3879
3949
  return entries.some((e) => isCaliberCommand(e.command, tail));
@@ -3943,10 +4013,10 @@ function removeLearningHooks() {
3943
4013
 
3944
4014
  // src/lib/state.ts
3945
4015
  init_constants();
3946
- import fs20 from "fs";
3947
- import path15 from "path";
3948
- import { execSync as execSync8 } from "child_process";
3949
- var STATE_FILE = path15.join(CALIBER_DIR, ".caliber-state.json");
4016
+ import fs21 from "fs";
4017
+ import path16 from "path";
4018
+ import { execSync as execSync9 } from "child_process";
4019
+ var STATE_FILE = path16.join(CALIBER_DIR, ".caliber-state.json");
3950
4020
  function normalizeTargetAgent(value) {
3951
4021
  if (Array.isArray(value)) return value;
3952
4022
  if (typeof value === "string") {
@@ -3957,8 +4027,8 @@ function normalizeTargetAgent(value) {
3957
4027
  }
3958
4028
  function readState() {
3959
4029
  try {
3960
- if (!fs20.existsSync(STATE_FILE)) return null;
3961
- const raw = JSON.parse(fs20.readFileSync(STATE_FILE, "utf-8"));
4030
+ if (!fs21.existsSync(STATE_FILE)) return null;
4031
+ const raw = JSON.parse(fs21.readFileSync(STATE_FILE, "utf-8"));
3962
4032
  if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
3963
4033
  return raw;
3964
4034
  } catch {
@@ -3966,14 +4036,14 @@ function readState() {
3966
4036
  }
3967
4037
  }
3968
4038
  function writeState(state) {
3969
- if (!fs20.existsSync(CALIBER_DIR)) {
3970
- fs20.mkdirSync(CALIBER_DIR, { recursive: true });
4039
+ if (!fs21.existsSync(CALIBER_DIR)) {
4040
+ fs21.mkdirSync(CALIBER_DIR, { recursive: true });
3971
4041
  }
3972
- fs20.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
4042
+ fs21.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
3973
4043
  }
3974
4044
  function getCurrentHeadSha() {
3975
4045
  try {
3976
- return execSync8("git rev-parse HEAD", {
4046
+ return execSync9("git rev-parse HEAD", {
3977
4047
  encoding: "utf-8",
3978
4048
  stdio: ["pipe", "pipe", "pipe"]
3979
4049
  }).trim();
@@ -4918,7 +4988,7 @@ function checkGrounding(dir) {
4918
4988
 
4919
4989
  // src/scoring/checks/accuracy.ts
4920
4990
  import { existsSync as existsSync4, statSync as statSync2 } from "fs";
4921
- import { execSync as execSync9 } from "child_process";
4991
+ import { execSync as execSync10 } from "child_process";
4922
4992
  import { join as join5 } from "path";
4923
4993
  function validateReferences(dir) {
4924
4994
  const configContent = collectPrimaryConfigContent(dir);
@@ -4927,13 +4997,13 @@ function validateReferences(dir) {
4927
4997
  }
4928
4998
  function detectGitDrift(dir) {
4929
4999
  try {
4930
- execSync9("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
5000
+ execSync10("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
4931
5001
  } catch {
4932
5002
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: false };
4933
5003
  }
4934
5004
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".cursor/rules"];
4935
5005
  try {
4936
- const headTimestamp = execSync9(
5006
+ const headTimestamp = execSync10(
4937
5007
  "git log -1 --format=%ct HEAD",
4938
5008
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4939
5009
  ).trim();
@@ -4954,7 +5024,7 @@ function detectGitDrift(dir) {
4954
5024
  let latestConfigCommitHash = null;
4955
5025
  for (const file of configFiles) {
4956
5026
  try {
4957
- const hash = execSync9(
5027
+ const hash = execSync10(
4958
5028
  `git log -1 --format=%H -- "${file}"`,
4959
5029
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4960
5030
  ).trim();
@@ -4963,7 +5033,7 @@ function detectGitDrift(dir) {
4963
5033
  latestConfigCommitHash = hash;
4964
5034
  } else {
4965
5035
  try {
4966
- execSync9(
5036
+ execSync10(
4967
5037
  `git merge-base --is-ancestor ${latestConfigCommitHash} ${hash}`,
4968
5038
  { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }
4969
5039
  );
@@ -4978,12 +5048,12 @@ function detectGitDrift(dir) {
4978
5048
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: true };
4979
5049
  }
4980
5050
  try {
4981
- const countStr = execSync9(
5051
+ const countStr = execSync10(
4982
5052
  `git rev-list --count ${latestConfigCommitHash}..HEAD`,
4983
5053
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4984
5054
  ).trim();
4985
5055
  const commitsSince = parseInt(countStr, 10) || 0;
4986
- const lastDate = execSync9(
5056
+ const lastDate = execSync10(
4987
5057
  `git log -1 --format=%ci ${latestConfigCommitHash}`,
4988
5058
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4989
5059
  ).trim();
@@ -5055,12 +5125,12 @@ function checkAccuracy(dir) {
5055
5125
 
5056
5126
  // src/scoring/checks/freshness.ts
5057
5127
  import { existsSync as existsSync5, statSync as statSync3 } from "fs";
5058
- import { execSync as execSync10 } from "child_process";
5128
+ import { execSync as execSync11 } from "child_process";
5059
5129
  import { join as join6 } from "path";
5060
5130
  function getCommitsSinceConfigUpdate(dir) {
5061
5131
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules"];
5062
5132
  try {
5063
- const headTimestamp = execSync10(
5133
+ const headTimestamp = execSync11(
5064
5134
  "git log -1 --format=%ct HEAD",
5065
5135
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5066
5136
  ).trim();
@@ -5080,12 +5150,12 @@ function getCommitsSinceConfigUpdate(dir) {
5080
5150
  }
5081
5151
  for (const file of configFiles) {
5082
5152
  try {
5083
- const hash = execSync10(
5153
+ const hash = execSync11(
5084
5154
  `git log -1 --format=%H -- "${file}"`,
5085
5155
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5086
5156
  ).trim();
5087
5157
  if (hash) {
5088
- const countStr = execSync10(
5158
+ const countStr = execSync11(
5089
5159
  `git rev-list --count ${hash}..HEAD`,
5090
5160
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5091
5161
  ).trim();
@@ -5203,11 +5273,11 @@ function checkFreshness(dir) {
5203
5273
 
5204
5274
  // src/scoring/checks/bonus.ts
5205
5275
  import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
5206
- import { execSync as execSync11 } from "child_process";
5276
+ import { execSync as execSync12 } from "child_process";
5207
5277
  import { join as join7 } from "path";
5208
5278
  function hasPreCommitHook(dir) {
5209
5279
  try {
5210
- const gitDir = execSync11("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
5280
+ const gitDir = execSync12("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
5211
5281
  const hookPath = join7(gitDir, "hooks", "pre-commit");
5212
5282
  const content = readFileOrNull2(hookPath);
5213
5283
  return content ? content.includes("caliber") : false;
@@ -5321,22 +5391,22 @@ function checkBonus(dir) {
5321
5391
 
5322
5392
  // src/scoring/dismissed.ts
5323
5393
  init_constants();
5324
- import fs21 from "fs";
5325
- import path16 from "path";
5326
- var DISMISSED_FILE = path16.join(CALIBER_DIR, "dismissed-checks.json");
5394
+ import fs22 from "fs";
5395
+ import path17 from "path";
5396
+ var DISMISSED_FILE = path17.join(CALIBER_DIR, "dismissed-checks.json");
5327
5397
  function readDismissedChecks() {
5328
5398
  try {
5329
- if (!fs21.existsSync(DISMISSED_FILE)) return [];
5330
- return JSON.parse(fs21.readFileSync(DISMISSED_FILE, "utf-8"));
5399
+ if (!fs22.existsSync(DISMISSED_FILE)) return [];
5400
+ return JSON.parse(fs22.readFileSync(DISMISSED_FILE, "utf-8"));
5331
5401
  } catch {
5332
5402
  return [];
5333
5403
  }
5334
5404
  }
5335
5405
  function writeDismissedChecks(checks) {
5336
- if (!fs21.existsSync(CALIBER_DIR)) {
5337
- fs21.mkdirSync(CALIBER_DIR, { recursive: true });
5406
+ if (!fs22.existsSync(CALIBER_DIR)) {
5407
+ fs22.mkdirSync(CALIBER_DIR, { recursive: true });
5338
5408
  }
5339
- fs21.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
5409
+ fs22.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
5340
5410
  }
5341
5411
  function getDismissedIds() {
5342
5412
  return new Set(readDismissedChecks().map((c) => c.id));
@@ -5581,13 +5651,13 @@ import { mkdirSync, readFileSync as readFileSync4, readdirSync as readdirSync4,
5581
5651
  import { join as join9, dirname as dirname2 } from "path";
5582
5652
 
5583
5653
  // src/scanner/index.ts
5584
- import fs22 from "fs";
5585
- import path17 from "path";
5586
- import crypto2 from "crypto";
5654
+ import fs23 from "fs";
5655
+ import path18 from "path";
5656
+ import crypto3 from "crypto";
5587
5657
  function scanLocalState(dir) {
5588
5658
  const items = [];
5589
- const claudeMdPath = path17.join(dir, "CLAUDE.md");
5590
- if (fs22.existsSync(claudeMdPath)) {
5659
+ const claudeMdPath = path18.join(dir, "CLAUDE.md");
5660
+ if (fs23.existsSync(claudeMdPath)) {
5591
5661
  items.push({
5592
5662
  type: "rule",
5593
5663
  platform: "claude",
@@ -5596,10 +5666,10 @@ function scanLocalState(dir) {
5596
5666
  path: claudeMdPath
5597
5667
  });
5598
5668
  }
5599
- const skillsDir = path17.join(dir, ".claude", "skills");
5600
- if (fs22.existsSync(skillsDir)) {
5601
- for (const file of fs22.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
5602
- const filePath = path17.join(skillsDir, file);
5669
+ const skillsDir = path18.join(dir, ".claude", "skills");
5670
+ if (fs23.existsSync(skillsDir)) {
5671
+ for (const file of fs23.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
5672
+ const filePath = path18.join(skillsDir, file);
5603
5673
  items.push({
5604
5674
  type: "skill",
5605
5675
  platform: "claude",
@@ -5609,10 +5679,10 @@ function scanLocalState(dir) {
5609
5679
  });
5610
5680
  }
5611
5681
  }
5612
- const mcpJsonPath = path17.join(dir, ".mcp.json");
5613
- if (fs22.existsSync(mcpJsonPath)) {
5682
+ const mcpJsonPath = path18.join(dir, ".mcp.json");
5683
+ if (fs23.existsSync(mcpJsonPath)) {
5614
5684
  try {
5615
- const mcpJson = JSON.parse(fs22.readFileSync(mcpJsonPath, "utf-8"));
5685
+ const mcpJson = JSON.parse(fs23.readFileSync(mcpJsonPath, "utf-8"));
5616
5686
  if (mcpJson.mcpServers) {
5617
5687
  for (const name of Object.keys(mcpJson.mcpServers)) {
5618
5688
  items.push({
@@ -5627,8 +5697,8 @@ function scanLocalState(dir) {
5627
5697
  } catch {
5628
5698
  }
5629
5699
  }
5630
- const agentsMdPath = path17.join(dir, "AGENTS.md");
5631
- if (fs22.existsSync(agentsMdPath)) {
5700
+ const agentsMdPath = path18.join(dir, "AGENTS.md");
5701
+ if (fs23.existsSync(agentsMdPath)) {
5632
5702
  items.push({
5633
5703
  type: "rule",
5634
5704
  platform: "codex",
@@ -5637,12 +5707,12 @@ function scanLocalState(dir) {
5637
5707
  path: agentsMdPath
5638
5708
  });
5639
5709
  }
5640
- const codexSkillsDir = path17.join(dir, ".agents", "skills");
5641
- if (fs22.existsSync(codexSkillsDir)) {
5710
+ const codexSkillsDir = path18.join(dir, ".agents", "skills");
5711
+ if (fs23.existsSync(codexSkillsDir)) {
5642
5712
  try {
5643
- for (const name of fs22.readdirSync(codexSkillsDir)) {
5644
- const skillFile = path17.join(codexSkillsDir, name, "SKILL.md");
5645
- if (fs22.existsSync(skillFile)) {
5713
+ for (const name of fs23.readdirSync(codexSkillsDir)) {
5714
+ const skillFile = path18.join(codexSkillsDir, name, "SKILL.md");
5715
+ if (fs23.existsSync(skillFile)) {
5646
5716
  items.push({
5647
5717
  type: "skill",
5648
5718
  platform: "codex",
@@ -5655,8 +5725,8 @@ function scanLocalState(dir) {
5655
5725
  } catch {
5656
5726
  }
5657
5727
  }
5658
- const cursorrulesPath = path17.join(dir, ".cursorrules");
5659
- if (fs22.existsSync(cursorrulesPath)) {
5728
+ const cursorrulesPath = path18.join(dir, ".cursorrules");
5729
+ if (fs23.existsSync(cursorrulesPath)) {
5660
5730
  items.push({
5661
5731
  type: "rule",
5662
5732
  platform: "cursor",
@@ -5665,10 +5735,10 @@ function scanLocalState(dir) {
5665
5735
  path: cursorrulesPath
5666
5736
  });
5667
5737
  }
5668
- const cursorRulesDir = path17.join(dir, ".cursor", "rules");
5669
- if (fs22.existsSync(cursorRulesDir)) {
5670
- for (const file of fs22.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
5671
- const filePath = path17.join(cursorRulesDir, file);
5738
+ const cursorRulesDir = path18.join(dir, ".cursor", "rules");
5739
+ if (fs23.existsSync(cursorRulesDir)) {
5740
+ for (const file of fs23.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
5741
+ const filePath = path18.join(cursorRulesDir, file);
5672
5742
  items.push({
5673
5743
  type: "rule",
5674
5744
  platform: "cursor",
@@ -5678,12 +5748,12 @@ function scanLocalState(dir) {
5678
5748
  });
5679
5749
  }
5680
5750
  }
5681
- const cursorSkillsDir = path17.join(dir, ".cursor", "skills");
5682
- if (fs22.existsSync(cursorSkillsDir)) {
5751
+ const cursorSkillsDir = path18.join(dir, ".cursor", "skills");
5752
+ if (fs23.existsSync(cursorSkillsDir)) {
5683
5753
  try {
5684
- for (const name of fs22.readdirSync(cursorSkillsDir)) {
5685
- const skillFile = path17.join(cursorSkillsDir, name, "SKILL.md");
5686
- if (fs22.existsSync(skillFile)) {
5754
+ for (const name of fs23.readdirSync(cursorSkillsDir)) {
5755
+ const skillFile = path18.join(cursorSkillsDir, name, "SKILL.md");
5756
+ if (fs23.existsSync(skillFile)) {
5687
5757
  items.push({
5688
5758
  type: "skill",
5689
5759
  platform: "cursor",
@@ -5696,10 +5766,10 @@ function scanLocalState(dir) {
5696
5766
  } catch {
5697
5767
  }
5698
5768
  }
5699
- const cursorMcpPath = path17.join(dir, ".cursor", "mcp.json");
5700
- if (fs22.existsSync(cursorMcpPath)) {
5769
+ const cursorMcpPath = path18.join(dir, ".cursor", "mcp.json");
5770
+ if (fs23.existsSync(cursorMcpPath)) {
5701
5771
  try {
5702
- const mcpJson = JSON.parse(fs22.readFileSync(cursorMcpPath, "utf-8"));
5772
+ const mcpJson = JSON.parse(fs23.readFileSync(cursorMcpPath, "utf-8"));
5703
5773
  if (mcpJson.mcpServers) {
5704
5774
  for (const name of Object.keys(mcpJson.mcpServers)) {
5705
5775
  items.push({
@@ -5717,11 +5787,11 @@ function scanLocalState(dir) {
5717
5787
  return items;
5718
5788
  }
5719
5789
  function hashFile(filePath) {
5720
- const text = fs22.readFileSync(filePath, "utf-8");
5721
- return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
5790
+ const text = fs23.readFileSync(filePath, "utf-8");
5791
+ return crypto3.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
5722
5792
  }
5723
5793
  function hashJson(obj) {
5724
- return crypto2.createHash("sha256").update(JSON.stringify(obj)).digest("hex");
5794
+ return crypto3.createHash("sha256").update(JSON.stringify(obj)).digest("hex");
5725
5795
  }
5726
5796
 
5727
5797
  // src/commands/recommend.ts
@@ -5732,40 +5802,40 @@ import { PostHog } from "posthog-node";
5732
5802
  import chalk7 from "chalk";
5733
5803
 
5734
5804
  // src/telemetry/config.ts
5735
- import fs23 from "fs";
5736
- import path18 from "path";
5805
+ import fs24 from "fs";
5806
+ import path19 from "path";
5737
5807
  import os5 from "os";
5738
- import crypto3 from "crypto";
5739
- import { execSync as execSync12 } from "child_process";
5740
- var CONFIG_DIR2 = path18.join(os5.homedir(), ".caliber");
5741
- var CONFIG_FILE2 = path18.join(CONFIG_DIR2, "config.json");
5808
+ import crypto4 from "crypto";
5809
+ import { execSync as execSync13 } from "child_process";
5810
+ var CONFIG_DIR2 = path19.join(os5.homedir(), ".caliber");
5811
+ var CONFIG_FILE2 = path19.join(CONFIG_DIR2, "config.json");
5742
5812
  var runtimeDisabled = false;
5743
5813
  function readConfig() {
5744
5814
  try {
5745
- if (!fs23.existsSync(CONFIG_FILE2)) return {};
5746
- return JSON.parse(fs23.readFileSync(CONFIG_FILE2, "utf-8"));
5815
+ if (!fs24.existsSync(CONFIG_FILE2)) return {};
5816
+ return JSON.parse(fs24.readFileSync(CONFIG_FILE2, "utf-8"));
5747
5817
  } catch {
5748
5818
  return {};
5749
5819
  }
5750
5820
  }
5751
5821
  function writeConfig(config) {
5752
- if (!fs23.existsSync(CONFIG_DIR2)) {
5753
- fs23.mkdirSync(CONFIG_DIR2, { recursive: true });
5822
+ if (!fs24.existsSync(CONFIG_DIR2)) {
5823
+ fs24.mkdirSync(CONFIG_DIR2, { recursive: true });
5754
5824
  }
5755
- fs23.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
5825
+ fs24.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
5756
5826
  }
5757
5827
  function getMachineId() {
5758
5828
  const config = readConfig();
5759
5829
  if (config.machineId) return config.machineId;
5760
- const machineId = crypto3.randomUUID();
5830
+ const machineId = crypto4.randomUUID();
5761
5831
  writeConfig({ ...config, machineId });
5762
5832
  return machineId;
5763
5833
  }
5764
5834
  function getGitEmailHash() {
5765
5835
  try {
5766
- const email = execSync12("git config user.email", { encoding: "utf-8" }).trim();
5836
+ const email = execSync13("git config user.email", { encoding: "utf-8" }).trim();
5767
5837
  if (!email) return void 0;
5768
- return crypto3.createHash("sha256").update(email).digest("hex");
5838
+ return crypto4.createHash("sha256").update(email).digest("hex");
5769
5839
  } catch {
5770
5840
  return void 0;
5771
5841
  }
@@ -6708,11 +6778,11 @@ function countIssuePoints(issues) {
6708
6778
  }
6709
6779
  async function scoreAndRefine(setup, dir, sessionHistory, callbacks) {
6710
6780
  const existsCache = /* @__PURE__ */ new Map();
6711
- const cachedExists = (path29) => {
6712
- const cached = existsCache.get(path29);
6781
+ const cachedExists = (path30) => {
6782
+ const cached = existsCache.get(path30);
6713
6783
  if (cached !== void 0) return cached;
6714
- const result = existsSync9(path29);
6715
- existsCache.set(path29, result);
6784
+ const result = existsSync9(path30);
6785
+ existsCache.set(path30, result);
6716
6786
  return result;
6717
6787
  };
6718
6788
  const projectStructure = collectProjectStructure(dir);
@@ -6851,8 +6921,8 @@ async function runScoreRefineWithSpinner(setup, dir, sessionHistory) {
6851
6921
  }
6852
6922
 
6853
6923
  // src/lib/debug-report.ts
6854
- import fs24 from "fs";
6855
- import path19 from "path";
6924
+ import fs25 from "fs";
6925
+ import path20 from "path";
6856
6926
  var DebugReport = class {
6857
6927
  sections = [];
6858
6928
  startTime;
@@ -6921,11 +6991,11 @@ var DebugReport = class {
6921
6991
  lines.push(`| **Total** | **${formatMs(totalMs)}** |`);
6922
6992
  lines.push("");
6923
6993
  }
6924
- const dir = path19.dirname(outputPath);
6925
- if (!fs24.existsSync(dir)) {
6926
- fs24.mkdirSync(dir, { recursive: true });
6994
+ const dir = path20.dirname(outputPath);
6995
+ if (!fs25.existsSync(dir)) {
6996
+ fs25.mkdirSync(dir, { recursive: true });
6927
6997
  }
6928
- fs24.writeFileSync(outputPath, lines.join("\n"));
6998
+ fs25.writeFileSync(outputPath, lines.join("\n"));
6929
6999
  }
6930
7000
  };
6931
7001
  function formatMs(ms) {
@@ -7050,9 +7120,17 @@ var ParallelTaskDisplay = class {
7050
7120
  cachedCardLines = null;
7051
7121
  cachedCardIndex = -1;
7052
7122
  cachedCardCols = -1;
7123
+ cachedConnectors = null;
7053
7124
  add(name, options) {
7054
7125
  const index = this.tasks.length;
7055
- this.tasks.push({ name, status: "pending", message: "", depth: options?.depth ?? 0 });
7126
+ this.tasks.push({
7127
+ name,
7128
+ status: "pending",
7129
+ message: "",
7130
+ depth: options?.depth ?? 0,
7131
+ pipelineLabel: options?.pipelineLabel,
7132
+ pipelineRow: options?.pipelineRow ?? 0
7133
+ });
7056
7134
  return index;
7057
7135
  }
7058
7136
  start() {
@@ -7159,42 +7237,84 @@ var ParallelTaskDisplay = class {
7159
7237
  const boundary = lastSpace > max * 0.5 ? lastSpace : max - 1;
7160
7238
  return text.slice(0, boundary) + "\u2026";
7161
7239
  }
7162
- renderLine(task) {
7163
- const cols = process.stdout.columns || 80;
7164
- const elapsed = task.startTime ? this.formatTime((task.endTime ?? Date.now()) - task.startTime) : "";
7165
- const timeStr = elapsed ? ` ${chalk10.dim(elapsed)}` : "";
7166
- const timePlain = elapsed ? ` ${elapsed}` : "";
7167
- let icon;
7168
- let nameStyle;
7169
- let msgStyle;
7240
+ statusIcon(task) {
7170
7241
  switch (task.status) {
7171
7242
  case "pending":
7172
- icon = chalk10.dim("\u25CB");
7173
- nameStyle = chalk10.dim;
7174
- msgStyle = chalk10.dim;
7175
- break;
7176
- case "running":
7177
- icon = chalk10.cyan(SPINNER_FRAMES[this.spinnerFrame]);
7178
- nameStyle = chalk10.white;
7179
- msgStyle = chalk10.dim;
7180
- break;
7243
+ return { char: "\u25CB", styled: chalk10.dim("\u25CB") };
7244
+ case "running": {
7245
+ const frame = SPINNER_FRAMES[this.spinnerFrame];
7246
+ return { char: frame, styled: chalk10.cyan(frame) };
7247
+ }
7181
7248
  case "done":
7182
- icon = chalk10.green("\u2713");
7183
- nameStyle = chalk10.white;
7184
- msgStyle = chalk10.dim;
7185
- break;
7249
+ return { char: "\u2713", styled: chalk10.green("\u2713") };
7186
7250
  case "failed":
7187
- icon = chalk10.red("\u2717");
7188
- nameStyle = chalk10.white;
7189
- msgStyle = chalk10.red;
7190
- break;
7191
- }
7192
- const indent = " ".repeat(task.depth);
7193
- const paddedName = task.name.padEnd(Math.max(0, NAME_COL_WIDTH - indent.length));
7194
- const usedByFixed = PREFIX.length + indent.length + 2 + NAME_COL_WIDTH + timePlain.length;
7251
+ return { char: "\u2717", styled: chalk10.red("\u2717") };
7252
+ }
7253
+ }
7254
+ renderPipelineHeader() {
7255
+ const mainTasks = this.tasks.filter((t) => t.pipelineLabel && t.pipelineRow === 0);
7256
+ const branchTasks = this.tasks.filter((t) => t.pipelineLabel && t.pipelineRow === 1);
7257
+ if (mainTasks.length === 0) return [];
7258
+ const arrow = " \u2192 ";
7259
+ const styledArrow = chalk10.dim(arrow);
7260
+ const renderNode = (t) => {
7261
+ const { char, styled: icon } = this.statusIcon(t);
7262
+ const label = t.pipelineLabel;
7263
+ const styledLabel = t.status === "pending" ? chalk10.dim(label) : label;
7264
+ return {
7265
+ plain: `[${char} ${label}]`,
7266
+ styled: chalk10.dim("[") + icon + " " + styledLabel + chalk10.dim("]")
7267
+ };
7268
+ };
7269
+ const mainNodes = mainTasks.map(renderNode);
7270
+ const mainLine = PREFIX + mainNodes.map((n) => n.styled).join(styledArrow);
7271
+ const lines = [mainLine];
7272
+ if (branchTasks.length > 0) {
7273
+ const firstNodePlainWidth = mainNodes[0].plain.length;
7274
+ const indent = " ".repeat(PREFIX.length + firstNodePlainWidth + arrow.length);
7275
+ const branchNodes = branchTasks.map(renderNode);
7276
+ const branchLine = indent + chalk10.dim("\u2198 ") + branchNodes.map((n) => n.styled).join(styledArrow) + chalk10.dim(" \u2197");
7277
+ lines.push(branchLine);
7278
+ }
7279
+ return lines;
7280
+ }
7281
+ hasSiblingAfter(startIdx, depth) {
7282
+ for (let i = startIdx; i < this.tasks.length; i++) {
7283
+ if (this.tasks[i].depth < depth) return false;
7284
+ if (this.tasks[i].depth === depth) return true;
7285
+ }
7286
+ return false;
7287
+ }
7288
+ getTreeConnector(index) {
7289
+ const task = this.tasks[index];
7290
+ if (task.depth === 0) return "";
7291
+ if (task.depth === 1) {
7292
+ return this.hasSiblingAfter(index + 1, 1) ? "\u251C\u2500 " : "\u2514\u2500 ";
7293
+ }
7294
+ if (task.depth === 2) {
7295
+ const pipe = this.hasSiblingAfter(index + 1, 1) ? "\u2502" : " ";
7296
+ return `${pipe} \u2514\u2500 `;
7297
+ }
7298
+ return " ".repeat(task.depth);
7299
+ }
7300
+ renderLine(task, index) {
7301
+ const cols = process.stdout.columns || 80;
7302
+ const elapsed = task.startTime ? this.formatTime((task.endTime ?? Date.now()) - task.startTime) : "";
7303
+ const timeStr = elapsed ? ` ${chalk10.dim(elapsed)}` : "";
7304
+ const timePlain = elapsed ? ` ${elapsed}` : "";
7305
+ const { styled: icon } = this.statusIcon(task);
7306
+ const nameStyle = task.status === "pending" ? chalk10.dim : chalk10.white;
7307
+ const msgStyle = task.status === "failed" ? chalk10.red : chalk10.dim;
7308
+ if (!this.cachedConnectors) {
7309
+ this.cachedConnectors = this.tasks.map((_, i) => this.getTreeConnector(i));
7310
+ }
7311
+ const connector = this.cachedConnectors[index];
7312
+ const connectorStyled = connector ? chalk10.dim(connector) : "";
7313
+ const paddedName = task.name.padEnd(Math.max(0, NAME_COL_WIDTH - connector.length));
7314
+ const usedByFixed = PREFIX.length + connector.length + 2 + NAME_COL_WIDTH + timePlain.length;
7195
7315
  const msgMax = Math.max(cols - usedByFixed - 2, 10);
7196
7316
  const msg = task.message ? this.smartTruncate(task.message, msgMax) : "";
7197
- return `${PREFIX}${indent}${icon} ${nameStyle(paddedName)}${msg ? msgStyle(msg) : ""}${timeStr}`;
7317
+ return `${PREFIX}${connectorStyled}${icon} ${nameStyle(paddedName)}${msg ? msgStyle(msg) : ""}${timeStr}`;
7198
7318
  }
7199
7319
  draw(initial) {
7200
7320
  const { stdout } = process;
@@ -7202,7 +7322,15 @@ var ParallelTaskDisplay = class {
7202
7322
  stdout.write(`\x1B[${this.lineCount}A`);
7203
7323
  }
7204
7324
  stdout.write("\x1B[0J");
7205
- const lines = this.tasks.map((t) => this.renderLine(t));
7325
+ const pipelineHeader = this.renderPipelineHeader();
7326
+ const taskLines = this.tasks.map((t, i) => this.renderLine(t, i));
7327
+ const lines = [];
7328
+ if (pipelineHeader.length > 0) {
7329
+ lines.push(...pipelineHeader);
7330
+ const cols = stdout.columns || 80;
7331
+ lines.push(PREFIX + chalk10.dim("\u2500".repeat(Math.min(cols - PREFIX.length * 2, 55))));
7332
+ }
7333
+ lines.push(...taskLines);
7206
7334
  if (this.waitingEnabled && this.waitingCards.length > 0 && stdout.isTTY) {
7207
7335
  const cols = stdout.columns || 80;
7208
7336
  if (this.currentCard !== this.cachedCardIndex || cols !== this.cachedCardCols || !this.cachedCardLines) {
@@ -7353,21 +7481,20 @@ async function initCommand(options) {
7353
7481
  let skillSearchResult = { results: [], contentMap: /* @__PURE__ */ new Map() };
7354
7482
  let fingerprint;
7355
7483
  const display = new ParallelTaskDisplay();
7356
- const TASK_STACK = display.add("Detecting project stack");
7357
- const TASK_CONFIG = display.add("Generating configs");
7358
- const TASK_SKILLS_GEN = display.add("Generating skills", { depth: 1 });
7359
- const TASK_SKILLS_SEARCH = wantsSkills ? display.add("Searching community skills") : -1;
7360
- const TASK_SCORE_REFINE = display.add("Validating & refining setup");
7484
+ const TASK_STACK = display.add("Detecting project stack", { pipelineLabel: "Scan" });
7485
+ const TASK_CONFIG = display.add("Generating configs", { depth: 1, pipelineLabel: "Generate" });
7486
+ const TASK_SKILLS_GEN = display.add("Generating skills", { depth: 2, pipelineLabel: "Skills" });
7487
+ const TASK_SKILLS_SEARCH = wantsSkills ? display.add("Searching community skills", { depth: 1, pipelineLabel: "Search", pipelineRow: 1 }) : -1;
7488
+ const TASK_SCORE_REFINE = display.add("Validating & refining setup", { pipelineLabel: "Validate" });
7361
7489
  display.start();
7362
7490
  display.enableWaitingContent();
7363
7491
  try {
7364
7492
  display.update(TASK_STACK, "running");
7365
7493
  fingerprint = await collectFingerprint(process.cwd());
7366
- const stackSummary = [
7367
- ...fingerprint.languages,
7368
- ...fingerprint.frameworks
7369
- ].join(", ") || "no languages";
7370
- display.update(TASK_STACK, "done", stackSummary);
7494
+ const stackParts = [...fingerprint.languages, ...fingerprint.frameworks];
7495
+ const stackSummary = stackParts.join(", ") || "no languages";
7496
+ const largeRepoNote = fingerprint.fileTree.length > 5e3 ? ` (${fingerprint.fileTree.length.toLocaleString()} files, smart sampling active)` : "";
7497
+ display.update(TASK_STACK, "done", stackSummary + largeRepoNote);
7371
7498
  trackInitProjectDiscovered(fingerprint.languages.length, fingerprint.frameworks.length, fingerprint.fileTree.length);
7372
7499
  log(options.verbose, `Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`);
7373
7500
  if (report) {
@@ -7385,7 +7512,8 @@ async function initCommand(options) {
7385
7512
  }
7386
7513
  display.update(TASK_CONFIG, "running");
7387
7514
  const generatePromise = (async () => {
7388
- const failingForDismissal = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
7515
+ let localBaseline = baselineScore;
7516
+ const failingForDismissal = localBaseline.checks.filter((c) => !c.passed && c.maxPoints > 0);
7389
7517
  if (failingForDismissal.length > 0) {
7390
7518
  display.update(TASK_CONFIG, "running", "Evaluating baseline checks...");
7391
7519
  try {
@@ -7395,7 +7523,7 @@ async function initCommand(options) {
7395
7523
  const existingIds = new Set(existing.map((d) => d.id));
7396
7524
  const merged = [...existing, ...newDismissals.filter((d) => !existingIds.has(d.id))];
7397
7525
  writeDismissedChecks(merged);
7398
- baselineScore = computeLocalScore(process.cwd(), targetAgent);
7526
+ localBaseline = computeLocalScore(process.cwd(), targetAgent);
7399
7527
  }
7400
7528
  } catch {
7401
7529
  display.update(TASK_CONFIG, "running", "Skipped dismissal evaluation");
@@ -7404,11 +7532,11 @@ async function initCommand(options) {
7404
7532
  let failingChecks;
7405
7533
  let passingChecks;
7406
7534
  let currentScore;
7407
- if (hasExistingConfig && baselineScore.score >= 95 && !options.force) {
7408
- const currentLlmFixable = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0 && !NON_LLM_CHECKS.has(c.id));
7535
+ if (hasExistingConfig && localBaseline.score >= 95 && !options.force) {
7536
+ const currentLlmFixable = localBaseline.checks.filter((c) => !c.passed && c.maxPoints > 0 && !NON_LLM_CHECKS.has(c.id));
7409
7537
  failingChecks = currentLlmFixable.map((c) => ({ name: c.name, suggestion: c.suggestion, fix: c.fix }));
7410
- passingChecks = baselineScore.checks.filter((c) => c.passed).map((c) => ({ name: c.name }));
7411
- currentScore = baselineScore.score;
7538
+ passingChecks = localBaseline.checks.filter((c) => c.passed).map((c) => ({ name: c.name }));
7539
+ currentScore = localBaseline.score;
7412
7540
  }
7413
7541
  if (report) {
7414
7542
  const fullPrompt = buildGeneratePrompt(fingerprint, targetAgent, fingerprint.description, failingChecks, currentScore, passingChecks);
@@ -7602,7 +7730,7 @@ async function initCommand(options) {
7602
7730
  }
7603
7731
  const writeSpinner = ora3("Writing config files...").start();
7604
7732
  try {
7605
- if (targetAgent.includes("codex") && !fs25.existsSync("AGENTS.md") && !generatedSetup.codex) {
7733
+ if (targetAgent.includes("codex") && !fs26.existsSync("AGENTS.md") && !generatedSetup.codex) {
7606
7734
  const claude = generatedSetup.claude;
7607
7735
  const cursor = generatedSetup.cursor;
7608
7736
  const agentRefs = [];
@@ -7786,9 +7914,9 @@ ${agentRefs.join(" ")}
7786
7914
  }
7787
7915
  if (report) {
7788
7916
  report.markStep("Finished");
7789
- const reportPath = path20.join(process.cwd(), ".caliber", "debug-report.md");
7917
+ const reportPath = path21.join(process.cwd(), ".caliber", "debug-report.md");
7790
7918
  report.write(reportPath);
7791
- console.log(chalk11.dim(` Debug report written to ${path20.relative(process.cwd(), reportPath)}
7919
+ console.log(chalk11.dim(` Debug report written to ${path21.relative(process.cwd(), reportPath)}
7792
7920
  `));
7793
7921
  }
7794
7922
  }
@@ -7835,7 +7963,7 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
7835
7963
  }
7836
7964
  function summarizeSetup(action, setup) {
7837
7965
  const descriptions = setup.fileDescriptions;
7838
- const files = descriptions ? Object.entries(descriptions).map(([path29, desc]) => ` ${path29}: ${desc}`).join("\n") : Object.keys(setup).filter((k) => k !== "targetAgent" && k !== "fileDescriptions").join(", ");
7966
+ const files = descriptions ? Object.entries(descriptions).map(([path30, desc]) => ` ${path30}: ${desc}`).join("\n") : Object.keys(setup).filter((k) => k !== "targetAgent" && k !== "fileDescriptions").join(", ");
7839
7967
  return `${action}. Files:
7840
7968
  ${files}`;
7841
7969
  }
@@ -7969,7 +8097,7 @@ function printSetupSummary(setup) {
7969
8097
  };
7970
8098
  if (claude) {
7971
8099
  if (claude.claudeMd) {
7972
- const icon = fs25.existsSync("CLAUDE.md") ? chalk11.yellow("~") : chalk11.green("+");
8100
+ const icon = fs26.existsSync("CLAUDE.md") ? chalk11.yellow("~") : chalk11.green("+");
7973
8101
  const desc = getDescription("CLAUDE.md");
7974
8102
  console.log(` ${icon} ${chalk11.bold("CLAUDE.md")}`);
7975
8103
  if (desc) console.log(chalk11.dim(` ${desc}`));
@@ -7979,7 +8107,7 @@ function printSetupSummary(setup) {
7979
8107
  if (Array.isArray(skills) && skills.length > 0) {
7980
8108
  for (const skill of skills) {
7981
8109
  const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
7982
- const icon = fs25.existsSync(skillPath) ? chalk11.yellow("~") : chalk11.green("+");
8110
+ const icon = fs26.existsSync(skillPath) ? chalk11.yellow("~") : chalk11.green("+");
7983
8111
  const desc = getDescription(skillPath);
7984
8112
  console.log(` ${icon} ${chalk11.bold(skillPath)}`);
7985
8113
  console.log(chalk11.dim(` ${desc || skill.description || skill.name}`));
@@ -7990,7 +8118,7 @@ function printSetupSummary(setup) {
7990
8118
  const codex = setup.codex;
7991
8119
  if (codex) {
7992
8120
  if (codex.agentsMd) {
7993
- const icon = fs25.existsSync("AGENTS.md") ? chalk11.yellow("~") : chalk11.green("+");
8121
+ const icon = fs26.existsSync("AGENTS.md") ? chalk11.yellow("~") : chalk11.green("+");
7994
8122
  const desc = getDescription("AGENTS.md");
7995
8123
  console.log(` ${icon} ${chalk11.bold("AGENTS.md")}`);
7996
8124
  if (desc) console.log(chalk11.dim(` ${desc}`));
@@ -8000,7 +8128,7 @@ function printSetupSummary(setup) {
8000
8128
  if (Array.isArray(codexSkills) && codexSkills.length > 0) {
8001
8129
  for (const skill of codexSkills) {
8002
8130
  const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
8003
- const icon = fs25.existsSync(skillPath) ? chalk11.yellow("~") : chalk11.green("+");
8131
+ const icon = fs26.existsSync(skillPath) ? chalk11.yellow("~") : chalk11.green("+");
8004
8132
  const desc = getDescription(skillPath);
8005
8133
  console.log(` ${icon} ${chalk11.bold(skillPath)}`);
8006
8134
  console.log(chalk11.dim(` ${desc || skill.description || skill.name}`));
@@ -8010,7 +8138,7 @@ function printSetupSummary(setup) {
8010
8138
  }
8011
8139
  if (cursor) {
8012
8140
  if (cursor.cursorrules) {
8013
- const icon = fs25.existsSync(".cursorrules") ? chalk11.yellow("~") : chalk11.green("+");
8141
+ const icon = fs26.existsSync(".cursorrules") ? chalk11.yellow("~") : chalk11.green("+");
8014
8142
  const desc = getDescription(".cursorrules");
8015
8143
  console.log(` ${icon} ${chalk11.bold(".cursorrules")}`);
8016
8144
  if (desc) console.log(chalk11.dim(` ${desc}`));
@@ -8020,7 +8148,7 @@ function printSetupSummary(setup) {
8020
8148
  if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
8021
8149
  for (const skill of cursorSkills) {
8022
8150
  const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
8023
- const icon = fs25.existsSync(skillPath) ? chalk11.yellow("~") : chalk11.green("+");
8151
+ const icon = fs26.existsSync(skillPath) ? chalk11.yellow("~") : chalk11.green("+");
8024
8152
  const desc = getDescription(skillPath);
8025
8153
  console.log(` ${icon} ${chalk11.bold(skillPath)}`);
8026
8154
  console.log(chalk11.dim(` ${desc || skill.description || skill.name}`));
@@ -8031,7 +8159,7 @@ function printSetupSummary(setup) {
8031
8159
  if (Array.isArray(rules) && rules.length > 0) {
8032
8160
  for (const rule of rules) {
8033
8161
  const rulePath = `.cursor/rules/${rule.filename}`;
8034
- const icon = fs25.existsSync(rulePath) ? chalk11.yellow("~") : chalk11.green("+");
8162
+ const icon = fs26.existsSync(rulePath) ? chalk11.yellow("~") : chalk11.green("+");
8035
8163
  const desc = getDescription(rulePath);
8036
8164
  console.log(` ${icon} ${chalk11.bold(rulePath)}`);
8037
8165
  if (desc) {
@@ -8095,8 +8223,8 @@ function ensurePermissions(fingerprint) {
8095
8223
  const settingsPath = ".claude/settings.json";
8096
8224
  let settings = {};
8097
8225
  try {
8098
- if (fs25.existsSync(settingsPath)) {
8099
- settings = JSON.parse(fs25.readFileSync(settingsPath, "utf-8"));
8226
+ if (fs26.existsSync(settingsPath)) {
8227
+ settings = JSON.parse(fs26.readFileSync(settingsPath, "utf-8"));
8100
8228
  }
8101
8229
  } catch {
8102
8230
  }
@@ -8105,8 +8233,8 @@ function ensurePermissions(fingerprint) {
8105
8233
  if (Array.isArray(allow) && allow.length > 0) return;
8106
8234
  permissions.allow = derivePermissions(fingerprint);
8107
8235
  settings.permissions = permissions;
8108
- if (!fs25.existsSync(".claude")) fs25.mkdirSync(".claude", { recursive: true });
8109
- fs25.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
8236
+ if (!fs26.existsSync(".claude")) fs26.mkdirSync(".claude", { recursive: true });
8237
+ fs26.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
8110
8238
  }
8111
8239
  function displayTokenUsage() {
8112
8240
  const summary = getUsageSummary();
@@ -8130,7 +8258,7 @@ function displayTokenUsage() {
8130
8258
  }
8131
8259
  function writeErrorLog(config, rawOutput, error, stopReason) {
8132
8260
  try {
8133
- const logPath = path20.join(process.cwd(), ".caliber", "error-log.md");
8261
+ const logPath = path21.join(process.cwd(), ".caliber", "error-log.md");
8134
8262
  const lines = [
8135
8263
  `# Generation Error \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
8136
8264
  "",
@@ -8143,8 +8271,8 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
8143
8271
  lines.push("## Error", "```", error, "```", "");
8144
8272
  }
8145
8273
  lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
8146
- fs25.mkdirSync(path20.join(process.cwd(), ".caliber"), { recursive: true });
8147
- fs25.writeFileSync(logPath, lines.join("\n"));
8274
+ fs26.mkdirSync(path21.join(process.cwd(), ".caliber"), { recursive: true });
8275
+ fs26.writeFileSync(logPath, lines.join("\n"));
8148
8276
  console.log(chalk11.dim(`
8149
8277
  Error log written to .caliber/error-log.md`));
8150
8278
  } catch {
@@ -8185,7 +8313,7 @@ function undoCommand() {
8185
8313
 
8186
8314
  // src/commands/status.ts
8187
8315
  import chalk13 from "chalk";
8188
- import fs26 from "fs";
8316
+ import fs27 from "fs";
8189
8317
  init_config();
8190
8318
  async function statusCommand(options) {
8191
8319
  const config = loadConfig();
@@ -8212,7 +8340,7 @@ async function statusCommand(options) {
8212
8340
  }
8213
8341
  console.log(` Files managed: ${chalk13.cyan(manifest.entries.length.toString())}`);
8214
8342
  for (const entry of manifest.entries) {
8215
- const exists = fs26.existsSync(entry.path);
8343
+ const exists = fs27.existsSync(entry.path);
8216
8344
  const icon = exists ? chalk13.green("\u2713") : chalk13.red("\u2717");
8217
8345
  console.log(` ${icon} ${entry.path} (${entry.action})`);
8218
8346
  }
@@ -8394,13 +8522,13 @@ async function scoreCommand(options) {
8394
8522
  }
8395
8523
 
8396
8524
  // src/commands/refresh.ts
8397
- import fs30 from "fs";
8398
- import path24 from "path";
8525
+ import fs31 from "fs";
8526
+ import path25 from "path";
8399
8527
  import chalk16 from "chalk";
8400
8528
  import ora6 from "ora";
8401
8529
 
8402
8530
  // src/lib/git-diff.ts
8403
- import { execSync as execSync13 } from "child_process";
8531
+ import { execSync as execSync14 } from "child_process";
8404
8532
  var MAX_DIFF_BYTES = 1e5;
8405
8533
  var DOC_PATTERNS = [
8406
8534
  "CLAUDE.md",
@@ -8415,7 +8543,7 @@ function excludeArgs() {
8415
8543
  }
8416
8544
  function safeExec(cmd) {
8417
8545
  try {
8418
- return execSync13(cmd, {
8546
+ return execSync14(cmd, {
8419
8547
  encoding: "utf-8",
8420
8548
  stdio: ["pipe", "pipe", "pipe"],
8421
8549
  maxBuffer: 10 * 1024 * 1024
@@ -8473,37 +8601,37 @@ function collectDiff(lastSha) {
8473
8601
  }
8474
8602
 
8475
8603
  // src/writers/refresh.ts
8476
- import fs27 from "fs";
8477
- import path21 from "path";
8604
+ import fs28 from "fs";
8605
+ import path22 from "path";
8478
8606
  function writeRefreshDocs(docs) {
8479
8607
  const written = [];
8480
8608
  if (docs.claudeMd) {
8481
- fs27.writeFileSync("CLAUDE.md", docs.claudeMd);
8609
+ fs28.writeFileSync("CLAUDE.md", docs.claudeMd);
8482
8610
  written.push("CLAUDE.md");
8483
8611
  }
8484
8612
  if (docs.readmeMd) {
8485
- fs27.writeFileSync("README.md", docs.readmeMd);
8613
+ fs28.writeFileSync("README.md", docs.readmeMd);
8486
8614
  written.push("README.md");
8487
8615
  }
8488
8616
  if (docs.cursorrules) {
8489
- fs27.writeFileSync(".cursorrules", docs.cursorrules);
8617
+ fs28.writeFileSync(".cursorrules", docs.cursorrules);
8490
8618
  written.push(".cursorrules");
8491
8619
  }
8492
8620
  if (docs.cursorRules) {
8493
- const rulesDir = path21.join(".cursor", "rules");
8494
- if (!fs27.existsSync(rulesDir)) fs27.mkdirSync(rulesDir, { recursive: true });
8621
+ const rulesDir = path22.join(".cursor", "rules");
8622
+ if (!fs28.existsSync(rulesDir)) fs28.mkdirSync(rulesDir, { recursive: true });
8495
8623
  for (const rule of docs.cursorRules) {
8496
- const filePath = path21.join(rulesDir, rule.filename);
8497
- fs27.writeFileSync(filePath, rule.content);
8624
+ const filePath = path22.join(rulesDir, rule.filename);
8625
+ fs28.writeFileSync(filePath, rule.content);
8498
8626
  written.push(filePath);
8499
8627
  }
8500
8628
  }
8501
8629
  if (docs.claudeSkills) {
8502
- const skillsDir = path21.join(".claude", "skills");
8503
- if (!fs27.existsSync(skillsDir)) fs27.mkdirSync(skillsDir, { recursive: true });
8630
+ const skillsDir = path22.join(".claude", "skills");
8631
+ if (!fs28.existsSync(skillsDir)) fs28.mkdirSync(skillsDir, { recursive: true });
8504
8632
  for (const skill of docs.claudeSkills) {
8505
- const filePath = path21.join(skillsDir, skill.filename);
8506
- fs27.writeFileSync(filePath, skill.content);
8633
+ const filePath = path22.join(skillsDir, skill.filename);
8634
+ fs28.writeFileSync(filePath, skill.content);
8507
8635
  written.push(filePath);
8508
8636
  }
8509
8637
  }
@@ -8580,8 +8708,8 @@ Changed files: ${diff.changedFiles.join(", ")}`);
8580
8708
  }
8581
8709
 
8582
8710
  // src/learner/writer.ts
8583
- import fs28 from "fs";
8584
- import path22 from "path";
8711
+ import fs29 from "fs";
8712
+ import path23 from "path";
8585
8713
  var LEARNINGS_FILE = "CALIBER_LEARNINGS.md";
8586
8714
  var LEARNINGS_HEADER = `# Caliber Learnings
8587
8715
 
@@ -8665,16 +8793,16 @@ function deduplicateLearnedItems(existing, incoming) {
8665
8793
  function writeLearnedSection(content) {
8666
8794
  const existingSection = readLearnedSection();
8667
8795
  const { merged, newCount, newItems } = deduplicateLearnedItems(existingSection, content);
8668
- fs28.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + merged + "\n");
8796
+ fs29.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + merged + "\n");
8669
8797
  return { newCount, newItems };
8670
8798
  }
8671
8799
  function writeLearnedSkill(skill) {
8672
- const skillDir = path22.join(".claude", "skills", skill.name);
8673
- if (!fs28.existsSync(skillDir)) fs28.mkdirSync(skillDir, { recursive: true });
8674
- const skillPath = path22.join(skillDir, "SKILL.md");
8675
- if (!skill.isNew && fs28.existsSync(skillPath)) {
8676
- const existing = fs28.readFileSync(skillPath, "utf-8");
8677
- fs28.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
8800
+ const skillDir = path23.join(".claude", "skills", skill.name);
8801
+ if (!fs29.existsSync(skillDir)) fs29.mkdirSync(skillDir, { recursive: true });
8802
+ const skillPath = path23.join(skillDir, "SKILL.md");
8803
+ if (!skill.isNew && fs29.existsSync(skillPath)) {
8804
+ const existing = fs29.readFileSync(skillPath, "utf-8");
8805
+ fs29.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
8678
8806
  } else {
8679
8807
  const frontmatter = [
8680
8808
  "---",
@@ -8683,37 +8811,37 @@ function writeLearnedSkill(skill) {
8683
8811
  "---",
8684
8812
  ""
8685
8813
  ].join("\n");
8686
- fs28.writeFileSync(skillPath, frontmatter + skill.content);
8814
+ fs29.writeFileSync(skillPath, frontmatter + skill.content);
8687
8815
  }
8688
8816
  return skillPath;
8689
8817
  }
8690
8818
  function readLearnedSection() {
8691
- if (fs28.existsSync(LEARNINGS_FILE)) {
8692
- const content2 = fs28.readFileSync(LEARNINGS_FILE, "utf-8");
8819
+ if (fs29.existsSync(LEARNINGS_FILE)) {
8820
+ const content2 = fs29.readFileSync(LEARNINGS_FILE, "utf-8");
8693
8821
  const bullets = content2.split("\n").filter((l) => l.startsWith("- ")).join("\n");
8694
8822
  return bullets || null;
8695
8823
  }
8696
8824
  const claudeMdPath = "CLAUDE.md";
8697
- if (!fs28.existsSync(claudeMdPath)) return null;
8698
- const content = fs28.readFileSync(claudeMdPath, "utf-8");
8825
+ if (!fs29.existsSync(claudeMdPath)) return null;
8826
+ const content = fs29.readFileSync(claudeMdPath, "utf-8");
8699
8827
  const startIdx = content.indexOf(LEARNED_START);
8700
8828
  const endIdx = content.indexOf(LEARNED_END);
8701
8829
  if (startIdx === -1 || endIdx === -1) return null;
8702
8830
  return content.slice(startIdx + LEARNED_START.length, endIdx).trim() || null;
8703
8831
  }
8704
8832
  function migrateInlineLearnings() {
8705
- if (fs28.existsSync(LEARNINGS_FILE)) return false;
8833
+ if (fs29.existsSync(LEARNINGS_FILE)) return false;
8706
8834
  const claudeMdPath = "CLAUDE.md";
8707
- if (!fs28.existsSync(claudeMdPath)) return false;
8708
- const content = fs28.readFileSync(claudeMdPath, "utf-8");
8835
+ if (!fs29.existsSync(claudeMdPath)) return false;
8836
+ const content = fs29.readFileSync(claudeMdPath, "utf-8");
8709
8837
  const startIdx = content.indexOf(LEARNED_START);
8710
8838
  const endIdx = content.indexOf(LEARNED_END);
8711
8839
  if (startIdx === -1 || endIdx === -1) return false;
8712
8840
  const section = content.slice(startIdx + LEARNED_START.length, endIdx).trim();
8713
8841
  if (!section) return false;
8714
- fs28.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
8842
+ fs29.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
8715
8843
  const cleaned = content.slice(0, startIdx) + content.slice(endIdx + LEARNED_END.length);
8716
- fs28.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
8844
+ fs29.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
8717
8845
  return true;
8718
8846
  }
8719
8847
 
@@ -8725,11 +8853,11 @@ function log2(quiet, ...args) {
8725
8853
  function discoverGitRepos(parentDir) {
8726
8854
  const repos = [];
8727
8855
  try {
8728
- const entries = fs30.readdirSync(parentDir, { withFileTypes: true });
8856
+ const entries = fs31.readdirSync(parentDir, { withFileTypes: true });
8729
8857
  for (const entry of entries) {
8730
8858
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
8731
- const childPath = path24.join(parentDir, entry.name);
8732
- if (fs30.existsSync(path24.join(childPath, ".git"))) {
8859
+ const childPath = path25.join(parentDir, entry.name);
8860
+ if (fs31.existsSync(path25.join(childPath, ".git"))) {
8733
8861
  repos.push(childPath);
8734
8862
  }
8735
8863
  }
@@ -8832,7 +8960,7 @@ async function refreshCommand(options) {
8832
8960
  `));
8833
8961
  const originalDir = process.cwd();
8834
8962
  for (const repo of repos) {
8835
- const repoName = path24.basename(repo);
8963
+ const repoName = path25.basename(repo);
8836
8964
  try {
8837
8965
  process.chdir(repo);
8838
8966
  await refreshSingleRepo(repo, { ...options, label: repoName });
@@ -9056,7 +9184,7 @@ async function configCommand() {
9056
9184
  }
9057
9185
 
9058
9186
  // src/commands/learn.ts
9059
- import fs33 from "fs";
9187
+ import fs34 from "fs";
9060
9188
  import chalk19 from "chalk";
9061
9189
 
9062
9190
  // src/learner/stdin.ts
@@ -9088,8 +9216,8 @@ function readStdin() {
9088
9216
 
9089
9217
  // src/learner/storage.ts
9090
9218
  init_constants();
9091
- import fs31 from "fs";
9092
- import path25 from "path";
9219
+ import fs32 from "fs";
9220
+ import path26 from "path";
9093
9221
  var MAX_RESPONSE_LENGTH = 2e3;
9094
9222
  var DEFAULT_STATE = {
9095
9223
  sessionId: null,
@@ -9097,15 +9225,15 @@ var DEFAULT_STATE = {
9097
9225
  lastAnalysisTimestamp: null
9098
9226
  };
9099
9227
  function ensureLearningDir() {
9100
- if (!fs31.existsSync(LEARNING_DIR)) {
9101
- fs31.mkdirSync(LEARNING_DIR, { recursive: true });
9228
+ if (!fs32.existsSync(LEARNING_DIR)) {
9229
+ fs32.mkdirSync(LEARNING_DIR, { recursive: true });
9102
9230
  }
9103
9231
  }
9104
9232
  function sessionFilePath() {
9105
- return path25.join(LEARNING_DIR, LEARNING_SESSION_FILE);
9233
+ return path26.join(LEARNING_DIR, LEARNING_SESSION_FILE);
9106
9234
  }
9107
9235
  function stateFilePath() {
9108
- return path25.join(LEARNING_DIR, LEARNING_STATE_FILE);
9236
+ return path26.join(LEARNING_DIR, LEARNING_STATE_FILE);
9109
9237
  }
9110
9238
  function truncateResponse(response) {
9111
9239
  const str = JSON.stringify(response);
@@ -9116,29 +9244,29 @@ function appendEvent(event) {
9116
9244
  ensureLearningDir();
9117
9245
  const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
9118
9246
  const filePath = sessionFilePath();
9119
- fs31.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
9247
+ fs32.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
9120
9248
  const count = getEventCount();
9121
9249
  if (count > LEARNING_MAX_EVENTS) {
9122
- const lines = fs31.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9250
+ const lines = fs32.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9123
9251
  const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
9124
- fs31.writeFileSync(filePath, kept.join("\n") + "\n");
9252
+ fs32.writeFileSync(filePath, kept.join("\n") + "\n");
9125
9253
  }
9126
9254
  }
9127
9255
  function appendPromptEvent(event) {
9128
9256
  ensureLearningDir();
9129
9257
  const filePath = sessionFilePath();
9130
- fs31.appendFileSync(filePath, JSON.stringify(event) + "\n");
9258
+ fs32.appendFileSync(filePath, JSON.stringify(event) + "\n");
9131
9259
  const count = getEventCount();
9132
9260
  if (count > LEARNING_MAX_EVENTS) {
9133
- const lines = fs31.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9261
+ const lines = fs32.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9134
9262
  const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
9135
- fs31.writeFileSync(filePath, kept.join("\n") + "\n");
9263
+ fs32.writeFileSync(filePath, kept.join("\n") + "\n");
9136
9264
  }
9137
9265
  }
9138
9266
  function readAllEvents() {
9139
9267
  const filePath = sessionFilePath();
9140
- if (!fs31.existsSync(filePath)) return [];
9141
- const lines = fs31.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9268
+ if (!fs32.existsSync(filePath)) return [];
9269
+ const lines = fs32.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9142
9270
  const events = [];
9143
9271
  for (const line of lines) {
9144
9272
  try {
@@ -9150,26 +9278,26 @@ function readAllEvents() {
9150
9278
  }
9151
9279
  function getEventCount() {
9152
9280
  const filePath = sessionFilePath();
9153
- if (!fs31.existsSync(filePath)) return 0;
9154
- const content = fs31.readFileSync(filePath, "utf-8");
9281
+ if (!fs32.existsSync(filePath)) return 0;
9282
+ const content = fs32.readFileSync(filePath, "utf-8");
9155
9283
  return content.split("\n").filter(Boolean).length;
9156
9284
  }
9157
9285
  function clearSession() {
9158
9286
  const filePath = sessionFilePath();
9159
- if (fs31.existsSync(filePath)) fs31.unlinkSync(filePath);
9287
+ if (fs32.existsSync(filePath)) fs32.unlinkSync(filePath);
9160
9288
  }
9161
9289
  function readState2() {
9162
9290
  const filePath = stateFilePath();
9163
- if (!fs31.existsSync(filePath)) return { ...DEFAULT_STATE };
9291
+ if (!fs32.existsSync(filePath)) return { ...DEFAULT_STATE };
9164
9292
  try {
9165
- return JSON.parse(fs31.readFileSync(filePath, "utf-8"));
9293
+ return JSON.parse(fs32.readFileSync(filePath, "utf-8"));
9166
9294
  } catch {
9167
9295
  return { ...DEFAULT_STATE };
9168
9296
  }
9169
9297
  }
9170
9298
  function writeState2(state) {
9171
9299
  ensureLearningDir();
9172
- fs31.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
9300
+ fs32.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
9173
9301
  }
9174
9302
  function resetState() {
9175
9303
  writeState2({ ...DEFAULT_STATE });
@@ -9177,14 +9305,14 @@ function resetState() {
9177
9305
  var LOCK_FILE2 = "finalize.lock";
9178
9306
  var LOCK_STALE_MS = 5 * 60 * 1e3;
9179
9307
  function lockFilePath() {
9180
- return path25.join(LEARNING_DIR, LOCK_FILE2);
9308
+ return path26.join(LEARNING_DIR, LOCK_FILE2);
9181
9309
  }
9182
9310
  function acquireFinalizeLock() {
9183
9311
  ensureLearningDir();
9184
9312
  const lockPath = lockFilePath();
9185
- if (fs31.existsSync(lockPath)) {
9313
+ if (fs32.existsSync(lockPath)) {
9186
9314
  try {
9187
- const stat = fs31.statSync(lockPath);
9315
+ const stat = fs32.statSync(lockPath);
9188
9316
  if (Date.now() - stat.mtimeMs < LOCK_STALE_MS) {
9189
9317
  return false;
9190
9318
  }
@@ -9192,7 +9320,7 @@ function acquireFinalizeLock() {
9192
9320
  }
9193
9321
  }
9194
9322
  try {
9195
- fs31.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
9323
+ fs32.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
9196
9324
  return true;
9197
9325
  } catch {
9198
9326
  return false;
@@ -9201,7 +9329,7 @@ function acquireFinalizeLock() {
9201
9329
  function releaseFinalizeLock() {
9202
9330
  const lockPath = lockFilePath();
9203
9331
  try {
9204
- if (fs31.existsSync(lockPath)) fs31.unlinkSync(lockPath);
9332
+ if (fs32.existsSync(lockPath)) fs32.unlinkSync(lockPath);
9205
9333
  } catch {
9206
9334
  }
9207
9335
  }
@@ -9247,7 +9375,7 @@ function sanitizeSecrets(text) {
9247
9375
 
9248
9376
  // src/ai/learn.ts
9249
9377
  init_config();
9250
- var MAX_PROMPT_TOKENS2 = 1e5;
9378
+ var MAX_PROMPT_TOKENS = 1e5;
9251
9379
  function formatEventsForPrompt(events) {
9252
9380
  return events.map((e, i) => {
9253
9381
  if (e.hook_event_name === "UserPromptSubmit") {
@@ -9295,7 +9423,7 @@ function parseAnalysisResponse(raw) {
9295
9423
  }
9296
9424
  }
9297
9425
  async function analyzeEvents(events, existingClaudeMd, existingLearnedSection, existingSkills) {
9298
- const fittedEvents = trimEventsToFit(events, MAX_PROMPT_TOKENS2 - 1e4);
9426
+ const fittedEvents = trimEventsToFit(events, MAX_PROMPT_TOKENS - 1e4);
9299
9427
  const eventsText = formatEventsForPrompt(fittedEvents);
9300
9428
  const contextParts = [];
9301
9429
  if (existingClaudeMd) {
@@ -9357,8 +9485,8 @@ init_config();
9357
9485
 
9358
9486
  // src/learner/roi.ts
9359
9487
  init_constants();
9360
- import fs32 from "fs";
9361
- import path26 from "path";
9488
+ import fs33 from "fs";
9489
+ import path27 from "path";
9362
9490
  var DEFAULT_TOTALS = {
9363
9491
  totalWasteTokens: 0,
9364
9492
  totalWasteSeconds: 0,
@@ -9372,22 +9500,22 @@ var DEFAULT_TOTALS = {
9372
9500
  lastSessionTimestamp: ""
9373
9501
  };
9374
9502
  function roiFilePath() {
9375
- return path26.join(LEARNING_DIR, LEARNING_ROI_FILE);
9503
+ return path27.join(LEARNING_DIR, LEARNING_ROI_FILE);
9376
9504
  }
9377
9505
  function readROIStats() {
9378
9506
  const filePath = roiFilePath();
9379
- if (!fs32.existsSync(filePath)) {
9507
+ if (!fs33.existsSync(filePath)) {
9380
9508
  return { learnings: [], sessions: [], totals: { ...DEFAULT_TOTALS } };
9381
9509
  }
9382
9510
  try {
9383
- return JSON.parse(fs32.readFileSync(filePath, "utf-8"));
9511
+ return JSON.parse(fs33.readFileSync(filePath, "utf-8"));
9384
9512
  } catch {
9385
9513
  return { learnings: [], sessions: [], totals: { ...DEFAULT_TOTALS } };
9386
9514
  }
9387
9515
  }
9388
9516
  function writeROIStats(stats) {
9389
9517
  ensureLearningDir();
9390
- fs32.writeFileSync(roiFilePath(), JSON.stringify(stats, null, 2));
9518
+ fs33.writeFileSync(roiFilePath(), JSON.stringify(stats, null, 2));
9391
9519
  }
9392
9520
  function recalculateTotals(stats) {
9393
9521
  const totals = stats.totals;
@@ -9640,7 +9768,7 @@ async function learnFinalizeCommand(options) {
9640
9768
  }
9641
9769
  async function learnInstallCommand() {
9642
9770
  let anyInstalled = false;
9643
- if (fs33.existsSync(".claude")) {
9771
+ if (fs34.existsSync(".claude")) {
9644
9772
  const r = installLearningHooks();
9645
9773
  if (r.installed) {
9646
9774
  console.log(chalk19.green("\u2713") + " Claude Code learning hooks installed");
@@ -9649,7 +9777,7 @@ async function learnInstallCommand() {
9649
9777
  console.log(chalk19.dim(" Claude Code hooks already installed"));
9650
9778
  }
9651
9779
  }
9652
- if (fs33.existsSync(".cursor")) {
9780
+ if (fs34.existsSync(".cursor")) {
9653
9781
  const r = installCursorLearningHooks();
9654
9782
  if (r.installed) {
9655
9783
  console.log(chalk19.green("\u2713") + " Cursor learning hooks installed");
@@ -9658,7 +9786,7 @@ async function learnInstallCommand() {
9658
9786
  console.log(chalk19.dim(" Cursor hooks already installed"));
9659
9787
  }
9660
9788
  }
9661
- if (!fs33.existsSync(".claude") && !fs33.existsSync(".cursor")) {
9789
+ if (!fs34.existsSync(".claude") && !fs34.existsSync(".cursor")) {
9662
9790
  console.log(chalk19.yellow("No .claude/ or .cursor/ directory found."));
9663
9791
  console.log(chalk19.dim(" Run `caliber init` first, or create the directory manually."));
9664
9792
  return;
@@ -9730,9 +9858,9 @@ Learned items in CALIBER_LEARNINGS.md: ${chalk19.cyan(String(lineCount))}`);
9730
9858
  }
9731
9859
 
9732
9860
  // src/cli.ts
9733
- var __dirname = path27.dirname(fileURLToPath(import.meta.url));
9861
+ var __dirname = path28.dirname(fileURLToPath(import.meta.url));
9734
9862
  var pkg = JSON.parse(
9735
- fs34.readFileSync(path27.resolve(__dirname, "..", "package.json"), "utf-8")
9863
+ fs35.readFileSync(path28.resolve(__dirname, "..", "package.json"), "utf-8")
9736
9864
  );
9737
9865
  var program = new Command();
9738
9866
  var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
@@ -9806,16 +9934,16 @@ learn.command("remove").description("Remove learning hooks from .claude/settings
9806
9934
  learn.command("status").description("Show learning system status").action(tracked("learn:status", learnStatusCommand));
9807
9935
 
9808
9936
  // src/utils/version-check.ts
9809
- import fs35 from "fs";
9810
- import path28 from "path";
9937
+ import fs36 from "fs";
9938
+ import path29 from "path";
9811
9939
  import { fileURLToPath as fileURLToPath2 } from "url";
9812
- import { execSync as execSync14 } from "child_process";
9940
+ import { execSync as execSync15 } from "child_process";
9813
9941
  import chalk20 from "chalk";
9814
9942
  import ora7 from "ora";
9815
9943
  import confirm2 from "@inquirer/confirm";
9816
- var __dirname_vc = path28.dirname(fileURLToPath2(import.meta.url));
9944
+ var __dirname_vc = path29.dirname(fileURLToPath2(import.meta.url));
9817
9945
  var pkg2 = JSON.parse(
9818
- fs35.readFileSync(path28.resolve(__dirname_vc, "..", "package.json"), "utf-8")
9946
+ fs36.readFileSync(path29.resolve(__dirname_vc, "..", "package.json"), "utf-8")
9819
9947
  );
9820
9948
  function getChannel(version) {
9821
9949
  const match = version.match(/-(dev|next)\./);
@@ -9839,9 +9967,9 @@ function isNewer(registry, current) {
9839
9967
  }
9840
9968
  function getInstalledVersion() {
9841
9969
  try {
9842
- const globalRoot = execSync14("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
9843
- const pkgPath = path28.join(globalRoot, "@rely-ai", "caliber", "package.json");
9844
- return JSON.parse(fs35.readFileSync(pkgPath, "utf-8")).version;
9970
+ const globalRoot = execSync15("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
9971
+ const pkgPath = path29.join(globalRoot, "@rely-ai", "caliber", "package.json");
9972
+ return JSON.parse(fs36.readFileSync(pkgPath, "utf-8")).version;
9845
9973
  } catch {
9846
9974
  return null;
9847
9975
  }
@@ -9887,7 +10015,7 @@ Update available: ${current} -> ${latest}`)
9887
10015
  const tag = channel === "latest" ? latest : channel;
9888
10016
  const spinner = ora7("Updating caliber...").start();
9889
10017
  try {
9890
- execSync14(`npm install -g @rely-ai/caliber@${tag}`, {
10018
+ execSync15(`npm install -g @rely-ai/caliber@${tag}`, {
9891
10019
  stdio: "pipe",
9892
10020
  timeout: 12e4,
9893
10021
  env: { ...process.env, npm_config_fund: "false", npm_config_audit: "false" }
@@ -9904,7 +10032,7 @@ Update available: ${current} -> ${latest}`)
9904
10032
  console.log(chalk20.dim(`
9905
10033
  Restarting: caliber ${args.join(" ")}
9906
10034
  `));
9907
- execSync14(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
10035
+ execSync15(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
9908
10036
  stdio: "inherit",
9909
10037
  env: { ...process.env, CALIBER_SKIP_UPDATE_CHECK: "1" }
9910
10038
  });