@rely-ai/caliber 1.20.0-dev.1773685636 → 1.20.0-dev.1773686772
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 +364 -345
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -177,13 +177,13 @@ __export(lock_exports, {
|
|
|
177
177
|
isCaliberRunning: () => isCaliberRunning,
|
|
178
178
|
releaseLock: () => releaseLock
|
|
179
179
|
});
|
|
180
|
-
import
|
|
181
|
-
import
|
|
182
|
-
import
|
|
180
|
+
import fs29 from "fs";
|
|
181
|
+
import path23 from "path";
|
|
182
|
+
import os5 from "os";
|
|
183
183
|
function isCaliberRunning() {
|
|
184
184
|
try {
|
|
185
|
-
if (!
|
|
186
|
-
const raw =
|
|
185
|
+
if (!fs29.existsSync(LOCK_FILE)) return false;
|
|
186
|
+
const raw = fs29.readFileSync(LOCK_FILE, "utf-8").trim();
|
|
187
187
|
const { pid, ts } = JSON.parse(raw);
|
|
188
188
|
if (Date.now() - ts > STALE_MS) return false;
|
|
189
189
|
try {
|
|
@@ -198,13 +198,13 @@ function isCaliberRunning() {
|
|
|
198
198
|
}
|
|
199
199
|
function acquireLock() {
|
|
200
200
|
try {
|
|
201
|
-
|
|
201
|
+
fs29.writeFileSync(LOCK_FILE, JSON.stringify({ pid: process.pid, ts: Date.now() }));
|
|
202
202
|
} catch {
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
205
|
function releaseLock() {
|
|
206
206
|
try {
|
|
207
|
-
if (
|
|
207
|
+
if (fs29.existsSync(LOCK_FILE)) fs29.unlinkSync(LOCK_FILE);
|
|
208
208
|
} catch {
|
|
209
209
|
}
|
|
210
210
|
}
|
|
@@ -212,24 +212,24 @@ var LOCK_FILE, STALE_MS;
|
|
|
212
212
|
var init_lock = __esm({
|
|
213
213
|
"src/lib/lock.ts"() {
|
|
214
214
|
"use strict";
|
|
215
|
-
LOCK_FILE =
|
|
215
|
+
LOCK_FILE = path23.join(os5.tmpdir(), ".caliber.lock");
|
|
216
216
|
STALE_MS = 10 * 60 * 1e3;
|
|
217
217
|
}
|
|
218
218
|
});
|
|
219
219
|
|
|
220
220
|
// src/cli.ts
|
|
221
221
|
import { Command } from "commander";
|
|
222
|
-
import
|
|
223
|
-
import
|
|
222
|
+
import fs33 from "fs";
|
|
223
|
+
import path26 from "path";
|
|
224
224
|
import { fileURLToPath } from "url";
|
|
225
225
|
|
|
226
226
|
// src/commands/init.ts
|
|
227
|
-
import
|
|
227
|
+
import path20 from "path";
|
|
228
228
|
import chalk10 from "chalk";
|
|
229
229
|
import ora2 from "ora";
|
|
230
230
|
import select5 from "@inquirer/select";
|
|
231
231
|
import checkbox from "@inquirer/checkbox";
|
|
232
|
-
import
|
|
232
|
+
import fs25 from "fs";
|
|
233
233
|
|
|
234
234
|
// src/fingerprint/index.ts
|
|
235
235
|
import fs6 from "fs";
|
|
@@ -2262,15 +2262,15 @@ init_config();
|
|
|
2262
2262
|
// src/utils/dependencies.ts
|
|
2263
2263
|
import { readFileSync } from "fs";
|
|
2264
2264
|
import { join } from "path";
|
|
2265
|
-
function readFileOrNull(
|
|
2265
|
+
function readFileOrNull(path28) {
|
|
2266
2266
|
try {
|
|
2267
|
-
return readFileSync(
|
|
2267
|
+
return readFileSync(path28, "utf-8");
|
|
2268
2268
|
} catch {
|
|
2269
2269
|
return null;
|
|
2270
2270
|
}
|
|
2271
2271
|
}
|
|
2272
|
-
function readJsonOrNull(
|
|
2273
|
-
const content = readFileOrNull(
|
|
2272
|
+
function readJsonOrNull(path28) {
|
|
2273
|
+
const content = readFileOrNull(path28);
|
|
2274
2274
|
if (!content) return null;
|
|
2275
2275
|
try {
|
|
2276
2276
|
return JSON.parse(content);
|
|
@@ -3236,13 +3236,23 @@ function cleanupStaging() {
|
|
|
3236
3236
|
|
|
3237
3237
|
// src/utils/review.ts
|
|
3238
3238
|
import chalk2 from "chalk";
|
|
3239
|
-
import
|
|
3239
|
+
import fs15 from "fs";
|
|
3240
3240
|
import select2 from "@inquirer/select";
|
|
3241
3241
|
import { createTwoFilesPatch } from "diff";
|
|
3242
3242
|
|
|
3243
3243
|
// src/utils/editor.ts
|
|
3244
3244
|
import { execSync as execSync5, spawn as spawn3 } from "child_process";
|
|
3245
|
+
import fs14 from "fs";
|
|
3246
|
+
import path12 from "path";
|
|
3247
|
+
import os3 from "os";
|
|
3245
3248
|
var IS_WINDOWS3 = process.platform === "win32";
|
|
3249
|
+
var DIFF_TEMP_DIR = path12.join(os3.tmpdir(), "caliber-diff");
|
|
3250
|
+
function getEmptyFilePath(proposedPath) {
|
|
3251
|
+
fs14.mkdirSync(DIFF_TEMP_DIR, { recursive: true });
|
|
3252
|
+
const tempPath = path12.join(DIFF_TEMP_DIR, path12.basename(proposedPath));
|
|
3253
|
+
fs14.writeFileSync(tempPath, "");
|
|
3254
|
+
return tempPath;
|
|
3255
|
+
}
|
|
3246
3256
|
function commandExists(cmd) {
|
|
3247
3257
|
try {
|
|
3248
3258
|
const check = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
|
|
@@ -3263,13 +3273,12 @@ function openDiffsInEditor(editor, files) {
|
|
|
3263
3273
|
const cmd = editor === "cursor" ? "cursor" : "code";
|
|
3264
3274
|
for (const file of files) {
|
|
3265
3275
|
try {
|
|
3276
|
+
const leftPath = file.originalPath ?? getEmptyFilePath(file.proposedPath);
|
|
3266
3277
|
if (IS_WINDOWS3) {
|
|
3267
3278
|
const quote = (s) => `"${s}"`;
|
|
3268
|
-
|
|
3269
|
-
spawn3(parts.join(" "), { shell: true, stdio: "ignore", detached: true }).unref();
|
|
3279
|
+
spawn3([cmd, "--diff", quote(leftPath), quote(file.proposedPath)].join(" "), { shell: true, stdio: "ignore", detached: true }).unref();
|
|
3270
3280
|
} else {
|
|
3271
|
-
|
|
3272
|
-
spawn3(cmd, args, { stdio: "ignore", detached: true }).unref();
|
|
3281
|
+
spawn3(cmd, ["--diff", leftPath, file.proposedPath], { stdio: "ignore", detached: true }).unref();
|
|
3273
3282
|
}
|
|
3274
3283
|
} catch {
|
|
3275
3284
|
continue;
|
|
@@ -3312,8 +3321,8 @@ async function openReview(method, stagedFiles) {
|
|
|
3312
3321
|
return;
|
|
3313
3322
|
}
|
|
3314
3323
|
const fileInfos = stagedFiles.map((file) => {
|
|
3315
|
-
const proposed =
|
|
3316
|
-
const current = file.currentPath ?
|
|
3324
|
+
const proposed = fs15.readFileSync(file.proposedPath, "utf-8");
|
|
3325
|
+
const current = file.currentPath ? fs15.readFileSync(file.currentPath, "utf-8") : "";
|
|
3317
3326
|
const patch = createTwoFilesPatch(
|
|
3318
3327
|
file.isNew ? "/dev/null" : file.relativePath,
|
|
3319
3328
|
file.relativePath,
|
|
@@ -3476,7 +3485,7 @@ async function interactiveDiffExplorer(files) {
|
|
|
3476
3485
|
}
|
|
3477
3486
|
|
|
3478
3487
|
// src/commands/setup-files.ts
|
|
3479
|
-
import
|
|
3488
|
+
import fs16 from "fs";
|
|
3480
3489
|
function buildSkillContent(skill) {
|
|
3481
3490
|
const frontmatter = `---
|
|
3482
3491
|
name: ${skill.name}
|
|
@@ -3525,7 +3534,7 @@ function collectSetupFiles(setup, targetAgent) {
|
|
|
3525
3534
|
}
|
|
3526
3535
|
}
|
|
3527
3536
|
const codexTargeted = targetAgent ? targetAgent.includes("codex") : false;
|
|
3528
|
-
if (codexTargeted && !
|
|
3537
|
+
if (codexTargeted && !fs16.existsSync("AGENTS.md") && !(codex && codex.agentsMd)) {
|
|
3529
3538
|
const agentRefs = [];
|
|
3530
3539
|
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
3531
3540
|
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
@@ -3542,12 +3551,12 @@ ${agentRefs.join(" ")}
|
|
|
3542
3551
|
}
|
|
3543
3552
|
|
|
3544
3553
|
// src/lib/hooks.ts
|
|
3545
|
-
import
|
|
3546
|
-
import
|
|
3554
|
+
import fs18 from "fs";
|
|
3555
|
+
import path13 from "path";
|
|
3547
3556
|
import { execSync as execSync7 } from "child_process";
|
|
3548
3557
|
|
|
3549
3558
|
// src/lib/resolve-caliber.ts
|
|
3550
|
-
import
|
|
3559
|
+
import fs17 from "fs";
|
|
3551
3560
|
import { execSync as execSync6 } from "child_process";
|
|
3552
3561
|
var _resolved = null;
|
|
3553
3562
|
function resolveCaliber() {
|
|
@@ -3570,7 +3579,7 @@ function resolveCaliber() {
|
|
|
3570
3579
|
} catch {
|
|
3571
3580
|
}
|
|
3572
3581
|
const binPath = process.argv[1];
|
|
3573
|
-
if (binPath &&
|
|
3582
|
+
if (binPath && fs17.existsSync(binPath)) {
|
|
3574
3583
|
_resolved = binPath;
|
|
3575
3584
|
return _resolved;
|
|
3576
3585
|
}
|
|
@@ -3586,24 +3595,24 @@ function isCaliberCommand(command, subcommandTail) {
|
|
|
3586
3595
|
}
|
|
3587
3596
|
|
|
3588
3597
|
// src/lib/hooks.ts
|
|
3589
|
-
var SETTINGS_PATH =
|
|
3598
|
+
var SETTINGS_PATH = path13.join(".claude", "settings.json");
|
|
3590
3599
|
var REFRESH_TAIL = "refresh --quiet";
|
|
3591
3600
|
var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
|
|
3592
3601
|
function getHookCommand() {
|
|
3593
3602
|
return `${resolveCaliber()} ${REFRESH_TAIL}`;
|
|
3594
3603
|
}
|
|
3595
3604
|
function readSettings() {
|
|
3596
|
-
if (!
|
|
3605
|
+
if (!fs18.existsSync(SETTINGS_PATH)) return {};
|
|
3597
3606
|
try {
|
|
3598
|
-
return JSON.parse(
|
|
3607
|
+
return JSON.parse(fs18.readFileSync(SETTINGS_PATH, "utf-8"));
|
|
3599
3608
|
} catch {
|
|
3600
3609
|
return {};
|
|
3601
3610
|
}
|
|
3602
3611
|
}
|
|
3603
3612
|
function writeSettings(settings) {
|
|
3604
|
-
const dir =
|
|
3605
|
-
if (!
|
|
3606
|
-
|
|
3613
|
+
const dir = path13.dirname(SETTINGS_PATH);
|
|
3614
|
+
if (!fs18.existsSync(dir)) fs18.mkdirSync(dir, { recursive: true });
|
|
3615
|
+
fs18.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
3607
3616
|
}
|
|
3608
3617
|
function findHookIndex(sessionEnd) {
|
|
3609
3618
|
return sessionEnd.findIndex(
|
|
@@ -3666,19 +3675,19 @@ ${PRECOMMIT_END}`;
|
|
|
3666
3675
|
function getGitHooksDir() {
|
|
3667
3676
|
try {
|
|
3668
3677
|
const gitDir = execSync7("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
3669
|
-
return
|
|
3678
|
+
return path13.join(gitDir, "hooks");
|
|
3670
3679
|
} catch {
|
|
3671
3680
|
return null;
|
|
3672
3681
|
}
|
|
3673
3682
|
}
|
|
3674
3683
|
function getPreCommitPath() {
|
|
3675
3684
|
const hooksDir = getGitHooksDir();
|
|
3676
|
-
return hooksDir ?
|
|
3685
|
+
return hooksDir ? path13.join(hooksDir, "pre-commit") : null;
|
|
3677
3686
|
}
|
|
3678
3687
|
function isPreCommitHookInstalled() {
|
|
3679
3688
|
const hookPath = getPreCommitPath();
|
|
3680
|
-
if (!hookPath || !
|
|
3681
|
-
const content =
|
|
3689
|
+
if (!hookPath || !fs18.existsSync(hookPath)) return false;
|
|
3690
|
+
const content = fs18.readFileSync(hookPath, "utf-8");
|
|
3682
3691
|
return content.includes(PRECOMMIT_START);
|
|
3683
3692
|
}
|
|
3684
3693
|
function installPreCommitHook() {
|
|
@@ -3687,43 +3696,43 @@ function installPreCommitHook() {
|
|
|
3687
3696
|
}
|
|
3688
3697
|
const hookPath = getPreCommitPath();
|
|
3689
3698
|
if (!hookPath) return { installed: false, alreadyInstalled: false };
|
|
3690
|
-
const hooksDir =
|
|
3691
|
-
if (!
|
|
3699
|
+
const hooksDir = path13.dirname(hookPath);
|
|
3700
|
+
if (!fs18.existsSync(hooksDir)) fs18.mkdirSync(hooksDir, { recursive: true });
|
|
3692
3701
|
let content = "";
|
|
3693
|
-
if (
|
|
3694
|
-
content =
|
|
3702
|
+
if (fs18.existsSync(hookPath)) {
|
|
3703
|
+
content = fs18.readFileSync(hookPath, "utf-8");
|
|
3695
3704
|
if (!content.endsWith("\n")) content += "\n";
|
|
3696
3705
|
content += "\n" + getPrecommitBlock() + "\n";
|
|
3697
3706
|
} else {
|
|
3698
3707
|
content = "#!/bin/sh\n\n" + getPrecommitBlock() + "\n";
|
|
3699
3708
|
}
|
|
3700
|
-
|
|
3701
|
-
|
|
3709
|
+
fs18.writeFileSync(hookPath, content);
|
|
3710
|
+
fs18.chmodSync(hookPath, 493);
|
|
3702
3711
|
return { installed: true, alreadyInstalled: false };
|
|
3703
3712
|
}
|
|
3704
3713
|
function removePreCommitHook() {
|
|
3705
3714
|
const hookPath = getPreCommitPath();
|
|
3706
|
-
if (!hookPath || !
|
|
3715
|
+
if (!hookPath || !fs18.existsSync(hookPath)) {
|
|
3707
3716
|
return { removed: false, notFound: true };
|
|
3708
3717
|
}
|
|
3709
|
-
let content =
|
|
3718
|
+
let content = fs18.readFileSync(hookPath, "utf-8");
|
|
3710
3719
|
if (!content.includes(PRECOMMIT_START)) {
|
|
3711
3720
|
return { removed: false, notFound: true };
|
|
3712
3721
|
}
|
|
3713
|
-
const
|
|
3714
|
-
content = content.replace(
|
|
3722
|
+
const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
|
|
3723
|
+
content = content.replace(regex, "\n");
|
|
3715
3724
|
if (content.trim() === "#!/bin/sh" || content.trim() === "") {
|
|
3716
|
-
|
|
3725
|
+
fs18.unlinkSync(hookPath);
|
|
3717
3726
|
} else {
|
|
3718
|
-
|
|
3727
|
+
fs18.writeFileSync(hookPath, content);
|
|
3719
3728
|
}
|
|
3720
3729
|
return { removed: true, notFound: false };
|
|
3721
3730
|
}
|
|
3722
3731
|
|
|
3723
3732
|
// src/lib/learning-hooks.ts
|
|
3724
|
-
import
|
|
3725
|
-
import
|
|
3726
|
-
var SETTINGS_PATH2 =
|
|
3733
|
+
import fs19 from "fs";
|
|
3734
|
+
import path14 from "path";
|
|
3735
|
+
var SETTINGS_PATH2 = path14.join(".claude", "settings.json");
|
|
3727
3736
|
var HOOK_TAILS = [
|
|
3728
3737
|
{ event: "PostToolUse", tail: "learn observe", description: "Caliber: recording tool usage for session learning" },
|
|
3729
3738
|
{ event: "PostToolUseFailure", tail: "learn observe --failure", description: "Caliber: recording tool failure for session learning" },
|
|
@@ -3740,17 +3749,17 @@ function getHookConfigs() {
|
|
|
3740
3749
|
}));
|
|
3741
3750
|
}
|
|
3742
3751
|
function readSettings2() {
|
|
3743
|
-
if (!
|
|
3752
|
+
if (!fs19.existsSync(SETTINGS_PATH2)) return {};
|
|
3744
3753
|
try {
|
|
3745
|
-
return JSON.parse(
|
|
3754
|
+
return JSON.parse(fs19.readFileSync(SETTINGS_PATH2, "utf-8"));
|
|
3746
3755
|
} catch {
|
|
3747
3756
|
return {};
|
|
3748
3757
|
}
|
|
3749
3758
|
}
|
|
3750
3759
|
function writeSettings2(settings) {
|
|
3751
|
-
const dir =
|
|
3752
|
-
if (!
|
|
3753
|
-
|
|
3760
|
+
const dir = path14.dirname(SETTINGS_PATH2);
|
|
3761
|
+
if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
|
|
3762
|
+
fs19.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
|
|
3754
3763
|
}
|
|
3755
3764
|
function hasLearningHook(matchers, tail) {
|
|
3756
3765
|
return matchers.some((entry) => entry.hooks?.some((h) => isCaliberCommand(h.command, tail)));
|
|
@@ -3784,7 +3793,7 @@ function installLearningHooks() {
|
|
|
3784
3793
|
writeSettings2(settings);
|
|
3785
3794
|
return { installed: true, alreadyInstalled: false };
|
|
3786
3795
|
}
|
|
3787
|
-
var CURSOR_HOOKS_PATH =
|
|
3796
|
+
var CURSOR_HOOKS_PATH = path14.join(".cursor", "hooks.json");
|
|
3788
3797
|
var CURSOR_HOOK_EVENTS = [
|
|
3789
3798
|
{ event: "postToolUse", tail: "learn observe" },
|
|
3790
3799
|
{ event: "postToolUseFailure", tail: "learn observe --failure" },
|
|
@@ -3792,17 +3801,17 @@ var CURSOR_HOOK_EVENTS = [
|
|
|
3792
3801
|
{ event: "sessionEnd", tail: "learn finalize" }
|
|
3793
3802
|
];
|
|
3794
3803
|
function readCursorHooks() {
|
|
3795
|
-
if (!
|
|
3804
|
+
if (!fs19.existsSync(CURSOR_HOOKS_PATH)) return { version: 1, hooks: {} };
|
|
3796
3805
|
try {
|
|
3797
|
-
return JSON.parse(
|
|
3806
|
+
return JSON.parse(fs19.readFileSync(CURSOR_HOOKS_PATH, "utf-8"));
|
|
3798
3807
|
} catch {
|
|
3799
3808
|
return { version: 1, hooks: {} };
|
|
3800
3809
|
}
|
|
3801
3810
|
}
|
|
3802
3811
|
function writeCursorHooks(config) {
|
|
3803
|
-
const dir =
|
|
3804
|
-
if (!
|
|
3805
|
-
|
|
3812
|
+
const dir = path14.dirname(CURSOR_HOOKS_PATH);
|
|
3813
|
+
if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
|
|
3814
|
+
fs19.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(config, null, 2));
|
|
3806
3815
|
}
|
|
3807
3816
|
function hasCursorHook(entries, tail) {
|
|
3808
3817
|
return entries.some((e) => isCaliberCommand(e.command, tail));
|
|
@@ -3872,10 +3881,10 @@ function removeLearningHooks() {
|
|
|
3872
3881
|
|
|
3873
3882
|
// src/lib/state.ts
|
|
3874
3883
|
init_constants();
|
|
3875
|
-
import
|
|
3876
|
-
import
|
|
3884
|
+
import fs20 from "fs";
|
|
3885
|
+
import path15 from "path";
|
|
3877
3886
|
import { execSync as execSync8 } from "child_process";
|
|
3878
|
-
var STATE_FILE =
|
|
3887
|
+
var STATE_FILE = path15.join(CALIBER_DIR, ".caliber-state.json");
|
|
3879
3888
|
function normalizeTargetAgent(value) {
|
|
3880
3889
|
if (Array.isArray(value)) return value;
|
|
3881
3890
|
if (typeof value === "string") {
|
|
@@ -3886,8 +3895,8 @@ function normalizeTargetAgent(value) {
|
|
|
3886
3895
|
}
|
|
3887
3896
|
function readState() {
|
|
3888
3897
|
try {
|
|
3889
|
-
if (!
|
|
3890
|
-
const raw = JSON.parse(
|
|
3898
|
+
if (!fs20.existsSync(STATE_FILE)) return null;
|
|
3899
|
+
const raw = JSON.parse(fs20.readFileSync(STATE_FILE, "utf-8"));
|
|
3891
3900
|
if (raw.targetAgent) raw.targetAgent = normalizeTargetAgent(raw.targetAgent);
|
|
3892
3901
|
return raw;
|
|
3893
3902
|
} catch {
|
|
@@ -3895,10 +3904,10 @@ function readState() {
|
|
|
3895
3904
|
}
|
|
3896
3905
|
}
|
|
3897
3906
|
function writeState(state) {
|
|
3898
|
-
if (!
|
|
3899
|
-
|
|
3907
|
+
if (!fs20.existsSync(CALIBER_DIR)) {
|
|
3908
|
+
fs20.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
3900
3909
|
}
|
|
3901
|
-
|
|
3910
|
+
fs20.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
|
|
3902
3911
|
}
|
|
3903
3912
|
function getCurrentHeadSha() {
|
|
3904
3913
|
try {
|
|
@@ -5255,22 +5264,22 @@ function checkBonus(dir) {
|
|
|
5255
5264
|
|
|
5256
5265
|
// src/scoring/dismissed.ts
|
|
5257
5266
|
init_constants();
|
|
5258
|
-
import
|
|
5259
|
-
import
|
|
5260
|
-
var DISMISSED_FILE =
|
|
5267
|
+
import fs21 from "fs";
|
|
5268
|
+
import path16 from "path";
|
|
5269
|
+
var DISMISSED_FILE = path16.join(CALIBER_DIR, "dismissed-checks.json");
|
|
5261
5270
|
function readDismissedChecks() {
|
|
5262
5271
|
try {
|
|
5263
|
-
if (!
|
|
5264
|
-
return JSON.parse(
|
|
5272
|
+
if (!fs21.existsSync(DISMISSED_FILE)) return [];
|
|
5273
|
+
return JSON.parse(fs21.readFileSync(DISMISSED_FILE, "utf-8"));
|
|
5265
5274
|
} catch {
|
|
5266
5275
|
return [];
|
|
5267
5276
|
}
|
|
5268
5277
|
}
|
|
5269
5278
|
function writeDismissedChecks(checks) {
|
|
5270
|
-
if (!
|
|
5271
|
-
|
|
5279
|
+
if (!fs21.existsSync(CALIBER_DIR)) {
|
|
5280
|
+
fs21.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
5272
5281
|
}
|
|
5273
|
-
|
|
5282
|
+
fs21.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
|
|
5274
5283
|
}
|
|
5275
5284
|
function getDismissedIds() {
|
|
5276
5285
|
return new Set(readDismissedChecks().map((c) => c.id));
|
|
@@ -5466,13 +5475,13 @@ import { mkdirSync, readFileSync as readFileSync4, readdirSync as readdirSync4,
|
|
|
5466
5475
|
import { join as join9, dirname as dirname2 } from "path";
|
|
5467
5476
|
|
|
5468
5477
|
// src/scanner/index.ts
|
|
5469
|
-
import
|
|
5470
|
-
import
|
|
5478
|
+
import fs22 from "fs";
|
|
5479
|
+
import path17 from "path";
|
|
5471
5480
|
import crypto2 from "crypto";
|
|
5472
5481
|
function scanLocalState(dir) {
|
|
5473
5482
|
const items = [];
|
|
5474
|
-
const claudeMdPath =
|
|
5475
|
-
if (
|
|
5483
|
+
const claudeMdPath = path17.join(dir, "CLAUDE.md");
|
|
5484
|
+
if (fs22.existsSync(claudeMdPath)) {
|
|
5476
5485
|
items.push({
|
|
5477
5486
|
type: "rule",
|
|
5478
5487
|
platform: "claude",
|
|
@@ -5481,10 +5490,10 @@ function scanLocalState(dir) {
|
|
|
5481
5490
|
path: claudeMdPath
|
|
5482
5491
|
});
|
|
5483
5492
|
}
|
|
5484
|
-
const skillsDir =
|
|
5485
|
-
if (
|
|
5486
|
-
for (const file of
|
|
5487
|
-
const filePath =
|
|
5493
|
+
const skillsDir = path17.join(dir, ".claude", "skills");
|
|
5494
|
+
if (fs22.existsSync(skillsDir)) {
|
|
5495
|
+
for (const file of fs22.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
5496
|
+
const filePath = path17.join(skillsDir, file);
|
|
5488
5497
|
items.push({
|
|
5489
5498
|
type: "skill",
|
|
5490
5499
|
platform: "claude",
|
|
@@ -5494,10 +5503,10 @@ function scanLocalState(dir) {
|
|
|
5494
5503
|
});
|
|
5495
5504
|
}
|
|
5496
5505
|
}
|
|
5497
|
-
const mcpJsonPath =
|
|
5498
|
-
if (
|
|
5506
|
+
const mcpJsonPath = path17.join(dir, ".mcp.json");
|
|
5507
|
+
if (fs22.existsSync(mcpJsonPath)) {
|
|
5499
5508
|
try {
|
|
5500
|
-
const mcpJson = JSON.parse(
|
|
5509
|
+
const mcpJson = JSON.parse(fs22.readFileSync(mcpJsonPath, "utf-8"));
|
|
5501
5510
|
if (mcpJson.mcpServers) {
|
|
5502
5511
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
5503
5512
|
items.push({
|
|
@@ -5512,8 +5521,8 @@ function scanLocalState(dir) {
|
|
|
5512
5521
|
} catch {
|
|
5513
5522
|
}
|
|
5514
5523
|
}
|
|
5515
|
-
const agentsMdPath =
|
|
5516
|
-
if (
|
|
5524
|
+
const agentsMdPath = path17.join(dir, "AGENTS.md");
|
|
5525
|
+
if (fs22.existsSync(agentsMdPath)) {
|
|
5517
5526
|
items.push({
|
|
5518
5527
|
type: "rule",
|
|
5519
5528
|
platform: "codex",
|
|
@@ -5522,12 +5531,12 @@ function scanLocalState(dir) {
|
|
|
5522
5531
|
path: agentsMdPath
|
|
5523
5532
|
});
|
|
5524
5533
|
}
|
|
5525
|
-
const codexSkillsDir =
|
|
5526
|
-
if (
|
|
5534
|
+
const codexSkillsDir = path17.join(dir, ".agents", "skills");
|
|
5535
|
+
if (fs22.existsSync(codexSkillsDir)) {
|
|
5527
5536
|
try {
|
|
5528
|
-
for (const name of
|
|
5529
|
-
const skillFile =
|
|
5530
|
-
if (
|
|
5537
|
+
for (const name of fs22.readdirSync(codexSkillsDir)) {
|
|
5538
|
+
const skillFile = path17.join(codexSkillsDir, name, "SKILL.md");
|
|
5539
|
+
if (fs22.existsSync(skillFile)) {
|
|
5531
5540
|
items.push({
|
|
5532
5541
|
type: "skill",
|
|
5533
5542
|
platform: "codex",
|
|
@@ -5540,8 +5549,8 @@ function scanLocalState(dir) {
|
|
|
5540
5549
|
} catch {
|
|
5541
5550
|
}
|
|
5542
5551
|
}
|
|
5543
|
-
const cursorrulesPath =
|
|
5544
|
-
if (
|
|
5552
|
+
const cursorrulesPath = path17.join(dir, ".cursorrules");
|
|
5553
|
+
if (fs22.existsSync(cursorrulesPath)) {
|
|
5545
5554
|
items.push({
|
|
5546
5555
|
type: "rule",
|
|
5547
5556
|
platform: "cursor",
|
|
@@ -5550,10 +5559,10 @@ function scanLocalState(dir) {
|
|
|
5550
5559
|
path: cursorrulesPath
|
|
5551
5560
|
});
|
|
5552
5561
|
}
|
|
5553
|
-
const cursorRulesDir =
|
|
5554
|
-
if (
|
|
5555
|
-
for (const file of
|
|
5556
|
-
const filePath =
|
|
5562
|
+
const cursorRulesDir = path17.join(dir, ".cursor", "rules");
|
|
5563
|
+
if (fs22.existsSync(cursorRulesDir)) {
|
|
5564
|
+
for (const file of fs22.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
5565
|
+
const filePath = path17.join(cursorRulesDir, file);
|
|
5557
5566
|
items.push({
|
|
5558
5567
|
type: "rule",
|
|
5559
5568
|
platform: "cursor",
|
|
@@ -5563,12 +5572,12 @@ function scanLocalState(dir) {
|
|
|
5563
5572
|
});
|
|
5564
5573
|
}
|
|
5565
5574
|
}
|
|
5566
|
-
const cursorSkillsDir =
|
|
5567
|
-
if (
|
|
5575
|
+
const cursorSkillsDir = path17.join(dir, ".cursor", "skills");
|
|
5576
|
+
if (fs22.existsSync(cursorSkillsDir)) {
|
|
5568
5577
|
try {
|
|
5569
|
-
for (const name of
|
|
5570
|
-
const skillFile =
|
|
5571
|
-
if (
|
|
5578
|
+
for (const name of fs22.readdirSync(cursorSkillsDir)) {
|
|
5579
|
+
const skillFile = path17.join(cursorSkillsDir, name, "SKILL.md");
|
|
5580
|
+
if (fs22.existsSync(skillFile)) {
|
|
5572
5581
|
items.push({
|
|
5573
5582
|
type: "skill",
|
|
5574
5583
|
platform: "cursor",
|
|
@@ -5581,10 +5590,10 @@ function scanLocalState(dir) {
|
|
|
5581
5590
|
} catch {
|
|
5582
5591
|
}
|
|
5583
5592
|
}
|
|
5584
|
-
const cursorMcpPath =
|
|
5585
|
-
if (
|
|
5593
|
+
const cursorMcpPath = path17.join(dir, ".cursor", "mcp.json");
|
|
5594
|
+
if (fs22.existsSync(cursorMcpPath)) {
|
|
5586
5595
|
try {
|
|
5587
|
-
const mcpJson = JSON.parse(
|
|
5596
|
+
const mcpJson = JSON.parse(fs22.readFileSync(cursorMcpPath, "utf-8"));
|
|
5588
5597
|
if (mcpJson.mcpServers) {
|
|
5589
5598
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
5590
5599
|
items.push({
|
|
@@ -5602,7 +5611,7 @@ function scanLocalState(dir) {
|
|
|
5602
5611
|
return items;
|
|
5603
5612
|
}
|
|
5604
5613
|
function hashFile(filePath) {
|
|
5605
|
-
const text =
|
|
5614
|
+
const text = fs22.readFileSync(filePath, "utf-8");
|
|
5606
5615
|
return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
5607
5616
|
}
|
|
5608
5617
|
function hashJson(obj) {
|
|
@@ -5617,27 +5626,27 @@ import { PostHog } from "posthog-node";
|
|
|
5617
5626
|
import chalk7 from "chalk";
|
|
5618
5627
|
|
|
5619
5628
|
// src/telemetry/config.ts
|
|
5620
|
-
import
|
|
5621
|
-
import
|
|
5622
|
-
import
|
|
5629
|
+
import fs23 from "fs";
|
|
5630
|
+
import path18 from "path";
|
|
5631
|
+
import os4 from "os";
|
|
5623
5632
|
import crypto3 from "crypto";
|
|
5624
5633
|
import { execSync as execSync12 } from "child_process";
|
|
5625
|
-
var CONFIG_DIR2 =
|
|
5626
|
-
var CONFIG_FILE2 =
|
|
5634
|
+
var CONFIG_DIR2 = path18.join(os4.homedir(), ".caliber");
|
|
5635
|
+
var CONFIG_FILE2 = path18.join(CONFIG_DIR2, "config.json");
|
|
5627
5636
|
var runtimeDisabled = false;
|
|
5628
5637
|
function readConfig() {
|
|
5629
5638
|
try {
|
|
5630
|
-
if (!
|
|
5631
|
-
return JSON.parse(
|
|
5639
|
+
if (!fs23.existsSync(CONFIG_FILE2)) return {};
|
|
5640
|
+
return JSON.parse(fs23.readFileSync(CONFIG_FILE2, "utf-8"));
|
|
5632
5641
|
} catch {
|
|
5633
5642
|
return {};
|
|
5634
5643
|
}
|
|
5635
5644
|
}
|
|
5636
5645
|
function writeConfig(config) {
|
|
5637
|
-
if (!
|
|
5638
|
-
|
|
5646
|
+
if (!fs23.existsSync(CONFIG_DIR2)) {
|
|
5647
|
+
fs23.mkdirSync(CONFIG_DIR2, { recursive: true });
|
|
5639
5648
|
}
|
|
5640
|
-
|
|
5649
|
+
fs23.writeFileSync(CONFIG_FILE2, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
|
|
5641
5650
|
}
|
|
5642
5651
|
function getMachineId() {
|
|
5643
5652
|
const config = readConfig();
|
|
@@ -6361,8 +6370,8 @@ function printSkills(recs) {
|
|
|
6361
6370
|
}
|
|
6362
6371
|
|
|
6363
6372
|
// src/lib/debug-report.ts
|
|
6364
|
-
import
|
|
6365
|
-
import
|
|
6373
|
+
import fs24 from "fs";
|
|
6374
|
+
import path19 from "path";
|
|
6366
6375
|
var DebugReport = class {
|
|
6367
6376
|
sections = [];
|
|
6368
6377
|
startTime;
|
|
@@ -6431,11 +6440,11 @@ var DebugReport = class {
|
|
|
6431
6440
|
lines.push(`| **Total** | **${formatMs(totalMs)}** |`);
|
|
6432
6441
|
lines.push("");
|
|
6433
6442
|
}
|
|
6434
|
-
const dir =
|
|
6435
|
-
if (!
|
|
6436
|
-
|
|
6443
|
+
const dir = path19.dirname(outputPath);
|
|
6444
|
+
if (!fs24.existsSync(dir)) {
|
|
6445
|
+
fs24.mkdirSync(dir, { recursive: true });
|
|
6437
6446
|
}
|
|
6438
|
-
|
|
6447
|
+
fs24.writeFileSync(outputPath, lines.join("\n"));
|
|
6439
6448
|
}
|
|
6440
6449
|
};
|
|
6441
6450
|
function formatMs(ms) {
|
|
@@ -6449,31 +6458,10 @@ function formatMs(ms) {
|
|
|
6449
6458
|
|
|
6450
6459
|
// src/utils/parallel-tasks.ts
|
|
6451
6460
|
import chalk9 from "chalk";
|
|
6452
|
-
|
|
6453
|
-
// node_modules/ansi-regex/index.js
|
|
6454
|
-
function ansiRegex({ onlyFirst = false } = {}) {
|
|
6455
|
-
const ST = "(?:\\u0007|\\u001B\\u005C|\\u009C)";
|
|
6456
|
-
const osc = `(?:\\u001B\\][\\s\\S]*?${ST})`;
|
|
6457
|
-
const csi = "[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]";
|
|
6458
|
-
const pattern = `${osc}|${csi}`;
|
|
6459
|
-
return new RegExp(pattern, onlyFirst ? void 0 : "g");
|
|
6460
|
-
}
|
|
6461
|
-
|
|
6462
|
-
// node_modules/strip-ansi/index.js
|
|
6463
|
-
var regex = ansiRegex();
|
|
6464
|
-
function stripAnsi(string) {
|
|
6465
|
-
if (typeof string !== "string") {
|
|
6466
|
-
throw new TypeError(`Expected a \`string\`, got \`${typeof string}\``);
|
|
6467
|
-
}
|
|
6468
|
-
if (!string.includes("\x1B") && !string.includes("\x9B")) {
|
|
6469
|
-
return string;
|
|
6470
|
-
}
|
|
6471
|
-
return string.replace(regex, "");
|
|
6472
|
-
}
|
|
6473
|
-
|
|
6474
|
-
// src/utils/parallel-tasks.ts
|
|
6475
6461
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
6476
6462
|
var SPINNER_INTERVAL_MS = 80;
|
|
6463
|
+
var NAME_COL_WIDTH = 26;
|
|
6464
|
+
var PREFIX = " ";
|
|
6477
6465
|
var ParallelTaskDisplay = class {
|
|
6478
6466
|
tasks = [];
|
|
6479
6467
|
lineCount = 0;
|
|
@@ -6518,48 +6506,48 @@ var ParallelTaskDisplay = class {
|
|
|
6518
6506
|
if (secs < 60) return `${secs}s`;
|
|
6519
6507
|
return `${Math.floor(secs / 60)}m ${secs % 60}s`;
|
|
6520
6508
|
}
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
if (text[i] === "\x1B") {
|
|
6528
|
-
const end = text.indexOf("m", i);
|
|
6529
|
-
if (end !== -1) {
|
|
6530
|
-
i = end + 1;
|
|
6531
|
-
continue;
|
|
6532
|
-
}
|
|
6533
|
-
}
|
|
6534
|
-
visible++;
|
|
6535
|
-
i++;
|
|
6536
|
-
}
|
|
6537
|
-
return text.slice(0, i) + "...";
|
|
6509
|
+
smartTruncate(text, max) {
|
|
6510
|
+
if (text.length <= max) return text;
|
|
6511
|
+
const cut = text.slice(0, max - 1);
|
|
6512
|
+
const lastSpace = cut.lastIndexOf(" ");
|
|
6513
|
+
const boundary = lastSpace > max * 0.5 ? lastSpace : max - 1;
|
|
6514
|
+
return text.slice(0, boundary) + "\u2026";
|
|
6538
6515
|
}
|
|
6539
6516
|
renderLine(task) {
|
|
6540
|
-
const
|
|
6517
|
+
const cols = process.stdout.columns || 80;
|
|
6541
6518
|
const elapsed = task.startTime ? this.formatTime((task.endTime ?? Date.now()) - task.startTime) : "";
|
|
6542
|
-
|
|
6519
|
+
const timeStr = elapsed ? ` ${chalk9.dim(elapsed)}` : "";
|
|
6520
|
+
const timePlain = elapsed ? ` ${elapsed}` : "";
|
|
6521
|
+
let icon;
|
|
6522
|
+
let nameStyle;
|
|
6523
|
+
let msgStyle;
|
|
6543
6524
|
switch (task.status) {
|
|
6544
6525
|
case "pending":
|
|
6545
|
-
|
|
6526
|
+
icon = chalk9.dim("\u25CB");
|
|
6527
|
+
nameStyle = chalk9.dim;
|
|
6528
|
+
msgStyle = chalk9.dim;
|
|
6546
6529
|
break;
|
|
6547
|
-
case "running":
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6530
|
+
case "running":
|
|
6531
|
+
icon = chalk9.cyan(SPINNER_FRAMES[this.spinnerFrame]);
|
|
6532
|
+
nameStyle = chalk9.white;
|
|
6533
|
+
msgStyle = chalk9.dim;
|
|
6551
6534
|
break;
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6535
|
+
case "done":
|
|
6536
|
+
icon = chalk9.green("\u2713");
|
|
6537
|
+
nameStyle = chalk9.white;
|
|
6538
|
+
msgStyle = chalk9.dim;
|
|
6556
6539
|
break;
|
|
6557
|
-
}
|
|
6558
6540
|
case "failed":
|
|
6559
|
-
|
|
6541
|
+
icon = chalk9.red("\u2717");
|
|
6542
|
+
nameStyle = chalk9.white;
|
|
6543
|
+
msgStyle = chalk9.red;
|
|
6560
6544
|
break;
|
|
6561
6545
|
}
|
|
6562
|
-
|
|
6546
|
+
const paddedName = task.name.padEnd(NAME_COL_WIDTH);
|
|
6547
|
+
const usedByFixed = PREFIX.length + 2 + NAME_COL_WIDTH + timePlain.length;
|
|
6548
|
+
const msgMax = Math.max(cols - usedByFixed - 2, 10);
|
|
6549
|
+
const msg = task.message ? this.smartTruncate(task.message, msgMax) : "";
|
|
6550
|
+
return `${PREFIX}${icon} ${nameStyle(paddedName)}${msg ? msgStyle(msg) : ""}${timeStr}`;
|
|
6563
6551
|
}
|
|
6564
6552
|
draw(initial) {
|
|
6565
6553
|
const { stdout } = process;
|
|
@@ -6568,9 +6556,6 @@ var ParallelTaskDisplay = class {
|
|
|
6568
6556
|
}
|
|
6569
6557
|
stdout.write("\x1B[0J");
|
|
6570
6558
|
const lines = this.tasks.map((t) => this.renderLine(t));
|
|
6571
|
-
const totalElapsed = this.formatTime(Date.now() - this.startTime);
|
|
6572
|
-
lines.push(chalk9.dim(`
|
|
6573
|
-
Total: ${totalElapsed}`));
|
|
6574
6559
|
const output = lines.join("\n");
|
|
6575
6560
|
stdout.write(output + "\n");
|
|
6576
6561
|
this.lineCount = output.split("\n").length;
|
|
@@ -6710,57 +6695,59 @@ async function initCommand(options) {
|
|
|
6710
6695
|
let genStopReason;
|
|
6711
6696
|
let skillSearchResult = { results: [], contentMap: /* @__PURE__ */ new Map() };
|
|
6712
6697
|
let fingerprint;
|
|
6713
|
-
const fpSpinner = ora2("Scanning project...").start();
|
|
6714
|
-
try {
|
|
6715
|
-
fingerprint = await collectFingerprint(process.cwd());
|
|
6716
|
-
fpSpinner.succeed(`Stack detected \u2014 ${fingerprint.languages.join(", ") || "no languages"}${fingerprint.frameworks.length > 0 ? `, ${fingerprint.frameworks.join(", ")}` : ""}`);
|
|
6717
|
-
} catch (err) {
|
|
6718
|
-
fpSpinner.fail("Failed to scan project");
|
|
6719
|
-
throw new Error("__exit__");
|
|
6720
|
-
}
|
|
6721
|
-
trackInitProjectDiscovered(fingerprint.languages.length, fingerprint.frameworks.length, fingerprint.fileTree.length);
|
|
6722
|
-
log(options.verbose, `Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`);
|
|
6723
|
-
if (report) {
|
|
6724
|
-
report.addJson("Fingerprint: Git", { remote: fingerprint.gitRemoteUrl, packageName: fingerprint.packageName });
|
|
6725
|
-
report.addCodeBlock("Fingerprint: File Tree", fingerprint.fileTree.join("\n"));
|
|
6726
|
-
report.addJson("Fingerprint: Detected Stack", { languages: fingerprint.languages, frameworks: fingerprint.frameworks, tools: fingerprint.tools });
|
|
6727
|
-
report.addJson("Fingerprint: Existing Configs", fingerprint.existingConfigs);
|
|
6728
|
-
if (fingerprint.codeAnalysis) report.addJson("Fingerprint: Code Analysis", fingerprint.codeAnalysis);
|
|
6729
|
-
}
|
|
6730
|
-
const isEmpty = fingerprint.fileTree.length < 3;
|
|
6731
|
-
if (isEmpty) {
|
|
6732
|
-
fingerprint.description = await promptInput("What will you build in this project?");
|
|
6733
|
-
}
|
|
6734
|
-
const failingForDismissal = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
|
|
6735
|
-
if (failingForDismissal.length > 0) {
|
|
6736
|
-
const newDismissals = await evaluateDismissals(failingForDismissal, fingerprint);
|
|
6737
|
-
if (newDismissals.length > 0) {
|
|
6738
|
-
const existing = readDismissedChecks();
|
|
6739
|
-
const existingIds = new Set(existing.map((d) => d.id));
|
|
6740
|
-
const merged = [...existing, ...newDismissals.filter((d) => !existingIds.has(d.id))];
|
|
6741
|
-
writeDismissedChecks(merged);
|
|
6742
|
-
baselineScore = computeLocalScore(process.cwd(), targetAgent);
|
|
6743
|
-
}
|
|
6744
|
-
}
|
|
6745
|
-
let failingChecks;
|
|
6746
|
-
let passingChecks;
|
|
6747
|
-
let currentScore;
|
|
6748
|
-
if (hasExistingConfig && baselineScore.score >= 95 && !options.force) {
|
|
6749
|
-
const currentLlmFixable = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0 && !NON_LLM_CHECKS.has(c.id));
|
|
6750
|
-
failingChecks = currentLlmFixable.map((c) => ({ name: c.name, suggestion: c.suggestion, fix: c.fix }));
|
|
6751
|
-
passingChecks = baselineScore.checks.filter((c) => c.passed).map((c) => ({ name: c.name }));
|
|
6752
|
-
currentScore = baselineScore.score;
|
|
6753
|
-
}
|
|
6754
|
-
if (report) {
|
|
6755
|
-
const fullPrompt = buildGeneratePrompt(fingerprint, targetAgent, fingerprint.description, failingChecks, currentScore, passingChecks);
|
|
6756
|
-
report.addCodeBlock("Generation: Full LLM Prompt", fullPrompt);
|
|
6757
|
-
}
|
|
6758
6698
|
const display = new ParallelTaskDisplay();
|
|
6699
|
+
const TASK_STACK = display.add("Detecting project stack");
|
|
6759
6700
|
const TASK_CONFIG = display.add("Generating configs");
|
|
6760
6701
|
const TASK_SKILLS_GEN = display.add("Generating skills");
|
|
6761
6702
|
const TASK_SKILLS_SEARCH = wantsSkills ? display.add("Searching community skills") : -1;
|
|
6762
6703
|
display.start();
|
|
6763
6704
|
try {
|
|
6705
|
+
display.update(TASK_STACK, "running");
|
|
6706
|
+
fingerprint = await collectFingerprint(process.cwd());
|
|
6707
|
+
const stackSummary = [
|
|
6708
|
+
...fingerprint.languages,
|
|
6709
|
+
...fingerprint.frameworks
|
|
6710
|
+
].join(", ") || "no languages";
|
|
6711
|
+
display.update(TASK_STACK, "done", stackSummary);
|
|
6712
|
+
trackInitProjectDiscovered(fingerprint.languages.length, fingerprint.frameworks.length, fingerprint.fileTree.length);
|
|
6713
|
+
log(options.verbose, `Fingerprint: ${fingerprint.languages.length} languages, ${fingerprint.frameworks.length} frameworks, ${fingerprint.fileTree.length} files`);
|
|
6714
|
+
if (report) {
|
|
6715
|
+
report.addJson("Fingerprint: Git", { remote: fingerprint.gitRemoteUrl, packageName: fingerprint.packageName });
|
|
6716
|
+
report.addCodeBlock("Fingerprint: File Tree", fingerprint.fileTree.join("\n"));
|
|
6717
|
+
report.addJson("Fingerprint: Detected Stack", { languages: fingerprint.languages, frameworks: fingerprint.frameworks, tools: fingerprint.tools });
|
|
6718
|
+
report.addJson("Fingerprint: Existing Configs", fingerprint.existingConfigs);
|
|
6719
|
+
if (fingerprint.codeAnalysis) report.addJson("Fingerprint: Code Analysis", fingerprint.codeAnalysis);
|
|
6720
|
+
}
|
|
6721
|
+
const isEmpty = fingerprint.fileTree.length < 3;
|
|
6722
|
+
if (isEmpty) {
|
|
6723
|
+
display.stop();
|
|
6724
|
+
fingerprint.description = await promptInput("What will you build in this project?");
|
|
6725
|
+
display.start();
|
|
6726
|
+
}
|
|
6727
|
+
const failingForDismissal = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
|
|
6728
|
+
if (failingForDismissal.length > 0) {
|
|
6729
|
+
const newDismissals = await evaluateDismissals(failingForDismissal, fingerprint);
|
|
6730
|
+
if (newDismissals.length > 0) {
|
|
6731
|
+
const existing = readDismissedChecks();
|
|
6732
|
+
const existingIds = new Set(existing.map((d) => d.id));
|
|
6733
|
+
const merged = [...existing, ...newDismissals.filter((d) => !existingIds.has(d.id))];
|
|
6734
|
+
writeDismissedChecks(merged);
|
|
6735
|
+
baselineScore = computeLocalScore(process.cwd(), targetAgent);
|
|
6736
|
+
}
|
|
6737
|
+
}
|
|
6738
|
+
let failingChecks;
|
|
6739
|
+
let passingChecks;
|
|
6740
|
+
let currentScore;
|
|
6741
|
+
if (hasExistingConfig && baselineScore.score >= 95 && !options.force) {
|
|
6742
|
+
const currentLlmFixable = baselineScore.checks.filter((c) => !c.passed && c.maxPoints > 0 && !NON_LLM_CHECKS.has(c.id));
|
|
6743
|
+
failingChecks = currentLlmFixable.map((c) => ({ name: c.name, suggestion: c.suggestion, fix: c.fix }));
|
|
6744
|
+
passingChecks = baselineScore.checks.filter((c) => c.passed).map((c) => ({ name: c.name }));
|
|
6745
|
+
currentScore = baselineScore.score;
|
|
6746
|
+
}
|
|
6747
|
+
if (report) {
|
|
6748
|
+
const fullPrompt = buildGeneratePrompt(fingerprint, targetAgent, fingerprint.description, failingChecks, currentScore, passingChecks);
|
|
6749
|
+
report.addCodeBlock("Generation: Full LLM Prompt", fullPrompt);
|
|
6750
|
+
}
|
|
6764
6751
|
display.update(TASK_CONFIG, "running");
|
|
6765
6752
|
const generatePromise = (async () => {
|
|
6766
6753
|
const result = await generateSetup(
|
|
@@ -6813,7 +6800,7 @@ async function initCommand(options) {
|
|
|
6813
6800
|
]);
|
|
6814
6801
|
skillSearchResult = await searchWithTimeout;
|
|
6815
6802
|
const count = skillSearchResult.results.length;
|
|
6816
|
-
display.update(TASK_SKILLS_SEARCH, "done", count > 0 ? `${count}
|
|
6803
|
+
display.update(TASK_SKILLS_SEARCH, "done", count > 0 ? `${count} found` : "No matches");
|
|
6817
6804
|
} catch (err) {
|
|
6818
6805
|
const reason = err instanceof Error && err.message === "timeout" ? "Timed out" : "Search failed";
|
|
6819
6806
|
display.update(TASK_SKILLS_SEARCH, "failed", reason);
|
|
@@ -6836,7 +6823,7 @@ async function initCommand(options) {
|
|
|
6836
6823
|
const secs = Math.floor(elapsedMs % 6e4 / 1e3);
|
|
6837
6824
|
const timeStr = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
|
|
6838
6825
|
console.log(chalk10.dim(`
|
|
6839
|
-
|
|
6826
|
+
Done in ${timeStr}
|
|
6840
6827
|
`));
|
|
6841
6828
|
if (!generatedSetup) {
|
|
6842
6829
|
console.log(chalk10.red(" Failed to generate setup."));
|
|
@@ -6869,8 +6856,9 @@ async function initCommand(options) {
|
|
|
6869
6856
|
} else {
|
|
6870
6857
|
console.log("");
|
|
6871
6858
|
}
|
|
6859
|
+
const hasSkillResults = skillSearchResult.results.length > 0;
|
|
6872
6860
|
let action;
|
|
6873
|
-
if (totalChanges === 0 &&
|
|
6861
|
+
if (totalChanges === 0 && !hasSkillResults) {
|
|
6874
6862
|
console.log(chalk10.dim(" No changes needed \u2014 your configs are already up to date.\n"));
|
|
6875
6863
|
cleanupStaging();
|
|
6876
6864
|
action = "accept";
|
|
@@ -6880,13 +6868,20 @@ async function initCommand(options) {
|
|
|
6880
6868
|
trackInitReviewAction(action, "auto-approved");
|
|
6881
6869
|
} else {
|
|
6882
6870
|
if (totalChanges > 0) {
|
|
6883
|
-
const
|
|
6884
|
-
|
|
6871
|
+
const reviewChoice = await select5({
|
|
6872
|
+
message: "Review your tailored setup?",
|
|
6873
|
+
choices: [
|
|
6874
|
+
{ name: "Yes, show me the diffs", value: "review" },
|
|
6875
|
+
...hasSkillResults ? [{ name: `No, continue to community skills (${skillSearchResult.results.length} found)`, value: "skip" }] : [],
|
|
6876
|
+
{ name: "No, continue", value: "skip" }
|
|
6877
|
+
]
|
|
6878
|
+
});
|
|
6879
|
+
if (reviewChoice === "review") {
|
|
6885
6880
|
const reviewMethod = await promptReviewMethod();
|
|
6886
6881
|
await openReview(reviewMethod, staged.stagedFiles);
|
|
6887
6882
|
}
|
|
6888
6883
|
}
|
|
6889
|
-
action = await promptReviewAction();
|
|
6884
|
+
action = await promptReviewAction(hasSkillResults);
|
|
6890
6885
|
trackInitReviewAction(action, totalChanges > 0 ? "reviewed" : "skipped");
|
|
6891
6886
|
}
|
|
6892
6887
|
let refinementRound = 0;
|
|
@@ -6905,7 +6900,7 @@ async function initCommand(options) {
|
|
|
6905
6900
|
`));
|
|
6906
6901
|
printSetupSummary(generatedSetup);
|
|
6907
6902
|
await openReview("terminal", restaged.stagedFiles);
|
|
6908
|
-
action = await promptReviewAction();
|
|
6903
|
+
action = await promptReviewAction(hasSkillResults);
|
|
6909
6904
|
trackInitReviewAction(action, "terminal");
|
|
6910
6905
|
}
|
|
6911
6906
|
cleanupStaging();
|
|
@@ -6921,7 +6916,7 @@ async function initCommand(options) {
|
|
|
6921
6916
|
}
|
|
6922
6917
|
const writeSpinner = ora2("Writing config files...").start();
|
|
6923
6918
|
try {
|
|
6924
|
-
if (targetAgent.includes("codex") && !
|
|
6919
|
+
if (targetAgent.includes("codex") && !fs25.existsSync("AGENTS.md") && !generatedSetup.codex) {
|
|
6925
6920
|
const claude = generatedSetup.claude;
|
|
6926
6921
|
const cursor = generatedSetup.cursor;
|
|
6927
6922
|
const agentRefs = [];
|
|
@@ -7062,9 +7057,9 @@ ${agentRefs.join(" ")}
|
|
|
7062
7057
|
}
|
|
7063
7058
|
if (report) {
|
|
7064
7059
|
report.markStep("Finished");
|
|
7065
|
-
const reportPath =
|
|
7060
|
+
const reportPath = path20.join(process.cwd(), ".caliber", "debug-report.md");
|
|
7066
7061
|
report.write(reportPath);
|
|
7067
|
-
console.log(chalk10.dim(` Debug report written to ${
|
|
7062
|
+
console.log(chalk10.dim(` Debug report written to ${path20.relative(process.cwd(), reportPath)}
|
|
7068
7063
|
`));
|
|
7069
7064
|
}
|
|
7070
7065
|
}
|
|
@@ -7111,7 +7106,7 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
|
|
|
7111
7106
|
}
|
|
7112
7107
|
function summarizeSetup(action, setup) {
|
|
7113
7108
|
const descriptions = setup.fileDescriptions;
|
|
7114
|
-
const files = descriptions ? Object.entries(descriptions).map(([
|
|
7109
|
+
const files = descriptions ? Object.entries(descriptions).map(([path28, desc]) => ` ${path28}: ${desc}`).join("\n") : Object.keys(setup).filter((k) => k !== "targetAgent" && k !== "fileDescriptions").join(", ");
|
|
7115
7110
|
return `${action}. Files:
|
|
7116
7111
|
${files}`;
|
|
7117
7112
|
}
|
|
@@ -7205,13 +7200,14 @@ async function promptHookType(targetAgent) {
|
|
|
7205
7200
|
choices
|
|
7206
7201
|
});
|
|
7207
7202
|
}
|
|
7208
|
-
async function promptReviewAction() {
|
|
7203
|
+
async function promptReviewAction(hasSkillResults = false) {
|
|
7204
|
+
const acceptLabel = hasSkillResults ? "Accept and continue to community skills" : "Accept and apply";
|
|
7209
7205
|
return select5({
|
|
7210
7206
|
message: "What would you like to do?",
|
|
7211
7207
|
choices: [
|
|
7212
|
-
{ name:
|
|
7208
|
+
{ name: acceptLabel, value: "accept" },
|
|
7213
7209
|
{ name: "Refine via chat", value: "refine" },
|
|
7214
|
-
{ name: "Decline", value: "decline" }
|
|
7210
|
+
{ name: "Decline all changes", value: "decline" }
|
|
7215
7211
|
]
|
|
7216
7212
|
});
|
|
7217
7213
|
}
|
|
@@ -7221,13 +7217,13 @@ function printSetupSummary(setup) {
|
|
|
7221
7217
|
const fileDescriptions = setup.fileDescriptions;
|
|
7222
7218
|
const deletions = setup.deletions;
|
|
7223
7219
|
console.log("");
|
|
7224
|
-
console.log(chalk10.bold("
|
|
7220
|
+
console.log(chalk10.bold(" Your tailored setup:\n"));
|
|
7225
7221
|
const getDescription = (filePath) => {
|
|
7226
7222
|
return fileDescriptions?.[filePath];
|
|
7227
7223
|
};
|
|
7228
7224
|
if (claude) {
|
|
7229
7225
|
if (claude.claudeMd) {
|
|
7230
|
-
const icon =
|
|
7226
|
+
const icon = fs25.existsSync("CLAUDE.md") ? chalk10.yellow("~") : chalk10.green("+");
|
|
7231
7227
|
const desc = getDescription("CLAUDE.md");
|
|
7232
7228
|
console.log(` ${icon} ${chalk10.bold("CLAUDE.md")}`);
|
|
7233
7229
|
if (desc) console.log(chalk10.dim(` ${desc}`));
|
|
@@ -7237,7 +7233,7 @@ function printSetupSummary(setup) {
|
|
|
7237
7233
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
7238
7234
|
for (const skill of skills) {
|
|
7239
7235
|
const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
|
|
7240
|
-
const icon =
|
|
7236
|
+
const icon = fs25.existsSync(skillPath) ? chalk10.yellow("~") : chalk10.green("+");
|
|
7241
7237
|
const desc = getDescription(skillPath);
|
|
7242
7238
|
console.log(` ${icon} ${chalk10.bold(skillPath)}`);
|
|
7243
7239
|
console.log(chalk10.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -7248,7 +7244,7 @@ function printSetupSummary(setup) {
|
|
|
7248
7244
|
const codex = setup.codex;
|
|
7249
7245
|
if (codex) {
|
|
7250
7246
|
if (codex.agentsMd) {
|
|
7251
|
-
const icon =
|
|
7247
|
+
const icon = fs25.existsSync("AGENTS.md") ? chalk10.yellow("~") : chalk10.green("+");
|
|
7252
7248
|
const desc = getDescription("AGENTS.md");
|
|
7253
7249
|
console.log(` ${icon} ${chalk10.bold("AGENTS.md")}`);
|
|
7254
7250
|
if (desc) console.log(chalk10.dim(` ${desc}`));
|
|
@@ -7258,7 +7254,7 @@ function printSetupSummary(setup) {
|
|
|
7258
7254
|
if (Array.isArray(codexSkills) && codexSkills.length > 0) {
|
|
7259
7255
|
for (const skill of codexSkills) {
|
|
7260
7256
|
const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
|
|
7261
|
-
const icon =
|
|
7257
|
+
const icon = fs25.existsSync(skillPath) ? chalk10.yellow("~") : chalk10.green("+");
|
|
7262
7258
|
const desc = getDescription(skillPath);
|
|
7263
7259
|
console.log(` ${icon} ${chalk10.bold(skillPath)}`);
|
|
7264
7260
|
console.log(chalk10.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -7268,7 +7264,7 @@ function printSetupSummary(setup) {
|
|
|
7268
7264
|
}
|
|
7269
7265
|
if (cursor) {
|
|
7270
7266
|
if (cursor.cursorrules) {
|
|
7271
|
-
const icon =
|
|
7267
|
+
const icon = fs25.existsSync(".cursorrules") ? chalk10.yellow("~") : chalk10.green("+");
|
|
7272
7268
|
const desc = getDescription(".cursorrules");
|
|
7273
7269
|
console.log(` ${icon} ${chalk10.bold(".cursorrules")}`);
|
|
7274
7270
|
if (desc) console.log(chalk10.dim(` ${desc}`));
|
|
@@ -7278,7 +7274,7 @@ function printSetupSummary(setup) {
|
|
|
7278
7274
|
if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
|
|
7279
7275
|
for (const skill of cursorSkills) {
|
|
7280
7276
|
const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
|
|
7281
|
-
const icon =
|
|
7277
|
+
const icon = fs25.existsSync(skillPath) ? chalk10.yellow("~") : chalk10.green("+");
|
|
7282
7278
|
const desc = getDescription(skillPath);
|
|
7283
7279
|
console.log(` ${icon} ${chalk10.bold(skillPath)}`);
|
|
7284
7280
|
console.log(chalk10.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -7289,7 +7285,7 @@ function printSetupSummary(setup) {
|
|
|
7289
7285
|
if (Array.isArray(rules) && rules.length > 0) {
|
|
7290
7286
|
for (const rule of rules) {
|
|
7291
7287
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
7292
|
-
const icon =
|
|
7288
|
+
const icon = fs25.existsSync(rulePath) ? chalk10.yellow("~") : chalk10.green("+");
|
|
7293
7289
|
const desc = getDescription(rulePath);
|
|
7294
7290
|
console.log(` ${icon} ${chalk10.bold(rulePath)}`);
|
|
7295
7291
|
if (desc) {
|
|
@@ -7353,8 +7349,8 @@ function ensurePermissions(fingerprint) {
|
|
|
7353
7349
|
const settingsPath = ".claude/settings.json";
|
|
7354
7350
|
let settings = {};
|
|
7355
7351
|
try {
|
|
7356
|
-
if (
|
|
7357
|
-
settings = JSON.parse(
|
|
7352
|
+
if (fs25.existsSync(settingsPath)) {
|
|
7353
|
+
settings = JSON.parse(fs25.readFileSync(settingsPath, "utf-8"));
|
|
7358
7354
|
}
|
|
7359
7355
|
} catch {
|
|
7360
7356
|
}
|
|
@@ -7363,8 +7359,8 @@ function ensurePermissions(fingerprint) {
|
|
|
7363
7359
|
if (Array.isArray(allow) && allow.length > 0) return;
|
|
7364
7360
|
permissions.allow = derivePermissions(fingerprint);
|
|
7365
7361
|
settings.permissions = permissions;
|
|
7366
|
-
if (!
|
|
7367
|
-
|
|
7362
|
+
if (!fs25.existsSync(".claude")) fs25.mkdirSync(".claude", { recursive: true });
|
|
7363
|
+
fs25.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
7368
7364
|
}
|
|
7369
7365
|
function displayTokenUsage() {
|
|
7370
7366
|
const summary = getUsageSummary();
|
|
@@ -7388,7 +7384,7 @@ function displayTokenUsage() {
|
|
|
7388
7384
|
}
|
|
7389
7385
|
function writeErrorLog(config, rawOutput, error, stopReason) {
|
|
7390
7386
|
try {
|
|
7391
|
-
const logPath =
|
|
7387
|
+
const logPath = path20.join(process.cwd(), ".caliber", "error-log.md");
|
|
7392
7388
|
const lines = [
|
|
7393
7389
|
`# Generation Error \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
7394
7390
|
"",
|
|
@@ -7401,8 +7397,8 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
|
|
|
7401
7397
|
lines.push("## Error", "```", error, "```", "");
|
|
7402
7398
|
}
|
|
7403
7399
|
lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
|
|
7404
|
-
|
|
7405
|
-
|
|
7400
|
+
fs25.mkdirSync(path20.join(process.cwd(), ".caliber"), { recursive: true });
|
|
7401
|
+
fs25.writeFileSync(logPath, lines.join("\n"));
|
|
7406
7402
|
console.log(chalk10.dim(`
|
|
7407
7403
|
Error log written to .caliber/error-log.md`));
|
|
7408
7404
|
} catch {
|
|
@@ -7443,7 +7439,7 @@ function undoCommand() {
|
|
|
7443
7439
|
|
|
7444
7440
|
// src/commands/status.ts
|
|
7445
7441
|
import chalk12 from "chalk";
|
|
7446
|
-
import
|
|
7442
|
+
import fs26 from "fs";
|
|
7447
7443
|
init_config();
|
|
7448
7444
|
async function statusCommand(options) {
|
|
7449
7445
|
const config = loadConfig();
|
|
@@ -7470,7 +7466,7 @@ async function statusCommand(options) {
|
|
|
7470
7466
|
}
|
|
7471
7467
|
console.log(` Files managed: ${chalk12.cyan(manifest.entries.length.toString())}`);
|
|
7472
7468
|
for (const entry of manifest.entries) {
|
|
7473
|
-
const exists =
|
|
7469
|
+
const exists = fs26.existsSync(entry.path);
|
|
7474
7470
|
const icon = exists ? chalk12.green("\u2713") : chalk12.red("\u2717");
|
|
7475
7471
|
console.log(` ${icon} ${entry.path} (${entry.action})`);
|
|
7476
7472
|
}
|
|
@@ -7651,8 +7647,8 @@ async function scoreCommand(options) {
|
|
|
7651
7647
|
}
|
|
7652
7648
|
|
|
7653
7649
|
// src/commands/refresh.ts
|
|
7654
|
-
import
|
|
7655
|
-
import
|
|
7650
|
+
import fs30 from "fs";
|
|
7651
|
+
import path24 from "path";
|
|
7656
7652
|
import chalk15 from "chalk";
|
|
7657
7653
|
import ora5 from "ora";
|
|
7658
7654
|
|
|
@@ -7730,37 +7726,37 @@ function collectDiff(lastSha) {
|
|
|
7730
7726
|
}
|
|
7731
7727
|
|
|
7732
7728
|
// src/writers/refresh.ts
|
|
7733
|
-
import
|
|
7734
|
-
import
|
|
7729
|
+
import fs27 from "fs";
|
|
7730
|
+
import path21 from "path";
|
|
7735
7731
|
function writeRefreshDocs(docs) {
|
|
7736
7732
|
const written = [];
|
|
7737
7733
|
if (docs.claudeMd) {
|
|
7738
|
-
|
|
7734
|
+
fs27.writeFileSync("CLAUDE.md", docs.claudeMd);
|
|
7739
7735
|
written.push("CLAUDE.md");
|
|
7740
7736
|
}
|
|
7741
7737
|
if (docs.readmeMd) {
|
|
7742
|
-
|
|
7738
|
+
fs27.writeFileSync("README.md", docs.readmeMd);
|
|
7743
7739
|
written.push("README.md");
|
|
7744
7740
|
}
|
|
7745
7741
|
if (docs.cursorrules) {
|
|
7746
|
-
|
|
7742
|
+
fs27.writeFileSync(".cursorrules", docs.cursorrules);
|
|
7747
7743
|
written.push(".cursorrules");
|
|
7748
7744
|
}
|
|
7749
7745
|
if (docs.cursorRules) {
|
|
7750
|
-
const rulesDir =
|
|
7751
|
-
if (!
|
|
7746
|
+
const rulesDir = path21.join(".cursor", "rules");
|
|
7747
|
+
if (!fs27.existsSync(rulesDir)) fs27.mkdirSync(rulesDir, { recursive: true });
|
|
7752
7748
|
for (const rule of docs.cursorRules) {
|
|
7753
|
-
const filePath =
|
|
7754
|
-
|
|
7749
|
+
const filePath = path21.join(rulesDir, rule.filename);
|
|
7750
|
+
fs27.writeFileSync(filePath, rule.content);
|
|
7755
7751
|
written.push(filePath);
|
|
7756
7752
|
}
|
|
7757
7753
|
}
|
|
7758
7754
|
if (docs.claudeSkills) {
|
|
7759
|
-
const skillsDir =
|
|
7760
|
-
if (!
|
|
7755
|
+
const skillsDir = path21.join(".claude", "skills");
|
|
7756
|
+
if (!fs27.existsSync(skillsDir)) fs27.mkdirSync(skillsDir, { recursive: true });
|
|
7761
7757
|
for (const skill of docs.claudeSkills) {
|
|
7762
|
-
const filePath =
|
|
7763
|
-
|
|
7758
|
+
const filePath = path21.join(skillsDir, skill.filename);
|
|
7759
|
+
fs27.writeFileSync(filePath, skill.content);
|
|
7764
7760
|
written.push(filePath);
|
|
7765
7761
|
}
|
|
7766
7762
|
}
|
|
@@ -7837,8 +7833,8 @@ Changed files: ${diff.changedFiles.join(", ")}`);
|
|
|
7837
7833
|
}
|
|
7838
7834
|
|
|
7839
7835
|
// src/learner/writer.ts
|
|
7840
|
-
import
|
|
7841
|
-
import
|
|
7836
|
+
import fs28 from "fs";
|
|
7837
|
+
import path22 from "path";
|
|
7842
7838
|
var LEARNINGS_FILE = "CALIBER_LEARNINGS.md";
|
|
7843
7839
|
var LEARNINGS_HEADER = `# Caliber Learnings
|
|
7844
7840
|
|
|
@@ -7922,16 +7918,16 @@ function deduplicateLearnedItems(existing, incoming) {
|
|
|
7922
7918
|
function writeLearnedSection(content) {
|
|
7923
7919
|
const existingSection = readLearnedSection();
|
|
7924
7920
|
const { merged, newCount, newItems } = deduplicateLearnedItems(existingSection, content);
|
|
7925
|
-
|
|
7921
|
+
fs28.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + merged + "\n");
|
|
7926
7922
|
return { newCount, newItems };
|
|
7927
7923
|
}
|
|
7928
7924
|
function writeLearnedSkill(skill) {
|
|
7929
|
-
const skillDir =
|
|
7930
|
-
if (!
|
|
7931
|
-
const skillPath =
|
|
7932
|
-
if (!skill.isNew &&
|
|
7933
|
-
const existing =
|
|
7934
|
-
|
|
7925
|
+
const skillDir = path22.join(".claude", "skills", skill.name);
|
|
7926
|
+
if (!fs28.existsSync(skillDir)) fs28.mkdirSync(skillDir, { recursive: true });
|
|
7927
|
+
const skillPath = path22.join(skillDir, "SKILL.md");
|
|
7928
|
+
if (!skill.isNew && fs28.existsSync(skillPath)) {
|
|
7929
|
+
const existing = fs28.readFileSync(skillPath, "utf-8");
|
|
7930
|
+
fs28.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
|
|
7935
7931
|
} else {
|
|
7936
7932
|
const frontmatter = [
|
|
7937
7933
|
"---",
|
|
@@ -7940,37 +7936,37 @@ function writeLearnedSkill(skill) {
|
|
|
7940
7936
|
"---",
|
|
7941
7937
|
""
|
|
7942
7938
|
].join("\n");
|
|
7943
|
-
|
|
7939
|
+
fs28.writeFileSync(skillPath, frontmatter + skill.content);
|
|
7944
7940
|
}
|
|
7945
7941
|
return skillPath;
|
|
7946
7942
|
}
|
|
7947
7943
|
function readLearnedSection() {
|
|
7948
|
-
if (
|
|
7949
|
-
const content2 =
|
|
7944
|
+
if (fs28.existsSync(LEARNINGS_FILE)) {
|
|
7945
|
+
const content2 = fs28.readFileSync(LEARNINGS_FILE, "utf-8");
|
|
7950
7946
|
const bullets = content2.split("\n").filter((l) => l.startsWith("- ")).join("\n");
|
|
7951
7947
|
return bullets || null;
|
|
7952
7948
|
}
|
|
7953
7949
|
const claudeMdPath = "CLAUDE.md";
|
|
7954
|
-
if (!
|
|
7955
|
-
const content =
|
|
7950
|
+
if (!fs28.existsSync(claudeMdPath)) return null;
|
|
7951
|
+
const content = fs28.readFileSync(claudeMdPath, "utf-8");
|
|
7956
7952
|
const startIdx = content.indexOf(LEARNED_START);
|
|
7957
7953
|
const endIdx = content.indexOf(LEARNED_END);
|
|
7958
7954
|
if (startIdx === -1 || endIdx === -1) return null;
|
|
7959
7955
|
return content.slice(startIdx + LEARNED_START.length, endIdx).trim() || null;
|
|
7960
7956
|
}
|
|
7961
7957
|
function migrateInlineLearnings() {
|
|
7962
|
-
if (
|
|
7958
|
+
if (fs28.existsSync(LEARNINGS_FILE)) return false;
|
|
7963
7959
|
const claudeMdPath = "CLAUDE.md";
|
|
7964
|
-
if (!
|
|
7965
|
-
const content =
|
|
7960
|
+
if (!fs28.existsSync(claudeMdPath)) return false;
|
|
7961
|
+
const content = fs28.readFileSync(claudeMdPath, "utf-8");
|
|
7966
7962
|
const startIdx = content.indexOf(LEARNED_START);
|
|
7967
7963
|
const endIdx = content.indexOf(LEARNED_END);
|
|
7968
7964
|
if (startIdx === -1 || endIdx === -1) return false;
|
|
7969
7965
|
const section = content.slice(startIdx + LEARNED_START.length, endIdx).trim();
|
|
7970
7966
|
if (!section) return false;
|
|
7971
|
-
|
|
7967
|
+
fs28.writeFileSync(LEARNINGS_FILE, LEARNINGS_HEADER + section + "\n");
|
|
7972
7968
|
const cleaned = content.slice(0, startIdx) + content.slice(endIdx + LEARNED_END.length);
|
|
7973
|
-
|
|
7969
|
+
fs28.writeFileSync(claudeMdPath, cleaned.replace(/\n{3,}/g, "\n\n").trim() + "\n");
|
|
7974
7970
|
return true;
|
|
7975
7971
|
}
|
|
7976
7972
|
|
|
@@ -7982,11 +7978,11 @@ function log2(quiet, ...args) {
|
|
|
7982
7978
|
function discoverGitRepos(parentDir) {
|
|
7983
7979
|
const repos = [];
|
|
7984
7980
|
try {
|
|
7985
|
-
const entries =
|
|
7981
|
+
const entries = fs30.readdirSync(parentDir, { withFileTypes: true });
|
|
7986
7982
|
for (const entry of entries) {
|
|
7987
7983
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
7988
|
-
const childPath =
|
|
7989
|
-
if (
|
|
7984
|
+
const childPath = path24.join(parentDir, entry.name);
|
|
7985
|
+
if (fs30.existsSync(path24.join(childPath, ".git"))) {
|
|
7990
7986
|
repos.push(childPath);
|
|
7991
7987
|
}
|
|
7992
7988
|
}
|
|
@@ -8089,7 +8085,7 @@ async function refreshCommand(options) {
|
|
|
8089
8085
|
`));
|
|
8090
8086
|
const originalDir = process.cwd();
|
|
8091
8087
|
for (const repo of repos) {
|
|
8092
|
-
const repoName =
|
|
8088
|
+
const repoName = path24.basename(repo);
|
|
8093
8089
|
try {
|
|
8094
8090
|
process.chdir(repo);
|
|
8095
8091
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
@@ -8313,7 +8309,7 @@ async function configCommand() {
|
|
|
8313
8309
|
}
|
|
8314
8310
|
|
|
8315
8311
|
// src/commands/learn.ts
|
|
8316
|
-
import
|
|
8312
|
+
import fs32 from "fs";
|
|
8317
8313
|
import chalk18 from "chalk";
|
|
8318
8314
|
|
|
8319
8315
|
// src/learner/stdin.ts
|
|
@@ -8345,8 +8341,8 @@ function readStdin() {
|
|
|
8345
8341
|
|
|
8346
8342
|
// src/learner/storage.ts
|
|
8347
8343
|
init_constants();
|
|
8348
|
-
import
|
|
8349
|
-
import
|
|
8344
|
+
import fs31 from "fs";
|
|
8345
|
+
import path25 from "path";
|
|
8350
8346
|
var MAX_RESPONSE_LENGTH = 2e3;
|
|
8351
8347
|
var DEFAULT_STATE = {
|
|
8352
8348
|
sessionId: null,
|
|
@@ -8354,15 +8350,15 @@ var DEFAULT_STATE = {
|
|
|
8354
8350
|
lastAnalysisTimestamp: null
|
|
8355
8351
|
};
|
|
8356
8352
|
function ensureLearningDir() {
|
|
8357
|
-
if (!
|
|
8358
|
-
|
|
8353
|
+
if (!fs31.existsSync(LEARNING_DIR)) {
|
|
8354
|
+
fs31.mkdirSync(LEARNING_DIR, { recursive: true });
|
|
8359
8355
|
}
|
|
8360
8356
|
}
|
|
8361
8357
|
function sessionFilePath() {
|
|
8362
|
-
return
|
|
8358
|
+
return path25.join(LEARNING_DIR, LEARNING_SESSION_FILE);
|
|
8363
8359
|
}
|
|
8364
8360
|
function stateFilePath() {
|
|
8365
|
-
return
|
|
8361
|
+
return path25.join(LEARNING_DIR, LEARNING_STATE_FILE);
|
|
8366
8362
|
}
|
|
8367
8363
|
function truncateResponse(response) {
|
|
8368
8364
|
const str = JSON.stringify(response);
|
|
@@ -8373,29 +8369,29 @@ function appendEvent(event) {
|
|
|
8373
8369
|
ensureLearningDir();
|
|
8374
8370
|
const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
|
|
8375
8371
|
const filePath = sessionFilePath();
|
|
8376
|
-
|
|
8372
|
+
fs31.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
|
|
8377
8373
|
const count = getEventCount();
|
|
8378
8374
|
if (count > LEARNING_MAX_EVENTS) {
|
|
8379
|
-
const lines =
|
|
8375
|
+
const lines = fs31.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
8380
8376
|
const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
|
|
8381
|
-
|
|
8377
|
+
fs31.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
8382
8378
|
}
|
|
8383
8379
|
}
|
|
8384
8380
|
function appendPromptEvent(event) {
|
|
8385
8381
|
ensureLearningDir();
|
|
8386
8382
|
const filePath = sessionFilePath();
|
|
8387
|
-
|
|
8383
|
+
fs31.appendFileSync(filePath, JSON.stringify(event) + "\n");
|
|
8388
8384
|
const count = getEventCount();
|
|
8389
8385
|
if (count > LEARNING_MAX_EVENTS) {
|
|
8390
|
-
const lines =
|
|
8386
|
+
const lines = fs31.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
8391
8387
|
const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
|
|
8392
|
-
|
|
8388
|
+
fs31.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
8393
8389
|
}
|
|
8394
8390
|
}
|
|
8395
8391
|
function readAllEvents() {
|
|
8396
8392
|
const filePath = sessionFilePath();
|
|
8397
|
-
if (!
|
|
8398
|
-
const lines =
|
|
8393
|
+
if (!fs31.existsSync(filePath)) return [];
|
|
8394
|
+
const lines = fs31.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
8399
8395
|
const events = [];
|
|
8400
8396
|
for (const line of lines) {
|
|
8401
8397
|
try {
|
|
@@ -8407,26 +8403,26 @@ function readAllEvents() {
|
|
|
8407
8403
|
}
|
|
8408
8404
|
function getEventCount() {
|
|
8409
8405
|
const filePath = sessionFilePath();
|
|
8410
|
-
if (!
|
|
8411
|
-
const content =
|
|
8406
|
+
if (!fs31.existsSync(filePath)) return 0;
|
|
8407
|
+
const content = fs31.readFileSync(filePath, "utf-8");
|
|
8412
8408
|
return content.split("\n").filter(Boolean).length;
|
|
8413
8409
|
}
|
|
8414
8410
|
function clearSession() {
|
|
8415
8411
|
const filePath = sessionFilePath();
|
|
8416
|
-
if (
|
|
8412
|
+
if (fs31.existsSync(filePath)) fs31.unlinkSync(filePath);
|
|
8417
8413
|
}
|
|
8418
8414
|
function readState2() {
|
|
8419
8415
|
const filePath = stateFilePath();
|
|
8420
|
-
if (!
|
|
8416
|
+
if (!fs31.existsSync(filePath)) return { ...DEFAULT_STATE };
|
|
8421
8417
|
try {
|
|
8422
|
-
return JSON.parse(
|
|
8418
|
+
return JSON.parse(fs31.readFileSync(filePath, "utf-8"));
|
|
8423
8419
|
} catch {
|
|
8424
8420
|
return { ...DEFAULT_STATE };
|
|
8425
8421
|
}
|
|
8426
8422
|
}
|
|
8427
8423
|
function writeState2(state) {
|
|
8428
8424
|
ensureLearningDir();
|
|
8429
|
-
|
|
8425
|
+
fs31.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
|
|
8430
8426
|
}
|
|
8431
8427
|
function resetState() {
|
|
8432
8428
|
writeState2({ ...DEFAULT_STATE });
|
|
@@ -8434,14 +8430,14 @@ function resetState() {
|
|
|
8434
8430
|
var LOCK_FILE2 = "finalize.lock";
|
|
8435
8431
|
var LOCK_STALE_MS = 5 * 60 * 1e3;
|
|
8436
8432
|
function lockFilePath() {
|
|
8437
|
-
return
|
|
8433
|
+
return path25.join(LEARNING_DIR, LOCK_FILE2);
|
|
8438
8434
|
}
|
|
8439
8435
|
function acquireFinalizeLock() {
|
|
8440
8436
|
ensureLearningDir();
|
|
8441
8437
|
const lockPath = lockFilePath();
|
|
8442
|
-
if (
|
|
8438
|
+
if (fs31.existsSync(lockPath)) {
|
|
8443
8439
|
try {
|
|
8444
|
-
const stat =
|
|
8440
|
+
const stat = fs31.statSync(lockPath);
|
|
8445
8441
|
if (Date.now() - stat.mtimeMs < LOCK_STALE_MS) {
|
|
8446
8442
|
return false;
|
|
8447
8443
|
}
|
|
@@ -8449,7 +8445,7 @@ function acquireFinalizeLock() {
|
|
|
8449
8445
|
}
|
|
8450
8446
|
}
|
|
8451
8447
|
try {
|
|
8452
|
-
|
|
8448
|
+
fs31.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
8453
8449
|
return true;
|
|
8454
8450
|
} catch {
|
|
8455
8451
|
return false;
|
|
@@ -8458,7 +8454,7 @@ function acquireFinalizeLock() {
|
|
|
8458
8454
|
function releaseFinalizeLock() {
|
|
8459
8455
|
const lockPath = lockFilePath();
|
|
8460
8456
|
try {
|
|
8461
|
-
if (
|
|
8457
|
+
if (fs31.existsSync(lockPath)) fs31.unlinkSync(lockPath);
|
|
8462
8458
|
} catch {
|
|
8463
8459
|
}
|
|
8464
8460
|
}
|
|
@@ -8690,7 +8686,7 @@ async function learnFinalizeCommand(options) {
|
|
|
8690
8686
|
}
|
|
8691
8687
|
async function learnInstallCommand() {
|
|
8692
8688
|
let anyInstalled = false;
|
|
8693
|
-
if (
|
|
8689
|
+
if (fs32.existsSync(".claude")) {
|
|
8694
8690
|
const r = installLearningHooks();
|
|
8695
8691
|
if (r.installed) {
|
|
8696
8692
|
console.log(chalk18.green("\u2713") + " Claude Code learning hooks installed");
|
|
@@ -8699,7 +8695,7 @@ async function learnInstallCommand() {
|
|
|
8699
8695
|
console.log(chalk18.dim(" Claude Code hooks already installed"));
|
|
8700
8696
|
}
|
|
8701
8697
|
}
|
|
8702
|
-
if (
|
|
8698
|
+
if (fs32.existsSync(".cursor")) {
|
|
8703
8699
|
const r = installCursorLearningHooks();
|
|
8704
8700
|
if (r.installed) {
|
|
8705
8701
|
console.log(chalk18.green("\u2713") + " Cursor learning hooks installed");
|
|
@@ -8708,7 +8704,7 @@ async function learnInstallCommand() {
|
|
|
8708
8704
|
console.log(chalk18.dim(" Cursor hooks already installed"));
|
|
8709
8705
|
}
|
|
8710
8706
|
}
|
|
8711
|
-
if (!
|
|
8707
|
+
if (!fs32.existsSync(".claude") && !fs32.existsSync(".cursor")) {
|
|
8712
8708
|
console.log(chalk18.yellow("No .claude/ or .cursor/ directory found."));
|
|
8713
8709
|
console.log(chalk18.dim(" Run `caliber init` first, or create the directory manually."));
|
|
8714
8710
|
return;
|
|
@@ -8771,9 +8767,9 @@ Learned items in CALIBER_LEARNINGS.md: ${chalk18.cyan(String(lineCount))}`);
|
|
|
8771
8767
|
}
|
|
8772
8768
|
|
|
8773
8769
|
// src/cli.ts
|
|
8774
|
-
var __dirname =
|
|
8770
|
+
var __dirname = path26.dirname(fileURLToPath(import.meta.url));
|
|
8775
8771
|
var pkg = JSON.parse(
|
|
8776
|
-
|
|
8772
|
+
fs33.readFileSync(path26.resolve(__dirname, "..", "package.json"), "utf-8")
|
|
8777
8773
|
);
|
|
8778
8774
|
var program = new Command();
|
|
8779
8775
|
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
|
|
@@ -8847,22 +8843,42 @@ learn.command("remove").description("Remove learning hooks from .claude/settings
|
|
|
8847
8843
|
learn.command("status").description("Show learning system status").action(tracked("learn:status", learnStatusCommand));
|
|
8848
8844
|
|
|
8849
8845
|
// src/utils/version-check.ts
|
|
8850
|
-
import
|
|
8851
|
-
import
|
|
8846
|
+
import fs34 from "fs";
|
|
8847
|
+
import path27 from "path";
|
|
8852
8848
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8853
8849
|
import { execSync as execSync14 } from "child_process";
|
|
8854
8850
|
import chalk19 from "chalk";
|
|
8855
8851
|
import ora6 from "ora";
|
|
8856
8852
|
import confirm2 from "@inquirer/confirm";
|
|
8857
|
-
var __dirname_vc =
|
|
8853
|
+
var __dirname_vc = path27.dirname(fileURLToPath2(import.meta.url));
|
|
8858
8854
|
var pkg2 = JSON.parse(
|
|
8859
|
-
|
|
8855
|
+
fs34.readFileSync(path27.resolve(__dirname_vc, "..", "package.json"), "utf-8")
|
|
8860
8856
|
);
|
|
8857
|
+
function getChannel(version) {
|
|
8858
|
+
const match = version.match(/-(dev|next)\./);
|
|
8859
|
+
return match ? match[1] : "latest";
|
|
8860
|
+
}
|
|
8861
|
+
function isNewer(registry, current) {
|
|
8862
|
+
const parse = (v) => {
|
|
8863
|
+
const [core, pre] = v.split("-");
|
|
8864
|
+
const parts = core.split(".").map(Number);
|
|
8865
|
+
return { major: parts[0], minor: parts[1], patch: parts[2], pre };
|
|
8866
|
+
};
|
|
8867
|
+
const r = parse(registry);
|
|
8868
|
+
const c = parse(current);
|
|
8869
|
+
if (r.major !== c.major) return r.major > c.major;
|
|
8870
|
+
if (r.minor !== c.minor) return r.minor > c.minor;
|
|
8871
|
+
if (r.patch !== c.patch) return r.patch > c.patch;
|
|
8872
|
+
if (!r.pre && c.pre) return true;
|
|
8873
|
+
if (r.pre && !c.pre) return false;
|
|
8874
|
+
if (r.pre && c.pre) return r.pre > c.pre;
|
|
8875
|
+
return false;
|
|
8876
|
+
}
|
|
8861
8877
|
function getInstalledVersion() {
|
|
8862
8878
|
try {
|
|
8863
8879
|
const globalRoot = execSync14("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
8864
|
-
const pkgPath =
|
|
8865
|
-
return JSON.parse(
|
|
8880
|
+
const pkgPath = path27.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
8881
|
+
return JSON.parse(fs34.readFileSync(pkgPath, "utf-8")).version;
|
|
8866
8882
|
} catch {
|
|
8867
8883
|
return null;
|
|
8868
8884
|
}
|
|
@@ -8870,9 +8886,11 @@ function getInstalledVersion() {
|
|
|
8870
8886
|
async function checkForUpdates() {
|
|
8871
8887
|
if (process.env.CALIBER_SKIP_UPDATE_CHECK) return;
|
|
8872
8888
|
try {
|
|
8889
|
+
const current = pkg2.version;
|
|
8890
|
+
const channel = getChannel(current);
|
|
8873
8891
|
const controller = new AbortController();
|
|
8874
8892
|
const timeout = setTimeout(() => controller.abort(), 2e3);
|
|
8875
|
-
const res = await fetch(
|
|
8893
|
+
const res = await fetch(`https://registry.npmjs.org/@rely-ai/caliber/${channel}`, {
|
|
8876
8894
|
signal: controller.signal
|
|
8877
8895
|
});
|
|
8878
8896
|
clearTimeout(timeout);
|
|
@@ -8880,15 +8898,15 @@ async function checkForUpdates() {
|
|
|
8880
8898
|
const data = await res.json();
|
|
8881
8899
|
const latest = data.version;
|
|
8882
8900
|
if (!latest) return;
|
|
8883
|
-
|
|
8884
|
-
if (current === latest) return;
|
|
8901
|
+
if (!isNewer(latest, current)) return;
|
|
8885
8902
|
const isInteractive = process.stdin.isTTY === true;
|
|
8886
8903
|
if (!isInteractive) {
|
|
8904
|
+
const installTag = channel === "latest" ? "" : `@${channel}`;
|
|
8887
8905
|
console.log(
|
|
8888
8906
|
chalk19.yellow(
|
|
8889
8907
|
`
|
|
8890
8908
|
Update available: ${current} -> ${latest}
|
|
8891
|
-
Run ${chalk19.bold(
|
|
8909
|
+
Run ${chalk19.bold(`npm install -g @rely-ai/caliber${installTag}`)} to upgrade.
|
|
8892
8910
|
`
|
|
8893
8911
|
)
|
|
8894
8912
|
);
|
|
@@ -8903,9 +8921,10 @@ Update available: ${current} -> ${latest}`)
|
|
|
8903
8921
|
console.log();
|
|
8904
8922
|
return;
|
|
8905
8923
|
}
|
|
8924
|
+
const tag = channel === "latest" ? latest : channel;
|
|
8906
8925
|
const spinner = ora6("Updating caliber...").start();
|
|
8907
8926
|
try {
|
|
8908
|
-
execSync14(`npm install -g @rely-ai/caliber@${
|
|
8927
|
+
execSync14(`npm install -g @rely-ai/caliber@${tag}`, {
|
|
8909
8928
|
stdio: "pipe",
|
|
8910
8929
|
timeout: 12e4,
|
|
8911
8930
|
env: { ...process.env, npm_config_fund: "false", npm_config_audit: "false" }
|
|
@@ -8913,7 +8932,7 @@ Update available: ${current} -> ${latest}`)
|
|
|
8913
8932
|
const installed = getInstalledVersion();
|
|
8914
8933
|
if (installed !== latest) {
|
|
8915
8934
|
spinner.fail(`Update incomplete \u2014 got ${installed ?? "unknown"}, expected ${latest}`);
|
|
8916
|
-
console.log(chalk19.yellow(`Run ${chalk19.bold(`npm install -g @rely-ai/caliber@${
|
|
8935
|
+
console.log(chalk19.yellow(`Run ${chalk19.bold(`npm install -g @rely-ai/caliber@${tag}`)} manually.
|
|
8917
8936
|
`));
|
|
8918
8937
|
return;
|
|
8919
8938
|
}
|
|
@@ -8936,7 +8955,7 @@ Restarting: caliber ${args.join(" ")}
|
|
|
8936
8955
|
}
|
|
8937
8956
|
console.log(
|
|
8938
8957
|
chalk19.yellow(
|
|
8939
|
-
`Run ${chalk19.bold(`npm install -g @rely-ai/caliber@${
|
|
8958
|
+
`Run ${chalk19.bold(`npm install -g @rely-ai/caliber@${tag}`)} manually to upgrade.
|
|
8940
8959
|
`
|
|
8941
8960
|
)
|
|
8942
8961
|
);
|