@rely-ai/caliber 1.30.7 → 1.31.0-dev.1774711564

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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/bin.js +950 -728
  3. package/package.json +4 -4
package/dist/bin.js CHANGED
@@ -230,21 +230,303 @@ var init_types = __esm({
230
230
  }
231
231
  });
232
232
 
233
+ // src/lib/builtin-skills.ts
234
+ var builtin_skills_exports = {};
235
+ __export(builtin_skills_exports, {
236
+ BUILTIN_SKILLS: () => BUILTIN_SKILLS,
237
+ FIND_SKILLS_SKILL: () => FIND_SKILLS_SKILL,
238
+ SAVE_LEARNING_SKILL: () => SAVE_LEARNING_SKILL,
239
+ SETUP_CALIBER_SKILL: () => SETUP_CALIBER_SKILL,
240
+ buildSkillContent: () => buildSkillContent,
241
+ ensureBuiltinSkills: () => ensureBuiltinSkills
242
+ });
243
+ import fs17 from "fs";
244
+ import path16 from "path";
245
+ function buildSkillContent(skill) {
246
+ const frontmatter = `---
247
+ name: ${skill.name}
248
+ description: ${skill.description}
249
+ ---
250
+
251
+ `;
252
+ return frontmatter + skill.content;
253
+ }
254
+ function getFindSkillsContent() {
255
+ const bin = resolveCaliber();
256
+ return `# Find Skills
257
+
258
+ Search the public skill registry for community-contributed skills
259
+ relevant to the user's current task and install them into this project.
260
+
261
+ ## Instructions
262
+
263
+ 1. Identify the key technologies, frameworks, or task types from the
264
+ user's request that might have community skills available
265
+ 2. Ask the user: "Would you like me to search for community skills
266
+ for [identified technologies]?"
267
+ 3. If the user agrees, run:
268
+ \`\`\`bash
269
+ ${bin} skills --query "<relevant terms>"
270
+ \`\`\`
271
+ This outputs the top 5 matching skills with scores and descriptions.
272
+ 4. Present the results to the user and ask which ones to install
273
+ 5. Install the selected skills:
274
+ \`\`\`bash
275
+ ${bin} skills --install <slug1>,<slug2>
276
+ \`\`\`
277
+ 6. Read the installed SKILL.md files to load them into your current
278
+ context so you can use them immediately in this session
279
+ 7. Summarize what was installed and continue with the user's task
280
+
281
+ ## Examples
282
+
283
+ User: "let's build a web app using React"
284
+ -> "I notice you want to work with React. Would you like me to search
285
+ for community skills that could help with React development?"
286
+ -> If yes: run \`${bin} skills --query "react frontend"\`
287
+ -> Show the user the results, ask which to install
288
+ -> Run \`${bin} skills --install <selected-slugs>\`
289
+ -> Read the installed files and continue
290
+
291
+ User: "help me set up Docker for this project"
292
+ -> "Would you like me to search for Docker-related skills?"
293
+ -> If yes: run \`${bin} skills --query "docker deployment"\`
294
+
295
+ User: "I need to write tests for this Python ML pipeline"
296
+ -> "Would you like me to find skills for Python ML testing?"
297
+ -> If yes: run \`${bin} skills --query "python machine-learning testing"\`
298
+
299
+ ## When NOT to trigger
300
+
301
+ - The user is working within an already well-configured area
302
+ - You already suggested skills for this technology in this session
303
+ - The user is in the middle of urgent debugging or time-sensitive work
304
+ - The technology is too generic (e.g. just "code" or "programming")
305
+ `;
306
+ }
307
+ function getSaveLearningContent() {
308
+ const bin = resolveCaliber();
309
+ return `# Save Learning
310
+
311
+ Save a user's instruction or preference as a persistent learning that
312
+ will be applied in all future sessions on this project.
313
+
314
+ ## Instructions
315
+
316
+ 1. Detect when the user gives an instruction to remember, such as:
317
+ - "remember this", "save this", "always do X", "never do Y"
318
+ - "from now on", "going forward", "in this project we..."
319
+ - Any stated convention, preference, or rule
320
+ 2. Refine the instruction into a clean, actionable learning bullet with
321
+ an appropriate type prefix:
322
+ - \`**[convention]**\` \u2014 coding style, workflow, git conventions
323
+ - \`**[pattern]**\` \u2014 reusable code patterns
324
+ - \`**[anti-pattern]**\` \u2014 things to avoid
325
+ - \`**[preference]**\` \u2014 personal/team preferences
326
+ - \`**[context]**\` \u2014 project-specific context
327
+ 3. Show the refined learning to the user and ask for confirmation
328
+ 4. If confirmed, run:
329
+ \`\`\`bash
330
+ ${bin} learn add "<refined learning>"
331
+ \`\`\`
332
+ For personal preferences (not project-level), add \`--personal\`:
333
+ \`\`\`bash
334
+ ${bin} learn add --personal "<refined learning>"
335
+ \`\`\`
336
+ 5. Stage the learnings file for the next commit:
337
+ \`\`\`bash
338
+ git add CALIBER_LEARNINGS.md
339
+ \`\`\`
340
+
341
+ ## Examples
342
+
343
+ User: "when developing features, push to next branch not master, remember it"
344
+ -> Refine: \`**[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`\`
345
+ -> "I'll save this as a project learning:
346
+ **[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`
347
+ Save for future sessions?"
348
+ -> If yes: run \`${bin} learn add "**[convention]** Push feature commits to the next branch, not master"\`
349
+ -> Run \`git add CALIBER_LEARNINGS.md\`
350
+
351
+ User: "always use bun instead of npm"
352
+ -> Refine: \`**[preference]** Use \\\`bun\\\` instead of \\\`npm\\\` for package management\`
353
+ -> Confirm and save
354
+
355
+ User: "never use any in TypeScript, use unknown instead"
356
+ -> Refine: \`**[convention]** Use \\\`unknown\\\` instead of \\\`any\\\` in TypeScript\`
357
+ -> Confirm and save
358
+
359
+ ## When NOT to trigger
360
+
361
+ - The user is giving a one-time instruction for the current task only
362
+ - The instruction is too vague to be actionable
363
+ - The user explicitly says "just for now" or "only this time"
364
+ `;
365
+ }
366
+ function getSetupCaliberContent() {
367
+ const bin = resolveCaliber();
368
+ return `# Setup Caliber
369
+
370
+ Set up Caliber for automatic AI agent context sync on this project.
371
+ Caliber keeps your CLAUDE.md, Cursor rules, Copilot instructions, and Codex configs
372
+ in sync with your codebase \u2014 automatically on every commit.
373
+
374
+ ## Instructions
375
+
376
+ 1. Check if Caliber is already set up locally:
377
+ \`\`\`bash
378
+ grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"
379
+ \`\`\`
380
+
381
+ 2. If **hook-active**, tell the user: "Caliber is already set up on this repo.
382
+ Your agent configs update automatically on every commit." Then stop.
383
+
384
+ 3. If **no-hook**, ask the user: "Are you setting up Caliber for yourself (solo)
385
+ or for your team?"
386
+
387
+ ### Solo Setup
388
+
389
+ a. Tell the user: "I'll set up Caliber to sync your agent configs automatically
390
+ on every commit."
391
+ b. Install the pre-commit hook:
392
+ \`\`\`bash
393
+ ${bin} hooks --install
394
+ \`\`\`
395
+ c. Run a refresh to make sure configs are current:
396
+ \`\`\`bash
397
+ ${bin} refresh
398
+ \`\`\`
399
+ d. Tell the user what happened and that configs will auto-sync on every commit.
400
+
401
+ ### Team Setup
402
+
403
+ a. Tell the user: "I'll set up Caliber for your team \u2014 local hooks for you,
404
+ plus a GitHub Action for automatic sync across the team."
405
+ b. Install the local pre-commit hook:
406
+ \`\`\`bash
407
+ ${bin} hooks --install
408
+ \`\`\`
409
+ c. Run a refresh:
410
+ \`\`\`bash
411
+ ${bin} refresh
412
+ \`\`\`
413
+ d. Create the GitHub Action workflow for nightly sync. Write this file to
414
+ \`.github/workflows/caliber-sync.yml\`:
415
+ \`\`\`yaml
416
+ name: Caliber Sync
417
+ on:
418
+ schedule:
419
+ - cron: '0 3 * * 1-5'
420
+ pull_request:
421
+ types: [opened, synchronize]
422
+ workflow_dispatch:
423
+ jobs:
424
+ sync:
425
+ runs-on: ubuntu-latest
426
+ steps:
427
+ - uses: actions/checkout@v4
428
+ - uses: caliber-ai-org/ai-setup@v1
429
+ with:
430
+ mode: sync
431
+ auto-refresh: true
432
+ comment: true
433
+ github-token: \${{ secrets.GITHUB_TOKEN }}
434
+ env:
435
+ ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}
436
+ \`\`\`
437
+ e. Tell the user: "Created the GitHub Action workflow. Add your ANTHROPIC_API_KEY
438
+ to your repo secrets (Settings \u2192 Secrets \u2192 Actions), then commit and push this
439
+ workflow file. From now on, every PR gets a Caliber sync check and configs
440
+ refresh nightly."
441
+ f. Offer to commit the workflow file and the updated configs.
442
+
443
+ ### If \`${bin}\` is not found
444
+
445
+ Install it first:
446
+ \`\`\`bash
447
+ npm install -g @rely-ai/caliber
448
+ \`\`\`
449
+ Or use npx (no install needed):
450
+ \`\`\`bash
451
+ npx @rely-ai/caliber hooks --install
452
+ \`\`\`
453
+
454
+ ## When to trigger
455
+
456
+ - When a developer first opens this project and Caliber hooks are not installed
457
+ - When the user asks about keeping agent configs up to date
458
+ - When the user asks "how do I set up Caliber" or "what is Caliber"
459
+
460
+ ## When NOT to trigger
461
+
462
+ - Hooks are already installed and working
463
+ - The user is in the middle of time-sensitive work
464
+ `;
465
+ }
466
+ function ensureBuiltinSkills() {
467
+ const written = [];
468
+ for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
469
+ if (!fs17.existsSync(platformDir)) continue;
470
+ for (const skill of BUILTIN_SKILLS) {
471
+ const skillPath = path16.join(skillsDir, skill.name, "SKILL.md");
472
+ if (fs17.existsSync(skillPath)) continue;
473
+ fs17.mkdirSync(path16.dirname(skillPath), { recursive: true });
474
+ fs17.writeFileSync(skillPath, buildSkillContent(skill));
475
+ written.push(skillPath);
476
+ }
477
+ }
478
+ return written;
479
+ }
480
+ var FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL, SETUP_CALIBER_SKILL, BUILTIN_SKILLS, PLATFORM_CONFIGS;
481
+ var init_builtin_skills = __esm({
482
+ "src/lib/builtin-skills.ts"() {
483
+ "use strict";
484
+ init_resolve_caliber();
485
+ FIND_SKILLS_SKILL = {
486
+ name: "find-skills",
487
+ 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.",
488
+ get content() {
489
+ return getFindSkillsContent();
490
+ }
491
+ };
492
+ SAVE_LEARNING_SKILL = {
493
+ name: "save-learning",
494
+ 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.",
495
+ get content() {
496
+ return getSaveLearningContent();
497
+ }
498
+ };
499
+ SETUP_CALIBER_SKILL = {
500
+ name: "setup-caliber",
501
+ description: "Sets up Caliber for automatic AI agent context sync. Installs pre-commit hooks so CLAUDE.md, Cursor rules, and Copilot instructions update automatically on every commit. Use when Caliber hooks are not yet installed or when the user asks about keeping agent configs in sync.",
502
+ get content() {
503
+ return getSetupCaliberContent();
504
+ }
505
+ };
506
+ BUILTIN_SKILLS = [FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL, SETUP_CALIBER_SKILL];
507
+ PLATFORM_CONFIGS = [
508
+ { platformDir: ".claude", skillsDir: path16.join(".claude", "skills") },
509
+ { platformDir: ".cursor", skillsDir: path16.join(".cursor", "skills") },
510
+ { platformDir: ".agents", skillsDir: path16.join(".agents", "skills") }
511
+ ];
512
+ }
513
+ });
514
+
233
515
  // src/utils/editor.ts
234
- import { execSync as execSync13, spawn as spawn3 } from "child_process";
235
- import fs26 from "fs";
236
- import path22 from "path";
516
+ import { execSync as execSync14, spawn as spawn3 } from "child_process";
517
+ import fs27 from "fs";
518
+ import path23 from "path";
237
519
  import os6 from "os";
238
520
  function getEmptyFilePath(proposedPath) {
239
- fs26.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
240
- const tempPath = path22.join(DIFF_TEMP_DIR, path22.basename(proposedPath));
241
- fs26.writeFileSync(tempPath, "");
521
+ fs27.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
522
+ const tempPath = path23.join(DIFF_TEMP_DIR, path23.basename(proposedPath));
523
+ fs27.writeFileSync(tempPath, "");
242
524
  return tempPath;
243
525
  }
244
526
  function commandExists(cmd) {
245
527
  try {
246
528
  const check = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
247
- execSync13(check, { stdio: "ignore" });
529
+ execSync14(check, { stdio: "ignore" });
248
530
  return true;
249
531
  } catch {
250
532
  return false;
@@ -278,7 +560,7 @@ var init_editor = __esm({
278
560
  "src/utils/editor.ts"() {
279
561
  "use strict";
280
562
  IS_WINDOWS3 = process.platform === "win32";
281
- DIFF_TEMP_DIR = path22.join(os6.tmpdir(), "caliber-diff");
563
+ DIFF_TEMP_DIR = path23.join(os6.tmpdir(), "caliber-diff");
282
564
  }
283
565
  });
284
566
 
@@ -290,7 +572,7 @@ __export(review_exports, {
290
572
  promptWantsReview: () => promptWantsReview
291
573
  });
292
574
  import chalk10 from "chalk";
293
- import fs27 from "fs";
575
+ import fs28 from "fs";
294
576
  import select4 from "@inquirer/select";
295
577
  import { createTwoFilesPatch } from "diff";
296
578
  async function promptWantsReview() {
@@ -327,8 +609,8 @@ async function openReview(method, stagedFiles) {
327
609
  return;
328
610
  }
329
611
  const fileInfos = stagedFiles.map((file) => {
330
- const proposed = fs27.readFileSync(file.proposedPath, "utf-8");
331
- const current = file.currentPath ? fs27.readFileSync(file.currentPath, "utf-8") : "";
612
+ const proposed = fs28.readFileSync(file.proposedPath, "utf-8");
613
+ const current = file.currentPath ? fs28.readFileSync(file.currentPath, "utf-8") : "";
332
614
  const patch = createTwoFilesPatch(
333
615
  file.isNew ? "/dev/null" : file.relativePath,
334
616
  file.relativePath,
@@ -503,13 +785,13 @@ __export(lock_exports, {
503
785
  isCaliberRunning: () => isCaliberRunning,
504
786
  releaseLock: () => releaseLock
505
787
  });
506
- import fs37 from "fs";
507
- import path29 from "path";
788
+ import fs38 from "fs";
789
+ import path30 from "path";
508
790
  import os8 from "os";
509
791
  function isCaliberRunning() {
510
792
  try {
511
- if (!fs37.existsSync(LOCK_FILE)) return false;
512
- const raw = fs37.readFileSync(LOCK_FILE, "utf-8").trim();
793
+ if (!fs38.existsSync(LOCK_FILE)) return false;
794
+ const raw = fs38.readFileSync(LOCK_FILE, "utf-8").trim();
513
795
  const { pid, ts } = JSON.parse(raw);
514
796
  if (Date.now() - ts > STALE_MS) return false;
515
797
  try {
@@ -524,13 +806,13 @@ function isCaliberRunning() {
524
806
  }
525
807
  function acquireLock() {
526
808
  try {
527
- fs37.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
809
+ fs38.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
528
810
  } catch {
529
811
  }
530
812
  }
531
813
  function releaseLock() {
532
814
  try {
533
- if (fs37.existsSync(LOCK_FILE)) fs37.unlinkSync(LOCK_FILE);
815
+ if (fs38.existsSync(LOCK_FILE)) fs38.unlinkSync(LOCK_FILE);
534
816
  } catch {
535
817
  }
536
818
  }
@@ -538,7 +820,7 @@ var LOCK_FILE, STALE_MS;
538
820
  var init_lock = __esm({
539
821
  "src/lib/lock.ts"() {
540
822
  "use strict";
541
- LOCK_FILE = path29.join(os8.tmpdir(), ".caliber.lock");
823
+ LOCK_FILE = path30.join(os8.tmpdir(), ".caliber.lock");
542
824
  STALE_MS = 10 * 60 * 1e3;
543
825
  }
544
826
  });
@@ -550,9 +832,9 @@ import path38 from "path";
550
832
  import { fileURLToPath } from "url";
551
833
 
552
834
  // src/commands/init.ts
553
- import path25 from "path";
835
+ import path26 from "path";
554
836
  import chalk14 from "chalk";
555
- import fs32 from "fs";
837
+ import fs33 from "fs";
556
838
 
557
839
  // src/fingerprint/index.ts
558
840
  import fs8 from "fs";
@@ -2450,7 +2732,7 @@ PRIORITY WHEN CONSTRAINTS CONFLICT: Grounding and reference density matter more
2450
2732
  Note: Permissions, hooks, freshness tracking, and OpenSkills frontmatter are scored automatically by caliber \u2014 do not optimize for them.
2451
2733
  README.md is provided for context only \u2014 do NOT include a readmeMd field in your output.`;
2452
2734
  var OUTPUT_SIZE_CONSTRAINTS = `OUTPUT SIZE CONSTRAINTS \u2014 these are critical:
2453
- - CLAUDE.md / AGENTS.md: MUST be under 150 lines for maximum score. Aim for 100-140 lines. Be concise \u2014 commands, architecture overview, and key conventions. Use bullet points and tables, not prose.
2735
+ - CLAUDE.md / AGENTS.md: MUST be under 400 lines for maximum score. Aim for 200-350 lines. Be thorough \u2014 commands, architecture overview, key conventions, data flow, and important patterns. Use bullet points and tables, not prose.
2454
2736
 
2455
2737
  Pack project references densely in architecture sections \u2014 use inline paths, not prose paragraphs:
2456
2738
  GOOD: **Entry**: \`src/bin.ts\` \u2192 \`src/cli.ts\` \xB7 **LLM** (\`src/llm/\`): \`anthropic.ts\` \xB7 \`vertex.ts\` \xB7 \`openai-compat.ts\`
@@ -2578,7 +2860,7 @@ Structure:
2578
2860
  5. "## Common Issues" (required) \u2014 specific error messages and their fixes. Not "check your config" but "If you see 'Connection refused on port 5432': 1. Verify postgres is running: docker ps | grep postgres 2. Check .env has correct DATABASE_URL"
2579
2861
 
2580
2862
  Rules:
2581
- - Max 150 lines. Focus on actionable instructions, not documentation prose.
2863
+ - Max 400 lines. Focus on actionable instructions, not documentation prose.
2582
2864
  - Study existing code in the project context to extract the real patterns being used. A skill for "create API route" should show the exact file structure, imports, error handling, and naming that existing routes use.
2583
2865
  - Be specific and actionable. GOOD: "Run \`pnpm test -- --filter=api\` to verify". BAD: "Validate the data before proceeding."
2584
2866
  - Never use ambiguous language. Instead of "handle errors properly", write "Wrap the DB call in try/catch. On failure, return { error: string, code: number } matching the ErrorResponse type in \`src/types.ts\`."
@@ -2632,7 +2914,7 @@ Rules:
2632
2914
  - Update the "fileDescriptions" to reflect any changes you make.
2633
2915
 
2634
2916
  Quality constraints \u2014 your changes are scored, so do not break these:
2635
- - CLAUDE.md / AGENTS.md: MUST stay under 150 lines. If adding content, remove less important lines to stay within budget. Do not refuse the user's request \u2014 make the change and trim elsewhere.
2917
+ - CLAUDE.md / AGENTS.md: MUST stay under 400 lines. If adding content, remove less important lines to stay within budget. Do not refuse the user's request \u2014 make the change and trim elsewhere.
2636
2918
  - Avoid vague instructions ("follow best practices", "write clean code", "ensure quality").
2637
2919
  - Do NOT add directory tree listings in code blocks.
2638
2920
  - Do NOT remove existing code blocks \u2014 they contribute to the executable content score.
@@ -2656,7 +2938,7 @@ CONSERVATIVE UPDATE means:
2656
2938
  - NEVER replace specific paths/commands with generic prose
2657
2939
 
2658
2940
  Quality constraints (the output is scored deterministically):
2659
- - CLAUDE.md / AGENTS.md: MUST stay under 150 lines. If the diff adds content, trim the least important lines elsewhere.
2941
+ - CLAUDE.md / AGENTS.md: MUST stay under 400 lines. If the diff adds content, trim the least important lines elsewhere.
2660
2942
  - Keep 3+ code blocks with executable commands \u2014 do not remove code blocks
2661
2943
  - Every file path, command, and identifier must be in backticks
2662
2944
  - ONLY reference file paths that exist in the provided file tree \u2014 do NOT invent paths
@@ -2664,6 +2946,7 @@ Quality constraints (the output is scored deterministically):
2664
2946
 
2665
2947
  Managed content:
2666
2948
  - Keep managed blocks (<!-- caliber:managed --> ... <!-- /caliber:managed -->) intact
2949
+ - Keep context sync blocks (<!-- caliber:managed:sync --> ... <!-- /caliber:managed:sync -->) intact
2667
2950
  - Do NOT modify CALIBER_LEARNINGS.md \u2014 it is managed separately
2668
2951
  - Preserve any references to CALIBER_LEARNINGS.md in CLAUDE.md
2669
2952
 
@@ -2679,9 +2962,12 @@ Return a JSON object with this exact shape:
2679
2962
  "copilotInstructionFiles": [{"filename": "name.instructions.md", "content": "..."}] or null
2680
2963
  },
2681
2964
  "changesSummary": "<1-2 sentence summary of what was updated and why>",
2965
+ "fileChanges": [{"file": "CLAUDE.md", "description": "added new API routes, updated build commands"}],
2682
2966
  "docsUpdated": ["CLAUDE.md", "README.md"]
2683
2967
  }
2684
2968
 
2969
+ The "fileChanges" array MUST include one entry per file that was updated (non-null in updatedDocs). Each entry describes what specifically changed in that file \u2014 be concrete (e.g. "added auth middleware section" not "updated docs").
2970
+
2685
2971
  Respond with ONLY the JSON object, no markdown fences or extra text.`;
2686
2972
  var LEARN_SYSTEM_PROMPT = `You are an expert developer experience engineer. You analyze raw tool call events from AI coding sessions to extract reusable operational lessons that will help future LLM sessions work more effectively in this project.
2687
2973
 
@@ -3173,63 +3459,205 @@ function getCursorConfigDir() {
3173
3459
  return path8.join(home, ".config", "Cursor");
3174
3460
  }
3175
3461
 
3176
- // src/fingerprint/sources.ts
3462
+ // src/lib/hooks.ts
3463
+ init_resolve_caliber();
3177
3464
  import fs10 from "fs";
3178
3465
  import path9 from "path";
3179
-
3180
- // src/scoring/utils.ts
3181
- import { existsSync as existsSync2, readFileSync, readdirSync } from "fs";
3182
- import { execFileSync } from "child_process";
3183
- import { join, relative } from "path";
3184
- function readFileOrNull(filePath) {
3466
+ import { execSync as execSync8 } from "child_process";
3467
+ var SETTINGS_PATH = path9.join(".claude", "settings.json");
3468
+ var REFRESH_TAIL = "refresh --quiet";
3469
+ var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
3470
+ function getHookCommand() {
3471
+ return `${resolveCaliber()} ${REFRESH_TAIL}`;
3472
+ }
3473
+ function readSettings() {
3474
+ if (!fs10.existsSync(SETTINGS_PATH)) return {};
3185
3475
  try {
3186
- return readFileSync(filePath, "utf-8");
3476
+ return JSON.parse(fs10.readFileSync(SETTINGS_PATH, "utf-8"));
3187
3477
  } catch {
3188
- return null;
3478
+ return {};
3189
3479
  }
3190
3480
  }
3191
- var IGNORED_DIRS = /* @__PURE__ */ new Set([
3192
- "node_modules",
3193
- ".git",
3194
- "dist",
3195
- "build",
3196
- "out",
3197
- ".next",
3198
- ".nuxt",
3199
- "__pycache__",
3200
- ".venv",
3201
- "venv",
3202
- "env",
3203
- ".env",
3204
- "target",
3205
- "vendor",
3206
- ".cache",
3207
- ".parcel-cache",
3208
- "coverage",
3209
- ".nyc_output",
3210
- ".turbo",
3211
- ".caliber",
3212
- ".claude",
3213
- ".cursor",
3214
- ".agents",
3215
- ".codex"
3216
- ]);
3217
- var IGNORED_FILES = /* @__PURE__ */ new Set([
3218
- ".DS_Store",
3219
- "Thumbs.db",
3220
- ".gitignore",
3221
- ".editorconfig",
3222
- ".prettierrc",
3223
- ".prettierignore",
3224
- ".eslintignore",
3225
- "package-lock.json",
3226
- "yarn.lock",
3227
- "pnpm-lock.yaml",
3228
- "bun.lockb"
3229
- ]);
3230
- function isGitRepo2(dir) {
3231
- try {
3232
- execFileSync("git", ["rev-parse", "--git-dir"], {
3481
+ function writeSettings(settings) {
3482
+ const dir = path9.dirname(SETTINGS_PATH);
3483
+ if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
3484
+ fs10.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
3485
+ }
3486
+ function findHookIndex(sessionEnd) {
3487
+ return sessionEnd.findIndex(
3488
+ (entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, REFRESH_TAIL))
3489
+ );
3490
+ }
3491
+ function isHookInstalled() {
3492
+ const settings = readSettings();
3493
+ const sessionEnd = settings.hooks?.SessionEnd;
3494
+ if (!Array.isArray(sessionEnd)) return false;
3495
+ return findHookIndex(sessionEnd) !== -1;
3496
+ }
3497
+ function installHook() {
3498
+ const settings = readSettings();
3499
+ if (!settings.hooks) settings.hooks = {};
3500
+ if (!Array.isArray(settings.hooks.SessionEnd)) settings.hooks.SessionEnd = [];
3501
+ if (findHookIndex(settings.hooks.SessionEnd) !== -1) {
3502
+ return { installed: false, alreadyInstalled: true };
3503
+ }
3504
+ settings.hooks.SessionEnd.push({
3505
+ matcher: "",
3506
+ hooks: [{ type: "command", command: getHookCommand(), description: HOOK_DESCRIPTION }]
3507
+ });
3508
+ writeSettings(settings);
3509
+ return { installed: true, alreadyInstalled: false };
3510
+ }
3511
+ function removeHook() {
3512
+ const settings = readSettings();
3513
+ const sessionEnd = settings.hooks?.SessionEnd;
3514
+ if (!Array.isArray(sessionEnd)) {
3515
+ return { removed: false, notFound: true };
3516
+ }
3517
+ const idx = findHookIndex(sessionEnd);
3518
+ if (idx === -1) {
3519
+ return { removed: false, notFound: true };
3520
+ }
3521
+ sessionEnd.splice(idx, 1);
3522
+ if (sessionEnd.length === 0) {
3523
+ delete settings.hooks.SessionEnd;
3524
+ }
3525
+ if (settings.hooks && Object.keys(settings.hooks).length === 0) {
3526
+ delete settings.hooks;
3527
+ }
3528
+ writeSettings(settings);
3529
+ return { removed: true, notFound: false };
3530
+ }
3531
+ var PRECOMMIT_START = "# caliber:pre-commit:start";
3532
+ var PRECOMMIT_END = "# caliber:pre-commit:end";
3533
+ function getPrecommitBlock() {
3534
+ const bin = resolveCaliber();
3535
+ const npx = isNpxResolution();
3536
+ const guard = npx ? "command -v npx >/dev/null 2>&1" : `[ -x "${bin}" ] || command -v "${bin}" >/dev/null 2>&1`;
3537
+ const invoke = npx ? bin : `"${bin}"`;
3538
+ return `${PRECOMMIT_START}
3539
+ if ${guard}; then
3540
+ echo "\\033[2mcaliber: refreshing docs...\\033[0m"
3541
+ ${invoke} refresh 2>/dev/null || true
3542
+ ${invoke} learn finalize 2>/dev/null || true
3543
+ git diff --name-only -- CLAUDE.md .claude/ .cursor/ AGENTS.md CALIBER_LEARNINGS.md 2>/dev/null | xargs git add 2>/dev/null || true
3544
+ fi
3545
+ ${PRECOMMIT_END}`;
3546
+ }
3547
+ function getGitHooksDir() {
3548
+ try {
3549
+ const gitDir = execSync8("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3550
+ return path9.join(gitDir, "hooks");
3551
+ } catch {
3552
+ return null;
3553
+ }
3554
+ }
3555
+ function getPreCommitPath() {
3556
+ const hooksDir = getGitHooksDir();
3557
+ return hooksDir ? path9.join(hooksDir, "pre-commit") : null;
3558
+ }
3559
+ function isPreCommitHookInstalled() {
3560
+ const hookPath = getPreCommitPath();
3561
+ if (!hookPath || !fs10.existsSync(hookPath)) return false;
3562
+ const content = fs10.readFileSync(hookPath, "utf-8");
3563
+ return content.includes(PRECOMMIT_START);
3564
+ }
3565
+ function installPreCommitHook() {
3566
+ if (isPreCommitHookInstalled()) {
3567
+ return { installed: false, alreadyInstalled: true };
3568
+ }
3569
+ const hookPath = getPreCommitPath();
3570
+ if (!hookPath) return { installed: false, alreadyInstalled: false };
3571
+ const hooksDir = path9.dirname(hookPath);
3572
+ if (!fs10.existsSync(hooksDir)) fs10.mkdirSync(hooksDir, { recursive: true });
3573
+ let content = "";
3574
+ if (fs10.existsSync(hookPath)) {
3575
+ content = fs10.readFileSync(hookPath, "utf-8");
3576
+ if (!content.endsWith("\n")) content += "\n";
3577
+ content += "\n" + getPrecommitBlock() + "\n";
3578
+ } else {
3579
+ content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
3580
+ }
3581
+ fs10.writeFileSync(hookPath, content);
3582
+ fs10.chmodSync(hookPath, 493);
3583
+ return { installed: true, alreadyInstalled: false };
3584
+ }
3585
+ function removePreCommitHook() {
3586
+ const hookPath = getPreCommitPath();
3587
+ if (!hookPath || !fs10.existsSync(hookPath)) {
3588
+ return { removed: false, notFound: true };
3589
+ }
3590
+ let content = fs10.readFileSync(hookPath, "utf-8");
3591
+ if (!content.includes(PRECOMMIT_START)) {
3592
+ return { removed: false, notFound: true };
3593
+ }
3594
+ const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
3595
+ content = content.replace(regex, "\n");
3596
+ if (content.trim() === "#!/bin/sh" || content.trim() === "") {
3597
+ fs10.unlinkSync(hookPath);
3598
+ } else {
3599
+ fs10.writeFileSync(hookPath, content);
3600
+ }
3601
+ return { removed: true, notFound: false };
3602
+ }
3603
+
3604
+ // src/fingerprint/sources.ts
3605
+ import fs11 from "fs";
3606
+ import path10 from "path";
3607
+
3608
+ // src/scoring/utils.ts
3609
+ import { existsSync as existsSync2, readFileSync, readdirSync } from "fs";
3610
+ import { execFileSync } from "child_process";
3611
+ import { join, relative } from "path";
3612
+ function readFileOrNull(filePath) {
3613
+ try {
3614
+ return readFileSync(filePath, "utf-8");
3615
+ } catch {
3616
+ return null;
3617
+ }
3618
+ }
3619
+ var IGNORED_DIRS = /* @__PURE__ */ new Set([
3620
+ "node_modules",
3621
+ ".git",
3622
+ "dist",
3623
+ "build",
3624
+ "out",
3625
+ ".next",
3626
+ ".nuxt",
3627
+ "__pycache__",
3628
+ ".venv",
3629
+ "venv",
3630
+ "env",
3631
+ ".env",
3632
+ "target",
3633
+ "vendor",
3634
+ ".cache",
3635
+ ".parcel-cache",
3636
+ "coverage",
3637
+ ".nyc_output",
3638
+ ".turbo",
3639
+ ".caliber",
3640
+ ".claude",
3641
+ ".cursor",
3642
+ ".agents",
3643
+ ".codex"
3644
+ ]);
3645
+ var IGNORED_FILES = /* @__PURE__ */ new Set([
3646
+ ".DS_Store",
3647
+ "Thumbs.db",
3648
+ ".gitignore",
3649
+ ".editorconfig",
3650
+ ".prettierrc",
3651
+ ".prettierignore",
3652
+ ".eslintignore",
3653
+ "package-lock.json",
3654
+ "yarn.lock",
3655
+ "pnpm-lock.yaml",
3656
+ "bun.lockb"
3657
+ ]);
3658
+ function isGitRepo2(dir) {
3659
+ try {
3660
+ execFileSync("git", ["rev-parse", "--git-dir"], {
3233
3661
  cwd: dir,
3234
3662
  encoding: "utf-8",
3235
3663
  stdio: ["pipe", "pipe", "pipe"]
@@ -3494,7 +3922,7 @@ var SOURCE_CONTENT_LIMIT = 2e3;
3494
3922
  var README_CONTENT_LIMIT = 1e3;
3495
3923
  var ORIGIN_PRIORITY = { cli: 0, config: 1, workspace: 2 };
3496
3924
  function loadSourcesConfig(dir) {
3497
- const configPath = path9.join(dir, ".caliber", "sources.json");
3925
+ const configPath = path10.join(dir, ".caliber", "sources.json");
3498
3926
  const content = readFileOrNull(configPath);
3499
3927
  if (!content) return [];
3500
3928
  try {
@@ -3512,29 +3940,29 @@ function loadSourcesConfig(dir) {
3512
3940
  }
3513
3941
  }
3514
3942
  function writeSourcesConfig(dir, sources2) {
3515
- const configDir = path9.join(dir, ".caliber");
3516
- if (!fs10.existsSync(configDir)) {
3517
- fs10.mkdirSync(configDir, { recursive: true });
3943
+ const configDir = path10.join(dir, ".caliber");
3944
+ if (!fs11.existsSync(configDir)) {
3945
+ fs11.mkdirSync(configDir, { recursive: true });
3518
3946
  }
3519
- const configPath = path9.join(configDir, "sources.json");
3520
- fs10.writeFileSync(configPath, JSON.stringify({ sources: sources2 }, null, 2) + "\n", "utf-8");
3947
+ const configPath = path10.join(configDir, "sources.json");
3948
+ fs11.writeFileSync(configPath, JSON.stringify({ sources: sources2 }, null, 2) + "\n", "utf-8");
3521
3949
  }
3522
3950
  function detectSourceType(absPath) {
3523
3951
  try {
3524
- return fs10.statSync(absPath).isDirectory() ? "repo" : "file";
3952
+ return fs11.statSync(absPath).isDirectory() ? "repo" : "file";
3525
3953
  } catch {
3526
3954
  return "file";
3527
3955
  }
3528
3956
  }
3529
3957
  function isInsideDir(childPath, parentDir) {
3530
- const relative2 = path9.relative(parentDir, childPath);
3531
- return !relative2.startsWith("..") && !path9.isAbsolute(relative2);
3958
+ const relative2 = path10.relative(parentDir, childPath);
3959
+ return !relative2.startsWith("..") && !path10.isAbsolute(relative2);
3532
3960
  }
3533
3961
  function resolveAllSources(dir, cliSources, workspaces) {
3534
3962
  const seen = /* @__PURE__ */ new Map();
3535
- const projectRoot = path9.resolve(dir);
3963
+ const projectRoot = path10.resolve(dir);
3536
3964
  for (const src of cliSources) {
3537
- const absPath = path9.resolve(dir, src);
3965
+ const absPath = path10.resolve(dir, src);
3538
3966
  if (seen.has(absPath)) continue;
3539
3967
  const type = detectSourceType(absPath);
3540
3968
  seen.set(absPath, {
@@ -3547,12 +3975,12 @@ function resolveAllSources(dir, cliSources, workspaces) {
3547
3975
  for (const cfg of configSources) {
3548
3976
  if (cfg.type === "url") continue;
3549
3977
  if (!cfg.path) continue;
3550
- const absPath = path9.resolve(dir, cfg.path);
3978
+ const absPath = path10.resolve(dir, cfg.path);
3551
3979
  if (seen.has(absPath)) continue;
3552
3980
  seen.set(absPath, { absPath, config: cfg, origin: "config" });
3553
3981
  }
3554
3982
  for (const ws of workspaces) {
3555
- const absPath = path9.resolve(dir, ws);
3983
+ const absPath = path10.resolve(dir, ws);
3556
3984
  if (seen.has(absPath)) continue;
3557
3985
  if (!isInsideDir(absPath, projectRoot)) continue;
3558
3986
  seen.set(absPath, {
@@ -3565,7 +3993,7 @@ function resolveAllSources(dir, cliSources, workspaces) {
3565
3993
  for (const [absPath, resolved] of seen) {
3566
3994
  let stat;
3567
3995
  try {
3568
- stat = fs10.statSync(absPath);
3996
+ stat = fs11.statSync(absPath);
3569
3997
  } catch {
3570
3998
  console.warn(`Source ${resolved.config.path || absPath} not found, skipping`);
3571
3999
  continue;
@@ -3594,13 +4022,13 @@ function collectSourceSummary(resolved, projectDir) {
3594
4022
  if (config.type === "file") {
3595
4023
  return collectFileSummary(resolved, projectDir);
3596
4024
  }
3597
- const summaryPath = path9.join(absPath, ".caliber", "summary.json");
4025
+ const summaryPath = path10.join(absPath, ".caliber", "summary.json");
3598
4026
  const summaryContent = readFileOrNull(summaryPath);
3599
4027
  if (summaryContent) {
3600
4028
  try {
3601
4029
  const published = JSON.parse(summaryContent);
3602
4030
  return {
3603
- name: published.name || path9.basename(absPath),
4031
+ name: published.name || path10.basename(absPath),
3604
4032
  type: "repo",
3605
4033
  role: config.role || published.role || "related-repo",
3606
4034
  description: config.description || published.description || "",
@@ -3620,18 +4048,18 @@ function collectRepoSummary(resolved, projectDir) {
3620
4048
  let topLevelDirs;
3621
4049
  let keyFiles;
3622
4050
  try {
3623
- const entries = fs10.readdirSync(absPath, { withFileTypes: true });
4051
+ const entries = fs11.readdirSync(absPath, { withFileTypes: true });
3624
4052
  topLevelDirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name).slice(0, 20);
3625
4053
  keyFiles = entries.filter((e) => e.isFile() && !e.name.startsWith(".")).map((e) => e.name).slice(0, 15);
3626
4054
  } catch {
3627
4055
  }
3628
- const claudeMdContent = readFileOrNull(path9.join(absPath, "CLAUDE.md"));
4056
+ const claudeMdContent = readFileOrNull(path10.join(absPath, "CLAUDE.md"));
3629
4057
  const existingClaudeMd = claudeMdContent ? claudeMdContent.slice(0, SOURCE_CONTENT_LIMIT) : void 0;
3630
- const readmeContent = readFileOrNull(path9.join(absPath, "README.md"));
4058
+ const readmeContent = readFileOrNull(path10.join(absPath, "README.md"));
3631
4059
  const readmeExcerpt = readmeContent ? readmeContent.slice(0, README_CONTENT_LIMIT) : void 0;
3632
4060
  const gitRemoteUrl = getGitRemoteUrl(absPath);
3633
4061
  return {
3634
- name: packageName || path9.basename(absPath),
4062
+ name: packageName || path10.basename(absPath),
3635
4063
  type: "repo",
3636
4064
  role: config.role || "related-repo",
3637
4065
  description: config.description || "",
@@ -3648,7 +4076,7 @@ function collectFileSummary(resolved, projectDir) {
3648
4076
  const { config, origin, absPath } = resolved;
3649
4077
  const content = readFileOrNull(absPath);
3650
4078
  return {
3651
- name: path9.basename(absPath),
4079
+ name: path10.basename(absPath),
3652
4080
  type: "file",
3653
4081
  role: config.role || "reference-doc",
3654
4082
  description: config.description || content?.slice(0, 100).split("\n")[0] || "",
@@ -4287,39 +4715,52 @@ ${f.content}
4287
4715
  }
4288
4716
 
4289
4717
  // src/writers/index.ts
4290
- import fs18 from "fs";
4718
+ import fs19 from "fs";
4291
4719
 
4292
4720
  // src/writers/claude/index.ts
4293
- import fs11 from "fs";
4294
- import path10 from "path";
4721
+ import fs12 from "fs";
4722
+ import path11 from "path";
4295
4723
 
4296
4724
  // src/writers/pre-commit-block.ts
4297
4725
  init_resolve_caliber();
4298
4726
  var BLOCK_START = "<!-- caliber:managed:pre-commit -->";
4299
4727
  var BLOCK_END = "<!-- /caliber:managed:pre-commit -->";
4300
- var MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .github/copilot-instructions.md AGENTS.md CALIBER_LEARNINGS.md";
4728
+ var MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .cursorrules .github/copilot-instructions.md .github/instructions/ AGENTS.md CALIBER_LEARNINGS.md";
4301
4729
  function getPreCommitBlock() {
4302
4730
  const bin = resolveCaliber();
4303
4731
  return `${BLOCK_START}
4304
4732
  ## Before Committing
4305
4733
 
4306
- Run \`${bin} refresh\` before creating git commits to keep docs in sync with code changes.
4307
- After it completes, stage any modified doc files before committing:
4734
+ **IMPORTANT:** Before every git commit, you MUST ensure Caliber syncs agent configs with the latest code changes.
4308
4735
 
4736
+ First, check if the pre-commit hook is already installed:
4309
4737
  \`\`\`bash
4310
- ${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null
4738
+ grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"
4311
4739
  \`\`\`
4740
+
4741
+ - If **hook-active**: the hook handles sync automatically \u2014 just commit normally. Tell the user: "Caliber will sync your agent configs automatically via the pre-commit hook."
4742
+ - If **no-hook**: run Caliber manually before committing:
4743
+ 1. Tell the user: "Caliber: Syncing agent configs with your latest changes..."
4744
+ 2. Run: \`${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null\`
4745
+ 3. After it completes, briefly tell the user what Caliber updated. Then proceed with the commit.
4746
+
4747
+ If \`${bin}\` is not found, tell the user: "This project uses Caliber for agent config sync. Install it with: npm install -g @rely-ai/caliber"
4312
4748
  ${BLOCK_END}`;
4313
4749
  }
4314
4750
  var CURSOR_RULE_FILENAME = "caliber-pre-commit.mdc";
4315
4751
  function getCursorRuleContent() {
4316
4752
  const bin = resolveCaliber();
4317
4753
  return `---
4318
- description: Run caliber refresh before git commits to keep docs in sync
4754
+ description: Run Caliber sync before git commits to keep agent configs up to date
4319
4755
  alwaysApply: true
4320
4756
  ---
4321
- Before creating git commits, run \`${bin} refresh\` to update docs based on code changes,
4322
- then stage modified doc files (${MANAGED_DOC_PATHS}).
4757
+ Before every git commit, check if the Caliber pre-commit hook is installed:
4758
+ Run: \`grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"\`
4759
+
4760
+ If hook-active: just commit normally \u2014 Caliber syncs automatically via the hook. Tell the user: "Caliber will sync your agent configs automatically."
4761
+ If no-hook: run \`${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null\` before committing, and tell the user what was updated.
4762
+
4763
+ If \`${bin}\` is not found, tell the user: "This project uses Caliber for agent config sync. Install with: npm install -g @rely-ai/caliber"
4323
4764
  `;
4324
4765
  }
4325
4766
  function hasPreCommitBlock(content) {
@@ -4360,17 +4801,52 @@ function appendLearningsBlock(content) {
4360
4801
  function getCursorLearningsRule() {
4361
4802
  return { filename: CURSOR_LEARNINGS_FILENAME, content: CURSOR_LEARNINGS_CONTENT };
4362
4803
  }
4804
+ var SYNC_BLOCK_START = "<!-- caliber:managed:sync -->";
4805
+ var SYNC_BLOCK_END = "<!-- /caliber:managed:sync -->";
4806
+ function getSyncBlock() {
4807
+ const bin = resolveCaliber();
4808
+ return `${SYNC_BLOCK_START}
4809
+ ## Context Sync
4810
+
4811
+ This project uses [Caliber](https://github.com/rely-ai/caliber) to keep AI agent configs in sync across Claude Code, Cursor, Copilot, and Codex.
4812
+ Configs update automatically before each commit via \`${bin} refresh\`.
4813
+ If the pre-commit hook is not set up, run \`${bin} hooks --install\` to enable automatic sync.
4814
+ ${SYNC_BLOCK_END}`;
4815
+ }
4816
+ function hasSyncBlock(content) {
4817
+ return content.includes(SYNC_BLOCK_START);
4818
+ }
4819
+ function appendSyncBlock(content) {
4820
+ if (hasSyncBlock(content)) return content;
4821
+ const trimmed = content.trimEnd();
4822
+ return trimmed + "\n\n" + getSyncBlock() + "\n";
4823
+ }
4824
+ var CURSOR_SYNC_FILENAME = "caliber-sync.mdc";
4825
+ function getCursorSyncContent() {
4826
+ const bin = resolveCaliber();
4827
+ return `---
4828
+ description: This project uses Caliber for automatic AI agent context sync
4829
+ alwaysApply: true
4830
+ ---
4831
+ This project uses Caliber to keep AI agent configs (CLAUDE.md, Cursor rules, Copilot instructions)
4832
+ in sync with the codebase automatically. Before each commit, run \`${bin} refresh\` to sync configs.
4833
+ If \`${bin}\` is not found, tell the user: "This project uses Caliber for agent config sync. Install with: npm install -g @rely-ai/caliber"
4834
+ `;
4835
+ }
4836
+ function getCursorSyncRule() {
4837
+ return { filename: CURSOR_SYNC_FILENAME, content: getCursorSyncContent() };
4838
+ }
4363
4839
 
4364
4840
  // src/writers/claude/index.ts
4365
4841
  function writeClaudeConfig(config) {
4366
4842
  const written = [];
4367
- fs11.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(config.claudeMd)));
4843
+ fs12.writeFileSync("CLAUDE.md", appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(config.claudeMd))));
4368
4844
  written.push("CLAUDE.md");
4369
4845
  if (config.skills?.length) {
4370
4846
  for (const skill of config.skills) {
4371
- const skillDir = path10.join(".claude", "skills", skill.name);
4372
- if (!fs11.existsSync(skillDir)) fs11.mkdirSync(skillDir, { recursive: true });
4373
- const skillPath = path10.join(skillDir, "SKILL.md");
4847
+ const skillDir = path11.join(".claude", "skills", skill.name);
4848
+ if (!fs12.existsSync(skillDir)) fs12.mkdirSync(skillDir, { recursive: true });
4849
+ const skillPath = path11.join(skillDir, "SKILL.md");
4374
4850
  const frontmatter = [
4375
4851
  "---",
4376
4852
  `name: ${skill.name}`,
@@ -4378,50 +4854,51 @@ function writeClaudeConfig(config) {
4378
4854
  "---",
4379
4855
  ""
4380
4856
  ].join("\n");
4381
- fs11.writeFileSync(skillPath, frontmatter + skill.content);
4857
+ fs12.writeFileSync(skillPath, frontmatter + skill.content);
4382
4858
  written.push(skillPath);
4383
4859
  }
4384
4860
  }
4385
4861
  if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
4386
4862
  let existingServers = {};
4387
4863
  try {
4388
- if (fs11.existsSync(".mcp.json")) {
4389
- const existing = JSON.parse(fs11.readFileSync(".mcp.json", "utf-8"));
4864
+ if (fs12.existsSync(".mcp.json")) {
4865
+ const existing = JSON.parse(fs12.readFileSync(".mcp.json", "utf-8"));
4390
4866
  if (existing.mcpServers) existingServers = existing.mcpServers;
4391
4867
  }
4392
4868
  } catch {
4393
4869
  }
4394
4870
  const mergedServers = { ...existingServers, ...config.mcpServers };
4395
- fs11.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
4871
+ fs12.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
4396
4872
  written.push(".mcp.json");
4397
4873
  }
4398
4874
  return written;
4399
4875
  }
4400
4876
 
4401
4877
  // src/writers/cursor/index.ts
4402
- import fs12 from "fs";
4403
- import path11 from "path";
4878
+ import fs13 from "fs";
4879
+ import path12 from "path";
4404
4880
  function writeCursorConfig(config) {
4405
4881
  const written = [];
4406
4882
  if (config.cursorrules) {
4407
- fs12.writeFileSync(".cursorrules", config.cursorrules);
4883
+ fs13.writeFileSync(".cursorrules", config.cursorrules);
4408
4884
  written.push(".cursorrules");
4409
4885
  }
4410
4886
  const preCommitRule = getCursorPreCommitRule();
4411
4887
  const learningsRule = getCursorLearningsRule();
4412
- const allRules = [...config.rules || [], preCommitRule, learningsRule];
4413
- const rulesDir = path11.join(".cursor", "rules");
4414
- if (!fs12.existsSync(rulesDir)) fs12.mkdirSync(rulesDir, { recursive: true });
4888
+ const syncRule = getCursorSyncRule();
4889
+ const allRules = [...config.rules || [], preCommitRule, learningsRule, syncRule];
4890
+ const rulesDir = path12.join(".cursor", "rules");
4891
+ if (!fs13.existsSync(rulesDir)) fs13.mkdirSync(rulesDir, { recursive: true });
4415
4892
  for (const rule of allRules) {
4416
- const rulePath = path11.join(rulesDir, rule.filename);
4417
- fs12.writeFileSync(rulePath, rule.content);
4893
+ const rulePath = path12.join(rulesDir, rule.filename);
4894
+ fs13.writeFileSync(rulePath, rule.content);
4418
4895
  written.push(rulePath);
4419
4896
  }
4420
4897
  if (config.skills?.length) {
4421
4898
  for (const skill of config.skills) {
4422
- const skillDir = path11.join(".cursor", "skills", skill.name);
4423
- if (!fs12.existsSync(skillDir)) fs12.mkdirSync(skillDir, { recursive: true });
4424
- const skillPath = path11.join(skillDir, "SKILL.md");
4899
+ const skillDir = path12.join(".cursor", "skills", skill.name);
4900
+ if (!fs13.existsSync(skillDir)) fs13.mkdirSync(skillDir, { recursive: true });
4901
+ const skillPath = path12.join(skillDir, "SKILL.md");
4425
4902
  const frontmatter = [
4426
4903
  "---",
4427
4904
  `name: ${skill.name}`,
@@ -4429,41 +4906,41 @@ function writeCursorConfig(config) {
4429
4906
  "---",
4430
4907
  ""
4431
4908
  ].join("\n");
4432
- fs12.writeFileSync(skillPath, frontmatter + skill.content);
4909
+ fs13.writeFileSync(skillPath, frontmatter + skill.content);
4433
4910
  written.push(skillPath);
4434
4911
  }
4435
4912
  }
4436
4913
  if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
4437
4914
  const cursorDir = ".cursor";
4438
- if (!fs12.existsSync(cursorDir)) fs12.mkdirSync(cursorDir, { recursive: true });
4439
- const mcpPath = path11.join(cursorDir, "mcp.json");
4915
+ if (!fs13.existsSync(cursorDir)) fs13.mkdirSync(cursorDir, { recursive: true });
4916
+ const mcpPath = path12.join(cursorDir, "mcp.json");
4440
4917
  let existingServers = {};
4441
4918
  try {
4442
- if (fs12.existsSync(mcpPath)) {
4443
- const existing = JSON.parse(fs12.readFileSync(mcpPath, "utf-8"));
4919
+ if (fs13.existsSync(mcpPath)) {
4920
+ const existing = JSON.parse(fs13.readFileSync(mcpPath, "utf-8"));
4444
4921
  if (existing.mcpServers) existingServers = existing.mcpServers;
4445
4922
  }
4446
4923
  } catch {
4447
4924
  }
4448
4925
  const mergedServers = { ...existingServers, ...config.mcpServers };
4449
- fs12.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
4926
+ fs13.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
4450
4927
  written.push(mcpPath);
4451
4928
  }
4452
4929
  return written;
4453
4930
  }
4454
4931
 
4455
4932
  // src/writers/codex/index.ts
4456
- import fs13 from "fs";
4457
- import path12 from "path";
4933
+ import fs14 from "fs";
4934
+ import path13 from "path";
4458
4935
  function writeCodexConfig(config) {
4459
4936
  const written = [];
4460
- fs13.writeFileSync("AGENTS.md", appendLearningsBlock(appendPreCommitBlock(config.agentsMd)));
4937
+ fs14.writeFileSync("AGENTS.md", appendLearningsBlock(appendPreCommitBlock(config.agentsMd)));
4461
4938
  written.push("AGENTS.md");
4462
4939
  if (config.skills?.length) {
4463
4940
  for (const skill of config.skills) {
4464
- const skillDir = path12.join(".agents", "skills", skill.name);
4465
- if (!fs13.existsSync(skillDir)) fs13.mkdirSync(skillDir, { recursive: true });
4466
- const skillPath = path12.join(skillDir, "SKILL.md");
4941
+ const skillDir = path13.join(".agents", "skills", skill.name);
4942
+ if (!fs14.existsSync(skillDir)) fs14.mkdirSync(skillDir, { recursive: true });
4943
+ const skillPath = path13.join(skillDir, "SKILL.md");
4467
4944
  const frontmatter = [
4468
4945
  "---",
4469
4946
  `name: ${skill.name}`,
@@ -4471,7 +4948,7 @@ function writeCodexConfig(config) {
4471
4948
  "---",
4472
4949
  ""
4473
4950
  ].join("\n");
4474
- fs13.writeFileSync(skillPath, frontmatter + skill.content);
4951
+ fs14.writeFileSync(skillPath, frontmatter + skill.content);
4475
4952
  written.push(skillPath);
4476
4953
  }
4477
4954
  }
@@ -4479,242 +4956,85 @@ function writeCodexConfig(config) {
4479
4956
  }
4480
4957
 
4481
4958
  // src/writers/github-copilot/index.ts
4482
- import fs14 from "fs";
4483
- import path13 from "path";
4484
- function writeGithubCopilotConfig(config) {
4485
- const written = [];
4486
- if (config.instructions) {
4487
- fs14.mkdirSync(".github", { recursive: true });
4488
- fs14.writeFileSync(path13.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(config.instructions)));
4489
- written.push(".github/copilot-instructions.md");
4490
- }
4491
- if (config.instructionFiles?.length) {
4492
- const instructionsDir = path13.join(".github", "instructions");
4493
- fs14.mkdirSync(instructionsDir, { recursive: true });
4494
- for (const file of config.instructionFiles) {
4495
- fs14.writeFileSync(path13.join(instructionsDir, file.filename), file.content);
4496
- written.push(`.github/instructions/${file.filename}`);
4497
- }
4498
- }
4499
- return written;
4500
- }
4501
-
4502
- // src/writers/backup.ts
4503
4959
  import fs15 from "fs";
4504
- import path14 from "path";
4505
- function createBackup(files) {
4506
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4507
- const backupDir = path14.join(BACKUPS_DIR, timestamp);
4508
- for (const file of files) {
4509
- if (!fs15.existsSync(file)) continue;
4510
- const dest = path14.join(backupDir, file);
4511
- const destDir = path14.dirname(dest);
4512
- if (!fs15.existsSync(destDir)) {
4513
- fs15.mkdirSync(destDir, { recursive: true });
4514
- }
4515
- fs15.copyFileSync(file, dest);
4516
- }
4517
- return backupDir;
4518
- }
4519
- function restoreBackup(backupDir, file) {
4520
- const backupFile = path14.join(backupDir, file);
4521
- if (!fs15.existsSync(backupFile)) return false;
4522
- const destDir = path14.dirname(file);
4523
- if (!fs15.existsSync(destDir)) {
4524
- fs15.mkdirSync(destDir, { recursive: true });
4525
- }
4526
- fs15.copyFileSync(backupFile, file);
4527
- return true;
4528
- }
4529
-
4530
- // src/lib/builtin-skills.ts
4531
- init_resolve_caliber();
4532
- import fs16 from "fs";
4533
- import path15 from "path";
4534
- function buildSkillContent(skill) {
4535
- const frontmatter = `---
4536
- name: ${skill.name}
4537
- description: ${skill.description}
4538
- ---
4539
-
4540
- `;
4541
- return frontmatter + skill.content;
4542
- }
4543
- function getFindSkillsContent() {
4544
- const bin = resolveCaliber();
4545
- return `# Find Skills
4546
-
4547
- Search the public skill registry for community-contributed skills
4548
- relevant to the user's current task and install them into this project.
4549
-
4550
- ## Instructions
4551
-
4552
- 1. Identify the key technologies, frameworks, or task types from the
4553
- user's request that might have community skills available
4554
- 2. Ask the user: "Would you like me to search for community skills
4555
- for [identified technologies]?"
4556
- 3. If the user agrees, run:
4557
- \`\`\`bash
4558
- ${bin} skills --query "<relevant terms>"
4559
- \`\`\`
4560
- This outputs the top 5 matching skills with scores and descriptions.
4561
- 4. Present the results to the user and ask which ones to install
4562
- 5. Install the selected skills:
4563
- \`\`\`bash
4564
- ${bin} skills --install <slug1>,<slug2>
4565
- \`\`\`
4566
- 6. Read the installed SKILL.md files to load them into your current
4567
- context so you can use them immediately in this session
4568
- 7. Summarize what was installed and continue with the user's task
4569
-
4570
- ## Examples
4571
-
4572
- User: "let's build a web app using React"
4573
- -> "I notice you want to work with React. Would you like me to search
4574
- for community skills that could help with React development?"
4575
- -> If yes: run \`${bin} skills --query "react frontend"\`
4576
- -> Show the user the results, ask which to install
4577
- -> Run \`${bin} skills --install <selected-slugs>\`
4578
- -> Read the installed files and continue
4579
-
4580
- User: "help me set up Docker for this project"
4581
- -> "Would you like me to search for Docker-related skills?"
4582
- -> If yes: run \`${bin} skills --query "docker deployment"\`
4583
-
4584
- User: "I need to write tests for this Python ML pipeline"
4585
- -> "Would you like me to find skills for Python ML testing?"
4586
- -> If yes: run \`${bin} skills --query "python machine-learning testing"\`
4587
-
4588
- ## When NOT to trigger
4589
-
4590
- - The user is working within an already well-configured area
4591
- - You already suggested skills for this technology in this session
4592
- - The user is in the middle of urgent debugging or time-sensitive work
4593
- - The technology is too generic (e.g. just "code" or "programming")
4594
- `;
4595
- }
4596
- function getSaveLearningContent() {
4597
- const bin = resolveCaliber();
4598
- return `# Save Learning
4599
-
4600
- Save a user's instruction or preference as a persistent learning that
4601
- will be applied in all future sessions on this project.
4602
-
4603
- ## Instructions
4604
-
4605
- 1. Detect when the user gives an instruction to remember, such as:
4606
- - "remember this", "save this", "always do X", "never do Y"
4607
- - "from now on", "going forward", "in this project we..."
4608
- - Any stated convention, preference, or rule
4609
- 2. Refine the instruction into a clean, actionable learning bullet with
4610
- an appropriate type prefix:
4611
- - \`**[convention]**\` \u2014 coding style, workflow, git conventions
4612
- - \`**[pattern]**\` \u2014 reusable code patterns
4613
- - \`**[anti-pattern]**\` \u2014 things to avoid
4614
- - \`**[preference]**\` \u2014 personal/team preferences
4615
- - \`**[context]**\` \u2014 project-specific context
4616
- 3. Show the refined learning to the user and ask for confirmation
4617
- 4. If confirmed, run:
4618
- \`\`\`bash
4619
- ${bin} learn add "<refined learning>"
4620
- \`\`\`
4621
- For personal preferences (not project-level), add \`--personal\`:
4622
- \`\`\`bash
4623
- ${bin} learn add --personal "<refined learning>"
4624
- \`\`\`
4625
- 5. Stage the learnings file for the next commit:
4626
- \`\`\`bash
4627
- git add CALIBER_LEARNINGS.md
4628
- \`\`\`
4629
-
4630
- ## Examples
4631
-
4632
- User: "when developing features, push to next branch not master, remember it"
4633
- -> Refine: \`**[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`\`
4634
- -> "I'll save this as a project learning:
4635
- **[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`
4636
- Save for future sessions?"
4637
- -> If yes: run \`${bin} learn add "**[convention]** Push feature commits to the next branch, not master"\`
4638
- -> Run \`git add CALIBER_LEARNINGS.md\`
4639
-
4640
- User: "always use bun instead of npm"
4641
- -> Refine: \`**[preference]** Use \\\`bun\\\` instead of \\\`npm\\\` for package management\`
4642
- -> Confirm and save
4643
-
4644
- User: "never use any in TypeScript, use unknown instead"
4645
- -> Refine: \`**[convention]** Use \\\`unknown\\\` instead of \\\`any\\\` in TypeScript\`
4646
- -> Confirm and save
4647
-
4648
- ## When NOT to trigger
4649
-
4650
- - The user is giving a one-time instruction for the current task only
4651
- - The instruction is too vague to be actionable
4652
- - The user explicitly says "just for now" or "only this time"
4653
- `;
4654
- }
4655
- var FIND_SKILLS_SKILL = {
4656
- name: "find-skills",
4657
- 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.",
4658
- get content() {
4659
- return getFindSkillsContent();
4660
- }
4661
- };
4662
- var SAVE_LEARNING_SKILL = {
4663
- name: "save-learning",
4664
- 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.",
4665
- get content() {
4666
- return getSaveLearningContent();
4667
- }
4668
- };
4669
- var BUILTIN_SKILLS = [FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL];
4670
- var PLATFORM_CONFIGS = [
4671
- { platformDir: ".claude", skillsDir: path15.join(".claude", "skills") },
4672
- { platformDir: ".cursor", skillsDir: path15.join(".cursor", "skills") },
4673
- { platformDir: ".agents", skillsDir: path15.join(".agents", "skills") }
4674
- ];
4675
- function ensureBuiltinSkills() {
4960
+ import path14 from "path";
4961
+ function writeGithubCopilotConfig(config) {
4676
4962
  const written = [];
4677
- for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
4678
- if (!fs16.existsSync(platformDir)) continue;
4679
- for (const skill of BUILTIN_SKILLS) {
4680
- const skillPath = path15.join(skillsDir, skill.name, "SKILL.md");
4681
- if (fs16.existsSync(skillPath)) continue;
4682
- fs16.mkdirSync(path15.dirname(skillPath), { recursive: true });
4683
- fs16.writeFileSync(skillPath, buildSkillContent(skill));
4684
- written.push(skillPath);
4963
+ if (config.instructions) {
4964
+ fs15.mkdirSync(".github", { recursive: true });
4965
+ fs15.writeFileSync(path14.join(".github", "copilot-instructions.md"), appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(config.instructions))));
4966
+ written.push(".github/copilot-instructions.md");
4967
+ }
4968
+ if (config.instructionFiles?.length) {
4969
+ const instructionsDir = path14.join(".github", "instructions");
4970
+ fs15.mkdirSync(instructionsDir, { recursive: true });
4971
+ for (const file of config.instructionFiles) {
4972
+ fs15.writeFileSync(path14.join(instructionsDir, file.filename), file.content);
4973
+ written.push(`.github/instructions/${file.filename}`);
4685
4974
  }
4686
4975
  }
4687
4976
  return written;
4688
4977
  }
4689
4978
 
4979
+ // src/writers/backup.ts
4980
+ import fs16 from "fs";
4981
+ import path15 from "path";
4982
+ function createBackup(files) {
4983
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4984
+ const backupDir = path15.join(BACKUPS_DIR, timestamp);
4985
+ for (const file of files) {
4986
+ if (!fs16.existsSync(file)) continue;
4987
+ const dest = path15.join(backupDir, file);
4988
+ const destDir = path15.dirname(dest);
4989
+ if (!fs16.existsSync(destDir)) {
4990
+ fs16.mkdirSync(destDir, { recursive: true });
4991
+ }
4992
+ fs16.copyFileSync(file, dest);
4993
+ }
4994
+ return backupDir;
4995
+ }
4996
+ function restoreBackup(backupDir, file) {
4997
+ const backupFile = path15.join(backupDir, file);
4998
+ if (!fs16.existsSync(backupFile)) return false;
4999
+ const destDir = path15.dirname(file);
5000
+ if (!fs16.existsSync(destDir)) {
5001
+ fs16.mkdirSync(destDir, { recursive: true });
5002
+ }
5003
+ fs16.copyFileSync(backupFile, file);
5004
+ return true;
5005
+ }
5006
+
5007
+ // src/writers/index.ts
5008
+ init_builtin_skills();
5009
+
4690
5010
  // src/writers/manifest.ts
4691
- import fs17 from "fs";
5011
+ import fs18 from "fs";
4692
5012
  import crypto3 from "crypto";
4693
5013
  function readManifest() {
4694
5014
  try {
4695
- if (!fs17.existsSync(MANIFEST_FILE)) return null;
4696
- return JSON.parse(fs17.readFileSync(MANIFEST_FILE, "utf-8"));
5015
+ if (!fs18.existsSync(MANIFEST_FILE)) return null;
5016
+ return JSON.parse(fs18.readFileSync(MANIFEST_FILE, "utf-8"));
4697
5017
  } catch {
4698
5018
  return null;
4699
5019
  }
4700
5020
  }
4701
5021
  function writeManifest(manifest) {
4702
- if (!fs17.existsSync(CALIBER_DIR)) {
4703
- fs17.mkdirSync(CALIBER_DIR, { recursive: true });
5022
+ if (!fs18.existsSync(CALIBER_DIR)) {
5023
+ fs18.mkdirSync(CALIBER_DIR, { recursive: true });
4704
5024
  }
4705
- fs17.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
5025
+ fs18.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
4706
5026
  }
4707
5027
  function fileChecksum(filePath) {
4708
- const content = fs17.readFileSync(filePath);
5028
+ const content = fs18.readFileSync(filePath);
4709
5029
  return crypto3.createHash("sha256").update(content).digest("hex");
4710
5030
  }
4711
5031
 
4712
5032
  // src/writers/index.ts
4713
5033
  function writeSetup(setup) {
4714
5034
  const filesToWrite = getFilesToWrite(setup);
4715
- const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs18.existsSync(f));
5035
+ const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs19.existsSync(f));
4716
5036
  const existingFiles = [
4717
- ...filesToWrite.filter((f) => fs18.existsSync(f)),
5037
+ ...filesToWrite.filter((f) => fs19.existsSync(f)),
4718
5038
  ...filesToDelete
4719
5039
  ];
4720
5040
  const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
@@ -4733,7 +5053,7 @@ function writeSetup(setup) {
4733
5053
  }
4734
5054
  const deleted = [];
4735
5055
  for (const filePath of filesToDelete) {
4736
- fs18.unlinkSync(filePath);
5056
+ fs19.unlinkSync(filePath);
4737
5057
  deleted.push(filePath);
4738
5058
  }
4739
5059
  written.push(...ensureBuiltinSkills());
@@ -4764,8 +5084,8 @@ function undoSetup() {
4764
5084
  const removed = [];
4765
5085
  for (const entry of manifest.entries) {
4766
5086
  if (entry.action === "created") {
4767
- if (fs18.existsSync(entry.path)) {
4768
- fs18.unlinkSync(entry.path);
5087
+ if (fs19.existsSync(entry.path)) {
5088
+ fs19.unlinkSync(entry.path);
4769
5089
  removed.push(entry.path);
4770
5090
  }
4771
5091
  } else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
@@ -4774,8 +5094,8 @@ function undoSetup() {
4774
5094
  }
4775
5095
  }
4776
5096
  }
4777
- if (fs18.existsSync(MANIFEST_FILE)) {
4778
- fs18.unlinkSync(MANIFEST_FILE);
5097
+ if (fs19.existsSync(MANIFEST_FILE)) {
5098
+ fs19.unlinkSync(MANIFEST_FILE);
4779
5099
  }
4780
5100
  return { restored, removed };
4781
5101
  }
@@ -4816,22 +5136,22 @@ function getFilesToWrite(setup) {
4816
5136
  }
4817
5137
  function ensureGitignore() {
4818
5138
  const gitignorePath = ".gitignore";
4819
- if (fs18.existsSync(gitignorePath)) {
4820
- const content = fs18.readFileSync(gitignorePath, "utf-8");
5139
+ if (fs19.existsSync(gitignorePath)) {
5140
+ const content = fs19.readFileSync(gitignorePath, "utf-8");
4821
5141
  if (!content.includes(".caliber/")) {
4822
- fs18.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
5142
+ fs19.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
4823
5143
  }
4824
5144
  } else {
4825
- fs18.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
5145
+ fs19.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
4826
5146
  }
4827
5147
  }
4828
5148
 
4829
5149
  // src/writers/staging.ts
4830
- import fs19 from "fs";
4831
- import path16 from "path";
4832
- var STAGED_DIR = path16.join(CALIBER_DIR, "staged");
4833
- var PROPOSED_DIR = path16.join(STAGED_DIR, "proposed");
4834
- var CURRENT_DIR = path16.join(STAGED_DIR, "current");
5150
+ import fs20 from "fs";
5151
+ import path17 from "path";
5152
+ var STAGED_DIR = path17.join(CALIBER_DIR, "staged");
5153
+ var PROPOSED_DIR = path17.join(STAGED_DIR, "proposed");
5154
+ var CURRENT_DIR = path17.join(STAGED_DIR, "current");
4835
5155
  function normalizeContent(content) {
4836
5156
  return content.split("\n").map((line) => line.trimEnd()).join("\n").replace(/\n{3,}/g, "\n\n").trim();
4837
5157
  }
@@ -4841,20 +5161,20 @@ function stageFiles(files, projectDir) {
4841
5161
  let modifiedFiles = 0;
4842
5162
  const stagedFiles = [];
4843
5163
  for (const file of files) {
4844
- const originalPath = path16.join(projectDir, file.path);
4845
- if (fs19.existsSync(originalPath)) {
4846
- const existing = fs19.readFileSync(originalPath, "utf-8");
5164
+ const originalPath = path17.join(projectDir, file.path);
5165
+ if (fs20.existsSync(originalPath)) {
5166
+ const existing = fs20.readFileSync(originalPath, "utf-8");
4847
5167
  if (normalizeContent(existing) === normalizeContent(file.content)) {
4848
5168
  continue;
4849
5169
  }
4850
5170
  }
4851
- const proposedPath = path16.join(PROPOSED_DIR, file.path);
4852
- fs19.mkdirSync(path16.dirname(proposedPath), { recursive: true });
4853
- fs19.writeFileSync(proposedPath, file.content);
4854
- if (fs19.existsSync(originalPath)) {
4855
- const currentPath = path16.join(CURRENT_DIR, file.path);
4856
- fs19.mkdirSync(path16.dirname(currentPath), { recursive: true });
4857
- fs19.copyFileSync(originalPath, currentPath);
5171
+ const proposedPath = path17.join(PROPOSED_DIR, file.path);
5172
+ fs20.mkdirSync(path17.dirname(proposedPath), { recursive: true });
5173
+ fs20.writeFileSync(proposedPath, file.content);
5174
+ if (fs20.existsSync(originalPath)) {
5175
+ const currentPath = path17.join(CURRENT_DIR, file.path);
5176
+ fs20.mkdirSync(path17.dirname(currentPath), { recursive: true });
5177
+ fs20.copyFileSync(originalPath, currentPath);
4858
5178
  modifiedFiles++;
4859
5179
  stagedFiles.push({ relativePath: file.path, proposedPath, currentPath, originalPath, isNew: false });
4860
5180
  } else {
@@ -4865,13 +5185,14 @@ function stageFiles(files, projectDir) {
4865
5185
  return { newFiles, modifiedFiles, stagedFiles };
4866
5186
  }
4867
5187
  function cleanupStaging() {
4868
- if (fs19.existsSync(STAGED_DIR)) {
4869
- fs19.rmSync(STAGED_DIR, { recursive: true, force: true });
5188
+ if (fs20.existsSync(STAGED_DIR)) {
5189
+ fs20.rmSync(STAGED_DIR, { recursive: true, force: true });
4870
5190
  }
4871
5191
  }
4872
5192
 
4873
5193
  // src/commands/setup-files.ts
4874
- import fs20 from "fs";
5194
+ init_builtin_skills();
5195
+ import fs21 from "fs";
4875
5196
  function collectSetupFiles(setup, targetAgent) {
4876
5197
  const files = [];
4877
5198
  const claude = setup.claude;
@@ -4930,14 +5251,14 @@ function collectSetupFiles(setup, targetAgent) {
4930
5251
  }
4931
5252
  }
4932
5253
  const codexTargeted = targetAgent ? targetAgent.includes("codex") : false;
4933
- if (codexTargeted && !fs20.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
5254
+ if (codexTargeted && !fs21.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
4934
5255
  const agentRefs = [];
4935
5256
  if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
4936
5257
  if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
4937
5258
  if (agentRefs.length === 0) agentRefs.push("See CLAUDE.md and .cursor/rules/ for agent configurations.");
4938
5259
  const stubContent = `# AGENTS.md
4939
5260
 
4940
- This project uses AI coding agents configured by [Caliber](https://github.com/caliber-ai-org/ai-setup).
5261
+ This project uses AI coding agents configured by [Caliber](https://github.com/rely-ai-org/caliber).
4941
5262
 
4942
5263
  ${agentRefs.join(" ")}
4943
5264
  `;
@@ -4948,9 +5269,9 @@ ${agentRefs.join(" ")}
4948
5269
 
4949
5270
  // src/lib/learning-hooks.ts
4950
5271
  init_resolve_caliber();
4951
- import fs21 from "fs";
4952
- import path17 from "path";
4953
- var SETTINGS_PATH = path17.join(".claude", "settings.json");
5272
+ import fs22 from "fs";
5273
+ import path18 from "path";
5274
+ var SETTINGS_PATH2 = path18.join(".claude", "settings.json");
4954
5275
  var HOOK_TAILS = [
4955
5276
  { event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
4956
5277
  { event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
@@ -4966,24 +5287,24 @@ function getHookConfigs() {
4966
5287
  description
4967
5288
  }));
4968
5289
  }
4969
- function readSettings() {
4970
- if (!fs21.existsSync(SETTINGS_PATH)) return {};
5290
+ function readSettings2() {
5291
+ if (!fs22.existsSync(SETTINGS_PATH2)) return {};
4971
5292
  try {
4972
- return JSON.parse(fs21.readFileSync(SETTINGS_PATH, "utf-8"));
5293
+ return JSON.parse(fs22.readFileSync(SETTINGS_PATH2, "utf-8"));
4973
5294
  } catch {
4974
5295
  return {};
4975
5296
  }
4976
5297
  }
4977
- function writeSettings(settings) {
4978
- const dir = path17.dirname(SETTINGS_PATH);
4979
- if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
4980
- fs21.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
5298
+ function writeSettings2(settings) {
5299
+ const dir = path18.dirname(SETTINGS_PATH2);
5300
+ if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
5301
+ fs22.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
4981
5302
  }
4982
5303
  function hasLearningHook(matchers, tail) {
4983
5304
  return matchers.some((entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, tail)));
4984
5305
  }
4985
5306
  function areLearningHooksInstalled() {
4986
- const settings = readSettings();
5307
+ const settings = readSettings2();
4987
5308
  if (!settings.hooks) return false;
4988
5309
  return HOOK_TAILS.every((cfg) => {
4989
5310
  const matchers = settings.hooks[cfg.event];
@@ -4994,7 +5315,7 @@ function installLearningHooks() {
4994
5315
  if (areLearningHooksInstalled()) {
4995
5316
  return { installed: false, alreadyInstalled: true };
4996
5317
  }
4997
- const settings = readSettings();
5318
+ const settings = readSettings2();
4998
5319
  if (!settings.hooks) settings.hooks = {};
4999
5320
  const configs = getHookConfigs();
5000
5321
  for (const cfg of configs) {
@@ -5008,10 +5329,10 @@ function installLearningHooks() {
5008
5329
  });
5009
5330
  }
5010
5331
  }
5011
- writeSettings(settings);
5332
+ writeSettings2(settings);
5012
5333
  return { installed: true, alreadyInstalled: false };
5013
5334
  }
5014
- var CURSOR_HOOKS_PATH = path17.join(".cursor", "hooks.json");
5335
+ var CURSOR_HOOKS_PATH = path18.join(".cursor", "hooks.json");
5015
5336
  var CURSOR_HOOK_EVENTS = [
5016
5337
  { event: "postToolUse", tail: "learn observe" },
5017
5338
  { event: "postToolUseFailure", tail: "learn observe --failure" },
@@ -5019,17 +5340,17 @@ var CURSOR_HOOK_EVENTS = [
5019
5340
  { event: "sessionEnd", tail: "learn finalize --auto" }
5020
5341
  ];
5021
5342
  function readCursorHooks() {
5022
- if (!fs21.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
5343
+ if (!fs22.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
5023
5344
  try {
5024
- return JSON.parse(fs21.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
5345
+ return JSON.parse(fs22.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
5025
5346
  } catch {
5026
5347
  return { version: 1, hooks: {} };
5027
5348
  }
5028
5349
  }
5029
5350
  function writeCursorHooks(config) {
5030
- const dir = path17.dirname(CURSOR_HOOKS_PATH);
5031
- if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
5032
- fs21.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
5351
+ const dir = path18.dirname(CURSOR_HOOKS_PATH);
5352
+ if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
5353
+ fs22.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
5033
5354
  }
5034
5355
  function hasCursorHook(entries, tail) {
5035
5356
  return entries.some((e) => isCaliberCommand(e.command, tail));
@@ -5076,7 +5397,7 @@ function removeCursorLearningHooks() {
5076
5397
  return { removed: true, notFound: false };
5077
5398
  }
5078
5399
  function removeLearningHooks() {
5079
- const settings = readSettings();
5400
+ const settings = readSettings2();
5080
5401
  if (!settings.hooks) return { removed: false, notFound: true };
5081
5402
  let removedAny = false;
5082
5403
  for (const cfg of HOOK_TAILS) {
@@ -5093,7 +5414,7 @@ function removeLearningHooks() {
5093
5414
  delete settings.hooks;
5094
5415
  }
5095
5416
  if (!removedAny) return { removed: false, notFound: true };
5096
- writeSettings(settings);
5417
+ writeSettings2(settings);
5097
5418
  return { removed: true, notFound: false };
5098
5419
  }
5099
5420
 
@@ -5101,10 +5422,10 @@ function removeLearningHooks() {
5101
5422
  init_resolve_caliber();
5102
5423
 
5103
5424
  // src/lib/state.ts
5104
- import fs22 from "fs";
5105
- import path18 from "path";
5106
- import { execSync as execSync8 } from "child_process";
5107
- var STATE_FILE = path18.join(CALIBER_DIR, ".caliber-state.json");
5425
+ import fs23 from "fs";
5426
+ import path19 from "path";
5427
+ import { execSync as execSync9 } from "child_process";
5428
+ var STATE_FILE = path19.join(CALIBER_DIR, ".caliber-state.json");
5108
5429
  function normalizeTargetAgent(value) {
5109
5430
  if (Array.isArray(value)) return value;
5110
5431
  if (typeof value === "string") {
@@ -5115,8 +5436,8 @@ function normalizeTargetAgent(value) {
5115
5436
  }
5116
5437
  function readState() {
5117
5438
  try {
5118
- if (!fs22.existsSync(STATE_FILE)) return null;
5119
- const raw = JSON.parse(fs22.readFileSync(STATE_FILE, "utf-8"));
5439
+ if (!fs23.existsSync(STATE_FILE)) return null;
5440
+ const raw = JSON.parse(fs23.readFileSync(STATE_FILE, "utf-8"));
5120
5441
  if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
5121
5442
  return raw;
5122
5443
  } catch {
@@ -5124,14 +5445,14 @@ function readState() {
5124
5445
  }
5125
5446
  }
5126
5447
  function writeState(state) {
5127
- if (!fs22.existsSync(CALIBER_DIR)) {
5128
- fs22.mkdirSync(CALIBER_DIR, { recursive: true });
5448
+ if (!fs23.existsSync(CALIBER_DIR)) {
5449
+ fs23.mkdirSync(CALIBER_DIR, { recursive: true });
5129
5450
  }
5130
- fs22.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
5451
+ fs23.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
5131
5452
  }
5132
5453
  function getCurrentHeadSha() {
5133
5454
  try {
5134
- return execSync8("git rev-parse HEAD", {
5455
+ return execSync9("git rev-parse HEAD", {
5135
5456
  encoding: "utf-8",
5136
5457
  stdio: ["pipe", "pipe", "pipe"]
5137
5458
  }).trim();
@@ -5278,11 +5599,11 @@ var POINTS_LEARNED_CONTENT = 2;
5278
5599
  var POINTS_SOURCES_CONFIGURED = 3;
5279
5600
  var POINTS_SOURCES_REFERENCED = 3;
5280
5601
  var TOKEN_BUDGET_THRESHOLDS = [
5281
- { maxTokens: 2e3, points: 6 },
5282
- { maxTokens: 3500, points: 5 },
5283
- { maxTokens: 5e3, points: 4 },
5284
- { maxTokens: 8e3, points: 2 },
5285
- { maxTokens: 12e3, points: 1 }
5602
+ { maxTokens: 5e3, points: 6 },
5603
+ { maxTokens: 8e3, points: 5 },
5604
+ { maxTokens: 12e3, points: 4 },
5605
+ { maxTokens: 16e3, points: 2 },
5606
+ { maxTokens: 24e3, points: 1 }
5286
5607
  ];
5287
5608
  var CODE_BLOCK_THRESHOLDS = [
5288
5609
  { minBlocks: 3, points: 8 },
@@ -5744,7 +6065,7 @@ function checkGrounding(dir) {
5744
6065
 
5745
6066
  // src/scoring/checks/accuracy.ts
5746
6067
  import { existsSync as existsSync4, statSync as statSync2 } from "fs";
5747
- import { execSync as execSync9 } from "child_process";
6068
+ import { execSync as execSync10 } from "child_process";
5748
6069
  import { join as join5 } from "path";
5749
6070
  init_resolve_caliber();
5750
6071
  function validateReferences(dir) {
@@ -5754,13 +6075,13 @@ function validateReferences(dir) {
5754
6075
  }
5755
6076
  function detectGitDrift(dir) {
5756
6077
  try {
5757
- execSync9("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
6078
+ execSync10("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
5758
6079
  } catch {
5759
6080
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: false };
5760
6081
  }
5761
6082
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".cursor/rules"];
5762
6083
  try {
5763
- const headTimestamp = execSync9(
6084
+ const headTimestamp = execSync10(
5764
6085
  "git log -1 --format=%ct HEAD",
5765
6086
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5766
6087
  ).trim();
@@ -5781,7 +6102,7 @@ function detectGitDrift(dir) {
5781
6102
  let latestConfigCommitHash = null;
5782
6103
  for (const file of configFiles) {
5783
6104
  try {
5784
- const hash = execSync9(
6105
+ const hash = execSync10(
5785
6106
  `git log -1 --format=%H -- "${file}"`,
5786
6107
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5787
6108
  ).trim();
@@ -5790,7 +6111,7 @@ function detectGitDrift(dir) {
5790
6111
  latestConfigCommitHash = hash;
5791
6112
  } else {
5792
6113
  try {
5793
- execSync9(
6114
+ execSync10(
5794
6115
  `git merge-base --is-ancestor ${latestConfigCommitHash} ${hash}`,
5795
6116
  { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }
5796
6117
  );
@@ -5805,12 +6126,12 @@ function detectGitDrift(dir) {
5805
6126
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: true };
5806
6127
  }
5807
6128
  try {
5808
- const countStr = execSync9(
6129
+ const countStr = execSync10(
5809
6130
  `git rev-list --count ${latestConfigCommitHash}..HEAD`,
5810
6131
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5811
6132
  ).trim();
5812
6133
  const commitsSince = parseInt(countStr, 10) || 0;
5813
- const lastDate = execSync9(
6134
+ const lastDate = execSync10(
5814
6135
  `git log -1 --format=%ci ${latestConfigCommitHash}`,
5815
6136
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5816
6137
  ).trim();
@@ -5883,12 +6204,12 @@ function checkAccuracy(dir) {
5883
6204
  // src/scoring/checks/freshness.ts
5884
6205
  init_resolve_caliber();
5885
6206
  import { existsSync as existsSync5, statSync as statSync3 } from "fs";
5886
- import { execSync as execSync10 } from "child_process";
6207
+ import { execSync as execSync11 } from "child_process";
5887
6208
  import { join as join6 } from "path";
5888
6209
  function getCommitsSinceConfigUpdate(dir) {
5889
6210
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules"];
5890
6211
  try {
5891
- const headTimestamp = execSync10(
6212
+ const headTimestamp = execSync11(
5892
6213
  "git log -1 --format=%ct HEAD",
5893
6214
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5894
6215
  ).trim();
@@ -5908,12 +6229,12 @@ function getCommitsSinceConfigUpdate(dir) {
5908
6229
  }
5909
6230
  for (const file of configFiles) {
5910
6231
  try {
5911
- const hash = execSync10(
6232
+ const hash = execSync11(
5912
6233
  `git log -1 --format=%H -- "${file}"`,
5913
6234
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5914
6235
  ).trim();
5915
6236
  if (hash) {
5916
- const countStr = execSync10(
6237
+ const countStr = execSync11(
5917
6238
  `git rev-list --count ${hash}..HEAD`,
5918
6239
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5919
6240
  ).trim();
@@ -6031,12 +6352,12 @@ function checkFreshness(dir) {
6031
6352
 
6032
6353
  // src/scoring/checks/bonus.ts
6033
6354
  import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
6034
- import { execSync as execSync11 } from "child_process";
6355
+ import { execSync as execSync12 } from "child_process";
6035
6356
  import { join as join7 } from "path";
6036
6357
  init_resolve_caliber();
6037
6358
  function hasPreCommitHook(dir) {
6038
6359
  try {
6039
- const gitDir = execSync11("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
6360
+ const gitDir = execSync12("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
6040
6361
  const hookPath = join7(gitDir, "hooks", "pre-commit");
6041
6362
  const content = readFileOrNull(hookPath);
6042
6363
  return content ? content.includes("caliber") : false;
@@ -6193,22 +6514,22 @@ function checkSources(dir) {
6193
6514
  }
6194
6515
 
6195
6516
  // src/scoring/dismissed.ts
6196
- import fs23 from "fs";
6197
- import path19 from "path";
6198
- var DISMISSED_FILE = path19.join(CALIBER_DIR, "dismissed-checks.json");
6517
+ import fs24 from "fs";
6518
+ import path20 from "path";
6519
+ var DISMISSED_FILE = path20.join(CALIBER_DIR, "dismissed-checks.json");
6199
6520
  function readDismissedChecks() {
6200
6521
  try {
6201
- if (!fs23.existsSync(DISMISSED_FILE)) return [];
6202
- return JSON.parse(fs23.readFileSync(DISMISSED_FILE, "utf-8"));
6522
+ if (!fs24.existsSync(DISMISSED_FILE)) return [];
6523
+ return JSON.parse(fs24.readFileSync(DISMISSED_FILE, "utf-8"));
6203
6524
  } catch {
6204
6525
  return [];
6205
6526
  }
6206
6527
  }
6207
6528
  function writeDismissedChecks(checks) {
6208
- if (!fs23.existsSync(CALIBER_DIR)) {
6209
- fs23.mkdirSync(CALIBER_DIR, { recursive: true });
6529
+ if (!fs24.existsSync(CALIBER_DIR)) {
6530
+ fs24.mkdirSync(CALIBER_DIR, { recursive: true });
6210
6531
  }
6211
- fs23.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
6532
+ fs24.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
6212
6533
  }
6213
6534
  function getDismissedIds() {
6214
6535
  return new Set(readDismissedChecks().map((c) => c.id));
@@ -6463,27 +6784,27 @@ import { PostHog } from "posthog-node";
6463
6784
  import chalk5 from "chalk";
6464
6785
 
6465
6786
  // src/telemetry/config.ts
6466
- import fs24 from "fs";
6467
- import path20 from "path";
6787
+ import fs25 from "fs";
6788
+ import path21 from "path";
6468
6789
  import os5 from "os";
6469
6790
  import crypto4 from "crypto";
6470
- import { execSync as execSync12 } from "child_process";
6471
- var CONFIG_DIR2 = path20.join(os5.homedir(), ".caliber");
6472
- var CONFIG_FILE2 = path20.join(CONFIG_DIR2, "config.json");
6791
+ import { execSync as execSync13 } from "child_process";
6792
+ var CONFIG_DIR2 = path21.join(os5.homedir(), ".caliber");
6793
+ var CONFIG_FILE2 = path21.join(CONFIG_DIR2, "config.json");
6473
6794
  var runtimeDisabled = false;
6474
6795
  function readConfig() {
6475
6796
  try {
6476
- if (!fs24.existsSync(CONFIG_FILE2)) return {};
6477
- return JSON.parse(fs24.readFileSync(CONFIG_FILE2, "utf-8"));
6797
+ if (!fs25.existsSync(CONFIG_FILE2)) return {};
6798
+ return JSON.parse(fs25.readFileSync(CONFIG_FILE2, "utf-8"));
6478
6799
  } catch {
6479
6800
  return {};
6480
6801
  }
6481
6802
  }
6482
6803
  function writeConfig(config) {
6483
- if (!fs24.existsSync(CONFIG_DIR2)) {
6484
- fs24.mkdirSync(CONFIG_DIR2, { recursive: true });
6804
+ if (!fs25.existsSync(CONFIG_DIR2)) {
6805
+ fs25.mkdirSync(CONFIG_DIR2, { recursive: true });
6485
6806
  }
6486
- fs24.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
6807
+ fs25.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
6487
6808
  }
6488
6809
  function getMachineId() {
6489
6810
  const config = readConfig();
@@ -6495,7 +6816,7 @@ function getMachineId() {
6495
6816
  var EMAIL_HASH_KEY = "caliber-telemetry-v1";
6496
6817
  function getGitEmailHash() {
6497
6818
  try {
6498
- const email = execSync12("git config user.email", { encoding: "utf-8" }).trim();
6819
+ const email = execSync13("git config user.email", { encoding: "utf-8" }).trim();
6499
6820
  if (!email) return void 0;
6500
6821
  return crypto4.createHmac("sha256", EMAIL_HASH_KEY).update(email).digest("hex");
6501
6822
  } catch {
@@ -7694,8 +8015,8 @@ async function runScoreRefineWithSpinner(setup, dir, sessionHistory) {
7694
8015
  }
7695
8016
 
7696
8017
  // src/lib/debug-report.ts
7697
- import fs25 from "fs";
7698
- import path21 from "path";
8018
+ import fs26 from "fs";
8019
+ import path22 from "path";
7699
8020
  var DebugReport = class {
7700
8021
  sections = [];
7701
8022
  startTime;
@@ -7764,11 +8085,11 @@ var DebugReport = class {
7764
8085
  lines.push(`| **Total** | **${formatMs(totalMs)}** |`);
7765
8086
  lines.push("");
7766
8087
  }
7767
- const dir = path21.dirname(outputPath);
7768
- if (!fs25.existsSync(dir)) {
7769
- fs25.mkdirSync(dir, { recursive: true });
8088
+ const dir = path22.dirname(outputPath);
8089
+ if (!fs26.existsSync(dir)) {
8090
+ fs26.mkdirSync(dir, { recursive: true });
7770
8091
  }
7771
- fs25.writeFileSync(outputPath, lines.join("\n"));
8092
+ fs26.writeFileSync(outputPath, lines.join("\n"));
7772
8093
  }
7773
8094
  };
7774
8095
  function formatMs(ms) {
@@ -8169,7 +8490,7 @@ import chalk11 from "chalk";
8169
8490
  import ora3 from "ora";
8170
8491
  import select5 from "@inquirer/select";
8171
8492
  import checkbox from "@inquirer/checkbox";
8172
- import fs28 from "fs";
8493
+ import fs29 from "fs";
8173
8494
 
8174
8495
  // src/ai/refine.ts
8175
8496
  async function refineSetup(currentSetup, message, conversationHistory, callbacks) {
@@ -8310,10 +8631,10 @@ init_config();
8310
8631
  init_review();
8311
8632
  function detectAgents(dir) {
8312
8633
  const agents = [];
8313
- if (fs28.existsSync(`${dir}/.claude`)) agents.push("claude");
8314
- if (fs28.existsSync(`${dir}/.cursor`)) agents.push("cursor");
8315
- if (fs28.existsSync(`${dir}/.agents`) || fs28.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
8316
- if (fs28.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
8634
+ if (fs29.existsSync(`${dir}/.claude`)) agents.push("claude");
8635
+ if (fs29.existsSync(`${dir}/.cursor`)) agents.push("cursor");
8636
+ if (fs29.existsSync(`${dir}/.agents`) || fs29.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
8637
+ if (fs29.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
8317
8638
  return agents;
8318
8639
  }
8319
8640
  async function promptAgent(detected) {
@@ -8435,18 +8756,18 @@ async function refineLoop(currentSetup, sessionHistory, summarizeSetup2, printSu
8435
8756
 
8436
8757
  // src/commands/init-display.ts
8437
8758
  import chalk12 from "chalk";
8438
- import fs29 from "fs";
8759
+ import fs30 from "fs";
8439
8760
  function formatWhatChanged(setup) {
8440
8761
  const lines = [];
8441
8762
  const claude = setup.claude;
8442
8763
  const codex = setup.codex;
8443
8764
  const cursor = setup.cursor;
8444
8765
  if (claude?.claudeMd) {
8445
- const action = fs29.existsSync("CLAUDE.md") ? "Updated" : "Created";
8766
+ const action = fs30.existsSync("CLAUDE.md") ? "Updated" : "Created";
8446
8767
  lines.push(`${action} CLAUDE.md`);
8447
8768
  }
8448
8769
  if (codex?.agentsMd) {
8449
- const action = fs29.existsSync("AGENTS.md") ? "Updated" : "Created";
8770
+ const action = fs30.existsSync("AGENTS.md") ? "Updated" : "Created";
8450
8771
  lines.push(`${action} AGENTS.md`);
8451
8772
  }
8452
8773
  const allSkills = [];
@@ -8483,7 +8804,7 @@ function printSetupSummary(setup) {
8483
8804
  };
8484
8805
  if (claude) {
8485
8806
  if (claude.claudeMd) {
8486
- const icon = fs29.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
8807
+ const icon = fs30.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
8487
8808
  const desc = getDescription("CLAUDE.md");
8488
8809
  console.log(` ${icon} ${chalk12.bold("CLAUDE.md")}`);
8489
8810
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -8493,7 +8814,7 @@ function printSetupSummary(setup) {
8493
8814
  if (Array.isArray(skills) && skills.length > 0) {
8494
8815
  for (const skill of skills) {
8495
8816
  const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
8496
- const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8817
+ const icon = fs30.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8497
8818
  const desc = getDescription(skillPath);
8498
8819
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
8499
8820
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -8504,7 +8825,7 @@ function printSetupSummary(setup) {
8504
8825
  const codex = setup.codex;
8505
8826
  if (codex) {
8506
8827
  if (codex.agentsMd) {
8507
- const icon = fs29.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
8828
+ const icon = fs30.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
8508
8829
  const desc = getDescription("AGENTS.md");
8509
8830
  console.log(` ${icon} ${chalk12.bold("AGENTS.md")}`);
8510
8831
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -8514,7 +8835,7 @@ function printSetupSummary(setup) {
8514
8835
  if (Array.isArray(codexSkills) && codexSkills.length > 0) {
8515
8836
  for (const skill of codexSkills) {
8516
8837
  const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
8517
- const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8838
+ const icon = fs30.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8518
8839
  const desc = getDescription(skillPath);
8519
8840
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
8520
8841
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -8524,7 +8845,7 @@ function printSetupSummary(setup) {
8524
8845
  }
8525
8846
  if (cursor) {
8526
8847
  if (cursor.cursorrules) {
8527
- const icon = fs29.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
8848
+ const icon = fs30.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
8528
8849
  const desc = getDescription(".cursorrules");
8529
8850
  console.log(` ${icon} ${chalk12.bold(".cursorrules")}`);
8530
8851
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -8534,7 +8855,7 @@ function printSetupSummary(setup) {
8534
8855
  if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
8535
8856
  for (const skill of cursorSkills) {
8536
8857
  const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
8537
- const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8858
+ const icon = fs30.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8538
8859
  const desc = getDescription(skillPath);
8539
8860
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
8540
8861
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -8545,7 +8866,7 @@ function printSetupSummary(setup) {
8545
8866
  if (Array.isArray(rulesArr) && rulesArr.length > 0) {
8546
8867
  for (const rule of rulesArr) {
8547
8868
  const rulePath = `.cursor/rules/${rule.filename}`;
8548
- const icon = fs29.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
8869
+ const icon = fs30.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
8549
8870
  const desc = getDescription(rulePath);
8550
8871
  console.log(` ${icon} ${chalk12.bold(rulePath)}`);
8551
8872
  if (desc) {
@@ -8592,12 +8913,12 @@ function displayTokenUsage() {
8592
8913
  // src/commands/init-helpers.ts
8593
8914
  init_config();
8594
8915
  import chalk13 from "chalk";
8595
- import fs30 from "fs";
8596
- import path23 from "path";
8916
+ import fs31 from "fs";
8917
+ import path24 from "path";
8597
8918
  function isFirstRun(dir) {
8598
- const caliberDir = path23.join(dir, ".caliber");
8919
+ const caliberDir = path24.join(dir, ".caliber");
8599
8920
  try {
8600
- const stat = fs30.statSync(caliberDir);
8921
+ const stat = fs31.statSync(caliberDir);
8601
8922
  return !stat.isDirectory();
8602
8923
  } catch {
8603
8924
  return true;
@@ -8650,8 +8971,8 @@ function ensurePermissions(fingerprint) {
8650
8971
  const settingsPath = ".claude/settings.json";
8651
8972
  let settings = {};
8652
8973
  try {
8653
- if (fs30.existsSync(settingsPath)) {
8654
- settings = JSON.parse(fs30.readFileSync(settingsPath, "utf-8"));
8974
+ if (fs31.existsSync(settingsPath)) {
8975
+ settings = JSON.parse(fs31.readFileSync(settingsPath, "utf-8"));
8655
8976
  }
8656
8977
  } catch {
8657
8978
  }
@@ -8660,12 +8981,12 @@ function ensurePermissions(fingerprint) {
8660
8981
  if (Array.isArray(allow) && allow.length > 0) return;
8661
8982
  permissions.allow = derivePermissions(fingerprint);
8662
8983
  settings.permissions = permissions;
8663
- if (!fs30.existsSync(".claude")) fs30.mkdirSync(".claude", { recursive: true });
8664
- fs30.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
8984
+ if (!fs31.existsSync(".claude")) fs31.mkdirSync(".claude", { recursive: true });
8985
+ fs31.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
8665
8986
  }
8666
8987
  function writeErrorLog(config, rawOutput, error, stopReason) {
8667
8988
  try {
8668
- const logPath = path23.join(process.cwd(), ".caliber", "error-log.md");
8989
+ const logPath = path24.join(process.cwd(), ".caliber", "error-log.md");
8669
8990
  const lines = [
8670
8991
  `# Generation Error \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
8671
8992
  "",
@@ -8678,8 +8999,8 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
8678
8999
  lines.push("## Error", "```", error, "```", "");
8679
9000
  }
8680
9001
  lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
8681
- fs30.mkdirSync(path23.join(process.cwd(), ".caliber"), { recursive: true });
8682
- fs30.writeFileSync(logPath, lines.join("\n"));
9002
+ fs31.mkdirSync(path24.join(process.cwd(), ".caliber"), { recursive: true });
9003
+ fs31.writeFileSync(logPath, lines.join("\n"));
8683
9004
  console.log(chalk13.dim(`
8684
9005
  Error log written to .caliber/error-log.md`));
8685
9006
  } catch {
@@ -8730,13 +9051,13 @@ ${JSON.stringify(checkList, null, 2)}`,
8730
9051
  }
8731
9052
 
8732
9053
  // src/scoring/history.ts
8733
- import fs31 from "fs";
8734
- import path24 from "path";
9054
+ import fs32 from "fs";
9055
+ import path25 from "path";
8735
9056
  var HISTORY_FILE = "score-history.jsonl";
8736
9057
  var MAX_ENTRIES = 500;
8737
9058
  var TRIM_THRESHOLD = MAX_ENTRIES + 50;
8738
9059
  function historyFilePath() {
8739
- return path24.join(CALIBER_DIR, HISTORY_FILE);
9060
+ return path25.join(CALIBER_DIR, HISTORY_FILE);
8740
9061
  }
8741
9062
  function recordScore(result, trigger) {
8742
9063
  const entry = {
@@ -8747,14 +9068,14 @@ function recordScore(result, trigger) {
8747
9068
  trigger
8748
9069
  };
8749
9070
  try {
8750
- fs31.mkdirSync(CALIBER_DIR, { recursive: true });
9071
+ fs32.mkdirSync(CALIBER_DIR, { recursive: true });
8751
9072
  const filePath = historyFilePath();
8752
- fs31.appendFileSync(filePath, JSON.stringify(entry) + "\n");
8753
- const stat = fs31.statSync(filePath);
9073
+ fs32.appendFileSync(filePath, JSON.stringify(entry) + "\n");
9074
+ const stat = fs32.statSync(filePath);
8754
9075
  if (stat.size > TRIM_THRESHOLD * 120) {
8755
- const lines = fs31.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9076
+ const lines = fs32.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
8756
9077
  if (lines.length > MAX_ENTRIES) {
8757
- fs31.writeFileSync(filePath, lines.slice(-MAX_ENTRIES).join("\n") + "\n");
9078
+ fs32.writeFileSync(filePath, lines.slice(-MAX_ENTRIES).join("\n") + "\n");
8758
9079
  }
8759
9080
  }
8760
9081
  } catch {
@@ -8763,7 +9084,7 @@ function recordScore(result, trigger) {
8763
9084
  function readScoreHistory() {
8764
9085
  const filePath = historyFilePath();
8765
9086
  try {
8766
- const content = fs31.readFileSync(filePath, "utf-8");
9087
+ const content = fs32.readFileSync(filePath, "utf-8");
8767
9088
  const entries = [];
8768
9089
  for (const line of content.split("\n")) {
8769
9090
  if (!line) continue;
@@ -8809,13 +9130,13 @@ async function initCommand(options) {
8809
9130
  \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551
8810
9131
  \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
8811
9132
  `));
8812
- console.log(chalk14.dim(" Scan your project and generate tailored config files for"));
8813
- console.log(chalk14.dim(" Claude Code, Cursor, Codex, and GitHub Copilot.\n"));
9133
+ console.log(chalk14.dim(" Keep your AI agent configs in sync \u2014 automatically."));
9134
+ console.log(chalk14.dim(" Works across Claude Code, Cursor, Codex, and GitHub Copilot.\n"));
8814
9135
  console.log(title.bold(" How it works:\n"));
8815
9136
  console.log(chalk14.dim(" 1. Connect Link your LLM provider and select your agents"));
8816
- console.log(chalk14.dim(" 2. Engine Detect stack, generate configs & skills in parallel"));
8817
- console.log(chalk14.dim(" 3. Review See all changes \u2014 accept, refine, or decline"));
8818
- console.log(chalk14.dim(" 4. Finalize Score check and auto-sync hooks\n"));
9137
+ console.log(chalk14.dim(" 2. Setup Detect stack, install sync hooks & skills"));
9138
+ console.log(chalk14.dim(" 3. Generate Audit existing config or generate from scratch"));
9139
+ console.log(chalk14.dim(" 4. Finalize Review changes and score your setup\n"));
8819
9140
  } else {
8820
9141
  console.log(brand.bold("\n CALIBER") + chalk14.dim(" \u2014 regenerating config\n"));
8821
9142
  }
@@ -8865,8 +9186,26 @@ async function initCommand(options) {
8865
9186
  console.log(chalk14.dim(` Target: ${targetAgent.join(", ")}
8866
9187
  `));
8867
9188
  trackInitAgentSelected(targetAgent, agentAutoDetected);
9189
+ console.log(title.bold(" Step 2/4 \u2014 Setup\n"));
9190
+ const hookResult = installPreCommitHook();
9191
+ if (hookResult.installed) {
9192
+ console.log(` ${chalk14.green("\u2713")} Pre-commit hook \u2014 configs sync on every commit`);
9193
+ } else if (hookResult.alreadyInstalled) {
9194
+ console.log(` ${chalk14.green("\u2713")} Pre-commit hook \u2014 already installed`);
9195
+ }
9196
+ const { ensureBuiltinSkills: ensureBuiltinSkills2 } = await Promise.resolve().then(() => (init_builtin_skills(), builtin_skills_exports));
9197
+ for (const agent of targetAgent) {
9198
+ if (agent === "claude" && !fs33.existsSync(".claude")) fs33.mkdirSync(".claude", { recursive: true });
9199
+ if (agent === "cursor" && !fs33.existsSync(".cursor")) fs33.mkdirSync(".cursor", { recursive: true });
9200
+ if (agent === "codex" && !fs33.existsSync(".agents")) fs33.mkdirSync(".agents", { recursive: true });
9201
+ }
9202
+ const skillsWritten = ensureBuiltinSkills2();
9203
+ if (skillsWritten.length > 0) {
9204
+ console.log(` ${chalk14.green("\u2713")} Agent skills \u2014 ${skillsWritten.length} skills installed (setup-caliber, find-skills, save-learning)`);
9205
+ }
9206
+ console.log("");
8868
9207
  let baselineScore = computeLocalScore(process.cwd(), targetAgent);
8869
- console.log(chalk14.dim("\n Current config score:"));
9208
+ console.log(chalk14.dim(" Current config score:"));
8870
9209
  displayScoreSummary(baselineScore);
8871
9210
  if (options.verbose) {
8872
9211
  for (const c of baselineScore.checks) {
@@ -8893,29 +9232,37 @@ async function initCommand(options) {
8893
9232
  ]);
8894
9233
  const passingCount = baselineScore.checks.filter((c) => c.passed).length;
8895
9234
  const failingCount = baselineScore.checks.filter((c) => !c.passed).length;
9235
+ let skipGeneration = false;
8896
9236
  if (hasExistingConfig && baselineScore.score === 100) {
8897
9237
  trackInitScoreComputed(baselineScore.score, passingCount, failingCount, true);
8898
- console.log(chalk14.bold.green(" Your config is already optimal \u2014 nothing to change.\n"));
8899
- console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to regenerate anyway.\n"));
8900
- if (!options.force) return;
9238
+ console.log(chalk14.bold.green("\n Your config is already optimal.\n"));
9239
+ skipGeneration = !options.force;
9240
+ } else if (hasExistingConfig && !options.force && !options.autoApprove) {
9241
+ trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
9242
+ const auditAnswer = await promptInput(" Want Caliber to audit and improve your existing config? (Y/n) ");
9243
+ skipGeneration = auditAnswer.toLowerCase() === "n";
9244
+ } else if (!hasExistingConfig && !options.force && !options.autoApprove) {
9245
+ trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
9246
+ const generateAnswer = await promptInput(" Want Caliber to generate agent configs for your project? (Y/n) ");
9247
+ skipGeneration = generateAnswer.toLowerCase() === "n";
9248
+ } else {
9249
+ trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
9250
+ }
9251
+ if (skipGeneration) {
9252
+ const sha2 = getCurrentHeadSha();
9253
+ writeState({
9254
+ lastRefreshSha: sha2 ?? "",
9255
+ lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
9256
+ targetAgent
9257
+ });
9258
+ console.log(chalk14.bold.green("\n Caliber sync is set up!\n"));
9259
+ console.log(chalk14.dim(" Your agent configs will sync automatically on every commit."));
9260
+ console.log(chalk14.dim(" Run ") + title(`${bin} init --force`) + chalk14.dim(" anytime to generate or improve configs.\n"));
9261
+ return;
8901
9262
  }
8902
9263
  const allFailingChecks = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
8903
9264
  const llmFixableChecks = allFailingChecks.filter((c) => !NON_LLM_CHECKS.has(c.id));
8904
- trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
8905
- if (hasExistingConfig && llmFixableChecks.length === 0 && allFailingChecks.length > 0 && !options.force) {
8906
- console.log(chalk14.bold.green("\n Your config is fully optimized for LLM generation.\n"));
8907
- console.log(chalk14.dim(" Remaining items need CLI actions:\n"));
8908
- for (const check of allFailingChecks) {
8909
- console.log(chalk14.dim(` \u2022 ${check.name}`));
8910
- if (check.suggestion) {
8911
- console.log(` ${chalk14.hex("#83D1EB")(check.suggestion)}`);
8912
- }
8913
- }
8914
- console.log("");
8915
- console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to regenerate anyway.\n"));
8916
- return;
8917
- }
8918
- console.log(title.bold(" Step 2/4 \u2014 Engine\n"));
9265
+ console.log(title.bold("\n Step 3/4 \u2014 Generate\n"));
8919
9266
  const genModelInfo = fastModel ? ` Using ${displayModel} for docs, ${fastModel} for skills` : ` Using ${displayModel}`;
8920
9267
  console.log(chalk14.dim(genModelInfo + "\n"));
8921
9268
  if (report) report.markStep("Generation");
@@ -9114,7 +9461,7 @@ async function initCommand(options) {
9114
9461
  report.addJson("Generation: Parsed Config", generatedSetup);
9115
9462
  }
9116
9463
  log(options.verbose, `Generation completed: ${elapsedMs}ms, stopReason: ${genStopReason || "end_turn"}`);
9117
- console.log(title.bold(" Step 3/4 \u2014 Review\n"));
9464
+ console.log(title.bold(" Step 4/4 \u2014 Finalize\n"));
9118
9465
  const setupFiles = collectSetupFiles(generatedSetup, targetAgent);
9119
9466
  const staged = stageFiles(setupFiles, process.cwd());
9120
9467
  const totalChanges = staged.newFiles + staged.modifiedFiles;
@@ -9177,7 +9524,6 @@ async function initCommand(options) {
9177
9524
  console.log(chalk14.dim("Declined. No files were modified."));
9178
9525
  return;
9179
9526
  }
9180
- console.log(title.bold("\n Step 4/4 \u2014 Finalize\n"));
9181
9527
  if (options.dryRun) {
9182
9528
  console.log(chalk14.yellow("\n[Dry run] Would write the following files:"));
9183
9529
  console.log(JSON.stringify(generatedSetup, null, 2));
@@ -9186,7 +9532,7 @@ async function initCommand(options) {
9186
9532
  const { default: ora9 } = await import("ora");
9187
9533
  const writeSpinner = ora9("Writing config files...").start();
9188
9534
  try {
9189
- if (targetAgent.includes("codex") && !fs32.existsSync("AGENTS.md") && !generatedSetup.codex) {
9535
+ if (targetAgent.includes("codex") && !fs33.existsSync("AGENTS.md") && !generatedSetup.codex) {
9190
9536
  const claude = generatedSetup.claude;
9191
9537
  const cursor = generatedSetup.cursor;
9192
9538
  const agentRefs = [];
@@ -9195,7 +9541,7 @@ async function initCommand(options) {
9195
9541
  if (agentRefs.length === 0) agentRefs.push("See CLAUDE.md and .cursor/rules/ for agent configurations.");
9196
9542
  const stubContent = `# AGENTS.md
9197
9543
 
9198
- This project uses AI coding agents configured by [Caliber](https://github.com/caliber-ai-org/ai-setup).
9544
+ This project uses AI coding agents configured by [Caliber](https://github.com/rely-ai-org/caliber).
9199
9545
 
9200
9546
  ${agentRefs.join(" ")}
9201
9547
  `;
@@ -9306,9 +9652,9 @@ ${agentRefs.join(" ")}
9306
9652
  if (targetAgent.includes("cursor")) installCursorLearningHooks();
9307
9653
  }
9308
9654
  }
9309
- console.log(chalk14.bold.green("\n Configuration complete!"));
9310
- console.log(chalk14.dim(" Your AI agents now understand your project's architecture, build commands,"));
9311
- console.log(chalk14.dim(" testing patterns, and conventions. All changes are backed up automatically.\n"));
9655
+ console.log(chalk14.bold.green("\n Caliber is set up!"));
9656
+ console.log(chalk14.dim(" Your AI agent configs will now stay in sync with your codebase automatically."));
9657
+ console.log(chalk14.dim(" Every commit refreshes configs for all your agents. All changes are backed up.\n"));
9312
9658
  const done = chalk14.green("\u2713");
9313
9659
  const skip = chalk14.dim("\u2013");
9314
9660
  console.log(chalk14.bold(" What was configured:\n"));
@@ -9336,9 +9682,9 @@ ${agentRefs.join(" ")}
9336
9682
  }
9337
9683
  if (report) {
9338
9684
  report.markStep("Finished");
9339
- const reportPath = path25.join(process.cwd(), ".caliber", "debug-report.md");
9685
+ const reportPath = path26.join(process.cwd(), ".caliber", "debug-report.md");
9340
9686
  report.write(reportPath);
9341
- console.log(chalk14.dim(` Debug report written to ${path25.relative(process.cwd(), reportPath)}
9687
+ console.log(chalk14.dim(` Debug report written to ${path26.relative(process.cwd(), reportPath)}
9342
9688
  `));
9343
9689
  }
9344
9690
  }
@@ -9377,7 +9723,7 @@ function undoCommand() {
9377
9723
 
9378
9724
  // src/commands/status.ts
9379
9725
  import chalk16 from "chalk";
9380
- import fs33 from "fs";
9726
+ import fs34 from "fs";
9381
9727
  init_config();
9382
9728
  init_resolve_caliber();
9383
9729
  async function statusCommand(options) {
@@ -9406,7 +9752,7 @@ async function statusCommand(options) {
9406
9752
  }
9407
9753
  console.log(` Files managed: ${chalk16.cyan(manifest.entries.length.toString())}`);
9408
9754
  for (const entry of manifest.entries) {
9409
- const exists = fs33.existsSync(entry.path);
9755
+ const exists = fs34.existsSync(entry.path);
9410
9756
  const icon = exists ? chalk16.green("\u2713") : chalk16.red("\u2717");
9411
9757
  console.log(` ${icon} ${entry.path} (${entry.action})`);
9412
9758
  }
@@ -9563,9 +9909,9 @@ async function regenerateCommand(options) {
9563
9909
  }
9564
9910
 
9565
9911
  // src/commands/score.ts
9566
- import fs34 from "fs";
9912
+ import fs35 from "fs";
9567
9913
  import os7 from "os";
9568
- import path26 from "path";
9914
+ import path27 from "path";
9569
9915
  import { execFileSync as execFileSync2 } from "child_process";
9570
9916
  import chalk18 from "chalk";
9571
9917
  init_resolve_caliber();
@@ -9573,12 +9919,12 @@ var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".cursorrules", "CALIBER_LEARNINGS
9573
9919
  var CONFIG_DIRS = [".claude", ".cursor"];
9574
9920
  function scoreBaseRef(ref, target) {
9575
9921
  if (!/^[\w.\-\/~^@{}]+$/.test(ref)) return null;
9576
- const tmpDir = fs34.mkdtempSync(path26.join(os7.tmpdir(), "caliber-compare-"));
9922
+ const tmpDir = fs35.mkdtempSync(path27.join(os7.tmpdir(), "caliber-compare-"));
9577
9923
  try {
9578
9924
  for (const file of CONFIG_FILES) {
9579
9925
  try {
9580
9926
  const content = execFileSync2("git", ["show", `${ref}:${file}`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
9581
- fs34.writeFileSync(path26.join(tmpDir, file), content);
9927
+ fs35.writeFileSync(path27.join(tmpDir, file), content);
9582
9928
  } catch {
9583
9929
  }
9584
9930
  }
@@ -9586,10 +9932,10 @@ function scoreBaseRef(ref, target) {
9586
9932
  try {
9587
9933
  const files = execFileSync2("git", ["ls-tree", "-r", "--name-only", ref, `${dir}/`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim().split("\n").filter(Boolean);
9588
9934
  for (const file of files) {
9589
- const filePath = path26.join(tmpDir, file);
9590
- fs34.mkdirSync(path26.dirname(filePath), { recursive: true });
9935
+ const filePath = path27.join(tmpDir, file);
9936
+ fs35.mkdirSync(path27.dirname(filePath), { recursive: true });
9591
9937
  const content = execFileSync2("git", ["show", `${ref}:${file}`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
9592
- fs34.writeFileSync(filePath, content);
9938
+ fs35.writeFileSync(filePath, content);
9593
9939
  }
9594
9940
  } catch {
9595
9941
  }
@@ -9599,7 +9945,7 @@ function scoreBaseRef(ref, target) {
9599
9945
  } catch {
9600
9946
  return null;
9601
9947
  } finally {
9602
- fs34.rmSync(tmpDir, { recursive: true, force: true });
9948
+ fs35.rmSync(tmpDir, { recursive: true, force: true });
9603
9949
  }
9604
9950
  }
9605
9951
  async function scoreCommand(options) {
@@ -9661,13 +10007,13 @@ async function scoreCommand(options) {
9661
10007
  }
9662
10008
 
9663
10009
  // src/commands/refresh.ts
9664
- import fs38 from "fs";
9665
- import path30 from "path";
10010
+ import fs39 from "fs";
10011
+ import path31 from "path";
9666
10012
  import chalk19 from "chalk";
9667
10013
  import ora6 from "ora";
9668
10014
 
9669
10015
  // src/lib/git-diff.ts
9670
- import { execSync as execSync14 } from "child_process";
10016
+ import { execSync as execSync15 } from "child_process";
9671
10017
  var MAX_DIFF_BYTES = 1e5;
9672
10018
  var DOC_PATTERNS = [
9673
10019
  "CLAUDE.md",
@@ -9682,7 +10028,7 @@ function excludeArgs() {
9682
10028
  }
9683
10029
  function safeExec(cmd) {
9684
10030
  try {
9685
- return execSync14(cmd, {
10031
+ return execSync15(cmd, {
9686
10032
  encoding: "utf-8",
9687
10033
  stdio: ["pipe", "pipe", "pipe"],
9688
10034
  maxBuffer: 10 * 1024 * 1024
@@ -9740,48 +10086,48 @@ function collectDiff(lastSha) {
9740
10086
  }
9741
10087
 
9742
10088
  // src/writers/refresh.ts
9743
- import fs35 from "fs";
9744
- import path27 from "path";
10089
+ import fs36 from "fs";
10090
+ import path28 from "path";
9745
10091
  function writeRefreshDocs(docs) {
9746
10092
  const written = [];
9747
10093
  if (docs.claudeMd) {
9748
- fs35.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(docs.claudeMd)));
10094
+ fs36.writeFileSync("CLAUDE.md", appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(docs.claudeMd))));
9749
10095
  written.push("CLAUDE.md");
9750
10096
  }
9751
10097
  if (docs.readmeMd) {
9752
- fs35.writeFileSync("README.md", docs.readmeMd);
10098
+ fs36.writeFileSync("README.md", docs.readmeMd);
9753
10099
  written.push("README.md");
9754
10100
  }
9755
10101
  if (docs.cursorrules) {
9756
- fs35.writeFileSync(".cursorrules", docs.cursorrules);
10102
+ fs36.writeFileSync(".cursorrules", docs.cursorrules);
9757
10103
  written.push(".cursorrules");
9758
10104
  }
9759
10105
  if (docs.cursorRules) {
9760
- const rulesDir = path27.join(".cursor", "rules");
9761
- if (!fs35.existsSync(rulesDir)) fs35.mkdirSync(rulesDir, { recursive: true });
10106
+ const rulesDir = path28.join(".cursor", "rules");
10107
+ if (!fs36.existsSync(rulesDir)) fs36.mkdirSync(rulesDir, { recursive: true });
9762
10108
  for (const rule of docs.cursorRules) {
9763
- fs35.writeFileSync(path27.join(rulesDir, rule.filename), rule.content);
10109
+ fs36.writeFileSync(path28.join(rulesDir, rule.filename), rule.content);
9764
10110
  written.push(`.cursor/rules/${rule.filename}`);
9765
10111
  }
9766
10112
  }
9767
10113
  if (docs.claudeSkills) {
9768
- const skillsDir = path27.join(".claude", "skills");
9769
- if (!fs35.existsSync(skillsDir)) fs35.mkdirSync(skillsDir, { recursive: true });
10114
+ const skillsDir = path28.join(".claude", "skills");
10115
+ if (!fs36.existsSync(skillsDir)) fs36.mkdirSync(skillsDir, { recursive: true });
9770
10116
  for (const skill of docs.claudeSkills) {
9771
- fs35.writeFileSync(path27.join(skillsDir, skill.filename), skill.content);
10117
+ fs36.writeFileSync(path28.join(skillsDir, skill.filename), skill.content);
9772
10118
  written.push(`.claude/skills/${skill.filename}`);
9773
10119
  }
9774
10120
  }
9775
10121
  if (docs.copilotInstructions) {
9776
- fs35.mkdirSync(".github", { recursive: true });
9777
- fs35.writeFileSync(path27.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(docs.copilotInstructions)));
10122
+ fs36.mkdirSync(".github", { recursive: true });
10123
+ fs36.writeFileSync(path28.join(".github", "copilot-instructions.md"), appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(docs.copilotInstructions))));
9778
10124
  written.push(".github/copilot-instructions.md");
9779
10125
  }
9780
10126
  if (docs.copilotInstructionFiles) {
9781
- const instructionsDir = path27.join(".github", "instructions");
9782
- fs35.mkdirSync(instructionsDir, { recursive: true });
10127
+ const instructionsDir = path28.join(".github", "instructions");
10128
+ fs36.mkdirSync(instructionsDir, { recursive: true });
9783
10129
  for (const file of docs.copilotInstructionFiles) {
9784
- fs35.writeFileSync(path27.join(instructionsDir, file.filename), file.content);
10130
+ fs36.writeFileSync(path28.join(instructionsDir, file.filename), file.content);
9785
10131
  written.push(`.github/instructions/${file.filename}`);
9786
10132
  }
9787
10133
  }
@@ -9867,8 +10213,8 @@ Changed files: ${diff.changedFiles.join(", ")}`);
9867
10213
  }
9868
10214
 
9869
10215
  // src/learner/writer.ts
9870
- import fs36 from "fs";
9871
- import path28 from "path";
10216
+ import fs37 from "fs";
10217
+ import path29 from "path";
9872
10218
 
9873
10219
  // src/learner/utils.ts
9874
10220
  var TYPE_PREFIX_RE = /^\*\*\[[^\]]+\]\*\*\s*/;
@@ -9901,13 +10247,13 @@ var LEARNINGS_FILE = "CALIBER_LEARNINGS.md";
9901
10247
  var LEARNINGS_HEADER = `# Caliber Learnings
9902
10248
 
9903
10249
  Accumulated patterns and anti-patterns from development sessions.
9904
- Auto-managed by [caliber](https://github.com/caliber-ai-org/ai-setup) \u2014 do not edit manually.
10250
+ Auto-managed by [caliber](https://github.com/rely-ai-org/caliber) \u2014 do not edit manually.
9905
10251
 
9906
10252
  `;
9907
10253
  var PERSONAL_LEARNINGS_HEADER = `# Personal Learnings
9908
10254
 
9909
10255
  Developer-specific patterns and preferences.
9910
- Auto-managed by [caliber](https://github.com/caliber-ai-org/ai-setup) \u2014 do not edit manually.
10256
+ Auto-managed by [caliber](https://github.com/rely-ai-org/caliber) \u2014 do not edit manually.
9911
10257
 
9912
10258
  `;
9913
10259
  var LEARNED_START = "<!-- caliber:learned -->";
@@ -9985,20 +10331,20 @@ function deduplicateLearnedItems(existing, incoming) {
9985
10331
  }
9986
10332
  function writeLearnedSectionTo(filePath, header, existing, incoming, mode) {
9987
10333
  const { merged, newCount, newItems } = deduplicateLearnedItems(existing, incoming);
9988
- fs36.writeFileSync(filePath, header + merged + "\n");
9989
- if (mode) fs36.chmodSync(filePath, mode);
10334
+ fs37.writeFileSync(filePath, header + merged + "\n");
10335
+ if (mode) fs37.chmodSync(filePath, mode);
9990
10336
  return { newCount, newItems };
9991
10337
  }
9992
10338
  function writeLearnedSection(content) {
9993
10339
  return writeLearnedSectionTo(LEARNINGS_FILE, LEARNINGS_HEADER, readLearnedSection(), content);
9994
10340
  }
9995
10341
  function writeLearnedSkill(skill) {
9996
- const skillDir = path28.join(".claude", "skills", skill.name);
9997
- if (!fs36.existsSync(skillDir)) fs36.mkdirSync(skillDir, { recursive: true });
9998
- const skillPath = path28.join(skillDir, "SKILL.md");
9999
- if (!skill.isNew && fs36.existsSync(skillPath)) {
10000
- const existing = fs36.readFileSync(skillPath, "utf-8");
10001
- fs36.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
10342
+ const skillDir = path29.join(".claude", "skills", skill.name);
10343
+ if (!fs37.existsSync(skillDir)) fs37.mkdirSync(skillDir, { recursive: true });
10344
+ const skillPath = path29.join(skillDir, "SKILL.md");
10345
+ if (!skill.isNew && fs37.existsSync(skillPath)) {
10346
+ const existing = fs37.readFileSync(skillPath, "utf-8");
10347
+ fs37.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
10002
10348
  } else {
10003
10349
  const frontmatter = [
10004
10350
  "---",
@@ -10007,12 +10353,12 @@ function writeLearnedSkill(skill) {
10007
10353
  "---",
10008
10354
  ""
10009
10355
  ].join("\n");
10010
- fs36.writeFileSync(skillPath, frontmatter + skill.content);
10356
+ fs37.writeFileSync(skillPath, frontmatter + skill.content);
10011
10357
  }
10012
10358
  return skillPath;
10013
10359
  }
10014
10360
  function writePersonalLearnedSection(content) {
10015
- if (!fs36.existsSync(AUTH_DIR)) fs36.mkdirSync(AUTH_DIR, { recursive: true });
10361
+ if (!fs37.existsSync(AUTH_DIR)) fs37.mkdirSync(AUTH_DIR, { recursive: true });
10016
10362
  return writeLearnedSectionTo(PERSONAL_LEARNINGS_FILE, PERSONAL_LEARNINGS_HEADER, readPersonalLearnings(), content, 384);
10017
10363
  }
10018
10364
  function addLearning(bullet, scope = "project") {
@@ -10025,55 +10371,65 @@ function addLearning(bullet, scope = "project") {
10025
10371
  return { file: LEARNINGS_FILE, added: result.newCount > 0 };
10026
10372
  }
10027
10373
  function readPersonalLearnings() {
10028
- if (!fs36.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
10029
- const content = fs36.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
10374
+ if (!fs37.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
10375
+ const content = fs37.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
10030
10376
  const bullets = content.split("\n").filter((l) => l.startsWith("- ")).join("\n");
10031
10377
  return bullets || null;
10032
10378
  }
10033
10379
  function readLearnedSection() {
10034
- if (fs36.existsSync(LEARNINGS_FILE)) {
10035
- const content2 = fs36.readFileSync(LEARNINGS_FILE, "utf-8");
10380
+ if (fs37.existsSync(LEARNINGS_FILE)) {
10381
+ const content2 = fs37.readFileSync(LEARNINGS_FILE, "utf-8");
10036
10382
  const bullets = content2.split("\n").filter((l) => l.startsWith("- ")).join("\n");
10037
10383
  return bullets || null;
10038
10384
  }
10039
10385
  const claudeMdPath = "CLAUDE.md";
10040
- if (!fs36.existsSync(claudeMdPath)) return null;
10041
- const content = fs36.readFileSync(claudeMdPath, "utf-8");
10386
+ if (!fs37.existsSync(claudeMdPath)) return null;
10387
+ const content = fs37.readFileSync(claudeMdPath, "utf-8");
10042
10388
  const startIdx = content.indexOf(LEARNED_START);
10043
10389
  const endIdx = content.indexOf(LEARNED_END);
10044
10390
  if (startIdx === -1 || endIdx === -1) return null;
10045
10391
  return content.slice(startIdx + LEARNED_START.length, endIdx).trim() || null;
10046
10392
  }
10047
10393
  function migrateInlineLearnings() {
10048
- if (fs36.existsSync(LEARNINGS_FILE)) return false;
10394
+ if (fs37.existsSync(LEARNINGS_FILE)) return false;
10049
10395
  const claudeMdPath = "CLAUDE.md";
10050
- if (!fs36.existsSync(claudeMdPath)) return false;
10051
- const content = fs36.readFileSync(claudeMdPath, "utf-8");
10396
+ if (!fs37.existsSync(claudeMdPath)) return false;
10397
+ const content = fs37.readFileSync(claudeMdPath, "utf-8");
10052
10398
  const startIdx = content.indexOf(LEARNED_START);
10053
10399
  const endIdx = content.indexOf(LEARNED_END);
10054
10400
  if (startIdx === -1 || endIdx === -1) return false;
10055
10401
  const section = content.slice(startIdx + LEARNED_START.length, endIdx).trim();
10056
10402
  if (!section) return false;
10057
- fs36.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
10403
+ fs37.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
10058
10404
  const cleaned = content.slice(0, startIdx) + content.slice(endIdx + LEARNED_END.length);
10059
- fs36.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
10405
+ fs37.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
10060
10406
  return true;
10061
10407
  }
10062
10408
 
10063
10409
  // src/commands/refresh.ts
10064
10410
  init_config();
10065
10411
  init_resolve_caliber();
10412
+ init_builtin_skills();
10413
+ function detectSyncedAgents(writtenFiles) {
10414
+ const agents = [];
10415
+ const joined = writtenFiles.join(" ");
10416
+ if (joined.includes("CLAUDE.md") || joined.includes(".claude/")) agents.push("Claude Code");
10417
+ if (joined.includes(".cursor/") || joined.includes(".cursorrules")) agents.push("Cursor");
10418
+ if (joined.includes("copilot-instructions") || joined.includes(".github/instructions/")) agents.push("Copilot");
10419
+ if (joined.includes("AGENTS.md") || joined.includes(".agents/")) agents.push("Codex");
10420
+ return agents;
10421
+ }
10066
10422
  function log2(quiet, ...args) {
10067
10423
  if (!quiet) console.log(...args);
10068
10424
  }
10069
10425
  function discoverGitRepos(parentDir) {
10070
10426
  const repos = [];
10071
10427
  try {
10072
- const entries = fs38.readdirSync(parentDir, { withFileTypes: true });
10428
+ const entries = fs39.readdirSync(parentDir, { withFileTypes: true });
10073
10429
  for (const entry of entries) {
10074
10430
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
10075
- const childPath = path30.join(parentDir, entry.name);
10076
- if (fs38.existsSync(path30.join(childPath, ".git"))) {
10431
+ const childPath = path31.join(parentDir, entry.name);
10432
+ if (fs39.existsSync(path31.join(childPath, ".git"))) {
10077
10433
  repos.push(childPath);
10078
10434
  }
10079
10435
  }
@@ -10159,9 +10515,9 @@ async function refreshSingleRepo(repoDir, options) {
10159
10515
  const filesToWrite = response.docsUpdated || [];
10160
10516
  const preRefreshContents = /* @__PURE__ */ new Map();
10161
10517
  for (const filePath of filesToWrite) {
10162
- const fullPath = path30.resolve(repoDir, filePath);
10518
+ const fullPath = path31.resolve(repoDir, filePath);
10163
10519
  try {
10164
- preRefreshContents.set(filePath, fs38.readFileSync(fullPath, "utf-8"));
10520
+ preRefreshContents.set(filePath, fs39.readFileSync(fullPath, "utf-8"));
10165
10521
  } catch {
10166
10522
  preRefreshContents.set(filePath, null);
10167
10523
  }
@@ -10171,14 +10527,14 @@ async function refreshSingleRepo(repoDir, options) {
10171
10527
  const postScore = computeLocalScore(repoDir, targetAgent);
10172
10528
  if (postScore.score < preScore.score) {
10173
10529
  for (const [filePath, content] of preRefreshContents) {
10174
- const fullPath = path30.resolve(repoDir, filePath);
10530
+ const fullPath = path31.resolve(repoDir, filePath);
10175
10531
  if (content === null) {
10176
10532
  try {
10177
- fs38.unlinkSync(fullPath);
10533
+ fs39.unlinkSync(fullPath);
10178
10534
  } catch {
10179
10535
  }
10180
10536
  } else {
10181
- fs38.writeFileSync(fullPath, content);
10537
+ fs39.writeFileSync(fullPath, content);
10182
10538
  }
10183
10539
  }
10184
10540
  spinner?.warn(`${prefix}Refresh reverted \u2014 score would drop from ${preScore.score} to ${postScore.score}`);
@@ -10190,8 +10546,18 @@ async function refreshSingleRepo(repoDir, options) {
10190
10546
  }
10191
10547
  recordScore(postScore, "refresh");
10192
10548
  spinner?.succeed(`${prefix}Updated ${written.length} doc${written.length === 1 ? "" : "s"}`);
10549
+ const fileChangesMap = new Map(
10550
+ (response.fileChanges || []).map((fc) => [fc.file, fc.description])
10551
+ );
10193
10552
  for (const file of written) {
10194
- log2(quiet, ` ${chalk19.green("\u2713")} ${file}`);
10553
+ const desc = fileChangesMap.get(file);
10554
+ const suffix = desc ? chalk19.dim(` \u2014 ${desc}`) : "";
10555
+ log2(quiet, ` ${chalk19.green("\u2713")} ${file}${suffix}`);
10556
+ }
10557
+ const agents = detectSyncedAgents(written);
10558
+ if (agents.length > 1) {
10559
+ log2(quiet, chalk19.cyan(`
10560
+ ${agents.length} agent formats in sync (${agents.join(", ")})`));
10195
10561
  }
10196
10562
  if (response.changesSummary) {
10197
10563
  log2(quiet, chalk19.dim(`
@@ -10233,7 +10599,7 @@ async function refreshCommand(options) {
10233
10599
  `));
10234
10600
  const originalDir = process.cwd();
10235
10601
  for (const repo of repos) {
10236
- const repoName = path30.basename(repo);
10602
+ const repoName = path31.basename(repo);
10237
10603
  try {
10238
10604
  process.chdir(repo);
10239
10605
  await refreshSingleRepo(repo, { ...options, label: repoName });
@@ -10255,150 +10621,6 @@ async function refreshCommand(options) {
10255
10621
  // src/commands/hooks.ts
10256
10622
  import chalk20 from "chalk";
10257
10623
  import fs40 from "fs";
10258
-
10259
- // src/lib/hooks.ts
10260
- init_resolve_caliber();
10261
- import fs39 from "fs";
10262
- import path31 from "path";
10263
- import { execSync as execSync15 } from "child_process";
10264
- var SETTINGS_PATH2 = path31.join(".claude", "settings.json");
10265
- var REFRESH_TAIL = "refresh --quiet";
10266
- var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
10267
- function getHookCommand() {
10268
- return `${resolveCaliber()} ${REFRESH_TAIL}`;
10269
- }
10270
- function readSettings2() {
10271
- if (!fs39.existsSync(SETTINGS_PATH2)) return {};
10272
- try {
10273
- return JSON.parse(fs39.readFileSync(SETTINGS_PATH2, "utf-8"));
10274
- } catch {
10275
- return {};
10276
- }
10277
- }
10278
- function writeSettings2(settings) {
10279
- const dir = path31.dirname(SETTINGS_PATH2);
10280
- if (!fs39.existsSync(dir)) fs39.mkdirSync(dir, { recursive: true });
10281
- fs39.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
10282
- }
10283
- function findHookIndex(sessionEnd) {
10284
- return sessionEnd.findIndex(
10285
- (entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, REFRESH_TAIL))
10286
- );
10287
- }
10288
- function isHookInstalled() {
10289
- const settings = readSettings2();
10290
- const sessionEnd = settings.hooks?.SessionEnd;
10291
- if (!Array.isArray(sessionEnd)) return false;
10292
- return findHookIndex(sessionEnd) !== -1;
10293
- }
10294
- function installHook() {
10295
- const settings = readSettings2();
10296
- if (!settings.hooks) settings.hooks = {};
10297
- if (!Array.isArray(settings.hooks.SessionEnd)) settings.hooks.SessionEnd = [];
10298
- if (findHookIndex(settings.hooks.SessionEnd) !== -1) {
10299
- return { installed: false, alreadyInstalled: true };
10300
- }
10301
- settings.hooks.SessionEnd.push({
10302
- matcher: "",
10303
- hooks: [{ type: "command", command: getHookCommand(), description: HOOK_DESCRIPTION }]
10304
- });
10305
- writeSettings2(settings);
10306
- return { installed: true, alreadyInstalled: false };
10307
- }
10308
- function removeHook() {
10309
- const settings = readSettings2();
10310
- const sessionEnd = settings.hooks?.SessionEnd;
10311
- if (!Array.isArray(sessionEnd)) {
10312
- return { removed: false, notFound: true };
10313
- }
10314
- const idx = findHookIndex(sessionEnd);
10315
- if (idx === -1) {
10316
- return { removed: false, notFound: true };
10317
- }
10318
- sessionEnd.splice(idx, 1);
10319
- if (sessionEnd.length === 0) {
10320
- delete settings.hooks.SessionEnd;
10321
- }
10322
- if (settings.hooks && Object.keys(settings.hooks).length === 0) {
10323
- delete settings.hooks;
10324
- }
10325
- writeSettings2(settings);
10326
- return { removed: true, notFound: false };
10327
- }
10328
- var PRECOMMIT_START = "# caliber:pre-commit:start";
10329
- var PRECOMMIT_END = "# caliber:pre-commit:end";
10330
- function getPrecommitBlock() {
10331
- const bin = resolveCaliber();
10332
- const npx = isNpxResolution();
10333
- const guard = npx ? "command -v npx >/dev/null 2>&1" : `[ -x "${bin}" ] || command -v "${bin}" >/dev/null 2>&1`;
10334
- const invoke = npx ? bin : `"${bin}"`;
10335
- return `${PRECOMMIT_START}
10336
- if ${guard}; then
10337
- echo "\\033[2mcaliber: refreshing docs...\\033[0m"
10338
- ${invoke} refresh 2>/dev/null || true
10339
- ${invoke} learn finalize 2>/dev/null || true
10340
- git diff --name-only -- CLAUDE.md .claude/ .cursor/ AGENTS.md CALIBER_LEARNINGS.md 2>/dev/null | xargs git add 2>/dev/null || true
10341
- fi
10342
- ${PRECOMMIT_END}`;
10343
- }
10344
- function getGitHooksDir() {
10345
- try {
10346
- const gitDir = execSync15("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
10347
- return path31.join(gitDir, "hooks");
10348
- } catch {
10349
- return null;
10350
- }
10351
- }
10352
- function getPreCommitPath() {
10353
- const hooksDir = getGitHooksDir();
10354
- return hooksDir ? path31.join(hooksDir, "pre-commit") : null;
10355
- }
10356
- function isPreCommitHookInstalled() {
10357
- const hookPath = getPreCommitPath();
10358
- if (!hookPath || !fs39.existsSync(hookPath)) return false;
10359
- const content = fs39.readFileSync(hookPath, "utf-8");
10360
- return content.includes(PRECOMMIT_START);
10361
- }
10362
- function installPreCommitHook() {
10363
- if (isPreCommitHookInstalled()) {
10364
- return { installed: false, alreadyInstalled: true };
10365
- }
10366
- const hookPath = getPreCommitPath();
10367
- if (!hookPath) return { installed: false, alreadyInstalled: false };
10368
- const hooksDir = path31.dirname(hookPath);
10369
- if (!fs39.existsSync(hooksDir)) fs39.mkdirSync(hooksDir, { recursive: true });
10370
- let content = "";
10371
- if (fs39.existsSync(hookPath)) {
10372
- content = fs39.readFileSync(hookPath, "utf-8");
10373
- if (!content.endsWith("\n")) content += "\n";
10374
- content += "\n" + getPrecommitBlock() + "\n";
10375
- } else {
10376
- content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
10377
- }
10378
- fs39.writeFileSync(hookPath, content);
10379
- fs39.chmodSync(hookPath, 493);
10380
- return { installed: true, alreadyInstalled: false };
10381
- }
10382
- function removePreCommitHook() {
10383
- const hookPath = getPreCommitPath();
10384
- if (!hookPath || !fs39.existsSync(hookPath)) {
10385
- return { removed: false, notFound: true };
10386
- }
10387
- let content = fs39.readFileSync(hookPath, "utf-8");
10388
- if (!content.includes(PRECOMMIT_START)) {
10389
- return { removed: false, notFound: true };
10390
- }
10391
- const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
10392
- content = content.replace(regex, "\n");
10393
- if (content.trim() === "#!/bin/sh" || content.trim() === "") {
10394
- fs39.unlinkSync(hookPath);
10395
- } else {
10396
- fs39.writeFileSync(hookPath, content);
10397
- }
10398
- return { removed: true, notFound: false };
10399
- }
10400
-
10401
- // src/commands/hooks.ts
10402
10624
  var HOOKS = [
10403
10625
  {
10404
10626
  id: "session-end",