@rely-ai/caliber 1.4.1 → 1.4.2
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 +631 -618
- 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
|
|
54
|
+
import fs28 from "fs";
|
|
55
55
|
import path22 from "path";
|
|
56
56
|
import { fileURLToPath } from "url";
|
|
57
57
|
|
|
58
58
|
// src/commands/onboard.ts
|
|
59
|
-
import
|
|
59
|
+
import chalk6 from "chalk";
|
|
60
60
|
import ora2 from "ora";
|
|
61
61
|
import readline4 from "readline";
|
|
62
|
-
import
|
|
62
|
+
import select3 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";
|
|
@@ -2163,6 +2163,12 @@ function cleanupStaging() {
|
|
|
2163
2163
|
}
|
|
2164
2164
|
}
|
|
2165
2165
|
|
|
2166
|
+
// src/utils/review.ts
|
|
2167
|
+
import chalk from "chalk";
|
|
2168
|
+
import fs14 from "fs";
|
|
2169
|
+
import select from "@inquirer/select";
|
|
2170
|
+
import { createTwoFilesPatch } from "diff";
|
|
2171
|
+
|
|
2166
2172
|
// src/utils/editor.ts
|
|
2167
2173
|
import { execSync as execSync4, spawn as spawn3 } from "child_process";
|
|
2168
2174
|
function commandExists(cmd) {
|
|
@@ -2201,11 +2207,206 @@ function openDiffsInEditor(editor, files) {
|
|
|
2201
2207
|
}
|
|
2202
2208
|
}
|
|
2203
2209
|
|
|
2204
|
-
// src/
|
|
2205
|
-
|
|
2210
|
+
// src/utils/review.ts
|
|
2211
|
+
async function promptWantsReview() {
|
|
2212
|
+
return select({
|
|
2213
|
+
message: "Would you like to review the diffs before deciding?",
|
|
2214
|
+
choices: [
|
|
2215
|
+
{ name: "Yes, show me the diffs", value: true },
|
|
2216
|
+
{ name: "No, continue", value: false }
|
|
2217
|
+
]
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
2220
|
+
async function promptReviewMethod() {
|
|
2221
|
+
const available = detectAvailableEditors();
|
|
2222
|
+
if (available.length === 1) return "terminal";
|
|
2223
|
+
const choices = available.map((method) => {
|
|
2224
|
+
switch (method) {
|
|
2225
|
+
case "cursor":
|
|
2226
|
+
return { name: "Cursor (diff view)", value: "cursor" };
|
|
2227
|
+
case "vscode":
|
|
2228
|
+
return { name: "VS Code (diff view)", value: "vscode" };
|
|
2229
|
+
case "terminal":
|
|
2230
|
+
return { name: "Terminal", value: "terminal" };
|
|
2231
|
+
}
|
|
2232
|
+
});
|
|
2233
|
+
return select({ message: "How would you like to review the changes?", choices });
|
|
2234
|
+
}
|
|
2235
|
+
async function openReview(method, stagedFiles) {
|
|
2236
|
+
if (method === "cursor" || method === "vscode") {
|
|
2237
|
+
openDiffsInEditor(method, stagedFiles.map((f) => ({
|
|
2238
|
+
originalPath: f.originalPath,
|
|
2239
|
+
proposedPath: f.proposedPath
|
|
2240
|
+
})));
|
|
2241
|
+
console.log(chalk.dim(" Diffs opened in your editor.\n"));
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
const fileInfos = stagedFiles.map((file) => {
|
|
2245
|
+
const proposed = fs14.readFileSync(file.proposedPath, "utf-8");
|
|
2246
|
+
const current = file.currentPath ? fs14.readFileSync(file.currentPath, "utf-8") : "";
|
|
2247
|
+
const patch = createTwoFilesPatch(
|
|
2248
|
+
file.isNew ? "/dev/null" : file.relativePath,
|
|
2249
|
+
file.relativePath,
|
|
2250
|
+
current,
|
|
2251
|
+
proposed
|
|
2252
|
+
);
|
|
2253
|
+
let added = 0, removed = 0;
|
|
2254
|
+
for (const line of patch.split("\n")) {
|
|
2255
|
+
if (line.startsWith("+") && !line.startsWith("+++")) added++;
|
|
2256
|
+
if (line.startsWith("-") && !line.startsWith("---")) removed++;
|
|
2257
|
+
}
|
|
2258
|
+
return {
|
|
2259
|
+
relativePath: file.relativePath,
|
|
2260
|
+
isNew: file.isNew,
|
|
2261
|
+
added,
|
|
2262
|
+
removed,
|
|
2263
|
+
lines: proposed.split("\n").length,
|
|
2264
|
+
patch
|
|
2265
|
+
};
|
|
2266
|
+
});
|
|
2267
|
+
await interactiveDiffExplorer(fileInfos);
|
|
2268
|
+
}
|
|
2269
|
+
async function interactiveDiffExplorer(files) {
|
|
2270
|
+
if (!process.stdin.isTTY) {
|
|
2271
|
+
for (const f of files) {
|
|
2272
|
+
const icon = f.isNew ? chalk.green("+") : chalk.yellow("~");
|
|
2273
|
+
const stats = f.isNew ? chalk.dim(`${f.lines} lines`) : `${chalk.green(`+${f.added}`)} ${chalk.red(`-${f.removed}`)}`;
|
|
2274
|
+
console.log(` ${icon} ${f.relativePath} ${stats}`);
|
|
2275
|
+
}
|
|
2276
|
+
console.log("");
|
|
2277
|
+
return;
|
|
2278
|
+
}
|
|
2279
|
+
const { stdin, stdout } = process;
|
|
2280
|
+
let cursor = 0;
|
|
2281
|
+
let viewing = null;
|
|
2282
|
+
let scrollOffset = 0;
|
|
2283
|
+
let lineCount = 0;
|
|
2284
|
+
function getTermHeight() {
|
|
2285
|
+
return (stdout.rows || 24) - 4;
|
|
2286
|
+
}
|
|
2287
|
+
function renderFileList() {
|
|
2288
|
+
const lines = [];
|
|
2289
|
+
lines.push(chalk.bold(" Review changes"));
|
|
2290
|
+
lines.push("");
|
|
2291
|
+
for (let i = 0; i < files.length; i++) {
|
|
2292
|
+
const f = files[i];
|
|
2293
|
+
const ptr = i === cursor ? chalk.cyan(">") : " ";
|
|
2294
|
+
const icon = f.isNew ? chalk.green("+") : chalk.yellow("~");
|
|
2295
|
+
const stats = f.isNew ? chalk.dim(`${f.lines} lines`) : `${chalk.green(`+${f.added}`)} ${chalk.red(`-${f.removed}`)}`;
|
|
2296
|
+
lines.push(` ${ptr} ${icon} ${f.relativePath} ${stats}`);
|
|
2297
|
+
}
|
|
2298
|
+
lines.push("");
|
|
2299
|
+
lines.push(chalk.dim(" \u2191\u2193 navigate \u23CE view diff q done"));
|
|
2300
|
+
return lines.join("\n");
|
|
2301
|
+
}
|
|
2302
|
+
function renderDiff(index) {
|
|
2303
|
+
const f = files[index];
|
|
2304
|
+
const lines = [];
|
|
2305
|
+
const header = f.isNew ? ` ${chalk.green("+")} ${f.relativePath} ${chalk.dim("(new file)")}` : ` ${chalk.yellow("~")} ${f.relativePath} ${chalk.green(`+${f.added}`)} ${chalk.red(`-${f.removed}`)}`;
|
|
2306
|
+
lines.push(header);
|
|
2307
|
+
lines.push(chalk.dim(" " + "\u2500".repeat(60)));
|
|
2308
|
+
const patchLines = f.patch.split("\n");
|
|
2309
|
+
const bodyLines = patchLines.slice(4);
|
|
2310
|
+
const maxVisible = getTermHeight() - 4;
|
|
2311
|
+
const visibleLines = bodyLines.slice(scrollOffset, scrollOffset + maxVisible);
|
|
2312
|
+
for (const line of visibleLines) {
|
|
2313
|
+
if (line.startsWith("+")) {
|
|
2314
|
+
lines.push(chalk.green(" " + line));
|
|
2315
|
+
} else if (line.startsWith("-")) {
|
|
2316
|
+
lines.push(chalk.red(" " + line));
|
|
2317
|
+
} else if (line.startsWith("@@")) {
|
|
2318
|
+
lines.push(chalk.cyan(" " + line));
|
|
2319
|
+
} else {
|
|
2320
|
+
lines.push(chalk.dim(" " + line));
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
const totalBody = bodyLines.length;
|
|
2324
|
+
if (totalBody > maxVisible) {
|
|
2325
|
+
const pct = Math.round((scrollOffset + maxVisible) / totalBody * 100);
|
|
2326
|
+
lines.push(chalk.dim(` \u2500\u2500 ${Math.min(pct, 100)}% \u2500\u2500`));
|
|
2327
|
+
}
|
|
2328
|
+
lines.push("");
|
|
2329
|
+
lines.push(chalk.dim(" \u2191\u2193 scroll \u23B5/esc back to file list"));
|
|
2330
|
+
return lines.join("\n");
|
|
2331
|
+
}
|
|
2332
|
+
function draw(initial) {
|
|
2333
|
+
if (!initial && lineCount > 0) {
|
|
2334
|
+
stdout.write(`\x1B[${lineCount}A`);
|
|
2335
|
+
}
|
|
2336
|
+
stdout.write("\x1B[0J");
|
|
2337
|
+
const output = viewing !== null ? renderDiff(viewing) : renderFileList();
|
|
2338
|
+
stdout.write(output + "\n");
|
|
2339
|
+
lineCount = output.split("\n").length;
|
|
2340
|
+
}
|
|
2341
|
+
return new Promise((resolve2) => {
|
|
2342
|
+
console.log("");
|
|
2343
|
+
draw(true);
|
|
2344
|
+
stdin.setRawMode(true);
|
|
2345
|
+
stdin.resume();
|
|
2346
|
+
stdin.setEncoding("utf8");
|
|
2347
|
+
function cleanup() {
|
|
2348
|
+
stdin.removeListener("data", onData);
|
|
2349
|
+
stdin.setRawMode(false);
|
|
2350
|
+
stdin.pause();
|
|
2351
|
+
}
|
|
2352
|
+
function onData(key) {
|
|
2353
|
+
if (viewing !== null) {
|
|
2354
|
+
const f = files[viewing];
|
|
2355
|
+
const totalBody = f.patch.split("\n").length - 4;
|
|
2356
|
+
const maxVisible = getTermHeight() - 4;
|
|
2357
|
+
switch (key) {
|
|
2358
|
+
case "\x1B[A":
|
|
2359
|
+
scrollOffset = Math.max(0, scrollOffset - 1);
|
|
2360
|
+
draw(false);
|
|
2361
|
+
break;
|
|
2362
|
+
case "\x1B[B":
|
|
2363
|
+
scrollOffset = Math.min(Math.max(0, totalBody - maxVisible), scrollOffset + 1);
|
|
2364
|
+
draw(false);
|
|
2365
|
+
break;
|
|
2366
|
+
case " ":
|
|
2367
|
+
case "\x1B":
|
|
2368
|
+
viewing = null;
|
|
2369
|
+
scrollOffset = 0;
|
|
2370
|
+
draw(false);
|
|
2371
|
+
break;
|
|
2372
|
+
case "q":
|
|
2373
|
+
case "":
|
|
2374
|
+
cleanup();
|
|
2375
|
+
console.log("");
|
|
2376
|
+
resolve2();
|
|
2377
|
+
break;
|
|
2378
|
+
}
|
|
2379
|
+
} else {
|
|
2380
|
+
switch (key) {
|
|
2381
|
+
case "\x1B[A":
|
|
2382
|
+
cursor = (cursor - 1 + files.length) % files.length;
|
|
2383
|
+
draw(false);
|
|
2384
|
+
break;
|
|
2385
|
+
case "\x1B[B":
|
|
2386
|
+
cursor = (cursor + 1) % files.length;
|
|
2387
|
+
draw(false);
|
|
2388
|
+
break;
|
|
2389
|
+
case "\r":
|
|
2390
|
+
case "\n":
|
|
2391
|
+
viewing = cursor;
|
|
2392
|
+
scrollOffset = 0;
|
|
2393
|
+
draw(false);
|
|
2394
|
+
break;
|
|
2395
|
+
case "q":
|
|
2396
|
+
case "":
|
|
2397
|
+
cleanup();
|
|
2398
|
+
console.log("");
|
|
2399
|
+
resolve2();
|
|
2400
|
+
break;
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
stdin.on("data", onData);
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2206
2407
|
|
|
2207
2408
|
// src/commands/setup-files.ts
|
|
2208
|
-
import
|
|
2409
|
+
import fs15 from "fs";
|
|
2209
2410
|
function buildSkillContent(skill) {
|
|
2210
2411
|
const frontmatter = `---
|
|
2211
2412
|
name: ${skill.name}
|
|
@@ -2253,7 +2454,7 @@ function collectSetupFiles(setup) {
|
|
|
2253
2454
|
}
|
|
2254
2455
|
}
|
|
2255
2456
|
}
|
|
2256
|
-
if (!
|
|
2457
|
+
if (!fs15.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
|
|
2257
2458
|
const agentRefs = [];
|
|
2258
2459
|
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
2259
2460
|
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
@@ -2272,24 +2473,24 @@ ${agentRefs.join(" ")}
|
|
|
2272
2473
|
}
|
|
2273
2474
|
|
|
2274
2475
|
// src/lib/hooks.ts
|
|
2275
|
-
import
|
|
2476
|
+
import fs16 from "fs";
|
|
2276
2477
|
import path12 from "path";
|
|
2277
2478
|
import { execSync as execSync5 } from "child_process";
|
|
2278
2479
|
var SETTINGS_PATH = path12.join(".claude", "settings.json");
|
|
2279
2480
|
var HOOK_COMMAND = "caliber refresh --quiet";
|
|
2280
2481
|
var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
|
|
2281
2482
|
function readSettings() {
|
|
2282
|
-
if (!
|
|
2483
|
+
if (!fs16.existsSync(SETTINGS_PATH)) return {};
|
|
2283
2484
|
try {
|
|
2284
|
-
return JSON.parse(
|
|
2485
|
+
return JSON.parse(fs16.readFileSync(SETTINGS_PATH, "utf-8"));
|
|
2285
2486
|
} catch {
|
|
2286
2487
|
return {};
|
|
2287
2488
|
}
|
|
2288
2489
|
}
|
|
2289
2490
|
function writeSettings(settings) {
|
|
2290
2491
|
const dir = path12.dirname(SETTINGS_PATH);
|
|
2291
|
-
if (!
|
|
2292
|
-
|
|
2492
|
+
if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
|
|
2493
|
+
fs16.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
2293
2494
|
}
|
|
2294
2495
|
function findHookIndex(sessionEnd) {
|
|
2295
2496
|
return sessionEnd.findIndex(
|
|
@@ -2358,8 +2559,8 @@ function getPreCommitPath() {
|
|
|
2358
2559
|
}
|
|
2359
2560
|
function isPreCommitHookInstalled() {
|
|
2360
2561
|
const hookPath = getPreCommitPath();
|
|
2361
|
-
if (!hookPath || !
|
|
2362
|
-
const content =
|
|
2562
|
+
if (!hookPath || !fs16.existsSync(hookPath)) return false;
|
|
2563
|
+
const content = fs16.readFileSync(hookPath, "utf-8");
|
|
2363
2564
|
return content.includes(PRECOMMIT_START);
|
|
2364
2565
|
}
|
|
2365
2566
|
function installPreCommitHook() {
|
|
@@ -2369,40 +2570,40 @@ function installPreCommitHook() {
|
|
|
2369
2570
|
const hookPath = getPreCommitPath();
|
|
2370
2571
|
if (!hookPath) return { installed: false, alreadyInstalled: false };
|
|
2371
2572
|
const hooksDir = path12.dirname(hookPath);
|
|
2372
|
-
if (!
|
|
2573
|
+
if (!fs16.existsSync(hooksDir)) fs16.mkdirSync(hooksDir, { recursive: true });
|
|
2373
2574
|
let content = "";
|
|
2374
|
-
if (
|
|
2375
|
-
content =
|
|
2575
|
+
if (fs16.existsSync(hookPath)) {
|
|
2576
|
+
content = fs16.readFileSync(hookPath, "utf-8");
|
|
2376
2577
|
if (!content.endsWith("\n")) content += "\n";
|
|
2377
2578
|
content += "\n" + PRECOMMIT_BLOCK + "\n";
|
|
2378
2579
|
} else {
|
|
2379
2580
|
content = "#!/bin/sh\n\n" + PRECOMMIT_BLOCK + "\n";
|
|
2380
2581
|
}
|
|
2381
|
-
|
|
2382
|
-
|
|
2582
|
+
fs16.writeFileSync(hookPath, content);
|
|
2583
|
+
fs16.chmodSync(hookPath, 493);
|
|
2383
2584
|
return { installed: true, alreadyInstalled: false };
|
|
2384
2585
|
}
|
|
2385
2586
|
function removePreCommitHook() {
|
|
2386
2587
|
const hookPath = getPreCommitPath();
|
|
2387
|
-
if (!hookPath || !
|
|
2588
|
+
if (!hookPath || !fs16.existsSync(hookPath)) {
|
|
2388
2589
|
return { removed: false, notFound: true };
|
|
2389
2590
|
}
|
|
2390
|
-
let content =
|
|
2591
|
+
let content = fs16.readFileSync(hookPath, "utf-8");
|
|
2391
2592
|
if (!content.includes(PRECOMMIT_START)) {
|
|
2392
2593
|
return { removed: false, notFound: true };
|
|
2393
2594
|
}
|
|
2394
2595
|
const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
|
|
2395
2596
|
content = content.replace(regex, "\n");
|
|
2396
2597
|
if (content.trim() === "#!/bin/sh" || content.trim() === "") {
|
|
2397
|
-
|
|
2598
|
+
fs16.unlinkSync(hookPath);
|
|
2398
2599
|
} else {
|
|
2399
|
-
|
|
2600
|
+
fs16.writeFileSync(hookPath, content);
|
|
2400
2601
|
}
|
|
2401
2602
|
return { removed: true, notFound: false };
|
|
2402
2603
|
}
|
|
2403
2604
|
|
|
2404
2605
|
// src/lib/learning-hooks.ts
|
|
2405
|
-
import
|
|
2606
|
+
import fs17 from "fs";
|
|
2406
2607
|
import path13 from "path";
|
|
2407
2608
|
var SETTINGS_PATH2 = path13.join(".claude", "settings.json");
|
|
2408
2609
|
var HOOK_CONFIGS = [
|
|
@@ -2423,17 +2624,17 @@ var HOOK_CONFIGS = [
|
|
|
2423
2624
|
}
|
|
2424
2625
|
];
|
|
2425
2626
|
function readSettings2() {
|
|
2426
|
-
if (!
|
|
2627
|
+
if (!fs17.existsSync(SETTINGS_PATH2)) return {};
|
|
2427
2628
|
try {
|
|
2428
|
-
return JSON.parse(
|
|
2629
|
+
return JSON.parse(fs17.readFileSync(SETTINGS_PATH2, "utf-8"));
|
|
2429
2630
|
} catch {
|
|
2430
2631
|
return {};
|
|
2431
2632
|
}
|
|
2432
2633
|
}
|
|
2433
2634
|
function writeSettings2(settings) {
|
|
2434
2635
|
const dir = path13.dirname(SETTINGS_PATH2);
|
|
2435
|
-
if (!
|
|
2436
|
-
|
|
2636
|
+
if (!fs17.existsSync(dir)) fs17.mkdirSync(dir, { recursive: true });
|
|
2637
|
+
fs17.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
|
|
2437
2638
|
}
|
|
2438
2639
|
function hasLearningHook(matchers, command) {
|
|
2439
2640
|
return matchers.some((entry) => entry.hooks?.some((h) => h.command === command));
|
|
@@ -2490,7 +2691,7 @@ function removeLearningHooks() {
|
|
|
2490
2691
|
|
|
2491
2692
|
// src/lib/state.ts
|
|
2492
2693
|
init_constants();
|
|
2493
|
-
import
|
|
2694
|
+
import fs18 from "fs";
|
|
2494
2695
|
import path14 from "path";
|
|
2495
2696
|
import { execSync as execSync6 } from "child_process";
|
|
2496
2697
|
var STATE_FILE = path14.join(CALIBER_DIR, ".caliber-state.json");
|
|
@@ -2504,8 +2705,8 @@ function normalizeTargetAgent(value) {
|
|
|
2504
2705
|
}
|
|
2505
2706
|
function readState() {
|
|
2506
2707
|
try {
|
|
2507
|
-
if (!
|
|
2508
|
-
const raw = JSON.parse(
|
|
2708
|
+
if (!fs18.existsSync(STATE_FILE)) return null;
|
|
2709
|
+
const raw = JSON.parse(fs18.readFileSync(STATE_FILE, "utf-8"));
|
|
2509
2710
|
if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
|
|
2510
2711
|
return raw;
|
|
2511
2712
|
} catch {
|
|
@@ -2513,10 +2714,10 @@ function readState() {
|
|
|
2513
2714
|
}
|
|
2514
2715
|
}
|
|
2515
2716
|
function writeState(state) {
|
|
2516
|
-
if (!
|
|
2517
|
-
|
|
2717
|
+
if (!fs18.existsSync(CALIBER_DIR)) {
|
|
2718
|
+
fs18.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
2518
2719
|
}
|
|
2519
|
-
|
|
2720
|
+
fs18.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
|
|
2520
2721
|
}
|
|
2521
2722
|
function getCurrentHeadSha() {
|
|
2522
2723
|
try {
|
|
@@ -2530,7 +2731,7 @@ function getCurrentHeadSha() {
|
|
|
2530
2731
|
}
|
|
2531
2732
|
|
|
2532
2733
|
// src/utils/spinner-messages.ts
|
|
2533
|
-
import
|
|
2734
|
+
import chalk2 from "chalk";
|
|
2534
2735
|
var GENERATION_MESSAGES = [
|
|
2535
2736
|
"Analyzing your project structure and dependencies...",
|
|
2536
2737
|
"Mapping out build commands and test workflows...",
|
|
@@ -2580,9 +2781,9 @@ var SpinnerMessages = class {
|
|
|
2580
2781
|
this.currentBaseMessage = this.messages[0];
|
|
2581
2782
|
this.updateSpinnerText();
|
|
2582
2783
|
if (this.showElapsedTime) {
|
|
2583
|
-
this.spinner.suffixText =
|
|
2784
|
+
this.spinner.suffixText = chalk2.dim(`(${this.formatElapsed()})`);
|
|
2584
2785
|
this.elapsedTimer = setInterval(() => {
|
|
2585
|
-
this.spinner.suffixText =
|
|
2786
|
+
this.spinner.suffixText = chalk2.dim(`(${this.formatElapsed()})`);
|
|
2586
2787
|
}, 1e3);
|
|
2587
2788
|
}
|
|
2588
2789
|
this.timer = setInterval(() => {
|
|
@@ -2617,13 +2818,13 @@ var SpinnerMessages = class {
|
|
|
2617
2818
|
};
|
|
2618
2819
|
|
|
2619
2820
|
// src/commands/interactive-provider-setup.ts
|
|
2620
|
-
import
|
|
2821
|
+
import chalk3 from "chalk";
|
|
2621
2822
|
import readline2 from "readline";
|
|
2622
|
-
import
|
|
2823
|
+
import select2 from "@inquirer/select";
|
|
2623
2824
|
function promptInput(question) {
|
|
2624
2825
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
2625
2826
|
return new Promise((resolve2) => {
|
|
2626
|
-
rl.question(
|
|
2827
|
+
rl.question(chalk3.cyan(`${question} `), (answer) => {
|
|
2627
2828
|
rl.close();
|
|
2628
2829
|
resolve2(answer.trim());
|
|
2629
2830
|
});
|
|
@@ -2638,7 +2839,7 @@ var PROVIDER_CHOICES = [
|
|
|
2638
2839
|
];
|
|
2639
2840
|
async function runInteractiveProviderSetup(options) {
|
|
2640
2841
|
const message = options?.selectMessage ?? "Select LLM provider";
|
|
2641
|
-
const provider = await
|
|
2842
|
+
const provider = await select2({
|
|
2642
2843
|
message,
|
|
2643
2844
|
choices: PROVIDER_CHOICES
|
|
2644
2845
|
});
|
|
@@ -2646,19 +2847,19 @@ async function runInteractiveProviderSetup(options) {
|
|
|
2646
2847
|
switch (provider) {
|
|
2647
2848
|
case "claude-cli": {
|
|
2648
2849
|
config.model = "default";
|
|
2649
|
-
console.log(
|
|
2850
|
+
console.log(chalk3.dim(" Run `claude` once and log in with your Pro/Max/Team account if you haven't."));
|
|
2650
2851
|
break;
|
|
2651
2852
|
}
|
|
2652
2853
|
case "cursor": {
|
|
2653
2854
|
config.model = "default";
|
|
2654
|
-
console.log(
|
|
2855
|
+
console.log(chalk3.dim(" Run `agent login` if you haven't, or set CURSOR_API_KEY."));
|
|
2655
2856
|
break;
|
|
2656
2857
|
}
|
|
2657
2858
|
case "anthropic": {
|
|
2658
|
-
console.log(
|
|
2859
|
+
console.log(chalk3.dim(" Get a key at https://console.anthropic.com (same account as Claude Pro/Team/Max)."));
|
|
2659
2860
|
config.apiKey = await promptInput("Anthropic API key:");
|
|
2660
2861
|
if (!config.apiKey) {
|
|
2661
|
-
console.log(
|
|
2862
|
+
console.log(chalk3.red("API key is required."));
|
|
2662
2863
|
throw new Error("__exit__");
|
|
2663
2864
|
}
|
|
2664
2865
|
config.model = await promptInput(`Model (default: ${DEFAULT_MODELS.anthropic}):`) || DEFAULT_MODELS.anthropic;
|
|
@@ -2667,7 +2868,7 @@ async function runInteractiveProviderSetup(options) {
|
|
|
2667
2868
|
case "vertex": {
|
|
2668
2869
|
config.vertexProjectId = await promptInput("GCP Project ID:");
|
|
2669
2870
|
if (!config.vertexProjectId) {
|
|
2670
|
-
console.log(
|
|
2871
|
+
console.log(chalk3.red("Project ID is required."));
|
|
2671
2872
|
throw new Error("__exit__");
|
|
2672
2873
|
}
|
|
2673
2874
|
config.vertexRegion = await promptInput("Region (default: us-east5):") || "us-east5";
|
|
@@ -2678,7 +2879,7 @@ async function runInteractiveProviderSetup(options) {
|
|
|
2678
2879
|
case "openai": {
|
|
2679
2880
|
config.apiKey = await promptInput("API key:");
|
|
2680
2881
|
if (!config.apiKey) {
|
|
2681
|
-
console.log(
|
|
2882
|
+
console.log(chalk3.red("API key is required."));
|
|
2682
2883
|
throw new Error("__exit__");
|
|
2683
2884
|
}
|
|
2684
2885
|
config.baseUrl = await promptInput("Base URL (leave empty for OpenAI, or enter custom endpoint):") || void 0;
|
|
@@ -3737,22 +3938,22 @@ function checkBonus(dir) {
|
|
|
3737
3938
|
|
|
3738
3939
|
// src/scoring/dismissed.ts
|
|
3739
3940
|
init_constants();
|
|
3740
|
-
import
|
|
3941
|
+
import fs19 from "fs";
|
|
3741
3942
|
import path15 from "path";
|
|
3742
3943
|
var DISMISSED_FILE = path15.join(CALIBER_DIR, "dismissed-checks.json");
|
|
3743
3944
|
function readDismissedChecks() {
|
|
3744
3945
|
try {
|
|
3745
|
-
if (!
|
|
3746
|
-
return JSON.parse(
|
|
3946
|
+
if (!fs19.existsSync(DISMISSED_FILE)) return [];
|
|
3947
|
+
return JSON.parse(fs19.readFileSync(DISMISSED_FILE, "utf-8"));
|
|
3747
3948
|
} catch {
|
|
3748
3949
|
return [];
|
|
3749
3950
|
}
|
|
3750
3951
|
}
|
|
3751
3952
|
function writeDismissedChecks(checks) {
|
|
3752
|
-
if (!
|
|
3753
|
-
|
|
3953
|
+
if (!fs19.existsSync(CALIBER_DIR)) {
|
|
3954
|
+
fs19.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
3754
3955
|
}
|
|
3755
|
-
|
|
3956
|
+
fs19.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
|
|
3756
3957
|
}
|
|
3757
3958
|
function getDismissedIds() {
|
|
3758
3959
|
return new Set(readDismissedChecks().map((c) => c.id));
|
|
@@ -3816,7 +4017,7 @@ function computeLocalScore(dir, targetAgent) {
|
|
|
3816
4017
|
}
|
|
3817
4018
|
|
|
3818
4019
|
// src/scoring/display.ts
|
|
3819
|
-
import
|
|
4020
|
+
import chalk4 from "chalk";
|
|
3820
4021
|
var AGENT_DISPLAY_NAMES = {
|
|
3821
4022
|
claude: "Claude Code",
|
|
3822
4023
|
cursor: "Cursor",
|
|
@@ -3834,31 +4035,31 @@ var CATEGORY_ORDER = ["existence", "quality", "coverage", "accuracy", "freshness
|
|
|
3834
4035
|
function gradeColor(grade) {
|
|
3835
4036
|
switch (grade) {
|
|
3836
4037
|
case "A":
|
|
3837
|
-
return
|
|
4038
|
+
return chalk4.green;
|
|
3838
4039
|
case "B":
|
|
3839
|
-
return
|
|
4040
|
+
return chalk4.greenBright;
|
|
3840
4041
|
case "C":
|
|
3841
|
-
return
|
|
4042
|
+
return chalk4.yellow;
|
|
3842
4043
|
case "D":
|
|
3843
|
-
return
|
|
4044
|
+
return chalk4.hex("#f97316");
|
|
3844
4045
|
case "F":
|
|
3845
|
-
return
|
|
4046
|
+
return chalk4.red;
|
|
3846
4047
|
default:
|
|
3847
|
-
return
|
|
4048
|
+
return chalk4.white;
|
|
3848
4049
|
}
|
|
3849
4050
|
}
|
|
3850
4051
|
function progressBar(score, max, width = 40) {
|
|
3851
4052
|
const filled = Math.round(score / max * width);
|
|
3852
4053
|
const empty = width - filled;
|
|
3853
|
-
const bar =
|
|
4054
|
+
const bar = chalk4.hex("#f97316")("\u2593".repeat(filled)) + chalk4.gray("\u2591".repeat(empty));
|
|
3854
4055
|
return bar;
|
|
3855
4056
|
}
|
|
3856
4057
|
function formatCheck(check) {
|
|
3857
|
-
const icon = check.passed ?
|
|
3858
|
-
const points = check.passed ?
|
|
3859
|
-
const name = check.passed ?
|
|
3860
|
-
const detail = check.detail ?
|
|
3861
|
-
const suggestion = !check.passed && check.suggestion ?
|
|
4058
|
+
const icon = check.passed ? chalk4.green("\u2713") : check.earnedPoints < 0 ? chalk4.red("\u2717") : chalk4.gray("\u2717");
|
|
4059
|
+
const points = check.passed ? chalk4.green(`+${check.earnedPoints}`.padStart(4)) : check.earnedPoints < 0 ? chalk4.red(`${check.earnedPoints}`.padStart(4)) : chalk4.gray(" \u2014");
|
|
4060
|
+
const name = check.passed ? chalk4.white(check.name) : chalk4.gray(check.name);
|
|
4061
|
+
const detail = check.detail ? chalk4.gray(` (${check.detail})`) : "";
|
|
4062
|
+
const suggestion = !check.passed && check.suggestion ? chalk4.gray(`
|
|
3862
4063
|
\u2192 ${check.suggestion}`) : "";
|
|
3863
4064
|
return ` ${icon} ${name.padEnd(38)}${points}${detail}${suggestion}`;
|
|
3864
4065
|
}
|
|
@@ -3866,19 +4067,19 @@ function displayScore(result) {
|
|
|
3866
4067
|
const gc = gradeColor(result.grade);
|
|
3867
4068
|
const agentLabel = result.targetAgent.map((a) => AGENT_DISPLAY_NAMES[a] || a).join(" + ");
|
|
3868
4069
|
console.log("");
|
|
3869
|
-
console.log(
|
|
4070
|
+
console.log(chalk4.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
3870
4071
|
console.log("");
|
|
3871
|
-
console.log(` ${
|
|
4072
|
+
console.log(` ${chalk4.bold("Agent Config Score")} ${gc(chalk4.bold(`${result.score} / ${result.maxScore}`))} Grade ${gc(chalk4.bold(result.grade))}`);
|
|
3872
4073
|
console.log(` ${progressBar(result.score, result.maxScore)}`);
|
|
3873
|
-
console.log(
|
|
4074
|
+
console.log(chalk4.dim(` Target: ${agentLabel}`));
|
|
3874
4075
|
console.log("");
|
|
3875
|
-
console.log(
|
|
4076
|
+
console.log(chalk4.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
3876
4077
|
console.log("");
|
|
3877
4078
|
for (const category of CATEGORY_ORDER) {
|
|
3878
4079
|
const summary = result.categories[category];
|
|
3879
4080
|
const categoryChecks = result.checks.filter((c) => c.category === category);
|
|
3880
4081
|
console.log(
|
|
3881
|
-
|
|
4082
|
+
chalk4.gray(` ${CATEGORY_LABELS[category]}`) + chalk4.gray(" ".repeat(Math.max(1, 45 - CATEGORY_LABELS[category].length))) + chalk4.white(`${summary.earned}`) + chalk4.gray(` / ${summary.max}`)
|
|
3882
4083
|
);
|
|
3883
4084
|
for (const check of categoryChecks) {
|
|
3884
4085
|
console.log(formatCheck(check));
|
|
@@ -3891,48 +4092,48 @@ function displayScoreSummary(result) {
|
|
|
3891
4092
|
const agentLabel = result.targetAgent.map((a) => AGENT_DISPLAY_NAMES[a] || a).join(" + ");
|
|
3892
4093
|
console.log("");
|
|
3893
4094
|
console.log(
|
|
3894
|
-
|
|
4095
|
+
chalk4.gray(" ") + gc(`${result.score}/${result.maxScore}`) + chalk4.gray(` (Grade ${result.grade})`) + chalk4.gray(` \xB7 ${agentLabel}`) + chalk4.gray(` \xB7 ${progressBar(result.score, result.maxScore, 20)}`)
|
|
3895
4096
|
);
|
|
3896
4097
|
const failing = result.checks.filter((c) => !c.passed);
|
|
3897
4098
|
if (failing.length > 0) {
|
|
3898
4099
|
const shown = failing.slice(0, 5);
|
|
3899
4100
|
for (const check of shown) {
|
|
3900
|
-
console.log(
|
|
4101
|
+
console.log(chalk4.gray(` \u2717 ${check.name}`));
|
|
3901
4102
|
}
|
|
3902
4103
|
const remaining = failing.length - shown.length;
|
|
3903
4104
|
const moreText = remaining > 0 ? ` (+${remaining} more)` : "";
|
|
3904
|
-
console.log(
|
|
3905
|
-
Run ${
|
|
4105
|
+
console.log(chalk4.dim(`
|
|
4106
|
+
Run ${chalk4.hex("#83D1EB")("caliber score")} for details.${moreText}`));
|
|
3906
4107
|
}
|
|
3907
4108
|
console.log("");
|
|
3908
4109
|
}
|
|
3909
4110
|
function displayScoreDelta(before, after) {
|
|
3910
4111
|
const delta = after.score - before.score;
|
|
3911
4112
|
const deltaStr = delta >= 0 ? `+${delta}` : `${delta}`;
|
|
3912
|
-
const deltaColor = delta >= 0 ?
|
|
4113
|
+
const deltaColor = delta >= 0 ? chalk4.green : chalk4.red;
|
|
3913
4114
|
const beforeGc = gradeColor(before.grade);
|
|
3914
4115
|
const afterGc = gradeColor(after.grade);
|
|
3915
4116
|
console.log("");
|
|
3916
|
-
console.log(
|
|
4117
|
+
console.log(chalk4.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
3917
4118
|
console.log("");
|
|
3918
4119
|
console.log(
|
|
3919
|
-
` Score: ${beforeGc(`${before.score}`)} ${
|
|
4120
|
+
` Score: ${beforeGc(`${before.score}`)} ${chalk4.gray("\u2192")} ${afterGc(`${after.score}`)} ${deltaColor(deltaStr + " pts")} ${beforeGc(before.grade)} ${chalk4.gray("\u2192")} ${afterGc(after.grade)}`
|
|
3920
4121
|
);
|
|
3921
|
-
console.log(` ${progressBar(before.score, before.maxScore, 19)} ${
|
|
4122
|
+
console.log(` ${progressBar(before.score, before.maxScore, 19)} ${chalk4.gray("\u2192")} ${progressBar(after.score, after.maxScore, 19)}`);
|
|
3922
4123
|
console.log("");
|
|
3923
|
-
console.log(
|
|
4124
|
+
console.log(chalk4.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
3924
4125
|
console.log("");
|
|
3925
4126
|
const improved = after.checks.filter((ac) => {
|
|
3926
4127
|
const bc = before.checks.find((b) => b.id === ac.id);
|
|
3927
4128
|
return bc && ac.earnedPoints > bc.earnedPoints;
|
|
3928
4129
|
});
|
|
3929
4130
|
if (improved.length > 0) {
|
|
3930
|
-
console.log(
|
|
4131
|
+
console.log(chalk4.gray(" What improved:"));
|
|
3931
4132
|
for (const check of improved) {
|
|
3932
4133
|
const bc = before.checks.find((b) => b.id === check.id);
|
|
3933
4134
|
const gain = check.earnedPoints - bc.earnedPoints;
|
|
3934
4135
|
console.log(
|
|
3935
|
-
|
|
4136
|
+
chalk4.green(" +") + chalk4.white(` ${check.name.padEnd(50)}`) + chalk4.green(`+${gain}`)
|
|
3936
4137
|
);
|
|
3937
4138
|
}
|
|
3938
4139
|
console.log("");
|
|
@@ -3940,10 +4141,10 @@ function displayScoreDelta(before, after) {
|
|
|
3940
4141
|
}
|
|
3941
4142
|
|
|
3942
4143
|
// src/mcp/index.ts
|
|
3943
|
-
import
|
|
4144
|
+
import chalk5 from "chalk";
|
|
3944
4145
|
import ora from "ora";
|
|
3945
4146
|
import readline3 from "readline";
|
|
3946
|
-
import
|
|
4147
|
+
import fs20 from "fs";
|
|
3947
4148
|
import path16 from "path";
|
|
3948
4149
|
|
|
3949
4150
|
// src/mcp/search.ts
|
|
@@ -4218,38 +4419,38 @@ ${truncated}`,
|
|
|
4218
4419
|
|
|
4219
4420
|
// src/mcp/index.ts
|
|
4220
4421
|
async function discoverAndInstallMcps(targetAgent, fingerprint, dir) {
|
|
4221
|
-
console.log(
|
|
4422
|
+
console.log(chalk5.hex("#6366f1").bold("\n MCP Server Discovery\n"));
|
|
4222
4423
|
const toolDeps = fingerprint.tools;
|
|
4223
4424
|
if (toolDeps.length === 0) {
|
|
4224
|
-
console.log(
|
|
4425
|
+
console.log(chalk5.dim(" No external tools or services detected \u2014 skipping MCP discovery"));
|
|
4225
4426
|
return { installed: 0, names: [] };
|
|
4226
4427
|
}
|
|
4227
4428
|
const spinner = ora(`Searching MCP servers for ${toolDeps.length} detected tool${toolDeps.length === 1 ? "" : "s"}...`).start();
|
|
4228
|
-
console.log(
|
|
4429
|
+
console.log(chalk5.dim(` Detected: ${toolDeps.join(", ")}`));
|
|
4229
4430
|
const existingMcps = getExistingMcpNames(fingerprint, targetAgent);
|
|
4230
4431
|
const filteredDeps = toolDeps.filter((d) => {
|
|
4231
4432
|
const lower = d.toLowerCase();
|
|
4232
4433
|
return !existingMcps.some((name) => name.includes(lower) || lower.includes(name));
|
|
4233
4434
|
});
|
|
4234
4435
|
if (filteredDeps.length === 0) {
|
|
4235
|
-
spinner.succeed(
|
|
4436
|
+
spinner.succeed(chalk5.dim("All detected tools already have MCP servers configured"));
|
|
4236
4437
|
return { installed: 0, names: [] };
|
|
4237
4438
|
}
|
|
4238
4439
|
const candidates = await searchAllMcpSources(filteredDeps);
|
|
4239
4440
|
if (candidates.length === 0) {
|
|
4240
|
-
spinner.succeed(
|
|
4441
|
+
spinner.succeed(chalk5.dim("No MCP servers found for detected tools"));
|
|
4241
4442
|
return { installed: 0, names: [] };
|
|
4242
4443
|
}
|
|
4243
4444
|
spinner.succeed(`Found ${candidates.length} candidate${candidates.length === 1 ? "" : "s"} for ${filteredDeps.join(", ")}`);
|
|
4244
4445
|
const scoreSpinner = ora("Scoring MCP candidates...").start();
|
|
4245
4446
|
const scored = await validateAndScore(candidates, filteredDeps);
|
|
4246
4447
|
if (scored.length === 0) {
|
|
4247
|
-
scoreSpinner.succeed(
|
|
4248
|
-
console.log(
|
|
4448
|
+
scoreSpinner.succeed(chalk5.dim("No quality MCP servers passed validation"));
|
|
4449
|
+
console.log(chalk5.dim(` Candidates checked: ${candidates.map((c) => c.name).join(", ")}`));
|
|
4249
4450
|
return { installed: 0, names: [] };
|
|
4250
4451
|
}
|
|
4251
4452
|
scoreSpinner.succeed(`${scored.length} quality MCP server${scored.length === 1 ? "" : "s"} found`);
|
|
4252
|
-
console.log(
|
|
4453
|
+
console.log(chalk5.dim(` Scored: ${scored.map((c) => `${c.name} (${c.score})`).join(", ")}`));
|
|
4253
4454
|
const selected = await interactiveSelect(scored);
|
|
4254
4455
|
if (!selected || selected.length === 0) {
|
|
4255
4456
|
return { installed: 0, names: [] };
|
|
@@ -4257,18 +4458,18 @@ async function discoverAndInstallMcps(targetAgent, fingerprint, dir) {
|
|
|
4257
4458
|
const mcpServers = {};
|
|
4258
4459
|
const installedNames = [];
|
|
4259
4460
|
for (const mcp of selected) {
|
|
4260
|
-
console.log(
|
|
4461
|
+
console.log(chalk5.bold(`
|
|
4261
4462
|
Configuring ${mcp.name}...`));
|
|
4262
4463
|
const readme = await fetchReadme(mcp.repoFullName);
|
|
4263
4464
|
if (!readme) {
|
|
4264
|
-
console.log(
|
|
4265
|
-
console.log(
|
|
4465
|
+
console.log(chalk5.yellow(` Could not fetch README for ${mcp.repoFullName} \u2014 skipping`));
|
|
4466
|
+
console.log(chalk5.dim(` Manual setup: ${mcp.url}`));
|
|
4266
4467
|
continue;
|
|
4267
4468
|
}
|
|
4268
4469
|
const config = await extractMcpConfig(readme, mcp.name);
|
|
4269
4470
|
if (!config || !config.command) {
|
|
4270
|
-
console.log(
|
|
4271
|
-
console.log(
|
|
4471
|
+
console.log(chalk5.yellow(` Could not extract config for ${mcp.name} \u2014 skipping`));
|
|
4472
|
+
console.log(chalk5.dim(` Manual setup: ${mcp.url}`));
|
|
4272
4473
|
continue;
|
|
4273
4474
|
}
|
|
4274
4475
|
const env = {};
|
|
@@ -4286,7 +4487,7 @@ async function discoverAndInstallMcps(targetAgent, fingerprint, dir) {
|
|
|
4286
4487
|
if (Object.keys(env).length > 0) serverConfig.env = env;
|
|
4287
4488
|
mcpServers[mcp.name] = serverConfig;
|
|
4288
4489
|
installedNames.push(mcp.name);
|
|
4289
|
-
console.log(` ${
|
|
4490
|
+
console.log(` ${chalk5.green("\u2713")} ${mcp.name} configured`);
|
|
4290
4491
|
}
|
|
4291
4492
|
if (installedNames.length === 0) {
|
|
4292
4493
|
return { installed: 0, names: [] };
|
|
@@ -4296,7 +4497,7 @@ async function discoverAndInstallMcps(targetAgent, fingerprint, dir) {
|
|
|
4296
4497
|
}
|
|
4297
4498
|
if (targetAgent.includes("cursor")) {
|
|
4298
4499
|
const cursorDir = path16.join(dir, ".cursor");
|
|
4299
|
-
if (!
|
|
4500
|
+
if (!fs20.existsSync(cursorDir)) fs20.mkdirSync(cursorDir, { recursive: true });
|
|
4300
4501
|
writeMcpJson(path16.join(cursorDir, "mcp.json"), mcpServers);
|
|
4301
4502
|
}
|
|
4302
4503
|
return { installed: installedNames.length, names: installedNames };
|
|
@@ -4304,14 +4505,14 @@ async function discoverAndInstallMcps(targetAgent, fingerprint, dir) {
|
|
|
4304
4505
|
function writeMcpJson(filePath, mcpServers) {
|
|
4305
4506
|
let existing = {};
|
|
4306
4507
|
try {
|
|
4307
|
-
if (
|
|
4308
|
-
const parsed = JSON.parse(
|
|
4508
|
+
if (fs20.existsSync(filePath)) {
|
|
4509
|
+
const parsed = JSON.parse(fs20.readFileSync(filePath, "utf-8"));
|
|
4309
4510
|
if (parsed.mcpServers) existing = parsed.mcpServers;
|
|
4310
4511
|
}
|
|
4311
4512
|
} catch {
|
|
4312
4513
|
}
|
|
4313
4514
|
const merged = { ...existing, ...mcpServers };
|
|
4314
|
-
|
|
4515
|
+
fs20.writeFileSync(filePath, JSON.stringify({ mcpServers: merged }, null, 2) + "\n");
|
|
4315
4516
|
}
|
|
4316
4517
|
function getExistingMcpNames(fingerprint, targetAgent) {
|
|
4317
4518
|
const names = [];
|
|
@@ -4330,7 +4531,7 @@ function getExistingMcpNames(fingerprint, targetAgent) {
|
|
|
4330
4531
|
function promptInput2(question) {
|
|
4331
4532
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
4332
4533
|
return new Promise((resolve2) => {
|
|
4333
|
-
rl.question(
|
|
4534
|
+
rl.question(chalk5.cyan(`${question}: `), (answer) => {
|
|
4334
4535
|
rl.close();
|
|
4335
4536
|
resolve2(answer.trim());
|
|
4336
4537
|
});
|
|
@@ -4338,10 +4539,10 @@ function promptInput2(question) {
|
|
|
4338
4539
|
}
|
|
4339
4540
|
async function interactiveSelect(candidates) {
|
|
4340
4541
|
if (!process.stdin.isTTY) {
|
|
4341
|
-
console.log(
|
|
4542
|
+
console.log(chalk5.bold("\n Available MCP servers:\n"));
|
|
4342
4543
|
for (const c of candidates) {
|
|
4343
|
-
const vendorTag = c.vendor ?
|
|
4344
|
-
console.log(` ${String(c.score).padStart(3)} ${c.name}${vendorTag} ${
|
|
4544
|
+
const vendorTag = c.vendor ? chalk5.blue(" (vendor)") : "";
|
|
4545
|
+
console.log(` ${String(c.score).padStart(3)} ${c.name}${vendorTag} ${chalk5.dim(c.reason)}`);
|
|
4345
4546
|
}
|
|
4346
4547
|
console.log("");
|
|
4347
4548
|
return null;
|
|
@@ -4352,18 +4553,18 @@ async function interactiveSelect(candidates) {
|
|
|
4352
4553
|
let lineCount = 0;
|
|
4353
4554
|
function render() {
|
|
4354
4555
|
const lines = [];
|
|
4355
|
-
lines.push(
|
|
4556
|
+
lines.push(chalk5.bold(" Select MCP servers to install:"));
|
|
4356
4557
|
lines.push("");
|
|
4357
4558
|
for (let i = 0; i < candidates.length; i++) {
|
|
4358
4559
|
const c = candidates[i];
|
|
4359
|
-
const check = selected.has(i) ?
|
|
4360
|
-
const ptr = i === cursor ?
|
|
4361
|
-
const scoreColor = c.score >= 90 ?
|
|
4362
|
-
const vendorTag = c.vendor ?
|
|
4363
|
-
lines.push(` ${ptr} ${check} ${scoreColor(String(c.score).padStart(3))} ${c.name}${vendorTag} ${
|
|
4560
|
+
const check = selected.has(i) ? chalk5.green("[x]") : "[ ]";
|
|
4561
|
+
const ptr = i === cursor ? chalk5.cyan(">") : " ";
|
|
4562
|
+
const scoreColor = c.score >= 90 ? chalk5.green : c.score >= 70 ? chalk5.yellow : chalk5.dim;
|
|
4563
|
+
const vendorTag = c.vendor ? chalk5.blue(" (vendor)") : "";
|
|
4564
|
+
lines.push(` ${ptr} ${check} ${scoreColor(String(c.score).padStart(3))} ${c.name}${vendorTag} ${chalk5.dim(c.reason.slice(0, 40))}`);
|
|
4364
4565
|
}
|
|
4365
4566
|
lines.push("");
|
|
4366
|
-
lines.push(
|
|
4567
|
+
lines.push(chalk5.dim(" \u2191\u2193 navigate \u23B5 toggle a all n none \u23CE install q skip"));
|
|
4367
4568
|
return lines.join("\n");
|
|
4368
4569
|
}
|
|
4369
4570
|
function draw(initial) {
|
|
@@ -4412,7 +4613,7 @@ async function interactiveSelect(candidates) {
|
|
|
4412
4613
|
case "\n":
|
|
4413
4614
|
cleanup();
|
|
4414
4615
|
if (selected.size === 0) {
|
|
4415
|
-
console.log(
|
|
4616
|
+
console.log(chalk5.dim("\n No MCP servers selected.\n"));
|
|
4416
4617
|
resolve2(null);
|
|
4417
4618
|
} else {
|
|
4418
4619
|
resolve2(Array.from(selected).sort().map((i) => candidates[i]));
|
|
@@ -4422,7 +4623,7 @@ async function interactiveSelect(candidates) {
|
|
|
4422
4623
|
case "\x1B":
|
|
4423
4624
|
case "":
|
|
4424
4625
|
cleanup();
|
|
4425
|
-
console.log(
|
|
4626
|
+
console.log(chalk5.dim("\n Skipped MCP server installation.\n"));
|
|
4426
4627
|
resolve2(null);
|
|
4427
4628
|
break;
|
|
4428
4629
|
}
|
|
@@ -4433,8 +4634,8 @@ async function interactiveSelect(candidates) {
|
|
|
4433
4634
|
|
|
4434
4635
|
// src/commands/onboard.ts
|
|
4435
4636
|
async function initCommand(options) {
|
|
4436
|
-
const brand =
|
|
4437
|
-
const title =
|
|
4637
|
+
const brand = chalk6.hex("#EB9D83");
|
|
4638
|
+
const title = chalk6.hex("#83D1EB");
|
|
4438
4639
|
console.log(brand.bold(`
|
|
4439
4640
|
\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
|
|
4440
4641
|
\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
|
|
@@ -4443,20 +4644,20 @@ async function initCommand(options) {
|
|
|
4443
4644
|
\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
|
|
4444
4645
|
\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
|
|
4445
4646
|
`));
|
|
4446
|
-
console.log(
|
|
4647
|
+
console.log(chalk6.dim(" Onboard your project for AI-assisted development\n"));
|
|
4447
4648
|
console.log(title.bold(" Welcome to Caliber\n"));
|
|
4448
|
-
console.log(
|
|
4449
|
-
console.log(
|
|
4649
|
+
console.log(chalk6.dim(" Caliber analyzes your codebase and creates tailored config files"));
|
|
4650
|
+
console.log(chalk6.dim(" so your AI coding agents understand your project from day one.\n"));
|
|
4450
4651
|
console.log(title.bold(" How onboarding works:\n"));
|
|
4451
|
-
console.log(
|
|
4452
|
-
console.log(
|
|
4453
|
-
console.log(
|
|
4454
|
-
console.log(
|
|
4455
|
-
console.log(
|
|
4652
|
+
console.log(chalk6.dim(" 1. Connect Set up your LLM provider"));
|
|
4653
|
+
console.log(chalk6.dim(" 2. Discover Analyze your code, dependencies, and structure"));
|
|
4654
|
+
console.log(chalk6.dim(" 3. Generate Create config files tailored to your project"));
|
|
4655
|
+
console.log(chalk6.dim(" 4. Review Preview, refine, and apply the changes"));
|
|
4656
|
+
console.log(chalk6.dim(" 5. Enhance Discover MCP servers for your tools\n"));
|
|
4456
4657
|
console.log(title.bold(" Step 1/5 \u2014 Connect your LLM\n"));
|
|
4457
4658
|
let config = loadConfig();
|
|
4458
4659
|
if (!config) {
|
|
4459
|
-
console.log(
|
|
4660
|
+
console.log(chalk6.dim(" No LLM provider set yet. Choose how to run Caliber:\n"));
|
|
4460
4661
|
try {
|
|
4461
4662
|
await runInteractiveProviderSetup({
|
|
4462
4663
|
selectMessage: "How do you want to use Caliber? (choose LLM provider)"
|
|
@@ -4467,23 +4668,23 @@ async function initCommand(options) {
|
|
|
4467
4668
|
}
|
|
4468
4669
|
config = loadConfig();
|
|
4469
4670
|
if (!config) {
|
|
4470
|
-
console.log(
|
|
4671
|
+
console.log(chalk6.red(" Setup was cancelled or failed.\n"));
|
|
4471
4672
|
throw new Error("__exit__");
|
|
4472
4673
|
}
|
|
4473
|
-
console.log(
|
|
4674
|
+
console.log(chalk6.green(" \u2713 Provider saved. Let's continue.\n"));
|
|
4474
4675
|
}
|
|
4475
4676
|
const displayModel = config.model === "default" && config.provider === "claude-cli" ? process.env.ANTHROPIC_MODEL || "default (inherited from Claude Code)" : config.model;
|
|
4476
4677
|
const fastModel = getFastModel();
|
|
4477
4678
|
const modelLine = fastModel ? ` Provider: ${config.provider} | Model: ${displayModel} | Scan: ${fastModel}` : ` Provider: ${config.provider} | Model: ${displayModel}`;
|
|
4478
|
-
console.log(
|
|
4679
|
+
console.log(chalk6.dim(modelLine + "\n"));
|
|
4479
4680
|
console.log(title.bold(" Step 2/5 \u2014 Discover your project\n"));
|
|
4480
|
-
console.log(
|
|
4681
|
+
console.log(chalk6.dim(" Learning about your languages, dependencies, structure, and existing configs.\n"));
|
|
4481
4682
|
const spinner = ora2("Analyzing project...").start();
|
|
4482
4683
|
const fingerprint = collectFingerprint(process.cwd());
|
|
4483
4684
|
await enrichFingerprintWithLLM(fingerprint, process.cwd());
|
|
4484
4685
|
spinner.succeed("Project analyzed");
|
|
4485
|
-
console.log(
|
|
4486
|
-
console.log(
|
|
4686
|
+
console.log(chalk6.dim(` Languages: ${fingerprint.languages.join(", ") || "none detected"}`));
|
|
4687
|
+
console.log(chalk6.dim(` Files: ${fingerprint.fileTree.length} found
|
|
4487
4688
|
`));
|
|
4488
4689
|
const targetAgent = options.agent || await promptAgent();
|
|
4489
4690
|
const preScore = computeLocalScore(process.cwd(), targetAgent);
|
|
@@ -4502,23 +4703,23 @@ async function initCommand(options) {
|
|
|
4502
4703
|
const hasExistingConfig = !!(fingerprint.existingConfigs.claudeMd || fingerprint.existingConfigs.claudeSettings || fingerprint.existingConfigs.claudeSkills?.length || fingerprint.existingConfigs.cursorrules || fingerprint.existingConfigs.cursorRules?.length || fingerprint.existingConfigs.agentsMd);
|
|
4503
4704
|
const NON_LLM_CHECKS = /* @__PURE__ */ new Set(["hooks_configured", "agents_md_exists", "permissions_configured", "mcp_servers"]);
|
|
4504
4705
|
if (hasExistingConfig && baselineScore.score === 100) {
|
|
4505
|
-
console.log(
|
|
4506
|
-
console.log(
|
|
4706
|
+
console.log(chalk6.bold.green(" Your setup is already optimal \u2014 nothing to change.\n"));
|
|
4707
|
+
console.log(chalk6.dim(" Run ") + chalk6.hex("#83D1EB")("caliber onboard --force") + chalk6.dim(" to regenerate anyway.\n"));
|
|
4507
4708
|
if (!options.force) return;
|
|
4508
4709
|
}
|
|
4509
4710
|
const allFailingChecks = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
|
|
4510
4711
|
const llmFixableChecks = allFailingChecks.filter((c) => !NON_LLM_CHECKS.has(c.id));
|
|
4511
4712
|
if (hasExistingConfig && llmFixableChecks.length === 0 && allFailingChecks.length > 0 && !options.force) {
|
|
4512
|
-
console.log(
|
|
4513
|
-
console.log(
|
|
4713
|
+
console.log(chalk6.bold.green("\n Your config is fully optimized for LLM generation.\n"));
|
|
4714
|
+
console.log(chalk6.dim(" Remaining items need CLI actions:\n"));
|
|
4514
4715
|
for (const check of allFailingChecks) {
|
|
4515
|
-
console.log(
|
|
4716
|
+
console.log(chalk6.dim(` \u2022 ${check.name}`));
|
|
4516
4717
|
if (check.suggestion) {
|
|
4517
|
-
console.log(` ${
|
|
4718
|
+
console.log(` ${chalk6.hex("#83D1EB")(check.suggestion)}`);
|
|
4518
4719
|
}
|
|
4519
4720
|
}
|
|
4520
4721
|
console.log("");
|
|
4521
|
-
console.log(
|
|
4722
|
+
console.log(chalk6.dim(" Run ") + chalk6.hex("#83D1EB")("caliber onboard --force") + chalk6.dim(" to regenerate anyway.\n"));
|
|
4522
4723
|
return;
|
|
4523
4724
|
}
|
|
4524
4725
|
const isEmpty = fingerprint.fileTree.length < 3;
|
|
@@ -4534,22 +4735,22 @@ async function initCommand(options) {
|
|
|
4534
4735
|
currentScore = baselineScore.score;
|
|
4535
4736
|
if (failingChecks.length > 0) {
|
|
4536
4737
|
console.log(title.bold(" Step 3/5 \u2014 Fine-tuning\n"));
|
|
4537
|
-
console.log(
|
|
4738
|
+
console.log(chalk6.dim(` Your setup scores ${baselineScore.score}/100 \u2014 fixing ${failingChecks.length} remaining issue${failingChecks.length === 1 ? "" : "s"}:
|
|
4538
4739
|
`));
|
|
4539
4740
|
for (const check of failingChecks) {
|
|
4540
|
-
console.log(
|
|
4741
|
+
console.log(chalk6.dim(` \u2022 ${check.name}`));
|
|
4541
4742
|
}
|
|
4542
4743
|
console.log("");
|
|
4543
4744
|
}
|
|
4544
4745
|
} else if (hasExistingConfig) {
|
|
4545
4746
|
console.log(title.bold(" Step 3/5 \u2014 Improve your setup\n"));
|
|
4546
|
-
console.log(
|
|
4547
|
-
console.log(
|
|
4747
|
+
console.log(chalk6.dim(" Reviewing your existing configs against your codebase"));
|
|
4748
|
+
console.log(chalk6.dim(" and preparing improvements.\n"));
|
|
4548
4749
|
} else {
|
|
4549
4750
|
console.log(title.bold(" Step 3/5 \u2014 Build your agent setup\n"));
|
|
4550
|
-
console.log(
|
|
4751
|
+
console.log(chalk6.dim(" Creating config files tailored to your project.\n"));
|
|
4551
4752
|
}
|
|
4552
|
-
console.log(
|
|
4753
|
+
console.log(chalk6.dim(" This can take a couple of minutes depending on your model and provider.\n"));
|
|
4553
4754
|
const genStartTime = Date.now();
|
|
4554
4755
|
const genSpinner = ora2("Generating setup...").start();
|
|
4555
4756
|
const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, { showElapsedTime: true });
|
|
@@ -4591,8 +4792,8 @@ async function initCommand(options) {
|
|
|
4591
4792
|
if (!generatedSetup) {
|
|
4592
4793
|
genSpinner.fail("Failed to generate setup.");
|
|
4593
4794
|
if (rawOutput) {
|
|
4594
|
-
console.log(
|
|
4595
|
-
console.log(
|
|
4795
|
+
console.log(chalk6.dim("\nRaw LLM output (JSON parse failed):"));
|
|
4796
|
+
console.log(chalk6.dim(rawOutput.slice(0, 500)));
|
|
4596
4797
|
}
|
|
4597
4798
|
throw new Error("__exit__");
|
|
4598
4799
|
}
|
|
@@ -4600,7 +4801,7 @@ async function initCommand(options) {
|
|
|
4600
4801
|
const mins = Math.floor(elapsedMs / 6e4);
|
|
4601
4802
|
const secs = Math.floor(elapsedMs % 6e4 / 1e3);
|
|
4602
4803
|
const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
|
|
4603
|
-
genSpinner.succeed(`Setup generated ${
|
|
4804
|
+
genSpinner.succeed(`Setup generated ${chalk6.dim(`in ${timeStr}`)}`);
|
|
4604
4805
|
printSetupSummary(generatedSetup);
|
|
4605
4806
|
const sessionHistory = [];
|
|
4606
4807
|
sessionHistory.push({
|
|
@@ -4611,11 +4812,11 @@ async function initCommand(options) {
|
|
|
4611
4812
|
const setupFiles = collectSetupFiles(generatedSetup);
|
|
4612
4813
|
const staged = stageFiles(setupFiles, process.cwd());
|
|
4613
4814
|
const totalChanges = staged.newFiles + staged.modifiedFiles;
|
|
4614
|
-
console.log(
|
|
4815
|
+
console.log(chalk6.dim(` ${chalk6.green(`${staged.newFiles} new`)} / ${chalk6.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}
|
|
4615
4816
|
`));
|
|
4616
4817
|
let action;
|
|
4617
4818
|
if (totalChanges === 0) {
|
|
4618
|
-
console.log(
|
|
4819
|
+
console.log(chalk6.dim(" No changes needed \u2014 your configs are already up to date.\n"));
|
|
4619
4820
|
cleanupStaging();
|
|
4620
4821
|
action = "accept";
|
|
4621
4822
|
} else {
|
|
@@ -4630,12 +4831,12 @@ async function initCommand(options) {
|
|
|
4630
4831
|
generatedSetup = await refineLoop(generatedSetup, targetAgent, sessionHistory);
|
|
4631
4832
|
if (!generatedSetup) {
|
|
4632
4833
|
cleanupStaging();
|
|
4633
|
-
console.log(
|
|
4834
|
+
console.log(chalk6.dim("Refinement cancelled. No files were modified."));
|
|
4634
4835
|
return;
|
|
4635
4836
|
}
|
|
4636
4837
|
const updatedFiles = collectSetupFiles(generatedSetup);
|
|
4637
4838
|
const restaged = stageFiles(updatedFiles, process.cwd());
|
|
4638
|
-
console.log(
|
|
4839
|
+
console.log(chalk6.dim(` ${chalk6.green(`${restaged.newFiles} new`)} / ${chalk6.yellow(`${restaged.modifiedFiles} modified`)} file${restaged.newFiles + restaged.modifiedFiles !== 1 ? "s" : ""}
|
|
4639
4840
|
`));
|
|
4640
4841
|
printSetupSummary(generatedSetup);
|
|
4641
4842
|
await openReview("terminal", restaged.stagedFiles);
|
|
@@ -4643,11 +4844,11 @@ async function initCommand(options) {
|
|
|
4643
4844
|
}
|
|
4644
4845
|
cleanupStaging();
|
|
4645
4846
|
if (action === "decline") {
|
|
4646
|
-
console.log(
|
|
4847
|
+
console.log(chalk6.dim("Setup declined. No files were modified."));
|
|
4647
4848
|
return;
|
|
4648
4849
|
}
|
|
4649
4850
|
if (options.dryRun) {
|
|
4650
|
-
console.log(
|
|
4851
|
+
console.log(chalk6.yellow("\n[Dry run] Would write the following files:"));
|
|
4651
4852
|
console.log(JSON.stringify(generatedSetup, null, 2));
|
|
4652
4853
|
return;
|
|
4653
4854
|
}
|
|
@@ -4655,43 +4856,43 @@ async function initCommand(options) {
|
|
|
4655
4856
|
try {
|
|
4656
4857
|
const result = writeSetup(generatedSetup);
|
|
4657
4858
|
writeSpinner.succeed("Config files written");
|
|
4658
|
-
console.log(
|
|
4859
|
+
console.log(chalk6.bold("\nFiles created/updated:"));
|
|
4659
4860
|
for (const file of result.written) {
|
|
4660
|
-
console.log(` ${
|
|
4861
|
+
console.log(` ${chalk6.green("\u2713")} ${file}`);
|
|
4661
4862
|
}
|
|
4662
4863
|
if (result.deleted.length > 0) {
|
|
4663
|
-
console.log(
|
|
4864
|
+
console.log(chalk6.bold("\nFiles removed:"));
|
|
4664
4865
|
for (const file of result.deleted) {
|
|
4665
|
-
console.log(` ${
|
|
4866
|
+
console.log(` ${chalk6.red("\u2717")} ${file}`);
|
|
4666
4867
|
}
|
|
4667
4868
|
}
|
|
4668
4869
|
if (result.backupDir) {
|
|
4669
|
-
console.log(
|
|
4870
|
+
console.log(chalk6.dim(`
|
|
4670
4871
|
Backups saved to ${result.backupDir}`));
|
|
4671
4872
|
}
|
|
4672
4873
|
} catch (err) {
|
|
4673
4874
|
writeSpinner.fail("Failed to write files");
|
|
4674
|
-
console.error(
|
|
4875
|
+
console.error(chalk6.red(err instanceof Error ? err.message : "Unknown error"));
|
|
4675
4876
|
throw new Error("__exit__");
|
|
4676
4877
|
}
|
|
4677
4878
|
console.log(title.bold("\n Step 5/5 \u2014 Enhance with MCP servers\n"));
|
|
4678
|
-
console.log(
|
|
4679
|
-
console.log(
|
|
4879
|
+
console.log(chalk6.dim(" MCP servers connect your AI agents to external tools and services"));
|
|
4880
|
+
console.log(chalk6.dim(" like databases, APIs, and platforms your project depends on.\n"));
|
|
4680
4881
|
if (fingerprint.tools.length > 0) {
|
|
4681
4882
|
try {
|
|
4682
4883
|
const mcpResult = await discoverAndInstallMcps(targetAgent, fingerprint, process.cwd());
|
|
4683
4884
|
if (mcpResult.installed > 0) {
|
|
4684
|
-
console.log(
|
|
4885
|
+
console.log(chalk6.bold(`
|
|
4685
4886
|
${mcpResult.installed} MCP server${mcpResult.installed > 1 ? "s" : ""} configured`));
|
|
4686
4887
|
for (const name of mcpResult.names) {
|
|
4687
|
-
console.log(` ${
|
|
4888
|
+
console.log(` ${chalk6.green("\u2713")} ${name}`);
|
|
4688
4889
|
}
|
|
4689
4890
|
}
|
|
4690
4891
|
} catch (err) {
|
|
4691
|
-
console.log(
|
|
4892
|
+
console.log(chalk6.dim(" MCP discovery skipped: " + (err instanceof Error ? err.message : "unknown error")));
|
|
4692
4893
|
}
|
|
4693
4894
|
} else {
|
|
4694
|
-
console.log(
|
|
4895
|
+
console.log(chalk6.dim(" No external tools or services detected \u2014 skipping MCP discovery.\n"));
|
|
4695
4896
|
}
|
|
4696
4897
|
ensurePermissions();
|
|
4697
4898
|
const sha = getCurrentHeadSha();
|
|
@@ -4702,56 +4903,56 @@ async function initCommand(options) {
|
|
|
4702
4903
|
});
|
|
4703
4904
|
console.log("");
|
|
4704
4905
|
console.log(title.bold(" Keep your configs fresh\n"));
|
|
4705
|
-
console.log(
|
|
4906
|
+
console.log(chalk6.dim(" Caliber can automatically update your agent configs when your code changes.\n"));
|
|
4706
4907
|
const hookChoice = await promptHookType(targetAgent);
|
|
4707
4908
|
if (hookChoice === "claude" || hookChoice === "both") {
|
|
4708
4909
|
const hookResult = installHook();
|
|
4709
4910
|
if (hookResult.installed) {
|
|
4710
|
-
console.log(` ${
|
|
4711
|
-
console.log(
|
|
4911
|
+
console.log(` ${chalk6.green("\u2713")} Claude Code hook installed \u2014 docs update on session end`);
|
|
4912
|
+
console.log(chalk6.dim(" Run ") + chalk6.hex("#83D1EB")("caliber hooks --remove") + chalk6.dim(" to disable"));
|
|
4712
4913
|
} else if (hookResult.alreadyInstalled) {
|
|
4713
|
-
console.log(
|
|
4914
|
+
console.log(chalk6.dim(" Claude Code hook already installed"));
|
|
4714
4915
|
}
|
|
4715
4916
|
const learnResult = installLearningHooks();
|
|
4716
4917
|
if (learnResult.installed) {
|
|
4717
|
-
console.log(` ${
|
|
4718
|
-
console.log(
|
|
4918
|
+
console.log(` ${chalk6.green("\u2713")} Learning hooks installed \u2014 session insights captured automatically`);
|
|
4919
|
+
console.log(chalk6.dim(" Run ") + chalk6.hex("#83D1EB")("caliber learn remove") + chalk6.dim(" to disable"));
|
|
4719
4920
|
} else if (learnResult.alreadyInstalled) {
|
|
4720
|
-
console.log(
|
|
4921
|
+
console.log(chalk6.dim(" Learning hooks already installed"));
|
|
4721
4922
|
}
|
|
4722
4923
|
}
|
|
4723
4924
|
if (hookChoice === "precommit" || hookChoice === "both") {
|
|
4724
4925
|
const precommitResult = installPreCommitHook();
|
|
4725
4926
|
if (precommitResult.installed) {
|
|
4726
|
-
console.log(` ${
|
|
4727
|
-
console.log(
|
|
4927
|
+
console.log(` ${chalk6.green("\u2713")} Pre-commit hook installed \u2014 docs refresh before each commit`);
|
|
4928
|
+
console.log(chalk6.dim(" Run ") + chalk6.hex("#83D1EB")("caliber hooks --remove") + chalk6.dim(" to disable"));
|
|
4728
4929
|
} else if (precommitResult.alreadyInstalled) {
|
|
4729
|
-
console.log(
|
|
4930
|
+
console.log(chalk6.dim(" Pre-commit hook already installed"));
|
|
4730
4931
|
} else {
|
|
4731
|
-
console.log(
|
|
4932
|
+
console.log(chalk6.yellow(" Could not install pre-commit hook (not a git repository?)"));
|
|
4732
4933
|
}
|
|
4733
4934
|
}
|
|
4734
4935
|
if (hookChoice === "skip") {
|
|
4735
|
-
console.log(
|
|
4936
|
+
console.log(chalk6.dim(" Skipped auto-refresh hooks. Run ") + chalk6.hex("#83D1EB")("caliber hooks --install") + chalk6.dim(" later to enable."));
|
|
4736
4937
|
}
|
|
4737
4938
|
const afterScore = computeLocalScore(process.cwd(), targetAgent);
|
|
4738
4939
|
if (afterScore.score < baselineScore.score) {
|
|
4739
4940
|
console.log("");
|
|
4740
|
-
console.log(
|
|
4941
|
+
console.log(chalk6.yellow(` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`));
|
|
4741
4942
|
try {
|
|
4742
4943
|
const { restored, removed } = undoSetup();
|
|
4743
4944
|
if (restored.length > 0 || removed.length > 0) {
|
|
4744
|
-
console.log(
|
|
4945
|
+
console.log(chalk6.dim(` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`));
|
|
4745
4946
|
}
|
|
4746
4947
|
} catch {
|
|
4747
4948
|
}
|
|
4748
|
-
console.log(
|
|
4949
|
+
console.log(chalk6.dim(" Run ") + chalk6.hex("#83D1EB")("caliber onboard --force") + chalk6.dim(" to override.\n"));
|
|
4749
4950
|
return;
|
|
4750
4951
|
}
|
|
4751
4952
|
displayScoreDelta(baselineScore, afterScore);
|
|
4752
|
-
console.log(
|
|
4753
|
-
console.log(
|
|
4754
|
-
console.log(
|
|
4953
|
+
console.log(chalk6.bold.green(" Onboarding complete! Your project is ready for AI-assisted development."));
|
|
4954
|
+
console.log(chalk6.dim(" Run ") + chalk6.hex("#83D1EB")("caliber undo") + chalk6.dim(" to revert changes.\n"));
|
|
4955
|
+
console.log(chalk6.bold(" Next steps:\n"));
|
|
4755
4956
|
console.log(` ${title("caliber score")} See your full config breakdown`);
|
|
4756
4957
|
console.log(` ${title("caliber recommend")} Discover community skills for your stack`);
|
|
4757
4958
|
console.log(` ${title("caliber undo")} Revert all changes from this run`);
|
|
@@ -4768,9 +4969,9 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
|
|
|
4768
4969
|
}
|
|
4769
4970
|
const isValid = await classifyRefineIntent(message);
|
|
4770
4971
|
if (!isValid) {
|
|
4771
|
-
console.log(
|
|
4772
|
-
console.log(
|
|
4773
|
-
console.log(
|
|
4972
|
+
console.log(chalk6.dim(" This doesn't look like a config change request."));
|
|
4973
|
+
console.log(chalk6.dim(" Describe what to add, remove, or modify in your configs."));
|
|
4974
|
+
console.log(chalk6.dim(' Type "done" to accept the current setup.\n'));
|
|
4774
4975
|
continue;
|
|
4775
4976
|
}
|
|
4776
4977
|
const refineSpinner = ora2("Refining setup...").start();
|
|
@@ -4791,10 +4992,10 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
|
|
|
4791
4992
|
});
|
|
4792
4993
|
refineSpinner.succeed("Setup updated");
|
|
4793
4994
|
printSetupSummary(refined);
|
|
4794
|
-
console.log(
|
|
4995
|
+
console.log(chalk6.dim('Type "done" to accept, or describe more changes.'));
|
|
4795
4996
|
} else {
|
|
4796
4997
|
refineSpinner.fail("Refinement failed \u2014 could not parse AI response.");
|
|
4797
|
-
console.log(
|
|
4998
|
+
console.log(chalk6.dim('Try rephrasing your request, or type "done" to keep the current setup.'));
|
|
4798
4999
|
}
|
|
4799
5000
|
}
|
|
4800
5001
|
}
|
|
@@ -4854,7 +5055,7 @@ ${JSON.stringify(checkList, null, 2)}`,
|
|
|
4854
5055
|
function promptInput3(question) {
|
|
4855
5056
|
const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
|
|
4856
5057
|
return new Promise((resolve2) => {
|
|
4857
|
-
rl.question(
|
|
5058
|
+
rl.question(chalk6.cyan(`${question} `), (answer) => {
|
|
4858
5059
|
rl.close();
|
|
4859
5060
|
resolve2(answer.trim());
|
|
4860
5061
|
});
|
|
@@ -4886,210 +5087,13 @@ async function promptHookType(targetAgent) {
|
|
|
4886
5087
|
choices.push({ name: "Both (Claude Code + pre-commit)", value: "both" });
|
|
4887
5088
|
}
|
|
4888
5089
|
choices.push({ name: "Skip for now", value: "skip" });
|
|
4889
|
-
return
|
|
5090
|
+
return select3({
|
|
4890
5091
|
message: "How would you like to auto-refresh your docs?",
|
|
4891
5092
|
choices
|
|
4892
5093
|
});
|
|
4893
5094
|
}
|
|
4894
|
-
async function promptWantsReview() {
|
|
4895
|
-
const answer = await select2({
|
|
4896
|
-
message: "Would you like to review the diffs before deciding?",
|
|
4897
|
-
choices: [
|
|
4898
|
-
{ name: "Yes, show me the diffs", value: true },
|
|
4899
|
-
{ name: "No, continue", value: false }
|
|
4900
|
-
]
|
|
4901
|
-
});
|
|
4902
|
-
return answer;
|
|
4903
|
-
}
|
|
4904
|
-
async function promptReviewMethod() {
|
|
4905
|
-
const available = detectAvailableEditors();
|
|
4906
|
-
if (available.length === 1) return "terminal";
|
|
4907
|
-
const choices = available.map((method) => {
|
|
4908
|
-
switch (method) {
|
|
4909
|
-
case "cursor":
|
|
4910
|
-
return { name: "Cursor (diff view)", value: "cursor" };
|
|
4911
|
-
case "vscode":
|
|
4912
|
-
return { name: "VS Code (diff view)", value: "vscode" };
|
|
4913
|
-
case "terminal":
|
|
4914
|
-
return { name: "Terminal", value: "terminal" };
|
|
4915
|
-
}
|
|
4916
|
-
});
|
|
4917
|
-
return select2({ message: "How would you like to review the changes?", choices });
|
|
4918
|
-
}
|
|
4919
|
-
async function openReview(method, stagedFiles) {
|
|
4920
|
-
if (method === "cursor" || method === "vscode") {
|
|
4921
|
-
openDiffsInEditor(method, stagedFiles.map((f) => ({
|
|
4922
|
-
originalPath: f.originalPath,
|
|
4923
|
-
proposedPath: f.proposedPath
|
|
4924
|
-
})));
|
|
4925
|
-
console.log(chalk5.dim(" Diffs opened in your editor.\n"));
|
|
4926
|
-
return;
|
|
4927
|
-
}
|
|
4928
|
-
const fileInfos = stagedFiles.map((file) => {
|
|
4929
|
-
const proposed = fs20.readFileSync(file.proposedPath, "utf-8");
|
|
4930
|
-
const current = file.currentPath ? fs20.readFileSync(file.currentPath, "utf-8") : "";
|
|
4931
|
-
const patch = createTwoFilesPatch(
|
|
4932
|
-
file.isNew ? "/dev/null" : file.relativePath,
|
|
4933
|
-
file.relativePath,
|
|
4934
|
-
current,
|
|
4935
|
-
proposed
|
|
4936
|
-
);
|
|
4937
|
-
let added = 0, removed = 0;
|
|
4938
|
-
for (const line of patch.split("\n")) {
|
|
4939
|
-
if (line.startsWith("+") && !line.startsWith("+++")) added++;
|
|
4940
|
-
if (line.startsWith("-") && !line.startsWith("---")) removed++;
|
|
4941
|
-
}
|
|
4942
|
-
return {
|
|
4943
|
-
relativePath: file.relativePath,
|
|
4944
|
-
isNew: file.isNew,
|
|
4945
|
-
added,
|
|
4946
|
-
removed,
|
|
4947
|
-
lines: proposed.split("\n").length,
|
|
4948
|
-
patch
|
|
4949
|
-
};
|
|
4950
|
-
});
|
|
4951
|
-
await interactiveDiffExplorer(fileInfos);
|
|
4952
|
-
}
|
|
4953
|
-
async function interactiveDiffExplorer(files) {
|
|
4954
|
-
if (!process.stdin.isTTY) {
|
|
4955
|
-
for (const f of files) {
|
|
4956
|
-
const icon = f.isNew ? chalk5.green("+") : chalk5.yellow("~");
|
|
4957
|
-
const stats = f.isNew ? chalk5.dim(`${f.lines} lines`) : `${chalk5.green(`+${f.added}`)} ${chalk5.red(`-${f.removed}`)}`;
|
|
4958
|
-
console.log(` ${icon} ${f.relativePath} ${stats}`);
|
|
4959
|
-
}
|
|
4960
|
-
console.log("");
|
|
4961
|
-
return;
|
|
4962
|
-
}
|
|
4963
|
-
const { stdin, stdout } = process;
|
|
4964
|
-
let cursor = 0;
|
|
4965
|
-
let viewing = null;
|
|
4966
|
-
let scrollOffset = 0;
|
|
4967
|
-
let lineCount = 0;
|
|
4968
|
-
function getTermHeight() {
|
|
4969
|
-
return (stdout.rows || 24) - 4;
|
|
4970
|
-
}
|
|
4971
|
-
function renderFileList() {
|
|
4972
|
-
const lines = [];
|
|
4973
|
-
lines.push(chalk5.bold(" Review changes"));
|
|
4974
|
-
lines.push("");
|
|
4975
|
-
for (let i = 0; i < files.length; i++) {
|
|
4976
|
-
const f = files[i];
|
|
4977
|
-
const ptr = i === cursor ? chalk5.cyan(">") : " ";
|
|
4978
|
-
const icon = f.isNew ? chalk5.green("+") : chalk5.yellow("~");
|
|
4979
|
-
const stats = f.isNew ? chalk5.dim(`${f.lines} lines`) : `${chalk5.green(`+${f.added}`)} ${chalk5.red(`-${f.removed}`)}`;
|
|
4980
|
-
lines.push(` ${ptr} ${icon} ${f.relativePath} ${stats}`);
|
|
4981
|
-
}
|
|
4982
|
-
lines.push("");
|
|
4983
|
-
lines.push(chalk5.dim(" \u2191\u2193 navigate \u23CE view diff q done"));
|
|
4984
|
-
return lines.join("\n");
|
|
4985
|
-
}
|
|
4986
|
-
function renderDiff(index) {
|
|
4987
|
-
const f = files[index];
|
|
4988
|
-
const lines = [];
|
|
4989
|
-
const header = f.isNew ? ` ${chalk5.green("+")} ${f.relativePath} ${chalk5.dim("(new file)")}` : ` ${chalk5.yellow("~")} ${f.relativePath} ${chalk5.green(`+${f.added}`)} ${chalk5.red(`-${f.removed}`)}`;
|
|
4990
|
-
lines.push(header);
|
|
4991
|
-
lines.push(chalk5.dim(" " + "\u2500".repeat(60)));
|
|
4992
|
-
const patchLines = f.patch.split("\n");
|
|
4993
|
-
const bodyLines = patchLines.slice(4);
|
|
4994
|
-
const maxVisible = getTermHeight() - 4;
|
|
4995
|
-
const visibleLines = bodyLines.slice(scrollOffset, scrollOffset + maxVisible);
|
|
4996
|
-
for (const line of visibleLines) {
|
|
4997
|
-
if (line.startsWith("+")) {
|
|
4998
|
-
lines.push(chalk5.green(" " + line));
|
|
4999
|
-
} else if (line.startsWith("-")) {
|
|
5000
|
-
lines.push(chalk5.red(" " + line));
|
|
5001
|
-
} else if (line.startsWith("@@")) {
|
|
5002
|
-
lines.push(chalk5.cyan(" " + line));
|
|
5003
|
-
} else {
|
|
5004
|
-
lines.push(chalk5.dim(" " + line));
|
|
5005
|
-
}
|
|
5006
|
-
}
|
|
5007
|
-
const totalBody = bodyLines.length;
|
|
5008
|
-
if (totalBody > maxVisible) {
|
|
5009
|
-
const pct = Math.round((scrollOffset + maxVisible) / totalBody * 100);
|
|
5010
|
-
lines.push(chalk5.dim(` \u2500\u2500 ${Math.min(pct, 100)}% \u2500\u2500`));
|
|
5011
|
-
}
|
|
5012
|
-
lines.push("");
|
|
5013
|
-
lines.push(chalk5.dim(" \u2191\u2193 scroll \u23B5/esc back to file list"));
|
|
5014
|
-
return lines.join("\n");
|
|
5015
|
-
}
|
|
5016
|
-
function draw(initial) {
|
|
5017
|
-
if (!initial && lineCount > 0) {
|
|
5018
|
-
stdout.write(`\x1B[${lineCount}A`);
|
|
5019
|
-
}
|
|
5020
|
-
stdout.write("\x1B[0J");
|
|
5021
|
-
const output = viewing !== null ? renderDiff(viewing) : renderFileList();
|
|
5022
|
-
stdout.write(output + "\n");
|
|
5023
|
-
lineCount = output.split("\n").length;
|
|
5024
|
-
}
|
|
5025
|
-
return new Promise((resolve2) => {
|
|
5026
|
-
console.log("");
|
|
5027
|
-
draw(true);
|
|
5028
|
-
stdin.setRawMode(true);
|
|
5029
|
-
stdin.resume();
|
|
5030
|
-
stdin.setEncoding("utf8");
|
|
5031
|
-
function cleanup() {
|
|
5032
|
-
stdin.removeListener("data", onData);
|
|
5033
|
-
stdin.setRawMode(false);
|
|
5034
|
-
stdin.pause();
|
|
5035
|
-
}
|
|
5036
|
-
function onData(key) {
|
|
5037
|
-
if (viewing !== null) {
|
|
5038
|
-
const f = files[viewing];
|
|
5039
|
-
const totalBody = f.patch.split("\n").length - 4;
|
|
5040
|
-
const maxVisible = getTermHeight() - 4;
|
|
5041
|
-
switch (key) {
|
|
5042
|
-
case "\x1B[A":
|
|
5043
|
-
scrollOffset = Math.max(0, scrollOffset - 1);
|
|
5044
|
-
draw(false);
|
|
5045
|
-
break;
|
|
5046
|
-
case "\x1B[B":
|
|
5047
|
-
scrollOffset = Math.min(Math.max(0, totalBody - maxVisible), scrollOffset + 1);
|
|
5048
|
-
draw(false);
|
|
5049
|
-
break;
|
|
5050
|
-
case " ":
|
|
5051
|
-
case "\x1B":
|
|
5052
|
-
viewing = null;
|
|
5053
|
-
scrollOffset = 0;
|
|
5054
|
-
draw(false);
|
|
5055
|
-
break;
|
|
5056
|
-
case "q":
|
|
5057
|
-
case "":
|
|
5058
|
-
cleanup();
|
|
5059
|
-
console.log("");
|
|
5060
|
-
resolve2();
|
|
5061
|
-
break;
|
|
5062
|
-
}
|
|
5063
|
-
} else {
|
|
5064
|
-
switch (key) {
|
|
5065
|
-
case "\x1B[A":
|
|
5066
|
-
cursor = (cursor - 1 + files.length) % files.length;
|
|
5067
|
-
draw(false);
|
|
5068
|
-
break;
|
|
5069
|
-
case "\x1B[B":
|
|
5070
|
-
cursor = (cursor + 1) % files.length;
|
|
5071
|
-
draw(false);
|
|
5072
|
-
break;
|
|
5073
|
-
case "\r":
|
|
5074
|
-
case "\n":
|
|
5075
|
-
viewing = cursor;
|
|
5076
|
-
scrollOffset = 0;
|
|
5077
|
-
draw(false);
|
|
5078
|
-
break;
|
|
5079
|
-
case "q":
|
|
5080
|
-
case "":
|
|
5081
|
-
cleanup();
|
|
5082
|
-
console.log("");
|
|
5083
|
-
resolve2();
|
|
5084
|
-
break;
|
|
5085
|
-
}
|
|
5086
|
-
}
|
|
5087
|
-
}
|
|
5088
|
-
stdin.on("data", onData);
|
|
5089
|
-
});
|
|
5090
|
-
}
|
|
5091
5095
|
async function promptReviewAction() {
|
|
5092
|
-
return
|
|
5096
|
+
return select3({
|
|
5093
5097
|
message: "What would you like to do?",
|
|
5094
5098
|
choices: [
|
|
5095
5099
|
{ name: "Accept and apply", value: "accept" },
|
|
@@ -5104,26 +5108,26 @@ function printSetupSummary(setup) {
|
|
|
5104
5108
|
const fileDescriptions = setup.fileDescriptions;
|
|
5105
5109
|
const deletions = setup.deletions;
|
|
5106
5110
|
console.log("");
|
|
5107
|
-
console.log(
|
|
5111
|
+
console.log(chalk6.bold(" Proposed changes:\n"));
|
|
5108
5112
|
const getDescription = (filePath) => {
|
|
5109
5113
|
return fileDescriptions?.[filePath];
|
|
5110
5114
|
};
|
|
5111
5115
|
if (claude) {
|
|
5112
5116
|
if (claude.claudeMd) {
|
|
5113
|
-
const icon =
|
|
5117
|
+
const icon = fs21.existsSync("CLAUDE.md") ? chalk6.yellow("~") : chalk6.green("+");
|
|
5114
5118
|
const desc = getDescription("CLAUDE.md");
|
|
5115
|
-
console.log(` ${icon} ${
|
|
5116
|
-
if (desc) console.log(
|
|
5119
|
+
console.log(` ${icon} ${chalk6.bold("CLAUDE.md")}`);
|
|
5120
|
+
if (desc) console.log(chalk6.dim(` ${desc}`));
|
|
5117
5121
|
console.log("");
|
|
5118
5122
|
}
|
|
5119
5123
|
const skills = claude.skills;
|
|
5120
5124
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
5121
5125
|
for (const skill of skills) {
|
|
5122
5126
|
const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
|
|
5123
|
-
const icon =
|
|
5127
|
+
const icon = fs21.existsSync(skillPath) ? chalk6.yellow("~") : chalk6.green("+");
|
|
5124
5128
|
const desc = getDescription(skillPath);
|
|
5125
|
-
console.log(` ${icon} ${
|
|
5126
|
-
console.log(
|
|
5129
|
+
console.log(` ${icon} ${chalk6.bold(skillPath)}`);
|
|
5130
|
+
console.log(chalk6.dim(` ${desc || skill.description || skill.name}`));
|
|
5127
5131
|
console.log("");
|
|
5128
5132
|
}
|
|
5129
5133
|
}
|
|
@@ -5131,40 +5135,40 @@ function printSetupSummary(setup) {
|
|
|
5131
5135
|
const codex = setup.codex;
|
|
5132
5136
|
if (codex) {
|
|
5133
5137
|
if (codex.agentsMd) {
|
|
5134
|
-
const icon =
|
|
5138
|
+
const icon = fs21.existsSync("AGENTS.md") ? chalk6.yellow("~") : chalk6.green("+");
|
|
5135
5139
|
const desc = getDescription("AGENTS.md");
|
|
5136
|
-
console.log(` ${icon} ${
|
|
5137
|
-
if (desc) console.log(
|
|
5140
|
+
console.log(` ${icon} ${chalk6.bold("AGENTS.md")}`);
|
|
5141
|
+
if (desc) console.log(chalk6.dim(` ${desc}`));
|
|
5138
5142
|
console.log("");
|
|
5139
5143
|
}
|
|
5140
5144
|
const codexSkills = codex.skills;
|
|
5141
5145
|
if (Array.isArray(codexSkills) && codexSkills.length > 0) {
|
|
5142
5146
|
for (const skill of codexSkills) {
|
|
5143
5147
|
const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
|
|
5144
|
-
const icon =
|
|
5148
|
+
const icon = fs21.existsSync(skillPath) ? chalk6.yellow("~") : chalk6.green("+");
|
|
5145
5149
|
const desc = getDescription(skillPath);
|
|
5146
|
-
console.log(` ${icon} ${
|
|
5147
|
-
console.log(
|
|
5150
|
+
console.log(` ${icon} ${chalk6.bold(skillPath)}`);
|
|
5151
|
+
console.log(chalk6.dim(` ${desc || skill.description || skill.name}`));
|
|
5148
5152
|
console.log("");
|
|
5149
5153
|
}
|
|
5150
5154
|
}
|
|
5151
5155
|
}
|
|
5152
5156
|
if (cursor) {
|
|
5153
5157
|
if (cursor.cursorrules) {
|
|
5154
|
-
const icon =
|
|
5158
|
+
const icon = fs21.existsSync(".cursorrules") ? chalk6.yellow("~") : chalk6.green("+");
|
|
5155
5159
|
const desc = getDescription(".cursorrules");
|
|
5156
|
-
console.log(` ${icon} ${
|
|
5157
|
-
if (desc) console.log(
|
|
5160
|
+
console.log(` ${icon} ${chalk6.bold(".cursorrules")}`);
|
|
5161
|
+
if (desc) console.log(chalk6.dim(` ${desc}`));
|
|
5158
5162
|
console.log("");
|
|
5159
5163
|
}
|
|
5160
5164
|
const cursorSkills = cursor.skills;
|
|
5161
5165
|
if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
|
|
5162
5166
|
for (const skill of cursorSkills) {
|
|
5163
5167
|
const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
|
|
5164
|
-
const icon =
|
|
5168
|
+
const icon = fs21.existsSync(skillPath) ? chalk6.yellow("~") : chalk6.green("+");
|
|
5165
5169
|
const desc = getDescription(skillPath);
|
|
5166
|
-
console.log(` ${icon} ${
|
|
5167
|
-
console.log(
|
|
5170
|
+
console.log(` ${icon} ${chalk6.bold(skillPath)}`);
|
|
5171
|
+
console.log(chalk6.dim(` ${desc || skill.description || skill.name}`));
|
|
5168
5172
|
console.log("");
|
|
5169
5173
|
}
|
|
5170
5174
|
}
|
|
@@ -5172,40 +5176,40 @@ function printSetupSummary(setup) {
|
|
|
5172
5176
|
if (Array.isArray(rules) && rules.length > 0) {
|
|
5173
5177
|
for (const rule of rules) {
|
|
5174
5178
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
5175
|
-
const icon =
|
|
5179
|
+
const icon = fs21.existsSync(rulePath) ? chalk6.yellow("~") : chalk6.green("+");
|
|
5176
5180
|
const desc = getDescription(rulePath);
|
|
5177
|
-
console.log(` ${icon} ${
|
|
5181
|
+
console.log(` ${icon} ${chalk6.bold(rulePath)}`);
|
|
5178
5182
|
if (desc) {
|
|
5179
|
-
console.log(
|
|
5183
|
+
console.log(chalk6.dim(` ${desc}`));
|
|
5180
5184
|
} else {
|
|
5181
5185
|
const firstLine = rule.content.split("\n").filter((l) => l.trim() && !l.trim().startsWith("#"))[0];
|
|
5182
|
-
if (firstLine) console.log(
|
|
5186
|
+
if (firstLine) console.log(chalk6.dim(` ${firstLine.trim().slice(0, 80)}`));
|
|
5183
5187
|
}
|
|
5184
5188
|
console.log("");
|
|
5185
5189
|
}
|
|
5186
5190
|
}
|
|
5187
5191
|
}
|
|
5188
|
-
if (!codex && !
|
|
5189
|
-
console.log(` ${
|
|
5190
|
-
console.log(
|
|
5192
|
+
if (!codex && !fs21.existsSync("AGENTS.md")) {
|
|
5193
|
+
console.log(` ${chalk6.green("+")} ${chalk6.bold("AGENTS.md")}`);
|
|
5194
|
+
console.log(chalk6.dim(" Cross-agent coordination file"));
|
|
5191
5195
|
console.log("");
|
|
5192
5196
|
}
|
|
5193
5197
|
if (Array.isArray(deletions) && deletions.length > 0) {
|
|
5194
5198
|
for (const del of deletions) {
|
|
5195
|
-
console.log(` ${
|
|
5196
|
-
console.log(
|
|
5199
|
+
console.log(` ${chalk6.red("-")} ${chalk6.bold(del.filePath)}`);
|
|
5200
|
+
console.log(chalk6.dim(` ${del.reason}`));
|
|
5197
5201
|
console.log("");
|
|
5198
5202
|
}
|
|
5199
5203
|
}
|
|
5200
|
-
console.log(` ${
|
|
5204
|
+
console.log(` ${chalk6.green("+")} ${chalk6.dim("new")} ${chalk6.yellow("~")} ${chalk6.dim("modified")} ${chalk6.red("-")} ${chalk6.dim("removed")}`);
|
|
5201
5205
|
console.log("");
|
|
5202
5206
|
}
|
|
5203
5207
|
function ensurePermissions() {
|
|
5204
5208
|
const settingsPath = ".claude/settings.json";
|
|
5205
5209
|
let settings = {};
|
|
5206
5210
|
try {
|
|
5207
|
-
if (
|
|
5208
|
-
settings = JSON.parse(
|
|
5211
|
+
if (fs21.existsSync(settingsPath)) {
|
|
5212
|
+
settings = JSON.parse(fs21.readFileSync(settingsPath, "utf-8"));
|
|
5209
5213
|
}
|
|
5210
5214
|
} catch {
|
|
5211
5215
|
}
|
|
@@ -5219,12 +5223,12 @@ function ensurePermissions() {
|
|
|
5219
5223
|
"Bash(git *)"
|
|
5220
5224
|
];
|
|
5221
5225
|
settings.permissions = permissions;
|
|
5222
|
-
if (!
|
|
5223
|
-
|
|
5226
|
+
if (!fs21.existsSync(".claude")) fs21.mkdirSync(".claude", { recursive: true });
|
|
5227
|
+
fs21.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
5224
5228
|
}
|
|
5225
5229
|
|
|
5226
5230
|
// src/commands/undo.ts
|
|
5227
|
-
import
|
|
5231
|
+
import chalk7 from "chalk";
|
|
5228
5232
|
import ora3 from "ora";
|
|
5229
5233
|
function undoCommand() {
|
|
5230
5234
|
const spinner = ora3("Reverting setup...").start();
|
|
@@ -5236,27 +5240,27 @@ function undoCommand() {
|
|
|
5236
5240
|
}
|
|
5237
5241
|
spinner.succeed("Setup reverted successfully.\n");
|
|
5238
5242
|
if (restored.length > 0) {
|
|
5239
|
-
console.log(
|
|
5243
|
+
console.log(chalk7.cyan(" Restored from backup:"));
|
|
5240
5244
|
for (const file of restored) {
|
|
5241
|
-
console.log(` ${
|
|
5245
|
+
console.log(` ${chalk7.green("\u21A9")} ${file}`);
|
|
5242
5246
|
}
|
|
5243
5247
|
}
|
|
5244
5248
|
if (removed.length > 0) {
|
|
5245
|
-
console.log(
|
|
5249
|
+
console.log(chalk7.cyan(" Removed:"));
|
|
5246
5250
|
for (const file of removed) {
|
|
5247
|
-
console.log(` ${
|
|
5251
|
+
console.log(` ${chalk7.red("\u2717")} ${file}`);
|
|
5248
5252
|
}
|
|
5249
5253
|
}
|
|
5250
5254
|
console.log("");
|
|
5251
5255
|
} catch (err) {
|
|
5252
|
-
spinner.fail(
|
|
5256
|
+
spinner.fail(chalk7.red(err instanceof Error ? err.message : "Undo failed"));
|
|
5253
5257
|
throw new Error("__exit__");
|
|
5254
5258
|
}
|
|
5255
5259
|
}
|
|
5256
5260
|
|
|
5257
5261
|
// src/commands/status.ts
|
|
5258
|
-
import
|
|
5259
|
-
import
|
|
5262
|
+
import chalk8 from "chalk";
|
|
5263
|
+
import fs22 from "fs";
|
|
5260
5264
|
async function statusCommand(options) {
|
|
5261
5265
|
const config = loadConfig();
|
|
5262
5266
|
const manifest = readManifest();
|
|
@@ -5269,39 +5273,39 @@ async function statusCommand(options) {
|
|
|
5269
5273
|
}, null, 2));
|
|
5270
5274
|
return;
|
|
5271
5275
|
}
|
|
5272
|
-
console.log(
|
|
5276
|
+
console.log(chalk8.bold("\nCaliber Status\n"));
|
|
5273
5277
|
if (config) {
|
|
5274
|
-
console.log(` LLM: ${
|
|
5278
|
+
console.log(` LLM: ${chalk8.green(config.provider)} (${config.model})`);
|
|
5275
5279
|
} else {
|
|
5276
|
-
console.log(` LLM: ${
|
|
5280
|
+
console.log(` LLM: ${chalk8.yellow("Not configured")} \u2014 run ${chalk8.hex("#83D1EB")("caliber config")}`);
|
|
5277
5281
|
}
|
|
5278
5282
|
if (!manifest) {
|
|
5279
|
-
console.log(` Setup: ${
|
|
5280
|
-
console.log(
|
|
5283
|
+
console.log(` Setup: ${chalk8.dim("No setup applied")}`);
|
|
5284
|
+
console.log(chalk8.dim("\n Run ") + chalk8.hex("#83D1EB")("caliber onboard") + chalk8.dim(" to get started.\n"));
|
|
5281
5285
|
return;
|
|
5282
5286
|
}
|
|
5283
|
-
console.log(` Files managed: ${
|
|
5287
|
+
console.log(` Files managed: ${chalk8.cyan(manifest.entries.length.toString())}`);
|
|
5284
5288
|
for (const entry of manifest.entries) {
|
|
5285
|
-
const exists =
|
|
5286
|
-
const icon = exists ?
|
|
5289
|
+
const exists = fs22.existsSync(entry.path);
|
|
5290
|
+
const icon = exists ? chalk8.green("\u2713") : chalk8.red("\u2717");
|
|
5287
5291
|
console.log(` ${icon} ${entry.path} (${entry.action})`);
|
|
5288
5292
|
}
|
|
5289
5293
|
console.log("");
|
|
5290
5294
|
}
|
|
5291
5295
|
|
|
5292
5296
|
// src/commands/regenerate.ts
|
|
5293
|
-
import
|
|
5297
|
+
import chalk9 from "chalk";
|
|
5294
5298
|
import ora4 from "ora";
|
|
5295
|
-
import
|
|
5299
|
+
import select4 from "@inquirer/select";
|
|
5296
5300
|
async function regenerateCommand(options) {
|
|
5297
5301
|
const config = loadConfig();
|
|
5298
5302
|
if (!config) {
|
|
5299
|
-
console.log(
|
|
5303
|
+
console.log(chalk9.red("No LLM provider configured. Run ") + chalk9.hex("#83D1EB")("caliber config") + chalk9.red(" first."));
|
|
5300
5304
|
throw new Error("__exit__");
|
|
5301
5305
|
}
|
|
5302
5306
|
const manifest = readManifest();
|
|
5303
5307
|
if (!manifest) {
|
|
5304
|
-
console.log(
|
|
5308
|
+
console.log(chalk9.yellow("No existing setup found. Run ") + chalk9.hex("#83D1EB")("caliber onboard") + chalk9.yellow(" first."));
|
|
5305
5309
|
throw new Error("__exit__");
|
|
5306
5310
|
}
|
|
5307
5311
|
const targetAgent = readState()?.targetAgent ?? ["claude", "cursor"];
|
|
@@ -5311,6 +5315,10 @@ async function regenerateCommand(options) {
|
|
|
5311
5315
|
spinner.succeed("Project analyzed");
|
|
5312
5316
|
const baselineScore = computeLocalScore(process.cwd(), targetAgent);
|
|
5313
5317
|
displayScoreSummary(baselineScore);
|
|
5318
|
+
if (baselineScore.score === 100) {
|
|
5319
|
+
console.log(chalk9.green(" Your setup is already at 100/100 \u2014 nothing to regenerate.\n"));
|
|
5320
|
+
return;
|
|
5321
|
+
}
|
|
5314
5322
|
const genSpinner = ora4("Regenerating setup...").start();
|
|
5315
5323
|
const genMessages = new SpinnerMessages(genSpinner, GENERATION_MESSAGES, { showElapsedTime: true });
|
|
5316
5324
|
genMessages.start();
|
|
@@ -5349,23 +5357,28 @@ async function regenerateCommand(options) {
|
|
|
5349
5357
|
const setupFiles = collectSetupFiles(generatedSetup);
|
|
5350
5358
|
const staged = stageFiles(setupFiles, process.cwd());
|
|
5351
5359
|
const totalChanges = staged.newFiles + staged.modifiedFiles;
|
|
5352
|
-
console.log(
|
|
5353
|
-
${
|
|
5360
|
+
console.log(chalk9.dim(`
|
|
5361
|
+
${chalk9.green(`${staged.newFiles} new`)} / ${chalk9.yellow(`${staged.modifiedFiles} modified`)} file${totalChanges !== 1 ? "s" : ""}
|
|
5354
5362
|
`));
|
|
5355
5363
|
if (totalChanges === 0) {
|
|
5356
|
-
console.log(
|
|
5364
|
+
console.log(chalk9.dim(" No changes needed \u2014 your configs are already up to date.\n"));
|
|
5357
5365
|
cleanupStaging();
|
|
5358
5366
|
return;
|
|
5359
5367
|
}
|
|
5360
5368
|
if (options.dryRun) {
|
|
5361
|
-
console.log(
|
|
5369
|
+
console.log(chalk9.yellow("[Dry run] Would write:"));
|
|
5362
5370
|
for (const f of staged.stagedFiles) {
|
|
5363
|
-
console.log(` ${f.isNew ?
|
|
5371
|
+
console.log(` ${f.isNew ? chalk9.green("+") : chalk9.yellow("~")} ${f.relativePath}`);
|
|
5364
5372
|
}
|
|
5365
5373
|
cleanupStaging();
|
|
5366
5374
|
return;
|
|
5367
5375
|
}
|
|
5368
|
-
const
|
|
5376
|
+
const wantsReview = await promptWantsReview();
|
|
5377
|
+
if (wantsReview) {
|
|
5378
|
+
const reviewMethod = await promptReviewMethod();
|
|
5379
|
+
await openReview(reviewMethod, staged.stagedFiles);
|
|
5380
|
+
}
|
|
5381
|
+
const action = await select4({
|
|
5369
5382
|
message: "Apply regenerated setup?",
|
|
5370
5383
|
choices: [
|
|
5371
5384
|
{ name: "Accept and apply", value: "accept" },
|
|
@@ -5374,7 +5387,7 @@ async function regenerateCommand(options) {
|
|
|
5374
5387
|
});
|
|
5375
5388
|
cleanupStaging();
|
|
5376
5389
|
if (action === "decline") {
|
|
5377
|
-
console.log(
|
|
5390
|
+
console.log(chalk9.dim("Regeneration cancelled. No files were modified."));
|
|
5378
5391
|
return;
|
|
5379
5392
|
}
|
|
5380
5393
|
const writeSpinner = ora4("Writing config files...").start();
|
|
@@ -5382,20 +5395,20 @@ async function regenerateCommand(options) {
|
|
|
5382
5395
|
const result = writeSetup(generatedSetup);
|
|
5383
5396
|
writeSpinner.succeed("Config files written");
|
|
5384
5397
|
for (const file of result.written) {
|
|
5385
|
-
console.log(` ${
|
|
5398
|
+
console.log(` ${chalk9.green("\u2713")} ${file}`);
|
|
5386
5399
|
}
|
|
5387
5400
|
if (result.deleted.length > 0) {
|
|
5388
5401
|
for (const file of result.deleted) {
|
|
5389
|
-
console.log(` ${
|
|
5402
|
+
console.log(` ${chalk9.red("\u2717")} ${file}`);
|
|
5390
5403
|
}
|
|
5391
5404
|
}
|
|
5392
5405
|
if (result.backupDir) {
|
|
5393
|
-
console.log(
|
|
5406
|
+
console.log(chalk9.dim(`
|
|
5394
5407
|
Backups saved to ${result.backupDir}`));
|
|
5395
5408
|
}
|
|
5396
5409
|
} catch (err) {
|
|
5397
5410
|
writeSpinner.fail("Failed to write files");
|
|
5398
|
-
console.error(
|
|
5411
|
+
console.error(chalk9.red(err instanceof Error ? err.message : "Unknown error"));
|
|
5399
5412
|
throw new Error("__exit__");
|
|
5400
5413
|
}
|
|
5401
5414
|
const sha = getCurrentHeadSha();
|
|
@@ -5407,36 +5420,36 @@ async function regenerateCommand(options) {
|
|
|
5407
5420
|
const afterScore = computeLocalScore(process.cwd(), targetAgent);
|
|
5408
5421
|
if (afterScore.score < baselineScore.score) {
|
|
5409
5422
|
console.log("");
|
|
5410
|
-
console.log(
|
|
5423
|
+
console.log(chalk9.yellow(` Score would drop from ${baselineScore.score} to ${afterScore.score} \u2014 reverting changes.`));
|
|
5411
5424
|
try {
|
|
5412
5425
|
const { restored, removed } = undoSetup();
|
|
5413
5426
|
if (restored.length > 0 || removed.length > 0) {
|
|
5414
|
-
console.log(
|
|
5427
|
+
console.log(chalk9.dim(` Reverted ${restored.length + removed.length} file${restored.length + removed.length === 1 ? "" : "s"} from backup.`));
|
|
5415
5428
|
}
|
|
5416
5429
|
} catch {
|
|
5417
5430
|
}
|
|
5418
|
-
console.log(
|
|
5431
|
+
console.log(chalk9.dim(" Run ") + chalk9.hex("#83D1EB")("caliber onboard --force") + chalk9.dim(" to override.\n"));
|
|
5419
5432
|
return;
|
|
5420
5433
|
}
|
|
5421
5434
|
displayScoreDelta(baselineScore, afterScore);
|
|
5422
|
-
console.log(
|
|
5423
|
-
console.log(
|
|
5435
|
+
console.log(chalk9.bold.green(" Regeneration complete!"));
|
|
5436
|
+
console.log(chalk9.dim(" Run ") + chalk9.hex("#83D1EB")("caliber undo") + chalk9.dim(" to revert changes.\n"));
|
|
5424
5437
|
}
|
|
5425
5438
|
|
|
5426
5439
|
// src/commands/recommend.ts
|
|
5427
|
-
import
|
|
5440
|
+
import chalk10 from "chalk";
|
|
5428
5441
|
import ora5 from "ora";
|
|
5429
5442
|
import { mkdirSync, readFileSync as readFileSync7, readdirSync as readdirSync5, existsSync as existsSync9, writeFileSync } from "fs";
|
|
5430
5443
|
import { join as join8, dirname as dirname2 } from "path";
|
|
5431
5444
|
|
|
5432
5445
|
// src/scanner/index.ts
|
|
5433
|
-
import
|
|
5446
|
+
import fs23 from "fs";
|
|
5434
5447
|
import path17 from "path";
|
|
5435
5448
|
import crypto2 from "crypto";
|
|
5436
5449
|
function scanLocalState(dir) {
|
|
5437
5450
|
const items = [];
|
|
5438
5451
|
const claudeMdPath = path17.join(dir, "CLAUDE.md");
|
|
5439
|
-
if (
|
|
5452
|
+
if (fs23.existsSync(claudeMdPath)) {
|
|
5440
5453
|
items.push({
|
|
5441
5454
|
type: "rule",
|
|
5442
5455
|
platform: "claude",
|
|
@@ -5446,8 +5459,8 @@ function scanLocalState(dir) {
|
|
|
5446
5459
|
});
|
|
5447
5460
|
}
|
|
5448
5461
|
const skillsDir = path17.join(dir, ".claude", "skills");
|
|
5449
|
-
if (
|
|
5450
|
-
for (const file of
|
|
5462
|
+
if (fs23.existsSync(skillsDir)) {
|
|
5463
|
+
for (const file of fs23.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
5451
5464
|
const filePath = path17.join(skillsDir, file);
|
|
5452
5465
|
items.push({
|
|
5453
5466
|
type: "skill",
|
|
@@ -5459,9 +5472,9 @@ function scanLocalState(dir) {
|
|
|
5459
5472
|
}
|
|
5460
5473
|
}
|
|
5461
5474
|
const mcpJsonPath = path17.join(dir, ".mcp.json");
|
|
5462
|
-
if (
|
|
5475
|
+
if (fs23.existsSync(mcpJsonPath)) {
|
|
5463
5476
|
try {
|
|
5464
|
-
const mcpJson = JSON.parse(
|
|
5477
|
+
const mcpJson = JSON.parse(fs23.readFileSync(mcpJsonPath, "utf-8"));
|
|
5465
5478
|
if (mcpJson.mcpServers) {
|
|
5466
5479
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
5467
5480
|
items.push({
|
|
@@ -5477,7 +5490,7 @@ function scanLocalState(dir) {
|
|
|
5477
5490
|
}
|
|
5478
5491
|
}
|
|
5479
5492
|
const agentsMdPath = path17.join(dir, "AGENTS.md");
|
|
5480
|
-
if (
|
|
5493
|
+
if (fs23.existsSync(agentsMdPath)) {
|
|
5481
5494
|
items.push({
|
|
5482
5495
|
type: "rule",
|
|
5483
5496
|
platform: "codex",
|
|
@@ -5487,11 +5500,11 @@ function scanLocalState(dir) {
|
|
|
5487
5500
|
});
|
|
5488
5501
|
}
|
|
5489
5502
|
const codexSkillsDir = path17.join(dir, ".agents", "skills");
|
|
5490
|
-
if (
|
|
5503
|
+
if (fs23.existsSync(codexSkillsDir)) {
|
|
5491
5504
|
try {
|
|
5492
|
-
for (const name of
|
|
5505
|
+
for (const name of fs23.readdirSync(codexSkillsDir)) {
|
|
5493
5506
|
const skillFile = path17.join(codexSkillsDir, name, "SKILL.md");
|
|
5494
|
-
if (
|
|
5507
|
+
if (fs23.existsSync(skillFile)) {
|
|
5495
5508
|
items.push({
|
|
5496
5509
|
type: "skill",
|
|
5497
5510
|
platform: "codex",
|
|
@@ -5505,7 +5518,7 @@ function scanLocalState(dir) {
|
|
|
5505
5518
|
}
|
|
5506
5519
|
}
|
|
5507
5520
|
const cursorrulesPath = path17.join(dir, ".cursorrules");
|
|
5508
|
-
if (
|
|
5521
|
+
if (fs23.existsSync(cursorrulesPath)) {
|
|
5509
5522
|
items.push({
|
|
5510
5523
|
type: "rule",
|
|
5511
5524
|
platform: "cursor",
|
|
@@ -5515,8 +5528,8 @@ function scanLocalState(dir) {
|
|
|
5515
5528
|
});
|
|
5516
5529
|
}
|
|
5517
5530
|
const cursorRulesDir = path17.join(dir, ".cursor", "rules");
|
|
5518
|
-
if (
|
|
5519
|
-
for (const file of
|
|
5531
|
+
if (fs23.existsSync(cursorRulesDir)) {
|
|
5532
|
+
for (const file of fs23.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
5520
5533
|
const filePath = path17.join(cursorRulesDir, file);
|
|
5521
5534
|
items.push({
|
|
5522
5535
|
type: "rule",
|
|
@@ -5528,11 +5541,11 @@ function scanLocalState(dir) {
|
|
|
5528
5541
|
}
|
|
5529
5542
|
}
|
|
5530
5543
|
const cursorSkillsDir = path17.join(dir, ".cursor", "skills");
|
|
5531
|
-
if (
|
|
5544
|
+
if (fs23.existsSync(cursorSkillsDir)) {
|
|
5532
5545
|
try {
|
|
5533
|
-
for (const name of
|
|
5546
|
+
for (const name of fs23.readdirSync(cursorSkillsDir)) {
|
|
5534
5547
|
const skillFile = path17.join(cursorSkillsDir, name, "SKILL.md");
|
|
5535
|
-
if (
|
|
5548
|
+
if (fs23.existsSync(skillFile)) {
|
|
5536
5549
|
items.push({
|
|
5537
5550
|
type: "skill",
|
|
5538
5551
|
platform: "cursor",
|
|
@@ -5546,9 +5559,9 @@ function scanLocalState(dir) {
|
|
|
5546
5559
|
}
|
|
5547
5560
|
}
|
|
5548
5561
|
const cursorMcpPath = path17.join(dir, ".cursor", "mcp.json");
|
|
5549
|
-
if (
|
|
5562
|
+
if (fs23.existsSync(cursorMcpPath)) {
|
|
5550
5563
|
try {
|
|
5551
|
-
const mcpJson = JSON.parse(
|
|
5564
|
+
const mcpJson = JSON.parse(fs23.readFileSync(cursorMcpPath, "utf-8"));
|
|
5552
5565
|
if (mcpJson.mcpServers) {
|
|
5553
5566
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
5554
5567
|
items.push({
|
|
@@ -5566,7 +5579,7 @@ function scanLocalState(dir) {
|
|
|
5566
5579
|
return items;
|
|
5567
5580
|
}
|
|
5568
5581
|
function hashFile(filePath) {
|
|
5569
|
-
const text =
|
|
5582
|
+
const text = fs23.readFileSync(filePath, "utf-8");
|
|
5570
5583
|
return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
5571
5584
|
}
|
|
5572
5585
|
function hashJson(obj) {
|
|
@@ -5881,7 +5894,7 @@ async function recommendCommand(options) {
|
|
|
5881
5894
|
...extractTopDeps()
|
|
5882
5895
|
].filter(Boolean))];
|
|
5883
5896
|
if (technologies.length === 0) {
|
|
5884
|
-
console.log(
|
|
5897
|
+
console.log(chalk10.yellow("Could not detect any languages or dependencies. Try running from a project root."));
|
|
5885
5898
|
throw new Error("__exit__");
|
|
5886
5899
|
}
|
|
5887
5900
|
const primaryPlatform = platforms.includes("claude") ? "claude" : platforms[0];
|
|
@@ -5898,7 +5911,7 @@ async function recommendCommand(options) {
|
|
|
5898
5911
|
return;
|
|
5899
5912
|
}
|
|
5900
5913
|
searchSpinner.succeed(
|
|
5901
|
-
`Found ${allCandidates.length} skills` + (filteredCount > 0 ?
|
|
5914
|
+
`Found ${allCandidates.length} skills` + (filteredCount > 0 ? chalk10.dim(` (${filteredCount} already installed)`) : "")
|
|
5902
5915
|
);
|
|
5903
5916
|
let results;
|
|
5904
5917
|
const config = loadConfig();
|
|
@@ -5932,7 +5945,7 @@ async function recommendCommand(options) {
|
|
|
5932
5945
|
}
|
|
5933
5946
|
const unavailableCount = results.length - available.length;
|
|
5934
5947
|
fetchSpinner.succeed(
|
|
5935
|
-
`${available.length} installable skill${available.length > 1 ? "s" : ""}` + (unavailableCount > 0 ?
|
|
5948
|
+
`${available.length} installable skill${available.length > 1 ? "s" : ""}` + (unavailableCount > 0 ? chalk10.dim(` (${unavailableCount} unavailable)`) : "")
|
|
5936
5949
|
);
|
|
5937
5950
|
const selected = await interactiveSelect2(available);
|
|
5938
5951
|
if (selected?.length) {
|
|
@@ -5951,27 +5964,27 @@ async function interactiveSelect2(recs) {
|
|
|
5951
5964
|
const hasScores = recs.some((r) => r.score > 0);
|
|
5952
5965
|
function render() {
|
|
5953
5966
|
const lines = [];
|
|
5954
|
-
lines.push(
|
|
5967
|
+
lines.push(chalk10.bold(" Recommendations"));
|
|
5955
5968
|
lines.push("");
|
|
5956
5969
|
if (hasScores) {
|
|
5957
|
-
lines.push(` ${
|
|
5970
|
+
lines.push(` ${chalk10.dim("Score".padEnd(7))} ${chalk10.dim("Name".padEnd(28))} ${chalk10.dim("Why")}`);
|
|
5958
5971
|
} else {
|
|
5959
|
-
lines.push(` ${
|
|
5972
|
+
lines.push(` ${chalk10.dim("Name".padEnd(30))} ${chalk10.dim("Technology".padEnd(18))} ${chalk10.dim("Source")}`);
|
|
5960
5973
|
}
|
|
5961
|
-
lines.push(
|
|
5974
|
+
lines.push(chalk10.dim(" " + "\u2500".repeat(70)));
|
|
5962
5975
|
for (let i = 0; i < recs.length; i++) {
|
|
5963
5976
|
const rec = recs[i];
|
|
5964
|
-
const check = selected.has(i) ?
|
|
5965
|
-
const ptr = i === cursor ?
|
|
5977
|
+
const check = selected.has(i) ? chalk10.green("[x]") : "[ ]";
|
|
5978
|
+
const ptr = i === cursor ? chalk10.cyan(">") : " ";
|
|
5966
5979
|
if (hasScores) {
|
|
5967
|
-
const scoreColor = rec.score >= 90 ?
|
|
5968
|
-
lines.push(` ${ptr} ${check} ${scoreColor(String(rec.score).padStart(3))} ${rec.name.padEnd(26)} ${
|
|
5980
|
+
const scoreColor = rec.score >= 90 ? chalk10.green : rec.score >= 70 ? chalk10.yellow : chalk10.dim;
|
|
5981
|
+
lines.push(` ${ptr} ${check} ${scoreColor(String(rec.score).padStart(3))} ${rec.name.padEnd(26)} ${chalk10.dim(rec.reason.slice(0, 40))}`);
|
|
5969
5982
|
} else {
|
|
5970
|
-
lines.push(` ${ptr} ${check} ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${
|
|
5983
|
+
lines.push(` ${ptr} ${check} ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${chalk10.dim(rec.source_url || "")}`);
|
|
5971
5984
|
}
|
|
5972
5985
|
}
|
|
5973
5986
|
lines.push("");
|
|
5974
|
-
lines.push(
|
|
5987
|
+
lines.push(chalk10.dim(" \u2191\u2193 navigate \u23B5 toggle a all n none \u23CE install q cancel"));
|
|
5975
5988
|
return lines.join("\n");
|
|
5976
5989
|
}
|
|
5977
5990
|
function draw(initial) {
|
|
@@ -6020,7 +6033,7 @@ async function interactiveSelect2(recs) {
|
|
|
6020
6033
|
case "\n":
|
|
6021
6034
|
cleanup();
|
|
6022
6035
|
if (selected.size === 0) {
|
|
6023
|
-
console.log(
|
|
6036
|
+
console.log(chalk10.dim("\n No skills selected.\n"));
|
|
6024
6037
|
resolve2(null);
|
|
6025
6038
|
} else {
|
|
6026
6039
|
resolve2(Array.from(selected).sort().map((i) => recs[i]));
|
|
@@ -6030,7 +6043,7 @@ async function interactiveSelect2(recs) {
|
|
|
6030
6043
|
case "\x1B":
|
|
6031
6044
|
case "":
|
|
6032
6045
|
cleanup();
|
|
6033
|
-
console.log(
|
|
6046
|
+
console.log(chalk10.dim("\n Cancelled.\n"));
|
|
6034
6047
|
resolve2(null);
|
|
6035
6048
|
break;
|
|
6036
6049
|
}
|
|
@@ -6093,7 +6106,7 @@ async function installSkills(recs, platforms, contentMap) {
|
|
|
6093
6106
|
if (installed.length > 0) {
|
|
6094
6107
|
spinner.succeed(`Installed ${installed.length} file${installed.length > 1 ? "s" : ""}`);
|
|
6095
6108
|
for (const p of installed) {
|
|
6096
|
-
console.log(
|
|
6109
|
+
console.log(chalk10.green(` \u2713 ${p}`));
|
|
6097
6110
|
}
|
|
6098
6111
|
} else {
|
|
6099
6112
|
spinner.fail("No skills were installed");
|
|
@@ -6102,25 +6115,25 @@ async function installSkills(recs, platforms, contentMap) {
|
|
|
6102
6115
|
}
|
|
6103
6116
|
function printRecommendations(recs) {
|
|
6104
6117
|
const hasScores = recs.some((r) => r.score > 0);
|
|
6105
|
-
console.log(
|
|
6118
|
+
console.log(chalk10.bold("\n Recommendations\n"));
|
|
6106
6119
|
if (hasScores) {
|
|
6107
|
-
console.log(` ${
|
|
6120
|
+
console.log(` ${chalk10.dim("Score".padEnd(7))} ${chalk10.dim("Name".padEnd(28))} ${chalk10.dim("Why")}`);
|
|
6108
6121
|
} else {
|
|
6109
|
-
console.log(` ${
|
|
6122
|
+
console.log(` ${chalk10.dim("Name".padEnd(30))} ${chalk10.dim("Technology".padEnd(18))} ${chalk10.dim("Source")}`);
|
|
6110
6123
|
}
|
|
6111
|
-
console.log(
|
|
6124
|
+
console.log(chalk10.dim(" " + "\u2500".repeat(70)));
|
|
6112
6125
|
for (const rec of recs) {
|
|
6113
6126
|
if (hasScores) {
|
|
6114
|
-
console.log(` ${String(rec.score).padStart(3)} ${rec.name.padEnd(26)} ${
|
|
6127
|
+
console.log(` ${String(rec.score).padStart(3)} ${rec.name.padEnd(26)} ${chalk10.dim(rec.reason.slice(0, 50))}`);
|
|
6115
6128
|
} else {
|
|
6116
|
-
console.log(` ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${
|
|
6129
|
+
console.log(` ${rec.name.padEnd(28)} ${rec.detected_technology.padEnd(16)} ${chalk10.dim(rec.source_url || "")}`);
|
|
6117
6130
|
}
|
|
6118
6131
|
}
|
|
6119
6132
|
console.log("");
|
|
6120
6133
|
}
|
|
6121
6134
|
|
|
6122
6135
|
// src/commands/score.ts
|
|
6123
|
-
import
|
|
6136
|
+
import chalk11 from "chalk";
|
|
6124
6137
|
async function scoreCommand(options) {
|
|
6125
6138
|
const dir = process.cwd();
|
|
6126
6139
|
const target = options.agent ?? readState()?.targetAgent;
|
|
@@ -6134,22 +6147,22 @@ async function scoreCommand(options) {
|
|
|
6134
6147
|
return;
|
|
6135
6148
|
}
|
|
6136
6149
|
displayScore(result);
|
|
6137
|
-
const separator =
|
|
6150
|
+
const separator = chalk11.gray(" " + "\u2500".repeat(53));
|
|
6138
6151
|
console.log(separator);
|
|
6139
6152
|
if (result.score < 40) {
|
|
6140
|
-
console.log(
|
|
6153
|
+
console.log(chalk11.gray(" Run ") + chalk11.hex("#83D1EB")("caliber onboard") + chalk11.gray(" to generate a complete, optimized setup."));
|
|
6141
6154
|
} else if (result.score < 70) {
|
|
6142
|
-
console.log(
|
|
6155
|
+
console.log(chalk11.gray(" Run ") + chalk11.hex("#83D1EB")("caliber onboard") + chalk11.gray(" to improve your setup."));
|
|
6143
6156
|
} else {
|
|
6144
|
-
console.log(
|
|
6157
|
+
console.log(chalk11.green(" Looking good!") + chalk11.gray(" Run ") + chalk11.hex("#83D1EB")("caliber regenerate") + chalk11.gray(" to rebuild from scratch."));
|
|
6145
6158
|
}
|
|
6146
6159
|
console.log("");
|
|
6147
6160
|
}
|
|
6148
6161
|
|
|
6149
6162
|
// src/commands/refresh.ts
|
|
6150
|
-
import
|
|
6163
|
+
import fs25 from "fs";
|
|
6151
6164
|
import path19 from "path";
|
|
6152
|
-
import
|
|
6165
|
+
import chalk12 from "chalk";
|
|
6153
6166
|
import ora6 from "ora";
|
|
6154
6167
|
|
|
6155
6168
|
// src/lib/git-diff.ts
|
|
@@ -6225,37 +6238,37 @@ function collectDiff(lastSha) {
|
|
|
6225
6238
|
}
|
|
6226
6239
|
|
|
6227
6240
|
// src/writers/refresh.ts
|
|
6228
|
-
import
|
|
6241
|
+
import fs24 from "fs";
|
|
6229
6242
|
import path18 from "path";
|
|
6230
6243
|
function writeRefreshDocs(docs) {
|
|
6231
6244
|
const written = [];
|
|
6232
6245
|
if (docs.claudeMd) {
|
|
6233
|
-
|
|
6246
|
+
fs24.writeFileSync("CLAUDE.md", docs.claudeMd);
|
|
6234
6247
|
written.push("CLAUDE.md");
|
|
6235
6248
|
}
|
|
6236
6249
|
if (docs.readmeMd) {
|
|
6237
|
-
|
|
6250
|
+
fs24.writeFileSync("README.md", docs.readmeMd);
|
|
6238
6251
|
written.push("README.md");
|
|
6239
6252
|
}
|
|
6240
6253
|
if (docs.cursorrules) {
|
|
6241
|
-
|
|
6254
|
+
fs24.writeFileSync(".cursorrules", docs.cursorrules);
|
|
6242
6255
|
written.push(".cursorrules");
|
|
6243
6256
|
}
|
|
6244
6257
|
if (docs.cursorRules) {
|
|
6245
6258
|
const rulesDir = path18.join(".cursor", "rules");
|
|
6246
|
-
if (!
|
|
6259
|
+
if (!fs24.existsSync(rulesDir)) fs24.mkdirSync(rulesDir, { recursive: true });
|
|
6247
6260
|
for (const rule of docs.cursorRules) {
|
|
6248
6261
|
const filePath = path18.join(rulesDir, rule.filename);
|
|
6249
|
-
|
|
6262
|
+
fs24.writeFileSync(filePath, rule.content);
|
|
6250
6263
|
written.push(filePath);
|
|
6251
6264
|
}
|
|
6252
6265
|
}
|
|
6253
6266
|
if (docs.claudeSkills) {
|
|
6254
6267
|
const skillsDir = path18.join(".claude", "skills");
|
|
6255
|
-
if (!
|
|
6268
|
+
if (!fs24.existsSync(skillsDir)) fs24.mkdirSync(skillsDir, { recursive: true });
|
|
6256
6269
|
for (const skill of docs.claudeSkills) {
|
|
6257
6270
|
const filePath = path18.join(skillsDir, skill.filename);
|
|
6258
|
-
|
|
6271
|
+
fs24.writeFileSync(filePath, skill.content);
|
|
6259
6272
|
written.push(filePath);
|
|
6260
6273
|
}
|
|
6261
6274
|
}
|
|
@@ -6332,11 +6345,11 @@ function log(quiet, ...args) {
|
|
|
6332
6345
|
function discoverGitRepos(parentDir) {
|
|
6333
6346
|
const repos = [];
|
|
6334
6347
|
try {
|
|
6335
|
-
const entries =
|
|
6348
|
+
const entries = fs25.readdirSync(parentDir, { withFileTypes: true });
|
|
6336
6349
|
for (const entry of entries) {
|
|
6337
6350
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
6338
6351
|
const childPath = path19.join(parentDir, entry.name);
|
|
6339
|
-
if (
|
|
6352
|
+
if (fs25.existsSync(path19.join(childPath, ".git"))) {
|
|
6340
6353
|
repos.push(childPath);
|
|
6341
6354
|
}
|
|
6342
6355
|
}
|
|
@@ -6346,7 +6359,7 @@ function discoverGitRepos(parentDir) {
|
|
|
6346
6359
|
}
|
|
6347
6360
|
async function refreshSingleRepo(repoDir, options) {
|
|
6348
6361
|
const quiet = !!options.quiet;
|
|
6349
|
-
const prefix = options.label ? `${
|
|
6362
|
+
const prefix = options.label ? `${chalk12.bold(options.label)} ` : "";
|
|
6350
6363
|
const state = readState();
|
|
6351
6364
|
const lastSha = state?.lastRefreshSha ?? null;
|
|
6352
6365
|
const diff = collectDiff(lastSha);
|
|
@@ -6355,7 +6368,7 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
6355
6368
|
if (currentSha) {
|
|
6356
6369
|
writeState({ lastRefreshSha: currentSha, lastRefreshTimestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
6357
6370
|
}
|
|
6358
|
-
log(quiet,
|
|
6371
|
+
log(quiet, chalk12.dim(`${prefix}No changes since last refresh.`));
|
|
6359
6372
|
return;
|
|
6360
6373
|
}
|
|
6361
6374
|
const spinner = quiet ? null : ora6(`${prefix}Analyzing changes...`).start();
|
|
@@ -6387,10 +6400,10 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
6387
6400
|
if (options.dryRun) {
|
|
6388
6401
|
spinner?.info(`${prefix}Dry run \u2014 would update:`);
|
|
6389
6402
|
for (const doc of response.docsUpdated) {
|
|
6390
|
-
console.log(` ${
|
|
6403
|
+
console.log(` ${chalk12.yellow("~")} ${doc}`);
|
|
6391
6404
|
}
|
|
6392
6405
|
if (response.changesSummary) {
|
|
6393
|
-
console.log(
|
|
6406
|
+
console.log(chalk12.dim(`
|
|
6394
6407
|
${response.changesSummary}`));
|
|
6395
6408
|
}
|
|
6396
6409
|
return;
|
|
@@ -6398,10 +6411,10 @@ async function refreshSingleRepo(repoDir, options) {
|
|
|
6398
6411
|
const written = writeRefreshDocs(response.updatedDocs);
|
|
6399
6412
|
spinner?.succeed(`${prefix}Updated ${written.length} doc${written.length === 1 ? "" : "s"}`);
|
|
6400
6413
|
for (const file of written) {
|
|
6401
|
-
log(quiet, ` ${
|
|
6414
|
+
log(quiet, ` ${chalk12.green("\u2713")} ${file}`);
|
|
6402
6415
|
}
|
|
6403
6416
|
if (response.changesSummary) {
|
|
6404
|
-
log(quiet,
|
|
6417
|
+
log(quiet, chalk12.dim(`
|
|
6405
6418
|
${response.changesSummary}`));
|
|
6406
6419
|
}
|
|
6407
6420
|
if (currentSha) {
|
|
@@ -6414,7 +6427,7 @@ async function refreshCommand(options) {
|
|
|
6414
6427
|
const config = loadConfig();
|
|
6415
6428
|
if (!config) {
|
|
6416
6429
|
if (quiet) return;
|
|
6417
|
-
console.log(
|
|
6430
|
+
console.log(chalk12.red("No LLM provider configured. Run ") + chalk12.hex("#83D1EB")("caliber config") + chalk12.red(" (e.g. choose Cursor) or set an API key."));
|
|
6418
6431
|
throw new Error("__exit__");
|
|
6419
6432
|
}
|
|
6420
6433
|
if (isGitRepo()) {
|
|
@@ -6424,10 +6437,10 @@ async function refreshCommand(options) {
|
|
|
6424
6437
|
const repos = discoverGitRepos(process.cwd());
|
|
6425
6438
|
if (repos.length === 0) {
|
|
6426
6439
|
if (quiet) return;
|
|
6427
|
-
console.log(
|
|
6440
|
+
console.log(chalk12.red("Not inside a git repository and no git repos found in child directories."));
|
|
6428
6441
|
throw new Error("__exit__");
|
|
6429
6442
|
}
|
|
6430
|
-
log(quiet,
|
|
6443
|
+
log(quiet, chalk12.dim(`Found ${repos.length} git repo${repos.length === 1 ? "" : "s"}
|
|
6431
6444
|
`));
|
|
6432
6445
|
const originalDir = process.cwd();
|
|
6433
6446
|
for (const repo of repos) {
|
|
@@ -6437,7 +6450,7 @@ async function refreshCommand(options) {
|
|
|
6437
6450
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
6438
6451
|
} catch (err) {
|
|
6439
6452
|
if (err instanceof Error && err.message === "__exit__") continue;
|
|
6440
|
-
log(quiet,
|
|
6453
|
+
log(quiet, chalk12.yellow(`${repoName}: refresh failed \u2014 ${err instanceof Error ? err.message : "unknown error"}`));
|
|
6441
6454
|
}
|
|
6442
6455
|
}
|
|
6443
6456
|
process.chdir(originalDir);
|
|
@@ -6445,13 +6458,13 @@ async function refreshCommand(options) {
|
|
|
6445
6458
|
if (err instanceof Error && err.message === "__exit__") throw err;
|
|
6446
6459
|
if (quiet) return;
|
|
6447
6460
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
6448
|
-
console.log(
|
|
6461
|
+
console.log(chalk12.red(`Refresh failed: ${msg}`));
|
|
6449
6462
|
throw new Error("__exit__");
|
|
6450
6463
|
}
|
|
6451
6464
|
}
|
|
6452
6465
|
|
|
6453
6466
|
// src/commands/hooks.ts
|
|
6454
|
-
import
|
|
6467
|
+
import chalk13 from "chalk";
|
|
6455
6468
|
var HOOKS = [
|
|
6456
6469
|
{
|
|
6457
6470
|
id: "session-end",
|
|
@@ -6471,13 +6484,13 @@ var HOOKS = [
|
|
|
6471
6484
|
}
|
|
6472
6485
|
];
|
|
6473
6486
|
function printStatus() {
|
|
6474
|
-
console.log(
|
|
6487
|
+
console.log(chalk13.bold("\n Hooks\n"));
|
|
6475
6488
|
for (const hook of HOOKS) {
|
|
6476
6489
|
const installed = hook.isInstalled();
|
|
6477
|
-
const icon = installed ?
|
|
6478
|
-
const state = installed ?
|
|
6490
|
+
const icon = installed ? chalk13.green("\u2713") : chalk13.dim("\u2717");
|
|
6491
|
+
const state = installed ? chalk13.green("enabled") : chalk13.dim("disabled");
|
|
6479
6492
|
console.log(` ${icon} ${hook.label.padEnd(26)} ${state}`);
|
|
6480
|
-
console.log(
|
|
6493
|
+
console.log(chalk13.dim(` ${hook.description}`));
|
|
6481
6494
|
}
|
|
6482
6495
|
console.log("");
|
|
6483
6496
|
}
|
|
@@ -6486,9 +6499,9 @@ async function hooksCommand(options) {
|
|
|
6486
6499
|
for (const hook of HOOKS) {
|
|
6487
6500
|
const result = hook.install();
|
|
6488
6501
|
if (result.alreadyInstalled) {
|
|
6489
|
-
console.log(
|
|
6502
|
+
console.log(chalk13.dim(` ${hook.label} already enabled.`));
|
|
6490
6503
|
} else {
|
|
6491
|
-
console.log(
|
|
6504
|
+
console.log(chalk13.green(" \u2713") + ` ${hook.label} enabled`);
|
|
6492
6505
|
}
|
|
6493
6506
|
}
|
|
6494
6507
|
return;
|
|
@@ -6497,9 +6510,9 @@ async function hooksCommand(options) {
|
|
|
6497
6510
|
for (const hook of HOOKS) {
|
|
6498
6511
|
const result = hook.remove();
|
|
6499
6512
|
if (result.notFound) {
|
|
6500
|
-
console.log(
|
|
6513
|
+
console.log(chalk13.dim(` ${hook.label} already disabled.`));
|
|
6501
6514
|
} else {
|
|
6502
|
-
console.log(
|
|
6515
|
+
console.log(chalk13.green(" \u2713") + ` ${hook.label} removed`);
|
|
6503
6516
|
}
|
|
6504
6517
|
}
|
|
6505
6518
|
return;
|
|
@@ -6514,18 +6527,18 @@ async function hooksCommand(options) {
|
|
|
6514
6527
|
const states = HOOKS.map((h) => h.isInstalled());
|
|
6515
6528
|
function render() {
|
|
6516
6529
|
const lines = [];
|
|
6517
|
-
lines.push(
|
|
6530
|
+
lines.push(chalk13.bold(" Hooks"));
|
|
6518
6531
|
lines.push("");
|
|
6519
6532
|
for (let i = 0; i < HOOKS.length; i++) {
|
|
6520
6533
|
const hook = HOOKS[i];
|
|
6521
6534
|
const enabled = states[i];
|
|
6522
|
-
const toggle = enabled ?
|
|
6523
|
-
const ptr = i === cursor ?
|
|
6535
|
+
const toggle = enabled ? chalk13.green("[on] ") : chalk13.dim("[off]");
|
|
6536
|
+
const ptr = i === cursor ? chalk13.cyan(">") : " ";
|
|
6524
6537
|
lines.push(` ${ptr} ${toggle} ${hook.label}`);
|
|
6525
|
-
lines.push(
|
|
6538
|
+
lines.push(chalk13.dim(` ${hook.description}`));
|
|
6526
6539
|
}
|
|
6527
6540
|
lines.push("");
|
|
6528
|
-
lines.push(
|
|
6541
|
+
lines.push(chalk13.dim(" \u2191\u2193 navigate \u23B5 toggle a all on n all off \u23CE apply q cancel"));
|
|
6529
6542
|
return lines.join("\n");
|
|
6530
6543
|
}
|
|
6531
6544
|
function draw(initial) {
|
|
@@ -6556,16 +6569,16 @@ async function hooksCommand(options) {
|
|
|
6556
6569
|
const wantEnabled = states[i];
|
|
6557
6570
|
if (wantEnabled && !wasInstalled) {
|
|
6558
6571
|
hook.install();
|
|
6559
|
-
console.log(
|
|
6572
|
+
console.log(chalk13.green(" \u2713") + ` ${hook.label} enabled`);
|
|
6560
6573
|
changed++;
|
|
6561
6574
|
} else if (!wantEnabled && wasInstalled) {
|
|
6562
6575
|
hook.remove();
|
|
6563
|
-
console.log(
|
|
6576
|
+
console.log(chalk13.green(" \u2713") + ` ${hook.label} disabled`);
|
|
6564
6577
|
changed++;
|
|
6565
6578
|
}
|
|
6566
6579
|
}
|
|
6567
6580
|
if (changed === 0) {
|
|
6568
|
-
console.log(
|
|
6581
|
+
console.log(chalk13.dim(" No changes."));
|
|
6569
6582
|
}
|
|
6570
6583
|
console.log("");
|
|
6571
6584
|
}
|
|
@@ -6601,7 +6614,7 @@ async function hooksCommand(options) {
|
|
|
6601
6614
|
case "\x1B":
|
|
6602
6615
|
case "":
|
|
6603
6616
|
cleanup();
|
|
6604
|
-
console.log(
|
|
6617
|
+
console.log(chalk13.dim("\n Cancelled.\n"));
|
|
6605
6618
|
resolve2();
|
|
6606
6619
|
break;
|
|
6607
6620
|
}
|
|
@@ -6611,48 +6624,48 @@ async function hooksCommand(options) {
|
|
|
6611
6624
|
}
|
|
6612
6625
|
|
|
6613
6626
|
// src/commands/config.ts
|
|
6614
|
-
import
|
|
6627
|
+
import chalk14 from "chalk";
|
|
6615
6628
|
async function configCommand() {
|
|
6616
6629
|
const existing = loadConfig();
|
|
6617
6630
|
if (existing) {
|
|
6618
6631
|
const displayModel = existing.model === "default" && existing.provider === "claude-cli" ? process.env.ANTHROPIC_MODEL || "default (inherited from Claude Code)" : existing.model;
|
|
6619
6632
|
const fastModel = getFastModel();
|
|
6620
|
-
console.log(
|
|
6621
|
-
console.log(` Provider: ${
|
|
6622
|
-
console.log(` Model: ${
|
|
6633
|
+
console.log(chalk14.bold("\nCurrent Configuration\n"));
|
|
6634
|
+
console.log(` Provider: ${chalk14.cyan(existing.provider)}`);
|
|
6635
|
+
console.log(` Model: ${chalk14.cyan(displayModel)}`);
|
|
6623
6636
|
if (fastModel) {
|
|
6624
|
-
console.log(` Scan: ${
|
|
6637
|
+
console.log(` Scan: ${chalk14.cyan(fastModel)}`);
|
|
6625
6638
|
}
|
|
6626
6639
|
if (existing.apiKey) {
|
|
6627
6640
|
const masked = existing.apiKey.slice(0, 8) + "..." + existing.apiKey.slice(-4);
|
|
6628
|
-
console.log(` API Key: ${
|
|
6641
|
+
console.log(` API Key: ${chalk14.dim(masked)}`);
|
|
6629
6642
|
}
|
|
6630
6643
|
if (existing.provider === "cursor") {
|
|
6631
|
-
console.log(` Seat: ${
|
|
6644
|
+
console.log(` Seat: ${chalk14.dim("Cursor (agent acp)")}`);
|
|
6632
6645
|
}
|
|
6633
6646
|
if (existing.provider === "claude-cli") {
|
|
6634
|
-
console.log(` Seat: ${
|
|
6647
|
+
console.log(` Seat: ${chalk14.dim("Claude Code (claude -p)")}`);
|
|
6635
6648
|
}
|
|
6636
6649
|
if (existing.baseUrl) {
|
|
6637
|
-
console.log(` Base URL: ${
|
|
6650
|
+
console.log(` Base URL: ${chalk14.dim(existing.baseUrl)}`);
|
|
6638
6651
|
}
|
|
6639
6652
|
if (existing.vertexProjectId) {
|
|
6640
|
-
console.log(` Vertex Project: ${
|
|
6641
|
-
console.log(` Vertex Region: ${
|
|
6653
|
+
console.log(` Vertex Project: ${chalk14.dim(existing.vertexProjectId)}`);
|
|
6654
|
+
console.log(` Vertex Region: ${chalk14.dim(existing.vertexRegion || "us-east5")}`);
|
|
6642
6655
|
}
|
|
6643
|
-
console.log(` Source: ${
|
|
6656
|
+
console.log(` Source: ${chalk14.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())}`);
|
|
6644
6657
|
console.log("");
|
|
6645
6658
|
}
|
|
6646
6659
|
await runInteractiveProviderSetup();
|
|
6647
|
-
console.log(
|
|
6648
|
-
console.log(
|
|
6660
|
+
console.log(chalk14.green("\n\u2713 Configuration saved"));
|
|
6661
|
+
console.log(chalk14.dim(` ${getConfigFilePath()}
|
|
6649
6662
|
`));
|
|
6650
|
-
console.log(
|
|
6651
|
-
console.log(
|
|
6663
|
+
console.log(chalk14.dim(" You can also set environment variables instead:"));
|
|
6664
|
+
console.log(chalk14.dim(" ANTHROPIC_API_KEY, OPENAI_API_KEY, VERTEX_PROJECT_ID, CALIBER_USE_CURSOR_SEAT=1, or CALIBER_USE_CLAUDE_CLI=1\n"));
|
|
6652
6665
|
}
|
|
6653
6666
|
|
|
6654
6667
|
// src/commands/learn.ts
|
|
6655
|
-
import
|
|
6668
|
+
import chalk15 from "chalk";
|
|
6656
6669
|
|
|
6657
6670
|
// src/learner/stdin.ts
|
|
6658
6671
|
var STDIN_TIMEOUT_MS = 5e3;
|
|
@@ -6683,7 +6696,7 @@ function readStdin() {
|
|
|
6683
6696
|
|
|
6684
6697
|
// src/learner/storage.ts
|
|
6685
6698
|
init_constants();
|
|
6686
|
-
import
|
|
6699
|
+
import fs26 from "fs";
|
|
6687
6700
|
import path20 from "path";
|
|
6688
6701
|
var MAX_RESPONSE_LENGTH = 2e3;
|
|
6689
6702
|
var DEFAULT_STATE = {
|
|
@@ -6692,8 +6705,8 @@ var DEFAULT_STATE = {
|
|
|
6692
6705
|
lastAnalysisTimestamp: null
|
|
6693
6706
|
};
|
|
6694
6707
|
function ensureLearningDir() {
|
|
6695
|
-
if (!
|
|
6696
|
-
|
|
6708
|
+
if (!fs26.existsSync(LEARNING_DIR)) {
|
|
6709
|
+
fs26.mkdirSync(LEARNING_DIR, { recursive: true });
|
|
6697
6710
|
}
|
|
6698
6711
|
}
|
|
6699
6712
|
function sessionFilePath() {
|
|
@@ -6711,49 +6724,49 @@ function appendEvent(event) {
|
|
|
6711
6724
|
ensureLearningDir();
|
|
6712
6725
|
const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
|
|
6713
6726
|
const filePath = sessionFilePath();
|
|
6714
|
-
|
|
6727
|
+
fs26.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
|
|
6715
6728
|
const count = getEventCount();
|
|
6716
6729
|
if (count > LEARNING_MAX_EVENTS) {
|
|
6717
|
-
const lines =
|
|
6730
|
+
const lines = fs26.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
6718
6731
|
const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
|
|
6719
|
-
|
|
6732
|
+
fs26.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
6720
6733
|
}
|
|
6721
6734
|
}
|
|
6722
6735
|
function readAllEvents() {
|
|
6723
6736
|
const filePath = sessionFilePath();
|
|
6724
|
-
if (!
|
|
6725
|
-
const lines =
|
|
6737
|
+
if (!fs26.existsSync(filePath)) return [];
|
|
6738
|
+
const lines = fs26.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
6726
6739
|
return lines.map((line) => JSON.parse(line));
|
|
6727
6740
|
}
|
|
6728
6741
|
function getEventCount() {
|
|
6729
6742
|
const filePath = sessionFilePath();
|
|
6730
|
-
if (!
|
|
6731
|
-
const content =
|
|
6743
|
+
if (!fs26.existsSync(filePath)) return 0;
|
|
6744
|
+
const content = fs26.readFileSync(filePath, "utf-8");
|
|
6732
6745
|
return content.split("\n").filter(Boolean).length;
|
|
6733
6746
|
}
|
|
6734
6747
|
function clearSession() {
|
|
6735
6748
|
const filePath = sessionFilePath();
|
|
6736
|
-
if (
|
|
6749
|
+
if (fs26.existsSync(filePath)) fs26.unlinkSync(filePath);
|
|
6737
6750
|
}
|
|
6738
6751
|
function readState2() {
|
|
6739
6752
|
const filePath = stateFilePath();
|
|
6740
|
-
if (!
|
|
6753
|
+
if (!fs26.existsSync(filePath)) return { ...DEFAULT_STATE };
|
|
6741
6754
|
try {
|
|
6742
|
-
return JSON.parse(
|
|
6755
|
+
return JSON.parse(fs26.readFileSync(filePath, "utf-8"));
|
|
6743
6756
|
} catch {
|
|
6744
6757
|
return { ...DEFAULT_STATE };
|
|
6745
6758
|
}
|
|
6746
6759
|
}
|
|
6747
6760
|
function writeState2(state) {
|
|
6748
6761
|
ensureLearningDir();
|
|
6749
|
-
|
|
6762
|
+
fs26.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
|
|
6750
6763
|
}
|
|
6751
6764
|
function resetState() {
|
|
6752
6765
|
writeState2({ ...DEFAULT_STATE });
|
|
6753
6766
|
}
|
|
6754
6767
|
|
|
6755
6768
|
// src/learner/writer.ts
|
|
6756
|
-
import
|
|
6769
|
+
import fs27 from "fs";
|
|
6757
6770
|
import path21 from "path";
|
|
6758
6771
|
var LEARNED_START = "<!-- caliber:learned -->";
|
|
6759
6772
|
var LEARNED_END = "<!-- /caliber:learned -->";
|
|
@@ -6774,8 +6787,8 @@ function writeLearnedContent(update) {
|
|
|
6774
6787
|
function writeLearnedSection(content) {
|
|
6775
6788
|
const claudeMdPath = "CLAUDE.md";
|
|
6776
6789
|
let existing = "";
|
|
6777
|
-
if (
|
|
6778
|
-
existing =
|
|
6790
|
+
if (fs27.existsSync(claudeMdPath)) {
|
|
6791
|
+
existing = fs27.readFileSync(claudeMdPath, "utf-8");
|
|
6779
6792
|
}
|
|
6780
6793
|
const section = `${LEARNED_START}
|
|
6781
6794
|
${content}
|
|
@@ -6789,15 +6802,15 @@ ${LEARNED_END}`;
|
|
|
6789
6802
|
const separator = existing.endsWith("\n") || existing === "" ? "" : "\n";
|
|
6790
6803
|
updated = existing + separator + "\n" + section + "\n";
|
|
6791
6804
|
}
|
|
6792
|
-
|
|
6805
|
+
fs27.writeFileSync(claudeMdPath, updated);
|
|
6793
6806
|
}
|
|
6794
6807
|
function writeLearnedSkill(skill) {
|
|
6795
6808
|
const skillDir = path21.join(".claude", "skills", skill.name);
|
|
6796
|
-
if (!
|
|
6809
|
+
if (!fs27.existsSync(skillDir)) fs27.mkdirSync(skillDir, { recursive: true });
|
|
6797
6810
|
const skillPath = path21.join(skillDir, "SKILL.md");
|
|
6798
|
-
if (!skill.isNew &&
|
|
6799
|
-
const existing =
|
|
6800
|
-
|
|
6811
|
+
if (!skill.isNew && fs27.existsSync(skillPath)) {
|
|
6812
|
+
const existing = fs27.readFileSync(skillPath, "utf-8");
|
|
6813
|
+
fs27.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
|
|
6801
6814
|
} else {
|
|
6802
6815
|
const frontmatter = [
|
|
6803
6816
|
"---",
|
|
@@ -6806,14 +6819,14 @@ function writeLearnedSkill(skill) {
|
|
|
6806
6819
|
"---",
|
|
6807
6820
|
""
|
|
6808
6821
|
].join("\n");
|
|
6809
|
-
|
|
6822
|
+
fs27.writeFileSync(skillPath, frontmatter + skill.content);
|
|
6810
6823
|
}
|
|
6811
6824
|
return skillPath;
|
|
6812
6825
|
}
|
|
6813
6826
|
function readLearnedSection() {
|
|
6814
6827
|
const claudeMdPath = "CLAUDE.md";
|
|
6815
|
-
if (!
|
|
6816
|
-
const content =
|
|
6828
|
+
if (!fs27.existsSync(claudeMdPath)) return null;
|
|
6829
|
+
const content = fs27.readFileSync(claudeMdPath, "utf-8");
|
|
6817
6830
|
const startIdx = content.indexOf(LEARNED_START);
|
|
6818
6831
|
const endIdx = content.indexOf(LEARNED_END);
|
|
6819
6832
|
if (startIdx === -1 || endIdx === -1) return null;
|
|
@@ -6953,53 +6966,53 @@ async function learnFinalizeCommand() {
|
|
|
6953
6966
|
async function learnInstallCommand() {
|
|
6954
6967
|
const result = installLearningHooks();
|
|
6955
6968
|
if (result.alreadyInstalled) {
|
|
6956
|
-
console.log(
|
|
6969
|
+
console.log(chalk15.dim("Learning hooks already installed."));
|
|
6957
6970
|
return;
|
|
6958
6971
|
}
|
|
6959
|
-
console.log(
|
|
6960
|
-
console.log(
|
|
6961
|
-
console.log(
|
|
6972
|
+
console.log(chalk15.green("\u2713") + " Learning hooks installed in .claude/settings.json");
|
|
6973
|
+
console.log(chalk15.dim(" PostToolUse, PostToolUseFailure, and SessionEnd hooks active."));
|
|
6974
|
+
console.log(chalk15.dim(" Session learnings will be written to CLAUDE.md and skills."));
|
|
6962
6975
|
}
|
|
6963
6976
|
async function learnRemoveCommand() {
|
|
6964
6977
|
const result = removeLearningHooks();
|
|
6965
6978
|
if (result.notFound) {
|
|
6966
|
-
console.log(
|
|
6979
|
+
console.log(chalk15.dim("Learning hooks not found."));
|
|
6967
6980
|
return;
|
|
6968
6981
|
}
|
|
6969
|
-
console.log(
|
|
6982
|
+
console.log(chalk15.green("\u2713") + " Learning hooks removed from .claude/settings.json");
|
|
6970
6983
|
}
|
|
6971
6984
|
async function learnStatusCommand() {
|
|
6972
6985
|
const installed = areLearningHooksInstalled();
|
|
6973
6986
|
const state = readState2();
|
|
6974
6987
|
const eventCount = getEventCount();
|
|
6975
|
-
console.log(
|
|
6988
|
+
console.log(chalk15.bold("Session Learning Status"));
|
|
6976
6989
|
console.log();
|
|
6977
6990
|
if (installed) {
|
|
6978
|
-
console.log(
|
|
6991
|
+
console.log(chalk15.green("\u2713") + " Learning hooks are " + chalk15.green("installed"));
|
|
6979
6992
|
} else {
|
|
6980
|
-
console.log(
|
|
6981
|
-
console.log(
|
|
6993
|
+
console.log(chalk15.dim("\u2717") + " Learning hooks are " + chalk15.yellow("not installed"));
|
|
6994
|
+
console.log(chalk15.dim(" Run `caliber learn install` to enable session learning."));
|
|
6982
6995
|
}
|
|
6983
6996
|
console.log();
|
|
6984
|
-
console.log(`Events recorded: ${
|
|
6985
|
-
console.log(`Total this session: ${
|
|
6997
|
+
console.log(`Events recorded: ${chalk15.cyan(String(eventCount))}`);
|
|
6998
|
+
console.log(`Total this session: ${chalk15.cyan(String(state.eventCount))}`);
|
|
6986
6999
|
if (state.lastAnalysisTimestamp) {
|
|
6987
|
-
console.log(`Last analysis: ${
|
|
7000
|
+
console.log(`Last analysis: ${chalk15.cyan(state.lastAnalysisTimestamp)}`);
|
|
6988
7001
|
} else {
|
|
6989
|
-
console.log(`Last analysis: ${
|
|
7002
|
+
console.log(`Last analysis: ${chalk15.dim("none")}`);
|
|
6990
7003
|
}
|
|
6991
7004
|
const learnedSection = readLearnedSection();
|
|
6992
7005
|
if (learnedSection) {
|
|
6993
7006
|
const lineCount = learnedSection.split("\n").filter(Boolean).length;
|
|
6994
7007
|
console.log(`
|
|
6995
|
-
Learned items in CLAUDE.md: ${
|
|
7008
|
+
Learned items in CLAUDE.md: ${chalk15.cyan(String(lineCount))}`);
|
|
6996
7009
|
}
|
|
6997
7010
|
}
|
|
6998
7011
|
|
|
6999
7012
|
// src/cli.ts
|
|
7000
7013
|
var __dirname = path22.dirname(fileURLToPath(import.meta.url));
|
|
7001
7014
|
var pkg = JSON.parse(
|
|
7002
|
-
|
|
7015
|
+
fs28.readFileSync(path22.resolve(__dirname, "..", "package.json"), "utf-8")
|
|
7003
7016
|
);
|
|
7004
7017
|
var program = new Command();
|
|
7005
7018
|
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
|
|
@@ -7032,22 +7045,22 @@ learn.command("remove").description("Remove learning hooks from .claude/settings
|
|
|
7032
7045
|
learn.command("status").description("Show learning system status").action(learnStatusCommand);
|
|
7033
7046
|
|
|
7034
7047
|
// src/utils/version-check.ts
|
|
7035
|
-
import
|
|
7048
|
+
import fs29 from "fs";
|
|
7036
7049
|
import path23 from "path";
|
|
7037
7050
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7038
7051
|
import { execSync as execSync9 } from "child_process";
|
|
7039
|
-
import
|
|
7052
|
+
import chalk16 from "chalk";
|
|
7040
7053
|
import ora7 from "ora";
|
|
7041
7054
|
import confirm from "@inquirer/confirm";
|
|
7042
7055
|
var __dirname_vc = path23.dirname(fileURLToPath2(import.meta.url));
|
|
7043
7056
|
var pkg2 = JSON.parse(
|
|
7044
|
-
|
|
7057
|
+
fs29.readFileSync(path23.resolve(__dirname_vc, "..", "package.json"), "utf-8")
|
|
7045
7058
|
);
|
|
7046
7059
|
function getInstalledVersion() {
|
|
7047
7060
|
try {
|
|
7048
7061
|
const globalRoot = execSync9("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
7049
7062
|
const pkgPath = path23.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
7050
|
-
return JSON.parse(
|
|
7063
|
+
return JSON.parse(fs29.readFileSync(pkgPath, "utf-8")).version;
|
|
7051
7064
|
} catch {
|
|
7052
7065
|
return null;
|
|
7053
7066
|
}
|
|
@@ -7070,17 +7083,17 @@ async function checkForUpdates() {
|
|
|
7070
7083
|
const isInteractive = process.stdin.isTTY === true;
|
|
7071
7084
|
if (!isInteractive) {
|
|
7072
7085
|
console.log(
|
|
7073
|
-
|
|
7086
|
+
chalk16.yellow(
|
|
7074
7087
|
`
|
|
7075
7088
|
Update available: ${current} -> ${latest}
|
|
7076
|
-
Run ${
|
|
7089
|
+
Run ${chalk16.bold("npm install -g @rely-ai/caliber")} to upgrade.
|
|
7077
7090
|
`
|
|
7078
7091
|
)
|
|
7079
7092
|
);
|
|
7080
7093
|
return;
|
|
7081
7094
|
}
|
|
7082
7095
|
console.log(
|
|
7083
|
-
|
|
7096
|
+
chalk16.yellow(`
|
|
7084
7097
|
Update available: ${current} -> ${latest}`)
|
|
7085
7098
|
);
|
|
7086
7099
|
const shouldUpdate = await confirm({ message: "Would you like to update now? (Y/n)", default: true });
|
|
@@ -7098,13 +7111,13 @@ Update available: ${current} -> ${latest}`)
|
|
|
7098
7111
|
const installed = getInstalledVersion();
|
|
7099
7112
|
if (installed !== latest) {
|
|
7100
7113
|
spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
|
|
7101
|
-
console.log(
|
|
7114
|
+
console.log(chalk16.yellow(`Run ${chalk16.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually.
|
|
7102
7115
|
`));
|
|
7103
7116
|
return;
|
|
7104
7117
|
}
|
|
7105
|
-
spinner.succeed(
|
|
7118
|
+
spinner.succeed(chalk16.green(`Updated to ${latest}`));
|
|
7106
7119
|
const args = process.argv.slice(2);
|
|
7107
|
-
console.log(
|
|
7120
|
+
console.log(chalk16.dim(`
|
|
7108
7121
|
Restarting: caliber ${args.join(" ")}
|
|
7109
7122
|
`));
|
|
7110
7123
|
execSync9(`caliber ${args.map((a) => JSON.stringify(a)).join(" ")}`, {
|
|
@@ -7117,11 +7130,11 @@ Restarting: caliber ${args.join(" ")}
|
|
|
7117
7130
|
if (err instanceof Error) {
|
|
7118
7131
|
const stderr = err.stderr;
|
|
7119
7132
|
const errMsg = stderr ? String(stderr).trim().split("\n").pop() : err.message.split("\n")[0];
|
|
7120
|
-
if (errMsg && !errMsg.includes("SIGTERM")) console.log(
|
|
7133
|
+
if (errMsg && !errMsg.includes("SIGTERM")) console.log(chalk16.dim(` ${errMsg}`));
|
|
7121
7134
|
}
|
|
7122
7135
|
console.log(
|
|
7123
|
-
|
|
7124
|
-
`Run ${
|
|
7136
|
+
chalk16.yellow(
|
|
7137
|
+
`Run ${chalk16.bold(`npm install -g @rely-ai/caliber@${latest}`)} manually to upgrade.
|
|
7125
7138
|
`
|
|
7126
7139
|
)
|
|
7127
7140
|
);
|