@caliber-ai/cli 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -92,7 +92,7 @@ import chalk3 from "chalk";
92
92
  import ora2 from "ora";
93
93
  import readline from "readline";
94
94
  import select from "@inquirer/select";
95
- import fs17 from "fs";
95
+ import fs18 from "fs";
96
96
 
97
97
  // src/auth/token-store.ts
98
98
  init_constants();
@@ -1518,25 +1518,112 @@ function removeHook() {
1518
1518
  return { removed: true, notFound: false };
1519
1519
  }
1520
1520
 
1521
- // src/lib/state.ts
1522
- init_constants();
1521
+ // src/lib/learning-hooks.ts
1523
1522
  import fs16 from "fs";
1524
1523
  import path15 from "path";
1524
+ var SETTINGS_PATH2 = path15.join(".claude", "settings.json");
1525
+ var HOOK_CONFIGS = [
1526
+ {
1527
+ event: "PostToolUse",
1528
+ command: "caliber learn observe",
1529
+ description: "Caliber: recording tool usage for session learning"
1530
+ },
1531
+ {
1532
+ event: "PostToolUseFailure",
1533
+ command: "caliber learn observe --failure",
1534
+ description: "Caliber: recording tool failure for session learning"
1535
+ },
1536
+ {
1537
+ event: "SessionEnd",
1538
+ command: "caliber learn finalize",
1539
+ description: "Caliber: finalizing session learnings"
1540
+ }
1541
+ ];
1542
+ function readSettings2() {
1543
+ if (!fs16.existsSync(SETTINGS_PATH2)) return {};
1544
+ try {
1545
+ return JSON.parse(fs16.readFileSync(SETTINGS_PATH2, "utf-8"));
1546
+ } catch {
1547
+ return {};
1548
+ }
1549
+ }
1550
+ function writeSettings2(settings) {
1551
+ const dir = path15.dirname(SETTINGS_PATH2);
1552
+ if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
1553
+ fs16.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
1554
+ }
1555
+ function hasLearningHook(matchers, command) {
1556
+ return matchers.some((entry) => entry.hooks?.some((h) => h.command === command));
1557
+ }
1558
+ function areLearningHooksInstalled() {
1559
+ const settings = readSettings2();
1560
+ if (!settings.hooks) return false;
1561
+ return HOOK_CONFIGS.every((cfg) => {
1562
+ const matchers = settings.hooks[cfg.event];
1563
+ return Array.isArray(matchers) && hasLearningHook(matchers, cfg.command);
1564
+ });
1565
+ }
1566
+ function installLearningHooks() {
1567
+ if (areLearningHooksInstalled()) {
1568
+ return { installed: false, alreadyInstalled: true };
1569
+ }
1570
+ const settings = readSettings2();
1571
+ if (!settings.hooks) settings.hooks = {};
1572
+ for (const cfg of HOOK_CONFIGS) {
1573
+ if (!Array.isArray(settings.hooks[cfg.event])) {
1574
+ settings.hooks[cfg.event] = [];
1575
+ }
1576
+ if (!hasLearningHook(settings.hooks[cfg.event], cfg.command)) {
1577
+ settings.hooks[cfg.event].push({
1578
+ matcher: "",
1579
+ hooks: [{ type: "command", command: cfg.command, description: cfg.description }]
1580
+ });
1581
+ }
1582
+ }
1583
+ writeSettings2(settings);
1584
+ return { installed: true, alreadyInstalled: false };
1585
+ }
1586
+ function removeLearningHooks() {
1587
+ const settings = readSettings2();
1588
+ if (!settings.hooks) return { removed: false, notFound: true };
1589
+ let removedAny = false;
1590
+ for (const cfg of HOOK_CONFIGS) {
1591
+ const matchers = settings.hooks[cfg.event];
1592
+ if (!Array.isArray(matchers)) continue;
1593
+ const idx = matchers.findIndex((entry) => entry.hooks?.some((h) => h.command === cfg.command));
1594
+ if (idx !== -1) {
1595
+ matchers.splice(idx, 1);
1596
+ removedAny = true;
1597
+ if (matchers.length === 0) delete settings.hooks[cfg.event];
1598
+ }
1599
+ }
1600
+ if (settings.hooks && Object.keys(settings.hooks).length === 0) {
1601
+ delete settings.hooks;
1602
+ }
1603
+ if (!removedAny) return { removed: false, notFound: true };
1604
+ writeSettings2(settings);
1605
+ return { removed: true, notFound: false };
1606
+ }
1607
+
1608
+ // src/lib/state.ts
1609
+ init_constants();
1610
+ import fs17 from "fs";
1611
+ import path16 from "path";
1525
1612
  import { execSync as execSync3 } from "child_process";
1526
- var STATE_FILE = path15.join(CALIBER_DIR, ".caliber-state.json");
1613
+ var STATE_FILE = path16.join(CALIBER_DIR, ".caliber-state.json");
1527
1614
  function readState() {
1528
1615
  try {
1529
- if (!fs16.existsSync(STATE_FILE)) return null;
1530
- return JSON.parse(fs16.readFileSync(STATE_FILE, "utf-8"));
1616
+ if (!fs17.existsSync(STATE_FILE)) return null;
1617
+ return JSON.parse(fs17.readFileSync(STATE_FILE, "utf-8"));
1531
1618
  } catch {
1532
1619
  return null;
1533
1620
  }
1534
1621
  }
1535
1622
  function writeState(state) {
1536
- if (!fs16.existsSync(CALIBER_DIR)) {
1537
- fs16.mkdirSync(CALIBER_DIR, { recursive: true });
1623
+ if (!fs17.existsSync(CALIBER_DIR)) {
1624
+ fs17.mkdirSync(CALIBER_DIR, { recursive: true });
1538
1625
  }
1539
- fs16.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
1626
+ fs17.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
1540
1627
  }
1541
1628
  function getCurrentHeadSha() {
1542
1629
  try {
@@ -1893,6 +1980,13 @@ async function initCommand(options) {
1893
1980
  } else if (hookResult.alreadyInstalled) {
1894
1981
  console.log(chalk3.dim(" Auto-refresh hook already installed"));
1895
1982
  }
1983
+ const learnResult = installLearningHooks();
1984
+ if (learnResult.installed) {
1985
+ console.log(` ${chalk3.green("\u2713")} Learning hooks installed \u2014 session insights captured automatically`);
1986
+ console.log(chalk3.dim(" Run `caliber learn remove` to disable"));
1987
+ } else if (learnResult.alreadyInstalled) {
1988
+ console.log(chalk3.dim(" Learning hooks already installed"));
1989
+ }
1896
1990
  }
1897
1991
  try {
1898
1992
  let projectId = existingProjectId;
@@ -2024,8 +2118,8 @@ function openReview(method, stagedFiles) {
2024
2118
  } else {
2025
2119
  for (const file of stagedFiles) {
2026
2120
  if (file.currentPath) {
2027
- const currentLines = fs17.readFileSync(file.currentPath, "utf-8").split("\n");
2028
- const proposedLines = fs17.readFileSync(file.proposedPath, "utf-8").split("\n");
2121
+ const currentLines = fs18.readFileSync(file.currentPath, "utf-8").split("\n");
2122
+ const proposedLines = fs18.readFileSync(file.proposedPath, "utf-8").split("\n");
2029
2123
  const patch = createTwoFilesPatch(file.relativePath, file.relativePath, currentLines.join("\n"), proposedLines.join("\n"));
2030
2124
  let added = 0, removed = 0;
2031
2125
  for (const line of patch.split("\n")) {
@@ -2034,7 +2128,7 @@ function openReview(method, stagedFiles) {
2034
2128
  }
2035
2129
  console.log(` ${chalk3.yellow("~")} ${file.relativePath} ${chalk3.green(`+${added}`)} ${chalk3.red(`-${removed}`)}`);
2036
2130
  } else {
2037
- const lines = fs17.readFileSync(file.proposedPath, "utf-8").split("\n").length;
2131
+ const lines = fs18.readFileSync(file.proposedPath, "utf-8").split("\n").length;
2038
2132
  console.log(` ${chalk3.green("+")} ${file.relativePath} ${chalk3.dim(`${lines} lines`)}`);
2039
2133
  }
2040
2134
  }
@@ -2065,7 +2159,7 @@ function printSetupSummary(setup) {
2065
2159
  };
2066
2160
  if (claude) {
2067
2161
  if (claude.claudeMd) {
2068
- const icon = fs17.existsSync("CLAUDE.md") ? chalk3.yellow("~") : chalk3.green("+");
2162
+ const icon = fs18.existsSync("CLAUDE.md") ? chalk3.yellow("~") : chalk3.green("+");
2069
2163
  const desc = getDescription("CLAUDE.md");
2070
2164
  console.log(` ${icon} ${chalk3.bold("CLAUDE.md")}`);
2071
2165
  if (desc) {
@@ -2077,7 +2171,7 @@ function printSetupSummary(setup) {
2077
2171
  if (Array.isArray(skills) && skills.length > 0) {
2078
2172
  for (const skill of skills) {
2079
2173
  const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
2080
- const icon = fs17.existsSync(skillPath) ? chalk3.yellow("~") : chalk3.green("+");
2174
+ const icon = fs18.existsSync(skillPath) ? chalk3.yellow("~") : chalk3.green("+");
2081
2175
  const desc = getDescription(skillPath);
2082
2176
  console.log(` ${icon} ${chalk3.bold(skillPath)}`);
2083
2177
  console.log(chalk3.dim(` ${desc || skill.description || skill.name}`));
@@ -2087,7 +2181,7 @@ function printSetupSummary(setup) {
2087
2181
  }
2088
2182
  if (cursor) {
2089
2183
  if (cursor.cursorrules) {
2090
- const icon = fs17.existsSync(".cursorrules") ? chalk3.yellow("~") : chalk3.green("+");
2184
+ const icon = fs18.existsSync(".cursorrules") ? chalk3.yellow("~") : chalk3.green("+");
2091
2185
  const desc = getDescription(".cursorrules");
2092
2186
  console.log(` ${icon} ${chalk3.bold(".cursorrules")}`);
2093
2187
  if (desc) console.log(chalk3.dim(` ${desc}`));
@@ -2097,7 +2191,7 @@ function printSetupSummary(setup) {
2097
2191
  if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
2098
2192
  for (const skill of cursorSkills) {
2099
2193
  const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
2100
- const icon = fs17.existsSync(skillPath) ? chalk3.yellow("~") : chalk3.green("+");
2194
+ const icon = fs18.existsSync(skillPath) ? chalk3.yellow("~") : chalk3.green("+");
2101
2195
  const desc = getDescription(skillPath);
2102
2196
  console.log(` ${icon} ${chalk3.bold(skillPath)}`);
2103
2197
  console.log(chalk3.dim(` ${desc || skill.description || skill.name}`));
@@ -2108,7 +2202,7 @@ function printSetupSummary(setup) {
2108
2202
  if (Array.isArray(rules) && rules.length > 0) {
2109
2203
  for (const rule of rules) {
2110
2204
  const rulePath = `.cursor/rules/${rule.filename}`;
2111
- const icon = fs17.existsSync(rulePath) ? chalk3.yellow("~") : chalk3.green("+");
2205
+ const icon = fs18.existsSync(rulePath) ? chalk3.yellow("~") : chalk3.green("+");
2112
2206
  const desc = getDescription(rulePath);
2113
2207
  console.log(` ${icon} ${chalk3.bold(rulePath)}`);
2114
2208
  if (desc) {
@@ -2205,16 +2299,16 @@ function undoCommand() {
2205
2299
 
2206
2300
  // src/commands/status.ts
2207
2301
  import chalk5 from "chalk";
2208
- import fs19 from "fs";
2302
+ import fs20 from "fs";
2209
2303
 
2210
2304
  // src/scanner/index.ts
2211
- import fs18 from "fs";
2212
- import path16 from "path";
2305
+ import fs19 from "fs";
2306
+ import path17 from "path";
2213
2307
  import crypto4 from "crypto";
2214
2308
  function scanLocalState(dir) {
2215
2309
  const items = [];
2216
- const claudeMdPath = path16.join(dir, "CLAUDE.md");
2217
- if (fs18.existsSync(claudeMdPath)) {
2310
+ const claudeMdPath = path17.join(dir, "CLAUDE.md");
2311
+ if (fs19.existsSync(claudeMdPath)) {
2218
2312
  items.push({
2219
2313
  type: "rule",
2220
2314
  platform: "claude",
@@ -2223,10 +2317,10 @@ function scanLocalState(dir) {
2223
2317
  path: claudeMdPath
2224
2318
  });
2225
2319
  }
2226
- const skillsDir = path16.join(dir, ".claude", "skills");
2227
- if (fs18.existsSync(skillsDir)) {
2228
- for (const file of fs18.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
2229
- const filePath = path16.join(skillsDir, file);
2320
+ const skillsDir = path17.join(dir, ".claude", "skills");
2321
+ if (fs19.existsSync(skillsDir)) {
2322
+ for (const file of fs19.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
2323
+ const filePath = path17.join(skillsDir, file);
2230
2324
  items.push({
2231
2325
  type: "skill",
2232
2326
  platform: "claude",
@@ -2236,10 +2330,10 @@ function scanLocalState(dir) {
2236
2330
  });
2237
2331
  }
2238
2332
  }
2239
- const mcpJsonPath = path16.join(dir, ".mcp.json");
2240
- if (fs18.existsSync(mcpJsonPath)) {
2333
+ const mcpJsonPath = path17.join(dir, ".mcp.json");
2334
+ if (fs19.existsSync(mcpJsonPath)) {
2241
2335
  try {
2242
- const mcpJson = JSON.parse(fs18.readFileSync(mcpJsonPath, "utf-8"));
2336
+ const mcpJson = JSON.parse(fs19.readFileSync(mcpJsonPath, "utf-8"));
2243
2337
  if (mcpJson.mcpServers) {
2244
2338
  for (const name of Object.keys(mcpJson.mcpServers)) {
2245
2339
  items.push({
@@ -2254,8 +2348,8 @@ function scanLocalState(dir) {
2254
2348
  } catch {
2255
2349
  }
2256
2350
  }
2257
- const cursorrulesPath = path16.join(dir, ".cursorrules");
2258
- if (fs18.existsSync(cursorrulesPath)) {
2351
+ const cursorrulesPath = path17.join(dir, ".cursorrules");
2352
+ if (fs19.existsSync(cursorrulesPath)) {
2259
2353
  items.push({
2260
2354
  type: "rule",
2261
2355
  platform: "cursor",
@@ -2264,10 +2358,10 @@ function scanLocalState(dir) {
2264
2358
  path: cursorrulesPath
2265
2359
  });
2266
2360
  }
2267
- const cursorRulesDir = path16.join(dir, ".cursor", "rules");
2268
- if (fs18.existsSync(cursorRulesDir)) {
2269
- for (const file of fs18.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
2270
- const filePath = path16.join(cursorRulesDir, file);
2361
+ const cursorRulesDir = path17.join(dir, ".cursor", "rules");
2362
+ if (fs19.existsSync(cursorRulesDir)) {
2363
+ for (const file of fs19.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
2364
+ const filePath = path17.join(cursorRulesDir, file);
2271
2365
  items.push({
2272
2366
  type: "rule",
2273
2367
  platform: "cursor",
@@ -2277,12 +2371,12 @@ function scanLocalState(dir) {
2277
2371
  });
2278
2372
  }
2279
2373
  }
2280
- const cursorSkillsDir = path16.join(dir, ".cursor", "skills");
2281
- if (fs18.existsSync(cursorSkillsDir)) {
2374
+ const cursorSkillsDir = path17.join(dir, ".cursor", "skills");
2375
+ if (fs19.existsSync(cursorSkillsDir)) {
2282
2376
  try {
2283
- for (const name of fs18.readdirSync(cursorSkillsDir)) {
2284
- const skillFile = path16.join(cursorSkillsDir, name, "SKILL.md");
2285
- if (fs18.existsSync(skillFile)) {
2377
+ for (const name of fs19.readdirSync(cursorSkillsDir)) {
2378
+ const skillFile = path17.join(cursorSkillsDir, name, "SKILL.md");
2379
+ if (fs19.existsSync(skillFile)) {
2286
2380
  items.push({
2287
2381
  type: "skill",
2288
2382
  platform: "cursor",
@@ -2295,10 +2389,10 @@ function scanLocalState(dir) {
2295
2389
  } catch {
2296
2390
  }
2297
2391
  }
2298
- const cursorMcpPath = path16.join(dir, ".cursor", "mcp.json");
2299
- if (fs18.existsSync(cursorMcpPath)) {
2392
+ const cursorMcpPath = path17.join(dir, ".cursor", "mcp.json");
2393
+ if (fs19.existsSync(cursorMcpPath)) {
2300
2394
  try {
2301
- const mcpJson = JSON.parse(fs18.readFileSync(cursorMcpPath, "utf-8"));
2395
+ const mcpJson = JSON.parse(fs19.readFileSync(cursorMcpPath, "utf-8"));
2302
2396
  if (mcpJson.mcpServers) {
2303
2397
  for (const name of Object.keys(mcpJson.mcpServers)) {
2304
2398
  items.push({
@@ -2342,7 +2436,7 @@ function compareState(serverItems, localItems) {
2342
2436
  return { installed, missing, outdated, extra };
2343
2437
  }
2344
2438
  function hashFile(filePath) {
2345
- const text = fs18.readFileSync(filePath, "utf-8");
2439
+ const text = fs19.readFileSync(filePath, "utf-8");
2346
2440
  return crypto4.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
2347
2441
  }
2348
2442
  function hashJson(obj) {
@@ -2374,7 +2468,7 @@ async function statusCommand(options) {
2374
2468
  }
2375
2469
  console.log(` Files managed: ${chalk5.cyan(manifest.entries.length.toString())}`);
2376
2470
  for (const entry of manifest.entries) {
2377
- const exists = fs19.existsSync(entry.path);
2471
+ const exists = fs20.existsSync(entry.path);
2378
2472
  const icon = exists ? chalk5.green("\u2713") : chalk5.red("\u2717");
2379
2473
  console.log(` ${icon} ${entry.path} (${entry.action})`);
2380
2474
  }
@@ -3009,8 +3103,8 @@ async function diffCommand(options) {
3009
3103
  }
3010
3104
 
3011
3105
  // src/commands/refresh.ts
3012
- import fs21 from "fs";
3013
- import path18 from "path";
3106
+ import fs22 from "fs";
3107
+ import path19 from "path";
3014
3108
  import chalk11 from "chalk";
3015
3109
  import ora8 from "ora";
3016
3110
 
@@ -3087,37 +3181,37 @@ function collectDiff(lastSha) {
3087
3181
  }
3088
3182
 
3089
3183
  // src/writers/refresh.ts
3090
- import fs20 from "fs";
3091
- import path17 from "path";
3184
+ import fs21 from "fs";
3185
+ import path18 from "path";
3092
3186
  function writeRefreshDocs(docs) {
3093
3187
  const written = [];
3094
3188
  if (docs.claudeMd) {
3095
- fs20.writeFileSync("CLAUDE.md", docs.claudeMd);
3189
+ fs21.writeFileSync("CLAUDE.md", docs.claudeMd);
3096
3190
  written.push("CLAUDE.md");
3097
3191
  }
3098
3192
  if (docs.readmeMd) {
3099
- fs20.writeFileSync("README.md", docs.readmeMd);
3193
+ fs21.writeFileSync("README.md", docs.readmeMd);
3100
3194
  written.push("README.md");
3101
3195
  }
3102
3196
  if (docs.cursorrules) {
3103
- fs20.writeFileSync(".cursorrules", docs.cursorrules);
3197
+ fs21.writeFileSync(".cursorrules", docs.cursorrules);
3104
3198
  written.push(".cursorrules");
3105
3199
  }
3106
3200
  if (docs.cursorRules) {
3107
- const rulesDir = path17.join(".cursor", "rules");
3108
- if (!fs20.existsSync(rulesDir)) fs20.mkdirSync(rulesDir, { recursive: true });
3201
+ const rulesDir = path18.join(".cursor", "rules");
3202
+ if (!fs21.existsSync(rulesDir)) fs21.mkdirSync(rulesDir, { recursive: true });
3109
3203
  for (const rule of docs.cursorRules) {
3110
- const filePath = path17.join(rulesDir, rule.filename);
3111
- fs20.writeFileSync(filePath, rule.content);
3204
+ const filePath = path18.join(rulesDir, rule.filename);
3205
+ fs21.writeFileSync(filePath, rule.content);
3112
3206
  written.push(filePath);
3113
3207
  }
3114
3208
  }
3115
3209
  if (docs.claudeSkills) {
3116
- const skillsDir = path17.join(".claude", "skills");
3117
- if (!fs20.existsSync(skillsDir)) fs20.mkdirSync(skillsDir, { recursive: true });
3210
+ const skillsDir = path18.join(".claude", "skills");
3211
+ if (!fs21.existsSync(skillsDir)) fs21.mkdirSync(skillsDir, { recursive: true });
3118
3212
  for (const skill of docs.claudeSkills) {
3119
- const filePath = path17.join(skillsDir, skill.filename);
3120
- fs20.writeFileSync(filePath, skill.content);
3213
+ const filePath = path18.join(skillsDir, skill.filename);
3214
+ fs21.writeFileSync(filePath, skill.content);
3121
3215
  written.push(filePath);
3122
3216
  }
3123
3217
  }
@@ -3131,11 +3225,11 @@ function log(quiet, ...args) {
3131
3225
  function discoverGitRepos(parentDir) {
3132
3226
  const repos = [];
3133
3227
  try {
3134
- const entries = fs21.readdirSync(parentDir, { withFileTypes: true });
3228
+ const entries = fs22.readdirSync(parentDir, { withFileTypes: true });
3135
3229
  for (const entry of entries) {
3136
3230
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
3137
- const childPath = path18.join(parentDir, entry.name);
3138
- if (fs21.existsSync(path18.join(childPath, ".git"))) {
3231
+ const childPath = path19.join(parentDir, entry.name);
3232
+ if (fs22.existsSync(path19.join(childPath, ".git"))) {
3139
3233
  repos.push(childPath);
3140
3234
  }
3141
3235
  }
@@ -3238,7 +3332,7 @@ async function refreshCommand(options) {
3238
3332
  `));
3239
3333
  const originalDir = process.cwd();
3240
3334
  for (const repo of repos) {
3241
- const repoName = path18.basename(repo);
3335
+ const repoName = path19.basename(repo);
3242
3336
  try {
3243
3337
  process.chdir(repo);
3244
3338
  await refreshSingleRepo(repo, { ...options, label: repoName });
@@ -3411,8 +3505,8 @@ function readStdin() {
3411
3505
 
3412
3506
  // src/learner/storage.ts
3413
3507
  init_constants();
3414
- import fs22 from "fs";
3415
- import path19 from "path";
3508
+ import fs23 from "fs";
3509
+ import path20 from "path";
3416
3510
  var MAX_RESPONSE_LENGTH = 2e3;
3417
3511
  var DEFAULT_STATE = {
3418
3512
  sessionId: null,
@@ -3420,15 +3514,15 @@ var DEFAULT_STATE = {
3420
3514
  lastAnalysisTimestamp: null
3421
3515
  };
3422
3516
  function ensureLearningDir() {
3423
- if (!fs22.existsSync(LEARNING_DIR)) {
3424
- fs22.mkdirSync(LEARNING_DIR, { recursive: true });
3517
+ if (!fs23.existsSync(LEARNING_DIR)) {
3518
+ fs23.mkdirSync(LEARNING_DIR, { recursive: true });
3425
3519
  }
3426
3520
  }
3427
3521
  function sessionFilePath() {
3428
- return path19.join(LEARNING_DIR, LEARNING_SESSION_FILE);
3522
+ return path20.join(LEARNING_DIR, LEARNING_SESSION_FILE);
3429
3523
  }
3430
3524
  function stateFilePath() {
3431
- return path19.join(LEARNING_DIR, LEARNING_STATE_FILE);
3525
+ return path20.join(LEARNING_DIR, LEARNING_STATE_FILE);
3432
3526
  }
3433
3527
  function truncateResponse(response) {
3434
3528
  const str = JSON.stringify(response);
@@ -3439,50 +3533,50 @@ function appendEvent(event) {
3439
3533
  ensureLearningDir();
3440
3534
  const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
3441
3535
  const filePath = sessionFilePath();
3442
- fs22.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
3536
+ fs23.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
3443
3537
  const count = getEventCount();
3444
3538
  if (count > LEARNING_MAX_EVENTS) {
3445
- const lines = fs22.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
3539
+ const lines = fs23.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
3446
3540
  const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
3447
- fs22.writeFileSync(filePath, kept.join("\n") + "\n");
3541
+ fs23.writeFileSync(filePath, kept.join("\n") + "\n");
3448
3542
  }
3449
3543
  }
3450
3544
  function readAllEvents() {
3451
3545
  const filePath = sessionFilePath();
3452
- if (!fs22.existsSync(filePath)) return [];
3453
- const lines = fs22.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
3546
+ if (!fs23.existsSync(filePath)) return [];
3547
+ const lines = fs23.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
3454
3548
  return lines.map((line) => JSON.parse(line));
3455
3549
  }
3456
3550
  function getEventCount() {
3457
3551
  const filePath = sessionFilePath();
3458
- if (!fs22.existsSync(filePath)) return 0;
3459
- const content = fs22.readFileSync(filePath, "utf-8");
3552
+ if (!fs23.existsSync(filePath)) return 0;
3553
+ const content = fs23.readFileSync(filePath, "utf-8");
3460
3554
  return content.split("\n").filter(Boolean).length;
3461
3555
  }
3462
3556
  function clearSession() {
3463
3557
  const filePath = sessionFilePath();
3464
- if (fs22.existsSync(filePath)) fs22.unlinkSync(filePath);
3558
+ if (fs23.existsSync(filePath)) fs23.unlinkSync(filePath);
3465
3559
  }
3466
3560
  function readState2() {
3467
3561
  const filePath = stateFilePath();
3468
- if (!fs22.existsSync(filePath)) return { ...DEFAULT_STATE };
3562
+ if (!fs23.existsSync(filePath)) return { ...DEFAULT_STATE };
3469
3563
  try {
3470
- return JSON.parse(fs22.readFileSync(filePath, "utf-8"));
3564
+ return JSON.parse(fs23.readFileSync(filePath, "utf-8"));
3471
3565
  } catch {
3472
3566
  return { ...DEFAULT_STATE };
3473
3567
  }
3474
3568
  }
3475
3569
  function writeState2(state) {
3476
3570
  ensureLearningDir();
3477
- fs22.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
3571
+ fs23.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
3478
3572
  }
3479
3573
  function resetState() {
3480
3574
  writeState2({ ...DEFAULT_STATE });
3481
3575
  }
3482
3576
 
3483
3577
  // src/learner/writer.ts
3484
- import fs23 from "fs";
3485
- import path20 from "path";
3578
+ import fs24 from "fs";
3579
+ import path21 from "path";
3486
3580
  var LEARNED_START = "<!-- caliber:learned -->";
3487
3581
  var LEARNED_END = "<!-- /caliber:learned -->";
3488
3582
  function writeLearnedContent(update) {
@@ -3502,8 +3596,8 @@ function writeLearnedContent(update) {
3502
3596
  function writeLearnedSection(content) {
3503
3597
  const claudeMdPath = "CLAUDE.md";
3504
3598
  let existing = "";
3505
- if (fs23.existsSync(claudeMdPath)) {
3506
- existing = fs23.readFileSync(claudeMdPath, "utf-8");
3599
+ if (fs24.existsSync(claudeMdPath)) {
3600
+ existing = fs24.readFileSync(claudeMdPath, "utf-8");
3507
3601
  }
3508
3602
  const section = `${LEARNED_START}
3509
3603
  ${content}
@@ -3517,15 +3611,15 @@ ${LEARNED_END}`;
3517
3611
  const separator = existing.endsWith("\n") || existing === "" ? "" : "\n";
3518
3612
  updated = existing + separator + "\n" + section + "\n";
3519
3613
  }
3520
- fs23.writeFileSync(claudeMdPath, updated);
3614
+ fs24.writeFileSync(claudeMdPath, updated);
3521
3615
  }
3522
3616
  function writeLearnedSkill(skill) {
3523
- const skillDir = path20.join(".claude", "skills", skill.name);
3524
- if (!fs23.existsSync(skillDir)) fs23.mkdirSync(skillDir, { recursive: true });
3525
- const skillPath = path20.join(skillDir, "SKILL.md");
3526
- if (!skill.isNew && fs23.existsSync(skillPath)) {
3527
- const existing = fs23.readFileSync(skillPath, "utf-8");
3528
- fs23.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
3617
+ const skillDir = path21.join(".claude", "skills", skill.name);
3618
+ if (!fs24.existsSync(skillDir)) fs24.mkdirSync(skillDir, { recursive: true });
3619
+ const skillPath = path21.join(skillDir, "SKILL.md");
3620
+ if (!skill.isNew && fs24.existsSync(skillPath)) {
3621
+ const existing = fs24.readFileSync(skillPath, "utf-8");
3622
+ fs24.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
3529
3623
  } else {
3530
3624
  const frontmatter = [
3531
3625
  "---",
@@ -3534,107 +3628,20 @@ function writeLearnedSkill(skill) {
3534
3628
  "---",
3535
3629
  ""
3536
3630
  ].join("\n");
3537
- fs23.writeFileSync(skillPath, frontmatter + skill.content);
3631
+ fs24.writeFileSync(skillPath, frontmatter + skill.content);
3538
3632
  }
3539
3633
  return skillPath;
3540
3634
  }
3541
3635
  function readLearnedSection() {
3542
3636
  const claudeMdPath = "CLAUDE.md";
3543
- if (!fs23.existsSync(claudeMdPath)) return null;
3544
- const content = fs23.readFileSync(claudeMdPath, "utf-8");
3637
+ if (!fs24.existsSync(claudeMdPath)) return null;
3638
+ const content = fs24.readFileSync(claudeMdPath, "utf-8");
3545
3639
  const startIdx = content.indexOf(LEARNED_START);
3546
3640
  const endIdx = content.indexOf(LEARNED_END);
3547
3641
  if (startIdx === -1 || endIdx === -1) return null;
3548
3642
  return content.slice(startIdx + LEARNED_START.length, endIdx).trim();
3549
3643
  }
3550
3644
 
3551
- // src/lib/learning-hooks.ts
3552
- import fs24 from "fs";
3553
- import path21 from "path";
3554
- var SETTINGS_PATH2 = path21.join(".claude", "settings.json");
3555
- var HOOK_CONFIGS = [
3556
- {
3557
- event: "PostToolUse",
3558
- command: "caliber learn observe",
3559
- description: "Caliber: recording tool usage for session learning"
3560
- },
3561
- {
3562
- event: "PostToolUseFailure",
3563
- command: "caliber learn observe --failure",
3564
- description: "Caliber: recording tool failure for session learning"
3565
- },
3566
- {
3567
- event: "SessionEnd",
3568
- command: "caliber learn finalize",
3569
- description: "Caliber: finalizing session learnings"
3570
- }
3571
- ];
3572
- function readSettings2() {
3573
- if (!fs24.existsSync(SETTINGS_PATH2)) return {};
3574
- try {
3575
- return JSON.parse(fs24.readFileSync(SETTINGS_PATH2, "utf-8"));
3576
- } catch {
3577
- return {};
3578
- }
3579
- }
3580
- function writeSettings2(settings) {
3581
- const dir = path21.dirname(SETTINGS_PATH2);
3582
- if (!fs24.existsSync(dir)) fs24.mkdirSync(dir, { recursive: true });
3583
- fs24.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
3584
- }
3585
- function hasLearningHook(matchers, command) {
3586
- return matchers.some((entry) => entry.hooks?.some((h) => h.command === command));
3587
- }
3588
- function areLearningHooksInstalled() {
3589
- const settings = readSettings2();
3590
- if (!settings.hooks) return false;
3591
- return HOOK_CONFIGS.every((cfg) => {
3592
- const matchers = settings.hooks[cfg.event];
3593
- return Array.isArray(matchers) && hasLearningHook(matchers, cfg.command);
3594
- });
3595
- }
3596
- function installLearningHooks() {
3597
- if (areLearningHooksInstalled()) {
3598
- return { installed: false, alreadyInstalled: true };
3599
- }
3600
- const settings = readSettings2();
3601
- if (!settings.hooks) settings.hooks = {};
3602
- for (const cfg of HOOK_CONFIGS) {
3603
- if (!Array.isArray(settings.hooks[cfg.event])) {
3604
- settings.hooks[cfg.event] = [];
3605
- }
3606
- if (!hasLearningHook(settings.hooks[cfg.event], cfg.command)) {
3607
- settings.hooks[cfg.event].push({
3608
- matcher: "",
3609
- hooks: [{ type: "command", command: cfg.command, description: cfg.description }]
3610
- });
3611
- }
3612
- }
3613
- writeSettings2(settings);
3614
- return { installed: true, alreadyInstalled: false };
3615
- }
3616
- function removeLearningHooks() {
3617
- const settings = readSettings2();
3618
- if (!settings.hooks) return { removed: false, notFound: true };
3619
- let removedAny = false;
3620
- for (const cfg of HOOK_CONFIGS) {
3621
- const matchers = settings.hooks[cfg.event];
3622
- if (!Array.isArray(matchers)) continue;
3623
- const idx = matchers.findIndex((entry) => entry.hooks?.some((h) => h.command === cfg.command));
3624
- if (idx !== -1) {
3625
- matchers.splice(idx, 1);
3626
- removedAny = true;
3627
- if (matchers.length === 0) delete settings.hooks[cfg.event];
3628
- }
3629
- }
3630
- if (settings.hooks && Object.keys(settings.hooks).length === 0) {
3631
- delete settings.hooks;
3632
- }
3633
- if (!removedAny) return { removed: false, notFound: true };
3634
- writeSettings2(settings);
3635
- return { removed: true, notFound: false };
3636
- }
3637
-
3638
3645
  // src/commands/learn.ts
3639
3646
  async function learnObserveCommand(options) {
3640
3647
  try {