@rely-ai/caliber 1.6.0 → 1.6.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.
- package/dist/bin.js +381 -896
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -51,17 +51,17 @@ var init_constants = __esm({
|
|
|
51
51
|
|
|
52
52
|
// src/cli.ts
|
|
53
53
|
import { Command } from "commander";
|
|
54
|
-
import
|
|
55
|
-
import
|
|
54
|
+
import fs27 from "fs";
|
|
55
|
+
import path21 from "path";
|
|
56
56
|
import { fileURLToPath } from "url";
|
|
57
57
|
|
|
58
58
|
// src/commands/onboard.ts
|
|
59
|
-
import
|
|
60
|
-
import
|
|
61
|
-
import
|
|
59
|
+
import chalk6 from "chalk";
|
|
60
|
+
import ora2 from "ora";
|
|
61
|
+
import readline3 from "readline";
|
|
62
62
|
import select4 from "@inquirer/select";
|
|
63
63
|
import checkbox from "@inquirer/checkbox";
|
|
64
|
-
import
|
|
64
|
+
import fs21 from "fs";
|
|
65
65
|
|
|
66
66
|
// src/fingerprint/index.ts
|
|
67
67
|
import fs6 from "fs";
|
|
@@ -3016,15 +3016,15 @@ function computeGrade(score) {
|
|
|
3016
3016
|
// src/scoring/checks/coverage.ts
|
|
3017
3017
|
import { readFileSync, readdirSync } from "fs";
|
|
3018
3018
|
import { join } from "path";
|
|
3019
|
-
function readFileOrNull(
|
|
3019
|
+
function readFileOrNull(path23) {
|
|
3020
3020
|
try {
|
|
3021
|
-
return readFileSync(
|
|
3021
|
+
return readFileSync(path23, "utf-8");
|
|
3022
3022
|
} catch {
|
|
3023
3023
|
return null;
|
|
3024
3024
|
}
|
|
3025
3025
|
}
|
|
3026
|
-
function readJsonOrNull(
|
|
3027
|
-
const content = readFileOrNull(
|
|
3026
|
+
function readJsonOrNull(path23) {
|
|
3027
|
+
const content = readFileOrNull(path23);
|
|
3028
3028
|
if (!content) return null;
|
|
3029
3029
|
try {
|
|
3030
3030
|
return JSON.parse(content);
|
|
@@ -3392,9 +3392,9 @@ function checkExistence(dir) {
|
|
|
3392
3392
|
// src/scoring/checks/quality.ts
|
|
3393
3393
|
import { readFileSync as readFileSync3 } from "fs";
|
|
3394
3394
|
import { join as join3 } from "path";
|
|
3395
|
-
function readFileOrNull2(
|
|
3395
|
+
function readFileOrNull2(path23) {
|
|
3396
3396
|
try {
|
|
3397
|
-
return readFileSync3(
|
|
3397
|
+
return readFileSync3(path23, "utf-8");
|
|
3398
3398
|
} catch {
|
|
3399
3399
|
return null;
|
|
3400
3400
|
}
|
|
@@ -3547,15 +3547,15 @@ function checkQuality(dir) {
|
|
|
3547
3547
|
// src/scoring/checks/accuracy.ts
|
|
3548
3548
|
import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync } from "fs";
|
|
3549
3549
|
import { join as join4 } from "path";
|
|
3550
|
-
function readFileOrNull3(
|
|
3550
|
+
function readFileOrNull3(path23) {
|
|
3551
3551
|
try {
|
|
3552
|
-
return readFileSync4(
|
|
3552
|
+
return readFileSync4(path23, "utf-8");
|
|
3553
3553
|
} catch {
|
|
3554
3554
|
return null;
|
|
3555
3555
|
}
|
|
3556
3556
|
}
|
|
3557
|
-
function readJsonOrNull2(
|
|
3558
|
-
const content = readFileOrNull3(
|
|
3557
|
+
function readJsonOrNull2(path23) {
|
|
3558
|
+
const content = readFileOrNull3(path23);
|
|
3559
3559
|
if (!content) return null;
|
|
3560
3560
|
try {
|
|
3561
3561
|
return JSON.parse(content);
|
|
@@ -3738,9 +3738,9 @@ function checkAccuracy(dir) {
|
|
|
3738
3738
|
// src/scoring/checks/freshness.ts
|
|
3739
3739
|
import { existsSync as existsSync6, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
3740
3740
|
import { join as join5 } from "path";
|
|
3741
|
-
function readFileOrNull4(
|
|
3741
|
+
function readFileOrNull4(path23) {
|
|
3742
3742
|
try {
|
|
3743
|
-
return readFileSync5(
|
|
3743
|
+
return readFileSync5(path23, "utf-8");
|
|
3744
3744
|
} catch {
|
|
3745
3745
|
return null;
|
|
3746
3746
|
}
|
|
@@ -3854,9 +3854,9 @@ function checkFreshness(dir) {
|
|
|
3854
3854
|
import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "fs";
|
|
3855
3855
|
import { execSync as execSync7 } from "child_process";
|
|
3856
3856
|
import { join as join6 } from "path";
|
|
3857
|
-
function readFileOrNull5(
|
|
3857
|
+
function readFileOrNull5(path23) {
|
|
3858
3858
|
try {
|
|
3859
|
-
return readFileSync6(
|
|
3859
|
+
return readFileSync6(path23, "utf-8");
|
|
3860
3860
|
} catch {
|
|
3861
3861
|
return null;
|
|
3862
3862
|
}
|
|
@@ -4153,517 +4153,21 @@ function displayScoreDelta(before, after) {
|
|
|
4153
4153
|
}
|
|
4154
4154
|
}
|
|
4155
4155
|
|
|
4156
|
-
// src/
|
|
4156
|
+
// src/commands/recommend.ts
|
|
4157
4157
|
import chalk5 from "chalk";
|
|
4158
4158
|
import ora from "ora";
|
|
4159
|
-
import readline3 from "readline";
|
|
4160
|
-
import fs20 from "fs";
|
|
4161
|
-
import path16 from "path";
|
|
4162
|
-
|
|
4163
|
-
// src/mcp/search.ts
|
|
4164
|
-
var AWESOME_MCP_URL = "https://raw.githubusercontent.com/punkpeye/awesome-mcp-servers/main/README.md";
|
|
4165
|
-
var GITHUB_SEARCH_URL = "https://github.com/search";
|
|
4166
|
-
var SEARCH_HEADERS = {
|
|
4167
|
-
"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",
|
|
4168
|
-
"Accept": "text/html"
|
|
4169
|
-
};
|
|
4170
|
-
function parseGitHubSearchHtml(html) {
|
|
4171
|
-
try {
|
|
4172
|
-
const scriptMatch = html.match(/<script type="application\/json" data-target="react-app\.embeddedData">([\s\S]*?)<\/script>/);
|
|
4173
|
-
if (!scriptMatch) {
|
|
4174
|
-
return [];
|
|
4175
|
-
}
|
|
4176
|
-
const data = JSON.parse(scriptMatch[1]);
|
|
4177
|
-
const results = data?.payload?.results;
|
|
4178
|
-
if (!Array.isArray(results)) {
|
|
4179
|
-
return [];
|
|
4180
|
-
}
|
|
4181
|
-
return results.map((r) => {
|
|
4182
|
-
const repo = r.repo?.repository;
|
|
4183
|
-
const ownerLogin = repo?.owner_login || "";
|
|
4184
|
-
const name = repo?.name || "";
|
|
4185
|
-
const description = typeof r.hl_trunc_description === "string" ? r.hl_trunc_description.replace(/<\/?em>/g, "") : null;
|
|
4186
|
-
return {
|
|
4187
|
-
full_name: `${ownerLogin}/${name}`,
|
|
4188
|
-
description,
|
|
4189
|
-
stargazers_count: typeof r.followers === "number" ? r.followers : 0,
|
|
4190
|
-
pushed_at: repo?.updated_at || "",
|
|
4191
|
-
owner_login: ownerLogin
|
|
4192
|
-
};
|
|
4193
|
-
});
|
|
4194
|
-
} catch {
|
|
4195
|
-
return [];
|
|
4196
|
-
}
|
|
4197
|
-
}
|
|
4198
|
-
async function searchAllMcpSources(toolDeps) {
|
|
4199
|
-
if (toolDeps.length === 0) return [];
|
|
4200
|
-
const searches = [
|
|
4201
|
-
searchAwesomeMcpLists(toolDeps),
|
|
4202
|
-
...toolDeps.map((dep) => searchGitHub(dep)),
|
|
4203
|
-
...toolDeps.map((dep) => searchVendorOrg(dep))
|
|
4204
|
-
];
|
|
4205
|
-
const results = await Promise.all(searches);
|
|
4206
|
-
const seen = /* @__PURE__ */ new Map();
|
|
4207
|
-
for (const batch of results) {
|
|
4208
|
-
for (const candidate of batch) {
|
|
4209
|
-
const key = candidate.repoFullName.toLowerCase();
|
|
4210
|
-
const existing = seen.get(key);
|
|
4211
|
-
if (!existing || candidate.vendor || candidate.stars > existing.stars) {
|
|
4212
|
-
seen.set(key, candidate);
|
|
4213
|
-
}
|
|
4214
|
-
}
|
|
4215
|
-
}
|
|
4216
|
-
return Array.from(seen.values());
|
|
4217
|
-
}
|
|
4218
|
-
async function searchAwesomeMcpLists(toolDeps) {
|
|
4219
|
-
try {
|
|
4220
|
-
const resp = await fetch(AWESOME_MCP_URL, {
|
|
4221
|
-
signal: AbortSignal.timeout(1e4)
|
|
4222
|
-
});
|
|
4223
|
-
if (!resp.ok) return [];
|
|
4224
|
-
const markdown = await resp.text();
|
|
4225
|
-
const candidates = [];
|
|
4226
|
-
const depLower = toolDeps.map((d) => d.toLowerCase().replace(/^@[^/]+\//, ""));
|
|
4227
|
-
const itemPattern = /^[-*]\s+\[([^\]]+)\]\(([^)]+)\)\s*[-–—:]\s*(.*)/gm;
|
|
4228
|
-
let match;
|
|
4229
|
-
while ((match = itemPattern.exec(markdown)) !== null) {
|
|
4230
|
-
const [, name, url, description] = match;
|
|
4231
|
-
if (!url.includes("github.com")) continue;
|
|
4232
|
-
const text = `${name} ${description}`.toLowerCase();
|
|
4233
|
-
const matchedDep = depLower.find((d) => text.includes(d));
|
|
4234
|
-
if (!matchedDep) continue;
|
|
4235
|
-
const repoMatch = url.match(/github\.com\/([^/]+\/[^/]+)/);
|
|
4236
|
-
if (!repoMatch) continue;
|
|
4237
|
-
candidates.push({
|
|
4238
|
-
name: name.trim(),
|
|
4239
|
-
repoFullName: repoMatch[1],
|
|
4240
|
-
url: url.trim(),
|
|
4241
|
-
description: description.trim().slice(0, 200),
|
|
4242
|
-
stars: 0,
|
|
4243
|
-
lastPush: "",
|
|
4244
|
-
vendor: false,
|
|
4245
|
-
score: 0,
|
|
4246
|
-
reason: "",
|
|
4247
|
-
matchedDep: toolDeps.find((d) => d.toLowerCase().replace(/^@[^/]+\//, "") === matchedDep) || matchedDep
|
|
4248
|
-
});
|
|
4249
|
-
}
|
|
4250
|
-
return candidates;
|
|
4251
|
-
} catch {
|
|
4252
|
-
return [];
|
|
4253
|
-
}
|
|
4254
|
-
}
|
|
4255
|
-
async function searchGitHub(dep) {
|
|
4256
|
-
const depName = dep.replace(/^@[^/]+\//, "");
|
|
4257
|
-
try {
|
|
4258
|
-
const query = encodeURIComponent(`${depName} mcp server`);
|
|
4259
|
-
const url = `${GITHUB_SEARCH_URL}?q=${query}&type=repositories&s=stars&o=desc`;
|
|
4260
|
-
const resp = await fetch(url, {
|
|
4261
|
-
signal: AbortSignal.timeout(1e4),
|
|
4262
|
-
headers: SEARCH_HEADERS
|
|
4263
|
-
});
|
|
4264
|
-
if (!resp.ok) return [];
|
|
4265
|
-
const html = await resp.text();
|
|
4266
|
-
const repos = parseGitHubSearchHtml(html).slice(0, 5);
|
|
4267
|
-
return repos.map((repo) => ({
|
|
4268
|
-
name: repo.full_name.split("/")[1],
|
|
4269
|
-
repoFullName: repo.full_name,
|
|
4270
|
-
url: `https://github.com/${repo.full_name}`,
|
|
4271
|
-
description: repo.description || "",
|
|
4272
|
-
stars: repo.stargazers_count,
|
|
4273
|
-
lastPush: repo.pushed_at,
|
|
4274
|
-
vendor: false,
|
|
4275
|
-
score: 0,
|
|
4276
|
-
reason: "",
|
|
4277
|
-
matchedDep: dep
|
|
4278
|
-
}));
|
|
4279
|
-
} catch {
|
|
4280
|
-
return [];
|
|
4281
|
-
}
|
|
4282
|
-
}
|
|
4283
|
-
async function searchVendorOrg(dep) {
|
|
4284
|
-
const depName = dep.replace(/^@([^/]+)\/.*/, "$1").replace(/^@/, "");
|
|
4285
|
-
const orgName = depName.toLowerCase();
|
|
4286
|
-
try {
|
|
4287
|
-
const query = encodeURIComponent(`mcp org:${orgName}`);
|
|
4288
|
-
const url = `${GITHUB_SEARCH_URL}?q=${query}&type=repositories&s=stars&o=desc`;
|
|
4289
|
-
const resp = await fetch(url, {
|
|
4290
|
-
signal: AbortSignal.timeout(1e4),
|
|
4291
|
-
headers: SEARCH_HEADERS
|
|
4292
|
-
});
|
|
4293
|
-
if (!resp.ok) return [];
|
|
4294
|
-
const html = await resp.text();
|
|
4295
|
-
const repos = parseGitHubSearchHtml(html).slice(0, 5);
|
|
4296
|
-
return repos.map((repo) => ({
|
|
4297
|
-
name: repo.full_name.split("/")[1],
|
|
4298
|
-
repoFullName: repo.full_name,
|
|
4299
|
-
url: `https://github.com/${repo.full_name}`,
|
|
4300
|
-
description: repo.description || "",
|
|
4301
|
-
stars: repo.stargazers_count,
|
|
4302
|
-
lastPush: repo.pushed_at,
|
|
4303
|
-
vendor: repo.owner_login.toLowerCase() === orgName,
|
|
4304
|
-
score: 0,
|
|
4305
|
-
reason: "",
|
|
4306
|
-
matchedDep: dep
|
|
4307
|
-
}));
|
|
4308
|
-
} catch {
|
|
4309
|
-
return [];
|
|
4310
|
-
}
|
|
4311
|
-
}
|
|
4312
|
-
|
|
4313
|
-
// src/mcp/prompts.ts
|
|
4314
|
-
var SCORE_MCP_PROMPT = `You evaluate MCP (Model Context Protocol) server candidates for relevance to a software project.
|
|
4315
|
-
|
|
4316
|
-
Score each candidate from 0-100 based on:
|
|
4317
|
-
- **Relevance** (40%): How directly does it match the project's detected tool dependencies?
|
|
4318
|
-
- **Capabilities** (30%): Does it provide meaningful read+write operations (not just read-only)?
|
|
4319
|
-
- **Quality signals** (30%): Stars, recent activity, vendor/official status
|
|
4320
|
-
|
|
4321
|
-
Return a JSON array where each element has:
|
|
4322
|
-
- "index": the candidate's index number
|
|
4323
|
-
- "score": relevance score 0-100
|
|
4324
|
-
- "reason": one-liner explaining the score (max 80 chars)
|
|
4325
|
-
|
|
4326
|
-
Be selective. Only score candidates that would genuinely help developers working on this project.
|
|
4327
|
-
Return ONLY the JSON array.`;
|
|
4328
|
-
var EXTRACT_CONFIG_PROMPT = `You extract MCP server configuration from a README file.
|
|
4329
|
-
|
|
4330
|
-
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.
|
|
4331
|
-
|
|
4332
|
-
Return a JSON object with:
|
|
4333
|
-
- "command": the executable command (e.g., "npx", "uvx", "node", "docker")
|
|
4334
|
-
- "args": array of arguments (e.g., ["-y", "@supabase/mcp-server"])
|
|
4335
|
-
- "env": array of objects, each with:
|
|
4336
|
-
- "key": environment variable name (e.g., "SUPABASE_ACCESS_TOKEN")
|
|
4337
|
-
- "description": brief description of what this value is (e.g., "Personal access token from dashboard")
|
|
4338
|
-
- "required": boolean
|
|
4339
|
-
|
|
4340
|
-
If the README shows multiple configuration methods, prefer npx > uvx > node > docker.
|
|
4341
|
-
If you cannot determine the configuration, return {"command": "", "args": [], "env": []}.
|
|
4342
|
-
Return ONLY the JSON object.`;
|
|
4343
|
-
|
|
4344
|
-
// src/mcp/validate.ts
|
|
4345
|
-
var MIN_STARS = 100;
|
|
4346
|
-
var MAX_AGE_DAYS = 180;
|
|
4347
|
-
async function validateAndScore(candidates, toolDeps) {
|
|
4348
|
-
const qualityFiltered = candidates.filter((c) => {
|
|
4349
|
-
if (c.vendor) return true;
|
|
4350
|
-
if (c.stars > 0 && c.stars < MIN_STARS) return false;
|
|
4351
|
-
if (c.lastPush) {
|
|
4352
|
-
const pushDate = new Date(c.lastPush);
|
|
4353
|
-
const daysAgo = (Date.now() - pushDate.getTime()) / (1e3 * 60 * 60 * 24);
|
|
4354
|
-
if (daysAgo > MAX_AGE_DAYS) return false;
|
|
4355
|
-
}
|
|
4356
|
-
return true;
|
|
4357
|
-
});
|
|
4358
|
-
if (qualityFiltered.length === 0) return [];
|
|
4359
|
-
try {
|
|
4360
|
-
return await scoreWithLLM(qualityFiltered, toolDeps);
|
|
4361
|
-
} catch {
|
|
4362
|
-
return qualityFiltered.slice(0, 5).map((c) => ({
|
|
4363
|
-
...c,
|
|
4364
|
-
score: 50,
|
|
4365
|
-
reason: c.description.slice(0, 80)
|
|
4366
|
-
}));
|
|
4367
|
-
}
|
|
4368
|
-
}
|
|
4369
|
-
async function scoreWithLLM(candidates, toolDeps) {
|
|
4370
|
-
const candidateList = candidates.map((c, i) => {
|
|
4371
|
-
const vendorTag = c.vendor ? " [VENDOR/OFFICIAL]" : "";
|
|
4372
|
-
return `${i}. "${c.name}"${vendorTag} (${c.stars} stars) \u2014 ${c.description.slice(0, 100)}`;
|
|
4373
|
-
}).join("\n");
|
|
4374
|
-
const fastModel = getFastModel();
|
|
4375
|
-
const scored = await llmJsonCall({
|
|
4376
|
-
system: SCORE_MCP_PROMPT,
|
|
4377
|
-
prompt: `TOOL DEPENDENCIES IN PROJECT:
|
|
4378
|
-
${toolDeps.join(", ")}
|
|
4379
|
-
|
|
4380
|
-
MCP SERVER CANDIDATES:
|
|
4381
|
-
${candidateList}`,
|
|
4382
|
-
maxTokens: 4e3,
|
|
4383
|
-
...fastModel ? { model: fastModel } : {}
|
|
4384
|
-
});
|
|
4385
|
-
if (!Array.isArray(scored)) return [];
|
|
4386
|
-
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) => ({
|
|
4387
|
-
...candidates[s.index],
|
|
4388
|
-
score: s.score,
|
|
4389
|
-
reason: s.reason || candidates[s.index].description.slice(0, 80)
|
|
4390
|
-
}));
|
|
4391
|
-
}
|
|
4392
|
-
|
|
4393
|
-
// src/mcp/config-extract.ts
|
|
4394
|
-
async function fetchReadme(repoFullName) {
|
|
4395
|
-
try {
|
|
4396
|
-
const resp = await fetch(
|
|
4397
|
-
`https://raw.githubusercontent.com/${repoFullName}/HEAD/README.md`,
|
|
4398
|
-
{ signal: AbortSignal.timeout(1e4) }
|
|
4399
|
-
);
|
|
4400
|
-
if (resp.ok) return await resp.text();
|
|
4401
|
-
} catch {
|
|
4402
|
-
}
|
|
4403
|
-
try {
|
|
4404
|
-
const resp = await fetch(
|
|
4405
|
-
`https://raw.githubusercontent.com/${repoFullName}/main/README.md`,
|
|
4406
|
-
{ signal: AbortSignal.timeout(1e4) }
|
|
4407
|
-
);
|
|
4408
|
-
if (resp.ok) return await resp.text();
|
|
4409
|
-
} catch {
|
|
4410
|
-
}
|
|
4411
|
-
return null;
|
|
4412
|
-
}
|
|
4413
|
-
async function extractMcpConfig(readme, serverName) {
|
|
4414
|
-
try {
|
|
4415
|
-
const truncated = readme.length > 15e3 ? readme.slice(0, 15e3) : readme;
|
|
4416
|
-
const fastModel = getFastModel();
|
|
4417
|
-
const result = await llmJsonCall({
|
|
4418
|
-
system: EXTRACT_CONFIG_PROMPT,
|
|
4419
|
-
prompt: `MCP Server: ${serverName}
|
|
4420
|
-
|
|
4421
|
-
README:
|
|
4422
|
-
${truncated}`,
|
|
4423
|
-
maxTokens: 2e3,
|
|
4424
|
-
...fastModel ? { model: fastModel } : {}
|
|
4425
|
-
});
|
|
4426
|
-
if (!result || !result.command) return null;
|
|
4427
|
-
return {
|
|
4428
|
-
command: result.command,
|
|
4429
|
-
args: Array.isArray(result.args) ? result.args : [],
|
|
4430
|
-
env: Array.isArray(result.env) ? result.env : []
|
|
4431
|
-
};
|
|
4432
|
-
} catch {
|
|
4433
|
-
return null;
|
|
4434
|
-
}
|
|
4435
|
-
}
|
|
4436
|
-
|
|
4437
|
-
// src/mcp/index.ts
|
|
4438
|
-
async function discoverAndInstallMcps(targetAgent, fingerprint, dir) {
|
|
4439
|
-
console.log(chalk5.hex("#6366f1").bold("\n MCP Server Discovery\n"));
|
|
4440
|
-
const toolDeps = fingerprint.tools;
|
|
4441
|
-
if (toolDeps.length === 0) {
|
|
4442
|
-
console.log(chalk5.dim(" No external tools or services detected \u2014 skipping MCP discovery"));
|
|
4443
|
-
return { installed: 0, names: [] };
|
|
4444
|
-
}
|
|
4445
|
-
const spinner = ora(`Searching MCP servers for ${toolDeps.length} detected tool${toolDeps.length === 1 ? "" : "s"}...`).start();
|
|
4446
|
-
console.log(chalk5.dim(` Detected: ${toolDeps.join(", ")}`));
|
|
4447
|
-
const existingMcps = getExistingMcpNames(fingerprint, targetAgent);
|
|
4448
|
-
const filteredDeps = toolDeps.filter((d) => {
|
|
4449
|
-
const lower = d.toLowerCase();
|
|
4450
|
-
return !existingMcps.some((name) => name.includes(lower) || lower.includes(name));
|
|
4451
|
-
});
|
|
4452
|
-
if (filteredDeps.length === 0) {
|
|
4453
|
-
spinner.succeed(chalk5.dim("All detected tools already have MCP servers configured"));
|
|
4454
|
-
return { installed: 0, names: [] };
|
|
4455
|
-
}
|
|
4456
|
-
const candidates = await searchAllMcpSources(filteredDeps);
|
|
4457
|
-
if (candidates.length === 0) {
|
|
4458
|
-
spinner.succeed(chalk5.dim("No MCP servers found for detected tools"));
|
|
4459
|
-
return { installed: 0, names: [] };
|
|
4460
|
-
}
|
|
4461
|
-
spinner.succeed(`Found ${candidates.length} candidate${candidates.length === 1 ? "" : "s"} for ${filteredDeps.join(", ")}`);
|
|
4462
|
-
const scoreSpinner = ora("Scoring MCP candidates...").start();
|
|
4463
|
-
const scored = await validateAndScore(candidates, filteredDeps);
|
|
4464
|
-
if (scored.length === 0) {
|
|
4465
|
-
scoreSpinner.succeed(chalk5.dim("No quality MCP servers passed validation"));
|
|
4466
|
-
console.log(chalk5.dim(` Candidates checked: ${candidates.map((c) => c.name).join(", ")}`));
|
|
4467
|
-
return { installed: 0, names: [] };
|
|
4468
|
-
}
|
|
4469
|
-
scoreSpinner.succeed(`${scored.length} quality MCP server${scored.length === 1 ? "" : "s"} found`);
|
|
4470
|
-
console.log(chalk5.dim(` Scored: ${scored.map((c) => `${c.name} (${c.score})`).join(", ")}`));
|
|
4471
|
-
const selected = await interactiveSelect(scored);
|
|
4472
|
-
if (!selected || selected.length === 0) {
|
|
4473
|
-
return { installed: 0, names: [] };
|
|
4474
|
-
}
|
|
4475
|
-
const mcpServers = {};
|
|
4476
|
-
const installedNames = [];
|
|
4477
|
-
for (const mcp of selected) {
|
|
4478
|
-
console.log(chalk5.bold(`
|
|
4479
|
-
Configuring ${mcp.name}...`));
|
|
4480
|
-
const readme = await fetchReadme(mcp.repoFullName);
|
|
4481
|
-
if (!readme) {
|
|
4482
|
-
console.log(chalk5.yellow(` Could not fetch README for ${mcp.repoFullName} \u2014 skipping`));
|
|
4483
|
-
console.log(chalk5.dim(` Manual setup: ${mcp.url}`));
|
|
4484
|
-
continue;
|
|
4485
|
-
}
|
|
4486
|
-
const config = await extractMcpConfig(readme, mcp.name);
|
|
4487
|
-
if (!config || !config.command) {
|
|
4488
|
-
console.log(chalk5.yellow(` Could not extract config for ${mcp.name} \u2014 skipping`));
|
|
4489
|
-
console.log(chalk5.dim(` Manual setup: ${mcp.url}`));
|
|
4490
|
-
continue;
|
|
4491
|
-
}
|
|
4492
|
-
const env = {};
|
|
4493
|
-
for (const envVar of config.env) {
|
|
4494
|
-
if (!envVar.required) continue;
|
|
4495
|
-
const value = await promptInput2(` ? ${envVar.key} (${envVar.description})`);
|
|
4496
|
-
if (value) {
|
|
4497
|
-
env[envVar.key] = value;
|
|
4498
|
-
}
|
|
4499
|
-
}
|
|
4500
|
-
const serverConfig = {
|
|
4501
|
-
command: config.command
|
|
4502
|
-
};
|
|
4503
|
-
if (config.args.length > 0) serverConfig.args = config.args;
|
|
4504
|
-
if (Object.keys(env).length > 0) serverConfig.env = env;
|
|
4505
|
-
mcpServers[mcp.name] = serverConfig;
|
|
4506
|
-
installedNames.push(mcp.name);
|
|
4507
|
-
console.log(` ${chalk5.green("\u2713")} ${mcp.name} configured`);
|
|
4508
|
-
}
|
|
4509
|
-
if (installedNames.length === 0) {
|
|
4510
|
-
return { installed: 0, names: [] };
|
|
4511
|
-
}
|
|
4512
|
-
if (targetAgent.includes("claude") || targetAgent.includes("codex")) {
|
|
4513
|
-
writeMcpJson(path16.join(dir, ".mcp.json"), mcpServers);
|
|
4514
|
-
}
|
|
4515
|
-
if (targetAgent.includes("cursor")) {
|
|
4516
|
-
const cursorDir = path16.join(dir, ".cursor");
|
|
4517
|
-
if (!fs20.existsSync(cursorDir)) fs20.mkdirSync(cursorDir, { recursive: true });
|
|
4518
|
-
writeMcpJson(path16.join(cursorDir, "mcp.json"), mcpServers);
|
|
4519
|
-
}
|
|
4520
|
-
return { installed: installedNames.length, names: installedNames };
|
|
4521
|
-
}
|
|
4522
|
-
function writeMcpJson(filePath, mcpServers) {
|
|
4523
|
-
let existing = {};
|
|
4524
|
-
try {
|
|
4525
|
-
if (fs20.existsSync(filePath)) {
|
|
4526
|
-
const parsed = JSON.parse(fs20.readFileSync(filePath, "utf-8"));
|
|
4527
|
-
if (parsed.mcpServers) existing = parsed.mcpServers;
|
|
4528
|
-
}
|
|
4529
|
-
} catch {
|
|
4530
|
-
}
|
|
4531
|
-
const merged = { ...existing, ...mcpServers };
|
|
4532
|
-
fs20.writeFileSync(filePath, JSON.stringify({ mcpServers: merged }, null, 2) + "\n");
|
|
4533
|
-
}
|
|
4534
|
-
function getExistingMcpNames(fingerprint, targetAgent) {
|
|
4535
|
-
const names = [];
|
|
4536
|
-
if (targetAgent.includes("claude")) {
|
|
4537
|
-
if (fingerprint.existingConfigs.claudeMcpServers) {
|
|
4538
|
-
names.push(...Object.keys(fingerprint.existingConfigs.claudeMcpServers).map((k) => k.toLowerCase()));
|
|
4539
|
-
}
|
|
4540
|
-
}
|
|
4541
|
-
if (targetAgent.includes("cursor")) {
|
|
4542
|
-
if (fingerprint.existingConfigs.cursorMcpServers) {
|
|
4543
|
-
names.push(...Object.keys(fingerprint.existingConfigs.cursorMcpServers).map((k) => k.toLowerCase()));
|
|
4544
|
-
}
|
|
4545
|
-
}
|
|
4546
|
-
return names;
|
|
4547
|
-
}
|
|
4548
|
-
function promptInput2(question) {
|
|
4549
|
-
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
4550
|
-
return new Promise((resolve2) => {
|
|
4551
|
-
rl.question(chalk5.cyan(`${question}: `), (answer) => {
|
|
4552
|
-
rl.close();
|
|
4553
|
-
resolve2(answer.trim());
|
|
4554
|
-
});
|
|
4555
|
-
});
|
|
4556
|
-
}
|
|
4557
|
-
async function interactiveSelect(candidates) {
|
|
4558
|
-
if (!process.stdin.isTTY) {
|
|
4559
|
-
console.log(chalk5.bold("\n Available MCP servers:\n"));
|
|
4560
|
-
for (const c of candidates) {
|
|
4561
|
-
const vendorTag = c.vendor ? chalk5.blue(" (vendor)") : "";
|
|
4562
|
-
console.log(` ${String(c.score).padStart(3)} ${c.name}${vendorTag} ${chalk5.dim(c.reason)}`);
|
|
4563
|
-
}
|
|
4564
|
-
console.log("");
|
|
4565
|
-
return null;
|
|
4566
|
-
}
|
|
4567
|
-
const selected = /* @__PURE__ */ new Set();
|
|
4568
|
-
let cursor = 0;
|
|
4569
|
-
const { stdin, stdout } = process;
|
|
4570
|
-
let lineCount = 0;
|
|
4571
|
-
function render() {
|
|
4572
|
-
const lines = [];
|
|
4573
|
-
lines.push(chalk5.bold(" Select MCP servers to install:"));
|
|
4574
|
-
lines.push("");
|
|
4575
|
-
for (let i = 0; i < candidates.length; i++) {
|
|
4576
|
-
const c = candidates[i];
|
|
4577
|
-
const check = selected.has(i) ? chalk5.green("[x]") : "[ ]";
|
|
4578
|
-
const ptr = i === cursor ? chalk5.cyan(">") : " ";
|
|
4579
|
-
const scoreColor = c.score >= 90 ? chalk5.green : c.score >= 70 ? chalk5.yellow : chalk5.dim;
|
|
4580
|
-
const vendorTag = c.vendor ? chalk5.blue(" (vendor)") : "";
|
|
4581
|
-
lines.push(` ${ptr} ${check} ${scoreColor(String(c.score).padStart(3))} ${c.name}${vendorTag} ${chalk5.dim(c.reason.slice(0, 40))}`);
|
|
4582
|
-
}
|
|
4583
|
-
lines.push("");
|
|
4584
|
-
lines.push(chalk5.dim(" \u2191\u2193 navigate \u23B5 toggle a all n none \u23CE install q skip"));
|
|
4585
|
-
return lines.join("\n");
|
|
4586
|
-
}
|
|
4587
|
-
function draw(initial) {
|
|
4588
|
-
if (!initial && lineCount > 0) {
|
|
4589
|
-
stdout.write(`\x1B[${lineCount}A`);
|
|
4590
|
-
}
|
|
4591
|
-
stdout.write("\x1B[0J");
|
|
4592
|
-
const output = render();
|
|
4593
|
-
stdout.write(output + "\n");
|
|
4594
|
-
lineCount = output.split("\n").length;
|
|
4595
|
-
}
|
|
4596
|
-
return new Promise((resolve2) => {
|
|
4597
|
-
console.log("");
|
|
4598
|
-
draw(true);
|
|
4599
|
-
stdin.setRawMode(true);
|
|
4600
|
-
stdin.resume();
|
|
4601
|
-
stdin.setEncoding("utf8");
|
|
4602
|
-
function cleanup() {
|
|
4603
|
-
stdin.removeListener("data", onData);
|
|
4604
|
-
stdin.setRawMode(false);
|
|
4605
|
-
stdin.pause();
|
|
4606
|
-
}
|
|
4607
|
-
function onData(key) {
|
|
4608
|
-
switch (key) {
|
|
4609
|
-
case "\x1B[A":
|
|
4610
|
-
cursor = (cursor - 1 + candidates.length) % candidates.length;
|
|
4611
|
-
draw(false);
|
|
4612
|
-
break;
|
|
4613
|
-
case "\x1B[B":
|
|
4614
|
-
cursor = (cursor + 1) % candidates.length;
|
|
4615
|
-
draw(false);
|
|
4616
|
-
break;
|
|
4617
|
-
case " ":
|
|
4618
|
-
selected.has(cursor) ? selected.delete(cursor) : selected.add(cursor);
|
|
4619
|
-
draw(false);
|
|
4620
|
-
break;
|
|
4621
|
-
case "a":
|
|
4622
|
-
candidates.forEach((_, i) => selected.add(i));
|
|
4623
|
-
draw(false);
|
|
4624
|
-
break;
|
|
4625
|
-
case "n":
|
|
4626
|
-
selected.clear();
|
|
4627
|
-
draw(false);
|
|
4628
|
-
break;
|
|
4629
|
-
case "\r":
|
|
4630
|
-
case "\n":
|
|
4631
|
-
cleanup();
|
|
4632
|
-
if (selected.size === 0) {
|
|
4633
|
-
console.log(chalk5.dim("\n No MCP servers selected.\n"));
|
|
4634
|
-
resolve2(null);
|
|
4635
|
-
} else {
|
|
4636
|
-
resolve2(Array.from(selected).sort().map((i) => candidates[i]));
|
|
4637
|
-
}
|
|
4638
|
-
break;
|
|
4639
|
-
case "q":
|
|
4640
|
-
case "\x1B":
|
|
4641
|
-
case "":
|
|
4642
|
-
cleanup();
|
|
4643
|
-
console.log(chalk5.dim("\n Skipped MCP server installation.\n"));
|
|
4644
|
-
resolve2(null);
|
|
4645
|
-
break;
|
|
4646
|
-
}
|
|
4647
|
-
}
|
|
4648
|
-
stdin.on("data", onData);
|
|
4649
|
-
});
|
|
4650
|
-
}
|
|
4651
|
-
|
|
4652
|
-
// src/commands/recommend.ts
|
|
4653
|
-
import chalk6 from "chalk";
|
|
4654
|
-
import ora2 from "ora";
|
|
4655
4159
|
import select3 from "@inquirer/select";
|
|
4656
4160
|
import { mkdirSync, readFileSync as readFileSync7, readdirSync as readdirSync5, existsSync as existsSync9, writeFileSync } from "fs";
|
|
4657
4161
|
import { join as join8, dirname as dirname2 } from "path";
|
|
4658
4162
|
|
|
4659
4163
|
// src/scanner/index.ts
|
|
4660
|
-
import
|
|
4661
|
-
import
|
|
4164
|
+
import fs20 from "fs";
|
|
4165
|
+
import path16 from "path";
|
|
4662
4166
|
import crypto2 from "crypto";
|
|
4663
4167
|
function scanLocalState(dir) {
|
|
4664
4168
|
const items = [];
|
|
4665
|
-
const claudeMdPath =
|
|
4666
|
-
if (
|
|
4169
|
+
const claudeMdPath = path16.join(dir, "CLAUDE.md");
|
|
4170
|
+
if (fs20.existsSync(claudeMdPath)) {
|
|
4667
4171
|
items.push({
|
|
4668
4172
|
type: "rule",
|
|
4669
4173
|
platform: "claude",
|
|
@@ -4672,10 +4176,10 @@ function scanLocalState(dir) {
|
|
|
4672
4176
|
path: claudeMdPath
|
|
4673
4177
|
});
|
|
4674
4178
|
}
|
|
4675
|
-
const skillsDir =
|
|
4676
|
-
if (
|
|
4677
|
-
for (const file of
|
|
4678
|
-
const filePath =
|
|
4179
|
+
const skillsDir = path16.join(dir, ".claude", "skills");
|
|
4180
|
+
if (fs20.existsSync(skillsDir)) {
|
|
4181
|
+
for (const file of fs20.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
4182
|
+
const filePath = path16.join(skillsDir, file);
|
|
4679
4183
|
items.push({
|
|
4680
4184
|
type: "skill",
|
|
4681
4185
|
platform: "claude",
|
|
@@ -4685,10 +4189,10 @@ function scanLocalState(dir) {
|
|
|
4685
4189
|
});
|
|
4686
4190
|
}
|
|
4687
4191
|
}
|
|
4688
|
-
const mcpJsonPath =
|
|
4689
|
-
if (
|
|
4192
|
+
const mcpJsonPath = path16.join(dir, ".mcp.json");
|
|
4193
|
+
if (fs20.existsSync(mcpJsonPath)) {
|
|
4690
4194
|
try {
|
|
4691
|
-
const mcpJson = JSON.parse(
|
|
4195
|
+
const mcpJson = JSON.parse(fs20.readFileSync(mcpJsonPath, "utf-8"));
|
|
4692
4196
|
if (mcpJson.mcpServers) {
|
|
4693
4197
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
4694
4198
|
items.push({
|
|
@@ -4703,8 +4207,8 @@ function scanLocalState(dir) {
|
|
|
4703
4207
|
} catch {
|
|
4704
4208
|
}
|
|
4705
4209
|
}
|
|
4706
|
-
const agentsMdPath =
|
|
4707
|
-
if (
|
|
4210
|
+
const agentsMdPath = path16.join(dir, "AGENTS.md");
|
|
4211
|
+
if (fs20.existsSync(agentsMdPath)) {
|
|
4708
4212
|
items.push({
|
|
4709
4213
|
type: "rule",
|
|
4710
4214
|
platform: "codex",
|
|
@@ -4713,12 +4217,12 @@ function scanLocalState(dir) {
|
|
|
4713
4217
|
path: agentsMdPath
|
|
4714
4218
|
});
|
|
4715
4219
|
}
|
|
4716
|
-
const codexSkillsDir =
|
|
4717
|
-
if (
|
|
4220
|
+
const codexSkillsDir = path16.join(dir, ".agents", "skills");
|
|
4221
|
+
if (fs20.existsSync(codexSkillsDir)) {
|
|
4718
4222
|
try {
|
|
4719
|
-
for (const name of
|
|
4720
|
-
const skillFile =
|
|
4721
|
-
if (
|
|
4223
|
+
for (const name of fs20.readdirSync(codexSkillsDir)) {
|
|
4224
|
+
const skillFile = path16.join(codexSkillsDir, name, "SKILL.md");
|
|
4225
|
+
if (fs20.existsSync(skillFile)) {
|
|
4722
4226
|
items.push({
|
|
4723
4227
|
type: "skill",
|
|
4724
4228
|
platform: "codex",
|
|
@@ -4731,8 +4235,8 @@ function scanLocalState(dir) {
|
|
|
4731
4235
|
} catch {
|
|
4732
4236
|
}
|
|
4733
4237
|
}
|
|
4734
|
-
const cursorrulesPath =
|
|
4735
|
-
if (
|
|
4238
|
+
const cursorrulesPath = path16.join(dir, ".cursorrules");
|
|
4239
|
+
if (fs20.existsSync(cursorrulesPath)) {
|
|
4736
4240
|
items.push({
|
|
4737
4241
|
type: "rule",
|
|
4738
4242
|
platform: "cursor",
|
|
@@ -4741,10 +4245,10 @@ function scanLocalState(dir) {
|
|
|
4741
4245
|
path: cursorrulesPath
|
|
4742
4246
|
});
|
|
4743
4247
|
}
|
|
4744
|
-
const cursorRulesDir =
|
|
4745
|
-
if (
|
|
4746
|
-
for (const file of
|
|
4747
|
-
const filePath =
|
|
4248
|
+
const cursorRulesDir = path16.join(dir, ".cursor", "rules");
|
|
4249
|
+
if (fs20.existsSync(cursorRulesDir)) {
|
|
4250
|
+
for (const file of fs20.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
4251
|
+
const filePath = path16.join(cursorRulesDir, file);
|
|
4748
4252
|
items.push({
|
|
4749
4253
|
type: "rule",
|
|
4750
4254
|
platform: "cursor",
|
|
@@ -4754,12 +4258,12 @@ function scanLocalState(dir) {
|
|
|
4754
4258
|
});
|
|
4755
4259
|
}
|
|
4756
4260
|
}
|
|
4757
|
-
const cursorSkillsDir =
|
|
4758
|
-
if (
|
|
4261
|
+
const cursorSkillsDir = path16.join(dir, ".cursor", "skills");
|
|
4262
|
+
if (fs20.existsSync(cursorSkillsDir)) {
|
|
4759
4263
|
try {
|
|
4760
|
-
for (const name of
|
|
4761
|
-
const skillFile =
|
|
4762
|
-
if (
|
|
4264
|
+
for (const name of fs20.readdirSync(cursorSkillsDir)) {
|
|
4265
|
+
const skillFile = path16.join(cursorSkillsDir, name, "SKILL.md");
|
|
4266
|
+
if (fs20.existsSync(skillFile)) {
|
|
4763
4267
|
items.push({
|
|
4764
4268
|
type: "skill",
|
|
4765
4269
|
platform: "cursor",
|
|
@@ -4772,10 +4276,10 @@ function scanLocalState(dir) {
|
|
|
4772
4276
|
} catch {
|
|
4773
4277
|
}
|
|
4774
4278
|
}
|
|
4775
|
-
const cursorMcpPath =
|
|
4776
|
-
if (
|
|
4279
|
+
const cursorMcpPath = path16.join(dir, ".cursor", "mcp.json");
|
|
4280
|
+
if (fs20.existsSync(cursorMcpPath)) {
|
|
4777
4281
|
try {
|
|
4778
|
-
const mcpJson = JSON.parse(
|
|
4282
|
+
const mcpJson = JSON.parse(fs20.readFileSync(cursorMcpPath, "utf-8"));
|
|
4779
4283
|
if (mcpJson.mcpServers) {
|
|
4780
4284
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
4781
4285
|
items.push({
|
|
@@ -4793,7 +4297,7 @@ function scanLocalState(dir) {
|
|
|
4793
4297
|
return items;
|
|
4794
4298
|
}
|
|
4795
4299
|
function hashFile(filePath) {
|
|
4796
|
-
const text =
|
|
4300
|
+
const text = fs20.readFileSync(filePath, "utf-8");
|
|
4797
4301
|
return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
4798
4302
|
}
|
|
4799
4303
|
function hashJson(obj) {
|
|
@@ -4955,7 +4459,7 @@ async function searchAllProviders(technologies, platform) {
|
|
|
4955
4459
|
}
|
|
4956
4460
|
return combined;
|
|
4957
4461
|
}
|
|
4958
|
-
async function
|
|
4462
|
+
async function scoreWithLLM(candidates, projectContext, technologies) {
|
|
4959
4463
|
const candidateList = candidates.map((c, i) => `${i}. "${c.name}" \u2014 ${c.reason || "no description"}`).join("\n");
|
|
4960
4464
|
const fastModel = getFastModel();
|
|
4961
4465
|
const scored = await llmJsonCall({
|
|
@@ -5108,7 +4612,7 @@ async function recommendCommand() {
|
|
|
5108
4612
|
]
|
|
5109
4613
|
});
|
|
5110
4614
|
if (!proceed) {
|
|
5111
|
-
console.log(
|
|
4615
|
+
console.log(chalk5.dim(" Cancelled.\n"));
|
|
5112
4616
|
return;
|
|
5113
4617
|
}
|
|
5114
4618
|
await searchAndInstallSkills();
|
|
@@ -5123,11 +4627,11 @@ async function searchAndInstallSkills() {
|
|
|
5123
4627
|
...extractTopDeps()
|
|
5124
4628
|
].filter(Boolean))];
|
|
5125
4629
|
if (technologies.length === 0) {
|
|
5126
|
-
console.log(
|
|
4630
|
+
console.log(chalk5.yellow("Could not detect any languages or dependencies. Try running from a project root."));
|
|
5127
4631
|
throw new Error("__exit__");
|
|
5128
4632
|
}
|
|
5129
4633
|
const primaryPlatform = platforms.includes("claude") ? "claude" : platforms[0];
|
|
5130
|
-
const searchSpinner =
|
|
4634
|
+
const searchSpinner = ora("Searching skill registries...").start();
|
|
5131
4635
|
const allCandidates = await searchAllProviders(technologies, primaryPlatform);
|
|
5132
4636
|
if (!allCandidates.length) {
|
|
5133
4637
|
searchSpinner.succeed("No skills found matching your tech stack.");
|
|
@@ -5140,15 +4644,15 @@ async function searchAndInstallSkills() {
|
|
|
5140
4644
|
return;
|
|
5141
4645
|
}
|
|
5142
4646
|
searchSpinner.succeed(
|
|
5143
|
-
`Found ${allCandidates.length} skills` + (filteredCount > 0 ?
|
|
4647
|
+
`Found ${allCandidates.length} skills` + (filteredCount > 0 ? chalk5.dim(` (${filteredCount} already installed)`) : "")
|
|
5144
4648
|
);
|
|
5145
4649
|
let results;
|
|
5146
4650
|
const config = loadConfig();
|
|
5147
4651
|
if (config) {
|
|
5148
|
-
const scoreSpinner =
|
|
4652
|
+
const scoreSpinner = ora("Scoring relevance for your project...").start();
|
|
5149
4653
|
try {
|
|
5150
4654
|
const projectContext = buildProjectContext(fingerprint);
|
|
5151
|
-
results = await
|
|
4655
|
+
results = await scoreWithLLM(newCandidates, projectContext, technologies);
|
|
5152
4656
|
if (results.length === 0) {
|
|
5153
4657
|
scoreSpinner.succeed("No highly relevant skills found for your specific project.");
|
|
5154
4658
|
return;
|
|
@@ -5161,7 +4665,7 @@ async function searchAndInstallSkills() {
|
|
|
5161
4665
|
} else {
|
|
5162
4666
|
results = newCandidates.slice(0, 20);
|
|
5163
4667
|
}
|
|
5164
|
-
const fetchSpinner =
|
|
4668
|
+
const fetchSpinner = ora("Verifying skill availability...").start();
|
|
5165
4669
|
const contentMap = /* @__PURE__ */ new Map();
|
|
5166
4670
|
await Promise.all(results.map(async (rec) => {
|
|
5167
4671
|
const content = await fetchSkillContent(rec);
|
|
@@ -5174,14 +4678,14 @@ async function searchAndInstallSkills() {
|
|
|
5174
4678
|
}
|
|
5175
4679
|
const unavailableCount = results.length - available.length;
|
|
5176
4680
|
fetchSpinner.succeed(
|
|
5177
|
-
`${available.length} installable skill${available.length > 1 ? "s" : ""}` + (unavailableCount > 0 ?
|
|
4681
|
+
`${available.length} installable skill${available.length > 1 ? "s" : ""}` + (unavailableCount > 0 ? chalk5.dim(` (${unavailableCount} unavailable)`) : "")
|
|
5178
4682
|
);
|
|
5179
|
-
const selected = await
|
|
4683
|
+
const selected = await interactiveSelect(available);
|
|
5180
4684
|
if (selected?.length) {
|
|
5181
4685
|
await installSkills(selected, platforms, contentMap);
|
|
5182
4686
|
}
|
|
5183
4687
|
}
|
|
5184
|
-
async function
|
|
4688
|
+
async function interactiveSelect(recs) {
|
|
5185
4689
|
if (!process.stdin.isTTY) {
|
|
5186
4690
|
printSkills(recs);
|
|
5187
4691
|
return null;
|
|
@@ -5197,30 +4701,30 @@ async function interactiveSelect2(recs) {
|
|
|
5197
4701
|
const nameWidth = Math.max(...recs.map((r) => r.name.length), 4) + 2;
|
|
5198
4702
|
const prefixWidth = 8;
|
|
5199
4703
|
const scoreWidth = 6;
|
|
5200
|
-
lines.push(
|
|
4704
|
+
lines.push(chalk5.bold(" Skills"));
|
|
5201
4705
|
lines.push("");
|
|
5202
4706
|
if (hasScores) {
|
|
5203
|
-
const header = " ".repeat(prefixWidth) +
|
|
4707
|
+
const header = " ".repeat(prefixWidth) + chalk5.dim("Score".padEnd(scoreWidth)) + chalk5.dim("Name".padEnd(nameWidth)) + chalk5.dim("Why");
|
|
5204
4708
|
lines.push(header);
|
|
5205
4709
|
} else {
|
|
5206
|
-
const header = " ".repeat(prefixWidth) +
|
|
4710
|
+
const header = " ".repeat(prefixWidth) + chalk5.dim("Name".padEnd(nameWidth)) + chalk5.dim("Technology".padEnd(18)) + chalk5.dim("Source");
|
|
5207
4711
|
lines.push(header);
|
|
5208
4712
|
}
|
|
5209
|
-
lines.push(
|
|
4713
|
+
lines.push(chalk5.dim(" " + "\u2500".repeat(Math.min(cols - 4, 90))));
|
|
5210
4714
|
for (let i = 0; i < recs.length; i++) {
|
|
5211
4715
|
const rec = recs[i];
|
|
5212
|
-
const check = selected.has(i) ?
|
|
5213
|
-
const ptr = i === cursor ?
|
|
4716
|
+
const check = selected.has(i) ? chalk5.green("[x]") : "[ ]";
|
|
4717
|
+
const ptr = i === cursor ? chalk5.cyan(">") : " ";
|
|
5214
4718
|
if (hasScores) {
|
|
5215
|
-
const scoreColor = rec.score >= 90 ?
|
|
4719
|
+
const scoreColor = rec.score >= 90 ? chalk5.green : rec.score >= 70 ? chalk5.yellow : chalk5.dim;
|
|
5216
4720
|
const reasonMax = Math.max(cols - prefixWidth - scoreWidth - nameWidth - 2, 20);
|
|
5217
|
-
lines.push(` ${ptr} ${check} ${scoreColor(String(rec.score).padStart(3))} ${rec.name.padEnd(nameWidth)}${
|
|
4721
|
+
lines.push(` ${ptr} ${check} ${scoreColor(String(rec.score).padStart(3))} ${rec.name.padEnd(nameWidth)}${chalk5.dim(rec.reason.slice(0, reasonMax))}`);
|
|
5218
4722
|
} else {
|
|
5219
|
-
lines.push(` ${ptr} ${check} ${rec.name.padEnd(nameWidth)}${rec.detected_technology.padEnd(16)} ${
|
|
4723
|
+
lines.push(` ${ptr} ${check} ${rec.name.padEnd(nameWidth)}${rec.detected_technology.padEnd(16)} ${chalk5.dim(rec.source_url || "")}`);
|
|
5220
4724
|
}
|
|
5221
4725
|
}
|
|
5222
4726
|
lines.push("");
|
|
5223
|
-
lines.push(
|
|
4727
|
+
lines.push(chalk5.dim(" \u2191\u2193 navigate \u23B5 toggle a all n none \u23CE install q cancel"));
|
|
5224
4728
|
return lines.join("\n");
|
|
5225
4729
|
}
|
|
5226
4730
|
function draw(initial) {
|
|
@@ -5269,7 +4773,7 @@ async function interactiveSelect2(recs) {
|
|
|
5269
4773
|
case "\n":
|
|
5270
4774
|
cleanup();
|
|
5271
4775
|
if (selected.size === 0) {
|
|
5272
|
-
console.log(
|
|
4776
|
+
console.log(chalk5.dim("\n No skills selected.\n"));
|
|
5273
4777
|
resolve2(null);
|
|
5274
4778
|
} else {
|
|
5275
4779
|
resolve2(Array.from(selected).sort().map((i) => recs[i]));
|
|
@@ -5279,7 +4783,7 @@ async function interactiveSelect2(recs) {
|
|
|
5279
4783
|
case "\x1B":
|
|
5280
4784
|
case "":
|
|
5281
4785
|
cleanup();
|
|
5282
|
-
console.log(
|
|
4786
|
+
console.log(chalk5.dim("\n Cancelled.\n"));
|
|
5283
4787
|
resolve2(null);
|
|
5284
4788
|
break;
|
|
5285
4789
|
}
|
|
@@ -5326,7 +4830,7 @@ async function fetchSkillContent(rec) {
|
|
|
5326
4830
|
return null;
|
|
5327
4831
|
}
|
|
5328
4832
|
async function installSkills(recs, platforms, contentMap) {
|
|
5329
|
-
const spinner =
|
|
4833
|
+
const spinner = ora(`Installing ${recs.length} skill${recs.length > 1 ? "s" : ""}...`).start();
|
|
5330
4834
|
const installed = [];
|
|
5331
4835
|
for (const rec of recs) {
|
|
5332
4836
|
const content = contentMap.get(rec.slug);
|
|
@@ -5342,7 +4846,7 @@ async function installSkills(recs, platforms, contentMap) {
|
|
|
5342
4846
|
if (installed.length > 0) {
|
|
5343
4847
|
spinner.succeed(`Installed ${installed.length} file${installed.length > 1 ? "s" : ""}`);
|
|
5344
4848
|
for (const p of installed) {
|
|
5345
|
-
console.log(
|
|
4849
|
+
console.log(chalk5.green(` \u2713 ${p}`));
|
|
5346
4850
|
}
|
|
5347
4851
|
} else {
|
|
5348
4852
|
spinner.fail("No skills were installed");
|
|
@@ -5355,19 +4859,19 @@ function printSkills(recs) {
|
|
|
5355
4859
|
const nameWidth = Math.max(...recs.map((r) => r.name.length), 4) + 2;
|
|
5356
4860
|
const scoreWidth = 6;
|
|
5357
4861
|
const prefixWidth = 2;
|
|
5358
|
-
console.log(
|
|
4862
|
+
console.log(chalk5.bold("\n Skills\n"));
|
|
5359
4863
|
if (hasScores) {
|
|
5360
|
-
console.log(" ".repeat(prefixWidth) +
|
|
4864
|
+
console.log(" ".repeat(prefixWidth) + chalk5.dim("Score".padEnd(scoreWidth)) + chalk5.dim("Name".padEnd(nameWidth)) + chalk5.dim("Why"));
|
|
5361
4865
|
} else {
|
|
5362
|
-
console.log(" ".repeat(prefixWidth) +
|
|
4866
|
+
console.log(" ".repeat(prefixWidth) + chalk5.dim("Name".padEnd(nameWidth)) + chalk5.dim("Technology".padEnd(18)) + chalk5.dim("Source"));
|
|
5363
4867
|
}
|
|
5364
|
-
console.log(
|
|
4868
|
+
console.log(chalk5.dim(" " + "\u2500".repeat(Math.min(cols - 4, 90))));
|
|
5365
4869
|
for (const rec of recs) {
|
|
5366
4870
|
if (hasScores) {
|
|
5367
4871
|
const reasonMax = Math.max(cols - prefixWidth - scoreWidth - nameWidth - 2, 20);
|
|
5368
|
-
console.log(` ${String(rec.score).padStart(3)} ${rec.name.padEnd(nameWidth)}${
|
|
4872
|
+
console.log(` ${String(rec.score).padStart(3)} ${rec.name.padEnd(nameWidth)}${chalk5.dim(rec.reason.slice(0, reasonMax))}`);
|
|
5369
4873
|
} else {
|
|
5370
|
-
console.log(` ${rec.name.padEnd(nameWidth)}${rec.detected_technology.padEnd(16)} ${
|
|
4874
|
+
console.log(` ${rec.name.padEnd(nameWidth)}${rec.detected_technology.padEnd(16)} ${chalk5.dim(rec.source_url || "")}`);
|
|
5371
4875
|
}
|
|
5372
4876
|
}
|
|
5373
4877
|
console.log("");
|
|
@@ -5375,8 +4879,8 @@ function printSkills(recs) {
|
|
|
5375
4879
|
|
|
5376
4880
|
// src/commands/onboard.ts
|
|
5377
4881
|
async function initCommand(options) {
|
|
5378
|
-
const brand =
|
|
5379
|
-
const title =
|
|
4882
|
+
const brand = chalk6.hex("#EB9D83");
|
|
4883
|
+
const title = chalk6.hex("#83D1EB");
|
|
5380
4884
|
console.log(brand.bold(`
|
|
5381
4885
|
\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
|
|
5382
4886
|
\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
|
|
@@ -5385,21 +4889,21 @@ async function initCommand(options) {
|
|
|
5385
4889
|
\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
|
|
5386
4890
|
\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
|
|
5387
4891
|
`));
|
|
5388
|
-
console.log(
|
|
4892
|
+
console.log(chalk6.dim(" Onboard your project for AI-assisted development\n"));
|
|
5389
4893
|
console.log(title.bold(" Welcome to Caliber\n"));
|
|
5390
|
-
console.log(
|
|
5391
|
-
console.log(
|
|
4894
|
+
console.log(chalk6.dim(" Caliber analyzes your codebase and creates tailored config files"));
|
|
4895
|
+
console.log(chalk6.dim(" so your AI coding agents understand your project from day one.\n"));
|
|
5392
4896
|
console.log(title.bold(" How onboarding works:\n"));
|
|
5393
|
-
console.log(
|
|
5394
|
-
console.log(
|
|
5395
|
-
console.log(
|
|
5396
|
-
console.log(
|
|
5397
|
-
console.log(
|
|
5398
|
-
console.log(
|
|
4897
|
+
console.log(chalk6.dim(" 1. Connect Set up your LLM provider"));
|
|
4898
|
+
console.log(chalk6.dim(" 2. Discover Analyze your code, dependencies, and structure"));
|
|
4899
|
+
console.log(chalk6.dim(" 3. Generate Create config files tailored to your project"));
|
|
4900
|
+
console.log(chalk6.dim(" 4. Review Preview, refine, and apply the changes"));
|
|
4901
|
+
console.log(chalk6.dim(" 5. Enhance Discover MCP servers for your tools"));
|
|
4902
|
+
console.log(chalk6.dim(" 6. Skills Browse community skills for your stack\n"));
|
|
5399
4903
|
console.log(title.bold(" Step 1/6 \u2014 Connect your LLM\n"));
|
|
5400
4904
|
let config = loadConfig();
|
|
5401
4905
|
if (!config) {
|
|
5402
|
-
console.log(
|
|
4906
|
+
console.log(chalk6.dim(" No LLM provider set yet. Choose how to run Caliber:\n"));
|
|
5403
4907
|
try {
|
|
5404
4908
|
await runInteractiveProviderSetup({
|
|
5405
4909
|
selectMessage: "How do you want to use Caliber? (choose LLM provider)"
|
|
@@ -5410,22 +4914,22 @@ async function initCommand(options) {
|
|
|
5410
4914
|
}
|
|
5411
4915
|
config = loadConfig();
|
|
5412
4916
|
if (!config) {
|
|
5413
|
-
console.log(
|
|
4917
|
+
console.log(chalk6.red(" Setup was cancelled or failed.\n"));
|
|
5414
4918
|
throw new Error("__exit__");
|
|
5415
4919
|
}
|
|
5416
|
-
console.log(
|
|
4920
|
+
console.log(chalk6.green(" \u2713 Provider saved. Let's continue.\n"));
|
|
5417
4921
|
}
|
|
5418
4922
|
const displayModel = config.model === "default" && config.provider === "claude-cli" ? process.env.ANTHROPIC_MODEL || "default (inherited from Claude Code)" : config.model;
|
|
5419
4923
|
const fastModel = getFastModel();
|
|
5420
4924
|
const modelLine = fastModel ? ` Provider: ${config.provider} | Model: ${displayModel} | Scan: ${fastModel}` : ` Provider: ${config.provider} | Model: ${displayModel}`;
|
|
5421
|
-
console.log(
|
|
4925
|
+
console.log(chalk6.dim(modelLine + "\n"));
|
|
5422
4926
|
console.log(title.bold(" Step 2/6 \u2014 Discover your project\n"));
|
|
5423
|
-
console.log(
|
|
5424
|
-
const spinner =
|
|
4927
|
+
console.log(chalk6.dim(" Learning about your languages, dependencies, structure, and existing configs.\n"));
|
|
4928
|
+
const spinner = ora2("Analyzing project...").start();
|
|
5425
4929
|
const fingerprint = await collectFingerprint(process.cwd());
|
|
5426
4930
|
spinner.succeed("Project analyzed");
|
|
5427
|
-
console.log(
|
|
5428
|
-
console.log(
|
|
4931
|
+
console.log(chalk6.dim(` Languages: ${fingerprint.languages.join(", ") || "none detected"}`));
|
|
4932
|
+
console.log(chalk6.dim(` Files: ${fingerprint.fileTree.length} found
|
|
5429
4933
|
`));
|
|
5430
4934
|
const targetAgent = options.agent || await promptAgent();
|
|
5431
4935
|
const preScore = computeLocalScore(process.cwd(), targetAgent);
|
|
@@ -5444,28 +4948,28 @@ async function initCommand(options) {
|
|
|
5444
4948
|
const hasExistingConfig = !!(fingerprint.existingConfigs.claudeMd || fingerprint.existingConfigs.claudeSettings || fingerprint.existingConfigs.claudeSkills?.length || fingerprint.existingConfigs.cursorrules || fingerprint.existingConfigs.cursorRules?.length || fingerprint.existingConfigs.agentsMd);
|
|
5445
4949
|
const NON_LLM_CHECKS = /* @__PURE__ */ new Set(["hooks_configured", "agents_md_exists", "permissions_configured", "mcp_servers"]);
|
|
5446
4950
|
if (hasExistingConfig && baselineScore.score === 100) {
|
|
5447
|
-
console.log(
|
|
5448
|
-
console.log(
|
|
4951
|
+
console.log(chalk6.bold.green(" Your setup is already optimal \u2014 nothing to change.\n"));
|
|
4952
|
+
console.log(chalk6.dim(" Run ") + chalk6.hex("#83D1EB")("caliber onboard --force") + chalk6.dim(" to regenerate anyway.\n"));
|
|
5449
4953
|
if (!options.force) return;
|
|
5450
4954
|
}
|
|
5451
4955
|
const allFailingChecks = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
|
|
5452
4956
|
const llmFixableChecks = allFailingChecks.filter((c) => !NON_LLM_CHECKS.has(c.id));
|
|
5453
4957
|
if (hasExistingConfig && llmFixableChecks.length === 0 && allFailingChecks.length > 0 && !options.force) {
|
|
5454
|
-
console.log(
|
|
5455
|
-
console.log(
|
|
4958
|
+
console.log(chalk6.bold.green("\n Your config is fully optimized for LLM generation.\n"));
|
|
4959
|
+
console.log(chalk6.dim(" Remaining items need CLI actions:\n"));
|
|
5456
4960
|
for (const check of allFailingChecks) {
|
|
5457
|
-
console.log(
|
|
4961
|
+
console.log(chalk6.dim(` \u2022 ${check.name}`));
|
|
5458
4962
|
if (check.suggestion) {
|
|
5459
|
-
console.log(` ${
|
|
4963
|
+
console.log(` ${chalk6.hex("#83D1EB")(check.suggestion)}`);
|
|
5460
4964
|
}
|
|
5461
4965
|
}
|
|
5462
4966
|
console.log("");
|
|
5463
|
-
console.log(
|
|
4967
|
+
console.log(chalk6.dim(" Run ") + chalk6.hex("#83D1EB")("caliber onboard --force") + chalk6.dim(" to regenerate anyway.\n"));
|
|
5464
4968
|
return;
|
|
5465
4969
|
}
|
|
5466
4970
|
const isEmpty = fingerprint.fileTree.length < 3;
|
|
5467
4971
|
if (isEmpty) {
|
|
5468
|
-
fingerprint.description = await
|
|
4972
|
+
fingerprint.description = await promptInput2("What will you build in this project?");
|
|
5469
4973
|
}
|
|
5470
4974
|
let failingChecks;
|
|
5471
4975
|
let passingChecks;
|
|
@@ -5476,24 +4980,24 @@ async function initCommand(options) {
|
|
|
5476
4980
|
currentScore = baselineScore.score;
|
|
5477
4981
|
if (failingChecks.length > 0) {
|
|
5478
4982
|
console.log(title.bold(" Step 3/6 \u2014 Fine-tuning\n"));
|
|
5479
|
-
console.log(
|
|
4983
|
+
console.log(chalk6.dim(` Your setup scores ${baselineScore.score}/100 \u2014 fixing ${failingChecks.length} remaining issue${failingChecks.length === 1 ? "" : "s"}:
|
|
5480
4984
|
`));
|
|
5481
4985
|
for (const check of failingChecks) {
|
|
5482
|
-
console.log(
|
|
4986
|
+
console.log(chalk6.dim(` \u2022 ${check.name}`));
|
|
5483
4987
|
}
|
|
5484
4988
|
console.log("");
|
|
5485
4989
|
}
|
|
5486
4990
|
} else if (hasExistingConfig) {
|
|
5487
4991
|
console.log(title.bold(" Step 3/6 \u2014 Improve your setup\n"));
|
|
5488
|
-
console.log(
|
|
5489
|
-
console.log(
|
|
4992
|
+
console.log(chalk6.dim(" Reviewing your existing configs against your codebase"));
|
|
4993
|
+
console.log(chalk6.dim(" and preparing improvements.\n"));
|
|
5490
4994
|
} else {
|
|
5491
4995
|
console.log(title.bold(" Step 3/6 \u2014 Build your agent setup\n"));
|
|
5492
|
-
console.log(
|
|
4996
|
+
console.log(chalk6.dim(" Creating config files tailored to your project.\n"));
|
|
5493
4997
|
}
|
|
5494
|
-
console.log(
|
|
4998
|
+
console.log(chalk6.dim(" This can take a couple of minutes depending on your model and provider.\n"));
|
|
5495
4999
|
const genStartTime = Date.now();
|
|
5496
|
-
const genSpinner =
|
|
5000
|
+
const genSpinner = ora2("Generating setup...").start();
|
|
5497
5001
|
const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, { showElapsedTime: true });
|
|
5498
5002
|
genMessages.start();
|
|
5499
5003
|
let generatedSetup = null;
|
|
@@ -5533,8 +5037,8 @@ async function initCommand(options) {
|
|
|
5533
5037
|
if (!generatedSetup) {
|
|
5534
5038
|
genSpinner.fail("Failed to generate setup.");
|
|
5535
5039
|
if (rawOutput) {
|
|
5536
|
-
console.log(
|
|
5537
|
-
console.log(
|
|
5040
|
+
console.log(chalk6.dim("\nRaw LLM output (JSON parse failed):"));
|
|
5041
|
+
console.log(chalk6.dim(rawOutput.slice(0, 500)));
|
|
5538
5042
|
}
|
|
5539
5043
|
throw new Error("__exit__");
|
|
5540
5044
|
}
|
|
@@ -5542,7 +5046,7 @@ async function initCommand(options) {
|
|
|
5542
5046
|
const mins = Math.floor(elapsedMs / 6e4);
|
|
5543
5047
|
const secs = Math.floor(elapsedMs % 6e4 / 1e3);
|
|
5544
5048
|
const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
|
|
5545
|
-
genSpinner.succeed(`Setup generated ${
|
|
5049
|
+
genSpinner.succeed(`Setup generated ${chalk6.dim(`in ${timeStr}`)}`);
|
|
5546
5050
|
printSetupSummary(generatedSetup);
|
|
5547
5051
|
const sessionHistory = [];
|
|
5548
5052
|
sessionHistory.push({
|
|
@@ -5553,11 +5057,11 @@ async function initCommand(options) {
|
|
|
5553
5057
|
const setupFiles = collectSetupFiles(generatedSetup);
|
|
5554
5058
|
const staged = stageFiles(setupFiles, process.cwd());
|
|
5555
5059
|
const totalChanges = staged.newFiles + staged.modifiedFiles;
|
|
5556
|
-
console.log(
|
|
5060
|
+
console.log(chalk6.dim(` ${chalk6.green(`${staged.newFiles} new`)} / ${chalk6.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}
|
|
5557
5061
|
`));
|
|
5558
5062
|
let action;
|
|
5559
5063
|
if (totalChanges === 0) {
|
|
5560
|
-
console.log(
|
|
5064
|
+
console.log(chalk6.dim(" No changes needed \u2014 your configs are already up to date.\n"));
|
|
5561
5065
|
cleanupStaging();
|
|
5562
5066
|
action = "accept";
|
|
5563
5067
|
} else {
|
|
@@ -5572,12 +5076,12 @@ async function initCommand(options) {
|
|
|
5572
5076
|
generatedSetup = await refineLoop(generatedSetup, targetAgent, sessionHistory);
|
|
5573
5077
|
if (!generatedSetup) {
|
|
5574
5078
|
cleanupStaging();
|
|
5575
|
-
console.log(
|
|
5079
|
+
console.log(chalk6.dim("Refinement cancelled. No files were modified."));
|
|
5576
5080
|
return;
|
|
5577
5081
|
}
|
|
5578
5082
|
const updatedFiles = collectSetupFiles(generatedSetup);
|
|
5579
5083
|
const restaged = stageFiles(updatedFiles, process.cwd());
|
|
5580
|
-
console.log(
|
|
5084
|
+
console.log(chalk6.dim(` ${chalk6.green(`${restaged.newFiles} new`)} / ${chalk6.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
|
|
5581
5085
|
`));
|
|
5582
5086
|
printSetupSummary(generatedSetup);
|
|
5583
5087
|
await openReview("terminal", restaged.stagedFiles);
|
|
@@ -5585,56 +5089,37 @@ async function initCommand(options) {
|
|
|
5585
5089
|
}
|
|
5586
5090
|
cleanupStaging();
|
|
5587
5091
|
if (action === "decline") {
|
|
5588
|
-
console.log(
|
|
5092
|
+
console.log(chalk6.dim("Setup declined. No files were modified."));
|
|
5589
5093
|
return;
|
|
5590
5094
|
}
|
|
5591
5095
|
if (options.dryRun) {
|
|
5592
|
-
console.log(
|
|
5096
|
+
console.log(chalk6.yellow("\n[Dry run] Would write the following files:"));
|
|
5593
5097
|
console.log(JSON.stringify(generatedSetup, null, 2));
|
|
5594
5098
|
return;
|
|
5595
5099
|
}
|
|
5596
|
-
const writeSpinner =
|
|
5100
|
+
const writeSpinner = ora2("Writing config files...").start();
|
|
5597
5101
|
try {
|
|
5598
5102
|
const result = writeSetup(generatedSetup);
|
|
5599
5103
|
writeSpinner.succeed("Config files written");
|
|
5600
|
-
console.log(
|
|
5104
|
+
console.log(chalk6.bold("\nFiles created/updated:"));
|
|
5601
5105
|
for (const file of result.written) {
|
|
5602
|
-
console.log(` ${
|
|
5106
|
+
console.log(` ${chalk6.green("\u2713")} ${file}`);
|
|
5603
5107
|
}
|
|
5604
5108
|
if (result.deleted.length > 0) {
|
|
5605
|
-
console.log(
|
|
5109
|
+
console.log(chalk6.bold("\nFiles removed:"));
|
|
5606
5110
|
for (const file of result.deleted) {
|
|
5607
|
-
console.log(` ${
|
|
5111
|
+
console.log(` ${chalk6.red("\u2717")} ${file}`);
|
|
5608
5112
|
}
|
|
5609
5113
|
}
|
|
5610
5114
|
if (result.backupDir) {
|
|
5611
|
-
console.log(
|
|
5115
|
+
console.log(chalk6.dim(`
|
|
5612
5116
|
Backups saved to ${result.backupDir}`));
|
|
5613
5117
|
}
|
|
5614
5118
|
} catch (err) {
|
|
5615
5119
|
writeSpinner.fail("Failed to write files");
|
|
5616
|
-
console.error(
|
|
5120
|
+
console.error(chalk6.red(err instanceof Error ? err.message : "Unknown error"));
|
|
5617
5121
|
throw new Error("__exit__");
|
|
5618
5122
|
}
|
|
5619
|
-
console.log(title.bold("\n Step 5/6 \u2014 Enhance with MCP servers\n"));
|
|
5620
|
-
console.log(chalk7.dim(" MCP servers connect your AI agents to external tools and services"));
|
|
5621
|
-
console.log(chalk7.dim(" like databases, APIs, and platforms your project depends on.\n"));
|
|
5622
|
-
if (fingerprint.tools.length > 0) {
|
|
5623
|
-
try {
|
|
5624
|
-
const mcpResult = await discoverAndInstallMcps(targetAgent, fingerprint, process.cwd());
|
|
5625
|
-
if (mcpResult.installed > 0) {
|
|
5626
|
-
console.log(chalk7.bold(`
|
|
5627
|
-
${mcpResult.installed} MCP server${mcpResult.installed > 1 ? "s" : ""} configured`));
|
|
5628
|
-
for (const name of mcpResult.names) {
|
|
5629
|
-
console.log(` ${chalk7.green("\u2713")} ${name}`);
|
|
5630
|
-
}
|
|
5631
|
-
}
|
|
5632
|
-
} catch (err) {
|
|
5633
|
-
console.log(chalk7.dim(" MCP discovery skipped: " + (err instanceof Error ? err.message : "unknown error")));
|
|
5634
|
-
}
|
|
5635
|
-
} else {
|
|
5636
|
-
console.log(chalk7.dim(" No external tools or services detected \u2014 skipping MCP discovery.\n"));
|
|
5637
|
-
}
|
|
5638
5123
|
ensurePermissions();
|
|
5639
5124
|
const sha = getCurrentHeadSha();
|
|
5640
5125
|
writeState({
|
|
@@ -5644,55 +5129,55 @@ async function initCommand(options) {
|
|
|
5644
5129
|
});
|
|
5645
5130
|
console.log("");
|
|
5646
5131
|
console.log(title.bold(" Keep your configs fresh\n"));
|
|
5647
|
-
console.log(
|
|
5132
|
+
console.log(chalk6.dim(" Caliber can automatically update your agent configs when your code changes.\n"));
|
|
5648
5133
|
const hookChoice = await promptHookType(targetAgent);
|
|
5649
5134
|
if (hookChoice === "claude" || hookChoice === "both") {
|
|
5650
5135
|
const hookResult = installHook();
|
|
5651
5136
|
if (hookResult.installed) {
|
|
5652
|
-
console.log(` ${
|
|
5653
|
-
console.log(
|
|
5137
|
+
console.log(` ${chalk6.green("\u2713")} Claude Code hook installed \u2014 docs update on session end`);
|
|
5138
|
+
console.log(chalk6.dim(" Run ") + chalk6.hex("#83D1EB")("caliber hooks --remove") + chalk6.dim(" to disable"));
|
|
5654
5139
|
} else if (hookResult.alreadyInstalled) {
|
|
5655
|
-
console.log(
|
|
5140
|
+
console.log(chalk6.dim(" Claude Code hook already installed"));
|
|
5656
5141
|
}
|
|
5657
5142
|
const learnResult = installLearningHooks();
|
|
5658
5143
|
if (learnResult.installed) {
|
|
5659
|
-
console.log(` ${
|
|
5660
|
-
console.log(
|
|
5144
|
+
console.log(` ${chalk6.green("\u2713")} Learning hooks installed \u2014 session insights captured automatically`);
|
|
5145
|
+
console.log(chalk6.dim(" Run ") + chalk6.hex("#83D1EB")("caliber learn remove") + chalk6.dim(" to disable"));
|
|
5661
5146
|
} else if (learnResult.alreadyInstalled) {
|
|
5662
|
-
console.log(
|
|
5147
|
+
console.log(chalk6.dim(" Learning hooks already installed"));
|
|
5663
5148
|
}
|
|
5664
5149
|
}
|
|
5665
5150
|
if (hookChoice === "precommit" || hookChoice === "both") {
|
|
5666
5151
|
const precommitResult = installPreCommitHook();
|
|
5667
5152
|
if (precommitResult.installed) {
|
|
5668
|
-
console.log(` ${
|
|
5669
|
-
console.log(
|
|
5153
|
+
console.log(` ${chalk6.green("\u2713")} Pre-commit hook installed \u2014 docs refresh before each commit`);
|
|
5154
|
+
console.log(chalk6.dim(" Run ") + chalk6.hex("#83D1EB")("caliber hooks --remove") + chalk6.dim(" to disable"));
|
|
5670
5155
|
} else if (precommitResult.alreadyInstalled) {
|
|
5671
|
-
console.log(
|
|
5156
|
+
console.log(chalk6.dim(" Pre-commit hook already installed"));
|
|
5672
5157
|
} else {
|
|
5673
|
-
console.log(
|
|
5158
|
+
console.log(chalk6.yellow(" Could not install pre-commit hook (not a git repository?)"));
|
|
5674
5159
|
}
|
|
5675
5160
|
}
|
|
5676
5161
|
if (hookChoice === "skip") {
|
|
5677
|
-
console.log(
|
|
5162
|
+
console.log(chalk6.dim(" Skipped auto-refresh hooks. Run ") + chalk6.hex("#83D1EB")("caliber hooks --install") + chalk6.dim(" later to enable."));
|
|
5678
5163
|
}
|
|
5679
5164
|
const afterScore = computeLocalScore(process.cwd(), targetAgent);
|
|
5680
5165
|
if (afterScore.score < baselineScore.score) {
|
|
5681
5166
|
console.log("");
|
|
5682
|
-
console.log(
|
|
5167
|
+
console.log(chalk6.yellow(` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`));
|
|
5683
5168
|
try {
|
|
5684
5169
|
const { restored, removed } = undoSetup();
|
|
5685
5170
|
if (restored.length > 0 || removed.length > 0) {
|
|
5686
|
-
console.log(
|
|
5171
|
+
console.log(chalk6.dim(` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`));
|
|
5687
5172
|
}
|
|
5688
5173
|
} catch {
|
|
5689
5174
|
}
|
|
5690
|
-
console.log(
|
|
5175
|
+
console.log(chalk6.dim(" Run ") + chalk6.hex("#83D1EB")("caliber onboard --force") + chalk6.dim(" to override.\n"));
|
|
5691
5176
|
return;
|
|
5692
5177
|
}
|
|
5693
5178
|
displayScoreDelta(baselineScore, afterScore);
|
|
5694
5179
|
console.log(title.bold("\n Step 6/6 \u2014 Community skills\n"));
|
|
5695
|
-
console.log(
|
|
5180
|
+
console.log(chalk6.dim(" Search public skill registries for skills that match your tech stack.\n"));
|
|
5696
5181
|
const wantsSkills = await select4({
|
|
5697
5182
|
message: "Search public repos for relevant skills to add to this project?",
|
|
5698
5183
|
choices: [
|
|
@@ -5705,16 +5190,16 @@ async function initCommand(options) {
|
|
|
5705
5190
|
await searchAndInstallSkills();
|
|
5706
5191
|
} catch (err) {
|
|
5707
5192
|
if (err.message !== "__exit__") {
|
|
5708
|
-
console.log(
|
|
5193
|
+
console.log(chalk6.dim(" Skills search failed: " + (err.message || "unknown error")));
|
|
5709
5194
|
}
|
|
5710
|
-
console.log(
|
|
5195
|
+
console.log(chalk6.dim(" Run ") + chalk6.hex("#83D1EB")("caliber skills") + chalk6.dim(" later to try again.\n"));
|
|
5711
5196
|
}
|
|
5712
5197
|
} else {
|
|
5713
|
-
console.log(
|
|
5198
|
+
console.log(chalk6.dim(" Skipped. Run ") + chalk6.hex("#83D1EB")("caliber skills") + chalk6.dim(" later to browse.\n"));
|
|
5714
5199
|
}
|
|
5715
|
-
console.log(
|
|
5716
|
-
console.log(
|
|
5717
|
-
console.log(
|
|
5200
|
+
console.log(chalk6.bold.green(" Onboarding complete! Your project is ready for AI-assisted development."));
|
|
5201
|
+
console.log(chalk6.dim(" Run ") + chalk6.hex("#83D1EB")("caliber undo") + chalk6.dim(" to revert changes.\n"));
|
|
5202
|
+
console.log(chalk6.bold(" Next steps:\n"));
|
|
5718
5203
|
console.log(` ${title("caliber score")} See your full config breakdown`);
|
|
5719
5204
|
console.log(` ${title("caliber skills")} Discover community skills for your stack`);
|
|
5720
5205
|
console.log(` ${title("caliber undo")} Revert all changes from this run`);
|
|
@@ -5722,7 +5207,7 @@ async function initCommand(options) {
|
|
|
5722
5207
|
}
|
|
5723
5208
|
async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
|
|
5724
5209
|
while (true) {
|
|
5725
|
-
const message = await
|
|
5210
|
+
const message = await promptInput2("\nWhat would you like to change?");
|
|
5726
5211
|
if (!message || message.toLowerCase() === "done" || message.toLowerCase() === "accept") {
|
|
5727
5212
|
return currentSetup;
|
|
5728
5213
|
}
|
|
@@ -5731,12 +5216,12 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
|
|
|
5731
5216
|
}
|
|
5732
5217
|
const isValid = await classifyRefineIntent(message);
|
|
5733
5218
|
if (!isValid) {
|
|
5734
|
-
console.log(
|
|
5735
|
-
console.log(
|
|
5736
|
-
console.log(
|
|
5219
|
+
console.log(chalk6.dim(" This doesn't look like a config change request."));
|
|
5220
|
+
console.log(chalk6.dim(" Describe what to add, remove, or modify in your configs."));
|
|
5221
|
+
console.log(chalk6.dim(' Type "done" to accept the current setup.\n'));
|
|
5737
5222
|
continue;
|
|
5738
5223
|
}
|
|
5739
|
-
const refineSpinner =
|
|
5224
|
+
const refineSpinner = ora2("Refining setup...").start();
|
|
5740
5225
|
const refineMessages = new SpinnerMessages(refineSpinner, REFINE_MESSAGES);
|
|
5741
5226
|
refineMessages.start();
|
|
5742
5227
|
const refined = await refineSetup(
|
|
@@ -5754,16 +5239,16 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
|
|
|
5754
5239
|
});
|
|
5755
5240
|
refineSpinner.succeed("Setup updated");
|
|
5756
5241
|
printSetupSummary(refined);
|
|
5757
|
-
console.log(
|
|
5242
|
+
console.log(chalk6.dim('Type "done" to accept, or describe more changes.'));
|
|
5758
5243
|
} else {
|
|
5759
5244
|
refineSpinner.fail("Refinement failed \u2014 could not parse AI response.");
|
|
5760
|
-
console.log(
|
|
5245
|
+
console.log(chalk6.dim('Try rephrasing your request, or type "done" to keep the current setup.'));
|
|
5761
5246
|
}
|
|
5762
5247
|
}
|
|
5763
5248
|
}
|
|
5764
5249
|
function summarizeSetup(action, setup) {
|
|
5765
5250
|
const descriptions = setup.fileDescriptions;
|
|
5766
|
-
const files = descriptions ? Object.entries(descriptions).map(([
|
|
5251
|
+
const files = descriptions ? Object.entries(descriptions).map(([path23, desc]) => ` ${path23}: ${desc}`).join("\n") : Object.keys(setup).filter((k) => k !== "targetAgent" && k !== "fileDescriptions").join(", ");
|
|
5767
5252
|
return `${action}. Files:
|
|
5768
5253
|
${files}`;
|
|
5769
5254
|
}
|
|
@@ -5814,10 +5299,10 @@ ${JSON.stringify(checkList, null, 2)}`,
|
|
|
5814
5299
|
return [];
|
|
5815
5300
|
}
|
|
5816
5301
|
}
|
|
5817
|
-
function
|
|
5818
|
-
const rl =
|
|
5302
|
+
function promptInput2(question) {
|
|
5303
|
+
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
5819
5304
|
return new Promise((resolve2) => {
|
|
5820
|
-
rl.question(
|
|
5305
|
+
rl.question(chalk6.cyan(`${question} `), (answer) => {
|
|
5821
5306
|
rl.close();
|
|
5822
5307
|
resolve2(answer.trim());
|
|
5823
5308
|
});
|
|
@@ -5870,26 +5355,26 @@ function printSetupSummary(setup) {
|
|
|
5870
5355
|
const fileDescriptions = setup.fileDescriptions;
|
|
5871
5356
|
const deletions = setup.deletions;
|
|
5872
5357
|
console.log("");
|
|
5873
|
-
console.log(
|
|
5358
|
+
console.log(chalk6.bold(" Proposed changes:\n"));
|
|
5874
5359
|
const getDescription = (filePath) => {
|
|
5875
5360
|
return fileDescriptions?.[filePath];
|
|
5876
5361
|
};
|
|
5877
5362
|
if (claude) {
|
|
5878
5363
|
if (claude.claudeMd) {
|
|
5879
|
-
const icon =
|
|
5364
|
+
const icon = fs21.existsSync("CLAUDE.md") ? chalk6.yellow("~") : chalk6.green("+");
|
|
5880
5365
|
const desc = getDescription("CLAUDE.md");
|
|
5881
|
-
console.log(` ${icon} ${
|
|
5882
|
-
if (desc) console.log(
|
|
5366
|
+
console.log(` ${icon} ${chalk6.bold("CLAUDE.md")}`);
|
|
5367
|
+
if (desc) console.log(chalk6.dim(` ${desc}`));
|
|
5883
5368
|
console.log("");
|
|
5884
5369
|
}
|
|
5885
5370
|
const skills = claude.skills;
|
|
5886
5371
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
5887
5372
|
for (const skill of skills) {
|
|
5888
5373
|
const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
|
|
5889
|
-
const icon =
|
|
5374
|
+
const icon = fs21.existsSync(skillPath) ? chalk6.yellow("~") : chalk6.green("+");
|
|
5890
5375
|
const desc = getDescription(skillPath);
|
|
5891
|
-
console.log(` ${icon} ${
|
|
5892
|
-
console.log(
|
|
5376
|
+
console.log(` ${icon} ${chalk6.bold(skillPath)}`);
|
|
5377
|
+
console.log(chalk6.dim(` ${desc || skill.description || skill.name}`));
|
|
5893
5378
|
console.log("");
|
|
5894
5379
|
}
|
|
5895
5380
|
}
|
|
@@ -5897,40 +5382,40 @@ function printSetupSummary(setup) {
|
|
|
5897
5382
|
const codex = setup.codex;
|
|
5898
5383
|
if (codex) {
|
|
5899
5384
|
if (codex.agentsMd) {
|
|
5900
|
-
const icon =
|
|
5385
|
+
const icon = fs21.existsSync("AGENTS.md") ? chalk6.yellow("~") : chalk6.green("+");
|
|
5901
5386
|
const desc = getDescription("AGENTS.md");
|
|
5902
|
-
console.log(` ${icon} ${
|
|
5903
|
-
if (desc) console.log(
|
|
5387
|
+
console.log(` ${icon} ${chalk6.bold("AGENTS.md")}`);
|
|
5388
|
+
if (desc) console.log(chalk6.dim(` ${desc}`));
|
|
5904
5389
|
console.log("");
|
|
5905
5390
|
}
|
|
5906
5391
|
const codexSkills = codex.skills;
|
|
5907
5392
|
if (Array.isArray(codexSkills) && codexSkills.length > 0) {
|
|
5908
5393
|
for (const skill of codexSkills) {
|
|
5909
5394
|
const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
|
|
5910
|
-
const icon =
|
|
5395
|
+
const icon = fs21.existsSync(skillPath) ? chalk6.yellow("~") : chalk6.green("+");
|
|
5911
5396
|
const desc = getDescription(skillPath);
|
|
5912
|
-
console.log(` ${icon} ${
|
|
5913
|
-
console.log(
|
|
5397
|
+
console.log(` ${icon} ${chalk6.bold(skillPath)}`);
|
|
5398
|
+
console.log(chalk6.dim(` ${desc || skill.description || skill.name}`));
|
|
5914
5399
|
console.log("");
|
|
5915
5400
|
}
|
|
5916
5401
|
}
|
|
5917
5402
|
}
|
|
5918
5403
|
if (cursor) {
|
|
5919
5404
|
if (cursor.cursorrules) {
|
|
5920
|
-
const icon =
|
|
5405
|
+
const icon = fs21.existsSync(".cursorrules") ? chalk6.yellow("~") : chalk6.green("+");
|
|
5921
5406
|
const desc = getDescription(".cursorrules");
|
|
5922
|
-
console.log(` ${icon} ${
|
|
5923
|
-
if (desc) console.log(
|
|
5407
|
+
console.log(` ${icon} ${chalk6.bold(".cursorrules")}`);
|
|
5408
|
+
if (desc) console.log(chalk6.dim(` ${desc}`));
|
|
5924
5409
|
console.log("");
|
|
5925
5410
|
}
|
|
5926
5411
|
const cursorSkills = cursor.skills;
|
|
5927
5412
|
if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
|
|
5928
5413
|
for (const skill of cursorSkills) {
|
|
5929
5414
|
const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
|
|
5930
|
-
const icon =
|
|
5415
|
+
const icon = fs21.existsSync(skillPath) ? chalk6.yellow("~") : chalk6.green("+");
|
|
5931
5416
|
const desc = getDescription(skillPath);
|
|
5932
|
-
console.log(` ${icon} ${
|
|
5933
|
-
console.log(
|
|
5417
|
+
console.log(` ${icon} ${chalk6.bold(skillPath)}`);
|
|
5418
|
+
console.log(chalk6.dim(` ${desc || skill.description || skill.name}`));
|
|
5934
5419
|
console.log("");
|
|
5935
5420
|
}
|
|
5936
5421
|
}
|
|
@@ -5938,40 +5423,40 @@ function printSetupSummary(setup) {
|
|
|
5938
5423
|
if (Array.isArray(rules) && rules.length > 0) {
|
|
5939
5424
|
for (const rule of rules) {
|
|
5940
5425
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
5941
|
-
const icon =
|
|
5426
|
+
const icon = fs21.existsSync(rulePath) ? chalk6.yellow("~") : chalk6.green("+");
|
|
5942
5427
|
const desc = getDescription(rulePath);
|
|
5943
|
-
console.log(` ${icon} ${
|
|
5428
|
+
console.log(` ${icon} ${chalk6.bold(rulePath)}`);
|
|
5944
5429
|
if (desc) {
|
|
5945
|
-
console.log(
|
|
5430
|
+
console.log(chalk6.dim(` ${desc}`));
|
|
5946
5431
|
} else {
|
|
5947
5432
|
const firstLine = rule.content.split("\n").filter((l) => l.trim() && !l.trim().startsWith("#"))[0];
|
|
5948
|
-
if (firstLine) console.log(
|
|
5433
|
+
if (firstLine) console.log(chalk6.dim(` ${firstLine.trim().slice(0, 80)}`));
|
|
5949
5434
|
}
|
|
5950
5435
|
console.log("");
|
|
5951
5436
|
}
|
|
5952
5437
|
}
|
|
5953
5438
|
}
|
|
5954
|
-
if (!codex && !
|
|
5955
|
-
console.log(` ${
|
|
5956
|
-
console.log(
|
|
5439
|
+
if (!codex && !fs21.existsSync("AGENTS.md")) {
|
|
5440
|
+
console.log(` ${chalk6.green("+")} ${chalk6.bold("AGENTS.md")}`);
|
|
5441
|
+
console.log(chalk6.dim(" Cross-agent coordination file"));
|
|
5957
5442
|
console.log("");
|
|
5958
5443
|
}
|
|
5959
5444
|
if (Array.isArray(deletions) && deletions.length > 0) {
|
|
5960
5445
|
for (const del of deletions) {
|
|
5961
|
-
console.log(` ${
|
|
5962
|
-
console.log(
|
|
5446
|
+
console.log(` ${chalk6.red("-")} ${chalk6.bold(del.filePath)}`);
|
|
5447
|
+
console.log(chalk6.dim(` ${del.reason}`));
|
|
5963
5448
|
console.log("");
|
|
5964
5449
|
}
|
|
5965
5450
|
}
|
|
5966
|
-
console.log(` ${
|
|
5451
|
+
console.log(` ${chalk6.green("+")} ${chalk6.dim("new")} ${chalk6.yellow("~")} ${chalk6.dim("modified")} ${chalk6.red("-")} ${chalk6.dim("removed")}`);
|
|
5967
5452
|
console.log("");
|
|
5968
5453
|
}
|
|
5969
5454
|
function ensurePermissions() {
|
|
5970
5455
|
const settingsPath = ".claude/settings.json";
|
|
5971
5456
|
let settings = {};
|
|
5972
5457
|
try {
|
|
5973
|
-
if (
|
|
5974
|
-
settings = JSON.parse(
|
|
5458
|
+
if (fs21.existsSync(settingsPath)) {
|
|
5459
|
+
settings = JSON.parse(fs21.readFileSync(settingsPath, "utf-8"));
|
|
5975
5460
|
}
|
|
5976
5461
|
} catch {
|
|
5977
5462
|
}
|
|
@@ -5985,15 +5470,15 @@ function ensurePermissions() {
|
|
|
5985
5470
|
"Bash(git *)"
|
|
5986
5471
|
];
|
|
5987
5472
|
settings.permissions = permissions;
|
|
5988
|
-
if (!
|
|
5989
|
-
|
|
5473
|
+
if (!fs21.existsSync(".claude")) fs21.mkdirSync(".claude", { recursive: true });
|
|
5474
|
+
fs21.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
5990
5475
|
}
|
|
5991
5476
|
|
|
5992
5477
|
// src/commands/undo.ts
|
|
5993
|
-
import
|
|
5994
|
-
import
|
|
5478
|
+
import chalk7 from "chalk";
|
|
5479
|
+
import ora3 from "ora";
|
|
5995
5480
|
function undoCommand() {
|
|
5996
|
-
const spinner =
|
|
5481
|
+
const spinner = ora3("Reverting setup...").start();
|
|
5997
5482
|
try {
|
|
5998
5483
|
const { restored, removed } = undoSetup();
|
|
5999
5484
|
if (restored.length === 0 && removed.length === 0) {
|
|
@@ -6002,27 +5487,27 @@ function undoCommand() {
|
|
|
6002
5487
|
}
|
|
6003
5488
|
spinner.succeed("Setup reverted successfully.\n");
|
|
6004
5489
|
if (restored.length > 0) {
|
|
6005
|
-
console.log(
|
|
5490
|
+
console.log(chalk7.cyan(" Restored from backup:"));
|
|
6006
5491
|
for (const file of restored) {
|
|
6007
|
-
console.log(` ${
|
|
5492
|
+
console.log(` ${chalk7.green("\u21A9")} ${file}`);
|
|
6008
5493
|
}
|
|
6009
5494
|
}
|
|
6010
5495
|
if (removed.length > 0) {
|
|
6011
|
-
console.log(
|
|
5496
|
+
console.log(chalk7.cyan(" Removed:"));
|
|
6012
5497
|
for (const file of removed) {
|
|
6013
|
-
console.log(` ${
|
|
5498
|
+
console.log(` ${chalk7.red("\u2717")} ${file}`);
|
|
6014
5499
|
}
|
|
6015
5500
|
}
|
|
6016
5501
|
console.log("");
|
|
6017
5502
|
} catch (err) {
|
|
6018
|
-
spinner.fail(
|
|
5503
|
+
spinner.fail(chalk7.red(err instanceof Error ? err.message : "Undo failed"));
|
|
6019
5504
|
throw new Error("__exit__");
|
|
6020
5505
|
}
|
|
6021
5506
|
}
|
|
6022
5507
|
|
|
6023
5508
|
// src/commands/status.ts
|
|
6024
|
-
import
|
|
6025
|
-
import
|
|
5509
|
+
import chalk8 from "chalk";
|
|
5510
|
+
import fs22 from "fs";
|
|
6026
5511
|
async function statusCommand(options) {
|
|
6027
5512
|
const config = loadConfig();
|
|
6028
5513
|
const manifest = readManifest();
|
|
@@ -6035,52 +5520,52 @@ async function statusCommand(options) {
|
|
|
6035
5520
|
}, null, 2));
|
|
6036
5521
|
return;
|
|
6037
5522
|
}
|
|
6038
|
-
console.log(
|
|
5523
|
+
console.log(chalk8.bold("\nCaliber Status\n"));
|
|
6039
5524
|
if (config) {
|
|
6040
|
-
console.log(` LLM: ${
|
|
5525
|
+
console.log(` LLM: ${chalk8.green(config.provider)} (${config.model})`);
|
|
6041
5526
|
} else {
|
|
6042
|
-
console.log(` LLM: ${
|
|
5527
|
+
console.log(` LLM: ${chalk8.yellow("Not configured")} \u2014 run ${chalk8.hex("#83D1EB")("caliber config")}`);
|
|
6043
5528
|
}
|
|
6044
5529
|
if (!manifest) {
|
|
6045
|
-
console.log(` Setup: ${
|
|
6046
|
-
console.log(
|
|
5530
|
+
console.log(` Setup: ${chalk8.dim("No setup applied")}`);
|
|
5531
|
+
console.log(chalk8.dim("\n Run ") + chalk8.hex("#83D1EB")("caliber onboard") + chalk8.dim(" to get started.\n"));
|
|
6047
5532
|
return;
|
|
6048
5533
|
}
|
|
6049
|
-
console.log(` Files managed: ${
|
|
5534
|
+
console.log(` Files managed: ${chalk8.cyan(manifest.entries.length.toString())}`);
|
|
6050
5535
|
for (const entry of manifest.entries) {
|
|
6051
|
-
const exists =
|
|
6052
|
-
const icon = exists ?
|
|
5536
|
+
const exists = fs22.existsSync(entry.path);
|
|
5537
|
+
const icon = exists ? chalk8.green("\u2713") : chalk8.red("\u2717");
|
|
6053
5538
|
console.log(` ${icon} ${entry.path} (${entry.action})`);
|
|
6054
5539
|
}
|
|
6055
5540
|
console.log("");
|
|
6056
5541
|
}
|
|
6057
5542
|
|
|
6058
5543
|
// src/commands/regenerate.ts
|
|
6059
|
-
import
|
|
6060
|
-
import
|
|
5544
|
+
import chalk9 from "chalk";
|
|
5545
|
+
import ora4 from "ora";
|
|
6061
5546
|
import select5 from "@inquirer/select";
|
|
6062
5547
|
async function regenerateCommand(options) {
|
|
6063
5548
|
const config = loadConfig();
|
|
6064
5549
|
if (!config) {
|
|
6065
|
-
console.log(
|
|
5550
|
+
console.log(chalk9.red("No LLM provider configured. Run ") + chalk9.hex("#83D1EB")("caliber config") + chalk9.red(" first."));
|
|
6066
5551
|
throw new Error("__exit__");
|
|
6067
5552
|
}
|
|
6068
5553
|
const manifest = readManifest();
|
|
6069
5554
|
if (!manifest) {
|
|
6070
|
-
console.log(
|
|
5555
|
+
console.log(chalk9.yellow("No existing setup found. Run ") + chalk9.hex("#83D1EB")("caliber onboard") + chalk9.yellow(" first."));
|
|
6071
5556
|
throw new Error("__exit__");
|
|
6072
5557
|
}
|
|
6073
5558
|
const targetAgent = readState()?.targetAgent ?? ["claude", "cursor"];
|
|
6074
|
-
const spinner =
|
|
5559
|
+
const spinner = ora4("Analyzing project...").start();
|
|
6075
5560
|
const fingerprint = await collectFingerprint(process.cwd());
|
|
6076
5561
|
spinner.succeed("Project analyzed");
|
|
6077
5562
|
const baselineScore = computeLocalScore(process.cwd(), targetAgent);
|
|
6078
5563
|
displayScoreSummary(baselineScore);
|
|
6079
5564
|
if (baselineScore.score === 100) {
|
|
6080
|
-
console.log(
|
|
5565
|
+
console.log(chalk9.green(" Your setup is already at 100/100 \u2014 nothing to regenerate.\n"));
|
|
6081
5566
|
return;
|
|
6082
5567
|
}
|
|
6083
|
-
const genSpinner =
|
|
5568
|
+
const genSpinner = ora4("Regenerating setup...").start();
|
|
6084
5569
|
const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, { showElapsedTime: true });
|
|
6085
5570
|
genMessages.start();
|
|
6086
5571
|
let generatedSetup = null;
|
|
@@ -6118,18 +5603,18 @@ async function regenerateCommand(options) {
|
|
|
6118
5603
|
const setupFiles = collectSetupFiles(generatedSetup);
|
|
6119
5604
|
const staged = stageFiles(setupFiles, process.cwd());
|
|
6120
5605
|
const totalChanges = staged.newFiles + staged.modifiedFiles;
|
|
6121
|
-
console.log(
|
|
6122
|
-
${
|
|
5606
|
+
console.log(chalk9.dim(`
|
|
5607
|
+
${chalk9.green(`${staged.newFiles} new`)} / ${chalk9.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}
|
|
6123
5608
|
`));
|
|
6124
5609
|
if (totalChanges === 0) {
|
|
6125
|
-
console.log(
|
|
5610
|
+
console.log(chalk9.dim(" No changes needed \u2014 your configs are already up to date.\n"));
|
|
6126
5611
|
cleanupStaging();
|
|
6127
5612
|
return;
|
|
6128
5613
|
}
|
|
6129
5614
|
if (options.dryRun) {
|
|
6130
|
-
console.log(
|
|
5615
|
+
console.log(chalk9.yellow("[Dry run] Would write:"));
|
|
6131
5616
|
for (const f of staged.stagedFiles) {
|
|
6132
|
-
console.log(` ${f.isNew ?
|
|
5617
|
+
console.log(` ${f.isNew ? chalk9.green("+") : chalk9.yellow("~")} ${f.relativePath}`);
|
|
6133
5618
|
}
|
|
6134
5619
|
cleanupStaging();
|
|
6135
5620
|
return;
|
|
@@ -6148,28 +5633,28 @@ async function regenerateCommand(options) {
|
|
|
6148
5633
|
});
|
|
6149
5634
|
cleanupStaging();
|
|
6150
5635
|
if (action === "decline") {
|
|
6151
|
-
console.log(
|
|
5636
|
+
console.log(chalk9.dim("Regeneration cancelled. No files were modified."));
|
|
6152
5637
|
return;
|
|
6153
5638
|
}
|
|
6154
|
-
const writeSpinner =
|
|
5639
|
+
const writeSpinner = ora4("Writing config files...").start();
|
|
6155
5640
|
try {
|
|
6156
5641
|
const result = writeSetup(generatedSetup);
|
|
6157
5642
|
writeSpinner.succeed("Config files written");
|
|
6158
5643
|
for (const file of result.written) {
|
|
6159
|
-
console.log(` ${
|
|
5644
|
+
console.log(` ${chalk9.green("\u2713")} ${file}`);
|
|
6160
5645
|
}
|
|
6161
5646
|
if (result.deleted.length > 0) {
|
|
6162
5647
|
for (const file of result.deleted) {
|
|
6163
|
-
console.log(` ${
|
|
5648
|
+
console.log(` ${chalk9.red("\u2717")} ${file}`);
|
|
6164
5649
|
}
|
|
6165
5650
|
}
|
|
6166
5651
|
if (result.backupDir) {
|
|
6167
|
-
console.log(
|
|
5652
|
+
console.log(chalk9.dim(`
|
|
6168
5653
|
Backups saved to ${result.backupDir}`));
|
|
6169
5654
|
}
|
|
6170
5655
|
} catch (err) {
|
|
6171
5656
|
writeSpinner.fail("Failed to write files");
|
|
6172
|
-
console.error(
|
|
5657
|
+
console.error(chalk9.red(err instanceof Error ? err.message : "Unknown error"));
|
|
6173
5658
|
throw new Error("__exit__");
|
|
6174
5659
|
}
|
|
6175
5660
|
const sha = getCurrentHeadSha();
|
|
@@ -6181,24 +5666,24 @@ async function regenerateCommand(options) {
|
|
|
6181
5666
|
const afterScore = computeLocalScore(process.cwd(), targetAgent);
|
|
6182
5667
|
if (afterScore.score < baselineScore.score) {
|
|
6183
5668
|
console.log("");
|
|
6184
|
-
console.log(
|
|
5669
|
+
console.log(chalk9.yellow(` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`));
|
|
6185
5670
|
try {
|
|
6186
5671
|
const { restored, removed } = undoSetup();
|
|
6187
5672
|
if (restored.length > 0 || removed.length > 0) {
|
|
6188
|
-
console.log(
|
|
5673
|
+
console.log(chalk9.dim(` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`));
|
|
6189
5674
|
}
|
|
6190
5675
|
} catch {
|
|
6191
5676
|
}
|
|
6192
|
-
console.log(
|
|
5677
|
+
console.log(chalk9.dim(" Run ") + chalk9.hex("#83D1EB")("caliber onboard --force") + chalk9.dim(" to override.\n"));
|
|
6193
5678
|
return;
|
|
6194
5679
|
}
|
|
6195
5680
|
displayScoreDelta(baselineScore, afterScore);
|
|
6196
|
-
console.log(
|
|
6197
|
-
console.log(
|
|
5681
|
+
console.log(chalk9.bold.green(" Regeneration complete!"));
|
|
5682
|
+
console.log(chalk9.dim(" Run ") + chalk9.hex("#83D1EB")("caliber undo") + chalk9.dim(" to revert changes.\n"));
|
|
6198
5683
|
}
|
|
6199
5684
|
|
|
6200
5685
|
// src/commands/score.ts
|
|
6201
|
-
import
|
|
5686
|
+
import chalk10 from "chalk";
|
|
6202
5687
|
async function scoreCommand(options) {
|
|
6203
5688
|
const dir = process.cwd();
|
|
6204
5689
|
const target = options.agent ?? readState()?.targetAgent;
|
|
@@ -6212,23 +5697,23 @@ async function scoreCommand(options) {
|
|
|
6212
5697
|
return;
|
|
6213
5698
|
}
|
|
6214
5699
|
displayScore(result);
|
|
6215
|
-
const separator =
|
|
5700
|
+
const separator = chalk10.gray(" " + "\u2500".repeat(53));
|
|
6216
5701
|
console.log(separator);
|
|
6217
5702
|
if (result.score < 40) {
|
|
6218
|
-
console.log(
|
|
5703
|
+
console.log(chalk10.gray(" Run ") + chalk10.hex("#83D1EB")("caliber onboard") + chalk10.gray(" to generate a complete, optimized setup."));
|
|
6219
5704
|
} else if (result.score < 70) {
|
|
6220
|
-
console.log(
|
|
5705
|
+
console.log(chalk10.gray(" Run ") + chalk10.hex("#83D1EB")("caliber onboard") + chalk10.gray(" to improve your setup."));
|
|
6221
5706
|
} else {
|
|
6222
|
-
console.log(
|
|
5707
|
+
console.log(chalk10.green(" Looking good!") + chalk10.gray(" Run ") + chalk10.hex("#83D1EB")("caliber regenerate") + chalk10.gray(" to rebuild from scratch."));
|
|
6223
5708
|
}
|
|
6224
5709
|
console.log("");
|
|
6225
5710
|
}
|
|
6226
5711
|
|
|
6227
5712
|
// src/commands/refresh.ts
|
|
6228
|
-
import
|
|
6229
|
-
import
|
|
6230
|
-
import
|
|
6231
|
-
import
|
|
5713
|
+
import fs24 from "fs";
|
|
5714
|
+
import path18 from "path";
|
|
5715
|
+
import chalk11 from "chalk";
|
|
5716
|
+
import ora5 from "ora";
|
|
6232
5717
|
|
|
6233
5718
|
// src/lib/git-diff.ts
|
|
6234
5719
|
import { execSync as execSync8 } from "child_process";
|
|
@@ -6303,37 +5788,37 @@ function collectDiff(lastSha) {
|
|
|
6303
5788
|
}
|
|
6304
5789
|
|
|
6305
5790
|
// src/writers/refresh.ts
|
|
6306
|
-
import
|
|
6307
|
-
import
|
|
5791
|
+
import fs23 from "fs";
|
|
5792
|
+
import path17 from "path";
|
|
6308
5793
|
function writeRefreshDocs(docs) {
|
|
6309
5794
|
const written = [];
|
|
6310
5795
|
if (docs.claudeMd) {
|
|
6311
|
-
|
|
5796
|
+
fs23.writeFileSync("CLAUDE.md", docs.claudeMd);
|
|
6312
5797
|
written.push("CLAUDE.md");
|
|
6313
5798
|
}
|
|
6314
5799
|
if (docs.readmeMd) {
|
|
6315
|
-
|
|
5800
|
+
fs23.writeFileSync("README.md", docs.readmeMd);
|
|
6316
5801
|
written.push("README.md");
|
|
6317
5802
|
}
|
|
6318
5803
|
if (docs.cursorrules) {
|
|
6319
|
-
|
|
5804
|
+
fs23.writeFileSync(".cursorrules", docs.cursorrules);
|
|
6320
5805
|
written.push(".cursorrules");
|
|
6321
5806
|
}
|
|
6322
5807
|
if (docs.cursorRules) {
|
|
6323
|
-
const rulesDir =
|
|
6324
|
-
if (!
|
|
5808
|
+
const rulesDir = path17.join(".cursor", "rules");
|
|
5809
|
+
if (!fs23.existsSync(rulesDir)) fs23.mkdirSync(rulesDir, { recursive: true });
|
|
6325
5810
|
for (const rule of docs.cursorRules) {
|
|
6326
|
-
const filePath =
|
|
6327
|
-
|
|
5811
|
+
const filePath = path17.join(rulesDir, rule.filename);
|
|
5812
|
+
fs23.writeFileSync(filePath, rule.content);
|
|
6328
5813
|
written.push(filePath);
|
|
6329
5814
|
}
|
|
6330
5815
|
}
|
|
6331
5816
|
if (docs.claudeSkills) {
|
|
6332
|
-
const skillsDir =
|
|
6333
|
-
if (!
|
|
5817
|
+
const skillsDir = path17.join(".claude", "skills");
|
|
5818
|
+
if (!fs23.existsSync(skillsDir)) fs23.mkdirSync(skillsDir, { recursive: true });
|
|
6334
5819
|
for (const skill of docs.claudeSkills) {
|
|
6335
|
-
const filePath =
|
|
6336
|
-
|
|
5820
|
+
const filePath = path17.join(skillsDir, skill.filename);
|
|
5821
|
+
fs23.writeFileSync(filePath, skill.content);
|
|
6337
5822
|
written.push(filePath);
|
|
6338
5823
|
}
|
|
6339
5824
|
}
|
|
@@ -6410,11 +5895,11 @@ function log(quiet, ...args) {
|
|
|
6410
5895
|
function discoverGitRepos(parentDir) {
|
|
6411
5896
|
const repos = [];
|
|
6412
5897
|
try {
|
|
6413
|
-
const entries =
|
|
5898
|
+
const entries = fs24.readdirSync(parentDir, { withFileTypes: true });
|
|
6414
5899
|
for (const entry of entries) {
|
|
6415
5900
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
6416
|
-
const childPath =
|
|
6417
|
-
if (
|
|
5901
|
+
const childPath = path18.join(parentDir, entry.name);
|
|
5902
|
+
if (fs24.existsSync(path18.join(childPath, ".git"))) {
|
|
6418
5903
|
repos.push(childPath);
|
|
6419
5904
|
}
|
|
6420
5905
|
}
|
|
@@ -6424,7 +5909,7 @@ function discoverGitRepos(parentDir) {
|
|
|
6424
5909
|
}
|
|
6425
5910
|
async function refreshSingleRepo(repoDir, options) {
|
|
6426
5911
|
const quiet = !!options.quiet;
|
|
6427
|
-
const prefix = options.label ? `${
|
|
5912
|
+
const prefix = options.label ? `${chalk11.bold(options.label)} ` : "";
|
|
6428
5913
|
const state = readState();
|
|
6429
5914
|
const lastSha = state?.lastRefreshSha ?? null;
|
|
6430
5915
|
const diff = collectDiff(lastSha);
|
|
@@ -6433,10 +5918,10 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
6433
5918
|
if (currentSha) {
|
|
6434
5919
|
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
6435
5920
|
}
|
|
6436
|
-
log(quiet,
|
|
5921
|
+
log(quiet, chalk11.dim(`${prefix}No changes since last refresh.`));
|
|
6437
5922
|
return;
|
|
6438
5923
|
}
|
|
6439
|
-
const spinner = quiet ? null :
|
|
5924
|
+
const spinner = quiet ? null : ora5(`${prefix}Analyzing changes...`).start();
|
|
6440
5925
|
const existingDocs = readExistingConfigs(repoDir);
|
|
6441
5926
|
const fingerprint = await collectFingerprint(repoDir);
|
|
6442
5927
|
const projectContext = {
|
|
@@ -6465,10 +5950,10 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
6465
5950
|
if (options.dryRun) {
|
|
6466
5951
|
spinner?.info(`${prefix}Dry run \u2014 would update:`);
|
|
6467
5952
|
for (const doc of response.docsUpdated) {
|
|
6468
|
-
console.log(` ${
|
|
5953
|
+
console.log(` ${chalk11.yellow("~")} ${doc}`);
|
|
6469
5954
|
}
|
|
6470
5955
|
if (response.changesSummary) {
|
|
6471
|
-
console.log(
|
|
5956
|
+
console.log(chalk11.dim(`
|
|
6472
5957
|
${response.changesSummary}`));
|
|
6473
5958
|
}
|
|
6474
5959
|
return;
|
|
@@ -6476,10 +5961,10 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
6476
5961
|
const written = writeRefreshDocs(response.updatedDocs);
|
|
6477
5962
|
spinner?.succeed(`${prefix}Updated ${written.length} doc${written.length === 1 ? "" : "s"}`);
|
|
6478
5963
|
for (const file of written) {
|
|
6479
|
-
log(quiet, ` ${
|
|
5964
|
+
log(quiet, ` ${chalk11.green("\u2713")} ${file}`);
|
|
6480
5965
|
}
|
|
6481
5966
|
if (response.changesSummary) {
|
|
6482
|
-
log(quiet,
|
|
5967
|
+
log(quiet, chalk11.dim(`
|
|
6483
5968
|
${response.changesSummary}`));
|
|
6484
5969
|
}
|
|
6485
5970
|
if (currentSha) {
|
|
@@ -6492,7 +5977,7 @@ async function refreshCommand(options) {
|
|
|
6492
5977
|
const config = loadConfig();
|
|
6493
5978
|
if (!config) {
|
|
6494
5979
|
if (quiet) return;
|
|
6495
|
-
console.log(
|
|
5980
|
+
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."));
|
|
6496
5981
|
throw new Error("__exit__");
|
|
6497
5982
|
}
|
|
6498
5983
|
if (isGitRepo()) {
|
|
@@ -6502,20 +5987,20 @@ async function refreshCommand(options) {
|
|
|
6502
5987
|
const repos = discoverGitRepos(process.cwd());
|
|
6503
5988
|
if (repos.length === 0) {
|
|
6504
5989
|
if (quiet) return;
|
|
6505
|
-
console.log(
|
|
5990
|
+
console.log(chalk11.red("Not inside a git repository and no git repos found in child directories."));
|
|
6506
5991
|
throw new Error("__exit__");
|
|
6507
5992
|
}
|
|
6508
|
-
log(quiet,
|
|
5993
|
+
log(quiet, chalk11.dim(`Found ${repos.length} git repo${repos.length === 1 ? "" : "s"}
|
|
6509
5994
|
`));
|
|
6510
5995
|
const originalDir = process.cwd();
|
|
6511
5996
|
for (const repo of repos) {
|
|
6512
|
-
const repoName =
|
|
5997
|
+
const repoName = path18.basename(repo);
|
|
6513
5998
|
try {
|
|
6514
5999
|
process.chdir(repo);
|
|
6515
6000
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
6516
6001
|
} catch (err) {
|
|
6517
6002
|
if (err instanceof Error && err.message === "__exit__") continue;
|
|
6518
|
-
log(quiet,
|
|
6003
|
+
log(quiet, chalk11.yellow(`${repoName}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`));
|
|
6519
6004
|
}
|
|
6520
6005
|
}
|
|
6521
6006
|
process.chdir(originalDir);
|
|
@@ -6523,13 +6008,13 @@ async function refreshCommand(options) {
|
|
|
6523
6008
|
if (err instanceof Error && err.message === "__exit__") throw err;
|
|
6524
6009
|
if (quiet) return;
|
|
6525
6010
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
6526
|
-
console.log(
|
|
6011
|
+
console.log(chalk11.red(`Refresh failed: ${msg}`));
|
|
6527
6012
|
throw new Error("__exit__");
|
|
6528
6013
|
}
|
|
6529
6014
|
}
|
|
6530
6015
|
|
|
6531
6016
|
// src/commands/hooks.ts
|
|
6532
|
-
import
|
|
6017
|
+
import chalk12 from "chalk";
|
|
6533
6018
|
var HOOKS = [
|
|
6534
6019
|
{
|
|
6535
6020
|
id: "session-end",
|
|
@@ -6549,13 +6034,13 @@ var HOOKS = [
|
|
|
6549
6034
|
}
|
|
6550
6035
|
];
|
|
6551
6036
|
function printStatus() {
|
|
6552
|
-
console.log(
|
|
6037
|
+
console.log(chalk12.bold("\n Hooks\n"));
|
|
6553
6038
|
for (const hook of HOOKS) {
|
|
6554
6039
|
const installed = hook.isInstalled();
|
|
6555
|
-
const icon = installed ?
|
|
6556
|
-
const state = installed ?
|
|
6040
|
+
const icon = installed ? chalk12.green("\u2713") : chalk12.dim("\u2717");
|
|
6041
|
+
const state = installed ? chalk12.green("enabled") : chalk12.dim("disabled");
|
|
6557
6042
|
console.log(` ${icon} ${hook.label.padEnd(26)} ${state}`);
|
|
6558
|
-
console.log(
|
|
6043
|
+
console.log(chalk12.dim(` ${hook.description}`));
|
|
6559
6044
|
}
|
|
6560
6045
|
console.log("");
|
|
6561
6046
|
}
|
|
@@ -6564,9 +6049,9 @@ async function hooksCommand(options) {
|
|
|
6564
6049
|
for (const hook of HOOKS) {
|
|
6565
6050
|
const result = hook.install();
|
|
6566
6051
|
if (result.alreadyInstalled) {
|
|
6567
|
-
console.log(
|
|
6052
|
+
console.log(chalk12.dim(` ${hook.label} already enabled.`));
|
|
6568
6053
|
} else {
|
|
6569
|
-
console.log(
|
|
6054
|
+
console.log(chalk12.green(" \u2713") + ` ${hook.label} enabled`);
|
|
6570
6055
|
}
|
|
6571
6056
|
}
|
|
6572
6057
|
return;
|
|
@@ -6575,9 +6060,9 @@ async function hooksCommand(options) {
|
|
|
6575
6060
|
for (const hook of HOOKS) {
|
|
6576
6061
|
const result = hook.remove();
|
|
6577
6062
|
if (result.notFound) {
|
|
6578
|
-
console.log(
|
|
6063
|
+
console.log(chalk12.dim(` ${hook.label} already disabled.`));
|
|
6579
6064
|
} else {
|
|
6580
|
-
console.log(
|
|
6065
|
+
console.log(chalk12.green(" \u2713") + ` ${hook.label} removed`);
|
|
6581
6066
|
}
|
|
6582
6067
|
}
|
|
6583
6068
|
return;
|
|
@@ -6592,18 +6077,18 @@ async function hooksCommand(options) {
|
|
|
6592
6077
|
const states = HOOKS.map((h) => h.isInstalled());
|
|
6593
6078
|
function render() {
|
|
6594
6079
|
const lines = [];
|
|
6595
|
-
lines.push(
|
|
6080
|
+
lines.push(chalk12.bold(" Hooks"));
|
|
6596
6081
|
lines.push("");
|
|
6597
6082
|
for (let i = 0; i < HOOKS.length; i++) {
|
|
6598
6083
|
const hook = HOOKS[i];
|
|
6599
6084
|
const enabled = states[i];
|
|
6600
|
-
const toggle = enabled ?
|
|
6601
|
-
const ptr = i === cursor ?
|
|
6085
|
+
const toggle = enabled ? chalk12.green("[on] ") : chalk12.dim("[off]");
|
|
6086
|
+
const ptr = i === cursor ? chalk12.cyan(">") : " ";
|
|
6602
6087
|
lines.push(` ${ptr} ${toggle} ${hook.label}`);
|
|
6603
|
-
lines.push(
|
|
6088
|
+
lines.push(chalk12.dim(` ${hook.description}`));
|
|
6604
6089
|
}
|
|
6605
6090
|
lines.push("");
|
|
6606
|
-
lines.push(
|
|
6091
|
+
lines.push(chalk12.dim(" \u2191\u2193 navigate \u23B5 toggle a all on n all off \u23CE apply q cancel"));
|
|
6607
6092
|
return lines.join("\n");
|
|
6608
6093
|
}
|
|
6609
6094
|
function draw(initial) {
|
|
@@ -6634,16 +6119,16 @@ async function hooksCommand(options) {
|
|
|
6634
6119
|
const wantEnabled = states[i];
|
|
6635
6120
|
if (wantEnabled && !wasInstalled) {
|
|
6636
6121
|
hook.install();
|
|
6637
|
-
console.log(
|
|
6122
|
+
console.log(chalk12.green(" \u2713") + ` ${hook.label} enabled`);
|
|
6638
6123
|
changed++;
|
|
6639
6124
|
} else if (!wantEnabled && wasInstalled) {
|
|
6640
6125
|
hook.remove();
|
|
6641
|
-
console.log(
|
|
6126
|
+
console.log(chalk12.green(" \u2713") + ` ${hook.label} disabled`);
|
|
6642
6127
|
changed++;
|
|
6643
6128
|
}
|
|
6644
6129
|
}
|
|
6645
6130
|
if (changed === 0) {
|
|
6646
|
-
console.log(
|
|
6131
|
+
console.log(chalk12.dim(" No changes."));
|
|
6647
6132
|
}
|
|
6648
6133
|
console.log("");
|
|
6649
6134
|
}
|
|
@@ -6679,7 +6164,7 @@ async function hooksCommand(options) {
|
|
|
6679
6164
|
case "\x1B":
|
|
6680
6165
|
case "":
|
|
6681
6166
|
cleanup();
|
|
6682
|
-
console.log(
|
|
6167
|
+
console.log(chalk12.dim("\n Cancelled.\n"));
|
|
6683
6168
|
resolve2();
|
|
6684
6169
|
break;
|
|
6685
6170
|
}
|
|
@@ -6689,48 +6174,48 @@ async function hooksCommand(options) {
|
|
|
6689
6174
|
}
|
|
6690
6175
|
|
|
6691
6176
|
// src/commands/config.ts
|
|
6692
|
-
import
|
|
6177
|
+
import chalk13 from "chalk";
|
|
6693
6178
|
async function configCommand() {
|
|
6694
6179
|
const existing = loadConfig();
|
|
6695
6180
|
if (existing) {
|
|
6696
6181
|
const displayModel = existing.model === "default" && existing.provider === "claude-cli" ? process.env.ANTHROPIC_MODEL || "default (inherited from Claude Code)" : existing.model;
|
|
6697
6182
|
const fastModel = getFastModel();
|
|
6698
|
-
console.log(
|
|
6699
|
-
console.log(` Provider: ${
|
|
6700
|
-
console.log(` Model: ${
|
|
6183
|
+
console.log(chalk13.bold("\nCurrent Configuration\n"));
|
|
6184
|
+
console.log(` Provider: ${chalk13.cyan(existing.provider)}`);
|
|
6185
|
+
console.log(` Model: ${chalk13.cyan(displayModel)}`);
|
|
6701
6186
|
if (fastModel) {
|
|
6702
|
-
console.log(` Scan: ${
|
|
6187
|
+
console.log(` Scan: ${chalk13.cyan(fastModel)}`);
|
|
6703
6188
|
}
|
|
6704
6189
|
if (existing.apiKey) {
|
|
6705
6190
|
const masked = existing.apiKey.slice(0, 8) + "..." + existing.apiKey.slice(-4);
|
|
6706
|
-
console.log(` API Key: ${
|
|
6191
|
+
console.log(` API Key: ${chalk13.dim(masked)}`);
|
|
6707
6192
|
}
|
|
6708
6193
|
if (existing.provider === "cursor") {
|
|
6709
|
-
console.log(` Seat: ${
|
|
6194
|
+
console.log(` Seat: ${chalk13.dim("Cursor (agent acp)")}`);
|
|
6710
6195
|
}
|
|
6711
6196
|
if (existing.provider === "claude-cli") {
|
|
6712
|
-
console.log(` Seat: ${
|
|
6197
|
+
console.log(` Seat: ${chalk13.dim("Claude Code (claude -p)")}`);
|
|
6713
6198
|
}
|
|
6714
6199
|
if (existing.baseUrl) {
|
|
6715
|
-
console.log(` Base URL: ${
|
|
6200
|
+
console.log(` Base URL: ${chalk13.dim(existing.baseUrl)}`);
|
|
6716
6201
|
}
|
|
6717
6202
|
if (existing.vertexProjectId) {
|
|
6718
|
-
console.log(` Vertex Project: ${
|
|
6719
|
-
console.log(` Vertex Region: ${
|
|
6203
|
+
console.log(` Vertex Project: ${chalk13.dim(existing.vertexProjectId)}`);
|
|
6204
|
+
console.log(` Vertex Region: ${chalk13.dim(existing.vertexRegion || "us-east5")}`);
|
|
6720
6205
|
}
|
|
6721
|
-
console.log(` Source: ${
|
|
6206
|
+
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())}`);
|
|
6722
6207
|
console.log("");
|
|
6723
6208
|
}
|
|
6724
6209
|
await runInteractiveProviderSetup();
|
|
6725
|
-
console.log(
|
|
6726
|
-
console.log(
|
|
6210
|
+
console.log(chalk13.green("\n\u2713 Configuration saved"));
|
|
6211
|
+
console.log(chalk13.dim(` ${getConfigFilePath()}
|
|
6727
6212
|
`));
|
|
6728
|
-
console.log(
|
|
6729
|
-
console.log(
|
|
6213
|
+
console.log(chalk13.dim(" You can also set environment variables instead:"));
|
|
6214
|
+
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"));
|
|
6730
6215
|
}
|
|
6731
6216
|
|
|
6732
6217
|
// src/commands/learn.ts
|
|
6733
|
-
import
|
|
6218
|
+
import chalk14 from "chalk";
|
|
6734
6219
|
|
|
6735
6220
|
// src/learner/stdin.ts
|
|
6736
6221
|
var STDIN_TIMEOUT_MS = 5e3;
|
|
@@ -6761,8 +6246,8 @@ function readStdin() {
|
|
|
6761
6246
|
|
|
6762
6247
|
// src/learner/storage.ts
|
|
6763
6248
|
init_constants();
|
|
6764
|
-
import
|
|
6765
|
-
import
|
|
6249
|
+
import fs25 from "fs";
|
|
6250
|
+
import path19 from "path";
|
|
6766
6251
|
var MAX_RESPONSE_LENGTH = 2e3;
|
|
6767
6252
|
var DEFAULT_STATE = {
|
|
6768
6253
|
sessionId: null,
|
|
@@ -6770,15 +6255,15 @@ var DEFAULT_STATE = {
|
|
|
6770
6255
|
lastAnalysisTimestamp: null
|
|
6771
6256
|
};
|
|
6772
6257
|
function ensureLearningDir() {
|
|
6773
|
-
if (!
|
|
6774
|
-
|
|
6258
|
+
if (!fs25.existsSync(LEARNING_DIR)) {
|
|
6259
|
+
fs25.mkdirSync(LEARNING_DIR, { recursive: true });
|
|
6775
6260
|
}
|
|
6776
6261
|
}
|
|
6777
6262
|
function sessionFilePath() {
|
|
6778
|
-
return
|
|
6263
|
+
return path19.join(LEARNING_DIR, LEARNING_SESSION_FILE);
|
|
6779
6264
|
}
|
|
6780
6265
|
function stateFilePath() {
|
|
6781
|
-
return
|
|
6266
|
+
return path19.join(LEARNING_DIR, LEARNING_STATE_FILE);
|
|
6782
6267
|
}
|
|
6783
6268
|
function truncateResponse(response) {
|
|
6784
6269
|
const str = JSON.stringify(response);
|
|
@@ -6789,50 +6274,50 @@ function appendEvent(event) {
|
|
|
6789
6274
|
ensureLearningDir();
|
|
6790
6275
|
const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
|
|
6791
6276
|
const filePath = sessionFilePath();
|
|
6792
|
-
|
|
6277
|
+
fs25.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
|
|
6793
6278
|
const count = getEventCount();
|
|
6794
6279
|
if (count > LEARNING_MAX_EVENTS) {
|
|
6795
|
-
const lines =
|
|
6280
|
+
const lines = fs25.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
6796
6281
|
const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
|
|
6797
|
-
|
|
6282
|
+
fs25.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
6798
6283
|
}
|
|
6799
6284
|
}
|
|
6800
6285
|
function readAllEvents() {
|
|
6801
6286
|
const filePath = sessionFilePath();
|
|
6802
|
-
if (!
|
|
6803
|
-
const lines =
|
|
6287
|
+
if (!fs25.existsSync(filePath)) return [];
|
|
6288
|
+
const lines = fs25.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
6804
6289
|
return lines.map((line) => JSON.parse(line));
|
|
6805
6290
|
}
|
|
6806
6291
|
function getEventCount() {
|
|
6807
6292
|
const filePath = sessionFilePath();
|
|
6808
|
-
if (!
|
|
6809
|
-
const content =
|
|
6293
|
+
if (!fs25.existsSync(filePath)) return 0;
|
|
6294
|
+
const content = fs25.readFileSync(filePath, "utf-8");
|
|
6810
6295
|
return content.split("\n").filter(Boolean).length;
|
|
6811
6296
|
}
|
|
6812
6297
|
function clearSession() {
|
|
6813
6298
|
const filePath = sessionFilePath();
|
|
6814
|
-
if (
|
|
6299
|
+
if (fs25.existsSync(filePath)) fs25.unlinkSync(filePath);
|
|
6815
6300
|
}
|
|
6816
6301
|
function readState2() {
|
|
6817
6302
|
const filePath = stateFilePath();
|
|
6818
|
-
if (!
|
|
6303
|
+
if (!fs25.existsSync(filePath)) return { ...DEFAULT_STATE };
|
|
6819
6304
|
try {
|
|
6820
|
-
return JSON.parse(
|
|
6305
|
+
return JSON.parse(fs25.readFileSync(filePath, "utf-8"));
|
|
6821
6306
|
} catch {
|
|
6822
6307
|
return { ...DEFAULT_STATE };
|
|
6823
6308
|
}
|
|
6824
6309
|
}
|
|
6825
6310
|
function writeState2(state) {
|
|
6826
6311
|
ensureLearningDir();
|
|
6827
|
-
|
|
6312
|
+
fs25.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
|
|
6828
6313
|
}
|
|
6829
6314
|
function resetState() {
|
|
6830
6315
|
writeState2({ ...DEFAULT_STATE });
|
|
6831
6316
|
}
|
|
6832
6317
|
|
|
6833
6318
|
// src/learner/writer.ts
|
|
6834
|
-
import
|
|
6835
|
-
import
|
|
6319
|
+
import fs26 from "fs";
|
|
6320
|
+
import path20 from "path";
|
|
6836
6321
|
var LEARNED_START = "<!-- caliber:learned -->";
|
|
6837
6322
|
var LEARNED_END = "<!-- /caliber:learned -->";
|
|
6838
6323
|
function writeLearnedContent(update) {
|
|
@@ -6852,8 +6337,8 @@ function writeLearnedContent(update) {
|
|
|
6852
6337
|
function writeLearnedSection(content) {
|
|
6853
6338
|
const claudeMdPath = "CLAUDE.md";
|
|
6854
6339
|
let existing = "";
|
|
6855
|
-
if (
|
|
6856
|
-
existing =
|
|
6340
|
+
if (fs26.existsSync(claudeMdPath)) {
|
|
6341
|
+
existing = fs26.readFileSync(claudeMdPath, "utf-8");
|
|
6857
6342
|
}
|
|
6858
6343
|
const section = `${LEARNED_START}
|
|
6859
6344
|
${content}
|
|
@@ -6867,15 +6352,15 @@ ${LEARNED_END}`;
|
|
|
6867
6352
|
const separator = existing.endsWith("\n") || existing === "" ? "" : "\n";
|
|
6868
6353
|
updated = existing + separator + "\n" + section + "\n";
|
|
6869
6354
|
}
|
|
6870
|
-
|
|
6355
|
+
fs26.writeFileSync(claudeMdPath, updated);
|
|
6871
6356
|
}
|
|
6872
6357
|
function writeLearnedSkill(skill) {
|
|
6873
|
-
const skillDir =
|
|
6874
|
-
if (!
|
|
6875
|
-
const skillPath =
|
|
6876
|
-
if (!skill.isNew &&
|
|
6877
|
-
const existing =
|
|
6878
|
-
|
|
6358
|
+
const skillDir = path20.join(".claude", "skills", skill.name);
|
|
6359
|
+
if (!fs26.existsSync(skillDir)) fs26.mkdirSync(skillDir, { recursive: true });
|
|
6360
|
+
const skillPath = path20.join(skillDir, "SKILL.md");
|
|
6361
|
+
if (!skill.isNew && fs26.existsSync(skillPath)) {
|
|
6362
|
+
const existing = fs26.readFileSync(skillPath, "utf-8");
|
|
6363
|
+
fs26.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
|
|
6879
6364
|
} else {
|
|
6880
6365
|
const frontmatter = [
|
|
6881
6366
|
"---",
|
|
@@ -6884,14 +6369,14 @@ function writeLearnedSkill(skill) {
|
|
|
6884
6369
|
"---",
|
|
6885
6370
|
""
|
|
6886
6371
|
].join("\n");
|
|
6887
|
-
|
|
6372
|
+
fs26.writeFileSync(skillPath, frontmatter + skill.content);
|
|
6888
6373
|
}
|
|
6889
6374
|
return skillPath;
|
|
6890
6375
|
}
|
|
6891
6376
|
function readLearnedSection() {
|
|
6892
6377
|
const claudeMdPath = "CLAUDE.md";
|
|
6893
|
-
if (!
|
|
6894
|
-
const content =
|
|
6378
|
+
if (!fs26.existsSync(claudeMdPath)) return null;
|
|
6379
|
+
const content = fs26.readFileSync(claudeMdPath, "utf-8");
|
|
6895
6380
|
const startIdx = content.indexOf(LEARNED_START);
|
|
6896
6381
|
const endIdx = content.indexOf(LEARNED_END);
|
|
6897
6382
|
if (startIdx === -1 || endIdx === -1) return null;
|
|
@@ -7033,53 +6518,53 @@ async function learnFinalizeCommand() {
|
|
|
7033
6518
|
async function learnInstallCommand() {
|
|
7034
6519
|
const result = installLearningHooks();
|
|
7035
6520
|
if (result.alreadyInstalled) {
|
|
7036
|
-
console.log(
|
|
6521
|
+
console.log(chalk14.dim("Learning hooks already installed."));
|
|
7037
6522
|
return;
|
|
7038
6523
|
}
|
|
7039
|
-
console.log(
|
|
7040
|
-
console.log(
|
|
7041
|
-
console.log(
|
|
6524
|
+
console.log(chalk14.green("\u2713") + " Learning hooks installed in .claude/settings.json");
|
|
6525
|
+
console.log(chalk14.dim(" PostToolUse, PostToolUseFailure, and SessionEnd hooks active."));
|
|
6526
|
+
console.log(chalk14.dim(" Session learnings will be written to CLAUDE.md and skills."));
|
|
7042
6527
|
}
|
|
7043
6528
|
async function learnRemoveCommand() {
|
|
7044
6529
|
const result = removeLearningHooks();
|
|
7045
6530
|
if (result.notFound) {
|
|
7046
|
-
console.log(
|
|
6531
|
+
console.log(chalk14.dim("Learning hooks not found."));
|
|
7047
6532
|
return;
|
|
7048
6533
|
}
|
|
7049
|
-
console.log(
|
|
6534
|
+
console.log(chalk14.green("\u2713") + " Learning hooks removed from .claude/settings.json");
|
|
7050
6535
|
}
|
|
7051
6536
|
async function learnStatusCommand() {
|
|
7052
6537
|
const installed = areLearningHooksInstalled();
|
|
7053
6538
|
const state = readState2();
|
|
7054
6539
|
const eventCount = getEventCount();
|
|
7055
|
-
console.log(
|
|
6540
|
+
console.log(chalk14.bold("Session Learning Status"));
|
|
7056
6541
|
console.log();
|
|
7057
6542
|
if (installed) {
|
|
7058
|
-
console.log(
|
|
6543
|
+
console.log(chalk14.green("\u2713") + " Learning hooks are " + chalk14.green("installed"));
|
|
7059
6544
|
} else {
|
|
7060
|
-
console.log(
|
|
7061
|
-
console.log(
|
|
6545
|
+
console.log(chalk14.dim("\u2717") + " Learning hooks are " + chalk14.yellow("not installed"));
|
|
6546
|
+
console.log(chalk14.dim(" Run `caliber learn install` to enable session learning."));
|
|
7062
6547
|
}
|
|
7063
6548
|
console.log();
|
|
7064
|
-
console.log(`Events recorded: ${
|
|
7065
|
-
console.log(`Total this session: ${
|
|
6549
|
+
console.log(`Events recorded: ${chalk14.cyan(String(eventCount))}`);
|
|
6550
|
+
console.log(`Total this session: ${chalk14.cyan(String(state.eventCount))}`);
|
|
7066
6551
|
if (state.lastAnalysisTimestamp) {
|
|
7067
|
-
console.log(`Last analysis: ${
|
|
6552
|
+
console.log(`Last analysis: ${chalk14.cyan(state.lastAnalysisTimestamp)}`);
|
|
7068
6553
|
} else {
|
|
7069
|
-
console.log(`Last analysis: ${
|
|
6554
|
+
console.log(`Last analysis: ${chalk14.dim("none")}`);
|
|
7070
6555
|
}
|
|
7071
6556
|
const learnedSection = readLearnedSection();
|
|
7072
6557
|
if (learnedSection) {
|
|
7073
6558
|
const lineCount = learnedSection.split("\n").filter(Boolean).length;
|
|
7074
6559
|
console.log(`
|
|
7075
|
-
Learned items in CLAUDE.md: ${
|
|
6560
|
+
Learned items in CLAUDE.md: ${chalk14.cyan(String(lineCount))}`);
|
|
7076
6561
|
}
|
|
7077
6562
|
}
|
|
7078
6563
|
|
|
7079
6564
|
// src/cli.ts
|
|
7080
|
-
var __dirname =
|
|
6565
|
+
var __dirname = path21.dirname(fileURLToPath(import.meta.url));
|
|
7081
6566
|
var pkg = JSON.parse(
|
|
7082
|
-
|
|
6567
|
+
fs27.readFileSync(path21.resolve(__dirname, "..", "package.json"), "utf-8")
|
|
7083
6568
|
);
|
|
7084
6569
|
var program = new Command();
|
|
7085
6570
|
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
|
|
@@ -7112,22 +6597,22 @@ learn.command("remove").description("Remove learning hooks from .claude/settings
|
|
|
7112
6597
|
learn.command("status").description("Show learning system status").action(learnStatusCommand);
|
|
7113
6598
|
|
|
7114
6599
|
// src/utils/version-check.ts
|
|
7115
|
-
import
|
|
7116
|
-
import
|
|
6600
|
+
import fs28 from "fs";
|
|
6601
|
+
import path22 from "path";
|
|
7117
6602
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7118
6603
|
import { execSync as execSync9 } from "child_process";
|
|
7119
|
-
import
|
|
7120
|
-
import
|
|
6604
|
+
import chalk15 from "chalk";
|
|
6605
|
+
import ora6 from "ora";
|
|
7121
6606
|
import confirm from "@inquirer/confirm";
|
|
7122
|
-
var __dirname_vc =
|
|
6607
|
+
var __dirname_vc = path22.dirname(fileURLToPath2(import.meta.url));
|
|
7123
6608
|
var pkg2 = JSON.parse(
|
|
7124
|
-
|
|
6609
|
+
fs28.readFileSync(path22.resolve(__dirname_vc, "..", "package.json"), "utf-8")
|
|
7125
6610
|
);
|
|
7126
6611
|
function getInstalledVersion() {
|
|
7127
6612
|
try {
|
|
7128
6613
|
const globalRoot = execSync9("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
7129
|
-
const pkgPath =
|
|
7130
|
-
return JSON.parse(
|
|
6614
|
+
const pkgPath = path22.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
6615
|
+
return JSON.parse(fs28.readFileSync(pkgPath, "utf-8")).version;
|
|
7131
6616
|
} catch {
|
|
7132
6617
|
return null;
|
|
7133
6618
|
}
|
|
@@ -7150,17 +6635,17 @@ async function checkForUpdates() {
|
|
|
7150
6635
|
const isInteractive = process.stdin.isTTY === true;
|
|
7151
6636
|
if (!isInteractive) {
|
|
7152
6637
|
console.log(
|
|
7153
|
-
|
|
6638
|
+
chalk15.yellow(
|
|
7154
6639
|
`
|
|
7155
6640
|
Update available: ${current} -> ${latest}
|
|
7156
|
-
Run ${
|
|
6641
|
+
Run ${chalk15.bold("npm install -g @rely-ai/caliber")} to upgrade.
|
|
7157
6642
|
`
|
|
7158
6643
|
)
|
|
7159
6644
|
);
|
|
7160
6645
|
return;
|
|
7161
6646
|
}
|
|
7162
6647
|
console.log(
|
|
7163
|
-
|
|
6648
|
+
chalk15.yellow(`
|
|
7164
6649
|
Update available: ${current} -> ${latest}`)
|
|
7165
6650
|
);
|
|
7166
6651
|
const shouldUpdate = await confirm({ message: "Would you like to update now? (Y/n)", default: true });
|
|
@@ -7168,7 +6653,7 @@ Update available: ${current} -> ${latest}`)
|
|
|
7168
6653
|
console.log();
|
|
7169
6654
|
return;
|
|
7170
6655
|
}
|
|
7171
|
-
const spinner =
|
|
6656
|
+
const spinner = ora6("Updating caliber...").start();
|
|
7172
6657
|
try {
|
|
7173
6658
|
execSync9(`npm install -g @rely-ai/caliber@${latest}`, {
|
|
7174
6659
|
stdio: "pipe",
|
|
@@ -7178,13 +6663,13 @@ Update available: ${current} -> ${latest}`)
|
|
|
7178
6663
|
const installed = getInstalledVersion();
|
|
7179
6664
|
if (installed !== latest) {
|
|
7180
6665
|
spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
|
|
7181
|
-
console.log(
|
|
6666
|
+
console.log(chalk15.yellow(`Run ${chalk15.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually.
|
|
7182
6667
|
`));
|
|
7183
6668
|
return;
|
|
7184
6669
|
}
|
|
7185
|
-
spinner.succeed(
|
|
6670
|
+
spinner.succeed(chalk15.green(`Updated to ${latest}`));
|
|
7186
6671
|
const args = process.argv.slice(2);
|
|
7187
|
-
console.log(
|
|
6672
|
+
console.log(chalk15.dim(`
|
|
7188
6673
|
Restarting: caliber ${args.join(" ")}
|
|
7189
6674
|
`));
|
|
7190
6675
|
execSync9(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
|
|
@@ -7197,11 +6682,11 @@ Restarting: caliber ${args.join(" ")}
|
|
|
7197
6682
|
if (err instanceof Error) {
|
|
7198
6683
|
const stderr = err.stderr;
|
|
7199
6684
|
const errMsg = stderr ? String(stderr).trim().split("\n").pop() : err.message.split("\n")[0];
|
|
7200
|
-
if (errMsg && !errMsg.includes("SIGTERM")) console.log(
|
|
6685
|
+
if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk15.dim(` ${errMsg}`));
|
|
7201
6686
|
}
|
|
7202
6687
|
console.log(
|
|
7203
|
-
|
|
7204
|
-
`Run ${
|
|
6688
|
+
chalk15.yellow(
|
|
6689
|
+
`Run ${chalk15.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually to upgrade.
|
|
7205
6690
|
`
|
|
7206
6691
|
)
|
|
7207
6692
|
);
|