@rely-ai/caliber 1.25.1 → 1.26.0-dev.1773936625

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 +586 -308
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -230,7 +230,7 @@ __export(resolve_caliber_exports, {
230
230
  isCaliberCommand: () => isCaliberCommand,
231
231
  resolveCaliber: () => resolveCaliber
232
232
  });
233
- import fs18 from "fs";
233
+ import fs19 from "fs";
234
234
  import { execSync as execSync6 } from "child_process";
235
235
  function resolveCaliber() {
236
236
  if (_resolved) return _resolved;
@@ -252,7 +252,7 @@ function resolveCaliber() {
252
252
  } catch {
253
253
  }
254
254
  const binPath = process.argv[1];
255
- if (binPath && fs18.existsSync(binPath)) {
255
+ if (binPath && fs19.existsSync(binPath)) {
256
256
  _resolved = binPath;
257
257
  return _resolved;
258
258
  }
@@ -276,13 +276,13 @@ var init_resolve_caliber = __esm({
276
276
 
277
277
  // src/utils/editor.ts
278
278
  import { execSync as execSync12, spawn as spawn3 } from "child_process";
279
- import fs25 from "fs";
280
- import path21 from "path";
279
+ import fs26 from "fs";
280
+ import path22 from "path";
281
281
  import os5 from "os";
282
282
  function getEmptyFilePath(proposedPath) {
283
- fs25.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
284
- const tempPath = path21.join(DIFF_TEMP_DIR, path21.basename(proposedPath));
285
- fs25.writeFileSync(tempPath, "");
283
+ fs26.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
284
+ const tempPath = path22.join(DIFF_TEMP_DIR, path22.basename(proposedPath));
285
+ fs26.writeFileSync(tempPath, "");
286
286
  return tempPath;
287
287
  }
288
288
  function commandExists(cmd) {
@@ -322,7 +322,7 @@ var init_editor = __esm({
322
322
  "src/utils/editor.ts"() {
323
323
  "use strict";
324
324
  IS_WINDOWS3 = process.platform === "win32";
325
- DIFF_TEMP_DIR = path21.join(os5.tmpdir(), "caliber-diff");
325
+ DIFF_TEMP_DIR = path22.join(os5.tmpdir(), "caliber-diff");
326
326
  }
327
327
  });
328
328
 
@@ -334,7 +334,7 @@ __export(review_exports, {
334
334
  promptWantsReview: () => promptWantsReview
335
335
  });
336
336
  import chalk10 from "chalk";
337
- import fs26 from "fs";
337
+ import fs27 from "fs";
338
338
  import select4 from "@inquirer/select";
339
339
  import { createTwoFilesPatch } from "diff";
340
340
  async function promptWantsReview() {
@@ -371,8 +371,8 @@ async function openReview(method, stagedFiles) {
371
371
  return;
372
372
  }
373
373
  const fileInfos = stagedFiles.map((file) => {
374
- const proposed = fs26.readFileSync(file.proposedPath, "utf-8");
375
- const current = file.currentPath ? fs26.readFileSync(file.currentPath, "utf-8") : "";
374
+ const proposed = fs27.readFileSync(file.proposedPath, "utf-8");
375
+ const current = file.currentPath ? fs27.readFileSync(file.currentPath, "utf-8") : "";
376
376
  const patch = createTwoFilesPatch(
377
377
  file.isNew ? "/dev/null" : file.relativePath,
378
378
  file.relativePath,
@@ -547,13 +547,13 @@ __export(lock_exports, {
547
547
  isCaliberRunning: () => isCaliberRunning,
548
548
  releaseLock: () => releaseLock
549
549
  });
550
- import fs35 from "fs";
551
- import path27 from "path";
550
+ import fs36 from "fs";
551
+ import path28 from "path";
552
552
  import os7 from "os";
553
553
  function isCaliberRunning() {
554
554
  try {
555
- if (!fs35.existsSync(LOCK_FILE)) return false;
556
- const raw = fs35.readFileSync(LOCK_FILE, "utf-8").trim();
555
+ if (!fs36.existsSync(LOCK_FILE)) return false;
556
+ const raw = fs36.readFileSync(LOCK_FILE, "utf-8").trim();
557
557
  const { pid, ts } = JSON.parse(raw);
558
558
  if (Date.now() - ts > STALE_MS) return false;
559
559
  try {
@@ -568,13 +568,13 @@ function isCaliberRunning() {
568
568
  }
569
569
  function acquireLock() {
570
570
  try {
571
- fs35.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
571
+ fs36.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
572
572
  } catch {
573
573
  }
574
574
  }
575
575
  function releaseLock() {
576
576
  try {
577
- if (fs35.existsSync(LOCK_FILE)) fs35.unlinkSync(LOCK_FILE);
577
+ if (fs36.existsSync(LOCK_FILE)) fs36.unlinkSync(LOCK_FILE);
578
578
  } catch {
579
579
  }
580
580
  }
@@ -582,21 +582,21 @@ var LOCK_FILE, STALE_MS;
582
582
  var init_lock = __esm({
583
583
  "src/lib/lock.ts"() {
584
584
  "use strict";
585
- LOCK_FILE = path27.join(os7.tmpdir(), ".caliber.lock");
585
+ LOCK_FILE = path28.join(os7.tmpdir(), ".caliber.lock");
586
586
  STALE_MS = 10 * 60 * 1e3;
587
587
  }
588
588
  });
589
589
 
590
590
  // src/cli.ts
591
591
  import { Command } from "commander";
592
- import fs45 from "fs";
593
- import path36 from "path";
592
+ import fs46 from "fs";
593
+ import path37 from "path";
594
594
  import { fileURLToPath } from "url";
595
595
 
596
596
  // src/commands/init.ts
597
- import path23 from "path";
597
+ import path24 from "path";
598
598
  import chalk14 from "chalk";
599
- import fs30 from "fs";
599
+ import fs31 from "fs";
600
600
 
601
601
  // src/fingerprint/index.ts
602
602
  import fs7 from "fs";
@@ -3424,15 +3424,15 @@ init_config();
3424
3424
  // src/utils/dependencies.ts
3425
3425
  import { readFileSync as readFileSync2 } from "fs";
3426
3426
  import { join as join2 } from "path";
3427
- function readFileOrNull2(path38) {
3427
+ function readFileOrNull2(path39) {
3428
3428
  try {
3429
- return readFileSync2(path38, "utf-8");
3429
+ return readFileSync2(path39, "utf-8");
3430
3430
  } catch {
3431
3431
  return null;
3432
3432
  }
3433
3433
  }
3434
- function readJsonOrNull(path38) {
3435
- const content = readFileOrNull2(path38);
3434
+ function readJsonOrNull(path39) {
3435
+ const content = readFileOrNull2(path39);
3436
3436
  if (!content) return null;
3437
3437
  try {
3438
3438
  return JSON.parse(content);
@@ -4439,7 +4439,11 @@ function cleanupStaging() {
4439
4439
  }
4440
4440
 
4441
4441
  // src/commands/setup-files.ts
4442
+ import fs18 from "fs";
4443
+
4444
+ // src/lib/builtin-skills.ts
4442
4445
  import fs17 from "fs";
4446
+ import path15 from "path";
4443
4447
  function buildSkillContent(skill) {
4444
4448
  const frontmatter = `---
4445
4449
  name: ${skill.name}
@@ -4449,6 +4453,142 @@ description: ${skill.description}
4449
4453
  `;
4450
4454
  return frontmatter + skill.content;
4451
4455
  }
4456
+ var FIND_SKILLS_SKILL = {
4457
+ name: "find-skills",
4458
+ description: "Discovers and installs community skills from the public registry. Use when the user mentions a technology, framework, or task that could benefit from specialized skills not yet installed, asks 'how do I do X', 'find a skill for X', or starts work in a new technology area. Proactively suggest when the user's task involves tools or frameworks without existing skills.",
4459
+ content: `# Find Skills
4460
+
4461
+ Search the public skill registry for community-contributed skills
4462
+ relevant to the user's current task and install them into this project.
4463
+
4464
+ ## Instructions
4465
+
4466
+ 1. Identify the key technologies, frameworks, or task types from the
4467
+ user's request that might have community skills available
4468
+ 2. Ask the user: "Would you like me to search for community skills
4469
+ for [identified technologies]?"
4470
+ 3. If the user agrees, run:
4471
+ \`\`\`bash
4472
+ caliber skills --query "<relevant terms>"
4473
+ \`\`\`
4474
+ This outputs the top 5 matching skills with scores and descriptions.
4475
+ 4. Present the results to the user and ask which ones to install
4476
+ 5. Install the selected skills:
4477
+ \`\`\`bash
4478
+ caliber skills --install <slug1>,<slug2>
4479
+ \`\`\`
4480
+ 6. Read the installed SKILL.md files to load them into your current
4481
+ context so you can use them immediately in this session
4482
+ 7. Summarize what was installed and continue with the user's task
4483
+
4484
+ ## Examples
4485
+
4486
+ User: "let's build a web app using React"
4487
+ -> "I notice you want to work with React. Would you like me to search
4488
+ for community skills that could help with React development?"
4489
+ -> If yes: run \`caliber skills --query "react frontend"\`
4490
+ -> Show the user the results, ask which to install
4491
+ -> Run \`caliber skills --install <selected-slugs>\`
4492
+ -> Read the installed files and continue
4493
+
4494
+ User: "help me set up Docker for this project"
4495
+ -> "Would you like me to search for Docker-related skills?"
4496
+ -> If yes: run \`caliber skills --query "docker deployment"\`
4497
+
4498
+ User: "I need to write tests for this Python ML pipeline"
4499
+ -> "Would you like me to find skills for Python ML testing?"
4500
+ -> If yes: run \`caliber skills --query "python machine-learning testing"\`
4501
+
4502
+ ## When NOT to trigger
4503
+
4504
+ - The user is working within an already well-configured area
4505
+ - You already suggested skills for this technology in this session
4506
+ - The user is in the middle of urgent debugging or time-sensitive work
4507
+ - The technology is too generic (e.g. just "code" or "programming")
4508
+ `
4509
+ };
4510
+ var SAVE_LEARNING_SKILL = {
4511
+ name: "save-learning",
4512
+ description: "Saves user instructions as persistent learnings for future sessions. Use when the user says 'remember this', 'always do X', 'from now on', 'never do Y', or gives any instruction they want persisted across sessions. Proactively suggest when the user states a preference, convention, or rule they clearly want followed in the future.",
4513
+ content: `# Save Learning
4514
+
4515
+ Save a user's instruction or preference as a persistent learning that
4516
+ will be applied in all future sessions on this project.
4517
+
4518
+ ## Instructions
4519
+
4520
+ 1. Detect when the user gives an instruction to remember, such as:
4521
+ - "remember this", "save this", "always do X", "never do Y"
4522
+ - "from now on", "going forward", "in this project we..."
4523
+ - Any stated convention, preference, or rule
4524
+ 2. Refine the instruction into a clean, actionable learning bullet with
4525
+ an appropriate type prefix:
4526
+ - \`**[convention]**\` \u2014 coding style, workflow, git conventions
4527
+ - \`**[pattern]**\` \u2014 reusable code patterns
4528
+ - \`**[anti-pattern]**\` \u2014 things to avoid
4529
+ - \`**[preference]**\` \u2014 personal/team preferences
4530
+ - \`**[context]**\` \u2014 project-specific context
4531
+ 3. Show the refined learning to the user and ask for confirmation
4532
+ 4. If confirmed, run:
4533
+ \`\`\`bash
4534
+ caliber learn add "<refined learning>"
4535
+ \`\`\`
4536
+ For personal preferences (not project-level), add \`--personal\`:
4537
+ \`\`\`bash
4538
+ caliber learn add --personal "<refined learning>"
4539
+ \`\`\`
4540
+ 5. Stage the learnings file for the next commit:
4541
+ \`\`\`bash
4542
+ git add CALIBER_LEARNINGS.md
4543
+ \`\`\`
4544
+
4545
+ ## Examples
4546
+
4547
+ User: "when developing features, push to next branch not master, remember it"
4548
+ -> Refine: \`**[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`\`
4549
+ -> "I'll save this as a project learning:
4550
+ **[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`
4551
+ Save for future sessions?"
4552
+ -> If yes: run \`caliber learn add "**[convention]** Push feature commits to the next branch, not master"\`
4553
+ -> Run \`git add CALIBER_LEARNINGS.md\`
4554
+
4555
+ User: "always use bun instead of npm"
4556
+ -> Refine: \`**[preference]** Use \\\`bun\\\` instead of \\\`npm\\\` for package management\`
4557
+ -> Confirm and save
4558
+
4559
+ User: "never use any in TypeScript, use unknown instead"
4560
+ -> Refine: \`**[convention]** Use \\\`unknown\\\` instead of \\\`any\\\` in TypeScript\`
4561
+ -> Confirm and save
4562
+
4563
+ ## When NOT to trigger
4564
+
4565
+ - The user is giving a one-time instruction for the current task only
4566
+ - The instruction is too vague to be actionable
4567
+ - The user explicitly says "just for now" or "only this time"
4568
+ `
4569
+ };
4570
+ var BUILTIN_SKILLS = [FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL];
4571
+ var PLATFORM_CONFIGS = [
4572
+ { platformDir: ".claude", skillsDir: path15.join(".claude", "skills") },
4573
+ { platformDir: ".cursor", skillsDir: path15.join(".cursor", "skills") },
4574
+ { platformDir: ".agents", skillsDir: path15.join(".agents", "skills") }
4575
+ ];
4576
+ function ensureBuiltinSkills() {
4577
+ const written = [];
4578
+ for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
4579
+ if (!fs17.existsSync(platformDir)) continue;
4580
+ for (const skill of BUILTIN_SKILLS) {
4581
+ const skillPath = path15.join(skillsDir, skill.name, "SKILL.md");
4582
+ if (fs17.existsSync(skillPath)) continue;
4583
+ fs17.mkdirSync(path15.dirname(skillPath), { recursive: true });
4584
+ fs17.writeFileSync(skillPath, buildSkillContent(skill));
4585
+ written.push(skillPath);
4586
+ }
4587
+ }
4588
+ return written;
4589
+ }
4590
+
4591
+ // src/commands/setup-files.ts
4452
4592
  function collectSetupFiles(setup, targetAgent) {
4453
4593
  const files = [];
4454
4594
  const claude = setup.claude;
@@ -4462,6 +4602,9 @@ function collectSetupFiles(setup, targetAgent) {
4462
4602
  files.push({ path: `.claude/skills/${skill.name}/SKILL.md`, content: buildSkillContent(skill) });
4463
4603
  }
4464
4604
  }
4605
+ for (const builtin of BUILTIN_SKILLS) {
4606
+ files.push({ path: `.claude/skills/${builtin.name}/SKILL.md`, content: buildSkillContent(builtin) });
4607
+ }
4465
4608
  }
4466
4609
  if (codex) {
4467
4610
  if (codex.agentsMd) files.push({ path: "AGENTS.md", content: codex.agentsMd });
@@ -4471,6 +4614,9 @@ function collectSetupFiles(setup, targetAgent) {
4471
4614
  files.push({ path: `.agents/skills/${skill.name}/SKILL.md`, content: buildSkillContent(skill) });
4472
4615
  }
4473
4616
  }
4617
+ for (const builtin of BUILTIN_SKILLS) {
4618
+ files.push({ path: `.agents/skills/${builtin.name}/SKILL.md`, content: buildSkillContent(builtin) });
4619
+ }
4474
4620
  }
4475
4621
  if (cursor) {
4476
4622
  if (cursor.cursorrules) files.push({ path: ".cursorrules", content: cursor.cursorrules });
@@ -4480,6 +4626,9 @@ function collectSetupFiles(setup, targetAgent) {
4480
4626
  files.push({ path: `.cursor/skills/${skill.name}/SKILL.md`, content: buildSkillContent(skill) });
4481
4627
  }
4482
4628
  }
4629
+ for (const builtin of BUILTIN_SKILLS) {
4630
+ files.push({ path: `.cursor/skills/${builtin.name}/SKILL.md`, content: buildSkillContent(builtin) });
4631
+ }
4483
4632
  const rules = cursor.rules;
4484
4633
  if (Array.isArray(rules)) {
4485
4634
  for (const rule of rules) {
@@ -4498,7 +4647,7 @@ function collectSetupFiles(setup, targetAgent) {
4498
4647
  }
4499
4648
  }
4500
4649
  const codexTargeted = targetAgent ? targetAgent.includes("codex") : false;
4501
- if (codexTargeted && !fs17.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
4650
+ if (codexTargeted && !fs18.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
4502
4651
  const agentRefs = [];
4503
4652
  if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
4504
4653
  if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
@@ -4516,9 +4665,9 @@ ${agentRefs.join(" ")}
4516
4665
 
4517
4666
  // src/lib/learning-hooks.ts
4518
4667
  init_resolve_caliber();
4519
- import fs19 from "fs";
4520
- import path15 from "path";
4521
- var SETTINGS_PATH = path15.join(".claude", "settings.json");
4668
+ import fs20 from "fs";
4669
+ import path16 from "path";
4670
+ var SETTINGS_PATH = path16.join(".claude", "settings.json");
4522
4671
  var HOOK_TAILS = [
4523
4672
  { event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
4524
4673
  { event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
@@ -4535,17 +4684,17 @@ function getHookConfigs() {
4535
4684
  }));
4536
4685
  }
4537
4686
  function readSettings() {
4538
- if (!fs19.existsSync(SETTINGS_PATH)) return {};
4687
+ if (!fs20.existsSync(SETTINGS_PATH)) return {};
4539
4688
  try {
4540
- return JSON.parse(fs19.readFileSync(SETTINGS_PATH, "utf-8"));
4689
+ return JSON.parse(fs20.readFileSync(SETTINGS_PATH, "utf-8"));
4541
4690
  } catch {
4542
4691
  return {};
4543
4692
  }
4544
4693
  }
4545
4694
  function writeSettings(settings) {
4546
- const dir = path15.dirname(SETTINGS_PATH);
4547
- if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
4548
- fs19.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
4695
+ const dir = path16.dirname(SETTINGS_PATH);
4696
+ if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
4697
+ fs20.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
4549
4698
  }
4550
4699
  function hasLearningHook(matchers, tail) {
4551
4700
  return matchers.some((entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, tail)));
@@ -4579,7 +4728,7 @@ function installLearningHooks() {
4579
4728
  writeSettings(settings);
4580
4729
  return { installed: true, alreadyInstalled: false };
4581
4730
  }
4582
- var CURSOR_HOOKS_PATH = path15.join(".cursor", "hooks.json");
4731
+ var CURSOR_HOOKS_PATH = path16.join(".cursor", "hooks.json");
4583
4732
  var CURSOR_HOOK_EVENTS = [
4584
4733
  { event: "postToolUse", tail: "learn observe" },
4585
4734
  { event: "postToolUseFailure", tail: "learn observe --failure" },
@@ -4587,17 +4736,17 @@ var CURSOR_HOOK_EVENTS = [
4587
4736
  { event: "sessionEnd", tail: "learn finalize --auto" }
4588
4737
  ];
4589
4738
  function readCursorHooks() {
4590
- if (!fs19.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
4739
+ if (!fs20.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
4591
4740
  try {
4592
- return JSON.parse(fs19.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
4741
+ return JSON.parse(fs20.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
4593
4742
  } catch {
4594
4743
  return { version: 1, hooks: {} };
4595
4744
  }
4596
4745
  }
4597
4746
  function writeCursorHooks(config) {
4598
- const dir = path15.dirname(CURSOR_HOOKS_PATH);
4599
- if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
4600
- fs19.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
4747
+ const dir = path16.dirname(CURSOR_HOOKS_PATH);
4748
+ if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
4749
+ fs20.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
4601
4750
  }
4602
4751
  function hasCursorHook(entries, tail) {
4603
4752
  return entries.some((e) => isCaliberCommand(e.command, tail));
@@ -4667,10 +4816,10 @@ function removeLearningHooks() {
4667
4816
 
4668
4817
  // src/lib/state.ts
4669
4818
  init_constants();
4670
- import fs20 from "fs";
4671
- import path16 from "path";
4819
+ import fs21 from "fs";
4820
+ import path17 from "path";
4672
4821
  import { execSync as execSync7 } from "child_process";
4673
- var STATE_FILE = path16.join(CALIBER_DIR, ".caliber-state.json");
4822
+ var STATE_FILE = path17.join(CALIBER_DIR, ".caliber-state.json");
4674
4823
  function normalizeTargetAgent(value) {
4675
4824
  if (Array.isArray(value)) return value;
4676
4825
  if (typeof value === "string") {
@@ -4681,8 +4830,8 @@ function normalizeTargetAgent(value) {
4681
4830
  }
4682
4831
  function readState() {
4683
4832
  try {
4684
- if (!fs20.existsSync(STATE_FILE)) return null;
4685
- const raw = JSON.parse(fs20.readFileSync(STATE_FILE, "utf-8"));
4833
+ if (!fs21.existsSync(STATE_FILE)) return null;
4834
+ const raw = JSON.parse(fs21.readFileSync(STATE_FILE, "utf-8"));
4686
4835
  if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
4687
4836
  return raw;
4688
4837
  } catch {
@@ -4690,10 +4839,10 @@ function readState() {
4690
4839
  }
4691
4840
  }
4692
4841
  function writeState(state) {
4693
- if (!fs20.existsSync(CALIBER_DIR)) {
4694
- fs20.mkdirSync(CALIBER_DIR, { recursive: true });
4842
+ if (!fs21.existsSync(CALIBER_DIR)) {
4843
+ fs21.mkdirSync(CALIBER_DIR, { recursive: true });
4695
4844
  }
4696
- fs20.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
4845
+ fs21.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
4697
4846
  }
4698
4847
  function getCurrentHeadSha() {
4699
4848
  try {
@@ -5752,22 +5901,22 @@ function checkSources(dir) {
5752
5901
 
5753
5902
  // src/scoring/dismissed.ts
5754
5903
  init_constants();
5755
- import fs21 from "fs";
5756
- import path17 from "path";
5757
- var DISMISSED_FILE = path17.join(CALIBER_DIR, "dismissed-checks.json");
5904
+ import fs22 from "fs";
5905
+ import path18 from "path";
5906
+ var DISMISSED_FILE = path18.join(CALIBER_DIR, "dismissed-checks.json");
5758
5907
  function readDismissedChecks() {
5759
5908
  try {
5760
- if (!fs21.existsSync(DISMISSED_FILE)) return [];
5761
- return JSON.parse(fs21.readFileSync(DISMISSED_FILE, "utf-8"));
5909
+ if (!fs22.existsSync(DISMISSED_FILE)) return [];
5910
+ return JSON.parse(fs22.readFileSync(DISMISSED_FILE, "utf-8"));
5762
5911
  } catch {
5763
5912
  return [];
5764
5913
  }
5765
5914
  }
5766
5915
  function writeDismissedChecks(checks) {
5767
- if (!fs21.existsSync(CALIBER_DIR)) {
5768
- fs21.mkdirSync(CALIBER_DIR, { recursive: true });
5916
+ if (!fs22.existsSync(CALIBER_DIR)) {
5917
+ fs22.mkdirSync(CALIBER_DIR, { recursive: true });
5769
5918
  }
5770
- fs21.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
5919
+ fs22.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
5771
5920
  }
5772
5921
  function getDismissedIds() {
5773
5922
  return new Set(readDismissedChecks().map((c) => c.id));
@@ -6015,13 +6164,13 @@ import { mkdirSync, readFileSync as readFileSync4, readdirSync as readdirSync4,
6015
6164
  import { join as join10, dirname as dirname2 } from "path";
6016
6165
 
6017
6166
  // src/scanner/index.ts
6018
- import fs22 from "fs";
6019
- import path18 from "path";
6167
+ import fs23 from "fs";
6168
+ import path19 from "path";
6020
6169
  import crypto3 from "crypto";
6021
6170
  function scanLocalState(dir) {
6022
6171
  const items = [];
6023
- const claudeMdPath = path18.join(dir, "CLAUDE.md");
6024
- if (fs22.existsSync(claudeMdPath)) {
6172
+ const claudeMdPath = path19.join(dir, "CLAUDE.md");
6173
+ if (fs23.existsSync(claudeMdPath)) {
6025
6174
  items.push({
6026
6175
  type: "rule",
6027
6176
  platform: "claude",
@@ -6030,10 +6179,10 @@ function scanLocalState(dir) {
6030
6179
  path: claudeMdPath
6031
6180
  });
6032
6181
  }
6033
- const skillsDir = path18.join(dir, ".claude", "skills");
6034
- if (fs22.existsSync(skillsDir)) {
6035
- for (const file of fs22.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
6036
- const filePath = path18.join(skillsDir, file);
6182
+ const skillsDir = path19.join(dir, ".claude", "skills");
6183
+ if (fs23.existsSync(skillsDir)) {
6184
+ for (const file of fs23.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
6185
+ const filePath = path19.join(skillsDir, file);
6037
6186
  items.push({
6038
6187
  type: "skill",
6039
6188
  platform: "claude",
@@ -6043,10 +6192,10 @@ function scanLocalState(dir) {
6043
6192
  });
6044
6193
  }
6045
6194
  }
6046
- const mcpJsonPath = path18.join(dir, ".mcp.json");
6047
- if (fs22.existsSync(mcpJsonPath)) {
6195
+ const mcpJsonPath = path19.join(dir, ".mcp.json");
6196
+ if (fs23.existsSync(mcpJsonPath)) {
6048
6197
  try {
6049
- const mcpJson = JSON.parse(fs22.readFileSync(mcpJsonPath, "utf-8"));
6198
+ const mcpJson = JSON.parse(fs23.readFileSync(mcpJsonPath, "utf-8"));
6050
6199
  if (mcpJson.mcpServers) {
6051
6200
  for (const name of Object.keys(mcpJson.mcpServers)) {
6052
6201
  items.push({
@@ -6061,8 +6210,8 @@ function scanLocalState(dir) {
6061
6210
  } catch {
6062
6211
  }
6063
6212
  }
6064
- const agentsMdPath = path18.join(dir, "AGENTS.md");
6065
- if (fs22.existsSync(agentsMdPath)) {
6213
+ const agentsMdPath = path19.join(dir, "AGENTS.md");
6214
+ if (fs23.existsSync(agentsMdPath)) {
6066
6215
  items.push({
6067
6216
  type: "rule",
6068
6217
  platform: "codex",
@@ -6071,12 +6220,12 @@ function scanLocalState(dir) {
6071
6220
  path: agentsMdPath
6072
6221
  });
6073
6222
  }
6074
- const codexSkillsDir = path18.join(dir, ".agents", "skills");
6075
- if (fs22.existsSync(codexSkillsDir)) {
6223
+ const codexSkillsDir = path19.join(dir, ".agents", "skills");
6224
+ if (fs23.existsSync(codexSkillsDir)) {
6076
6225
  try {
6077
- for (const name of fs22.readdirSync(codexSkillsDir)) {
6078
- const skillFile = path18.join(codexSkillsDir, name, "SKILL.md");
6079
- if (fs22.existsSync(skillFile)) {
6226
+ for (const name of fs23.readdirSync(codexSkillsDir)) {
6227
+ const skillFile = path19.join(codexSkillsDir, name, "SKILL.md");
6228
+ if (fs23.existsSync(skillFile)) {
6080
6229
  items.push({
6081
6230
  type: "skill",
6082
6231
  platform: "codex",
@@ -6089,8 +6238,8 @@ function scanLocalState(dir) {
6089
6238
  } catch {
6090
6239
  }
6091
6240
  }
6092
- const cursorrulesPath = path18.join(dir, ".cursorrules");
6093
- if (fs22.existsSync(cursorrulesPath)) {
6241
+ const cursorrulesPath = path19.join(dir, ".cursorrules");
6242
+ if (fs23.existsSync(cursorrulesPath)) {
6094
6243
  items.push({
6095
6244
  type: "rule",
6096
6245
  platform: "cursor",
@@ -6099,10 +6248,10 @@ function scanLocalState(dir) {
6099
6248
  path: cursorrulesPath
6100
6249
  });
6101
6250
  }
6102
- const cursorRulesDir = path18.join(dir, ".cursor", "rules");
6103
- if (fs22.existsSync(cursorRulesDir)) {
6104
- for (const file of fs22.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
6105
- const filePath = path18.join(cursorRulesDir, file);
6251
+ const cursorRulesDir = path19.join(dir, ".cursor", "rules");
6252
+ if (fs23.existsSync(cursorRulesDir)) {
6253
+ for (const file of fs23.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
6254
+ const filePath = path19.join(cursorRulesDir, file);
6106
6255
  items.push({
6107
6256
  type: "rule",
6108
6257
  platform: "cursor",
@@ -6112,12 +6261,12 @@ function scanLocalState(dir) {
6112
6261
  });
6113
6262
  }
6114
6263
  }
6115
- const cursorSkillsDir = path18.join(dir, ".cursor", "skills");
6116
- if (fs22.existsSync(cursorSkillsDir)) {
6264
+ const cursorSkillsDir = path19.join(dir, ".cursor", "skills");
6265
+ if (fs23.existsSync(cursorSkillsDir)) {
6117
6266
  try {
6118
- for (const name of fs22.readdirSync(cursorSkillsDir)) {
6119
- const skillFile = path18.join(cursorSkillsDir, name, "SKILL.md");
6120
- if (fs22.existsSync(skillFile)) {
6267
+ for (const name of fs23.readdirSync(cursorSkillsDir)) {
6268
+ const skillFile = path19.join(cursorSkillsDir, name, "SKILL.md");
6269
+ if (fs23.existsSync(skillFile)) {
6121
6270
  items.push({
6122
6271
  type: "skill",
6123
6272
  platform: "cursor",
@@ -6130,10 +6279,10 @@ function scanLocalState(dir) {
6130
6279
  } catch {
6131
6280
  }
6132
6281
  }
6133
- const cursorMcpPath = path18.join(dir, ".cursor", "mcp.json");
6134
- if (fs22.existsSync(cursorMcpPath)) {
6282
+ const cursorMcpPath = path19.join(dir, ".cursor", "mcp.json");
6283
+ if (fs23.existsSync(cursorMcpPath)) {
6135
6284
  try {
6136
- const mcpJson = JSON.parse(fs22.readFileSync(cursorMcpPath, "utf-8"));
6285
+ const mcpJson = JSON.parse(fs23.readFileSync(cursorMcpPath, "utf-8"));
6137
6286
  if (mcpJson.mcpServers) {
6138
6287
  for (const name of Object.keys(mcpJson.mcpServers)) {
6139
6288
  items.push({
@@ -6151,7 +6300,7 @@ function scanLocalState(dir) {
6151
6300
  return items;
6152
6301
  }
6153
6302
  function hashFile(filePath) {
6154
- const text = fs22.readFileSync(filePath, "utf-8");
6303
+ const text = fs23.readFileSync(filePath, "utf-8");
6155
6304
  return crypto3.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
6156
6305
  }
6157
6306
  function hashJson(obj) {
@@ -6166,27 +6315,27 @@ import { PostHog } from "posthog-node";
6166
6315
  import chalk5 from "chalk";
6167
6316
 
6168
6317
  // src/telemetry/config.ts
6169
- import fs23 from "fs";
6170
- import path19 from "path";
6318
+ import fs24 from "fs";
6319
+ import path20 from "path";
6171
6320
  import os4 from "os";
6172
6321
  import crypto4 from "crypto";
6173
6322
  import { execSync as execSync11 } from "child_process";
6174
- var CONFIG_DIR2 = path19.join(os4.homedir(), ".caliber");
6175
- var CONFIG_FILE2 = path19.join(CONFIG_DIR2, "config.json");
6323
+ var CONFIG_DIR2 = path20.join(os4.homedir(), ".caliber");
6324
+ var CONFIG_FILE2 = path20.join(CONFIG_DIR2, "config.json");
6176
6325
  var runtimeDisabled = false;
6177
6326
  function readConfig() {
6178
6327
  try {
6179
- if (!fs23.existsSync(CONFIG_FILE2)) return {};
6180
- return JSON.parse(fs23.readFileSync(CONFIG_FILE2, "utf-8"));
6328
+ if (!fs24.existsSync(CONFIG_FILE2)) return {};
6329
+ return JSON.parse(fs24.readFileSync(CONFIG_FILE2, "utf-8"));
6181
6330
  } catch {
6182
6331
  return {};
6183
6332
  }
6184
6333
  }
6185
6334
  function writeConfig(config) {
6186
- if (!fs23.existsSync(CONFIG_DIR2)) {
6187
- fs23.mkdirSync(CONFIG_DIR2, { recursive: true });
6335
+ if (!fs24.existsSync(CONFIG_DIR2)) {
6336
+ fs24.mkdirSync(CONFIG_DIR2, { recursive: true });
6188
6337
  }
6189
- fs23.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
6338
+ fs24.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
6190
6339
  }
6191
6340
  function getMachineId() {
6192
6341
  const config = readConfig();
@@ -6671,7 +6820,109 @@ async function searchSkills(fingerprint, targetPlatforms, onStatus) {
6671
6820
  const available = results.filter((r) => contentMap.has(r.slug));
6672
6821
  return { results: available, contentMap };
6673
6822
  }
6674
- async function recommendCommand() {
6823
+ async function querySkills(query) {
6824
+ const terms = query.split(/[\s,]+/).filter(Boolean);
6825
+ if (terms.length === 0) {
6826
+ console.log(chalk6.yellow("Please provide search terms."));
6827
+ throw new Error("__exit__");
6828
+ }
6829
+ const platforms = detectLocalPlatforms();
6830
+ const installedSkills = getInstalledSkills(platforms);
6831
+ const primaryPlatform = platforms.includes("claude") ? "claude" : platforms[0];
6832
+ const searchSpinner = ora("Searching skill registries...").start();
6833
+ const allCandidates = await searchAllProviders(terms, primaryPlatform);
6834
+ if (!allCandidates.length) {
6835
+ searchSpinner.succeed("No skills found matching your query.");
6836
+ return;
6837
+ }
6838
+ const newCandidates = allCandidates.filter((c) => !installedSkills.has(c.slug.toLowerCase()));
6839
+ if (!newCandidates.length) {
6840
+ searchSpinner.succeed("All matching skills are already installed.");
6841
+ return;
6842
+ }
6843
+ searchSpinner.succeed(`Found ${newCandidates.length} candidates`);
6844
+ let results;
6845
+ const config = loadConfig();
6846
+ if (config) {
6847
+ const scoreSpinner = ora("Scoring relevance...").start();
6848
+ try {
6849
+ const queryContext = `User is looking for skills related to: ${query}`;
6850
+ results = await scoreWithLLM(newCandidates, queryContext, terms);
6851
+ if (results.length === 0) {
6852
+ scoreSpinner.succeed("No highly relevant skills found.");
6853
+ return;
6854
+ }
6855
+ scoreSpinner.succeed(`${results.length} relevant`);
6856
+ } catch {
6857
+ results = newCandidates.slice(0, 5);
6858
+ }
6859
+ } else {
6860
+ results = newCandidates.slice(0, 5);
6861
+ }
6862
+ const top = results.slice(0, 5);
6863
+ const fetchSpinner = ora("Verifying availability...").start();
6864
+ const contentMap = /* @__PURE__ */ new Map();
6865
+ await Promise.all(top.map(async (rec) => {
6866
+ const content = await fetchSkillContent(rec);
6867
+ if (content) contentMap.set(rec.slug, content);
6868
+ }));
6869
+ const available = top.filter((r) => contentMap.has(r.slug));
6870
+ fetchSpinner.succeed(`${available.length} available`);
6871
+ if (!available.length) {
6872
+ console.log(chalk6.dim(" No installable skills found.\n"));
6873
+ return;
6874
+ }
6875
+ console.log("");
6876
+ for (let i = 0; i < available.length; i++) {
6877
+ const r = available[i];
6878
+ const scoreStr = r.score > 0 ? ` (score: ${r.score})` : "";
6879
+ console.log(` ${i + 1}. ${r.slug}${scoreStr}`);
6880
+ console.log(` ${r.reason || r.name}`);
6881
+ }
6882
+ console.log("");
6883
+ console.log(chalk6.dim(` Install with: caliber skills --install ${available.map((r) => r.slug).join(",")}`));
6884
+ console.log("");
6885
+ }
6886
+ async function installBySlug(slugStr) {
6887
+ const slugs = slugStr.split(",").map((s) => s.trim()).filter(Boolean);
6888
+ if (slugs.length === 0) {
6889
+ console.log(chalk6.yellow("Please provide skill slugs to install."));
6890
+ throw new Error("__exit__");
6891
+ }
6892
+ const platforms = detectLocalPlatforms();
6893
+ const spinner = ora(`Fetching ${slugs.length} skill${slugs.length > 1 ? "s" : ""}...`).start();
6894
+ const allResults = await searchAllProviders(slugs);
6895
+ const matched = [];
6896
+ for (const slug of slugs) {
6897
+ const match = allResults.find((r) => r.slug.toLowerCase() === slug.toLowerCase());
6898
+ if (match) matched.push(match);
6899
+ }
6900
+ if (!matched.length) {
6901
+ spinner.fail("No matching skills found in the registry.");
6902
+ return;
6903
+ }
6904
+ const contentMap = /* @__PURE__ */ new Map();
6905
+ await Promise.all(matched.map(async (rec) => {
6906
+ const content = await fetchSkillContent(rec);
6907
+ if (content) contentMap.set(rec.slug, content);
6908
+ }));
6909
+ const installable = matched.filter((r) => contentMap.has(r.slug));
6910
+ if (!installable.length) {
6911
+ spinner.fail("Could not fetch skill content.");
6912
+ return;
6913
+ }
6914
+ spinner.succeed(`Fetched ${installable.length} skill${installable.length > 1 ? "s" : ""}`);
6915
+ await installSkills(installable, platforms, contentMap);
6916
+ }
6917
+ async function recommendCommand(options) {
6918
+ if (options.install) {
6919
+ await installBySlug(options.install);
6920
+ return;
6921
+ }
6922
+ if (options.query) {
6923
+ await querySkills(options.query);
6924
+ return;
6925
+ }
6675
6926
  const proceed = await select3({
6676
6927
  message: "Search public repos for relevant skills to add to this project?",
6677
6928
  choices: [
@@ -7145,11 +7396,11 @@ function countIssuePoints(issues) {
7145
7396
  }
7146
7397
  async function scoreAndRefine(setup, dir, sessionHistory, callbacks) {
7147
7398
  const existsCache = /* @__PURE__ */ new Map();
7148
- const cachedExists = (path38) => {
7149
- const cached = existsCache.get(path38);
7399
+ const cachedExists = (path39) => {
7400
+ const cached = existsCache.get(path39);
7150
7401
  if (cached !== void 0) return cached;
7151
- const result = existsSync9(path38);
7152
- existsCache.set(path38, result);
7402
+ const result = existsSync9(path39);
7403
+ existsCache.set(path39, result);
7153
7404
  return result;
7154
7405
  };
7155
7406
  const projectStructure = collectProjectStructure(dir);
@@ -7288,8 +7539,8 @@ async function runScoreRefineWithSpinner(setup, dir, sessionHistory) {
7288
7539
  }
7289
7540
 
7290
7541
  // src/lib/debug-report.ts
7291
- import fs24 from "fs";
7292
- import path20 from "path";
7542
+ import fs25 from "fs";
7543
+ import path21 from "path";
7293
7544
  var DebugReport = class {
7294
7545
  sections = [];
7295
7546
  startTime;
@@ -7358,11 +7609,11 @@ var DebugReport = class {
7358
7609
  lines.push(`| **Total** | **${formatMs(totalMs)}** |`);
7359
7610
  lines.push("");
7360
7611
  }
7361
- const dir = path20.dirname(outputPath);
7362
- if (!fs24.existsSync(dir)) {
7363
- fs24.mkdirSync(dir, { recursive: true });
7612
+ const dir = path21.dirname(outputPath);
7613
+ if (!fs25.existsSync(dir)) {
7614
+ fs25.mkdirSync(dir, { recursive: true });
7364
7615
  }
7365
- fs24.writeFileSync(outputPath, lines.join("\n"));
7616
+ fs25.writeFileSync(outputPath, lines.join("\n"));
7366
7617
  }
7367
7618
  };
7368
7619
  function formatMs(ms) {
@@ -7763,7 +8014,7 @@ import chalk11 from "chalk";
7763
8014
  import ora3 from "ora";
7764
8015
  import select5 from "@inquirer/select";
7765
8016
  import checkbox from "@inquirer/checkbox";
7766
- import fs27 from "fs";
8017
+ import fs28 from "fs";
7767
8018
 
7768
8019
  // src/ai/refine.ts
7769
8020
  async function refineSetup(currentSetup, message, conversationHistory, callbacks) {
@@ -7904,10 +8155,10 @@ init_config();
7904
8155
  init_review();
7905
8156
  function detectAgents(dir) {
7906
8157
  const agents = [];
7907
- if (fs27.existsSync(`${dir}/.claude`)) agents.push("claude");
7908
- if (fs27.existsSync(`${dir}/.cursor`)) agents.push("cursor");
7909
- if (fs27.existsSync(`${dir}/.agents`) || fs27.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
7910
- if (fs27.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
8158
+ if (fs28.existsSync(`${dir}/.claude`)) agents.push("claude");
8159
+ if (fs28.existsSync(`${dir}/.cursor`)) agents.push("cursor");
8160
+ if (fs28.existsSync(`${dir}/.agents`) || fs28.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
8161
+ if (fs28.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
7911
8162
  return agents;
7912
8163
  }
7913
8164
  async function promptAgent(detected) {
@@ -8029,18 +8280,18 @@ async function refineLoop(currentSetup, sessionHistory, summarizeSetup2, printSu
8029
8280
 
8030
8281
  // src/commands/init-display.ts
8031
8282
  import chalk12 from "chalk";
8032
- import fs28 from "fs";
8283
+ import fs29 from "fs";
8033
8284
  function formatWhatChanged(setup) {
8034
8285
  const lines = [];
8035
8286
  const claude = setup.claude;
8036
8287
  const codex = setup.codex;
8037
8288
  const cursor = setup.cursor;
8038
8289
  if (claude?.claudeMd) {
8039
- const action = fs28.existsSync("CLAUDE.md") ? "Updated" : "Created";
8290
+ const action = fs29.existsSync("CLAUDE.md") ? "Updated" : "Created";
8040
8291
  lines.push(`${action} CLAUDE.md`);
8041
8292
  }
8042
8293
  if (codex?.agentsMd) {
8043
- const action = fs28.existsSync("AGENTS.md") ? "Updated" : "Created";
8294
+ const action = fs29.existsSync("AGENTS.md") ? "Updated" : "Created";
8044
8295
  lines.push(`${action} AGENTS.md`);
8045
8296
  }
8046
8297
  const allSkills = [];
@@ -8077,7 +8328,7 @@ function printSetupSummary(setup) {
8077
8328
  };
8078
8329
  if (claude) {
8079
8330
  if (claude.claudeMd) {
8080
- const icon = fs28.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
8331
+ const icon = fs29.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
8081
8332
  const desc = getDescription("CLAUDE.md");
8082
8333
  console.log(` ${icon} ${chalk12.bold("CLAUDE.md")}`);
8083
8334
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -8087,7 +8338,7 @@ function printSetupSummary(setup) {
8087
8338
  if (Array.isArray(skills) && skills.length > 0) {
8088
8339
  for (const skill of skills) {
8089
8340
  const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
8090
- const icon = fs28.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8341
+ const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8091
8342
  const desc = getDescription(skillPath);
8092
8343
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
8093
8344
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -8098,7 +8349,7 @@ function printSetupSummary(setup) {
8098
8349
  const codex = setup.codex;
8099
8350
  if (codex) {
8100
8351
  if (codex.agentsMd) {
8101
- const icon = fs28.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
8352
+ const icon = fs29.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
8102
8353
  const desc = getDescription("AGENTS.md");
8103
8354
  console.log(` ${icon} ${chalk12.bold("AGENTS.md")}`);
8104
8355
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -8108,7 +8359,7 @@ function printSetupSummary(setup) {
8108
8359
  if (Array.isArray(codexSkills) && codexSkills.length > 0) {
8109
8360
  for (const skill of codexSkills) {
8110
8361
  const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
8111
- const icon = fs28.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8362
+ const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8112
8363
  const desc = getDescription(skillPath);
8113
8364
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
8114
8365
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -8118,7 +8369,7 @@ function printSetupSummary(setup) {
8118
8369
  }
8119
8370
  if (cursor) {
8120
8371
  if (cursor.cursorrules) {
8121
- const icon = fs28.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
8372
+ const icon = fs29.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
8122
8373
  const desc = getDescription(".cursorrules");
8123
8374
  console.log(` ${icon} ${chalk12.bold(".cursorrules")}`);
8124
8375
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -8128,7 +8379,7 @@ function printSetupSummary(setup) {
8128
8379
  if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
8129
8380
  for (const skill of cursorSkills) {
8130
8381
  const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
8131
- const icon = fs28.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8382
+ const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8132
8383
  const desc = getDescription(skillPath);
8133
8384
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
8134
8385
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -8139,7 +8390,7 @@ function printSetupSummary(setup) {
8139
8390
  if (Array.isArray(rulesArr) && rulesArr.length > 0) {
8140
8391
  for (const rule of rulesArr) {
8141
8392
  const rulePath = `.cursor/rules/${rule.filename}`;
8142
- const icon = fs28.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
8393
+ const icon = fs29.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
8143
8394
  const desc = getDescription(rulePath);
8144
8395
  console.log(` ${icon} ${chalk12.bold(rulePath)}`);
8145
8396
  if (desc) {
@@ -8186,12 +8437,12 @@ function displayTokenUsage() {
8186
8437
  // src/commands/init-helpers.ts
8187
8438
  init_config();
8188
8439
  import chalk13 from "chalk";
8189
- import fs29 from "fs";
8190
- import path22 from "path";
8440
+ import fs30 from "fs";
8441
+ import path23 from "path";
8191
8442
  function isFirstRun(dir) {
8192
- const caliberDir = path22.join(dir, ".caliber");
8443
+ const caliberDir = path23.join(dir, ".caliber");
8193
8444
  try {
8194
- const stat = fs29.statSync(caliberDir);
8445
+ const stat = fs30.statSync(caliberDir);
8195
8446
  return !stat.isDirectory();
8196
8447
  } catch {
8197
8448
  return true;
@@ -8244,8 +8495,8 @@ function ensurePermissions(fingerprint) {
8244
8495
  const settingsPath = ".claude/settings.json";
8245
8496
  let settings = {};
8246
8497
  try {
8247
- if (fs29.existsSync(settingsPath)) {
8248
- settings = JSON.parse(fs29.readFileSync(settingsPath, "utf-8"));
8498
+ if (fs30.existsSync(settingsPath)) {
8499
+ settings = JSON.parse(fs30.readFileSync(settingsPath, "utf-8"));
8249
8500
  }
8250
8501
  } catch {
8251
8502
  }
@@ -8254,12 +8505,12 @@ function ensurePermissions(fingerprint) {
8254
8505
  if (Array.isArray(allow) && allow.length > 0) return;
8255
8506
  permissions.allow = derivePermissions(fingerprint);
8256
8507
  settings.permissions = permissions;
8257
- if (!fs29.existsSync(".claude")) fs29.mkdirSync(".claude", { recursive: true });
8258
- fs29.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
8508
+ if (!fs30.existsSync(".claude")) fs30.mkdirSync(".claude", { recursive: true });
8509
+ fs30.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
8259
8510
  }
8260
8511
  function writeErrorLog(config, rawOutput, error, stopReason) {
8261
8512
  try {
8262
- const logPath = path22.join(process.cwd(), ".caliber", "error-log.md");
8513
+ const logPath = path23.join(process.cwd(), ".caliber", "error-log.md");
8263
8514
  const lines = [
8264
8515
  `# Generation Error \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
8265
8516
  "",
@@ -8272,8 +8523,8 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
8272
8523
  lines.push("## Error", "```", error, "```", "");
8273
8524
  }
8274
8525
  lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
8275
- fs29.mkdirSync(path22.join(process.cwd(), ".caliber"), { recursive: true });
8276
- fs29.writeFileSync(logPath, lines.join("\n"));
8526
+ fs30.mkdirSync(path23.join(process.cwd(), ".caliber"), { recursive: true });
8527
+ fs30.writeFileSync(logPath, lines.join("\n"));
8277
8528
  console.log(chalk13.dim(`
8278
8529
  Error log written to .caliber/error-log.md`));
8279
8530
  } catch {
@@ -8711,7 +8962,7 @@ async function initCommand(options) {
8711
8962
  const { default: ora9 } = await import("ora");
8712
8963
  const writeSpinner = ora9("Writing config files...").start();
8713
8964
  try {
8714
- if (targetAgent.includes("codex") && !fs30.existsSync("AGENTS.md") && !generatedSetup.codex) {
8965
+ if (targetAgent.includes("codex") && !fs31.existsSync("AGENTS.md") && !generatedSetup.codex) {
8715
8966
  const claude = generatedSetup.claude;
8716
8967
  const cursor = generatedSetup.cursor;
8717
8968
  const agentRefs = [];
@@ -8860,9 +9111,9 @@ ${agentRefs.join(" ")}
8860
9111
  }
8861
9112
  if (report) {
8862
9113
  report.markStep("Finished");
8863
- const reportPath = path23.join(process.cwd(), ".caliber", "debug-report.md");
9114
+ const reportPath = path24.join(process.cwd(), ".caliber", "debug-report.md");
8864
9115
  report.write(reportPath);
8865
- console.log(chalk14.dim(` Debug report written to ${path23.relative(process.cwd(), reportPath)}
9116
+ console.log(chalk14.dim(` Debug report written to ${path24.relative(process.cwd(), reportPath)}
8866
9117
  `));
8867
9118
  }
8868
9119
  }
@@ -8901,7 +9152,7 @@ function undoCommand() {
8901
9152
 
8902
9153
  // src/commands/status.ts
8903
9154
  import chalk16 from "chalk";
8904
- import fs31 from "fs";
9155
+ import fs32 from "fs";
8905
9156
  init_config();
8906
9157
  async function statusCommand(options) {
8907
9158
  const config = loadConfig();
@@ -8928,7 +9179,7 @@ async function statusCommand(options) {
8928
9179
  }
8929
9180
  console.log(` Files managed: ${chalk16.cyan(manifest.entries.length.toString())}`);
8930
9181
  for (const entry of manifest.entries) {
8931
- const exists = fs31.existsSync(entry.path);
9182
+ const exists = fs32.existsSync(entry.path);
8932
9183
  const icon = exists ? chalk16.green("\u2713") : chalk16.red("\u2717");
8933
9184
  console.log(` ${icon} ${entry.path} (${entry.action})`);
8934
9185
  }
@@ -9083,21 +9334,21 @@ async function regenerateCommand(options) {
9083
9334
  }
9084
9335
 
9085
9336
  // src/commands/score.ts
9086
- import fs32 from "fs";
9337
+ import fs33 from "fs";
9087
9338
  import os6 from "os";
9088
- import path24 from "path";
9339
+ import path25 from "path";
9089
9340
  import { execFileSync } from "child_process";
9090
9341
  import chalk18 from "chalk";
9091
9342
  var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".cursorrules", "CALIBER_LEARNINGS.md"];
9092
9343
  var CONFIG_DIRS = [".claude", ".cursor"];
9093
9344
  function scoreBaseRef(ref, target) {
9094
9345
  if (!/^[\w.\-\/~^@{}]+$/.test(ref)) return null;
9095
- const tmpDir = fs32.mkdtempSync(path24.join(os6.tmpdir(), "caliber-compare-"));
9346
+ const tmpDir = fs33.mkdtempSync(path25.join(os6.tmpdir(), "caliber-compare-"));
9096
9347
  try {
9097
9348
  for (const file of CONFIG_FILES) {
9098
9349
  try {
9099
9350
  const content = execFileSync("git", ["show", `${ref}:${file}`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
9100
- fs32.writeFileSync(path24.join(tmpDir, file), content);
9351
+ fs33.writeFileSync(path25.join(tmpDir, file), content);
9101
9352
  } catch {
9102
9353
  }
9103
9354
  }
@@ -9105,10 +9356,10 @@ function scoreBaseRef(ref, target) {
9105
9356
  try {
9106
9357
  const files = execFileSync("git", ["ls-tree", "-r", "--name-only", ref, `${dir}/`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim().split("\n").filter(Boolean);
9107
9358
  for (const file of files) {
9108
- const filePath = path24.join(tmpDir, file);
9109
- fs32.mkdirSync(path24.dirname(filePath), { recursive: true });
9359
+ const filePath = path25.join(tmpDir, file);
9360
+ fs33.mkdirSync(path25.dirname(filePath), { recursive: true });
9110
9361
  const content = execFileSync("git", ["show", `${ref}:${file}`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
9111
- fs32.writeFileSync(filePath, content);
9362
+ fs33.writeFileSync(filePath, content);
9112
9363
  }
9113
9364
  } catch {
9114
9365
  }
@@ -9118,7 +9369,7 @@ function scoreBaseRef(ref, target) {
9118
9369
  } catch {
9119
9370
  return null;
9120
9371
  } finally {
9121
- fs32.rmSync(tmpDir, { recursive: true, force: true });
9372
+ fs33.rmSync(tmpDir, { recursive: true, force: true });
9122
9373
  }
9123
9374
  }
9124
9375
  async function scoreCommand(options) {
@@ -9178,8 +9429,8 @@ async function scoreCommand(options) {
9178
9429
  }
9179
9430
 
9180
9431
  // src/commands/refresh.ts
9181
- import fs36 from "fs";
9182
- import path28 from "path";
9432
+ import fs37 from "fs";
9433
+ import path29 from "path";
9183
9434
  import chalk19 from "chalk";
9184
9435
  import ora6 from "ora";
9185
9436
 
@@ -9257,48 +9508,48 @@ function collectDiff(lastSha) {
9257
9508
  }
9258
9509
 
9259
9510
  // src/writers/refresh.ts
9260
- import fs33 from "fs";
9261
- import path25 from "path";
9511
+ import fs34 from "fs";
9512
+ import path26 from "path";
9262
9513
  function writeRefreshDocs(docs) {
9263
9514
  const written = [];
9264
9515
  if (docs.claudeMd) {
9265
- fs33.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(docs.claudeMd)));
9516
+ fs34.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(docs.claudeMd)));
9266
9517
  written.push("CLAUDE.md");
9267
9518
  }
9268
9519
  if (docs.readmeMd) {
9269
- fs33.writeFileSync("README.md", docs.readmeMd);
9520
+ fs34.writeFileSync("README.md", docs.readmeMd);
9270
9521
  written.push("README.md");
9271
9522
  }
9272
9523
  if (docs.cursorrules) {
9273
- fs33.writeFileSync(".cursorrules", docs.cursorrules);
9524
+ fs34.writeFileSync(".cursorrules", docs.cursorrules);
9274
9525
  written.push(".cursorrules");
9275
9526
  }
9276
9527
  if (docs.cursorRules) {
9277
- const rulesDir = path25.join(".cursor", "rules");
9278
- if (!fs33.existsSync(rulesDir)) fs33.mkdirSync(rulesDir, { recursive: true });
9528
+ const rulesDir = path26.join(".cursor", "rules");
9529
+ if (!fs34.existsSync(rulesDir)) fs34.mkdirSync(rulesDir, { recursive: true });
9279
9530
  for (const rule of docs.cursorRules) {
9280
- fs33.writeFileSync(path25.join(rulesDir, rule.filename), rule.content);
9531
+ fs34.writeFileSync(path26.join(rulesDir, rule.filename), rule.content);
9281
9532
  written.push(`.cursor/rules/${rule.filename}`);
9282
9533
  }
9283
9534
  }
9284
9535
  if (docs.claudeSkills) {
9285
- const skillsDir = path25.join(".claude", "skills");
9286
- if (!fs33.existsSync(skillsDir)) fs33.mkdirSync(skillsDir, { recursive: true });
9536
+ const skillsDir = path26.join(".claude", "skills");
9537
+ if (!fs34.existsSync(skillsDir)) fs34.mkdirSync(skillsDir, { recursive: true });
9287
9538
  for (const skill of docs.claudeSkills) {
9288
- fs33.writeFileSync(path25.join(skillsDir, skill.filename), skill.content);
9539
+ fs34.writeFileSync(path26.join(skillsDir, skill.filename), skill.content);
9289
9540
  written.push(`.claude/skills/${skill.filename}`);
9290
9541
  }
9291
9542
  }
9292
9543
  if (docs.copilotInstructions) {
9293
- fs33.mkdirSync(".github", { recursive: true });
9294
- fs33.writeFileSync(path25.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(docs.copilotInstructions)));
9544
+ fs34.mkdirSync(".github", { recursive: true });
9545
+ fs34.writeFileSync(path26.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(docs.copilotInstructions)));
9295
9546
  written.push(".github/copilot-instructions.md");
9296
9547
  }
9297
9548
  if (docs.copilotInstructionFiles) {
9298
- const instructionsDir = path25.join(".github", "instructions");
9299
- fs33.mkdirSync(instructionsDir, { recursive: true });
9549
+ const instructionsDir = path26.join(".github", "instructions");
9550
+ fs34.mkdirSync(instructionsDir, { recursive: true });
9300
9551
  for (const file of docs.copilotInstructionFiles) {
9301
- fs33.writeFileSync(path25.join(instructionsDir, file.filename), file.content);
9552
+ fs34.writeFileSync(path26.join(instructionsDir, file.filename), file.content);
9302
9553
  written.push(`.github/instructions/${file.filename}`);
9303
9554
  }
9304
9555
  }
@@ -9378,8 +9629,8 @@ Changed files: ${diff.changedFiles.join(", ")}`);
9378
9629
  }
9379
9630
 
9380
9631
  // src/learner/writer.ts
9381
- import fs34 from "fs";
9382
- import path26 from "path";
9632
+ import fs35 from "fs";
9633
+ import path27 from "path";
9383
9634
 
9384
9635
  // src/learner/utils.ts
9385
9636
  var TYPE_PREFIX_RE = /^\*\*\[[^\]]+\]\*\*\s*/;
@@ -9497,20 +9748,20 @@ function deduplicateLearnedItems(existing, incoming) {
9497
9748
  }
9498
9749
  function writeLearnedSectionTo(filePath, header, existing, incoming, mode) {
9499
9750
  const { merged, newCount, newItems } = deduplicateLearnedItems(existing, incoming);
9500
- fs34.writeFileSync(filePath, header + merged + "\n");
9501
- if (mode) fs34.chmodSync(filePath, mode);
9751
+ fs35.writeFileSync(filePath, header + merged + "\n");
9752
+ if (mode) fs35.chmodSync(filePath, mode);
9502
9753
  return { newCount, newItems };
9503
9754
  }
9504
9755
  function writeLearnedSection(content) {
9505
9756
  return writeLearnedSectionTo(LEARNINGS_FILE, LEARNINGS_HEADER, readLearnedSection(), content);
9506
9757
  }
9507
9758
  function writeLearnedSkill(skill) {
9508
- const skillDir = path26.join(".claude", "skills", skill.name);
9509
- if (!fs34.existsSync(skillDir)) fs34.mkdirSync(skillDir, { recursive: true });
9510
- const skillPath = path26.join(skillDir, "SKILL.md");
9511
- if (!skill.isNew && fs34.existsSync(skillPath)) {
9512
- const existing = fs34.readFileSync(skillPath, "utf-8");
9513
- fs34.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
9759
+ const skillDir = path27.join(".claude", "skills", skill.name);
9760
+ if (!fs35.existsSync(skillDir)) fs35.mkdirSync(skillDir, { recursive: true });
9761
+ const skillPath = path27.join(skillDir, "SKILL.md");
9762
+ if (!skill.isNew && fs35.existsSync(skillPath)) {
9763
+ const existing = fs35.readFileSync(skillPath, "utf-8");
9764
+ fs35.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
9514
9765
  } else {
9515
9766
  const frontmatter = [
9516
9767
  "---",
@@ -9519,47 +9770,56 @@ function writeLearnedSkill(skill) {
9519
9770
  "---",
9520
9771
  ""
9521
9772
  ].join("\n");
9522
- fs34.writeFileSync(skillPath, frontmatter + skill.content);
9773
+ fs35.writeFileSync(skillPath, frontmatter + skill.content);
9523
9774
  }
9524
9775
  return skillPath;
9525
9776
  }
9526
9777
  function writePersonalLearnedSection(content) {
9527
- if (!fs34.existsSync(AUTH_DIR)) fs34.mkdirSync(AUTH_DIR, { recursive: true });
9778
+ if (!fs35.existsSync(AUTH_DIR)) fs35.mkdirSync(AUTH_DIR, { recursive: true });
9528
9779
  return writeLearnedSectionTo(PERSONAL_LEARNINGS_FILE, PERSONAL_LEARNINGS_HEADER, readPersonalLearnings(), content, 384);
9529
9780
  }
9781
+ function addLearning(bullet, scope = "project") {
9782
+ const formatted = bullet.startsWith("- ") ? bullet : `- ${bullet}`;
9783
+ if (scope === "personal") {
9784
+ const result2 = writePersonalLearnedSection(formatted);
9785
+ return { file: PERSONAL_LEARNINGS_FILE, added: result2.newCount > 0 };
9786
+ }
9787
+ const result = writeLearnedSection(formatted);
9788
+ return { file: LEARNINGS_FILE, added: result.newCount > 0 };
9789
+ }
9530
9790
  function readPersonalLearnings() {
9531
- if (!fs34.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
9532
- const content = fs34.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
9791
+ if (!fs35.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
9792
+ const content = fs35.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
9533
9793
  const bullets = content.split("\n").filter((l) => l.startsWith("- ")).join("\n");
9534
9794
  return bullets || null;
9535
9795
  }
9536
9796
  function readLearnedSection() {
9537
- if (fs34.existsSync(LEARNINGS_FILE)) {
9538
- const content2 = fs34.readFileSync(LEARNINGS_FILE, "utf-8");
9797
+ if (fs35.existsSync(LEARNINGS_FILE)) {
9798
+ const content2 = fs35.readFileSync(LEARNINGS_FILE, "utf-8");
9539
9799
  const bullets = content2.split("\n").filter((l) => l.startsWith("- ")).join("\n");
9540
9800
  return bullets || null;
9541
9801
  }
9542
9802
  const claudeMdPath = "CLAUDE.md";
9543
- if (!fs34.existsSync(claudeMdPath)) return null;
9544
- const content = fs34.readFileSync(claudeMdPath, "utf-8");
9803
+ if (!fs35.existsSync(claudeMdPath)) return null;
9804
+ const content = fs35.readFileSync(claudeMdPath, "utf-8");
9545
9805
  const startIdx = content.indexOf(LEARNED_START);
9546
9806
  const endIdx = content.indexOf(LEARNED_END);
9547
9807
  if (startIdx === -1 || endIdx === -1) return null;
9548
9808
  return content.slice(startIdx + LEARNED_START.length, endIdx).trim() || null;
9549
9809
  }
9550
9810
  function migrateInlineLearnings() {
9551
- if (fs34.existsSync(LEARNINGS_FILE)) return false;
9811
+ if (fs35.existsSync(LEARNINGS_FILE)) return false;
9552
9812
  const claudeMdPath = "CLAUDE.md";
9553
- if (!fs34.existsSync(claudeMdPath)) return false;
9554
- const content = fs34.readFileSync(claudeMdPath, "utf-8");
9813
+ if (!fs35.existsSync(claudeMdPath)) return false;
9814
+ const content = fs35.readFileSync(claudeMdPath, "utf-8");
9555
9815
  const startIdx = content.indexOf(LEARNED_START);
9556
9816
  const endIdx = content.indexOf(LEARNED_END);
9557
9817
  if (startIdx === -1 || endIdx === -1) return false;
9558
9818
  const section = content.slice(startIdx + LEARNED_START.length, endIdx).trim();
9559
9819
  if (!section) return false;
9560
- fs34.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
9820
+ fs35.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
9561
9821
  const cleaned = content.slice(0, startIdx) + content.slice(endIdx + LEARNED_END.length);
9562
- fs34.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
9822
+ fs35.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
9563
9823
  return true;
9564
9824
  }
9565
9825
 
@@ -9571,11 +9831,11 @@ function log2(quiet, ...args) {
9571
9831
  function discoverGitRepos(parentDir) {
9572
9832
  const repos = [];
9573
9833
  try {
9574
- const entries = fs36.readdirSync(parentDir, { withFileTypes: true });
9834
+ const entries = fs37.readdirSync(parentDir, { withFileTypes: true });
9575
9835
  for (const entry of entries) {
9576
9836
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
9577
- const childPath = path28.join(parentDir, entry.name);
9578
- if (fs36.existsSync(path28.join(childPath, ".git"))) {
9837
+ const childPath = path29.join(parentDir, entry.name);
9838
+ if (fs37.existsSync(path29.join(childPath, ".git"))) {
9579
9839
  repos.push(childPath);
9580
9840
  }
9581
9841
  }
@@ -9649,6 +9909,10 @@ async function refreshSingleRepo(repoDir, options) {
9649
9909
  log2(quiet, chalk19.dim(`
9650
9910
  ${response.changesSummary}`));
9651
9911
  }
9912
+ const builtinWritten = ensureBuiltinSkills();
9913
+ for (const file of builtinWritten) {
9914
+ log2(quiet, ` ${chalk19.green("\u2713")} ${file} ${chalk19.dim("(built-in)")}`);
9915
+ }
9652
9916
  if (currentSha) {
9653
9917
  writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
9654
9918
  }
@@ -9681,7 +9945,7 @@ async function refreshCommand(options) {
9681
9945
  `));
9682
9946
  const originalDir = process.cwd();
9683
9947
  for (const repo of repos) {
9684
- const repoName = path28.basename(repo);
9948
+ const repoName = path29.basename(repo);
9685
9949
  try {
9686
9950
  process.chdir(repo);
9687
9951
  await refreshSingleRepo(repo, { ...options, label: repoName });
@@ -9702,31 +9966,31 @@ async function refreshCommand(options) {
9702
9966
 
9703
9967
  // src/commands/hooks.ts
9704
9968
  import chalk20 from "chalk";
9705
- import fs38 from "fs";
9969
+ import fs39 from "fs";
9706
9970
 
9707
9971
  // src/lib/hooks.ts
9708
9972
  init_resolve_caliber();
9709
- import fs37 from "fs";
9710
- import path29 from "path";
9973
+ import fs38 from "fs";
9974
+ import path30 from "path";
9711
9975
  import { execSync as execSync14 } from "child_process";
9712
- var SETTINGS_PATH2 = path29.join(".claude", "settings.json");
9976
+ var SETTINGS_PATH2 = path30.join(".claude", "settings.json");
9713
9977
  var REFRESH_TAIL = "refresh --quiet";
9714
9978
  var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
9715
9979
  function getHookCommand() {
9716
9980
  return `${resolveCaliber()} ${REFRESH_TAIL}`;
9717
9981
  }
9718
9982
  function readSettings2() {
9719
- if (!fs37.existsSync(SETTINGS_PATH2)) return {};
9983
+ if (!fs38.existsSync(SETTINGS_PATH2)) return {};
9720
9984
  try {
9721
- return JSON.parse(fs37.readFileSync(SETTINGS_PATH2, "utf-8"));
9985
+ return JSON.parse(fs38.readFileSync(SETTINGS_PATH2, "utf-8"));
9722
9986
  } catch {
9723
9987
  return {};
9724
9988
  }
9725
9989
  }
9726
9990
  function writeSettings2(settings) {
9727
- const dir = path29.dirname(SETTINGS_PATH2);
9728
- if (!fs37.existsSync(dir)) fs37.mkdirSync(dir, { recursive: true });
9729
- fs37.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
9991
+ const dir = path30.dirname(SETTINGS_PATH2);
9992
+ if (!fs38.existsSync(dir)) fs38.mkdirSync(dir, { recursive: true });
9993
+ fs38.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
9730
9994
  }
9731
9995
  function findHookIndex(sessionEnd) {
9732
9996
  return sessionEnd.findIndex(
@@ -9789,19 +10053,19 @@ ${PRECOMMIT_END}`;
9789
10053
  function getGitHooksDir() {
9790
10054
  try {
9791
10055
  const gitDir = execSync14("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
9792
- return path29.join(gitDir, "hooks");
10056
+ return path30.join(gitDir, "hooks");
9793
10057
  } catch {
9794
10058
  return null;
9795
10059
  }
9796
10060
  }
9797
10061
  function getPreCommitPath() {
9798
10062
  const hooksDir = getGitHooksDir();
9799
- return hooksDir ? path29.join(hooksDir, "pre-commit") : null;
10063
+ return hooksDir ? path30.join(hooksDir, "pre-commit") : null;
9800
10064
  }
9801
10065
  function isPreCommitHookInstalled() {
9802
10066
  const hookPath = getPreCommitPath();
9803
- if (!hookPath || !fs37.existsSync(hookPath)) return false;
9804
- const content = fs37.readFileSync(hookPath, "utf-8");
10067
+ if (!hookPath || !fs38.existsSync(hookPath)) return false;
10068
+ const content = fs38.readFileSync(hookPath, "utf-8");
9805
10069
  return content.includes(PRECOMMIT_START);
9806
10070
  }
9807
10071
  function installPreCommitHook() {
@@ -9810,35 +10074,35 @@ function installPreCommitHook() {
9810
10074
  }
9811
10075
  const hookPath = getPreCommitPath();
9812
10076
  if (!hookPath) return { installed: false, alreadyInstalled: false };
9813
- const hooksDir = path29.dirname(hookPath);
9814
- if (!fs37.existsSync(hooksDir)) fs37.mkdirSync(hooksDir, { recursive: true });
10077
+ const hooksDir = path30.dirname(hookPath);
10078
+ if (!fs38.existsSync(hooksDir)) fs38.mkdirSync(hooksDir, { recursive: true });
9815
10079
  let content = "";
9816
- if (fs37.existsSync(hookPath)) {
9817
- content = fs37.readFileSync(hookPath, "utf-8");
10080
+ if (fs38.existsSync(hookPath)) {
10081
+ content = fs38.readFileSync(hookPath, "utf-8");
9818
10082
  if (!content.endsWith("\n")) content += "\n";
9819
10083
  content += "\n" + getPrecommitBlock() + "\n";
9820
10084
  } else {
9821
10085
  content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
9822
10086
  }
9823
- fs37.writeFileSync(hookPath, content);
9824
- fs37.chmodSync(hookPath, 493);
10087
+ fs38.writeFileSync(hookPath, content);
10088
+ fs38.chmodSync(hookPath, 493);
9825
10089
  return { installed: true, alreadyInstalled: false };
9826
10090
  }
9827
10091
  function removePreCommitHook() {
9828
10092
  const hookPath = getPreCommitPath();
9829
- if (!hookPath || !fs37.existsSync(hookPath)) {
10093
+ if (!hookPath || !fs38.existsSync(hookPath)) {
9830
10094
  return { removed: false, notFound: true };
9831
10095
  }
9832
- let content = fs37.readFileSync(hookPath, "utf-8");
10096
+ let content = fs38.readFileSync(hookPath, "utf-8");
9833
10097
  if (!content.includes(PRECOMMIT_START)) {
9834
10098
  return { removed: false, notFound: true };
9835
10099
  }
9836
10100
  const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
9837
10101
  content = content.replace(regex, "\n");
9838
10102
  if (content.trim() === "#!/bin/sh" || content.trim() === "") {
9839
- fs37.unlinkSync(hookPath);
10103
+ fs38.unlinkSync(hookPath);
9840
10104
  } else {
9841
- fs37.writeFileSync(hookPath, content);
10105
+ fs38.writeFileSync(hookPath, content);
9842
10106
  }
9843
10107
  return { removed: true, notFound: false };
9844
10108
  }
@@ -9887,11 +10151,11 @@ async function hooksCommand(options) {
9887
10151
  console.log(chalk20.green(" \u2713") + ` ${hook.label} enabled`);
9888
10152
  }
9889
10153
  }
9890
- if (fs38.existsSync(".claude")) {
10154
+ if (fs39.existsSync(".claude")) {
9891
10155
  const r = installLearningHooks();
9892
10156
  if (r.installed) console.log(chalk20.green(" \u2713") + " Claude Code learning hooks enabled");
9893
10157
  }
9894
- if (fs38.existsSync(".cursor")) {
10158
+ if (fs39.existsSync(".cursor")) {
9895
10159
  const r = installCursorLearningHooks();
9896
10160
  if (r.installed) console.log(chalk20.green(" \u2713") + " Cursor learning hooks enabled");
9897
10161
  }
@@ -10059,8 +10323,8 @@ async function configCommand() {
10059
10323
  }
10060
10324
 
10061
10325
  // src/commands/learn.ts
10062
- import fs42 from "fs";
10063
- import path33 from "path";
10326
+ import fs43 from "fs";
10327
+ import path34 from "path";
10064
10328
  import chalk23 from "chalk";
10065
10329
 
10066
10330
  // src/learner/stdin.ts
@@ -10092,8 +10356,8 @@ function readStdin() {
10092
10356
 
10093
10357
  // src/learner/storage.ts
10094
10358
  init_constants();
10095
- import fs39 from "fs";
10096
- import path30 from "path";
10359
+ import fs40 from "fs";
10360
+ import path31 from "path";
10097
10361
  var MAX_RESPONSE_LENGTH = 2e3;
10098
10362
  var DEFAULT_STATE = {
10099
10363
  sessionId: null,
@@ -10102,15 +10366,15 @@ var DEFAULT_STATE = {
10102
10366
  lastAnalysisEventCount: 0
10103
10367
  };
10104
10368
  function ensureLearningDir() {
10105
- if (!fs39.existsSync(LEARNING_DIR)) {
10106
- fs39.mkdirSync(LEARNING_DIR, { recursive: true });
10369
+ if (!fs40.existsSync(LEARNING_DIR)) {
10370
+ fs40.mkdirSync(LEARNING_DIR, { recursive: true });
10107
10371
  }
10108
10372
  }
10109
10373
  function sessionFilePath() {
10110
- return path30.join(LEARNING_DIR, LEARNING_SESSION_FILE);
10374
+ return path31.join(LEARNING_DIR, LEARNING_SESSION_FILE);
10111
10375
  }
10112
10376
  function stateFilePath() {
10113
- return path30.join(LEARNING_DIR, LEARNING_STATE_FILE);
10377
+ return path31.join(LEARNING_DIR, LEARNING_STATE_FILE);
10114
10378
  }
10115
10379
  function truncateResponse(response) {
10116
10380
  const str = JSON.stringify(response);
@@ -10120,10 +10384,10 @@ function truncateResponse(response) {
10120
10384
  function trimSessionFileIfNeeded(filePath) {
10121
10385
  const state = readState2();
10122
10386
  if (state.eventCount + 1 > LEARNING_MAX_EVENTS) {
10123
- const lines = fs39.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
10387
+ const lines = fs40.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
10124
10388
  if (lines.length > LEARNING_MAX_EVENTS) {
10125
10389
  const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
10126
- fs39.writeFileSync(filePath, kept.join("\n") + "\n");
10390
+ fs40.writeFileSync(filePath, kept.join("\n") + "\n");
10127
10391
  }
10128
10392
  }
10129
10393
  }
@@ -10131,19 +10395,19 @@ function appendEvent(event) {
10131
10395
  ensureLearningDir();
10132
10396
  const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
10133
10397
  const filePath = sessionFilePath();
10134
- fs39.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
10398
+ fs40.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
10135
10399
  trimSessionFileIfNeeded(filePath);
10136
10400
  }
10137
10401
  function appendPromptEvent(event) {
10138
10402
  ensureLearningDir();
10139
10403
  const filePath = sessionFilePath();
10140
- fs39.appendFileSync(filePath, JSON.stringify(event) + "\n");
10404
+ fs40.appendFileSync(filePath, JSON.stringify(event) + "\n");
10141
10405
  trimSessionFileIfNeeded(filePath);
10142
10406
  }
10143
10407
  function readAllEvents() {
10144
10408
  const filePath = sessionFilePath();
10145
- if (!fs39.existsSync(filePath)) return [];
10146
- const lines = fs39.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
10409
+ if (!fs40.existsSync(filePath)) return [];
10410
+ const lines = fs40.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
10147
10411
  const events = [];
10148
10412
  for (const line of lines) {
10149
10413
  try {
@@ -10155,26 +10419,26 @@ function readAllEvents() {
10155
10419
  }
10156
10420
  function getEventCount() {
10157
10421
  const filePath = sessionFilePath();
10158
- if (!fs39.existsSync(filePath)) return 0;
10159
- const content = fs39.readFileSync(filePath, "utf-8");
10422
+ if (!fs40.existsSync(filePath)) return 0;
10423
+ const content = fs40.readFileSync(filePath, "utf-8");
10160
10424
  return content.split("\n").filter(Boolean).length;
10161
10425
  }
10162
10426
  function clearSession() {
10163
10427
  const filePath = sessionFilePath();
10164
- if (fs39.existsSync(filePath)) fs39.unlinkSync(filePath);
10428
+ if (fs40.existsSync(filePath)) fs40.unlinkSync(filePath);
10165
10429
  }
10166
10430
  function readState2() {
10167
10431
  const filePath = stateFilePath();
10168
- if (!fs39.existsSync(filePath)) return { ...DEFAULT_STATE };
10432
+ if (!fs40.existsSync(filePath)) return { ...DEFAULT_STATE };
10169
10433
  try {
10170
- return JSON.parse(fs39.readFileSync(filePath, "utf-8"));
10434
+ return JSON.parse(fs40.readFileSync(filePath, "utf-8"));
10171
10435
  } catch {
10172
10436
  return { ...DEFAULT_STATE };
10173
10437
  }
10174
10438
  }
10175
10439
  function writeState2(state) {
10176
10440
  ensureLearningDir();
10177
- fs39.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
10441
+ fs40.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
10178
10442
  }
10179
10443
  function resetState() {
10180
10444
  writeState2({ ...DEFAULT_STATE });
@@ -10182,16 +10446,16 @@ function resetState() {
10182
10446
  var LOCK_FILE2 = "finalize.lock";
10183
10447
  var LOCK_STALE_MS = 5 * 60 * 1e3;
10184
10448
  function lockFilePath() {
10185
- return path30.join(LEARNING_DIR, LOCK_FILE2);
10449
+ return path31.join(LEARNING_DIR, LOCK_FILE2);
10186
10450
  }
10187
10451
  function acquireFinalizeLock() {
10188
10452
  ensureLearningDir();
10189
10453
  const lockPath = lockFilePath();
10190
- if (fs39.existsSync(lockPath)) {
10454
+ if (fs40.existsSync(lockPath)) {
10191
10455
  try {
10192
- const stat = fs39.statSync(lockPath);
10456
+ const stat = fs40.statSync(lockPath);
10193
10457
  if (Date.now() - stat.mtimeMs < LOCK_STALE_MS) {
10194
- const pid = parseInt(fs39.readFileSync(lockPath, "utf-8").trim(), 10);
10458
+ const pid = parseInt(fs40.readFileSync(lockPath, "utf-8").trim(), 10);
10195
10459
  if (!isNaN(pid) && isProcessAlive(pid)) {
10196
10460
  return false;
10197
10461
  }
@@ -10199,12 +10463,12 @@ function acquireFinalizeLock() {
10199
10463
  } catch {
10200
10464
  }
10201
10465
  try {
10202
- fs39.unlinkSync(lockPath);
10466
+ fs40.unlinkSync(lockPath);
10203
10467
  } catch {
10204
10468
  }
10205
10469
  }
10206
10470
  try {
10207
- fs39.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
10471
+ fs40.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
10208
10472
  return true;
10209
10473
  } catch {
10210
10474
  return false;
@@ -10221,7 +10485,7 @@ function isProcessAlive(pid) {
10221
10485
  function releaseFinalizeLock() {
10222
10486
  const lockPath = lockFilePath();
10223
10487
  try {
10224
- if (fs39.existsSync(lockPath)) fs39.unlinkSync(lockPath);
10488
+ if (fs40.existsSync(lockPath)) fs40.unlinkSync(lockPath);
10225
10489
  } catch {
10226
10490
  }
10227
10491
  }
@@ -10267,22 +10531,22 @@ function sanitizeSecrets(text) {
10267
10531
 
10268
10532
  // src/lib/notifications.ts
10269
10533
  init_constants();
10270
- import fs40 from "fs";
10271
- import path31 from "path";
10534
+ import fs41 from "fs";
10535
+ import path32 from "path";
10272
10536
  import chalk22 from "chalk";
10273
- var NOTIFICATION_FILE = path31.join(LEARNING_DIR, "last-finalize-summary.json");
10537
+ var NOTIFICATION_FILE = path32.join(LEARNING_DIR, "last-finalize-summary.json");
10274
10538
  function writeFinalizeSummary(summary) {
10275
10539
  try {
10276
10540
  ensureLearningDir();
10277
- fs40.writeFileSync(NOTIFICATION_FILE, JSON.stringify(summary, null, 2));
10541
+ fs41.writeFileSync(NOTIFICATION_FILE, JSON.stringify(summary, null, 2));
10278
10542
  } catch {
10279
10543
  }
10280
10544
  }
10281
10545
  function checkPendingNotifications() {
10282
10546
  try {
10283
- if (!fs40.existsSync(NOTIFICATION_FILE)) return;
10284
- const raw = fs40.readFileSync(NOTIFICATION_FILE, "utf-8");
10285
- fs40.unlinkSync(NOTIFICATION_FILE);
10547
+ if (!fs41.existsSync(NOTIFICATION_FILE)) return;
10548
+ const raw = fs41.readFileSync(NOTIFICATION_FILE, "utf-8");
10549
+ fs41.unlinkSync(NOTIFICATION_FILE);
10286
10550
  const summary = JSON.parse(raw);
10287
10551
  if (!summary.newItemCount || summary.newItemCount === 0) return;
10288
10552
  const wasteLabel = summary.wasteTokens > 0 ? ` (~${summary.wasteTokens.toLocaleString()} wasted tokens captured)` : "";
@@ -10298,7 +10562,7 @@ function checkPendingNotifications() {
10298
10562
  console.log("");
10299
10563
  } catch {
10300
10564
  try {
10301
- fs40.unlinkSync(NOTIFICATION_FILE);
10565
+ fs41.unlinkSync(NOTIFICATION_FILE);
10302
10566
  } catch {
10303
10567
  }
10304
10568
  }
@@ -10436,8 +10700,8 @@ init_config();
10436
10700
 
10437
10701
  // src/learner/roi.ts
10438
10702
  init_constants();
10439
- import fs41 from "fs";
10440
- import path32 from "path";
10703
+ import fs42 from "fs";
10704
+ import path33 from "path";
10441
10705
  var DEFAULT_TOTALS = {
10442
10706
  totalWasteTokens: 0,
10443
10707
  totalWasteSeconds: 0,
@@ -10451,19 +10715,19 @@ var DEFAULT_TOTALS = {
10451
10715
  lastSessionTimestamp: ""
10452
10716
  };
10453
10717
  function roiFilePath() {
10454
- return path32.join(LEARNING_DIR, LEARNING_ROI_FILE);
10718
+ return path33.join(LEARNING_DIR, LEARNING_ROI_FILE);
10455
10719
  }
10456
10720
  function readROIStats() {
10457
10721
  const filePath = roiFilePath();
10458
- if (!fs41.existsSync(filePath)) {
10722
+ if (!fs42.existsSync(filePath)) {
10459
10723
  return { learnings: [], sessions: [], totals: { ...DEFAULT_TOTALS } };
10460
10724
  }
10461
10725
  try {
10462
- return JSON.parse(fs41.readFileSync(filePath, "utf-8"));
10726
+ return JSON.parse(fs42.readFileSync(filePath, "utf-8"));
10463
10727
  } catch {
10464
10728
  try {
10465
10729
  const corruptPath = filePath + ".corrupt";
10466
- fs41.renameSync(filePath, corruptPath);
10730
+ fs42.renameSync(filePath, corruptPath);
10467
10731
  console.error(`caliber: roi-stats.json was corrupt \u2014 renamed to ${corruptPath}`);
10468
10732
  } catch {
10469
10733
  }
@@ -10472,7 +10736,7 @@ function readROIStats() {
10472
10736
  }
10473
10737
  function writeROIStats(stats) {
10474
10738
  ensureLearningDir();
10475
- fs41.writeFileSync(roiFilePath(), JSON.stringify(stats, null, 2));
10739
+ fs42.writeFileSync(roiFilePath(), JSON.stringify(stats, null, 2));
10476
10740
  }
10477
10741
  function recalculateTotals(stats) {
10478
10742
  const totals = stats.totals;
@@ -10679,9 +10943,9 @@ var AUTO_SETTLE_MS = 200;
10679
10943
  var INCREMENTAL_INTERVAL = 50;
10680
10944
  function writeFinalizeError(message) {
10681
10945
  try {
10682
- const errorPath = path33.join(LEARNING_DIR, LEARNING_LAST_ERROR_FILE);
10683
- if (!fs42.existsSync(LEARNING_DIR)) fs42.mkdirSync(LEARNING_DIR, { recursive: true });
10684
- fs42.writeFileSync(errorPath, JSON.stringify({
10946
+ const errorPath = path34.join(LEARNING_DIR, LEARNING_LAST_ERROR_FILE);
10947
+ if (!fs43.existsSync(LEARNING_DIR)) fs43.mkdirSync(LEARNING_DIR, { recursive: true });
10948
+ fs43.writeFileSync(errorPath, JSON.stringify({
10685
10949
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
10686
10950
  error: message,
10687
10951
  pid: process.pid
@@ -10691,9 +10955,9 @@ function writeFinalizeError(message) {
10691
10955
  }
10692
10956
  function readFinalizeError() {
10693
10957
  try {
10694
- const errorPath = path33.join(LEARNING_DIR, LEARNING_LAST_ERROR_FILE);
10695
- if (!fs42.existsSync(errorPath)) return null;
10696
- return JSON.parse(fs42.readFileSync(errorPath, "utf-8"));
10958
+ const errorPath = path34.join(LEARNING_DIR, LEARNING_LAST_ERROR_FILE);
10959
+ if (!fs43.existsSync(errorPath)) return null;
10960
+ return JSON.parse(fs43.readFileSync(errorPath, "utf-8"));
10697
10961
  } catch {
10698
10962
  return null;
10699
10963
  }
@@ -10740,14 +11004,14 @@ async function learnObserveCommand(options) {
10740
11004
  const { resolveCaliber: resolveCaliber2 } = await Promise.resolve().then(() => (init_resolve_caliber(), resolve_caliber_exports));
10741
11005
  const bin = resolveCaliber2();
10742
11006
  const { spawn: spawn4 } = await import("child_process");
10743
- const logPath = path33.join(LEARNING_DIR, LEARNING_FINALIZE_LOG);
10744
- if (!fs42.existsSync(LEARNING_DIR)) fs42.mkdirSync(LEARNING_DIR, { recursive: true });
10745
- const logFd = fs42.openSync(logPath, "a");
11007
+ const logPath = path34.join(LEARNING_DIR, LEARNING_FINALIZE_LOG);
11008
+ if (!fs43.existsSync(LEARNING_DIR)) fs43.mkdirSync(LEARNING_DIR, { recursive: true });
11009
+ const logFd = fs43.openSync(logPath, "a");
10746
11010
  spawn4(bin, ["learn", "finalize", "--auto", "--incremental"], {
10747
11011
  detached: true,
10748
11012
  stdio: ["ignore", logFd, logFd]
10749
11013
  }).unref();
10750
- fs42.closeSync(logFd);
11014
+ fs43.closeSync(logFd);
10751
11015
  } catch {
10752
11016
  }
10753
11017
  }
@@ -10954,7 +11218,7 @@ async function learnFinalizeCommand(options) {
10954
11218
  }
10955
11219
  async function learnInstallCommand() {
10956
11220
  let anyInstalled = false;
10957
- if (fs42.existsSync(".claude")) {
11221
+ if (fs43.existsSync(".claude")) {
10958
11222
  const r = installLearningHooks();
10959
11223
  if (r.installed) {
10960
11224
  console.log(chalk23.green("\u2713") + " Claude Code learning hooks installed");
@@ -10963,7 +11227,7 @@ async function learnInstallCommand() {
10963
11227
  console.log(chalk23.dim(" Claude Code hooks already installed"));
10964
11228
  }
10965
11229
  }
10966
- if (fs42.existsSync(".cursor")) {
11230
+ if (fs43.existsSync(".cursor")) {
10967
11231
  const r = installCursorLearningHooks();
10968
11232
  if (r.installed) {
10969
11233
  console.log(chalk23.green("\u2713") + " Cursor learning hooks installed");
@@ -10972,7 +11236,7 @@ async function learnInstallCommand() {
10972
11236
  console.log(chalk23.dim(" Cursor hooks already installed"));
10973
11237
  }
10974
11238
  }
10975
- if (!fs42.existsSync(".claude") && !fs42.existsSync(".cursor")) {
11239
+ if (!fs43.existsSync(".claude") && !fs43.existsSync(".cursor")) {
10976
11240
  console.log(chalk23.yellow("No .claude/ or .cursor/ directory found."));
10977
11241
  console.log(chalk23.dim(" Run `caliber init` first, or create the directory manually."));
10978
11242
  return;
@@ -11030,8 +11294,8 @@ async function learnStatusCommand() {
11030
11294
  if (lastError) {
11031
11295
  console.log(`Last error: ${chalk23.red(lastError.error)}`);
11032
11296
  console.log(chalk23.dim(` at ${lastError.timestamp}`));
11033
- const logPath = path33.join(LEARNING_DIR, LEARNING_FINALIZE_LOG);
11034
- if (fs42.existsSync(logPath)) {
11297
+ const logPath = path34.join(LEARNING_DIR, LEARNING_FINALIZE_LOG);
11298
+ if (fs43.existsSync(logPath)) {
11035
11299
  console.log(chalk23.dim(` Full log: ${logPath}`));
11036
11300
  }
11037
11301
  }
@@ -11111,11 +11375,11 @@ async function learnDeleteCommand(indexStr) {
11111
11375
  }
11112
11376
  const item = items[targetIdx];
11113
11377
  const filePath = item.source === "personal" ? PERSONAL_LEARNINGS_FILE : "CALIBER_LEARNINGS.md";
11114
- if (!fs42.existsSync(filePath)) {
11378
+ if (!fs43.existsSync(filePath)) {
11115
11379
  console.log(chalk23.red("Learnings file not found."));
11116
11380
  return;
11117
11381
  }
11118
- const content = fs42.readFileSync(filePath, "utf-8");
11382
+ const content = fs43.readFileSync(filePath, "utf-8");
11119
11383
  const lines = content.split("\n");
11120
11384
  const bulletsOfSource = items.filter((i) => i.source === item.source);
11121
11385
  const posInFile = bulletsOfSource.indexOf(item);
@@ -11136,9 +11400,9 @@ async function learnDeleteCommand(indexStr) {
11136
11400
  }
11137
11401
  const bulletToRemove = lines[lineToRemove];
11138
11402
  const newLines = lines.filter((_, i) => i !== lineToRemove);
11139
- fs42.writeFileSync(filePath, newLines.join("\n"));
11403
+ fs43.writeFileSync(filePath, newLines.join("\n"));
11140
11404
  if (item.source === "personal") {
11141
- fs42.chmodSync(filePath, 384);
11405
+ fs43.chmodSync(filePath, 384);
11142
11406
  }
11143
11407
  const roiStats = readROIStats();
11144
11408
  const cleanText = bulletToRemove.replace(/^- /, "").replace(/^\*\*\[[^\]]+\]\*\*\s*/, "").trim();
@@ -11149,6 +11413,19 @@ async function learnDeleteCommand(indexStr) {
11149
11413
  }
11150
11414
  console.log(chalk23.green("\u2713") + ` Removed: ${bulletToRemove.replace(/^- /, "").slice(0, 80)}`);
11151
11415
  }
11416
+ async function learnAddCommand(content, options) {
11417
+ if (!content.trim()) {
11418
+ console.log(chalk23.yellow("Please provide learning content."));
11419
+ throw new Error("__exit__");
11420
+ }
11421
+ const scope = options.personal ? "personal" : "project";
11422
+ const result = addLearning(content.trim(), scope);
11423
+ if (result.added) {
11424
+ console.log(chalk23.green("\u2713") + ` Learning saved to ${result.file}`);
11425
+ } else {
11426
+ console.log(chalk23.dim(" Similar learning already exists \u2014 skipped."));
11427
+ }
11428
+ }
11152
11429
 
11153
11430
  // src/commands/insights.ts
11154
11431
  import chalk24 from "chalk";
@@ -11285,8 +11562,8 @@ async function insightsCommand(options) {
11285
11562
  }
11286
11563
 
11287
11564
  // src/commands/sources.ts
11288
- import fs43 from "fs";
11289
- import path34 from "path";
11565
+ import fs44 from "fs";
11566
+ import path35 from "path";
11290
11567
  import chalk25 from "chalk";
11291
11568
  async function sourcesListCommand() {
11292
11569
  const dir = process.cwd();
@@ -11302,9 +11579,9 @@ async function sourcesListCommand() {
11302
11579
  if (configSources.length > 0) {
11303
11580
  for (const source of configSources) {
11304
11581
  const sourcePath = source.path || source.url || "";
11305
- const exists = source.path ? fs43.existsSync(path34.resolve(dir, source.path)) : false;
11582
+ const exists = source.path ? fs44.existsSync(path35.resolve(dir, source.path)) : false;
11306
11583
  const status = exists ? chalk25.green("reachable") : chalk25.red("not found");
11307
- const hasSummary = source.path && fs43.existsSync(path34.join(path34.resolve(dir, source.path), ".caliber", "summary.json"));
11584
+ const hasSummary = source.path && fs44.existsSync(path35.join(path35.resolve(dir, source.path), ".caliber", "summary.json"));
11308
11585
  console.log(` ${chalk25.bold(source.role || source.type)} ${chalk25.dim(sourcePath)}`);
11309
11586
  console.log(` Type: ${source.type} Status: ${status}${hasSummary ? " " + chalk25.cyan("has summary.json") : ""}`);
11310
11587
  if (source.description) console.log(` ${chalk25.dim(source.description)}`);
@@ -11314,7 +11591,7 @@ async function sourcesListCommand() {
11314
11591
  if (workspaces.length > 0) {
11315
11592
  console.log(chalk25.dim(" Auto-detected workspaces:"));
11316
11593
  for (const ws of workspaces) {
11317
- const exists = fs43.existsSync(path34.resolve(dir, ws));
11594
+ const exists = fs44.existsSync(path35.resolve(dir, ws));
11318
11595
  console.log(` ${exists ? chalk25.green("\u25CF") : chalk25.red("\u25CF")} ${ws}`);
11319
11596
  }
11320
11597
  console.log("");
@@ -11322,8 +11599,8 @@ async function sourcesListCommand() {
11322
11599
  }
11323
11600
  async function sourcesAddCommand(sourcePath) {
11324
11601
  const dir = process.cwd();
11325
- const absPath = path34.resolve(dir, sourcePath);
11326
- if (!fs43.existsSync(absPath)) {
11602
+ const absPath = path35.resolve(dir, sourcePath);
11603
+ if (!fs44.existsSync(absPath)) {
11327
11604
  console.log(chalk25.red(`
11328
11605
  Path not found: ${sourcePath}
11329
11606
  `));
@@ -11338,7 +11615,7 @@ async function sourcesAddCommand(sourcePath) {
11338
11615
  }
11339
11616
  const existing = loadSourcesConfig(dir);
11340
11617
  const alreadyConfigured = existing.some(
11341
- (s) => s.path && path34.resolve(dir, s.path) === absPath
11618
+ (s) => s.path && path35.resolve(dir, s.path) === absPath
11342
11619
  );
11343
11620
  if (alreadyConfigured) {
11344
11621
  console.log(chalk25.yellow(`
@@ -11386,8 +11663,8 @@ async function sourcesRemoveCommand(name) {
11386
11663
  }
11387
11664
 
11388
11665
  // src/commands/publish.ts
11389
- import fs44 from "fs";
11390
- import path35 from "path";
11666
+ import fs45 from "fs";
11667
+ import path36 from "path";
11391
11668
  import chalk26 from "chalk";
11392
11669
  import ora7 from "ora";
11393
11670
  init_config();
@@ -11401,10 +11678,10 @@ async function publishCommand() {
11401
11678
  const spinner = ora7("Generating project summary...").start();
11402
11679
  try {
11403
11680
  const fingerprint = await collectFingerprint(dir);
11404
- const claudeMd = readFileOrNull(path35.join(dir, "CLAUDE.md"));
11681
+ const claudeMd = readFileOrNull(path36.join(dir, "CLAUDE.md"));
11405
11682
  const topLevelDirs = fingerprint.fileTree.filter((f) => f.endsWith("/") && !f.includes("/")).map((f) => f.replace(/\/$/, ""));
11406
11683
  const summary = {
11407
- name: fingerprint.packageName || path35.basename(dir),
11684
+ name: fingerprint.packageName || path36.basename(dir),
11408
11685
  version: "1.0.0",
11409
11686
  description: fingerprint.description || "",
11410
11687
  languages: fingerprint.languages,
@@ -11416,7 +11693,7 @@ async function publishCommand() {
11416
11693
  summary.conventions = claudeMd.slice(0, 2e3);
11417
11694
  }
11418
11695
  try {
11419
- const pkgContent = readFileOrNull(path35.join(dir, "package.json"));
11696
+ const pkgContent = readFileOrNull(path36.join(dir, "package.json"));
11420
11697
  if (pkgContent) {
11421
11698
  const pkg3 = JSON.parse(pkgContent);
11422
11699
  if (pkg3.scripts) {
@@ -11429,14 +11706,14 @@ async function publishCommand() {
11429
11706
  }
11430
11707
  } catch {
11431
11708
  }
11432
- const outputDir = path35.join(dir, ".caliber");
11433
- if (!fs44.existsSync(outputDir)) {
11434
- fs44.mkdirSync(outputDir, { recursive: true });
11709
+ const outputDir = path36.join(dir, ".caliber");
11710
+ if (!fs45.existsSync(outputDir)) {
11711
+ fs45.mkdirSync(outputDir, { recursive: true });
11435
11712
  }
11436
- const outputPath = path35.join(outputDir, "summary.json");
11437
- fs44.writeFileSync(outputPath, JSON.stringify(summary, null, 2) + "\n", "utf-8");
11713
+ const outputPath = path36.join(outputDir, "summary.json");
11714
+ fs45.writeFileSync(outputPath, JSON.stringify(summary, null, 2) + "\n", "utf-8");
11438
11715
  spinner.succeed("Project summary published");
11439
- console.log(` ${chalk26.green("\u2713")} ${path35.relative(dir, outputPath)}`);
11716
+ console.log(` ${chalk26.green("\u2713")} ${path36.relative(dir, outputPath)}`);
11440
11717
  console.log(chalk26.dim("\n Other projects can now reference this repo as a source."));
11441
11718
  console.log(chalk26.dim(" When they run `caliber init`, they'll read this summary automatically.\n"));
11442
11719
  } catch (err) {
@@ -11448,9 +11725,9 @@ async function publishCommand() {
11448
11725
  }
11449
11726
 
11450
11727
  // src/cli.ts
11451
- var __dirname = path36.dirname(fileURLToPath(import.meta.url));
11728
+ var __dirname = path37.dirname(fileURLToPath(import.meta.url));
11452
11729
  var pkg = JSON.parse(
11453
- fs45.readFileSync(path36.resolve(__dirname, "..", "package.json"), "utf-8")
11730
+ fs46.readFileSync(path37.resolve(__dirname, "..", "package.json"), "utf-8")
11454
11731
  );
11455
11732
  var program = new Command();
11456
11733
  var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
@@ -11516,7 +11793,7 @@ program.command("undo").description("Revert all config changes made by Caliber")
11516
11793
  program.command("status").description("Show current Caliber setup status").option("--json", "Output as JSON").action(tracked("status", statusCommand));
11517
11794
  program.command("regenerate").alias("regen").alias("re").description("Re-analyze project and regenerate setup").option("--dry-run", "Preview changes without writing files").action(tracked("regenerate", regenerateCommand));
11518
11795
  program.command("config").description("Configure LLM provider, API key, and model").action(tracked("config", configCommand));
11519
- program.command("skills").description("Discover and install community skills for your project").action(tracked("skills", recommendCommand));
11796
+ program.command("skills").description("Discover and install community skills for your project").option("--query <terms>", 'Search for skills by topic (e.g. "react frontend")').option("--install <slugs>", "Install specific skills by slug (comma-separated)").action(tracked("skills", recommendCommand));
11520
11797
  program.command("score").description("Score your current agent config setup (deterministic, no network)").option("--json", "Output as JSON").option("--quiet", "One-line output for scripts/hooks").option("--agent <type>", "Target agents (comma-separated): claude, cursor, codex, github-copilot", parseAgentOption).option("--compare <ref>", "Compare score against a git ref (branch, tag, or SHA)").action(tracked("score", scoreCommand));
11521
11798
  program.command("refresh").description("Update docs based on recent code changes").option("--quiet", "Suppress output (for use in hooks)").option("--dry-run", "Preview changes without writing files").action(tracked("refresh", refreshCommand));
11522
11799
  program.command("hooks").description("Manage auto-refresh hooks (toggle interactively)").option("--install", "Enable all hooks non-interactively").option("--remove", "Disable all hooks non-interactively").action(tracked("hooks", hooksCommand));
@@ -11534,18 +11811,19 @@ learn.command("remove").description("Remove learning hooks from .claude/settings
11534
11811
  learn.command("status").description("Show learning system status").action(tracked("learn:status", learnStatusCommand));
11535
11812
  learn.command("list").description("List all learnings with their source and activation data").option("--verbose", "Show explanations and activation counts").action(tracked("learn:list", (opts) => learnListCommand(opts)));
11536
11813
  learn.command("delete <index>").description("Delete a learning by its index number (from `caliber learn list`)").action(tracked("learn:delete", (index) => learnDeleteCommand(index)));
11814
+ learn.command("add <content>").description("Add a learning directly (used by agent skills)").option("--personal", "Save as a personal learning instead of project-level").action(tracked("learn:add", learnAddCommand));
11537
11815
 
11538
11816
  // src/utils/version-check.ts
11539
- import fs46 from "fs";
11540
- import path37 from "path";
11817
+ import fs47 from "fs";
11818
+ import path38 from "path";
11541
11819
  import { fileURLToPath as fileURLToPath2 } from "url";
11542
11820
  import { execSync as execSync15 } from "child_process";
11543
11821
  import chalk27 from "chalk";
11544
11822
  import ora8 from "ora";
11545
11823
  import confirm2 from "@inquirer/confirm";
11546
- var __dirname_vc = path37.dirname(fileURLToPath2(import.meta.url));
11824
+ var __dirname_vc = path38.dirname(fileURLToPath2(import.meta.url));
11547
11825
  var pkg2 = JSON.parse(
11548
- fs46.readFileSync(path37.resolve(__dirname_vc, "..", "package.json"), "utf-8")
11826
+ fs47.readFileSync(path38.resolve(__dirname_vc, "..", "package.json"), "utf-8")
11549
11827
  );
11550
11828
  function getChannel(version) {
11551
11829
  const match = version.match(/-(dev|next)\./);
@@ -11570,8 +11848,8 @@ function isNewer(registry, current) {
11570
11848
  function getInstalledVersion() {
11571
11849
  try {
11572
11850
  const globalRoot = execSync15("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
11573
- const pkgPath = path37.join(globalRoot, "@rely-ai", "caliber", "package.json");
11574
- return JSON.parse(fs46.readFileSync(pkgPath, "utf-8")).version;
11851
+ const pkgPath = path38.join(globalRoot, "@rely-ai", "caliber", "package.json");
11852
+ return JSON.parse(fs47.readFileSync(pkgPath, "utf-8")).version;
11575
11853
  } catch {
11576
11854
  return null;
11577
11855
  }