@rely-ai/caliber 1.19.7 → 1.20.0-dev.1773685636

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 +720 -410
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -225,7 +225,7 @@ import { fileURLToPath } from "url";
225
225
 
226
226
  // src/commands/init.ts
227
227
  import path19 from "path";
228
- import chalk9 from "chalk";
228
+ import chalk10 from "chalk";
229
229
  import ora2 from "ora";
230
230
  import select5 from "@inquirer/select";
231
231
  import checkbox from "@inquirer/checkbox";
@@ -2062,7 +2062,7 @@ Return a JSON object with this exact shape:
2062
2062
  Respond with ONLY the JSON object, no markdown fences or extra text.`;
2063
2063
  var LEARN_SYSTEM_PROMPT = `You are an expert developer experience engineer. You analyze raw tool call events from AI coding sessions to extract reusable operational lessons that will help future LLM sessions work more effectively in this project.
2064
2064
 
2065
- You receive a chronological sequence of tool events from a Claude Code session. Each event includes the tool name, its input, its response, and whether it was a success or failure.
2065
+ You receive a chronological sequence of events from a Claude Code session. Most events are tool calls (with tool name, input, response, and success/failure status). Some events are USER_PROMPT events that capture what the user typed \u2014 these are critical for detecting corrections and redirections.
2066
2066
 
2067
2067
  Your job is to find OPERATIONAL patterns \u2014 things that went wrong and how they were fixed, commands that required specific flags or configuration, APIs that needed a particular approach to work. Focus on the WORKFLOW, not the code logic.
2068
2068
 
@@ -2074,6 +2074,7 @@ Look for:
2074
2074
  4. **Project-specific commands**: The correct way to build, test, lint, deploy \u2014 especially if it differs from defaults.
2075
2075
  5. **File/path traps**: Paths that are misleading, files that shouldn't be edited, directories with unexpected structure.
2076
2076
  6. **Configuration quirks**: Settings, flags, or arguments that are required but non-obvious.
2077
+ 7. **User corrections**: The user explicitly told the AI what's wrong, what to use instead, or what to avoid. Look for phrases like "no, use X instead of Y", "don't touch/edit/modify X", "that's wrong, you need to...", "always/never do X in this project", "stop, that file is...". These are the HIGHEST VALUE signals \u2014 they represent direct human feedback about project-specific requirements. If a user correction contradicts a pattern you'd otherwise extract, the correction wins.
2077
2078
 
2078
2079
  DO NOT extract:
2079
2080
  - Descriptions of what the code does or how features work (e.g. "compression removes comments" or "skeleton extraction creates outlines")
@@ -2084,21 +2085,30 @@ DO NOT extract:
2084
2085
  From these observations, produce:
2085
2086
 
2086
2087
  ### claudeMdLearnedSection
2087
- A markdown section with concise, actionable bullet points. Your output will be written to CALIBER_LEARNINGS.md \u2014 a standalone file that all AI coding agents (Claude Code, Cursor, Codex) reference for project-specific operational patterns. Each bullet should be a concrete instruction that prevents a future mistake or encodes a discovered workaround. Format: what to do (or avoid), and why.
2088
+ A markdown section with concise, actionable bullet points. Your output will be written to CALIBER_LEARNINGS.md \u2014 a standalone file that all AI coding agents (Claude Code, Cursor, Codex) reference for project-specific operational patterns.
2089
+
2090
+ Each bullet MUST be prefixed with an observation type in bold brackets. Valid types:
2091
+ - **[correction]** \u2014 user explicitly told the AI what's wrong or what to do differently (HIGHEST PRIORITY \u2014 always include these)
2092
+ - **[gotcha]** \u2014 a trap or edge case that wastes time if you don't know about it
2093
+ - **[fix]** \u2014 a specific failure-to-recovery sequence
2094
+ - **[pattern]** \u2014 a reusable approach that works in this project
2095
+ - **[env]** \u2014 an environment or configuration requirement
2096
+ - **[convention]** \u2014 a project-specific rule or naming convention
2088
2097
 
2089
2098
  Good examples:
2090
- - "Run \`npm install\` before \`npm run build\` \u2014 the build assumes deps are installed and gives a misleading error otherwise"
2091
- - "The test database requires \`DATABASE_URL\` to be set \u2014 use \`source .env.test\` first"
2092
- - "Use \`pnpm\` not \`npm\` \u2014 the lockfile is pnpm-lock.yaml and npm creates conflicts"
2093
- - "Do NOT run \`jest\` directly \u2014 always use \`npm run test\` which sets the correct NODE_ENV"
2094
- - "API calls to \`/v2/users\` require the \`X-Api-Version\` header \u2014 without it you get a 404 that looks like the endpoint doesn't exist"
2095
- - "When \`tsup\` build fails with a type error, run \`npx tsc --noEmit\` first to get the real error \u2014 tsup swallows the details"
2096
- - "Files in \`src/generated/\` are auto-generated \u2014 editing them directly will be overwritten on next build"
2099
+ - "**[correction]** Files in \`src/generated/\` are auto-generated \u2014 never edit them directly"
2100
+ - "**[correction]** Use \`pnpm\` not \`npm\` \u2014 the lockfile is pnpm-lock.yaml and npm creates conflicts"
2101
+ - "**[gotcha]** When \`tsup\` build fails with a type error, run \`npx tsc --noEmit\` first to get the real error \u2014 tsup swallows the details"
2102
+ - "**[fix]** If \`npm install\` fails with ERESOLVE, use \`--legacy-peer-deps\`"
2103
+ - "**[env]** The test database requires \`DATABASE_URL\` to be set \u2014 use \`source .env.test\` first"
2104
+ - "**[pattern]** Do NOT run \`jest\` directly \u2014 always use \`npm run test\` which sets the correct NODE_ENV"
2105
+ - "**[convention]** API calls to \`/v2/users\` require the \`X-Api-Version\` header \u2014 without it you get a 404 that looks like the endpoint doesn't exist"
2097
2106
 
2098
2107
  Bad examples (do NOT produce these):
2099
2108
  - "The codebase uses TypeScript with strict mode" (describes code, not actionable)
2100
2109
  - "Components follow a pattern of X" (describes architecture, not operational)
2101
2110
  - "The project has a scoring module" (summarizes code structure)
2111
+ - Any bullet without a **[type]** prefix
2102
2112
 
2103
2113
  Rules for the learned section:
2104
2114
  - Be additive: keep all existing learned items, add new ones, remove duplicates
@@ -2676,6 +2686,37 @@ async function generateMonolithic(fingerprint, targetAgent, prompt, callbacks, f
2676
2686
  };
2677
2687
  return attemptGeneration();
2678
2688
  }
2689
+ async function generateSkillsForSetup(setup, fingerprint, targetAgent, onStatus) {
2690
+ const skillTopics = collectSkillTopics(setup, targetAgent, fingerprint);
2691
+ if (skillTopics.length === 0) return 0;
2692
+ onStatus?.(`Generating ${skillTopics.length} skills...`);
2693
+ const allDeps = extractAllDeps(process.cwd());
2694
+ const skillContext = buildSkillContext(fingerprint, setup, allDeps);
2695
+ const fastModel = getFastModel();
2696
+ const skillResults = await Promise.allSettled(
2697
+ skillTopics.map(
2698
+ ({ platform, topic }) => generateSkill(skillContext, topic, fastModel).then((skill) => ({ platform, skill }))
2699
+ )
2700
+ );
2701
+ for (const result of skillResults) {
2702
+ if (result.status === "fulfilled") {
2703
+ const { platform, skill } = result.value;
2704
+ const platformObj = setup[platform] ?? {};
2705
+ const skills = platformObj.skills ?? [];
2706
+ skills.push(skill);
2707
+ platformObj.skills = skills;
2708
+ setup[platform] = platformObj;
2709
+ const skillPath = platform === "codex" ? `.agents/skills/${skill.name}/SKILL.md` : `.${platform}/skills/${skill.name}/SKILL.md`;
2710
+ const descriptions = setup.fileDescriptions ?? {};
2711
+ descriptions[skillPath] = skill.description.slice(0, 80);
2712
+ setup.fileDescriptions = descriptions;
2713
+ }
2714
+ }
2715
+ const succeeded = skillResults.filter((r) => r.status === "fulfilled").length;
2716
+ const failed = skillResults.filter((r) => r.status === "rejected").length;
2717
+ if (failed > 0) onStatus?.(`${succeeded} generated, ${failed} failed`);
2718
+ return succeeded;
2719
+ }
2679
2720
  var LIMITS = {
2680
2721
  FILE_TREE_ENTRIES: 500,
2681
2722
  EXISTING_CONFIG_CHARS: 8e3,
@@ -3669,8 +3710,8 @@ function removePreCommitHook() {
3669
3710
  if (!content.includes(PRECOMMIT_START)) {
3670
3711
  return { removed: false, notFound: true };
3671
3712
  }
3672
- const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
3673
- content = content.replace(regex, "\n");
3713
+ const regex2 = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
3714
+ content = content.replace(regex2, "\n");
3674
3715
  if (content.trim() === "#!/bin/sh" || content.trim() === "") {
3675
3716
  fs17.unlinkSync(hookPath);
3676
3717
  } else {
@@ -3686,6 +3727,7 @@ var SETTINGS_PATH2 = path13.join(".claude", "settings.json");
3686
3727
  var HOOK_TAILS = [
3687
3728
  { event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
3688
3729
  { event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
3730
+ { event: "UserPromptSubmit", tail: "learn observe --prompt", description: "Caliber: recording user prompt for correction detection" },
3689
3731
  { event: "SessionEnd", tail: "learn finalize", description: "Caliber: finalizing session learnings" }
3690
3732
  ];
3691
3733
  function getHookConfigs() {
@@ -3746,6 +3788,7 @@ var CURSOR_HOOKS_PATH = path13.join(".cursor", "hooks.json");
3746
3788
  var CURSOR_HOOK_EVENTS = [
3747
3789
  { event: "postToolUse", tail: "learn observe" },
3748
3790
  { event: "postToolUseFailure", tail: "learn observe --failure" },
3791
+ { event: "userPromptSubmit", tail: "learn observe --prompt" },
3749
3792
  { event: "sessionEnd", tail: "learn finalize" }
3750
3793
  ];
3751
3794
  function readCursorHooks() {
@@ -5995,6 +6038,51 @@ function extractTopDeps() {
5995
6038
  return [];
5996
6039
  }
5997
6040
  }
6041
+ async function searchSkills(fingerprint, targetPlatforms, onStatus) {
6042
+ const installedSkills = getInstalledSkills(targetPlatforms);
6043
+ const technologies = [...new Set([
6044
+ ...fingerprint.languages,
6045
+ ...fingerprint.frameworks,
6046
+ ...extractTopDeps()
6047
+ ].filter(Boolean))];
6048
+ if (technologies.length === 0) {
6049
+ return { results: [], contentMap: /* @__PURE__ */ new Map() };
6050
+ }
6051
+ const primaryPlatform = targetPlatforms.includes("claude") ? "claude" : targetPlatforms[0];
6052
+ onStatus?.("Searching skill registries...");
6053
+ const allCandidates = await searchAllProviders(technologies, primaryPlatform);
6054
+ if (!allCandidates.length) {
6055
+ return { results: [], contentMap: /* @__PURE__ */ new Map() };
6056
+ }
6057
+ const newCandidates = allCandidates.filter((c) => !installedSkills.has(c.slug.toLowerCase()));
6058
+ if (!newCandidates.length) {
6059
+ return { results: [], contentMap: /* @__PURE__ */ new Map() };
6060
+ }
6061
+ onStatus?.(`Scoring ${newCandidates.length} candidates...`);
6062
+ let results;
6063
+ const config = loadConfig();
6064
+ if (config) {
6065
+ try {
6066
+ const projectContext = buildProjectContext(fingerprint, targetPlatforms);
6067
+ results = await scoreWithLLM(newCandidates, projectContext, technologies);
6068
+ } catch {
6069
+ results = newCandidates.slice(0, 20);
6070
+ }
6071
+ } else {
6072
+ results = newCandidates.slice(0, 20);
6073
+ }
6074
+ if (results.length === 0) {
6075
+ return { results: [], contentMap: /* @__PURE__ */ new Map() };
6076
+ }
6077
+ onStatus?.("Fetching skill content...");
6078
+ const contentMap = /* @__PURE__ */ new Map();
6079
+ await Promise.all(results.map(async (rec) => {
6080
+ const content = await fetchSkillContent(rec);
6081
+ if (content) contentMap.set(rec.slug, content);
6082
+ }));
6083
+ const available = results.filter((r) => contentMap.has(r.slug));
6084
+ return { results: available, contentMap };
6085
+ }
5998
6086
  async function recommendCommand() {
5999
6087
  const proceed = await select4({
6000
6088
  message: "Search public repos for relevant skills to add to this project?",
@@ -6359,13 +6447,144 @@ function formatMs(ms) {
6359
6447
  return `${secs}s`;
6360
6448
  }
6361
6449
 
6450
+ // src/utils/parallel-tasks.ts
6451
+ import chalk9 from "chalk";
6452
+
6453
+ // node_modules/ansi-regex/index.js
6454
+ function ansiRegex({ onlyFirst = false } = {}) {
6455
+ const ST = "(?:\\u0007|\\u001B\\u005C|\\u009C)";
6456
+ const osc = `(?:\\u001B\\][\\s\\S]*?${ST})`;
6457
+ const csi = "[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]";
6458
+ const pattern = `${osc}|${csi}`;
6459
+ return new RegExp(pattern, onlyFirst ? void 0 : "g");
6460
+ }
6461
+
6462
+ // node_modules/strip-ansi/index.js
6463
+ var regex = ansiRegex();
6464
+ function stripAnsi(string) {
6465
+ if (typeof string !== "string") {
6466
+ throw new TypeError(`Expected a \`string\`, got \`${typeof string}\``);
6467
+ }
6468
+ if (!string.includes("\x1B") && !string.includes("\x9B")) {
6469
+ return string;
6470
+ }
6471
+ return string.replace(regex, "");
6472
+ }
6473
+
6474
+ // src/utils/parallel-tasks.ts
6475
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
6476
+ var SPINNER_INTERVAL_MS = 80;
6477
+ var ParallelTaskDisplay = class {
6478
+ tasks = [];
6479
+ lineCount = 0;
6480
+ spinnerFrame = 0;
6481
+ timer = null;
6482
+ startTime = 0;
6483
+ rendered = false;
6484
+ add(name) {
6485
+ const index = this.tasks.length;
6486
+ this.tasks.push({ name, status: "pending", message: "" });
6487
+ return index;
6488
+ }
6489
+ start() {
6490
+ this.startTime = Date.now();
6491
+ this.draw(true);
6492
+ this.timer = setInterval(() => {
6493
+ this.spinnerFrame = (this.spinnerFrame + 1) % SPINNER_FRAMES.length;
6494
+ this.draw(false);
6495
+ }, SPINNER_INTERVAL_MS);
6496
+ }
6497
+ update(index, status, message) {
6498
+ const task = this.tasks[index];
6499
+ if (!task) return;
6500
+ if (status === "running" && task.status === "pending") {
6501
+ task.startTime = Date.now();
6502
+ }
6503
+ if ((status === "done" || status === "failed") && !task.endTime) {
6504
+ task.endTime = Date.now();
6505
+ }
6506
+ task.status = status;
6507
+ if (message !== void 0) task.message = message;
6508
+ }
6509
+ stop() {
6510
+ if (this.timer) {
6511
+ clearInterval(this.timer);
6512
+ this.timer = null;
6513
+ }
6514
+ this.draw(false);
6515
+ }
6516
+ formatTime(ms) {
6517
+ const secs = Math.floor(ms / 1e3);
6518
+ if (secs < 60) return `${secs}s`;
6519
+ return `${Math.floor(secs / 60)}m ${secs % 60}s`;
6520
+ }
6521
+ truncate(text, maxVisible) {
6522
+ const plain = stripAnsi(text);
6523
+ if (plain.length <= maxVisible) return text;
6524
+ let visible = 0;
6525
+ let i = 0;
6526
+ while (i < text.length && visible < maxVisible - 3) {
6527
+ if (text[i] === "\x1B") {
6528
+ const end = text.indexOf("m", i);
6529
+ if (end !== -1) {
6530
+ i = end + 1;
6531
+ continue;
6532
+ }
6533
+ }
6534
+ visible++;
6535
+ i++;
6536
+ }
6537
+ return text.slice(0, i) + "...";
6538
+ }
6539
+ renderLine(task) {
6540
+ const maxWidth = process.stdout.columns || 80;
6541
+ const elapsed = task.startTime ? this.formatTime((task.endTime ?? Date.now()) - task.startTime) : "";
6542
+ let line;
6543
+ switch (task.status) {
6544
+ case "pending":
6545
+ line = ` ${chalk9.dim("\u25CB")} ${chalk9.dim(task.name)}${task.message ? chalk9.dim(` \u2014 ${task.message}`) : ""}`;
6546
+ break;
6547
+ case "running": {
6548
+ const spinner = chalk9.cyan(SPINNER_FRAMES[this.spinnerFrame]);
6549
+ const time = elapsed ? chalk9.dim(` (${elapsed})`) : "";
6550
+ line = ` ${spinner} ${task.name}${task.message ? chalk9.dim(` \u2014 ${task.message}`) : ""}${time}`;
6551
+ break;
6552
+ }
6553
+ case "done": {
6554
+ const time = elapsed ? chalk9.dim(` (${elapsed})`) : "";
6555
+ line = ` ${chalk9.green("\u2713")} ${task.name}${task.message ? chalk9.dim(` \u2014 ${task.message}`) : ""}${time}`;
6556
+ break;
6557
+ }
6558
+ case "failed":
6559
+ line = ` ${chalk9.red("\u2717")} ${task.name}${task.message ? chalk9.red(` \u2014 ${task.message}`) : ""}`;
6560
+ break;
6561
+ }
6562
+ return this.truncate(line, maxWidth - 1);
6563
+ }
6564
+ draw(initial) {
6565
+ const { stdout } = process;
6566
+ if (!initial && this.rendered && this.lineCount > 0) {
6567
+ stdout.write(`\x1B[${this.lineCount}A`);
6568
+ }
6569
+ stdout.write("\x1B[0J");
6570
+ const lines = this.tasks.map((t) => this.renderLine(t));
6571
+ const totalElapsed = this.formatTime(Date.now() - this.startTime);
6572
+ lines.push(chalk9.dim(`
6573
+ Total: ${totalElapsed}`));
6574
+ const output = lines.join("\n");
6575
+ stdout.write(output + "\n");
6576
+ this.lineCount = output.split("\n").length;
6577
+ this.rendered = true;
6578
+ }
6579
+ };
6580
+
6362
6581
  // src/commands/init.ts
6363
6582
  function log(verbose, ...args) {
6364
- if (verbose) console.log(chalk9.dim(` [verbose] ${args.map(String).join(" ")}`));
6583
+ if (verbose) console.log(chalk10.dim(` [verbose] ${args.map(String).join(" ")}`));
6365
6584
  }
6366
6585
  async function initCommand(options) {
6367
- const brand = chalk9.hex("#EB9D83");
6368
- const title = chalk9.hex("#83D1EB");
6586
+ const brand = chalk10.hex("#EB9D83");
6587
+ const title = chalk10.hex("#83D1EB");
6369
6588
  console.log(brand.bold(`
6370
6589
  \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
6371
6590
  \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
@@ -6374,34 +6593,33 @@ async function initCommand(options) {
6374
6593
  \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
6375
6594
  \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
6376
6595
  `));
6377
- console.log(chalk9.dim(" Scan your project and generate tailored config files for"));
6378
- console.log(chalk9.dim(" Claude Code, Cursor, and Codex.\n"));
6596
+ console.log(chalk10.dim(" Scan your project and generate tailored config files for"));
6597
+ console.log(chalk10.dim(" Claude Code, Cursor, and Codex.\n"));
6379
6598
  const report = options.debugReport ? new DebugReport() : null;
6380
6599
  console.log(title.bold(" How it works:\n"));
6381
- console.log(chalk9.dim(" 1. Connect Choose your LLM provider"));
6382
- console.log(chalk9.dim(" 2. Discover Scan code, dependencies, and existing configs"));
6383
- console.log(chalk9.dim(" 3. Generate Build CLAUDE.md, rules, and skills for your codebase"));
6384
- console.log(chalk9.dim(" 4. Review See a diff \u2014 accept, refine via chat, or decline"));
6385
- console.log(chalk9.dim(" 5. Skills Search community registries and install skills\n"));
6386
- console.log(title.bold(" Step 1/5 \u2014 Connect\n"));
6600
+ console.log(chalk10.dim(" 1. Setup Connect your LLM provider and select your agents"));
6601
+ console.log(chalk10.dim(" 2. Engine Detect stack, generate configs & skills in parallel"));
6602
+ console.log(chalk10.dim(" 3. Review See all changes \u2014 accept, refine, or decline"));
6603
+ console.log(chalk10.dim(" 4. Finalize Score check and auto-sync hooks\n"));
6604
+ console.log(title.bold(" Step 1/4 \u2014 Setup\n"));
6387
6605
  let config = loadConfig();
6388
6606
  if (!config) {
6389
- console.log(chalk9.dim(" No LLM provider set yet. Choose how to run Caliber:\n"));
6607
+ console.log(chalk10.dim(" No LLM provider configured yet.\n"));
6390
6608
  await runInteractiveProviderSetup({
6391
6609
  selectMessage: "How do you want to use Caliber? (choose LLM provider)"
6392
6610
  });
6393
6611
  config = loadConfig();
6394
6612
  if (!config) {
6395
- console.log(chalk9.red(" Setup was cancelled or failed.\n"));
6613
+ console.log(chalk10.red(" Setup was cancelled or failed.\n"));
6396
6614
  throw new Error("__exit__");
6397
6615
  }
6398
- console.log(chalk9.green(" \u2713 Provider saved. Let's continue.\n"));
6616
+ console.log(chalk10.green(" \u2713 Provider saved\n"));
6399
6617
  }
6400
6618
  trackInitProviderSelected(config.provider, config.model);
6401
6619
  const displayModel = getDisplayModel(config);
6402
6620
  const fastModel = getFastModel();
6403
6621
  const modelLine = fastModel ? ` Provider: ${config.provider} | Model: ${displayModel} | Scan: ${fastModel}` : ` Provider: ${config.provider} | Model: ${displayModel}`;
6404
- console.log(chalk9.dim(modelLine + "\n"));
6622
+ console.log(chalk10.dim(modelLine + "\n"));
6405
6623
  if (report) {
6406
6624
  report.markStep("Provider setup");
6407
6625
  report.addSection("LLM Provider", `- **Provider**: ${config.provider}
@@ -6409,30 +6627,6 @@ async function initCommand(options) {
6409
6627
  - **Fast model**: ${fastModel || "none"}`);
6410
6628
  }
6411
6629
  await validateModel({ fast: true });
6412
- console.log(title.bold(" Step 2/5 \u2014 Discover\n"));
6413
- const spinner = ora2("Scanning code, dependencies, and existing configs...").start();
6414
- const fingerprint = await collectFingerprint(process.cwd());
6415
- spinner.succeed("Project scanned");
6416
- log(options.verbose, `Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`);
6417
- if (options.verbose && fingerprint.codeAnalysis) {
6418
- log(options.verbose, `Code analysis: ${fingerprint.codeAnalysis.filesIncluded}/${fingerprint.codeAnalysis.filesAnalyzed} files, ~${fingerprint.codeAnalysis.includedTokens.toLocaleString()} tokens, ${fingerprint.codeAnalysis.duplicateGroups} dedup groups`);
6419
- }
6420
- trackInitProjectDiscovered(fingerprint.languages.length, fingerprint.frameworks.length, fingerprint.fileTree.length);
6421
- const langDisplay = fingerprint.languages.join(", ") || "none detected";
6422
- const frameworkDisplay = fingerprint.frameworks.length > 0 ? ` (${fingerprint.frameworks.join(", ")})` : "";
6423
- console.log(chalk9.dim(` Languages: ${langDisplay}${frameworkDisplay}`));
6424
- console.log(chalk9.dim(` Files: ${fingerprint.fileTree.length} found
6425
- `));
6426
- if (report) {
6427
- report.markStep("Fingerprint");
6428
- report.addJson("Fingerprint: Git", { remote: fingerprint.gitRemoteUrl, packageName: fingerprint.packageName });
6429
- report.addCodeBlock("Fingerprint: File Tree", fingerprint.fileTree.join("\n"));
6430
- report.addJson("Fingerprint: Detected Stack", { languages: fingerprint.languages, frameworks: fingerprint.frameworks, tools: fingerprint.tools });
6431
- report.addJson("Fingerprint: Existing Configs", fingerprint.existingConfigs);
6432
- if (fingerprint.codeAnalysis) {
6433
- report.addJson("Fingerprint: Code Analysis", fingerprint.codeAnalysis);
6434
- }
6435
- }
6436
6630
  let targetAgent;
6437
6631
  if (options.agent) {
6438
6632
  targetAgent = options.agent;
@@ -6442,30 +6636,27 @@ async function initCommand(options) {
6442
6636
  } else {
6443
6637
  targetAgent = await promptAgent();
6444
6638
  }
6445
- console.log(chalk9.dim(` Target: ${targetAgent.join(", ")}
6639
+ console.log(chalk10.dim(` Target: ${targetAgent.join(", ")}
6446
6640
  `));
6447
6641
  trackInitAgentSelected(targetAgent);
6448
- const preScore = computeLocalScore(process.cwd(), targetAgent);
6449
- const failingForDismissal = preScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
6450
- if (failingForDismissal.length > 0) {
6451
- const newDismissals = await evaluateDismissals(failingForDismissal, fingerprint);
6452
- if (newDismissals.length > 0) {
6453
- const existing = readDismissedChecks();
6454
- const existingIds = new Set(existing.map((d) => d.id));
6455
- const merged = [...existing, ...newDismissals.filter((d) => !existingIds.has(d.id))];
6456
- writeDismissedChecks(merged);
6457
- }
6642
+ let wantsSkills = false;
6643
+ if (!options.autoApprove) {
6644
+ wantsSkills = await select5({
6645
+ message: "Discover community-maintained skills that match your stack?",
6646
+ choices: [
6647
+ { name: "Yes, find skills for my project", value: true },
6648
+ { name: "Skip for now", value: false }
6649
+ ]
6650
+ });
6458
6651
  }
6459
- const baselineScore = computeLocalScore(process.cwd(), targetAgent);
6460
- console.log(chalk9.dim(" Current setup score:"));
6652
+ let baselineScore = computeLocalScore(process.cwd(), targetAgent);
6653
+ console.log(chalk10.dim("\n Current setup score:"));
6461
6654
  displayScoreSummary(baselineScore);
6462
6655
  if (options.verbose) {
6463
6656
  for (const c of baselineScore.checks) {
6464
6657
  log(options.verbose, ` ${c.passed ? "\u2713" : "\u2717"} ${c.name}: ${c.earnedPoints}/${c.maxPoints}${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`);
6465
6658
  }
6466
6659
  }
6467
- const passingCount = baselineScore.checks.filter((c) => c.passed).length;
6468
- const failingCount = baselineScore.checks.filter((c) => !c.passed).length;
6469
6660
  if (report) {
6470
6661
  report.markStep("Baseline scoring");
6471
6662
  report.addSection("Scoring: Baseline", `**Score**: ${baselineScore.score}/100
@@ -6475,7 +6666,7 @@ async function initCommand(options) {
6475
6666
  ` + baselineScore.checks.map((c) => `| ${c.name} | ${c.passed ? "Yes" : "No"} | ${c.earnedPoints} | ${c.maxPoints} |`).join("\n"));
6476
6667
  report.addSection("Generation: Target Agents", targetAgent.join(", "));
6477
6668
  }
6478
- const hasExistingConfig = !!(fingerprint.existingConfigs.claudeMd || fingerprint.existingConfigs.claudeSettings || fingerprint.existingConfigs.claudeSkills?.length || fingerprint.existingConfigs.cursorrules || fingerprint.existingConfigs.cursorRules?.length || fingerprint.existingConfigs.agentsMd);
6669
+ const hasExistingConfig = !!(baselineScore.checks.some((c) => c.id === "claude_md_exists" && c.passed) || baselineScore.checks.some((c) => c.id === "cursorrules_exists" && c.passed));
6479
6670
  const NON_LLM_CHECKS = /* @__PURE__ */ new Set([
6480
6671
  "hooks_configured",
6481
6672
  "agents_md_exists",
@@ -6484,111 +6675,175 @@ async function initCommand(options) {
6484
6675
  "service_coverage",
6485
6676
  "mcp_completeness"
6486
6677
  ]);
6678
+ const passingCount = baselineScore.checks.filter((c) => c.passed).length;
6679
+ const failingCount = baselineScore.checks.filter((c) => !c.passed).length;
6487
6680
  if (hasExistingConfig && baselineScore.score === 100) {
6488
6681
  trackInitScoreComputed(baselineScore.score, passingCount, failingCount, true);
6489
- console.log(chalk9.bold.green(" Your setup is already optimal \u2014 nothing to change.\n"));
6490
- console.log(chalk9.dim(" Run ") + chalk9.hex("#83D1EB")("caliber init --force") + chalk9.dim(" to regenerate anyway.\n"));
6682
+ console.log(chalk10.bold.green(" Your setup is already optimal \u2014 nothing to change.\n"));
6683
+ console.log(chalk10.dim(" Run ") + chalk10.hex("#83D1EB")("caliber init --force") + chalk10.dim(" to regenerate anyway.\n"));
6491
6684
  if (!options.force) return;
6492
6685
  }
6493
6686
  const allFailingChecks = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
6494
6687
  const llmFixableChecks = allFailingChecks.filter((c) => !NON_LLM_CHECKS.has(c.id));
6495
6688
  trackInitScoreComputed(baselineScore.score, passingCount, failingCount, false);
6496
6689
  if (hasExistingConfig && llmFixableChecks.length === 0 && allFailingChecks.length > 0 && !options.force) {
6497
- console.log(chalk9.bold.green("\n Your config is fully optimized for LLM generation.\n"));
6498
- console.log(chalk9.dim(" Remaining items need CLI actions:\n"));
6690
+ console.log(chalk10.bold.green("\n Your config is fully optimized for LLM generation.\n"));
6691
+ console.log(chalk10.dim(" Remaining items need CLI actions:\n"));
6499
6692
  for (const check of allFailingChecks) {
6500
- console.log(chalk9.dim(` \u2022 ${check.name}`));
6693
+ console.log(chalk10.dim(` \u2022 ${check.name}`));
6501
6694
  if (check.suggestion) {
6502
- console.log(` ${chalk9.hex("#83D1EB")(check.suggestion)}`);
6695
+ console.log(` ${chalk10.hex("#83D1EB")(check.suggestion)}`);
6503
6696
  }
6504
6697
  }
6505
6698
  console.log("");
6506
- console.log(chalk9.dim(" Run ") + chalk9.hex("#83D1EB")("caliber init --force") + chalk9.dim(" to regenerate anyway.\n"));
6699
+ console.log(chalk10.dim(" Run ") + chalk10.hex("#83D1EB")("caliber init --force") + chalk10.dim(" to regenerate anyway.\n"));
6507
6700
  return;
6508
6701
  }
6702
+ console.log(title.bold(" Step 2/4 \u2014 Engine\n"));
6703
+ const genModelInfo = fastModel ? ` Using ${displayModel} for docs, ${fastModel} for skills` : ` Using ${displayModel}`;
6704
+ console.log(chalk10.dim(genModelInfo + "\n"));
6705
+ if (report) report.markStep("Generation");
6706
+ trackInitGenerationStarted(false);
6707
+ const genStartTime = Date.now();
6708
+ let generatedSetup = null;
6709
+ let rawOutput;
6710
+ let genStopReason;
6711
+ let skillSearchResult = { results: [], contentMap: /* @__PURE__ */ new Map() };
6712
+ let fingerprint;
6713
+ const fpSpinner = ora2("Scanning project...").start();
6714
+ try {
6715
+ fingerprint = await collectFingerprint(process.cwd());
6716
+ fpSpinner.succeed(`Stack detected \u2014 ${fingerprint.languages.join(", ") || "no languages"}${fingerprint.frameworks.length > 0 ? `, ${fingerprint.frameworks.join(", ")}` : ""}`);
6717
+ } catch (err) {
6718
+ fpSpinner.fail("Failed to scan project");
6719
+ throw new Error("__exit__");
6720
+ }
6721
+ trackInitProjectDiscovered(fingerprint.languages.length, fingerprint.frameworks.length, fingerprint.fileTree.length);
6722
+ log(options.verbose, `Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`);
6723
+ if (report) {
6724
+ report.addJson("Fingerprint: Git", { remote: fingerprint.gitRemoteUrl, packageName: fingerprint.packageName });
6725
+ report.addCodeBlock("Fingerprint: File Tree", fingerprint.fileTree.join("\n"));
6726
+ report.addJson("Fingerprint: Detected Stack", { languages: fingerprint.languages, frameworks: fingerprint.frameworks, tools: fingerprint.tools });
6727
+ report.addJson("Fingerprint: Existing Configs", fingerprint.existingConfigs);
6728
+ if (fingerprint.codeAnalysis) report.addJson("Fingerprint: Code Analysis", fingerprint.codeAnalysis);
6729
+ }
6509
6730
  const isEmpty = fingerprint.fileTree.length < 3;
6510
6731
  if (isEmpty) {
6511
6732
  fingerprint.description = await promptInput("What will you build in this project?");
6512
6733
  }
6734
+ const failingForDismissal = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
6735
+ if (failingForDismissal.length > 0) {
6736
+ const newDismissals = await evaluateDismissals(failingForDismissal, fingerprint);
6737
+ if (newDismissals.length > 0) {
6738
+ const existing = readDismissedChecks();
6739
+ const existingIds = new Set(existing.map((d) => d.id));
6740
+ const merged = [...existing, ...newDismissals.filter((d) => !existingIds.has(d.id))];
6741
+ writeDismissedChecks(merged);
6742
+ baselineScore = computeLocalScore(process.cwd(), targetAgent);
6743
+ }
6744
+ }
6513
6745
  let failingChecks;
6514
6746
  let passingChecks;
6515
6747
  let currentScore;
6516
6748
  if (hasExistingConfig && baselineScore.score >= 95 && !options.force) {
6517
- failingChecks = llmFixableChecks.map((c) => ({ name: c.name, suggestion: c.suggestion, fix: c.fix }));
6749
+ const currentLlmFixable = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0 && !NON_LLM_CHECKS.has(c.id));
6750
+ failingChecks = currentLlmFixable.map((c) => ({ name: c.name, suggestion: c.suggestion, fix: c.fix }));
6518
6751
  passingChecks = baselineScore.checks.filter((c) => c.passed).map((c) => ({ name: c.name }));
6519
6752
  currentScore = baselineScore.score;
6520
- if (failingChecks.length > 0) {
6521
- console.log(title.bold(" Step 3/5 \u2014 Generate\n"));
6522
- console.log(chalk9.dim(` Score is ${baselineScore.score}/100 \u2014 fine-tuning ${failingChecks.length} remaining issue${failingChecks.length === 1 ? "" : "s"}:
6523
- `));
6524
- for (const check of failingChecks) {
6525
- console.log(chalk9.dim(` \u2022 ${check.name}`));
6526
- }
6527
- console.log("");
6528
- }
6529
- } else if (hasExistingConfig) {
6530
- console.log(title.bold(" Step 3/5 \u2014 Generate\n"));
6531
- console.log(chalk9.dim(" Auditing your existing configs against your codebase and improving them.\n"));
6532
- } else {
6533
- console.log(title.bold(" Step 3/5 \u2014 Generate\n"));
6534
- console.log(chalk9.dim(" Building CLAUDE.md, rules, and skills tailored to your project.\n"));
6535
6753
  }
6536
- const genModelInfo = fastModel ? ` Using ${displayModel} for docs, ${fastModel} for skills` : ` Using ${displayModel}`;
6537
- console.log(chalk9.dim(genModelInfo));
6538
- console.log(chalk9.dim(" This can take a couple of minutes depending on your model and provider.\n"));
6539
6754
  if (report) {
6540
- report.markStep("Generation");
6541
6755
  const fullPrompt = buildGeneratePrompt(fingerprint, targetAgent, fingerprint.description, failingChecks, currentScore, passingChecks);
6542
6756
  report.addCodeBlock("Generation: Full LLM Prompt", fullPrompt);
6543
6757
  }
6544
- trackInitGenerationStarted(!!failingChecks);
6545
- const genStartTime = Date.now();
6546
- const genSpinner = ora2("Generating setup...").start();
6547
- const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, { showElapsedTime: true });
6548
- genMessages.start();
6549
- let generatedSetup = null;
6550
- let rawOutput;
6551
- let genStopReason;
6758
+ const display = new ParallelTaskDisplay();
6759
+ const TASK_CONFIG = display.add("Generating configs");
6760
+ const TASK_SKILLS_GEN = display.add("Generating skills");
6761
+ const TASK_SKILLS_SEARCH = wantsSkills ? display.add("Searching community skills") : -1;
6762
+ display.start();
6552
6763
  try {
6553
- const result = await generateSetup(
6554
- fingerprint,
6555
- targetAgent,
6556
- fingerprint.description,
6557
- {
6558
- onStatus: (status) => {
6559
- genMessages.handleServerStatus(status);
6560
- },
6561
- onComplete: (setup) => {
6562
- generatedSetup = setup;
6764
+ display.update(TASK_CONFIG, "running");
6765
+ const generatePromise = (async () => {
6766
+ const result = await generateSetup(
6767
+ fingerprint,
6768
+ targetAgent,
6769
+ fingerprint.description,
6770
+ {
6771
+ onStatus: (status) => display.update(TASK_CONFIG, "running", status),
6772
+ onComplete: (setup) => {
6773
+ generatedSetup = setup;
6774
+ },
6775
+ onError: (error) => display.update(TASK_CONFIG, "failed", error)
6563
6776
  },
6564
- onError: (error) => {
6565
- genMessages.stop();
6566
- genSpinner.fail(`Generation error: ${error}`);
6567
- }
6568
- },
6569
- failingChecks,
6570
- currentScore,
6571
- passingChecks
6572
- );
6573
- if (!generatedSetup) {
6574
- generatedSetup = result.setup;
6777
+ failingChecks,
6778
+ currentScore,
6779
+ passingChecks,
6780
+ { skipSkills: true }
6781
+ );
6782
+ if (!generatedSetup) generatedSetup = result.setup;
6575
6783
  rawOutput = result.raw;
6576
- }
6577
- genStopReason = result.stopReason;
6784
+ genStopReason = result.stopReason;
6785
+ if (!generatedSetup) {
6786
+ display.update(TASK_CONFIG, "failed", "Could not parse LLM response");
6787
+ display.update(TASK_SKILLS_GEN, "failed", "Skipped");
6788
+ return;
6789
+ }
6790
+ display.update(TASK_CONFIG, "done");
6791
+ display.update(TASK_SKILLS_GEN, "running");
6792
+ const skillCount = await generateSkillsForSetup(
6793
+ generatedSetup,
6794
+ fingerprint,
6795
+ targetAgent,
6796
+ (status) => display.update(TASK_SKILLS_GEN, "running", status)
6797
+ );
6798
+ display.update(TASK_SKILLS_GEN, "done", `${skillCount} skills`);
6799
+ })();
6800
+ const SEARCH_TIMEOUT_MS = 6e4;
6801
+ const searchPromise = wantsSkills ? (async () => {
6802
+ display.update(TASK_SKILLS_SEARCH, "running");
6803
+ try {
6804
+ const searchWithTimeout = Promise.race([
6805
+ searchSkills(
6806
+ fingerprint,
6807
+ targetAgent,
6808
+ (status) => display.update(TASK_SKILLS_SEARCH, "running", status)
6809
+ ),
6810
+ new Promise(
6811
+ (_, reject) => setTimeout(() => reject(new Error("timeout")), SEARCH_TIMEOUT_MS)
6812
+ )
6813
+ ]);
6814
+ skillSearchResult = await searchWithTimeout;
6815
+ const count = skillSearchResult.results.length;
6816
+ display.update(TASK_SKILLS_SEARCH, "done", count > 0 ? `${count} skills found` : "No matches");
6817
+ } catch (err) {
6818
+ const reason = err instanceof Error && err.message === "timeout" ? "Timed out" : "Search failed";
6819
+ display.update(TASK_SKILLS_SEARCH, "failed", reason);
6820
+ }
6821
+ })() : Promise.resolve();
6822
+ await Promise.all([generatePromise, searchPromise]);
6578
6823
  } catch (err) {
6579
- genMessages.stop();
6824
+ display.stop();
6580
6825
  const msg = err instanceof Error ? err.message : "Unknown error";
6581
- genSpinner.fail(`Generation failed: ${msg}`);
6826
+ console.log(chalk10.red(`
6827
+ Engine failed: ${msg}
6828
+ `));
6582
6829
  writeErrorLog(config, void 0, msg, "exception");
6583
6830
  throw new Error("__exit__");
6584
6831
  }
6585
- genMessages.stop();
6832
+ display.stop();
6833
+ const elapsedMs = Date.now() - genStartTime;
6834
+ trackInitGenerationCompleted(elapsedMs, 0);
6835
+ const mins = Math.floor(elapsedMs / 6e4);
6836
+ const secs = Math.floor(elapsedMs % 6e4 / 1e3);
6837
+ const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
6838
+ console.log(chalk10.dim(`
6839
+ Completed in ${timeStr}
6840
+ `));
6586
6841
  if (!generatedSetup) {
6587
- genSpinner.fail("Failed to generate setup.");
6842
+ console.log(chalk10.red(" Failed to generate setup."));
6588
6843
  writeErrorLog(config, rawOutput, void 0, genStopReason);
6589
6844
  if (rawOutput) {
6590
- console.log(chalk9.dim("\nRaw LLM output (JSON parse failed):"));
6591
- console.log(chalk9.dim(rawOutput.slice(0, 500)));
6845
+ console.log(chalk10.dim("\nRaw LLM output (JSON parse failed):"));
6846
+ console.log(chalk10.dim(rawOutput.slice(0, 500)));
6592
6847
  }
6593
6848
  throw new Error("__exit__");
6594
6849
  }
@@ -6596,12 +6851,6 @@ async function initCommand(options) {
6596
6851
  if (rawOutput) report.addCodeBlock("Generation: Raw LLM Response", rawOutput);
6597
6852
  report.addJson("Generation: Parsed Setup", generatedSetup);
6598
6853
  }
6599
- const elapsedMs = Date.now() - genStartTime;
6600
- trackInitGenerationCompleted(elapsedMs, 0);
6601
- const mins = Math.floor(elapsedMs / 6e4);
6602
- const secs = Math.floor(elapsedMs % 6e4 / 1e3);
6603
- const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
6604
- genSpinner.succeed(`Setup generated ${chalk9.dim(`in ${timeStr}`)}`);
6605
6854
  log(options.verbose, `Generation completed: ${elapsedMs}ms, stopReason: ${genStopReason || "end_turn"}`);
6606
6855
  printSetupSummary(generatedSetup);
6607
6856
  const sessionHistory = [];
@@ -6609,15 +6858,20 @@ async function initCommand(options) {
6609
6858
  role: "assistant",
6610
6859
  content: summarizeSetup("Initial generation", generatedSetup)
6611
6860
  });
6612
- console.log(title.bold(" Step 4/5 \u2014 Review\n"));
6861
+ console.log(title.bold(" Step 3/4 \u2014 Review\n"));
6613
6862
  const setupFiles = collectSetupFiles(generatedSetup, targetAgent);
6614
6863
  const staged = stageFiles(setupFiles, process.cwd());
6615
6864
  const totalChanges = staged.newFiles + staged.modifiedFiles;
6616
- console.log(chalk9.dim(` ${chalk9.green(`${staged.newFiles} new`)} / ${chalk9.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}
6865
+ console.log(chalk10.dim(` ${chalk10.green(`${staged.newFiles} new`)} / ${chalk10.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}`));
6866
+ if (skillSearchResult.results.length > 0) {
6867
+ console.log(chalk10.dim(` ${chalk10.cyan(`${skillSearchResult.results.length}`)} community skills available to install
6617
6868
  `));
6869
+ } else {
6870
+ console.log("");
6871
+ }
6618
6872
  let action;
6619
- if (totalChanges === 0) {
6620
- console.log(chalk9.dim(" No changes needed \u2014 your configs are already up to date.\n"));
6873
+ if (totalChanges === 0 && skillSearchResult.results.length === 0) {
6874
+ console.log(chalk10.dim(" No changes needed \u2014 your configs are already up to date.\n"));
6621
6875
  cleanupStaging();
6622
6876
  action = "accept";
6623
6877
  } else if (options.autoApprove) {
@@ -6625,13 +6879,15 @@ async function initCommand(options) {
6625
6879
  action = "accept";
6626
6880
  trackInitReviewAction(action, "auto-approved");
6627
6881
  } else {
6628
- const wantsReview = await promptWantsReview();
6629
- if (wantsReview) {
6630
- const reviewMethod = await promptReviewMethod();
6631
- await openReview(reviewMethod, staged.stagedFiles);
6882
+ if (totalChanges > 0) {
6883
+ const wantsReview = await promptWantsReview();
6884
+ if (wantsReview) {
6885
+ const reviewMethod = await promptReviewMethod();
6886
+ await openReview(reviewMethod, staged.stagedFiles);
6887
+ }
6632
6888
  }
6633
6889
  action = await promptReviewAction();
6634
- trackInitReviewAction(action, wantsReview ? "reviewed" : "skipped");
6890
+ trackInitReviewAction(action, totalChanges > 0 ? "reviewed" : "skipped");
6635
6891
  }
6636
6892
  let refinementRound = 0;
6637
6893
  while (action === "refine") {
@@ -6640,12 +6896,12 @@ async function initCommand(options) {
6640
6896
  trackInitRefinementRound(refinementRound, !!generatedSetup);
6641
6897
  if (!generatedSetup) {
6642
6898
  cleanupStaging();
6643
- console.log(chalk9.dim("Refinement cancelled. No files were modified."));
6899
+ console.log(chalk10.dim("Refinement cancelled. No files were modified."));
6644
6900
  return;
6645
6901
  }
6646
6902
  const updatedFiles = collectSetupFiles(generatedSetup, targetAgent);
6647
6903
  const restaged = stageFiles(updatedFiles, process.cwd());
6648
- console.log(chalk9.dim(` ${chalk9.green(`${restaged.newFiles} new`)} / ${chalk9.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
6904
+ console.log(chalk10.dim(` ${chalk10.green(`${restaged.newFiles} new`)} / ${chalk10.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
6649
6905
  `));
6650
6906
  printSetupSummary(generatedSetup);
6651
6907
  await openReview("terminal", restaged.stagedFiles);
@@ -6654,11 +6910,12 @@ async function initCommand(options) {
6654
6910
  }
6655
6911
  cleanupStaging();
6656
6912
  if (action === "decline") {
6657
- console.log(chalk9.dim("Setup declined. No files were modified."));
6913
+ console.log(chalk10.dim("Setup declined. No files were modified."));
6658
6914
  return;
6659
6915
  }
6916
+ console.log(title.bold("\n Step 4/4 \u2014 Finalize\n"));
6660
6917
  if (options.dryRun) {
6661
- console.log(chalk9.yellow("\n[Dry run] Would write the following files:"));
6918
+ console.log(chalk10.yellow("\n[Dry run] Would write the following files:"));
6662
6919
  console.log(JSON.stringify(generatedSetup, null, 2));
6663
6920
  return;
6664
6921
  }
@@ -6687,85 +6944,45 @@ ${agentRefs.join(" ")}
6687
6944
  0,
6688
6945
  result.deleted.length
6689
6946
  );
6690
- console.log(chalk9.bold("\nFiles created/updated:"));
6947
+ console.log(chalk10.bold("\nFiles created/updated:"));
6691
6948
  for (const file of result.written) {
6692
- console.log(` ${chalk9.green("\u2713")} ${file}`);
6949
+ console.log(` ${chalk10.green("\u2713")} ${file}`);
6693
6950
  }
6694
6951
  if (result.deleted.length > 0) {
6695
- console.log(chalk9.bold("\nFiles removed:"));
6952
+ console.log(chalk10.bold("\nFiles removed:"));
6696
6953
  for (const file of result.deleted) {
6697
- console.log(` ${chalk9.red("\u2717")} ${file}`);
6954
+ console.log(` ${chalk10.red("\u2717")} ${file}`);
6698
6955
  }
6699
6956
  }
6700
6957
  if (result.backupDir) {
6701
- console.log(chalk9.dim(`
6958
+ console.log(chalk10.dim(`
6702
6959
  Backups saved to ${result.backupDir}`));
6703
6960
  }
6704
6961
  } catch (err) {
6705
6962
  writeSpinner.fail("Failed to write files");
6706
- console.error(chalk9.red(err instanceof Error ? err.message : "Unknown error"));
6963
+ console.error(chalk10.red(err instanceof Error ? err.message : "Unknown error"));
6707
6964
  throw new Error("__exit__");
6708
6965
  }
6709
- ensurePermissions(fingerprint);
6966
+ if (fingerprint) ensurePermissions(fingerprint);
6710
6967
  const sha = getCurrentHeadSha();
6711
6968
  writeState({
6712
6969
  lastRefreshSha: sha ?? "",
6713
6970
  lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
6714
6971
  targetAgent
6715
6972
  });
6716
- console.log("");
6717
- console.log(chalk9.dim(" Auto-refresh: keep your configs in sync as your code changes.\n"));
6718
- let hookChoice;
6719
- if (options.autoApprove) {
6720
- hookChoice = "skip";
6721
- log(options.verbose, "Auto-approve: skipping hook installation");
6722
- } else {
6723
- hookChoice = await promptHookType(targetAgent);
6724
- }
6725
- trackInitHookSelected(hookChoice);
6726
- if (hookChoice === "claude" || hookChoice === "both") {
6727
- const hookResult = installHook();
6728
- if (hookResult.installed) {
6729
- console.log(` ${chalk9.green("\u2713")} Claude Code hook installed \u2014 docs update on session end`);
6730
- console.log(chalk9.dim(" Run ") + chalk9.hex("#83D1EB")("caliber hooks --remove") + chalk9.dim(" to disable"));
6731
- } else if (hookResult.alreadyInstalled) {
6732
- console.log(chalk9.dim(" Claude Code hook already installed"));
6733
- }
6734
- const learnResult = installLearningHooks();
6735
- if (learnResult.installed) {
6736
- console.log(` ${chalk9.green("\u2713")} Learning hooks installed \u2014 session insights captured automatically`);
6737
- console.log(chalk9.dim(" Run ") + chalk9.hex("#83D1EB")("caliber learn remove") + chalk9.dim(" to disable"));
6738
- } else if (learnResult.alreadyInstalled) {
6739
- console.log(chalk9.dim(" Learning hooks already installed"));
6740
- }
6741
- }
6742
- if (hookChoice === "precommit" || hookChoice === "both") {
6743
- const precommitResult = installPreCommitHook();
6744
- if (precommitResult.installed) {
6745
- console.log(` ${chalk9.green("\u2713")} Pre-commit hook installed \u2014 docs refresh before each commit`);
6746
- console.log(chalk9.dim(" Run ") + chalk9.hex("#83D1EB")("caliber hooks --remove") + chalk9.dim(" to disable"));
6747
- } else if (precommitResult.alreadyInstalled) {
6748
- console.log(chalk9.dim(" Pre-commit hook already installed"));
6749
- } else {
6750
- console.log(chalk9.yellow(" Could not install pre-commit hook (not a git repository?)"));
6751
- }
6752
- }
6753
- if (hookChoice === "skip") {
6754
- console.log(chalk9.dim(" Skipped auto-refresh hooks. Run ") + chalk9.hex("#83D1EB")("caliber hooks --install") + chalk9.dim(" later to enable."));
6755
- }
6756
6973
  const afterScore = computeLocalScore(process.cwd(), targetAgent);
6757
6974
  if (afterScore.score < baselineScore.score) {
6758
6975
  trackInitScoreRegression(baselineScore.score, afterScore.score);
6759
6976
  console.log("");
6760
- console.log(chalk9.yellow(` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`));
6977
+ console.log(chalk10.yellow(` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`));
6761
6978
  try {
6762
6979
  const { restored, removed } = undoSetup();
6763
6980
  if (restored.length > 0 || removed.length > 0) {
6764
- console.log(chalk9.dim(` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`));
6981
+ console.log(chalk10.dim(` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`));
6765
6982
  }
6766
6983
  } catch {
6767
6984
  }
6768
- console.log(chalk9.dim(" Run ") + chalk9.hex("#83D1EB")("caliber init --force") + chalk9.dim(" to override.\n"));
6985
+ console.log(chalk10.dim(" Run ") + chalk10.hex("#83D1EB")("caliber init --force") + chalk10.dim(" to override.\n"));
6769
6986
  return;
6770
6987
  }
6771
6988
  if (report) {
@@ -6783,39 +7000,57 @@ ${agentRefs.join(" ")}
6783
7000
  log(options.verbose, ` Still failing: ${c.name} (${c.earnedPoints}/${c.maxPoints})${c.suggestion ? ` \u2014 ${c.suggestion}` : ""}`);
6784
7001
  }
6785
7002
  }
6786
- console.log(title.bold("\n Step 5/5 \u2014 Skills\n"));
6787
- console.log(chalk9.dim(" Search community registries for skills that match your tech stack.\n"));
6788
- let wantsSkills;
7003
+ if (skillSearchResult.results.length > 0 && !options.autoApprove) {
7004
+ console.log(chalk10.dim(" Community skills matched to your project:\n"));
7005
+ const selected = await interactiveSelect(skillSearchResult.results);
7006
+ if (selected?.length) {
7007
+ await installSkills(selected, targetAgent, skillSearchResult.contentMap);
7008
+ trackInitSkillsSearch(true, selected.length);
7009
+ }
7010
+ }
7011
+ console.log("");
7012
+ let hookChoice;
6789
7013
  if (options.autoApprove) {
6790
- wantsSkills = false;
6791
- log(options.verbose, "Auto-approve: skipping skills search");
7014
+ hookChoice = "skip";
7015
+ log(options.verbose, "Auto-approve: skipping hook installation");
6792
7016
  } else {
6793
- wantsSkills = await select5({
6794
- message: "Search public repos for relevant skills to add to this project?",
6795
- choices: [
6796
- { name: "Yes, find skills for my project", value: true },
6797
- { name: "Skip for now", value: false }
6798
- ]
6799
- });
7017
+ hookChoice = await promptHookType(targetAgent);
6800
7018
  }
6801
- if (wantsSkills) {
6802
- trackInitSkillsSearch(true, 0);
6803
- try {
6804
- await searchAndInstallSkills(targetAgent);
6805
- } catch (err) {
6806
- if (err.message !== "__exit__") {
6807
- console.log(chalk9.dim(" Skills search failed: " + (err.message || "unknown error")));
6808
- }
6809
- console.log(chalk9.dim(" Run ") + chalk9.hex("#83D1EB")("caliber skills") + chalk9.dim(" later to try again.\n"));
7019
+ trackInitHookSelected(hookChoice);
7020
+ if (hookChoice === "claude" || hookChoice === "both") {
7021
+ const hookResult = installHook();
7022
+ if (hookResult.installed) {
7023
+ console.log(` ${chalk10.green("\u2713")} Claude Code hook installed \u2014 docs update on session end`);
7024
+ console.log(chalk10.dim(" Run ") + chalk10.hex("#83D1EB")("caliber hooks --remove") + chalk10.dim(" to disable"));
7025
+ } else if (hookResult.alreadyInstalled) {
7026
+ console.log(chalk10.dim(" Claude Code hook already installed"));
7027
+ }
7028
+ const learnResult = installLearningHooks();
7029
+ if (learnResult.installed) {
7030
+ console.log(` ${chalk10.green("\u2713")} Learning hooks installed \u2014 session insights captured automatically`);
7031
+ console.log(chalk10.dim(" Run ") + chalk10.hex("#83D1EB")("caliber learn remove") + chalk10.dim(" to disable"));
7032
+ } else if (learnResult.alreadyInstalled) {
7033
+ console.log(chalk10.dim(" Learning hooks already installed"));
6810
7034
  }
6811
- } else {
6812
- trackInitSkillsSearch(false, 0);
6813
- console.log(chalk9.dim(" Skipped. Run ") + chalk9.hex("#83D1EB")("caliber skills") + chalk9.dim(" later to browse.\n"));
6814
7035
  }
6815
- console.log(chalk9.bold.green(" Setup complete!"));
6816
- console.log(chalk9.dim(" Your AI agents now understand your project's architecture, build commands,"));
6817
- console.log(chalk9.dim(" testing patterns, and conventions. All changes are backed up automatically.\n"));
6818
- console.log(chalk9.bold(" Next steps:\n"));
7036
+ if (hookChoice === "precommit" || hookChoice === "both") {
7037
+ const precommitResult = installPreCommitHook();
7038
+ if (precommitResult.installed) {
7039
+ console.log(` ${chalk10.green("\u2713")} Pre-commit hook installed \u2014 docs refresh before each commit`);
7040
+ console.log(chalk10.dim(" Run ") + chalk10.hex("#83D1EB")("caliber hooks --remove") + chalk10.dim(" to disable"));
7041
+ } else if (precommitResult.alreadyInstalled) {
7042
+ console.log(chalk10.dim(" Pre-commit hook already installed"));
7043
+ } else {
7044
+ console.log(chalk10.yellow(" Could not install pre-commit hook (not a git repository?)"));
7045
+ }
7046
+ }
7047
+ if (hookChoice === "skip") {
7048
+ console.log(chalk10.dim(" Skipped auto-sync hooks. Run ") + chalk10.hex("#83D1EB")("caliber hooks --install") + chalk10.dim(" later to enable."));
7049
+ }
7050
+ console.log(chalk10.bold.green("\n Setup complete!"));
7051
+ console.log(chalk10.dim(" Your AI agents now understand your project's architecture, build commands,"));
7052
+ console.log(chalk10.dim(" testing patterns, and conventions. All changes are backed up automatically.\n"));
7053
+ console.log(chalk10.bold(" Next steps:\n"));
6819
7054
  console.log(` ${title("caliber score")} See your full config breakdown`);
6820
7055
  console.log(` ${title("caliber refresh")} Update docs after code changes`);
6821
7056
  console.log(` ${title("caliber hooks")} Toggle auto-refresh hooks`);
@@ -6829,7 +7064,7 @@ ${agentRefs.join(" ")}
6829
7064
  report.markStep("Finished");
6830
7065
  const reportPath = path19.join(process.cwd(), ".caliber", "debug-report.md");
6831
7066
  report.write(reportPath);
6832
- console.log(chalk9.dim(` Debug report written to ${path19.relative(process.cwd(), reportPath)}
7067
+ console.log(chalk10.dim(` Debug report written to ${path19.relative(process.cwd(), reportPath)}
6833
7068
  `));
6834
7069
  }
6835
7070
  }
@@ -6844,9 +7079,9 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
6844
7079
  }
6845
7080
  const isValid = await classifyRefineIntent(message);
6846
7081
  if (!isValid) {
6847
- console.log(chalk9.dim(" This doesn't look like a config change request."));
6848
- console.log(chalk9.dim(" Describe what to add, remove, or modify in your configs."));
6849
- console.log(chalk9.dim(' Type "done" to accept the current setup.\n'));
7082
+ console.log(chalk10.dim(" This doesn't look like a config change request."));
7083
+ console.log(chalk10.dim(" Describe what to add, remove, or modify in your configs."));
7084
+ console.log(chalk10.dim(' Type "done" to accept the current setup.\n'));
6850
7085
  continue;
6851
7086
  }
6852
7087
  const refineSpinner = ora2("Refining setup...").start();
@@ -6867,10 +7102,10 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
6867
7102
  });
6868
7103
  refineSpinner.succeed("Setup updated");
6869
7104
  printSetupSummary(refined);
6870
- console.log(chalk9.dim('Type "done" to accept, or describe more changes.'));
7105
+ console.log(chalk10.dim('Type "done" to accept, or describe more changes.'));
6871
7106
  } else {
6872
7107
  refineSpinner.fail("Refinement failed \u2014 could not parse AI response.");
6873
- console.log(chalk9.dim('Try rephrasing your request, or type "done" to keep the current setup.'));
7108
+ console.log(chalk10.dim('Try rephrasing your request, or type "done" to keep the current setup.'));
6874
7109
  }
6875
7110
  }
6876
7111
  }
@@ -6898,6 +7133,7 @@ Return {"valid": true} or {"valid": false}. Nothing else.`,
6898
7133
  }
6899
7134
  }
6900
7135
  async function evaluateDismissals(failingChecks, fingerprint) {
7136
+ if (failingChecks.length === 0) return [];
6901
7137
  const fastModel = getFastModel();
6902
7138
  const checkList = failingChecks.map((c) => ({
6903
7139
  id: c.id,
@@ -6965,7 +7201,7 @@ async function promptHookType(targetAgent) {
6965
7201
  }
6966
7202
  choices.push({ name: "Skip for now", value: "skip" });
6967
7203
  return select5({
6968
- message: "How would you like to auto-refresh your setup?",
7204
+ message: "Keep your AI docs & skills in sync as your code evolves?",
6969
7205
  choices
6970
7206
  });
6971
7207
  }
@@ -6985,26 +7221,26 @@ function printSetupSummary(setup) {
6985
7221
  const fileDescriptions = setup.fileDescriptions;
6986
7222
  const deletions = setup.deletions;
6987
7223
  console.log("");
6988
- console.log(chalk9.bold(" Proposed changes:\n"));
7224
+ console.log(chalk10.bold(" Proposed changes:\n"));
6989
7225
  const getDescription = (filePath) => {
6990
7226
  return fileDescriptions?.[filePath];
6991
7227
  };
6992
7228
  if (claude) {
6993
7229
  if (claude.claudeMd) {
6994
- const icon = fs24.existsSync("CLAUDE.md") ? chalk9.yellow("~") : chalk9.green("+");
7230
+ const icon = fs24.existsSync("CLAUDE.md") ? chalk10.yellow("~") : chalk10.green("+");
6995
7231
  const desc = getDescription("CLAUDE.md");
6996
- console.log(` ${icon} ${chalk9.bold("CLAUDE.md")}`);
6997
- if (desc) console.log(chalk9.dim(` ${desc}`));
7232
+ console.log(` ${icon} ${chalk10.bold("CLAUDE.md")}`);
7233
+ if (desc) console.log(chalk10.dim(` ${desc}`));
6998
7234
  console.log("");
6999
7235
  }
7000
7236
  const skills = claude.skills;
7001
7237
  if (Array.isArray(skills) && skills.length > 0) {
7002
7238
  for (const skill of skills) {
7003
7239
  const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
7004
- const icon = fs24.existsSync(skillPath) ? chalk9.yellow("~") : chalk9.green("+");
7240
+ const icon = fs24.existsSync(skillPath) ? chalk10.yellow("~") : chalk10.green("+");
7005
7241
  const desc = getDescription(skillPath);
7006
- console.log(` ${icon} ${chalk9.bold(skillPath)}`);
7007
- console.log(chalk9.dim(` ${desc || skill.description || skill.name}`));
7242
+ console.log(` ${icon} ${chalk10.bold(skillPath)}`);
7243
+ console.log(chalk10.dim(` ${desc || skill.description || skill.name}`));
7008
7244
  console.log("");
7009
7245
  }
7010
7246
  }
@@ -7012,40 +7248,40 @@ function printSetupSummary(setup) {
7012
7248
  const codex = setup.codex;
7013
7249
  if (codex) {
7014
7250
  if (codex.agentsMd) {
7015
- const icon = fs24.existsSync("AGENTS.md") ? chalk9.yellow("~") : chalk9.green("+");
7251
+ const icon = fs24.existsSync("AGENTS.md") ? chalk10.yellow("~") : chalk10.green("+");
7016
7252
  const desc = getDescription("AGENTS.md");
7017
- console.log(` ${icon} ${chalk9.bold("AGENTS.md")}`);
7018
- if (desc) console.log(chalk9.dim(` ${desc}`));
7253
+ console.log(` ${icon} ${chalk10.bold("AGENTS.md")}`);
7254
+ if (desc) console.log(chalk10.dim(` ${desc}`));
7019
7255
  console.log("");
7020
7256
  }
7021
7257
  const codexSkills = codex.skills;
7022
7258
  if (Array.isArray(codexSkills) && codexSkills.length > 0) {
7023
7259
  for (const skill of codexSkills) {
7024
7260
  const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
7025
- const icon = fs24.existsSync(skillPath) ? chalk9.yellow("~") : chalk9.green("+");
7261
+ const icon = fs24.existsSync(skillPath) ? chalk10.yellow("~") : chalk10.green("+");
7026
7262
  const desc = getDescription(skillPath);
7027
- console.log(` ${icon} ${chalk9.bold(skillPath)}`);
7028
- console.log(chalk9.dim(` ${desc || skill.description || skill.name}`));
7263
+ console.log(` ${icon} ${chalk10.bold(skillPath)}`);
7264
+ console.log(chalk10.dim(` ${desc || skill.description || skill.name}`));
7029
7265
  console.log("");
7030
7266
  }
7031
7267
  }
7032
7268
  }
7033
7269
  if (cursor) {
7034
7270
  if (cursor.cursorrules) {
7035
- const icon = fs24.existsSync(".cursorrules") ? chalk9.yellow("~") : chalk9.green("+");
7271
+ const icon = fs24.existsSync(".cursorrules") ? chalk10.yellow("~") : chalk10.green("+");
7036
7272
  const desc = getDescription(".cursorrules");
7037
- console.log(` ${icon} ${chalk9.bold(".cursorrules")}`);
7038
- if (desc) console.log(chalk9.dim(` ${desc}`));
7273
+ console.log(` ${icon} ${chalk10.bold(".cursorrules")}`);
7274
+ if (desc) console.log(chalk10.dim(` ${desc}`));
7039
7275
  console.log("");
7040
7276
  }
7041
7277
  const cursorSkills = cursor.skills;
7042
7278
  if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
7043
7279
  for (const skill of cursorSkills) {
7044
7280
  const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
7045
- const icon = fs24.existsSync(skillPath) ? chalk9.yellow("~") : chalk9.green("+");
7281
+ const icon = fs24.existsSync(skillPath) ? chalk10.yellow("~") : chalk10.green("+");
7046
7282
  const desc = getDescription(skillPath);
7047
- console.log(` ${icon} ${chalk9.bold(skillPath)}`);
7048
- console.log(chalk9.dim(` ${desc || skill.description || skill.name}`));
7283
+ console.log(` ${icon} ${chalk10.bold(skillPath)}`);
7284
+ console.log(chalk10.dim(` ${desc || skill.description || skill.name}`));
7049
7285
  console.log("");
7050
7286
  }
7051
7287
  }
@@ -7053,14 +7289,14 @@ function printSetupSummary(setup) {
7053
7289
  if (Array.isArray(rules) && rules.length > 0) {
7054
7290
  for (const rule of rules) {
7055
7291
  const rulePath = `.cursor/rules/${rule.filename}`;
7056
- const icon = fs24.existsSync(rulePath) ? chalk9.yellow("~") : chalk9.green("+");
7292
+ const icon = fs24.existsSync(rulePath) ? chalk10.yellow("~") : chalk10.green("+");
7057
7293
  const desc = getDescription(rulePath);
7058
- console.log(` ${icon} ${chalk9.bold(rulePath)}`);
7294
+ console.log(` ${icon} ${chalk10.bold(rulePath)}`);
7059
7295
  if (desc) {
7060
- console.log(chalk9.dim(` ${desc}`));
7296
+ console.log(chalk10.dim(` ${desc}`));
7061
7297
  } else {
7062
7298
  const firstLine = rule.content.split("\n").filter((l) => l.trim() && !l.trim().startsWith("#"))[0];
7063
- if (firstLine) console.log(chalk9.dim(` ${firstLine.trim().slice(0, 80)}`));
7299
+ if (firstLine) console.log(chalk10.dim(` ${firstLine.trim().slice(0, 80)}`));
7064
7300
  }
7065
7301
  console.log("");
7066
7302
  }
@@ -7068,12 +7304,12 @@ function printSetupSummary(setup) {
7068
7304
  }
7069
7305
  if (Array.isArray(deletions) && deletions.length > 0) {
7070
7306
  for (const del of deletions) {
7071
- console.log(` ${chalk9.red("-")} ${chalk9.bold(del.filePath)}`);
7072
- console.log(chalk9.dim(` ${del.reason}`));
7307
+ console.log(` ${chalk10.red("-")} ${chalk10.bold(del.filePath)}`);
7308
+ console.log(chalk10.dim(` ${del.reason}`));
7073
7309
  console.log("");
7074
7310
  }
7075
7311
  }
7076
- console.log(` ${chalk9.green("+")} ${chalk9.dim("new")} ${chalk9.yellow("~")} ${chalk9.dim("modified")} ${chalk9.red("-")} ${chalk9.dim("removed")}`);
7312
+ console.log(` ${chalk10.green("+")} ${chalk10.dim("new")} ${chalk10.yellow("~")} ${chalk10.dim("modified")} ${chalk10.red("-")} ${chalk10.dim("removed")}`);
7077
7313
  console.log("");
7078
7314
  }
7079
7315
  function derivePermissions(fingerprint) {
@@ -7133,20 +7369,20 @@ function ensurePermissions(fingerprint) {
7133
7369
  function displayTokenUsage() {
7134
7370
  const summary = getUsageSummary();
7135
7371
  if (summary.length === 0) {
7136
- console.log(chalk9.dim(" Token tracking not available for this provider.\n"));
7372
+ console.log(chalk10.dim(" Token tracking not available for this provider.\n"));
7137
7373
  return;
7138
7374
  }
7139
- console.log(chalk9.bold(" Token usage:\n"));
7375
+ console.log(chalk10.bold(" Token usage:\n"));
7140
7376
  let totalIn = 0;
7141
7377
  let totalOut = 0;
7142
7378
  for (const m of summary) {
7143
7379
  totalIn += m.inputTokens;
7144
7380
  totalOut += m.outputTokens;
7145
- const cacheInfo = m.cacheReadTokens > 0 || m.cacheWriteTokens > 0 ? chalk9.dim(` (cache: ${m.cacheReadTokens.toLocaleString()} read, ${m.cacheWriteTokens.toLocaleString()} write)`) : "";
7146
- console.log(` ${chalk9.dim(m.model)}: ${m.inputTokens.toLocaleString()} in / ${m.outputTokens.toLocaleString()} out (${m.calls} call${m.calls === 1 ? "" : "s"})${cacheInfo}`);
7381
+ const cacheInfo = m.cacheReadTokens > 0 || m.cacheWriteTokens > 0 ? chalk10.dim(` (cache: ${m.cacheReadTokens.toLocaleString()} read, ${m.cacheWriteTokens.toLocaleString()} write)`) : "";
7382
+ console.log(` ${chalk10.dim(m.model)}: ${m.inputTokens.toLocaleString()} in / ${m.outputTokens.toLocaleString()} out (${m.calls} call${m.calls === 1 ? "" : "s"})${cacheInfo}`);
7147
7383
  }
7148
7384
  if (summary.length > 1) {
7149
- console.log(` ${chalk9.dim("Total")}: ${totalIn.toLocaleString()} in / ${totalOut.toLocaleString()} out`);
7385
+ console.log(` ${chalk10.dim("Total")}: ${totalIn.toLocaleString()} in / ${totalOut.toLocaleString()} out`);
7150
7386
  }
7151
7387
  console.log("");
7152
7388
  }
@@ -7167,14 +7403,14 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
7167
7403
  lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
7168
7404
  fs24.mkdirSync(path19.join(process.cwd(), ".caliber"), { recursive: true });
7169
7405
  fs24.writeFileSync(logPath, lines.join("\n"));
7170
- console.log(chalk9.dim(`
7406
+ console.log(chalk10.dim(`
7171
7407
  Error log written to .caliber/error-log.md`));
7172
7408
  } catch {
7173
7409
  }
7174
7410
  }
7175
7411
 
7176
7412
  // src/commands/undo.ts
7177
- import chalk10 from "chalk";
7413
+ import chalk11 from "chalk";
7178
7414
  import ora3 from "ora";
7179
7415
  function undoCommand() {
7180
7416
  const spinner = ora3("Reverting setup...").start();
@@ -7187,26 +7423,26 @@ function undoCommand() {
7187
7423
  trackUndoExecuted();
7188
7424
  spinner.succeed("Setup reverted successfully.\n");
7189
7425
  if (restored.length > 0) {
7190
- console.log(chalk10.cyan(" Restored from backup:"));
7426
+ console.log(chalk11.cyan(" Restored from backup:"));
7191
7427
  for (const file of restored) {
7192
- console.log(` ${chalk10.green("\u21A9")} ${file}`);
7428
+ console.log(` ${chalk11.green("\u21A9")} ${file}`);
7193
7429
  }
7194
7430
  }
7195
7431
  if (removed.length > 0) {
7196
- console.log(chalk10.cyan(" Removed:"));
7432
+ console.log(chalk11.cyan(" Removed:"));
7197
7433
  for (const file of removed) {
7198
- console.log(` ${chalk10.red("\u2717")} ${file}`);
7434
+ console.log(` ${chalk11.red("\u2717")} ${file}`);
7199
7435
  }
7200
7436
  }
7201
7437
  console.log("");
7202
7438
  } catch (err) {
7203
- spinner.fail(chalk10.red(err instanceof Error ? err.message : "Undo failed"));
7439
+ spinner.fail(chalk11.red(err instanceof Error ? err.message : "Undo failed"));
7204
7440
  throw new Error("__exit__");
7205
7441
  }
7206
7442
  }
7207
7443
 
7208
7444
  // src/commands/status.ts
7209
- import chalk11 from "chalk";
7445
+ import chalk12 from "chalk";
7210
7446
  import fs25 from "fs";
7211
7447
  init_config();
7212
7448
  async function statusCommand(options) {
@@ -7221,40 +7457,40 @@ async function statusCommand(options) {
7221
7457
  }, null, 2));
7222
7458
  return;
7223
7459
  }
7224
- console.log(chalk11.bold("\nCaliber Status\n"));
7460
+ console.log(chalk12.bold("\nCaliber Status\n"));
7225
7461
  if (config) {
7226
- console.log(` LLM: ${chalk11.green(config.provider)} (${config.model})`);
7462
+ console.log(` LLM: ${chalk12.green(config.provider)} (${config.model})`);
7227
7463
  } else {
7228
- console.log(` LLM: ${chalk11.yellow("Not configured")} \u2014 run ${chalk11.hex("#83D1EB")("caliber config")}`);
7464
+ console.log(` LLM: ${chalk12.yellow("Not configured")} \u2014 run ${chalk12.hex("#83D1EB")("caliber config")}`);
7229
7465
  }
7230
7466
  if (!manifest) {
7231
- console.log(` Setup: ${chalk11.dim("No setup applied")}`);
7232
- console.log(chalk11.dim("\n Run ") + chalk11.hex("#83D1EB")("caliber init") + chalk11.dim(" to get started.\n"));
7467
+ console.log(` Setup: ${chalk12.dim("No setup applied")}`);
7468
+ console.log(chalk12.dim("\n Run ") + chalk12.hex("#83D1EB")("caliber init") + chalk12.dim(" to get started.\n"));
7233
7469
  return;
7234
7470
  }
7235
- console.log(` Files managed: ${chalk11.cyan(manifest.entries.length.toString())}`);
7471
+ console.log(` Files managed: ${chalk12.cyan(manifest.entries.length.toString())}`);
7236
7472
  for (const entry of manifest.entries) {
7237
7473
  const exists = fs25.existsSync(entry.path);
7238
- const icon = exists ? chalk11.green("\u2713") : chalk11.red("\u2717");
7474
+ const icon = exists ? chalk12.green("\u2713") : chalk12.red("\u2717");
7239
7475
  console.log(` ${icon} ${entry.path} (${entry.action})`);
7240
7476
  }
7241
7477
  console.log("");
7242
7478
  }
7243
7479
 
7244
7480
  // src/commands/regenerate.ts
7245
- import chalk12 from "chalk";
7481
+ import chalk13 from "chalk";
7246
7482
  import ora4 from "ora";
7247
7483
  import select6 from "@inquirer/select";
7248
7484
  init_config();
7249
7485
  async function regenerateCommand(options) {
7250
7486
  const config = loadConfig();
7251
7487
  if (!config) {
7252
- console.log(chalk12.red("No LLM provider configured. Run ") + chalk12.hex("#83D1EB")("caliber config") + chalk12.red(" first."));
7488
+ console.log(chalk13.red("No LLM provider configured. Run ") + chalk13.hex("#83D1EB")("caliber config") + chalk13.red(" first."));
7253
7489
  throw new Error("__exit__");
7254
7490
  }
7255
7491
  const manifest = readManifest();
7256
7492
  if (!manifest) {
7257
- console.log(chalk12.yellow("No existing setup found. Run ") + chalk12.hex("#83D1EB")("caliber init") + chalk12.yellow(" first."));
7493
+ console.log(chalk13.yellow("No existing setup found. Run ") + chalk13.hex("#83D1EB")("caliber init") + chalk13.yellow(" first."));
7258
7494
  throw new Error("__exit__");
7259
7495
  }
7260
7496
  const targetAgent = readState()?.targetAgent ?? ["claude", "cursor"];
@@ -7265,7 +7501,7 @@ async function regenerateCommand(options) {
7265
7501
  const baselineScore = computeLocalScore(process.cwd(), targetAgent);
7266
7502
  displayScoreSummary(baselineScore);
7267
7503
  if (baselineScore.score === 100) {
7268
- console.log(chalk12.green(" Your setup is already at 100/100 \u2014 nothing to regenerate.\n"));
7504
+ console.log(chalk13.green(" Your setup is already at 100/100 \u2014 nothing to regenerate.\n"));
7269
7505
  return;
7270
7506
  }
7271
7507
  const genSpinner = ora4("Regenerating setup...").start();
@@ -7306,18 +7542,18 @@ async function regenerateCommand(options) {
7306
7542
  const setupFiles = collectSetupFiles(generatedSetup, targetAgent);
7307
7543
  const staged = stageFiles(setupFiles, process.cwd());
7308
7544
  const totalChanges = staged.newFiles + staged.modifiedFiles;
7309
- console.log(chalk12.dim(`
7310
- ${chalk12.green(`${staged.newFiles} new`)} / ${chalk12.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}
7545
+ console.log(chalk13.dim(`
7546
+ ${chalk13.green(`${staged.newFiles} new`)} / ${chalk13.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}
7311
7547
  `));
7312
7548
  if (totalChanges === 0) {
7313
- console.log(chalk12.dim(" No changes needed \u2014 your configs are already up to date.\n"));
7549
+ console.log(chalk13.dim(" No changes needed \u2014 your configs are already up to date.\n"));
7314
7550
  cleanupStaging();
7315
7551
  return;
7316
7552
  }
7317
7553
  if (options.dryRun) {
7318
- console.log(chalk12.yellow("[Dry run] Would write:"));
7554
+ console.log(chalk13.yellow("[Dry run] Would write:"));
7319
7555
  for (const f of staged.stagedFiles) {
7320
- console.log(` ${f.isNew ? chalk12.green("+") : chalk12.yellow("~")} ${f.relativePath}`);
7556
+ console.log(` ${f.isNew ? chalk13.green("+") : chalk13.yellow("~")} ${f.relativePath}`);
7321
7557
  }
7322
7558
  cleanupStaging();
7323
7559
  return;
@@ -7336,7 +7572,7 @@ async function regenerateCommand(options) {
7336
7572
  });
7337
7573
  cleanupStaging();
7338
7574
  if (action === "decline") {
7339
- console.log(chalk12.dim("Regeneration cancelled. No files were modified."));
7575
+ console.log(chalk13.dim("Regeneration cancelled. No files were modified."));
7340
7576
  return;
7341
7577
  }
7342
7578
  const writeSpinner = ora4("Writing config files...").start();
@@ -7344,20 +7580,20 @@ async function regenerateCommand(options) {
7344
7580
  const result = writeSetup(generatedSetup);
7345
7581
  writeSpinner.succeed("Config files written");
7346
7582
  for (const file of result.written) {
7347
- console.log(` ${chalk12.green("\u2713")} ${file}`);
7583
+ console.log(` ${chalk13.green("\u2713")} ${file}`);
7348
7584
  }
7349
7585
  if (result.deleted.length > 0) {
7350
7586
  for (const file of result.deleted) {
7351
- console.log(` ${chalk12.red("\u2717")} ${file}`);
7587
+ console.log(` ${chalk13.red("\u2717")} ${file}`);
7352
7588
  }
7353
7589
  }
7354
7590
  if (result.backupDir) {
7355
- console.log(chalk12.dim(`
7591
+ console.log(chalk13.dim(`
7356
7592
  Backups saved to ${result.backupDir}`));
7357
7593
  }
7358
7594
  } catch (err) {
7359
7595
  writeSpinner.fail("Failed to write files");
7360
- console.error(chalk12.red(err instanceof Error ? err.message : "Unknown error"));
7596
+ console.error(chalk13.red(err instanceof Error ? err.message : "Unknown error"));
7361
7597
  throw new Error("__exit__");
7362
7598
  }
7363
7599
  const sha = getCurrentHeadSha();
@@ -7369,25 +7605,25 @@ async function regenerateCommand(options) {
7369
7605
  const afterScore = computeLocalScore(process.cwd(), targetAgent);
7370
7606
  if (afterScore.score < baselineScore.score) {
7371
7607
  console.log("");
7372
- console.log(chalk12.yellow(` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`));
7608
+ console.log(chalk13.yellow(` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`));
7373
7609
  try {
7374
7610
  const { restored, removed } = undoSetup();
7375
7611
  if (restored.length > 0 || removed.length > 0) {
7376
- console.log(chalk12.dim(` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`));
7612
+ console.log(chalk13.dim(` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`));
7377
7613
  }
7378
7614
  } catch {
7379
7615
  }
7380
- console.log(chalk12.dim(" Run ") + chalk12.hex("#83D1EB")("caliber init --force") + chalk12.dim(" to override.\n"));
7616
+ console.log(chalk13.dim(" Run ") + chalk13.hex("#83D1EB")("caliber init --force") + chalk13.dim(" to override.\n"));
7381
7617
  return;
7382
7618
  }
7383
7619
  displayScoreDelta(baselineScore, afterScore);
7384
7620
  trackRegenerateCompleted(action, Date.now());
7385
- console.log(chalk12.bold.green(" Regeneration complete!"));
7386
- console.log(chalk12.dim(" Run ") + chalk12.hex("#83D1EB")("caliber undo") + chalk12.dim(" to revert changes.\n"));
7621
+ console.log(chalk13.bold.green(" Regeneration complete!"));
7622
+ console.log(chalk13.dim(" Run ") + chalk13.hex("#83D1EB")("caliber undo") + chalk13.dim(" to revert changes.\n"));
7387
7623
  }
7388
7624
 
7389
7625
  // src/commands/score.ts
7390
- import chalk13 from "chalk";
7626
+ import chalk14 from "chalk";
7391
7627
  async function scoreCommand(options) {
7392
7628
  const dir = process.cwd();
7393
7629
  const target = options.agent ?? readState()?.targetAgent;
@@ -7402,14 +7638,14 @@ async function scoreCommand(options) {
7402
7638
  return;
7403
7639
  }
7404
7640
  displayScore(result);
7405
- const separator = chalk13.gray(" " + "\u2500".repeat(53));
7641
+ const separator = chalk14.gray(" " + "\u2500".repeat(53));
7406
7642
  console.log(separator);
7407
7643
  if (result.score < 40) {
7408
- console.log(chalk13.gray(" Run ") + chalk13.hex("#83D1EB")("caliber init") + chalk13.gray(" to generate a complete, optimized setup."));
7644
+ console.log(chalk14.gray(" Run ") + chalk14.hex("#83D1EB")("caliber init") + chalk14.gray(" to generate a complete, optimized setup."));
7409
7645
  } else if (result.score < 70) {
7410
- console.log(chalk13.gray(" Run ") + chalk13.hex("#83D1EB")("caliber init") + chalk13.gray(" to improve your setup."));
7646
+ console.log(chalk14.gray(" Run ") + chalk14.hex("#83D1EB")("caliber init") + chalk14.gray(" to improve your setup."));
7411
7647
  } else {
7412
- console.log(chalk13.green(" Looking good!") + chalk13.gray(" Run ") + chalk13.hex("#83D1EB")("caliber regenerate") + chalk13.gray(" to rebuild from scratch."));
7648
+ console.log(chalk14.green(" Looking good!") + chalk14.gray(" Run ") + chalk14.hex("#83D1EB")("caliber regenerate") + chalk14.gray(" to rebuild from scratch."));
7413
7649
  }
7414
7650
  console.log("");
7415
7651
  }
@@ -7417,7 +7653,7 @@ async function scoreCommand(options) {
7417
7653
  // src/commands/refresh.ts
7418
7654
  import fs29 from "fs";
7419
7655
  import path23 from "path";
7420
- import chalk14 from "chalk";
7656
+ import chalk15 from "chalk";
7421
7657
  import ora5 from "ora";
7422
7658
 
7423
7659
  // src/lib/git-diff.ts
@@ -7649,8 +7885,12 @@ function parseBullets(content) {
7649
7885
  if (current) bullets.push(current);
7650
7886
  return bullets;
7651
7887
  }
7888
+ var TYPE_PREFIX_RE = /^\*\*\[[^\]]+\]\*\*\s*/;
7652
7889
  function normalizeBullet(bullet) {
7653
- return bullet.replace(/^- /, "").replace(/`[^`]*`/g, "").replace(/\s+/g, " ").toLowerCase().trim();
7890
+ return bullet.replace(/^- /, "").replace(TYPE_PREFIX_RE, "").replace(/`[^`]*`/g, "").replace(/\s+/g, " ").toLowerCase().trim();
7891
+ }
7892
+ function hasTypePrefix(bullet) {
7893
+ return TYPE_PREFIX_RE.test(bullet.replace(/^- /, ""));
7654
7894
  }
7655
7895
  function deduplicateLearnedItems(existing, incoming) {
7656
7896
  const existingBullets = existing ? parseBullets(existing) : [];
@@ -7660,14 +7900,18 @@ function deduplicateLearnedItems(existing, incoming) {
7660
7900
  for (const bullet of incomingBullets) {
7661
7901
  const norm = normalizeBullet(bullet);
7662
7902
  if (!norm) continue;
7663
- const isDup = merged.some((e) => {
7903
+ const dupIdx = merged.findIndex((e) => {
7664
7904
  const eNorm = normalizeBullet(e);
7665
7905
  const shorter = Math.min(norm.length, eNorm.length);
7666
7906
  const longer = Math.max(norm.length, eNorm.length);
7667
7907
  if (!(eNorm.includes(norm) || norm.includes(eNorm))) return false;
7668
7908
  return shorter / longer > 0.7;
7669
7909
  });
7670
- if (!isDup) {
7910
+ if (dupIdx !== -1) {
7911
+ if (hasTypePrefix(bullet) && !hasTypePrefix(merged[dupIdx])) {
7912
+ merged[dupIdx] = bullet;
7913
+ }
7914
+ } else {
7671
7915
  merged.push(bullet);
7672
7916
  newItems.push(bullet);
7673
7917
  }
@@ -7752,7 +7996,7 @@ function discoverGitRepos(parentDir) {
7752
7996
  }
7753
7997
  async function refreshSingleRepo(repoDir, options) {
7754
7998
  const quiet = !!options.quiet;
7755
- const prefix = options.label ? `${chalk14.bold(options.label)} ` : "";
7999
+ const prefix = options.label ? `${chalk15.bold(options.label)} ` : "";
7756
8000
  const state = readState();
7757
8001
  const lastSha = state?.lastRefreshSha ?? null;
7758
8002
  const diff = collectDiff(lastSha);
@@ -7761,7 +8005,7 @@ async function refreshSingleRepo(repoDir, options) {
7761
8005
  if (currentSha) {
7762
8006
  writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
7763
8007
  }
7764
- log2(quiet, chalk14.dim(`${prefix}No changes since last refresh.`));
8008
+ log2(quiet, chalk15.dim(`${prefix}No changes since last refresh.`));
7765
8009
  return;
7766
8010
  }
7767
8011
  const spinner = quiet ? null : ora5(`${prefix}Analyzing changes...`).start();
@@ -7795,10 +8039,10 @@ async function refreshSingleRepo(repoDir, options) {
7795
8039
  if (options.dryRun) {
7796
8040
  spinner?.info(`${prefix}Dry run \u2014 would update:`);
7797
8041
  for (const doc of response.docsUpdated) {
7798
- console.log(` ${chalk14.yellow("~")} ${doc}`);
8042
+ console.log(` ${chalk15.yellow("~")} ${doc}`);
7799
8043
  }
7800
8044
  if (response.changesSummary) {
7801
- console.log(chalk14.dim(`
8045
+ console.log(chalk15.dim(`
7802
8046
  ${response.changesSummary}`));
7803
8047
  }
7804
8048
  return;
@@ -7807,10 +8051,10 @@ async function refreshSingleRepo(repoDir, options) {
7807
8051
  trackRefreshCompleted(written.length, Date.now());
7808
8052
  spinner?.succeed(`${prefix}Updated ${written.length} doc${written.length === 1 ? "" : "s"}`);
7809
8053
  for (const file of written) {
7810
- log2(quiet, ` ${chalk14.green("\u2713")} ${file}`);
8054
+ log2(quiet, ` ${chalk15.green("\u2713")} ${file}`);
7811
8055
  }
7812
8056
  if (response.changesSummary) {
7813
- log2(quiet, chalk14.dim(`
8057
+ log2(quiet, chalk15.dim(`
7814
8058
  ${response.changesSummary}`));
7815
8059
  }
7816
8060
  if (currentSha) {
@@ -7827,7 +8071,7 @@ async function refreshCommand(options) {
7827
8071
  const config = loadConfig();
7828
8072
  if (!config) {
7829
8073
  if (quiet) return;
7830
- console.log(chalk14.red("No LLM provider configured. Run ") + chalk14.hex("#83D1EB")("caliber config") + chalk14.red(" (e.g. choose Cursor) or set an API key."));
8074
+ console.log(chalk15.red("No LLM provider configured. Run ") + chalk15.hex("#83D1EB")("caliber config") + chalk15.red(" (e.g. choose Cursor) or set an API key."));
7831
8075
  throw new Error("__exit__");
7832
8076
  }
7833
8077
  await validateModel({ fast: true });
@@ -7838,10 +8082,10 @@ async function refreshCommand(options) {
7838
8082
  const repos = discoverGitRepos(process.cwd());
7839
8083
  if (repos.length === 0) {
7840
8084
  if (quiet) return;
7841
- console.log(chalk14.red("Not inside a git repository and no git repos found in child directories."));
8085
+ console.log(chalk15.red("Not inside a git repository and no git repos found in child directories."));
7842
8086
  throw new Error("__exit__");
7843
8087
  }
7844
- log2(quiet, chalk14.dim(`Found ${repos.length} git repo${repos.length === 1 ? "" : "s"}
8088
+ log2(quiet, chalk15.dim(`Found ${repos.length} git repo${repos.length === 1 ? "" : "s"}
7845
8089
  `));
7846
8090
  const originalDir = process.cwd();
7847
8091
  for (const repo of repos) {
@@ -7851,7 +8095,7 @@ async function refreshCommand(options) {
7851
8095
  await refreshSingleRepo(repo, { ...options, label: repoName });
7852
8096
  } catch (err) {
7853
8097
  if (err instanceof Error && err.message === "__exit__") continue;
7854
- log2(quiet, chalk14.yellow(`${repoName}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`));
8098
+ log2(quiet, chalk15.yellow(`${repoName}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`));
7855
8099
  }
7856
8100
  }
7857
8101
  process.chdir(originalDir);
@@ -7859,13 +8103,13 @@ async function refreshCommand(options) {
7859
8103
  if (err instanceof Error && err.message === "__exit__") throw err;
7860
8104
  if (quiet) return;
7861
8105
  const msg = err instanceof Error ? err.message : "Unknown error";
7862
- console.log(chalk14.red(`Refresh failed: ${msg}`));
8106
+ console.log(chalk15.red(`Refresh failed: ${msg}`));
7863
8107
  throw new Error("__exit__");
7864
8108
  }
7865
8109
  }
7866
8110
 
7867
8111
  // src/commands/hooks.ts
7868
- import chalk15 from "chalk";
8112
+ import chalk16 from "chalk";
7869
8113
  var HOOKS = [
7870
8114
  {
7871
8115
  id: "session-end",
@@ -7885,13 +8129,13 @@ var HOOKS = [
7885
8129
  }
7886
8130
  ];
7887
8131
  function printStatus() {
7888
- console.log(chalk15.bold("\n Hooks\n"));
8132
+ console.log(chalk16.bold("\n Hooks\n"));
7889
8133
  for (const hook of HOOKS) {
7890
8134
  const installed = hook.isInstalled();
7891
- const icon = installed ? chalk15.green("\u2713") : chalk15.dim("\u2717");
7892
- const state = installed ? chalk15.green("enabled") : chalk15.dim("disabled");
8135
+ const icon = installed ? chalk16.green("\u2713") : chalk16.dim("\u2717");
8136
+ const state = installed ? chalk16.green("enabled") : chalk16.dim("disabled");
7893
8137
  console.log(` ${icon} ${hook.label.padEnd(26)} ${state}`);
7894
- console.log(chalk15.dim(` ${hook.description}`));
8138
+ console.log(chalk16.dim(` ${hook.description}`));
7895
8139
  }
7896
8140
  console.log("");
7897
8141
  }
@@ -7900,9 +8144,9 @@ async function hooksCommand(options) {
7900
8144
  for (const hook of HOOKS) {
7901
8145
  const result = hook.install();
7902
8146
  if (result.alreadyInstalled) {
7903
- console.log(chalk15.dim(` ${hook.label} already enabled.`));
8147
+ console.log(chalk16.dim(` ${hook.label} already enabled.`));
7904
8148
  } else {
7905
- console.log(chalk15.green(" \u2713") + ` ${hook.label} enabled`);
8149
+ console.log(chalk16.green(" \u2713") + ` ${hook.label} enabled`);
7906
8150
  }
7907
8151
  }
7908
8152
  return;
@@ -7911,9 +8155,9 @@ async function hooksCommand(options) {
7911
8155
  for (const hook of HOOKS) {
7912
8156
  const result = hook.remove();
7913
8157
  if (result.notFound) {
7914
- console.log(chalk15.dim(` ${hook.label} already disabled.`));
8158
+ console.log(chalk16.dim(` ${hook.label} already disabled.`));
7915
8159
  } else {
7916
- console.log(chalk15.green(" \u2713") + ` ${hook.label} removed`);
8160
+ console.log(chalk16.green(" \u2713") + ` ${hook.label} removed`);
7917
8161
  }
7918
8162
  }
7919
8163
  return;
@@ -7928,18 +8172,18 @@ async function hooksCommand(options) {
7928
8172
  const states = HOOKS.map((h) => h.isInstalled());
7929
8173
  function render() {
7930
8174
  const lines = [];
7931
- lines.push(chalk15.bold(" Hooks"));
8175
+ lines.push(chalk16.bold(" Hooks"));
7932
8176
  lines.push("");
7933
8177
  for (let i = 0; i < HOOKS.length; i++) {
7934
8178
  const hook = HOOKS[i];
7935
8179
  const enabled = states[i];
7936
- const toggle = enabled ? chalk15.green("[on] ") : chalk15.dim("[off]");
7937
- const ptr = i === cursor ? chalk15.cyan(">") : " ";
8180
+ const toggle = enabled ? chalk16.green("[on] ") : chalk16.dim("[off]");
8181
+ const ptr = i === cursor ? chalk16.cyan(">") : " ";
7938
8182
  lines.push(` ${ptr} ${toggle} ${hook.label}`);
7939
- lines.push(chalk15.dim(` ${hook.description}`));
8183
+ lines.push(chalk16.dim(` ${hook.description}`));
7940
8184
  }
7941
8185
  lines.push("");
7942
- lines.push(chalk15.dim(" \u2191\u2193 navigate \u23B5 toggle a all on n all off \u23CE apply q cancel"));
8186
+ lines.push(chalk16.dim(" \u2191\u2193 navigate \u23B5 toggle a all on n all off \u23CE apply q cancel"));
7943
8187
  return lines.join("\n");
7944
8188
  }
7945
8189
  function draw(initial) {
@@ -7970,16 +8214,16 @@ async function hooksCommand(options) {
7970
8214
  const wantEnabled = states[i];
7971
8215
  if (wantEnabled && !wasInstalled) {
7972
8216
  hook.install();
7973
- console.log(chalk15.green(" \u2713") + ` ${hook.label} enabled`);
8217
+ console.log(chalk16.green(" \u2713") + ` ${hook.label} enabled`);
7974
8218
  changed++;
7975
8219
  } else if (!wantEnabled && wasInstalled) {
7976
8220
  hook.remove();
7977
- console.log(chalk15.green(" \u2713") + ` ${hook.label} disabled`);
8221
+ console.log(chalk16.green(" \u2713") + ` ${hook.label} disabled`);
7978
8222
  changed++;
7979
8223
  }
7980
8224
  }
7981
8225
  if (changed === 0) {
7982
- console.log(chalk15.dim(" No changes."));
8226
+ console.log(chalk16.dim(" No changes."));
7983
8227
  }
7984
8228
  console.log("");
7985
8229
  }
@@ -8015,7 +8259,7 @@ async function hooksCommand(options) {
8015
8259
  case "\x1B":
8016
8260
  case "":
8017
8261
  cleanup();
8018
- console.log(chalk15.dim("\n Cancelled.\n"));
8262
+ console.log(chalk16.dim("\n Cancelled.\n"));
8019
8263
  resolve2();
8020
8264
  break;
8021
8265
  }
@@ -8026,51 +8270,51 @@ async function hooksCommand(options) {
8026
8270
 
8027
8271
  // src/commands/config.ts
8028
8272
  init_config();
8029
- import chalk16 from "chalk";
8273
+ import chalk17 from "chalk";
8030
8274
  async function configCommand() {
8031
8275
  const existing = loadConfig();
8032
8276
  if (existing) {
8033
8277
  const displayModel = getDisplayModel(existing);
8034
8278
  const fastModel = getFastModel();
8035
- console.log(chalk16.bold("\nCurrent Configuration\n"));
8036
- console.log(` Provider: ${chalk16.cyan(existing.provider)}`);
8037
- console.log(` Model: ${chalk16.cyan(displayModel)}`);
8279
+ console.log(chalk17.bold("\nCurrent Configuration\n"));
8280
+ console.log(` Provider: ${chalk17.cyan(existing.provider)}`);
8281
+ console.log(` Model: ${chalk17.cyan(displayModel)}`);
8038
8282
  if (fastModel) {
8039
- console.log(` Scan: ${chalk16.cyan(fastModel)}`);
8283
+ console.log(` Scan: ${chalk17.cyan(fastModel)}`);
8040
8284
  }
8041
8285
  if (existing.apiKey) {
8042
8286
  const masked = existing.apiKey.slice(0, 8) + "..." + existing.apiKey.slice(-4);
8043
- console.log(` API Key: ${chalk16.dim(masked)}`);
8287
+ console.log(` API Key: ${chalk17.dim(masked)}`);
8044
8288
  }
8045
8289
  if (existing.provider === "cursor") {
8046
- console.log(` Seat: ${chalk16.dim("Cursor (agent acp)")}`);
8290
+ console.log(` Seat: ${chalk17.dim("Cursor (agent acp)")}`);
8047
8291
  }
8048
8292
  if (existing.provider === "claude-cli") {
8049
- console.log(` Seat: ${chalk16.dim("Claude Code (claude -p)")}`);
8293
+ console.log(` Seat: ${chalk17.dim("Claude Code (claude -p)")}`);
8050
8294
  }
8051
8295
  if (existing.baseUrl) {
8052
- console.log(` Base URL: ${chalk16.dim(existing.baseUrl)}`);
8296
+ console.log(` Base URL: ${chalk17.dim(existing.baseUrl)}`);
8053
8297
  }
8054
8298
  if (existing.vertexProjectId) {
8055
- console.log(` Vertex Project: ${chalk16.dim(existing.vertexProjectId)}`);
8056
- console.log(` Vertex Region: ${chalk16.dim(existing.vertexRegion || "us-east5")}`);
8299
+ console.log(` Vertex Project: ${chalk17.dim(existing.vertexProjectId)}`);
8300
+ console.log(` Vertex Region: ${chalk17.dim(existing.vertexRegion || "us-east5")}`);
8057
8301
  }
8058
- console.log(` Source: ${chalk16.dim(process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY || process.env.VERTEX_PROJECT_ID || process.env.CALIBER_USE_CURSOR_SEAT || process.env.CALIBER_USE_CLAUDE_CLI ? "environment variables" : getConfigFilePath())}`);
8302
+ console.log(` Source: ${chalk17.dim(process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY || process.env.VERTEX_PROJECT_ID || process.env.CALIBER_USE_CURSOR_SEAT || process.env.CALIBER_USE_CLAUDE_CLI ? "environment variables" : getConfigFilePath())}`);
8059
8303
  console.log("");
8060
8304
  }
8061
8305
  await runInteractiveProviderSetup();
8062
8306
  const updated = loadConfig();
8063
8307
  if (updated) trackConfigProviderSet(updated.provider);
8064
- console.log(chalk16.green("\n\u2713 Configuration saved"));
8065
- console.log(chalk16.dim(` ${getConfigFilePath()}
8308
+ console.log(chalk17.green("\n\u2713 Configuration saved"));
8309
+ console.log(chalk17.dim(` ${getConfigFilePath()}
8066
8310
  `));
8067
- console.log(chalk16.dim(" You can also set environment variables instead:"));
8068
- console.log(chalk16.dim(" ANTHROPIC_API_KEY, OPENAI_API_KEY, VERTEX_PROJECT_ID, CALIBER_USE_CURSOR_SEAT=1, or CALIBER_USE_CLAUDE_CLI=1\n"));
8311
+ console.log(chalk17.dim(" You can also set environment variables instead:"));
8312
+ console.log(chalk17.dim(" ANTHROPIC_API_KEY, OPENAI_API_KEY, VERTEX_PROJECT_ID, CALIBER_USE_CURSOR_SEAT=1, or CALIBER_USE_CLAUDE_CLI=1\n"));
8069
8313
  }
8070
8314
 
8071
8315
  // src/commands/learn.ts
8072
8316
  import fs31 from "fs";
8073
- import chalk17 from "chalk";
8317
+ import chalk18 from "chalk";
8074
8318
 
8075
8319
  // src/learner/stdin.ts
8076
8320
  var STDIN_TIMEOUT_MS = 5e3;
@@ -8137,6 +8381,17 @@ function appendEvent(event) {
8137
8381
  fs30.writeFileSync(filePath, kept.join("\n") + "\n");
8138
8382
  }
8139
8383
  }
8384
+ function appendPromptEvent(event) {
8385
+ ensureLearningDir();
8386
+ const filePath = sessionFilePath();
8387
+ fs30.appendFileSync(filePath, JSON.stringify(event) + "\n");
8388
+ const count = getEventCount();
8389
+ if (count > LEARNING_MAX_EVENTS) {
8390
+ const lines = fs30.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
8391
+ const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
8392
+ fs30.writeFileSync(filePath, kept.join("\n") + "\n");
8393
+ }
8394
+ }
8140
8395
  function readAllEvents() {
8141
8396
  const filePath = sessionFilePath();
8142
8397
  if (!fs30.existsSync(filePath)) return [];
@@ -8197,14 +8452,6 @@ function acquireFinalizeLock() {
8197
8452
  fs30.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
8198
8453
  return true;
8199
8454
  } catch {
8200
- try {
8201
- const stat = fs30.statSync(lockPath);
8202
- if (Date.now() - stat.mtimeMs >= LOCK_STALE_MS) {
8203
- fs30.writeFileSync(lockPath, String(process.pid));
8204
- return true;
8205
- }
8206
- } catch {
8207
- }
8208
8455
  return false;
8209
8456
  }
8210
8457
  }
@@ -8216,17 +8463,64 @@ function releaseFinalizeLock() {
8216
8463
  }
8217
8464
  }
8218
8465
 
8466
+ // src/lib/sanitize.ts
8467
+ var KNOWN_PREFIX_PATTERNS = [
8468
+ // Anthropic (before generic sk- pattern)
8469
+ [/sk-ant-[A-Za-z0-9_-]{20,}/g, "[REDACTED]"],
8470
+ // AWS access key IDs
8471
+ [/AKIA[0-9A-Z]{16}/g, "[REDACTED]"],
8472
+ // AWS secret keys in assignments
8473
+ [/(?:aws)?_?secret_?(?:access)?_?key\s*[:=]\s*['"]?[A-Za-z0-9/+=]{40}['"]?/gi, "[REDACTED]"],
8474
+ // GitHub tokens (PAT, OAuth, server, app install, fine-grained)
8475
+ [/gh[pousr]_[A-Za-z0-9_]{36,}/g, "[REDACTED]"],
8476
+ [/github_pat_[A-Za-z0-9_]{22,}/g, "[REDACTED]"],
8477
+ // Stripe keys
8478
+ [/[sr]k_(live|test)_[A-Za-z0-9]{20,}/g, "[REDACTED]"],
8479
+ // Slack tokens
8480
+ [/xox[bpsar]-[A-Za-z0-9-]{10,}/g, "[REDACTED]"],
8481
+ // JWTs (3-segment base64url)
8482
+ [/eyJ[A-Za-z0-9_-]{20,}\.eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}/g, "[REDACTED]"],
8483
+ // OpenAI keys (after sk-ant- to avoid false match)
8484
+ [/sk-[A-Za-z0-9-]{20,}/g, "[REDACTED]"],
8485
+ // Google API keys
8486
+ [/AIza[A-Za-z0-9_-]{35}/g, "[REDACTED]"],
8487
+ // Bearer tokens
8488
+ [/[Bb]earer\s+[A-Za-z0-9_\-.]{20,}/g, "[REDACTED]"],
8489
+ // PEM private keys
8490
+ [/-----BEGIN[A-Z ]+KEY-----[\s\S]+?-----END[A-Z ]+KEY-----/g, "[REDACTED]"]
8491
+ ];
8492
+ var SENSITIVE_ASSIGNMENT = /(?:api[_-]?key|secret[_-]?key|password|token|credential|auth[_-]?token|private[_-]?key)\s*[:=]\s*['"]?([^\s'"]{8,500})['"]?/gi;
8493
+ function sanitizeSecrets(text) {
8494
+ let result = text;
8495
+ for (const [pattern, replacement] of KNOWN_PREFIX_PATTERNS) {
8496
+ result = result.replace(pattern, replacement);
8497
+ }
8498
+ result = result.replace(
8499
+ SENSITIVE_ASSIGNMENT,
8500
+ (match, value) => match.replace(value, "[REDACTED]")
8501
+ );
8502
+ return result;
8503
+ }
8504
+
8219
8505
  // src/ai/learn.ts
8220
8506
  init_config();
8221
8507
  var MAX_PROMPT_TOKENS = 1e5;
8222
8508
  function formatEventsForPrompt(events) {
8223
8509
  return events.map((e, i) => {
8224
- const status = e.hook_event_name === "PostToolUseFailure" ? "FAILURE" : "SUCCESS";
8225
- const inputStr = JSON.stringify(e.tool_input, null, 2);
8226
- const responseStr = typeof e.tool_response === "object" && "_truncated" in e.tool_response ? String(e.tool_response._truncated) : JSON.stringify(e.tool_response, null, 2);
8510
+ if (e.hook_event_name === "UserPromptSubmit") {
8511
+ const pe = e;
8512
+ return `--- Event ${i + 1} [USER_PROMPT] ---
8513
+ Time: ${pe.timestamp}
8514
+ User said:
8515
+ ${pe.prompt_content}`;
8516
+ }
8517
+ const te = e;
8518
+ const status = te.hook_event_name === "PostToolUseFailure" ? "FAILURE" : "SUCCESS";
8519
+ const inputStr = JSON.stringify(te.tool_input, null, 2);
8520
+ const responseStr = typeof te.tool_response === "object" && "_truncated" in te.tool_response ? String(te.tool_response._truncated) : JSON.stringify(te.tool_response, null, 2);
8227
8521
  return `--- Event ${i + 1} [${status}] ---
8228
- Tool: ${e.tool_name}
8229
- Time: ${e.timestamp}
8522
+ Tool: ${te.tool_name}
8523
+ Time: ${te.timestamp}
8230
8524
  Input:
8231
8525
  ${inputStr}
8232
8526
  Response:
@@ -8298,9 +8592,25 @@ async function learnObserveCommand(options) {
8298
8592
  const raw = await readStdin();
8299
8593
  if (!raw.trim()) return;
8300
8594
  const hookData = JSON.parse(raw);
8595
+ const sessionId = hookData.session_id || hookData.conversation_id || "unknown";
8596
+ if (options.prompt) {
8597
+ const event2 = {
8598
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
8599
+ session_id: sessionId,
8600
+ hook_event_name: "UserPromptSubmit",
8601
+ prompt_content: sanitizeSecrets(String(hookData.prompt_content || hookData.content || hookData.prompt || "")),
8602
+ cwd: hookData.cwd || process.cwd()
8603
+ };
8604
+ appendPromptEvent(event2);
8605
+ const state2 = readState2();
8606
+ state2.eventCount++;
8607
+ if (!state2.sessionId) state2.sessionId = sessionId;
8608
+ writeState2(state2);
8609
+ return;
8610
+ }
8301
8611
  const event = {
8302
8612
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
8303
- session_id: hookData.session_id || hookData.conversation_id || "unknown",
8613
+ session_id: sessionId,
8304
8614
  hook_event_name: options.failure ? "PostToolUseFailure" : "PostToolUse",
8305
8615
  tool_name: hookData.tool_name || "unknown",
8306
8616
  tool_input: hookData.tool_input || {},
@@ -8311,7 +8621,7 @@ async function learnObserveCommand(options) {
8311
8621
  appendEvent(event);
8312
8622
  const state = readState2();
8313
8623
  state.eventCount++;
8314
- if (!state.sessionId) state.sessionId = event.session_id;
8624
+ if (!state.sessionId) state.sessionId = sessionId;
8315
8625
  writeState2(state);
8316
8626
  } catch {
8317
8627
  }
@@ -8320,26 +8630,26 @@ async function learnFinalizeCommand(options) {
8320
8630
  if (!options?.force) {
8321
8631
  const { isCaliberRunning: isCaliberRunning2 } = await Promise.resolve().then(() => (init_lock(), lock_exports));
8322
8632
  if (isCaliberRunning2()) {
8323
- console.log(chalk17.dim("caliber: skipping finalize \u2014 another caliber process is running"));
8633
+ console.log(chalk18.dim("caliber: skipping finalize \u2014 another caliber process is running"));
8324
8634
  return;
8325
8635
  }
8326
8636
  }
8327
8637
  if (!acquireFinalizeLock()) {
8328
- console.log(chalk17.dim("caliber: skipping finalize \u2014 another finalize is in progress"));
8638
+ console.log(chalk18.dim("caliber: skipping finalize \u2014 another finalize is in progress"));
8329
8639
  return;
8330
8640
  }
8331
8641
  let analyzed = false;
8332
8642
  try {
8333
8643
  const config = loadConfig();
8334
8644
  if (!config) {
8335
- console.log(chalk17.yellow("caliber: no LLM provider configured \u2014 run `caliber config` first"));
8645
+ console.log(chalk18.yellow("caliber: no LLM provider configured \u2014 run `caliber config` first"));
8336
8646
  clearSession();
8337
8647
  resetState();
8338
8648
  return;
8339
8649
  }
8340
8650
  const events = readAllEvents();
8341
8651
  if (events.length < MIN_EVENTS_FOR_ANALYSIS) {
8342
- console.log(chalk17.dim(`caliber: ${events.length}/${MIN_EVENTS_FOR_ANALYSIS} events recorded \u2014 need more before analysis`));
8652
+ console.log(chalk18.dim(`caliber: ${events.length}/${MIN_EVENTS_FOR_ANALYSIS} events recorded \u2014 need more before analysis`));
8343
8653
  return;
8344
8654
  }
8345
8655
  await validateModel({ fast: true });
@@ -8360,15 +8670,15 @@ async function learnFinalizeCommand(options) {
8360
8670
  skills: response.skills
8361
8671
  });
8362
8672
  if (result.newItemCount > 0) {
8363
- console.log(chalk17.dim(`caliber: learned ${result.newItemCount} new pattern${result.newItemCount === 1 ? "" : "s"}`));
8673
+ console.log(chalk18.dim(`caliber: learned ${result.newItemCount} new pattern${result.newItemCount === 1 ? "" : "s"}`));
8364
8674
  for (const item of result.newItems) {
8365
- console.log(chalk17.dim(` + ${item.replace(/^- /, "").slice(0, 80)}`));
8675
+ console.log(chalk18.dim(` + ${item.replace(/^- /, "").slice(0, 80)}`));
8366
8676
  }
8367
8677
  }
8368
8678
  }
8369
8679
  } catch (err) {
8370
8680
  if (options?.force) {
8371
- console.error(chalk17.red("caliber: finalize failed \u2014"), err instanceof Error ? err.message : err);
8681
+ console.error(chalk18.red("caliber: finalize failed \u2014"), err instanceof Error ? err.message : err);
8372
8682
  }
8373
8683
  } finally {
8374
8684
  if (analyzed) {
@@ -8383,45 +8693,45 @@ async function learnInstallCommand() {
8383
8693
  if (fs31.existsSync(".claude")) {
8384
8694
  const r = installLearningHooks();
8385
8695
  if (r.installed) {
8386
- console.log(chalk17.green("\u2713") + " Claude Code learning hooks installed");
8696
+ console.log(chalk18.green("\u2713") + " Claude Code learning hooks installed");
8387
8697
  anyInstalled = true;
8388
8698
  } else if (r.alreadyInstalled) {
8389
- console.log(chalk17.dim(" Claude Code hooks already installed"));
8699
+ console.log(chalk18.dim(" Claude Code hooks already installed"));
8390
8700
  }
8391
8701
  }
8392
8702
  if (fs31.existsSync(".cursor")) {
8393
8703
  const r = installCursorLearningHooks();
8394
8704
  if (r.installed) {
8395
- console.log(chalk17.green("\u2713") + " Cursor learning hooks installed");
8705
+ console.log(chalk18.green("\u2713") + " Cursor learning hooks installed");
8396
8706
  anyInstalled = true;
8397
8707
  } else if (r.alreadyInstalled) {
8398
- console.log(chalk17.dim(" Cursor hooks already installed"));
8708
+ console.log(chalk18.dim(" Cursor hooks already installed"));
8399
8709
  }
8400
8710
  }
8401
8711
  if (!fs31.existsSync(".claude") && !fs31.existsSync(".cursor")) {
8402
- console.log(chalk17.yellow("No .claude/ or .cursor/ directory found."));
8403
- console.log(chalk17.dim(" Run `caliber init` first, or create the directory manually."));
8712
+ console.log(chalk18.yellow("No .claude/ or .cursor/ directory found."));
8713
+ console.log(chalk18.dim(" Run `caliber init` first, or create the directory manually."));
8404
8714
  return;
8405
8715
  }
8406
8716
  if (anyInstalled) {
8407
- console.log(chalk17.dim(` Tool usage will be recorded and learnings extracted after \u2265${MIN_EVENTS_FOR_ANALYSIS} events.`));
8408
- console.log(chalk17.dim(" Learnings written to CALIBER_LEARNINGS.md."));
8717
+ console.log(chalk18.dim(` Tool usage will be recorded and learnings extracted after \u2265${MIN_EVENTS_FOR_ANALYSIS} events.`));
8718
+ console.log(chalk18.dim(" Learnings written to CALIBER_LEARNINGS.md."));
8409
8719
  }
8410
8720
  }
8411
8721
  async function learnRemoveCommand() {
8412
8722
  let anyRemoved = false;
8413
8723
  const r1 = removeLearningHooks();
8414
8724
  if (r1.removed) {
8415
- console.log(chalk17.green("\u2713") + " Claude Code learning hooks removed");
8725
+ console.log(chalk18.green("\u2713") + " Claude Code learning hooks removed");
8416
8726
  anyRemoved = true;
8417
8727
  }
8418
8728
  const r2 = removeCursorLearningHooks();
8419
8729
  if (r2.removed) {
8420
- console.log(chalk17.green("\u2713") + " Cursor learning hooks removed");
8730
+ console.log(chalk18.green("\u2713") + " Cursor learning hooks removed");
8421
8731
  anyRemoved = true;
8422
8732
  }
8423
8733
  if (!anyRemoved) {
8424
- console.log(chalk17.dim("No learning hooks found."));
8734
+ console.log(chalk18.dim("No learning hooks found."));
8425
8735
  }
8426
8736
  }
8427
8737
  async function learnStatusCommand() {
@@ -8429,34 +8739,34 @@ async function learnStatusCommand() {
8429
8739
  const cursorInstalled = areCursorLearningHooksInstalled();
8430
8740
  const state = readState2();
8431
8741
  const eventCount = getEventCount();
8432
- console.log(chalk17.bold("Session Learning Status"));
8742
+ console.log(chalk18.bold("Session Learning Status"));
8433
8743
  console.log();
8434
8744
  if (claudeInstalled) {
8435
- console.log(chalk17.green("\u2713") + " Claude Code hooks " + chalk17.green("installed"));
8745
+ console.log(chalk18.green("\u2713") + " Claude Code hooks " + chalk18.green("installed"));
8436
8746
  } else {
8437
- console.log(chalk17.dim("\u2717") + " Claude Code hooks " + chalk17.dim("not installed"));
8747
+ console.log(chalk18.dim("\u2717") + " Claude Code hooks " + chalk18.dim("not installed"));
8438
8748
  }
8439
8749
  if (cursorInstalled) {
8440
- console.log(chalk17.green("\u2713") + " Cursor hooks " + chalk17.green("installed"));
8750
+ console.log(chalk18.green("\u2713") + " Cursor hooks " + chalk18.green("installed"));
8441
8751
  } else {
8442
- console.log(chalk17.dim("\u2717") + " Cursor hooks " + chalk17.dim("not installed"));
8752
+ console.log(chalk18.dim("\u2717") + " Cursor hooks " + chalk18.dim("not installed"));
8443
8753
  }
8444
8754
  if (!claudeInstalled && !cursorInstalled) {
8445
- console.log(chalk17.dim(" Run `caliber learn install` to enable session learning."));
8755
+ console.log(chalk18.dim(" Run `caliber learn install` to enable session learning."));
8446
8756
  }
8447
8757
  console.log();
8448
- console.log(`Events recorded: ${chalk17.cyan(String(eventCount))}`);
8449
- console.log(`Threshold for analysis: ${chalk17.cyan(String(MIN_EVENTS_FOR_ANALYSIS))}`);
8758
+ console.log(`Events recorded: ${chalk18.cyan(String(eventCount))}`);
8759
+ console.log(`Threshold for analysis: ${chalk18.cyan(String(MIN_EVENTS_FOR_ANALYSIS))}`);
8450
8760
  if (state.lastAnalysisTimestamp) {
8451
- console.log(`Last analysis: ${chalk17.cyan(state.lastAnalysisTimestamp)}`);
8761
+ console.log(`Last analysis: ${chalk18.cyan(state.lastAnalysisTimestamp)}`);
8452
8762
  } else {
8453
- console.log(`Last analysis: ${chalk17.dim("none")}`);
8763
+ console.log(`Last analysis: ${chalk18.dim("none")}`);
8454
8764
  }
8455
8765
  const learnedSection = readLearnedSection();
8456
8766
  if (learnedSection) {
8457
8767
  const lineCount = learnedSection.split("\n").filter(Boolean).length;
8458
8768
  console.log(`
8459
- Learned items in CALIBER_LEARNINGS.md: ${chalk17.cyan(String(lineCount))}`);
8769
+ Learned items in CALIBER_LEARNINGS.md: ${chalk18.cyan(String(lineCount))}`);
8460
8770
  }
8461
8771
  }
8462
8772
 
@@ -8530,7 +8840,7 @@ program.command("score").description("Score your current agent config setup (det
8530
8840
  program.command("refresh").description("Update docs based on recent code changes").option("--quiet", "Suppress output (for use in hooks)").option("--dry-run", "Preview changes without writing files").action(tracked("refresh", refreshCommand));
8531
8841
  program.command("hooks").description("Manage auto-refresh hooks (toggle interactively)").option("--install", "Enable all hooks non-interactively").option("--remove", "Disable all hooks non-interactively").action(tracked("hooks", hooksCommand));
8532
8842
  var learn = program.command("learn", { hidden: true }).description("[dev] Session learning \u2014 observe tool usage and extract reusable instructions");
8533
- learn.command("observe").description("Record a tool event from stdin (called by hooks)").option("--failure", "Mark event as a tool failure").action(tracked("learn:observe", learnObserveCommand));
8843
+ learn.command("observe").description("Record a tool event from stdin (called by hooks)").option("--failure", "Mark event as a tool failure").option("--prompt", "Record a user prompt event").action(tracked("learn:observe", learnObserveCommand));
8534
8844
  learn.command("finalize").description("Analyze session events and update CALIBER_LEARNINGS.md (called on SessionEnd)").option("--force", "Skip the running-process check (for manual invocation)").action(tracked("learn:finalize", (opts) => learnFinalizeCommand(opts)));
8535
8845
  learn.command("install").description("Install learning hooks into .claude/settings.json").action(tracked("learn:install", learnInstallCommand));
8536
8846
  learn.command("remove").description("Remove learning hooks from .claude/settings.json").action(tracked("learn:remove", learnRemoveCommand));
@@ -8541,7 +8851,7 @@ import fs33 from "fs";
8541
8851
  import path26 from "path";
8542
8852
  import { fileURLToPath as fileURLToPath2 } from "url";
8543
8853
  import { execSync as execSync14 } from "child_process";
8544
- import chalk18 from "chalk";
8854
+ import chalk19 from "chalk";
8545
8855
  import ora6 from "ora";
8546
8856
  import confirm2 from "@inquirer/confirm";
8547
8857
  var __dirname_vc = path26.dirname(fileURLToPath2(import.meta.url));
@@ -8575,17 +8885,17 @@ async function checkForUpdates() {
8575
8885
  const isInteractive = process.stdin.isTTY === true;
8576
8886
  if (!isInteractive) {
8577
8887
  console.log(
8578
- chalk18.yellow(
8888
+ chalk19.yellow(
8579
8889
  `
8580
8890
  Update available: ${current} -> ${latest}
8581
- Run ${chalk18.bold("npm install -g @rely-ai/caliber")} to upgrade.
8891
+ Run ${chalk19.bold("npm install -g @rely-ai/caliber")} to upgrade.
8582
8892
  `
8583
8893
  )
8584
8894
  );
8585
8895
  return;
8586
8896
  }
8587
8897
  console.log(
8588
- chalk18.yellow(`
8898
+ chalk19.yellow(`
8589
8899
  Update available: ${current} -> ${latest}`)
8590
8900
  );
8591
8901
  const shouldUpdate = await confirm2({ message: "Would you like to update now? (Y/n)", default: true });
@@ -8603,13 +8913,13 @@ Update available: ${current} -> ${latest}`)
8603
8913
  const installed = getInstalledVersion();
8604
8914
  if (installed !== latest) {
8605
8915
  spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
8606
- console.log(chalk18.yellow(`Run ${chalk18.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually.
8916
+ console.log(chalk19.yellow(`Run ${chalk19.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually.
8607
8917
  `));
8608
8918
  return;
8609
8919
  }
8610
- spinner.succeed(chalk18.green(`Updated to ${latest}`));
8920
+ spinner.succeed(chalk19.green(`Updated to ${latest}`));
8611
8921
  const args = process.argv.slice(2);
8612
- console.log(chalk18.dim(`
8922
+ console.log(chalk19.dim(`
8613
8923
  Restarting: caliber ${args.join(" ")}
8614
8924
  `));
8615
8925
  execSync14(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
@@ -8622,11 +8932,11 @@ Restarting: caliber ${args.join(" ")}
8622
8932
  if (err instanceof Error) {
8623
8933
  const stderr = err.stderr;
8624
8934
  const errMsg = stderr ? String(stderr).trim().split("\n").pop() : err.message.split("\n")[0];
8625
- if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk18.dim(` ${errMsg}`));
8935
+ if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk19.dim(` ${errMsg}`));
8626
8936
  }
8627
8937
  console.log(
8628
- chalk18.yellow(
8629
- `Run ${chalk18.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually to upgrade.
8938
+ chalk19.yellow(
8939
+ `Run ${chalk19.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually to upgrade.
8630
8940
  `
8631
8941
  )
8632
8942
  );