@rely-ai/caliber 1.0.0 → 1.1.0
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 +209 -135
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -51,8 +51,8 @@ var init_constants = __esm({
|
|
|
51
51
|
|
|
52
52
|
// src/cli.ts
|
|
53
53
|
import { Command } from "commander";
|
|
54
|
-
import
|
|
55
|
-
import
|
|
54
|
+
import fs25 from "fs";
|
|
55
|
+
import path22 from "path";
|
|
56
56
|
import { fileURLToPath } from "url";
|
|
57
57
|
|
|
58
58
|
// src/commands/onboard.ts
|
|
@@ -60,7 +60,7 @@ import chalk4 from "chalk";
|
|
|
60
60
|
import ora from "ora";
|
|
61
61
|
import readline3 from "readline";
|
|
62
62
|
import select2 from "@inquirer/select";
|
|
63
|
-
import
|
|
63
|
+
import fs18 from "fs";
|
|
64
64
|
|
|
65
65
|
// src/fingerprint/index.ts
|
|
66
66
|
import fs7 from "fs";
|
|
@@ -2127,16 +2127,25 @@ import path12 from "path";
|
|
|
2127
2127
|
var STAGED_DIR = path12.join(CALIBER_DIR, "staged");
|
|
2128
2128
|
var PROPOSED_DIR = path12.join(STAGED_DIR, "proposed");
|
|
2129
2129
|
var CURRENT_DIR = path12.join(STAGED_DIR, "current");
|
|
2130
|
+
function normalizeContent(content) {
|
|
2131
|
+
return content.split("\n").map((line) => line.trimEnd()).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
2132
|
+
}
|
|
2130
2133
|
function stageFiles(files, projectDir) {
|
|
2131
2134
|
cleanupStaging();
|
|
2132
2135
|
let newFiles = 0;
|
|
2133
2136
|
let modifiedFiles = 0;
|
|
2134
2137
|
const stagedFiles = [];
|
|
2135
2138
|
for (const file of files) {
|
|
2139
|
+
const originalPath = path12.join(projectDir, file.path);
|
|
2140
|
+
if (fs13.existsSync(originalPath)) {
|
|
2141
|
+
const existing = fs13.readFileSync(originalPath, "utf-8");
|
|
2142
|
+
if (normalizeContent(existing) === normalizeContent(file.content)) {
|
|
2143
|
+
continue;
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2136
2146
|
const proposedPath = path12.join(PROPOSED_DIR, file.path);
|
|
2137
2147
|
fs13.mkdirSync(path12.dirname(proposedPath), { recursive: true });
|
|
2138
2148
|
fs13.writeFileSync(proposedPath, file.content);
|
|
2139
|
-
const originalPath = path12.join(projectDir, file.path);
|
|
2140
2149
|
if (fs13.existsSync(originalPath)) {
|
|
2141
2150
|
const currentPath = path12.join(CURRENT_DIR, file.path);
|
|
2142
2151
|
fs13.mkdirSync(path12.dirname(currentPath), { recursive: true });
|
|
@@ -2715,15 +2724,15 @@ function computeGrade(score) {
|
|
|
2715
2724
|
// src/scoring/checks/coverage.ts
|
|
2716
2725
|
import { readFileSync, readdirSync } from "fs";
|
|
2717
2726
|
import { join } from "path";
|
|
2718
|
-
function readFileOrNull(
|
|
2727
|
+
function readFileOrNull(path24) {
|
|
2719
2728
|
try {
|
|
2720
|
-
return readFileSync(
|
|
2729
|
+
return readFileSync(path24, "utf-8");
|
|
2721
2730
|
} catch {
|
|
2722
2731
|
return null;
|
|
2723
2732
|
}
|
|
2724
2733
|
}
|
|
2725
|
-
function readJsonOrNull(
|
|
2726
|
-
const content = readFileOrNull(
|
|
2734
|
+
function readJsonOrNull(path24) {
|
|
2735
|
+
const content = readFileOrNull(path24);
|
|
2727
2736
|
if (!content) return null;
|
|
2728
2737
|
try {
|
|
2729
2738
|
return JSON.parse(content);
|
|
@@ -3079,9 +3088,9 @@ function checkExistence(dir) {
|
|
|
3079
3088
|
// src/scoring/checks/quality.ts
|
|
3080
3089
|
import { readFileSync as readFileSync3 } from "fs";
|
|
3081
3090
|
import { join as join3 } from "path";
|
|
3082
|
-
function readFileOrNull2(
|
|
3091
|
+
function readFileOrNull2(path24) {
|
|
3083
3092
|
try {
|
|
3084
|
-
return readFileSync3(
|
|
3093
|
+
return readFileSync3(path24, "utf-8");
|
|
3085
3094
|
} catch {
|
|
3086
3095
|
return null;
|
|
3087
3096
|
}
|
|
@@ -3232,15 +3241,15 @@ function checkQuality(dir) {
|
|
|
3232
3241
|
// src/scoring/checks/accuracy.ts
|
|
3233
3242
|
import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync } from "fs";
|
|
3234
3243
|
import { join as join4 } from "path";
|
|
3235
|
-
function readFileOrNull3(
|
|
3244
|
+
function readFileOrNull3(path24) {
|
|
3236
3245
|
try {
|
|
3237
|
-
return readFileSync4(
|
|
3246
|
+
return readFileSync4(path24, "utf-8");
|
|
3238
3247
|
} catch {
|
|
3239
3248
|
return null;
|
|
3240
3249
|
}
|
|
3241
3250
|
}
|
|
3242
|
-
function readJsonOrNull2(
|
|
3243
|
-
const content = readFileOrNull3(
|
|
3251
|
+
function readJsonOrNull2(path24) {
|
|
3252
|
+
const content = readFileOrNull3(path24);
|
|
3244
3253
|
if (!content) return null;
|
|
3245
3254
|
try {
|
|
3246
3255
|
return JSON.parse(content);
|
|
@@ -3423,9 +3432,9 @@ function checkAccuracy(dir) {
|
|
|
3423
3432
|
// src/scoring/checks/freshness.ts
|
|
3424
3433
|
import { readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
3425
3434
|
import { join as join5 } from "path";
|
|
3426
|
-
function readFileOrNull4(
|
|
3435
|
+
function readFileOrNull4(path24) {
|
|
3427
3436
|
try {
|
|
3428
|
-
return readFileSync5(
|
|
3437
|
+
return readFileSync5(path24, "utf-8");
|
|
3429
3438
|
} catch {
|
|
3430
3439
|
return null;
|
|
3431
3440
|
}
|
|
@@ -3535,9 +3544,9 @@ function checkFreshness(dir) {
|
|
|
3535
3544
|
import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "fs";
|
|
3536
3545
|
import { execSync as execSync7 } from "child_process";
|
|
3537
3546
|
import { join as join6 } from "path";
|
|
3538
|
-
function readFileOrNull5(
|
|
3547
|
+
function readFileOrNull5(path24) {
|
|
3539
3548
|
try {
|
|
3540
|
-
return readFileSync6(
|
|
3549
|
+
return readFileSync6(path24, "utf-8");
|
|
3541
3550
|
} catch {
|
|
3542
3551
|
return null;
|
|
3543
3552
|
}
|
|
@@ -3630,6 +3639,29 @@ function checkBonus(dir) {
|
|
|
3630
3639
|
return checks;
|
|
3631
3640
|
}
|
|
3632
3641
|
|
|
3642
|
+
// src/scoring/dismissed.ts
|
|
3643
|
+
init_constants();
|
|
3644
|
+
import fs17 from "fs";
|
|
3645
|
+
import path16 from "path";
|
|
3646
|
+
var DISMISSED_FILE = path16.join(CALIBER_DIR, "dismissed-checks.json");
|
|
3647
|
+
function readDismissedChecks() {
|
|
3648
|
+
try {
|
|
3649
|
+
if (!fs17.existsSync(DISMISSED_FILE)) return [];
|
|
3650
|
+
return JSON.parse(fs17.readFileSync(DISMISSED_FILE, "utf-8"));
|
|
3651
|
+
} catch {
|
|
3652
|
+
return [];
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
function writeDismissedChecks(checks) {
|
|
3656
|
+
if (!fs17.existsSync(CALIBER_DIR)) {
|
|
3657
|
+
fs17.mkdirSync(CALIBER_DIR, { recursive: true });
|
|
3658
|
+
}
|
|
3659
|
+
fs17.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
|
|
3660
|
+
}
|
|
3661
|
+
function getDismissedIds() {
|
|
3662
|
+
return new Set(readDismissedChecks().map((c) => c.id));
|
|
3663
|
+
}
|
|
3664
|
+
|
|
3633
3665
|
// src/scoring/index.ts
|
|
3634
3666
|
function sumCategory(checks, category) {
|
|
3635
3667
|
const categoryChecks = checks.filter((c) => c.category === category);
|
|
@@ -3666,7 +3698,8 @@ function computeLocalScore(dir, targetAgent) {
|
|
|
3666
3698
|
...checkFreshness(dir),
|
|
3667
3699
|
...checkBonus(dir)
|
|
3668
3700
|
];
|
|
3669
|
-
const
|
|
3701
|
+
const dismissed = getDismissedIds();
|
|
3702
|
+
const checks = filterChecksForTarget(allChecks, target).filter((c) => !dismissed.has(c.id));
|
|
3670
3703
|
const maxPossible = checks.reduce((s, c) => s + c.maxPoints, 0);
|
|
3671
3704
|
const earned = checks.reduce((s, c) => s + c.earnedPoints, 0);
|
|
3672
3705
|
const score = maxPossible > 0 ? Math.min(100, Math.max(0, Math.round(earned / maxPossible * 100))) : 0;
|
|
@@ -3772,7 +3805,7 @@ function displayScoreSummary(result) {
|
|
|
3772
3805
|
const remaining = failing.length - shown.length;
|
|
3773
3806
|
const moreText = remaining > 0 ? ` (+${remaining} more)` : "";
|
|
3774
3807
|
console.log(chalk3.dim(`
|
|
3775
|
-
Run ${chalk3.
|
|
3808
|
+
Run ${chalk3.hex("#83D1EB")("caliber score")} for details.${moreText}`));
|
|
3776
3809
|
}
|
|
3777
3810
|
console.log("");
|
|
3778
3811
|
}
|
|
@@ -3874,12 +3907,23 @@ async function initCommand(options) {
|
|
|
3874
3907
|
console.log(chalk4.dim(` Files: ${fingerprint.fileTree.length} found
|
|
3875
3908
|
`));
|
|
3876
3909
|
const targetAgent = options.agent || await promptAgent();
|
|
3910
|
+
const preScore = computeLocalScore(process.cwd(), targetAgent);
|
|
3911
|
+
const failingForDismissal = preScore.checks.filter((c) => !c.passed && c.maxPoints > 0);
|
|
3912
|
+
if (failingForDismissal.length > 0) {
|
|
3913
|
+
const newDismissals = await evaluateDismissals(failingForDismissal, fingerprint);
|
|
3914
|
+
if (newDismissals.length > 0) {
|
|
3915
|
+
const existing = readDismissedChecks();
|
|
3916
|
+
const existingIds = new Set(existing.map((d) => d.id));
|
|
3917
|
+
const merged = [...existing, ...newDismissals.filter((d) => !existingIds.has(d.id))];
|
|
3918
|
+
writeDismissedChecks(merged);
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3877
3921
|
const baselineScore = computeLocalScore(process.cwd(), targetAgent);
|
|
3878
3922
|
displayScoreSummary(baselineScore);
|
|
3879
3923
|
const hasExistingConfig = !!(fingerprint.existingConfigs.claudeMd || fingerprint.existingConfigs.claudeSettings || fingerprint.existingConfigs.claudeSkills?.length || fingerprint.existingConfigs.cursorrules || fingerprint.existingConfigs.cursorRules?.length);
|
|
3880
3924
|
if (hasExistingConfig && baselineScore.score === 100) {
|
|
3881
3925
|
console.log(chalk4.bold.green(" Your setup is already optimal \u2014 nothing to change.\n"));
|
|
3882
|
-
console.log(chalk4.dim(" Run
|
|
3926
|
+
console.log(chalk4.dim(" Run ") + chalk4.hex("#83D1EB")("caliber onboard --force") + chalk4.dim(" to regenerate anyway.\n"));
|
|
3883
3927
|
if (!options.force) return;
|
|
3884
3928
|
}
|
|
3885
3929
|
const isEmpty = fingerprint.fileTree.length < 3;
|
|
@@ -4042,14 +4086,14 @@ async function initCommand(options) {
|
|
|
4042
4086
|
const hookResult = installHook();
|
|
4043
4087
|
if (hookResult.installed) {
|
|
4044
4088
|
console.log(` ${chalk4.green("\u2713")} Claude Code hook installed \u2014 docs update on session end`);
|
|
4045
|
-
console.log(chalk4.dim(" Run
|
|
4089
|
+
console.log(chalk4.dim(" Run ") + chalk4.hex("#83D1EB")("caliber hooks remove") + chalk4.dim(" to disable"));
|
|
4046
4090
|
} else if (hookResult.alreadyInstalled) {
|
|
4047
4091
|
console.log(chalk4.dim(" Claude Code hook already installed"));
|
|
4048
4092
|
}
|
|
4049
4093
|
const learnResult = installLearningHooks();
|
|
4050
4094
|
if (learnResult.installed) {
|
|
4051
4095
|
console.log(` ${chalk4.green("\u2713")} Learning hooks installed \u2014 session insights captured automatically`);
|
|
4052
|
-
console.log(chalk4.dim(" Run
|
|
4096
|
+
console.log(chalk4.dim(" Run ") + chalk4.hex("#83D1EB")("caliber learn remove") + chalk4.dim(" to disable"));
|
|
4053
4097
|
} else if (learnResult.alreadyInstalled) {
|
|
4054
4098
|
console.log(chalk4.dim(" Learning hooks already installed"));
|
|
4055
4099
|
}
|
|
@@ -4058,7 +4102,7 @@ async function initCommand(options) {
|
|
|
4058
4102
|
const precommitResult = installPreCommitHook();
|
|
4059
4103
|
if (precommitResult.installed) {
|
|
4060
4104
|
console.log(` ${chalk4.green("\u2713")} Pre-commit hook installed \u2014 docs refresh before each commit`);
|
|
4061
|
-
console.log(chalk4.dim(" Run
|
|
4105
|
+
console.log(chalk4.dim(" Run ") + chalk4.hex("#83D1EB")("caliber hooks remove-precommit") + chalk4.dim(" to disable"));
|
|
4062
4106
|
} else if (precommitResult.alreadyInstalled) {
|
|
4063
4107
|
console.log(chalk4.dim(" Pre-commit hook already installed"));
|
|
4064
4108
|
} else {
|
|
@@ -4066,7 +4110,7 @@ async function initCommand(options) {
|
|
|
4066
4110
|
}
|
|
4067
4111
|
}
|
|
4068
4112
|
if (hookChoice === "skip") {
|
|
4069
|
-
console.log(chalk4.dim(" Skipped auto-refresh hooks. Run
|
|
4113
|
+
console.log(chalk4.dim(" Skipped auto-refresh hooks. Run ") + chalk4.hex("#83D1EB")("caliber hooks install") + chalk4.dim(" later to enable."));
|
|
4070
4114
|
}
|
|
4071
4115
|
const afterScore = computeLocalScore(process.cwd(), targetAgent);
|
|
4072
4116
|
if (afterScore.score < baselineScore.score) {
|
|
@@ -4079,12 +4123,12 @@ async function initCommand(options) {
|
|
|
4079
4123
|
}
|
|
4080
4124
|
} catch {
|
|
4081
4125
|
}
|
|
4082
|
-
console.log(chalk4.dim(" Run
|
|
4126
|
+
console.log(chalk4.dim(" Run ") + chalk4.hex("#83D1EB")("caliber onboard --force") + chalk4.dim(" to override.\n"));
|
|
4083
4127
|
return;
|
|
4084
4128
|
}
|
|
4085
4129
|
displayScoreDelta(baselineScore, afterScore);
|
|
4086
4130
|
console.log(chalk4.bold.green(" Onboarding complete! Your project is ready for AI-assisted development."));
|
|
4087
|
-
console.log(chalk4.dim(" Run
|
|
4131
|
+
console.log(chalk4.dim(" Run ") + chalk4.hex("#83D1EB")("caliber undo") + chalk4.dim(" to revert changes.\n"));
|
|
4088
4132
|
console.log(chalk4.bold(" Next steps:\n"));
|
|
4089
4133
|
console.log(` ${title("caliber score")} See your full config breakdown`);
|
|
4090
4134
|
console.log(` ${title("caliber recommend")} Discover community skills for your stack`);
|
|
@@ -4134,7 +4178,7 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
|
|
|
4134
4178
|
}
|
|
4135
4179
|
function summarizeSetup(action, setup) {
|
|
4136
4180
|
const descriptions = setup.fileDescriptions;
|
|
4137
|
-
const files = descriptions ? Object.entries(descriptions).map(([
|
|
4181
|
+
const files = descriptions ? Object.entries(descriptions).map(([path24, desc]) => ` ${path24}: ${desc}`).join("\n") : Object.keys(setup).filter((k) => k !== "targetAgent" && k !== "fileDescriptions").join(", ");
|
|
4138
4182
|
return `${action}. Files:
|
|
4139
4183
|
${files}`;
|
|
4140
4184
|
}
|
|
@@ -4155,6 +4199,36 @@ Return {"valid": true} or {"valid": false}. Nothing else.`,
|
|
|
4155
4199
|
return true;
|
|
4156
4200
|
}
|
|
4157
4201
|
}
|
|
4202
|
+
async function evaluateDismissals(failingChecks, fingerprint) {
|
|
4203
|
+
const fastModel = process.env.ANTHROPIC_SMALL_FAST_MODEL;
|
|
4204
|
+
const checkList = failingChecks.map((c) => ({
|
|
4205
|
+
id: c.id,
|
|
4206
|
+
name: c.name,
|
|
4207
|
+
suggestion: c.suggestion
|
|
4208
|
+
}));
|
|
4209
|
+
try {
|
|
4210
|
+
const result = await llmJsonCall({
|
|
4211
|
+
system: `You evaluate whether scoring checks are applicable to a project.
|
|
4212
|
+
Given the project's languages/frameworks and a list of failing checks, return which checks are NOT applicable.
|
|
4213
|
+
|
|
4214
|
+
Only dismiss checks that truly don't apply \u2014 e.g. "Build/test/lint commands" for a pure Terraform/HCL repo with no build system.
|
|
4215
|
+
Do NOT dismiss checks that could reasonably apply even if the project doesn't use them yet.
|
|
4216
|
+
|
|
4217
|
+
Return {"dismissed": [{"id": "check_id", "reason": "brief reason"}]} or {"dismissed": []} if all apply.`,
|
|
4218
|
+
prompt: `Languages: ${fingerprint.languages.join(", ") || "none"}
|
|
4219
|
+
Frameworks: ${fingerprint.frameworks.join(", ") || "none"}
|
|
4220
|
+
|
|
4221
|
+
Failing checks:
|
|
4222
|
+
${JSON.stringify(checkList, null, 2)}`,
|
|
4223
|
+
maxTokens: 200,
|
|
4224
|
+
...fastModel ? { model: fastModel } : {}
|
|
4225
|
+
});
|
|
4226
|
+
if (!Array.isArray(result.dismissed)) return [];
|
|
4227
|
+
return result.dismissed.filter((d) => d.id && d.reason && failingChecks.some((c) => c.id === d.id)).map((d) => ({ id: d.id, reason: d.reason, dismissedAt: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
4228
|
+
} catch {
|
|
4229
|
+
return [];
|
|
4230
|
+
}
|
|
4231
|
+
}
|
|
4158
4232
|
function promptInput2(question) {
|
|
4159
4233
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
4160
4234
|
return new Promise((resolve2) => {
|
|
@@ -4224,8 +4298,8 @@ async function openReview(method, stagedFiles) {
|
|
|
4224
4298
|
return;
|
|
4225
4299
|
}
|
|
4226
4300
|
const fileInfos = stagedFiles.map((file) => {
|
|
4227
|
-
const proposed =
|
|
4228
|
-
const current = file.currentPath ?
|
|
4301
|
+
const proposed = fs18.readFileSync(file.proposedPath, "utf-8");
|
|
4302
|
+
const current = file.currentPath ? fs18.readFileSync(file.currentPath, "utf-8") : "";
|
|
4229
4303
|
const patch = createTwoFilesPatch(
|
|
4230
4304
|
file.isNew ? "/dev/null" : file.relativePath,
|
|
4231
4305
|
file.relativePath,
|
|
@@ -4408,7 +4482,7 @@ function printSetupSummary(setup) {
|
|
|
4408
4482
|
};
|
|
4409
4483
|
if (claude) {
|
|
4410
4484
|
if (claude.claudeMd) {
|
|
4411
|
-
const icon =
|
|
4485
|
+
const icon = fs18.existsSync("CLAUDE.md") ? chalk4.yellow("~") : chalk4.green("+");
|
|
4412
4486
|
const desc = getDescription("CLAUDE.md");
|
|
4413
4487
|
console.log(` ${icon} ${chalk4.bold("CLAUDE.md")}`);
|
|
4414
4488
|
if (desc) console.log(chalk4.dim(` ${desc}`));
|
|
@@ -4418,7 +4492,7 @@ function printSetupSummary(setup) {
|
|
|
4418
4492
|
if (Array.isArray(skills) && skills.length > 0) {
|
|
4419
4493
|
for (const skill of skills) {
|
|
4420
4494
|
const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
|
|
4421
|
-
const icon =
|
|
4495
|
+
const icon = fs18.existsSync(skillPath) ? chalk4.yellow("~") : chalk4.green("+");
|
|
4422
4496
|
const desc = getDescription(skillPath);
|
|
4423
4497
|
console.log(` ${icon} ${chalk4.bold(skillPath)}`);
|
|
4424
4498
|
console.log(chalk4.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -4428,7 +4502,7 @@ function printSetupSummary(setup) {
|
|
|
4428
4502
|
}
|
|
4429
4503
|
if (cursor) {
|
|
4430
4504
|
if (cursor.cursorrules) {
|
|
4431
|
-
const icon =
|
|
4505
|
+
const icon = fs18.existsSync(".cursorrules") ? chalk4.yellow("~") : chalk4.green("+");
|
|
4432
4506
|
const desc = getDescription(".cursorrules");
|
|
4433
4507
|
console.log(` ${icon} ${chalk4.bold(".cursorrules")}`);
|
|
4434
4508
|
if (desc) console.log(chalk4.dim(` ${desc}`));
|
|
@@ -4438,7 +4512,7 @@ function printSetupSummary(setup) {
|
|
|
4438
4512
|
if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
|
|
4439
4513
|
for (const skill of cursorSkills) {
|
|
4440
4514
|
const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
|
|
4441
|
-
const icon =
|
|
4515
|
+
const icon = fs18.existsSync(skillPath) ? chalk4.yellow("~") : chalk4.green("+");
|
|
4442
4516
|
const desc = getDescription(skillPath);
|
|
4443
4517
|
console.log(` ${icon} ${chalk4.bold(skillPath)}`);
|
|
4444
4518
|
console.log(chalk4.dim(` ${desc || skill.description || skill.name}`));
|
|
@@ -4449,7 +4523,7 @@ function printSetupSummary(setup) {
|
|
|
4449
4523
|
if (Array.isArray(rules) && rules.length > 0) {
|
|
4450
4524
|
for (const rule of rules) {
|
|
4451
4525
|
const rulePath = `.cursor/rules/${rule.filename}`;
|
|
4452
|
-
const icon =
|
|
4526
|
+
const icon = fs18.existsSync(rulePath) ? chalk4.yellow("~") : chalk4.green("+");
|
|
4453
4527
|
const desc = getDescription(rulePath);
|
|
4454
4528
|
console.log(` ${icon} ${chalk4.bold(rulePath)}`);
|
|
4455
4529
|
if (desc) {
|
|
@@ -4462,7 +4536,7 @@ function printSetupSummary(setup) {
|
|
|
4462
4536
|
}
|
|
4463
4537
|
}
|
|
4464
4538
|
}
|
|
4465
|
-
if (!
|
|
4539
|
+
if (!fs18.existsSync("AGENTS.md")) {
|
|
4466
4540
|
console.log(` ${chalk4.green("+")} ${chalk4.bold("AGENTS.md")}`);
|
|
4467
4541
|
console.log(chalk4.dim(" Cross-agent coordination file"));
|
|
4468
4542
|
console.log("");
|
|
@@ -4490,8 +4564,8 @@ function ensurePermissions() {
|
|
|
4490
4564
|
const settingsPath = ".claude/settings.json";
|
|
4491
4565
|
let settings = {};
|
|
4492
4566
|
try {
|
|
4493
|
-
if (
|
|
4494
|
-
settings = JSON.parse(
|
|
4567
|
+
if (fs18.existsSync(settingsPath)) {
|
|
4568
|
+
settings = JSON.parse(fs18.readFileSync(settingsPath, "utf-8"));
|
|
4495
4569
|
}
|
|
4496
4570
|
} catch {
|
|
4497
4571
|
}
|
|
@@ -4505,8 +4579,8 @@ function ensurePermissions() {
|
|
|
4505
4579
|
"Bash(git *)"
|
|
4506
4580
|
];
|
|
4507
4581
|
settings.permissions = permissions;
|
|
4508
|
-
if (!
|
|
4509
|
-
|
|
4582
|
+
if (!fs18.existsSync(".claude")) fs18.mkdirSync(".claude", { recursive: true });
|
|
4583
|
+
fs18.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
4510
4584
|
}
|
|
4511
4585
|
function collectSetupFiles(setup) {
|
|
4512
4586
|
const files = [];
|
|
@@ -4536,7 +4610,7 @@ function collectSetupFiles(setup) {
|
|
|
4536
4610
|
}
|
|
4537
4611
|
}
|
|
4538
4612
|
}
|
|
4539
|
-
if (!
|
|
4613
|
+
if (!fs18.existsSync("AGENTS.md")) {
|
|
4540
4614
|
const agentRefs = [];
|
|
4541
4615
|
if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
|
|
4542
4616
|
if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
|
|
@@ -4587,7 +4661,7 @@ function undoCommand() {
|
|
|
4587
4661
|
|
|
4588
4662
|
// src/commands/status.ts
|
|
4589
4663
|
import chalk6 from "chalk";
|
|
4590
|
-
import
|
|
4664
|
+
import fs19 from "fs";
|
|
4591
4665
|
async function statusCommand(options) {
|
|
4592
4666
|
const config = loadConfig();
|
|
4593
4667
|
const manifest = readManifest();
|
|
@@ -4604,16 +4678,16 @@ async function statusCommand(options) {
|
|
|
4604
4678
|
if (config) {
|
|
4605
4679
|
console.log(` LLM: ${chalk6.green(config.provider)} (${config.model})`);
|
|
4606
4680
|
} else {
|
|
4607
|
-
console.log(` LLM: ${chalk6.yellow("Not configured")} \u2014 run
|
|
4681
|
+
console.log(` LLM: ${chalk6.yellow("Not configured")} \u2014 run ${chalk6.hex("#83D1EB")("caliber config")}`);
|
|
4608
4682
|
}
|
|
4609
4683
|
if (!manifest) {
|
|
4610
4684
|
console.log(` Setup: ${chalk6.dim("No setup applied")}`);
|
|
4611
|
-
console.log(chalk6.dim("\n Run
|
|
4685
|
+
console.log(chalk6.dim("\n Run ") + chalk6.hex("#83D1EB")("caliber onboard") + chalk6.dim(" to get started.\n"));
|
|
4612
4686
|
return;
|
|
4613
4687
|
}
|
|
4614
4688
|
console.log(` Files managed: ${chalk6.cyan(manifest.entries.length.toString())}`);
|
|
4615
4689
|
for (const entry of manifest.entries) {
|
|
4616
|
-
const exists =
|
|
4690
|
+
const exists = fs19.existsSync(entry.path);
|
|
4617
4691
|
const icon = exists ? chalk6.green("\u2713") : chalk6.red("\u2717");
|
|
4618
4692
|
console.log(` ${icon} ${entry.path} (${entry.action})`);
|
|
4619
4693
|
}
|
|
@@ -4627,12 +4701,12 @@ import confirm from "@inquirer/confirm";
|
|
|
4627
4701
|
async function regenerateCommand(options) {
|
|
4628
4702
|
const config = loadConfig();
|
|
4629
4703
|
if (!config) {
|
|
4630
|
-
console.log(chalk7.red("No LLM provider configured. Run
|
|
4704
|
+
console.log(chalk7.red("No LLM provider configured. Run ") + chalk7.hex("#83D1EB")("caliber config") + chalk7.red(" (e.g. choose Cursor) or set ANTHROPIC_API_KEY."));
|
|
4631
4705
|
throw new Error("__exit__");
|
|
4632
4706
|
}
|
|
4633
4707
|
const manifest = readManifest();
|
|
4634
4708
|
if (!manifest) {
|
|
4635
|
-
console.log(chalk7.yellow("No existing setup found. Run
|
|
4709
|
+
console.log(chalk7.yellow("No existing setup found. Run ") + chalk7.hex("#83D1EB")("caliber onboard") + chalk7.yellow(" first."));
|
|
4636
4710
|
throw new Error("__exit__");
|
|
4637
4711
|
}
|
|
4638
4712
|
const spinner = ora3("Re-analyzing project...").start();
|
|
@@ -4700,13 +4774,13 @@ import { mkdirSync, readFileSync as readFileSync7, readdirSync as readdirSync5,
|
|
|
4700
4774
|
import { join as join8, dirname as dirname2 } from "path";
|
|
4701
4775
|
|
|
4702
4776
|
// src/scanner/index.ts
|
|
4703
|
-
import
|
|
4704
|
-
import
|
|
4777
|
+
import fs20 from "fs";
|
|
4778
|
+
import path17 from "path";
|
|
4705
4779
|
import crypto2 from "crypto";
|
|
4706
4780
|
function scanLocalState(dir) {
|
|
4707
4781
|
const items = [];
|
|
4708
|
-
const claudeMdPath =
|
|
4709
|
-
if (
|
|
4782
|
+
const claudeMdPath = path17.join(dir, "CLAUDE.md");
|
|
4783
|
+
if (fs20.existsSync(claudeMdPath)) {
|
|
4710
4784
|
items.push({
|
|
4711
4785
|
type: "rule",
|
|
4712
4786
|
platform: "claude",
|
|
@@ -4715,10 +4789,10 @@ function scanLocalState(dir) {
|
|
|
4715
4789
|
path: claudeMdPath
|
|
4716
4790
|
});
|
|
4717
4791
|
}
|
|
4718
|
-
const skillsDir =
|
|
4719
|
-
if (
|
|
4720
|
-
for (const file of
|
|
4721
|
-
const filePath =
|
|
4792
|
+
const skillsDir = path17.join(dir, ".claude", "skills");
|
|
4793
|
+
if (fs20.existsSync(skillsDir)) {
|
|
4794
|
+
for (const file of fs20.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
|
|
4795
|
+
const filePath = path17.join(skillsDir, file);
|
|
4722
4796
|
items.push({
|
|
4723
4797
|
type: "skill",
|
|
4724
4798
|
platform: "claude",
|
|
@@ -4728,10 +4802,10 @@ function scanLocalState(dir) {
|
|
|
4728
4802
|
});
|
|
4729
4803
|
}
|
|
4730
4804
|
}
|
|
4731
|
-
const mcpJsonPath =
|
|
4732
|
-
if (
|
|
4805
|
+
const mcpJsonPath = path17.join(dir, ".mcp.json");
|
|
4806
|
+
if (fs20.existsSync(mcpJsonPath)) {
|
|
4733
4807
|
try {
|
|
4734
|
-
const mcpJson = JSON.parse(
|
|
4808
|
+
const mcpJson = JSON.parse(fs20.readFileSync(mcpJsonPath, "utf-8"));
|
|
4735
4809
|
if (mcpJson.mcpServers) {
|
|
4736
4810
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
4737
4811
|
items.push({
|
|
@@ -4746,8 +4820,8 @@ function scanLocalState(dir) {
|
|
|
4746
4820
|
} catch {
|
|
4747
4821
|
}
|
|
4748
4822
|
}
|
|
4749
|
-
const cursorrulesPath =
|
|
4750
|
-
if (
|
|
4823
|
+
const cursorrulesPath = path17.join(dir, ".cursorrules");
|
|
4824
|
+
if (fs20.existsSync(cursorrulesPath)) {
|
|
4751
4825
|
items.push({
|
|
4752
4826
|
type: "rule",
|
|
4753
4827
|
platform: "cursor",
|
|
@@ -4756,10 +4830,10 @@ function scanLocalState(dir) {
|
|
|
4756
4830
|
path: cursorrulesPath
|
|
4757
4831
|
});
|
|
4758
4832
|
}
|
|
4759
|
-
const cursorRulesDir =
|
|
4760
|
-
if (
|
|
4761
|
-
for (const file of
|
|
4762
|
-
const filePath =
|
|
4833
|
+
const cursorRulesDir = path17.join(dir, ".cursor", "rules");
|
|
4834
|
+
if (fs20.existsSync(cursorRulesDir)) {
|
|
4835
|
+
for (const file of fs20.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
|
|
4836
|
+
const filePath = path17.join(cursorRulesDir, file);
|
|
4763
4837
|
items.push({
|
|
4764
4838
|
type: "rule",
|
|
4765
4839
|
platform: "cursor",
|
|
@@ -4769,12 +4843,12 @@ function scanLocalState(dir) {
|
|
|
4769
4843
|
});
|
|
4770
4844
|
}
|
|
4771
4845
|
}
|
|
4772
|
-
const cursorSkillsDir =
|
|
4773
|
-
if (
|
|
4846
|
+
const cursorSkillsDir = path17.join(dir, ".cursor", "skills");
|
|
4847
|
+
if (fs20.existsSync(cursorSkillsDir)) {
|
|
4774
4848
|
try {
|
|
4775
|
-
for (const name of
|
|
4776
|
-
const skillFile =
|
|
4777
|
-
if (
|
|
4849
|
+
for (const name of fs20.readdirSync(cursorSkillsDir)) {
|
|
4850
|
+
const skillFile = path17.join(cursorSkillsDir, name, "SKILL.md");
|
|
4851
|
+
if (fs20.existsSync(skillFile)) {
|
|
4778
4852
|
items.push({
|
|
4779
4853
|
type: "skill",
|
|
4780
4854
|
platform: "cursor",
|
|
@@ -4787,10 +4861,10 @@ function scanLocalState(dir) {
|
|
|
4787
4861
|
} catch {
|
|
4788
4862
|
}
|
|
4789
4863
|
}
|
|
4790
|
-
const cursorMcpPath =
|
|
4791
|
-
if (
|
|
4864
|
+
const cursorMcpPath = path17.join(dir, ".cursor", "mcp.json");
|
|
4865
|
+
if (fs20.existsSync(cursorMcpPath)) {
|
|
4792
4866
|
try {
|
|
4793
|
-
const mcpJson = JSON.parse(
|
|
4867
|
+
const mcpJson = JSON.parse(fs20.readFileSync(cursorMcpPath, "utf-8"));
|
|
4794
4868
|
if (mcpJson.mcpServers) {
|
|
4795
4869
|
for (const name of Object.keys(mcpJson.mcpServers)) {
|
|
4796
4870
|
items.push({
|
|
@@ -4808,7 +4882,7 @@ function scanLocalState(dir) {
|
|
|
4808
4882
|
return items;
|
|
4809
4883
|
}
|
|
4810
4884
|
function hashFile(filePath) {
|
|
4811
|
-
const text =
|
|
4885
|
+
const text = fs20.readFileSync(filePath, "utf-8");
|
|
4812
4886
|
return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
4813
4887
|
}
|
|
4814
4888
|
function hashJson(obj) {
|
|
@@ -5374,18 +5448,18 @@ async function scoreCommand(options) {
|
|
|
5374
5448
|
const separator = chalk9.gray(" " + "\u2500".repeat(53));
|
|
5375
5449
|
console.log(separator);
|
|
5376
5450
|
if (result.score < 40) {
|
|
5377
|
-
console.log(chalk9.gray(" Run ") + chalk9.hex("#
|
|
5451
|
+
console.log(chalk9.gray(" Run ") + chalk9.hex("#83D1EB")("caliber onboard") + chalk9.gray(" to generate a complete, optimized setup."));
|
|
5378
5452
|
} else if (result.score < 70) {
|
|
5379
|
-
console.log(chalk9.gray(" Run ") + chalk9.hex("#
|
|
5453
|
+
console.log(chalk9.gray(" Run ") + chalk9.hex("#83D1EB")("caliber onboard") + chalk9.gray(" to improve your setup."));
|
|
5380
5454
|
} else {
|
|
5381
|
-
console.log(chalk9.green(" Looking good!") + chalk9.gray(" Run ") + chalk9.hex("#
|
|
5455
|
+
console.log(chalk9.green(" Looking good!") + chalk9.gray(" Run ") + chalk9.hex("#83D1EB")("caliber update") + chalk9.gray(" to keep it fresh."));
|
|
5382
5456
|
}
|
|
5383
5457
|
console.log("");
|
|
5384
5458
|
}
|
|
5385
5459
|
|
|
5386
5460
|
// src/commands/refresh.ts
|
|
5387
|
-
import
|
|
5388
|
-
import
|
|
5461
|
+
import fs22 from "fs";
|
|
5462
|
+
import path19 from "path";
|
|
5389
5463
|
import chalk10 from "chalk";
|
|
5390
5464
|
import ora5 from "ora";
|
|
5391
5465
|
|
|
@@ -5462,37 +5536,37 @@ function collectDiff(lastSha) {
|
|
|
5462
5536
|
}
|
|
5463
5537
|
|
|
5464
5538
|
// src/writers/refresh.ts
|
|
5465
|
-
import
|
|
5466
|
-
import
|
|
5539
|
+
import fs21 from "fs";
|
|
5540
|
+
import path18 from "path";
|
|
5467
5541
|
function writeRefreshDocs(docs) {
|
|
5468
5542
|
const written = [];
|
|
5469
5543
|
if (docs.claudeMd) {
|
|
5470
|
-
|
|
5544
|
+
fs21.writeFileSync("CLAUDE.md", docs.claudeMd);
|
|
5471
5545
|
written.push("CLAUDE.md");
|
|
5472
5546
|
}
|
|
5473
5547
|
if (docs.readmeMd) {
|
|
5474
|
-
|
|
5548
|
+
fs21.writeFileSync("README.md", docs.readmeMd);
|
|
5475
5549
|
written.push("README.md");
|
|
5476
5550
|
}
|
|
5477
5551
|
if (docs.cursorrules) {
|
|
5478
|
-
|
|
5552
|
+
fs21.writeFileSync(".cursorrules", docs.cursorrules);
|
|
5479
5553
|
written.push(".cursorrules");
|
|
5480
5554
|
}
|
|
5481
5555
|
if (docs.cursorRules) {
|
|
5482
|
-
const rulesDir =
|
|
5483
|
-
if (!
|
|
5556
|
+
const rulesDir = path18.join(".cursor", "rules");
|
|
5557
|
+
if (!fs21.existsSync(rulesDir)) fs21.mkdirSync(rulesDir, { recursive: true });
|
|
5484
5558
|
for (const rule of docs.cursorRules) {
|
|
5485
|
-
const filePath =
|
|
5486
|
-
|
|
5559
|
+
const filePath = path18.join(rulesDir, rule.filename);
|
|
5560
|
+
fs21.writeFileSync(filePath, rule.content);
|
|
5487
5561
|
written.push(filePath);
|
|
5488
5562
|
}
|
|
5489
5563
|
}
|
|
5490
5564
|
if (docs.claudeSkills) {
|
|
5491
|
-
const skillsDir =
|
|
5492
|
-
if (!
|
|
5565
|
+
const skillsDir = path18.join(".claude", "skills");
|
|
5566
|
+
if (!fs21.existsSync(skillsDir)) fs21.mkdirSync(skillsDir, { recursive: true });
|
|
5493
5567
|
for (const skill of docs.claudeSkills) {
|
|
5494
|
-
const filePath =
|
|
5495
|
-
|
|
5568
|
+
const filePath = path18.join(skillsDir, skill.filename);
|
|
5569
|
+
fs21.writeFileSync(filePath, skill.content);
|
|
5496
5570
|
written.push(filePath);
|
|
5497
5571
|
}
|
|
5498
5572
|
}
|
|
@@ -5567,11 +5641,11 @@ function log(quiet, ...args) {
|
|
|
5567
5641
|
function discoverGitRepos(parentDir) {
|
|
5568
5642
|
const repos = [];
|
|
5569
5643
|
try {
|
|
5570
|
-
const entries =
|
|
5644
|
+
const entries = fs22.readdirSync(parentDir, { withFileTypes: true });
|
|
5571
5645
|
for (const entry of entries) {
|
|
5572
5646
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
5573
|
-
const childPath =
|
|
5574
|
-
if (
|
|
5647
|
+
const childPath = path19.join(parentDir, entry.name);
|
|
5648
|
+
if (fs22.existsSync(path19.join(childPath, ".git"))) {
|
|
5575
5649
|
repos.push(childPath);
|
|
5576
5650
|
}
|
|
5577
5651
|
}
|
|
@@ -5649,7 +5723,7 @@ async function refreshCommand(options) {
|
|
|
5649
5723
|
const config = loadConfig();
|
|
5650
5724
|
if (!config) {
|
|
5651
5725
|
if (quiet) return;
|
|
5652
|
-
console.log(chalk10.red("No LLM provider configured. Run
|
|
5726
|
+
console.log(chalk10.red("No LLM provider configured. Run ") + chalk10.hex("#83D1EB")("caliber config") + chalk10.red(" (e.g. choose Cursor) or set an API key."));
|
|
5653
5727
|
throw new Error("__exit__");
|
|
5654
5728
|
}
|
|
5655
5729
|
if (isGitRepo()) {
|
|
@@ -5666,7 +5740,7 @@ async function refreshCommand(options) {
|
|
|
5666
5740
|
`));
|
|
5667
5741
|
const originalDir = process.cwd();
|
|
5668
5742
|
for (const repo of repos) {
|
|
5669
|
-
const repoName =
|
|
5743
|
+
const repoName = path19.basename(repo);
|
|
5670
5744
|
try {
|
|
5671
5745
|
process.chdir(repo);
|
|
5672
5746
|
await refreshSingleRepo(repo, { ...options, label: repoName });
|
|
@@ -5913,8 +5987,8 @@ function readStdin() {
|
|
|
5913
5987
|
|
|
5914
5988
|
// src/learner/storage.ts
|
|
5915
5989
|
init_constants();
|
|
5916
|
-
import
|
|
5917
|
-
import
|
|
5990
|
+
import fs23 from "fs";
|
|
5991
|
+
import path20 from "path";
|
|
5918
5992
|
var MAX_RESPONSE_LENGTH = 2e3;
|
|
5919
5993
|
var DEFAULT_STATE = {
|
|
5920
5994
|
sessionId: null,
|
|
@@ -5922,15 +5996,15 @@ var DEFAULT_STATE = {
|
|
|
5922
5996
|
lastAnalysisTimestamp: null
|
|
5923
5997
|
};
|
|
5924
5998
|
function ensureLearningDir() {
|
|
5925
|
-
if (!
|
|
5926
|
-
|
|
5999
|
+
if (!fs23.existsSync(LEARNING_DIR)) {
|
|
6000
|
+
fs23.mkdirSync(LEARNING_DIR, { recursive: true });
|
|
5927
6001
|
}
|
|
5928
6002
|
}
|
|
5929
6003
|
function sessionFilePath() {
|
|
5930
|
-
return
|
|
6004
|
+
return path20.join(LEARNING_DIR, LEARNING_SESSION_FILE);
|
|
5931
6005
|
}
|
|
5932
6006
|
function stateFilePath() {
|
|
5933
|
-
return
|
|
6007
|
+
return path20.join(LEARNING_DIR, LEARNING_STATE_FILE);
|
|
5934
6008
|
}
|
|
5935
6009
|
function truncateResponse(response) {
|
|
5936
6010
|
const str = JSON.stringify(response);
|
|
@@ -5941,50 +6015,50 @@ function appendEvent(event) {
|
|
|
5941
6015
|
ensureLearningDir();
|
|
5942
6016
|
const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
|
|
5943
6017
|
const filePath = sessionFilePath();
|
|
5944
|
-
|
|
6018
|
+
fs23.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
|
|
5945
6019
|
const count = getEventCount();
|
|
5946
6020
|
if (count > LEARNING_MAX_EVENTS) {
|
|
5947
|
-
const lines =
|
|
6021
|
+
const lines = fs23.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
5948
6022
|
const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
|
|
5949
|
-
|
|
6023
|
+
fs23.writeFileSync(filePath, kept.join("\n") + "\n");
|
|
5950
6024
|
}
|
|
5951
6025
|
}
|
|
5952
6026
|
function readAllEvents() {
|
|
5953
6027
|
const filePath = sessionFilePath();
|
|
5954
|
-
if (!
|
|
5955
|
-
const lines =
|
|
6028
|
+
if (!fs23.existsSync(filePath)) return [];
|
|
6029
|
+
const lines = fs23.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
5956
6030
|
return lines.map((line) => JSON.parse(line));
|
|
5957
6031
|
}
|
|
5958
6032
|
function getEventCount() {
|
|
5959
6033
|
const filePath = sessionFilePath();
|
|
5960
|
-
if (!
|
|
5961
|
-
const content =
|
|
6034
|
+
if (!fs23.existsSync(filePath)) return 0;
|
|
6035
|
+
const content = fs23.readFileSync(filePath, "utf-8");
|
|
5962
6036
|
return content.split("\n").filter(Boolean).length;
|
|
5963
6037
|
}
|
|
5964
6038
|
function clearSession() {
|
|
5965
6039
|
const filePath = sessionFilePath();
|
|
5966
|
-
if (
|
|
6040
|
+
if (fs23.existsSync(filePath)) fs23.unlinkSync(filePath);
|
|
5967
6041
|
}
|
|
5968
6042
|
function readState2() {
|
|
5969
6043
|
const filePath = stateFilePath();
|
|
5970
|
-
if (!
|
|
6044
|
+
if (!fs23.existsSync(filePath)) return { ...DEFAULT_STATE };
|
|
5971
6045
|
try {
|
|
5972
|
-
return JSON.parse(
|
|
6046
|
+
return JSON.parse(fs23.readFileSync(filePath, "utf-8"));
|
|
5973
6047
|
} catch {
|
|
5974
6048
|
return { ...DEFAULT_STATE };
|
|
5975
6049
|
}
|
|
5976
6050
|
}
|
|
5977
6051
|
function writeState2(state) {
|
|
5978
6052
|
ensureLearningDir();
|
|
5979
|
-
|
|
6053
|
+
fs23.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
|
|
5980
6054
|
}
|
|
5981
6055
|
function resetState() {
|
|
5982
6056
|
writeState2({ ...DEFAULT_STATE });
|
|
5983
6057
|
}
|
|
5984
6058
|
|
|
5985
6059
|
// src/learner/writer.ts
|
|
5986
|
-
import
|
|
5987
|
-
import
|
|
6060
|
+
import fs24 from "fs";
|
|
6061
|
+
import path21 from "path";
|
|
5988
6062
|
var LEARNED_START = "<!-- caliber:learned -->";
|
|
5989
6063
|
var LEARNED_END = "<!-- /caliber:learned -->";
|
|
5990
6064
|
function writeLearnedContent(update) {
|
|
@@ -6004,8 +6078,8 @@ function writeLearnedContent(update) {
|
|
|
6004
6078
|
function writeLearnedSection(content) {
|
|
6005
6079
|
const claudeMdPath = "CLAUDE.md";
|
|
6006
6080
|
let existing = "";
|
|
6007
|
-
if (
|
|
6008
|
-
existing =
|
|
6081
|
+
if (fs24.existsSync(claudeMdPath)) {
|
|
6082
|
+
existing = fs24.readFileSync(claudeMdPath, "utf-8");
|
|
6009
6083
|
}
|
|
6010
6084
|
const section = `${LEARNED_START}
|
|
6011
6085
|
${content}
|
|
@@ -6019,15 +6093,15 @@ ${LEARNED_END}`;
|
|
|
6019
6093
|
const separator = existing.endsWith("\n") || existing === "" ? "" : "\n";
|
|
6020
6094
|
updated = existing + separator + "\n" + section + "\n";
|
|
6021
6095
|
}
|
|
6022
|
-
|
|
6096
|
+
fs24.writeFileSync(claudeMdPath, updated);
|
|
6023
6097
|
}
|
|
6024
6098
|
function writeLearnedSkill(skill) {
|
|
6025
|
-
const skillDir =
|
|
6026
|
-
if (!
|
|
6027
|
-
const skillPath =
|
|
6028
|
-
if (!skill.isNew &&
|
|
6029
|
-
const existing =
|
|
6030
|
-
|
|
6099
|
+
const skillDir = path21.join(".claude", "skills", skill.name);
|
|
6100
|
+
if (!fs24.existsSync(skillDir)) fs24.mkdirSync(skillDir, { recursive: true });
|
|
6101
|
+
const skillPath = path21.join(skillDir, "SKILL.md");
|
|
6102
|
+
if (!skill.isNew && fs24.existsSync(skillPath)) {
|
|
6103
|
+
const existing = fs24.readFileSync(skillPath, "utf-8");
|
|
6104
|
+
fs24.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
|
|
6031
6105
|
} else {
|
|
6032
6106
|
const frontmatter = [
|
|
6033
6107
|
"---",
|
|
@@ -6036,14 +6110,14 @@ function writeLearnedSkill(skill) {
|
|
|
6036
6110
|
"---",
|
|
6037
6111
|
""
|
|
6038
6112
|
].join("\n");
|
|
6039
|
-
|
|
6113
|
+
fs24.writeFileSync(skillPath, frontmatter + skill.content);
|
|
6040
6114
|
}
|
|
6041
6115
|
return skillPath;
|
|
6042
6116
|
}
|
|
6043
6117
|
function readLearnedSection() {
|
|
6044
6118
|
const claudeMdPath = "CLAUDE.md";
|
|
6045
|
-
if (!
|
|
6046
|
-
const content =
|
|
6119
|
+
if (!fs24.existsSync(claudeMdPath)) return null;
|
|
6120
|
+
const content = fs24.readFileSync(claudeMdPath, "utf-8");
|
|
6047
6121
|
const startIdx = content.indexOf(LEARNED_START);
|
|
6048
6122
|
const endIdx = content.indexOf(LEARNED_END);
|
|
6049
6123
|
if (startIdx === -1 || endIdx === -1) return null;
|
|
@@ -6227,9 +6301,9 @@ Learned items in CLAUDE.md: ${chalk13.cyan(String(lineCount))}`);
|
|
|
6227
6301
|
}
|
|
6228
6302
|
|
|
6229
6303
|
// src/cli.ts
|
|
6230
|
-
var __dirname =
|
|
6304
|
+
var __dirname = path22.dirname(fileURLToPath(import.meta.url));
|
|
6231
6305
|
var pkg = JSON.parse(
|
|
6232
|
-
|
|
6306
|
+
fs25.readFileSync(path22.resolve(__dirname, "..", "package.json"), "utf-8")
|
|
6233
6307
|
);
|
|
6234
6308
|
var program = new Command();
|
|
6235
6309
|
var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
|
|
@@ -6251,22 +6325,22 @@ learn.command("remove").description("Remove learning hooks from .claude/settings
|
|
|
6251
6325
|
learn.command("status").description("Show learning system status").action(learnStatusCommand);
|
|
6252
6326
|
|
|
6253
6327
|
// src/utils/version-check.ts
|
|
6254
|
-
import
|
|
6255
|
-
import
|
|
6328
|
+
import fs26 from "fs";
|
|
6329
|
+
import path23 from "path";
|
|
6256
6330
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6257
6331
|
import { execSync as execSync9 } from "child_process";
|
|
6258
6332
|
import chalk14 from "chalk";
|
|
6259
6333
|
import ora6 from "ora";
|
|
6260
6334
|
import confirm2 from "@inquirer/confirm";
|
|
6261
|
-
var __dirname_vc =
|
|
6335
|
+
var __dirname_vc = path23.dirname(fileURLToPath2(import.meta.url));
|
|
6262
6336
|
var pkg2 = JSON.parse(
|
|
6263
|
-
|
|
6337
|
+
fs26.readFileSync(path23.resolve(__dirname_vc, "..", "package.json"), "utf-8")
|
|
6264
6338
|
);
|
|
6265
6339
|
function getInstalledVersion() {
|
|
6266
6340
|
try {
|
|
6267
6341
|
const globalRoot = execSync9("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
6268
|
-
const pkgPath =
|
|
6269
|
-
return JSON.parse(
|
|
6342
|
+
const pkgPath = path23.join(globalRoot, "@rely-ai", "caliber", "package.json");
|
|
6343
|
+
return JSON.parse(fs26.readFileSync(pkgPath, "utf-8")).version;
|
|
6270
6344
|
} catch {
|
|
6271
6345
|
return null;
|
|
6272
6346
|
}
|
package/package.json
CHANGED