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

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