@rely-ai/caliber 1.33.4 → 1.34.0-dev.1774825558

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