@rely-ai/caliber 1.23.0-dev.1773792440 → 1.23.0-dev.1773794235

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 +788 -556
  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",
@@ -145,6 +169,22 @@ var init_config = __esm({
145
169
  }
146
170
  });
147
171
 
172
+ // src/llm/types.ts
173
+ var types_exports = {};
174
+ __export(types_exports, {
175
+ isSeatBased: () => isSeatBased
176
+ });
177
+ function isSeatBased(provider) {
178
+ return SEAT_BASED_PROVIDERS.has(provider);
179
+ }
180
+ var SEAT_BASED_PROVIDERS;
181
+ var init_types = __esm({
182
+ "src/llm/types.ts"() {
183
+ "use strict";
184
+ SEAT_BASED_PROVIDERS = /* @__PURE__ */ new Set(["cursor", "claude-cli"]);
185
+ }
186
+ });
187
+
148
188
  // src/constants.ts
149
189
  var constants_exports = {};
150
190
  __export(constants_exports, {
@@ -158,17 +198,17 @@ __export(constants_exports, {
158
198
  LEARNING_STATE_FILE: () => LEARNING_STATE_FILE,
159
199
  MANIFEST_FILE: () => MANIFEST_FILE
160
200
  });
161
- import path9 from "path";
201
+ import path10 from "path";
162
202
  import os3 from "os";
163
203
  var AUTH_DIR, CALIBER_DIR, MANIFEST_FILE, BACKUPS_DIR, LEARNING_DIR, LEARNING_SESSION_FILE, LEARNING_STATE_FILE, LEARNING_MAX_EVENTS, LEARNING_ROI_FILE;
164
204
  var init_constants = __esm({
165
205
  "src/constants.ts"() {
166
206
  "use strict";
167
- AUTH_DIR = path9.join(os3.homedir(), ".caliber");
207
+ AUTH_DIR = path10.join(os3.homedir(), ".caliber");
168
208
  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");
209
+ MANIFEST_FILE = path10.join(CALIBER_DIR, "manifest.json");
210
+ BACKUPS_DIR = path10.join(CALIBER_DIR, "backups");
211
+ LEARNING_DIR = path10.join(CALIBER_DIR, "learning");
172
212
  LEARNING_SESSION_FILE = "current-session.jsonl";
173
213
  LEARNING_STATE_FILE = "state.json";
174
214
  LEARNING_MAX_EVENTS = 500;
@@ -183,13 +223,13 @@ __export(lock_exports, {
183
223
  isCaliberRunning: () => isCaliberRunning,
184
224
  releaseLock: () => releaseLock
185
225
  });
186
- import fs29 from "fs";
187
- import path23 from "path";
226
+ import fs30 from "fs";
227
+ import path24 from "path";
188
228
  import os6 from "os";
189
229
  function isCaliberRunning() {
190
230
  try {
191
- if (!fs29.existsSync(LOCK_FILE)) return false;
192
- const raw = fs29.readFileSync(LOCK_FILE, "utf-8").trim();
231
+ if (!fs30.existsSync(LOCK_FILE)) return false;
232
+ const raw = fs30.readFileSync(LOCK_FILE, "utf-8").trim();
193
233
  const { pid, ts } = JSON.parse(raw);
194
234
  if (Date.now() - ts > STALE_MS) return false;
195
235
  try {
@@ -204,13 +244,13 @@ function isCaliberRunning() {
204
244
  }
205
245
  function acquireLock() {
206
246
  try {
207
- fs29.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
247
+ fs30.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
208
248
  } catch {
209
249
  }
210
250
  }
211
251
  function releaseLock() {
212
252
  try {
213
- if (fs29.existsSync(LOCK_FILE)) fs29.unlinkSync(LOCK_FILE);
253
+ if (fs30.existsSync(LOCK_FILE)) fs30.unlinkSync(LOCK_FILE);
214
254
  } catch {
215
255
  }
216
256
  }
@@ -218,28 +258,28 @@ var LOCK_FILE, STALE_MS;
218
258
  var init_lock = __esm({
219
259
  "src/lib/lock.ts"() {
220
260
  "use strict";
221
- LOCK_FILE = path23.join(os6.tmpdir(), ".caliber.lock");
261
+ LOCK_FILE = path24.join(os6.tmpdir(), ".caliber.lock");
222
262
  STALE_MS = 10 * 60 * 1e3;
223
263
  }
224
264
  });
225
265
 
226
266
  // src/cli.ts
227
267
  import { Command } from "commander";
228
- import fs34 from "fs";
229
- import path27 from "path";
268
+ import fs35 from "fs";
269
+ import path28 from "path";
230
270
  import { fileURLToPath } from "url";
231
271
 
232
272
  // src/commands/init.ts
233
- import path20 from "path";
273
+ import path21 from "path";
234
274
  import chalk11 from "chalk";
235
275
  import ora3 from "ora";
236
276
  import select5 from "@inquirer/select";
237
277
  import checkbox from "@inquirer/checkbox";
238
- import fs25 from "fs";
278
+ import fs26 from "fs";
239
279
 
240
280
  // src/fingerprint/index.ts
241
- import fs6 from "fs";
242
- import path5 from "path";
281
+ import fs7 from "fs";
282
+ import path6 from "path";
243
283
 
244
284
  // src/fingerprint/git.ts
245
285
  import { execSync } from "child_process";
@@ -292,15 +332,23 @@ function getFileTree(dir, maxDepth = 3) {
292
332
  for (const e of entries) {
293
333
  (e.isDir ? dirs : files).push(e);
294
334
  }
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;
335
+ const dirMaxMtime = /* @__PURE__ */ new Map();
336
+ for (const d of dirs) dirMaxMtime.set(d.relPath, d.mtime);
337
+ for (const f of files) {
338
+ let remaining = f.relPath;
339
+ while (true) {
340
+ const lastSlash = remaining.lastIndexOf("/");
341
+ if (lastSlash === -1) break;
342
+ const dirPrefix = remaining.slice(0, lastSlash + 1);
343
+ const current = dirMaxMtime.get(dirPrefix);
344
+ if (current !== void 0 && f.mtime > current) {
345
+ dirMaxMtime.set(dirPrefix, f.mtime);
301
346
  }
347
+ remaining = remaining.slice(0, lastSlash);
302
348
  }
303
- d.mtime = maxChildMtime;
349
+ }
350
+ for (const d of dirs) {
351
+ d.mtime = dirMaxMtime.get(d.relPath) ?? d.mtime;
304
352
  }
305
353
  dirs.sort((a, b) => b.mtime - a.mtime);
306
354
  files.sort((a, b) => b.mtime - a.mtime);
@@ -1004,8 +1052,8 @@ var AnthropicProvider = class {
1004
1052
  cacheWriteTokens: u.cache_creation_input_tokens
1005
1053
  });
1006
1054
  }
1007
- const block = response.content[0];
1008
- return block.type === "text" ? block.text : "";
1055
+ const block = response.content?.[0];
1056
+ return block?.type === "text" ? block.text : "";
1009
1057
  }
1010
1058
  async listModels() {
1011
1059
  const models = [];
@@ -1108,8 +1156,8 @@ var VertexProvider = class {
1108
1156
  cacheWriteTokens: u.cache_creation_input_tokens
1109
1157
  });
1110
1158
  }
1111
- const block = response.content[0];
1112
- return block.type === "text" ? block.text : "";
1159
+ const block = response.content?.[0];
1160
+ return block?.type === "text" ? block.text : "";
1113
1161
  }
1114
1162
  async stream(options, callbacks) {
1115
1163
  const messages = options.messages ? [
@@ -1229,14 +1277,44 @@ var OpenAICompatProvider = class {
1229
1277
  // src/llm/cursor-acp.ts
1230
1278
  import { spawn, execSync as execSync3 } from "child_process";
1231
1279
  import os2 from "os";
1280
+
1281
+ // src/llm/seat-based-errors.ts
1282
+ var ERROR_PATTERNS = [
1283
+ { pattern: /not logged in|not authenticated|login required|unauthorized/i, message: "Authentication required. Run the login command for your provider to re-authenticate." },
1284
+ { pattern: /rate limit|too many requests|429/i, message: "Rate limit exceeded. Retrying..." },
1285
+ { pattern: /model.*not found|invalid model|model.*unavailable/i, message: "The requested model is not available. Run `caliber config` to select a different model." }
1286
+ ];
1287
+ function parseSeatBasedError(stderr, exitCode) {
1288
+ if (!stderr && exitCode === 0) return null;
1289
+ for (const { pattern, message } of ERROR_PATTERNS) {
1290
+ if (pattern.test(stderr)) return message;
1291
+ }
1292
+ return null;
1293
+ }
1294
+ function isRateLimitError(stderr) {
1295
+ return /rate limit|too many requests|429/i.test(stderr);
1296
+ }
1297
+
1298
+ // src/llm/cursor-acp.ts
1232
1299
  var AGENT_BIN = "agent";
1233
1300
  var IS_WINDOWS = process.platform === "win32";
1301
+ var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
1302
+ var SIGKILL_DELAY_MS = 5e3;
1303
+ var STDERR_MAX_BYTES = 10 * 1024;
1234
1304
  var CursorAcpProvider = class {
1235
1305
  defaultModel;
1236
1306
  cursorApiKey;
1307
+ timeoutMs;
1308
+ warmProcess = null;
1309
+ warmModel = null;
1237
1310
  constructor(config) {
1238
1311
  this.defaultModel = config.model || "sonnet-4.6";
1239
1312
  this.cursorApiKey = process.env.CURSOR_API_KEY ?? process.env.CURSOR_AUTH_TOKEN;
1313
+ const envTimeout = process.env.CALIBER_CURSOR_TIMEOUT_MS;
1314
+ this.timeoutMs = envTimeout ? parseInt(envTimeout, 10) : DEFAULT_TIMEOUT_MS;
1315
+ if (!Number.isFinite(this.timeoutMs) || this.timeoutMs < 1e3) {
1316
+ this.timeoutMs = DEFAULT_TIMEOUT_MS;
1317
+ }
1240
1318
  }
1241
1319
  async call(options) {
1242
1320
  const prompt = this.buildPrompt(options);
@@ -1248,6 +1326,29 @@ var CursorAcpProvider = class {
1248
1326
  const model = options.model || this.defaultModel;
1249
1327
  return this.runPrintStream(model, prompt, callbacks);
1250
1328
  }
1329
+ /**
1330
+ * Pre-spawn an agent process so it's ready when the first call comes.
1331
+ * Call this during fingerprint collection to hide spawn latency.
1332
+ */
1333
+ prewarm(model) {
1334
+ const targetModel = model || this.defaultModel;
1335
+ if (this.warmProcess && !this.warmProcess.killed && this.warmModel === targetModel) return;
1336
+ const args = this.buildArgs(targetModel, false);
1337
+ this.warmProcess = spawn(AGENT_BIN, args, {
1338
+ stdio: ["pipe", "pipe", "pipe"],
1339
+ env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
1340
+ ...IS_WINDOWS && { shell: true }
1341
+ });
1342
+ this.warmModel = targetModel;
1343
+ this.warmProcess.on("error", () => {
1344
+ this.warmProcess = null;
1345
+ this.warmModel = null;
1346
+ });
1347
+ this.warmProcess.on("close", () => {
1348
+ this.warmProcess = null;
1349
+ this.warmModel = null;
1350
+ });
1351
+ }
1251
1352
  buildArgs(model, streaming) {
1252
1353
  const args = ["--print", "--trust", "--workspace", os2.tmpdir()];
1253
1354
  if (model && model !== "auto" && model !== "default") {
@@ -1261,23 +1362,78 @@ var CursorAcpProvider = class {
1261
1362
  }
1262
1363
  return args;
1263
1364
  }
1365
+ takeWarmProcess(model, streaming) {
1366
+ if (!streaming && this.warmProcess && !this.warmProcess.killed && this.warmModel === model) {
1367
+ const proc = this.warmProcess;
1368
+ this.warmProcess = null;
1369
+ this.warmModel = null;
1370
+ return proc;
1371
+ }
1372
+ return null;
1373
+ }
1374
+ spawnAgent(model, streaming) {
1375
+ const warm = this.takeWarmProcess(model, streaming);
1376
+ if (warm) {
1377
+ const stderrChunks2 = [];
1378
+ warm.stderr?.on("data", (chunk) => {
1379
+ if (Buffer.concat(stderrChunks2).length < STDERR_MAX_BYTES) stderrChunks2.push(chunk);
1380
+ });
1381
+ return { child: warm, stderrChunks: stderrChunks2 };
1382
+ }
1383
+ const args = this.buildArgs(model, streaming);
1384
+ const child = spawn(AGENT_BIN, args, {
1385
+ stdio: ["pipe", "pipe", "pipe"],
1386
+ env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
1387
+ ...IS_WINDOWS && { shell: true }
1388
+ });
1389
+ const stderrChunks = [];
1390
+ child.stderr.on("data", (chunk) => {
1391
+ if (Buffer.concat(stderrChunks).length < STDERR_MAX_BYTES) stderrChunks.push(chunk);
1392
+ });
1393
+ return { child, stderrChunks };
1394
+ }
1395
+ killWithEscalation(child) {
1396
+ child.kill("SIGTERM");
1397
+ const killTimer = setTimeout(() => {
1398
+ if (!child.killed) child.kill("SIGKILL");
1399
+ }, SIGKILL_DELAY_MS);
1400
+ killTimer.unref();
1401
+ }
1402
+ buildErrorMessage(code, stderrChunks) {
1403
+ const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
1404
+ const parsed = parseSeatBasedError(stderr, code);
1405
+ if (parsed) return parsed;
1406
+ const base = `Cursor agent exited with code ${code}`;
1407
+ return stderr ? `${base}: ${stderr.slice(0, 200)}` : base;
1408
+ }
1264
1409
  runPrint(model, prompt) {
1265
1410
  return new Promise((resolve2, reject) => {
1266
- const args = this.buildArgs(model, false);
1267
- const child = spawn(AGENT_BIN, args, {
1268
- stdio: ["pipe", "pipe", "ignore"],
1269
- env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
1270
- ...IS_WINDOWS && { shell: true }
1271
- });
1411
+ const { child, stderrChunks } = this.spawnAgent(model, false);
1412
+ let settled = false;
1272
1413
  const chunks = [];
1273
- child.stdout.on("data", (data) => {
1274
- chunks.push(data);
1414
+ child.stdout.on("data", (data) => chunks.push(data));
1415
+ const timer = setTimeout(() => {
1416
+ this.killWithEscalation(child);
1417
+ if (!settled) {
1418
+ settled = true;
1419
+ reject(new Error(`Cursor agent timed out after ${this.timeoutMs / 1e3}s. Set CALIBER_CURSOR_TIMEOUT_MS to increase.`));
1420
+ }
1421
+ }, this.timeoutMs);
1422
+ timer.unref();
1423
+ child.on("error", (err) => {
1424
+ clearTimeout(timer);
1425
+ if (!settled) {
1426
+ settled = true;
1427
+ reject(err);
1428
+ }
1275
1429
  });
1276
- child.on("error", reject);
1277
1430
  child.on("close", (code) => {
1431
+ clearTimeout(timer);
1432
+ if (settled) return;
1433
+ settled = true;
1278
1434
  const output = Buffer.concat(chunks).toString("utf-8").trim();
1279
1435
  if (code !== 0 && !output) {
1280
- reject(new Error(`Cursor agent exited with code ${code}`));
1436
+ reject(new Error(this.buildErrorMessage(code, stderrChunks)));
1281
1437
  } else {
1282
1438
  resolve2(output);
1283
1439
  }
@@ -1288,14 +1444,20 @@ var CursorAcpProvider = class {
1288
1444
  }
1289
1445
  runPrintStream(model, prompt, callbacks) {
1290
1446
  return new Promise((resolve2, reject) => {
1291
- const args = this.buildArgs(model, true);
1292
- const child = spawn(AGENT_BIN, args, {
1293
- stdio: ["pipe", "pipe", "ignore"],
1294
- env: { ...process.env, ...this.cursorApiKey && { CURSOR_API_KEY: this.cursorApiKey } },
1295
- ...IS_WINDOWS && { shell: true }
1296
- });
1447
+ const { child, stderrChunks } = this.spawnAgent(model, true);
1297
1448
  let buffer = "";
1298
1449
  let endCalled = false;
1450
+ let settled = false;
1451
+ const timer = setTimeout(() => {
1452
+ this.killWithEscalation(child);
1453
+ if (!settled) {
1454
+ settled = true;
1455
+ const err = new Error(`Cursor agent timed out after ${this.timeoutMs / 1e3}s. Set CALIBER_CURSOR_TIMEOUT_MS to increase.`);
1456
+ callbacks.onError(err);
1457
+ reject(err);
1458
+ }
1459
+ }, this.timeoutMs);
1460
+ timer.unref();
1299
1461
  child.stdout.on("data", (data) => {
1300
1462
  buffer += data.toString("utf-8");
1301
1463
  const lines = buffer.split("\n");
@@ -1320,16 +1482,26 @@ var CursorAcpProvider = class {
1320
1482
  }
1321
1483
  });
1322
1484
  child.on("error", (err) => {
1323
- callbacks.onError(err);
1324
- reject(err);
1485
+ clearTimeout(timer);
1486
+ if (!settled) {
1487
+ settled = true;
1488
+ callbacks.onError(err);
1489
+ reject(err);
1490
+ }
1325
1491
  });
1326
1492
  child.on("close", (code) => {
1493
+ clearTimeout(timer);
1494
+ if (settled) return;
1495
+ settled = true;
1327
1496
  if (buffer.trim()) {
1328
1497
  try {
1329
1498
  const event = JSON.parse(buffer);
1330
1499
  if (event.type === "assistant") {
1331
- const text = event.message?.content?.[0]?.text || event.content;
1332
- if (text) callbacks.onText(text);
1500
+ const isDelta = "timestamp_ms" in event;
1501
+ if (isDelta) {
1502
+ const text = event.message?.content?.[0]?.text || event.content;
1503
+ if (text) callbacks.onText(text);
1504
+ }
1333
1505
  } else if (event.type === "result") {
1334
1506
  endCalled = true;
1335
1507
  callbacks.onEnd({ stopReason: event.is_error ? "error" : "end_turn" });
@@ -1342,9 +1514,16 @@ var CursorAcpProvider = class {
1342
1514
  callbacks.onEnd({ stopReason: code === 0 ? "end_turn" : "error" });
1343
1515
  }
1344
1516
  if (code !== 0 && code !== null) {
1345
- const err = new Error(`Cursor agent exited with code ${code}`);
1346
- callbacks.onError(err);
1347
- reject(err);
1517
+ const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
1518
+ if (isRateLimitError(stderr)) {
1519
+ const err = new Error("Rate limit exceeded");
1520
+ callbacks.onError(err);
1521
+ reject(err);
1522
+ } else {
1523
+ const err = new Error(this.buildErrorMessage(code, stderrChunks));
1524
+ callbacks.onError(err);
1525
+ reject(err);
1526
+ }
1348
1527
  } else {
1349
1528
  resolve2();
1350
1529
  }
@@ -1371,28 +1550,48 @@ var CursorAcpProvider = class {
1371
1550
  };
1372
1551
  function isCursorAgentAvailable() {
1373
1552
  try {
1374
- const cmd = process.platform === "win32" ? `where ${AGENT_BIN}` : `which ${AGENT_BIN}`;
1553
+ const cmd = IS_WINDOWS ? `where ${AGENT_BIN}` : `which ${AGENT_BIN}`;
1375
1554
  execSync3(cmd, { stdio: "ignore" });
1376
1555
  return true;
1377
1556
  } catch {
1378
1557
  return false;
1379
1558
  }
1380
1559
  }
1560
+ function isCursorLoggedIn() {
1561
+ try {
1562
+ const result = execSync3(`${AGENT_BIN} status`, { stdio: ["ignore", "pipe", "ignore"], timeout: 5e3 });
1563
+ return !result.toString().includes("not logged in");
1564
+ } catch {
1565
+ return false;
1566
+ }
1567
+ }
1381
1568
 
1382
1569
  // src/llm/claude-cli.ts
1383
1570
  import { spawn as spawn2, execSync as execSync4 } from "child_process";
1384
1571
  var CLAUDE_CLI_BIN = "claude";
1385
- var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
1572
+ var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
1386
1573
  var IS_WINDOWS2 = process.platform === "win32";
1574
+ function spawnClaude(args) {
1575
+ return IS_WINDOWS2 ? spawn2([CLAUDE_CLI_BIN, ...args].join(" "), {
1576
+ cwd: process.cwd(),
1577
+ stdio: ["pipe", "pipe", "pipe"],
1578
+ env: process.env,
1579
+ shell: true
1580
+ }) : spawn2(CLAUDE_CLI_BIN, args, {
1581
+ cwd: process.cwd(),
1582
+ stdio: ["pipe", "pipe", "pipe"],
1583
+ env: process.env
1584
+ });
1585
+ }
1387
1586
  var ClaudeCliProvider = class {
1388
1587
  defaultModel;
1389
1588
  timeoutMs;
1390
1589
  constructor(config) {
1391
1590
  this.defaultModel = config.model || "default";
1392
1591
  const envTimeout = process.env.CALIBER_CLAUDE_CLI_TIMEOUT_MS;
1393
- this.timeoutMs = envTimeout ? parseInt(envTimeout, 10) : DEFAULT_TIMEOUT_MS;
1592
+ this.timeoutMs = envTimeout ? parseInt(envTimeout, 10) : DEFAULT_TIMEOUT_MS2;
1394
1593
  if (!Number.isFinite(this.timeoutMs) || this.timeoutMs < 1e3) {
1395
- this.timeoutMs = DEFAULT_TIMEOUT_MS;
1594
+ this.timeoutMs = DEFAULT_TIMEOUT_MS2;
1396
1595
  }
1397
1596
  }
1398
1597
  async call(options) {
@@ -1403,23 +1602,16 @@ var ClaudeCliProvider = class {
1403
1602
  const combined = this.buildCombinedPrompt(options);
1404
1603
  const args = ["-p"];
1405
1604
  if (options.model) args.push("--model", options.model);
1406
- const child = IS_WINDOWS2 ? spawn2([CLAUDE_CLI_BIN, ...args].join(" "), {
1407
- cwd: process.cwd(),
1408
- stdio: ["pipe", "pipe", "inherit"],
1409
- env: process.env,
1410
- shell: true
1411
- }) : spawn2(CLAUDE_CLI_BIN, args, {
1412
- cwd: process.cwd(),
1413
- stdio: ["pipe", "pipe", "inherit"],
1414
- env: process.env
1415
- });
1605
+ const child = spawnClaude(args);
1416
1606
  child.stdin.end(combined);
1417
1607
  let settled = false;
1418
1608
  const chunks = [];
1609
+ const stderrChunks = [];
1419
1610
  child.stdout.on("data", (chunk) => {
1420
1611
  chunks.push(chunk);
1421
1612
  callbacks.onText(chunk.toString("utf-8"));
1422
1613
  });
1614
+ child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
1423
1615
  const timer = setTimeout(() => {
1424
1616
  child.kill("SIGTERM");
1425
1617
  if (!settled) {
@@ -1445,45 +1637,40 @@ var ClaudeCliProvider = class {
1445
1637
  if (code === 0) {
1446
1638
  callbacks.onEnd({ stopReason: "end_turn" });
1447
1639
  } else {
1640
+ const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
1641
+ const friendly = parseSeatBasedError(stderr, code);
1448
1642
  const stdout = Buffer.concat(chunks).toString("utf-8").trim();
1449
- const msg = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
1450
- callbacks.onError(new Error(stdout ? `${msg}. Output: ${stdout.slice(0, 200)}` : msg));
1643
+ const base = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
1644
+ const detail = friendly || stderr || (stdout ? stdout.slice(0, 200) : "");
1645
+ callbacks.onError(new Error(detail ? `${base}. ${detail}` : base));
1451
1646
  }
1452
1647
  });
1453
1648
  }
1454
1649
  buildCombinedPrompt(options) {
1455
1650
  const streamOpts = options;
1456
1651
  const hasHistory = streamOpts.messages && streamOpts.messages.length > 0;
1457
- let combined = "";
1458
- combined += "[[System]]\n" + options.system + "\n\n";
1652
+ let combined = options.system + "\n\n";
1459
1653
  if (hasHistory) {
1460
1654
  for (const msg of streamOpts.messages) {
1461
- combined += `[[${msg.role === "user" ? "User" : "Assistant"}]]
1462
- ${msg.content}
1655
+ const label = msg.role === "user" ? "User" : "Assistant";
1656
+ combined += `${label}: ${msg.content}
1463
1657
 
1464
1658
  `;
1465
1659
  }
1466
1660
  }
1467
- combined += "[[User]]\n" + options.prompt;
1661
+ combined += options.prompt;
1468
1662
  return combined;
1469
1663
  }
1470
1664
  runClaudePrint(combinedPrompt, model) {
1471
1665
  return new Promise((resolve2, reject) => {
1472
1666
  const args = ["-p"];
1473
1667
  if (model) args.push("--model", model);
1474
- const child = IS_WINDOWS2 ? spawn2([CLAUDE_CLI_BIN, ...args].join(" "), {
1475
- cwd: process.cwd(),
1476
- stdio: ["pipe", "pipe", "inherit"],
1477
- env: process.env,
1478
- shell: true
1479
- }) : spawn2(CLAUDE_CLI_BIN, args, {
1480
- cwd: process.cwd(),
1481
- stdio: ["pipe", "pipe", "inherit"],
1482
- env: process.env
1483
- });
1668
+ const child = spawnClaude(args);
1484
1669
  child.stdin.end(combinedPrompt);
1485
1670
  const chunks = [];
1671
+ const stderrChunks = [];
1486
1672
  child.stdout.on("data", (chunk) => chunks.push(chunk));
1673
+ child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
1487
1674
  child.on("error", (err) => {
1488
1675
  clearTimeout(timer);
1489
1676
  reject(err);
@@ -1494,8 +1681,11 @@ ${msg.content}
1494
1681
  if (code === 0) {
1495
1682
  resolve2(stdout);
1496
1683
  } else {
1497
- const msg = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
1498
- reject(new Error(stdout ? `${msg}. Output: ${stdout.slice(0, 200)}` : msg));
1684
+ const stderr = Buffer.concat(stderrChunks).toString("utf-8").trim();
1685
+ const friendly = parseSeatBasedError(stderr, code);
1686
+ const base = signal ? `Claude CLI killed (${signal})` : code != null ? `Claude CLI exited with code ${code}` : "Claude CLI exited";
1687
+ const detail = friendly || stderr || (stdout ? stdout.slice(0, 200) : "");
1688
+ reject(new Error(detail ? `${base}. ${detail}` : base));
1499
1689
  }
1500
1690
  });
1501
1691
  const timer = setTimeout(() => {
@@ -1669,6 +1859,7 @@ async function handleModelNotAvailable(failedModel, provider, config) {
1669
1859
  }
1670
1860
 
1671
1861
  // src/llm/index.ts
1862
+ init_types();
1672
1863
  init_config();
1673
1864
  var cachedProvider = null;
1674
1865
  var cachedConfig = null;
@@ -1744,11 +1935,11 @@ async function llmCall(options) {
1744
1935
  throw error;
1745
1936
  }
1746
1937
  if (isOverloaded(error) && attempt < MAX_RETRIES) {
1747
- await new Promise((r) => setTimeout(r, 2e3));
1938
+ await new Promise((r) => setTimeout(r, 1e3 * Math.pow(2, attempt - 1)));
1748
1939
  continue;
1749
1940
  }
1750
1941
  if (isTransientError(error) && attempt < MAX_RETRIES) {
1751
- await new Promise((r) => setTimeout(r, 2e3));
1942
+ await new Promise((r) => setTimeout(r, 1e3 * Math.pow(2, attempt - 1)));
1752
1943
  continue;
1753
1944
  }
1754
1945
  throw error;
@@ -1764,7 +1955,8 @@ async function validateModel(options) {
1764
1955
  const provider = getProvider();
1765
1956
  const config = cachedConfig;
1766
1957
  if (!config) return;
1767
- if (config.provider === "cursor" || config.provider === "claude-cli") return;
1958
+ const { isSeatBased: isSeatBased2 } = await Promise.resolve().then(() => (init_types(), types_exports));
1959
+ if (isSeatBased2(config.provider)) return;
1768
1960
  const modelsToCheck = [config.model];
1769
1961
  if (options?.fast) {
1770
1962
  const { getFastModel: getFastModel2 } = await Promise.resolve().then(() => (init_config(), config_exports));
@@ -2212,12 +2404,112 @@ async function detectProjectStack(fileTree, suffixCounts) {
2212
2404
 
2213
2405
  // src/fingerprint/index.ts
2214
2406
  init_config();
2407
+
2408
+ // src/fingerprint/cache.ts
2409
+ import fs6 from "fs";
2410
+ import path5 from "path";
2411
+ import crypto from "crypto";
2412
+ import { execSync as execSync5 } from "child_process";
2413
+ var CACHE_VERSION = 1;
2414
+ var CACHE_DIR = ".caliber/cache";
2415
+ var CACHE_FILE = "fingerprint.json";
2416
+ function getCachePath(dir) {
2417
+ return path5.join(dir, CACHE_DIR, CACHE_FILE);
2418
+ }
2419
+ function getGitHead(dir) {
2420
+ try {
2421
+ return execSync5("git rev-parse HEAD", {
2422
+ cwd: dir,
2423
+ encoding: "utf-8",
2424
+ stdio: ["pipe", "pipe", "pipe"],
2425
+ timeout: 3e3
2426
+ }).trim();
2427
+ } catch {
2428
+ return "";
2429
+ }
2430
+ }
2431
+ function getDirtySignature(dir) {
2432
+ try {
2433
+ const output = execSync5("git diff --name-only HEAD", {
2434
+ cwd: dir,
2435
+ encoding: "utf-8",
2436
+ stdio: ["pipe", "pipe", "pipe"],
2437
+ timeout: 3e3
2438
+ }).trim();
2439
+ return output.split("\n").slice(0, 100).join("\n");
2440
+ } catch {
2441
+ return "";
2442
+ }
2443
+ }
2444
+ function computeTreeSignature(fileTree, dir) {
2445
+ const hash = crypto.createHash("sha256");
2446
+ hash.update(fileTree.join("\0"));
2447
+ hash.update("\n");
2448
+ hash.update(getDirtySignature(dir));
2449
+ return hash.digest("hex").slice(0, 16);
2450
+ }
2451
+ function loadFingerprintCache(dir, fileTree) {
2452
+ const cachePath = getCachePath(dir);
2453
+ try {
2454
+ if (!fs6.existsSync(cachePath)) return null;
2455
+ const raw = fs6.readFileSync(cachePath, "utf-8");
2456
+ const cache = JSON.parse(raw);
2457
+ if (cache.version !== CACHE_VERSION) return null;
2458
+ const currentHead = getGitHead(dir);
2459
+ if (currentHead && cache.gitHead !== currentHead) return null;
2460
+ const currentSig = computeTreeSignature(fileTree, dir);
2461
+ if (cache.treeSignature !== currentSig) return null;
2462
+ return {
2463
+ codeAnalysis: cache.codeAnalysis,
2464
+ languages: cache.languages,
2465
+ frameworks: cache.frameworks,
2466
+ tools: cache.tools
2467
+ };
2468
+ } catch {
2469
+ return null;
2470
+ }
2471
+ }
2472
+ function saveFingerprintCache(dir, fileTree, codeAnalysis, languages, frameworks, tools) {
2473
+ const cachePath = getCachePath(dir);
2474
+ try {
2475
+ const cacheDir = path5.dirname(cachePath);
2476
+ if (!fs6.existsSync(cacheDir)) {
2477
+ fs6.mkdirSync(cacheDir, { recursive: true });
2478
+ }
2479
+ const cache = {
2480
+ version: CACHE_VERSION,
2481
+ gitHead: getGitHead(dir),
2482
+ treeSignature: computeTreeSignature(fileTree, dir),
2483
+ codeAnalysis,
2484
+ languages,
2485
+ frameworks,
2486
+ tools
2487
+ };
2488
+ fs6.writeFileSync(cachePath, JSON.stringify(cache), "utf-8");
2489
+ } catch {
2490
+ }
2491
+ }
2492
+
2493
+ // src/fingerprint/index.ts
2215
2494
  async function collectFingerprint(dir) {
2216
2495
  const gitRemoteUrl = getGitRemoteUrl();
2217
2496
  const fileTree = getFileTree(dir);
2218
2497
  const existingConfigs = readExistingConfigs(dir);
2219
- const codeAnalysis = analyzeCode(dir);
2220
2498
  const packageName = readPackageName(dir);
2499
+ const cached = loadFingerprintCache(dir, fileTree);
2500
+ if (cached) {
2501
+ return {
2502
+ gitRemoteUrl,
2503
+ packageName,
2504
+ languages: cached.languages,
2505
+ frameworks: cached.frameworks,
2506
+ tools: cached.tools,
2507
+ fileTree,
2508
+ existingConfigs,
2509
+ codeAnalysis: cached.codeAnalysis
2510
+ };
2511
+ }
2512
+ const codeAnalysis = analyzeCode(dir);
2221
2513
  const fingerprint = {
2222
2514
  gitRemoteUrl,
2223
2515
  packageName,
@@ -2229,13 +2521,21 @@ async function collectFingerprint(dir) {
2229
2521
  codeAnalysis
2230
2522
  };
2231
2523
  await enrichWithLLM(fingerprint);
2524
+ saveFingerprintCache(
2525
+ dir,
2526
+ fileTree,
2527
+ codeAnalysis,
2528
+ fingerprint.languages,
2529
+ fingerprint.frameworks,
2530
+ fingerprint.tools
2531
+ );
2232
2532
  return fingerprint;
2233
2533
  }
2234
2534
  function readPackageName(dir) {
2235
2535
  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"));
2536
+ const pkgPath = path6.join(dir, "package.json");
2537
+ if (!fs7.existsSync(pkgPath)) return void 0;
2538
+ const pkg3 = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
2239
2539
  return pkg3.name;
2240
2540
  } catch {
2241
2541
  return void 0;
@@ -2249,7 +2549,7 @@ async function enrichWithLLM(fingerprint) {
2249
2549
  const suffixCounts = {};
2250
2550
  for (const entry of fingerprint.fileTree) {
2251
2551
  if (entry.endsWith("/")) continue;
2252
- const ext = path5.extname(entry).toLowerCase();
2552
+ const ext = path6.extname(entry).toLowerCase();
2253
2553
  if (ext) {
2254
2554
  suffixCounts[ext] = (suffixCounts[ext] || 0) + 1;
2255
2555
  }
@@ -2268,15 +2568,15 @@ init_config();
2268
2568
  // src/utils/dependencies.ts
2269
2569
  import { readFileSync } from "fs";
2270
2570
  import { join } from "path";
2271
- function readFileOrNull(path29) {
2571
+ function readFileOrNull(path30) {
2272
2572
  try {
2273
- return readFileSync(path29, "utf-8");
2573
+ return readFileSync(path30, "utf-8");
2274
2574
  } catch {
2275
2575
  return null;
2276
2576
  }
2277
2577
  }
2278
- function readJsonOrNull(path29) {
2279
- const content = readFileOrNull(path29);
2578
+ function readJsonOrNull(path30) {
2579
+ const content = readFileOrNull(path30);
2280
2580
  if (!content) return null;
2281
2581
  try {
2282
2582
  return JSON.parse(content);
@@ -2500,15 +2800,14 @@ Generate the skill content following the instructions in the system prompt.`;
2500
2800
  content
2501
2801
  };
2502
2802
  }
2503
- async function generateCore(fingerprint, targetAgent, prompt, callbacks, failingChecks, currentScore, passingChecks) {
2803
+ async function streamGeneration(config) {
2504
2804
  const provider = getProvider();
2505
- const userMessage = buildGeneratePrompt(fingerprint, targetAgent, prompt, failingChecks, currentScore, passingChecks);
2506
2805
  let attempt = 0;
2507
2806
  const attemptGeneration = async () => {
2508
2807
  attempt++;
2509
2808
  const maxTokensForAttempt = Math.min(
2510
- CORE_MAX_TOKENS + attempt * 8e3,
2511
- GENERATION_MAX_TOKENS
2809
+ config.baseMaxTokens + attempt * config.tokenIncrement,
2810
+ config.maxTokensCap
2512
2811
  );
2513
2812
  return new Promise((resolve2) => {
2514
2813
  let preJsonBuffer = "";
@@ -2518,8 +2817,8 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
2518
2817
  let stopReason = null;
2519
2818
  provider.stream(
2520
2819
  {
2521
- system: CORE_GENERATION_PROMPT,
2522
- prompt: userMessage,
2820
+ system: config.systemPrompt,
2821
+ prompt: config.userMessage,
2523
2822
  maxTokens: maxTokensForAttempt
2524
2823
  },
2525
2824
  {
@@ -2532,7 +2831,7 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
2532
2831
  const trimmed = completedLines[i].trim();
2533
2832
  if (trimmed.startsWith("STATUS:")) {
2534
2833
  const status = trimmed.slice(7).trim();
2535
- if (status && callbacks) callbacks.onStatus(status);
2834
+ if (status && config.callbacks) config.callbacks.onStatus(status);
2536
2835
  }
2537
2836
  }
2538
2837
  sentStatuses = completedLines.length;
@@ -2562,7 +2861,7 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
2562
2861
  } catch {
2563
2862
  }
2564
2863
  if (!setup && stopReason === "max_tokens" && attempt < MAX_RETRIES2) {
2565
- if (callbacks) callbacks.onStatus("Output was truncated, retrying with higher token limit...");
2864
+ if (config.callbacks) config.callbacks.onStatus("Output was truncated, retrying with higher token limit...");
2566
2865
  setTimeout(() => attemptGeneration().then(resolve2), 1e3);
2567
2866
  return;
2568
2867
  }
@@ -2572,7 +2871,7 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
2572
2871
  explanation = explainMatch[1].trim();
2573
2872
  }
2574
2873
  if (setup) {
2575
- if (callbacks) callbacks.onComplete(setup, explanation);
2874
+ if (config.callbacks) config.callbacks.onComplete(setup, explanation);
2576
2875
  resolve2({ setup, explanation, stopReason: stopReason ?? void 0 });
2577
2876
  } else {
2578
2877
  resolve2({ setup: null, explanation, raw: preJsonBuffer, stopReason: stopReason ?? void 0 });
@@ -2580,117 +2879,43 @@ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failing
2580
2879
  },
2581
2880
  onError: (error) => {
2582
2881
  if (isTransientError2(error) && attempt < MAX_RETRIES2) {
2583
- if (callbacks) callbacks.onStatus("Connection interrupted, retrying...");
2882
+ if (config.callbacks) config.callbacks.onStatus("Connection interrupted, retrying...");
2584
2883
  setTimeout(() => attemptGeneration().then(resolve2), 2e3);
2585
2884
  return;
2586
2885
  }
2587
- if (callbacks) callbacks.onError(error.message);
2886
+ if (config.callbacks) config.callbacks.onError(error.message);
2588
2887
  resolve2({ setup: null, raw: error.message, stopReason: "error" });
2589
2888
  }
2590
2889
  }
2591
2890
  ).catch((error) => {
2592
- if (callbacks) callbacks.onError(error.message);
2891
+ if (config.callbacks) config.callbacks.onError(error.message);
2593
2892
  resolve2({ setup: null, raw: error.message, stopReason: "error" });
2594
2893
  });
2595
2894
  });
2596
2895
  };
2597
2896
  return attemptGeneration();
2598
2897
  }
2898
+ async function generateCore(fingerprint, targetAgent, prompt, callbacks, failingChecks, currentScore, passingChecks) {
2899
+ const userMessage = buildGeneratePrompt(fingerprint, targetAgent, prompt, failingChecks, currentScore, passingChecks);
2900
+ return streamGeneration({
2901
+ systemPrompt: CORE_GENERATION_PROMPT,
2902
+ userMessage,
2903
+ baseMaxTokens: CORE_MAX_TOKENS,
2904
+ tokenIncrement: 8e3,
2905
+ maxTokensCap: GENERATION_MAX_TOKENS,
2906
+ callbacks
2907
+ });
2908
+ }
2599
2909
  async function generateMonolithic(fingerprint, targetAgent, prompt, callbacks, failingChecks, currentScore, passingChecks) {
2600
- const provider = getProvider();
2601
2910
  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();
2911
+ return streamGeneration({
2912
+ systemPrompt: GENERATION_SYSTEM_PROMPT,
2913
+ userMessage,
2914
+ baseMaxTokens: GENERATION_MAX_TOKENS,
2915
+ tokenIncrement: 16e3,
2916
+ maxTokensCap: MODEL_MAX_OUTPUT_TOKENS,
2917
+ callbacks
2918
+ });
2694
2919
  }
2695
2920
  async function generateSkillsForSetup(setup, fingerprint, targetAgent, onStatus) {
2696
2921
  const skillTopics = collectSkillTopics(setup, targetAgent, fingerprint);
@@ -2723,7 +2948,6 @@ async function generateSkillsForSetup(setup, fingerprint, targetAgent, onStatus)
2723
2948
  if (failed > 0) onStatus?.(`${succeeded} generated, ${failed} failed`);
2724
2949
  return succeeded;
2725
2950
  }
2726
- var MAX_PROMPT_TOKENS = 12e4;
2727
2951
  var LIMITS = {
2728
2952
  FILE_TREE_ENTRIES: 500,
2729
2953
  EXISTING_CONFIG_CHARS: 8e3,
@@ -2738,6 +2962,7 @@ function truncate(text, maxChars) {
2738
2962
  }
2739
2963
  function sampleFileTree(fileTree, codeAnalysisPaths, limit) {
2740
2964
  if (fileTree.length <= limit) return fileTree;
2965
+ const fileTreeSet = new Set(fileTree);
2741
2966
  const dirs = [];
2742
2967
  const rootFiles = [];
2743
2968
  const nestedFiles = [];
@@ -2763,7 +2988,7 @@ function sampleFileTree(fileTree, codeAnalysisPaths, limit) {
2763
2988
  for (const f of rootFiles.slice(0, 50)) add(f);
2764
2989
  for (const p of codeAnalysisPaths) {
2765
2990
  if (result.length >= limit) break;
2766
- if (fileTree.includes(p)) add(p);
2991
+ if (fileTreeSet.has(p)) add(p);
2767
2992
  }
2768
2993
  for (const f of nestedFiles) {
2769
2994
  if (result.length >= limit) break;
@@ -2887,20 +3112,25 @@ User instructions: ${prompt}`);
2887
3112
  if (fingerprint.codeAnalysis) {
2888
3113
  const ca = fingerprint.codeAnalysis;
2889
3114
  const basePrompt = parts.join("\n");
3115
+ const maxPromptTokens = getMaxPromptTokens();
2890
3116
  const baseTokens = estimateTokens(basePrompt);
2891
- const tokenBudgetForCode = Math.max(0, MAX_PROMPT_TOKENS - baseTokens);
3117
+ const tokenBudgetForCode = Math.max(0, maxPromptTokens - baseTokens);
2892
3118
  const codeLines = [];
2893
3119
  let codeChars = 0;
2894
- codeLines.push("Study these files to extract patterns for skills. Use the exact code patterns you see here.\n");
3120
+ const introLine = "Study these files to extract patterns for skills. Use the exact code patterns you see here.\n";
3121
+ codeLines.push(introLine);
3122
+ let runningCodeLen = introLine.length;
2895
3123
  const sortedFiles = [...ca.files].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
2896
3124
  let includedFiles = 0;
2897
3125
  for (const f of sortedFiles) {
2898
3126
  const entry = `[${f.path}]
2899
3127
  ${f.content}
2900
3128
  `;
2901
- if (estimateTokens(codeLines.join("\n") + entry) > tokenBudgetForCode && includedFiles > 0) break;
3129
+ const projectedLen = runningCodeLen + 1 + entry.length;
3130
+ if (Math.ceil(projectedLen / 4) > tokenBudgetForCode && includedFiles > 0) break;
2902
3131
  codeLines.push(entry);
2903
3132
  codeChars += f.content.length;
3133
+ runningCodeLen = projectedLen;
2904
3134
  includedFiles++;
2905
3135
  }
2906
3136
  const includedTokens = Math.ceil(codeChars / 4);
@@ -2971,20 +3201,20 @@ Return the complete updated AgentSetup JSON incorporating the user's changes. Re
2971
3201
  }
2972
3202
 
2973
3203
  // src/writers/index.ts
2974
- import fs12 from "fs";
3204
+ import fs13 from "fs";
2975
3205
 
2976
3206
  // src/writers/claude/index.ts
2977
- import fs7 from "fs";
2978
- import path6 from "path";
3207
+ import fs8 from "fs";
3208
+ import path7 from "path";
2979
3209
  function writeClaudeConfig(config) {
2980
3210
  const written = [];
2981
- fs7.writeFileSync("CLAUDE.md", config.claudeMd);
3211
+ fs8.writeFileSync("CLAUDE.md", config.claudeMd);
2982
3212
  written.push("CLAUDE.md");
2983
3213
  if (config.skills?.length) {
2984
3214
  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");
3215
+ const skillDir = path7.join(".claude", "skills", skill.name);
3216
+ if (!fs8.existsSync(skillDir)) fs8.mkdirSync(skillDir, { recursive: true });
3217
+ const skillPath = path7.join(skillDir, "SKILL.md");
2988
3218
  const frontmatter = [
2989
3219
  "---",
2990
3220
  `name: ${skill.name}`,
@@ -2992,49 +3222,49 @@ function writeClaudeConfig(config) {
2992
3222
  "---",
2993
3223
  ""
2994
3224
  ].join("\n");
2995
- fs7.writeFileSync(skillPath, frontmatter + skill.content);
3225
+ fs8.writeFileSync(skillPath, frontmatter + skill.content);
2996
3226
  written.push(skillPath);
2997
3227
  }
2998
3228
  }
2999
3229
  if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
3000
3230
  let existingServers = {};
3001
3231
  try {
3002
- if (fs7.existsSync(".mcp.json")) {
3003
- const existing = JSON.parse(fs7.readFileSync(".mcp.json", "utf-8"));
3232
+ if (fs8.existsSync(".mcp.json")) {
3233
+ const existing = JSON.parse(fs8.readFileSync(".mcp.json", "utf-8"));
3004
3234
  if (existing.mcpServers) existingServers = existing.mcpServers;
3005
3235
  }
3006
3236
  } catch {
3007
3237
  }
3008
3238
  const mergedServers = { ...existingServers, ...config.mcpServers };
3009
- fs7.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
3239
+ fs8.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
3010
3240
  written.push(".mcp.json");
3011
3241
  }
3012
3242
  return written;
3013
3243
  }
3014
3244
 
3015
3245
  // src/writers/cursor/index.ts
3016
- import fs8 from "fs";
3017
- import path7 from "path";
3246
+ import fs9 from "fs";
3247
+ import path8 from "path";
3018
3248
  function writeCursorConfig(config) {
3019
3249
  const written = [];
3020
3250
  if (config.cursorrules) {
3021
- fs8.writeFileSync(".cursorrules", config.cursorrules);
3251
+ fs9.writeFileSync(".cursorrules", config.cursorrules);
3022
3252
  written.push(".cursorrules");
3023
3253
  }
3024
3254
  if (config.rules?.length) {
3025
- const rulesDir = path7.join(".cursor", "rules");
3026
- if (!fs8.existsSync(rulesDir)) fs8.mkdirSync(rulesDir, { recursive: true });
3255
+ const rulesDir = path8.join(".cursor", "rules");
3256
+ if (!fs9.existsSync(rulesDir)) fs9.mkdirSync(rulesDir, { recursive: true });
3027
3257
  for (const rule of config.rules) {
3028
- const rulePath = path7.join(rulesDir, rule.filename);
3029
- fs8.writeFileSync(rulePath, rule.content);
3258
+ const rulePath = path8.join(rulesDir, rule.filename);
3259
+ fs9.writeFileSync(rulePath, rule.content);
3030
3260
  written.push(rulePath);
3031
3261
  }
3032
3262
  }
3033
3263
  if (config.skills?.length) {
3034
3264
  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");
3265
+ const skillDir = path8.join(".cursor", "skills", skill.name);
3266
+ if (!fs9.existsSync(skillDir)) fs9.mkdirSync(skillDir, { recursive: true });
3267
+ const skillPath = path8.join(skillDir, "SKILL.md");
3038
3268
  const frontmatter = [
3039
3269
  "---",
3040
3270
  `name: ${skill.name}`,
@@ -3042,41 +3272,41 @@ function writeCursorConfig(config) {
3042
3272
  "---",
3043
3273
  ""
3044
3274
  ].join("\n");
3045
- fs8.writeFileSync(skillPath, frontmatter + skill.content);
3275
+ fs9.writeFileSync(skillPath, frontmatter + skill.content);
3046
3276
  written.push(skillPath);
3047
3277
  }
3048
3278
  }
3049
3279
  if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
3050
3280
  const cursorDir = ".cursor";
3051
- if (!fs8.existsSync(cursorDir)) fs8.mkdirSync(cursorDir, { recursive: true });
3052
- const mcpPath = path7.join(cursorDir, "mcp.json");
3281
+ if (!fs9.existsSync(cursorDir)) fs9.mkdirSync(cursorDir, { recursive: true });
3282
+ const mcpPath = path8.join(cursorDir, "mcp.json");
3053
3283
  let existingServers = {};
3054
3284
  try {
3055
- if (fs8.existsSync(mcpPath)) {
3056
- const existing = JSON.parse(fs8.readFileSync(mcpPath, "utf-8"));
3285
+ if (fs9.existsSync(mcpPath)) {
3286
+ const existing = JSON.parse(fs9.readFileSync(mcpPath, "utf-8"));
3057
3287
  if (existing.mcpServers) existingServers = existing.mcpServers;
3058
3288
  }
3059
3289
  } catch {
3060
3290
  }
3061
3291
  const mergedServers = { ...existingServers, ...config.mcpServers };
3062
- fs8.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
3292
+ fs9.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
3063
3293
  written.push(mcpPath);
3064
3294
  }
3065
3295
  return written;
3066
3296
  }
3067
3297
 
3068
3298
  // src/writers/codex/index.ts
3069
- import fs9 from "fs";
3070
- import path8 from "path";
3299
+ import fs10 from "fs";
3300
+ import path9 from "path";
3071
3301
  function writeCodexConfig(config) {
3072
3302
  const written = [];
3073
- fs9.writeFileSync("AGENTS.md", config.agentsMd);
3303
+ fs10.writeFileSync("AGENTS.md", config.agentsMd);
3074
3304
  written.push("AGENTS.md");
3075
3305
  if (config.skills?.length) {
3076
3306
  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");
3307
+ const skillDir = path9.join(".agents", "skills", skill.name);
3308
+ if (!fs10.existsSync(skillDir)) fs10.mkdirSync(skillDir, { recursive: true });
3309
+ const skillPath = path9.join(skillDir, "SKILL.md");
3080
3310
  const frontmatter = [
3081
3311
  "---",
3082
3312
  `name: ${skill.name}`,
@@ -3084,7 +3314,7 @@ function writeCodexConfig(config) {
3084
3314
  "---",
3085
3315
  ""
3086
3316
  ].join("\n");
3087
- fs9.writeFileSync(skillPath, frontmatter + skill.content);
3317
+ fs10.writeFileSync(skillPath, frontmatter + skill.content);
3088
3318
  written.push(skillPath);
3089
3319
  }
3090
3320
  }
@@ -3093,62 +3323,62 @@ function writeCodexConfig(config) {
3093
3323
 
3094
3324
  // src/writers/backup.ts
3095
3325
  init_constants();
3096
- import fs10 from "fs";
3097
- import path10 from "path";
3326
+ import fs11 from "fs";
3327
+ import path11 from "path";
3098
3328
  function createBackup(files) {
3099
3329
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3100
- const backupDir = path10.join(BACKUPS_DIR, timestamp);
3330
+ const backupDir = path11.join(BACKUPS_DIR, timestamp);
3101
3331
  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 });
3332
+ if (!fs11.existsSync(file)) continue;
3333
+ const dest = path11.join(backupDir, file);
3334
+ const destDir = path11.dirname(dest);
3335
+ if (!fs11.existsSync(destDir)) {
3336
+ fs11.mkdirSync(destDir, { recursive: true });
3107
3337
  }
3108
- fs10.copyFileSync(file, dest);
3338
+ fs11.copyFileSync(file, dest);
3109
3339
  }
3110
3340
  return backupDir;
3111
3341
  }
3112
3342
  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 });
3343
+ const backupFile = path11.join(backupDir, file);
3344
+ if (!fs11.existsSync(backupFile)) return false;
3345
+ const destDir = path11.dirname(file);
3346
+ if (!fs11.existsSync(destDir)) {
3347
+ fs11.mkdirSync(destDir, { recursive: true });
3118
3348
  }
3119
- fs10.copyFileSync(backupFile, file);
3349
+ fs11.copyFileSync(backupFile, file);
3120
3350
  return true;
3121
3351
  }
3122
3352
 
3123
3353
  // src/writers/manifest.ts
3124
3354
  init_constants();
3125
- import fs11 from "fs";
3126
- import crypto from "crypto";
3355
+ import fs12 from "fs";
3356
+ import crypto2 from "crypto";
3127
3357
  function readManifest() {
3128
3358
  try {
3129
- if (!fs11.existsSync(MANIFEST_FILE)) return null;
3130
- return JSON.parse(fs11.readFileSync(MANIFEST_FILE, "utf-8"));
3359
+ if (!fs12.existsSync(MANIFEST_FILE)) return null;
3360
+ return JSON.parse(fs12.readFileSync(MANIFEST_FILE, "utf-8"));
3131
3361
  } catch {
3132
3362
  return null;
3133
3363
  }
3134
3364
  }
3135
3365
  function writeManifest(manifest) {
3136
- if (!fs11.existsSync(CALIBER_DIR)) {
3137
- fs11.mkdirSync(CALIBER_DIR, { recursive: true });
3366
+ if (!fs12.existsSync(CALIBER_DIR)) {
3367
+ fs12.mkdirSync(CALIBER_DIR, { recursive: true });
3138
3368
  }
3139
- fs11.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
3369
+ fs12.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
3140
3370
  }
3141
3371
  function fileChecksum(filePath) {
3142
- const content = fs11.readFileSync(filePath);
3143
- return crypto.createHash("sha256").update(content).digest("hex");
3372
+ const content = fs12.readFileSync(filePath);
3373
+ return crypto2.createHash("sha256").update(content).digest("hex");
3144
3374
  }
3145
3375
 
3146
3376
  // src/writers/index.ts
3147
3377
  function writeSetup(setup) {
3148
3378
  const filesToWrite = getFilesToWrite(setup);
3149
- const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs12.existsSync(f));
3379
+ const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs13.existsSync(f));
3150
3380
  const existingFiles = [
3151
- ...filesToWrite.filter((f) => fs12.existsSync(f)),
3381
+ ...filesToWrite.filter((f) => fs13.existsSync(f)),
3152
3382
  ...filesToDelete
3153
3383
  ];
3154
3384
  const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
@@ -3164,7 +3394,7 @@ function writeSetup(setup) {
3164
3394
  }
3165
3395
  const deleted = [];
3166
3396
  for (const filePath of filesToDelete) {
3167
- fs12.unlinkSync(filePath);
3397
+ fs13.unlinkSync(filePath);
3168
3398
  deleted.push(filePath);
3169
3399
  }
3170
3400
  ensureGitignore();
@@ -3194,8 +3424,8 @@ function undoSetup() {
3194
3424
  const removed = [];
3195
3425
  for (const entry of manifest.entries) {
3196
3426
  if (entry.action === "created") {
3197
- if (fs12.existsSync(entry.path)) {
3198
- fs12.unlinkSync(entry.path);
3427
+ if (fs13.existsSync(entry.path)) {
3428
+ fs13.unlinkSync(entry.path);
3199
3429
  removed.push(entry.path);
3200
3430
  }
3201
3431
  } else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
@@ -3205,8 +3435,8 @@ function undoSetup() {
3205
3435
  }
3206
3436
  }
3207
3437
  const { MANIFEST_FILE: MANIFEST_FILE2 } = (init_constants(), __toCommonJS(constants_exports));
3208
- if (fs12.existsSync(MANIFEST_FILE2)) {
3209
- fs12.unlinkSync(MANIFEST_FILE2);
3438
+ if (fs13.existsSync(MANIFEST_FILE2)) {
3439
+ fs13.unlinkSync(MANIFEST_FILE2);
3210
3440
  }
3211
3441
  return { restored, removed };
3212
3442
  }
@@ -3241,23 +3471,23 @@ function getFilesToWrite(setup) {
3241
3471
  }
3242
3472
  function ensureGitignore() {
3243
3473
  const gitignorePath = ".gitignore";
3244
- if (fs12.existsSync(gitignorePath)) {
3245
- const content = fs12.readFileSync(gitignorePath, "utf-8");
3474
+ if (fs13.existsSync(gitignorePath)) {
3475
+ const content = fs13.readFileSync(gitignorePath, "utf-8");
3246
3476
  if (!content.includes(".caliber/")) {
3247
- fs12.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
3477
+ fs13.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
3248
3478
  }
3249
3479
  } else {
3250
- fs12.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
3480
+ fs13.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
3251
3481
  }
3252
3482
  }
3253
3483
 
3254
3484
  // src/writers/staging.ts
3255
3485
  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");
3486
+ import fs14 from "fs";
3487
+ import path12 from "path";
3488
+ var STAGED_DIR = path12.join(CALIBER_DIR, "staged");
3489
+ var PROPOSED_DIR = path12.join(STAGED_DIR, "proposed");
3490
+ var CURRENT_DIR = path12.join(STAGED_DIR, "current");
3261
3491
  function normalizeContent(content) {
3262
3492
  return content.split("\n").map((line) => line.trimEnd()).join("\n").replace(/\n{3,}/g, "\n\n").trim();
3263
3493
  }
@@ -3267,20 +3497,20 @@ function stageFiles(files, projectDir) {
3267
3497
  let modifiedFiles = 0;
3268
3498
  const stagedFiles = [];
3269
3499
  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");
3500
+ const originalPath = path12.join(projectDir, file.path);
3501
+ if (fs14.existsSync(originalPath)) {
3502
+ const existing = fs14.readFileSync(originalPath, "utf-8");
3273
3503
  if (normalizeContent(existing) === normalizeContent(file.content)) {
3274
3504
  continue;
3275
3505
  }
3276
3506
  }
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);
3507
+ const proposedPath = path12.join(PROPOSED_DIR, file.path);
3508
+ fs14.mkdirSync(path12.dirname(proposedPath), { recursive: true });
3509
+ fs14.writeFileSync(proposedPath, file.content);
3510
+ if (fs14.existsSync(originalPath)) {
3511
+ const currentPath = path12.join(CURRENT_DIR, file.path);
3512
+ fs14.mkdirSync(path12.dirname(currentPath), { recursive: true });
3513
+ fs14.copyFileSync(originalPath, currentPath);
3284
3514
  modifiedFiles++;
3285
3515
  stagedFiles.push({ relativePath: file.path, proposedPath, currentPath, originalPath, isNew: false });
3286
3516
  } else {
@@ -3291,34 +3521,34 @@ function stageFiles(files, projectDir) {
3291
3521
  return { newFiles, modifiedFiles, stagedFiles };
3292
3522
  }
3293
3523
  function cleanupStaging() {
3294
- if (fs13.existsSync(STAGED_DIR)) {
3295
- fs13.rmSync(STAGED_DIR, { recursive: true, force: true });
3524
+ if (fs14.existsSync(STAGED_DIR)) {
3525
+ fs14.rmSync(STAGED_DIR, { recursive: true, force: true });
3296
3526
  }
3297
3527
  }
3298
3528
 
3299
3529
  // src/utils/review.ts
3300
3530
  import chalk2 from "chalk";
3301
- import fs15 from "fs";
3531
+ import fs16 from "fs";
3302
3532
  import select2 from "@inquirer/select";
3303
3533
  import { createTwoFilesPatch } from "diff";
3304
3534
 
3305
3535
  // 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";
3536
+ import { execSync as execSync6, spawn as spawn3 } from "child_process";
3537
+ import fs15 from "fs";
3538
+ import path13 from "path";
3309
3539
  import os4 from "os";
3310
3540
  var IS_WINDOWS3 = process.platform === "win32";
3311
- var DIFF_TEMP_DIR = path12.join(os4.tmpdir(), "caliber-diff");
3541
+ var DIFF_TEMP_DIR = path13.join(os4.tmpdir(), "caliber-diff");
3312
3542
  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, "");
3543
+ fs15.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
3544
+ const tempPath = path13.join(DIFF_TEMP_DIR, path13.basename(proposedPath));
3545
+ fs15.writeFileSync(tempPath, "");
3316
3546
  return tempPath;
3317
3547
  }
3318
3548
  function commandExists(cmd) {
3319
3549
  try {
3320
3550
  const check = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
3321
- execSync5(check, { stdio: "ignore" });
3551
+ execSync6(check, { stdio: "ignore" });
3322
3552
  return true;
3323
3553
  } catch {
3324
3554
  return false;
@@ -3383,8 +3613,8 @@ async function openReview(method, stagedFiles) {
3383
3613
  return;
3384
3614
  }
3385
3615
  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") : "";
3616
+ const proposed = fs16.readFileSync(file.proposedPath, "utf-8");
3617
+ const current = file.currentPath ? fs16.readFileSync(file.currentPath, "utf-8") : "";
3388
3618
  const patch = createTwoFilesPatch(
3389
3619
  file.isNew ? "/dev/null" : file.relativePath,
3390
3620
  file.relativePath,
@@ -3547,7 +3777,7 @@ async function interactiveDiffExplorer(files) {
3547
3777
  }
3548
3778
 
3549
3779
  // src/commands/setup-files.ts
3550
- import fs16 from "fs";
3780
+ import fs17 from "fs";
3551
3781
  function buildSkillContent(skill) {
3552
3782
  const frontmatter = `---
3553
3783
  name: ${skill.name}
@@ -3596,7 +3826,7 @@ function collectSetupFiles(setup, targetAgent) {
3596
3826
  }
3597
3827
  }
3598
3828
  const codexTargeted = targetAgent ? targetAgent.includes("codex") : false;
3599
- if (codexTargeted && !fs16.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
3829
+ if (codexTargeted && !fs17.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
3600
3830
  const agentRefs = [];
3601
3831
  if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
3602
3832
  if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
@@ -3613,13 +3843,13 @@ ${agentRefs.join(" ")}
3613
3843
  }
3614
3844
 
3615
3845
  // src/lib/hooks.ts
3616
- import fs18 from "fs";
3617
- import path13 from "path";
3618
- import { execSync as execSync7 } from "child_process";
3846
+ import fs19 from "fs";
3847
+ import path14 from "path";
3848
+ import { execSync as execSync8 } from "child_process";
3619
3849
 
3620
3850
  // src/lib/resolve-caliber.ts
3621
- import fs17 from "fs";
3622
- import { execSync as execSync6 } from "child_process";
3851
+ import fs18 from "fs";
3852
+ import { execSync as execSync7 } from "child_process";
3623
3853
  var _resolved = null;
3624
3854
  function resolveCaliber() {
3625
3855
  if (_resolved) return _resolved;
@@ -3630,7 +3860,7 @@ function resolveCaliber() {
3630
3860
  }
3631
3861
  try {
3632
3862
  const whichCmd = process.platform === "win32" ? "where caliber" : "which caliber";
3633
- const found = execSync6(whichCmd, {
3863
+ const found = execSync7(whichCmd, {
3634
3864
  encoding: "utf-8",
3635
3865
  stdio: ["pipe", "pipe", "pipe"]
3636
3866
  }).trim();
@@ -3641,7 +3871,7 @@ function resolveCaliber() {
3641
3871
  } catch {
3642
3872
  }
3643
3873
  const binPath = process.argv[1];
3644
- if (binPath && fs17.existsSync(binPath)) {
3874
+ if (binPath && fs18.existsSync(binPath)) {
3645
3875
  _resolved = binPath;
3646
3876
  return _resolved;
3647
3877
  }
@@ -3657,24 +3887,24 @@ function isCaliberCommand(command, subcommandTail) {
3657
3887
  }
3658
3888
 
3659
3889
  // src/lib/hooks.ts
3660
- var SETTINGS_PATH = path13.join(".claude", "settings.json");
3890
+ var SETTINGS_PATH = path14.join(".claude", "settings.json");
3661
3891
  var REFRESH_TAIL = "refresh --quiet";
3662
3892
  var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
3663
3893
  function getHookCommand() {
3664
3894
  return `${resolveCaliber()} ${REFRESH_TAIL}`;
3665
3895
  }
3666
3896
  function readSettings() {
3667
- if (!fs18.existsSync(SETTINGS_PATH)) return {};
3897
+ if (!fs19.existsSync(SETTINGS_PATH)) return {};
3668
3898
  try {
3669
- return JSON.parse(fs18.readFileSync(SETTINGS_PATH, "utf-8"));
3899
+ return JSON.parse(fs19.readFileSync(SETTINGS_PATH, "utf-8"));
3670
3900
  } catch {
3671
3901
  return {};
3672
3902
  }
3673
3903
  }
3674
3904
  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));
3905
+ const dir = path14.dirname(SETTINGS_PATH);
3906
+ if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
3907
+ fs19.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
3678
3908
  }
3679
3909
  function findHookIndex(sessionEnd) {
3680
3910
  return sessionEnd.findIndex(
@@ -3736,20 +3966,20 @@ ${PRECOMMIT_END}`;
3736
3966
  }
3737
3967
  function getGitHooksDir() {
3738
3968
  try {
3739
- const gitDir = execSync7("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3740
- return path13.join(gitDir, "hooks");
3969
+ const gitDir = execSync8("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3970
+ return path14.join(gitDir, "hooks");
3741
3971
  } catch {
3742
3972
  return null;
3743
3973
  }
3744
3974
  }
3745
3975
  function getPreCommitPath() {
3746
3976
  const hooksDir = getGitHooksDir();
3747
- return hooksDir ? path13.join(hooksDir, "pre-commit") : null;
3977
+ return hooksDir ? path14.join(hooksDir, "pre-commit") : null;
3748
3978
  }
3749
3979
  function isPreCommitHookInstalled() {
3750
3980
  const hookPath = getPreCommitPath();
3751
- if (!hookPath || !fs18.existsSync(hookPath)) return false;
3752
- const content = fs18.readFileSync(hookPath, "utf-8");
3981
+ if (!hookPath || !fs19.existsSync(hookPath)) return false;
3982
+ const content = fs19.readFileSync(hookPath, "utf-8");
3753
3983
  return content.includes(PRECOMMIT_START);
3754
3984
  }
3755
3985
  function installPreCommitHook() {
@@ -3758,43 +3988,43 @@ function installPreCommitHook() {
3758
3988
  }
3759
3989
  const hookPath = getPreCommitPath();
3760
3990
  if (!hookPath) return { installed: false, alreadyInstalled: false };
3761
- const hooksDir = path13.dirname(hookPath);
3762
- if (!fs18.existsSync(hooksDir)) fs18.mkdirSync(hooksDir, { recursive: true });
3991
+ const hooksDir = path14.dirname(hookPath);
3992
+ if (!fs19.existsSync(hooksDir)) fs19.mkdirSync(hooksDir, { recursive: true });
3763
3993
  let content = "";
3764
- if (fs18.existsSync(hookPath)) {
3765
- content = fs18.readFileSync(hookPath, "utf-8");
3994
+ if (fs19.existsSync(hookPath)) {
3995
+ content = fs19.readFileSync(hookPath, "utf-8");
3766
3996
  if (!content.endsWith("\n")) content += "\n";
3767
3997
  content += "\n" + getPrecommitBlock() + "\n";
3768
3998
  } else {
3769
3999
  content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
3770
4000
  }
3771
- fs18.writeFileSync(hookPath, content);
3772
- fs18.chmodSync(hookPath, 493);
4001
+ fs19.writeFileSync(hookPath, content);
4002
+ fs19.chmodSync(hookPath, 493);
3773
4003
  return { installed: true, alreadyInstalled: false };
3774
4004
  }
3775
4005
  function removePreCommitHook() {
3776
4006
  const hookPath = getPreCommitPath();
3777
- if (!hookPath || !fs18.existsSync(hookPath)) {
4007
+ if (!hookPath || !fs19.existsSync(hookPath)) {
3778
4008
  return { removed: false, notFound: true };
3779
4009
  }
3780
- let content = fs18.readFileSync(hookPath, "utf-8");
4010
+ let content = fs19.readFileSync(hookPath, "utf-8");
3781
4011
  if (!content.includes(PRECOMMIT_START)) {
3782
4012
  return { removed: false, notFound: true };
3783
4013
  }
3784
4014
  const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
3785
4015
  content = content.replace(regex, "\n");
3786
4016
  if (content.trim() === "#!/bin/sh" || content.trim() === "") {
3787
- fs18.unlinkSync(hookPath);
4017
+ fs19.unlinkSync(hookPath);
3788
4018
  } else {
3789
- fs18.writeFileSync(hookPath, content);
4019
+ fs19.writeFileSync(hookPath, content);
3790
4020
  }
3791
4021
  return { removed: true, notFound: false };
3792
4022
  }
3793
4023
 
3794
4024
  // src/lib/learning-hooks.ts
3795
- import fs19 from "fs";
3796
- import path14 from "path";
3797
- var SETTINGS_PATH2 = path14.join(".claude", "settings.json");
4025
+ import fs20 from "fs";
4026
+ import path15 from "path";
4027
+ var SETTINGS_PATH2 = path15.join(".claude", "settings.json");
3798
4028
  var HOOK_TAILS = [
3799
4029
  { event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
3800
4030
  { event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
@@ -3811,17 +4041,17 @@ function getHookConfigs() {
3811
4041
  }));
3812
4042
  }
3813
4043
  function readSettings2() {
3814
- if (!fs19.existsSync(SETTINGS_PATH2)) return {};
4044
+ if (!fs20.existsSync(SETTINGS_PATH2)) return {};
3815
4045
  try {
3816
- return JSON.parse(fs19.readFileSync(SETTINGS_PATH2, "utf-8"));
4046
+ return JSON.parse(fs20.readFileSync(SETTINGS_PATH2, "utf-8"));
3817
4047
  } catch {
3818
4048
  return {};
3819
4049
  }
3820
4050
  }
3821
4051
  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));
4052
+ const dir = path15.dirname(SETTINGS_PATH2);
4053
+ if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
4054
+ fs20.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
3825
4055
  }
3826
4056
  function hasLearningHook(matchers, tail) {
3827
4057
  return matchers.some((entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, tail)));
@@ -3855,7 +4085,7 @@ function installLearningHooks() {
3855
4085
  writeSettings2(settings);
3856
4086
  return { installed: true, alreadyInstalled: false };
3857
4087
  }
3858
- var CURSOR_HOOKS_PATH = path14.join(".cursor", "hooks.json");
4088
+ var CURSOR_HOOKS_PATH = path15.join(".cursor", "hooks.json");
3859
4089
  var CURSOR_HOOK_EVENTS = [
3860
4090
  { event: "postToolUse", tail: "learn observe" },
3861
4091
  { event: "postToolUseFailure", tail: "learn observe --failure" },
@@ -3863,17 +4093,17 @@ var CURSOR_HOOK_EVENTS = [
3863
4093
  { event: "sessionEnd", tail: "learn finalize" }
3864
4094
  ];
3865
4095
  function readCursorHooks() {
3866
- if (!fs19.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
4096
+ if (!fs20.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
3867
4097
  try {
3868
- return JSON.parse(fs19.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
4098
+ return JSON.parse(fs20.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
3869
4099
  } catch {
3870
4100
  return { version: 1, hooks: {} };
3871
4101
  }
3872
4102
  }
3873
4103
  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));
4104
+ const dir = path15.dirname(CURSOR_HOOKS_PATH);
4105
+ if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
4106
+ fs20.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
3877
4107
  }
3878
4108
  function hasCursorHook(entries, tail) {
3879
4109
  return entries.some((e) => isCaliberCommand(e.command, tail));
@@ -3943,10 +4173,10 @@ function removeLearningHooks() {
3943
4173
 
3944
4174
  // src/lib/state.ts
3945
4175
  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");
4176
+ import fs21 from "fs";
4177
+ import path16 from "path";
4178
+ import { execSync as execSync9 } from "child_process";
4179
+ var STATE_FILE = path16.join(CALIBER_DIR, ".caliber-state.json");
3950
4180
  function normalizeTargetAgent(value) {
3951
4181
  if (Array.isArray(value)) return value;
3952
4182
  if (typeof value === "string") {
@@ -3957,8 +4187,8 @@ function normalizeTargetAgent(value) {
3957
4187
  }
3958
4188
  function readState() {
3959
4189
  try {
3960
- if (!fs20.existsSync(STATE_FILE)) return null;
3961
- const raw = JSON.parse(fs20.readFileSync(STATE_FILE, "utf-8"));
4190
+ if (!fs21.existsSync(STATE_FILE)) return null;
4191
+ const raw = JSON.parse(fs21.readFileSync(STATE_FILE, "utf-8"));
3962
4192
  if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
3963
4193
  return raw;
3964
4194
  } catch {
@@ -3966,14 +4196,14 @@ function readState() {
3966
4196
  }
3967
4197
  }
3968
4198
  function writeState(state) {
3969
- if (!fs20.existsSync(CALIBER_DIR)) {
3970
- fs20.mkdirSync(CALIBER_DIR, { recursive: true });
4199
+ if (!fs21.existsSync(CALIBER_DIR)) {
4200
+ fs21.mkdirSync(CALIBER_DIR, { recursive: true });
3971
4201
  }
3972
- fs20.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
4202
+ fs21.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
3973
4203
  }
3974
4204
  function getCurrentHeadSha() {
3975
4205
  try {
3976
- return execSync8("git rev-parse HEAD", {
4206
+ return execSync9("git rev-parse HEAD", {
3977
4207
  encoding: "utf-8",
3978
4208
  stdio: ["pipe", "pipe", "pipe"]
3979
4209
  }).trim();
@@ -4126,8 +4356,11 @@ async function runInteractiveProviderSetup(options) {
4126
4356
  console.log(chalk5.dim(" Then run ") + chalk5.hex("#83D1EB")("agent login") + chalk5.dim(" to authenticate.\n"));
4127
4357
  const proceed = await confirm({ message: "Continue anyway?" });
4128
4358
  if (!proceed) throw new Error("__exit__");
4129
- } else {
4130
- console.log(chalk5.dim(" Run `agent login` if you haven't, or set CURSOR_API_KEY."));
4359
+ } else if (!isCursorLoggedIn()) {
4360
+ console.log(chalk5.yellow("\n Cursor Agent CLI found but not logged in."));
4361
+ console.log(chalk5.dim(" Run ") + chalk5.hex("#83D1EB")("agent login") + chalk5.dim(" to authenticate.\n"));
4362
+ const proceed = await confirm({ message: "Continue anyway?" });
4363
+ if (!proceed) throw new Error("__exit__");
4131
4364
  }
4132
4365
  break;
4133
4366
  }
@@ -4918,7 +5151,7 @@ function checkGrounding(dir) {
4918
5151
 
4919
5152
  // src/scoring/checks/accuracy.ts
4920
5153
  import { existsSync as existsSync4, statSync as statSync2 } from "fs";
4921
- import { execSync as execSync9 } from "child_process";
5154
+ import { execSync as execSync10 } from "child_process";
4922
5155
  import { join as join5 } from "path";
4923
5156
  function validateReferences(dir) {
4924
5157
  const configContent = collectPrimaryConfigContent(dir);
@@ -4927,13 +5160,13 @@ function validateReferences(dir) {
4927
5160
  }
4928
5161
  function detectGitDrift(dir) {
4929
5162
  try {
4930
- execSync9("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
5163
+ execSync10("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
4931
5164
  } catch {
4932
5165
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: false };
4933
5166
  }
4934
5167
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".cursor/rules"];
4935
5168
  try {
4936
- const headTimestamp = execSync9(
5169
+ const headTimestamp = execSync10(
4937
5170
  "git log -1 --format=%ct HEAD",
4938
5171
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4939
5172
  ).trim();
@@ -4954,7 +5187,7 @@ function detectGitDrift(dir) {
4954
5187
  let latestConfigCommitHash = null;
4955
5188
  for (const file of configFiles) {
4956
5189
  try {
4957
- const hash = execSync9(
5190
+ const hash = execSync10(
4958
5191
  `git log -1 --format=%H -- "${file}"`,
4959
5192
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4960
5193
  ).trim();
@@ -4963,7 +5196,7 @@ function detectGitDrift(dir) {
4963
5196
  latestConfigCommitHash = hash;
4964
5197
  } else {
4965
5198
  try {
4966
- execSync9(
5199
+ execSync10(
4967
5200
  `git merge-base --is-ancestor ${latestConfigCommitHash} ${hash}`,
4968
5201
  { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }
4969
5202
  );
@@ -4978,12 +5211,12 @@ function detectGitDrift(dir) {
4978
5211
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: true };
4979
5212
  }
4980
5213
  try {
4981
- const countStr = execSync9(
5214
+ const countStr = execSync10(
4982
5215
  `git rev-list --count ${latestConfigCommitHash}..HEAD`,
4983
5216
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4984
5217
  ).trim();
4985
5218
  const commitsSince = parseInt(countStr, 10) || 0;
4986
- const lastDate = execSync9(
5219
+ const lastDate = execSync10(
4987
5220
  `git log -1 --format=%ci ${latestConfigCommitHash}`,
4988
5221
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4989
5222
  ).trim();
@@ -5055,12 +5288,12 @@ function checkAccuracy(dir) {
5055
5288
 
5056
5289
  // src/scoring/checks/freshness.ts
5057
5290
  import { existsSync as existsSync5, statSync as statSync3 } from "fs";
5058
- import { execSync as execSync10 } from "child_process";
5291
+ import { execSync as execSync11 } from "child_process";
5059
5292
  import { join as join6 } from "path";
5060
5293
  function getCommitsSinceConfigUpdate(dir) {
5061
5294
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules"];
5062
5295
  try {
5063
- const headTimestamp = execSync10(
5296
+ const headTimestamp = execSync11(
5064
5297
  "git log -1 --format=%ct HEAD",
5065
5298
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5066
5299
  ).trim();
@@ -5080,12 +5313,12 @@ function getCommitsSinceConfigUpdate(dir) {
5080
5313
  }
5081
5314
  for (const file of configFiles) {
5082
5315
  try {
5083
- const hash = execSync10(
5316
+ const hash = execSync11(
5084
5317
  `git log -1 --format=%H -- "${file}"`,
5085
5318
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5086
5319
  ).trim();
5087
5320
  if (hash) {
5088
- const countStr = execSync10(
5321
+ const countStr = execSync11(
5089
5322
  `git rev-list --count ${hash}..HEAD`,
5090
5323
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5091
5324
  ).trim();
@@ -5203,11 +5436,11 @@ function checkFreshness(dir) {
5203
5436
 
5204
5437
  // src/scoring/checks/bonus.ts
5205
5438
  import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
5206
- import { execSync as execSync11 } from "child_process";
5439
+ import { execSync as execSync12 } from "child_process";
5207
5440
  import { join as join7 } from "path";
5208
5441
  function hasPreCommitHook(dir) {
5209
5442
  try {
5210
- const gitDir = execSync11("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
5443
+ const gitDir = execSync12("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
5211
5444
  const hookPath = join7(gitDir, "hooks", "pre-commit");
5212
5445
  const content = readFileOrNull2(hookPath);
5213
5446
  return content ? content.includes("caliber") : false;
@@ -5321,22 +5554,22 @@ function checkBonus(dir) {
5321
5554
 
5322
5555
  // src/scoring/dismissed.ts
5323
5556
  init_constants();
5324
- import fs21 from "fs";
5325
- import path16 from "path";
5326
- var DISMISSED_FILE = path16.join(CALIBER_DIR, "dismissed-checks.json");
5557
+ import fs22 from "fs";
5558
+ import path17 from "path";
5559
+ var DISMISSED_FILE = path17.join(CALIBER_DIR, "dismissed-checks.json");
5327
5560
  function readDismissedChecks() {
5328
5561
  try {
5329
- if (!fs21.existsSync(DISMISSED_FILE)) return [];
5330
- return JSON.parse(fs21.readFileSync(DISMISSED_FILE, "utf-8"));
5562
+ if (!fs22.existsSync(DISMISSED_FILE)) return [];
5563
+ return JSON.parse(fs22.readFileSync(DISMISSED_FILE, "utf-8"));
5331
5564
  } catch {
5332
5565
  return [];
5333
5566
  }
5334
5567
  }
5335
5568
  function writeDismissedChecks(checks) {
5336
- if (!fs21.existsSync(CALIBER_DIR)) {
5337
- fs21.mkdirSync(CALIBER_DIR, { recursive: true });
5569
+ if (!fs22.existsSync(CALIBER_DIR)) {
5570
+ fs22.mkdirSync(CALIBER_DIR, { recursive: true });
5338
5571
  }
5339
- fs21.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
5572
+ fs22.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
5340
5573
  }
5341
5574
  function getDismissedIds() {
5342
5575
  return new Set(readDismissedChecks().map((c) => c.id));
@@ -5581,13 +5814,13 @@ import { mkdirSync, readFileSync as readFileSync4, readdirSync as readdirSync4,
5581
5814
  import { join as join9, dirname as dirname2 } from "path";
5582
5815
 
5583
5816
  // src/scanner/index.ts
5584
- import fs22 from "fs";
5585
- import path17 from "path";
5586
- import crypto2 from "crypto";
5817
+ import fs23 from "fs";
5818
+ import path18 from "path";
5819
+ import crypto3 from "crypto";
5587
5820
  function scanLocalState(dir) {
5588
5821
  const items = [];
5589
- const claudeMdPath = path17.join(dir, "CLAUDE.md");
5590
- if (fs22.existsSync(claudeMdPath)) {
5822
+ const claudeMdPath = path18.join(dir, "CLAUDE.md");
5823
+ if (fs23.existsSync(claudeMdPath)) {
5591
5824
  items.push({
5592
5825
  type: "rule",
5593
5826
  platform: "claude",
@@ -5596,10 +5829,10 @@ function scanLocalState(dir) {
5596
5829
  path: claudeMdPath
5597
5830
  });
5598
5831
  }
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);
5832
+ const skillsDir = path18.join(dir, ".claude", "skills");
5833
+ if (fs23.existsSync(skillsDir)) {
5834
+ for (const file of fs23.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
5835
+ const filePath = path18.join(skillsDir, file);
5603
5836
  items.push({
5604
5837
  type: "skill",
5605
5838
  platform: "claude",
@@ -5609,10 +5842,10 @@ function scanLocalState(dir) {
5609
5842
  });
5610
5843
  }
5611
5844
  }
5612
- const mcpJsonPath = path17.join(dir, ".mcp.json");
5613
- if (fs22.existsSync(mcpJsonPath)) {
5845
+ const mcpJsonPath = path18.join(dir, ".mcp.json");
5846
+ if (fs23.existsSync(mcpJsonPath)) {
5614
5847
  try {
5615
- const mcpJson = JSON.parse(fs22.readFileSync(mcpJsonPath, "utf-8"));
5848
+ const mcpJson = JSON.parse(fs23.readFileSync(mcpJsonPath, "utf-8"));
5616
5849
  if (mcpJson.mcpServers) {
5617
5850
  for (const name of Object.keys(mcpJson.mcpServers)) {
5618
5851
  items.push({
@@ -5627,8 +5860,8 @@ function scanLocalState(dir) {
5627
5860
  } catch {
5628
5861
  }
5629
5862
  }
5630
- const agentsMdPath = path17.join(dir, "AGENTS.md");
5631
- if (fs22.existsSync(agentsMdPath)) {
5863
+ const agentsMdPath = path18.join(dir, "AGENTS.md");
5864
+ if (fs23.existsSync(agentsMdPath)) {
5632
5865
  items.push({
5633
5866
  type: "rule",
5634
5867
  platform: "codex",
@@ -5637,12 +5870,12 @@ function scanLocalState(dir) {
5637
5870
  path: agentsMdPath
5638
5871
  });
5639
5872
  }
5640
- const codexSkillsDir = path17.join(dir, ".agents", "skills");
5641
- if (fs22.existsSync(codexSkillsDir)) {
5873
+ const codexSkillsDir = path18.join(dir, ".agents", "skills");
5874
+ if (fs23.existsSync(codexSkillsDir)) {
5642
5875
  try {
5643
- for (const name of fs22.readdirSync(codexSkillsDir)) {
5644
- const skillFile = path17.join(codexSkillsDir, name, "SKILL.md");
5645
- if (fs22.existsSync(skillFile)) {
5876
+ for (const name of fs23.readdirSync(codexSkillsDir)) {
5877
+ const skillFile = path18.join(codexSkillsDir, name, "SKILL.md");
5878
+ if (fs23.existsSync(skillFile)) {
5646
5879
  items.push({
5647
5880
  type: "skill",
5648
5881
  platform: "codex",
@@ -5655,8 +5888,8 @@ function scanLocalState(dir) {
5655
5888
  } catch {
5656
5889
  }
5657
5890
  }
5658
- const cursorrulesPath = path17.join(dir, ".cursorrules");
5659
- if (fs22.existsSync(cursorrulesPath)) {
5891
+ const cursorrulesPath = path18.join(dir, ".cursorrules");
5892
+ if (fs23.existsSync(cursorrulesPath)) {
5660
5893
  items.push({
5661
5894
  type: "rule",
5662
5895
  platform: "cursor",
@@ -5665,10 +5898,10 @@ function scanLocalState(dir) {
5665
5898
  path: cursorrulesPath
5666
5899
  });
5667
5900
  }
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);
5901
+ const cursorRulesDir = path18.join(dir, ".cursor", "rules");
5902
+ if (fs23.existsSync(cursorRulesDir)) {
5903
+ for (const file of fs23.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
5904
+ const filePath = path18.join(cursorRulesDir, file);
5672
5905
  items.push({
5673
5906
  type: "rule",
5674
5907
  platform: "cursor",
@@ -5678,12 +5911,12 @@ function scanLocalState(dir) {
5678
5911
  });
5679
5912
  }
5680
5913
  }
5681
- const cursorSkillsDir = path17.join(dir, ".cursor", "skills");
5682
- if (fs22.existsSync(cursorSkillsDir)) {
5914
+ const cursorSkillsDir = path18.join(dir, ".cursor", "skills");
5915
+ if (fs23.existsSync(cursorSkillsDir)) {
5683
5916
  try {
5684
- for (const name of fs22.readdirSync(cursorSkillsDir)) {
5685
- const skillFile = path17.join(cursorSkillsDir, name, "SKILL.md");
5686
- if (fs22.existsSync(skillFile)) {
5917
+ for (const name of fs23.readdirSync(cursorSkillsDir)) {
5918
+ const skillFile = path18.join(cursorSkillsDir, name, "SKILL.md");
5919
+ if (fs23.existsSync(skillFile)) {
5687
5920
  items.push({
5688
5921
  type: "skill",
5689
5922
  platform: "cursor",
@@ -5696,10 +5929,10 @@ function scanLocalState(dir) {
5696
5929
  } catch {
5697
5930
  }
5698
5931
  }
5699
- const cursorMcpPath = path17.join(dir, ".cursor", "mcp.json");
5700
- if (fs22.existsSync(cursorMcpPath)) {
5932
+ const cursorMcpPath = path18.join(dir, ".cursor", "mcp.json");
5933
+ if (fs23.existsSync(cursorMcpPath)) {
5701
5934
  try {
5702
- const mcpJson = JSON.parse(fs22.readFileSync(cursorMcpPath, "utf-8"));
5935
+ const mcpJson = JSON.parse(fs23.readFileSync(cursorMcpPath, "utf-8"));
5703
5936
  if (mcpJson.mcpServers) {
5704
5937
  for (const name of Object.keys(mcpJson.mcpServers)) {
5705
5938
  items.push({
@@ -5717,11 +5950,11 @@ function scanLocalState(dir) {
5717
5950
  return items;
5718
5951
  }
5719
5952
  function hashFile(filePath) {
5720
- const text = fs22.readFileSync(filePath, "utf-8");
5721
- return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
5953
+ const text = fs23.readFileSync(filePath, "utf-8");
5954
+ return crypto3.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
5722
5955
  }
5723
5956
  function hashJson(obj) {
5724
- return crypto2.createHash("sha256").update(JSON.stringify(obj)).digest("hex");
5957
+ return crypto3.createHash("sha256").update(JSON.stringify(obj)).digest("hex");
5725
5958
  }
5726
5959
 
5727
5960
  // src/commands/recommend.ts
@@ -5732,40 +5965,40 @@ import { PostHog } from "posthog-node";
5732
5965
  import chalk7 from "chalk";
5733
5966
 
5734
5967
  // src/telemetry/config.ts
5735
- import fs23 from "fs";
5736
- import path18 from "path";
5968
+ import fs24 from "fs";
5969
+ import path19 from "path";
5737
5970
  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");
5971
+ import crypto4 from "crypto";
5972
+ import { execSync as execSync13 } from "child_process";
5973
+ var CONFIG_DIR2 = path19.join(os5.homedir(), ".caliber");
5974
+ var CONFIG_FILE2 = path19.join(CONFIG_DIR2, "config.json");
5742
5975
  var runtimeDisabled = false;
5743
5976
  function readConfig() {
5744
5977
  try {
5745
- if (!fs23.existsSync(CONFIG_FILE2)) return {};
5746
- return JSON.parse(fs23.readFileSync(CONFIG_FILE2, "utf-8"));
5978
+ if (!fs24.existsSync(CONFIG_FILE2)) return {};
5979
+ return JSON.parse(fs24.readFileSync(CONFIG_FILE2, "utf-8"));
5747
5980
  } catch {
5748
5981
  return {};
5749
5982
  }
5750
5983
  }
5751
5984
  function writeConfig(config) {
5752
- if (!fs23.existsSync(CONFIG_DIR2)) {
5753
- fs23.mkdirSync(CONFIG_DIR2, { recursive: true });
5985
+ if (!fs24.existsSync(CONFIG_DIR2)) {
5986
+ fs24.mkdirSync(CONFIG_DIR2, { recursive: true });
5754
5987
  }
5755
- fs23.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
5988
+ fs24.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
5756
5989
  }
5757
5990
  function getMachineId() {
5758
5991
  const config = readConfig();
5759
5992
  if (config.machineId) return config.machineId;
5760
- const machineId = crypto3.randomUUID();
5993
+ const machineId = crypto4.randomUUID();
5761
5994
  writeConfig({ ...config, machineId });
5762
5995
  return machineId;
5763
5996
  }
5764
5997
  function getGitEmailHash() {
5765
5998
  try {
5766
- const email = execSync12("git config user.email", { encoding: "utf-8" }).trim();
5999
+ const email = execSync13("git config user.email", { encoding: "utf-8" }).trim();
5767
6000
  if (!email) return void 0;
5768
- return crypto3.createHash("sha256").update(email).digest("hex");
6001
+ return crypto4.createHash("sha256").update(email).digest("hex");
5769
6002
  } catch {
5770
6003
  return void 0;
5771
6004
  }
@@ -6708,11 +6941,11 @@ function countIssuePoints(issues) {
6708
6941
  }
6709
6942
  async function scoreAndRefine(setup, dir, sessionHistory, callbacks) {
6710
6943
  const existsCache = /* @__PURE__ */ new Map();
6711
- const cachedExists = (path29) => {
6712
- const cached = existsCache.get(path29);
6944
+ const cachedExists = (path30) => {
6945
+ const cached = existsCache.get(path30);
6713
6946
  if (cached !== void 0) return cached;
6714
- const result = existsSync9(path29);
6715
- existsCache.set(path29, result);
6947
+ const result = existsSync9(path30);
6948
+ existsCache.set(path30, result);
6716
6949
  return result;
6717
6950
  };
6718
6951
  const projectStructure = collectProjectStructure(dir);
@@ -6851,8 +7084,8 @@ async function runScoreRefineWithSpinner(setup, dir, sessionHistory) {
6851
7084
  }
6852
7085
 
6853
7086
  // src/lib/debug-report.ts
6854
- import fs24 from "fs";
6855
- import path19 from "path";
7087
+ import fs25 from "fs";
7088
+ import path20 from "path";
6856
7089
  var DebugReport = class {
6857
7090
  sections = [];
6858
7091
  startTime;
@@ -6921,11 +7154,11 @@ var DebugReport = class {
6921
7154
  lines.push(`| **Total** | **${formatMs(totalMs)}** |`);
6922
7155
  lines.push("");
6923
7156
  }
6924
- const dir = path19.dirname(outputPath);
6925
- if (!fs24.existsSync(dir)) {
6926
- fs24.mkdirSync(dir, { recursive: true });
7157
+ const dir = path20.dirname(outputPath);
7158
+ if (!fs25.existsSync(dir)) {
7159
+ fs25.mkdirSync(dir, { recursive: true });
6927
7160
  }
6928
- fs24.writeFileSync(outputPath, lines.join("\n"));
7161
+ fs25.writeFileSync(outputPath, lines.join("\n"));
6929
7162
  }
6930
7163
  };
6931
7164
  function formatMs(ms) {
@@ -7421,11 +7654,10 @@ async function initCommand(options) {
7421
7654
  try {
7422
7655
  display.update(TASK_STACK, "running");
7423
7656
  fingerprint = await collectFingerprint(process.cwd());
7424
- const stackSummary = [
7425
- ...fingerprint.languages,
7426
- ...fingerprint.frameworks
7427
- ].join(", ") || "no languages";
7428
- display.update(TASK_STACK, "done", stackSummary);
7657
+ const stackParts = [...fingerprint.languages, ...fingerprint.frameworks];
7658
+ const stackSummary = stackParts.join(", ") || "no languages";
7659
+ const largeRepoNote = fingerprint.fileTree.length > 5e3 ? ` (${fingerprint.fileTree.length.toLocaleString()} files, smart sampling active)` : "";
7660
+ display.update(TASK_STACK, "done", stackSummary + largeRepoNote);
7429
7661
  trackInitProjectDiscovered(fingerprint.languages.length, fingerprint.frameworks.length, fingerprint.fileTree.length);
7430
7662
  log(options.verbose, `Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`);
7431
7663
  if (report) {
@@ -7661,7 +7893,7 @@ async function initCommand(options) {
7661
7893
  }
7662
7894
  const writeSpinner = ora3("Writing config files...").start();
7663
7895
  try {
7664
- if (targetAgent.includes("codex") && !fs25.existsSync("AGENTS.md") && !generatedSetup.codex) {
7896
+ if (targetAgent.includes("codex") && !fs26.existsSync("AGENTS.md") && !generatedSetup.codex) {
7665
7897
  const claude = generatedSetup.claude;
7666
7898
  const cursor = generatedSetup.cursor;
7667
7899
  const agentRefs = [];
@@ -7845,9 +8077,9 @@ ${agentRefs.join(" ")}
7845
8077
  }
7846
8078
  if (report) {
7847
8079
  report.markStep("Finished");
7848
- const reportPath = path20.join(process.cwd(), ".caliber", "debug-report.md");
8080
+ const reportPath = path21.join(process.cwd(), ".caliber", "debug-report.md");
7849
8081
  report.write(reportPath);
7850
- console.log(chalk11.dim(` Debug report written to ${path20.relative(process.cwd(), reportPath)}
8082
+ console.log(chalk11.dim(` Debug report written to ${path21.relative(process.cwd(), reportPath)}
7851
8083
  `));
7852
8084
  }
7853
8085
  }
@@ -7894,7 +8126,7 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
7894
8126
  }
7895
8127
  function summarizeSetup(action, setup) {
7896
8128
  const descriptions = setup.fileDescriptions;
7897
- const files = descriptions ? Object.entries(descriptions).map(([path29, desc]) => ` ${path29}: ${desc}`).join("\n") : Object.keys(setup).filter((k) => k !== "targetAgent" && k !== "fileDescriptions").join(", ");
8129
+ const files = descriptions ? Object.entries(descriptions).map(([path30, desc]) => ` ${path30}: ${desc}`).join("\n") : Object.keys(setup).filter((k) => k !== "targetAgent" && k !== "fileDescriptions").join(", ");
7898
8130
  return `${action}. Files:
7899
8131
  ${files}`;
7900
8132
  }
@@ -8028,7 +8260,7 @@ function printSetupSummary(setup) {
8028
8260
  };
8029
8261
  if (claude) {
8030
8262
  if (claude.claudeMd) {
8031
- const icon = fs25.existsSync("CLAUDE.md") ? chalk11.yellow("~") : chalk11.green("+");
8263
+ const icon = fs26.existsSync("CLAUDE.md") ? chalk11.yellow("~") : chalk11.green("+");
8032
8264
  const desc = getDescription("CLAUDE.md");
8033
8265
  console.log(` ${icon} ${chalk11.bold("CLAUDE.md")}`);
8034
8266
  if (desc) console.log(chalk11.dim(` ${desc}`));
@@ -8038,7 +8270,7 @@ function printSetupSummary(setup) {
8038
8270
  if (Array.isArray(skills) && skills.length > 0) {
8039
8271
  for (const skill of skills) {
8040
8272
  const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
8041
- const icon = fs25.existsSync(skillPath) ? chalk11.yellow("~") : chalk11.green("+");
8273
+ const icon = fs26.existsSync(skillPath) ? chalk11.yellow("~") : chalk11.green("+");
8042
8274
  const desc = getDescription(skillPath);
8043
8275
  console.log(` ${icon} ${chalk11.bold(skillPath)}`);
8044
8276
  console.log(chalk11.dim(` ${desc || skill.description || skill.name}`));
@@ -8049,7 +8281,7 @@ function printSetupSummary(setup) {
8049
8281
  const codex = setup.codex;
8050
8282
  if (codex) {
8051
8283
  if (codex.agentsMd) {
8052
- const icon = fs25.existsSync("AGENTS.md") ? chalk11.yellow("~") : chalk11.green("+");
8284
+ const icon = fs26.existsSync("AGENTS.md") ? chalk11.yellow("~") : chalk11.green("+");
8053
8285
  const desc = getDescription("AGENTS.md");
8054
8286
  console.log(` ${icon} ${chalk11.bold("AGENTS.md")}`);
8055
8287
  if (desc) console.log(chalk11.dim(` ${desc}`));
@@ -8059,7 +8291,7 @@ function printSetupSummary(setup) {
8059
8291
  if (Array.isArray(codexSkills) && codexSkills.length > 0) {
8060
8292
  for (const skill of codexSkills) {
8061
8293
  const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
8062
- const icon = fs25.existsSync(skillPath) ? chalk11.yellow("~") : chalk11.green("+");
8294
+ const icon = fs26.existsSync(skillPath) ? chalk11.yellow("~") : chalk11.green("+");
8063
8295
  const desc = getDescription(skillPath);
8064
8296
  console.log(` ${icon} ${chalk11.bold(skillPath)}`);
8065
8297
  console.log(chalk11.dim(` ${desc || skill.description || skill.name}`));
@@ -8069,7 +8301,7 @@ function printSetupSummary(setup) {
8069
8301
  }
8070
8302
  if (cursor) {
8071
8303
  if (cursor.cursorrules) {
8072
- const icon = fs25.existsSync(".cursorrules") ? chalk11.yellow("~") : chalk11.green("+");
8304
+ const icon = fs26.existsSync(".cursorrules") ? chalk11.yellow("~") : chalk11.green("+");
8073
8305
  const desc = getDescription(".cursorrules");
8074
8306
  console.log(` ${icon} ${chalk11.bold(".cursorrules")}`);
8075
8307
  if (desc) console.log(chalk11.dim(` ${desc}`));
@@ -8079,7 +8311,7 @@ function printSetupSummary(setup) {
8079
8311
  if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
8080
8312
  for (const skill of cursorSkills) {
8081
8313
  const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
8082
- const icon = fs25.existsSync(skillPath) ? chalk11.yellow("~") : chalk11.green("+");
8314
+ const icon = fs26.existsSync(skillPath) ? chalk11.yellow("~") : chalk11.green("+");
8083
8315
  const desc = getDescription(skillPath);
8084
8316
  console.log(` ${icon} ${chalk11.bold(skillPath)}`);
8085
8317
  console.log(chalk11.dim(` ${desc || skill.description || skill.name}`));
@@ -8090,7 +8322,7 @@ function printSetupSummary(setup) {
8090
8322
  if (Array.isArray(rules) && rules.length > 0) {
8091
8323
  for (const rule of rules) {
8092
8324
  const rulePath = `.cursor/rules/${rule.filename}`;
8093
- const icon = fs25.existsSync(rulePath) ? chalk11.yellow("~") : chalk11.green("+");
8325
+ const icon = fs26.existsSync(rulePath) ? chalk11.yellow("~") : chalk11.green("+");
8094
8326
  const desc = getDescription(rulePath);
8095
8327
  console.log(` ${icon} ${chalk11.bold(rulePath)}`);
8096
8328
  if (desc) {
@@ -8154,8 +8386,8 @@ function ensurePermissions(fingerprint) {
8154
8386
  const settingsPath = ".claude/settings.json";
8155
8387
  let settings = {};
8156
8388
  try {
8157
- if (fs25.existsSync(settingsPath)) {
8158
- settings = JSON.parse(fs25.readFileSync(settingsPath, "utf-8"));
8389
+ if (fs26.existsSync(settingsPath)) {
8390
+ settings = JSON.parse(fs26.readFileSync(settingsPath, "utf-8"));
8159
8391
  }
8160
8392
  } catch {
8161
8393
  }
@@ -8164,8 +8396,8 @@ function ensurePermissions(fingerprint) {
8164
8396
  if (Array.isArray(allow) && allow.length > 0) return;
8165
8397
  permissions.allow = derivePermissions(fingerprint);
8166
8398
  settings.permissions = permissions;
8167
- if (!fs25.existsSync(".claude")) fs25.mkdirSync(".claude", { recursive: true });
8168
- fs25.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
8399
+ if (!fs26.existsSync(".claude")) fs26.mkdirSync(".claude", { recursive: true });
8400
+ fs26.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
8169
8401
  }
8170
8402
  function displayTokenUsage() {
8171
8403
  const summary = getUsageSummary();
@@ -8189,7 +8421,7 @@ function displayTokenUsage() {
8189
8421
  }
8190
8422
  function writeErrorLog(config, rawOutput, error, stopReason) {
8191
8423
  try {
8192
- const logPath = path20.join(process.cwd(), ".caliber", "error-log.md");
8424
+ const logPath = path21.join(process.cwd(), ".caliber", "error-log.md");
8193
8425
  const lines = [
8194
8426
  `# Generation Error \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
8195
8427
  "",
@@ -8202,8 +8434,8 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
8202
8434
  lines.push("## Error", "```", error, "```", "");
8203
8435
  }
8204
8436
  lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
8205
- fs25.mkdirSync(path20.join(process.cwd(), ".caliber"), { recursive: true });
8206
- fs25.writeFileSync(logPath, lines.join("\n"));
8437
+ fs26.mkdirSync(path21.join(process.cwd(), ".caliber"), { recursive: true });
8438
+ fs26.writeFileSync(logPath, lines.join("\n"));
8207
8439
  console.log(chalk11.dim(`
8208
8440
  Error log written to .caliber/error-log.md`));
8209
8441
  } catch {
@@ -8244,7 +8476,7 @@ function undoCommand() {
8244
8476
 
8245
8477
  // src/commands/status.ts
8246
8478
  import chalk13 from "chalk";
8247
- import fs26 from "fs";
8479
+ import fs27 from "fs";
8248
8480
  init_config();
8249
8481
  async function statusCommand(options) {
8250
8482
  const config = loadConfig();
@@ -8271,7 +8503,7 @@ async function statusCommand(options) {
8271
8503
  }
8272
8504
  console.log(` Files managed: ${chalk13.cyan(manifest.entries.length.toString())}`);
8273
8505
  for (const entry of manifest.entries) {
8274
- const exists = fs26.existsSync(entry.path);
8506
+ const exists = fs27.existsSync(entry.path);
8275
8507
  const icon = exists ? chalk13.green("\u2713") : chalk13.red("\u2717");
8276
8508
  console.log(` ${icon} ${entry.path} (${entry.action})`);
8277
8509
  }
@@ -8453,13 +8685,13 @@ async function scoreCommand(options) {
8453
8685
  }
8454
8686
 
8455
8687
  // src/commands/refresh.ts
8456
- import fs30 from "fs";
8457
- import path24 from "path";
8688
+ import fs31 from "fs";
8689
+ import path25 from "path";
8458
8690
  import chalk16 from "chalk";
8459
8691
  import ora6 from "ora";
8460
8692
 
8461
8693
  // src/lib/git-diff.ts
8462
- import { execSync as execSync13 } from "child_process";
8694
+ import { execSync as execSync14 } from "child_process";
8463
8695
  var MAX_DIFF_BYTES = 1e5;
8464
8696
  var DOC_PATTERNS = [
8465
8697
  "CLAUDE.md",
@@ -8474,7 +8706,7 @@ function excludeArgs() {
8474
8706
  }
8475
8707
  function safeExec(cmd) {
8476
8708
  try {
8477
- return execSync13(cmd, {
8709
+ return execSync14(cmd, {
8478
8710
  encoding: "utf-8",
8479
8711
  stdio: ["pipe", "pipe", "pipe"],
8480
8712
  maxBuffer: 10 * 1024 * 1024
@@ -8532,37 +8764,37 @@ function collectDiff(lastSha) {
8532
8764
  }
8533
8765
 
8534
8766
  // src/writers/refresh.ts
8535
- import fs27 from "fs";
8536
- import path21 from "path";
8767
+ import fs28 from "fs";
8768
+ import path22 from "path";
8537
8769
  function writeRefreshDocs(docs) {
8538
8770
  const written = [];
8539
8771
  if (docs.claudeMd) {
8540
- fs27.writeFileSync("CLAUDE.md", docs.claudeMd);
8772
+ fs28.writeFileSync("CLAUDE.md", docs.claudeMd);
8541
8773
  written.push("CLAUDE.md");
8542
8774
  }
8543
8775
  if (docs.readmeMd) {
8544
- fs27.writeFileSync("README.md", docs.readmeMd);
8776
+ fs28.writeFileSync("README.md", docs.readmeMd);
8545
8777
  written.push("README.md");
8546
8778
  }
8547
8779
  if (docs.cursorrules) {
8548
- fs27.writeFileSync(".cursorrules", docs.cursorrules);
8780
+ fs28.writeFileSync(".cursorrules", docs.cursorrules);
8549
8781
  written.push(".cursorrules");
8550
8782
  }
8551
8783
  if (docs.cursorRules) {
8552
- const rulesDir = path21.join(".cursor", "rules");
8553
- if (!fs27.existsSync(rulesDir)) fs27.mkdirSync(rulesDir, { recursive: true });
8784
+ const rulesDir = path22.join(".cursor", "rules");
8785
+ if (!fs28.existsSync(rulesDir)) fs28.mkdirSync(rulesDir, { recursive: true });
8554
8786
  for (const rule of docs.cursorRules) {
8555
- const filePath = path21.join(rulesDir, rule.filename);
8556
- fs27.writeFileSync(filePath, rule.content);
8787
+ const filePath = path22.join(rulesDir, rule.filename);
8788
+ fs28.writeFileSync(filePath, rule.content);
8557
8789
  written.push(filePath);
8558
8790
  }
8559
8791
  }
8560
8792
  if (docs.claudeSkills) {
8561
- const skillsDir = path21.join(".claude", "skills");
8562
- if (!fs27.existsSync(skillsDir)) fs27.mkdirSync(skillsDir, { recursive: true });
8793
+ const skillsDir = path22.join(".claude", "skills");
8794
+ if (!fs28.existsSync(skillsDir)) fs28.mkdirSync(skillsDir, { recursive: true });
8563
8795
  for (const skill of docs.claudeSkills) {
8564
- const filePath = path21.join(skillsDir, skill.filename);
8565
- fs27.writeFileSync(filePath, skill.content);
8796
+ const filePath = path22.join(skillsDir, skill.filename);
8797
+ fs28.writeFileSync(filePath, skill.content);
8566
8798
  written.push(filePath);
8567
8799
  }
8568
8800
  }
@@ -8639,8 +8871,8 @@ Changed files: ${diff.changedFiles.join(", ")}`);
8639
8871
  }
8640
8872
 
8641
8873
  // src/learner/writer.ts
8642
- import fs28 from "fs";
8643
- import path22 from "path";
8874
+ import fs29 from "fs";
8875
+ import path23 from "path";
8644
8876
  var LEARNINGS_FILE = "CALIBER_LEARNINGS.md";
8645
8877
  var LEARNINGS_HEADER = `# Caliber Learnings
8646
8878
 
@@ -8724,16 +8956,16 @@ function deduplicateLearnedItems(existing, incoming) {
8724
8956
  function writeLearnedSection(content) {
8725
8957
  const existingSection = readLearnedSection();
8726
8958
  const { merged, newCount, newItems } = deduplicateLearnedItems(existingSection, content);
8727
- fs28.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + merged + "\n");
8959
+ fs29.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + merged + "\n");
8728
8960
  return { newCount, newItems };
8729
8961
  }
8730
8962
  function writeLearnedSkill(skill) {
8731
- const skillDir = path22.join(".claude", "skills", skill.name);
8732
- if (!fs28.existsSync(skillDir)) fs28.mkdirSync(skillDir, { recursive: true });
8733
- const skillPath = path22.join(skillDir, "SKILL.md");
8734
- if (!skill.isNew && fs28.existsSync(skillPath)) {
8735
- const existing = fs28.readFileSync(skillPath, "utf-8");
8736
- fs28.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
8963
+ const skillDir = path23.join(".claude", "skills", skill.name);
8964
+ if (!fs29.existsSync(skillDir)) fs29.mkdirSync(skillDir, { recursive: true });
8965
+ const skillPath = path23.join(skillDir, "SKILL.md");
8966
+ if (!skill.isNew && fs29.existsSync(skillPath)) {
8967
+ const existing = fs29.readFileSync(skillPath, "utf-8");
8968
+ fs29.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
8737
8969
  } else {
8738
8970
  const frontmatter = [
8739
8971
  "---",
@@ -8742,37 +8974,37 @@ function writeLearnedSkill(skill) {
8742
8974
  "---",
8743
8975
  ""
8744
8976
  ].join("\n");
8745
- fs28.writeFileSync(skillPath, frontmatter + skill.content);
8977
+ fs29.writeFileSync(skillPath, frontmatter + skill.content);
8746
8978
  }
8747
8979
  return skillPath;
8748
8980
  }
8749
8981
  function readLearnedSection() {
8750
- if (fs28.existsSync(LEARNINGS_FILE)) {
8751
- const content2 = fs28.readFileSync(LEARNINGS_FILE, "utf-8");
8982
+ if (fs29.existsSync(LEARNINGS_FILE)) {
8983
+ const content2 = fs29.readFileSync(LEARNINGS_FILE, "utf-8");
8752
8984
  const bullets = content2.split("\n").filter((l) => l.startsWith("- ")).join("\n");
8753
8985
  return bullets || null;
8754
8986
  }
8755
8987
  const claudeMdPath = "CLAUDE.md";
8756
- if (!fs28.existsSync(claudeMdPath)) return null;
8757
- const content = fs28.readFileSync(claudeMdPath, "utf-8");
8988
+ if (!fs29.existsSync(claudeMdPath)) return null;
8989
+ const content = fs29.readFileSync(claudeMdPath, "utf-8");
8758
8990
  const startIdx = content.indexOf(LEARNED_START);
8759
8991
  const endIdx = content.indexOf(LEARNED_END);
8760
8992
  if (startIdx === -1 || endIdx === -1) return null;
8761
8993
  return content.slice(startIdx + LEARNED_START.length, endIdx).trim() || null;
8762
8994
  }
8763
8995
  function migrateInlineLearnings() {
8764
- if (fs28.existsSync(LEARNINGS_FILE)) return false;
8996
+ if (fs29.existsSync(LEARNINGS_FILE)) return false;
8765
8997
  const claudeMdPath = "CLAUDE.md";
8766
- if (!fs28.existsSync(claudeMdPath)) return false;
8767
- const content = fs28.readFileSync(claudeMdPath, "utf-8");
8998
+ if (!fs29.existsSync(claudeMdPath)) return false;
8999
+ const content = fs29.readFileSync(claudeMdPath, "utf-8");
8768
9000
  const startIdx = content.indexOf(LEARNED_START);
8769
9001
  const endIdx = content.indexOf(LEARNED_END);
8770
9002
  if (startIdx === -1 || endIdx === -1) return false;
8771
9003
  const section = content.slice(startIdx + LEARNED_START.length, endIdx).trim();
8772
9004
  if (!section) return false;
8773
- fs28.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
9005
+ fs29.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
8774
9006
  const cleaned = content.slice(0, startIdx) + content.slice(endIdx + LEARNED_END.length);
8775
- fs28.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
9007
+ fs29.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
8776
9008
  return true;
8777
9009
  }
8778
9010
 
@@ -8784,11 +9016,11 @@ function log2(quiet, ...args) {
8784
9016
  function discoverGitRepos(parentDir) {
8785
9017
  const repos = [];
8786
9018
  try {
8787
- const entries = fs30.readdirSync(parentDir, { withFileTypes: true });
9019
+ const entries = fs31.readdirSync(parentDir, { withFileTypes: true });
8788
9020
  for (const entry of entries) {
8789
9021
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
8790
- const childPath = path24.join(parentDir, entry.name);
8791
- if (fs30.existsSync(path24.join(childPath, ".git"))) {
9022
+ const childPath = path25.join(parentDir, entry.name);
9023
+ if (fs31.existsSync(path25.join(childPath, ".git"))) {
8792
9024
  repos.push(childPath);
8793
9025
  }
8794
9026
  }
@@ -8891,7 +9123,7 @@ async function refreshCommand(options) {
8891
9123
  `));
8892
9124
  const originalDir = process.cwd();
8893
9125
  for (const repo of repos) {
8894
- const repoName = path24.basename(repo);
9126
+ const repoName = path25.basename(repo);
8895
9127
  try {
8896
9128
  process.chdir(repo);
8897
9129
  await refreshSingleRepo(repo, { ...options, label: repoName });
@@ -9115,7 +9347,7 @@ async function configCommand() {
9115
9347
  }
9116
9348
 
9117
9349
  // src/commands/learn.ts
9118
- import fs33 from "fs";
9350
+ import fs34 from "fs";
9119
9351
  import chalk19 from "chalk";
9120
9352
 
9121
9353
  // src/learner/stdin.ts
@@ -9147,8 +9379,8 @@ function readStdin() {
9147
9379
 
9148
9380
  // src/learner/storage.ts
9149
9381
  init_constants();
9150
- import fs31 from "fs";
9151
- import path25 from "path";
9382
+ import fs32 from "fs";
9383
+ import path26 from "path";
9152
9384
  var MAX_RESPONSE_LENGTH = 2e3;
9153
9385
  var DEFAULT_STATE = {
9154
9386
  sessionId: null,
@@ -9156,15 +9388,15 @@ var DEFAULT_STATE = {
9156
9388
  lastAnalysisTimestamp: null
9157
9389
  };
9158
9390
  function ensureLearningDir() {
9159
- if (!fs31.existsSync(LEARNING_DIR)) {
9160
- fs31.mkdirSync(LEARNING_DIR, { recursive: true });
9391
+ if (!fs32.existsSync(LEARNING_DIR)) {
9392
+ fs32.mkdirSync(LEARNING_DIR, { recursive: true });
9161
9393
  }
9162
9394
  }
9163
9395
  function sessionFilePath() {
9164
- return path25.join(LEARNING_DIR, LEARNING_SESSION_FILE);
9396
+ return path26.join(LEARNING_DIR, LEARNING_SESSION_FILE);
9165
9397
  }
9166
9398
  function stateFilePath() {
9167
- return path25.join(LEARNING_DIR, LEARNING_STATE_FILE);
9399
+ return path26.join(LEARNING_DIR, LEARNING_STATE_FILE);
9168
9400
  }
9169
9401
  function truncateResponse(response) {
9170
9402
  const str = JSON.stringify(response);
@@ -9175,29 +9407,29 @@ function appendEvent(event) {
9175
9407
  ensureLearningDir();
9176
9408
  const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
9177
9409
  const filePath = sessionFilePath();
9178
- fs31.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
9410
+ fs32.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
9179
9411
  const count = getEventCount();
9180
9412
  if (count > LEARNING_MAX_EVENTS) {
9181
- const lines = fs31.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9413
+ const lines = fs32.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9182
9414
  const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
9183
- fs31.writeFileSync(filePath, kept.join("\n") + "\n");
9415
+ fs32.writeFileSync(filePath, kept.join("\n") + "\n");
9184
9416
  }
9185
9417
  }
9186
9418
  function appendPromptEvent(event) {
9187
9419
  ensureLearningDir();
9188
9420
  const filePath = sessionFilePath();
9189
- fs31.appendFileSync(filePath, JSON.stringify(event) + "\n");
9421
+ fs32.appendFileSync(filePath, JSON.stringify(event) + "\n");
9190
9422
  const count = getEventCount();
9191
9423
  if (count > LEARNING_MAX_EVENTS) {
9192
- const lines = fs31.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9424
+ const lines = fs32.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9193
9425
  const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
9194
- fs31.writeFileSync(filePath, kept.join("\n") + "\n");
9426
+ fs32.writeFileSync(filePath, kept.join("\n") + "\n");
9195
9427
  }
9196
9428
  }
9197
9429
  function readAllEvents() {
9198
9430
  const filePath = sessionFilePath();
9199
- if (!fs31.existsSync(filePath)) return [];
9200
- const lines = fs31.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9431
+ if (!fs32.existsSync(filePath)) return [];
9432
+ const lines = fs32.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9201
9433
  const events = [];
9202
9434
  for (const line of lines) {
9203
9435
  try {
@@ -9209,26 +9441,26 @@ function readAllEvents() {
9209
9441
  }
9210
9442
  function getEventCount() {
9211
9443
  const filePath = sessionFilePath();
9212
- if (!fs31.existsSync(filePath)) return 0;
9213
- const content = fs31.readFileSync(filePath, "utf-8");
9444
+ if (!fs32.existsSync(filePath)) return 0;
9445
+ const content = fs32.readFileSync(filePath, "utf-8");
9214
9446
  return content.split("\n").filter(Boolean).length;
9215
9447
  }
9216
9448
  function clearSession() {
9217
9449
  const filePath = sessionFilePath();
9218
- if (fs31.existsSync(filePath)) fs31.unlinkSync(filePath);
9450
+ if (fs32.existsSync(filePath)) fs32.unlinkSync(filePath);
9219
9451
  }
9220
9452
  function readState2() {
9221
9453
  const filePath = stateFilePath();
9222
- if (!fs31.existsSync(filePath)) return { ...DEFAULT_STATE };
9454
+ if (!fs32.existsSync(filePath)) return { ...DEFAULT_STATE };
9223
9455
  try {
9224
- return JSON.parse(fs31.readFileSync(filePath, "utf-8"));
9456
+ return JSON.parse(fs32.readFileSync(filePath, "utf-8"));
9225
9457
  } catch {
9226
9458
  return { ...DEFAULT_STATE };
9227
9459
  }
9228
9460
  }
9229
9461
  function writeState2(state) {
9230
9462
  ensureLearningDir();
9231
- fs31.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
9463
+ fs32.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
9232
9464
  }
9233
9465
  function resetState() {
9234
9466
  writeState2({ ...DEFAULT_STATE });
@@ -9236,14 +9468,14 @@ function resetState() {
9236
9468
  var LOCK_FILE2 = "finalize.lock";
9237
9469
  var LOCK_STALE_MS = 5 * 60 * 1e3;
9238
9470
  function lockFilePath() {
9239
- return path25.join(LEARNING_DIR, LOCK_FILE2);
9471
+ return path26.join(LEARNING_DIR, LOCK_FILE2);
9240
9472
  }
9241
9473
  function acquireFinalizeLock() {
9242
9474
  ensureLearningDir();
9243
9475
  const lockPath = lockFilePath();
9244
- if (fs31.existsSync(lockPath)) {
9476
+ if (fs32.existsSync(lockPath)) {
9245
9477
  try {
9246
- const stat = fs31.statSync(lockPath);
9478
+ const stat = fs32.statSync(lockPath);
9247
9479
  if (Date.now() - stat.mtimeMs < LOCK_STALE_MS) {
9248
9480
  return false;
9249
9481
  }
@@ -9251,7 +9483,7 @@ function acquireFinalizeLock() {
9251
9483
  }
9252
9484
  }
9253
9485
  try {
9254
- fs31.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
9486
+ fs32.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
9255
9487
  return true;
9256
9488
  } catch {
9257
9489
  return false;
@@ -9260,7 +9492,7 @@ function acquireFinalizeLock() {
9260
9492
  function releaseFinalizeLock() {
9261
9493
  const lockPath = lockFilePath();
9262
9494
  try {
9263
- if (fs31.existsSync(lockPath)) fs31.unlinkSync(lockPath);
9495
+ if (fs32.existsSync(lockPath)) fs32.unlinkSync(lockPath);
9264
9496
  } catch {
9265
9497
  }
9266
9498
  }
@@ -9306,7 +9538,7 @@ function sanitizeSecrets(text) {
9306
9538
 
9307
9539
  // src/ai/learn.ts
9308
9540
  init_config();
9309
- var MAX_PROMPT_TOKENS2 = 1e5;
9541
+ var MAX_PROMPT_TOKENS = 1e5;
9310
9542
  function formatEventsForPrompt(events) {
9311
9543
  return events.map((e, i) => {
9312
9544
  if (e.hook_event_name === "UserPromptSubmit") {
@@ -9354,7 +9586,7 @@ function parseAnalysisResponse(raw) {
9354
9586
  }
9355
9587
  }
9356
9588
  async function analyzeEvents(events, existingClaudeMd, existingLearnedSection, existingSkills) {
9357
- const fittedEvents = trimEventsToFit(events, MAX_PROMPT_TOKENS2 - 1e4);
9589
+ const fittedEvents = trimEventsToFit(events, MAX_PROMPT_TOKENS - 1e4);
9358
9590
  const eventsText = formatEventsForPrompt(fittedEvents);
9359
9591
  const contextParts = [];
9360
9592
  if (existingClaudeMd) {
@@ -9416,8 +9648,8 @@ init_config();
9416
9648
 
9417
9649
  // src/learner/roi.ts
9418
9650
  init_constants();
9419
- import fs32 from "fs";
9420
- import path26 from "path";
9651
+ import fs33 from "fs";
9652
+ import path27 from "path";
9421
9653
  var DEFAULT_TOTALS = {
9422
9654
  totalWasteTokens: 0,
9423
9655
  totalWasteSeconds: 0,
@@ -9431,22 +9663,22 @@ var DEFAULT_TOTALS = {
9431
9663
  lastSessionTimestamp: ""
9432
9664
  };
9433
9665
  function roiFilePath() {
9434
- return path26.join(LEARNING_DIR, LEARNING_ROI_FILE);
9666
+ return path27.join(LEARNING_DIR, LEARNING_ROI_FILE);
9435
9667
  }
9436
9668
  function readROIStats() {
9437
9669
  const filePath = roiFilePath();
9438
- if (!fs32.existsSync(filePath)) {
9670
+ if (!fs33.existsSync(filePath)) {
9439
9671
  return { learnings: [], sessions: [], totals: { ...DEFAULT_TOTALS } };
9440
9672
  }
9441
9673
  try {
9442
- return JSON.parse(fs32.readFileSync(filePath, "utf-8"));
9674
+ return JSON.parse(fs33.readFileSync(filePath, "utf-8"));
9443
9675
  } catch {
9444
9676
  return { learnings: [], sessions: [], totals: { ...DEFAULT_TOTALS } };
9445
9677
  }
9446
9678
  }
9447
9679
  function writeROIStats(stats) {
9448
9680
  ensureLearningDir();
9449
- fs32.writeFileSync(roiFilePath(), JSON.stringify(stats, null, 2));
9681
+ fs33.writeFileSync(roiFilePath(), JSON.stringify(stats, null, 2));
9450
9682
  }
9451
9683
  function recalculateTotals(stats) {
9452
9684
  const totals = stats.totals;
@@ -9699,7 +9931,7 @@ async function learnFinalizeCommand(options) {
9699
9931
  }
9700
9932
  async function learnInstallCommand() {
9701
9933
  let anyInstalled = false;
9702
- if (fs33.existsSync(".claude")) {
9934
+ if (fs34.existsSync(".claude")) {
9703
9935
  const r = installLearningHooks();
9704
9936
  if (r.installed) {
9705
9937
  console.log(chalk19.green("\u2713") + " Claude Code learning hooks installed");
@@ -9708,7 +9940,7 @@ async function learnInstallCommand() {
9708
9940
  console.log(chalk19.dim(" Claude Code hooks already installed"));
9709
9941
  }
9710
9942
  }
9711
- if (fs33.existsSync(".cursor")) {
9943
+ if (fs34.existsSync(".cursor")) {
9712
9944
  const r = installCursorLearningHooks();
9713
9945
  if (r.installed) {
9714
9946
  console.log(chalk19.green("\u2713") + " Cursor learning hooks installed");
@@ -9717,7 +9949,7 @@ async function learnInstallCommand() {
9717
9949
  console.log(chalk19.dim(" Cursor hooks already installed"));
9718
9950
  }
9719
9951
  }
9720
- if (!fs33.existsSync(".claude") && !fs33.existsSync(".cursor")) {
9952
+ if (!fs34.existsSync(".claude") && !fs34.existsSync(".cursor")) {
9721
9953
  console.log(chalk19.yellow("No .claude/ or .cursor/ directory found."));
9722
9954
  console.log(chalk19.dim(" Run `caliber init` first, or create the directory manually."));
9723
9955
  return;
@@ -9789,9 +10021,9 @@ Learned items in CALIBER_LEARNINGS.md: ${chalk19.cyan(String(lineCount))}`);
9789
10021
  }
9790
10022
 
9791
10023
  // src/cli.ts
9792
- var __dirname = path27.dirname(fileURLToPath(import.meta.url));
10024
+ var __dirname = path28.dirname(fileURLToPath(import.meta.url));
9793
10025
  var pkg = JSON.parse(
9794
- fs34.readFileSync(path27.resolve(__dirname, "..", "package.json"), "utf-8")
10026
+ fs35.readFileSync(path28.resolve(__dirname, "..", "package.json"), "utf-8")
9795
10027
  );
9796
10028
  var program = new Command();
9797
10029
  var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
@@ -9865,16 +10097,16 @@ learn.command("remove").description("Remove learning hooks from .claude/settings
9865
10097
  learn.command("status").description("Show learning system status").action(tracked("learn:status", learnStatusCommand));
9866
10098
 
9867
10099
  // src/utils/version-check.ts
9868
- import fs35 from "fs";
9869
- import path28 from "path";
10100
+ import fs36 from "fs";
10101
+ import path29 from "path";
9870
10102
  import { fileURLToPath as fileURLToPath2 } from "url";
9871
- import { execSync as execSync14 } from "child_process";
10103
+ import { execSync as execSync15 } from "child_process";
9872
10104
  import chalk20 from "chalk";
9873
10105
  import ora7 from "ora";
9874
10106
  import confirm2 from "@inquirer/confirm";
9875
- var __dirname_vc = path28.dirname(fileURLToPath2(import.meta.url));
10107
+ var __dirname_vc = path29.dirname(fileURLToPath2(import.meta.url));
9876
10108
  var pkg2 = JSON.parse(
9877
- fs35.readFileSync(path28.resolve(__dirname_vc, "..", "package.json"), "utf-8")
10109
+ fs36.readFileSync(path29.resolve(__dirname_vc, "..", "package.json"), "utf-8")
9878
10110
  );
9879
10111
  function getChannel(version) {
9880
10112
  const match = version.match(/-(dev|next)\./);
@@ -9898,9 +10130,9 @@ function isNewer(registry, current) {
9898
10130
  }
9899
10131
  function getInstalledVersion() {
9900
10132
  try {
9901
- const globalRoot = execSync14("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
9902
- const pkgPath = path28.join(globalRoot, "@rely-ai", "caliber", "package.json");
9903
- return JSON.parse(fs35.readFileSync(pkgPath, "utf-8")).version;
10133
+ const globalRoot = execSync15("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
10134
+ const pkgPath = path29.join(globalRoot, "@rely-ai", "caliber", "package.json");
10135
+ return JSON.parse(fs36.readFileSync(pkgPath, "utf-8")).version;
9904
10136
  } catch {
9905
10137
  return null;
9906
10138
  }
@@ -9946,7 +10178,7 @@ Update available: ${current} -> ${latest}`)
9946
10178
  const tag = channel === "latest" ? latest : channel;
9947
10179
  const spinner = ora7("Updating caliber...").start();
9948
10180
  try {
9949
- execSync14(`npm install -g @rely-ai/caliber@${tag}`, {
10181
+ execSync15(`npm install -g @rely-ai/caliber@${tag}`, {
9950
10182
  stdio: "pipe",
9951
10183
  timeout: 12e4,
9952
10184
  env: { ...process.env, npm_config_fund: "false", npm_config_audit: "false" }
@@ -9963,7 +10195,7 @@ Update available: ${current} -> ${latest}`)
9963
10195
  console.log(chalk20.dim(`
9964
10196
  Restarting: caliber ${args.join(" ")}
9965
10197
  `));
9966
- execSync14(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
10198
+ execSync15(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
9967
10199
  stdio: "inherit",
9968
10200
  env: { ...process.env, CALIBER_SKIP_UPDATE_CHECK: "1" }
9969
10201
  });