@rely-ai/caliber 1.31.0 → 1.32.0-dev.1774815754

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/bin.js +1673 -1113
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -230,21 +230,615 @@ var init_types = __esm({
230
230
  }
231
231
  });
232
232
 
233
+ // src/writers/pre-commit-block.ts
234
+ var pre_commit_block_exports = {};
235
+ __export(pre_commit_block_exports, {
236
+ appendLearningsBlock: () => appendLearningsBlock,
237
+ appendManagedBlocks: () => appendManagedBlocks,
238
+ appendPreCommitBlock: () => appendPreCommitBlock,
239
+ appendSyncBlock: () => appendSyncBlock,
240
+ getCursorLearningsRule: () => getCursorLearningsRule,
241
+ getCursorPreCommitRule: () => getCursorPreCommitRule,
242
+ getCursorSetupRule: () => getCursorSetupRule,
243
+ getCursorSyncRule: () => getCursorSyncRule,
244
+ hasLearningsBlock: () => hasLearningsBlock,
245
+ hasPreCommitBlock: () => hasPreCommitBlock,
246
+ hasSyncBlock: () => hasSyncBlock,
247
+ stripManagedBlocks: () => stripManagedBlocks
248
+ });
249
+ function getCaliberNotFoundFallback(platform, bin) {
250
+ switch (platform) {
251
+ case "claude":
252
+ return `If \`${bin}\` is not found, tell the user: "This project uses Caliber for agent config sync. Run /setup-caliber to get set up."`;
253
+ case "codex":
254
+ return `If \`${bin}\` is not found, read \`.agents/skills/setup-caliber/SKILL.md\` and follow its instructions to install Caliber.`;
255
+ case "copilot":
256
+ return `If \`${bin}\` is not found, tell the developer to set up Caliber by running \`/setup-caliber\` in Claude Code or Cursor. Alternatively, they can run these commands in their terminal:
257
+ \`\`\`
258
+ npx @rely-ai/caliber hooks --install
259
+ npx @rely-ai/caliber refresh
260
+ \`\`\``;
261
+ }
262
+ }
263
+ function getPreCommitBlock(platform = "claude") {
264
+ const bin = resolveCaliber();
265
+ return `${BLOCK_START}
266
+ ## Before Committing
267
+
268
+ **IMPORTANT:** Before every git commit, you MUST ensure Caliber syncs agent configs with the latest code changes.
269
+
270
+ First, check if the pre-commit hook is already installed:
271
+ \`\`\`bash
272
+ grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"
273
+ \`\`\`
274
+
275
+ - 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."
276
+ - If **no-hook**: run Caliber manually before committing:
277
+ 1. Tell the user: "Caliber: Syncing agent configs with your latest changes..."
278
+ 2. Run: \`${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null\`
279
+ 3. After it completes, briefly tell the user what Caliber updated. Then proceed with the commit.
280
+
281
+ ${getCaliberNotFoundFallback(platform, bin)}
282
+ ${BLOCK_END}`;
283
+ }
284
+ function getCursorRuleContent() {
285
+ const bin = resolveCaliber();
286
+ return `---
287
+ description: Run Caliber sync before git commits to keep agent configs up to date
288
+ alwaysApply: true
289
+ ---
290
+ Before every git commit, check if the Caliber pre-commit hook is installed:
291
+ Run: \`grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "hook-active" || echo "no-hook"\`
292
+
293
+ If hook-active: just commit normally \u2014 Caliber syncs automatically via the hook. Tell the user: "Caliber will sync your agent configs automatically."
294
+ If no-hook: run \`${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null\` before committing, and tell the user what was updated.
295
+
296
+ If \`${bin}\` is not found, read the setup-caliber skill from .cursor/skills/setup-caliber/SKILL.md and follow its instructions to install Caliber.
297
+ `;
298
+ }
299
+ function hasPreCommitBlock(content) {
300
+ return content.includes(BLOCK_START);
301
+ }
302
+ function appendPreCommitBlock(content, platform = "claude") {
303
+ if (hasPreCommitBlock(content)) return content;
304
+ const trimmed = content.trimEnd();
305
+ return trimmed + "\n\n" + getPreCommitBlock(platform) + "\n";
306
+ }
307
+ function getCursorPreCommitRule() {
308
+ return { filename: CURSOR_RULE_FILENAME, content: getCursorRuleContent() };
309
+ }
310
+ function hasLearningsBlock(content) {
311
+ return content.includes(LEARNINGS_BLOCK_START);
312
+ }
313
+ function appendLearningsBlock(content) {
314
+ if (hasLearningsBlock(content)) return content;
315
+ const trimmed = content.trimEnd();
316
+ return trimmed + "\n\n" + LEARNINGS_BLOCK + "\n";
317
+ }
318
+ function getCursorLearningsRule() {
319
+ return { filename: CURSOR_LEARNINGS_FILENAME, content: CURSOR_LEARNINGS_CONTENT };
320
+ }
321
+ function getSyncSetupInstruction(platform) {
322
+ switch (platform) {
323
+ case "claude":
324
+ return `If the pre-commit hook is not set up, run \`/setup-caliber\` to configure everything automatically.`;
325
+ case "codex":
326
+ return `If the pre-commit hook is not set up, read \`.agents/skills/setup-caliber/SKILL.md\` and follow the setup instructions.`;
327
+ case "copilot":
328
+ return `If the pre-commit hook is not set up, the developer should run \`/setup-caliber\` in Claude Code or Cursor for automated setup. Alternatively, run in terminal:
329
+ \`\`\`bash
330
+ npx @rely-ai/caliber hooks --install
331
+ npx @rely-ai/caliber refresh
332
+ git add ${MANAGED_DOC_PATHS} 2>/dev/null
333
+ \`\`\``;
334
+ }
335
+ }
336
+ function getSyncBlock(platform = "claude") {
337
+ const bin = resolveCaliber();
338
+ return `${SYNC_BLOCK_START}
339
+ ## Context Sync
340
+
341
+ This project uses [Caliber](https://github.com/caliber-ai-org/ai-setup) to keep AI agent configs in sync across Claude Code, Cursor, Copilot, and Codex.
342
+ Configs update automatically before each commit via \`${bin} refresh\`.
343
+ ${getSyncSetupInstruction(platform)}
344
+ ${SYNC_BLOCK_END}`;
345
+ }
346
+ function hasSyncBlock(content) {
347
+ return content.includes(SYNC_BLOCK_START);
348
+ }
349
+ function appendSyncBlock(content, platform = "claude") {
350
+ if (hasSyncBlock(content)) return content;
351
+ const trimmed = content.trimEnd();
352
+ return trimmed + "\n\n" + getSyncBlock(platform) + "\n";
353
+ }
354
+ function appendManagedBlocks(content, platform = "claude") {
355
+ return appendSyncBlock(appendLearningsBlock(appendPreCommitBlock(content, platform)), platform);
356
+ }
357
+ function getCursorSyncContent() {
358
+ const bin = resolveCaliber();
359
+ return `---
360
+ description: This project uses Caliber for automatic AI agent context sync
361
+ alwaysApply: true
362
+ ---
363
+ This project uses Caliber to keep AI agent configs (CLAUDE.md, Cursor rules, Copilot instructions)
364
+ in sync with the codebase automatically. Before each commit, run \`${bin} refresh\` to sync configs.
365
+ If \`${bin}\` is not found, read the setup-caliber skill from .cursor/skills/setup-caliber/SKILL.md and follow its instructions to install Caliber.
366
+ `;
367
+ }
368
+ function getCursorSyncRule() {
369
+ return { filename: CURSOR_SYNC_FILENAME, content: getCursorSyncContent() };
370
+ }
371
+ function getCursorSetupContent() {
372
+ return `---
373
+ description: Check if Caliber agent config sync is set up and offer installation on new sessions
374
+ alwaysApply: true
375
+ ---
376
+ This project uses Caliber to keep AI agent configs in sync automatically.
377
+ At the start of each session, silently check if sync is active:
378
+
379
+ Run: \`grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "SYNC_ACTIVE" || echo "NO_SYNC"\`
380
+
381
+ If SYNC_ACTIVE: No action needed \u2014 Caliber syncs automatically on every commit.
382
+ If NO_SYNC: Tell the user: "This project uses Caliber for automatic agent config sync, but it's not set up on your machine yet. Want me to set it up? It takes about 30 seconds."
383
+
384
+ If the user agrees, read \`.cursor/skills/setup-caliber/SKILL.md\` and follow its instructions step by step.
385
+ `;
386
+ }
387
+ function getCursorSetupRule() {
388
+ return { filename: CURSOR_SETUP_FILENAME, content: getCursorSetupContent() };
389
+ }
390
+ function stripManagedBlocks(content) {
391
+ let result = content;
392
+ for (const [start, end] of MANAGED_BLOCK_PAIRS) {
393
+ const regex = new RegExp(`\\n?${start.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[\\s\\S]*?${end.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n?`, "g");
394
+ result = result.replace(regex, "\n");
395
+ }
396
+ return result.replace(/\n{3,}/g, "\n\n").trim() + "\n";
397
+ }
398
+ var BLOCK_START, BLOCK_END, MANAGED_DOC_PATHS, CURSOR_RULE_FILENAME, LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END, LEARNINGS_BLOCK, CURSOR_LEARNINGS_FILENAME, CURSOR_LEARNINGS_CONTENT, SYNC_BLOCK_START, SYNC_BLOCK_END, CURSOR_SYNC_FILENAME, CURSOR_SETUP_FILENAME, MANAGED_BLOCK_PAIRS;
399
+ var init_pre_commit_block = __esm({
400
+ "src/writers/pre-commit-block.ts"() {
401
+ "use strict";
402
+ init_resolve_caliber();
403
+ BLOCK_START = "<!-- caliber:managed:pre-commit -->";
404
+ BLOCK_END = "<!-- /caliber:managed:pre-commit -->";
405
+ MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .cursorrules .github/copilot-instructions.md .github/instructions/ AGENTS.md CALIBER_LEARNINGS.md";
406
+ CURSOR_RULE_FILENAME = "caliber-pre-commit.mdc";
407
+ LEARNINGS_BLOCK_START = "<!-- caliber:managed:learnings -->";
408
+ LEARNINGS_BLOCK_END = "<!-- /caliber:managed:learnings -->";
409
+ LEARNINGS_BLOCK = `${LEARNINGS_BLOCK_START}
410
+ ## Session Learnings
411
+
412
+ Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
413
+ These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
414
+ ${LEARNINGS_BLOCK_END}`;
415
+ CURSOR_LEARNINGS_FILENAME = "caliber-learnings.mdc";
416
+ CURSOR_LEARNINGS_CONTENT = `---
417
+ description: Reference session-learned patterns from CALIBER_LEARNINGS.md
418
+ alwaysApply: true
419
+ ---
420
+ Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
421
+ These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
422
+ `;
423
+ SYNC_BLOCK_START = "<!-- caliber:managed:sync -->";
424
+ SYNC_BLOCK_END = "<!-- /caliber:managed:sync -->";
425
+ CURSOR_SYNC_FILENAME = "caliber-sync.mdc";
426
+ CURSOR_SETUP_FILENAME = "caliber-setup.mdc";
427
+ MANAGED_BLOCK_PAIRS = [
428
+ [BLOCK_START, BLOCK_END],
429
+ [LEARNINGS_BLOCK_START, LEARNINGS_BLOCK_END],
430
+ [SYNC_BLOCK_START, SYNC_BLOCK_END]
431
+ ];
432
+ }
433
+ });
434
+
435
+ // src/lib/builtin-skills.ts
436
+ var builtin_skills_exports = {};
437
+ __export(builtin_skills_exports, {
438
+ BUILTIN_SKILLS: () => BUILTIN_SKILLS,
439
+ BUILTIN_SKILL_NAMES: () => BUILTIN_SKILL_NAMES,
440
+ FIND_SKILLS_SKILL: () => FIND_SKILLS_SKILL,
441
+ PLATFORM_CONFIGS: () => PLATFORM_CONFIGS,
442
+ SAVE_LEARNING_SKILL: () => SAVE_LEARNING_SKILL,
443
+ SETUP_CALIBER_SKILL: () => SETUP_CALIBER_SKILL,
444
+ buildSkillContent: () => buildSkillContent,
445
+ ensureBuiltinSkills: () => ensureBuiltinSkills
446
+ });
447
+ import fs17 from "fs";
448
+ import path17 from "path";
449
+ function buildSkillContent(skill) {
450
+ const frontmatter = `---
451
+ name: ${skill.name}
452
+ description: ${skill.description}
453
+ ---
454
+
455
+ `;
456
+ return frontmatter + skill.content;
457
+ }
458
+ function getFindSkillsContent() {
459
+ const bin = resolveCaliber();
460
+ return `# Find Skills
461
+
462
+ Search the public skill registry for community-contributed skills
463
+ relevant to the user's current task and install them into this project.
464
+
465
+ ## Instructions
466
+
467
+ 1. Identify the key technologies, frameworks, or task types from the
468
+ user's request that might have community skills available
469
+ 2. Ask the user: "Would you like me to search for community skills
470
+ for [identified technologies]?"
471
+ 3. If the user agrees, run:
472
+ \`\`\`bash
473
+ ${bin} skills --query "<relevant terms>"
474
+ \`\`\`
475
+ This outputs the top 5 matching skills with scores and descriptions.
476
+ 4. Present the results to the user and ask which ones to install
477
+ 5. Install the selected skills:
478
+ \`\`\`bash
479
+ ${bin} skills --install <slug1>,<slug2>
480
+ \`\`\`
481
+ 6. Read the installed SKILL.md files to load them into your current
482
+ context so you can use them immediately in this session
483
+ 7. Summarize what was installed and continue with the user's task
484
+
485
+ ## Examples
486
+
487
+ User: "let's build a web app using React"
488
+ -> "I notice you want to work with React. Would you like me to search
489
+ for community skills that could help with React development?"
490
+ -> If yes: run \`${bin} skills --query "react frontend"\`
491
+ -> Show the user the results, ask which to install
492
+ -> Run \`${bin} skills --install <selected-slugs>\`
493
+ -> Read the installed files and continue
494
+
495
+ User: "help me set up Docker for this project"
496
+ -> "Would you like me to search for Docker-related skills?"
497
+ -> If yes: run \`${bin} skills --query "docker deployment"\`
498
+
499
+ User: "I need to write tests for this Python ML pipeline"
500
+ -> "Would you like me to find skills for Python ML testing?"
501
+ -> If yes: run \`${bin} skills --query "python machine-learning testing"\`
502
+
503
+ ## When NOT to trigger
504
+
505
+ - The user is working within an already well-configured area
506
+ - You already suggested skills for this technology in this session
507
+ - The user is in the middle of urgent debugging or time-sensitive work
508
+ - The technology is too generic (e.g. just "code" or "programming")
509
+ `;
510
+ }
511
+ function getSaveLearningContent() {
512
+ const bin = resolveCaliber();
513
+ return `# Save Learning
514
+
515
+ Save a user's instruction or preference as a persistent learning that
516
+ will be applied in all future sessions on this project.
517
+
518
+ ## Instructions
519
+
520
+ 1. Detect when the user gives an instruction to remember, such as:
521
+ - "remember this", "save this", "always do X", "never do Y"
522
+ - "from now on", "going forward", "in this project we..."
523
+ - Any stated convention, preference, or rule
524
+ 2. Refine the instruction into a clean, actionable learning bullet with
525
+ an appropriate type prefix:
526
+ - \`**[convention]**\` \u2014 coding style, workflow, git conventions
527
+ - \`**[pattern]**\` \u2014 reusable code patterns
528
+ - \`**[anti-pattern]**\` \u2014 things to avoid
529
+ - \`**[preference]**\` \u2014 personal/team preferences
530
+ - \`**[context]**\` \u2014 project-specific context
531
+ 3. Show the refined learning to the user and ask for confirmation
532
+ 4. If confirmed, run:
533
+ \`\`\`bash
534
+ ${bin} learn add "<refined learning>"
535
+ \`\`\`
536
+ For personal preferences (not project-level), add \`--personal\`:
537
+ \`\`\`bash
538
+ ${bin} learn add --personal "<refined learning>"
539
+ \`\`\`
540
+ 5. Stage the learnings file for the next commit:
541
+ \`\`\`bash
542
+ git add CALIBER_LEARNINGS.md
543
+ \`\`\`
544
+
545
+ ## Examples
546
+
547
+ User: "when developing features, push to next branch not master, remember it"
548
+ -> Refine: \`**[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`\`
549
+ -> "I'll save this as a project learning:
550
+ **[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`
551
+ Save for future sessions?"
552
+ -> If yes: run \`${bin} learn add "**[convention]** Push feature commits to the next branch, not master"\`
553
+ -> Run \`git add CALIBER_LEARNINGS.md\`
554
+
555
+ User: "always use bun instead of npm"
556
+ -> Refine: \`**[preference]** Use \\\`bun\\\` instead of \\\`npm\\\` for package management\`
557
+ -> Confirm and save
558
+
559
+ User: "never use any in TypeScript, use unknown instead"
560
+ -> Refine: \`**[convention]** Use \\\`unknown\\\` instead of \\\`any\\\` in TypeScript\`
561
+ -> Confirm and save
562
+
563
+ ## When NOT to trigger
564
+
565
+ - The user is giving a one-time instruction for the current task only
566
+ - The instruction is too vague to be actionable
567
+ - The user explicitly says "just for now" or "only this time"
568
+ `;
569
+ }
570
+ function getSetupCaliberContent() {
571
+ return `# Setup Caliber
572
+
573
+ Dynamic onboarding for Caliber \u2014 automatic AI agent context sync.
574
+ Run all diagnostic steps below on every invocation to determine what's already
575
+ set up and what still needs to be done.
576
+
577
+ ## Instructions
578
+
579
+ Run these checks in order. For each step, check the current state first,
580
+ then only act if something is missing.
581
+
582
+ ### Step 1: Check if Caliber is installed
583
+
584
+ \`\`\`bash
585
+ command -v caliber >/dev/null 2>&1 && caliber --version || echo "NOT_INSTALLED"
586
+ \`\`\`
587
+
588
+ - If a version prints \u2192 Caliber is installed globally. Set \`CALIBER="caliber"\` and move to Step 2.
589
+ - If NOT_INSTALLED \u2192 Install it globally (faster for daily use since the pre-commit hook runs on every commit):
590
+ \`\`\`bash
591
+ npm install -g @rely-ai/caliber
592
+ \`\`\`
593
+ Set \`CALIBER="caliber"\`.
594
+
595
+ If npm fails (permissions, no sudo, etc.), fall back to npx:
596
+ \`\`\`bash
597
+ npx @rely-ai/caliber --version 2>/dev/null || echo "NO_NODE"
598
+ \`\`\`
599
+ - If npx works \u2192 Set \`CALIBER="npx @rely-ai/caliber"\`. This works but adds ~500ms per invocation.
600
+ - If NO_NODE \u2192 Tell the user: "Caliber requires Node.js >= 20. Install Node first, then run /setup-caliber again." Stop here.
601
+
602
+ ### Step 2: Check if pre-commit hook is installed
603
+
604
+ \`\`\`bash
605
+ grep -q "caliber" .git/hooks/pre-commit 2>/dev/null && echo "HOOK_ACTIVE" || echo "NO_HOOK"
606
+ \`\`\`
607
+
608
+ - If HOOK_ACTIVE \u2192 Tell the user: "Pre-commit hook is active \u2014 configs sync on every commit." Move to Step 3.
609
+ - If NO_HOOK \u2192 Tell the user: "I'll install the pre-commit hook so your agent configs sync automatically on every commit."
610
+ \`\`\`bash
611
+ $CALIBER hooks --install
612
+ \`\`\`
613
+
614
+ ### Step 3: Detect agents and check if configs exist
615
+
616
+ First, detect which coding agents are configured in this project:
617
+ \`\`\`bash
618
+ AGENTS=""
619
+ [ -d .claude ] && AGENTS="claude"
620
+ [ -d .cursor ] && AGENTS="\${AGENTS:+$AGENTS,}cursor"
621
+ [ -d .agents ] || [ -f AGENTS.md ] && AGENTS="\${AGENTS:+$AGENTS,}codex"
622
+ [ -f .github/copilot-instructions.md ] && AGENTS="\${AGENTS:+$AGENTS,}github-copilot"
623
+ echo "DETECTED_AGENTS=\${AGENTS:-none}"
624
+ \`\`\`
625
+
626
+ If no agents are detected, ask the user which coding agents they use (Claude Code, Cursor, Codex, GitHub Copilot).
627
+ Build the agent list from their answer as a comma-separated string (e.g. "claude,cursor").
628
+
629
+ Then check if agent configs exist:
630
+ \`\`\`bash
631
+ echo "CLAUDE_MD=$([ -f CLAUDE.md ] && echo exists || echo missing)"
632
+ echo "CURSOR_RULES=$([ -d .cursor/rules ] && ls .cursor/rules/*.mdc 2>/dev/null | wc -l | tr -d ' ' || echo 0)"
633
+ echo "AGENTS_MD=$([ -f AGENTS.md ] && echo exists || echo missing)"
634
+ echo "COPILOT=$([ -f .github/copilot-instructions.md ] && echo exists || echo missing)"
635
+ \`\`\`
636
+
637
+ - If configs exist for the detected agents \u2192 Tell the user which configs are present. Move to Step 4.
638
+ - If configs are missing \u2192 Tell the user: "No agent configs found. I'll generate them now."
639
+ Use the detected or user-selected agent list:
640
+ \`\`\`bash
641
+ $CALIBER init --auto-approve --agent <comma-separated-agents>
642
+ \`\`\`
643
+ For example: \`$CALIBER init --auto-approve --agent claude,cursor\`
644
+ This generates CLAUDE.md, Cursor rules, AGENTS.md, skills, and sync infrastructure for the specified agents.
645
+
646
+ ### Step 4: Check if configs are fresh
647
+
648
+ \`\`\`bash
649
+ $CALIBER score --json --quiet 2>/dev/null | head -1
650
+ \`\`\`
651
+
652
+ - If score is 80+ \u2192 Tell the user: "Your configs are in good shape (score: X/100)."
653
+ - If score is below 80 \u2192 Tell the user: "Your configs could be improved (score: X/100). Want me to run a refresh?"
654
+ If yes:
655
+ \`\`\`bash
656
+ $CALIBER refresh
657
+ \`\`\`
658
+
659
+ ### Step 5: Ask about team setup
660
+
661
+ Ask the user: "Are you setting up for yourself only, or for your team too?"
662
+
663
+ - If **solo** \u2192 Continue with solo setup:
664
+
665
+ Check if session learning is enabled:
666
+ \`\`\`bash
667
+ $CALIBER learn status 2>/dev/null | head -3
668
+ \`\`\`
669
+ - If learning is already enabled \u2192 note it in the summary.
670
+ - If not enabled \u2192 ask the user: "Caliber can learn from your coding sessions \u2014 when you correct a mistake or fix a pattern, it remembers for next time. Enable session learning?"
671
+ If yes:
672
+ \`\`\`bash
673
+ $CALIBER learn install
674
+ \`\`\`
675
+
676
+ Then tell the user:
677
+ "You're all set! Here's what happens next:
678
+ - Every time you commit, Caliber syncs your agent configs automatically
679
+ - Your CLAUDE.md, Cursor rules, and AGENTS.md stay current with your code
680
+ - Run \`$CALIBER skills\` anytime to discover community skills for your stack"
681
+
682
+ Then show the summary (see below) and stop.
683
+
684
+ - If **team** \u2192 Check if the GitHub Action already exists:
685
+ \`\`\`bash
686
+ [ -f .github/workflows/caliber-sync.yml ] && echo "ACTION_EXISTS" || echo "NO_ACTION"
687
+ \`\`\`
688
+ - If ACTION_EXISTS \u2192 Tell the user: "GitHub Action is already configured."
689
+ - If NO_ACTION \u2192 Tell the user: "I'll create a GitHub Action that syncs configs nightly and on every PR."
690
+ Write this file to \`.github/workflows/caliber-sync.yml\`:
691
+ \`\`\`yaml
692
+ name: Caliber Sync
693
+ on:
694
+ schedule:
695
+ - cron: '0 3 * * 1-5'
696
+ pull_request:
697
+ types: [opened, synchronize]
698
+ workflow_dispatch:
699
+ jobs:
700
+ sync:
701
+ runs-on: ubuntu-latest
702
+ steps:
703
+ - uses: actions/checkout@v4
704
+ - uses: caliber-ai-org/ai-setup@v1
705
+ with:
706
+ mode: sync
707
+ auto-refresh: true
708
+ comment: true
709
+ github-token: \${{ secrets.GITHUB_TOKEN }}
710
+ env:
711
+ ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}
712
+ \`\`\`
713
+ Now determine which LLM provider the team uses. Check the local Caliber config:
714
+ \`\`\`bash
715
+ $CALIBER config --show 2>/dev/null || echo "NO_CONFIG"
716
+ \`\`\`
717
+
718
+ Based on the provider, the GitHub Action needs the corresponding secret:
719
+ - **anthropic** \u2192 \`ANTHROPIC_API_KEY\`
720
+ - **openai** \u2192 \`OPENAI_API_KEY\`
721
+ - **vertex** \u2192 \`VERTEX_PROJECT_ID\` and \`GOOGLE_APPLICATION_CREDENTIALS\` (service account JSON)
722
+
723
+ Update the workflow env block to match the provider. For example, if using OpenAI:
724
+ \`\`\`yaml
725
+ env:
726
+ OPENAI_API_KEY: \${{ secrets.OPENAI_API_KEY }}
727
+ \`\`\`
728
+
729
+ Then check if the \`gh\` CLI is available to set the secret:
730
+ \`\`\`bash
731
+ command -v gh >/dev/null 2>&1 && echo "GH_AVAILABLE" || echo "NO_GH"
732
+ \`\`\`
733
+
734
+ - If GH_AVAILABLE \u2192 Ask the user for their API key and set it:
735
+ \`\`\`bash
736
+ gh secret set ANTHROPIC_API_KEY
737
+ \`\`\`
738
+ (This prompts for the value securely via stdin)
739
+ - If NO_GH \u2192 Tell the user exactly what to do:
740
+ "Go to your repo on GitHub \u2192 Settings \u2192 Secrets and variables \u2192 Actions \u2192 New repository secret.
741
+ Name: ANTHROPIC_API_KEY (or OPENAI_API_KEY depending on provider)
742
+ Value: your API key"
743
+
744
+ Finally, offer to commit and push the workflow file:
745
+ \`\`\`bash
746
+ git add .github/workflows/caliber-sync.yml
747
+ git commit -m "feat: add Caliber sync GitHub Action"
748
+ git push
749
+ \`\`\`
750
+
751
+ ### Summary
752
+
753
+ After completing all steps, show the user what's configured:
754
+
755
+ \`\`\`
756
+ Caliber Setup Complete:
757
+ \u2713 Caliber installed (vX.X.X)
758
+ \u2713 Pre-commit hook \u2014 configs sync on every commit
759
+ \u2713 Agent configs \u2014 CLAUDE.md, Cursor rules, AGENTS.md
760
+ \u2713 Config score: X/100
761
+ \u2713 GitHub Action \u2014 nightly sync + PR checks (team only)
762
+
763
+ From now on, every commit keeps all your agent configs in sync automatically.
764
+ \`\`\`
765
+
766
+ ## When to trigger
767
+
768
+ - When a developer first opens this project
769
+ - When the user asks about keeping agent configs up to date
770
+ - When the user asks "how do I set up Caliber" or "what is Caliber"
771
+ - When the user sees a message saying "Run /setup-caliber to get set up"
772
+
773
+ ## When NOT to trigger
774
+
775
+ - The user is in the middle of time-sensitive work
776
+ `;
777
+ }
778
+ function ensureBuiltinSkills() {
779
+ const written = [];
780
+ for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
781
+ if (!fs17.existsSync(platformDir)) continue;
782
+ for (const skill of BUILTIN_SKILLS) {
783
+ const skillPath = path17.join(skillsDir, skill.name, "SKILL.md");
784
+ fs17.mkdirSync(path17.dirname(skillPath), { recursive: true });
785
+ fs17.writeFileSync(skillPath, buildSkillContent(skill));
786
+ written.push(skillPath);
787
+ }
788
+ }
789
+ return written;
790
+ }
791
+ var FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL, SETUP_CALIBER_SKILL, BUILTIN_SKILLS, PLATFORM_CONFIGS, BUILTIN_SKILL_NAMES;
792
+ var init_builtin_skills = __esm({
793
+ "src/lib/builtin-skills.ts"() {
794
+ "use strict";
795
+ init_resolve_caliber();
796
+ FIND_SKILLS_SKILL = {
797
+ name: "find-skills",
798
+ 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.",
799
+ get content() {
800
+ return getFindSkillsContent();
801
+ }
802
+ };
803
+ SAVE_LEARNING_SKILL = {
804
+ name: "save-learning",
805
+ 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.",
806
+ get content() {
807
+ return getSaveLearningContent();
808
+ }
809
+ };
810
+ SETUP_CALIBER_SKILL = {
811
+ name: "setup-caliber",
812
+ 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.",
813
+ get content() {
814
+ return getSetupCaliberContent();
815
+ }
816
+ };
817
+ BUILTIN_SKILLS = [FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL, SETUP_CALIBER_SKILL];
818
+ PLATFORM_CONFIGS = [
819
+ { platformDir: ".claude", skillsDir: path17.join(".claude", "skills") },
820
+ { platformDir: ".cursor", skillsDir: path17.join(".cursor", "skills") },
821
+ { platformDir: ".agents", skillsDir: path17.join(".agents", "skills") }
822
+ ];
823
+ BUILTIN_SKILL_NAMES = new Set(BUILTIN_SKILLS.map((s) => s.name));
824
+ }
825
+ });
826
+
233
827
  // src/utils/editor.ts
234
- import { execSync as execSync13, spawn as spawn3 } from "child_process";
235
- import fs26 from "fs";
236
- import path23 from "path";
828
+ import { execSync as execSync14, spawn as spawn3 } from "child_process";
829
+ import fs27 from "fs";
830
+ import path24 from "path";
237
831
  import os6 from "os";
238
832
  function getEmptyFilePath(proposedPath) {
239
- fs26.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
240
- const tempPath = path23.join(DIFF_TEMP_DIR, path23.basename(proposedPath));
241
- fs26.writeFileSync(tempPath, "");
833
+ fs27.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
834
+ const tempPath = path24.join(DIFF_TEMP_DIR, path24.basename(proposedPath));
835
+ fs27.writeFileSync(tempPath, "");
242
836
  return tempPath;
243
837
  }
244
838
  function commandExists(cmd) {
245
839
  try {
246
840
  const check = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
247
- execSync13(check, { stdio: "ignore" });
841
+ execSync14(check, { stdio: "ignore" });
248
842
  return true;
249
843
  } catch {
250
844
  return false;
@@ -278,7 +872,7 @@ var init_editor = __esm({
278
872
  "src/utils/editor.ts"() {
279
873
  "use strict";
280
874
  IS_WINDOWS3 = process.platform === "win32";
281
- DIFF_TEMP_DIR = path23.join(os6.tmpdir(), "caliber-diff");
875
+ DIFF_TEMP_DIR = path24.join(os6.tmpdir(), "caliber-diff");
282
876
  }
283
877
  });
284
878
 
@@ -290,7 +884,7 @@ __export(review_exports, {
290
884
  promptWantsReview: () => promptWantsReview
291
885
  });
292
886
  import chalk10 from "chalk";
293
- import fs27 from "fs";
887
+ import fs28 from "fs";
294
888
  import select4 from "@inquirer/select";
295
889
  import { createTwoFilesPatch } from "diff";
296
890
  async function promptWantsReview() {
@@ -327,8 +921,8 @@ async function openReview(method, stagedFiles) {
327
921
  return;
328
922
  }
329
923
  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") : "";
924
+ const proposed = fs28.readFileSync(file.proposedPath, "utf-8");
925
+ const current = file.currentPath ? fs28.readFileSync(file.currentPath, "utf-8") : "";
332
926
  const patch = createTwoFilesPatch(
333
927
  file.isNew ? "/dev/null" : file.relativePath,
334
928
  file.relativePath,
@@ -502,14 +1096,14 @@ __export(lock_exports, {
502
1096
  acquireLock: () => acquireLock,
503
1097
  isCaliberRunning: () => isCaliberRunning,
504
1098
  releaseLock: () => releaseLock
505
- });
506
- import fs37 from "fs";
507
- import path30 from "path";
1099
+ });
1100
+ import fs38 from "fs";
1101
+ import path31 from "path";
508
1102
  import os8 from "os";
509
1103
  function isCaliberRunning() {
510
1104
  try {
511
- if (!fs37.existsSync(LOCK_FILE)) return false;
512
- const raw = fs37.readFileSync(LOCK_FILE, "utf-8").trim();
1105
+ if (!fs38.existsSync(LOCK_FILE)) return false;
1106
+ const raw = fs38.readFileSync(LOCK_FILE, "utf-8").trim();
513
1107
  const { pid, ts } = JSON.parse(raw);
514
1108
  if (Date.now() - ts > STALE_MS) return false;
515
1109
  try {
@@ -524,13 +1118,13 @@ function isCaliberRunning() {
524
1118
  }
525
1119
  function acquireLock() {
526
1120
  try {
527
- fs37.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
1121
+ fs38.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
528
1122
  } catch {
529
1123
  }
530
1124
  }
531
1125
  function releaseLock() {
532
1126
  try {
533
- if (fs37.existsSync(LOCK_FILE)) fs37.unlinkSync(LOCK_FILE);
1127
+ if (fs38.existsSync(LOCK_FILE)) fs38.unlinkSync(LOCK_FILE);
534
1128
  } catch {
535
1129
  }
536
1130
  }
@@ -538,21 +1132,21 @@ var LOCK_FILE, STALE_MS;
538
1132
  var init_lock = __esm({
539
1133
  "src/lib/lock.ts"() {
540
1134
  "use strict";
541
- LOCK_FILE = path30.join(os8.tmpdir(), ".caliber.lock");
1135
+ LOCK_FILE = path31.join(os8.tmpdir(), ".caliber.lock");
542
1136
  STALE_MS = 10 * 60 * 1e3;
543
1137
  }
544
1138
  });
545
1139
 
546
1140
  // src/cli.ts
547
1141
  import { Command } from "commander";
548
- import fs47 from "fs";
549
- import path39 from "path";
1142
+ import fs49 from "fs";
1143
+ import path40 from "path";
550
1144
  import { fileURLToPath } from "url";
551
1145
 
552
1146
  // src/commands/init.ts
553
- import path26 from "path";
1147
+ import path27 from "path";
554
1148
  import chalk14 from "chalk";
555
- import fs32 from "fs";
1149
+ import fs33 from "fs";
556
1150
 
557
1151
  // src/fingerprint/index.ts
558
1152
  import fs8 from "fs";
@@ -3227,9 +3821,151 @@ function getCursorConfigDir() {
3227
3821
  return path9.join(home, ".config", "Cursor");
3228
3822
  }
3229
3823
 
3230
- // src/fingerprint/sources.ts
3824
+ // src/lib/hooks.ts
3825
+ init_resolve_caliber();
3231
3826
  import fs10 from "fs";
3232
3827
  import path10 from "path";
3828
+ import { execSync as execSync8 } from "child_process";
3829
+ var SETTINGS_PATH = path10.join(".claude", "settings.json");
3830
+ var REFRESH_TAIL = "refresh --quiet";
3831
+ var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
3832
+ function getHookCommand() {
3833
+ return `${resolveCaliber()} ${REFRESH_TAIL}`;
3834
+ }
3835
+ function readSettings() {
3836
+ if (!fs10.existsSync(SETTINGS_PATH)) return {};
3837
+ try {
3838
+ return JSON.parse(fs10.readFileSync(SETTINGS_PATH, "utf-8"));
3839
+ } catch {
3840
+ return {};
3841
+ }
3842
+ }
3843
+ function writeSettings(settings) {
3844
+ const dir = path10.dirname(SETTINGS_PATH);
3845
+ if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
3846
+ fs10.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
3847
+ }
3848
+ function findHookIndex(sessionEnd) {
3849
+ return sessionEnd.findIndex(
3850
+ (entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, REFRESH_TAIL))
3851
+ );
3852
+ }
3853
+ function isHookInstalled() {
3854
+ const settings = readSettings();
3855
+ const sessionEnd = settings.hooks?.SessionEnd;
3856
+ if (!Array.isArray(sessionEnd)) return false;
3857
+ return findHookIndex(sessionEnd) !== -1;
3858
+ }
3859
+ function installHook() {
3860
+ const settings = readSettings();
3861
+ if (!settings.hooks) settings.hooks = {};
3862
+ if (!Array.isArray(settings.hooks.SessionEnd)) settings.hooks.SessionEnd = [];
3863
+ if (findHookIndex(settings.hooks.SessionEnd) !== -1) {
3864
+ return { installed: false, alreadyInstalled: true };
3865
+ }
3866
+ settings.hooks.SessionEnd.push({
3867
+ matcher: "",
3868
+ hooks: [{ type: "command", command: getHookCommand(), description: HOOK_DESCRIPTION }]
3869
+ });
3870
+ writeSettings(settings);
3871
+ return { installed: true, alreadyInstalled: false };
3872
+ }
3873
+ function removeHook() {
3874
+ const settings = readSettings();
3875
+ const sessionEnd = settings.hooks?.SessionEnd;
3876
+ if (!Array.isArray(sessionEnd)) {
3877
+ return { removed: false, notFound: true };
3878
+ }
3879
+ const idx = findHookIndex(sessionEnd);
3880
+ if (idx === -1) {
3881
+ return { removed: false, notFound: true };
3882
+ }
3883
+ sessionEnd.splice(idx, 1);
3884
+ if (sessionEnd.length === 0) {
3885
+ delete settings.hooks.SessionEnd;
3886
+ }
3887
+ if (settings.hooks && Object.keys(settings.hooks).length === 0) {
3888
+ delete settings.hooks;
3889
+ }
3890
+ writeSettings(settings);
3891
+ return { removed: true, notFound: false };
3892
+ }
3893
+ var PRECOMMIT_START = "# caliber:pre-commit:start";
3894
+ var PRECOMMIT_END = "# caliber:pre-commit:end";
3895
+ function getPrecommitBlock() {
3896
+ const bin = resolveCaliber();
3897
+ const npx = isNpxResolution();
3898
+ const guard = npx ? "command -v npx >/dev/null 2>&1" : `[ -x "${bin}" ] || command -v "${bin}" >/dev/null 2>&1`;
3899
+ const invoke = npx ? bin : `"${bin}"`;
3900
+ return `${PRECOMMIT_START}
3901
+ if ${guard}; then
3902
+ echo "\\033[2mcaliber: refreshing docs...\\033[0m"
3903
+ ${invoke} refresh 2>/dev/null || true
3904
+ ${invoke} learn finalize 2>/dev/null || true
3905
+ git diff --name-only -- CLAUDE.md .claude/ .cursor/ AGENTS.md CALIBER_LEARNINGS.md 2>/dev/null | xargs git add 2>/dev/null || true
3906
+ fi
3907
+ ${PRECOMMIT_END}`;
3908
+ }
3909
+ function getGitHooksDir() {
3910
+ try {
3911
+ const gitDir = execSync8("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3912
+ return path10.join(gitDir, "hooks");
3913
+ } catch {
3914
+ return null;
3915
+ }
3916
+ }
3917
+ function getPreCommitPath() {
3918
+ const hooksDir = getGitHooksDir();
3919
+ return hooksDir ? path10.join(hooksDir, "pre-commit") : null;
3920
+ }
3921
+ function isPreCommitHookInstalled() {
3922
+ const hookPath = getPreCommitPath();
3923
+ if (!hookPath || !fs10.existsSync(hookPath)) return false;
3924
+ const content = fs10.readFileSync(hookPath, "utf-8");
3925
+ return content.includes(PRECOMMIT_START);
3926
+ }
3927
+ function installPreCommitHook() {
3928
+ if (isPreCommitHookInstalled()) {
3929
+ return { installed: false, alreadyInstalled: true };
3930
+ }
3931
+ const hookPath = getPreCommitPath();
3932
+ if (!hookPath) return { installed: false, alreadyInstalled: false };
3933
+ const hooksDir = path10.dirname(hookPath);
3934
+ if (!fs10.existsSync(hooksDir)) fs10.mkdirSync(hooksDir, { recursive: true });
3935
+ let content = "";
3936
+ if (fs10.existsSync(hookPath)) {
3937
+ content = fs10.readFileSync(hookPath, "utf-8");
3938
+ if (!content.endsWith("\n")) content += "\n";
3939
+ content += "\n" + getPrecommitBlock() + "\n";
3940
+ } else {
3941
+ content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
3942
+ }
3943
+ fs10.writeFileSync(hookPath, content);
3944
+ fs10.chmodSync(hookPath, 493);
3945
+ return { installed: true, alreadyInstalled: false };
3946
+ }
3947
+ function removePreCommitHook() {
3948
+ const hookPath = getPreCommitPath();
3949
+ if (!hookPath || !fs10.existsSync(hookPath)) {
3950
+ return { removed: false, notFound: true };
3951
+ }
3952
+ let content = fs10.readFileSync(hookPath, "utf-8");
3953
+ if (!content.includes(PRECOMMIT_START)) {
3954
+ return { removed: false, notFound: true };
3955
+ }
3956
+ const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
3957
+ content = content.replace(regex, "\n");
3958
+ if (content.trim() === "#!/bin/sh" || content.trim() === "") {
3959
+ fs10.unlinkSync(hookPath);
3960
+ } else {
3961
+ fs10.writeFileSync(hookPath, content);
3962
+ }
3963
+ return { removed: true, notFound: false };
3964
+ }
3965
+
3966
+ // src/fingerprint/sources.ts
3967
+ import fs11 from "fs";
3968
+ import path11 from "path";
3233
3969
 
3234
3970
  // src/scoring/utils.ts
3235
3971
  import { existsSync as existsSync2, readFileSync, readdirSync } from "fs";
@@ -3558,7 +4294,7 @@ var SOURCE_CONTENT_LIMIT = 2e3;
3558
4294
  var README_CONTENT_LIMIT = 1e3;
3559
4295
  var ORIGIN_PRIORITY = { cli: 0, config: 1, workspace: 2 };
3560
4296
  function loadSourcesConfig(dir) {
3561
- const configPath = path10.join(dir, ".caliber", "sources.json");
4297
+ const configPath = path11.join(dir, ".caliber", "sources.json");
3562
4298
  const content = readFileOrNull(configPath);
3563
4299
  if (!content) return [];
3564
4300
  try {
@@ -3576,29 +4312,29 @@ function loadSourcesConfig(dir) {
3576
4312
  }
3577
4313
  }
3578
4314
  function writeSourcesConfig(dir, sources2) {
3579
- const configDir = path10.join(dir, ".caliber");
3580
- if (!fs10.existsSync(configDir)) {
3581
- fs10.mkdirSync(configDir, { recursive: true });
4315
+ const configDir = path11.join(dir, ".caliber");
4316
+ if (!fs11.existsSync(configDir)) {
4317
+ fs11.mkdirSync(configDir, { recursive: true });
3582
4318
  }
3583
- const configPath = path10.join(configDir, "sources.json");
3584
- fs10.writeFileSync(configPath, JSON.stringify({ sources: sources2 }, null, 2) + "\n", "utf-8");
4319
+ const configPath = path11.join(configDir, "sources.json");
4320
+ fs11.writeFileSync(configPath, JSON.stringify({ sources: sources2 }, null, 2) + "\n", "utf-8");
3585
4321
  }
3586
4322
  function detectSourceType(absPath) {
3587
4323
  try {
3588
- return fs10.statSync(absPath).isDirectory() ? "repo" : "file";
4324
+ return fs11.statSync(absPath).isDirectory() ? "repo" : "file";
3589
4325
  } catch {
3590
4326
  return "file";
3591
4327
  }
3592
4328
  }
3593
4329
  function isInsideDir(childPath, parentDir) {
3594
- const relative2 = path10.relative(parentDir, childPath);
3595
- return !relative2.startsWith("..") && !path10.isAbsolute(relative2);
4330
+ const relative2 = path11.relative(parentDir, childPath);
4331
+ return !relative2.startsWith("..") && !path11.isAbsolute(relative2);
3596
4332
  }
3597
4333
  function resolveAllSources(dir, cliSources, workspaces) {
3598
4334
  const seen = /* @__PURE__ */ new Map();
3599
- const projectRoot = path10.resolve(dir);
4335
+ const projectRoot = path11.resolve(dir);
3600
4336
  for (const src of cliSources) {
3601
- const absPath = path10.resolve(dir, src);
4337
+ const absPath = path11.resolve(dir, src);
3602
4338
  if (seen.has(absPath)) continue;
3603
4339
  const type = detectSourceType(absPath);
3604
4340
  seen.set(absPath, {
@@ -3611,12 +4347,12 @@ function resolveAllSources(dir, cliSources, workspaces) {
3611
4347
  for (const cfg of configSources) {
3612
4348
  if (cfg.type === "url") continue;
3613
4349
  if (!cfg.path) continue;
3614
- const absPath = path10.resolve(dir, cfg.path);
4350
+ const absPath = path11.resolve(dir, cfg.path);
3615
4351
  if (seen.has(absPath)) continue;
3616
4352
  seen.set(absPath, { absPath, config: cfg, origin: "config" });
3617
4353
  }
3618
4354
  for (const ws of workspaces) {
3619
- const absPath = path10.resolve(dir, ws);
4355
+ const absPath = path11.resolve(dir, ws);
3620
4356
  if (seen.has(absPath)) continue;
3621
4357
  if (!isInsideDir(absPath, projectRoot)) continue;
3622
4358
  seen.set(absPath, {
@@ -3629,7 +4365,7 @@ function resolveAllSources(dir, cliSources, workspaces) {
3629
4365
  for (const [absPath, resolved] of seen) {
3630
4366
  let stat;
3631
4367
  try {
3632
- stat = fs10.statSync(absPath);
4368
+ stat = fs11.statSync(absPath);
3633
4369
  } catch {
3634
4370
  console.warn(`Source ${resolved.config.path || absPath} not found, skipping`);
3635
4371
  continue;
@@ -3658,13 +4394,13 @@ function collectSourceSummary(resolved, projectDir) {
3658
4394
  if (config.type === "file") {
3659
4395
  return collectFileSummary(resolved, projectDir);
3660
4396
  }
3661
- const summaryPath = path10.join(absPath, ".caliber", "summary.json");
4397
+ const summaryPath = path11.join(absPath, ".caliber", "summary.json");
3662
4398
  const summaryContent = readFileOrNull(summaryPath);
3663
4399
  if (summaryContent) {
3664
4400
  try {
3665
4401
  const published = JSON.parse(summaryContent);
3666
4402
  return {
3667
- name: published.name || path10.basename(absPath),
4403
+ name: published.name || path11.basename(absPath),
3668
4404
  type: "repo",
3669
4405
  role: config.role || published.role || "related-repo",
3670
4406
  description: config.description || published.description || "",
@@ -3684,18 +4420,18 @@ function collectRepoSummary(resolved, _projectDir) {
3684
4420
  let topLevelDirs;
3685
4421
  let keyFiles;
3686
4422
  try {
3687
- const entries = fs10.readdirSync(absPath, { withFileTypes: true });
4423
+ const entries = fs11.readdirSync(absPath, { withFileTypes: true });
3688
4424
  topLevelDirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name).slice(0, 20);
3689
4425
  keyFiles = entries.filter((e) => e.isFile() && !e.name.startsWith(".")).map((e) => e.name).slice(0, 15);
3690
4426
  } catch {
3691
4427
  }
3692
- const claudeMdContent = readFileOrNull(path10.join(absPath, "CLAUDE.md"));
4428
+ const claudeMdContent = readFileOrNull(path11.join(absPath, "CLAUDE.md"));
3693
4429
  const existingClaudeMd = claudeMdContent ? claudeMdContent.slice(0, SOURCE_CONTENT_LIMIT) : void 0;
3694
- const readmeContent = readFileOrNull(path10.join(absPath, "README.md"));
4430
+ const readmeContent = readFileOrNull(path11.join(absPath, "README.md"));
3695
4431
  const readmeExcerpt = readmeContent ? readmeContent.slice(0, README_CONTENT_LIMIT) : void 0;
3696
4432
  const gitRemoteUrl = getGitRemoteUrl(absPath);
3697
4433
  return {
3698
- name: packageName || path10.basename(absPath),
4434
+ name: packageName || path11.basename(absPath),
3699
4435
  type: "repo",
3700
4436
  role: config.role || "related-repo",
3701
4437
  description: config.description || "",
@@ -3712,7 +4448,7 @@ function collectFileSummary(resolved, _projectDir) {
3712
4448
  const { config, origin, absPath } = resolved;
3713
4449
  const content = readFileOrNull(absPath);
3714
4450
  return {
3715
- name: path10.basename(absPath),
4451
+ name: path11.basename(absPath),
3716
4452
  type: "file",
3717
4453
  role: config.role || "reference-doc",
3718
4454
  description: config.description || content?.slice(0, 100).split("\n")[0] || "",
@@ -3756,15 +4492,15 @@ init_config();
3756
4492
  // src/utils/dependencies.ts
3757
4493
  import { readFileSync as readFileSync2 } from "fs";
3758
4494
  import { join as join2 } from "path";
3759
- function readFileOrNull2(path41) {
4495
+ function readFileOrNull2(path42) {
3760
4496
  try {
3761
- return readFileSync2(path41, "utf-8");
4497
+ return readFileSync2(path42, "utf-8");
3762
4498
  } catch {
3763
4499
  return null;
3764
4500
  }
3765
4501
  }
3766
- function readJsonOrNull(path41) {
3767
- const content = readFileOrNull2(path41);
4502
+ function readJsonOrNull(path42) {
4503
+ const content = readFileOrNull2(path42);
3768
4504
  if (!content) return null;
3769
4505
  try {
3770
4506
  return JSON.parse(content);
@@ -4305,191 +5041,71 @@ ${existing.personalLearnings}`);
4305
5041
  if (allDeps.length > 0) {
4306
5042
  parts.push(`
4307
5043
  Project dependencies (${allDeps.length}):`);
4308
- parts.push(allDeps.join(", "));
4309
- }
4310
- if (prompt) parts.push(`
4311
- User instructions: ${prompt}`);
4312
- if (fingerprint.codeAnalysis) {
4313
- const ca = fingerprint.codeAnalysis;
4314
- const basePrompt = parts.join("\n");
4315
- const maxPromptTokens = getMaxPromptTokens();
4316
- const baseTokens = estimateTokens(basePrompt);
4317
- const tokenBudgetForCode = Math.max(0, maxPromptTokens - baseTokens);
4318
- const codeLines = [];
4319
- let codeChars = 0;
4320
- const introLine = "Study these files to extract patterns for skills. Use the exact code patterns you see here.\n";
4321
- codeLines.push(introLine);
4322
- let runningCodeLen = introLine.length;
4323
- const sortedFiles = [...ca.files].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
4324
- let includedFiles = 0;
4325
- for (const f of sortedFiles) {
4326
- const entry = `[${f.path}]
4327
- ${f.content}
4328
- `;
4329
- const projectedLen = runningCodeLen + 1 + entry.length;
4330
- if (Math.ceil(projectedLen / 4) > tokenBudgetForCode && includedFiles > 0) break;
4331
- codeLines.push(entry);
4332
- codeChars += f.content.length;
4333
- runningCodeLen = projectedLen;
4334
- includedFiles++;
4335
- }
4336
- const includedTokens = Math.ceil(codeChars / 4);
4337
- let header;
4338
- if (includedFiles < ca.files.length) {
4339
- const pct = ca.totalProjectTokens > 0 ? Math.round(includedTokens / ca.totalProjectTokens * 100) : 100;
4340
- header = `
4341
- --- Project Files (trimmed to ~${includedTokens.toLocaleString()}/${ca.totalProjectTokens.toLocaleString()} tokens, ${pct}% of total) ---`;
4342
- } else if (ca.truncated) {
4343
- const pct = ca.totalProjectTokens > 0 ? Math.round(ca.includedTokens / ca.totalProjectTokens * 100) : 100;
4344
- header = `
4345
- --- Project Files (trimmed to ~${ca.includedTokens.toLocaleString()}/${ca.totalProjectTokens.toLocaleString()} tokens, ${pct}% of total) ---`;
4346
- } else {
4347
- header = `
4348
- --- Project Files (${ca.files.length} files, ~${ca.includedTokens.toLocaleString()} tokens) ---`;
4349
- }
4350
- parts.push(header);
4351
- parts.push(codeLines.join("\n"));
4352
- }
4353
- if (fingerprint.sources?.length) {
4354
- parts.push(formatSourcesForPrompt(fingerprint.sources));
4355
- }
4356
- return parts.join("\n");
4357
- }
4358
-
4359
- // src/writers/index.ts
4360
- import fs18 from "fs";
4361
-
4362
- // src/writers/claude/index.ts
4363
- import fs11 from "fs";
4364
- import path11 from "path";
4365
-
4366
- // src/writers/pre-commit-block.ts
4367
- init_resolve_caliber();
4368
- var BLOCK_START = "<!-- caliber:managed:pre-commit -->";
4369
- var BLOCK_END = "<!-- /caliber:managed:pre-commit -->";
4370
- var MANAGED_DOC_PATHS = "CLAUDE.md .claude/ .cursor/ .github/copilot-instructions.md AGENTS.md CALIBER_LEARNINGS.md";
4371
- function getPreCommitBlock() {
4372
- const bin = resolveCaliber();
4373
- return `${BLOCK_START}
4374
- ## Before Committing
4375
-
4376
- Run \`${bin} refresh\` before creating git commits to keep docs in sync with code changes.
4377
- After it completes, stage any modified doc files before committing:
4378
-
4379
- \`\`\`bash
4380
- ${bin} refresh && git add ${MANAGED_DOC_PATHS} 2>/dev/null
4381
- \`\`\`
4382
- ${BLOCK_END}`;
4383
- }
4384
- var CURSOR_RULE_FILENAME = "caliber-pre-commit.mdc";
4385
- function getCursorRuleContent() {
4386
- const bin = resolveCaliber();
4387
- return `---
4388
- description: Run caliber refresh before git commits to keep docs in sync
4389
- alwaysApply: true
4390
- ---
4391
- Before creating git commits, run \`${bin} refresh\` to update docs based on code changes,
4392
- then stage modified doc files (${MANAGED_DOC_PATHS}).
4393
- `;
4394
- }
4395
- function hasPreCommitBlock(content) {
4396
- return content.includes(BLOCK_START);
4397
- }
4398
- function appendPreCommitBlock(content) {
4399
- if (hasPreCommitBlock(content)) return content;
4400
- const trimmed = content.trimEnd();
4401
- return trimmed + "\n\n" + getPreCommitBlock() + "\n";
4402
- }
4403
- function getCursorPreCommitRule() {
4404
- return { filename: CURSOR_RULE_FILENAME, content: getCursorRuleContent() };
4405
- }
4406
- var LEARNINGS_BLOCK_START = "<!-- caliber:managed:learnings -->";
4407
- var LEARNINGS_BLOCK_END = "<!-- /caliber:managed:learnings -->";
4408
- var LEARNINGS_BLOCK = `${LEARNINGS_BLOCK_START}
4409
- ## Session Learnings
4410
-
4411
- Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
4412
- These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
4413
- ${LEARNINGS_BLOCK_END}`;
4414
- var CURSOR_LEARNINGS_FILENAME = "caliber-learnings.mdc";
4415
- var CURSOR_LEARNINGS_CONTENT = `---
4416
- description: Reference session-learned patterns from CALIBER_LEARNINGS.md
4417
- alwaysApply: true
4418
- ---
4419
- Read \`CALIBER_LEARNINGS.md\` for patterns and anti-patterns learned from previous sessions.
4420
- These are auto-extracted from real tool usage \u2014 treat them as project-specific rules.
4421
- `;
4422
- function hasLearningsBlock(content) {
4423
- return content.includes(LEARNINGS_BLOCK_START);
4424
- }
4425
- function appendLearningsBlock(content) {
4426
- if (hasLearningsBlock(content)) return content;
4427
- const trimmed = content.trimEnd();
4428
- return trimmed + "\n\n" + LEARNINGS_BLOCK + "\n";
4429
- }
4430
- function getCursorLearningsRule() {
4431
- return { filename: CURSOR_LEARNINGS_FILENAME, content: CURSOR_LEARNINGS_CONTENT };
4432
- }
4433
-
4434
- // src/writers/claude/index.ts
4435
- function writeClaudeConfig(config) {
4436
- const written = [];
4437
- fs11.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(config.claudeMd)));
4438
- written.push("CLAUDE.md");
4439
- if (config.skills?.length) {
4440
- for (const skill of config.skills) {
4441
- const skillDir = path11.join(".claude", "skills", skill.name);
4442
- if (!fs11.existsSync(skillDir)) fs11.mkdirSync(skillDir, { recursive: true });
4443
- const skillPath = path11.join(skillDir, "SKILL.md");
4444
- const frontmatter = [
4445
- "---",
4446
- `name: ${skill.name}`,
4447
- `description: ${skill.description}`,
4448
- "---",
4449
- ""
4450
- ].join("\n");
4451
- fs11.writeFileSync(skillPath, frontmatter + skill.content);
4452
- written.push(skillPath);
4453
- }
5044
+ parts.push(allDeps.join(", "));
4454
5045
  }
4455
- if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
4456
- let existingServers = {};
4457
- try {
4458
- if (fs11.existsSync(".mcp.json")) {
4459
- const existing = JSON.parse(fs11.readFileSync(".mcp.json", "utf-8"));
4460
- if (existing.mcpServers) existingServers = existing.mcpServers;
4461
- }
4462
- } catch {
5046
+ if (prompt) parts.push(`
5047
+ User instructions: ${prompt}`);
5048
+ if (fingerprint.codeAnalysis) {
5049
+ const ca = fingerprint.codeAnalysis;
5050
+ const basePrompt = parts.join("\n");
5051
+ const maxPromptTokens = getMaxPromptTokens();
5052
+ const baseTokens = estimateTokens(basePrompt);
5053
+ const tokenBudgetForCode = Math.max(0, maxPromptTokens - baseTokens);
5054
+ const codeLines = [];
5055
+ let codeChars = 0;
5056
+ const introLine = "Study these files to extract patterns for skills. Use the exact code patterns you see here.\n";
5057
+ codeLines.push(introLine);
5058
+ let runningCodeLen = introLine.length;
5059
+ const sortedFiles = [...ca.files].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
5060
+ let includedFiles = 0;
5061
+ for (const f of sortedFiles) {
5062
+ const entry = `[${f.path}]
5063
+ ${f.content}
5064
+ `;
5065
+ const projectedLen = runningCodeLen + 1 + entry.length;
5066
+ if (Math.ceil(projectedLen / 4) > tokenBudgetForCode && includedFiles > 0) break;
5067
+ codeLines.push(entry);
5068
+ codeChars += f.content.length;
5069
+ runningCodeLen = projectedLen;
5070
+ includedFiles++;
4463
5071
  }
4464
- const mergedServers = { ...existingServers, ...config.mcpServers };
4465
- fs11.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
4466
- written.push(".mcp.json");
5072
+ const includedTokens = Math.ceil(codeChars / 4);
5073
+ let header;
5074
+ if (includedFiles < ca.files.length) {
5075
+ const pct = ca.totalProjectTokens > 0 ? Math.round(includedTokens / ca.totalProjectTokens * 100) : 100;
5076
+ header = `
5077
+ --- Project Files (trimmed to ~${includedTokens.toLocaleString()}/${ca.totalProjectTokens.toLocaleString()} tokens, ${pct}% of total) ---`;
5078
+ } else if (ca.truncated) {
5079
+ const pct = ca.totalProjectTokens > 0 ? Math.round(ca.includedTokens / ca.totalProjectTokens * 100) : 100;
5080
+ header = `
5081
+ --- Project Files (trimmed to ~${ca.includedTokens.toLocaleString()}/${ca.totalProjectTokens.toLocaleString()} tokens, ${pct}% of total) ---`;
5082
+ } else {
5083
+ header = `
5084
+ --- Project Files (${ca.files.length} files, ~${ca.includedTokens.toLocaleString()} tokens) ---`;
5085
+ }
5086
+ parts.push(header);
5087
+ parts.push(codeLines.join("\n"));
4467
5088
  }
4468
- return written;
5089
+ if (fingerprint.sources?.length) {
5090
+ parts.push(formatSourcesForPrompt(fingerprint.sources));
5091
+ }
5092
+ return parts.join("\n");
4469
5093
  }
4470
5094
 
4471
- // src/writers/cursor/index.ts
5095
+ // src/writers/index.ts
5096
+ import fs19 from "fs";
5097
+
5098
+ // src/writers/claude/index.ts
5099
+ init_pre_commit_block();
4472
5100
  import fs12 from "fs";
4473
5101
  import path12 from "path";
4474
- function writeCursorConfig(config) {
5102
+ function writeClaudeConfig(config) {
4475
5103
  const written = [];
4476
- if (config.cursorrules) {
4477
- fs12.writeFileSync(".cursorrules", config.cursorrules);
4478
- written.push(".cursorrules");
4479
- }
4480
- const preCommitRule = getCursorPreCommitRule();
4481
- const learningsRule = getCursorLearningsRule();
4482
- const allRules = [...config.rules || [], preCommitRule, learningsRule];
4483
- const rulesDir = path12.join(".cursor", "rules");
4484
- if (!fs12.existsSync(rulesDir)) fs12.mkdirSync(rulesDir, { recursive: true });
4485
- for (const rule of allRules) {
4486
- const rulePath = path12.join(rulesDir, rule.filename);
4487
- fs12.writeFileSync(rulePath, rule.content);
4488
- written.push(rulePath);
4489
- }
5104
+ fs12.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(config.claudeMd)));
5105
+ written.push("CLAUDE.md");
4490
5106
  if (config.skills?.length) {
4491
5107
  for (const skill of config.skills) {
4492
- const skillDir = path12.join(".cursor", "skills", skill.name);
5108
+ const skillDir = path12.join(".claude", "skills", skill.name);
4493
5109
  if (!fs12.existsSync(skillDir)) fs12.mkdirSync(skillDir, { recursive: true });
4494
5110
  const skillPath = path12.join(skillDir, "SKILL.md");
4495
5111
  const frontmatter = [
@@ -4504,286 +5120,183 @@ function writeCursorConfig(config) {
4504
5120
  }
4505
5121
  }
4506
5122
  if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
4507
- const cursorDir = ".cursor";
4508
- if (!fs12.existsSync(cursorDir)) fs12.mkdirSync(cursorDir, { recursive: true });
4509
- const mcpPath = path12.join(cursorDir, "mcp.json");
4510
5123
  let existingServers = {};
4511
5124
  try {
4512
- if (fs12.existsSync(mcpPath)) {
4513
- const existing = JSON.parse(fs12.readFileSync(mcpPath, "utf-8"));
5125
+ if (fs12.existsSync(".mcp.json")) {
5126
+ const existing = JSON.parse(fs12.readFileSync(".mcp.json", "utf-8"));
4514
5127
  if (existing.mcpServers) existingServers = existing.mcpServers;
4515
5128
  }
4516
5129
  } catch {
4517
5130
  }
4518
5131
  const mergedServers = { ...existingServers, ...config.mcpServers };
4519
- fs12.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
4520
- written.push(mcpPath);
4521
- }
4522
- return written;
4523
- }
4524
-
4525
- // src/writers/codex/index.ts
4526
- import fs13 from "fs";
4527
- import path13 from "path";
4528
- function writeCodexConfig(config) {
4529
- const written = [];
4530
- fs13.writeFileSync("AGENTS.md", appendLearningsBlock(appendPreCommitBlock(config.agentsMd)));
4531
- written.push("AGENTS.md");
4532
- if (config.skills?.length) {
4533
- for (const skill of config.skills) {
4534
- const skillDir = path13.join(".agents", "skills", skill.name);
4535
- if (!fs13.existsSync(skillDir)) fs13.mkdirSync(skillDir, { recursive: true });
4536
- const skillPath = path13.join(skillDir, "SKILL.md");
4537
- const frontmatter = [
4538
- "---",
4539
- `name: ${skill.name}`,
4540
- `description: ${skill.description}`,
4541
- "---",
4542
- ""
4543
- ].join("\n");
4544
- fs13.writeFileSync(skillPath, frontmatter + skill.content);
4545
- written.push(skillPath);
4546
- }
4547
- }
4548
- return written;
4549
- }
4550
-
4551
- // src/writers/github-copilot/index.ts
4552
- import fs14 from "fs";
4553
- import path14 from "path";
4554
- function writeGithubCopilotConfig(config) {
4555
- const written = [];
4556
- if (config.instructions) {
4557
- fs14.mkdirSync(".github", { recursive: true });
4558
- fs14.writeFileSync(path14.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(config.instructions)));
4559
- written.push(".github/copilot-instructions.md");
4560
- }
4561
- if (config.instructionFiles?.length) {
4562
- const instructionsDir = path14.join(".github", "instructions");
4563
- fs14.mkdirSync(instructionsDir, { recursive: true });
4564
- for (const file of config.instructionFiles) {
4565
- fs14.writeFileSync(path14.join(instructionsDir, file.filename), file.content);
4566
- written.push(`.github/instructions/${file.filename}`);
4567
- }
5132
+ fs12.writeFileSync(".mcp.json", JSON.stringify({ mcpServers: mergedServers }, null, 2));
5133
+ written.push(".mcp.json");
4568
5134
  }
4569
5135
  return written;
4570
5136
  }
4571
5137
 
4572
- // src/writers/backup.ts
4573
- import fs15 from "fs";
4574
- import path15 from "path";
4575
- function createBackup(files) {
4576
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4577
- const backupDir = path15.join(BACKUPS_DIR, timestamp);
4578
- for (const file of files) {
4579
- if (!fs15.existsSync(file)) continue;
4580
- const dest = path15.join(backupDir, file);
4581
- const destDir = path15.dirname(dest);
4582
- if (!fs15.existsSync(destDir)) {
4583
- fs15.mkdirSync(destDir, { recursive: true });
4584
- }
4585
- fs15.copyFileSync(file, dest);
4586
- }
4587
- return backupDir;
4588
- }
4589
- function restoreBackup(backupDir, file) {
4590
- const backupFile = path15.join(backupDir, file);
4591
- if (!fs15.existsSync(backupFile)) return false;
4592
- const destDir = path15.dirname(file);
4593
- if (!fs15.existsSync(destDir)) {
4594
- fs15.mkdirSync(destDir, { recursive: true });
4595
- }
4596
- fs15.copyFileSync(backupFile, file);
4597
- return true;
4598
- }
4599
-
4600
- // src/lib/builtin-skills.ts
4601
- init_resolve_caliber();
4602
- import fs16 from "fs";
4603
- import path16 from "path";
4604
- function buildSkillContent(skill) {
4605
- const frontmatter = `---
4606
- name: ${skill.name}
4607
- description: ${skill.description}
4608
- ---
4609
-
4610
- `;
4611
- return frontmatter + skill.content;
4612
- }
4613
- function getFindSkillsContent() {
4614
- const bin = resolveCaliber();
4615
- return `# Find Skills
4616
-
4617
- Search the public skill registry for community-contributed skills
4618
- relevant to the user's current task and install them into this project.
4619
-
4620
- ## Instructions
4621
-
4622
- 1. Identify the key technologies, frameworks, or task types from the
4623
- user's request that might have community skills available
4624
- 2. Ask the user: "Would you like me to search for community skills
4625
- for [identified technologies]?"
4626
- 3. If the user agrees, run:
4627
- \`\`\`bash
4628
- ${bin} skills --query "<relevant terms>"
4629
- \`\`\`
4630
- This outputs the top 5 matching skills with scores and descriptions.
4631
- 4. Present the results to the user and ask which ones to install
4632
- 5. Install the selected skills:
4633
- \`\`\`bash
4634
- ${bin} skills --install <slug1>,<slug2>
4635
- \`\`\`
4636
- 6. Read the installed SKILL.md files to load them into your current
4637
- context so you can use them immediately in this session
4638
- 7. Summarize what was installed and continue with the user's task
4639
-
4640
- ## Examples
4641
-
4642
- User: "let's build a web app using React"
4643
- -> "I notice you want to work with React. Would you like me to search
4644
- for community skills that could help with React development?"
4645
- -> If yes: run \`${bin} skills --query "react frontend"\`
4646
- -> Show the user the results, ask which to install
4647
- -> Run \`${bin} skills --install <selected-slugs>\`
4648
- -> Read the installed files and continue
4649
-
4650
- User: "help me set up Docker for this project"
4651
- -> "Would you like me to search for Docker-related skills?"
4652
- -> If yes: run \`${bin} skills --query "docker deployment"\`
4653
-
4654
- User: "I need to write tests for this Python ML pipeline"
4655
- -> "Would you like me to find skills for Python ML testing?"
4656
- -> If yes: run \`${bin} skills --query "python machine-learning testing"\`
4657
-
4658
- ## When NOT to trigger
4659
-
4660
- - The user is working within an already well-configured area
4661
- - You already suggested skills for this technology in this session
4662
- - The user is in the middle of urgent debugging or time-sensitive work
4663
- - The technology is too generic (e.g. just "code" or "programming")
4664
- `;
4665
- }
4666
- function getSaveLearningContent() {
4667
- const bin = resolveCaliber();
4668
- return `# Save Learning
4669
-
4670
- Save a user's instruction or preference as a persistent learning that
4671
- will be applied in all future sessions on this project.
4672
-
4673
- ## Instructions
4674
-
4675
- 1. Detect when the user gives an instruction to remember, such as:
4676
- - "remember this", "save this", "always do X", "never do Y"
4677
- - "from now on", "going forward", "in this project we..."
4678
- - Any stated convention, preference, or rule
4679
- 2. Refine the instruction into a clean, actionable learning bullet with
4680
- an appropriate type prefix:
4681
- - \`**[convention]**\` \u2014 coding style, workflow, git conventions
4682
- - \`**[pattern]**\` \u2014 reusable code patterns
4683
- - \`**[anti-pattern]**\` \u2014 things to avoid
4684
- - \`**[preference]**\` \u2014 personal/team preferences
4685
- - \`**[context]**\` \u2014 project-specific context
4686
- 3. Show the refined learning to the user and ask for confirmation
4687
- 4. If confirmed, run:
4688
- \`\`\`bash
4689
- ${bin} learn add "<refined learning>"
4690
- \`\`\`
4691
- For personal preferences (not project-level), add \`--personal\`:
4692
- \`\`\`bash
4693
- ${bin} learn add --personal "<refined learning>"
4694
- \`\`\`
4695
- 5. Stage the learnings file for the next commit:
4696
- \`\`\`bash
4697
- git add CALIBER_LEARNINGS.md
4698
- \`\`\`
4699
-
4700
- ## Examples
4701
-
4702
- User: "when developing features, push to next branch not master, remember it"
4703
- -> Refine: \`**[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`\`
4704
- -> "I'll save this as a project learning:
4705
- **[convention]** Push feature commits to the \\\`next\\\` branch, not \\\`master\\\`
4706
- Save for future sessions?"
4707
- -> If yes: run \`${bin} learn add "**[convention]** Push feature commits to the next branch, not master"\`
4708
- -> Run \`git add CALIBER_LEARNINGS.md\`
4709
-
4710
- User: "always use bun instead of npm"
4711
- -> Refine: \`**[preference]** Use \\\`bun\\\` instead of \\\`npm\\\` for package management\`
4712
- -> Confirm and save
4713
-
4714
- User: "never use any in TypeScript, use unknown instead"
4715
- -> Refine: \`**[convention]** Use \\\`unknown\\\` instead of \\\`any\\\` in TypeScript\`
4716
- -> Confirm and save
4717
-
4718
- ## When NOT to trigger
4719
-
4720
- - The user is giving a one-time instruction for the current task only
4721
- - The instruction is too vague to be actionable
4722
- - The user explicitly says "just for now" or "only this time"
4723
- `;
4724
- }
4725
- var FIND_SKILLS_SKILL = {
4726
- name: "find-skills",
4727
- 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.",
4728
- get content() {
4729
- return getFindSkillsContent();
5138
+ // src/writers/cursor/index.ts
5139
+ init_pre_commit_block();
5140
+ import fs13 from "fs";
5141
+ import path13 from "path";
5142
+ function writeCursorConfig(config) {
5143
+ const written = [];
5144
+ if (config.cursorrules) {
5145
+ fs13.writeFileSync(".cursorrules", config.cursorrules);
5146
+ written.push(".cursorrules");
4730
5147
  }
4731
- };
4732
- var SAVE_LEARNING_SKILL = {
4733
- name: "save-learning",
4734
- 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.",
4735
- get content() {
4736
- return getSaveLearningContent();
5148
+ const preCommitRule = getCursorPreCommitRule();
5149
+ const learningsRule = getCursorLearningsRule();
5150
+ const allRules = [...config.rules || [], preCommitRule, learningsRule];
5151
+ const rulesDir = path13.join(".cursor", "rules");
5152
+ if (!fs13.existsSync(rulesDir)) fs13.mkdirSync(rulesDir, { recursive: true });
5153
+ for (const rule of allRules) {
5154
+ const rulePath = path13.join(rulesDir, rule.filename);
5155
+ fs13.writeFileSync(rulePath, rule.content);
5156
+ written.push(rulePath);
4737
5157
  }
4738
- };
4739
- var BUILTIN_SKILLS = [FIND_SKILLS_SKILL, SAVE_LEARNING_SKILL];
4740
- var PLATFORM_CONFIGS = [
4741
- { platformDir: ".claude", skillsDir: path16.join(".claude", "skills") },
4742
- { platformDir: ".cursor", skillsDir: path16.join(".cursor", "skills") },
4743
- { platformDir: ".agents", skillsDir: path16.join(".agents", "skills") }
4744
- ];
4745
- function ensureBuiltinSkills() {
5158
+ if (config.skills?.length) {
5159
+ for (const skill of config.skills) {
5160
+ const skillDir = path13.join(".cursor", "skills", skill.name);
5161
+ if (!fs13.existsSync(skillDir)) fs13.mkdirSync(skillDir, { recursive: true });
5162
+ const skillPath = path13.join(skillDir, "SKILL.md");
5163
+ const frontmatter = [
5164
+ "---",
5165
+ `name: ${skill.name}`,
5166
+ `description: ${skill.description}`,
5167
+ "---",
5168
+ ""
5169
+ ].join("\n");
5170
+ fs13.writeFileSync(skillPath, frontmatter + skill.content);
5171
+ written.push(skillPath);
5172
+ }
5173
+ }
5174
+ if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
5175
+ const cursorDir = ".cursor";
5176
+ if (!fs13.existsSync(cursorDir)) fs13.mkdirSync(cursorDir, { recursive: true });
5177
+ const mcpPath = path13.join(cursorDir, "mcp.json");
5178
+ let existingServers = {};
5179
+ try {
5180
+ if (fs13.existsSync(mcpPath)) {
5181
+ const existing = JSON.parse(fs13.readFileSync(mcpPath, "utf-8"));
5182
+ if (existing.mcpServers) existingServers = existing.mcpServers;
5183
+ }
5184
+ } catch {
5185
+ }
5186
+ const mergedServers = { ...existingServers, ...config.mcpServers };
5187
+ fs13.writeFileSync(mcpPath, JSON.stringify({ mcpServers: mergedServers }, null, 2));
5188
+ written.push(mcpPath);
5189
+ }
5190
+ return written;
5191
+ }
5192
+
5193
+ // src/writers/codex/index.ts
5194
+ init_pre_commit_block();
5195
+ import fs14 from "fs";
5196
+ import path14 from "path";
5197
+ function writeCodexConfig(config) {
4746
5198
  const written = [];
4747
- for (const { platformDir, skillsDir } of PLATFORM_CONFIGS) {
4748
- if (!fs16.existsSync(platformDir)) continue;
4749
- for (const skill of BUILTIN_SKILLS) {
4750
- const skillPath = path16.join(skillsDir, skill.name, "SKILL.md");
4751
- if (fs16.existsSync(skillPath)) continue;
4752
- fs16.mkdirSync(path16.dirname(skillPath), { recursive: true });
4753
- fs16.writeFileSync(skillPath, buildSkillContent(skill));
5199
+ fs14.writeFileSync("AGENTS.md", appendLearningsBlock(appendPreCommitBlock(config.agentsMd)));
5200
+ written.push("AGENTS.md");
5201
+ if (config.skills?.length) {
5202
+ for (const skill of config.skills) {
5203
+ const skillDir = path14.join(".agents", "skills", skill.name);
5204
+ if (!fs14.existsSync(skillDir)) fs14.mkdirSync(skillDir, { recursive: true });
5205
+ const skillPath = path14.join(skillDir, "SKILL.md");
5206
+ const frontmatter = [
5207
+ "---",
5208
+ `name: ${skill.name}`,
5209
+ `description: ${skill.description}`,
5210
+ "---",
5211
+ ""
5212
+ ].join("\n");
5213
+ fs14.writeFileSync(skillPath, frontmatter + skill.content);
4754
5214
  written.push(skillPath);
4755
5215
  }
4756
5216
  }
4757
5217
  return written;
4758
5218
  }
4759
5219
 
5220
+ // src/writers/github-copilot/index.ts
5221
+ init_pre_commit_block();
5222
+ import fs15 from "fs";
5223
+ import path15 from "path";
5224
+ function writeGithubCopilotConfig(config) {
5225
+ const written = [];
5226
+ if (config.instructions) {
5227
+ fs15.mkdirSync(".github", { recursive: true });
5228
+ fs15.writeFileSync(path15.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(config.instructions)));
5229
+ written.push(".github/copilot-instructions.md");
5230
+ }
5231
+ if (config.instructionFiles?.length) {
5232
+ const instructionsDir = path15.join(".github", "instructions");
5233
+ fs15.mkdirSync(instructionsDir, { recursive: true });
5234
+ for (const file of config.instructionFiles) {
5235
+ fs15.writeFileSync(path15.join(instructionsDir, file.filename), file.content);
5236
+ written.push(`.github/instructions/${file.filename}`);
5237
+ }
5238
+ }
5239
+ return written;
5240
+ }
5241
+
5242
+ // src/writers/backup.ts
5243
+ import fs16 from "fs";
5244
+ import path16 from "path";
5245
+ function createBackup(files) {
5246
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
5247
+ const backupDir = path16.join(BACKUPS_DIR, timestamp);
5248
+ for (const file of files) {
5249
+ if (!fs16.existsSync(file)) continue;
5250
+ const dest = path16.join(backupDir, file);
5251
+ const destDir = path16.dirname(dest);
5252
+ if (!fs16.existsSync(destDir)) {
5253
+ fs16.mkdirSync(destDir, { recursive: true });
5254
+ }
5255
+ fs16.copyFileSync(file, dest);
5256
+ }
5257
+ return backupDir;
5258
+ }
5259
+ function restoreBackup(backupDir, file) {
5260
+ const backupFile = path16.join(backupDir, file);
5261
+ if (!fs16.existsSync(backupFile)) return false;
5262
+ const destDir = path16.dirname(file);
5263
+ if (!fs16.existsSync(destDir)) {
5264
+ fs16.mkdirSync(destDir, { recursive: true });
5265
+ }
5266
+ fs16.copyFileSync(backupFile, file);
5267
+ return true;
5268
+ }
5269
+
5270
+ // src/writers/index.ts
5271
+ init_builtin_skills();
5272
+
4760
5273
  // src/writers/manifest.ts
4761
- import fs17 from "fs";
5274
+ import fs18 from "fs";
4762
5275
  import crypto3 from "crypto";
4763
5276
  function readManifest() {
4764
5277
  try {
4765
- if (!fs17.existsSync(MANIFEST_FILE)) return null;
4766
- return JSON.parse(fs17.readFileSync(MANIFEST_FILE, "utf-8"));
5278
+ if (!fs18.existsSync(MANIFEST_FILE)) return null;
5279
+ return JSON.parse(fs18.readFileSync(MANIFEST_FILE, "utf-8"));
4767
5280
  } catch {
4768
5281
  return null;
4769
5282
  }
4770
5283
  }
4771
5284
  function writeManifest(manifest) {
4772
- if (!fs17.existsSync(CALIBER_DIR)) {
4773
- fs17.mkdirSync(CALIBER_DIR, { recursive: true });
5285
+ if (!fs18.existsSync(CALIBER_DIR)) {
5286
+ fs18.mkdirSync(CALIBER_DIR, { recursive: true });
4774
5287
  }
4775
- fs17.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
5288
+ fs18.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
4776
5289
  }
4777
5290
  function fileChecksum(filePath) {
4778
- const content = fs17.readFileSync(filePath);
5291
+ const content = fs18.readFileSync(filePath);
4779
5292
  return crypto3.createHash("sha256").update(content).digest("hex");
4780
5293
  }
4781
5294
 
4782
5295
  // src/writers/index.ts
4783
5296
  function writeSetup(setup) {
4784
5297
  const filesToWrite = getFilesToWrite(setup);
4785
- const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs18.existsSync(f));
4786
- const existingFiles = [...filesToWrite.filter((f) => fs18.existsSync(f)), ...filesToDelete];
5298
+ const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs19.existsSync(f));
5299
+ const existingFiles = [...filesToWrite.filter((f) => fs19.existsSync(f)), ...filesToDelete];
4787
5300
  const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
4788
5301
  const written = [];
4789
5302
  if (setup.targetAgent.includes("claude") && setup.claude) {
@@ -4800,7 +5313,7 @@ function writeSetup(setup) {
4800
5313
  }
4801
5314
  const deleted = [];
4802
5315
  for (const filePath of filesToDelete) {
4803
- fs18.unlinkSync(filePath);
5316
+ fs19.unlinkSync(filePath);
4804
5317
  deleted.push(filePath);
4805
5318
  }
4806
5319
  written.push(...ensureBuiltinSkills());
@@ -4831,8 +5344,8 @@ function undoSetup() {
4831
5344
  const removed = [];
4832
5345
  for (const entry of manifest.entries) {
4833
5346
  if (entry.action === "created") {
4834
- if (fs18.existsSync(entry.path)) {
4835
- fs18.unlinkSync(entry.path);
5347
+ if (fs19.existsSync(entry.path)) {
5348
+ fs19.unlinkSync(entry.path);
4836
5349
  removed.push(entry.path);
4837
5350
  }
4838
5351
  } else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
@@ -4841,8 +5354,8 @@ function undoSetup() {
4841
5354
  }
4842
5355
  }
4843
5356
  }
4844
- if (fs18.existsSync(MANIFEST_FILE)) {
4845
- fs18.unlinkSync(MANIFEST_FILE);
5357
+ if (fs19.existsSync(MANIFEST_FILE)) {
5358
+ fs19.unlinkSync(MANIFEST_FILE);
4846
5359
  }
4847
5360
  return { restored, removed };
4848
5361
  }
@@ -4884,22 +5397,22 @@ function getFilesToWrite(setup) {
4884
5397
  }
4885
5398
  function ensureGitignore() {
4886
5399
  const gitignorePath = ".gitignore";
4887
- if (fs18.existsSync(gitignorePath)) {
4888
- const content = fs18.readFileSync(gitignorePath, "utf-8");
5400
+ if (fs19.existsSync(gitignorePath)) {
5401
+ const content = fs19.readFileSync(gitignorePath, "utf-8");
4889
5402
  if (!content.includes(".caliber/")) {
4890
- fs18.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
5403
+ fs19.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
4891
5404
  }
4892
5405
  } else {
4893
- fs18.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
5406
+ fs19.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
4894
5407
  }
4895
5408
  }
4896
5409
 
4897
5410
  // src/writers/staging.ts
4898
- import fs19 from "fs";
4899
- import path17 from "path";
4900
- var STAGED_DIR = path17.join(CALIBER_DIR, "staged");
4901
- var PROPOSED_DIR = path17.join(STAGED_DIR, "proposed");
4902
- var CURRENT_DIR = path17.join(STAGED_DIR, "current");
5411
+ import fs20 from "fs";
5412
+ import path18 from "path";
5413
+ var STAGED_DIR = path18.join(CALIBER_DIR, "staged");
5414
+ var PROPOSED_DIR = path18.join(STAGED_DIR, "proposed");
5415
+ var CURRENT_DIR = path18.join(STAGED_DIR, "current");
4903
5416
  function normalizeContent(content) {
4904
5417
  return content.split("\n").map((line) => line.trimEnd()).join("\n").replace(/\n{3,}/g, "\n\n").trim();
4905
5418
  }
@@ -4910,20 +5423,20 @@ function stageFiles(files, projectDir) {
4910
5423
  const stagedFiles = [];
4911
5424
  for (const file of files) {
4912
5425
  assertPathWithinDir(file.path, projectDir);
4913
- const originalPath = path17.join(projectDir, file.path);
4914
- if (fs19.existsSync(originalPath)) {
4915
- const existing = fs19.readFileSync(originalPath, "utf-8");
5426
+ const originalPath = path18.join(projectDir, file.path);
5427
+ if (fs20.existsSync(originalPath)) {
5428
+ const existing = fs20.readFileSync(originalPath, "utf-8");
4916
5429
  if (normalizeContent(existing) === normalizeContent(file.content)) {
4917
5430
  continue;
4918
5431
  }
4919
5432
  }
4920
- const proposedPath = path17.join(PROPOSED_DIR, file.path);
4921
- fs19.mkdirSync(path17.dirname(proposedPath), { recursive: true });
4922
- fs19.writeFileSync(proposedPath, file.content);
4923
- if (fs19.existsSync(originalPath)) {
4924
- const currentPath = path17.join(CURRENT_DIR, file.path);
4925
- fs19.mkdirSync(path17.dirname(currentPath), { recursive: true });
4926
- fs19.copyFileSync(originalPath, currentPath);
5433
+ const proposedPath = path18.join(PROPOSED_DIR, file.path);
5434
+ fs20.mkdirSync(path18.dirname(proposedPath), { recursive: true });
5435
+ fs20.writeFileSync(proposedPath, file.content);
5436
+ if (fs20.existsSync(originalPath)) {
5437
+ const currentPath = path18.join(CURRENT_DIR, file.path);
5438
+ fs20.mkdirSync(path18.dirname(currentPath), { recursive: true });
5439
+ fs20.copyFileSync(originalPath, currentPath);
4927
5440
  modifiedFiles++;
4928
5441
  stagedFiles.push({
4929
5442
  relativePath: file.path,
@@ -4940,13 +5453,14 @@ function stageFiles(files, projectDir) {
4940
5453
  return { newFiles, modifiedFiles, stagedFiles };
4941
5454
  }
4942
5455
  function cleanupStaging() {
4943
- if (fs19.existsSync(STAGED_DIR)) {
4944
- fs19.rmSync(STAGED_DIR, { recursive: true, force: true });
5456
+ if (fs20.existsSync(STAGED_DIR)) {
5457
+ fs20.rmSync(STAGED_DIR, { recursive: true, force: true });
4945
5458
  }
4946
5459
  }
4947
5460
 
4948
5461
  // src/commands/setup-files.ts
4949
- import fs20 from "fs";
5462
+ init_builtin_skills();
5463
+ import fs21 from "fs";
4950
5464
  function collectSetupFiles(setup, targetAgent) {
4951
5465
  const files = [];
4952
5466
  const claude = setup.claude;
@@ -5031,7 +5545,7 @@ function collectSetupFiles(setup, targetAgent) {
5031
5545
  }
5032
5546
  }
5033
5547
  const codexTargeted = targetAgent ? targetAgent.includes("codex") : false;
5034
- if (codexTargeted && !fs20.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
5548
+ if (codexTargeted && !fs21.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
5035
5549
  const agentRefs = [];
5036
5550
  if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
5037
5551
  if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
@@ -5050,9 +5564,9 @@ ${agentRefs.join(" ")}
5050
5564
 
5051
5565
  // src/lib/learning-hooks.ts
5052
5566
  init_resolve_caliber();
5053
- import fs21 from "fs";
5054
- import path18 from "path";
5055
- var SETTINGS_PATH = path18.join(".claude", "settings.json");
5567
+ import fs22 from "fs";
5568
+ import path19 from "path";
5569
+ var SETTINGS_PATH2 = path19.join(".claude", "settings.json");
5056
5570
  var HOOK_TAILS = [
5057
5571
  { event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
5058
5572
  { event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
@@ -5068,24 +5582,24 @@ function getHookConfigs() {
5068
5582
  description
5069
5583
  }));
5070
5584
  }
5071
- function readSettings() {
5072
- if (!fs21.existsSync(SETTINGS_PATH)) return {};
5585
+ function readSettings2() {
5586
+ if (!fs22.existsSync(SETTINGS_PATH2)) return {};
5073
5587
  try {
5074
- return JSON.parse(fs21.readFileSync(SETTINGS_PATH, "utf-8"));
5588
+ return JSON.parse(fs22.readFileSync(SETTINGS_PATH2, "utf-8"));
5075
5589
  } catch {
5076
5590
  return {};
5077
5591
  }
5078
5592
  }
5079
- function writeSettings(settings) {
5080
- const dir = path18.dirname(SETTINGS_PATH);
5081
- if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
5082
- fs21.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
5593
+ function writeSettings2(settings) {
5594
+ const dir = path19.dirname(SETTINGS_PATH2);
5595
+ if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
5596
+ fs22.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
5083
5597
  }
5084
5598
  function hasLearningHook(matchers, tail) {
5085
5599
  return matchers.some((entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, tail)));
5086
5600
  }
5087
5601
  function areLearningHooksInstalled() {
5088
- const settings = readSettings();
5602
+ const settings = readSettings2();
5089
5603
  if (!settings.hooks) return false;
5090
5604
  return HOOK_TAILS.every((cfg) => {
5091
5605
  const matchers = settings.hooks[cfg.event];
@@ -5096,7 +5610,7 @@ function installLearningHooks() {
5096
5610
  if (areLearningHooksInstalled()) {
5097
5611
  return { installed: false, alreadyInstalled: true };
5098
5612
  }
5099
- const settings = readSettings();
5613
+ const settings = readSettings2();
5100
5614
  if (!settings.hooks) settings.hooks = {};
5101
5615
  const configs = getHookConfigs();
5102
5616
  for (const cfg of configs) {
@@ -5110,10 +5624,10 @@ function installLearningHooks() {
5110
5624
  });
5111
5625
  }
5112
5626
  }
5113
- writeSettings(settings);
5627
+ writeSettings2(settings);
5114
5628
  return { installed: true, alreadyInstalled: false };
5115
5629
  }
5116
- var CURSOR_HOOKS_PATH = path18.join(".cursor", "hooks.json");
5630
+ var CURSOR_HOOKS_PATH = path19.join(".cursor", "hooks.json");
5117
5631
  var CURSOR_HOOK_EVENTS = [
5118
5632
  { event: "postToolUse", tail: "learn observe" },
5119
5633
  { event: "postToolUseFailure", tail: "learn observe --failure" },
@@ -5121,17 +5635,17 @@ var CURSOR_HOOK_EVENTS = [
5121
5635
  { event: "sessionEnd", tail: "learn finalize --auto" }
5122
5636
  ];
5123
5637
  function readCursorHooks() {
5124
- if (!fs21.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
5638
+ if (!fs22.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
5125
5639
  try {
5126
- return JSON.parse(fs21.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
5640
+ return JSON.parse(fs22.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
5127
5641
  } catch {
5128
5642
  return { version: 1, hooks: {} };
5129
5643
  }
5130
5644
  }
5131
5645
  function writeCursorHooks(config) {
5132
- const dir = path18.dirname(CURSOR_HOOKS_PATH);
5133
- if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
5134
- fs21.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
5646
+ const dir = path19.dirname(CURSOR_HOOKS_PATH);
5647
+ if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
5648
+ fs22.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
5135
5649
  }
5136
5650
  function hasCursorHook(entries, tail) {
5137
5651
  return entries.some((e) => isCaliberCommand(e.command, tail));
@@ -5178,7 +5692,7 @@ function removeCursorLearningHooks() {
5178
5692
  return { removed: true, notFound: false };
5179
5693
  }
5180
5694
  function removeLearningHooks() {
5181
- const settings = readSettings();
5695
+ const settings = readSettings2();
5182
5696
  if (!settings.hooks) return { removed: false, notFound: true };
5183
5697
  let removedAny = false;
5184
5698
  for (const cfg of HOOK_TAILS) {
@@ -5195,7 +5709,7 @@ function removeLearningHooks() {
5195
5709
  delete settings.hooks;
5196
5710
  }
5197
5711
  if (!removedAny) return { removed: false, notFound: true };
5198
- writeSettings(settings);
5712
+ writeSettings2(settings);
5199
5713
  return { removed: true, notFound: false };
5200
5714
  }
5201
5715
 
@@ -5203,10 +5717,10 @@ function removeLearningHooks() {
5203
5717
  init_resolve_caliber();
5204
5718
 
5205
5719
  // src/lib/state.ts
5206
- import fs22 from "fs";
5207
- import path19 from "path";
5208
- import { execSync as execSync8 } from "child_process";
5209
- var STATE_FILE = path19.join(CALIBER_DIR, ".caliber-state.json");
5720
+ import fs23 from "fs";
5721
+ import path20 from "path";
5722
+ import { execSync as execSync9 } from "child_process";
5723
+ var STATE_FILE = path20.join(CALIBER_DIR, ".caliber-state.json");
5210
5724
  function normalizeTargetAgent(value) {
5211
5725
  if (Array.isArray(value)) return value;
5212
5726
  if (typeof value === "string") {
@@ -5217,8 +5731,8 @@ function normalizeTargetAgent(value) {
5217
5731
  }
5218
5732
  function readState() {
5219
5733
  try {
5220
- if (!fs22.existsSync(STATE_FILE)) return null;
5221
- const raw = JSON.parse(fs22.readFileSync(STATE_FILE, "utf-8"));
5734
+ if (!fs23.existsSync(STATE_FILE)) return null;
5735
+ const raw = JSON.parse(fs23.readFileSync(STATE_FILE, "utf-8"));
5222
5736
  if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
5223
5737
  return raw;
5224
5738
  } catch {
@@ -5226,14 +5740,14 @@ function readState() {
5226
5740
  }
5227
5741
  }
5228
5742
  function writeState(state) {
5229
- if (!fs22.existsSync(CALIBER_DIR)) {
5230
- fs22.mkdirSync(CALIBER_DIR, { recursive: true });
5743
+ if (!fs23.existsSync(CALIBER_DIR)) {
5744
+ fs23.mkdirSync(CALIBER_DIR, { recursive: true });
5231
5745
  }
5232
- fs22.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
5746
+ fs23.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
5233
5747
  }
5234
5748
  function getCurrentHeadSha() {
5235
5749
  try {
5236
- return execSync8("git rev-parse HEAD", {
5750
+ return execSync9("git rev-parse HEAD", {
5237
5751
  encoding: "utf-8",
5238
5752
  stdio: ["pipe", "pipe", "pipe"]
5239
5753
  }).trim();
@@ -5343,6 +5857,9 @@ async function runInteractiveProviderSetup(options) {
5343
5857
  return config;
5344
5858
  }
5345
5859
 
5860
+ // src/commands/init.ts
5861
+ import confirm2 from "@inquirer/confirm";
5862
+
5346
5863
  // src/scoring/index.ts
5347
5864
  import { existsSync as existsSync7 } from "fs";
5348
5865
  import { join as join9 } from "path";
@@ -5833,7 +6350,7 @@ function checkGrounding(dir) {
5833
6350
 
5834
6351
  // src/scoring/checks/accuracy.ts
5835
6352
  import { existsSync as existsSync4, statSync } from "fs";
5836
- import { execSync as execSync9 } from "child_process";
6353
+ import { execSync as execSync10 } from "child_process";
5837
6354
  import { join as join5 } from "path";
5838
6355
  init_resolve_caliber();
5839
6356
  function validateReferences(dir) {
@@ -5843,13 +6360,13 @@ function validateReferences(dir) {
5843
6360
  }
5844
6361
  function detectGitDrift(dir) {
5845
6362
  try {
5846
- execSync9("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
6363
+ execSync10("git rev-parse --git-dir", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] });
5847
6364
  } catch {
5848
6365
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: false };
5849
6366
  }
5850
6367
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules", ".cursor/rules"];
5851
6368
  try {
5852
- const headTimestamp = execSync9(
6369
+ const headTimestamp = execSync10(
5853
6370
  "git log -1 --format=%ct HEAD",
5854
6371
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5855
6372
  ).trim();
@@ -5870,7 +6387,7 @@ function detectGitDrift(dir) {
5870
6387
  let latestConfigCommitHash = null;
5871
6388
  for (const file of configFiles) {
5872
6389
  try {
5873
- const hash = execSync9(
6390
+ const hash = execSync10(
5874
6391
  `git log -1 --format=%H -- "${file}"`,
5875
6392
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5876
6393
  ).trim();
@@ -5879,7 +6396,7 @@ function detectGitDrift(dir) {
5879
6396
  latestConfigCommitHash = hash;
5880
6397
  } else {
5881
6398
  try {
5882
- execSync9(
6399
+ execSync10(
5883
6400
  `git merge-base --is-ancestor ${latestConfigCommitHash} ${hash}`,
5884
6401
  { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }
5885
6402
  );
@@ -5894,12 +6411,12 @@ function detectGitDrift(dir) {
5894
6411
  return { commitsSinceConfigUpdate: 0, lastConfigCommit: null, isGitRepo: true };
5895
6412
  }
5896
6413
  try {
5897
- const countStr = execSync9(
6414
+ const countStr = execSync10(
5898
6415
  `git rev-list --count ${latestConfigCommitHash}..HEAD`,
5899
6416
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5900
6417
  ).trim();
5901
6418
  const commitsSince = parseInt(countStr, 10) || 0;
5902
- const lastDate = execSync9(
6419
+ const lastDate = execSync10(
5903
6420
  `git log -1 --format=%ci ${latestConfigCommitHash}`,
5904
6421
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5905
6422
  ).trim();
@@ -5972,12 +6489,12 @@ function checkAccuracy(dir) {
5972
6489
  // src/scoring/checks/freshness.ts
5973
6490
  init_resolve_caliber();
5974
6491
  import { existsSync as existsSync5, statSync as statSync2 } from "fs";
5975
- import { execSync as execSync10 } from "child_process";
6492
+ import { execSync as execSync11 } from "child_process";
5976
6493
  import { join as join6 } from "path";
5977
6494
  function getCommitsSinceConfigUpdate(dir) {
5978
6495
  const configFiles = ["CLAUDE.md", "AGENTS.md", ".cursorrules"];
5979
6496
  try {
5980
- const headTimestamp = execSync10(
6497
+ const headTimestamp = execSync11(
5981
6498
  "git log -1 --format=%ct HEAD",
5982
6499
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5983
6500
  ).trim();
@@ -5997,12 +6514,12 @@ function getCommitsSinceConfigUpdate(dir) {
5997
6514
  }
5998
6515
  for (const file of configFiles) {
5999
6516
  try {
6000
- const hash = execSync10(
6517
+ const hash = execSync11(
6001
6518
  `git log -1 --format=%H -- "${file}"`,
6002
6519
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
6003
6520
  ).trim();
6004
6521
  if (hash) {
6005
- const countStr = execSync10(
6522
+ const countStr = execSync11(
6006
6523
  `git rev-list --count ${hash}..HEAD`,
6007
6524
  { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
6008
6525
  ).trim();
@@ -6120,12 +6637,13 @@ function checkFreshness(dir) {
6120
6637
 
6121
6638
  // src/scoring/checks/bonus.ts
6122
6639
  import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
6123
- import { execSync as execSync11 } from "child_process";
6640
+ import { execSync as execSync12 } from "child_process";
6124
6641
  import { join as join7 } from "path";
6125
6642
  init_resolve_caliber();
6643
+ init_pre_commit_block();
6126
6644
  function hasPreCommitHook(dir) {
6127
6645
  try {
6128
- const gitDir = execSync11("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
6646
+ const gitDir = execSync12("git rev-parse --git-dir", { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
6129
6647
  const hookPath = join7(gitDir, "hooks", "pre-commit");
6130
6648
  const content = readFileOrNull(hookPath);
6131
6649
  return content ? content.includes("caliber") : false;
@@ -6282,22 +6800,22 @@ function checkSources(dir) {
6282
6800
  }
6283
6801
 
6284
6802
  // src/scoring/dismissed.ts
6285
- import fs23 from "fs";
6286
- import path20 from "path";
6287
- var DISMISSED_FILE = path20.join(CALIBER_DIR, "dismissed-checks.json");
6803
+ import fs24 from "fs";
6804
+ import path21 from "path";
6805
+ var DISMISSED_FILE = path21.join(CALIBER_DIR, "dismissed-checks.json");
6288
6806
  function readDismissedChecks() {
6289
6807
  try {
6290
- if (!fs23.existsSync(DISMISSED_FILE)) return [];
6291
- return JSON.parse(fs23.readFileSync(DISMISSED_FILE, "utf-8"));
6808
+ if (!fs24.existsSync(DISMISSED_FILE)) return [];
6809
+ return JSON.parse(fs24.readFileSync(DISMISSED_FILE, "utf-8"));
6292
6810
  } catch {
6293
6811
  return [];
6294
6812
  }
6295
6813
  }
6296
6814
  function writeDismissedChecks(checks) {
6297
- if (!fs23.existsSync(CALIBER_DIR)) {
6298
- fs23.mkdirSync(CALIBER_DIR, { recursive: true });
6815
+ if (!fs24.existsSync(CALIBER_DIR)) {
6816
+ fs24.mkdirSync(CALIBER_DIR, { recursive: true });
6299
6817
  }
6300
- fs23.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
6818
+ fs24.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
6301
6819
  }
6302
6820
  function getDismissedIds() {
6303
6821
  return new Set(readDismissedChecks().map((c) => c.id));
@@ -6327,8 +6845,8 @@ function detectTargetAgent(dir) {
6327
6845
  const agents = [];
6328
6846
  if (existsSync7(join9(dir, "CLAUDE.md")) || existsSync7(join9(dir, ".claude", "skills"))) agents.push("claude");
6329
6847
  if (existsSync7(join9(dir, ".cursorrules")) || existsSync7(join9(dir, ".cursor", "rules"))) agents.push("cursor");
6330
- if (existsSync7(join9(dir, ".codex")) || existsSync7(join9(dir, ".agents", "skills"))) agents.push("codex");
6331
- if (existsSync7(join9(dir, ".github", "copilot-instructions.md"))) agents.push("github-copilot");
6848
+ if (existsSync7(join9(dir, ".codex")) || existsSync7(join9(dir, ".agents", "skills")) || existsSync7(join9(dir, "AGENTS.md"))) agents.push("codex");
6849
+ if (existsSync7(join9(dir, ".github", "copilot-instructions.md")) || existsSync7(join9(dir, ".github", "instructions"))) agents.push("github-copilot");
6332
6850
  return agents.length > 0 ? agents : ["claude"];
6333
6851
  }
6334
6852
  function computeLocalScore(dir, targetAgent) {
@@ -6552,27 +7070,27 @@ import { PostHog } from "posthog-node";
6552
7070
  import chalk5 from "chalk";
6553
7071
 
6554
7072
  // src/telemetry/config.ts
6555
- import fs24 from "fs";
6556
- import path21 from "path";
7073
+ import fs25 from "fs";
7074
+ import path22 from "path";
6557
7075
  import os5 from "os";
6558
7076
  import crypto4 from "crypto";
6559
- import { execSync as execSync12 } from "child_process";
6560
- var CONFIG_DIR2 = path21.join(os5.homedir(), ".caliber");
6561
- var CONFIG_FILE2 = path21.join(CONFIG_DIR2, "config.json");
7077
+ import { execSync as execSync13 } from "child_process";
7078
+ var CONFIG_DIR2 = path22.join(os5.homedir(), ".caliber");
7079
+ var CONFIG_FILE2 = path22.join(CONFIG_DIR2, "config.json");
6562
7080
  var runtimeDisabled = false;
6563
7081
  function readConfig() {
6564
7082
  try {
6565
- if (!fs24.existsSync(CONFIG_FILE2)) return {};
6566
- return JSON.parse(fs24.readFileSync(CONFIG_FILE2, "utf-8"));
7083
+ if (!fs25.existsSync(CONFIG_FILE2)) return {};
7084
+ return JSON.parse(fs25.readFileSync(CONFIG_FILE2, "utf-8"));
6567
7085
  } catch {
6568
7086
  return {};
6569
7087
  }
6570
7088
  }
6571
7089
  function writeConfig(config) {
6572
- if (!fs24.existsSync(CONFIG_DIR2)) {
6573
- fs24.mkdirSync(CONFIG_DIR2, { recursive: true });
7090
+ if (!fs25.existsSync(CONFIG_DIR2)) {
7091
+ fs25.mkdirSync(CONFIG_DIR2, { recursive: true });
6574
7092
  }
6575
- fs24.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
7093
+ fs25.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
6576
7094
  }
6577
7095
  function getMachineId() {
6578
7096
  const config = readConfig();
@@ -6582,11 +7100,57 @@ function getMachineId() {
6582
7100
  return machineId;
6583
7101
  }
6584
7102
  var EMAIL_HASH_KEY = "caliber-telemetry-v1";
6585
- function getGitEmailHash() {
7103
+ var PERSONAL_DOMAINS = /* @__PURE__ */ new Set([
7104
+ "gmail.com",
7105
+ "googlemail.com",
7106
+ "outlook.com",
7107
+ "hotmail.com",
7108
+ "live.com",
7109
+ "yahoo.com",
7110
+ "yahoo.co.uk",
7111
+ "icloud.com",
7112
+ "me.com",
7113
+ "mac.com",
7114
+ "aol.com",
7115
+ "protonmail.com",
7116
+ "proton.me",
7117
+ "pm.me",
7118
+ "mail.com",
7119
+ "zoho.com",
7120
+ "yandex.com",
7121
+ "gmx.com",
7122
+ "gmx.net",
7123
+ "fastmail.com",
7124
+ "tutanota.com",
7125
+ "tuta.io"
7126
+ ]);
7127
+ function getGitEmail() {
6586
7128
  try {
6587
- const email = execSync12("git config user.email", { encoding: "utf-8" }).trim();
6588
- if (!email) return void 0;
6589
- return crypto4.createHmac("sha256", EMAIL_HASH_KEY).update(email).digest("hex");
7129
+ const email = execSync13("git config user.email", { encoding: "utf-8" }).trim();
7130
+ return email || void 0;
7131
+ } catch {
7132
+ return void 0;
7133
+ }
7134
+ }
7135
+ function getGitEmailInfo() {
7136
+ const email = getGitEmail();
7137
+ if (!email) return {};
7138
+ const hash = crypto4.createHmac("sha256", EMAIL_HASH_KEY).update(email).digest("hex");
7139
+ let domain;
7140
+ if (email.includes("@")) {
7141
+ const d = email.split("@")[1].toLowerCase();
7142
+ if (!PERSONAL_DOMAINS.has(d)) domain = d;
7143
+ }
7144
+ return { hash, domain };
7145
+ }
7146
+ function getRepoHash() {
7147
+ try {
7148
+ const remote = execSync13("git remote get-url origin || git rev-parse --show-toplevel", {
7149
+ encoding: "utf-8",
7150
+ stdio: ["pipe", "pipe", "pipe"]
7151
+ }).trim();
7152
+ if (!remote) return void 0;
7153
+ return crypto4.createHmac("sha256", EMAIL_HASH_KEY).update(remote).digest("hex").slice(0, 16);
6590
7154
  } catch {
6591
7155
  return void 0;
6592
7156
  }
@@ -6611,6 +7175,7 @@ function markNoticeShown() {
6611
7175
  var POSTHOG_KEY = "phc_XXrV0pSX4s2QVxVoOaeuyXDvtlRwPAjovt1ttMGVMPp";
6612
7176
  var client = null;
6613
7177
  var distinctId = null;
7178
+ var superProperties = {};
6614
7179
  function initTelemetry() {
6615
7180
  if (isTelemetryDisabled()) return;
6616
7181
  const machineId = getMachineId();
@@ -6626,11 +7191,17 @@ function initTelemetry() {
6626
7191
  );
6627
7192
  markNoticeShown();
6628
7193
  }
6629
- const gitEmailHash = getGitEmailHash();
7194
+ const { hash: gitEmailHash, domain: emailDomain } = getGitEmailInfo();
7195
+ const repoHash = getRepoHash();
7196
+ superProperties = {
7197
+ ...repoHash ? { repo_hash: repoHash } : {},
7198
+ ...emailDomain ? { email_domain: emailDomain } : {}
7199
+ };
6630
7200
  client.identify({
6631
7201
  distinctId: machineId,
6632
7202
  properties: {
6633
- ...gitEmailHash ? { git_email_hash: gitEmailHash } : {}
7203
+ ...gitEmailHash ? { git_email_hash: gitEmailHash } : {},
7204
+ ...emailDomain ? { email_domain: emailDomain } : {}
6634
7205
  }
6635
7206
  });
6636
7207
  }
@@ -6639,7 +7210,7 @@ function trackEvent(name, properties) {
6639
7210
  client.capture({
6640
7211
  distinctId,
6641
7212
  event: name,
6642
- properties: properties ?? {}
7213
+ properties: { ...superProperties, ...properties }
6643
7214
  });
6644
7215
  }
6645
7216
  async function flushTelemetry() {
@@ -6688,11 +7259,14 @@ function trackInitSkillsSearch(searched, installedCount) {
6688
7259
  function trackInitScoreRegression(oldScore, newScore) {
6689
7260
  trackEvent("init_score_regression", { old_score: oldScore, new_score: newScore });
6690
7261
  }
7262
+ function trackInitCompleted(path42, score) {
7263
+ trackEvent("init_completed", { path: path42, score });
7264
+ }
6691
7265
  function trackRegenerateCompleted(action, durationMs) {
6692
7266
  trackEvent("regenerate_completed", { action, duration_ms: durationMs });
6693
7267
  }
6694
- function trackRefreshCompleted(changesCount, durationMs) {
6695
- trackEvent("refresh_completed", { changes_count: changesCount, duration_ms: durationMs });
7268
+ function trackRefreshCompleted(changesCount, durationMs, trigger) {
7269
+ trackEvent("refresh_completed", { changes_count: changesCount, duration_ms: durationMs, trigger: trigger ?? "manual" });
6696
7270
  }
6697
7271
  function trackScoreComputed(score, agent) {
6698
7272
  trackEvent("score_computed", { score, agent });
@@ -6706,6 +7280,9 @@ function trackSkillsInstalled(count) {
6706
7280
  function trackUndoExecuted() {
6707
7281
  trackEvent("undo_executed");
6708
7282
  }
7283
+ function trackUninstallExecuted() {
7284
+ trackEvent("uninstall_executed");
7285
+ }
6709
7286
  function trackInitLearnEnabled(enabled) {
6710
7287
  trackEvent("init_learn_enabled", { enabled });
6711
7288
  }
@@ -7677,11 +8254,11 @@ function countIssuePoints(issues) {
7677
8254
  }
7678
8255
  async function scoreAndRefine(setup, dir, sessionHistory, callbacks) {
7679
8256
  const existsCache = /* @__PURE__ */ new Map();
7680
- const cachedExists = (path41) => {
7681
- const cached = existsCache.get(path41);
8257
+ const cachedExists = (path42) => {
8258
+ const cached = existsCache.get(path42);
7682
8259
  if (cached !== void 0) return cached;
7683
- const result = existsSync9(path41);
7684
- existsCache.set(path41, result);
8260
+ const result = existsSync9(path42);
8261
+ existsCache.set(path42, result);
7685
8262
  return result;
7686
8263
  };
7687
8264
  const projectStructure = collectProjectStructure(dir);
@@ -7820,8 +8397,8 @@ async function runScoreRefineWithSpinner(setup, dir, sessionHistory) {
7820
8397
  }
7821
8398
 
7822
8399
  // src/lib/debug-report.ts
7823
- import fs25 from "fs";
7824
- import path22 from "path";
8400
+ import fs26 from "fs";
8401
+ import path23 from "path";
7825
8402
  var DebugReport = class {
7826
8403
  sections = [];
7827
8404
  startTime;
@@ -7890,11 +8467,11 @@ var DebugReport = class {
7890
8467
  lines.push(`| **Total** | **${formatMs(totalMs)}** |`);
7891
8468
  lines.push("");
7892
8469
  }
7893
- const dir = path22.dirname(outputPath);
7894
- if (!fs25.existsSync(dir)) {
7895
- fs25.mkdirSync(dir, { recursive: true });
8470
+ const dir = path23.dirname(outputPath);
8471
+ if (!fs26.existsSync(dir)) {
8472
+ fs26.mkdirSync(dir, { recursive: true });
7896
8473
  }
7897
- fs25.writeFileSync(outputPath, lines.join("\n"));
8474
+ fs26.writeFileSync(outputPath, lines.join("\n"));
7898
8475
  }
7899
8476
  };
7900
8477
  function formatMs(ms) {
@@ -8295,7 +8872,7 @@ import chalk11 from "chalk";
8295
8872
  import ora3 from "ora";
8296
8873
  import select5 from "@inquirer/select";
8297
8874
  import checkbox from "@inquirer/checkbox";
8298
- import fs28 from "fs";
8875
+ import fs29 from "fs";
8299
8876
 
8300
8877
  // src/ai/refine.ts
8301
8878
  async function refineSetup(currentSetup, message, conversationHistory, callbacks) {
@@ -8436,10 +9013,10 @@ init_config();
8436
9013
  init_review();
8437
9014
  function detectAgents(dir) {
8438
9015
  const agents = [];
8439
- if (fs28.existsSync(`${dir}/.claude`)) agents.push("claude");
8440
- if (fs28.existsSync(`${dir}/.cursor`)) agents.push("cursor");
8441
- if (fs28.existsSync(`${dir}/.agents`) || fs28.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
8442
- if (fs28.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
9016
+ if (fs29.existsSync(`${dir}/.claude`)) agents.push("claude");
9017
+ if (fs29.existsSync(`${dir}/.cursor`)) agents.push("cursor");
9018
+ if (fs29.existsSync(`${dir}/.agents`) || fs29.existsSync(`${dir}/AGENTS.md`)) agents.push("codex");
9019
+ if (fs29.existsSync(`${dir}/.github/copilot-instructions.md`)) agents.push("github-copilot");
8443
9020
  return agents;
8444
9021
  }
8445
9022
  async function promptAgent(detected) {
@@ -8461,25 +9038,6 @@ async function promptAgent(detected) {
8461
9038
  });
8462
9039
  return selected;
8463
9040
  }
8464
- async function promptLearnInstall(targetAgent) {
8465
- const hasClaude = targetAgent.includes("claude");
8466
- const hasCursor = targetAgent.includes("cursor");
8467
- const agentName = hasClaude && hasCursor ? "Claude and Cursor" : hasClaude ? "Claude" : "Cursor";
8468
- console.log(chalk11.bold(`
8469
- Session Learning
8470
- `));
8471
- console.log(chalk11.dim(` Caliber can learn from your ${agentName} sessions \u2014 when a tool fails`));
8472
- console.log(chalk11.dim(` or you correct a mistake, it captures the lesson so it won't`));
8473
- console.log(chalk11.dim(` happen again. Runs once at session end using the fast model.
8474
- `));
8475
- return select5({
8476
- message: "Enable session learning?",
8477
- choices: [
8478
- { name: "Enable session learning (recommended)", value: true },
8479
- { name: "Skip for now", value: false }
8480
- ]
8481
- });
8482
- }
8483
9041
  async function promptReviewAction(hasSkillResults, hasChanges, staged) {
8484
9042
  if (!hasChanges && !hasSkillResults) return "accept";
8485
9043
  const choices = [];
@@ -8561,18 +9119,18 @@ async function refineLoop(currentSetup, sessionHistory, summarizeSetup2, printSu
8561
9119
 
8562
9120
  // src/commands/init-display.ts
8563
9121
  import chalk12 from "chalk";
8564
- import fs29 from "fs";
9122
+ import fs30 from "fs";
8565
9123
  function formatWhatChanged(setup) {
8566
9124
  const lines = [];
8567
9125
  const claude = setup.claude;
8568
9126
  const codex = setup.codex;
8569
9127
  const cursor = setup.cursor;
8570
9128
  if (claude?.claudeMd) {
8571
- const action = fs29.existsSync("CLAUDE.md") ? "Updated" : "Created";
9129
+ const action = fs30.existsSync("CLAUDE.md") ? "Updated" : "Created";
8572
9130
  lines.push(`${action} CLAUDE.md`);
8573
9131
  }
8574
9132
  if (codex?.agentsMd) {
8575
- const action = fs29.existsSync("AGENTS.md") ? "Updated" : "Created";
9133
+ const action = fs30.existsSync("AGENTS.md") ? "Updated" : "Created";
8576
9134
  lines.push(`${action} AGENTS.md`);
8577
9135
  }
8578
9136
  const allSkills = [];
@@ -8615,7 +9173,7 @@ function printSetupSummary(setup) {
8615
9173
  };
8616
9174
  if (claude) {
8617
9175
  if (claude.claudeMd) {
8618
- const icon = fs29.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
9176
+ const icon = fs30.existsSync("CLAUDE.md") ? chalk12.yellow("~") : chalk12.green("+");
8619
9177
  const desc = getDescription("CLAUDE.md");
8620
9178
  console.log(` ${icon} ${chalk12.bold("CLAUDE.md")}`);
8621
9179
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -8625,7 +9183,7 @@ function printSetupSummary(setup) {
8625
9183
  if (Array.isArray(skills) && skills.length > 0) {
8626
9184
  for (const skill of skills) {
8627
9185
  const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
8628
- const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
9186
+ const icon = fs30.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8629
9187
  const desc = getDescription(skillPath);
8630
9188
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
8631
9189
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -8636,7 +9194,7 @@ function printSetupSummary(setup) {
8636
9194
  const codex = setup.codex;
8637
9195
  if (codex) {
8638
9196
  if (codex.agentsMd) {
8639
- const icon = fs29.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
9197
+ const icon = fs30.existsSync("AGENTS.md") ? chalk12.yellow("~") : chalk12.green("+");
8640
9198
  const desc = getDescription("AGENTS.md");
8641
9199
  console.log(` ${icon} ${chalk12.bold("AGENTS.md")}`);
8642
9200
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -8646,7 +9204,7 @@ function printSetupSummary(setup) {
8646
9204
  if (Array.isArray(codexSkills) && codexSkills.length > 0) {
8647
9205
  for (const skill of codexSkills) {
8648
9206
  const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
8649
- const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
9207
+ const icon = fs30.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8650
9208
  const desc = getDescription(skillPath);
8651
9209
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
8652
9210
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -8656,7 +9214,7 @@ function printSetupSummary(setup) {
8656
9214
  }
8657
9215
  if (cursor) {
8658
9216
  if (cursor.cursorrules) {
8659
- const icon = fs29.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
9217
+ const icon = fs30.existsSync(".cursorrules") ? chalk12.yellow("~") : chalk12.green("+");
8660
9218
  const desc = getDescription(".cursorrules");
8661
9219
  console.log(` ${icon} ${chalk12.bold(".cursorrules")}`);
8662
9220
  if (desc) console.log(chalk12.dim(` ${desc}`));
@@ -8666,7 +9224,7 @@ function printSetupSummary(setup) {
8666
9224
  if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
8667
9225
  for (const skill of cursorSkills) {
8668
9226
  const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
8669
- const icon = fs29.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
9227
+ const icon = fs30.existsSync(skillPath) ? chalk12.yellow("~") : chalk12.green("+");
8670
9228
  const desc = getDescription(skillPath);
8671
9229
  console.log(` ${icon} ${chalk12.bold(skillPath)}`);
8672
9230
  console.log(chalk12.dim(` ${desc || skill.description || skill.name}`));
@@ -8677,7 +9235,7 @@ function printSetupSummary(setup) {
8677
9235
  if (Array.isArray(rulesArr) && rulesArr.length > 0) {
8678
9236
  for (const rule of rulesArr) {
8679
9237
  const rulePath = `.cursor/rules/${rule.filename}`;
8680
- const icon = fs29.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
9238
+ const icon = fs30.existsSync(rulePath) ? chalk12.yellow("~") : chalk12.green("+");
8681
9239
  const desc = getDescription(rulePath);
8682
9240
  console.log(` ${icon} ${chalk12.bold(rulePath)}`);
8683
9241
  if (desc) {
@@ -8732,12 +9290,12 @@ function displayTokenUsage() {
8732
9290
  // src/commands/init-helpers.ts
8733
9291
  init_config();
8734
9292
  import chalk13 from "chalk";
8735
- import fs30 from "fs";
8736
- import path24 from "path";
9293
+ import fs31 from "fs";
9294
+ import path25 from "path";
8737
9295
  function isFirstRun(dir) {
8738
- const caliberDir = path24.join(dir, ".caliber");
9296
+ const caliberDir = path25.join(dir, ".caliber");
8739
9297
  try {
8740
- const stat = fs30.statSync(caliberDir);
9298
+ const stat = fs31.statSync(caliberDir);
8741
9299
  return !stat.isDirectory();
8742
9300
  } catch {
8743
9301
  return true;
@@ -8790,8 +9348,8 @@ function ensurePermissions(fingerprint) {
8790
9348
  const settingsPath = ".claude/settings.json";
8791
9349
  let settings = {};
8792
9350
  try {
8793
- if (fs30.existsSync(settingsPath)) {
8794
- settings = JSON.parse(fs30.readFileSync(settingsPath, "utf-8"));
9351
+ if (fs31.existsSync(settingsPath)) {
9352
+ settings = JSON.parse(fs31.readFileSync(settingsPath, "utf-8"));
8795
9353
  }
8796
9354
  } catch {
8797
9355
  }
@@ -8800,12 +9358,12 @@ function ensurePermissions(fingerprint) {
8800
9358
  if (Array.isArray(allow) && allow.length > 0) return;
8801
9359
  permissions.allow = derivePermissions(fingerprint);
8802
9360
  settings.permissions = permissions;
8803
- if (!fs30.existsSync(".claude")) fs30.mkdirSync(".claude", { recursive: true });
8804
- fs30.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
9361
+ if (!fs31.existsSync(".claude")) fs31.mkdirSync(".claude", { recursive: true });
9362
+ fs31.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
8805
9363
  }
8806
9364
  function writeErrorLog(config, rawOutput, error, stopReason) {
8807
9365
  try {
8808
- const logPath = path24.join(process.cwd(), ".caliber", "error-log.md");
9366
+ const logPath = path25.join(process.cwd(), ".caliber", "error-log.md");
8809
9367
  const lines = [
8810
9368
  `# Generation Error \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
8811
9369
  "",
@@ -8818,8 +9376,8 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
8818
9376
  lines.push("## Error", "```", error, "```", "");
8819
9377
  }
8820
9378
  lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
8821
- fs30.mkdirSync(path24.join(process.cwd(), ".caliber"), { recursive: true });
8822
- fs30.writeFileSync(logPath, lines.join("\n"));
9379
+ fs31.mkdirSync(path25.join(process.cwd(), ".caliber"), { recursive: true });
9380
+ fs31.writeFileSync(logPath, lines.join("\n"));
8823
9381
  console.log(chalk13.dim(`
8824
9382
  Error log written to .caliber/error-log.md`));
8825
9383
  } catch {
@@ -8870,13 +9428,13 @@ ${JSON.stringify(checkList, null, 2)}`,
8870
9428
  }
8871
9429
 
8872
9430
  // src/scoring/history.ts
8873
- import fs31 from "fs";
8874
- import path25 from "path";
9431
+ import fs32 from "fs";
9432
+ import path26 from "path";
8875
9433
  var HISTORY_FILE = "score-history.jsonl";
8876
9434
  var MAX_ENTRIES = 500;
8877
9435
  var TRIM_THRESHOLD = MAX_ENTRIES + 50;
8878
9436
  function historyFilePath() {
8879
- return path25.join(CALIBER_DIR, HISTORY_FILE);
9437
+ return path26.join(CALIBER_DIR, HISTORY_FILE);
8880
9438
  }
8881
9439
  function recordScore(result, trigger) {
8882
9440
  const entry = {
@@ -8887,14 +9445,14 @@ function recordScore(result, trigger) {
8887
9445
  trigger
8888
9446
  };
8889
9447
  try {
8890
- fs31.mkdirSync(CALIBER_DIR, { recursive: true });
9448
+ fs32.mkdirSync(CALIBER_DIR, { recursive: true });
8891
9449
  const filePath = historyFilePath();
8892
- fs31.appendFileSync(filePath, JSON.stringify(entry) + "\n");
8893
- const stat = fs31.statSync(filePath);
9450
+ fs32.appendFileSync(filePath, JSON.stringify(entry) + "\n");
9451
+ const stat = fs32.statSync(filePath);
8894
9452
  if (stat.size > TRIM_THRESHOLD * 120) {
8895
- const lines = fs31.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
9453
+ const lines = fs32.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
8896
9454
  if (lines.length > MAX_ENTRIES) {
8897
- fs31.writeFileSync(filePath, lines.slice(-MAX_ENTRIES).join("\n") + "\n");
9455
+ fs32.writeFileSync(filePath, lines.slice(-MAX_ENTRIES).join("\n") + "\n");
8898
9456
  }
8899
9457
  }
8900
9458
  } catch {
@@ -8903,7 +9461,7 @@ function recordScore(result, trigger) {
8903
9461
  function readScoreHistory() {
8904
9462
  const filePath = historyFilePath();
8905
9463
  try {
8906
- const content = fs31.readFileSync(filePath, "utf-8");
9464
+ const content = fs32.readFileSync(filePath, "utf-8");
8907
9465
  const entries = [];
8908
9466
  for (const line of content.split("\n")) {
8909
9467
  if (!line) continue;
@@ -8941,49 +9499,73 @@ async function initCommand(options) {
8941
9499
  const bin = resolveCaliber();
8942
9500
  const firstRun = isFirstRun(process.cwd());
8943
9501
  if (firstRun) {
8944
- console.log(
8945
- brand.bold(`
9502
+ console.log(brand.bold(`
8946
9503
  \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
8947
9504
  \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
8948
9505
  \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
8949
9506
  \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
8950
9507
  \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
8951
9508
  \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
8952
- `)
8953
- );
8954
- console.log(chalk14.dim(" Scan your project and generate tailored config files for"));
8955
- console.log(chalk14.dim(" Claude Code, Cursor, Codex, and GitHub Copilot.\n"));
9509
+ `));
9510
+ console.log(chalk14.dim(" Keep your AI agent configs in sync \u2014 automatically."));
9511
+ console.log(chalk14.dim(" Works across Claude Code, Cursor, Codex, and GitHub Copilot.\n"));
8956
9512
  console.log(title.bold(" How it works:\n"));
8957
- console.log(chalk14.dim(" 1. Connect Link your LLM provider and select your agents"));
8958
- console.log(chalk14.dim(" 2. Engine Detect stack, generate configs & skills in parallel"));
8959
- console.log(chalk14.dim(" 3. Review See all changes \u2014 accept, refine, or decline"));
8960
- console.log(chalk14.dim(" 4. Finalize Score check and auto-sync hooks\n"));
9513
+ console.log(chalk14.dim(" 1. Connect Auto-detect your LLM provider and agents"));
9514
+ console.log(chalk14.dim(" 2. Build Install sync, scan your project, generate configs"));
9515
+ console.log(chalk14.dim(" 3. Done Review score and start syncing\n"));
8961
9516
  } else {
8962
- console.log(brand.bold("\n CALIBER") + chalk14.dim(" \u2014 regenerating config\n"));
9517
+ console.log(brand.bold("\n CALIBER") + chalk14.dim(" \u2014 setting up continuous sync\n"));
8963
9518
  }
8964
9519
  const platforms = detectPlatforms();
8965
9520
  if (!platforms.claude && !platforms.cursor && !platforms.codex) {
8966
9521
  console.log(chalk14.yellow(" \u26A0 No supported AI platforms detected (Claude, Cursor, Codex)."));
8967
- console.log(
8968
- chalk14.yellow(
8969
- " Caliber will still generate config files, but they won't be auto-installed.\n"
8970
- )
8971
- );
9522
+ console.log(chalk14.yellow(" Caliber will still generate config files, but they won't be auto-installed.\n"));
8972
9523
  }
8973
9524
  const report = options.debugReport ? new DebugReport() : null;
8974
- console.log(title.bold(" Step 1/4 \u2014 Connect\n"));
9525
+ console.log(title.bold(" Step 1/3 \u2014 Connect\n"));
8975
9526
  let config = loadConfig();
9527
+ if (!config && !options.autoApprove) {
9528
+ if (isClaudeCliAvailable()) {
9529
+ console.log(chalk14.dim(" Detected: Claude Code CLI (uses your Pro/Max/Team subscription)\n"));
9530
+ const useIt = await confirm2({ message: "Use Claude Code as your LLM provider?" });
9531
+ if (useIt) {
9532
+ const autoConfig = { provider: "claude-cli", model: "default" };
9533
+ writeConfigFile(autoConfig);
9534
+ config = autoConfig;
9535
+ }
9536
+ } else if (isCursorAgentAvailable() && isCursorLoggedIn()) {
9537
+ console.log(chalk14.dim(" Detected: Cursor (uses your existing subscription)\n"));
9538
+ const useIt = await confirm2({ message: "Use Cursor as your LLM provider?" });
9539
+ if (useIt) {
9540
+ const autoConfig = { provider: "cursor", model: "sonnet-4.6" };
9541
+ writeConfigFile(autoConfig);
9542
+ config = autoConfig;
9543
+ }
9544
+ }
9545
+ }
8976
9546
  if (!config) {
8977
- console.log(chalk14.dim(" No LLM provider configured yet.\n"));
8978
- await runInteractiveProviderSetup({
8979
- selectMessage: "How do you want to use Caliber? (choose LLM provider)"
8980
- });
8981
- config = loadConfig();
9547
+ if (options.autoApprove) {
9548
+ if (isClaudeCliAvailable()) {
9549
+ const autoConfig = { provider: "claude-cli", model: "default" };
9550
+ writeConfigFile(autoConfig);
9551
+ config = autoConfig;
9552
+ } else if (isCursorAgentAvailable() && isCursorLoggedIn()) {
9553
+ const autoConfig = { provider: "cursor", model: "sonnet-4.6" };
9554
+ writeConfigFile(autoConfig);
9555
+ config = autoConfig;
9556
+ }
9557
+ }
8982
9558
  if (!config) {
8983
- console.log(chalk14.red(" Configuration cancelled or failed.\n"));
8984
- throw new Error("__exit__");
9559
+ console.log(chalk14.dim(" No LLM provider detected.\n"));
9560
+ await runInteractiveProviderSetup({
9561
+ selectMessage: "How do you want to use Caliber? (choose LLM provider)"
9562
+ });
9563
+ config = loadConfig();
9564
+ if (!config) {
9565
+ console.log(chalk14.red(" Configuration cancelled or failed.\n"));
9566
+ throw new Error("__exit__");
9567
+ }
8985
9568
  }
8986
- console.log(chalk14.green(" \u2713 Provider saved\n"));
8987
9569
  }
8988
9570
  trackInitProviderSelected(config.provider, config.model, firstRun);
8989
9571
  const displayModel = getDisplayModel(config);
@@ -8992,51 +9574,67 @@ async function initCommand(options) {
8992
9574
  console.log(chalk14.dim(modelLine + "\n"));
8993
9575
  if (report) {
8994
9576
  report.markStep("Provider connection");
8995
- report.addSection(
8996
- "LLM Provider",
8997
- `- **Provider**: ${config.provider}
9577
+ report.addSection("LLM Provider", `- **Provider**: ${config.provider}
8998
9578
  - **Model**: ${displayModel}
8999
- - **Fast model**: ${fastModel || "none"}`
9000
- );
9579
+ - **Fast model**: ${fastModel || "none"}`);
9001
9580
  }
9002
9581
  await validateModel({ fast: true });
9003
9582
  let targetAgent;
9004
9583
  const agentAutoDetected = !options.agent;
9005
9584
  if (options.agent) {
9006
9585
  targetAgent = options.agent;
9007
- } else if (options.autoApprove) {
9008
- targetAgent = ["claude"];
9009
- log(options.verbose, "Auto-approve: defaulting to claude agent");
9010
9586
  } else {
9011
9587
  const detected = detectAgents(process.cwd());
9012
- targetAgent = await promptAgent(detected.length > 0 ? detected : void 0);
9588
+ if (detected.length > 0 && (options.autoApprove || firstRun)) {
9589
+ targetAgent = detected;
9590
+ console.log(chalk14.dim(` Detected agents: ${detected.join(", ")}
9591
+ `));
9592
+ } else if (detected.length > 0) {
9593
+ console.log(chalk14.dim(` Detected agents: ${detected.join(", ")}
9594
+ `));
9595
+ const useDetected = await confirm2({ message: "Use detected agents?" });
9596
+ targetAgent = useDetected ? detected : await promptAgent();
9597
+ } else {
9598
+ targetAgent = options.autoApprove ? ["claude"] : await promptAgent();
9599
+ }
9013
9600
  }
9014
9601
  console.log(chalk14.dim(` Target: ${targetAgent.join(", ")}
9015
9602
  `));
9016
9603
  trackInitAgentSelected(targetAgent, agentAutoDetected);
9017
- const baselineScore = computeLocalScore(process.cwd(), targetAgent);
9018
- console.log(chalk14.dim("\n Current config score:"));
9019
- displayScoreSummary(baselineScore);
9020
- if (options.verbose) {
9021
- for (const c of baselineScore.checks) {
9022
- log(
9023
- options.verbose,
9024
- ` ${c.passed ? "\u2713" : "\u2717"} ${c.name}: ${c.earnedPoints}/${c.maxPoints}${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`
9025
- );
9026
- }
9604
+ console.log(title.bold(" Step 2/3 \u2014 Build\n"));
9605
+ const hookResult = installPreCommitHook();
9606
+ if (hookResult.installed) {
9607
+ console.log(` ${chalk14.green("\u2713")} Pre-commit hook installed`);
9608
+ } else if (hookResult.alreadyInstalled) {
9609
+ console.log(` ${chalk14.green("\u2713")} Pre-commit hook \u2014 active`);
9610
+ }
9611
+ const { ensureBuiltinSkills: ensureBuiltinSkills2 } = await Promise.resolve().then(() => (init_builtin_skills(), builtin_skills_exports));
9612
+ for (const agent of targetAgent) {
9613
+ if (agent === "claude" && !fs33.existsSync(".claude")) fs33.mkdirSync(".claude", { recursive: true });
9614
+ if (agent === "cursor" && !fs33.existsSync(".cursor")) fs33.mkdirSync(".cursor", { recursive: true });
9615
+ if (agent === "codex" && !fs33.existsSync(".agents")) fs33.mkdirSync(".agents", { recursive: true });
9616
+ }
9617
+ const skillsWritten = ensureBuiltinSkills2();
9618
+ if (skillsWritten.length > 0) {
9619
+ console.log(` ${chalk14.green("\u2713")} Agent skills installed`);
9620
+ }
9621
+ const hasLearnableAgent = targetAgent.includes("claude") || targetAgent.includes("cursor");
9622
+ if (hasLearnableAgent) {
9623
+ if (targetAgent.includes("claude")) installLearningHooks();
9624
+ if (targetAgent.includes("cursor")) installCursorLearningHooks();
9625
+ console.log(` ${chalk14.green("\u2713")} Session learning enabled`);
9626
+ trackInitLearnEnabled(true);
9027
9627
  }
9628
+ console.log("");
9629
+ const baselineScore = computeLocalScore(process.cwd(), targetAgent);
9630
+ log(options.verbose, `Baseline score: ${baselineScore.score}/100`);
9028
9631
  if (report) {
9029
9632
  report.markStep("Baseline scoring");
9030
- report.addSection(
9031
- "Scoring: Baseline",
9032
- `**Score**: ${baselineScore.score}/100
9633
+ report.addSection("Scoring: Baseline", `**Score**: ${baselineScore.score}/100
9033
9634
 
9034
9635
  | Check | Passed | Points | Max |
9035
9636
  |-------|--------|--------|-----|
9036
- ` + baselineScore.checks.map(
9037
- (c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`
9038
- ).join("\n")
9039
- );
9637
+ ` + baselineScore.checks.map((c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`).join("\n"));
9040
9638
  report.addSection("Generation: Target Agents", targetAgent.join(", "));
9041
9639
  }
9042
9640
  const hasExistingConfig = !!(baselineScore.checks.some((c) => c.id === "claude_md_exists" && c.passed) || baselineScore.checks.some((c) => c.id === "cursorrules_exists" && c.passed));
@@ -9050,33 +9648,77 @@ async function initCommand(options) {
9050
9648
  ]);
9051
9649
  const passingCount = baselineScore.checks.filter((c) => c.passed).length;
9052
9650
  const failingCount = baselineScore.checks.filter((c) => !c.passed).length;
9053
- if (hasExistingConfig && baselineScore.score === 100) {
9054
- trackInitScoreComputed(baselineScore.score, passingCount, failingCount, true);
9055
- console.log(chalk14.bold.green(" Your config is already optimal \u2014 nothing to change.\n"));
9056
- console.log(
9057
- chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to regenerate anyway.\n")
9058
- );
9059
- if (!options.force) return;
9060
- }
9061
- const allFailingChecks = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
9062
- const llmFixableChecks = allFailingChecks.filter((c) => !NON_LLM_CHECKS.has(c.id));
9063
9651
  trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
9064
- if (hasExistingConfig && llmFixableChecks.length === 0 && allFailingChecks.length > 0 && !options.force) {
9065
- console.log(chalk14.bold.green("\n Your config is fully optimized for LLM generation.\n"));
9066
- console.log(chalk14.dim(" Remaining items need CLI actions:\n"));
9067
- for (const check of allFailingChecks) {
9068
- console.log(chalk14.dim(` \u2022 ${check.name}`));
9069
- if (check.suggestion) {
9070
- console.log(` ${chalk14.hex("#83D1EB")(check.suggestion)}`);
9652
+ let skipGeneration = false;
9653
+ if (hasExistingConfig && baselineScore.score === 100 && !options.force) {
9654
+ skipGeneration = true;
9655
+ } else if (hasExistingConfig && !options.force && !options.autoApprove) {
9656
+ console.log(chalk14.dim(` Config score: ${baselineScore.score}/100 \u2014 Caliber can improve this.
9657
+ `));
9658
+ const improveAnswer = await confirm2({ message: "Improve your existing configs?" });
9659
+ skipGeneration = !improveAnswer;
9660
+ }
9661
+ if (skipGeneration) {
9662
+ const {
9663
+ appendManagedBlocks: appendManagedBlocks2,
9664
+ getCursorPreCommitRule: getCursorPreCommitRule2,
9665
+ getCursorLearningsRule: getCursorLearningsRule2,
9666
+ getCursorSyncRule: getCursorSyncRule2,
9667
+ getCursorSetupRule: getCursorSetupRule2
9668
+ } = await Promise.resolve().then(() => (init_pre_commit_block(), pre_commit_block_exports));
9669
+ const claudeMdPath = "CLAUDE.md";
9670
+ let claudeContent = "";
9671
+ try {
9672
+ claudeContent = fs33.readFileSync(claudeMdPath, "utf-8");
9673
+ } catch {
9674
+ }
9675
+ if (!claudeContent) {
9676
+ claudeContent = `# ${path27.basename(process.cwd())}
9677
+ `;
9678
+ }
9679
+ const updatedClaude = appendManagedBlocks2(claudeContent, "claude");
9680
+ if (updatedClaude !== claudeContent || !fs33.existsSync(claudeMdPath)) {
9681
+ fs33.writeFileSync(claudeMdPath, updatedClaude);
9682
+ console.log(` ${chalk14.green("\u2713")} CLAUDE.md \u2014 added Caliber sync instructions`);
9683
+ }
9684
+ if (targetAgent.includes("cursor")) {
9685
+ const rulesDir = path27.join(".cursor", "rules");
9686
+ if (!fs33.existsSync(rulesDir)) fs33.mkdirSync(rulesDir, { recursive: true });
9687
+ for (const rule of [getCursorPreCommitRule2(), getCursorLearningsRule2(), getCursorSyncRule2(), getCursorSetupRule2()]) {
9688
+ fs33.writeFileSync(path27.join(rulesDir, rule.filename), rule.content);
9071
9689
  }
9690
+ console.log(` ${chalk14.green("\u2713")} Cursor rules \u2014 added Caliber sync rules`);
9072
9691
  }
9073
- console.log("");
9074
- console.log(
9075
- chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to regenerate anyway.\n")
9076
- );
9692
+ if (targetAgent.includes("github-copilot")) {
9693
+ const copilotPath = path27.join(".github", "copilot-instructions.md");
9694
+ let copilotContent = "";
9695
+ try {
9696
+ copilotContent = fs33.readFileSync(copilotPath, "utf-8");
9697
+ } catch {
9698
+ }
9699
+ if (!copilotContent) {
9700
+ fs33.mkdirSync(".github", { recursive: true });
9701
+ copilotContent = `# ${path27.basename(process.cwd())}
9702
+ `;
9703
+ }
9704
+ const updatedCopilot = appendManagedBlocks2(copilotContent, "copilot");
9705
+ if (updatedCopilot !== copilotContent) {
9706
+ fs33.writeFileSync(copilotPath, updatedCopilot);
9707
+ console.log(` ${chalk14.green("\u2713")} Copilot instructions \u2014 added Caliber sync instructions`);
9708
+ }
9709
+ }
9710
+ const sha2 = getCurrentHeadSha();
9711
+ writeState({
9712
+ lastRefreshSha: sha2 ?? "",
9713
+ lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
9714
+ targetAgent
9715
+ });
9716
+ trackInitCompleted("sync-only", baselineScore.score);
9717
+ console.log(chalk14.bold.green("\n Caliber sync is set up!\n"));
9718
+ console.log(chalk14.dim(" Your agent configs will sync automatically on every commit."));
9719
+ console.log(chalk14.dim(" Run ") + title(`${bin} init --force`) + chalk14.dim(" anytime to generate or improve configs.\n"));
9077
9720
  return;
9078
9721
  }
9079
- console.log(title.bold(" Step 2/4 \u2014 Engine\n"));
9080
9722
  const genModelInfo = fastModel ? ` Using ${displayModel} for docs, ${fastModel} for skills` : ` Using ${displayModel}`;
9081
9723
  console.log(chalk14.dim(genModelInfo + "\n"));
9082
9724
  if (report) report.markStep("Generation");
@@ -9092,14 +9734,8 @@ async function initCommand(options) {
9092
9734
  const TASK_STACK = display.add("Detecting project stack", { pipelineLabel: "Scan" });
9093
9735
  const TASK_CONFIG = display.add("Generating configs", { depth: 1, pipelineLabel: "Generate" });
9094
9736
  const TASK_SKILLS_GEN = display.add("Generating skills", { depth: 2, pipelineLabel: "Skills" });
9095
- const TASK_SKILLS_SEARCH = display.add("Searching community skills", {
9096
- depth: 1,
9097
- pipelineLabel: "Search",
9098
- pipelineRow: 1
9099
- });
9100
- const TASK_SCORE_REFINE = display.add("Validating & refining config", {
9101
- pipelineLabel: "Validate"
9102
- });
9737
+ const TASK_SKILLS_SEARCH = display.add("Searching community skills", { depth: 1, pipelineLabel: "Search", pipelineRow: 1 });
9738
+ const TASK_SCORE_REFINE = display.add("Validating & refining config", { pipelineLabel: "Validate" });
9103
9739
  display.start();
9104
9740
  display.enableWaitingContent();
9105
9741
  try {
@@ -9109,39 +9745,21 @@ async function initCommand(options) {
9109
9745
  const stackSummary = stackParts.join(", ") || "no languages";
9110
9746
  const largeRepoNote = fingerprint.fileTree.length > 5e3 ? ` (${fingerprint.fileTree.length.toLocaleString()} files, smart sampling active)` : "";
9111
9747
  display.update(TASK_STACK, "done", stackSummary + largeRepoNote);
9112
- trackInitProjectDiscovered(
9113
- fingerprint.languages.length,
9114
- fingerprint.frameworks.length,
9115
- fingerprint.fileTree.length
9116
- );
9117
- log(
9118
- options.verbose,
9119
- `Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`
9120
- );
9748
+ trackInitProjectDiscovered(fingerprint.languages.length, fingerprint.frameworks.length, fingerprint.fileTree.length);
9749
+ log(options.verbose, `Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`);
9121
9750
  const cliSources = options.source || [];
9122
9751
  const workspaces = getDetectedWorkspaces(process.cwd());
9123
9752
  const sources2 = resolveAllSources(process.cwd(), cliSources, workspaces);
9124
9753
  if (sources2.length > 0) {
9125
9754
  fingerprint.sources = sources2;
9126
- log(
9127
- options.verbose,
9128
- `Sources: ${sources2.length} resolved (${sources2.map((s) => s.name).join(", ")})`
9129
- );
9755
+ log(options.verbose, `Sources: ${sources2.length} resolved (${sources2.map((s) => s.name).join(", ")})`);
9130
9756
  }
9131
9757
  if (report) {
9132
- report.addJson("Fingerprint: Git", {
9133
- remote: fingerprint.gitRemoteUrl,
9134
- packageName: fingerprint.packageName
9135
- });
9758
+ report.addJson("Fingerprint: Git", { remote: fingerprint.gitRemoteUrl, packageName: fingerprint.packageName });
9136
9759
  report.addCodeBlock("Fingerprint: File Tree", fingerprint.fileTree.join("\n"));
9137
- report.addJson("Fingerprint: Detected Stack", {
9138
- languages: fingerprint.languages,
9139
- frameworks: fingerprint.frameworks,
9140
- tools: fingerprint.tools
9141
- });
9760
+ report.addJson("Fingerprint: Detected Stack", { languages: fingerprint.languages, frameworks: fingerprint.frameworks, tools: fingerprint.tools });
9142
9761
  report.addJson("Fingerprint: Existing Configs", fingerprint.existingConfigs);
9143
- if (fingerprint.codeAnalysis)
9144
- report.addJson("Fingerprint: Code Analysis", fingerprint.codeAnalysis);
9762
+ if (fingerprint.codeAnalysis) report.addJson("Fingerprint: Code Analysis", fingerprint.codeAnalysis);
9145
9763
  }
9146
9764
  const isEmpty = fingerprint.fileTree.length < 3;
9147
9765
  if (isEmpty) {
@@ -9172,26 +9790,13 @@ async function initCommand(options) {
9172
9790
  let passingChecks;
9173
9791
  let currentScore;
9174
9792
  if (hasExistingConfig && localBaseline.score >= 95 && !options.force) {
9175
- const currentLlmFixable = localBaseline.checks.filter(
9176
- (c) => !c.passed && c.maxPoints > 0 && !NON_LLM_CHECKS.has(c.id)
9177
- );
9178
- failingChecks = currentLlmFixable.map((c) => ({
9179
- name: c.name,
9180
- suggestion: c.suggestion,
9181
- fix: c.fix
9182
- }));
9793
+ const currentLlmFixable = localBaseline.checks.filter((c) => !c.passed && c.maxPoints > 0 && !NON_LLM_CHECKS.has(c.id));
9794
+ failingChecks = currentLlmFixable.map((c) => ({ name: c.name, suggestion: c.suggestion, fix: c.fix }));
9183
9795
  passingChecks = localBaseline.checks.filter((c) => c.passed).map((c) => ({ name: c.name }));
9184
9796
  currentScore = localBaseline.score;
9185
9797
  }
9186
9798
  if (report) {
9187
- const fullPrompt = buildGeneratePrompt(
9188
- fingerprint,
9189
- targetAgent,
9190
- fingerprint.description,
9191
- failingChecks,
9192
- currentScore,
9193
- passingChecks
9194
- );
9799
+ const fullPrompt = buildGeneratePrompt(fingerprint, targetAgent, fingerprint.description, failingChecks, currentScore, passingChecks);
9195
9800
  report.addCodeBlock("Generation: Full LLM Prompt", fullPrompt);
9196
9801
  }
9197
9802
  const result = await generateSetup(
@@ -9311,11 +9916,8 @@ async function initCommand(options) {
9311
9916
  if (rawOutput) report.addCodeBlock("Generation: Raw LLM Response", rawOutput);
9312
9917
  report.addJson("Generation: Parsed Config", generatedSetup);
9313
9918
  }
9314
- log(
9315
- options.verbose,
9316
- `Generation completed: ${elapsedMs}ms, stopReason: ${genStopReason || "end_turn"}`
9317
- );
9318
- console.log(title.bold(" Step 3/4 \u2014 Review\n"));
9919
+ log(options.verbose, `Generation completed: ${elapsedMs}ms, stopReason: ${genStopReason || "end_turn"}`);
9920
+ console.log(title.bold(" Step 3/3 \u2014 Done\n"));
9319
9921
  const setupFiles = collectSetupFiles(generatedSetup, targetAgent);
9320
9922
  const staged = stageFiles(setupFiles, process.cwd());
9321
9923
  const totalChanges = staged.newFiles + staged.modifiedFiles;
@@ -9324,20 +9926,12 @@ async function initCommand(options) {
9324
9926
  for (const line of changes) {
9325
9927
  console.log(` ${chalk14.dim("\u2022")} ${line}`);
9326
9928
  }
9327
- console.log("");
9328
- }
9329
- console.log(
9330
- chalk14.dim(
9331
- ` ${chalk14.green(`${staged.newFiles} new`)} / ${chalk14.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}`
9332
- )
9333
- );
9929
+ console.log("");
9930
+ }
9931
+ console.log(chalk14.dim(` ${chalk14.green(`${staged.newFiles} new`)} / ${chalk14.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}`));
9334
9932
  if (skillSearchResult.results.length > 0) {
9335
- console.log(
9336
- chalk14.dim(
9337
- ` ${chalk14.cyan(`${skillSearchResult.results.length}`)} community skills available to install
9338
- `
9339
- )
9340
- );
9933
+ console.log(chalk14.dim(` ${chalk14.cyan(`${skillSearchResult.results.length}`)} community skills available to install
9934
+ `));
9341
9935
  } else {
9342
9936
  console.log("");
9343
9937
  }
@@ -9373,12 +9967,8 @@ async function initCommand(options) {
9373
9967
  }
9374
9968
  const updatedFiles = collectSetupFiles(generatedSetup, targetAgent);
9375
9969
  const restaged = stageFiles(updatedFiles, process.cwd());
9376
- console.log(
9377
- chalk14.dim(
9378
- ` ${chalk14.green(`${restaged.newFiles} new`)} / ${chalk14.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
9379
- `
9380
- )
9381
- );
9970
+ console.log(chalk14.dim(` ${chalk14.green(`${restaged.newFiles} new`)} / ${chalk14.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
9971
+ `));
9382
9972
  printSetupSummary(generatedSetup);
9383
9973
  const { openReview: openRev } = await Promise.resolve().then(() => (init_review(), review_exports));
9384
9974
  await openRev("terminal", restaged.stagedFiles);
@@ -9390,7 +9980,6 @@ async function initCommand(options) {
9390
9980
  console.log(chalk14.dim("Declined. No files were modified."));
9391
9981
  return;
9392
9982
  }
9393
- console.log(title.bold("\n Step 4/4 \u2014 Finalize\n"));
9394
9983
  if (options.dryRun) {
9395
9984
  console.log(chalk14.yellow("\n[Dry run] Would write the following files:"));
9396
9985
  console.log(JSON.stringify(generatedSetup, null, 2));
@@ -9399,14 +9988,13 @@ async function initCommand(options) {
9399
9988
  const { default: ora9 } = await import("ora");
9400
9989
  const writeSpinner = ora9("Writing config files...").start();
9401
9990
  try {
9402
- if (targetAgent.includes("codex") && !fs32.existsSync("AGENTS.md") && !generatedSetup.codex) {
9991
+ if (targetAgent.includes("codex") && !fs33.existsSync("AGENTS.md") && !generatedSetup.codex) {
9403
9992
  const claude = generatedSetup.claude;
9404
9993
  const cursor = generatedSetup.cursor;
9405
9994
  const agentRefs = [];
9406
9995
  if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
9407
9996
  if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
9408
- if (agentRefs.length === 0)
9409
- agentRefs.push("See CLAUDE.md and .cursor/rules/ for agent configurations.");
9997
+ if (agentRefs.length === 0) agentRefs.push("See CLAUDE.md and .cursor/rules/ for agent configurations.");
9410
9998
  const stubContent = `# AGENTS.md
9411
9999
 
9412
10000
  This project uses AI coding agents configured by [Caliber](https://github.com/caliber-ai-org/ai-setup).
@@ -9453,49 +10041,32 @@ ${agentRefs.join(" ")}
9453
10041
  if (afterScore.score < baselineScore.score) {
9454
10042
  trackInitScoreRegression(baselineScore.score, afterScore.score);
9455
10043
  console.log("");
9456
- console.log(
9457
- chalk14.yellow(
9458
- ` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`
9459
- )
9460
- );
10044
+ console.log(chalk14.yellow(` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`));
9461
10045
  try {
9462
10046
  const { restored, removed } = undoSetup();
9463
10047
  if (restored.length > 0 || removed.length > 0) {
9464
- console.log(
9465
- chalk14.dim(
9466
- ` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`
9467
- )
9468
- );
10048
+ console.log(chalk14.dim(` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`));
9469
10049
  }
9470
10050
  } catch {
9471
10051
  }
9472
- console.log(
9473
- chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to override.\n")
9474
- );
10052
+ console.log(chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} init --force`) + chalk14.dim(" to override.\n"));
9475
10053
  return;
9476
10054
  }
9477
10055
  if (report) {
9478
10056
  report.markStep("Post-write scoring");
9479
- report.addSection(
9480
- "Scoring: Post-Write",
9481
- `**Score**: ${afterScore.score}/100 (delta: ${afterScore.score - baselineScore.score >= 0 ? "+" : ""}${afterScore.score - baselineScore.score})
10057
+ report.addSection("Scoring: Post-Write", `**Score**: ${afterScore.score}/100 (delta: ${afterScore.score - baselineScore.score >= 0 ? "+" : ""}${afterScore.score - baselineScore.score})
9482
10058
 
9483
10059
  | Check | Passed | Points | Max |
9484
10060
  |-------|--------|--------|-----|
9485
- ` + afterScore.checks.map(
9486
- (c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`
9487
- ).join("\n")
9488
- );
10061
+ ` + afterScore.checks.map((c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`).join("\n"));
9489
10062
  }
9490
10063
  recordScore(afterScore, "init");
10064
+ trackInitCompleted("full-generation", afterScore.score);
9491
10065
  displayScoreDelta(baselineScore, afterScore);
9492
10066
  if (options.verbose) {
9493
10067
  log(options.verbose, `Final score: ${afterScore.score}/100`);
9494
10068
  for (const c of afterScore.checks.filter((ch) => !ch.passed)) {
9495
- log(
9496
- options.verbose,
9497
- ` Still failing: ${c.name} (${c.earnedPoints}/${c.maxPoints})${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`
9498
- );
10069
+ log(options.verbose, ` Still failing: ${c.name} (${c.earnedPoints}/${c.maxPoints})${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`);
9499
10070
  }
9500
10071
  }
9501
10072
  let communitySkillsInstalled = 0;
@@ -9508,99 +10079,36 @@ ${agentRefs.join(" ")}
9508
10079
  communitySkillsInstalled = selected.length;
9509
10080
  }
9510
10081
  }
9511
- console.log("");
9512
- console.log(
9513
- ` ${chalk14.green("\u2713")} Docs auto-refresh ${chalk14.dim(`agents run ${resolveCaliber()} refresh before commits`)}`
9514
- );
9515
10082
  trackInitHookSelected("config-instructions");
9516
- const hasLearnableAgent = targetAgent.includes("claude") || targetAgent.includes("cursor");
9517
- let enableLearn = false;
9518
- if (hasLearnableAgent) {
9519
- if (!options.autoApprove) {
9520
- enableLearn = await promptLearnInstall(targetAgent);
9521
- trackInitLearnEnabled(enableLearn);
9522
- if (enableLearn) {
9523
- if (targetAgent.includes("claude")) {
9524
- const r = installLearningHooks();
9525
- if (r.installed)
9526
- console.log(` ${chalk14.green("\u2713")} Learning hooks installed for Claude Code`);
9527
- else if (r.alreadyInstalled)
9528
- console.log(chalk14.dim(" Claude Code learning hooks already installed"));
9529
- }
9530
- if (targetAgent.includes("cursor")) {
9531
- const r = installCursorLearningHooks();
9532
- if (r.installed) console.log(` ${chalk14.green("\u2713")} Learning hooks installed for Cursor`);
9533
- else if (r.alreadyInstalled)
9534
- console.log(chalk14.dim(" Cursor learning hooks already installed"));
9535
- }
9536
- console.log(
9537
- chalk14.dim(" Run ") + chalk14.hex("#83D1EB")(`${bin} learn status`) + chalk14.dim(" to see insights")
9538
- );
9539
- } else {
9540
- console.log(
9541
- chalk14.dim(" Skipped. Run ") + chalk14.hex("#83D1EB")(`${bin} learn install`) + chalk14.dim(" later to enable.")
9542
- );
9543
- }
9544
- } else {
9545
- enableLearn = true;
9546
- if (targetAgent.includes("claude")) installLearningHooks();
9547
- if (targetAgent.includes("cursor")) installCursorLearningHooks();
9548
- }
9549
- }
9550
- console.log(chalk14.bold.green("\n Configuration complete!"));
9551
- console.log(
9552
- chalk14.dim(" Your AI agents now understand your project's architecture, build commands,")
9553
- );
9554
- console.log(
9555
- chalk14.dim(" testing patterns, and conventions. All changes are backed up automatically.\n")
9556
- );
9557
10083
  const done = chalk14.green("\u2713");
9558
- const skip = chalk14.dim("\u2013");
9559
- console.log(chalk14.bold(" What was configured:\n"));
9560
- console.log(
9561
- ` ${done} Config generated ${title(`${bin} score`)} ${chalk14.dim("for full breakdown")}`
9562
- );
9563
- console.log(
9564
- ` ${done} Docs auto-refresh ${chalk14.dim(`agents run ${bin} refresh before commits`)}`
9565
- );
10084
+ console.log(chalk14.bold.green("\n Caliber is set up!\n"));
10085
+ console.log(chalk14.bold(" What's configured:\n"));
10086
+ console.log(` ${done} Continuous sync ${chalk14.dim("pre-commit hook keeps all agent configs in sync")}`);
10087
+ console.log(` ${done} Config generated ${chalk14.dim(`score: ${afterScore.score}/100`)}`);
10088
+ console.log(` ${done} Agent skills ${chalk14.dim("/setup-caliber for new team members")}`);
9566
10089
  if (hasLearnableAgent) {
9567
- if (enableLearn) {
9568
- console.log(
9569
- ` ${done} Session learning ${chalk14.dim("agent learns from your feedback")}`
9570
- );
9571
- } else {
9572
- console.log(
9573
- ` ${skip} Session learning ${title(`${bin} learn install`)} to enable later`
9574
- );
9575
- }
10090
+ console.log(` ${done} Session learning ${chalk14.dim("learns from your corrections")}`);
9576
10091
  }
9577
10092
  if (communitySkillsInstalled > 0) {
9578
- console.log(
9579
- ` ${done} Community skills ${chalk14.dim(`${communitySkillsInstalled} skill${communitySkillsInstalled > 1 ? "s" : ""} installed for your stack`)}`
9580
- );
9581
- } else if (skillSearchResult.results.length > 0) {
9582
- console.log(` ${skip} Community skills ${chalk14.dim("available but skipped")}`);
9583
- }
9584
- console.log(chalk14.bold("\n Explore next:\n"));
9585
- console.log(
9586
- ` ${title(`${bin} skills`)} Find more community skills as your codebase evolves`
9587
- );
9588
- console.log(
9589
- ` ${title(`${bin} score`)} See the full scoring breakdown with improvement tips`
9590
- );
9591
- console.log(` ${title(`${bin} undo`)} Revert all changes from this run`);
10093
+ console.log(` ${done} Community skills ${chalk14.dim(`${communitySkillsInstalled} installed for your stack`)}`);
10094
+ }
10095
+ console.log(chalk14.bold("\n What happens next:\n"));
10096
+ console.log(chalk14.dim(" Every commit syncs your agent configs automatically."));
10097
+ console.log(chalk14.dim(" New team members run /setup-caliber to get set up instantly.\n"));
10098
+ console.log(` ${title(`${bin} score`)} Full scoring breakdown`);
10099
+ console.log(` ${title(`${bin} skills`)} Find community skills`);
10100
+ console.log(` ${title(`${bin} undo`)} Revert changes`);
10101
+ console.log(` ${title(`${bin} uninstall`)} Remove Caliber completely`);
9592
10102
  console.log("");
9593
10103
  if (options.showTokens) {
9594
10104
  displayTokenUsage();
9595
10105
  }
9596
10106
  if (report) {
9597
10107
  report.markStep("Finished");
9598
- const reportPath = path26.join(process.cwd(), ".caliber", "debug-report.md");
10108
+ const reportPath = path27.join(process.cwd(), ".caliber", "debug-report.md");
9599
10109
  report.write(reportPath);
9600
- console.log(
9601
- chalk14.dim(` Debug report written to ${path26.relative(process.cwd(), reportPath)}
9602
- `)
9603
- );
10110
+ console.log(chalk14.dim(` Debug report written to ${path27.relative(process.cwd(), reportPath)}
10111
+ `));
9604
10112
  }
9605
10113
  }
9606
10114
 
@@ -9638,7 +10146,7 @@ function undoCommand() {
9638
10146
 
9639
10147
  // src/commands/status.ts
9640
10148
  import chalk16 from "chalk";
9641
- import fs33 from "fs";
10149
+ import fs34 from "fs";
9642
10150
  init_config();
9643
10151
  init_resolve_caliber();
9644
10152
  async function statusCommand(options) {
@@ -9667,7 +10175,7 @@ async function statusCommand(options) {
9667
10175
  }
9668
10176
  console.log(` Files managed: ${chalk16.cyan(manifest.entries.length.toString())}`);
9669
10177
  for (const entry of manifest.entries) {
9670
- const exists = fs33.existsSync(entry.path);
10178
+ const exists = fs34.existsSync(entry.path);
9671
10179
  const icon = exists ? chalk16.green("\u2713") : chalk16.red("\u2717");
9672
10180
  console.log(` ${icon} ${entry.path} (${entry.action})`);
9673
10181
  }
@@ -9824,9 +10332,9 @@ async function regenerateCommand(options) {
9824
10332
  }
9825
10333
 
9826
10334
  // src/commands/score.ts
9827
- import fs34 from "fs";
10335
+ import fs35 from "fs";
9828
10336
  import os7 from "os";
9829
- import path27 from "path";
10337
+ import path28 from "path";
9830
10338
  import { execFileSync as execFileSync2 } from "child_process";
9831
10339
  import chalk18 from "chalk";
9832
10340
  init_resolve_caliber();
@@ -9834,7 +10342,7 @@ var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".cursorrules", "CALIBER_LEARNINGS
9834
10342
  var CONFIG_DIRS = [".claude", ".cursor"];
9835
10343
  function scoreBaseRef(ref, target) {
9836
10344
  if (!/^[\w.\-/~^@{}]+$/.test(ref)) return null;
9837
- const tmpDir = fs34.mkdtempSync(path27.join(os7.tmpdir(), "caliber-compare-"));
10345
+ const tmpDir = fs35.mkdtempSync(path28.join(os7.tmpdir(), "caliber-compare-"));
9838
10346
  try {
9839
10347
  for (const file of CONFIG_FILES) {
9840
10348
  try {
@@ -9842,7 +10350,7 @@ function scoreBaseRef(ref, target) {
9842
10350
  encoding: "utf-8",
9843
10351
  stdio: ["pipe", "pipe", "pipe"]
9844
10352
  });
9845
- fs34.writeFileSync(path27.join(tmpDir, file), content);
10353
+ fs35.writeFileSync(path28.join(tmpDir, file), content);
9846
10354
  } catch {
9847
10355
  }
9848
10356
  }
@@ -9853,13 +10361,13 @@ function scoreBaseRef(ref, target) {
9853
10361
  stdio: ["pipe", "pipe", "pipe"]
9854
10362
  }).trim().split("\n").filter(Boolean);
9855
10363
  for (const file of files) {
9856
- const filePath = path27.join(tmpDir, file);
9857
- fs34.mkdirSync(path27.dirname(filePath), { recursive: true });
10364
+ const filePath = path28.join(tmpDir, file);
10365
+ fs35.mkdirSync(path28.dirname(filePath), { recursive: true });
9858
10366
  const content = execFileSync2("git", ["show", `${ref}:${file}`], {
9859
10367
  encoding: "utf-8",
9860
10368
  stdio: ["pipe", "pipe", "pipe"]
9861
10369
  });
9862
- fs34.writeFileSync(filePath, content);
10370
+ fs35.writeFileSync(filePath, content);
9863
10371
  }
9864
10372
  } catch {
9865
10373
  }
@@ -9869,7 +10377,7 @@ function scoreBaseRef(ref, target) {
9869
10377
  } catch {
9870
10378
  return null;
9871
10379
  } finally {
9872
- fs34.rmSync(tmpDir, { recursive: true, force: true });
10380
+ fs35.rmSync(tmpDir, { recursive: true, force: true });
9873
10381
  }
9874
10382
  }
9875
10383
  async function scoreCommand(options) {
@@ -9953,13 +10461,13 @@ async function scoreCommand(options) {
9953
10461
  }
9954
10462
 
9955
10463
  // src/commands/refresh.ts
9956
- import fs38 from "fs";
9957
- import path31 from "path";
10464
+ import fs39 from "fs";
10465
+ import path32 from "path";
9958
10466
  import chalk19 from "chalk";
9959
10467
  import ora6 from "ora";
9960
10468
 
9961
10469
  // src/lib/git-diff.ts
9962
- import { execSync as execSync14 } from "child_process";
10470
+ import { execSync as execSync15 } from "child_process";
9963
10471
  var MAX_DIFF_BYTES = 1e5;
9964
10472
  var DOC_PATTERNS = [
9965
10473
  "CLAUDE.md",
@@ -9974,7 +10482,7 @@ function excludeArgs() {
9974
10482
  }
9975
10483
  function safeExec(cmd) {
9976
10484
  try {
9977
- return execSync14(cmd, {
10485
+ return execSync15(cmd, {
9978
10486
  encoding: "utf-8",
9979
10487
  stdio: ["pipe", "pipe", "pipe"],
9980
10488
  maxBuffer: 10 * 1024 * 1024
@@ -10032,48 +10540,49 @@ function collectDiff(lastSha) {
10032
10540
  }
10033
10541
 
10034
10542
  // src/writers/refresh.ts
10035
- import fs35 from "fs";
10036
- import path28 from "path";
10543
+ init_pre_commit_block();
10544
+ import fs36 from "fs";
10545
+ import path29 from "path";
10037
10546
  function writeRefreshDocs(docs) {
10038
10547
  const written = [];
10039
10548
  if (docs.claudeMd) {
10040
- fs35.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(docs.claudeMd)));
10549
+ fs36.writeFileSync("CLAUDE.md", appendLearningsBlock(appendPreCommitBlock(docs.claudeMd)));
10041
10550
  written.push("CLAUDE.md");
10042
10551
  }
10043
10552
  if (docs.readmeMd) {
10044
- fs35.writeFileSync("README.md", docs.readmeMd);
10553
+ fs36.writeFileSync("README.md", docs.readmeMd);
10045
10554
  written.push("README.md");
10046
10555
  }
10047
10556
  if (docs.cursorrules) {
10048
- fs35.writeFileSync(".cursorrules", docs.cursorrules);
10557
+ fs36.writeFileSync(".cursorrules", docs.cursorrules);
10049
10558
  written.push(".cursorrules");
10050
10559
  }
10051
10560
  if (docs.cursorRules) {
10052
- const rulesDir = path28.join(".cursor", "rules");
10053
- if (!fs35.existsSync(rulesDir)) fs35.mkdirSync(rulesDir, { recursive: true });
10561
+ const rulesDir = path29.join(".cursor", "rules");
10562
+ if (!fs36.existsSync(rulesDir)) fs36.mkdirSync(rulesDir, { recursive: true });
10054
10563
  for (const rule of docs.cursorRules) {
10055
- fs35.writeFileSync(path28.join(rulesDir, rule.filename), rule.content);
10564
+ fs36.writeFileSync(path29.join(rulesDir, rule.filename), rule.content);
10056
10565
  written.push(`.cursor/rules/${rule.filename}`);
10057
10566
  }
10058
10567
  }
10059
10568
  if (docs.claudeSkills) {
10060
- const skillsDir = path28.join(".claude", "skills");
10061
- if (!fs35.existsSync(skillsDir)) fs35.mkdirSync(skillsDir, { recursive: true });
10569
+ const skillsDir = path29.join(".claude", "skills");
10570
+ if (!fs36.existsSync(skillsDir)) fs36.mkdirSync(skillsDir, { recursive: true });
10062
10571
  for (const skill of docs.claudeSkills) {
10063
- fs35.writeFileSync(path28.join(skillsDir, skill.filename), skill.content);
10572
+ fs36.writeFileSync(path29.join(skillsDir, skill.filename), skill.content);
10064
10573
  written.push(`.claude/skills/${skill.filename}`);
10065
10574
  }
10066
10575
  }
10067
10576
  if (docs.copilotInstructions) {
10068
- fs35.mkdirSync(".github", { recursive: true });
10069
- fs35.writeFileSync(path28.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(docs.copilotInstructions)));
10577
+ fs36.mkdirSync(".github", { recursive: true });
10578
+ fs36.writeFileSync(path29.join(".github", "copilot-instructions.md"), appendLearningsBlock(appendPreCommitBlock(docs.copilotInstructions)));
10070
10579
  written.push(".github/copilot-instructions.md");
10071
10580
  }
10072
10581
  if (docs.copilotInstructionFiles) {
10073
- const instructionsDir = path28.join(".github", "instructions");
10074
- fs35.mkdirSync(instructionsDir, { recursive: true });
10582
+ const instructionsDir = path29.join(".github", "instructions");
10583
+ fs36.mkdirSync(instructionsDir, { recursive: true });
10075
10584
  for (const file of docs.copilotInstructionFiles) {
10076
- fs35.writeFileSync(path28.join(instructionsDir, file.filename), file.content);
10585
+ fs36.writeFileSync(path29.join(instructionsDir, file.filename), file.content);
10077
10586
  written.push(`.github/instructions/${file.filename}`);
10078
10587
  }
10079
10588
  }
@@ -10159,8 +10668,8 @@ Changed files: ${diff.changedFiles.join(", ")}`);
10159
10668
  }
10160
10669
 
10161
10670
  // src/learner/writer.ts
10162
- import fs36 from "fs";
10163
- import path29 from "path";
10671
+ import fs37 from "fs";
10672
+ import path30 from "path";
10164
10673
 
10165
10674
  // src/learner/utils.ts
10166
10675
  var TYPE_PREFIX_RE = /^\*\*\[[^\]]+\]\*\*\s*/;
@@ -10277,20 +10786,20 @@ function deduplicateLearnedItems(existing, incoming) {
10277
10786
  }
10278
10787
  function writeLearnedSectionTo(filePath, header, existing, incoming, mode) {
10279
10788
  const { merged, newCount, newItems } = deduplicateLearnedItems(existing, incoming);
10280
- fs36.writeFileSync(filePath, header + merged + "\n");
10281
- if (mode) fs36.chmodSync(filePath, mode);
10789
+ fs37.writeFileSync(filePath, header + merged + "\n");
10790
+ if (mode) fs37.chmodSync(filePath, mode);
10282
10791
  return { newCount, newItems };
10283
10792
  }
10284
10793
  function writeLearnedSection(content) {
10285
10794
  return writeLearnedSectionTo(LEARNINGS_FILE, LEARNINGS_HEADER, readLearnedSection(), content);
10286
10795
  }
10287
10796
  function writeLearnedSkill(skill) {
10288
- const skillDir = path29.join(".claude", "skills", skill.name);
10289
- if (!fs36.existsSync(skillDir)) fs36.mkdirSync(skillDir, { recursive: true });
10290
- const skillPath = path29.join(skillDir, "SKILL.md");
10291
- if (!skill.isNew && fs36.existsSync(skillPath)) {
10292
- const existing = fs36.readFileSync(skillPath, "utf-8");
10293
- fs36.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
10797
+ const skillDir = path30.join(".claude", "skills", skill.name);
10798
+ if (!fs37.existsSync(skillDir)) fs37.mkdirSync(skillDir, { recursive: true });
10799
+ const skillPath = path30.join(skillDir, "SKILL.md");
10800
+ if (!skill.isNew && fs37.existsSync(skillPath)) {
10801
+ const existing = fs37.readFileSync(skillPath, "utf-8");
10802
+ fs37.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
10294
10803
  } else {
10295
10804
  const frontmatter = [
10296
10805
  "---",
@@ -10299,12 +10808,12 @@ function writeLearnedSkill(skill) {
10299
10808
  "---",
10300
10809
  ""
10301
10810
  ].join("\n");
10302
- fs36.writeFileSync(skillPath, frontmatter + skill.content);
10811
+ fs37.writeFileSync(skillPath, frontmatter + skill.content);
10303
10812
  }
10304
10813
  return skillPath;
10305
10814
  }
10306
10815
  function writePersonalLearnedSection(content) {
10307
- if (!fs36.existsSync(AUTH_DIR)) fs36.mkdirSync(AUTH_DIR, { recursive: true });
10816
+ if (!fs37.existsSync(AUTH_DIR)) fs37.mkdirSync(AUTH_DIR, { recursive: true });
10308
10817
  return writeLearnedSectionTo(PERSONAL_LEARNINGS_FILE, PERSONAL_LEARNINGS_HEADER, readPersonalLearnings(), content, 384);
10309
10818
  }
10310
10819
  function addLearning(bullet, scope = "project") {
@@ -10317,55 +10826,56 @@ function addLearning(bullet, scope = "project") {
10317
10826
  return { file: LEARNINGS_FILE, added: result.newCount > 0 };
10318
10827
  }
10319
10828
  function readPersonalLearnings() {
10320
- if (!fs36.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
10321
- const content = fs36.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
10829
+ if (!fs37.existsSync(PERSONAL_LEARNINGS_FILE)) return null;
10830
+ const content = fs37.readFileSync(PERSONAL_LEARNINGS_FILE, "utf-8");
10322
10831
  const bullets = content.split("\n").filter((l) => l.startsWith("- ")).join("\n");
10323
10832
  return bullets || null;
10324
10833
  }
10325
10834
  function readLearnedSection() {
10326
- if (fs36.existsSync(LEARNINGS_FILE)) {
10327
- const content2 = fs36.readFileSync(LEARNINGS_FILE, "utf-8");
10835
+ if (fs37.existsSync(LEARNINGS_FILE)) {
10836
+ const content2 = fs37.readFileSync(LEARNINGS_FILE, "utf-8");
10328
10837
  const bullets = content2.split("\n").filter((l) => l.startsWith("- ")).join("\n");
10329
10838
  return bullets || null;
10330
10839
  }
10331
10840
  const claudeMdPath = "CLAUDE.md";
10332
- if (!fs36.existsSync(claudeMdPath)) return null;
10333
- const content = fs36.readFileSync(claudeMdPath, "utf-8");
10841
+ if (!fs37.existsSync(claudeMdPath)) return null;
10842
+ const content = fs37.readFileSync(claudeMdPath, "utf-8");
10334
10843
  const startIdx = content.indexOf(LEARNED_START);
10335
10844
  const endIdx = content.indexOf(LEARNED_END);
10336
10845
  if (startIdx === -1 || endIdx === -1) return null;
10337
10846
  return content.slice(startIdx + LEARNED_START.length, endIdx).trim() || null;
10338
10847
  }
10339
10848
  function migrateInlineLearnings() {
10340
- if (fs36.existsSync(LEARNINGS_FILE)) return false;
10849
+ if (fs37.existsSync(LEARNINGS_FILE)) return false;
10341
10850
  const claudeMdPath = "CLAUDE.md";
10342
- if (!fs36.existsSync(claudeMdPath)) return false;
10343
- const content = fs36.readFileSync(claudeMdPath, "utf-8");
10851
+ if (!fs37.existsSync(claudeMdPath)) return false;
10852
+ const content = fs37.readFileSync(claudeMdPath, "utf-8");
10344
10853
  const startIdx = content.indexOf(LEARNED_START);
10345
10854
  const endIdx = content.indexOf(LEARNED_END);
10346
10855
  if (startIdx === -1 || endIdx === -1) return false;
10347
10856
  const section = content.slice(startIdx + LEARNED_START.length, endIdx).trim();
10348
10857
  if (!section) return false;
10349
- fs36.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
10858
+ fs37.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
10350
10859
  const cleaned = content.slice(0, startIdx) + content.slice(endIdx + LEARNED_END.length);
10351
- fs36.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
10860
+ fs37.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
10352
10861
  return true;
10353
10862
  }
10354
10863
 
10355
10864
  // src/commands/refresh.ts
10356
10865
  init_config();
10357
10866
  init_resolve_caliber();
10867
+ init_builtin_skills();
10358
10868
  function log2(quiet, ...args) {
10359
10869
  if (!quiet) console.log(...args);
10360
10870
  }
10361
10871
  function discoverGitRepos(parentDir) {
10362
10872
  const repos = [];
10363
10873
  try {
10364
- const entries = fs38.readdirSync(parentDir, { withFileTypes: true });
10874
+ const entries = fs39.readdirSync(parentDir, { withFileTypes: true });
10365
10875
  for (const entry of entries) {
10366
10876
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
10367
- const childPath = path31.join(parentDir, entry.name);
10368
- if (fs38.existsSync(path31.join(childPath, ".git"))) {
10877
+ const childPath = path32.join(parentDir, entry.name);
10878
+ if (fs39.existsSync(path32.join(childPath, ".git"))) {
10369
10879
  repos.push(childPath);
10370
10880
  }
10371
10881
  }
@@ -10451,26 +10961,27 @@ async function refreshSingleRepo(repoDir, options) {
10451
10961
  const filesToWrite = response.docsUpdated || [];
10452
10962
  const preRefreshContents = /* @__PURE__ */ new Map();
10453
10963
  for (const filePath of filesToWrite) {
10454
- const fullPath = path31.resolve(repoDir, filePath);
10964
+ const fullPath = path32.resolve(repoDir, filePath);
10455
10965
  try {
10456
- preRefreshContents.set(filePath, fs38.readFileSync(fullPath, "utf-8"));
10966
+ preRefreshContents.set(filePath, fs39.readFileSync(fullPath, "utf-8"));
10457
10967
  } catch {
10458
10968
  preRefreshContents.set(filePath, null);
10459
10969
  }
10460
10970
  }
10461
10971
  const written = writeRefreshDocs(response.updatedDocs);
10462
- trackRefreshCompleted(written.length, Date.now());
10972
+ const trigger = quiet ? "hook" : "manual";
10973
+ trackRefreshCompleted(written.length, Date.now(), trigger);
10463
10974
  const postScore = computeLocalScore(repoDir, targetAgent);
10464
10975
  if (postScore.score < preScore.score) {
10465
10976
  for (const [filePath, content] of preRefreshContents) {
10466
- const fullPath = path31.resolve(repoDir, filePath);
10977
+ const fullPath = path32.resolve(repoDir, filePath);
10467
10978
  if (content === null) {
10468
10979
  try {
10469
- fs38.unlinkSync(fullPath);
10980
+ fs39.unlinkSync(fullPath);
10470
10981
  } catch {
10471
10982
  }
10472
10983
  } else {
10473
- fs38.writeFileSync(fullPath, content);
10984
+ fs39.writeFileSync(fullPath, content);
10474
10985
  }
10475
10986
  }
10476
10987
  spinner?.warn(`${prefix}Refresh reverted \u2014 score would drop from ${preScore.score} to ${postScore.score}`);
@@ -10525,7 +11036,7 @@ async function refreshCommand(options) {
10525
11036
  `));
10526
11037
  const originalDir = process.cwd();
10527
11038
  for (const repo of repos) {
10528
- const repoName = path31.basename(repo);
11039
+ const repoName = path32.basename(repo);
10529
11040
  try {
10530
11041
  process.chdir(repo);
10531
11042
  await refreshSingleRepo(repo, { ...options, label: repoName });
@@ -10547,150 +11058,6 @@ async function refreshCommand(options) {
10547
11058
  // src/commands/hooks.ts
10548
11059
  import chalk20 from "chalk";
10549
11060
  import fs40 from "fs";
10550
-
10551
- // src/lib/hooks.ts
10552
- init_resolve_caliber();
10553
- import fs39 from "fs";
10554
- import path32 from "path";
10555
- import { execSync as execSync15 } from "child_process";
10556
- var SETTINGS_PATH2 = path32.join(".claude", "settings.json");
10557
- var REFRESH_TAIL = "refresh --quiet";
10558
- var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
10559
- function getHookCommand() {
10560
- return `${resolveCaliber()} ${REFRESH_TAIL}`;
10561
- }
10562
- function readSettings2() {
10563
- if (!fs39.existsSync(SETTINGS_PATH2)) return {};
10564
- try {
10565
- return JSON.parse(fs39.readFileSync(SETTINGS_PATH2, "utf-8"));
10566
- } catch {
10567
- return {};
10568
- }
10569
- }
10570
- function writeSettings2(settings) {
10571
- const dir = path32.dirname(SETTINGS_PATH2);
10572
- if (!fs39.existsSync(dir)) fs39.mkdirSync(dir, { recursive: true });
10573
- fs39.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
10574
- }
10575
- function findHookIndex(sessionEnd) {
10576
- return sessionEnd.findIndex(
10577
- (entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, REFRESH_TAIL))
10578
- );
10579
- }
10580
- function isHookInstalled() {
10581
- const settings = readSettings2();
10582
- const sessionEnd = settings.hooks?.SessionEnd;
10583
- if (!Array.isArray(sessionEnd)) return false;
10584
- return findHookIndex(sessionEnd) !== -1;
10585
- }
10586
- function installHook() {
10587
- const settings = readSettings2();
10588
- if (!settings.hooks) settings.hooks = {};
10589
- if (!Array.isArray(settings.hooks.SessionEnd)) settings.hooks.SessionEnd = [];
10590
- if (findHookIndex(settings.hooks.SessionEnd) !== -1) {
10591
- return { installed: false, alreadyInstalled: true };
10592
- }
10593
- settings.hooks.SessionEnd.push({
10594
- matcher: "",
10595
- hooks: [{ type: "command", command: getHookCommand(), description: HOOK_DESCRIPTION }]
10596
- });
10597
- writeSettings2(settings);
10598
- return { installed: true, alreadyInstalled: false };
10599
- }
10600
- function removeHook() {
10601
- const settings = readSettings2();
10602
- const sessionEnd = settings.hooks?.SessionEnd;
10603
- if (!Array.isArray(sessionEnd)) {
10604
- return { removed: false, notFound: true };
10605
- }
10606
- const idx = findHookIndex(sessionEnd);
10607
- if (idx === -1) {
10608
- return { removed: false, notFound: true };
10609
- }
10610
- sessionEnd.splice(idx, 1);
10611
- if (sessionEnd.length === 0) {
10612
- delete settings.hooks.SessionEnd;
10613
- }
10614
- if (settings.hooks && Object.keys(settings.hooks).length === 0) {
10615
- delete settings.hooks;
10616
- }
10617
- writeSettings2(settings);
10618
- return { removed: true, notFound: false };
10619
- }
10620
- var PRECOMMIT_START = "# caliber:pre-commit:start";
10621
- var PRECOMMIT_END = "# caliber:pre-commit:end";
10622
- function getPrecommitBlock() {
10623
- const bin = resolveCaliber();
10624
- const npx = isNpxResolution();
10625
- const guard = npx ? "command -v npx >/dev/null 2>&1" : `[ -x "${bin}" ] || command -v "${bin}" >/dev/null 2>&1`;
10626
- const invoke = npx ? bin : `"${bin}"`;
10627
- return `${PRECOMMIT_START}
10628
- if ${guard}; then
10629
- echo "\\033[2mcaliber: refreshing docs...\\033[0m"
10630
- ${invoke} refresh 2>/dev/null || true
10631
- ${invoke} learn finalize 2>/dev/null || true
10632
- git diff --name-only -- CLAUDE.md .claude/ .cursor/ AGENTS.md CALIBER_LEARNINGS.md 2>/dev/null | xargs git add 2>/dev/null || true
10633
- fi
10634
- ${PRECOMMIT_END}`;
10635
- }
10636
- function getGitHooksDir() {
10637
- try {
10638
- const gitDir = execSync15("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
10639
- return path32.join(gitDir, "hooks");
10640
- } catch {
10641
- return null;
10642
- }
10643
- }
10644
- function getPreCommitPath() {
10645
- const hooksDir = getGitHooksDir();
10646
- return hooksDir ? path32.join(hooksDir, "pre-commit") : null;
10647
- }
10648
- function isPreCommitHookInstalled() {
10649
- const hookPath = getPreCommitPath();
10650
- if (!hookPath || !fs39.existsSync(hookPath)) return false;
10651
- const content = fs39.readFileSync(hookPath, "utf-8");
10652
- return content.includes(PRECOMMIT_START);
10653
- }
10654
- function installPreCommitHook() {
10655
- if (isPreCommitHookInstalled()) {
10656
- return { installed: false, alreadyInstalled: true };
10657
- }
10658
- const hookPath = getPreCommitPath();
10659
- if (!hookPath) return { installed: false, alreadyInstalled: false };
10660
- const hooksDir = path32.dirname(hookPath);
10661
- if (!fs39.existsSync(hooksDir)) fs39.mkdirSync(hooksDir, { recursive: true });
10662
- let content = "";
10663
- if (fs39.existsSync(hookPath)) {
10664
- content = fs39.readFileSync(hookPath, "utf-8");
10665
- if (!content.endsWith("\n")) content += "\n";
10666
- content += "\n" + getPrecommitBlock() + "\n";
10667
- } else {
10668
- content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
10669
- }
10670
- fs39.writeFileSync(hookPath, content);
10671
- fs39.chmodSync(hookPath, 493);
10672
- return { installed: true, alreadyInstalled: false };
10673
- }
10674
- function removePreCommitHook() {
10675
- const hookPath = getPreCommitPath();
10676
- if (!hookPath || !fs39.existsSync(hookPath)) {
10677
- return { removed: false, notFound: true };
10678
- }
10679
- let content = fs39.readFileSync(hookPath, "utf-8");
10680
- if (!content.includes(PRECOMMIT_START)) {
10681
- return { removed: false, notFound: true };
10682
- }
10683
- const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
10684
- content = content.replace(regex, "\n");
10685
- if (content.trim() === "#!/bin/sh" || content.trim() === "") {
10686
- fs39.unlinkSync(hookPath);
10687
- } else {
10688
- fs39.writeFileSync(hookPath, content);
10689
- }
10690
- return { removed: true, notFound: false };
10691
- }
10692
-
10693
- // src/commands/hooks.ts
10694
11061
  var HOOKS = [
10695
11062
  {
10696
11063
  id: "session-end",
@@ -12299,10 +12666,199 @@ async function publishCommand() {
12299
12666
  }
12300
12667
  }
12301
12668
 
12669
+ // src/commands/bootstrap.ts
12670
+ init_builtin_skills();
12671
+ import fs47 from "fs";
12672
+ import chalk27 from "chalk";
12673
+ var PLATFORM_SKILL_DIRS = {
12674
+ claude: ".claude/skills",
12675
+ cursor: ".cursor/skills",
12676
+ codex: ".agents/skills"
12677
+ };
12678
+ async function bootstrapCommand() {
12679
+ const platforms = detectPlatforms();
12680
+ const detected = [];
12681
+ if (platforms.claude) detected.push("claude");
12682
+ if (platforms.cursor) detected.push("cursor");
12683
+ if (platforms.codex) detected.push("codex");
12684
+ if (detected.length === 0) detected.push("claude");
12685
+ const written = [];
12686
+ for (const platform of detected) {
12687
+ const skillsDir = PLATFORM_SKILL_DIRS[platform];
12688
+ if (!skillsDir) continue;
12689
+ for (const skill of BUILTIN_SKILLS) {
12690
+ const skillDir = `${skillsDir}/${skill.name}`;
12691
+ const skillPath = `${skillDir}/SKILL.md`;
12692
+ fs47.mkdirSync(skillDir, { recursive: true });
12693
+ fs47.writeFileSync(skillPath, buildSkillContent(skill));
12694
+ written.push(skillPath);
12695
+ }
12696
+ }
12697
+ if (written.length === 0) {
12698
+ console.log(chalk27.yellow("No skills were written."));
12699
+ return;
12700
+ }
12701
+ console.log(chalk27.bold.green("\n Caliber skills installed!\n"));
12702
+ for (const file of written) {
12703
+ console.log(` ${chalk27.green("\u2713")} ${file}`);
12704
+ }
12705
+ console.log(chalk27.dim("\n Your agent can now run /setup-caliber to complete the setup."));
12706
+ console.log(chalk27.dim(' Just tell your agent: "Run /setup-caliber"\n'));
12707
+ }
12708
+
12709
+ // src/commands/uninstall.ts
12710
+ import fs48 from "fs";
12711
+ import path39 from "path";
12712
+ import chalk28 from "chalk";
12713
+ import confirm3 from "@inquirer/confirm";
12714
+ init_pre_commit_block();
12715
+ init_builtin_skills();
12716
+ init_config();
12717
+ var MANAGED_DOC_FILES = [
12718
+ "CLAUDE.md",
12719
+ path39.join(".github", "copilot-instructions.md"),
12720
+ "AGENTS.md"
12721
+ ];
12722
+ var SKILL_DIRS = PLATFORM_CONFIGS.map((c) => c.skillsDir);
12723
+ var CURSOR_RULES_DIR = path39.join(".cursor", "rules");
12724
+ function removeCaliberCursorRules() {
12725
+ const removed = [];
12726
+ if (!fs48.existsSync(CURSOR_RULES_DIR)) return removed;
12727
+ for (const file of fs48.readdirSync(CURSOR_RULES_DIR)) {
12728
+ if (file.startsWith("caliber-") && file.endsWith(".mdc")) {
12729
+ const fullPath = path39.join(CURSOR_RULES_DIR, file);
12730
+ fs48.unlinkSync(fullPath);
12731
+ removed.push(fullPath);
12732
+ }
12733
+ }
12734
+ return removed;
12735
+ }
12736
+ function removeBuiltinSkills() {
12737
+ const removed = [];
12738
+ for (const skillsDir of SKILL_DIRS) {
12739
+ if (!fs48.existsSync(skillsDir)) continue;
12740
+ for (const name of BUILTIN_SKILL_NAMES) {
12741
+ const skillDir = path39.join(skillsDir, name);
12742
+ if (fs48.existsSync(skillDir)) {
12743
+ fs48.rmSync(skillDir, { recursive: true });
12744
+ removed.push(skillDir);
12745
+ }
12746
+ }
12747
+ }
12748
+ return removed;
12749
+ }
12750
+ function stripManagedBlocksFromFiles() {
12751
+ const modified = [];
12752
+ for (const filePath of MANAGED_DOC_FILES) {
12753
+ if (!fs48.existsSync(filePath)) continue;
12754
+ const original = fs48.readFileSync(filePath, "utf-8");
12755
+ const stripped = stripManagedBlocks(original);
12756
+ if (stripped !== original) {
12757
+ const trimmed = stripped.trim();
12758
+ if (!trimmed || /^#\s*\S*$/.test(trimmed)) {
12759
+ fs48.unlinkSync(filePath);
12760
+ } else {
12761
+ fs48.writeFileSync(filePath, stripped);
12762
+ }
12763
+ modified.push(filePath);
12764
+ }
12765
+ }
12766
+ return modified;
12767
+ }
12768
+ function removeDirectory(dir) {
12769
+ if (!fs48.existsSync(dir)) return false;
12770
+ fs48.rmSync(dir, { recursive: true });
12771
+ return true;
12772
+ }
12773
+ async function uninstallCommand(options) {
12774
+ console.log(chalk28.bold("\n Caliber Uninstall\n"));
12775
+ console.log(chalk28.dim(" This will remove all Caliber resources from this project:\n"));
12776
+ console.log(chalk28.dim(" \u2022 Pre-commit hook"));
12777
+ console.log(chalk28.dim(" \u2022 Session learning hooks"));
12778
+ console.log(chalk28.dim(" \u2022 Managed blocks in CLAUDE.md, AGENTS.md, copilot-instructions.md"));
12779
+ console.log(chalk28.dim(" \u2022 Cursor rules (caliber-*.mdc)"));
12780
+ console.log(chalk28.dim(" \u2022 Built-in skills (setup-caliber, find-skills, save-learning)"));
12781
+ console.log(chalk28.dim(" \u2022 CALIBER_LEARNINGS.md"));
12782
+ console.log(chalk28.dim(" \u2022 .caliber/ directory (backups, cache, state)\n"));
12783
+ if (!options.force) {
12784
+ const proceed = await confirm3({ message: "Continue with uninstall?" });
12785
+ if (!proceed) {
12786
+ console.log(chalk28.dim("\n Cancelled.\n"));
12787
+ return;
12788
+ }
12789
+ }
12790
+ console.log("");
12791
+ const actions = [];
12792
+ const hookResult = removePreCommitHook();
12793
+ if (hookResult.removed) {
12794
+ console.log(` ${chalk28.red("\u2717")} Pre-commit hook removed`);
12795
+ actions.push("pre-commit hook");
12796
+ }
12797
+ const learnResult = removeLearningHooks();
12798
+ if (learnResult.removed) {
12799
+ console.log(` ${chalk28.red("\u2717")} Claude Code learning hooks removed`);
12800
+ actions.push("claude learning hooks");
12801
+ }
12802
+ const cursorLearnResult = removeCursorLearningHooks();
12803
+ if (cursorLearnResult.removed) {
12804
+ console.log(` ${chalk28.red("\u2717")} Cursor learning hooks removed`);
12805
+ actions.push("cursor learning hooks");
12806
+ }
12807
+ const strippedFiles = stripManagedBlocksFromFiles();
12808
+ for (const file of strippedFiles) {
12809
+ console.log(` ${chalk28.yellow("~")} ${file} \u2014 managed blocks removed`);
12810
+ actions.push(file);
12811
+ }
12812
+ const removedRules = removeCaliberCursorRules();
12813
+ for (const rule of removedRules) {
12814
+ console.log(` ${chalk28.red("\u2717")} ${rule}`);
12815
+ }
12816
+ if (removedRules.length > 0) actions.push("cursor rules");
12817
+ const removedSkills = removeBuiltinSkills();
12818
+ for (const skill of removedSkills) {
12819
+ console.log(` ${chalk28.red("\u2717")} ${skill}/`);
12820
+ }
12821
+ if (removedSkills.length > 0) actions.push("builtin skills");
12822
+ if (fs48.existsSync("CALIBER_LEARNINGS.md")) {
12823
+ fs48.unlinkSync("CALIBER_LEARNINGS.md");
12824
+ console.log(` ${chalk28.red("\u2717")} CALIBER_LEARNINGS.md`);
12825
+ actions.push("learnings file");
12826
+ }
12827
+ if (removeDirectory(CALIBER_DIR)) {
12828
+ console.log(` ${chalk28.red("\u2717")} .caliber/ directory`);
12829
+ actions.push(".caliber directory");
12830
+ }
12831
+ if (actions.length === 0) {
12832
+ console.log(chalk28.dim(" Nothing to remove \u2014 Caliber is not installed in this project.\n"));
12833
+ return;
12834
+ }
12835
+ trackUninstallExecuted();
12836
+ const configPath = getConfigFilePath();
12837
+ if (fs48.existsSync(configPath)) {
12838
+ console.log("");
12839
+ const removeConfig = options.force || await confirm3({
12840
+ message: `Remove global config (~/.caliber/config.json)? This affects all projects.`
12841
+ });
12842
+ if (removeConfig) {
12843
+ fs48.unlinkSync(configPath);
12844
+ console.log(` ${chalk28.red("\u2717")} ${configPath}`);
12845
+ const configDir = path39.dirname(configPath);
12846
+ try {
12847
+ const remaining = fs48.readdirSync(configDir);
12848
+ if (remaining.length === 0) fs48.rmdirSync(configDir);
12849
+ } catch {
12850
+ }
12851
+ }
12852
+ }
12853
+ console.log(chalk28.bold.green(`
12854
+ Caliber has been removed from this project.`));
12855
+ console.log(chalk28.dim(" Your code is untouched \u2014 only Caliber config files were removed.\n"));
12856
+ }
12857
+
12302
12858
  // src/cli.ts
12303
- var __dirname = path39.dirname(fileURLToPath(import.meta.url));
12859
+ var __dirname = path40.dirname(fileURLToPath(import.meta.url));
12304
12860
  var pkg = JSON.parse(
12305
- fs47.readFileSync(path39.resolve(__dirname, "..", "package.json"), "utf-8")
12861
+ fs49.readFileSync(path40.resolve(__dirname, "..", "package.json"), "utf-8")
12306
12862
  );
12307
12863
  var program = new Command();
12308
12864
  var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
@@ -12310,9 +12866,11 @@ program.name(process.env.CALIBER_LOCAL ? "caloc" : "caliber").description("AI co
12310
12866
  function tracked(commandName, handler) {
12311
12867
  const wrapper = async (...args) => {
12312
12868
  const start = Date.now();
12869
+ const isCI = !!(process.env.CI || process.env.GITHUB_ACTIONS);
12313
12870
  trackEvent("command_started", {
12314
12871
  command: commandName,
12315
- cli_version: pkg.version
12872
+ cli_version: pkg.version,
12873
+ is_ci: isCI
12316
12874
  });
12317
12875
  try {
12318
12876
  await handler(...args);
@@ -12364,7 +12922,9 @@ function parseAgentOption(value) {
12364
12922
  return agents;
12365
12923
  }
12366
12924
  program.command("init").description("Initialize your project for AI-assisted development").option("--agent <type>", "Target agents (comma-separated): claude, cursor, codex, github-copilot", parseAgentOption).option("--source <paths...>", "Related source paths to include as context").option("--dry-run", "Preview changes without writing files").option("--force", "Overwrite existing config without prompting").option("--debug-report", void 0, false).option("--show-tokens", "Show token usage summary at the end").option("--auto-approve", "Run without interactive prompts (auto-accept all)").option("--verbose", "Show detailed logs of each step").action(tracked("init", initCommand));
12925
+ program.command("bootstrap").description("Install agent skills (/setup-caliber, /find-skills, /save-learning) without running init").action(tracked("bootstrap", bootstrapCommand));
12367
12926
  program.command("undo").description("Revert all config changes made by Caliber").action(tracked("undo", undoCommand));
12927
+ program.command("uninstall").description("Remove all Caliber resources from this project").option("--force", "Skip confirmation prompt").action(tracked("uninstall", (options) => uninstallCommand(options)));
12368
12928
  program.command("status").description("Show current Caliber config status").option("--json", "Output as JSON").action(tracked("status", statusCommand));
12369
12929
  program.command("regenerate").alias("regen").alias("re").description("Re-analyze project and regenerate config").option("--dry-run", "Preview changes without writing files").action(tracked("regenerate", regenerateCommand));
12370
12930
  program.command("config").description("Configure LLM provider, API key, and model").action(tracked("config", configCommand));
@@ -12389,15 +12949,15 @@ learn.command("delete <index>").description("Delete a learning by its index numb
12389
12949
  learn.command("add <content>").description("Add a learning directly (used by agent skills)").option("--personal", "Save as a personal learning instead of project-level").action(tracked("learn:add", learnAddCommand));
12390
12950
 
12391
12951
  // src/utils/version-check.ts
12392
- import fs48 from "fs";
12393
- import path40 from "path";
12952
+ import fs50 from "fs";
12953
+ import path41 from "path";
12394
12954
  import { fileURLToPath as fileURLToPath2 } from "url";
12395
12955
  import { execSync as execSync16, execFileSync as execFileSync3 } from "child_process";
12396
- import chalk27 from "chalk";
12956
+ import chalk29 from "chalk";
12397
12957
  import ora8 from "ora";
12398
- import confirm2 from "@inquirer/confirm";
12399
- var __dirname_vc = path40.dirname(fileURLToPath2(import.meta.url));
12400
- var pkg2 = JSON.parse(fs48.readFileSync(path40.resolve(__dirname_vc, "..", "package.json"), "utf-8"));
12958
+ import confirm4 from "@inquirer/confirm";
12959
+ var __dirname_vc = path41.dirname(fileURLToPath2(import.meta.url));
12960
+ var pkg2 = JSON.parse(fs50.readFileSync(path41.resolve(__dirname_vc, "..", "package.json"), "utf-8"));
12401
12961
  function getChannel(version) {
12402
12962
  const match = version.match(/-(dev|next)\./);
12403
12963
  return match ? match[1] : "latest";
@@ -12424,8 +12984,8 @@ function getInstalledVersion() {
12424
12984
  encoding: "utf-8",
12425
12985
  stdio: ["pipe", "pipe", "pipe"]
12426
12986
  }).trim();
12427
- const pkgPath = path40.join(globalRoot, "@rely-ai", "caliber", "package.json");
12428
- return JSON.parse(fs48.readFileSync(pkgPath, "utf-8")).version;
12987
+ const pkgPath = path41.join(globalRoot, "@rely-ai", "caliber", "package.json");
12988
+ return JSON.parse(fs50.readFileSync(pkgPath, "utf-8")).version;
12429
12989
  } catch {
12430
12990
  return null;
12431
12991
  }
@@ -12450,18 +13010,18 @@ async function checkForUpdates() {
12450
13010
  if (!isInteractive) {
12451
13011
  const installTag = channel === "latest" ? "" : `@${channel}`;
12452
13012
  console.log(
12453
- chalk27.yellow(
13013
+ chalk29.yellow(
12454
13014
  `
12455
13015
  Update available: ${current} -> ${latest}
12456
- Run ${chalk27.bold(`npm install -g @rely-ai/caliber${installTag}`)} to upgrade.
13016
+ Run ${chalk29.bold(`npm install -g @rely-ai/caliber${installTag}`)} to upgrade.
12457
13017
  `
12458
13018
  )
12459
13019
  );
12460
13020
  return;
12461
13021
  }
12462
- console.log(chalk27.yellow(`
13022
+ console.log(chalk29.yellow(`
12463
13023
  Update available: ${current} -> ${latest}`));
12464
- const shouldUpdate = await confirm2({
13024
+ const shouldUpdate = await confirm4({
12465
13025
  message: "Would you like to update now? (Y/n)",
12466
13026
  default: true
12467
13027
  });
@@ -12482,14 +13042,14 @@ Update available: ${current} -> ${latest}`));
12482
13042
  if (installed !== latest) {
12483
13043
  spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
12484
13044
  console.log(
12485
- chalk27.yellow(`Run ${chalk27.bold(`npm install -g @rely-ai/caliber@${tag}`)} manually.
13045
+ chalk29.yellow(`Run ${chalk29.bold(`npm install -g @rely-ai/caliber@${tag}`)} manually.
12486
13046
  `)
12487
13047
  );
12488
13048
  return;
12489
13049
  }
12490
- spinner.succeed(chalk27.green(`Updated to ${latest}`));
13050
+ spinner.succeed(chalk29.green(`Updated to ${latest}`));
12491
13051
  const args = process.argv.slice(2);
12492
- console.log(chalk27.dim(`
13052
+ console.log(chalk29.dim(`
12493
13053
  Restarting: caliber ${args.join(" ")}
12494
13054
  `));
12495
13055
  execFileSync3("caliber", args, {
@@ -12502,11 +13062,11 @@ Restarting: caliber ${args.join(" ")}
12502
13062
  if (err instanceof Error) {
12503
13063
  const stderr = err.stderr;
12504
13064
  const errMsg = stderr ? String(stderr).trim().split("\n").pop() : err.message.split("\n")[0];
12505
- if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk27.dim(` ${errMsg}`));
13065
+ if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk29.dim(` ${errMsg}`));
12506
13066
  }
12507
13067
  console.log(
12508
- chalk27.yellow(
12509
- `Run ${chalk27.bold(`npm install -g @rely-ai/caliber@${tag}`)} manually to upgrade.
13068
+ chalk29.yellow(
13069
+ `Run ${chalk29.bold(`npm install -g @rely-ai/caliber@${tag}`)} manually to upgrade.
12510
13070
  `
12511
13071
  )
12512
13072
  );