@rely-ai/caliber 1.1.0 → 1.1.1

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 +1121 -361
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -51,16 +51,16 @@ var init_constants = __esm({
51
51
 
52
52
  // src/cli.ts
53
53
  import { Command } from "commander";
54
- import fs25 from "fs";
55
- import path22 from "path";
54
+ import fs27 from "fs";
55
+ import path24 from "path";
56
56
  import { fileURLToPath } from "url";
57
57
 
58
58
  // src/commands/onboard.ts
59
- import chalk4 from "chalk";
60
- import ora from "ora";
61
- import readline3 from "readline";
59
+ import chalk5 from "chalk";
60
+ import ora2 from "ora";
61
+ import readline4 from "readline";
62
62
  import select2 from "@inquirer/select";
63
- import fs18 from "fs";
63
+ import fs20 from "fs";
64
64
 
65
65
  // src/fingerprint/index.ts
66
66
  import fs7 from "fs";
@@ -2724,15 +2724,15 @@ function computeGrade(score) {
2724
2724
  // src/scoring/checks/coverage.ts
2725
2725
  import { readFileSync, readdirSync } from "fs";
2726
2726
  import { join } from "path";
2727
- function readFileOrNull(path24) {
2727
+ function readFileOrNull(path26) {
2728
2728
  try {
2729
- return readFileSync(path24, "utf-8");
2729
+ return readFileSync(path26, "utf-8");
2730
2730
  } catch {
2731
2731
  return null;
2732
2732
  }
2733
2733
  }
2734
- function readJsonOrNull(path24) {
2735
- const content = readFileOrNull(path24);
2734
+ function readJsonOrNull(path26) {
2735
+ const content = readFileOrNull(path26);
2736
2736
  if (!content) return null;
2737
2737
  try {
2738
2738
  return JSON.parse(content);
@@ -3088,9 +3088,9 @@ function checkExistence(dir) {
3088
3088
  // src/scoring/checks/quality.ts
3089
3089
  import { readFileSync as readFileSync3 } from "fs";
3090
3090
  import { join as join3 } from "path";
3091
- function readFileOrNull2(path24) {
3091
+ function readFileOrNull2(path26) {
3092
3092
  try {
3093
- return readFileSync3(path24, "utf-8");
3093
+ return readFileSync3(path26, "utf-8");
3094
3094
  } catch {
3095
3095
  return null;
3096
3096
  }
@@ -3241,15 +3241,15 @@ function checkQuality(dir) {
3241
3241
  // src/scoring/checks/accuracy.ts
3242
3242
  import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync } from "fs";
3243
3243
  import { join as join4 } from "path";
3244
- function readFileOrNull3(path24) {
3244
+ function readFileOrNull3(path26) {
3245
3245
  try {
3246
- return readFileSync4(path24, "utf-8");
3246
+ return readFileSync4(path26, "utf-8");
3247
3247
  } catch {
3248
3248
  return null;
3249
3249
  }
3250
3250
  }
3251
- function readJsonOrNull2(path24) {
3252
- const content = readFileOrNull3(path24);
3251
+ function readJsonOrNull2(path26) {
3252
+ const content = readFileOrNull3(path26);
3253
3253
  if (!content) return null;
3254
3254
  try {
3255
3255
  return JSON.parse(content);
@@ -3432,9 +3432,9 @@ function checkAccuracy(dir) {
3432
3432
  // src/scoring/checks/freshness.ts
3433
3433
  import { readFileSync as readFileSync5, statSync as statSync2 } from "fs";
3434
3434
  import { join as join5 } from "path";
3435
- function readFileOrNull4(path24) {
3435
+ function readFileOrNull4(path26) {
3436
3436
  try {
3437
- return readFileSync5(path24, "utf-8");
3437
+ return readFileSync5(path26, "utf-8");
3438
3438
  } catch {
3439
3439
  return null;
3440
3440
  }
@@ -3544,9 +3544,9 @@ function checkFreshness(dir) {
3544
3544
  import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "fs";
3545
3545
  import { execSync as execSync7 } from "child_process";
3546
3546
  import { join as join6 } from "path";
3547
- function readFileOrNull5(path24) {
3547
+ function readFileOrNull5(path26) {
3548
3548
  try {
3549
- return readFileSync6(path24, "utf-8");
3549
+ return readFileSync6(path26, "utf-8");
3550
3550
  } catch {
3551
3551
  return null;
3552
3552
  }
@@ -3853,10 +3853,758 @@ function displayScoreDelta(before, after) {
3853
3853
  }
3854
3854
  }
3855
3855
 
3856
+ // src/mcp/index.ts
3857
+ import chalk4 from "chalk";
3858
+ import ora from "ora";
3859
+ import readline3 from "readline";
3860
+ import fs19 from "fs";
3861
+ import path18 from "path";
3862
+
3863
+ // src/mcp/deps.ts
3864
+ import fs18 from "fs";
3865
+ import path17 from "path";
3866
+ function extractAllDeps(dir) {
3867
+ const deps = /* @__PURE__ */ new Set();
3868
+ parsePackageJson(dir, deps);
3869
+ parseRequirementsTxt(dir, deps);
3870
+ parsePyprojectToml(dir, deps);
3871
+ parseGoMod(dir, deps);
3872
+ parseCargoToml(dir, deps);
3873
+ parseGemfile(dir, deps);
3874
+ parseComposerJson(dir, deps);
3875
+ return Array.from(deps);
3876
+ }
3877
+ function readFileSafe(filePath) {
3878
+ try {
3879
+ if (fs18.existsSync(filePath)) {
3880
+ return fs18.readFileSync(filePath, "utf-8");
3881
+ }
3882
+ } catch {
3883
+ }
3884
+ return null;
3885
+ }
3886
+ function parsePackageJson(dir, deps) {
3887
+ const content = readFileSafe(path17.join(dir, "package.json"));
3888
+ if (!content) return;
3889
+ try {
3890
+ const pkg3 = JSON.parse(content);
3891
+ const allDeps = {
3892
+ ...pkg3.dependencies,
3893
+ ...pkg3.devDependencies
3894
+ };
3895
+ for (const name of Object.keys(allDeps)) {
3896
+ deps.add(name);
3897
+ }
3898
+ } catch {
3899
+ }
3900
+ }
3901
+ function parseRequirementsTxt(dir, deps) {
3902
+ const content = readFileSafe(path17.join(dir, "requirements.txt"));
3903
+ if (!content) return;
3904
+ for (const line of content.split("\n")) {
3905
+ const trimmed = line.trim();
3906
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("-")) continue;
3907
+ const match = trimmed.match(/^([a-zA-Z0-9_-]+(?:\[[^\]]*\])?)/);
3908
+ if (match) {
3909
+ deps.add(match[1].replace(/\[.*\]/, "").toLowerCase());
3910
+ }
3911
+ }
3912
+ }
3913
+ function parsePyprojectToml(dir, deps) {
3914
+ const content = readFileSafe(path17.join(dir, "pyproject.toml"));
3915
+ if (!content) return;
3916
+ const depsMatch = content.match(/\bdependencies\s*=\s*\[([\s\S]*?)\]/);
3917
+ if (depsMatch) {
3918
+ const items = depsMatch[1].matchAll(/"([a-zA-Z0-9_-]+)/g);
3919
+ for (const m of items) {
3920
+ deps.add(m[1].toLowerCase());
3921
+ }
3922
+ }
3923
+ }
3924
+ function parseGoMod(dir, deps) {
3925
+ const content = readFileSafe(path17.join(dir, "go.mod"));
3926
+ if (!content) return;
3927
+ const requireBlock = content.match(/require\s*\(([\s\S]*?)\)/g);
3928
+ if (requireBlock) {
3929
+ for (const block of requireBlock) {
3930
+ const lines = block.split("\n");
3931
+ for (const line of lines) {
3932
+ const match = line.trim().match(/^([a-zA-Z0-9./\-_]+)\s/);
3933
+ if (match && !match[1].startsWith("//") && match[1].includes("/")) {
3934
+ const parts = match[1].split("/");
3935
+ deps.add(parts[parts.length - 1]);
3936
+ }
3937
+ }
3938
+ }
3939
+ }
3940
+ }
3941
+ function parseCargoToml(dir, deps) {
3942
+ const content = readFileSafe(path17.join(dir, "Cargo.toml"));
3943
+ if (!content) return;
3944
+ const sections = content.split(/\[/);
3945
+ for (const section of sections) {
3946
+ if (section.startsWith("dependencies]") || section.startsWith("dev-dependencies]")) {
3947
+ const lines = section.split("\n").slice(1);
3948
+ for (const line of lines) {
3949
+ if (line.startsWith("[")) break;
3950
+ const match = line.match(/^([a-zA-Z0-9_-]+)\s*=/);
3951
+ if (match) {
3952
+ deps.add(match[1]);
3953
+ }
3954
+ }
3955
+ }
3956
+ }
3957
+ }
3958
+ function parseGemfile(dir, deps) {
3959
+ const content = readFileSafe(path17.join(dir, "Gemfile"));
3960
+ if (!content) return;
3961
+ const gemPattern = /gem\s+['"]([a-zA-Z0-9_-]+)['"]/g;
3962
+ let match;
3963
+ while ((match = gemPattern.exec(content)) !== null) {
3964
+ deps.add(match[1]);
3965
+ }
3966
+ }
3967
+ function parseComposerJson(dir, deps) {
3968
+ const content = readFileSafe(path17.join(dir, "composer.json"));
3969
+ if (!content) return;
3970
+ try {
3971
+ const composer = JSON.parse(content);
3972
+ const allDeps = {
3973
+ ...composer.require,
3974
+ ...composer["require-dev"]
3975
+ };
3976
+ for (const name of Object.keys(allDeps)) {
3977
+ if (name !== "php" && !name.startsWith("ext-")) {
3978
+ deps.add(name);
3979
+ }
3980
+ }
3981
+ } catch {
3982
+ }
3983
+ }
3984
+
3985
+ // src/mcp/prompts.ts
3986
+ var CLASSIFY_DEPS_PROMPT = `You classify software dependencies into two categories:
3987
+
3988
+ **Tools** (MCP-worthy): Services, platforms, APIs, databases, SaaS products, and cloud services that have their own web dashboards, APIs, or external infrastructure. Examples: supabase, stripe, sentry, datadog, firebase, mongodb, redis, slack, linear, github, vercel, aws-sdk, twilio, sendgrid, algolia, elasticsearch, prisma, planetscale, neon, clerk, auth0.
3989
+
3990
+ **Libraries** (skip): Utility packages, frameworks, build tools, test runners, and local-only code that does NOT connect to an external service. Examples: lodash, react, express, vitest, webpack, zod, chalk, commander, typescript, eslint, prettier, axios, dayjs, uuid.
3991
+
3992
+ Given a list of dependencies, return ONLY the tool dependencies as a JSON array of strings.
3993
+ Return ONLY the JSON array, no explanation.`;
3994
+ var SCORE_MCP_PROMPT = `You evaluate MCP (Model Context Protocol) server candidates for relevance to a software project.
3995
+
3996
+ Score each candidate from 0-100 based on:
3997
+ - **Relevance** (40%): How directly does it match the project's detected tool dependencies?
3998
+ - **Capabilities** (30%): Does it provide meaningful read+write operations (not just read-only)?
3999
+ - **Quality signals** (30%): Stars, recent activity, vendor/official status
4000
+
4001
+ Return a JSON array where each element has:
4002
+ - "index": the candidate's index number
4003
+ - "score": relevance score 0-100
4004
+ - "reason": one-liner explaining the score (max 80 chars)
4005
+
4006
+ Be selective. Only score candidates that would genuinely help developers working on this project.
4007
+ Return ONLY the JSON array.`;
4008
+ var EXTRACT_CONFIG_PROMPT = `You extract MCP server configuration from a README file.
4009
+
4010
+ Look for the MCP server's configuration block \u2014 typically a JSON snippet showing how to add it to claude_desktop_config.json, .mcp.json, or similar.
4011
+
4012
+ Return a JSON object with:
4013
+ - "command": the executable command (e.g., "npx", "uvx", "node", "docker")
4014
+ - "args": array of arguments (e.g., ["-y", "@supabase/mcp-server"])
4015
+ - "env": array of objects, each with:
4016
+ - "key": environment variable name (e.g., "SUPABASE_ACCESS_TOKEN")
4017
+ - "description": brief description of what this value is (e.g., "Personal access token from dashboard")
4018
+ - "required": boolean
4019
+
4020
+ If the README shows multiple configuration methods, prefer npx > uvx > node > docker.
4021
+ If you cannot determine the configuration, return {"command": "", "args": [], "env": []}.
4022
+ Return ONLY the JSON object.`;
4023
+
4024
+ // src/mcp/classify.ts
4025
+ async function classifyDeps(allDeps) {
4026
+ if (allDeps.length === 0) return [];
4027
+ try {
4028
+ const result = await llmJsonCall({
4029
+ system: CLASSIFY_DEPS_PROMPT,
4030
+ prompt: `Dependencies:
4031
+ ${JSON.stringify(allDeps)}`,
4032
+ maxTokens: 2e3
4033
+ });
4034
+ if (!Array.isArray(result)) return [];
4035
+ const inputLower = new Set(allDeps.map((d) => d.toLowerCase()));
4036
+ return result.filter((d) => typeof d === "string" && inputLower.has(d.toLowerCase()));
4037
+ } catch {
4038
+ return fallbackClassify(allDeps);
4039
+ }
4040
+ }
4041
+ function fallbackClassify(deps) {
4042
+ const utilityPatterns = [
4043
+ /^@types\//,
4044
+ /^eslint/,
4045
+ /^prettier/,
4046
+ /^@typescript-eslint\//,
4047
+ /^@commitlint\//,
4048
+ /^@eslint\//,
4049
+ /^webpack/,
4050
+ /^rollup/,
4051
+ /^babel/,
4052
+ /^@babel\//,
4053
+ /^postcss/,
4054
+ /^tailwindcss/,
4055
+ /^autoprefixer/
4056
+ ];
4057
+ const utilities = /* @__PURE__ */ new Set([
4058
+ "typescript",
4059
+ "tslib",
4060
+ "ts-node",
4061
+ "tsx",
4062
+ "prettier",
4063
+ "eslint",
4064
+ "rimraf",
4065
+ "cross-env",
4066
+ "dotenv",
4067
+ "nodemon",
4068
+ "husky",
4069
+ "lint-staged",
4070
+ "commitlint",
4071
+ "chalk",
4072
+ "ora",
4073
+ "commander",
4074
+ "yargs",
4075
+ "meow",
4076
+ "inquirer",
4077
+ "glob",
4078
+ "minimatch",
4079
+ "micromatch",
4080
+ "diff",
4081
+ "semver",
4082
+ "uuid",
4083
+ "nanoid",
4084
+ "debug",
4085
+ "ms",
4086
+ "lodash",
4087
+ "underscore",
4088
+ "ramda",
4089
+ "tsup",
4090
+ "esbuild",
4091
+ "rollup",
4092
+ "webpack",
4093
+ "vite",
4094
+ "parcel",
4095
+ "vitest",
4096
+ "jest",
4097
+ "mocha",
4098
+ "chai",
4099
+ "ava",
4100
+ "tap",
4101
+ "fs-extra",
4102
+ "mkdirp",
4103
+ "del",
4104
+ "path-to-regexp",
4105
+ "strip-ansi",
4106
+ "ansi-colors",
4107
+ "react",
4108
+ "react-dom",
4109
+ "next",
4110
+ "vue",
4111
+ "angular",
4112
+ "svelte",
4113
+ "express",
4114
+ "fastify",
4115
+ "koa",
4116
+ "hapi",
4117
+ "zod",
4118
+ "joi",
4119
+ "yup",
4120
+ "ajv",
4121
+ "axios",
4122
+ "node-fetch",
4123
+ "got",
4124
+ "undici",
4125
+ "moment",
4126
+ "dayjs",
4127
+ "date-fns",
4128
+ "luxon"
4129
+ ]);
4130
+ return deps.filter((d) => {
4131
+ const lower = d.toLowerCase();
4132
+ if (utilities.has(lower)) return false;
4133
+ if (utilityPatterns.some((p) => p.test(lower))) return false;
4134
+ return true;
4135
+ });
4136
+ }
4137
+
4138
+ // src/mcp/search.ts
4139
+ var AWESOME_MCP_URL = "https://raw.githubusercontent.com/punkpeye/awesome-mcp-servers/main/README.md";
4140
+ var GITHUB_SEARCH_URL = "https://github.com/search";
4141
+ var SEARCH_HEADERS = {
4142
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
4143
+ "Accept": "text/html"
4144
+ };
4145
+ function parseGitHubSearchHtml(html) {
4146
+ try {
4147
+ const scriptMatch = html.match(/<script type="application\/json" data-target="react-app\.embeddedData">([\s\S]*?)<\/script>/);
4148
+ if (!scriptMatch) {
4149
+ return [];
4150
+ }
4151
+ const data = JSON.parse(scriptMatch[1]);
4152
+ const results = data?.payload?.results;
4153
+ if (!Array.isArray(results)) {
4154
+ return [];
4155
+ }
4156
+ return results.map((r) => {
4157
+ const repo = r.repo?.repository;
4158
+ const ownerLogin = repo?.owner_login || "";
4159
+ const name = repo?.name || "";
4160
+ const description = typeof r.hl_trunc_description === "string" ? r.hl_trunc_description.replace(/<\/?em>/g, "") : null;
4161
+ return {
4162
+ full_name: `${ownerLogin}/${name}`,
4163
+ description,
4164
+ stargazers_count: typeof r.followers === "number" ? r.followers : 0,
4165
+ pushed_at: repo?.updated_at || "",
4166
+ owner_login: ownerLogin
4167
+ };
4168
+ });
4169
+ } catch {
4170
+ return [];
4171
+ }
4172
+ }
4173
+ async function searchAllMcpSources(toolDeps) {
4174
+ if (toolDeps.length === 0) return [];
4175
+ const searches = [
4176
+ searchAwesomeMcpLists(toolDeps),
4177
+ ...toolDeps.map((dep) => searchGitHub(dep)),
4178
+ ...toolDeps.map((dep) => searchVendorOrg(dep))
4179
+ ];
4180
+ const results = await Promise.all(searches);
4181
+ const seen = /* @__PURE__ */ new Map();
4182
+ for (const batch of results) {
4183
+ for (const candidate of batch) {
4184
+ const key = candidate.repoFullName.toLowerCase();
4185
+ const existing = seen.get(key);
4186
+ if (!existing || candidate.vendor || candidate.stars > existing.stars) {
4187
+ seen.set(key, candidate);
4188
+ }
4189
+ }
4190
+ }
4191
+ return Array.from(seen.values());
4192
+ }
4193
+ async function searchAwesomeMcpLists(toolDeps) {
4194
+ try {
4195
+ const resp = await fetch(AWESOME_MCP_URL, {
4196
+ signal: AbortSignal.timeout(1e4)
4197
+ });
4198
+ if (!resp.ok) return [];
4199
+ const markdown = await resp.text();
4200
+ const candidates = [];
4201
+ const depLower = toolDeps.map((d) => d.toLowerCase().replace(/^@[^/]+\//, ""));
4202
+ const itemPattern = /^[-*]\s+\[([^\]]+)\]\(([^)]+)\)\s*[-–—:]\s*(.*)/gm;
4203
+ let match;
4204
+ while ((match = itemPattern.exec(markdown)) !== null) {
4205
+ const [, name, url, description] = match;
4206
+ if (!url.includes("github.com")) continue;
4207
+ const text = `${name} ${description}`.toLowerCase();
4208
+ const matchedDep = depLower.find((d) => text.includes(d));
4209
+ if (!matchedDep) continue;
4210
+ const repoMatch = url.match(/github\.com\/([^/]+\/[^/]+)/);
4211
+ if (!repoMatch) continue;
4212
+ candidates.push({
4213
+ name: name.trim(),
4214
+ repoFullName: repoMatch[1],
4215
+ url: url.trim(),
4216
+ description: description.trim().slice(0, 200),
4217
+ stars: 0,
4218
+ lastPush: "",
4219
+ vendor: false,
4220
+ score: 0,
4221
+ reason: "",
4222
+ matchedDep: toolDeps.find((d) => d.toLowerCase().replace(/^@[^/]+\//, "") === matchedDep) || matchedDep
4223
+ });
4224
+ }
4225
+ return candidates;
4226
+ } catch {
4227
+ return [];
4228
+ }
4229
+ }
4230
+ async function searchGitHub(dep) {
4231
+ const depName = dep.replace(/^@[^/]+\//, "");
4232
+ try {
4233
+ const query = encodeURIComponent(`${depName} mcp server`);
4234
+ const url = `${GITHUB_SEARCH_URL}?q=${query}&type=repositories&s=stars&o=desc`;
4235
+ const resp = await fetch(url, {
4236
+ signal: AbortSignal.timeout(1e4),
4237
+ headers: SEARCH_HEADERS
4238
+ });
4239
+ if (!resp.ok) return [];
4240
+ const html = await resp.text();
4241
+ const repos = parseGitHubSearchHtml(html).slice(0, 5);
4242
+ return repos.map((repo) => ({
4243
+ name: repo.full_name.split("/")[1],
4244
+ repoFullName: repo.full_name,
4245
+ url: `https://github.com/${repo.full_name}`,
4246
+ description: repo.description || "",
4247
+ stars: repo.stargazers_count,
4248
+ lastPush: repo.pushed_at,
4249
+ vendor: false,
4250
+ score: 0,
4251
+ reason: "",
4252
+ matchedDep: dep
4253
+ }));
4254
+ } catch {
4255
+ return [];
4256
+ }
4257
+ }
4258
+ async function searchVendorOrg(dep) {
4259
+ const depName = dep.replace(/^@([^/]+)\/.*/, "$1").replace(/^@/, "");
4260
+ const orgName = depName.toLowerCase();
4261
+ try {
4262
+ const query = encodeURIComponent(`mcp org:${orgName}`);
4263
+ const url = `${GITHUB_SEARCH_URL}?q=${query}&type=repositories&s=stars&o=desc`;
4264
+ const resp = await fetch(url, {
4265
+ signal: AbortSignal.timeout(1e4),
4266
+ headers: SEARCH_HEADERS
4267
+ });
4268
+ if (!resp.ok) return [];
4269
+ const html = await resp.text();
4270
+ const repos = parseGitHubSearchHtml(html).slice(0, 5);
4271
+ return repos.map((repo) => ({
4272
+ name: repo.full_name.split("/")[1],
4273
+ repoFullName: repo.full_name,
4274
+ url: `https://github.com/${repo.full_name}`,
4275
+ description: repo.description || "",
4276
+ stars: repo.stargazers_count,
4277
+ lastPush: repo.pushed_at,
4278
+ vendor: repo.owner_login.toLowerCase() === orgName,
4279
+ score: 0,
4280
+ reason: "",
4281
+ matchedDep: dep
4282
+ }));
4283
+ } catch {
4284
+ return [];
4285
+ }
4286
+ }
4287
+
4288
+ // src/mcp/validate.ts
4289
+ var MIN_STARS = 100;
4290
+ var MAX_AGE_DAYS = 180;
4291
+ async function validateAndScore(candidates, toolDeps) {
4292
+ const qualityFiltered = candidates.filter((c) => {
4293
+ if (c.vendor) return true;
4294
+ if (c.stars > 0 && c.stars < MIN_STARS) return false;
4295
+ if (c.lastPush) {
4296
+ const pushDate = new Date(c.lastPush);
4297
+ const daysAgo = (Date.now() - pushDate.getTime()) / (1e3 * 60 * 60 * 24);
4298
+ if (daysAgo > MAX_AGE_DAYS) return false;
4299
+ }
4300
+ return true;
4301
+ });
4302
+ if (qualityFiltered.length === 0) return [];
4303
+ try {
4304
+ return await scoreWithLLM(qualityFiltered, toolDeps);
4305
+ } catch {
4306
+ return qualityFiltered.slice(0, 5).map((c) => ({
4307
+ ...c,
4308
+ score: 50,
4309
+ reason: c.description.slice(0, 80)
4310
+ }));
4311
+ }
4312
+ }
4313
+ async function scoreWithLLM(candidates, toolDeps) {
4314
+ const candidateList = candidates.map((c, i) => {
4315
+ const vendorTag = c.vendor ? " [VENDOR/OFFICIAL]" : "";
4316
+ return `${i}. "${c.name}"${vendorTag} (${c.stars} stars) \u2014 ${c.description.slice(0, 100)}`;
4317
+ }).join("\n");
4318
+ const scored = await llmJsonCall({
4319
+ system: SCORE_MCP_PROMPT,
4320
+ prompt: `TOOL DEPENDENCIES IN PROJECT:
4321
+ ${toolDeps.join(", ")}
4322
+
4323
+ MCP SERVER CANDIDATES:
4324
+ ${candidateList}`,
4325
+ maxTokens: 4e3
4326
+ });
4327
+ if (!Array.isArray(scored)) return [];
4328
+ return scored.filter((s) => s.score >= 60 && s.index >= 0 && s.index < candidates.length).sort((a, b) => b.score - a.score).slice(0, 5).map((s) => ({
4329
+ ...candidates[s.index],
4330
+ score: s.score,
4331
+ reason: s.reason || candidates[s.index].description.slice(0, 80)
4332
+ }));
4333
+ }
4334
+
4335
+ // src/mcp/config-extract.ts
4336
+ async function fetchReadme(repoFullName) {
4337
+ try {
4338
+ const resp = await fetch(
4339
+ `https://raw.githubusercontent.com/${repoFullName}/HEAD/README.md`,
4340
+ { signal: AbortSignal.timeout(1e4) }
4341
+ );
4342
+ if (resp.ok) return await resp.text();
4343
+ } catch {
4344
+ }
4345
+ try {
4346
+ const resp = await fetch(
4347
+ `https://raw.githubusercontent.com/${repoFullName}/main/README.md`,
4348
+ { signal: AbortSignal.timeout(1e4) }
4349
+ );
4350
+ if (resp.ok) return await resp.text();
4351
+ } catch {
4352
+ }
4353
+ return null;
4354
+ }
4355
+ async function extractMcpConfig(readme, serverName) {
4356
+ try {
4357
+ const truncated = readme.length > 15e3 ? readme.slice(0, 15e3) : readme;
4358
+ const result = await llmJsonCall({
4359
+ system: EXTRACT_CONFIG_PROMPT,
4360
+ prompt: `MCP Server: ${serverName}
4361
+
4362
+ README:
4363
+ ${truncated}`,
4364
+ maxTokens: 2e3
4365
+ });
4366
+ if (!result || !result.command) return null;
4367
+ return {
4368
+ command: result.command,
4369
+ args: Array.isArray(result.args) ? result.args : [],
4370
+ env: Array.isArray(result.env) ? result.env : []
4371
+ };
4372
+ } catch {
4373
+ return null;
4374
+ }
4375
+ }
4376
+
4377
+ // src/mcp/index.ts
4378
+ async function discoverAndInstallMcps(targetAgent, fingerprint, dir) {
4379
+ console.log(chalk4.hex("#6366f1").bold("\n MCP Server Discovery\n"));
4380
+ const spinner = ora("Analyzing dependencies for tool integrations...").start();
4381
+ const allDeps = extractAllDeps(dir);
4382
+ if (allDeps.length === 0) {
4383
+ spinner.succeed(chalk4.dim("No dependencies found \u2014 skipping MCP discovery"));
4384
+ return { installed: 0, names: [] };
4385
+ }
4386
+ const toolDeps = await classifyDeps(allDeps);
4387
+ if (toolDeps.length === 0) {
4388
+ spinner.succeed(chalk4.dim("No tool dependencies detected \u2014 skipping MCP discovery"));
4389
+ console.log(chalk4.dim(` All deps (${allDeps.length}): ${allDeps.slice(0, 10).join(", ")}${allDeps.length > 10 ? "..." : ""}`));
4390
+ return { installed: 0, names: [] };
4391
+ }
4392
+ spinner.succeed(`Found ${toolDeps.length} tool dependenc${toolDeps.length === 1 ? "y" : "ies"}: ${toolDeps.join(", ")}`);
4393
+ console.log(chalk4.dim(` All deps (${allDeps.length}): ${allDeps.slice(0, 10).join(", ")}${allDeps.length > 10 ? "..." : ""}`));
4394
+ console.log(chalk4.dim(` Classified as tools: ${toolDeps.join(", ")}`));
4395
+ const existingMcps = getExistingMcpNames(fingerprint, targetAgent);
4396
+ const filteredDeps = toolDeps.filter((d) => {
4397
+ const lower = d.toLowerCase().replace(/^@[^/]+\//, "");
4398
+ return !existingMcps.some((name) => name.includes(lower) || lower.includes(name));
4399
+ });
4400
+ if (filteredDeps.length === 0) {
4401
+ console.log(chalk4.dim(" All detected tools already have MCP servers configured."));
4402
+ console.log(chalk4.dim(` Existing MCPs: ${existingMcps.join(", ")}`));
4403
+ return { installed: 0, names: [] };
4404
+ }
4405
+ const searchSpinner = ora("Searching for MCP servers...").start();
4406
+ const candidates = await searchAllMcpSources(filteredDeps);
4407
+ if (candidates.length === 0) {
4408
+ searchSpinner.succeed(chalk4.dim("No MCP servers found for your dependencies"));
4409
+ console.log(chalk4.dim(` Searched for: ${filteredDeps.join(", ")}`));
4410
+ return { installed: 0, names: [] };
4411
+ }
4412
+ searchSpinner.succeed(`Found ${candidates.length} candidate${candidates.length === 1 ? "" : "s"}`);
4413
+ console.log(chalk4.dim(` Sources: ${candidates.map((c) => c.repoFullName).join(", ")}`));
4414
+ const scoreSpinner = ora("Scoring MCP candidates...").start();
4415
+ const scored = await validateAndScore(candidates, filteredDeps);
4416
+ if (scored.length === 0) {
4417
+ scoreSpinner.succeed(chalk4.dim("No quality MCP servers passed validation"));
4418
+ console.log(chalk4.dim(` Candidates checked: ${candidates.map((c) => c.name).join(", ")}`));
4419
+ return { installed: 0, names: [] };
4420
+ }
4421
+ scoreSpinner.succeed(`${scored.length} quality MCP server${scored.length === 1 ? "" : "s"} found`);
4422
+ console.log(chalk4.dim(` Scored: ${scored.map((c) => `${c.name} (${c.score})`).join(", ")}`));
4423
+ const selected = await interactiveSelect(scored);
4424
+ if (!selected || selected.length === 0) {
4425
+ return { installed: 0, names: [] };
4426
+ }
4427
+ const mcpServers = {};
4428
+ const installedNames = [];
4429
+ for (const mcp of selected) {
4430
+ console.log(chalk4.bold(`
4431
+ Configuring ${mcp.name}...`));
4432
+ const readme = await fetchReadme(mcp.repoFullName);
4433
+ if (!readme) {
4434
+ console.log(chalk4.yellow(` Could not fetch README for ${mcp.repoFullName} \u2014 skipping`));
4435
+ console.log(chalk4.dim(` Manual setup: ${mcp.url}`));
4436
+ continue;
4437
+ }
4438
+ const config = await extractMcpConfig(readme, mcp.name);
4439
+ if (!config || !config.command) {
4440
+ console.log(chalk4.yellow(` Could not extract config for ${mcp.name} \u2014 skipping`));
4441
+ console.log(chalk4.dim(` Manual setup: ${mcp.url}`));
4442
+ continue;
4443
+ }
4444
+ const env = {};
4445
+ for (const envVar of config.env) {
4446
+ if (!envVar.required) continue;
4447
+ const value = await promptInput2(` ? ${envVar.key} (${envVar.description})`);
4448
+ if (value) {
4449
+ env[envVar.key] = value;
4450
+ }
4451
+ }
4452
+ const serverConfig = {
4453
+ command: config.command
4454
+ };
4455
+ if (config.args.length > 0) serverConfig.args = config.args;
4456
+ if (Object.keys(env).length > 0) serverConfig.env = env;
4457
+ mcpServers[mcp.name] = serverConfig;
4458
+ installedNames.push(mcp.name);
4459
+ console.log(` ${chalk4.green("\u2713")} ${mcp.name} configured`);
4460
+ }
4461
+ if (installedNames.length === 0) {
4462
+ return { installed: 0, names: [] };
4463
+ }
4464
+ if (targetAgent === "claude" || targetAgent === "both") {
4465
+ writeMcpJson(path18.join(dir, ".mcp.json"), mcpServers);
4466
+ }
4467
+ if (targetAgent === "cursor" || targetAgent === "both") {
4468
+ const cursorDir = path18.join(dir, ".cursor");
4469
+ if (!fs19.existsSync(cursorDir)) fs19.mkdirSync(cursorDir, { recursive: true });
4470
+ writeMcpJson(path18.join(cursorDir, "mcp.json"), mcpServers);
4471
+ }
4472
+ return { installed: installedNames.length, names: installedNames };
4473
+ }
4474
+ function writeMcpJson(filePath, mcpServers) {
4475
+ let existing = {};
4476
+ try {
4477
+ if (fs19.existsSync(filePath)) {
4478
+ const parsed = JSON.parse(fs19.readFileSync(filePath, "utf-8"));
4479
+ if (parsed.mcpServers) existing = parsed.mcpServers;
4480
+ }
4481
+ } catch {
4482
+ }
4483
+ const merged = { ...existing, ...mcpServers };
4484
+ fs19.writeFileSync(filePath, JSON.stringify({ mcpServers: merged }, null, 2) + "\n");
4485
+ }
4486
+ function getExistingMcpNames(fingerprint, targetAgent) {
4487
+ const names = [];
4488
+ if (targetAgent === "claude" || targetAgent === "both") {
4489
+ if (fingerprint.existingConfigs.claudeMcpServers) {
4490
+ names.push(...Object.keys(fingerprint.existingConfigs.claudeMcpServers).map((k) => k.toLowerCase()));
4491
+ }
4492
+ }
4493
+ if (targetAgent === "cursor" || targetAgent === "both") {
4494
+ if (fingerprint.existingConfigs.cursorMcpServers) {
4495
+ names.push(...Object.keys(fingerprint.existingConfigs.cursorMcpServers).map((k) => k.toLowerCase()));
4496
+ }
4497
+ }
4498
+ return names;
4499
+ }
4500
+ function promptInput2(question) {
4501
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
4502
+ return new Promise((resolve2) => {
4503
+ rl.question(chalk4.cyan(`${question}: `), (answer) => {
4504
+ rl.close();
4505
+ resolve2(answer.trim());
4506
+ });
4507
+ });
4508
+ }
4509
+ async function interactiveSelect(candidates) {
4510
+ if (!process.stdin.isTTY) {
4511
+ console.log(chalk4.bold("\n Available MCP servers:\n"));
4512
+ for (const c of candidates) {
4513
+ const vendorTag = c.vendor ? chalk4.blue(" (vendor)") : "";
4514
+ console.log(` ${String(c.score).padStart(3)} ${c.name}${vendorTag} ${chalk4.dim(c.reason)}`);
4515
+ }
4516
+ console.log("");
4517
+ return null;
4518
+ }
4519
+ const selected = /* @__PURE__ */ new Set();
4520
+ let cursor = 0;
4521
+ const { stdin, stdout } = process;
4522
+ let lineCount = 0;
4523
+ function render() {
4524
+ const lines = [];
4525
+ lines.push(chalk4.bold(" Select MCP servers to install:"));
4526
+ lines.push("");
4527
+ for (let i = 0; i < candidates.length; i++) {
4528
+ const c = candidates[i];
4529
+ const check = selected.has(i) ? chalk4.green("[x]") : "[ ]";
4530
+ const ptr = i === cursor ? chalk4.cyan(">") : " ";
4531
+ const scoreColor = c.score >= 90 ? chalk4.green : c.score >= 70 ? chalk4.yellow : chalk4.dim;
4532
+ const vendorTag = c.vendor ? chalk4.blue(" (vendor)") : "";
4533
+ lines.push(` ${ptr} ${check} ${scoreColor(String(c.score).padStart(3))} ${c.name}${vendorTag} ${chalk4.dim(c.reason.slice(0, 40))}`);
4534
+ }
4535
+ lines.push("");
4536
+ lines.push(chalk4.dim(" \u2191\u2193 navigate \u23B5 toggle a all n none \u23CE install q skip"));
4537
+ return lines.join("\n");
4538
+ }
4539
+ function draw(initial) {
4540
+ if (!initial && lineCount > 0) {
4541
+ stdout.write(`\x1B[${lineCount}A`);
4542
+ }
4543
+ stdout.write("\x1B[0J");
4544
+ const output = render();
4545
+ stdout.write(output + "\n");
4546
+ lineCount = output.split("\n").length;
4547
+ }
4548
+ return new Promise((resolve2) => {
4549
+ console.log("");
4550
+ draw(true);
4551
+ stdin.setRawMode(true);
4552
+ stdin.resume();
4553
+ stdin.setEncoding("utf8");
4554
+ function cleanup() {
4555
+ stdin.removeListener("data", onData);
4556
+ stdin.setRawMode(false);
4557
+ stdin.pause();
4558
+ }
4559
+ function onData(key) {
4560
+ switch (key) {
4561
+ case "\x1B[A":
4562
+ cursor = (cursor - 1 + candidates.length) % candidates.length;
4563
+ draw(false);
4564
+ break;
4565
+ case "\x1B[B":
4566
+ cursor = (cursor + 1) % candidates.length;
4567
+ draw(false);
4568
+ break;
4569
+ case " ":
4570
+ selected.has(cursor) ? selected.delete(cursor) : selected.add(cursor);
4571
+ draw(false);
4572
+ break;
4573
+ case "a":
4574
+ candidates.forEach((_, i) => selected.add(i));
4575
+ draw(false);
4576
+ break;
4577
+ case "n":
4578
+ selected.clear();
4579
+ draw(false);
4580
+ break;
4581
+ case "\r":
4582
+ case "\n":
4583
+ cleanup();
4584
+ if (selected.size === 0) {
4585
+ console.log(chalk4.dim("\n No MCP servers selected.\n"));
4586
+ resolve2(null);
4587
+ } else {
4588
+ resolve2(Array.from(selected).sort().map((i) => candidates[i]));
4589
+ }
4590
+ break;
4591
+ case "q":
4592
+ case "\x1B":
4593
+ case "":
4594
+ cleanup();
4595
+ console.log(chalk4.dim("\n Skipped MCP server installation.\n"));
4596
+ resolve2(null);
4597
+ break;
4598
+ }
4599
+ }
4600
+ stdin.on("data", onData);
4601
+ });
4602
+ }
4603
+
3856
4604
  // src/commands/onboard.ts
3857
4605
  async function initCommand(options) {
3858
- const brand = chalk4.hex("#EB9D83");
3859
- const title = chalk4.hex("#83D1EB");
4606
+ const brand = chalk5.hex("#EB9D83");
4607
+ const title = chalk5.hex("#83D1EB");
3860
4608
  console.log(brand.bold(`
3861
4609
  \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
3862
4610
  \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
@@ -3865,19 +4613,19 @@ async function initCommand(options) {
3865
4613
  \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
3866
4614
  \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
3867
4615
  `));
3868
- console.log(chalk4.dim(" Onboard your project for AI-assisted development\n"));
4616
+ console.log(chalk5.dim(" Onboard your project for AI-assisted development\n"));
3869
4617
  console.log(title.bold(" Welcome to Caliber\n"));
3870
- console.log(chalk4.dim(" Caliber analyzes your codebase and creates tailored config files"));
3871
- console.log(chalk4.dim(" so your AI coding agents understand your project from day one.\n"));
4618
+ console.log(chalk5.dim(" Caliber analyzes your codebase and creates tailored config files"));
4619
+ console.log(chalk5.dim(" so your AI coding agents understand your project from day one.\n"));
3872
4620
  console.log(title.bold(" How onboarding works:\n"));
3873
- console.log(chalk4.dim(" 1. Connect Set up your LLM provider"));
3874
- console.log(chalk4.dim(" 2. Discover Analyze your code, dependencies, and structure"));
3875
- console.log(chalk4.dim(" 3. Generate Create config files tailored to your project"));
3876
- console.log(chalk4.dim(" 4. Review Preview, refine, and apply the changes\n"));
4621
+ console.log(chalk5.dim(" 1. Connect Set up your LLM provider"));
4622
+ console.log(chalk5.dim(" 2. Discover Analyze your code, dependencies, and structure"));
4623
+ console.log(chalk5.dim(" 3. Generate Create config files tailored to your project"));
4624
+ console.log(chalk5.dim(" 4. Review Preview, refine, and apply the changes\n"));
3877
4625
  console.log(title.bold(" Step 1/4 \u2014 Connect your LLM\n"));
3878
4626
  let config = loadConfig();
3879
4627
  if (!config) {
3880
- console.log(chalk4.dim(" No LLM provider set yet. Choose how to run Caliber:\n"));
4628
+ console.log(chalk5.dim(" No LLM provider set yet. Choose how to run Caliber:\n"));
3881
4629
  try {
3882
4630
  await runInteractiveProviderSetup({
3883
4631
  selectMessage: "How do you want to use Caliber? (choose LLM provider)"
@@ -3888,23 +4636,23 @@ async function initCommand(options) {
3888
4636
  }
3889
4637
  config = loadConfig();
3890
4638
  if (!config) {
3891
- console.log(chalk4.red(" Setup was cancelled or failed.\n"));
4639
+ console.log(chalk5.red(" Setup was cancelled or failed.\n"));
3892
4640
  throw new Error("__exit__");
3893
4641
  }
3894
- console.log(chalk4.green(" \u2713 Provider saved. Let's continue.\n"));
4642
+ console.log(chalk5.green(" \u2713 Provider saved. Let's continue.\n"));
3895
4643
  }
3896
4644
  const displayModel = config.model === "default" && config.provider === "claude-cli" ? process.env.ANTHROPIC_MODEL || "default (inherited from Claude Code)" : config.model;
3897
4645
  const fastModel = process.env.ANTHROPIC_SMALL_FAST_MODEL;
3898
4646
  const modelLine = fastModel ? ` Provider: ${config.provider} | Model: ${displayModel} | Scan: ${fastModel}` : ` Provider: ${config.provider} | Model: ${displayModel}`;
3899
- console.log(chalk4.dim(modelLine + "\n"));
4647
+ console.log(chalk5.dim(modelLine + "\n"));
3900
4648
  console.log(title.bold(" Step 2/4 \u2014 Discover your project\n"));
3901
- console.log(chalk4.dim(" Learning about your languages, dependencies, structure, and existing configs.\n"));
3902
- const spinner = ora("Analyzing project...").start();
4649
+ console.log(chalk5.dim(" Learning about your languages, dependencies, structure, and existing configs.\n"));
4650
+ const spinner = ora2("Analyzing project...").start();
3903
4651
  const fingerprint = collectFingerprint(process.cwd());
3904
4652
  await enrichFingerprintWithLLM(fingerprint, process.cwd());
3905
4653
  spinner.succeed("Project analyzed");
3906
- console.log(chalk4.dim(` Languages: ${fingerprint.languages.join(", ") || "none detected"}`));
3907
- console.log(chalk4.dim(` Files: ${fingerprint.fileTree.length} found
4654
+ console.log(chalk5.dim(` Languages: ${fingerprint.languages.join(", ") || "none detected"}`));
4655
+ console.log(chalk5.dim(` Files: ${fingerprint.fileTree.length} found
3908
4656
  `));
3909
4657
  const targetAgent = options.agent || await promptAgent();
3910
4658
  const preScore = computeLocalScore(process.cwd(), targetAgent);
@@ -3922,13 +4670,13 @@ async function initCommand(options) {
3922
4670
  displayScoreSummary(baselineScore);
3923
4671
  const hasExistingConfig = !!(fingerprint.existingConfigs.claudeMd || fingerprint.existingConfigs.claudeSettings || fingerprint.existingConfigs.claudeSkills?.length || fingerprint.existingConfigs.cursorrules || fingerprint.existingConfigs.cursorRules?.length);
3924
4672
  if (hasExistingConfig && baselineScore.score === 100) {
3925
- console.log(chalk4.bold.green(" Your setup is already optimal \u2014 nothing to change.\n"));
3926
- console.log(chalk4.dim(" Run ") + chalk4.hex("#83D1EB")("caliber onboard --force") + chalk4.dim(" to regenerate anyway.\n"));
4673
+ console.log(chalk5.bold.green(" Your setup is already optimal \u2014 nothing to change.\n"));
4674
+ console.log(chalk5.dim(" Run ") + chalk5.hex("#83D1EB")("caliber onboard --force") + chalk5.dim(" to regenerate anyway.\n"));
3927
4675
  if (!options.force) return;
3928
4676
  }
3929
4677
  const isEmpty = fingerprint.fileTree.length < 3;
3930
4678
  if (isEmpty) {
3931
- fingerprint.description = await promptInput2("What will you build in this project?");
4679
+ fingerprint.description = await promptInput3("What will you build in this project?");
3932
4680
  }
3933
4681
  let failingChecks;
3934
4682
  let passingChecks;
@@ -3939,24 +4687,24 @@ async function initCommand(options) {
3939
4687
  currentScore = baselineScore.score;
3940
4688
  if (failingChecks.length > 0) {
3941
4689
  console.log(title.bold(" Step 3/4 \u2014 Fine-tuning\n"));
3942
- console.log(chalk4.dim(` Your setup scores ${baselineScore.score}/100 \u2014 fixing ${failingChecks.length} remaining issue${failingChecks.length === 1 ? "" : "s"}:
4690
+ console.log(chalk5.dim(` Your setup scores ${baselineScore.score}/100 \u2014 fixing ${failingChecks.length} remaining issue${failingChecks.length === 1 ? "" : "s"}:
3943
4691
  `));
3944
4692
  for (const check of failingChecks) {
3945
- console.log(chalk4.dim(` \u2022 ${check.name}`));
4693
+ console.log(chalk5.dim(` \u2022 ${check.name}`));
3946
4694
  }
3947
4695
  console.log("");
3948
4696
  }
3949
4697
  } else if (hasExistingConfig) {
3950
4698
  console.log(title.bold(" Step 3/4 \u2014 Improve your setup\n"));
3951
- console.log(chalk4.dim(" Reviewing your existing configs against your codebase"));
3952
- console.log(chalk4.dim(" and preparing improvements.\n"));
4699
+ console.log(chalk5.dim(" Reviewing your existing configs against your codebase"));
4700
+ console.log(chalk5.dim(" and preparing improvements.\n"));
3953
4701
  } else {
3954
4702
  console.log(title.bold(" Step 3/4 \u2014 Build your agent setup\n"));
3955
- console.log(chalk4.dim(" Creating config files tailored to your project.\n"));
4703
+ console.log(chalk5.dim(" Creating config files tailored to your project.\n"));
3956
4704
  }
3957
- console.log(chalk4.dim(" This can take a couple of minutes depending on your model and provider.\n"));
4705
+ console.log(chalk5.dim(" This can take a couple of minutes depending on your model and provider.\n"));
3958
4706
  const genStartTime = Date.now();
3959
- const genSpinner = ora("Generating setup...").start();
4707
+ const genSpinner = ora2("Generating setup...").start();
3960
4708
  const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, { showElapsedTime: true });
3961
4709
  genMessages.start();
3962
4710
  let generatedSetup = null;
@@ -3996,8 +4744,8 @@ async function initCommand(options) {
3996
4744
  if (!generatedSetup) {
3997
4745
  genSpinner.fail("Failed to generate setup.");
3998
4746
  if (rawOutput) {
3999
- console.log(chalk4.dim("\nRaw LLM output (JSON parse failed):"));
4000
- console.log(chalk4.dim(rawOutput.slice(0, 500)));
4747
+ console.log(chalk5.dim("\nRaw LLM output (JSON parse failed):"));
4748
+ console.log(chalk5.dim(rawOutput.slice(0, 500)));
4001
4749
  }
4002
4750
  throw new Error("__exit__");
4003
4751
  }
@@ -4005,7 +4753,7 @@ async function initCommand(options) {
4005
4753
  const mins = Math.floor(elapsedMs / 6e4);
4006
4754
  const secs = Math.floor(elapsedMs % 6e4 / 1e3);
4007
4755
  const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
4008
- genSpinner.succeed(`Setup generated ${chalk4.dim(`in ${timeStr}`)}`);
4756
+ genSpinner.succeed(`Setup generated ${chalk5.dim(`in ${timeStr}`)}`);
4009
4757
  printSetupSummary(generatedSetup);
4010
4758
  const sessionHistory = [];
4011
4759
  sessionHistory.push({
@@ -4015,7 +4763,7 @@ async function initCommand(options) {
4015
4763
  console.log(title.bold(" Step 4/4 \u2014 Review and apply\n"));
4016
4764
  const setupFiles = collectSetupFiles(generatedSetup);
4017
4765
  const staged = stageFiles(setupFiles, process.cwd());
4018
- console.log(chalk4.dim(` ${chalk4.green(`${staged.newFiles} new`)} / ${chalk4.yellow(`${staged.modifiedFiles} modified`)} file${staged.newFiles + staged.modifiedFiles !== 1 ? "s" : ""}
4766
+ console.log(chalk5.dim(` ${chalk5.green(`${staged.newFiles} new`)} / ${chalk5.yellow(`${staged.modifiedFiles} modified`)} file${staged.newFiles + staged.modifiedFiles !== 1 ? "s" : ""}
4019
4767
  `));
4020
4768
  const wantsReview = await promptWantsReview();
4021
4769
  if (wantsReview) {
@@ -4027,12 +4775,12 @@ async function initCommand(options) {
4027
4775
  generatedSetup = await refineLoop(generatedSetup, targetAgent, sessionHistory);
4028
4776
  if (!generatedSetup) {
4029
4777
  cleanupStaging();
4030
- console.log(chalk4.dim("Refinement cancelled. No files were modified."));
4778
+ console.log(chalk5.dim("Refinement cancelled. No files were modified."));
4031
4779
  return;
4032
4780
  }
4033
4781
  const updatedFiles = collectSetupFiles(generatedSetup);
4034
4782
  const restaged = stageFiles(updatedFiles, process.cwd());
4035
- console.log(chalk4.dim(` ${chalk4.green(`${restaged.newFiles} new`)} / ${chalk4.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
4783
+ console.log(chalk5.dim(` ${chalk5.green(`${restaged.newFiles} new`)} / ${chalk5.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
4036
4784
  `));
4037
4785
  printSetupSummary(generatedSetup);
4038
4786
  await openReview("terminal", restaged.stagedFiles);
@@ -4040,37 +4788,49 @@ async function initCommand(options) {
4040
4788
  }
4041
4789
  cleanupStaging();
4042
4790
  if (action === "decline") {
4043
- console.log(chalk4.dim("Setup declined. No files were modified."));
4791
+ console.log(chalk5.dim("Setup declined. No files were modified."));
4044
4792
  return;
4045
4793
  }
4046
4794
  if (options.dryRun) {
4047
- console.log(chalk4.yellow("\n[Dry run] Would write the following files:"));
4795
+ console.log(chalk5.yellow("\n[Dry run] Would write the following files:"));
4048
4796
  console.log(JSON.stringify(generatedSetup, null, 2));
4049
4797
  return;
4050
4798
  }
4051
- const writeSpinner = ora("Writing config files...").start();
4799
+ const writeSpinner = ora2("Writing config files...").start();
4052
4800
  try {
4053
4801
  const result = writeSetup(generatedSetup);
4054
4802
  writeSpinner.succeed("Config files written");
4055
- console.log(chalk4.bold("\nFiles created/updated:"));
4803
+ console.log(chalk5.bold("\nFiles created/updated:"));
4056
4804
  for (const file of result.written) {
4057
- console.log(` ${chalk4.green("\u2713")} ${file}`);
4805
+ console.log(` ${chalk5.green("\u2713")} ${file}`);
4058
4806
  }
4059
4807
  if (result.deleted.length > 0) {
4060
- console.log(chalk4.bold("\nFiles removed:"));
4808
+ console.log(chalk5.bold("\nFiles removed:"));
4061
4809
  for (const file of result.deleted) {
4062
- console.log(` ${chalk4.red("\u2717")} ${file}`);
4810
+ console.log(` ${chalk5.red("\u2717")} ${file}`);
4063
4811
  }
4064
4812
  }
4065
4813
  if (result.backupDir) {
4066
- console.log(chalk4.dim(`
4814
+ console.log(chalk5.dim(`
4067
4815
  Backups saved to ${result.backupDir}`));
4068
4816
  }
4069
4817
  } catch (err) {
4070
4818
  writeSpinner.fail("Failed to write files");
4071
- console.error(chalk4.red(err instanceof Error ? err.message : "Unknown error"));
4819
+ console.error(chalk5.red(err instanceof Error ? err.message : "Unknown error"));
4072
4820
  throw new Error("__exit__");
4073
4821
  }
4822
+ try {
4823
+ const mcpResult = await discoverAndInstallMcps(targetAgent, fingerprint, process.cwd());
4824
+ if (mcpResult.installed > 0) {
4825
+ console.log(chalk5.bold(`
4826
+ ${mcpResult.installed} MCP server${mcpResult.installed > 1 ? "s" : ""} configured`));
4827
+ for (const name of mcpResult.names) {
4828
+ console.log(` ${chalk5.green("\u2713")} ${name}`);
4829
+ }
4830
+ }
4831
+ } catch (err) {
4832
+ console.log(chalk5.dim(" MCP discovery skipped: " + (err instanceof Error ? err.message : "unknown error")));
4833
+ }
4074
4834
  ensurePermissions();
4075
4835
  const sha = getCurrentHeadSha();
4076
4836
  writeState({
@@ -4080,56 +4840,56 @@ async function initCommand(options) {
4080
4840
  });
4081
4841
  console.log("");
4082
4842
  console.log(title.bold(" Keep your configs fresh\n"));
4083
- console.log(chalk4.dim(" Caliber can automatically update your agent configs when your code changes.\n"));
4843
+ console.log(chalk5.dim(" Caliber can automatically update your agent configs when your code changes.\n"));
4084
4844
  const hookChoice = await promptHookType(targetAgent);
4085
4845
  if (hookChoice === "claude" || hookChoice === "both") {
4086
4846
  const hookResult = installHook();
4087
4847
  if (hookResult.installed) {
4088
- console.log(` ${chalk4.green("\u2713")} Claude Code hook installed \u2014 docs update on session end`);
4089
- console.log(chalk4.dim(" Run ") + chalk4.hex("#83D1EB")("caliber hooks remove") + chalk4.dim(" to disable"));
4848
+ console.log(` ${chalk5.green("\u2713")} Claude Code hook installed \u2014 docs update on session end`);
4849
+ console.log(chalk5.dim(" Run ") + chalk5.hex("#83D1EB")("caliber hooks remove") + chalk5.dim(" to disable"));
4090
4850
  } else if (hookResult.alreadyInstalled) {
4091
- console.log(chalk4.dim(" Claude Code hook already installed"));
4851
+ console.log(chalk5.dim(" Claude Code hook already installed"));
4092
4852
  }
4093
4853
  const learnResult = installLearningHooks();
4094
4854
  if (learnResult.installed) {
4095
- console.log(` ${chalk4.green("\u2713")} Learning hooks installed \u2014 session insights captured automatically`);
4096
- console.log(chalk4.dim(" Run ") + chalk4.hex("#83D1EB")("caliber learn remove") + chalk4.dim(" to disable"));
4855
+ console.log(` ${chalk5.green("\u2713")} Learning hooks installed \u2014 session insights captured automatically`);
4856
+ console.log(chalk5.dim(" Run ") + chalk5.hex("#83D1EB")("caliber learn remove") + chalk5.dim(" to disable"));
4097
4857
  } else if (learnResult.alreadyInstalled) {
4098
- console.log(chalk4.dim(" Learning hooks already installed"));
4858
+ console.log(chalk5.dim(" Learning hooks already installed"));
4099
4859
  }
4100
4860
  }
4101
4861
  if (hookChoice === "precommit" || hookChoice === "both") {
4102
4862
  const precommitResult = installPreCommitHook();
4103
4863
  if (precommitResult.installed) {
4104
- console.log(` ${chalk4.green("\u2713")} Pre-commit hook installed \u2014 docs refresh before each commit`);
4105
- console.log(chalk4.dim(" Run ") + chalk4.hex("#83D1EB")("caliber hooks remove-precommit") + chalk4.dim(" to disable"));
4864
+ console.log(` ${chalk5.green("\u2713")} Pre-commit hook installed \u2014 docs refresh before each commit`);
4865
+ console.log(chalk5.dim(" Run ") + chalk5.hex("#83D1EB")("caliber hooks remove-precommit") + chalk5.dim(" to disable"));
4106
4866
  } else if (precommitResult.alreadyInstalled) {
4107
- console.log(chalk4.dim(" Pre-commit hook already installed"));
4867
+ console.log(chalk5.dim(" Pre-commit hook already installed"));
4108
4868
  } else {
4109
- console.log(chalk4.yellow(" Could not install pre-commit hook (not a git repository?)"));
4869
+ console.log(chalk5.yellow(" Could not install pre-commit hook (not a git repository?)"));
4110
4870
  }
4111
4871
  }
4112
4872
  if (hookChoice === "skip") {
4113
- console.log(chalk4.dim(" Skipped auto-refresh hooks. Run ") + chalk4.hex("#83D1EB")("caliber hooks install") + chalk4.dim(" later to enable."));
4873
+ console.log(chalk5.dim(" Skipped auto-refresh hooks. Run ") + chalk5.hex("#83D1EB")("caliber hooks install") + chalk5.dim(" later to enable."));
4114
4874
  }
4115
4875
  const afterScore = computeLocalScore(process.cwd(), targetAgent);
4116
4876
  if (afterScore.score < baselineScore.score) {
4117
4877
  console.log("");
4118
- console.log(chalk4.yellow(` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`));
4878
+ console.log(chalk5.yellow(` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`));
4119
4879
  try {
4120
4880
  const { restored, removed } = undoSetup();
4121
4881
  if (restored.length > 0 || removed.length > 0) {
4122
- console.log(chalk4.dim(` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`));
4882
+ console.log(chalk5.dim(` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`));
4123
4883
  }
4124
4884
  } catch {
4125
4885
  }
4126
- console.log(chalk4.dim(" Run ") + chalk4.hex("#83D1EB")("caliber onboard --force") + chalk4.dim(" to override.\n"));
4886
+ console.log(chalk5.dim(" Run ") + chalk5.hex("#83D1EB")("caliber onboard --force") + chalk5.dim(" to override.\n"));
4127
4887
  return;
4128
4888
  }
4129
4889
  displayScoreDelta(baselineScore, afterScore);
4130
- console.log(chalk4.bold.green(" Onboarding complete! Your project is ready for AI-assisted development."));
4131
- console.log(chalk4.dim(" Run ") + chalk4.hex("#83D1EB")("caliber undo") + chalk4.dim(" to revert changes.\n"));
4132
- console.log(chalk4.bold(" Next steps:\n"));
4890
+ console.log(chalk5.bold.green(" Onboarding complete! Your project is ready for AI-assisted development."));
4891
+ console.log(chalk5.dim(" Run ") + chalk5.hex("#83D1EB")("caliber undo") + chalk5.dim(" to revert changes.\n"));
4892
+ console.log(chalk5.bold(" Next steps:\n"));
4133
4893
  console.log(` ${title("caliber score")} See your full config breakdown`);
4134
4894
  console.log(` ${title("caliber recommend")} Discover community skills for your stack`);
4135
4895
  console.log(` ${title("caliber undo")} Revert all changes from this run`);
@@ -4137,7 +4897,7 @@ async function initCommand(options) {
4137
4897
  }
4138
4898
  async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
4139
4899
  while (true) {
4140
- const message = await promptInput2("\nWhat would you like to change?");
4900
+ const message = await promptInput3("\nWhat would you like to change?");
4141
4901
  if (!message || message.toLowerCase() === "done" || message.toLowerCase() === "accept") {
4142
4902
  return currentSetup;
4143
4903
  }
@@ -4146,12 +4906,12 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
4146
4906
  }
4147
4907
  const isValid = await classifyRefineIntent(message);
4148
4908
  if (!isValid) {
4149
- console.log(chalk4.dim(" This doesn't look like a config change request."));
4150
- console.log(chalk4.dim(" Describe what to add, remove, or modify in your configs."));
4151
- console.log(chalk4.dim(' Type "done" to accept the current setup.\n'));
4909
+ console.log(chalk5.dim(" This doesn't look like a config change request."));
4910
+ console.log(chalk5.dim(" Describe what to add, remove, or modify in your configs."));
4911
+ console.log(chalk5.dim(' Type "done" to accept the current setup.\n'));
4152
4912
  continue;
4153
4913
  }
4154
- const refineSpinner = ora("Refining setup...").start();
4914
+ const refineSpinner = ora2("Refining setup...").start();
4155
4915
  const refineMessages = new SpinnerMessages(refineSpinner, REFINE_MESSAGES);
4156
4916
  refineMessages.start();
4157
4917
  const refined = await refineSetup(
@@ -4169,16 +4929,16 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
4169
4929
  });
4170
4930
  refineSpinner.succeed("Setup updated");
4171
4931
  printSetupSummary(refined);
4172
- console.log(chalk4.dim('Type "done" to accept, or describe more changes.'));
4932
+ console.log(chalk5.dim('Type "done" to accept, or describe more changes.'));
4173
4933
  } else {
4174
4934
  refineSpinner.fail("Refinement failed \u2014 could not parse AI response.");
4175
- console.log(chalk4.dim('Try rephrasing your request, or type "done" to keep the current setup.'));
4935
+ console.log(chalk5.dim('Try rephrasing your request, or type "done" to keep the current setup.'));
4176
4936
  }
4177
4937
  }
4178
4938
  }
4179
4939
  function summarizeSetup(action, setup) {
4180
4940
  const descriptions = setup.fileDescriptions;
4181
- const files = descriptions ? Object.entries(descriptions).map(([path24, desc]) => ` ${path24}: ${desc}`).join("\n") : Object.keys(setup).filter((k) => k !== "targetAgent" && k !== "fileDescriptions").join(", ");
4941
+ const files = descriptions ? Object.entries(descriptions).map(([path26, desc]) => ` ${path26}: ${desc}`).join("\n") : Object.keys(setup).filter((k) => k !== "targetAgent" && k !== "fileDescriptions").join(", ");
4182
4942
  return `${action}. Files:
4183
4943
  ${files}`;
4184
4944
  }
@@ -4229,10 +4989,10 @@ ${JSON.stringify(checkList, null, 2)}`,
4229
4989
  return [];
4230
4990
  }
4231
4991
  }
4232
- function promptInput2(question) {
4233
- const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
4992
+ function promptInput3(question) {
4993
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
4234
4994
  return new Promise((resolve2) => {
4235
- rl.question(chalk4.cyan(`${question} `), (answer) => {
4995
+ rl.question(chalk5.cyan(`${question} `), (answer) => {
4236
4996
  rl.close();
4237
4997
  resolve2(answer.trim());
4238
4998
  });
@@ -4294,12 +5054,12 @@ async function openReview(method, stagedFiles) {
4294
5054
  originalPath: f.originalPath,
4295
5055
  proposedPath: f.proposedPath
4296
5056
  })));
4297
- console.log(chalk4.dim(" Diffs opened in your editor.\n"));
5057
+ console.log(chalk5.dim(" Diffs opened in your editor.\n"));
4298
5058
  return;
4299
5059
  }
4300
5060
  const fileInfos = stagedFiles.map((file) => {
4301
- const proposed = fs18.readFileSync(file.proposedPath, "utf-8");
4302
- const current = file.currentPath ? fs18.readFileSync(file.currentPath, "utf-8") : "";
5061
+ const proposed = fs20.readFileSync(file.proposedPath, "utf-8");
5062
+ const current = file.currentPath ? fs20.readFileSync(file.currentPath, "utf-8") : "";
4303
5063
  const patch = createTwoFilesPatch(
4304
5064
  file.isNew ? "/dev/null" : file.relativePath,
4305
5065
  file.relativePath,
@@ -4325,8 +5085,8 @@ async function openReview(method, stagedFiles) {
4325
5085
  async function interactiveDiffExplorer(files) {
4326
5086
  if (!process.stdin.isTTY) {
4327
5087
  for (const f of files) {
4328
- const icon = f.isNew ? chalk4.green("+") : chalk4.yellow("~");
4329
- const stats = f.isNew ? chalk4.dim(`${f.lines} lines`) : `${chalk4.green(`+${f.added}`)} ${chalk4.red(`-${f.removed}`)}`;
5088
+ const icon = f.isNew ? chalk5.green("+") : chalk5.yellow("~");
5089
+ const stats = f.isNew ? chalk5.dim(`${f.lines} lines`) : `${chalk5.green(`+${f.added}`)} ${chalk5.red(`-${f.removed}`)}`;
4330
5090
  console.log(` ${icon} ${f.relativePath} ${stats}`);
4331
5091
  }
4332
5092
  console.log("");
@@ -4342,47 +5102,47 @@ async function interactiveDiffExplorer(files) {
4342
5102
  }
4343
5103
  function renderFileList() {
4344
5104
  const lines = [];
4345
- lines.push(chalk4.bold(" Review changes"));
5105
+ lines.push(chalk5.bold(" Review changes"));
4346
5106
  lines.push("");
4347
5107
  for (let i = 0; i < files.length; i++) {
4348
5108
  const f = files[i];
4349
- const ptr = i === cursor ? chalk4.cyan(">") : " ";
4350
- const icon = f.isNew ? chalk4.green("+") : chalk4.yellow("~");
4351
- const stats = f.isNew ? chalk4.dim(`${f.lines} lines`) : `${chalk4.green(`+${f.added}`)} ${chalk4.red(`-${f.removed}`)}`;
5109
+ const ptr = i === cursor ? chalk5.cyan(">") : " ";
5110
+ const icon = f.isNew ? chalk5.green("+") : chalk5.yellow("~");
5111
+ const stats = f.isNew ? chalk5.dim(`${f.lines} lines`) : `${chalk5.green(`+${f.added}`)} ${chalk5.red(`-${f.removed}`)}`;
4352
5112
  lines.push(` ${ptr} ${icon} ${f.relativePath} ${stats}`);
4353
5113
  }
4354
5114
  lines.push("");
4355
- lines.push(chalk4.dim(" \u2191\u2193 navigate \u23CE view diff q done"));
5115
+ lines.push(chalk5.dim(" \u2191\u2193 navigate \u23CE view diff q done"));
4356
5116
  return lines.join("\n");
4357
5117
  }
4358
5118
  function renderDiff(index) {
4359
5119
  const f = files[index];
4360
5120
  const lines = [];
4361
- const header = f.isNew ? ` ${chalk4.green("+")} ${f.relativePath} ${chalk4.dim("(new file)")}` : ` ${chalk4.yellow("~")} ${f.relativePath} ${chalk4.green(`+${f.added}`)} ${chalk4.red(`-${f.removed}`)}`;
5121
+ const header = f.isNew ? ` ${chalk5.green("+")} ${f.relativePath} ${chalk5.dim("(new file)")}` : ` ${chalk5.yellow("~")} ${f.relativePath} ${chalk5.green(`+${f.added}`)} ${chalk5.red(`-${f.removed}`)}`;
4362
5122
  lines.push(header);
4363
- lines.push(chalk4.dim(" " + "\u2500".repeat(60)));
5123
+ lines.push(chalk5.dim(" " + "\u2500".repeat(60)));
4364
5124
  const patchLines = f.patch.split("\n");
4365
5125
  const bodyLines = patchLines.slice(4);
4366
5126
  const maxVisible = getTermHeight() - 4;
4367
5127
  const visibleLines = bodyLines.slice(scrollOffset, scrollOffset + maxVisible);
4368
5128
  for (const line of visibleLines) {
4369
5129
  if (line.startsWith("+")) {
4370
- lines.push(chalk4.green(" " + line));
5130
+ lines.push(chalk5.green(" " + line));
4371
5131
  } else if (line.startsWith("-")) {
4372
- lines.push(chalk4.red(" " + line));
5132
+ lines.push(chalk5.red(" " + line));
4373
5133
  } else if (line.startsWith("@@")) {
4374
- lines.push(chalk4.cyan(" " + line));
5134
+ lines.push(chalk5.cyan(" " + line));
4375
5135
  } else {
4376
- lines.push(chalk4.dim(" " + line));
5136
+ lines.push(chalk5.dim(" " + line));
4377
5137
  }
4378
5138
  }
4379
5139
  const totalBody = bodyLines.length;
4380
5140
  if (totalBody > maxVisible) {
4381
5141
  const pct = Math.round((scrollOffset + maxVisible) / totalBody * 100);
4382
- lines.push(chalk4.dim(` \u2500\u2500 ${Math.min(pct, 100)}% \u2500\u2500`));
5142
+ lines.push(chalk5.dim(` \u2500\u2500 ${Math.min(pct, 100)}% \u2500\u2500`));
4383
5143
  }
4384
5144
  lines.push("");
4385
- lines.push(chalk4.dim(" \u2191\u2193 scroll \u23B5/esc back to file list"));
5145
+ lines.push(chalk5.dim(" \u2191\u2193 scroll \u23B5/esc back to file list"));
4386
5146
  return lines.join("\n");
4387
5147
  }
4388
5148
  function draw(initial) {
@@ -4476,46 +5236,46 @@ function printSetupSummary(setup) {
4476
5236
  const fileDescriptions = setup.fileDescriptions;
4477
5237
  const deletions = setup.deletions;
4478
5238
  console.log("");
4479
- console.log(chalk4.bold(" Proposed changes:\n"));
5239
+ console.log(chalk5.bold(" Proposed changes:\n"));
4480
5240
  const getDescription = (filePath) => {
4481
5241
  return fileDescriptions?.[filePath];
4482
5242
  };
4483
5243
  if (claude) {
4484
5244
  if (claude.claudeMd) {
4485
- const icon = fs18.existsSync("CLAUDE.md") ? chalk4.yellow("~") : chalk4.green("+");
5245
+ const icon = fs20.existsSync("CLAUDE.md") ? chalk5.yellow("~") : chalk5.green("+");
4486
5246
  const desc = getDescription("CLAUDE.md");
4487
- console.log(` ${icon} ${chalk4.bold("CLAUDE.md")}`);
4488
- if (desc) console.log(chalk4.dim(` ${desc}`));
5247
+ console.log(` ${icon} ${chalk5.bold("CLAUDE.md")}`);
5248
+ if (desc) console.log(chalk5.dim(` ${desc}`));
4489
5249
  console.log("");
4490
5250
  }
4491
5251
  const skills = claude.skills;
4492
5252
  if (Array.isArray(skills) && skills.length > 0) {
4493
5253
  for (const skill of skills) {
4494
5254
  const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
4495
- const icon = fs18.existsSync(skillPath) ? chalk4.yellow("~") : chalk4.green("+");
5255
+ const icon = fs20.existsSync(skillPath) ? chalk5.yellow("~") : chalk5.green("+");
4496
5256
  const desc = getDescription(skillPath);
4497
- console.log(` ${icon} ${chalk4.bold(skillPath)}`);
4498
- console.log(chalk4.dim(` ${desc || skill.description || skill.name}`));
5257
+ console.log(` ${icon} ${chalk5.bold(skillPath)}`);
5258
+ console.log(chalk5.dim(` ${desc || skill.description || skill.name}`));
4499
5259
  console.log("");
4500
5260
  }
4501
5261
  }
4502
5262
  }
4503
5263
  if (cursor) {
4504
5264
  if (cursor.cursorrules) {
4505
- const icon = fs18.existsSync(".cursorrules") ? chalk4.yellow("~") : chalk4.green("+");
5265
+ const icon = fs20.existsSync(".cursorrules") ? chalk5.yellow("~") : chalk5.green("+");
4506
5266
  const desc = getDescription(".cursorrules");
4507
- console.log(` ${icon} ${chalk4.bold(".cursorrules")}`);
4508
- if (desc) console.log(chalk4.dim(` ${desc}`));
5267
+ console.log(` ${icon} ${chalk5.bold(".cursorrules")}`);
5268
+ if (desc) console.log(chalk5.dim(` ${desc}`));
4509
5269
  console.log("");
4510
5270
  }
4511
5271
  const cursorSkills = cursor.skills;
4512
5272
  if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
4513
5273
  for (const skill of cursorSkills) {
4514
5274
  const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
4515
- const icon = fs18.existsSync(skillPath) ? chalk4.yellow("~") : chalk4.green("+");
5275
+ const icon = fs20.existsSync(skillPath) ? chalk5.yellow("~") : chalk5.green("+");
4516
5276
  const desc = getDescription(skillPath);
4517
- console.log(` ${icon} ${chalk4.bold(skillPath)}`);
4518
- console.log(chalk4.dim(` ${desc || skill.description || skill.name}`));
5277
+ console.log(` ${icon} ${chalk5.bold(skillPath)}`);
5278
+ console.log(chalk5.dim(` ${desc || skill.description || skill.name}`));
4519
5279
  console.log("");
4520
5280
  }
4521
5281
  }
@@ -4523,32 +5283,32 @@ function printSetupSummary(setup) {
4523
5283
  if (Array.isArray(rules) && rules.length > 0) {
4524
5284
  for (const rule of rules) {
4525
5285
  const rulePath = `.cursor/rules/${rule.filename}`;
4526
- const icon = fs18.existsSync(rulePath) ? chalk4.yellow("~") : chalk4.green("+");
5286
+ const icon = fs20.existsSync(rulePath) ? chalk5.yellow("~") : chalk5.green("+");
4527
5287
  const desc = getDescription(rulePath);
4528
- console.log(` ${icon} ${chalk4.bold(rulePath)}`);
5288
+ console.log(` ${icon} ${chalk5.bold(rulePath)}`);
4529
5289
  if (desc) {
4530
- console.log(chalk4.dim(` ${desc}`));
5290
+ console.log(chalk5.dim(` ${desc}`));
4531
5291
  } else {
4532
5292
  const firstLine = rule.content.split("\n").filter((l) => l.trim() && !l.trim().startsWith("#"))[0];
4533
- if (firstLine) console.log(chalk4.dim(` ${firstLine.trim().slice(0, 80)}`));
5293
+ if (firstLine) console.log(chalk5.dim(` ${firstLine.trim().slice(0, 80)}`));
4534
5294
  }
4535
5295
  console.log("");
4536
5296
  }
4537
5297
  }
4538
5298
  }
4539
- if (!fs18.existsSync("AGENTS.md")) {
4540
- console.log(` ${chalk4.green("+")} ${chalk4.bold("AGENTS.md")}`);
4541
- console.log(chalk4.dim(" Cross-agent coordination file"));
5299
+ if (!fs20.existsSync("AGENTS.md")) {
5300
+ console.log(` ${chalk5.green("+")} ${chalk5.bold("AGENTS.md")}`);
5301
+ console.log(chalk5.dim(" Cross-agent coordination file"));
4542
5302
  console.log("");
4543
5303
  }
4544
5304
  if (Array.isArray(deletions) && deletions.length > 0) {
4545
5305
  for (const del of deletions) {
4546
- console.log(` ${chalk4.red("-")} ${chalk4.bold(del.filePath)}`);
4547
- console.log(chalk4.dim(` ${del.reason}`));
5306
+ console.log(` ${chalk5.red("-")} ${chalk5.bold(del.filePath)}`);
5307
+ console.log(chalk5.dim(` ${del.reason}`));
4548
5308
  console.log("");
4549
5309
  }
4550
5310
  }
4551
- console.log(` ${chalk4.green("+")} ${chalk4.dim("new")} ${chalk4.yellow("~")} ${chalk4.dim("modified")} ${chalk4.red("-")} ${chalk4.dim("removed")}`);
5311
+ console.log(` ${chalk5.green("+")} ${chalk5.dim("new")} ${chalk5.yellow("~")} ${chalk5.dim("modified")} ${chalk5.red("-")} ${chalk5.dim("removed")}`);
4552
5312
  console.log("");
4553
5313
  }
4554
5314
  function buildSkillContent(skill) {
@@ -4564,8 +5324,8 @@ function ensurePermissions() {
4564
5324
  const settingsPath = ".claude/settings.json";
4565
5325
  let settings = {};
4566
5326
  try {
4567
- if (fs18.existsSync(settingsPath)) {
4568
- settings = JSON.parse(fs18.readFileSync(settingsPath, "utf-8"));
5327
+ if (fs20.existsSync(settingsPath)) {
5328
+ settings = JSON.parse(fs20.readFileSync(settingsPath, "utf-8"));
4569
5329
  }
4570
5330
  } catch {
4571
5331
  }
@@ -4579,8 +5339,8 @@ function ensurePermissions() {
4579
5339
  "Bash(git *)"
4580
5340
  ];
4581
5341
  settings.permissions = permissions;
4582
- if (!fs18.existsSync(".claude")) fs18.mkdirSync(".claude", { recursive: true });
4583
- fs18.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
5342
+ if (!fs20.existsSync(".claude")) fs20.mkdirSync(".claude", { recursive: true });
5343
+ fs20.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
4584
5344
  }
4585
5345
  function collectSetupFiles(setup) {
4586
5346
  const files = [];
@@ -4610,7 +5370,7 @@ function collectSetupFiles(setup) {
4610
5370
  }
4611
5371
  }
4612
5372
  }
4613
- if (!fs18.existsSync("AGENTS.md")) {
5373
+ if (!fs20.existsSync("AGENTS.md")) {
4614
5374
  const agentRefs = [];
4615
5375
  if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
4616
5376
  if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
@@ -4629,10 +5389,10 @@ ${agentRefs.join(" ")}
4629
5389
  }
4630
5390
 
4631
5391
  // src/commands/undo.ts
4632
- import chalk5 from "chalk";
4633
- import ora2 from "ora";
5392
+ import chalk6 from "chalk";
5393
+ import ora3 from "ora";
4634
5394
  function undoCommand() {
4635
- const spinner = ora2("Reverting setup...").start();
5395
+ const spinner = ora3("Reverting setup...").start();
4636
5396
  try {
4637
5397
  const { restored, removed } = undoSetup();
4638
5398
  if (restored.length === 0 && removed.length === 0) {
@@ -4641,27 +5401,27 @@ function undoCommand() {
4641
5401
  }
4642
5402
  spinner.succeed("Setup reverted successfully.\n");
4643
5403
  if (restored.length > 0) {
4644
- console.log(chalk5.cyan(" Restored from backup:"));
5404
+ console.log(chalk6.cyan(" Restored from backup:"));
4645
5405
  for (const file of restored) {
4646
- console.log(` ${chalk5.green("\u21A9")} ${file}`);
5406
+ console.log(` ${chalk6.green("\u21A9")} ${file}`);
4647
5407
  }
4648
5408
  }
4649
5409
  if (removed.length > 0) {
4650
- console.log(chalk5.cyan(" Removed:"));
5410
+ console.log(chalk6.cyan(" Removed:"));
4651
5411
  for (const file of removed) {
4652
- console.log(` ${chalk5.red("\u2717")} ${file}`);
5412
+ console.log(` ${chalk6.red("\u2717")} ${file}`);
4653
5413
  }
4654
5414
  }
4655
5415
  console.log("");
4656
5416
  } catch (err) {
4657
- spinner.fail(chalk5.red(err instanceof Error ? err.message : "Undo failed"));
5417
+ spinner.fail(chalk6.red(err instanceof Error ? err.message : "Undo failed"));
4658
5418
  throw new Error("__exit__");
4659
5419
  }
4660
5420
  }
4661
5421
 
4662
5422
  // src/commands/status.ts
4663
- import chalk6 from "chalk";
4664
- import fs19 from "fs";
5423
+ import chalk7 from "chalk";
5424
+ import fs21 from "fs";
4665
5425
  async function statusCommand(options) {
4666
5426
  const config = loadConfig();
4667
5427
  const manifest = readManifest();
@@ -4674,45 +5434,45 @@ async function statusCommand(options) {
4674
5434
  }, null, 2));
4675
5435
  return;
4676
5436
  }
4677
- console.log(chalk6.bold("\nCaliber Status\n"));
5437
+ console.log(chalk7.bold("\nCaliber Status\n"));
4678
5438
  if (config) {
4679
- console.log(` LLM: ${chalk6.green(config.provider)} (${config.model})`);
5439
+ console.log(` LLM: ${chalk7.green(config.provider)} (${config.model})`);
4680
5440
  } else {
4681
- console.log(` LLM: ${chalk6.yellow("Not configured")} \u2014 run ${chalk6.hex("#83D1EB")("caliber config")}`);
5441
+ console.log(` LLM: ${chalk7.yellow("Not configured")} \u2014 run ${chalk7.hex("#83D1EB")("caliber config")}`);
4682
5442
  }
4683
5443
  if (!manifest) {
4684
- console.log(` Setup: ${chalk6.dim("No setup applied")}`);
4685
- console.log(chalk6.dim("\n Run ") + chalk6.hex("#83D1EB")("caliber onboard") + chalk6.dim(" to get started.\n"));
5444
+ console.log(` Setup: ${chalk7.dim("No setup applied")}`);
5445
+ console.log(chalk7.dim("\n Run ") + chalk7.hex("#83D1EB")("caliber onboard") + chalk7.dim(" to get started.\n"));
4686
5446
  return;
4687
5447
  }
4688
- console.log(` Files managed: ${chalk6.cyan(manifest.entries.length.toString())}`);
5448
+ console.log(` Files managed: ${chalk7.cyan(manifest.entries.length.toString())}`);
4689
5449
  for (const entry of manifest.entries) {
4690
- const exists = fs19.existsSync(entry.path);
4691
- const icon = exists ? chalk6.green("\u2713") : chalk6.red("\u2717");
5450
+ const exists = fs21.existsSync(entry.path);
5451
+ const icon = exists ? chalk7.green("\u2713") : chalk7.red("\u2717");
4692
5452
  console.log(` ${icon} ${entry.path} (${entry.action})`);
4693
5453
  }
4694
5454
  console.log("");
4695
5455
  }
4696
5456
 
4697
5457
  // src/commands/regenerate.ts
4698
- import chalk7 from "chalk";
4699
- import ora3 from "ora";
5458
+ import chalk8 from "chalk";
5459
+ import ora4 from "ora";
4700
5460
  import confirm from "@inquirer/confirm";
4701
5461
  async function regenerateCommand(options) {
4702
5462
  const config = loadConfig();
4703
5463
  if (!config) {
4704
- console.log(chalk7.red("No LLM provider configured. Run ") + chalk7.hex("#83D1EB")("caliber config") + chalk7.red(" (e.g. choose Cursor) or set ANTHROPIC_API_KEY."));
5464
+ console.log(chalk8.red("No LLM provider configured. Run ") + chalk8.hex("#83D1EB")("caliber config") + chalk8.red(" (e.g. choose Cursor) or set ANTHROPIC_API_KEY."));
4705
5465
  throw new Error("__exit__");
4706
5466
  }
4707
5467
  const manifest = readManifest();
4708
5468
  if (!manifest) {
4709
- console.log(chalk7.yellow("No existing setup found. Run ") + chalk7.hex("#83D1EB")("caliber onboard") + chalk7.yellow(" first."));
5469
+ console.log(chalk8.yellow("No existing setup found. Run ") + chalk8.hex("#83D1EB")("caliber onboard") + chalk8.yellow(" first."));
4710
5470
  throw new Error("__exit__");
4711
5471
  }
4712
- const spinner = ora3("Re-analyzing project...").start();
5472
+ const spinner = ora4("Re-analyzing project...").start();
4713
5473
  const fingerprint = collectFingerprint(process.cwd());
4714
5474
  spinner.succeed("Project re-analyzed");
4715
- const genSpinner = ora3("Regenerating setup...").start();
5475
+ const genSpinner = ora4("Regenerating setup...").start();
4716
5476
  const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES);
4717
5477
  genMessages.start();
4718
5478
  let generatedSetup = null;
@@ -4749,38 +5509,38 @@ async function regenerateCommand(options) {
4749
5509
  }
4750
5510
  genSpinner.succeed("Setup regenerated");
4751
5511
  if (options.dryRun) {
4752
- console.log(chalk7.yellow("\n[Dry run] Would write:"));
5512
+ console.log(chalk8.yellow("\n[Dry run] Would write:"));
4753
5513
  console.log(JSON.stringify(generatedSetup, null, 2));
4754
5514
  return;
4755
5515
  }
4756
5516
  const shouldApply = await confirm({ message: "Apply regenerated setup?", default: true });
4757
5517
  if (!shouldApply) {
4758
- console.log(chalk7.dim("Regeneration cancelled."));
5518
+ console.log(chalk8.dim("Regeneration cancelled."));
4759
5519
  return;
4760
5520
  }
4761
- const writeSpinner = ora3("Updating config files...").start();
5521
+ const writeSpinner = ora4("Updating config files...").start();
4762
5522
  const result = writeSetup(generatedSetup);
4763
5523
  writeSpinner.succeed("Config files updated");
4764
5524
  for (const file of result.written) {
4765
- console.log(` ${chalk7.green("\u2713")} ${file}`);
5525
+ console.log(` ${chalk8.green("\u2713")} ${file}`);
4766
5526
  }
4767
5527
  console.log("");
4768
5528
  }
4769
5529
 
4770
5530
  // src/commands/recommend.ts
4771
- import chalk8 from "chalk";
4772
- import ora4 from "ora";
5531
+ import chalk9 from "chalk";
5532
+ import ora5 from "ora";
4773
5533
  import { mkdirSync, readFileSync as readFileSync7, readdirSync as readdirSync5, existsSync as existsSync9, writeFileSync } from "fs";
4774
5534
  import { join as join8, dirname as dirname2 } from "path";
4775
5535
 
4776
5536
  // src/scanner/index.ts
4777
- import fs20 from "fs";
4778
- import path17 from "path";
5537
+ import fs22 from "fs";
5538
+ import path19 from "path";
4779
5539
  import crypto2 from "crypto";
4780
5540
  function scanLocalState(dir) {
4781
5541
  const items = [];
4782
- const claudeMdPath = path17.join(dir, "CLAUDE.md");
4783
- if (fs20.existsSync(claudeMdPath)) {
5542
+ const claudeMdPath = path19.join(dir, "CLAUDE.md");
5543
+ if (fs22.existsSync(claudeMdPath)) {
4784
5544
  items.push({
4785
5545
  type: "rule",
4786
5546
  platform: "claude",
@@ -4789,10 +5549,10 @@ function scanLocalState(dir) {
4789
5549
  path: claudeMdPath
4790
5550
  });
4791
5551
  }
4792
- const skillsDir = path17.join(dir, ".claude", "skills");
4793
- if (fs20.existsSync(skillsDir)) {
4794
- for (const file of fs20.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
4795
- const filePath = path17.join(skillsDir, file);
5552
+ const skillsDir = path19.join(dir, ".claude", "skills");
5553
+ if (fs22.existsSync(skillsDir)) {
5554
+ for (const file of fs22.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
5555
+ const filePath = path19.join(skillsDir, file);
4796
5556
  items.push({
4797
5557
  type: "skill",
4798
5558
  platform: "claude",
@@ -4802,10 +5562,10 @@ function scanLocalState(dir) {
4802
5562
  });
4803
5563
  }
4804
5564
  }
4805
- const mcpJsonPath = path17.join(dir, ".mcp.json");
4806
- if (fs20.existsSync(mcpJsonPath)) {
5565
+ const mcpJsonPath = path19.join(dir, ".mcp.json");
5566
+ if (fs22.existsSync(mcpJsonPath)) {
4807
5567
  try {
4808
- const mcpJson = JSON.parse(fs20.readFileSync(mcpJsonPath, "utf-8"));
5568
+ const mcpJson = JSON.parse(fs22.readFileSync(mcpJsonPath, "utf-8"));
4809
5569
  if (mcpJson.mcpServers) {
4810
5570
  for (const name of Object.keys(mcpJson.mcpServers)) {
4811
5571
  items.push({
@@ -4820,8 +5580,8 @@ function scanLocalState(dir) {
4820
5580
  } catch {
4821
5581
  }
4822
5582
  }
4823
- const cursorrulesPath = path17.join(dir, ".cursorrules");
4824
- if (fs20.existsSync(cursorrulesPath)) {
5583
+ const cursorrulesPath = path19.join(dir, ".cursorrules");
5584
+ if (fs22.existsSync(cursorrulesPath)) {
4825
5585
  items.push({
4826
5586
  type: "rule",
4827
5587
  platform: "cursor",
@@ -4830,10 +5590,10 @@ function scanLocalState(dir) {
4830
5590
  path: cursorrulesPath
4831
5591
  });
4832
5592
  }
4833
- const cursorRulesDir = path17.join(dir, ".cursor", "rules");
4834
- if (fs20.existsSync(cursorRulesDir)) {
4835
- for (const file of fs20.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
4836
- const filePath = path17.join(cursorRulesDir, file);
5593
+ const cursorRulesDir = path19.join(dir, ".cursor", "rules");
5594
+ if (fs22.existsSync(cursorRulesDir)) {
5595
+ for (const file of fs22.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
5596
+ const filePath = path19.join(cursorRulesDir, file);
4837
5597
  items.push({
4838
5598
  type: "rule",
4839
5599
  platform: "cursor",
@@ -4843,12 +5603,12 @@ function scanLocalState(dir) {
4843
5603
  });
4844
5604
  }
4845
5605
  }
4846
- const cursorSkillsDir = path17.join(dir, ".cursor", "skills");
4847
- if (fs20.existsSync(cursorSkillsDir)) {
5606
+ const cursorSkillsDir = path19.join(dir, ".cursor", "skills");
5607
+ if (fs22.existsSync(cursorSkillsDir)) {
4848
5608
  try {
4849
- for (const name of fs20.readdirSync(cursorSkillsDir)) {
4850
- const skillFile = path17.join(cursorSkillsDir, name, "SKILL.md");
4851
- if (fs20.existsSync(skillFile)) {
5609
+ for (const name of fs22.readdirSync(cursorSkillsDir)) {
5610
+ const skillFile = path19.join(cursorSkillsDir, name, "SKILL.md");
5611
+ if (fs22.existsSync(skillFile)) {
4852
5612
  items.push({
4853
5613
  type: "skill",
4854
5614
  platform: "cursor",
@@ -4861,10 +5621,10 @@ function scanLocalState(dir) {
4861
5621
  } catch {
4862
5622
  }
4863
5623
  }
4864
- const cursorMcpPath = path17.join(dir, ".cursor", "mcp.json");
4865
- if (fs20.existsSync(cursorMcpPath)) {
5624
+ const cursorMcpPath = path19.join(dir, ".cursor", "mcp.json");
5625
+ if (fs22.existsSync(cursorMcpPath)) {
4866
5626
  try {
4867
- const mcpJson = JSON.parse(fs20.readFileSync(cursorMcpPath, "utf-8"));
5627
+ const mcpJson = JSON.parse(fs22.readFileSync(cursorMcpPath, "utf-8"));
4868
5628
  if (mcpJson.mcpServers) {
4869
5629
  for (const name of Object.keys(mcpJson.mcpServers)) {
4870
5630
  items.push({
@@ -4882,7 +5642,7 @@ function scanLocalState(dir) {
4882
5642
  return items;
4883
5643
  }
4884
5644
  function hashFile(filePath) {
4885
- const text = fs20.readFileSync(filePath, "utf-8");
5645
+ const text = fs22.readFileSync(filePath, "utf-8");
4886
5646
  return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
4887
5647
  }
4888
5648
  function hashJson(obj) {
@@ -5040,7 +5800,7 @@ async function searchAllProviders(technologies, platform) {
5040
5800
  }
5041
5801
  return combined;
5042
5802
  }
5043
- async function scoreWithLLM(candidates, projectContext, technologies) {
5803
+ async function scoreWithLLM2(candidates, projectContext, technologies) {
5044
5804
  const candidateList = candidates.map((c, i) => `${i}. "${c.name}" \u2014 ${c.reason || "no description"}`).join("\n");
5045
5805
  const scored = await llmJsonCall({
5046
5806
  system: `You evaluate whether AI agent skills and tools are relevant to a specific software project.
@@ -5193,11 +5953,11 @@ async function recommendCommand(options) {
5193
5953
  ...extractTopDeps()
5194
5954
  ].filter(Boolean))];
5195
5955
  if (technologies.length === 0) {
5196
- console.log(chalk8.yellow("Could not detect any languages or dependencies. Try running from a project root."));
5956
+ console.log(chalk9.yellow("Could not detect any languages or dependencies. Try running from a project root."));
5197
5957
  throw new Error("__exit__");
5198
5958
  }
5199
5959
  const primaryPlatform = platforms.includes("claude") ? "claude" : platforms[0];
5200
- const searchSpinner = ora4("Searching skill registries...").start();
5960
+ const searchSpinner = ora5("Searching skill registries...").start();
5201
5961
  const allCandidates = await searchAllProviders(technologies, primaryPlatform);
5202
5962
  if (!allCandidates.length) {
5203
5963
  searchSpinner.succeed("No skills found matching your tech stack.");
@@ -5210,15 +5970,15 @@ async function recommendCommand(options) {
5210
5970
  return;
5211
5971
  }
5212
5972
  searchSpinner.succeed(
5213
- `Found ${allCandidates.length} skills` + (filteredCount > 0 ? chalk8.dim(` (${filteredCount} already installed)`) : "")
5973
+ `Found ${allCandidates.length} skills` + (filteredCount > 0 ? chalk9.dim(` (${filteredCount} already installed)`) : "")
5214
5974
  );
5215
5975
  let results;
5216
5976
  const config = loadConfig();
5217
5977
  if (config) {
5218
- const scoreSpinner = ora4("Scoring relevance for your project...").start();
5978
+ const scoreSpinner = ora5("Scoring relevance for your project...").start();
5219
5979
  try {
5220
5980
  const projectContext = buildProjectContext(process.cwd());
5221
- results = await scoreWithLLM(newCandidates, projectContext, technologies);
5981
+ results = await scoreWithLLM2(newCandidates, projectContext, technologies);
5222
5982
  if (results.length === 0) {
5223
5983
  scoreSpinner.succeed("No highly relevant skills found for your specific project.");
5224
5984
  return;
@@ -5231,7 +5991,7 @@ async function recommendCommand(options) {
5231
5991
  } else {
5232
5992
  results = newCandidates.slice(0, 20);
5233
5993
  }
5234
- const fetchSpinner = ora4("Verifying skill availability...").start();
5994
+ const fetchSpinner = ora5("Verifying skill availability...").start();
5235
5995
  const contentMap = /* @__PURE__ */ new Map();
5236
5996
  await Promise.all(results.map(async (rec) => {
5237
5997
  const content = await fetchSkillContent(rec);
@@ -5244,14 +6004,14 @@ async function recommendCommand(options) {
5244
6004
  }
5245
6005
  const unavailableCount = results.length - available.length;
5246
6006
  fetchSpinner.succeed(
5247
- `${available.length} installable skill${available.length > 1 ? "s" : ""}` + (unavailableCount > 0 ? chalk8.dim(` (${unavailableCount} unavailable)`) : "")
6007
+ `${available.length} installable skill${available.length > 1 ? "s" : ""}` + (unavailableCount > 0 ? chalk9.dim(` (${unavailableCount} unavailable)`) : "")
5248
6008
  );
5249
- const selected = await interactiveSelect(available);
6009
+ const selected = await interactiveSelect2(available);
5250
6010
  if (selected?.length) {
5251
6011
  await installSkills(selected, platforms, contentMap);
5252
6012
  }
5253
6013
  }
5254
- async function interactiveSelect(recs) {
6014
+ async function interactiveSelect2(recs) {
5255
6015
  if (!process.stdin.isTTY) {
5256
6016
  printRecommendations(recs);
5257
6017
  return null;
@@ -5263,27 +6023,27 @@ async function interactiveSelect(recs) {
5263
6023
  const hasScores = recs.some((r) => r.score > 0);
5264
6024
  function render() {
5265
6025
  const lines = [];
5266
- lines.push(chalk8.bold(" Recommendations"));
6026
+ lines.push(chalk9.bold(" Recommendations"));
5267
6027
  lines.push("");
5268
6028
  if (hasScores) {
5269
- lines.push(` ${chalk8.dim("Score".padEnd(7))} ${chalk8.dim("Name".padEnd(28))} ${chalk8.dim("Why")}`);
6029
+ lines.push(` ${chalk9.dim("Score".padEnd(7))} ${chalk9.dim("Name".padEnd(28))} ${chalk9.dim("Why")}`);
5270
6030
  } else {
5271
- lines.push(` ${chalk8.dim("Name".padEnd(30))} ${chalk8.dim("Technology".padEnd(18))} ${chalk8.dim("Source")}`);
6031
+ lines.push(` ${chalk9.dim("Name".padEnd(30))} ${chalk9.dim("Technology".padEnd(18))} ${chalk9.dim("Source")}`);
5272
6032
  }
5273
- lines.push(chalk8.dim(" " + "\u2500".repeat(70)));
6033
+ lines.push(chalk9.dim(" " + "\u2500".repeat(70)));
5274
6034
  for (let i = 0; i < recs.length; i++) {
5275
6035
  const rec = recs[i];
5276
- const check = selected.has(i) ? chalk8.green("[x]") : "[ ]";
5277
- const ptr = i === cursor ? chalk8.cyan(">") : " ";
6036
+ const check = selected.has(i) ? chalk9.green("[x]") : "[ ]";
6037
+ const ptr = i === cursor ? chalk9.cyan(">") : " ";
5278
6038
  if (hasScores) {
5279
- const scoreColor = rec.score >= 90 ? chalk8.green : rec.score >= 70 ? chalk8.yellow : chalk8.dim;
5280
- lines.push(` ${ptr} ${check} ${scoreColor(String(rec.score).padStart(3))} ${rec.name.padEnd(26)} ${chalk8.dim(rec.reason.slice(0, 40))}`);
6039
+ const scoreColor = rec.score >= 90 ? chalk9.green : rec.score >= 70 ? chalk9.yellow : chalk9.dim;
6040
+ lines.push(` ${ptr} ${check} ${scoreColor(String(rec.score).padStart(3))} ${rec.name.padEnd(26)} ${chalk9.dim(rec.reason.slice(0, 40))}`);
5281
6041
  } else {
5282
- lines.push(` ${ptr} ${check} ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${chalk8.dim(rec.source_url || "")}`);
6042
+ lines.push(` ${ptr} ${check} ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${chalk9.dim(rec.source_url || "")}`);
5283
6043
  }
5284
6044
  }
5285
6045
  lines.push("");
5286
- lines.push(chalk8.dim(" \u2191\u2193 navigate \u23B5 toggle a all n none \u23CE install q cancel"));
6046
+ lines.push(chalk9.dim(" \u2191\u2193 navigate \u23B5 toggle a all n none \u23CE install q cancel"));
5287
6047
  return lines.join("\n");
5288
6048
  }
5289
6049
  function draw(initial) {
@@ -5332,7 +6092,7 @@ async function interactiveSelect(recs) {
5332
6092
  case "\n":
5333
6093
  cleanup();
5334
6094
  if (selected.size === 0) {
5335
- console.log(chalk8.dim("\n No skills selected.\n"));
6095
+ console.log(chalk9.dim("\n No skills selected.\n"));
5336
6096
  resolve2(null);
5337
6097
  } else {
5338
6098
  resolve2(Array.from(selected).sort().map((i) => recs[i]));
@@ -5342,7 +6102,7 @@ async function interactiveSelect(recs) {
5342
6102
  case "\x1B":
5343
6103
  case "":
5344
6104
  cleanup();
5345
- console.log(chalk8.dim("\n Cancelled.\n"));
6105
+ console.log(chalk9.dim("\n Cancelled.\n"));
5346
6106
  resolve2(null);
5347
6107
  break;
5348
6108
  }
@@ -5388,7 +6148,7 @@ async function fetchSkillContent(rec) {
5388
6148
  return null;
5389
6149
  }
5390
6150
  async function installSkills(recs, platforms, contentMap) {
5391
- const spinner = ora4(`Installing ${recs.length} skill${recs.length > 1 ? "s" : ""}...`).start();
6151
+ const spinner = ora5(`Installing ${recs.length} skill${recs.length > 1 ? "s" : ""}...`).start();
5392
6152
  const installed = [];
5393
6153
  for (const rec of recs) {
5394
6154
  const content = contentMap.get(rec.slug);
@@ -5404,7 +6164,7 @@ async function installSkills(recs, platforms, contentMap) {
5404
6164
  if (installed.length > 0) {
5405
6165
  spinner.succeed(`Installed ${installed.length} file${installed.length > 1 ? "s" : ""}`);
5406
6166
  for (const p of installed) {
5407
- console.log(chalk8.green(` \u2713 ${p}`));
6167
+ console.log(chalk9.green(` \u2713 ${p}`));
5408
6168
  }
5409
6169
  } else {
5410
6170
  spinner.fail("No skills were installed");
@@ -5413,25 +6173,25 @@ async function installSkills(recs, platforms, contentMap) {
5413
6173
  }
5414
6174
  function printRecommendations(recs) {
5415
6175
  const hasScores = recs.some((r) => r.score > 0);
5416
- console.log(chalk8.bold("\n Recommendations\n"));
6176
+ console.log(chalk9.bold("\n Recommendations\n"));
5417
6177
  if (hasScores) {
5418
- console.log(` ${chalk8.dim("Score".padEnd(7))} ${chalk8.dim("Name".padEnd(28))} ${chalk8.dim("Why")}`);
6178
+ console.log(` ${chalk9.dim("Score".padEnd(7))} ${chalk9.dim("Name".padEnd(28))} ${chalk9.dim("Why")}`);
5419
6179
  } else {
5420
- console.log(` ${chalk8.dim("Name".padEnd(30))} ${chalk8.dim("Technology".padEnd(18))} ${chalk8.dim("Source")}`);
6180
+ console.log(` ${chalk9.dim("Name".padEnd(30))} ${chalk9.dim("Technology".padEnd(18))} ${chalk9.dim("Source")}`);
5421
6181
  }
5422
- console.log(chalk8.dim(" " + "\u2500".repeat(70)));
6182
+ console.log(chalk9.dim(" " + "\u2500".repeat(70)));
5423
6183
  for (const rec of recs) {
5424
6184
  if (hasScores) {
5425
- console.log(` ${String(rec.score).padStart(3)} ${rec.name.padEnd(26)} ${chalk8.dim(rec.reason.slice(0, 50))}`);
6185
+ console.log(` ${String(rec.score).padStart(3)} ${rec.name.padEnd(26)} ${chalk9.dim(rec.reason.slice(0, 50))}`);
5426
6186
  } else {
5427
- console.log(` ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${chalk8.dim(rec.source_url || "")}`);
6187
+ console.log(` ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${chalk9.dim(rec.source_url || "")}`);
5428
6188
  }
5429
6189
  }
5430
6190
  console.log("");
5431
6191
  }
5432
6192
 
5433
6193
  // src/commands/score.ts
5434
- import chalk9 from "chalk";
6194
+ import chalk10 from "chalk";
5435
6195
  async function scoreCommand(options) {
5436
6196
  const dir = process.cwd();
5437
6197
  const target = options.agent ?? readState()?.targetAgent;
@@ -5445,23 +6205,23 @@ async function scoreCommand(options) {
5445
6205
  return;
5446
6206
  }
5447
6207
  displayScore(result);
5448
- const separator = chalk9.gray(" " + "\u2500".repeat(53));
6208
+ const separator = chalk10.gray(" " + "\u2500".repeat(53));
5449
6209
  console.log(separator);
5450
6210
  if (result.score < 40) {
5451
- console.log(chalk9.gray(" Run ") + chalk9.hex("#83D1EB")("caliber onboard") + chalk9.gray(" to generate a complete, optimized setup."));
6211
+ console.log(chalk10.gray(" Run ") + chalk10.hex("#83D1EB")("caliber onboard") + chalk10.gray(" to generate a complete, optimized setup."));
5452
6212
  } else if (result.score < 70) {
5453
- console.log(chalk9.gray(" Run ") + chalk9.hex("#83D1EB")("caliber onboard") + chalk9.gray(" to improve your setup."));
6213
+ console.log(chalk10.gray(" Run ") + chalk10.hex("#83D1EB")("caliber onboard") + chalk10.gray(" to improve your setup."));
5454
6214
  } else {
5455
- console.log(chalk9.green(" Looking good!") + chalk9.gray(" Run ") + chalk9.hex("#83D1EB")("caliber update") + chalk9.gray(" to keep it fresh."));
6215
+ console.log(chalk10.green(" Looking good!") + chalk10.gray(" Run ") + chalk10.hex("#83D1EB")("caliber update") + chalk10.gray(" to keep it fresh."));
5456
6216
  }
5457
6217
  console.log("");
5458
6218
  }
5459
6219
 
5460
6220
  // src/commands/refresh.ts
5461
- import fs22 from "fs";
5462
- import path19 from "path";
5463
- import chalk10 from "chalk";
5464
- import ora5 from "ora";
6221
+ import fs24 from "fs";
6222
+ import path21 from "path";
6223
+ import chalk11 from "chalk";
6224
+ import ora6 from "ora";
5465
6225
 
5466
6226
  // src/lib/git-diff.ts
5467
6227
  import { execSync as execSync8 } from "child_process";
@@ -5536,37 +6296,37 @@ function collectDiff(lastSha) {
5536
6296
  }
5537
6297
 
5538
6298
  // src/writers/refresh.ts
5539
- import fs21 from "fs";
5540
- import path18 from "path";
6299
+ import fs23 from "fs";
6300
+ import path20 from "path";
5541
6301
  function writeRefreshDocs(docs) {
5542
6302
  const written = [];
5543
6303
  if (docs.claudeMd) {
5544
- fs21.writeFileSync("CLAUDE.md", docs.claudeMd);
6304
+ fs23.writeFileSync("CLAUDE.md", docs.claudeMd);
5545
6305
  written.push("CLAUDE.md");
5546
6306
  }
5547
6307
  if (docs.readmeMd) {
5548
- fs21.writeFileSync("README.md", docs.readmeMd);
6308
+ fs23.writeFileSync("README.md", docs.readmeMd);
5549
6309
  written.push("README.md");
5550
6310
  }
5551
6311
  if (docs.cursorrules) {
5552
- fs21.writeFileSync(".cursorrules", docs.cursorrules);
6312
+ fs23.writeFileSync(".cursorrules", docs.cursorrules);
5553
6313
  written.push(".cursorrules");
5554
6314
  }
5555
6315
  if (docs.cursorRules) {
5556
- const rulesDir = path18.join(".cursor", "rules");
5557
- if (!fs21.existsSync(rulesDir)) fs21.mkdirSync(rulesDir, { recursive: true });
6316
+ const rulesDir = path20.join(".cursor", "rules");
6317
+ if (!fs23.existsSync(rulesDir)) fs23.mkdirSync(rulesDir, { recursive: true });
5558
6318
  for (const rule of docs.cursorRules) {
5559
- const filePath = path18.join(rulesDir, rule.filename);
5560
- fs21.writeFileSync(filePath, rule.content);
6319
+ const filePath = path20.join(rulesDir, rule.filename);
6320
+ fs23.writeFileSync(filePath, rule.content);
5561
6321
  written.push(filePath);
5562
6322
  }
5563
6323
  }
5564
6324
  if (docs.claudeSkills) {
5565
- const skillsDir = path18.join(".claude", "skills");
5566
- if (!fs21.existsSync(skillsDir)) fs21.mkdirSync(skillsDir, { recursive: true });
6325
+ const skillsDir = path20.join(".claude", "skills");
6326
+ if (!fs23.existsSync(skillsDir)) fs23.mkdirSync(skillsDir, { recursive: true });
5567
6327
  for (const skill of docs.claudeSkills) {
5568
- const filePath = path18.join(skillsDir, skill.filename);
5569
- fs21.writeFileSync(filePath, skill.content);
6328
+ const filePath = path20.join(skillsDir, skill.filename);
6329
+ fs23.writeFileSync(filePath, skill.content);
5570
6330
  written.push(filePath);
5571
6331
  }
5572
6332
  }
@@ -5641,11 +6401,11 @@ function log(quiet, ...args) {
5641
6401
  function discoverGitRepos(parentDir) {
5642
6402
  const repos = [];
5643
6403
  try {
5644
- const entries = fs22.readdirSync(parentDir, { withFileTypes: true });
6404
+ const entries = fs24.readdirSync(parentDir, { withFileTypes: true });
5645
6405
  for (const entry of entries) {
5646
6406
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
5647
- const childPath = path19.join(parentDir, entry.name);
5648
- if (fs22.existsSync(path19.join(childPath, ".git"))) {
6407
+ const childPath = path21.join(parentDir, entry.name);
6408
+ if (fs24.existsSync(path21.join(childPath, ".git"))) {
5649
6409
  repos.push(childPath);
5650
6410
  }
5651
6411
  }
@@ -5655,7 +6415,7 @@ function discoverGitRepos(parentDir) {
5655
6415
  }
5656
6416
  async function refreshSingleRepo(repoDir, options) {
5657
6417
  const quiet = !!options.quiet;
5658
- const prefix = options.label ? `${chalk10.bold(options.label)} ` : "";
6418
+ const prefix = options.label ? `${chalk11.bold(options.label)} ` : "";
5659
6419
  const state = readState();
5660
6420
  const lastSha = state?.lastRefreshSha ?? null;
5661
6421
  const diff = collectDiff(lastSha);
@@ -5664,10 +6424,10 @@ async function refreshSingleRepo(repoDir, options) {
5664
6424
  if (currentSha) {
5665
6425
  writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
5666
6426
  }
5667
- log(quiet, chalk10.dim(`${prefix}No changes since last refresh.`));
6427
+ log(quiet, chalk11.dim(`${prefix}No changes since last refresh.`));
5668
6428
  return;
5669
6429
  }
5670
- const spinner = quiet ? null : ora5(`${prefix}Analyzing changes...`).start();
6430
+ const spinner = quiet ? null : ora6(`${prefix}Analyzing changes...`).start();
5671
6431
  const existingDocs = readExistingConfigs(repoDir);
5672
6432
  const fingerprint = collectFingerprint(repoDir);
5673
6433
  const projectContext = {
@@ -5696,10 +6456,10 @@ async function refreshSingleRepo(repoDir, options) {
5696
6456
  if (options.dryRun) {
5697
6457
  spinner?.info(`${prefix}Dry run \u2014 would update:`);
5698
6458
  for (const doc of response.docsUpdated) {
5699
- console.log(` ${chalk10.yellow("~")} ${doc}`);
6459
+ console.log(` ${chalk11.yellow("~")} ${doc}`);
5700
6460
  }
5701
6461
  if (response.changesSummary) {
5702
- console.log(chalk10.dim(`
6462
+ console.log(chalk11.dim(`
5703
6463
  ${response.changesSummary}`));
5704
6464
  }
5705
6465
  return;
@@ -5707,10 +6467,10 @@ async function refreshSingleRepo(repoDir, options) {
5707
6467
  const written = writeRefreshDocs(response.updatedDocs);
5708
6468
  spinner?.succeed(`${prefix}Updated ${written.length} doc${written.length === 1 ? "" : "s"}`);
5709
6469
  for (const file of written) {
5710
- log(quiet, ` ${chalk10.green("\u2713")} ${file}`);
6470
+ log(quiet, ` ${chalk11.green("\u2713")} ${file}`);
5711
6471
  }
5712
6472
  if (response.changesSummary) {
5713
- log(quiet, chalk10.dim(`
6473
+ log(quiet, chalk11.dim(`
5714
6474
  ${response.changesSummary}`));
5715
6475
  }
5716
6476
  if (currentSha) {
@@ -5723,7 +6483,7 @@ async function refreshCommand(options) {
5723
6483
  const config = loadConfig();
5724
6484
  if (!config) {
5725
6485
  if (quiet) return;
5726
- console.log(chalk10.red("No LLM provider configured. Run ") + chalk10.hex("#83D1EB")("caliber config") + chalk10.red(" (e.g. choose Cursor) or set an API key."));
6486
+ console.log(chalk11.red("No LLM provider configured. Run ") + chalk11.hex("#83D1EB")("caliber config") + chalk11.red(" (e.g. choose Cursor) or set an API key."));
5727
6487
  throw new Error("__exit__");
5728
6488
  }
5729
6489
  if (isGitRepo()) {
@@ -5733,20 +6493,20 @@ async function refreshCommand(options) {
5733
6493
  const repos = discoverGitRepos(process.cwd());
5734
6494
  if (repos.length === 0) {
5735
6495
  if (quiet) return;
5736
- console.log(chalk10.red("Not inside a git repository and no git repos found in child directories."));
6496
+ console.log(chalk11.red("Not inside a git repository and no git repos found in child directories."));
5737
6497
  throw new Error("__exit__");
5738
6498
  }
5739
- log(quiet, chalk10.dim(`Found ${repos.length} git repo${repos.length === 1 ? "" : "s"}
6499
+ log(quiet, chalk11.dim(`Found ${repos.length} git repo${repos.length === 1 ? "" : "s"}
5740
6500
  `));
5741
6501
  const originalDir = process.cwd();
5742
6502
  for (const repo of repos) {
5743
- const repoName = path19.basename(repo);
6503
+ const repoName = path21.basename(repo);
5744
6504
  try {
5745
6505
  process.chdir(repo);
5746
6506
  await refreshSingleRepo(repo, { ...options, label: repoName });
5747
6507
  } catch (err) {
5748
6508
  if (err instanceof Error && err.message === "__exit__") continue;
5749
- log(quiet, chalk10.yellow(`${repoName}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`));
6509
+ log(quiet, chalk11.yellow(`${repoName}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`));
5750
6510
  }
5751
6511
  }
5752
6512
  process.chdir(originalDir);
@@ -5754,13 +6514,13 @@ async function refreshCommand(options) {
5754
6514
  if (err instanceof Error && err.message === "__exit__") throw err;
5755
6515
  if (quiet) return;
5756
6516
  const msg = err instanceof Error ? err.message : "Unknown error";
5757
- console.log(chalk10.red(`Refresh failed: ${msg}`));
6517
+ console.log(chalk11.red(`Refresh failed: ${msg}`));
5758
6518
  throw new Error("__exit__");
5759
6519
  }
5760
6520
  }
5761
6521
 
5762
6522
  // src/commands/hooks.ts
5763
- import chalk11 from "chalk";
6523
+ import chalk12 from "chalk";
5764
6524
  var HOOKS = [
5765
6525
  {
5766
6526
  id: "session-end",
@@ -5780,13 +6540,13 @@ var HOOKS = [
5780
6540
  }
5781
6541
  ];
5782
6542
  function printStatus() {
5783
- console.log(chalk11.bold("\n Hooks\n"));
6543
+ console.log(chalk12.bold("\n Hooks\n"));
5784
6544
  for (const hook of HOOKS) {
5785
6545
  const installed = hook.isInstalled();
5786
- const icon = installed ? chalk11.green("\u2713") : chalk11.dim("\u2717");
5787
- const state = installed ? chalk11.green("enabled") : chalk11.dim("disabled");
6546
+ const icon = installed ? chalk12.green("\u2713") : chalk12.dim("\u2717");
6547
+ const state = installed ? chalk12.green("enabled") : chalk12.dim("disabled");
5788
6548
  console.log(` ${icon} ${hook.label.padEnd(26)} ${state}`);
5789
- console.log(chalk11.dim(` ${hook.description}`));
6549
+ console.log(chalk12.dim(` ${hook.description}`));
5790
6550
  }
5791
6551
  console.log("");
5792
6552
  }
@@ -5795,9 +6555,9 @@ async function hooksCommand(options) {
5795
6555
  for (const hook of HOOKS) {
5796
6556
  const result = hook.install();
5797
6557
  if (result.alreadyInstalled) {
5798
- console.log(chalk11.dim(` ${hook.label} already enabled.`));
6558
+ console.log(chalk12.dim(` ${hook.label} already enabled.`));
5799
6559
  } else {
5800
- console.log(chalk11.green(" \u2713") + ` ${hook.label} enabled`);
6560
+ console.log(chalk12.green(" \u2713") + ` ${hook.label} enabled`);
5801
6561
  }
5802
6562
  }
5803
6563
  return;
@@ -5806,9 +6566,9 @@ async function hooksCommand(options) {
5806
6566
  for (const hook of HOOKS) {
5807
6567
  const result = hook.remove();
5808
6568
  if (result.notFound) {
5809
- console.log(chalk11.dim(` ${hook.label} already disabled.`));
6569
+ console.log(chalk12.dim(` ${hook.label} already disabled.`));
5810
6570
  } else {
5811
- console.log(chalk11.green(" \u2713") + ` ${hook.label} removed`);
6571
+ console.log(chalk12.green(" \u2713") + ` ${hook.label} removed`);
5812
6572
  }
5813
6573
  }
5814
6574
  return;
@@ -5823,18 +6583,18 @@ async function hooksCommand(options) {
5823
6583
  const states = HOOKS.map((h) => h.isInstalled());
5824
6584
  function render() {
5825
6585
  const lines = [];
5826
- lines.push(chalk11.bold(" Hooks"));
6586
+ lines.push(chalk12.bold(" Hooks"));
5827
6587
  lines.push("");
5828
6588
  for (let i = 0; i < HOOKS.length; i++) {
5829
6589
  const hook = HOOKS[i];
5830
6590
  const enabled = states[i];
5831
- const toggle = enabled ? chalk11.green("[on] ") : chalk11.dim("[off]");
5832
- const ptr = i === cursor ? chalk11.cyan(">") : " ";
6591
+ const toggle = enabled ? chalk12.green("[on] ") : chalk12.dim("[off]");
6592
+ const ptr = i === cursor ? chalk12.cyan(">") : " ";
5833
6593
  lines.push(` ${ptr} ${toggle} ${hook.label}`);
5834
- lines.push(chalk11.dim(` ${hook.description}`));
6594
+ lines.push(chalk12.dim(` ${hook.description}`));
5835
6595
  }
5836
6596
  lines.push("");
5837
- lines.push(chalk11.dim(" \u2191\u2193 navigate \u23B5 toggle a all on n all off \u23CE apply q cancel"));
6597
+ lines.push(chalk12.dim(" \u2191\u2193 navigate \u23B5 toggle a all on n all off \u23CE apply q cancel"));
5838
6598
  return lines.join("\n");
5839
6599
  }
5840
6600
  function draw(initial) {
@@ -5865,16 +6625,16 @@ async function hooksCommand(options) {
5865
6625
  const wantEnabled = states[i];
5866
6626
  if (wantEnabled && !wasInstalled) {
5867
6627
  hook.install();
5868
- console.log(chalk11.green(" \u2713") + ` ${hook.label} enabled`);
6628
+ console.log(chalk12.green(" \u2713") + ` ${hook.label} enabled`);
5869
6629
  changed++;
5870
6630
  } else if (!wantEnabled && wasInstalled) {
5871
6631
  hook.remove();
5872
- console.log(chalk11.green(" \u2713") + ` ${hook.label} disabled`);
6632
+ console.log(chalk12.green(" \u2713") + ` ${hook.label} disabled`);
5873
6633
  changed++;
5874
6634
  }
5875
6635
  }
5876
6636
  if (changed === 0) {
5877
- console.log(chalk11.dim(" No changes."));
6637
+ console.log(chalk12.dim(" No changes."));
5878
6638
  }
5879
6639
  console.log("");
5880
6640
  }
@@ -5910,7 +6670,7 @@ async function hooksCommand(options) {
5910
6670
  case "\x1B":
5911
6671
  case "":
5912
6672
  cleanup();
5913
- console.log(chalk11.dim("\n Cancelled.\n"));
6673
+ console.log(chalk12.dim("\n Cancelled.\n"));
5914
6674
  resolve2();
5915
6675
  break;
5916
6676
  }
@@ -5920,43 +6680,43 @@ async function hooksCommand(options) {
5920
6680
  }
5921
6681
 
5922
6682
  // src/commands/config.ts
5923
- import chalk12 from "chalk";
6683
+ import chalk13 from "chalk";
5924
6684
  async function configCommand() {
5925
6685
  const existing = loadConfig();
5926
6686
  if (existing) {
5927
- console.log(chalk12.bold("\nCurrent Configuration\n"));
5928
- console.log(` Provider: ${chalk12.cyan(existing.provider)}`);
5929
- console.log(` Model: ${chalk12.cyan(existing.model)}`);
6687
+ console.log(chalk13.bold("\nCurrent Configuration\n"));
6688
+ console.log(` Provider: ${chalk13.cyan(existing.provider)}`);
6689
+ console.log(` Model: ${chalk13.cyan(existing.model)}`);
5930
6690
  if (existing.apiKey) {
5931
6691
  const masked = existing.apiKey.slice(0, 8) + "..." + existing.apiKey.slice(-4);
5932
- console.log(` API Key: ${chalk12.dim(masked)}`);
6692
+ console.log(` API Key: ${chalk13.dim(masked)}`);
5933
6693
  }
5934
6694
  if (existing.provider === "cursor") {
5935
- console.log(` Seat: ${chalk12.dim("Cursor (agent acp)")}`);
6695
+ console.log(` Seat: ${chalk13.dim("Cursor (agent acp)")}`);
5936
6696
  }
5937
6697
  if (existing.provider === "claude-cli") {
5938
- console.log(` Seat: ${chalk12.dim("Claude Code (claude -p)")}`);
6698
+ console.log(` Seat: ${chalk13.dim("Claude Code (claude -p)")}`);
5939
6699
  }
5940
6700
  if (existing.baseUrl) {
5941
- console.log(` Base URL: ${chalk12.dim(existing.baseUrl)}`);
6701
+ console.log(` Base URL: ${chalk13.dim(existing.baseUrl)}`);
5942
6702
  }
5943
6703
  if (existing.vertexProjectId) {
5944
- console.log(` Vertex Project: ${chalk12.dim(existing.vertexProjectId)}`);
5945
- console.log(` Vertex Region: ${chalk12.dim(existing.vertexRegion || "us-east5")}`);
6704
+ console.log(` Vertex Project: ${chalk13.dim(existing.vertexProjectId)}`);
6705
+ console.log(` Vertex Region: ${chalk13.dim(existing.vertexRegion || "us-east5")}`);
5946
6706
  }
5947
- console.log(` Source: ${chalk12.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())}`);
6707
+ console.log(` Source: ${chalk13.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())}`);
5948
6708
  console.log("");
5949
6709
  }
5950
6710
  await runInteractiveProviderSetup();
5951
- console.log(chalk12.green("\n\u2713 Configuration saved"));
5952
- console.log(chalk12.dim(` ${getConfigFilePath()}
6711
+ console.log(chalk13.green("\n\u2713 Configuration saved"));
6712
+ console.log(chalk13.dim(` ${getConfigFilePath()}
5953
6713
  `));
5954
- console.log(chalk12.dim(" You can also set environment variables instead:"));
5955
- console.log(chalk12.dim(" ANTHROPIC_API_KEY, OPENAI_API_KEY, VERTEX_PROJECT_ID, CALIBER_USE_CURSOR_SEAT=1, or CALIBER_USE_CLAUDE_CLI=1\n"));
6714
+ console.log(chalk13.dim(" You can also set environment variables instead:"));
6715
+ console.log(chalk13.dim(" ANTHROPIC_API_KEY, OPENAI_API_KEY, VERTEX_PROJECT_ID, CALIBER_USE_CURSOR_SEAT=1, or CALIBER_USE_CLAUDE_CLI=1\n"));
5956
6716
  }
5957
6717
 
5958
6718
  // src/commands/learn.ts
5959
- import chalk13 from "chalk";
6719
+ import chalk14 from "chalk";
5960
6720
 
5961
6721
  // src/learner/stdin.ts
5962
6722
  var STDIN_TIMEOUT_MS = 5e3;
@@ -5987,8 +6747,8 @@ function readStdin() {
5987
6747
 
5988
6748
  // src/learner/storage.ts
5989
6749
  init_constants();
5990
- import fs23 from "fs";
5991
- import path20 from "path";
6750
+ import fs25 from "fs";
6751
+ import path22 from "path";
5992
6752
  var MAX_RESPONSE_LENGTH = 2e3;
5993
6753
  var DEFAULT_STATE = {
5994
6754
  sessionId: null,
@@ -5996,15 +6756,15 @@ var DEFAULT_STATE = {
5996
6756
  lastAnalysisTimestamp: null
5997
6757
  };
5998
6758
  function ensureLearningDir() {
5999
- if (!fs23.existsSync(LEARNING_DIR)) {
6000
- fs23.mkdirSync(LEARNING_DIR, { recursive: true });
6759
+ if (!fs25.existsSync(LEARNING_DIR)) {
6760
+ fs25.mkdirSync(LEARNING_DIR, { recursive: true });
6001
6761
  }
6002
6762
  }
6003
6763
  function sessionFilePath() {
6004
- return path20.join(LEARNING_DIR, LEARNING_SESSION_FILE);
6764
+ return path22.join(LEARNING_DIR, LEARNING_SESSION_FILE);
6005
6765
  }
6006
6766
  function stateFilePath() {
6007
- return path20.join(LEARNING_DIR, LEARNING_STATE_FILE);
6767
+ return path22.join(LEARNING_DIR, LEARNING_STATE_FILE);
6008
6768
  }
6009
6769
  function truncateResponse(response) {
6010
6770
  const str = JSON.stringify(response);
@@ -6015,50 +6775,50 @@ function appendEvent(event) {
6015
6775
  ensureLearningDir();
6016
6776
  const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
6017
6777
  const filePath = sessionFilePath();
6018
- fs23.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
6778
+ fs25.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
6019
6779
  const count = getEventCount();
6020
6780
  if (count > LEARNING_MAX_EVENTS) {
6021
- const lines = fs23.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
6781
+ const lines = fs25.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
6022
6782
  const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
6023
- fs23.writeFileSync(filePath, kept.join("\n") + "\n");
6783
+ fs25.writeFileSync(filePath, kept.join("\n") + "\n");
6024
6784
  }
6025
6785
  }
6026
6786
  function readAllEvents() {
6027
6787
  const filePath = sessionFilePath();
6028
- if (!fs23.existsSync(filePath)) return [];
6029
- const lines = fs23.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
6788
+ if (!fs25.existsSync(filePath)) return [];
6789
+ const lines = fs25.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
6030
6790
  return lines.map((line) => JSON.parse(line));
6031
6791
  }
6032
6792
  function getEventCount() {
6033
6793
  const filePath = sessionFilePath();
6034
- if (!fs23.existsSync(filePath)) return 0;
6035
- const content = fs23.readFileSync(filePath, "utf-8");
6794
+ if (!fs25.existsSync(filePath)) return 0;
6795
+ const content = fs25.readFileSync(filePath, "utf-8");
6036
6796
  return content.split("\n").filter(Boolean).length;
6037
6797
  }
6038
6798
  function clearSession() {
6039
6799
  const filePath = sessionFilePath();
6040
- if (fs23.existsSync(filePath)) fs23.unlinkSync(filePath);
6800
+ if (fs25.existsSync(filePath)) fs25.unlinkSync(filePath);
6041
6801
  }
6042
6802
  function readState2() {
6043
6803
  const filePath = stateFilePath();
6044
- if (!fs23.existsSync(filePath)) return { ...DEFAULT_STATE };
6804
+ if (!fs25.existsSync(filePath)) return { ...DEFAULT_STATE };
6045
6805
  try {
6046
- return JSON.parse(fs23.readFileSync(filePath, "utf-8"));
6806
+ return JSON.parse(fs25.readFileSync(filePath, "utf-8"));
6047
6807
  } catch {
6048
6808
  return { ...DEFAULT_STATE };
6049
6809
  }
6050
6810
  }
6051
6811
  function writeState2(state) {
6052
6812
  ensureLearningDir();
6053
- fs23.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
6813
+ fs25.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
6054
6814
  }
6055
6815
  function resetState() {
6056
6816
  writeState2({ ...DEFAULT_STATE });
6057
6817
  }
6058
6818
 
6059
6819
  // src/learner/writer.ts
6060
- import fs24 from "fs";
6061
- import path21 from "path";
6820
+ import fs26 from "fs";
6821
+ import path23 from "path";
6062
6822
  var LEARNED_START = "<!-- caliber:learned -->";
6063
6823
  var LEARNED_END = "<!-- /caliber:learned -->";
6064
6824
  function writeLearnedContent(update) {
@@ -6078,8 +6838,8 @@ function writeLearnedContent(update) {
6078
6838
  function writeLearnedSection(content) {
6079
6839
  const claudeMdPath = "CLAUDE.md";
6080
6840
  let existing = "";
6081
- if (fs24.existsSync(claudeMdPath)) {
6082
- existing = fs24.readFileSync(claudeMdPath, "utf-8");
6841
+ if (fs26.existsSync(claudeMdPath)) {
6842
+ existing = fs26.readFileSync(claudeMdPath, "utf-8");
6083
6843
  }
6084
6844
  const section = `${LEARNED_START}
6085
6845
  ${content}
@@ -6093,15 +6853,15 @@ ${LEARNED_END}`;
6093
6853
  const separator = existing.endsWith("\n") || existing === "" ? "" : "\n";
6094
6854
  updated = existing + separator + "\n" + section + "\n";
6095
6855
  }
6096
- fs24.writeFileSync(claudeMdPath, updated);
6856
+ fs26.writeFileSync(claudeMdPath, updated);
6097
6857
  }
6098
6858
  function writeLearnedSkill(skill) {
6099
- const skillDir = path21.join(".claude", "skills", skill.name);
6100
- if (!fs24.existsSync(skillDir)) fs24.mkdirSync(skillDir, { recursive: true });
6101
- const skillPath = path21.join(skillDir, "SKILL.md");
6102
- if (!skill.isNew && fs24.existsSync(skillPath)) {
6103
- const existing = fs24.readFileSync(skillPath, "utf-8");
6104
- fs24.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
6859
+ const skillDir = path23.join(".claude", "skills", skill.name);
6860
+ if (!fs26.existsSync(skillDir)) fs26.mkdirSync(skillDir, { recursive: true });
6861
+ const skillPath = path23.join(skillDir, "SKILL.md");
6862
+ if (!skill.isNew && fs26.existsSync(skillPath)) {
6863
+ const existing = fs26.readFileSync(skillPath, "utf-8");
6864
+ fs26.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
6105
6865
  } else {
6106
6866
  const frontmatter = [
6107
6867
  "---",
@@ -6110,14 +6870,14 @@ function writeLearnedSkill(skill) {
6110
6870
  "---",
6111
6871
  ""
6112
6872
  ].join("\n");
6113
- fs24.writeFileSync(skillPath, frontmatter + skill.content);
6873
+ fs26.writeFileSync(skillPath, frontmatter + skill.content);
6114
6874
  }
6115
6875
  return skillPath;
6116
6876
  }
6117
6877
  function readLearnedSection() {
6118
6878
  const claudeMdPath = "CLAUDE.md";
6119
- if (!fs24.existsSync(claudeMdPath)) return null;
6120
- const content = fs24.readFileSync(claudeMdPath, "utf-8");
6879
+ if (!fs26.existsSync(claudeMdPath)) return null;
6880
+ const content = fs26.readFileSync(claudeMdPath, "utf-8");
6121
6881
  const startIdx = content.indexOf(LEARNED_START);
6122
6882
  const endIdx = content.indexOf(LEARNED_END);
6123
6883
  if (startIdx === -1 || endIdx === -1) return null;
@@ -6257,53 +7017,53 @@ async function learnFinalizeCommand() {
6257
7017
  async function learnInstallCommand() {
6258
7018
  const result = installLearningHooks();
6259
7019
  if (result.alreadyInstalled) {
6260
- console.log(chalk13.dim("Learning hooks already installed."));
7020
+ console.log(chalk14.dim("Learning hooks already installed."));
6261
7021
  return;
6262
7022
  }
6263
- console.log(chalk13.green("\u2713") + " Learning hooks installed in .claude/settings.json");
6264
- console.log(chalk13.dim(" PostToolUse, PostToolUseFailure, and SessionEnd hooks active."));
6265
- console.log(chalk13.dim(" Session learnings will be written to CLAUDE.md and skills."));
7023
+ console.log(chalk14.green("\u2713") + " Learning hooks installed in .claude/settings.json");
7024
+ console.log(chalk14.dim(" PostToolUse, PostToolUseFailure, and SessionEnd hooks active."));
7025
+ console.log(chalk14.dim(" Session learnings will be written to CLAUDE.md and skills."));
6266
7026
  }
6267
7027
  async function learnRemoveCommand() {
6268
7028
  const result = removeLearningHooks();
6269
7029
  if (result.notFound) {
6270
- console.log(chalk13.dim("Learning hooks not found."));
7030
+ console.log(chalk14.dim("Learning hooks not found."));
6271
7031
  return;
6272
7032
  }
6273
- console.log(chalk13.green("\u2713") + " Learning hooks removed from .claude/settings.json");
7033
+ console.log(chalk14.green("\u2713") + " Learning hooks removed from .claude/settings.json");
6274
7034
  }
6275
7035
  async function learnStatusCommand() {
6276
7036
  const installed = areLearningHooksInstalled();
6277
7037
  const state = readState2();
6278
7038
  const eventCount = getEventCount();
6279
- console.log(chalk13.bold("Session Learning Status"));
7039
+ console.log(chalk14.bold("Session Learning Status"));
6280
7040
  console.log();
6281
7041
  if (installed) {
6282
- console.log(chalk13.green("\u2713") + " Learning hooks are " + chalk13.green("installed"));
7042
+ console.log(chalk14.green("\u2713") + " Learning hooks are " + chalk14.green("installed"));
6283
7043
  } else {
6284
- console.log(chalk13.dim("\u2717") + " Learning hooks are " + chalk13.yellow("not installed"));
6285
- console.log(chalk13.dim(" Run `caliber learn install` to enable session learning."));
7044
+ console.log(chalk14.dim("\u2717") + " Learning hooks are " + chalk14.yellow("not installed"));
7045
+ console.log(chalk14.dim(" Run `caliber learn install` to enable session learning."));
6286
7046
  }
6287
7047
  console.log();
6288
- console.log(`Events recorded: ${chalk13.cyan(String(eventCount))}`);
6289
- console.log(`Total this session: ${chalk13.cyan(String(state.eventCount))}`);
7048
+ console.log(`Events recorded: ${chalk14.cyan(String(eventCount))}`);
7049
+ console.log(`Total this session: ${chalk14.cyan(String(state.eventCount))}`);
6290
7050
  if (state.lastAnalysisTimestamp) {
6291
- console.log(`Last analysis: ${chalk13.cyan(state.lastAnalysisTimestamp)}`);
7051
+ console.log(`Last analysis: ${chalk14.cyan(state.lastAnalysisTimestamp)}`);
6292
7052
  } else {
6293
- console.log(`Last analysis: ${chalk13.dim("none")}`);
7053
+ console.log(`Last analysis: ${chalk14.dim("none")}`);
6294
7054
  }
6295
7055
  const learnedSection = readLearnedSection();
6296
7056
  if (learnedSection) {
6297
7057
  const lineCount = learnedSection.split("\n").filter(Boolean).length;
6298
7058
  console.log(`
6299
- Learned items in CLAUDE.md: ${chalk13.cyan(String(lineCount))}`);
7059
+ Learned items in CLAUDE.md: ${chalk14.cyan(String(lineCount))}`);
6300
7060
  }
6301
7061
  }
6302
7062
 
6303
7063
  // src/cli.ts
6304
- var __dirname = path22.dirname(fileURLToPath(import.meta.url));
7064
+ var __dirname = path24.dirname(fileURLToPath(import.meta.url));
6305
7065
  var pkg = JSON.parse(
6306
- fs25.readFileSync(path22.resolve(__dirname, "..", "package.json"), "utf-8")
7066
+ fs27.readFileSync(path24.resolve(__dirname, "..", "package.json"), "utf-8")
6307
7067
  );
6308
7068
  var program = new Command();
6309
7069
  var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
@@ -6325,22 +7085,22 @@ learn.command("remove").description("Remove learning hooks from .claude/settings
6325
7085
  learn.command("status").description("Show learning system status").action(learnStatusCommand);
6326
7086
 
6327
7087
  // src/utils/version-check.ts
6328
- import fs26 from "fs";
6329
- import path23 from "path";
7088
+ import fs28 from "fs";
7089
+ import path25 from "path";
6330
7090
  import { fileURLToPath as fileURLToPath2 } from "url";
6331
7091
  import { execSync as execSync9 } from "child_process";
6332
- import chalk14 from "chalk";
6333
- import ora6 from "ora";
7092
+ import chalk15 from "chalk";
7093
+ import ora7 from "ora";
6334
7094
  import confirm2 from "@inquirer/confirm";
6335
- var __dirname_vc = path23.dirname(fileURLToPath2(import.meta.url));
7095
+ var __dirname_vc = path25.dirname(fileURLToPath2(import.meta.url));
6336
7096
  var pkg2 = JSON.parse(
6337
- fs26.readFileSync(path23.resolve(__dirname_vc, "..", "package.json"), "utf-8")
7097
+ fs28.readFileSync(path25.resolve(__dirname_vc, "..", "package.json"), "utf-8")
6338
7098
  );
6339
7099
  function getInstalledVersion() {
6340
7100
  try {
6341
7101
  const globalRoot = execSync9("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
6342
- const pkgPath = path23.join(globalRoot, "@rely-ai", "caliber", "package.json");
6343
- return JSON.parse(fs26.readFileSync(pkgPath, "utf-8")).version;
7102
+ const pkgPath = path25.join(globalRoot, "@rely-ai", "caliber", "package.json");
7103
+ return JSON.parse(fs28.readFileSync(pkgPath, "utf-8")).version;
6344
7104
  } catch {
6345
7105
  return null;
6346
7106
  }
@@ -6363,17 +7123,17 @@ async function checkForUpdates() {
6363
7123
  const isInteractive = process.stdin.isTTY === true;
6364
7124
  if (!isInteractive) {
6365
7125
  console.log(
6366
- chalk14.yellow(
7126
+ chalk15.yellow(
6367
7127
  `
6368
7128
  Update available: ${current} -> ${latest}
6369
- Run ${chalk14.bold("npm install -g @rely-ai/caliber")} to upgrade.
7129
+ Run ${chalk15.bold("npm install -g @rely-ai/caliber")} to upgrade.
6370
7130
  `
6371
7131
  )
6372
7132
  );
6373
7133
  return;
6374
7134
  }
6375
7135
  console.log(
6376
- chalk14.yellow(`
7136
+ chalk15.yellow(`
6377
7137
  Update available: ${current} -> ${latest}`)
6378
7138
  );
6379
7139
  const shouldUpdate = await confirm2({ message: "Would you like to update now? (Y/n)", default: true });
@@ -6381,7 +7141,7 @@ Update available: ${current} -> ${latest}`)
6381
7141
  console.log();
6382
7142
  return;
6383
7143
  }
6384
- const spinner = ora6("Updating caliber...").start();
7144
+ const spinner = ora7("Updating caliber...").start();
6385
7145
  try {
6386
7146
  execSync9(`npm install -g @rely-ai/caliber@${latest}`, {
6387
7147
  stdio: "pipe",
@@ -6391,13 +7151,13 @@ Update available: ${current} -> ${latest}`)
6391
7151
  const installed = getInstalledVersion();
6392
7152
  if (installed !== latest) {
6393
7153
  spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
6394
- console.log(chalk14.yellow(`Run ${chalk14.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually.
7154
+ console.log(chalk15.yellow(`Run ${chalk15.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually.
6395
7155
  `));
6396
7156
  return;
6397
7157
  }
6398
- spinner.succeed(chalk14.green(`Updated to ${latest}`));
7158
+ spinner.succeed(chalk15.green(`Updated to ${latest}`));
6399
7159
  const args = process.argv.slice(2);
6400
- console.log(chalk14.dim(`
7160
+ console.log(chalk15.dim(`
6401
7161
  Restarting: caliber ${args.join(" ")}
6402
7162
  `));
6403
7163
  execSync9(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
@@ -6410,11 +7170,11 @@ Restarting: caliber ${args.join(" ")}
6410
7170
  if (err instanceof Error) {
6411
7171
  const stderr = err.stderr;
6412
7172
  const errMsg = stderr ? String(stderr).trim().split("\n").pop() : err.message.split("\n")[0];
6413
- if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk14.dim(` ${errMsg}`));
7173
+ if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk15.dim(` ${errMsg}`));
6414
7174
  }
6415
7175
  console.log(
6416
- chalk14.yellow(
6417
- `Run ${chalk14.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually to upgrade.
7176
+ chalk15.yellow(
7177
+ `Run ${chalk15.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually to upgrade.
6418
7178
  `
6419
7179
  )
6420
7180
  );